PageSourceSearch

https://patrickywu.com/site_libs/quarto-html/tabsets/tabsets.js

js patrickywu.com collected 2026-09-24 10:29:28 UTC 2,882 bytes, 95 lines download raw bytes

1// grouped tabsets
2
3export function init() {
4  window.addEventListener("pageshow", (_event) => {
5    function getTabSettings() {
6      const data = localStorage.getItem("quarto-persistent-tabsets-data");
7      if (!data) {
8        localStorage.setItem("quarto-persistent-tabsets-data", "{}");
9        return {};
10      }
11      if (data) {
12        return JSON.parse(data);
13      }
14    }
15
16    function setTabSettings(data) {
17      localStorage.setItem(
18        "quarto-persistent-tabsets-data",
19        JSON.stringify(data)
20      );
21    }
22
23    function setTabState(groupName, groupValue) {
24      const data = getTabSettings();
25      data[groupName] = groupValue;
26      setTabSettings(data);
27    }
28
29    function toggleTab(tab, active) {
30      const tabPanelId = tab.getAttribute("aria-controls");
31      const tabPanel = document.getElementById(tabPanelId);
32      if (active) {
33        tab.classList.add("active");
34        tabPanel.classList.add("active");
35      } else {
36        tab.classList.remove("active");
37        tabPanel.classList.remove("active");
38      }
39    }
40
41    function toggleAll(selectedGroup, selectorsToSync) {
42      for (const [thisGroup, tabs] of Object.entries(selectorsToSync)) {
43        const active = selectedGroup === thisGroup;
44        for (const tab of tabs) {
45          toggleTab(tab, active);
46        }
47      }
48    }
49
50    function findSelectorsToSyncByLanguage() {
51      const result = {};
52      const tabs = Array.from(
53        document.querySelectorAll(`div[data-group] a[id^='tabset-']`)
54      );
55      for (const item of tabs) {
56        const div = item.parentElement.parentElement.parentElement;
57        const group = div.getAttribute("data-group");
58        if (!result[group]) {
59          result[group] = {};
60        }
61        const selectorsToSync = result[group];
62        const value = item.innerHTML;
63        if (!selectorsToSync[value]) {
64          selectorsToSync[value] = [];
65        }
66        selectorsToSync[value].push(item);
67      }
68      return result;
69    }
70
71    function setupSelectorSync() {
72      const selectorsToSync = findSelectorsToSyncByLanguage();
73      Object.entries(selectorsToSync).forEach(([group, tabSetsByValue]) => {
74        Object.entries(tabSetsByValue).forEach(([value, items]) => {
75          items.forEach((item) => {
76            item.addEventListener("click", (_event) => {
77              setTabState(group, value);
78              toggleAll(value, selectorsToSync[group]);
79            });
80          });
81        });
82      });
83      return selectorsToSync;
84    }
85
86    const selectorsToSync = setupSelectorSync();
87    for (const [group, selectedName] of Object.entries(getTabSettings())) {
88      const selectors = selectorsToSync[group];
89      // it's possible that stale state gives us empty selections, so we explicitly check here.
90      if (selectors) {
91        toggleAll(selectedName, selectors);
92      }
93    }
94  });
95}

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.