1import * as tabsets from "./tabsets/tabsets.js"; 2import * as axe from "./axe/axe-check.js"; 3 4const sectionChanged = new CustomEvent("quarto-sectionChanged", { 5 detail: {}, 6 bubbles: true, 7 cancelable: false, 8 composed: false, 9}); 10 11const layoutMarginEls = () => { 12 // Find any conflicting margin elements and add margins to the 13 // top to prevent overlap 14 const marginChildren = window.document.querySelectorAll( 15 ".column-margin.column-container > *, .margin-caption, .aside" 16 ); 17 18 let lastBottom = 0; 19 for (const marginChild of marginChildren) { 20 if (marginChild.offsetParent !== null) { 21 // clear the top margin so we recompute it 22 marginChild.style.marginTop = null; 23 const top = marginChild.getBoundingClientRect().top + window.scrollY; 24 if (top < lastBottom) { 25 const marginChildStyle = window.getComputedStyle(marginChild); 26 const marginBottom = parseFloat(marginChildStyle["marginBottom"]); 27 const margin = lastBottom - top + marginBottom; 28 marginChild.style.marginTop = `${margin}px`; 29 } 30 const styles = window.getComputedStyle(marginChild); 31 const marginTop = parseFloat(styles["marginTop"]); 32 lastBottom = top + marginChild.getBoundingClientRect().height + marginTop; 33 } 34 } 35}; 36 37window.document.addEventListener("DOMContentLoaded", function (_event) { 38 // Recompute the position of margin elements anytime the body size changes 39 if (window.ResizeObserver) { 40 const resizeObserver = new window.ResizeObserver( 41 throttle(() => { 42 layoutMarginEls(); 43 if ( 44 window.document.body.getBoundingClientRect().width < 990 && 45 isReaderMode() 46 ) { 47 quartoToggleReader(); 48 } 49 }, 50) 50 ); 51 resizeObserver.observe(window.document.body); 52 } 53 54 const tocEl = window.document.querySelector('nav.toc-active[role="doc-toc"]'); 55 const sidebarEl = window.document.getElementById("quarto-sidebar"); 56 const leftTocEl = window.document.getElementById("quarto-sidebar-toc-left"); 57 const marginSidebarEl = window.document.getElementById( 58 "quarto-margin-sidebar" 59 ); 60 // function to determine whether the element has a previous sibling that is active 61 const prevSiblingIsActiveLink = (el) => { 62 const sibling = el.previousElementSibling; 63 if (sibling && sibling.tagName === "A") { 64 return sibling.classList.contains("active"); 65 } else { 66 return false; 67 } 68 }; 69 70 // dispatch for htmlwidgets 71 // they use slideenter event to trigger resize 72 function fireSlideEnter() { 73 const event = window.document.createEvent("Event"); 74 event.initEvent("slideenter", true, true); 75 window.document.dispatchEvent(event); 76 } 77 78 const tabs = window.document.querySelectorAll('a[data-bs-toggle="tab"]');
79 tabs.forEach((tab) => { 80 tab.addEventListener("shown.bs.tab", fireSlideEnter); 81 }); 82 83 // dispatch for shiny 84 // they use BS shown and hidden events to trigger rendering 85 function distpatchShinyEvents(previous, current) { 86 if (window.jQuery) { 87 if (previous) { 88 window.jQuery(previous).trigger("hidden"); 89 } 90 if (current) { 91 window.jQuery(current).trigger("shown"); 92 } 93 } 94 } 95 96 // tabby.js listener: Trigger event for htmlwidget and shiny 97 document.addEventListener( 98 "tabby", 99 function (event) { 100 fireSlideEnter(); 101 distpatchShinyEvents(event.detail.previousTab, event.detail.tab); 102 }, 103 false 104 ); 105 106 // Track scrolling and mark TOC links as active 107 // get table of contents and sidebar (bail if we don't have at least one) 108 const tocLinks = tocEl 109 ? [...tocEl.querySelectorAll("a[data-scroll-target]")] 110 : []; 111 const makeActive = (link) => tocLinks[link].classList.add("active"); 112 const removeActive = (link) => tocLinks[link].classList.remove("active"); 113 const removeAllActive = () => 114 [...Array(tocLinks.length).keys()].forEach((link) => removeActive(link)); 115 116 // activate the anchor for a section associated with this TOC entry 117 tocLinks.forEach((link) => { 118 link.addEventListener("click", () => { 119 if (link.href.indexOf("#") !== -1) { 120 const anchor = link.href.split("#")[1]; 121 const heading = window.document.querySelector( 122 `[data-anchor-id="${anchor}"]` 123 ); 124 if (heading) { 125 // Add the class 126 heading.classList.add("reveal-anchorjs-link"); 127 128 // function to show the anchor 129 const handleMouseout = () => { 130 heading.classList.remove("reveal-anchorjs-link"); 131 heading.removeEventListener("mouseout", handleMouseout); 132 }; 133 134 // add a function to clear the anchor when the user mouses out of it 135 heading.addEventListener("mouseout", handleMouseout); 136 } 137 } 138 }); 139 }); 140 141 const sections = tocLinks.map((link) => { 142 const target = link.getAttribute("data-scroll-target"); 143 if (target.startsWith("#")) { 144 return window.document.getElementById(decodeURI(`${target.slice(1)}`)); 145 } else { 146 return window.document.querySelector(decodeURI(`${target}`)); 147 } 148 }); 149 150 const sectionMargin = 200; 151 let currentActive = 0; 152 // track whether we've initialized state the first time 153 let init = false; 154 155 const updateActiveLink = () => { 156 // The index from bottom to top (e.g. reversed list) 157 let sectionIndex = -1; 158 if ( 159 window.innerHeight + window.pageYOffset >= 160 window.document.body.offsetHeight 161 ) { 162 // This is the no-scroll case where last section should be the active one 163 sectionIndex = 0; 164 } else { 165 // This finds the last section visible on screen that should be made active 166 sectionIndex = [...sections].reverse().findIndex((section) => { 167 if (section) { 168 return window.pageYOffset >= section.offsetTop - sectionMargin; 169 } else { 170 return false; 171 } 172 }); 173 } 174 if (sectionIndex > -1) { 175 const current = sections.length - sectionIndex - 1; 176 if (current !== currentActive) { 177 removeAllActive(); 178 currentActive = current; 179 makeActive(current); 180 if (init) { 181 window.dispatchEvent(sectionChanged); 182 } 183 init = true; 184 } 185 } 186 }; 187 188 const inHiddenRegion = (top, bottom, hiddenRegions) => { 189 for (const region of hiddenRegions) { 190 if (top <= region.bottom && bottom >= region.top) { 191 return true; 192 } 193 } 194 return false; 195 }; 196 197 const categorySelector = "header.quarto-title-block .quarto-category"; 198 const activateCategories = (href) => { 199 // Find any categories 200 // Surround them with a link pointing back to: 201 // #category=Authoring 202 try { 203 const categoryEls = window.document.querySelectorAll(categorySelector); 204 for (const categoryEl of categoryEls) { 205 const categoryText = categoryEl.textContent; 206 if (categoryText) { 207 const link = `${href}#category=${encodeURIComponent(categoryText)}`; 208 const linkEl = window.document.createElement("a"); 209 linkEl.setAttribute("href", link); 210 for (const child of categoryEl.childNodes) { 211 linkEl.append(child); 212 } 213 categoryEl.appendChild(linkEl); 214 } 215 } 216 } catch { 217 // Ignore errors 218 } 219 }; 220 function hasTitleCategories() { 221 return window.document.querySelector(categorySelector) !== null; 222 } 223 224 function offsetRelativeUrl(url) { 225 const offset = getMeta("quarto:offset"); 226 return offset ? offset + url : url; 227 } 228 229 function offsetAbsoluteUrl(url) { 230 const offset = getMeta("quarto:offset"); 231 const baseUrl = new URL(offset, window.location); 232 233 const projRelativeUrl = url.replace(baseUrl, ""); 234 if (projRelativeUrl.startsWith("/")) { 235 return projRelativeUrl; 236 } else { 237 return "/" + projRelativeUrl; 238 } 239 } 240 241 // read a meta tag value 242 function getMeta(metaName) { 243 const metas = window.document.getElementsByTagName("meta"); 244 for (let i = 0; i < metas.length; i++) { 245 if (metas[i].getAttribute("name") === metaName) { 246 return metas[i].getAttribute("content"); 247 } 248 } 249 return ""; 250 } 251 252 async function findAndActivateCategories() { 253 // Categories search with listing only use path without query 254 const currentPagePath = offsetAbsoluteUrl( 255 window.location.origin + window.location.pathname 256 ); 257 const response = await fetch(offsetRelativeUrl("listings.json")); 258 if (response.status == 200) { 259 return response.json().then(function (listingPaths) { 260 const listingHrefs = []; 261 for (const listingPath of listingPaths) { 262 const pathWithoutLeadingSlash = listingPath.listing.substring(1); 263 for (const item of listingPath.items) { 264 const encodedItem = encodeURI(item); 265 if ( 266 encodedItem === currentPagePath || 267 encodedItem === currentPagePath + "index.html" 268 ) { 269 // Resolve this path against the offset to be sure 270 // we already are using the correct path to the listing 271 // (this adjusts the listing urls to be rooted against 272 // whatever root the page is actually running against) 273 const relative = offsetRelativeUrl(pathWithoutLeadingSlash); 274 const baseUrl = window.location; 275 const resolvedPath = new URL(relative, baseUrl); 276 listingHrefs.push(resolvedPath.pathname); 277 break; 278 } 279 } 280 } 281 282 // Look up the tree for a nearby linting and use that if we find one 283 const nearestListing = findNearestParentListing( 284 offsetAbsoluteUrl(window.location.pathname), 285 listingHrefs 286 ); 287 if (nearestListing) { 288 activateCategories(nearestListing); 289 } else { 290 // See if the referrer is a listing page for this item 291 const referredRelativePath = offsetAbsoluteUrl(document.referrer); 292 const referrerListing = listingHrefs.find((listingHref) => { 293 const isListingReferrer = 294 listingHref === referredRelativePath || 295 listingHref === referredRelativePath + "index.html"; 296 return isListingReferrer; 297 }); 298 299 if (referrerListing) { 300 // Try to use the referrer if possible 301 activateCategories(referrerListing); 302 } else if (listingHrefs.length > 0) { 303 // Otherwise, just fall back to the first listing 304 activateCategories(listingHrefs[0]); 305 } 306 } 307 }); 308 } 309 } 310 if (hasTitleCategories()) { 311 findAndActivateCategories(); 312 } 313 314 const findNearestParentListing = (href, listingHrefs) => { 315 if (!href || !listingHrefs) { 316 return undefined; 317 } 318 // Look up the tree for a nearby linting and use that if we find one 319 const relativeParts = href.substring(1).split("/"); 320 while (relativeParts.length > 0) { 321 const path = relativeParts.join("/"); 322 for (const listingHref of listingHrefs) { 323 if (listingHref.startsWith(path)) { 324 return listingHref; 325 } 326 } 327 relativeParts.pop(); 328 } 329 330 return undefined; 331 }; 332 333 const manageSidebarVisiblity = (el, placeholderDescriptor) => { 334 let isVisible = true; 335 let elRect; 336 337 return (hiddenRegions) => { 338 if (el === null) { 339 return; 340 } 341 342 // Find the last element of the TOC 343 const lastChildEl = el.lastElementChild; 344 345 if (lastChildEl) { 346 // Converts the sidebar to a menu 347 const convertToMenu = () => { 348 for (const child of el.children) { 349 child.style.opacity = 0; 350 child.style.overflow = "hidden"; 351 child.style.pointerEvents = "none"; 352 } 353 354 nexttick(() => { 355 const toggleContainer = window.document.createElement("div"); 356 toggleContainer.style.width = "100%"; 357 toggleContainer.classList.add("zindex-over-content"); 358 toggleContainer.classList.add("quarto-sidebar-toggle"); 359 toggleContainer.classList.add("headroom-target"); // Marks this to be managed by headeroom 360 toggleContainer.id = placeholderDescriptor.id; 361 toggleContainer.style.position = "fixed"; 362 363 const toggleIcon = window.document.createElement("i"); 364 toggleIcon.classList.add("quarto-sidebar-toggle-icon"); 365 toggleIcon.classList.add("bi"); 366 toggleIcon.classList.add("bi-caret-down-fill"); 367 368 const toggleTitle = window.document.createElement("div"); 369 const titleEl = window.document.body.querySelector( 370 placeholderDescriptor.titleSelector 371 ); 372 if (titleEl) { 373 toggleTitle.append( 374 titleEl.textContent || titleEl.innerText, 375 toggleIcon 376 ); 377 } 378 toggleTitle.classList.add("zindex-over-content"); 379 toggleTitle.classList.add("quarto-sidebar-toggle-title"); 380 toggleContainer.append(toggleTitle); 381 382 const toggleContents = window.document.createElement("div"); 383 toggleContents.classList = el.classList; 384 toggleContents.classList.add("zindex-over-content"); 385 toggleContents.classList.add("quarto-sidebar-toggle-contents"); 386 for (const child of el.children) { 387 if (child.id === "toc-title") { 388 continue; 389 } 390 391 const clone = child.cloneNode(true); 392 clone.style.opacity = 1; 393 clone.style.pointerEvents = null; 394 clone.style.display = null; 395 toggleContents.append(clone); 396 } 397 toggleContents.style.height = "0px"; 398 const positionToggle = () => { 399 // position the element (top left of parent, same width as parent) 400 if (!elRect) { 401 elRect = el.getBoundingClientRect(); 402 } 403 toggleContainer.style.left = `${elRect.left}px`; 404 toggleContainer.style.top = `${elRect.top}px`; 405 toggleContainer.style.width = `${elRect.width}px`; 406 };
407 positionToggle(); 408 409 toggleContainer.append(toggleContents); 410 el.parentElement.prepend(toggleContainer); 411 412 // Process clicks 413 let tocShowing = false; 414 // Allow the caller to control whether this is dismissed 415 // when it is clicked (e.g. sidebar navigation supports 416 // opening and closing the nav tree, so don't dismiss on click) 417 const clickEl = placeholderDescriptor.dismissOnClick 418 ? toggleContainer 419 : toggleTitle; 420 421 const closeToggle = () => { 422 if (tocShowing) { 423 toggleContainer.classList.remove("expanded"); 424 toggleContents.style.height = "0px"; 425 tocShowing = false; 426 } 427 }; 428 429 // Get rid of any expanded toggle if the user scrolls 430 window.document.addEventListener( 431 "scroll", 432 throttle(() => { 433 closeToggle(); 434 }, 50) 435 ); 436 437 // Handle positioning of the toggle 438 window.addEventListener( 439 "resize", 440 throttle(() => { 441 elRect = undefined; 442 positionToggle(); 443 }, 50) 444 ); 445 446 window.addEventListener("quarto-hrChanged", () => { 447 elRect = undefined; 448 }); 449 450 // Process the click 451 clickEl.onclick = () => { 452 if (!tocShowing) { 453 toggleContainer.classList.add("expanded"); 454 toggleContents.style.height = null; 455 tocShowing = true; 456 } else { 457 closeToggle(); 458 } 459 }; 460 }); 461 }; 462 463 // Converts a sidebar from a menu back to a sidebar 464 const convertToSidebar = () => { 465 for (const child of el.children) { 466 child.style.opacity = 1; 467 child.style.overflow = null; 468 child.style.pointerEvents = null; 469 } 470 471 const placeholderEl = window.document.getElementById( 472 placeholderDescriptor.id 473 ); 474 if (placeholderEl) { 475 placeholderEl.remove(); 476 } 477 478 el.classList.remove("rollup"); 479 }; 480 481 if (isReaderMode()) { 482 convertToMenu(); 483 isVisible = false; 484 } else { 485 // Find the top and bottom o the element that is being managed 486 const elTop = el.offsetTop; 487 const elBottom = 488 elTop + lastChildEl.offsetTop + lastChildEl.offsetHeight; 489 490 if (!isVisible) { 491 // If the element is current not visible reveal if there are 492 // no conflicts with overlay regions 493 if (!inHiddenRegion(elTop, elBottom, hiddenRegions)) { 494 convertToSidebar(); 495 isVisible = true; 496 } 497 } else { 498 // If the element is visible, hide it if it conflicts with overlay regions 499 // and insert a placeholder toggle (or if we're in reader mode) 500 if (inHiddenRegion(elTop, elBottom, hiddenRegions)) { 501 convertToMenu(); 502 isVisible = false; 503 } 504 } 505 } 506 } 507 }; 508 }; 509 510 const tabEls = document.querySelectorAll('a[data-bs-toggle="tab"]'); 511 for (const tabEl of tabEls) { 512 const id = tabEl.getAttribute("data-bs-target"); 513 if (id) { 514 const columnEl = document.querySelector( 515 `${id} .column-margin, .tabset-margin-content` 516 ); 517 if (columnEl) 518 tabEl.addEventListener("shown.bs.tab", function (event) { 519 const el = event.srcElement; 520 if (el) { 521 const visibleCls = `${el.id}-margin-content`; 522 // walk up until we find a parent tabset 523 let panelTabsetEl = el.parentElement; 524 while (panelTabsetEl) { 525 if (panelTabsetEl.classList.contains("panel-tabset")) { 526 break; 527 } 528 panelTabsetEl = panelTabsetEl.parentElement; 529 } 530 531 if (panelTabsetEl) { 532 const prevSib = panelTabsetEl.previousElementSibling; 533 if ( 534 prevSib && 535 prevSib.classList.contains("tabset-margin-container") 536 ) { 537 const childNodes = prevSib.querySelectorAll( 538 ".tabset-margin-content" 539 ); 540 for (const childEl of childNodes) { 541 if (childEl.classList.contains(visibleCls)) { 542 childEl.classList.remove("collapse"); 543 } else { 544 childEl.classList.add("collapse"); 545 } 546 } 547 } 548 } 549 } 550 551 layoutMarginEls(); 552 }); 553 } 554 } 555 556 // Manage the visibility of the toc and the sidebar 557 const marginScrollVisibility = manageSidebarVisiblity(marginSidebarEl, { 558 id: "quarto-toc-toggle", 559 titleSelector: "#toc-title", 560 dismissOnClick: true, 561 }); 562 const sidebarScrollVisiblity = manageSidebarVisiblity(sidebarEl, { 563 id: "quarto-sidebarnav-toggle", 564 titleSelector: ".title", 565 dismissOnClick: false, 566 }); 567 let tocLeftScrollVisibility; 568 if (leftTocEl) { 569 tocLeftScrollVisibility = manageSidebarVisiblity(leftTocEl, { 570 id: "quarto-lefttoc-toggle", 571 titleSelector: "#toc-title",
572 dismissOnClick: true, 573 }); 574 } 575 576 // Find the first element that uses formatting in special columns 577 const conflictingEls = window.document.body.querySelectorAll( 578 '[class^="column-"], [class*=" column-"], aside, [class*="margin-caption"], [class*=" margin-caption"], [class*="margin-ref"], [class*=" margin-ref"]' 579 ); 580 581 // Filter all the possibly conflicting elements into ones 582 // the do conflict on the left or ride side 583 const arrConflictingEls = Array.from(conflictingEls); 584 const leftSideConflictEls = arrConflictingEls.filter((el) => { 585 if (el.tagName === "ASIDE") { 586 return false; 587 } 588 return Array.from(el.classList).find((className) => { 589 return ( 590 className !== "column-body" && 591 className.startsWith("column-") && 592 !className.endsWith("right") && 593 !className.endsWith("container") && 594 className !== "column-margin" 595 ); 596 }); 597 }); 598 const rightSideConflictEls = arrConflictingEls.filter((el) => { 599 if (el.tagName === "ASIDE") { 600 return true; 601 } 602 603 const hasMarginCaption = Array.from(el.classList).find((className) => { 604 return className == "margin-caption"; 605 }); 606 if (hasMarginCaption) { 607 return true; 608 } 609 610 return Array.from(el.classList).find((className) => { 611 return ( 612 className !== "column-body" && 613 !className.endsWith("container") && 614 className.startsWith("column-") && 615 !className.endsWith("left") 616 ); 617 }); 618 }); 619 620 const kOverlapPaddingSize = 10; 621 function toRegions(els) { 622 return els.map((el) => { 623 const boundRect = el.getBoundingClientRect(); 624 const top = 625 boundRect.top + 626 document.documentElement.scrollTop - 627 kOverlapPaddingSize; 628 return { 629 top, 630 bottom: top + el.scrollHeight + 2 * kOverlapPaddingSize, 631 }; 632 }); 633 } 634 635 let hasObserved = false; 636 const visibleItemObserver = (els) => { 637 let visibleElements = [...els]; 638 const intersectionObserver = new IntersectionObserver( 639 (entries, _observer) => { 640 entries.forEach((entry) => { 641 if (entry.isIntersecting) { 642 if (visibleElements.indexOf(entry.target) === -1) { 643 visibleElements.push(entry.target); 644 } 645 } else { 646 visibleElements = visibleElements.filter((visibleEntry) => { 647 return visibleEntry !== entry; 648 }); 649 } 650 }); 651 652 if (!hasObserved) { 653 hideOverlappedSidebars(); 654 } 655 hasObserved = true; 656 }, 657 {} 658 ); 659 els.forEach((el) => { 660 intersectionObserver.observe(el); 661 }); 662 663 return { 664 getVisibleEntries: () => { 665 return visibleElements; 666 }, 667 }; 668 }; 669 670 const rightElementObserver = visibleItemObserver(rightSideConflictEls); 671 const leftElementObserver = visibleItemObserver(leftSideConflictEls); 672 673 const hideOverlappedSidebars = () => { 674 marginScrollVisibility(toRegions(rightElementObserver.getVisibleEntries())); 675 sidebarScrollVisiblity(toRegions(leftElementObserver.getVisibleEntries())); 676 if (tocLeftScrollVisibility) { 677 tocLeftScrollVisibility( 678 toRegions(leftElementObserver.getVisibleEntries()) 679 ); 680 } 681 }; 682 683 window.quartoToggleReader = () => { 684 // Applies a slow class (or removes it) 685 // to update the transition speed 686 const slowTransition = (slow) => { 687 const manageTransition = (id, slow) => { 688 const el = document.getElementById(id); 689 if (el) { 690 if (slow) { 691 el.classList.add("slow"); 692 } else { 693 el.classList.remove("slow"); 694 } 695 } 696 }; 697 698 manageTransition("TOC", slow); 699 manageTransition("quarto-sidebar", slow); 700 }; 701 const readerMode = !isReaderMode(); 702 setReaderModeValue(readerMode); 703 704 // If we're entering reader mode, slow the transition 705 if (readerMode) { 706 slowTransition(readerMode); 707 } 708 highlightReaderToggle(readerMode); 709 hideOverlappedSidebars(); 710 711 // If we're exiting reader mode, restore the non-slow transition 712 if (!readerMode) { 713 slowTransition(!readerMode); 714 } 715 }; 716 717 const highlightReaderToggle = (readerMode) => { 718 const els = document.querySelectorAll(".quarto-reader-toggle"); 719 if (els) {
720 els.forEach((el) => { 721 if (readerMode) { 722 el.classList.add("reader"); 723 } else { 724 el.classList.remove("reader"); 725 } 726 }); 727 } 728 }; 729 730 const setReaderModeValue = (val) => { 731 if (window.location.protocol !== "file:") { 732 window.localStorage.setItem("quarto-reader-mode", val); 733 } else { 734 localReaderMode = val; 735 } 736 }; 737 738 const isReaderMode = () => { 739 if (window.location.protocol !== "file:") { 740 return window.localStorage.getItem("quarto-reader-mode") === "true"; 741 } else { 742 return localReaderMode; 743 } 744 }; 745 let localReaderMode = null; 746 747 const tocOpenDepthStr = tocEl?.getAttribute("data-toc-expanded"); 748 const tocOpenDepth = tocOpenDepthStr ? Number(tocOpenDepthStr) : 1; 749 750 // Walk the TOC and collapse/expand nodes 751 // Nodes are expanded if: 752 // - they are top level 753 // - they have children that are 'active' links 754 // - they are directly below an link that is 'active' 755 const walk = (el, depth) => { 756 // Tick depth when we enter a UL 757 if (el.tagName === "UL") { 758 depth = depth + 1; 759 } 760 761 // It this is active link 762 let isActiveNode = false; 763 if (el.tagName === "A" && el.classList.contains("active")) { 764 isActiveNode = true; 765 } 766 767 // See if there is an active child to this element 768 let hasActiveChild = false; 769 for (const child of el.children) { 770 hasActiveChild = walk(child, depth) || hasActiveChild; 771 } 772 773 // Process the collapse state if this is an UL 774 if (el.tagName === "UL") { 775 if (tocOpenDepth === -1 && depth > 1) { 776 // toc-expand: false 777 el.classList.add("collapse"); 778 } else if ( 779 depth <= tocOpenDepth || 780 hasActiveChild || 781 prevSiblingIsActiveLink(el) 782 ) { 783 el.classList.remove("collapse"); 784 } else { 785 el.classList.add("collapse"); 786 } 787 788 // untick depth when we leave a UL 789 depth = depth - 1; 790 } 791 return hasActiveChild || isActiveNode; 792 }; 793 794 // walk the TOC and expand / collapse any items that should be shown 795 if (tocEl) { 796 updateActiveLink(); 797 walk(tocEl, 0); 798 } 799 800 // Throttle the scroll event and walk peridiocally 801 window.document.addEventListener( 802 "scroll", 803 throttle(() => { 804 if (tocEl) { 805 updateActiveLink(); 806 walk(tocEl, 0); 807 } 808 if (!isReaderMode()) { 809 hideOverlappedSidebars(); 810 } 811 }, 5) 812 ); 813 window.addEventListener( 814 "resize", 815 throttle(() => { 816 if (tocEl) { 817 updateActiveLink(); 818 walk(tocEl, 0); 819 } 820 if (!isReaderMode()) { 821 hideOverlappedSidebars(); 822 } 823 }, 10) 824 ); 825 hideOverlappedSidebars(); 826 highlightReaderToggle(isReaderMode()); 827}); 828 829tabsets.init(); 830axe.init(); 831 832function throttle(func, wait) { 833 let waiting = false; 834 return function () { 835 if (!waiting) { 836 func.apply(this, arguments); 837 waiting = true; 838 setTimeout(function () { 839 waiting = false; 840 }, wait); 841 } 842 }; 843} 844 845function nexttick(func) { 846 return setTimeout(func, 0); 847}
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.