PageSourceSearch

https://www.draytek.co.uk/media/vendor/bootstrap/js/tab.min.js?5.3.8

js draytek.co.uk collected 2026-10-02 04:31:23 UTC 11,226 bytes, 333 lines download raw bytes

vendor: 7,052 bytes, lines 1-194
1import { E as EventHandler, f as isDisabled, S as SelectorEngine, d as defineJQueryPlugin, B as BaseComponent, g as getNextActiveElement } from './dom.js?5.3.0';
2
3/**
4 * --------------------------------------------------------------------------
5 * Bootstrap tab.js
6 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
7 * --------------------------------------------------------------------------
8 */
9
10/**
11 * Constants
12 */
13
14const NAME = 'tab';
15const DATA_KEY = 'bs.tab';
16const EVENT_KEY = `.${DATA_KEY}`;
17const EVENT_HIDE = `hide${EVENT_KEY}`;
18const EVENT_HIDDEN = `hidden${EVENT_KEY}`;
19const EVENT_SHOW = `show${EVENT_KEY}`;
20const EVENT_SHOWN = `shown${EVENT_KEY}`;
21const EVENT_CLICK_DATA_API = `click${EVENT_KEY}`;
22const EVENT_KEYDOWN = `keydown${EVENT_KEY}`;
23const EVENT_LOAD_DATA_API = `load${EVENT_KEY}`;
24const ARROW_LEFT_KEY = 'ArrowLeft';
25const ARROW_RIGHT_KEY = 'ArrowRight';
26const ARROW_UP_KEY = 'ArrowUp';
27const ARROW_DOWN_KEY = 'ArrowDown';
28const HOME_KEY = 'Home';
29const END_KEY = 'End';
30const CLASS_NAME_ACTIVE = 'active';
31const CLASS_NAME_FADE = 'fade';
32const CLASS_NAME_SHOW = 'show';
33const CLASS_DROPDOWN = 'dropdown';
34const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle';
35const SELECTOR_DROPDOWN_MENU = '.dropdown-menu';
36const NOT_SELECTOR_DROPDOWN_TOGGLE = `:not(${SELECTOR_DROPDOWN_TOGGLE})`;
37const SELECTOR_TAB_PANEL = '.list-group, .nav, [role="tablist"]';
38const SELECTOR_OUTER = '.nav-item, .list-group-item';
39const SELECTOR_INNER = `.nav-link${NOT_SELECTOR_DROPDOWN_TOGGLE}, .list-group-item${NOT_SELECTOR_DROPDOWN_TOGGLE}, [role="tab"]${NOT_SELECTOR_DROPDOWN_TOGGLE}`;
40const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]'; // TODO: could only be `tab` in v6
41const SELECTOR_INNER_ELEM = `${SELECTOR_INNER}, ${SELECTOR_DATA_TOGGLE}`;
42const SELECTOR_DATA_TOGGLE_ACTIVE = `.${CLASS_NAME_ACTIVE}[data-bs-toggle="tab"], .${CLASS_NAME_ACTIVE}[data-bs-toggle="pill"], .${CLASS_NAME_ACTIVE}[data-bs-toggle="list"]`;
43
44/**
45 * Class definition
46 */
47
48class Tab extends BaseComponent {
49  constructor(element) {
50    super(element);
51    this._parent = this._element.closest(SELECTOR_TAB_PANEL);
52    if (!this._parent) {
53      return;
54      // TODO: should throw exception in v6
55      // throw new TypeError(`${element.outerHTML} has not a valid parent ${SELECTOR_INNER_ELEM}`)
56    }
57
58    // Set up initial aria attributes
59    this._setInitialAttributes(this._parent, this._getChildren());
60    EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event));
61  }
62
63  // Getters
64  static get NAME() {
65    return NAME;
66  }
67
68  // Public
69  show() {
70    // Shows this elem and deactivate the active sibling if exists
71    const innerElem = this._element;
72    if (this._elemIsActive(innerElem)) {
73      return;
74    }
75
76    // Search for active tab on same parent to deactivate it
77    const active = this._getActiveElem();
78    //const hideEvent = active ? EventHandler.trigger(active, EVENT_HIDE, {
79    //  relatedTarget: innerElem
80  //  }) : null;
81    const showEvent = EventHandler.trigger(innerElem, EVENT_SHOW, {
82      relatedTarget: active
83    });
84  //  if (showEvent.defaultPrevented || hideEvent && hideEvent.defaultPrevented) {
85   //   return;
86  //  }
87    this._deactivate(active, innerElem);
88    this._activate(innerElem, active);
89  }
90
91  // Private
92  _activate(element, relatedElem) {
93    if (!element) {
94      return;
95    }
96    element.classList.add(CLASS_NAME_ACTIVE);
97    this._activate(SelectorEngine.getElementFromSelector(element)); // Search and activate/show the proper section
98
99    const complete = () => {
100      if (element.getAttribute('role') !== 'tab') {
101        element.classList.add(CLASS_NAME_SHOW);
102        return;
103      }
104      element.removeAttribute('tabindex');
105      element.setAttribute('aria-selected', true);
106      this._toggleDropDown(element, true);
107      EventHandler.trigger(element, EVENT_SHOWN, {
108        relatedTarget: relatedElem
109      });
110    };
111    this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE));
112  }
113  _deactivate(element, relatedElem) {
114    if (!element) {
115      return;
116    }
117    element.classList.remove(CLASS_NAME_ACTIVE);
118    element.blur();
119    this._deactivate(SelectorEngine.getElementFromSelector(element)); // Search and deactivate the shown section too
120
121    const complete = () => {
122      if (element.getAttribute('role') !== 'tab') {
123        element.classList.remove(CLASS_NAME_SHOW);
124        return;
125      }
126      element.setAttribute('aria-selected', false);
127      element.setAttribute('tabindex', '-1');
128      this._toggleDropDown(element, false);
129      EventHandler.trigger(element, EVENT_HIDDEN, {
130        relatedTarget: relatedElem
131      });
132    };
133    this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE));
134  }
135  _keydown(event) {
136    if (![ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY, HOME_KEY, END_KEY].includes(event.key)) {
137      return;
138    }
139    event.stopPropagation(); // stopPropagation/preventDefault both added to support up/down keys without scrolling the page
140    event.preventDefault();
141    const children = this._getChildren().filter(element => !isDisabled(element));
142    let nextActiveElement;
143    if ([HOME_KEY, END_KEY].includes(event.key)) {
144      nextActiveElement = children[event.key === HOME_KEY ? 0 : children.length - 1];
145    } else {
146      const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key);
147      nextActiveElement = getNextActiveElement(children, event.target, isNext, true);
148    }
149    if (nextActiveElement) {
150      nextActiveElement.focus({
151        preventScroll: true
152      });
153      Tab.getOrCreateInstance(nextActiveElement).show();
154    }
155  }
156  _getChildren() {
157    // collection of inner elements
158    return SelectorEngine.find(SELECTOR_INNER_ELEM, this._parent);
159  }
160  _getActiveElem() {
161    return this._getChildren().find(child => this._elemIsActive(child)) || null;
162  }
163  _setInitialAttributes(parent, children) {
164    this._setAttributeIfNotExists(parent, 'role', 'tablist');
165    for (const child of children) {
166      this._setInitialAttributesOnChild(child);
167    }
168  }
169  _setInitialAttributesOnChild(child) {
170    child = this._getInnerElement(child);
171    const isActive = this._elemIsActive(child);
172    const outerElem = this._getOuterElement(child);
173    child.setAttribute('aria-selected', isActive);
174    if (outerElem !== child) {
175      this._setAttributeIfNotExists(outerElem, 'role', 'presentation');
176    }
177    if (!isActive) {
178      child.setAttribute('tabindex', '-1');
179    }
180    this._setAttributeIfNotExists(child, 'role', 'tab');
181
182    // set attributes to the related panel too
183    this._setInitialAttributesOnTargetPanel(child);
184  }
185  _setInitialAttributesOnTargetPanel(child) {
186    const target = SelectorEngine.getElementFromSelector(child);
187    if (!target) {
188      return;
189    }
190    this._setAttributeIfNotExists(target, 'role', 'tabpanel');
191    if (child.id) {
192      this._setAttributeIfNotExists(target, 'aria-labelledby', `${child.id}`);
193    }
194  }
195  _toggleDropDown(element, open) {
196    const outerElem = this._getOuterElement(element);
197    if (!outerElem.classList.contains(CLASS_DROPDOWN)) {
198      return;
199    }
200    const toggle = (selector, className) => {
201      const element = SelectorEngine.findOne(selector, outerElem);
202      if (element) {
203        element.classList.toggle(className, open);
204      }
205    };
206    toggle(SELECTOR_DROPDOWN_TOGGLE, CLASS_NAME_ACTIVE);
207    toggle(SELECTOR_DROPDOWN_MENU, CLASS_NAME_SHOW);
208    outerElem.setAttribute('aria-expanded', open);
209  }
210  _setAttributeIfNotExists(element, attribute, value) {
211    if (!element.hasAttribute(attribute)) {
212      element.setAttribute(attribute, value);
213    }
214  }
215  _elemIsActive(elem) {
216    return elem.classList.contains(CLASS_NAME_ACTIVE);
217  }
218
219  // Try to get the inner element (usually the .nav-link)
220  _getInnerElement(elem) {
221    return elem.matches(SELECTOR_INNER_ELEM) ? elem : SelectorEngine.findOne(SELECTOR_INNER_ELEM, elem);
222  }
223
224  // Try to get the outer element (usually the .nav-item)
225  _getOuterElement(elem) {
226    return elem.closest(SELECTOR_OUTER) || elem;
227  }
228
229  // Static
230  static jQueryInterface(config) {
231    return this.each(function () {
232      const data = Tab.getOrCreateInstance(this);
233      if (typeof config !== 'string') {
234        return;
235      }
236      if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
237        throw new TypeError(`No method named "${config}"`);
238      }
239      data[config]();
240    });
241  }
242}
243
244/**
245 * Data API implementation
246 */
247
248EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
249  if (['A', 'AREA'].includes(this.tagName)) {
250    event.preventDefault();
251  }
252  if (isDisabled(this)) {
253    return;
254  }
255  Tab.getOrCreateInstance(this).show();
256});
257
258/**
259 * Initialize on focus
260 */
261EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
262  for (const element of SelectorEngine.find(SELECTOR_DATA_TOGGLE_ACTIVE)) {
263    Tab.getOrCreateInstance(element);
264  }
265});
266/**
267 * jQuery
268 */
269
270defineJQueryPlugin(Tab);
271
272window.Joomla = window.Joomla || {};
273window.bootstrap = window.bootstrap || {};
274window.bootstrap.Tab = Tab;
275
276/**
277 * Initialise the Tabs interactivity
278 *
279 * @param {HTMLElement} el The element that will become a collapse
280 * @param {object} options The options for this collapse
281 */
282Joomla.initialiseTabs = (el, options) => {
283  if (!(el instanceof Element) && options.isJoomla) {
284    const tab = document.querySelector(`${el}Content`);
285    if (tab) {
286      const related = Array.from(tab.children);
287
288      // Build the navigation
289      if (related.length) {
290        related.forEach(element => {
291          if (!element.classList.contains('tab-pane')) {
292            return;
293          }
294          const isActive = element.dataset.active !== '';
295          const ul = document.querySelector(`${el}Tabs`);
296          if (ul) {
297            const link = document.createElement('a');
298            link.href = `#${element.dataset.id}`;
299            link.classList.add('nav-link');
300            if (isActive) {
301              link.classList.add('active');
302            }
303            link.dataset.bsToggle = 'tab';
304            link.setAttribute('role', 'tab');
305            link.setAttribute('aria-controls', element.dataset.id);
306            link.setAttribute('aria-selected', element.dataset.id);
307            link.innerHTML = Joomla.sanitizeHtml(element.dataset.title);
308            const li = document.createElement('li');
309            li.classList.add('nav-item');
310            li.setAttribute('role', 'presentation');
311            li.appendChild(link);
312            ul.appendChild(li);
313
314            // eslint-disable-next-line no-new
315            new window.bootstrap.Tab(li);
316          }
317        });
318      }
319    }
320  } else {
321    Array.from(document.querySelectorAll(`${el} a`)).map(tab => new window.bootstrap.Tab(tab, options));
322  }
323};
324if (Joomla && Joomla.getOptions) {
325  // Get the elements/configurations from the PHP
326  const tabs = Joomla.getOptions('bootstrap.tabs');
327  // Initialise the elements
328  if (typeof tabs === 'object' && tabs !== null) {
329    Object.keys(tabs).map(tab => Joomla.initialiseTabs(tab, tabs[tab]));
330  }
331}
332
333export { Tab as T };

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.