A loose federation of distributed, typed datasets
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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