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