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.