PageSourceSearch

https://soci-229-w1.netlify.app/index_files/libs/quarto-html/tabby.min.js

js soci-229-w1.netlify.app collected 2026-10-03 11:25:27 UTC 11,289 bytes, 418 lines download raw bytes

1(function (root, factory) {
2  if (typeof define === "function" && define.amd) {
3    define([], function () {
4      return factory(root);
5    });
6  } else if (typeof exports === "object") {
7    module.exports = factory(root);
8  } else {
9    root.Tabby = factory(root);
10  }
11})(
12  typeof global !== "undefined"
13    ? global
14    : typeof window !== "undefined"
15    ? window
16    : this,
17  function (window) {
18    "use strict";
19
20    //
21    // Variables
22    //
23
24    var defaults = {
25      idPrefix: "tabby-toggle_",
26      default: "[data-tabby-default]",
27    };
28
29    //
30    // Methods
31    //
32
33    /**
34     * Merge two or more objects together.
35     * @param   {Object}   objects  The objects to merge together
36     * @returns {Object}            Merged values of defaults and options
37     */
38    var extend = function () {
39      var merged = {};
40      Array.prototype.forEach.call(arguments, function (obj) {
41        for (var key in obj) {
42          if (!obj.hasOwnProperty(key)) return;
43          merged[key] = obj[key];
44        }
45      });
46      return merged;
47    };
48
49    /**
50     * Emit a custom event
51     * @param  {String} type    The event type
52     * @param  {Node}   tab     The tab to attach the event to
53     * @param  {Node}   details Details about the event
54     */
55    var emitEvent = function (tab, details) {
56      // Create a new event
57      var event;
58      if (typeof window.CustomEvent === "function") {
59        event = new CustomEvent("tabby", {
60          bubbles: true,
61          cancelable: true,
62          detail: details,
63        });
64      } else {
65        event = document.createEvent("CustomEvent");
66        event.initCustomEvent("tabby", true, true, details);
67      }
68
69      // Dispatch the event
70      tab.dispatchEvent(event);
71    };
72
73    var focusHandler = function (event) {
74      toggle(event.target);
75    };
76
77    var getKeyboardFocusableElements = function (element) {
78      return [
79        ...element.querySelectorAll(
80          'a[href], button, input, textarea, select, details,[tabindex]:not([tabindex="-1"])'
81        ),
82      ].filter(
83        (el) => !el.hasAttribute("disabled") && !el.getAttribute("aria-hidden")
84      );
85    };
86
87    /**
88     * Remove roles and attributes from a tab and its content
89     * @param  {Node}   tab      The tab
90     * @param  {Node}   content  The tab content
91     * @param  {Object} settings User settings and options
92     */
93    var destroyTab = function (tab, content, settings) {
94      // Remove the generated ID
95      if (tab.id.slice(0, settings.idPrefix.length) === settings.idPrefix) {
96        tab.id = "";
97      }
98
99      // remove event listener
100      tab.removeEventListener("focus", focusHandler, true);
101
102      // Remove roles
103      tab.removeAttribute("role");
104      tab.removeAttribute("aria-controls");
105      tab.removeAttribute("aria-selected");
106      tab.removeAttribute("tabindex");
107      tab.closest("li").removeAttribute("role");
108      content.removeAttribute("role");
109      content.removeAttribute("aria-labelledby");
110      content.removeAttribute("hidden");
111    };
112
113    /**
114     * Add the required roles and attributes to a tab and its content
115     * @param  {Node}   tab      The tab
116     * @param  {Node}   content  The tab content
117     * @param  {Object} settings User settings and options
118     */
119    var setupTab = function (tab, content, settings) {
120      // Give tab an ID if it doesn't already have one
121      if (!tab.id) {
122        tab.id = settings.idPrefix + content.id;
123      }
124
125      // Add roles
126      tab.setAttribute("role", "tab");
127      tab.setAttribute("aria-controls", content.id);
128      tab.closest("li").setAttribute("role", "presentation");
129      content.setAttribute("role", "tabpanel");
130      content.setAttribute("aria-labelledby", tab.id);
131
132      // Add selected state
133      if (tab.matches(settings.default)) {
134        tab.setAttribute("aria-selected", "true");
135      } else {
136        tab.setAttribute("aria-selected", "false");
137        content.setAttribute("hidden", "hidden");
138      }
139
140      // add focus event listender
141      tab.addEventListener("focus", focusHandler);
142    };
143
144    /**
145     * Hide a tab and its content
146     * @param  {Node} newTab The new tab that's replacing it
147     */
148    var hide = function (newTab) {
149      // Variables
150      var tabGroup = newTab.closest('[role="tablist"]');
151      if (!tabGroup) return {};
152      var tab = tabGroup.querySelector('[role="tab"][aria-selected="true"]');
153      if (!tab) return {};
154      var content = document.querySelector(tab.hash);
155
156      // Hide the tab
157      tab.setAttribute("aria-selected", "false");
158
159      // Hide the content
160      if (!content) return { previousTab: tab };
161      content.setAttribute("hidden", "hidden");
162
163      // Return the hidden tab and content
164      return {
165        previousTab: tab,
166        previousContent: content,
167      };
168    };
169
170    /**
171     * Show a tab and its content
172     * @param  {Node} tab      The tab
173     * @param  {Node} content  The tab content
174     */
175    var show = function (tab, content) {
176      tab.setAttribute("aria-selected", "true");
177      content.removeAttribute("hidden");
178      tab.focus();
179    };
180
181    /**
182     * Toggle a new tab
183     * @param  {Node} tab The tab to show
184     */
185    var toggle = function (tab) {
186      // Make sure there's a tab to toggle and it's not already active
187      if (!tab || tab.getAttribute("aria-selected") == "true") return;
188
189      // Variables
190      var content = document.querySelector(tab.hash);
191      if (!content) return;
192
193      // Hide active tab and content
194      var details = hide(tab);
195
196      // Show new tab and content
197      show(tab, content);
198
199      // Add event details
200      details.tab = tab;
201      details.content = content;
202
203      // Emit a custom event
204      emitEvent(tab, details);
205    };
206
207    /**
208     * Get all of the tabs in a tablist
209     * @param  {Node}   tab  A tab from the list
210     * @return {Object}      The tabs and the index of the currently active one
211     */
212    var getTabsMap = function (tab) {
213      var tabGroup = tab.closest('[role="tablist"]');
214      var tabs = tabGroup ? tabGroup.querySelectorAll('[role="tab"]') : null;
215      if (!tabs) return;
216      return {
217        tabs: tabs,
218        index: Array.prototype.indexOf.call(tabs, tab),
219      };
220    };
221
222    /**
223     * Switch the active tab based on keyboard activity
224     * @param  {Node} tab The currently active tab
225     * @param  {Key}  key The key that was pressed
226     */
227    var switchTabs = function (tab, key) {
228      // Get a map of tabs
229      var map = getTabsMap(tab);
230      if (!map) return;
231      var length = map.tabs.length - 1;
232      var index;
233
234      // Go to previous tab
235      if (["ArrowUp", "ArrowLeft", "Up", "Left"].indexOf(key) > -1) {
236        index = map.index < 1 ? length : map.index - 1;
237      }
238
239      // Go to next tab
240      else if (["ArrowDown", "ArrowRight", "Down", "Right"].indexOf(key) > -1) {
241        index = map.index === length ? 0 : map.index + 1;
242      }
243
244      // Go to home
245      else if (key === "Home") {
246        index = 0;
247      }
248
249      // Go to end
250      else if (key === "End") {
251        index = length;
252      }
253
254      // Toggle the tab
255      toggle(map.tabs[index]);
256    };
257
258    /**
259     * Create the Constructor object
260     */
261    var Constructor = function (selector, options) {
262      //
263      // Variables
264      //
265
266      var publicAPIs = {};
267      var settings, tabWrapper;
268
269      //
270      // Methods
271      //
272
273      publicAPIs.destroy = function () {
274        // Get all tabs
275        var tabs = tabWrapper.querySelectorAll("a");
276
277        // Add roles to tabs
278        Array.prototype.forEach.call(tabs, function (tab) {
279          // Get the tab content
280          var content = document.querySelector(tab.hash);
281          if (!content) return;
282
283          // Setup the tab
284          destroyTab(tab, content, settings);
285        });
286
287        // Remove role from wrapper
288        tabWrapper.removeAttribute("role");
289
290        // Remove event listeners
291        document.documentElement.removeEventListener(
292          "click",
293          clickHandler,
294          true
295        );
296        tabWrapper.removeEventListener("keydown", keyHandler, true);
297
298        // Reset variables
299        settings = null;
300        tabWrapper = null;
301      };
302
303      /**
304       * Setup the DOM with the proper attributes
305       */
306      publicAPIs.setup = function () {
307        // Variables
308        tabWrapper = document.querySelector(selector);
309        if (!tabWrapper) return;
310        var tabs = tabWrapper.querySelectorAll("a");
311
312        // Add role to wrapper
313        tabWrapper.setAttribute("role", "tablist");
314
315        // Add roles to tabs. provide dynanmic tab indexes if we are within reveal
316        var contentTabindexes =
317          window.document.body.classList.contains("reveal-viewport");
318        var nextTabindex = 1;
319        Array.prototype.forEach.call(tabs, function (tab) {
320          if (contentTabindexes) {
321            tab.setAttribute("tabindex", "" + nextTabindex++);
322          } else {
323            tab.setAttribute("tabindex", "0");
324          }
325
326          // Get the tab content
327          var content = document.querySelector(tab.hash);
328          if (!content) return;
329
330          // set tab indexes for content
331          if (contentTabindexes) {
332            getKeyboardFocusableElements(content).forEach(function (el) {
333              el.setAttribute("tabindex", "" + nextTabindex++);
334            });
335          }
336
337          // Setup the tab
338          setupTab(tab, content, settings);
339        });
340      };
341
342      /**
343       * Toggle a tab based on an ID
344       * @param  {String|Node} id The tab to toggle
345       */
346      publicAPIs.toggle = function (id) {
347        // Get the tab
348        var tab = id;
349        if (typeof id === "string") {
350          tab = document.querySelector(
351            selector + ' [role="tab"][href*="' + id + '"]'
352          );
353        }
354
355        // Toggle the tab
356        toggle(tab);
357      };
358
359      /**
360       * Handle click events
361       */
362      var clickHandler = function (event) {
363        // Only run on toggles
364        var tab = event.target.closest(selector + ' [role="tab"]');
365        if (!tab) return;
366
367        // Prevent link behavior
368        event.preventDefault();
369
370        // Toggle the tab
371        toggle(tab);
372      };
373
374      /**
375       * Handle keydown events
376       */
377      var keyHandler = function (event) {
378        // Only run if a tab is in focus
379        var tab = document.activeElement;
380        if (!tab.matches(selector + ' [role="tab"]')) return;
381
382        // Only run for specific keys
383        if (["Home", "End"].indexOf(event.key) < 0) return;
384
385        // Switch tabs
386        switchTabs(tab, event.key);
387      };
388
389      /**
390       * Initialize the instance
391       */
392      var init = function () {
393        // Merge user options with defaults
394        settings = extend(defaults, options || {});
395
396        // Setup the DOM
397        publicAPIs.setup();
398
399        // Add event listeners
400        document.documentElement.addEventListener("click", clickHandler, true);
401        tabWrapper.addEventListener("keydown", keyHandler, true);
402      };
403
404      //
405      // Initialize and return the Public APIs
406      //
407
408      init();
409      return publicAPIs;
410    };
411
412    //
413    // Return the Constructor
414    //
415
416    return Constructor;
417  }
418);

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.