PageSourceSearch

https://sayak.dev/site_libs/quarto-html/quarto.js

js sayak.dev collected 2026-09-24 10:18:51 UTC 26,830 bytes, 845 lines download raw bytes

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}

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.