diff --git a/.gitignore b/.gitignore index fce99854..f25241a9 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,5 @@ package-lock.json .npmignore .vscode/ node_modules/ -local/ +experiments/ demo.html \ No newline at end of file diff --git a/.npmignore b/.npmignore index a94d67ea..fb3ce638 100644 --- a/.npmignore +++ b/.npmignore @@ -2,7 +2,7 @@ .vscode/ assets/ node_modules/ -local/ +experiments/ CONTRIBUTING.md package-lock.json index.html diff --git a/assets/css/theme.css b/assets/css/theme.css index 6a507e19..61ba2f63 100644 --- a/assets/css/theme.css +++ b/assets/css/theme.css @@ -91,9 +91,12 @@ pre[class*="language-"] {margin: 0 0 1rem} top: 0.5rem } .nav-stacked ul { - padding-left: 1rem; + padding: 0 0 0 1rem; display: none } +[dir="rtl"] .nav-stacked ul { + padding: 0 1rem 0 0; +} .nav-stacked li > a.active + ul { display: block } /* v5 nav */ diff --git a/assets/js/scripts.js b/assets/js/scripts.js index 1fead381..62d58ab9 100644 --- a/assets/js/scripts.js +++ b/assets/js/scripts.js @@ -118,7 +118,12 @@ var tooltipTemplateExample = new BSN.Tooltip('#tooltipTemplateExample', { sanitizeFn: function(dirty){ return DOMPurify.sanitize( dirty ); } -}) +}); + +// Tooltip HTMLElement as title option +var tooltipElementContent = document.getElementById('tooltipElementContent'); +var tooltipTitle = document.createElement('span'); tooltipTitle.innerHTML = 'Tooltip on LEFT HOT
This tooltip uses a title set via JavaScript as HTMLElement elements.
This feature is JavaScript only.'; +new BSN.Tooltip(tooltipElementContent, { title: tooltipTitle }); // Popover var popover1 = new BSN.Popover('#popover-via-click', { container: '#popoverExamples', trigger: 'click' } ); @@ -146,23 +151,32 @@ popoverEvents.addEventListener('shown.bs.popover', function(){ console.log('The popoverEvents.addEventListener('hide.bs.popover', function(){ console.log('The hide.bs.popover event fired for #' + popoverEvents.id); }, false); popoverEvents.addEventListener('hidden.bs.popover', function(){ console.log('The hidden.bs.popover event fired for #' + popoverEvents.id); }, false); +var popoverElementContents = document.getElementById('popoverElementContents'); +var popoverTitle = document.createElement('span'); popoverTitle.innerHTML = 'Popover on RIGHT HOT'; +var popoverContent = document.createElement('span'); popoverContent.innerHTML = 'This popover uses a custom class set via data-bs-custom-class attribute, but the title and content are set via JavaScript as HTMLElement elements. This feature is JavaScript only.'; +new BSN.Popover(popoverElementContents, { + title: popoverTitle, + content: popoverContent +}); + // TOAST var toastBTN = document.getElementById('myTastyToastBTN'); var toastElement = toastBTN.closest('.toast'); var showToastBTN = document.getElementById('showToastBTN'); + toastElement.addEventListener('show.bs.toast',function(e){ - console.log( 'The "show.bs.toast" event fired for #' + toastBTN.id ); + console.log( 'The "show.bs.toast" event fired for #' + toastElement.id ); },false) toastElement.addEventListener('shown.bs.toast',function(e){ - console.log( 'The "shown.bs.toast" event fired for #' + toastBTN.id ); - showToastBTN.classList.add('d-none') + console.log( 'The "shown.bs.toast" event fired for #' + toastElement.id ); + // showToastBTN.classList.add('d-none') },false) toastElement.addEventListener('hide.bs.toast',function(e){ - console.log( 'The "hide.bs.toast" event fired for #' + toastBTN.id ); + console.log( 'The "hide.bs.toast" event fired for #' + toastElement.id ); },false) toastElement.addEventListener('hidden.bs.toast',function(e){ - console.log( 'The "hidden.bs.toast" event fired for #' + toastBTN.id ); - showToastBTN.classList.remove('d-none') + console.log( 'The "hidden.bs.toast" event fired for #' + toastElement.id ); + // showToastBTN.classList.remove('d-none') },false) showToastBTN.addEventListener('click',function(){ @@ -171,17 +185,27 @@ showToastBTN.addEventListener('click',function(){ }, false) // ScrollSpy +var disposableSpy = document.getElementById('disposableSpy'); +var scrollSpyEventCallback = function(e){ + const { tagName, classList } = e.relatedTarget; + var scrollSpyLog = 'The "activate.bs.scrollspy" event fired for #' + disposableSpy.id + + '\nevent.relatedTarget: ' + (e.relatedTarget ? (tagName + '.' + [...classList].join('.')) : 'null'); + console.log(scrollSpyLog); +} +disposableSpy.addEventListener('activate.bs.scrollspy', scrollSpyEventCallback); + function toggleScrollSpy(){ - var disposableSpy = document.getElementById('disposableSpy'); var spyInstance = BSN.ScrollSpy.getInstance(disposableSpy); if ( spyInstance ){ + disposableSpy.removeEventListener('activate.bs.scrollspy', scrollSpyEventCallback); spyInstance.dispose() this.innerHTML = 'Init' this.classList.remove( 'btn-outline-danger' ) this.classList.add( 'btn-outline-primary' ) } else { new BSN.ScrollSpy(disposableSpy) + disposableSpy.addEventListener('activate.bs.scrollspy', scrollSpyEventCallback); this.innerHTML = 'Dispose' this.classList.remove( 'btn-outline-primary' ) this.classList.add( 'btn-outline-danger' ) @@ -226,4 +250,21 @@ carouselGenericExample.addEventListener('slid.bs.carousel', function(e) { var to = `\n> to index ${e.to}`; var direction = `\n> with direction ${e.direction}`; console.log('The "slid.bs.carousel" event fired for
' + direction + from + to + related); -}, false); \ No newline at end of file +}, false); + +// RTL play +function switchDirection() { + var isRTL = document.documentElement.dir === 'rtl'; + var bsCSS = document.getElementById('bsCSS'); + var href = bsCSS.getAttribute('href'); + + if (isRTL) { + bsCSS.href = href.replace('bootstrap.rtl.min', 'bootstrap.min'); + document.documentElement.removeAttribute('dir'); + this.innerText = 'RTL'; + } else { + bsCSS.href = href.replace('bootstrap.min', 'bootstrap.rtl.min'); + document.documentElement.setAttribute('dir', 'rtl'); + this.innerText = 'LTR'; + } +} diff --git a/dist/bootstrap-native-v4-esm.js b/dist/bootstrap-native-v4-esm.js index 02efa189..91ef1bc3 100644 --- a/dist/bootstrap-native-v4-esm.js +++ b/dist/bootstrap-native-v4-esm.js @@ -1,43 +1,69 @@ /*! - * Native JavaScript for Bootstrap v4.1.0 (https://thednp.github.io/bootstrap.native/) - * Copyright 2015-2021 © dnp_theme + * Native JavaScript for Bootstrap v4.1.0alpha1 (https://thednp.github.io/bootstrap.native/) + * Copyright 2015-2022 © dnp_theme * Licensed under MIT (https://github.com/thednp/bootstrap.native/blob/master/LICENSE) */ /** - * A global namespace for 'transitionend' string. - * @type {string} + * A global namespace for `document.head`. */ -var transitionEndEvent = 'webkitTransition' in document.head.style ? 'webkitTransitionEnd' : 'transitionend'; +var documentHead = document.head; /** - * A global namespace for CSS3 transition support. + * A global `boolean` for CSS3 transition support. * @type {boolean} */ -var supportTransition = 'webkitTransition' in document.head.style || 'transition' in document.head.style; +var supportTransition = 'webkitTransition' in documentHead.style || 'transition' in documentHead.style; + +/** + * A global namespace for 'transitionend' string. + * @type {string} + */ +var transitionEndEvent = 'webkitTransition' in documentHead.style ? 'webkitTransitionEnd' : 'transitionend'; /** * A global namespace for 'transitionDelay' string. * @type {string} */ -var transitionDelay = 'webkitTransition' in document.head.style ? 'webkitTransitionDelay' : 'transitionDelay'; +var transitionDelay = 'webkitTransition' in documentHead.style ? 'webkitTransitionDelay' : 'transitionDelay'; /** - * A global namespace for 'transitionProperty' string. + * A global namespace for: + * * `transitionProperty` string for Firefox, + * * `webkitTransition` for older Chrome / Safari browsers, + * * `transition` property for all other browsers. * @type {string} */ -var transitionProperty = 'webkitTransition' in document.head.style ? 'webkitTransitionProperty' : 'transitionProperty'; +var transitionProperty = 'webkitTransition' in documentHead.style ? 'webkitTransitionProperty' : 'transitionProperty'; + +/** + * Shortcut for `window.getComputedStyle(element).propertyName` + * static method. + * + * * If `element` parameter is not an `HTMLElement`, `getComputedStyle` + * throws a `ReferenceError`. + * + * @param {HTMLElement | Element} element target + * @param {string} property the css property + * @return {string} the css property value + */ +function getElementStyle(element, property) { + var computedStyle = getComputedStyle(element); + + // @ts-ignore -- must use camelcase strings, + // or non-camelcase strings with `getPropertyValue` + return property in computedStyle ? computedStyle[property] : ''; +} /** - * Utility to get the computed transitionDelay + * Utility to get the computed `transitionDelay` * from Element in miliseconds. * - * @param {Element} element target + * @param {HTMLElement | Element} element target * @return {number} the value in miliseconds */ function getElementTransitionDelay(element) { - var computedStyle = getComputedStyle(element); - var propertyValue = computedStyle[transitionProperty]; - var delayValue = computedStyle[transitionDelay]; + var propertyValue = getElementStyle(element, transitionProperty); + var delayValue = getElementStyle(element, transitionDelay); var delayScale = delayValue.includes('ms') ? 1 : 1000; var duration = supportTransition && propertyValue && propertyValue !== 'none' ? parseFloat(delayValue) * delayScale : 0; @@ -49,19 +75,18 @@ function getElementTransitionDelay(element) { * A global namespace for 'transitionDuration' string. * @type {string} */ -var transitionDuration = 'webkitTransition' in document.head.style ? 'webkitTransitionDuration' : 'transitionDuration'; +var transitionDuration = 'webkitTransition' in documentHead.style ? 'webkitTransitionDuration' : 'transitionDuration'; /** - * Utility to get the computed transitionDuration + * Utility to get the computed `transitionDuration` * from Element in miliseconds. * - * @param {Element} element target + * @param {HTMLElement | Element} element target * @return {number} the value in miliseconds */ function getElementTransitionDuration(element) { - var computedStyle = getComputedStyle(element); - var propertyValue = computedStyle[transitionProperty]; - var durationValue = computedStyle[transitionDuration]; + var propertyValue = getElementStyle(element, transitionProperty); + var durationValue = getElementStyle(element, transitionDuration); var durationScale = durationValue.includes('ms') ? 1 : 1000; var duration = supportTransition && propertyValue && propertyValue !== 'none' ? parseFloat(durationValue) * durationScale : 0; @@ -73,8 +98,8 @@ function getElementTransitionDuration(element) { * Utility to make sure callbacks are consistently * called when transition ends. * - * @param {Element} element target - * @param {function} handler `transitionend` callback + * @param {HTMLElement | Element} element target + * @param {EventListener} handler `transitionend` callback */ function emulateTransitionEnd(element, handler) { var called = 0; @@ -82,11 +107,10 @@ function emulateTransitionEnd(element, handler) { var duration = getElementTransitionDuration(element); var delay = getElementTransitionDelay(element); - if (duration) { + if (supportTransition && duration) { /** * Wrap the handler in on -> off callback * @param {Event} e Event object - * @callback */ var transitionEndWrapper = function (e) { if (e.target === element) { @@ -105,27 +129,45 @@ function emulateTransitionEnd(element, handler) { } /** - * Checks if an element is an `Element`. - * - * @param {any} element the target element - * @returns {boolean} the query result + * Returns the `document` or the `#document` element. + * @see https://github.com/floating-ui/floating-ui + * @param {(Node | HTMLElement | Element | globalThis)=} node + * @returns {Document} */ -function isElement(element) { - return element instanceof Element; +function getDocument(node) { + if (node instanceof HTMLElement) { return node.ownerDocument; } + if (node instanceof Window) { return node.document; } + return window.document; } /** - * Utility to check if target is typeof Element + * A global array of possible `ParentNode`. + */ +var parentNodes = [Document, Node, Element, HTMLElement]; + +/** + * A global array with `Element` | `HTMLElement`. + */ +var elementNodes = [Element, HTMLElement]; + +/** + * Utility to check if target is typeof `HTMLElement`, `Element`, `Node` * or find one that matches a selector. * - * @param {Element | string} selector the input selector or target element - * @param {Element=} parent optional Element to look into - * @return {Element?} the Element or `querySelector` result + * @param {HTMLElement | Element | string} selector the input selector or target element + * @param {(HTMLElement | Element | Node | Document)=} parent optional node to look into + * @return {(HTMLElement | Element)?} the `HTMLElement` or `querySelector` result */ -function queryElement(selector, parent) { - var lookUp = parent && isElement(parent) ? parent : document; - // @ts-ignore - return isElement(selector) ? selector : lookUp.querySelector(selector); +function querySelector(selector, parent) { + var selectorIsString = typeof selector === 'string'; + var lookUp = parent && parentNodes.some(function (x) { return parent instanceof x; }) + ? parent : getDocument(); + + if (!selectorIsString && [].concat( elementNodes ).some(function (x) { return selector instanceof x; })) { + return selector; + } + // @ts-ignore -- `ShadowRoot` is also a node + return selectorIsString ? lookUp.querySelector(selector) : null; } /** BSN v4 custom event */ @@ -182,7 +224,7 @@ function Alert(elem) { // event handlers function clickHandler(e) { alert = e && e.target.closest('.alert'); - element = queryElement('[data-dismiss="alert"]', alert); + element = querySelector('[data-dismiss="alert"]', alert); if (element && alert && (element === e.target || element.contains(e.target))) { self.close(); } } function transitionEndHandler() { @@ -209,7 +251,7 @@ function Alert(elem) { // INIT // initialization element - element = queryElement(elem); + element = querySelector(elem); // find the target alert alert = element.closest('.alert'); @@ -341,7 +383,7 @@ function Button(elem) { // init // initialization element - element = queryElement(elem); + element = querySelector(elem); // reset on re-init if (element.Button) { element.Button.dispose(); } @@ -363,7 +405,7 @@ function Button(elem) { // activate items on load Array.from(labels).forEach(function (btn) { - var hasChecked = queryElement('input:checked', btn); + var hasChecked = querySelector('input:checked', btn); if (!btn.classList.contains('active') && hasChecked) { btn.classList.add('active'); } @@ -380,70 +422,85 @@ function Button(elem) { var mouseHoverEvents = ('onmouseleave' in document) ? ['mouseenter', 'mouseleave'] : ['mouseover', 'mouseout']; /** - * A global namespace for 'addEventListener' string. - * @type {string} - */ -var addEventListener = 'addEventListener'; - -/** - * A global namespace for 'removeEventListener' string. - * @type {string} + * A global namespace for most scroll event listeners. + * @type {Partial} */ -var removeEventListener = 'removeEventListener'; +var passiveHandler = { passive: true }; /** - * A global namespace for passive events support. - * @type {boolean} + * Returns the bounding client rect of a target `HTMLElement`. + * + * @see https://github.com/floating-ui/floating-ui + * + * @param {HTMLElement | Element} element event.target + * @param {boolean=} includeScale when *true*, the target scale is also computed + * @returns {SHORTER.BoundingClientRect} the bounding client rect object */ -var supportPassive = (function () { - var result = false; - try { - var opts = Object.defineProperty({}, 'passive', { - get: function get() { - result = true; - return result; - }, - }); - document[addEventListener]('DOMContentLoaded', function wrap() { - document[removeEventListener]('DOMContentLoaded', wrap, opts); - }, opts); - } catch (e) { - throw Error('Passive events are not supported'); +function getBoundingClientRect(element, includeScale) { + var ref = element.getBoundingClientRect(); + var width = ref.width; + var height = ref.height; + var top = ref.top; + var right = ref.right; + var bottom = ref.bottom; + var left = ref.left; + var scaleX = 1; + var scaleY = 1; + + if (includeScale && element instanceof HTMLElement) { + var offsetWidth = element.offsetWidth; + var offsetHeight = element.offsetHeight; + scaleX = offsetWidth > 0 ? Math.round(width) / offsetWidth || 1 : 1; + scaleY = offsetHeight > 0 ? Math.round(height) / offsetHeight || 1 : 1; } - return result; -})(); - -// general event options + return { + width: width / scaleX, + height: height / scaleY, + top: top / scaleY, + right: right / scaleX, + bottom: bottom / scaleY, + left: left / scaleX, + x: left / scaleX, + y: top / scaleY, + }; +} /** - * A global namespace for most scroll event listeners. + * Returns the `document.documentElement` or the `` element. + * + * @param {(Node | HTMLElement | Element | globalThis)=} node + * @returns {HTMLElement | HTMLHtmlElement} */ -var passiveHandler = supportPassive ? { passive: true } : false; +function getDocumentElement(node) { + return getDocument(node).documentElement; +} /** - * Utility to determine if an `Element` + * Utility to determine if an `HTMLElement` * is partially visible in viewport. * - * @param {Element} element target - * @return {boolean} Boolean + * @param {HTMLElement | Element} element target + * @return {boolean} the query result */ -function isElementInScrollRange(element) { - var bcr = element.getBoundingClientRect(); - var viewportHeight = window.innerHeight || document.documentElement.clientHeight; - return bcr.top <= viewportHeight && bcr.bottom >= 0; // bottom && top -} +var isElementInScrollRange = function (element) { + var ref = getBoundingClientRect(element); + var top = ref.top; + var bottom = ref.bottom; + var ref$1 = getDocumentElement(element); + var clientHeight = ref$1.clientHeight; + // checks bottom && top + return top <= clientHeight && bottom >= 0; +}; /** - * Utility to force re-paint of an Element + * Utility to force re-paint of an `HTMLElement` target. * - * @param {Element | HTMLElement} element is the target - * @return {number} the Element.offsetHeight value + * @param {HTMLElement | Element} element is the target + * @return {number} the `Element.offsetHeight` value */ -function reflow(element) { - // @ts-ignore - return element.offsetHeight; -} +// @ts-ignore +var reflow = function (element) { return element.offsetHeight; }; /* Native JavaScript for Bootstrap 4 | Carousel ----------------------------------------------- */ @@ -733,7 +790,7 @@ function Carousel(elem, opsInput) { // init // initialization element - element = queryElement(elem); + element = querySelector(elem); // reset on re-init if (element.Carousel) { element.Carousel.dispose(); } @@ -887,8 +944,8 @@ function Collapse(elem, opsInput) { if (accordion) { (assign = accordion.getElementsByClassName('collapse show'), activeCollapse = assign[0]); - activeElement = activeCollapse && (queryElement(("[data-target=\"#" + (activeCollapse.id) + "\"]"), accordion) - || queryElement(("[href=\"#" + (activeCollapse.id) + "\"]"), accordion)); + activeElement = activeCollapse && (querySelector(("[data-target=\"#" + (activeCollapse.id) + "\"]"), accordion) + || querySelector(("[href=\"#" + (activeCollapse.id) + "\"]"), accordion)); } if (!collapse.isAnimating) { @@ -908,7 +965,7 @@ function Collapse(elem, opsInput) { // init // initialization element - element = queryElement(elem); + element = querySelector(elem); // reset on re-init if (element.Collapse) { element.Collapse.dispose(); } @@ -923,7 +980,7 @@ function Collapse(elem, opsInput) { hiddenCustomEvent = bootstrapCustomEvent('hidden', 'collapse'); // determine targets - collapse = queryElement(options.target || element.getAttribute('data-target') || element.getAttribute('href')); + collapse = querySelector(options.target || element.getAttribute('data-target') || element.getAttribute('href')); if (collapse !== null) { collapse.isAnimating = false; } var accordionSelector = options.parent || accordionData; @@ -944,11 +1001,9 @@ function Collapse(elem, opsInput) { /** * Points the focus to a specific element. - * @param {Element} element target + * @param {HTMLElement} element target */ -function setFocus(element) { - element.focus(); -} +var setFocus = function (element) { return element.focus(); }; /* Native JavaScript for Bootstrap 4 | Dropdown ----------------------------------------------- */ @@ -976,7 +1031,7 @@ function Dropdown(elem, option) { // preventDefault on empty anchor links function preventEmptyAnchor(anchor) { if ((anchor.hasAttribute('href') && anchor.href.slice(-1) === '#') || (anchor.parentNode - && anchor.hasAttribute('href') + && anchor.parentNode.hasAttribute('href') && anchor.parentNode.href.slice(-1) === '#')) { this.preventDefault(); } } // toggle dismissible events @@ -1091,14 +1146,14 @@ function Dropdown(elem, option) { // init // initialization element - element = queryElement(elem); + element = querySelector(elem); // reset on re-init if (element.Dropdown) { element.Dropdown.dispose(); } // set targets parent = element.parentNode; - menu = queryElement('.dropdown-menu', parent); + menu = querySelector('.dropdown-menu', parent); Array.from(menu.children).forEach(function (child) { if (child.children.length && child.children[0].tagName === 'A') { @@ -1201,7 +1256,7 @@ function Modal(elem, opsInput) { // element can be the modal/triggering button } function createOverlay() { var newOverlay = document.createElement('div'); - overlay = queryElement('.modal-backdrop'); + overlay = querySelector('.modal-backdrop'); if (overlay === null) { newOverlay.setAttribute('class', ("modal-backdrop" + (ops.animation ? ' fade' : ''))); @@ -1211,7 +1266,7 @@ function Modal(elem, opsInput) { // element can be the modal/triggering button return overlay; } function removeOverlay() { - overlay = queryElement('.modal-backdrop'); + overlay = querySelector('.modal-backdrop'); if (overlay && !document.getElementsByClassName('modal show')[0]) { document.body.removeChild(overlay); overlay = null; } @@ -1252,7 +1307,7 @@ function Modal(elem, opsInput) { // element can be the modal/triggering button modal.style.display = ''; if (element) { setFocus(element); } - overlay = queryElement('.modal-backdrop'); + overlay = querySelector('.modal-backdrop'); // force can also be the transitionEvent object, we wanna make sure it's not if (force !== 1 && overlay && overlay.classList.contains('show') && !document.getElementsByClassName('modal show')[0]) { @@ -1354,7 +1409,7 @@ function Modal(elem, opsInput) { // element can be the modal/triggering button else { triggerHide(); } }; self.setContent = function (content) { - queryElement('.modal-content', modal).innerHTML = content; + querySelector('.modal-content', modal).innerHTML = content; }; self.update = function () { if (modal.classList.contains('show')) { @@ -1369,10 +1424,10 @@ function Modal(elem, opsInput) { // element can be the modal/triggering button // init // the modal (both JavaScript / DATA API init) / triggering button element (DATA API) - element = queryElement(elem); + element = querySelector(elem); // determine modal, triggering element - var checkModal = queryElement(element.getAttribute('data-target') || element.getAttribute('href')); + var checkModal = querySelector(element.getAttribute('data-target') || element.getAttribute('href')); modal = element.classList.contains('modal') ? element : checkModal; // set fixed items @@ -1416,7 +1471,7 @@ function Modal(elem, opsInput) { // element can be the modal/triggering button /** * A global namespace for mouse click events. - * @type {{down: string, up: string}} + * @type {Record} */ var mouseClickEvents = { down: 'mousedown', up: 'mouseup' }; @@ -1566,7 +1621,7 @@ function Popover(elem, opsInput) { // handlers function dismissibleHandler(e) { - if (popover !== null && e.target === queryElement('.close', popover)) { + if (popover !== null && e.target === querySelector('.close', popover)) { self.hide(); } } @@ -1622,8 +1677,8 @@ function Popover(elem, opsInput) { popover.className = popoverTemplate.firstChild.className; popover.innerHTML = popoverTemplate.firstChild.innerHTML; - var popoverHeader = queryElement('.popover-header', popover); - var popoverBody = queryElement('.popover-body', popover); + var popoverHeader = querySelector('.popover-header', popover); + var popoverBody = querySelector('.popover-body', popover); // fill the template with content from data attributes if (titleString && popoverHeader) { popoverHeader.innerHTML = titleString.trim(); } @@ -1730,7 +1785,7 @@ function Popover(elem, opsInput) { // INIT // initialization element - element = queryElement(elem); + element = querySelector(elem); // reset on re-init if (element.Popover) { element.Popover.dispose(); } @@ -1754,8 +1809,8 @@ function Popover(elem, opsInput) { hiddenCustomEvent = bootstrapCustomEvent('hidden', 'popover'); // check container - var containerElement = queryElement(options.container); - var containerDataElement = queryElement(containerData); + var containerElement = querySelector(options.container); + var containerDataElement = querySelector(containerData); // maybe the element is inside a modal var modal = element.closest('.modal'); @@ -1839,7 +1894,7 @@ function ScrollSpy(elem, opsInput) { Array.from(links).forEach(function (link) { href = link.getAttribute('href'); - targetItem = href && href.charAt(0) === '#' && href.slice(-1) !== '#' && queryElement(href); + targetItem = href && href.charAt(0) === '#' && href.slice(-1) !== '#' && querySelector(href); if (targetItem) { vars.items.push(link); @@ -1933,7 +1988,7 @@ function ScrollSpy(elem, opsInput) { // init // initialization element, the element we spy on - element = queryElement(elem); + element = querySelector(elem); // reset on re-init if (element.ScrollSpy) { element.ScrollSpy.dispose(); } @@ -1944,7 +1999,7 @@ function ScrollSpy(elem, opsInput) { var offsetData = element.getAttribute('data-offset'); // targets - spyTarget = queryElement(options.target || targetData); + spyTarget = querySelector(options.target || targetData); // determine which is the real scrollTarget scrollTarget = element.clientHeight < element.scrollHeight ? element : window; @@ -2079,7 +2134,7 @@ function Tab(elem, opsInput) { } return activeTab; } - function getActiveContent() { return queryElement(getActiveTab().getAttribute('href')); } + function getActiveContent() { return querySelector(getActiveTab().getAttribute('href')); } // handler function clickHandler(e) { e.preventDefault(); @@ -2092,7 +2147,7 @@ function Tab(elem, opsInput) { next = next || element; if (!next.classList.contains('active')) { - nextContent = queryElement(next.getAttribute('href')); // this is the actual object, the next tab content to activate + nextContent = querySelector(next.getAttribute('href')); // this is the actual object, the next tab content to activate activeTab = getActiveTab(); activeContent = getActiveContent(); @@ -2125,7 +2180,7 @@ function Tab(elem, opsInput) { // INIT // initialization element - element = queryElement(elem); + element = querySelector(elem); // reset on re-init if (element.Tab) { element.Tab.dispose(); } @@ -2134,7 +2189,7 @@ function Tab(elem, opsInput) { var heightData = element.getAttribute('data-height'); // event targets tabs = element.closest('.nav'); - dropdown = tabs && queryElement('.dropdown-toggle', tabs); + dropdown = tabs && querySelector('.dropdown-toggle', tabs); // instance options var animateHeight = !(!supportTransition || (options.height === false || heightData === 'false')); @@ -2233,7 +2288,7 @@ function Toast(elem, opsInput) { // init // initialization element - element = queryElement(elem); + element = querySelector(elem); // reset on re-init if (element.Toast) { element.Toast.dispose(); } @@ -2317,7 +2372,7 @@ function Tooltip(elem, opsInput) { tooltip.className = tooltipMarkup.firstChild.className; tooltip.innerHTML = tooltipMarkup.firstChild.innerHTML; - queryElement('.tooltip-inner', tooltip).innerHTML = titleString.trim(); + querySelector('.tooltip-inner', tooltip).innerHTML = titleString.trim(); } else { // tooltip arrow var tooltipArrow = document.createElement('div'); @@ -2418,7 +2473,7 @@ function Tooltip(elem, opsInput) { // init // initialization element - element = queryElement(elem); + element = querySelector(elem); // reset on re-init if (element.Tooltip) { element.Tooltip.dispose(); } @@ -2430,8 +2485,8 @@ function Tooltip(elem, opsInput) { var containerData = element.getAttribute('data-container'); // check container - var containerElement = queryElement(options.container); - var containerDataElement = queryElement(containerData); + var containerElement = querySelector(options.container); + var containerDataElement = querySelector(containerData); // maybe the element is inside a modal var modal = element.closest('.modal'); @@ -2524,7 +2579,7 @@ function removeDataAPI(context) { }); } -var version = "4.1.0"; +var version = "4.1.0alpha1"; var Version = version; diff --git a/dist/bootstrap-native-v4-esm.min.js b/dist/bootstrap-native-v4-esm.min.js index a07d8232..7d8db084 100644 --- a/dist/bootstrap-native-v4-esm.min.js +++ b/dist/bootstrap-native-v4-esm.min.js @@ -1,2 +1,2 @@ -// Native JavaScript for Bootstrap v4.1.0 | 2021 © dnp_theme | MIT-License -var t="webkitTransition"in document.head.style?"webkitTransitionEnd":"transitionend",e="webkitTransition"in document.head.style||"transition"in document.head.style,i="webkitTransition"in document.head.style?"webkitTransitionDelay":"transitionDelay",n="webkitTransition"in document.head.style?"webkitTransitionProperty":"transitionProperty";var a="webkitTransition"in document.head.style?"webkitTransitionDuration":"transitionDuration";function o(t){var i=getComputedStyle(t),o=i[n],s=i[a],l=s.includes("ms")?1:1e3,r=e&&o&&"none"!==o?parseFloat(s)*l:0;return Number.isNaN(r)?0:r}function s(a,s){var l=0,r=new Event(t),c=o(a),d=function(t){var a=getComputedStyle(t),o=a[n],s=a[i],l=s.includes("ms")?1:1e3,r=e&&o&&"none"!==o?parseFloat(s)*l:0;return Number.isNaN(r)?0:r}(a);if(c){var u=function(e){e.target===a&&(s.apply(a,[e]),a.removeEventListener(t,u),l=1)};a.addEventListener(t,u),setTimeout((function(){l||a.dispatchEvent(r)}),c+d+17)}else s.apply(a,[r])}function l(t){return t instanceof Element}function r(t,e){var i=e&&l(e)?e:document;return l(t)?t:i.querySelector(t)}function c(t,e,i){var n=new CustomEvent(t+".bs."+e,{cancelable:!0});return void 0!==i&&Object.keys(i).forEach((function(t){Object.defineProperty(n,t,{value:i[t]})})),n}function d(t){this&&this.dispatchEvent(t)}function u(t){var e,i,n=this,a=c("close","alert"),o=c("closed","alert");function l(t){e[t?"addEventListener":"removeEventListener"]("click",u,!1)}function u(t){i=t&&t.target.closest(".alert"),(e=r('[data-dismiss="alert"]',i))&&i&&(e===t.target||e.contains(t.target))&&n.close()}function m(){l(),i.parentNode.removeChild(i),d.call(i,o)}n.close=function(){if(i&&e&&i.classList.contains("show")){if(d.call(i,a),a.defaultPrevented)return;n.dispose(),i.classList.remove("show"),i.classList.contains("fade")?s(i,m):m()}},n.dispose=function(){l(),delete e.Alert},e=r(t),i=e.closest(".alert"),e.Alert&&e.Alert.dispose(),e.Alert||l(1),n.element=e,e.Alert=n}function m(t){var e,i,n=c("change","button");function a(t){var a=t.target,o=a.closest("LABEL"),s=null;"LABEL"===a.tagName?s=a:o&&(s=o);var l=s&&s.getElementsByTagName("INPUT")[0];if(l){if(d.call(l,n),d.call(e,n),"checkbox"===l.type){if(n.defaultPrevented)return;l.checked?(s.classList.remove("active"),l.getAttribute("checked"),l.removeAttribute("checked"),l.checked=!1):(s.classList.add("active"),l.getAttribute("checked"),l.setAttribute("checked","checked"),l.checked=!0),e.toggled||(e.toggled=!0)}if("radio"===l.type&&!e.toggled){if(n.defaultPrevented)return;(!l.checked||0===t.screenX&&0===t.screenY)&&(s.classList.add("active"),s.classList.add("focus"),l.setAttribute("checked","checked"),l.checked=!0,e.toggled=!0,Array.from(i).forEach((function(t){var e=t.getElementsByTagName("INPUT")[0];t!==s&&t.classList.contains("active")&&(d.call(e,n),t.classList.remove("active"),e.removeAttribute("checked"),e.checked=!1)})))}setTimeout((function(){e.toggled=!1}),50)}}function o(t){32===(t.which||t.keyCode)&&t.target===document.activeElement&&a(t)}function s(t){32===(t.which||t.keyCode)&&t.preventDefault()}function l(t){if("INPUT"===t.target.tagName){var e="focusin"===t.type?"add":"remove";t.target.closest(".btn").classList[e]("focus")}}function u(t){var i=t?"addEventListener":"removeEventListener";e[i]("click",a,!1),e[i]("keyup",o,!1),e[i]("keydown",s,!1),e[i]("focusin",l,!1),e[i]("focusout",l,!1)}this.dispose=function(){u(),delete e.Button},(e=r(t)).Button&&e.Button.dispose(),(i=e.getElementsByClassName("btn")).length&&(e.Button||u(1),e.toggled=!1,e.Button=this,Array.from(i).forEach((function(t){var e=r("input:checked",t);!t.classList.contains("active")&&e&&t.classList.add("active"),t.classList.contains("active")&&!e&&t.classList.remove("active")})))}var f="onmouseleave"in document?["mouseenter","mouseleave"]:["mouseover","mouseout"],h=!!function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){return t=!0}});document.addEventListener("DOMContentLoaded",(function t(){document.removeEventListener("DOMContentLoaded",t,e)}),e)}catch(t){throw Error("Passive events are not supported")}return t}()&&{passive:!0};function g(t){return t.offsetHeight}function p(t,e){var i,n,a,l,u,m,p,v,L,b,y,w,A,T=e||{},E=this;function k(){!1===m.interval||l.classList.contains("paused")||(l.classList.add("paused"),u.isSliding||(clearInterval(u.timer),u.timer=null))}function C(){!1!==m.interval&&l.classList.contains("paused")&&(l.classList.remove("paused"),u.isSliding||(clearInterval(u.timer),u.timer=null,E.cycle()))}function x(t){if(t.preventDefault(),!u.isSliding){var e=t.target;e&&!e.classList.contains("active")&&e.getAttribute("data-slide-to")&&(u.index=+e.getAttribute("data-slide-to"),E.slideTo(u.index))}}function N(t){if(t.preventDefault(),!u.isSliding){var e=t.currentTarget||t.srcElement;e===y?u.index+=1:e===b&&(u.index-=1),E.slideTo(u.index)}}function P(t){var e=t.which;if(!u.isSliding){switch(e){case 39:u.index+=1;break;case 37:u.index-=1;break;default:return}E.slideTo(u.index)}}function H(t){var e=t?"addEventListener":"removeEventListener";m.pause&&m.interval&&(l[e](f[0],k,!1),l[e](f[1],C,!1),l[e]("touchstart",k,h),l[e]("touchend",C,h)),m.touch&&L.length>1&&l[e]("touchstart",B,h),y&&y[e]("click",N,!1),b&&b[e]("click",N,!1),w&&w[e]("click",x,!1),m.keyboard&&window[e]("keydown",P,!1)}function S(t){var e=t?"addEventListener":"removeEventListener";l[e]("touchmove",M,h),l[e]("touchend",I,h)}function B(t){u.isTouch||(u.touchPosition.startX=t.changedTouches[0].pageX,l.contains(t.target)&&(u.isTouch=!0,S(1)))}function M(t){u.isTouch?(u.touchPosition.currentX=t.changedTouches[0].pageX,"touchmove"===t.type&&t.changedTouches.length>1&&t.preventDefault()):t.preventDefault()}function I(t){if(u.isTouch&&!u.isSliding&&(u.touchPosition.endX=u.touchPosition.currentX||t.changedTouches[0].pageX,u.isTouch)){if((!l.contains(t.target)||!l.contains(t.relatedTarget))&&Math.abs(u.touchPosition.startX-u.touchPosition.endX)<75)return;u.touchPosition.currentXu.touchPosition.startX&&(u.index-=1),u.isTouch=!1,E.slideTo(u.index),S()}}function D(t){Array.from(A).forEach((function(t){return t.classList.remove("active")})),A[t]&&A[t].classList.add("active")}function X(t){if(u.touchPosition){var e=u.index,i=t&&t.target!==L[e]?1e3*t.elapsedTime+100:20,n=E.getActiveIndex(),a="left"===u.direction?"next":"prev";u.isSliding&&setTimeout((function(){u.touchPosition&&(u.isSliding=!1,L[e].classList.add("active"),L[n].classList.remove("active"),L[e].classList.remove("carousel-item-"+a),L[e].classList.remove("carousel-item-"+u.direction),L[n].classList.remove("carousel-item-"+u.direction),d.call(l,v),document.hidden||!m.interval||l.classList.contains("paused")||E.cycle())}),i)}}if(E.cycle=function(){u.timer&&(clearInterval(u.timer),u.timer=null),u.timer=setInterval((function(){var t=u.index||E.getActiveIndex();(function(t){var e=t.getBoundingClientRect(),i=window.innerHeight||document.documentElement.clientHeight;return e.top<=i&&e.bottom>=0})(l)&&(t+=1,E.slideTo(t))}),m.interval)},E.slideTo=function(t){if(!u.isSliding){var e=E.getActiveIndex(),i=t;if(e!==i){ei||e===L.length-1&&0===i)&&(u.direction="right"),i<0?i=L.length-1:i>=L.length&&(i=0);var n="left"===u.direction?"next":"prev",a={relatedTarget:L[i],direction:u.direction,from:e,to:i};p=c("slide","carousel",a),v=c("slid","carousel",a),d.call(l,p),p.defaultPrevented||(u.index=i,u.isSliding=!0,clearInterval(u.timer),u.timer=null,D(i),o(L[i])&&l.classList.contains("slide")?(L[i].classList.add("carousel-item-"+n),g(L[i]),L[i].classList.add("carousel-item-"+u.direction),L[e].classList.add("carousel-item-"+u.direction),s(L[i],X)):(L[i].classList.add("active"),g(L[i]),L[e].classList.remove("active"),setTimeout((function(){u.isSliding=!1,m.interval&&l&&!l.classList.contains("paused")&&E.cycle(),d.call(l,v)}),100)))}}},E.getActiveIndex=function(){return Array.from(L).indexOf(l.getElementsByClassName("carousel-item active")[0])||0},E.dispose=function(){var t=["left","right","prev","next"];Array.from(L).forEach((function(e,i){e.classList.contains("active")&&D(i),t.forEach((function(t){return e.classList.remove("carousel-item-"+t)}))})),clearInterval(u.timer),H(),u={},m={},delete l.Carousel},(l=r(t)).Carousel&&l.Carousel.dispose(),L=l.getElementsByClassName("carousel-item"),i=l.getElementsByClassName("carousel-control-prev"),b=i[0],n=l.getElementsByClassName("carousel-control-next"),y=n[0],a=l.getElementsByClassName("carousel-indicators"),w=a[0],A=w&&w.getElementsByTagName("LI")||[],!(L.length<2)){var O=l.getAttribute("data-interval"),R="false"===O?0:+O,W="false"===l.getAttribute("data-touch")?0:1,j="hover"===l.getAttribute("data-pause")||!1,z="true"===l.getAttribute("data-keyboard")||!1,U=T.interval,q=T.touch;(m={}).keyboard=!0===T.keyboard||z,m.pause=!("hover"!==T.pause&&!j)&&"hover",m.touch=q||W,m.interval=5e3,"number"==typeof U?m.interval=U:!1===U||0===R||!1===R?m.interval=0:Number.isNaN(R)||(m.interval=R),E.getActiveIndex()<0&&(L.length&&L[0].classList.add("active"),A.length&&D(0)),(u={}).direction="left",u.index=0,u.timer=null,u.isSliding=!1,u.isTouch=!1,u.touchPosition={startX:0,currentX:0,endX:0},H(1),m.interval&&E.cycle(),l.Carousel=E}}function v(t,e){var i,n,a,o,l,u,m,f=e||{},h=this,p=null,v=null;function L(t,e){d.call(t,u),u.defaultPrevented||(t.isAnimating=!0,t.style.height=t.scrollHeight+"px",t.classList.remove("collapse"),t.classList.remove("show"),t.classList.add("collapsing"),g(t),t.style.height="0px",s(t,(function(){t.isAnimating=!1,t.setAttribute("aria-expanded","false"),e.setAttribute("aria-expanded","false"),t.classList.remove("collapsing"),t.classList.add("collapse"),t.style.height="",d.call(t,m)})))}h.toggle=function(t){(t&&"A"===t.target.tagName||"A"===i.tagName)&&t.preventDefault(),(i.contains(t.target)||t.target===i)&&(v.classList.contains("show")?h.hide():h.show())},h.hide=function(){v.isAnimating||(L(v,i),i.classList.add("collapsed"))},h.show=function(){var t,e,c;p&&(t=p.getElementsByClassName("collapse show"),n=t[0],a=n&&(r('[data-target="#'+n.id+'"]',p)||r('[href="#'+n.id+'"]',p))),v.isAnimating||(a&&n!==v&&(L(n,a),a.classList.add("collapsed")),e=v,c=i,d.call(e,o),o.defaultPrevented||(e.isAnimating=!0,e.classList.add("collapsing"),e.classList.remove("collapse"),e.style.height=e.scrollHeight+"px",s(e,(function(){e.isAnimating=!1,e.setAttribute("aria-expanded","true"),c.setAttribute("aria-expanded","true"),e.classList.remove("collapsing"),e.classList.add("collapse"),e.classList.add("show"),e.style.height="",d.call(e,l)}))),i.classList.remove("collapsed"))},h.dispose=function(){i.removeEventListener("click",h.toggle,!1),delete i.Collapse},(i=r(t)).Collapse&&i.Collapse.dispose();var b=i.getAttribute("data-parent");o=c("show","collapse"),l=c("shown","collapse"),u=c("hide","collapse"),m=c("hidden","collapse"),null!==(v=r(f.target||i.getAttribute("data-target")||i.getAttribute("href")))&&(v.isAnimating=!1);var y=f.parent||b;p=y?i.closest(y):null,i.Collapse||i.addEventListener("click",h.toggle,!1),i.Collapse=h}function L(t){t.focus()}function b(t,e){var i,n,a,o,s,l,u,m,f=this,h=null,g=[];function p(t){(t.hasAttribute("href")&&"#"===t.href.slice(-1)||t.parentNode&&t.hasAttribute("href")&&"#"===t.parentNode.href.slice(-1))&&this.preventDefault()}function v(){var t=i.open?"addEventListener":"removeEventListener";document[t]("click",b,!1),document[t]("keydown",w,!1),document[t]("keyup",A,!1),document[t]("focus",b,!1)}function b(t){var e=t.target;if(e.getAttribute){var n=e&&e.getAttribute("data-toggle")||e.parentNode&&e.parentNode.getAttribute&&e.parentNode.getAttribute("data-toggle");("focus"!==t.type||e!==i&&e!==u&&!u.contains(e))&&(e!==u&&!u.contains(e)||!m&&!n)&&(h=e===i||i.contains(e)?i:null,f.hide(),p.call(t,e))}}function y(t){h=i,f.show(),p.call(t,t.target)}function w(t){var e=t.which||t.keyCode;38!==e&&40!==e||t.preventDefault()}function A(t){var e=t.which||t.keyCode,n=document.activeElement,a=n===i,o=u.contains(n),s=n.parentNode===u||n.parentNode.parentNode===u,l=g.indexOf(n);s&&(a?l=0:38===e?l=l>1?l-1:0:40===e&&(l=l=d,w=m.top+c/2+g/2>=u,T=m.top-c<0,E=m.left-r<0,k=m.top+c+g>=u,C=m.left+r+h>=d,x=i;x="right"===(x="left"===(x="bottom"===(x="top"===(x=("left"===x||"right"===x)&&E&&C?"top":x)&&T?"bottom":x)&&k?"top":x)&&E?"right":x)&&C?"left":x,-1===e.className.indexOf(x)&&(e.className=e.className.replace(/\b(top|bottom|left|right)+/,x));var N=v.offsetWidth,P=v.offsetHeight;"left"===x||"right"===x?(o="left"===x?m.left+f.x-r-(p?N:0):m.left+f.x+h,L?(a=m.top+f.y,s=g/2-N):w?(a=m.top+f.y-c+g,s=c-g/2-N):(a=m.top+f.y-c/2+g/2,s=c/2-(p?.9*P:P/2))):"top"!==x&&"bottom"!==x||(a="top"===x?m.top+f.y-c-(p?P:0):m.top+f.y+g,b?(o=0,l=m.left+h/2-N):y?(o=d-1.01*r,l=r-(d-m.left)+h/2-N/2):(o=m.left+f.x-r/2+h/2,l=r/2-(p?N:N/2))),e.style.top=a+"px",e.style.left=o+"px",s&&(v.style.top=s+"px"),l&&(v.style.left=l+"px")}function E(t,e){var i,n,a,o,l,u,m,g,p,v=e||{},L=this,b=null,y=0,A=/(iPhone|iPod|iPad)/.test(navigator.userAgent),E={};function k(t){null!==b&&t.target===r(".close",b)&&L.hide()}function C(t){return v[t]||i.dataset[t]||null}function x(){return C("title")}function N(){return C("content")}function P(){null===b&&i.focus()}function H(t){var e=t?"addEventListener":"removeEventListener";"hover"===E.trigger?(i[e](w,L.show),i[e](f[0],L.show),E.dismissible||i[e](f[1],L.hide)):"click"===E.trigger?i[e](E.trigger,L.toggle):"focus"===E.trigger&&(A&&i[e]("click",P,!1),i[e](E.trigger,L.toggle))}function S(t){b&&b.contains(t.target)||t.target===i||i.contains(t.target)||L.hide()}function B(t){var e=t?"addEventListener":"removeEventListener";E.dismissible?document[e]("click",k,!1):("focus"===E.trigger&&i[e]("blur",L.hide),"hover"===E.trigger&&document[e]("touchstart",S,h)),window[e]("resize",L.hide,h)}function M(){B(1),d.call(i,m)}function I(){B(),E.container.removeChild(b),y=null,b=null,d.call(i,p)}L.toggle=function(){null===b?L.show():L.hide()},L.show=function(){clearTimeout(y),y=setTimeout((function(){if(null===b){if(d.call(i,u),u.defaultPrevented)return;!function(){n=x(),a=(a=N())?a.trim():null,b=document.createElement("div");var t=document.createElement("div");if(t.classList.add("arrow"),b.appendChild(t),null!==a&&null===E.template){if(b.setAttribute("role","tooltip"),null!==n){var e=document.createElement("h3");e.classList.add("popover-header"),e.innerHTML=E.dismissible?n+l:n,b.appendChild(e)}var i=document.createElement("div");i.classList.add("popover-body"),i.innerHTML=E.dismissible&&null===n?a+l:a,b.appendChild(i)}else{var s=document.createElement("div");s.innerHTML=E.template.trim(),b.className=s.firstChild.className,b.innerHTML=s.firstChild.innerHTML;var c=r(".popover-header",b),d=r(".popover-body",b);n&&c&&(c.innerHTML=n.trim()),a&&d&&(d.innerHTML=a.trim())}E.container.appendChild(b),b.style.display="block",b.classList.contains("popover")||b.classList.add("popover"),b.classList.contains(E.animation)||b.classList.add(E.animation),b.classList.contains(o)||b.classList.add(o)}(),T(i,b,E.placement,E.container),b.classList.contains("show")||b.classList.add("show"),E.animation?s(b,M):M()}}),20)},L.hide=function(){clearTimeout(y),y=setTimeout((function(){if(b&&null!==b&&b.classList.contains("show")){if(d.call(i,g),g.defaultPrevented)return;b.classList.remove("show"),E.animation?s(b,I):I()}}),E.delay)},L.dispose=function(){L.hide(),H(),delete i.Popover},(i=r(t)).Popover&&i.Popover.dispose();var D=i.getAttribute("data-trigger"),X=i.getAttribute("data-animation"),O=i.getAttribute("data-placement"),R=i.getAttribute("data-dismissible"),W=i.getAttribute("data-delay"),j=i.getAttribute("data-container");l='',u=c("show","popover"),m=c("shown","popover"),g=c("hide","popover"),p=c("hidden","popover");var z=r(v.container),U=r(j),q=i.closest(".modal"),F=i.closest(".fixed-top"),Y=i.closest(".fixed-bottom");E.template=v.template?v.template:null,E.trigger=v.trigger?v.trigger:D||"hover",E.animation=v.animation&&"fade"!==v.animation?v.animation:X||"fade",E.placement=v.placement?v.placement:O||"top",E.delay=parseInt(v.delay||W,10)||200,E.dismissible=!(!v.dismissible&&"true"!==R),E.container=z||U||F||Y||q||document.body,o="bs-popover-"+E.placement,n=x(),((a=N())||E.template)&&(i.Popover||H(1),i.Popover=L)}function k(t,e){var i,n,a,o,s,l=e||{},u=this,m={};function f(){var t,e,s;(a=o.getElementsByTagName("A"),n.scrollTop=n.isWindow?A().y:i.scrollTop,n.length!==a.length||p()!==n.scrollHeight)&&(n.items=[],n.offsets=[],n.scrollHeight=p(),n.maxScroll=n.scrollHeight-(n.isWindow?window.innerHeight:i.getBoundingClientRect().height),Array.from(a).forEach((function(i){t=i.getAttribute("href"),(e=t&&"#"===t.charAt(0)&&"#"!==t.slice(-1)&&r(t))&&(n.items.push(i),s=e.getBoundingClientRect(),n.offsets.push((n.isWindow?s.top+n.scrollTop:e.offsetTop)-m.offset))})),n.length=n.items.length)}function g(t){var e=t?"addEventListener":"removeEventListener";s[e]("scroll",u.refresh,h),window[e]("resize",u.refresh,h)}function p(){return s.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)}function v(){Array.from(a).map((function(t){return t.classList.contains("active")&&t.classList.remove("active")}))}function L(t){var e,a=t;v(),n.activeItem=a,a.classList.add("active");for(var o=[];a.parentNode!==document.body;)((e=(a=a.parentNode).classList).contains("dropdown-menu")||e.contains("nav"))&&o.push(a);o.forEach((function(t){var e=t.previousElementSibling;e&&!e.classList.contains("active")&&e.classList.add("active")})),d.call(i,c("activate","scrollspy",{relatedTarget:n.activeItem}))}u.refresh=function(){if(f(),n.scrollTop>=n.maxScroll){var t=n.items[n.length-1];n.activeItem!==t&&L(t)}else{if(n.activeItem&&n.scrollTop0)return n.activeItem=null,void v();for(var e=n.length;e>-1;)n.activeItem!==n.items[e]&&n.scrollTop>=n.offsets[e]&&(void 0===n.offsets[e+1]||n.scrollTop1&&(p=t[t.length-1]):p=t[0],p}function P(){return r(N().getAttribute("href"))}function H(t){t.preventDefault(),h=t.currentTarget,a.isAnimating||T.show()}T.show=function(){if(!(h=h||n).classList.contains("active")){if(L=r(h.getAttribute("href")),p=N(),v=P(),m=c("hide","tab",{relatedTarget:h}),d.call(p,m),m.defaultPrevented)return;a.isAnimating=!0,p.classList.remove("active"),p.setAttribute("aria-selected","false"),h.classList.add("active"),h.setAttribute("aria-selected","true"),o&&(n.parentNode.classList.contains("dropdown-menu")?o.classList.contains("active")||o.classList.add("active"):o.classList.contains("active")&&o.classList.remove("active")),v.classList.contains("fade")?(v.classList.remove("show"),s(v,x)):x()}},T.dispose=function(){n.removeEventListener("click",H,!1),delete n.Tab},(n=r(t)).Tab&&n.Tab.dispose();var S=n.getAttribute("data-height");a=n.closest(".nav"),o=a&&r(".dropdown-toggle",a);var B=!(!e||!1===A.height||"false"===S);a.isAnimating=!1,n.Tab||n.addEventListener("click",H,!1),B&&(E=P().parentNode),n.Tab=T}function x(t,e){var i,n,a,o,l,u,m=e||{},f=this,h=0,p={};function v(){n.classList.remove("showing"),n.classList.add("show"),d.call(n,l),p.autohide&&f.hide()}function L(){n.classList.add("hide"),d.call(n,u)}function b(){n.classList.remove("show"),p.animation?s(n,L):L()}function y(){clearTimeout(h),i.removeEventListener("click",f.hide,!1),delete i.Toast}f.show=function(){if(n&&!n.classList.contains("show")){if(d.call(n,a),a.defaultPrevented)return;p.animation&&n.classList.add("fade"),n.classList.remove("hide"),g(n),n.classList.add("showing"),p.animation?s(n,v):v()}},f.hide=function(t){if(n&&n.classList.contains("show")){if(d.call(n,o),o.defaultPrevented)return;t?b():h=setTimeout(b,p.delay)}},f.dispose=function(){p.animation?s(n,y):y()},(i=r(t)).Toast&&i.Toast.dispose(),n=i.closest(".toast");var w=i.getAttribute("data-animation"),A=i.getAttribute("data-autohide"),T=i.getAttribute("data-delay");a=c("show","toast"),o=c("hide","toast"),l=c("shown","toast"),u=c("hidden","toast"),p.animation=!1===m.animation||"false"===w?0:1,p.autohide=!1===m.autohide||"false"===A?0:1,p.delay=parseInt(m.delay||T,10)||500,i.Toast||i.addEventListener("click",f.hide,!1),i.Toast=f}function N(t,e){var i,n,a,o,l,u,m,g=e||{},p=this,v=null,L=0,b={};function y(){return i.getAttribute("title")||i.getAttribute("data-title")||i.getAttribute("data-original-title")}function A(t){v&&v.contains(t.target)||t.target===i||i.contains(t.target)||p.hide()}function E(t){var e=t?"addEventListener":"removeEventListener";document[e]("touchstart",A,h),window[e]("resize",p.hide,h)}function k(){E(1),d.call(i,l)}function C(){E(),b.container.removeChild(v),v=null,L=null,d.call(i,m)}function x(t){var e=t?"addEventListener":"removeEventListener";i[e](w,p.show,!1),i[e](f[0],p.show,!1),i[e](f[1],p.hide,!1)}p.show=function(){clearTimeout(L),L=setTimeout((function(){if(null===v){if(d.call(i,o),o.defaultPrevented)return;!1!==function(){if(n=y()){if(v=document.createElement("div"),b.template){var t=document.createElement("div");t.innerHTML=b.template.trim(),v.className=t.firstChild.className,v.innerHTML=t.firstChild.innerHTML,r(".tooltip-inner",v).innerHTML=n.trim()}else{var e=document.createElement("div");e.classList.add("arrow"),v.appendChild(e);var i=document.createElement("div");i.classList.add("tooltip-inner"),v.appendChild(i),i.innerHTML=n}v.style.left="0",v.style.top="0",v.setAttribute("role","tooltip"),v.classList.contains("tooltip")||v.classList.add("tooltip"),v.classList.contains(b.animation)||v.classList.add(b.animation),v.classList.contains(a)||v.classList.add(a),b.container.appendChild(v)}}()&&(T(i,v,b.placement,b.container),v.classList.contains("show")||v.classList.add("show"),b.animation?s(v,k):k())}}),20)},p.hide=function(){clearTimeout(L),L=setTimeout((function(){if(v&&v.classList.contains("show")){if(d.call(i,u),u.defaultPrevented)return;v.classList.remove("show"),b.animation?s(v,C):C()}}),b.delay)},p.toggle=function(){v?p.hide():p.show()},p.dispose=function(){x(),p.hide(),i.setAttribute("title",i.getAttribute("data-original-title")),i.removeAttribute("data-original-title"),delete i.Tooltip},(i=r(t)).Tooltip&&i.Tooltip.dispose();var N=i.getAttribute("data-animation"),P=i.getAttribute("data-placement"),H=i.getAttribute("data-delay"),S=i.getAttribute("data-container"),B=r(g.container),M=r(S),I=i.closest(".modal");o=c("show","tooltip"),l=c("shown","tooltip"),u=c("hide","tooltip"),m=c("hidden","tooltip");var D=i.closest(".fixed-top"),X=i.closest(".fixed-bottom");b.animation=g.animation&&"fade"!==g.animation?g.animation:N||"fade",b.placement=g.placement?g.placement:P||"top",b.template=g.template?g.template:null,b.delay=parseInt(g.delay||H,10)||200,b.container=B||M||D||X||I||document.body,a="bs-tooltip-"+b.placement,(n=y())&&(i.Tooltip||(i.setAttribute("data-original-title",n),i.removeAttribute("title"),x(1)),i.Tooltip=p)}var P={};function H(t){var e=t instanceof Element?t:document;Object.keys(P).forEach((function(t){var i,n;i=P[t][0],n=e.querySelectorAll(P[t][1]),Array.from(n).map((function(t){return new i(t)}))}))}P.Alert=[u,'[data-dismiss="alert"]'],P.Button=[m,'[data-toggle="buttons"]'],P.Carousel=[p,'[data-ride="carousel"]'],P.Collapse=[v,'[data-toggle="collapse"]'],P.Dropdown=[b,'[data-toggle="dropdown"]'],P.Modal=[y,'[data-toggle="modal"]'],P.Popover=[E,'[data-toggle="popover"],[data-tip="popover"]'],P.ScrollSpy=[k,'[data-spy="scroll"]'],P.Tab=[C,'[data-toggle="tab"]'],P.Toast=[x,'[data-dismiss="toast"]'],P.Tooltip=[N,'[data-toggle="tooltip"],[data-tip="tooltip"]'],document.body?H():document.addEventListener("DOMContentLoaded",(function t(){H(),document.removeEventListener("DOMContentLoaded",t,!1)}),!1);var S={Alert:u,Button:m,Carousel:p,Collapse:v,Dropdown:b,Modal:y,Popover:E,ScrollSpy:k,Tab:C,Toast:x,Tooltip:N,initCallback:H,removeDataAPI:function(t){var e=t instanceof Element?t:document;Object.keys(P).forEach((function(t){var i,n;i=t,n=e.querySelectorAll(P[t][1]),Array.from(n).map((function(t){return t[i].dispose()}))}))},componentsInit:P,Version:"4.1.0"};export{S as default}; +// Native JavaScript for Bootstrap v4.1.0alpha1 | 2022 © dnp_theme | MIT-License +var t=document.head,e="webkitTransition"in t.style||"transition"in t.style,i="webkitTransition"in t.style?"webkitTransitionEnd":"transitionend",n="webkitTransition"in t.style?"webkitTransitionDelay":"transitionDelay",a="webkitTransition"in t.style?"webkitTransitionProperty":"transitionProperty";function o(t,e){var i=getComputedStyle(t);return e in i?i[e]:""}var s="webkitTransition"in t.style?"webkitTransitionDuration":"transitionDuration";function l(t){var i=o(t,a),n=o(t,s),l=n.includes("ms")?1:1e3,r=e&&i&&"none"!==i?parseFloat(n)*l:0;return Number.isNaN(r)?0:r}function r(t,s){var r=0,c=new Event(i),d=l(t),u=function(t){var i=o(t,a),s=o(t,n),l=s.includes("ms")?1:1e3,r=e&&i&&"none"!==i?parseFloat(s)*l:0;return Number.isNaN(r)?0:r}(t);if(e&&d){var m=function(e){e.target===t&&(s.apply(t,[e]),t.removeEventListener(i,m),r=1)};t.addEventListener(i,m),setTimeout((function(){r||t.dispatchEvent(c)}),d+u+17)}else s.apply(t,[c])}function c(t){return t instanceof HTMLElement?t.ownerDocument:t instanceof Window?t.document:window.document}var d=[Document,Node,Element,HTMLElement],u=[Element,HTMLElement];function m(t,e){var i="string"==typeof t,n=e&&d.some((function(t){return e instanceof t}))?e:c();return!i&&[].concat(u).some((function(e){return t instanceof e}))?t:i?n.querySelector(t):null}function f(t,e,i){var n=new CustomEvent(t+".bs."+e,{cancelable:!0});return void 0!==i&&Object.keys(i).forEach((function(t){Object.defineProperty(n,t,{value:i[t]})})),n}function h(t){this&&this.dispatchEvent(t)}function g(t){var e,i,n=this,a=f("close","alert"),o=f("closed","alert");function s(t){e[t?"addEventListener":"removeEventListener"]("click",l,!1)}function l(t){i=t&&t.target.closest(".alert"),(e=m('[data-dismiss="alert"]',i))&&i&&(e===t.target||e.contains(t.target))&&n.close()}function c(){s(),i.parentNode.removeChild(i),h.call(i,o)}n.close=function(){if(i&&e&&i.classList.contains("show")){if(h.call(i,a),a.defaultPrevented)return;n.dispose(),i.classList.remove("show"),i.classList.contains("fade")?r(i,c):c()}},n.dispose=function(){s(),delete e.Alert},e=m(t),i=e.closest(".alert"),e.Alert&&e.Alert.dispose(),e.Alert||s(1),n.element=e,e.Alert=n}function p(t){var e,i,n=f("change","button");function a(t){var a=t.target,o=a.closest("LABEL"),s=null;"LABEL"===a.tagName?s=a:o&&(s=o);var l=s&&s.getElementsByTagName("INPUT")[0];if(l){if(h.call(l,n),h.call(e,n),"checkbox"===l.type){if(n.defaultPrevented)return;l.checked?(s.classList.remove("active"),l.getAttribute("checked"),l.removeAttribute("checked"),l.checked=!1):(s.classList.add("active"),l.getAttribute("checked"),l.setAttribute("checked","checked"),l.checked=!0),e.toggled||(e.toggled=!0)}if("radio"===l.type&&!e.toggled){if(n.defaultPrevented)return;(!l.checked||0===t.screenX&&0===t.screenY)&&(s.classList.add("active"),s.classList.add("focus"),l.setAttribute("checked","checked"),l.checked=!0,e.toggled=!0,Array.from(i).forEach((function(t){var e=t.getElementsByTagName("INPUT")[0];t!==s&&t.classList.contains("active")&&(h.call(e,n),t.classList.remove("active"),e.removeAttribute("checked"),e.checked=!1)})))}setTimeout((function(){e.toggled=!1}),50)}}function o(t){32===(t.which||t.keyCode)&&t.target===document.activeElement&&a(t)}function s(t){32===(t.which||t.keyCode)&&t.preventDefault()}function l(t){if("INPUT"===t.target.tagName){var e="focusin"===t.type?"add":"remove";t.target.closest(".btn").classList[e]("focus")}}function r(t){var i=t?"addEventListener":"removeEventListener";e[i]("click",a,!1),e[i]("keyup",o,!1),e[i]("keydown",s,!1),e[i]("focusin",l,!1),e[i]("focusout",l,!1)}this.dispose=function(){r(),delete e.Button},(e=m(t)).Button&&e.Button.dispose(),(i=e.getElementsByClassName("btn")).length&&(e.Button||r(1),e.toggled=!1,e.Button=this,Array.from(i).forEach((function(t){var e=m("input:checked",t);!t.classList.contains("active")&&e&&t.classList.add("active"),t.classList.contains("active")&&!e&&t.classList.remove("active")})))}var v="onmouseleave"in document?["mouseenter","mouseleave"]:["mouseover","mouseout"],L={passive:!0};var b=function(t){var e=function(t,e){var i=t.getBoundingClientRect(),n=i.width,a=i.height,o=i.top,s=i.right,l=i.bottom,r=i.left,c=1,d=1;if(e&&t instanceof HTMLElement){var u=t.offsetWidth,m=t.offsetHeight;c=u>0&&Math.round(n)/u||1,d=m>0&&Math.round(a)/m||1}return{width:n/c,height:a/d,top:o/d,right:s/c,bottom:l/d,left:r/c,x:r/c,y:o/d}}(t),i=e.top,n=e.bottom;return i<=c(t).documentElement.clientHeight&&n>=0},y=function(t){return t.offsetHeight};function w(t,e){var i,n,a,o,s,c,d,u,g,p,w,A,T,E=e||{},k=this;function x(){!1===c.interval||o.classList.contains("paused")||(o.classList.add("paused"),s.isSliding||(clearInterval(s.timer),s.timer=null))}function N(){!1!==c.interval&&o.classList.contains("paused")&&(o.classList.remove("paused"),s.isSliding||(clearInterval(s.timer),s.timer=null,k.cycle()))}function C(t){if(t.preventDefault(),!s.isSliding){var e=t.target;e&&!e.classList.contains("active")&&e.getAttribute("data-slide-to")&&(s.index=+e.getAttribute("data-slide-to"),k.slideTo(s.index))}}function P(t){if(t.preventDefault(),!s.isSliding){var e=t.currentTarget||t.srcElement;e===w?s.index+=1:e===p&&(s.index-=1),k.slideTo(s.index)}}function H(t){var e=t.which;if(!s.isSliding){switch(e){case 39:s.index+=1;break;case 37:s.index-=1;break;default:return}k.slideTo(s.index)}}function M(t){var e=t?"addEventListener":"removeEventListener";c.pause&&c.interval&&(o[e](v[0],x,!1),o[e](v[1],N,!1),o[e]("touchstart",x,L),o[e]("touchend",N,L)),c.touch&&g.length>1&&o[e]("touchstart",B,L),w&&w[e]("click",P,!1),p&&p[e]("click",P,!1),A&&A[e]("click",C,!1),c.keyboard&&window[e]("keydown",H,!1)}function S(t){var e=t?"addEventListener":"removeEventListener";o[e]("touchmove",I,L),o[e]("touchend",D,L)}function B(t){s.isTouch||(s.touchPosition.startX=t.changedTouches[0].pageX,o.contains(t.target)&&(s.isTouch=!0,S(1)))}function I(t){s.isTouch?(s.touchPosition.currentX=t.changedTouches[0].pageX,"touchmove"===t.type&&t.changedTouches.length>1&&t.preventDefault()):t.preventDefault()}function D(t){if(s.isTouch&&!s.isSliding&&(s.touchPosition.endX=s.touchPosition.currentX||t.changedTouches[0].pageX,s.isTouch)){if((!o.contains(t.target)||!o.contains(t.relatedTarget))&&Math.abs(s.touchPosition.startX-s.touchPosition.endX)<75)return;s.touchPosition.currentXs.touchPosition.startX&&(s.index-=1),s.isTouch=!1,k.slideTo(s.index),S()}}function X(t){Array.from(T).forEach((function(t){return t.classList.remove("active")})),T[t]&&T[t].classList.add("active")}function R(t){if(s.touchPosition){var e=s.index,i=t&&t.target!==g[e]?1e3*t.elapsedTime+100:20,n=k.getActiveIndex(),a="left"===s.direction?"next":"prev";s.isSliding&&setTimeout((function(){s.touchPosition&&(s.isSliding=!1,g[e].classList.add("active"),g[n].classList.remove("active"),g[e].classList.remove("carousel-item-"+a),g[e].classList.remove("carousel-item-"+s.direction),g[n].classList.remove("carousel-item-"+s.direction),h.call(o,u),document.hidden||!c.interval||o.classList.contains("paused")||k.cycle())}),i)}}if(k.cycle=function(){s.timer&&(clearInterval(s.timer),s.timer=null),s.timer=setInterval((function(){var t=s.index||k.getActiveIndex();b(o)&&(t+=1,k.slideTo(t))}),c.interval)},k.slideTo=function(t){if(!s.isSliding){var e=k.getActiveIndex(),i=t;if(e!==i){ei||e===g.length-1&&0===i)&&(s.direction="right"),i<0?i=g.length-1:i>=g.length&&(i=0);var n="left"===s.direction?"next":"prev",a={relatedTarget:g[i],direction:s.direction,from:e,to:i};d=f("slide","carousel",a),u=f("slid","carousel",a),h.call(o,d),d.defaultPrevented||(s.index=i,s.isSliding=!0,clearInterval(s.timer),s.timer=null,X(i),l(g[i])&&o.classList.contains("slide")?(g[i].classList.add("carousel-item-"+n),y(g[i]),g[i].classList.add("carousel-item-"+s.direction),g[e].classList.add("carousel-item-"+s.direction),r(g[i],R)):(g[i].classList.add("active"),y(g[i]),g[e].classList.remove("active"),setTimeout((function(){s.isSliding=!1,c.interval&&o&&!o.classList.contains("paused")&&k.cycle(),h.call(o,u)}),100)))}}},k.getActiveIndex=function(){return Array.from(g).indexOf(o.getElementsByClassName("carousel-item active")[0])||0},k.dispose=function(){var t=["left","right","prev","next"];Array.from(g).forEach((function(e,i){e.classList.contains("active")&&X(i),t.forEach((function(t){return e.classList.remove("carousel-item-"+t)}))})),clearInterval(s.timer),M(),s={},c={},delete o.Carousel},(o=m(t)).Carousel&&o.Carousel.dispose(),g=o.getElementsByClassName("carousel-item"),i=o.getElementsByClassName("carousel-control-prev"),p=i[0],n=o.getElementsByClassName("carousel-control-next"),w=n[0],a=o.getElementsByClassName("carousel-indicators"),A=a[0],T=A&&A.getElementsByTagName("LI")||[],!(g.length<2)){var W=o.getAttribute("data-interval"),O="false"===W?0:+W,j="false"===o.getAttribute("data-touch")?0:1,z="hover"===o.getAttribute("data-pause")||!1,U="true"===o.getAttribute("data-keyboard")||!1,q=E.interval,F=E.touch;(c={}).keyboard=!0===E.keyboard||U,c.pause=!("hover"!==E.pause&&!z)&&"hover",c.touch=F||j,c.interval=5e3,"number"==typeof q?c.interval=q:!1===q||0===O||!1===O?c.interval=0:Number.isNaN(O)||(c.interval=O),k.getActiveIndex()<0&&(g.length&&g[0].classList.add("active"),T.length&&X(0)),(s={}).direction="left",s.index=0,s.timer=null,s.isSliding=!1,s.isTouch=!1,s.touchPosition={startX:0,currentX:0,endX:0},M(1),c.interval&&k.cycle(),o.Carousel=k}}function A(t,e){var i,n,a,o,s,l,c,d=e||{},u=this,g=null,p=null;function v(t,e){h.call(t,l),l.defaultPrevented||(t.isAnimating=!0,t.style.height=t.scrollHeight+"px",t.classList.remove("collapse"),t.classList.remove("show"),t.classList.add("collapsing"),y(t),t.style.height="0px",r(t,(function(){t.isAnimating=!1,t.setAttribute("aria-expanded","false"),e.setAttribute("aria-expanded","false"),t.classList.remove("collapsing"),t.classList.add("collapse"),t.style.height="",h.call(t,c)})))}u.toggle=function(t){(t&&"A"===t.target.tagName||"A"===i.tagName)&&t.preventDefault(),(i.contains(t.target)||t.target===i)&&(p.classList.contains("show")?u.hide():u.show())},u.hide=function(){p.isAnimating||(v(p,i),i.classList.add("collapsed"))},u.show=function(){var t,e,l;g&&(t=g.getElementsByClassName("collapse show"),n=t[0],a=n&&(m('[data-target="#'+n.id+'"]',g)||m('[href="#'+n.id+'"]',g))),p.isAnimating||(a&&n!==p&&(v(n,a),a.classList.add("collapsed")),e=p,l=i,h.call(e,o),o.defaultPrevented||(e.isAnimating=!0,e.classList.add("collapsing"),e.classList.remove("collapse"),e.style.height=e.scrollHeight+"px",r(e,(function(){e.isAnimating=!1,e.setAttribute("aria-expanded","true"),l.setAttribute("aria-expanded","true"),e.classList.remove("collapsing"),e.classList.add("collapse"),e.classList.add("show"),e.style.height="",h.call(e,s)}))),i.classList.remove("collapsed"))},u.dispose=function(){i.removeEventListener("click",u.toggle,!1),delete i.Collapse},(i=m(t)).Collapse&&i.Collapse.dispose();var L=i.getAttribute("data-parent");o=f("show","collapse"),s=f("shown","collapse"),l=f("hide","collapse"),c=f("hidden","collapse"),null!==(p=m(d.target||i.getAttribute("data-target")||i.getAttribute("href")))&&(p.isAnimating=!1);var b=d.parent||L;g=b?i.closest(b):null,i.Collapse||i.addEventListener("click",u.toggle,!1),i.Collapse=u}var T=function(t){return t.focus()};function E(t,e){var i,n,a,o,s,l,r,c,d=this,u=null,g=[];function p(t){(t.hasAttribute("href")&&"#"===t.href.slice(-1)||t.parentNode&&t.parentNode.hasAttribute("href")&&"#"===t.parentNode.href.slice(-1))&&this.preventDefault()}function v(){var t=i.open?"addEventListener":"removeEventListener";document[t]("click",L,!1),document[t]("keydown",y,!1),document[t]("keyup",w,!1),document[t]("focus",L,!1)}function L(t){var e=t.target;if(e.getAttribute){var n=e&&e.getAttribute("data-toggle")||e.parentNode&&e.parentNode.getAttribute&&e.parentNode.getAttribute("data-toggle");("focus"!==t.type||e!==i&&e!==r&&!r.contains(e))&&(e!==r&&!r.contains(e)||!c&&!n)&&(u=e===i||i.contains(e)?i:null,d.hide(),p.call(t,e))}}function b(t){u=i,d.show(),p.call(t,t.target)}function y(t){var e=t.which||t.keyCode;38!==e&&40!==e||t.preventDefault()}function w(t){var e=t.which||t.keyCode,n=document.activeElement,a=n===i,o=r.contains(n),s=n.parentNode===r||n.parentNode.parentNode===r,l=g.indexOf(n);s&&(a?l=0:38===e?l=l>1?l-1:0:40===e&&(l=l=d,w=m.top+c/2+g/2>=u,A=m.top-c<0,T=m.left-r<0,E=m.top+c+g>=u,k=m.left+r+h>=d,x=i;x="right"===(x="left"===(x="bottom"===(x="top"===(x=("left"===x||"right"===x)&&T&&k?"top":x)&&A?"bottom":x)&&E?"top":x)&&T?"right":x)&&k?"left":x,-1===e.className.indexOf(x)&&(e.className=e.className.replace(/\b(top|bottom|left|right)+/,x));var C=v.offsetWidth,P=v.offsetHeight;"left"===x||"right"===x?(o="left"===x?m.left+f.x-r-(p?C:0):m.left+f.x+h,L?(a=m.top+f.y,s=g/2-C):w?(a=m.top+f.y-c+g,s=c-g/2-C):(a=m.top+f.y-c/2+g/2,s=c/2-(p?.9*P:P/2))):"top"!==x&&"bottom"!==x||(a="top"===x?m.top+f.y-c-(p?P:0):m.top+f.y+g,b?(o=0,l=m.left+h/2-C):y?(o=d-1.01*r,l=r-(d-m.left)+h/2-C/2):(o=m.left+f.x-r/2+h/2,l=r/2-(p?C:C/2))),e.style.top=a+"px",e.style.left=o+"px",s&&(v.style.top=s+"px"),l&&(v.style.left=l+"px")}function P(t,e){var i,n,a,o,s,l,c,d,u,g=e||{},p=this,b=null,y=0,w=/(iPhone|iPod|iPad)/.test(navigator.userAgent),A={};function T(t){null!==b&&t.target===m(".close",b)&&p.hide()}function E(t){return g[t]||i.dataset[t]||null}function k(){return E("title")}function N(){return E("content")}function P(){null===b&&i.focus()}function H(t){var e=t?"addEventListener":"removeEventListener";"hover"===A.trigger?(i[e](x,p.show),i[e](v[0],p.show),A.dismissible||i[e](v[1],p.hide)):"click"===A.trigger?i[e](A.trigger,p.toggle):"focus"===A.trigger&&(w&&i[e]("click",P,!1),i[e](A.trigger,p.toggle))}function M(t){b&&b.contains(t.target)||t.target===i||i.contains(t.target)||p.hide()}function S(t){var e=t?"addEventListener":"removeEventListener";A.dismissible?document[e]("click",T,!1):("focus"===A.trigger&&i[e]("blur",p.hide),"hover"===A.trigger&&document[e]("touchstart",M,L)),window[e]("resize",p.hide,L)}function B(){S(1),h.call(i,c)}function I(){S(),A.container.removeChild(b),y=null,b=null,h.call(i,u)}p.toggle=function(){null===b?p.show():p.hide()},p.show=function(){clearTimeout(y),y=setTimeout((function(){if(null===b){if(h.call(i,l),l.defaultPrevented)return;!function(){n=k(),a=(a=N())?a.trim():null,b=document.createElement("div");var t=document.createElement("div");if(t.classList.add("arrow"),b.appendChild(t),null!==a&&null===A.template){if(b.setAttribute("role","tooltip"),null!==n){var e=document.createElement("h3");e.classList.add("popover-header"),e.innerHTML=A.dismissible?n+s:n,b.appendChild(e)}var i=document.createElement("div");i.classList.add("popover-body"),i.innerHTML=A.dismissible&&null===n?a+s:a,b.appendChild(i)}else{var l=document.createElement("div");l.innerHTML=A.template.trim(),b.className=l.firstChild.className,b.innerHTML=l.firstChild.innerHTML;var r=m(".popover-header",b),c=m(".popover-body",b);n&&r&&(r.innerHTML=n.trim()),a&&c&&(c.innerHTML=a.trim())}A.container.appendChild(b),b.style.display="block",b.classList.contains("popover")||b.classList.add("popover"),b.classList.contains(A.animation)||b.classList.add(A.animation),b.classList.contains(o)||b.classList.add(o)}(),C(i,b,A.placement,A.container),b.classList.contains("show")||b.classList.add("show"),A.animation?r(b,B):B()}}),20)},p.hide=function(){clearTimeout(y),y=setTimeout((function(){if(b&&null!==b&&b.classList.contains("show")){if(h.call(i,d),d.defaultPrevented)return;b.classList.remove("show"),A.animation?r(b,I):I()}}),A.delay)},p.dispose=function(){p.hide(),H(),delete i.Popover},(i=m(t)).Popover&&i.Popover.dispose();var D=i.getAttribute("data-trigger"),X=i.getAttribute("data-animation"),R=i.getAttribute("data-placement"),W=i.getAttribute("data-dismissible"),O=i.getAttribute("data-delay"),j=i.getAttribute("data-container");s='',l=f("show","popover"),c=f("shown","popover"),d=f("hide","popover"),u=f("hidden","popover");var z=m(g.container),U=m(j),q=i.closest(".modal"),F=i.closest(".fixed-top"),Y=i.closest(".fixed-bottom");A.template=g.template?g.template:null,A.trigger=g.trigger?g.trigger:D||"hover",A.animation=g.animation&&"fade"!==g.animation?g.animation:X||"fade",A.placement=g.placement?g.placement:R||"top",A.delay=parseInt(g.delay||O,10)||200,A.dismissible=!(!g.dismissible&&"true"!==W),A.container=z||U||F||Y||q||document.body,o="bs-popover-"+A.placement,n=k(),((a=N())||A.template)&&(i.Popover||H(1),i.Popover=p)}function H(t,e){var i,n,a,o,s,l=e||{},r=this,c={};function d(){var t,e,s;(a=o.getElementsByTagName("A"),n.scrollTop=n.isWindow?N().y:i.scrollTop,n.length!==a.length||g()!==n.scrollHeight)&&(n.items=[],n.offsets=[],n.scrollHeight=g(),n.maxScroll=n.scrollHeight-(n.isWindow?window.innerHeight:i.getBoundingClientRect().height),Array.from(a).forEach((function(i){t=i.getAttribute("href"),(e=t&&"#"===t.charAt(0)&&"#"!==t.slice(-1)&&m(t))&&(n.items.push(i),s=e.getBoundingClientRect(),n.offsets.push((n.isWindow?s.top+n.scrollTop:e.offsetTop)-c.offset))})),n.length=n.items.length)}function u(t){var e=t?"addEventListener":"removeEventListener";s[e]("scroll",r.refresh,L),window[e]("resize",r.refresh,L)}function g(){return s.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)}function p(){Array.from(a).map((function(t){return t.classList.contains("active")&&t.classList.remove("active")}))}function v(t){var e,a=t;p(),n.activeItem=a,a.classList.add("active");for(var o=[];a.parentNode!==document.body;)((e=(a=a.parentNode).classList).contains("dropdown-menu")||e.contains("nav"))&&o.push(a);o.forEach((function(t){var e=t.previousElementSibling;e&&!e.classList.contains("active")&&e.classList.add("active")})),h.call(i,f("activate","scrollspy",{relatedTarget:n.activeItem}))}r.refresh=function(){if(d(),n.scrollTop>=n.maxScroll){var t=n.items[n.length-1];n.activeItem!==t&&v(t)}else{if(n.activeItem&&n.scrollTop0)return n.activeItem=null,void p();for(var e=n.length;e>-1;)n.activeItem!==n.items[e]&&n.scrollTop>=n.offsets[e]&&(void 0===n.offsets[e+1]||n.scrollTop1&&(g=t[t.length-1]):g=t[0],g}function P(){return m(C().getAttribute("href"))}function H(t){t.preventDefault(),u=t.currentTarget,a.isAnimating||T.show()}T.show=function(){if(!(u=u||n).classList.contains("active")){if(v=m(u.getAttribute("href")),g=C(),p=P(),c=f("hide","tab",{relatedTarget:u}),h.call(g,c),c.defaultPrevented)return;a.isAnimating=!0,g.classList.remove("active"),g.setAttribute("aria-selected","false"),u.classList.add("active"),u.setAttribute("aria-selected","true"),o&&(n.parentNode.classList.contains("dropdown-menu")?o.classList.contains("active")||o.classList.add("active"):o.classList.contains("active")&&o.classList.remove("active")),p.classList.contains("fade")?(p.classList.remove("show"),r(p,N)):N()}},T.dispose=function(){n.removeEventListener("click",H,!1),delete n.Tab},(n=m(t)).Tab&&n.Tab.dispose();var M=n.getAttribute("data-height");a=n.closest(".nav"),o=a&&m(".dropdown-toggle",a);var S=!(!e||!1===A.height||"false"===M);a.isAnimating=!1,n.Tab||n.addEventListener("click",H,!1),S&&(E=P().parentNode),n.Tab=T}function S(t,e){var i,n,a,o,s,l,c=e||{},d=this,u=0,g={};function p(){n.classList.remove("showing"),n.classList.add("show"),h.call(n,s),g.autohide&&d.hide()}function v(){n.classList.add("hide"),h.call(n,l)}function L(){n.classList.remove("show"),g.animation?r(n,v):v()}function b(){clearTimeout(u),i.removeEventListener("click",d.hide,!1),delete i.Toast}d.show=function(){if(n&&!n.classList.contains("show")){if(h.call(n,a),a.defaultPrevented)return;g.animation&&n.classList.add("fade"),n.classList.remove("hide"),y(n),n.classList.add("showing"),g.animation?r(n,p):p()}},d.hide=function(t){if(n&&n.classList.contains("show")){if(h.call(n,o),o.defaultPrevented)return;t?L():u=setTimeout(L,g.delay)}},d.dispose=function(){g.animation?r(n,b):b()},(i=m(t)).Toast&&i.Toast.dispose(),n=i.closest(".toast");var w=i.getAttribute("data-animation"),A=i.getAttribute("data-autohide"),T=i.getAttribute("data-delay");a=f("show","toast"),o=f("hide","toast"),s=f("shown","toast"),l=f("hidden","toast"),g.animation=!1===c.animation||"false"===w?0:1,g.autohide=!1===c.autohide||"false"===A?0:1,g.delay=parseInt(c.delay||T,10)||500,i.Toast||i.addEventListener("click",d.hide,!1),i.Toast=d}function B(t,e){var i,n,a,o,s,l,c,d=e||{},u=this,g=null,p=0,b={};function y(){return i.getAttribute("title")||i.getAttribute("data-title")||i.getAttribute("data-original-title")}function w(t){g&&g.contains(t.target)||t.target===i||i.contains(t.target)||u.hide()}function A(t){var e=t?"addEventListener":"removeEventListener";document[e]("touchstart",w,L),window[e]("resize",u.hide,L)}function T(){A(1),h.call(i,s)}function E(){A(),b.container.removeChild(g),g=null,p=null,h.call(i,c)}function k(t){var e=t?"addEventListener":"removeEventListener";i[e](x,u.show,!1),i[e](v[0],u.show,!1),i[e](v[1],u.hide,!1)}u.show=function(){clearTimeout(p),p=setTimeout((function(){if(null===g){if(h.call(i,o),o.defaultPrevented)return;!1!==function(){if(n=y()){if(g=document.createElement("div"),b.template){var t=document.createElement("div");t.innerHTML=b.template.trim(),g.className=t.firstChild.className,g.innerHTML=t.firstChild.innerHTML,m(".tooltip-inner",g).innerHTML=n.trim()}else{var e=document.createElement("div");e.classList.add("arrow"),g.appendChild(e);var i=document.createElement("div");i.classList.add("tooltip-inner"),g.appendChild(i),i.innerHTML=n}g.style.left="0",g.style.top="0",g.setAttribute("role","tooltip"),g.classList.contains("tooltip")||g.classList.add("tooltip"),g.classList.contains(b.animation)||g.classList.add(b.animation),g.classList.contains(a)||g.classList.add(a),b.container.appendChild(g)}}()&&(C(i,g,b.placement,b.container),g.classList.contains("show")||g.classList.add("show"),b.animation?r(g,T):T())}}),20)},u.hide=function(){clearTimeout(p),p=setTimeout((function(){if(g&&g.classList.contains("show")){if(h.call(i,l),l.defaultPrevented)return;g.classList.remove("show"),b.animation?r(g,E):E()}}),b.delay)},u.toggle=function(){g?u.hide():u.show()},u.dispose=function(){k(),u.hide(),i.setAttribute("title",i.getAttribute("data-original-title")),i.removeAttribute("data-original-title"),delete i.Tooltip},(i=m(t)).Tooltip&&i.Tooltip.dispose();var N=i.getAttribute("data-animation"),P=i.getAttribute("data-placement"),H=i.getAttribute("data-delay"),M=i.getAttribute("data-container"),S=m(d.container),B=m(M),I=i.closest(".modal");o=f("show","tooltip"),s=f("shown","tooltip"),l=f("hide","tooltip"),c=f("hidden","tooltip");var D=i.closest(".fixed-top"),X=i.closest(".fixed-bottom");b.animation=d.animation&&"fade"!==d.animation?d.animation:N||"fade",b.placement=d.placement?d.placement:P||"top",b.template=d.template?d.template:null,b.delay=parseInt(d.delay||H,10)||200,b.container=S||B||D||X||I||document.body,a="bs-tooltip-"+b.placement,(n=y())&&(i.Tooltip||(i.setAttribute("data-original-title",n),i.removeAttribute("title"),k(1)),i.Tooltip=u)}var I={};function D(t){var e=t instanceof Element?t:document;Object.keys(I).forEach((function(t){var i,n;i=I[t][0],n=e.querySelectorAll(I[t][1]),Array.from(n).map((function(t){return new i(t)}))}))}I.Alert=[g,'[data-dismiss="alert"]'],I.Button=[p,'[data-toggle="buttons"]'],I.Carousel=[w,'[data-ride="carousel"]'],I.Collapse=[A,'[data-toggle="collapse"]'],I.Dropdown=[E,'[data-toggle="dropdown"]'],I.Modal=[k,'[data-toggle="modal"]'],I.Popover=[P,'[data-toggle="popover"],[data-tip="popover"]'],I.ScrollSpy=[H,'[data-spy="scroll"]'],I.Tab=[M,'[data-toggle="tab"]'],I.Toast=[S,'[data-dismiss="toast"]'],I.Tooltip=[B,'[data-toggle="tooltip"],[data-tip="tooltip"]'],document.body?D():document.addEventListener("DOMContentLoaded",(function t(){D(),document.removeEventListener("DOMContentLoaded",t,!1)}),!1);var X={Alert:g,Button:p,Carousel:w,Collapse:A,Dropdown:E,Modal:k,Popover:P,ScrollSpy:H,Tab:M,Toast:S,Tooltip:B,initCallback:D,removeDataAPI:function(t){var e=t instanceof Element?t:document;Object.keys(I).forEach((function(t){var i,n;i=t,n=e.querySelectorAll(I[t][1]),Array.from(n).map((function(t){return t[i].dispose()}))}))},componentsInit:I,Version:"4.1.0alpha1"};export{X as default}; diff --git a/dist/bootstrap-native-v4.js b/dist/bootstrap-native-v4.js index 73425278..981154e1 100644 --- a/dist/bootstrap-native-v4.js +++ b/dist/bootstrap-native-v4.js @@ -1,6 +1,6 @@ /*! - * Native JavaScript for Bootstrap v4.1.0 (https://thednp.github.io/bootstrap.native/) - * Copyright 2015-2021 © dnp_theme + * Native JavaScript for Bootstrap v4.1.0alpha1 (https://thednp.github.io/bootstrap.native/) + * Copyright 2015-2022 © dnp_theme * Licensed under MIT (https://github.com/thednp/bootstrap.native/blob/master/LICENSE) */ (function (global, factory) { @@ -10,40 +10,66 @@ })(this, (function () { 'use strict'; /** - * A global namespace for 'transitionend' string. - * @type {string} + * A global namespace for `document.head`. */ - var transitionEndEvent = 'webkitTransition' in document.head.style ? 'webkitTransitionEnd' : 'transitionend'; + var documentHead = document.head; /** - * A global namespace for CSS3 transition support. + * A global `boolean` for CSS3 transition support. * @type {boolean} */ - var supportTransition = 'webkitTransition' in document.head.style || 'transition' in document.head.style; + var supportTransition = 'webkitTransition' in documentHead.style || 'transition' in documentHead.style; + + /** + * A global namespace for 'transitionend' string. + * @type {string} + */ + var transitionEndEvent = 'webkitTransition' in documentHead.style ? 'webkitTransitionEnd' : 'transitionend'; /** * A global namespace for 'transitionDelay' string. * @type {string} */ - var transitionDelay = 'webkitTransition' in document.head.style ? 'webkitTransitionDelay' : 'transitionDelay'; + var transitionDelay = 'webkitTransition' in documentHead.style ? 'webkitTransitionDelay' : 'transitionDelay'; /** - * A global namespace for 'transitionProperty' string. + * A global namespace for: + * * `transitionProperty` string for Firefox, + * * `webkitTransition` for older Chrome / Safari browsers, + * * `transition` property for all other browsers. * @type {string} */ - var transitionProperty = 'webkitTransition' in document.head.style ? 'webkitTransitionProperty' : 'transitionProperty'; + var transitionProperty = 'webkitTransition' in documentHead.style ? 'webkitTransitionProperty' : 'transitionProperty'; + + /** + * Shortcut for `window.getComputedStyle(element).propertyName` + * static method. + * + * * If `element` parameter is not an `HTMLElement`, `getComputedStyle` + * throws a `ReferenceError`. + * + * @param {HTMLElement | Element} element target + * @param {string} property the css property + * @return {string} the css property value + */ + function getElementStyle(element, property) { + var computedStyle = getComputedStyle(element); + + // @ts-ignore -- must use camelcase strings, + // or non-camelcase strings with `getPropertyValue` + return property in computedStyle ? computedStyle[property] : ''; + } /** - * Utility to get the computed transitionDelay + * Utility to get the computed `transitionDelay` * from Element in miliseconds. * - * @param {Element} element target + * @param {HTMLElement | Element} element target * @return {number} the value in miliseconds */ function getElementTransitionDelay(element) { - var computedStyle = getComputedStyle(element); - var propertyValue = computedStyle[transitionProperty]; - var delayValue = computedStyle[transitionDelay]; + var propertyValue = getElementStyle(element, transitionProperty); + var delayValue = getElementStyle(element, transitionDelay); var delayScale = delayValue.includes('ms') ? 1 : 1000; var duration = supportTransition && propertyValue && propertyValue !== 'none' ? parseFloat(delayValue) * delayScale : 0; @@ -55,19 +81,18 @@ * A global namespace for 'transitionDuration' string. * @type {string} */ - var transitionDuration = 'webkitTransition' in document.head.style ? 'webkitTransitionDuration' : 'transitionDuration'; + var transitionDuration = 'webkitTransition' in documentHead.style ? 'webkitTransitionDuration' : 'transitionDuration'; /** - * Utility to get the computed transitionDuration + * Utility to get the computed `transitionDuration` * from Element in miliseconds. * - * @param {Element} element target + * @param {HTMLElement | Element} element target * @return {number} the value in miliseconds */ function getElementTransitionDuration(element) { - var computedStyle = getComputedStyle(element); - var propertyValue = computedStyle[transitionProperty]; - var durationValue = computedStyle[transitionDuration]; + var propertyValue = getElementStyle(element, transitionProperty); + var durationValue = getElementStyle(element, transitionDuration); var durationScale = durationValue.includes('ms') ? 1 : 1000; var duration = supportTransition && propertyValue && propertyValue !== 'none' ? parseFloat(durationValue) * durationScale : 0; @@ -79,8 +104,8 @@ * Utility to make sure callbacks are consistently * called when transition ends. * - * @param {Element} element target - * @param {function} handler `transitionend` callback + * @param {HTMLElement | Element} element target + * @param {EventListener} handler `transitionend` callback */ function emulateTransitionEnd(element, handler) { var called = 0; @@ -88,11 +113,10 @@ var duration = getElementTransitionDuration(element); var delay = getElementTransitionDelay(element); - if (duration) { + if (supportTransition && duration) { /** * Wrap the handler in on -> off callback * @param {Event} e Event object - * @callback */ var transitionEndWrapper = function (e) { if (e.target === element) { @@ -111,27 +135,45 @@ } /** - * Checks if an element is an `Element`. - * - * @param {any} element the target element - * @returns {boolean} the query result + * Returns the `document` or the `#document` element. + * @see https://github.com/floating-ui/floating-ui + * @param {(Node | HTMLElement | Element | globalThis)=} node + * @returns {Document} */ - function isElement(element) { - return element instanceof Element; + function getDocument(node) { + if (node instanceof HTMLElement) { return node.ownerDocument; } + if (node instanceof Window) { return node.document; } + return window.document; } /** - * Utility to check if target is typeof Element + * A global array of possible `ParentNode`. + */ + var parentNodes = [Document, Node, Element, HTMLElement]; + + /** + * A global array with `Element` | `HTMLElement`. + */ + var elementNodes = [Element, HTMLElement]; + + /** + * Utility to check if target is typeof `HTMLElement`, `Element`, `Node` * or find one that matches a selector. * - * @param {Element | string} selector the input selector or target element - * @param {Element=} parent optional Element to look into - * @return {Element?} the Element or `querySelector` result + * @param {HTMLElement | Element | string} selector the input selector or target element + * @param {(HTMLElement | Element | Node | Document)=} parent optional node to look into + * @return {(HTMLElement | Element)?} the `HTMLElement` or `querySelector` result */ - function queryElement(selector, parent) { - var lookUp = parent && isElement(parent) ? parent : document; - // @ts-ignore - return isElement(selector) ? selector : lookUp.querySelector(selector); + function querySelector(selector, parent) { + var selectorIsString = typeof selector === 'string'; + var lookUp = parent && parentNodes.some(function (x) { return parent instanceof x; }) + ? parent : getDocument(); + + if (!selectorIsString && [].concat( elementNodes ).some(function (x) { return selector instanceof x; })) { + return selector; + } + // @ts-ignore -- `ShadowRoot` is also a node + return selectorIsString ? lookUp.querySelector(selector) : null; } /** BSN v4 custom event */ @@ -188,7 +230,7 @@ // event handlers function clickHandler(e) { alert = e && e.target.closest('.alert'); - element = queryElement('[data-dismiss="alert"]', alert); + element = querySelector('[data-dismiss="alert"]', alert); if (element && alert && (element === e.target || element.contains(e.target))) { self.close(); } } function transitionEndHandler() { @@ -215,7 +257,7 @@ // INIT // initialization element - element = queryElement(elem); + element = querySelector(elem); // find the target alert alert = element.closest('.alert'); @@ -347,7 +389,7 @@ // init // initialization element - element = queryElement(elem); + element = querySelector(elem); // reset on re-init if (element.Button) { element.Button.dispose(); } @@ -369,7 +411,7 @@ // activate items on load Array.from(labels).forEach(function (btn) { - var hasChecked = queryElement('input:checked', btn); + var hasChecked = querySelector('input:checked', btn); if (!btn.classList.contains('active') && hasChecked) { btn.classList.add('active'); } @@ -386,70 +428,85 @@ var mouseHoverEvents = ('onmouseleave' in document) ? ['mouseenter', 'mouseleave'] : ['mouseover', 'mouseout']; /** - * A global namespace for 'addEventListener' string. - * @type {string} - */ - var addEventListener = 'addEventListener'; - - /** - * A global namespace for 'removeEventListener' string. - * @type {string} + * A global namespace for most scroll event listeners. + * @type {Partial} */ - var removeEventListener = 'removeEventListener'; + var passiveHandler = { passive: true }; /** - * A global namespace for passive events support. - * @type {boolean} + * Returns the bounding client rect of a target `HTMLElement`. + * + * @see https://github.com/floating-ui/floating-ui + * + * @param {HTMLElement | Element} element event.target + * @param {boolean=} includeScale when *true*, the target scale is also computed + * @returns {SHORTER.BoundingClientRect} the bounding client rect object */ - var supportPassive = (function () { - var result = false; - try { - var opts = Object.defineProperty({}, 'passive', { - get: function get() { - result = true; - return result; - }, - }); - document[addEventListener]('DOMContentLoaded', function wrap() { - document[removeEventListener]('DOMContentLoaded', wrap, opts); - }, opts); - } catch (e) { - throw Error('Passive events are not supported'); + function getBoundingClientRect(element, includeScale) { + var ref = element.getBoundingClientRect(); + var width = ref.width; + var height = ref.height; + var top = ref.top; + var right = ref.right; + var bottom = ref.bottom; + var left = ref.left; + var scaleX = 1; + var scaleY = 1; + + if (includeScale && element instanceof HTMLElement) { + var offsetWidth = element.offsetWidth; + var offsetHeight = element.offsetHeight; + scaleX = offsetWidth > 0 ? Math.round(width) / offsetWidth || 1 : 1; + scaleY = offsetHeight > 0 ? Math.round(height) / offsetHeight || 1 : 1; } - return result; - })(); - - // general event options + return { + width: width / scaleX, + height: height / scaleY, + top: top / scaleY, + right: right / scaleX, + bottom: bottom / scaleY, + left: left / scaleX, + x: left / scaleX, + y: top / scaleY, + }; + } /** - * A global namespace for most scroll event listeners. + * Returns the `document.documentElement` or the `` element. + * + * @param {(Node | HTMLElement | Element | globalThis)=} node + * @returns {HTMLElement | HTMLHtmlElement} */ - var passiveHandler = supportPassive ? { passive: true } : false; + function getDocumentElement(node) { + return getDocument(node).documentElement; + } /** - * Utility to determine if an `Element` + * Utility to determine if an `HTMLElement` * is partially visible in viewport. * - * @param {Element} element target - * @return {boolean} Boolean + * @param {HTMLElement | Element} element target + * @return {boolean} the query result */ - function isElementInScrollRange(element) { - var bcr = element.getBoundingClientRect(); - var viewportHeight = window.innerHeight || document.documentElement.clientHeight; - return bcr.top <= viewportHeight && bcr.bottom >= 0; // bottom && top - } + var isElementInScrollRange = function (element) { + var ref = getBoundingClientRect(element); + var top = ref.top; + var bottom = ref.bottom; + var ref$1 = getDocumentElement(element); + var clientHeight = ref$1.clientHeight; + // checks bottom && top + return top <= clientHeight && bottom >= 0; + }; /** - * Utility to force re-paint of an Element + * Utility to force re-paint of an `HTMLElement` target. * - * @param {Element | HTMLElement} element is the target - * @return {number} the Element.offsetHeight value + * @param {HTMLElement | Element} element is the target + * @return {number} the `Element.offsetHeight` value */ - function reflow(element) { - // @ts-ignore - return element.offsetHeight; - } + // @ts-ignore + var reflow = function (element) { return element.offsetHeight; }; /* Native JavaScript for Bootstrap 4 | Carousel ----------------------------------------------- */ @@ -739,7 +796,7 @@ // init // initialization element - element = queryElement(elem); + element = querySelector(elem); // reset on re-init if (element.Carousel) { element.Carousel.dispose(); } @@ -893,8 +950,8 @@ if (accordion) { (assign = accordion.getElementsByClassName('collapse show'), activeCollapse = assign[0]); - activeElement = activeCollapse && (queryElement(("[data-target=\"#" + (activeCollapse.id) + "\"]"), accordion) - || queryElement(("[href=\"#" + (activeCollapse.id) + "\"]"), accordion)); + activeElement = activeCollapse && (querySelector(("[data-target=\"#" + (activeCollapse.id) + "\"]"), accordion) + || querySelector(("[href=\"#" + (activeCollapse.id) + "\"]"), accordion)); } if (!collapse.isAnimating) { @@ -914,7 +971,7 @@ // init // initialization element - element = queryElement(elem); + element = querySelector(elem); // reset on re-init if (element.Collapse) { element.Collapse.dispose(); } @@ -929,7 +986,7 @@ hiddenCustomEvent = bootstrapCustomEvent('hidden', 'collapse'); // determine targets - collapse = queryElement(options.target || element.getAttribute('data-target') || element.getAttribute('href')); + collapse = querySelector(options.target || element.getAttribute('data-target') || element.getAttribute('href')); if (collapse !== null) { collapse.isAnimating = false; } var accordionSelector = options.parent || accordionData; @@ -950,11 +1007,9 @@ /** * Points the focus to a specific element. - * @param {Element} element target + * @param {HTMLElement} element target */ - function setFocus(element) { - element.focus(); - } + var setFocus = function (element) { return element.focus(); }; /* Native JavaScript for Bootstrap 4 | Dropdown ----------------------------------------------- */ @@ -982,7 +1037,7 @@ // preventDefault on empty anchor links function preventEmptyAnchor(anchor) { if ((anchor.hasAttribute('href') && anchor.href.slice(-1) === '#') || (anchor.parentNode - && anchor.hasAttribute('href') + && anchor.parentNode.hasAttribute('href') && anchor.parentNode.href.slice(-1) === '#')) { this.preventDefault(); } } // toggle dismissible events @@ -1097,14 +1152,14 @@ // init // initialization element - element = queryElement(elem); + element = querySelector(elem); // reset on re-init if (element.Dropdown) { element.Dropdown.dispose(); } // set targets parent = element.parentNode; - menu = queryElement('.dropdown-menu', parent); + menu = querySelector('.dropdown-menu', parent); Array.from(menu.children).forEach(function (child) { if (child.children.length && child.children[0].tagName === 'A') { @@ -1207,7 +1262,7 @@ } function createOverlay() { var newOverlay = document.createElement('div'); - overlay = queryElement('.modal-backdrop'); + overlay = querySelector('.modal-backdrop'); if (overlay === null) { newOverlay.setAttribute('class', ("modal-backdrop" + (ops.animation ? ' fade' : ''))); @@ -1217,7 +1272,7 @@ return overlay; } function removeOverlay() { - overlay = queryElement('.modal-backdrop'); + overlay = querySelector('.modal-backdrop'); if (overlay && !document.getElementsByClassName('modal show')[0]) { document.body.removeChild(overlay); overlay = null; } @@ -1258,7 +1313,7 @@ modal.style.display = ''; if (element) { setFocus(element); } - overlay = queryElement('.modal-backdrop'); + overlay = querySelector('.modal-backdrop'); // force can also be the transitionEvent object, we wanna make sure it's not if (force !== 1 && overlay && overlay.classList.contains('show') && !document.getElementsByClassName('modal show')[0]) { @@ -1360,7 +1415,7 @@ else { triggerHide(); } }; self.setContent = function (content) { - queryElement('.modal-content', modal).innerHTML = content; + querySelector('.modal-content', modal).innerHTML = content; }; self.update = function () { if (modal.classList.contains('show')) { @@ -1375,10 +1430,10 @@ // init // the modal (both JavaScript / DATA API init) / triggering button element (DATA API) - element = queryElement(elem); + element = querySelector(elem); // determine modal, triggering element - var checkModal = queryElement(element.getAttribute('data-target') || element.getAttribute('href')); + var checkModal = querySelector(element.getAttribute('data-target') || element.getAttribute('href')); modal = element.classList.contains('modal') ? element : checkModal; // set fixed items @@ -1422,7 +1477,7 @@ /** * A global namespace for mouse click events. - * @type {{down: string, up: string}} + * @type {Record} */ var mouseClickEvents = { down: 'mousedown', up: 'mouseup' }; @@ -1572,7 +1627,7 @@ // handlers function dismissibleHandler(e) { - if (popover !== null && e.target === queryElement('.close', popover)) { + if (popover !== null && e.target === querySelector('.close', popover)) { self.hide(); } } @@ -1628,8 +1683,8 @@ popover.className = popoverTemplate.firstChild.className; popover.innerHTML = popoverTemplate.firstChild.innerHTML; - var popoverHeader = queryElement('.popover-header', popover); - var popoverBody = queryElement('.popover-body', popover); + var popoverHeader = querySelector('.popover-header', popover); + var popoverBody = querySelector('.popover-body', popover); // fill the template with content from data attributes if (titleString && popoverHeader) { popoverHeader.innerHTML = titleString.trim(); } @@ -1736,7 +1791,7 @@ // INIT // initialization element - element = queryElement(elem); + element = querySelector(elem); // reset on re-init if (element.Popover) { element.Popover.dispose(); } @@ -1760,8 +1815,8 @@ hiddenCustomEvent = bootstrapCustomEvent('hidden', 'popover'); // check container - var containerElement = queryElement(options.container); - var containerDataElement = queryElement(containerData); + var containerElement = querySelector(options.container); + var containerDataElement = querySelector(containerData); // maybe the element is inside a modal var modal = element.closest('.modal'); @@ -1845,7 +1900,7 @@ Array.from(links).forEach(function (link) { href = link.getAttribute('href'); - targetItem = href && href.charAt(0) === '#' && href.slice(-1) !== '#' && queryElement(href); + targetItem = href && href.charAt(0) === '#' && href.slice(-1) !== '#' && querySelector(href); if (targetItem) { vars.items.push(link); @@ -1939,7 +1994,7 @@ // init // initialization element, the element we spy on - element = queryElement(elem); + element = querySelector(elem); // reset on re-init if (element.ScrollSpy) { element.ScrollSpy.dispose(); } @@ -1950,7 +2005,7 @@ var offsetData = element.getAttribute('data-offset'); // targets - spyTarget = queryElement(options.target || targetData); + spyTarget = querySelector(options.target || targetData); // determine which is the real scrollTarget scrollTarget = element.clientHeight < element.scrollHeight ? element : window; @@ -2085,7 +2140,7 @@ } return activeTab; } - function getActiveContent() { return queryElement(getActiveTab().getAttribute('href')); } + function getActiveContent() { return querySelector(getActiveTab().getAttribute('href')); } // handler function clickHandler(e) { e.preventDefault(); @@ -2098,7 +2153,7 @@ next = next || element; if (!next.classList.contains('active')) { - nextContent = queryElement(next.getAttribute('href')); // this is the actual object, the next tab content to activate + nextContent = querySelector(next.getAttribute('href')); // this is the actual object, the next tab content to activate activeTab = getActiveTab(); activeContent = getActiveContent(); @@ -2131,7 +2186,7 @@ // INIT // initialization element - element = queryElement(elem); + element = querySelector(elem); // reset on re-init if (element.Tab) { element.Tab.dispose(); } @@ -2140,7 +2195,7 @@ var heightData = element.getAttribute('data-height'); // event targets tabs = element.closest('.nav'); - dropdown = tabs && queryElement('.dropdown-toggle', tabs); + dropdown = tabs && querySelector('.dropdown-toggle', tabs); // instance options var animateHeight = !(!supportTransition || (options.height === false || heightData === 'false')); @@ -2239,7 +2294,7 @@ // init // initialization element - element = queryElement(elem); + element = querySelector(elem); // reset on re-init if (element.Toast) { element.Toast.dispose(); } @@ -2323,7 +2378,7 @@ tooltip.className = tooltipMarkup.firstChild.className; tooltip.innerHTML = tooltipMarkup.firstChild.innerHTML; - queryElement('.tooltip-inner', tooltip).innerHTML = titleString.trim(); + querySelector('.tooltip-inner', tooltip).innerHTML = titleString.trim(); } else { // tooltip arrow var tooltipArrow = document.createElement('div'); @@ -2424,7 +2479,7 @@ // init // initialization element - element = queryElement(elem); + element = querySelector(elem); // reset on re-init if (element.Tooltip) { element.Tooltip.dispose(); } @@ -2436,8 +2491,8 @@ var containerData = element.getAttribute('data-container'); // check container - var containerElement = queryElement(options.container); - var containerDataElement = queryElement(containerData); + var containerElement = querySelector(options.container); + var containerDataElement = querySelector(containerData); // maybe the element is inside a modal var modal = element.closest('.modal'); @@ -2530,7 +2585,7 @@ }); } - var version = "4.1.0"; + var version = "4.1.0alpha1"; var Version = version; diff --git a/dist/bootstrap-native-v4.min.js b/dist/bootstrap-native-v4.min.js index 6d69cef7..68967c36 100644 --- a/dist/bootstrap-native-v4.min.js +++ b/dist/bootstrap-native-v4.min.js @@ -1,2 +1,2 @@ -// Native JavaScript for Bootstrap v4.1.0 | 2021 © dnp_theme | MIT-License -!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).BSN=e()}(this,(function(){"use strict";var t="webkitTransition"in document.head.style?"webkitTransitionEnd":"transitionend",e="webkitTransition"in document.head.style||"transition"in document.head.style,i="webkitTransition"in document.head.style?"webkitTransitionDelay":"transitionDelay",n="webkitTransition"in document.head.style?"webkitTransitionProperty":"transitionProperty";var a="webkitTransition"in document.head.style?"webkitTransitionDuration":"transitionDuration";function o(t){var i=getComputedStyle(t),o=i[n],s=i[a],l=s.includes("ms")?1:1e3,r=e&&o&&"none"!==o?parseFloat(s)*l:0;return Number.isNaN(r)?0:r}function s(a,s){var l=0,r=new Event(t),c=o(a),d=function(t){var a=getComputedStyle(t),o=a[n],s=a[i],l=s.includes("ms")?1:1e3,r=e&&o&&"none"!==o?parseFloat(s)*l:0;return Number.isNaN(r)?0:r}(a);if(c){var u=function(e){e.target===a&&(s.apply(a,[e]),a.removeEventListener(t,u),l=1)};a.addEventListener(t,u),setTimeout((function(){l||a.dispatchEvent(r)}),c+d+17)}else s.apply(a,[r])}function l(t){return t instanceof Element}function r(t,e){var i=e&&l(e)?e:document;return l(t)?t:i.querySelector(t)}function c(t,e,i){var n=new CustomEvent(t+".bs."+e,{cancelable:!0});return void 0!==i&&Object.keys(i).forEach((function(t){Object.defineProperty(n,t,{value:i[t]})})),n}function d(t){this&&this.dispatchEvent(t)}function u(t){var e,i,n=this,a=c("close","alert"),o=c("closed","alert");function l(t){e[t?"addEventListener":"removeEventListener"]("click",u,!1)}function u(t){i=t&&t.target.closest(".alert"),(e=r('[data-dismiss="alert"]',i))&&i&&(e===t.target||e.contains(t.target))&&n.close()}function m(){l(),i.parentNode.removeChild(i),d.call(i,o)}n.close=function(){if(i&&e&&i.classList.contains("show")){if(d.call(i,a),a.defaultPrevented)return;n.dispose(),i.classList.remove("show"),i.classList.contains("fade")?s(i,m):m()}},n.dispose=function(){l(),delete e.Alert},e=r(t),i=e.closest(".alert"),e.Alert&&e.Alert.dispose(),e.Alert||l(1),n.element=e,e.Alert=n}function m(t){var e,i,n=c("change","button");function a(t){var a=t.target,o=a.closest("LABEL"),s=null;"LABEL"===a.tagName?s=a:o&&(s=o);var l=s&&s.getElementsByTagName("INPUT")[0];if(l){if(d.call(l,n),d.call(e,n),"checkbox"===l.type){if(n.defaultPrevented)return;l.checked?(s.classList.remove("active"),l.getAttribute("checked"),l.removeAttribute("checked"),l.checked=!1):(s.classList.add("active"),l.getAttribute("checked"),l.setAttribute("checked","checked"),l.checked=!0),e.toggled||(e.toggled=!0)}if("radio"===l.type&&!e.toggled){if(n.defaultPrevented)return;(!l.checked||0===t.screenX&&0===t.screenY)&&(s.classList.add("active"),s.classList.add("focus"),l.setAttribute("checked","checked"),l.checked=!0,e.toggled=!0,Array.from(i).forEach((function(t){var e=t.getElementsByTagName("INPUT")[0];t!==s&&t.classList.contains("active")&&(d.call(e,n),t.classList.remove("active"),e.removeAttribute("checked"),e.checked=!1)})))}setTimeout((function(){e.toggled=!1}),50)}}function o(t){32===(t.which||t.keyCode)&&t.target===document.activeElement&&a(t)}function s(t){32===(t.which||t.keyCode)&&t.preventDefault()}function l(t){if("INPUT"===t.target.tagName){var e="focusin"===t.type?"add":"remove";t.target.closest(".btn").classList[e]("focus")}}function u(t){var i=t?"addEventListener":"removeEventListener";e[i]("click",a,!1),e[i]("keyup",o,!1),e[i]("keydown",s,!1),e[i]("focusin",l,!1),e[i]("focusout",l,!1)}this.dispose=function(){u(),delete e.Button},(e=r(t)).Button&&e.Button.dispose(),(i=e.getElementsByClassName("btn")).length&&(e.Button||u(1),e.toggled=!1,e.Button=this,Array.from(i).forEach((function(t){var e=r("input:checked",t);!t.classList.contains("active")&&e&&t.classList.add("active"),t.classList.contains("active")&&!e&&t.classList.remove("active")})))}var f="onmouseleave"in document?["mouseenter","mouseleave"]:["mouseover","mouseout"],h=!!function(){var t=!1;try{var e=Object.defineProperty({},"passive",{get:function(){return t=!0}});document.addEventListener("DOMContentLoaded",(function t(){document.removeEventListener("DOMContentLoaded",t,e)}),e)}catch(t){throw Error("Passive events are not supported")}return t}()&&{passive:!0};function g(t){return t.offsetHeight}function p(t,e){var i,n,a,l,u,m,p,v,L,b,y,w,A,T=e||{},E=this;function k(){!1===m.interval||l.classList.contains("paused")||(l.classList.add("paused"),u.isSliding||(clearInterval(u.timer),u.timer=null))}function x(){!1!==m.interval&&l.classList.contains("paused")&&(l.classList.remove("paused"),u.isSliding||(clearInterval(u.timer),u.timer=null,E.cycle()))}function C(t){if(t.preventDefault(),!u.isSliding){var e=t.target;e&&!e.classList.contains("active")&&e.getAttribute("data-slide-to")&&(u.index=+e.getAttribute("data-slide-to"),E.slideTo(u.index))}}function N(t){if(t.preventDefault(),!u.isSliding){var e=t.currentTarget||t.srcElement;e===y?u.index+=1:e===b&&(u.index-=1),E.slideTo(u.index)}}function P(t){var e=t.which;if(!u.isSliding){switch(e){case 39:u.index+=1;break;case 37:u.index-=1;break;default:return}E.slideTo(u.index)}}function H(t){var e=t?"addEventListener":"removeEventListener";m.pause&&m.interval&&(l[e](f[0],k,!1),l[e](f[1],x,!1),l[e]("touchstart",k,h),l[e]("touchend",x,h)),m.touch&&L.length>1&&l[e]("touchstart",B,h),y&&y[e]("click",N,!1),b&&b[e]("click",N,!1),w&&w[e]("click",C,!1),m.keyboard&&window[e]("keydown",P,!1)}function S(t){var e=t?"addEventListener":"removeEventListener";l[e]("touchmove",M,h),l[e]("touchend",I,h)}function B(t){u.isTouch||(u.touchPosition.startX=t.changedTouches[0].pageX,l.contains(t.target)&&(u.isTouch=!0,S(1)))}function M(t){u.isTouch?(u.touchPosition.currentX=t.changedTouches[0].pageX,"touchmove"===t.type&&t.changedTouches.length>1&&t.preventDefault()):t.preventDefault()}function I(t){if(u.isTouch&&!u.isSliding&&(u.touchPosition.endX=u.touchPosition.currentX||t.changedTouches[0].pageX,u.isTouch)){if((!l.contains(t.target)||!l.contains(t.relatedTarget))&&Math.abs(u.touchPosition.startX-u.touchPosition.endX)<75)return;u.touchPosition.currentXu.touchPosition.startX&&(u.index-=1),u.isTouch=!1,E.slideTo(u.index),S()}}function D(t){Array.from(A).forEach((function(t){return t.classList.remove("active")})),A[t]&&A[t].classList.add("active")}function X(t){if(u.touchPosition){var e=u.index,i=t&&t.target!==L[e]?1e3*t.elapsedTime+100:20,n=E.getActiveIndex(),a="left"===u.direction?"next":"prev";u.isSliding&&setTimeout((function(){u.touchPosition&&(u.isSliding=!1,L[e].classList.add("active"),L[n].classList.remove("active"),L[e].classList.remove("carousel-item-"+a),L[e].classList.remove("carousel-item-"+u.direction),L[n].classList.remove("carousel-item-"+u.direction),d.call(l,v),document.hidden||!m.interval||l.classList.contains("paused")||E.cycle())}),i)}}if(E.cycle=function(){u.timer&&(clearInterval(u.timer),u.timer=null),u.timer=setInterval((function(){var t=u.index||E.getActiveIndex();(function(t){var e=t.getBoundingClientRect(),i=window.innerHeight||document.documentElement.clientHeight;return e.top<=i&&e.bottom>=0})(l)&&(t+=1,E.slideTo(t))}),m.interval)},E.slideTo=function(t){if(!u.isSliding){var e=E.getActiveIndex(),i=t;if(e!==i){ei||e===L.length-1&&0===i)&&(u.direction="right"),i<0?i=L.length-1:i>=L.length&&(i=0);var n="left"===u.direction?"next":"prev",a={relatedTarget:L[i],direction:u.direction,from:e,to:i};p=c("slide","carousel",a),v=c("slid","carousel",a),d.call(l,p),p.defaultPrevented||(u.index=i,u.isSliding=!0,clearInterval(u.timer),u.timer=null,D(i),o(L[i])&&l.classList.contains("slide")?(L[i].classList.add("carousel-item-"+n),g(L[i]),L[i].classList.add("carousel-item-"+u.direction),L[e].classList.add("carousel-item-"+u.direction),s(L[i],X)):(L[i].classList.add("active"),g(L[i]),L[e].classList.remove("active"),setTimeout((function(){u.isSliding=!1,m.interval&&l&&!l.classList.contains("paused")&&E.cycle(),d.call(l,v)}),100)))}}},E.getActiveIndex=function(){return Array.from(L).indexOf(l.getElementsByClassName("carousel-item active")[0])||0},E.dispose=function(){var t=["left","right","prev","next"];Array.from(L).forEach((function(e,i){e.classList.contains("active")&&D(i),t.forEach((function(t){return e.classList.remove("carousel-item-"+t)}))})),clearInterval(u.timer),H(),u={},m={},delete l.Carousel},(l=r(t)).Carousel&&l.Carousel.dispose(),L=l.getElementsByClassName("carousel-item"),i=l.getElementsByClassName("carousel-control-prev"),b=i[0],n=l.getElementsByClassName("carousel-control-next"),y=n[0],a=l.getElementsByClassName("carousel-indicators"),w=a[0],A=w&&w.getElementsByTagName("LI")||[],!(L.length<2)){var O=l.getAttribute("data-interval"),R="false"===O?0:+O,W="false"===l.getAttribute("data-touch")?0:1,j="hover"===l.getAttribute("data-pause")||!1,z="true"===l.getAttribute("data-keyboard")||!1,U=T.interval,q=T.touch;(m={}).keyboard=!0===T.keyboard||z,m.pause=!("hover"!==T.pause&&!j)&&"hover",m.touch=q||W,m.interval=5e3,"number"==typeof U?m.interval=U:!1===U||0===R||!1===R?m.interval=0:Number.isNaN(R)||(m.interval=R),E.getActiveIndex()<0&&(L.length&&L[0].classList.add("active"),A.length&&D(0)),(u={}).direction="left",u.index=0,u.timer=null,u.isSliding=!1,u.isTouch=!1,u.touchPosition={startX:0,currentX:0,endX:0},H(1),m.interval&&E.cycle(),l.Carousel=E}}function v(t,e){var i,n,a,o,l,u,m,f=e||{},h=this,p=null,v=null;function L(t,e){d.call(t,u),u.defaultPrevented||(t.isAnimating=!0,t.style.height=t.scrollHeight+"px",t.classList.remove("collapse"),t.classList.remove("show"),t.classList.add("collapsing"),g(t),t.style.height="0px",s(t,(function(){t.isAnimating=!1,t.setAttribute("aria-expanded","false"),e.setAttribute("aria-expanded","false"),t.classList.remove("collapsing"),t.classList.add("collapse"),t.style.height="",d.call(t,m)})))}h.toggle=function(t){(t&&"A"===t.target.tagName||"A"===i.tagName)&&t.preventDefault(),(i.contains(t.target)||t.target===i)&&(v.classList.contains("show")?h.hide():h.show())},h.hide=function(){v.isAnimating||(L(v,i),i.classList.add("collapsed"))},h.show=function(){var t,e,c;p&&(t=p.getElementsByClassName("collapse show"),n=t[0],a=n&&(r('[data-target="#'+n.id+'"]',p)||r('[href="#'+n.id+'"]',p))),v.isAnimating||(a&&n!==v&&(L(n,a),a.classList.add("collapsed")),e=v,c=i,d.call(e,o),o.defaultPrevented||(e.isAnimating=!0,e.classList.add("collapsing"),e.classList.remove("collapse"),e.style.height=e.scrollHeight+"px",s(e,(function(){e.isAnimating=!1,e.setAttribute("aria-expanded","true"),c.setAttribute("aria-expanded","true"),e.classList.remove("collapsing"),e.classList.add("collapse"),e.classList.add("show"),e.style.height="",d.call(e,l)}))),i.classList.remove("collapsed"))},h.dispose=function(){i.removeEventListener("click",h.toggle,!1),delete i.Collapse},(i=r(t)).Collapse&&i.Collapse.dispose();var b=i.getAttribute("data-parent");o=c("show","collapse"),l=c("shown","collapse"),u=c("hide","collapse"),m=c("hidden","collapse"),null!==(v=r(f.target||i.getAttribute("data-target")||i.getAttribute("href")))&&(v.isAnimating=!1);var y=f.parent||b;p=y?i.closest(y):null,i.Collapse||i.addEventListener("click",h.toggle,!1),i.Collapse=h}function L(t){t.focus()}function b(t,e){var i,n,a,o,s,l,u,m,f=this,h=null,g=[];function p(t){(t.hasAttribute("href")&&"#"===t.href.slice(-1)||t.parentNode&&t.hasAttribute("href")&&"#"===t.parentNode.href.slice(-1))&&this.preventDefault()}function v(){var t=i.open?"addEventListener":"removeEventListener";document[t]("click",b,!1),document[t]("keydown",w,!1),document[t]("keyup",A,!1),document[t]("focus",b,!1)}function b(t){var e=t.target;if(e.getAttribute){var n=e&&e.getAttribute("data-toggle")||e.parentNode&&e.parentNode.getAttribute&&e.parentNode.getAttribute("data-toggle");("focus"!==t.type||e!==i&&e!==u&&!u.contains(e))&&(e!==u&&!u.contains(e)||!m&&!n)&&(h=e===i||i.contains(e)?i:null,f.hide(),p.call(t,e))}}function y(t){h=i,f.show(),p.call(t,t.target)}function w(t){var e=t.which||t.keyCode;38!==e&&40!==e||t.preventDefault()}function A(t){var e=t.which||t.keyCode,n=document.activeElement,a=n===i,o=u.contains(n),s=n.parentNode===u||n.parentNode.parentNode===u,l=g.indexOf(n);s&&(a?l=0:38===e?l=l>1?l-1:0:40===e&&(l=l=d,w=m.top+c/2+g/2>=u,T=m.top-c<0,E=m.left-r<0,k=m.top+c+g>=u,x=m.left+r+h>=d,C=i;C="right"===(C="left"===(C="bottom"===(C="top"===(C=("left"===C||"right"===C)&&E&&x?"top":C)&&T?"bottom":C)&&k?"top":C)&&E?"right":C)&&x?"left":C,-1===e.className.indexOf(C)&&(e.className=e.className.replace(/\b(top|bottom|left|right)+/,C));var N=v.offsetWidth,P=v.offsetHeight;"left"===C||"right"===C?(o="left"===C?m.left+f.x-r-(p?N:0):m.left+f.x+h,L?(a=m.top+f.y,s=g/2-N):w?(a=m.top+f.y-c+g,s=c-g/2-N):(a=m.top+f.y-c/2+g/2,s=c/2-(p?.9*P:P/2))):"top"!==C&&"bottom"!==C||(a="top"===C?m.top+f.y-c-(p?P:0):m.top+f.y+g,b?(o=0,l=m.left+h/2-N):y?(o=d-1.01*r,l=r-(d-m.left)+h/2-N/2):(o=m.left+f.x-r/2+h/2,l=r/2-(p?N:N/2))),e.style.top=a+"px",e.style.left=o+"px",s&&(v.style.top=s+"px"),l&&(v.style.left=l+"px")}function E(t,e){var i,n,a,o,l,u,m,g,p,v=e||{},L=this,b=null,y=0,A=/(iPhone|iPod|iPad)/.test(navigator.userAgent),E={};function k(t){null!==b&&t.target===r(".close",b)&&L.hide()}function x(t){return v[t]||i.dataset[t]||null}function C(){return x("title")}function N(){return x("content")}function P(){null===b&&i.focus()}function H(t){var e=t?"addEventListener":"removeEventListener";"hover"===E.trigger?(i[e](w,L.show),i[e](f[0],L.show),E.dismissible||i[e](f[1],L.hide)):"click"===E.trigger?i[e](E.trigger,L.toggle):"focus"===E.trigger&&(A&&i[e]("click",P,!1),i[e](E.trigger,L.toggle))}function S(t){b&&b.contains(t.target)||t.target===i||i.contains(t.target)||L.hide()}function B(t){var e=t?"addEventListener":"removeEventListener";E.dismissible?document[e]("click",k,!1):("focus"===E.trigger&&i[e]("blur",L.hide),"hover"===E.trigger&&document[e]("touchstart",S,h)),window[e]("resize",L.hide,h)}function M(){B(1),d.call(i,m)}function I(){B(),E.container.removeChild(b),y=null,b=null,d.call(i,p)}L.toggle=function(){null===b?L.show():L.hide()},L.show=function(){clearTimeout(y),y=setTimeout((function(){if(null===b){if(d.call(i,u),u.defaultPrevented)return;!function(){n=C(),a=(a=N())?a.trim():null,b=document.createElement("div");var t=document.createElement("div");if(t.classList.add("arrow"),b.appendChild(t),null!==a&&null===E.template){if(b.setAttribute("role","tooltip"),null!==n){var e=document.createElement("h3");e.classList.add("popover-header"),e.innerHTML=E.dismissible?n+l:n,b.appendChild(e)}var i=document.createElement("div");i.classList.add("popover-body"),i.innerHTML=E.dismissible&&null===n?a+l:a,b.appendChild(i)}else{var s=document.createElement("div");s.innerHTML=E.template.trim(),b.className=s.firstChild.className,b.innerHTML=s.firstChild.innerHTML;var c=r(".popover-header",b),d=r(".popover-body",b);n&&c&&(c.innerHTML=n.trim()),a&&d&&(d.innerHTML=a.trim())}E.container.appendChild(b),b.style.display="block",b.classList.contains("popover")||b.classList.add("popover"),b.classList.contains(E.animation)||b.classList.add(E.animation),b.classList.contains(o)||b.classList.add(o)}(),T(i,b,E.placement,E.container),b.classList.contains("show")||b.classList.add("show"),E.animation?s(b,M):M()}}),20)},L.hide=function(){clearTimeout(y),y=setTimeout((function(){if(b&&null!==b&&b.classList.contains("show")){if(d.call(i,g),g.defaultPrevented)return;b.classList.remove("show"),E.animation?s(b,I):I()}}),E.delay)},L.dispose=function(){L.hide(),H(),delete i.Popover},(i=r(t)).Popover&&i.Popover.dispose();var D=i.getAttribute("data-trigger"),X=i.getAttribute("data-animation"),O=i.getAttribute("data-placement"),R=i.getAttribute("data-dismissible"),W=i.getAttribute("data-delay"),j=i.getAttribute("data-container");l='',u=c("show","popover"),m=c("shown","popover"),g=c("hide","popover"),p=c("hidden","popover");var z=r(v.container),U=r(j),q=i.closest(".modal"),F=i.closest(".fixed-top"),Y=i.closest(".fixed-bottom");E.template=v.template?v.template:null,E.trigger=v.trigger?v.trigger:D||"hover",E.animation=v.animation&&"fade"!==v.animation?v.animation:X||"fade",E.placement=v.placement?v.placement:O||"top",E.delay=parseInt(v.delay||W,10)||200,E.dismissible=!(!v.dismissible&&"true"!==R),E.container=z||U||F||Y||q||document.body,o="bs-popover-"+E.placement,n=C(),((a=N())||E.template)&&(i.Popover||H(1),i.Popover=L)}function k(t,e){var i,n,a,o,s,l=e||{},u=this,m={};function f(){var t,e,s;(a=o.getElementsByTagName("A"),n.scrollTop=n.isWindow?A().y:i.scrollTop,n.length!==a.length||p()!==n.scrollHeight)&&(n.items=[],n.offsets=[],n.scrollHeight=p(),n.maxScroll=n.scrollHeight-(n.isWindow?window.innerHeight:i.getBoundingClientRect().height),Array.from(a).forEach((function(i){t=i.getAttribute("href"),(e=t&&"#"===t.charAt(0)&&"#"!==t.slice(-1)&&r(t))&&(n.items.push(i),s=e.getBoundingClientRect(),n.offsets.push((n.isWindow?s.top+n.scrollTop:e.offsetTop)-m.offset))})),n.length=n.items.length)}function g(t){var e=t?"addEventListener":"removeEventListener";s[e]("scroll",u.refresh,h),window[e]("resize",u.refresh,h)}function p(){return s.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)}function v(){Array.from(a).map((function(t){return t.classList.contains("active")&&t.classList.remove("active")}))}function L(t){var e,a=t;v(),n.activeItem=a,a.classList.add("active");for(var o=[];a.parentNode!==document.body;)((e=(a=a.parentNode).classList).contains("dropdown-menu")||e.contains("nav"))&&o.push(a);o.forEach((function(t){var e=t.previousElementSibling;e&&!e.classList.contains("active")&&e.classList.add("active")})),d.call(i,c("activate","scrollspy",{relatedTarget:n.activeItem}))}u.refresh=function(){if(f(),n.scrollTop>=n.maxScroll){var t=n.items[n.length-1];n.activeItem!==t&&L(t)}else{if(n.activeItem&&n.scrollTop0)return n.activeItem=null,void v();for(var e=n.length;e>-1;)n.activeItem!==n.items[e]&&n.scrollTop>=n.offsets[e]&&(void 0===n.offsets[e+1]||n.scrollTop1&&(p=t[t.length-1]):p=t[0],p}function P(){return r(N().getAttribute("href"))}function H(t){t.preventDefault(),h=t.currentTarget,a.isAnimating||T.show()}T.show=function(){if(!(h=h||n).classList.contains("active")){if(L=r(h.getAttribute("href")),p=N(),v=P(),m=c("hide","tab",{relatedTarget:h}),d.call(p,m),m.defaultPrevented)return;a.isAnimating=!0,p.classList.remove("active"),p.setAttribute("aria-selected","false"),h.classList.add("active"),h.setAttribute("aria-selected","true"),o&&(n.parentNode.classList.contains("dropdown-menu")?o.classList.contains("active")||o.classList.add("active"):o.classList.contains("active")&&o.classList.remove("active")),v.classList.contains("fade")?(v.classList.remove("show"),s(v,C)):C()}},T.dispose=function(){n.removeEventListener("click",H,!1),delete n.Tab},(n=r(t)).Tab&&n.Tab.dispose();var S=n.getAttribute("data-height");a=n.closest(".nav"),o=a&&r(".dropdown-toggle",a);var B=!(!e||!1===A.height||"false"===S);a.isAnimating=!1,n.Tab||n.addEventListener("click",H,!1),B&&(E=P().parentNode),n.Tab=T}function C(t,e){var i,n,a,o,l,u,m=e||{},f=this,h=0,p={};function v(){n.classList.remove("showing"),n.classList.add("show"),d.call(n,l),p.autohide&&f.hide()}function L(){n.classList.add("hide"),d.call(n,u)}function b(){n.classList.remove("show"),p.animation?s(n,L):L()}function y(){clearTimeout(h),i.removeEventListener("click",f.hide,!1),delete i.Toast}f.show=function(){if(n&&!n.classList.contains("show")){if(d.call(n,a),a.defaultPrevented)return;p.animation&&n.classList.add("fade"),n.classList.remove("hide"),g(n),n.classList.add("showing"),p.animation?s(n,v):v()}},f.hide=function(t){if(n&&n.classList.contains("show")){if(d.call(n,o),o.defaultPrevented)return;t?b():h=setTimeout(b,p.delay)}},f.dispose=function(){p.animation?s(n,y):y()},(i=r(t)).Toast&&i.Toast.dispose(),n=i.closest(".toast");var w=i.getAttribute("data-animation"),A=i.getAttribute("data-autohide"),T=i.getAttribute("data-delay");a=c("show","toast"),o=c("hide","toast"),l=c("shown","toast"),u=c("hidden","toast"),p.animation=!1===m.animation||"false"===w?0:1,p.autohide=!1===m.autohide||"false"===A?0:1,p.delay=parseInt(m.delay||T,10)||500,i.Toast||i.addEventListener("click",f.hide,!1),i.Toast=f}function N(t,e){var i,n,a,o,l,u,m,g=e||{},p=this,v=null,L=0,b={};function y(){return i.getAttribute("title")||i.getAttribute("data-title")||i.getAttribute("data-original-title")}function A(t){v&&v.contains(t.target)||t.target===i||i.contains(t.target)||p.hide()}function E(t){var e=t?"addEventListener":"removeEventListener";document[e]("touchstart",A,h),window[e]("resize",p.hide,h)}function k(){E(1),d.call(i,l)}function x(){E(),b.container.removeChild(v),v=null,L=null,d.call(i,m)}function C(t){var e=t?"addEventListener":"removeEventListener";i[e](w,p.show,!1),i[e](f[0],p.show,!1),i[e](f[1],p.hide,!1)}p.show=function(){clearTimeout(L),L=setTimeout((function(){if(null===v){if(d.call(i,o),o.defaultPrevented)return;!1!==function(){if(n=y()){if(v=document.createElement("div"),b.template){var t=document.createElement("div");t.innerHTML=b.template.trim(),v.className=t.firstChild.className,v.innerHTML=t.firstChild.innerHTML,r(".tooltip-inner",v).innerHTML=n.trim()}else{var e=document.createElement("div");e.classList.add("arrow"),v.appendChild(e);var i=document.createElement("div");i.classList.add("tooltip-inner"),v.appendChild(i),i.innerHTML=n}v.style.left="0",v.style.top="0",v.setAttribute("role","tooltip"),v.classList.contains("tooltip")||v.classList.add("tooltip"),v.classList.contains(b.animation)||v.classList.add(b.animation),v.classList.contains(a)||v.classList.add(a),b.container.appendChild(v)}}()&&(T(i,v,b.placement,b.container),v.classList.contains("show")||v.classList.add("show"),b.animation?s(v,k):k())}}),20)},p.hide=function(){clearTimeout(L),L=setTimeout((function(){if(v&&v.classList.contains("show")){if(d.call(i,u),u.defaultPrevented)return;v.classList.remove("show"),b.animation?s(v,x):x()}}),b.delay)},p.toggle=function(){v?p.hide():p.show()},p.dispose=function(){C(),p.hide(),i.setAttribute("title",i.getAttribute("data-original-title")),i.removeAttribute("data-original-title"),delete i.Tooltip},(i=r(t)).Tooltip&&i.Tooltip.dispose();var N=i.getAttribute("data-animation"),P=i.getAttribute("data-placement"),H=i.getAttribute("data-delay"),S=i.getAttribute("data-container"),B=r(g.container),M=r(S),I=i.closest(".modal");o=c("show","tooltip"),l=c("shown","tooltip"),u=c("hide","tooltip"),m=c("hidden","tooltip");var D=i.closest(".fixed-top"),X=i.closest(".fixed-bottom");b.animation=g.animation&&"fade"!==g.animation?g.animation:N||"fade",b.placement=g.placement?g.placement:P||"top",b.template=g.template?g.template:null,b.delay=parseInt(g.delay||H,10)||200,b.container=B||M||D||X||I||document.body,a="bs-tooltip-"+b.placement,(n=y())&&(i.Tooltip||(i.setAttribute("data-original-title",n),i.removeAttribute("title"),C(1)),i.Tooltip=p)}var P={};function H(t){var e=t instanceof Element?t:document;Object.keys(P).forEach((function(t){var i,n;i=P[t][0],n=e.querySelectorAll(P[t][1]),Array.from(n).map((function(t){return new i(t)}))}))}P.Alert=[u,'[data-dismiss="alert"]'],P.Button=[m,'[data-toggle="buttons"]'],P.Carousel=[p,'[data-ride="carousel"]'],P.Collapse=[v,'[data-toggle="collapse"]'],P.Dropdown=[b,'[data-toggle="dropdown"]'],P.Modal=[y,'[data-toggle="modal"]'],P.Popover=[E,'[data-toggle="popover"],[data-tip="popover"]'],P.ScrollSpy=[k,'[data-spy="scroll"]'],P.Tab=[x,'[data-toggle="tab"]'],P.Toast=[C,'[data-dismiss="toast"]'],P.Tooltip=[N,'[data-toggle="tooltip"],[data-tip="tooltip"]'],document.body?H():document.addEventListener("DOMContentLoaded",(function t(){H(),document.removeEventListener("DOMContentLoaded",t,!1)}),!1);return{Alert:u,Button:m,Carousel:p,Collapse:v,Dropdown:b,Modal:y,Popover:E,ScrollSpy:k,Tab:x,Toast:C,Tooltip:N,initCallback:H,removeDataAPI:function(t){var e=t instanceof Element?t:document;Object.keys(P).forEach((function(t){var i,n;i=t,n=e.querySelectorAll(P[t][1]),Array.from(n).map((function(t){return t[i].dispose()}))}))},componentsInit:P,Version:"4.1.0"}})); +// Native JavaScript for Bootstrap v4.1.0alpha1 | 2022 © dnp_theme | MIT-License +!function(t,e){"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).BSN=e()}(this,(function(){"use strict";var t=document.head,e="webkitTransition"in t.style||"transition"in t.style,i="webkitTransition"in t.style?"webkitTransitionEnd":"transitionend",n="webkitTransition"in t.style?"webkitTransitionDelay":"transitionDelay",a="webkitTransition"in t.style?"webkitTransitionProperty":"transitionProperty";function o(t,e){var i=getComputedStyle(t);return e in i?i[e]:""}var s="webkitTransition"in t.style?"webkitTransitionDuration":"transitionDuration";function l(t){var i=o(t,a),n=o(t,s),l=n.includes("ms")?1:1e3,r=e&&i&&"none"!==i?parseFloat(n)*l:0;return Number.isNaN(r)?0:r}function r(t,s){var r=0,c=new Event(i),d=l(t),u=function(t){var i=o(t,a),s=o(t,n),l=s.includes("ms")?1:1e3,r=e&&i&&"none"!==i?parseFloat(s)*l:0;return Number.isNaN(r)?0:r}(t);if(e&&d){var m=function(e){e.target===t&&(s.apply(t,[e]),t.removeEventListener(i,m),r=1)};t.addEventListener(i,m),setTimeout((function(){r||t.dispatchEvent(c)}),d+u+17)}else s.apply(t,[c])}function c(t){return t instanceof HTMLElement?t.ownerDocument:t instanceof Window?t.document:window.document}var d=[Document,Node,Element,HTMLElement],u=[Element,HTMLElement];function m(t,e){var i="string"==typeof t,n=e&&d.some((function(t){return e instanceof t}))?e:c();return!i&&[].concat(u).some((function(e){return t instanceof e}))?t:i?n.querySelector(t):null}function f(t,e,i){var n=new CustomEvent(t+".bs."+e,{cancelable:!0});return void 0!==i&&Object.keys(i).forEach((function(t){Object.defineProperty(n,t,{value:i[t]})})),n}function h(t){this&&this.dispatchEvent(t)}function g(t){var e,i,n=this,a=f("close","alert"),o=f("closed","alert");function s(t){e[t?"addEventListener":"removeEventListener"]("click",l,!1)}function l(t){i=t&&t.target.closest(".alert"),(e=m('[data-dismiss="alert"]',i))&&i&&(e===t.target||e.contains(t.target))&&n.close()}function c(){s(),i.parentNode.removeChild(i),h.call(i,o)}n.close=function(){if(i&&e&&i.classList.contains("show")){if(h.call(i,a),a.defaultPrevented)return;n.dispose(),i.classList.remove("show"),i.classList.contains("fade")?r(i,c):c()}},n.dispose=function(){s(),delete e.Alert},e=m(t),i=e.closest(".alert"),e.Alert&&e.Alert.dispose(),e.Alert||s(1),n.element=e,e.Alert=n}function p(t){var e,i,n=f("change","button");function a(t){var a=t.target,o=a.closest("LABEL"),s=null;"LABEL"===a.tagName?s=a:o&&(s=o);var l=s&&s.getElementsByTagName("INPUT")[0];if(l){if(h.call(l,n),h.call(e,n),"checkbox"===l.type){if(n.defaultPrevented)return;l.checked?(s.classList.remove("active"),l.getAttribute("checked"),l.removeAttribute("checked"),l.checked=!1):(s.classList.add("active"),l.getAttribute("checked"),l.setAttribute("checked","checked"),l.checked=!0),e.toggled||(e.toggled=!0)}if("radio"===l.type&&!e.toggled){if(n.defaultPrevented)return;(!l.checked||0===t.screenX&&0===t.screenY)&&(s.classList.add("active"),s.classList.add("focus"),l.setAttribute("checked","checked"),l.checked=!0,e.toggled=!0,Array.from(i).forEach((function(t){var e=t.getElementsByTagName("INPUT")[0];t!==s&&t.classList.contains("active")&&(h.call(e,n),t.classList.remove("active"),e.removeAttribute("checked"),e.checked=!1)})))}setTimeout((function(){e.toggled=!1}),50)}}function o(t){32===(t.which||t.keyCode)&&t.target===document.activeElement&&a(t)}function s(t){32===(t.which||t.keyCode)&&t.preventDefault()}function l(t){if("INPUT"===t.target.tagName){var e="focusin"===t.type?"add":"remove";t.target.closest(".btn").classList[e]("focus")}}function r(t){var i=t?"addEventListener":"removeEventListener";e[i]("click",a,!1),e[i]("keyup",o,!1),e[i]("keydown",s,!1),e[i]("focusin",l,!1),e[i]("focusout",l,!1)}this.dispose=function(){r(),delete e.Button},(e=m(t)).Button&&e.Button.dispose(),(i=e.getElementsByClassName("btn")).length&&(e.Button||r(1),e.toggled=!1,e.Button=this,Array.from(i).forEach((function(t){var e=m("input:checked",t);!t.classList.contains("active")&&e&&t.classList.add("active"),t.classList.contains("active")&&!e&&t.classList.remove("active")})))}var v="onmouseleave"in document?["mouseenter","mouseleave"]:["mouseover","mouseout"],L={passive:!0};var b=function(t){var e=function(t,e){var i=t.getBoundingClientRect(),n=i.width,a=i.height,o=i.top,s=i.right,l=i.bottom,r=i.left,c=1,d=1;if(e&&t instanceof HTMLElement){var u=t.offsetWidth,m=t.offsetHeight;c=u>0&&Math.round(n)/u||1,d=m>0&&Math.round(a)/m||1}return{width:n/c,height:a/d,top:o/d,right:s/c,bottom:l/d,left:r/c,x:r/c,y:o/d}}(t),i=e.top,n=e.bottom;return i<=c(t).documentElement.clientHeight&&n>=0},y=function(t){return t.offsetHeight};function w(t,e){var i,n,a,o,s,c,d,u,g,p,w,A,T,E=e||{},k=this;function x(){!1===c.interval||o.classList.contains("paused")||(o.classList.add("paused"),s.isSliding||(clearInterval(s.timer),s.timer=null))}function N(){!1!==c.interval&&o.classList.contains("paused")&&(o.classList.remove("paused"),s.isSliding||(clearInterval(s.timer),s.timer=null,k.cycle()))}function C(t){if(t.preventDefault(),!s.isSliding){var e=t.target;e&&!e.classList.contains("active")&&e.getAttribute("data-slide-to")&&(s.index=+e.getAttribute("data-slide-to"),k.slideTo(s.index))}}function P(t){if(t.preventDefault(),!s.isSliding){var e=t.currentTarget||t.srcElement;e===w?s.index+=1:e===p&&(s.index-=1),k.slideTo(s.index)}}function H(t){var e=t.which;if(!s.isSliding){switch(e){case 39:s.index+=1;break;case 37:s.index-=1;break;default:return}k.slideTo(s.index)}}function M(t){var e=t?"addEventListener":"removeEventListener";c.pause&&c.interval&&(o[e](v[0],x,!1),o[e](v[1],N,!1),o[e]("touchstart",x,L),o[e]("touchend",N,L)),c.touch&&g.length>1&&o[e]("touchstart",B,L),w&&w[e]("click",P,!1),p&&p[e]("click",P,!1),A&&A[e]("click",C,!1),c.keyboard&&window[e]("keydown",H,!1)}function S(t){var e=t?"addEventListener":"removeEventListener";o[e]("touchmove",I,L),o[e]("touchend",D,L)}function B(t){s.isTouch||(s.touchPosition.startX=t.changedTouches[0].pageX,o.contains(t.target)&&(s.isTouch=!0,S(1)))}function I(t){s.isTouch?(s.touchPosition.currentX=t.changedTouches[0].pageX,"touchmove"===t.type&&t.changedTouches.length>1&&t.preventDefault()):t.preventDefault()}function D(t){if(s.isTouch&&!s.isSliding&&(s.touchPosition.endX=s.touchPosition.currentX||t.changedTouches[0].pageX,s.isTouch)){if((!o.contains(t.target)||!o.contains(t.relatedTarget))&&Math.abs(s.touchPosition.startX-s.touchPosition.endX)<75)return;s.touchPosition.currentXs.touchPosition.startX&&(s.index-=1),s.isTouch=!1,k.slideTo(s.index),S()}}function X(t){Array.from(T).forEach((function(t){return t.classList.remove("active")})),T[t]&&T[t].classList.add("active")}function R(t){if(s.touchPosition){var e=s.index,i=t&&t.target!==g[e]?1e3*t.elapsedTime+100:20,n=k.getActiveIndex(),a="left"===s.direction?"next":"prev";s.isSliding&&setTimeout((function(){s.touchPosition&&(s.isSliding=!1,g[e].classList.add("active"),g[n].classList.remove("active"),g[e].classList.remove("carousel-item-"+a),g[e].classList.remove("carousel-item-"+s.direction),g[n].classList.remove("carousel-item-"+s.direction),h.call(o,u),document.hidden||!c.interval||o.classList.contains("paused")||k.cycle())}),i)}}if(k.cycle=function(){s.timer&&(clearInterval(s.timer),s.timer=null),s.timer=setInterval((function(){var t=s.index||k.getActiveIndex();b(o)&&(t+=1,k.slideTo(t))}),c.interval)},k.slideTo=function(t){if(!s.isSliding){var e=k.getActiveIndex(),i=t;if(e!==i){ei||e===g.length-1&&0===i)&&(s.direction="right"),i<0?i=g.length-1:i>=g.length&&(i=0);var n="left"===s.direction?"next":"prev",a={relatedTarget:g[i],direction:s.direction,from:e,to:i};d=f("slide","carousel",a),u=f("slid","carousel",a),h.call(o,d),d.defaultPrevented||(s.index=i,s.isSliding=!0,clearInterval(s.timer),s.timer=null,X(i),l(g[i])&&o.classList.contains("slide")?(g[i].classList.add("carousel-item-"+n),y(g[i]),g[i].classList.add("carousel-item-"+s.direction),g[e].classList.add("carousel-item-"+s.direction),r(g[i],R)):(g[i].classList.add("active"),y(g[i]),g[e].classList.remove("active"),setTimeout((function(){s.isSliding=!1,c.interval&&o&&!o.classList.contains("paused")&&k.cycle(),h.call(o,u)}),100)))}}},k.getActiveIndex=function(){return Array.from(g).indexOf(o.getElementsByClassName("carousel-item active")[0])||0},k.dispose=function(){var t=["left","right","prev","next"];Array.from(g).forEach((function(e,i){e.classList.contains("active")&&X(i),t.forEach((function(t){return e.classList.remove("carousel-item-"+t)}))})),clearInterval(s.timer),M(),s={},c={},delete o.Carousel},(o=m(t)).Carousel&&o.Carousel.dispose(),g=o.getElementsByClassName("carousel-item"),i=o.getElementsByClassName("carousel-control-prev"),p=i[0],n=o.getElementsByClassName("carousel-control-next"),w=n[0],a=o.getElementsByClassName("carousel-indicators"),A=a[0],T=A&&A.getElementsByTagName("LI")||[],!(g.length<2)){var W=o.getAttribute("data-interval"),O="false"===W?0:+W,j="false"===o.getAttribute("data-touch")?0:1,z="hover"===o.getAttribute("data-pause")||!1,U="true"===o.getAttribute("data-keyboard")||!1,q=E.interval,F=E.touch;(c={}).keyboard=!0===E.keyboard||U,c.pause=!("hover"!==E.pause&&!z)&&"hover",c.touch=F||j,c.interval=5e3,"number"==typeof q?c.interval=q:!1===q||0===O||!1===O?c.interval=0:Number.isNaN(O)||(c.interval=O),k.getActiveIndex()<0&&(g.length&&g[0].classList.add("active"),T.length&&X(0)),(s={}).direction="left",s.index=0,s.timer=null,s.isSliding=!1,s.isTouch=!1,s.touchPosition={startX:0,currentX:0,endX:0},M(1),c.interval&&k.cycle(),o.Carousel=k}}function A(t,e){var i,n,a,o,s,l,c,d=e||{},u=this,g=null,p=null;function v(t,e){h.call(t,l),l.defaultPrevented||(t.isAnimating=!0,t.style.height=t.scrollHeight+"px",t.classList.remove("collapse"),t.classList.remove("show"),t.classList.add("collapsing"),y(t),t.style.height="0px",r(t,(function(){t.isAnimating=!1,t.setAttribute("aria-expanded","false"),e.setAttribute("aria-expanded","false"),t.classList.remove("collapsing"),t.classList.add("collapse"),t.style.height="",h.call(t,c)})))}u.toggle=function(t){(t&&"A"===t.target.tagName||"A"===i.tagName)&&t.preventDefault(),(i.contains(t.target)||t.target===i)&&(p.classList.contains("show")?u.hide():u.show())},u.hide=function(){p.isAnimating||(v(p,i),i.classList.add("collapsed"))},u.show=function(){var t,e,l;g&&(t=g.getElementsByClassName("collapse show"),n=t[0],a=n&&(m('[data-target="#'+n.id+'"]',g)||m('[href="#'+n.id+'"]',g))),p.isAnimating||(a&&n!==p&&(v(n,a),a.classList.add("collapsed")),e=p,l=i,h.call(e,o),o.defaultPrevented||(e.isAnimating=!0,e.classList.add("collapsing"),e.classList.remove("collapse"),e.style.height=e.scrollHeight+"px",r(e,(function(){e.isAnimating=!1,e.setAttribute("aria-expanded","true"),l.setAttribute("aria-expanded","true"),e.classList.remove("collapsing"),e.classList.add("collapse"),e.classList.add("show"),e.style.height="",h.call(e,s)}))),i.classList.remove("collapsed"))},u.dispose=function(){i.removeEventListener("click",u.toggle,!1),delete i.Collapse},(i=m(t)).Collapse&&i.Collapse.dispose();var L=i.getAttribute("data-parent");o=f("show","collapse"),s=f("shown","collapse"),l=f("hide","collapse"),c=f("hidden","collapse"),null!==(p=m(d.target||i.getAttribute("data-target")||i.getAttribute("href")))&&(p.isAnimating=!1);var b=d.parent||L;g=b?i.closest(b):null,i.Collapse||i.addEventListener("click",u.toggle,!1),i.Collapse=u}var T=function(t){return t.focus()};function E(t,e){var i,n,a,o,s,l,r,c,d=this,u=null,g=[];function p(t){(t.hasAttribute("href")&&"#"===t.href.slice(-1)||t.parentNode&&t.parentNode.hasAttribute("href")&&"#"===t.parentNode.href.slice(-1))&&this.preventDefault()}function v(){var t=i.open?"addEventListener":"removeEventListener";document[t]("click",L,!1),document[t]("keydown",y,!1),document[t]("keyup",w,!1),document[t]("focus",L,!1)}function L(t){var e=t.target;if(e.getAttribute){var n=e&&e.getAttribute("data-toggle")||e.parentNode&&e.parentNode.getAttribute&&e.parentNode.getAttribute("data-toggle");("focus"!==t.type||e!==i&&e!==r&&!r.contains(e))&&(e!==r&&!r.contains(e)||!c&&!n)&&(u=e===i||i.contains(e)?i:null,d.hide(),p.call(t,e))}}function b(t){u=i,d.show(),p.call(t,t.target)}function y(t){var e=t.which||t.keyCode;38!==e&&40!==e||t.preventDefault()}function w(t){var e=t.which||t.keyCode,n=document.activeElement,a=n===i,o=r.contains(n),s=n.parentNode===r||n.parentNode.parentNode===r,l=g.indexOf(n);s&&(a?l=0:38===e?l=l>1?l-1:0:40===e&&(l=l=d,w=m.top+c/2+g/2>=u,A=m.top-c<0,T=m.left-r<0,E=m.top+c+g>=u,k=m.left+r+h>=d,x=i;x="right"===(x="left"===(x="bottom"===(x="top"===(x=("left"===x||"right"===x)&&T&&k?"top":x)&&A?"bottom":x)&&E?"top":x)&&T?"right":x)&&k?"left":x,-1===e.className.indexOf(x)&&(e.className=e.className.replace(/\b(top|bottom|left|right)+/,x));var C=v.offsetWidth,P=v.offsetHeight;"left"===x||"right"===x?(o="left"===x?m.left+f.x-r-(p?C:0):m.left+f.x+h,L?(a=m.top+f.y,s=g/2-C):w?(a=m.top+f.y-c+g,s=c-g/2-C):(a=m.top+f.y-c/2+g/2,s=c/2-(p?.9*P:P/2))):"top"!==x&&"bottom"!==x||(a="top"===x?m.top+f.y-c-(p?P:0):m.top+f.y+g,b?(o=0,l=m.left+h/2-C):y?(o=d-1.01*r,l=r-(d-m.left)+h/2-C/2):(o=m.left+f.x-r/2+h/2,l=r/2-(p?C:C/2))),e.style.top=a+"px",e.style.left=o+"px",s&&(v.style.top=s+"px"),l&&(v.style.left=l+"px")}function P(t,e){var i,n,a,o,s,l,c,d,u,g=e||{},p=this,b=null,y=0,w=/(iPhone|iPod|iPad)/.test(navigator.userAgent),A={};function T(t){null!==b&&t.target===m(".close",b)&&p.hide()}function E(t){return g[t]||i.dataset[t]||null}function k(){return E("title")}function N(){return E("content")}function P(){null===b&&i.focus()}function H(t){var e=t?"addEventListener":"removeEventListener";"hover"===A.trigger?(i[e](x,p.show),i[e](v[0],p.show),A.dismissible||i[e](v[1],p.hide)):"click"===A.trigger?i[e](A.trigger,p.toggle):"focus"===A.trigger&&(w&&i[e]("click",P,!1),i[e](A.trigger,p.toggle))}function M(t){b&&b.contains(t.target)||t.target===i||i.contains(t.target)||p.hide()}function S(t){var e=t?"addEventListener":"removeEventListener";A.dismissible?document[e]("click",T,!1):("focus"===A.trigger&&i[e]("blur",p.hide),"hover"===A.trigger&&document[e]("touchstart",M,L)),window[e]("resize",p.hide,L)}function B(){S(1),h.call(i,c)}function I(){S(),A.container.removeChild(b),y=null,b=null,h.call(i,u)}p.toggle=function(){null===b?p.show():p.hide()},p.show=function(){clearTimeout(y),y=setTimeout((function(){if(null===b){if(h.call(i,l),l.defaultPrevented)return;!function(){n=k(),a=(a=N())?a.trim():null,b=document.createElement("div");var t=document.createElement("div");if(t.classList.add("arrow"),b.appendChild(t),null!==a&&null===A.template){if(b.setAttribute("role","tooltip"),null!==n){var e=document.createElement("h3");e.classList.add("popover-header"),e.innerHTML=A.dismissible?n+s:n,b.appendChild(e)}var i=document.createElement("div");i.classList.add("popover-body"),i.innerHTML=A.dismissible&&null===n?a+s:a,b.appendChild(i)}else{var l=document.createElement("div");l.innerHTML=A.template.trim(),b.className=l.firstChild.className,b.innerHTML=l.firstChild.innerHTML;var r=m(".popover-header",b),c=m(".popover-body",b);n&&r&&(r.innerHTML=n.trim()),a&&c&&(c.innerHTML=a.trim())}A.container.appendChild(b),b.style.display="block",b.classList.contains("popover")||b.classList.add("popover"),b.classList.contains(A.animation)||b.classList.add(A.animation),b.classList.contains(o)||b.classList.add(o)}(),C(i,b,A.placement,A.container),b.classList.contains("show")||b.classList.add("show"),A.animation?r(b,B):B()}}),20)},p.hide=function(){clearTimeout(y),y=setTimeout((function(){if(b&&null!==b&&b.classList.contains("show")){if(h.call(i,d),d.defaultPrevented)return;b.classList.remove("show"),A.animation?r(b,I):I()}}),A.delay)},p.dispose=function(){p.hide(),H(),delete i.Popover},(i=m(t)).Popover&&i.Popover.dispose();var D=i.getAttribute("data-trigger"),X=i.getAttribute("data-animation"),R=i.getAttribute("data-placement"),W=i.getAttribute("data-dismissible"),O=i.getAttribute("data-delay"),j=i.getAttribute("data-container");s='',l=f("show","popover"),c=f("shown","popover"),d=f("hide","popover"),u=f("hidden","popover");var z=m(g.container),U=m(j),q=i.closest(".modal"),F=i.closest(".fixed-top"),Y=i.closest(".fixed-bottom");A.template=g.template?g.template:null,A.trigger=g.trigger?g.trigger:D||"hover",A.animation=g.animation&&"fade"!==g.animation?g.animation:X||"fade",A.placement=g.placement?g.placement:R||"top",A.delay=parseInt(g.delay||O,10)||200,A.dismissible=!(!g.dismissible&&"true"!==W),A.container=z||U||F||Y||q||document.body,o="bs-popover-"+A.placement,n=k(),((a=N())||A.template)&&(i.Popover||H(1),i.Popover=p)}function H(t,e){var i,n,a,o,s,l=e||{},r=this,c={};function d(){var t,e,s;(a=o.getElementsByTagName("A"),n.scrollTop=n.isWindow?N().y:i.scrollTop,n.length!==a.length||g()!==n.scrollHeight)&&(n.items=[],n.offsets=[],n.scrollHeight=g(),n.maxScroll=n.scrollHeight-(n.isWindow?window.innerHeight:i.getBoundingClientRect().height),Array.from(a).forEach((function(i){t=i.getAttribute("href"),(e=t&&"#"===t.charAt(0)&&"#"!==t.slice(-1)&&m(t))&&(n.items.push(i),s=e.getBoundingClientRect(),n.offsets.push((n.isWindow?s.top+n.scrollTop:e.offsetTop)-c.offset))})),n.length=n.items.length)}function u(t){var e=t?"addEventListener":"removeEventListener";s[e]("scroll",r.refresh,L),window[e]("resize",r.refresh,L)}function g(){return s.scrollHeight||Math.max(document.body.scrollHeight,document.documentElement.scrollHeight)}function p(){Array.from(a).map((function(t){return t.classList.contains("active")&&t.classList.remove("active")}))}function v(t){var e,a=t;p(),n.activeItem=a,a.classList.add("active");for(var o=[];a.parentNode!==document.body;)((e=(a=a.parentNode).classList).contains("dropdown-menu")||e.contains("nav"))&&o.push(a);o.forEach((function(t){var e=t.previousElementSibling;e&&!e.classList.contains("active")&&e.classList.add("active")})),h.call(i,f("activate","scrollspy",{relatedTarget:n.activeItem}))}r.refresh=function(){if(d(),n.scrollTop>=n.maxScroll){var t=n.items[n.length-1];n.activeItem!==t&&v(t)}else{if(n.activeItem&&n.scrollTop0)return n.activeItem=null,void p();for(var e=n.length;e>-1;)n.activeItem!==n.items[e]&&n.scrollTop>=n.offsets[e]&&(void 0===n.offsets[e+1]||n.scrollTop1&&(g=t[t.length-1]):g=t[0],g}function P(){return m(C().getAttribute("href"))}function H(t){t.preventDefault(),u=t.currentTarget,a.isAnimating||T.show()}T.show=function(){if(!(u=u||n).classList.contains("active")){if(v=m(u.getAttribute("href")),g=C(),p=P(),c=f("hide","tab",{relatedTarget:u}),h.call(g,c),c.defaultPrevented)return;a.isAnimating=!0,g.classList.remove("active"),g.setAttribute("aria-selected","false"),u.classList.add("active"),u.setAttribute("aria-selected","true"),o&&(n.parentNode.classList.contains("dropdown-menu")?o.classList.contains("active")||o.classList.add("active"):o.classList.contains("active")&&o.classList.remove("active")),p.classList.contains("fade")?(p.classList.remove("show"),r(p,N)):N()}},T.dispose=function(){n.removeEventListener("click",H,!1),delete n.Tab},(n=m(t)).Tab&&n.Tab.dispose();var M=n.getAttribute("data-height");a=n.closest(".nav"),o=a&&m(".dropdown-toggle",a);var S=!(!e||!1===A.height||"false"===M);a.isAnimating=!1,n.Tab||n.addEventListener("click",H,!1),S&&(E=P().parentNode),n.Tab=T}function S(t,e){var i,n,a,o,s,l,c=e||{},d=this,u=0,g={};function p(){n.classList.remove("showing"),n.classList.add("show"),h.call(n,s),g.autohide&&d.hide()}function v(){n.classList.add("hide"),h.call(n,l)}function L(){n.classList.remove("show"),g.animation?r(n,v):v()}function b(){clearTimeout(u),i.removeEventListener("click",d.hide,!1),delete i.Toast}d.show=function(){if(n&&!n.classList.contains("show")){if(h.call(n,a),a.defaultPrevented)return;g.animation&&n.classList.add("fade"),n.classList.remove("hide"),y(n),n.classList.add("showing"),g.animation?r(n,p):p()}},d.hide=function(t){if(n&&n.classList.contains("show")){if(h.call(n,o),o.defaultPrevented)return;t?L():u=setTimeout(L,g.delay)}},d.dispose=function(){g.animation?r(n,b):b()},(i=m(t)).Toast&&i.Toast.dispose(),n=i.closest(".toast");var w=i.getAttribute("data-animation"),A=i.getAttribute("data-autohide"),T=i.getAttribute("data-delay");a=f("show","toast"),o=f("hide","toast"),s=f("shown","toast"),l=f("hidden","toast"),g.animation=!1===c.animation||"false"===w?0:1,g.autohide=!1===c.autohide||"false"===A?0:1,g.delay=parseInt(c.delay||T,10)||500,i.Toast||i.addEventListener("click",d.hide,!1),i.Toast=d}function B(t,e){var i,n,a,o,s,l,c,d=e||{},u=this,g=null,p=0,b={};function y(){return i.getAttribute("title")||i.getAttribute("data-title")||i.getAttribute("data-original-title")}function w(t){g&&g.contains(t.target)||t.target===i||i.contains(t.target)||u.hide()}function A(t){var e=t?"addEventListener":"removeEventListener";document[e]("touchstart",w,L),window[e]("resize",u.hide,L)}function T(){A(1),h.call(i,s)}function E(){A(),b.container.removeChild(g),g=null,p=null,h.call(i,c)}function k(t){var e=t?"addEventListener":"removeEventListener";i[e](x,u.show,!1),i[e](v[0],u.show,!1),i[e](v[1],u.hide,!1)}u.show=function(){clearTimeout(p),p=setTimeout((function(){if(null===g){if(h.call(i,o),o.defaultPrevented)return;!1!==function(){if(n=y()){if(g=document.createElement("div"),b.template){var t=document.createElement("div");t.innerHTML=b.template.trim(),g.className=t.firstChild.className,g.innerHTML=t.firstChild.innerHTML,m(".tooltip-inner",g).innerHTML=n.trim()}else{var e=document.createElement("div");e.classList.add("arrow"),g.appendChild(e);var i=document.createElement("div");i.classList.add("tooltip-inner"),g.appendChild(i),i.innerHTML=n}g.style.left="0",g.style.top="0",g.setAttribute("role","tooltip"),g.classList.contains("tooltip")||g.classList.add("tooltip"),g.classList.contains(b.animation)||g.classList.add(b.animation),g.classList.contains(a)||g.classList.add(a),b.container.appendChild(g)}}()&&(C(i,g,b.placement,b.container),g.classList.contains("show")||g.classList.add("show"),b.animation?r(g,T):T())}}),20)},u.hide=function(){clearTimeout(p),p=setTimeout((function(){if(g&&g.classList.contains("show")){if(h.call(i,l),l.defaultPrevented)return;g.classList.remove("show"),b.animation?r(g,E):E()}}),b.delay)},u.toggle=function(){g?u.hide():u.show()},u.dispose=function(){k(),u.hide(),i.setAttribute("title",i.getAttribute("data-original-title")),i.removeAttribute("data-original-title"),delete i.Tooltip},(i=m(t)).Tooltip&&i.Tooltip.dispose();var N=i.getAttribute("data-animation"),P=i.getAttribute("data-placement"),H=i.getAttribute("data-delay"),M=i.getAttribute("data-container"),S=m(d.container),B=m(M),I=i.closest(".modal");o=f("show","tooltip"),s=f("shown","tooltip"),l=f("hide","tooltip"),c=f("hidden","tooltip");var D=i.closest(".fixed-top"),X=i.closest(".fixed-bottom");b.animation=d.animation&&"fade"!==d.animation?d.animation:N||"fade",b.placement=d.placement?d.placement:P||"top",b.template=d.template?d.template:null,b.delay=parseInt(d.delay||H,10)||200,b.container=S||B||D||X||I||document.body,a="bs-tooltip-"+b.placement,(n=y())&&(i.Tooltip||(i.setAttribute("data-original-title",n),i.removeAttribute("title"),k(1)),i.Tooltip=u)}var I={};function D(t){var e=t instanceof Element?t:document;Object.keys(I).forEach((function(t){var i,n;i=I[t][0],n=e.querySelectorAll(I[t][1]),Array.from(n).map((function(t){return new i(t)}))}))}I.Alert=[g,'[data-dismiss="alert"]'],I.Button=[p,'[data-toggle="buttons"]'],I.Carousel=[w,'[data-ride="carousel"]'],I.Collapse=[A,'[data-toggle="collapse"]'],I.Dropdown=[E,'[data-toggle="dropdown"]'],I.Modal=[k,'[data-toggle="modal"]'],I.Popover=[P,'[data-toggle="popover"],[data-tip="popover"]'],I.ScrollSpy=[H,'[data-spy="scroll"]'],I.Tab=[M,'[data-toggle="tab"]'],I.Toast=[S,'[data-dismiss="toast"]'],I.Tooltip=[B,'[data-toggle="tooltip"],[data-tip="tooltip"]'],document.body?D():document.addEventListener("DOMContentLoaded",(function t(){D(),document.removeEventListener("DOMContentLoaded",t,!1)}),!1);return{Alert:g,Button:p,Carousel:w,Collapse:A,Dropdown:E,Modal:k,Popover:P,ScrollSpy:H,Tab:M,Toast:S,Tooltip:B,initCallback:D,removeDataAPI:function(t){var e=t instanceof Element?t:document;Object.keys(I).forEach((function(t){var i,n;i=t,n=e.querySelectorAll(I[t][1]),Array.from(n).map((function(t){return t[i].dispose()}))}))},componentsInit:I,Version:"4.1.0alpha1"}})); diff --git a/dist/bootstrap-native.esm.js b/dist/bootstrap-native.esm.js index 4569c799..291415c9 100644 --- a/dist/bootstrap-native.esm.js +++ b/dist/bootstrap-native.esm.js @@ -1,45 +1,67 @@ /*! - * Native JavaScript for Bootstrap v4.1.0 (https://thednp.github.io/bootstrap.native/) - * Copyright 2015-2021 © dnp_theme + * Native JavaScript for Bootstrap v4.1.0alpha1 (https://thednp.github.io/bootstrap.native/) + * Copyright 2015-2022 © dnp_theme * Licensed under MIT (https://github.com/thednp/bootstrap.native/blob/master/LICENSE) */ /** - * A global namespace for 'transitionend' string. + * A global namespace for `click` event. * @type {string} */ -const transitionEndEvent = 'webkitTransition' in document.head.style ? 'webkitTransitionEnd' : 'transitionend'; +const mouseclickEvent = 'click'; /** - * A global namespace for CSS3 transition support. - * @type {boolean} + * A global namespace for 'transitionend' string. + * @type {string} */ -const supportTransition = 'webkitTransition' in document.head.style || 'transition' in document.head.style; +const transitionEndEvent = 'transitionend'; /** * A global namespace for 'transitionDelay' string. * @type {string} */ -const transitionDelay = 'webkitTransition' in document.head.style ? 'webkitTransitionDelay' : 'transitionDelay'; +const transitionDelay = 'transitionDelay'; /** - * A global namespace for 'transitionProperty' string. + * A global namespace for: + * * `transitionProperty` string for Firefox, + * * `transition` property for all other browsers. + * * @type {string} */ -const transitionProperty = 'webkitTransition' in document.head.style ? 'webkitTransitionProperty' : 'transitionProperty'; +const transitionProperty = 'transitionProperty'; + +/** + * Shortcut for `window.getComputedStyle(element).propertyName` + * static method. + * + * * If `element` parameter is not an `HTMLElement`, `getComputedStyle` + * throws a `ReferenceError`. + * + * @param {HTMLElement | Element} element target + * @param {string} property the css property + * @return {string} the css property value + */ +function getElementStyle(element, property) { + const computedStyle = getComputedStyle(element); + + // @ts-ignore -- must use camelcase strings, + // or non-camelcase strings with `getPropertyValue` + return property in computedStyle ? computedStyle[property] : ''; +} /** - * Utility to get the computed transitionDelay + * Utility to get the computed `transitionDelay` * from Element in miliseconds. * - * @param {Element} element target + * @param {HTMLElement | Element} element target * @return {number} the value in miliseconds */ function getElementTransitionDelay(element) { - const computedStyle = getComputedStyle(element); - const propertyValue = computedStyle[transitionProperty]; - const delayValue = computedStyle[transitionDelay]; + const propertyValue = getElementStyle(element, transitionProperty); + const delayValue = getElementStyle(element, transitionDelay); + const delayScale = delayValue.includes('ms') ? 1 : 1000; - const duration = supportTransition && propertyValue && propertyValue !== 'none' + const duration = propertyValue && propertyValue !== 'none' ? parseFloat(delayValue) * delayScale : 0; return !Number.isNaN(duration) ? duration : 0; @@ -49,32 +71,57 @@ function getElementTransitionDelay(element) { * A global namespace for 'transitionDuration' string. * @type {string} */ -const transitionDuration = 'webkitTransition' in document.head.style ? 'webkitTransitionDuration' : 'transitionDuration'; +const transitionDuration = 'transitionDuration'; /** - * Utility to get the computed transitionDuration + * Utility to get the computed `transitionDuration` * from Element in miliseconds. * - * @param {Element} element target + * @param {HTMLElement | Element} element target * @return {number} the value in miliseconds */ function getElementTransitionDuration(element) { - const computedStyle = getComputedStyle(element); - const propertyValue = computedStyle[transitionProperty]; - const durationValue = computedStyle[transitionDuration]; + const propertyValue = getElementStyle(element, transitionProperty); + const durationValue = getElementStyle(element, transitionDuration); const durationScale = durationValue.includes('ms') ? 1 : 1000; - const duration = supportTransition && propertyValue && propertyValue !== 'none' + const duration = propertyValue && propertyValue !== 'none' ? parseFloat(durationValue) * durationScale : 0; return !Number.isNaN(duration) ? duration : 0; } +/** + * Add eventListener to an `Element` | `HTMLElement` | `Document` target. + * + * @param {HTMLElement | Element | Document | Window} element event.target + * @param {string} eventName event.type + * @param {EventListenerObject['handleEvent']} handler callback + * @param {(EventListenerOptions | boolean)=} options other event options + */ +function on(element, eventName, handler, options) { + const ops = options || false; + element.addEventListener(eventName, handler, ops); +} + +/** + * Remove eventListener from an `Element` | `HTMLElement` | `Document` | `Window` target. + * + * @param {HTMLElement | Element | Document | Window} element event.target + * @param {string} eventName event.type + * @param {EventListenerObject['handleEvent']} handler callback + * @param {(EventListenerOptions | boolean)=} options other event options + */ +function off(element, eventName, handler, options) { + const ops = options || false; + element.removeEventListener(eventName, handler, ops); +} + /** * Utility to make sure callbacks are consistently * called when transition ends. * - * @param {Element} element target - * @param {function} handler `transitionend` callback + * @param {HTMLElement | Element} element target + * @param {EventListener} handler `transitionend` callback */ function emulateTransitionEnd(element, handler) { let called = 0; @@ -85,17 +132,16 @@ function emulateTransitionEnd(element, handler) { if (duration) { /** * Wrap the handler in on -> off callback - * @param {Event} e Event object - * @callback + * @param {TransitionEvent} e Event object */ const transitionEndWrapper = (e) => { if (e.target === element) { handler.apply(element, [e]); - element.removeEventListener(transitionEndEvent, transitionEndWrapper); + off(element, transitionEndEvent, transitionEndWrapper); called = 1; } }; - element.addEventListener(transitionEndEvent, transitionEndWrapper); + on(element, transitionEndEvent, transitionEndWrapper); setTimeout(() => { if (!called) element.dispatchEvent(endEvent); }, duration + delay + 17); @@ -105,33 +151,75 @@ function emulateTransitionEnd(element, handler) { } /** - * Checks if an element is an `Element`. - * - * @param {any} element the target element - * @returns {boolean} the query result + * Returns the `document` or the `#document` element. + * @see https://github.com/floating-ui/floating-ui + * @param {(Node | HTMLElement | Element | globalThis)=} node + * @returns {Document} */ -function isElement(element) { - return element instanceof Element; +function getDocument(node) { + if (node instanceof HTMLElement) return node.ownerDocument; + if (node instanceof Window) return node.document; + return window.document; } /** - * Utility to check if target is typeof Element + * A global array of possible `ParentNode`. + */ +const parentNodes = [Document, Node, Element, HTMLElement]; + +/** + * A global array with `Element` | `HTMLElement`. + */ +const elementNodes = [Element, HTMLElement]; + +/** + * Utility to check if target is typeof `HTMLElement`, `Element`, `Node` * or find one that matches a selector. * - * @param {Element | string} selector the input selector or target element - * @param {Element=} parent optional Element to look into - * @return {Element?} the Element or `querySelector` result + * @param {HTMLElement | Element | string} selector the input selector or target element + * @param {(HTMLElement | Element | Node | Document)=} parent optional node to look into + * @return {(HTMLElement | Element)?} the `HTMLElement` or `querySelector` result */ -function queryElement(selector, parent) { - const lookUp = parent && isElement(parent) ? parent : document; - // @ts-ignore - return isElement(selector) ? selector : lookUp.querySelector(selector); +function querySelector(selector, parent) { + const selectorIsString = typeof selector === 'string'; + const lookUp = parent && parentNodes.some((x) => parent instanceof x) + ? parent : getDocument(); + + if (!selectorIsString && [...elementNodes].some((x) => selector instanceof x)) { + return selector; + } + // @ts-ignore -- `ShadowRoot` is also a node + return selectorIsString ? lookUp.querySelector(selector) : null; +} + +/** + * Shortcut for `HTMLElement.closest` method which also works + * with children of `ShadowRoot`. The order of the parameters + * is intentional since they're both required. + * + * @see https://stackoverflow.com/q/54520554/803358 + * + * @param {HTMLElement | Element} element Element to look into + * @param {string} selector the selector name + * @return {(HTMLElement | Element)?} the query result + */ +function closest(element, selector) { + return element ? (element.closest(selector) + // @ts-ignore -- break out of `ShadowRoot` + || closest(element.getRootNode().host, selector)) : null; } /** - * Check class in Element.classList + * Shortcut for `Object.assign()` static method. + * @param {Record} obj a target object + * @param {Record} source a source object + */ +const ObjectAssign = (obj, source) => Object.assign(obj, source); + +/** + * Check class in `HTMLElement.classList`. * - * @param {Element} element target + * @param {HTMLElement | Element} element target * @param {string} classNAME to check * @return {boolean} */ @@ -140,9 +228,9 @@ function hasClass(element, classNAME) { } /** - * Remove class from Element.classList + * Remove class from `HTMLElement.classList`. * - * @param {Element} element target + * @param {HTMLElement | Element} element target * @param {string} classNAME to remove */ function removeClass(element, classNAME) { @@ -150,17 +238,14 @@ function removeClass(element, classNAME) { } /** - * A global namespace for 'addEventListener' string. - * @type {string} - */ -const addEventListener = 'addEventListener'; - -/** - * A global namespace for 'removeEventListener' string. - * @type {string} + * Shortcut for the `Element.dispatchEvent(Event)` method. + * + * @param {HTMLElement | Element} element is the target + * @param {Event} event is the `Event` object */ -const removeEventListener = 'removeEventListener'; +const dispatchEvent = (element, event) => element.dispatchEvent(event); +/** @type {Map>>} */ const componentData = new Map(); /** * An interface for web components background data. @@ -169,59 +254,58 @@ const componentData = new Map(); const Data = { /** * Sets web components data. - * @param {Element | string} element target element + * @param {HTMLElement | Element | string} target target element * @param {string} component the component's name or a unique key - * @param {any} instance the component instance + * @param {Record} instance the component instance */ - set: (element, component, instance) => { - const ELEMENT = queryElement(element); - if (!isElement(ELEMENT)) return; + set: (target, component, instance) => { + const element = querySelector(target); + if (!element) return; if (!componentData.has(component)) { componentData.set(component, new Map()); } const instanceMap = componentData.get(component); - instanceMap.set(ELEMENT, instance); + // @ts-ignore - not undefined, but defined right above + instanceMap.set(element, instance); }, /** * Returns all instances for specified component. * @param {string} component the component's name or a unique key - * @returns {any?} all the component instances + * @returns {Map>?} all the component instances */ getAllFor: (component) => { - if (componentData.has(component)) { - return componentData.get(component); - } - return null; + const instanceMap = componentData.get(component); + + return instanceMap || null; }, /** * Returns the instance associated with the target. - * @param {Element | string} element target element + * @param {HTMLElement | Element | string} target target element * @param {string} component the component's name or a unique key - * @returns {any?} the instance + * @returns {Record?} the instance */ - get: (element, component) => { - const ELEMENT = queryElement(element); - + get: (target, component) => { + const element = querySelector(target); const allForC = Data.getAllFor(component); - if (allForC && isElement(ELEMENT) && allForC.has(ELEMENT)) { - return allForC.get(ELEMENT); - } - return null; + const instance = element && allForC && allForC.get(element); + + return instance || null; }, /** * Removes web components data. - * @param {Element} element target element + * @param {HTMLElement | Element | string} target target element * @param {string} component the component's name or a unique key */ - remove: (element, component) => { - if (!componentData.has(component)) return; - + remove: (target, component) => { + const element = querySelector(target); const instanceMap = componentData.get(component); + if (!instanceMap || !element) return; + instanceMap.delete(element); if (instanceMap.size === 0) { @@ -232,11 +316,9 @@ const Data = { /** * An alias for `Data.get()`. - * @param {Element | string} element target element - * @param {string} component the component's name or a unique key - * @returns {any} the request result + * @type {SHORTER.getInstance} */ -const getInstance = (element, component) => Data.get(element, component); +const getInstance = (target, component) => Data.get(target, component); /** * Global namespace for most components `fade` class. @@ -253,30 +335,17 @@ const showClass = 'show'; */ const dataBsDismiss = 'data-bs-dismiss'; -/** Returns an original event for Bootstrap Native components. */ -class OriginalEvent extends CustomEvent { - /** - * @param {string} EventType event.type - * @param {Record=} config Event.options | Event.properties - */ - constructor(EventType, config) { - super(EventType, config); - /** @type {EventTarget?} */ - this.relatedTarget = null; - } -} - /** * Returns a namespaced `CustomEvent` specific to each component. * @param {string} EventType Event.type * @param {Record=} config Event.options | Event.properties - * @returns {OriginalEvent} a new namespaced event + * @returns {BSN.OriginalEvent} a new namespaced event */ function bootstrapCustomEvent(EventType, config) { - const OriginalCustomEvent = new OriginalEvent(EventType, { cancelable: true, bubbles: true }); + const OriginalCustomEvent = new CustomEvent(EventType, { cancelable: true, bubbles: true }); if (config instanceof Object) { - Object.assign(OriginalCustomEvent, config); + ObjectAssign(OriginalCustomEvent, config); } return OriginalCustomEvent; } @@ -284,7 +353,7 @@ function bootstrapCustomEvent(EventType, config) { /** * The raw value or a given component option. * - * @typedef {string | Element | Function | number | boolean | null} niceValue + * @typedef {string | HTMLElement | Function | number | boolean | null} niceValue */ /** @@ -310,94 +379,93 @@ function normalizeValue(value) { return null; } - // string / function / Element / object + // string / function / HTMLElement / object return value; } /** - * Utility to normalize component options + * Shortcut for `Object.keys()` static method. + * @param {Record} obj a target object + * @returns {string[]} + */ +const ObjectKeys = (obj) => Object.keys(obj); + +/** + * Utility to normalize component options. * - * @param {Element} element target - * @param {object} defaultOps component default options - * @param {object} inputOps component instance options - * @param {string} ns component namespace - * @return {object} normalized component options object + * @param {HTMLElement | Element} element target + * @param {Record} defaultOps component default options + * @param {Record} inputOps component instance options + * @param {string=} ns component namespace + * @return {Record} normalized component options object */ function normalizeOptions(element, defaultOps, inputOps, ns) { - // @ts-ignore + // @ts-ignore -- our targets are always `HTMLElement` const data = { ...element.dataset }; + /** @type {Record} */ const normalOps = {}; + /** @type {Record} */ const dataOps = {}; - Object.keys(data) - .forEach((k) => { - const key = k.includes(ns) - ? k.replace(ns, '').replace(/[A-Z]/, (match) => match.toLowerCase()) - : k; + ObjectKeys(data).forEach((k) => { + const key = ns && k.includes(ns) + ? k.replace(ns, '').replace(/[A-Z]/, (match) => match.toLowerCase()) + : k; - dataOps[key] = normalizeValue(data[k]); - }); + dataOps[key] = normalizeValue(data[k]); + }); - Object.keys(inputOps) - .forEach((k) => { - inputOps[k] = normalizeValue(inputOps[k]); - }); + ObjectKeys(inputOps).forEach((k) => { + inputOps[k] = normalizeValue(inputOps[k]); + }); - Object.keys(defaultOps) - .forEach((k) => { - if (k in inputOps) { - normalOps[k] = inputOps[k]; - } else if (k in dataOps) { - normalOps[k] = dataOps[k]; - } else { - normalOps[k] = defaultOps[k]; - } - }); + ObjectKeys(defaultOps).forEach((k) => { + if (k in inputOps) { + normalOps[k] = inputOps[k]; + } else if (k in dataOps) { + normalOps[k] = dataOps[k]; + } else { + normalOps[k] = defaultOps[k]; + } + }); return normalOps; } -var version = "4.1.0"; +var version = "4.1.0alpha1"; const Version = version; /* Native JavaScript for Bootstrap 5 | Base Component ----------------------------------------------------- */ -/** - * Returns a new `BaseComponent` instance. - */ +/** Returns a new `BaseComponent` instance. */ class BaseComponent { /** - * @param {Element | string} target Element or selector string + * @param {HTMLElement | Element | string} target `Element` or selector string * @param {BSN.ComponentOptions=} config component instance options */ constructor(target, config) { const self = this; - const element = queryElement(target); + const element = querySelector(target); - if (!isElement(element)) { - throw TypeError(`${self.name} Error: "${target}" not a valid selector.`); + if (!element) { + throw Error(`${self.name} Error: "${target}" is not a valid selector.`); } - /** @type {BSN.ComponentOptions} */ + /** @static @type {BSN.ComponentOptions} */ self.options = {}; - // @ts-ignore const prevInstance = Data.get(element, self.name); if (prevInstance) prevInstance.dispose(); - /** @type {Element} */ - // @ts-ignore + /** @type {HTMLElement | Element} */ self.element = element; if (self.defaults && Object.keys(self.defaults).length) { - /** @static @type {Record} */ - // @ts-ignore self.options = normalizeOptions(element, self.defaults, (config || {}), 'bs'); } - // @ts-ignore Data.set(element, self.name, self); } @@ -418,10 +486,9 @@ class BaseComponent { */ dispose() { const self = this; - // @ts-ignore Data.remove(self.element, self.name); // @ts-ignore - Object.keys(self).forEach((prop) => { self[prop] = null; }); + ObjectKeys(self).forEach((prop) => { self[prop] = null; }); } } @@ -464,7 +531,7 @@ function alertTransitionEnd(self) { const { element } = self; toggleAlertHandler(self); - element.dispatchEvent(closedAlertEvent); + dispatchEvent(element, closedAlertEvent); self.dispose(); element.remove(); @@ -478,16 +545,16 @@ function alertTransitionEnd(self) { * @param {boolean=} add when `true`, event listener is added */ function toggleAlertHandler(self, add) { - const action = add ? addEventListener : removeEventListener; - // @ts-ignore - if (isElement(self.dismiss)) self.dismiss[action]('click', self.close); + const action = add ? on : off; + const { dismiss } = self; + if (dismiss) action(dismiss, mouseclickEvent, self.close); } // ALERT DEFINITION // ================ /** Creates a new Alert instance. */ class Alert extends BaseComponent { - /** @param {Element | string} target element or selector */ + /** @param {HTMLElement | Element | string} target element or selector */ constructor(target) { super(target); // bind @@ -497,11 +564,8 @@ class Alert extends BaseComponent { const { element } = self; // the dismiss button - /** @static @type {Element?} */ - // @ts-ignore - self.dismiss = queryElement(alertDismissSelector, element); - /** @static @type {Element?} */ - self.relatedTarget = null; + /** @static @type {(HTMLElement | Element)?} */ + self.dismiss = querySelector(alertDismissSelector, element); // add event listener toggleAlertHandler(self, true); @@ -522,16 +586,17 @@ class Alert extends BaseComponent { * disposes the instance once animation is complete, then * removes the element from the DOM. * - * @param {Event} e most likely the `click` event + * @param {Event=} e most likely the `click` event + * @this {Alert} the `Alert` instance or `EventTarget` */ close(e) { - const target = e ? e.target : null; // @ts-ignore - const self = e ? getAlertInstance(target.closest(alertSelector)) : this; + const self = e ? getAlertInstance(closest(this, alertSelector)) : this; + if (!self) return; const { element } = self; - if (self && element && hasClass(element, showClass)) { - element.dispatchEvent(closeAlertEvent); + if (hasClass(element, showClass)) { + dispatchEvent(element, closeAlertEvent); if (closeAlertEvent.defaultPrevented) return; removeClass(element, showClass); @@ -549,28 +614,36 @@ class Alert extends BaseComponent { } } -Object.assign(Alert, { +ObjectAssign(Alert, { selector: alertSelector, init: alertInitCallback, getInstance: getAlertInstance, }); /** - * Add class to Element.classList + * A global namespace for aria-pressed. + * @type {string} + */ +const ariaPressed = 'aria-pressed'; + +/** + * Shortcut for `HTMLElement.setAttribute()` method. + * @param {HTMLElement | Element} element target element + * @param {string} attribute attribute name + * @param {string} value attribute value + */ +const setAttribute = (element, attribute, value) => element.setAttribute(attribute, value); + +/** + * Add class to `HTMLElement.classList`. * - * @param {Element} element target + * @param {HTMLElement | Element} element target * @param {string} classNAME to add */ function addClass(element, classNAME) { element.classList.add(classNAME); } -/** - * A global namespace for aria-pressed. - * @type {string} - */ -const ariaPressed = 'aria-pressed'; - /** * Global namespace for most components active class. */ @@ -612,9 +685,8 @@ const buttonInitCallback = (element) => new Button(element); * @param {boolean=} add when `true`, event listener is added */ function toggleButtonHandler(self, add) { - const action = add ? addEventListener : removeEventListener; - // @ts-ignore - self.element[action]('click', self.toggle); + const action = add ? on : off; + action(self.element, mouseclickEvent, self.toggle); } // BUTTON DEFINITION @@ -622,7 +694,7 @@ function toggleButtonHandler(self, add) { /** Creates a new `Button` instance. */ class Button extends BaseComponent { /** - * @param {Element | string} target usually a `.btn` element + * @param {HTMLElement | Element | string} target usually a `.btn` element */ constructor(target) { super(target); @@ -632,9 +704,9 @@ class Button extends BaseComponent { const { element } = self; // set initial state - /** @private @type {boolean} */ + /** @type {boolean} */ self.isActive = hasClass(element, activeClass); - element.setAttribute(ariaPressed, `${!!self.isActive}`); + setAttribute(element, ariaPressed, `${!!self.isActive}`); // add event listener toggleButtonHandler(self, true); @@ -652,12 +724,13 @@ class Button extends BaseComponent { // ===================== /** * Toggles the state of the target button. - * @param {Event} e usually `click` Event object + * @param {MouseEvent} e usually `click` Event object */ toggle(e) { if (e) e.preventDefault(); // @ts-ignore const self = e ? getButtonInstance(this) : this; + if (!self) return; const { element } = self; if (hasClass(element, 'disabled')) return; @@ -667,7 +740,7 @@ class Button extends BaseComponent { const action = isActive ? removeClass : addClass; action(element, activeClass); - element.setAttribute(ariaPressed, isActive ? 'false' : 'true'); + setAttribute(element, ariaPressed, isActive ? 'false' : 'true'); } /** Removes the `Button` component from the target element. */ @@ -677,64 +750,304 @@ class Button extends BaseComponent { } } -Object.assign(Button, { +ObjectAssign(Button, { selector: buttonSelector, init: buttonInitCallback, getInstance: getButtonInstance, }); /** - * A global namespace for passive events support. - * @type {boolean} + * A global namespace for most scroll event listeners. + * @type {Partial} */ -const supportPassive = (() => { - let result = false; - try { - const opts = Object.defineProperty({}, 'passive', { - get() { - result = true; - return result; - }, - }); - document[addEventListener]('DOMContentLoaded', function wrap() { - document[removeEventListener]('DOMContentLoaded', wrap, opts); - }, opts); - } catch (e) { - throw Error('Passive events are not supported'); - } +const passiveHandler = { passive: true }; - return result; -})(); +/** + * Utility to force re-paint of an `HTMLElement` target. + * + * @param {HTMLElement | Element} element is the target + * @return {number} the `Element.offsetHeight` value + */ +// @ts-ignore +const reflow = (element) => element.offsetHeight; -// general event options +/** + * Returns the bounding client rect of a target `HTMLElement`. + * + * @see https://github.com/floating-ui/floating-ui + * + * @param {HTMLElement | Element} element event.target + * @param {boolean=} includeScale when *true*, the target scale is also computed + * @returns {SHORTER.BoundingClientRect} the bounding client rect object + */ +function getBoundingClientRect(element, includeScale) { + const { + width, height, top, right, bottom, left, + } = element.getBoundingClientRect(); + let scaleX = 1; + let scaleY = 1; + + if (includeScale && element instanceof HTMLElement) { + const { offsetWidth, offsetHeight } = element; + scaleX = offsetWidth > 0 ? Math.round(width) / offsetWidth || 1 : 1; + scaleY = offsetHeight > 0 ? Math.round(height) / offsetHeight || 1 : 1; + } + + return { + width: width / scaleX, + height: height / scaleY, + top: top / scaleY, + right: right / scaleX, + bottom: bottom / scaleY, + left: left / scaleX, + x: left / scaleX, + y: top / scaleY, + }; +} /** - * A global namespace for most scroll event listeners. + * Returns the `document.documentElement` or the `` element. + * + * @param {(Node | HTMLElement | Element | globalThis)=} node + * @returns {HTMLElement | HTMLHtmlElement} + */ +function getDocumentElement(node) { + return getDocument(node).documentElement; +} + +/** + * Utility to determine if an `HTMLElement` + * is partially visible in viewport. + * + * @param {HTMLElement | Element} element target + * @return {boolean} the query result + */ +const isElementInScrollRange = (element) => { + const { top, bottom } = getBoundingClientRect(element); + const { clientHeight } = getDocumentElement(element); + // checks bottom && top + return top <= clientHeight && bottom >= 0; +}; + +/** + * A shortcut for `(document|Element).querySelectorAll`. + * + * @param {string} selector the input selector + * @param {(HTMLElement | Element | Document | Node)=} parent optional node to look into + * @return {NodeListOf} the query result */ -const passiveHandler = supportPassive ? { passive: true } : false; +function querySelectorAll(selector, parent) { + const lookUp = parent && parentNodes + .some((x) => parent instanceof x) ? parent : getDocument(); + // @ts-ignore -- `ShadowRoot` is also a node + return lookUp.querySelectorAll(selector); +} /** - * Utility to force re-paint of an Element + * Shortcut for `HTMLElement.getElementsByClassName` method. Some `Node` elements + * like `ShadowRoot` do not support `getElementsByClassName`. * - * @param {Element | HTMLElement} element is the target - * @return {number} the Element.offsetHeight value + * @param {string} selector the class name + * @param {(HTMLElement | Element | Document)=} parent optional Element to look into + * @return {HTMLCollectionOf} the 'HTMLCollection' + */ +function getElementsByClassName(selector, parent) { + const lookUp = parent && parentNodes.some((x) => parent instanceof x) + ? parent : getDocument(); + return lookUp.getElementsByClassName(selector); +} + +/** + * A global namespace for `mouseenter` event. + * @type {string} */ -function reflow(element) { +const mouseenterEvent = 'mouseenter'; + +/** + * A global namespace for `mouseleave` event. + * @type {string} + */ +const mouseleaveEvent = 'mouseleave'; + +/** + * A global namespace for `keydown` event. + * @type {string} + */ +const keydownEvent = 'keydown'; + +/** + * A global namespace for `touchmove` event. + * @type {string} + */ +const touchmoveEvent = 'touchmove'; + +/** + * A global namespace for `touchend` event. + * @type {string} + */ +const touchendEvent = 'touchend'; + +/** + * A global namespace for `touchstart` event. + * @type {string} + */ +const touchstartEvent = 'touchstart'; + +/** + * Shortcut for `HTMLElement.getAttribute()` method. + * @param {HTMLElement | Element} element target element + * @param {string} attribute attribute name + */ +const getAttribute = (element, attribute) => element.getAttribute(attribute); + +/** + * A global namespace for `ArrowLeft` key. + * @type {string} e.which = 37 equivalent + */ +const keyArrowLeft = 'ArrowLeft'; + +/** + * A global namespace for `ArrowRight` key. + * @type {string} e.which = 39 equivalent + */ +const keyArrowRight = 'ArrowRight'; + +/** + * Checks if a page is Right To Left. + * @param {(HTMLElement | Element)=} node the target + * @returns {boolean} the query result + */ +const isRTL = (node) => getDocumentElement(node).dir === 'rtl'; + +/** @type {Map} */ +const TimeCache = new Map(); +/** + * An interface for one or more `TimerHandler`s per `Element`. + * @see https://github.com/thednp/navbar.js/ + */ +const Timer = { + /** + * Sets a new timeout timer for an element, or element -> key association. + * @param {HTMLElement | Element | string} target target element + * @param {ReturnType} callback the callback + * @param {number} delay the execution delay + * @param {string=} key a unique + */ + set: (target, callback, delay, key) => { + const element = querySelector(target); + + if (!element) return; + + if (key && key.length) { + if (!TimeCache.has(element)) { + TimeCache.set(element, new Map()); + } + const keyTimers = TimeCache.get(element); + keyTimers.set(key, setTimeout(callback, delay)); + } else { + TimeCache.set(element, setTimeout(callback, delay)); + } + }, + + /** + * Returns the timer associated with the target. + * @param {HTMLElement | Element | string} target target element + * @param {string=} key a unique + * @returns {number?} the timer + */ + get: (target, key) => { + const element = querySelector(target); + + if (!element) return null; + const keyTimers = TimeCache.get(element); + + if (key && key.length && keyTimers && keyTimers.get) { + return keyTimers.get(key) || null; + } + return keyTimers || null; + }, + + /** + * Clears the element's timer. + * @param {HTMLElement | Element | string} target target element + * @param {string=} key a unique key + */ + clear: (target, key) => { + const element = querySelector(target); + + if (!element) return; + + if (key && key.length) { + const keyTimers = TimeCache.get(element); + + if (keyTimers && keyTimers.get) { + clearTimeout(keyTimers.get(key)); + keyTimers.delete(key); + if (keyTimers.size === 0) { + TimeCache.delete(element); + } + } + } else { + clearTimeout(TimeCache.get(element)); + TimeCache.delete(element); + } + }, +}; + +/** + * Returns the `Window` object of a target node. + * @see https://github.com/floating-ui/floating-ui + * + * @param {(Node | HTMLElement | Element | Window)=} node target node + * @returns {globalThis} + */ +function getWindow(node) { + if (node == null) { + return window; + } + + if (!(node instanceof Window)) { + const { ownerDocument } = node; + return ownerDocument ? ownerDocument.defaultView || window : window; + } + // @ts-ignore - return element.offsetHeight; + return node; } /** - * Utility to determine if an `Element` - * is partially visible in viewport. + * Global namespace for most components `target` option. + */ +const dataBsTarget = 'data-bs-target'; + +/** + * Global namespace for most components `parent` option. + */ +const dataBsParent = 'data-bs-parent'; + +/** + * Global namespace for most components `container` option. + */ +const dataBsContainer = 'data-bs-container'; + +/** + * Returns the `Element` that THIS one targets + * via `data-bs-target`, `href`, `data-bs-parent` or `data-bs-container`. * - * @param {Element} element target - * @return {boolean} Boolean + * @param {HTMLElement | Element} element the target element + * @returns {(HTMLElement | Element)?} the query result */ -function isElementInScrollRange(element) { - const bcr = element.getBoundingClientRect(); - const viewportHeight = window.innerHeight || document.documentElement.clientHeight; - return bcr.top <= viewportHeight && bcr.bottom >= 0; // bottom && top +function getTargetElement(element) { + const targetAttr = [dataBsTarget, dataBsParent, dataBsContainer, 'href']; + const doc = getDocument(element); + + return targetAttr.map((att) => { + const attValue = getAttribute(element, att); + if (attValue) { + return att === dataBsParent ? closest(element, attValue) : querySelector(attValue, doc); + } + return null; + }).filter((x) => x)[0]; } /* Native JavaScript for Bootstrap 5 | Carousel @@ -745,9 +1058,10 @@ function isElementInScrollRange(element) { const carouselString = 'carousel'; const carouselComponent = 'Carousel'; const carouselSelector = `[data-bs-ride="${carouselString}"]`; -const carouselControl = `${carouselString}-control`; const carouselItem = `${carouselString}-item`; const dataBsSlideTo = 'data-bs-slide-to'; +const dataBsSlide = 'data-bs-slide'; + const pausedClass = 'paused'; const carouselDefaults = { @@ -788,18 +1102,14 @@ const carouselSlidEvent = bootstrapCustomEvent(`slid.bs.${carouselString}`); */ function carouselTransitionEndHandler(self) { const { - // @ts-ignore - index, direction, element, slides, options, isAnimating, + index, direction, element, slides, options, } = self; // discontinue disposed instances - // @ts-ignore - if (isAnimating && getCarouselInstance(element)) { + if (self.isAnimating && getCarouselInstance(element)) { const activeItem = getActiveIndex(self); const orientation = direction === 'left' ? 'next' : 'prev'; const directionClass = direction === 'left' ? 'start' : 'end'; - // @ts-ignore - self.isAnimating = false; addClass(slides[index], activeClass); removeClass(slides[activeItem], activeClass); @@ -808,13 +1118,12 @@ function carouselTransitionEndHandler(self) { removeClass(slides[index], `${carouselItem}-${directionClass}`); removeClass(slides[activeItem], `${carouselItem}-${directionClass}`); - // @ts-ignore - element.dispatchEvent(carouselSlidEvent); + dispatchEvent(element, carouselSlidEvent); + Timer.clear(element, dataBsSlide); // check for element, might have been disposed - if (!document.hidden && options.interval - // @ts-ignore - && !hasClass(element, pausedClass)) { + if (!getDocument(element).hidden && options.interval + && !self.isPaused) { self.cycle(); } } @@ -824,97 +1133,78 @@ function carouselTransitionEndHandler(self) { * Handles the `mouseenter` / `touchstart` events when *options.pause* * is set to `hover`. * - * @param {Event} e the `Event` object + * @param {MouseEvent} e the `Event` object */ function carouselPauseHandler(e) { const eventTarget = e.target; // @ts-ignore - const self = getCarouselInstance(eventTarget.closest(carouselSelector)); - // @ts-ignore - const { element, isAnimating } = self; + const self = getCarouselInstance(closest(eventTarget, carouselSelector)); - // @ts-ignore - if (!hasClass(element, pausedClass)) { - // @ts-ignore - addClass(element, pausedClass); - if (!isAnimating) { - // @ts-ignore - clearInterval(self.timer); - // @ts-ignore - self.timer = null; - } + if (self && !self.isPaused) { + self.pause(); } } /** - * Handles the `mouseleave` / `touchsend` events when *options.pause* + * Handles the `mouseleave` / `touchend` events when *options.pause* * is set to `hover`. * - * @param {Event} e the `Event` object + * @param {MouseEvent} e the `Event` object */ function carouselResumeHandler(e) { const { target } = e; // @ts-ignore - const self = getCarouselInstance(target.closest(carouselSelector)); - // @ts-ignore - const { isPaused, isAnimating, element } = self; + const self = getCarouselInstance(closest(target, carouselSelector)); + if (!self) return; + const { element } = self; - // @ts-ignore - if (!isPaused && hasClass(element, pausedClass)) { - // @ts-ignore + if (self.isPaused) { removeClass(element, pausedClass); - - if (!isAnimating) { - // @ts-ignore - clearInterval(self.timer); - // @ts-ignore - self.timer = null; - self.cycle(); - } + self.cycle(); } } /** * Handles the `click` event for the `Carousel` indicators. * - * @param {Event} e the `Event` object + * @this {HTMLElement} + * @param {MouseEvent} e the `Event` object */ function carouselIndicatorHandler(e) { e.preventDefault(); - const { target } = e; - // @ts-ignore - const self = getCarouselInstance(target.closest(carouselSelector)); - // @ts-ignore - if (self.isAnimating) return; + const indicator = this; + const element = closest(indicator, carouselSelector) || getTargetElement(indicator); + if (!element) return; + const self = getCarouselInstance(element); - // @ts-ignore - const newIndex = target.getAttribute(dataBsSlideTo); + if (!self || self.isAnimating) return; // @ts-ignore - if (target && !hasClass(target, activeClass) // event target is not active - && newIndex) { // AND has the specific attribute - self.to(+newIndex); // do the slide + const newIndex = +getAttribute(indicator, dataBsSlideTo); + + if (indicator && !hasClass(indicator, activeClass) // event target is not active + && !Number.isNaN(newIndex)) { // AND has the specific attribute + self.to(newIndex); // do the slide } } /** * Handles the `click` event for the `Carousel` arrows. * - * @this {Element} - * @param {Event} e the `Event` object + * @this {HTMLElement} + * @param {MouseEvent} e the `Event` object */ function carouselControlsHandler(e) { e.preventDefault(); - const that = this; - // @ts-ignore - const self = getCarouselInstance(that.closest(carouselSelector)); + const control = this; + const element = closest(control, carouselSelector) || getTargetElement(control); + const self = element && getCarouselInstance(element); + if (!self || self.isAnimating) return; + const orientation = getAttribute(control, dataBsSlide); - // @ts-ignore - const { controls } = self; - - if (controls[1] && that === controls[1]) { + if (orientation === 'next') { self.next(); - } else if (controls[1] && that === controls[0]) { + } else if (orientation === 'prev') { self.prev(); } } @@ -922,23 +1212,20 @@ function carouselControlsHandler(e) { /** * Handles the keyboard `keydown` event for the visible `Carousel` elements. * - * @param {{which: number}} e the `Event` object + * @param {KeyboardEvent} e the `Event` object */ -function carouselKeyHandler({ which }) { - const [element] = Array.from(document.querySelectorAll(carouselSelector)) +function carouselKeyHandler({ code }) { + const [element] = [...querySelectorAll(carouselSelector)] .filter((x) => isElementInScrollRange(x)); - if (!element) return; const self = getCarouselInstance(element); + if (!self) return; + const RTL = isRTL(); + const arrowKeyNext = !RTL ? keyArrowRight : keyArrowLeft; + const arrowKeyPrev = !RTL ? keyArrowLeft : keyArrowRight; - switch (which) { - case 39: - self.next(); - break; - case 37: - self.prev(); - break; - } + if (code === arrowKeyPrev) self.prev(); + else if (code === arrowKeyNext) self.next(); } // CAROUSEL TOUCH HANDLERS @@ -946,22 +1233,19 @@ function carouselKeyHandler({ which }) { /** * Handles the `touchdown` event for the `Carousel` element. * - * @this {Element} - * @param {Event} e the `Event` object + * @this {HTMLElement | Element} + * @param {TouchEvent} e the `Event` object */ function carouselTouchDownHandler(e) { const element = this; const self = getCarouselInstance(element); - // @ts-ignore if (!self || self.isTouch) { return; } - // @ts-ignore startX = e.changedTouches[0].pageX; // @ts-ignore if (element.contains(e.target)) { - // @ts-ignore self.isTouch = true; toggleCarouselTouchHandlers(self, true); } @@ -970,21 +1254,19 @@ function carouselTouchDownHandler(e) { /** * Handles the `touchmove` event for the `Carousel` element. * - * @this {Element} - * @param {Event} e the `Event` object + * @this {HTMLElement | Element} + * @param {TouchEvent} e */ function carouselTouchMoveHandler(e) { - // @ts-ignore const { changedTouches, type } = e; const self = getCarouselInstance(this); - // @ts-ignore if (!self || !self.isTouch) { return; } currentX = changedTouches[0].pageX; // cancel touch if more than one changedTouches detected - if (type === 'touchmove' && changedTouches.length > 1) { + if (type === touchmoveEvent && changedTouches.length > 1) { e.preventDefault(); } } @@ -992,20 +1274,18 @@ function carouselTouchMoveHandler(e) { /** * Handles the `touchend` event for the `Carousel` element. * - * @this {Element} - * @param {Event} e the `Event` object + * @this {HTMLElement | Element} + + * @param {TouchEvent} e */ function carouselTouchEndHandler(e) { const element = this; const self = getCarouselInstance(element); - // @ts-ignore if (!self || !self.isTouch) { return; } - // @ts-ignore endX = currentX || e.changedTouches[0].pageX; - // @ts-ignore if (self.isTouch) { // the event target is outside the carousel OR carousel doens't include the related target // @ts-ignore @@ -1015,16 +1295,12 @@ function carouselTouchEndHandler(e) { return; } // OR determine next index to slide to if (currentX < startX) { - // @ts-ignore self.index += 1; } else if (currentX > startX) { - // @ts-ignore self.index -= 1; } - // @ts-ignore self.isTouch = false; - // @ts-ignore self.to(self.index); // do the slide toggleCarouselTouchHandlers(self); // remove touch events handlers @@ -1039,10 +1315,9 @@ function carouselTouchEndHandler(e) { * @param {number} pageIndex the index of the new active indicator */ function activateCarouselIndicator(self, pageIndex) { - // @ts-ignore const { indicators } = self; - Array.from(indicators).forEach((x) => removeClass(x, activeClass)); - // @ts-ignore + [...indicators].forEach((x) => removeClass(x, activeClass)); + if (self.indicators[pageIndex]) addClass(indicators[pageIndex], activeClass); } @@ -1053,11 +1328,9 @@ function activateCarouselIndicator(self, pageIndex) { */ function toggleCarouselTouchHandlers(self, add) { const { element } = self; - const action = add ? addEventListener : removeEventListener; - // @ts-ignore - element[action]('touchmove', carouselTouchMoveHandler, passiveHandler); - // @ts-ignore - element[action]('touchend', carouselTouchEndHandler, passiveHandler); + const action = add ? on : off; + action(element, touchmoveEvent, carouselTouchMoveHandler, passiveHandler); + action(element, touchendEvent, carouselTouchEndHandler, passiveHandler); } /** @@ -1067,39 +1340,37 @@ function toggleCarouselTouchHandlers(self, add) { */ function toggleCarouselHandlers(self, add) { const { - // @ts-ignore - element, options, slides, controls, indicator, + element, options, slides, controls, indicators, } = self; const { touch, pause, interval, keyboard, } = options; - const action = add ? addEventListener : removeEventListener; + const action = add ? on : off; if (pause && interval) { - // @ts-ignore - element[action]('mouseenter', carouselPauseHandler); - // @ts-ignore - element[action]('mouseleave', carouselResumeHandler); - // @ts-ignore - element[action]('touchstart', carouselPauseHandler, passiveHandler); - // @ts-ignore - element[action]('touchend', carouselResumeHandler, passiveHandler); + action(element, mouseenterEvent, carouselPauseHandler); + action(element, mouseleaveEvent, carouselResumeHandler); + action(element, touchstartEvent, carouselPauseHandler, passiveHandler); + action(element, touchendEvent, carouselResumeHandler, passiveHandler); } if (touch && slides.length > 1) { - // @ts-ignore - element[action]('touchstart', carouselTouchDownHandler, passiveHandler); + action(element, touchstartEvent, carouselTouchDownHandler, passiveHandler); } - controls.forEach((arrow) => { - // @ts-ignore - if (arrow) arrow[action]('click', carouselControlsHandler); - }); + if (controls.length) { + controls.forEach((arrow) => { + if (arrow) action(arrow, mouseclickEvent, carouselControlsHandler); + }); + } + if (indicators.length) { + indicators.forEach((indicator) => { + action(indicator, mouseclickEvent, carouselIndicatorHandler); + }); + } // @ts-ignore - if (indicator) indicator[action]('click', carouselIndicatorHandler); - // @ts-ignore - if (keyboard) window[action]('keydown', carouselKeyHandler); + if (keyboard) action(getWindow(element), keydownEvent, carouselKeyHandler); } /** @@ -1108,10 +1379,10 @@ function toggleCarouselHandlers(self, add) { * @returns {number} the query result */ function getActiveIndex(self) { - // @ts-ignore const { slides, element } = self; - return Array.from(slides) - .indexOf(element.getElementsByClassName(`${carouselItem} ${activeClass}`)[0]) || 0; + const activeItem = querySelector(`.${carouselItem}.${activeClass}`, element); + // @ts-ignore + return [...slides].indexOf(activeItem); } // CAROUSEL DEFINITION @@ -1119,7 +1390,7 @@ function getActiveIndex(self) { /** Creates a new `Carousel` instance. */ class Carousel extends BaseComponent { /** - * @param {Element | string} target mostly a `.carousel` element + * @param {HTMLElement | Element | string} target mostly a `.carousel` element * @param {BSN.Options.Carousel=} config instance options */ constructor(target, config) { @@ -1128,42 +1399,38 @@ class Carousel extends BaseComponent { const self = this; // additional properties - /** @private @type {any?} */ - self.timer = null; - /** @private @type {string} */ - self.direction = 'left'; - /** @private @type {boolean} */ - self.isPaused = false; - /** @private @type {boolean} */ - self.isAnimating = false; - /** @private @type {number} */ + /** @type {string} */ + self.direction = isRTL() ? 'right' : 'left'; + /** @type {number} */ self.index = 0; - /** @private @type {boolean} */ + /** @type {boolean} */ self.isTouch = false; // initialization element const { element } = self; // carousel elements // a LIVE collection is prefferable - /** @private @type {HTMLCollection} */ - self.slides = element.getElementsByClassName(carouselItem); + self.slides = getElementsByClassName(carouselItem, element); const { slides } = self; // invalidate when not enough items // no need to go further if (slides.length < 2) { return; } - /** @private @type {[?Element, ?Element]} */ self.controls = [ - queryElement(`.${carouselControl}-prev`, element), - queryElement(`.${carouselControl}-next`, element), + ...querySelectorAll(`[${dataBsSlide}]`, element), + ...querySelectorAll(`[${dataBsSlide}][${dataBsTarget}="#${element.id}"]`), ]; + /** @type {(HTMLElement | Element)?} */ + self.indicator = querySelector(`.${carouselString}-indicators`, element); + // a LIVE collection is prefferable - /** @private @type {Element?} */ - self.indicator = queryElement('.carousel-indicators', element); - /** @private @type {NodeList | any[]} */ - self.indicators = (self.indicator && self.indicator.querySelectorAll(`[${dataBsSlideTo}]`)) || []; + /** @type {(HTMLElement | Element)[]} */ + self.indicators = [ + ...(self.indicator ? querySelectorAll(`[${dataBsSlideTo}]`, self.indicator) : []), + ...querySelectorAll(`[${dataBsSlideTo}][${dataBsTarget}="#${element.id}"]`), + ]; // set JavaScript and DATA API options const { options } = self; @@ -1199,39 +1466,45 @@ class Carousel extends BaseComponent { get defaults() { return carouselDefaults; } /* eslint-enable */ + /** + * Check if instance is paused. + * @returns {boolean} + */ + get isPaused() { + return hasClass(this.element, pausedClass); + } + + /** + * Check if instance is animating. + * @returns {boolean} + */ + get isAnimating() { + return querySelector(`.${carouselItem}-next,.${carouselItem}-prev`, this.element) !== null; + } + // CAROUSEL PUBLIC METHODS // ======================= /** Slide automatically through items. */ cycle() { const self = this; - const { isPaused, element, options } = self; - if (self.timer) { - clearInterval(self.timer); - self.timer = null; - } + const { element, options } = self; - if (isPaused) { - removeClass(element, pausedClass); - self.isPaused = !isPaused; - } + Timer.clear(element, carouselString); - self.timer = setInterval(() => { - if (isElementInScrollRange(element)) { + Timer.set(element, () => { + if (!self.isPaused && isElementInScrollRange(element)) { self.index += 1; self.to(self.index); } - }, options.interval); + }, options.interval, carouselString); } /** Pause the automatic cycle. */ pause() { const self = this; - const { element, options, isPaused } = self; - if (options.interval && !isPaused) { - clearInterval(self.timer); - self.timer = null; + const { element, options } = self; + if (!self.isPaused && options.interval) { addClass(element, pausedClass); - self.isPaused = !isPaused; } } @@ -1254,20 +1527,21 @@ class Carousel extends BaseComponent { to(idx) { const self = this; const { - element, isAnimating, slides, options, + element, slides, options, } = self; const activeItem = getActiveIndex(self); + const RTL = isRTL(); let next = idx; // when controled via methods, make sure to check again // first return if we're on the same item #227 - if (isAnimating || activeItem === next) return; + if (self.isAnimating || activeItem === next) return; // determine transition direction if ((activeItem < next) || (activeItem === 0 && next === slides.length - 1)) { - self.direction = 'left'; // next + self.direction = RTL ? 'right' : 'left'; // next } else if ((activeItem > next) || (activeItem === slides.length - 1 && next === 0)) { - self.direction = 'right'; // prev + self.direction = RTL ? 'left' : 'right'; // prev } const { direction } = self; @@ -1286,43 +1560,39 @@ class Carousel extends BaseComponent { }; // update event properties - Object.assign(carouselSlideEvent, eventProperties); - Object.assign(carouselSlidEvent, eventProperties); + ObjectAssign(carouselSlideEvent, eventProperties); + ObjectAssign(carouselSlidEvent, eventProperties); // discontinue when prevented - element.dispatchEvent(carouselSlideEvent); + dispatchEvent(element, carouselSlideEvent); if (carouselSlideEvent.defaultPrevented) return; // update index self.index = next; - - clearInterval(self.timer); - self.timer = null; - - self.isAnimating = true; activateCarouselIndicator(self, next); if (getElementTransitionDuration(slides[next]) && hasClass(element, 'slide')) { - addClass(slides[next], `${carouselItem}-${orientation}`); - reflow(slides[next]); - addClass(slides[next], `${carouselItem}-${directionClass}`); - addClass(slides[activeItem], `${carouselItem}-${directionClass}`); - - emulateTransitionEnd(slides[next], () => carouselTransitionEndHandler(self)); + Timer.set(element, () => { + addClass(slides[next], `${carouselItem}-${orientation}`); + reflow(slides[next]); + addClass(slides[next], `${carouselItem}-${directionClass}`); + addClass(slides[activeItem], `${carouselItem}-${directionClass}`); + + emulateTransitionEnd(slides[next], () => carouselTransitionEndHandler(self)); + }, 17, dataBsSlide); } else { addClass(slides[next], activeClass); removeClass(slides[activeItem], activeClass); - setTimeout(() => { - self.isAnimating = false; - + Timer.set(element, () => { + Timer.clear(element, dataBsSlide); // check for element, might have been disposed - if (element && options.interval && !hasClass(element, pausedClass)) { + if (element && options.interval && !self.isPaused) { self.cycle(); } - element.dispatchEvent(carouselSlidEvent); - }, 17); + dispatchEvent(element, carouselSlidEvent); + }, 17, dataBsSlide); } } @@ -1332,18 +1602,17 @@ class Carousel extends BaseComponent { const { slides } = self; const itemClasses = ['start', 'end', 'prev', 'next']; - Array.from(slides).forEach((slide, idx) => { + [...slides].forEach((slide, idx) => { if (hasClass(slide, activeClass)) activateCarouselIndicator(self, idx); itemClasses.forEach((c) => removeClass(slide, `${carouselItem}-${c}`)); }); toggleCarouselHandlers(self); - clearInterval(self.timer); super.dispose(); } } -Object.assign(Carousel, { +ObjectAssign(Carousel, { selector: carouselSelector, init: carouselInitCallback, getInstance: getCarouselInstance, @@ -1361,36 +1630,6 @@ const ariaExpanded = 'aria-expanded'; */ const collapsingClass = 'collapsing'; -/** - * Global namespace for most components `target` option. - */ -const dataBsTarget = 'data-bs-target'; - -/** - * Global namespace for most components `parent` option. - */ -const dataBsParent = 'data-bs-parent'; - -/** - * Global namespace for most components `container` option. - */ -const dataBsContainer = 'data-bs-container'; - -// @ts-nocheck - -/** - * Returns the `Element` that THIS one targets - * via `data-bs-target`, `href`, `data-bs-parent` or `data-bs-container`. - * - * @param {Element} element the target element - * @returns {Element?} the query result - */ -function getTargetElement(element) { - return queryElement(element.getAttribute(dataBsTarget) || element.getAttribute('href')) - || element.closest(element.getAttribute(dataBsParent)) - || queryElement(element.getAttribute(dataBsContainer)); -} - /* Native JavaScript for Bootstrap 5 | Collapse ----------------------------------------------- */ @@ -1431,17 +1670,14 @@ const hiddenCollapseEvent = bootstrapCustomEvent(`hidden.bs.${collapseString}`); */ function expandCollapse(self) { const { - // @ts-ignore element, parent, triggers, } = self; - element.dispatchEvent(showCollapseEvent); + dispatchEvent(element, showCollapseEvent); if (showCollapseEvent.defaultPrevented) return; - // @ts-ignore - self.isAnimating = true; - // @ts-ignore - if (parent) parent.isAnimating = true; + Timer.set(element, () => {}, 17); + if (parent) Timer.set(parent, () => {}, 17); addClass(element, collapsingClass); removeClass(element, collapseString); @@ -1450,12 +1686,10 @@ function expandCollapse(self) { element.style.height = `${element.scrollHeight}px`; emulateTransitionEnd(element, () => { - // @ts-ignore - self.isAnimating = false; - // @ts-ignore - if (parent) parent.isAnimating = false; + Timer.clear(element); + if (parent) Timer.clear(parent); - triggers.forEach((btn) => btn.setAttribute(ariaExpanded, 'true')); + triggers.forEach((btn) => setAttribute(btn, ariaExpanded, 'true')); removeClass(element, collapsingClass); addClass(element, collapseString); @@ -1464,7 +1698,7 @@ function expandCollapse(self) { // @ts-ignore element.style.height = ''; - element.dispatchEvent(shownCollapseEvent); + dispatchEvent(element, shownCollapseEvent); }); } @@ -1478,14 +1712,12 @@ function collapseContent(self) { element, parent, triggers, } = self; - element.dispatchEvent(hideCollapseEvent); + dispatchEvent(element, hideCollapseEvent); if (hideCollapseEvent.defaultPrevented) return; - // @ts-ignore - self.isAnimating = true; - // @ts-ignore - if (parent) parent.isAnimating = true; + Timer.set(element, () => {}, 17); + if (parent) Timer.set(parent, () => {}, 17); // @ts-ignore element.style.height = `${element.scrollHeight}px`; @@ -1499,12 +1731,10 @@ function collapseContent(self) { element.style.height = '0px'; emulateTransitionEnd(element, () => { - // @ts-ignore - self.isAnimating = false; - // @ts-ignore - if (parent) parent.isAnimating = false; + Timer.clear(element); + if (parent) Timer.clear(parent); - triggers.forEach((btn) => btn.setAttribute(ariaExpanded, 'false')); + triggers.forEach((btn) => setAttribute(btn, ariaExpanded, 'false')); removeClass(element, collapsingClass); addClass(element, collapseString); @@ -1512,7 +1742,7 @@ function collapseContent(self) { // @ts-ignore element.style.height = ''; - element.dispatchEvent(hiddenCollapseEvent); + dispatchEvent(element, hiddenCollapseEvent); }); } @@ -1522,13 +1752,11 @@ function collapseContent(self) { * @param {boolean=} add when `true`, the event listener is added */ function toggleCollapseHandler(self, add) { - const action = add ? addEventListener : removeEventListener; - // @ts-ignore + const action = add ? on : off; const { triggers } = self; if (triggers.length) { - // @ts-ignore - triggers.forEach((btn) => btn[action]('click', collapseClickHandler)); + triggers.forEach((btn) => action(btn, mouseclickEvent, collapseClickHandler)); } } @@ -1536,13 +1764,12 @@ function toggleCollapseHandler(self, add) { // ====================== /** * Handles the `click` event for the `Collapse` instance. - * @param {Event} e the `Event` object + * @param {MouseEvent} e the `Event` object */ function collapseClickHandler(e) { - const { target } = e; - // @ts-ignore - const trigger = target.closest(collapseToggleSelector); - const element = getTargetElement(trigger); + const { target } = e; // @ts-ignore - our target is `HTMLElement` + const trigger = target && closest(target, collapseToggleSelector); + const element = trigger && getTargetElement(trigger); const self = element && getCollapseInstance(element); if (self) self.toggle(); @@ -1556,7 +1783,7 @@ function collapseClickHandler(e) { /** Returns a new `Colapse` instance. */ class Collapse extends BaseComponent { /** - * @param {Element | string} target and `Element` that matches the selector + * @param {HTMLElement | Element | string} target and `Element` that matches the selector * @param {BSN.Options.Collapse=} config instance options */ constructor(target, config) { @@ -1568,20 +1795,13 @@ class Collapse extends BaseComponent { const { element, options } = self; // set triggering elements - /** @private @type {Element[]} */ - self.triggers = Array.from(document.querySelectorAll(collapseToggleSelector)) + /** @type {(HTMLElement | Element)[]} */ + self.triggers = [...querySelectorAll(collapseToggleSelector)] .filter((btn) => getTargetElement(btn) === element); // set parent accordion - /** @private @type {Element?} */ - self.parent = queryElement(options.parent); - const { parent } = self; - - // set initial state - /** @private @type {boolean} */ - self.isAnimating = false; - // @ts-ignore - if (parent) parent.isAnimating = false; + /** @type {(HTMLElement | Element)?} */ + self.parent = querySelector(options.parent); // add event listeners toggleCollapseHandler(self, true); @@ -1612,8 +1832,8 @@ class Collapse extends BaseComponent { /** Hides the collapse. */ hide() { const self = this; - const { triggers, isAnimating } = self; - if (isAnimating) return; + const { triggers, element } = self; + if (Timer.get(element)) return; collapseContent(self); if (triggers.length) { @@ -1625,19 +1845,18 @@ class Collapse extends BaseComponent { show() { const self = this; const { - element, parent, triggers, isAnimating, + element, parent, triggers, } = self; let activeCollapse; let activeCollapseInstance; if (parent) { - activeCollapse = Array.from(parent.querySelectorAll(`.${collapseString}.${showClass}`)) + activeCollapse = [...querySelectorAll(`.${collapseString}.${showClass}`, parent)] .find((i) => getCollapseInstance(i)); activeCollapseInstance = activeCollapse && getCollapseInstance(activeCollapse); } - // @ts-ignore - if ((!parent || (parent && !parent.isAnimating)) && !isAnimating) { + if ((!parent || (parent && !Timer.get(parent))) && !Timer.get(element)) { if (activeCollapseInstance && activeCollapse !== element) { collapseContent(activeCollapseInstance); activeCollapseInstance.triggers.forEach((btn) => { @@ -1655,21 +1874,83 @@ class Collapse extends BaseComponent { /** Remove the `Collapse` component from the target `Element`. */ dispose() { const self = this; - const { parent } = self; toggleCollapseHandler(self); - // @ts-ignore - if (parent) delete parent.isAnimating; super.dispose(); } } -Object.assign(Collapse, { +ObjectAssign(Collapse, { selector: collapseSelector, init: collapseInitCallback, getInstance: getCollapseInstance, }); +/** + * A global namespace for `focus` event. + * @type {string} + */ +const focusEvent = 'focus'; + +/** + * A global namespace for `keyup` event. + * @type {string} + */ +const keyupEvent = 'keyup'; + +/** + * A global namespace for `scroll` event. + * @type {string} + */ +const scrollEvent = 'scroll'; + +/** + * A global namespace for `resize` event. + * @type {string} + */ +const resizeEvent = 'resize'; + +/** + * A global namespace for `ArrowUp` key. + * @type {string} e.which = 38 equivalent + */ +const keyArrowUp = 'ArrowUp'; + +/** + * A global namespace for `ArrowDown` key. + * @type {string} e.which = 40 equivalent + */ +const keyArrowDown = 'ArrowDown'; + +/** + * A global namespace for `Escape` key. + * @type {string} e.which = 27 equivalent + */ +const keyEscape = 'Escape'; + +/** + * Shortcut for multiple uses of `HTMLElement.style.propertyName` method. + * @param {HTMLElement | Element} element target element + * @param {Partial} styles attribute value + */ +// @ts-ignore +const setElementStyle = (element, styles) => { ObjectAssign(element.style, styles); }; + +/** + * Utility to focus an `HTMLElement` target. + * + * @param {HTMLElement | Element} element is the target + */ +// @ts-ignore -- `Element`s resulted from querySelector can focus too +const focus = (element) => element.focus(); + +/** + * Shortcut for `HTMLElement.hasAttribute()` method. + * @param {HTMLElement | Element} element target element + * @param {string} attribute attribute name + */ +const hasAttribute = (element, attribute) => element.hasAttribute(attribute); + /** * Global namespace for `Dropdown` types / classes. */ @@ -1684,23 +1965,16 @@ const dropdownMenuClass = 'dropdown-menu'; * Checks if an *event.target* or its parent has an `href="#"` value. * We need to prevent jumping around onclick, don't we? * - * @param {Element} elem the target element + * @param {HTMLElement | HTMLAnchorElement | EventTarget} element the target element * @returns {boolean} the query result */ -function isEmptyAnchor(elem) { - const parentAnchor = elem.closest('A'); - // anchor href starts with # - return elem && ((elem.hasAttribute('href') && elem.href.slice(-1) === '#') - // OR a child of an anchor with href starts with # - || (parentAnchor && parentAnchor.hasAttribute('href') && parentAnchor.href.slice(-1) === '#')); -} - -/** - * Points the focus to a specific element. - * @param {Element} element target - */ -function setFocus(element) { - element.focus(); +function isEmptyAnchor(element) { + // @ts-ignore -- `EventTarget` must be `HTMLElement` + const parentAnchor = closest(element, 'A'); + // @ts-ignore -- anchor href starts with # + return element && ((hasAttribute(element, 'href') && element.href.slice(-1) === '#') + // @ts-ignore -- OR a child of an anchor with href starts with # + || (parentAnchor && hasAttribute(parentAnchor, 'href') && parentAnchor.href.slice(-1) === '#')); } /* Native JavaScript for Bootstrap 5 | Dropdown @@ -1708,7 +1982,12 @@ function setFocus(element) { // DROPDOWN PRIVATE GC // =================== -const [dropdownString] = dropdownMenuClasses; +const [ + dropdownString, + dropupString, + dropstartString, + dropendString, +] = dropdownMenuClasses; const dropdownComponent = 'Dropdown'; const dropdownSelector = `[${dataBsToggle}="${dropdownString}"]`; @@ -1728,13 +2007,10 @@ const dropdownInitCallback = (element) => new Dropdown(element); // DROPDOWN PRIVATE GC // =================== -const dropupString = dropdownMenuClasses[1]; -const dropstartString = dropdownMenuClasses[2]; -const dropendString = dropdownMenuClasses[3]; const dropdownMenuEndClass = `${dropdownMenuClass}-end`; -const hideMenuClass = ['d-block', 'invisible']; const verticalClass = [dropdownString, dropupString]; const horizontalClass = [dropstartString, dropendString]; +const menuFocusTags = ['A', 'BUTTON']; const dropdownDefaults = { offset: 5, // [number] 5(px) @@ -1755,90 +2031,79 @@ const hiddenDropdownEvent = bootstrapCustomEvent(`hidden.bs.${dropdownString}`); * accomodate the layout and the page scroll. * * @param {Dropdown} self the `Dropdown` instance - * @param {boolean=} show when `true` will have a different effect */ -function styleDropdown(self, show) { +function styleDropdown(self) { const { - // @ts-ignore - element, menu, originalClass, menuEnd, options, + element, menu, parentElement, options, } = self; const { offset } = options; - const parent = element.parentElement; + + // don't apply any style on mobile view + if (getElementStyle(menu, 'position') === 'static') return; + + const RTL = isRTL(element); + const menuEnd = hasClass(parentElement, dropdownMenuEndClass); // reset menu offset and position const resetProps = ['margin', 'top', 'bottom', 'left', 'right']; // @ts-ignore resetProps.forEach((p) => { menu.style[p] = ''; }); - // @ts-ignore - removeClass(parent, 'position-static'); - - if (!show) { - const menuEndNow = hasClass(menu, dropdownMenuEndClass); - // @ts-ignore - parent.className = originalClass.join(' '); - if (menuEndNow && !menuEnd) removeClass(menu, dropdownMenuEndClass); - else if (!menuEndNow && menuEnd) addClass(menu, dropdownMenuEndClass); - return; - } // set initial position class // take into account .btn-group parent as .dropdown - let positionClass = dropdownMenuClasses.find((c) => originalClass.includes(c)) || dropdownString; + let positionClass = dropdownMenuClasses.find((c) => hasClass(parentElement, c)) || dropdownString; + /** @type {Record>} */ let dropdownMargin = { dropdown: [offset, 0, 0], dropup: [0, 0, offset], - dropstart: [-1, offset, 0], - dropend: [-1, 0, 0, offset], + dropstart: RTL ? [-1, 0, 0, offset] : [-1, offset, 0], + dropend: RTL ? [-1, offset, 0] : [-1, 0, 0, offset], }; + /** @type {Record>} */ const dropdownPosition = { dropdown: { top: '100%' }, dropup: { top: 'auto', bottom: '100%' }, - dropstart: { left: 'auto', right: '100%' }, - dropend: { left: '100%', right: 'auto' }, - menuEnd: { right: 0, left: 'auto' }, + dropstart: RTL ? { left: '100%', right: 'auto' } : { left: 'auto', right: '100%' }, + dropend: RTL ? { left: 'auto', right: '100%' } : { left: '100%', right: 'auto' }, + menuEnd: RTL ? { right: 'auto', left: 0 } : { right: 0, left: 'auto' }, }; - // force showing the menu to calculate its size - hideMenuClass.forEach((c) => addClass(menu, c)); - - const dropdownRegex = new RegExp(`\\b(${dropdownString}|${dropupString}|${dropstartString}|${dropendString})+`); // @ts-ignore - const elementDimensions = { w: element.offsetWidth, h: element.offsetHeight }; - // @ts-ignore - const menuDimensions = { w: menu.offsetWidth, h: menu.offsetHeight }; - const HTML = document.documentElement; - const BD = document.body; - const windowWidth = (HTML.clientWidth || BD.clientWidth); - const windowHeight = (HTML.clientHeight || BD.clientHeight); - const targetBCR = element.getBoundingClientRect(); - // dropdownMenuEnd && [ dropdown | dropup ] - const leftExceed = targetBCR.left + elementDimensions.w - menuDimensions.w < 0; - // dropstart - const leftFullExceed = targetBCR.left - menuDimensions.w < 0; - // !dropdownMenuEnd && [ dropdown | dropup ] - const rightExceed = targetBCR.left + menuDimensions.w >= windowWidth; + const { offsetWidth: menuWidth, offsetHeight: menuHeight } = menu; + + const { clientWidth, clientHeight } = getDocumentElement(element); + const { + left: targetLeft, top: targetTop, + width: targetWidth, height: targetHeight, + } = getBoundingClientRect(element); + + // dropstart | dropend + const leftFullExceed = targetLeft - menuWidth - offset < 0; // dropend - const rightFullExceed = targetBCR.left + menuDimensions.w + elementDimensions.w >= windowWidth; + const rightFullExceed = targetLeft + menuWidth + targetWidth + offset >= clientWidth; // dropstart | dropend - const bottomExceed = targetBCR.top + menuDimensions.h >= windowHeight; + const bottomExceed = targetTop + menuHeight + offset >= clientHeight; // dropdown - const bottomFullExceed = targetBCR.top + menuDimensions.h + elementDimensions.h >= windowHeight; + const bottomFullExceed = targetTop + menuHeight + targetHeight + offset >= clientHeight; // dropup - const topExceed = targetBCR.top - menuDimensions.h < 0; + const topExceed = targetTop - menuHeight - offset < 0; + // dropdown / dropup + const leftExceed = ((!RTL && menuEnd) || (RTL && !menuEnd)) + && targetLeft + targetWidth - menuWidth < 0; + const rightExceed = ((RTL && menuEnd) || (!RTL && !menuEnd)) + && targetLeft + menuWidth >= clientWidth; // recompute position + // handle RTL as well if (horizontalClass.includes(positionClass) && leftFullExceed && rightFullExceed) { positionClass = dropdownString; } - if (horizontalClass.includes(positionClass) && bottomExceed) { - positionClass = dropupString; - } - if (positionClass === dropstartString && leftFullExceed && !bottomExceed) { + if (positionClass === dropstartString && (!RTL ? leftFullExceed : rightFullExceed)) { positionClass = dropendString; } - if (positionClass === dropendString && rightFullExceed && !bottomExceed) { + if (positionClass === dropendString && (RTL ? leftFullExceed : rightFullExceed)) { positionClass = dropstartString; } if (positionClass === dropupString && topExceed && !bottomFullExceed) { @@ -1847,41 +2112,49 @@ function styleDropdown(self, show) { if (positionClass === dropdownString && bottomFullExceed && !topExceed) { positionClass = dropupString; } + // override position for horizontal classes + if (horizontalClass.includes(positionClass) && bottomExceed) { + ObjectAssign(dropdownPosition[positionClass], { + top: 'auto', bottom: 0, + }); + } + // override position for vertical classes + if (verticalClass.includes(positionClass) && (leftExceed || rightExceed)) { + // don't realign when menu is wider than window + // in both RTL and non-RTL readability is KING + if (targetLeft + targetWidth + Math.abs(menuWidth - targetWidth) + offset < clientWidth) { + ObjectAssign(dropdownPosition[positionClass], + leftExceed ? { left: 0, right: 'auto' } : { left: 'auto', right: 0 }); + } + } - // set spacing - // @ts-ignore dropdownMargin = dropdownMargin[positionClass]; // @ts-ignore menu.style.margin = `${dropdownMargin.map((x) => (x ? `${x}px` : x)).join(' ')}`; - // @ts-ignore - Object.keys(dropdownPosition[positionClass]).forEach((position) => { - // @ts-ignore - menu.style[position] = dropdownPosition[positionClass][position]; - }); - - // update dropdown position class - // @ts-ignore - if (!hasClass(parent, positionClass)) { - // @ts-ignore - parent.className = parent.className.replace(dropdownRegex, positionClass); - } - // update dropdown / dropup to handle parent btn-group element - // as well as the dropdown-menu-end utility class - if (verticalClass.includes(positionClass)) { - if (!menuEnd && rightExceed) addClass(menu, dropdownMenuEndClass); - else if (menuEnd && leftExceed) removeClass(menu, dropdownMenuEndClass); + setElementStyle(menu, dropdownPosition[positionClass]); - if (hasClass(menu, dropdownMenuEndClass)) { - Object.keys(dropdownPosition.menuEnd).forEach((p) => { - // @ts-ignore - menu.style[p] = dropdownPosition.menuEnd[p]; - }); - } + // update dropdown-menu-end + if (hasClass(menu, dropdownMenuEndClass)) { + setElementStyle(menu, dropdownPosition.menuEnd); } +} - // remove util classes from the menu, we have its size - hideMenuClass.forEach((c) => removeClass(menu, c)); +/** + * Returns an `Array` of focusable items in the given dropdown-menu. + * @param {HTMLElement | Element} menu + * @returns {(HTMLElement | Element)[]} + */ +function getMenuItems(menu) { + // @ts-ignore + return [...menu.children].map((c) => { + if (c && menuFocusTags.includes(c.tagName)) return c; + const { firstElementChild } = c; + if (firstElementChild && menuFocusTags.includes(firstElementChild.tagName)) { + return firstElementChild; + } + return null; + }).filter((c) => c); } /** @@ -1891,23 +2164,20 @@ function styleDropdown(self, show) { * @param {Dropdown} self the `Dropdown` instance */ function toggleDropdownDismiss(self) { - // @ts-ignore - const action = self.open ? addEventListener : removeEventListener; + const { element } = self; + const action = self.open ? on : off; + const doc = getDocument(element); - // @ts-ignore - document[action]('click', dropdownDismissHandler); - // @ts-ignore - document[action]('focus', dropdownDismissHandler); - // @ts-ignore - document[action]('keydown', dropdownPreventScroll); - // @ts-ignore - document[action]('keyup', dropdownKeyHandler); + action(doc, mouseclickEvent, dropdownDismissHandler); + action(doc, focusEvent, dropdownDismissHandler); + action(doc, keydownEvent, dropdownPreventScroll); + action(doc, keyupEvent, dropdownKeyHandler); if (self.options.display === 'dynamic') { - // @ts-ignore - window[action]('scroll', dropdownLayoutHandler, passiveHandler); - // @ts-ignore - window[action]('resize', dropdownLayoutHandler, passiveHandler); + [scrollEvent, resizeEvent].forEach((ev) => { + // @ts-ignore + action(getWindow(element), ev, dropdownLayoutHandler, passiveHandler); + }); } } @@ -1918,25 +2188,25 @@ function toggleDropdownDismiss(self) { * @param {boolean=} add when `true`, it will add the event listener */ function toggleDropdownHandler(self, add) { - const action = add ? addEventListener : removeEventListener; - // @ts-ignore - self.element[action]('click', dropdownClickHandler); + const action = add ? on : off; + action(self.element, mouseclickEvent, dropdownClickHandler); } /** * Returns the currently open `.dropdown` element. * - * @returns {Element?} the query result + * @param {(Document | HTMLElement | Element | globalThis)=} element target + * @returns {HTMLElement?} the query result */ -function getCurrentOpenDropdown() { +function getCurrentOpenDropdown(element) { const currentParent = [...dropdownMenuClasses, 'btn-group', 'input-group'] - .map((c) => document.getElementsByClassName(`${c} ${showClass}`)) + .map((c) => getElementsByClassName(`${c} ${showClass}`), getDocument(element)) .find((x) => x.length); if (currentParent && currentParent.length) { - // @ts-ignore - return Array.from(currentParent[0].children) - .find((x) => x.hasAttribute(dataBsToggle)); + // @ts-ignore -- HTMLElement is also Element + return [...currentParent[0].children] + .find((x) => hasAttribute(x, dataBsToggle)); } return null; } @@ -1946,33 +2216,35 @@ function getCurrentOpenDropdown() { /** * Handles the `click` event for the `Dropdown` instance. * - * @param {Event} e event object + * @param {MouseEvent} e event object + * @this {Document} */ function dropdownDismissHandler(e) { const { target, type } = e; // @ts-ignore - if (!target.closest) return; // some weird FF bug #409 + if (!target || !target.closest) return; // some weird FF bug #409 - const element = getCurrentOpenDropdown(); + // @ts-ignore + const element = getCurrentOpenDropdown(target); if (!element) return; const self = getDropdownInstance(element); - const parent = element.parentNode; - // @ts-ignore - const menu = self && self.menu; + if (!self) return; + + const { parentElement, menu } = self; // @ts-ignore - const hasData = target.closest(dropdownSelector) !== null; + const hasData = closest(target, dropdownSelector) !== null; // @ts-ignore - const isForm = parent && parent.contains(target) + const isForm = parentElement && parentElement.contains(target) // @ts-ignore - && (target.tagName === 'form' || target.closest('form') !== null); + && (target.tagName === 'form' || closest(target, 'form') !== null); // @ts-ignore - if (type === 'click' && isEmptyAnchor(target)) { + if (type === mouseclickEvent && isEmptyAnchor(target)) { e.preventDefault(); } - if (type === 'focus' // @ts-ignore + if (type === focusEvent // @ts-ignore && (target === element || target === menu || menu.contains(target))) { return; } @@ -1984,87 +2256,78 @@ function dropdownDismissHandler(e) { /** * Handles `click` event listener for `Dropdown`. - * @this {Element} - * @param {Event} e event object + * @this {HTMLElement | Element} + * @param {MouseEvent} e event object */ function dropdownClickHandler(e) { const element = this; + const { target } = e; const self = getDropdownInstance(element); - self.toggle(); - // @ts-ignore - if (isEmptyAnchor(e.target)) e.preventDefault(); + if (self) { + self.toggle(); + if (target && isEmptyAnchor(target)) e.preventDefault(); + } } /** * Prevents scroll when dropdown-menu is visible. - * @param {Event} e event object + * @param {KeyboardEvent} e event object */ function dropdownPreventScroll(e) { - // @ts-ignore - if (e.which === 38 || e.which === 40) e.preventDefault(); + if ([keyArrowDown, keyArrowUp].includes(e.code)) e.preventDefault(); } /** * Handles keyboard `keydown` events for `Dropdown`. - * @param {{which: number}} e keyboard key + * @param {KeyboardEvent} e keyboard key + * @this {Document} */ -function dropdownKeyHandler({ which }) { - const element = getCurrentOpenDropdown(); - // @ts-ignore - const self = getDropdownInstance(element); - // @ts-ignore - const { menu, menuItems, open } = self; - const activeItem = document.activeElement; - const isSameElement = activeItem === element; - const isInsideMenu = menu.contains(activeItem); - // @ts-ignore - const isMenuItem = activeItem.parentNode === menu || activeItem.parentNode.parentNode === menu; - - // @ts-ignore - let idx = menuItems.indexOf(activeItem); - - if (isMenuItem) { // navigate up | down - if (isSameElement) { +function dropdownKeyHandler(e) { + const { code } = e; + const element = getCurrentOpenDropdown(this); + const self = element && getDropdownInstance(element); + const activeItem = element && getDocument(element).activeElement; + if (!self || !activeItem) return; + const { menu, open } = self; + const menuItems = getMenuItems(menu); + + // arrow up & down + if (menuItems && menuItems.length) { + let idx = menuItems.indexOf(activeItem); + if (activeItem === element) { idx = 0; - } else if (which === 38) { + } else if (code === keyArrowUp) { idx = idx > 1 ? idx - 1 : 0; - } else if (which === 40) { + } else if (code === keyArrowDown) { idx = idx < menuItems.length - 1 ? idx + 1 : idx; } - - if (menuItems[idx]) setFocus(menuItems[idx]); + if (menuItems[idx]) focus(menuItems[idx]); } - if (((menuItems.length && isMenuItem) // menu has items - || (!menuItems.length && (isInsideMenu || isSameElement)) // menu might be a form - || !isInsideMenu) // or the focused element is not in the menu at all - && open && which === 27 // menu must be open - ) { + if (keyEscape === code && open) { self.toggle(); + focus(element); } } /** + * @this {globalThis} * @returns {void} */ function dropdownLayoutHandler() { - const element = getCurrentOpenDropdown(); + const element = getCurrentOpenDropdown(this); const self = element && getDropdownInstance(element); - // @ts-ignore - if (self && self.open) styleDropdown(self, true); + if (self && self.open) styleDropdown(self); } // DROPDOWN DEFINITION // =================== -/** - * Returns a new Dropdown instance. - * @implements {BaseComponent} - */ +/** Returns a new Dropdown instance. */ class Dropdown extends BaseComponent { /** - * @param {Element | string} target Element or string selector + * @param {HTMLElement | Element | string} target Element or string selector * @param {BSN.Options.Dropdown=} config the instance options */ constructor(target, config) { @@ -2074,32 +2337,18 @@ class Dropdown extends BaseComponent { // initialization element const { element } = self; + const { parentElement } = element; // set targets - const { parentElement } = element; - /** @private @type {Element} */ + /** @type {(Element | HTMLElement)} */ // @ts-ignore - self.menu = queryElement(`.${dropdownMenuClass}`, parentElement); - const { menu } = self; - - /** @private @type {string[]} */ + self.parentElement = parentElement; + /** @type {(Element | HTMLElement)} */ // @ts-ignore - self.originalClass = Array.from(parentElement.classList); - - // set original position - /** @private @type {boolean} */ - self.menuEnd = hasClass(menu, dropdownMenuEndClass); - - /** @private @type {Element[]} */ - self.menuItems = []; - - Array.from(menu.children).forEach((child) => { - if (child.children.length && (child.children[0].tagName === 'A')) self.menuItems.push(child.children[0]); - if (child.tagName === 'A') self.menuItems.push(child); - }); + self.menu = querySelector(`.${dropdownMenuClass}`, parentElement); // set initial state to closed - /** @private @type {boolean} */ + /** @type {boolean} */ self.open = false; // add event listener @@ -2132,99 +2381,201 @@ class Dropdown extends BaseComponent { /** Shows the dropdown menu to the user. */ show() { const self = this; - const currentParent = queryElement(dropdownMenuClasses.concat('btn-group', 'input-group').map((c) => `.${c}.${showClass}`).join(',')); - const currentElement = currentParent && queryElement(dropdownSelector, currentParent); - - if (currentElement) getDropdownInstance(currentElement).hide(); + const { + element, open, menu, parentElement, + } = self; - const { element, menu, open } = self; - const { parentElement } = element; + const currentElement = getCurrentOpenDropdown(element); + const currentInstance = currentElement && getDropdownInstance(currentElement); + if (currentInstance) currentInstance.hide(); // dispatch [showDropdownEvent, shownDropdownEvent].forEach((e) => { e.relatedTarget = element; }); - - // @ts-ignore - parentElement.dispatchEvent(showDropdownEvent); + dispatchEvent(parentElement, showDropdownEvent); if (showDropdownEvent.defaultPrevented) return; - // change menu position - styleDropdown(self, true); - addClass(menu, showClass); - // @ts-ignore addClass(parentElement, showClass); + setAttribute(element, ariaExpanded, 'true'); + + // change menu position + styleDropdown(self); - element.setAttribute(ariaExpanded, 'true'); self.open = !open; setTimeout(() => { - setFocus(element); // focus the element + focus(element); // focus the element toggleDropdownDismiss(self); - // @ts-ignore - parentElement.dispatchEvent(shownDropdownEvent); + dispatchEvent(parentElement, shownDropdownEvent); }, 1); } /** Hides the dropdown menu from the user. */ hide() { const self = this; - const { element, menu, open } = self; - const { parentElement } = element; - // @ts-ignore + const { + element, open, menu, parentElement, + } = self; [hideDropdownEvent, hiddenDropdownEvent].forEach((e) => { e.relatedTarget = element; }); - // @ts-ignore - parentElement.dispatchEvent(hideDropdownEvent); + dispatchEvent(parentElement, hideDropdownEvent); if (hideDropdownEvent.defaultPrevented) return; removeClass(menu, showClass); - // @ts-ignore removeClass(parentElement, showClass); + setAttribute(element, ariaExpanded, 'false'); - // revert to original position - styleDropdown(self); - - element.setAttribute(ariaExpanded, 'false'); self.open = !open; // only re-attach handler if the instance is not disposed setTimeout(() => toggleDropdownDismiss(self), 1); - // @ts-ignore - parentElement.dispatchEvent(hiddenDropdownEvent); + dispatchEvent(parentElement, hiddenDropdownEvent); } /** Removes the `Dropdown` component from the target element. */ dispose() { const self = this; - const { element } = self; + const { parentElement } = self; - // @ts-ignore - if (hasClass(element.parentNode, showClass) && self.open) self.hide(); + if (hasClass(parentElement, showClass) && self.open) self.hide(); toggleDropdownHandler(self); - super.dispose(); + super.dispose(); + } +} + +ObjectAssign(Dropdown, { + selector: dropdownSelector, + init: dropdownInitCallback, + getInstance: getDropdownInstance, +}); + +/** + * Shortcut for `HTMLElement.removeAttribute()` method. + * @param {HTMLElement | Element} element target element + * @param {string} attribute attribute name + */ +const removeAttribute = (element, attribute) => element.removeAttribute(attribute); + +/** + * Returns the `document.body` or the `` element. + * + * @param {(Node | HTMLElement | Element | globalThis)=} node + * @returns {HTMLElement | HTMLBodyElement} + */ +function getDocumentBody(node) { + return getDocument(node).body; +} + +/** + * A global namespace for aria-hidden. + * @type {string} + */ +const ariaHidden = 'aria-hidden'; + +/** + * A global namespace for aria-modal. + * @type {string} + */ +const ariaModal = 'aria-modal'; + +/** + * Check if target is a `ShadowRoot`. + * + * @param {any} element target + * @returns {boolean} the query result + */ +const isShadowRoot = (element) => { + const OwnElement = getWindow(element).ShadowRoot; + return element instanceof OwnElement || element instanceof ShadowRoot; +}; + +/** + * Returns the `parentNode` also going through `ShadowRoot`. + * @see https://github.com/floating-ui/floating-ui + * + * @param {Node | HTMLElement | Element} node the target node + * @returns {Node | HTMLElement | Element} the apropriate parent node + */ +function getParentNode(node) { + if (node.nodeName === 'HTML') { + return node; + } + + // this is a quicker (but less type safe) way to save quite some bytes from the bundle + return ( + // @ts-ignore + node.assignedSlot // step into the shadow DOM of the parent of a slotted node + || node.parentNode // @ts-ignore DOM Element detected + || (isShadowRoot(node) ? node.host : null) // ShadowRoot detected + || getDocumentElement(node) // fallback + ); +} + +/** + * Check if a target element is a ``, `
` or ``. + * @param {any} element the target element + * @returns {boolean} the query result + */ +const isTableElement = (element) => ['TABLE', 'TD', 'TH'].includes(element.tagName); + +/** + * Returns an `HTMLElement` to be used as default value for *options.container* + * for `Tooltip` / `Popover` components. + * + * When `getOffset` is *true*, it returns the `offsetParent` for tooltip/popover + * offsets computation similar to **floating-ui**. + * @see https://github.com/floating-ui/floating-ui + * + * @param {HTMLElement | Element} element the target + * @param {boolean=} getOffset when *true* it will return an `offsetParent` + * @returns {HTMLElement | HTMLBodyElement | Window} the query result + */ +function getElementContainer(element, getOffset) { + const majorBlockTags = ['HTML', 'BODY']; + + if (getOffset) { + /** @type {any} */ + let { offsetParent } = element; + + while (offsetParent && isTableElement(offsetParent) + && getElementStyle(offsetParent, 'position') === 'static' + && offsetParent instanceof HTMLElement + && getElementStyle(offsetParent, 'position') !== 'fixed') { + offsetParent = offsetParent.offsetParent; + } + + if (!offsetParent || (offsetParent + && (majorBlockTags.includes(offsetParent.tagName) + && getElementStyle(offsetParent, 'position') === 'static'))) { + offsetParent = getWindow(element); + } + return offsetParent; } -} -Object.assign(Dropdown, { - selector: dropdownSelector, - init: dropdownInitCallback, - getInstance: getDropdownInstance, -}); + /** @type {(HTMLElement)[]} */ + const containers = []; + /** @type {any} */ + let { parentNode } = element; -/** - * A global namespace for aria-hidden. - * @type {string} - */ -const ariaHidden = 'aria-hidden'; + while (parentNode && !majorBlockTags.includes(parentNode.nodeName)) { + parentNode = getParentNode(parentNode); + if (!(isShadowRoot(parentNode) || !!parentNode.shadowRoot + || isTableElement(parentNode))) { + containers.push(parentNode); + } + } -/** - * A global namespace for aria-modal. - * @type {string} - */ -const ariaModal = 'aria-modal'; + return containers.find((c, i) => { + if (getElementStyle(c, 'position') !== 'relative' + && containers.slice(i + 1).every((r) => getElementStyle(r, 'position') === 'static')) { + return c; + } + return null; + }) || getDocumentBody(element); +} /** * Global namespace for components `fixed-top` class. @@ -2241,28 +2592,40 @@ const fixedBottomClass = 'fixed-bottom'; */ const stickyTopClass = 'sticky-top'; -const fixedItems = [ - ...document.getElementsByClassName(fixedTopClass), - ...document.getElementsByClassName(fixedBottomClass), - ...document.getElementsByClassName(stickyTopClass), - ...document.getElementsByClassName('is-fixed'), +/** + * Global namespace for components `position-sticky` class. + */ +const positionStickyClass = 'position-sticky'; + +/** @param {(HTMLElement | Element | Document)=} parent */ +const getFixedItems = (parent) => [ + ...getElementsByClassName(fixedTopClass, parent), + ...getElementsByClassName(fixedBottomClass, parent), + ...getElementsByClassName(stickyTopClass, parent), + ...getElementsByClassName(positionStickyClass, parent), + ...getElementsByClassName('is-fixed', parent), ]; /** * Removes *padding* and *overflow* from the `` * and all spacing from fixed items. + * @param {(HTMLElement | Element)=} element the target modal/offcanvas */ -function resetScrollbar() { - const bd = document.body; - bd.style.paddingRight = ''; - bd.style.overflow = ''; +function resetScrollbar(element) { + const bd = getDocumentBody(element); + setElementStyle(bd, { + paddingRight: '', + overflow: '', + }); + + const fixedItems = getFixedItems(bd); if (fixedItems.length) { fixedItems.forEach((fixed) => { - // @ts-ignore - fixed.style.paddingRight = ''; - // @ts-ignore - fixed.style.marginRight = ''; + setElementStyle(fixed, { + paddingRight: '', + marginRight: '', + }); }); } } @@ -2270,39 +2633,42 @@ function resetScrollbar() { /** * Returns the scrollbar width if the body does overflow * the window. + * @param {(HTMLElement | Element)=} element * @returns {number} the value */ -function measureScrollbar() { - const { clientWidth } = document.documentElement; - return Math.abs(window.innerWidth - clientWidth); +function measureScrollbar(element) { + const { clientWidth } = getDocumentElement(element); + const { innerWidth } = getWindow(element); + return Math.abs(innerWidth - clientWidth); } /** * Sets the `` and fixed items style when modal / offcanvas * is shown to the user. * - * @param {number} scrollbarWidth the previously measured scrollbar width - * @param {boolean | number} overflow body does overflow or not + * @param {HTMLElement | Element} element the target modal/offcanvas + * @param {boolean=} overflow body does overflow or not */ -function setScrollbar(scrollbarWidth, overflow) { - const bd = document.body; - const bdStyle = getComputedStyle(bd); - const bodyPad = parseInt(bdStyle.paddingRight, 10); - const isOpen = bdStyle.overflow === 'hidden'; - const sbWidth = isOpen && bodyPad ? 0 : scrollbarWidth; +function setScrollbar(element, overflow) { + const bd = getDocumentBody(element); + const bodyPad = parseInt(getElementStyle(bd, 'paddingRight'), 10); + const isOpen = getElementStyle(bd, 'overflow') === 'hidden'; + const sbWidth = isOpen && bodyPad ? 0 : measureScrollbar(element); + const fixedItems = getFixedItems(bd); if (overflow) { - bd.style.overflow = 'hidden'; - bd.style.paddingRight = `${bodyPad + sbWidth}px`; + setElementStyle(bd, { + overflow: 'hidden', + paddingRight: `${bodyPad + sbWidth}px`, + }); if (fixedItems.length) { fixedItems.forEach((fixed) => { - const isSticky = hasClass(fixed, stickyTopClass); - const itemPadValue = getComputedStyle(fixed).paddingRight; + const itemPadValue = getElementStyle(fixed, 'paddingRight'); // @ts-ignore fixed.style.paddingRight = `${parseInt(itemPadValue, 10) + sbWidth}px`; - if (isSticky) { - const itemMValue = getComputedStyle(fixed).marginRight; + if ([stickyTopClass, positionStickyClass].some((c) => hasClass(fixed, c))) { + const itemMValue = getElementStyle(fixed, 'marginRight'); // @ts-ignore fixed.style.marginRight = `${parseInt(itemMValue, 10) - sbWidth}px`; } @@ -2315,14 +2681,17 @@ const modalBackdropClass = 'modal-backdrop'; const offcanvasBackdropClass = 'offcanvas-backdrop'; const modalActiveSelector = `.modal.${showClass}`; const offcanvasActiveSelector = `.offcanvas.${showClass}`; -const overlay = document.createElement('div'); + +// any document would suffice +const overlay = getDocument().createElement('div'); /** * Returns the current active modal / offcancas element. - * @returns {Element?} the requested element + * @param {(HTMLElement | Element)=} element the context element + * @returns {(HTMLElement | Element)?} the requested element */ -function getCurrentOpen() { - return queryElement(`${modalActiveSelector},${offcanvasActiveSelector}`); +function getCurrentOpen(element) { + return querySelector(`${modalActiveSelector},${offcanvasActiveSelector}`, getDocument(element)); } /** @@ -2339,12 +2708,13 @@ function toggleOverlayType(isModal) { /** * Append the overlay to DOM. + * @param {HTMLElement | Element} container * @param {boolean} hasFade * @param {boolean=} isModal */ -function appendOverlay(hasFade, isModal) { +function appendOverlay(container, hasFade, isModal) { toggleOverlayType(isModal); - document.body.append(overlay); + container.append(overlay); if (hasFade) addClass(overlay, fadeClass); } @@ -2365,17 +2735,23 @@ function hideOverlay() { /** * Removes the overlay from DOM. + * @param {(HTMLElement | Element)=} element */ -function removeOverlay() { - if (!getCurrentOpen()) { +function removeOverlay(element) { + if (!getCurrentOpen(element)) { removeClass(overlay, fadeClass); overlay.remove(); - resetScrollbar(); + resetScrollbar(element); } } +/** + * @param {HTMLElement | Element} element target + * @returns {boolean} + */ function isVisible(element) { - return getComputedStyle(element).visibility !== 'hidden' + return element && getElementStyle(element, 'visibility') !== 'hidden' + // @ts-ignore && element.offsetParent !== null; } @@ -2426,19 +2802,18 @@ const hiddenModalEvent = bootstrapCustomEvent(`hidden.bs.${modalString}`); * @param {Modal} self the `Modal` instance */ function setModalScrollbar(self) { - // @ts-ignore - const { element, scrollbarWidth } = self; - const bd = document.body; - const html = document.documentElement; - const bodyOverflow = html.clientHeight !== html.scrollHeight - || bd.clientHeight !== bd.scrollHeight; - const modalOverflow = element.clientHeight !== element.scrollHeight; + const { element } = self; + const scrollbarWidth = measureScrollbar(element); + const { clientHeight, scrollHeight } = getDocumentElement(element); + const { clientHeight: modalHeight, scrollHeight: modalScrollHeight } = element; + const modalOverflow = modalHeight !== modalScrollHeight; if (!modalOverflow && scrollbarWidth) { + const pad = isRTL(element) ? 'paddingLeft' : 'paddingRight'; // @ts-ignore - element.style.paddingRight = `${scrollbarWidth}px`; + element.style[pad] = `${scrollbarWidth}px`; } - setScrollbar(scrollbarWidth, (modalOverflow || bodyOverflow)); + setScrollbar(element, (modalOverflow || clientHeight !== scrollHeight)); } /** @@ -2448,13 +2823,12 @@ function setModalScrollbar(self) { * @param {boolean=} add when `true`, event listeners are added */ function toggleModalDismiss(self, add) { - const action = add ? addEventListener : removeEventListener; - // @ts-ignore - window[action]('resize', self.update, passiveHandler); - // @ts-ignore - self.element[action]('click', modalDismissHandler); + const action = add ? on : off; + const { element } = self; + action(element, mouseclickEvent, modalDismissHandler); // @ts-ignore - document[action]('keydown', modalKeyHandler); + action(getWindow(element), resizeEvent, self.update, passiveHandler); + action(getDocument(element), keydownEvent, modalKeyHandler); } /** @@ -2463,13 +2837,11 @@ function toggleModalDismiss(self, add) { * @param {boolean=} add when `true`, event listener is added */ function toggleModalHandler(self, add) { - const action = add ? addEventListener : removeEventListener; - // @ts-ignore + const action = add ? on : off; const { triggers } = self; if (triggers.length) { - // @ts-ignore - triggers.forEach((btn) => btn[action]('click', modalClickHandler)); + triggers.forEach((btn) => action(btn, mouseclickEvent, modalClickHandler)); } } @@ -2478,17 +2850,14 @@ function toggleModalHandler(self, add) { * @param {Modal} self the `Modal` instance */ function afterModalHide(self) { + const { triggers, element } = self; + removeOverlay(element); // @ts-ignore - const { triggers } = self; - removeOverlay(); - // @ts-ignore - self.element.style.paddingRight = ''; - // @ts-ignore - self.isAnimating = false; + element.style.paddingRight = ''; if (triggers.length) { const visibleTrigger = triggers.find((x) => isVisible(x)); - if (visibleTrigger) setFocus(visibleTrigger); + if (visibleTrigger) focus(visibleTrigger); } } @@ -2497,17 +2866,12 @@ function afterModalHide(self) { * @param {Modal} self the `Modal` instance */ function afterModalShow(self) { - // @ts-ignore const { element, relatedTarget } = self; - setFocus(element); - // @ts-ignore - self.isAnimating = false; - + focus(element); toggleModalDismiss(self, true); - // @ts-ignore shownModalEvent.relatedTarget = relatedTarget; - element.dispatchEvent(shownModalEvent); + dispatchEvent(element, shownModalEvent); } /** @@ -2515,19 +2879,18 @@ function afterModalShow(self) { * @param {Modal} self the `Modal` instance */ function beforeModalShow(self) { - // @ts-ignore const { element, hasFade } = self; // @ts-ignore element.style.display = 'block'; setModalScrollbar(self); - if (!getCurrentOpen()) { - document.body.style.overflow = 'hidden'; + if (!getCurrentOpen(element)) { + getDocumentBody(element).style.overflow = 'hidden'; } addClass(element, showClass); - element.removeAttribute(ariaHidden); - element.setAttribute(ariaModal, 'true'); + removeAttribute(element, ariaHidden); + setAttribute(element, ariaModal, 'true'); if (hasFade) emulateTransitionEnd(element, () => afterModalShow(self)); else afterModalShow(self); @@ -2540,7 +2903,6 @@ function beforeModalShow(self) { */ function beforeModalHide(self, force) { const { - // @ts-ignore element, options, relatedTarget, hasFade, } = self; @@ -2550,7 +2912,7 @@ function beforeModalHide(self, force) { // force can also be the transitionEvent object, we wanna make sure it's not // call is not forced and overlay is visible if (options.backdrop && !force && hasFade && hasClass(overlay, showClass) - && !getCurrentOpen()) { // AND no modal is visible + && !getCurrentOpen(element)) { // AND no modal is visible hideOverlay(); emulateTransitionEnd(overlay, () => afterModalHide(self)); } else { @@ -2560,31 +2922,27 @@ function beforeModalHide(self, force) { toggleModalDismiss(self); hiddenModalEvent.relatedTarget = relatedTarget; - element.dispatchEvent(hiddenModalEvent); + dispatchEvent(element, hiddenModalEvent); } // MODAL EVENT HANDLERS // ==================== /** * Handles the `click` event listener for modal. - * @param {Event} e the `Event` object + * @param {MouseEvent} e the `Event` object + * @this {HTMLElement | Element} */ function modalClickHandler(e) { const { target } = e; - // @ts-ignore - const trigger = target.closest(modalToggleSelector); - const element = getTargetElement(trigger); - const self = element && getModalInstance(element); - if (!self) return; - if (trigger.tagName === 'A') e.preventDefault(); + const trigger = target && closest(this, modalToggleSelector); + const element = trigger && getTargetElement(trigger); + const self = element && getModalInstance(element); - // @ts-ignore - if (self.isAnimating) return; + if (!self) return; - // @ts-ignore + if (trigger && trigger.tagName === 'A') e.preventDefault(); self.relatedTarget = trigger; - self.toggle(); } @@ -2592,19 +2950,15 @@ function modalClickHandler(e) { * Handles the `keydown` event listener for modal * to hide the modal when user type the `ESC` key. * - * @param {{which: number}} e the `Event` object + * @param {KeyboardEvent} e the `Event` object */ -function modalKeyHandler({ which }) { - const element = queryElement(modalActiveSelector); - // @ts-ignore - const self = getModalInstance(element); - // @ts-ignore - const { options, isAnimating } = self; - if (!isAnimating // modal has no animations running - && options.keyboard && which === 27 // the keyboard option is enabled and the key is 27 - // @ts-ignore +function modalKeyHandler({ code }) { + const element = querySelector(modalActiveSelector); + const self = element && getModalInstance(element); + if (!self) return; + const { options } = self; + if (options.keyboard && code === keyEscape // the keyboard option is enabled and the key is 27 && hasClass(element, showClass)) { // the modal is not visible - // @ts-ignore self.relatedTarget = null; self.hide(); } @@ -2613,35 +2967,34 @@ function modalKeyHandler({ which }) { /** * Handles the `click` event listeners that hide the modal. * - * @this {Element} - * @param {Event} e the `Event` object + * @this {HTMLElement | Element} + * @param {MouseEvent} e the `Event` object */ function modalDismissHandler(e) { const element = this; const self = getModalInstance(element); - // @ts-ignore - if (self.isAnimating) return; + // this timer is needed + if (!self || Timer.get(element)) return; - // @ts-ignore const { options, isStatic, modalDialog } = self; const { backdrop } = options; const { target } = e; + // @ts-ignore - const selectedText = document.getSelection().toString().length; + const selectedText = getDocument(element).getSelection().toString().length; // @ts-ignore const targetInsideDialog = modalDialog.contains(target); // @ts-ignore - const dismiss = target.closest(modalDismissSelector); + const dismiss = target && closest(target, modalDismissSelector); if (isStatic && !targetInsideDialog) { - addClass(element, modalStaticClass); - // @ts-ignore - self.isAnimating = true; - // @ts-ignore - emulateTransitionEnd(modalDialog, () => staticTransitionEnd(self)); + const dismissCallback = () => { + addClass(element, modalStaticClass); + emulateTransitionEnd(modalDialog, () => staticTransitionEnd(self)); + }; + Timer.set(element, dismissCallback, 17); } else if (dismiss || (!selectedText && !isStatic && !targetInsideDialog && backdrop)) { - // @ts-ignore self.relatedTarget = dismiss || null; self.hide(); e.preventDefault(); @@ -2654,12 +3007,11 @@ function modalDismissHandler(e) { * @param {Modal} self the `Modal` instance */ function staticTransitionEnd(self) { - // @ts-ignore - const duration = getElementTransitionDuration(self.modalDialog) + 17; - removeClass(self.element, modalStaticClass); + const { element, modalDialog } = self; + const duration = getElementTransitionDuration(modalDialog) + 17; + removeClass(element, modalStaticClass); // user must wait for zoom out transition - // @ts-ignore - setTimeout(() => { self.isAnimating = false; }, duration); + Timer.set(element, () => Timer.clear(element), duration); } // MODAL DEFINITION @@ -2667,7 +3019,7 @@ function staticTransitionEnd(self) { /** Returns a new `Modal` instance. */ class Modal extends BaseComponent { /** - * @param {Element | string} target usually the `.modal` element + * @param {HTMLElement | Element | string} target usually the `.modal` element * @param {BSN.Options.Modal=} config instance options */ constructor(target, config) { @@ -2680,25 +3032,25 @@ class Modal extends BaseComponent { const { element } = self; // the modal-dialog - /** @private @type {Element?} */ - self.modalDialog = queryElement(`.${modalString}-dialog`, element); + /** @type {(HTMLElement | Element)} */ + // @ts-ignore + self.modalDialog = querySelector(`.${modalString}-dialog`, element); // modal can have multiple triggering elements - /** @private @type {Element[]} */ - self.triggers = Array.from(document.querySelectorAll(modalToggleSelector)) + /** @type {(HTMLElement | Element)[]} */ + self.triggers = [...querySelectorAll(modalToggleSelector)] .filter((btn) => getTargetElement(btn) === element); // additional internals - /** @private @type {boolean} */ + /** @type {boolean} */ self.isStatic = self.options.backdrop === 'static'; - /** @private @type {boolean} */ + /** @type {boolean} */ self.hasFade = hasClass(element, fadeClass); - /** @private @type {boolean} */ - self.isAnimating = false; - /** @private @type {number} */ - self.scrollbarWidth = measureScrollbar(); - /** @private @type {Element?} */ + /** @type {(HTMLElement | Element)?} */ self.relatedTarget = null; + /** @type {HTMLBodyElement | HTMLElement | Element} */ + // @ts-ignore + self.container = getElementContainer(element); // attach event listeners toggleModalHandler(self, true); @@ -2733,31 +3085,28 @@ class Modal extends BaseComponent { show() { const self = this; const { - element, options, isAnimating, hasFade, relatedTarget, + element, options, hasFade, relatedTarget, container, } = self; const { backdrop } = options; let overlayDelay = 0; - if (hasClass(element, showClass) && !isAnimating) return; + if (hasClass(element, showClass)) return; - // @ts-ignore showModalEvent.relatedTarget = relatedTarget || null; - element.dispatchEvent(showModalEvent); + dispatchEvent(element, showModalEvent); if (showModalEvent.defaultPrevented) return; // we elegantly hide any opened modal/offcanvas - const currentOpen = getCurrentOpen(); + const currentOpen = getCurrentOpen(element); if (currentOpen && currentOpen !== element) { const this1 = getModalInstance(currentOpen); const that1 = this1 || getInstance(currentOpen, 'Offcanvas'); that1.hide(); } - self.isAnimating = true; - if (backdrop) { if (!currentOpen && !hasClass(overlay, showClass)) { - appendOverlay(hasFade, true); + appendOverlay(container, hasFade, true); } else { toggleOverlayType(true); } @@ -2780,19 +3129,17 @@ class Modal extends BaseComponent { hide(force) { const self = this; const { - element, isAnimating, hasFade, relatedTarget, + element, hasFade, relatedTarget, } = self; - if (!hasClass(element, showClass) && !isAnimating) return; - // @ts-ignore + if (!hasClass(element, showClass)) return; + hideModalEvent.relatedTarget = relatedTarget || null; - element.dispatchEvent(hideModalEvent); + dispatchEvent(element, hideModalEvent); if (hideModalEvent.defaultPrevented) return; - - self.isAnimating = true; removeClass(element, showClass); - element.setAttribute(ariaHidden, 'true'); - element.removeAttribute(ariaModal); + setAttribute(element, ariaHidden, 'true'); + removeAttribute(element, ariaModal); if (hasFade && force !== false) { emulateTransitionEnd(element, () => beforeModalHide(self)); @@ -2819,7 +3166,7 @@ class Modal extends BaseComponent { } } -Object.assign(Modal, { +ObjectAssign(Modal, { selector: modalSelector, init: modalInitCallback, getInstance: getModalInstance, @@ -2873,37 +3220,33 @@ const hiddenOffcanvasEvent = bootstrapCustomEvent(`hidden.bs.${offcanvasString}` * @param {Offcanvas} self the `Offcanvas` instance */ function setOffCanvasScrollbar(self) { - const bd = document.body; - const html = document.documentElement; - const bodyOverflow = html.clientHeight !== html.scrollHeight - || bd.clientHeight !== bd.scrollHeight; - // @ts-ignore - setScrollbar(self.scrollbarWidth, bodyOverflow); + const { element } = self; + const { clientHeight, scrollHeight } = getDocumentElement(element); + setScrollbar(element, clientHeight !== scrollHeight); } /** * Toggles on/off the `click` event listeners. * * @param {Offcanvas} self the `Offcanvas` instance - * @param {boolean=} add when `true`, listeners are added + * @param {boolean=} add when *true*, listeners are added */ function toggleOffcanvasEvents(self, add) { - const action = add ? addEventListener : removeEventListener; - // @ts-ignore - self.triggers.forEach((btn) => btn[action]('click', offcanvasTriggerHandler)); + const action = add ? on : off; + self.triggers.forEach((btn) => action(btn, mouseclickEvent, offcanvasTriggerHandler)); } /** * Toggles on/off the listeners of the events that close the offcanvas. * - * @param {boolean=} add the `Offcanvas` instance + * @param {Offcanvas} self the `Offcanvas` instance + * @param {boolean=} add when *true* listeners are added */ -function toggleOffCanvasDismiss(add) { - const action = add ? addEventListener : removeEventListener; - // @ts-ignore - document[action]('keydown', offcanvasKeyDismissHandler); - // @ts-ignore - document[action]('click', offcanvasDismissHandler); +function toggleOffCanvasDismiss(self, add) { + const action = add ? on : off; + const doc = getDocument(self.element); + action(doc, keydownEvent, offcanvasKeyDismissHandler); + action(doc, mouseclickEvent, offcanvasDismissHandler); } /** @@ -2915,8 +3258,8 @@ function beforeOffcanvasShow(self) { const { element, options } = self; if (!options.scroll) { - document.body.style.overflow = 'hidden'; setOffCanvasScrollbar(self); + getDocumentBody(element).style.overflow = 'hidden'; } addClass(element, offcanvasTogglingClass); @@ -2934,7 +3277,7 @@ function beforeOffcanvasShow(self) { */ function beforeOffcanvasHide(self) { const { element, options } = self; - const currentOpen = getCurrentOpen(); + const currentOpen = getCurrentOpen(element); // @ts-ignore element.blur(); @@ -2950,63 +3293,73 @@ function beforeOffcanvasHide(self) { /** * Handles the `click` event listeners. * - * @this {Element} - * @param {Event} e the `Event` object + * @this {HTMLElement | Element} + * @param {MouseEvent} e the `Event` object */ function offcanvasTriggerHandler(e) { - const trigger = this.closest(offcanvasToggleSelector); + const trigger = closest(this, offcanvasToggleSelector); const element = trigger && getTargetElement(trigger); const self = element && getOffcanvasInstance(element); - if (trigger && trigger.tagName === 'A') e.preventDefault(); if (self) { + self.relatedTarget = trigger; self.toggle(); + if (trigger && trigger.tagName === 'A') { + e.preventDefault(); + } } } /** * Handles the event listeners that close the offcanvas. * - * @param {Event} e the `Event` object + * @this {Document} + * @param {MouseEvent} e the `Event` object */ function offcanvasDismissHandler(e) { - const element = queryElement(offcanvasActiveSelector); + const element = querySelector(offcanvasActiveSelector, this); if (!element) return; - const offCanvasDismiss = queryElement(offcanvasDismissSelector, element); + const offCanvasDismiss = querySelector(offcanvasDismissSelector, element); const self = getOffcanvasInstance(element); + if (!self) return; - // @ts-ignore const { options, triggers } = self; const { target } = e; - // @ts-ignore - const trigger = target.closest(offcanvasToggleSelector); - - if (trigger && trigger.tagName === 'A') e.preventDefault(); + // @ts-ignore -- `EventTarget` is `HTMLElement` + const trigger = closest(target, offcanvasToggleSelector); + const selection = getDocument(element).getSelection(); - // @ts-ignore - if ((!element.contains(target) && options.backdrop + if (!(selection && selection.toString().length) + // @ts-ignore + && ((!element.contains(target) && options.backdrop && (!trigger || (trigger && !triggers.includes(trigger)))) // @ts-ignore - || (offCanvasDismiss && offCanvasDismiss.contains(target))) { + || (offCanvasDismiss && offCanvasDismiss.contains(target)))) { + // @ts-ignore + self.relatedTarget = offCanvasDismiss && offCanvasDismiss.contains(target) + ? offCanvasDismiss : null; self.hide(); } + if (trigger && trigger.tagName === 'A') e.preventDefault(); } /** * Handles the `keydown` event listener for offcanvas * to hide it when user type the `ESC` key. * - * @param {{which: number}} e the `Event` object + * @param {KeyboardEvent} e the `Event` object + * @this {Document} */ -function offcanvasKeyDismissHandler({ which }) { - const element = queryElement(offcanvasActiveSelector); +function offcanvasKeyDismissHandler({ code }) { + const element = querySelector(offcanvasActiveSelector, this); if (!element) return; const self = getOffcanvasInstance(element); - if (self && self.options.keyboard && which === 27) { + if (self && self.options.keyboard && code === keyEscape) { + self.relatedTarget = null; self.hide(); } } @@ -3017,24 +3370,21 @@ function offcanvasKeyDismissHandler({ which }) { * @param {Offcanvas} self the `Offcanvas` instance */ function showOffcanvasComplete(self) { - // @ts-ignore const { element, triggers } = self; removeClass(element, offcanvasTogglingClass); - element.removeAttribute(ariaHidden); - element.setAttribute(ariaModal, 'true'); - element.setAttribute('role', 'dialog'); - // @ts-ignore - self.isAnimating = false; + removeAttribute(element, ariaHidden); + setAttribute(element, ariaModal, 'true'); + setAttribute(element, 'role', 'dialog'); if (triggers.length) { - triggers.forEach((btn) => btn.setAttribute(ariaExpanded, 'true')); + triggers.forEach((btn) => setAttribute(btn, ariaExpanded, 'true')); } - element.dispatchEvent(shownOffcanvasEvent); + dispatchEvent(element, shownOffcanvasEvent); - toggleOffCanvasDismiss(true); - setFocus(element); + toggleOffCanvasDismiss(self, true); + focus(element); } /** @@ -3043,31 +3393,29 @@ function showOffcanvasComplete(self) { * @param {Offcanvas} self the `Offcanvas` instance */ function hideOffcanvasComplete(self) { - const { - // @ts-ignore - element, triggers, - } = self; + const { element, triggers } = self; - element.setAttribute(ariaHidden, 'true'); - element.removeAttribute(ariaModal); - element.removeAttribute('role'); + setAttribute(element, ariaHidden, 'true'); + removeAttribute(element, ariaModal); + removeAttribute(element, 'role'); // @ts-ignore element.style.visibility = ''; - // @ts-ignore - self.isAnimating = false; if (triggers.length) { - triggers.forEach((btn) => btn.setAttribute(ariaExpanded, 'false')); + triggers.forEach((btn) => setAttribute(btn, ariaExpanded, 'false')); const visibleTrigger = triggers.find((x) => isVisible(x)); - if (visibleTrigger) setFocus(visibleTrigger); + if (visibleTrigger) focus(visibleTrigger); } - removeOverlay(); + removeOverlay(element); - element.dispatchEvent(hiddenOffcanvasEvent); + dispatchEvent(element, hiddenOffcanvasEvent); removeClass(element, offcanvasTogglingClass); - toggleOffCanvasDismiss(); + // must check for open instances + if (!getCurrentOpen(element)) { + toggleOffCanvasDismiss(self); + } } // OFFCANVAS DEFINITION @@ -3075,7 +3423,7 @@ function hideOffcanvasComplete(self) { /** Returns a new `Offcanvas` instance. */ class Offcanvas extends BaseComponent { /** - * @param {Element | string} target usually an `.offcanvas` element + * @param {HTMLElement | Element | string} target usually an `.offcanvas` element * @param {BSN.Options.Offcanvas=} config instance options */ constructor(target, config) { @@ -3086,15 +3434,16 @@ class Offcanvas extends BaseComponent { const { element } = self; // all the triggering buttons - /** @private @type {Element[]} */ - self.triggers = Array.from(document.querySelectorAll(offcanvasToggleSelector)) + /** @type {(HTMLElement | Element)[]} */ + self.triggers = [...querySelectorAll(offcanvasToggleSelector)] .filter((btn) => getTargetElement(btn) === element); // additional instance property - /** @private @type {boolean} */ - self.isAnimating = false; - /** @private @type {number} */ - self.scrollbarWidth = measureScrollbar(); + /** @type {HTMLBodyElement | HTMLElement | Element} */ + // @ts-ignore + self.container = getElementContainer(element); + /** @type {(HTMLElement | Element)?} */ + self.relatedTarget = null; // attach event listeners toggleOffcanvasEvents(self, true); @@ -3126,35 +3475,32 @@ class Offcanvas extends BaseComponent { show() { const self = this; const { - element, options, isAnimating, + element, options, container, relatedTarget, } = self; let overlayDelay = 0; - if (hasClass(element, showClass) || isAnimating) return; - - element.dispatchEvent(showOffcanvasEvent); + if (hasClass(element, showClass)) return; + showOffcanvasEvent.relatedTarget = relatedTarget; + shownOffcanvasEvent.relatedTarget = relatedTarget; + dispatchEvent(element, showOffcanvasEvent); if (showOffcanvasEvent.defaultPrevented) return; // we elegantly hide any opened modal/offcanvas - const currentOpen = getCurrentOpen(); + const currentOpen = getCurrentOpen(element); if (currentOpen && currentOpen !== element) { const this1 = getOffcanvasInstance(currentOpen); const that1 = this1 || getInstance(currentOpen, 'Modal'); that1.hide(); } - self.isAnimating = true; - if (options.backdrop) { if (!currentOpen) { - appendOverlay(true); + appendOverlay(container, true); } else { toggleOverlayType(); } - overlayDelay = getElementTransitionDuration(overlay); - if (!hasClass(overlay, showClass)) showOverlay(); setTimeout(() => beforeOffcanvasShow(self), overlayDelay); @@ -3172,14 +3518,15 @@ class Offcanvas extends BaseComponent { */ hide(force) { const self = this; - const { element, isAnimating } = self; + const { element, relatedTarget } = self; - if (!hasClass(element, showClass) || isAnimating) return; + if (!hasClass(element, showClass)) return; - element.dispatchEvent(hideOffcanvasEvent); + hideOffcanvasEvent.relatedTarget = relatedTarget; + hiddenOffcanvasEvent.relatedTarget = relatedTarget; + dispatchEvent(element, hideOffcanvasEvent); if (hideOffcanvasEvent.defaultPrevented) return; - self.isAnimating = true; addClass(element, offcanvasTogglingClass); removeClass(element, showClass); @@ -3197,7 +3544,7 @@ class Offcanvas extends BaseComponent { } } -Object.assign(Offcanvas, { +ObjectAssign(Offcanvas, { selector: offcanvasSelector, init: offcanvasInitCallback, getInstance: getOffcanvasInstance, @@ -3210,89 +3557,244 @@ Object.assign(Offcanvas, { const ariaDescribedBy = 'aria-describedby'; /** - * Checks if an element is an ``, `` or `