PageSourceSearch

https://lucialaford.ca/site_libs/quarto-html/quarto.js

js lucialaford.ca collected 2026-09-29 07:07:27 UTC 28,840 bytes, 908 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    const currentPagePath = offsetAbsoluteUrl(window.location.href);
229    const response = await fetch(offsetRelativeUrl("listings.json"));
230    if (response.status == 200) {
231      return response.json().then(function (listingPaths) {
232        const listingHrefs = [];
233        for (const listingPath of listingPaths) {
234          const pathWithoutLeadingSlash = listingPath.listing.substring(1);
235          for (const item of listingPath.items) {
236            if (
237              item === currentPagePath ||
238              item === currentPagePath + "index.html"
239            ) {
240              // Resolve this path against the offset to be sure
241              // we already are using the correct path to the listing
242              // (this adjusts the listing urls to be rooted against
243              // whatever root the page is actually running against)
244              const relative = offsetRelativeUrl(pathWithoutLeadingSlash);
245              const baseUrl = window.location;
246              const resolvedPath = new URL(relative, baseUrl);
247              listingHrefs.push(resolvedPath.pathname);
248              break;
249            }
250          }
251        }
252
253        // Look up the tree for a nearby linting and use that if we find one
254        const nearestListing = findNearestParentListing(
255          offsetAbsoluteUrl(window.location.pathname),
256          listingHrefs
257        );
258        if (nearestListing) {
259          activateCategories(nearestListing);
260        } else {
261          // See if the referrer is a listing page for this item
262          const referredRelativePath = offsetAbsoluteUrl(document.referrer);
263          const referrerListing = listingHrefs.find((listingHref) => {
264            const isListingReferrer =
265              listingHref === referredRelativePath ||
266              listingHref === referredRelativePath + "index.html";
267            return isListingReferrer;
268          });
269
270          if (referrerListing) {
271            // Try to use the referrer if possible
272            activateCategories(referrerListing);
273          } else if (listingHrefs.length > 0) {
274            // Otherwise, just fall back to the first listing
275            activateCategories(listingHrefs[0]);
276          }
277        }
278      });
279    }
280  }
281  if (hasTitleCategories()) {
282    findAndActivateCategories();
283  }
284
285  const findNearestParentListing = (href, listingHrefs) => {
286    if (!href || !listingHrefs) {
287      return undefined;
288    }
289    // Look up the tree for a nearby linting and use that if we find one
290    const relativeParts = href.substring(1).split("/");
291    while (relativeParts.length > 0) {
292      const path = relativeParts.join("/");
293      for (const listingHref of listingHrefs) {
294        if (listingHref.startsWith(path)) {
295          return listingHref;
296        }
297      }
298      relativeParts.pop();
299    }
300
301    return undefined;
302  };
303
304  const manageSidebarVisiblity = (el, placeholderDescriptor) => {
305    let isVisible = true;
306    let elRect;
307
308    return (hiddenRegions) => {
309      if (el === null) {
310        return;
311      }
312
313      // Find the last element of the TOC
314      const lastChildEl = el.lastElementChild;
315
316      if (lastChildEl) {
317        // Converts the sidebar to a menu
318        const convertToMenu = () => {
319          for (const child of el.children) {
320            child.style.opacity = 0;
321            child.style.overflow = "hidden";
322            child.style.pointerEvents = "none";
323          }
324
325          nexttick(() => {
326            const toggleContainer = window.document.createElement("div");
327            toggleContainer.style.width = "100%";
328            toggleContainer.classList.add("zindex-over-content");
329            toggleContainer.classList.add("quarto-sidebar-toggle");
330            toggleContainer.classList.add("headroom-target"); // Marks this to be managed by headeroom
331            toggleContainer.id = placeholderDescriptor.id;
332            toggleContainer.style.position = "fixed";
333
334            const toggleIcon = window.document.createElement("i");
335            toggleIcon.classList.add("quarto-sidebar-toggle-icon");
336            toggleIcon.classList.add("bi");
337            toggleIcon.classList.add("bi-caret-down-fill");
338
339            const toggleTitle = window.document.createElement("div");
340            const titleEl = window.document.body.querySelector(
341              placeholderDescriptor.titleSelector
342            );
343            if (titleEl) {
344              toggleTitle.append(
345                titleEl.textContent || titleEl.innerText,
346                toggleIcon
347              );
348            }
349            toggleTitle.classList.add("zindex-over-content");
350            toggleTitle.classList.add("quarto-sidebar-toggle-title");
351            toggleContainer.append(toggleTitle);
352
353            const toggleContents = window.document.createElement("div");
354            toggleContents.classList = el.classList;
355            toggleContents.classList.add("zindex-over-content");
356            toggleContents.classList.add("quarto-sidebar-toggle-contents");
357            for (const child of el.children) {
358              if (child.id === "toc-title") {
359                continue;
360              }
361
362              const clone = child.cloneNode(true);
363              clone.style.opacity = 1;
364              clone.style.pointerEvents = null;
365              clone.style.display = null;
366              toggleContents.append(clone);
367            }
368            toggleContents.style.height = "0px";
369            const positionToggle = () => {
370              // position the element (top left of parent, same width as parent)
371              if (!elRect) {
372                elRect = el.getBoundingClientRect();
373              }
374              toggleContainer.style.left = `${elRect.left}px`;
375              toggleContainer.style.top = `${elRect.top}px`;
376              toggleContainer.style.width = `${elRect.width}px`;
377            };
378            positionToggle();
379
380            toggleContainer.append(toggleContents);
381            el.parentElement.prepend(toggleContainer);
382
383            // Process clicks
384            let tocShowing = false;
385            // Allow the caller to control whether this is dismissed
386            // when it is clicked (e.g. sidebar navigation supports
387            // opening and closing the nav tree, so don't dismiss on click)
388            const clickEl = placeholderDescriptor.dismissOnClick
389              ? toggleContainer
390              : toggleTitle;
391
392            const closeToggle = () => {
393              if (tocShowing) {
394                toggleContainer.classList.remove("expanded");
395                toggleContents.style.height = "0px";
396                tocShowing = false;
397              }
398            };
399
400            // Get rid of any expanded toggle if the user scrolls
401            window.document.addEventListener(
402              "scroll",
403              throttle(() => {
404                closeToggle();
405              }, 50)
406            );
407
408            // Handle positioning of the toggle
409            window.addEventListener(
410              "resize",
411              throttle(() => {
412                elRect = undefined;
413                positionToggle();
414              }, 50)
415            );
416
417            window.addEventListener("quarto-hrChanged", () => {
418              elRect = undefined;
419            });
420
421            // Process the click
422            clickEl.onclick = () => {
423              if (!tocShowing) {
424                toggleContainer.classList.add("expanded");
425                toggleContents.style.height = null;
426                tocShowing = true;
427              } else {
428                closeToggle();
429              }
430            };
431          });
432        };
433
434        // Converts a sidebar from a menu back to a sidebar
435        const convertToSidebar = () => {
436          for (const child of el.children) {
437            child.style.opacity = 1;
438            child.style.overflow = null;
439            child.style.pointerEvents = null;
440          }
441
442          const placeholderEl = window.document.getElementById(
443            placeholderDescriptor.id
444          );
445          if (placeholderEl) {
446            placeholderEl.remove();
447          }
448
449          el.classList.remove("rollup");
450        };
451
452        if (isReaderMode()) {
453          convertToMenu();
454          isVisible = false;
455        } else {
456          // Find the top and bottom o the element that is being managed
457          const elTop = el.offsetTop;
458          const elBottom =
459            elTop + lastChildEl.offsetTop + lastChildEl.offsetHeight;
460
461          if (!isVisible) {
462            // If the element is current not visible reveal if there are
463            // no conflicts with overlay regions
464            if (!inHiddenRegion(elTop, elBottom, hiddenRegions)) {
465              convertToSidebar();
466              isVisible = true;
467            }
468          } else {
469            // If the element is visible, hide it if it conflicts with overlay regions
470            // and insert a placeholder toggle (or if we're in reader mode)
471            if (inHiddenRegion(elTop, elBottom, hiddenRegions)) {
472              convertToMenu();
473              isVisible = false;
474            }
475          }
476        }
477      }
478    };
479  };
480
481  const tabEls = document.querySelectorAll('a[data-bs-toggle="tab"]');
482  for (const tabEl of tabEls) {
483    const id = tabEl.getAttribute("data-bs-target");
484    if (id) {
485      const columnEl = document.querySelector(
486        `${id} .column-margin, .tabset-margin-content`
487      );
488      if (columnEl)
489        tabEl.addEventListener("shown.bs.tab", function (event) {
490          const el = event.srcElement;
491          if (el) {
492            const visibleCls = `${el.id}-margin-content`;
493            // walk up until we find a parent tabset
494            let panelTabsetEl = el.parentElement;
495            while (panelTabsetEl) {
496              if (panelTabsetEl.classList.contains("panel-tabset")) {
497                break;
498              }
499              panelTabsetEl = panelTabsetEl.parentElement;
500            }
501
502            if (panelTabsetEl) {
503              const prevSib = panelTabsetEl.previousElementSibling;
504              if (
505                prevSib &&
506                prevSib.classList.contains("tabset-margin-container")
507              ) {
508                const childNodes = prevSib.querySelectorAll(
509                  ".tabset-margin-content"
510                );
511                for (const childEl of childNodes) {
512                  if (childEl.classList.contains(visibleCls)) {
513                    childEl.classList.remove("collapse");
514                  } else {
515                    childEl.classList.add("collapse");
516                  }
517                }
518              }
519            }
520          }
521
522          layoutMarginEls();
523        });
524    }
525  }
526
527  // Manage the visibility of the toc and the sidebar
528  const marginScrollVisibility = manageSidebarVisiblity(marginSidebarEl, {
529    id: "quarto-toc-toggle",
530    titleSelector: "#toc-title",
531    dismissOnClick: true,
532  });
533  const sidebarScrollVisiblity = manageSidebarVisiblity(sidebarEl, {
534    id: "quarto-sidebarnav-toggle",
535    titleSelector: ".title",
536    dismissOnClick: false,
537  });
538  let tocLeftScrollVisibility;
539  if (leftTocEl) {
540    tocLeftScrollVisibility = manageSidebarVisiblity(leftTocEl, {
541      id: "quarto-lefttoc-toggle",
542      titleSelector: "#toc-title",
543      dismissOnClick: true,
544    });
545  }
546
547  // Find the first element that uses formatting in special columns
548  const conflictingEls = window.document.body.querySelectorAll(
549    '[class^="column-"], [class*=" column-"], aside, [class*="margin-caption"], [class*=" margin-caption"], [class*="margin-ref"], [class*=" margin-ref"]'
550  );
551
552  // Filter all the possibly conflicting elements into ones
553  // the do conflict on the left or ride side
554  const arrConflictingEls = Array.from(conflictingEls);
555  const leftSideConflictEls = arrConflictingEls.filter((el) => {
556    if (el.tagName === "ASIDE") {
557      return false;
558    }
559    return Array.from(el.classList).find((className) => {
560      return (
561        className !== "column-body" &&
562        className.startsWith("column-") &&
563        !className.endsWith("right") &&
564        !className.endsWith("container") &&
565        className !== "column-margin"
566      );
567    });
568  });
569  const rightSideConflictEls = arrConflictingEls.filter((el) => {
570    if (el.tagName === "ASIDE") {
571      return true;
572    }
573
574    const hasMarginCaption = Array.from(el.classList).find((className) => {
575      return className == "margin-caption";
576    });
577    if (hasMarginCaption) {
578      return true;
579    }
580
581    return Array.from(el.classList).find((className) => {
582      return (
583        className !== "column-body" &&
584        !className.endsWith("container") &&
585        className.startsWith("column-") &&
586        !className.endsWith("left")
587      );
588    });
589  });
590
591  const kOverlapPaddingSize = 10;
592  function toRegions(els) {
593    return els.map((el) => {
594      const boundRect = el.getBoundingClientRect();
595      const top =
596        boundRect.top +
597        document.documentElement.scrollTop -
598        kOverlapPaddingSize;
599      return {
600        top,
601        bottom: top + el.scrollHeight + 2 * kOverlapPaddingSize,
602      };
603    });
604  }
605
606  let hasObserved = false;
607  const visibleItemObserver = (els) => {
608    let visibleElements = [...els];
609    const intersectionObserver = new IntersectionObserver(
610      (entries, _observer) => {
611        entries.forEach((entry) => {
612          if (entry.isIntersecting) {
613            if (visibleElements.indexOf(entry.target) === -1) {
614              visibleElements.push(entry.target);
615            }
616          } else {
617            visibleElements = visibleElements.filter((visibleEntry) => {
618              return visibleEntry !== entry;
619            });
620          }
621        });
622
623        if (!hasObserved) {
624          hideOverlappedSidebars();
625        }
626        hasObserved = true;
627      },
628      {}
629    );
630    els.forEach((el) => {
631      intersectionObserver.observe(el);
632    });
633
634    return {
635      getVisibleEntries: () => {
636        return visibleElements;
637      },
638    };
639  };
640
641  const rightElementObserver = visibleItemObserver(rightSideConflictEls);
642  const leftElementObserver = visibleItemObserver(leftSideConflictEls);
643
644  const hideOverlappedSidebars = () => {
645    marginScrollVisibility(toRegions(rightElementObserver.getVisibleEntries()));
646    sidebarScrollVisiblity(toRegions(leftElementObserver.getVisibleEntries()));
647    if (tocLeftScrollVisibility) {
648      tocLeftScrollVisibility(
649        toRegions(leftElementObserver.getVisibleEntries())
650      );
651    }
652  };
653
654  window.quartoToggleReader = () => {
655    // Applies a slow class (or removes it)
656    // to update the transition speed
657    const slowTransition = (slow) => {
658      const manageTransition = (id, slow) => {
659        const el = document.getElementById(id);
660        if (el) {
661          if (slow) {
662            el.classList.add("slow");
663          } else {
664            el.classList.remove("slow");
665          }
666        }
667      };
668
669      manageTransition("TOC", slow);
670      manageTransition("quarto-sidebar", slow);
671    };
672    const readerMode = !isReaderMode();
673    setReaderModeValue(readerMode);
674
675    // If we're entering reader mode, slow the transition
676    if (readerMode) {
677      slowTransition(readerMode);
678    }
679    highlightReaderToggle(readerMode);
680    hideOverlappedSidebars();
681
682    // If we're exiting reader mode, restore the non-slow transition
683    if (!readerMode) {
684      slowTransition(!readerMode);
685    }
686  };
687
688  const highlightReaderToggle = (readerMode) => {
689    const els = document.querySelectorAll(".quarto-reader-toggle");
690    if (els) {
691      els.forEach((el) => {
692        if (readerMode) {
693          el.classList.add("reader");
694        } else {
695          el.classList.remove("reader");
696        }
697      });
698    }
699  };
700
701  const setReaderModeValue = (val) => {
702    if (window.location.protocol !== "file:") {
703      window.localStorage.setItem("quarto-reader-mode", val);
704    } else {
705      localReaderMode = val;
706    }
707  };
708
709  const isReaderMode = () => {
710    if (window.location.protocol !== "file:") {
711      return window.localStorage.getItem("quarto-reader-mode") === "true";
712    } else {
713      return localReaderMode;
714    }
715  };
716  let localReaderMode = null;
717
718  const tocOpenDepthStr = tocEl?.getAttribute("data-toc-expanded");
719  const tocOpenDepth = tocOpenDepthStr ? Number(tocOpenDepthStr) : 1;
720
721  // Walk the TOC and collapse/expand nodes
722  // Nodes are expanded if:
723  // - they are top level
724  // - they have children that are 'active' links
725  // - they are directly below an link that is 'active'
726  const walk = (el, depth) => {
727    // Tick depth when we enter a UL
728    if (el.tagName === "UL") {
729      depth = depth + 1;
730    }
731
732    // It this is active link
733    let isActiveNode = false;
734    if (el.tagName === "A" && el.classList.contains("active")) {
735      isActiveNode = true;
736    }
737
738    // See if there is an active child to this element
739    let hasActiveChild = false;
740    for (child of el.children) {
741      hasActiveChild = walk(child, depth) || hasActiveChild;
742    }
743
744    // Process the collapse state if this is an UL
745    if (el.tagName === "UL") {
746      if (tocOpenDepth === -1 && depth > 1) {
747        // toc-expand: false
748        el.classList.add("collapse");
749      } else if (
750        depth <= tocOpenDepth ||
751        hasActiveChild ||
752        prevSiblingIsActiveLink(el)
753      ) {
754        el.classList.remove("collapse");
755      } else {
756        el.classList.add("collapse");
757      }
758
759      // untick depth when we leave a UL
760      depth = depth - 1;
761    }
762    return hasActiveChild || isActiveNode;
763  };
764
765  // walk the TOC and expand / collapse any items that should be shown
766  if (tocEl) {
767    updateActiveLink();
768    walk(tocEl, 0);
769  }
770
771  // Throttle the scroll event and walk peridiocally
772  window.document.addEventListener(
773    "scroll",
774    throttle(() => {
775      if (tocEl) {
776        updateActiveLink();
777        walk(tocEl, 0);
778      }
779      if (!isReaderMode()) {
780        hideOverlappedSidebars();
781      }
782    }, 5)
783  );
784  window.addEventListener(
785    "resize",
786    throttle(() => {
787      if (tocEl) {
788        updateActiveLink();
789        walk(tocEl, 0);
790      }
791      if (!isReaderMode()) {
792        hideOverlappedSidebars();
793      }
794    }, 10)
795  );
796  hideOverlappedSidebars();
797  highlightReaderToggle(isReaderMode());
798});
799
800// grouped tabsets
801window.addEventListener("pageshow", (_event) => {
802  function getTabSettings() {
803    const data = localStorage.getItem("quarto-persistent-tabsets-data");
804    if (!data) {
805      localStorage.setItem("quarto-persistent-tabsets-data", "{}");
806      return {};
807    }
808    if (data) {
809      return JSON.parse(data);
810    }
811  }
812
813  function setTabSettings(data) {
814    localStorage.setItem(
815      "quarto-persistent-tabsets-data",
816      JSON.stringify(data)
817    );
818  }
819
820  function setTabState(groupName, groupValue) {
821    const data = getTabSettings();
822    data[groupName] = groupValue;
823    setTabSettings(data);
824  }
825
826  function toggleTab(tab, active) {
827    const tabPanelId = tab.getAttribute("aria-controls");
828    const tabPanel = document.getElementById(tabPanelId);
829    if (active) {
830      tab.classList.add("active");
831      tabPanel.classList.add("active");
832    } else {
833      tab.classList.remove("active");
834      tabPanel.classList.remove("active");
835    }
836  }
837
838  function toggleAll(selectedGroup, selectorsToSync) {
839    for (const [thisGroup, tabs] of Object.entries(selectorsToSync)) {
840      const active = selectedGroup === thisGroup;
841      for (const tab of tabs) {
842        toggleTab(tab, active);
843      }
844    }
845  }
846
847  function findSelectorsToSyncByLanguage() {
848    const result = {};
849    const tabs = Array.from(
850      document.querySelectorAll(`div[data-group] a[id^='tabset-']`)
851    );
852    for (const item of tabs) {
853      const div = item.parentElement.parentElement.parentElement;
854      const group = div.getAttribute("data-group");
855      if (!result[group]) {
856        result[group] = {};
857      }
858      const selectorsToSync = result[group];
859      const value = item.innerHTML;
860      if (!selectorsToSync[value]) {
861        selectorsToSync[value] = [];
862      }
863      selectorsToSync[value].push(item);
864    }
865    return result;
866  }
867
868  function setupSelectorSync() {
869    const selectorsToSync = findSelectorsToSyncByLanguage();
870    Object.entries(selectorsToSync).forEach(([group, tabSetsByValue]) => {
871      Object.entries(tabSetsByValue).forEach(([value, items]) => {
872        items.forEach((item) => {
873          item.addEventListener("click", (_event) => {
874            setTabState(group, value);
875            toggleAll(value, selectorsToSync[group]);
876          });
877        });
878      });
879    });
880    return selectorsToSync;
881  }
882
883  const selectorsToSync = setupSelectorSync();
884  for (const [group, selectedName] of Object.entries(getTabSettings())) {
885    const selectors = selectorsToSync[group];
886    // it's possible that stale state gives us empty selections, so we explicitly check here.
887    if (selectors) {
888      toggleAll(selectedName, selectors);
889    }
890  }
891});
892
893function throttle(func, wait) {
894  let waiting = false;
895  return function () {
896    if (!waiting) {
897      func.apply(this, arguments);
898      waiting = true;
899      setTimeout(function () {
900        waiting = false;
901      }, wait);
902    }
903  };
904}
905
906function nexttick(func) {
907  return setTimeout(func, 0);
908}

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.