PageSourceSearch

https://alemartinello.com/site_libs/quarto-html/quarto.js

js alemartinello.com collected 2026-09-24 10:40:43 UTC 28,947 bytes, 911 lines download raw bytes

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

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.