1(function (window, document) { 2 'use strict'; 3 4 var ARTICLE_SELECTOR = 'article.post-content'; 5 var HEADING_SELECTOR = 'h1, h2, h3, h4, h5, h6'; 6 var DEFAULT_MIN_HEADING_COUNT = 4; 7 var BLOG_MIN_HEADING_COUNT = 2; 8 var MANUAL_TOC_MIN_LINK_COUNT = 4; 9 var hasInitialized = false; 10 11 function normalizePath(path) { 12 if (!path) { 13 return '/'; 14 } 15 return path.toLowerCase(); 16 } 17 18 function isHomePage(path) { 19 return path === '/' || path === '/index.html'; 20 } 21 22 function isBlogArticle(path) { 23 return path.indexOf('/blog/') === 0 && path !== '/blog/' && path !== '/blog/index.html'; 24 } 25 26 function getMinimumHeadingCount(path) { 27 if (isBlogArticle(path)) { 28 return BLOG_MIN_HEADING_COUNT; 29 } 30 return DEFAULT_MIN_HEADING_COUNT; 31 } 32 33 function normalizeText(text) { 34 return (text || '').replace(/\s+/g, ' ').trim(); 35 } 36 37 function getHeadingText(node) { 38 var clone; 39 var removable; 40 var i; 41 42 if (!node) { 43 return ''; 44 } 45 46 clone = node.cloneNode(true); 47 removable = clone.querySelectorAll( 48 '.heading-anchor-link, .sr-only, a.anchor, a.headerlink, a.anchorjs-link' 49 ); 50 51 for (i = 0; i < removable.length; i += 1) { 52 removeNode(removable[i]); 53 } 54 55 return normalizeText(clone.textContent); 56 } 57 58 function headingLevel(node) { 59 var level = parseInt(node.tagName.substring(1), 10); 60 if (isNaN(level)) { 61 return 6; 62 } 63 return Math.max(1, Math.min(6, level)); 64 } 65 66 function parseLevel(value) { 67 var level = parseInt(value, 10); 68 if (isNaN(level)) { 69 return null; 70 } 71 return Math.max(1, Math.min(6, level)); 72 } 73 74 function isTruthy(value) { 75 if (typeof value !== 'string') { 76 return false; 77 } 78 value = value.toLowerCase(); 79 return value === '1' || value === 'true' || value === 'yes'; 80 } 81 82 function filterHeadingsByLevel(headings, article) { 83 var highestLevel = 6; 84 var minLevel = parseLevel(article.getAttribute('data-toc-min-level')); 85 var maxLevel = parseLevel(article.getAttribute('data-toc-max-level')); 86 var highestOnly = isTruthy(article.getAttribute('data-toc-highest-only')); 87 var filtered = []; 88 var i; 89 var level; 90 91 if (!headings.length) { 92 return headings; 93 } 94 95 for (i = 0; i < headings.length; i += 1) { 96 level = headingLevel(headings[i]); 97 if (level < highestLevel) { 98 highestLevel = level; 99 } 100 } 101 102 if (highestOnly) { 103 minLevel = highestLevel; 104 maxLevel = highestLevel; 105 } else { 106 if (minLevel === null) { 107 minLevel = 1; 108 } 109 if (maxLevel === null) { 110 maxLevel = 6; 111 } 112 if (minLevel > maxLevel) { 113 level = minLevel; 114 minLevel = maxLevel; 115 maxLevel = level; 116 } 117 } 118 119 for (i = 0; i < headings.length; i += 1) { 120 level = headingLevel(headings[i]); 121 if (level >= minLevel && level <= maxLevel) { 122 filtered.push(headings[i]); 123 } 124 } 125 126 return filtered; 127 } 128 129 function isManualTocHeading(node) { 130 var text = getHeadingText(node).toLowerCase(); 131 return text === 'table of contents' || text === 'contents' || text === 'sections'; 132 } 133 134 function isInternalAnchorList(node) { 135 var links; 136 var i; 137 var href; 138 139 if (!node || (node.tagName !== 'UL' && node.tagName !== 'OL')) { 140 return false; 141 } 142 143 links = node.querySelectorAll('a[href]'); 144 if (links.length < MANUAL_TOC_MIN_LINK_COUNT) { 145 return false; 146 } 147 148 for (i = 0; i < links.length; i += 1) { 149 href = links[i].getAttribute('href') || ''; 150 if (href.charAt(0) !== '#') { 151 return false; 152 } 153 } 154 155 return true; 156 } 157 158 function removeNode(node) { 159 if (node && node.parentNode) { 160 node.parentNode.removeChild(node); 161 } 162 } 163 164 function cleanupEmptyParagraph(node) { 165 if (!node || node.tagName !== 'P') { 166 return; 167 } 168 if (!normalizeText(node.textContent) && !node.querySelector('img, video, iframe, table, pre, code')) { 169 removeNode(node); 170 } 171 } 172 173 function removeHardcodedToc(article) { 174 var i; 175 var anchor; 176 var heading; 177 var next; 178 var markers = article.querySelectorAll('ul#markdown-toc, ol#markdown-toc, details.llms-toc, details.rfs-toc'); 179 var tocHeadings = article.querySelectorAll(HEADING_SELECTOR); 180 var children = article.children; 181 var firstHeading = article.querySelector(HEADING_SELECTOR); 182 183 for (i = 0; i < markers.length; i += 1) { 184 removeNode(markers[i]); 185 } 186 187 anchor = article.querySelector('a#table-of-contents'); 188 if (anchor) { 189 heading = anchor.parentNode; 190 removeNode(anchor); 191 cleanupEmptyParagraph(heading); 192 } 193 194 for (i = 0; i < tocHeadings.length; i += 1) { 195 heading = tocHeadings[i]; 196 if (!isManualTocHeading(heading)) { 197 continue; 198 } 199 next = heading.nextElementSibling; 200 if (isInternalAnchorList(next)) { 201 removeNode(next); 202 removeNode(heading); 203 } 204 } 205 206 for (i = 0; i < children.length; i += 1) { 207 if (firstHeading && children[i] === firstHeading) { 208 break; 209 } 210 if (isInternalAnchorList(children[i])) { 211 removeNode(children[i]); 212 break; 213 } 214 } 215 } 216 217 function slugify(text) { 218 var slug = normalizeText(text) 219 .toLowerCase() 220 .replace(/[^a-z0-9\s-]/g, '') 221 .replace(/\s+/g, '-') 222 .replace(/-+/g, '-') 223 .replace(/^-|-$/g, ''); 224 return slug || 'section'; 225 } 226 227 function listContains(nodes, node) { 228 var i; 229 for (i = 0; i < nodes.length; i += 1) { 230 if (nodes[i] === node) { 231 return true; 232 } 233 } 234 return false;
235 } 236 237 function collectHeadings(article) { 238 var nodes = article.querySelectorAll(HEADING_SELECTOR); 239 var headings = []; 240 var i; 241 242 for (i = 0; i < nodes.length; i += 1) { 243 if (nodes[i].closest && nodes[i].closest('[data-generated-toc]')) { 244 continue; 245 } 246 if (isManualTocHeading(nodes[i])) { 247 continue; 248 } 249 if (!getHeadingText(nodes[i])) { 250 continue; 251 } 252 headings.push(nodes[i]); 253 } 254 255 return headings; 256 } 257 258 function usedIdsOutsideHeadings(headings) { 259 var ids = Object.create(null); 260 var nodes = document.querySelectorAll('[id]'); 261 var i; 262 var nodeId; 263 264 for (i = 0; i < nodes.length; i += 1) { 265 if (listContains(headings, nodes[i])) { 266 continue; 267 } 268 nodeId = nodes[i].getAttribute('id'); 269 if (nodeId) { 270 ids[nodeId] = true; 271 } 272 } 273 274 return ids; 275 } 276 277 function ensureHeadingIds(headings) { 278 var ids = usedIdsOutsideHeadings(headings); 279 var i; 280 var id; 281 var base; 282 var candidate; 283 var counter; 284 285 for (i = 0; i < headings.length; i += 1) { 286 id = headings[i].getAttribute('id'); 287 if (id && !ids[id]) { 288 ids[id] = true; 289 continue; 290 } 291 292 base = slugify(getHeadingText(headings[i])); 293 candidate = base; 294 counter = 2; 295 296 while (ids[candidate]) { 297 candidate = base + '-' + counter; 298 counter += 1; 299 } 300 301 headings[i].setAttribute('id', candidate); 302 ids[candidate] = true; 303 } 304 } 305 306 function shouldCollapseTocByDefault() { 307 return true; 308 } 309 310 function createToc(article, headings, collapseByDefault) { 311 var details = document.createElement('details'); 312 var summary = document.createElement('summary'); 313 var title = document.createElement('span'); 314 var nav = document.createElement('nav'); 315 var list = document.createElement('ol'); 316 var minLevel = 6; 317 var i; 318 var level; 319 var item; 320 var link; 321 322 details.className = 'article-toc'; 323 details.setAttribute('data-generated-toc', 'true'); 324 if (!document.getElementById('table-of-contents')) { 325 details.id = 'table-of-contents'; 326 } 327 if ( 328 !collapseByDefault && 329 !(window.matchMedia && window.matchMedia('(max-width: 860px)').matches) 330 ) { 331 details.open = true; 332 } 333 334 summary.className = 'article-toc__summary'; 335 title.className = 'article-toc__title'; 336 title.textContent = 'Table of contents'; 337 summary.appendChild(title); 338 details.appendChild(summary); 339 340 nav.className = 'article-toc__nav'; 341 nav.setAttribute('aria-label', 'Table of contents'); 342 list.className = 'article-toc__list'; 343 344 for (i = 0; i < headings.length; i += 1) { 345 level = headingLevel(headings[i]); 346 if (level < minLevel) { 347 minLevel = level; 348 } 349 } 350 351 for (i = 0; i < headings.length; i += 1) { 352 level = headingLevel(headings[i]); 353 item = document.createElement('li'); 354 item.className = 'article-toc__item article-toc__item--level-' + level; 355 item.style.marginLeft = Math.max(0, (level - minLevel) * 14) + 'px'; 356 357 link = document.createElement('a'); 358 link.className = 'article-toc__link'; 359 link.setAttribute('href', '#' + headings[i].id); 360 link.textContent = getHeadingText(headings[i]); 361 362 item.appendChild(link); 363 list.appendChild(item); 364 } 365 366 nav.appendChild(list); 367 details.appendChild(nav); 368 article.insertBefore(details, article.firstChild); 369 370 return details; 371 } 372 373 function updateActiveLink(details) { 374 var hash = window.location.hash ? decodeURIComponent(window.location.hash.slice(1)) : ''; 375 var links = details.querySelectorAll('.article-toc__link'); 376 var i; 377 var hrefId; 378 379 for (i = 0; i < links.length; i += 1) { 380 hrefId = decodeURIComponent((links[i].getAttribute('href') || '').replace(/^#/, '')); 381 if (hash && hrefId === hash) { 382 links[i].classList.add('is-active'); 383 } else { 384 links[i].classList.remove('is-active'); 385 } 386 } 387 } 388 389 function init() { 390 if (hasInitialized) { 391 return; 392 } 393 hasInitialized = true; 394 395 var articles = document.querySelectorAll(ARTICLE_SELECTOR); 396 var path = normalizePath(window.location.pathname); 397 var minimumHeadingCount = getMinimumHeadingCount(path); 398 var i; 399 var headings; 400 var collapseByDefault; 401 var toc; 402 403 if (isHomePage(path)) { 404 return; 405 } 406 407 for (i = 0; i < articles.length; i += 1) { 408 if (articles[i].querySelector('[data-generated-toc]')) { 409 continue; 410 } 411 412 removeHardcodedToc(articles[i]); 413 headings = collectHeadings(articles[i]); 414 headings = filterHeadingsByLevel(headings, articles[i]); 415 if (headings.length < minimumHeadingCount) { 416 continue; 417 } 418 419 ensureHeadingIds(headings); 420 collapseByDefault = shouldCollapseTocByDefault(); 421 toc = createToc(articles[i], headings, collapseByDefault); 422 updateActiveLink(toc); 423 424 window.addEventListener('hashchange', (function (node) { 425 return function () { 426 updateActiveLink(node); 427 }; 428 })(toc)); 429 } 430 } 431 432 window.ArticleToc = { 433 init: init 434 }; 435 436 if (document.readyState === 'loading') { 437 document.addEventListener('DOMContentLoaded', init); 438 } else { 439 init(); 440 } 441})(window, document);
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.