PageSourceSearch

https://www.door-tablet.com/doortablet/js/blog-scripts.js

js door-tablet.com collected 2026-10-01 15:55:10 UTC 8,001 bytes, 215 lines download raw bytes

1/* Blog sidebar. Collapsible lists, Archive relabelled, counts made pills. */
2(function () {
3    const BLOG_CATEGORIES_URL = "/doortablet/dtcms.nsf/base.xsp?open&cmd=blogcategs&return=json";
4    const BLOG_CATEGORIES_CACHE_KEY = "blogCategories";
5    const BLOG_CATEGORIES_CACHE_TIME_KEY = "blogCategoriesTime";
6    const BLOG_CATEGORIES_MAX_AGE = 4 * 60 * 60 * 1000; // 4 hours
7
8    function addCount(link, digits) {
9        var pill = document.createElement("span");
10        pill.className = "dt-count";
11        pill.appendChild(document.createTextNode(digits));
12        link.appendChild(pill);
13    }
14
15    function hasClass(el, name) {
16        return (" " + el.className + " ").indexOf(" " + name + " ") !== -1;
17    }
18
19    function toggle(box, head) {
20        var open = hasClass(box, "dt-open");
21        if (open) {
22            box.className = (" " + box.className + " ").replace(" dt-open ", " ").replace(/^\s+|\s+$/g, "");
23        } else {
24            box.className = (box.className + " dt-open").replace(/^\s+/, "");
25        }
26        head.setAttribute("aria-expanded", open ? "false" : "true");
27    }
28
29    /* Cap the list at MAX rows. Anything past it is hidden rather than removed */
30    /* so the links stay in the markup and remain crawlable. */
31    function capList(list, max) {
32        var extra = [].slice.call(list.getElementsByTagName("a"), max);
33        var i = extra.length;
34        while (i--) extra[i].className = extra[i].className + " dt-hide";
35    }
36
37    function build(box, label, max) {
38        if (!box || box.getAttribute("data-dt")) return;
39        box.setAttribute("data-dt", "1");
40
41        var head = box.getElementsByTagName("span")[0];
42        if (!head) return;
43
44        /* The CMS puts the same id on both headings, so the heading is reached */
45        /* through its parent rather than by id. */
46        if (label) {
47            while (head.firstChild) head.removeChild(head.firstChild);
48            head.appendChild(document.createTextNode(label));
49        }
50        head.className = "dt-head";
51        head.setAttribute("role", "button");
52        head.setAttribute("tabindex", "0");
53        head.setAttribute("aria-expanded", "false");
54
55        var list = document.createElement("div");
56        list.className = "dt-list";
57
58        var node = head.nextSibling;
59        var next,
60            lastLink = null,
61            digits;
62        while (node) {
63            next = node.nextSibling;
64            digits = null;
65            if (node.nodeType === 3 && /\(\s*\d+\s*\)/.test(node.nodeValue)) {
66                digits = node.nodeValue.match(/\d+/)[0];
67            }
68            if (digits !== null && lastLink) {
69                addCount(lastLink, digits);
70                box.removeChild(node);
71            } else {
72                if (node.nodeType === 1 && node.tagName === "A") lastLink = node;
73                list.appendChild(node);
74            }
75            node = next;
76        }
77        box.appendChild(list);
78        capList(list, max);
79
80        head.onclick = function () {
81            toggle(box, head);
82        };
83        head.onkeydown = function (ev) {
84            var e = ev || window.event;
85            if (e.keyCode === 13 || e.keyCode === 32) {
86                if (e.preventDefault) e.preventDefault();
87                toggle(box, head);
88            }
89        };
90    }
91
92    function init() {
93        build(document.getElementById("recent-posts"), null, 12);
94        build(document.getElementById("archive-list"), "Previous Posts", 12);
95        initBlogCategories();
96    }
97
98    if (document.readyState === "loading") {
99        document.addEventListener("DOMContentLoaded", init);
100    } else {
101        init();
102    }
103
104    async function loadBlogCategories() {
105        const now = Date.now();
106        const cachedData = localStorage.getItem(BLOG_CATEGORIES_CACHE_KEY);
107        const cachedTime = parseInt(localStorage.getItem(BLOG_CATEGORIES_CACHE_TIME_KEY) || "0", 10);
108        const cacheExpired = !cachedData || !cachedTime || now - cachedTime > BLOG_CATEGORIES_MAX_AGE;
109
110        const useServer = hasDominoWebSession() || cacheExpired;
111        if (!useServer) return JSON.parse(cachedData);
112
113        try {
114            const response = await fetch(BLOG_CATEGORIES_URL, {
115                cache: "no-store",
116            });
117
118            if (!response.ok) throw new Error("HTTP " + response.status);
119            const data = await response.json();
120
121            localStorage.setItem(BLOG_CATEGORIES_CACHE_KEY, JSON.stringify(data));
122
123            localStorage.setItem(BLOG_CATEGORIES_CACHE_TIME_KEY, now.toString());
124            return data;
125        } catch (err) {
126            console.error("Unable to load blog categories:", err);
127            if (cachedData) return JSON.parse(cachedData);
128            throw err;
129        }
130    }
131
132    function hasDominoWebSession() {
133        return document.cookie.split(";").some((cookie) => cookie.trim().startsWith("DomAuthSessId="));
134    }
135
136    function renderBlogCategories(data) {
137        const container = document.getElementById("blogCategories");
138        if (!container || !data || !data.categories) return;
139        container.innerHTML = "";
140        data.categories.forEach((item) => {
141            const link = document.createElement("a");
142
143            link.className = "blog-category";
144            link.href = "/doortablet/dtcms.nsf/blog?open&type=categ&key=" + encodeURIComponent(item.category);
145
146            const name = document.createElement("span");
147            name.className = "blog-category-name";
148            name.textContent = item.category;
149
150            const badge = document.createElement("span");
151            badge.className = "blog-category-badge";
152            badge.textContent = item.count;
153
154            link.appendChild(name);
155            link.appendChild(badge);
156
157            container.appendChild(link);
158        });
159    }
160
161    async function initBlogCategories() {
162        try {
163            const data = await loadBlogCategories();
164            renderBlogCategories(data);
165            renderBlogDocumentCategories(data);
166        } catch (err) {
167            console.error("Blog categories unavailable:", err);
168        }
169    }
170
171    function renderBlogDocumentCategories(data) {
172        if (!data || !data.categories) return;
173
174        // Create a lookup table for the category counts
175        const categoryMap = new Map();
176
177        data.categories.forEach((item) => {
178            categoryMap.set(item.category.trim().toLowerCase(), item);
179        });
180
181        document.querySelectorAll(".blogCategs").forEach((container) => {
182            const raw = container.dataset.categ;
183            if (!raw) return;
184            const wantedCategories = raw
185                .split(",")
186                .map((c) => c.trim())
187                .filter(Boolean);
188
189            container.innerHTML = "";
190
191            wantedCategories.forEach((categoryName) => {
192                const item = categoryMap.get(categoryName.toLowerCase());
193                const link = document.createElement("a");
194                link.className = "blog-category";
195                link.href = "/doortablet/dtcms.nsf/blog?open&type=categ&key=" + encodeURIComponent(categoryName);
196                const name = document.createElement("span");
197                name.className = "blog-category-name";
198                name.textContent = categoryName;
199
200                link.appendChild(name);
201
202                // Add badge if we found this category in the server data
203                if (item) {
204                    const badge = document.createElement("span");
205                    badge.className = "blog-category-badge";
206                    badge.textContent = item.count;
207
208                    link.appendChild(badge);
209                }
210
211                container.appendChild(link);
212            });
213        });
214    }
215})();

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.