PageSourceSearch

https://www.chiragjpgroup.org/site_libs/quarto-html/quarto.js

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

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.