PageSourceSearch

https://fusion809.github.io/libs/common/navPanel.js

js fusion809.github.io collected 2026-10-03 09:13:17 UTC 9,470 bytes, 227 lines download raw bytes

1function toggleTOC() {
2    const toc = document.getElementById("toc-panel");
3    if (!toc) {
4        // Panel not yet loaded; retry shortly
5        setTimeout(toggleTOC, 10);
6        return;
7    }
8    toc.style.display = (toc.style.display === "none" || toc.style.display === "") ? "block" : "none";
9}
10
11function addH1ToTOC(tocPanel, tocList, headers) {
12    var i = 0;
13    headers.forEach(h => {
14        if (!h.id) h.id = h.textContent.trim().replace(/\s+/g, "-").toLowerCase();
15        let label = h.textContent.trim();
16        if (!label) return;
17        if (h.tagName === "H1") {
18            var text = '<span id="h1_toc">' + label + '</span>'
19        } else {
20            return;
21        }
22        if (i == 0) {
23            tocPanel.insertAdjacentHTML("afterbegin", `<a href="#${h.id}">${text}</a>`);
24        } else {
25            const li = document.createElement("li");
26            var text = label;
27            li.innerHTML = `<a href="#${h.id}">${text}</a>`;
28            tocList.appendChild(li);
29        }
30        i++;
31    });
32}
33function addHeadersToTOC(tocList, headers) {
34    headers.forEach(h => {
35        if (!h.id) h.id = h.textContent.trim().replace(/\s+/g, "-").toLowerCase();
36        const li = document.createElement("li");
37        let label = h.textContent.trim();
38        if (!label) return;
39        if (h.tagName === "H1") {
40            return;
41        } else {
42            var text = label;
43        }
44        li.innerHTML = `<a href="#${h.id}">${text}</a>`;
45        tocList.appendChild(li);
46    });
47}
48
49function addTablesToTOC(tocList, tables) {
50    tables.forEach(table => {
51        const id = table.id;
52        const suffix = id;
53        const infoEl = document.getElementById("info" + suffix);
54        var label;
55        if (id == "parameterForm") {
56            label = "Simulation parameter form"
57        } else if (id == "buttontable") {
58            label = "Table of plot, tabulation and animation trigger buttons."
59        } else if (infoEl) {
60            label = infoEl.textContent.trim();
61        } else {
62            var suff = id.replace(/([A-Z])/g, (match) => ' ' + match.toLowerCase()).trim();
63            suff = suff.replace(/ theta/g, ", θ");
64            suff = suff.replace(/minimum and maximum/, "minimum and maximum, ");
65            suff = suff + ".";
66            label = suff.charAt(0).toUpperCase() + suff.slice(1)
67        }
68        if (!table.id) return;
69        const li = document.createElement("li");
70        li.innerHTML = `<a href="#${id}">${label.replace(/container/gi, "animation").trim()}</a>`;
71
72        tocList.appendChild(li);
73    });
74}
75
76function generateContLabel(id, content) {
77    // If infoEl is missing or empty, fallback to a cleaned-up version of the ID
78    const rawSuffix = id.replace(/^container/, "");
79    if (rawSuffix == "tableOutputs") {
80        label = "Table of outputs"
81    } else if (id == "container") {
82        label = "System animation"
83    } else if (/animationSIR/.test(content)) {
84        label = "Animation of S, I and R phase plot."
85    } else if (/animationSER/.test(content)) {
86        label = "Animation of S, E and R phase plot."
87    } else if (/animationEIR/.test(content)) {
88        label = "Animation of E, I and R phase plot."
89    } else if (/animationSEI/.test(content)) {
90        label = "Animation of S, E and I phase plot."
91    } else if (/animation/.test(content)) {
92        let suff = rawSuffix.replace(/([A-Z])/g, (match) => ' ' + match.toLowerCase()).trim();
93        suff = suff.replace(/([a-zA-Z])(\d)$/g, '$1<sub>$2</sub>');
94        suff = suff.replace(/(\d)/, "<sub>$1</sub>");
95        suff = suff.replace(/theta/g, "θ");
96        label = `Animation of ${suff} plot`
97    } else if (/errorPlot/.test(content)) {
98        label = "Error plot"
99    } else if (/phasePlotXYZ/.test(content)) {
100        label = "3D phase plot of x, y and z."
101    } else if (/phasePlotXY/.test(content)) {
102        label = "Phase plot of y vs x."
103    } else if (/phasePlotXZ/.test(content)) {
104        label = "Phase plot of z vs x."
105    } else if (/phasePlotYZ/.test(content)) {
106        label = "Phase plot of z vs y."
107    }
107 else if (/phasePlotSIR/.test(content)) {
108        label = "3D phase plot of S, I and R."
109    } else if (/phasePlotEIR/.test(content)) {
110        label = "3D phase plot of E, I and R."
111    } else if (/phasePlotSER/.test(content)) {
112        label = "3D phase plot of S, E and R."
113    } else if (/phasePlotSEI/.test(content)) {
114        label = "3D phase plot of S, E and I."
115    } else if (/phasePlotSI/.test(content)) {
116        label = "Phase plot of I vs S."
117    } else if (/phasePlotSR/.test(content)) {
118        label = "Phase plot of R vs S."
119    } else if (/phasePlotIR/.test(content)) {
120        label = "Phase plot of R vs I."
121    } else if (/phasePlot/.test(content)) {
122        let suff = rawSuffix.replace(/phasePlot/, "").replace(/([A-Z])/g, (match) => ' ' + match.toLowerCase()).trim();
123        if (suff) {    
124            suff = suff.replace(/\s([a-zA-Z]*\d)$/, ' vs $1').replace(/\s([a-zA-Z]*)$/, ' vs $1');
125            suff = suff.replace(/([a-zA-Z])(\d)$/g, '$1<sub>$2</sub>');
126            suff = suff.replace(/d([a-zA-Z0-9]*)/, "d$1/dt")
127            suff = suff.replace(/(\d)/, "<sub>$1</sub>");
128            suff = suff.replace(/theta/g, "θ");
129            label = `Phase plot of ${suff}`
130        } else {
131            label = "Phase plot"
132        }
133    } else if (/pendulum.*Plot/.test(content) || /timePlot/.test(content)) {
134        suff = rawSuffix.replace(/([A-Z])/g, (match) => ' ' + match.toLowerCase()).trim();
135        label = suff.charAt(0).toUpperCase() + suff.slice(1)
136    } else if (/[pP]lot/.test(content)) {
137        let suff = rawSuffix.replace(/([A-Z])/g, (match) => ' ' + match.toLowerCase()).trim();
138        suff = suff.charAt(0).toUpperCase() + suff.slice(1)
139        suff = suff.replace(/[pP]lot /, "Plot of ");
140        // Replace digits after a letter with <sub>digit>
141        suff = suff.replace(/\s([a-zA-Z]*\d)$/, ' vs $1').replace(/\s([a-zA-Z]*)$/, ' vs $1');
142        suff = suff.replace(/d([a-zA-Z0-9]*)/, "d$1/dt")
143        suff = suff.replace(/([a-zA-Z])(\d)$/g, '$1<sub>$2</sub>');
144        suff = suff.replace(/theta/g, "θ");
145        // Add "vs" before the last variable with subscript (or number)
146        label = suff.replace(/(\d)/, "<sub>$1</sub>")
147    } else if (rawSuffix) {
148        label = `Figure ${rawSuffix}`
149    } else {
150        label = id;
151    }
152    return label;
153}
154function addContainersToTOC(tocList, containers) {
155    containers.forEach(div => {
156        const id = div.id;
157        var suffix = id.replace(/^container/, "");
158        const infoEl = document.getElementById("info" + suffix);
159        let label = infoEl?.textContent.trim();
160        const content = document.getElementById(id).innerHTML;
161        if (!label) label = generateContLabel(id, content);
162        if (!div.id) return;
163        const li = document.createElement("li");
164        li.innerHTML = `<a href="#${id}">${label.replace(/container/gi, "animation").trim()}</a>`;
165        tocList.appendChild(li);
166    });
167}
168function createTOC() {
169    const tocPanel = document.getElementById("toc-panel");
170    tocPanel.innerHTML = '<ul id="toc-list" style="list-style: none; padding-left: 0;"></ul>';
171    const headers = [...document.querySelectorAll('h1, h2, h3:not(div.page-foot h3):not(div[id^="container"] h3), h4:not(div.page-foot h4), h5')];
172    const containers = [...document.querySelectorAll("div[id^='container']")];
173    const tables = [...document.querySelectorAll("table")];
174    const tocList = document.getElementById("toc-list");
175    addH1ToTOC(tocPanel, tocList, headers);
176    addHeadersToTOC(tocList, headers);
177    addTablesToTOC(tocList, tables);
178    addContainersToTOC(tocList, containers)
179}
180
181document.addEventListener("DOMContentLoaded", function () {
182    createTOC();
183    const sectionMap = new Map(); // ID → entry
184    const tocLinks = document.querySelectorAll("#toc-list a");
185    
186    const observer = new IntersectionObserver((entries) => {
187    entries.forEach(entry => {
188        const id = entry.target.getAttribute("id");
189        if (!id) return;
190
191        if (entry.isIntersecting) {
192            sectionMap.set(id, entry);
193        } else {
194            sectionMap.delete(id);
195        }
196    });
197
198    let bestId = null;
199    let bestRatio = 0;
200    let bestTop = Infinity;
201
202    sectionMap.forEach((entry, id) => {
203        const ratio = entry.intersectionRatio;
204        const top = entry.boundingClientRect.top;
205
206        // Prefer highest visible ratio, break ties with top position
207        if (ratio > bestRatio || (ratio === bestRatio && top < bestTop)) {
208            bestRatio = ratio;
209            bestTop = top;
210            bestId = id;
211        }
212    });
213
214    tocLinks.forEach(link => link.classList.remove("active"));
215    if (bestId) {
216        const activeLink = document.querySelector(`#toc-list a[href="#${bestId}"]`);
217        if (activeLink) activeLink.classList.add("active");
218    }
219    }, {
220        threshold: [0.1, 0.25, 0.5, 0.75], // tracks how much is visible
221        rootMargin: "0px 0px -30% 0px" // helps delay early triggering
222    });
223
224    document.querySelectorAll("div[id^='container'], a[id^='anchor-'], h1, h2, h3, h4, h5, table").forEach(container => {
225        observer.observe(container);
226    });
227});

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.