A loose federation of distributed, typed datasets
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}