aboutsummaryrefslogtreecommitdiff
path: root/js
diff options
context:
space:
mode:
authorBobby <[email protected]>2024-08-16 20:47:33 -0400
committerGitHub <[email protected]>2024-08-16 20:47:33 -0400
commit6b28433d9cfde435be8ec2bd6cf91e6324d08865 (patch)
tree8343c27b8b95ff5639233e81cf157f92e5688466 /js
parentd53094ec16ba385faae2973ddee648698b32ab24 (diff)
parent048f56f51460df75e92a2f7b472e1c56baeb68f7 (diff)
downloadbootstrap-main.tar.xz
bootstrap-main.zip
Merge branch 'twbs:main' into mainHEADmain
Diffstat (limited to 'js')
-rw-r--r--js/dist/alert.js189
-rw-r--r--js/dist/alert.js.map2
-rw-r--r--js/dist/base-component.js167
-rw-r--r--js/dist/base-component.js.map2
-rw-r--r--js/dist/button.js105
-rw-r--r--js/dist/button.js.map2
-rw-r--r--js/dist/carousel.js773
-rw-r--r--js/dist/carousel.js.map2
-rw-r--r--js/dist/collapse.js402
-rw-r--r--js/dist/collapse.js.map2
-rw-r--r--js/dist/dom/data.js22
-rw-r--r--js/dist/dom/data.js.map2
-rw-r--r--js/dist/dom/event-handler.js263
-rw-r--r--js/dist/dom/event-handler.js.map2
-rw-r--r--js/dist/dom/manipulator.js59
-rw-r--r--js/dist/dom/manipulator.js.map2
-rw-r--r--js/dist/dom/selector-engine.js118
-rw-r--r--js/dist/dom/selector-engine.js.map2
-rw-r--r--js/dist/dropdown.js553
-rw-r--r--js/dist/dropdown.js.map2
-rw-r--r--js/dist/modal.js952
-rw-r--r--js/dist/modal.js.map2
-rw-r--r--js/dist/offcanvas.js807
-rw-r--r--js/dist/offcanvas.js.map2
-rw-r--r--js/dist/popover.js172
-rw-r--r--js/dist/popover.js.map2
-rw-r--r--js/dist/scrollspy.js476
-rw-r--r--js/dist/scrollspy.js.map2
-rw-r--r--js/dist/tab.js442
-rw-r--r--js/dist/tab.js.map2
-rw-r--r--js/dist/toast.js328
-rw-r--r--js/dist/toast.js.map2
-rw-r--r--js/dist/tooltip.js1026
-rw-r--r--js/dist/tooltip.js.map2
-rw-r--r--js/dist/util/backdrop.js139
-rw-r--r--js/dist/util/backdrop.js.map1
-rw-r--r--js/dist/util/component-functions.js42
-rw-r--r--js/dist/util/component-functions.js.map1
-rw-r--r--js/dist/util/config.js68
-rw-r--r--js/dist/util/config.js.map1
-rw-r--r--js/dist/util/focustrap.js113
-rw-r--r--js/dist/util/focustrap.js.map1
-rw-r--r--js/dist/util/index.js281
-rw-r--r--js/dist/util/index.js.map1
-rw-r--r--js/dist/util/sanitizer.js114
-rw-r--r--js/dist/util/sanitizer.js.map1
-rw-r--r--js/dist/util/scrollbar.js113
-rw-r--r--js/dist/util/scrollbar.js.map1
-rw-r--r--js/dist/util/swipe.js135
-rw-r--r--js/dist/util/swipe.js.map1
-rw-r--r--js/dist/util/template-factory.js151
-rw-r--r--js/dist/util/template-factory.js.map1
-rw-r--r--js/index.esm.js26
-rw-r--r--js/index.umd.js26
-rw-r--r--js/src/alert.js10
-rw-r--r--js/src/base-component.js38
-rw-r--r--js/src/button.js8
-rw-r--r--js/src/carousel.js412
-rw-r--r--js/src/collapse.js77
-rw-r--r--js/src/dom/data.js2
-rw-r--r--js/src/dom/event-handler.js173
-rw-r--r--js/src/dom/manipulator.js44
-rw-r--r--js/src/dom/selector-engine.js68
-rw-r--r--js/src/dropdown.js221
-rw-r--r--js/src/modal.js152
-rw-r--r--js/src/offcanvas.js105
-rw-r--r--js/src/popover.js67
-rw-r--r--js/src/scrollspy.js304
-rw-r--r--js/src/tab.js295
-rw-r--r--js/src/toast.js62
-rw-r--r--js/src/tooltip.js482
-rw-r--r--js/src/util/backdrop.js55
-rw-r--r--js/src/util/component-functions.js9
-rw-r--r--js/src/util/config.js65
-rw-r--r--js/src/util/focustrap.js50
-rw-r--r--js/src/util/index.js149
-rw-r--r--js/src/util/sanitizer.js91
-rw-r--r--js/src/util/scrollbar.js61
-rw-r--r--js/src/util/swipe.js40
-rw-r--r--js/src/util/template-factory.js55
-rw-r--r--js/tests/README.md30
-rw-r--r--js/tests/browsers.js8
-rw-r--r--js/tests/helpers/fixture.js45
-rw-r--r--js/tests/integration/bundle-modularity.js2
-rw-r--r--js/tests/integration/rollup.bundle-modularity.js4
-rw-r--r--js/tests/integration/rollup.bundle.js2
-rw-r--r--js/tests/karma.conf.js46
-rw-r--r--js/tests/unit/.eslintrc.json13
-rw-r--r--js/tests/unit/alert.spec.js132
-rw-r--r--js/tests/unit/base-component.spec.js39
-rw-r--r--js/tests/unit/button.spec.js28
-rw-r--r--js/tests/unit/carousel.spec.js1474
-rw-r--r--js/tests/unit/collapse.spec.js1211
-rw-r--r--js/tests/unit/dom/data.spec.js15
-rw-r--r--js/tests/unit/dom/event-handler.spec.js579
-rw-r--r--js/tests/unit/dom/manipulator.spec.js101
-rw-r--r--js/tests/unit/dom/selector-engine.spec.js234
-rw-r--r--js/tests/unit/dropdown.spec.js3018
-rw-r--r--js/tests/unit/jquery.spec.js56
-rw-r--r--js/tests/unit/modal.spec.js1473
-rw-r--r--js/tests/unit/offcanvas.spec.js797
-rw-r--r--js/tests/unit/popover.spec.js329
-rw-r--r--js/tests/unit/scrollspy.spec.js1195
-rw-r--r--js/tests/unit/tab.spec.js1372
-rw-r--r--js/tests/unit/toast.spec.js696
-rw-r--r--js/tests/unit/tooltip.spec.js1478
-rw-r--r--js/tests/unit/util/backdrop.spec.js441
-rw-r--r--js/tests/unit/util/component-functions.spec.js40
-rw-r--r--js/tests/unit/util/config.spec.js166
-rw-r--r--js/tests/unit/util/focustrap.spec.js266
-rw-r--r--js/tests/unit/util/index.spec.js499
-rw-r--r--js/tests/unit/util/sanitizer.spec.js84
-rw-r--r--js/tests/unit/util/scrollbar.spec.js146
-rw-r--r--js/tests/unit/util/swipe.spec.js230
-rw-r--r--js/tests/unit/util/template-factory.spec.js59
-rw-r--r--js/tests/visual/alert.html6
-rw-r--r--js/tests/visual/button.html10
-rw-r--r--js/tests/visual/carousel.html19
-rw-r--r--js/tests/visual/collapse.html7
-rw-r--r--js/tests/visual/dropdown.html37
-rw-r--r--js/tests/visual/floating-label.html337
-rw-r--r--js/tests/visual/input.html78
-rw-r--r--js/tests/visual/modal.html109
-rw-r--r--js/tests/visual/popover.html16
-rw-r--r--js/tests/visual/scrollspy.html22
-rw-r--r--js/tests/visual/tab.html11
-rw-r--r--js/tests/visual/toast.html33
-rw-r--r--js/tests/visual/tooltip.html75
128 files changed, 14174 insertions, 14094 deletions
diff --git a/js/dist/alert.js b/js/dist/alert.js
index 3f73b68d2..d37e96e43 100644
--- a/js/dist/alert.js
+++ b/js/dist/alert.js
@@ -1,155 +1,24 @@
/*!
- * Bootstrap alert.js v5.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Bootstrap alert.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/event-handler.js'), require('./base-component.js')) :
- typeof define === 'function' && define.amd ? define(['./dom/event-handler', './base-component'], factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Alert = factory(global.EventHandler, global.Base));
-})(this, (function (EventHandler, BaseComponent) { 'use strict';
-
- const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
-
- const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
- const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
-
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/index.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
-
- const getSelector = element => {
- let selector = element.getAttribute('data-bs-target');
-
- if (!selector || selector === '#') {
- let hrefAttr = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes,
- // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
- // `document.querySelector` will rightfully complain it is invalid.
- // See https://github.com/twbs/bootstrap/issues/32273
-
- if (!hrefAttr || !hrefAttr.includes('#') && !hrefAttr.startsWith('.')) {
- return null;
- } // Just in case some CMS puts out a full URL with the anchor appended
-
-
- if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {
- hrefAttr = `#${hrefAttr.split('#')[1]}`;
- }
-
- selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null;
- }
-
- return selector;
- };
-
- const getElementFromSelector = element => {
- const selector = getSelector(element);
- return selector ? document.querySelector(selector) : null;
- };
-
- const isDisabled = element => {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return true;
- }
-
- if (element.classList.contains('disabled')) {
- return true;
- }
-
- if (typeof element.disabled !== 'undefined') {
- return element.disabled;
- }
-
- return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';
- };
-
- const getjQuery = () => {
- const {
- jQuery
- } = window;
-
- if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
- return jQuery;
- }
-
- return null;
- };
-
- const DOMContentLoadedCallbacks = [];
-
- const onDOMContentLoaded = callback => {
- if (document.readyState === 'loading') {
- // add listener on the first call when the document is in loading state
- if (!DOMContentLoadedCallbacks.length) {
- document.addEventListener('DOMContentLoaded', () => {
- DOMContentLoadedCallbacks.forEach(callback => callback());
- });
- }
-
- DOMContentLoadedCallbacks.push(callback);
- } else {
- callback();
- }
- };
-
- const defineJQueryPlugin = plugin => {
- onDOMContentLoaded(() => {
- const $ = getjQuery();
- /* istanbul ignore if */
-
- if ($) {
- const name = plugin.NAME;
- const JQUERY_NO_CONFLICT = $.fn[name];
- $.fn[name] = plugin.jQueryInterface;
- $.fn[name].Constructor = plugin;
-
- $.fn[name].noConflict = () => {
- $.fn[name] = JQUERY_NO_CONFLICT;
- return plugin.jQueryInterface;
- };
- }
- });
- };
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./util/component-functions.js'), require('./util/index.js')) :
+ typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './util/component-functions', './util/index'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Alert = factory(global.BaseComponent, global.EventHandler, global.ComponentFunctions, global.Index));
+})(this, (function (BaseComponent, EventHandler, componentFunctions_js, index_js) { 'use strict';
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/component-functions.js
+ * Bootstrap alert.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
- const enableDismissTrigger = (component, method = 'hide') => {
- const clickEvent = `click.dismiss${component.EVENT_KEY}`;
- const name = component.NAME;
- EventHandler__default.default.on(document, clickEvent, `[data-bs-dismiss="${name}"]`, function (event) {
- if (['A', 'AREA'].includes(this.tagName)) {
- event.preventDefault();
- }
-
- if (isDisabled(this)) {
- return;
- }
- const target = getElementFromSelector(this) || this.closest(`.${name}`);
- const instance = component.getOrCreateInstance(target); // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method
-
- instance[method]();
- });
- };
-
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): alert.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
/**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'alert';
@@ -159,75 +28,61 @@
const EVENT_CLOSED = `closed${EVENT_KEY}`;
const CLASS_NAME_FADE = 'fade';
const CLASS_NAME_SHOW = 'show';
+
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
- class Alert extends BaseComponent__default.default {
+ class Alert extends BaseComponent {
// Getters
static get NAME() {
return NAME;
- } // Public
-
+ }
+ // Public
close() {
- const closeEvent = EventHandler__default.default.trigger(this._element, EVENT_CLOSE);
-
+ const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE);
if (closeEvent.defaultPrevented) {
return;
}
-
this._element.classList.remove(CLASS_NAME_SHOW);
-
const isAnimated = this._element.classList.contains(CLASS_NAME_FADE);
-
this._queueCallback(() => this._destroyElement(), this._element, isAnimated);
- } // Private
-
+ }
+ // Private
_destroyElement() {
this._element.remove();
-
- EventHandler__default.default.trigger(this._element, EVENT_CLOSED);
+ EventHandler.trigger(this._element, EVENT_CLOSED);
this.dispose();
- } // Static
-
+ }
+ // Static
static jQueryInterface(config) {
return this.each(function () {
const data = Alert.getOrCreateInstance(this);
-
if (typeof config !== 'string') {
return;
}
-
if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
throw new TypeError(`No method named "${config}"`);
}
-
data[config](this);
});
}
-
}
+
/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
+ * Data API implementation
*/
+ componentFunctions_js.enableDismissTrigger(Alert, 'close');
- enableDismissTrigger(Alert, 'close');
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .Alert to jQuery only if jQuery is present
*/
- defineJQueryPlugin(Alert);
+ index_js.defineJQueryPlugin(Alert);
return Alert;
diff --git a/js/dist/alert.js.map b/js/dist/alert.js.map
index 0f66ace06..bcb368c79 100644
--- a/js/dist/alert.js.map
+++ b/js/dist/alert.js.map
@@ -1 +1 @@
-{"version":3,"file":"alert.js","sources":["../src/util/index.js","../src/util/component-functions.js","../src/alert.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n// Shoutout AngusCroll (https://goo.gl/pxwQGp)\nconst toType = obj => {\n if (obj === null || obj === undefined) {\n return `${obj}`\n }\n\n return {}.toString.call(obj).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttr = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {\n hrefAttr = `#${hrefAttr.split('#')[1]}`\n }\n\n selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null\n }\n\n return selector\n}\n\nconst getSelectorFromElement = element => {\n const selector = getSelector(element)\n\n if (selector) {\n return document.querySelector(selector) ? selector : null\n }\n\n return null\n}\n\nconst getElementFromSelector = element => {\n const selector = getSelector(element)\n\n return selector ? document.querySelector(selector) : null\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = obj => {\n if (!obj || typeof obj !== 'object') {\n return false\n }\n\n if (typeof obj.jquery !== 'undefined') {\n obj = obj[0]\n }\n\n return typeof obj.nodeType !== 'undefined'\n}\n\nconst getElement = obj => {\n if (isElement(obj)) { // it's a jQuery object or a node element\n return obj.jquery ? obj[0] : obj\n }\n\n if (typeof obj === 'string' && obj.length > 0) {\n return document.querySelector(obj)\n }\n\n return null\n}\n\nconst typeCheckConfig = (componentName, config, configTypes) => {\n Object.keys(configTypes).forEach(property => {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${componentName.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n })\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n return getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n // eslint-disable-next-line no-unused-expressions\n element.offsetHeight\n}\n\nconst getjQuery = () => {\n const { jQuery } = window\n\n if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n DOMContentLoadedCallbacks.forEach(callback => callback())\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = callback => {\n if (typeof callback === 'function') {\n callback()\n }\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element depending on the direction and if cycle is allowed\n if (index === -1) {\n return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]\n }\n\n const listLength = list.length\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n getElement,\n getUID,\n getSelectorFromElement,\n getElementFromSelector,\n getTransitionDurationFromElement,\n triggerTransitionEnd,\n isElement,\n typeCheckConfig,\n isVisible,\n isDisabled,\n findShadowRoot,\n noop,\n getNextActiveElement,\n reflow,\n getjQuery,\n onDOMContentLoaded,\n isRTL,\n defineJQueryPlugin,\n execute,\n executeAfterTransition\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/component-functions.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler'\nimport { getElementFromSelector, isDisabled } from './index'\n\nconst enableDismissTrigger = (component, method = 'hide') => {\n const clickEvent = `click.dismiss${component.EVENT_KEY}`\n const name = component.NAME\n\n EventHandler.on(document, clickEvent, `[data-bs-dismiss=\"${name}\"]`, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n const target = getElementFromSelector(this) || this.closest(`.${name}`)\n const instance = component.getOrCreateInstance(target)\n\n // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method\n instance[method]()\n })\n}\n\nexport {\n enableDismissTrigger\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): alert.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { defineJQueryPlugin } from './util/index'\nimport EventHandler from './dom/event-handler'\nimport BaseComponent from './base-component'\nimport { enableDismissTrigger } from './util/component-functions'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'alert'\nconst DATA_KEY = 'bs.alert'\nconst EVENT_KEY = `.${DATA_KEY}`\n\nconst EVENT_CLOSE = `close${EVENT_KEY}`\nconst EVENT_CLOSED = `closed${EVENT_KEY}`\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Alert extends BaseComponent {\n // Getters\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\n close() {\n const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE)\n\n if (closeEvent.defaultPrevented) {\n return\n }\n\n this._element.classList.remove(CLASS_NAME_SHOW)\n\n const isAnimated = this._element.classList.contains(CLASS_NAME_FADE)\n this._queueCallback(() => this._destroyElement(), this._element, isAnimated)\n }\n\n // Private\n _destroyElement() {\n this._element.remove()\n EventHandler.trigger(this._element, EVENT_CLOSED)\n this.dispose()\n }\n\n // Static\n\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Alert.getOrCreateInstance(this)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](this)\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\nenableDismissTrigger(Alert, 'close')\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Alert to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Alert)\n\nexport default Alert\n"],"names":["getSelector","element","selector","getAttribute","hrefAttr","includes","startsWith","split","trim","getElementFromSelector","document","querySelector","isDisabled","nodeType","Node","ELEMENT_NODE","classList","contains","disabled","hasAttribute","getjQuery","jQuery","window","body","DOMContentLoadedCallbacks","onDOMContentLoaded","callback","readyState","length","addEventListener","forEach","push","defineJQueryPlugin","plugin","$","name","NAME","JQUERY_NO_CONFLICT","fn","jQueryInterface","Constructor","noConflict","enableDismissTrigger","component","method","clickEvent","EVENT_KEY","EventHandler","on","event","tagName","preventDefault","target","closest","instance","getOrCreateInstance","DATA_KEY","EVENT_CLOSE","EVENT_CLOSED","CLASS_NAME_FADE","CLASS_NAME_SHOW","Alert","BaseComponent","close","closeEvent","trigger","_element","defaultPrevented","remove","isAnimated","_queueCallback","_destroyElement","dispose","config","each","data","undefined","TypeError"],"mappings":"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"} \ No newline at end of file
+{"version":3,"file":"alert.js","sources":["../src/alert.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap alert.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport { enableDismissTrigger } from './util/component-functions.js'\nimport { defineJQueryPlugin } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'alert'\nconst DATA_KEY = 'bs.alert'\nconst EVENT_KEY = `.${DATA_KEY}`\n\nconst EVENT_CLOSE = `close${EVENT_KEY}`\nconst EVENT_CLOSED = `closed${EVENT_KEY}`\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\n\n/**\n * Class definition\n */\n\nclass Alert extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME\n }\n\n // Public\n close() {\n const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE)\n\n if (closeEvent.defaultPrevented) {\n return\n }\n\n this._element.classList.remove(CLASS_NAME_SHOW)\n\n const isAnimated = this._element.classList.contains(CLASS_NAME_FADE)\n this._queueCallback(() => this._destroyElement(), this._element, isAnimated)\n }\n\n // Private\n _destroyElement() {\n this._element.remove()\n EventHandler.trigger(this._element, EVENT_CLOSED)\n this.dispose()\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Alert.getOrCreateInstance(this)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](this)\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nenableDismissTrigger(Alert, 'close')\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Alert)\n\nexport default Alert\n"],"names":["NAME","DATA_KEY","EVENT_KEY","EVENT_CLOSE","EVENT_CLOSED","CLASS_NAME_FADE","CLASS_NAME_SHOW","Alert","BaseComponent","close","closeEvent","EventHandler","trigger","_element","defaultPrevented","classList","remove","isAnimated","contains","_queueCallback","_destroyElement","dispose","jQueryInterface","config","each","data","getOrCreateInstance","undefined","startsWith","TypeError","enableDismissTrigger","defineJQueryPlugin"],"mappings":";;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;;;EAOA;EACA;EACA;;EAEA,MAAMA,IAAI,GAAG,OAAO,CAAA;EACpB,MAAMC,QAAQ,GAAG,UAAU,CAAA;EAC3B,MAAMC,SAAS,GAAI,CAAGD,CAAAA,EAAAA,QAAS,CAAC,CAAA,CAAA;EAEhC,MAAME,WAAW,GAAI,CAAOD,KAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EACvC,MAAME,YAAY,GAAI,CAAQF,MAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EACzC,MAAMG,eAAe,GAAG,MAAM,CAAA;EAC9B,MAAMC,eAAe,GAAG,MAAM,CAAA;;EAE9B;EACA;EACA;;EAEA,MAAMC,KAAK,SAASC,aAAa,CAAC;EAChC;IACA,WAAWR,IAAIA,GAAG;EAChB,IAAA,OAAOA,IAAI,CAAA;EACb,GAAA;;EAEA;EACAS,EAAAA,KAAKA,GAAG;MACN,MAAMC,UAAU,GAAGC,YAAY,CAACC,OAAO,CAAC,IAAI,CAACC,QAAQ,EAAEV,WAAW,CAAC,CAAA;MAEnE,IAAIO,UAAU,CAACI,gBAAgB,EAAE;EAC/B,MAAA,OAAA;EACF,KAAA;MAEA,IAAI,CAACD,QAAQ,CAACE,SAAS,CAACC,MAAM,CAACV,eAAe,CAAC,CAAA;MAE/C,MAAMW,UAAU,GAAG,IAAI,CAACJ,QAAQ,CAACE,SAAS,CAACG,QAAQ,CAACb,eAAe,CAAC,CAAA;EACpE,IAAA,IAAI,CAACc,cAAc,CAAC,MAAM,IAAI,CAACC,eAAe,EAAE,EAAE,IAAI,CAACP,QAAQ,EAAEI,UAAU,CAAC,CAAA;EAC9E,GAAA;;EAEA;EACAG,EAAAA,eAAeA,GAAG;EAChB,IAAA,IAAI,CAACP,QAAQ,CAACG,MAAM,EAAE,CAAA;MACtBL,YAAY,CAACC,OAAO,CAAC,IAAI,CAACC,QAAQ,EAAET,YAAY,CAAC,CAAA;MACjD,IAAI,CAACiB,OAAO,EAAE,CAAA;EAChB,GAAA;;EAEA;IACA,OAAOC,eAAeA,CAACC,MAAM,EAAE;EAC7B,IAAA,OAAO,IAAI,CAACC,IAAI,CAAC,YAAY;EAC3B,MAAA,MAAMC,IAAI,GAAGlB,KAAK,CAACmB,mBAAmB,CAAC,IAAI,CAAC,CAAA;EAE5C,MAAA,IAAI,OAAOH,MAAM,KAAK,QAAQ,EAAE;EAC9B,QAAA,OAAA;EACF,OAAA;EAEA,MAAA,IAAIE,IAAI,CAACF,MAAM,CAAC,KAAKI,SAAS,IAAIJ,MAAM,CAACK,UAAU,CAAC,GAAG,CAAC,IAAIL,MAAM,KAAK,aAAa,EAAE;EACpF,QAAA,MAAM,IAAIM,SAAS,CAAE,CAAmBN,iBAAAA,EAAAA,MAAO,GAAE,CAAC,CAAA;EACpD,OAAA;EAEAE,MAAAA,IAAI,CAACF,MAAM,CAAC,CAAC,IAAI,CAAC,CAAA;EACpB,KAAC,CAAC,CAAA;EACJ,GAAA;EACF,CAAA;;EAEA;EACA;EACA;;AAEAO,4CAAoB,CAACvB,KAAK,EAAE,OAAO,CAAC,CAAA;;EAEpC;EACA;EACA;;AAEAwB,6BAAkB,CAACxB,KAAK,CAAC;;;;;;;;"} \ No newline at end of file
diff --git a/js/dist/base-component.js b/js/dist/base-component.js
index efa333ba6..a5af19f19 100644
--- a/js/dist/base-component.js
+++ b/js/dist/base-component.js
@@ -1,180 +1,81 @@
/*!
- * Bootstrap base-component.js v5.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Bootstrap base-component.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/data.js'), require('./dom/event-handler.js')) :
- typeof define === 'function' && define.amd ? define(['./dom/data', './dom/event-handler'], factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Base = factory(global.Data, global.EventHandler));
-})(this, (function (Data, EventHandler) { 'use strict';
-
- const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
-
- const Data__default = /*#__PURE__*/_interopDefaultLegacy(Data);
- const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/data.js'), require('./dom/event-handler.js'), require('./util/config.js'), require('./util/index.js')) :
+ typeof define === 'function' && define.amd ? define(['./dom/data', './dom/event-handler', './util/config', './util/index'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.BaseComponent = factory(global.Data, global.EventHandler, global.Config, global.Index));
+})(this, (function (Data, EventHandler, Config, index_js) { 'use strict';
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/index.js
+ * Bootstrap base-component.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
- const MILLISECONDS_MULTIPLIER = 1000;
- const TRANSITION_END = 'transitionend'; // Shoutout AngusCroll (https://goo.gl/pxwQGp)
-
- const getTransitionDurationFromElement = element => {
- if (!element) {
- return 0;
- } // Get transition-duration of the element
-
-
- let {
- transitionDuration,
- transitionDelay
- } = window.getComputedStyle(element);
- const floatTransitionDuration = Number.parseFloat(transitionDuration);
- const floatTransitionDelay = Number.parseFloat(transitionDelay); // Return 0 if element or transition duration is not found
-
- if (!floatTransitionDuration && !floatTransitionDelay) {
- return 0;
- } // If multiple durations are defined, take the first
-
-
- transitionDuration = transitionDuration.split(',')[0];
- transitionDelay = transitionDelay.split(',')[0];
- return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;
- };
-
- const triggerTransitionEnd = element => {
- element.dispatchEvent(new Event(TRANSITION_END));
- };
-
- const isElement = obj => {
- if (!obj || typeof obj !== 'object') {
- return false;
- }
-
- if (typeof obj.jquery !== 'undefined') {
- obj = obj[0];
- }
- return typeof obj.nodeType !== 'undefined';
- };
- const getElement = obj => {
- if (isElement(obj)) {
- // it's a jQuery object or a node element
- return obj.jquery ? obj[0] : obj;
- }
-
- if (typeof obj === 'string' && obj.length > 0) {
- return document.querySelector(obj);
- }
-
- return null;
- };
-
- const execute = callback => {
- if (typeof callback === 'function') {
- callback();
- }
- };
-
- const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {
- if (!waitForTransition) {
- execute(callback);
- return;
- }
-
- const durationPadding = 5;
- const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding;
- let called = false;
-
- const handler = ({
- target
- }) => {
- if (target !== transitionElement) {
- return;
- }
-
- called = true;
- transitionElement.removeEventListener(TRANSITION_END, handler);
- execute(callback);
- };
-
- transitionElement.addEventListener(TRANSITION_END, handler);
- setTimeout(() => {
- if (!called) {
- triggerTransitionEnd(transitionElement);
- }
- }, emulatedDuration);
- };
-
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): base-component.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
/**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
- const VERSION = '5.1.3';
+ const VERSION = '5.3.3';
- class BaseComponent {
- constructor(element) {
- element = getElement(element);
+ /**
+ * Class definition
+ */
+ class BaseComponent extends Config {
+ constructor(element, config) {
+ super();
+ element = index_js.getElement(element);
if (!element) {
return;
}
-
this._element = element;
- Data__default.default.set(this._element, this.constructor.DATA_KEY, this);
+ this._config = this._getConfig(config);
+ Data.set(this._element, this.constructor.DATA_KEY, this);
}
+ // Public
dispose() {
- Data__default.default.remove(this._element, this.constructor.DATA_KEY);
- EventHandler__default.default.off(this._element, this.constructor.EVENT_KEY);
- Object.getOwnPropertyNames(this).forEach(propertyName => {
+ Data.remove(this._element, this.constructor.DATA_KEY);
+ EventHandler.off(this._element, this.constructor.EVENT_KEY);
+ for (const propertyName of Object.getOwnPropertyNames(this)) {
this[propertyName] = null;
- });
+ }
}
-
_queueCallback(callback, element, isAnimated = true) {
- executeAfterTransition(callback, element, isAnimated);
+ index_js.executeAfterTransition(callback, element, isAnimated);
+ }
+ _getConfig(config) {
+ config = this._mergeConfigObj(config, this._element);
+ config = this._configAfterMerge(config);
+ this._typeCheckConfig(config);
+ return config;
}
- /** Static */
-
+ // Static
static getInstance(element) {
- return Data__default.default.get(getElement(element), this.DATA_KEY);
+ return Data.get(index_js.getElement(element), this.DATA_KEY);
}
-
static getOrCreateInstance(element, config = {}) {
return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null);
}
-
static get VERSION() {
return VERSION;
}
-
- static get NAME() {
- throw new Error('You have to implement the static method "NAME", for each component!');
- }
-
static get DATA_KEY() {
return `bs.${this.NAME}`;
}
-
static get EVENT_KEY() {
return `.${this.DATA_KEY}`;
}
-
+ static eventName(name) {
+ return `${name}${this.EVENT_KEY}`;
+ }
}
return BaseComponent;
diff --git a/js/dist/base-component.js.map b/js/dist/base-component.js.map
index 911c233a7..c8e850631 100644
--- a/js/dist/base-component.js.map
+++ b/js/dist/base-component.js.map
@@ -1 +1 @@
-{"version":3,"file":"base-component.js","sources":["../src/util/index.js","../src/base-component.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n// Shoutout AngusCroll (https://goo.gl/pxwQGp)\nconst toType = obj => {\n if (obj === null || obj === undefined) {\n return `${obj}`\n }\n\n return {}.toString.call(obj).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttr = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {\n hrefAttr = `#${hrefAttr.split('#')[1]}`\n }\n\n selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null\n }\n\n return selector\n}\n\nconst getSelectorFromElement = element => {\n const selector = getSelector(element)\n\n if (selector) {\n return document.querySelector(selector) ? selector : null\n }\n\n return null\n}\n\nconst getElementFromSelector = element => {\n const selector = getSelector(element)\n\n return selector ? document.querySelector(selector) : null\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = obj => {\n if (!obj || typeof obj !== 'object') {\n return false\n }\n\n if (typeof obj.jquery !== 'undefined') {\n obj = obj[0]\n }\n\n return typeof obj.nodeType !== 'undefined'\n}\n\nconst getElement = obj => {\n if (isElement(obj)) { // it's a jQuery object or a node element\n return obj.jquery ? obj[0] : obj\n }\n\n if (typeof obj === 'string' && obj.length > 0) {\n return document.querySelector(obj)\n }\n\n return null\n}\n\nconst typeCheckConfig = (componentName, config, configTypes) => {\n Object.keys(configTypes).forEach(property => {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${componentName.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n })\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n return getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n // eslint-disable-next-line no-unused-expressions\n element.offsetHeight\n}\n\nconst getjQuery = () => {\n const { jQuery } = window\n\n if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n DOMContentLoadedCallbacks.forEach(callback => callback())\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = callback => {\n if (typeof callback === 'function') {\n callback()\n }\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element depending on the direction and if cycle is allowed\n if (index === -1) {\n return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]\n }\n\n const listLength = list.length\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n getElement,\n getUID,\n getSelectorFromElement,\n getElementFromSelector,\n getTransitionDurationFromElement,\n triggerTransitionEnd,\n isElement,\n typeCheckConfig,\n isVisible,\n isDisabled,\n findShadowRoot,\n noop,\n getNextActiveElement,\n reflow,\n getjQuery,\n onDOMContentLoaded,\n isRTL,\n defineJQueryPlugin,\n execute,\n executeAfterTransition\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): base-component.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Data from './dom/data'\nimport {\n executeAfterTransition,\n getElement\n} from './util/index'\nimport EventHandler from './dom/event-handler'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst VERSION = '5.1.3'\n\nclass BaseComponent {\n constructor(element) {\n element = getElement(element)\n\n if (!element) {\n return\n }\n\n this._element = element\n Data.set(this._element, this.constructor.DATA_KEY, this)\n }\n\n dispose() {\n Data.remove(this._element, this.constructor.DATA_KEY)\n EventHandler.off(this._element, this.constructor.EVENT_KEY)\n\n Object.getOwnPropertyNames(this).forEach(propertyName => {\n this[propertyName] = null\n })\n }\n\n _queueCallback(callback, element, isAnimated = true) {\n executeAfterTransition(callback, element, isAnimated)\n }\n\n /** Static */\n\n static getInstance(element) {\n return Data.get(getElement(element), this.DATA_KEY)\n }\n\n static getOrCreateInstance(element, config = {}) {\n return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null)\n }\n\n static get VERSION() {\n return VERSION\n }\n\n static get NAME() {\n throw new Error('You have to implement the static method \"NAME\", for each component!')\n }\n\n static get DATA_KEY() {\n return `bs.${this.NAME}`\n }\n\n static get EVENT_KEY() {\n return `.${this.DATA_KEY}`\n }\n}\n\nexport default BaseComponent\n"],"names":["MILLISECONDS_MULTIPLIER","TRANSITION_END","getTransitionDurationFromElement","element","transitionDuration","transitionDelay","window","getComputedStyle","floatTransitionDuration","Number","parseFloat","floatTransitionDelay","split","triggerTransitionEnd","dispatchEvent","Event","isElement","obj","jquery","nodeType","getElement","length","document","querySelector","execute","callback","executeAfterTransition","transitionElement","waitForTransition","durationPadding","emulatedDuration","called","handler","target","removeEventListener","addEventListener","setTimeout","VERSION","BaseComponent","constructor","_element","Data","set","DATA_KEY","dispose","remove","EventHandler","off","EVENT_KEY","Object","getOwnPropertyNames","forEach","propertyName","_queueCallback","isAnimated","getInstance","get","getOrCreateInstance","config","NAME","Error"],"mappings":";;;;;;;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;EAGA,MAAMA,uBAAuB,GAAG,IAAhC;EACA,MAAMC,cAAc,GAAG,eAAvB;;EAkEA,MAAMC,gCAAgC,GAAGC,OAAO,IAAI;EAClD,MAAI,CAACA,OAAL,EAAc;EACZ,WAAO,CAAP;EACD,GAHiD;;;EAMlD,MAAI;EAAEC,IAAAA,kBAAF;EAAsBC,IAAAA;EAAtB,MAA0CC,MAAM,CAACC,gBAAP,CAAwBJ,OAAxB,CAA9C;EAEA,QAAMK,uBAAuB,GAAGC,MAAM,CAACC,UAAP,CAAkBN,kBAAlB,CAAhC;EACA,QAAMO,oBAAoB,GAAGF,MAAM,CAACC,UAAP,CAAkBL,eAAlB,CAA7B,CATkD;;EAYlD,MAAI,CAACG,uBAAD,IAA4B,CAACG,oBAAjC,EAAuD;EACrD,WAAO,CAAP;EACD,GAdiD;;;EAiBlDP,EAAAA,kBAAkB,GAAGA,kBAAkB,CAACQ,KAAnB,CAAyB,GAAzB,EAA8B,CAA9B,CAArB;EACAP,EAAAA,eAAe,GAAGA,eAAe,CAACO,KAAhB,CAAsB,GAAtB,EAA2B,CAA3B,CAAlB;EAEA,SAAO,CAACH,MAAM,CAACC,UAAP,CAAkBN,kBAAlB,IAAwCK,MAAM,CAACC,UAAP,CAAkBL,eAAlB,CAAzC,IAA+EL,uBAAtF;EACD,CArBD;;EAuBA,MAAMa,oBAAoB,GAAGV,OAAO,IAAI;EACtCA,EAAAA,OAAO,CAACW,aAAR,CAAsB,IAAIC,KAAJ,CAAUd,cAAV,CAAtB;EACD,CAFD;;EAIA,MAAMe,SAAS,GAAGC,GAAG,IAAI;EACvB,MAAI,CAACA,GAAD,IAAQ,OAAOA,GAAP,KAAe,QAA3B,EAAqC;EACnC,WAAO,KAAP;EACD;;EAED,MAAI,OAAOA,GAAG,CAACC,MAAX,KAAsB,WAA1B,EAAuC;EACrCD,IAAAA,GAAG,GAAGA,GAAG,CAAC,CAAD,CAAT;EACD;;EAED,SAAO,OAAOA,GAAG,CAACE,QAAX,KAAwB,WAA/B;EACD,CAVD;;EAYA,MAAMC,UAAU,GAAGH,GAAG,IAAI;EACxB,MAAID,SAAS,CAACC,GAAD,CAAb,EAAoB;EAAE;EACpB,WAAOA,GAAG,CAACC,MAAJ,GAAaD,GAAG,CAAC,CAAD,CAAhB,GAAsBA,GAA7B;EACD;;EAED,MAAI,OAAOA,GAAP,KAAe,QAAf,IAA2BA,GAAG,CAACI,MAAJ,GAAa,CAA5C,EAA+C;EAC7C,WAAOC,QAAQ,CAACC,aAAT,CAAuBN,GAAvB,CAAP;EACD;;EAED,SAAO,IAAP;EACD,CAVD;;EAsIA,MAAMO,OAAO,GAAGC,QAAQ,IAAI;EAC1B,MAAI,OAAOA,QAAP,KAAoB,UAAxB,EAAoC;EAClCA,IAAAA,QAAQ;EACT;EACF,CAJD;;EAMA,MAAMC,sBAAsB,GAAG,CAACD,QAAD,EAAWE,iBAAX,EAA8BC,iBAAiB,GAAG,IAAlD,KAA2D;EACxF,MAAI,CAACA,iBAAL,EAAwB;EACtBJ,IAAAA,OAAO,CAACC,QAAD,CAAP;EACA;EACD;;EAED,QAAMI,eAAe,GAAG,CAAxB;EACA,QAAMC,gBAAgB,GAAG5B,gCAAgC,CAACyB,iBAAD,CAAhC,GAAsDE,eAA/E;EAEA,MAAIE,MAAM,GAAG,KAAb;;EAEA,QAAMC,OAAO,GAAG,CAAC;EAAEC,IAAAA;EAAF,GAAD,KAAgB;EAC9B,QAAIA,MAAM,KAAKN,iBAAf,EAAkC;EAChC;EACD;;EAEDI,IAAAA,MAAM,GAAG,IAAT;EACAJ,IAAAA,iBAAiB,CAACO,mBAAlB,CAAsCjC,cAAtC,EAAsD+B,OAAtD;EACAR,IAAAA,OAAO,CAACC,QAAD,CAAP;EACD,GARD;;EAUAE,EAAAA,iBAAiB,CAACQ,gBAAlB,CAAmClC,cAAnC,EAAmD+B,OAAnD;EACAI,EAAAA,UAAU,CAAC,MAAM;EACf,QAAI,CAACL,MAAL,EAAa;EACXlB,MAAAA,oBAAoB,CAACc,iBAAD,CAApB;EACD;EACF,GAJS,EAIPG,gBAJO,CAAV;EAKD,CA3BD;;EC9PA;EACA;EACA;EACA;EACA;EACA;EASA;EACA;EACA;EACA;EACA;;EAEA,MAAMO,OAAO,GAAG,OAAhB;;EAEA,MAAMC,aAAN,CAAoB;EAClBC,EAAAA,WAAW,CAACpC,OAAD,EAAU;EACnBA,IAAAA,OAAO,GAAGiB,UAAU,CAACjB,OAAD,CAApB;;EAEA,QAAI,CAACA,OAAL,EAAc;EACZ;EACD;;EAED,SAAKqC,QAAL,GAAgBrC,OAAhB;EACAsC,IAAAA,qBAAI,CAACC,GAAL,CAAS,KAAKF,QAAd,EAAwB,KAAKD,WAAL,CAAiBI,QAAzC,EAAmD,IAAnD;EACD;;EAEDC,EAAAA,OAAO,GAAG;EACRH,IAAAA,qBAAI,CAACI,MAAL,CAAY,KAAKL,QAAjB,EAA2B,KAAKD,WAAL,CAAiBI,QAA5C;EACAG,IAAAA,6BAAY,CAACC,GAAb,CAAiB,KAAKP,QAAtB,EAAgC,KAAKD,WAAL,CAAiBS,SAAjD;EAEAC,IAAAA,MAAM,CAACC,mBAAP,CAA2B,IAA3B,EAAiCC,OAAjC,CAAyCC,YAAY,IAAI;EACvD,WAAKA,YAAL,IAAqB,IAArB;EACD,KAFD;EAGD;;EAEDC,EAAAA,cAAc,CAAC5B,QAAD,EAAWtB,OAAX,EAAoBmD,UAAU,GAAG,IAAjC,EAAuC;EACnD5B,IAAAA,sBAAsB,CAACD,QAAD,EAAWtB,OAAX,EAAoBmD,UAApB,CAAtB;EACD;EAED;;;EAEkB,SAAXC,WAAW,CAACpD,OAAD,EAAU;EAC1B,WAAOsC,qBAAI,CAACe,GAAL,CAASpC,UAAU,CAACjB,OAAD,CAAnB,EAA8B,KAAKwC,QAAnC,CAAP;EACD;;EAEyB,SAAnBc,mBAAmB,CAACtD,OAAD,EAAUuD,MAAM,GAAG,EAAnB,EAAuB;EAC/C,WAAO,KAAKH,WAAL,CAAiBpD,OAAjB,KAA6B,IAAI,IAAJ,CAASA,OAAT,EAAkB,OAAOuD,MAAP,KAAkB,QAAlB,GAA6BA,MAA7B,GAAsC,IAAxD,CAApC;EACD;;EAEiB,aAAPrB,OAAO,GAAG;EACnB,WAAOA,OAAP;EACD;;EAEc,aAAJsB,IAAI,GAAG;EAChB,UAAM,IAAIC,KAAJ,CAAU,qEAAV,CAAN;EACD;;EAEkB,aAARjB,QAAQ,GAAG;EACpB,WAAQ,MAAK,KAAKgB,IAAK,EAAvB;EACD;;EAEmB,aAATX,SAAS,GAAG;EACrB,WAAQ,IAAG,KAAKL,QAAS,EAAzB;EACD;;EAjDiB;;;;;;;;"} \ No newline at end of file
+{"version":3,"file":"base-component.js","sources":["../src/base-component.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap base-component.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Data from './dom/data.js'\nimport EventHandler from './dom/event-handler.js'\nimport Config from './util/config.js'\nimport { executeAfterTransition, getElement } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst VERSION = '5.3.3'\n\n/**\n * Class definition\n */\n\nclass BaseComponent extends Config {\n constructor(element, config) {\n super()\n\n element = getElement(element)\n if (!element) {\n return\n }\n\n this._element = element\n this._config = this._getConfig(config)\n\n Data.set(this._element, this.constructor.DATA_KEY, this)\n }\n\n // Public\n dispose() {\n Data.remove(this._element, this.constructor.DATA_KEY)\n EventHandler.off(this._element, this.constructor.EVENT_KEY)\n\n for (const propertyName of Object.getOwnPropertyNames(this)) {\n this[propertyName] = null\n }\n }\n\n _queueCallback(callback, element, isAnimated = true) {\n executeAfterTransition(callback, element, isAnimated)\n }\n\n _getConfig(config) {\n config = this._mergeConfigObj(config, this._element)\n config = this._configAfterMerge(config)\n this._typeCheckConfig(config)\n return config\n }\n\n // Static\n static getInstance(element) {\n return Data.get(getElement(element), this.DATA_KEY)\n }\n\n static getOrCreateInstance(element, config = {}) {\n return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null)\n }\n\n static get VERSION() {\n return VERSION\n }\n\n static get DATA_KEY() {\n return `bs.${this.NAME}`\n }\n\n static get EVENT_KEY() {\n return `.${this.DATA_KEY}`\n }\n\n static eventName(name) {\n return `${name}${this.EVENT_KEY}`\n }\n}\n\nexport default BaseComponent\n"],"names":["VERSION","BaseComponent","Config","constructor","element","config","getElement","_element","_config","_getConfig","Data","set","DATA_KEY","dispose","remove","EventHandler","off","EVENT_KEY","propertyName","Object","getOwnPropertyNames","_queueCallback","callback","isAnimated","executeAfterTransition","_mergeConfigObj","_configAfterMerge","_typeCheckConfig","getInstance","get","getOrCreateInstance","NAME","eventName","name"],"mappings":";;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;;;EAOA;EACA;EACA;;EAEA,MAAMA,OAAO,GAAG,OAAO,CAAA;;EAEvB;EACA;EACA;;EAEA,MAAMC,aAAa,SAASC,MAAM,CAAC;EACjCC,EAAAA,WAAWA,CAACC,OAAO,EAAEC,MAAM,EAAE;EAC3B,IAAA,KAAK,EAAE,CAAA;EAEPD,IAAAA,OAAO,GAAGE,mBAAU,CAACF,OAAO,CAAC,CAAA;MAC7B,IAAI,CAACA,OAAO,EAAE;EACZ,MAAA,OAAA;EACF,KAAA;MAEA,IAAI,CAACG,QAAQ,GAAGH,OAAO,CAAA;MACvB,IAAI,CAACI,OAAO,GAAG,IAAI,CAACC,UAAU,CAACJ,MAAM,CAAC,CAAA;EAEtCK,IAAAA,IAAI,CAACC,GAAG,CAAC,IAAI,CAACJ,QAAQ,EAAE,IAAI,CAACJ,WAAW,CAACS,QAAQ,EAAE,IAAI,CAAC,CAAA;EAC1D,GAAA;;EAEA;EACAC,EAAAA,OAAOA,GAAG;EACRH,IAAAA,IAAI,CAACI,MAAM,CAAC,IAAI,CAACP,QAAQ,EAAE,IAAI,CAACJ,WAAW,CAACS,QAAQ,CAAC,CAAA;EACrDG,IAAAA,YAAY,CAACC,GAAG,CAAC,IAAI,CAACT,QAAQ,EAAE,IAAI,CAACJ,WAAW,CAACc,SAAS,CAAC,CAAA;MAE3D,KAAK,MAAMC,YAAY,IAAIC,MAAM,CAACC,mBAAmB,CAAC,IAAI,CAAC,EAAE;EAC3D,MAAA,IAAI,CAACF,YAAY,CAAC,GAAG,IAAI,CAAA;EAC3B,KAAA;EACF,GAAA;IAEAG,cAAcA,CAACC,QAAQ,EAAElB,OAAO,EAAEmB,UAAU,GAAG,IAAI,EAAE;EACnDC,IAAAA,+BAAsB,CAACF,QAAQ,EAAElB,OAAO,EAAEmB,UAAU,CAAC,CAAA;EACvD,GAAA;IAEAd,UAAUA,CAACJ,MAAM,EAAE;MACjBA,MAAM,GAAG,IAAI,CAACoB,eAAe,CAACpB,MAAM,EAAE,IAAI,CAACE,QAAQ,CAAC,CAAA;EACpDF,IAAAA,MAAM,GAAG,IAAI,CAACqB,iBAAiB,CAACrB,MAAM,CAAC,CAAA;EACvC,IAAA,IAAI,CAACsB,gBAAgB,CAACtB,MAAM,CAAC,CAAA;EAC7B,IAAA,OAAOA,MAAM,CAAA;EACf,GAAA;;EAEA;IACA,OAAOuB,WAAWA,CAACxB,OAAO,EAAE;EAC1B,IAAA,OAAOM,IAAI,CAACmB,GAAG,CAACvB,mBAAU,CAACF,OAAO,CAAC,EAAE,IAAI,CAACQ,QAAQ,CAAC,CAAA;EACrD,GAAA;IAEA,OAAOkB,mBAAmBA,CAAC1B,OAAO,EAAEC,MAAM,GAAG,EAAE,EAAE;MAC/C,OAAO,IAAI,CAACuB,WAAW,CAACxB,OAAO,CAAC,IAAI,IAAI,IAAI,CAACA,OAAO,EAAE,OAAOC,MAAM,KAAK,QAAQ,GAAGA,MAAM,GAAG,IAAI,CAAC,CAAA;EACnG,GAAA;IAEA,WAAWL,OAAOA,GAAG;EACnB,IAAA,OAAOA,OAAO,CAAA;EAChB,GAAA;IAEA,WAAWY,QAAQA,GAAG;EACpB,IAAA,OAAQ,CAAK,GAAA,EAAA,IAAI,CAACmB,IAAK,CAAC,CAAA,CAAA;EAC1B,GAAA;IAEA,WAAWd,SAASA,GAAG;EACrB,IAAA,OAAQ,CAAG,CAAA,EAAA,IAAI,CAACL,QAAS,CAAC,CAAA,CAAA;EAC5B,GAAA;IAEA,OAAOoB,SAASA,CAACC,IAAI,EAAE;EACrB,IAAA,OAAQ,GAAEA,IAAK,CAAA,EAAE,IAAI,CAAChB,SAAU,CAAC,CAAA,CAAA;EACnC,GAAA;EACF;;;;;;;;"} \ No newline at end of file
diff --git a/js/dist/button.js b/js/dist/button.js
index f9755e3d4..09d8caab9 100644
--- a/js/dist/button.js
+++ b/js/dist/button.js
@@ -1,84 +1,24 @@
/*!
- * Bootstrap button.js v5.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Bootstrap button.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/event-handler.js'), require('./base-component.js')) :
- typeof define === 'function' && define.amd ? define(['./dom/event-handler', './base-component'], factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Button = factory(global.EventHandler, global.Base));
-})(this, (function (EventHandler, BaseComponent) { 'use strict';
-
- const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
-
- const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
- const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./util/index.js')) :
+ typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './util/index'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Button = factory(global.BaseComponent, global.EventHandler, global.Index));
+})(this, (function (BaseComponent, EventHandler, index_js) { 'use strict';
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/index.js
+ * Bootstrap button.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
- const getjQuery = () => {
- const {
- jQuery
- } = window;
-
- if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
- return jQuery;
- }
-
- return null;
- };
-
- const DOMContentLoadedCallbacks = [];
-
- const onDOMContentLoaded = callback => {
- if (document.readyState === 'loading') {
- // add listener on the first call when the document is in loading state
- if (!DOMContentLoadedCallbacks.length) {
- document.addEventListener('DOMContentLoaded', () => {
- DOMContentLoadedCallbacks.forEach(callback => callback());
- });
- }
-
- DOMContentLoadedCallbacks.push(callback);
- } else {
- callback();
- }
- };
-
- const defineJQueryPlugin = plugin => {
- onDOMContentLoaded(() => {
- const $ = getjQuery();
- /* istanbul ignore if */
-
- if ($) {
- const name = plugin.NAME;
- const JQUERY_NO_CONFLICT = $.fn[name];
- $.fn[name] = plugin.jQueryInterface;
- $.fn[name].Constructor = plugin;
-
- $.fn[name].noConflict = () => {
- $.fn[name] = JQUERY_NO_CONFLICT;
- return plugin.jQueryInterface;
- };
- }
- });
- };
/**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): button.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- /**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'button';
@@ -88,57 +28,50 @@
const CLASS_NAME_ACTIVE = 'active';
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="button"]';
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;
+
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
- class Button extends BaseComponent__default.default {
+ class Button extends BaseComponent {
// Getters
static get NAME() {
return NAME;
- } // Public
-
+ }
+ // Public
toggle() {
// Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method
this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE));
- } // Static
-
+ }
+ // Static
static jQueryInterface(config) {
return this.each(function () {
const data = Button.getOrCreateInstance(this);
-
if (config === 'toggle') {
data[config]();
}
});
}
-
}
+
/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
+ * Data API implementation
*/
-
- EventHandler__default.default.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {
+ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {
event.preventDefault();
const button = event.target.closest(SELECTOR_DATA_TOGGLE);
const data = Button.getOrCreateInstance(button);
data.toggle();
});
+
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .Button to jQuery only if jQuery is present
*/
- defineJQueryPlugin(Button);
+ index_js.defineJQueryPlugin(Button);
return Button;
diff --git a/js/dist/button.js.map b/js/dist/button.js.map
index a58a00c3d..19f7e9d73 100644
--- a/js/dist/button.js.map
+++ b/js/dist/button.js.map
@@ -1 +1 @@
-{"version":3,"file":"button.js","sources":["../src/util/index.js","../src/button.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n// Shoutout AngusCroll (https://goo.gl/pxwQGp)\nconst toType = obj => {\n if (obj === null || obj === undefined) {\n return `${obj}`\n }\n\n return {}.toString.call(obj).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttr = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {\n hrefAttr = `#${hrefAttr.split('#')[1]}`\n }\n\n selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null\n }\n\n return selector\n}\n\nconst getSelectorFromElement = element => {\n const selector = getSelector(element)\n\n if (selector) {\n return document.querySelector(selector) ? selector : null\n }\n\n return null\n}\n\nconst getElementFromSelector = element => {\n const selector = getSelector(element)\n\n return selector ? document.querySelector(selector) : null\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = obj => {\n if (!obj || typeof obj !== 'object') {\n return false\n }\n\n if (typeof obj.jquery !== 'undefined') {\n obj = obj[0]\n }\n\n return typeof obj.nodeType !== 'undefined'\n}\n\nconst getElement = obj => {\n if (isElement(obj)) { // it's a jQuery object or a node element\n return obj.jquery ? obj[0] : obj\n }\n\n if (typeof obj === 'string' && obj.length > 0) {\n return document.querySelector(obj)\n }\n\n return null\n}\n\nconst typeCheckConfig = (componentName, config, configTypes) => {\n Object.keys(configTypes).forEach(property => {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${componentName.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n })\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n return getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n // eslint-disable-next-line no-unused-expressions\n element.offsetHeight\n}\n\nconst getjQuery = () => {\n const { jQuery } = window\n\n if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n DOMContentLoadedCallbacks.forEach(callback => callback())\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = callback => {\n if (typeof callback === 'function') {\n callback()\n }\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element depending on the direction and if cycle is allowed\n if (index === -1) {\n return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]\n }\n\n const listLength = list.length\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n getElement,\n getUID,\n getSelectorFromElement,\n getElementFromSelector,\n getTransitionDurationFromElement,\n triggerTransitionEnd,\n isElement,\n typeCheckConfig,\n isVisible,\n isDisabled,\n findShadowRoot,\n noop,\n getNextActiveElement,\n reflow,\n getjQuery,\n onDOMContentLoaded,\n isRTL,\n defineJQueryPlugin,\n execute,\n executeAfterTransition\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): button.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { defineJQueryPlugin } from './util/index'\nimport EventHandler from './dom/event-handler'\nimport BaseComponent from './base-component'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'button'\nconst DATA_KEY = 'bs.button'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst CLASS_NAME_ACTIVE = 'active'\n\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"button\"]'\n\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Button extends BaseComponent {\n // Getters\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\n toggle() {\n // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method\n this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE))\n }\n\n // Static\n\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Button.getOrCreateInstance(this)\n\n if (config === 'toggle') {\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {\n event.preventDefault()\n\n const button = event.target.closest(SELECTOR_DATA_TOGGLE)\n const data = Button.getOrCreateInstance(button)\n\n data.toggle()\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Button to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Button)\n\nexport default Button\n"],"names":["getjQuery","jQuery","window","document","body","hasAttribute","DOMContentLoadedCallbacks","onDOMContentLoaded","callback","readyState","length","addEventListener","forEach","push","defineJQueryPlugin","plugin","$","name","NAME","JQUERY_NO_CONFLICT","fn","jQueryInterface","Constructor","noConflict","DATA_KEY","EVENT_KEY","DATA_API_KEY","CLASS_NAME_ACTIVE","SELECTOR_DATA_TOGGLE","EVENT_CLICK_DATA_API","Button","BaseComponent","toggle","_element","setAttribute","classList","config","each","data","getOrCreateInstance","EventHandler","on","event","preventDefault","button","target","closest"],"mappings":"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"} \ No newline at end of file
+{"version":3,"file":"button.js","sources":["../src/button.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap button.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport { defineJQueryPlugin } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'button'\nconst DATA_KEY = 'bs.button'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst CLASS_NAME_ACTIVE = 'active'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"button\"]'\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\n/**\n * Class definition\n */\n\nclass Button extends BaseComponent {\n // Getters\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle() {\n // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method\n this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE))\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Button.getOrCreateInstance(this)\n\n if (config === 'toggle') {\n data[config]()\n }\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {\n event.preventDefault()\n\n const button = event.target.closest(SELECTOR_DATA_TOGGLE)\n const data = Button.getOrCreateInstance(button)\n\n data.toggle()\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Button)\n\nexport default Button\n"],"names":["NAME","DATA_KEY","EVENT_KEY","DATA_API_KEY","CLASS_NAME_ACTIVE","SELECTOR_DATA_TOGGLE","EVENT_CLICK_DATA_API","Button","BaseComponent","toggle","_element","setAttribute","classList","jQueryInterface","config","each","data","getOrCreateInstance","EventHandler","on","document","event","preventDefault","button","target","closest","defineJQueryPlugin"],"mappings":";;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;;;EAMA;EACA;EACA;;EAEA,MAAMA,IAAI,GAAG,QAAQ,CAAA;EACrB,MAAMC,QAAQ,GAAG,WAAW,CAAA;EAC5B,MAAMC,SAAS,GAAI,CAAGD,CAAAA,EAAAA,QAAS,CAAC,CAAA,CAAA;EAChC,MAAME,YAAY,GAAG,WAAW,CAAA;EAEhC,MAAMC,iBAAiB,GAAG,QAAQ,CAAA;EAClC,MAAMC,oBAAoB,GAAG,2BAA2B,CAAA;EACxD,MAAMC,oBAAoB,GAAI,CAAA,KAAA,EAAOJ,SAAU,CAAA,EAAEC,YAAa,CAAC,CAAA,CAAA;;EAE/D;EACA;EACA;;EAEA,MAAMI,MAAM,SAASC,aAAa,CAAC;EACjC;IACA,WAAWR,IAAIA,GAAG;EAChB,IAAA,OAAOA,IAAI,CAAA;EACb,GAAA;;EAEA;EACAS,EAAAA,MAAMA,GAAG;EACP;EACA,IAAA,IAAI,CAACC,QAAQ,CAACC,YAAY,CAAC,cAAc,EAAE,IAAI,CAACD,QAAQ,CAACE,SAAS,CAACH,MAAM,CAACL,iBAAiB,CAAC,CAAC,CAAA;EAC/F,GAAA;;EAEA;IACA,OAAOS,eAAeA,CAACC,MAAM,EAAE;EAC7B,IAAA,OAAO,IAAI,CAACC,IAAI,CAAC,YAAY;EAC3B,MAAA,MAAMC,IAAI,GAAGT,MAAM,CAACU,mBAAmB,CAAC,IAAI,CAAC,CAAA;QAE7C,IAAIH,MAAM,KAAK,QAAQ,EAAE;EACvBE,QAAAA,IAAI,CAACF,MAAM,CAAC,EAAE,CAAA;EAChB,OAAA;EACF,KAAC,CAAC,CAAA;EACJ,GAAA;EACF,CAAA;;EAEA;EACA;EACA;;EAEAI,YAAY,CAACC,EAAE,CAACC,QAAQ,EAAEd,oBAAoB,EAAED,oBAAoB,EAAEgB,KAAK,IAAI;IAC7EA,KAAK,CAACC,cAAc,EAAE,CAAA;IAEtB,MAAMC,MAAM,GAAGF,KAAK,CAACG,MAAM,CAACC,OAAO,CAACpB,oBAAoB,CAAC,CAAA;EACzD,EAAA,MAAMW,IAAI,GAAGT,MAAM,CAACU,mBAAmB,CAACM,MAAM,CAAC,CAAA;IAE/CP,IAAI,CAACP,MAAM,EAAE,CAAA;EACf,CAAC,CAAC,CAAA;;EAEF;EACA;EACA;;AAEAiB,6BAAkB,CAACnB,MAAM,CAAC;;;;;;;;"} \ No newline at end of file
diff --git a/js/dist/carousel.js b/js/dist/carousel.js
index 94fb272a1..9422e74b5 100644
--- a/js/dist/carousel.js
+++ b/js/dist/carousel.js
@@ -1,203 +1,24 @@
/*!
- * Bootstrap carousel.js v5.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Bootstrap carousel.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./base-component.js')) :
- typeof define === 'function' && define.amd ? define(['./dom/event-handler', './dom/manipulator', './dom/selector-engine', './base-component'], factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Carousel = factory(global.EventHandler, global.Manipulator, global.SelectorEngine, global.Base));
-})(this, (function (EventHandler, Manipulator, SelectorEngine, BaseComponent) { 'use strict';
-
- const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
-
- const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
- const Manipulator__default = /*#__PURE__*/_interopDefaultLegacy(Manipulator);
- const SelectorEngine__default = /*#__PURE__*/_interopDefaultLegacy(SelectorEngine);
- const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./util/index.js'), require('./util/swipe.js')) :
+ typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './dom/manipulator', './dom/selector-engine', './util/index', './util/swipe'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Carousel = factory(global.BaseComponent, global.EventHandler, global.Manipulator, global.SelectorEngine, global.Index, global.Swipe));
+})(this, (function (BaseComponent, EventHandler, Manipulator, SelectorEngine, index_js, Swipe) { 'use strict';
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/index.js
+ * Bootstrap carousel.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
- const TRANSITION_END = 'transitionend'; // Shoutout AngusCroll (https://goo.gl/pxwQGp)
-
- const toType = obj => {
- if (obj === null || obj === undefined) {
- return `${obj}`;
- }
-
- return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase();
- };
-
- const getSelector = element => {
- let selector = element.getAttribute('data-bs-target');
-
- if (!selector || selector === '#') {
- let hrefAttr = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes,
- // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
- // `document.querySelector` will rightfully complain it is invalid.
- // See https://github.com/twbs/bootstrap/issues/32273
-
- if (!hrefAttr || !hrefAttr.includes('#') && !hrefAttr.startsWith('.')) {
- return null;
- } // Just in case some CMS puts out a full URL with the anchor appended
-
-
- if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {
- hrefAttr = `#${hrefAttr.split('#')[1]}`;
- }
-
- selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null;
- }
-
- return selector;
- };
-
- const getElementFromSelector = element => {
- const selector = getSelector(element);
- return selector ? document.querySelector(selector) : null;
- };
-
- const triggerTransitionEnd = element => {
- element.dispatchEvent(new Event(TRANSITION_END));
- };
-
- const isElement = obj => {
- if (!obj || typeof obj !== 'object') {
- return false;
- }
-
- if (typeof obj.jquery !== 'undefined') {
- obj = obj[0];
- }
-
- return typeof obj.nodeType !== 'undefined';
- };
-
- const typeCheckConfig = (componentName, config, configTypes) => {
- Object.keys(configTypes).forEach(property => {
- const expectedTypes = configTypes[property];
- const value = config[property];
- const valueType = value && isElement(value) ? 'element' : toType(value);
-
- if (!new RegExp(expectedTypes).test(valueType)) {
- throw new TypeError(`${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`);
- }
- });
- };
-
- const isVisible = element => {
- if (!isElement(element) || element.getClientRects().length === 0) {
- return false;
- }
-
- return getComputedStyle(element).getPropertyValue('visibility') === 'visible';
- };
- /**
- * Trick to restart an element's animation
- *
- * @param {HTMLElement} element
- * @return void
- *
- * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
- */
-
-
- const reflow = element => {
- // eslint-disable-next-line no-unused-expressions
- element.offsetHeight;
- };
-
- const getjQuery = () => {
- const {
- jQuery
- } = window;
-
- if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
- return jQuery;
- }
-
- return null;
- };
-
- const DOMContentLoadedCallbacks = [];
-
- const onDOMContentLoaded = callback => {
- if (document.readyState === 'loading') {
- // add listener on the first call when the document is in loading state
- if (!DOMContentLoadedCallbacks.length) {
- document.addEventListener('DOMContentLoaded', () => {
- DOMContentLoadedCallbacks.forEach(callback => callback());
- });
- }
-
- DOMContentLoadedCallbacks.push(callback);
- } else {
- callback();
- }
- };
-
- const isRTL = () => document.documentElement.dir === 'rtl';
-
- const defineJQueryPlugin = plugin => {
- onDOMContentLoaded(() => {
- const $ = getjQuery();
- /* istanbul ignore if */
-
- if ($) {
- const name = plugin.NAME;
- const JQUERY_NO_CONFLICT = $.fn[name];
- $.fn[name] = plugin.jQueryInterface;
- $.fn[name].Constructor = plugin;
-
- $.fn[name].noConflict = () => {
- $.fn[name] = JQUERY_NO_CONFLICT;
- return plugin.jQueryInterface;
- };
- }
- });
- };
- /**
- * Return the previous/next element of a list.
- *
- * @param {array} list The list of elements
- * @param activeElement The active element
- * @param shouldGetNext Choose to get next or previous element
- * @param isCycleAllowed
- * @return {Element|elem} The proper element
- */
-
-
- const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {
- let index = list.indexOf(activeElement); // if the element does not exist in the list return an element depending on the direction and if cycle is allowed
-
- if (index === -1) {
- return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0];
- }
-
- const listLength = list.length;
- index += shouldGetNext ? 1 : -1;
-
- if (isCycleAllowed) {
- index = (index + listLength) % listLength;
- }
- return list[Math.max(0, Math.min(index, listLength - 1))];
- };
/**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): carousel.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- /**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'carousel';
@@ -208,41 +29,15 @@
const ARROW_RIGHT_KEY = 'ArrowRight';
const TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch
- const SWIPE_THRESHOLD = 40;
- const Default = {
- interval: 5000,
- keyboard: true,
- slide: false,
- pause: 'hover',
- wrap: true,
- touch: true
- };
- const DefaultType = {
- interval: '(number|boolean)',
- keyboard: 'boolean',
- slide: '(boolean|string)',
- pause: '(string|boolean)',
- wrap: 'boolean',
- touch: 'boolean'
- };
const ORDER_NEXT = 'next';
const ORDER_PREV = 'prev';
const DIRECTION_LEFT = 'left';
const DIRECTION_RIGHT = 'right';
- const KEY_TO_DIRECTION = {
- [ARROW_LEFT_KEY]: DIRECTION_RIGHT,
- [ARROW_RIGHT_KEY]: DIRECTION_LEFT
- };
const EVENT_SLIDE = `slide${EVENT_KEY}`;
const EVENT_SLID = `slid${EVENT_KEY}`;
const EVENT_KEYDOWN = `keydown${EVENT_KEY}`;
const EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}`;
const EVENT_MOUSELEAVE = `mouseleave${EVENT_KEY}`;
- const EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`;
- const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}`;
- const EVENT_TOUCHEND = `touchend${EVENT_KEY}`;
- const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`;
- const EVENT_POINTERUP = `pointerup${EVENT_KEY}`;
const EVENT_DRAG_START = `dragstart${EVENT_KEY}`;
const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`;
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;
@@ -253,489 +48,339 @@
const CLASS_NAME_START = 'carousel-item-start';
const CLASS_NAME_NEXT = 'carousel-item-next';
const CLASS_NAME_PREV = 'carousel-item-prev';
- const CLASS_NAME_POINTER_EVENT = 'pointer-event';
const SELECTOR_ACTIVE = '.active';
- const SELECTOR_ACTIVE_ITEM = '.active.carousel-item';
const SELECTOR_ITEM = '.carousel-item';
+ const SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM;
const SELECTOR_ITEM_IMG = '.carousel-item img';
- const SELECTOR_NEXT_PREV = '.carousel-item-next, .carousel-item-prev';
const SELECTOR_INDICATORS = '.carousel-indicators';
- const SELECTOR_INDICATOR = '[data-bs-target]';
const SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]';
const SELECTOR_DATA_RIDE = '[data-bs-ride="carousel"]';
- const POINTER_TYPE_TOUCH = 'touch';
- const POINTER_TYPE_PEN = 'pen';
+ const KEY_TO_DIRECTION = {
+ [ARROW_LEFT_KEY]: DIRECTION_RIGHT,
+ [ARROW_RIGHT_KEY]: DIRECTION_LEFT
+ };
+ const Default = {
+ interval: 5000,
+ keyboard: true,
+ pause: 'hover',
+ ride: false,
+ touch: true,
+ wrap: true
+ };
+ const DefaultType = {
+ interval: '(number|boolean)',
+ // TODO:v6 remove boolean support
+ keyboard: 'boolean',
+ pause: '(string|boolean)',
+ ride: '(boolean|string)',
+ touch: 'boolean',
+ wrap: 'boolean'
+ };
+
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
- class Carousel extends BaseComponent__default.default {
+ class Carousel extends BaseComponent {
constructor(element, config) {
- super(element);
- this._items = null;
+ super(element, config);
this._interval = null;
this._activeElement = null;
- this._isPaused = false;
this._isSliding = false;
this.touchTimeout = null;
- this.touchStartX = 0;
- this.touchDeltaX = 0;
- this._config = this._getConfig(config);
- this._indicatorsElement = SelectorEngine__default.default.findOne(SELECTOR_INDICATORS, this._element);
- this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0;
- this._pointerEvent = Boolean(window.PointerEvent);
-
+ this._swipeHelper = null;
+ this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element);
this._addEventListeners();
- } // Getters
-
+ if (this._config.ride === CLASS_NAME_CAROUSEL) {
+ this.cycle();
+ }
+ }
+ // Getters
static get Default() {
return Default;
}
-
+ static get DefaultType() {
+ return DefaultType;
+ }
static get NAME() {
return NAME;
- } // Public
-
+ }
+ // Public
next() {
this._slide(ORDER_NEXT);
}
-
nextWhenVisible() {
+ // FIXME TODO use `document.visibilityState`
// Don't call next when the page isn't visible
// or the carousel or its parent isn't visible
- if (!document.hidden && isVisible(this._element)) {
+ if (!document.hidden && index_js.isVisible(this._element)) {
this.next();
}
}
-
prev() {
this._slide(ORDER_PREV);
}
-
- pause(event) {
- if (!event) {
- this._isPaused = true;
- }
-
- if (SelectorEngine__default.default.findOne(SELECTOR_NEXT_PREV, this._element)) {
- triggerTransitionEnd(this._element);
- this.cycle(true);
+ pause() {
+ if (this._isSliding) {
+ index_js.triggerTransitionEnd(this._element);
}
-
- clearInterval(this._interval);
- this._interval = null;
+ this._clearInterval();
}
-
- cycle(event) {
- if (!event) {
- this._isPaused = false;
- }
-
- if (this._interval) {
- clearInterval(this._interval);
- this._interval = null;
+ cycle() {
+ this._clearInterval();
+ this._updateInterval();
+ this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval);
+ }
+ _maybeEnableCycle() {
+ if (!this._config.ride) {
+ return;
}
-
- if (this._config && this._config.interval && !this._isPaused) {
- this._updateInterval();
-
- this._interval = setInterval((document.visibilityState ? this.nextWhenVisible : this.next).bind(this), this._config.interval);
+ if (this._isSliding) {
+ EventHandler.one(this._element, EVENT_SLID, () => this.cycle());
+ return;
}
+ this.cycle();
}
-
to(index) {
- this._activeElement = SelectorEngine__default.default.findOne(SELECTOR_ACTIVE_ITEM, this._element);
-
- const activeIndex = this._getItemIndex(this._activeElement);
-
- if (index > this._items.length - 1 || index < 0) {
+ const items = this._getItems();
+ if (index > items.length - 1 || index < 0) {
return;
}
-
if (this._isSliding) {
- EventHandler__default.default.one(this._element, EVENT_SLID, () => this.to(index));
+ EventHandler.one(this._element, EVENT_SLID, () => this.to(index));
return;
}
-
+ const activeIndex = this._getItemIndex(this._getActive());
if (activeIndex === index) {
- this.pause();
- this.cycle();
return;
}
-
const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV;
-
- this._slide(order, this._items[index]);
- } // Private
-
-
- _getConfig(config) {
- config = { ...Default,
- ...Manipulator__default.default.getDataAttributes(this._element),
- ...(typeof config === 'object' ? config : {})
- };
- typeCheckConfig(NAME, config, DefaultType);
- return config;
+ this._slide(order, items[index]);
}
-
- _handleSwipe() {
- const absDeltax = Math.abs(this.touchDeltaX);
-
- if (absDeltax <= SWIPE_THRESHOLD) {
- return;
+ dispose() {
+ if (this._swipeHelper) {
+ this._swipeHelper.dispose();
}
-
- const direction = absDeltax / this.touchDeltaX;
- this.touchDeltaX = 0;
-
- if (!direction) {
- return;
- }
-
- this._slide(direction > 0 ? DIRECTION_RIGHT : DIRECTION_LEFT);
+ super.dispose();
}
+ // Private
+ _configAfterMerge(config) {
+ config.defaultInterval = config.interval;
+ return config;
+ }
_addEventListeners() {
if (this._config.keyboard) {
- EventHandler__default.default.on(this._element, EVENT_KEYDOWN, event => this._keydown(event));
+ EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event));
}
-
if (this._config.pause === 'hover') {
- EventHandler__default.default.on(this._element, EVENT_MOUSEENTER, event => this.pause(event));
- EventHandler__default.default.on(this._element, EVENT_MOUSELEAVE, event => this.cycle(event));
+ EventHandler.on(this._element, EVENT_MOUSEENTER, () => this.pause());
+ EventHandler.on(this._element, EVENT_MOUSELEAVE, () => this._maybeEnableCycle());
}
-
- if (this._config.touch && this._touchSupported) {
+ if (this._config.touch && Swipe.isSupported()) {
this._addTouchEventListeners();
}
}
-
_addTouchEventListeners() {
- const hasPointerPenTouch = event => {
- return this._pointerEvent && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH);
- };
-
- const start = event => {
- if (hasPointerPenTouch(event)) {
- this.touchStartX = event.clientX;
- } else if (!this._pointerEvent) {
- this.touchStartX = event.touches[0].clientX;
- }
- };
-
- const move = event => {
- // ensure swiping with one touch and not pinching
- this.touchDeltaX = event.touches && event.touches.length > 1 ? 0 : event.touches[0].clientX - this.touchStartX;
- };
-
- const end = event => {
- if (hasPointerPenTouch(event)) {
- this.touchDeltaX = event.clientX - this.touchStartX;
+ for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {
+ EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault());
+ }
+ const endCallBack = () => {
+ if (this._config.pause !== 'hover') {
+ return;
}
- this._handleSwipe();
-
- if (this._config.pause === 'hover') {
- // If it's a touch-enabled device, mouseenter/leave are fired as
- // part of the mouse compatibility events on first tap - the carousel
- // would stop cycling until user tapped out of it;
- // here, we listen for touchend, explicitly pause the carousel
- // (as if it's the second time we tap on it, mouseenter compat event
- // is NOT fired) and after a timeout (to allow for mouse compatibility
- // events to fire) we explicitly restart cycling
- this.pause();
-
- if (this.touchTimeout) {
- clearTimeout(this.touchTimeout);
- }
+ // If it's a touch-enabled device, mouseenter/leave are fired as
+ // part of the mouse compatibility events on first tap - the carousel
+ // would stop cycling until user tapped out of it;
+ // here, we listen for touchend, explicitly pause the carousel
+ // (as if it's the second time we tap on it, mouseenter compat event
+ // is NOT fired) and after a timeout (to allow for mouse compatibility
+ // events to fire) we explicitly restart cycling
- this.touchTimeout = setTimeout(event => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval);
+ this.pause();
+ if (this.touchTimeout) {
+ clearTimeout(this.touchTimeout);
}
+ this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval);
};
-
- SelectorEngine__default.default.find(SELECTOR_ITEM_IMG, this._element).forEach(itemImg => {
- EventHandler__default.default.on(itemImg, EVENT_DRAG_START, event => event.preventDefault());
- });
-
- if (this._pointerEvent) {
- EventHandler__default.default.on(this._element, EVENT_POINTERDOWN, event => start(event));
- EventHandler__default.default.on(this._element, EVENT_POINTERUP, event => end(event));
-
- this._element.classList.add(CLASS_NAME_POINTER_EVENT);
- } else {
- EventHandler__default.default.on(this._element, EVENT_TOUCHSTART, event => start(event));
- EventHandler__default.default.on(this._element, EVENT_TOUCHMOVE, event => move(event));
- EventHandler__default.default.on(this._element, EVENT_TOUCHEND, event => end(event));
- }
+ const swipeConfig = {
+ leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)),
+ rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)),
+ endCallback: endCallBack
+ };
+ this._swipeHelper = new Swipe(this._element, swipeConfig);
}
-
_keydown(event) {
if (/input|textarea/i.test(event.target.tagName)) {
return;
}
-
const direction = KEY_TO_DIRECTION[event.key];
-
if (direction) {
event.preventDefault();
-
- this._slide(direction);
+ this._slide(this._directionToOrder(direction));
}
}
-
_getItemIndex(element) {
- this._items = element && element.parentNode ? SelectorEngine__default.default.find(SELECTOR_ITEM, element.parentNode) : [];
- return this._items.indexOf(element);
- }
-
- _getItemByOrder(order, activeElement) {
- const isNext = order === ORDER_NEXT;
- return getNextActiveElement(this._items, activeElement, isNext, this._config.wrap);
- }
-
- _triggerSlideEvent(relatedTarget, eventDirectionName) {
- const targetIndex = this._getItemIndex(relatedTarget);
-
- const fromIndex = this._getItemIndex(SelectorEngine__default.default.findOne(SELECTOR_ACTIVE_ITEM, this._element));
-
- return EventHandler__default.default.trigger(this._element, EVENT_SLIDE, {
- relatedTarget,
- direction: eventDirectionName,
- from: fromIndex,
- to: targetIndex
- });
+ return this._getItems().indexOf(element);
}
-
- _setActiveIndicatorElement(element) {
- if (this._indicatorsElement) {
- const activeIndicator = SelectorEngine__default.default.findOne(SELECTOR_ACTIVE, this._indicatorsElement);
- activeIndicator.classList.remove(CLASS_NAME_ACTIVE);
- activeIndicator.removeAttribute('aria-current');
- const indicators = SelectorEngine__default.default.find(SELECTOR_INDICATOR, this._indicatorsElement);
-
- for (let i = 0; i < indicators.length; i++) {
- if (Number.parseInt(indicators[i].getAttribute('data-bs-slide-to'), 10) === this._getItemIndex(element)) {
- indicators[i].classList.add(CLASS_NAME_ACTIVE);
- indicators[i].setAttribute('aria-current', 'true');
- break;
- }
- }
+ _setActiveIndicatorElement(index) {
+ if (!this._indicatorsElement) {
+ return;
+ }
+ const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement);
+ activeIndicator.classList.remove(CLASS_NAME_ACTIVE);
+ activeIndicator.removeAttribute('aria-current');
+ const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to="${index}"]`, this._indicatorsElement);
+ if (newActiveIndicator) {
+ newActiveIndicator.classList.add(CLASS_NAME_ACTIVE);
+ newActiveIndicator.setAttribute('aria-current', 'true');
}
}
-
_updateInterval() {
- const element = this._activeElement || SelectorEngine__default.default.findOne(SELECTOR_ACTIVE_ITEM, this._element);
-
+ const element = this._activeElement || this._getActive();
if (!element) {
return;
}
-
const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10);
-
- if (elementInterval) {
- this._config.defaultInterval = this._config.defaultInterval || this._config.interval;
- this._config.interval = elementInterval;
- } else {
- this._config.interval = this._config.defaultInterval || this._config.interval;
- }
+ this._config.interval = elementInterval || this._config.defaultInterval;
}
-
- _slide(directionOrOrder, element) {
- const order = this._directionToOrder(directionOrOrder);
-
- const activeElement = SelectorEngine__default.default.findOne(SELECTOR_ACTIVE_ITEM, this._element);
-
- const activeElementIndex = this._getItemIndex(activeElement);
-
- const nextElement = element || this._getItemByOrder(order, activeElement);
-
- const nextElementIndex = this._getItemIndex(nextElement);
-
- const isCycling = Boolean(this._interval);
- const isNext = order === ORDER_NEXT;
- const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END;
- const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV;
-
- const eventDirectionName = this._orderToDirection(order);
-
- if (nextElement && nextElement.classList.contains(CLASS_NAME_ACTIVE)) {
- this._isSliding = false;
+ _slide(order, element = null) {
+ if (this._isSliding) {
return;
}
-
- if (this._isSliding) {
+ const activeElement = this._getActive();
+ const isNext = order === ORDER_NEXT;
+ const nextElement = element || index_js.getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap);
+ if (nextElement === activeElement) {
return;
}
-
- const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName);
-
+ const nextElementIndex = this._getItemIndex(nextElement);
+ const triggerEvent = eventName => {
+ return EventHandler.trigger(this._element, eventName, {
+ relatedTarget: nextElement,
+ direction: this._orderToDirection(order),
+ from: this._getItemIndex(activeElement),
+ to: nextElementIndex
+ });
+ };
+ const slideEvent = triggerEvent(EVENT_SLIDE);
if (slideEvent.defaultPrevented) {
return;
}
-
if (!activeElement || !nextElement) {
// Some weirdness is happening, so we bail
+ // TODO: change tests that use empty divs to avoid this check
return;
}
-
+ const isCycling = Boolean(this._interval);
+ this.pause();
this._isSliding = true;
-
- if (isCycling) {
- this.pause();
- }
-
- this._setActiveIndicatorElement(nextElement);
-
+ this._setActiveIndicatorElement(nextElementIndex);
this._activeElement = nextElement;
-
- const triggerSlidEvent = () => {
- EventHandler__default.default.trigger(this._element, EVENT_SLID, {
- relatedTarget: nextElement,
- direction: eventDirectionName,
- from: activeElementIndex,
- to: nextElementIndex
- });
- };
-
- if (this._element.classList.contains(CLASS_NAME_SLIDE)) {
- nextElement.classList.add(orderClassName);
- reflow(nextElement);
- activeElement.classList.add(directionalClassName);
- nextElement.classList.add(directionalClassName);
-
- const completeCallBack = () => {
- nextElement.classList.remove(directionalClassName, orderClassName);
- nextElement.classList.add(CLASS_NAME_ACTIVE);
- activeElement.classList.remove(CLASS_NAME_ACTIVE, orderClassName, directionalClassName);
- this._isSliding = false;
- setTimeout(triggerSlidEvent, 0);
- };
-
- this._queueCallback(completeCallBack, activeElement, true);
- } else {
- activeElement.classList.remove(CLASS_NAME_ACTIVE);
+ const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END;
+ const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV;
+ nextElement.classList.add(orderClassName);
+ index_js.reflow(nextElement);
+ activeElement.classList.add(directionalClassName);
+ nextElement.classList.add(directionalClassName);
+ const completeCallBack = () => {
+ nextElement.classList.remove(directionalClassName, orderClassName);
nextElement.classList.add(CLASS_NAME_ACTIVE);
+ activeElement.classList.remove(CLASS_NAME_ACTIVE, orderClassName, directionalClassName);
this._isSliding = false;
- triggerSlidEvent();
- }
-
+ triggerEvent(EVENT_SLID);
+ };
+ this._queueCallback(completeCallBack, activeElement, this._isAnimated());
if (isCycling) {
this.cycle();
}
}
-
- _directionToOrder(direction) {
- if (![DIRECTION_RIGHT, DIRECTION_LEFT].includes(direction)) {
- return direction;
+ _isAnimated() {
+ return this._element.classList.contains(CLASS_NAME_SLIDE);
+ }
+ _getActive() {
+ return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element);
+ }
+ _getItems() {
+ return SelectorEngine.find(SELECTOR_ITEM, this._element);
+ }
+ _clearInterval() {
+ if (this._interval) {
+ clearInterval(this._interval);
+ this._interval = null;
}
-
- if (isRTL()) {
+ }
+ _directionToOrder(direction) {
+ if (index_js.isRTL()) {
return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT;
}
-
return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV;
}
-
_orderToDirection(order) {
- if (![ORDER_NEXT, ORDER_PREV].includes(order)) {
- return order;
- }
-
- if (isRTL()) {
+ if (index_js.isRTL()) {
return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT;
}
-
return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT;
- } // Static
-
-
- static carouselInterface(element, config) {
- const data = Carousel.getOrCreateInstance(element, config);
- let {
- _config
- } = data;
-
- if (typeof config === 'object') {
- _config = { ..._config,
- ...config
- };
- }
-
- const action = typeof config === 'string' ? config : _config.slide;
-
- if (typeof config === 'number') {
- data.to(config);
- } else if (typeof action === 'string') {
- if (typeof data[action] === 'undefined') {
- throw new TypeError(`No method named "${action}"`);
- }
-
- data[action]();
- } else if (_config.interval && _config.ride) {
- data.pause();
- data.cycle();
- }
}
+ // Static
static jQueryInterface(config) {
return this.each(function () {
- Carousel.carouselInterface(this, config);
+ const data = Carousel.getOrCreateInstance(this, config);
+ if (typeof config === 'number') {
+ data.to(config);
+ return;
+ }
+ if (typeof config === 'string') {
+ if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
+ throw new TypeError(`No method named "${config}"`);
+ }
+ data[config]();
+ }
});
}
-
- static dataApiClickHandler(event) {
- const target = getElementFromSelector(this);
-
- if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {
- return;
- }
-
- const config = { ...Manipulator__default.default.getDataAttributes(target),
- ...Manipulator__default.default.getDataAttributes(this)
- };
- const slideIndex = this.getAttribute('data-bs-slide-to');
-
- if (slideIndex) {
- config.interval = false;
- }
-
- Carousel.carouselInterface(target, config);
-
- if (slideIndex) {
- Carousel.getInstance(target).to(slideIndex);
- }
-
- event.preventDefault();
- }
-
}
+
/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
+ * Data API implementation
*/
-
- EventHandler__default.default.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, Carousel.dataApiClickHandler);
- EventHandler__default.default.on(window, EVENT_LOAD_DATA_API, () => {
- const carousels = SelectorEngine__default.default.find(SELECTOR_DATA_RIDE);
-
- for (let i = 0, len = carousels.length; i < len; i++) {
- Carousel.carouselInterface(carousels[i], Carousel.getInstance(carousels[i]));
+ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, function (event) {
+ const target = SelectorEngine.getElementFromSelector(this);
+ if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {
+ return;
+ }
+ event.preventDefault();
+ const carousel = Carousel.getOrCreateInstance(target);
+ const slideIndex = this.getAttribute('data-bs-slide-to');
+ if (slideIndex) {
+ carousel.to(slideIndex);
+ carousel._maybeEnableCycle();
+ return;
+ }
+ if (Manipulator.getDataAttribute(this, 'slide') === 'next') {
+ carousel.next();
+ carousel._maybeEnableCycle();
+ return;
+ }
+ carousel.prev();
+ carousel._maybeEnableCycle();
+ });
+ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
+ const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE);
+ for (const carousel of carousels) {
+ Carousel.getOrCreateInstance(carousel);
}
});
+
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .Carousel to jQuery only if jQuery is present
*/
- defineJQueryPlugin(Carousel);
+ index_js.defineJQueryPlugin(Carousel);
return Carousel;
diff --git a/js/dist/carousel.js.map b/js/dist/carousel.js.map
index deda5f51e..ca45ba49c 100644
--- a/js/dist/carousel.js.map
+++ b/js/dist/carousel.js.map
@@ -1 +1 @@
-{"version":3,"file":"carousel.js","sources":["../src/util/index.js","../src/carousel.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n// Shoutout AngusCroll (https://goo.gl/pxwQGp)\nconst toType = obj => {\n if (obj === null || obj === undefined) {\n return `${obj}`\n }\n\n return {}.toString.call(obj).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttr = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {\n hrefAttr = `#${hrefAttr.split('#')[1]}`\n }\n\n selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null\n }\n\n return selector\n}\n\nconst getSelectorFromElement = element => {\n const selector = getSelector(element)\n\n if (selector) {\n return document.querySelector(selector) ? selector : null\n }\n\n return null\n}\n\nconst getElementFromSelector = element => {\n const selector = getSelector(element)\n\n return selector ? document.querySelector(selector) : null\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = obj => {\n if (!obj || typeof obj !== 'object') {\n return false\n }\n\n if (typeof obj.jquery !== 'undefined') {\n obj = obj[0]\n }\n\n return typeof obj.nodeType !== 'undefined'\n}\n\nconst getElement = obj => {\n if (isElement(obj)) { // it's a jQuery object or a node element\n return obj.jquery ? obj[0] : obj\n }\n\n if (typeof obj === 'string' && obj.length > 0) {\n return document.querySelector(obj)\n }\n\n return null\n}\n\nconst typeCheckConfig = (componentName, config, configTypes) => {\n Object.keys(configTypes).forEach(property => {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${componentName.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n })\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n return getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n // eslint-disable-next-line no-unused-expressions\n element.offsetHeight\n}\n\nconst getjQuery = () => {\n const { jQuery } = window\n\n if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n DOMContentLoadedCallbacks.forEach(callback => callback())\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = callback => {\n if (typeof callback === 'function') {\n callback()\n }\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element depending on the direction and if cycle is allowed\n if (index === -1) {\n return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]\n }\n\n const listLength = list.length\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n getElement,\n getUID,\n getSelectorFromElement,\n getElementFromSelector,\n getTransitionDurationFromElement,\n triggerTransitionEnd,\n isElement,\n typeCheckConfig,\n isVisible,\n isDisabled,\n findShadowRoot,\n noop,\n getNextActiveElement,\n reflow,\n getjQuery,\n onDOMContentLoaded,\n isRTL,\n defineJQueryPlugin,\n execute,\n executeAfterTransition\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): carousel.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport {\n defineJQueryPlugin,\n getElementFromSelector,\n isRTL,\n isVisible,\n getNextActiveElement,\n reflow,\n triggerTransitionEnd,\n typeCheckConfig\n} from './util/index'\nimport EventHandler from './dom/event-handler'\nimport Manipulator from './dom/manipulator'\nimport SelectorEngine from './dom/selector-engine'\nimport BaseComponent from './base-component'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'carousel'\nconst DATA_KEY = 'bs.carousel'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst ARROW_LEFT_KEY = 'ArrowLeft'\nconst ARROW_RIGHT_KEY = 'ArrowRight'\nconst TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch\nconst SWIPE_THRESHOLD = 40\n\nconst Default = {\n interval: 5000,\n keyboard: true,\n slide: false,\n pause: 'hover',\n wrap: true,\n touch: true\n}\n\nconst DefaultType = {\n interval: '(number|boolean)',\n keyboard: 'boolean',\n slide: '(boolean|string)',\n pause: '(string|boolean)',\n wrap: 'boolean',\n touch: 'boolean'\n}\n\nconst ORDER_NEXT = 'next'\nconst ORDER_PREV = 'prev'\nconst DIRECTION_LEFT = 'left'\nconst DIRECTION_RIGHT = 'right'\n\nconst KEY_TO_DIRECTION = {\n [ARROW_LEFT_KEY]: DIRECTION_RIGHT,\n [ARROW_RIGHT_KEY]: DIRECTION_LEFT\n}\n\nconst EVENT_SLIDE = `slide${EVENT_KEY}`\nconst EVENT_SLID = `slid${EVENT_KEY}`\nconst EVENT_KEYDOWN = `keydown${EVENT_KEY}`\nconst EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}`\nconst EVENT_MOUSELEAVE = `mouseleave${EVENT_KEY}`\nconst EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`\nconst EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}`\nconst EVENT_TOUCHEND = `touchend${EVENT_KEY}`\nconst EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`\nconst EVENT_POINTERUP = `pointerup${EVENT_KEY}`\nconst EVENT_DRAG_START = `dragstart${EVENT_KEY}`\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_CAROUSEL = 'carousel'\nconst CLASS_NAME_ACTIVE = 'active'\nconst CLASS_NAME_SLIDE = 'slide'\nconst CLASS_NAME_END = 'carousel-item-end'\nconst CLASS_NAME_START = 'carousel-item-start'\nconst CLASS_NAME_NEXT = 'carousel-item-next'\nconst CLASS_NAME_PREV = 'carousel-item-prev'\nconst CLASS_NAME_POINTER_EVENT = 'pointer-event'\n\nconst SELECTOR_ACTIVE = '.active'\nconst SELECTOR_ACTIVE_ITEM = '.active.carousel-item'\nconst SELECTOR_ITEM = '.carousel-item'\nconst SELECTOR_ITEM_IMG = '.carousel-item img'\nconst SELECTOR_NEXT_PREV = '.carousel-item-next, .carousel-item-prev'\nconst SELECTOR_INDICATORS = '.carousel-indicators'\nconst SELECTOR_INDICATOR = '[data-bs-target]'\nconst SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]'\nconst SELECTOR_DATA_RIDE = '[data-bs-ride=\"carousel\"]'\n\nconst POINTER_TYPE_TOUCH = 'touch'\nconst POINTER_TYPE_PEN = 'pen'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\nclass Carousel extends BaseComponent {\n constructor(element, config) {\n super(element)\n\n this._items = null\n this._interval = null\n this._activeElement = null\n this._isPaused = false\n this._isSliding = false\n this.touchTimeout = null\n this.touchStartX = 0\n this.touchDeltaX = 0\n\n this._config = this._getConfig(config)\n this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element)\n this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0\n this._pointerEvent = Boolean(window.PointerEvent)\n\n this._addEventListeners()\n }\n\n // Getters\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\n next() {\n this._slide(ORDER_NEXT)\n }\n\n nextWhenVisible() {\n // Don't call next when the page isn't visible\n // or the carousel or its parent isn't visible\n if (!document.hidden && isVisible(this._element)) {\n this.next()\n }\n }\n\n prev() {\n this._slide(ORDER_PREV)\n }\n\n pause(event) {\n if (!event) {\n this._isPaused = true\n }\n\n if (SelectorEngine.findOne(SELECTOR_NEXT_PREV, this._element)) {\n triggerTransitionEnd(this._element)\n this.cycle(true)\n }\n\n clearInterval(this._interval)\n this._interval = null\n }\n\n cycle(event) {\n if (!event) {\n this._isPaused = false\n }\n\n if (this._interval) {\n clearInterval(this._interval)\n this._interval = null\n }\n\n if (this._config && this._config.interval && !this._isPaused) {\n this._updateInterval()\n\n this._interval = setInterval(\n (document.visibilityState ? this.nextWhenVisible : this.next).bind(this),\n this._config.interval\n )\n }\n }\n\n to(index) {\n this._activeElement = SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)\n const activeIndex = this._getItemIndex(this._activeElement)\n\n if (index > this._items.length - 1 || index < 0) {\n return\n }\n\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.to(index))\n return\n }\n\n if (activeIndex === index) {\n this.pause()\n this.cycle()\n return\n }\n\n const order = index > activeIndex ?\n ORDER_NEXT :\n ORDER_PREV\n\n this._slide(order, this._items[index])\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...Manipulator.getDataAttributes(this._element),\n ...(typeof config === 'object' ? config : {})\n }\n typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _handleSwipe() {\n const absDeltax = Math.abs(this.touchDeltaX)\n\n if (absDeltax <= SWIPE_THRESHOLD) {\n return\n }\n\n const direction = absDeltax / this.touchDeltaX\n\n this.touchDeltaX = 0\n\n if (!direction) {\n return\n }\n\n this._slide(direction > 0 ? DIRECTION_RIGHT : DIRECTION_LEFT)\n }\n\n _addEventListeners() {\n if (this._config.keyboard) {\n EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event))\n }\n\n if (this._config.pause === 'hover') {\n EventHandler.on(this._element, EVENT_MOUSEENTER, event => this.pause(event))\n EventHandler.on(this._element, EVENT_MOUSELEAVE, event => this.cycle(event))\n }\n\n if (this._config.touch && this._touchSupported) {\n this._addTouchEventListeners()\n }\n }\n\n _addTouchEventListeners() {\n const hasPointerPenTouch = event => {\n return this._pointerEvent &&\n (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH)\n }\n\n const start = event => {\n if (hasPointerPenTouch(event)) {\n this.touchStartX = event.clientX\n } else if (!this._pointerEvent) {\n this.touchStartX = event.touches[0].clientX\n }\n }\n\n const move = event => {\n // ensure swiping with one touch and not pinching\n this.touchDeltaX = event.touches && event.touches.length > 1 ?\n 0 :\n event.touches[0].clientX - this.touchStartX\n }\n\n const end = event => {\n if (hasPointerPenTouch(event)) {\n this.touchDeltaX = event.clientX - this.touchStartX\n }\n\n this._handleSwipe()\n if (this._config.pause === 'hover') {\n // If it's a touch-enabled device, mouseenter/leave are fired as\n // part of the mouse compatibility events on first tap - the carousel\n // would stop cycling until user tapped out of it;\n // here, we listen for touchend, explicitly pause the carousel\n // (as if it's the second time we tap on it, mouseenter compat event\n // is NOT fired) and after a timeout (to allow for mouse compatibility\n // events to fire) we explicitly restart cycling\n\n this.pause()\n if (this.touchTimeout) {\n clearTimeout(this.touchTimeout)\n }\n\n this.touchTimeout = setTimeout(event => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)\n }\n }\n\n SelectorEngine.find(SELECTOR_ITEM_IMG, this._element).forEach(itemImg => {\n EventHandler.on(itemImg, EVENT_DRAG_START, event => event.preventDefault())\n })\n\n if (this._pointerEvent) {\n EventHandler.on(this._element, EVENT_POINTERDOWN, event => start(event))\n EventHandler.on(this._element, EVENT_POINTERUP, event => end(event))\n\n this._element.classList.add(CLASS_NAME_POINTER_EVENT)\n } else {\n EventHandler.on(this._element, EVENT_TOUCHSTART, event => start(event))\n EventHandler.on(this._element, EVENT_TOUCHMOVE, event => move(event))\n EventHandler.on(this._element, EVENT_TOUCHEND, event => end(event))\n }\n }\n\n _keydown(event) {\n if (/input|textarea/i.test(event.target.tagName)) {\n return\n }\n\n const direction = KEY_TO_DIRECTION[event.key]\n if (direction) {\n event.preventDefault()\n this._slide(direction)\n }\n }\n\n _getItemIndex(element) {\n this._items = element && element.parentNode ?\n SelectorEngine.find(SELECTOR_ITEM, element.parentNode) :\n []\n\n return this._items.indexOf(element)\n }\n\n _getItemByOrder(order, activeElement) {\n const isNext = order === ORDER_NEXT\n return getNextActiveElement(this._items, activeElement, isNext, this._config.wrap)\n }\n\n _triggerSlideEvent(relatedTarget, eventDirectionName) {\n const targetIndex = this._getItemIndex(relatedTarget)\n const fromIndex = this._getItemIndex(SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element))\n\n return EventHandler.trigger(this._element, EVENT_SLIDE, {\n relatedTarget,\n direction: eventDirectionName,\n from: fromIndex,\n to: targetIndex\n })\n }\n\n _setActiveIndicatorElement(element) {\n if (this._indicatorsElement) {\n const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement)\n\n activeIndicator.classList.remove(CLASS_NAME_ACTIVE)\n activeIndicator.removeAttribute('aria-current')\n\n const indicators = SelectorEngine.find(SELECTOR_INDICATOR, this._indicatorsElement)\n\n for (let i = 0; i < indicators.length; i++) {\n if (Number.parseInt(indicators[i].getAttribute('data-bs-slide-to'), 10) === this._getItemIndex(element)) {\n indicators[i].classList.add(CLASS_NAME_ACTIVE)\n indicators[i].setAttribute('aria-current', 'true')\n break\n }\n }\n }\n }\n\n _updateInterval() {\n const element = this._activeElement || SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)\n\n if (!element) {\n return\n }\n\n const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10)\n\n if (elementInterval) {\n this._config.defaultInterval = this._config.defaultInterval || this._config.interval\n this._config.interval = elementInterval\n } else {\n this._config.interval = this._config.defaultInterval || this._config.interval\n }\n }\n\n _slide(directionOrOrder, element) {\n const order = this._directionToOrder(directionOrOrder)\n const activeElement = SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)\n const activeElementIndex = this._getItemIndex(activeElement)\n const nextElement = element || this._getItemByOrder(order, activeElement)\n\n const nextElementIndex = this._getItemIndex(nextElement)\n const isCycling = Boolean(this._interval)\n\n const isNext = order === ORDER_NEXT\n const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END\n const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV\n const eventDirectionName = this._orderToDirection(order)\n\n if (nextElement && nextElement.classList.contains(CLASS_NAME_ACTIVE)) {\n this._isSliding = false\n return\n }\n\n if (this._isSliding) {\n return\n }\n\n const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName)\n if (slideEvent.defaultPrevented) {\n return\n }\n\n if (!activeElement || !nextElement) {\n // Some weirdness is happening, so we bail\n return\n }\n\n this._isSliding = true\n\n if (isCycling) {\n this.pause()\n }\n\n this._setActiveIndicatorElement(nextElement)\n this._activeElement = nextElement\n\n const triggerSlidEvent = () => {\n EventHandler.trigger(this._element, EVENT_SLID, {\n relatedTarget: nextElement,\n direction: eventDirectionName,\n from: activeElementIndex,\n to: nextElementIndex\n })\n }\n\n if (this._element.classList.contains(CLASS_NAME_SLIDE)) {\n nextElement.classList.add(orderClassName)\n\n reflow(nextElement)\n\n activeElement.classList.add(directionalClassName)\n nextElement.classList.add(directionalClassName)\n\n const completeCallBack = () => {\n nextElement.classList.remove(directionalClassName, orderClassName)\n nextElement.classList.add(CLASS_NAME_ACTIVE)\n\n activeElement.classList.remove(CLASS_NAME_ACTIVE, orderClassName, directionalClassName)\n\n this._isSliding = false\n\n setTimeout(triggerSlidEvent, 0)\n }\n\n this._queueCallback(completeCallBack, activeElement, true)\n } else {\n activeElement.classList.remove(CLASS_NAME_ACTIVE)\n nextElement.classList.add(CLASS_NAME_ACTIVE)\n\n this._isSliding = false\n triggerSlidEvent()\n }\n\n if (isCycling) {\n this.cycle()\n }\n }\n\n _directionToOrder(direction) {\n if (![DIRECTION_RIGHT, DIRECTION_LEFT].includes(direction)) {\n return direction\n }\n\n if (isRTL()) {\n return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT\n }\n\n return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV\n }\n\n _orderToDirection(order) {\n if (![ORDER_NEXT, ORDER_PREV].includes(order)) {\n return order\n }\n\n if (isRTL()) {\n return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT\n }\n\n return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT\n }\n\n // Static\n\n static carouselInterface(element, config) {\n const data = Carousel.getOrCreateInstance(element, config)\n\n let { _config } = data\n if (typeof config === 'object') {\n _config = {\n ..._config,\n ...config\n }\n }\n\n const action = typeof config === 'string' ? config : _config.slide\n\n if (typeof config === 'number') {\n data.to(config)\n } else if (typeof action === 'string') {\n if (typeof data[action] === 'undefined') {\n throw new TypeError(`No method named \"${action}\"`)\n }\n\n data[action]()\n } else if (_config.interval && _config.ride) {\n data.pause()\n data.cycle()\n }\n }\n\n static jQueryInterface(config) {\n return this.each(function () {\n Carousel.carouselInterface(this, config)\n })\n }\n\n static dataApiClickHandler(event) {\n const target = getElementFromSelector(this)\n\n if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {\n return\n }\n\n const config = {\n ...Manipulator.getDataAttributes(target),\n ...Manipulator.getDataAttributes(this)\n }\n const slideIndex = this.getAttribute('data-bs-slide-to')\n\n if (slideIndex) {\n config.interval = false\n }\n\n Carousel.carouselInterface(target, config)\n\n if (slideIndex) {\n Carousel.getInstance(target).to(slideIndex)\n }\n\n event.preventDefault()\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, Carousel.dataApiClickHandler)\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE)\n\n for (let i = 0, len = carousels.length; i < len; i++) {\n Carousel.carouselInterface(carousels[i], Carousel.getInstance(carousels[i]))\n }\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Carousel to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Carousel)\n\nexport default Carousel\n"],"names":["TRANSITION_END","toType","obj","undefined","toString","call","match","toLowerCase","getSelector","element","selector","getAttribute","hrefAttr","includes","startsWith","split","trim","getElementFromSelector","document","querySelector","triggerTransitionEnd","dispatchEvent","Event","isElement","jquery","nodeType","typeCheckConfig","componentName","config","configTypes","Object","keys","forEach","property","expectedTypes","value","valueType","RegExp","test","TypeError","toUpperCase","isVisible","getClientRects","length","getComputedStyle","getPropertyValue","reflow","offsetHeight","getjQuery","jQuery","window","body","hasAttribute","DOMContentLoadedCallbacks","onDOMContentLoaded","callback","readyState","addEventListener","push","isRTL","documentElement","dir","defineJQueryPlugin","plugin","$","name","NAME","JQUERY_NO_CONFLICT","fn","jQueryInterface","Constructor","noConflict","getNextActiveElement","list","activeElement","shouldGetNext","isCycleAllowed","index","indexOf","listLength","Math","max","min","DATA_KEY","EVENT_KEY","DATA_API_KEY","ARROW_LEFT_KEY","ARROW_RIGHT_KEY","TOUCHEVENT_COMPAT_WAIT","SWIPE_THRESHOLD","Default","interval","keyboard","slide","pause","wrap","touch","DefaultType","ORDER_NEXT","ORDER_PREV","DIRECTION_LEFT","DIRECTION_RIGHT","KEY_TO_DIRECTION","EVENT_SLIDE","EVENT_SLID","EVENT_KEYDOWN","EVENT_MOUSEENTER","EVENT_MOUSELEAVE","EVENT_TOUCHSTART","EVENT_TOUCHMOVE","EVENT_TOUCHEND","EVENT_POINTERDOWN","EVENT_POINTERUP","EVENT_DRAG_START","EVENT_LOAD_DATA_API","EVENT_CLICK_DATA_API","CLASS_NAME_CAROUSEL","CLASS_NAME_ACTIVE","CLASS_NAME_SLIDE","CLASS_NAME_END","CLASS_NAME_START","CLASS_NAME_NEXT","CLASS_NAME_PREV","CLASS_NAME_POINTER_EVENT","SELECTOR_ACTIVE","SELECTOR_ACTIVE_ITEM","SELECTOR_ITEM","SELECTOR_ITEM_IMG","SELECTOR_NEXT_PREV","SELECTOR_INDICATORS","SELECTOR_INDICATOR","SELECTOR_DATA_SLIDE","SELECTOR_DATA_RIDE","POINTER_TYPE_TOUCH","POINTER_TYPE_PEN","Carousel","BaseComponent","constructor","_items","_interval","_activeElement","_isPaused","_isSliding","touchTimeout","touchStartX","touchDeltaX","_config","_getConfig","_indicatorsElement","SelectorEngine","findOne","_element","_touchSupported","navigator","maxTouchPoints","_pointerEvent","Boolean","PointerEvent","_addEventListeners","next","_slide","nextWhenVisible","hidden","prev","event","cycle","clearInterval","_updateInterval","setInterval","visibilityState","bind","to","activeIndex","_getItemIndex","EventHandler","one","order","Manipulator","getDataAttributes","_handleSwipe","absDeltax","abs","direction","on","_keydown","_addTouchEventListeners","hasPointerPenTouch","pointerType","start","clientX","touches","move","end","clearTimeout","setTimeout","find","itemImg","preventDefault","classList","add","target","tagName","key","parentNode","_getItemByOrder","isNext","_triggerSlideEvent","relatedTarget","eventDirectionName","targetIndex","fromIndex","trigger","from","_setActiveIndicatorElement","activeIndicator","remove","removeAttribute","indicators","i","Number","parseInt","setAttribute","elementInterval","defaultInterval","directionOrOrder","_directionToOrder","activeElementIndex","nextElement","nextElementIndex","isCycling","directionalClassName","orderClassName","_orderToDirection","contains","slideEvent","defaultPrevented","triggerSlidEvent","completeCallBack","_queueCallback","carouselInterface","data","getOrCreateInstance","action","ride","each","dataApiClickHandler","slideIndex","getInstance","carousels","len"],"mappings":"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"} \ No newline at end of file
+{"version":3,"file":"carousel.js","sources":["../src/carousel.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap carousel.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport Manipulator from './dom/manipulator.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport {\n defineJQueryPlugin,\n getNextActiveElement,\n isRTL,\n isVisible,\n reflow,\n triggerTransitionEnd\n} from './util/index.js'\nimport Swipe from './util/swipe.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'carousel'\nconst DATA_KEY = 'bs.carousel'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst ARROW_LEFT_KEY = 'ArrowLeft'\nconst ARROW_RIGHT_KEY = 'ArrowRight'\nconst TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch\n\nconst ORDER_NEXT = 'next'\nconst ORDER_PREV = 'prev'\nconst DIRECTION_LEFT = 'left'\nconst DIRECTION_RIGHT = 'right'\n\nconst EVENT_SLIDE = `slide${EVENT_KEY}`\nconst EVENT_SLID = `slid${EVENT_KEY}`\nconst EVENT_KEYDOWN = `keydown${EVENT_KEY}`\nconst EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}`\nconst EVENT_MOUSELEAVE = `mouseleave${EVENT_KEY}`\nconst EVENT_DRAG_START = `dragstart${EVENT_KEY}`\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_CAROUSEL = 'carousel'\nconst CLASS_NAME_ACTIVE = 'active'\nconst CLASS_NAME_SLIDE = 'slide'\nconst CLASS_NAME_END = 'carousel-item-end'\nconst CLASS_NAME_START = 'carousel-item-start'\nconst CLASS_NAME_NEXT = 'carousel-item-next'\nconst CLASS_NAME_PREV = 'carousel-item-prev'\n\nconst SELECTOR_ACTIVE = '.active'\nconst SELECTOR_ITEM = '.carousel-item'\nconst SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM\nconst SELECTOR_ITEM_IMG = '.carousel-item img'\nconst SELECTOR_INDICATORS = '.carousel-indicators'\nconst SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]'\nconst SELECTOR_DATA_RIDE = '[data-bs-ride=\"carousel\"]'\n\nconst KEY_TO_DIRECTION = {\n [ARROW_LEFT_KEY]: DIRECTION_RIGHT,\n [ARROW_RIGHT_KEY]: DIRECTION_LEFT\n}\n\nconst Default = {\n interval: 5000,\n keyboard: true,\n pause: 'hover',\n ride: false,\n touch: true,\n wrap: true\n}\n\nconst DefaultType = {\n interval: '(number|boolean)', // TODO:v6 remove boolean support\n keyboard: 'boolean',\n pause: '(string|boolean)',\n ride: '(boolean|string)',\n touch: 'boolean',\n wrap: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Carousel extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._interval = null\n this._activeElement = null\n this._isSliding = false\n this.touchTimeout = null\n this._swipeHelper = null\n\n this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element)\n this._addEventListeners()\n\n if (this._config.ride === CLASS_NAME_CAROUSEL) {\n this.cycle()\n }\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n next() {\n this._slide(ORDER_NEXT)\n }\n\n nextWhenVisible() {\n // FIXME TODO use `document.visibilityState`\n // Don't call next when the page isn't visible\n // or the carousel or its parent isn't visible\n if (!document.hidden && isVisible(this._element)) {\n this.next()\n }\n }\n\n prev() {\n this._slide(ORDER_PREV)\n }\n\n pause() {\n if (this._isSliding) {\n triggerTransitionEnd(this._element)\n }\n\n this._clearInterval()\n }\n\n cycle() {\n this._clearInterval()\n this._updateInterval()\n\n this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval)\n }\n\n _maybeEnableCycle() {\n if (!this._config.ride) {\n return\n }\n\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.cycle())\n return\n }\n\n this.cycle()\n }\n\n to(index) {\n const items = this._getItems()\n if (index > items.length - 1 || index < 0) {\n return\n }\n\n if (this._isSliding) {\n EventHandler.one(this._element, EVENT_SLID, () => this.to(index))\n return\n }\n\n const activeIndex = this._getItemIndex(this._getActive())\n if (activeIndex === index) {\n return\n }\n\n const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV\n\n this._slide(order, items[index])\n }\n\n dispose() {\n if (this._swipeHelper) {\n this._swipeHelper.dispose()\n }\n\n super.dispose()\n }\n\n // Private\n _configAfterMerge(config) {\n config.defaultInterval = config.interval\n return config\n }\n\n _addEventListeners() {\n if (this._config.keyboard) {\n EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event))\n }\n\n if (this._config.pause === 'hover') {\n EventHandler.on(this._element, EVENT_MOUSEENTER, () => this.pause())\n EventHandler.on(this._element, EVENT_MOUSELEAVE, () => this._maybeEnableCycle())\n }\n\n if (this._config.touch && Swipe.isSupported()) {\n this._addTouchEventListeners()\n }\n }\n\n _addTouchEventListeners() {\n for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {\n EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault())\n }\n\n const endCallBack = () => {\n if (this._config.pause !== 'hover') {\n return\n }\n\n // If it's a touch-enabled device, mouseenter/leave are fired as\n // part of the mouse compatibility events on first tap - the carousel\n // would stop cycling until user tapped out of it;\n // here, we listen for touchend, explicitly pause the carousel\n // (as if it's the second time we tap on it, mouseenter compat event\n // is NOT fired) and after a timeout (to allow for mouse compatibility\n // events to fire) we explicitly restart cycling\n\n this.pause()\n if (this.touchTimeout) {\n clearTimeout(this.touchTimeout)\n }\n\n this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval)\n }\n\n const swipeConfig = {\n leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)),\n rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)),\n endCallback: endCallBack\n }\n\n this._swipeHelper = new Swipe(this._element, swipeConfig)\n }\n\n _keydown(event) {\n if (/input|textarea/i.test(event.target.tagName)) {\n return\n }\n\n const direction = KEY_TO_DIRECTION[event.key]\n if (direction) {\n event.preventDefault()\n this._slide(this._directionToOrder(direction))\n }\n }\n\n _getItemIndex(element) {\n return this._getItems().indexOf(element)\n }\n\n _setActiveIndicatorElement(index) {\n if (!this._indicatorsElement) {\n return\n }\n\n const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement)\n\n activeIndicator.classList.remove(CLASS_NAME_ACTIVE)\n activeIndicator.removeAttribute('aria-current')\n\n const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to=\"${index}\"]`, this._indicatorsElement)\n\n if (newActiveIndicator) {\n newActiveIndicator.classList.add(CLASS_NAME_ACTIVE)\n newActiveIndicator.setAttribute('aria-current', 'true')\n }\n }\n\n _updateInterval() {\n const element = this._activeElement || this._getActive()\n\n if (!element) {\n return\n }\n\n const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10)\n\n this._config.interval = elementInterval || this._config.defaultInterval\n }\n\n _slide(order, element = null) {\n if (this._isSliding) {\n return\n }\n\n const activeElement = this._getActive()\n const isNext = order === ORDER_NEXT\n const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap)\n\n if (nextElement === activeElement) {\n return\n }\n\n const nextElementIndex = this._getItemIndex(nextElement)\n\n const triggerEvent = eventName => {\n return EventHandler.trigger(this._element, eventName, {\n relatedTarget: nextElement,\n direction: this._orderToDirection(order),\n from: this._getItemIndex(activeElement),\n to: nextElementIndex\n })\n }\n\n const slideEvent = triggerEvent(EVENT_SLIDE)\n\n if (slideEvent.defaultPrevented) {\n return\n }\n\n if (!activeElement || !nextElement) {\n // Some weirdness is happening, so we bail\n // TODO: change tests that use empty divs to avoid this check\n return\n }\n\n const isCycling = Boolean(this._interval)\n this.pause()\n\n this._isSliding = true\n\n this._setActiveIndicatorElement(nextElementIndex)\n this._activeElement = nextElement\n\n const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END\n const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV\n\n nextElement.classList.add(orderClassName)\n\n reflow(nextElement)\n\n activeElement.classList.add(directionalClassName)\n nextElement.classList.add(directionalClassName)\n\n const completeCallBack = () => {\n nextElement.classList.remove(directionalClassName, orderClassName)\n nextElement.classList.add(CLASS_NAME_ACTIVE)\n\n activeElement.classList.remove(CLASS_NAME_ACTIVE, orderClassName, directionalClassName)\n\n this._isSliding = false\n\n triggerEvent(EVENT_SLID)\n }\n\n this._queueCallback(completeCallBack, activeElement, this._isAnimated())\n\n if (isCycling) {\n this.cycle()\n }\n }\n\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_SLIDE)\n }\n\n _getActive() {\n return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)\n }\n\n _getItems() {\n return SelectorEngine.find(SELECTOR_ITEM, this._element)\n }\n\n _clearInterval() {\n if (this._interval) {\n clearInterval(this._interval)\n this._interval = null\n }\n }\n\n _directionToOrder(direction) {\n if (isRTL()) {\n return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT\n }\n\n return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV\n }\n\n _orderToDirection(order) {\n if (isRTL()) {\n return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT\n }\n\n return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Carousel.getOrCreateInstance(this, config)\n\n if (typeof config === 'number') {\n data.to(config)\n return\n }\n\n if (typeof config === 'string') {\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n }\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this)\n\n if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {\n return\n }\n\n event.preventDefault()\n\n const carousel = Carousel.getOrCreateInstance(target)\n const slideIndex = this.getAttribute('data-bs-slide-to')\n\n if (slideIndex) {\n carousel.to(slideIndex)\n carousel._maybeEnableCycle()\n return\n }\n\n if (Manipulator.getDataAttribute(this, 'slide') === 'next') {\n carousel.next()\n carousel._maybeEnableCycle()\n return\n }\n\n carousel.prev()\n carousel._maybeEnableCycle()\n})\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE)\n\n for (const carousel of carousels) {\n Carousel.getOrCreateInstance(carousel)\n }\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Carousel)\n\nexport default Carousel\n"],"names":["NAME","DATA_KEY","EVENT_KEY","DATA_API_KEY","ARROW_LEFT_KEY","ARROW_RIGHT_KEY","TOUCHEVENT_COMPAT_WAIT","ORDER_NEXT","ORDER_PREV","DIRECTION_LEFT","DIRECTION_RIGHT","EVENT_SLIDE","EVENT_SLID","EVENT_KEYDOWN","EVENT_MOUSEENTER","EVENT_MOUSELEAVE","EVENT_DRAG_START","EVENT_LOAD_DATA_API","EVENT_CLICK_DATA_API","CLASS_NAME_CAROUSEL","CLASS_NAME_ACTIVE","CLASS_NAME_SLIDE","CLASS_NAME_END","CLASS_NAME_START","CLASS_NAME_NEXT","CLASS_NAME_PREV","SELECTOR_ACTIVE","SELECTOR_ITEM","SELECTOR_ACTIVE_ITEM","SELECTOR_ITEM_IMG","SELECTOR_INDICATORS","SELECTOR_DATA_SLIDE","SELECTOR_DATA_RIDE","KEY_TO_DIRECTION","Default","interval","keyboard","pause","ride","touch","wrap","DefaultType","Carousel","BaseComponent","constructor","element","config","_interval","_activeElement","_isSliding","touchTimeout","_swipeHelper","_indicatorsElement","SelectorEngine","findOne","_element","_addEventListeners","_config","cycle","next","_slide","nextWhenVisible","document","hidden","isVisible","prev","triggerTransitionEnd","_clearInterval","_updateInterval","setInterval","_maybeEnableCycle","EventHandler","one","to","index","items","_getItems","length","activeIndex","_getItemIndex","_getActive","order","dispose","_configAfterMerge","defaultInterval","on","event","_keydown","Swipe","isSupported","_addTouchEventListeners","img","find","preventDefault","endCallBack","clearTimeout","setTimeout","swipeConfig","leftCallback","_directionToOrder","rightCallback","endCallback","test","target","tagName","direction","key","indexOf","_setActiveIndicatorElement","activeIndicator","classList","remove","removeAttribute","newActiveIndicator","add","setAttribute","elementInterval","Number","parseInt","getAttribute","activeElement","isNext","nextElement","getNextActiveElement","nextElementIndex","triggerEvent","eventName","trigger","relatedTarget","_orderToDirection","from","slideEvent","defaultPrevented","isCycling","Boolean","directionalClassName","orderClassName","reflow","completeCallBack","_queueCallback","_isAnimated","contains","clearInterval","isRTL","jQueryInterface","each","data","getOrCreateInstance","undefined","startsWith","TypeError","getElementFromSelector","carousel","slideIndex","Manipulator","getDataAttribute","window","carousels","defineJQueryPlugin"],"mappings":";;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;;;EAgBA;EACA;EACA;;EAEA,MAAMA,IAAI,GAAG,UAAU,CAAA;EACvB,MAAMC,QAAQ,GAAG,aAAa,CAAA;EAC9B,MAAMC,SAAS,GAAI,CAAGD,CAAAA,EAAAA,QAAS,CAAC,CAAA,CAAA;EAChC,MAAME,YAAY,GAAG,WAAW,CAAA;EAEhC,MAAMC,cAAc,GAAG,WAAW,CAAA;EAClC,MAAMC,eAAe,GAAG,YAAY,CAAA;EACpC,MAAMC,sBAAsB,GAAG,GAAG,CAAC;;EAEnC,MAAMC,UAAU,GAAG,MAAM,CAAA;EACzB,MAAMC,UAAU,GAAG,MAAM,CAAA;EACzB,MAAMC,cAAc,GAAG,MAAM,CAAA;EAC7B,MAAMC,eAAe,GAAG,OAAO,CAAA;EAE/B,MAAMC,WAAW,GAAI,CAAOT,KAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EACvC,MAAMU,UAAU,GAAI,CAAMV,IAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EACrC,MAAMW,aAAa,GAAI,CAASX,OAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EAC3C,MAAMY,gBAAgB,GAAI,CAAYZ,UAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EACjD,MAAMa,gBAAgB,GAAI,CAAYb,UAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EACjD,MAAMc,gBAAgB,GAAI,CAAWd,SAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EAChD,MAAMe,mBAAmB,GAAI,CAAA,IAAA,EAAMf,SAAU,CAAA,EAAEC,YAAa,CAAC,CAAA,CAAA;EAC7D,MAAMe,oBAAoB,GAAI,CAAA,KAAA,EAAOhB,SAAU,CAAA,EAAEC,YAAa,CAAC,CAAA,CAAA;EAE/D,MAAMgB,mBAAmB,GAAG,UAAU,CAAA;EACtC,MAAMC,iBAAiB,GAAG,QAAQ,CAAA;EAClC,MAAMC,gBAAgB,GAAG,OAAO,CAAA;EAChC,MAAMC,cAAc,GAAG,mBAAmB,CAAA;EAC1C,MAAMC,gBAAgB,GAAG,qBAAqB,CAAA;EAC9C,MAAMC,eAAe,GAAG,oBAAoB,CAAA;EAC5C,MAAMC,eAAe,GAAG,oBAAoB,CAAA;EAE5C,MAAMC,eAAe,GAAG,SAAS,CAAA;EACjC,MAAMC,aAAa,GAAG,gBAAgB,CAAA;EACtC,MAAMC,oBAAoB,GAAGF,eAAe,GAAGC,aAAa,CAAA;EAC5D,MAAME,iBAAiB,GAAG,oBAAoB,CAAA;EAC9C,MAAMC,mBAAmB,GAAG,sBAAsB,CAAA;EAClD,MAAMC,mBAAmB,GAAG,qCAAqC,CAAA;EACjE,MAAMC,kBAAkB,GAAG,2BAA2B,CAAA;EAEtD,MAAMC,gBAAgB,GAAG;IACvB,CAAC7B,cAAc,GAAGM,eAAe;EACjC,EAAA,CAACL,eAAe,GAAGI,cAAAA;EACrB,CAAC,CAAA;EAED,MAAMyB,OAAO,GAAG;EACdC,EAAAA,QAAQ,EAAE,IAAI;EACdC,EAAAA,QAAQ,EAAE,IAAI;EACdC,EAAAA,KAAK,EAAE,OAAO;EACdC,EAAAA,IAAI,EAAE,KAAK;EACXC,EAAAA,KAAK,EAAE,IAAI;EACXC,EAAAA,IAAI,EAAE,IAAA;EACR,CAAC,CAAA;EAED,MAAMC,WAAW,GAAG;EAClBN,EAAAA,QAAQ,EAAE,kBAAkB;EAAE;EAC9BC,EAAAA,QAAQ,EAAE,SAAS;EACnBC,EAAAA,KAAK,EAAE,kBAAkB;EACzBC,EAAAA,IAAI,EAAE,kBAAkB;EACxBC,EAAAA,KAAK,EAAE,SAAS;EAChBC,EAAAA,IAAI,EAAE,SAAA;EACR,CAAC,CAAA;;EAED;EACA;EACA;;EAEA,MAAME,QAAQ,SAASC,aAAa,CAAC;EACnCC,EAAAA,WAAWA,CAACC,OAAO,EAAEC,MAAM,EAAE;EAC3B,IAAA,KAAK,CAACD,OAAO,EAAEC,MAAM,CAAC,CAAA;MAEtB,IAAI,CAACC,SAAS,GAAG,IAAI,CAAA;MACrB,IAAI,CAACC,cAAc,GAAG,IAAI,CAAA;MAC1B,IAAI,CAACC,UAAU,GAAG,KAAK,CAAA;MACvB,IAAI,CAACC,YAAY,GAAG,IAAI,CAAA;MACxB,IAAI,CAACC,YAAY,GAAG,IAAI,CAAA;EAExB,IAAA,IAAI,CAACC,kBAAkB,GAAGC,cAAc,CAACC,OAAO,CAACxB,mBAAmB,EAAE,IAAI,CAACyB,QAAQ,CAAC,CAAA;MACpF,IAAI,CAACC,kBAAkB,EAAE,CAAA;EAEzB,IAAA,IAAI,IAAI,CAACC,OAAO,CAACnB,IAAI,KAAKnB,mBAAmB,EAAE;QAC7C,IAAI,CAACuC,KAAK,EAAE,CAAA;EACd,KAAA;EACF,GAAA;;EAEA;IACA,WAAWxB,OAAOA,GAAG;EACnB,IAAA,OAAOA,OAAO,CAAA;EAChB,GAAA;IAEA,WAAWO,WAAWA,GAAG;EACvB,IAAA,OAAOA,WAAW,CAAA;EACpB,GAAA;IAEA,WAAWzC,IAAIA,GAAG;EAChB,IAAA,OAAOA,IAAI,CAAA;EACb,GAAA;;EAEA;EACA2D,EAAAA,IAAIA,GAAG;EACL,IAAA,IAAI,CAACC,MAAM,CAACrD,UAAU,CAAC,CAAA;EACzB,GAAA;EAEAsD,EAAAA,eAAeA,GAAG;EAChB;EACA;EACA;MACA,IAAI,CAACC,QAAQ,CAACC,MAAM,IAAIC,kBAAS,CAAC,IAAI,CAACT,QAAQ,CAAC,EAAE;QAChD,IAAI,CAACI,IAAI,EAAE,CAAA;EACb,KAAA;EACF,GAAA;EAEAM,EAAAA,IAAIA,GAAG;EACL,IAAA,IAAI,CAACL,MAAM,CAACpD,UAAU,CAAC,CAAA;EACzB,GAAA;EAEA6B,EAAAA,KAAKA,GAAG;MACN,IAAI,IAAI,CAACY,UAAU,EAAE;EACnBiB,MAAAA,6BAAoB,CAAC,IAAI,CAACX,QAAQ,CAAC,CAAA;EACrC,KAAA;MAEA,IAAI,CAACY,cAAc,EAAE,CAAA;EACvB,GAAA;EAEAT,EAAAA,KAAKA,GAAG;MACN,IAAI,CAACS,cAAc,EAAE,CAAA;MACrB,IAAI,CAACC,eAAe,EAAE,CAAA;EAEtB,IAAA,IAAI,CAACrB,SAAS,GAAGsB,WAAW,CAAC,MAAM,IAAI,CAACR,eAAe,EAAE,EAAE,IAAI,CAACJ,OAAO,CAACtB,QAAQ,CAAC,CAAA;EACnF,GAAA;EAEAmC,EAAAA,iBAAiBA,GAAG;EAClB,IAAA,IAAI,CAAC,IAAI,CAACb,OAAO,CAACnB,IAAI,EAAE;EACtB,MAAA,OAAA;EACF,KAAA;MAEA,IAAI,IAAI,CAACW,UAAU,EAAE;EACnBsB,MAAAA,YAAY,CAACC,GAAG,CAAC,IAAI,CAACjB,QAAQ,EAAE3C,UAAU,EAAE,MAAM,IAAI,CAAC8C,KAAK,EAAE,CAAC,CAAA;EAC/D,MAAA,OAAA;EACF,KAAA;MAEA,IAAI,CAACA,KAAK,EAAE,CAAA;EACd,GAAA;IAEAe,EAAEA,CAACC,KAAK,EAAE;EACR,IAAA,MAAMC,KAAK,GAAG,IAAI,CAACC,SAAS,EAAE,CAAA;MAC9B,IAAIF,KAAK,GAAGC,KAAK,CAACE,MAAM,GAAG,CAAC,IAAIH,KAAK,GAAG,CAAC,EAAE;EACzC,MAAA,OAAA;EACF,KAAA;MAEA,IAAI,IAAI,CAACzB,UAAU,EAAE;EACnBsB,MAAAA,YAAY,CAACC,GAAG,CAAC,IAAI,CAACjB,QAAQ,EAAE3C,UAAU,EAAE,MAAM,IAAI,CAAC6D,EAAE,CAACC,KAAK,CAAC,CAAC,CAAA;EACjE,MAAA,OAAA;EACF,KAAA;MAEA,MAAMI,WAAW,GAAG,IAAI,CAACC,aAAa,CAAC,IAAI,CAACC,UAAU,EAAE,CAAC,CAAA;MACzD,IAAIF,WAAW,KAAKJ,KAAK,EAAE;EACzB,MAAA,OAAA;EACF,KAAA;MAEA,MAAMO,KAAK,GAAGP,KAAK,GAAGI,WAAW,GAAGvE,UAAU,GAAGC,UAAU,CAAA;MAE3D,IAAI,CAACoD,MAAM,CAACqB,KAAK,EAAEN,KAAK,CAACD,KAAK,CAAC,CAAC,CAAA;EAClC,GAAA;EAEAQ,EAAAA,OAAOA,GAAG;MACR,IAAI,IAAI,CAAC/B,YAAY,EAAE;EACrB,MAAA,IAAI,CAACA,YAAY,CAAC+B,OAAO,EAAE,CAAA;EAC7B,KAAA;MAEA,KAAK,CAACA,OAAO,EAAE,CAAA;EACjB,GAAA;;EAEA;IACAC,iBAAiBA,CAACrC,MAAM,EAAE;EACxBA,IAAAA,MAAM,CAACsC,eAAe,GAAGtC,MAAM,CAACX,QAAQ,CAAA;EACxC,IAAA,OAAOW,MAAM,CAAA;EACf,GAAA;EAEAU,EAAAA,kBAAkBA,GAAG;EACnB,IAAA,IAAI,IAAI,CAACC,OAAO,CAACrB,QAAQ,EAAE;EACzBmC,MAAAA,YAAY,CAACc,EAAE,CAAC,IAAI,CAAC9B,QAAQ,EAAE1C,aAAa,EAAEyE,KAAK,IAAI,IAAI,CAACC,QAAQ,CAACD,KAAK,CAAC,CAAC,CAAA;EAC9E,KAAA;EAEA,IAAA,IAAI,IAAI,CAAC7B,OAAO,CAACpB,KAAK,KAAK,OAAO,EAAE;EAClCkC,MAAAA,YAAY,CAACc,EAAE,CAAC,IAAI,CAAC9B,QAAQ,EAAEzC,gBAAgB,EAAE,MAAM,IAAI,CAACuB,KAAK,EAAE,CAAC,CAAA;EACpEkC,MAAAA,YAAY,CAACc,EAAE,CAAC,IAAI,CAAC9B,QAAQ,EAAExC,gBAAgB,EAAE,MAAM,IAAI,CAACuD,iBAAiB,EAAE,CAAC,CAAA;EAClF,KAAA;MAEA,IAAI,IAAI,CAACb,OAAO,CAAClB,KAAK,IAAIiD,KAAK,CAACC,WAAW,EAAE,EAAE;QAC7C,IAAI,CAACC,uBAAuB,EAAE,CAAA;EAChC,KAAA;EACF,GAAA;EAEAA,EAAAA,uBAAuBA,GAAG;EACxB,IAAA,KAAK,MAAMC,GAAG,IAAItC,cAAc,CAACuC,IAAI,CAAC/D,iBAAiB,EAAE,IAAI,CAAC0B,QAAQ,CAAC,EAAE;EACvEgB,MAAAA,YAAY,CAACc,EAAE,CAACM,GAAG,EAAE3E,gBAAgB,EAAEsE,KAAK,IAAIA,KAAK,CAACO,cAAc,EAAE,CAAC,CAAA;EACzE,KAAA;MAEA,MAAMC,WAAW,GAAGA,MAAM;EACxB,MAAA,IAAI,IAAI,CAACrC,OAAO,CAACpB,KAAK,KAAK,OAAO,EAAE;EAClC,QAAA,OAAA;EACF,OAAA;;EAEA;EACA;EACA;EACA;EACA;EACA;EACA;;QAEA,IAAI,CAACA,KAAK,EAAE,CAAA;QACZ,IAAI,IAAI,CAACa,YAAY,EAAE;EACrB6C,QAAAA,YAAY,CAAC,IAAI,CAAC7C,YAAY,CAAC,CAAA;EACjC,OAAA;EAEA,MAAA,IAAI,CAACA,YAAY,GAAG8C,UAAU,CAAC,MAAM,IAAI,CAAC1B,iBAAiB,EAAE,EAAEhE,sBAAsB,GAAG,IAAI,CAACmD,OAAO,CAACtB,QAAQ,CAAC,CAAA;OAC/G,CAAA;EAED,IAAA,MAAM8D,WAAW,GAAG;EAClBC,MAAAA,YAAY,EAAEA,MAAM,IAAI,CAACtC,MAAM,CAAC,IAAI,CAACuC,iBAAiB,CAAC1F,cAAc,CAAC,CAAC;EACvE2F,MAAAA,aAAa,EAAEA,MAAM,IAAI,CAACxC,MAAM,CAAC,IAAI,CAACuC,iBAAiB,CAACzF,eAAe,CAAC,CAAC;EACzE2F,MAAAA,WAAW,EAAEP,WAAAA;OACd,CAAA;MAED,IAAI,CAAC3C,YAAY,GAAG,IAAIqC,KAAK,CAAC,IAAI,CAACjC,QAAQ,EAAE0C,WAAW,CAAC,CAAA;EAC3D,GAAA;IAEAV,QAAQA,CAACD,KAAK,EAAE;MACd,IAAI,iBAAiB,CAACgB,IAAI,CAAChB,KAAK,CAACiB,MAAM,CAACC,OAAO,CAAC,EAAE;EAChD,MAAA,OAAA;EACF,KAAA;EAEA,IAAA,MAAMC,SAAS,GAAGxE,gBAAgB,CAACqD,KAAK,CAACoB,GAAG,CAAC,CAAA;EAC7C,IAAA,IAAID,SAAS,EAAE;QACbnB,KAAK,CAACO,cAAc,EAAE,CAAA;QACtB,IAAI,CAACjC,MAAM,CAAC,IAAI,CAACuC,iBAAiB,CAACM,SAAS,CAAC,CAAC,CAAA;EAChD,KAAA;EACF,GAAA;IAEA1B,aAAaA,CAAClC,OAAO,EAAE;MACrB,OAAO,IAAI,CAAC+B,SAAS,EAAE,CAAC+B,OAAO,CAAC9D,OAAO,CAAC,CAAA;EAC1C,GAAA;IAEA+D,0BAA0BA,CAAClC,KAAK,EAAE;EAChC,IAAA,IAAI,CAAC,IAAI,CAACtB,kBAAkB,EAAE;EAC5B,MAAA,OAAA;EACF,KAAA;MAEA,MAAMyD,eAAe,GAAGxD,cAAc,CAACC,OAAO,CAAC5B,eAAe,EAAE,IAAI,CAAC0B,kBAAkB,CAAC,CAAA;EAExFyD,IAAAA,eAAe,CAACC,SAAS,CAACC,MAAM,CAAC3F,iBAAiB,CAAC,CAAA;EACnDyF,IAAAA,eAAe,CAACG,eAAe,CAAC,cAAc,CAAC,CAAA;EAE/C,IAAA,MAAMC,kBAAkB,GAAG5D,cAAc,CAACC,OAAO,CAAE,CAAqBoB,mBAAAA,EAAAA,KAAM,CAAG,EAAA,CAAA,EAAE,IAAI,CAACtB,kBAAkB,CAAC,CAAA;EAE3G,IAAA,IAAI6D,kBAAkB,EAAE;EACtBA,MAAAA,kBAAkB,CAACH,SAAS,CAACI,GAAG,CAAC9F,iBAAiB,CAAC,CAAA;EACnD6F,MAAAA,kBAAkB,CAACE,YAAY,CAAC,cAAc,EAAE,MAAM,CAAC,CAAA;EACzD,KAAA;EACF,GAAA;EAEA/C,EAAAA,eAAeA,GAAG;MAChB,MAAMvB,OAAO,GAAG,IAAI,CAACG,cAAc,IAAI,IAAI,CAACgC,UAAU,EAAE,CAAA;MAExD,IAAI,CAACnC,OAAO,EAAE;EACZ,MAAA,OAAA;EACF,KAAA;EAEA,IAAA,MAAMuE,eAAe,GAAGC,MAAM,CAACC,QAAQ,CAACzE,OAAO,CAAC0E,YAAY,CAAC,kBAAkB,CAAC,EAAE,EAAE,CAAC,CAAA;MAErF,IAAI,CAAC9D,OAAO,CAACtB,QAAQ,GAAGiF,eAAe,IAAI,IAAI,CAAC3D,OAAO,CAAC2B,eAAe,CAAA;EACzE,GAAA;EAEAxB,EAAAA,MAAMA,CAACqB,KAAK,EAAEpC,OAAO,GAAG,IAAI,EAAE;MAC5B,IAAI,IAAI,CAACI,UAAU,EAAE;EACnB,MAAA,OAAA;EACF,KAAA;EAEA,IAAA,MAAMuE,aAAa,GAAG,IAAI,CAACxC,UAAU,EAAE,CAAA;EACvC,IAAA,MAAMyC,MAAM,GAAGxC,KAAK,KAAK1E,UAAU,CAAA;MACnC,MAAMmH,WAAW,GAAG7E,OAAO,IAAI8E,6BAAoB,CAAC,IAAI,CAAC/C,SAAS,EAAE,EAAE4C,aAAa,EAAEC,MAAM,EAAE,IAAI,CAAChE,OAAO,CAACjB,IAAI,CAAC,CAAA;MAE/G,IAAIkF,WAAW,KAAKF,aAAa,EAAE;EACjC,MAAA,OAAA;EACF,KAAA;EAEA,IAAA,MAAMI,gBAAgB,GAAG,IAAI,CAAC7C,aAAa,CAAC2C,WAAW,CAAC,CAAA;MAExD,MAAMG,YAAY,GAAGC,SAAS,IAAI;QAChC,OAAOvD,YAAY,CAACwD,OAAO,CAAC,IAAI,CAACxE,QAAQ,EAAEuE,SAAS,EAAE;EACpDE,QAAAA,aAAa,EAAEN,WAAW;EAC1BjB,QAAAA,SAAS,EAAE,IAAI,CAACwB,iBAAiB,CAAChD,KAAK,CAAC;EACxCiD,QAAAA,IAAI,EAAE,IAAI,CAACnD,aAAa,CAACyC,aAAa,CAAC;EACvC/C,QAAAA,EAAE,EAAEmD,gBAAAA;EACN,OAAC,CAAC,CAAA;OACH,CAAA;EAED,IAAA,MAAMO,UAAU,GAAGN,YAAY,CAAClH,WAAW,CAAC,CAAA;MAE5C,IAAIwH,UAAU,CAACC,gBAAgB,EAAE;EAC/B,MAAA,OAAA;EACF,KAAA;EAEA,IAAA,IAAI,CAACZ,aAAa,IAAI,CAACE,WAAW,EAAE;EAClC;EACA;EACA,MAAA,OAAA;EACF,KAAA;EAEA,IAAA,MAAMW,SAAS,GAAGC,OAAO,CAAC,IAAI,CAACvF,SAAS,CAAC,CAAA;MACzC,IAAI,CAACV,KAAK,EAAE,CAAA;MAEZ,IAAI,CAACY,UAAU,GAAG,IAAI,CAAA;EAEtB,IAAA,IAAI,CAAC2D,0BAA0B,CAACgB,gBAAgB,CAAC,CAAA;MACjD,IAAI,CAAC5E,cAAc,GAAG0E,WAAW,CAAA;EAEjC,IAAA,MAAMa,oBAAoB,GAAGd,MAAM,GAAGlG,gBAAgB,GAAGD,cAAc,CAAA;EACvE,IAAA,MAAMkH,cAAc,GAAGf,MAAM,GAAGjG,eAAe,GAAGC,eAAe,CAAA;EAEjEiG,IAAAA,WAAW,CAACZ,SAAS,CAACI,GAAG,CAACsB,cAAc,CAAC,CAAA;MAEzCC,eAAM,CAACf,WAAW,CAAC,CAAA;EAEnBF,IAAAA,aAAa,CAACV,SAAS,CAACI,GAAG,CAACqB,oBAAoB,CAAC,CAAA;EACjDb,IAAAA,WAAW,CAACZ,SAAS,CAACI,GAAG,CAACqB,oBAAoB,CAAC,CAAA;MAE/C,MAAMG,gBAAgB,GAAGA,MAAM;QAC7BhB,WAAW,CAACZ,SAAS,CAACC,MAAM,CAACwB,oBAAoB,EAAEC,cAAc,CAAC,CAAA;EAClEd,MAAAA,WAAW,CAACZ,SAAS,CAACI,GAAG,CAAC9F,iBAAiB,CAAC,CAAA;QAE5CoG,aAAa,CAACV,SAAS,CAACC,MAAM,CAAC3F,iBAAiB,EAAEoH,cAAc,EAAED,oBAAoB,CAAC,CAAA;QAEvF,IAAI,CAACtF,UAAU,GAAG,KAAK,CAAA;QAEvB4E,YAAY,CAACjH,UAAU,CAAC,CAAA;OACzB,CAAA;EAED,IAAA,IAAI,CAAC+H,cAAc,CAACD,gBAAgB,EAAElB,aAAa,EAAE,IAAI,CAACoB,WAAW,EAAE,CAAC,CAAA;EAExE,IAAA,IAAIP,SAAS,EAAE;QACb,IAAI,CAAC3E,KAAK,EAAE,CAAA;EACd,KAAA;EACF,GAAA;EAEAkF,EAAAA,WAAWA,GAAG;MACZ,OAAO,IAAI,CAACrF,QAAQ,CAACuD,SAAS,CAAC+B,QAAQ,CAACxH,gBAAgB,CAAC,CAAA;EAC3D,GAAA;EAEA2D,EAAAA,UAAUA,GAAG;MACX,OAAO3B,cAAc,CAACC,OAAO,CAAC1B,oBAAoB,EAAE,IAAI,CAAC2B,QAAQ,CAAC,CAAA;EACpE,GAAA;EAEAqB,EAAAA,SAASA,GAAG;MACV,OAAOvB,cAAc,CAACuC,IAAI,CAACjE,aAAa,EAAE,IAAI,CAAC4B,QAAQ,CAAC,CAAA;EAC1D,GAAA;EAEAY,EAAAA,cAAcA,GAAG;MACf,IAAI,IAAI,CAACpB,SAAS,EAAE;EAClB+F,MAAAA,aAAa,CAAC,IAAI,CAAC/F,SAAS,CAAC,CAAA;QAC7B,IAAI,CAACA,SAAS,GAAG,IAAI,CAAA;EACvB,KAAA;EACF,GAAA;IAEAoD,iBAAiBA,CAACM,SAAS,EAAE;MAC3B,IAAIsC,cAAK,EAAE,EAAE;EACX,MAAA,OAAOtC,SAAS,KAAKhG,cAAc,GAAGD,UAAU,GAAGD,UAAU,CAAA;EAC/D,KAAA;EAEA,IAAA,OAAOkG,SAAS,KAAKhG,cAAc,GAAGF,UAAU,GAAGC,UAAU,CAAA;EAC/D,GAAA;IAEAyH,iBAAiBA,CAAChD,KAAK,EAAE;MACvB,IAAI8D,cAAK,EAAE,EAAE;EACX,MAAA,OAAO9D,KAAK,KAAKzE,UAAU,GAAGC,cAAc,GAAGC,eAAe,CAAA;EAChE,KAAA;EAEA,IAAA,OAAOuE,KAAK,KAAKzE,UAAU,GAAGE,eAAe,GAAGD,cAAc,CAAA;EAChE,GAAA;;EAEA;IACA,OAAOuI,eAAeA,CAAClG,MAAM,EAAE;EAC7B,IAAA,OAAO,IAAI,CAACmG,IAAI,CAAC,YAAY;QAC3B,MAAMC,IAAI,GAAGxG,QAAQ,CAACyG,mBAAmB,CAAC,IAAI,EAAErG,MAAM,CAAC,CAAA;EAEvD,MAAA,IAAI,OAAOA,MAAM,KAAK,QAAQ,EAAE;EAC9BoG,QAAAA,IAAI,CAACzE,EAAE,CAAC3B,MAAM,CAAC,CAAA;EACf,QAAA,OAAA;EACF,OAAA;EAEA,MAAA,IAAI,OAAOA,MAAM,KAAK,QAAQ,EAAE;EAC9B,QAAA,IAAIoG,IAAI,CAACpG,MAAM,CAAC,KAAKsG,SAAS,IAAItG,MAAM,CAACuG,UAAU,CAAC,GAAG,CAAC,IAAIvG,MAAM,KAAK,aAAa,EAAE;EACpF,UAAA,MAAM,IAAIwG,SAAS,CAAE,CAAmBxG,iBAAAA,EAAAA,MAAO,GAAE,CAAC,CAAA;EACpD,SAAA;EAEAoG,QAAAA,IAAI,CAACpG,MAAM,CAAC,EAAE,CAAA;EAChB,OAAA;EACF,KAAC,CAAC,CAAA;EACJ,GAAA;EACF,CAAA;;EAEA;EACA;EACA;;EAEAyB,YAAY,CAACc,EAAE,CAACvB,QAAQ,EAAE5C,oBAAoB,EAAEa,mBAAmB,EAAE,UAAUuD,KAAK,EAAE;EACpF,EAAA,MAAMiB,MAAM,GAAGlD,cAAc,CAACkG,sBAAsB,CAAC,IAAI,CAAC,CAAA;EAE1D,EAAA,IAAI,CAAChD,MAAM,IAAI,CAACA,MAAM,CAACO,SAAS,CAAC+B,QAAQ,CAAC1H,mBAAmB,CAAC,EAAE;EAC9D,IAAA,OAAA;EACF,GAAA;IAEAmE,KAAK,CAACO,cAAc,EAAE,CAAA;EAEtB,EAAA,MAAM2D,QAAQ,GAAG9G,QAAQ,CAACyG,mBAAmB,CAAC5C,MAAM,CAAC,CAAA;EACrD,EAAA,MAAMkD,UAAU,GAAG,IAAI,CAAClC,YAAY,CAAC,kBAAkB,CAAC,CAAA;EAExD,EAAA,IAAIkC,UAAU,EAAE;EACdD,IAAAA,QAAQ,CAAC/E,EAAE,CAACgF,UAAU,CAAC,CAAA;MACvBD,QAAQ,CAAClF,iBAAiB,EAAE,CAAA;EAC5B,IAAA,OAAA;EACF,GAAA;IAEA,IAAIoF,WAAW,CAACC,gBAAgB,CAAC,IAAI,EAAE,OAAO,CAAC,KAAK,MAAM,EAAE;MAC1DH,QAAQ,CAAC7F,IAAI,EAAE,CAAA;MACf6F,QAAQ,CAAClF,iBAAiB,EAAE,CAAA;EAC5B,IAAA,OAAA;EACF,GAAA;IAEAkF,QAAQ,CAACvF,IAAI,EAAE,CAAA;IACfuF,QAAQ,CAAClF,iBAAiB,EAAE,CAAA;EAC9B,CAAC,CAAC,CAAA;EAEFC,YAAY,CAACc,EAAE,CAACuE,MAAM,EAAE3I,mBAAmB,EAAE,MAAM;EACjD,EAAA,MAAM4I,SAAS,GAAGxG,cAAc,CAACuC,IAAI,CAAC5D,kBAAkB,CAAC,CAAA;EAEzD,EAAA,KAAK,MAAMwH,QAAQ,IAAIK,SAAS,EAAE;EAChCnH,IAAAA,QAAQ,CAACyG,mBAAmB,CAACK,QAAQ,CAAC,CAAA;EACxC,GAAA;EACF,CAAC,CAAC,CAAA;;EAEF;EACA;EACA;;AAEAM,6BAAkB,CAACpH,QAAQ,CAAC;;;;;;;;"} \ No newline at end of file
diff --git a/js/dist/collapse.js b/js/dist/collapse.js
index ebd0ae26d..fd915f06f 100644
--- a/js/dist/collapse.js
+++ b/js/dist/collapse.js
@@ -1,199 +1,30 @@
/*!
- * Bootstrap collapse.js v5.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Bootstrap collapse.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/data.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./base-component.js')) :
- typeof define === 'function' && define.amd ? define(['./dom/data', './dom/event-handler', './dom/manipulator', './dom/selector-engine', './base-component'], factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Collapse = factory(global.Data, global.EventHandler, global.Manipulator, global.SelectorEngine, global.Base));
-})(this, (function (Data, EventHandler, Manipulator, SelectorEngine, BaseComponent) { 'use strict';
-
- const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
-
- const Data__default = /*#__PURE__*/_interopDefaultLegacy(Data);
- const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
- const Manipulator__default = /*#__PURE__*/_interopDefaultLegacy(Manipulator);
- const SelectorEngine__default = /*#__PURE__*/_interopDefaultLegacy(SelectorEngine);
- const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/selector-engine.js'), require('./util/index.js')) :
+ typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './dom/selector-engine', './util/index'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Collapse = factory(global.BaseComponent, global.EventHandler, global.SelectorEngine, global.Index));
+})(this, (function (BaseComponent, EventHandler, SelectorEngine, index_js) { 'use strict';
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/index.js
+ * Bootstrap collapse.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
- const toType = obj => {
- if (obj === null || obj === undefined) {
- return `${obj}`;
- }
-
- return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase();
- };
-
- const getSelector = element => {
- let selector = element.getAttribute('data-bs-target');
-
- if (!selector || selector === '#') {
- let hrefAttr = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes,
- // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
- // `document.querySelector` will rightfully complain it is invalid.
- // See https://github.com/twbs/bootstrap/issues/32273
-
- if (!hrefAttr || !hrefAttr.includes('#') && !hrefAttr.startsWith('.')) {
- return null;
- } // Just in case some CMS puts out a full URL with the anchor appended
-
-
- if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {
- hrefAttr = `#${hrefAttr.split('#')[1]}`;
- }
- selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null;
- }
-
- return selector;
- };
-
- const getSelectorFromElement = element => {
- const selector = getSelector(element);
-
- if (selector) {
- return document.querySelector(selector) ? selector : null;
- }
-
- return null;
- };
-
- const getElementFromSelector = element => {
- const selector = getSelector(element);
- return selector ? document.querySelector(selector) : null;
- };
-
- const isElement = obj => {
- if (!obj || typeof obj !== 'object') {
- return false;
- }
-
- if (typeof obj.jquery !== 'undefined') {
- obj = obj[0];
- }
-
- return typeof obj.nodeType !== 'undefined';
- };
-
- const getElement = obj => {
- if (isElement(obj)) {
- // it's a jQuery object or a node element
- return obj.jquery ? obj[0] : obj;
- }
-
- if (typeof obj === 'string' && obj.length > 0) {
- return document.querySelector(obj);
- }
-
- return null;
- };
-
- const typeCheckConfig = (componentName, config, configTypes) => {
- Object.keys(configTypes).forEach(property => {
- const expectedTypes = configTypes[property];
- const value = config[property];
- const valueType = value && isElement(value) ? 'element' : toType(value);
-
- if (!new RegExp(expectedTypes).test(valueType)) {
- throw new TypeError(`${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`);
- }
- });
- };
/**
- * Trick to restart an element's animation
- *
- * @param {HTMLElement} element
- * @return void
- *
- * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
- */
-
-
- const reflow = element => {
- // eslint-disable-next-line no-unused-expressions
- element.offsetHeight;
- };
-
- const getjQuery = () => {
- const {
- jQuery
- } = window;
-
- if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
- return jQuery;
- }
-
- return null;
- };
-
- const DOMContentLoadedCallbacks = [];
-
- const onDOMContentLoaded = callback => {
- if (document.readyState === 'loading') {
- // add listener on the first call when the document is in loading state
- if (!DOMContentLoadedCallbacks.length) {
- document.addEventListener('DOMContentLoaded', () => {
- DOMContentLoadedCallbacks.forEach(callback => callback());
- });
- }
-
- DOMContentLoadedCallbacks.push(callback);
- } else {
- callback();
- }
- };
-
- const defineJQueryPlugin = plugin => {
- onDOMContentLoaded(() => {
- const $ = getjQuery();
- /* istanbul ignore if */
-
- if ($) {
- const name = plugin.NAME;
- const JQUERY_NO_CONFLICT = $.fn[name];
- $.fn[name] = plugin.jQueryInterface;
- $.fn[name].Constructor = plugin;
-
- $.fn[name].noConflict = () => {
- $.fn[name] = JQUERY_NO_CONFLICT;
- return plugin.jQueryInterface;
- };
- }
- });
- };
-
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): collapse.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- /**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'collapse';
const DATA_KEY = 'bs.collapse';
const EVENT_KEY = `.${DATA_KEY}`;
const DATA_API_KEY = '.data-api';
- const Default = {
- toggle: true,
- parent: null
- };
- const DefaultType = {
- toggle: 'boolean',
- parent: '(null|element)'
- };
const EVENT_SHOW = `show${EVENT_KEY}`;
const EVENT_SHOWN = `shown${EVENT_KEY}`;
const EVENT_HIDE = `hide${EVENT_KEY}`;
@@ -209,53 +40,53 @@
const HEIGHT = 'height';
const SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing';
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="collapse"]';
+ const Default = {
+ parent: null,
+ toggle: true
+ };
+ const DefaultType = {
+ parent: '(null|element)',
+ toggle: 'boolean'
+ };
+
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
- class Collapse extends BaseComponent__default.default {
+ class Collapse extends BaseComponent {
constructor(element, config) {
- super(element);
+ super(element, config);
this._isTransitioning = false;
- this._config = this._getConfig(config);
this._triggerArray = [];
- const toggleList = SelectorEngine__default.default.find(SELECTOR_DATA_TOGGLE);
-
- for (let i = 0, len = toggleList.length; i < len; i++) {
- const elem = toggleList[i];
- const selector = getSelectorFromElement(elem);
- const filterElement = SelectorEngine__default.default.find(selector).filter(foundElem => foundElem === this._element);
-
+ const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE);
+ for (const elem of toggleList) {
+ const selector = SelectorEngine.getSelectorFromElement(elem);
+ const filterElement = SelectorEngine.find(selector).filter(foundElement => foundElement === this._element);
if (selector !== null && filterElement.length) {
- this._selector = selector;
-
this._triggerArray.push(elem);
}
}
-
this._initializeChildren();
-
if (!this._config.parent) {
this._addAriaAndCollapsedClass(this._triggerArray, this._isShown());
}
-
if (this._config.toggle) {
this.toggle();
}
- } // Getters
-
+ }
+ // Getters
static get Default() {
return Default;
}
-
+ static get DefaultType() {
+ return DefaultType;
+ }
static get NAME() {
return NAME;
- } // Public
-
+ }
+ // Public
toggle() {
if (this._isShown()) {
this.hide();
@@ -263,231 +94,154 @@
this.show();
}
}
-
show() {
if (this._isTransitioning || this._isShown()) {
return;
}
+ let activeChildren = [];
- let actives = [];
- let activesData;
-
+ // find active children
if (this._config.parent) {
- const children = SelectorEngine__default.default.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent);
- actives = SelectorEngine__default.default.find(SELECTOR_ACTIVES, this._config.parent).filter(elem => !children.includes(elem)); // remove children if greater depth
+ activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES).filter(element => element !== this._element).map(element => Collapse.getOrCreateInstance(element, {
+ toggle: false
+ }));
}
-
- const container = SelectorEngine__default.default.findOne(this._selector);
-
- if (actives.length) {
- const tempActiveData = actives.find(elem => container !== elem);
- activesData = tempActiveData ? Collapse.getInstance(tempActiveData) : null;
-
- if (activesData && activesData._isTransitioning) {
- return;
- }
+ if (activeChildren.length && activeChildren[0]._isTransitioning) {
+ return;
}
-
- const startEvent = EventHandler__default.default.trigger(this._element, EVENT_SHOW);
-
+ const startEvent = EventHandler.trigger(this._element, EVENT_SHOW);
if (startEvent.defaultPrevented) {
return;
}
-
- actives.forEach(elemActive => {
- if (container !== elemActive) {
- Collapse.getOrCreateInstance(elemActive, {
- toggle: false
- }).hide();
- }
-
- if (!activesData) {
- Data__default.default.set(elemActive, DATA_KEY, null);
- }
- });
-
+ for (const activeInstance of activeChildren) {
+ activeInstance.hide();
+ }
const dimension = this._getDimension();
-
this._element.classList.remove(CLASS_NAME_COLLAPSE);
-
this._element.classList.add(CLASS_NAME_COLLAPSING);
-
this._element.style[dimension] = 0;
-
this._addAriaAndCollapsedClass(this._triggerArray, true);
-
this._isTransitioning = true;
-
const complete = () => {
this._isTransitioning = false;
-
this._element.classList.remove(CLASS_NAME_COLLAPSING);
-
this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW);
-
this._element.style[dimension] = '';
- EventHandler__default.default.trigger(this._element, EVENT_SHOWN);
+ EventHandler.trigger(this._element, EVENT_SHOWN);
};
-
const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1);
const scrollSize = `scroll${capitalizedDimension}`;
-
this._queueCallback(complete, this._element, true);
-
this._element.style[dimension] = `${this._element[scrollSize]}px`;
}
-
hide() {
if (this._isTransitioning || !this._isShown()) {
return;
}
-
- const startEvent = EventHandler__default.default.trigger(this._element, EVENT_HIDE);
-
+ const startEvent = EventHandler.trigger(this._element, EVENT_HIDE);
if (startEvent.defaultPrevented) {
return;
}
-
const dimension = this._getDimension();
-
this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`;
- reflow(this._element);
-
+ index_js.reflow(this._element);
this._element.classList.add(CLASS_NAME_COLLAPSING);
-
this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW);
-
- const triggerArrayLength = this._triggerArray.length;
-
- for (let i = 0; i < triggerArrayLength; i++) {
- const trigger = this._triggerArray[i];
- const elem = getElementFromSelector(trigger);
-
- if (elem && !this._isShown(elem)) {
+ for (const trigger of this._triggerArray) {
+ const element = SelectorEngine.getElementFromSelector(trigger);
+ if (element && !this._isShown(element)) {
this._addAriaAndCollapsedClass([trigger], false);
}
}
-
this._isTransitioning = true;
-
const complete = () => {
this._isTransitioning = false;
-
this._element.classList.remove(CLASS_NAME_COLLAPSING);
-
this._element.classList.add(CLASS_NAME_COLLAPSE);
-
- EventHandler__default.default.trigger(this._element, EVENT_HIDDEN);
+ EventHandler.trigger(this._element, EVENT_HIDDEN);
};
-
this._element.style[dimension] = '';
-
this._queueCallback(complete, this._element, true);
}
-
_isShown(element = this._element) {
return element.classList.contains(CLASS_NAME_SHOW);
- } // Private
-
+ }
- _getConfig(config) {
- config = { ...Default,
- ...Manipulator__default.default.getDataAttributes(this._element),
- ...config
- };
+ // Private
+ _configAfterMerge(config) {
config.toggle = Boolean(config.toggle); // Coerce string values
-
- config.parent = getElement(config.parent);
- typeCheckConfig(NAME, config, DefaultType);
+ config.parent = index_js.getElement(config.parent);
return config;
}
-
_getDimension() {
return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT;
}
-
_initializeChildren() {
if (!this._config.parent) {
return;
}
-
- const children = SelectorEngine__default.default.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent);
- SelectorEngine__default.default.find(SELECTOR_DATA_TOGGLE, this._config.parent).filter(elem => !children.includes(elem)).forEach(element => {
- const selected = getElementFromSelector(element);
-
+ const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE);
+ for (const element of children) {
+ const selected = SelectorEngine.getElementFromSelector(element);
if (selected) {
this._addAriaAndCollapsedClass([element], this._isShown(selected));
}
- });
+ }
+ }
+ _getFirstLevelChildren(selector) {
+ const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent);
+ // remove children if greater depth
+ return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element));
}
-
_addAriaAndCollapsedClass(triggerArray, isOpen) {
if (!triggerArray.length) {
return;
}
+ for (const element of triggerArray) {
+ element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen);
+ element.setAttribute('aria-expanded', isOpen);
+ }
+ }
- triggerArray.forEach(elem => {
- if (isOpen) {
- elem.classList.remove(CLASS_NAME_COLLAPSED);
- } else {
- elem.classList.add(CLASS_NAME_COLLAPSED);
- }
-
- elem.setAttribute('aria-expanded', isOpen);
- });
- } // Static
-
-
+ // Static
static jQueryInterface(config) {
+ const _config = {};
+ if (typeof config === 'string' && /show|hide/.test(config)) {
+ _config.toggle = false;
+ }
return this.each(function () {
- const _config = {};
-
- if (typeof config === 'string' && /show|hide/.test(config)) {
- _config.toggle = false;
- }
-
const data = Collapse.getOrCreateInstance(this, _config);
-
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`);
}
-
data[config]();
}
});
}
-
}
+
/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
+ * Data API implementation
*/
-
- EventHandler__default.default.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
+ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
// preventDefault only for <a> elements (which change the URL) not inside the collapsible element
if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') {
event.preventDefault();
}
-
- const selector = getSelectorFromElement(this);
- const selectorElements = SelectorEngine__default.default.find(selector);
- selectorElements.forEach(element => {
+ for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) {
Collapse.getOrCreateInstance(element, {
toggle: false
}).toggle();
- });
+ }
});
+
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .Collapse to jQuery only if jQuery is present
*/
- defineJQueryPlugin(Collapse);
+ index_js.defineJQueryPlugin(Collapse);
return Collapse;
diff --git a/js/dist/collapse.js.map b/js/dist/collapse.js.map
index 032ad440c..1c766305c 100644
--- a/js/dist/collapse.js.map
+++ b/js/dist/collapse.js.map
@@ -1 +1 @@
-{"version":3,"file":"collapse.js","sources":["../src/util/index.js","../src/collapse.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n// Shoutout AngusCroll (https://goo.gl/pxwQGp)\nconst toType = obj => {\n if (obj === null || obj === undefined) {\n return `${obj}`\n }\n\n return {}.toString.call(obj).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttr = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {\n hrefAttr = `#${hrefAttr.split('#')[1]}`\n }\n\n selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null\n }\n\n return selector\n}\n\nconst getSelectorFromElement = element => {\n const selector = getSelector(element)\n\n if (selector) {\n return document.querySelector(selector) ? selector : null\n }\n\n return null\n}\n\nconst getElementFromSelector = element => {\n const selector = getSelector(element)\n\n return selector ? document.querySelector(selector) : null\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = obj => {\n if (!obj || typeof obj !== 'object') {\n return false\n }\n\n if (typeof obj.jquery !== 'undefined') {\n obj = obj[0]\n }\n\n return typeof obj.nodeType !== 'undefined'\n}\n\nconst getElement = obj => {\n if (isElement(obj)) { // it's a jQuery object or a node element\n return obj.jquery ? obj[0] : obj\n }\n\n if (typeof obj === 'string' && obj.length > 0) {\n return document.querySelector(obj)\n }\n\n return null\n}\n\nconst typeCheckConfig = (componentName, config, configTypes) => {\n Object.keys(configTypes).forEach(property => {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${componentName.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n })\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n return getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n // eslint-disable-next-line no-unused-expressions\n element.offsetHeight\n}\n\nconst getjQuery = () => {\n const { jQuery } = window\n\n if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n DOMContentLoadedCallbacks.forEach(callback => callback())\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = callback => {\n if (typeof callback === 'function') {\n callback()\n }\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element depending on the direction and if cycle is allowed\n if (index === -1) {\n return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]\n }\n\n const listLength = list.length\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n getElement,\n getUID,\n getSelectorFromElement,\n getElementFromSelector,\n getTransitionDurationFromElement,\n triggerTransitionEnd,\n isElement,\n typeCheckConfig,\n isVisible,\n isDisabled,\n findShadowRoot,\n noop,\n getNextActiveElement,\n reflow,\n getjQuery,\n onDOMContentLoaded,\n isRTL,\n defineJQueryPlugin,\n execute,\n executeAfterTransition\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): collapse.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport {\n defineJQueryPlugin,\n getElement,\n getSelectorFromElement,\n getElementFromSelector,\n reflow,\n typeCheckConfig\n} from './util/index'\nimport Data from './dom/data'\nimport EventHandler from './dom/event-handler'\nimport Manipulator from './dom/manipulator'\nimport SelectorEngine from './dom/selector-engine'\nimport BaseComponent from './base-component'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'collapse'\nconst DATA_KEY = 'bs.collapse'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst Default = {\n toggle: true,\n parent: null\n}\n\nconst DefaultType = {\n toggle: 'boolean',\n parent: '(null|element)'\n}\n\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_COLLAPSE = 'collapse'\nconst CLASS_NAME_COLLAPSING = 'collapsing'\nconst CLASS_NAME_COLLAPSED = 'collapsed'\nconst CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`\nconst CLASS_NAME_HORIZONTAL = 'collapse-horizontal'\n\nconst WIDTH = 'width'\nconst HEIGHT = 'height'\n\nconst SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"collapse\"]'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Collapse extends BaseComponent {\n constructor(element, config) {\n super(element)\n\n this._isTransitioning = false\n this._config = this._getConfig(config)\n this._triggerArray = []\n\n const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE)\n\n for (let i = 0, len = toggleList.length; i < len; i++) {\n const elem = toggleList[i]\n const selector = getSelectorFromElement(elem)\n const filterElement = SelectorEngine.find(selector)\n .filter(foundElem => foundElem === this._element)\n\n if (selector !== null && filterElement.length) {\n this._selector = selector\n this._triggerArray.push(elem)\n }\n }\n\n this._initializeChildren()\n\n if (!this._config.parent) {\n this._addAriaAndCollapsedClass(this._triggerArray, this._isShown())\n }\n\n if (this._config.toggle) {\n this.toggle()\n }\n }\n\n // Getters\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\n toggle() {\n if (this._isShown()) {\n this.hide()\n } else {\n this.show()\n }\n }\n\n show() {\n if (this._isTransitioning || this._isShown()) {\n return\n }\n\n let actives = []\n let activesData\n\n if (this._config.parent) {\n const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent)\n actives = SelectorEngine.find(SELECTOR_ACTIVES, this._config.parent).filter(elem => !children.includes(elem)) // remove children if greater depth\n }\n\n const container = SelectorEngine.findOne(this._selector)\n if (actives.length) {\n const tempActiveData = actives.find(elem => container !== elem)\n activesData = tempActiveData ? Collapse.getInstance(tempActiveData) : null\n\n if (activesData && activesData._isTransitioning) {\n return\n }\n }\n\n const startEvent = EventHandler.trigger(this._element, EVENT_SHOW)\n if (startEvent.defaultPrevented) {\n return\n }\n\n actives.forEach(elemActive => {\n if (container !== elemActive) {\n Collapse.getOrCreateInstance(elemActive, { toggle: false }).hide()\n }\n\n if (!activesData) {\n Data.set(elemActive, DATA_KEY, null)\n }\n })\n\n const dimension = this._getDimension()\n\n this._element.classList.remove(CLASS_NAME_COLLAPSE)\n this._element.classList.add(CLASS_NAME_COLLAPSING)\n\n this._element.style[dimension] = 0\n\n this._addAriaAndCollapsedClass(this._triggerArray, true)\n this._isTransitioning = true\n\n const complete = () => {\n this._isTransitioning = false\n\n this._element.classList.remove(CLASS_NAME_COLLAPSING)\n this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)\n\n this._element.style[dimension] = ''\n\n EventHandler.trigger(this._element, EVENT_SHOWN)\n }\n\n const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)\n const scrollSize = `scroll${capitalizedDimension}`\n\n this._queueCallback(complete, this._element, true)\n this._element.style[dimension] = `${this._element[scrollSize]}px`\n }\n\n hide() {\n if (this._isTransitioning || !this._isShown()) {\n return\n }\n\n const startEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n if (startEvent.defaultPrevented) {\n return\n }\n\n const dimension = this._getDimension()\n\n this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`\n\n reflow(this._element)\n\n this._element.classList.add(CLASS_NAME_COLLAPSING)\n this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)\n\n const triggerArrayLength = this._triggerArray.length\n for (let i = 0; i < triggerArrayLength; i++) {\n const trigger = this._triggerArray[i]\n const elem = getElementFromSelector(trigger)\n\n if (elem && !this._isShown(elem)) {\n this._addAriaAndCollapsedClass([trigger], false)\n }\n }\n\n this._isTransitioning = true\n\n const complete = () => {\n this._isTransitioning = false\n this._element.classList.remove(CLASS_NAME_COLLAPSING)\n this._element.classList.add(CLASS_NAME_COLLAPSE)\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n }\n\n this._element.style[dimension] = ''\n\n this._queueCallback(complete, this._element, true)\n }\n\n _isShown(element = this._element) {\n return element.classList.contains(CLASS_NAME_SHOW)\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...Manipulator.getDataAttributes(this._element),\n ...config\n }\n config.toggle = Boolean(config.toggle) // Coerce string values\n config.parent = getElement(config.parent)\n typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _getDimension() {\n return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT\n }\n\n _initializeChildren() {\n if (!this._config.parent) {\n return\n }\n\n const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent)\n SelectorEngine.find(SELECTOR_DATA_TOGGLE, this._config.parent).filter(elem => !children.includes(elem))\n .forEach(element => {\n const selected = getElementFromSelector(element)\n\n if (selected) {\n this._addAriaAndCollapsedClass([element], this._isShown(selected))\n }\n })\n }\n\n _addAriaAndCollapsedClass(triggerArray, isOpen) {\n if (!triggerArray.length) {\n return\n }\n\n triggerArray.forEach(elem => {\n if (isOpen) {\n elem.classList.remove(CLASS_NAME_COLLAPSED)\n } else {\n elem.classList.add(CLASS_NAME_COLLAPSED)\n }\n\n elem.setAttribute('aria-expanded', isOpen)\n })\n }\n\n // Static\n\n static jQueryInterface(config) {\n return this.each(function () {\n const _config = {}\n if (typeof config === 'string' && /show|hide/.test(config)) {\n _config.toggle = false\n }\n\n const data = Collapse.getOrCreateInstance(this, _config)\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n // preventDefault only for <a> elements (which change the URL) not inside the collapsible element\n if (event.target.tagName === 'A' || (event.delegateTarget && event.delegateTarget.tagName === 'A')) {\n event.preventDefault()\n }\n\n const selector = getSelectorFromElement(this)\n const selectorElements = SelectorEngine.find(selector)\n\n selectorElements.forEach(element => {\n Collapse.getOrCreateInstance(element, { toggle: false }).toggle()\n })\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Collapse to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Collapse)\n\nexport default Collapse\n"],"names":["toType","obj","undefined","toString","call","match","toLowerCase","getSelector","element","selector","getAttribute","hrefAttr","includes","startsWith","split","trim","getSelectorFromElement","document","querySelector","getElementFromSelector","isElement","jquery","nodeType","getElement","length","typeCheckConfig","componentName","config","configTypes","Object","keys","forEach","property","expectedTypes","value","valueType","RegExp","test","TypeError","toUpperCase","reflow","offsetHeight","getjQuery","jQuery","window","body","hasAttribute","DOMContentLoadedCallbacks","onDOMContentLoaded","callback","readyState","addEventListener","push","defineJQueryPlugin","plugin","$","name","NAME","JQUERY_NO_CONFLICT","fn","jQueryInterface","Constructor","noConflict","DATA_KEY","EVENT_KEY","DATA_API_KEY","Default","toggle","parent","DefaultType","EVENT_SHOW","EVENT_SHOWN","EVENT_HIDE","EVENT_HIDDEN","EVENT_CLICK_DATA_API","CLASS_NAME_SHOW","CLASS_NAME_COLLAPSE","CLASS_NAME_COLLAPSING","CLASS_NAME_COLLAPSED","CLASS_NAME_DEEPER_CHILDREN","CLASS_NAME_HORIZONTAL","WIDTH","HEIGHT","SELECTOR_ACTIVES","SELECTOR_DATA_TOGGLE","Collapse","BaseComponent","constructor","_isTransitioning","_config","_getConfig","_triggerArray","toggleList","SelectorEngine","find","i","len","elem","filterElement","filter","foundElem","_element","_selector","_initializeChildren","_addAriaAndCollapsedClass","_isShown","hide","show","actives","activesData","children","container","findOne","tempActiveData","getInstance","startEvent","EventHandler","trigger","defaultPrevented","elemActive","getOrCreateInstance","Data","set","dimension","_getDimension","classList","remove","add","style","complete","capitalizedDimension","slice","scrollSize","_queueCallback","getBoundingClientRect","triggerArrayLength","contains","Manipulator","getDataAttributes","Boolean","selected","triggerArray","isOpen","setAttribute","each","data","on","event","target","tagName","delegateTarget","preventDefault","selectorElements"],"mappings":"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"} \ No newline at end of file
+{"version":3,"file":"collapse.js","sources":["../src/collapse.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap collapse.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport {\n defineJQueryPlugin,\n getElement,\n reflow\n} from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'collapse'\nconst DATA_KEY = 'bs.collapse'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_COLLAPSE = 'collapse'\nconst CLASS_NAME_COLLAPSING = 'collapsing'\nconst CLASS_NAME_COLLAPSED = 'collapsed'\nconst CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`\nconst CLASS_NAME_HORIZONTAL = 'collapse-horizontal'\n\nconst WIDTH = 'width'\nconst HEIGHT = 'height'\n\nconst SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"collapse\"]'\n\nconst Default = {\n parent: null,\n toggle: true\n}\n\nconst DefaultType = {\n parent: '(null|element)',\n toggle: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Collapse extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._isTransitioning = false\n this._triggerArray = []\n\n const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE)\n\n for (const elem of toggleList) {\n const selector = SelectorEngine.getSelectorFromElement(elem)\n const filterElement = SelectorEngine.find(selector)\n .filter(foundElement => foundElement === this._element)\n\n if (selector !== null && filterElement.length) {\n this._triggerArray.push(elem)\n }\n }\n\n this._initializeChildren()\n\n if (!this._config.parent) {\n this._addAriaAndCollapsedClass(this._triggerArray, this._isShown())\n }\n\n if (this._config.toggle) {\n this.toggle()\n }\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle() {\n if (this._isShown()) {\n this.hide()\n } else {\n this.show()\n }\n }\n\n show() {\n if (this._isTransitioning || this._isShown()) {\n return\n }\n\n let activeChildren = []\n\n // find active children\n if (this._config.parent) {\n activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES)\n .filter(element => element !== this._element)\n .map(element => Collapse.getOrCreateInstance(element, { toggle: false }))\n }\n\n if (activeChildren.length && activeChildren[0]._isTransitioning) {\n return\n }\n\n const startEvent = EventHandler.trigger(this._element, EVENT_SHOW)\n if (startEvent.defaultPrevented) {\n return\n }\n\n for (const activeInstance of activeChildren) {\n activeInstance.hide()\n }\n\n const dimension = this._getDimension()\n\n this._element.classList.remove(CLASS_NAME_COLLAPSE)\n this._element.classList.add(CLASS_NAME_COLLAPSING)\n\n this._element.style[dimension] = 0\n\n this._addAriaAndCollapsedClass(this._triggerArray, true)\n this._isTransitioning = true\n\n const complete = () => {\n this._isTransitioning = false\n\n this._element.classList.remove(CLASS_NAME_COLLAPSING)\n this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)\n\n this._element.style[dimension] = ''\n\n EventHandler.trigger(this._element, EVENT_SHOWN)\n }\n\n const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)\n const scrollSize = `scroll${capitalizedDimension}`\n\n this._queueCallback(complete, this._element, true)\n this._element.style[dimension] = `${this._element[scrollSize]}px`\n }\n\n hide() {\n if (this._isTransitioning || !this._isShown()) {\n return\n }\n\n const startEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n if (startEvent.defaultPrevented) {\n return\n }\n\n const dimension = this._getDimension()\n\n this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`\n\n reflow(this._element)\n\n this._element.classList.add(CLASS_NAME_COLLAPSING)\n this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)\n\n for (const trigger of this._triggerArray) {\n const element = SelectorEngine.getElementFromSelector(trigger)\n\n if (element && !this._isShown(element)) {\n this._addAriaAndCollapsedClass([trigger], false)\n }\n }\n\n this._isTransitioning = true\n\n const complete = () => {\n this._isTransitioning = false\n this._element.classList.remove(CLASS_NAME_COLLAPSING)\n this._element.classList.add(CLASS_NAME_COLLAPSE)\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n }\n\n this._element.style[dimension] = ''\n\n this._queueCallback(complete, this._element, true)\n }\n\n _isShown(element = this._element) {\n return element.classList.contains(CLASS_NAME_SHOW)\n }\n\n // Private\n _configAfterMerge(config) {\n config.toggle = Boolean(config.toggle) // Coerce string values\n config.parent = getElement(config.parent)\n return config\n }\n\n _getDimension() {\n return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT\n }\n\n _initializeChildren() {\n if (!this._config.parent) {\n return\n }\n\n const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE)\n\n for (const element of children) {\n const selected = SelectorEngine.getElementFromSelector(element)\n\n if (selected) {\n this._addAriaAndCollapsedClass([element], this._isShown(selected))\n }\n }\n }\n\n _getFirstLevelChildren(selector) {\n const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent)\n // remove children if greater depth\n return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element))\n }\n\n _addAriaAndCollapsedClass(triggerArray, isOpen) {\n if (!triggerArray.length) {\n return\n }\n\n for (const element of triggerArray) {\n element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen)\n element.setAttribute('aria-expanded', isOpen)\n }\n }\n\n // Static\n static jQueryInterface(config) {\n const _config = {}\n if (typeof config === 'string' && /show|hide/.test(config)) {\n _config.toggle = false\n }\n\n return this.each(function () {\n const data = Collapse.getOrCreateInstance(this, _config)\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n }\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n // preventDefault only for <a> elements (which change the URL) not inside the collapsible element\n if (event.target.tagName === 'A' || (event.delegateTarget && event.delegateTarget.tagName === 'A')) {\n event.preventDefault()\n }\n\n for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) {\n Collapse.getOrCreateInstance(element, { toggle: false }).toggle()\n }\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Collapse)\n\nexport default Collapse\n"],"names":["NAME","DATA_KEY","EVENT_KEY","DATA_API_KEY","EVENT_SHOW","EVENT_SHOWN","EVENT_HIDE","EVENT_HIDDEN","EVENT_CLICK_DATA_API","CLASS_NAME_SHOW","CLASS_NAME_COLLAPSE","CLASS_NAME_COLLAPSING","CLASS_NAME_COLLAPSED","CLASS_NAME_DEEPER_CHILDREN","CLASS_NAME_HORIZONTAL","WIDTH","HEIGHT","SELECTOR_ACTIVES","SELECTOR_DATA_TOGGLE","Default","parent","toggle","DefaultType","Collapse","BaseComponent","constructor","element","config","_isTransitioning","_triggerArray","toggleList","SelectorEngine","find","elem","selector","getSelectorFromElement","filterElement","filter","foundElement","_element","length","push","_initializeChildren","_config","_addAriaAndCollapsedClass","_isShown","hide","show","activeChildren","_getFirstLevelChildren","map","getOrCreateInstance","startEvent","EventHandler","trigger","defaultPrevented","activeInstance","dimension","_getDimension","classList","remove","add","style","complete","capitalizedDimension","toUpperCase","slice","scrollSize","_queueCallback","getBoundingClientRect","reflow","getElementFromSelector","contains","_configAfterMerge","Boolean","getElement","children","selected","includes","triggerArray","isOpen","setAttribute","jQueryInterface","test","each","data","TypeError","on","document","event","target","tagName","delegateTarget","preventDefault","getMultipleElementsFromSelector","defineJQueryPlugin"],"mappings":";;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;;;EAWA;EACA;EACA;;EAEA,MAAMA,IAAI,GAAG,UAAU,CAAA;EACvB,MAAMC,QAAQ,GAAG,aAAa,CAAA;EAC9B,MAAMC,SAAS,GAAI,CAAGD,CAAAA,EAAAA,QAAS,CAAC,CAAA,CAAA;EAChC,MAAME,YAAY,GAAG,WAAW,CAAA;EAEhC,MAAMC,UAAU,GAAI,CAAMF,IAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EACrC,MAAMG,WAAW,GAAI,CAAOH,KAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EACvC,MAAMI,UAAU,GAAI,CAAMJ,IAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EACrC,MAAMK,YAAY,GAAI,CAAQL,MAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EACzC,MAAMM,oBAAoB,GAAI,CAAA,KAAA,EAAON,SAAU,CAAA,EAAEC,YAAa,CAAC,CAAA,CAAA;EAE/D,MAAMM,eAAe,GAAG,MAAM,CAAA;EAC9B,MAAMC,mBAAmB,GAAG,UAAU,CAAA;EACtC,MAAMC,qBAAqB,GAAG,YAAY,CAAA;EAC1C,MAAMC,oBAAoB,GAAG,WAAW,CAAA;EACxC,MAAMC,0BAA0B,GAAI,CAAA,QAAA,EAAUH,mBAAoB,CAAA,EAAA,EAAIA,mBAAoB,CAAC,CAAA,CAAA;EAC3F,MAAMI,qBAAqB,GAAG,qBAAqB,CAAA;EAEnD,MAAMC,KAAK,GAAG,OAAO,CAAA;EACrB,MAAMC,MAAM,GAAG,QAAQ,CAAA;EAEvB,MAAMC,gBAAgB,GAAG,sCAAsC,CAAA;EAC/D,MAAMC,oBAAoB,GAAG,6BAA6B,CAAA;EAE1D,MAAMC,OAAO,GAAG;EACdC,EAAAA,MAAM,EAAE,IAAI;EACZC,EAAAA,MAAM,EAAE,IAAA;EACV,CAAC,CAAA;EAED,MAAMC,WAAW,GAAG;EAClBF,EAAAA,MAAM,EAAE,gBAAgB;EACxBC,EAAAA,MAAM,EAAE,SAAA;EACV,CAAC,CAAA;;EAED;EACA;EACA;;EAEA,MAAME,QAAQ,SAASC,aAAa,CAAC;EACnCC,EAAAA,WAAWA,CAACC,OAAO,EAAEC,MAAM,EAAE;EAC3B,IAAA,KAAK,CAACD,OAAO,EAAEC,MAAM,CAAC,CAAA;MAEtB,IAAI,CAACC,gBAAgB,GAAG,KAAK,CAAA;MAC7B,IAAI,CAACC,aAAa,GAAG,EAAE,CAAA;EAEvB,IAAA,MAAMC,UAAU,GAAGC,cAAc,CAACC,IAAI,CAACd,oBAAoB,CAAC,CAAA;EAE5D,IAAA,KAAK,MAAMe,IAAI,IAAIH,UAAU,EAAE;EAC7B,MAAA,MAAMI,QAAQ,GAAGH,cAAc,CAACI,sBAAsB,CAACF,IAAI,CAAC,CAAA;EAC5D,MAAA,MAAMG,aAAa,GAAGL,cAAc,CAACC,IAAI,CAACE,QAAQ,CAAC,CAChDG,MAAM,CAACC,YAAY,IAAIA,YAAY,KAAK,IAAI,CAACC,QAAQ,CAAC,CAAA;EAEzD,MAAA,IAAIL,QAAQ,KAAK,IAAI,IAAIE,aAAa,CAACI,MAAM,EAAE;EAC7C,QAAA,IAAI,CAACX,aAAa,CAACY,IAAI,CAACR,IAAI,CAAC,CAAA;EAC/B,OAAA;EACF,KAAA;MAEA,IAAI,CAACS,mBAAmB,EAAE,CAAA;EAE1B,IAAA,IAAI,CAAC,IAAI,CAACC,OAAO,CAACvB,MAAM,EAAE;EACxB,MAAA,IAAI,CAACwB,yBAAyB,CAAC,IAAI,CAACf,aAAa,EAAE,IAAI,CAACgB,QAAQ,EAAE,CAAC,CAAA;EACrE,KAAA;EAEA,IAAA,IAAI,IAAI,CAACF,OAAO,CAACtB,MAAM,EAAE;QACvB,IAAI,CAACA,MAAM,EAAE,CAAA;EACf,KAAA;EACF,GAAA;;EAEA;IACA,WAAWF,OAAOA,GAAG;EACnB,IAAA,OAAOA,OAAO,CAAA;EAChB,GAAA;IAEA,WAAWG,WAAWA,GAAG;EACvB,IAAA,OAAOA,WAAW,CAAA;EACpB,GAAA;IAEA,WAAWtB,IAAIA,GAAG;EAChB,IAAA,OAAOA,IAAI,CAAA;EACb,GAAA;;EAEA;EACAqB,EAAAA,MAAMA,GAAG;EACP,IAAA,IAAI,IAAI,CAACwB,QAAQ,EAAE,EAAE;QACnB,IAAI,CAACC,IAAI,EAAE,CAAA;EACb,KAAC,MAAM;QACL,IAAI,CAACC,IAAI,EAAE,CAAA;EACb,KAAA;EACF,GAAA;EAEAA,EAAAA,IAAIA,GAAG;MACL,IAAI,IAAI,CAACnB,gBAAgB,IAAI,IAAI,CAACiB,QAAQ,EAAE,EAAE;EAC5C,MAAA,OAAA;EACF,KAAA;MAEA,IAAIG,cAAc,GAAG,EAAE,CAAA;;EAEvB;EACA,IAAA,IAAI,IAAI,CAACL,OAAO,CAACvB,MAAM,EAAE;EACvB4B,MAAAA,cAAc,GAAG,IAAI,CAACC,sBAAsB,CAAChC,gBAAgB,CAAC,CAC3DoB,MAAM,CAACX,OAAO,IAAIA,OAAO,KAAK,IAAI,CAACa,QAAQ,CAAC,CAC5CW,GAAG,CAACxB,OAAO,IAAIH,QAAQ,CAAC4B,mBAAmB,CAACzB,OAAO,EAAE;EAAEL,QAAAA,MAAM,EAAE,KAAA;EAAM,OAAC,CAAC,CAAC,CAAA;EAC7E,KAAA;MAEA,IAAI2B,cAAc,CAACR,MAAM,IAAIQ,cAAc,CAAC,CAAC,CAAC,CAACpB,gBAAgB,EAAE;EAC/D,MAAA,OAAA;EACF,KAAA;MAEA,MAAMwB,UAAU,GAAGC,YAAY,CAACC,OAAO,CAAC,IAAI,CAACf,QAAQ,EAAEnC,UAAU,CAAC,CAAA;MAClE,IAAIgD,UAAU,CAACG,gBAAgB,EAAE;EAC/B,MAAA,OAAA;EACF,KAAA;EAEA,IAAA,KAAK,MAAMC,cAAc,IAAIR,cAAc,EAAE;QAC3CQ,cAAc,CAACV,IAAI,EAAE,CAAA;EACvB,KAAA;EAEA,IAAA,MAAMW,SAAS,GAAG,IAAI,CAACC,aAAa,EAAE,CAAA;MAEtC,IAAI,CAACnB,QAAQ,CAACoB,SAAS,CAACC,MAAM,CAAClD,mBAAmB,CAAC,CAAA;MACnD,IAAI,CAAC6B,QAAQ,CAACoB,SAAS,CAACE,GAAG,CAAClD,qBAAqB,CAAC,CAAA;MAElD,IAAI,CAAC4B,QAAQ,CAACuB,KAAK,CAACL,SAAS,CAAC,GAAG,CAAC,CAAA;MAElC,IAAI,CAACb,yBAAyB,CAAC,IAAI,CAACf,aAAa,EAAE,IAAI,CAAC,CAAA;MACxD,IAAI,CAACD,gBAAgB,GAAG,IAAI,CAAA;MAE5B,MAAMmC,QAAQ,GAAGA,MAAM;QACrB,IAAI,CAACnC,gBAAgB,GAAG,KAAK,CAAA;QAE7B,IAAI,CAACW,QAAQ,CAACoB,SAAS,CAACC,MAAM,CAACjD,qBAAqB,CAAC,CAAA;QACrD,IAAI,CAAC4B,QAAQ,CAACoB,SAAS,CAACE,GAAG,CAACnD,mBAAmB,EAAED,eAAe,CAAC,CAAA;QAEjE,IAAI,CAAC8B,QAAQ,CAACuB,KAAK,CAACL,SAAS,CAAC,GAAG,EAAE,CAAA;QAEnCJ,YAAY,CAACC,OAAO,CAAC,IAAI,CAACf,QAAQ,EAAElC,WAAW,CAAC,CAAA;OACjD,CAAA;EAED,IAAA,MAAM2D,oBAAoB,GAAGP,SAAS,CAAC,CAAC,CAAC,CAACQ,WAAW,EAAE,GAAGR,SAAS,CAACS,KAAK,CAAC,CAAC,CAAC,CAAA;EAC5E,IAAA,MAAMC,UAAU,GAAI,CAAQH,MAAAA,EAAAA,oBAAqB,CAAC,CAAA,CAAA;MAElD,IAAI,CAACI,cAAc,CAACL,QAAQ,EAAE,IAAI,CAACxB,QAAQ,EAAE,IAAI,CAAC,CAAA;EAClD,IAAA,IAAI,CAACA,QAAQ,CAACuB,KAAK,CAACL,SAAS,CAAC,GAAI,CAAA,EAAE,IAAI,CAAClB,QAAQ,CAAC4B,UAAU,CAAE,CAAG,EAAA,CAAA,CAAA;EACnE,GAAA;EAEArB,EAAAA,IAAIA,GAAG;MACL,IAAI,IAAI,CAAClB,gBAAgB,IAAI,CAAC,IAAI,CAACiB,QAAQ,EAAE,EAAE;EAC7C,MAAA,OAAA;EACF,KAAA;MAEA,MAAMO,UAAU,GAAGC,YAAY,CAACC,OAAO,CAAC,IAAI,CAACf,QAAQ,EAAEjC,UAAU,CAAC,CAAA;MAClE,IAAI8C,UAAU,CAACG,gBAAgB,EAAE;EAC/B,MAAA,OAAA;EACF,KAAA;EAEA,IAAA,MAAME,SAAS,GAAG,IAAI,CAACC,aAAa,EAAE,CAAA;EAEtC,IAAA,IAAI,CAACnB,QAAQ,CAACuB,KAAK,CAACL,SAAS,CAAC,GAAI,CAAA,EAAE,IAAI,CAAClB,QAAQ,CAAC8B,qBAAqB,EAAE,CAACZ,SAAS,CAAE,CAAG,EAAA,CAAA,CAAA;EAExFa,IAAAA,eAAM,CAAC,IAAI,CAAC/B,QAAQ,CAAC,CAAA;MAErB,IAAI,CAACA,QAAQ,CAACoB,SAAS,CAACE,GAAG,CAAClD,qBAAqB,CAAC,CAAA;MAClD,IAAI,CAAC4B,QAAQ,CAACoB,SAAS,CAACC,MAAM,CAAClD,mBAAmB,EAAED,eAAe,CAAC,CAAA;EAEpE,IAAA,KAAK,MAAM6C,OAAO,IAAI,IAAI,CAACzB,aAAa,EAAE;EACxC,MAAA,MAAMH,OAAO,GAAGK,cAAc,CAACwC,sBAAsB,CAACjB,OAAO,CAAC,CAAA;QAE9D,IAAI5B,OAAO,IAAI,CAAC,IAAI,CAACmB,QAAQ,CAACnB,OAAO,CAAC,EAAE;UACtC,IAAI,CAACkB,yBAAyB,CAAC,CAACU,OAAO,CAAC,EAAE,KAAK,CAAC,CAAA;EAClD,OAAA;EACF,KAAA;MAEA,IAAI,CAAC1B,gBAAgB,GAAG,IAAI,CAAA;MAE5B,MAAMmC,QAAQ,GAAGA,MAAM;QACrB,IAAI,CAACnC,gBAAgB,GAAG,KAAK,CAAA;QAC7B,IAAI,CAACW,QAAQ,CAACoB,SAAS,CAACC,MAAM,CAACjD,qBAAqB,CAAC,CAAA;QACrD,IAAI,CAAC4B,QAAQ,CAACoB,SAAS,CAACE,GAAG,CAACnD,mBAAmB,CAAC,CAAA;QAChD2C,YAAY,CAACC,OAAO,CAAC,IAAI,CAACf,QAAQ,EAAEhC,YAAY,CAAC,CAAA;OAClD,CAAA;MAED,IAAI,CAACgC,QAAQ,CAACuB,KAAK,CAACL,SAAS,CAAC,GAAG,EAAE,CAAA;MAEnC,IAAI,CAACW,cAAc,CAACL,QAAQ,EAAE,IAAI,CAACxB,QAAQ,EAAE,IAAI,CAAC,CAAA;EACpD,GAAA;EAEAM,EAAAA,QAAQA,CAACnB,OAAO,GAAG,IAAI,CAACa,QAAQ,EAAE;EAChC,IAAA,OAAOb,OAAO,CAACiC,SAAS,CAACa,QAAQ,CAAC/D,eAAe,CAAC,CAAA;EACpD,GAAA;;EAEA;IACAgE,iBAAiBA,CAAC9C,MAAM,EAAE;MACxBA,MAAM,CAACN,MAAM,GAAGqD,OAAO,CAAC/C,MAAM,CAACN,MAAM,CAAC,CAAC;MACvCM,MAAM,CAACP,MAAM,GAAGuD,mBAAU,CAAChD,MAAM,CAACP,MAAM,CAAC,CAAA;EACzC,IAAA,OAAOO,MAAM,CAAA;EACf,GAAA;EAEA+B,EAAAA,aAAaA,GAAG;EACd,IAAA,OAAO,IAAI,CAACnB,QAAQ,CAACoB,SAAS,CAACa,QAAQ,CAAC1D,qBAAqB,CAAC,GAAGC,KAAK,GAAGC,MAAM,CAAA;EACjF,GAAA;EAEA0B,EAAAA,mBAAmBA,GAAG;EACpB,IAAA,IAAI,CAAC,IAAI,CAACC,OAAO,CAACvB,MAAM,EAAE;EACxB,MAAA,OAAA;EACF,KAAA;EAEA,IAAA,MAAMwD,QAAQ,GAAG,IAAI,CAAC3B,sBAAsB,CAAC/B,oBAAoB,CAAC,CAAA;EAElE,IAAA,KAAK,MAAMQ,OAAO,IAAIkD,QAAQ,EAAE;EAC9B,MAAA,MAAMC,QAAQ,GAAG9C,cAAc,CAACwC,sBAAsB,CAAC7C,OAAO,CAAC,CAAA;EAE/D,MAAA,IAAImD,QAAQ,EAAE;EACZ,QAAA,IAAI,CAACjC,yBAAyB,CAAC,CAAClB,OAAO,CAAC,EAAE,IAAI,CAACmB,QAAQ,CAACgC,QAAQ,CAAC,CAAC,CAAA;EACpE,OAAA;EACF,KAAA;EACF,GAAA;IAEA5B,sBAAsBA,CAACf,QAAQ,EAAE;EAC/B,IAAA,MAAM0C,QAAQ,GAAG7C,cAAc,CAACC,IAAI,CAACnB,0BAA0B,EAAE,IAAI,CAAC8B,OAAO,CAACvB,MAAM,CAAC,CAAA;EACrF;MACA,OAAOW,cAAc,CAACC,IAAI,CAACE,QAAQ,EAAE,IAAI,CAACS,OAAO,CAACvB,MAAM,CAAC,CAACiB,MAAM,CAACX,OAAO,IAAI,CAACkD,QAAQ,CAACE,QAAQ,CAACpD,OAAO,CAAC,CAAC,CAAA;EAC1G,GAAA;EAEAkB,EAAAA,yBAAyBA,CAACmC,YAAY,EAAEC,MAAM,EAAE;EAC9C,IAAA,IAAI,CAACD,YAAY,CAACvC,MAAM,EAAE;EACxB,MAAA,OAAA;EACF,KAAA;EAEA,IAAA,KAAK,MAAMd,OAAO,IAAIqD,YAAY,EAAE;QAClCrD,OAAO,CAACiC,SAAS,CAACtC,MAAM,CAACT,oBAAoB,EAAE,CAACoE,MAAM,CAAC,CAAA;EACvDtD,MAAAA,OAAO,CAACuD,YAAY,CAAC,eAAe,EAAED,MAAM,CAAC,CAAA;EAC/C,KAAA;EACF,GAAA;;EAEA;IACA,OAAOE,eAAeA,CAACvD,MAAM,EAAE;MAC7B,MAAMgB,OAAO,GAAG,EAAE,CAAA;MAClB,IAAI,OAAOhB,MAAM,KAAK,QAAQ,IAAI,WAAW,CAACwD,IAAI,CAACxD,MAAM,CAAC,EAAE;QAC1DgB,OAAO,CAACtB,MAAM,GAAG,KAAK,CAAA;EACxB,KAAA;EAEA,IAAA,OAAO,IAAI,CAAC+D,IAAI,CAAC,YAAY;QAC3B,MAAMC,IAAI,GAAG9D,QAAQ,CAAC4B,mBAAmB,CAAC,IAAI,EAAER,OAAO,CAAC,CAAA;EAExD,MAAA,IAAI,OAAOhB,MAAM,KAAK,QAAQ,EAAE;EAC9B,QAAA,IAAI,OAAO0D,IAAI,CAAC1D,MAAM,CAAC,KAAK,WAAW,EAAE;EACvC,UAAA,MAAM,IAAI2D,SAAS,CAAE,CAAmB3D,iBAAAA,EAAAA,MAAO,GAAE,CAAC,CAAA;EACpD,SAAA;EAEA0D,QAAAA,IAAI,CAAC1D,MAAM,CAAC,EAAE,CAAA;EAChB,OAAA;EACF,KAAC,CAAC,CAAA;EACJ,GAAA;EACF,CAAA;;EAEA;EACA;EACA;;EAEA0B,YAAY,CAACkC,EAAE,CAACC,QAAQ,EAAEhF,oBAAoB,EAAEU,oBAAoB,EAAE,UAAUuE,KAAK,EAAE;EACrF;EACA,EAAA,IAAIA,KAAK,CAACC,MAAM,CAACC,OAAO,KAAK,GAAG,IAAKF,KAAK,CAACG,cAAc,IAAIH,KAAK,CAACG,cAAc,CAACD,OAAO,KAAK,GAAI,EAAE;MAClGF,KAAK,CAACI,cAAc,EAAE,CAAA;EACxB,GAAA;IAEA,KAAK,MAAMnE,OAAO,IAAIK,cAAc,CAAC+D,+BAA+B,CAAC,IAAI,CAAC,EAAE;EAC1EvE,IAAAA,QAAQ,CAAC4B,mBAAmB,CAACzB,OAAO,EAAE;EAAEL,MAAAA,MAAM,EAAE,KAAA;EAAM,KAAC,CAAC,CAACA,MAAM,EAAE,CAAA;EACnE,GAAA;EACF,CAAC,CAAC,CAAA;;EAEF;EACA;EACA;;AAEA0E,6BAAkB,CAACxE,QAAQ,CAAC;;;;;;;;"} \ No newline at end of file
diff --git a/js/dist/dom/data.js b/js/dist/dom/data.js
index f8dafc699..23d4cb9cd 100644
--- a/js/dist/dom/data.js
+++ b/js/dist/dom/data.js
@@ -1,6 +1,6 @@
/*!
- * Bootstrap data.js v5.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Bootstrap data.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
@@ -11,56 +11,50 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): dom/data.js
+ * Bootstrap dom/data.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
/**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
+
const elementMap = new Map();
const data = {
set(element, key, instance) {
if (!elementMap.has(element)) {
elementMap.set(element, new Map());
}
+ const instanceMap = elementMap.get(element);
- const instanceMap = elementMap.get(element); // make it clear we only want one instance per element
+ // make it clear we only want one instance per element
// can be removed later when multiple key/instances are fine to be used
-
if (!instanceMap.has(key) && instanceMap.size !== 0) {
// eslint-disable-next-line no-console
console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`);
return;
}
-
instanceMap.set(key, instance);
},
-
get(element, key) {
if (elementMap.has(element)) {
return elementMap.get(element).get(key) || null;
}
-
return null;
},
-
remove(element, key) {
if (!elementMap.has(element)) {
return;
}
-
const instanceMap = elementMap.get(element);
- instanceMap.delete(key); // free up element references if there are no instances left for an element
+ instanceMap.delete(key);
+ // free up element references if there are no instances left for an element
if (instanceMap.size === 0) {
elementMap.delete(element);
}
}
-
};
return data;
diff --git a/js/dist/dom/data.js.map b/js/dist/dom/data.js.map
index e5f9ae7a3..e5e833776 100644
--- a/js/dist/dom/data.js.map
+++ b/js/dist/dom/data.js.map
@@ -1 +1 @@
-{"version":3,"file":"data.js","sources":["../../src/dom/data.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): dom/data.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst elementMap = new Map()\n\nexport default {\n set(element, key, instance) {\n if (!elementMap.has(element)) {\n elementMap.set(element, new Map())\n }\n\n const instanceMap = elementMap.get(element)\n\n // make it clear we only want one instance per element\n // can be removed later when multiple key/instances are fine to be used\n if (!instanceMap.has(key) && instanceMap.size !== 0) {\n // eslint-disable-next-line no-console\n console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`)\n return\n }\n\n instanceMap.set(key, instance)\n },\n\n get(element, key) {\n if (elementMap.has(element)) {\n return elementMap.get(element).get(key) || null\n }\n\n return null\n },\n\n remove(element, key) {\n if (!elementMap.has(element)) {\n return\n }\n\n const instanceMap = elementMap.get(element)\n\n instanceMap.delete(key)\n\n // free up element references if there are no instances left for an element\n if (instanceMap.size === 0) {\n elementMap.delete(element)\n }\n }\n}\n"],"names":["elementMap","Map","set","element","key","instance","has","instanceMap","get","size","console","error","Array","from","keys","remove","delete"],"mappings":";;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;;EAEA;EACA;EACA;EACA;EACA;EAEA,MAAMA,UAAU,GAAG,IAAIC,GAAJ,EAAnB;AAEA,eAAe;EACbC,EAAAA,GAAG,CAACC,OAAD,EAAUC,GAAV,EAAeC,QAAf,EAAyB;EAC1B,QAAI,CAACL,UAAU,CAACM,GAAX,CAAeH,OAAf,CAAL,EAA8B;EAC5BH,MAAAA,UAAU,CAACE,GAAX,CAAeC,OAAf,EAAwB,IAAIF,GAAJ,EAAxB;EACD;;EAED,UAAMM,WAAW,GAAGP,UAAU,CAACQ,GAAX,CAAeL,OAAf,CAApB,CAL0B;EAQ1B;;EACA,QAAI,CAACI,WAAW,CAACD,GAAZ,CAAgBF,GAAhB,CAAD,IAAyBG,WAAW,CAACE,IAAZ,KAAqB,CAAlD,EAAqD;EACnD;EACAC,MAAAA,OAAO,CAACC,KAAR,CAAe,+EAA8EC,KAAK,CAACC,IAAN,CAAWN,WAAW,CAACO,IAAZ,EAAX,EAA+B,CAA/B,CAAkC,GAA/H;EACA;EACD;;EAEDP,IAAAA,WAAW,CAACL,GAAZ,CAAgBE,GAAhB,EAAqBC,QAArB;EACD,GAjBY;;EAmBbG,EAAAA,GAAG,CAACL,OAAD,EAAUC,GAAV,EAAe;EAChB,QAAIJ,UAAU,CAACM,GAAX,CAAeH,OAAf,CAAJ,EAA6B;EAC3B,aAAOH,UAAU,CAACQ,GAAX,CAAeL,OAAf,EAAwBK,GAAxB,CAA4BJ,GAA5B,KAAoC,IAA3C;EACD;;EAED,WAAO,IAAP;EACD,GAzBY;;EA2BbW,EAAAA,MAAM,CAACZ,OAAD,EAAUC,GAAV,EAAe;EACnB,QAAI,CAACJ,UAAU,CAACM,GAAX,CAAeH,OAAf,CAAL,EAA8B;EAC5B;EACD;;EAED,UAAMI,WAAW,GAAGP,UAAU,CAACQ,GAAX,CAAeL,OAAf,CAApB;EAEAI,IAAAA,WAAW,CAACS,MAAZ,CAAmBZ,GAAnB,EAPmB;;EAUnB,QAAIG,WAAW,CAACE,IAAZ,KAAqB,CAAzB,EAA4B;EAC1BT,MAAAA,UAAU,CAACgB,MAAX,CAAkBb,OAAlB;EACD;EACF;;EAxCY,CAAf;;;;;;;;"} \ No newline at end of file
+{"version":3,"file":"data.js","sources":["../../src/dom/data.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/data.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n/**\n * Constants\n */\n\nconst elementMap = new Map()\n\nexport default {\n set(element, key, instance) {\n if (!elementMap.has(element)) {\n elementMap.set(element, new Map())\n }\n\n const instanceMap = elementMap.get(element)\n\n // make it clear we only want one instance per element\n // can be removed later when multiple key/instances are fine to be used\n if (!instanceMap.has(key) && instanceMap.size !== 0) {\n // eslint-disable-next-line no-console\n console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`)\n return\n }\n\n instanceMap.set(key, instance)\n },\n\n get(element, key) {\n if (elementMap.has(element)) {\n return elementMap.get(element).get(key) || null\n }\n\n return null\n },\n\n remove(element, key) {\n if (!elementMap.has(element)) {\n return\n }\n\n const instanceMap = elementMap.get(element)\n\n instanceMap.delete(key)\n\n // free up element references if there are no instances left for an element\n if (instanceMap.size === 0) {\n elementMap.delete(element)\n }\n }\n}\n"],"names":["elementMap","Map","set","element","key","instance","has","instanceMap","get","size","console","error","Array","from","keys","remove","delete"],"mappings":";;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;;EAEA;EACA;EACA;;EAEA,MAAMA,UAAU,GAAG,IAAIC,GAAG,EAAE,CAAA;AAE5B,eAAe;EACbC,EAAAA,GAAGA,CAACC,OAAO,EAAEC,GAAG,EAAEC,QAAQ,EAAE;EAC1B,IAAA,IAAI,CAACL,UAAU,CAACM,GAAG,CAACH,OAAO,CAAC,EAAE;QAC5BH,UAAU,CAACE,GAAG,CAACC,OAAO,EAAE,IAAIF,GAAG,EAAE,CAAC,CAAA;EACpC,KAAA;EAEA,IAAA,MAAMM,WAAW,GAAGP,UAAU,CAACQ,GAAG,CAACL,OAAO,CAAC,CAAA;;EAE3C;EACA;EACA,IAAA,IAAI,CAACI,WAAW,CAACD,GAAG,CAACF,GAAG,CAAC,IAAIG,WAAW,CAACE,IAAI,KAAK,CAAC,EAAE;EACnD;EACAC,MAAAA,OAAO,CAACC,KAAK,CAAE,+EAA8EC,KAAK,CAACC,IAAI,CAACN,WAAW,CAACO,IAAI,EAAE,CAAC,CAAC,CAAC,CAAE,GAAE,CAAC,CAAA;EAClI,MAAA,OAAA;EACF,KAAA;EAEAP,IAAAA,WAAW,CAACL,GAAG,CAACE,GAAG,EAAEC,QAAQ,CAAC,CAAA;KAC/B;EAEDG,EAAAA,GAAGA,CAACL,OAAO,EAAEC,GAAG,EAAE;EAChB,IAAA,IAAIJ,UAAU,CAACM,GAAG,CAACH,OAAO,CAAC,EAAE;EAC3B,MAAA,OAAOH,UAAU,CAACQ,GAAG,CAACL,OAAO,CAAC,CAACK,GAAG,CAACJ,GAAG,CAAC,IAAI,IAAI,CAAA;EACjD,KAAA;EAEA,IAAA,OAAO,IAAI,CAAA;KACZ;EAEDW,EAAAA,MAAMA,CAACZ,OAAO,EAAEC,GAAG,EAAE;EACnB,IAAA,IAAI,CAACJ,UAAU,CAACM,GAAG,CAACH,OAAO,CAAC,EAAE;EAC5B,MAAA,OAAA;EACF,KAAA;EAEA,IAAA,MAAMI,WAAW,GAAGP,UAAU,CAACQ,GAAG,CAACL,OAAO,CAAC,CAAA;EAE3CI,IAAAA,WAAW,CAACS,MAAM,CAACZ,GAAG,CAAC,CAAA;;EAEvB;EACA,IAAA,IAAIG,WAAW,CAACE,IAAI,KAAK,CAAC,EAAE;EAC1BT,MAAAA,UAAU,CAACgB,MAAM,CAACb,OAAO,CAAC,CAAA;EAC5B,KAAA;EACF,GAAA;EACF,CAAC;;;;;;;;"} \ No newline at end of file
diff --git a/js/dist/dom/event-handler.js b/js/dist/dom/event-handler.js
index 954fe1f37..3d9d839ce 100644
--- a/js/dist/dom/event-handler.js
+++ b/js/dist/dom/event-handler.js
@@ -1,274 +1,197 @@
/*!
- * Bootstrap event-handler.js v5.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Bootstrap event-handler.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
- typeof define === 'function' && define.amd ? define(factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.EventHandler = factory());
-})(this, (function () { 'use strict';
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../util/index.js')) :
+ typeof define === 'function' && define.amd ? define(['../util/index'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.EventHandler = factory(global.Index));
+})(this, (function (index_js) { 'use strict';
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/index.js
+ * Bootstrap dom/event-handler.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
- const getjQuery = () => {
- const {
- jQuery
- } = window;
-
- if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
- return jQuery;
- }
-
- return null;
- };
/**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): dom/event-handler.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- /**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const namespaceRegex = /[^.]*(?=\..*)\.|.*/;
const stripNameRegex = /\..*/;
const stripUidRegex = /::\d+$/;
const eventRegistry = {}; // Events storage
-
let uidEvent = 1;
const customEvents = {
mouseenter: 'mouseover',
mouseleave: 'mouseout'
};
- const customEventsRegex = /^(mouseenter|mouseleave)/i;
const nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']);
+
/**
- * ------------------------------------------------------------------------
* Private methods
- * ------------------------------------------------------------------------
*/
- function getUidEvent(element, uid) {
+ function makeEventUid(element, uid) {
return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++;
}
-
- function getEvent(element) {
- const uid = getUidEvent(element);
+ function getElementEvents(element) {
+ const uid = makeEventUid(element);
element.uidEvent = uid;
eventRegistry[uid] = eventRegistry[uid] || {};
return eventRegistry[uid];
}
-
function bootstrapHandler(element, fn) {
return function handler(event) {
- event.delegateTarget = element;
-
+ hydrateObj(event, {
+ delegateTarget: element
+ });
if (handler.oneOff) {
EventHandler.off(element, event.type, fn);
}
-
return fn.apply(element, [event]);
};
}
-
function bootstrapDelegationHandler(element, selector, fn) {
return function handler(event) {
const domElements = element.querySelectorAll(selector);
-
for (let {
target
} = event; target && target !== this; target = target.parentNode) {
- for (let i = domElements.length; i--;) {
- if (domElements[i] === target) {
- event.delegateTarget = target;
-
- if (handler.oneOff) {
- EventHandler.off(element, event.type, selector, fn);
- }
-
- return fn.apply(target, [event]);
+ for (const domElement of domElements) {
+ if (domElement !== target) {
+ continue;
+ }
+ hydrateObj(event, {
+ delegateTarget: target
+ });
+ if (handler.oneOff) {
+ EventHandler.off(element, event.type, selector, fn);
}
+ return fn.apply(target, [event]);
}
- } // To please ESLint
-
-
- return null;
+ }
};
}
-
- function findHandler(events, handler, delegationSelector = null) {
- const uidEventList = Object.keys(events);
-
- for (let i = 0, len = uidEventList.length; i < len; i++) {
- const event = events[uidEventList[i]];
-
- if (event.originalHandler === handler && event.delegationSelector === delegationSelector) {
- return event;
- }
- }
-
- return null;
+ function findHandler(events, callable, delegationSelector = null) {
+ return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector);
}
-
- function normalizeParams(originalTypeEvent, handler, delegationFn) {
- const delegation = typeof handler === 'string';
- const originalHandler = delegation ? delegationFn : handler;
+ function normalizeParameters(originalTypeEvent, handler, delegationFunction) {
+ const isDelegated = typeof handler === 'string';
+ // TODO: tooltip passes `false` instead of selector, so we need to check
+ const callable = isDelegated ? delegationFunction : handler || delegationFunction;
let typeEvent = getTypeEvent(originalTypeEvent);
- const isNative = nativeEvents.has(typeEvent);
-
- if (!isNative) {
+ if (!nativeEvents.has(typeEvent)) {
typeEvent = originalTypeEvent;
}
-
- return [delegation, originalHandler, typeEvent];
+ return [isDelegated, callable, typeEvent];
}
-
- function addHandler(element, originalTypeEvent, handler, delegationFn, oneOff) {
+ function addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {
if (typeof originalTypeEvent !== 'string' || !element) {
return;
}
+ let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);
- if (!handler) {
- handler = delegationFn;
- delegationFn = null;
- } // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position
+ // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position
// this prevents the handler from being dispatched the same way as mouseover or mouseout does
-
-
- if (customEventsRegex.test(originalTypeEvent)) {
- const wrapFn = fn => {
+ if (originalTypeEvent in customEvents) {
+ const wrapFunction = fn => {
return function (event) {
if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) {
return fn.call(this, event);
}
};
};
-
- if (delegationFn) {
- delegationFn = wrapFn(delegationFn);
- } else {
- handler = wrapFn(handler);
- }
+ callable = wrapFunction(callable);
}
-
- const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn);
- const events = getEvent(element);
+ const events = getElementEvents(element);
const handlers = events[typeEvent] || (events[typeEvent] = {});
- const previousFn = findHandler(handlers, originalHandler, delegation ? handler : null);
-
- if (previousFn) {
- previousFn.oneOff = previousFn.oneOff && oneOff;
+ const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null);
+ if (previousFunction) {
+ previousFunction.oneOff = previousFunction.oneOff && oneOff;
return;
}
-
- const uid = getUidEvent(originalHandler, originalTypeEvent.replace(namespaceRegex, ''));
- const fn = delegation ? bootstrapDelegationHandler(element, handler, delegationFn) : bootstrapHandler(element, handler);
- fn.delegationSelector = delegation ? handler : null;
- fn.originalHandler = originalHandler;
+ const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''));
+ const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable);
+ fn.delegationSelector = isDelegated ? handler : null;
+ fn.callable = callable;
fn.oneOff = oneOff;
fn.uidEvent = uid;
handlers[uid] = fn;
- element.addEventListener(typeEvent, fn, delegation);
+ element.addEventListener(typeEvent, fn, isDelegated);
}
-
function removeHandler(element, events, typeEvent, handler, delegationSelector) {
const fn = findHandler(events[typeEvent], handler, delegationSelector);
-
if (!fn) {
return;
}
-
element.removeEventListener(typeEvent, fn, Boolean(delegationSelector));
delete events[typeEvent][fn.uidEvent];
}
-
function removeNamespacedHandlers(element, events, typeEvent, namespace) {
const storeElementEvent = events[typeEvent] || {};
- Object.keys(storeElementEvent).forEach(handlerKey => {
+ for (const [handlerKey, event] of Object.entries(storeElementEvent)) {
if (handlerKey.includes(namespace)) {
- const event = storeElementEvent[handlerKey];
- removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector);
+ removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);
}
- });
+ }
}
-
function getTypeEvent(event) {
// allow to get the native events from namespaced events ('click.bs.button' --> 'click')
event = event.replace(stripNameRegex, '');
return customEvents[event] || event;
}
-
const EventHandler = {
- on(element, event, handler, delegationFn) {
- addHandler(element, event, handler, delegationFn, false);
+ on(element, event, handler, delegationFunction) {
+ addHandler(element, event, handler, delegationFunction, false);
},
-
- one(element, event, handler, delegationFn) {
- addHandler(element, event, handler, delegationFn, true);
+ one(element, event, handler, delegationFunction) {
+ addHandler(element, event, handler, delegationFunction, true);
},
-
- off(element, originalTypeEvent, handler, delegationFn) {
+ off(element, originalTypeEvent, handler, delegationFunction) {
if (typeof originalTypeEvent !== 'string' || !element) {
return;
}
-
- const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn);
+ const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction);
const inNamespace = typeEvent !== originalTypeEvent;
- const events = getEvent(element);
+ const events = getElementEvents(element);
+ const storeElementEvent = events[typeEvent] || {};
const isNamespace = originalTypeEvent.startsWith('.');
-
- if (typeof originalHandler !== 'undefined') {
+ if (typeof callable !== 'undefined') {
// Simplest case: handler is passed, remove that listener ONLY.
- if (!events || !events[typeEvent]) {
+ if (!Object.keys(storeElementEvent).length) {
return;
}
-
- removeHandler(element, events, typeEvent, originalHandler, delegation ? handler : null);
+ removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null);
return;
}
-
if (isNamespace) {
- Object.keys(events).forEach(elementEvent => {
+ for (const elementEvent of Object.keys(events)) {
removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1));
- });
+ }
}
-
- const storeElementEvent = events[typeEvent] || {};
- Object.keys(storeElementEvent).forEach(keyHandlers => {
+ for (const [keyHandlers, event] of Object.entries(storeElementEvent)) {
const handlerKey = keyHandlers.replace(stripUidRegex, '');
-
if (!inNamespace || originalTypeEvent.includes(handlerKey)) {
- const event = storeElementEvent[keyHandlers];
- removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector);
+ removeHandler(element, events, typeEvent, event.callable, event.delegationSelector);
}
- });
+ }
},
-
trigger(element, event, args) {
if (typeof event !== 'string' || !element) {
return null;
}
-
- const $ = getjQuery();
+ const $ = index_js.getjQuery();
const typeEvent = getTypeEvent(event);
const inNamespace = event !== typeEvent;
- const isNative = nativeEvents.has(typeEvent);
- let jQueryEvent;
+ let jQueryEvent = null;
let bubbles = true;
let nativeDispatch = true;
let defaultPrevented = false;
- let evt = null;
-
if (inNamespace && $) {
jQueryEvent = $.Event(event, args);
$(element).trigger(jQueryEvent);
@@ -276,45 +199,37 @@
nativeDispatch = !jQueryEvent.isImmediatePropagationStopped();
defaultPrevented = jQueryEvent.isDefaultPrevented();
}
-
- if (isNative) {
- evt = document.createEvent('HTMLEvents');
- evt.initEvent(typeEvent, bubbles, true);
- } else {
- evt = new CustomEvent(event, {
- bubbles,
- cancelable: true
- });
- } // merge custom information in our event
-
-
- if (typeof args !== 'undefined') {
- Object.keys(args).forEach(key => {
- Object.defineProperty(evt, key, {
- get() {
- return args[key];
- }
-
- });
- });
- }
-
+ const evt = hydrateObj(new Event(event, {
+ bubbles,
+ cancelable: true
+ }), args);
if (defaultPrevented) {
evt.preventDefault();
}
-
if (nativeDispatch) {
element.dispatchEvent(evt);
}
-
- if (evt.defaultPrevented && typeof jQueryEvent !== 'undefined') {
+ if (evt.defaultPrevented && jQueryEvent) {
jQueryEvent.preventDefault();
}
-
return evt;
}
-
};
+ function hydrateObj(obj, meta = {}) {
+ for (const [key, value] of Object.entries(meta)) {
+ try {
+ obj[key] = value;
+ } catch (_unused) {
+ Object.defineProperty(obj, key, {
+ configurable: true,
+ get() {
+ return value;
+ }
+ });
+ }
+ }
+ return obj;
+ }
return EventHandler;
diff --git a/js/dist/dom/event-handler.js.map b/js/dist/dom/event-handler.js.map
index d83e03721..b5d7fe212 100644
--- a/js/dist/dom/event-handler.js.map
+++ b/js/dist/dom/event-handler.js.map
@@ -1 +1 @@
-{"version":3,"file":"event-handler.js","sources":["../../src/util/index.js","../../src/dom/event-handler.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n// Shoutout AngusCroll (https://goo.gl/pxwQGp)\nconst toType = obj => {\n if (obj === null || obj === undefined) {\n return `${obj}`\n }\n\n return {}.toString.call(obj).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttr = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {\n hrefAttr = `#${hrefAttr.split('#')[1]}`\n }\n\n selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null\n }\n\n return selector\n}\n\nconst getSelectorFromElement = element => {\n const selector = getSelector(element)\n\n if (selector) {\n return document.querySelector(selector) ? selector : null\n }\n\n return null\n}\n\nconst getElementFromSelector = element => {\n const selector = getSelector(element)\n\n return selector ? document.querySelector(selector) : null\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = obj => {\n if (!obj || typeof obj !== 'object') {\n return false\n }\n\n if (typeof obj.jquery !== 'undefined') {\n obj = obj[0]\n }\n\n return typeof obj.nodeType !== 'undefined'\n}\n\nconst getElement = obj => {\n if (isElement(obj)) { // it's a jQuery object or a node element\n return obj.jquery ? obj[0] : obj\n }\n\n if (typeof obj === 'string' && obj.length > 0) {\n return document.querySelector(obj)\n }\n\n return null\n}\n\nconst typeCheckConfig = (componentName, config, configTypes) => {\n Object.keys(configTypes).forEach(property => {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${componentName.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n })\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n return getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n // eslint-disable-next-line no-unused-expressions\n element.offsetHeight\n}\n\nconst getjQuery = () => {\n const { jQuery } = window\n\n if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n DOMContentLoadedCallbacks.forEach(callback => callback())\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = callback => {\n if (typeof callback === 'function') {\n callback()\n }\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element depending on the direction and if cycle is allowed\n if (index === -1) {\n return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]\n }\n\n const listLength = list.length\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n getElement,\n getUID,\n getSelectorFromElement,\n getElementFromSelector,\n getTransitionDurationFromElement,\n triggerTransitionEnd,\n isElement,\n typeCheckConfig,\n isVisible,\n isDisabled,\n findShadowRoot,\n noop,\n getNextActiveElement,\n reflow,\n getjQuery,\n onDOMContentLoaded,\n isRTL,\n defineJQueryPlugin,\n execute,\n executeAfterTransition\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): dom/event-handler.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { getjQuery } from '../util/index'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst namespaceRegex = /[^.]*(?=\\..*)\\.|.*/\nconst stripNameRegex = /\\..*/\nconst stripUidRegex = /::\\d+$/\nconst eventRegistry = {} // Events storage\nlet uidEvent = 1\nconst customEvents = {\n mouseenter: 'mouseover',\n mouseleave: 'mouseout'\n}\nconst customEventsRegex = /^(mouseenter|mouseleave)/i\nconst nativeEvents = new Set([\n 'click',\n 'dblclick',\n 'mouseup',\n 'mousedown',\n 'contextmenu',\n 'mousewheel',\n 'DOMMouseScroll',\n 'mouseover',\n 'mouseout',\n 'mousemove',\n 'selectstart',\n 'selectend',\n 'keydown',\n 'keypress',\n 'keyup',\n 'orientationchange',\n 'touchstart',\n 'touchmove',\n 'touchend',\n 'touchcancel',\n 'pointerdown',\n 'pointermove',\n 'pointerup',\n 'pointerleave',\n 'pointercancel',\n 'gesturestart',\n 'gesturechange',\n 'gestureend',\n 'focus',\n 'blur',\n 'change',\n 'reset',\n 'select',\n 'submit',\n 'focusin',\n 'focusout',\n 'load',\n 'unload',\n 'beforeunload',\n 'resize',\n 'move',\n 'DOMContentLoaded',\n 'readystatechange',\n 'error',\n 'abort',\n 'scroll'\n])\n\n/**\n * ------------------------------------------------------------------------\n * Private methods\n * ------------------------------------------------------------------------\n */\n\nfunction getUidEvent(element, uid) {\n return (uid && `${uid}::${uidEvent++}`) || element.uidEvent || uidEvent++\n}\n\nfunction getEvent(element) {\n const uid = getUidEvent(element)\n\n element.uidEvent = uid\n eventRegistry[uid] = eventRegistry[uid] || {}\n\n return eventRegistry[uid]\n}\n\nfunction bootstrapHandler(element, fn) {\n return function handler(event) {\n event.delegateTarget = element\n\n if (handler.oneOff) {\n EventHandler.off(element, event.type, fn)\n }\n\n return fn.apply(element, [event])\n }\n}\n\nfunction bootstrapDelegationHandler(element, selector, fn) {\n return function handler(event) {\n const domElements = element.querySelectorAll(selector)\n\n for (let { target } = event; target && target !== this; target = target.parentNode) {\n for (let i = domElements.length; i--;) {\n if (domElements[i] === target) {\n event.delegateTarget = target\n\n if (handler.oneOff) {\n EventHandler.off(element, event.type, selector, fn)\n }\n\n return fn.apply(target, [event])\n }\n }\n }\n\n // To please ESLint\n return null\n }\n}\n\nfunction findHandler(events, handler, delegationSelector = null) {\n const uidEventList = Object.keys(events)\n\n for (let i = 0, len = uidEventList.length; i < len; i++) {\n const event = events[uidEventList[i]]\n\n if (event.originalHandler === handler && event.delegationSelector === delegationSelector) {\n return event\n }\n }\n\n return null\n}\n\nfunction normalizeParams(originalTypeEvent, handler, delegationFn) {\n const delegation = typeof handler === 'string'\n const originalHandler = delegation ? delegationFn : handler\n\n let typeEvent = getTypeEvent(originalTypeEvent)\n const isNative = nativeEvents.has(typeEvent)\n\n if (!isNative) {\n typeEvent = originalTypeEvent\n }\n\n return [delegation, originalHandler, typeEvent]\n}\n\nfunction addHandler(element, originalTypeEvent, handler, delegationFn, oneOff) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return\n }\n\n if (!handler) {\n handler = delegationFn\n delegationFn = null\n }\n\n // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position\n // this prevents the handler from being dispatched the same way as mouseover or mouseout does\n if (customEventsRegex.test(originalTypeEvent)) {\n const wrapFn = fn => {\n return function (event) {\n if (!event.relatedTarget || (event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget))) {\n return fn.call(this, event)\n }\n }\n }\n\n if (delegationFn) {\n delegationFn = wrapFn(delegationFn)\n } else {\n handler = wrapFn(handler)\n }\n }\n\n const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn)\n const events = getEvent(element)\n const handlers = events[typeEvent] || (events[typeEvent] = {})\n const previousFn = findHandler(handlers, originalHandler, delegation ? handler : null)\n\n if (previousFn) {\n previousFn.oneOff = previousFn.oneOff && oneOff\n\n return\n }\n\n const uid = getUidEvent(originalHandler, originalTypeEvent.replace(namespaceRegex, ''))\n const fn = delegation ?\n bootstrapDelegationHandler(element, handler, delegationFn) :\n bootstrapHandler(element, handler)\n\n fn.delegationSelector = delegation ? handler : null\n fn.originalHandler = originalHandler\n fn.oneOff = oneOff\n fn.uidEvent = uid\n handlers[uid] = fn\n\n element.addEventListener(typeEvent, fn, delegation)\n}\n\nfunction removeHandler(element, events, typeEvent, handler, delegationSelector) {\n const fn = findHandler(events[typeEvent], handler, delegationSelector)\n\n if (!fn) {\n return\n }\n\n element.removeEventListener(typeEvent, fn, Boolean(delegationSelector))\n delete events[typeEvent][fn.uidEvent]\n}\n\nfunction removeNamespacedHandlers(element, events, typeEvent, namespace) {\n const storeElementEvent = events[typeEvent] || {}\n\n Object.keys(storeElementEvent).forEach(handlerKey => {\n if (handlerKey.includes(namespace)) {\n const event = storeElementEvent[handlerKey]\n\n removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector)\n }\n })\n}\n\nfunction getTypeEvent(event) {\n // allow to get the native events from namespaced events ('click.bs.button' --> 'click')\n event = event.replace(stripNameRegex, '')\n return customEvents[event] || event\n}\n\nconst EventHandler = {\n on(element, event, handler, delegationFn) {\n addHandler(element, event, handler, delegationFn, false)\n },\n\n one(element, event, handler, delegationFn) {\n addHandler(element, event, handler, delegationFn, true)\n },\n\n off(element, originalTypeEvent, handler, delegationFn) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return\n }\n\n const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn)\n const inNamespace = typeEvent !== originalTypeEvent\n const events = getEvent(element)\n const isNamespace = originalTypeEvent.startsWith('.')\n\n if (typeof originalHandler !== 'undefined') {\n // Simplest case: handler is passed, remove that listener ONLY.\n if (!events || !events[typeEvent]) {\n return\n }\n\n removeHandler(element, events, typeEvent, originalHandler, delegation ? handler : null)\n return\n }\n\n if (isNamespace) {\n Object.keys(events).forEach(elementEvent => {\n removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1))\n })\n }\n\n const storeElementEvent = events[typeEvent] || {}\n Object.keys(storeElementEvent).forEach(keyHandlers => {\n const handlerKey = keyHandlers.replace(stripUidRegex, '')\n\n if (!inNamespace || originalTypeEvent.includes(handlerKey)) {\n const event = storeElementEvent[keyHandlers]\n\n removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector)\n }\n })\n },\n\n trigger(element, event, args) {\n if (typeof event !== 'string' || !element) {\n return null\n }\n\n const $ = getjQuery()\n const typeEvent = getTypeEvent(event)\n const inNamespace = event !== typeEvent\n const isNative = nativeEvents.has(typeEvent)\n\n let jQueryEvent\n let bubbles = true\n let nativeDispatch = true\n let defaultPrevented = false\n let evt = null\n\n if (inNamespace && $) {\n jQueryEvent = $.Event(event, args)\n\n $(element).trigger(jQueryEvent)\n bubbles = !jQueryEvent.isPropagationStopped()\n nativeDispatch = !jQueryEvent.isImmediatePropagationStopped()\n defaultPrevented = jQueryEvent.isDefaultPrevented()\n }\n\n if (isNative) {\n evt = document.createEvent('HTMLEvents')\n evt.initEvent(typeEvent, bubbles, true)\n } else {\n evt = new CustomEvent(event, {\n bubbles,\n cancelable: true\n })\n }\n\n // merge custom information in our event\n if (typeof args !== 'undefined') {\n Object.keys(args).forEach(key => {\n Object.defineProperty(evt, key, {\n get() {\n return args[key]\n }\n })\n })\n }\n\n if (defaultPrevented) {\n evt.preventDefault()\n }\n\n if (nativeDispatch) {\n element.dispatchEvent(evt)\n }\n\n if (evt.defaultPrevented && typeof jQueryEvent !== 'undefined') {\n jQueryEvent.preventDefault()\n }\n\n return evt\n }\n}\n\nexport default EventHandler\n"],"names":["getjQuery","jQuery","window","document","body","hasAttribute","namespaceRegex","stripNameRegex","stripUidRegex","eventRegistry","uidEvent","customEvents","mouseenter","mouseleave","customEventsRegex","nativeEvents","Set","getUidEvent","element","uid","getEvent","bootstrapHandler","fn","handler","event","delegateTarget","oneOff","EventHandler","off","type","apply","bootstrapDelegationHandler","selector","domElements","querySelectorAll","target","parentNode","i","length","findHandler","events","delegationSelector","uidEventList","Object","keys","len","originalHandler","normalizeParams","originalTypeEvent","delegationFn","delegation","typeEvent","getTypeEvent","isNative","has","addHandler","test","wrapFn","relatedTarget","contains","call","handlers","previousFn","replace","addEventListener","removeHandler","removeEventListener","Boolean","removeNamespacedHandlers","namespace","storeElementEvent","forEach","handlerKey","includes","on","one","inNamespace","isNamespace","startsWith","elementEvent","slice","keyHandlers","trigger","args","$","jQueryEvent","bubbles","nativeDispatch","defaultPrevented","evt","Event","isPropagationStopped","isImmediatePropagationStopped","isDefaultPrevented","createEvent","initEvent","CustomEvent","cancelable","key","defineProperty","get","preventDefault","dispatchEvent"],"mappings":"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"} \ No newline at end of file
+{"version":3,"file":"event-handler.js","sources":["../../src/dom/event-handler.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/event-handler.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { getjQuery } from '../util/index.js'\n\n/**\n * Constants\n */\n\nconst namespaceRegex = /[^.]*(?=\\..*)\\.|.*/\nconst stripNameRegex = /\\..*/\nconst stripUidRegex = /::\\d+$/\nconst eventRegistry = {} // Events storage\nlet uidEvent = 1\nconst customEvents = {\n mouseenter: 'mouseover',\n mouseleave: 'mouseout'\n}\n\nconst nativeEvents = new Set([\n 'click',\n 'dblclick',\n 'mouseup',\n 'mousedown',\n 'contextmenu',\n 'mousewheel',\n 'DOMMouseScroll',\n 'mouseover',\n 'mouseout',\n 'mousemove',\n 'selectstart',\n 'selectend',\n 'keydown',\n 'keypress',\n 'keyup',\n 'orientationchange',\n 'touchstart',\n 'touchmove',\n 'touchend',\n 'touchcancel',\n 'pointerdown',\n 'pointermove',\n 'pointerup',\n 'pointerleave',\n 'pointercancel',\n 'gesturestart',\n 'gesturechange',\n 'gestureend',\n 'focus',\n 'blur',\n 'change',\n 'reset',\n 'select',\n 'submit',\n 'focusin',\n 'focusout',\n 'load',\n 'unload',\n 'beforeunload',\n 'resize',\n 'move',\n 'DOMContentLoaded',\n 'readystatechange',\n 'error',\n 'abort',\n 'scroll'\n])\n\n/**\n * Private methods\n */\n\nfunction makeEventUid(element, uid) {\n return (uid && `${uid}::${uidEvent++}`) || element.uidEvent || uidEvent++\n}\n\nfunction getElementEvents(element) {\n const uid = makeEventUid(element)\n\n element.uidEvent = uid\n eventRegistry[uid] = eventRegistry[uid] || {}\n\n return eventRegistry[uid]\n}\n\nfunction bootstrapHandler(element, fn) {\n return function handler(event) {\n hydrateObj(event, { delegateTarget: element })\n\n if (handler.oneOff) {\n EventHandler.off(element, event.type, fn)\n }\n\n return fn.apply(element, [event])\n }\n}\n\nfunction bootstrapDelegationHandler(element, selector, fn) {\n return function handler(event) {\n const domElements = element.querySelectorAll(selector)\n\n for (let { target } = event; target && target !== this; target = target.parentNode) {\n for (const domElement of domElements) {\n if (domElement !== target) {\n continue\n }\n\n hydrateObj(event, { delegateTarget: target })\n\n if (handler.oneOff) {\n EventHandler.off(element, event.type, selector, fn)\n }\n\n return fn.apply(target, [event])\n }\n }\n }\n}\n\nfunction findHandler(events, callable, delegationSelector = null) {\n return Object.values(events)\n .find(event => event.callable === callable && event.delegationSelector === delegationSelector)\n}\n\nfunction normalizeParameters(originalTypeEvent, handler, delegationFunction) {\n const isDelegated = typeof handler === 'string'\n // TODO: tooltip passes `false` instead of selector, so we need to check\n const callable = isDelegated ? delegationFunction : (handler || delegationFunction)\n let typeEvent = getTypeEvent(originalTypeEvent)\n\n if (!nativeEvents.has(typeEvent)) {\n typeEvent = originalTypeEvent\n }\n\n return [isDelegated, callable, typeEvent]\n}\n\nfunction addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return\n }\n\n let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction)\n\n // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position\n // this prevents the handler from being dispatched the same way as mouseover or mouseout does\n if (originalTypeEvent in customEvents) {\n const wrapFunction = fn => {\n return function (event) {\n if (!event.relatedTarget || (event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget))) {\n return fn.call(this, event)\n }\n }\n }\n\n callable = wrapFunction(callable)\n }\n\n const events = getElementEvents(element)\n const handlers = events[typeEvent] || (events[typeEvent] = {})\n const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null)\n\n if (previousFunction) {\n previousFunction.oneOff = previousFunction.oneOff && oneOff\n\n return\n }\n\n const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''))\n const fn = isDelegated ?\n bootstrapDelegationHandler(element, handler, callable) :\n bootstrapHandler(element, callable)\n\n fn.delegationSelector = isDelegated ? handler : null\n fn.callable = callable\n fn.oneOff = oneOff\n fn.uidEvent = uid\n handlers[uid] = fn\n\n element.addEventListener(typeEvent, fn, isDelegated)\n}\n\nfunction removeHandler(element, events, typeEvent, handler, delegationSelector) {\n const fn = findHandler(events[typeEvent], handler, delegationSelector)\n\n if (!fn) {\n return\n }\n\n element.removeEventListener(typeEvent, fn, Boolean(delegationSelector))\n delete events[typeEvent][fn.uidEvent]\n}\n\nfunction removeNamespacedHandlers(element, events, typeEvent, namespace) {\n const storeElementEvent = events[typeEvent] || {}\n\n for (const [handlerKey, event] of Object.entries(storeElementEvent)) {\n if (handlerKey.includes(namespace)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector)\n }\n }\n}\n\nfunction getTypeEvent(event) {\n // allow to get the native events from namespaced events ('click.bs.button' --> 'click')\n event = event.replace(stripNameRegex, '')\n return customEvents[event] || event\n}\n\nconst EventHandler = {\n on(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, false)\n },\n\n one(element, event, handler, delegationFunction) {\n addHandler(element, event, handler, delegationFunction, true)\n },\n\n off(element, originalTypeEvent, handler, delegationFunction) {\n if (typeof originalTypeEvent !== 'string' || !element) {\n return\n }\n\n const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction)\n const inNamespace = typeEvent !== originalTypeEvent\n const events = getElementEvents(element)\n const storeElementEvent = events[typeEvent] || {}\n const isNamespace = originalTypeEvent.startsWith('.')\n\n if (typeof callable !== 'undefined') {\n // Simplest case: handler is passed, remove that listener ONLY.\n if (!Object.keys(storeElementEvent).length) {\n return\n }\n\n removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null)\n return\n }\n\n if (isNamespace) {\n for (const elementEvent of Object.keys(events)) {\n removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1))\n }\n }\n\n for (const [keyHandlers, event] of Object.entries(storeElementEvent)) {\n const handlerKey = keyHandlers.replace(stripUidRegex, '')\n\n if (!inNamespace || originalTypeEvent.includes(handlerKey)) {\n removeHandler(element, events, typeEvent, event.callable, event.delegationSelector)\n }\n }\n },\n\n trigger(element, event, args) {\n if (typeof event !== 'string' || !element) {\n return null\n }\n\n const $ = getjQuery()\n const typeEvent = getTypeEvent(event)\n const inNamespace = event !== typeEvent\n\n let jQueryEvent = null\n let bubbles = true\n let nativeDispatch = true\n let defaultPrevented = false\n\n if (inNamespace && $) {\n jQueryEvent = $.Event(event, args)\n\n $(element).trigger(jQueryEvent)\n bubbles = !jQueryEvent.isPropagationStopped()\n nativeDispatch = !jQueryEvent.isImmediatePropagationStopped()\n defaultPrevented = jQueryEvent.isDefaultPrevented()\n }\n\n const evt = hydrateObj(new Event(event, { bubbles, cancelable: true }), args)\n\n if (defaultPrevented) {\n evt.preventDefault()\n }\n\n if (nativeDispatch) {\n element.dispatchEvent(evt)\n }\n\n if (evt.defaultPrevented && jQueryEvent) {\n jQueryEvent.preventDefault()\n }\n\n return evt\n }\n}\n\nfunction hydrateObj(obj, meta = {}) {\n for (const [key, value] of Object.entries(meta)) {\n try {\n obj[key] = value\n } catch {\n Object.defineProperty(obj, key, {\n configurable: true,\n get() {\n return value\n }\n })\n }\n }\n\n return obj\n}\n\nexport default EventHandler\n"],"names":["namespaceRegex","stripNameRegex","stripUidRegex","eventRegistry","uidEvent","customEvents","mouseenter","mouseleave","nativeEvents","Set","makeEventUid","element","uid","getElementEvents","bootstrapHandler","fn","handler","event","hydrateObj","delegateTarget","oneOff","EventHandler","off","type","apply","bootstrapDelegationHandler","selector","domElements","querySelectorAll","target","parentNode","domElement","findHandler","events","callable","delegationSelector","Object","values","find","normalizeParameters","originalTypeEvent","delegationFunction","isDelegated","typeEvent","getTypeEvent","has","addHandler","wrapFunction","relatedTarget","contains","call","handlers","previousFunction","replace","addEventListener","removeHandler","removeEventListener","Boolean","removeNamespacedHandlers","namespace","storeElementEvent","handlerKey","entries","includes","on","one","inNamespace","isNamespace","startsWith","keys","length","elementEvent","slice","keyHandlers","trigger","args","$","getjQuery","jQueryEvent","bubbles","nativeDispatch","defaultPrevented","Event","isPropagationStopped","isImmediatePropagationStopped","isDefaultPrevented","evt","cancelable","preventDefault","dispatchEvent","obj","meta","key","value","_unused","defineProperty","configurable","get"],"mappings":"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"} \ No newline at end of file
diff --git a/js/dist/dom/manipulator.js b/js/dist/dom/manipulator.js
index bac8a4d5a..b037a6e92 100644
--- a/js/dist/dom/manipulator.js
+++ b/js/dist/dom/manipulator.js
@@ -1,6 +1,6 @@
/*!
- * Bootstrap manipulator.js v5.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Bootstrap manipulator.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
@@ -11,76 +11,59 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): dom/manipulator.js
+ * Bootstrap dom/manipulator.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
- function normalizeData(val) {
- if (val === 'true') {
+
+ function normalizeData(value) {
+ if (value === 'true') {
return true;
}
-
- if (val === 'false') {
+ if (value === 'false') {
return false;
}
-
- if (val === Number(val).toString()) {
- return Number(val);
+ if (value === Number(value).toString()) {
+ return Number(value);
}
-
- if (val === '' || val === 'null') {
+ if (value === '' || value === 'null') {
return null;
}
-
- return val;
+ if (typeof value !== 'string') {
+ return value;
+ }
+ try {
+ return JSON.parse(decodeURIComponent(value));
+ } catch (_unused) {
+ return value;
+ }
}
-
function normalizeDataKey(key) {
return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`);
}
-
const Manipulator = {
setDataAttribute(element, key, value) {
element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value);
},
-
removeDataAttribute(element, key) {
element.removeAttribute(`data-bs-${normalizeDataKey(key)}`);
},
-
getDataAttributes(element) {
if (!element) {
return {};
}
-
const attributes = {};
- Object.keys(element.dataset).filter(key => key.startsWith('bs')).forEach(key => {
+ const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'));
+ for (const key of bsKeys) {
let pureKey = key.replace(/^bs/, '');
pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length);
attributes[pureKey] = normalizeData(element.dataset[key]);
- });
+ }
return attributes;
},
-
getDataAttribute(element, key) {
return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`));
- },
-
- offset(element) {
- const rect = element.getBoundingClientRect();
- return {
- top: rect.top + window.pageYOffset,
- left: rect.left + window.pageXOffset
- };
- },
-
- position(element) {
- return {
- top: element.offsetTop,
- left: element.offsetLeft
- };
}
-
};
return Manipulator;
diff --git a/js/dist/dom/manipulator.js.map b/js/dist/dom/manipulator.js.map
index 9c893e61a..9884dcdbb 100644
--- a/js/dist/dom/manipulator.js.map
+++ b/js/dist/dom/manipulator.js.map
@@ -1 +1 @@
-{"version":3,"file":"manipulator.js","sources":["../../src/dom/manipulator.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): dom/manipulator.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nfunction normalizeData(val) {\n if (val === 'true') {\n return true\n }\n\n if (val === 'false') {\n return false\n }\n\n if (val === Number(val).toString()) {\n return Number(val)\n }\n\n if (val === '' || val === 'null') {\n return null\n }\n\n return val\n}\n\nfunction normalizeDataKey(key) {\n return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`)\n}\n\nconst Manipulator = {\n setDataAttribute(element, key, value) {\n element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value)\n },\n\n removeDataAttribute(element, key) {\n element.removeAttribute(`data-bs-${normalizeDataKey(key)}`)\n },\n\n getDataAttributes(element) {\n if (!element) {\n return {}\n }\n\n const attributes = {}\n\n Object.keys(element.dataset)\n .filter(key => key.startsWith('bs'))\n .forEach(key => {\n let pureKey = key.replace(/^bs/, '')\n pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length)\n attributes[pureKey] = normalizeData(element.dataset[key])\n })\n\n return attributes\n },\n\n getDataAttribute(element, key) {\n return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`))\n },\n\n offset(element) {\n const rect = element.getBoundingClientRect()\n\n return {\n top: rect.top + window.pageYOffset,\n left: rect.left + window.pageXOffset\n }\n },\n\n position(element) {\n return {\n top: element.offsetTop,\n left: element.offsetLeft\n }\n }\n}\n\nexport default Manipulator\n"],"names":["normalizeData","val","Number","toString","normalizeDataKey","key","replace","chr","toLowerCase","Manipulator","setDataAttribute","element","value","setAttribute","removeDataAttribute","removeAttribute","getDataAttributes","attributes","Object","keys","dataset","filter","startsWith","forEach","pureKey","charAt","slice","length","getDataAttribute","getAttribute","offset","rect","getBoundingClientRect","top","window","pageYOffset","left","pageXOffset","position","offsetTop","offsetLeft"],"mappings":";;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;EAEA,SAASA,aAAT,CAAuBC,GAAvB,EAA4B;EAC1B,MAAIA,GAAG,KAAK,MAAZ,EAAoB;EAClB,WAAO,IAAP;EACD;;EAED,MAAIA,GAAG,KAAK,OAAZ,EAAqB;EACnB,WAAO,KAAP;EACD;;EAED,MAAIA,GAAG,KAAKC,MAAM,CAACD,GAAD,CAAN,CAAYE,QAAZ,EAAZ,EAAoC;EAClC,WAAOD,MAAM,CAACD,GAAD,CAAb;EACD;;EAED,MAAIA,GAAG,KAAK,EAAR,IAAcA,GAAG,KAAK,MAA1B,EAAkC;EAChC,WAAO,IAAP;EACD;;EAED,SAAOA,GAAP;EACD;;EAED,SAASG,gBAAT,CAA0BC,GAA1B,EAA+B;EAC7B,SAAOA,GAAG,CAACC,OAAJ,CAAY,QAAZ,EAAsBC,GAAG,IAAK,IAAGA,GAAG,CAACC,WAAJ,EAAkB,EAAnD,CAAP;EACD;;QAEKC,WAAW,GAAG;EAClBC,EAAAA,gBAAgB,CAACC,OAAD,EAAUN,GAAV,EAAeO,KAAf,EAAsB;EACpCD,IAAAA,OAAO,CAACE,YAAR,CAAsB,WAAUT,gBAAgB,CAACC,GAAD,CAAM,EAAtD,EAAyDO,KAAzD;EACD,GAHiB;;EAKlBE,EAAAA,mBAAmB,CAACH,OAAD,EAAUN,GAAV,EAAe;EAChCM,IAAAA,OAAO,CAACI,eAAR,CAAyB,WAAUX,gBAAgB,CAACC,GAAD,CAAM,EAAzD;EACD,GAPiB;;EASlBW,EAAAA,iBAAiB,CAACL,OAAD,EAAU;EACzB,QAAI,CAACA,OAAL,EAAc;EACZ,aAAO,EAAP;EACD;;EAED,UAAMM,UAAU,GAAG,EAAnB;EAEAC,IAAAA,MAAM,CAACC,IAAP,CAAYR,OAAO,CAACS,OAApB,EACGC,MADH,CACUhB,GAAG,IAAIA,GAAG,CAACiB,UAAJ,CAAe,IAAf,CADjB,EAEGC,OAFH,CAEWlB,GAAG,IAAI;EACd,UAAImB,OAAO,GAAGnB,GAAG,CAACC,OAAJ,CAAY,KAAZ,EAAmB,EAAnB,CAAd;EACAkB,MAAAA,OAAO,GAAGA,OAAO,CAACC,MAAR,CAAe,CAAf,EAAkBjB,WAAlB,KAAkCgB,OAAO,CAACE,KAAR,CAAc,CAAd,EAAiBF,OAAO,CAACG,MAAzB,CAA5C;EACAV,MAAAA,UAAU,CAACO,OAAD,CAAV,GAAsBxB,aAAa,CAACW,OAAO,CAACS,OAAR,CAAgBf,GAAhB,CAAD,CAAnC;EACD,KANH;EAQA,WAAOY,UAAP;EACD,GAzBiB;;EA2BlBW,EAAAA,gBAAgB,CAACjB,OAAD,EAAUN,GAAV,EAAe;EAC7B,WAAOL,aAAa,CAACW,OAAO,CAACkB,YAAR,CAAsB,WAAUzB,gBAAgB,CAACC,GAAD,CAAM,EAAtD,CAAD,CAApB;EACD,GA7BiB;;EA+BlByB,EAAAA,MAAM,CAACnB,OAAD,EAAU;EACd,UAAMoB,IAAI,GAAGpB,OAAO,CAACqB,qBAAR,EAAb;EAEA,WAAO;EACLC,MAAAA,GAAG,EAAEF,IAAI,CAACE,GAAL,GAAWC,MAAM,CAACC,WADlB;EAELC,MAAAA,IAAI,EAAEL,IAAI,CAACK,IAAL,GAAYF,MAAM,CAACG;EAFpB,KAAP;EAID,GAtCiB;;EAwClBC,EAAAA,QAAQ,CAAC3B,OAAD,EAAU;EAChB,WAAO;EACLsB,MAAAA,GAAG,EAAEtB,OAAO,CAAC4B,SADR;EAELH,MAAAA,IAAI,EAAEzB,OAAO,CAAC6B;EAFT,KAAP;EAID;;EA7CiB;;;;;;;;"} \ No newline at end of file
+{"version":3,"file":"manipulator.js","sources":["../../src/dom/manipulator.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/manipulator.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nfunction normalizeData(value) {\n if (value === 'true') {\n return true\n }\n\n if (value === 'false') {\n return false\n }\n\n if (value === Number(value).toString()) {\n return Number(value)\n }\n\n if (value === '' || value === 'null') {\n return null\n }\n\n if (typeof value !== 'string') {\n return value\n }\n\n try {\n return JSON.parse(decodeURIComponent(value))\n } catch {\n return value\n }\n}\n\nfunction normalizeDataKey(key) {\n return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`)\n}\n\nconst Manipulator = {\n setDataAttribute(element, key, value) {\n element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value)\n },\n\n removeDataAttribute(element, key) {\n element.removeAttribute(`data-bs-${normalizeDataKey(key)}`)\n },\n\n getDataAttributes(element) {\n if (!element) {\n return {}\n }\n\n const attributes = {}\n const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'))\n\n for (const key of bsKeys) {\n let pureKey = key.replace(/^bs/, '')\n pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length)\n attributes[pureKey] = normalizeData(element.dataset[key])\n }\n\n return attributes\n },\n\n getDataAttribute(element, key) {\n return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`))\n }\n}\n\nexport default Manipulator\n"],"names":["normalizeData","value","Number","toString","JSON","parse","decodeURIComponent","_unused","normalizeDataKey","key","replace","chr","toLowerCase","Manipulator","setDataAttribute","element","setAttribute","removeDataAttribute","removeAttribute","getDataAttributes","attributes","bsKeys","Object","keys","dataset","filter","startsWith","pureKey","charAt","slice","length","getDataAttribute","getAttribute"],"mappings":"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"} \ No newline at end of file
diff --git a/js/dist/dom/selector-engine.js b/js/dist/dom/selector-engine.js
index 8cf7e2925..993a88f13 100644
--- a/js/dist/dom/selector-engine.js
+++ b/js/dist/dom/selector-engine.js
@@ -1,125 +1,101 @@
/*!
- * Bootstrap selector-engine.js v5.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Bootstrap selector-engine.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
- typeof define === 'function' && define.amd ? define(factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.SelectorEngine = factory());
-})(this, (function () { 'use strict';
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../util/index.js')) :
+ typeof define === 'function' && define.amd ? define(['../util/index'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.SelectorEngine = factory(global.Index));
+})(this, (function (index_js) { 'use strict';
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/index.js
+ * Bootstrap dom/selector-engine.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
- const isElement = obj => {
- if (!obj || typeof obj !== 'object') {
- return false;
- }
-
- if (typeof obj.jquery !== 'undefined') {
- obj = obj[0];
- }
-
- return typeof obj.nodeType !== 'undefined';
- };
-
- const isVisible = element => {
- if (!isElement(element) || element.getClientRects().length === 0) {
- return false;
- }
-
- return getComputedStyle(element).getPropertyValue('visibility') === 'visible';
- };
-
- const isDisabled = element => {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return true;
- }
-
- if (element.classList.contains('disabled')) {
- return true;
- }
+ const getSelector = element => {
+ let selector = element.getAttribute('data-bs-target');
+ if (!selector || selector === '#') {
+ let hrefAttribute = element.getAttribute('href');
+
+ // The only valid content that could double as a selector are IDs or classes,
+ // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
+ // `document.querySelector` will rightfully complain it is invalid.
+ // See https://github.com/twbs/bootstrap/issues/32273
+ if (!hrefAttribute || !hrefAttribute.includes('#') && !hrefAttribute.startsWith('.')) {
+ return null;
+ }
- if (typeof element.disabled !== 'undefined') {
- return element.disabled;
+ // Just in case some CMS puts out a full URL with the anchor appended
+ if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {
+ hrefAttribute = `#${hrefAttribute.split('#')[1]}`;
+ }
+ selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null;
}
-
- return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';
+ return selector ? selector.split(',').map(sel => index_js.parseSelector(sel)).join(',') : null;
};
-
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): dom/selector-engine.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- const NODE_TEXT = 3;
const SelectorEngine = {
find(selector, element = document.documentElement) {
return [].concat(...Element.prototype.querySelectorAll.call(element, selector));
},
-
findOne(selector, element = document.documentElement) {
return Element.prototype.querySelector.call(element, selector);
},
-
children(element, selector) {
return [].concat(...element.children).filter(child => child.matches(selector));
},
-
parents(element, selector) {
const parents = [];
- let ancestor = element.parentNode;
-
- while (ancestor && ancestor.nodeType === Node.ELEMENT_NODE && ancestor.nodeType !== NODE_TEXT) {
- if (ancestor.matches(selector)) {
- parents.push(ancestor);
- }
-
- ancestor = ancestor.parentNode;
+ let ancestor = element.parentNode.closest(selector);
+ while (ancestor) {
+ parents.push(ancestor);
+ ancestor = ancestor.parentNode.closest(selector);
}
-
return parents;
},
-
prev(element, selector) {
let previous = element.previousElementSibling;
-
while (previous) {
if (previous.matches(selector)) {
return [previous];
}
-
previous = previous.previousElementSibling;
}
-
return [];
},
-
+ // TODO: this is now unused; remove later along with prev()
next(element, selector) {
let next = element.nextElementSibling;
-
while (next) {
if (next.matches(selector)) {
return [next];
}
-
next = next.nextElementSibling;
}
-
return [];
},
-
focusableChildren(element) {
- const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable="true"]'].map(selector => `${selector}:not([tabindex^="-"])`).join(', ');
- return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el));
+ const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable="true"]'].map(selector => `${selector}:not([tabindex^="-"])`).join(',');
+ return this.find(focusables, element).filter(el => !index_js.isDisabled(el) && index_js.isVisible(el));
+ },
+ getSelectorFromElement(element) {
+ const selector = getSelector(element);
+ if (selector) {
+ return SelectorEngine.findOne(selector) ? selector : null;
+ }
+ return null;
+ },
+ getElementFromSelector(element) {
+ const selector = getSelector(element);
+ return selector ? SelectorEngine.findOne(selector) : null;
+ },
+ getMultipleElementsFromSelector(element) {
+ const selector = getSelector(element);
+ return selector ? SelectorEngine.find(selector) : [];
}
-
};
return SelectorEngine;
diff --git a/js/dist/dom/selector-engine.js.map b/js/dist/dom/selector-engine.js.map
index 49ce84771..301622202 100644
--- a/js/dist/dom/selector-engine.js.map
+++ b/js/dist/dom/selector-engine.js.map
@@ -1 +1 @@
-{"version":3,"file":"selector-engine.js","sources":["../../src/util/index.js","../../src/dom/selector-engine.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n// Shoutout AngusCroll (https://goo.gl/pxwQGp)\nconst toType = obj => {\n if (obj === null || obj === undefined) {\n return `${obj}`\n }\n\n return {}.toString.call(obj).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttr = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {\n hrefAttr = `#${hrefAttr.split('#')[1]}`\n }\n\n selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null\n }\n\n return selector\n}\n\nconst getSelectorFromElement = element => {\n const selector = getSelector(element)\n\n if (selector) {\n return document.querySelector(selector) ? selector : null\n }\n\n return null\n}\n\nconst getElementFromSelector = element => {\n const selector = getSelector(element)\n\n return selector ? document.querySelector(selector) : null\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = obj => {\n if (!obj || typeof obj !== 'object') {\n return false\n }\n\n if (typeof obj.jquery !== 'undefined') {\n obj = obj[0]\n }\n\n return typeof obj.nodeType !== 'undefined'\n}\n\nconst getElement = obj => {\n if (isElement(obj)) { // it's a jQuery object or a node element\n return obj.jquery ? obj[0] : obj\n }\n\n if (typeof obj === 'string' && obj.length > 0) {\n return document.querySelector(obj)\n }\n\n return null\n}\n\nconst typeCheckConfig = (componentName, config, configTypes) => {\n Object.keys(configTypes).forEach(property => {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${componentName.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n })\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n return getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n // eslint-disable-next-line no-unused-expressions\n element.offsetHeight\n}\n\nconst getjQuery = () => {\n const { jQuery } = window\n\n if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n DOMContentLoadedCallbacks.forEach(callback => callback())\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = callback => {\n if (typeof callback === 'function') {\n callback()\n }\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element depending on the direction and if cycle is allowed\n if (index === -1) {\n return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]\n }\n\n const listLength = list.length\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n getElement,\n getUID,\n getSelectorFromElement,\n getElementFromSelector,\n getTransitionDurationFromElement,\n triggerTransitionEnd,\n isElement,\n typeCheckConfig,\n isVisible,\n isDisabled,\n findShadowRoot,\n noop,\n getNextActiveElement,\n reflow,\n getjQuery,\n onDOMContentLoaded,\n isRTL,\n defineJQueryPlugin,\n execute,\n executeAfterTransition\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): dom/selector-engine.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nimport { isDisabled, isVisible } from '../util/index'\n\nconst NODE_TEXT = 3\n\nconst SelectorEngine = {\n find(selector, element = document.documentElement) {\n return [].concat(...Element.prototype.querySelectorAll.call(element, selector))\n },\n\n findOne(selector, element = document.documentElement) {\n return Element.prototype.querySelector.call(element, selector)\n },\n\n children(element, selector) {\n return [].concat(...element.children)\n .filter(child => child.matches(selector))\n },\n\n parents(element, selector) {\n const parents = []\n\n let ancestor = element.parentNode\n\n while (ancestor && ancestor.nodeType === Node.ELEMENT_NODE && ancestor.nodeType !== NODE_TEXT) {\n if (ancestor.matches(selector)) {\n parents.push(ancestor)\n }\n\n ancestor = ancestor.parentNode\n }\n\n return parents\n },\n\n prev(element, selector) {\n let previous = element.previousElementSibling\n\n while (previous) {\n if (previous.matches(selector)) {\n return [previous]\n }\n\n previous = previous.previousElementSibling\n }\n\n return []\n },\n\n next(element, selector) {\n let next = element.nextElementSibling\n\n while (next) {\n if (next.matches(selector)) {\n return [next]\n }\n\n next = next.nextElementSibling\n }\n\n return []\n },\n\n focusableChildren(element) {\n const focusables = [\n 'a',\n 'button',\n 'input',\n 'textarea',\n 'select',\n 'details',\n '[tabindex]',\n '[contenteditable=\"true\"]'\n ].map(selector => `${selector}:not([tabindex^=\"-\"])`).join(', ')\n\n return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el))\n }\n}\n\nexport default SelectorEngine\n"],"names":["isElement","obj","jquery","nodeType","isVisible","element","getClientRects","length","getComputedStyle","getPropertyValue","isDisabled","Node","ELEMENT_NODE","classList","contains","disabled","hasAttribute","getAttribute","NODE_TEXT","SelectorEngine","find","selector","document","documentElement","concat","Element","prototype","querySelectorAll","call","findOne","querySelector","children","filter","child","matches","parents","ancestor","parentNode","push","prev","previous","previousElementSibling","next","nextElementSibling","focusableChildren","focusables","map","join","el"],"mappings":"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"} \ No newline at end of file
+{"version":3,"file":"selector-engine.js","sources":["../../src/dom/selector-engine.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap dom/selector-engine.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { isDisabled, isVisible, parseSelector } from '../util/index.js'\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttribute = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttribute || (!hrefAttribute.includes('#') && !hrefAttribute.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {\n hrefAttribute = `#${hrefAttribute.split('#')[1]}`\n }\n\n selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null\n }\n\n return selector ? selector.split(',').map(sel => parseSelector(sel)).join(',') : null\n}\n\nconst SelectorEngine = {\n find(selector, element = document.documentElement) {\n return [].concat(...Element.prototype.querySelectorAll.call(element, selector))\n },\n\n findOne(selector, element = document.documentElement) {\n return Element.prototype.querySelector.call(element, selector)\n },\n\n children(element, selector) {\n return [].concat(...element.children).filter(child => child.matches(selector))\n },\n\n parents(element, selector) {\n const parents = []\n let ancestor = element.parentNode.closest(selector)\n\n while (ancestor) {\n parents.push(ancestor)\n ancestor = ancestor.parentNode.closest(selector)\n }\n\n return parents\n },\n\n prev(element, selector) {\n let previous = element.previousElementSibling\n\n while (previous) {\n if (previous.matches(selector)) {\n return [previous]\n }\n\n previous = previous.previousElementSibling\n }\n\n return []\n },\n // TODO: this is now unused; remove later along with prev()\n next(element, selector) {\n let next = element.nextElementSibling\n\n while (next) {\n if (next.matches(selector)) {\n return [next]\n }\n\n next = next.nextElementSibling\n }\n\n return []\n },\n\n focusableChildren(element) {\n const focusables = [\n 'a',\n 'button',\n 'input',\n 'textarea',\n 'select',\n 'details',\n '[tabindex]',\n '[contenteditable=\"true\"]'\n ].map(selector => `${selector}:not([tabindex^=\"-\"])`).join(',')\n\n return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el))\n },\n\n getSelectorFromElement(element) {\n const selector = getSelector(element)\n\n if (selector) {\n return SelectorEngine.findOne(selector) ? selector : null\n }\n\n return null\n },\n\n getElementFromSelector(element) {\n const selector = getSelector(element)\n\n return selector ? SelectorEngine.findOne(selector) : null\n },\n\n getMultipleElementsFromSelector(element) {\n const selector = getSelector(element)\n\n return selector ? SelectorEngine.find(selector) : []\n }\n}\n\nexport default SelectorEngine\n"],"names":["getSelector","element","selector","getAttribute","hrefAttribute","includes","startsWith","split","trim","map","sel","parseSelector","join","SelectorEngine","find","document","documentElement","concat","Element","prototype","querySelectorAll","call","findOne","querySelector","children","filter","child","matches","parents","ancestor","parentNode","closest","push","prev","previous","previousElementSibling","next","nextElementSibling","focusableChildren","focusables","el","isDisabled","isVisible","getSelectorFromElement","getElementFromSelector","getMultipleElementsFromSelector"],"mappings":"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"} \ No newline at end of file
diff --git a/js/dist/dropdown.js b/js/dist/dropdown.js
index 1ac223fa8..09eaec0ab 100644
--- a/js/dist/dropdown.js
+++ b/js/dist/dropdown.js
@@ -1,19 +1,16 @@
/*!
- * Bootstrap dropdown.js v5.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Bootstrap dropdown.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@popperjs/core'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./base-component.js')) :
- typeof define === 'function' && define.amd ? define(['@popperjs/core', './dom/event-handler', './dom/manipulator', './dom/selector-engine', './base-component'], factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Dropdown = factory(global.Popper, global.EventHandler, global.Manipulator, global.SelectorEngine, global.Base));
-})(this, (function (Popper, EventHandler, Manipulator, SelectorEngine, BaseComponent) { 'use strict';
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@popperjs/core'), require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./util/index.js')) :
+ typeof define === 'function' && define.amd ? define(['@popperjs/core', './base-component', './dom/event-handler', './dom/manipulator', './dom/selector-engine', './util/index'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Dropdown = factory(global["@popperjs/core"], global.BaseComponent, global.EventHandler, global.Manipulator, global.SelectorEngine, global.Index));
+})(this, (function (Popper, BaseComponent, EventHandler, Manipulator, SelectorEngine, index_js) { 'use strict';
- const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
-
- function _interopNamespace(e) {
- if (e && e.__esModule) return e;
- const n = Object.create(null);
+ function _interopNamespaceDefault(e) {
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } });
if (e) {
for (const k in e) {
if (k !== 'default') {
@@ -29,206 +26,18 @@
return Object.freeze(n);
}
- const Popper__namespace = /*#__PURE__*/_interopNamespace(Popper);
- const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
- const Manipulator__default = /*#__PURE__*/_interopDefaultLegacy(Manipulator);
- const SelectorEngine__default = /*#__PURE__*/_interopDefaultLegacy(SelectorEngine);
- const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
+ const Popper__namespace = /*#__PURE__*/_interopNamespaceDefault(Popper);
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/index.js
+ * Bootstrap dropdown.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
- const toType = obj => {
- if (obj === null || obj === undefined) {
- return `${obj}`;
- }
-
- return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase();
- };
-
- const getSelector = element => {
- let selector = element.getAttribute('data-bs-target');
-
- if (!selector || selector === '#') {
- let hrefAttr = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes,
- // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
- // `document.querySelector` will rightfully complain it is invalid.
- // See https://github.com/twbs/bootstrap/issues/32273
-
- if (!hrefAttr || !hrefAttr.includes('#') && !hrefAttr.startsWith('.')) {
- return null;
- } // Just in case some CMS puts out a full URL with the anchor appended
-
-
- if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {
- hrefAttr = `#${hrefAttr.split('#')[1]}`;
- }
-
- selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null;
- }
-
- return selector;
- };
-
- const getElementFromSelector = element => {
- const selector = getSelector(element);
- return selector ? document.querySelector(selector) : null;
- };
-
- const isElement = obj => {
- if (!obj || typeof obj !== 'object') {
- return false;
- }
-
- if (typeof obj.jquery !== 'undefined') {
- obj = obj[0];
- }
-
- return typeof obj.nodeType !== 'undefined';
- };
-
- const getElement = obj => {
- if (isElement(obj)) {
- // it's a jQuery object or a node element
- return obj.jquery ? obj[0] : obj;
- }
-
- if (typeof obj === 'string' && obj.length > 0) {
- return document.querySelector(obj);
- }
-
- return null;
- };
-
- const typeCheckConfig = (componentName, config, configTypes) => {
- Object.keys(configTypes).forEach(property => {
- const expectedTypes = configTypes[property];
- const value = config[property];
- const valueType = value && isElement(value) ? 'element' : toType(value);
-
- if (!new RegExp(expectedTypes).test(valueType)) {
- throw new TypeError(`${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`);
- }
- });
- };
-
- const isVisible = element => {
- if (!isElement(element) || element.getClientRects().length === 0) {
- return false;
- }
-
- return getComputedStyle(element).getPropertyValue('visibility') === 'visible';
- };
-
- const isDisabled = element => {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return true;
- }
-
- if (element.classList.contains('disabled')) {
- return true;
- }
-
- if (typeof element.disabled !== 'undefined') {
- return element.disabled;
- }
-
- return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';
- };
-
- const noop = () => {};
-
- const getjQuery = () => {
- const {
- jQuery
- } = window;
-
- if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
- return jQuery;
- }
-
- return null;
- };
-
- const DOMContentLoadedCallbacks = [];
-
- const onDOMContentLoaded = callback => {
- if (document.readyState === 'loading') {
- // add listener on the first call when the document is in loading state
- if (!DOMContentLoadedCallbacks.length) {
- document.addEventListener('DOMContentLoaded', () => {
- DOMContentLoadedCallbacks.forEach(callback => callback());
- });
- }
-
- DOMContentLoadedCallbacks.push(callback);
- } else {
- callback();
- }
- };
-
- const isRTL = () => document.documentElement.dir === 'rtl';
-
- const defineJQueryPlugin = plugin => {
- onDOMContentLoaded(() => {
- const $ = getjQuery();
- /* istanbul ignore if */
-
- if ($) {
- const name = plugin.NAME;
- const JQUERY_NO_CONFLICT = $.fn[name];
- $.fn[name] = plugin.jQueryInterface;
- $.fn[name].Constructor = plugin;
-
- $.fn[name].noConflict = () => {
- $.fn[name] = JQUERY_NO_CONFLICT;
- return plugin.jQueryInterface;
- };
- }
- });
- };
- /**
- * Return the previous/next element of a list.
- *
- * @param {array} list The list of elements
- * @param activeElement The active element
- * @param shouldGetNext Choose to get next or previous element
- * @param isCycleAllowed
- * @return {Element|elem} The proper element
- */
-
-
- const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {
- let index = list.indexOf(activeElement); // if the element does not exist in the list return an element depending on the direction and if cycle is allowed
-
- if (index === -1) {
- return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0];
- }
-
- const listLength = list.length;
- index += shouldGetNext ? 1 : -1;
-
- if (isCycleAllowed) {
- index = (index + listLength) % listLength;
- }
-
- return list[Math.max(0, Math.min(index, listLength - 1))];
- };
/**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): dropdown.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- /**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'dropdown';
@@ -236,13 +45,11 @@
const EVENT_KEY = `.${DATA_KEY}`;
const DATA_API_KEY = '.data-api';
const ESCAPE_KEY = 'Escape';
- const SPACE_KEY = 'Space';
const TAB_KEY = 'Tab';
const ARROW_UP_KEY = 'ArrowUp';
const ARROW_DOWN_KEY = 'ArrowDown';
const RIGHT_MOUSE_BUTTON = 2; // MouseEvent.button value for the secondary button, usually the right button
- const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEY}|${ARROW_DOWN_KEY}|${ESCAPE_KEY}`);
const EVENT_HIDE = `hide${EVENT_KEY}`;
const EVENT_HIDDEN = `hidden${EVENT_KEY}`;
const EVENT_SHOW = `show${EVENT_KEY}`;
@@ -254,252 +61,204 @@
const CLASS_NAME_DROPUP = 'dropup';
const CLASS_NAME_DROPEND = 'dropend';
const CLASS_NAME_DROPSTART = 'dropstart';
- const CLASS_NAME_NAVBAR = 'navbar';
- const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="dropdown"]';
+ const CLASS_NAME_DROPUP_CENTER = 'dropup-center';
+ const CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center';
+ const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)';
+ const SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE}.${CLASS_NAME_SHOW}`;
const SELECTOR_MENU = '.dropdown-menu';
+ const SELECTOR_NAVBAR = '.navbar';
const SELECTOR_NAVBAR_NAV = '.navbar-nav';
const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)';
- const PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start';
- const PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end';
- const PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start';
- const PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end';
- const PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start';
- const PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start';
+ const PLACEMENT_TOP = index_js.isRTL() ? 'top-end' : 'top-start';
+ const PLACEMENT_TOPEND = index_js.isRTL() ? 'top-start' : 'top-end';
+ const PLACEMENT_BOTTOM = index_js.isRTL() ? 'bottom-end' : 'bottom-start';
+ const PLACEMENT_BOTTOMEND = index_js.isRTL() ? 'bottom-start' : 'bottom-end';
+ const PLACEMENT_RIGHT = index_js.isRTL() ? 'left-start' : 'right-start';
+ const PLACEMENT_LEFT = index_js.isRTL() ? 'right-start' : 'left-start';
+ const PLACEMENT_TOPCENTER = 'top';
+ const PLACEMENT_BOTTOMCENTER = 'bottom';
const Default = {
- offset: [0, 2],
+ autoClose: true,
boundary: 'clippingParents',
- reference: 'toggle',
display: 'dynamic',
+ offset: [0, 2],
popperConfig: null,
- autoClose: true
+ reference: 'toggle'
};
const DefaultType = {
- offset: '(array|string|function)',
+ autoClose: '(boolean|string)',
boundary: '(string|element)',
- reference: '(string|element|object)',
display: 'string',
+ offset: '(array|string|function)',
popperConfig: '(null|object|function)',
- autoClose: '(boolean|string)'
+ reference: '(string|element|object)'
};
+
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
- class Dropdown extends BaseComponent__default.default {
+ class Dropdown extends BaseComponent {
constructor(element, config) {
- super(element);
+ super(element, config);
this._popper = null;
- this._config = this._getConfig(config);
- this._menu = this._getMenuElement();
+ this._parent = this._element.parentNode; // dropdown wrapper
+ // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/
+ this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0] || SelectorEngine.findOne(SELECTOR_MENU, this._parent);
this._inNavbar = this._detectNavbar();
- } // Getters
-
+ }
+ // Getters
static get Default() {
return Default;
}
-
static get DefaultType() {
return DefaultType;
}
-
static get NAME() {
return NAME;
- } // Public
-
+ }
+ // Public
toggle() {
return this._isShown() ? this.hide() : this.show();
}
-
show() {
- if (isDisabled(this._element) || this._isShown(this._menu)) {
+ if (index_js.isDisabled(this._element) || this._isShown()) {
return;
}
-
const relatedTarget = {
relatedTarget: this._element
};
- const showEvent = EventHandler__default.default.trigger(this._element, EVENT_SHOW, relatedTarget);
-
+ const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, relatedTarget);
if (showEvent.defaultPrevented) {
return;
}
+ this._createPopper();
- const parent = Dropdown.getParentFromElement(this._element); // Totally disable Popper for Dropdowns in Navbar
-
- if (this._inNavbar) {
- Manipulator__default.default.setDataAttribute(this._menu, 'popper', 'none');
- } else {
- this._createPopper(parent);
- } // If this is a touch-enabled device we add extra
+ // If this is a touch-enabled device we add extra
// empty mouseover listeners to the body's immediate children;
// only needed because of broken event delegation on iOS
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
-
-
- if ('ontouchstart' in document.documentElement && !parent.closest(SELECTOR_NAVBAR_NAV)) {
- [].concat(...document.body.children).forEach(elem => EventHandler__default.default.on(elem, 'mouseover', noop));
+ if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {
+ for (const element of [].concat(...document.body.children)) {
+ EventHandler.on(element, 'mouseover', index_js.noop);
+ }
}
-
this._element.focus();
-
this._element.setAttribute('aria-expanded', true);
-
this._menu.classList.add(CLASS_NAME_SHOW);
-
this._element.classList.add(CLASS_NAME_SHOW);
-
- EventHandler__default.default.trigger(this._element, EVENT_SHOWN, relatedTarget);
+ EventHandler.trigger(this._element, EVENT_SHOWN, relatedTarget);
}
-
hide() {
- if (isDisabled(this._element) || !this._isShown(this._menu)) {
+ if (index_js.isDisabled(this._element) || !this._isShown()) {
return;
}
-
const relatedTarget = {
relatedTarget: this._element
};
-
this._completeHide(relatedTarget);
}
-
dispose() {
if (this._popper) {
this._popper.destroy();
}
-
super.dispose();
}
-
update() {
this._inNavbar = this._detectNavbar();
-
if (this._popper) {
this._popper.update();
}
- } // Private
-
+ }
+ // Private
_completeHide(relatedTarget) {
- const hideEvent = EventHandler__default.default.trigger(this._element, EVENT_HIDE, relatedTarget);
-
+ const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE, relatedTarget);
if (hideEvent.defaultPrevented) {
return;
- } // If this is a touch-enabled device we remove the extra
- // empty mouseover listeners we added for iOS support
-
+ }
+ // If this is a touch-enabled device we remove the extra
+ // empty mouseover listeners we added for iOS support
if ('ontouchstart' in document.documentElement) {
- [].concat(...document.body.children).forEach(elem => EventHandler__default.default.off(elem, 'mouseover', noop));
+ for (const element of [].concat(...document.body.children)) {
+ EventHandler.off(element, 'mouseover', index_js.noop);
+ }
}
-
if (this._popper) {
this._popper.destroy();
}
-
this._menu.classList.remove(CLASS_NAME_SHOW);
-
this._element.classList.remove(CLASS_NAME_SHOW);
-
this._element.setAttribute('aria-expanded', 'false');
-
- Manipulator__default.default.removeDataAttribute(this._menu, 'popper');
- EventHandler__default.default.trigger(this._element, EVENT_HIDDEN, relatedTarget);
+ Manipulator.removeDataAttribute(this._menu, 'popper');
+ EventHandler.trigger(this._element, EVENT_HIDDEN, relatedTarget);
}
-
_getConfig(config) {
- config = { ...this.constructor.Default,
- ...Manipulator__default.default.getDataAttributes(this._element),
- ...config
- };
- typeCheckConfig(NAME, config, this.constructor.DefaultType);
-
- if (typeof config.reference === 'object' && !isElement(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') {
+ config = super._getConfig(config);
+ if (typeof config.reference === 'object' && !index_js.isElement(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') {
// Popper virtual elements require a getBoundingClientRect method
throw new TypeError(`${NAME.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`);
}
-
return config;
}
-
- _createPopper(parent) {
+ _createPopper() {
if (typeof Popper__namespace === 'undefined') {
throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org)');
}
-
let referenceElement = this._element;
-
if (this._config.reference === 'parent') {
- referenceElement = parent;
- } else if (isElement(this._config.reference)) {
- referenceElement = getElement(this._config.reference);
+ referenceElement = this._parent;
+ } else if (index_js.isElement(this._config.reference)) {
+ referenceElement = index_js.getElement(this._config.reference);
} else if (typeof this._config.reference === 'object') {
referenceElement = this._config.reference;
}
-
const popperConfig = this._getPopperConfig();
-
- const isDisplayStatic = popperConfig.modifiers.find(modifier => modifier.name === 'applyStyles' && modifier.enabled === false);
this._popper = Popper__namespace.createPopper(referenceElement, this._menu, popperConfig);
-
- if (isDisplayStatic) {
- Manipulator__default.default.setDataAttribute(this._menu, 'popper', 'static');
- }
}
-
- _isShown(element = this._element) {
- return element.classList.contains(CLASS_NAME_SHOW);
+ _isShown() {
+ return this._menu.classList.contains(CLASS_NAME_SHOW);
}
-
- _getMenuElement() {
- return SelectorEngine__default.default.next(this._element, SELECTOR_MENU)[0];
- }
-
_getPlacement() {
- const parentDropdown = this._element.parentNode;
-
+ const parentDropdown = this._parent;
if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {
return PLACEMENT_RIGHT;
}
-
if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {
return PLACEMENT_LEFT;
- } // We need to trim the value because custom properties can also include spaces
-
+ }
+ if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) {
+ return PLACEMENT_TOPCENTER;
+ }
+ if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) {
+ return PLACEMENT_BOTTOMCENTER;
+ }
+ // We need to trim the value because custom properties can also include spaces
const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end';
-
if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {
return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP;
}
-
return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM;
}
-
_detectNavbar() {
- return this._element.closest(`.${CLASS_NAME_NAVBAR}`) !== null;
+ return this._element.closest(SELECTOR_NAVBAR) !== null;
}
-
_getOffset() {
const {
offset
} = this._config;
-
if (typeof offset === 'string') {
- return offset.split(',').map(val => Number.parseInt(val, 10));
+ return offset.split(',').map(value => Number.parseInt(value, 10));
}
-
if (typeof offset === 'function') {
return popperData => offset(popperData, this._element);
}
-
return offset;
}
-
_getPopperConfig() {
const defaultBsPopperConfig = {
placement: this._getPlacement(),
@@ -514,172 +273,128 @@
offset: this._getOffset()
}
}]
- }; // Disable Popper if we have a static display
+ };
- if (this._config.display === 'static') {
+ // Disable Popper if we have a static display or Dropdown is in Navbar
+ if (this._inNavbar || this._config.display === 'static') {
+ Manipulator.setDataAttribute(this._menu, 'popper', 'static'); // TODO: v6 remove
defaultBsPopperConfig.modifiers = [{
name: 'applyStyles',
enabled: false
}];
}
-
- return { ...defaultBsPopperConfig,
- ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)
+ return {
+ ...defaultBsPopperConfig,
+ ...index_js.execute(this._config.popperConfig, [defaultBsPopperConfig])
};
}
-
_selectMenuItem({
key,
target
}) {
- const items = SelectorEngine__default.default.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(isVisible);
-
+ const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => index_js.isVisible(element));
if (!items.length) {
return;
- } // if target isn't included in items (e.g. when expanding the dropdown)
- // allow cycling to get the last item in case key equals ARROW_UP_KEY
-
-
- getNextActiveElement(items, target, key === ARROW_DOWN_KEY, !items.includes(target)).focus();
- } // Static
+ }
+ // if target isn't included in items (e.g. when expanding the dropdown)
+ // allow cycling to get the last item in case key equals ARROW_UP_KEY
+ index_js.getNextActiveElement(items, target, key === ARROW_DOWN_KEY, !items.includes(target)).focus();
+ }
+ // Static
static jQueryInterface(config) {
return this.each(function () {
const data = Dropdown.getOrCreateInstance(this, config);
-
if (typeof config !== 'string') {
return;
}
-
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`);
}
-
data[config]();
});
}
-
static clearMenus(event) {
- if (event && (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY)) {
+ if (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY) {
return;
}
-
- const toggles = SelectorEngine__default.default.find(SELECTOR_DATA_TOGGLE);
-
- for (let i = 0, len = toggles.length; i < len; i++) {
- const context = Dropdown.getInstance(toggles[i]);
-
+ const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN);
+ for (const toggle of openToggles) {
+ const context = Dropdown.getInstance(toggle);
if (!context || context._config.autoClose === false) {
continue;
}
-
- if (!context._isShown()) {
+ const composedPath = event.composedPath();
+ const isMenuTarget = composedPath.includes(context._menu);
+ if (composedPath.includes(context._element) || context._config.autoClose === 'inside' && !isMenuTarget || context._config.autoClose === 'outside' && isMenuTarget) {
continue;
}
+ // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu
+ if (context._menu.contains(event.target) && (event.type === 'keyup' && event.key === TAB_KEY || /input|select|option|textarea|form/i.test(event.target.tagName))) {
+ continue;
+ }
const relatedTarget = {
relatedTarget: context._element
};
-
- if (event) {
- const composedPath = event.composedPath();
- const isMenuTarget = composedPath.includes(context._menu);
-
- if (composedPath.includes(context._element) || context._config.autoClose === 'inside' && !isMenuTarget || context._config.autoClose === 'outside' && isMenuTarget) {
- continue;
- } // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu
-
-
- if (context._menu.contains(event.target) && (event.type === 'keyup' && event.key === TAB_KEY || /input|select|option|textarea|form/i.test(event.target.tagName))) {
- continue;
- }
-
- if (event.type === 'click') {
- relatedTarget.clickEvent = event;
- }
+ if (event.type === 'click') {
+ relatedTarget.clickEvent = event;
}
-
context._completeHide(relatedTarget);
}
}
-
- static getParentFromElement(element) {
- return getElementFromSelector(element) || element.parentNode;
- }
-
static dataApiKeydownHandler(event) {
- // If not input/textarea:
- // - And not a key in REGEXP_KEYDOWN => not a dropdown command
- // If input/textarea:
- // - If space key => not a dropdown command
- // - If key is other than escape
- // - If key is not up or down => not a dropdown command
- // - If trigger inside the menu => not a dropdown command
- if (/input|textarea/i.test(event.target.tagName) ? event.key === SPACE_KEY || event.key !== ESCAPE_KEY && (event.key !== ARROW_DOWN_KEY && event.key !== ARROW_UP_KEY || event.target.closest(SELECTOR_MENU)) : !REGEXP_KEYDOWN.test(event.key)) {
- return;
- }
-
- const isActive = this.classList.contains(CLASS_NAME_SHOW);
+ // If not an UP | DOWN | ESCAPE key => not a dropdown command
+ // If input/textarea && if key is other than ESCAPE => not a dropdown command
- if (!isActive && event.key === ESCAPE_KEY) {
+ const isInput = /input|textarea/i.test(event.target.tagName);
+ const isEscapeEvent = event.key === ESCAPE_KEY;
+ const isUpOrDownEvent = [ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key);
+ if (!isUpOrDownEvent && !isEscapeEvent) {
return;
}
-
- event.preventDefault();
- event.stopPropagation();
-
- if (isDisabled(this)) {
+ if (isInput && !isEscapeEvent) {
return;
}
+ event.preventDefault();
- const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine__default.default.prev(this, SELECTOR_DATA_TOGGLE)[0];
+ // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/
+ const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0] || SelectorEngine.next(this, SELECTOR_DATA_TOGGLE)[0] || SelectorEngine.findOne(SELECTOR_DATA_TOGGLE, event.delegateTarget.parentNode);
const instance = Dropdown.getOrCreateInstance(getToggleButton);
-
- if (event.key === ESCAPE_KEY) {
- instance.hide();
- return;
- }
-
- if (event.key === ARROW_UP_KEY || event.key === ARROW_DOWN_KEY) {
- if (!isActive) {
- instance.show();
- }
-
+ if (isUpOrDownEvent) {
+ event.stopPropagation();
+ instance.show();
instance._selectMenuItem(event);
-
return;
}
-
- if (!isActive || event.key === SPACE_KEY) {
- Dropdown.clearMenus();
+ if (instance._isShown()) {
+ // else is escape and we check if it is shown
+ event.stopPropagation();
+ instance.hide();
+ getToggleButton.focus();
}
}
-
}
+
/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
+ * Data API implementation
*/
-
- EventHandler__default.default.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler);
- EventHandler__default.default.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler);
- EventHandler__default.default.on(document, EVENT_CLICK_DATA_API, Dropdown.clearMenus);
- EventHandler__default.default.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus);
- EventHandler__default.default.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
+ EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler);
+ EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler);
+ EventHandler.on(document, EVENT_CLICK_DATA_API, Dropdown.clearMenus);
+ EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus);
+ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
event.preventDefault();
Dropdown.getOrCreateInstance(this).toggle();
});
+
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .Dropdown to jQuery only if jQuery is present
*/
- defineJQueryPlugin(Dropdown);
+ index_js.defineJQueryPlugin(Dropdown);
return Dropdown;
diff --git a/js/dist/dropdown.js.map b/js/dist/dropdown.js.map
index 171983de2..4b6914585 100644
--- a/js/dist/dropdown.js.map
+++ b/js/dist/dropdown.js.map
@@ -1 +1 @@
-{"version":3,"file":"dropdown.js","sources":["../src/util/index.js","../src/dropdown.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n// Shoutout AngusCroll (https://goo.gl/pxwQGp)\nconst toType = obj => {\n if (obj === null || obj === undefined) {\n return `${obj}`\n }\n\n return {}.toString.call(obj).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttr = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {\n hrefAttr = `#${hrefAttr.split('#')[1]}`\n }\n\n selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null\n }\n\n return selector\n}\n\nconst getSelectorFromElement = element => {\n const selector = getSelector(element)\n\n if (selector) {\n return document.querySelector(selector) ? selector : null\n }\n\n return null\n}\n\nconst getElementFromSelector = element => {\n const selector = getSelector(element)\n\n return selector ? document.querySelector(selector) : null\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = obj => {\n if (!obj || typeof obj !== 'object') {\n return false\n }\n\n if (typeof obj.jquery !== 'undefined') {\n obj = obj[0]\n }\n\n return typeof obj.nodeType !== 'undefined'\n}\n\nconst getElement = obj => {\n if (isElement(obj)) { // it's a jQuery object or a node element\n return obj.jquery ? obj[0] : obj\n }\n\n if (typeof obj === 'string' && obj.length > 0) {\n return document.querySelector(obj)\n }\n\n return null\n}\n\nconst typeCheckConfig = (componentName, config, configTypes) => {\n Object.keys(configTypes).forEach(property => {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${componentName.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n })\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n return getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n // eslint-disable-next-line no-unused-expressions\n element.offsetHeight\n}\n\nconst getjQuery = () => {\n const { jQuery } = window\n\n if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n DOMContentLoadedCallbacks.forEach(callback => callback())\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = callback => {\n if (typeof callback === 'function') {\n callback()\n }\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element depending on the direction and if cycle is allowed\n if (index === -1) {\n return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]\n }\n\n const listLength = list.length\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n getElement,\n getUID,\n getSelectorFromElement,\n getElementFromSelector,\n getTransitionDurationFromElement,\n triggerTransitionEnd,\n isElement,\n typeCheckConfig,\n isVisible,\n isDisabled,\n findShadowRoot,\n noop,\n getNextActiveElement,\n reflow,\n getjQuery,\n onDOMContentLoaded,\n isRTL,\n defineJQueryPlugin,\n execute,\n executeAfterTransition\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): dropdown.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport * as Popper from '@popperjs/core'\n\nimport {\n defineJQueryPlugin,\n getElement,\n getElementFromSelector,\n getNextActiveElement,\n isDisabled,\n isElement,\n isRTL,\n isVisible,\n noop,\n typeCheckConfig\n} from './util/index'\nimport EventHandler from './dom/event-handler'\nimport Manipulator from './dom/manipulator'\nimport SelectorEngine from './dom/selector-engine'\nimport BaseComponent from './base-component'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'dropdown'\nconst DATA_KEY = 'bs.dropdown'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst ESCAPE_KEY = 'Escape'\nconst SPACE_KEY = 'Space'\nconst TAB_KEY = 'Tab'\nconst ARROW_UP_KEY = 'ArrowUp'\nconst ARROW_DOWN_KEY = 'ArrowDown'\nconst RIGHT_MOUSE_BUTTON = 2 // MouseEvent.button value for the secondary button, usually the right button\n\nconst REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEY}|${ARROW_DOWN_KEY}|${ESCAPE_KEY}`)\n\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_DROPUP = 'dropup'\nconst CLASS_NAME_DROPEND = 'dropend'\nconst CLASS_NAME_DROPSTART = 'dropstart'\nconst CLASS_NAME_NAVBAR = 'navbar'\n\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"dropdown\"]'\nconst SELECTOR_MENU = '.dropdown-menu'\nconst SELECTOR_NAVBAR_NAV = '.navbar-nav'\nconst SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'\n\nconst PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start'\nconst PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end'\nconst PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start'\nconst PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end'\nconst PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start'\nconst PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start'\n\nconst Default = {\n offset: [0, 2],\n boundary: 'clippingParents',\n reference: 'toggle',\n display: 'dynamic',\n popperConfig: null,\n autoClose: true\n}\n\nconst DefaultType = {\n offset: '(array|string|function)',\n boundary: '(string|element)',\n reference: '(string|element|object)',\n display: 'string',\n popperConfig: '(null|object|function)',\n autoClose: '(boolean|string)'\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Dropdown extends BaseComponent {\n constructor(element, config) {\n super(element)\n\n this._popper = null\n this._config = this._getConfig(config)\n this._menu = this._getMenuElement()\n this._inNavbar = this._detectNavbar()\n }\n\n // Getters\n\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\n toggle() {\n return this._isShown() ? this.hide() : this.show()\n }\n\n show() {\n if (isDisabled(this._element) || this._isShown(this._menu)) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, relatedTarget)\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n const parent = Dropdown.getParentFromElement(this._element)\n // Totally disable Popper for Dropdowns in Navbar\n if (this._inNavbar) {\n Manipulator.setDataAttribute(this._menu, 'popper', 'none')\n } else {\n this._createPopper(parent)\n }\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement &&\n !parent.closest(SELECTOR_NAVBAR_NAV)) {\n [].concat(...document.body.children)\n .forEach(elem => EventHandler.on(elem, 'mouseover', noop))\n }\n\n this._element.focus()\n this._element.setAttribute('aria-expanded', true)\n\n this._menu.classList.add(CLASS_NAME_SHOW)\n this._element.classList.add(CLASS_NAME_SHOW)\n EventHandler.trigger(this._element, EVENT_SHOWN, relatedTarget)\n }\n\n hide() {\n if (isDisabled(this._element) || !this._isShown(this._menu)) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n\n this._completeHide(relatedTarget)\n }\n\n dispose() {\n if (this._popper) {\n this._popper.destroy()\n }\n\n super.dispose()\n }\n\n update() {\n this._inNavbar = this._detectNavbar()\n if (this._popper) {\n this._popper.update()\n }\n }\n\n // Private\n\n _completeHide(relatedTarget) {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE, relatedTarget)\n if (hideEvent.defaultPrevented) {\n return\n }\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n [].concat(...document.body.children)\n .forEach(elem => EventHandler.off(elem, 'mouseover', noop))\n }\n\n if (this._popper) {\n this._popper.destroy()\n }\n\n this._menu.classList.remove(CLASS_NAME_SHOW)\n this._element.classList.remove(CLASS_NAME_SHOW)\n this._element.setAttribute('aria-expanded', 'false')\n Manipulator.removeDataAttribute(this._menu, 'popper')\n EventHandler.trigger(this._element, EVENT_HIDDEN, relatedTarget)\n }\n\n _getConfig(config) {\n config = {\n ...this.constructor.Default,\n ...Manipulator.getDataAttributes(this._element),\n ...config\n }\n\n typeCheckConfig(NAME, config, this.constructor.DefaultType)\n\n if (typeof config.reference === 'object' && !isElement(config.reference) &&\n typeof config.reference.getBoundingClientRect !== 'function'\n ) {\n // Popper virtual elements require a getBoundingClientRect method\n throw new TypeError(`${NAME.toUpperCase()}: Option \"reference\" provided type \"object\" without a required \"getBoundingClientRect\" method.`)\n }\n\n return config\n }\n\n _createPopper(parent) {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s dropdowns require Popper (https://popper.js.org)')\n }\n\n let referenceElement = this._element\n\n if (this._config.reference === 'parent') {\n referenceElement = parent\n } else if (isElement(this._config.reference)) {\n referenceElement = getElement(this._config.reference)\n } else if (typeof this._config.reference === 'object') {\n referenceElement = this._config.reference\n }\n\n const popperConfig = this._getPopperConfig()\n const isDisplayStatic = popperConfig.modifiers.find(modifier => modifier.name === 'applyStyles' && modifier.enabled === false)\n\n this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig)\n\n if (isDisplayStatic) {\n Manipulator.setDataAttribute(this._menu, 'popper', 'static')\n }\n }\n\n _isShown(element = this._element) {\n return element.classList.contains(CLASS_NAME_SHOW)\n }\n\n _getMenuElement() {\n return SelectorEngine.next(this._element, SELECTOR_MENU)[0]\n }\n\n _getPlacement() {\n const parentDropdown = this._element.parentNode\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {\n return PLACEMENT_RIGHT\n }\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {\n return PLACEMENT_LEFT\n }\n\n // We need to trim the value because custom properties can also include spaces\n const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end'\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {\n return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP\n }\n\n return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM\n }\n\n _detectNavbar() {\n return this._element.closest(`.${CLASS_NAME_NAVBAR}`) !== null\n }\n\n _getOffset() {\n const { offset } = this._config\n\n if (typeof offset === 'string') {\n return offset.split(',').map(val => Number.parseInt(val, 10))\n }\n\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element)\n }\n\n return offset\n }\n\n _getPopperConfig() {\n const defaultBsPopperConfig = {\n placement: this._getPlacement(),\n modifiers: [{\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n },\n {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n }]\n }\n\n // Disable Popper if we have a static display\n if (this._config.display === 'static') {\n defaultBsPopperConfig.modifiers = [{\n name: 'applyStyles',\n enabled: false\n }]\n }\n\n return {\n ...defaultBsPopperConfig,\n ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)\n }\n }\n\n _selectMenuItem({ key, target }) {\n const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(isVisible)\n\n if (!items.length) {\n return\n }\n\n // if target isn't included in items (e.g. when expanding the dropdown)\n // allow cycling to get the last item in case key equals ARROW_UP_KEY\n getNextActiveElement(items, target, key === ARROW_DOWN_KEY, !items.includes(target)).focus()\n }\n\n // Static\n\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Dropdown.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n\n static clearMenus(event) {\n if (event && (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY))) {\n return\n }\n\n const toggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE)\n\n for (let i = 0, len = toggles.length; i < len; i++) {\n const context = Dropdown.getInstance(toggles[i])\n if (!context || context._config.autoClose === false) {\n continue\n }\n\n if (!context._isShown()) {\n continue\n }\n\n const relatedTarget = {\n relatedTarget: context._element\n }\n\n if (event) {\n const composedPath = event.composedPath()\n const isMenuTarget = composedPath.includes(context._menu)\n if (\n composedPath.includes(context._element) ||\n (context._config.autoClose === 'inside' && !isMenuTarget) ||\n (context._config.autoClose === 'outside' && isMenuTarget)\n ) {\n continue\n }\n\n // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu\n if (context._menu.contains(event.target) && ((event.type === 'keyup' && event.key === TAB_KEY) || /input|select|option|textarea|form/i.test(event.target.tagName))) {\n continue\n }\n\n if (event.type === 'click') {\n relatedTarget.clickEvent = event\n }\n }\n\n context._completeHide(relatedTarget)\n }\n }\n\n static getParentFromElement(element) {\n return getElementFromSelector(element) || element.parentNode\n }\n\n static dataApiKeydownHandler(event) {\n // If not input/textarea:\n // - And not a key in REGEXP_KEYDOWN => not a dropdown command\n // If input/textarea:\n // - If space key => not a dropdown command\n // - If key is other than escape\n // - If key is not up or down => not a dropdown command\n // - If trigger inside the menu => not a dropdown command\n if (/input|textarea/i.test(event.target.tagName) ?\n event.key === SPACE_KEY || (event.key !== ESCAPE_KEY &&\n ((event.key !== ARROW_DOWN_KEY && event.key !== ARROW_UP_KEY) ||\n event.target.closest(SELECTOR_MENU))) :\n !REGEXP_KEYDOWN.test(event.key)) {\n return\n }\n\n const isActive = this.classList.contains(CLASS_NAME_SHOW)\n\n if (!isActive && event.key === ESCAPE_KEY) {\n return\n }\n\n event.preventDefault()\n event.stopPropagation()\n\n if (isDisabled(this)) {\n return\n }\n\n const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0]\n const instance = Dropdown.getOrCreateInstance(getToggleButton)\n\n if (event.key === ESCAPE_KEY) {\n instance.hide()\n return\n }\n\n if (event.key === ARROW_UP_KEY || event.key === ARROW_DOWN_KEY) {\n if (!isActive) {\n instance.show()\n }\n\n instance._selectMenuItem(event)\n return\n }\n\n if (!isActive || event.key === SPACE_KEY) {\n Dropdown.clearMenus()\n }\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler)\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler)\nEventHandler.on(document, EVENT_CLICK_DATA_API, Dropdown.clearMenus)\nEventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus)\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n event.preventDefault()\n Dropdown.getOrCreateInstance(this).toggle()\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Dropdown to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Dropdown)\n\nexport default Dropdown\n"],"names":["toType","obj","undefined","toString","call","match","toLowerCase","getSelector","element","selector","getAttribute","hrefAttr","includes","startsWith","split","trim","getElementFromSelector","document","querySelector","isElement","jquery","nodeType","getElement","length","typeCheckConfig","componentName","config","configTypes","Object","keys","forEach","property","expectedTypes","value","valueType","RegExp","test","TypeError","toUpperCase","isVisible","getClientRects","getComputedStyle","getPropertyValue","isDisabled","Node","ELEMENT_NODE","classList","contains","disabled","hasAttribute","noop","getjQuery","jQuery","window","body","DOMContentLoadedCallbacks","onDOMContentLoaded","callback","readyState","addEventListener","push","isRTL","documentElement","dir","defineJQueryPlugin","plugin","$","name","NAME","JQUERY_NO_CONFLICT","fn","jQueryInterface","Constructor","noConflict","getNextActiveElement","list","activeElement","shouldGetNext","isCycleAllowed","index","indexOf","listLength","Math","max","min","DATA_KEY","EVENT_KEY","DATA_API_KEY","ESCAPE_KEY","SPACE_KEY","TAB_KEY","ARROW_UP_KEY","ARROW_DOWN_KEY","RIGHT_MOUSE_BUTTON","REGEXP_KEYDOWN","EVENT_HIDE","EVENT_HIDDEN","EVENT_SHOW","EVENT_SHOWN","EVENT_CLICK_DATA_API","EVENT_KEYDOWN_DATA_API","EVENT_KEYUP_DATA_API","CLASS_NAME_SHOW","CLASS_NAME_DROPUP","CLASS_NAME_DROPEND","CLASS_NAME_DROPSTART","CLASS_NAME_NAVBAR","SELECTOR_DATA_TOGGLE","SELECTOR_MENU","SELECTOR_NAVBAR_NAV","SELECTOR_VISIBLE_ITEMS","PLACEMENT_TOP","PLACEMENT_TOPEND","PLACEMENT_BOTTOM","PLACEMENT_BOTTOMEND","PLACEMENT_RIGHT","PLACEMENT_LEFT","Default","offset","boundary","reference","display","popperConfig","autoClose","DefaultType","Dropdown","BaseComponent","constructor","_popper","_config","_getConfig","_menu","_getMenuElement","_inNavbar","_detectNavbar","toggle","_isShown","hide","show","_element","relatedTarget","showEvent","EventHandler","trigger","defaultPrevented","parent","getParentFromElement","Manipulator","setDataAttribute","_createPopper","closest","concat","children","elem","on","focus","setAttribute","add","_completeHide","dispose","destroy","update","hideEvent","off","remove","removeDataAttribute","getDataAttributes","getBoundingClientRect","Popper","referenceElement","_getPopperConfig","isDisplayStatic","modifiers","find","modifier","enabled","createPopper","SelectorEngine","next","_getPlacement","parentDropdown","parentNode","isEnd","_getOffset","map","val","Number","parseInt","popperData","defaultBsPopperConfig","placement","options","_selectMenuItem","key","target","items","filter","each","data","getOrCreateInstance","clearMenus","event","button","type","toggles","i","len","context","getInstance","composedPath","isMenuTarget","tagName","clickEvent","dataApiKeydownHandler","isActive","preventDefault","stopPropagation","getToggleButton","matches","prev","instance"],"mappings":"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"} \ No newline at end of file
+{"version":3,"file":"dropdown.js","sources":["../src/dropdown.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap dropdown.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport * as Popper from '@popperjs/core'\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport Manipulator from './dom/manipulator.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport {\n defineJQueryPlugin,\n execute,\n getElement,\n getNextActiveElement,\n isDisabled,\n isElement,\n isRTL,\n isVisible,\n noop\n} from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'dropdown'\nconst DATA_KEY = 'bs.dropdown'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst ESCAPE_KEY = 'Escape'\nconst TAB_KEY = 'Tab'\nconst ARROW_UP_KEY = 'ArrowUp'\nconst ARROW_DOWN_KEY = 'ArrowDown'\nconst RIGHT_MOUSE_BUTTON = 2 // MouseEvent.button value for the secondary button, usually the right button\n\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_DROPUP = 'dropup'\nconst CLASS_NAME_DROPEND = 'dropend'\nconst CLASS_NAME_DROPSTART = 'dropstart'\nconst CLASS_NAME_DROPUP_CENTER = 'dropup-center'\nconst CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center'\n\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"dropdown\"]:not(.disabled):not(:disabled)'\nconst SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE}.${CLASS_NAME_SHOW}`\nconst SELECTOR_MENU = '.dropdown-menu'\nconst SELECTOR_NAVBAR = '.navbar'\nconst SELECTOR_NAVBAR_NAV = '.navbar-nav'\nconst SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'\n\nconst PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start'\nconst PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end'\nconst PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start'\nconst PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end'\nconst PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start'\nconst PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start'\nconst PLACEMENT_TOPCENTER = 'top'\nconst PLACEMENT_BOTTOMCENTER = 'bottom'\n\nconst Default = {\n autoClose: true,\n boundary: 'clippingParents',\n display: 'dynamic',\n offset: [0, 2],\n popperConfig: null,\n reference: 'toggle'\n}\n\nconst DefaultType = {\n autoClose: '(boolean|string)',\n boundary: '(string|element)',\n display: 'string',\n offset: '(array|string|function)',\n popperConfig: '(null|object|function)',\n reference: '(string|element|object)'\n}\n\n/**\n * Class definition\n */\n\nclass Dropdown extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._popper = null\n this._parent = this._element.parentNode // dropdown wrapper\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] ||\n SelectorEngine.prev(this._element, SELECTOR_MENU)[0] ||\n SelectorEngine.findOne(SELECTOR_MENU, this._parent)\n this._inNavbar = this._detectNavbar()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle() {\n return this._isShown() ? this.hide() : this.show()\n }\n\n show() {\n if (isDisabled(this._element) || this._isShown()) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, relatedTarget)\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._createPopper()\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop)\n }\n }\n\n this._element.focus()\n this._element.setAttribute('aria-expanded', true)\n\n this._menu.classList.add(CLASS_NAME_SHOW)\n this._element.classList.add(CLASS_NAME_SHOW)\n EventHandler.trigger(this._element, EVENT_SHOWN, relatedTarget)\n }\n\n hide() {\n if (isDisabled(this._element) || !this._isShown()) {\n return\n }\n\n const relatedTarget = {\n relatedTarget: this._element\n }\n\n this._completeHide(relatedTarget)\n }\n\n dispose() {\n if (this._popper) {\n this._popper.destroy()\n }\n\n super.dispose()\n }\n\n update() {\n this._inNavbar = this._detectNavbar()\n if (this._popper) {\n this._popper.update()\n }\n }\n\n // Private\n _completeHide(relatedTarget) {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE, relatedTarget)\n if (hideEvent.defaultPrevented) {\n return\n }\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop)\n }\n }\n\n if (this._popper) {\n this._popper.destroy()\n }\n\n this._menu.classList.remove(CLASS_NAME_SHOW)\n this._element.classList.remove(CLASS_NAME_SHOW)\n this._element.setAttribute('aria-expanded', 'false')\n Manipulator.removeDataAttribute(this._menu, 'popper')\n EventHandler.trigger(this._element, EVENT_HIDDEN, relatedTarget)\n }\n\n _getConfig(config) {\n config = super._getConfig(config)\n\n if (typeof config.reference === 'object' && !isElement(config.reference) &&\n typeof config.reference.getBoundingClientRect !== 'function'\n ) {\n // Popper virtual elements require a getBoundingClientRect method\n throw new TypeError(`${NAME.toUpperCase()}: Option \"reference\" provided type \"object\" without a required \"getBoundingClientRect\" method.`)\n }\n\n return config\n }\n\n _createPopper() {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s dropdowns require Popper (https://popper.js.org)')\n }\n\n let referenceElement = this._element\n\n if (this._config.reference === 'parent') {\n referenceElement = this._parent\n } else if (isElement(this._config.reference)) {\n referenceElement = getElement(this._config.reference)\n } else if (typeof this._config.reference === 'object') {\n referenceElement = this._config.reference\n }\n\n const popperConfig = this._getPopperConfig()\n this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig)\n }\n\n _isShown() {\n return this._menu.classList.contains(CLASS_NAME_SHOW)\n }\n\n _getPlacement() {\n const parentDropdown = this._parent\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {\n return PLACEMENT_RIGHT\n }\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) {\n return PLACEMENT_LEFT\n }\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) {\n return PLACEMENT_TOPCENTER\n }\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) {\n return PLACEMENT_BOTTOMCENTER\n }\n\n // We need to trim the value because custom properties can also include spaces\n const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end'\n\n if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {\n return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP\n }\n\n return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM\n }\n\n _detectNavbar() {\n return this._element.closest(SELECTOR_NAVBAR) !== null\n }\n\n _getOffset() {\n const { offset } = this._config\n\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10))\n }\n\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element)\n }\n\n return offset\n }\n\n _getPopperConfig() {\n const defaultBsPopperConfig = {\n placement: this._getPlacement(),\n modifiers: [{\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n },\n {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n }]\n }\n\n // Disable Popper if we have a static display or Dropdown is in Navbar\n if (this._inNavbar || this._config.display === 'static') {\n Manipulator.setDataAttribute(this._menu, 'popper', 'static') // TODO: v6 remove\n defaultBsPopperConfig.modifiers = [{\n name: 'applyStyles',\n enabled: false\n }]\n }\n\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [defaultBsPopperConfig])\n }\n }\n\n _selectMenuItem({ key, target }) {\n const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element))\n\n if (!items.length) {\n return\n }\n\n // if target isn't included in items (e.g. when expanding the dropdown)\n // allow cycling to get the last item in case key equals ARROW_UP_KEY\n getNextActiveElement(items, target, key === ARROW_DOWN_KEY, !items.includes(target)).focus()\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Dropdown.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n\n static clearMenus(event) {\n if (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY)) {\n return\n }\n\n const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN)\n\n for (const toggle of openToggles) {\n const context = Dropdown.getInstance(toggle)\n if (!context || context._config.autoClose === false) {\n continue\n }\n\n const composedPath = event.composedPath()\n const isMenuTarget = composedPath.includes(context._menu)\n if (\n composedPath.includes(context._element) ||\n (context._config.autoClose === 'inside' && !isMenuTarget) ||\n (context._config.autoClose === 'outside' && isMenuTarget)\n ) {\n continue\n }\n\n // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu\n if (context._menu.contains(event.target) && ((event.type === 'keyup' && event.key === TAB_KEY) || /input|select|option|textarea|form/i.test(event.target.tagName))) {\n continue\n }\n\n const relatedTarget = { relatedTarget: context._element }\n\n if (event.type === 'click') {\n relatedTarget.clickEvent = event\n }\n\n context._completeHide(relatedTarget)\n }\n }\n\n static dataApiKeydownHandler(event) {\n // If not an UP | DOWN | ESCAPE key => not a dropdown command\n // If input/textarea && if key is other than ESCAPE => not a dropdown command\n\n const isInput = /input|textarea/i.test(event.target.tagName)\n const isEscapeEvent = event.key === ESCAPE_KEY\n const isUpOrDownEvent = [ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key)\n\n if (!isUpOrDownEvent && !isEscapeEvent) {\n return\n }\n\n if (isInput && !isEscapeEvent) {\n return\n }\n\n event.preventDefault()\n\n // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/\n const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ?\n this :\n (SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0] ||\n SelectorEngine.next(this, SELECTOR_DATA_TOGGLE)[0] ||\n SelectorEngine.findOne(SELECTOR_DATA_TOGGLE, event.delegateTarget.parentNode))\n\n const instance = Dropdown.getOrCreateInstance(getToggleButton)\n\n if (isUpOrDownEvent) {\n event.stopPropagation()\n instance.show()\n instance._selectMenuItem(event)\n return\n }\n\n if (instance._isShown()) { // else is escape and we check if it is shown\n event.stopPropagation()\n instance.hide()\n getToggleButton.focus()\n }\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler)\nEventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler)\nEventHandler.on(document, EVENT_CLICK_DATA_API, Dropdown.clearMenus)\nEventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus)\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n event.preventDefault()\n Dropdown.getOrCreateInstance(this).toggle()\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Dropdown)\n\nexport default Dropdown\n"],"names":["NAME","DATA_KEY","EVENT_KEY","DATA_API_KEY","ESCAPE_KEY","TAB_KEY","ARROW_UP_KEY","ARROW_DOWN_KEY","RIGHT_MOUSE_BUTTON","EVENT_HIDE","EVENT_HIDDEN","EVENT_SHOW","EVENT_SHOWN","EVENT_CLICK_DATA_API","EVENT_KEYDOWN_DATA_API","EVENT_KEYUP_DATA_API","CLASS_NAME_SHOW","CLASS_NAME_DROPUP","CLASS_NAME_DROPEND","CLASS_NAME_DROPSTART","CLASS_NAME_DROPUP_CENTER","CLASS_NAME_DROPDOWN_CENTER","SELECTOR_DATA_TOGGLE","SELECTOR_DATA_TOGGLE_SHOWN","SELECTOR_MENU","SELECTOR_NAVBAR","SELECTOR_NAVBAR_NAV","SELECTOR_VISIBLE_ITEMS","PLACEMENT_TOP","isRTL","PLACEMENT_TOPEND","PLACEMENT_BOTTOM","PLACEMENT_BOTTOMEND","PLACEMENT_RIGHT","PLACEMENT_LEFT","PLACEMENT_TOPCENTER","PLACEMENT_BOTTOMCENTER","Default","autoClose","boundary","display","offset","popperConfig","reference","DefaultType","Dropdown","BaseComponent","constructor","element","config","_popper","_parent","_element","parentNode","_menu","SelectorEngine","next","prev","findOne","_inNavbar","_detectNavbar","toggle","_isShown","hide","show","isDisabled","relatedTarget","showEvent","EventHandler","trigger","defaultPrevented","_createPopper","document","documentElement","closest","concat","body","children","on","noop","focus","setAttribute","classList","add","_completeHide","dispose","destroy","update","hideEvent","off","remove","Manipulator","removeDataAttribute","_getConfig","isElement","getBoundingClientRect","TypeError","toUpperCase","Popper","referenceElement","_config","getElement","_getPopperConfig","createPopper","contains","_getPlacement","parentDropdown","isEnd","getComputedStyle","getPropertyValue","trim","_getOffset","split","map","value","Number","parseInt","popperData","defaultBsPopperConfig","placement","modifiers","name","options","setDataAttribute","enabled","execute","_selectMenuItem","key","target","items","find","filter","isVisible","length","getNextActiveElement","includes","jQueryInterface","each","data","getOrCreateInstance","clearMenus","event","button","type","openToggles","context","getInstance","composedPath","isMenuTarget","test","tagName","clickEvent","dataApiKeydownHandler","isInput","isEscapeEvent","isUpOrDownEvent","preventDefault","getToggleButton","matches","delegateTarget","instance","stopPropagation","defineJQueryPlugin"],"mappings":"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"} \ No newline at end of file
diff --git a/js/dist/modal.js b/js/dist/modal.js
index c6983290e..b67d11ad3 100644
--- a/js/dist/modal.js
+++ b/js/dist/modal.js
@@ -1,622 +1,24 @@
/*!
- * Bootstrap modal.js v5.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Bootstrap modal.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./base-component.js')) :
- typeof define === 'function' && define.amd ? define(['./dom/event-handler', './dom/manipulator', './dom/selector-engine', './base-component'], factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Modal = factory(global.EventHandler, global.Manipulator, global.SelectorEngine, global.Base));
-})(this, (function (EventHandler, Manipulator, SelectorEngine, BaseComponent) { 'use strict';
-
- const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
-
- const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
- const Manipulator__default = /*#__PURE__*/_interopDefaultLegacy(Manipulator);
- const SelectorEngine__default = /*#__PURE__*/_interopDefaultLegacy(SelectorEngine);
- const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
-
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/index.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- const MILLISECONDS_MULTIPLIER = 1000;
- const TRANSITION_END = 'transitionend'; // Shoutout AngusCroll (https://goo.gl/pxwQGp)
-
- const toType = obj => {
- if (obj === null || obj === undefined) {
- return `${obj}`;
- }
-
- return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase();
- };
-
- const getSelector = element => {
- let selector = element.getAttribute('data-bs-target');
-
- if (!selector || selector === '#') {
- let hrefAttr = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes,
- // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
- // `document.querySelector` will rightfully complain it is invalid.
- // See https://github.com/twbs/bootstrap/issues/32273
-
- if (!hrefAttr || !hrefAttr.includes('#') && !hrefAttr.startsWith('.')) {
- return null;
- } // Just in case some CMS puts out a full URL with the anchor appended
-
-
- if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {
- hrefAttr = `#${hrefAttr.split('#')[1]}`;
- }
-
- selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null;
- }
-
- return selector;
- };
-
- const getElementFromSelector = element => {
- const selector = getSelector(element);
- return selector ? document.querySelector(selector) : null;
- };
-
- const getTransitionDurationFromElement = element => {
- if (!element) {
- return 0;
- } // Get transition-duration of the element
-
-
- let {
- transitionDuration,
- transitionDelay
- } = window.getComputedStyle(element);
- const floatTransitionDuration = Number.parseFloat(transitionDuration);
- const floatTransitionDelay = Number.parseFloat(transitionDelay); // Return 0 if element or transition duration is not found
-
- if (!floatTransitionDuration && !floatTransitionDelay) {
- return 0;
- } // If multiple durations are defined, take the first
-
-
- transitionDuration = transitionDuration.split(',')[0];
- transitionDelay = transitionDelay.split(',')[0];
- return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;
- };
-
- const triggerTransitionEnd = element => {
- element.dispatchEvent(new Event(TRANSITION_END));
- };
-
- const isElement = obj => {
- if (!obj || typeof obj !== 'object') {
- return false;
- }
-
- if (typeof obj.jquery !== 'undefined') {
- obj = obj[0];
- }
-
- return typeof obj.nodeType !== 'undefined';
- };
-
- const getElement = obj => {
- if (isElement(obj)) {
- // it's a jQuery object or a node element
- return obj.jquery ? obj[0] : obj;
- }
-
- if (typeof obj === 'string' && obj.length > 0) {
- return document.querySelector(obj);
- }
-
- return null;
- };
-
- const typeCheckConfig = (componentName, config, configTypes) => {
- Object.keys(configTypes).forEach(property => {
- const expectedTypes = configTypes[property];
- const value = config[property];
- const valueType = value && isElement(value) ? 'element' : toType(value);
-
- if (!new RegExp(expectedTypes).test(valueType)) {
- throw new TypeError(`${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`);
- }
- });
- };
-
- const isVisible = element => {
- if (!isElement(element) || element.getClientRects().length === 0) {
- return false;
- }
-
- return getComputedStyle(element).getPropertyValue('visibility') === 'visible';
- };
-
- const isDisabled = element => {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return true;
- }
-
- if (element.classList.contains('disabled')) {
- return true;
- }
-
- if (typeof element.disabled !== 'undefined') {
- return element.disabled;
- }
-
- return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';
- };
- /**
- * Trick to restart an element's animation
- *
- * @param {HTMLElement} element
- * @return void
- *
- * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
- */
-
-
- const reflow = element => {
- // eslint-disable-next-line no-unused-expressions
- element.offsetHeight;
- };
-
- const getjQuery = () => {
- const {
- jQuery
- } = window;
-
- if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
- return jQuery;
- }
-
- return null;
- };
-
- const DOMContentLoadedCallbacks = [];
-
- const onDOMContentLoaded = callback => {
- if (document.readyState === 'loading') {
- // add listener on the first call when the document is in loading state
- if (!DOMContentLoadedCallbacks.length) {
- document.addEventListener('DOMContentLoaded', () => {
- DOMContentLoadedCallbacks.forEach(callback => callback());
- });
- }
-
- DOMContentLoadedCallbacks.push(callback);
- } else {
- callback();
- }
- };
-
- const isRTL = () => document.documentElement.dir === 'rtl';
-
- const defineJQueryPlugin = plugin => {
- onDOMContentLoaded(() => {
- const $ = getjQuery();
- /* istanbul ignore if */
-
- if ($) {
- const name = plugin.NAME;
- const JQUERY_NO_CONFLICT = $.fn[name];
- $.fn[name] = plugin.jQueryInterface;
- $.fn[name].Constructor = plugin;
-
- $.fn[name].noConflict = () => {
- $.fn[name] = JQUERY_NO_CONFLICT;
- return plugin.jQueryInterface;
- };
- }
- });
- };
-
- const execute = callback => {
- if (typeof callback === 'function') {
- callback();
- }
- };
-
- const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {
- if (!waitForTransition) {
- execute(callback);
- return;
- }
-
- const durationPadding = 5;
- const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding;
- let called = false;
-
- const handler = ({
- target
- }) => {
- if (target !== transitionElement) {
- return;
- }
-
- called = true;
- transitionElement.removeEventListener(TRANSITION_END, handler);
- execute(callback);
- };
-
- transitionElement.addEventListener(TRANSITION_END, handler);
- setTimeout(() => {
- if (!called) {
- triggerTransitionEnd(transitionElement);
- }
- }, emulatedDuration);
- };
-
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/scrollBar.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top';
- const SELECTOR_STICKY_CONTENT = '.sticky-top';
-
- class ScrollBarHelper {
- constructor() {
- this._element = document.body;
- }
-
- getWidth() {
- // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes
- const documentWidth = document.documentElement.clientWidth;
- return Math.abs(window.innerWidth - documentWidth);
- }
-
- hide() {
- const width = this.getWidth();
-
- this._disableOverFlow(); // give padding to element to balance the hidden scrollbar width
-
-
- this._setElementAttributes(this._element, 'paddingRight', calculatedValue => calculatedValue + width); // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth
-
-
- this._setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width);
-
- this._setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width);
- }
-
- _disableOverFlow() {
- this._saveInitialAttribute(this._element, 'overflow');
-
- this._element.style.overflow = 'hidden';
- }
-
- _setElementAttributes(selector, styleProp, callback) {
- const scrollbarWidth = this.getWidth();
-
- const manipulationCallBack = element => {
- if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {
- return;
- }
-
- this._saveInitialAttribute(element, styleProp);
-
- const calculatedValue = window.getComputedStyle(element)[styleProp];
- element.style[styleProp] = `${callback(Number.parseFloat(calculatedValue))}px`;
- };
-
- this._applyManipulationCallback(selector, manipulationCallBack);
- }
-
- reset() {
- this._resetElementAttributes(this._element, 'overflow');
-
- this._resetElementAttributes(this._element, 'paddingRight');
-
- this._resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight');
-
- this._resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight');
- }
-
- _saveInitialAttribute(element, styleProp) {
- const actualValue = element.style[styleProp];
-
- if (actualValue) {
- Manipulator__default.default.setDataAttribute(element, styleProp, actualValue);
- }
- }
-
- _resetElementAttributes(selector, styleProp) {
- const manipulationCallBack = element => {
- const value = Manipulator__default.default.getDataAttribute(element, styleProp);
-
- if (typeof value === 'undefined') {
- element.style.removeProperty(styleProp);
- } else {
- Manipulator__default.default.removeDataAttribute(element, styleProp);
- element.style[styleProp] = value;
- }
- };
-
- this._applyManipulationCallback(selector, manipulationCallBack);
- }
-
- _applyManipulationCallback(selector, callBack) {
- if (isElement(selector)) {
- callBack(selector);
- } else {
- SelectorEngine__default.default.find(selector, this._element).forEach(callBack);
- }
- }
-
- isOverflowing() {
- return this.getWidth() > 0;
- }
-
- }
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/selector-engine.js'), require('./util/backdrop.js'), require('./util/component-functions.js'), require('./util/focustrap.js'), require('./util/index.js'), require('./util/scrollbar.js')) :
+ typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './dom/selector-engine', './util/backdrop', './util/component-functions', './util/focustrap', './util/index', './util/scrollbar'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Modal = factory(global.BaseComponent, global.EventHandler, global.SelectorEngine, global.Backdrop, global.ComponentFunctions, global.Focustrap, global.Index, global.Scrollbar));
+})(this, (function (BaseComponent, EventHandler, SelectorEngine, Backdrop, componentFunctions_js, FocusTrap, index_js, ScrollBarHelper) { 'use strict';
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/backdrop.js
+ * Bootstrap modal.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
- const Default$2 = {
- className: 'modal-backdrop',
- isVisible: true,
- // if false, we use the backdrop helper without adding any element to the dom
- isAnimated: false,
- rootElement: 'body',
- // give the choice to place backdrop under different elements
- clickCallback: null
- };
- const DefaultType$2 = {
- className: 'string',
- isVisible: 'boolean',
- isAnimated: 'boolean',
- rootElement: '(element|string)',
- clickCallback: '(function|null)'
- };
- const NAME$2 = 'backdrop';
- const CLASS_NAME_FADE$1 = 'fade';
- const CLASS_NAME_SHOW$1 = 'show';
- const EVENT_MOUSEDOWN = `mousedown.bs.${NAME$2}`;
- class Backdrop {
- constructor(config) {
- this._config = this._getConfig(config);
- this._isAppended = false;
- this._element = null;
- }
-
- show(callback) {
- if (!this._config.isVisible) {
- execute(callback);
- return;
- }
-
- this._append();
-
- if (this._config.isAnimated) {
- reflow(this._getElement());
- }
-
- this._getElement().classList.add(CLASS_NAME_SHOW$1);
-
- this._emulateAnimation(() => {
- execute(callback);
- });
- }
-
- hide(callback) {
- if (!this._config.isVisible) {
- execute(callback);
- return;
- }
-
- this._getElement().classList.remove(CLASS_NAME_SHOW$1);
-
- this._emulateAnimation(() => {
- this.dispose();
- execute(callback);
- });
- } // Private
-
-
- _getElement() {
- if (!this._element) {
- const backdrop = document.createElement('div');
- backdrop.className = this._config.className;
-
- if (this._config.isAnimated) {
- backdrop.classList.add(CLASS_NAME_FADE$1);
- }
-
- this._element = backdrop;
- }
-
- return this._element;
- }
-
- _getConfig(config) {
- config = { ...Default$2,
- ...(typeof config === 'object' ? config : {})
- }; // use getElement() with the default "body" to get a fresh Element on each instantiation
-
- config.rootElement = getElement(config.rootElement);
- typeCheckConfig(NAME$2, config, DefaultType$2);
- return config;
- }
-
- _append() {
- if (this._isAppended) {
- return;
- }
-
- this._config.rootElement.append(this._getElement());
-
- EventHandler__default.default.on(this._getElement(), EVENT_MOUSEDOWN, () => {
- execute(this._config.clickCallback);
- });
- this._isAppended = true;
- }
-
- dispose() {
- if (!this._isAppended) {
- return;
- }
-
- EventHandler__default.default.off(this._element, EVENT_MOUSEDOWN);
-
- this._element.remove();
-
- this._isAppended = false;
- }
-
- _emulateAnimation(callback) {
- executeAfterTransition(callback, this._getElement(), this._config.isAnimated);
- }
-
- }
/**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/focustrap.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- const Default$1 = {
- trapElement: null,
- // The element to trap focus inside of
- autofocus: true
- };
- const DefaultType$1 = {
- trapElement: 'element',
- autofocus: 'boolean'
- };
- const NAME$1 = 'focustrap';
- const DATA_KEY$1 = 'bs.focustrap';
- const EVENT_KEY$1 = `.${DATA_KEY$1}`;
- const EVENT_FOCUSIN = `focusin${EVENT_KEY$1}`;
- const EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY$1}`;
- const TAB_KEY = 'Tab';
- const TAB_NAV_FORWARD = 'forward';
- const TAB_NAV_BACKWARD = 'backward';
-
- class FocusTrap {
- constructor(config) {
- this._config = this._getConfig(config);
- this._isActive = false;
- this._lastTabNavDirection = null;
- }
-
- activate() {
- const {
- trapElement,
- autofocus
- } = this._config;
-
- if (this._isActive) {
- return;
- }
-
- if (autofocus) {
- trapElement.focus();
- }
-
- EventHandler__default.default.off(document, EVENT_KEY$1); // guard against infinite focus loop
-
- EventHandler__default.default.on(document, EVENT_FOCUSIN, event => this._handleFocusin(event));
- EventHandler__default.default.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event));
- this._isActive = true;
- }
-
- deactivate() {
- if (!this._isActive) {
- return;
- }
-
- this._isActive = false;
- EventHandler__default.default.off(document, EVENT_KEY$1);
- } // Private
-
-
- _handleFocusin(event) {
- const {
- target
- } = event;
- const {
- trapElement
- } = this._config;
-
- if (target === document || target === trapElement || trapElement.contains(target)) {
- return;
- }
-
- const elements = SelectorEngine__default.default.focusableChildren(trapElement);
-
- if (elements.length === 0) {
- trapElement.focus();
- } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {
- elements[elements.length - 1].focus();
- } else {
- elements[0].focus();
- }
- }
-
- _handleKeydown(event) {
- if (event.key !== TAB_KEY) {
- return;
- }
-
- this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD;
- }
-
- _getConfig(config) {
- config = { ...Default$1,
- ...(typeof config === 'object' ? config : {})
- };
- typeCheckConfig(NAME$1, config, DefaultType$1);
- return config;
- }
-
- }
-
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/component-functions.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
-
- const enableDismissTrigger = (component, method = 'hide') => {
- const clickEvent = `click.dismiss${component.EVENT_KEY}`;
- const name = component.NAME;
- EventHandler__default.default.on(document, clickEvent, `[data-bs-dismiss="${name}"]`, function (event) {
- if (['A', 'AREA'].includes(this.tagName)) {
- event.preventDefault();
- }
-
- if (isDisabled(this)) {
- return;
- }
-
- const target = getElementFromSelector(this) || this.closest(`.${name}`);
- const instance = component.getOrCreateInstance(target); // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method
-
- instance[method]();
- });
- };
-
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): modal.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- /**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'modal';
@@ -624,16 +26,6 @@
const EVENT_KEY = `.${DATA_KEY}`;
const DATA_API_KEY = '.data-api';
const ESCAPE_KEY = 'Escape';
- const Default = {
- backdrop: true,
- keyboard: true,
- focus: true
- };
- const DefaultType = {
- backdrop: '(boolean|string)',
- keyboard: 'boolean',
- focus: 'boolean'
- };
const EVENT_HIDE = `hide${EVENT_KEY}`;
const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`;
const EVENT_HIDDEN = `hidden${EVENT_KEY}`;
@@ -641,9 +33,8 @@
const EVENT_SHOWN = `shown${EVENT_KEY}`;
const EVENT_RESIZE = `resize${EVENT_KEY}`;
const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`;
- const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`;
- const EVENT_MOUSEUP_DISMISS = `mouseup.dismiss${EVENT_KEY}`;
const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`;
+ const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`;
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;
const CLASS_NAME_OPEN = 'modal-open';
const CLASS_NAME_FADE = 'fade';
@@ -653,388 +44,275 @@
const SELECTOR_DIALOG = '.modal-dialog';
const SELECTOR_MODAL_BODY = '.modal-body';
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="modal"]';
+ const Default = {
+ backdrop: true,
+ focus: true,
+ keyboard: true
+ };
+ const DefaultType = {
+ backdrop: '(boolean|string)',
+ focus: 'boolean',
+ keyboard: 'boolean'
+ };
+
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
- class Modal extends BaseComponent__default.default {
+ class Modal extends BaseComponent {
constructor(element, config) {
- super(element);
- this._config = this._getConfig(config);
- this._dialog = SelectorEngine__default.default.findOne(SELECTOR_DIALOG, this._element);
+ super(element, config);
+ this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element);
this._backdrop = this._initializeBackDrop();
this._focustrap = this._initializeFocusTrap();
this._isShown = false;
- this._ignoreBackdropClick = false;
this._isTransitioning = false;
this._scrollBar = new ScrollBarHelper();
- } // Getters
-
+ this._addEventListeners();
+ }
+ // Getters
static get Default() {
return Default;
}
-
+ static get DefaultType() {
+ return DefaultType;
+ }
static get NAME() {
return NAME;
- } // Public
-
+ }
+ // Public
toggle(relatedTarget) {
return this._isShown ? this.hide() : this.show(relatedTarget);
}
-
show(relatedTarget) {
if (this._isShown || this._isTransitioning) {
return;
}
-
- const showEvent = EventHandler__default.default.trigger(this._element, EVENT_SHOW, {
+ const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {
relatedTarget
});
-
if (showEvent.defaultPrevented) {
return;
}
-
this._isShown = true;
-
- if (this._isAnimated()) {
- this._isTransitioning = true;
- }
-
+ this._isTransitioning = true;
this._scrollBar.hide();
-
document.body.classList.add(CLASS_NAME_OPEN);
-
this._adjustDialog();
-
- this._setEscapeEvent();
-
- this._setResizeEvent();
-
- EventHandler__default.default.on(this._dialog, EVENT_MOUSEDOWN_DISMISS, () => {
- EventHandler__default.default.one(this._element, EVENT_MOUSEUP_DISMISS, event => {
- if (event.target === this._element) {
- this._ignoreBackdropClick = true;
- }
- });
- });
-
- this._showBackdrop(() => this._showElement(relatedTarget));
+ this._backdrop.show(() => this._showElement(relatedTarget));
}
-
hide() {
if (!this._isShown || this._isTransitioning) {
return;
}
-
- const hideEvent = EventHandler__default.default.trigger(this._element, EVENT_HIDE);
-
+ const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE);
if (hideEvent.defaultPrevented) {
return;
}
-
this._isShown = false;
-
- const isAnimated = this._isAnimated();
-
- if (isAnimated) {
- this._isTransitioning = true;
- }
-
- this._setEscapeEvent();
-
- this._setResizeEvent();
-
+ this._isTransitioning = true;
this._focustrap.deactivate();
-
this._element.classList.remove(CLASS_NAME_SHOW);
-
- EventHandler__default.default.off(this._element, EVENT_CLICK_DISMISS);
- EventHandler__default.default.off(this._dialog, EVENT_MOUSEDOWN_DISMISS);
-
- this._queueCallback(() => this._hideModal(), this._element, isAnimated);
+ this._queueCallback(() => this._hideModal(), this._element, this._isAnimated());
}
-
dispose() {
- [window, this._dialog].forEach(htmlElement => EventHandler__default.default.off(htmlElement, EVENT_KEY));
-
+ EventHandler.off(window, EVENT_KEY);
+ EventHandler.off(this._dialog, EVENT_KEY);
this._backdrop.dispose();
-
this._focustrap.deactivate();
-
super.dispose();
}
-
handleUpdate() {
this._adjustDialog();
- } // Private
-
+ }
+ // Private
_initializeBackDrop() {
return new Backdrop({
isVisible: Boolean(this._config.backdrop),
- // 'static' option will be translated to true, and booleans will keep their value
+ // 'static' option will be translated to true, and booleans will keep their value,
isAnimated: this._isAnimated()
});
}
-
_initializeFocusTrap() {
return new FocusTrap({
trapElement: this._element
});
}
-
- _getConfig(config) {
- config = { ...Default,
- ...Manipulator__default.default.getDataAttributes(this._element),
- ...(typeof config === 'object' ? config : {})
- };
- typeCheckConfig(NAME, config, DefaultType);
- return config;
- }
-
_showElement(relatedTarget) {
- const isAnimated = this._isAnimated();
-
- const modalBody = SelectorEngine__default.default.findOne(SELECTOR_MODAL_BODY, this._dialog);
-
- if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {
- // Don't move modal's DOM position
+ // try to append dynamic modal
+ if (!document.body.contains(this._element)) {
document.body.append(this._element);
}
-
this._element.style.display = 'block';
-
this._element.removeAttribute('aria-hidden');
-
this._element.setAttribute('aria-modal', true);
-
this._element.setAttribute('role', 'dialog');
-
this._element.scrollTop = 0;
-
+ const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog);
if (modalBody) {
modalBody.scrollTop = 0;
}
-
- if (isAnimated) {
- reflow(this._element);
- }
-
+ index_js.reflow(this._element);
this._element.classList.add(CLASS_NAME_SHOW);
-
const transitionComplete = () => {
if (this._config.focus) {
this._focustrap.activate();
}
-
this._isTransitioning = false;
- EventHandler__default.default.trigger(this._element, EVENT_SHOWN, {
+ EventHandler.trigger(this._element, EVENT_SHOWN, {
relatedTarget
});
};
-
- this._queueCallback(transitionComplete, this._dialog, isAnimated);
+ this._queueCallback(transitionComplete, this._dialog, this._isAnimated());
}
-
- _setEscapeEvent() {
- if (this._isShown) {
- EventHandler__default.default.on(this._element, EVENT_KEYDOWN_DISMISS, event => {
- if (this._config.keyboard && event.key === ESCAPE_KEY) {
- event.preventDefault();
- this.hide();
- } else if (!this._config.keyboard && event.key === ESCAPE_KEY) {
+ _addEventListeners() {
+ EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {
+ if (event.key !== ESCAPE_KEY) {
+ return;
+ }
+ if (this._config.keyboard) {
+ this.hide();
+ return;
+ }
+ this._triggerBackdropTransition();
+ });
+ EventHandler.on(window, EVENT_RESIZE, () => {
+ if (this._isShown && !this._isTransitioning) {
+ this._adjustDialog();
+ }
+ });
+ EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {
+ // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks
+ EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {
+ if (this._element !== event.target || this._element !== event2.target) {
+ return;
+ }
+ if (this._config.backdrop === 'static') {
this._triggerBackdropTransition();
+ return;
+ }
+ if (this._config.backdrop) {
+ this.hide();
}
});
- } else {
- EventHandler__default.default.off(this._element, EVENT_KEYDOWN_DISMISS);
- }
- }
-
- _setResizeEvent() {
- if (this._isShown) {
- EventHandler__default.default.on(window, EVENT_RESIZE, () => this._adjustDialog());
- } else {
- EventHandler__default.default.off(window, EVENT_RESIZE);
- }
+ });
}
-
_hideModal() {
this._element.style.display = 'none';
-
this._element.setAttribute('aria-hidden', true);
-
this._element.removeAttribute('aria-modal');
-
this._element.removeAttribute('role');
-
this._isTransitioning = false;
-
this._backdrop.hide(() => {
document.body.classList.remove(CLASS_NAME_OPEN);
-
this._resetAdjustments();
-
this._scrollBar.reset();
-
- EventHandler__default.default.trigger(this._element, EVENT_HIDDEN);
- });
- }
-
- _showBackdrop(callback) {
- EventHandler__default.default.on(this._element, EVENT_CLICK_DISMISS, event => {
- if (this._ignoreBackdropClick) {
- this._ignoreBackdropClick = false;
- return;
- }
-
- if (event.target !== event.currentTarget) {
- return;
- }
-
- if (this._config.backdrop === true) {
- this.hide();
- } else if (this._config.backdrop === 'static') {
- this._triggerBackdropTransition();
- }
+ EventHandler.trigger(this._element, EVENT_HIDDEN);
});
-
- this._backdrop.show(callback);
}
-
_isAnimated() {
return this._element.classList.contains(CLASS_NAME_FADE);
}
-
_triggerBackdropTransition() {
- const hideEvent = EventHandler__default.default.trigger(this._element, EVENT_HIDE_PREVENTED);
-
+ const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);
if (hideEvent.defaultPrevented) {
return;
}
-
- const {
- classList,
- scrollHeight,
- style
- } = this._element;
- const isModalOverflowing = scrollHeight > document.documentElement.clientHeight; // return if the following background transition hasn't yet completed
-
- if (!isModalOverflowing && style.overflowY === 'hidden' || classList.contains(CLASS_NAME_STATIC)) {
+ const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;
+ const initialOverflowY = this._element.style.overflowY;
+ // return if the following background transition hasn't yet completed
+ if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {
return;
}
-
if (!isModalOverflowing) {
- style.overflowY = 'hidden';
+ this._element.style.overflowY = 'hidden';
}
-
- classList.add(CLASS_NAME_STATIC);
-
+ this._element.classList.add(CLASS_NAME_STATIC);
this._queueCallback(() => {
- classList.remove(CLASS_NAME_STATIC);
-
- if (!isModalOverflowing) {
- this._queueCallback(() => {
- style.overflowY = '';
- }, this._dialog);
- }
+ this._element.classList.remove(CLASS_NAME_STATIC);
+ this._queueCallback(() => {
+ this._element.style.overflowY = initialOverflowY;
+ }, this._dialog);
}, this._dialog);
-
this._element.focus();
- } // ----------------------------------------------------------------------
- // the following methods are used to handle overflowing modals
- // ----------------------------------------------------------------------
+ }
+ /**
+ * The following methods are used to handle overflowing modals
+ */
_adjustDialog() {
const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight;
-
const scrollbarWidth = this._scrollBar.getWidth();
-
const isBodyOverflowing = scrollbarWidth > 0;
-
- if (!isBodyOverflowing && isModalOverflowing && !isRTL() || isBodyOverflowing && !isModalOverflowing && isRTL()) {
- this._element.style.paddingLeft = `${scrollbarWidth}px`;
+ if (isBodyOverflowing && !isModalOverflowing) {
+ const property = index_js.isRTL() ? 'paddingLeft' : 'paddingRight';
+ this._element.style[property] = `${scrollbarWidth}px`;
}
-
- if (isBodyOverflowing && !isModalOverflowing && !isRTL() || !isBodyOverflowing && isModalOverflowing && isRTL()) {
- this._element.style.paddingRight = `${scrollbarWidth}px`;
+ if (!isBodyOverflowing && isModalOverflowing) {
+ const property = index_js.isRTL() ? 'paddingRight' : 'paddingLeft';
+ this._element.style[property] = `${scrollbarWidth}px`;
}
}
-
_resetAdjustments() {
this._element.style.paddingLeft = '';
this._element.style.paddingRight = '';
- } // Static
-
+ }
+ // Static
static jQueryInterface(config, relatedTarget) {
return this.each(function () {
const data = Modal.getOrCreateInstance(this, config);
-
if (typeof config !== 'string') {
return;
}
-
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`);
}
-
data[config](relatedTarget);
});
}
-
}
+
/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
+ * Data API implementation
*/
-
- EventHandler__default.default.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
- const target = getElementFromSelector(this);
-
+ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
+ const target = SelectorEngine.getElementFromSelector(this);
if (['A', 'AREA'].includes(this.tagName)) {
event.preventDefault();
}
-
- EventHandler__default.default.one(target, EVENT_SHOW, showEvent => {
+ EventHandler.one(target, EVENT_SHOW, showEvent => {
if (showEvent.defaultPrevented) {
// only register focus restorer if modal will actually get shown
return;
}
-
- EventHandler__default.default.one(target, EVENT_HIDDEN, () => {
- if (isVisible(this)) {
+ EventHandler.one(target, EVENT_HIDDEN, () => {
+ if (index_js.isVisible(this)) {
this.focus();
}
});
- }); // avoid conflict when clicking moddal toggler while another one is open
-
- const allReadyOpen = SelectorEngine__default.default.findOne(OPEN_SELECTOR);
+ });
- if (allReadyOpen) {
- Modal.getInstance(allReadyOpen).hide();
+ // avoid conflict when clicking modal toggler while another one is open
+ const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR);
+ if (alreadyOpen) {
+ Modal.getInstance(alreadyOpen).hide();
}
-
const data = Modal.getOrCreateInstance(target);
data.toggle(this);
});
- enableDismissTrigger(Modal);
+ componentFunctions_js.enableDismissTrigger(Modal);
+
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .Modal to jQuery only if jQuery is present
*/
- defineJQueryPlugin(Modal);
+ index_js.defineJQueryPlugin(Modal);
return Modal;
diff --git a/js/dist/modal.js.map b/js/dist/modal.js.map
index b82c441e9..37dea6cbd 100644
--- a/js/dist/modal.js.map
+++ b/js/dist/modal.js.map
@@ -1 +1 @@
-{"version":3,"file":"modal.js","sources":["../src/util/index.js","../src/util/scrollbar.js","../src/util/backdrop.js","../src/util/focustrap.js","../src/util/component-functions.js","../src/modal.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n// Shoutout AngusCroll (https://goo.gl/pxwQGp)\nconst toType = obj => {\n if (obj === null || obj === undefined) {\n return `${obj}`\n }\n\n return {}.toString.call(obj).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttr = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {\n hrefAttr = `#${hrefAttr.split('#')[1]}`\n }\n\n selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null\n }\n\n return selector\n}\n\nconst getSelectorFromElement = element => {\n const selector = getSelector(element)\n\n if (selector) {\n return document.querySelector(selector) ? selector : null\n }\n\n return null\n}\n\nconst getElementFromSelector = element => {\n const selector = getSelector(element)\n\n return selector ? document.querySelector(selector) : null\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = obj => {\n if (!obj || typeof obj !== 'object') {\n return false\n }\n\n if (typeof obj.jquery !== 'undefined') {\n obj = obj[0]\n }\n\n return typeof obj.nodeType !== 'undefined'\n}\n\nconst getElement = obj => {\n if (isElement(obj)) { // it's a jQuery object or a node element\n return obj.jquery ? obj[0] : obj\n }\n\n if (typeof obj === 'string' && obj.length > 0) {\n return document.querySelector(obj)\n }\n\n return null\n}\n\nconst typeCheckConfig = (componentName, config, configTypes) => {\n Object.keys(configTypes).forEach(property => {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${componentName.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n })\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n return getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n // eslint-disable-next-line no-unused-expressions\n element.offsetHeight\n}\n\nconst getjQuery = () => {\n const { jQuery } = window\n\n if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n DOMContentLoadedCallbacks.forEach(callback => callback())\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = callback => {\n if (typeof callback === 'function') {\n callback()\n }\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element depending on the direction and if cycle is allowed\n if (index === -1) {\n return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]\n }\n\n const listLength = list.length\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n getElement,\n getUID,\n getSelectorFromElement,\n getElementFromSelector,\n getTransitionDurationFromElement,\n triggerTransitionEnd,\n isElement,\n typeCheckConfig,\n isVisible,\n isDisabled,\n findShadowRoot,\n noop,\n getNextActiveElement,\n reflow,\n getjQuery,\n onDOMContentLoaded,\n isRTL,\n defineJQueryPlugin,\n execute,\n executeAfterTransition\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/scrollBar.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport SelectorEngine from '../dom/selector-engine'\nimport Manipulator from '../dom/manipulator'\nimport { isElement } from './index'\n\nconst SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'\nconst SELECTOR_STICKY_CONTENT = '.sticky-top'\n\nclass ScrollBarHelper {\n constructor() {\n this._element = document.body\n }\n\n getWidth() {\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes\n const documentWidth = document.documentElement.clientWidth\n return Math.abs(window.innerWidth - documentWidth)\n }\n\n hide() {\n const width = this.getWidth()\n this._disableOverFlow()\n // give padding to element to balance the hidden scrollbar width\n this._setElementAttributes(this._element, 'paddingRight', calculatedValue => calculatedValue + width)\n // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth\n this._setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width)\n this._setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width)\n }\n\n _disableOverFlow() {\n this._saveInitialAttribute(this._element, 'overflow')\n this._element.style.overflow = 'hidden'\n }\n\n _setElementAttributes(selector, styleProp, callback) {\n const scrollbarWidth = this.getWidth()\n const manipulationCallBack = element => {\n if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {\n return\n }\n\n this._saveInitialAttribute(element, styleProp)\n const calculatedValue = window.getComputedStyle(element)[styleProp]\n element.style[styleProp] = `${callback(Number.parseFloat(calculatedValue))}px`\n }\n\n this._applyManipulationCallback(selector, manipulationCallBack)\n }\n\n reset() {\n this._resetElementAttributes(this._element, 'overflow')\n this._resetElementAttributes(this._element, 'paddingRight')\n this._resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight')\n this._resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight')\n }\n\n _saveInitialAttribute(element, styleProp) {\n const actualValue = element.style[styleProp]\n if (actualValue) {\n Manipulator.setDataAttribute(element, styleProp, actualValue)\n }\n }\n\n _resetElementAttributes(selector, styleProp) {\n const manipulationCallBack = element => {\n const value = Manipulator.getDataAttribute(element, styleProp)\n if (typeof value === 'undefined') {\n element.style.removeProperty(styleProp)\n } else {\n Manipulator.removeDataAttribute(element, styleProp)\n element.style[styleProp] = value\n }\n }\n\n this._applyManipulationCallback(selector, manipulationCallBack)\n }\n\n _applyManipulationCallback(selector, callBack) {\n if (isElement(selector)) {\n callBack(selector)\n } else {\n SelectorEngine.find(selector, this._element).forEach(callBack)\n }\n }\n\n isOverflowing() {\n return this.getWidth() > 0\n }\n}\n\nexport default ScrollBarHelper\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/backdrop.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler'\nimport { execute, executeAfterTransition, getElement, reflow, typeCheckConfig } from './index'\n\nconst Default = {\n className: 'modal-backdrop',\n isVisible: true, // if false, we use the backdrop helper without adding any element to the dom\n isAnimated: false,\n rootElement: 'body', // give the choice to place backdrop under different elements\n clickCallback: null\n}\n\nconst DefaultType = {\n className: 'string',\n isVisible: 'boolean',\n isAnimated: 'boolean',\n rootElement: '(element|string)',\n clickCallback: '(function|null)'\n}\nconst NAME = 'backdrop'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\n\nconst EVENT_MOUSEDOWN = `mousedown.bs.${NAME}`\n\nclass Backdrop {\n constructor(config) {\n this._config = this._getConfig(config)\n this._isAppended = false\n this._element = null\n }\n\n show(callback) {\n if (!this._config.isVisible) {\n execute(callback)\n return\n }\n\n this._append()\n\n if (this._config.isAnimated) {\n reflow(this._getElement())\n }\n\n this._getElement().classList.add(CLASS_NAME_SHOW)\n\n this._emulateAnimation(() => {\n execute(callback)\n })\n }\n\n hide(callback) {\n if (!this._config.isVisible) {\n execute(callback)\n return\n }\n\n this._getElement().classList.remove(CLASS_NAME_SHOW)\n\n this._emulateAnimation(() => {\n this.dispose()\n execute(callback)\n })\n }\n\n // Private\n\n _getElement() {\n if (!this._element) {\n const backdrop = document.createElement('div')\n backdrop.className = this._config.className\n if (this._config.isAnimated) {\n backdrop.classList.add(CLASS_NAME_FADE)\n }\n\n this._element = backdrop\n }\n\n return this._element\n }\n\n _getConfig(config) {\n config = {\n ...Default,\n ...(typeof config === 'object' ? config : {})\n }\n\n // use getElement() with the default \"body\" to get a fresh Element on each instantiation\n config.rootElement = getElement(config.rootElement)\n typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _append() {\n if (this._isAppended) {\n return\n }\n\n this._config.rootElement.append(this._getElement())\n\n EventHandler.on(this._getElement(), EVENT_MOUSEDOWN, () => {\n execute(this._config.clickCallback)\n })\n\n this._isAppended = true\n }\n\n dispose() {\n if (!this._isAppended) {\n return\n }\n\n EventHandler.off(this._element, EVENT_MOUSEDOWN)\n\n this._element.remove()\n this._isAppended = false\n }\n\n _emulateAnimation(callback) {\n executeAfterTransition(callback, this._getElement(), this._config.isAnimated)\n }\n}\n\nexport default Backdrop\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/focustrap.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler'\nimport SelectorEngine from '../dom/selector-engine'\nimport { typeCheckConfig } from './index'\n\nconst Default = {\n trapElement: null, // The element to trap focus inside of\n autofocus: true\n}\n\nconst DefaultType = {\n trapElement: 'element',\n autofocus: 'boolean'\n}\n\nconst NAME = 'focustrap'\nconst DATA_KEY = 'bs.focustrap'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst EVENT_FOCUSIN = `focusin${EVENT_KEY}`\nconst EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY}`\n\nconst TAB_KEY = 'Tab'\nconst TAB_NAV_FORWARD = 'forward'\nconst TAB_NAV_BACKWARD = 'backward'\n\nclass FocusTrap {\n constructor(config) {\n this._config = this._getConfig(config)\n this._isActive = false\n this._lastTabNavDirection = null\n }\n\n activate() {\n const { trapElement, autofocus } = this._config\n\n if (this._isActive) {\n return\n }\n\n if (autofocus) {\n trapElement.focus()\n }\n\n EventHandler.off(document, EVENT_KEY) // guard against infinite focus loop\n EventHandler.on(document, EVENT_FOCUSIN, event => this._handleFocusin(event))\n EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event))\n\n this._isActive = true\n }\n\n deactivate() {\n if (!this._isActive) {\n return\n }\n\n this._isActive = false\n EventHandler.off(document, EVENT_KEY)\n }\n\n // Private\n\n _handleFocusin(event) {\n const { target } = event\n const { trapElement } = this._config\n\n if (target === document || target === trapElement || trapElement.contains(target)) {\n return\n }\n\n const elements = SelectorEngine.focusableChildren(trapElement)\n\n if (elements.length === 0) {\n trapElement.focus()\n } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {\n elements[elements.length - 1].focus()\n } else {\n elements[0].focus()\n }\n }\n\n _handleKeydown(event) {\n if (event.key !== TAB_KEY) {\n return\n }\n\n this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD\n }\n\n _getConfig(config) {\n config = {\n ...Default,\n ...(typeof config === 'object' ? config : {})\n }\n typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n}\n\nexport default FocusTrap\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/component-functions.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler'\nimport { getElementFromSelector, isDisabled } from './index'\n\nconst enableDismissTrigger = (component, method = 'hide') => {\n const clickEvent = `click.dismiss${component.EVENT_KEY}`\n const name = component.NAME\n\n EventHandler.on(document, clickEvent, `[data-bs-dismiss=\"${name}\"]`, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n const target = getElementFromSelector(this) || this.closest(`.${name}`)\n const instance = component.getOrCreateInstance(target)\n\n // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method\n instance[method]()\n })\n}\n\nexport {\n enableDismissTrigger\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport {\n defineJQueryPlugin,\n getElementFromSelector,\n isRTL,\n isVisible,\n reflow,\n typeCheckConfig\n} from './util/index'\nimport EventHandler from './dom/event-handler'\nimport Manipulator from './dom/manipulator'\nimport SelectorEngine from './dom/selector-engine'\nimport ScrollBarHelper from './util/scrollbar'\nimport BaseComponent from './base-component'\nimport Backdrop from './util/backdrop'\nimport FocusTrap from './util/focustrap'\nimport { enableDismissTrigger } from './util/component-functions'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'modal'\nconst DATA_KEY = 'bs.modal'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst ESCAPE_KEY = 'Escape'\n\nconst Default = {\n backdrop: true,\n keyboard: true,\n focus: true\n}\n\nconst DefaultType = {\n backdrop: '(boolean|string)',\n keyboard: 'boolean',\n focus: 'boolean'\n}\n\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_RESIZE = `resize${EVENT_KEY}`\nconst EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`\nconst EVENT_MOUSEUP_DISMISS = `mouseup.dismiss${EVENT_KEY}`\nconst EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_OPEN = 'modal-open'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_STATIC = 'modal-static'\n\nconst OPEN_SELECTOR = '.modal.show'\nconst SELECTOR_DIALOG = '.modal-dialog'\nconst SELECTOR_MODAL_BODY = '.modal-body'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"modal\"]'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Modal extends BaseComponent {\n constructor(element, config) {\n super(element)\n\n this._config = this._getConfig(config)\n this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element)\n this._backdrop = this._initializeBackDrop()\n this._focustrap = this._initializeFocusTrap()\n this._isShown = false\n this._ignoreBackdropClick = false\n this._isTransitioning = false\n this._scrollBar = new ScrollBarHelper()\n }\n\n // Getters\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget)\n }\n\n show(relatedTarget) {\n if (this._isShown || this._isTransitioning) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {\n relatedTarget\n })\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._isShown = true\n\n if (this._isAnimated()) {\n this._isTransitioning = true\n }\n\n this._scrollBar.hide()\n\n document.body.classList.add(CLASS_NAME_OPEN)\n\n this._adjustDialog()\n\n this._setEscapeEvent()\n this._setResizeEvent()\n\n EventHandler.on(this._dialog, EVENT_MOUSEDOWN_DISMISS, () => {\n EventHandler.one(this._element, EVENT_MOUSEUP_DISMISS, event => {\n if (event.target === this._element) {\n this._ignoreBackdropClick = true\n }\n })\n })\n\n this._showBackdrop(() => this._showElement(relatedTarget))\n }\n\n hide() {\n if (!this._isShown || this._isTransitioning) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n\n if (hideEvent.defaultPrevented) {\n return\n }\n\n this._isShown = false\n const isAnimated = this._isAnimated()\n\n if (isAnimated) {\n this._isTransitioning = true\n }\n\n this._setEscapeEvent()\n this._setResizeEvent()\n\n this._focustrap.deactivate()\n\n this._element.classList.remove(CLASS_NAME_SHOW)\n\n EventHandler.off(this._element, EVENT_CLICK_DISMISS)\n EventHandler.off(this._dialog, EVENT_MOUSEDOWN_DISMISS)\n\n this._queueCallback(() => this._hideModal(), this._element, isAnimated)\n }\n\n dispose() {\n [window, this._dialog]\n .forEach(htmlElement => EventHandler.off(htmlElement, EVENT_KEY))\n\n this._backdrop.dispose()\n this._focustrap.deactivate()\n super.dispose()\n }\n\n handleUpdate() {\n this._adjustDialog()\n }\n\n // Private\n\n _initializeBackDrop() {\n return new Backdrop({\n isVisible: Boolean(this._config.backdrop), // 'static' option will be translated to true, and booleans will keep their value\n isAnimated: this._isAnimated()\n })\n }\n\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n })\n }\n\n _getConfig(config) {\n config = {\n ...Default,\n ...Manipulator.getDataAttributes(this._element),\n ...(typeof config === 'object' ? config : {})\n }\n typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _showElement(relatedTarget) {\n const isAnimated = this._isAnimated()\n const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog)\n\n if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {\n // Don't move modal's DOM position\n document.body.append(this._element)\n }\n\n this._element.style.display = 'block'\n this._element.removeAttribute('aria-hidden')\n this._element.setAttribute('aria-modal', true)\n this._element.setAttribute('role', 'dialog')\n this._element.scrollTop = 0\n\n if (modalBody) {\n modalBody.scrollTop = 0\n }\n\n if (isAnimated) {\n reflow(this._element)\n }\n\n this._element.classList.add(CLASS_NAME_SHOW)\n\n const transitionComplete = () => {\n if (this._config.focus) {\n this._focustrap.activate()\n }\n\n this._isTransitioning = false\n EventHandler.trigger(this._element, EVENT_SHOWN, {\n relatedTarget\n })\n }\n\n this._queueCallback(transitionComplete, this._dialog, isAnimated)\n }\n\n _setEscapeEvent() {\n if (this._isShown) {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (this._config.keyboard && event.key === ESCAPE_KEY) {\n event.preventDefault()\n this.hide()\n } else if (!this._config.keyboard && event.key === ESCAPE_KEY) {\n this._triggerBackdropTransition()\n }\n })\n } else {\n EventHandler.off(this._element, EVENT_KEYDOWN_DISMISS)\n }\n }\n\n _setResizeEvent() {\n if (this._isShown) {\n EventHandler.on(window, EVENT_RESIZE, () => this._adjustDialog())\n } else {\n EventHandler.off(window, EVENT_RESIZE)\n }\n }\n\n _hideModal() {\n this._element.style.display = 'none'\n this._element.setAttribute('aria-hidden', true)\n this._element.removeAttribute('aria-modal')\n this._element.removeAttribute('role')\n this._isTransitioning = false\n this._backdrop.hide(() => {\n document.body.classList.remove(CLASS_NAME_OPEN)\n this._resetAdjustments()\n this._scrollBar.reset()\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n })\n }\n\n _showBackdrop(callback) {\n EventHandler.on(this._element, EVENT_CLICK_DISMISS, event => {\n if (this._ignoreBackdropClick) {\n this._ignoreBackdropClick = false\n return\n }\n\n if (event.target !== event.currentTarget) {\n return\n }\n\n if (this._config.backdrop === true) {\n this.hide()\n } else if (this._config.backdrop === 'static') {\n this._triggerBackdropTransition()\n }\n })\n\n this._backdrop.show(callback)\n }\n\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_FADE)\n }\n\n _triggerBackdropTransition() {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)\n if (hideEvent.defaultPrevented) {\n return\n }\n\n const { classList, scrollHeight, style } = this._element\n const isModalOverflowing = scrollHeight > document.documentElement.clientHeight\n\n // return if the following background transition hasn't yet completed\n if ((!isModalOverflowing && style.overflowY === 'hidden') || classList.contains(CLASS_NAME_STATIC)) {\n return\n }\n\n if (!isModalOverflowing) {\n style.overflowY = 'hidden'\n }\n\n classList.add(CLASS_NAME_STATIC)\n this._queueCallback(() => {\n classList.remove(CLASS_NAME_STATIC)\n if (!isModalOverflowing) {\n this._queueCallback(() => {\n style.overflowY = ''\n }, this._dialog)\n }\n }, this._dialog)\n\n this._element.focus()\n }\n\n // ----------------------------------------------------------------------\n // the following methods are used to handle overflowing modals\n // ----------------------------------------------------------------------\n\n _adjustDialog() {\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight\n const scrollbarWidth = this._scrollBar.getWidth()\n const isBodyOverflowing = scrollbarWidth > 0\n\n if ((!isBodyOverflowing && isModalOverflowing && !isRTL()) || (isBodyOverflowing && !isModalOverflowing && isRTL())) {\n this._element.style.paddingLeft = `${scrollbarWidth}px`\n }\n\n if ((isBodyOverflowing && !isModalOverflowing && !isRTL()) || (!isBodyOverflowing && isModalOverflowing && isRTL())) {\n this._element.style.paddingRight = `${scrollbarWidth}px`\n }\n }\n\n _resetAdjustments() {\n this._element.style.paddingLeft = ''\n this._element.style.paddingRight = ''\n }\n\n // Static\n\n static jQueryInterface(config, relatedTarget) {\n return this.each(function () {\n const data = Modal.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](relatedTarget)\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n const target = getElementFromSelector(this)\n\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n EventHandler.one(target, EVENT_SHOW, showEvent => {\n if (showEvent.defaultPrevented) {\n // only register focus restorer if modal will actually get shown\n return\n }\n\n EventHandler.one(target, EVENT_HIDDEN, () => {\n if (isVisible(this)) {\n this.focus()\n }\n })\n })\n\n // avoid conflict when clicking moddal toggler while another one is open\n const allReadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)\n if (allReadyOpen) {\n Modal.getInstance(allReadyOpen).hide()\n }\n\n const data = Modal.getOrCreateInstance(target)\n\n data.toggle(this)\n})\n\nenableDismissTrigger(Modal)\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Modal to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Modal)\n\nexport default Modal\n"],"names":["MILLISECONDS_MULTIPLIER","TRANSITION_END","toType","obj","undefined","toString","call","match","toLowerCase","getSelector","element","selector","getAttribute","hrefAttr","includes","startsWith","split","trim","getElementFromSelector","document","querySelector","getTransitionDurationFromElement","transitionDuration","transitionDelay","window","getComputedStyle","floatTransitionDuration","Number","parseFloat","floatTransitionDelay","triggerTransitionEnd","dispatchEvent","Event","isElement","jquery","nodeType","getElement","length","typeCheckConfig","componentName","config","configTypes","Object","keys","forEach","property","expectedTypes","value","valueType","RegExp","test","TypeError","toUpperCase","isVisible","getClientRects","getPropertyValue","isDisabled","Node","ELEMENT_NODE","classList","contains","disabled","hasAttribute","reflow","offsetHeight","getjQuery","jQuery","body","DOMContentLoadedCallbacks","onDOMContentLoaded","callback","readyState","addEventListener","push","isRTL","documentElement","dir","defineJQueryPlugin","plugin","$","name","NAME","JQUERY_NO_CONFLICT","fn","jQueryInterface","Constructor","noConflict","execute","executeAfterTransition","transitionElement","waitForTransition","durationPadding","emulatedDuration","called","handler","target","removeEventListener","setTimeout","SELECTOR_FIXED_CONTENT","SELECTOR_STICKY_CONTENT","ScrollBarHelper","constructor","_element","getWidth","documentWidth","clientWidth","Math","abs","innerWidth","hide","width","_disableOverFlow","_setElementAttributes","calculatedValue","_saveInitialAttribute","style","overflow","styleProp","scrollbarWidth","manipulationCallBack","_applyManipulationCallback","reset","_resetElementAttributes","actualValue","Manipulator","setDataAttribute","getDataAttribute","removeProperty","removeDataAttribute","callBack","SelectorEngine","find","isOverflowing","Default","className","isAnimated","rootElement","clickCallback","DefaultType","CLASS_NAME_FADE","CLASS_NAME_SHOW","EVENT_MOUSEDOWN","Backdrop","_config","_getConfig","_isAppended","show","_append","_getElement","add","_emulateAnimation","remove","dispose","backdrop","createElement","append","EventHandler","on","off","trapElement","autofocus","DATA_KEY","EVENT_KEY","EVENT_FOCUSIN","EVENT_KEYDOWN_TAB","TAB_KEY","TAB_NAV_FORWARD","TAB_NAV_BACKWARD","FocusTrap","_isActive","_lastTabNavDirection","activate","focus","event","_handleFocusin","_handleKeydown","deactivate","elements","focusableChildren","key","shiftKey","enableDismissTrigger","component","method","clickEvent","tagName","preventDefault","closest","instance","getOrCreateInstance","DATA_API_KEY","ESCAPE_KEY","keyboard","EVENT_HIDE","EVENT_HIDE_PREVENTED","EVENT_HIDDEN","EVENT_SHOW","EVENT_SHOWN","EVENT_RESIZE","EVENT_CLICK_DISMISS","EVENT_KEYDOWN_DISMISS","EVENT_MOUSEUP_DISMISS","EVENT_MOUSEDOWN_DISMISS","EVENT_CLICK_DATA_API","CLASS_NAME_OPEN","CLASS_NAME_STATIC","OPEN_SELECTOR","SELECTOR_DIALOG","SELECTOR_MODAL_BODY","SELECTOR_DATA_TOGGLE","Modal","BaseComponent","_dialog","findOne","_backdrop","_initializeBackDrop","_focustrap","_initializeFocusTrap","_isShown","_ignoreBackdropClick","_isTransitioning","_scrollBar","toggle","relatedTarget","showEvent","trigger","defaultPrevented","_isAnimated","_adjustDialog","_setEscapeEvent","_setResizeEvent","one","_showBackdrop","_showElement","hideEvent","_queueCallback","_hideModal","htmlElement","handleUpdate","Boolean","getDataAttributes","modalBody","parentNode","display","removeAttribute","setAttribute","scrollTop","transitionComplete","_triggerBackdropTransition","_resetAdjustments","currentTarget","scrollHeight","isModalOverflowing","clientHeight","overflowY","isBodyOverflowing","paddingLeft","paddingRight","each","data","allReadyOpen","getInstance"],"mappings":"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"} \ No newline at end of file
+{"version":3,"file":"modal.js","sources":["../src/modal.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap modal.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport Backdrop from './util/backdrop.js'\nimport { enableDismissTrigger } from './util/component-functions.js'\nimport FocusTrap from './util/focustrap.js'\nimport {\n defineJQueryPlugin, isRTL, isVisible, reflow\n} from './util/index.js'\nimport ScrollBarHelper from './util/scrollbar.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'modal'\nconst DATA_KEY = 'bs.modal'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst ESCAPE_KEY = 'Escape'\n\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_RESIZE = `resize${EVENT_KEY}`\nconst EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`\nconst EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_OPEN = 'modal-open'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_STATIC = 'modal-static'\n\nconst OPEN_SELECTOR = '.modal.show'\nconst SELECTOR_DIALOG = '.modal-dialog'\nconst SELECTOR_MODAL_BODY = '.modal-body'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"modal\"]'\n\nconst Default = {\n backdrop: true,\n focus: true,\n keyboard: true\n}\n\nconst DefaultType = {\n backdrop: '(boolean|string)',\n focus: 'boolean',\n keyboard: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Modal extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element)\n this._backdrop = this._initializeBackDrop()\n this._focustrap = this._initializeFocusTrap()\n this._isShown = false\n this._isTransitioning = false\n this._scrollBar = new ScrollBarHelper()\n\n this._addEventListeners()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget)\n }\n\n show(relatedTarget) {\n if (this._isShown || this._isTransitioning) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {\n relatedTarget\n })\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._isShown = true\n this._isTransitioning = true\n\n this._scrollBar.hide()\n\n document.body.classList.add(CLASS_NAME_OPEN)\n\n this._adjustDialog()\n\n this._backdrop.show(() => this._showElement(relatedTarget))\n }\n\n hide() {\n if (!this._isShown || this._isTransitioning) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n\n if (hideEvent.defaultPrevented) {\n return\n }\n\n this._isShown = false\n this._isTransitioning = true\n this._focustrap.deactivate()\n\n this._element.classList.remove(CLASS_NAME_SHOW)\n\n this._queueCallback(() => this._hideModal(), this._element, this._isAnimated())\n }\n\n dispose() {\n EventHandler.off(window, EVENT_KEY)\n EventHandler.off(this._dialog, EVENT_KEY)\n\n this._backdrop.dispose()\n this._focustrap.deactivate()\n\n super.dispose()\n }\n\n handleUpdate() {\n this._adjustDialog()\n }\n\n // Private\n _initializeBackDrop() {\n return new Backdrop({\n isVisible: Boolean(this._config.backdrop), // 'static' option will be translated to true, and booleans will keep their value,\n isAnimated: this._isAnimated()\n })\n }\n\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n })\n }\n\n _showElement(relatedTarget) {\n // try to append dynamic modal\n if (!document.body.contains(this._element)) {\n document.body.append(this._element)\n }\n\n this._element.style.display = 'block'\n this._element.removeAttribute('aria-hidden')\n this._element.setAttribute('aria-modal', true)\n this._element.setAttribute('role', 'dialog')\n this._element.scrollTop = 0\n\n const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog)\n if (modalBody) {\n modalBody.scrollTop = 0\n }\n\n reflow(this._element)\n\n this._element.classList.add(CLASS_NAME_SHOW)\n\n const transitionComplete = () => {\n if (this._config.focus) {\n this._focustrap.activate()\n }\n\n this._isTransitioning = false\n EventHandler.trigger(this._element, EVENT_SHOWN, {\n relatedTarget\n })\n }\n\n this._queueCallback(transitionComplete, this._dialog, this._isAnimated())\n }\n\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (event.key !== ESCAPE_KEY) {\n return\n }\n\n if (this._config.keyboard) {\n this.hide()\n return\n }\n\n this._triggerBackdropTransition()\n })\n\n EventHandler.on(window, EVENT_RESIZE, () => {\n if (this._isShown && !this._isTransitioning) {\n this._adjustDialog()\n }\n })\n\n EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {\n // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks\n EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {\n if (this._element !== event.target || this._element !== event2.target) {\n return\n }\n\n if (this._config.backdrop === 'static') {\n this._triggerBackdropTransition()\n return\n }\n\n if (this._config.backdrop) {\n this.hide()\n }\n })\n })\n }\n\n _hideModal() {\n this._element.style.display = 'none'\n this._element.setAttribute('aria-hidden', true)\n this._element.removeAttribute('aria-modal')\n this._element.removeAttribute('role')\n this._isTransitioning = false\n\n this._backdrop.hide(() => {\n document.body.classList.remove(CLASS_NAME_OPEN)\n this._resetAdjustments()\n this._scrollBar.reset()\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n })\n }\n\n _isAnimated() {\n return this._element.classList.contains(CLASS_NAME_FADE)\n }\n\n _triggerBackdropTransition() {\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)\n if (hideEvent.defaultPrevented) {\n return\n }\n\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight\n const initialOverflowY = this._element.style.overflowY\n // return if the following background transition hasn't yet completed\n if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {\n return\n }\n\n if (!isModalOverflowing) {\n this._element.style.overflowY = 'hidden'\n }\n\n this._element.classList.add(CLASS_NAME_STATIC)\n this._queueCallback(() => {\n this._element.classList.remove(CLASS_NAME_STATIC)\n this._queueCallback(() => {\n this._element.style.overflowY = initialOverflowY\n }, this._dialog)\n }, this._dialog)\n\n this._element.focus()\n }\n\n /**\n * The following methods are used to handle overflowing modals\n */\n\n _adjustDialog() {\n const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight\n const scrollbarWidth = this._scrollBar.getWidth()\n const isBodyOverflowing = scrollbarWidth > 0\n\n if (isBodyOverflowing && !isModalOverflowing) {\n const property = isRTL() ? 'paddingLeft' : 'paddingRight'\n this._element.style[property] = `${scrollbarWidth}px`\n }\n\n if (!isBodyOverflowing && isModalOverflowing) {\n const property = isRTL() ? 'paddingRight' : 'paddingLeft'\n this._element.style[property] = `${scrollbarWidth}px`\n }\n }\n\n _resetAdjustments() {\n this._element.style.paddingLeft = ''\n this._element.style.paddingRight = ''\n }\n\n // Static\n static jQueryInterface(config, relatedTarget) {\n return this.each(function () {\n const data = Modal.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](relatedTarget)\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this)\n\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n EventHandler.one(target, EVENT_SHOW, showEvent => {\n if (showEvent.defaultPrevented) {\n // only register focus restorer if modal will actually get shown\n return\n }\n\n EventHandler.one(target, EVENT_HIDDEN, () => {\n if (isVisible(this)) {\n this.focus()\n }\n })\n })\n\n // avoid conflict when clicking modal toggler while another one is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)\n if (alreadyOpen) {\n Modal.getInstance(alreadyOpen).hide()\n }\n\n const data = Modal.getOrCreateInstance(target)\n\n data.toggle(this)\n})\n\nenableDismissTrigger(Modal)\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Modal)\n\nexport default Modal\n"],"names":["NAME","DATA_KEY","EVENT_KEY","DATA_API_KEY","ESCAPE_KEY","EVENT_HIDE","EVENT_HIDE_PREVENTED","EVENT_HIDDEN","EVENT_SHOW","EVENT_SHOWN","EVENT_RESIZE","EVENT_CLICK_DISMISS","EVENT_MOUSEDOWN_DISMISS","EVENT_KEYDOWN_DISMISS","EVENT_CLICK_DATA_API","CLASS_NAME_OPEN","CLASS_NAME_FADE","CLASS_NAME_SHOW","CLASS_NAME_STATIC","OPEN_SELECTOR","SELECTOR_DIALOG","SELECTOR_MODAL_BODY","SELECTOR_DATA_TOGGLE","Default","backdrop","focus","keyboard","DefaultType","Modal","BaseComponent","constructor","element","config","_dialog","SelectorEngine","findOne","_element","_backdrop","_initializeBackDrop","_focustrap","_initializeFocusTrap","_isShown","_isTransitioning","_scrollBar","ScrollBarHelper","_addEventListeners","toggle","relatedTarget","hide","show","showEvent","EventHandler","trigger","defaultPrevented","document","body","classList","add","_adjustDialog","_showElement","hideEvent","deactivate","remove","_queueCallback","_hideModal","_isAnimated","dispose","off","window","handleUpdate","Backdrop","isVisible","Boolean","_config","isAnimated","FocusTrap","trapElement","contains","append","style","display","removeAttribute","setAttribute","scrollTop","modalBody","reflow","transitionComplete","activate","on","event","key","_triggerBackdropTransition","one","event2","target","_resetAdjustments","reset","isModalOverflowing","scrollHeight","documentElement","clientHeight","initialOverflowY","overflowY","scrollbarWidth","getWidth","isBodyOverflowing","property","isRTL","paddingLeft","paddingRight","jQueryInterface","each","data","getOrCreateInstance","TypeError","getElementFromSelector","includes","tagName","preventDefault","alreadyOpen","getInstance","enableDismissTrigger","defineJQueryPlugin"],"mappings":";;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;;;EAaA;EACA;EACA;;EAEA,MAAMA,IAAI,GAAG,OAAO,CAAA;EACpB,MAAMC,QAAQ,GAAG,UAAU,CAAA;EAC3B,MAAMC,SAAS,GAAI,CAAGD,CAAAA,EAAAA,QAAS,CAAC,CAAA,CAAA;EAChC,MAAME,YAAY,GAAG,WAAW,CAAA;EAChC,MAAMC,UAAU,GAAG,QAAQ,CAAA;EAE3B,MAAMC,UAAU,GAAI,CAAMH,IAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EACrC,MAAMI,oBAAoB,GAAI,CAAeJ,aAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EACxD,MAAMK,YAAY,GAAI,CAAQL,MAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EACzC,MAAMM,UAAU,GAAI,CAAMN,IAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EACrC,MAAMO,WAAW,GAAI,CAAOP,KAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EACvC,MAAMQ,YAAY,GAAI,CAAQR,MAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EACzC,MAAMS,mBAAmB,GAAI,CAAeT,aAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EACvD,MAAMU,uBAAuB,GAAI,CAAmBV,iBAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EAC/D,MAAMW,qBAAqB,GAAI,CAAiBX,eAAAA,EAAAA,SAAU,CAAC,CAAA,CAAA;EAC3D,MAAMY,oBAAoB,GAAI,CAAA,KAAA,EAAOZ,SAAU,CAAA,EAAEC,YAAa,CAAC,CAAA,CAAA;EAE/D,MAAMY,eAAe,GAAG,YAAY,CAAA;EACpC,MAAMC,eAAe,GAAG,MAAM,CAAA;EAC9B,MAAMC,eAAe,GAAG,MAAM,CAAA;EAC9B,MAAMC,iBAAiB,GAAG,cAAc,CAAA;EAExC,MAAMC,aAAa,GAAG,aAAa,CAAA;EACnC,MAAMC,eAAe,GAAG,eAAe,CAAA;EACvC,MAAMC,mBAAmB,GAAG,aAAa,CAAA;EACzC,MAAMC,oBAAoB,GAAG,0BAA0B,CAAA;EAEvD,MAAMC,OAAO,GAAG;EACdC,EAAAA,QAAQ,EAAE,IAAI;EACdC,EAAAA,KAAK,EAAE,IAAI;EACXC,EAAAA,QAAQ,EAAE,IAAA;EACZ,CAAC,CAAA;EAED,MAAMC,WAAW,GAAG;EAClBH,EAAAA,QAAQ,EAAE,kBAAkB;EAC5BC,EAAAA,KAAK,EAAE,SAAS;EAChBC,EAAAA,QAAQ,EAAE,SAAA;EACZ,CAAC,CAAA;;EAED;EACA;EACA;;EAEA,MAAME,KAAK,SAASC,aAAa,CAAC;EAChCC,EAAAA,WAAWA,CAACC,OAAO,EAAEC,MAAM,EAAE;EAC3B,IAAA,KAAK,CAACD,OAAO,EAAEC,MAAM,CAAC,CAAA;EAEtB,IAAA,IAAI,CAACC,OAAO,GAAGC,cAAc,CAACC,OAAO,CAACf,eAAe,EAAE,IAAI,CAACgB,QAAQ,CAAC,CAAA;EACrE,IAAA,IAAI,CAACC,SAAS,GAAG,IAAI,CAACC,mBAAmB,EAAE,CAAA;EAC3C,IAAA,IAAI,CAACC,UAAU,GAAG,IAAI,CAACC,oBAAoB,EAAE,CAAA;MAC7C,IAAI,CAACC,QAAQ,GAAG,KAAK,CAAA;MACrB,IAAI,CAACC,gBAAgB,GAAG,KAAK,CAAA;EAC7B,IAAA,IAAI,CAACC,UAAU,GAAG,IAAIC,eAAe,EAAE,CAAA;MAEvC,IAAI,CAACC,kBAAkB,EAAE,CAAA;EAC3B,GAAA;;EAEA;IACA,WAAWtB,OAAOA,GAAG;EACnB,IAAA,OAAOA,OAAO,CAAA;EAChB,GAAA;IAEA,WAAWI,WAAWA,GAAG;EACvB,IAAA,OAAOA,WAAW,CAAA;EACpB,GAAA;IAEA,WAAW3B,IAAIA,GAAG;EAChB,IAAA,OAAOA,IAAI,CAAA;EACb,GAAA;;EAEA;IACA8C,MAAMA,CAACC,aAAa,EAAE;EACpB,IAAA,OAAO,IAAI,CAACN,QAAQ,GAAG,IAAI,CAACO,IAAI,EAAE,GAAG,IAAI,CAACC,IAAI,CAACF,aAAa,CAAC,CAAA;EAC/D,GAAA;IAEAE,IAAIA,CAACF,aAAa,EAAE;EAClB,IAAA,IAAI,IAAI,CAACN,QAAQ,IAAI,IAAI,CAACC,gBAAgB,EAAE;EAC1C,MAAA,OAAA;EACF,KAAA;MAEA,MAAMQ,SAAS,GAAGC,YAAY,CAACC,OAAO,CAAC,IAAI,CAAChB,QAAQ,EAAE5B,UAAU,EAAE;EAChEuC,MAAAA,aAAAA;EACF,KAAC,CAAC,CAAA;MAEF,IAAIG,SAAS,CAACG,gBAAgB,EAAE;EAC9B,MAAA,OAAA;EACF,KAAA;MAEA,IAAI,CAACZ,QAAQ,GAAG,IAAI,CAAA;MACpB,IAAI,CAACC,gBAAgB,GAAG,IAAI,CAAA;EAE5B,IAAA,IAAI,CAACC,UAAU,CAACK,IAAI,EAAE,CAAA;MAEtBM,QAAQ,CAACC,IAAI,CAACC,SAAS,CAACC,GAAG,CAAC1C,eAAe,CAAC,CAAA;MAE5C,IAAI,CAAC2C,aAAa,EAAE,CAAA;EAEpB,IAAA,IAAI,CAACrB,SAAS,CAACY,IAAI,CAAC,MAAM,IAAI,CAACU,YAAY,CAACZ,aAAa,CAAC,CAAC,CAAA;EAC7D,GAAA;EAEAC,EAAAA,IAAIA,GAAG;MACL,IAAI,CAAC,IAAI,CAACP,QAAQ,IAAI,IAAI,CAACC,gBAAgB,EAAE;EAC3C,MAAA,OAAA;EACF,KAAA;MAEA,MAAMkB,SAAS,GAAGT,YAAY,CAACC,OAAO,CAAC,IAAI,CAAChB,QAAQ,EAAE/B,UAAU,CAAC,CAAA;MAEjE,IAAIuD,SAAS,CAACP,gBAAgB,EAAE;EAC9B,MAAA,OAAA;EACF,KAAA;MAEA,IAAI,CAACZ,QAAQ,GAAG,KAAK,CAAA;MACrB,IAAI,CAACC,gBAAgB,GAAG,IAAI,CAAA;EAC5B,IAAA,IAAI,CAACH,UAAU,CAACsB,UAAU,EAAE,CAAA;MAE5B,IAAI,CAACzB,QAAQ,CAACoB,SAAS,CAACM,MAAM,CAAC7C,eAAe,CAAC,CAAA;EAE/C,IAAA,IAAI,CAAC8C,cAAc,CAAC,MAAM,IAAI,CAACC,UAAU,EAAE,EAAE,IAAI,CAAC5B,QAAQ,EAAE,IAAI,CAAC6B,WAAW,EAAE,CAAC,CAAA;EACjF,GAAA;EAEAC,EAAAA,OAAOA,GAAG;EACRf,IAAAA,YAAY,CAACgB,GAAG,CAACC,MAAM,EAAElE,SAAS,CAAC,CAAA;MACnCiD,YAAY,CAACgB,GAAG,CAAC,IAAI,CAAClC,OAAO,EAAE/B,SAAS,CAAC,CAAA;EAEzC,IAAA,IAAI,CAACmC,SAAS,CAAC6B,OAAO,EAAE,CAAA;EACxB,IAAA,IAAI,CAAC3B,UAAU,CAACsB,UAAU,EAAE,CAAA;MAE5B,KAAK,CAACK,OAAO,EAAE,CAAA;EACjB,GAAA;EAEAG,EAAAA,YAAYA,GAAG;MACb,IAAI,CAACX,aAAa,EAAE,CAAA;EACtB,GAAA;;EAEA;EACApB,EAAAA,mBAAmBA,GAAG;MACpB,OAAO,IAAIgC,QAAQ,CAAC;QAClBC,SAAS,EAAEC,OAAO,CAAC,IAAI,CAACC,OAAO,CAACjD,QAAQ,CAAC;EAAE;EAC3CkD,MAAAA,UAAU,EAAE,IAAI,CAACT,WAAW,EAAC;EAC/B,KAAC,CAAC,CAAA;EACJ,GAAA;EAEAzB,EAAAA,oBAAoBA,GAAG;MACrB,OAAO,IAAImC,SAAS,CAAC;QACnBC,WAAW,EAAE,IAAI,CAACxC,QAAAA;EACpB,KAAC,CAAC,CAAA;EACJ,GAAA;IAEAuB,YAAYA,CAACZ,aAAa,EAAE;EAC1B;MACA,IAAI,CAACO,QAAQ,CAACC,IAAI,CAACsB,QAAQ,CAAC,IAAI,CAACzC,QAAQ,CAAC,EAAE;QAC1CkB,QAAQ,CAACC,IAAI,CAACuB,MAAM,CAAC,IAAI,CAAC1C,QAAQ,CAAC,CAAA;EACrC,KAAA;EAEA,IAAA,IAAI,CAACA,QAAQ,CAAC2C,KAAK,CAACC,OAAO,GAAG,OAAO,CAAA;EACrC,IAAA,IAAI,CAAC5C,QAAQ,CAAC6C,eAAe,CAAC,aAAa,CAAC,CAAA;MAC5C,IAAI,CAAC7C,QAAQ,CAAC8C,YAAY,CAAC,YAAY,EAAE,IAAI,CAAC,CAAA;MAC9C,IAAI,CAAC9C,QAAQ,CAAC8C,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;EAC5C,IAAA,IAAI,CAAC9C,QAAQ,CAAC+C,SAAS,GAAG,CAAC,CAAA;MAE3B,MAAMC,SAAS,GAAGlD,cAAc,CAACC,OAAO,CAACd,mBAAmB,EAAE,IAAI,CAACY,OAAO,CAAC,CAAA;EAC3E,IAAA,IAAImD,SAAS,EAAE;QACbA,SAAS,CAACD,SAAS,GAAG,CAAC,CAAA;EACzB,KAAA;EAEAE,IAAAA,eAAM,CAAC,IAAI,CAACjD,QAAQ,CAAC,CAAA;MAErB,IAAI,CAACA,QAAQ,CAACoB,SAAS,CAACC,GAAG,CAACxC,eAAe,CAAC,CAAA;MAE5C,MAAMqE,kBAAkB,GAAGA,MAAM;EAC/B,MAAA,IAAI,IAAI,CAACb,OAAO,CAAChD,KAAK,EAAE;EACtB,QAAA,IAAI,CAACc,UAAU,CAACgD,QAAQ,EAAE,CAAA;EAC5B,OAAA;QAEA,IAAI,CAAC7C,gBAAgB,GAAG,KAAK,CAAA;QAC7BS,YAAY,CAACC,OAAO,CAAC,IAAI,CAAChB,QAAQ,EAAE3B,WAAW,EAAE;EAC/CsC,QAAAA,aAAAA;EACF,OAAC,CAAC,CAAA;OACH,CAAA;EAED,IAAA,IAAI,CAACgB,cAAc,CAACuB,kBAAkB,EAAE,IAAI,CAACrD,OAAO,EAAE,IAAI,CAACgC,WAAW,EAAE,CAAC,CAAA;EAC3E,GAAA;EAEApB,EAAAA,kBAAkBA,GAAG;MACnBM,YAAY,CAACqC,EAAE,CAAC,IAAI,CAACpD,QAAQ,EAAEvB,qBAAqB,EAAE4E,KAAK,IAAI;EAC7D,MAAA,IAAIA,KAAK,CAACC,GAAG,KAAKtF,UAAU,EAAE;EAC5B,QAAA,OAAA;EACF,OAAA;EAEA,MAAA,IAAI,IAAI,CAACqE,OAAO,CAAC/C,QAAQ,EAAE;UACzB,IAAI,CAACsB,IAAI,EAAE,CAAA;EACX,QAAA,OAAA;EACF,OAAA;QAEA,IAAI,CAAC2C,0BAA0B,EAAE,CAAA;EACnC,KAAC,CAAC,CAAA;EAEFxC,IAAAA,YAAY,CAACqC,EAAE,CAACpB,MAAM,EAAE1D,YAAY,EAAE,MAAM;QAC1C,IAAI,IAAI,CAAC+B,QAAQ,IAAI,CAAC,IAAI,CAACC,gBAAgB,EAAE;UAC3C,IAAI,CAACgB,aAAa,EAAE,CAAA;EACtB,OAAA;EACF,KAAC,CAAC,CAAA;MAEFP,YAAY,CAACqC,EAAE,CAAC,IAAI,CAACpD,QAAQ,EAAExB,uBAAuB,EAAE6E,KAAK,IAAI;EAC/D;QACAtC,YAAY,CAACyC,GAAG,CAAC,IAAI,CAACxD,QAAQ,EAAEzB,mBAAmB,EAAEkF,MAAM,IAAI;EAC7D,QAAA,IAAI,IAAI,CAACzD,QAAQ,KAAKqD,KAAK,CAACK,MAAM,IAAI,IAAI,CAAC1D,QAAQ,KAAKyD,MAAM,CAACC,MAAM,EAAE;EACrE,UAAA,OAAA;EACF,SAAA;EAEA,QAAA,IAAI,IAAI,CAACrB,OAAO,CAACjD,QAAQ,KAAK,QAAQ,EAAE;YACtC,IAAI,CAACmE,0BAA0B,EAAE,CAAA;EACjC,UAAA,OAAA;EACF,SAAA;EAEA,QAAA,IAAI,IAAI,CAAClB,OAAO,CAACjD,QAAQ,EAAE;YACzB,IAAI,CAACwB,IAAI,EAAE,CAAA;EACb,SAAA;EACF,OAAC,CAAC,CAAA;EACJ,KAAC,CAAC,CAAA;EACJ,GAAA;EAEAgB,EAAAA,UAAUA,GAAG;EACX,IAAA,IAAI,CAAC5B,QAAQ,CAAC2C,KAAK,CAACC,OAAO,GAAG,MAAM,CAAA;MACpC,IAAI,CAAC5C,QAAQ,CAAC8C,YAAY,CAAC,aAAa,EAAE,IAAI,CAAC,CAAA;EAC/C,IAAA,IAAI,CAAC9C,QAAQ,CAAC6C,eAAe,CAAC,YAAY,CAAC,CAAA;EAC3C,IAAA,IAAI,CAAC7C,QAAQ,CAAC6C,eAAe,CAAC,MAAM,CAAC,CAAA;MACrC,IAAI,CAACvC,gBAAgB,GAAG,KAAK,CAAA;EAE7B,IAAA,IAAI,CAACL,SAAS,CAACW,IAAI,CAAC,MAAM;QACxBM,QAAQ,CAACC,IAAI,CAACC,SAAS,CAACM,MAAM,CAAC/C,eAAe,CAAC,CAAA;QAC/C,IAAI,CAACgF,iBAAiB,EAAE,CAAA;EACxB,MAAA,IAAI,CAACpD,UAAU,CAACqD,KAAK,EAAE,CAAA;QACvB7C,YAAY,CAACC,OAAO,CAAC,IAAI,CAAChB,QAAQ,EAAE7B,YAAY,CAAC,CAAA;EACnD,KAAC,CAAC,CAAA;EACJ,GAAA;EAEA0D,EAAAA,WAAWA,GAAG;MACZ,OAAO,IAAI,CAAC7B,QAAQ,CAACoB,SAAS,CAACqB,QAAQ,CAAC7D,eAAe,CAAC,CAAA;EAC1D,GAAA;EAEA2E,EAAAA,0BAA0BA,GAAG;MAC3B,MAAM/B,SAAS,GAAGT,YAAY,CAACC,OAAO,CAAC,IAAI,CAAChB,QAAQ,EAAE9B,oBAAoB,CAAC,CAAA;MAC3E,IAAIsD,SAAS,CAACP,gBAAgB,EAAE;EAC9B,MAAA,OAAA;EACF,KAAA;EAEA,IAAA,MAAM4C,kBAAkB,GAAG,IAAI,CAAC7D,QAAQ,CAAC8D,YAAY,GAAG5C,QAAQ,CAAC6C,eAAe,CAACC,YAAY,CAAA;MAC7F,MAAMC,gBAAgB,GAAG,IAAI,CAACjE,QAAQ,CAAC2C,KAAK,CAACuB,SAAS,CAAA;EACtD;EACA,IAAA,IAAID,gBAAgB,KAAK,QAAQ,IAAI,IAAI,CAACjE,QAAQ,CAACoB,SAAS,CAACqB,QAAQ,CAAC3D,iBAAiB,CAAC,EAAE;EACxF,MAAA,OAAA;EACF,KAAA;MAEA,IAAI,CAAC+E,kBAAkB,EAAE;EACvB,MAAA,IAAI,CAAC7D,QAAQ,CAAC2C,KAAK,CAACuB,SAAS,GAAG,QAAQ,CAAA;EAC1C,KAAA;MAEA,IAAI,CAAClE,QAAQ,CAACoB,SAAS,CAACC,GAAG,CAACvC,iBAAiB,CAAC,CAAA;MAC9C,IAAI,CAAC6C,cAAc,CAAC,MAAM;QACxB,IAAI,CAAC3B,QAAQ,CAACoB,SAAS,CAACM,MAAM,CAAC5C,iBAAiB,CAAC,CAAA;QACjD,IAAI,CAAC6C,cAAc,CAAC,MAAM;EACxB,QAAA,IAAI,CAAC3B,QAAQ,CAAC2C,KAAK,CAACuB,SAAS,GAAGD,gBAAgB,CAAA;EAClD,OAAC,EAAE,IAAI,CAACpE,OAAO,CAAC,CAAA;EAClB,KAAC,EAAE,IAAI,CAACA,OAAO,CAAC,CAAA;EAEhB,IAAA,IAAI,CAACG,QAAQ,CAACX,KAAK,EAAE,CAAA;EACvB,GAAA;;EAEA;EACF;EACA;;EAEEiC,EAAAA,aAAaA,GAAG;EACd,IAAA,MAAMuC,kBAAkB,GAAG,IAAI,CAAC7D,QAAQ,CAAC8D,YAAY,GAAG5C,QAAQ,CAAC6C,eAAe,CAACC,YAAY,CAAA;MAC7F,MAAMG,cAAc,GAAG,IAAI,CAAC5D,UAAU,CAAC6D,QAAQ,EAAE,CAAA;EACjD,IAAA,MAAMC,iBAAiB,GAAGF,cAAc,GAAG,CAAC,CAAA;EAE5C,IAAA,IAAIE,iBAAiB,IAAI,CAACR,kBAAkB,EAAE;QAC5C,MAAMS,QAAQ,GAAGC,cAAK,EAAE,GAAG,aAAa,GAAG,cAAc,CAAA;QACzD,IAAI,CAACvE,QAAQ,CAAC2C,KAAK,CAAC2B,QAAQ,CAAC,GAAI,CAAEH,EAAAA,cAAe,CAAG,EAAA,CAAA,CAAA;EACvD,KAAA;EAEA,IAAA,IAAI,CAACE,iBAAiB,IAAIR,kBAAkB,EAAE;QAC5C,MAAMS,QAAQ,GAAGC,cAAK,EAAE,GAAG,cAAc,GAAG,aAAa,CAAA;QACzD,IAAI,CAACvE,QAAQ,CAAC2C,KAAK,CAAC2B,QAAQ,CAAC,GAAI,CAAEH,EAAAA,cAAe,CAAG,EAAA,CAAA,CAAA;EACvD,KAAA;EACF,GAAA;EAEAR,EAAAA,iBAAiBA,GAAG;EAClB,IAAA,IAAI,CAAC3D,QAAQ,CAAC2C,KAAK,CAAC6B,WAAW,GAAG,EAAE,CAAA;EACpC,IAAA,IAAI,CAACxE,QAAQ,CAAC2C,KAAK,CAAC8B,YAAY,GAAG,EAAE,CAAA;EACvC,GAAA;;EAEA;EACA,EAAA,OAAOC,eAAeA,CAAC9E,MAAM,EAAEe,aAAa,EAAE;EAC5C,IAAA,OAAO,IAAI,CAACgE,IAAI,CAAC,YAAY;QAC3B,MAAMC,IAAI,GAAGpF,KAAK,CAACqF,mBAAmB,CAAC,IAAI,EAAEjF,MAAM,CAAC,CAAA;EAEpD,MAAA,IAAI,OAAOA,MAAM,KAAK,QAAQ,EAAE;EAC9B,QAAA,OAAA;EACF,OAAA;EAEA,MAAA,IAAI,OAAOgF,IAAI,CAAChF,MAAM,CAAC,KAAK,WAAW,EAAE;EACvC,QAAA,MAAM,IAAIkF,SAAS,CAAE,CAAmBlF,iBAAAA,EAAAA,MAAO,GAAE,CAAC,CAAA;EACpD,OAAA;EAEAgF,MAAAA,IAAI,CAAChF,MAAM,CAAC,CAACe,aAAa,CAAC,CAAA;EAC7B,KAAC,CAAC,CAAA;EACJ,GAAA;EACF,CAAA;;EAEA;EACA;EACA;;EAEAI,YAAY,CAACqC,EAAE,CAAClC,QAAQ,EAAExC,oBAAoB,EAAEQ,oBAAoB,EAAE,UAAUmE,KAAK,EAAE;EACrF,EAAA,MAAMK,MAAM,GAAG5D,cAAc,CAACiF,sBAAsB,CAAC,IAAI,CAAC,CAAA;EAE1D,EAAA,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAACC,QAAQ,CAAC,IAAI,CAACC,OAAO,CAAC,EAAE;MACxC5B,KAAK,CAAC6B,cAAc,EAAE,CAAA;EACxB,GAAA;IAEAnE,YAAY,CAACyC,GAAG,CAACE,MAAM,EAAEtF,UAAU,EAAE0C,SAAS,IAAI;MAChD,IAAIA,SAAS,CAACG,gBAAgB,EAAE;EAC9B;EACA,MAAA,OAAA;EACF,KAAA;EAEAF,IAAAA,YAAY,CAACyC,GAAG,CAACE,MAAM,EAAEvF,YAAY,EAAE,MAAM;EAC3C,MAAA,IAAIgE,kBAAS,CAAC,IAAI,CAAC,EAAE;UACnB,IAAI,CAAC9C,KAAK,EAAE,CAAA;EACd,OAAA;EACF,KAAC,CAAC,CAAA;EACJ,GAAC,CAAC,CAAA;;EAEF;EACA,EAAA,MAAM8F,WAAW,GAAGrF,cAAc,CAACC,OAAO,CAAChB,aAAa,CAAC,CAAA;EACzD,EAAA,IAAIoG,WAAW,EAAE;MACf3F,KAAK,CAAC4F,WAAW,CAACD,WAAW,CAAC,CAACvE,IAAI,EAAE,CAAA;EACvC,GAAA;EAEA,EAAA,MAAMgE,IAAI,GAAGpF,KAAK,CAACqF,mBAAmB,CAACnB,MAAM,CAAC,CAAA;EAE9CkB,EAAAA,IAAI,CAAClE,MAAM,CAAC,IAAI,CAAC,CAAA;EACnB,CAAC,CAAC,CAAA;AAEF2E,4CAAoB,CAAC7F,KAAK,CAAC,CAAA;;EAE3B;EACA;EACA;;AAEA8F,6BAAkB,CAAC9F,KAAK,CAAC;;;;;;;;"} \ No newline at end of file
diff --git a/js/dist/offcanvas.js b/js/dist/offcanvas.js
index 9b9ab6a3c..b55b6ee5a 100644
--- a/js/dist/offcanvas.js
+++ b/js/dist/offcanvas.js
@@ -1,620 +1,24 @@
/*!
- * Bootstrap offcanvas.js v5.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Bootstrap offcanvas.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/selector-engine.js'), require('./dom/manipulator.js'), require('./dom/event-handler.js'), require('./base-component.js')) :
- typeof define === 'function' && define.amd ? define(['./dom/selector-engine', './dom/manipulator', './dom/event-handler', './base-component'], factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Offcanvas = factory(global.SelectorEngine, global.Manipulator, global.EventHandler, global.Base));
-})(this, (function (SelectorEngine, Manipulator, EventHandler, BaseComponent) { 'use strict';
-
- const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
-
- const SelectorEngine__default = /*#__PURE__*/_interopDefaultLegacy(SelectorEngine);
- const Manipulator__default = /*#__PURE__*/_interopDefaultLegacy(Manipulator);
- const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
- const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/selector-engine.js'), require('./util/backdrop.js'), require('./util/component-functions.js'), require('./util/focustrap.js'), require('./util/index.js'), require('./util/scrollbar.js')) :
+ typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './dom/selector-engine', './util/backdrop', './util/component-functions', './util/focustrap', './util/index', './util/scrollbar'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Offcanvas = factory(global.BaseComponent, global.EventHandler, global.SelectorEngine, global.Backdrop, global.ComponentFunctions, global.Focustrap, global.Index, global.Scrollbar));
+})(this, (function (BaseComponent, EventHandler, SelectorEngine, Backdrop, componentFunctions_js, FocusTrap, index_js, ScrollBarHelper) { 'use strict';
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/index.js
+ * Bootstrap offcanvas.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
- const MILLISECONDS_MULTIPLIER = 1000;
- const TRANSITION_END = 'transitionend'; // Shoutout AngusCroll (https://goo.gl/pxwQGp)
-
- const toType = obj => {
- if (obj === null || obj === undefined) {
- return `${obj}`;
- }
-
- return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase();
- };
-
- const getSelector = element => {
- let selector = element.getAttribute('data-bs-target');
-
- if (!selector || selector === '#') {
- let hrefAttr = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes,
- // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
- // `document.querySelector` will rightfully complain it is invalid.
- // See https://github.com/twbs/bootstrap/issues/32273
-
- if (!hrefAttr || !hrefAttr.includes('#') && !hrefAttr.startsWith('.')) {
- return null;
- } // Just in case some CMS puts out a full URL with the anchor appended
-
-
- if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {
- hrefAttr = `#${hrefAttr.split('#')[1]}`;
- }
-
- selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null;
- }
-
- return selector;
- };
-
- const getElementFromSelector = element => {
- const selector = getSelector(element);
- return selector ? document.querySelector(selector) : null;
- };
-
- const getTransitionDurationFromElement = element => {
- if (!element) {
- return 0;
- } // Get transition-duration of the element
-
-
- let {
- transitionDuration,
- transitionDelay
- } = window.getComputedStyle(element);
- const floatTransitionDuration = Number.parseFloat(transitionDuration);
- const floatTransitionDelay = Number.parseFloat(transitionDelay); // Return 0 if element or transition duration is not found
-
- if (!floatTransitionDuration && !floatTransitionDelay) {
- return 0;
- } // If multiple durations are defined, take the first
-
-
- transitionDuration = transitionDuration.split(',')[0];
- transitionDelay = transitionDelay.split(',')[0];
- return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;
- };
-
- const triggerTransitionEnd = element => {
- element.dispatchEvent(new Event(TRANSITION_END));
- };
-
- const isElement = obj => {
- if (!obj || typeof obj !== 'object') {
- return false;
- }
-
- if (typeof obj.jquery !== 'undefined') {
- obj = obj[0];
- }
-
- return typeof obj.nodeType !== 'undefined';
- };
-
- const getElement = obj => {
- if (isElement(obj)) {
- // it's a jQuery object or a node element
- return obj.jquery ? obj[0] : obj;
- }
-
- if (typeof obj === 'string' && obj.length > 0) {
- return document.querySelector(obj);
- }
-
- return null;
- };
-
- const typeCheckConfig = (componentName, config, configTypes) => {
- Object.keys(configTypes).forEach(property => {
- const expectedTypes = configTypes[property];
- const value = config[property];
- const valueType = value && isElement(value) ? 'element' : toType(value);
-
- if (!new RegExp(expectedTypes).test(valueType)) {
- throw new TypeError(`${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`);
- }
- });
- };
-
- const isVisible = element => {
- if (!isElement(element) || element.getClientRects().length === 0) {
- return false;
- }
-
- return getComputedStyle(element).getPropertyValue('visibility') === 'visible';
- };
-
- const isDisabled = element => {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return true;
- }
-
- if (element.classList.contains('disabled')) {
- return true;
- }
- if (typeof element.disabled !== 'undefined') {
- return element.disabled;
- }
- return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';
- };
/**
- * Trick to restart an element's animation
- *
- * @param {HTMLElement} element
- * @return void
- *
- * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
- */
-
-
- const reflow = element => {
- // eslint-disable-next-line no-unused-expressions
- element.offsetHeight;
- };
-
- const getjQuery = () => {
- const {
- jQuery
- } = window;
-
- if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
- return jQuery;
- }
-
- return null;
- };
-
- const DOMContentLoadedCallbacks = [];
-
- const onDOMContentLoaded = callback => {
- if (document.readyState === 'loading') {
- // add listener on the first call when the document is in loading state
- if (!DOMContentLoadedCallbacks.length) {
- document.addEventListener('DOMContentLoaded', () => {
- DOMContentLoadedCallbacks.forEach(callback => callback());
- });
- }
-
- DOMContentLoadedCallbacks.push(callback);
- } else {
- callback();
- }
- };
-
- const defineJQueryPlugin = plugin => {
- onDOMContentLoaded(() => {
- const $ = getjQuery();
- /* istanbul ignore if */
-
- if ($) {
- const name = plugin.NAME;
- const JQUERY_NO_CONFLICT = $.fn[name];
- $.fn[name] = plugin.jQueryInterface;
- $.fn[name].Constructor = plugin;
-
- $.fn[name].noConflict = () => {
- $.fn[name] = JQUERY_NO_CONFLICT;
- return plugin.jQueryInterface;
- };
- }
- });
- };
-
- const execute = callback => {
- if (typeof callback === 'function') {
- callback();
- }
- };
-
- const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {
- if (!waitForTransition) {
- execute(callback);
- return;
- }
-
- const durationPadding = 5;
- const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding;
- let called = false;
-
- const handler = ({
- target
- }) => {
- if (target !== transitionElement) {
- return;
- }
-
- called = true;
- transitionElement.removeEventListener(TRANSITION_END, handler);
- execute(callback);
- };
-
- transitionElement.addEventListener(TRANSITION_END, handler);
- setTimeout(() => {
- if (!called) {
- triggerTransitionEnd(transitionElement);
- }
- }, emulatedDuration);
- };
-
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/scrollBar.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top';
- const SELECTOR_STICKY_CONTENT = '.sticky-top';
-
- class ScrollBarHelper {
- constructor() {
- this._element = document.body;
- }
-
- getWidth() {
- // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes
- const documentWidth = document.documentElement.clientWidth;
- return Math.abs(window.innerWidth - documentWidth);
- }
-
- hide() {
- const width = this.getWidth();
-
- this._disableOverFlow(); // give padding to element to balance the hidden scrollbar width
-
-
- this._setElementAttributes(this._element, 'paddingRight', calculatedValue => calculatedValue + width); // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth
-
-
- this._setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width);
-
- this._setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width);
- }
-
- _disableOverFlow() {
- this._saveInitialAttribute(this._element, 'overflow');
-
- this._element.style.overflow = 'hidden';
- }
-
- _setElementAttributes(selector, styleProp, callback) {
- const scrollbarWidth = this.getWidth();
-
- const manipulationCallBack = element => {
- if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {
- return;
- }
-
- this._saveInitialAttribute(element, styleProp);
-
- const calculatedValue = window.getComputedStyle(element)[styleProp];
- element.style[styleProp] = `${callback(Number.parseFloat(calculatedValue))}px`;
- };
-
- this._applyManipulationCallback(selector, manipulationCallBack);
- }
-
- reset() {
- this._resetElementAttributes(this._element, 'overflow');
-
- this._resetElementAttributes(this._element, 'paddingRight');
-
- this._resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight');
-
- this._resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight');
- }
-
- _saveInitialAttribute(element, styleProp) {
- const actualValue = element.style[styleProp];
-
- if (actualValue) {
- Manipulator__default.default.setDataAttribute(element, styleProp, actualValue);
- }
- }
-
- _resetElementAttributes(selector, styleProp) {
- const manipulationCallBack = element => {
- const value = Manipulator__default.default.getDataAttribute(element, styleProp);
-
- if (typeof value === 'undefined') {
- element.style.removeProperty(styleProp);
- } else {
- Manipulator__default.default.removeDataAttribute(element, styleProp);
- element.style[styleProp] = value;
- }
- };
-
- this._applyManipulationCallback(selector, manipulationCallBack);
- }
-
- _applyManipulationCallback(selector, callBack) {
- if (isElement(selector)) {
- callBack(selector);
- } else {
- SelectorEngine__default.default.find(selector, this._element).forEach(callBack);
- }
- }
-
- isOverflowing() {
- return this.getWidth() > 0;
- }
-
- }
-
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/backdrop.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- const Default$2 = {
- className: 'modal-backdrop',
- isVisible: true,
- // if false, we use the backdrop helper without adding any element to the dom
- isAnimated: false,
- rootElement: 'body',
- // give the choice to place backdrop under different elements
- clickCallback: null
- };
- const DefaultType$2 = {
- className: 'string',
- isVisible: 'boolean',
- isAnimated: 'boolean',
- rootElement: '(element|string)',
- clickCallback: '(function|null)'
- };
- const NAME$2 = 'backdrop';
- const CLASS_NAME_FADE = 'fade';
- const CLASS_NAME_SHOW$1 = 'show';
- const EVENT_MOUSEDOWN = `mousedown.bs.${NAME$2}`;
-
- class Backdrop {
- constructor(config) {
- this._config = this._getConfig(config);
- this._isAppended = false;
- this._element = null;
- }
-
- show(callback) {
- if (!this._config.isVisible) {
- execute(callback);
- return;
- }
-
- this._append();
-
- if (this._config.isAnimated) {
- reflow(this._getElement());
- }
-
- this._getElement().classList.add(CLASS_NAME_SHOW$1);
-
- this._emulateAnimation(() => {
- execute(callback);
- });
- }
-
- hide(callback) {
- if (!this._config.isVisible) {
- execute(callback);
- return;
- }
-
- this._getElement().classList.remove(CLASS_NAME_SHOW$1);
-
- this._emulateAnimation(() => {
- this.dispose();
- execute(callback);
- });
- } // Private
-
-
- _getElement() {
- if (!this._element) {
- const backdrop = document.createElement('div');
- backdrop.className = this._config.className;
-
- if (this._config.isAnimated) {
- backdrop.classList.add(CLASS_NAME_FADE);
- }
-
- this._element = backdrop;
- }
-
- return this._element;
- }
-
- _getConfig(config) {
- config = { ...Default$2,
- ...(typeof config === 'object' ? config : {})
- }; // use getElement() with the default "body" to get a fresh Element on each instantiation
-
- config.rootElement = getElement(config.rootElement);
- typeCheckConfig(NAME$2, config, DefaultType$2);
- return config;
- }
-
- _append() {
- if (this._isAppended) {
- return;
- }
-
- this._config.rootElement.append(this._getElement());
-
- EventHandler__default.default.on(this._getElement(), EVENT_MOUSEDOWN, () => {
- execute(this._config.clickCallback);
- });
- this._isAppended = true;
- }
-
- dispose() {
- if (!this._isAppended) {
- return;
- }
-
- EventHandler__default.default.off(this._element, EVENT_MOUSEDOWN);
-
- this._element.remove();
-
- this._isAppended = false;
- }
-
- _emulateAnimation(callback) {
- executeAfterTransition(callback, this._getElement(), this._config.isAnimated);
- }
-
- }
-
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/focustrap.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- const Default$1 = {
- trapElement: null,
- // The element to trap focus inside of
- autofocus: true
- };
- const DefaultType$1 = {
- trapElement: 'element',
- autofocus: 'boolean'
- };
- const NAME$1 = 'focustrap';
- const DATA_KEY$1 = 'bs.focustrap';
- const EVENT_KEY$1 = `.${DATA_KEY$1}`;
- const EVENT_FOCUSIN = `focusin${EVENT_KEY$1}`;
- const EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY$1}`;
- const TAB_KEY = 'Tab';
- const TAB_NAV_FORWARD = 'forward';
- const TAB_NAV_BACKWARD = 'backward';
-
- class FocusTrap {
- constructor(config) {
- this._config = this._getConfig(config);
- this._isActive = false;
- this._lastTabNavDirection = null;
- }
-
- activate() {
- const {
- trapElement,
- autofocus
- } = this._config;
-
- if (this._isActive) {
- return;
- }
-
- if (autofocus) {
- trapElement.focus();
- }
-
- EventHandler__default.default.off(document, EVENT_KEY$1); // guard against infinite focus loop
-
- EventHandler__default.default.on(document, EVENT_FOCUSIN, event => this._handleFocusin(event));
- EventHandler__default.default.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event));
- this._isActive = true;
- }
-
- deactivate() {
- if (!this._isActive) {
- return;
- }
-
- this._isActive = false;
- EventHandler__default.default.off(document, EVENT_KEY$1);
- } // Private
-
-
- _handleFocusin(event) {
- const {
- target
- } = event;
- const {
- trapElement
- } = this._config;
-
- if (target === document || target === trapElement || trapElement.contains(target)) {
- return;
- }
-
- const elements = SelectorEngine__default.default.focusableChildren(trapElement);
-
- if (elements.length === 0) {
- trapElement.focus();
- } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {
- elements[elements.length - 1].focus();
- } else {
- elements[0].focus();
- }
- }
-
- _handleKeydown(event) {
- if (event.key !== TAB_KEY) {
- return;
- }
-
- this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD;
- }
-
- _getConfig(config) {
- config = { ...Default$1,
- ...(typeof config === 'object' ? config : {})
- };
- typeCheckConfig(NAME$1, config, DefaultType$1);
- return config;
- }
-
- }
-
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/component-functions.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
-
- const enableDismissTrigger = (component, method = 'hide') => {
- const clickEvent = `click.dismiss${component.EVENT_KEY}`;
- const name = component.NAME;
- EventHandler__default.default.on(document, clickEvent, `[data-bs-dismiss="${name}"]`, function (event) {
- if (['A', 'AREA'].includes(this.tagName)) {
- event.preventDefault();
- }
-
- if (isDisabled(this)) {
- return;
- }
-
- const target = getElementFromSelector(this) || this.closest(`.${name}`);
- const instance = component.getOrCreateInstance(target); // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method
-
- instance[method]();
- });
- };
-
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): offcanvas.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- /**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'offcanvas';
@@ -623,243 +27,218 @@
const DATA_API_KEY = '.data-api';
const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`;
const ESCAPE_KEY = 'Escape';
- const Default = {
- backdrop: true,
- keyboard: true,
- scroll: false
- };
- const DefaultType = {
- backdrop: 'boolean',
- keyboard: 'boolean',
- scroll: 'boolean'
- };
const CLASS_NAME_SHOW = 'show';
+ const CLASS_NAME_SHOWING = 'showing';
+ const CLASS_NAME_HIDING = 'hiding';
const CLASS_NAME_BACKDROP = 'offcanvas-backdrop';
const OPEN_SELECTOR = '.offcanvas.show';
const EVENT_SHOW = `show${EVENT_KEY}`;
const EVENT_SHOWN = `shown${EVENT_KEY}`;
const EVENT_HIDE = `hide${EVENT_KEY}`;
+ const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`;
const EVENT_HIDDEN = `hidden${EVENT_KEY}`;
+ const EVENT_RESIZE = `resize${EVENT_KEY}`;
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;
const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`;
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="offcanvas"]';
+ const Default = {
+ backdrop: true,
+ keyboard: true,
+ scroll: false
+ };
+ const DefaultType = {
+ backdrop: '(boolean|string)',
+ keyboard: 'boolean',
+ scroll: 'boolean'
+ };
+
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
- class Offcanvas extends BaseComponent__default.default {
+ class Offcanvas extends BaseComponent {
constructor(element, config) {
- super(element);
- this._config = this._getConfig(config);
+ super(element, config);
this._isShown = false;
this._backdrop = this._initializeBackDrop();
this._focustrap = this._initializeFocusTrap();
-
this._addEventListeners();
- } // Getters
-
-
- static get NAME() {
- return NAME;
}
+ // Getters
static get Default() {
return Default;
- } // Public
-
+ }
+ static get DefaultType() {
+ return DefaultType;
+ }
+ static get NAME() {
+ return NAME;
+ }
+ // Public
toggle(relatedTarget) {
return this._isShown ? this.hide() : this.show(relatedTarget);
}
-
show(relatedTarget) {
if (this._isShown) {
return;
}
-
- const showEvent = EventHandler__default.default.trigger(this._element, EVENT_SHOW, {
+ const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {
relatedTarget
});
-
if (showEvent.defaultPrevented) {
return;
}
-
this._isShown = true;
- this._element.style.visibility = 'visible';
-
this._backdrop.show();
-
if (!this._config.scroll) {
new ScrollBarHelper().hide();
}
-
- this._element.removeAttribute('aria-hidden');
-
this._element.setAttribute('aria-modal', true);
-
this._element.setAttribute('role', 'dialog');
-
- this._element.classList.add(CLASS_NAME_SHOW);
-
+ this._element.classList.add(CLASS_NAME_SHOWING);
const completeCallBack = () => {
- if (!this._config.scroll) {
+ if (!this._config.scroll || this._config.backdrop) {
this._focustrap.activate();
}
-
- EventHandler__default.default.trigger(this._element, EVENT_SHOWN, {
+ this._element.classList.add(CLASS_NAME_SHOW);
+ this._element.classList.remove(CLASS_NAME_SHOWING);
+ EventHandler.trigger(this._element, EVENT_SHOWN, {
relatedTarget
});
};
-
this._queueCallback(completeCallBack, this._element, true);
}
-
hide() {
if (!this._isShown) {
return;
}
-
- const hideEvent = EventHandler__default.default.trigger(this._element, EVENT_HIDE);
-
+ const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE);
if (hideEvent.defaultPrevented) {
return;
}
-
this._focustrap.deactivate();
-
this._element.blur();
-
this._isShown = false;
-
- this._element.classList.remove(CLASS_NAME_SHOW);
-
+ this._element.classList.add(CLASS_NAME_HIDING);
this._backdrop.hide();
-
const completeCallback = () => {
- this._element.setAttribute('aria-hidden', true);
-
+ this._element.classList.remove(CLASS_NAME_SHOW, CLASS_NAME_HIDING);
this._element.removeAttribute('aria-modal');
-
this._element.removeAttribute('role');
-
- this._element.style.visibility = 'hidden';
-
if (!this._config.scroll) {
new ScrollBarHelper().reset();
}
-
- EventHandler__default.default.trigger(this._element, EVENT_HIDDEN);
+ EventHandler.trigger(this._element, EVENT_HIDDEN);
};
-
this._queueCallback(completeCallback, this._element, true);
}
-
dispose() {
this._backdrop.dispose();
-
this._focustrap.deactivate();
-
super.dispose();
- } // Private
-
-
- _getConfig(config) {
- config = { ...Default,
- ...Manipulator__default.default.getDataAttributes(this._element),
- ...(typeof config === 'object' ? config : {})
- };
- typeCheckConfig(NAME, config, DefaultType);
- return config;
}
+ // Private
_initializeBackDrop() {
+ const clickCallback = () => {
+ if (this._config.backdrop === 'static') {
+ EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);
+ return;
+ }
+ this.hide();
+ };
+
+ // 'static' option will be translated to true, and booleans will keep their value
+ const isVisible = Boolean(this._config.backdrop);
return new Backdrop({
className: CLASS_NAME_BACKDROP,
- isVisible: this._config.backdrop,
+ isVisible,
isAnimated: true,
rootElement: this._element.parentNode,
- clickCallback: () => this.hide()
+ clickCallback: isVisible ? clickCallback : null
});
}
-
_initializeFocusTrap() {
return new FocusTrap({
trapElement: this._element
});
}
-
_addEventListeners() {
- EventHandler__default.default.on(this._element, EVENT_KEYDOWN_DISMISS, event => {
- if (this._config.keyboard && event.key === ESCAPE_KEY) {
+ EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {
+ if (event.key !== ESCAPE_KEY) {
+ return;
+ }
+ if (this._config.keyboard) {
this.hide();
+ return;
}
+ EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED);
});
- } // Static
-
+ }
+ // Static
static jQueryInterface(config) {
return this.each(function () {
const data = Offcanvas.getOrCreateInstance(this, config);
-
if (typeof config !== 'string') {
return;
}
-
if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
throw new TypeError(`No method named "${config}"`);
}
-
data[config](this);
});
}
-
}
+
/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
+ * Data API implementation
*/
-
- EventHandler__default.default.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
- const target = getElementFromSelector(this);
-
+ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
+ const target = SelectorEngine.getElementFromSelector(this);
if (['A', 'AREA'].includes(this.tagName)) {
event.preventDefault();
}
-
- if (isDisabled(this)) {
+ if (index_js.isDisabled(this)) {
return;
}
-
- EventHandler__default.default.one(target, EVENT_HIDDEN, () => {
+ EventHandler.one(target, EVENT_HIDDEN, () => {
// focus on trigger when it is closed
- if (isVisible(this)) {
+ if (index_js.isVisible(this)) {
this.focus();
}
- }); // avoid conflict when clicking a toggler of an offcanvas, while another is open
-
- const allReadyOpen = SelectorEngine__default.default.findOne(OPEN_SELECTOR);
+ });
- if (allReadyOpen && allReadyOpen !== target) {
- Offcanvas.getInstance(allReadyOpen).hide();
+ // avoid conflict when clicking a toggler of an offcanvas, while another is open
+ const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR);
+ if (alreadyOpen && alreadyOpen !== target) {
+ Offcanvas.getInstance(alreadyOpen).hide();
}
-
const data = Offcanvas.getOrCreateInstance(target);
data.toggle(this);
});
- EventHandler__default.default.on(window, EVENT_LOAD_DATA_API, () => SelectorEngine__default.default.find(OPEN_SELECTOR).forEach(el => Offcanvas.getOrCreateInstance(el).show()));
- enableDismissTrigger(Offcanvas);
+ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
+ for (const selector of SelectorEngine.find(OPEN_SELECTOR)) {
+ Offcanvas.getOrCreateInstance(selector).show();
+ }
+ });
+ EventHandler.on(window, EVENT_RESIZE, () => {
+ for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) {
+ if (getComputedStyle(element).position !== 'fixed') {
+ Offcanvas.getOrCreateInstance(element).hide();
+ }
+ }
+ });
+ componentFunctions_js.enableDismissTrigger(Offcanvas);
+
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
*/
- defineJQueryPlugin(Offcanvas);
+ index_js.defineJQueryPlugin(Offcanvas);
return Offcanvas;
diff --git a/js/dist/offcanvas.js.map b/js/dist/offcanvas.js.map
index 4b0868d61..d9da097d6 100644
--- a/js/dist/offcanvas.js.map
+++ b/js/dist/offcanvas.js.map
@@ -1 +1 @@
-{"version":3,"file":"offcanvas.js","sources":["../src/util/index.js","../src/util/scrollbar.js","../src/util/backdrop.js","../src/util/focustrap.js","../src/util/component-functions.js","../src/offcanvas.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n// Shoutout AngusCroll (https://goo.gl/pxwQGp)\nconst toType = obj => {\n if (obj === null || obj === undefined) {\n return `${obj}`\n }\n\n return {}.toString.call(obj).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttr = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {\n hrefAttr = `#${hrefAttr.split('#')[1]}`\n }\n\n selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null\n }\n\n return selector\n}\n\nconst getSelectorFromElement = element => {\n const selector = getSelector(element)\n\n if (selector) {\n return document.querySelector(selector) ? selector : null\n }\n\n return null\n}\n\nconst getElementFromSelector = element => {\n const selector = getSelector(element)\n\n return selector ? document.querySelector(selector) : null\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = obj => {\n if (!obj || typeof obj !== 'object') {\n return false\n }\n\n if (typeof obj.jquery !== 'undefined') {\n obj = obj[0]\n }\n\n return typeof obj.nodeType !== 'undefined'\n}\n\nconst getElement = obj => {\n if (isElement(obj)) { // it's a jQuery object or a node element\n return obj.jquery ? obj[0] : obj\n }\n\n if (typeof obj === 'string' && obj.length > 0) {\n return document.querySelector(obj)\n }\n\n return null\n}\n\nconst typeCheckConfig = (componentName, config, configTypes) => {\n Object.keys(configTypes).forEach(property => {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${componentName.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n })\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n return getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n // eslint-disable-next-line no-unused-expressions\n element.offsetHeight\n}\n\nconst getjQuery = () => {\n const { jQuery } = window\n\n if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n DOMContentLoadedCallbacks.forEach(callback => callback())\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = callback => {\n if (typeof callback === 'function') {\n callback()\n }\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element depending on the direction and if cycle is allowed\n if (index === -1) {\n return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]\n }\n\n const listLength = list.length\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n getElement,\n getUID,\n getSelectorFromElement,\n getElementFromSelector,\n getTransitionDurationFromElement,\n triggerTransitionEnd,\n isElement,\n typeCheckConfig,\n isVisible,\n isDisabled,\n findShadowRoot,\n noop,\n getNextActiveElement,\n reflow,\n getjQuery,\n onDOMContentLoaded,\n isRTL,\n defineJQueryPlugin,\n execute,\n executeAfterTransition\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/scrollBar.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport SelectorEngine from '../dom/selector-engine'\nimport Manipulator from '../dom/manipulator'\nimport { isElement } from './index'\n\nconst SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'\nconst SELECTOR_STICKY_CONTENT = '.sticky-top'\n\nclass ScrollBarHelper {\n constructor() {\n this._element = document.body\n }\n\n getWidth() {\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes\n const documentWidth = document.documentElement.clientWidth\n return Math.abs(window.innerWidth - documentWidth)\n }\n\n hide() {\n const width = this.getWidth()\n this._disableOverFlow()\n // give padding to element to balance the hidden scrollbar width\n this._setElementAttributes(this._element, 'paddingRight', calculatedValue => calculatedValue + width)\n // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth\n this._setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width)\n this._setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width)\n }\n\n _disableOverFlow() {\n this._saveInitialAttribute(this._element, 'overflow')\n this._element.style.overflow = 'hidden'\n }\n\n _setElementAttributes(selector, styleProp, callback) {\n const scrollbarWidth = this.getWidth()\n const manipulationCallBack = element => {\n if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {\n return\n }\n\n this._saveInitialAttribute(element, styleProp)\n const calculatedValue = window.getComputedStyle(element)[styleProp]\n element.style[styleProp] = `${callback(Number.parseFloat(calculatedValue))}px`\n }\n\n this._applyManipulationCallback(selector, manipulationCallBack)\n }\n\n reset() {\n this._resetElementAttributes(this._element, 'overflow')\n this._resetElementAttributes(this._element, 'paddingRight')\n this._resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight')\n this._resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight')\n }\n\n _saveInitialAttribute(element, styleProp) {\n const actualValue = element.style[styleProp]\n if (actualValue) {\n Manipulator.setDataAttribute(element, styleProp, actualValue)\n }\n }\n\n _resetElementAttributes(selector, styleProp) {\n const manipulationCallBack = element => {\n const value = Manipulator.getDataAttribute(element, styleProp)\n if (typeof value === 'undefined') {\n element.style.removeProperty(styleProp)\n } else {\n Manipulator.removeDataAttribute(element, styleProp)\n element.style[styleProp] = value\n }\n }\n\n this._applyManipulationCallback(selector, manipulationCallBack)\n }\n\n _applyManipulationCallback(selector, callBack) {\n if (isElement(selector)) {\n callBack(selector)\n } else {\n SelectorEngine.find(selector, this._element).forEach(callBack)\n }\n }\n\n isOverflowing() {\n return this.getWidth() > 0\n }\n}\n\nexport default ScrollBarHelper\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/backdrop.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler'\nimport { execute, executeAfterTransition, getElement, reflow, typeCheckConfig } from './index'\n\nconst Default = {\n className: 'modal-backdrop',\n isVisible: true, // if false, we use the backdrop helper without adding any element to the dom\n isAnimated: false,\n rootElement: 'body', // give the choice to place backdrop under different elements\n clickCallback: null\n}\n\nconst DefaultType = {\n className: 'string',\n isVisible: 'boolean',\n isAnimated: 'boolean',\n rootElement: '(element|string)',\n clickCallback: '(function|null)'\n}\nconst NAME = 'backdrop'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\n\nconst EVENT_MOUSEDOWN = `mousedown.bs.${NAME}`\n\nclass Backdrop {\n constructor(config) {\n this._config = this._getConfig(config)\n this._isAppended = false\n this._element = null\n }\n\n show(callback) {\n if (!this._config.isVisible) {\n execute(callback)\n return\n }\n\n this._append()\n\n if (this._config.isAnimated) {\n reflow(this._getElement())\n }\n\n this._getElement().classList.add(CLASS_NAME_SHOW)\n\n this._emulateAnimation(() => {\n execute(callback)\n })\n }\n\n hide(callback) {\n if (!this._config.isVisible) {\n execute(callback)\n return\n }\n\n this._getElement().classList.remove(CLASS_NAME_SHOW)\n\n this._emulateAnimation(() => {\n this.dispose()\n execute(callback)\n })\n }\n\n // Private\n\n _getElement() {\n if (!this._element) {\n const backdrop = document.createElement('div')\n backdrop.className = this._config.className\n if (this._config.isAnimated) {\n backdrop.classList.add(CLASS_NAME_FADE)\n }\n\n this._element = backdrop\n }\n\n return this._element\n }\n\n _getConfig(config) {\n config = {\n ...Default,\n ...(typeof config === 'object' ? config : {})\n }\n\n // use getElement() with the default \"body\" to get a fresh Element on each instantiation\n config.rootElement = getElement(config.rootElement)\n typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _append() {\n if (this._isAppended) {\n return\n }\n\n this._config.rootElement.append(this._getElement())\n\n EventHandler.on(this._getElement(), EVENT_MOUSEDOWN, () => {\n execute(this._config.clickCallback)\n })\n\n this._isAppended = true\n }\n\n dispose() {\n if (!this._isAppended) {\n return\n }\n\n EventHandler.off(this._element, EVENT_MOUSEDOWN)\n\n this._element.remove()\n this._isAppended = false\n }\n\n _emulateAnimation(callback) {\n executeAfterTransition(callback, this._getElement(), this._config.isAnimated)\n }\n}\n\nexport default Backdrop\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/focustrap.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler'\nimport SelectorEngine from '../dom/selector-engine'\nimport { typeCheckConfig } from './index'\n\nconst Default = {\n trapElement: null, // The element to trap focus inside of\n autofocus: true\n}\n\nconst DefaultType = {\n trapElement: 'element',\n autofocus: 'boolean'\n}\n\nconst NAME = 'focustrap'\nconst DATA_KEY = 'bs.focustrap'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst EVENT_FOCUSIN = `focusin${EVENT_KEY}`\nconst EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY}`\n\nconst TAB_KEY = 'Tab'\nconst TAB_NAV_FORWARD = 'forward'\nconst TAB_NAV_BACKWARD = 'backward'\n\nclass FocusTrap {\n constructor(config) {\n this._config = this._getConfig(config)\n this._isActive = false\n this._lastTabNavDirection = null\n }\n\n activate() {\n const { trapElement, autofocus } = this._config\n\n if (this._isActive) {\n return\n }\n\n if (autofocus) {\n trapElement.focus()\n }\n\n EventHandler.off(document, EVENT_KEY) // guard against infinite focus loop\n EventHandler.on(document, EVENT_FOCUSIN, event => this._handleFocusin(event))\n EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event))\n\n this._isActive = true\n }\n\n deactivate() {\n if (!this._isActive) {\n return\n }\n\n this._isActive = false\n EventHandler.off(document, EVENT_KEY)\n }\n\n // Private\n\n _handleFocusin(event) {\n const { target } = event\n const { trapElement } = this._config\n\n if (target === document || target === trapElement || trapElement.contains(target)) {\n return\n }\n\n const elements = SelectorEngine.focusableChildren(trapElement)\n\n if (elements.length === 0) {\n trapElement.focus()\n } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {\n elements[elements.length - 1].focus()\n } else {\n elements[0].focus()\n }\n }\n\n _handleKeydown(event) {\n if (event.key !== TAB_KEY) {\n return\n }\n\n this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD\n }\n\n _getConfig(config) {\n config = {\n ...Default,\n ...(typeof config === 'object' ? config : {})\n }\n typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n}\n\nexport default FocusTrap\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/component-functions.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler'\nimport { getElementFromSelector, isDisabled } from './index'\n\nconst enableDismissTrigger = (component, method = 'hide') => {\n const clickEvent = `click.dismiss${component.EVENT_KEY}`\n const name = component.NAME\n\n EventHandler.on(document, clickEvent, `[data-bs-dismiss=\"${name}\"]`, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n const target = getElementFromSelector(this) || this.closest(`.${name}`)\n const instance = component.getOrCreateInstance(target)\n\n // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method\n instance[method]()\n })\n}\n\nexport {\n enableDismissTrigger\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): offcanvas.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport {\n defineJQueryPlugin,\n getElementFromSelector,\n isDisabled,\n isVisible,\n typeCheckConfig\n} from './util/index'\nimport ScrollBarHelper from './util/scrollbar'\nimport EventHandler from './dom/event-handler'\nimport BaseComponent from './base-component'\nimport SelectorEngine from './dom/selector-engine'\nimport Manipulator from './dom/manipulator'\nimport Backdrop from './util/backdrop'\nimport FocusTrap from './util/focustrap'\nimport { enableDismissTrigger } from './util/component-functions'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'offcanvas'\nconst DATA_KEY = 'bs.offcanvas'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\nconst ESCAPE_KEY = 'Escape'\n\nconst Default = {\n backdrop: true,\n keyboard: true,\n scroll: false\n}\n\nconst DefaultType = {\n backdrop: 'boolean',\n keyboard: 'boolean',\n scroll: 'boolean'\n}\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_BACKDROP = 'offcanvas-backdrop'\nconst OPEN_SELECTOR = '.offcanvas.show'\n\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`\n\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"offcanvas\"]'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Offcanvas extends BaseComponent {\n constructor(element, config) {\n super(element)\n\n this._config = this._getConfig(config)\n this._isShown = false\n this._backdrop = this._initializeBackDrop()\n this._focustrap = this._initializeFocusTrap()\n this._addEventListeners()\n }\n\n // Getters\n\n static get NAME() {\n return NAME\n }\n\n static get Default() {\n return Default\n }\n\n // Public\n\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget)\n }\n\n show(relatedTarget) {\n if (this._isShown) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, { relatedTarget })\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._isShown = true\n this._element.style.visibility = 'visible'\n\n this._backdrop.show()\n\n if (!this._config.scroll) {\n new ScrollBarHelper().hide()\n }\n\n this._element.removeAttribute('aria-hidden')\n this._element.setAttribute('aria-modal', true)\n this._element.setAttribute('role', 'dialog')\n this._element.classList.add(CLASS_NAME_SHOW)\n\n const completeCallBack = () => {\n if (!this._config.scroll) {\n this._focustrap.activate()\n }\n\n EventHandler.trigger(this._element, EVENT_SHOWN, { relatedTarget })\n }\n\n this._queueCallback(completeCallBack, this._element, true)\n }\n\n hide() {\n if (!this._isShown) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n\n if (hideEvent.defaultPrevented) {\n return\n }\n\n this._focustrap.deactivate()\n this._element.blur()\n this._isShown = false\n this._element.classList.remove(CLASS_NAME_SHOW)\n this._backdrop.hide()\n\n const completeCallback = () => {\n this._element.setAttribute('aria-hidden', true)\n this._element.removeAttribute('aria-modal')\n this._element.removeAttribute('role')\n this._element.style.visibility = 'hidden'\n\n if (!this._config.scroll) {\n new ScrollBarHelper().reset()\n }\n\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n }\n\n this._queueCallback(completeCallback, this._element, true)\n }\n\n dispose() {\n this._backdrop.dispose()\n this._focustrap.deactivate()\n super.dispose()\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...Manipulator.getDataAttributes(this._element),\n ...(typeof config === 'object' ? config : {})\n }\n typeCheckConfig(NAME, config, DefaultType)\n return config\n }\n\n _initializeBackDrop() {\n return new Backdrop({\n className: CLASS_NAME_BACKDROP,\n isVisible: this._config.backdrop,\n isAnimated: true,\n rootElement: this._element.parentNode,\n clickCallback: () => this.hide()\n })\n }\n\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n })\n }\n\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (this._config.keyboard && event.key === ESCAPE_KEY) {\n this.hide()\n }\n })\n }\n\n // Static\n\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Offcanvas.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](this)\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n const target = getElementFromSelector(this)\n\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n EventHandler.one(target, EVENT_HIDDEN, () => {\n // focus on trigger when it is closed\n if (isVisible(this)) {\n this.focus()\n }\n })\n\n // avoid conflict when clicking a toggler of an offcanvas, while another is open\n const allReadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)\n if (allReadyOpen && allReadyOpen !== target) {\n Offcanvas.getInstance(allReadyOpen).hide()\n }\n\n const data = Offcanvas.getOrCreateInstance(target)\n data.toggle(this)\n})\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () =>\n SelectorEngine.find(OPEN_SELECTOR).forEach(el => Offcanvas.getOrCreateInstance(el).show())\n)\n\nenableDismissTrigger(Offcanvas)\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n */\n\ndefineJQueryPlugin(Offcanvas)\n\nexport default Offcanvas\n"],"names":["MILLISECONDS_MULTIPLIER","TRANSITION_END","toType","obj","undefined","toString","call","match","toLowerCase","getSelector","element","selector","getAttribute","hrefAttr","includes","startsWith","split","trim","getElementFromSelector","document","querySelector","getTransitionDurationFromElement","transitionDuration","transitionDelay","window","getComputedStyle","floatTransitionDuration","Number","parseFloat","floatTransitionDelay","triggerTransitionEnd","dispatchEvent","Event","isElement","jquery","nodeType","getElement","length","typeCheckConfig","componentName","config","configTypes","Object","keys","forEach","property","expectedTypes","value","valueType","RegExp","test","TypeError","toUpperCase","isVisible","getClientRects","getPropertyValue","isDisabled","Node","ELEMENT_NODE","classList","contains","disabled","hasAttribute","reflow","offsetHeight","getjQuery","jQuery","body","DOMContentLoadedCallbacks","onDOMContentLoaded","callback","readyState","addEventListener","push","defineJQueryPlugin","plugin","$","name","NAME","JQUERY_NO_CONFLICT","fn","jQueryInterface","Constructor","noConflict","execute","executeAfterTransition","transitionElement","waitForTransition","durationPadding","emulatedDuration","called","handler","target","removeEventListener","setTimeout","SELECTOR_FIXED_CONTENT","SELECTOR_STICKY_CONTENT","ScrollBarHelper","constructor","_element","getWidth","documentWidth","documentElement","clientWidth","Math","abs","innerWidth","hide","width","_disableOverFlow","_setElementAttributes","calculatedValue","_saveInitialAttribute","style","overflow","styleProp","scrollbarWidth","manipulationCallBack","_applyManipulationCallback","reset","_resetElementAttributes","actualValue","Manipulator","setDataAttribute","getDataAttribute","removeProperty","removeDataAttribute","callBack","SelectorEngine","find","isOverflowing","Default","className","isAnimated","rootElement","clickCallback","DefaultType","CLASS_NAME_FADE","CLASS_NAME_SHOW","EVENT_MOUSEDOWN","Backdrop","_config","_getConfig","_isAppended","show","_append","_getElement","add","_emulateAnimation","remove","dispose","backdrop","createElement","append","EventHandler","on","off","trapElement","autofocus","DATA_KEY","EVENT_KEY","EVENT_FOCUSIN","EVENT_KEYDOWN_TAB","TAB_KEY","TAB_NAV_FORWARD","TAB_NAV_BACKWARD","FocusTrap","_isActive","_lastTabNavDirection","activate","focus","event","_handleFocusin","_handleKeydown","deactivate","elements","focusableChildren","key","shiftKey","enableDismissTrigger","component","method","clickEvent","tagName","preventDefault","closest","instance","getOrCreateInstance","DATA_API_KEY","EVENT_LOAD_DATA_API","ESCAPE_KEY","keyboard","scroll","CLASS_NAME_BACKDROP","OPEN_SELECTOR","EVENT_SHOW","EVENT_SHOWN","EVENT_HIDE","EVENT_HIDDEN","EVENT_CLICK_DATA_API","EVENT_KEYDOWN_DISMISS","SELECTOR_DATA_TOGGLE","Offcanvas","BaseComponent","_isShown","_backdrop","_initializeBackDrop","_focustrap","_initializeFocusTrap","_addEventListeners","toggle","relatedTarget","showEvent","trigger","defaultPrevented","visibility","removeAttribute","setAttribute","completeCallBack","_queueCallback","hideEvent","blur","completeCallback","getDataAttributes","parentNode","each","data","one","allReadyOpen","findOne","getInstance","el"],"mappings":"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"} \ No newline at end of file
+{"version":3,"file":"offcanvas.js","sources":["../src/offcanvas.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap offcanvas.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport Backdrop from './util/backdrop.js'\nimport { enableDismissTrigger } from './util/component-functions.js'\nimport FocusTrap from './util/focustrap.js'\nimport {\n defineJQueryPlugin,\n isDisabled,\n isVisible\n} from './util/index.js'\nimport ScrollBarHelper from './util/scrollbar.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'offcanvas'\nconst DATA_KEY = 'bs.offcanvas'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\nconst ESCAPE_KEY = 'Escape'\n\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_SHOWING = 'showing'\nconst CLASS_NAME_HIDING = 'hiding'\nconst CLASS_NAME_BACKDROP = 'offcanvas-backdrop'\nconst OPEN_SELECTOR = '.offcanvas.show'\n\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_RESIZE = `resize${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\nconst EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`\n\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"offcanvas\"]'\n\nconst Default = {\n backdrop: true,\n keyboard: true,\n scroll: false\n}\n\nconst DefaultType = {\n backdrop: '(boolean|string)',\n keyboard: 'boolean',\n scroll: 'boolean'\n}\n\n/**\n * Class definition\n */\n\nclass Offcanvas extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._isShown = false\n this._backdrop = this._initializeBackDrop()\n this._focustrap = this._initializeFocusTrap()\n this._addEventListeners()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n toggle(relatedTarget) {\n return this._isShown ? this.hide() : this.show(relatedTarget)\n }\n\n show(relatedTarget) {\n if (this._isShown) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, { relatedTarget })\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._isShown = true\n this._backdrop.show()\n\n if (!this._config.scroll) {\n new ScrollBarHelper().hide()\n }\n\n this._element.setAttribute('aria-modal', true)\n this._element.setAttribute('role', 'dialog')\n this._element.classList.add(CLASS_NAME_SHOWING)\n\n const completeCallBack = () => {\n if (!this._config.scroll || this._config.backdrop) {\n this._focustrap.activate()\n }\n\n this._element.classList.add(CLASS_NAME_SHOW)\n this._element.classList.remove(CLASS_NAME_SHOWING)\n EventHandler.trigger(this._element, EVENT_SHOWN, { relatedTarget })\n }\n\n this._queueCallback(completeCallBack, this._element, true)\n }\n\n hide() {\n if (!this._isShown) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n\n if (hideEvent.defaultPrevented) {\n return\n }\n\n this._focustrap.deactivate()\n this._element.blur()\n this._isShown = false\n this._element.classList.add(CLASS_NAME_HIDING)\n this._backdrop.hide()\n\n const completeCallback = () => {\n this._element.classList.remove(CLASS_NAME_SHOW, CLASS_NAME_HIDING)\n this._element.removeAttribute('aria-modal')\n this._element.removeAttribute('role')\n\n if (!this._config.scroll) {\n new ScrollBarHelper().reset()\n }\n\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n }\n\n this._queueCallback(completeCallback, this._element, true)\n }\n\n dispose() {\n this._backdrop.dispose()\n this._focustrap.deactivate()\n super.dispose()\n }\n\n // Private\n _initializeBackDrop() {\n const clickCallback = () => {\n if (this._config.backdrop === 'static') {\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)\n return\n }\n\n this.hide()\n }\n\n // 'static' option will be translated to true, and booleans will keep their value\n const isVisible = Boolean(this._config.backdrop)\n\n return new Backdrop({\n className: CLASS_NAME_BACKDROP,\n isVisible,\n isAnimated: true,\n rootElement: this._element.parentNode,\n clickCallback: isVisible ? clickCallback : null\n })\n }\n\n _initializeFocusTrap() {\n return new FocusTrap({\n trapElement: this._element\n })\n }\n\n _addEventListeners() {\n EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {\n if (event.key !== ESCAPE_KEY) {\n return\n }\n\n if (this._config.keyboard) {\n this.hide()\n return\n }\n\n EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)\n })\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Offcanvas.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](this)\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n const target = SelectorEngine.getElementFromSelector(this)\n\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n EventHandler.one(target, EVENT_HIDDEN, () => {\n // focus on trigger when it is closed\n if (isVisible(this)) {\n this.focus()\n }\n })\n\n // avoid conflict when clicking a toggler of an offcanvas, while another is open\n const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)\n if (alreadyOpen && alreadyOpen !== target) {\n Offcanvas.getInstance(alreadyOpen).hide()\n }\n\n const data = Offcanvas.getOrCreateInstance(target)\n data.toggle(this)\n})\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n for (const selector of SelectorEngine.find(OPEN_SELECTOR)) {\n Offcanvas.getOrCreateInstance(selector).show()\n }\n})\n\nEventHandler.on(window, EVENT_RESIZE, () => {\n for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) {\n if (getComputedStyle(element).position !== 'fixed') {\n Offcanvas.getOrCreateInstance(element).hide()\n }\n }\n})\n\nenableDismissTrigger(Offcanvas)\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Offcanvas)\n\nexport default Offcanvas\n"],"names":["NAME","DATA_KEY","EVENT_KEY","DATA_API_KEY","EVENT_LOAD_DATA_API","ESCAPE_KEY","CLASS_NAME_SHOW","CLASS_NAME_SHOWING","CLASS_NAME_HIDING","CLASS_NAME_BACKDROP","OPEN_SELECTOR","EVENT_SHOW","EVENT_SHOWN","EVENT_HIDE","EVENT_HIDE_PREVENTED","EVENT_HIDDEN","EVENT_RESIZE","EVENT_CLICK_DATA_API","EVENT_KEYDOWN_DISMISS","SELECTOR_DATA_TOGGLE","Default","backdrop","keyboard","scroll","DefaultType","Offcanvas","BaseComponent","constructor","element","config","_isShown","_backdrop","_initializeBackDrop","_focustrap","_initializeFocusTrap","_addEventListeners","toggle","relatedTarget","hide","show","showEvent","EventHandler","trigger","_element","defaultPrevented","_config","ScrollBarHelper","setAttribute","classList","add","completeCallBack","activate","remove","_queueCallback","hideEvent","deactivate","blur","completeCallback","removeAttribute","reset","dispose","clickCallback","isVisible","Boolean","Backdrop","className","isAnimated","rootElement","parentNode","FocusTrap","trapElement","on","event","key","jQueryInterface","each","data","getOrCreateInstance","undefined","startsWith","TypeError","document","target","SelectorEngine","getElementFromSelector","includes","tagName","preventDefault","isDisabled","one","focus","alreadyOpen","findOne","getInstance","window","selector","find","getComputedStyle","position","enableDismissTrigger","defineJQueryPlugin"],"mappings":"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"} \ No newline at end of file
diff --git a/js/dist/popover.js b/js/dist/popover.js
index f46a97144..ddfd59f53 100644
--- a/js/dist/popover.js
+++ b/js/dist/popover.js
@@ -1,182 +1,94 @@
/*!
- * Bootstrap popover.js v5.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Bootstrap popover.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./tooltip.js')) :
- typeof define === 'function' && define.amd ? define(['./tooltip'], factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Popover = factory(global.Tooltip));
-})(this, (function (Tooltip) { 'use strict';
-
- const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
-
- const Tooltip__default = /*#__PURE__*/_interopDefaultLegacy(Tooltip);
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./tooltip.js'), require('./util/index.js')) :
+ typeof define === 'function' && define.amd ? define(['./tooltip', './util/index'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Popover = factory(global.Tooltip, global.Index));
+})(this, (function (Tooltip, index_js) { 'use strict';
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/index.js
+ * Bootstrap popover.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
- const getjQuery = () => {
- const {
- jQuery
- } = window;
-
- if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
- return jQuery;
- }
-
- return null;
- };
-
- const DOMContentLoadedCallbacks = [];
-
- const onDOMContentLoaded = callback => {
- if (document.readyState === 'loading') {
- // add listener on the first call when the document is in loading state
- if (!DOMContentLoadedCallbacks.length) {
- document.addEventListener('DOMContentLoaded', () => {
- DOMContentLoadedCallbacks.forEach(callback => callback());
- });
- }
-
- DOMContentLoadedCallbacks.push(callback);
- } else {
- callback();
- }
- };
-
- const defineJQueryPlugin = plugin => {
- onDOMContentLoaded(() => {
- const $ = getjQuery();
- /* istanbul ignore if */
-
- if ($) {
- const name = plugin.NAME;
- const JQUERY_NO_CONFLICT = $.fn[name];
- $.fn[name] = plugin.jQueryInterface;
- $.fn[name].Constructor = plugin;
- $.fn[name].noConflict = () => {
- $.fn[name] = JQUERY_NO_CONFLICT;
- return plugin.jQueryInterface;
- };
- }
- });
- };
-
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): popover.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
/**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'popover';
- const DATA_KEY = 'bs.popover';
- const EVENT_KEY = `.${DATA_KEY}`;
- const CLASS_PREFIX = 'bs-popover';
- const Default = { ...Tooltip__default.default.Default,
- placement: 'right',
- offset: [0, 8],
- trigger: 'click',
+ const SELECTOR_TITLE = '.popover-header';
+ const SELECTOR_CONTENT = '.popover-body';
+ const Default = {
+ ...Tooltip.Default,
content: '',
- template: '<div class="popover" role="tooltip">' + '<div class="popover-arrow"></div>' + '<h3 class="popover-header"></h3>' + '<div class="popover-body"></div>' + '</div>'
- };
- const DefaultType = { ...Tooltip__default.default.DefaultType,
- content: '(string|element|function)'
+ offset: [0, 8],
+ placement: 'right',
+ template: '<div class="popover" role="tooltip">' + '<div class="popover-arrow"></div>' + '<h3 class="popover-header"></h3>' + '<div class="popover-body"></div>' + '</div>',
+ trigger: 'click'
};
- const Event = {
- HIDE: `hide${EVENT_KEY}`,
- HIDDEN: `hidden${EVENT_KEY}`,
- SHOW: `show${EVENT_KEY}`,
- SHOWN: `shown${EVENT_KEY}`,
- INSERTED: `inserted${EVENT_KEY}`,
- CLICK: `click${EVENT_KEY}`,
- FOCUSIN: `focusin${EVENT_KEY}`,
- FOCUSOUT: `focusout${EVENT_KEY}`,
- MOUSEENTER: `mouseenter${EVENT_KEY}`,
- MOUSELEAVE: `mouseleave${EVENT_KEY}`
+ const DefaultType = {
+ ...Tooltip.DefaultType,
+ content: '(null|string|element|function)'
};
- const SELECTOR_TITLE = '.popover-header';
- const SELECTOR_CONTENT = '.popover-body';
+
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
- class Popover extends Tooltip__default.default {
+ class Popover extends Tooltip {
// Getters
static get Default() {
return Default;
}
-
+ static get DefaultType() {
+ return DefaultType;
+ }
static get NAME() {
return NAME;
}
- static get Event() {
- return Event;
+ // Overrides
+ _isWithContent() {
+ return this._getTitle() || this._getContent();
}
- static get DefaultType() {
- return DefaultType;
- } // Overrides
-
-
- isWithContent() {
- return this.getTitle() || this._getContent();
+ // Private
+ _getContentForTemplate() {
+ return {
+ [SELECTOR_TITLE]: this._getTitle(),
+ [SELECTOR_CONTENT]: this._getContent()
+ };
}
-
- setContent(tip) {
- this._sanitizeAndSetContent(tip, this.getTitle(), SELECTOR_TITLE);
-
- this._sanitizeAndSetContent(tip, this._getContent(), SELECTOR_CONTENT);
- } // Private
-
-
_getContent() {
return this._resolvePossibleFunction(this._config.content);
}
- _getBasicClassPrefix() {
- return CLASS_PREFIX;
- } // Static
-
-
+ // Static
static jQueryInterface(config) {
return this.each(function () {
const data = Popover.getOrCreateInstance(this, config);
-
- if (typeof config === 'string') {
- if (typeof data[config] === 'undefined') {
- throw new TypeError(`No method named "${config}"`);
- }
-
- data[config]();
+ if (typeof config !== 'string') {
+ return;
+ }
+ if (typeof data[config] === 'undefined') {
+ throw new TypeError(`No method named "${config}"`);
}
+ data[config]();
});
}
-
}
+
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .Popover to jQuery only if jQuery is present
*/
-
- defineJQueryPlugin(Popover);
+ index_js.defineJQueryPlugin(Popover);
return Popover;
diff --git a/js/dist/popover.js.map b/js/dist/popover.js.map
index 92a573a92..c15baef1c 100644
--- a/js/dist/popover.js.map
+++ b/js/dist/popover.js.map
@@ -1 +1 @@
-{"version":3,"file":"popover.js","sources":["../src/util/index.js","../src/popover.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n// Shoutout AngusCroll (https://goo.gl/pxwQGp)\nconst toType = obj => {\n if (obj === null || obj === undefined) {\n return `${obj}`\n }\n\n return {}.toString.call(obj).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttr = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {\n hrefAttr = `#${hrefAttr.split('#')[1]}`\n }\n\n selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null\n }\n\n return selector\n}\n\nconst getSelectorFromElement = element => {\n const selector = getSelector(element)\n\n if (selector) {\n return document.querySelector(selector) ? selector : null\n }\n\n return null\n}\n\nconst getElementFromSelector = element => {\n const selector = getSelector(element)\n\n return selector ? document.querySelector(selector) : null\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = obj => {\n if (!obj || typeof obj !== 'object') {\n return false\n }\n\n if (typeof obj.jquery !== 'undefined') {\n obj = obj[0]\n }\n\n return typeof obj.nodeType !== 'undefined'\n}\n\nconst getElement = obj => {\n if (isElement(obj)) { // it's a jQuery object or a node element\n return obj.jquery ? obj[0] : obj\n }\n\n if (typeof obj === 'string' && obj.length > 0) {\n return document.querySelector(obj)\n }\n\n return null\n}\n\nconst typeCheckConfig = (componentName, config, configTypes) => {\n Object.keys(configTypes).forEach(property => {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${componentName.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n })\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n return getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n // eslint-disable-next-line no-unused-expressions\n element.offsetHeight\n}\n\nconst getjQuery = () => {\n const { jQuery } = window\n\n if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n DOMContentLoadedCallbacks.forEach(callback => callback())\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = callback => {\n if (typeof callback === 'function') {\n callback()\n }\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element depending on the direction and if cycle is allowed\n if (index === -1) {\n return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]\n }\n\n const listLength = list.length\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n getElement,\n getUID,\n getSelectorFromElement,\n getElementFromSelector,\n getTransitionDurationFromElement,\n triggerTransitionEnd,\n isElement,\n typeCheckConfig,\n isVisible,\n isDisabled,\n findShadowRoot,\n noop,\n getNextActiveElement,\n reflow,\n getjQuery,\n onDOMContentLoaded,\n isRTL,\n defineJQueryPlugin,\n execute,\n executeAfterTransition\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): popover.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport { defineJQueryPlugin } from './util/index'\nimport Tooltip from './tooltip'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'popover'\nconst DATA_KEY = 'bs.popover'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst CLASS_PREFIX = 'bs-popover'\n\nconst Default = {\n ...Tooltip.Default,\n placement: 'right',\n offset: [0, 8],\n trigger: 'click',\n content: '',\n template: '<div class=\"popover\" role=\"tooltip\">' +\n '<div class=\"popover-arrow\"></div>' +\n '<h3 class=\"popover-header\"></h3>' +\n '<div class=\"popover-body\"></div>' +\n '</div>'\n}\n\nconst DefaultType = {\n ...Tooltip.DefaultType,\n content: '(string|element|function)'\n}\n\nconst Event = {\n HIDE: `hide${EVENT_KEY}`,\n HIDDEN: `hidden${EVENT_KEY}`,\n SHOW: `show${EVENT_KEY}`,\n SHOWN: `shown${EVENT_KEY}`,\n INSERTED: `inserted${EVENT_KEY}`,\n CLICK: `click${EVENT_KEY}`,\n FOCUSIN: `focusin${EVENT_KEY}`,\n FOCUSOUT: `focusout${EVENT_KEY}`,\n MOUSEENTER: `mouseenter${EVENT_KEY}`,\n MOUSELEAVE: `mouseleave${EVENT_KEY}`\n}\n\nconst SELECTOR_TITLE = '.popover-header'\nconst SELECTOR_CONTENT = '.popover-body'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Popover extends Tooltip {\n // Getters\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n static get Event() {\n return Event\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n // Overrides\n\n isWithContent() {\n return this.getTitle() || this._getContent()\n }\n\n setContent(tip) {\n this._sanitizeAndSetContent(tip, this.getTitle(), SELECTOR_TITLE)\n this._sanitizeAndSetContent(tip, this._getContent(), SELECTOR_CONTENT)\n }\n\n // Private\n\n _getContent() {\n return this._resolvePossibleFunction(this._config.content)\n }\n\n _getBasicClassPrefix() {\n return CLASS_PREFIX\n }\n\n // Static\n\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Popover.getOrCreateInstance(this, config)\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Popover to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Popover)\n\nexport default Popover\n"],"names":["getjQuery","jQuery","window","document","body","hasAttribute","DOMContentLoadedCallbacks","onDOMContentLoaded","callback","readyState","length","addEventListener","forEach","push","defineJQueryPlugin","plugin","$","name","NAME","JQUERY_NO_CONFLICT","fn","jQueryInterface","Constructor","noConflict","DATA_KEY","EVENT_KEY","CLASS_PREFIX","Default","Tooltip","placement","offset","trigger","content","template","DefaultType","Event","HIDE","HIDDEN","SHOW","SHOWN","INSERTED","CLICK","FOCUSIN","FOCUSOUT","MOUSEENTER","MOUSELEAVE","SELECTOR_TITLE","SELECTOR_CONTENT","Popover","isWithContent","getTitle","_getContent","setContent","tip","_sanitizeAndSetContent","_resolvePossibleFunction","_config","_getBasicClassPrefix","config","each","data","getOrCreateInstance","TypeError"],"mappings":"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"} \ No newline at end of file
+{"version":3,"file":"popover.js","sources":["../src/popover.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap popover.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Tooltip from './tooltip.js'\nimport { defineJQueryPlugin } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'popover'\n\nconst SELECTOR_TITLE = '.popover-header'\nconst SELECTOR_CONTENT = '.popover-body'\n\nconst Default = {\n ...Tooltip.Default,\n content: '',\n offset: [0, 8],\n placement: 'right',\n template: '<div class=\"popover\" role=\"tooltip\">' +\n '<div class=\"popover-arrow\"></div>' +\n '<h3 class=\"popover-header\"></h3>' +\n '<div class=\"popover-body\"></div>' +\n '</div>',\n trigger: 'click'\n}\n\nconst DefaultType = {\n ...Tooltip.DefaultType,\n content: '(null|string|element|function)'\n}\n\n/**\n * Class definition\n */\n\nclass Popover extends Tooltip {\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Overrides\n _isWithContent() {\n return this._getTitle() || this._getContent()\n }\n\n // Private\n _getContentForTemplate() {\n return {\n [SELECTOR_TITLE]: this._getTitle(),\n [SELECTOR_CONTENT]: this._getContent()\n }\n }\n\n _getContent() {\n return this._resolvePossibleFunction(this._config.content)\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Popover.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Popover)\n\nexport default Popover\n"],"names":["NAME","SELECTOR_TITLE","SELECTOR_CONTENT","Default","Tooltip","content","offset","placement","template","trigger","DefaultType","Popover","_isWithContent","_getTitle","_getContent","_getContentForTemplate","_resolvePossibleFunction","_config","jQueryInterface","config","each","data","getOrCreateInstance","TypeError","defineJQueryPlugin"],"mappings":";;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;;;EAKA;EACA;EACA;;EAEA,MAAMA,IAAI,GAAG,SAAS,CAAA;EAEtB,MAAMC,cAAc,GAAG,iBAAiB,CAAA;EACxC,MAAMC,gBAAgB,GAAG,eAAe,CAAA;EAExC,MAAMC,OAAO,GAAG;IACd,GAAGC,OAAO,CAACD,OAAO;EAClBE,EAAAA,OAAO,EAAE,EAAE;EACXC,EAAAA,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;EACdC,EAAAA,SAAS,EAAE,OAAO;IAClBC,QAAQ,EAAE,sCAAsC,GAC9C,mCAAmC,GACnC,kCAAkC,GAClC,kCAAkC,GAClC,QAAQ;EACVC,EAAAA,OAAO,EAAE,OAAA;EACX,CAAC,CAAA;EAED,MAAMC,WAAW,GAAG;IAClB,GAAGN,OAAO,CAACM,WAAW;EACtBL,EAAAA,OAAO,EAAE,gCAAA;EACX,CAAC,CAAA;;EAED;EACA;EACA;;EAEA,MAAMM,OAAO,SAASP,OAAO,CAAC;EAC5B;IACA,WAAWD,OAAOA,GAAG;EACnB,IAAA,OAAOA,OAAO,CAAA;EAChB,GAAA;IAEA,WAAWO,WAAWA,GAAG;EACvB,IAAA,OAAOA,WAAW,CAAA;EACpB,GAAA;IAEA,WAAWV,IAAIA,GAAG;EAChB,IAAA,OAAOA,IAAI,CAAA;EACb,GAAA;;EAEA;EACAY,EAAAA,cAAcA,GAAG;MACf,OAAO,IAAI,CAACC,SAAS,EAAE,IAAI,IAAI,CAACC,WAAW,EAAE,CAAA;EAC/C,GAAA;;EAEA;EACAC,EAAAA,sBAAsBA,GAAG;MACvB,OAAO;EACL,MAAA,CAACd,cAAc,GAAG,IAAI,CAACY,SAAS,EAAE;EAClC,MAAA,CAACX,gBAAgB,GAAG,IAAI,CAACY,WAAW,EAAC;OACtC,CAAA;EACH,GAAA;EAEAA,EAAAA,WAAWA,GAAG;MACZ,OAAO,IAAI,CAACE,wBAAwB,CAAC,IAAI,CAACC,OAAO,CAACZ,OAAO,CAAC,CAAA;EAC5D,GAAA;;EAEA;IACA,OAAOa,eAAeA,CAACC,MAAM,EAAE;EAC7B,IAAA,OAAO,IAAI,CAACC,IAAI,CAAC,YAAY;QAC3B,MAAMC,IAAI,GAAGV,OAAO,CAACW,mBAAmB,CAAC,IAAI,EAAEH,MAAM,CAAC,CAAA;EAEtD,MAAA,IAAI,OAAOA,MAAM,KAAK,QAAQ,EAAE;EAC9B,QAAA,OAAA;EACF,OAAA;EAEA,MAAA,IAAI,OAAOE,IAAI,CAACF,MAAM,CAAC,KAAK,WAAW,EAAE;EACvC,QAAA,MAAM,IAAII,SAAS,CAAE,CAAmBJ,iBAAAA,EAAAA,MAAO,GAAE,CAAC,CAAA;EACpD,OAAA;EAEAE,MAAAA,IAAI,CAACF,MAAM,CAAC,EAAE,CAAA;EAChB,KAAC,CAAC,CAAA;EACJ,GAAA;EACF,CAAA;;EAEA;EACA;EACA;;AAEAK,6BAAkB,CAACb,OAAO,CAAC;;;;;;;;"} \ No newline at end of file
diff --git a/js/dist/scrollspy.js b/js/dist/scrollspy.js
index 261c2658a..057ec71a6 100644
--- a/js/dist/scrollspy.js
+++ b/js/dist/scrollspy.js
@@ -1,389 +1,273 @@
/*!
- * Bootstrap scrollspy.js v5.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Bootstrap scrollspy.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./base-component.js')) :
- typeof define === 'function' && define.amd ? define(['./dom/event-handler', './dom/manipulator', './dom/selector-engine', './base-component'], factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.ScrollSpy = factory(global.EventHandler, global.Manipulator, global.SelectorEngine, global.Base));
-})(this, (function (EventHandler, Manipulator, SelectorEngine, BaseComponent) { 'use strict';
-
- const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
-
- const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
- const Manipulator__default = /*#__PURE__*/_interopDefaultLegacy(Manipulator);
- const SelectorEngine__default = /*#__PURE__*/_interopDefaultLegacy(SelectorEngine);
- const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/selector-engine.js'), require('./util/index.js')) :
+ typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './dom/selector-engine', './util/index'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Scrollspy = factory(global.BaseComponent, global.EventHandler, global.SelectorEngine, global.Index));
+})(this, (function (BaseComponent, EventHandler, SelectorEngine, index_js) { 'use strict';
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/index.js
+ * Bootstrap scrollspy.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
- const toType = obj => {
- if (obj === null || obj === undefined) {
- return `${obj}`;
- }
-
- return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase();
- };
-
- const getSelector = element => {
- let selector = element.getAttribute('data-bs-target');
-
- if (!selector || selector === '#') {
- let hrefAttr = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes,
- // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
- // `document.querySelector` will rightfully complain it is invalid.
- // See https://github.com/twbs/bootstrap/issues/32273
-
- if (!hrefAttr || !hrefAttr.includes('#') && !hrefAttr.startsWith('.')) {
- return null;
- } // Just in case some CMS puts out a full URL with the anchor appended
-
-
- if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {
- hrefAttr = `#${hrefAttr.split('#')[1]}`;
- }
-
- selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null;
- }
-
- return selector;
- };
-
- const getSelectorFromElement = element => {
- const selector = getSelector(element);
-
- if (selector) {
- return document.querySelector(selector) ? selector : null;
- }
-
- return null;
- };
-
- const isElement = obj => {
- if (!obj || typeof obj !== 'object') {
- return false;
- }
-
- if (typeof obj.jquery !== 'undefined') {
- obj = obj[0];
- }
-
- return typeof obj.nodeType !== 'undefined';
- };
-
- const getElement = obj => {
- if (isElement(obj)) {
- // it's a jQuery object or a node element
- return obj.jquery ? obj[0] : obj;
- }
-
- if (typeof obj === 'string' && obj.length > 0) {
- return document.querySelector(obj);
- }
-
- return null;
- };
-
- const typeCheckConfig = (componentName, config, configTypes) => {
- Object.keys(configTypes).forEach(property => {
- const expectedTypes = configTypes[property];
- const value = config[property];
- const valueType = value && isElement(value) ? 'element' : toType(value);
-
- if (!new RegExp(expectedTypes).test(valueType)) {
- throw new TypeError(`${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`);
- }
- });
- };
-
- const getjQuery = () => {
- const {
- jQuery
- } = window;
-
- if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
- return jQuery;
- }
-
- return null;
- };
-
- const DOMContentLoadedCallbacks = [];
-
- const onDOMContentLoaded = callback => {
- if (document.readyState === 'loading') {
- // add listener on the first call when the document is in loading state
- if (!DOMContentLoadedCallbacks.length) {
- document.addEventListener('DOMContentLoaded', () => {
- DOMContentLoadedCallbacks.forEach(callback => callback());
- });
- }
-
- DOMContentLoadedCallbacks.push(callback);
- } else {
- callback();
- }
- };
-
- const defineJQueryPlugin = plugin => {
- onDOMContentLoaded(() => {
- const $ = getjQuery();
- /* istanbul ignore if */
-
- if ($) {
- const name = plugin.NAME;
- const JQUERY_NO_CONFLICT = $.fn[name];
- $.fn[name] = plugin.jQueryInterface;
- $.fn[name].Constructor = plugin;
-
- $.fn[name].noConflict = () => {
- $.fn[name] = JQUERY_NO_CONFLICT;
- return plugin.jQueryInterface;
- };
- }
- });
- };
/**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): scrollspy.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- /**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'scrollspy';
const DATA_KEY = 'bs.scrollspy';
const EVENT_KEY = `.${DATA_KEY}`;
const DATA_API_KEY = '.data-api';
- const Default = {
- offset: 10,
- method: 'auto',
- target: ''
- };
- const DefaultType = {
- offset: 'number',
- method: 'string',
- target: '(string|element)'
- };
const EVENT_ACTIVATE = `activate${EVENT_KEY}`;
- const EVENT_SCROLL = `scroll${EVENT_KEY}`;
+ const EVENT_CLICK = `click${EVENT_KEY}`;
const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`;
const CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item';
const CLASS_NAME_ACTIVE = 'active';
const SELECTOR_DATA_SPY = '[data-bs-spy="scroll"]';
+ const SELECTOR_TARGET_LINKS = '[href]';
const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group';
const SELECTOR_NAV_LINKS = '.nav-link';
const SELECTOR_NAV_ITEMS = '.nav-item';
const SELECTOR_LIST_ITEMS = '.list-group-item';
- const SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}, .${CLASS_NAME_DROPDOWN_ITEM}`;
+ const SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`;
const SELECTOR_DROPDOWN = '.dropdown';
const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle';
- const METHOD_OFFSET = 'offset';
- const METHOD_POSITION = 'position';
+ const Default = {
+ offset: null,
+ // TODO: v6 @deprecated, keep it for backwards compatibility reasons
+ rootMargin: '0px 0px -25%',
+ smoothScroll: false,
+ target: null,
+ threshold: [0.1, 0.5, 1]
+ };
+ const DefaultType = {
+ offset: '(number|null)',
+ // TODO v6 @deprecated, keep it for backwards compatibility reasons
+ rootMargin: 'string',
+ smoothScroll: 'boolean',
+ target: 'element',
+ threshold: 'array'
+ };
+
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
- class ScrollSpy extends BaseComponent__default.default {
+ class ScrollSpy extends BaseComponent {
constructor(element, config) {
- super(element);
- this._scrollElement = this._element.tagName === 'BODY' ? window : this._element;
- this._config = this._getConfig(config);
- this._offsets = [];
- this._targets = [];
- this._activeTarget = null;
- this._scrollHeight = 0;
- EventHandler__default.default.on(this._scrollElement, EVENT_SCROLL, () => this._process());
- this.refresh();
-
- this._process();
- } // Getters
+ super(element, config);
+ // this._element is the observablesContainer and config.target the menu links wrapper
+ this._targetLinks = new Map();
+ this._observableSections = new Map();
+ this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element;
+ this._activeTarget = null;
+ this._observer = null;
+ this._previousScrollData = {
+ visibleEntryTop: 0,
+ parentScrollTop: 0
+ };
+ this.refresh(); // initialize
+ }
+ // Getters
static get Default() {
return Default;
}
-
+ static get DefaultType() {
+ return DefaultType;
+ }
static get NAME() {
return NAME;
- } // Public
-
+ }
+ // Public
refresh() {
- const autoMethod = this._scrollElement === this._scrollElement.window ? METHOD_OFFSET : METHOD_POSITION;
- const offsetMethod = this._config.method === 'auto' ? autoMethod : this._config.method;
- const offsetBase = offsetMethod === METHOD_POSITION ? this._getScrollTop() : 0;
- this._offsets = [];
- this._targets = [];
- this._scrollHeight = this._getScrollHeight();
- const targets = SelectorEngine__default.default.find(SELECTOR_LINK_ITEMS, this._config.target);
- targets.map(element => {
- const targetSelector = getSelectorFromElement(element);
- const target = targetSelector ? SelectorEngine__default.default.findOne(targetSelector) : null;
-
- if (target) {
- const targetBCR = target.getBoundingClientRect();
-
- if (targetBCR.width || targetBCR.height) {
- return [Manipulator__default.default[offsetMethod](target).top + offsetBase, targetSelector];
- }
- }
-
- return null;
- }).filter(item => item).sort((a, b) => a[0] - b[0]).forEach(item => {
- this._offsets.push(item[0]);
-
- this._targets.push(item[1]);
- });
+ this._initializeTargetsAndObservables();
+ this._maybeEnableSmoothScroll();
+ if (this._observer) {
+ this._observer.disconnect();
+ } else {
+ this._observer = this._getNewObserver();
+ }
+ for (const section of this._observableSections.values()) {
+ this._observer.observe(section);
+ }
}
-
dispose() {
- EventHandler__default.default.off(this._scrollElement, EVENT_KEY);
+ this._observer.disconnect();
super.dispose();
- } // Private
+ }
+ // Private
+ _configAfterMerge(config) {
+ // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case
+ config.target = index_js.getElement(config.target) || document.body;
- _getConfig(config) {
- config = { ...Default,
- ...Manipulator__default.default.getDataAttributes(this._element),
- ...(typeof config === 'object' && config ? config : {})
- };
- config.target = getElement(config.target) || document.documentElement;
- typeCheckConfig(NAME, config, DefaultType);
+ // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only
+ config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin;
+ if (typeof config.threshold === 'string') {
+ config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value));
+ }
return config;
}
+ _maybeEnableSmoothScroll() {
+ if (!this._config.smoothScroll) {
+ return;
+ }
- _getScrollTop() {
- return this._scrollElement === window ? this._scrollElement.pageYOffset : this._scrollElement.scrollTop;
- }
+ // unregister any previous listeners
+ EventHandler.off(this._config.target, EVENT_CLICK);
+ EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => {
+ const observableSection = this._observableSections.get(event.target.hash);
+ if (observableSection) {
+ event.preventDefault();
+ const root = this._rootElement || window;
+ const height = observableSection.offsetTop - this._element.offsetTop;
+ if (root.scrollTo) {
+ root.scrollTo({
+ top: height,
+ behavior: 'smooth'
+ });
+ return;
+ }
- _getScrollHeight() {
- return this._scrollElement.scrollHeight || Math.max(document.body.scrollHeight, document.documentElement.scrollHeight);
+ // Chrome 60 doesn't support `scrollTo`
+ root.scrollTop = height;
+ }
+ });
}
-
- _getOffsetHeight() {
- return this._scrollElement === window ? window.innerHeight : this._scrollElement.getBoundingClientRect().height;
+ _getNewObserver() {
+ const options = {
+ root: this._rootElement,
+ threshold: this._config.threshold,
+ rootMargin: this._config.rootMargin
+ };
+ return new IntersectionObserver(entries => this._observerCallback(entries), options);
}
- _process() {
- const scrollTop = this._getScrollTop() + this._config.offset;
-
- const scrollHeight = this._getScrollHeight();
-
- const maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight();
+ // The logic of selection
+ _observerCallback(entries) {
+ const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`);
+ const activate = entry => {
+ this._previousScrollData.visibleEntryTop = entry.target.offsetTop;
+ this._process(targetElement(entry));
+ };
+ const parentScrollTop = (this._rootElement || document.documentElement).scrollTop;
+ const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop;
+ this._previousScrollData.parentScrollTop = parentScrollTop;
+ for (const entry of entries) {
+ if (!entry.isIntersecting) {
+ this._activeTarget = null;
+ this._clearActiveClass(targetElement(entry));
+ continue;
+ }
+ const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop;
+ // if we are scrolling down, pick the bigger offsetTop
+ if (userScrollsDown && entryIsLowerThanPrevious) {
+ activate(entry);
+ // if parent isn't scrolled, let's keep the first visible item, breaking the iteration
+ if (!parentScrollTop) {
+ return;
+ }
+ continue;
+ }
- if (this._scrollHeight !== scrollHeight) {
- this.refresh();
+ // if we are scrolling up, pick the smallest offsetTop
+ if (!userScrollsDown && !entryIsLowerThanPrevious) {
+ activate(entry);
+ }
}
-
- if (scrollTop >= maxScroll) {
- const target = this._targets[this._targets.length - 1];
-
- if (this._activeTarget !== target) {
- this._activate(target);
+ }
+ _initializeTargetsAndObservables() {
+ this._targetLinks = new Map();
+ this._observableSections = new Map();
+ const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target);
+ for (const anchor of targetLinks) {
+ // ensure that the anchor has an id and is not disabled
+ if (!anchor.hash || index_js.isDisabled(anchor)) {
+ continue;
}
+ const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element);
+ // ensure that the observableSection exists & is visible
+ if (index_js.isVisible(observableSection)) {
+ this._targetLinks.set(decodeURI(anchor.hash), anchor);
+ this._observableSections.set(anchor.hash, observableSection);
+ }
+ }
+ }
+ _process(target) {
+ if (this._activeTarget === target) {
return;
}
-
- if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {
- this._activeTarget = null;
-
- this._clear();
-
+ this._clearActiveClass(this._config.target);
+ this._activeTarget = target;
+ target.classList.add(CLASS_NAME_ACTIVE);
+ this._activateParents(target);
+ EventHandler.trigger(this._element, EVENT_ACTIVATE, {
+ relatedTarget: target
+ });
+ }
+ _activateParents(target) {
+ // Activate dropdown parents
+ if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {
+ SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE, target.closest(SELECTOR_DROPDOWN)).classList.add(CLASS_NAME_ACTIVE);
return;
}
-
- for (let i = this._offsets.length; i--;) {
- const isActiveTarget = this._activeTarget !== this._targets[i] && scrollTop >= this._offsets[i] && (typeof this._offsets[i + 1] === 'undefined' || scrollTop < this._offsets[i + 1]);
-
- if (isActiveTarget) {
- this._activate(this._targets[i]);
+ for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) {
+ // Set triggered links parents as active
+ // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor
+ for (const item of SelectorEngine.prev(listGroup, SELECTOR_LINK_ITEMS)) {
+ item.classList.add(CLASS_NAME_ACTIVE);
}
}
}
-
- _activate(target) {
- this._activeTarget = target;
-
- this._clear();
-
- const queries = SELECTOR_LINK_ITEMS.split(',').map(selector => `${selector}[data-bs-target="${target}"],${selector}[href="${target}"]`);
- const link = SelectorEngine__default.default.findOne(queries.join(','), this._config.target);
- link.classList.add(CLASS_NAME_ACTIVE);
-
- if (link.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {
- SelectorEngine__default.default.findOne(SELECTOR_DROPDOWN_TOGGLE, link.closest(SELECTOR_DROPDOWN)).classList.add(CLASS_NAME_ACTIVE);
- } else {
- SelectorEngine__default.default.parents(link, SELECTOR_NAV_LIST_GROUP).forEach(listGroup => {
- // Set triggered links parents as active
- // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor
- SelectorEngine__default.default.prev(listGroup, `${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`).forEach(item => item.classList.add(CLASS_NAME_ACTIVE)); // Handle special case when .nav-link is inside .nav-item
-
- SelectorEngine__default.default.prev(listGroup, SELECTOR_NAV_ITEMS).forEach(navItem => {
- SelectorEngine__default.default.children(navItem, SELECTOR_NAV_LINKS).forEach(item => item.classList.add(CLASS_NAME_ACTIVE));
- });
- });
+ _clearActiveClass(parent) {
+ parent.classList.remove(CLASS_NAME_ACTIVE);
+ const activeNodes = SelectorEngine.find(`${SELECTOR_TARGET_LINKS}.${CLASS_NAME_ACTIVE}`, parent);
+ for (const node of activeNodes) {
+ node.classList.remove(CLASS_NAME_ACTIVE);
}
-
- EventHandler__default.default.trigger(this._scrollElement, EVENT_ACTIVATE, {
- relatedTarget: target
- });
}
- _clear() {
- SelectorEngine__default.default.find(SELECTOR_LINK_ITEMS, this._config.target).filter(node => node.classList.contains(CLASS_NAME_ACTIVE)).forEach(node => node.classList.remove(CLASS_NAME_ACTIVE));
- } // Static
-
-
+ // Static
static jQueryInterface(config) {
return this.each(function () {
const data = ScrollSpy.getOrCreateInstance(this, config);
-
if (typeof config !== 'string') {
return;
}
-
- if (typeof data[config] === 'undefined') {
+ if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
throw new TypeError(`No method named "${config}"`);
}
-
data[config]();
});
}
-
}
+
/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
+ * Data API implementation
*/
-
- EventHandler__default.default.on(window, EVENT_LOAD_DATA_API, () => {
- SelectorEngine__default.default.find(SELECTOR_DATA_SPY).forEach(spy => new ScrollSpy(spy));
+ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
+ for (const spy of SelectorEngine.find(SELECTOR_DATA_SPY)) {
+ ScrollSpy.getOrCreateInstance(spy);
+ }
});
+
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .ScrollSpy to jQuery only if jQuery is present
*/
- defineJQueryPlugin(ScrollSpy);
+ index_js.defineJQueryPlugin(ScrollSpy);
return ScrollSpy;
diff --git a/js/dist/scrollspy.js.map b/js/dist/scrollspy.js.map
index 7e2fa07f7..c732f587d 100644
--- a/js/dist/scrollspy.js.map
+++ b/js/dist/scrollspy.js.map
@@ -1 +1 @@
-{"version":3,"file":"scrollspy.js","sources":["../src/util/index.js","../src/scrollspy.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n// Shoutout AngusCroll (https://goo.gl/pxwQGp)\nconst toType = obj => {\n if (obj === null || obj === undefined) {\n return `${obj}`\n }\n\n return {}.toString.call(obj).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttr = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {\n hrefAttr = `#${hrefAttr.split('#')[1]}`\n }\n\n selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null\n }\n\n return selector\n}\n\nconst getSelectorFromElement = element => {\n const selector = getSelector(element)\n\n if (selector) {\n return document.querySelector(selector) ? selector : null\n }\n\n return null\n}\n\nconst getElementFromSelector = element => {\n const selector = getSelector(element)\n\n return selector ? document.querySelector(selector) : null\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = obj => {\n if (!obj || typeof obj !== 'object') {\n return false\n }\n\n if (typeof obj.jquery !== 'undefined') {\n obj = obj[0]\n }\n\n return typeof obj.nodeType !== 'undefined'\n}\n\nconst getElement = obj => {\n if (isElement(obj)) { // it's a jQuery object or a node element\n return obj.jquery ? obj[0] : obj\n }\n\n if (typeof obj === 'string' && obj.length > 0) {\n return document.querySelector(obj)\n }\n\n return null\n}\n\nconst typeCheckConfig = (componentName, config, configTypes) => {\n Object.keys(configTypes).forEach(property => {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${componentName.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n })\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n return getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n // eslint-disable-next-line no-unused-expressions\n element.offsetHeight\n}\n\nconst getjQuery = () => {\n const { jQuery } = window\n\n if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n DOMContentLoadedCallbacks.forEach(callback => callback())\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = callback => {\n if (typeof callback === 'function') {\n callback()\n }\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element depending on the direction and if cycle is allowed\n if (index === -1) {\n return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]\n }\n\n const listLength = list.length\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n getElement,\n getUID,\n getSelectorFromElement,\n getElementFromSelector,\n getTransitionDurationFromElement,\n triggerTransitionEnd,\n isElement,\n typeCheckConfig,\n isVisible,\n isDisabled,\n findShadowRoot,\n noop,\n getNextActiveElement,\n reflow,\n getjQuery,\n onDOMContentLoaded,\n isRTL,\n defineJQueryPlugin,\n execute,\n executeAfterTransition\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): scrollspy.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport {\n defineJQueryPlugin,\n getElement,\n getSelectorFromElement,\n typeCheckConfig\n} from './util/index'\nimport EventHandler from './dom/event-handler'\nimport Manipulator from './dom/manipulator'\nimport SelectorEngine from './dom/selector-engine'\nimport BaseComponent from './base-component'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'scrollspy'\nconst DATA_KEY = 'bs.scrollspy'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst Default = {\n offset: 10,\n method: 'auto',\n target: ''\n}\n\nconst DefaultType = {\n offset: 'number',\n method: 'string',\n target: '(string|element)'\n}\n\nconst EVENT_ACTIVATE = `activate${EVENT_KEY}`\nconst EVENT_SCROLL = `scroll${EVENT_KEY}`\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item'\nconst CLASS_NAME_ACTIVE = 'active'\n\nconst SELECTOR_DATA_SPY = '[data-bs-spy=\"scroll\"]'\nconst SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'\nconst SELECTOR_NAV_LINKS = '.nav-link'\nconst SELECTOR_NAV_ITEMS = '.nav-item'\nconst SELECTOR_LIST_ITEMS = '.list-group-item'\nconst SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}, .${CLASS_NAME_DROPDOWN_ITEM}`\nconst SELECTOR_DROPDOWN = '.dropdown'\nconst SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'\n\nconst METHOD_OFFSET = 'offset'\nconst METHOD_POSITION = 'position'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass ScrollSpy extends BaseComponent {\n constructor(element, config) {\n super(element)\n this._scrollElement = this._element.tagName === 'BODY' ? window : this._element\n this._config = this._getConfig(config)\n this._offsets = []\n this._targets = []\n this._activeTarget = null\n this._scrollHeight = 0\n\n EventHandler.on(this._scrollElement, EVENT_SCROLL, () => this._process())\n\n this.refresh()\n this._process()\n }\n\n // Getters\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\n refresh() {\n const autoMethod = this._scrollElement === this._scrollElement.window ?\n METHOD_OFFSET :\n METHOD_POSITION\n\n const offsetMethod = this._config.method === 'auto' ?\n autoMethod :\n this._config.method\n\n const offsetBase = offsetMethod === METHOD_POSITION ?\n this._getScrollTop() :\n 0\n\n this._offsets = []\n this._targets = []\n this._scrollHeight = this._getScrollHeight()\n\n const targets = SelectorEngine.find(SELECTOR_LINK_ITEMS, this._config.target)\n\n targets.map(element => {\n const targetSelector = getSelectorFromElement(element)\n const target = targetSelector ? SelectorEngine.findOne(targetSelector) : null\n\n if (target) {\n const targetBCR = target.getBoundingClientRect()\n if (targetBCR.width || targetBCR.height) {\n return [\n Manipulator[offsetMethod](target).top + offsetBase,\n targetSelector\n ]\n }\n }\n\n return null\n })\n .filter(item => item)\n .sort((a, b) => a[0] - b[0])\n .forEach(item => {\n this._offsets.push(item[0])\n this._targets.push(item[1])\n })\n }\n\n dispose() {\n EventHandler.off(this._scrollElement, EVENT_KEY)\n super.dispose()\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...Manipulator.getDataAttributes(this._element),\n ...(typeof config === 'object' && config ? config : {})\n }\n\n config.target = getElement(config.target) || document.documentElement\n\n typeCheckConfig(NAME, config, DefaultType)\n\n return config\n }\n\n _getScrollTop() {\n return this._scrollElement === window ?\n this._scrollElement.pageYOffset :\n this._scrollElement.scrollTop\n }\n\n _getScrollHeight() {\n return this._scrollElement.scrollHeight || Math.max(\n document.body.scrollHeight,\n document.documentElement.scrollHeight\n )\n }\n\n _getOffsetHeight() {\n return this._scrollElement === window ?\n window.innerHeight :\n this._scrollElement.getBoundingClientRect().height\n }\n\n _process() {\n const scrollTop = this._getScrollTop() + this._config.offset\n const scrollHeight = this._getScrollHeight()\n const maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight()\n\n if (this._scrollHeight !== scrollHeight) {\n this.refresh()\n }\n\n if (scrollTop >= maxScroll) {\n const target = this._targets[this._targets.length - 1]\n\n if (this._activeTarget !== target) {\n this._activate(target)\n }\n\n return\n }\n\n if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {\n this._activeTarget = null\n this._clear()\n return\n }\n\n for (let i = this._offsets.length; i--;) {\n const isActiveTarget = this._activeTarget !== this._targets[i] &&\n scrollTop >= this._offsets[i] &&\n (typeof this._offsets[i + 1] === 'undefined' || scrollTop < this._offsets[i + 1])\n\n if (isActiveTarget) {\n this._activate(this._targets[i])\n }\n }\n }\n\n _activate(target) {\n this._activeTarget = target\n\n this._clear()\n\n const queries = SELECTOR_LINK_ITEMS.split(',')\n .map(selector => `${selector}[data-bs-target=\"${target}\"],${selector}[href=\"${target}\"]`)\n\n const link = SelectorEngine.findOne(queries.join(','), this._config.target)\n\n link.classList.add(CLASS_NAME_ACTIVE)\n if (link.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {\n SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE, link.closest(SELECTOR_DROPDOWN))\n .classList.add(CLASS_NAME_ACTIVE)\n } else {\n SelectorEngine.parents(link, SELECTOR_NAV_LIST_GROUP)\n .forEach(listGroup => {\n // Set triggered links parents as active\n // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor\n SelectorEngine.prev(listGroup, `${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`)\n .forEach(item => item.classList.add(CLASS_NAME_ACTIVE))\n\n // Handle special case when .nav-link is inside .nav-item\n SelectorEngine.prev(listGroup, SELECTOR_NAV_ITEMS)\n .forEach(navItem => {\n SelectorEngine.children(navItem, SELECTOR_NAV_LINKS)\n .forEach(item => item.classList.add(CLASS_NAME_ACTIVE))\n })\n })\n }\n\n EventHandler.trigger(this._scrollElement, EVENT_ACTIVATE, {\n relatedTarget: target\n })\n }\n\n _clear() {\n SelectorEngine.find(SELECTOR_LINK_ITEMS, this._config.target)\n .filter(node => node.classList.contains(CLASS_NAME_ACTIVE))\n .forEach(node => node.classList.remove(CLASS_NAME_ACTIVE))\n }\n\n // Static\n\n static jQueryInterface(config) {\n return this.each(function () {\n const data = ScrollSpy.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n SelectorEngine.find(SELECTOR_DATA_SPY)\n .forEach(spy => new ScrollSpy(spy))\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .ScrollSpy to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(ScrollSpy)\n\nexport default ScrollSpy\n"],"names":["toType","obj","undefined","toString","call","match","toLowerCase","getSelector","element","selector","getAttribute","hrefAttr","includes","startsWith","split","trim","getSelectorFromElement","document","querySelector","isElement","jquery","nodeType","getElement","length","typeCheckConfig","componentName","config","configTypes","Object","keys","forEach","property","expectedTypes","value","valueType","RegExp","test","TypeError","toUpperCase","getjQuery","jQuery","window","body","hasAttribute","DOMContentLoadedCallbacks","onDOMContentLoaded","callback","readyState","addEventListener","push","defineJQueryPlugin","plugin","$","name","NAME","JQUERY_NO_CONFLICT","fn","jQueryInterface","Constructor","noConflict","DATA_KEY","EVENT_KEY","DATA_API_KEY","Default","offset","method","target","DefaultType","EVENT_ACTIVATE","EVENT_SCROLL","EVENT_LOAD_DATA_API","CLASS_NAME_DROPDOWN_ITEM","CLASS_NAME_ACTIVE","SELECTOR_DATA_SPY","SELECTOR_NAV_LIST_GROUP","SELECTOR_NAV_LINKS","SELECTOR_NAV_ITEMS","SELECTOR_LIST_ITEMS","SELECTOR_LINK_ITEMS","SELECTOR_DROPDOWN","SELECTOR_DROPDOWN_TOGGLE","METHOD_OFFSET","METHOD_POSITION","ScrollSpy","BaseComponent","constructor","_scrollElement","_element","tagName","_config","_getConfig","_offsets","_targets","_activeTarget","_scrollHeight","EventHandler","on","_process","refresh","autoMethod","offsetMethod","offsetBase","_getScrollTop","_getScrollHeight","targets","SelectorEngine","find","map","targetSelector","findOne","targetBCR","getBoundingClientRect","width","height","Manipulator","top","filter","item","sort","a","b","dispose","off","getDataAttributes","documentElement","pageYOffset","scrollTop","scrollHeight","Math","max","_getOffsetHeight","innerHeight","maxScroll","_activate","_clear","i","isActiveTarget","queries","link","join","classList","add","contains","closest","parents","listGroup","prev","navItem","children","trigger","relatedTarget","node","remove","each","data","getOrCreateInstance","spy"],"mappings":"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"} \ No newline at end of file
+{"version":3,"file":"scrollspy.js","sources":["../src/scrollspy.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap scrollspy.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport {\n defineJQueryPlugin, getElement, isDisabled, isVisible\n} from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'scrollspy'\nconst DATA_KEY = 'bs.scrollspy'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst EVENT_ACTIVATE = `activate${EVENT_KEY}`\nconst EVENT_CLICK = `click${EVENT_KEY}`\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item'\nconst CLASS_NAME_ACTIVE = 'active'\n\nconst SELECTOR_DATA_SPY = '[data-bs-spy=\"scroll\"]'\nconst SELECTOR_TARGET_LINKS = '[href]'\nconst SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'\nconst SELECTOR_NAV_LINKS = '.nav-link'\nconst SELECTOR_NAV_ITEMS = '.nav-item'\nconst SELECTOR_LIST_ITEMS = '.list-group-item'\nconst SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`\nconst SELECTOR_DROPDOWN = '.dropdown'\nconst SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'\n\nconst Default = {\n offset: null, // TODO: v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: '0px 0px -25%',\n smoothScroll: false,\n target: null,\n threshold: [0.1, 0.5, 1]\n}\n\nconst DefaultType = {\n offset: '(number|null)', // TODO v6 @deprecated, keep it for backwards compatibility reasons\n rootMargin: 'string',\n smoothScroll: 'boolean',\n target: 'element',\n threshold: 'array'\n}\n\n/**\n * Class definition\n */\n\nclass ScrollSpy extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n // this._element is the observablesContainer and config.target the menu links wrapper\n this._targetLinks = new Map()\n this._observableSections = new Map()\n this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element\n this._activeTarget = null\n this._observer = null\n this._previousScrollData = {\n visibleEntryTop: 0,\n parentScrollTop: 0\n }\n this.refresh() // initialize\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n refresh() {\n this._initializeTargetsAndObservables()\n this._maybeEnableSmoothScroll()\n\n if (this._observer) {\n this._observer.disconnect()\n } else {\n this._observer = this._getNewObserver()\n }\n\n for (const section of this._observableSections.values()) {\n this._observer.observe(section)\n }\n }\n\n dispose() {\n this._observer.disconnect()\n super.dispose()\n }\n\n // Private\n _configAfterMerge(config) {\n // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case\n config.target = getElement(config.target) || document.body\n\n // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only\n config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin\n\n if (typeof config.threshold === 'string') {\n config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value))\n }\n\n return config\n }\n\n _maybeEnableSmoothScroll() {\n if (!this._config.smoothScroll) {\n return\n }\n\n // unregister any previous listeners\n EventHandler.off(this._config.target, EVENT_CLICK)\n\n EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => {\n const observableSection = this._observableSections.get(event.target.hash)\n if (observableSection) {\n event.preventDefault()\n const root = this._rootElement || window\n const height = observableSection.offsetTop - this._element.offsetTop\n if (root.scrollTo) {\n root.scrollTo({ top: height, behavior: 'smooth' })\n return\n }\n\n // Chrome 60 doesn't support `scrollTo`\n root.scrollTop = height\n }\n })\n }\n\n _getNewObserver() {\n const options = {\n root: this._rootElement,\n threshold: this._config.threshold,\n rootMargin: this._config.rootMargin\n }\n\n return new IntersectionObserver(entries => this._observerCallback(entries), options)\n }\n\n // The logic of selection\n _observerCallback(entries) {\n const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`)\n const activate = entry => {\n this._previousScrollData.visibleEntryTop = entry.target.offsetTop\n this._process(targetElement(entry))\n }\n\n const parentScrollTop = (this._rootElement || document.documentElement).scrollTop\n const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop\n this._previousScrollData.parentScrollTop = parentScrollTop\n\n for (const entry of entries) {\n if (!entry.isIntersecting) {\n this._activeTarget = null\n this._clearActiveClass(targetElement(entry))\n\n continue\n }\n\n const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop\n // if we are scrolling down, pick the bigger offsetTop\n if (userScrollsDown && entryIsLowerThanPrevious) {\n activate(entry)\n // if parent isn't scrolled, let's keep the first visible item, breaking the iteration\n if (!parentScrollTop) {\n return\n }\n\n continue\n }\n\n // if we are scrolling up, pick the smallest offsetTop\n if (!userScrollsDown && !entryIsLowerThanPrevious) {\n activate(entry)\n }\n }\n }\n\n _initializeTargetsAndObservables() {\n this._targetLinks = new Map()\n this._observableSections = new Map()\n\n const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target)\n\n for (const anchor of targetLinks) {\n // ensure that the anchor has an id and is not disabled\n if (!anchor.hash || isDisabled(anchor)) {\n continue\n }\n\n const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element)\n\n // ensure that the observableSection exists & is visible\n if (isVisible(observableSection)) {\n this._targetLinks.set(decodeURI(anchor.hash), anchor)\n this._observableSections.set(anchor.hash, observableSection)\n }\n }\n }\n\n _process(target) {\n if (this._activeTarget === target) {\n return\n }\n\n this._clearActiveClass(this._config.target)\n this._activeTarget = target\n target.classList.add(CLASS_NAME_ACTIVE)\n this._activateParents(target)\n\n EventHandler.trigger(this._element, EVENT_ACTIVATE, { relatedTarget: target })\n }\n\n _activateParents(target) {\n // Activate dropdown parents\n if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {\n SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE, target.closest(SELECTOR_DROPDOWN))\n .classList.add(CLASS_NAME_ACTIVE)\n return\n }\n\n for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) {\n // Set triggered links parents as active\n // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor\n for (const item of SelectorEngine.prev(listGroup, SELECTOR_LINK_ITEMS)) {\n item.classList.add(CLASS_NAME_ACTIVE)\n }\n }\n }\n\n _clearActiveClass(parent) {\n parent.classList.remove(CLASS_NAME_ACTIVE)\n\n const activeNodes = SelectorEngine.find(`${SELECTOR_TARGET_LINKS}.${CLASS_NAME_ACTIVE}`, parent)\n for (const node of activeNodes) {\n node.classList.remove(CLASS_NAME_ACTIVE)\n }\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = ScrollSpy.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n for (const spy of SelectorEngine.find(SELECTOR_DATA_SPY)) {\n ScrollSpy.getOrCreateInstance(spy)\n }\n})\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(ScrollSpy)\n\nexport default ScrollSpy\n"],"names":["NAME","DATA_KEY","EVENT_KEY","DATA_API_KEY","EVENT_ACTIVATE","EVENT_CLICK","EVENT_LOAD_DATA_API","CLASS_NAME_DROPDOWN_ITEM","CLASS_NAME_ACTIVE","SELECTOR_DATA_SPY","SELECTOR_TARGET_LINKS","SELECTOR_NAV_LIST_GROUP","SELECTOR_NAV_LINKS","SELECTOR_NAV_ITEMS","SELECTOR_LIST_ITEMS","SELECTOR_LINK_ITEMS","SELECTOR_DROPDOWN","SELECTOR_DROPDOWN_TOGGLE","Default","offset","rootMargin","smoothScroll","target","threshold","DefaultType","ScrollSpy","BaseComponent","constructor","element","config","_targetLinks","Map","_observableSections","_rootElement","getComputedStyle","_element","overflowY","_activeTarget","_observer","_previousScrollData","visibleEntryTop","parentScrollTop","refresh","_initializeTargetsAndObservables","_maybeEnableSmoothScroll","disconnect","_getNewObserver","section","values","observe","dispose","_configAfterMerge","getElement","document","body","split","map","value","Number","parseFloat","_config","EventHandler","off","on","event","observableSection","get","hash","preventDefault","root","window","height","offsetTop","scrollTo","top","behavior","scrollTop","options","IntersectionObserver","entries","_observerCallback","targetElement","entry","id","activate","_process","documentElement","userScrollsDown","isIntersecting","_clearActiveClass","entryIsLowerThanPrevious","targetLinks","SelectorEngine","find","anchor","isDisabled","findOne","decodeURI","isVisible","set","classList","add","_activateParents","trigger","relatedTarget","contains","closest","listGroup","parents","item","prev","parent","remove","activeNodes","node","jQueryInterface","each","data","getOrCreateInstance","undefined","startsWith","TypeError","spy","defineJQueryPlugin"],"mappings":"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"} \ No newline at end of file
diff --git a/js/dist/tab.js b/js/dist/tab.js
index c3c24b75c..a4ad95a10 100644
--- a/js/dist/tab.js
+++ b/js/dist/tab.js
@@ -1,331 +1,283 @@
/*!
- * Bootstrap tab.js v5.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Bootstrap tab.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/event-handler.js'), require('./dom/selector-engine.js'), require('./base-component.js')) :
- typeof define === 'function' && define.amd ? define(['./dom/event-handler', './dom/selector-engine', './base-component'], factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Tab = factory(global.EventHandler, global.SelectorEngine, global.Base));
-})(this, (function (EventHandler, SelectorEngine, BaseComponent) { 'use strict';
-
- const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
-
- const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
- const SelectorEngine__default = /*#__PURE__*/_interopDefaultLegacy(SelectorEngine);
- const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/selector-engine.js'), require('./util/index.js')) :
+ typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './dom/selector-engine', './util/index'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Tab = factory(global.BaseComponent, global.EventHandler, global.SelectorEngine, global.Index));
+})(this, (function (BaseComponent, EventHandler, SelectorEngine, index_js) { 'use strict';
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/index.js
+ * Bootstrap tab.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
- const getSelector = element => {
- let selector = element.getAttribute('data-bs-target');
-
- if (!selector || selector === '#') {
- let hrefAttr = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes,
- // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
- // `document.querySelector` will rightfully complain it is invalid.
- // See https://github.com/twbs/bootstrap/issues/32273
-
- if (!hrefAttr || !hrefAttr.includes('#') && !hrefAttr.startsWith('.')) {
- return null;
- } // Just in case some CMS puts out a full URL with the anchor appended
-
-
- if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {
- hrefAttr = `#${hrefAttr.split('#')[1]}`;
- }
-
- selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null;
- }
-
- return selector;
- };
-
- const getElementFromSelector = element => {
- const selector = getSelector(element);
- return selector ? document.querySelector(selector) : null;
- };
-
- const isDisabled = element => {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return true;
- }
-
- if (element.classList.contains('disabled')) {
- return true;
- }
- if (typeof element.disabled !== 'undefined') {
- return element.disabled;
- }
-
- return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';
- };
/**
- * Trick to restart an element's animation
- *
- * @param {HTMLElement} element
- * @return void
- *
- * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
- */
-
-
- const reflow = element => {
- // eslint-disable-next-line no-unused-expressions
- element.offsetHeight;
- };
-
- const getjQuery = () => {
- const {
- jQuery
- } = window;
-
- if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
- return jQuery;
- }
-
- return null;
- };
-
- const DOMContentLoadedCallbacks = [];
-
- const onDOMContentLoaded = callback => {
- if (document.readyState === 'loading') {
- // add listener on the first call when the document is in loading state
- if (!DOMContentLoadedCallbacks.length) {
- document.addEventListener('DOMContentLoaded', () => {
- DOMContentLoadedCallbacks.forEach(callback => callback());
- });
- }
-
- DOMContentLoadedCallbacks.push(callback);
- } else {
- callback();
- }
- };
-
- const defineJQueryPlugin = plugin => {
- onDOMContentLoaded(() => {
- const $ = getjQuery();
- /* istanbul ignore if */
-
- if ($) {
- const name = plugin.NAME;
- const JQUERY_NO_CONFLICT = $.fn[name];
- $.fn[name] = plugin.jQueryInterface;
- $.fn[name].Constructor = plugin;
-
- $.fn[name].noConflict = () => {
- $.fn[name] = JQUERY_NO_CONFLICT;
- return plugin.jQueryInterface;
- };
- }
- });
- };
-
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): tab.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- /**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'tab';
const DATA_KEY = 'bs.tab';
const EVENT_KEY = `.${DATA_KEY}`;
- const DATA_API_KEY = '.data-api';
const EVENT_HIDE = `hide${EVENT_KEY}`;
const EVENT_HIDDEN = `hidden${EVENT_KEY}`;
const EVENT_SHOW = `show${EVENT_KEY}`;
const EVENT_SHOWN = `shown${EVENT_KEY}`;
- const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`;
- const CLASS_NAME_DROPDOWN_MENU = 'dropdown-menu';
+ const EVENT_CLICK_DATA_API = `click${EVENT_KEY}`;
+ const EVENT_KEYDOWN = `keydown${EVENT_KEY}`;
+ const EVENT_LOAD_DATA_API = `load${EVENT_KEY}`;
+ const ARROW_LEFT_KEY = 'ArrowLeft';
+ const ARROW_RIGHT_KEY = 'ArrowRight';
+ const ARROW_UP_KEY = 'ArrowUp';
+ const ARROW_DOWN_KEY = 'ArrowDown';
+ const HOME_KEY = 'Home';
+ const END_KEY = 'End';
const CLASS_NAME_ACTIVE = 'active';
const CLASS_NAME_FADE = 'fade';
const CLASS_NAME_SHOW = 'show';
- const SELECTOR_DROPDOWN = '.dropdown';
- const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group';
- const SELECTOR_ACTIVE = '.active';
- const SELECTOR_ACTIVE_UL = ':scope > li > .active';
- const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]';
+ const CLASS_DROPDOWN = 'dropdown';
const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle';
- const SELECTOR_DROPDOWN_ACTIVE_CHILD = ':scope > .dropdown-menu .active';
+ const SELECTOR_DROPDOWN_MENU = '.dropdown-menu';
+ const NOT_SELECTOR_DROPDOWN_TOGGLE = `:not(${SELECTOR_DROPDOWN_TOGGLE})`;
+ const SELECTOR_TAB_PANEL = '.list-group, .nav, [role="tablist"]';
+ const SELECTOR_OUTER = '.nav-item, .list-group-item';
+ const SELECTOR_INNER = `.nav-link${NOT_SELECTOR_DROPDOWN_TOGGLE}, .list-group-item${NOT_SELECTOR_DROPDOWN_TOGGLE}, [role="tab"]${NOT_SELECTOR_DROPDOWN_TOGGLE}`;
+ const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]'; // TODO: could only be `tab` in v6
+ const SELECTOR_INNER_ELEM = `${SELECTOR_INNER}, ${SELECTOR_DATA_TOGGLE}`;
+ const SELECTOR_DATA_TOGGLE_ACTIVE = `.${CLASS_NAME_ACTIVE}[data-bs-toggle="tab"], .${CLASS_NAME_ACTIVE}[data-bs-toggle="pill"], .${CLASS_NAME_ACTIVE}[data-bs-toggle="list"]`;
+
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
- class Tab extends BaseComponent__default.default {
+ class Tab extends BaseComponent {
+ constructor(element) {
+ super(element);
+ this._parent = this._element.closest(SELECTOR_TAB_PANEL);
+ if (!this._parent) {
+ return;
+ // TODO: should throw exception in v6
+ // throw new TypeError(`${element.outerHTML} has not a valid parent ${SELECTOR_INNER_ELEM}`)
+ }
+
+ // Set up initial aria attributes
+ this._setInitialAttributes(this._parent, this._getChildren());
+ EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event));
+ }
+
// Getters
static get NAME() {
return NAME;
- } // Public
-
+ }
+ // Public
show() {
- if (this._element.parentNode && this._element.parentNode.nodeType === Node.ELEMENT_NODE && this._element.classList.contains(CLASS_NAME_ACTIVE)) {
+ // Shows this elem and deactivate the active sibling if exists
+ const innerElem = this._element;
+ if (this._elemIsActive(innerElem)) {
return;
}
- let previous;
- const target = getElementFromSelector(this._element);
-
- const listElement = this._element.closest(SELECTOR_NAV_LIST_GROUP);
-
- if (listElement) {
- const itemSelector = listElement.nodeName === 'UL' || listElement.nodeName === 'OL' ? SELECTOR_ACTIVE_UL : SELECTOR_ACTIVE;
- previous = SelectorEngine__default.default.find(itemSelector, listElement);
- previous = previous[previous.length - 1];
- }
-
- const hideEvent = previous ? EventHandler__default.default.trigger(previous, EVENT_HIDE, {
- relatedTarget: this._element
+ // Search for active tab on same parent to deactivate it
+ const active = this._getActiveElem();
+ const hideEvent = active ? EventHandler.trigger(active, EVENT_HIDE, {
+ relatedTarget: innerElem
}) : null;
- const showEvent = EventHandler__default.default.trigger(this._element, EVENT_SHOW, {
- relatedTarget: previous
+ const showEvent = EventHandler.trigger(innerElem, EVENT_SHOW, {
+ relatedTarget: active
});
-
- if (showEvent.defaultPrevented || hideEvent !== null && hideEvent.defaultPrevented) {
+ if (showEvent.defaultPrevented || hideEvent && hideEvent.defaultPrevented) {
return;
}
+ this._deactivate(active, innerElem);
+ this._activate(innerElem, active);
+ }
- this._activate(this._element, listElement);
+ // Private
+ _activate(element, relatedElem) {
+ if (!element) {
+ return;
+ }
+ element.classList.add(CLASS_NAME_ACTIVE);
+ this._activate(SelectorEngine.getElementFromSelector(element)); // Search and activate/show the proper section
const complete = () => {
- EventHandler__default.default.trigger(previous, EVENT_HIDDEN, {
- relatedTarget: this._element
- });
- EventHandler__default.default.trigger(this._element, EVENT_SHOWN, {
- relatedTarget: previous
+ if (element.getAttribute('role') !== 'tab') {
+ element.classList.add(CLASS_NAME_SHOW);
+ return;
+ }
+ element.removeAttribute('tabindex');
+ element.setAttribute('aria-selected', true);
+ this._toggleDropDown(element, true);
+ EventHandler.trigger(element, EVENT_SHOWN, {
+ relatedTarget: relatedElem
});
};
-
- if (target) {
- this._activate(target, target.parentNode, complete);
- } else {
- complete();
+ this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE));
+ }
+ _deactivate(element, relatedElem) {
+ if (!element) {
+ return;
}
- } // Private
-
-
- _activate(element, container, callback) {
- const activeElements = container && (container.nodeName === 'UL' || container.nodeName === 'OL') ? SelectorEngine__default.default.find(SELECTOR_ACTIVE_UL, container) : SelectorEngine__default.default.children(container, SELECTOR_ACTIVE);
- const active = activeElements[0];
- const isTransitioning = callback && active && active.classList.contains(CLASS_NAME_FADE);
-
- const complete = () => this._transitionComplete(element, active, callback);
-
- if (active && isTransitioning) {
- active.classList.remove(CLASS_NAME_SHOW);
+ element.classList.remove(CLASS_NAME_ACTIVE);
+ element.blur();
+ this._deactivate(SelectorEngine.getElementFromSelector(element)); // Search and deactivate the shown section too
- this._queueCallback(complete, element, true);
+ const complete = () => {
+ if (element.getAttribute('role') !== 'tab') {
+ element.classList.remove(CLASS_NAME_SHOW);
+ return;
+ }
+ element.setAttribute('aria-selected', false);
+ element.setAttribute('tabindex', '-1');
+ this._toggleDropDown(element, false);
+ EventHandler.trigger(element, EVENT_HIDDEN, {
+ relatedTarget: relatedElem
+ });
+ };
+ this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE));
+ }
+ _keydown(event) {
+ if (![ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY, HOME_KEY, END_KEY].includes(event.key)) {
+ return;
+ }
+ event.stopPropagation(); // stopPropagation/preventDefault both added to support up/down keys without scrolling the page
+ event.preventDefault();
+ const children = this._getChildren().filter(element => !index_js.isDisabled(element));
+ let nextActiveElement;
+ if ([HOME_KEY, END_KEY].includes(event.key)) {
+ nextActiveElement = children[event.key === HOME_KEY ? 0 : children.length - 1];
} else {
- complete();
+ const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key);
+ nextActiveElement = index_js.getNextActiveElement(children, event.target, isNext, true);
+ }
+ if (nextActiveElement) {
+ nextActiveElement.focus({
+ preventScroll: true
+ });
+ Tab.getOrCreateInstance(nextActiveElement).show();
}
}
-
- _transitionComplete(element, active, callback) {
- if (active) {
- active.classList.remove(CLASS_NAME_ACTIVE);
- const dropdownChild = SelectorEngine__default.default.findOne(SELECTOR_DROPDOWN_ACTIVE_CHILD, active.parentNode);
-
- if (dropdownChild) {
- dropdownChild.classList.remove(CLASS_NAME_ACTIVE);
- }
-
- if (active.getAttribute('role') === 'tab') {
- active.setAttribute('aria-selected', false);
- }
+ _getChildren() {
+ // collection of inner elements
+ return SelectorEngine.find(SELECTOR_INNER_ELEM, this._parent);
+ }
+ _getActiveElem() {
+ return this._getChildren().find(child => this._elemIsActive(child)) || null;
+ }
+ _setInitialAttributes(parent, children) {
+ this._setAttributeIfNotExists(parent, 'role', 'tablist');
+ for (const child of children) {
+ this._setInitialAttributesOnChild(child);
}
-
- element.classList.add(CLASS_NAME_ACTIVE);
-
- if (element.getAttribute('role') === 'tab') {
- element.setAttribute('aria-selected', true);
+ }
+ _setInitialAttributesOnChild(child) {
+ child = this._getInnerElement(child);
+ const isActive = this._elemIsActive(child);
+ const outerElem = this._getOuterElement(child);
+ child.setAttribute('aria-selected', isActive);
+ if (outerElem !== child) {
+ this._setAttributeIfNotExists(outerElem, 'role', 'presentation');
}
-
- reflow(element);
-
- if (element.classList.contains(CLASS_NAME_FADE)) {
- element.classList.add(CLASS_NAME_SHOW);
+ if (!isActive) {
+ child.setAttribute('tabindex', '-1');
}
+ this._setAttributeIfNotExists(child, 'role', 'tab');
- let parent = element.parentNode;
-
- if (parent && parent.nodeName === 'LI') {
- parent = parent.parentNode;
+ // set attributes to the related panel too
+ this._setInitialAttributesOnTargetPanel(child);
+ }
+ _setInitialAttributesOnTargetPanel(child) {
+ const target = SelectorEngine.getElementFromSelector(child);
+ if (!target) {
+ return;
}
-
- if (parent && parent.classList.contains(CLASS_NAME_DROPDOWN_MENU)) {
- const dropdownElement = element.closest(SELECTOR_DROPDOWN);
-
- if (dropdownElement) {
- SelectorEngine__default.default.find(SELECTOR_DROPDOWN_TOGGLE, dropdownElement).forEach(dropdown => dropdown.classList.add(CLASS_NAME_ACTIVE));
+ this._setAttributeIfNotExists(target, 'role', 'tabpanel');
+ if (child.id) {
+ this._setAttributeIfNotExists(target, 'aria-labelledby', `${child.id}`);
+ }
+ }
+ _toggleDropDown(element, open) {
+ const outerElem = this._getOuterElement(element);
+ if (!outerElem.classList.contains(CLASS_DROPDOWN)) {
+ return;
+ }
+ const toggle = (selector, className) => {
+ const element = SelectorEngine.findOne(selector, outerElem);
+ if (element) {
+ element.classList.toggle(className, open);
}
-
- element.setAttribute('aria-expanded', true);
+ };
+ toggle(SELECTOR_DROPDOWN_TOGGLE, CLASS_NAME_ACTIVE);
+ toggle(SELECTOR_DROPDOWN_MENU, CLASS_NAME_SHOW);
+ outerElem.setAttribute('aria-expanded', open);
+ }
+ _setAttributeIfNotExists(element, attribute, value) {
+ if (!element.hasAttribute(attribute)) {
+ element.setAttribute(attribute, value);
}
+ }
+ _elemIsActive(elem) {
+ return elem.classList.contains(CLASS_NAME_ACTIVE);
+ }
- if (callback) {
- callback();
- }
- } // Static
+ // Try to get the inner element (usually the .nav-link)
+ _getInnerElement(elem) {
+ return elem.matches(SELECTOR_INNER_ELEM) ? elem : SelectorEngine.findOne(SELECTOR_INNER_ELEM, elem);
+ }
+ // Try to get the outer element (usually the .nav-item)
+ _getOuterElement(elem) {
+ return elem.closest(SELECTOR_OUTER) || elem;
+ }
+ // Static
static jQueryInterface(config) {
return this.each(function () {
const data = Tab.getOrCreateInstance(this);
-
- if (typeof config === 'string') {
- if (typeof data[config] === 'undefined') {
- throw new TypeError(`No method named "${config}"`);
- }
-
- data[config]();
+ if (typeof config !== 'string') {
+ return;
}
+ if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
+ throw new TypeError(`No method named "${config}"`);
+ }
+ data[config]();
});
}
-
}
+
/**
- * ------------------------------------------------------------------------
- * Data Api implementation
- * ------------------------------------------------------------------------
+ * Data API implementation
*/
-
- EventHandler__default.default.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
+ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
if (['A', 'AREA'].includes(this.tagName)) {
event.preventDefault();
}
-
- if (isDisabled(this)) {
+ if (index_js.isDisabled(this)) {
return;
}
+ Tab.getOrCreateInstance(this).show();
+ });
- const data = Tab.getOrCreateInstance(this);
- data.show();
+ /**
+ * Initialize on focus
+ */
+ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
+ for (const element of SelectorEngine.find(SELECTOR_DATA_TOGGLE_ACTIVE)) {
+ Tab.getOrCreateInstance(element);
+ }
});
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .Tab to jQuery only if jQuery is present
*/
- defineJQueryPlugin(Tab);
+ index_js.defineJQueryPlugin(Tab);
return Tab;
diff --git a/js/dist/tab.js.map b/js/dist/tab.js.map
index 6f6a3fd6b..c5c96d1bf 100644
--- a/js/dist/tab.js.map
+++ b/js/dist/tab.js.map
@@ -1 +1 @@
-{"version":3,"file":"tab.js","sources":["../src/util/index.js","../src/tab.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n// Shoutout AngusCroll (https://goo.gl/pxwQGp)\nconst toType = obj => {\n if (obj === null || obj === undefined) {\n return `${obj}`\n }\n\n return {}.toString.call(obj).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttr = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {\n hrefAttr = `#${hrefAttr.split('#')[1]}`\n }\n\n selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null\n }\n\n return selector\n}\n\nconst getSelectorFromElement = element => {\n const selector = getSelector(element)\n\n if (selector) {\n return document.querySelector(selector) ? selector : null\n }\n\n return null\n}\n\nconst getElementFromSelector = element => {\n const selector = getSelector(element)\n\n return selector ? document.querySelector(selector) : null\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = obj => {\n if (!obj || typeof obj !== 'object') {\n return false\n }\n\n if (typeof obj.jquery !== 'undefined') {\n obj = obj[0]\n }\n\n return typeof obj.nodeType !== 'undefined'\n}\n\nconst getElement = obj => {\n if (isElement(obj)) { // it's a jQuery object or a node element\n return obj.jquery ? obj[0] : obj\n }\n\n if (typeof obj === 'string' && obj.length > 0) {\n return document.querySelector(obj)\n }\n\n return null\n}\n\nconst typeCheckConfig = (componentName, config, configTypes) => {\n Object.keys(configTypes).forEach(property => {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${componentName.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n })\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n return getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n // eslint-disable-next-line no-unused-expressions\n element.offsetHeight\n}\n\nconst getjQuery = () => {\n const { jQuery } = window\n\n if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n DOMContentLoadedCallbacks.forEach(callback => callback())\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = callback => {\n if (typeof callback === 'function') {\n callback()\n }\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element depending on the direction and if cycle is allowed\n if (index === -1) {\n return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]\n }\n\n const listLength = list.length\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n getElement,\n getUID,\n getSelectorFromElement,\n getElementFromSelector,\n getTransitionDurationFromElement,\n triggerTransitionEnd,\n isElement,\n typeCheckConfig,\n isVisible,\n isDisabled,\n findShadowRoot,\n noop,\n getNextActiveElement,\n reflow,\n getjQuery,\n onDOMContentLoaded,\n isRTL,\n defineJQueryPlugin,\n execute,\n executeAfterTransition\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): tab.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport {\n defineJQueryPlugin,\n getElementFromSelector,\n isDisabled,\n reflow\n} from './util/index'\nimport EventHandler from './dom/event-handler'\nimport SelectorEngine from './dom/selector-engine'\nimport BaseComponent from './base-component'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'tab'\nconst DATA_KEY = 'bs.tab'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst DATA_API_KEY = '.data-api'\n\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`\n\nconst CLASS_NAME_DROPDOWN_MENU = 'dropdown-menu'\nconst CLASS_NAME_ACTIVE = 'active'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\n\nconst SELECTOR_DROPDOWN = '.dropdown'\nconst SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'\nconst SELECTOR_ACTIVE = '.active'\nconst SELECTOR_ACTIVE_UL = ':scope > li > .active'\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"tab\"], [data-bs-toggle=\"pill\"], [data-bs-toggle=\"list\"]'\nconst SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'\nconst SELECTOR_DROPDOWN_ACTIVE_CHILD = ':scope > .dropdown-menu .active'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Tab extends BaseComponent {\n // Getters\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\n show() {\n if ((this._element.parentNode &&\n this._element.parentNode.nodeType === Node.ELEMENT_NODE &&\n this._element.classList.contains(CLASS_NAME_ACTIVE))) {\n return\n }\n\n let previous\n const target = getElementFromSelector(this._element)\n const listElement = this._element.closest(SELECTOR_NAV_LIST_GROUP)\n\n if (listElement) {\n const itemSelector = listElement.nodeName === 'UL' || listElement.nodeName === 'OL' ? SELECTOR_ACTIVE_UL : SELECTOR_ACTIVE\n previous = SelectorEngine.find(itemSelector, listElement)\n previous = previous[previous.length - 1]\n }\n\n const hideEvent = previous ?\n EventHandler.trigger(previous, EVENT_HIDE, {\n relatedTarget: this._element\n }) :\n null\n\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {\n relatedTarget: previous\n })\n\n if (showEvent.defaultPrevented || (hideEvent !== null && hideEvent.defaultPrevented)) {\n return\n }\n\n this._activate(this._element, listElement)\n\n const complete = () => {\n EventHandler.trigger(previous, EVENT_HIDDEN, {\n relatedTarget: this._element\n })\n EventHandler.trigger(this._element, EVENT_SHOWN, {\n relatedTarget: previous\n })\n }\n\n if (target) {\n this._activate(target, target.parentNode, complete)\n } else {\n complete()\n }\n }\n\n // Private\n\n _activate(element, container, callback) {\n const activeElements = container && (container.nodeName === 'UL' || container.nodeName === 'OL') ?\n SelectorEngine.find(SELECTOR_ACTIVE_UL, container) :\n SelectorEngine.children(container, SELECTOR_ACTIVE)\n\n const active = activeElements[0]\n const isTransitioning = callback && (active && active.classList.contains(CLASS_NAME_FADE))\n\n const complete = () => this._transitionComplete(element, active, callback)\n\n if (active && isTransitioning) {\n active.classList.remove(CLASS_NAME_SHOW)\n this._queueCallback(complete, element, true)\n } else {\n complete()\n }\n }\n\n _transitionComplete(element, active, callback) {\n if (active) {\n active.classList.remove(CLASS_NAME_ACTIVE)\n\n const dropdownChild = SelectorEngine.findOne(SELECTOR_DROPDOWN_ACTIVE_CHILD, active.parentNode)\n\n if (dropdownChild) {\n dropdownChild.classList.remove(CLASS_NAME_ACTIVE)\n }\n\n if (active.getAttribute('role') === 'tab') {\n active.setAttribute('aria-selected', false)\n }\n }\n\n element.classList.add(CLASS_NAME_ACTIVE)\n if (element.getAttribute('role') === 'tab') {\n element.setAttribute('aria-selected', true)\n }\n\n reflow(element)\n\n if (element.classList.contains(CLASS_NAME_FADE)) {\n element.classList.add(CLASS_NAME_SHOW)\n }\n\n let parent = element.parentNode\n if (parent && parent.nodeName === 'LI') {\n parent = parent.parentNode\n }\n\n if (parent && parent.classList.contains(CLASS_NAME_DROPDOWN_MENU)) {\n const dropdownElement = element.closest(SELECTOR_DROPDOWN)\n\n if (dropdownElement) {\n SelectorEngine.find(SELECTOR_DROPDOWN_TOGGLE, dropdownElement)\n .forEach(dropdown => dropdown.classList.add(CLASS_NAME_ACTIVE))\n }\n\n element.setAttribute('aria-expanded', true)\n }\n\n if (callback) {\n callback()\n }\n }\n\n // Static\n\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Tab.getOrCreateInstance(this)\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * Data Api implementation\n * ------------------------------------------------------------------------\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n const data = Tab.getOrCreateInstance(this)\n data.show()\n})\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Tab to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Tab)\n\nexport default Tab\n"],"names":["getSelector","element","selector","getAttribute","hrefAttr","includes","startsWith","split","trim","getElementFromSelector","document","querySelector","isDisabled","nodeType","Node","ELEMENT_NODE","classList","contains","disabled","hasAttribute","reflow","offsetHeight","getjQuery","jQuery","window","body","DOMContentLoadedCallbacks","onDOMContentLoaded","callback","readyState","length","addEventListener","forEach","push","defineJQueryPlugin","plugin","$","name","NAME","JQUERY_NO_CONFLICT","fn","jQueryInterface","Constructor","noConflict","DATA_KEY","EVENT_KEY","DATA_API_KEY","EVENT_HIDE","EVENT_HIDDEN","EVENT_SHOW","EVENT_SHOWN","EVENT_CLICK_DATA_API","CLASS_NAME_DROPDOWN_MENU","CLASS_NAME_ACTIVE","CLASS_NAME_FADE","CLASS_NAME_SHOW","SELECTOR_DROPDOWN","SELECTOR_NAV_LIST_GROUP","SELECTOR_ACTIVE","SELECTOR_ACTIVE_UL","SELECTOR_DATA_TOGGLE","SELECTOR_DROPDOWN_TOGGLE","SELECTOR_DROPDOWN_ACTIVE_CHILD","Tab","BaseComponent","show","_element","parentNode","previous","target","listElement","closest","itemSelector","nodeName","SelectorEngine","find","hideEvent","EventHandler","trigger","relatedTarget","showEvent","defaultPrevented","_activate","complete","container","activeElements","children","active","isTransitioning","_transitionComplete","remove","_queueCallback","dropdownChild","findOne","setAttribute","add","parent","dropdownElement","dropdown","config","each","data","getOrCreateInstance","TypeError","on","event","tagName","preventDefault"],"mappings":"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"} \ No newline at end of file
+{"version":3,"file":"tab.js","sources":["../src/tab.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap tab.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport SelectorEngine from './dom/selector-engine.js'\nimport { defineJQueryPlugin, getNextActiveElement, isDisabled } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'tab'\nconst DATA_KEY = 'bs.tab'\nconst EVENT_KEY = `.${DATA_KEY}`\n\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\nconst EVENT_CLICK_DATA_API = `click${EVENT_KEY}`\nconst EVENT_KEYDOWN = `keydown${EVENT_KEY}`\nconst EVENT_LOAD_DATA_API = `load${EVENT_KEY}`\n\nconst ARROW_LEFT_KEY = 'ArrowLeft'\nconst ARROW_RIGHT_KEY = 'ArrowRight'\nconst ARROW_UP_KEY = 'ArrowUp'\nconst ARROW_DOWN_KEY = 'ArrowDown'\nconst HOME_KEY = 'Home'\nconst END_KEY = 'End'\n\nconst CLASS_NAME_ACTIVE = 'active'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_DROPDOWN = 'dropdown'\n\nconst SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'\nconst SELECTOR_DROPDOWN_MENU = '.dropdown-menu'\nconst NOT_SELECTOR_DROPDOWN_TOGGLE = `:not(${SELECTOR_DROPDOWN_TOGGLE})`\n\nconst SELECTOR_TAB_PANEL = '.list-group, .nav, [role=\"tablist\"]'\nconst SELECTOR_OUTER = '.nav-item, .list-group-item'\nconst SELECTOR_INNER = `.nav-link${NOT_SELECTOR_DROPDOWN_TOGGLE}, .list-group-item${NOT_SELECTOR_DROPDOWN_TOGGLE}, [role=\"tab\"]${NOT_SELECTOR_DROPDOWN_TOGGLE}`\nconst SELECTOR_DATA_TOGGLE = '[data-bs-toggle=\"tab\"], [data-bs-toggle=\"pill\"], [data-bs-toggle=\"list\"]' // TODO: could only be `tab` in v6\nconst SELECTOR_INNER_ELEM = `${SELECTOR_INNER}, ${SELECTOR_DATA_TOGGLE}`\n\nconst SELECTOR_DATA_TOGGLE_ACTIVE = `.${CLASS_NAME_ACTIVE}[data-bs-toggle=\"tab\"], .${CLASS_NAME_ACTIVE}[data-bs-toggle=\"pill\"], .${CLASS_NAME_ACTIVE}[data-bs-toggle=\"list\"]`\n\n/**\n * Class definition\n */\n\nclass Tab extends BaseComponent {\n constructor(element) {\n super(element)\n this._parent = this._element.closest(SELECTOR_TAB_PANEL)\n\n if (!this._parent) {\n return\n // TODO: should throw exception in v6\n // throw new TypeError(`${element.outerHTML} has not a valid parent ${SELECTOR_INNER_ELEM}`)\n }\n\n // Set up initial aria attributes\n this._setInitialAttributes(this._parent, this._getChildren())\n\n EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event))\n }\n\n // Getters\n static get NAME() {\n return NAME\n }\n\n // Public\n show() { // Shows this elem and deactivate the active sibling if exists\n const innerElem = this._element\n if (this._elemIsActive(innerElem)) {\n return\n }\n\n // Search for active tab on same parent to deactivate it\n const active = this._getActiveElem()\n\n const hideEvent = active ?\n EventHandler.trigger(active, EVENT_HIDE, { relatedTarget: innerElem }) :\n null\n\n const showEvent = EventHandler.trigger(innerElem, EVENT_SHOW, { relatedTarget: active })\n\n if (showEvent.defaultPrevented || (hideEvent && hideEvent.defaultPrevented)) {\n return\n }\n\n this._deactivate(active, innerElem)\n this._activate(innerElem, active)\n }\n\n // Private\n _activate(element, relatedElem) {\n if (!element) {\n return\n }\n\n element.classList.add(CLASS_NAME_ACTIVE)\n\n this._activate(SelectorEngine.getElementFromSelector(element)) // Search and activate/show the proper section\n\n const complete = () => {\n if (element.getAttribute('role') !== 'tab') {\n element.classList.add(CLASS_NAME_SHOW)\n return\n }\n\n element.removeAttribute('tabindex')\n element.setAttribute('aria-selected', true)\n this._toggleDropDown(element, true)\n EventHandler.trigger(element, EVENT_SHOWN, {\n relatedTarget: relatedElem\n })\n }\n\n this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE))\n }\n\n _deactivate(element, relatedElem) {\n if (!element) {\n return\n }\n\n element.classList.remove(CLASS_NAME_ACTIVE)\n element.blur()\n\n this._deactivate(SelectorEngine.getElementFromSelector(element)) // Search and deactivate the shown section too\n\n const complete = () => {\n if (element.getAttribute('role') !== 'tab') {\n element.classList.remove(CLASS_NAME_SHOW)\n return\n }\n\n element.setAttribute('aria-selected', false)\n element.setAttribute('tabindex', '-1')\n this._toggleDropDown(element, false)\n EventHandler.trigger(element, EVENT_HIDDEN, { relatedTarget: relatedElem })\n }\n\n this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE))\n }\n\n _keydown(event) {\n if (!([ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY, HOME_KEY, END_KEY].includes(event.key))) {\n return\n }\n\n event.stopPropagation()// stopPropagation/preventDefault both added to support up/down keys without scrolling the page\n event.preventDefault()\n\n const children = this._getChildren().filter(element => !isDisabled(element))\n let nextActiveElement\n\n if ([HOME_KEY, END_KEY].includes(event.key)) {\n nextActiveElement = children[event.key === HOME_KEY ? 0 : children.length - 1]\n } else {\n const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key)\n nextActiveElement = getNextActiveElement(children, event.target, isNext, true)\n }\n\n if (nextActiveElement) {\n nextActiveElement.focus({ preventScroll: true })\n Tab.getOrCreateInstance(nextActiveElement).show()\n }\n }\n\n _getChildren() { // collection of inner elements\n return SelectorEngine.find(SELECTOR_INNER_ELEM, this._parent)\n }\n\n _getActiveElem() {\n return this._getChildren().find(child => this._elemIsActive(child)) || null\n }\n\n _setInitialAttributes(parent, children) {\n this._setAttributeIfNotExists(parent, 'role', 'tablist')\n\n for (const child of children) {\n this._setInitialAttributesOnChild(child)\n }\n }\n\n _setInitialAttributesOnChild(child) {\n child = this._getInnerElement(child)\n const isActive = this._elemIsActive(child)\n const outerElem = this._getOuterElement(child)\n child.setAttribute('aria-selected', isActive)\n\n if (outerElem !== child) {\n this._setAttributeIfNotExists(outerElem, 'role', 'presentation')\n }\n\n if (!isActive) {\n child.setAttribute('tabindex', '-1')\n }\n\n this._setAttributeIfNotExists(child, 'role', 'tab')\n\n // set attributes to the related panel too\n this._setInitialAttributesOnTargetPanel(child)\n }\n\n _setInitialAttributesOnTargetPanel(child) {\n const target = SelectorEngine.getElementFromSelector(child)\n\n if (!target) {\n return\n }\n\n this._setAttributeIfNotExists(target, 'role', 'tabpanel')\n\n if (child.id) {\n this._setAttributeIfNotExists(target, 'aria-labelledby', `${child.id}`)\n }\n }\n\n _toggleDropDown(element, open) {\n const outerElem = this._getOuterElement(element)\n if (!outerElem.classList.contains(CLASS_DROPDOWN)) {\n return\n }\n\n const toggle = (selector, className) => {\n const element = SelectorEngine.findOne(selector, outerElem)\n if (element) {\n element.classList.toggle(className, open)\n }\n }\n\n toggle(SELECTOR_DROPDOWN_TOGGLE, CLASS_NAME_ACTIVE)\n toggle(SELECTOR_DROPDOWN_MENU, CLASS_NAME_SHOW)\n outerElem.setAttribute('aria-expanded', open)\n }\n\n _setAttributeIfNotExists(element, attribute, value) {\n if (!element.hasAttribute(attribute)) {\n element.setAttribute(attribute, value)\n }\n }\n\n _elemIsActive(elem) {\n return elem.classList.contains(CLASS_NAME_ACTIVE)\n }\n\n // Try to get the inner element (usually the .nav-link)\n _getInnerElement(elem) {\n return elem.matches(SELECTOR_INNER_ELEM) ? elem : SelectorEngine.findOne(SELECTOR_INNER_ELEM, elem)\n }\n\n // Try to get the outer element (usually the .nav-item)\n _getOuterElement(elem) {\n return elem.closest(SELECTOR_OUTER) || elem\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Tab.getOrCreateInstance(this)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nEventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n Tab.getOrCreateInstance(this).show()\n})\n\n/**\n * Initialize on focus\n */\nEventHandler.on(window, EVENT_LOAD_DATA_API, () => {\n for (const element of SelectorEngine.find(SELECTOR_DATA_TOGGLE_ACTIVE)) {\n Tab.getOrCreateInstance(element)\n }\n})\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Tab)\n\nexport default Tab\n"],"names":["NAME","DATA_KEY","EVENT_KEY","EVENT_HIDE","EVENT_HIDDEN","EVENT_SHOW","EVENT_SHOWN","EVENT_CLICK_DATA_API","EVENT_KEYDOWN","EVENT_LOAD_DATA_API","ARROW_LEFT_KEY","ARROW_RIGHT_KEY","ARROW_UP_KEY","ARROW_DOWN_KEY","HOME_KEY","END_KEY","CLASS_NAME_ACTIVE","CLASS_NAME_FADE","CLASS_NAME_SHOW","CLASS_DROPDOWN","SELECTOR_DROPDOWN_TOGGLE","SELECTOR_DROPDOWN_MENU","NOT_SELECTOR_DROPDOWN_TOGGLE","SELECTOR_TAB_PANEL","SELECTOR_OUTER","SELECTOR_INNER","SELECTOR_DATA_TOGGLE","SELECTOR_INNER_ELEM","SELECTOR_DATA_TOGGLE_ACTIVE","Tab","BaseComponent","constructor","element","_parent","_element","closest","_setInitialAttributes","_getChildren","EventHandler","on","event","_keydown","show","innerElem","_elemIsActive","active","_getActiveElem","hideEvent","trigger","relatedTarget","showEvent","defaultPrevented","_deactivate","_activate","relatedElem","classList","add","SelectorEngine","getElementFromSelector","complete","getAttribute","removeAttribute","setAttribute","_toggleDropDown","_queueCallback","contains","remove","blur","includes","key","stopPropagation","preventDefault","children","filter","isDisabled","nextActiveElement","length","isNext","getNextActiveElement","target","focus","preventScroll","getOrCreateInstance","find","child","parent","_setAttributeIfNotExists","_setInitialAttributesOnChild","_getInnerElement","isActive","outerElem","_getOuterElement","_setInitialAttributesOnTargetPanel","id","open","toggle","selector","className","findOne","attribute","value","hasAttribute","elem","matches","jQueryInterface","config","each","data","undefined","startsWith","TypeError","document","tagName","window","defineJQueryPlugin"],"mappings":"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"} \ No newline at end of file
diff --git a/js/dist/toast.js b/js/dist/toast.js
index 3a2992762..0362cf8d3 100644
--- a/js/dist/toast.js
+++ b/js/dist/toast.js
@@ -1,202 +1,24 @@
/*!
- * Bootstrap toast.js v5.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Bootstrap toast.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./base-component.js')) :
- typeof define === 'function' && define.amd ? define(['./dom/event-handler', './dom/manipulator', './base-component'], factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Toast = factory(global.EventHandler, global.Manipulator, global.Base));
-})(this, (function (EventHandler, Manipulator, BaseComponent) { 'use strict';
-
- const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
-
- const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
- const Manipulator__default = /*#__PURE__*/_interopDefaultLegacy(Manipulator);
- const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
-
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/index.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
-
- const toType = obj => {
- if (obj === null || obj === undefined) {
- return `${obj}`;
- }
-
- return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase();
- };
-
- const getSelector = element => {
- let selector = element.getAttribute('data-bs-target');
-
- if (!selector || selector === '#') {
- let hrefAttr = element.getAttribute('href'); // The only valid content that could double as a selector are IDs or classes,
- // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
- // `document.querySelector` will rightfully complain it is invalid.
- // See https://github.com/twbs/bootstrap/issues/32273
-
- if (!hrefAttr || !hrefAttr.includes('#') && !hrefAttr.startsWith('.')) {
- return null;
- } // Just in case some CMS puts out a full URL with the anchor appended
-
-
- if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {
- hrefAttr = `#${hrefAttr.split('#')[1]}`;
- }
-
- selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null;
- }
-
- return selector;
- };
-
- const getElementFromSelector = element => {
- const selector = getSelector(element);
- return selector ? document.querySelector(selector) : null;
- };
-
- const isElement = obj => {
- if (!obj || typeof obj !== 'object') {
- return false;
- }
-
- if (typeof obj.jquery !== 'undefined') {
- obj = obj[0];
- }
-
- return typeof obj.nodeType !== 'undefined';
- };
-
- const typeCheckConfig = (componentName, config, configTypes) => {
- Object.keys(configTypes).forEach(property => {
- const expectedTypes = configTypes[property];
- const value = config[property];
- const valueType = value && isElement(value) ? 'element' : toType(value);
-
- if (!new RegExp(expectedTypes).test(valueType)) {
- throw new TypeError(`${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`);
- }
- });
- };
-
- const isDisabled = element => {
- if (!element || element.nodeType !== Node.ELEMENT_NODE) {
- return true;
- }
-
- if (element.classList.contains('disabled')) {
- return true;
- }
-
- if (typeof element.disabled !== 'undefined') {
- return element.disabled;
- }
-
- return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';
- };
- /**
- * Trick to restart an element's animation
- *
- * @param {HTMLElement} element
- * @return void
- *
- * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
- */
-
-
- const reflow = element => {
- // eslint-disable-next-line no-unused-expressions
- element.offsetHeight;
- };
-
- const getjQuery = () => {
- const {
- jQuery
- } = window;
-
- if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
- return jQuery;
- }
-
- return null;
- };
-
- const DOMContentLoadedCallbacks = [];
-
- const onDOMContentLoaded = callback => {
- if (document.readyState === 'loading') {
- // add listener on the first call when the document is in loading state
- if (!DOMContentLoadedCallbacks.length) {
- document.addEventListener('DOMContentLoaded', () => {
- DOMContentLoadedCallbacks.forEach(callback => callback());
- });
- }
-
- DOMContentLoadedCallbacks.push(callback);
- } else {
- callback();
- }
- };
-
- const defineJQueryPlugin = plugin => {
- onDOMContentLoaded(() => {
- const $ = getjQuery();
- /* istanbul ignore if */
-
- if ($) {
- const name = plugin.NAME;
- const JQUERY_NO_CONFLICT = $.fn[name];
- $.fn[name] = plugin.jQueryInterface;
- $.fn[name].Constructor = plugin;
-
- $.fn[name].noConflict = () => {
- $.fn[name] = JQUERY_NO_CONFLICT;
- return plugin.jQueryInterface;
- };
- }
- });
- };
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('./base-component.js'), require('./dom/event-handler.js'), require('./util/component-functions.js'), require('./util/index.js')) :
+ typeof define === 'function' && define.amd ? define(['./base-component', './dom/event-handler', './util/component-functions', './util/index'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Toast = factory(global.BaseComponent, global.EventHandler, global.ComponentFunctions, global.Index));
+})(this, (function (BaseComponent, EventHandler, componentFunctions_js, index_js) { 'use strict';
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/component-functions.js
+ * Bootstrap toast.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
- const enableDismissTrigger = (component, method = 'hide') => {
- const clickEvent = `click.dismiss${component.EVENT_KEY}`;
- const name = component.NAME;
- EventHandler__default.default.on(document, clickEvent, `[data-bs-dismiss="${name}"]`, function (event) {
- if (['A', 'AREA'].includes(this.tagName)) {
- event.preventDefault();
- }
-
- if (isDisabled(this)) {
- return;
- }
-
- const target = getElementFromSelector(this) || this.closest(`.${name}`);
- const instance = component.getOrCreateInstance(target); // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method
-
- instance[method]();
- });
- };
/**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): toast.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- /**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'toast';
@@ -212,7 +34,6 @@
const EVENT_SHOWN = `shown${EVENT_KEY}`;
const CLASS_NAME_FADE = 'fade';
const CLASS_NAME_HIDE = 'hide'; // @deprecated - kept here only for backwards compatibility
-
const CLASS_NAME_SHOW = 'show';
const CLASS_NAME_SHOWING = 'showing';
const DefaultType = {
@@ -225,197 +46,152 @@
autohide: true,
delay: 5000
};
+
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
- class Toast extends BaseComponent__default.default {
+ class Toast extends BaseComponent {
constructor(element, config) {
- super(element);
- this._config = this._getConfig(config);
+ super(element, config);
this._timeout = null;
this._hasMouseInteraction = false;
this._hasKeyboardInteraction = false;
-
this._setListeners();
- } // Getters
-
-
- static get DefaultType() {
- return DefaultType;
}
+ // Getters
static get Default() {
return Default;
}
-
+ static get DefaultType() {
+ return DefaultType;
+ }
static get NAME() {
return NAME;
- } // Public
-
+ }
+ // Public
show() {
- const showEvent = EventHandler__default.default.trigger(this._element, EVENT_SHOW);
-
+ const showEvent = EventHandler.trigger(this._element, EVENT_SHOW);
if (showEvent.defaultPrevented) {
return;
}
-
this._clearTimeout();
-
if (this._config.animation) {
this._element.classList.add(CLASS_NAME_FADE);
}
-
const complete = () => {
this._element.classList.remove(CLASS_NAME_SHOWING);
-
- EventHandler__default.default.trigger(this._element, EVENT_SHOWN);
-
+ EventHandler.trigger(this._element, EVENT_SHOWN);
this._maybeScheduleHide();
};
-
this._element.classList.remove(CLASS_NAME_HIDE); // @deprecated
-
-
- reflow(this._element);
-
- this._element.classList.add(CLASS_NAME_SHOW);
-
- this._element.classList.add(CLASS_NAME_SHOWING);
-
+ index_js.reflow(this._element);
+ this._element.classList.add(CLASS_NAME_SHOW, CLASS_NAME_SHOWING);
this._queueCallback(complete, this._element, this._config.animation);
}
-
hide() {
- if (!this._element.classList.contains(CLASS_NAME_SHOW)) {
+ if (!this.isShown()) {
return;
}
-
- const hideEvent = EventHandler__default.default.trigger(this._element, EVENT_HIDE);
-
+ const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE);
if (hideEvent.defaultPrevented) {
return;
}
-
const complete = () => {
this._element.classList.add(CLASS_NAME_HIDE); // @deprecated
-
-
- this._element.classList.remove(CLASS_NAME_SHOWING);
-
- this._element.classList.remove(CLASS_NAME_SHOW);
-
- EventHandler__default.default.trigger(this._element, EVENT_HIDDEN);
+ this._element.classList.remove(CLASS_NAME_SHOWING, CLASS_NAME_SHOW);
+ EventHandler.trigger(this._element, EVENT_HIDDEN);
};
-
this._element.classList.add(CLASS_NAME_SHOWING);
-
this._queueCallback(complete, this._element, this._config.animation);
}
-
dispose() {
this._clearTimeout();
-
- if (this._element.classList.contains(CLASS_NAME_SHOW)) {
+ if (this.isShown()) {
this._element.classList.remove(CLASS_NAME_SHOW);
}
-
super.dispose();
- } // Private
-
-
- _getConfig(config) {
- config = { ...Default,
- ...Manipulator__default.default.getDataAttributes(this._element),
- ...(typeof config === 'object' && config ? config : {})
- };
- typeCheckConfig(NAME, config, this.constructor.DefaultType);
- return config;
+ }
+ isShown() {
+ return this._element.classList.contains(CLASS_NAME_SHOW);
}
+ // Private
+
_maybeScheduleHide() {
if (!this._config.autohide) {
return;
}
-
if (this._hasMouseInteraction || this._hasKeyboardInteraction) {
return;
}
-
this._timeout = setTimeout(() => {
this.hide();
}, this._config.delay);
}
-
_onInteraction(event, isInteracting) {
switch (event.type) {
case 'mouseover':
case 'mouseout':
- this._hasMouseInteraction = isInteracting;
- break;
-
+ {
+ this._hasMouseInteraction = isInteracting;
+ break;
+ }
case 'focusin':
case 'focusout':
- this._hasKeyboardInteraction = isInteracting;
- break;
+ {
+ this._hasKeyboardInteraction = isInteracting;
+ break;
+ }
}
-
if (isInteracting) {
this._clearTimeout();
-
return;
}
-
const nextElement = event.relatedTarget;
-
if (this._element === nextElement || this._element.contains(nextElement)) {
return;
}
-
this._maybeScheduleHide();
}
-
_setListeners() {
- EventHandler__default.default.on(this._element, EVENT_MOUSEOVER, event => this._onInteraction(event, true));
- EventHandler__default.default.on(this._element, EVENT_MOUSEOUT, event => this._onInteraction(event, false));
- EventHandler__default.default.on(this._element, EVENT_FOCUSIN, event => this._onInteraction(event, true));
- EventHandler__default.default.on(this._element, EVENT_FOCUSOUT, event => this._onInteraction(event, false));
+ EventHandler.on(this._element, EVENT_MOUSEOVER, event => this._onInteraction(event, true));
+ EventHandler.on(this._element, EVENT_MOUSEOUT, event => this._onInteraction(event, false));
+ EventHandler.on(this._element, EVENT_FOCUSIN, event => this._onInteraction(event, true));
+ EventHandler.on(this._element, EVENT_FOCUSOUT, event => this._onInteraction(event, false));
}
-
_clearTimeout() {
clearTimeout(this._timeout);
this._timeout = null;
- } // Static
-
+ }
+ // Static
static jQueryInterface(config) {
return this.each(function () {
const data = Toast.getOrCreateInstance(this, config);
-
if (typeof config === 'string') {
if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`);
}
-
data[config](this);
}
});
}
-
}
- enableDismissTrigger(Toast);
/**
- * ------------------------------------------------------------------------
+ * Data API implementation
+ */
+
+ componentFunctions_js.enableDismissTrigger(Toast);
+
+ /**
* jQuery
- * ------------------------------------------------------------------------
- * add .Toast to jQuery only if jQuery is present
*/
- defineJQueryPlugin(Toast);
+ index_js.defineJQueryPlugin(Toast);
return Toast;
diff --git a/js/dist/toast.js.map b/js/dist/toast.js.map
index 77d02170c..57f1bf60b 100644
--- a/js/dist/toast.js.map
+++ b/js/dist/toast.js.map
@@ -1 +1 @@
-{"version":3,"file":"toast.js","sources":["../src/util/index.js","../src/util/component-functions.js","../src/toast.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n// Shoutout AngusCroll (https://goo.gl/pxwQGp)\nconst toType = obj => {\n if (obj === null || obj === undefined) {\n return `${obj}`\n }\n\n return {}.toString.call(obj).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttr = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {\n hrefAttr = `#${hrefAttr.split('#')[1]}`\n }\n\n selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null\n }\n\n return selector\n}\n\nconst getSelectorFromElement = element => {\n const selector = getSelector(element)\n\n if (selector) {\n return document.querySelector(selector) ? selector : null\n }\n\n return null\n}\n\nconst getElementFromSelector = element => {\n const selector = getSelector(element)\n\n return selector ? document.querySelector(selector) : null\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = obj => {\n if (!obj || typeof obj !== 'object') {\n return false\n }\n\n if (typeof obj.jquery !== 'undefined') {\n obj = obj[0]\n }\n\n return typeof obj.nodeType !== 'undefined'\n}\n\nconst getElement = obj => {\n if (isElement(obj)) { // it's a jQuery object or a node element\n return obj.jquery ? obj[0] : obj\n }\n\n if (typeof obj === 'string' && obj.length > 0) {\n return document.querySelector(obj)\n }\n\n return null\n}\n\nconst typeCheckConfig = (componentName, config, configTypes) => {\n Object.keys(configTypes).forEach(property => {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${componentName.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n })\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n return getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n // eslint-disable-next-line no-unused-expressions\n element.offsetHeight\n}\n\nconst getjQuery = () => {\n const { jQuery } = window\n\n if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n DOMContentLoadedCallbacks.forEach(callback => callback())\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = callback => {\n if (typeof callback === 'function') {\n callback()\n }\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element depending on the direction and if cycle is allowed\n if (index === -1) {\n return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]\n }\n\n const listLength = list.length\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n getElement,\n getUID,\n getSelectorFromElement,\n getElementFromSelector,\n getTransitionDurationFromElement,\n triggerTransitionEnd,\n isElement,\n typeCheckConfig,\n isVisible,\n isDisabled,\n findShadowRoot,\n noop,\n getNextActiveElement,\n reflow,\n getjQuery,\n onDOMContentLoaded,\n isRTL,\n defineJQueryPlugin,\n execute,\n executeAfterTransition\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/component-functions.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler'\nimport { getElementFromSelector, isDisabled } from './index'\n\nconst enableDismissTrigger = (component, method = 'hide') => {\n const clickEvent = `click.dismiss${component.EVENT_KEY}`\n const name = component.NAME\n\n EventHandler.on(document, clickEvent, `[data-bs-dismiss=\"${name}\"]`, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n const target = getElementFromSelector(this) || this.closest(`.${name}`)\n const instance = component.getOrCreateInstance(target)\n\n // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method\n instance[method]()\n })\n}\n\nexport {\n enableDismissTrigger\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): toast.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport {\n defineJQueryPlugin,\n reflow,\n typeCheckConfig\n} from './util/index'\nimport EventHandler from './dom/event-handler'\nimport Manipulator from './dom/manipulator'\nimport BaseComponent from './base-component'\nimport { enableDismissTrigger } from './util/component-functions'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'toast'\nconst DATA_KEY = 'bs.toast'\nconst EVENT_KEY = `.${DATA_KEY}`\n\nconst EVENT_MOUSEOVER = `mouseover${EVENT_KEY}`\nconst EVENT_MOUSEOUT = `mouseout${EVENT_KEY}`\nconst EVENT_FOCUSIN = `focusin${EVENT_KEY}`\nconst EVENT_FOCUSOUT = `focusout${EVENT_KEY}`\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\n\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_HIDE = 'hide' // @deprecated - kept here only for backwards compatibility\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_SHOWING = 'showing'\n\nconst DefaultType = {\n animation: 'boolean',\n autohide: 'boolean',\n delay: 'number'\n}\n\nconst Default = {\n animation: true,\n autohide: true,\n delay: 5000\n}\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Toast extends BaseComponent {\n constructor(element, config) {\n super(element)\n\n this._config = this._getConfig(config)\n this._timeout = null\n this._hasMouseInteraction = false\n this._hasKeyboardInteraction = false\n this._setListeners()\n }\n\n // Getters\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n\n show() {\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW)\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._clearTimeout()\n\n if (this._config.animation) {\n this._element.classList.add(CLASS_NAME_FADE)\n }\n\n const complete = () => {\n this._element.classList.remove(CLASS_NAME_SHOWING)\n EventHandler.trigger(this._element, EVENT_SHOWN)\n\n this._maybeScheduleHide()\n }\n\n this._element.classList.remove(CLASS_NAME_HIDE) // @deprecated\n reflow(this._element)\n this._element.classList.add(CLASS_NAME_SHOW)\n this._element.classList.add(CLASS_NAME_SHOWING)\n\n this._queueCallback(complete, this._element, this._config.animation)\n }\n\n hide() {\n if (!this._element.classList.contains(CLASS_NAME_SHOW)) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n\n if (hideEvent.defaultPrevented) {\n return\n }\n\n const complete = () => {\n this._element.classList.add(CLASS_NAME_HIDE) // @deprecated\n this._element.classList.remove(CLASS_NAME_SHOWING)\n this._element.classList.remove(CLASS_NAME_SHOW)\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n }\n\n this._element.classList.add(CLASS_NAME_SHOWING)\n this._queueCallback(complete, this._element, this._config.animation)\n }\n\n dispose() {\n this._clearTimeout()\n\n if (this._element.classList.contains(CLASS_NAME_SHOW)) {\n this._element.classList.remove(CLASS_NAME_SHOW)\n }\n\n super.dispose()\n }\n\n // Private\n\n _getConfig(config) {\n config = {\n ...Default,\n ...Manipulator.getDataAttributes(this._element),\n ...(typeof config === 'object' && config ? config : {})\n }\n\n typeCheckConfig(NAME, config, this.constructor.DefaultType)\n\n return config\n }\n\n _maybeScheduleHide() {\n if (!this._config.autohide) {\n return\n }\n\n if (this._hasMouseInteraction || this._hasKeyboardInteraction) {\n return\n }\n\n this._timeout = setTimeout(() => {\n this.hide()\n }, this._config.delay)\n }\n\n _onInteraction(event, isInteracting) {\n switch (event.type) {\n case 'mouseover':\n case 'mouseout':\n this._hasMouseInteraction = isInteracting\n break\n case 'focusin':\n case 'focusout':\n this._hasKeyboardInteraction = isInteracting\n break\n default:\n break\n }\n\n if (isInteracting) {\n this._clearTimeout()\n return\n }\n\n const nextElement = event.relatedTarget\n if (this._element === nextElement || this._element.contains(nextElement)) {\n return\n }\n\n this._maybeScheduleHide()\n }\n\n _setListeners() {\n EventHandler.on(this._element, EVENT_MOUSEOVER, event => this._onInteraction(event, true))\n EventHandler.on(this._element, EVENT_MOUSEOUT, event => this._onInteraction(event, false))\n EventHandler.on(this._element, EVENT_FOCUSIN, event => this._onInteraction(event, true))\n EventHandler.on(this._element, EVENT_FOCUSOUT, event => this._onInteraction(event, false))\n }\n\n _clearTimeout() {\n clearTimeout(this._timeout)\n this._timeout = null\n }\n\n // Static\n\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Toast.getOrCreateInstance(this, config)\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](this)\n }\n })\n }\n}\n\nenableDismissTrigger(Toast)\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Toast to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Toast)\n\nexport default Toast\n"],"names":["toType","obj","undefined","toString","call","match","toLowerCase","getSelector","element","selector","getAttribute","hrefAttr","includes","startsWith","split","trim","getElementFromSelector","document","querySelector","isElement","jquery","nodeType","typeCheckConfig","componentName","config","configTypes","Object","keys","forEach","property","expectedTypes","value","valueType","RegExp","test","TypeError","toUpperCase","isDisabled","Node","ELEMENT_NODE","classList","contains","disabled","hasAttribute","reflow","offsetHeight","getjQuery","jQuery","window","body","DOMContentLoadedCallbacks","onDOMContentLoaded","callback","readyState","length","addEventListener","push","defineJQueryPlugin","plugin","$","name","NAME","JQUERY_NO_CONFLICT","fn","jQueryInterface","Constructor","noConflict","enableDismissTrigger","component","method","clickEvent","EVENT_KEY","EventHandler","on","event","tagName","preventDefault","target","closest","instance","getOrCreateInstance","DATA_KEY","EVENT_MOUSEOVER","EVENT_MOUSEOUT","EVENT_FOCUSIN","EVENT_FOCUSOUT","EVENT_HIDE","EVENT_HIDDEN","EVENT_SHOW","EVENT_SHOWN","CLASS_NAME_FADE","CLASS_NAME_HIDE","CLASS_NAME_SHOW","CLASS_NAME_SHOWING","DefaultType","animation","autohide","delay","Default","Toast","BaseComponent","constructor","_config","_getConfig","_timeout","_hasMouseInteraction","_hasKeyboardInteraction","_setListeners","show","showEvent","trigger","_element","defaultPrevented","_clearTimeout","add","complete","remove","_maybeScheduleHide","_queueCallback","hide","hideEvent","dispose","Manipulator","getDataAttributes","setTimeout","_onInteraction","isInteracting","type","nextElement","relatedTarget","clearTimeout","each","data"],"mappings":"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"} \ No newline at end of file
+{"version":3,"file":"toast.js","sources":["../src/toast.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap toast.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport { enableDismissTrigger } from './util/component-functions.js'\nimport { defineJQueryPlugin, reflow } from './util/index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'toast'\nconst DATA_KEY = 'bs.toast'\nconst EVENT_KEY = `.${DATA_KEY}`\n\nconst EVENT_MOUSEOVER = `mouseover${EVENT_KEY}`\nconst EVENT_MOUSEOUT = `mouseout${EVENT_KEY}`\nconst EVENT_FOCUSIN = `focusin${EVENT_KEY}`\nconst EVENT_FOCUSOUT = `focusout${EVENT_KEY}`\nconst EVENT_HIDE = `hide${EVENT_KEY}`\nconst EVENT_HIDDEN = `hidden${EVENT_KEY}`\nconst EVENT_SHOW = `show${EVENT_KEY}`\nconst EVENT_SHOWN = `shown${EVENT_KEY}`\n\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_HIDE = 'hide' // @deprecated - kept here only for backwards compatibility\nconst CLASS_NAME_SHOW = 'show'\nconst CLASS_NAME_SHOWING = 'showing'\n\nconst DefaultType = {\n animation: 'boolean',\n autohide: 'boolean',\n delay: 'number'\n}\n\nconst Default = {\n animation: true,\n autohide: true,\n delay: 5000\n}\n\n/**\n * Class definition\n */\n\nclass Toast extends BaseComponent {\n constructor(element, config) {\n super(element, config)\n\n this._timeout = null\n this._hasMouseInteraction = false\n this._hasKeyboardInteraction = false\n this._setListeners()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n show() {\n const showEvent = EventHandler.trigger(this._element, EVENT_SHOW)\n\n if (showEvent.defaultPrevented) {\n return\n }\n\n this._clearTimeout()\n\n if (this._config.animation) {\n this._element.classList.add(CLASS_NAME_FADE)\n }\n\n const complete = () => {\n this._element.classList.remove(CLASS_NAME_SHOWING)\n EventHandler.trigger(this._element, EVENT_SHOWN)\n\n this._maybeScheduleHide()\n }\n\n this._element.classList.remove(CLASS_NAME_HIDE) // @deprecated\n reflow(this._element)\n this._element.classList.add(CLASS_NAME_SHOW, CLASS_NAME_SHOWING)\n\n this._queueCallback(complete, this._element, this._config.animation)\n }\n\n hide() {\n if (!this.isShown()) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)\n\n if (hideEvent.defaultPrevented) {\n return\n }\n\n const complete = () => {\n this._element.classList.add(CLASS_NAME_HIDE) // @deprecated\n this._element.classList.remove(CLASS_NAME_SHOWING, CLASS_NAME_SHOW)\n EventHandler.trigger(this._element, EVENT_HIDDEN)\n }\n\n this._element.classList.add(CLASS_NAME_SHOWING)\n this._queueCallback(complete, this._element, this._config.animation)\n }\n\n dispose() {\n this._clearTimeout()\n\n if (this.isShown()) {\n this._element.classList.remove(CLASS_NAME_SHOW)\n }\n\n super.dispose()\n }\n\n isShown() {\n return this._element.classList.contains(CLASS_NAME_SHOW)\n }\n\n // Private\n\n _maybeScheduleHide() {\n if (!this._config.autohide) {\n return\n }\n\n if (this._hasMouseInteraction || this._hasKeyboardInteraction) {\n return\n }\n\n this._timeout = setTimeout(() => {\n this.hide()\n }, this._config.delay)\n }\n\n _onInteraction(event, isInteracting) {\n switch (event.type) {\n case 'mouseover':\n case 'mouseout': {\n this._hasMouseInteraction = isInteracting\n break\n }\n\n case 'focusin':\n case 'focusout': {\n this._hasKeyboardInteraction = isInteracting\n break\n }\n\n default: {\n break\n }\n }\n\n if (isInteracting) {\n this._clearTimeout()\n return\n }\n\n const nextElement = event.relatedTarget\n if (this._element === nextElement || this._element.contains(nextElement)) {\n return\n }\n\n this._maybeScheduleHide()\n }\n\n _setListeners() {\n EventHandler.on(this._element, EVENT_MOUSEOVER, event => this._onInteraction(event, true))\n EventHandler.on(this._element, EVENT_MOUSEOUT, event => this._onInteraction(event, false))\n EventHandler.on(this._element, EVENT_FOCUSIN, event => this._onInteraction(event, true))\n EventHandler.on(this._element, EVENT_FOCUSOUT, event => this._onInteraction(event, false))\n }\n\n _clearTimeout() {\n clearTimeout(this._timeout)\n this._timeout = null\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Toast.getOrCreateInstance(this, config)\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config](this)\n }\n })\n }\n}\n\n/**\n * Data API implementation\n */\n\nenableDismissTrigger(Toast)\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Toast)\n\nexport default Toast\n"],"names":["NAME","DATA_KEY","EVENT_KEY","EVENT_MOUSEOVER","EVENT_MOUSEOUT","EVENT_FOCUSIN","EVENT_FOCUSOUT","EVENT_HIDE","EVENT_HIDDEN","EVENT_SHOW","EVENT_SHOWN","CLASS_NAME_FADE","CLASS_NAME_HIDE","CLASS_NAME_SHOW","CLASS_NAME_SHOWING","DefaultType","animation","autohide","delay","Default","Toast","BaseComponent","constructor","element","config","_timeout","_hasMouseInteraction","_hasKeyboardInteraction","_setListeners","show","showEvent","EventHandler","trigger","_element","defaultPrevented","_clearTimeout","_config","classList","add","complete","remove","_maybeScheduleHide","reflow","_queueCallback","hide","isShown","hideEvent","dispose","contains","setTimeout","_onInteraction","event","isInteracting","type","nextElement","relatedTarget","on","clearTimeout","jQueryInterface","each","data","getOrCreateInstance","TypeError","enableDismissTrigger","defineJQueryPlugin"],"mappings":"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"} \ No newline at end of file
diff --git a/js/dist/tooltip.js b/js/dist/tooltip.js
index 883c8dbb0..541cd7d01 100644
--- a/js/dist/tooltip.js
+++ b/js/dist/tooltip.js
@@ -1,19 +1,16 @@
/*!
- * Bootstrap tooltip.js v5.1.3 (https://getbootstrap.com/)
- * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Bootstrap tooltip.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
*/
(function (global, factory) {
- typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@popperjs/core'), require('./dom/data.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./dom/selector-engine.js'), require('./base-component.js')) :
- typeof define === 'function' && define.amd ? define(['@popperjs/core', './dom/data', './dom/event-handler', './dom/manipulator', './dom/selector-engine', './base-component'], factory) :
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Tooltip = factory(global.Popper, global.Data, global.EventHandler, global.Manipulator, global.SelectorEngine, global.Base));
-})(this, (function (Popper, Data, EventHandler, Manipulator, SelectorEngine, BaseComponent) { 'use strict';
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('@popperjs/core'), require('./base-component.js'), require('./dom/event-handler.js'), require('./dom/manipulator.js'), require('./util/index.js'), require('./util/sanitizer.js'), require('./util/template-factory.js')) :
+ typeof define === 'function' && define.amd ? define(['@popperjs/core', './base-component', './dom/event-handler', './dom/manipulator', './util/index', './util/sanitizer', './util/template-factory'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Tooltip = factory(global["@popperjs/core"], global.BaseComponent, global.EventHandler, global.Manipulator, global.Index, global.Sanitizer, global.TemplateFactory));
+})(this, (function (Popper, BaseComponent, EventHandler, Manipulator, index_js, sanitizer_js, TemplateFactory) { 'use strict';
- const _interopDefaultLegacy = e => e && typeof e === 'object' && 'default' in e ? e : { default: e };
-
- function _interopNamespace(e) {
- if (e && e.__esModule) return e;
- const n = Object.create(null);
+ function _interopNamespaceDefault(e) {
+ const n = Object.create(null, { [Symbol.toStringTag]: { value: 'Module' } });
if (e) {
for (const k in e) {
if (k !== 'default') {
@@ -29,702 +26,331 @@
return Object.freeze(n);
}
- const Popper__namespace = /*#__PURE__*/_interopNamespace(Popper);
- const Data__default = /*#__PURE__*/_interopDefaultLegacy(Data);
- const EventHandler__default = /*#__PURE__*/_interopDefaultLegacy(EventHandler);
- const Manipulator__default = /*#__PURE__*/_interopDefaultLegacy(Manipulator);
- const SelectorEngine__default = /*#__PURE__*/_interopDefaultLegacy(SelectorEngine);
- const BaseComponent__default = /*#__PURE__*/_interopDefaultLegacy(BaseComponent);
-
- /**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/index.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- const MAX_UID = 1000000;
-
- const toType = obj => {
- if (obj === null || obj === undefined) {
- return `${obj}`;
- }
-
- return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase();
- };
- /**
- * --------------------------------------------------------------------------
- * Public Util Api
- * --------------------------------------------------------------------------
- */
-
-
- const getUID = prefix => {
- do {
- prefix += Math.floor(Math.random() * MAX_UID);
- } while (document.getElementById(prefix));
-
- return prefix;
- };
-
- const isElement = obj => {
- if (!obj || typeof obj !== 'object') {
- return false;
- }
-
- if (typeof obj.jquery !== 'undefined') {
- obj = obj[0];
- }
-
- return typeof obj.nodeType !== 'undefined';
- };
-
- const getElement = obj => {
- if (isElement(obj)) {
- // it's a jQuery object or a node element
- return obj.jquery ? obj[0] : obj;
- }
-
- if (typeof obj === 'string' && obj.length > 0) {
- return document.querySelector(obj);
- }
-
- return null;
- };
-
- const typeCheckConfig = (componentName, config, configTypes) => {
- Object.keys(configTypes).forEach(property => {
- const expectedTypes = configTypes[property];
- const value = config[property];
- const valueType = value && isElement(value) ? 'element' : toType(value);
-
- if (!new RegExp(expectedTypes).test(valueType)) {
- throw new TypeError(`${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`);
- }
- });
- };
-
- const findShadowRoot = element => {
- if (!document.documentElement.attachShadow) {
- return null;
- } // Can find the shadow root otherwise it'll return the document
-
-
- if (typeof element.getRootNode === 'function') {
- const root = element.getRootNode();
- return root instanceof ShadowRoot ? root : null;
- }
-
- if (element instanceof ShadowRoot) {
- return element;
- } // when we don't find a shadow root
-
-
- if (!element.parentNode) {
- return null;
- }
-
- return findShadowRoot(element.parentNode);
- };
-
- const noop = () => {};
-
- const getjQuery = () => {
- const {
- jQuery
- } = window;
-
- if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
- return jQuery;
- }
-
- return null;
- };
-
- const DOMContentLoadedCallbacks = [];
-
- const onDOMContentLoaded = callback => {
- if (document.readyState === 'loading') {
- // add listener on the first call when the document is in loading state
- if (!DOMContentLoadedCallbacks.length) {
- document.addEventListener('DOMContentLoaded', () => {
- DOMContentLoadedCallbacks.forEach(callback => callback());
- });
- }
-
- DOMContentLoadedCallbacks.push(callback);
- } else {
- callback();
- }
- };
-
- const isRTL = () => document.documentElement.dir === 'rtl';
-
- const defineJQueryPlugin = plugin => {
- onDOMContentLoaded(() => {
- const $ = getjQuery();
- /* istanbul ignore if */
-
- if ($) {
- const name = plugin.NAME;
- const JQUERY_NO_CONFLICT = $.fn[name];
- $.fn[name] = plugin.jQueryInterface;
- $.fn[name].Constructor = plugin;
-
- $.fn[name].noConflict = () => {
- $.fn[name] = JQUERY_NO_CONFLICT;
- return plugin.jQueryInterface;
- };
- }
- });
- };
+ const Popper__namespace = /*#__PURE__*/_interopNamespaceDefault(Popper);
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/sanitizer.js
+ * Bootstrap tooltip.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
- const uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']);
- const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i;
- /**
- * A pattern that recognizes a commonly useful subset of URLs that are safe.
- *
- * Shoutout to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts
- */
- const SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i;
- /**
- * A pattern that matches safe data URLs. Only matches image, video and audio types.
- *
- * Shoutout to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts
- */
-
- const DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i;
-
- const allowedAttribute = (attribute, allowedAttributeList) => {
- const attributeName = attribute.nodeName.toLowerCase();
-
- if (allowedAttributeList.includes(attributeName)) {
- if (uriAttributes.has(attributeName)) {
- return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue) || DATA_URL_PATTERN.test(attribute.nodeValue));
- }
-
- return true;
- }
-
- const regExp = allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp); // Check if a regular expression validates the attribute.
-
- for (let i = 0, len = regExp.length; i < len; i++) {
- if (regExp[i].test(attributeName)) {
- return true;
- }
- }
-
- return false;
- };
-
- const DefaultAllowlist = {
- // Global attributes allowed on any supplied element below.
- '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
- a: ['target', 'href', 'title', 'rel'],
- area: [],
- b: [],
- br: [],
- col: [],
- code: [],
- div: [],
- em: [],
- hr: [],
- h1: [],
- h2: [],
- h3: [],
- h4: [],
- h5: [],
- h6: [],
- i: [],
- img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],
- li: [],
- ol: [],
- p: [],
- pre: [],
- s: [],
- small: [],
- span: [],
- sub: [],
- sup: [],
- strong: [],
- u: [],
- ul: []
- };
- function sanitizeHtml(unsafeHtml, allowList, sanitizeFn) {
- if (!unsafeHtml.length) {
- return unsafeHtml;
- }
-
- if (sanitizeFn && typeof sanitizeFn === 'function') {
- return sanitizeFn(unsafeHtml);
- }
-
- const domParser = new window.DOMParser();
- const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');
- const elements = [].concat(...createdDocument.body.querySelectorAll('*'));
-
- for (let i = 0, len = elements.length; i < len; i++) {
- const element = elements[i];
- const elementName = element.nodeName.toLowerCase();
-
- if (!Object.keys(allowList).includes(elementName)) {
- element.remove();
- continue;
- }
-
- const attributeList = [].concat(...element.attributes);
- const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []);
- attributeList.forEach(attribute => {
- if (!allowedAttribute(attribute, allowedAttributes)) {
- element.removeAttribute(attribute.nodeName);
- }
- });
- }
-
- return createdDocument.body.innerHTML;
- }
/**
- * --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): tooltip.js
- * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
- * --------------------------------------------------------------------------
- */
- /**
- * ------------------------------------------------------------------------
* Constants
- * ------------------------------------------------------------------------
*/
const NAME = 'tooltip';
- const DATA_KEY = 'bs.tooltip';
- const EVENT_KEY = `.${DATA_KEY}`;
- const CLASS_PREFIX = 'bs-tooltip';
const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']);
- const DefaultType = {
- animation: 'boolean',
- template: 'string',
- title: '(string|element|function)',
- trigger: 'string',
- delay: '(number|object)',
- html: 'boolean',
- selector: '(string|boolean)',
- placement: '(string|function)',
- offset: '(array|string|function)',
- container: '(string|element|boolean)',
- fallbackPlacements: 'array',
- boundary: '(string|element)',
- customClass: '(string|function)',
- sanitize: 'boolean',
- sanitizeFn: '(null|function)',
- allowList: 'object',
- popperConfig: '(null|object|function)'
- };
+ const CLASS_NAME_FADE = 'fade';
+ const CLASS_NAME_MODAL = 'modal';
+ const CLASS_NAME_SHOW = 'show';
+ const SELECTOR_TOOLTIP_INNER = '.tooltip-inner';
+ const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`;
+ const EVENT_MODAL_HIDE = 'hide.bs.modal';
+ const TRIGGER_HOVER = 'hover';
+ const TRIGGER_FOCUS = 'focus';
+ const TRIGGER_CLICK = 'click';
+ const TRIGGER_MANUAL = 'manual';
+ const EVENT_HIDE = 'hide';
+ const EVENT_HIDDEN = 'hidden';
+ const EVENT_SHOW = 'show';
+ const EVENT_SHOWN = 'shown';
+ const EVENT_INSERTED = 'inserted';
+ const EVENT_CLICK = 'click';
+ const EVENT_FOCUSIN = 'focusin';
+ const EVENT_FOCUSOUT = 'focusout';
+ const EVENT_MOUSEENTER = 'mouseenter';
+ const EVENT_MOUSELEAVE = 'mouseleave';
const AttachmentMap = {
AUTO: 'auto',
TOP: 'top',
- RIGHT: isRTL() ? 'left' : 'right',
+ RIGHT: index_js.isRTL() ? 'left' : 'right',
BOTTOM: 'bottom',
- LEFT: isRTL() ? 'right' : 'left'
+ LEFT: index_js.isRTL() ? 'right' : 'left'
};
const Default = {
+ allowList: sanitizer_js.DefaultAllowlist,
animation: true,
- template: '<div class="tooltip" role="tooltip">' + '<div class="tooltip-arrow"></div>' + '<div class="tooltip-inner"></div>' + '</div>',
- trigger: 'hover focus',
- title: '',
+ boundary: 'clippingParents',
+ container: false,
+ customClass: '',
delay: 0,
+ fallbackPlacements: ['top', 'right', 'bottom', 'left'],
html: false,
- selector: false,
+ offset: [0, 6],
placement: 'top',
- offset: [0, 0],
- container: false,
- fallbackPlacements: ['top', 'right', 'bottom', 'left'],
- boundary: 'clippingParents',
- customClass: '',
+ popperConfig: null,
sanitize: true,
sanitizeFn: null,
- allowList: DefaultAllowlist,
- popperConfig: null
+ selector: false,
+ template: '<div class="tooltip" role="tooltip">' + '<div class="tooltip-arrow"></div>' + '<div class="tooltip-inner"></div>' + '</div>',
+ title: '',
+ trigger: 'hover focus'
};
- const Event = {
- HIDE: `hide${EVENT_KEY}`,
- HIDDEN: `hidden${EVENT_KEY}`,
- SHOW: `show${EVENT_KEY}`,
- SHOWN: `shown${EVENT_KEY}`,
- INSERTED: `inserted${EVENT_KEY}`,
- CLICK: `click${EVENT_KEY}`,
- FOCUSIN: `focusin${EVENT_KEY}`,
- FOCUSOUT: `focusout${EVENT_KEY}`,
- MOUSEENTER: `mouseenter${EVENT_KEY}`,
- MOUSELEAVE: `mouseleave${EVENT_KEY}`
+ const DefaultType = {
+ allowList: 'object',
+ animation: 'boolean',
+ boundary: '(string|element)',
+ container: '(string|element|boolean)',
+ customClass: '(string|function)',
+ delay: '(number|object)',
+ fallbackPlacements: 'array',
+ html: 'boolean',
+ offset: '(array|string|function)',
+ placement: '(string|function)',
+ popperConfig: '(null|object|function)',
+ sanitize: 'boolean',
+ sanitizeFn: '(null|function)',
+ selector: '(string|boolean)',
+ template: 'string',
+ title: '(string|element|function)',
+ trigger: 'string'
};
- const CLASS_NAME_FADE = 'fade';
- const CLASS_NAME_MODAL = 'modal';
- const CLASS_NAME_SHOW = 'show';
- const HOVER_STATE_SHOW = 'show';
- const HOVER_STATE_OUT = 'out';
- const SELECTOR_TOOLTIP_INNER = '.tooltip-inner';
- const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`;
- const EVENT_MODAL_HIDE = 'hide.bs.modal';
- const TRIGGER_HOVER = 'hover';
- const TRIGGER_FOCUS = 'focus';
- const TRIGGER_CLICK = 'click';
- const TRIGGER_MANUAL = 'manual';
+
/**
- * ------------------------------------------------------------------------
- * Class Definition
- * ------------------------------------------------------------------------
+ * Class definition
*/
- class Tooltip extends BaseComponent__default.default {
+ class Tooltip extends BaseComponent {
constructor(element, config) {
if (typeof Popper__namespace === 'undefined') {
throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org)');
}
+ super(element, config);
- super(element); // private
-
+ // Private
this._isEnabled = true;
this._timeout = 0;
- this._hoverState = '';
+ this._isHovered = null;
this._activeTrigger = {};
- this._popper = null; // Protected
+ this._popper = null;
+ this._templateFactory = null;
+ this._newContent = null;
- this._config = this._getConfig(config);
+ // Protected
this.tip = null;
-
this._setListeners();
- } // Getters
-
+ if (!this._config.selector) {
+ this._fixTitle();
+ }
+ }
+ // Getters
static get Default() {
return Default;
}
-
+ static get DefaultType() {
+ return DefaultType;
+ }
static get NAME() {
return NAME;
}
- static get Event() {
- return Event;
- }
-
- static get DefaultType() {
- return DefaultType;
- } // Public
-
-
+ // Public
enable() {
this._isEnabled = true;
}
-
disable() {
this._isEnabled = false;
}
-
toggleEnabled() {
this._isEnabled = !this._isEnabled;
}
-
- toggle(event) {
+ toggle() {
if (!this._isEnabled) {
return;
}
-
- if (event) {
- const context = this._initializeOnDelegatedTarget(event);
-
- context._activeTrigger.click = !context._activeTrigger.click;
-
- if (context._isWithActiveTrigger()) {
- context._enter(null, context);
- } else {
- context._leave(null, context);
- }
- } else {
- if (this.getTipElement().classList.contains(CLASS_NAME_SHOW)) {
- this._leave(null, this);
-
- return;
- }
-
- this._enter(null, this);
+ this._activeTrigger.click = !this._activeTrigger.click;
+ if (this._isShown()) {
+ this._leave();
+ return;
}
+ this._enter();
}
-
dispose() {
clearTimeout(this._timeout);
- EventHandler__default.default.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);
-
- if (this.tip) {
- this.tip.remove();
+ EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);
+ if (this._element.getAttribute('data-bs-original-title')) {
+ this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'));
}
-
this._disposePopper();
-
super.dispose();
}
-
show() {
if (this._element.style.display === 'none') {
throw new Error('Please use show on visible elements');
}
-
- if (!(this.isWithContent() && this._isEnabled)) {
+ if (!(this._isWithContent() && this._isEnabled)) {
return;
}
-
- const showEvent = EventHandler__default.default.trigger(this._element, this.constructor.Event.SHOW);
- const shadowRoot = findShadowRoot(this._element);
- const isInTheDom = shadowRoot === null ? this._element.ownerDocument.documentElement.contains(this._element) : shadowRoot.contains(this._element);
-
+ const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW));
+ const shadowRoot = index_js.findShadowRoot(this._element);
+ const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element);
if (showEvent.defaultPrevented || !isInTheDom) {
return;
- } // A trick to recreate a tooltip in case a new title is given by using the NOT documented `data-bs-original-title`
- // This will be removed later in favor of a `setContent` method
-
-
- if (this.constructor.NAME === 'tooltip' && this.tip && this.getTitle() !== this.tip.querySelector(SELECTOR_TOOLTIP_INNER).innerHTML) {
- this._disposePopper();
-
- this.tip.remove();
- this.tip = null;
- }
-
- const tip = this.getTipElement();
- const tipId = getUID(this.constructor.NAME);
- tip.setAttribute('id', tipId);
-
- this._element.setAttribute('aria-describedby', tipId);
-
- if (this._config.animation) {
- tip.classList.add(CLASS_NAME_FADE);
}
- const placement = typeof this._config.placement === 'function' ? this._config.placement.call(this, tip, this._element) : this._config.placement;
-
- const attachment = this._getAttachment(placement);
-
- this._addAttachmentClass(attachment);
-
+ // TODO: v6 remove this or make it optional
+ this._disposePopper();
+ const tip = this._getTipElement();
+ this._element.setAttribute('aria-describedby', tip.getAttribute('id'));
const {
container
} = this._config;
- Data__default.default.set(tip, this.constructor.DATA_KEY, this);
-
if (!this._element.ownerDocument.documentElement.contains(this.tip)) {
container.append(tip);
- EventHandler__default.default.trigger(this._element, this.constructor.Event.INSERTED);
+ EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED));
}
-
- if (this._popper) {
- this._popper.update();
- } else {
- this._popper = Popper__namespace.createPopper(this._element, tip, this._getPopperConfig(attachment));
- }
-
+ this._popper = this._createPopper(tip);
tip.classList.add(CLASS_NAME_SHOW);
- const customClass = this._resolvePossibleFunction(this._config.customClass);
-
- if (customClass) {
- tip.classList.add(...customClass.split(' '));
- } // If this is a touch-enabled device we add extra
+ // If this is a touch-enabled device we add extra
// empty mouseover listeners to the body's immediate children;
// only needed because of broken event delegation on iOS
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
-
-
if ('ontouchstart' in document.documentElement) {
- [].concat(...document.body.children).forEach(element => {
- EventHandler__default.default.on(element, 'mouseover', noop);
- });
+ for (const element of [].concat(...document.body.children)) {
+ EventHandler.on(element, 'mouseover', index_js.noop);
+ }
}
-
const complete = () => {
- const prevHoverState = this._hoverState;
- this._hoverState = null;
- EventHandler__default.default.trigger(this._element, this.constructor.Event.SHOWN);
-
- if (prevHoverState === HOVER_STATE_OUT) {
- this._leave(null, this);
+ EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN));
+ if (this._isHovered === false) {
+ this._leave();
}
+ this._isHovered = false;
};
-
- const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE);
-
- this._queueCallback(complete, this.tip, isAnimated);
+ this._queueCallback(complete, this.tip, this._isAnimated());
}
-
hide() {
- if (!this._popper) {
+ if (!this._isShown()) {
return;
}
-
- const tip = this.getTipElement();
-
- const complete = () => {
- if (this._isWithActiveTrigger()) {
- return;
- }
-
- if (this._hoverState !== HOVER_STATE_SHOW) {
- tip.remove();
- }
-
- this._cleanTipClass();
-
- this._element.removeAttribute('aria-describedby');
-
- EventHandler__default.default.trigger(this._element, this.constructor.Event.HIDDEN);
-
- this._disposePopper();
- };
-
- const hideEvent = EventHandler__default.default.trigger(this._element, this.constructor.Event.HIDE);
-
+ const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE));
if (hideEvent.defaultPrevented) {
return;
}
+ const tip = this._getTipElement();
+ tip.classList.remove(CLASS_NAME_SHOW);
- tip.classList.remove(CLASS_NAME_SHOW); // If this is a touch-enabled device we remove the extra
+ // If this is a touch-enabled device we remove the extra
// empty mouseover listeners we added for iOS support
-
if ('ontouchstart' in document.documentElement) {
- [].concat(...document.body.children).forEach(element => EventHandler__default.default.off(element, 'mouseover', noop));
+ for (const element of [].concat(...document.body.children)) {
+ EventHandler.off(element, 'mouseover', index_js.noop);
+ }
}
-
this._activeTrigger[TRIGGER_CLICK] = false;
this._activeTrigger[TRIGGER_FOCUS] = false;
this._activeTrigger[TRIGGER_HOVER] = false;
- const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE);
-
- this._queueCallback(complete, this.tip, isAnimated);
+ this._isHovered = null; // it is a trick to support manual triggering
- this._hoverState = '';
+ const complete = () => {
+ if (this._isWithActiveTrigger()) {
+ return;
+ }
+ if (!this._isHovered) {
+ this._disposePopper();
+ }
+ this._element.removeAttribute('aria-describedby');
+ EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN));
+ };
+ this._queueCallback(complete, this.tip, this._isAnimated());
}
-
update() {
- if (this._popper !== null) {
+ if (this._popper) {
this._popper.update();
}
- } // Protected
-
-
- isWithContent() {
- return Boolean(this.getTitle());
}
- getTipElement() {
- if (this.tip) {
- return this.tip;
+ // Protected
+ _isWithContent() {
+ return Boolean(this._getTitle());
+ }
+ _getTipElement() {
+ if (!this.tip) {
+ this.tip = this._createTipElement(this._newContent || this._getContentForTemplate());
}
-
- const element = document.createElement('div');
- element.innerHTML = this._config.template;
- const tip = element.children[0];
- this.setContent(tip);
- tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW);
- this.tip = tip;
return this.tip;
}
+ _createTipElement(content) {
+ const tip = this._getTemplateFactory(content).toHtml();
- setContent(tip) {
- this._sanitizeAndSetContent(tip, this.getTitle(), SELECTOR_TOOLTIP_INNER);
- }
-
- _sanitizeAndSetContent(template, content, selector) {
- const templateElement = SelectorEngine__default.default.findOne(selector, template);
-
- if (!content && templateElement) {
- templateElement.remove();
- return;
- } // we use append for html objects to maintain js events
-
-
- this.setElementContent(templateElement, content);
- }
-
- setElementContent(element, content) {
- if (element === null) {
- return;
+ // TODO: remove this check in v6
+ if (!tip) {
+ return null;
}
-
- if (isElement(content)) {
- content = getElement(content); // content is a DOM node or a jQuery
-
- if (this._config.html) {
- if (content.parentNode !== element) {
- element.innerHTML = '';
- element.append(content);
- }
- } else {
- element.textContent = content.textContent;
- }
-
- return;
+ tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW);
+ // TODO: v6 the following can be achieved with CSS only
+ tip.classList.add(`bs-${this.constructor.NAME}-auto`);
+ const tipId = index_js.getUID(this.constructor.NAME).toString();
+ tip.setAttribute('id', tipId);
+ if (this._isAnimated()) {
+ tip.classList.add(CLASS_NAME_FADE);
}
-
- if (this._config.html) {
- if (this._config.sanitize) {
- content = sanitizeHtml(content, this._config.allowList, this._config.sanitizeFn);
- }
-
- element.innerHTML = content;
+ return tip;
+ }
+ setContent(content) {
+ this._newContent = content;
+ if (this._isShown()) {
+ this._disposePopper();
+ this.show();
+ }
+ }
+ _getTemplateFactory(content) {
+ if (this._templateFactory) {
+ this._templateFactory.changeContent(content);
} else {
- element.textContent = content;
+ this._templateFactory = new TemplateFactory({
+ ...this._config,
+ // the `content` var has to be after `this._config`
+ // to override config.content in case of popover
+ content,
+ extraClass: this._resolvePossibleFunction(this._config.customClass)
+ });
}
+ return this._templateFactory;
}
-
- getTitle() {
- const title = this._element.getAttribute('data-bs-original-title') || this._config.title;
-
- return this._resolvePossibleFunction(title);
+ _getContentForTemplate() {
+ return {
+ [SELECTOR_TOOLTIP_INNER]: this._getTitle()
+ };
}
-
- updateAttachment(attachment) {
- if (attachment === 'right') {
- return 'end';
- }
-
- if (attachment === 'left') {
- return 'start';
- }
-
- return attachment;
- } // Private
-
-
- _initializeOnDelegatedTarget(event, context) {
- return context || this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig());
+ _getTitle() {
+ return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title');
}
+ // Private
+ _initializeOnDelegatedTarget(event) {
+ return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig());
+ }
+ _isAnimated() {
+ return this._config.animation || this.tip && this.tip.classList.contains(CLASS_NAME_FADE);
+ }
+ _isShown() {
+ return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW);
+ }
+ _createPopper(tip) {
+ const placement = index_js.execute(this._config.placement, [this, tip, this._element]);
+ const attachment = AttachmentMap[placement.toUpperCase()];
+ return Popper__namespace.createPopper(this._element, tip, this._getPopperConfig(attachment));
+ }
_getOffset() {
const {
offset
} = this._config;
-
if (typeof offset === 'string') {
- return offset.split(',').map(val => Number.parseInt(val, 10));
+ return offset.split(',').map(value => Number.parseInt(value, 10));
}
-
if (typeof offset === 'function') {
return popperData => offset(popperData, this._element);
}
-
return offset;
}
-
- _resolvePossibleFunction(content) {
- return typeof content === 'function' ? content.call(this._element) : content;
+ _resolvePossibleFunction(arg) {
+ return index_js.execute(arg, [this._element]);
}
-
_getPopperConfig(attachment) {
const defaultBsPopperConfig = {
placement: attachment,
@@ -749,256 +375,170 @@
element: `.${this.constructor.NAME}-arrow`
}
}, {
- name: 'onChange',
+ name: 'preSetPlacement',
enabled: true,
- phase: 'afterWrite',
- fn: data => this._handlePopperPlacementChange(data)
- }],
- onFirstUpdate: data => {
- if (data.options.placement !== data.placement) {
- this._handlePopperPlacementChange(data);
+ phase: 'beforeMain',
+ fn: data => {
+ // Pre-set Popper's placement attribute in order to read the arrow sizes properly.
+ // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement
+ this._getTipElement().setAttribute('data-popper-placement', data.state.placement);
}
- }
+ }]
};
- return { ...defaultBsPopperConfig,
- ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)
+ return {
+ ...defaultBsPopperConfig,
+ ...index_js.execute(this._config.popperConfig, [defaultBsPopperConfig])
};
}
-
- _addAttachmentClass(attachment) {
- this.getTipElement().classList.add(`${this._getBasicClassPrefix()}-${this.updateAttachment(attachment)}`);
- }
-
- _getAttachment(placement) {
- return AttachmentMap[placement.toUpperCase()];
- }
-
_setListeners() {
const triggers = this._config.trigger.split(' ');
-
- triggers.forEach(trigger => {
+ for (const trigger of triggers) {
if (trigger === 'click') {
- EventHandler__default.default.on(this._element, this.constructor.Event.CLICK, this._config.selector, event => this.toggle(event));
+ EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK), this._config.selector, event => {
+ const context = this._initializeOnDelegatedTarget(event);
+ context.toggle();
+ });
} else if (trigger !== TRIGGER_MANUAL) {
- const eventIn = trigger === TRIGGER_HOVER ? this.constructor.Event.MOUSEENTER : this.constructor.Event.FOCUSIN;
- const eventOut = trigger === TRIGGER_HOVER ? this.constructor.Event.MOUSELEAVE : this.constructor.Event.FOCUSOUT;
- EventHandler__default.default.on(this._element, eventIn, this._config.selector, event => this._enter(event));
- EventHandler__default.default.on(this._element, eventOut, this._config.selector, event => this._leave(event));
+ const eventIn = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSEENTER) : this.constructor.eventName(EVENT_FOCUSIN);
+ const eventOut = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSELEAVE) : this.constructor.eventName(EVENT_FOCUSOUT);
+ EventHandler.on(this._element, eventIn, this._config.selector, event => {
+ const context = this._initializeOnDelegatedTarget(event);
+ context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true;
+ context._enter();
+ });
+ EventHandler.on(this._element, eventOut, this._config.selector, event => {
+ const context = this._initializeOnDelegatedTarget(event);
+ context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget);
+ context._leave();
+ });
}
- });
-
+ }
this._hideModalHandler = () => {
if (this._element) {
this.hide();
}
};
-
- EventHandler__default.default.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);
-
- if (this._config.selector) {
- this._config = { ...this._config,
- trigger: 'manual',
- selector: ''
- };
- } else {
- this._fixTitle();
- }
+ EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler);
}
-
_fixTitle() {
const title = this._element.getAttribute('title');
-
- const originalTitleType = typeof this._element.getAttribute('data-bs-original-title');
-
- if (title || originalTitleType !== 'string') {
- this._element.setAttribute('data-bs-original-title', title || '');
-
- if (title && !this._element.getAttribute('aria-label') && !this._element.textContent) {
- this._element.setAttribute('aria-label', title);
- }
-
- this._element.setAttribute('title', '');
- }
- }
-
- _enter(event, context) {
- context = this._initializeOnDelegatedTarget(event, context);
-
- if (event) {
- context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true;
- }
-
- if (context.getTipElement().classList.contains(CLASS_NAME_SHOW) || context._hoverState === HOVER_STATE_SHOW) {
- context._hoverState = HOVER_STATE_SHOW;
+ if (!title) {
return;
}
-
- clearTimeout(context._timeout);
- context._hoverState = HOVER_STATE_SHOW;
-
- if (!context._config.delay || !context._config.delay.show) {
- context.show();
- return;
+ if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) {
+ this._element.setAttribute('aria-label', title);
}
-
- context._timeout = setTimeout(() => {
- if (context._hoverState === HOVER_STATE_SHOW) {
- context.show();
- }
- }, context._config.delay.show);
+ this._element.setAttribute('data-bs-original-title', title); // DO NOT USE IT. Is only for backwards compatibility
+ this._element.removeAttribute('title');
}
-
- _leave(event, context) {
- context = this._initializeOnDelegatedTarget(event, context);
-
- if (event) {
- context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget);
- }
-
- if (context._isWithActiveTrigger()) {
+ _enter() {
+ if (this._isShown() || this._isHovered) {
+ this._isHovered = true;
return;
}
-
- clearTimeout(context._timeout);
- context._hoverState = HOVER_STATE_OUT;
-
- if (!context._config.delay || !context._config.delay.hide) {
- context.hide();
+ this._isHovered = true;
+ this._setTimeout(() => {
+ if (this._isHovered) {
+ this.show();
+ }
+ }, this._config.delay.show);
+ }
+ _leave() {
+ if (this._isWithActiveTrigger()) {
return;
}
-
- context._timeout = setTimeout(() => {
- if (context._hoverState === HOVER_STATE_OUT) {
- context.hide();
+ this._isHovered = false;
+ this._setTimeout(() => {
+ if (!this._isHovered) {
+ this.hide();
}
- }, context._config.delay.hide);
+ }, this._config.delay.hide);
+ }
+ _setTimeout(handler, timeout) {
+ clearTimeout(this._timeout);
+ this._timeout = setTimeout(handler, timeout);
}
-
_isWithActiveTrigger() {
- for (const trigger in this._activeTrigger) {
- if (this._activeTrigger[trigger]) {
- return true;
- }
- }
-
- return false;
+ return Object.values(this._activeTrigger).includes(true);
}
-
_getConfig(config) {
- const dataAttributes = Manipulator__default.default.getDataAttributes(this._element);
- Object.keys(dataAttributes).forEach(dataAttr => {
- if (DISALLOWED_ATTRIBUTES.has(dataAttr)) {
- delete dataAttributes[dataAttr];
+ const dataAttributes = Manipulator.getDataAttributes(this._element);
+ for (const dataAttribute of Object.keys(dataAttributes)) {
+ if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {
+ delete dataAttributes[dataAttribute];
}
- });
- config = { ...this.constructor.Default,
+ }
+ config = {
...dataAttributes,
...(typeof config === 'object' && config ? config : {})
};
- config.container = config.container === false ? document.body : getElement(config.container);
-
+ config = this._mergeConfigObj(config);
+ config = this._configAfterMerge(config);
+ this._typeCheckConfig(config);
+ return config;
+ }
+ _configAfterMerge(config) {
+ config.container = config.container === false ? document.body : index_js.getElement(config.container);
if (typeof config.delay === 'number') {
config.delay = {
show: config.delay,
hide: config.delay
};
}
-
if (typeof config.title === 'number') {
config.title = config.title.toString();
}
-
if (typeof config.content === 'number') {
config.content = config.content.toString();
}
-
- typeCheckConfig(NAME, config, this.constructor.DefaultType);
-
- if (config.sanitize) {
- config.template = sanitizeHtml(config.template, config.allowList, config.sanitizeFn);
- }
-
return config;
}
-
_getDelegateConfig() {
const config = {};
-
- for (const key in this._config) {
- if (this.constructor.Default[key] !== this._config[key]) {
- config[key] = this._config[key];
+ for (const [key, value] of Object.entries(this._config)) {
+ if (this.constructor.Default[key] !== value) {
+ config[key] = value;
}
- } // In the future can be replaced with:
+ }
+ config.selector = false;
+ config.trigger = 'manual';
+
+ // In the future can be replaced with:
// const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])
// `Object.fromEntries(keysWithDifferentValues)`
-
-
return config;
}
-
- _cleanTipClass() {
- const tip = this.getTipElement();
- const basicClassPrefixRegex = new RegExp(`(^|\\s)${this._getBasicClassPrefix()}\\S+`, 'g');
- const tabClass = tip.getAttribute('class').match(basicClassPrefixRegex);
-
- if (tabClass !== null && tabClass.length > 0) {
- tabClass.map(token => token.trim()).forEach(tClass => tip.classList.remove(tClass));
- }
- }
-
- _getBasicClassPrefix() {
- return CLASS_PREFIX;
- }
-
- _handlePopperPlacementChange(popperData) {
- const {
- state
- } = popperData;
-
- if (!state) {
- return;
- }
-
- this.tip = state.elements.popper;
-
- this._cleanTipClass();
-
- this._addAttachmentClass(this._getAttachment(state.placement));
- }
-
_disposePopper() {
if (this._popper) {
this._popper.destroy();
-
this._popper = null;
}
- } // Static
-
+ if (this.tip) {
+ this.tip.remove();
+ this.tip = null;
+ }
+ }
+ // Static
static jQueryInterface(config) {
return this.each(function () {
const data = Tooltip.getOrCreateInstance(this, config);
-
- if (typeof config === 'string') {
- if (typeof data[config] === 'undefined') {
- throw new TypeError(`No method named "${config}"`);
- }
-
- data[config]();
+ if (typeof config !== 'string') {
+ return;
+ }
+ if (typeof data[config] === 'undefined') {
+ throw new TypeError(`No method named "${config}"`);
}
+ data[config]();
});
}
-
}
+
/**
- * ------------------------------------------------------------------------
* jQuery
- * ------------------------------------------------------------------------
- * add .Tooltip to jQuery only if jQuery is present
*/
-
- defineJQueryPlugin(Tooltip);
+ index_js.defineJQueryPlugin(Tooltip);
return Tooltip;
diff --git a/js/dist/tooltip.js.map b/js/dist/tooltip.js.map
index 8a85bda05..75f426044 100644
--- a/js/dist/tooltip.js.map
+++ b/js/dist/tooltip.js.map
@@ -1 +1 @@
-{"version":3,"file":"tooltip.js","sources":["../src/util/index.js","../src/util/sanitizer.js","../src/tooltip.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1000000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n// Shoutout AngusCroll (https://goo.gl/pxwQGp)\nconst toType = obj => {\n if (obj === null || obj === undefined) {\n return `${obj}`\n }\n\n return {}.toString.call(obj).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * --------------------------------------------------------------------------\n * Public Util Api\n * --------------------------------------------------------------------------\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getSelector = element => {\n let selector = element.getAttribute('data-bs-target')\n\n if (!selector || selector === '#') {\n let hrefAttr = element.getAttribute('href')\n\n // The only valid content that could double as a selector are IDs or classes,\n // so everything starting with `#` or `.`. If a \"real\" URL is used as the selector,\n // `document.querySelector` will rightfully complain it is invalid.\n // See https://github.com/twbs/bootstrap/issues/32273\n if (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {\n return null\n }\n\n // Just in case some CMS puts out a full URL with the anchor appended\n if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {\n hrefAttr = `#${hrefAttr.split('#')[1]}`\n }\n\n selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null\n }\n\n return selector\n}\n\nconst getSelectorFromElement = element => {\n const selector = getSelector(element)\n\n if (selector) {\n return document.querySelector(selector) ? selector : null\n }\n\n return null\n}\n\nconst getElementFromSelector = element => {\n const selector = getSelector(element)\n\n return selector ? document.querySelector(selector) : null\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = obj => {\n if (!obj || typeof obj !== 'object') {\n return false\n }\n\n if (typeof obj.jquery !== 'undefined') {\n obj = obj[0]\n }\n\n return typeof obj.nodeType !== 'undefined'\n}\n\nconst getElement = obj => {\n if (isElement(obj)) { // it's a jQuery object or a node element\n return obj.jquery ? obj[0] : obj\n }\n\n if (typeof obj === 'string' && obj.length > 0) {\n return document.querySelector(obj)\n }\n\n return null\n}\n\nconst typeCheckConfig = (componentName, config, configTypes) => {\n Object.keys(configTypes).forEach(property => {\n const expectedTypes = configTypes[property]\n const value = config[property]\n const valueType = value && isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${componentName.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n })\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n return getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n // eslint-disable-next-line no-unused-expressions\n element.offsetHeight\n}\n\nconst getjQuery = () => {\n const { jQuery } = window\n\n if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n DOMContentLoadedCallbacks.forEach(callback => callback())\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = callback => {\n if (typeof callback === 'function') {\n callback()\n }\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element depending on the direction and if cycle is allowed\n if (index === -1) {\n return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]\n }\n\n const listLength = list.length\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n getElement,\n getUID,\n getSelectorFromElement,\n getElementFromSelector,\n getTransitionDurationFromElement,\n triggerTransitionEnd,\n isElement,\n typeCheckConfig,\n isVisible,\n isDisabled,\n findShadowRoot,\n noop,\n getNextActiveElement,\n reflow,\n getjQuery,\n onDOMContentLoaded,\n isRTL,\n defineJQueryPlugin,\n execute,\n executeAfterTransition\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): util/sanitizer.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst uriAttributes = new Set([\n 'background',\n 'cite',\n 'href',\n 'itemtype',\n 'longdesc',\n 'poster',\n 'src',\n 'xlink:href'\n])\n\nconst ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i\n\n/**\n * A pattern that recognizes a commonly useful subset of URLs that are safe.\n *\n * Shoutout to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts\n */\nconst SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i\n\n/**\n * A pattern that matches safe data URLs. Only matches image, video and audio types.\n *\n * Shoutout to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts\n */\nconst DATA_URL_PATTERN = /^data:(?:image\\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\\/(?:mpeg|mp4|ogg|webm)|audio\\/(?:mp3|oga|ogg|opus));base64,[\\d+/a-z]+=*$/i\n\nconst allowedAttribute = (attribute, allowedAttributeList) => {\n const attributeName = attribute.nodeName.toLowerCase()\n\n if (allowedAttributeList.includes(attributeName)) {\n if (uriAttributes.has(attributeName)) {\n return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue) || DATA_URL_PATTERN.test(attribute.nodeValue))\n }\n\n return true\n }\n\n const regExp = allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp)\n\n // Check if a regular expression validates the attribute.\n for (let i = 0, len = regExp.length; i < len; i++) {\n if (regExp[i].test(attributeName)) {\n return true\n }\n }\n\n return false\n}\n\nexport const DefaultAllowlist = {\n // Global attributes allowed on any supplied element below.\n '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n a: ['target', 'href', 'title', 'rel'],\n area: [],\n b: [],\n br: [],\n col: [],\n code: [],\n div: [],\n em: [],\n hr: [],\n h1: [],\n h2: [],\n h3: [],\n h4: [],\n h5: [],\n h6: [],\n i: [],\n img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],\n li: [],\n ol: [],\n p: [],\n pre: [],\n s: [],\n small: [],\n span: [],\n sub: [],\n sup: [],\n strong: [],\n u: [],\n ul: []\n}\n\nexport function sanitizeHtml(unsafeHtml, allowList, sanitizeFn) {\n if (!unsafeHtml.length) {\n return unsafeHtml\n }\n\n if (sanitizeFn && typeof sanitizeFn === 'function') {\n return sanitizeFn(unsafeHtml)\n }\n\n const domParser = new window.DOMParser()\n const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html')\n const elements = [].concat(...createdDocument.body.querySelectorAll('*'))\n\n for (let i = 0, len = elements.length; i < len; i++) {\n const element = elements[i]\n const elementName = element.nodeName.toLowerCase()\n\n if (!Object.keys(allowList).includes(elementName)) {\n element.remove()\n\n continue\n }\n\n const attributeList = [].concat(...element.attributes)\n const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || [])\n\n attributeList.forEach(attribute => {\n if (!allowedAttribute(attribute, allowedAttributes)) {\n element.removeAttribute(attribute.nodeName)\n }\n })\n }\n\n return createdDocument.body.innerHTML\n}\n","/**\n * --------------------------------------------------------------------------\n * Bootstrap (v5.1.3): tooltip.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport * as Popper from '@popperjs/core'\n\nimport {\n defineJQueryPlugin,\n findShadowRoot,\n getElement,\n getUID,\n isElement,\n isRTL,\n noop,\n typeCheckConfig\n} from './util/index'\nimport { DefaultAllowlist, sanitizeHtml } from './util/sanitizer'\nimport Data from './dom/data'\nimport EventHandler from './dom/event-handler'\nimport Manipulator from './dom/manipulator'\nimport SelectorEngine from './dom/selector-engine'\nimport BaseComponent from './base-component'\n\n/**\n * ------------------------------------------------------------------------\n * Constants\n * ------------------------------------------------------------------------\n */\n\nconst NAME = 'tooltip'\nconst DATA_KEY = 'bs.tooltip'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst CLASS_PREFIX = 'bs-tooltip'\nconst DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn'])\n\nconst DefaultType = {\n animation: 'boolean',\n template: 'string',\n title: '(string|element|function)',\n trigger: 'string',\n delay: '(number|object)',\n html: 'boolean',\n selector: '(string|boolean)',\n placement: '(string|function)',\n offset: '(array|string|function)',\n container: '(string|element|boolean)',\n fallbackPlacements: 'array',\n boundary: '(string|element)',\n customClass: '(string|function)',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n allowList: 'object',\n popperConfig: '(null|object|function)'\n}\n\nconst AttachmentMap = {\n AUTO: 'auto',\n TOP: 'top',\n RIGHT: isRTL() ? 'left' : 'right',\n BOTTOM: 'bottom',\n LEFT: isRTL() ? 'right' : 'left'\n}\n\nconst Default = {\n animation: true,\n template: '<div class=\"tooltip\" role=\"tooltip\">' +\n '<div class=\"tooltip-arrow\"></div>' +\n '<div class=\"tooltip-inner\"></div>' +\n '</div>',\n trigger: 'hover focus',\n title: '',\n delay: 0,\n html: false,\n selector: false,\n placement: 'top',\n offset: [0, 0],\n container: false,\n fallbackPlacements: ['top', 'right', 'bottom', 'left'],\n boundary: 'clippingParents',\n customClass: '',\n sanitize: true,\n sanitizeFn: null,\n allowList: DefaultAllowlist,\n popperConfig: null\n}\n\nconst Event = {\n HIDE: `hide${EVENT_KEY}`,\n HIDDEN: `hidden${EVENT_KEY}`,\n SHOW: `show${EVENT_KEY}`,\n SHOWN: `shown${EVENT_KEY}`,\n INSERTED: `inserted${EVENT_KEY}`,\n CLICK: `click${EVENT_KEY}`,\n FOCUSIN: `focusin${EVENT_KEY}`,\n FOCUSOUT: `focusout${EVENT_KEY}`,\n MOUSEENTER: `mouseenter${EVENT_KEY}`,\n MOUSELEAVE: `mouseleave${EVENT_KEY}`\n}\n\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_MODAL = 'modal'\nconst CLASS_NAME_SHOW = 'show'\n\nconst HOVER_STATE_SHOW = 'show'\nconst HOVER_STATE_OUT = 'out'\n\nconst SELECTOR_TOOLTIP_INNER = '.tooltip-inner'\nconst SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`\n\nconst EVENT_MODAL_HIDE = 'hide.bs.modal'\n\nconst TRIGGER_HOVER = 'hover'\nconst TRIGGER_FOCUS = 'focus'\nconst TRIGGER_CLICK = 'click'\nconst TRIGGER_MANUAL = 'manual'\n\n/**\n * ------------------------------------------------------------------------\n * Class Definition\n * ------------------------------------------------------------------------\n */\n\nclass Tooltip extends BaseComponent {\n constructor(element, config) {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s tooltips require Popper (https://popper.js.org)')\n }\n\n super(element)\n\n // private\n this._isEnabled = true\n this._timeout = 0\n this._hoverState = ''\n this._activeTrigger = {}\n this._popper = null\n\n // Protected\n this._config = this._getConfig(config)\n this.tip = null\n\n this._setListeners()\n }\n\n // Getters\n\n static get Default() {\n return Default\n }\n\n static get NAME() {\n return NAME\n }\n\n static get Event() {\n return Event\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n // Public\n\n enable() {\n this._isEnabled = true\n }\n\n disable() {\n this._isEnabled = false\n }\n\n toggleEnabled() {\n this._isEnabled = !this._isEnabled\n }\n\n toggle(event) {\n if (!this._isEnabled) {\n return\n }\n\n if (event) {\n const context = this._initializeOnDelegatedTarget(event)\n\n context._activeTrigger.click = !context._activeTrigger.click\n\n if (context._isWithActiveTrigger()) {\n context._enter(null, context)\n } else {\n context._leave(null, context)\n }\n } else {\n if (this.getTipElement().classList.contains(CLASS_NAME_SHOW)) {\n this._leave(null, this)\n return\n }\n\n this._enter(null, this)\n }\n }\n\n dispose() {\n clearTimeout(this._timeout)\n\n EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler)\n\n if (this.tip) {\n this.tip.remove()\n }\n\n this._disposePopper()\n super.dispose()\n }\n\n show() {\n if (this._element.style.display === 'none') {\n throw new Error('Please use show on visible elements')\n }\n\n if (!(this.isWithContent() && this._isEnabled)) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, this.constructor.Event.SHOW)\n const shadowRoot = findShadowRoot(this._element)\n const isInTheDom = shadowRoot === null ?\n this._element.ownerDocument.documentElement.contains(this._element) :\n shadowRoot.contains(this._element)\n\n if (showEvent.defaultPrevented || !isInTheDom) {\n return\n }\n\n // A trick to recreate a tooltip in case a new title is given by using the NOT documented `data-bs-original-title`\n // This will be removed later in favor of a `setContent` method\n if (this.constructor.NAME === 'tooltip' && this.tip && this.getTitle() !== this.tip.querySelector(SELECTOR_TOOLTIP_INNER).innerHTML) {\n this._disposePopper()\n this.tip.remove()\n this.tip = null\n }\n\n const tip = this.getTipElement()\n const tipId = getUID(this.constructor.NAME)\n\n tip.setAttribute('id', tipId)\n this._element.setAttribute('aria-describedby', tipId)\n\n if (this._config.animation) {\n tip.classList.add(CLASS_NAME_FADE)\n }\n\n const placement = typeof this._config.placement === 'function' ?\n this._config.placement.call(this, tip, this._element) :\n this._config.placement\n\n const attachment = this._getAttachment(placement)\n this._addAttachmentClass(attachment)\n\n const { container } = this._config\n Data.set(tip, this.constructor.DATA_KEY, this)\n\n if (!this._element.ownerDocument.documentElement.contains(this.tip)) {\n container.append(tip)\n EventHandler.trigger(this._element, this.constructor.Event.INSERTED)\n }\n\n if (this._popper) {\n this._popper.update()\n } else {\n this._popper = Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))\n }\n\n tip.classList.add(CLASS_NAME_SHOW)\n\n const customClass = this._resolvePossibleFunction(this._config.customClass)\n if (customClass) {\n tip.classList.add(...customClass.split(' '))\n }\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement) {\n [].concat(...document.body.children).forEach(element => {\n EventHandler.on(element, 'mouseover', noop)\n })\n }\n\n const complete = () => {\n const prevHoverState = this._hoverState\n\n this._hoverState = null\n EventHandler.trigger(this._element, this.constructor.Event.SHOWN)\n\n if (prevHoverState === HOVER_STATE_OUT) {\n this._leave(null, this)\n }\n }\n\n const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE)\n this._queueCallback(complete, this.tip, isAnimated)\n }\n\n hide() {\n if (!this._popper) {\n return\n }\n\n const tip = this.getTipElement()\n const complete = () => {\n if (this._isWithActiveTrigger()) {\n return\n }\n\n if (this._hoverState !== HOVER_STATE_SHOW) {\n tip.remove()\n }\n\n this._cleanTipClass()\n this._element.removeAttribute('aria-describedby')\n EventHandler.trigger(this._element, this.constructor.Event.HIDDEN)\n\n this._disposePopper()\n }\n\n const hideEvent = EventHandler.trigger(this._element, this.constructor.Event.HIDE)\n if (hideEvent.defaultPrevented) {\n return\n }\n\n tip.classList.remove(CLASS_NAME_SHOW)\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n [].concat(...document.body.children)\n .forEach(element => EventHandler.off(element, 'mouseover', noop))\n }\n\n this._activeTrigger[TRIGGER_CLICK] = false\n this._activeTrigger[TRIGGER_FOCUS] = false\n this._activeTrigger[TRIGGER_HOVER] = false\n\n const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE)\n this._queueCallback(complete, this.tip, isAnimated)\n this._hoverState = ''\n }\n\n update() {\n if (this._popper !== null) {\n this._popper.update()\n }\n }\n\n // Protected\n\n isWithContent() {\n return Boolean(this.getTitle())\n }\n\n getTipElement() {\n if (this.tip) {\n return this.tip\n }\n\n const element = document.createElement('div')\n element.innerHTML = this._config.template\n\n const tip = element.children[0]\n this.setContent(tip)\n tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW)\n\n this.tip = tip\n return this.tip\n }\n\n setContent(tip) {\n this._sanitizeAndSetContent(tip, this.getTitle(), SELECTOR_TOOLTIP_INNER)\n }\n\n _sanitizeAndSetContent(template, content, selector) {\n const templateElement = SelectorEngine.findOne(selector, template)\n\n if (!content && templateElement) {\n templateElement.remove()\n return\n }\n\n // we use append for html objects to maintain js events\n this.setElementContent(templateElement, content)\n }\n\n setElementContent(element, content) {\n if (element === null) {\n return\n }\n\n if (isElement(content)) {\n content = getElement(content)\n\n // content is a DOM node or a jQuery\n if (this._config.html) {\n if (content.parentNode !== element) {\n element.innerHTML = ''\n element.append(content)\n }\n } else {\n element.textContent = content.textContent\n }\n\n return\n }\n\n if (this._config.html) {\n if (this._config.sanitize) {\n content = sanitizeHtml(content, this._config.allowList, this._config.sanitizeFn)\n }\n\n element.innerHTML = content\n } else {\n element.textContent = content\n }\n }\n\n getTitle() {\n const title = this._element.getAttribute('data-bs-original-title') || this._config.title\n\n return this._resolvePossibleFunction(title)\n }\n\n updateAttachment(attachment) {\n if (attachment === 'right') {\n return 'end'\n }\n\n if (attachment === 'left') {\n return 'start'\n }\n\n return attachment\n }\n\n // Private\n\n _initializeOnDelegatedTarget(event, context) {\n return context || this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig())\n }\n\n _getOffset() {\n const { offset } = this._config\n\n if (typeof offset === 'string') {\n return offset.split(',').map(val => Number.parseInt(val, 10))\n }\n\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element)\n }\n\n return offset\n }\n\n _resolvePossibleFunction(content) {\n return typeof content === 'function' ? content.call(this._element) : content\n }\n\n _getPopperConfig(attachment) {\n const defaultBsPopperConfig = {\n placement: attachment,\n modifiers: [\n {\n name: 'flip',\n options: {\n fallbackPlacements: this._config.fallbackPlacements\n }\n },\n {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n },\n {\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n },\n {\n name: 'arrow',\n options: {\n element: `.${this.constructor.NAME}-arrow`\n }\n },\n {\n name: 'onChange',\n enabled: true,\n phase: 'afterWrite',\n fn: data => this._handlePopperPlacementChange(data)\n }\n ],\n onFirstUpdate: data => {\n if (data.options.placement !== data.placement) {\n this._handlePopperPlacementChange(data)\n }\n }\n }\n\n return {\n ...defaultBsPopperConfig,\n ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)\n }\n }\n\n _addAttachmentClass(attachment) {\n this.getTipElement().classList.add(`${this._getBasicClassPrefix()}-${this.updateAttachment(attachment)}`)\n }\n\n _getAttachment(placement) {\n return AttachmentMap[placement.toUpperCase()]\n }\n\n _setListeners() {\n const triggers = this._config.trigger.split(' ')\n\n triggers.forEach(trigger => {\n if (trigger === 'click') {\n EventHandler.on(this._element, this.constructor.Event.CLICK, this._config.selector, event => this.toggle(event))\n } else if (trigger !== TRIGGER_MANUAL) {\n const eventIn = trigger === TRIGGER_HOVER ?\n this.constructor.Event.MOUSEENTER :\n this.constructor.Event.FOCUSIN\n const eventOut = trigger === TRIGGER_HOVER ?\n this.constructor.Event.MOUSELEAVE :\n this.constructor.Event.FOCUSOUT\n\n EventHandler.on(this._element, eventIn, this._config.selector, event => this._enter(event))\n EventHandler.on(this._element, eventOut, this._config.selector, event => this._leave(event))\n }\n })\n\n this._hideModalHandler = () => {\n if (this._element) {\n this.hide()\n }\n }\n\n EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler)\n\n if (this._config.selector) {\n this._config = {\n ...this._config,\n trigger: 'manual',\n selector: ''\n }\n } else {\n this._fixTitle()\n }\n }\n\n _fixTitle() {\n const title = this._element.getAttribute('title')\n const originalTitleType = typeof this._element.getAttribute('data-bs-original-title')\n\n if (title || originalTitleType !== 'string') {\n this._element.setAttribute('data-bs-original-title', title || '')\n if (title && !this._element.getAttribute('aria-label') && !this._element.textContent) {\n this._element.setAttribute('aria-label', title)\n }\n\n this._element.setAttribute('title', '')\n }\n }\n\n _enter(event, context) {\n context = this._initializeOnDelegatedTarget(event, context)\n\n if (event) {\n context._activeTrigger[\n event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER\n ] = true\n }\n\n if (context.getTipElement().classList.contains(CLASS_NAME_SHOW) || context._hoverState === HOVER_STATE_SHOW) {\n context._hoverState = HOVER_STATE_SHOW\n return\n }\n\n clearTimeout(context._timeout)\n\n context._hoverState = HOVER_STATE_SHOW\n\n if (!context._config.delay || !context._config.delay.show) {\n context.show()\n return\n }\n\n context._timeout = setTimeout(() => {\n if (context._hoverState === HOVER_STATE_SHOW) {\n context.show()\n }\n }, context._config.delay.show)\n }\n\n _leave(event, context) {\n context = this._initializeOnDelegatedTarget(event, context)\n\n if (event) {\n context._activeTrigger[\n event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER\n ] = context._element.contains(event.relatedTarget)\n }\n\n if (context._isWithActiveTrigger()) {\n return\n }\n\n clearTimeout(context._timeout)\n\n context._hoverState = HOVER_STATE_OUT\n\n if (!context._config.delay || !context._config.delay.hide) {\n context.hide()\n return\n }\n\n context._timeout = setTimeout(() => {\n if (context._hoverState === HOVER_STATE_OUT) {\n context.hide()\n }\n }, context._config.delay.hide)\n }\n\n _isWithActiveTrigger() {\n for (const trigger in this._activeTrigger) {\n if (this._activeTrigger[trigger]) {\n return true\n }\n }\n\n return false\n }\n\n _getConfig(config) {\n const dataAttributes = Manipulator.getDataAttributes(this._element)\n\n Object.keys(dataAttributes).forEach(dataAttr => {\n if (DISALLOWED_ATTRIBUTES.has(dataAttr)) {\n delete dataAttributes[dataAttr]\n }\n })\n\n config = {\n ...this.constructor.Default,\n ...dataAttributes,\n ...(typeof config === 'object' && config ? config : {})\n }\n\n config.container = config.container === false ? document.body : getElement(config.container)\n\n if (typeof config.delay === 'number') {\n config.delay = {\n show: config.delay,\n hide: config.delay\n }\n }\n\n if (typeof config.title === 'number') {\n config.title = config.title.toString()\n }\n\n if (typeof config.content === 'number') {\n config.content = config.content.toString()\n }\n\n typeCheckConfig(NAME, config, this.constructor.DefaultType)\n\n if (config.sanitize) {\n config.template = sanitizeHtml(config.template, config.allowList, config.sanitizeFn)\n }\n\n return config\n }\n\n _getDelegateConfig() {\n const config = {}\n\n for (const key in this._config) {\n if (this.constructor.Default[key] !== this._config[key]) {\n config[key] = this._config[key]\n }\n }\n\n // In the future can be replaced with:\n // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])\n // `Object.fromEntries(keysWithDifferentValues)`\n return config\n }\n\n _cleanTipClass() {\n const tip = this.getTipElement()\n const basicClassPrefixRegex = new RegExp(`(^|\\\\s)${this._getBasicClassPrefix()}\\\\S+`, 'g')\n const tabClass = tip.getAttribute('class').match(basicClassPrefixRegex)\n if (tabClass !== null && tabClass.length > 0) {\n tabClass.map(token => token.trim())\n .forEach(tClass => tip.classList.remove(tClass))\n }\n }\n\n _getBasicClassPrefix() {\n return CLASS_PREFIX\n }\n\n _handlePopperPlacementChange(popperData) {\n const { state } = popperData\n\n if (!state) {\n return\n }\n\n this.tip = state.elements.popper\n this._cleanTipClass()\n this._addAttachmentClass(this._getAttachment(state.placement))\n }\n\n _disposePopper() {\n if (this._popper) {\n this._popper.destroy()\n this._popper = null\n }\n }\n\n // Static\n\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Tooltip.getOrCreateInstance(this, config)\n\n if (typeof config === 'string') {\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n }\n })\n }\n}\n\n/**\n * ------------------------------------------------------------------------\n * jQuery\n * ------------------------------------------------------------------------\n * add .Tooltip to jQuery only if jQuery is present\n */\n\ndefineJQueryPlugin(Tooltip)\n\nexport default Tooltip\n"],"names":["MAX_UID","toType","obj","undefined","toString","call","match","toLowerCase","getUID","prefix","Math","floor","random","document","getElementById","isElement","jquery","nodeType","getElement","length","querySelector","typeCheckConfig","componentName","config","configTypes","Object","keys","forEach","property","expectedTypes","value","valueType","RegExp","test","TypeError","toUpperCase","findShadowRoot","element","documentElement","attachShadow","getRootNode","root","ShadowRoot","parentNode","noop","getjQuery","jQuery","window","body","hasAttribute","DOMContentLoadedCallbacks","onDOMContentLoaded","callback","readyState","addEventListener","push","isRTL","dir","defineJQueryPlugin","plugin","$","name","NAME","JQUERY_NO_CONFLICT","fn","jQueryInterface","Constructor","noConflict","uriAttributes","Set","ARIA_ATTRIBUTE_PATTERN","SAFE_URL_PATTERN","DATA_URL_PATTERN","allowedAttribute","attribute","allowedAttributeList","attributeName","nodeName","includes","has","Boolean","nodeValue","regExp","filter","attributeRegex","i","len","DefaultAllowlist","a","area","b","br","col","code","div","em","hr","h1","h2","h3","h4","h5","h6","img","li","ol","p","pre","s","small","span","sub","sup","strong","u","ul","sanitizeHtml","unsafeHtml","allowList","sanitizeFn","domParser","DOMParser","createdDocument","parseFromString","elements","concat","querySelectorAll","elementName","remove","attributeList","attributes","allowedAttributes","removeAttribute","innerHTML","DATA_KEY","EVENT_KEY","CLASS_PREFIX","DISALLOWED_ATTRIBUTES","DefaultType","animation","template","title","trigger","delay","html","selector","placement","offset","container","fallbackPlacements","boundary","customClass","sanitize","popperConfig","AttachmentMap","AUTO","TOP","RIGHT","BOTTOM","LEFT","Default","Event","HIDE","HIDDEN","SHOW","SHOWN","INSERTED","CLICK","FOCUSIN","FOCUSOUT","MOUSEENTER","MOUSELEAVE","CLASS_NAME_FADE","CLASS_NAME_MODAL","CLASS_NAME_SHOW","HOVER_STATE_SHOW","HOVER_STATE_OUT","SELECTOR_TOOLTIP_INNER","SELECTOR_MODAL","EVENT_MODAL_HIDE","TRIGGER_HOVER","TRIGGER_FOCUS","TRIGGER_CLICK","TRIGGER_MANUAL","Tooltip","BaseComponent","constructor","Popper","_isEnabled","_timeout","_hoverState","_activeTrigger","_popper","_config","_getConfig","tip","_setListeners","enable","disable","toggleEnabled","toggle","event","context","_initializeOnDelegatedTarget","click","_isWithActiveTrigger","_enter","_leave","getTipElement","classList","contains","dispose","clearTimeout","EventHandler","off","_element","closest","_hideModalHandler","_disposePopper","show","style","display","Error","isWithContent","showEvent","shadowRoot","isInTheDom","ownerDocument","defaultPrevented","getTitle","tipId","setAttribute","add","attachment","_getAttachment","_addAttachmentClass","Data","set","append","update","createPopper","_getPopperConfig","_resolvePossibleFunction","split","children","on","complete","prevHoverState","isAnimated","_queueCallback","hide","_cleanTipClass","hideEvent","createElement","setContent","_sanitizeAndSetContent","content","templateElement","SelectorEngine","findOne","setElementContent","textContent","getAttribute","updateAttachment","getOrCreateInstance","delegateTarget","_getDelegateConfig","_getOffset","map","val","Number","parseInt","popperData","defaultBsPopperConfig","modifiers","options","enabled","phase","data","_handlePopperPlacementChange","onFirstUpdate","_getBasicClassPrefix","triggers","eventIn","eventOut","_fixTitle","originalTitleType","type","setTimeout","relatedTarget","dataAttributes","Manipulator","getDataAttributes","dataAttr","key","basicClassPrefixRegex","tabClass","token","trim","tClass","state","popper","destroy","each"],"mappings":"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"} \ No newline at end of file
+{"version":3,"file":"tooltip.js","sources":["../src/tooltip.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap tooltip.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport * as Popper from '@popperjs/core'\nimport BaseComponent from './base-component.js'\nimport EventHandler from './dom/event-handler.js'\nimport Manipulator from './dom/manipulator.js'\nimport {\n defineJQueryPlugin, execute, findShadowRoot, getElement, getUID, isRTL, noop\n} from './util/index.js'\nimport { DefaultAllowlist } from './util/sanitizer.js'\nimport TemplateFactory from './util/template-factory.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'tooltip'\nconst DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn'])\n\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_MODAL = 'modal'\nconst CLASS_NAME_SHOW = 'show'\n\nconst SELECTOR_TOOLTIP_INNER = '.tooltip-inner'\nconst SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`\n\nconst EVENT_MODAL_HIDE = 'hide.bs.modal'\n\nconst TRIGGER_HOVER = 'hover'\nconst TRIGGER_FOCUS = 'focus'\nconst TRIGGER_CLICK = 'click'\nconst TRIGGER_MANUAL = 'manual'\n\nconst EVENT_HIDE = 'hide'\nconst EVENT_HIDDEN = 'hidden'\nconst EVENT_SHOW = 'show'\nconst EVENT_SHOWN = 'shown'\nconst EVENT_INSERTED = 'inserted'\nconst EVENT_CLICK = 'click'\nconst EVENT_FOCUSIN = 'focusin'\nconst EVENT_FOCUSOUT = 'focusout'\nconst EVENT_MOUSEENTER = 'mouseenter'\nconst EVENT_MOUSELEAVE = 'mouseleave'\n\nconst AttachmentMap = {\n AUTO: 'auto',\n TOP: 'top',\n RIGHT: isRTL() ? 'left' : 'right',\n BOTTOM: 'bottom',\n LEFT: isRTL() ? 'right' : 'left'\n}\n\nconst Default = {\n allowList: DefaultAllowlist,\n animation: true,\n boundary: 'clippingParents',\n container: false,\n customClass: '',\n delay: 0,\n fallbackPlacements: ['top', 'right', 'bottom', 'left'],\n html: false,\n offset: [0, 6],\n placement: 'top',\n popperConfig: null,\n sanitize: true,\n sanitizeFn: null,\n selector: false,\n template: '<div class=\"tooltip\" role=\"tooltip\">' +\n '<div class=\"tooltip-arrow\"></div>' +\n '<div class=\"tooltip-inner\"></div>' +\n '</div>',\n title: '',\n trigger: 'hover focus'\n}\n\nconst DefaultType = {\n allowList: 'object',\n animation: 'boolean',\n boundary: '(string|element)',\n container: '(string|element|boolean)',\n customClass: '(string|function)',\n delay: '(number|object)',\n fallbackPlacements: 'array',\n html: 'boolean',\n offset: '(array|string|function)',\n placement: '(string|function)',\n popperConfig: '(null|object|function)',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n selector: '(string|boolean)',\n template: 'string',\n title: '(string|element|function)',\n trigger: 'string'\n}\n\n/**\n * Class definition\n */\n\nclass Tooltip extends BaseComponent {\n constructor(element, config) {\n if (typeof Popper === 'undefined') {\n throw new TypeError('Bootstrap\\'s tooltips require Popper (https://popper.js.org)')\n }\n\n super(element, config)\n\n // Private\n this._isEnabled = true\n this._timeout = 0\n this._isHovered = null\n this._activeTrigger = {}\n this._popper = null\n this._templateFactory = null\n this._newContent = null\n\n // Protected\n this.tip = null\n\n this._setListeners()\n\n if (!this._config.selector) {\n this._fixTitle()\n }\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n enable() {\n this._isEnabled = true\n }\n\n disable() {\n this._isEnabled = false\n }\n\n toggleEnabled() {\n this._isEnabled = !this._isEnabled\n }\n\n toggle() {\n if (!this._isEnabled) {\n return\n }\n\n this._activeTrigger.click = !this._activeTrigger.click\n if (this._isShown()) {\n this._leave()\n return\n }\n\n this._enter()\n }\n\n dispose() {\n clearTimeout(this._timeout)\n\n EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler)\n\n if (this._element.getAttribute('data-bs-original-title')) {\n this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'))\n }\n\n this._disposePopper()\n super.dispose()\n }\n\n show() {\n if (this._element.style.display === 'none') {\n throw new Error('Please use show on visible elements')\n }\n\n if (!(this._isWithContent() && this._isEnabled)) {\n return\n }\n\n const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW))\n const shadowRoot = findShadowRoot(this._element)\n const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element)\n\n if (showEvent.defaultPrevented || !isInTheDom) {\n return\n }\n\n // TODO: v6 remove this or make it optional\n this._disposePopper()\n\n const tip = this._getTipElement()\n\n this._element.setAttribute('aria-describedby', tip.getAttribute('id'))\n\n const { container } = this._config\n\n if (!this._element.ownerDocument.documentElement.contains(this.tip)) {\n container.append(tip)\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED))\n }\n\n this._popper = this._createPopper(tip)\n\n tip.classList.add(CLASS_NAME_SHOW)\n\n // If this is a touch-enabled device we add extra\n // empty mouseover listeners to the body's immediate children;\n // only needed because of broken event delegation on iOS\n // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.on(element, 'mouseover', noop)\n }\n }\n\n const complete = () => {\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN))\n\n if (this._isHovered === false) {\n this._leave()\n }\n\n this._isHovered = false\n }\n\n this._queueCallback(complete, this.tip, this._isAnimated())\n }\n\n hide() {\n if (!this._isShown()) {\n return\n }\n\n const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE))\n if (hideEvent.defaultPrevented) {\n return\n }\n\n const tip = this._getTipElement()\n tip.classList.remove(CLASS_NAME_SHOW)\n\n // If this is a touch-enabled device we remove the extra\n // empty mouseover listeners we added for iOS support\n if ('ontouchstart' in document.documentElement) {\n for (const element of [].concat(...document.body.children)) {\n EventHandler.off(element, 'mouseover', noop)\n }\n }\n\n this._activeTrigger[TRIGGER_CLICK] = false\n this._activeTrigger[TRIGGER_FOCUS] = false\n this._activeTrigger[TRIGGER_HOVER] = false\n this._isHovered = null // it is a trick to support manual triggering\n\n const complete = () => {\n if (this._isWithActiveTrigger()) {\n return\n }\n\n if (!this._isHovered) {\n this._disposePopper()\n }\n\n this._element.removeAttribute('aria-describedby')\n EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN))\n }\n\n this._queueCallback(complete, this.tip, this._isAnimated())\n }\n\n update() {\n if (this._popper) {\n this._popper.update()\n }\n }\n\n // Protected\n _isWithContent() {\n return Boolean(this._getTitle())\n }\n\n _getTipElement() {\n if (!this.tip) {\n this.tip = this._createTipElement(this._newContent || this._getContentForTemplate())\n }\n\n return this.tip\n }\n\n _createTipElement(content) {\n const tip = this._getTemplateFactory(content).toHtml()\n\n // TODO: remove this check in v6\n if (!tip) {\n return null\n }\n\n tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW)\n // TODO: v6 the following can be achieved with CSS only\n tip.classList.add(`bs-${this.constructor.NAME}-auto`)\n\n const tipId = getUID(this.constructor.NAME).toString()\n\n tip.setAttribute('id', tipId)\n\n if (this._isAnimated()) {\n tip.classList.add(CLASS_NAME_FADE)\n }\n\n return tip\n }\n\n setContent(content) {\n this._newContent = content\n if (this._isShown()) {\n this._disposePopper()\n this.show()\n }\n }\n\n _getTemplateFactory(content) {\n if (this._templateFactory) {\n this._templateFactory.changeContent(content)\n } else {\n this._templateFactory = new TemplateFactory({\n ...this._config,\n // the `content` var has to be after `this._config`\n // to override config.content in case of popover\n content,\n extraClass: this._resolvePossibleFunction(this._config.customClass)\n })\n }\n\n return this._templateFactory\n }\n\n _getContentForTemplate() {\n return {\n [SELECTOR_TOOLTIP_INNER]: this._getTitle()\n }\n }\n\n _getTitle() {\n return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title')\n }\n\n // Private\n _initializeOnDelegatedTarget(event) {\n return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig())\n }\n\n _isAnimated() {\n return this._config.animation || (this.tip && this.tip.classList.contains(CLASS_NAME_FADE))\n }\n\n _isShown() {\n return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW)\n }\n\n _createPopper(tip) {\n const placement = execute(this._config.placement, [this, tip, this._element])\n const attachment = AttachmentMap[placement.toUpperCase()]\n return Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))\n }\n\n _getOffset() {\n const { offset } = this._config\n\n if (typeof offset === 'string') {\n return offset.split(',').map(value => Number.parseInt(value, 10))\n }\n\n if (typeof offset === 'function') {\n return popperData => offset(popperData, this._element)\n }\n\n return offset\n }\n\n _resolvePossibleFunction(arg) {\n return execute(arg, [this._element])\n }\n\n _getPopperConfig(attachment) {\n const defaultBsPopperConfig = {\n placement: attachment,\n modifiers: [\n {\n name: 'flip',\n options: {\n fallbackPlacements: this._config.fallbackPlacements\n }\n },\n {\n name: 'offset',\n options: {\n offset: this._getOffset()\n }\n },\n {\n name: 'preventOverflow',\n options: {\n boundary: this._config.boundary\n }\n },\n {\n name: 'arrow',\n options: {\n element: `.${this.constructor.NAME}-arrow`\n }\n },\n {\n name: 'preSetPlacement',\n enabled: true,\n phase: 'beforeMain',\n fn: data => {\n // Pre-set Popper's placement attribute in order to read the arrow sizes properly.\n // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement\n this._getTipElement().setAttribute('data-popper-placement', data.state.placement)\n }\n }\n ]\n }\n\n return {\n ...defaultBsPopperConfig,\n ...execute(this._config.popperConfig, [defaultBsPopperConfig])\n }\n }\n\n _setListeners() {\n const triggers = this._config.trigger.split(' ')\n\n for (const trigger of triggers) {\n if (trigger === 'click') {\n EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK), this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event)\n context.toggle()\n })\n } else if (trigger !== TRIGGER_MANUAL) {\n const eventIn = trigger === TRIGGER_HOVER ?\n this.constructor.eventName(EVENT_MOUSEENTER) :\n this.constructor.eventName(EVENT_FOCUSIN)\n const eventOut = trigger === TRIGGER_HOVER ?\n this.constructor.eventName(EVENT_MOUSELEAVE) :\n this.constructor.eventName(EVENT_FOCUSOUT)\n\n EventHandler.on(this._element, eventIn, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event)\n context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true\n context._enter()\n })\n EventHandler.on(this._element, eventOut, this._config.selector, event => {\n const context = this._initializeOnDelegatedTarget(event)\n context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] =\n context._element.contains(event.relatedTarget)\n\n context._leave()\n })\n }\n }\n\n this._hideModalHandler = () => {\n if (this._element) {\n this.hide()\n }\n }\n\n EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler)\n }\n\n _fixTitle() {\n const title = this._element.getAttribute('title')\n\n if (!title) {\n return\n }\n\n if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) {\n this._element.setAttribute('aria-label', title)\n }\n\n this._element.setAttribute('data-bs-original-title', title) // DO NOT USE IT. Is only for backwards compatibility\n this._element.removeAttribute('title')\n }\n\n _enter() {\n if (this._isShown() || this._isHovered) {\n this._isHovered = true\n return\n }\n\n this._isHovered = true\n\n this._setTimeout(() => {\n if (this._isHovered) {\n this.show()\n }\n }, this._config.delay.show)\n }\n\n _leave() {\n if (this._isWithActiveTrigger()) {\n return\n }\n\n this._isHovered = false\n\n this._setTimeout(() => {\n if (!this._isHovered) {\n this.hide()\n }\n }, this._config.delay.hide)\n }\n\n _setTimeout(handler, timeout) {\n clearTimeout(this._timeout)\n this._timeout = setTimeout(handler, timeout)\n }\n\n _isWithActiveTrigger() {\n return Object.values(this._activeTrigger).includes(true)\n }\n\n _getConfig(config) {\n const dataAttributes = Manipulator.getDataAttributes(this._element)\n\n for (const dataAttribute of Object.keys(dataAttributes)) {\n if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {\n delete dataAttributes[dataAttribute]\n }\n }\n\n config = {\n ...dataAttributes,\n ...(typeof config === 'object' && config ? config : {})\n }\n config = this._mergeConfigObj(config)\n config = this._configAfterMerge(config)\n this._typeCheckConfig(config)\n return config\n }\n\n _configAfterMerge(config) {\n config.container = config.container === false ? document.body : getElement(config.container)\n\n if (typeof config.delay === 'number') {\n config.delay = {\n show: config.delay,\n hide: config.delay\n }\n }\n\n if (typeof config.title === 'number') {\n config.title = config.title.toString()\n }\n\n if (typeof config.content === 'number') {\n config.content = config.content.toString()\n }\n\n return config\n }\n\n _getDelegateConfig() {\n const config = {}\n\n for (const [key, value] of Object.entries(this._config)) {\n if (this.constructor.Default[key] !== value) {\n config[key] = value\n }\n }\n\n config.selector = false\n config.trigger = 'manual'\n\n // In the future can be replaced with:\n // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])\n // `Object.fromEntries(keysWithDifferentValues)`\n return config\n }\n\n _disposePopper() {\n if (this._popper) {\n this._popper.destroy()\n this._popper = null\n }\n\n if (this.tip) {\n this.tip.remove()\n this.tip = null\n }\n }\n\n // Static\n static jQueryInterface(config) {\n return this.each(function () {\n const data = Tooltip.getOrCreateInstance(this, config)\n\n if (typeof config !== 'string') {\n return\n }\n\n if (typeof data[config] === 'undefined') {\n throw new TypeError(`No method named \"${config}\"`)\n }\n\n data[config]()\n })\n }\n}\n\n/**\n * jQuery\n */\n\ndefineJQueryPlugin(Tooltip)\n\nexport default Tooltip\n"],"names":["NAME","DISALLOWED_ATTRIBUTES","Set","CLASS_NAME_FADE","CLASS_NAME_MODAL","CLASS_NAME_SHOW","SELECTOR_TOOLTIP_INNER","SELECTOR_MODAL","EVENT_MODAL_HIDE","TRIGGER_HOVER","TRIGGER_FOCUS","TRIGGER_CLICK","TRIGGER_MANUAL","EVENT_HIDE","EVENT_HIDDEN","EVENT_SHOW","EVENT_SHOWN","EVENT_INSERTED","EVENT_CLICK","EVENT_FOCUSIN","EVENT_FOCUSOUT","EVENT_MOUSEENTER","EVENT_MOUSELEAVE","AttachmentMap","AUTO","TOP","RIGHT","isRTL","BOTTOM","LEFT","Default","allowList","DefaultAllowlist","animation","boundary","container","customClass","delay","fallbackPlacements","html","offset","placement","popperConfig","sanitize","sanitizeFn","selector","template","title","trigger","DefaultType","Tooltip","BaseComponent","constructor","element","config","Popper","TypeError","_isEnabled","_timeout","_isHovered","_activeTrigger","_popper","_templateFactory","_newContent","tip","_setListeners","_config","_fixTitle","enable","disable","toggleEnabled","toggle","click","_isShown","_leave","_enter","dispose","clearTimeout","EventHandler","off","_element","closest","_hideModalHandler","getAttribute","setAttribute","_disposePopper","show","style","display","Error","_isWithContent","showEvent","eventName","shadowRoot","findShadowRoot","isInTheDom","ownerDocument","documentElement","contains","defaultPrevented","_getTipElement","append","_createPopper","classList","add","document","concat","body","children","on","noop","complete","_queueCallback","_isAnimated","hide","hideEvent","remove","_isWithActiveTrigger","removeAttribute","update","Boolean","_getTitle","_createTipElement","_getContentForTemplate","content","_getTemplateFactory","toHtml","tipId","getUID","toString","setContent","changeContent","TemplateFactory","extraClass","_resolvePossibleFunction","_initializeOnDelegatedTarget","event","getOrCreateInstance","delegateTarget","_getDelegateConfig","execute","attachment","toUpperCase","createPopper","_getPopperConfig","_getOffset","split","map","value","Number","parseInt","popperData","arg","defaultBsPopperConfig","modifiers","name","options","enabled","phase","fn","data","state","triggers","context","eventIn","eventOut","type","relatedTarget","textContent","trim","_setTimeout","handler","timeout","setTimeout","Object","values","includes","_getConfig","dataAttributes","Manipulator","getDataAttributes","dataAttribute","keys","has","_mergeConfigObj","_configAfterMerge","_typeCheckConfig","getElement","key","entries","destroy","jQueryInterface","each","defineJQueryPlugin"],"mappings":"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"} \ No newline at end of file
diff --git a/js/dist/util/backdrop.js b/js/dist/util/backdrop.js
new file mode 100644
index 000000000..d3f8957d9
--- /dev/null
+++ b/js/dist/util/backdrop.js
@@ -0,0 +1,139 @@
+/*!
+ * Bootstrap backdrop.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../dom/event-handler.js'), require('./config.js'), require('./index.js')) :
+ typeof define === 'function' && define.amd ? define(['../dom/event-handler', './config', './index'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Backdrop = factory(global.EventHandler, global.Config, global.Index));
+})(this, (function (EventHandler, Config, index_js) { 'use strict';
+
+ /**
+ * --------------------------------------------------------------------------
+ * Bootstrap util/backdrop.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+
+
+ /**
+ * Constants
+ */
+
+ const NAME = 'backdrop';
+ const CLASS_NAME_FADE = 'fade';
+ const CLASS_NAME_SHOW = 'show';
+ const EVENT_MOUSEDOWN = `mousedown.bs.${NAME}`;
+ const Default = {
+ className: 'modal-backdrop',
+ clickCallback: null,
+ isAnimated: false,
+ isVisible: true,
+ // if false, we use the backdrop helper without adding any element to the dom
+ rootElement: 'body' // give the choice to place backdrop under different elements
+ };
+ const DefaultType = {
+ className: 'string',
+ clickCallback: '(function|null)',
+ isAnimated: 'boolean',
+ isVisible: 'boolean',
+ rootElement: '(element|string)'
+ };
+
+ /**
+ * Class definition
+ */
+
+ class Backdrop extends Config {
+ constructor(config) {
+ super();
+ this._config = this._getConfig(config);
+ this._isAppended = false;
+ this._element = null;
+ }
+
+ // Getters
+ static get Default() {
+ return Default;
+ }
+ static get DefaultType() {
+ return DefaultType;
+ }
+ static get NAME() {
+ return NAME;
+ }
+
+ // Public
+ show(callback) {
+ if (!this._config.isVisible) {
+ index_js.execute(callback);
+ return;
+ }
+ this._append();
+ const element = this._getElement();
+ if (this._config.isAnimated) {
+ index_js.reflow(element);
+ }
+ element.classList.add(CLASS_NAME_SHOW);
+ this._emulateAnimation(() => {
+ index_js.execute(callback);
+ });
+ }
+ hide(callback) {
+ if (!this._config.isVisible) {
+ index_js.execute(callback);
+ return;
+ }
+ this._getElement().classList.remove(CLASS_NAME_SHOW);
+ this._emulateAnimation(() => {
+ this.dispose();
+ index_js.execute(callback);
+ });
+ }
+ dispose() {
+ if (!this._isAppended) {
+ return;
+ }
+ EventHandler.off(this._element, EVENT_MOUSEDOWN);
+ this._element.remove();
+ this._isAppended = false;
+ }
+
+ // Private
+ _getElement() {
+ if (!this._element) {
+ const backdrop = document.createElement('div');
+ backdrop.className = this._config.className;
+ if (this._config.isAnimated) {
+ backdrop.classList.add(CLASS_NAME_FADE);
+ }
+ this._element = backdrop;
+ }
+ return this._element;
+ }
+ _configAfterMerge(config) {
+ // use getElement() with the default "body" to get a fresh Element on each instantiation
+ config.rootElement = index_js.getElement(config.rootElement);
+ return config;
+ }
+ _append() {
+ if (this._isAppended) {
+ return;
+ }
+ const element = this._getElement();
+ this._config.rootElement.append(element);
+ EventHandler.on(element, EVENT_MOUSEDOWN, () => {
+ index_js.execute(this._config.clickCallback);
+ });
+ this._isAppended = true;
+ }
+ _emulateAnimation(callback) {
+ index_js.executeAfterTransition(callback, this._getElement(), this._config.isAnimated);
+ }
+ }
+
+ return Backdrop;
+
+}));
+//# sourceMappingURL=backdrop.js.map
diff --git a/js/dist/util/backdrop.js.map b/js/dist/util/backdrop.js.map
new file mode 100644
index 000000000..5b87b8025
--- /dev/null
+++ b/js/dist/util/backdrop.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"backdrop.js","sources":["../../src/util/backdrop.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap util/backdrop.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport Config from './config.js'\nimport {\n execute, executeAfterTransition, getElement, reflow\n} from './index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'backdrop'\nconst CLASS_NAME_FADE = 'fade'\nconst CLASS_NAME_SHOW = 'show'\nconst EVENT_MOUSEDOWN = `mousedown.bs.${NAME}`\n\nconst Default = {\n className: 'modal-backdrop',\n clickCallback: null,\n isAnimated: false,\n isVisible: true, // if false, we use the backdrop helper without adding any element to the dom\n rootElement: 'body' // give the choice to place backdrop under different elements\n}\n\nconst DefaultType = {\n className: 'string',\n clickCallback: '(function|null)',\n isAnimated: 'boolean',\n isVisible: 'boolean',\n rootElement: '(element|string)'\n}\n\n/**\n * Class definition\n */\n\nclass Backdrop extends Config {\n constructor(config) {\n super()\n this._config = this._getConfig(config)\n this._isAppended = false\n this._element = null\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n show(callback) {\n if (!this._config.isVisible) {\n execute(callback)\n return\n }\n\n this._append()\n\n const element = this._getElement()\n if (this._config.isAnimated) {\n reflow(element)\n }\n\n element.classList.add(CLASS_NAME_SHOW)\n\n this._emulateAnimation(() => {\n execute(callback)\n })\n }\n\n hide(callback) {\n if (!this._config.isVisible) {\n execute(callback)\n return\n }\n\n this._getElement().classList.remove(CLASS_NAME_SHOW)\n\n this._emulateAnimation(() => {\n this.dispose()\n execute(callback)\n })\n }\n\n dispose() {\n if (!this._isAppended) {\n return\n }\n\n EventHandler.off(this._element, EVENT_MOUSEDOWN)\n\n this._element.remove()\n this._isAppended = false\n }\n\n // Private\n _getElement() {\n if (!this._element) {\n const backdrop = document.createElement('div')\n backdrop.className = this._config.className\n if (this._config.isAnimated) {\n backdrop.classList.add(CLASS_NAME_FADE)\n }\n\n this._element = backdrop\n }\n\n return this._element\n }\n\n _configAfterMerge(config) {\n // use getElement() with the default \"body\" to get a fresh Element on each instantiation\n config.rootElement = getElement(config.rootElement)\n return config\n }\n\n _append() {\n if (this._isAppended) {\n return\n }\n\n const element = this._getElement()\n this._config.rootElement.append(element)\n\n EventHandler.on(element, EVENT_MOUSEDOWN, () => {\n execute(this._config.clickCallback)\n })\n\n this._isAppended = true\n }\n\n _emulateAnimation(callback) {\n executeAfterTransition(callback, this._getElement(), this._config.isAnimated)\n }\n}\n\nexport default Backdrop\n"],"names":["NAME","CLASS_NAME_FADE","CLASS_NAME_SHOW","EVENT_MOUSEDOWN","Default","className","clickCallback","isAnimated","isVisible","rootElement","DefaultType","Backdrop","Config","constructor","config","_config","_getConfig","_isAppended","_element","show","callback","execute","_append","element","_getElement","reflow","classList","add","_emulateAnimation","hide","remove","dispose","EventHandler","off","backdrop","document","createElement","_configAfterMerge","getElement","append","on","executeAfterTransition"],"mappings":"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"} \ No newline at end of file
diff --git a/js/dist/util/component-functions.js b/js/dist/util/component-functions.js
new file mode 100644
index 000000000..7848b314b
--- /dev/null
+++ b/js/dist/util/component-functions.js
@@ -0,0 +1,42 @@
+/*!
+ * Bootstrap component-functions.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('../dom/event-handler.js'), require('../dom/selector-engine.js'), require('./index.js')) :
+ typeof define === 'function' && define.amd ? define(['exports', '../dom/event-handler', '../dom/selector-engine', './index'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.ComponentFunctions = {}, global.EventHandler, global.SelectorEngine, global.Index));
+})(this, (function (exports, EventHandler, SelectorEngine, index_js) { 'use strict';
+
+ /**
+ * --------------------------------------------------------------------------
+ * Bootstrap util/component-functions.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+
+ const enableDismissTrigger = (component, method = 'hide') => {
+ const clickEvent = `click.dismiss${component.EVENT_KEY}`;
+ const name = component.NAME;
+ EventHandler.on(document, clickEvent, `[data-bs-dismiss="${name}"]`, function (event) {
+ if (['A', 'AREA'].includes(this.tagName)) {
+ event.preventDefault();
+ }
+ if (index_js.isDisabled(this)) {
+ return;
+ }
+ const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`);
+ const instance = component.getOrCreateInstance(target);
+
+ // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method
+ instance[method]();
+ });
+ };
+
+ exports.enableDismissTrigger = enableDismissTrigger;
+
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
+
+}));
+//# sourceMappingURL=component-functions.js.map
diff --git a/js/dist/util/component-functions.js.map b/js/dist/util/component-functions.js.map
new file mode 100644
index 000000000..becaea3b2
--- /dev/null
+++ b/js/dist/util/component-functions.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"component-functions.js","sources":["../../src/util/component-functions.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap util/component-functions.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport SelectorEngine from '../dom/selector-engine.js'\nimport { isDisabled } from './index.js'\n\nconst enableDismissTrigger = (component, method = 'hide') => {\n const clickEvent = `click.dismiss${component.EVENT_KEY}`\n const name = component.NAME\n\n EventHandler.on(document, clickEvent, `[data-bs-dismiss=\"${name}\"]`, function (event) {\n if (['A', 'AREA'].includes(this.tagName)) {\n event.preventDefault()\n }\n\n if (isDisabled(this)) {\n return\n }\n\n const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`)\n const instance = component.getOrCreateInstance(target)\n\n // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method\n instance[method]()\n })\n}\n\nexport {\n enableDismissTrigger\n}\n"],"names":["enableDismissTrigger","component","method","clickEvent","EVENT_KEY","name","NAME","EventHandler","on","document","event","includes","tagName","preventDefault","isDisabled","target","SelectorEngine","getElementFromSelector","closest","instance","getOrCreateInstance"],"mappings":";;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;;AAMMA,QAAAA,oBAAoB,GAAGA,CAACC,SAAS,EAAEC,MAAM,GAAG,MAAM,KAAK;EAC3D,EAAA,MAAMC,UAAU,GAAI,CAAA,aAAA,EAAeF,SAAS,CAACG,SAAU,CAAC,CAAA,CAAA;EACxD,EAAA,MAAMC,IAAI,GAAGJ,SAAS,CAACK,IAAI,CAAA;EAE3BC,EAAAA,YAAY,CAACC,EAAE,CAACC,QAAQ,EAAEN,UAAU,EAAG,CAAA,kBAAA,EAAoBE,IAAK,CAAA,EAAA,CAAG,EAAE,UAAUK,KAAK,EAAE;EACpF,IAAA,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAACC,QAAQ,CAAC,IAAI,CAACC,OAAO,CAAC,EAAE;QACxCF,KAAK,CAACG,cAAc,EAAE,CAAA;EACxB,KAAA;EAEA,IAAA,IAAIC,mBAAU,CAAC,IAAI,CAAC,EAAE;EACpB,MAAA,OAAA;EACF,KAAA;EAEA,IAAA,MAAMC,MAAM,GAAGC,cAAc,CAACC,sBAAsB,CAAC,IAAI,CAAC,IAAI,IAAI,CAACC,OAAO,CAAE,CAAGb,CAAAA,EAAAA,IAAK,EAAC,CAAC,CAAA;EACtF,IAAA,MAAMc,QAAQ,GAAGlB,SAAS,CAACmB,mBAAmB,CAACL,MAAM,CAAC,CAAA;;EAEtD;EACAI,IAAAA,QAAQ,CAACjB,MAAM,CAAC,EAAE,CAAA;EACpB,GAAC,CAAC,CAAA;EACJ;;;;;;;;;;"} \ No newline at end of file
diff --git a/js/dist/util/config.js b/js/dist/util/config.js
new file mode 100644
index 000000000..0e1b2c77c
--- /dev/null
+++ b/js/dist/util/config.js
@@ -0,0 +1,68 @@
+/*!
+ * Bootstrap config.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../dom/manipulator.js'), require('./index.js')) :
+ typeof define === 'function' && define.amd ? define(['../dom/manipulator', './index'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Config = factory(global.Manipulator, global.Index));
+})(this, (function (Manipulator, index_js) { 'use strict';
+
+ /**
+ * --------------------------------------------------------------------------
+ * Bootstrap util/config.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+
+
+ /**
+ * Class definition
+ */
+
+ class Config {
+ // Getters
+ static get Default() {
+ return {};
+ }
+ static get DefaultType() {
+ return {};
+ }
+ static get NAME() {
+ throw new Error('You have to implement the static method "NAME", for each component!');
+ }
+ _getConfig(config) {
+ config = this._mergeConfigObj(config);
+ config = this._configAfterMerge(config);
+ this._typeCheckConfig(config);
+ return config;
+ }
+ _configAfterMerge(config) {
+ return config;
+ }
+ _mergeConfigObj(config, element) {
+ const jsonConfig = index_js.isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {}; // try to parse
+
+ return {
+ ...this.constructor.Default,
+ ...(typeof jsonConfig === 'object' ? jsonConfig : {}),
+ ...(index_js.isElement(element) ? Manipulator.getDataAttributes(element) : {}),
+ ...(typeof config === 'object' ? config : {})
+ };
+ }
+ _typeCheckConfig(config, configTypes = this.constructor.DefaultType) {
+ for (const [property, expectedTypes] of Object.entries(configTypes)) {
+ const value = config[property];
+ const valueType = index_js.isElement(value) ? 'element' : index_js.toType(value);
+ if (!new RegExp(expectedTypes).test(valueType)) {
+ throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`);
+ }
+ }
+ }
+ }
+
+ return Config;
+
+}));
+//# sourceMappingURL=config.js.map
diff --git a/js/dist/util/config.js.map b/js/dist/util/config.js.map
new file mode 100644
index 000000000..82b609e50
--- /dev/null
+++ b/js/dist/util/config.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"config.js","sources":["../../src/util/config.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap util/config.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Manipulator from '../dom/manipulator.js'\nimport { isElement, toType } from './index.js'\n\n/**\n * Class definition\n */\n\nclass Config {\n // Getters\n static get Default() {\n return {}\n }\n\n static get DefaultType() {\n return {}\n }\n\n static get NAME() {\n throw new Error('You have to implement the static method \"NAME\", for each component!')\n }\n\n _getConfig(config) {\n config = this._mergeConfigObj(config)\n config = this._configAfterMerge(config)\n this._typeCheckConfig(config)\n return config\n }\n\n _configAfterMerge(config) {\n return config\n }\n\n _mergeConfigObj(config, element) {\n const jsonConfig = isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {} // try to parse\n\n return {\n ...this.constructor.Default,\n ...(typeof jsonConfig === 'object' ? jsonConfig : {}),\n ...(isElement(element) ? Manipulator.getDataAttributes(element) : {}),\n ...(typeof config === 'object' ? config : {})\n }\n }\n\n _typeCheckConfig(config, configTypes = this.constructor.DefaultType) {\n for (const [property, expectedTypes] of Object.entries(configTypes)) {\n const value = config[property]\n const valueType = isElement(value) ? 'element' : toType(value)\n\n if (!new RegExp(expectedTypes).test(valueType)) {\n throw new TypeError(\n `${this.constructor.NAME.toUpperCase()}: Option \"${property}\" provided type \"${valueType}\" but expected type \"${expectedTypes}\".`\n )\n }\n }\n }\n}\n\nexport default Config\n"],"names":["Config","Default","DefaultType","NAME","Error","_getConfig","config","_mergeConfigObj","_configAfterMerge","_typeCheckConfig","element","jsonConfig","isElement","Manipulator","getDataAttribute","constructor","getDataAttributes","configTypes","property","expectedTypes","Object","entries","value","valueType","toType","RegExp","test","TypeError","toUpperCase"],"mappings":";;;;;;;;;;;EAAA;EACA;EACA;EACA;EACA;EACA;;;EAKA;EACA;EACA;;EAEA,MAAMA,MAAM,CAAC;EACX;IACA,WAAWC,OAAOA,GAAG;EACnB,IAAA,OAAO,EAAE,CAAA;EACX,GAAA;IAEA,WAAWC,WAAWA,GAAG;EACvB,IAAA,OAAO,EAAE,CAAA;EACX,GAAA;IAEA,WAAWC,IAAIA,GAAG;EAChB,IAAA,MAAM,IAAIC,KAAK,CAAC,qEAAqE,CAAC,CAAA;EACxF,GAAA;IAEAC,UAAUA,CAACC,MAAM,EAAE;EACjBA,IAAAA,MAAM,GAAG,IAAI,CAACC,eAAe,CAACD,MAAM,CAAC,CAAA;EACrCA,IAAAA,MAAM,GAAG,IAAI,CAACE,iBAAiB,CAACF,MAAM,CAAC,CAAA;EACvC,IAAA,IAAI,CAACG,gBAAgB,CAACH,MAAM,CAAC,CAAA;EAC7B,IAAA,OAAOA,MAAM,CAAA;EACf,GAAA;IAEAE,iBAAiBA,CAACF,MAAM,EAAE;EACxB,IAAA,OAAOA,MAAM,CAAA;EACf,GAAA;EAEAC,EAAAA,eAAeA,CAACD,MAAM,EAAEI,OAAO,EAAE;EAC/B,IAAA,MAAMC,UAAU,GAAGC,kBAAS,CAACF,OAAO,CAAC,GAAGG,WAAW,CAACC,gBAAgB,CAACJ,OAAO,EAAE,QAAQ,CAAC,GAAG,EAAE,CAAC;;MAE7F,OAAO;EACL,MAAA,GAAG,IAAI,CAACK,WAAW,CAACd,OAAO;QAC3B,IAAI,OAAOU,UAAU,KAAK,QAAQ,GAAGA,UAAU,GAAG,EAAE;EACpD,MAAA,IAAIC,kBAAS,CAACF,OAAO,CAAC,GAAGG,WAAW,CAACG,iBAAiB,CAACN,OAAO,CAAC,GAAG,EAAE;QACpE,IAAI,OAAOJ,MAAM,KAAK,QAAQ,GAAGA,MAAM,GAAG,EAAE;OAC7C,CAAA;EACH,GAAA;IAEAG,gBAAgBA,CAACH,MAAM,EAAEW,WAAW,GAAG,IAAI,CAACF,WAAW,CAACb,WAAW,EAAE;EACnE,IAAA,KAAK,MAAM,CAACgB,QAAQ,EAAEC,aAAa,CAAC,IAAIC,MAAM,CAACC,OAAO,CAACJ,WAAW,CAAC,EAAE;EACnE,MAAA,MAAMK,KAAK,GAAGhB,MAAM,CAACY,QAAQ,CAAC,CAAA;EAC9B,MAAA,MAAMK,SAAS,GAAGX,kBAAS,CAACU,KAAK,CAAC,GAAG,SAAS,GAAGE,eAAM,CAACF,KAAK,CAAC,CAAA;QAE9D,IAAI,CAAC,IAAIG,MAAM,CAACN,aAAa,CAAC,CAACO,IAAI,CAACH,SAAS,CAAC,EAAE;UAC9C,MAAM,IAAII,SAAS,CAChB,CAAA,EAAE,IAAI,CAACZ,WAAW,CAACZ,IAAI,CAACyB,WAAW,EAAG,aAAYV,QAAS,CAAA,iBAAA,EAAmBK,SAAU,CAAuBJ,qBAAAA,EAAAA,aAAc,IAChI,CAAC,CAAA;EACH,OAAA;EACF,KAAA;EACF,GAAA;EACF;;;;;;;;"} \ No newline at end of file
diff --git a/js/dist/util/focustrap.js b/js/dist/util/focustrap.js
new file mode 100644
index 000000000..c0deacb7c
--- /dev/null
+++ b/js/dist/util/focustrap.js
@@ -0,0 +1,113 @@
+/*!
+ * Bootstrap focustrap.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../dom/event-handler.js'), require('../dom/selector-engine.js'), require('./config.js')) :
+ typeof define === 'function' && define.amd ? define(['../dom/event-handler', '../dom/selector-engine', './config'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Focustrap = factory(global.EventHandler, global.SelectorEngine, global.Config));
+})(this, (function (EventHandler, SelectorEngine, Config) { 'use strict';
+
+ /**
+ * --------------------------------------------------------------------------
+ * Bootstrap util/focustrap.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+
+
+ /**
+ * Constants
+ */
+
+ const NAME = 'focustrap';
+ const DATA_KEY = 'bs.focustrap';
+ const EVENT_KEY = `.${DATA_KEY}`;
+ const EVENT_FOCUSIN = `focusin${EVENT_KEY}`;
+ const EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY}`;
+ const TAB_KEY = 'Tab';
+ const TAB_NAV_FORWARD = 'forward';
+ const TAB_NAV_BACKWARD = 'backward';
+ const Default = {
+ autofocus: true,
+ trapElement: null // The element to trap focus inside of
+ };
+ const DefaultType = {
+ autofocus: 'boolean',
+ trapElement: 'element'
+ };
+
+ /**
+ * Class definition
+ */
+
+ class FocusTrap extends Config {
+ constructor(config) {
+ super();
+ this._config = this._getConfig(config);
+ this._isActive = false;
+ this._lastTabNavDirection = null;
+ }
+
+ // Getters
+ static get Default() {
+ return Default;
+ }
+ static get DefaultType() {
+ return DefaultType;
+ }
+ static get NAME() {
+ return NAME;
+ }
+
+ // Public
+ activate() {
+ if (this._isActive) {
+ return;
+ }
+ if (this._config.autofocus) {
+ this._config.trapElement.focus();
+ }
+ EventHandler.off(document, EVENT_KEY); // guard against infinite focus loop
+ EventHandler.on(document, EVENT_FOCUSIN, event => this._handleFocusin(event));
+ EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event));
+ this._isActive = true;
+ }
+ deactivate() {
+ if (!this._isActive) {
+ return;
+ }
+ this._isActive = false;
+ EventHandler.off(document, EVENT_KEY);
+ }
+
+ // Private
+ _handleFocusin(event) {
+ const {
+ trapElement
+ } = this._config;
+ if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {
+ return;
+ }
+ const elements = SelectorEngine.focusableChildren(trapElement);
+ if (elements.length === 0) {
+ trapElement.focus();
+ } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {
+ elements[elements.length - 1].focus();
+ } else {
+ elements[0].focus();
+ }
+ }
+ _handleKeydown(event) {
+ if (event.key !== TAB_KEY) {
+ return;
+ }
+ this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD;
+ }
+ }
+
+ return FocusTrap;
+
+}));
+//# sourceMappingURL=focustrap.js.map
diff --git a/js/dist/util/focustrap.js.map b/js/dist/util/focustrap.js.map
new file mode 100644
index 000000000..9b2befaec
--- /dev/null
+++ b/js/dist/util/focustrap.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"focustrap.js","sources":["../../src/util/focustrap.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap util/focustrap.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport SelectorEngine from '../dom/selector-engine.js'\nimport Config from './config.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'focustrap'\nconst DATA_KEY = 'bs.focustrap'\nconst EVENT_KEY = `.${DATA_KEY}`\nconst EVENT_FOCUSIN = `focusin${EVENT_KEY}`\nconst EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY}`\n\nconst TAB_KEY = 'Tab'\nconst TAB_NAV_FORWARD = 'forward'\nconst TAB_NAV_BACKWARD = 'backward'\n\nconst Default = {\n autofocus: true,\n trapElement: null // The element to trap focus inside of\n}\n\nconst DefaultType = {\n autofocus: 'boolean',\n trapElement: 'element'\n}\n\n/**\n * Class definition\n */\n\nclass FocusTrap extends Config {\n constructor(config) {\n super()\n this._config = this._getConfig(config)\n this._isActive = false\n this._lastTabNavDirection = null\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n activate() {\n if (this._isActive) {\n return\n }\n\n if (this._config.autofocus) {\n this._config.trapElement.focus()\n }\n\n EventHandler.off(document, EVENT_KEY) // guard against infinite focus loop\n EventHandler.on(document, EVENT_FOCUSIN, event => this._handleFocusin(event))\n EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event))\n\n this._isActive = true\n }\n\n deactivate() {\n if (!this._isActive) {\n return\n }\n\n this._isActive = false\n EventHandler.off(document, EVENT_KEY)\n }\n\n // Private\n _handleFocusin(event) {\n const { trapElement } = this._config\n\n if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {\n return\n }\n\n const elements = SelectorEngine.focusableChildren(trapElement)\n\n if (elements.length === 0) {\n trapElement.focus()\n } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) {\n elements[elements.length - 1].focus()\n } else {\n elements[0].focus()\n }\n }\n\n _handleKeydown(event) {\n if (event.key !== TAB_KEY) {\n return\n }\n\n this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD\n }\n}\n\nexport default FocusTrap\n"],"names":["NAME","DATA_KEY","EVENT_KEY","EVENT_FOCUSIN","EVENT_KEYDOWN_TAB","TAB_KEY","TAB_NAV_FORWARD","TAB_NAV_BACKWARD","Default","autofocus","trapElement","DefaultType","FocusTrap","Config","constructor","config","_config","_getConfig","_isActive","_lastTabNavDirection","activate","focus","EventHandler","off","document","on","event","_handleFocusin","_handleKeydown","deactivate","target","contains","elements","SelectorEngine","focusableChildren","length","key","shiftKey"],"mappings":"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"} \ No newline at end of file
diff --git a/js/dist/util/index.js b/js/dist/util/index.js
new file mode 100644
index 000000000..f5495d688
--- /dev/null
+++ b/js/dist/util/index.js
@@ -0,0 +1,281 @@
+/*!
+ * Bootstrap index.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Index = {}));
+})(this, (function (exports) { 'use strict';
+
+ /**
+ * --------------------------------------------------------------------------
+ * Bootstrap util/index.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+
+ const MAX_UID = 1000000;
+ const MILLISECONDS_MULTIPLIER = 1000;
+ const TRANSITION_END = 'transitionend';
+
+ /**
+ * Properly escape IDs selectors to handle weird IDs
+ * @param {string} selector
+ * @returns {string}
+ */
+ const parseSelector = selector => {
+ if (selector && window.CSS && window.CSS.escape) {
+ // document.querySelector needs escaping to handle IDs (html5+) containing for instance /
+ selector = selector.replace(/#([^\s"#']+)/g, (match, id) => `#${CSS.escape(id)}`);
+ }
+ return selector;
+ };
+
+ // Shout-out Angus Croll (https://goo.gl/pxwQGp)
+ const toType = object => {
+ if (object === null || object === undefined) {
+ return `${object}`;
+ }
+ return Object.prototype.toString.call(object).match(/\s([a-z]+)/i)[1].toLowerCase();
+ };
+
+ /**
+ * Public Util API
+ */
+
+ const getUID = prefix => {
+ do {
+ prefix += Math.floor(Math.random() * MAX_UID);
+ } while (document.getElementById(prefix));
+ return prefix;
+ };
+ const getTransitionDurationFromElement = element => {
+ if (!element) {
+ return 0;
+ }
+
+ // Get transition-duration of the element
+ let {
+ transitionDuration,
+ transitionDelay
+ } = window.getComputedStyle(element);
+ const floatTransitionDuration = Number.parseFloat(transitionDuration);
+ const floatTransitionDelay = Number.parseFloat(transitionDelay);
+
+ // Return 0 if element or transition duration is not found
+ if (!floatTransitionDuration && !floatTransitionDelay) {
+ return 0;
+ }
+
+ // If multiple durations are defined, take the first
+ transitionDuration = transitionDuration.split(',')[0];
+ transitionDelay = transitionDelay.split(',')[0];
+ return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER;
+ };
+ const triggerTransitionEnd = element => {
+ element.dispatchEvent(new Event(TRANSITION_END));
+ };
+ const isElement = object => {
+ if (!object || typeof object !== 'object') {
+ return false;
+ }
+ if (typeof object.jquery !== 'undefined') {
+ object = object[0];
+ }
+ return typeof object.nodeType !== 'undefined';
+ };
+ const getElement = object => {
+ // it's a jQuery object or a node element
+ if (isElement(object)) {
+ return object.jquery ? object[0] : object;
+ }
+ if (typeof object === 'string' && object.length > 0) {
+ return document.querySelector(parseSelector(object));
+ }
+ return null;
+ };
+ const isVisible = element => {
+ if (!isElement(element) || element.getClientRects().length === 0) {
+ return false;
+ }
+ const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible';
+ // Handle `details` element as its content may falsie appear visible when it is closed
+ const closedDetails = element.closest('details:not([open])');
+ if (!closedDetails) {
+ return elementIsVisible;
+ }
+ if (closedDetails !== element) {
+ const summary = element.closest('summary');
+ if (summary && summary.parentNode !== closedDetails) {
+ return false;
+ }
+ if (summary === null) {
+ return false;
+ }
+ }
+ return elementIsVisible;
+ };
+ const isDisabled = element => {
+ if (!element || element.nodeType !== Node.ELEMENT_NODE) {
+ return true;
+ }
+ if (element.classList.contains('disabled')) {
+ return true;
+ }
+ if (typeof element.disabled !== 'undefined') {
+ return element.disabled;
+ }
+ return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false';
+ };
+ const findShadowRoot = element => {
+ if (!document.documentElement.attachShadow) {
+ return null;
+ }
+
+ // Can find the shadow root otherwise it'll return the document
+ if (typeof element.getRootNode === 'function') {
+ const root = element.getRootNode();
+ return root instanceof ShadowRoot ? root : null;
+ }
+ if (element instanceof ShadowRoot) {
+ return element;
+ }
+
+ // when we don't find a shadow root
+ if (!element.parentNode) {
+ return null;
+ }
+ return findShadowRoot(element.parentNode);
+ };
+ const noop = () => {};
+
+ /**
+ * Trick to restart an element's animation
+ *
+ * @param {HTMLElement} element
+ * @return void
+ *
+ * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
+ */
+ const reflow = element => {
+ element.offsetHeight; // eslint-disable-line no-unused-expressions
+ };
+ const getjQuery = () => {
+ if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
+ return window.jQuery;
+ }
+ return null;
+ };
+ const DOMContentLoadedCallbacks = [];
+ const onDOMContentLoaded = callback => {
+ if (document.readyState === 'loading') {
+ // add listener on the first call when the document is in loading state
+ if (!DOMContentLoadedCallbacks.length) {
+ document.addEventListener('DOMContentLoaded', () => {
+ for (const callback of DOMContentLoadedCallbacks) {
+ callback();
+ }
+ });
+ }
+ DOMContentLoadedCallbacks.push(callback);
+ } else {
+ callback();
+ }
+ };
+ const isRTL = () => document.documentElement.dir === 'rtl';
+ const defineJQueryPlugin = plugin => {
+ onDOMContentLoaded(() => {
+ const $ = getjQuery();
+ /* istanbul ignore if */
+ if ($) {
+ const name = plugin.NAME;
+ const JQUERY_NO_CONFLICT = $.fn[name];
+ $.fn[name] = plugin.jQueryInterface;
+ $.fn[name].Constructor = plugin;
+ $.fn[name].noConflict = () => {
+ $.fn[name] = JQUERY_NO_CONFLICT;
+ return plugin.jQueryInterface;
+ };
+ }
+ });
+ };
+ const execute = (possibleCallback, args = [], defaultValue = possibleCallback) => {
+ return typeof possibleCallback === 'function' ? possibleCallback(...args) : defaultValue;
+ };
+ const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {
+ if (!waitForTransition) {
+ execute(callback);
+ return;
+ }
+ const durationPadding = 5;
+ const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding;
+ let called = false;
+ const handler = ({
+ target
+ }) => {
+ if (target !== transitionElement) {
+ return;
+ }
+ called = true;
+ transitionElement.removeEventListener(TRANSITION_END, handler);
+ execute(callback);
+ };
+ transitionElement.addEventListener(TRANSITION_END, handler);
+ setTimeout(() => {
+ if (!called) {
+ triggerTransitionEnd(transitionElement);
+ }
+ }, emulatedDuration);
+ };
+
+ /**
+ * Return the previous/next element of a list.
+ *
+ * @param {array} list The list of elements
+ * @param activeElement The active element
+ * @param shouldGetNext Choose to get next or previous element
+ * @param isCycleAllowed
+ * @return {Element|elem} The proper element
+ */
+ const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {
+ const listLength = list.length;
+ let index = list.indexOf(activeElement);
+
+ // if the element does not exist in the list return an element
+ // depending on the direction and if cycle is allowed
+ if (index === -1) {
+ return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0];
+ }
+ index += shouldGetNext ? 1 : -1;
+ if (isCycleAllowed) {
+ index = (index + listLength) % listLength;
+ }
+ return list[Math.max(0, Math.min(index, listLength - 1))];
+ };
+
+ exports.defineJQueryPlugin = defineJQueryPlugin;
+ exports.execute = execute;
+ exports.executeAfterTransition = executeAfterTransition;
+ exports.findShadowRoot = findShadowRoot;
+ exports.getElement = getElement;
+ exports.getNextActiveElement = getNextActiveElement;
+ exports.getTransitionDurationFromElement = getTransitionDurationFromElement;
+ exports.getUID = getUID;
+ exports.getjQuery = getjQuery;
+ exports.isDisabled = isDisabled;
+ exports.isElement = isElement;
+ exports.isRTL = isRTL;
+ exports.isVisible = isVisible;
+ exports.noop = noop;
+ exports.onDOMContentLoaded = onDOMContentLoaded;
+ exports.parseSelector = parseSelector;
+ exports.reflow = reflow;
+ exports.toType = toType;
+ exports.triggerTransitionEnd = triggerTransitionEnd;
+
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
+
+}));
+//# sourceMappingURL=index.js.map
diff --git a/js/dist/util/index.js.map b/js/dist/util/index.js.map
new file mode 100644
index 000000000..b4fdf3981
--- /dev/null
+++ b/js/dist/util/index.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.js","sources":["../../src/util/index.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap util/index.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nconst MAX_UID = 1_000_000\nconst MILLISECONDS_MULTIPLIER = 1000\nconst TRANSITION_END = 'transitionend'\n\n/**\n * Properly escape IDs selectors to handle weird IDs\n * @param {string} selector\n * @returns {string}\n */\nconst parseSelector = selector => {\n if (selector && window.CSS && window.CSS.escape) {\n // document.querySelector needs escaping to handle IDs (html5+) containing for instance /\n selector = selector.replace(/#([^\\s\"#']+)/g, (match, id) => `#${CSS.escape(id)}`)\n }\n\n return selector\n}\n\n// Shout-out Angus Croll (https://goo.gl/pxwQGp)\nconst toType = object => {\n if (object === null || object === undefined) {\n return `${object}`\n }\n\n return Object.prototype.toString.call(object).match(/\\s([a-z]+)/i)[1].toLowerCase()\n}\n\n/**\n * Public Util API\n */\n\nconst getUID = prefix => {\n do {\n prefix += Math.floor(Math.random() * MAX_UID)\n } while (document.getElementById(prefix))\n\n return prefix\n}\n\nconst getTransitionDurationFromElement = element => {\n if (!element) {\n return 0\n }\n\n // Get transition-duration of the element\n let { transitionDuration, transitionDelay } = window.getComputedStyle(element)\n\n const floatTransitionDuration = Number.parseFloat(transitionDuration)\n const floatTransitionDelay = Number.parseFloat(transitionDelay)\n\n // Return 0 if element or transition duration is not found\n if (!floatTransitionDuration && !floatTransitionDelay) {\n return 0\n }\n\n // If multiple durations are defined, take the first\n transitionDuration = transitionDuration.split(',')[0]\n transitionDelay = transitionDelay.split(',')[0]\n\n return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER\n}\n\nconst triggerTransitionEnd = element => {\n element.dispatchEvent(new Event(TRANSITION_END))\n}\n\nconst isElement = object => {\n if (!object || typeof object !== 'object') {\n return false\n }\n\n if (typeof object.jquery !== 'undefined') {\n object = object[0]\n }\n\n return typeof object.nodeType !== 'undefined'\n}\n\nconst getElement = object => {\n // it's a jQuery object or a node element\n if (isElement(object)) {\n return object.jquery ? object[0] : object\n }\n\n if (typeof object === 'string' && object.length > 0) {\n return document.querySelector(parseSelector(object))\n }\n\n return null\n}\n\nconst isVisible = element => {\n if (!isElement(element) || element.getClientRects().length === 0) {\n return false\n }\n\n const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'\n // Handle `details` element as its content may falsie appear visible when it is closed\n const closedDetails = element.closest('details:not([open])')\n\n if (!closedDetails) {\n return elementIsVisible\n }\n\n if (closedDetails !== element) {\n const summary = element.closest('summary')\n if (summary && summary.parentNode !== closedDetails) {\n return false\n }\n\n if (summary === null) {\n return false\n }\n }\n\n return elementIsVisible\n}\n\nconst isDisabled = element => {\n if (!element || element.nodeType !== Node.ELEMENT_NODE) {\n return true\n }\n\n if (element.classList.contains('disabled')) {\n return true\n }\n\n if (typeof element.disabled !== 'undefined') {\n return element.disabled\n }\n\n return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'\n}\n\nconst findShadowRoot = element => {\n if (!document.documentElement.attachShadow) {\n return null\n }\n\n // Can find the shadow root otherwise it'll return the document\n if (typeof element.getRootNode === 'function') {\n const root = element.getRootNode()\n return root instanceof ShadowRoot ? root : null\n }\n\n if (element instanceof ShadowRoot) {\n return element\n }\n\n // when we don't find a shadow root\n if (!element.parentNode) {\n return null\n }\n\n return findShadowRoot(element.parentNode)\n}\n\nconst noop = () => {}\n\n/**\n * Trick to restart an element's animation\n *\n * @param {HTMLElement} element\n * @return void\n *\n * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation\n */\nconst reflow = element => {\n element.offsetHeight // eslint-disable-line no-unused-expressions\n}\n\nconst getjQuery = () => {\n if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {\n return window.jQuery\n }\n\n return null\n}\n\nconst DOMContentLoadedCallbacks = []\n\nconst onDOMContentLoaded = callback => {\n if (document.readyState === 'loading') {\n // add listener on the first call when the document is in loading state\n if (!DOMContentLoadedCallbacks.length) {\n document.addEventListener('DOMContentLoaded', () => {\n for (const callback of DOMContentLoadedCallbacks) {\n callback()\n }\n })\n }\n\n DOMContentLoadedCallbacks.push(callback)\n } else {\n callback()\n }\n}\n\nconst isRTL = () => document.documentElement.dir === 'rtl'\n\nconst defineJQueryPlugin = plugin => {\n onDOMContentLoaded(() => {\n const $ = getjQuery()\n /* istanbul ignore if */\n if ($) {\n const name = plugin.NAME\n const JQUERY_NO_CONFLICT = $.fn[name]\n $.fn[name] = plugin.jQueryInterface\n $.fn[name].Constructor = plugin\n $.fn[name].noConflict = () => {\n $.fn[name] = JQUERY_NO_CONFLICT\n return plugin.jQueryInterface\n }\n }\n })\n}\n\nconst execute = (possibleCallback, args = [], defaultValue = possibleCallback) => {\n return typeof possibleCallback === 'function' ? possibleCallback(...args) : defaultValue\n}\n\nconst executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {\n if (!waitForTransition) {\n execute(callback)\n return\n }\n\n const durationPadding = 5\n const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding\n\n let called = false\n\n const handler = ({ target }) => {\n if (target !== transitionElement) {\n return\n }\n\n called = true\n transitionElement.removeEventListener(TRANSITION_END, handler)\n execute(callback)\n }\n\n transitionElement.addEventListener(TRANSITION_END, handler)\n setTimeout(() => {\n if (!called) {\n triggerTransitionEnd(transitionElement)\n }\n }, emulatedDuration)\n}\n\n/**\n * Return the previous/next element of a list.\n *\n * @param {array} list The list of elements\n * @param activeElement The active element\n * @param shouldGetNext Choose to get next or previous element\n * @param isCycleAllowed\n * @return {Element|elem} The proper element\n */\nconst getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {\n const listLength = list.length\n let index = list.indexOf(activeElement)\n\n // if the element does not exist in the list return an element\n // depending on the direction and if cycle is allowed\n if (index === -1) {\n return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0]\n }\n\n index += shouldGetNext ? 1 : -1\n\n if (isCycleAllowed) {\n index = (index + listLength) % listLength\n }\n\n return list[Math.max(0, Math.min(index, listLength - 1))]\n}\n\nexport {\n defineJQueryPlugin,\n execute,\n executeAfterTransition,\n findShadowRoot,\n getElement,\n getjQuery,\n getNextActiveElement,\n getTransitionDurationFromElement,\n getUID,\n isDisabled,\n isElement,\n isRTL,\n isVisible,\n noop,\n onDOMContentLoaded,\n parseSelector,\n reflow,\n triggerTransitionEnd,\n toType\n}\n"],"names":["MAX_UID","MILLISECONDS_MULTIPLIER","TRANSITION_END","parseSelector","selector","window","CSS","escape","replace","match","id","toType","object","undefined","Object","prototype","toString","call","toLowerCase","getUID","prefix","Math","floor","random","document","getElementById","getTransitionDurationFromElement","element","transitionDuration","transitionDelay","getComputedStyle","floatTransitionDuration","Number","parseFloat","floatTransitionDelay","split","triggerTransitionEnd","dispatchEvent","Event","isElement","jquery","nodeType","getElement","length","querySelector","isVisible","getClientRects","elementIsVisible","getPropertyValue","closedDetails","closest","summary","parentNode","isDisabled","Node","ELEMENT_NODE","classList","contains","disabled","hasAttribute","getAttribute","findShadowRoot","documentElement","attachShadow","getRootNode","root","ShadowRoot","noop","reflow","offsetHeight","getjQuery","jQuery","body","DOMContentLoadedCallbacks","onDOMContentLoaded","callback","readyState","addEventListener","push","isRTL","dir","defineJQueryPlugin","plugin","$","name","NAME","JQUERY_NO_CONFLICT","fn","jQueryInterface","Constructor","noConflict","execute","possibleCallback","args","defaultValue","executeAfterTransition","transitionElement","waitForTransition","durationPadding","emulatedDuration","called","handler","target","removeEventListener","setTimeout","getNextActiveElement","list","activeElement","shouldGetNext","isCycleAllowed","listLength","index","indexOf","max","min"],"mappings":"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"} \ No newline at end of file
diff --git a/js/dist/util/sanitizer.js b/js/dist/util/sanitizer.js
new file mode 100644
index 000000000..fce1bc29d
--- /dev/null
+++ b/js/dist/util/sanitizer.js
@@ -0,0 +1,114 @@
+/*!
+ * Bootstrap sanitizer.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
+ typeof define === 'function' && define.amd ? define(['exports'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.Sanitizer = {}));
+})(this, (function (exports) { 'use strict';
+
+ /**
+ * --------------------------------------------------------------------------
+ * Bootstrap util/sanitizer.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+
+ // js-docs-start allow-list
+ const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i;
+ const DefaultAllowlist = {
+ // Global attributes allowed on any supplied element below.
+ '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
+ a: ['target', 'href', 'title', 'rel'],
+ area: [],
+ b: [],
+ br: [],
+ col: [],
+ code: [],
+ dd: [],
+ div: [],
+ dl: [],
+ dt: [],
+ em: [],
+ hr: [],
+ h1: [],
+ h2: [],
+ h3: [],
+ h4: [],
+ h5: [],
+ h6: [],
+ i: [],
+ img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],
+ li: [],
+ ol: [],
+ p: [],
+ pre: [],
+ s: [],
+ small: [],
+ span: [],
+ sub: [],
+ sup: [],
+ strong: [],
+ u: [],
+ ul: []
+ };
+ // js-docs-end allow-list
+
+ const uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']);
+
+ /**
+ * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation
+ * contexts.
+ *
+ * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38
+ */
+ // eslint-disable-next-line unicorn/better-regex
+ const SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i;
+ const allowedAttribute = (attribute, allowedAttributeList) => {
+ const attributeName = attribute.nodeName.toLowerCase();
+ if (allowedAttributeList.includes(attributeName)) {
+ if (uriAttributes.has(attributeName)) {
+ return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue));
+ }
+ return true;
+ }
+
+ // Check if a regular expression validates the attribute.
+ return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp).some(regex => regex.test(attributeName));
+ };
+ function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {
+ if (!unsafeHtml.length) {
+ return unsafeHtml;
+ }
+ if (sanitizeFunction && typeof sanitizeFunction === 'function') {
+ return sanitizeFunction(unsafeHtml);
+ }
+ const domParser = new window.DOMParser();
+ const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html');
+ const elements = [].concat(...createdDocument.body.querySelectorAll('*'));
+ for (const element of elements) {
+ const elementName = element.nodeName.toLowerCase();
+ if (!Object.keys(allowList).includes(elementName)) {
+ element.remove();
+ continue;
+ }
+ const attributeList = [].concat(...element.attributes);
+ const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []);
+ for (const attribute of attributeList) {
+ if (!allowedAttribute(attribute, allowedAttributes)) {
+ element.removeAttribute(attribute.nodeName);
+ }
+ }
+ }
+ return createdDocument.body.innerHTML;
+ }
+
+ exports.DefaultAllowlist = DefaultAllowlist;
+ exports.sanitizeHtml = sanitizeHtml;
+
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
+
+}));
+//# sourceMappingURL=sanitizer.js.map
diff --git a/js/dist/util/sanitizer.js.map b/js/dist/util/sanitizer.js.map
new file mode 100644
index 000000000..44973a361
--- /dev/null
+++ b/js/dist/util/sanitizer.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"sanitizer.js","sources":["../../src/util/sanitizer.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap util/sanitizer.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\n// js-docs-start allow-list\nconst ARIA_ATTRIBUTE_PATTERN = /^aria-[\\w-]*$/i\n\nexport const DefaultAllowlist = {\n // Global attributes allowed on any supplied element below.\n '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],\n a: ['target', 'href', 'title', 'rel'],\n area: [],\n b: [],\n br: [],\n col: [],\n code: [],\n dd: [],\n div: [],\n dl: [],\n dt: [],\n em: [],\n hr: [],\n h1: [],\n h2: [],\n h3: [],\n h4: [],\n h5: [],\n h6: [],\n i: [],\n img: ['src', 'srcset', 'alt', 'title', 'width', 'height'],\n li: [],\n ol: [],\n p: [],\n pre: [],\n s: [],\n small: [],\n span: [],\n sub: [],\n sup: [],\n strong: [],\n u: [],\n ul: []\n}\n// js-docs-end allow-list\n\nconst uriAttributes = new Set([\n 'background',\n 'cite',\n 'href',\n 'itemtype',\n 'longdesc',\n 'poster',\n 'src',\n 'xlink:href'\n])\n\n/**\n * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation\n * contexts.\n *\n * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38\n */\n// eslint-disable-next-line unicorn/better-regex\nconst SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i\n\nconst allowedAttribute = (attribute, allowedAttributeList) => {\n const attributeName = attribute.nodeName.toLowerCase()\n\n if (allowedAttributeList.includes(attributeName)) {\n if (uriAttributes.has(attributeName)) {\n return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue))\n }\n\n return true\n }\n\n // Check if a regular expression validates the attribute.\n return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp)\n .some(regex => regex.test(attributeName))\n}\n\nexport function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {\n if (!unsafeHtml.length) {\n return unsafeHtml\n }\n\n if (sanitizeFunction && typeof sanitizeFunction === 'function') {\n return sanitizeFunction(unsafeHtml)\n }\n\n const domParser = new window.DOMParser()\n const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html')\n const elements = [].concat(...createdDocument.body.querySelectorAll('*'))\n\n for (const element of elements) {\n const elementName = element.nodeName.toLowerCase()\n\n if (!Object.keys(allowList).includes(elementName)) {\n element.remove()\n continue\n }\n\n const attributeList = [].concat(...element.attributes)\n const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || [])\n\n for (const attribute of attributeList) {\n if (!allowedAttribute(attribute, allowedAttributes)) {\n element.removeAttribute(attribute.nodeName)\n }\n }\n }\n\n return createdDocument.body.innerHTML\n}\n"],"names":["ARIA_ATTRIBUTE_PATTERN","DefaultAllowlist","a","area","b","br","col","code","dd","div","dl","dt","em","hr","h1","h2","h3","h4","h5","h6","i","img","li","ol","p","pre","s","small","span","sub","sup","strong","u","ul","uriAttributes","Set","SAFE_URL_PATTERN","allowedAttribute","attribute","allowedAttributeList","attributeName","nodeName","toLowerCase","includes","has","Boolean","test","nodeValue","filter","attributeRegex","RegExp","some","regex","sanitizeHtml","unsafeHtml","allowList","sanitizeFunction","length","domParser","window","DOMParser","createdDocument","parseFromString","elements","concat","body","querySelectorAll","element","elementName","Object","keys","remove","attributeList","attributes","allowedAttributes","removeAttribute","innerHTML"],"mappings":"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"} \ No newline at end of file
diff --git a/js/dist/util/scrollbar.js b/js/dist/util/scrollbar.js
new file mode 100644
index 000000000..15dbadb24
--- /dev/null
+++ b/js/dist/util/scrollbar.js
@@ -0,0 +1,113 @@
+/*!
+ * Bootstrap scrollbar.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../dom/manipulator.js'), require('../dom/selector-engine.js'), require('./index.js')) :
+ typeof define === 'function' && define.amd ? define(['../dom/manipulator', '../dom/selector-engine', './index'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Scrollbar = factory(global.Manipulator, global.SelectorEngine, global.Index));
+})(this, (function (Manipulator, SelectorEngine, index_js) { 'use strict';
+
+ /**
+ * --------------------------------------------------------------------------
+ * Bootstrap util/scrollBar.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+
+
+ /**
+ * Constants
+ */
+
+ const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top';
+ const SELECTOR_STICKY_CONTENT = '.sticky-top';
+ const PROPERTY_PADDING = 'padding-right';
+ const PROPERTY_MARGIN = 'margin-right';
+
+ /**
+ * Class definition
+ */
+
+ class ScrollBarHelper {
+ constructor() {
+ this._element = document.body;
+ }
+
+ // Public
+ getWidth() {
+ // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes
+ const documentWidth = document.documentElement.clientWidth;
+ return Math.abs(window.innerWidth - documentWidth);
+ }
+ hide() {
+ const width = this.getWidth();
+ this._disableOverFlow();
+ // give padding to element to balance the hidden scrollbar width
+ this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width);
+ // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth
+ this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width);
+ this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width);
+ }
+ reset() {
+ this._resetElementAttributes(this._element, 'overflow');
+ this._resetElementAttributes(this._element, PROPERTY_PADDING);
+ this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING);
+ this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN);
+ }
+ isOverflowing() {
+ return this.getWidth() > 0;
+ }
+
+ // Private
+ _disableOverFlow() {
+ this._saveInitialAttribute(this._element, 'overflow');
+ this._element.style.overflow = 'hidden';
+ }
+ _setElementAttributes(selector, styleProperty, callback) {
+ const scrollbarWidth = this.getWidth();
+ const manipulationCallBack = element => {
+ if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {
+ return;
+ }
+ this._saveInitialAttribute(element, styleProperty);
+ const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty);
+ element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`);
+ };
+ this._applyManipulationCallback(selector, manipulationCallBack);
+ }
+ _saveInitialAttribute(element, styleProperty) {
+ const actualValue = element.style.getPropertyValue(styleProperty);
+ if (actualValue) {
+ Manipulator.setDataAttribute(element, styleProperty, actualValue);
+ }
+ }
+ _resetElementAttributes(selector, styleProperty) {
+ const manipulationCallBack = element => {
+ const value = Manipulator.getDataAttribute(element, styleProperty);
+ // We only want to remove the property if the value is `null`; the value can also be zero
+ if (value === null) {
+ element.style.removeProperty(styleProperty);
+ return;
+ }
+ Manipulator.removeDataAttribute(element, styleProperty);
+ element.style.setProperty(styleProperty, value);
+ };
+ this._applyManipulationCallback(selector, manipulationCallBack);
+ }
+ _applyManipulationCallback(selector, callBack) {
+ if (index_js.isElement(selector)) {
+ callBack(selector);
+ return;
+ }
+ for (const sel of SelectorEngine.find(selector, this._element)) {
+ callBack(sel);
+ }
+ }
+ }
+
+ return ScrollBarHelper;
+
+}));
+//# sourceMappingURL=scrollbar.js.map
diff --git a/js/dist/util/scrollbar.js.map b/js/dist/util/scrollbar.js.map
new file mode 100644
index 000000000..138317c8c
--- /dev/null
+++ b/js/dist/util/scrollbar.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"scrollbar.js","sources":["../../src/util/scrollbar.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap util/scrollBar.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport Manipulator from '../dom/manipulator.js'\nimport SelectorEngine from '../dom/selector-engine.js'\nimport { isElement } from './index.js'\n\n/**\n * Constants\n */\n\nconst SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'\nconst SELECTOR_STICKY_CONTENT = '.sticky-top'\nconst PROPERTY_PADDING = 'padding-right'\nconst PROPERTY_MARGIN = 'margin-right'\n\n/**\n * Class definition\n */\n\nclass ScrollBarHelper {\n constructor() {\n this._element = document.body\n }\n\n // Public\n getWidth() {\n // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes\n const documentWidth = document.documentElement.clientWidth\n return Math.abs(window.innerWidth - documentWidth)\n }\n\n hide() {\n const width = this.getWidth()\n this._disableOverFlow()\n // give padding to element to balance the hidden scrollbar width\n this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width)\n // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth\n this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width)\n this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width)\n }\n\n reset() {\n this._resetElementAttributes(this._element, 'overflow')\n this._resetElementAttributes(this._element, PROPERTY_PADDING)\n this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING)\n this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN)\n }\n\n isOverflowing() {\n return this.getWidth() > 0\n }\n\n // Private\n _disableOverFlow() {\n this._saveInitialAttribute(this._element, 'overflow')\n this._element.style.overflow = 'hidden'\n }\n\n _setElementAttributes(selector, styleProperty, callback) {\n const scrollbarWidth = this.getWidth()\n const manipulationCallBack = element => {\n if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {\n return\n }\n\n this._saveInitialAttribute(element, styleProperty)\n const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty)\n element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`)\n }\n\n this._applyManipulationCallback(selector, manipulationCallBack)\n }\n\n _saveInitialAttribute(element, styleProperty) {\n const actualValue = element.style.getPropertyValue(styleProperty)\n if (actualValue) {\n Manipulator.setDataAttribute(element, styleProperty, actualValue)\n }\n }\n\n _resetElementAttributes(selector, styleProperty) {\n const manipulationCallBack = element => {\n const value = Manipulator.getDataAttribute(element, styleProperty)\n // We only want to remove the property if the value is `null`; the value can also be zero\n if (value === null) {\n element.style.removeProperty(styleProperty)\n return\n }\n\n Manipulator.removeDataAttribute(element, styleProperty)\n element.style.setProperty(styleProperty, value)\n }\n\n this._applyManipulationCallback(selector, manipulationCallBack)\n }\n\n _applyManipulationCallback(selector, callBack) {\n if (isElement(selector)) {\n callBack(selector)\n return\n }\n\n for (const sel of SelectorEngine.find(selector, this._element)) {\n callBack(sel)\n }\n }\n}\n\nexport default ScrollBarHelper\n"],"names":["SELECTOR_FIXED_CONTENT","SELECTOR_STICKY_CONTENT","PROPERTY_PADDING","PROPERTY_MARGIN","ScrollBarHelper","constructor","_element","document","body","getWidth","documentWidth","documentElement","clientWidth","Math","abs","window","innerWidth","hide","width","_disableOverFlow","_setElementAttributes","calculatedValue","reset","_resetElementAttributes","isOverflowing","_saveInitialAttribute","style","overflow","selector","styleProperty","callback","scrollbarWidth","manipulationCallBack","element","getComputedStyle","getPropertyValue","setProperty","Number","parseFloat","_applyManipulationCallback","actualValue","Manipulator","setDataAttribute","value","getDataAttribute","removeProperty","removeDataAttribute","callBack","isElement","sel","SelectorEngine","find"],"mappings":"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"} \ No newline at end of file
diff --git a/js/dist/util/swipe.js b/js/dist/util/swipe.js
new file mode 100644
index 000000000..f99fd1906
--- /dev/null
+++ b/js/dist/util/swipe.js
@@ -0,0 +1,135 @@
+/*!
+ * Bootstrap swipe.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../dom/event-handler.js'), require('./config.js'), require('./index.js')) :
+ typeof define === 'function' && define.amd ? define(['../dom/event-handler', './config', './index'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Swipe = factory(global.EventHandler, global.Config, global.Index));
+})(this, (function (EventHandler, Config, index_js) { 'use strict';
+
+ /**
+ * --------------------------------------------------------------------------
+ * Bootstrap util/swipe.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+
+
+ /**
+ * Constants
+ */
+
+ const NAME = 'swipe';
+ const EVENT_KEY = '.bs.swipe';
+ const EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`;
+ const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}`;
+ const EVENT_TOUCHEND = `touchend${EVENT_KEY}`;
+ const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`;
+ const EVENT_POINTERUP = `pointerup${EVENT_KEY}`;
+ const POINTER_TYPE_TOUCH = 'touch';
+ const POINTER_TYPE_PEN = 'pen';
+ const CLASS_NAME_POINTER_EVENT = 'pointer-event';
+ const SWIPE_THRESHOLD = 40;
+ const Default = {
+ endCallback: null,
+ leftCallback: null,
+ rightCallback: null
+ };
+ const DefaultType = {
+ endCallback: '(function|null)',
+ leftCallback: '(function|null)',
+ rightCallback: '(function|null)'
+ };
+
+ /**
+ * Class definition
+ */
+
+ class Swipe extends Config {
+ constructor(element, config) {
+ super();
+ this._element = element;
+ if (!element || !Swipe.isSupported()) {
+ return;
+ }
+ this._config = this._getConfig(config);
+ this._deltaX = 0;
+ this._supportPointerEvents = Boolean(window.PointerEvent);
+ this._initEvents();
+ }
+
+ // Getters
+ static get Default() {
+ return Default;
+ }
+ static get DefaultType() {
+ return DefaultType;
+ }
+ static get NAME() {
+ return NAME;
+ }
+
+ // Public
+ dispose() {
+ EventHandler.off(this._element, EVENT_KEY);
+ }
+
+ // Private
+ _start(event) {
+ if (!this._supportPointerEvents) {
+ this._deltaX = event.touches[0].clientX;
+ return;
+ }
+ if (this._eventIsPointerPenTouch(event)) {
+ this._deltaX = event.clientX;
+ }
+ }
+ _end(event) {
+ if (this._eventIsPointerPenTouch(event)) {
+ this._deltaX = event.clientX - this._deltaX;
+ }
+ this._handleSwipe();
+ index_js.execute(this._config.endCallback);
+ }
+ _move(event) {
+ this._deltaX = event.touches && event.touches.length > 1 ? 0 : event.touches[0].clientX - this._deltaX;
+ }
+ _handleSwipe() {
+ const absDeltaX = Math.abs(this._deltaX);
+ if (absDeltaX <= SWIPE_THRESHOLD) {
+ return;
+ }
+ const direction = absDeltaX / this._deltaX;
+ this._deltaX = 0;
+ if (!direction) {
+ return;
+ }
+ index_js.execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback);
+ }
+ _initEvents() {
+ if (this._supportPointerEvents) {
+ EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event));
+ EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event));
+ this._element.classList.add(CLASS_NAME_POINTER_EVENT);
+ } else {
+ EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event));
+ EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event));
+ EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event));
+ }
+ }
+ _eventIsPointerPenTouch(event) {
+ return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH);
+ }
+
+ // Static
+ static isSupported() {
+ return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0;
+ }
+ }
+
+ return Swipe;
+
+}));
+//# sourceMappingURL=swipe.js.map
diff --git a/js/dist/util/swipe.js.map b/js/dist/util/swipe.js.map
new file mode 100644
index 000000000..7d88e5fd6
--- /dev/null
+++ b/js/dist/util/swipe.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"swipe.js","sources":["../../src/util/swipe.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap util/swipe.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport EventHandler from '../dom/event-handler.js'\nimport Config from './config.js'\nimport { execute } from './index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'swipe'\nconst EVENT_KEY = '.bs.swipe'\nconst EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`\nconst EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}`\nconst EVENT_TOUCHEND = `touchend${EVENT_KEY}`\nconst EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`\nconst EVENT_POINTERUP = `pointerup${EVENT_KEY}`\nconst POINTER_TYPE_TOUCH = 'touch'\nconst POINTER_TYPE_PEN = 'pen'\nconst CLASS_NAME_POINTER_EVENT = 'pointer-event'\nconst SWIPE_THRESHOLD = 40\n\nconst Default = {\n endCallback: null,\n leftCallback: null,\n rightCallback: null\n}\n\nconst DefaultType = {\n endCallback: '(function|null)',\n leftCallback: '(function|null)',\n rightCallback: '(function|null)'\n}\n\n/**\n * Class definition\n */\n\nclass Swipe extends Config {\n constructor(element, config) {\n super()\n this._element = element\n\n if (!element || !Swipe.isSupported()) {\n return\n }\n\n this._config = this._getConfig(config)\n this._deltaX = 0\n this._supportPointerEvents = Boolean(window.PointerEvent)\n this._initEvents()\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n dispose() {\n EventHandler.off(this._element, EVENT_KEY)\n }\n\n // Private\n _start(event) {\n if (!this._supportPointerEvents) {\n this._deltaX = event.touches[0].clientX\n\n return\n }\n\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX\n }\n }\n\n _end(event) {\n if (this._eventIsPointerPenTouch(event)) {\n this._deltaX = event.clientX - this._deltaX\n }\n\n this._handleSwipe()\n execute(this._config.endCallback)\n }\n\n _move(event) {\n this._deltaX = event.touches && event.touches.length > 1 ?\n 0 :\n event.touches[0].clientX - this._deltaX\n }\n\n _handleSwipe() {\n const absDeltaX = Math.abs(this._deltaX)\n\n if (absDeltaX <= SWIPE_THRESHOLD) {\n return\n }\n\n const direction = absDeltaX / this._deltaX\n\n this._deltaX = 0\n\n if (!direction) {\n return\n }\n\n execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback)\n }\n\n _initEvents() {\n if (this._supportPointerEvents) {\n EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event))\n EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event))\n\n this._element.classList.add(CLASS_NAME_POINTER_EVENT)\n } else {\n EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event))\n EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event))\n EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event))\n }\n }\n\n _eventIsPointerPenTouch(event) {\n return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH)\n }\n\n // Static\n static isSupported() {\n return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0\n }\n}\n\nexport default Swipe\n"],"names":["NAME","EVENT_KEY","EVENT_TOUCHSTART","EVENT_TOUCHMOVE","EVENT_TOUCHEND","EVENT_POINTERDOWN","EVENT_POINTERUP","POINTER_TYPE_TOUCH","POINTER_TYPE_PEN","CLASS_NAME_POINTER_EVENT","SWIPE_THRESHOLD","Default","endCallback","leftCallback","rightCallback","DefaultType","Swipe","Config","constructor","element","config","_element","isSupported","_config","_getConfig","_deltaX","_supportPointerEvents","Boolean","window","PointerEvent","_initEvents","dispose","EventHandler","off","_start","event","touches","clientX","_eventIsPointerPenTouch","_end","_handleSwipe","execute","_move","length","absDeltaX","Math","abs","direction","on","classList","add","pointerType","document","documentElement","navigator","maxTouchPoints"],"mappings":"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"} \ No newline at end of file
diff --git a/js/dist/util/template-factory.js b/js/dist/util/template-factory.js
new file mode 100644
index 000000000..75f225467
--- /dev/null
+++ b/js/dist/util/template-factory.js
@@ -0,0 +1,151 @@
+/*!
+ * Bootstrap template-factory.js v5.3.3 (https://getbootstrap.com/)
+ * Copyright 2011-2024 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ */
+(function (global, factory) {
+ typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('../dom/selector-engine.js'), require('./config.js'), require('./sanitizer.js'), require('./index.js')) :
+ typeof define === 'function' && define.amd ? define(['../dom/selector-engine', './config', './sanitizer', './index'], factory) :
+ (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.TemplateFactory = factory(global.SelectorEngine, global.Config, global.Sanitizer, global.Index));
+})(this, (function (SelectorEngine, Config, sanitizer_js, index_js) { 'use strict';
+
+ /**
+ * --------------------------------------------------------------------------
+ * Bootstrap util/template-factory.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+
+
+ /**
+ * Constants
+ */
+
+ const NAME = 'TemplateFactory';
+ const Default = {
+ allowList: sanitizer_js.DefaultAllowlist,
+ content: {},
+ // { selector : text , selector2 : text2 , }
+ extraClass: '',
+ html: false,
+ sanitize: true,
+ sanitizeFn: null,
+ template: '<div></div>'
+ };
+ const DefaultType = {
+ allowList: 'object',
+ content: 'object',
+ extraClass: '(string|function)',
+ html: 'boolean',
+ sanitize: 'boolean',
+ sanitizeFn: '(null|function)',
+ template: 'string'
+ };
+ const DefaultContentType = {
+ entry: '(string|element|function|null)',
+ selector: '(string|element)'
+ };
+
+ /**
+ * Class definition
+ */
+
+ class TemplateFactory extends Config {
+ constructor(config) {
+ super();
+ this._config = this._getConfig(config);
+ }
+
+ // Getters
+ static get Default() {
+ return Default;
+ }
+ static get DefaultType() {
+ return DefaultType;
+ }
+ static get NAME() {
+ return NAME;
+ }
+
+ // Public
+ getContent() {
+ return Object.values(this._config.content).map(config => this._resolvePossibleFunction(config)).filter(Boolean);
+ }
+ hasContent() {
+ return this.getContent().length > 0;
+ }
+ changeContent(content) {
+ this._checkContent(content);
+ this._config.content = {
+ ...this._config.content,
+ ...content
+ };
+ return this;
+ }
+ toHtml() {
+ const templateWrapper = document.createElement('div');
+ templateWrapper.innerHTML = this._maybeSanitize(this._config.template);
+ for (const [selector, text] of Object.entries(this._config.content)) {
+ this._setContent(templateWrapper, text, selector);
+ }
+ const template = templateWrapper.children[0];
+ const extraClass = this._resolvePossibleFunction(this._config.extraClass);
+ if (extraClass) {
+ template.classList.add(...extraClass.split(' '));
+ }
+ return template;
+ }
+
+ // Private
+ _typeCheckConfig(config) {
+ super._typeCheckConfig(config);
+ this._checkContent(config.content);
+ }
+ _checkContent(arg) {
+ for (const [selector, content] of Object.entries(arg)) {
+ super._typeCheckConfig({
+ selector,
+ entry: content
+ }, DefaultContentType);
+ }
+ }
+ _setContent(template, content, selector) {
+ const templateElement = SelectorEngine.findOne(selector, template);
+ if (!templateElement) {
+ return;
+ }
+ content = this._resolvePossibleFunction(content);
+ if (!content) {
+ templateElement.remove();
+ return;
+ }
+ if (index_js.isElement(content)) {
+ this._putElementInTemplate(index_js.getElement(content), templateElement);
+ return;
+ }
+ if (this._config.html) {
+ templateElement.innerHTML = this._maybeSanitize(content);
+ return;
+ }
+ templateElement.textContent = content;
+ }
+ _maybeSanitize(arg) {
+ return this._config.sanitize ? sanitizer_js.sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg;
+ }
+ _resolvePossibleFunction(arg) {
+ return index_js.execute(arg, [this]);
+ }
+ _putElementInTemplate(element, templateElement) {
+ if (this._config.html) {
+ templateElement.innerHTML = '';
+ templateElement.append(element);
+ return;
+ }
+ templateElement.textContent = element.textContent;
+ }
+ }
+
+ return TemplateFactory;
+
+}));
+//# sourceMappingURL=template-factory.js.map
diff --git a/js/dist/util/template-factory.js.map b/js/dist/util/template-factory.js.map
new file mode 100644
index 000000000..4815d382d
--- /dev/null
+++ b/js/dist/util/template-factory.js.map
@@ -0,0 +1 @@
+{"version":3,"file":"template-factory.js","sources":["../../src/util/template-factory.js"],"sourcesContent":["/**\n * --------------------------------------------------------------------------\n * Bootstrap util/template-factory.js\n * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)\n * --------------------------------------------------------------------------\n */\n\nimport SelectorEngine from '../dom/selector-engine.js'\nimport Config from './config.js'\nimport { DefaultAllowlist, sanitizeHtml } from './sanitizer.js'\nimport { execute, getElement, isElement } from './index.js'\n\n/**\n * Constants\n */\n\nconst NAME = 'TemplateFactory'\n\nconst Default = {\n allowList: DefaultAllowlist,\n content: {}, // { selector : text , selector2 : text2 , }\n extraClass: '',\n html: false,\n sanitize: true,\n sanitizeFn: null,\n template: '<div></div>'\n}\n\nconst DefaultType = {\n allowList: 'object',\n content: 'object',\n extraClass: '(string|function)',\n html: 'boolean',\n sanitize: 'boolean',\n sanitizeFn: '(null|function)',\n template: 'string'\n}\n\nconst DefaultContentType = {\n entry: '(string|element|function|null)',\n selector: '(string|element)'\n}\n\n/**\n * Class definition\n */\n\nclass TemplateFactory extends Config {\n constructor(config) {\n super()\n this._config = this._getConfig(config)\n }\n\n // Getters\n static get Default() {\n return Default\n }\n\n static get DefaultType() {\n return DefaultType\n }\n\n static get NAME() {\n return NAME\n }\n\n // Public\n getContent() {\n return Object.values(this._config.content)\n .map(config => this._resolvePossibleFunction(config))\n .filter(Boolean)\n }\n\n hasContent() {\n return this.getContent().length > 0\n }\n\n changeContent(content) {\n this._checkContent(content)\n this._config.content = { ...this._config.content, ...content }\n return this\n }\n\n toHtml() {\n const templateWrapper = document.createElement('div')\n templateWrapper.innerHTML = this._maybeSanitize(this._config.template)\n\n for (const [selector, text] of Object.entries(this._config.content)) {\n this._setContent(templateWrapper, text, selector)\n }\n\n const template = templateWrapper.children[0]\n const extraClass = this._resolvePossibleFunction(this._config.extraClass)\n\n if (extraClass) {\n template.classList.add(...extraClass.split(' '))\n }\n\n return template\n }\n\n // Private\n _typeCheckConfig(config) {\n super._typeCheckConfig(config)\n this._checkContent(config.content)\n }\n\n _checkContent(arg) {\n for (const [selector, content] of Object.entries(arg)) {\n super._typeCheckConfig({ selector, entry: content }, DefaultContentType)\n }\n }\n\n _setContent(template, content, selector) {\n const templateElement = SelectorEngine.findOne(selector, template)\n\n if (!templateElement) {\n return\n }\n\n content = this._resolvePossibleFunction(content)\n\n if (!content) {\n templateElement.remove()\n return\n }\n\n if (isElement(content)) {\n this._putElementInTemplate(getElement(content), templateElement)\n return\n }\n\n if (this._config.html) {\n templateElement.innerHTML = this._maybeSanitize(content)\n return\n }\n\n templateElement.textContent = content\n }\n\n _maybeSanitize(arg) {\n return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg\n }\n\n _resolvePossibleFunction(arg) {\n return execute(arg, [this])\n }\n\n _putElementInTemplate(element, templateElement) {\n if (this._config.html) {\n templateElement.innerHTML = ''\n templateElement.append(element)\n return\n }\n\n templateElement.textContent = element.textContent\n }\n}\n\nexport default TemplateFactory\n"],"names":["NAME","Default","allowList","DefaultAllowlist","content","extraClass","html","sanitize","sanitizeFn","template","DefaultType","DefaultContentType","entry","selector","TemplateFactory","Config","constructor","config","_config","_getConfig","getContent","Object","values","map","_resolvePossibleFunction","filter","Boolean","hasContent","length","changeContent","_checkContent","toHtml","templateWrapper","document","createElement","innerHTML","_maybeSanitize","text","entries","_setContent","children","classList","add","split","_typeCheckConfig","arg","templateElement","SelectorEngine","findOne","remove","isElement","_putElementInTemplate","getElement","textContent","sanitizeHtml","execute","element","append"],"mappings":"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"} \ No newline at end of file
diff --git a/js/index.esm.js b/js/index.esm.js
index 51a9ff8ea..155d9fb6a 100644
--- a/js/index.esm.js
+++ b/js/index.esm.js
@@ -1,19 +1,19 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): index.esm.js
+ * Bootstrap index.esm.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-export { default as Alert } from './src/alert'
-export { default as Button } from './src/button'
-export { default as Carousel } from './src/carousel'
-export { default as Collapse } from './src/collapse'
-export { default as Dropdown } from './src/dropdown'
-export { default as Modal } from './src/modal'
-export { default as Offcanvas } from './src/offcanvas'
-export { default as Popover } from './src/popover'
-export { default as ScrollSpy } from './src/scrollspy'
-export { default as Tab } from './src/tab'
-export { default as Toast } from './src/toast'
-export { default as Tooltip } from './src/tooltip'
+export { default as Alert } from './src/alert.js'
+export { default as Button } from './src/button.js'
+export { default as Carousel } from './src/carousel.js'
+export { default as Collapse } from './src/collapse.js'
+export { default as Dropdown } from './src/dropdown.js'
+export { default as Modal } from './src/modal.js'
+export { default as Offcanvas } from './src/offcanvas.js'
+export { default as Popover } from './src/popover.js'
+export { default as ScrollSpy } from './src/scrollspy.js'
+export { default as Tab } from './src/tab.js'
+export { default as Toast } from './src/toast.js'
+export { default as Tooltip } from './src/tooltip.js'
diff --git a/js/index.umd.js b/js/index.umd.js
index d6e587fb1..a33df7465 100644
--- a/js/index.umd.js
+++ b/js/index.umd.js
@@ -1,22 +1,22 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): index.umd.js
+ * Bootstrap index.umd.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import Alert from './src/alert'
-import Button from './src/button'
-import Carousel from './src/carousel'
-import Collapse from './src/collapse'
-import Dropdown from './src/dropdown'
-import Modal from './src/modal'
-import Offcanvas from './src/offcanvas'
-import Popover from './src/popover'
-import ScrollSpy from './src/scrollspy'
-import Tab from './src/tab'
-import Toast from './src/toast'
-import Tooltip from './src/tooltip'
+import Alert from './src/alert.js'
+import Button from './src/button.js'
+import Carousel from './src/carousel.js'
+import Collapse from './src/collapse.js'
+import Dropdown from './src/dropdown.js'
+import Modal from './src/modal.js'
+import Offcanvas from './src/offcanvas.js'
+import Popover from './src/popover.js'
+import ScrollSpy from './src/scrollspy.js'
+import Tab from './src/tab.js'
+import Toast from './src/toast.js'
+import Tooltip from './src/tooltip.js'
export default {
Alert,
diff --git a/js/src/alert.js b/js/src/alert.js
index 7d4b555ea..88232bceb 100644
--- a/js/src/alert.js
+++ b/js/src/alert.js
@@ -1,14 +1,14 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): alert.js
+ * Bootstrap alert.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import { defineJQueryPlugin } from './util/index'
-import EventHandler from './dom/event-handler'
-import BaseComponent from './base-component'
-import { enableDismissTrigger } from './util/component-functions'
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import { enableDismissTrigger } from './util/component-functions.js'
+import { defineJQueryPlugin } from './util/index.js'
/**
* Constants
diff --git a/js/src/base-component.js b/js/src/base-component.js
index 3c5eb460a..82bf77030 100644
--- a/js/src/base-component.js
+++ b/js/src/base-component.js
@@ -1,36 +1,37 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): base-component.js
+ * Bootstrap base-component.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import Data from './dom/data'
-import {
- executeAfterTransition,
- getElement
-} from './util/index'
-import EventHandler from './dom/event-handler'
+import Data from './dom/data.js'
+import EventHandler from './dom/event-handler.js'
+import Config from './util/config.js'
+import { executeAfterTransition, getElement } from './util/index.js'
/**
* Constants
*/
-const VERSION = '5.1.3'
+const VERSION = '5.3.3'
/**
* Class definition
*/
-class BaseComponent {
- constructor(element) {
- element = getElement(element)
+class BaseComponent extends Config {
+ constructor(element, config) {
+ super()
+ element = getElement(element)
if (!element) {
return
}
this._element = element
+ this._config = this._getConfig(config)
+
Data.set(this._element, this.constructor.DATA_KEY, this)
}
@@ -48,6 +49,13 @@ class BaseComponent {
executeAfterTransition(callback, element, isAnimated)
}
+ _getConfig(config) {
+ config = this._mergeConfigObj(config, this._element)
+ config = this._configAfterMerge(config)
+ this._typeCheckConfig(config)
+ return config
+ }
+
// Static
static getInstance(element) {
return Data.get(getElement(element), this.DATA_KEY)
@@ -61,10 +69,6 @@ class BaseComponent {
return VERSION
}
- static get NAME() {
- throw new Error('You have to implement the static method "NAME" for each component!')
- }
-
static get DATA_KEY() {
return `bs.${this.NAME}`
}
@@ -72,6 +76,10 @@ class BaseComponent {
static get EVENT_KEY() {
return `.${this.DATA_KEY}`
}
+
+ static eventName(name) {
+ return `${name}${this.EVENT_KEY}`
+ }
}
export default BaseComponent
diff --git a/js/src/button.js b/js/src/button.js
index e2a52e7eb..a797f5050 100644
--- a/js/src/button.js
+++ b/js/src/button.js
@@ -1,13 +1,13 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): button.js
+ * Bootstrap button.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import { defineJQueryPlugin } from './util/index'
-import EventHandler from './dom/event-handler'
-import BaseComponent from './base-component'
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import { defineJQueryPlugin } from './util/index.js'
/**
* Constants
diff --git a/js/src/carousel.js b/js/src/carousel.js
index 3589f2206..68d11a32f 100644
--- a/js/src/carousel.js
+++ b/js/src/carousel.js
@@ -1,25 +1,23 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): carousel.js
+ * Bootstrap carousel.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import Manipulator from './dom/manipulator.js'
+import SelectorEngine from './dom/selector-engine.js'
import {
defineJQueryPlugin,
- getElementFromSelector,
getNextActiveElement,
isRTL,
isVisible,
reflow,
- triggerTransitionEnd,
- typeCheckConfig
-} from './util/index'
-import EventHandler from './dom/event-handler'
-import Manipulator from './dom/manipulator'
-import SelectorEngine from './dom/selector-engine'
-import Swipe from './util/swipe'
-import BaseComponent from './base-component'
+ triggerTransitionEnd
+} from './util/index.js'
+import Swipe from './util/swipe.js'
/**
* Constants
@@ -57,12 +55,10 @@ const CLASS_NAME_NEXT = 'carousel-item-next'
const CLASS_NAME_PREV = 'carousel-item-prev'
const SELECTOR_ACTIVE = '.active'
-const SELECTOR_ACTIVE_ITEM = '.active.carousel-item'
const SELECTOR_ITEM = '.carousel-item'
+const SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM
const SELECTOR_ITEM_IMG = '.carousel-item img'
-const SELECTOR_NEXT_PREV = '.carousel-item-next, .carousel-item-prev'
const SELECTOR_INDICATORS = '.carousel-indicators'
-const SELECTOR_INDICATOR = '[data-bs-target]'
const SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]'
const SELECTOR_DATA_RIDE = '[data-bs-ride="carousel"]'
@@ -74,19 +70,19 @@ const KEY_TO_DIRECTION = {
const Default = {
interval: 5000,
keyboard: true,
- slide: false,
pause: 'hover',
- wrap: true,
- touch: true
+ ride: false,
+ touch: true,
+ wrap: true
}
const DefaultType = {
- interval: '(number|boolean)',
+ interval: '(number|boolean)', // TODO:v6 remove boolean support
keyboard: 'boolean',
- slide: '(boolean|string)',
pause: '(string|boolean)',
- wrap: 'boolean',
- touch: 'boolean'
+ ride: '(boolean|string)',
+ touch: 'boolean',
+ wrap: 'boolean'
}
/**
@@ -95,19 +91,20 @@ const DefaultType = {
class Carousel extends BaseComponent {
constructor(element, config) {
- super(element)
+ super(element, config)
- this._items = null
this._interval = null
this._activeElement = null
- this._isPaused = false
this._isSliding = false
this.touchTimeout = null
this._swipeHelper = null
- this._config = this._getConfig(config)
this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element)
this._addEventListeners()
+
+ if (this._config.ride === CLASS_NAME_CAROUSEL) {
+ this.cycle()
+ }
}
// Getters
@@ -115,6 +112,10 @@ class Carousel extends BaseComponent {
return Default
}
+ static get DefaultType() {
+ return DefaultType
+ }
+
static get NAME() {
return NAME
}
@@ -125,6 +126,7 @@ class Carousel extends BaseComponent {
}
nextWhenVisible() {
+ // FIXME TODO use `document.visibilityState`
// Don't call next when the page isn't visible
// or the carousel or its parent isn't visible
if (!document.hidden && isVisible(this._element)) {
@@ -136,45 +138,37 @@ class Carousel extends BaseComponent {
this._slide(ORDER_PREV)
}
- pause(event) {
- if (!event) {
- this._isPaused = true
- }
-
- if (SelectorEngine.findOne(SELECTOR_NEXT_PREV, this._element)) {
+ pause() {
+ if (this._isSliding) {
triggerTransitionEnd(this._element)
- this.cycle(true)
}
- clearInterval(this._interval)
- this._interval = null
+ this._clearInterval()
}
- cycle(event) {
- if (!event) {
- this._isPaused = false
- }
+ cycle() {
+ this._clearInterval()
+ this._updateInterval()
- if (this._interval) {
- clearInterval(this._interval)
- this._interval = null
- }
+ this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval)
+ }
- if (this._config && this._config.interval && !this._isPaused) {
- this._updateInterval()
+ _maybeEnableCycle() {
+ if (!this._config.ride) {
+ return
+ }
- this._interval = setInterval(
- (document.visibilityState ? this.nextWhenVisible : this.next).bind(this),
- this._config.interval
- )
+ if (this._isSliding) {
+ EventHandler.one(this._element, EVENT_SLID, () => this.cycle())
+ return
}
+
+ this.cycle()
}
to(index) {
- this._activeElement = SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)
- const activeIndex = this._getItemIndex(this._activeElement)
-
- if (index > this._items.length - 1 || index < 0) {
+ const items = this._getItems()
+ if (index > items.length - 1 || index < 0) {
return
}
@@ -183,17 +177,14 @@ class Carousel extends BaseComponent {
return
}
+ const activeIndex = this._getItemIndex(this._getActive())
if (activeIndex === index) {
- this.pause()
- this.cycle()
return
}
- const order = index > activeIndex ?
- ORDER_NEXT :
- ORDER_PREV
+ const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV
- this._slide(order, this._items[index])
+ this._slide(order, items[index])
}
dispose() {
@@ -205,13 +196,8 @@ class Carousel extends BaseComponent {
}
// Private
- _getConfig(config) {
- config = {
- ...Default,
- ...Manipulator.getDataAttributes(this._element),
- ...(typeof config === 'object' ? config : {})
- }
- typeCheckConfig(NAME, config, DefaultType)
+ _configAfterMerge(config) {
+ config.defaultInterval = config.interval
return config
}
@@ -221,8 +207,8 @@ class Carousel extends BaseComponent {
}
if (this._config.pause === 'hover') {
- EventHandler.on(this._element, EVENT_MOUSEENTER, event => this.pause(event))
- EventHandler.on(this._element, EVENT_MOUSELEAVE, event => this.cycle(event))
+ EventHandler.on(this._element, EVENT_MOUSEENTER, () => this.pause())
+ EventHandler.on(this._element, EVENT_MOUSELEAVE, () => this._maybeEnableCycle())
}
if (this._config.touch && Swipe.isSupported()) {
@@ -231,32 +217,34 @@ class Carousel extends BaseComponent {
}
_addTouchEventListeners() {
- for (const itemImg of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {
- EventHandler.on(itemImg, EVENT_DRAG_START, event => event.preventDefault())
+ for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) {
+ EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault())
}
const endCallBack = () => {
- if (this._config.pause === 'hover') {
- // If it's a touch-enabled device, mouseenter/leave are fired as
- // part of the mouse compatibility events on first tap - the carousel
- // would stop cycling until user tapped out of it;
- // here, we listen for touchend, explicitly pause the carousel
- // (as if it's the second time we tap on it, mouseenter compat event
- // is NOT fired) and after a timeout (to allow for mouse compatibility
- // events to fire) we explicitly restart cycling
-
- this.pause()
- if (this.touchTimeout) {
- clearTimeout(this.touchTimeout)
- }
+ if (this._config.pause !== 'hover') {
+ return
+ }
+
+ // If it's a touch-enabled device, mouseenter/leave are fired as
+ // part of the mouse compatibility events on first tap - the carousel
+ // would stop cycling until user tapped out of it;
+ // here, we listen for touchend, explicitly pause the carousel
+ // (as if it's the second time we tap on it, mouseenter compat event
+ // is NOT fired) and after a timeout (to allow for mouse compatibility
+ // events to fire) we explicitly restart cycling
- this.touchTimeout = setTimeout(event => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)
+ this.pause()
+ if (this.touchTimeout) {
+ clearTimeout(this.touchTimeout)
}
+
+ this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval)
}
const swipeConfig = {
- leftCallback: () => this._slide(DIRECTION_LEFT),
- rightCallback: () => this._slide(DIRECTION_RIGHT),
+ leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)),
+ rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)),
endCallback: endCallBack
}
@@ -271,56 +259,34 @@ class Carousel extends BaseComponent {
const direction = KEY_TO_DIRECTION[event.key]
if (direction) {
event.preventDefault()
- this._slide(direction)
+ this._slide(this._directionToOrder(direction))
}
}
_getItemIndex(element) {
- this._items = element && element.parentNode ?
- SelectorEngine.find(SELECTOR_ITEM, element.parentNode) :
- []
-
- return this._items.indexOf(element)
+ return this._getItems().indexOf(element)
}
- _getItemByOrder(order, activeElement) {
- const isNext = order === ORDER_NEXT
- return getNextActiveElement(this._items, activeElement, isNext, this._config.wrap)
- }
-
- _triggerSlideEvent(relatedTarget, eventDirectionName) {
- const targetIndex = this._getItemIndex(relatedTarget)
- const fromIndex = this._getItemIndex(SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element))
-
- return EventHandler.trigger(this._element, EVENT_SLIDE, {
- relatedTarget,
- direction: eventDirectionName,
- from: fromIndex,
- to: targetIndex
- })
- }
+ _setActiveIndicatorElement(index) {
+ if (!this._indicatorsElement) {
+ return
+ }
- _setActiveIndicatorElement(element) {
- if (this._indicatorsElement) {
- const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement)
+ const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement)
- activeIndicator.classList.remove(CLASS_NAME_ACTIVE)
- activeIndicator.removeAttribute('aria-current')
+ activeIndicator.classList.remove(CLASS_NAME_ACTIVE)
+ activeIndicator.removeAttribute('aria-current')
- const indicators = SelectorEngine.find(SELECTOR_INDICATOR, this._indicatorsElement)
+ const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to="${index}"]`, this._indicatorsElement)
- for (const indicator of indicators) {
- if (Number.parseInt(indicator.getAttribute('data-bs-slide-to'), 10) === this._getItemIndex(element)) {
- indicator.classList.add(CLASS_NAME_ACTIVE)
- indicator.setAttribute('aria-current', 'true')
- break
- }
- }
+ if (newActiveIndicator) {
+ newActiveIndicator.classList.add(CLASS_NAME_ACTIVE)
+ newActiveIndicator.setAttribute('aria-current', 'true')
}
}
_updateInterval() {
- const element = this._activeElement || SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)
+ const element = this._activeElement || this._getActive()
if (!element) {
return
@@ -328,103 +294,101 @@ class Carousel extends BaseComponent {
const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10)
- if (elementInterval) {
- this._config.defaultInterval = this._config.defaultInterval || this._config.interval
- this._config.interval = elementInterval
- } else {
- this._config.interval = this._config.defaultInterval || this._config.interval
- }
+ this._config.interval = elementInterval || this._config.defaultInterval
}
- _slide(directionOrOrder, element) {
- const order = this._directionToOrder(directionOrOrder)
- const activeElement = SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)
- const activeElementIndex = this._getItemIndex(activeElement)
- const nextElement = element || this._getItemByOrder(order, activeElement)
-
- const nextElementIndex = this._getItemIndex(nextElement)
- const isCycling = Boolean(this._interval)
+ _slide(order, element = null) {
+ if (this._isSliding) {
+ return
+ }
+ const activeElement = this._getActive()
const isNext = order === ORDER_NEXT
- const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END
- const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV
- const eventDirectionName = this._orderToDirection(order)
+ const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap)
- if (nextElement && nextElement.classList.contains(CLASS_NAME_ACTIVE)) {
- this._isSliding = false
+ if (nextElement === activeElement) {
return
}
- if (this._isSliding) {
- return
+ const nextElementIndex = this._getItemIndex(nextElement)
+
+ const triggerEvent = eventName => {
+ return EventHandler.trigger(this._element, eventName, {
+ relatedTarget: nextElement,
+ direction: this._orderToDirection(order),
+ from: this._getItemIndex(activeElement),
+ to: nextElementIndex
+ })
}
- const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName)
+ const slideEvent = triggerEvent(EVENT_SLIDE)
+
if (slideEvent.defaultPrevented) {
return
}
if (!activeElement || !nextElement) {
// Some weirdness is happening, so we bail
+ // TODO: change tests that use empty divs to avoid this check
return
}
- this._isSliding = true
+ const isCycling = Boolean(this._interval)
+ this.pause()
- if (isCycling) {
- this.pause()
- }
+ this._isSliding = true
- this._setActiveIndicatorElement(nextElement)
+ this._setActiveIndicatorElement(nextElementIndex)
this._activeElement = nextElement
- const triggerSlidEvent = () => {
- EventHandler.trigger(this._element, EVENT_SLID, {
- relatedTarget: nextElement,
- direction: eventDirectionName,
- from: activeElementIndex,
- to: nextElementIndex
- })
- }
-
- if (this._element.classList.contains(CLASS_NAME_SLIDE)) {
- nextElement.classList.add(orderClassName)
-
- reflow(nextElement)
-
- activeElement.classList.add(directionalClassName)
- nextElement.classList.add(directionalClassName)
-
- const completeCallBack = () => {
- nextElement.classList.remove(directionalClassName, orderClassName)
- nextElement.classList.add(CLASS_NAME_ACTIVE)
+ const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END
+ const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV
- activeElement.classList.remove(CLASS_NAME_ACTIVE, orderClassName, directionalClassName)
+ nextElement.classList.add(orderClassName)
- this._isSliding = false
+ reflow(nextElement)
- setTimeout(triggerSlidEvent, 0)
- }
+ activeElement.classList.add(directionalClassName)
+ nextElement.classList.add(directionalClassName)
- this._queueCallback(completeCallBack, activeElement, true)
- } else {
- activeElement.classList.remove(CLASS_NAME_ACTIVE)
+ const completeCallBack = () => {
+ nextElement.classList.remove(directionalClassName, orderClassName)
nextElement.classList.add(CLASS_NAME_ACTIVE)
+ activeElement.classList.remove(CLASS_NAME_ACTIVE, orderClassName, directionalClassName)
+
this._isSliding = false
- triggerSlidEvent()
+
+ triggerEvent(EVENT_SLID)
}
+ this._queueCallback(completeCallBack, activeElement, this._isAnimated())
+
if (isCycling) {
this.cycle()
}
}
- _directionToOrder(direction) {
- if (![DIRECTION_RIGHT, DIRECTION_LEFT].includes(direction)) {
- return direction
+ _isAnimated() {
+ return this._element.classList.contains(CLASS_NAME_SLIDE)
+ }
+
+ _getActive() {
+ return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)
+ }
+
+ _getItems() {
+ return SelectorEngine.find(SELECTOR_ITEM, this._element)
+ }
+
+ _clearInterval() {
+ if (this._interval) {
+ clearInterval(this._interval)
+ this._interval = null
}
+ }
+ _directionToOrder(direction) {
if (isRTL()) {
return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT
}
@@ -433,10 +397,6 @@ class Carousel extends BaseComponent {
}
_orderToDirection(order) {
- if (![ORDER_NEXT, ORDER_PREV].includes(order)) {
- return order
- }
-
if (isRTL()) {
return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT
}
@@ -445,77 +405,63 @@ class Carousel extends BaseComponent {
}
// Static
- static carouselInterface(element, config) {
- const data = Carousel.getOrCreateInstance(element, config)
-
- let { _config } = data
- if (typeof config === 'object') {
- _config = {
- ..._config,
- ...config
- }
- }
-
- const action = typeof config === 'string' ? config : _config.slide
+ static jQueryInterface(config) {
+ return this.each(function () {
+ const data = Carousel.getOrCreateInstance(this, config)
- if (typeof config === 'number') {
- data.to(config)
- } else if (typeof action === 'string') {
- if (typeof data[action] === 'undefined') {
- throw new TypeError(`No method named "${action}"`)
+ if (typeof config === 'number') {
+ data.to(config)
+ return
}
- data[action]()
- } else if (_config.interval && _config.ride) {
- data.pause()
- data.cycle()
- }
- }
+ if (typeof config === 'string') {
+ if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
+ throw new TypeError(`No method named "${config}"`)
+ }
- static jQueryInterface(config) {
- return this.each(function () {
- Carousel.carouselInterface(this, config)
+ data[config]()
+ }
})
}
+}
- static dataApiClickHandler(event) {
- const target = getElementFromSelector(this)
-
- if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {
- return
- }
+/**
+ * Data API implementation
+ */
- const config = {
- ...Manipulator.getDataAttributes(target),
- ...Manipulator.getDataAttributes(this)
- }
- const slideIndex = this.getAttribute('data-bs-slide-to')
+EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, function (event) {
+ const target = SelectorEngine.getElementFromSelector(this)
- if (slideIndex) {
- config.interval = false
- }
+ if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {
+ return
+ }
- Carousel.carouselInterface(target, config)
+ event.preventDefault()
- if (slideIndex) {
- Carousel.getInstance(target).to(slideIndex)
- }
+ const carousel = Carousel.getOrCreateInstance(target)
+ const slideIndex = this.getAttribute('data-bs-slide-to')
- event.preventDefault()
+ if (slideIndex) {
+ carousel.to(slideIndex)
+ carousel._maybeEnableCycle()
+ return
}
-}
-/**
- * Data API implementation
- */
+ if (Manipulator.getDataAttribute(this, 'slide') === 'next') {
+ carousel.next()
+ carousel._maybeEnableCycle()
+ return
+ }
-EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, Carousel.dataApiClickHandler)
+ carousel.prev()
+ carousel._maybeEnableCycle()
+})
EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE)
for (const carousel of carousels) {
- Carousel.carouselInterface(carousel, Carousel.getInstance(carousel))
+ Carousel.getOrCreateInstance(carousel)
}
})
diff --git a/js/src/collapse.js b/js/src/collapse.js
index 642f7e840..9f0c60cc5 100644
--- a/js/src/collapse.js
+++ b/js/src/collapse.js
@@ -1,22 +1,18 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): collapse.js
+ * Bootstrap collapse.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import SelectorEngine from './dom/selector-engine.js'
import {
defineJQueryPlugin,
getElement,
- getElementFromSelector,
- getSelectorFromElement,
- reflow,
- typeCheckConfig
-} from './util/index'
-import EventHandler from './dom/event-handler'
-import Manipulator from './dom/manipulator'
-import SelectorEngine from './dom/selector-engine'
-import BaseComponent from './base-component'
+ reflow
+} from './util/index.js'
/**
* Constants
@@ -47,13 +43,13 @@ const SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing'
const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="collapse"]'
const Default = {
- toggle: true,
- parent: null
+ parent: null,
+ toggle: true
}
const DefaultType = {
- toggle: 'boolean',
- parent: '(null|element)'
+ parent: '(null|element)',
+ toggle: 'boolean'
}
/**
@@ -62,18 +58,17 @@ const DefaultType = {
class Collapse extends BaseComponent {
constructor(element, config) {
- super(element)
+ super(element, config)
this._isTransitioning = false
- this._config = this._getConfig(config)
this._triggerArray = []
const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE)
for (const elem of toggleList) {
- const selector = getSelectorFromElement(elem)
+ const selector = SelectorEngine.getSelectorFromElement(elem)
const filterElement = SelectorEngine.find(selector)
- .filter(foundElem => foundElem === this._element)
+ .filter(foundElement => foundElement === this._element)
if (selector !== null && filterElement.length) {
this._triggerArray.push(elem)
@@ -96,6 +91,10 @@ class Collapse extends BaseComponent {
return Default
}
+ static get DefaultType() {
+ return DefaultType
+ }
+
static get NAME() {
return NAME
}
@@ -184,9 +183,9 @@ class Collapse extends BaseComponent {
this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW)
for (const trigger of this._triggerArray) {
- const elem = getElementFromSelector(trigger)
+ const element = SelectorEngine.getElementFromSelector(trigger)
- if (elem && !this._isShown(elem)) {
+ if (element && !this._isShown(element)) {
this._addAriaAndCollapsedClass([trigger], false)
}
}
@@ -210,15 +209,9 @@ class Collapse extends BaseComponent {
}
// Private
- _getConfig(config) {
- config = {
- ...Default,
- ...Manipulator.getDataAttributes(this._element),
- ...config
- }
+ _configAfterMerge(config) {
config.toggle = Boolean(config.toggle) // Coerce string values
config.parent = getElement(config.parent)
- typeCheckConfig(NAME, config, DefaultType)
return config
}
@@ -234,7 +227,7 @@ class Collapse extends BaseComponent {
const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE)
for (const element of children) {
- const selected = getElementFromSelector(element)
+ const selected = SelectorEngine.getElementFromSelector(element)
if (selected) {
this._addAriaAndCollapsedClass([element], this._isShown(selected))
@@ -245,7 +238,7 @@ class Collapse extends BaseComponent {
_getFirstLevelChildren(selector) {
const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent)
// remove children if greater depth
- return SelectorEngine.find(selector, this._config.parent).filter(elem => !children.includes(elem))
+ return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element))
}
_addAriaAndCollapsedClass(triggerArray, isOpen) {
@@ -253,25 +246,20 @@ class Collapse extends BaseComponent {
return
}
- for (const elem of triggerArray) {
- if (isOpen) {
- elem.classList.remove(CLASS_NAME_COLLAPSED)
- } else {
- elem.classList.add(CLASS_NAME_COLLAPSED)
- }
-
- elem.setAttribute('aria-expanded', isOpen)
+ for (const element of triggerArray) {
+ element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen)
+ element.setAttribute('aria-expanded', isOpen)
}
}
// Static
static jQueryInterface(config) {
- return this.each(function () {
- const _config = {}
- if (typeof config === 'string' && /show|hide/.test(config)) {
- _config.toggle = false
- }
+ const _config = {}
+ if (typeof config === 'string' && /show|hide/.test(config)) {
+ _config.toggle = false
+ }
+ return this.each(function () {
const data = Collapse.getOrCreateInstance(this, _config)
if (typeof config === 'string') {
@@ -295,10 +283,7 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
event.preventDefault()
}
- const selector = getSelectorFromElement(this)
- const selectorElements = SelectorEngine.find(selector)
-
- for (const element of selectorElements) {
+ for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) {
Collapse.getOrCreateInstance(element, { toggle: false }).toggle()
}
})
diff --git a/js/src/dom/data.js b/js/src/dom/data.js
index 4209f3188..407f67e39 100644
--- a/js/src/dom/data.js
+++ b/js/src/dom/data.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): dom/data.js
+ * Bootstrap dom/data.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
diff --git a/js/src/dom/event-handler.js b/js/src/dom/event-handler.js
index b9ebce324..561d8751d 100644
--- a/js/src/dom/event-handler.js
+++ b/js/src/dom/event-handler.js
@@ -1,11 +1,11 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): dom/event-handler.js
+ * Bootstrap dom/event-handler.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import { getjQuery } from '../util/index'
+import { getjQuery } from '../util/index.js'
/**
* Constants
@@ -20,7 +20,7 @@ const customEvents = {
mouseenter: 'mouseover',
mouseleave: 'mouseout'
}
-const customEventsRegex = /^(mouseenter|mouseleave)/i
+
const nativeEvents = new Set([
'click',
'dblclick',
@@ -74,12 +74,12 @@ const nativeEvents = new Set([
* Private methods
*/
-function getUidEvent(element, uid) {
+function makeEventUid(element, uid) {
return (uid && `${uid}::${uidEvent++}`) || element.uidEvent || uidEvent++
}
-function getEvent(element) {
- const uid = getUidEvent(element)
+function getElementEvents(element) {
+ const uid = makeEventUid(element)
element.uidEvent = uid
eventRegistry[uid] = eventRegistry[uid] || {}
@@ -89,7 +89,7 @@ function getEvent(element) {
function bootstrapHandler(element, fn) {
return function handler(event) {
- event.delegateTarget = element
+ hydrateObj(event, { delegateTarget: element })
if (handler.oneOff) {
EventHandler.off(element, event.type, fn)
@@ -104,65 +104,52 @@ function bootstrapDelegationHandler(element, selector, fn) {
const domElements = element.querySelectorAll(selector)
for (let { target } = event; target && target !== this; target = target.parentNode) {
- for (let i = domElements.length; i--;) {
- if (domElements[i] === target) {
- event.delegateTarget = target
+ for (const domElement of domElements) {
+ if (domElement !== target) {
+ continue
+ }
- if (handler.oneOff) {
- EventHandler.off(element, event.type, selector, fn)
- }
+ hydrateObj(event, { delegateTarget: target })
- return fn.apply(target, [event])
+ if (handler.oneOff) {
+ EventHandler.off(element, event.type, selector, fn)
}
+
+ return fn.apply(target, [event])
}
}
-
- // To please ESLint
- return null
}
}
-function findHandler(events, handler, delegationSelector = null) {
- const uidEventList = Object.keys(events)
-
- for (const uidEvent of uidEventList) {
- const event = events[uidEvent]
-
- if (event.originalHandler === handler && event.delegationSelector === delegationSelector) {
- return event
- }
- }
-
- return null
+function findHandler(events, callable, delegationSelector = null) {
+ return Object.values(events)
+ .find(event => event.callable === callable && event.delegationSelector === delegationSelector)
}
-function normalizeParams(originalTypeEvent, handler, delegationFn) {
- const delegation = typeof handler === 'string'
- const originalHandler = delegation ? delegationFn : handler
+function normalizeParameters(originalTypeEvent, handler, delegationFunction) {
+ const isDelegated = typeof handler === 'string'
+ // TODO: tooltip passes `false` instead of selector, so we need to check
+ const callable = isDelegated ? delegationFunction : (handler || delegationFunction)
let typeEvent = getTypeEvent(originalTypeEvent)
- const isNative = nativeEvents.has(typeEvent)
- if (!isNative) {
+ if (!nativeEvents.has(typeEvent)) {
typeEvent = originalTypeEvent
}
- return [delegation, originalHandler, typeEvent]
+ return [isDelegated, callable, typeEvent]
}
-function addHandler(element, originalTypeEvent, handler, delegationFn, oneOff) {
+function addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) {
if (typeof originalTypeEvent !== 'string' || !element) {
return
}
- if (!handler) {
- handler = delegationFn
- delegationFn = null
- }
+ let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction)
// in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position
// this prevents the handler from being dispatched the same way as mouseover or mouseout does
- if (customEventsRegex.test(originalTypeEvent)) {
- const wrapFn = fn => {
+ if (originalTypeEvent in customEvents) {
+ const wrapFunction = fn => {
return function (event) {
if (!event.relatedTarget || (event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget))) {
return fn.call(this, event)
@@ -170,36 +157,31 @@ function addHandler(element, originalTypeEvent, handler, delegationFn, oneOff) {
}
}
- if (delegationFn) {
- delegationFn = wrapFn(delegationFn)
- } else {
- handler = wrapFn(handler)
- }
+ callable = wrapFunction(callable)
}
- const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn)
- const events = getEvent(element)
+ const events = getElementEvents(element)
const handlers = events[typeEvent] || (events[typeEvent] = {})
- const previousFn = findHandler(handlers, originalHandler, delegation ? handler : null)
+ const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null)
- if (previousFn) {
- previousFn.oneOff = previousFn.oneOff && oneOff
+ if (previousFunction) {
+ previousFunction.oneOff = previousFunction.oneOff && oneOff
return
}
- const uid = getUidEvent(originalHandler, originalTypeEvent.replace(namespaceRegex, ''))
- const fn = delegation ?
- bootstrapDelegationHandler(element, handler, delegationFn) :
- bootstrapHandler(element, handler)
+ const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, ''))
+ const fn = isDelegated ?
+ bootstrapDelegationHandler(element, handler, callable) :
+ bootstrapHandler(element, callable)
- fn.delegationSelector = delegation ? handler : null
- fn.originalHandler = originalHandler
+ fn.delegationSelector = isDelegated ? handler : null
+ fn.callable = callable
fn.oneOff = oneOff
fn.uidEvent = uid
handlers[uid] = fn
- element.addEventListener(typeEvent, fn, delegation)
+ element.addEventListener(typeEvent, fn, isDelegated)
}
function removeHandler(element, events, typeEvent, handler, delegationSelector) {
@@ -216,10 +198,9 @@ function removeHandler(element, events, typeEvent, handler, delegationSelector)
function removeNamespacedHandlers(element, events, typeEvent, namespace) {
const storeElementEvent = events[typeEvent] || {}
- for (const handlerKey of Object.keys(storeElementEvent)) {
+ for (const [handlerKey, event] of Object.entries(storeElementEvent)) {
if (handlerKey.includes(namespace)) {
- const event = storeElementEvent[handlerKey]
- removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector)
+ removeHandler(element, events, typeEvent, event.callable, event.delegationSelector)
}
}
}
@@ -231,31 +212,32 @@ function getTypeEvent(event) {
}
const EventHandler = {
- on(element, event, handler, delegationFn) {
- addHandler(element, event, handler, delegationFn, false)
+ on(element, event, handler, delegationFunction) {
+ addHandler(element, event, handler, delegationFunction, false)
},
- one(element, event, handler, delegationFn) {
- addHandler(element, event, handler, delegationFn, true)
+ one(element, event, handler, delegationFunction) {
+ addHandler(element, event, handler, delegationFunction, true)
},
- off(element, originalTypeEvent, handler, delegationFn) {
+ off(element, originalTypeEvent, handler, delegationFunction) {
if (typeof originalTypeEvent !== 'string' || !element) {
return
}
- const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn)
+ const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction)
const inNamespace = typeEvent !== originalTypeEvent
- const events = getEvent(element)
+ const events = getElementEvents(element)
+ const storeElementEvent = events[typeEvent] || {}
const isNamespace = originalTypeEvent.startsWith('.')
- if (typeof originalHandler !== 'undefined') {
+ if (typeof callable !== 'undefined') {
// Simplest case: handler is passed, remove that listener ONLY.
- if (!events || !events[typeEvent]) {
+ if (!Object.keys(storeElementEvent).length) {
return
}
- removeHandler(element, events, typeEvent, originalHandler, delegation ? handler : null)
+ removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null)
return
}
@@ -265,13 +247,11 @@ const EventHandler = {
}
}
- const storeElementEvent = events[typeEvent] || {}
- for (const keyHandlers of Object.keys(storeElementEvent)) {
+ for (const [keyHandlers, event] of Object.entries(storeElementEvent)) {
const handlerKey = keyHandlers.replace(stripUidRegex, '')
if (!inNamespace || originalTypeEvent.includes(handlerKey)) {
- const event = storeElementEvent[keyHandlers]
- removeHandler(element, events, typeEvent, event.originalHandler, event.delegationSelector)
+ removeHandler(element, events, typeEvent, event.callable, event.delegationSelector)
}
}
},
@@ -284,13 +264,11 @@ const EventHandler = {
const $ = getjQuery()
const typeEvent = getTypeEvent(event)
const inNamespace = event !== typeEvent
- const isNative = nativeEvents.has(typeEvent)
- let jQueryEvent
+ let jQueryEvent = null
let bubbles = true
let nativeDispatch = true
let defaultPrevented = false
- let evt = null
if (inNamespace && $) {
jQueryEvent = $.Event(event, args)
@@ -301,23 +279,7 @@ const EventHandler = {
defaultPrevented = jQueryEvent.isDefaultPrevented()
}
- if (isNative) {
- evt = document.createEvent('HTMLEvents')
- evt.initEvent(typeEvent, bubbles, true)
- } else {
- evt = new CustomEvent(event, { bubbles, cancelable: true })
- }
-
- // merge custom information in our event
- if (typeof args !== 'undefined') {
- for (const key of Object.keys(args)) {
- Object.defineProperty(evt, key, {
- get() {
- return args[key]
- }
- })
- }
- }
+ const evt = hydrateObj(new Event(event, { bubbles, cancelable: true }), args)
if (defaultPrevented) {
evt.preventDefault()
@@ -327,7 +289,7 @@ const EventHandler = {
element.dispatchEvent(evt)
}
- if (evt.defaultPrevented && typeof jQueryEvent !== 'undefined') {
+ if (evt.defaultPrevented && jQueryEvent) {
jQueryEvent.preventDefault()
}
@@ -335,4 +297,21 @@ const EventHandler = {
}
}
+function hydrateObj(obj, meta = {}) {
+ for (const [key, value] of Object.entries(meta)) {
+ try {
+ obj[key] = value
+ } catch {
+ Object.defineProperty(obj, key, {
+ configurable: true,
+ get() {
+ return value
+ }
+ })
+ }
+ }
+
+ return obj
+}
+
export default EventHandler
diff --git a/js/src/dom/manipulator.js b/js/src/dom/manipulator.js
index a3e9e192a..a7edc9cb8 100644
--- a/js/src/dom/manipulator.js
+++ b/js/src/dom/manipulator.js
@@ -1,28 +1,36 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): dom/manipulator.js
+ * Bootstrap dom/manipulator.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-function normalizeData(val) {
- if (val === 'true') {
+function normalizeData(value) {
+ if (value === 'true') {
return true
}
- if (val === 'false') {
+ if (value === 'false') {
return false
}
- if (val === Number(val).toString()) {
- return Number(val)
+ if (value === Number(value).toString()) {
+ return Number(value)
}
- if (val === '' || val === 'null') {
+ if (value === '' || value === 'null') {
return null
}
- return val
+ if (typeof value !== 'string') {
+ return value
+ }
+
+ try {
+ return JSON.parse(decodeURIComponent(value))
+ } catch {
+ return value
+ }
}
function normalizeDataKey(key) {
@@ -44,11 +52,11 @@ const Manipulator = {
}
const attributes = {}
- const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs'))
+ const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig'))
for (const key of bsKeys) {
let pureKey = key.replace(/^bs/, '')
- pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length)
+ pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1)
attributes[pureKey] = normalizeData(element.dataset[key])
}
@@ -57,22 +65,6 @@ const Manipulator = {
getDataAttribute(element, key) {
return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`))
- },
-
- offset(element) {
- const rect = element.getBoundingClientRect()
-
- return {
- top: rect.top + window.pageYOffset,
- left: rect.left + window.pageXOffset
- }
- },
-
- position(element) {
- return {
- top: element.offsetTop,
- left: element.offsetLeft
- }
}
}
diff --git a/js/src/dom/selector-engine.js b/js/src/dom/selector-engine.js
index af27dc379..a4d81f3b9 100644
--- a/js/src/dom/selector-engine.js
+++ b/js/src/dom/selector-engine.js
@@ -1,17 +1,36 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): dom/selector-engine.js
+ * Bootstrap dom/selector-engine.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import { isDisabled, isVisible } from '../util/index'
+import { isDisabled, isVisible, parseSelector } from '../util/index.js'
-/**
- * Constants
- */
+const getSelector = element => {
+ let selector = element.getAttribute('data-bs-target')
+
+ if (!selector || selector === '#') {
+ let hrefAttribute = element.getAttribute('href')
+
+ // The only valid content that could double as a selector are IDs or classes,
+ // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
+ // `document.querySelector` will rightfully complain it is invalid.
+ // See https://github.com/twbs/bootstrap/issues/32273
+ if (!hrefAttribute || (!hrefAttribute.includes('#') && !hrefAttribute.startsWith('.'))) {
+ return null
+ }
+
+ // Just in case some CMS puts out a full URL with the anchor appended
+ if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) {
+ hrefAttribute = `#${hrefAttribute.split('#')[1]}`
+ }
+
+ selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null
+ }
-const NODE_TEXT = 3
+ return selector ? selector.split(',').map(sel => parseSelector(sel)).join(',') : null
+}
const SelectorEngine = {
find(selector, element = document.documentElement) {
@@ -28,14 +47,11 @@ const SelectorEngine = {
parents(element, selector) {
const parents = []
- let ancestor = element.parentNode
-
- while (ancestor && ancestor.nodeType === Node.ELEMENT_NODE && ancestor.nodeType !== NODE_TEXT) {
- if (ancestor.matches(selector)) {
- parents.push(ancestor)
- }
+ let ancestor = element.parentNode.closest(selector)
- ancestor = ancestor.parentNode
+ while (ancestor) {
+ parents.push(ancestor)
+ ancestor = ancestor.parentNode.closest(selector)
}
return parents
@@ -54,7 +70,7 @@ const SelectorEngine = {
return []
},
-
+ // TODO: this is now unused; remove later along with prev()
next(element, selector) {
let next = element.nextElementSibling
@@ -79,9 +95,31 @@ const SelectorEngine = {
'details',
'[tabindex]',
'[contenteditable="true"]'
- ].map(selector => `${selector}:not([tabindex^="-"])`).join(', ')
+ ].map(selector => `${selector}:not([tabindex^="-"])`).join(',')
return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el))
+ },
+
+ getSelectorFromElement(element) {
+ const selector = getSelector(element)
+
+ if (selector) {
+ return SelectorEngine.findOne(selector) ? selector : null
+ }
+
+ return null
+ },
+
+ getElementFromSelector(element) {
+ const selector = getSelector(element)
+
+ return selector ? SelectorEngine.findOne(selector) : null
+ },
+
+ getMultipleElementsFromSelector(element) {
+ const selector = getSelector(element)
+
+ return selector ? SelectorEngine.find(selector) : []
}
}
diff --git a/js/src/dropdown.js b/js/src/dropdown.js
index 6129707e2..96094a3e6 100644
--- a/js/src/dropdown.js
+++ b/js/src/dropdown.js
@@ -1,27 +1,26 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): dropdown.js
+ * Bootstrap dropdown.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import * as Popper from '@popperjs/core'
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import Manipulator from './dom/manipulator.js'
+import SelectorEngine from './dom/selector-engine.js'
import {
defineJQueryPlugin,
+ execute,
getElement,
- getElementFromSelector,
getNextActiveElement,
isDisabled,
isElement,
isRTL,
isVisible,
- noop,
- typeCheckConfig
-} from './util/index'
-import EventHandler from './dom/event-handler'
-import Manipulator from './dom/manipulator'
-import SelectorEngine from './dom/selector-engine'
-import BaseComponent from './base-component'
+ noop
+} from './util/index.js'
/**
* Constants
@@ -33,14 +32,11 @@ const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const ESCAPE_KEY = 'Escape'
-const SPACE_KEY = 'Space'
const TAB_KEY = 'Tab'
const ARROW_UP_KEY = 'ArrowUp'
const ARROW_DOWN_KEY = 'ArrowDown'
const RIGHT_MOUSE_BUTTON = 2 // MouseEvent.button value for the secondary button, usually the right button
-const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEY}|${ARROW_DOWN_KEY}|${ESCAPE_KEY}`)
-
const EVENT_HIDE = `hide${EVENT_KEY}`
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
const EVENT_SHOW = `show${EVENT_KEY}`
@@ -53,10 +49,13 @@ const CLASS_NAME_SHOW = 'show'
const CLASS_NAME_DROPUP = 'dropup'
const CLASS_NAME_DROPEND = 'dropend'
const CLASS_NAME_DROPSTART = 'dropstart'
-const CLASS_NAME_NAVBAR = 'navbar'
+const CLASS_NAME_DROPUP_CENTER = 'dropup-center'
+const CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center'
-const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="dropdown"]'
+const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)'
+const SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE}.${CLASS_NAME_SHOW}`
const SELECTOR_MENU = '.dropdown-menu'
+const SELECTOR_NAVBAR = '.navbar'
const SELECTOR_NAVBAR_NAV = '.navbar-nav'
const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'
@@ -66,23 +65,25 @@ const PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start'
const PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end'
const PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start'
const PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start'
+const PLACEMENT_TOPCENTER = 'top'
+const PLACEMENT_BOTTOMCENTER = 'bottom'
const Default = {
- offset: [0, 2],
+ autoClose: true,
boundary: 'clippingParents',
- reference: 'toggle',
display: 'dynamic',
+ offset: [0, 2],
popperConfig: null,
- autoClose: true
+ reference: 'toggle'
}
const DefaultType = {
- offset: '(array|string|function)',
+ autoClose: '(boolean|string)',
boundary: '(string|element)',
- reference: '(string|element|object)',
display: 'string',
+ offset: '(array|string|function)',
popperConfig: '(null|object|function)',
- autoClose: '(boolean|string)'
+ reference: '(string|element|object)'
}
/**
@@ -91,11 +92,14 @@ const DefaultType = {
class Dropdown extends BaseComponent {
constructor(element, config) {
- super(element)
+ super(element, config)
this._popper = null
- this._config = this._getConfig(config)
- this._menu = this._getMenuElement()
+ this._parent = this._element.parentNode // dropdown wrapper
+ // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/
+ this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] ||
+ SelectorEngine.prev(this._element, SELECTOR_MENU)[0] ||
+ SelectorEngine.findOne(SELECTOR_MENU, this._parent)
this._inNavbar = this._detectNavbar()
}
@@ -118,7 +122,7 @@ class Dropdown extends BaseComponent {
}
show() {
- if (isDisabled(this._element) || this._isShown(this._menu)) {
+ if (isDisabled(this._element) || this._isShown()) {
return
}
@@ -132,21 +136,15 @@ class Dropdown extends BaseComponent {
return
}
- const parent = Dropdown.getParentFromElement(this._element)
- // Totally disable Popper for Dropdowns in Navbar
- if (this._inNavbar) {
- Manipulator.setDataAttribute(this._menu, 'popper', 'none')
- } else {
- this._createPopper(parent)
- }
+ this._createPopper()
// If this is a touch-enabled device we add extra
// empty mouseover listeners to the body's immediate children;
// only needed because of broken event delegation on iOS
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
- if ('ontouchstart' in document.documentElement && !parent.closest(SELECTOR_NAVBAR_NAV)) {
- for (const elem of [].concat(...document.body.children)) {
- EventHandler.on(elem, 'mouseover', noop)
+ if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) {
+ for (const element of [].concat(...document.body.children)) {
+ EventHandler.on(element, 'mouseover', noop)
}
}
@@ -159,7 +157,7 @@ class Dropdown extends BaseComponent {
}
hide() {
- if (isDisabled(this._element) || !this._isShown(this._menu)) {
+ if (isDisabled(this._element) || !this._isShown()) {
return
}
@@ -195,8 +193,8 @@ class Dropdown extends BaseComponent {
// If this is a touch-enabled device we remove the extra
// empty mouseover listeners we added for iOS support
if ('ontouchstart' in document.documentElement) {
- for (const elem of [].concat(...document.body.children)) {
- EventHandler.off(elem, 'mouseover', noop)
+ for (const element of [].concat(...document.body.children)) {
+ EventHandler.off(element, 'mouseover', noop)
}
}
@@ -212,13 +210,7 @@ class Dropdown extends BaseComponent {
}
_getConfig(config) {
- config = {
- ...this.constructor.Default,
- ...Manipulator.getDataAttributes(this._element),
- ...config
- }
-
- typeCheckConfig(NAME, config, this.constructor.DefaultType)
+ config = super._getConfig(config)
if (typeof config.reference === 'object' && !isElement(config.reference) &&
typeof config.reference.getBoundingClientRect !== 'function'
@@ -230,15 +222,15 @@ class Dropdown extends BaseComponent {
return config
}
- _createPopper(parent) {
+ _createPopper() {
if (typeof Popper === 'undefined') {
- throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org)')
+ throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org/docs/v2/)')
}
let referenceElement = this._element
if (this._config.reference === 'parent') {
- referenceElement = parent
+ referenceElement = this._parent
} else if (isElement(this._config.reference)) {
referenceElement = getElement(this._config.reference)
} else if (typeof this._config.reference === 'object') {
@@ -246,25 +238,15 @@ class Dropdown extends BaseComponent {
}
const popperConfig = this._getPopperConfig()
- const isDisplayStatic = popperConfig.modifiers.find(modifier => modifier.name === 'applyStyles' && modifier.enabled === false)
-
this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig)
-
- if (isDisplayStatic) {
- Manipulator.setDataAttribute(this._menu, 'popper', 'static')
- }
}
- _isShown(element = this._element) {
- return element.classList.contains(CLASS_NAME_SHOW)
- }
-
- _getMenuElement() {
- return SelectorEngine.next(this._element, SELECTOR_MENU)[0]
+ _isShown() {
+ return this._menu.classList.contains(CLASS_NAME_SHOW)
}
_getPlacement() {
- const parentDropdown = this._element.parentNode
+ const parentDropdown = this._parent
if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) {
return PLACEMENT_RIGHT
@@ -274,6 +256,14 @@ class Dropdown extends BaseComponent {
return PLACEMENT_LEFT
}
+ if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) {
+ return PLACEMENT_TOPCENTER
+ }
+
+ if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) {
+ return PLACEMENT_BOTTOMCENTER
+ }
+
// We need to trim the value because custom properties can also include spaces
const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end'
@@ -285,14 +275,14 @@ class Dropdown extends BaseComponent {
}
_detectNavbar() {
- return this._element.closest(`.${CLASS_NAME_NAVBAR}`) !== null
+ return this._element.closest(SELECTOR_NAVBAR) !== null
}
_getOffset() {
const { offset } = this._config
if (typeof offset === 'string') {
- return offset.split(',').map(val => Number.parseInt(val, 10))
+ return offset.split(',').map(value => Number.parseInt(value, 10))
}
if (typeof offset === 'function') {
@@ -319,8 +309,9 @@ class Dropdown extends BaseComponent {
}]
}
- // Disable Popper if we have a static display
- if (this._config.display === 'static') {
+ // Disable Popper if we have a static display or Dropdown is in Navbar
+ if (this._inNavbar || this._config.display === 'static') {
+ Manipulator.setDataAttribute(this._menu, 'popper', 'static') // TODO: v6 remove
defaultBsPopperConfig.modifiers = [{
name: 'applyStyles',
enabled: false
@@ -329,12 +320,12 @@ class Dropdown extends BaseComponent {
return {
...defaultBsPopperConfig,
- ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)
+ ...execute(this._config.popperConfig, [undefined, defaultBsPopperConfig])
}
}
_selectMenuItem({ key, target }) {
- const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(el => isVisible(el))
+ const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element))
if (!items.length) {
return
@@ -363,103 +354,81 @@ class Dropdown extends BaseComponent {
}
static clearMenus(event) {
- if (event && (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY))) {
+ if (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY)) {
return
}
- const toggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE)
+ const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN)
- for (const toggle of toggles) {
+ for (const toggle of openToggles) {
const context = Dropdown.getInstance(toggle)
if (!context || context._config.autoClose === false) {
continue
}
- if (!context._isShown()) {
+ const composedPath = event.composedPath()
+ const isMenuTarget = composedPath.includes(context._menu)
+ if (
+ composedPath.includes(context._element) ||
+ (context._config.autoClose === 'inside' && !isMenuTarget) ||
+ (context._config.autoClose === 'outside' && isMenuTarget)
+ ) {
continue
}
- const relatedTarget = {
- relatedTarget: context._element
+ // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu
+ if (context._menu.contains(event.target) && ((event.type === 'keyup' && event.key === TAB_KEY) || /input|select|option|textarea|form/i.test(event.target.tagName))) {
+ continue
}
- if (event) {
- const composedPath = event.composedPath()
- const isMenuTarget = composedPath.includes(context._menu)
- if (
- composedPath.includes(context._element) ||
- (context._config.autoClose === 'inside' && !isMenuTarget) ||
- (context._config.autoClose === 'outside' && isMenuTarget)
- ) {
- continue
- }
-
- // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu
- if (context._menu.contains(event.target) && ((event.type === 'keyup' && event.key === TAB_KEY) || /input|select|option|textarea|form/i.test(event.target.tagName))) {
- continue
- }
+ const relatedTarget = { relatedTarget: context._element }
- if (event.type === 'click') {
- relatedTarget.clickEvent = event
- }
+ if (event.type === 'click') {
+ relatedTarget.clickEvent = event
}
context._completeHide(relatedTarget)
}
}
- static getParentFromElement(element) {
- return getElementFromSelector(element) || element.parentNode
- }
-
static dataApiKeydownHandler(event) {
- // If not input/textarea:
- // - And not a key in REGEXP_KEYDOWN => not a dropdown command
- // If input/textarea:
- // - If space key => not a dropdown command
- // - If key is other than escape
- // - If key is not up or down => not a dropdown command
- // - If trigger inside the menu => not a dropdown command
- if (/input|textarea/i.test(event.target.tagName) ?
- event.key === SPACE_KEY || (event.key !== ESCAPE_KEY &&
- ((event.key !== ARROW_DOWN_KEY && event.key !== ARROW_UP_KEY) ||
- event.target.closest(SELECTOR_MENU))) :
- !REGEXP_KEYDOWN.test(event.key)) {
- return
- }
+ // If not an UP | DOWN | ESCAPE key => not a dropdown command
+ // If input/textarea && if key is other than ESCAPE => not a dropdown command
- const isActive = this.classList.contains(CLASS_NAME_SHOW)
+ const isInput = /input|textarea/i.test(event.target.tagName)
+ const isEscapeEvent = event.key === ESCAPE_KEY
+ const isUpOrDownEvent = [ARROW_UP_KEY, ARROW_DOWN_KEY].includes(event.key)
- if (!isActive && event.key === ESCAPE_KEY) {
+ if (!isUpOrDownEvent && !isEscapeEvent) {
return
}
- event.preventDefault()
- event.stopPropagation()
-
- if (isDisabled(this)) {
+ if (isInput && !isEscapeEvent) {
return
}
- const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0]
- const instance = Dropdown.getOrCreateInstance(getToggleButton)
+ event.preventDefault()
- if (event.key === ESCAPE_KEY) {
- instance.hide()
- return
- }
+ // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/
+ const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE) ?
+ this :
+ (SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0] ||
+ SelectorEngine.next(this, SELECTOR_DATA_TOGGLE)[0] ||
+ SelectorEngine.findOne(SELECTOR_DATA_TOGGLE, event.delegateTarget.parentNode))
- if (event.key === ARROW_UP_KEY || event.key === ARROW_DOWN_KEY) {
- if (!isActive) {
- instance.show()
- }
+ const instance = Dropdown.getOrCreateInstance(getToggleButton)
+ if (isUpOrDownEvent) {
+ event.stopPropagation()
+ instance.show()
instance._selectMenuItem(event)
return
}
- if (!isActive || event.key === SPACE_KEY) {
- Dropdown.clearMenus()
+ if (instance._isShown()) { // else is escape and we check if it is shown
+ event.stopPropagation()
+ instance.hide()
+ getToggleButton.focus()
}
}
}
diff --git a/js/src/modal.js b/js/src/modal.js
index b8b144774..dd61649ec 100644
--- a/js/src/modal.js
+++ b/js/src/modal.js
@@ -1,26 +1,20 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): modal.js
+ * Bootstrap modal.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import SelectorEngine from './dom/selector-engine.js'
+import Backdrop from './util/backdrop.js'
+import { enableDismissTrigger } from './util/component-functions.js'
+import FocusTrap from './util/focustrap.js'
import {
- defineJQueryPlugin,
- getElementFromSelector,
- isRTL,
- isVisible,
- reflow,
- typeCheckConfig
-} from './util/index'
-import EventHandler from './dom/event-handler'
-import Manipulator from './dom/manipulator'
-import SelectorEngine from './dom/selector-engine'
-import ScrollBarHelper from './util/scrollbar'
-import BaseComponent from './base-component'
-import Backdrop from './util/backdrop'
-import FocusTrap from './util/focustrap'
-import { enableDismissTrigger } from './util/component-functions'
+ defineJQueryPlugin, isRTL, isVisible, reflow
+} from './util/index.js'
+import ScrollBarHelper from './util/scrollbar.js'
/**
* Constants
@@ -39,6 +33,7 @@ const EVENT_SHOW = `show${EVENT_KEY}`
const EVENT_SHOWN = `shown${EVENT_KEY}`
const EVENT_RESIZE = `resize${EVENT_KEY}`
const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`
+const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`
const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
@@ -54,14 +49,14 @@ const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="modal"]'
const Default = {
backdrop: true,
- keyboard: true,
- focus: true
+ focus: true,
+ keyboard: true
}
const DefaultType = {
backdrop: '(boolean|string)',
- keyboard: 'boolean',
- focus: 'boolean'
+ focus: 'boolean',
+ keyboard: 'boolean'
}
/**
@@ -70,15 +65,16 @@ const DefaultType = {
class Modal extends BaseComponent {
constructor(element, config) {
- super(element)
+ super(element, config)
- this._config = this._getConfig(config)
this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element)
this._backdrop = this._initializeBackDrop()
this._focustrap = this._initializeFocusTrap()
this._isShown = false
this._isTransitioning = false
this._scrollBar = new ScrollBarHelper()
+
+ this._addEventListeners()
}
// Getters
@@ -86,6 +82,10 @@ class Modal extends BaseComponent {
return Default
}
+ static get DefaultType() {
+ return DefaultType
+ }
+
static get NAME() {
return NAME
}
@@ -117,10 +117,7 @@ class Modal extends BaseComponent {
this._adjustDialog()
- this._toggleEscapeEventListener(true)
- this._toggleResizeEventListener(true)
-
- this._showBackdrop(() => this._showElement(relatedTarget))
+ this._backdrop.show(() => this._showElement(relatedTarget))
}
hide() {
@@ -136,10 +133,6 @@ class Modal extends BaseComponent {
this._isShown = false
this._isTransitioning = true
-
- this._toggleEscapeEventListener(false)
- this._toggleResizeEventListener(false)
-
this._focustrap.deactivate()
this._element.classList.remove(CLASS_NAME_SHOW)
@@ -148,12 +141,12 @@ class Modal extends BaseComponent {
}
dispose() {
- for (const htmlElement of [window, this._dialog]) {
- EventHandler.off(htmlElement, EVENT_KEY)
- }
+ EventHandler.off(window, EVENT_KEY)
+ EventHandler.off(this._dialog, EVENT_KEY)
this._backdrop.dispose()
this._focustrap.deactivate()
+
super.dispose()
}
@@ -164,7 +157,7 @@ class Modal extends BaseComponent {
// Private
_initializeBackDrop() {
return new Backdrop({
- isVisible: Boolean(this._config.backdrop), // 'static' option will be translated to true, and booleans will keep their value
+ isVisible: Boolean(this._config.backdrop), // 'static' option will be translated to true, and booleans will keep their value,
isAnimated: this._isAnimated()
})
}
@@ -175,16 +168,6 @@ class Modal extends BaseComponent {
})
}
- _getConfig(config) {
- config = {
- ...Default,
- ...Manipulator.getDataAttributes(this._element),
- ...(typeof config === 'object' ? config : {})
- }
- typeCheckConfig(NAME, config, DefaultType)
- return config
- }
-
_showElement(relatedTarget) {
// try to append dynamic modal
if (!document.body.contains(this._element)) {
@@ -220,34 +203,43 @@ class Modal extends BaseComponent {
this._queueCallback(transitionComplete, this._dialog, this._isAnimated())
}
- _toggleEscapeEventListener(enable) {
- if (!enable) {
- EventHandler.off(this._element, EVENT_KEYDOWN_DISMISS)
- return
- }
-
+ _addEventListeners() {
EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {
if (event.key !== ESCAPE_KEY) {
return
}
if (this._config.keyboard) {
- event.preventDefault()
this.hide()
return
}
this._triggerBackdropTransition()
})
- }
- _toggleResizeEventListener(enable) {
- if (enable) {
- EventHandler.on(window, EVENT_RESIZE, () => this._adjustDialog())
- return
- }
+ EventHandler.on(window, EVENT_RESIZE, () => {
+ if (this._isShown && !this._isTransitioning) {
+ this._adjustDialog()
+ }
+ })
- EventHandler.off(window, EVENT_RESIZE)
+ EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => {
+ // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks
+ EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => {
+ if (this._element !== event.target || this._element !== event2.target) {
+ return
+ }
+
+ if (this._config.backdrop === 'static') {
+ this._triggerBackdropTransition()
+ return
+ }
+
+ if (this._config.backdrop) {
+ this.hide()
+ }
+ })
+ })
}
_hideModal() {
@@ -265,25 +257,6 @@ class Modal extends BaseComponent {
})
}
- _showBackdrop(callback) {
- EventHandler.on(this._element, EVENT_CLICK_DISMISS, event => {
- if (event.target !== event.currentTarget) {
- return
- }
-
- if (this._config.backdrop === true) {
- this.hide()
- return
- }
-
- if (this._config.backdrop === 'static') {
- this._triggerBackdropTransition()
- }
- })
-
- this._backdrop.show(callback)
- }
-
_isAnimated() {
return this._element.classList.contains(CLASS_NAME_FADE)
}
@@ -294,23 +267,22 @@ class Modal extends BaseComponent {
return
}
- const { classList, scrollHeight, style } = this._element
- const isModalOverflowing = scrollHeight > document.documentElement.clientHeight
- const initialOverflowY = style.overflowY
+ const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight
+ const initialOverflowY = this._element.style.overflowY
// return if the following background transition hasn't yet completed
- if (initialOverflowY === 'hidden' || classList.contains(CLASS_NAME_STATIC)) {
+ if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) {
return
}
if (!isModalOverflowing) {
- style.overflowY = 'hidden'
+ this._element.style.overflowY = 'hidden'
}
- classList.add(CLASS_NAME_STATIC)
+ this._element.classList.add(CLASS_NAME_STATIC)
this._queueCallback(() => {
- classList.remove(CLASS_NAME_STATIC)
+ this._element.classList.remove(CLASS_NAME_STATIC)
this._queueCallback(() => {
- style.overflowY = initialOverflowY
+ this._element.style.overflowY = initialOverflowY
}, this._dialog)
}, this._dialog)
@@ -365,7 +337,7 @@ class Modal extends BaseComponent {
*/
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
- const target = getElementFromSelector(this)
+ const target = SelectorEngine.getElementFromSelector(this)
if (['A', 'AREA'].includes(this.tagName)) {
event.preventDefault()
@@ -384,10 +356,10 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
})
})
- // avoid conflict when clicking moddal toggler while another one is open
- const allReadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)
- if (allReadyOpen) {
- Modal.getInstance(allReadyOpen).hide()
+ // avoid conflict when clicking modal toggler while another one is open
+ const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR)
+ if (alreadyOpen) {
+ Modal.getInstance(alreadyOpen).hide()
}
const data = Modal.getOrCreateInstance(target)
diff --git a/js/src/offcanvas.js b/js/src/offcanvas.js
index 6878b1f62..8d1feb13b 100644
--- a/js/src/offcanvas.js
+++ b/js/src/offcanvas.js
@@ -1,25 +1,22 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): offcanvas.js
+ * Bootstrap offcanvas.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import SelectorEngine from './dom/selector-engine.js'
+import Backdrop from './util/backdrop.js'
+import { enableDismissTrigger } from './util/component-functions.js'
+import FocusTrap from './util/focustrap.js'
import {
defineJQueryPlugin,
- getElementFromSelector,
isDisabled,
- isVisible,
- typeCheckConfig
-} from './util/index'
-import ScrollBarHelper from './util/scrollbar'
-import EventHandler from './dom/event-handler'
-import BaseComponent from './base-component'
-import SelectorEngine from './dom/selector-engine'
-import Manipulator from './dom/manipulator'
-import Backdrop from './util/backdrop'
-import FocusTrap from './util/focustrap'
-import { enableDismissTrigger } from './util/component-functions'
+ isVisible
+} from './util/index.js'
+import ScrollBarHelper from './util/scrollbar.js'
/**
* Constants
@@ -33,13 +30,17 @@ const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`
const ESCAPE_KEY = 'Escape'
const CLASS_NAME_SHOW = 'show'
+const CLASS_NAME_SHOWING = 'showing'
+const CLASS_NAME_HIDING = 'hiding'
const CLASS_NAME_BACKDROP = 'offcanvas-backdrop'
const OPEN_SELECTOR = '.offcanvas.show'
const EVENT_SHOW = `show${EVENT_KEY}`
const EVENT_SHOWN = `shown${EVENT_KEY}`
const EVENT_HIDE = `hide${EVENT_KEY}`
+const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
+const EVENT_RESIZE = `resize${EVENT_KEY}`
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`
@@ -52,7 +53,7 @@ const Default = {
}
const DefaultType = {
- backdrop: 'boolean',
+ backdrop: '(boolean|string)',
keyboard: 'boolean',
scroll: 'boolean'
}
@@ -63,9 +64,8 @@ const DefaultType = {
class Offcanvas extends BaseComponent {
constructor(element, config) {
- super(element)
+ super(element, config)
- this._config = this._getConfig(config)
this._isShown = false
this._backdrop = this._initializeBackDrop()
this._focustrap = this._initializeFocusTrap()
@@ -73,14 +73,18 @@ class Offcanvas extends BaseComponent {
}
// Getters
- static get NAME() {
- return NAME
- }
-
static get Default() {
return Default
}
+ static get DefaultType() {
+ return DefaultType
+ }
+
+ static get NAME() {
+ return NAME
+ }
+
// Public
toggle(relatedTarget) {
return this._isShown ? this.hide() : this.show(relatedTarget)
@@ -98,24 +102,23 @@ class Offcanvas extends BaseComponent {
}
this._isShown = true
- this._element.style.visibility = 'visible'
-
this._backdrop.show()
if (!this._config.scroll) {
new ScrollBarHelper().hide()
}
- this._element.removeAttribute('aria-hidden')
this._element.setAttribute('aria-modal', true)
this._element.setAttribute('role', 'dialog')
- this._element.classList.add(CLASS_NAME_SHOW)
+ this._element.classList.add(CLASS_NAME_SHOWING)
const completeCallBack = () => {
- if (!this._config.scroll) {
+ if (!this._config.scroll || this._config.backdrop) {
this._focustrap.activate()
}
+ this._element.classList.add(CLASS_NAME_SHOW)
+ this._element.classList.remove(CLASS_NAME_SHOWING)
EventHandler.trigger(this._element, EVENT_SHOWN, { relatedTarget })
}
@@ -136,14 +139,13 @@ class Offcanvas extends BaseComponent {
this._focustrap.deactivate()
this._element.blur()
this._isShown = false
- this._element.classList.remove(CLASS_NAME_SHOW)
+ this._element.classList.add(CLASS_NAME_HIDING)
this._backdrop.hide()
const completeCallback = () => {
- this._element.setAttribute('aria-hidden', true)
+ this._element.classList.remove(CLASS_NAME_SHOW, CLASS_NAME_HIDING)
this._element.removeAttribute('aria-modal')
this._element.removeAttribute('role')
- this._element.style.visibility = 'hidden'
if (!this._config.scroll) {
new ScrollBarHelper().reset()
@@ -162,23 +164,25 @@ class Offcanvas extends BaseComponent {
}
// Private
- _getConfig(config) {
- config = {
- ...Default,
- ...Manipulator.getDataAttributes(this._element),
- ...(typeof config === 'object' ? config : {})
+ _initializeBackDrop() {
+ const clickCallback = () => {
+ if (this._config.backdrop === 'static') {
+ EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)
+ return
+ }
+
+ this.hide()
}
- typeCheckConfig(NAME, config, DefaultType)
- return config
- }
- _initializeBackDrop() {
+ // 'static' option will be translated to true, and booleans will keep their value
+ const isVisible = Boolean(this._config.backdrop)
+
return new Backdrop({
className: CLASS_NAME_BACKDROP,
- isVisible: this._config.backdrop,
+ isVisible,
isAnimated: true,
rootElement: this._element.parentNode,
- clickCallback: () => this.hide()
+ clickCallback: isVisible ? clickCallback : null
})
}
@@ -190,9 +194,16 @@ class Offcanvas extends BaseComponent {
_addEventListeners() {
EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {
- if (this._config.keyboard && event.key === ESCAPE_KEY) {
+ if (event.key !== ESCAPE_KEY) {
+ return
+ }
+
+ if (this._config.keyboard) {
this.hide()
+ return
}
+
+ EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)
})
}
@@ -219,7 +230,7 @@ class Offcanvas extends BaseComponent {
*/
EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
- const target = getElementFromSelector(this)
+ const target = SelectorEngine.getElementFromSelector(this)
if (['A', 'AREA'].includes(this.tagName)) {
event.preventDefault()
@@ -247,8 +258,16 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
})
EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
- for (const el of SelectorEngine.find(OPEN_SELECTOR)) {
- Offcanvas.getOrCreateInstance(el).show()
+ for (const selector of SelectorEngine.find(OPEN_SELECTOR)) {
+ Offcanvas.getOrCreateInstance(selector).show()
+ }
+})
+
+EventHandler.on(window, EVENT_RESIZE, () => {
+ for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) {
+ if (getComputedStyle(element).position !== 'fixed') {
+ Offcanvas.getOrCreateInstance(element).hide()
+ }
}
})
diff --git a/js/src/popover.js b/js/src/popover.js
index 19c1e42a4..612c5218f 100644
--- a/js/src/popover.js
+++ b/js/src/popover.js
@@ -1,53 +1,38 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): popover.js
+ * Bootstrap popover.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import { defineJQueryPlugin } from './util/index'
-import Tooltip from './tooltip'
+import Tooltip from './tooltip.js'
+import { defineJQueryPlugin } from './util/index.js'
/**
* Constants
*/
const NAME = 'popover'
-const DATA_KEY = 'bs.popover'
-const EVENT_KEY = `.${DATA_KEY}`
const SELECTOR_TITLE = '.popover-header'
const SELECTOR_CONTENT = '.popover-body'
const Default = {
...Tooltip.Default,
- placement: 'right',
- offset: [0, 8],
- trigger: 'click',
content: '',
+ offset: [0, 8],
+ placement: 'right',
template: '<div class="popover" role="tooltip">' +
- '<div class="popover-arrow"></div>' +
- '<h3 class="popover-header"></h3>' +
- '<div class="popover-body"></div>' +
- '</div>'
+ '<div class="popover-arrow"></div>' +
+ '<h3 class="popover-header"></h3>' +
+ '<div class="popover-body"></div>' +
+ '</div>',
+ trigger: 'click'
}
const DefaultType = {
...Tooltip.DefaultType,
- content: '(string|element|function)'
-}
-
-const Event = {
- HIDE: `hide${EVENT_KEY}`,
- HIDDEN: `hidden${EVENT_KEY}`,
- SHOW: `show${EVENT_KEY}`,
- SHOWN: `shown${EVENT_KEY}`,
- INSERTED: `inserted${EVENT_KEY}`,
- CLICK: `click${EVENT_KEY}`,
- FOCUSIN: `focusin${EVENT_KEY}`,
- FOCUSOUT: `focusout${EVENT_KEY}`,
- MOUSEENTER: `mouseenter${EVENT_KEY}`,
- MOUSELEAVE: `mouseleave${EVENT_KEY}`
+ content: '(null|string|element|function)'
}
/**
@@ -60,27 +45,23 @@ class Popover extends Tooltip {
return Default
}
- static get NAME() {
- return NAME
- }
-
- static get Event() {
- return Event
- }
-
static get DefaultType() {
return DefaultType
}
+ static get NAME() {
+ return NAME
+ }
+
// Overrides
- isWithContent() {
- return this.getTitle() || this._getContent()
+ _isWithContent() {
+ return this._getTitle() || this._getContent()
}
// Private
_getContentForTemplate() {
return {
- [SELECTOR_TITLE]: this.getTitle(),
+ [SELECTOR_TITLE]: this._getTitle(),
[SELECTOR_CONTENT]: this._getContent()
}
}
@@ -94,13 +75,15 @@ class Popover extends Tooltip {
return this.each(function () {
const data = Popover.getOrCreateInstance(this, config)
- if (typeof config === 'string') {
- if (typeof data[config] === 'undefined') {
- throw new TypeError(`No method named "${config}"`)
- }
+ if (typeof config !== 'string') {
+ return
+ }
- data[config]()
+ if (typeof data[config] === 'undefined') {
+ throw new TypeError(`No method named "${config}"`)
}
+
+ data[config]()
})
}
}
diff --git a/js/src/scrollspy.js b/js/src/scrollspy.js
index 27bc0cd87..368092de4 100644
--- a/js/src/scrollspy.js
+++ b/js/src/scrollspy.js
@@ -1,20 +1,16 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): scrollspy.js
+ * Bootstrap scrollspy.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import SelectorEngine from './dom/selector-engine.js'
import {
- defineJQueryPlugin,
- getElement,
- getSelectorFromElement,
- typeCheckConfig
-} from './util/index'
-import EventHandler from './dom/event-handler'
-import Manipulator from './dom/manipulator'
-import SelectorEngine from './dom/selector-engine'
-import BaseComponent from './base-component'
+ defineJQueryPlugin, getElement, isDisabled, isVisible
+} from './util/index.js'
/**
* Constants
@@ -26,34 +22,36 @@ const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
const EVENT_ACTIVATE = `activate${EVENT_KEY}`
-const EVENT_SCROLL = `scroll${EVENT_KEY}`
+const EVENT_CLICK = `click${EVENT_KEY}`
const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`
const CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item'
const CLASS_NAME_ACTIVE = 'active'
const SELECTOR_DATA_SPY = '[data-bs-spy="scroll"]'
+const SELECTOR_TARGET_LINKS = '[href]'
const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'
const SELECTOR_NAV_LINKS = '.nav-link'
const SELECTOR_NAV_ITEMS = '.nav-item'
const SELECTOR_LIST_ITEMS = '.list-group-item'
-const SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}, .${CLASS_NAME_DROPDOWN_ITEM}`
+const SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`
const SELECTOR_DROPDOWN = '.dropdown'
const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'
-const METHOD_OFFSET = 'offset'
-const METHOD_POSITION = 'position'
-
const Default = {
- offset: 10,
- method: 'auto',
- target: ''
+ offset: null, // TODO: v6 @deprecated, keep it for backwards compatibility reasons
+ rootMargin: '0px 0px -25%',
+ smoothScroll: false,
+ target: null,
+ threshold: [0.1, 0.5, 1]
}
const DefaultType = {
- offset: 'number',
- method: 'string',
- target: '(string|element)'
+ offset: '(number|null)', // TODO v6 @deprecated, keep it for backwards compatibility reasons
+ rootMargin: 'string',
+ smoothScroll: 'boolean',
+ target: 'element',
+ threshold: 'array'
}
/**
@@ -62,18 +60,19 @@ const DefaultType = {
class ScrollSpy extends BaseComponent {
constructor(element, config) {
- super(element)
- this._scrollElement = this._element.tagName === 'BODY' ? window : this._element
- this._config = this._getConfig(config)
- this._offsets = []
- this._targets = []
- this._activeTarget = null
- this._scrollHeight = 0
-
- EventHandler.on(this._scrollElement, EVENT_SCROLL, () => this._process())
+ super(element, config)
- this.refresh()
- this._process()
+ // this._element is the observablesContainer and config.target the menu links wrapper
+ this._targetLinks = new Map()
+ this._observableSections = new Map()
+ this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element
+ this._activeTarget = null
+ this._observer = null
+ this._previousScrollData = {
+ visibleEntryTop: 0,
+ parentScrollTop: 0
+ }
+ this.refresh() // initialize
}
// Getters
@@ -81,169 +80,180 @@ class ScrollSpy extends BaseComponent {
return Default
}
+ static get DefaultType() {
+ return DefaultType
+ }
+
static get NAME() {
return NAME
}
// Public
refresh() {
- const autoMethod = this._scrollElement === this._scrollElement.window ?
- METHOD_OFFSET :
- METHOD_POSITION
-
- const offsetMethod = this._config.method === 'auto' ?
- autoMethod :
- this._config.method
-
- const offsetBase = offsetMethod === METHOD_POSITION ?
- this._getScrollTop() :
- 0
-
- this._offsets = []
- this._targets = []
- this._scrollHeight = this._getScrollHeight()
-
- const targets = SelectorEngine.find(SELECTOR_LINK_ITEMS, this._config.target)
- .map(element => {
- const targetSelector = getSelectorFromElement(element)
- const target = targetSelector ? SelectorEngine.findOne(targetSelector) : null
-
- if (target) {
- const targetBCR = target.getBoundingClientRect()
- if (targetBCR.width || targetBCR.height) {
- return [
- Manipulator[offsetMethod](target).top + offsetBase,
- targetSelector
- ]
- }
- }
+ this._initializeTargetsAndObservables()
+ this._maybeEnableSmoothScroll()
- return null
- })
- .filter(item => item)
- .sort((a, b) => a[0] - b[0])
+ if (this._observer) {
+ this._observer.disconnect()
+ } else {
+ this._observer = this._getNewObserver()
+ }
- for (const item of targets) {
- this._offsets.push(item[0])
- this._targets.push(item[1])
+ for (const section of this._observableSections.values()) {
+ this._observer.observe(section)
}
}
dispose() {
- EventHandler.off(this._scrollElement, EVENT_KEY)
+ this._observer.disconnect()
super.dispose()
}
// Private
- _getConfig(config) {
- config = {
- ...Default,
- ...Manipulator.getDataAttributes(this._element),
- ...(typeof config === 'object' && config ? config : {})
- }
+ _configAfterMerge(config) {
+ // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case
+ config.target = getElement(config.target) || document.body
- config.target = getElement(config.target) || document.documentElement
+ // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only
+ config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin
- typeCheckConfig(NAME, config, DefaultType)
+ if (typeof config.threshold === 'string') {
+ config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value))
+ }
return config
}
- _getScrollTop() {
- return this._scrollElement === window ?
- this._scrollElement.pageYOffset :
- this._scrollElement.scrollTop
- }
+ _maybeEnableSmoothScroll() {
+ if (!this._config.smoothScroll) {
+ return
+ }
- _getScrollHeight() {
- return this._scrollElement.scrollHeight || Math.max(
- document.body.scrollHeight,
- document.documentElement.scrollHeight
- )
- }
+ // unregister any previous listeners
+ EventHandler.off(this._config.target, EVENT_CLICK)
+
+ EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => {
+ const observableSection = this._observableSections.get(event.target.hash)
+ if (observableSection) {
+ event.preventDefault()
+ const root = this._rootElement || window
+ const height = observableSection.offsetTop - this._element.offsetTop
+ if (root.scrollTo) {
+ root.scrollTo({ top: height, behavior: 'smooth' })
+ return
+ }
- _getOffsetHeight() {
- return this._scrollElement === window ?
- window.innerHeight :
- this._scrollElement.getBoundingClientRect().height
+ // Chrome 60 doesn't support `scrollTo`
+ root.scrollTop = height
+ }
+ })
}
- _process() {
- const scrollTop = this._getScrollTop() + this._config.offset
- const scrollHeight = this._getScrollHeight()
- const maxScroll = this._config.offset + scrollHeight - this._getOffsetHeight()
+ _getNewObserver() {
+ const options = {
+ root: this._rootElement,
+ threshold: this._config.threshold,
+ rootMargin: this._config.rootMargin
+ }
+
+ return new IntersectionObserver(entries => this._observerCallback(entries), options)
+ }
- if (this._scrollHeight !== scrollHeight) {
- this.refresh()
+ // The logic of selection
+ _observerCallback(entries) {
+ const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`)
+ const activate = entry => {
+ this._previousScrollData.visibleEntryTop = entry.target.offsetTop
+ this._process(targetElement(entry))
}
- if (scrollTop >= maxScroll) {
- const target = this._targets[this._targets.length - 1]
+ const parentScrollTop = (this._rootElement || document.documentElement).scrollTop
+ const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop
+ this._previousScrollData.parentScrollTop = parentScrollTop
+
+ for (const entry of entries) {
+ if (!entry.isIntersecting) {
+ this._activeTarget = null
+ this._clearActiveClass(targetElement(entry))
- if (this._activeTarget !== target) {
- this._activate(target)
+ continue
}
- return
- }
+ const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop
+ // if we are scrolling down, pick the bigger offsetTop
+ if (userScrollsDown && entryIsLowerThanPrevious) {
+ activate(entry)
+ // if parent isn't scrolled, let's keep the first visible item, breaking the iteration
+ if (!parentScrollTop) {
+ return
+ }
- if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) {
- this._activeTarget = null
- this._clear()
- return
+ continue
+ }
+
+ // if we are scrolling up, pick the smallest offsetTop
+ if (!userScrollsDown && !entryIsLowerThanPrevious) {
+ activate(entry)
+ }
}
+ }
+
+ _initializeTargetsAndObservables() {
+ this._targetLinks = new Map()
+ this._observableSections = new Map()
+
+ const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target)
- for (let i = this._offsets.length; i--;) {
- const isActiveTarget = this._activeTarget !== this._targets[i] &&
- scrollTop >= this._offsets[i] &&
- (typeof this._offsets[i + 1] === 'undefined' || scrollTop < this._offsets[i + 1])
+ for (const anchor of targetLinks) {
+ // ensure that the anchor has an id and is not disabled
+ if (!anchor.hash || isDisabled(anchor)) {
+ continue
+ }
+
+ const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element)
- if (isActiveTarget) {
- this._activate(this._targets[i])
+ // ensure that the observableSection exists & is visible
+ if (isVisible(observableSection)) {
+ this._targetLinks.set(decodeURI(anchor.hash), anchor)
+ this._observableSections.set(anchor.hash, observableSection)
}
}
}
- _activate(target) {
- this._activeTarget = target
-
- this._clear()
+ _process(target) {
+ if (this._activeTarget === target) {
+ return
+ }
- const queries = SELECTOR_LINK_ITEMS.split(',')
- .map(selector => `${selector}[data-bs-target="${target}"],${selector}[href="${target}"]`)
+ this._clearActiveClass(this._config.target)
+ this._activeTarget = target
+ target.classList.add(CLASS_NAME_ACTIVE)
+ this._activateParents(target)
- const link = SelectorEngine.findOne(queries.join(','), this._config.target)
+ EventHandler.trigger(this._element, EVENT_ACTIVATE, { relatedTarget: target })
+ }
- link.classList.add(CLASS_NAME_ACTIVE)
- if (link.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {
- SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE, link.closest(SELECTOR_DROPDOWN))
+ _activateParents(target) {
+ // Activate dropdown parents
+ if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {
+ SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE, target.closest(SELECTOR_DROPDOWN))
.classList.add(CLASS_NAME_ACTIVE)
- } else {
- for (const listGroup of SelectorEngine.parents(link, SELECTOR_NAV_LIST_GROUP)) {
- // Set triggered links parents as active
- // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor
- for (const item of SelectorEngine.prev(listGroup, `${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`)) {
- item.classList.add(CLASS_NAME_ACTIVE)
- }
+ return
+ }
- // Handle special case when .nav-link is inside .nav-item
- for (const navItem of SelectorEngine.prev(listGroup, SELECTOR_NAV_ITEMS)) {
- for (const item of SelectorEngine.children(navItem, SELECTOR_NAV_LINKS)) {
- item.classList.add(CLASS_NAME_ACTIVE)
- }
- }
+ for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) {
+ // Set triggered links parents as active
+ // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor
+ for (const item of SelectorEngine.prev(listGroup, SELECTOR_LINK_ITEMS)) {
+ item.classList.add(CLASS_NAME_ACTIVE)
}
}
-
- EventHandler.trigger(this._scrollElement, EVENT_ACTIVATE, {
- relatedTarget: target
- })
}
- _clear() {
- const activeNodes = SelectorEngine.find(SELECTOR_LINK_ITEMS, this._config.target)
- .filter(node => node.classList.contains(CLASS_NAME_ACTIVE))
+ _clearActiveClass(parent) {
+ parent.classList.remove(CLASS_NAME_ACTIVE)
+ const activeNodes = SelectorEngine.find(`${SELECTOR_TARGET_LINKS}.${CLASS_NAME_ACTIVE}`, parent)
for (const node of activeNodes) {
node.classList.remove(CLASS_NAME_ACTIVE)
}
@@ -258,7 +268,7 @@ class ScrollSpy extends BaseComponent {
return
}
- if (typeof data[config] === 'undefined') {
+ if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
throw new TypeError(`No method named "${config}"`)
}
@@ -273,7 +283,7 @@ class ScrollSpy extends BaseComponent {
EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
for (const spy of SelectorEngine.find(SELECTOR_DATA_SPY)) {
- new ScrollSpy(spy) // eslint-disable-line no-new
+ ScrollSpy.getOrCreateInstance(spy)
}
})
diff --git a/js/src/tab.js b/js/src/tab.js
index 4a018ca77..dfaef0ffa 100644
--- a/js/src/tab.js
+++ b/js/src/tab.js
@@ -1,19 +1,14 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): tab.js
+ * Bootstrap tab.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import {
- defineJQueryPlugin,
- getElementFromSelector,
- isDisabled,
- reflow
-} from './util/index'
-import EventHandler from './dom/event-handler'
-import SelectorEngine from './dom/selector-engine'
-import BaseComponent from './base-component'
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import SelectorEngine from './dom/selector-engine.js'
+import { defineJQueryPlugin, getNextActiveElement, isDisabled } from './util/index.js'
/**
* Constants
@@ -22,160 +17,267 @@ import BaseComponent from './base-component'
const NAME = 'tab'
const DATA_KEY = 'bs.tab'
const EVENT_KEY = `.${DATA_KEY}`
-const DATA_API_KEY = '.data-api'
const EVENT_HIDE = `hide${EVENT_KEY}`
const EVENT_HIDDEN = `hidden${EVENT_KEY}`
const EVENT_SHOW = `show${EVENT_KEY}`
const EVENT_SHOWN = `shown${EVENT_KEY}`
-const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
+const EVENT_CLICK_DATA_API = `click${EVENT_KEY}`
+const EVENT_KEYDOWN = `keydown${EVENT_KEY}`
+const EVENT_LOAD_DATA_API = `load${EVENT_KEY}`
+
+const ARROW_LEFT_KEY = 'ArrowLeft'
+const ARROW_RIGHT_KEY = 'ArrowRight'
+const ARROW_UP_KEY = 'ArrowUp'
+const ARROW_DOWN_KEY = 'ArrowDown'
+const HOME_KEY = 'Home'
+const END_KEY = 'End'
-const CLASS_NAME_DROPDOWN_MENU = 'dropdown-menu'
const CLASS_NAME_ACTIVE = 'active'
const CLASS_NAME_FADE = 'fade'
const CLASS_NAME_SHOW = 'show'
+const CLASS_DROPDOWN = 'dropdown'
-const SELECTOR_DROPDOWN = '.dropdown'
-const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'
-const SELECTOR_ACTIVE = '.active'
-const SELECTOR_ACTIVE_UL = ':scope > li > .active'
-const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]'
const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'
-const SELECTOR_DROPDOWN_ACTIVE_CHILD = ':scope > .dropdown-menu .active'
+const SELECTOR_DROPDOWN_MENU = '.dropdown-menu'
+const NOT_SELECTOR_DROPDOWN_TOGGLE = `:not(${SELECTOR_DROPDOWN_TOGGLE})`
+
+const SELECTOR_TAB_PANEL = '.list-group, .nav, [role="tablist"]'
+const SELECTOR_OUTER = '.nav-item, .list-group-item'
+const SELECTOR_INNER = `.nav-link${NOT_SELECTOR_DROPDOWN_TOGGLE}, .list-group-item${NOT_SELECTOR_DROPDOWN_TOGGLE}, [role="tab"]${NOT_SELECTOR_DROPDOWN_TOGGLE}`
+const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]' // TODO: could only be `tab` in v6
+const SELECTOR_INNER_ELEM = `${SELECTOR_INNER}, ${SELECTOR_DATA_TOGGLE}`
+
+const SELECTOR_DATA_TOGGLE_ACTIVE = `.${CLASS_NAME_ACTIVE}[data-bs-toggle="tab"], .${CLASS_NAME_ACTIVE}[data-bs-toggle="pill"], .${CLASS_NAME_ACTIVE}[data-bs-toggle="list"]`
/**
* Class definition
*/
class Tab extends BaseComponent {
+ constructor(element) {
+ super(element)
+ this._parent = this._element.closest(SELECTOR_TAB_PANEL)
+
+ if (!this._parent) {
+ return
+ // TODO: should throw exception in v6
+ // throw new TypeError(`${element.outerHTML} has not a valid parent ${SELECTOR_INNER_ELEM}`)
+ }
+
+ // Set up initial aria attributes
+ this._setInitialAttributes(this._parent, this._getChildren())
+
+ EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event))
+ }
+
// Getters
static get NAME() {
return NAME
}
// Public
- show() {
- if ((this._element.parentNode &&
- this._element.parentNode.nodeType === Node.ELEMENT_NODE &&
- this._element.classList.contains(CLASS_NAME_ACTIVE))) {
+ show() { // Shows this elem and deactivate the active sibling if exists
+ const innerElem = this._element
+ if (this._elemIsActive(innerElem)) {
return
}
- let previous
- const target = getElementFromSelector(this._element)
- const listElement = this._element.closest(SELECTOR_NAV_LIST_GROUP)
+ // Search for active tab on same parent to deactivate it
+ const active = this._getActiveElem()
- if (listElement) {
- const itemSelector = listElement.nodeName === 'UL' || listElement.nodeName === 'OL' ? SELECTOR_ACTIVE_UL : SELECTOR_ACTIVE
- previous = SelectorEngine.find(itemSelector, listElement)
- previous = previous[previous.length - 1]
- }
-
- const hideEvent = previous ?
- EventHandler.trigger(previous, EVENT_HIDE, { relatedTarget: this._element }) :
+ const hideEvent = active ?
+ EventHandler.trigger(active, EVENT_HIDE, { relatedTarget: innerElem }) :
null
- const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {
- relatedTarget: previous
- })
+ const showEvent = EventHandler.trigger(innerElem, EVENT_SHOW, { relatedTarget: active })
- if (showEvent.defaultPrevented || (hideEvent !== null && hideEvent.defaultPrevented)) {
+ if (showEvent.defaultPrevented || (hideEvent && hideEvent.defaultPrevented)) {
+ return
+ }
+
+ this._deactivate(active, innerElem)
+ this._activate(innerElem, active)
+ }
+
+ // Private
+ _activate(element, relatedElem) {
+ if (!element) {
return
}
- this._activate(this._element, listElement)
+ element.classList.add(CLASS_NAME_ACTIVE)
+
+ this._activate(SelectorEngine.getElementFromSelector(element)) // Search and activate/show the proper section
const complete = () => {
- EventHandler.trigger(previous, EVENT_HIDDEN, { relatedTarget: this._element })
- EventHandler.trigger(this._element, EVENT_SHOWN, { relatedTarget: previous })
+ if (element.getAttribute('role') !== 'tab') {
+ element.classList.add(CLASS_NAME_SHOW)
+ return
+ }
+
+ element.removeAttribute('tabindex')
+ element.setAttribute('aria-selected', true)
+ this._toggleDropDown(element, true)
+ EventHandler.trigger(element, EVENT_SHOWN, {
+ relatedTarget: relatedElem
+ })
}
- if (target) {
- this._activate(target, target.parentNode, complete)
- } else {
- complete()
+ this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE))
+ }
+
+ _deactivate(element, relatedElem) {
+ if (!element) {
+ return
}
+
+ element.classList.remove(CLASS_NAME_ACTIVE)
+ element.blur()
+
+ this._deactivate(SelectorEngine.getElementFromSelector(element)) // Search and deactivate the shown section too
+
+ const complete = () => {
+ if (element.getAttribute('role') !== 'tab') {
+ element.classList.remove(CLASS_NAME_SHOW)
+ return
+ }
+
+ element.setAttribute('aria-selected', false)
+ element.setAttribute('tabindex', '-1')
+ this._toggleDropDown(element, false)
+ EventHandler.trigger(element, EVENT_HIDDEN, { relatedTarget: relatedElem })
+ }
+
+ this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE))
}
- // Private
- _activate(element, container, callback) {
- const activeElements = container && (container.nodeName === 'UL' || container.nodeName === 'OL') ?
- SelectorEngine.find(SELECTOR_ACTIVE_UL, container) :
- SelectorEngine.children(container, SELECTOR_ACTIVE)
+ _keydown(event) {
+ if (!([ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY, HOME_KEY, END_KEY].includes(event.key))) {
+ return
+ }
- const active = activeElements[0]
- const isTransitioning = callback && (active && active.classList.contains(CLASS_NAME_FADE))
+ event.stopPropagation()// stopPropagation/preventDefault both added to support up/down keys without scrolling the page
+ event.preventDefault()
- const complete = () => this._transitionComplete(element, active, callback)
+ const children = this._getChildren().filter(element => !isDisabled(element))
+ let nextActiveElement
- if (active && isTransitioning) {
- active.classList.remove(CLASS_NAME_SHOW)
- this._queueCallback(complete, element, true)
+ if ([HOME_KEY, END_KEY].includes(event.key)) {
+ nextActiveElement = children[event.key === HOME_KEY ? 0 : children.length - 1]
} else {
- complete()
+ const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key)
+ nextActiveElement = getNextActiveElement(children, event.target, isNext, true)
+ }
+
+ if (nextActiveElement) {
+ nextActiveElement.focus({ preventScroll: true })
+ Tab.getOrCreateInstance(nextActiveElement).show()
}
}
- _transitionComplete(element, active, callback) {
- if (active) {
- active.classList.remove(CLASS_NAME_ACTIVE)
+ _getChildren() { // collection of inner elements
+ return SelectorEngine.find(SELECTOR_INNER_ELEM, this._parent)
+ }
- const dropdownChild = SelectorEngine.findOne(SELECTOR_DROPDOWN_ACTIVE_CHILD, active.parentNode)
+ _getActiveElem() {
+ return this._getChildren().find(child => this._elemIsActive(child)) || null
+ }
- if (dropdownChild) {
- dropdownChild.classList.remove(CLASS_NAME_ACTIVE)
- }
+ _setInitialAttributes(parent, children) {
+ this._setAttributeIfNotExists(parent, 'role', 'tablist')
- if (active.getAttribute('role') === 'tab') {
- active.setAttribute('aria-selected', false)
- }
+ for (const child of children) {
+ this._setInitialAttributesOnChild(child)
}
+ }
- element.classList.add(CLASS_NAME_ACTIVE)
- if (element.getAttribute('role') === 'tab') {
- element.setAttribute('aria-selected', true)
+ _setInitialAttributesOnChild(child) {
+ child = this._getInnerElement(child)
+ const isActive = this._elemIsActive(child)
+ const outerElem = this._getOuterElement(child)
+ child.setAttribute('aria-selected', isActive)
+
+ if (outerElem !== child) {
+ this._setAttributeIfNotExists(outerElem, 'role', 'presentation')
}
- reflow(element)
+ if (!isActive) {
+ child.setAttribute('tabindex', '-1')
+ }
- if (element.classList.contains(CLASS_NAME_FADE)) {
- element.classList.add(CLASS_NAME_SHOW)
+ this._setAttributeIfNotExists(child, 'role', 'tab')
+
+ // set attributes to the related panel too
+ this._setInitialAttributesOnTargetPanel(child)
+ }
+
+ _setInitialAttributesOnTargetPanel(child) {
+ const target = SelectorEngine.getElementFromSelector(child)
+
+ if (!target) {
+ return
}
- let parent = element.parentNode
- if (parent && parent.nodeName === 'LI') {
- parent = parent.parentNode
+ this._setAttributeIfNotExists(target, 'role', 'tabpanel')
+
+ if (child.id) {
+ this._setAttributeIfNotExists(target, 'aria-labelledby', `${child.id}`)
}
+ }
- if (parent && parent.classList.contains(CLASS_NAME_DROPDOWN_MENU)) {
- const dropdownElement = element.closest(SELECTOR_DROPDOWN)
+ _toggleDropDown(element, open) {
+ const outerElem = this._getOuterElement(element)
+ if (!outerElem.classList.contains(CLASS_DROPDOWN)) {
+ return
+ }
- if (dropdownElement) {
- for (const dropdown of SelectorEngine.find(SELECTOR_DROPDOWN_TOGGLE, dropdownElement)) {
- dropdown.classList.add(CLASS_NAME_ACTIVE)
- }
+ const toggle = (selector, className) => {
+ const element = SelectorEngine.findOne(selector, outerElem)
+ if (element) {
+ element.classList.toggle(className, open)
}
-
- element.setAttribute('aria-expanded', true)
}
- if (callback) {
- callback()
+ toggle(SELECTOR_DROPDOWN_TOGGLE, CLASS_NAME_ACTIVE)
+ toggle(SELECTOR_DROPDOWN_MENU, CLASS_NAME_SHOW)
+ outerElem.setAttribute('aria-expanded', open)
+ }
+
+ _setAttributeIfNotExists(element, attribute, value) {
+ if (!element.hasAttribute(attribute)) {
+ element.setAttribute(attribute, value)
}
}
+ _elemIsActive(elem) {
+ return elem.classList.contains(CLASS_NAME_ACTIVE)
+ }
+
+ // Try to get the inner element (usually the .nav-link)
+ _getInnerElement(elem) {
+ return elem.matches(SELECTOR_INNER_ELEM) ? elem : SelectorEngine.findOne(SELECTOR_INNER_ELEM, elem)
+ }
+
+ // Try to get the outer element (usually the .nav-item)
+ _getOuterElement(elem) {
+ return elem.closest(SELECTOR_OUTER) || elem
+ }
+
// Static
static jQueryInterface(config) {
return this.each(function () {
const data = Tab.getOrCreateInstance(this)
- if (typeof config === 'string') {
- if (typeof data[config] === 'undefined') {
- throw new TypeError(`No method named "${config}"`)
- }
+ if (typeof config !== 'string') {
+ return
+ }
- data[config]()
+ if (data[config] === undefined || config.startsWith('_') || config === 'constructor') {
+ throw new TypeError(`No method named "${config}"`)
}
+
+ data[config]()
})
}
}
@@ -193,11 +295,18 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
return
}
- const data = Tab.getOrCreateInstance(this)
- data.show()
+ Tab.getOrCreateInstance(this).show()
})
/**
+ * Initialize on focus
+ */
+EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
+ for (const element of SelectorEngine.find(SELECTOR_DATA_TOGGLE_ACTIVE)) {
+ Tab.getOrCreateInstance(element)
+ }
+})
+/**
* jQuery
*/
diff --git a/js/src/toast.js b/js/src/toast.js
index c45721c8f..d5d9c0ee0 100644
--- a/js/src/toast.js
+++ b/js/src/toast.js
@@ -1,19 +1,14 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): toast.js
+ * Bootstrap toast.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import {
- defineJQueryPlugin,
- reflow,
- typeCheckConfig
-} from './util/index'
-import EventHandler from './dom/event-handler'
-import Manipulator from './dom/manipulator'
-import BaseComponent from './base-component'
-import { enableDismissTrigger } from './util/component-functions'
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import { enableDismissTrigger } from './util/component-functions.js'
+import { defineJQueryPlugin, reflow } from './util/index.js'
/**
* Constants
@@ -55,9 +50,8 @@ const Default = {
class Toast extends BaseComponent {
constructor(element, config) {
- super(element)
+ super(element, config)
- this._config = this._getConfig(config)
this._timeout = null
this._hasMouseInteraction = false
this._hasKeyboardInteraction = false
@@ -65,14 +59,14 @@ class Toast extends BaseComponent {
}
// Getters
- static get DefaultType() {
- return DefaultType
- }
-
static get Default() {
return Default
}
+ static get DefaultType() {
+ return DefaultType
+ }
+
static get NAME() {
return NAME
}
@@ -100,14 +94,13 @@ class Toast extends BaseComponent {
this._element.classList.remove(CLASS_NAME_HIDE) // @deprecated
reflow(this._element)
- this._element.classList.add(CLASS_NAME_SHOW)
- this._element.classList.add(CLASS_NAME_SHOWING)
+ this._element.classList.add(CLASS_NAME_SHOW, CLASS_NAME_SHOWING)
this._queueCallback(complete, this._element, this._config.animation)
}
hide() {
- if (!this._element.classList.contains(CLASS_NAME_SHOW)) {
+ if (!this.isShown()) {
return
}
@@ -119,8 +112,7 @@ class Toast extends BaseComponent {
const complete = () => {
this._element.classList.add(CLASS_NAME_HIDE) // @deprecated
- this._element.classList.remove(CLASS_NAME_SHOWING)
- this._element.classList.remove(CLASS_NAME_SHOW)
+ this._element.classList.remove(CLASS_NAME_SHOWING, CLASS_NAME_SHOW)
EventHandler.trigger(this._element, EVENT_HIDDEN)
}
@@ -131,26 +123,19 @@ class Toast extends BaseComponent {
dispose() {
this._clearTimeout()
- if (this._element.classList.contains(CLASS_NAME_SHOW)) {
+ if (this.isShown()) {
this._element.classList.remove(CLASS_NAME_SHOW)
}
super.dispose()
}
- // Private
- _getConfig(config) {
- config = {
- ...Default,
- ...Manipulator.getDataAttributes(this._element),
- ...(typeof config === 'object' && config ? config : {})
- }
-
- typeCheckConfig(NAME, config, this.constructor.DefaultType)
-
- return config
+ isShown() {
+ return this._element.classList.contains(CLASS_NAME_SHOW)
}
+ // Private
+
_maybeScheduleHide() {
if (!this._config.autohide) {
return
@@ -168,15 +153,20 @@ class Toast extends BaseComponent {
_onInteraction(event, isInteracting) {
switch (event.type) {
case 'mouseover':
- case 'mouseout':
+ case 'mouseout': {
this._hasMouseInteraction = isInteracting
break
+ }
+
case 'focusin':
- case 'focusout':
+ case 'focusout': {
this._hasKeyboardInteraction = isInteracting
break
- default:
+ }
+
+ default: {
break
+ }
}
if (isInteracting) {
diff --git a/js/src/tooltip.js b/js/src/tooltip.js
index 29be4d8d2..92d455349 100644
--- a/js/src/tooltip.js
+++ b/js/src/tooltip.js
@@ -1,44 +1,31 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): tooltip.js
+ * Bootstrap tooltip.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
import * as Popper from '@popperjs/core'
+import BaseComponent from './base-component.js'
+import EventHandler from './dom/event-handler.js'
+import Manipulator from './dom/manipulator.js'
import {
- defineJQueryPlugin,
- findShadowRoot,
- getElement,
- getUID,
- isRTL,
- noop,
- typeCheckConfig
-} from './util/index'
-import { DefaultAllowlist } from './util/sanitizer'
-import Data from './dom/data'
-import EventHandler from './dom/event-handler'
-import Manipulator from './dom/manipulator'
-import BaseComponent from './base-component'
-import TemplateFactory from './util/template-factory'
+ defineJQueryPlugin, execute, findShadowRoot, getElement, getUID, isRTL, noop
+} from './util/index.js'
+import { DefaultAllowlist } from './util/sanitizer.js'
+import TemplateFactory from './util/template-factory.js'
/**
* Constants
*/
const NAME = 'tooltip'
-const DATA_KEY = 'bs.tooltip'
-const EVENT_KEY = `.${DATA_KEY}`
const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn'])
const CLASS_NAME_FADE = 'fade'
const CLASS_NAME_MODAL = 'modal'
const CLASS_NAME_SHOW = 'show'
-const HOVER_STATE_SHOW = 'show'
-const HOVER_STATE_OUT = 'out'
-
-const SELECTOR_TOOLTIP_ARROW = '.tooltip-arrow'
const SELECTOR_TOOLTIP_INNER = '.tooltip-inner'
const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`
@@ -49,6 +36,17 @@ const TRIGGER_FOCUS = 'focus'
const TRIGGER_CLICK = 'click'
const TRIGGER_MANUAL = 'manual'
+const EVENT_HIDE = 'hide'
+const EVENT_HIDDEN = 'hidden'
+const EVENT_SHOW = 'show'
+const EVENT_SHOWN = 'shown'
+const EVENT_INSERTED = 'inserted'
+const EVENT_CLICK = 'click'
+const EVENT_FOCUSIN = 'focusin'
+const EVENT_FOCUSOUT = 'focusout'
+const EVENT_MOUSEENTER = 'mouseenter'
+const EVENT_MOUSELEAVE = 'mouseleave'
+
const AttachmentMap = {
AUTO: 'auto',
TOP: 'top',
@@ -58,59 +56,46 @@ const AttachmentMap = {
}
const Default = {
+ allowList: DefaultAllowlist,
animation: true,
- template: '<div class="tooltip" role="tooltip">' +
- '<div class="tooltip-arrow"></div>' +
- '<div class="tooltip-inner"></div>' +
- '</div>',
- trigger: 'hover focus',
- title: '',
+ boundary: 'clippingParents',
+ container: false,
+ customClass: '',
delay: 0,
+ fallbackPlacements: ['top', 'right', 'bottom', 'left'],
html: false,
- selector: false,
+ offset: [0, 6],
placement: 'top',
- offset: [0, 0],
- container: false,
- fallbackPlacements: ['top', 'right', 'bottom', 'left'],
- boundary: 'clippingParents',
- customClass: '',
+ popperConfig: null,
sanitize: true,
sanitizeFn: null,
- allowList: DefaultAllowlist,
- popperConfig: null
+ selector: false,
+ template: '<div class="tooltip" role="tooltip">' +
+ '<div class="tooltip-arrow"></div>' +
+ '<div class="tooltip-inner"></div>' +
+ '</div>',
+ title: '',
+ trigger: 'hover focus'
}
const DefaultType = {
+ allowList: 'object',
animation: 'boolean',
- template: 'string',
- title: '(string|element|function)',
- trigger: 'string',
+ boundary: '(string|element)',
+ container: '(string|element|boolean)',
+ customClass: '(string|function)',
delay: '(number|object)',
+ fallbackPlacements: 'array',
html: 'boolean',
- selector: '(string|boolean)',
- placement: '(string|function)',
offset: '(array|string|function)',
- container: '(string|element|boolean)',
- fallbackPlacements: 'array',
- boundary: '(string|element)',
- customClass: '(string|function)',
+ placement: '(string|function)',
+ popperConfig: '(null|object|function)',
sanitize: 'boolean',
sanitizeFn: '(null|function)',
- allowList: 'object',
- popperConfig: '(null|object|function)'
-}
-
-const Event = {
- HIDE: `hide${EVENT_KEY}`,
- HIDDEN: `hidden${EVENT_KEY}`,
- SHOW: `show${EVENT_KEY}`,
- SHOWN: `shown${EVENT_KEY}`,
- INSERTED: `inserted${EVENT_KEY}`,
- CLICK: `click${EVENT_KEY}`,
- FOCUSIN: `focusin${EVENT_KEY}`,
- FOCUSOUT: `focusout${EVENT_KEY}`,
- MOUSEENTER: `mouseenter${EVENT_KEY}`,
- MOUSELEAVE: `mouseleave${EVENT_KEY}`
+ selector: '(string|boolean)',
+ template: 'string',
+ title: '(string|element|function)',
+ trigger: 'string'
}
/**
@@ -120,24 +105,28 @@ const Event = {
class Tooltip extends BaseComponent {
constructor(element, config) {
if (typeof Popper === 'undefined') {
- throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org)')
+ throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org/docs/v2/)')
}
- super(element)
+ super(element, config)
// Private
this._isEnabled = true
this._timeout = 0
- this._hoverState = ''
+ this._isHovered = null
this._activeTrigger = {}
this._popper = null
this._templateFactory = null
+ this._newContent = null
// Protected
- this._config = this._getConfig(config)
this.tip = null
this._setListeners()
+
+ if (!this._config.selector) {
+ this._fixTitle()
+ }
}
// Getters
@@ -145,18 +134,14 @@ class Tooltip extends BaseComponent {
return Default
}
- static get NAME() {
- return NAME
- }
-
- static get Event() {
- return Event
- }
-
static get DefaultType() {
return DefaultType
}
+ static get NAME() {
+ return NAME
+ }
+
// Public
enable() {
this._isEnabled = true
@@ -170,29 +155,18 @@ class Tooltip extends BaseComponent {
this._isEnabled = !this._isEnabled
}
- toggle(event) {
+ toggle() {
if (!this._isEnabled) {
return
}
- if (event) {
- const context = this._initializeOnDelegatedTarget(event)
-
- context._activeTrigger.click = !context._activeTrigger.click
-
- if (context._isWithActiveTrigger()) {
- context._enter(null, context)
- } else {
- context._leave(null, context)
- }
- } else {
- if (this.getTipElement().classList.contains(CLASS_NAME_SHOW)) {
- this._leave(null, this)
- return
- }
-
- this._enter(null, this)
+ this._activeTrigger.click = !this._activeTrigger.click
+ if (this._isShown()) {
+ this._leave()
+ return
}
+
+ this._enter()
}
dispose() {
@@ -200,8 +174,8 @@ class Tooltip extends BaseComponent {
EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler)
- if (this.tip) {
- this.tip.remove()
+ if (this._element.getAttribute('data-bs-original-title')) {
+ this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title'))
}
this._disposePopper()
@@ -213,41 +187,33 @@ class Tooltip extends BaseComponent {
throw new Error('Please use show on visible elements')
}
- if (!(this.isWithContent() && this._isEnabled)) {
+ if (!(this._isWithContent() && this._isEnabled)) {
return
}
- const showEvent = EventHandler.trigger(this._element, this.constructor.Event.SHOW)
+ const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW))
const shadowRoot = findShadowRoot(this._element)
- const isInTheDom = shadowRoot === null ?
- this._element.ownerDocument.documentElement.contains(this._element) :
- shadowRoot.contains(this._element)
+ const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element)
if (showEvent.defaultPrevented || !isInTheDom) {
return
}
- const tip = this.getTipElement()
+ // TODO: v6 remove this or make it optional
+ this._disposePopper()
+
+ const tip = this._getTipElement()
this._element.setAttribute('aria-describedby', tip.getAttribute('id'))
const { container } = this._config
- Data.set(tip, this.constructor.DATA_KEY, this)
if (!this._element.ownerDocument.documentElement.contains(this.tip)) {
container.append(tip)
- EventHandler.trigger(this._element, this.constructor.Event.INSERTED)
+ EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED))
}
- if (this._popper) {
- this._popper.update()
- } else {
- const placement = typeof this._config.placement === 'function' ?
- this._config.placement.call(this, tip, this._element) :
- this._config.placement
- const attachment = AttachmentMap[placement.toUpperCase()]
- this._popper = Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))
- }
+ this._popper = this._createPopper(tip)
tip.classList.add(CLASS_NAME_SHOW)
@@ -262,46 +228,29 @@ class Tooltip extends BaseComponent {
}
const complete = () => {
- const prevHoverState = this._hoverState
+ EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN))
- this._hoverState = null
- EventHandler.trigger(this._element, this.constructor.Event.SHOWN)
-
- if (prevHoverState === HOVER_STATE_OUT) {
- this._leave(null, this)
+ if (this._isHovered === false) {
+ this._leave()
}
+
+ this._isHovered = false
}
- const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE)
- this._queueCallback(complete, this.tip, isAnimated)
+ this._queueCallback(complete, this.tip, this._isAnimated())
}
hide() {
- if (!this._popper) {
+ if (!this._isShown()) {
return
}
- const tip = this.getTipElement()
- const complete = () => {
- if (this._isWithActiveTrigger()) {
- return
- }
-
- if (this._hoverState !== HOVER_STATE_SHOW) {
- tip.remove()
- }
-
- this._element.removeAttribute('aria-describedby')
- EventHandler.trigger(this._element, this.constructor.Event.HIDDEN)
-
- this._disposePopper()
- }
-
- const hideEvent = EventHandler.trigger(this._element, this.constructor.Event.HIDE)
+ const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE))
if (hideEvent.defaultPrevented) {
return
}
+ const tip = this._getTipElement()
tip.classList.remove(CLASS_NAME_SHOW)
// If this is a touch-enabled device we remove the extra
@@ -315,59 +264,70 @@ class Tooltip extends BaseComponent {
this._activeTrigger[TRIGGER_CLICK] = false
this._activeTrigger[TRIGGER_FOCUS] = false
this._activeTrigger[TRIGGER_HOVER] = false
+ this._isHovered = null // it is a trick to support manual triggering
+
+ const complete = () => {
+ if (this._isWithActiveTrigger()) {
+ return
+ }
+
+ if (!this._isHovered) {
+ this._disposePopper()
+ }
- const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE)
- this._queueCallback(complete, this.tip, isAnimated)
- this._hoverState = ''
+ this._element.removeAttribute('aria-describedby')
+ EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN))
+ }
+
+ this._queueCallback(complete, this.tip, this._isAnimated())
}
update() {
- if (this._popper !== null) {
+ if (this._popper) {
this._popper.update()
}
}
// Protected
- isWithContent() {
- return Boolean(this.getTitle())
+ _isWithContent() {
+ return Boolean(this._getTitle())
}
- getTipElement() {
- if (this.tip) {
- return this.tip
+ _getTipElement() {
+ if (!this.tip) {
+ this.tip = this._createTipElement(this._newContent || this._getContentForTemplate())
}
- const templateFactory = this._getTemplateFactory(this._getContentForTemplate())
+ return this.tip
+ }
+
+ _createTipElement(content) {
+ const tip = this._getTemplateFactory(content).toHtml()
+
+ // TODO: remove this check in v6
+ if (!tip) {
+ return null
+ }
- const tip = templateFactory.toHtml()
tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW)
- // todo on v6 the following can be done on css only
+ // TODO: v6 the following can be achieved with CSS only
tip.classList.add(`bs-${this.constructor.NAME}-auto`)
const tipId = getUID(this.constructor.NAME).toString()
tip.setAttribute('id', tipId)
- if (this._config.animation) {
+ if (this._isAnimated()) {
tip.classList.add(CLASS_NAME_FADE)
}
- this.tip = tip
- return this.tip
+ return tip
}
setContent(content) {
- let isShown = false
- if (this.tip) {
- isShown = this.tip.classList.contains(CLASS_NAME_SHOW)
- this.tip.remove()
- }
-
- this._disposePopper()
-
- this.tip = this._getTemplateFactory(content).toHtml()
-
- if (isShown) {
+ this._newContent = content
+ if (this._isShown()) {
+ this._disposePopper()
this.show()
}
}
@@ -390,36 +350,38 @@ class Tooltip extends BaseComponent {
_getContentForTemplate() {
return {
- [SELECTOR_TOOLTIP_INNER]: this.getTitle()
+ [SELECTOR_TOOLTIP_INNER]: this._getTitle()
}
}
- getTitle() {
- return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('title')
+ _getTitle() {
+ return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title')
}
- updateAttachment(attachment) {
- if (attachment === 'right') {
- return 'end'
- }
+ // Private
+ _initializeOnDelegatedTarget(event) {
+ return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig())
+ }
- if (attachment === 'left') {
- return 'start'
- }
+ _isAnimated() {
+ return this._config.animation || (this.tip && this.tip.classList.contains(CLASS_NAME_FADE))
+ }
- return attachment
+ _isShown() {
+ return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW)
}
- // Private
- _initializeOnDelegatedTarget(event, context) {
- return context || this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig())
+ _createPopper(tip) {
+ const placement = execute(this._config.placement, [this, tip, this._element])
+ const attachment = AttachmentMap[placement.toUpperCase()]
+ return Popper.createPopper(this._element, tip, this._getPopperConfig(attachment))
}
_getOffset() {
const { offset } = this._config
if (typeof offset === 'string') {
- return offset.split(',').map(val => Number.parseInt(val, 10))
+ return offset.split(',').map(value => Number.parseInt(value, 10))
}
if (typeof offset === 'function') {
@@ -430,7 +392,7 @@ class Tooltip extends BaseComponent {
}
_resolvePossibleFunction(arg) {
- return typeof arg === 'function' ? arg.call(this._element) : arg
+ return execute(arg, [this._element, this._element])
}
_getPopperConfig(attachment) {
@@ -458,7 +420,17 @@ class Tooltip extends BaseComponent {
{
name: 'arrow',
options: {
- element: SELECTOR_TOOLTIP_ARROW
+ element: `.${this.constructor.NAME}-arrow`
+ }
+ },
+ {
+ name: 'preSetPlacement',
+ enabled: true,
+ phase: 'beforeMain',
+ fn: data => {
+ // Pre-set Popper's placement attribute in order to read the arrow sizes properly.
+ // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement
+ this._getTipElement().setAttribute('data-popper-placement', data.state.placement)
}
}
]
@@ -466,7 +438,7 @@ class Tooltip extends BaseComponent {
return {
...defaultBsPopperConfig,
- ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig)
+ ...execute(this._config.popperConfig, [undefined, defaultBsPopperConfig])
}
}
@@ -475,17 +447,30 @@ class Tooltip extends BaseComponent {
for (const trigger of triggers) {
if (trigger === 'click') {
- EventHandler.on(this._element, this.constructor.Event.CLICK, this._config.selector, event => this.toggle(event))
+ EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK), this._config.selector, event => {
+ const context = this._initializeOnDelegatedTarget(event)
+ context.toggle()
+ })
} else if (trigger !== TRIGGER_MANUAL) {
const eventIn = trigger === TRIGGER_HOVER ?
- this.constructor.Event.MOUSEENTER :
- this.constructor.Event.FOCUSIN
+ this.constructor.eventName(EVENT_MOUSEENTER) :
+ this.constructor.eventName(EVENT_FOCUSIN)
const eventOut = trigger === TRIGGER_HOVER ?
- this.constructor.Event.MOUSELEAVE :
- this.constructor.Event.FOCUSOUT
-
- EventHandler.on(this._element, eventIn, this._config.selector, event => this._enter(event))
- EventHandler.on(this._element, eventOut, this._config.selector, event => this._leave(event))
+ this.constructor.eventName(EVENT_MOUSELEAVE) :
+ this.constructor.eventName(EVENT_FOCUSOUT)
+
+ EventHandler.on(this._element, eventIn, this._config.selector, event => {
+ const context = this._initializeOnDelegatedTarget(event)
+ context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true
+ context._enter()
+ })
+ EventHandler.on(this._element, eventOut, this._config.selector, event => {
+ const context = this._initializeOnDelegatedTarget(event)
+ context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] =
+ context._element.contains(event.relatedTarget)
+
+ context._leave()
+ })
}
}
@@ -496,83 +481,55 @@ class Tooltip extends BaseComponent {
}
EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler)
-
- if (this._config.selector) {
- this._config = {
- ...this._config,
- trigger: 'manual',
- selector: ''
- }
- } else {
- this._fixTitle()
- }
}
_fixTitle() {
const title = this._element.getAttribute('title')
- if (title && !this._element.getAttribute('aria-label') && !this._element.textContent) {
- this._element.setAttribute('aria-label', title)
- }
- }
-
- _enter(event, context) {
- context = this._initializeOnDelegatedTarget(event, context)
-
- if (event) {
- context._activeTrigger[
- event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER
- ] = true
- }
-
- if (context.getTipElement().classList.contains(CLASS_NAME_SHOW) || context._hoverState === HOVER_STATE_SHOW) {
- context._hoverState = HOVER_STATE_SHOW
+ if (!title) {
return
}
- clearTimeout(context._timeout)
-
- context._hoverState = HOVER_STATE_SHOW
-
- if (!context._config.delay || !context._config.delay.show) {
- context.show()
- return
+ if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) {
+ this._element.setAttribute('aria-label', title)
}
- context._timeout = setTimeout(() => {
- if (context._hoverState === HOVER_STATE_SHOW) {
- context.show()
- }
- }, context._config.delay.show)
+ this._element.setAttribute('data-bs-original-title', title) // DO NOT USE IT. Is only for backwards compatibility
+ this._element.removeAttribute('title')
}
- _leave(event, context) {
- context = this._initializeOnDelegatedTarget(event, context)
-
- if (event) {
- context._activeTrigger[
- event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER
- ] = context._element.contains(event.relatedTarget)
- }
-
- if (context._isWithActiveTrigger()) {
+ _enter() {
+ if (this._isShown() || this._isHovered) {
+ this._isHovered = true
return
}
- clearTimeout(context._timeout)
+ this._isHovered = true
- context._hoverState = HOVER_STATE_OUT
+ this._setTimeout(() => {
+ if (this._isHovered) {
+ this.show()
+ }
+ }, this._config.delay.show)
+ }
- if (!context._config.delay || !context._config.delay.hide) {
- context.hide()
+ _leave() {
+ if (this._isWithActiveTrigger()) {
return
}
- context._timeout = setTimeout(() => {
- if (context._hoverState === HOVER_STATE_OUT) {
- context.hide()
+ this._isHovered = false
+
+ this._setTimeout(() => {
+ if (!this._isHovered) {
+ this.hide()
}
- }, context._config.delay.hide)
+ }, this._config.delay.hide)
+ }
+
+ _setTimeout(handler, timeout) {
+ clearTimeout(this._timeout)
+ this._timeout = setTimeout(handler, timeout)
}
_isWithActiveTrigger() {
@@ -582,18 +539,23 @@ class Tooltip extends BaseComponent {
_getConfig(config) {
const dataAttributes = Manipulator.getDataAttributes(this._element)
- for (const dataAttr of Object.keys(dataAttributes)) {
- if (DISALLOWED_ATTRIBUTES.has(dataAttr)) {
- delete dataAttributes[dataAttr]
+ for (const dataAttribute of Object.keys(dataAttributes)) {
+ if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) {
+ delete dataAttributes[dataAttribute]
}
}
config = {
- ...this.constructor.Default,
...dataAttributes,
...(typeof config === 'object' && config ? config : {})
}
+ config = this._mergeConfigObj(config)
+ config = this._configAfterMerge(config)
+ this._typeCheckConfig(config)
+ return config
+ }
+ _configAfterMerge(config) {
config.container = config.container === false ? document.body : getElement(config.container)
if (typeof config.delay === 'number') {
@@ -611,19 +573,21 @@ class Tooltip extends BaseComponent {
config.content = config.content.toString()
}
- typeCheckConfig(NAME, config, this.constructor.DefaultType)
return config
}
_getDelegateConfig() {
const config = {}
- for (const key in this._config) {
- if (this.constructor.Default[key] !== this._config[key]) {
- config[key] = this._config[key]
+ for (const [key, value] of Object.entries(this._config)) {
+ if (this.constructor.Default[key] !== value) {
+ config[key] = value
}
}
+ config.selector = false
+ config.trigger = 'manual'
+
// In the future can be replaced with:
// const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]])
// `Object.fromEntries(keysWithDifferentValues)`
@@ -635,21 +599,27 @@ class Tooltip extends BaseComponent {
this._popper.destroy()
this._popper = null
}
+
+ if (this.tip) {
+ this.tip.remove()
+ this.tip = null
+ }
}
// Static
-
static jQueryInterface(config) {
return this.each(function () {
const data = Tooltip.getOrCreateInstance(this, config)
- if (typeof config === 'string') {
- if (typeof data[config] === 'undefined') {
- throw new TypeError(`No method named "${config}"`)
- }
+ if (typeof config !== 'string') {
+ return
+ }
- data[config]()
+ if (typeof data[config] === 'undefined') {
+ throw new TypeError(`No method named "${config}"`)
}
+
+ data[config]()
})
}
}
diff --git a/js/src/util/backdrop.js b/js/src/util/backdrop.js
index fb1b2776b..82b54900e 100644
--- a/js/src/util/backdrop.js
+++ b/js/src/util/backdrop.js
@@ -1,12 +1,15 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/backdrop.js
+ * Bootstrap util/backdrop.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import EventHandler from '../dom/event-handler'
-import { execute, executeAfterTransition, getElement, reflow, typeCheckConfig } from './index'
+import EventHandler from '../dom/event-handler.js'
+import Config from './config.js'
+import {
+ execute, executeAfterTransition, getElement, reflow
+} from './index.js'
/**
* Constants
@@ -19,31 +22,45 @@ const EVENT_MOUSEDOWN = `mousedown.bs.${NAME}`
const Default = {
className: 'modal-backdrop',
- isVisible: true, // if false, we use the backdrop helper without adding any element to the dom
+ clickCallback: null,
isAnimated: false,
- rootElement: 'body', // give the choice to place backdrop under different elements
- clickCallback: null
+ isVisible: true, // if false, we use the backdrop helper without adding any element to the dom
+ rootElement: 'body' // give the choice to place backdrop under different elements
}
const DefaultType = {
className: 'string',
- isVisible: 'boolean',
+ clickCallback: '(function|null)',
isAnimated: 'boolean',
- rootElement: '(element|string)',
- clickCallback: '(function|null)'
+ isVisible: 'boolean',
+ rootElement: '(element|string)'
}
/**
* Class definition
*/
-class Backdrop {
+class Backdrop extends Config {
constructor(config) {
+ super()
this._config = this._getConfig(config)
this._isAppended = false
this._element = null
}
+ // Getters
+ static get Default() {
+ return Default
+ }
+
+ static get DefaultType() {
+ return DefaultType
+ }
+
+ static get NAME() {
+ return NAME
+ }
+
// Public
show(callback) {
if (!this._config.isVisible) {
@@ -53,11 +70,12 @@ class Backdrop {
this._append()
+ const element = this._getElement()
if (this._config.isAnimated) {
- reflow(this._getElement())
+ reflow(element)
}
- this._getElement().classList.add(CLASS_NAME_SHOW)
+ element.classList.add(CLASS_NAME_SHOW)
this._emulateAnimation(() => {
execute(callback)
@@ -104,15 +122,9 @@ class Backdrop {
return this._element
}
- _getConfig(config) {
- config = {
- ...Default,
- ...(typeof config === 'object' ? config : {})
- }
-
+ _configAfterMerge(config) {
// use getElement() with the default "body" to get a fresh Element on each instantiation
config.rootElement = getElement(config.rootElement)
- typeCheckConfig(NAME, config, DefaultType)
return config
}
@@ -121,9 +133,10 @@ class Backdrop {
return
}
- this._config.rootElement.append(this._getElement())
+ const element = this._getElement()
+ this._config.rootElement.append(element)
- EventHandler.on(this._getElement(), EVENT_MOUSEDOWN, () => {
+ EventHandler.on(element, EVENT_MOUSEDOWN, () => {
execute(this._config.clickCallback)
})
diff --git a/js/src/util/component-functions.js b/js/src/util/component-functions.js
index bd44c3fdc..4be828f83 100644
--- a/js/src/util/component-functions.js
+++ b/js/src/util/component-functions.js
@@ -1,12 +1,13 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/component-functions.js
+ * Bootstrap util/component-functions.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import EventHandler from '../dom/event-handler'
-import { getElementFromSelector, isDisabled } from './index'
+import EventHandler from '../dom/event-handler.js'
+import SelectorEngine from '../dom/selector-engine.js'
+import { isDisabled } from './index.js'
const enableDismissTrigger = (component, method = 'hide') => {
const clickEvent = `click.dismiss${component.EVENT_KEY}`
@@ -21,7 +22,7 @@ const enableDismissTrigger = (component, method = 'hide') => {
return
}
- const target = getElementFromSelector(this) || this.closest(`.${name}`)
+ const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`)
const instance = component.getOrCreateInstance(target)
// Method argument is left, for Alert and only, as it doesn't implement the 'hide' method
diff --git a/js/src/util/config.js b/js/src/util/config.js
new file mode 100644
index 000000000..a2b4bfba0
--- /dev/null
+++ b/js/src/util/config.js
@@ -0,0 +1,65 @@
+/**
+ * --------------------------------------------------------------------------
+ * Bootstrap util/config.js
+ * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
+ * --------------------------------------------------------------------------
+ */
+
+import Manipulator from '../dom/manipulator.js'
+import { isElement, toType } from './index.js'
+
+/**
+ * Class definition
+ */
+
+class Config {
+ // Getters
+ static get Default() {
+ return {}
+ }
+
+ static get DefaultType() {
+ return {}
+ }
+
+ static get NAME() {
+ throw new Error('You have to implement the static method "NAME", for each component!')
+ }
+
+ _getConfig(config) {
+ config = this._mergeConfigObj(config)
+ config = this._configAfterMerge(config)
+ this._typeCheckConfig(config)
+ return config
+ }
+
+ _configAfterMerge(config) {
+ return config
+ }
+
+ _mergeConfigObj(config, element) {
+ const jsonConfig = isElement(element) ? Manipulator.getDataAttribute(element, 'config') : {} // try to parse
+
+ return {
+ ...this.constructor.Default,
+ ...(typeof jsonConfig === 'object' ? jsonConfig : {}),
+ ...(isElement(element) ? Manipulator.getDataAttributes(element) : {}),
+ ...(typeof config === 'object' ? config : {})
+ }
+ }
+
+ _typeCheckConfig(config, configTypes = this.constructor.DefaultType) {
+ for (const [property, expectedTypes] of Object.entries(configTypes)) {
+ const value = config[property]
+ const valueType = isElement(value) ? 'element' : toType(value)
+
+ if (!new RegExp(expectedTypes).test(valueType)) {
+ throw new TypeError(
+ `${this.constructor.NAME.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`
+ )
+ }
+ }
+ }
+}
+
+export default Config
diff --git a/js/src/util/focustrap.js b/js/src/util/focustrap.js
index a1975f489..158f3d184 100644
--- a/js/src/util/focustrap.js
+++ b/js/src/util/focustrap.js
@@ -1,13 +1,13 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/focustrap.js
+ * Bootstrap util/focustrap.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import EventHandler from '../dom/event-handler'
-import SelectorEngine from '../dom/selector-engine'
-import { typeCheckConfig } from './index'
+import EventHandler from '../dom/event-handler.js'
+import SelectorEngine from '../dom/selector-engine.js'
+import Config from './config.js'
/**
* Constants
@@ -24,36 +24,48 @@ const TAB_NAV_FORWARD = 'forward'
const TAB_NAV_BACKWARD = 'backward'
const Default = {
- trapElement: null, // The element to trap focus inside of
- autofocus: true
+ autofocus: true,
+ trapElement: null // The element to trap focus inside of
}
const DefaultType = {
- trapElement: 'element',
- autofocus: 'boolean'
+ autofocus: 'boolean',
+ trapElement: 'element'
}
/**
* Class definition
*/
-class FocusTrap {
+class FocusTrap extends Config {
constructor(config) {
+ super()
this._config = this._getConfig(config)
this._isActive = false
this._lastTabNavDirection = null
}
+ // Getters
+ static get Default() {
+ return Default
+ }
+
+ static get DefaultType() {
+ return DefaultType
+ }
+
+ static get NAME() {
+ return NAME
+ }
+
// Public
activate() {
- const { trapElement, autofocus } = this._config
-
if (this._isActive) {
return
}
- if (autofocus) {
- trapElement.focus()
+ if (this._config.autofocus) {
+ this._config.trapElement.focus()
}
EventHandler.off(document, EVENT_KEY) // guard against infinite focus loop
@@ -74,10 +86,9 @@ class FocusTrap {
// Private
_handleFocusin(event) {
- const { target } = event
const { trapElement } = this._config
- if (target === document || target === trapElement || trapElement.contains(target)) {
+ if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) {
return
}
@@ -99,15 +110,6 @@ class FocusTrap {
this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD
}
-
- _getConfig(config) {
- config = {
- ...Default,
- ...(typeof config === 'object' ? config : {})
- }
- typeCheckConfig(NAME, config, DefaultType)
- return config
- }
}
export default FocusTrap
diff --git a/js/src/util/index.js b/js/src/util/index.js
index 0ba6ce6f8..c271cc536 100644
--- a/js/src/util/index.js
+++ b/js/src/util/index.js
@@ -1,6 +1,6 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/index.js
+ * Bootstrap util/index.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
@@ -9,13 +9,27 @@ const MAX_UID = 1_000_000
const MILLISECONDS_MULTIPLIER = 1000
const TRANSITION_END = 'transitionend'
-// Shoutout AngusCroll (https://goo.gl/pxwQGp)
-const toType = obj => {
- if (obj === null || obj === undefined) {
- return `${obj}`
+/**
+ * Properly escape IDs selectors to handle weird IDs
+ * @param {string} selector
+ * @returns {string}
+ */
+const parseSelector = selector => {
+ if (selector && window.CSS && window.CSS.escape) {
+ // document.querySelector needs escaping to handle IDs (html5+) containing for instance /
+ selector = selector.replace(/#([^\s"#']+)/g, (match, id) => `#${CSS.escape(id)}`)
+ }
+
+ return selector
+}
+
+// Shout-out Angus Croll (https://goo.gl/pxwQGp)
+const toType = object => {
+ if (object === null || object === undefined) {
+ return `${object}`
}
- return Object.prototype.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase()
+ return Object.prototype.toString.call(object).match(/\s([a-z]+)/i)[1].toLowerCase()
}
/**
@@ -30,47 +44,6 @@ const getUID = prefix => {
return prefix
}
-const getSelector = element => {
- let selector = element.getAttribute('data-bs-target')
-
- if (!selector || selector === '#') {
- let hrefAttr = element.getAttribute('href')
-
- // The only valid content that could double as a selector are IDs or classes,
- // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
- // `document.querySelector` will rightfully complain it is invalid.
- // See https://github.com/twbs/bootstrap/issues/32273
- if (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {
- return null
- }
-
- // Just in case some CMS puts out a full URL with the anchor appended
- if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {
- hrefAttr = `#${hrefAttr.split('#')[1]}`
- }
-
- selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null
- }
-
- return selector
-}
-
-const getSelectorFromElement = element => {
- const selector = getSelector(element)
-
- if (selector) {
- return document.querySelector(selector) ? selector : null
- }
-
- return null
-}
-
-const getElementFromSelector = element => {
- const selector = getSelector(element)
-
- return selector ? document.querySelector(selector) : null
-}
-
const getTransitionDurationFromElement = element => {
if (!element) {
return 0
@@ -98,51 +71,56 @@ const triggerTransitionEnd = element => {
element.dispatchEvent(new Event(TRANSITION_END))
}
-const isElement = obj => {
- if (!obj || typeof obj !== 'object') {
+const isElement = object => {
+ if (!object || typeof object !== 'object') {
return false
}
- if (typeof obj.jquery !== 'undefined') {
- obj = obj[0]
+ if (typeof object.jquery !== 'undefined') {
+ object = object[0]
}
- return typeof obj.nodeType !== 'undefined'
+ return typeof object.nodeType !== 'undefined'
}
-const getElement = obj => {
+const getElement = object => {
// it's a jQuery object or a node element
- if (isElement(obj)) {
- return obj.jquery ? obj[0] : obj
+ if (isElement(object)) {
+ return object.jquery ? object[0] : object
}
- if (typeof obj === 'string' && obj.length > 0) {
- return document.querySelector(obj)
+ if (typeof object === 'string' && object.length > 0) {
+ return document.querySelector(parseSelector(object))
}
return null
}
-const typeCheckConfig = (componentName, config, configTypes) => {
- for (const property of Object.keys(configTypes)) {
- const expectedTypes = configTypes[property]
- const value = config[property]
- const valueType = value && isElement(value) ? 'element' : toType(value)
-
- if (!new RegExp(expectedTypes).test(valueType)) {
- throw new TypeError(
- `${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`
- )
- }
- }
-}
-
const isVisible = element => {
if (!isElement(element) || element.getClientRects().length === 0) {
return false
}
- return getComputedStyle(element).getPropertyValue('visibility') === 'visible'
+ const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'
+ // Handle `details` element as its content may falsie appear visible when it is closed
+ const closedDetails = element.closest('details:not([open])')
+
+ if (!closedDetails) {
+ return elementIsVisible
+ }
+
+ if (closedDetails !== element) {
+ const summary = element.closest('summary')
+ if (summary && summary.parentNode !== closedDetails) {
+ return false
+ }
+
+ if (summary === null) {
+ return false
+ }
+ }
+
+ return elementIsVisible
}
const isDisabled = element => {
@@ -192,17 +170,15 @@ const noop = () => {}
* @param {HTMLElement} element
* @return void
*
- * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
+ * @see https://www.harrytheo.com/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
*/
const reflow = element => {
element.offsetHeight // eslint-disable-line no-unused-expressions
}
const getjQuery = () => {
- const { jQuery } = window
-
- if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
- return jQuery
+ if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
+ return window.jQuery
}
return null
@@ -246,10 +222,8 @@ const defineJQueryPlugin = plugin => {
})
}
-const execute = callback => {
- if (typeof callback === 'function') {
- callback()
- }
+const execute = (possibleCallback, args = [], defaultValue = possibleCallback) => {
+ return typeof possibleCallback === 'function' ? possibleCallback.call(...args) : defaultValue
}
const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {
@@ -291,15 +265,15 @@ const executeAfterTransition = (callback, transitionElement, waitForTransition =
* @return {Element|elem} The proper element
*/
const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {
+ const listLength = list.length
let index = list.indexOf(activeElement)
- // if the element does not exist in the list return an element depending on the direction and if cycle is allowed
+ // if the element does not exist in the list return an element
+ // depending on the direction and if cycle is allowed
if (index === -1) {
- return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]
+ return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0]
}
- const listLength = list.length
-
index += shouldGetNext ? 1 : -1
if (isCycleAllowed) {
@@ -315,10 +289,8 @@ export {
executeAfterTransition,
findShadowRoot,
getElement,
- getElementFromSelector,
getjQuery,
getNextActiveElement,
- getSelectorFromElement,
getTransitionDurationFromElement,
getUID,
isDisabled,
@@ -327,7 +299,8 @@ export {
isVisible,
noop,
onDOMContentLoaded,
+ parseSelector,
reflow,
triggerTransitionEnd,
- typeCheckConfig
+ toType
}
diff --git a/js/src/util/sanitizer.js b/js/src/util/sanitizer.js
index 5a7a68035..3d2883aff 100644
--- a/js/src/util/sanitizer.js
+++ b/js/src/util/sanitizer.js
@@ -1,53 +1,13 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/sanitizer.js
+ * Bootstrap util/sanitizer.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-const uriAttributes = new Set([
- 'background',
- 'cite',
- 'href',
- 'itemtype',
- 'longdesc',
- 'poster',
- 'src',
- 'xlink:href'
-])
-
+// js-docs-start allow-list
const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i
-/**
- * A pattern that recognizes a commonly useful subset of URLs that are safe.
- *
- * Shoutout to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts
- */
-const SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file|sms):|[^#&/:?]*(?:[#/?]|$))/i
-
-/**
- * A pattern that matches safe data URLs. Only matches image, video and audio types.
- *
- * Shoutout to Angular https://github.com/angular/angular/blob/12.2.x/packages/core/src/sanitization/url_sanitizer.ts
- */
-const DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[\d+/a-z]+=*$/i
-
-const allowedAttribute = (attribute, allowedAttributeList) => {
- const attributeName = attribute.nodeName.toLowerCase()
-
- if (allowedAttributeList.includes(attributeName)) {
- if (uriAttributes.has(attributeName)) {
- return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue) || DATA_URL_PATTERN.test(attribute.nodeValue))
- }
-
- return true
- }
-
- // Check if a regular expression validates the attribute.
- return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp)
- .some(regex => regex.test(attributeName))
-}
-
export const DefaultAllowlist = {
// Global attributes allowed on any supplied element below.
'*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN],
@@ -57,7 +17,10 @@ export const DefaultAllowlist = {
br: [],
col: [],
code: [],
+ dd: [],
div: [],
+ dl: [],
+ dt: [],
em: [],
hr: [],
h1: [],
@@ -81,14 +44,51 @@ export const DefaultAllowlist = {
u: [],
ul: []
}
+// js-docs-end allow-list
+
+const uriAttributes = new Set([
+ 'background',
+ 'cite',
+ 'href',
+ 'itemtype',
+ 'longdesc',
+ 'poster',
+ 'src',
+ 'xlink:href'
+])
+
+/**
+ * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation
+ * contexts.
+ *
+ * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38
+ */
+// eslint-disable-next-line unicorn/better-regex
+const SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i
+
+const allowedAttribute = (attribute, allowedAttributeList) => {
+ const attributeName = attribute.nodeName.toLowerCase()
+
+ if (allowedAttributeList.includes(attributeName)) {
+ if (uriAttributes.has(attributeName)) {
+ return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue))
+ }
+
+ return true
+ }
+
+ // Check if a regular expression validates the attribute.
+ return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp)
+ .some(regex => regex.test(attributeName))
+}
-export function sanitizeHtml(unsafeHtml, allowList, sanitizeFn) {
+export function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) {
if (!unsafeHtml.length) {
return unsafeHtml
}
- if (sanitizeFn && typeof sanitizeFn === 'function') {
- return sanitizeFn(unsafeHtml)
+ if (sanitizeFunction && typeof sanitizeFunction === 'function') {
+ return sanitizeFunction(unsafeHtml)
}
const domParser = new window.DOMParser()
@@ -100,7 +100,6 @@ export function sanitizeHtml(unsafeHtml, allowList, sanitizeFn) {
if (!Object.keys(allowList).includes(elementName)) {
element.remove()
-
continue
}
diff --git a/js/src/util/scrollbar.js b/js/src/util/scrollbar.js
index 55b7244ab..413f178da 100644
--- a/js/src/util/scrollbar.js
+++ b/js/src/util/scrollbar.js
@@ -1,13 +1,13 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/scrollBar.js
+ * Bootstrap util/scrollBar.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import SelectorEngine from '../dom/selector-engine'
-import Manipulator from '../dom/manipulator'
-import { isElement } from './index'
+import Manipulator from '../dom/manipulator.js'
+import SelectorEngine from '../dom/selector-engine.js'
+import { isElement } from './index.js'
/**
* Constants
@@ -15,6 +15,8 @@ import { isElement } from './index'
const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'
const SELECTOR_STICKY_CONTENT = '.sticky-top'
+const PROPERTY_PADDING = 'padding-right'
+const PROPERTY_MARGIN = 'margin-right'
/**
* Class definition
@@ -36,17 +38,17 @@ class ScrollBarHelper {
const width = this.getWidth()
this._disableOverFlow()
// give padding to element to balance the hidden scrollbar width
- this._setElementAttributes(this._element, 'paddingRight', calculatedValue => calculatedValue + width)
+ this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width)
// trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth
- this._setElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight', calculatedValue => calculatedValue + width)
- this._setElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight', calculatedValue => calculatedValue - width)
+ this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width)
+ this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width)
}
reset() {
this._resetElementAttributes(this._element, 'overflow')
- this._resetElementAttributes(this._element, 'paddingRight')
- this._resetElementAttributes(SELECTOR_FIXED_CONTENT, 'paddingRight')
- this._resetElementAttributes(SELECTOR_STICKY_CONTENT, 'marginRight')
+ this._resetElementAttributes(this._element, PROPERTY_PADDING)
+ this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING)
+ this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN)
}
isOverflowing() {
@@ -59,37 +61,39 @@ class ScrollBarHelper {
this._element.style.overflow = 'hidden'
}
- _setElementAttributes(selector, styleProp, callback) {
+ _setElementAttributes(selector, styleProperty, callback) {
const scrollbarWidth = this.getWidth()
const manipulationCallBack = element => {
if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) {
return
}
- this._saveInitialAttribute(element, styleProp)
- const calculatedValue = window.getComputedStyle(element)[styleProp]
- element.style[styleProp] = `${callback(Number.parseFloat(calculatedValue))}px`
+ this._saveInitialAttribute(element, styleProperty)
+ const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty)
+ element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`)
}
this._applyManipulationCallback(selector, manipulationCallBack)
}
- _saveInitialAttribute(element, styleProp) {
- const actualValue = element.style[styleProp]
+ _saveInitialAttribute(element, styleProperty) {
+ const actualValue = element.style.getPropertyValue(styleProperty)
if (actualValue) {
- Manipulator.setDataAttribute(element, styleProp, actualValue)
+ Manipulator.setDataAttribute(element, styleProperty, actualValue)
}
}
- _resetElementAttributes(selector, styleProp) {
+ _resetElementAttributes(selector, styleProperty) {
const manipulationCallBack = element => {
- const value = Manipulator.getDataAttribute(element, styleProp)
- if (typeof value === 'undefined') {
- element.style.removeProperty(styleProp)
- } else {
- Manipulator.removeDataAttribute(element, styleProp)
- element.style[styleProp] = value
+ const value = Manipulator.getDataAttribute(element, styleProperty)
+ // We only want to remove the property if the value is `null`; the value can also be zero
+ if (value === null) {
+ element.style.removeProperty(styleProperty)
+ return
}
+
+ Manipulator.removeDataAttribute(element, styleProperty)
+ element.style.setProperty(styleProperty, value)
}
this._applyManipulationCallback(selector, manipulationCallBack)
@@ -98,10 +102,11 @@ class ScrollBarHelper {
_applyManipulationCallback(selector, callBack) {
if (isElement(selector)) {
callBack(selector)
- } else {
- for (const sel of SelectorEngine.find(selector, this._element)) {
- callBack(sel)
- }
+ return
+ }
+
+ for (const sel of SelectorEngine.find(selector, this._element)) {
+ callBack(sel)
}
}
}
diff --git a/js/src/util/swipe.js b/js/src/util/swipe.js
index 87a5f7f5a..d2f708711 100644
--- a/js/src/util/swipe.js
+++ b/js/src/util/swipe.js
@@ -1,12 +1,13 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/swipe.js
+ * Bootstrap util/swipe.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import EventHandler from '../dom/event-handler'
-import { execute, typeCheckConfig } from './index'
+import EventHandler from '../dom/event-handler.js'
+import Config from './config.js'
+import { execute } from './index.js'
/**
* Constants
@@ -25,23 +26,24 @@ const CLASS_NAME_POINTER_EVENT = 'pointer-event'
const SWIPE_THRESHOLD = 40
const Default = {
+ endCallback: null,
leftCallback: null,
- rightCallback: null,
- endCallback: null
+ rightCallback: null
}
const DefaultType = {
+ endCallback: '(function|null)',
leftCallback: '(function|null)',
- rightCallback: '(function|null)',
- endCallback: '(function|null)'
+ rightCallback: '(function|null)'
}
/**
* Class definition
*/
-class Swipe {
+class Swipe extends Config {
constructor(element, config) {
+ super()
this._element = element
if (!element || !Swipe.isSupported()) {
@@ -54,6 +56,19 @@ class Swipe {
this._initEvents()
}
+ // Getters
+ static get Default() {
+ return Default
+ }
+
+ static get DefaultType() {
+ return DefaultType
+ }
+
+ static get NAME() {
+ return NAME
+ }
+
// Public
dispose() {
EventHandler.off(this._element, EVENT_KEY)
@@ -118,15 +133,6 @@ class Swipe {
}
}
- _getConfig(config) {
- config = {
- ...Default,
- ...(typeof config === 'object' ? config : {})
- }
- typeCheckConfig(NAME, config, DefaultType)
- return config
- }
-
_eventIsPointerPenTouch(event) {
return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH)
}
diff --git a/js/src/util/template-factory.js b/js/src/util/template-factory.js
index a9cee1086..6d1532b4d 100644
--- a/js/src/util/template-factory.js
+++ b/js/src/util/template-factory.js
@@ -1,13 +1,14 @@
/**
* --------------------------------------------------------------------------
- * Bootstrap (v5.1.3): util/template-factory.js
+ * Bootstrap util/template-factory.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
* --------------------------------------------------------------------------
*/
-import { DefaultAllowlist, sanitizeHtml } from './sanitizer'
-import { getElement, isElement, typeCheckConfig } from '../util/index'
-import SelectorEngine from '../dom/selector-engine'
+import SelectorEngine from '../dom/selector-engine.js'
+import Config from './config.js'
+import { DefaultAllowlist, sanitizeHtml } from './sanitizer.js'
+import { execute, getElement, isElement } from './index.js'
/**
* Constants
@@ -16,48 +17,53 @@ import SelectorEngine from '../dom/selector-engine'
const NAME = 'TemplateFactory'
const Default = {
- extraClass: '',
- template: '<div></div>',
+ allowList: DefaultAllowlist,
content: {}, // { selector : text , selector2 : text2 , }
+ extraClass: '',
html: false,
sanitize: true,
sanitizeFn: null,
- allowList: DefaultAllowlist
+ template: '<div></div>'
}
const DefaultType = {
- extraClass: '(string|function)',
- template: 'string',
+ allowList: 'object',
content: 'object',
+ extraClass: '(string|function)',
html: 'boolean',
sanitize: 'boolean',
sanitizeFn: '(null|function)',
- allowList: 'object'
+ template: 'string'
}
const DefaultContentType = {
- selector: '(string|element)',
- entry: '(string|element|function|null)'
+ entry: '(string|element|function|null)',
+ selector: '(string|element)'
}
/**
* Class definition
*/
-class TemplateFactory {
+class TemplateFactory extends Config {
constructor(config) {
+ super()
this._config = this._getConfig(config)
}
// Getters
- static get NAME() {
- return NAME
- }
-
static get Default() {
return Default
}
+ static get DefaultType() {
+ return DefaultType
+ }
+
+ static get NAME() {
+ return NAME
+ }
+
// Public
getContent() {
return Object.values(this._config.content)
@@ -94,21 +100,14 @@ class TemplateFactory {
}
// Private
- _getConfig(config) {
- config = {
- ...Default,
- ...(typeof config === 'object' ? config : {})
- }
-
- typeCheckConfig(NAME, config, DefaultType)
+ _typeCheckConfig(config) {
+ super._typeCheckConfig(config)
this._checkContent(config.content)
-
- return config
}
_checkContent(arg) {
for (const [selector, content] of Object.entries(arg)) {
- typeCheckConfig(NAME, { selector, entry: content }, DefaultContentType)
+ super._typeCheckConfig({ selector, entry: content }, DefaultContentType)
}
}
@@ -144,7 +143,7 @@ class TemplateFactory {
}
_resolvePossibleFunction(arg) {
- return typeof arg === 'function' ? arg(this) : arg
+ return execute(arg, [undefined, this])
}
_putElementInTemplate(element, templateElement) {
diff --git a/js/tests/README.md b/js/tests/README.md
index 1e96a6b5d..79d05d444 100644
--- a/js/tests/README.md
+++ b/js/tests/README.md
@@ -35,7 +35,7 @@ Currently we're aiming for at least 90% test coverage for our code. To ensure yo
describe('getInstance', () => {
it('should return null if there is no instance', () => {
// Make assertion
- expect(Tab.getInstance(fixtureEl)).toEqual(null)
+ expect(Tab.getInstance(fixtureEl)).toBeNull()
})
it('should return this instance', () => {
@@ -50,22 +50,24 @@ describe('getInstance', () => {
})
// Asynchronous test
-it('should show a tooltip without the animation', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
+it('should show a tooltip without the animation', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- animation: false
- })
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl, {
+ animation: false
+ })
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- const tip = document.querySelector('.tooltip')
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ const tip = document.querySelector('.tooltip')
- expect(tip).not.toBeNull()
- expect(tip.classList.contains('fade')).toEqual(false)
- done()
- })
+ expect(tip).not.toBeNull()
+ expect(tip.classList.contains('fade')).toEqual(false)
+ resolve()
+ })
- tooltip.show()
+ tooltip.show()
+ })
})
```
diff --git a/js/tests/browsers.js b/js/tests/browsers.js
index 665b26a2e..c515e64a0 100644
--- a/js/tests/browsers.js
+++ b/js/tests/browsers.js
@@ -1,6 +1,7 @@
-/* eslint-env node */
/* eslint-disable camelcase */
+'use strict'
+
const browsers = {
safariMac: {
base: 'BrowserStack',
@@ -74,9 +75,6 @@ const browsers = {
}
}
-const browsersKeys = Object.keys(browsers)
-
module.exports = {
- browsers,
- browsersKeys
+ browsers
}
diff --git a/js/tests/helpers/fixture.js b/js/tests/helpers/fixture.js
index 02915af44..5ad14e1db 100644
--- a/js/tests/helpers/fixture.js
+++ b/js/tests/helpers/fixture.js
@@ -1,41 +1,38 @@
const fixtureId = 'fixture'
export const getFixture = () => {
- let fixtureEl = document.getElementById(fixtureId)
-
- if (!fixtureEl) {
- fixtureEl = document.createElement('div')
- fixtureEl.setAttribute('id', fixtureId)
- fixtureEl.style.position = 'absolute'
- fixtureEl.style.top = '-10000px'
- fixtureEl.style.left = '-10000px'
- fixtureEl.style.width = '10000px'
- fixtureEl.style.height = '10000px'
- document.body.append(fixtureEl)
+ let fixtureElement = document.getElementById(fixtureId)
+
+ if (!fixtureElement) {
+ fixtureElement = document.createElement('div')
+ fixtureElement.setAttribute('id', fixtureId)
+ fixtureElement.style.position = 'absolute'
+ fixtureElement.style.top = '-10000px'
+ fixtureElement.style.left = '-10000px'
+ fixtureElement.style.width = '10000px'
+ fixtureElement.style.height = '10000px'
+ document.body.append(fixtureElement)
}
- return fixtureEl
+ return fixtureElement
}
export const clearFixture = () => {
- const fixtureEl = getFixture()
+ const fixtureElement = getFixture()
- fixtureEl.innerHTML = ''
+ fixtureElement.innerHTML = ''
}
-export const createEvent = (eventName, params = {}) => {
- const event = document.createEvent('Event')
-
- event.initEvent(eventName, Boolean(params.bubbles), Boolean(params.cancelable))
- return event
+export const createEvent = (eventName, parameters = {}) => {
+ return new Event(eventName, parameters)
}
export const jQueryMock = {
elements: undefined,
fn: {},
each(fn) {
- for (const el of this.elements) {
- fn.call(el)
+ for (const element of this.elements) {
+ fn.call(element)
}
}
}
@@ -43,8 +40,8 @@ export const jQueryMock = {
export const clearBodyAndDocument = () => {
const attributes = ['data-bs-padding-right', 'style']
- for (const attr of attributes) {
- document.documentElement.removeAttribute(attr)
- document.body.removeAttribute(attr)
+ for (const attribute of attributes) {
+ document.documentElement.removeAttribute(attribute)
+ document.body.removeAttribute(attribute)
}
}
diff --git a/js/tests/integration/bundle-modularity.js b/js/tests/integration/bundle-modularity.js
index 8546141b1..3c1eec944 100644
--- a/js/tests/integration/bundle-modularity.js
+++ b/js/tests/integration/bundle-modularity.js
@@ -1,3 +1,5 @@
+/* eslint-disable import/extensions, import/no-unassigned-import */
+
import Tooltip from '../../dist/tooltip'
import '../../dist/carousel'
diff --git a/js/tests/integration/rollup.bundle-modularity.js b/js/tests/integration/rollup.bundle-modularity.js
index a8670ca8c..63d651525 100644
--- a/js/tests/integration/rollup.bundle-modularity.js
+++ b/js/tests/integration/rollup.bundle-modularity.js
@@ -1,7 +1,7 @@
-/* eslint-env node */
+'use strict'
const commonjs = require('@rollup/plugin-commonjs')
-const configRollup = require('./rollup.bundle')
+const configRollup = require('./rollup.bundle.js')
const config = {
...configRollup,
diff --git a/js/tests/integration/rollup.bundle.js b/js/tests/integration/rollup.bundle.js
index caddcab48..8b3c578a2 100644
--- a/js/tests/integration/rollup.bundle.js
+++ b/js/tests/integration/rollup.bundle.js
@@ -1,4 +1,4 @@
-/* eslint-env node */
+'use strict'
const { babel } = require('@rollup/plugin-babel')
const { nodeResolve } = require('@rollup/plugin-node-resolve')
diff --git a/js/tests/karma.conf.js b/js/tests/karma.conf.js
index d85f2441c..36bf7f2df 100644
--- a/js/tests/karma.conf.js
+++ b/js/tests/karma.conf.js
@@ -1,18 +1,12 @@
-/* eslint-env node */
-
'use strict'
-const path = require('path')
+const path = require('node:path')
const ip = require('ip')
const { babel } = require('@rollup/plugin-babel')
const istanbul = require('rollup-plugin-istanbul')
const { nodeResolve } = require('@rollup/plugin-node-resolve')
const replace = require('@rollup/plugin-replace')
-
-const {
- browsers,
- browsersKeys
-} = require('./browsers')
+const { browsers } = require('./browsers.js')
const ENV = process.env
const BROWSERSTACK = Boolean(ENV.BROWSERSTACK)
@@ -54,7 +48,7 @@ const detectBrowsers = {
}
}
-const conf = {
+const config = {
basePath: '../..',
port: 9876,
colors: true,
@@ -105,17 +99,17 @@ const conf = {
}
if (BROWSERSTACK) {
- conf.hostname = ip.address()
- conf.browserStack = {
+ config.hostname = ip.address()
+ config.browserStack = {
username: ENV.BROWSER_STACK_USERNAME,
accessKey: ENV.BROWSER_STACK_ACCESS_KEY,
- build: `bootstrap-${ENV.GITHUB_SHA ? ENV.GITHUB_SHA.slice(0, 7) + '-' : ''}${new Date().toISOString()}`,
+ build: `bootstrap-${ENV.GITHUB_SHA ? `${ENV.GITHUB_SHA.slice(0, 7)}-` : ''}${new Date().toISOString()}`,
project: 'Bootstrap',
retryLimit: 2
}
plugins.push('karma-browserstack-launcher', 'karma-jasmine-html-reporter')
- conf.customLaunchers = browsers
- conf.browsers = browsersKeys
+ config.customLaunchers = browsers
+ config.browsers = Object.keys(browsers)
reporters.push('BrowserStack', 'kjhtml')
} else if (JQUERY_TEST) {
frameworks.push('detectBrowsers')
@@ -124,8 +118,8 @@ if (BROWSERSTACK) {
'karma-firefox-launcher',
'karma-detect-browsers'
)
- conf.detectBrowsers = detectBrowsers
- conf.files = [
+ config.detectBrowsers = detectBrowsers
+ config.files = [
'node_modules/jquery/dist/jquery.slim.min.js',
{
pattern: 'js/tests/unit/jquery.spec.js',
@@ -141,8 +135,8 @@ if (BROWSERSTACK) {
'karma-coverage-istanbul-reporter'
)
reporters.push('coverage-istanbul')
- conf.detectBrowsers = detectBrowsers
- conf.coverageIstanbulReporter = {
+ config.detectBrowsers = detectBrowsers
+ config.coverageIstanbulReporter = {
dir: path.resolve(__dirname, '../coverage/'),
reports: ['lcov', 'text-summary'],
thresholds: {
@@ -157,19 +151,19 @@ if (BROWSERSTACK) {
}
if (DEBUG) {
- conf.hostname = ip.address()
+ config.hostname = ip.address()
plugins.push('karma-jasmine-html-reporter')
reporters.push('kjhtml')
- conf.singleRun = false
- conf.autoWatch = true
+ config.singleRun = false
+ config.autoWatch = true
}
}
-conf.frameworks = frameworks
-conf.plugins = plugins
-conf.reporters = reporters
+config.frameworks = frameworks
+config.plugins = plugins
+config.reporters = reporters
module.exports = karmaConfig => {
- conf.logLevel = karmaConfig.LOG_ERROR
- karmaConfig.set(conf)
+ config.logLevel = karmaConfig.LOG_ERROR
+ karmaConfig.set(config)
}
diff --git a/js/tests/unit/.eslintrc.json b/js/tests/unit/.eslintrc.json
deleted file mode 100644
index 6362a1acf..000000000
--- a/js/tests/unit/.eslintrc.json
+++ /dev/null
@@ -1,13 +0,0 @@
-{
- "extends": [
- "../../../.eslintrc.json"
- ],
- "env": {
- "jasmine": true
- },
- "rules": {
- "unicorn/consistent-function-scoping": "off",
- "unicorn/no-useless-undefined": "off",
- "unicorn/prefer-add-event-listener": "off"
- }
-}
diff --git a/js/tests/unit/alert.spec.js b/js/tests/unit/alert.spec.js
index cdda997c9..97cc3cc53 100644
--- a/js/tests/unit/alert.spec.js
+++ b/js/tests/unit/alert.spec.js
@@ -1,6 +1,6 @@
-import Alert from '../../src/alert'
-import { getTransitionDurationFromElement } from '../../src/util/index'
-import { clearFixture, getFixture, jQueryMock } from '../helpers/fixture'
+import Alert from '../../src/alert.js'
+import { getTransitionDurationFromElement } from '../../src/util/index.js'
+import { clearFixture, getFixture, jQueryMock } from '../helpers/fixture.js'
describe('Alert', () => {
let fixtureEl
@@ -25,7 +25,7 @@ describe('Alert', () => {
})
it('should return version', () => {
- expect(typeof Alert.VERSION).toEqual('string')
+ expect(Alert.VERSION).toEqual(jasmine.any(String))
})
describe('DATA_KEY', () => {
@@ -45,7 +45,7 @@ describe('Alert', () => {
const button = document.querySelector('button')
button.click()
- expect(document.querySelectorAll('.alert').length).toEqual(0)
+ expect(document.querySelectorAll('.alert')).toHaveSize(0)
})
it('should close an alert without instantiating it manually with the parent selector', () => {
@@ -58,65 +58,71 @@ describe('Alert', () => {
const button = document.querySelector('button')
button.click()
- expect(document.querySelectorAll('.alert').length).toEqual(0)
+ expect(document.querySelectorAll('.alert')).toHaveSize(0)
})
})
describe('close', () => {
- it('should close an alert', done => {
- const spy = jasmine.createSpy('spy', getTransitionDurationFromElement)
- fixtureEl.innerHTML = '<div class="alert"></div>'
+ it('should close an alert', () => {
+ return new Promise(resolve => {
+ const spy = jasmine.createSpy('spy', getTransitionDurationFromElement)
+ fixtureEl.innerHTML = '<div class="alert"></div>'
- const alertEl = document.querySelector('.alert')
- const alert = new Alert(alertEl)
+ const alertEl = document.querySelector('.alert')
+ const alert = new Alert(alertEl)
- alertEl.addEventListener('closed.bs.alert', () => {
- expect(document.querySelectorAll('.alert').length).toEqual(0)
- expect(spy).not.toHaveBeenCalled()
- done()
- })
+ alertEl.addEventListener('closed.bs.alert', () => {
+ expect(document.querySelectorAll('.alert')).toHaveSize(0)
+ expect(spy).not.toHaveBeenCalled()
+ resolve()
+ })
- alert.close()
+ alert.close()
+ })
})
- it('should close alert with fade class', done => {
- fixtureEl.innerHTML = '<div class="alert fade"></div>'
+ it('should close alert with fade class', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="alert fade"></div>'
- const alertEl = document.querySelector('.alert')
- const alert = new Alert(alertEl)
+ const alertEl = document.querySelector('.alert')
+ const alert = new Alert(alertEl)
- alertEl.addEventListener('transitionend', () => {
- expect().nothing()
- })
+ alertEl.addEventListener('transitionend', () => {
+ expect().nothing()
+ })
- alertEl.addEventListener('closed.bs.alert', () => {
- expect(document.querySelectorAll('.alert').length).toEqual(0)
- done()
- })
+ alertEl.addEventListener('closed.bs.alert', () => {
+ expect(document.querySelectorAll('.alert')).toHaveSize(0)
+ resolve()
+ })
- alert.close()
+ alert.close()
+ })
})
- it('should not remove alert if close event is prevented', done => {
- fixtureEl.innerHTML = '<div class="alert"></div>'
+ it('should not remove alert if close event is prevented', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = '<div class="alert"></div>'
- const getAlert = () => document.querySelector('.alert')
- const alertEl = getAlert()
- const alert = new Alert(alertEl)
+ const getAlert = () => document.querySelector('.alert')
+ const alertEl = getAlert()
+ const alert = new Alert(alertEl)
- alertEl.addEventListener('close.bs.alert', event => {
- event.preventDefault()
- setTimeout(() => {
- expect(getAlert()).not.toBeNull()
- done()
- }, 10)
- })
+ alertEl.addEventListener('close.bs.alert', event => {
+ event.preventDefault()
+ setTimeout(() => {
+ expect(getAlert()).not.toBeNull()
+ resolve()
+ }, 10)
+ })
- alertEl.addEventListener('closed.bs.alert', () => {
- throw new Error('should not fire closed event')
- })
+ alertEl.addEventListener('closed.bs.alert', () => {
+ reject(new Error('should not fire closed event'))
+ })
- alert.close()
+ alert.close()
+ })
})
})
@@ -142,14 +148,14 @@ describe('Alert', () => {
const alertEl = fixtureEl.querySelector('.alert')
const alert = new Alert(alertEl)
- spyOn(alert, 'close')
+ const spy = spyOn(alert, 'close')
jQueryMock.fn.alert = Alert.jQueryInterface
jQueryMock.elements = [alertEl]
jQueryMock.fn.alert.call(jQueryMock, 'close')
- expect(alert.close).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
})
it('should create new alert instance and call close', () => {
@@ -179,6 +185,34 @@ describe('Alert', () => {
expect(Alert.getInstance(alertEl)).not.toBeNull()
expect(fixtureEl.querySelector('.alert')).not.toBeNull()
})
+
+ it('should throw an error on undefined method', () => {
+ fixtureEl.innerHTML = '<div></div>'
+
+ const div = fixtureEl.querySelector('div')
+ const action = 'undefinedMethod'
+
+ jQueryMock.fn.alert = Alert.jQueryInterface
+ jQueryMock.elements = [div]
+
+ expect(() => {
+ jQueryMock.fn.alert.call(jQueryMock, action)
+ }).toThrowError(TypeError, `No method named "${action}"`)
+ })
+
+ it('should throw an error on protected method', () => {
+ fixtureEl.innerHTML = '<div></div>'
+
+ const div = fixtureEl.querySelector('div')
+ const action = '_getConfig'
+
+ jQueryMock.fn.alert = Alert.jQueryInterface
+ jQueryMock.elements = [div]
+
+ expect(() => {
+ jQueryMock.fn.alert.call(jQueryMock, action)
+ }).toThrowError(TypeError, `No method named "${action}"`)
+ })
})
describe('getInstance', () => {
@@ -197,7 +231,7 @@ describe('Alert', () => {
const div = fixtureEl.querySelector('div')
- expect(Alert.getInstance(div)).toEqual(null)
+ expect(Alert.getInstance(div)).toBeNull()
})
})
@@ -218,7 +252,7 @@ describe('Alert', () => {
const div = fixtureEl.querySelector('div')
- expect(Alert.getInstance(div)).toEqual(null)
+ expect(Alert.getInstance(div)).toBeNull()
expect(Alert.getOrCreateInstance(div)).toBeInstanceOf(Alert)
})
})
diff --git a/js/tests/unit/base-component.spec.js b/js/tests/unit/base-component.spec.js
index b8ec83f12..5b7d52e23 100644
--- a/js/tests/unit/base-component.spec.js
+++ b/js/tests/unit/base-component.spec.js
@@ -1,7 +1,7 @@
-import BaseComponent from '../../src/base-component'
-import { clearFixture, getFixture } from '../helpers/fixture'
-import EventHandler from '../../src/dom/event-handler'
-import { noop } from '../../src/util'
+import BaseComponent from '../../src/base-component.js'
+import EventHandler from '../../src/dom/event-handler.js'
+import { noop } from '../../src/util/index.js'
+import { clearFixture, getFixture } from '../helpers/fixture.js'
class DummyClass extends BaseComponent {
constructor(element) {
@@ -37,7 +37,7 @@ describe('Base Component', () => {
describe('Static Methods', () => {
describe('VERSION', () => {
it('should return version', () => {
- expect(typeof DummyClass.VERSION).toEqual('string')
+ expect(DummyClass.VERSION).toEqual(jasmine.any(String))
})
})
@@ -48,6 +48,13 @@ describe('Base Component', () => {
})
describe('NAME', () => {
+ it('should throw an Error if it is not initialized', () => {
+ expect(() => {
+ // eslint-disable-next-line no-unused-expressions
+ BaseComponent.NAME
+ }).toThrowError(Error)
+ })
+
it('should return plugin NAME', () => {
expect(DummyClass.NAME).toEqual(name)
})
@@ -59,6 +66,7 @@ describe('Base Component', () => {
})
})
})
+
describe('Public Methods', () => {
describe('constructor', () => {
it('should accept element, either passed as a CSS selector or DOM element', () => {
@@ -74,7 +82,19 @@ describe('Base Component', () => {
expect(elInstance._element).toEqual(el)
expect(selectorInstance._element).toEqual(fixtureEl.querySelector('#bar'))
})
+
+ it('should not initialize and add element record to Data (caching), if argument `element` is not an HTML element', () => {
+ fixtureEl.innerHTML = ''
+
+ const el = fixtureEl.querySelector('#foo')
+ const elInstance = new DummyClass(el)
+ const selectorInstance = new DummyClass('#bar')
+
+ expect(elInstance._element).not.toBeDefined()
+ expect(selectorInstance._element).not.toBeDefined()
+ })
})
+
describe('dispose', () => {
it('should dispose an component', () => {
createInstance()
@@ -88,11 +108,11 @@ describe('Base Component', () => {
it('should de-register element event listeners', () => {
createInstance()
- spyOn(EventHandler, 'off')
+ const spy = spyOn(EventHandler, 'off')
instance.dispose()
- expect(EventHandler.off).toHaveBeenCalledWith(element, DummyClass.EVENT_KEY)
+ expect(spy).toHaveBeenCalledWith(element, DummyClass.EVENT_KEY)
})
})
@@ -123,9 +143,10 @@ describe('Base Component', () => {
const div = fixtureEl.querySelector('div')
- expect(DummyClass.getInstance(div)).toEqual(null)
+ expect(DummyClass.getInstance(div)).toBeNull()
})
})
+
describe('getOrCreateInstance', () => {
it('should return an instance', () => {
createInstance()
@@ -139,7 +160,7 @@ describe('Base Component', () => {
fixtureEl.innerHTML = '<div id="foo"></div>'
element = fixtureEl.querySelector('#foo')
- expect(DummyClass.getInstance(element)).toEqual(null)
+ expect(DummyClass.getInstance(element)).toBeNull()
expect(DummyClass.getOrCreateInstance(element)).toBeInstanceOf(DummyClass)
})
})
diff --git a/js/tests/unit/button.spec.js b/js/tests/unit/button.spec.js
index e24ff5cb0..6624fee7c 100644
--- a/js/tests/unit/button.spec.js
+++ b/js/tests/unit/button.spec.js
@@ -1,5 +1,5 @@
-import Button from '../../src/button'
-import { getFixture, clearFixture, jQueryMock } from '../helpers/fixture'
+import Button from '../../src/button.js'
+import { clearFixture, getFixture, jQueryMock } from '../helpers/fixture.js'
describe('Button', () => {
let fixtureEl
@@ -45,19 +45,19 @@ describe('Button', () => {
const divTest = fixtureEl.querySelector('.test')
const btnTestParent = fixtureEl.querySelector('.testParent')
- expect(btn.classList.contains('active')).toEqual(false)
+ expect(btn).not.toHaveClass('active')
btn.click()
- expect(btn.classList.contains('active')).toEqual(true)
+ expect(btn).toHaveClass('active')
btn.click()
- expect(btn.classList.contains('active')).toEqual(false)
+ expect(btn).not.toHaveClass('active')
divTest.click()
- expect(btnTestParent.classList.contains('active')).toEqual(true)
+ expect(btnTestParent).toHaveClass('active')
})
})
@@ -69,12 +69,12 @@ describe('Button', () => {
const button = new Button(btnEl)
expect(btnEl.getAttribute('aria-pressed')).toEqual('false')
- expect(btnEl.classList.contains('active')).toEqual(false)
+ expect(btnEl).not.toHaveClass('active')
button.toggle()
expect(btnEl.getAttribute('aria-pressed')).toEqual('true')
- expect(btnEl.classList.contains('active')).toEqual(true)
+ expect(btnEl).toHaveClass('active')
})
})
@@ -100,14 +100,14 @@ describe('Button', () => {
const btnEl = fixtureEl.querySelector('.btn')
const button = new Button(btnEl)
- spyOn(button, 'toggle')
+ const spy = spyOn(button, 'toggle')
jQueryMock.fn.button = Button.jQueryInterface
jQueryMock.elements = [btnEl]
jQueryMock.fn.button.call(jQueryMock, 'toggle')
- expect(button.toggle).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
})
it('should create new button instance and call toggle', () => {
@@ -121,7 +121,7 @@ describe('Button', () => {
jQueryMock.fn.button.call(jQueryMock, 'toggle')
expect(Button.getInstance(btnEl)).not.toBeNull()
- expect(btnEl.classList.contains('active')).toEqual(true)
+ expect(btnEl).toHaveClass('active')
})
it('should just create a button instance without calling toggle', () => {
@@ -135,7 +135,7 @@ describe('Button', () => {
jQueryMock.fn.button.call(jQueryMock)
expect(Button.getInstance(btnEl)).not.toBeNull()
- expect(btnEl.classList.contains('active')).toEqual(false)
+ expect(btnEl).not.toHaveClass('active')
})
})
@@ -155,7 +155,7 @@ describe('Button', () => {
const div = fixtureEl.querySelector('div')
- expect(Button.getInstance(div)).toEqual(null)
+ expect(Button.getInstance(div)).toBeNull()
})
})
@@ -176,7 +176,7 @@ describe('Button', () => {
const div = fixtureEl.querySelector('div')
- expect(Button.getInstance(div)).toEqual(null)
+ expect(Button.getInstance(div)).toBeNull()
expect(Button.getOrCreateInstance(div)).toBeInstanceOf(Button)
})
})
diff --git a/js/tests/unit/carousel.spec.js b/js/tests/unit/carousel.spec.js
index a138f3ad5..2960eb5ce 100644
--- a/js/tests/unit/carousel.spec.js
+++ b/js/tests/unit/carousel.spec.js
@@ -1,8 +1,10 @@
-import Carousel from '../../src/carousel'
-import EventHandler from '../../src/dom/event-handler'
-import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
-import { isRTL, noop } from '../../src/util/index'
-import Swipe from '../../src/util/swipe'
+import Carousel from '../../src/carousel.js'
+import EventHandler from '../../src/dom/event-handler.js'
+import { isRTL, noop } from '../../src/util/index.js'
+import Swipe from '../../src/util/swipe.js'
+import {
+ clearFixture, createEvent, getFixture, jQueryMock
+} from '../helpers/fixture.js'
describe('Carousel', () => {
const { Simulator, PointerEvent } = window
@@ -63,94 +65,148 @@ describe('Carousel', () => {
expect(carouselByElement._element).toEqual(carouselEl)
})
- it('should go to next item if right arrow key is pressed', done => {
- fixtureEl.innerHTML = [
- '<div id="myCarousel" class="carousel slide">',
- ' <div class="carousel-inner">',
- ' <div class="carousel-item active">item 1</div>',
- ' <div id="item2" class="carousel-item">item 2</div>',
- ' <div class="carousel-item">item 3</div>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should start cycling if `ride`===`carousel`', () => {
+ fixtureEl.innerHTML = '<div id="myCarousel" class="carousel slide" data-bs-ride="carousel"></div>'
- const carouselEl = fixtureEl.querySelector('#myCarousel')
- const carousel = new Carousel(carouselEl, {
- keyboard: true
- })
+ const carousel = new Carousel('#myCarousel')
+ expect(carousel._interval).not.toBeNull()
+ })
- spyOn(carousel, '_keydown').and.callThrough()
+ it('should not start cycling if `ride`!==`carousel`', () => {
+ fixtureEl.innerHTML = '<div id="myCarousel" class="carousel slide" data-bs-ride="true"></div>'
- carouselEl.addEventListener('slid.bs.carousel', () => {
- expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item2'))
- expect(carousel._keydown).toHaveBeenCalled()
- done()
- })
+ const carousel = new Carousel('#myCarousel')
+ expect(carousel._interval).toBeNull()
+ })
- const keydown = createEvent('keydown')
- keydown.key = 'ArrowRight'
+ it('should go to next item if right arrow key is pressed', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="myCarousel" class="carousel slide">',
+ ' <div class="carousel-inner">',
+ ' <div class="carousel-item active">item 1</div>',
+ ' <div id="item2" class="carousel-item">item 2</div>',
+ ' <div class="carousel-item">item 3</div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const carouselEl = fixtureEl.querySelector('#myCarousel')
+ const carousel = new Carousel(carouselEl, {
+ keyboard: true
+ })
+
+ const spy = spyOn(carousel, '_keydown').and.callThrough()
+
+ carouselEl.addEventListener('slid.bs.carousel', () => {
+ expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item2'))
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- carouselEl.dispatchEvent(keydown)
+ const keydown = createEvent('keydown')
+ keydown.key = 'ArrowRight'
+
+ carouselEl.dispatchEvent(keydown)
+ })
})
- it('should go to previous item if left arrow key is pressed', done => {
+ it('should ignore keyboard events if data-bs-keyboard=false', () => {
fixtureEl.innerHTML = [
- '<div id="myCarousel" class="carousel slide">',
+ '<div id="myCarousel" class="carousel slide" data-bs-keyboard="false">',
' <div class="carousel-inner">',
- ' <div id="item1" class="carousel-item">item 1</div>',
- ' <div class="carousel-item active">item 2</div>',
- ' <div class="carousel-item">item 3</div>',
+ ' <div class="carousel-item active">item 1</div>',
+ ' <div id="item2" class="carousel-item">item 2</div>',
' </div>',
'</div>'
].join('')
+ const spy = spyOn(EventHandler, 'trigger').and.callThrough()
const carouselEl = fixtureEl.querySelector('#myCarousel')
- const carousel = new Carousel(carouselEl, {
- keyboard: true
- })
-
- spyOn(carousel, '_keydown').and.callThrough()
-
- carouselEl.addEventListener('slid.bs.carousel', () => {
- expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item1'))
- expect(carousel._keydown).toHaveBeenCalled()
- done()
- })
-
- const keydown = createEvent('keydown')
- keydown.key = 'ArrowLeft'
-
- carouselEl.dispatchEvent(keydown)
+ // eslint-disable-next-line no-new
+ new Carousel('#myCarousel')
+ expect(spy).not.toHaveBeenCalledWith(carouselEl, 'keydown.bs.carousel', jasmine.any(Function))
})
- it('should not prevent keydown if key is not ARROW_LEFT or ARROW_RIGHT', done => {
+ it('should ignore mouse events if data-bs-pause=false', () => {
fixtureEl.innerHTML = [
- '<div id="myCarousel" class="carousel slide">',
+ '<div id="myCarousel" class="carousel slide" data-bs-pause="false">',
' <div class="carousel-inner">',
' <div class="carousel-item active">item 1</div>',
- ' <div class="carousel-item">item 2</div>',
- ' <div class="carousel-item">item 3</div>',
+ ' <div id="item2" class="carousel-item">item 2</div>',
' </div>',
'</div>'
].join('')
+ const spy = spyOn(EventHandler, 'trigger').and.callThrough()
const carouselEl = fixtureEl.querySelector('#myCarousel')
- const carousel = new Carousel(carouselEl, {
- keyboard: true
- })
+ // eslint-disable-next-line no-new
+ new Carousel('#myCarousel')
+ expect(spy).not.toHaveBeenCalledWith(carouselEl, 'hover.bs.carousel', jasmine.any(Function))
+ })
+
+ it('should go to previous item if left arrow key is pressed', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="myCarousel" class="carousel slide">',
+ ' <div class="carousel-inner">',
+ ' <div id="item1" class="carousel-item">item 1</div>',
+ ' <div class="carousel-item active">item 2</div>',
+ ' <div class="carousel-item">item 3</div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const carouselEl = fixtureEl.querySelector('#myCarousel')
+ const carousel = new Carousel(carouselEl, {
+ keyboard: true
+ })
+
+ const spy = spyOn(carousel, '_keydown').and.callThrough()
+
+ carouselEl.addEventListener('slid.bs.carousel', () => {
+ expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item1'))
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- spyOn(carousel, '_keydown').and.callThrough()
+ const keydown = createEvent('keydown')
+ keydown.key = 'ArrowLeft'
- carouselEl.addEventListener('keydown', event => {
- expect(carousel._keydown).toHaveBeenCalled()
- expect(event.defaultPrevented).toEqual(false)
- done()
+ carouselEl.dispatchEvent(keydown)
})
+ })
+
+ it('should not prevent keydown if key is not ARROW_LEFT or ARROW_RIGHT', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="myCarousel" class="carousel slide">',
+ ' <div class="carousel-inner">',
+ ' <div class="carousel-item active">item 1</div>',
+ ' <div class="carousel-item">item 2</div>',
+ ' <div class="carousel-item">item 3</div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const carouselEl = fixtureEl.querySelector('#myCarousel')
+ const carousel = new Carousel(carouselEl, {
+ keyboard: true
+ })
+
+ const spy = spyOn(carousel, '_keydown').and.callThrough()
+
+ carouselEl.addEventListener('keydown', event => {
+ expect(spy).toHaveBeenCalled()
+ expect(event.defaultPrevented).toBeFalse()
+ resolve()
+ })
- const keydown = createEvent('keydown')
- keydown.key = 'ArrowDown'
+ const keydown = createEvent('keydown')
+ keydown.key = 'ArrowDown'
- carouselEl.dispatchEvent(keydown)
+ carouselEl.dispatchEvent(keydown)
+ })
})
it('should ignore keyboard events within <input>s and <textarea>s', () => {
@@ -208,7 +264,7 @@ describe('Carousel', () => {
const carouselEl = fixtureEl.querySelector('div')
const carousel = new Carousel(carouselEl, {})
- spyOn(carousel, '_triggerSlideEvent')
+ const spy = spyOn(EventHandler, 'trigger')
carousel._isSliding = true
@@ -219,75 +275,77 @@ describe('Carousel', () => {
carouselEl.dispatchEvent(keydown)
}
- expect(carousel._triggerSlideEvent).not.toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
})
- it('should wrap around from end to start when wrap option is true', done => {
- fixtureEl.innerHTML = [
- '<div id="myCarousel" class="carousel slide">',
- ' <div class="carousel-inner">',
- ' <div id="one" class="carousel-item active"></div>',
- ' <div id="two" class="carousel-item"></div>',
- ' <div id="three" class="carousel-item">item 3</div>',
- ' </div>',
- '</div>'
- ].join('')
-
- const carouselEl = fixtureEl.querySelector('#myCarousel')
- const carousel = new Carousel(carouselEl, { wrap: true })
- const getActiveId = () => {
- return carouselEl.querySelector('.carousel-item.active').getAttribute('id')
- }
-
- carouselEl.addEventListener('slid.bs.carousel', event => {
- const activeId = getActiveId()
-
- if (activeId === 'two') {
- carousel.next()
- return
- }
-
- if (activeId === 'three') {
- carousel.next()
- return
- }
-
- if (activeId === 'one') {
- // carousel wrapped around and slid from 3rd to 1st slide
- expect(activeId).toEqual('one')
- expect(event.from + 1).toEqual(3)
- done()
- }
+ it('should wrap around from end to start when wrap option is true', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="myCarousel" class="carousel slide">',
+ ' <div class="carousel-inner">',
+ ' <div id="one" class="carousel-item active"></div>',
+ ' <div id="two" class="carousel-item"></div>',
+ ' <div id="three" class="carousel-item">item 3</div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const carouselEl = fixtureEl.querySelector('#myCarousel')
+ const carousel = new Carousel(carouselEl, { wrap: true })
+ const getActiveId = () => carouselEl.querySelector('.carousel-item.active').getAttribute('id')
+
+ carouselEl.addEventListener('slid.bs.carousel', event => {
+ const activeId = getActiveId()
+
+ if (activeId === 'two') {
+ carousel.next()
+ return
+ }
+
+ if (activeId === 'three') {
+ carousel.next()
+ return
+ }
+
+ if (activeId === 'one') {
+ // carousel wrapped around and slid from 3rd to 1st slide
+ expect(activeId).toEqual('one')
+ expect(event.from + 1).toEqual(3)
+ resolve()
+ }
+ })
+
+ carousel.next()
})
-
- carousel.next()
})
- it('should stay at the start when the prev method is called and wrap is false', done => {
- fixtureEl.innerHTML = [
- '<div id="myCarousel" class="carousel slide">',
- ' <div class="carousel-inner">',
- ' <div id="one" class="carousel-item active"></div>',
- ' <div id="two" class="carousel-item"></div>',
- ' <div id="three" class="carousel-item">item 3</div>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should stay at the start when the prev method is called and wrap is false', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<div id="myCarousel" class="carousel slide">',
+ ' <div class="carousel-inner">',
+ ' <div id="one" class="carousel-item active"></div>',
+ ' <div id="two" class="carousel-item"></div>',
+ ' <div id="three" class="carousel-item">item 3</div>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const carouselEl = fixtureEl.querySelector('#myCarousel')
- const firstElement = fixtureEl.querySelector('#one')
- const carousel = new Carousel(carouselEl, { wrap: false })
+ const carouselEl = fixtureEl.querySelector('#myCarousel')
+ const firstElement = fixtureEl.querySelector('#one')
+ const carousel = new Carousel(carouselEl, { wrap: false })
- carouselEl.addEventListener('slid.bs.carousel', () => {
- throw new Error('carousel slid when it should not have slid')
- })
+ carouselEl.addEventListener('slid.bs.carousel', () => {
+ reject(new Error('carousel slid when it should not have slid'))
+ })
- carousel.prev()
+ carousel.prev()
- setTimeout(() => {
- expect(firstElement.classList.contains('active')).toEqual(true)
- done()
- }, 10)
+ setTimeout(() => {
+ expect(firstElement).toHaveClass('active')
+ resolve()
+ }, 10)
+ })
})
it('should not add touch event listeners if touch = false', () => {
@@ -295,13 +353,13 @@ describe('Carousel', () => {
const carouselEl = fixtureEl.querySelector('div')
- spyOn(Carousel.prototype, '_addTouchEventListeners')
+ const spy = spyOn(Carousel.prototype, '_addTouchEventListeners')
const carousel = new Carousel(carouselEl, {
touch: false
})
- expect(carousel._addTouchEventListeners).not.toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
expect(carousel._swipeHelper).toBeNull()
})
@@ -314,11 +372,11 @@ describe('Carousel', () => {
const carousel = new Carousel(carouselEl)
EventHandler.off(carouselEl, Carousel.EVENT_KEY)
- spyOn(carousel, '_addTouchEventListeners')
+ const spy = spyOn(carousel, '_addTouchEventListeners')
carousel._addEventListeners()
- expect(carousel._addTouchEventListeners).not.toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
expect(carousel._swipeHelper).toBeNull()
})
@@ -337,274 +395,292 @@ describe('Carousel', () => {
expect(carousel._addTouchEventListeners).toHaveBeenCalled()
})
- it('should allow swiperight and call _slide (prev) with pointer events', done => {
- if (!supportPointerEvent) {
- expect().nothing()
- done()
- return
- }
-
- document.documentElement.ontouchstart = noop
- document.head.append(stylesCarousel)
- Simulator.setType('pointer')
-
- fixtureEl.innerHTML = [
- '<div class="carousel" data-bs-interval="false">',
- ' <div class="carousel-inner">',
- ' <div id="item" class="carousel-item">',
- ' <img alt="">',
- ' </div>',
- ' <div class="carousel-item active">',
- ' <img alt="">',
- ' </div>',
- ' </div>',
- '</div>'
- ].join('')
-
- const carouselEl = fixtureEl.querySelector('.carousel')
- const item = fixtureEl.querySelector('#item')
- const carousel = new Carousel(carouselEl)
-
- spyOn(carousel, '_slide').and.callThrough()
-
- carouselEl.addEventListener('slid.bs.carousel', event => {
- expect(item.classList.contains('active')).toEqual(true)
- expect(carousel._slide).toHaveBeenCalledWith('right')
- expect(event.direction).toEqual('right')
- stylesCarousel.remove()
- delete document.documentElement.ontouchstart
- done()
- })
+ it('should allow swiperight and call _slide (prev) with pointer events', () => {
+ return new Promise(resolve => {
+ if (!supportPointerEvent) {
+ expect().nothing()
+ resolve()
+ return
+ }
- Simulator.gestures.swipe(carouselEl, {
- deltaX: 300,
- deltaY: 0
+ document.documentElement.ontouchstart = noop
+ document.head.append(stylesCarousel)
+ Simulator.setType('pointer')
+
+ fixtureEl.innerHTML = [
+ '<div class="carousel">',
+ ' <div class="carousel-inner">',
+ ' <div id="item" class="carousel-item">',
+ ' <img alt="">',
+ ' </div>',
+ ' <div class="carousel-item active">',
+ ' <img alt="">',
+ ' </div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const carouselEl = fixtureEl.querySelector('.carousel')
+ const item = fixtureEl.querySelector('#item')
+ const carousel = new Carousel(carouselEl)
+
+ const spy = spyOn(carousel, '_slide').and.callThrough()
+
+ carouselEl.addEventListener('slid.bs.carousel', event => {
+ expect(item).toHaveClass('active')
+ expect(spy).toHaveBeenCalledWith('prev')
+ expect(event.direction).toEqual('right')
+ stylesCarousel.remove()
+ delete document.documentElement.ontouchstart
+ resolve()
+ })
+
+ Simulator.gestures.swipe(carouselEl, {
+ deltaX: 300,
+ deltaY: 0
+ })
})
})
- it('should allow swipeleft and call next with pointer events', done => {
- if (!supportPointerEvent) {
- expect().nothing()
- done()
- return
- }
-
- document.documentElement.ontouchstart = noop
- document.head.append(stylesCarousel)
- Simulator.setType('pointer')
-
- fixtureEl.innerHTML = [
- '<div class="carousel" data-bs-interval="false">',
- ' <div class="carousel-inner">',
- ' <div id="item" class="carousel-item active">',
- ' <img alt="">',
- ' </div>',
- ' <div class="carousel-item">',
- ' <img alt="">',
- ' </div>',
- ' </div>',
- '</div>'
- ].join('')
-
- const carouselEl = fixtureEl.querySelector('.carousel')
- const item = fixtureEl.querySelector('#item')
- const carousel = new Carousel(carouselEl)
-
- spyOn(carousel, '_slide').and.callThrough()
-
- carouselEl.addEventListener('slid.bs.carousel', event => {
- expect(item.classList.contains('active')).toEqual(false)
- expect(carousel._slide).toHaveBeenCalledWith('left')
- expect(event.direction).toEqual('left')
- stylesCarousel.remove()
- delete document.documentElement.ontouchstart
- done()
- })
+ it('should allow swipeleft and call next with pointer events', () => {
+ return new Promise(resolve => {
+ if (!supportPointerEvent) {
+ expect().nothing()
+ resolve()
+ return
+ }
- Simulator.gestures.swipe(carouselEl, {
- pos: [300, 10],
- deltaX: -300,
- deltaY: 0
+ document.documentElement.ontouchstart = noop
+ document.head.append(stylesCarousel)
+ Simulator.setType('pointer')
+
+ fixtureEl.innerHTML = [
+ '<div class="carousel">',
+ ' <div class="carousel-inner">',
+ ' <div id="item" class="carousel-item active">',
+ ' <img alt="">',
+ ' </div>',
+ ' <div class="carousel-item">',
+ ' <img alt="">',
+ ' </div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const carouselEl = fixtureEl.querySelector('.carousel')
+ const item = fixtureEl.querySelector('#item')
+ const carousel = new Carousel(carouselEl)
+
+ const spy = spyOn(carousel, '_slide').and.callThrough()
+
+ carouselEl.addEventListener('slid.bs.carousel', event => {
+ expect(item).not.toHaveClass('active')
+ expect(spy).toHaveBeenCalledWith('next')
+ expect(event.direction).toEqual('left')
+ stylesCarousel.remove()
+ delete document.documentElement.ontouchstart
+ resolve()
+ })
+
+ Simulator.gestures.swipe(carouselEl, {
+ pos: [300, 10],
+ deltaX: -300,
+ deltaY: 0
+ })
})
})
- it('should allow swiperight and call _slide (prev) with touch events', done => {
- Simulator.setType('touch')
- clearPointerEvents()
- document.documentElement.ontouchstart = noop
-
- fixtureEl.innerHTML = [
- '<div class="carousel" data-bs-interval="false">',
- ' <div class="carousel-inner">',
- ' <div id="item" class="carousel-item">',
- ' <img alt="">',
- ' </div>',
- ' <div class="carousel-item active">',
- ' <img alt="">',
- ' </div>',
- ' </div>',
- '</div>'
- ].join('')
-
- const carouselEl = fixtureEl.querySelector('.carousel')
- const item = fixtureEl.querySelector('#item')
- const carousel = new Carousel(carouselEl)
-
- spyOn(carousel, '_slide').and.callThrough()
-
- carouselEl.addEventListener('slid.bs.carousel', event => {
- expect(item.classList.contains('active')).toEqual(true)
- expect(carousel._slide).toHaveBeenCalledWith('right')
- expect(event.direction).toEqual('right')
- delete document.documentElement.ontouchstart
- restorePointerEvents()
- done()
- })
-
- Simulator.gestures.swipe(carouselEl, {
- deltaX: 300,
- deltaY: 0
+ it('should allow swiperight and call _slide (prev) with touch events', () => {
+ return new Promise(resolve => {
+ Simulator.setType('touch')
+ clearPointerEvents()
+ document.documentElement.ontouchstart = noop
+
+ fixtureEl.innerHTML = [
+ '<div class="carousel">',
+ ' <div class="carousel-inner">',
+ ' <div id="item" class="carousel-item">',
+ ' <img alt="">',
+ ' </div>',
+ ' <div class="carousel-item active">',
+ ' <img alt="">',
+ ' </div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const carouselEl = fixtureEl.querySelector('.carousel')
+ const item = fixtureEl.querySelector('#item')
+ const carousel = new Carousel(carouselEl)
+
+ const spy = spyOn(carousel, '_slide').and.callThrough()
+
+ carouselEl.addEventListener('slid.bs.carousel', event => {
+ expect(item).toHaveClass('active')
+ expect(spy).toHaveBeenCalledWith('prev')
+ expect(event.direction).toEqual('right')
+ delete document.documentElement.ontouchstart
+ restorePointerEvents()
+ resolve()
+ })
+
+ Simulator.gestures.swipe(carouselEl, {
+ deltaX: 300,
+ deltaY: 0
+ })
})
})
- it('should allow swipeleft and call _slide (next) with touch events', done => {
- Simulator.setType('touch')
- clearPointerEvents()
- document.documentElement.ontouchstart = noop
-
- fixtureEl.innerHTML = [
- '<div class="carousel" data-bs-interval="false">',
- ' <div class="carousel-inner">',
- ' <div id="item" class="carousel-item active">',
- ' <img alt="">',
- ' </div>',
- ' <div class="carousel-item">',
- ' <img alt="">',
- ' </div>',
- ' </div>',
- '</div>'
- ].join('')
-
- const carouselEl = fixtureEl.querySelector('.carousel')
- const item = fixtureEl.querySelector('#item')
- const carousel = new Carousel(carouselEl)
-
- spyOn(carousel, '_slide').and.callThrough()
-
- carouselEl.addEventListener('slid.bs.carousel', event => {
- expect(item.classList.contains('active')).toEqual(false)
- expect(carousel._slide).toHaveBeenCalledWith('left')
- expect(event.direction).toEqual('left')
- delete document.documentElement.ontouchstart
- restorePointerEvents()
- done()
- })
-
- Simulator.gestures.swipe(carouselEl, {
- pos: [300, 10],
- deltaX: -300,
- deltaY: 0
+ it('should allow swipeleft and call _slide (next) with touch events', () => {
+ return new Promise(resolve => {
+ Simulator.setType('touch')
+ clearPointerEvents()
+ document.documentElement.ontouchstart = noop
+
+ fixtureEl.innerHTML = [
+ '<div class="carousel">',
+ ' <div class="carousel-inner">',
+ ' <div id="item" class="carousel-item active">',
+ ' <img alt="">',
+ ' </div>',
+ ' <div class="carousel-item">',
+ ' <img alt="">',
+ ' </div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const carouselEl = fixtureEl.querySelector('.carousel')
+ const item = fixtureEl.querySelector('#item')
+ const carousel = new Carousel(carouselEl)
+
+ const spy = spyOn(carousel, '_slide').and.callThrough()
+
+ carouselEl.addEventListener('slid.bs.carousel', event => {
+ expect(item).not.toHaveClass('active')
+ expect(spy).toHaveBeenCalledWith('next')
+ expect(event.direction).toEqual('left')
+ delete document.documentElement.ontouchstart
+ restorePointerEvents()
+ resolve()
+ })
+
+ Simulator.gestures.swipe(carouselEl, {
+ pos: [300, 10],
+ deltaX: -300,
+ deltaY: 0
+ })
})
})
- it('should not slide when swiping and carousel is sliding', done => {
- Simulator.setType('touch')
- clearPointerEvents()
- document.documentElement.ontouchstart = noop
-
- fixtureEl.innerHTML = [
- '<div class="carousel" data-bs-interval="false">',
- ' <div class="carousel-inner">',
- ' <div id="item" class="carousel-item active">',
- ' <img alt="">',
- ' </div>',
- ' <div class="carousel-item">',
- ' <img alt="">',
- ' </div>',
- ' </div>',
- '</div>'
- ].join('')
-
- const carouselEl = fixtureEl.querySelector('.carousel')
- const carousel = new Carousel(carouselEl)
- carousel._isSliding = true
-
- spyOn(carousel, '_triggerSlideEvent')
+ it('should not slide when swiping and carousel is sliding', () => {
+ return new Promise(resolve => {
+ Simulator.setType('touch')
+ clearPointerEvents()
+ document.documentElement.ontouchstart = noop
+
+ fixtureEl.innerHTML = [
+ '<div class="carousel">',
+ ' <div class="carousel-inner">',
+ ' <div id="item" class="carousel-item active">',
+ ' <img alt="">',
+ ' </div>',
+ ' <div class="carousel-item">',
+ ' <img alt="">',
+ ' </div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const carouselEl = fixtureEl.querySelector('.carousel')
+ const carousel = new Carousel(carouselEl)
+ carousel._isSliding = true
+
+ const spy = spyOn(EventHandler, 'trigger')
+
+ Simulator.gestures.swipe(carouselEl, {
+ deltaX: 300,
+ deltaY: 0
+ })
+
+ Simulator.gestures.swipe(carouselEl, {
+ pos: [300, 10],
+ deltaX: -300,
+ deltaY: 0
+ })
- Simulator.gestures.swipe(carouselEl, {
- deltaX: 300,
- deltaY: 0
- })
-
- Simulator.gestures.swipe(carouselEl, {
- pos: [300, 10],
- deltaX: -300,
- deltaY: 0
+ setTimeout(() => {
+ expect(spy).not.toHaveBeenCalled()
+ delete document.documentElement.ontouchstart
+ restorePointerEvents()
+ resolve()
+ }, 300)
})
-
- setTimeout(() => {
- expect(carousel._triggerSlideEvent).not.toHaveBeenCalled()
- delete document.documentElement.ontouchstart
- restorePointerEvents()
- done()
- }, 300)
})
- it('should not allow pinch with touch events', done => {
- Simulator.setType('touch')
- clearPointerEvents()
- document.documentElement.ontouchstart = noop
-
- fixtureEl.innerHTML = '<div class="carousel" data-bs-interval="false"></div>'
-
- const carouselEl = fixtureEl.querySelector('.carousel')
- const carousel = new Carousel(carouselEl)
-
- Simulator.gestures.swipe(carouselEl, {
- pos: [300, 10],
- deltaX: -300,
- deltaY: 0,
- touches: 2
- }, () => {
- restorePointerEvents()
- delete document.documentElement.ontouchstart
- expect(carousel._swipeHelper._deltaX).toEqual(0)
- done()
+ it('should not allow pinch with touch events', () => {
+ return new Promise(resolve => {
+ Simulator.setType('touch')
+ clearPointerEvents()
+ document.documentElement.ontouchstart = noop
+
+ fixtureEl.innerHTML = '<div class="carousel"></div>'
+
+ const carouselEl = fixtureEl.querySelector('.carousel')
+ const carousel = new Carousel(carouselEl)
+
+ Simulator.gestures.swipe(carouselEl, {
+ pos: [300, 10],
+ deltaX: -300,
+ deltaY: 0,
+ touches: 2
+ }, () => {
+ restorePointerEvents()
+ delete document.documentElement.ontouchstart
+ expect(carousel._swipeHelper._deltaX).toEqual(0)
+ resolve()
+ })
})
})
- it('should call pause method on mouse over with pause equal to hover', done => {
- fixtureEl.innerHTML = '<div class="carousel"></div>'
+ it('should call pause method on mouse over with pause equal to hover', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="carousel"></div>'
- const carouselEl = fixtureEl.querySelector('.carousel')
- const carousel = new Carousel(carouselEl)
+ const carouselEl = fixtureEl.querySelector('.carousel')
+ const carousel = new Carousel(carouselEl)
- spyOn(carousel, 'pause')
+ const spy = spyOn(carousel, 'pause')
- const mouseOverEvent = createEvent('mouseover')
- carouselEl.dispatchEvent(mouseOverEvent)
+ const mouseOverEvent = createEvent('mouseover')
+ carouselEl.dispatchEvent(mouseOverEvent)
- setTimeout(() => {
- expect(carousel.pause).toHaveBeenCalled()
- done()
- }, 10)
+ setTimeout(() => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ }, 10)
+ })
})
- it('should call cycle on mouse out with pause equal to hover', done => {
- fixtureEl.innerHTML = '<div class="carousel"></div>'
+ it('should call `maybeEnableCycle` on mouse out with pause equal to hover', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="carousel" data-bs-ride="true"></div>'
- const carouselEl = fixtureEl.querySelector('.carousel')
- const carousel = new Carousel(carouselEl)
+ const carouselEl = fixtureEl.querySelector('.carousel')
+ const carousel = new Carousel(carouselEl)
- spyOn(carousel, 'cycle')
+ const spyEnable = spyOn(carousel, '_maybeEnableCycle').and.callThrough()
+ const spyCycle = spyOn(carousel, 'cycle')
- const mouseOutEvent = createEvent('mouseout')
- carouselEl.dispatchEvent(mouseOutEvent)
+ const mouseOutEvent = createEvent('mouseout')
+ carouselEl.dispatchEvent(mouseOutEvent)
- setTimeout(() => {
- expect(carousel.cycle).toHaveBeenCalled()
- done()
- }, 10)
+ setTimeout(() => {
+ expect(spyEnable).toHaveBeenCalled()
+ expect(spyCycle).toHaveBeenCalled()
+ resolve()
+ }, 10)
+ })
})
})
@@ -615,108 +691,114 @@ describe('Carousel', () => {
const carouselEl = fixtureEl.querySelector('div')
const carousel = new Carousel(carouselEl, {})
- spyOn(carousel, '_triggerSlideEvent')
+ const spy = spyOn(EventHandler, 'trigger')
carousel._isSliding = true
carousel.next()
- expect(carousel._triggerSlideEvent).not.toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
})
- it('should not fire slid when slide is prevented', done => {
- fixtureEl.innerHTML = '<div></div>'
+ it('should not fire slid when slide is prevented', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div></div>'
- const carouselEl = fixtureEl.querySelector('div')
- const carousel = new Carousel(carouselEl, {})
- let slidEvent = false
+ const carouselEl = fixtureEl.querySelector('div')
+ const carousel = new Carousel(carouselEl, {})
+ let slidEvent = false
- const doneTest = () => {
- setTimeout(() => {
- expect(slidEvent).toEqual(false)
- done()
- }, 20)
- }
+ const doneTest = () => {
+ setTimeout(() => {
+ expect(slidEvent).toBeFalse()
+ resolve()
+ }, 20)
+ }
- carouselEl.addEventListener('slide.bs.carousel', event => {
- event.preventDefault()
- doneTest()
- })
+ carouselEl.addEventListener('slide.bs.carousel', event => {
+ event.preventDefault()
+ doneTest()
+ })
- carouselEl.addEventListener('slid.bs.carousel', () => {
- slidEvent = true
- })
+ carouselEl.addEventListener('slid.bs.carousel', () => {
+ slidEvent = true
+ })
- carousel.next()
+ carousel.next()
+ })
})
- it('should fire slide event with: direction, relatedTarget, from and to', done => {
- fixtureEl.innerHTML = [
- '<div id="myCarousel" class="carousel slide">',
- ' <div class="carousel-inner">',
- ' <div class="carousel-item active">item 1</div>',
- ' <div class="carousel-item">item 2</div>',
- ' <div class="carousel-item">item 3</div>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should fire slide event with: direction, relatedTarget, from and to', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="myCarousel" class="carousel slide">',
+ ' <div class="carousel-inner">',
+ ' <div class="carousel-item active">item 1</div>',
+ ' <div class="carousel-item">item 2</div>',
+ ' <div class="carousel-item">item 3</div>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const carouselEl = fixtureEl.querySelector('#myCarousel')
- const carousel = new Carousel(carouselEl, {})
+ const carouselEl = fixtureEl.querySelector('#myCarousel')
+ const carousel = new Carousel(carouselEl, {})
- const onSlide = event => {
- expect(event.direction).toEqual('left')
- expect(event.relatedTarget.classList.contains('carousel-item')).toEqual(true)
- expect(event.from).toEqual(0)
- expect(event.to).toEqual(1)
+ const onSlide = event => {
+ expect(event.direction).toEqual('left')
+ expect(event.relatedTarget).toHaveClass('carousel-item')
+ expect(event.from).toEqual(0)
+ expect(event.to).toEqual(1)
- carouselEl.removeEventListener('slide.bs.carousel', onSlide)
- carouselEl.addEventListener('slide.bs.carousel', onSlide2)
+ carouselEl.removeEventListener('slide.bs.carousel', onSlide)
+ carouselEl.addEventListener('slide.bs.carousel', onSlide2)
- carousel.prev()
- }
+ carousel.prev()
+ }
- const onSlide2 = event => {
- expect(event.direction).toEqual('right')
- done()
- }
+ const onSlide2 = event => {
+ expect(event.direction).toEqual('right')
+ resolve()
+ }
- carouselEl.addEventListener('slide.bs.carousel', onSlide)
- carousel.next()
+ carouselEl.addEventListener('slide.bs.carousel', onSlide)
+ carousel.next()
+ })
})
- it('should fire slid event with: direction, relatedTarget, from and to', done => {
- fixtureEl.innerHTML = [
- '<div id="myCarousel" class="carousel slide">',
- ' <div class="carousel-inner">',
- ' <div class="carousel-item active">item 1</div>',
- ' <div class="carousel-item">item 2</div>',
- ' <div class="carousel-item">item 3</div>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should fire slid event with: direction, relatedTarget, from and to', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="myCarousel" class="carousel slide">',
+ ' <div class="carousel-inner">',
+ ' <div class="carousel-item active">item 1</div>',
+ ' <div class="carousel-item">item 2</div>',
+ ' <div class="carousel-item">item 3</div>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const carouselEl = fixtureEl.querySelector('#myCarousel')
- const carousel = new Carousel(carouselEl, {})
+ const carouselEl = fixtureEl.querySelector('#myCarousel')
+ const carousel = new Carousel(carouselEl, {})
- const onSlid = event => {
- expect(event.direction).toEqual('left')
- expect(event.relatedTarget.classList.contains('carousel-item')).toEqual(true)
- expect(event.from).toEqual(0)
- expect(event.to).toEqual(1)
+ const onSlid = event => {
+ expect(event.direction).toEqual('left')
+ expect(event.relatedTarget).toHaveClass('carousel-item')
+ expect(event.from).toEqual(0)
+ expect(event.to).toEqual(1)
- carouselEl.removeEventListener('slid.bs.carousel', onSlid)
- carouselEl.addEventListener('slid.bs.carousel', onSlid2)
+ carouselEl.removeEventListener('slid.bs.carousel', onSlid)
+ carouselEl.addEventListener('slid.bs.carousel', onSlid2)
- carousel.prev()
- }
+ carousel.prev()
+ }
- const onSlid2 = event => {
- expect(event.direction).toEqual('right')
- done()
- }
+ const onSlid2 = event => {
+ expect(event.direction).toEqual('right')
+ resolve()
+ }
- carouselEl.addEventListener('slid.bs.carousel', onSlid)
- carousel.next()
+ carouselEl.addEventListener('slid.bs.carousel', onSlid)
+ carousel.next()
+ })
})
it('should update the active element to the next item before sliding', () => {
@@ -739,36 +821,60 @@ describe('Carousel', () => {
expect(carousel._activeElement).toEqual(secondItemEl)
})
- it('should update indicators if present', done => {
+ it('should continue cycling if it was already', () => {
fixtureEl.innerHTML = [
'<div id="myCarousel" class="carousel slide">',
- ' <div class="carousel-indicators">',
- ' <button type="button" id="firstIndicator" data-bs-target="myCarousel" data-bs-slide-to="0" class="active" aria-current="true" aria-label="Slide 1"></button>',
- ' <button type="button" id="secondIndicator" data-bs-target="myCarousel" data-bs-slide-to="1" aria-label="Slide 2"></button>',
- ' <button type="button" data-bs-target="myCarousel" data-bs-slide-to="2" aria-label="Slide 3"></button>',
- ' </div>',
' <div class="carousel-inner">',
' <div class="carousel-item active">item 1</div>',
- ' <div class="carousel-item" data-bs-interval="7">item 2</div>',
- ' <div class="carousel-item">item 3</div>',
+ ' <div class="carousel-item">item 2</div>',
' </div>',
'</div>'
].join('')
const carouselEl = fixtureEl.querySelector('#myCarousel')
- const firstIndicator = fixtureEl.querySelector('#firstIndicator')
- const secondIndicator = fixtureEl.querySelector('#secondIndicator')
const carousel = new Carousel(carouselEl)
+ const spy = spyOn(carousel, 'cycle')
- carouselEl.addEventListener('slid.bs.carousel', () => {
- expect(firstIndicator.classList.contains('active')).toEqual(false)
- expect(firstIndicator.hasAttribute('aria-current')).toEqual(false)
- expect(secondIndicator.classList.contains('active')).toEqual(true)
- expect(secondIndicator.getAttribute('aria-current')).toEqual('true')
- done()
- })
+ carousel.next()
+ expect(spy).not.toHaveBeenCalled()
+ carousel.cycle()
carousel.next()
+ expect(spy).toHaveBeenCalledTimes(1)
+ })
+
+ it('should update indicators if present', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="myCarousel" class="carousel slide">',
+ ' <div class="carousel-indicators">',
+ ' <button type="button" id="firstIndicator" data-bs-target="myCarousel" data-bs-slide-to="0" class="active" aria-current="true" aria-label="Slide 1"></button>',
+ ' <button type="button" id="secondIndicator" data-bs-target="myCarousel" data-bs-slide-to="1" aria-label="Slide 2"></button>',
+ ' <button type="button" data-bs-target="myCarousel" data-bs-slide-to="2" aria-label="Slide 3"></button>',
+ ' </div>',
+ ' <div class="carousel-inner">',
+ ' <div class="carousel-item active">item 1</div>',
+ ' <div class="carousel-item" data-bs-interval="7">item 2</div>',
+ ' <div class="carousel-item">item 3</div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const carouselEl = fixtureEl.querySelector('#myCarousel')
+ const firstIndicator = fixtureEl.querySelector('#firstIndicator')
+ const secondIndicator = fixtureEl.querySelector('#secondIndicator')
+ const carousel = new Carousel(carouselEl)
+
+ carouselEl.addEventListener('slid.bs.carousel', () => {
+ expect(firstIndicator).not.toHaveClass('active')
+ expect(firstIndicator.hasAttribute('aria-current')).toBeFalse()
+ expect(secondIndicator).toHaveClass('active')
+ expect(secondIndicator.getAttribute('aria-current')).toEqual('true')
+ resolve()
+ })
+
+ carousel.next()
+ })
})
it('should call next()/prev() instance methods when clicking the respective direction buttons', () => {
@@ -791,12 +897,14 @@ describe('Carousel', () => {
const carousel = new Carousel(carouselEl)
const nextSpy = spyOn(carousel, 'next')
const prevSpy = spyOn(carousel, 'prev')
+ const spyEnable = spyOn(carousel, '_maybeEnableCycle')
nextBtnEl.click()
prevBtnEl.click()
expect(nextSpy).toHaveBeenCalled()
expect(prevSpy).toHaveBeenCalled()
+ expect(spyEnable).toHaveBeenCalled()
})
})
@@ -804,18 +912,18 @@ describe('Carousel', () => {
it('should not call next when the page is not visible', () => {
fixtureEl.innerHTML = [
'<div style="display: none;">',
- ' <div class="carousel" data-bs-interval="false"></div>',
+ ' <div class="carousel"></div>',
'</div>'
].join('')
const carouselEl = fixtureEl.querySelector('.carousel')
const carousel = new Carousel(carouselEl)
- spyOn(carousel, 'next')
+ const spy = spyOn(carousel, 'next')
carousel.nextWhenVisible()
- expect(carousel.next).not.toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
})
})
@@ -826,91 +934,42 @@ describe('Carousel', () => {
const carouselEl = fixtureEl.querySelector('div')
const carousel = new Carousel(carouselEl, {})
- spyOn(carousel, '_triggerSlideEvent')
+ const spy = spyOn(EventHandler, 'trigger')
carousel._isSliding = true
carousel.prev()
- expect(carousel._triggerSlideEvent).not.toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
})
})
describe('pause', () => {
- it('should call cycle if the carousel have carousel-item-next and carousel-item-prev class', () => {
- fixtureEl.innerHTML = [
- '<div id="myCarousel" class="carousel slide">',
- ' <div class="carousel-inner">',
- ' <div class="carousel-item active">item 1</div>',
- ' <div class="carousel-item carousel-item-next">item 2</div>',
- ' <div class="carousel-item">item 3</div>',
- ' </div>',
- ' <div class="carousel-control-prev"></div>',
- ' <div class="carousel-control-next"></div>',
- '</div>'
- ].join('')
-
- const carouselEl = fixtureEl.querySelector('#myCarousel')
- const carousel = new Carousel(carouselEl)
-
- spyOn(carousel, 'cycle')
- spyOn(window, 'clearInterval')
-
- carousel.pause()
-
- expect(carousel.cycle).toHaveBeenCalledWith(true)
- expect(window.clearInterval).toHaveBeenCalled()
- expect(carousel._isPaused).toEqual(true)
- })
-
- it('should not call cycle if nothing is in transition', () => {
- fixtureEl.innerHTML = [
- '<div id="myCarousel" class="carousel slide">',
- ' <div class="carousel-inner">',
- ' <div class="carousel-item active">item 1</div>',
- ' <div class="carousel-item">item 2</div>',
- ' <div class="carousel-item">item 3</div>',
- ' </div>',
- ' <div class="carousel-control-prev"></div>',
- ' <div class="carousel-control-next"></div>',
- '</div>'
- ].join('')
-
- const carouselEl = fixtureEl.querySelector('#myCarousel')
- const carousel = new Carousel(carouselEl)
-
- spyOn(carousel, 'cycle')
- spyOn(window, 'clearInterval')
-
- carousel.pause()
-
- expect(carousel.cycle).not.toHaveBeenCalled()
- expect(window.clearInterval).toHaveBeenCalled()
- expect(carousel._isPaused).toEqual(true)
- })
-
- it('should not set is paused at true if an event is passed', () => {
- fixtureEl.innerHTML = [
- '<div id="myCarousel" class="carousel slide">',
- ' <div class="carousel-inner">',
- ' <div class="carousel-item active">item 1</div>',
- ' <div class="carousel-item">item 2</div>',
- ' <div class="carousel-item">item 3</div>',
- ' </div>',
- ' <div class="carousel-control-prev"></div>',
- ' <div class="carousel-control-next"></div>',
- '</div>'
- ].join('')
-
- const carouselEl = fixtureEl.querySelector('#myCarousel')
- const carousel = new Carousel(carouselEl)
- const event = createEvent('mouseenter')
-
- spyOn(window, 'clearInterval')
-
- carousel.pause(event)
-
- expect(window.clearInterval).toHaveBeenCalled()
- expect(carousel._isPaused).toEqual(false)
+ it('should trigger transitionend if the carousel have carousel-item-next or carousel-item-prev class, cause is sliding', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="myCarousel" class="carousel slide">',
+ ' <div class="carousel-inner">',
+ ' <div class="carousel-item active">item 1</div>',
+ ' <div class="carousel-item carousel-item-next">item 2</div>',
+ ' <div class="carousel-item">item 3</div>',
+ ' </div>',
+ ' <div class="carousel-control-prev"></div>',
+ ' <div class="carousel-control-next"></div>',
+ '</div>'
+ ].join('')
+
+ const carouselEl = fixtureEl.querySelector('#myCarousel')
+ const carousel = new Carousel(carouselEl)
+ const spy = spyOn(carousel, '_clearInterval')
+
+ carouselEl.addEventListener('transitionend', () => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
+
+ carousel._slide('next')
+ carousel.pause()
+ })
})
})
@@ -931,35 +990,11 @@ describe('Carousel', () => {
const carouselEl = fixtureEl.querySelector('#myCarousel')
const carousel = new Carousel(carouselEl)
- spyOn(window, 'setInterval').and.callThrough()
+ const spy = spyOn(window, 'setInterval').and.callThrough()
carousel.cycle()
- expect(window.setInterval).toHaveBeenCalled()
- })
-
- it('should not set interval if the carousel is paused', () => {
- fixtureEl.innerHTML = [
- '<div id="myCarousel" class="carousel slide">',
- ' <div class="carousel-inner">',
- ' <div class="carousel-item active">item 1</div>',
- ' <div class="carousel-item">item 2</div>',
- ' <div class="carousel-item">item 3</div>',
- ' </div>',
- ' <div class="carousel-control-prev"></div>',
- ' <div class="carousel-control-next"></div>',
- '</div>'
- ].join('')
-
- const carouselEl = fixtureEl.querySelector('#myCarousel')
- const carousel = new Carousel(carouselEl)
-
- spyOn(window, 'setInterval').and.callThrough()
-
- carousel._isPaused = true
- carousel.cycle(true)
-
- expect(window.setInterval).not.toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
})
it('should clear interval if there is one', () => {
@@ -980,13 +1015,13 @@ describe('Carousel', () => {
carousel._interval = setInterval(noop, 10)
- spyOn(window, 'setInterval').and.callThrough()
- spyOn(window, 'clearInterval').and.callThrough()
+ const spySet = spyOn(window, 'setInterval').and.callThrough()
+ const spyClear = spyOn(window, 'clearInterval').and.callThrough()
carousel.cycle()
- expect(window.setInterval).toHaveBeenCalled()
- expect(window.clearInterval).toHaveBeenCalled()
+ expect(spySet).toHaveBeenCalled()
+ expect(spyClear).toHaveBeenCalled()
})
it('should get interval from data attribute on the active item element', () => {
@@ -1020,51 +1055,55 @@ describe('Carousel', () => {
})
describe('to', () => {
- it('should go directly to the provided index', done => {
- fixtureEl.innerHTML = [
- '<div id="myCarousel" class="carousel slide">',
- ' <div class="carousel-inner">',
- ' <div id="item1" class="carousel-item active">item 1</div>',
- ' <div class="carousel-item">item 2</div>',
- ' <div id="item3" class="carousel-item">item 3</div>',
- ' </div>',
- '</div>'
- ].join('')
-
- const carouselEl = fixtureEl.querySelector('#myCarousel')
- const carousel = new Carousel(carouselEl, {})
+ it('should go directly to the provided index', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="myCarousel" class="carousel slide">',
+ ' <div class="carousel-inner">',
+ ' <div id="item1" class="carousel-item active">item 1</div>',
+ ' <div class="carousel-item">item 2</div>',
+ ' <div id="item3" class="carousel-item">item 3</div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const carouselEl = fixtureEl.querySelector('#myCarousel')
+ const carousel = new Carousel(carouselEl, {})
- expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item1'))
+ expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item1'))
- carousel.to(2)
+ carousel.to(2)
- carouselEl.addEventListener('slid.bs.carousel', () => {
- expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item3'))
- done()
+ carouselEl.addEventListener('slid.bs.carousel', () => {
+ expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item3'))
+ resolve()
+ })
})
})
- it('should return to a previous slide if the provided index is lower than the current', done => {
- fixtureEl.innerHTML = [
- '<div id="myCarousel" class="carousel slide">',
- ' <div class="carousel-inner">',
- ' <div class="carousel-item">item 1</div>',
- ' <div id="item2" class="carousel-item">item 2</div>',
- ' <div id="item3" class="carousel-item active">item 3</div>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should return to a previous slide if the provided index is lower than the current', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="myCarousel" class="carousel slide">',
+ ' <div class="carousel-inner">',
+ ' <div class="carousel-item">item 1</div>',
+ ' <div id="item2" class="carousel-item">item 2</div>',
+ ' <div id="item3" class="carousel-item active">item 3</div>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const carouselEl = fixtureEl.querySelector('#myCarousel')
- const carousel = new Carousel(carouselEl, {})
+ const carouselEl = fixtureEl.querySelector('#myCarousel')
+ const carousel = new Carousel(carouselEl, {})
- expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item3'))
+ expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item3'))
- carousel.to(1)
+ carousel.to(1)
- carouselEl.addEventListener('slid.bs.carousel', () => {
- expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item2'))
- done()
+ carouselEl.addEventListener('slid.bs.carousel', () => {
+ expect(fixtureEl.querySelector('.active')).toEqual(fixtureEl.querySelector('#item2'))
+ resolve()
+ })
})
})
@@ -1095,7 +1134,7 @@ describe('Carousel', () => {
expect(spy).not.toHaveBeenCalled()
})
- it('should call pause and cycle is the provided is the same compare to the current one', () => {
+ it('should not continue if the provided is the same compare to the current one', () => {
fixtureEl.innerHTML = [
'<div id="myCarousel" class="carousel slide">',
' <div class="carousel-inner">',
@@ -1109,50 +1148,49 @@ describe('Carousel', () => {
const carouselEl = fixtureEl.querySelector('#myCarousel')
const carousel = new Carousel(carouselEl, {})
- spyOn(carousel, '_slide')
- spyOn(carousel, 'pause')
- spyOn(carousel, 'cycle')
+ const spy = spyOn(carousel, '_slide')
carousel.to(0)
- expect(carousel._slide).not.toHaveBeenCalled()
- expect(carousel.pause).toHaveBeenCalled()
- expect(carousel.cycle).toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
})
- it('should wait before performing to if a slide is sliding', done => {
- fixtureEl.innerHTML = [
- '<div id="myCarousel" class="carousel slide">',
- ' <div class="carousel-inner">',
- ' <div class="carousel-item active">item 1</div>',
- ' <div class="carousel-item" data-bs-interval="7">item 2</div>',
- ' <div class="carousel-item">item 3</div>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should wait before performing to if a slide is sliding', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="myCarousel" class="carousel slide">',
+ ' <div class="carousel-inner">',
+ ' <div class="carousel-item active">item 1</div>',
+ ' <div class="carousel-item" data-bs-interval="7">item 2</div>',
+ ' <div class="carousel-item">item 3</div>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const carouselEl = fixtureEl.querySelector('#myCarousel')
- const carousel = new Carousel(carouselEl, {})
+ const carouselEl = fixtureEl.querySelector('#myCarousel')
+ const carousel = new Carousel(carouselEl, {})
- spyOn(EventHandler, 'one').and.callThrough()
- spyOn(carousel, '_slide')
+ const spyOne = spyOn(EventHandler, 'one').and.callThrough()
+ const spySlide = spyOn(carousel, '_slide')
- carousel._isSliding = true
- carousel.to(1)
+ carousel._isSliding = true
+ carousel.to(1)
- expect(carousel._slide).not.toHaveBeenCalled()
- expect(EventHandler.one).toHaveBeenCalled()
+ expect(spySlide).not.toHaveBeenCalled()
+ expect(spyOne).toHaveBeenCalled()
- spyOn(carousel, 'to')
+ const spyTo = spyOn(carousel, 'to')
- EventHandler.trigger(carouselEl, 'slid.bs.carousel')
+ EventHandler.trigger(carouselEl, 'slid.bs.carousel')
- setTimeout(() => {
- expect(carousel.to).toHaveBeenCalledWith(1)
- done()
+ setTimeout(() => {
+ expect(spyTo).toHaveBeenCalledWith(1)
+ resolve()
+ })
})
})
})
+
describe('rtl function', () => {
it('"_directionToOrder" and "_orderToDirection" must return the right results', () => {
fixtureEl.innerHTML = '<div></div>'
@@ -1161,9 +1199,7 @@ describe('Carousel', () => {
const carousel = new Carousel(carouselEl, {})
expect(carousel._directionToOrder('left')).toEqual('next')
- expect(carousel._directionToOrder('prev')).toEqual('prev')
expect(carousel._directionToOrder('right')).toEqual('prev')
- expect(carousel._directionToOrder('next')).toEqual('next')
expect(carousel._orderToDirection('next')).toEqual('left')
expect(carousel._orderToDirection('prev')).toEqual('right')
@@ -1175,12 +1211,10 @@ describe('Carousel', () => {
const carouselEl = fixtureEl.querySelector('div')
const carousel = new Carousel(carouselEl, {})
- expect(isRTL()).toEqual(true, 'rtl has to be true')
+ expect(isRTL()).toBeTrue()
expect(carousel._directionToOrder('left')).toEqual('prev')
- expect(carousel._directionToOrder('prev')).toEqual('prev')
expect(carousel._directionToOrder('right')).toEqual('next')
- expect(carousel._directionToOrder('next')).toEqual('next')
expect(carousel._orderToDirection('next')).toEqual('right')
expect(carousel._orderToDirection('prev')).toEqual('left')
@@ -1192,16 +1226,14 @@ describe('Carousel', () => {
const carouselEl = fixtureEl.querySelector('div')
const carousel = new Carousel(carouselEl, {})
- const spy = spyOn(carousel, '_directionToOrder').and.callThrough()
- const spy2 = spyOn(carousel, '_orderToDirection').and.callThrough()
- carousel._slide('left')
- expect(spy).toHaveBeenCalledWith('left')
- expect(spy2).toHaveBeenCalledWith('next')
+ const spy = spyOn(carousel, '_orderToDirection').and.callThrough()
- carousel._slide('right')
- expect(spy).toHaveBeenCalledWith('right')
- expect(spy2).toHaveBeenCalledWith('prev')
+ carousel._slide(carousel._directionToOrder('left'))
+ expect(spy).toHaveBeenCalledWith('next')
+
+ carousel._slide(carousel._directionToOrder('right'))
+ expect(spy).toHaveBeenCalledWith('prev')
})
it('"_slide" has to call "_directionToOrder" and "_orderToDirection" when rtl=true', () => {
@@ -1210,16 +1242,13 @@ describe('Carousel', () => {
const carouselEl = fixtureEl.querySelector('div')
const carousel = new Carousel(carouselEl, {})
- const spy = spyOn(carousel, '_directionToOrder').and.callThrough()
- const spy2 = spyOn(carousel, '_orderToDirection').and.callThrough()
+ const spy = spyOn(carousel, '_orderToDirection').and.callThrough()
- carousel._slide('left')
- expect(spy).toHaveBeenCalledWith('left')
- expect(spy2).toHaveBeenCalledWith('prev')
+ carousel._slide(carousel._directionToOrder('left'))
+ expect(spy).toHaveBeenCalledWith('prev')
- carousel._slide('right')
- expect(spy).toHaveBeenCalledWith('right')
- expect(spy2).toHaveBeenCalledWith('next')
+ carousel._slide(carousel._directionToOrder('right'))
+ expect(spy).toHaveBeenCalledWith('next')
document.documentElement.dir = 'ltl'
})
@@ -1292,7 +1321,7 @@ describe('Carousel', () => {
const div = fixtureEl.querySelector('div')
- expect(Carousel.getInstance(div)).toEqual(null)
+ expect(Carousel.getInstance(div)).toBeNull()
})
})
@@ -1313,7 +1342,7 @@ describe('Carousel', () => {
const div = fixtureEl.querySelector('div')
- expect(Carousel.getInstance(div)).toEqual(null)
+ expect(Carousel.getInstance(div)).toBeNull()
expect(Carousel.getOrCreateInstance(div)).toBeInstanceOf(Carousel)
})
@@ -1322,7 +1351,7 @@ describe('Carousel', () => {
const div = fixtureEl.querySelector('div')
- expect(Carousel.getInstance(div)).toEqual(null)
+ expect(Carousel.getInstance(div)).toBeNull()
const carousel = Carousel.getOrCreateInstance(div, {
interval: 1
})
@@ -1385,14 +1414,14 @@ describe('Carousel', () => {
const carousel = new Carousel(div)
const slideTo = 2
- spyOn(carousel, 'to')
+ const spy = spyOn(carousel, 'to')
jQueryMock.fn.carousel = Carousel.jQueryInterface
jQueryMock.elements = [div]
jQueryMock.fn.carousel.call(jQueryMock, slideTo)
- expect(carousel.to).toHaveBeenCalledWith(slideTo)
+ expect(spy).toHaveBeenCalledWith(slideTo)
})
it('should throw error on undefined method', () => {
@@ -1418,79 +1447,86 @@ describe('Carousel', () => {
const loadEvent = createEvent('load')
window.dispatchEvent(loadEvent)
-
- expect(Carousel.getInstance(carouselEl)).not.toBeNull()
+ const carousel = Carousel.getInstance(carouselEl)
+ expect(carousel._interval).not.toBeNull()
})
- it('should create carousel and go to the next slide on click (with real button controls)', done => {
- fixtureEl.innerHTML = [
- '<div id="myCarousel" class="carousel slide">',
- ' <div class="carousel-inner">',
- ' <div class="carousel-item active">item 1</div>',
- ' <div id="item2" class="carousel-item">item 2</div>',
- ' <div class="carousel-item">item 3</div>',
- ' </div>',
- ' <button class="carousel-control-prev" data-bs-target="#myCarousel" type="button" data-bs-slide="prev"></button>',
- ' <button id="next" class="carousel-control-next" data-bs-target="#myCarousel" type="button" data-bs-slide="next"></div>',
- '</div>'
- ].join('')
+ it('should create carousel and go to the next slide on click (with real button controls)', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="myCarousel" class="carousel slide">',
+ ' <div class="carousel-inner">',
+ ' <div class="carousel-item active">item 1</div>',
+ ' <div id="item2" class="carousel-item">item 2</div>',
+ ' <div class="carousel-item">item 3</div>',
+ ' </div>',
+ ' <button class="carousel-control-prev" data-bs-target="#myCarousel" type="button" data-bs-slide="prev"></button>',
+ ' <button id="next" class="carousel-control-next" data-bs-target="#myCarousel" type="button" data-bs-slide="next"></button>',
+ '</div>'
+ ].join('')
- const next = fixtureEl.querySelector('#next')
- const item2 = fixtureEl.querySelector('#item2')
+ const next = fixtureEl.querySelector('#next')
+ const item2 = fixtureEl.querySelector('#item2')
- next.click()
+ next.click()
- setTimeout(() => {
- expect(item2.classList.contains('active')).toEqual(true)
- done()
- }, 10)
+ setTimeout(() => {
+ expect(item2).toHaveClass('active')
+ resolve()
+ }, 10)
+ })
})
- it('should create carousel and go to the next slide on click (using links as controls)', done => {
- fixtureEl.innerHTML = [
- '<div id="myCarousel" class="carousel slide">',
- ' <div class="carousel-inner">',
- ' <div class="carousel-item active">item 1</div>',
- ' <div id="item2" class="carousel-item">item 2</div>',
- ' <div class="carousel-item">item 3</div>',
- ' </div>',
- ' <a class="carousel-control-prev" href="#myCarousel" role="button" data-bs-slide="prev"></button>',
- ' <a id="next" class="carousel-control-next" href="#myCarousel" role="button" data-bs-slide="next"></div>',
- '</div>'
- ].join('')
+ it('should create carousel and go to the next slide on click (using links as controls)', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="myCarousel" class="carousel slide">',
+ ' <div class="carousel-inner">',
+ ' <div class="carousel-item active">item 1</div>',
+ ' <div id="item2" class="carousel-item">item 2</div>',
+ ' <div class="carousel-item">item 3</div>',
+ ' </div>',
+ ' <a class="carousel-control-prev" href="#myCarousel" role="button" data-bs-slide="prev"></a>',
+ ' <a id="next" class="carousel-control-next" href="#myCarousel" role="button" data-bs-slide="next"></a>',
+ '</div>'
+ ].join('')
- const next = fixtureEl.querySelector('#next')
- const item2 = fixtureEl.querySelector('#item2')
+ const next = fixtureEl.querySelector('#next')
+ const item2 = fixtureEl.querySelector('#item2')
- next.click()
+ next.click()
- setTimeout(() => {
- expect(item2.classList.contains('active')).toEqual(true)
- done()
- }, 10)
+ setTimeout(() => {
+ expect(item2).toHaveClass('active')
+ resolve()
+ }, 10)
+ })
})
- it('should create carousel and go to the next slide on click with data-bs-slide-to', done => {
- fixtureEl.innerHTML = [
- '<div id="myCarousel" class="carousel slide">',
- ' <div class="carousel-inner">',
- ' <div class="carousel-item active">item 1</div>',
- ' <div id="item2" class="carousel-item">item 2</div>',
- ' <div class="carousel-item">item 3</div>',
- ' </div>',
- ' <div id="next" data-bs-target="#myCarousel" data-bs-slide-to="1"></div>',
- '</div>'
- ].join('')
+ it('should create carousel and go to the next slide on click with data-bs-slide-to', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="myCarousel" class="carousel slide" data-bs-ride="true">',
+ ' <div class="carousel-inner">',
+ ' <div class="carousel-item active">item 1</div>',
+ ' <div id="item2" class="carousel-item">item 2</div>',
+ ' <div class="carousel-item">item 3</div>',
+ ' </div>',
+ ' <div id="next" data-bs-target="#myCarousel" data-bs-slide-to="1"></div>',
+ '</div>'
+ ].join('')
- const next = fixtureEl.querySelector('#next')
- const item2 = fixtureEl.querySelector('#item2')
+ const next = fixtureEl.querySelector('#next')
+ const item2 = fixtureEl.querySelector('#item2')
- next.click()
+ next.click()
- setTimeout(() => {
- expect(item2.classList.contains('active')).toEqual(true)
- done()
- }, 10)
+ setTimeout(() => {
+ expect(item2).toHaveClass('active')
+ expect(Carousel.getInstance('#myCarousel')._interval).not.toBeNull()
+ resolve()
+ }, 10)
+ })
})
it('should do nothing if no selector on click on arrows', () => {
@@ -1521,8 +1557,8 @@ describe('Carousel', () => {
' <div id="item2" class="carousel-item">item 2</div>',
' <div class="carousel-item">item 3</div>',
' </div>',
- ' <button class="carousel-control-prev" data-bs-target="#myCarousel" type="button" data-bs-slide="prev"></div>',
- ' <button id="next" class="carousel-control-next" data-bs-target="#myCarousel" type="button" data-bs-slide="next"></div>',
+ ' <button class="carousel-control-prev" data-bs-target="#myCarousel" type="button" data-bs-slide="prev"></button>',
+ ' <button id="next" class="carousel-control-next" data-bs-target="#myCarousel" type="button" data-bs-slide="next"></button>',
'</div>'
].join('')
diff --git a/js/tests/unit/collapse.spec.js b/js/tests/unit/collapse.spec.js
index 89d20a6d8..58c536752 100644
--- a/js/tests/unit/collapse.spec.js
+++ b/js/tests/unit/collapse.spec.js
@@ -1,6 +1,6 @@
-import Collapse from '../../src/collapse'
-import EventHandler from '../../src/dom/event-handler'
-import { clearFixture, getFixture, jQueryMock } from '../helpers/fixture'
+import Collapse from '../../src/collapse.js'
+import EventHandler from '../../src/dom/event-handler.js'
+import { clearFixture, getFixture, jQueryMock } from '../helpers/fixture.js'
describe('Collapse', () => {
let fixtureEl
@@ -112,11 +112,11 @@ describe('Collapse', () => {
const collapseEl = fixtureEl.querySelector('div')
const collapse = new Collapse(collapseEl)
- spyOn(collapse, 'show')
+ const spy = spyOn(collapse, 'show')
collapse.toggle()
- expect(collapse.show).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
})
it('should call hide method if show class is present', () => {
@@ -127,44 +127,46 @@ describe('Collapse', () => {
toggle: false
})
- spyOn(collapse, 'hide')
+ const spy = spyOn(collapse, 'hide')
collapse.toggle()
- expect(collapse.hide).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
})
- it('should find collapse children if they have collapse class too not only data-bs-parent', done => {
- fixtureEl.innerHTML = [
- '<div class="my-collapse">',
- ' <div class="item">',
- ' <a data-bs-toggle="collapse" href="#">Toggle item 1</a>',
- ' <div id="collapse1" class="collapse show">Lorem ipsum 1</div>',
- ' </div>',
- ' <div class="item">',
- ' <a id="triggerCollapse2" data-bs-toggle="collapse" href="#">Toggle item 2</a>',
- ' <div id="collapse2" class="collapse">Lorem ipsum 2</div>',
- ' </div>',
- '</div>'
- ].join('')
-
- const parent = fixtureEl.querySelector('.my-collapse')
- const collapseEl1 = fixtureEl.querySelector('#collapse1')
- const collapseEl2 = fixtureEl.querySelector('#collapse2')
-
- const collapseList = [].concat(...fixtureEl.querySelectorAll('.collapse'))
- .map(el => new Collapse(el, {
- parent,
- toggle: false
- }))
+ it('should find collapse children if they have collapse class too not only data-bs-parent', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="my-collapse">',
+ ' <div class="item">',
+ ' <a data-bs-toggle="collapse" href="#">Toggle item 1</a>',
+ ' <div id="collapse1" class="collapse show">Lorem ipsum 1</div>',
+ ' </div>',
+ ' <div class="item">',
+ ' <a id="triggerCollapse2" data-bs-toggle="collapse" href="#">Toggle item 2</a>',
+ ' <div id="collapse2" class="collapse">Lorem ipsum 2</div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const parent = fixtureEl.querySelector('.my-collapse')
+ const collapseEl1 = fixtureEl.querySelector('#collapse1')
+ const collapseEl2 = fixtureEl.querySelector('#collapse2')
+
+ const collapseList = [].concat(...fixtureEl.querySelectorAll('.collapse'))
+ .map(el => new Collapse(el, {
+ parent,
+ toggle: false
+ }))
+
+ collapseEl2.addEventListener('shown.bs.collapse', () => {
+ expect(collapseEl2).toHaveClass('show')
+ expect(collapseEl1).not.toHaveClass('show')
+ resolve()
+ })
- collapseEl2.addEventListener('shown.bs.collapse', () => {
- expect(collapseEl2.classList.contains('show')).toEqual(true)
- expect(collapseEl1.classList.contains('show')).toEqual(false)
- done()
+ collapseList[1].toggle()
})
-
- collapseList[1].toggle()
})
})
@@ -172,7 +174,7 @@ describe('Collapse', () => {
it('should do nothing if is transitioning', () => {
fixtureEl.innerHTML = '<div></div>'
- spyOn(EventHandler, 'trigger')
+ const spy = spyOn(EventHandler, 'trigger')
const collapseEl = fixtureEl.querySelector('div')
const collapse = new Collapse(collapseEl, {
@@ -182,13 +184,13 @@ describe('Collapse', () => {
collapse._isTransitioning = true
collapse.show()
- expect(EventHandler.trigger).not.toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
})
it('should do nothing if already shown', () => {
fixtureEl.innerHTML = '<div class="show"></div>'
- spyOn(EventHandler, 'trigger')
+ const spy = spyOn(EventHandler, 'trigger')
const collapseEl = fixtureEl.querySelector('div')
const collapse = new Collapse(collapseEl, {
@@ -197,205 +199,218 @@ describe('Collapse', () => {
collapse.show()
- expect(EventHandler.trigger).not.toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
})
- it('should show a collapsed element', done => {
- fixtureEl.innerHTML = '<div class="collapse" style="height: 0px;"></div>'
+ it('should show a collapsed element', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="collapse" style="height: 0px;"></div>'
- const collapseEl = fixtureEl.querySelector('div')
- const collapse = new Collapse(collapseEl, {
- toggle: false
- })
+ const collapseEl = fixtureEl.querySelector('div')
+ const collapse = new Collapse(collapseEl, {
+ toggle: false
+ })
- collapseEl.addEventListener('show.bs.collapse', () => {
- expect(collapseEl.style.height).toEqual('0px')
- })
- collapseEl.addEventListener('shown.bs.collapse', () => {
- expect(collapseEl.classList.contains('show')).toEqual(true)
- expect(collapseEl.style.height).toEqual('')
- done()
- })
+ collapseEl.addEventListener('show.bs.collapse', () => {
+ expect(collapseEl.style.height).toEqual('0px')
+ })
+ collapseEl.addEventListener('shown.bs.collapse', () => {
+ expect(collapseEl).toHaveClass('show')
+ expect(collapseEl.style.height).toEqual('')
+ resolve()
+ })
- collapse.show()
+ collapse.show()
+ })
})
- it('should show a collapsed element on width', done => {
- fixtureEl.innerHTML = '<div class="collapse collapse-horizontal" style="width: 0px;"></div>'
+ it('should show a collapsed element on width', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="collapse collapse-horizontal" style="width: 0px;"></div>'
- const collapseEl = fixtureEl.querySelector('div')
- const collapse = new Collapse(collapseEl, {
- toggle: false
- })
+ const collapseEl = fixtureEl.querySelector('div')
+ const collapse = new Collapse(collapseEl, {
+ toggle: false
+ })
- collapseEl.addEventListener('show.bs.collapse', () => {
- expect(collapseEl.style.width).toEqual('0px')
- })
- collapseEl.addEventListener('shown.bs.collapse', () => {
- expect(collapseEl.classList.contains('show')).toEqual(true)
- expect(collapseEl.style.width).toEqual('')
- done()
- })
+ collapseEl.addEventListener('show.bs.collapse', () => {
+ expect(collapseEl.style.width).toEqual('0px')
+ })
+ collapseEl.addEventListener('shown.bs.collapse', () => {
+ expect(collapseEl).toHaveClass('show')
+ expect(collapseEl.style.width).toEqual('')
+ resolve()
+ })
- collapse.show()
+ collapse.show()
+ })
})
- it('should collapse only the first collapse', done => {
- fixtureEl.innerHTML = [
- '<div class="card" id="accordion1">',
- ' <div id="collapse1" class="collapse"></div>',
- '</div>',
- '<div class="card" id="accordion2">',
- ' <div id="collapse2" class="collapse show"></div>',
- '</div>'
- ].join('')
+ it('should collapse only the first collapse', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="card" id="accordion1">',
+ ' <div id="collapse1" class="collapse"></div>',
+ '</div>',
+ '<div class="card" id="accordion2">',
+ ' <div id="collapse2" class="collapse show"></div>',
+ '</div>'
+ ].join('')
+
+ const el1 = fixtureEl.querySelector('#collapse1')
+ const el2 = fixtureEl.querySelector('#collapse2')
+ const collapse = new Collapse(el1, {
+ toggle: false
+ })
- const el1 = fixtureEl.querySelector('#collapse1')
- const el2 = fixtureEl.querySelector('#collapse2')
- const collapse = new Collapse(el1, {
- toggle: false
- })
+ el1.addEventListener('shown.bs.collapse', () => {
+ expect(el1).toHaveClass('show')
+ expect(el2).toHaveClass('show')
+ resolve()
+ })
- el1.addEventListener('shown.bs.collapse', () => {
- expect(el1.classList.contains('show')).toEqual(true)
- expect(el2.classList.contains('show')).toEqual(true)
- done()
+ collapse.show()
})
-
- collapse.show()
})
- it('should be able to handle toggling of other children siblings', done => {
- fixtureEl.innerHTML = [
- '<div id="parentGroup" class="accordion">',
- ' <div id="parentHeader" class="accordion-header">',
- ' <button data-bs-target="#parentContent" data-bs-toggle="collapse" role="button" class="accordion-toggle">Parent</button>',
- ' </div>',
- ' <div id="parentContent" class="accordion-collapse collapse" aria-labelledby="parentHeader" data-bs-parent="#parentGroup">',
- ' <div class="accordion-body">',
- ' <div id="childGroup" class="accordion">',
- ' <div class="accordion-item">',
- ' <div id="childHeader1" class="accordion-header">',
- ' <button data-bs-target="#childContent1" data-bs-toggle="collapse" role="button" class="accordion-toggle">Child 1</button>',
- ' </div>',
- ' <div id="childContent1" class="accordion-collapse collapse" aria-labelledby="childHeader1" data-bs-parent="#childGroup">',
- ' <div>content</div>',
- ' </div>',
- ' </div>',
- ' <div class="accordion-item">',
- ' <div id="childHeader2" class="accordion-header">',
- ' <button data-bs-target="#childContent2" data-bs-toggle="collapse" role="button" class="accordion-toggle">Child 2</button>',
- ' </div>',
- ' <div id="childContent2" class="accordion-collapse collapse" aria-labelledby="childHeader2" data-bs-parent="#childGroup">',
- ' <div>content</div>',
- ' </div>',
- ' </div>',
- ' </div>',
- ' </div>',
- ' </div>',
- '</div>'
- ].join('')
-
- const el = selector => fixtureEl.querySelector(selector)
-
- const parentBtn = el('[data-bs-target="#parentContent"]')
- const childBtn1 = el('[data-bs-target="#childContent1"]')
- const childBtn2 = el('[data-bs-target="#childContent2"]')
-
- const parentCollapseEl = el('#parentContent')
- const childCollapseEl1 = el('#childContent1')
- const childCollapseEl2 = el('#childContent2')
+ it('should be able to handle toggling of other children siblings', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="parentGroup" class="accordion">',
+ ' <div class="accordion-header">',
+ ' <button data-bs-target="#parentContent" data-bs-toggle="collapse" class="accordion-toggle">Parent</button>',
+ ' </div>',
+ ' <div id="parentContent" class="accordion-collapse collapse" data-bs-parent="#parentGroup">',
+ ' <div class="accordion-body">',
+ ' <div id="childGroup" class="accordion">',
+ ' <div class="accordion-item">',
+ ' <div class="accordion-header">',
+ ' <button data-bs-target="#childContent1" data-bs-toggle="collapse" class="accordion-toggle">Child 1</button>',
+ ' </div>',
+ ' <div id="childContent1" class="accordion-collapse collapse" data-bs-parent="#childGroup">',
+ ' <div>content</div>',
+ ' </div>',
+ ' </div>',
+ ' <div class="accordion-item">',
+ ' <div class="accordion-header">',
+ ' <button data-bs-target="#childContent2" data-bs-toggle="collapse" class="accordion-toggle">Child 2</button>',
+ ' </div>',
+ ' <div id="childContent2" class="accordion-collapse collapse" data-bs-parent="#childGroup">',
+ ' <div>content</div>',
+ ' </div>',
+ ' </div>',
+ ' </div>',
+ ' </div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const el = selector => fixtureEl.querySelector(selector)
+
+ const parentBtn = el('[data-bs-target="#parentContent"]')
+ const childBtn1 = el('[data-bs-target="#childContent1"]')
+ const childBtn2 = el('[data-bs-target="#childContent2"]')
+
+ const parentCollapseEl = el('#parentContent')
+ const childCollapseEl1 = el('#childContent1')
+ const childCollapseEl2 = el('#childContent2')
+
+ parentCollapseEl.addEventListener('shown.bs.collapse', () => {
+ expect(parentCollapseEl).toHaveClass('show')
+ childBtn1.click()
+ })
+ childCollapseEl1.addEventListener('shown.bs.collapse', () => {
+ expect(childCollapseEl1).toHaveClass('show')
+ childBtn2.click()
+ })
+ childCollapseEl2.addEventListener('shown.bs.collapse', () => {
+ expect(childCollapseEl2).toHaveClass('show')
+ expect(childCollapseEl1).not.toHaveClass('show')
+ resolve()
+ })
- parentCollapseEl.addEventListener('shown.bs.collapse', () => {
- expect(parentCollapseEl.classList.contains('show')).toEqual(true)
- childBtn1.click()
+ parentBtn.click()
})
- childCollapseEl1.addEventListener('shown.bs.collapse', () => {
- expect(childCollapseEl1.classList.contains('show')).toEqual(true)
- childBtn2.click()
- })
- childCollapseEl2.addEventListener('shown.bs.collapse', () => {
- expect(childCollapseEl2.classList.contains('show')).toEqual(true)
- expect(childCollapseEl1.classList.contains('show')).toEqual(false)
- done()
- })
-
- parentBtn.click()
})
- it('should not change tab tabpanels descendants on accordion', done => {
- fixtureEl.innerHTML = [
- '<div class="accordion" id="accordionExample">',
- ' <div class="accordion-item">',
- ' <h2 class="accordion-header" id="headingOne">',
- ' <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">',
- ' Accordion Item #1',
- ' </button>',
- ' </h2>',
- ' <div id="collapseOne" class="accordion-collapse collapse show" aria-labelledby="headingOne" data-bs-parent="#accordionExample">',
- ' <div class="accordion-body">',
- ' <nav>',
- ' <div class="nav nav-tabs" id="nav-tab" role="tablist">',
- ' <button class="nav-link active" id="nav-home-tab" data-bs-toggle="tab" data-bs-target="#nav-home" type="button" role="tab" aria-controls="nav-home" aria-selected="true">Home</button>',
- ' <button class="nav-link" id="nav-profile-tab" data-bs-toggle="tab" data-bs-target="#nav-profile" type="button" role="tab" aria-controls="nav-profile" aria-selected="false">Profile</button>',
- ' </div>',
- ' </nav>',
- ' <div class="tab-content" id="nav-tabContent">',
- ' <div class="tab-pane fade show active" id="nav-home" role="tabpanel" aria-labelledby="nav-home-tab">Home</div>',
- ' <div class="tab-pane fade" id="nav-profile" role="tabpanel" aria-labelledby="nav-profile-tab">Profile</div>',
- ' </div>',
- ' </div>',
- ' </div>',
- ' </div>',
- ' </div>'
- ].join('')
- const el = fixtureEl.querySelector('#collapseOne')
- const activeTabPane = fixtureEl.querySelector('#nav-home')
- const collapse = new Collapse(el)
- let times = 1
+ it('should not change tab tabpanels descendants on accordion', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="accordion" id="accordionExample">',
+ ' <div class="accordion-item">',
+ ' <h2 class="accordion-header">',
+ ' <button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne" aria-expanded="true" aria-controls="collapseOne">',
+ ' Accordion Item #1',
+ ' </button>',
+ ' </h2>',
+ ' <div id="collapseOne" class="accordion-collapse collapse show" data-bs-parent="#accordionExample">',
+ ' <div class="accordion-body">',
+ ' <nav>',
+ ' <div class="nav nav-tabs" id="nav-tab" role="tablist">',
+ ' <button class="nav-link active" id="nav-home-tab" data-bs-toggle="tab" data-bs-target="#nav-home" type="button" role="tab" aria-controls="nav-home" aria-selected="true">Home</button>',
+ ' <button class="nav-link" id="nav-profile-tab" data-bs-toggle="tab" data-bs-target="#nav-profile" type="button" role="tab" aria-controls="nav-profile" aria-selected="false">Profile</button>',
+ ' </div>',
+ ' </nav>',
+ ' <div class="tab-content" id="nav-tabContent">',
+ ' <div class="tab-pane fade show active" id="nav-home" role="tabpanel" aria-labelledby="nav-home-tab">Home</div>',
+ ' <div class="tab-pane fade" id="nav-profile" role="tabpanel" aria-labelledby="nav-profile-tab">Profile</div>',
+ ' </div>',
+ ' </div>',
+ ' </div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const el = fixtureEl.querySelector('#collapseOne')
+ const activeTabPane = fixtureEl.querySelector('#nav-home')
+ const collapse = new Collapse(el)
+ let times = 1
+
+ el.addEventListener('hidden.bs.collapse', () => {
+ collapse.show()
+ })
- el.addEventListener('hidden.bs.collapse', () => {
- collapse.show()
- })
+ el.addEventListener('shown.bs.collapse', () => {
+ expect(activeTabPane).toHaveClass('show')
+ times++
+ if (times === 2) {
+ resolve()
+ }
- el.addEventListener('shown.bs.collapse', () => {
- expect(activeTabPane.classList.contains('show')).toEqual(true)
- times++
- if (times === 2) {
- done()
- }
+ collapse.hide()
+ })
- collapse.hide()
+ collapse.show()
})
-
- collapse.show()
})
- it('should not fire shown when show is prevented', done => {
- fixtureEl.innerHTML = '<div class="collapse"></div>'
+ it('should not fire shown when show is prevented', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = '<div class="collapse"></div>'
- const collapseEl = fixtureEl.querySelector('div')
- const collapse = new Collapse(collapseEl, {
- toggle: false
- })
+ const collapseEl = fixtureEl.querySelector('div')
+ const collapse = new Collapse(collapseEl, {
+ toggle: false
+ })
- const expectEnd = () => {
- setTimeout(() => {
- expect().nothing()
- done()
- }, 10)
- }
+ const expectEnd = () => {
+ setTimeout(() => {
+ expect().nothing()
+ resolve()
+ }, 10)
+ }
- collapseEl.addEventListener('show.bs.collapse', event => {
- event.preventDefault()
- expectEnd()
- })
+ collapseEl.addEventListener('show.bs.collapse', event => {
+ event.preventDefault()
+ expectEnd()
+ })
- collapseEl.addEventListener('shown.bs.collapse', () => {
- throw new Error('should not fire shown event')
- })
+ collapseEl.addEventListener('shown.bs.collapse', () => {
+ reject(new Error('should not fire shown event'))
+ })
- collapse.show()
+ collapse.show()
+ })
})
})
@@ -403,7 +418,7 @@ describe('Collapse', () => {
it('should do nothing if is transitioning', () => {
fixtureEl.innerHTML = '<div></div>'
- spyOn(EventHandler, 'trigger')
+ const spy = spyOn(EventHandler, 'trigger')
const collapseEl = fixtureEl.querySelector('div')
const collapse = new Collapse(collapseEl, {
@@ -413,13 +428,13 @@ describe('Collapse', () => {
collapse._isTransitioning = true
collapse.hide()
- expect(EventHandler.trigger).not.toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
})
it('should do nothing if already shown', () => {
fixtureEl.innerHTML = '<div></div>'
- spyOn(EventHandler, 'trigger')
+ const spy = spyOn(EventHandler, 'trigger')
const collapseEl = fixtureEl.querySelector('div')
const collapse = new Collapse(collapseEl, {
@@ -428,51 +443,55 @@ describe('Collapse', () => {
collapse.hide()
- expect(EventHandler.trigger).not.toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
})
- it('should hide a collapsed element', done => {
- fixtureEl.innerHTML = '<div class="collapse show"></div>'
+ it('should hide a collapsed element', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="collapse show"></div>'
- const collapseEl = fixtureEl.querySelector('div')
- const collapse = new Collapse(collapseEl, {
- toggle: false
- })
+ const collapseEl = fixtureEl.querySelector('div')
+ const collapse = new Collapse(collapseEl, {
+ toggle: false
+ })
- collapseEl.addEventListener('hidden.bs.collapse', () => {
- expect(collapseEl.classList.contains('show')).toEqual(false)
- expect(collapseEl.style.height).toEqual('')
- done()
- })
+ collapseEl.addEventListener('hidden.bs.collapse', () => {
+ expect(collapseEl).not.toHaveClass('show')
+ expect(collapseEl.style.height).toEqual('')
+ resolve()
+ })
- collapse.hide()
+ collapse.hide()
+ })
})
- it('should not fire hidden when hide is prevented', done => {
- fixtureEl.innerHTML = '<div class="collapse show"></div>'
+ it('should not fire hidden when hide is prevented', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = '<div class="collapse show"></div>'
- const collapseEl = fixtureEl.querySelector('div')
- const collapse = new Collapse(collapseEl, {
- toggle: false
- })
+ const collapseEl = fixtureEl.querySelector('div')
+ const collapse = new Collapse(collapseEl, {
+ toggle: false
+ })
- const expectEnd = () => {
- setTimeout(() => {
- expect().nothing()
- done()
- }, 10)
- }
+ const expectEnd = () => {
+ setTimeout(() => {
+ expect().nothing()
+ resolve()
+ }, 10)
+ }
- collapseEl.addEventListener('hide.bs.collapse', event => {
- event.preventDefault()
- expectEnd()
- })
+ collapseEl.addEventListener('hide.bs.collapse', event => {
+ event.preventDefault()
+ expectEnd()
+ })
- collapseEl.addEventListener('hidden.bs.collapse', () => {
- throw new Error('should not fire hidden event')
- })
+ collapseEl.addEventListener('hidden.bs.collapse', () => {
+ reject(new Error('should not fire hidden event'))
+ })
- collapse.hide()
+ collapse.hide()
+ })
})
})
@@ -489,416 +508,438 @@ describe('Collapse', () => {
collapse.dispose()
- expect(Collapse.getInstance(collapseEl)).toEqual(null)
+ expect(Collapse.getInstance(collapseEl)).toBeNull()
})
})
describe('data-api', () => {
- it('should prevent url change if click on nested elements', done => {
- fixtureEl.innerHTML = [
- '<a role="button" data-bs-toggle="collapse" class="collapsed" href="#collapse">',
- ' <span id="nested"></span>',
- '</a>',
- '<div id="collapse" class="collapse"></div>'
- ].join('')
-
- const triggerEl = fixtureEl.querySelector('a')
- const nestedTriggerEl = fixtureEl.querySelector('#nested')
-
- spyOn(Event.prototype, 'preventDefault').and.callThrough()
+ it('should prevent url change if click on nested elements', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<a role="button" data-bs-toggle="collapse" class="collapsed" href="#collapse">',
+ ' <span id="nested"></span>',
+ '</a>',
+ '<div id="collapse" class="collapse"></div>'
+ ].join('')
+
+ const triggerEl = fixtureEl.querySelector('a')
+ const nestedTriggerEl = fixtureEl.querySelector('#nested')
+
+ const spy = spyOn(Event.prototype, 'preventDefault').and.callThrough()
+
+ triggerEl.addEventListener('click', event => {
+ expect(event.target.isEqualNode(nestedTriggerEl)).toBeTrue()
+ expect(event.delegateTarget.isEqualNode(triggerEl)).toBeTrue()
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- triggerEl.addEventListener('click', event => {
- expect(event.target.isEqualNode(nestedTriggerEl)).toEqual(true)
- expect(event.delegateTarget.isEqualNode(triggerEl)).toEqual(true)
- expect(Event.prototype.preventDefault).toHaveBeenCalled()
- done()
+ nestedTriggerEl.click()
})
-
- nestedTriggerEl.click()
})
- it('should show multiple collapsed elements', done => {
- fixtureEl.innerHTML = [
- '<a role="button" data-bs-toggle="collapse" class="collapsed" href=".multi"></a>',
- '<div id="collapse1" class="collapse multi"></div>',
- '<div id="collapse2" class="collapse multi"></div>'
- ].join('')
-
- const trigger = fixtureEl.querySelector('a')
- const collapse1 = fixtureEl.querySelector('#collapse1')
- const collapse2 = fixtureEl.querySelector('#collapse2')
+ it('should show multiple collapsed elements', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<a role="button" data-bs-toggle="collapse" class="collapsed" href=".multi"></a>',
+ '<div id="collapse1" class="collapse multi"></div>',
+ '<div id="collapse2" class="collapse multi"></div>'
+ ].join('')
+
+ const trigger = fixtureEl.querySelector('a')
+ const collapse1 = fixtureEl.querySelector('#collapse1')
+ const collapse2 = fixtureEl.querySelector('#collapse2')
+
+ collapse2.addEventListener('shown.bs.collapse', () => {
+ expect(trigger.getAttribute('aria-expanded')).toEqual('true')
+ expect(trigger).not.toHaveClass('collapsed')
+ expect(collapse1).toHaveClass('show')
+ expect(collapse1).toHaveClass('show')
+ resolve()
+ })
- collapse2.addEventListener('shown.bs.collapse', () => {
- expect(trigger.getAttribute('aria-expanded')).toEqual('true')
- expect(trigger.classList.contains('collapsed')).toEqual(false)
- expect(collapse1.classList.contains('show')).toEqual(true)
- expect(collapse1.classList.contains('show')).toEqual(true)
- done()
+ trigger.click()
})
-
- trigger.click()
})
- it('should hide multiple collapsed elements', done => {
- fixtureEl.innerHTML = [
- '<a role="button" data-bs-toggle="collapse" href=".multi"></a>',
- '<div id="collapse1" class="collapse multi show"></div>',
- '<div id="collapse2" class="collapse multi show"></div>'
- ].join('')
-
- const trigger = fixtureEl.querySelector('a')
- const collapse1 = fixtureEl.querySelector('#collapse1')
- const collapse2 = fixtureEl.querySelector('#collapse2')
+ it('should hide multiple collapsed elements', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<a role="button" data-bs-toggle="collapse" href=".multi"></a>',
+ '<div id="collapse1" class="collapse multi show"></div>',
+ '<div id="collapse2" class="collapse multi show"></div>'
+ ].join('')
+
+ const trigger = fixtureEl.querySelector('a')
+ const collapse1 = fixtureEl.querySelector('#collapse1')
+ const collapse2 = fixtureEl.querySelector('#collapse2')
+
+ collapse2.addEventListener('hidden.bs.collapse', () => {
+ expect(trigger.getAttribute('aria-expanded')).toEqual('false')
+ expect(trigger).toHaveClass('collapsed')
+ expect(collapse1).not.toHaveClass('show')
+ expect(collapse1).not.toHaveClass('show')
+ resolve()
+ })
- collapse2.addEventListener('hidden.bs.collapse', () => {
- expect(trigger.getAttribute('aria-expanded')).toEqual('false')
- expect(trigger.classList.contains('collapsed')).toEqual(true)
- expect(collapse1.classList.contains('show')).toEqual(false)
- expect(collapse1.classList.contains('show')).toEqual(false)
- done()
+ trigger.click()
})
-
- trigger.click()
})
- it('should remove "collapsed" class from target when collapse is shown', done => {
- fixtureEl.innerHTML = [
- '<a id="link1" role="button" data-bs-toggle="collapse" class="collapsed" href="#" data-bs-target="#test1"></a>',
- '<a id="link2" role="button" data-bs-toggle="collapse" class="collapsed" href="#" data-bs-target="#test1"></a>',
- '<div id="test1"></div>'
- ].join('')
-
- const link1 = fixtureEl.querySelector('#link1')
- const link2 = fixtureEl.querySelector('#link2')
- const collapseTest1 = fixtureEl.querySelector('#test1')
+ it('should remove "collapsed" class from target when collapse is shown', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<a id="link1" role="button" data-bs-toggle="collapse" class="collapsed" href="#" data-bs-target="#test1"></a>',
+ '<a id="link2" role="button" data-bs-toggle="collapse" class="collapsed" href="#" data-bs-target="#test1"></a>',
+ '<div id="test1"></div>'
+ ].join('')
+
+ const link1 = fixtureEl.querySelector('#link1')
+ const link2 = fixtureEl.querySelector('#link2')
+ const collapseTest1 = fixtureEl.querySelector('#test1')
+
+ collapseTest1.addEventListener('shown.bs.collapse', () => {
+ expect(link1.getAttribute('aria-expanded')).toEqual('true')
+ expect(link2.getAttribute('aria-expanded')).toEqual('true')
+ expect(link1).not.toHaveClass('collapsed')
+ expect(link2).not.toHaveClass('collapsed')
+ resolve()
+ })
- collapseTest1.addEventListener('shown.bs.collapse', () => {
- expect(link1.getAttribute('aria-expanded')).toEqual('true')
- expect(link2.getAttribute('aria-expanded')).toEqual('true')
- expect(link1.classList.contains('collapsed')).toEqual(false)
- expect(link2.classList.contains('collapsed')).toEqual(false)
- done()
+ link1.click()
})
-
- link1.click()
})
- it('should add "collapsed" class to target when collapse is hidden', done => {
- fixtureEl.innerHTML = [
- '<a id="link1" role="button" data-bs-toggle="collapse" href="#" data-bs-target="#test1"></a>',
- '<a id="link2" role="button" data-bs-toggle="collapse" href="#" data-bs-target="#test1"></a>',
- '<div id="test1" class="show"></div>'
- ].join('')
-
- const link1 = fixtureEl.querySelector('#link1')
- const link2 = fixtureEl.querySelector('#link2')
- const collapseTest1 = fixtureEl.querySelector('#test1')
+ it('should add "collapsed" class to target when collapse is hidden', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<a id="link1" role="button" data-bs-toggle="collapse" href="#" data-bs-target="#test1"></a>',
+ '<a id="link2" role="button" data-bs-toggle="collapse" href="#" data-bs-target="#test1"></a>',
+ '<div id="test1" class="show"></div>'
+ ].join('')
+
+ const link1 = fixtureEl.querySelector('#link1')
+ const link2 = fixtureEl.querySelector('#link2')
+ const collapseTest1 = fixtureEl.querySelector('#test1')
+
+ collapseTest1.addEventListener('hidden.bs.collapse', () => {
+ expect(link1.getAttribute('aria-expanded')).toEqual('false')
+ expect(link2.getAttribute('aria-expanded')).toEqual('false')
+ expect(link1).toHaveClass('collapsed')
+ expect(link2).toHaveClass('collapsed')
+ resolve()
+ })
- collapseTest1.addEventListener('hidden.bs.collapse', () => {
- expect(link1.getAttribute('aria-expanded')).toEqual('false')
- expect(link2.getAttribute('aria-expanded')).toEqual('false')
- expect(link1.classList.contains('collapsed')).toEqual(true)
- expect(link2.classList.contains('collapsed')).toEqual(true)
- done()
+ link1.click()
})
-
- link1.click()
})
- it('should allow accordion to use children other than card', done => {
- fixtureEl.innerHTML = [
- '<div id="accordion">',
- ' <div class="item">',
- ' <a id="linkTrigger" data-bs-toggle="collapse" href="#collapseOne" aria-expanded="false" aria-controls="collapseOne"></a>',
- ' <div id="collapseOne" class="collapse" role="tabpanel" aria-labelledby="headingThree" data-bs-parent="#accordion"></div>',
- ' </div>',
- ' <div class="item">',
- ' <a id="linkTriggerTwo" data-bs-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"></a>',
- ' <div id="collapseTwo" class="collapse show" role="tabpanel" aria-labelledby="headingTwo" data-bs-parent="#accordion"></div>',
- ' </div>',
- '</div>'
- ].join('')
-
- const trigger = fixtureEl.querySelector('#linkTrigger')
- const triggerTwo = fixtureEl.querySelector('#linkTriggerTwo')
- const collapseOne = fixtureEl.querySelector('#collapseOne')
- const collapseTwo = fixtureEl.querySelector('#collapseTwo')
-
- collapseOne.addEventListener('shown.bs.collapse', () => {
- expect(collapseOne.classList.contains('show')).toEqual(true)
- expect(collapseTwo.classList.contains('show')).toEqual(false)
+ it('should allow accordion to use children other than card', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="accordion">',
+ ' <div class="item">',
+ ' <a id="linkTrigger" data-bs-toggle="collapse" href="#collapseOne" aria-expanded="false" aria-controls="collapseOne"></a>',
+ ' <div id="collapseOne" class="collapse" role="tabpanel" data-bs-parent="#accordion"></div>',
+ ' </div>',
+ ' <div class="item">',
+ ' <a id="linkTriggerTwo" data-bs-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"></a>',
+ ' <div id="collapseTwo" class="collapse show" role="tabpanel" data-bs-parent="#accordion"></div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const trigger = fixtureEl.querySelector('#linkTrigger')
+ const triggerTwo = fixtureEl.querySelector('#linkTriggerTwo')
+ const collapseOne = fixtureEl.querySelector('#collapseOne')
+ const collapseTwo = fixtureEl.querySelector('#collapseTwo')
+
+ collapseOne.addEventListener('shown.bs.collapse', () => {
+ expect(collapseOne).toHaveClass('show')
+ expect(collapseTwo).not.toHaveClass('show')
+
+ collapseTwo.addEventListener('shown.bs.collapse', () => {
+ expect(collapseOne).not.toHaveClass('show')
+ expect(collapseTwo).toHaveClass('show')
+ resolve()
+ })
- collapseTwo.addEventListener('shown.bs.collapse', () => {
- expect(collapseOne.classList.contains('show')).toEqual(false)
- expect(collapseTwo.classList.contains('show')).toEqual(true)
- done()
+ triggerTwo.click()
})
- triggerTwo.click()
+ trigger.click()
})
-
- trigger.click()
})
- it('should not prevent event for input', done => {
- fixtureEl.innerHTML = [
- '<input type="checkbox" data-bs-toggle="collapse" data-bs-target="#collapsediv1">',
- '<div id="collapsediv1"></div>'
- ].join('')
+ it('should not prevent event for input', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<input type="checkbox" data-bs-toggle="collapse" data-bs-target="#collapsediv1">',
+ '<div id="collapsediv1"></div>'
+ ].join('')
- const target = fixtureEl.querySelector('input')
- const collapseEl = fixtureEl.querySelector('#collapsediv1')
+ const target = fixtureEl.querySelector('input')
+ const collapseEl = fixtureEl.querySelector('#collapsediv1')
- collapseEl.addEventListener('shown.bs.collapse', () => {
- expect(collapseEl.classList.contains('show')).toEqual(true)
- expect(target.checked).toEqual(true)
- done()
- })
+ collapseEl.addEventListener('shown.bs.collapse', () => {
+ expect(collapseEl).toHaveClass('show')
+ expect(target.checked).toBeTrue()
+ resolve()
+ })
- target.click()
+ target.click()
+ })
})
- it('should allow accordion to contain nested elements', done => {
- fixtureEl.innerHTML = [
- '<div id="accordion">',
- ' <div class="row">',
- ' <div class="col-lg-6">',
- ' <div class="item">',
- ' <a id="linkTrigger" data-bs-toggle="collapse" href="#collapseOne" aria-expanded="false" aria-controls="collapseOne"></a>',
- ' <div id="collapseOne" class="collapse" role="tabpanel" aria-labelledby="headingThree" data-bs-parent="#accordion"></div>',
- ' </div>',
- ' </div>',
- ' <div class="col-lg-6">',
- ' <div class="item">',
- ' <a id="linkTriggerTwo" data-bs-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"></a>',
- ' <div id="collapseTwo" class="collapse show" role="tabpanel" aria-labelledby="headingTwo" data-bs-parent="#accordion"></div>',
- ' </div>',
- ' </div>',
- ' </div>',
- '</div>'
- ].join('')
-
- const triggerEl = fixtureEl.querySelector('#linkTrigger')
- const triggerTwoEl = fixtureEl.querySelector('#linkTriggerTwo')
- const collapseOneEl = fixtureEl.querySelector('#collapseOne')
- const collapseTwoEl = fixtureEl.querySelector('#collapseTwo')
-
- collapseOneEl.addEventListener('shown.bs.collapse', () => {
- expect(collapseOneEl.classList.contains('show')).toEqual(true)
- expect(triggerEl.classList.contains('collapsed')).toEqual(false)
- expect(triggerEl.getAttribute('aria-expanded')).toEqual('true')
-
- expect(collapseTwoEl.classList.contains('show')).toEqual(false)
- expect(triggerTwoEl.classList.contains('collapsed')).toEqual(true)
- expect(triggerTwoEl.getAttribute('aria-expanded')).toEqual('false')
-
- collapseTwoEl.addEventListener('shown.bs.collapse', () => {
- expect(collapseOneEl.classList.contains('show')).toEqual(false)
- expect(triggerEl.classList.contains('collapsed')).toEqual(true)
- expect(triggerEl.getAttribute('aria-expanded')).toEqual('false')
+ it('should allow accordion to contain nested elements', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="accordion">',
+ ' <div class="row">',
+ ' <div class="col-lg-6">',
+ ' <div class="item">',
+ ' <a id="linkTrigger" data-bs-toggle="collapse" href="#collapseOne" aria-expanded="false" aria-controls="collapseOne"></a>',
+ ' <div id="collapseOne" class="collapse" role="tabpanel" data-bs-parent="#accordion"></div>',
+ ' </div>',
+ ' </div>',
+ ' <div class="col-lg-6">',
+ ' <div class="item">',
+ ' <a id="linkTriggerTwo" data-bs-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"></a>',
+ ' <div id="collapseTwo" class="collapse show" role="tabpanel" data-bs-parent="#accordion"></div>',
+ ' </div>',
+ ' </div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const triggerEl = fixtureEl.querySelector('#linkTrigger')
+ const triggerTwoEl = fixtureEl.querySelector('#linkTriggerTwo')
+ const collapseOneEl = fixtureEl.querySelector('#collapseOne')
+ const collapseTwoEl = fixtureEl.querySelector('#collapseTwo')
+
+ collapseOneEl.addEventListener('shown.bs.collapse', () => {
+ expect(collapseOneEl).toHaveClass('show')
+ expect(triggerEl).not.toHaveClass('collapsed')
+ expect(triggerEl.getAttribute('aria-expanded')).toEqual('true')
+
+ expect(collapseTwoEl).not.toHaveClass('show')
+ expect(triggerTwoEl).toHaveClass('collapsed')
+ expect(triggerTwoEl.getAttribute('aria-expanded')).toEqual('false')
+
+ collapseTwoEl.addEventListener('shown.bs.collapse', () => {
+ expect(collapseOneEl).not.toHaveClass('show')
+ expect(triggerEl).toHaveClass('collapsed')
+ expect(triggerEl.getAttribute('aria-expanded')).toEqual('false')
+
+ expect(collapseTwoEl).toHaveClass('show')
+ expect(triggerTwoEl).not.toHaveClass('collapsed')
+ expect(triggerTwoEl.getAttribute('aria-expanded')).toEqual('true')
+ resolve()
+ })
- expect(collapseTwoEl.classList.contains('show')).toEqual(true)
- expect(triggerTwoEl.classList.contains('collapsed')).toEqual(false)
- expect(triggerTwoEl.getAttribute('aria-expanded')).toEqual('true')
- done()
+ triggerTwoEl.click()
})
- triggerTwoEl.click()
+ triggerEl.click()
})
-
- triggerEl.click()
})
- it('should allow accordion to target multiple elements', done => {
- fixtureEl.innerHTML = [
- '<div id="accordion">',
- ' <a id="linkTriggerOne" data-bs-toggle="collapse" data-bs-target=".collapseOne" href="#" aria-expanded="false" aria-controls="collapseOne"></a>',
- ' <a id="linkTriggerTwo" data-bs-toggle="collapse" data-bs-target=".collapseTwo" href="#" aria-expanded="false" aria-controls="collapseTwo"></a>',
- ' <div id="collapseOneOne" class="collapse collapseOne" role="tabpanel" data-bs-parent="#accordion"></div>',
- ' <div id="collapseOneTwo" class="collapse collapseOne" role="tabpanel" data-bs-parent="#accordion"></div>',
- ' <div id="collapseTwoOne" class="collapse collapseTwo" role="tabpanel" data-bs-parent="#accordion"></div>',
- ' <div id="collapseTwoTwo" class="collapse collapseTwo" role="tabpanel" data-bs-parent="#accordion"></div>',
- '</div>'
- ].join('')
-
- const trigger = fixtureEl.querySelector('#linkTriggerOne')
- const triggerTwo = fixtureEl.querySelector('#linkTriggerTwo')
- const collapseOneOne = fixtureEl.querySelector('#collapseOneOne')
- const collapseOneTwo = fixtureEl.querySelector('#collapseOneTwo')
- const collapseTwoOne = fixtureEl.querySelector('#collapseTwoOne')
- const collapseTwoTwo = fixtureEl.querySelector('#collapseTwoTwo')
- const collapsedElements = {
- one: false,
- two: false
- }
-
- function firstTest() {
- expect(collapseOneOne.classList.contains('show')).toEqual(true)
- expect(collapseOneTwo.classList.contains('show')).toEqual(true)
-
- expect(collapseTwoOne.classList.contains('show')).toEqual(false)
- expect(collapseTwoTwo.classList.contains('show')).toEqual(false)
-
- triggerTwo.click()
- }
-
- function secondTest() {
- expect(collapseOneOne.classList.contains('show')).toEqual(false)
- expect(collapseOneTwo.classList.contains('show')).toEqual(false)
-
- expect(collapseTwoOne.classList.contains('show')).toEqual(true)
- expect(collapseTwoTwo.classList.contains('show')).toEqual(true)
- done()
- }
-
- collapseOneOne.addEventListener('shown.bs.collapse', () => {
- if (collapsedElements.one) {
- firstTest()
- } else {
- collapsedElements.one = true
+ it('should allow accordion to target multiple elements', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="accordion">',
+ ' <a id="linkTriggerOne" data-bs-toggle="collapse" data-bs-target=".collapseOne" href="#" aria-expanded="false" aria-controls="collapseOne"></a>',
+ ' <a id="linkTriggerTwo" data-bs-toggle="collapse" data-bs-target=".collapseTwo" href="#" aria-expanded="false" aria-controls="collapseTwo"></a>',
+ ' <div id="collapseOneOne" class="collapse collapseOne" role="tabpanel" data-bs-parent="#accordion"></div>',
+ ' <div id="collapseOneTwo" class="collapse collapseOne" role="tabpanel" data-bs-parent="#accordion"></div>',
+ ' <div id="collapseTwoOne" class="collapse collapseTwo" role="tabpanel" data-bs-parent="#accordion"></div>',
+ ' <div id="collapseTwoTwo" class="collapse collapseTwo" role="tabpanel" data-bs-parent="#accordion"></div>',
+ '</div>'
+ ].join('')
+
+ const trigger = fixtureEl.querySelector('#linkTriggerOne')
+ const triggerTwo = fixtureEl.querySelector('#linkTriggerTwo')
+ const collapseOneOne = fixtureEl.querySelector('#collapseOneOne')
+ const collapseOneTwo = fixtureEl.querySelector('#collapseOneTwo')
+ const collapseTwoOne = fixtureEl.querySelector('#collapseTwoOne')
+ const collapseTwoTwo = fixtureEl.querySelector('#collapseTwoTwo')
+ const collapsedElements = {
+ one: false,
+ two: false
}
- })
- collapseOneTwo.addEventListener('shown.bs.collapse', () => {
- if (collapsedElements.one) {
- firstTest()
- } else {
- collapsedElements.one = true
- }
- })
+ function firstTest() {
+ expect(collapseOneOne).toHaveClass('show')
+ expect(collapseOneTwo).toHaveClass('show')
- collapseTwoOne.addEventListener('shown.bs.collapse', () => {
- if (collapsedElements.two) {
- secondTest()
- } else {
- collapsedElements.two = true
- }
- })
+ expect(collapseTwoOne).not.toHaveClass('show')
+ expect(collapseTwoTwo).not.toHaveClass('show')
- collapseTwoTwo.addEventListener('shown.bs.collapse', () => {
- if (collapsedElements.two) {
- secondTest()
- } else {
- collapsedElements.two = true
+ triggerTwo.click()
}
- })
- trigger.click()
- })
+ function secondTest() {
+ expect(collapseOneOne).not.toHaveClass('show')
+ expect(collapseOneTwo).not.toHaveClass('show')
- it('should collapse accordion children but not nested accordion children', done => {
- fixtureEl.innerHTML = [
- '<div id="accordion">',
- ' <div class="item">',
- ' <a id="linkTrigger" data-bs-toggle="collapse" href="#collapseOne" aria-expanded="false" aria-controls="collapseOne"></a>',
- ' <div id="collapseOne" data-bs-parent="#accordion" class="collapse" role="tabpanel" aria-labelledby="headingThree">',
- ' <div id="nestedAccordion">',
- ' <div class="item">',
- ' <a id="nestedLinkTrigger" data-bs-toggle="collapse" href="#nestedCollapseOne" aria-expanded="false" aria-controls="nestedCollapseOne"></a>',
- ' <div id="nestedCollapseOne" data-bs-parent="#nestedAccordion" class="collapse" role="tabpanel" aria-labelledby="headingThree"></div>',
- ' </div>',
- ' </div>',
- ' </div>',
- ' </div>',
- ' <div class="item">',
- ' <a id="linkTriggerTwo" data-bs-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"></a>',
- ' <div id="collapseTwo" data-bs-parent="#accordion" class="collapse show" role="tabpanel" aria-labelledby="headingTwo"></div>',
- ' </div>',
- '</div>'
- ].join('')
+ expect(collapseTwoOne).toHaveClass('show')
+ expect(collapseTwoTwo).toHaveClass('show')
+ resolve()
+ }
- const trigger = fixtureEl.querySelector('#linkTrigger')
- const triggerTwo = fixtureEl.querySelector('#linkTriggerTwo')
- const nestedTrigger = fixtureEl.querySelector('#nestedLinkTrigger')
- const collapseOne = fixtureEl.querySelector('#collapseOne')
- const collapseTwo = fixtureEl.querySelector('#collapseTwo')
- const nestedCollapseOne = fixtureEl.querySelector('#nestedCollapseOne')
-
- function handlerCollapseOne() {
- expect(collapseOne.classList.contains('show')).toEqual(true)
- expect(collapseTwo.classList.contains('show')).toEqual(false)
- expect(nestedCollapseOne.classList.contains('show')).toEqual(false)
-
- nestedCollapseOne.addEventListener('shown.bs.collapse', handlerNestedCollapseOne)
- nestedTrigger.click()
- collapseOne.removeEventListener('shown.bs.collapse', handlerCollapseOne)
- }
+ collapseOneOne.addEventListener('shown.bs.collapse', () => {
+ if (collapsedElements.one) {
+ firstTest()
+ } else {
+ collapsedElements.one = true
+ }
+ })
- function handlerNestedCollapseOne() {
- expect(collapseOne.classList.contains('show')).toEqual(true)
- expect(collapseTwo.classList.contains('show')).toEqual(false)
- expect(nestedCollapseOne.classList.contains('show')).toEqual(true)
+ collapseOneTwo.addEventListener('shown.bs.collapse', () => {
+ if (collapsedElements.one) {
+ firstTest()
+ } else {
+ collapsedElements.one = true
+ }
+ })
- collapseTwo.addEventListener('shown.bs.collapse', () => {
- expect(collapseOne.classList.contains('show')).toEqual(false)
- expect(collapseTwo.classList.contains('show')).toEqual(true)
- expect(nestedCollapseOne.classList.contains('show')).toEqual(true)
- done()
+ collapseTwoOne.addEventListener('shown.bs.collapse', () => {
+ if (collapsedElements.two) {
+ secondTest()
+ } else {
+ collapsedElements.two = true
+ }
})
- triggerTwo.click()
- nestedCollapseOne.removeEventListener('shown.bs.collapse', handlerNestedCollapseOne)
- }
+ collapseTwoTwo.addEventListener('shown.bs.collapse', () => {
+ if (collapsedElements.two) {
+ secondTest()
+ } else {
+ collapsedElements.two = true
+ }
+ })
- collapseOne.addEventListener('shown.bs.collapse', handlerCollapseOne)
- trigger.click()
+ trigger.click()
+ })
})
- it('should add "collapsed" class and set aria-expanded to triggers only when all the targeted collapse are hidden', done => {
- fixtureEl.innerHTML = [
- '<a id="trigger1" role="button" data-bs-toggle="collapse" href="#test1"></a>',
- '<a id="trigger2" role="button" data-bs-toggle="collapse" href="#test2"></a>',
- '<a id="trigger3" role="button" data-bs-toggle="collapse" href=".multi"></a>',
- '<div id="test1" class="multi"></div>',
- '<div id="test2" class="multi"></div>'
- ].join('')
+ it('should collapse accordion children but not nested accordion children', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="accordion">',
+ ' <div class="item">',
+ ' <a id="linkTrigger" data-bs-toggle="collapse" href="#collapseOne" aria-expanded="false" aria-controls="collapseOne"></a>',
+ ' <div id="collapseOne" data-bs-parent="#accordion" class="collapse" role="tabpanel">',
+ ' <div id="nestedAccordion">',
+ ' <div class="item">',
+ ' <a id="nestedLinkTrigger" data-bs-toggle="collapse" href="#nestedCollapseOne" aria-expanded="false" aria-controls="nestedCollapseOne"></a>',
+ ' <div id="nestedCollapseOne" data-bs-parent="#nestedAccordion" class="collapse" role="tabpanel"></div>',
+ ' </div>',
+ ' </div>',
+ ' </div>',
+ ' </div>',
+ ' <div class="item">',
+ ' <a id="linkTriggerTwo" data-bs-toggle="collapse" href="#collapseTwo" aria-expanded="false" aria-controls="collapseTwo"></a>',
+ ' <div id="collapseTwo" data-bs-parent="#accordion" class="collapse show" role="tabpanel"></div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const trigger = fixtureEl.querySelector('#linkTrigger')
+ const triggerTwo = fixtureEl.querySelector('#linkTriggerTwo')
+ const nestedTrigger = fixtureEl.querySelector('#nestedLinkTrigger')
+ const collapseOne = fixtureEl.querySelector('#collapseOne')
+ const collapseTwo = fixtureEl.querySelector('#collapseTwo')
+ const nestedCollapseOne = fixtureEl.querySelector('#nestedCollapseOne')
+
+ function handlerCollapseOne() {
+ expect(collapseOne).toHaveClass('show')
+ expect(collapseTwo).not.toHaveClass('show')
+ expect(nestedCollapseOne).not.toHaveClass('show')
+
+ nestedCollapseOne.addEventListener('shown.bs.collapse', handlerNestedCollapseOne)
+ nestedTrigger.click()
+ collapseOne.removeEventListener('shown.bs.collapse', handlerCollapseOne)
+ }
- const trigger1 = fixtureEl.querySelector('#trigger1')
- const trigger2 = fixtureEl.querySelector('#trigger2')
- const trigger3 = fixtureEl.querySelector('#trigger3')
- const target1 = fixtureEl.querySelector('#test1')
- const target2 = fixtureEl.querySelector('#test2')
+ function handlerNestedCollapseOne() {
+ expect(collapseOne).toHaveClass('show')
+ expect(collapseTwo).not.toHaveClass('show')
+ expect(nestedCollapseOne).toHaveClass('show')
- const target2Shown = () => {
- expect(trigger1.classList.contains('collapsed')).toEqual(false)
- expect(trigger1.getAttribute('aria-expanded')).toEqual('true')
+ collapseTwo.addEventListener('shown.bs.collapse', () => {
+ expect(collapseOne).not.toHaveClass('show')
+ expect(collapseTwo).toHaveClass('show')
+ expect(nestedCollapseOne).toHaveClass('show')
+ resolve()
+ })
- expect(trigger2.classList.contains('collapsed')).toEqual(false)
- expect(trigger2.getAttribute('aria-expanded')).toEqual('true')
+ triggerTwo.click()
+ nestedCollapseOne.removeEventListener('shown.bs.collapse', handlerNestedCollapseOne)
+ }
- expect(trigger3.classList.contains('collapsed')).toEqual(false)
- expect(trigger3.getAttribute('aria-expanded')).toEqual('true')
+ collapseOne.addEventListener('shown.bs.collapse', handlerCollapseOne)
+ trigger.click()
+ })
+ })
- target2.addEventListener('hidden.bs.collapse', () => {
- expect(trigger1.classList.contains('collapsed')).toEqual(false)
+ it('should add "collapsed" class and set aria-expanded to triggers only when all the targeted collapse are hidden', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<a id="trigger1" role="button" data-bs-toggle="collapse" href="#test1"></a>',
+ '<a id="trigger2" role="button" data-bs-toggle="collapse" href="#0/my/id"></a>',
+ '<a id="trigger3" role="button" data-bs-toggle="collapse" href=".multi"></a>',
+ '<div id="test1" class="multi"></div>',
+ '<div id="0/my/id" class="multi"></div>'
+ ].join('')
+
+ const trigger1 = fixtureEl.querySelector('#trigger1')
+ const trigger2 = fixtureEl.querySelector('#trigger2')
+ const trigger3 = fixtureEl.querySelector('#trigger3')
+ const target1 = fixtureEl.querySelector('#test1')
+ const target2 = fixtureEl.querySelector(`#${CSS.escape('0/my/id')}`)
+
+ const target2Shown = () => {
+ expect(trigger1).not.toHaveClass('collapsed')
expect(trigger1.getAttribute('aria-expanded')).toEqual('true')
- expect(trigger2.classList.contains('collapsed')).toEqual(true)
- expect(trigger2.getAttribute('aria-expanded')).toEqual('false')
+ expect(trigger2).not.toHaveClass('collapsed')
+ expect(trigger2.getAttribute('aria-expanded')).toEqual('true')
- expect(trigger3.classList.contains('collapsed')).toEqual(false)
+ expect(trigger3).not.toHaveClass('collapsed')
expect(trigger3.getAttribute('aria-expanded')).toEqual('true')
- target1.addEventListener('hidden.bs.collapse', () => {
- expect(trigger1.classList.contains('collapsed')).toEqual(true)
- expect(trigger1.getAttribute('aria-expanded')).toEqual('false')
+ target2.addEventListener('hidden.bs.collapse', () => {
+ expect(trigger1).not.toHaveClass('collapsed')
+ expect(trigger1.getAttribute('aria-expanded')).toEqual('true')
- expect(trigger2.classList.contains('collapsed')).toEqual(true)
+ expect(trigger2).toHaveClass('collapsed')
expect(trigger2.getAttribute('aria-expanded')).toEqual('false')
- expect(trigger3.classList.contains('collapsed')).toEqual(true)
- expect(trigger3.getAttribute('aria-expanded')).toEqual('false')
- done()
- })
+ expect(trigger3).not.toHaveClass('collapsed')
+ expect(trigger3.getAttribute('aria-expanded')).toEqual('true')
- trigger1.click()
- })
+ target1.addEventListener('hidden.bs.collapse', () => {
+ expect(trigger1).toHaveClass('collapsed')
+ expect(trigger1.getAttribute('aria-expanded')).toEqual('false')
- trigger2.click()
- }
+ expect(trigger2).toHaveClass('collapsed')
+ expect(trigger2.getAttribute('aria-expanded')).toEqual('false')
- target2.addEventListener('shown.bs.collapse', target2Shown)
- trigger3.click()
+ expect(trigger3).toHaveClass('collapsed')
+ expect(trigger3.getAttribute('aria-expanded')).toEqual('false')
+ resolve()
+ })
+
+ trigger1.click()
+ })
+
+ trigger2.click()
+ }
+
+ target2.addEventListener('shown.bs.collapse', target2Shown)
+ trigger3.click()
+ })
})
})
@@ -961,7 +1002,7 @@ describe('Collapse', () => {
const div = fixtureEl.querySelector('div')
- expect(Collapse.getInstance(div)).toEqual(null)
+ expect(Collapse.getInstance(div)).toBeNull()
})
})
@@ -982,7 +1023,7 @@ describe('Collapse', () => {
const div = fixtureEl.querySelector('div')
- expect(Collapse.getInstance(div)).toEqual(null)
+ expect(Collapse.getInstance(div)).toBeNull()
expect(Collapse.getOrCreateInstance(div)).toBeInstanceOf(Collapse)
})
@@ -991,13 +1032,13 @@ describe('Collapse', () => {
const div = fixtureEl.querySelector('div')
- expect(Collapse.getInstance(div)).toEqual(null)
+ expect(Collapse.getInstance(div)).toBeNull()
const collapse = Collapse.getOrCreateInstance(div, {
toggle: false
})
expect(collapse).toBeInstanceOf(Collapse)
- expect(collapse._config.toggle).toEqual(false)
+ expect(collapse._config.toggle).toBeFalse()
})
it('should return the instance when exists without given configuration', () => {
@@ -1015,7 +1056,7 @@ describe('Collapse', () => {
expect(collapse).toBeInstanceOf(Collapse)
expect(collapse2).toEqual(collapse)
- expect(collapse2._config.toggle).toEqual(false)
+ expect(collapse2._config.toggle).toBeFalse()
})
})
})
diff --git a/js/tests/unit/dom/data.spec.js b/js/tests/unit/dom/data.spec.js
index 2560caff7..04e57a8bc 100644
--- a/js/tests/unit/dom/data.spec.js
+++ b/js/tests/unit/dom/data.spec.js
@@ -1,5 +1,5 @@
-import Data from '../../../src/dom/data'
-import { getFixture, clearFixture } from '../../helpers/fixture'
+import Data from '../../../src/dom/data.js'
+import { clearFixture, getFixture } from '../../helpers/fixture.js'
describe('Data', () => {
const TEST_KEY = 'bs.test'
@@ -50,7 +50,7 @@ describe('Data', () => {
Data.set(div, TEST_KEY, data)
- expect(Data.get(div, TEST_KEY)).toBe(data)
+ expect(Data.get(div, TEST_KEY)).toEqual(data)
})
it('should overwrite data if something is already stored', () => {
@@ -60,11 +60,12 @@ describe('Data', () => {
Data.set(div, TEST_KEY, data)
Data.set(div, TEST_KEY, copy)
+ // Using `toBe` since spread creates a shallow copy
expect(Data.get(div, TEST_KEY)).not.toBe(data)
expect(Data.get(div, TEST_KEY)).toBe(copy)
})
- it('should do nothing when an element have nothing stored', () => {
+ it('should do nothing when an element has nothing stored', () => {
Data.remove(div, TEST_KEY)
expect().nothing()
@@ -76,7 +77,7 @@ describe('Data', () => {
Data.set(div, TEST_KEY, data)
Data.remove(div, UNKNOWN_KEY)
- expect(Data.get(div, TEST_KEY)).toBe(data)
+ expect(Data.get(div, TEST_KEY)).toEqual(data)
})
it('should remove data for a given key', () => {
@@ -88,7 +89,6 @@ describe('Data', () => {
expect(Data.get(div, TEST_KEY)).toBeNull()
})
- /* eslint-disable no-console */
it('should console.error a message if called with multiple keys', () => {
console.error = jasmine.createSpy('console.error')
@@ -99,7 +99,6 @@ describe('Data', () => {
Data.set(div, UNKNOWN_KEY, copy)
expect(console.error).toHaveBeenCalled()
- expect(Data.get(div, UNKNOWN_KEY)).toBe(null)
+ expect(Data.get(div, UNKNOWN_KEY)).toBeNull()
})
- /* eslint-enable no-console */
})
diff --git a/js/tests/unit/dom/event-handler.spec.js b/js/tests/unit/dom/event-handler.spec.js
index 19d63492b..7f99c4122 100644
--- a/js/tests/unit/dom/event-handler.spec.js
+++ b/js/tests/unit/dom/event-handler.spec.js
@@ -1,5 +1,6 @@
-import EventHandler from '../../../src/dom/event-handler'
-import { getFixture, clearFixture } from '../../helpers/fixture'
+import EventHandler from '../../../src/dom/event-handler.js'
+import { noop } from '../../../src/util/index.js'
+import { clearFixture, getFixture } from '../../helpers/fixture.js'
describe('EventHandler', () => {
let fixtureEl
@@ -18,176 +19,190 @@ describe('EventHandler', () => {
const div = fixtureEl.querySelector('div')
- EventHandler.on(div, null, () => {})
- EventHandler.on(null, 'click', () => {})
+ EventHandler.on(div, null, noop)
+ EventHandler.on(null, 'click', noop)
expect().nothing()
})
- it('should add event listener', done => {
- fixtureEl.innerHTML = '<div></div>'
+ it('should add event listener', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div></div>'
- const div = fixtureEl.querySelector('div')
+ const div = fixtureEl.querySelector('div')
- EventHandler.on(div, 'click', () => {
- expect().nothing()
- done()
- })
+ EventHandler.on(div, 'click', () => {
+ expect().nothing()
+ resolve()
+ })
- div.click()
+ div.click()
+ })
})
- it('should add namespaced event listener', done => {
- fixtureEl.innerHTML = '<div></div>'
+ it('should add namespaced event listener', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div></div>'
- const div = fixtureEl.querySelector('div')
+ const div = fixtureEl.querySelector('div')
- EventHandler.on(div, 'bs.namespace', () => {
- expect().nothing()
- done()
- })
+ EventHandler.on(div, 'bs.namespace', () => {
+ expect().nothing()
+ resolve()
+ })
- EventHandler.trigger(div, 'bs.namespace')
+ EventHandler.trigger(div, 'bs.namespace')
+ })
})
- it('should add native namespaced event listener', done => {
- fixtureEl.innerHTML = '<div></div>'
+ it('should add native namespaced event listener', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div></div>'
- const div = fixtureEl.querySelector('div')
+ const div = fixtureEl.querySelector('div')
- EventHandler.on(div, 'click.namespace', () => {
- expect().nothing()
- done()
- })
+ EventHandler.on(div, 'click.namespace', () => {
+ expect().nothing()
+ resolve()
+ })
- EventHandler.trigger(div, 'click')
+ EventHandler.trigger(div, 'click')
+ })
})
- it('should handle event delegation', done => {
- EventHandler.on(document, 'click', '.test', () => {
- expect().nothing()
- done()
- })
+ it('should handle event delegation', () => {
+ return new Promise(resolve => {
+ EventHandler.on(document, 'click', '.test', () => {
+ expect().nothing()
+ resolve()
+ })
- fixtureEl.innerHTML = '<div class="test"></div>'
+ fixtureEl.innerHTML = '<div class="test"></div>'
- const div = fixtureEl.querySelector('div')
-
- div.click()
- })
+ const div = fixtureEl.querySelector('div')
- it('should handle mouseenter/mouseleave like the native counterpart', done => {
- fixtureEl.innerHTML = [
- '<div class="outer">',
- '<div class="inner">',
- '<div class="nested">',
- '<div class="deep"></div>',
- '</div>',
- '</div>',
- '<div class="sibling"></div>',
- '</div>'
- ]
-
- const outer = fixtureEl.querySelector('.outer')
- const inner = fixtureEl.querySelector('.inner')
- const nested = fixtureEl.querySelector('.nested')
- const deep = fixtureEl.querySelector('.deep')
- const sibling = fixtureEl.querySelector('.sibling')
-
- const enterSpy = jasmine.createSpy('mouseenter')
- const leaveSpy = jasmine.createSpy('mouseleave')
- const delegateEnterSpy = jasmine.createSpy('mouseenter')
- const delegateLeaveSpy = jasmine.createSpy('mouseleave')
-
- EventHandler.on(inner, 'mouseenter', enterSpy)
- EventHandler.on(inner, 'mouseleave', leaveSpy)
- EventHandler.on(outer, 'mouseenter', '.inner', delegateEnterSpy)
- EventHandler.on(outer, 'mouseleave', '.inner', delegateLeaveSpy)
-
- EventHandler.on(sibling, 'mouseenter', () => {
- expect(enterSpy.calls.count()).toBe(2)
- expect(leaveSpy.calls.count()).toBe(2)
- expect(delegateEnterSpy.calls.count()).toBe(2)
- expect(delegateLeaveSpy.calls.count()).toBe(2)
- done()
+ div.click()
})
+ })
- const moveMouse = (from, to) => {
- from.dispatchEvent(new MouseEvent('mouseout', {
- bubbles: true,
- relatedTarget: to
- }))
-
- to.dispatchEvent(new MouseEvent('mouseover', {
- bubbles: true,
- relatedTarget: from
- }))
- }
+ it('should handle mouseenter/mouseleave like the native counterpart', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="outer">',
+ '<div class="inner">',
+ '<div class="nested">',
+ '<div class="deep"></div>',
+ '</div>',
+ '</div>',
+ '<div class="sibling"></div>',
+ '</div>'
+ ].join('')
+
+ const outer = fixtureEl.querySelector('.outer')
+ const inner = fixtureEl.querySelector('.inner')
+ const nested = fixtureEl.querySelector('.nested')
+ const deep = fixtureEl.querySelector('.deep')
+ const sibling = fixtureEl.querySelector('.sibling')
+
+ const enterSpy = jasmine.createSpy('mouseenter')
+ const leaveSpy = jasmine.createSpy('mouseleave')
+ const delegateEnterSpy = jasmine.createSpy('mouseenter')
+ const delegateLeaveSpy = jasmine.createSpy('mouseleave')
+
+ EventHandler.on(inner, 'mouseenter', enterSpy)
+ EventHandler.on(inner, 'mouseleave', leaveSpy)
+ EventHandler.on(outer, 'mouseenter', '.inner', delegateEnterSpy)
+ EventHandler.on(outer, 'mouseleave', '.inner', delegateLeaveSpy)
+
+ EventHandler.on(sibling, 'mouseenter', () => {
+ expect(enterSpy.calls.count()).toEqual(2)
+ expect(leaveSpy.calls.count()).toEqual(2)
+ expect(delegateEnterSpy.calls.count()).toEqual(2)
+ expect(delegateLeaveSpy.calls.count()).toEqual(2)
+ resolve()
+ })
+
+ const moveMouse = (from, to) => {
+ from.dispatchEvent(new MouseEvent('mouseout', {
+ bubbles: true,
+ relatedTarget: to
+ }))
+
+ to.dispatchEvent(new MouseEvent('mouseover', {
+ bubbles: true,
+ relatedTarget: from
+ }))
+ }
- // from outer to deep and back to outer (nested)
- moveMouse(outer, inner)
- moveMouse(inner, nested)
- moveMouse(nested, deep)
- moveMouse(deep, nested)
- moveMouse(nested, inner)
- moveMouse(inner, outer)
-
- setTimeout(() => {
- expect(enterSpy.calls.count()).toBe(1)
- expect(leaveSpy.calls.count()).toBe(1)
- expect(delegateEnterSpy.calls.count()).toBe(1)
- expect(delegateLeaveSpy.calls.count()).toBe(1)
-
- // from outer to inner to sibling (adjacent)
+ // from outer to deep and back to outer (nested)
moveMouse(outer, inner)
- moveMouse(inner, sibling)
- }, 20)
+ moveMouse(inner, nested)
+ moveMouse(nested, deep)
+ moveMouse(deep, nested)
+ moveMouse(nested, inner)
+ moveMouse(inner, outer)
+
+ setTimeout(() => {
+ expect(enterSpy.calls.count()).toEqual(1)
+ expect(leaveSpy.calls.count()).toEqual(1)
+ expect(delegateEnterSpy.calls.count()).toEqual(1)
+ expect(delegateLeaveSpy.calls.count()).toEqual(1)
+
+ // from outer to inner to sibling (adjacent)
+ moveMouse(outer, inner)
+ moveMouse(inner, sibling)
+ }, 20)
+ })
})
})
describe('one', () => {
- it('should call listener just once', done => {
- fixtureEl.innerHTML = '<div></div>'
-
- let called = 0
- const div = fixtureEl.querySelector('div')
- const obj = {
- oneListener() {
- called++
+ it('should call listener just once', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div></div>'
+
+ let called = 0
+ const div = fixtureEl.querySelector('div')
+ const obj = {
+ oneListener() {
+ called++
+ }
}
- }
- EventHandler.one(div, 'bootstrap', obj.oneListener)
+ EventHandler.one(div, 'bootstrap', obj.oneListener)
- EventHandler.trigger(div, 'bootstrap')
- EventHandler.trigger(div, 'bootstrap')
+ EventHandler.trigger(div, 'bootstrap')
+ EventHandler.trigger(div, 'bootstrap')
- setTimeout(() => {
- expect(called).toEqual(1)
- done()
- }, 20)
+ setTimeout(() => {
+ expect(called).toEqual(1)
+ resolve()
+ }, 20)
+ })
})
- it('should call delegated listener just once', done => {
- fixtureEl.innerHTML = '<div></div>'
+ it('should call delegated listener just once', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div></div>'
- let called = 0
- const div = fixtureEl.querySelector('div')
- const obj = {
- oneListener() {
- called++
+ let called = 0
+ const div = fixtureEl.querySelector('div')
+ const obj = {
+ oneListener() {
+ called++
+ }
}
- }
- EventHandler.one(fixtureEl, 'bootstrap', 'div', obj.oneListener)
+ EventHandler.one(fixtureEl, 'bootstrap', 'div', obj.oneListener)
- EventHandler.trigger(div, 'bootstrap')
- EventHandler.trigger(div, 'bootstrap')
+ EventHandler.trigger(div, 'bootstrap')
+ EventHandler.trigger(div, 'bootstrap')
- setTimeout(() => {
- expect(called).toEqual(1)
- done()
- }, 20)
+ setTimeout(() => {
+ expect(called).toEqual(1)
+ resolve()
+ }, 20)
+ })
})
})
@@ -196,171 +211,185 @@ describe('EventHandler', () => {
fixtureEl.innerHTML = '<div></div>'
const div = fixtureEl.querySelector('div')
- EventHandler.off(div, null, () => {})
- EventHandler.off(null, 'click', () => {})
+ EventHandler.off(div, null, noop)
+ EventHandler.off(null, 'click', noop)
expect().nothing()
})
- it('should remove a listener', done => {
- fixtureEl.innerHTML = '<div></div>'
- const div = fixtureEl.querySelector('div')
+ it('should remove a listener', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div></div>'
+ const div = fixtureEl.querySelector('div')
- let called = 0
- const handler = () => {
- called++
- }
+ let called = 0
+ const handler = () => {
+ called++
+ }
- EventHandler.on(div, 'foobar', handler)
- EventHandler.trigger(div, 'foobar')
+ EventHandler.on(div, 'foobar', handler)
+ EventHandler.trigger(div, 'foobar')
- EventHandler.off(div, 'foobar', handler)
- EventHandler.trigger(div, 'foobar')
+ EventHandler.off(div, 'foobar', handler)
+ EventHandler.trigger(div, 'foobar')
- setTimeout(() => {
- expect(called).toEqual(1)
- done()
- }, 20)
+ setTimeout(() => {
+ expect(called).toEqual(1)
+ resolve()
+ }, 20)
+ })
})
- it('should remove all the events', done => {
- fixtureEl.innerHTML = '<div></div>'
- const div = fixtureEl.querySelector('div')
+ it('should remove all the events', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div></div>'
+ const div = fixtureEl.querySelector('div')
- let called = 0
+ let called = 0
- EventHandler.on(div, 'foobar', () => {
- called++
- })
- EventHandler.on(div, 'foobar', () => {
- called++
- })
- EventHandler.trigger(div, 'foobar')
+ EventHandler.on(div, 'foobar', () => {
+ called++
+ })
+ EventHandler.on(div, 'foobar', () => {
+ called++
+ })
+ EventHandler.trigger(div, 'foobar')
- EventHandler.off(div, 'foobar')
- EventHandler.trigger(div, 'foobar')
+ EventHandler.off(div, 'foobar')
+ EventHandler.trigger(div, 'foobar')
- setTimeout(() => {
- expect(called).toEqual(2)
- done()
- }, 20)
+ setTimeout(() => {
+ expect(called).toEqual(2)
+ resolve()
+ }, 20)
+ })
})
- it('should remove all the namespaced listeners if namespace is passed', done => {
- fixtureEl.innerHTML = '<div></div>'
- const div = fixtureEl.querySelector('div')
+ it('should remove all the namespaced listeners if namespace is passed', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div></div>'
+ const div = fixtureEl.querySelector('div')
- let called = 0
+ let called = 0
- EventHandler.on(div, 'foobar.namespace', () => {
- called++
- })
- EventHandler.on(div, 'foofoo.namespace', () => {
- called++
+ EventHandler.on(div, 'foobar.namespace', () => {
+ called++
+ })
+ EventHandler.on(div, 'foofoo.namespace', () => {
+ called++
+ })
+ EventHandler.trigger(div, 'foobar.namespace')
+ EventHandler.trigger(div, 'foofoo.namespace')
+
+ EventHandler.off(div, '.namespace')
+ EventHandler.trigger(div, 'foobar.namespace')
+ EventHandler.trigger(div, 'foofoo.namespace')
+
+ setTimeout(() => {
+ expect(called).toEqual(2)
+ resolve()
+ }, 20)
})
- EventHandler.trigger(div, 'foobar.namespace')
- EventHandler.trigger(div, 'foofoo.namespace')
-
- EventHandler.off(div, '.namespace')
- EventHandler.trigger(div, 'foobar.namespace')
- EventHandler.trigger(div, 'foofoo.namespace')
-
- setTimeout(() => {
- expect(called).toEqual(2)
- done()
- }, 20)
})
- it('should remove the namespaced listeners', done => {
- fixtureEl.innerHTML = '<div></div>'
- const div = fixtureEl.querySelector('div')
+ it('should remove the namespaced listeners', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div></div>'
+ const div = fixtureEl.querySelector('div')
- let calledCallback1 = 0
- let calledCallback2 = 0
+ let calledCallback1 = 0
+ let calledCallback2 = 0
- EventHandler.on(div, 'foobar.namespace', () => {
- calledCallback1++
- })
- EventHandler.on(div, 'foofoo.namespace', () => {
- calledCallback2++
- })
+ EventHandler.on(div, 'foobar.namespace', () => {
+ calledCallback1++
+ })
+ EventHandler.on(div, 'foofoo.namespace', () => {
+ calledCallback2++
+ })
- EventHandler.trigger(div, 'foobar.namespace')
- EventHandler.off(div, 'foobar.namespace')
- EventHandler.trigger(div, 'foobar.namespace')
+ EventHandler.trigger(div, 'foobar.namespace')
+ EventHandler.off(div, 'foobar.namespace')
+ EventHandler.trigger(div, 'foobar.namespace')
- EventHandler.trigger(div, 'foofoo.namespace')
+ EventHandler.trigger(div, 'foofoo.namespace')
- setTimeout(() => {
- expect(calledCallback1).toEqual(1)
- expect(calledCallback2).toEqual(1)
- done()
- }, 20)
+ setTimeout(() => {
+ expect(calledCallback1).toEqual(1)
+ expect(calledCallback2).toEqual(1)
+ resolve()
+ }, 20)
+ })
})
- it('should remove the all the namespaced listeners for native events', done => {
- fixtureEl.innerHTML = '<div></div>'
- const div = fixtureEl.querySelector('div')
+ it('should remove the all the namespaced listeners for native events', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div></div>'
+ const div = fixtureEl.querySelector('div')
- let called = 0
+ let called = 0
- EventHandler.on(div, 'click.namespace', () => {
- called++
- })
- EventHandler.on(div, 'click.namespace2', () => {
- called++
- })
+ EventHandler.on(div, 'click.namespace', () => {
+ called++
+ })
+ EventHandler.on(div, 'click.namespace2', () => {
+ called++
+ })
- EventHandler.trigger(div, 'click')
- EventHandler.off(div, 'click')
- EventHandler.trigger(div, 'click')
+ EventHandler.trigger(div, 'click')
+ EventHandler.off(div, 'click')
+ EventHandler.trigger(div, 'click')
- setTimeout(() => {
- expect(called).toEqual(2)
- done()
- }, 20)
+ setTimeout(() => {
+ expect(called).toEqual(2)
+ resolve()
+ }, 20)
+ })
})
- it('should remove the specified namespaced listeners for native events', done => {
- fixtureEl.innerHTML = '<div></div>'
- const div = fixtureEl.querySelector('div')
-
- let called1 = 0
- let called2 = 0
-
- EventHandler.on(div, 'click.namespace', () => {
- called1++
- })
- EventHandler.on(div, 'click.namespace2', () => {
- called2++
+ it('should remove the specified namespaced listeners for native events', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div></div>'
+ const div = fixtureEl.querySelector('div')
+
+ let called1 = 0
+ let called2 = 0
+
+ EventHandler.on(div, 'click.namespace', () => {
+ called1++
+ })
+ EventHandler.on(div, 'click.namespace2', () => {
+ called2++
+ })
+ EventHandler.trigger(div, 'click')
+
+ EventHandler.off(div, 'click.namespace')
+ EventHandler.trigger(div, 'click')
+
+ setTimeout(() => {
+ expect(called1).toEqual(1)
+ expect(called2).toEqual(2)
+ resolve()
+ }, 20)
})
- EventHandler.trigger(div, 'click')
-
- EventHandler.off(div, 'click.namespace')
- EventHandler.trigger(div, 'click')
-
- setTimeout(() => {
- expect(called1).toEqual(1)
- expect(called2).toEqual(2)
- done()
- }, 20)
})
- it('should remove a listener registered by .one', done => {
- fixtureEl.innerHTML = '<div></div>'
+ it('should remove a listener registered by .one', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = '<div></div>'
- const div = fixtureEl.querySelector('div')
- const handler = () => {
- throw new Error('called')
- }
+ const div = fixtureEl.querySelector('div')
+ const handler = () => {
+ reject(new Error('called'))
+ }
- EventHandler.one(div, 'foobar', handler)
- EventHandler.off(div, 'foobar', handler)
+ EventHandler.one(div, 'foobar', handler)
+ EventHandler.off(div, 'foobar', handler)
- EventHandler.trigger(div, 'foobar')
- setTimeout(() => {
- expect().nothing()
- done()
- }, 20)
+ EventHandler.trigger(div, 'foobar')
+ setTimeout(() => {
+ expect().nothing()
+ resolve()
+ }, 20)
+ })
})
it('should remove the correct delegated event listener', () => {
@@ -412,4 +441,40 @@ describe('EventHandler', () => {
expect(i).toEqual(5)
})
})
+
+ describe('general functionality', () => {
+ it('should hydrate properties, and make them configurable', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="div1">',
+ ' <div id="div2"></div>',
+ ' <div id="div3"></div>',
+ '</div>'
+ ].join('')
+
+ const div1 = fixtureEl.querySelector('#div1')
+ const div2 = fixtureEl.querySelector('#div2')
+
+ EventHandler.on(div1, 'click', event => {
+ expect(event.currentTarget).toBe(div2)
+ expect(event.delegateTarget).toBe(div1)
+ expect(event.originalTarget).toBeNull()
+
+ Object.defineProperty(event, 'currentTarget', {
+ configurable: true,
+ get() {
+ return div1
+ }
+ })
+
+ expect(event.currentTarget).toBe(div1)
+ resolve()
+ })
+
+ expect(() => {
+ EventHandler.trigger(div1, 'click', { originalTarget: null, currentTarget: div2 })
+ }).not.toThrowError(TypeError)
+ })
+ })
+ })
})
diff --git a/js/tests/unit/dom/manipulator.spec.js b/js/tests/unit/dom/manipulator.spec.js
index 61ffe7455..9d0be3218 100644
--- a/js/tests/unit/dom/manipulator.spec.js
+++ b/js/tests/unit/dom/manipulator.spec.js
@@ -1,5 +1,5 @@
-import Manipulator from '../../../src/dom/manipulator'
-import { getFixture, clearFixture } from '../../helpers/fixture'
+import Manipulator from '../../../src/dom/manipulator.js'
+import { clearFixture, getFixture } from '../../helpers/fixture.js'
describe('Manipulator', () => {
let fixtureEl
@@ -70,6 +70,17 @@ describe('Manipulator', () => {
target: '#element'
})
})
+
+ it('should omit `bs-config` data attribute', () => {
+ fixtureEl.innerHTML = '<div data-bs-toggle="tabs" data-bs-target="#element" data-bs-config=\'{"testBool":false}\'></div>'
+
+ const div = fixtureEl.querySelector('div')
+
+ expect(Manipulator.getDataAttributes(div)).toEqual({
+ toggle: 'tabs',
+ target: '#element'
+ })
+ })
})
describe('getDataAttribute', () => {
@@ -96,93 +107,29 @@ describe('Manipulator', () => {
const div = fixtureEl.querySelector('div')
- expect(Manipulator.getDataAttribute(div, 'test')).toEqual(false)
+ expect(Manipulator.getDataAttribute(div, 'test')).toBeFalse()
div.setAttribute('data-bs-test', 'true')
- expect(Manipulator.getDataAttribute(div, 'test')).toEqual(true)
+ expect(Manipulator.getDataAttribute(div, 'test')).toBeTrue()
div.setAttribute('data-bs-test', '1')
expect(Manipulator.getDataAttribute(div, 'test')).toEqual(1)
})
- })
-
- describe('offset', () => {
- it('should return an object with two properties top and left, both numbers', () => {
- fixtureEl.innerHTML = '<div></div>'
-
- const div = fixtureEl.querySelector('div')
- const offset = Manipulator.offset(div)
-
- expect(offset).toBeDefined()
- expect(offset.top).toEqual(jasmine.any(Number))
- expect(offset.left).toEqual(jasmine.any(Number))
- })
-
- it('should return offset relative to attached element\'s offset', () => {
- const top = 500
- const left = 1000
-
- fixtureEl.innerHTML = `<div style="position:absolute;top:${top}px;left:${left}px"></div>`
-
- const div = fixtureEl.querySelector('div')
- const offset = Manipulator.offset(div)
- const fixtureOffset = Manipulator.offset(fixtureEl)
-
- expect(offset).toEqual({
- top: fixtureOffset.top + top,
- left: fixtureOffset.left + left
- })
- })
-
- it('should not change offset when viewport is scrolled', done => {
- const top = 500
- const left = 1000
- const scrollY = 200
- const scrollX = 400
- fixtureEl.innerHTML = `<div style="position:absolute;top:${top}px;left:${left}px"></div>`
+ it('should normalize json data', () => {
+ fixtureEl.innerHTML = '<div data-bs-test=\'{"delay":{"show":100,"hide":10}}\'></div>'
const div = fixtureEl.querySelector('div')
- const offset = Manipulator.offset(div)
-
- // append an element that forces scrollbars on the window so we can scroll
- const { defaultView: win, body } = fixtureEl.ownerDocument
- const forceScrollBars = document.createElement('div')
- forceScrollBars.style.cssText = 'position:absolute;top:5000px;left:5000px;width:1px;height:1px'
- body.append(forceScrollBars)
-
- const scrollHandler = () => {
- expect(window.pageYOffset).toBe(scrollY)
- expect(window.pageXOffset).toBe(scrollX)
- const newOffset = Manipulator.offset(div)
+ expect(Manipulator.getDataAttribute(div, 'test')).toEqual({ delay: { show: 100, hide: 10 } })
- expect(newOffset).toEqual({
- top: offset.top,
- left: offset.left
- })
-
- win.removeEventListener('scroll', scrollHandler)
- forceScrollBars.remove()
- win.scrollTo(0, 0)
- done()
- }
-
- win.addEventListener('scroll', scrollHandler)
- win.scrollTo(scrollX, scrollY)
- })
- })
-
- describe('position', () => {
- it('should return an object with two properties top and left, both numbers', () => {
- fixtureEl.innerHTML = '<div></div>'
-
- const div = fixtureEl.querySelector('div')
- const position = Manipulator.position(div)
+ const objectData = { 'Super Hero': ['Iron Man', 'Super Man'], testNum: 90, url: 'http://localhost:8080/test?foo=bar' }
+ const dataStr = JSON.stringify(objectData)
+ div.setAttribute('data-bs-test', encodeURIComponent(dataStr))
+ expect(Manipulator.getDataAttribute(div, 'test')).toEqual(objectData)
- expect(position).toBeDefined()
- expect(position.top).toEqual(jasmine.any(Number))
- expect(position.left).toEqual(jasmine.any(Number))
+ div.setAttribute('data-bs-test', dataStr)
+ expect(Manipulator.getDataAttribute(div, 'test')).toEqual(objectData)
})
})
})
diff --git a/js/tests/unit/dom/selector-engine.spec.js b/js/tests/unit/dom/selector-engine.spec.js
index 09c85a88a..95d9bf8ec 100644
--- a/js/tests/unit/dom/selector-engine.spec.js
+++ b/js/tests/unit/dom/selector-engine.spec.js
@@ -1,5 +1,5 @@
-import SelectorEngine from '../../../src/dom/selector-engine'
-import { getFixture, clearFixture } from '../../helpers/fixture'
+import SelectorEngine from '../../../src/dom/selector-engine.js'
+import { clearFixture, getFixture } from '../../helpers/fixture.js'
describe('SelectorEngine', () => {
let fixtureEl
@@ -21,7 +21,7 @@ describe('SelectorEngine', () => {
expect(SelectorEngine.find('div', fixtureEl)).toEqual([div])
})
- it('should find elements globaly', () => {
+ it('should find elements globally', () => {
fixtureEl.innerHTML = '<div id="test"></div>'
const div = fixtureEl.querySelector('#test')
@@ -30,13 +30,15 @@ describe('SelectorEngine', () => {
})
it('should handle :scope selectors', () => {
- fixtureEl.innerHTML = `<ul>
- <li></li>
- <li>
- <a href="#" class="active">link</a>
- </li>
- <li></li>
- </ul>`
+ fixtureEl.innerHTML = [
+ '<ul>',
+ ' <li></li>',
+ ' <li>',
+ ' <a href="#" class="active">link</a>',
+ ' </li>',
+ ' <li></li>',
+ '</ul>'
+ ].join('')
const listEl = fixtureEl.querySelector('ul')
const aActive = fixtureEl.querySelector('.active')
@@ -57,11 +59,13 @@ describe('SelectorEngine', () => {
describe('children', () => {
it('should find children', () => {
- fixtureEl.innerHTML = `<ul>
- <li></li>
- <li></li>
- <li></li>
- </ul>`
+ fixtureEl.innerHTML = [
+ '<ul>',
+ ' <li></li>',
+ ' <li></li>',
+ ' <li></li>',
+ '</ul>'
+ ].join('')
const list = fixtureEl.querySelector('ul')
const liList = [].concat(...fixtureEl.querySelectorAll('li'))
@@ -73,7 +77,7 @@ describe('SelectorEngine', () => {
describe('parents', () => {
it('should return parents', () => {
- expect(SelectorEngine.parents(fixtureEl, 'body').length).toEqual(1)
+ expect(SelectorEngine.parents(fixtureEl, 'body')).toHaveSize(1)
})
})
@@ -162,7 +166,7 @@ describe('SelectorEngine', () => {
'<span>lorem</span>',
'<a>lorem</a>',
'<button>lorem</button>',
- '<input />',
+ '<input>',
'<textarea></textarea>',
'<select></select>',
'<details>lorem</details>'
@@ -197,9 +201,7 @@ describe('SelectorEngine', () => {
})
it('should return not return elements with negative tab index', () => {
- fixtureEl.innerHTML = [
- '<button tabindex="-1">lorem</button>'
- ].join('')
+ fixtureEl.innerHTML = '<button tabindex="-1">lorem</button>'
const expectedElements = []
@@ -207,9 +209,7 @@ describe('SelectorEngine', () => {
})
it('should return contenteditable elements', () => {
- fixtureEl.innerHTML = [
- '<div contenteditable="true">lorem</div>'
- ].join('')
+ fixtureEl.innerHTML = '<div contenteditable="true">lorem</div>'
const expectedElements = [fixtureEl.querySelector('[contenteditable="true"]')]
@@ -217,9 +217,7 @@ describe('SelectorEngine', () => {
})
it('should not return disabled elements', () => {
- fixtureEl.innerHTML = [
- '<button disabled="true">lorem</button>'
- ].join('')
+ fixtureEl.innerHTML = '<button disabled="true">lorem</button>'
const expectedElements = []
@@ -227,14 +225,190 @@ describe('SelectorEngine', () => {
})
it('should not return invisible elements', () => {
- fixtureEl.innerHTML = [
- '<button style="display:none;">lorem</button>'
- ].join('')
+ fixtureEl.innerHTML = '<button style="display:none;">lorem</button>'
const expectedElements = []
expect(SelectorEngine.focusableChildren(fixtureEl)).toEqual(expectedElements)
})
})
-})
+ describe('getSelectorFromElement', () => {
+ it('should get selector from data-bs-target', () => {
+ fixtureEl.innerHTML = [
+ '<div id="test" data-bs-target=".target"></div>',
+ '<div class="target"></div>'
+ ].join('')
+
+ const testEl = fixtureEl.querySelector('#test')
+
+ expect(SelectorEngine.getSelectorFromElement(testEl)).toEqual('.target')
+ })
+
+ it('should get selector from href if no data-bs-target set', () => {
+ fixtureEl.innerHTML = [
+ '<a id="test" href=".target"></a>',
+ '<div class="target"></div>'
+ ].join('')
+
+ const testEl = fixtureEl.querySelector('#test')
+
+ expect(SelectorEngine.getSelectorFromElement(testEl)).toEqual('.target')
+ })
+
+ it('should get selector from href if data-bs-target equal to #', () => {
+ fixtureEl.innerHTML = [
+ '<a id="test" data-bs-target="#" href=".target"></a>',
+ '<div class="target"></div>'
+ ].join('')
+
+ const testEl = fixtureEl.querySelector('#test')
+
+ expect(SelectorEngine.getSelectorFromElement(testEl)).toEqual('.target')
+ })
+
+ it('should return null if a selector from a href is a url without an anchor', () => {
+ fixtureEl.innerHTML = [
+ '<a id="test" data-bs-target="#" href="foo/bar.html"></a>',
+ '<div class="target"></div>'
+ ].join('')
+
+ const testEl = fixtureEl.querySelector('#test')
+
+ expect(SelectorEngine.getSelectorFromElement(testEl)).toBeNull()
+ })
+
+ it('should return the anchor if a selector from a href is a url', () => {
+ fixtureEl.innerHTML = [
+ '<a id="test" data-bs-target="#" href="foo/bar.html#target"></a>',
+ '<div id="target"></div>'
+ ].join('')
+
+ const testEl = fixtureEl.querySelector('#test')
+
+ expect(SelectorEngine.getSelectorFromElement(testEl)).toEqual('#target')
+ })
+
+ it('should return null if selector not found', () => {
+ fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
+
+ const testEl = fixtureEl.querySelector('#test')
+
+ expect(SelectorEngine.getSelectorFromElement(testEl)).toBeNull()
+ })
+
+ it('should return null if no selector', () => {
+ fixtureEl.innerHTML = '<div></div>'
+
+ const testEl = fixtureEl.querySelector('div')
+
+ expect(SelectorEngine.getSelectorFromElement(testEl)).toBeNull()
+ })
+ })
+
+ describe('getElementFromSelector', () => {
+ it('should get element from data-bs-target', () => {
+ fixtureEl.innerHTML = [
+ '<div id="test" data-bs-target=".target"></div>',
+ '<div class="target"></div>'
+ ].join('')
+
+ const testEl = fixtureEl.querySelector('#test')
+
+ expect(SelectorEngine.getElementFromSelector(testEl)).toEqual(fixtureEl.querySelector('.target'))
+ })
+
+ it('should get element from href if no data-bs-target set', () => {
+ fixtureEl.innerHTML = [
+ '<a id="test" href=".target"></a>',
+ '<div class="target"></div>'
+ ].join('')
+
+ const testEl = fixtureEl.querySelector('#test')
+
+ expect(SelectorEngine.getElementFromSelector(testEl)).toEqual(fixtureEl.querySelector('.target'))
+ })
+
+ it('should return null if element not found', () => {
+ fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
+
+ const testEl = fixtureEl.querySelector('#test')
+
+ expect(SelectorEngine.getElementFromSelector(testEl)).toBeNull()
+ })
+
+ it('should return null if no selector', () => {
+ fixtureEl.innerHTML = '<div></div>'
+
+ const testEl = fixtureEl.querySelector('div')
+
+ expect(SelectorEngine.getElementFromSelector(testEl)).toBeNull()
+ })
+ })
+
+ describe('getMultipleElementsFromSelector', () => {
+ it('should get elements from data-bs-target', () => {
+ fixtureEl.innerHTML = [
+ '<div id="test" data-bs-target=".target"></div>',
+ '<div class="target"></div>',
+ '<div class="target"></div>'
+ ].join('')
+
+ const testEl = fixtureEl.querySelector('#test')
+
+ expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual(Array.from(fixtureEl.querySelectorAll('.target')))
+ })
+
+ it('should get elements if several ids are given', () => {
+ fixtureEl.innerHTML = [
+ '<div id="test" data-bs-target="#target1,#target2"></div>',
+ '<div class="target" id="target1"></div>',
+ '<div class="target" id="target2"></div>'
+ ].join('')
+
+ const testEl = fixtureEl.querySelector('#test')
+
+ expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual(Array.from(fixtureEl.querySelectorAll('.target')))
+ })
+
+ it('should get elements if several ids with special chars are given', () => {
+ fixtureEl.innerHTML = [
+ '<div id="test" data-bs-target="#j_id11:exampleModal,#j_id22:exampleModal"></div>',
+ '<div class="target" id="j_id11:exampleModal"></div>',
+ '<div class="target" id="j_id22:exampleModal"></div>'
+ ].join('')
+
+ const testEl = fixtureEl.querySelector('#test')
+
+ expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual(Array.from(fixtureEl.querySelectorAll('.target')))
+ })
+
+ it('should get elements in array, from href if no data-bs-target set', () => {
+ fixtureEl.innerHTML = [
+ '<a id="test" href=".target"></a>',
+ '<div class="target"></div>',
+ '<div class="target"></div>'
+ ].join('')
+
+ const testEl = fixtureEl.querySelector('#test')
+
+ expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toEqual(Array.from(fixtureEl.querySelectorAll('.target')))
+ })
+
+ it('should return empty array if elements not found', () => {
+ fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
+
+ const testEl = fixtureEl.querySelector('#test')
+
+ expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toHaveSize(0)
+ })
+
+ it('should return empty array if no selector', () => {
+ fixtureEl.innerHTML = '<div></div>'
+
+ const testEl = fixtureEl.querySelector('div')
+
+ expect(SelectorEngine.getMultipleElementsFromSelector(testEl)).toHaveSize(0)
+ })
+ })
+})
diff --git a/js/tests/unit/dropdown.spec.js b/js/tests/unit/dropdown.spec.js
index f099e9990..63ae4bd10 100644
--- a/js/tests/unit/dropdown.spec.js
+++ b/js/tests/unit/dropdown.spec.js
@@ -1,7 +1,9 @@
-import Dropdown from '../../src/dropdown'
-import EventHandler from '../../src/dom/event-handler'
-import { noop } from '../../src/util/index'
-import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
+import EventHandler from '../../src/dom/event-handler.js'
+import Dropdown from '../../src/dropdown.js'
+import { noop } from '../../src/util/index.js'
+import {
+ clearFixture, createEvent, getFixture, jQueryMock
+} from '../helpers/fixture.js'
describe('Dropdown', () => {
let fixtureEl
@@ -57,36 +59,61 @@ describe('Dropdown', () => {
expect(dropdownByElement._element).toEqual(btnDropdown)
})
- it('should create offset modifier correctly when offset option is a function', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should work on invalid markup', () => {
+ return new Promise(resolve => {
+ // TODO: REMOVE in v6
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const getOffset = jasmine.createSpy('getOffset').and.returnValue([10, 20])
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = new Dropdown(btnDropdown, {
- offset: getOffset,
- popperConfig: {
- onFirstUpdate: state => {
- expect(getOffset).toHaveBeenCalledWith({
- popper: state.rects.popper,
- reference: state.rects.reference,
- placement: state.placement
- }, btnDropdown)
- done()
+ const dropdownElem = fixtureEl.querySelector('.dropdown-menu')
+ const dropdown = new Dropdown(dropdownElem)
+
+ dropdownElem.addEventListener('shown.bs.dropdown', () => {
+ resolve()
+ })
+
+ expect().nothing()
+ dropdown.show()
+ })
+ })
+
+ it('should create offset modifier correctly when offset option is a function', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const getOffset = jasmine.createSpy('getOffset').and.returnValue([10, 20])
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown, {
+ offset: getOffset,
+ popperConfig: {
+ onFirstUpdate(state) {
+ expect(getOffset).toHaveBeenCalledWith({
+ popper: state.rects.popper,
+ reference: state.rects.reference,
+ placement: state.placement
+ }, btnDropdown)
+ resolve()
+ }
}
- }
- })
- const offset = dropdown._getOffset()
+ })
+ const offset = dropdown._getOffset()
- expect(typeof offset).toEqual('function')
+ expect(typeof offset).toEqual('function')
- dropdown.show()
+ dropdown.show()
+ })
})
it('should create offset modifier correctly when offset option is a string into data attribute', () => {
@@ -130,7 +157,7 @@ describe('Dropdown', () => {
it('should allow to pass config to Popper with `popperConfig` as a function', () => {
fixtureEl.innerHTML = [
'<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-placement="right" >Dropdown</button>',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-placement="right">Dropdown</button>',
' <div class="dropdown-menu">',
' <a class="dropdown-item" href="#">Secondary link</a>',
' </div>',
@@ -145,767 +172,875 @@ describe('Dropdown', () => {
const popperConfig = dropdown._getPopperConfig()
- expect(getPopperConfig).toHaveBeenCalled()
+ // Ensure that the function was called with the default config.
+ expect(getPopperConfig).toHaveBeenCalledWith(jasmine.objectContaining({
+ placement: jasmine.any(String)
+ }))
expect(popperConfig.placement).toEqual('left')
})
})
describe('toggle', () => {
- it('should toggle a dropdown', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
-
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = new Dropdown(btnDropdown)
+ it('should toggle a dropdown', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown)
+
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ expect(btnDropdown).toHaveClass('show')
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+ resolve()
+ })
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- expect(btnDropdown.classList.contains('show')).toEqual(true)
- expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
- done()
+ dropdown.toggle()
})
-
- dropdown.toggle()
})
- it('should destroy old popper references on toggle', done => {
- fixtureEl.innerHTML = [
- '<div class="first dropdown">',
- ' <button class="firstBtn btn" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>',
- '<div class="second dropdown">',
- ' <button class="secondBtn btn" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should destroy old popper references on toggle', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="first dropdown">',
+ ' <button class="firstBtn btn" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>',
+ '<div class="second dropdown">',
+ ' <button class="secondBtn btn" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const btnDropdown1 = fixtureEl.querySelector('.firstBtn')
+ const btnDropdown2 = fixtureEl.querySelector('.secondBtn')
+ const firstDropdownEl = fixtureEl.querySelector('.first')
+ const secondDropdownEl = fixtureEl.querySelector('.second')
+ const dropdown1 = new Dropdown(btnDropdown1)
+
+ firstDropdownEl.addEventListener('shown.bs.dropdown', () => {
+ expect(btnDropdown1).toHaveClass('show')
+ spyOn(dropdown1._popper, 'destroy')
+ btnDropdown2.click()
+ })
- const btnDropdown1 = fixtureEl.querySelector('.firstBtn')
- const btnDropdown2 = fixtureEl.querySelector('.secondBtn')
- const firstDropdownEl = fixtureEl.querySelector('.first')
- const secondDropdownEl = fixtureEl.querySelector('.second')
- const dropdown1 = new Dropdown(btnDropdown1)
+ secondDropdownEl.addEventListener('shown.bs.dropdown', () => setTimeout(() => {
+ expect(dropdown1._popper.destroy).toHaveBeenCalled()
+ resolve()
+ }))
- firstDropdownEl.addEventListener('shown.bs.dropdown', () => {
- expect(btnDropdown1.classList.contains('show')).toEqual(true)
- spyOn(dropdown1._popper, 'destroy')
- btnDropdown2.click()
+ dropdown1.toggle()
})
+ })
- secondDropdownEl.addEventListener('shown.bs.dropdown', () => setTimeout(() => {
- expect(dropdown1._popper.destroy).toHaveBeenCalled()
- done()
- }))
+ it('should toggle a dropdown and add/remove event listener on mobile', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- dropdown1.toggle()
- })
+ const defaultValueOnTouchStart = document.documentElement.ontouchstart
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown)
- it('should toggle a dropdown and add/remove event listener on mobile', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ document.documentElement.ontouchstart = noop
+ const spy = spyOn(EventHandler, 'on')
+ const spyOff = spyOn(EventHandler, 'off')
- const defaultValueOnTouchStart = document.documentElement.ontouchstart
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = new Dropdown(btnDropdown)
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ expect(btnDropdown).toHaveClass('show')
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+ expect(spy).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
- document.documentElement.ontouchstart = noop
- spyOn(EventHandler, 'on')
- spyOn(EventHandler, 'off')
+ dropdown.toggle()
+ })
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- expect(btnDropdown.classList.contains('show')).toEqual(true)
- expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
- expect(EventHandler.on).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
+ btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+ expect(btnDropdown).not.toHaveClass('show')
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
+ expect(spyOff).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
+
+ document.documentElement.ontouchstart = defaultValueOnTouchStart
+ resolve()
+ })
dropdown.toggle()
})
+ })
- btnDropdown.addEventListener('hidden.bs.dropdown', () => {
- expect(btnDropdown.classList.contains('show')).toEqual(false)
- expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
- expect(EventHandler.off).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
+ it('should toggle a dropdown at the right', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+ ' <div class="dropdown-menu dropdown-menu-end">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- document.documentElement.ontouchstart = defaultValueOnTouchStart
- done()
- })
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown)
- dropdown.toggle()
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ expect(btnDropdown).toHaveClass('show')
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+ resolve()
+ })
+
+ dropdown.toggle()
+ })
})
- it('should toggle a dropdown at the right', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
- ' <div class="dropdown-menu dropdown-menu-end">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should toggle a centered dropdown', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown-center">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown)
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- expect(btnDropdown.classList.contains('show')).toEqual(true)
- expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
- done()
- })
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ expect(btnDropdown).toHaveClass('show')
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+ resolve()
+ })
- dropdown.toggle()
+ dropdown.toggle()
+ })
})
- it('should toggle a dropup', done => {
- fixtureEl.innerHTML = [
- '<div class="dropup">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should toggle a dropup', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropup">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropupEl = fixtureEl.querySelector('.dropup')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropupEl = fixtureEl.querySelector('.dropup')
+ const dropdown = new Dropdown(btnDropdown)
- dropupEl.addEventListener('shown.bs.dropdown', () => {
- expect(btnDropdown.classList.contains('show')).toEqual(true)
- expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
- done()
- })
+ dropupEl.addEventListener('shown.bs.dropdown', () => {
+ expect(btnDropdown).toHaveClass('show')
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+ resolve()
+ })
- dropdown.toggle()
+ dropdown.toggle()
+ })
})
- it('should toggle a dropup at the right', done => {
- fixtureEl.innerHTML = [
- '<div class="dropup">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
- ' <div class="dropdown-menu dropdown-menu-end">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should toggle a dropup centered', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropup-center">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropupEl = fixtureEl.querySelector('.dropup')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropupEl = fixtureEl.querySelector('.dropup-center')
+ const dropdown = new Dropdown(btnDropdown)
- dropupEl.addEventListener('shown.bs.dropdown', () => {
- expect(btnDropdown.classList.contains('show')).toEqual(true)
- expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
- done()
- })
+ dropupEl.addEventListener('shown.bs.dropdown', () => {
+ expect(btnDropdown).toHaveClass('show')
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+ resolve()
+ })
- dropdown.toggle()
+ dropdown.toggle()
+ })
})
- it('should toggle a dropend', done => {
- fixtureEl.innerHTML = [
- '<div class="dropend">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should toggle a dropup at the right', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropup">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+ ' <div class="dropdown-menu dropdown-menu-end">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropendEl = fixtureEl.querySelector('.dropend')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropupEl = fixtureEl.querySelector('.dropup')
+ const dropdown = new Dropdown(btnDropdown)
- dropendEl.addEventListener('shown.bs.dropdown', () => {
- expect(btnDropdown.classList.contains('show')).toEqual(true)
- expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
- done()
- })
+ dropupEl.addEventListener('shown.bs.dropdown', () => {
+ expect(btnDropdown).toHaveClass('show')
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+ resolve()
+ })
- dropdown.toggle()
+ dropdown.toggle()
+ })
})
- it('should toggle a dropstart', done => {
- fixtureEl.innerHTML = [
- '<div class="dropstart">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should toggle a dropend', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropend">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropstartEl = fixtureEl.querySelector('.dropstart')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropendEl = fixtureEl.querySelector('.dropend')
+ const dropdown = new Dropdown(btnDropdown)
- dropstartEl.addEventListener('shown.bs.dropdown', () => {
- expect(btnDropdown.classList.contains('show')).toEqual(true)
- expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
- done()
- })
+ dropendEl.addEventListener('shown.bs.dropdown', () => {
+ expect(btnDropdown).toHaveClass('show')
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+ resolve()
+ })
- dropdown.toggle()
+ dropdown.toggle()
+ })
})
- it('should toggle a dropdown with parent reference', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should toggle a dropstart', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropstart">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = new Dropdown(btnDropdown, {
- reference: 'parent'
- })
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropstartEl = fixtureEl.querySelector('.dropstart')
+ const dropdown = new Dropdown(btnDropdown)
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- expect(btnDropdown.classList.contains('show')).toEqual(true)
- expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
- done()
- })
+ dropstartEl.addEventListener('shown.bs.dropdown', () => {
+ expect(btnDropdown).toHaveClass('show')
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+ resolve()
+ })
- dropdown.toggle()
+ dropdown.toggle()
+ })
})
- it('should toggle a dropdown with a dom node reference', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should toggle a dropdown with parent reference', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = new Dropdown(btnDropdown, {
- reference: fixtureEl
- })
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown, {
+ reference: 'parent'
+ })
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- expect(btnDropdown.classList.contains('show')).toEqual(true)
- expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
- done()
- })
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ expect(btnDropdown).toHaveClass('show')
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+ resolve()
+ })
- dropdown.toggle()
+ dropdown.toggle()
+ })
})
- it('should toggle a dropdown with a jquery object reference', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should toggle a dropdown with a dom node reference', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = new Dropdown(btnDropdown, {
- reference: { 0: fixtureEl, jquery: 'jQuery' }
- })
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown, {
+ reference: fixtureEl
+ })
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- expect(btnDropdown.classList.contains('show')).toEqual(true)
- expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
- done()
- })
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ expect(btnDropdown).toHaveClass('show')
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+ resolve()
+ })
- dropdown.toggle()
+ dropdown.toggle()
+ })
})
- it('should toggle a dropdown with a valid virtual element reference', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle visually-hidden" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should toggle a dropdown with a jquery object reference', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const virtualElement = {
- nodeType: 1,
- getBoundingClientRect() {
- return {
- width: 0,
- height: 0,
- top: 0,
- right: 0,
- bottom: 0,
- left: 0
- }
- }
- }
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown, {
+ reference: { 0: fixtureEl, jquery: 'jQuery' }
+ })
- expect(() => new Dropdown(btnDropdown, {
- reference: {}
- })).toThrowError(TypeError, 'DROPDOWN: Option "reference" provided type "object" without a required "getBoundingClientRect" method.')
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ expect(btnDropdown).toHaveClass('show')
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+ resolve()
+ })
- expect(() => new Dropdown(btnDropdown, {
- reference: {
- getBoundingClientRect: 'not-a-function'
- }
- })).toThrowError(TypeError, 'DROPDOWN: Option "reference" provided type "object" without a required "getBoundingClientRect" method.')
+ dropdown.toggle()
+ })
+ })
- // use onFirstUpdate as Poppers internal update is executed async
- const dropdown = new Dropdown(btnDropdown, {
- reference: virtualElement,
- popperConfig: {
- onFirstUpdate() {
- expect(virtualElement.getBoundingClientRect).toHaveBeenCalled()
- expect(btnDropdown.classList.contains('show')).toEqual(true)
- expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
- done()
+ it('should toggle a dropdown with a valid virtual element reference', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle visually-hidden" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const virtualElement = {
+ nodeType: 1,
+ getBoundingClientRect() {
+ return {
+ width: 0,
+ height: 0,
+ top: 0,
+ right: 0,
+ bottom: 0,
+ left: 0
+ }
}
}
- })
- spyOn(virtualElement, 'getBoundingClientRect').and.callThrough()
+ expect(() => new Dropdown(btnDropdown, {
+ reference: {}
+ })).toThrowError(TypeError, 'DROPDOWN: Option "reference" provided type "object" without a required "getBoundingClientRect" method.')
- dropdown.toggle()
+ expect(() => new Dropdown(btnDropdown, {
+ reference: {
+ getBoundingClientRect: 'not-a-function'
+ }
+ })).toThrowError(TypeError, 'DROPDOWN: Option "reference" provided type "object" without a required "getBoundingClientRect" method.')
+
+ // use onFirstUpdate as Poppers internal update is executed async
+ const dropdown = new Dropdown(btnDropdown, {
+ reference: virtualElement,
+ popperConfig: {
+ onFirstUpdate() {
+ expect(spy).toHaveBeenCalled()
+ expect(btnDropdown).toHaveClass('show')
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+ resolve()
+ }
+ }
+ })
+
+ const spy = spyOn(virtualElement, 'getBoundingClientRect').and.callThrough()
+
+ dropdown.toggle()
+ })
})
- it('should not toggle a dropdown if the element is disabled', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button disabled class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should not toggle a dropdown if the element is disabled', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button disabled class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown)
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- throw new Error('should not throw shown.bs.dropdown event')
- })
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ reject(new Error('should not throw shown.bs.dropdown event'))
+ })
- dropdown.toggle()
+ dropdown.toggle()
- setTimeout(() => {
- expect().nothing()
- done()
+ setTimeout(() => {
+ expect().nothing()
+ resolve()
+ })
})
})
- it('should not toggle a dropdown if the element contains .disabled', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle disabled" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should not toggle a dropdown if the element contains .disabled', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle disabled" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown)
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- throw new Error('should not throw shown.bs.dropdown event')
- })
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ reject(new Error('should not throw shown.bs.dropdown event'))
+ })
- dropdown.toggle()
+ dropdown.toggle()
- setTimeout(() => {
- expect().nothing()
- done()
+ setTimeout(() => {
+ expect().nothing()
+ resolve()
+ })
})
})
- it('should not toggle a dropdown if the menu is shown', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu show">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should not toggle a dropdown if the menu is shown', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu show">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown)
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- throw new Error('should not throw shown.bs.dropdown event')
- })
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ reject(new Error('should not throw shown.bs.dropdown event'))
+ })
- dropdown.toggle()
+ dropdown.toggle()
- setTimeout(() => {
- expect().nothing()
- done()
+ setTimeout(() => {
+ expect().nothing()
+ resolve()
+ })
})
})
- it('should not toggle a dropdown if show event is prevented', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should not toggle a dropdown if show event is prevented', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown)
- btnDropdown.addEventListener('show.bs.dropdown', event => {
- event.preventDefault()
- })
+ btnDropdown.addEventListener('show.bs.dropdown', event => {
+ event.preventDefault()
+ })
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- throw new Error('should not throw shown.bs.dropdown event')
- })
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ reject(new Error('should not throw shown.bs.dropdown event'))
+ })
- dropdown.toggle()
+ dropdown.toggle()
- setTimeout(() => {
- expect().nothing()
- done()
+ setTimeout(() => {
+ expect().nothing()
+ resolve()
+ })
})
})
})
describe('show', () => {
- it('should show a dropdown', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
-
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = new Dropdown(btnDropdown)
+ it('should show a dropdown', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown)
+
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ expect(btnDropdown).toHaveClass('show')
+ resolve()
+ })
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- expect(btnDropdown.classList.contains('show')).toEqual(true)
- done()
+ dropdown.show()
})
-
- dropdown.show()
})
- it('should not show a dropdown if the element is disabled', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button disabled class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should not show a dropdown if the element is disabled', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button disabled class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown)
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- throw new Error('should not throw shown.bs.dropdown event')
- })
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ reject(new Error('should not throw shown.bs.dropdown event'))
+ })
- dropdown.show()
+ dropdown.show()
- setTimeout(() => {
- expect().nothing()
- done()
- }, 10)
+ setTimeout(() => {
+ expect().nothing()
+ resolve()
+ }, 10)
+ })
})
- it('should not show a dropdown if the element contains .disabled', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle disabled" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should not show a dropdown if the element contains .disabled', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle disabled" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown)
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- throw new Error('should not throw shown.bs.dropdown event')
- })
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ reject(new Error('should not throw shown.bs.dropdown event'))
+ })
- dropdown.show()
+ dropdown.show()
- setTimeout(() => {
- expect().nothing()
- done()
- }, 10)
+ setTimeout(() => {
+ expect().nothing()
+ resolve()
+ }, 10)
+ })
})
- it('should not show a dropdown if the menu is shown', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu show">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should not show a dropdown if the menu is shown', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu show">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown)
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- throw new Error('should not throw shown.bs.dropdown event')
- })
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ reject(new Error('should not throw shown.bs.dropdown event'))
+ })
- dropdown.show()
+ dropdown.show()
- setTimeout(() => {
- expect().nothing()
- done()
- }, 10)
+ setTimeout(() => {
+ expect().nothing()
+ resolve()
+ }, 10)
+ })
})
- it('should not show a dropdown if show event is prevented', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should not show a dropdown if show event is prevented', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown)
- btnDropdown.addEventListener('show.bs.dropdown', event => {
- event.preventDefault()
- })
+ btnDropdown.addEventListener('show.bs.dropdown', event => {
+ event.preventDefault()
+ })
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- throw new Error('should not throw shown.bs.dropdown event')
- })
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ reject(new Error('should not throw shown.bs.dropdown event'))
+ })
- dropdown.show()
+ dropdown.show()
- setTimeout(() => {
- expect().nothing()
- done()
- }, 10)
+ setTimeout(() => {
+ expect().nothing()
+ resolve()
+ }, 10)
+ })
})
})
describe('hide', () => {
- it('should hide a dropdown', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="true">Dropdown</button>',
- ' <div class="dropdown-menu show">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
-
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
- const dropdown = new Dropdown(btnDropdown)
+ it('should hide a dropdown', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="true">Dropdown</button>',
+ ' <div class="dropdown-menu show">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+ const dropdown = new Dropdown(btnDropdown)
+
+ btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+ expect(dropdownMenu).not.toHaveClass('show')
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
+ resolve()
+ })
- btnDropdown.addEventListener('hidden.bs.dropdown', () => {
- expect(dropdownMenu.classList.contains('show')).toEqual(false)
- expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
- done()
+ dropdown.hide()
})
-
- dropdown.hide()
})
- it('should hide a dropdown and destroy popper', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should hide a dropdown and destroy popper', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown)
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- spyOn(dropdown._popper, 'destroy')
- dropdown.hide()
- })
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ spyOn(dropdown._popper, 'destroy')
+ dropdown.hide()
+ })
- btnDropdown.addEventListener('hidden.bs.dropdown', () => {
- expect(dropdown._popper.destroy).toHaveBeenCalled()
- done()
- })
+ btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+ expect(dropdown._popper.destroy).toHaveBeenCalled()
+ resolve()
+ })
- dropdown.show()
+ dropdown.show()
+ })
})
- it('should not hide a dropdown if the element is disabled', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button disabled class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu show">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should not hide a dropdown if the element is disabled', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button disabled class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu show">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+ const dropdown = new Dropdown(btnDropdown)
- btnDropdown.addEventListener('hidden.bs.dropdown', () => {
- throw new Error('should not throw hidden.bs.dropdown event')
- })
+ btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+ reject(new Error('should not throw hidden.bs.dropdown event'))
+ })
- dropdown.hide()
+ dropdown.hide()
- setTimeout(() => {
- expect(dropdownMenu.classList.contains('show')).toEqual(true)
- done()
- }, 10)
+ setTimeout(() => {
+ expect(dropdownMenu).toHaveClass('show')
+ resolve()
+ }, 10)
+ })
})
- it('should not hide a dropdown if the element contains .disabled', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle disabled" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu show">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should not hide a dropdown if the element contains .disabled', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle disabled" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu show">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+ const dropdown = new Dropdown(btnDropdown)
- btnDropdown.addEventListener('hidden.bs.dropdown', () => {
- throw new Error('should not throw hidden.bs.dropdown event')
- })
+ btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+ reject(new Error('should not throw hidden.bs.dropdown event'))
+ })
- dropdown.hide()
+ dropdown.hide()
- setTimeout(() => {
- expect(dropdownMenu.classList.contains('show')).toEqual(true)
- done()
- }, 10)
+ setTimeout(() => {
+ expect(dropdownMenu).toHaveClass('show')
+ resolve()
+ }, 10)
+ })
})
- it('should not hide a dropdown if the menu is not shown', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should not hide a dropdown if the menu is not shown', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown)
- btnDropdown.addEventListener('hidden.bs.dropdown', () => {
- throw new Error('should not throw hidden.bs.dropdown event')
- })
+ btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+ reject(new Error('should not throw hidden.bs.dropdown event'))
+ })
- dropdown.hide()
+ dropdown.hide()
- setTimeout(() => {
- expect().nothing()
- done()
- }, 10)
+ setTimeout(() => {
+ expect().nothing()
+ resolve()
+ }, 10)
+ })
})
- it('should not hide a dropdown if hide event is prevented', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu show">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should not hide a dropdown if hide event is prevented', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu show">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+ const dropdown = new Dropdown(btnDropdown)
- btnDropdown.addEventListener('hide.bs.dropdown', event => {
- event.preventDefault()
- })
+ btnDropdown.addEventListener('hide.bs.dropdown', event => {
+ event.preventDefault()
+ })
- btnDropdown.addEventListener('hidden.bs.dropdown', () => {
- throw new Error('should not throw hidden.bs.dropdown event')
- })
+ btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+ reject(new Error('should not throw hidden.bs.dropdown event'))
+ })
- dropdown.hide()
+ dropdown.hide()
- setTimeout(() => {
- expect(dropdownMenu.classList.contains('show')).toEqual(true)
- done()
+ setTimeout(() => {
+ expect(dropdownMenu).toHaveClass('show')
+ resolve()
+ })
})
})
- it('should remove event listener on touch-enabled device that was added in show method', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Dropdwon item</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should remove event listener on touch-enabled device that was added in show method', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Dropdown item</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const defaultValueOnTouchStart = document.documentElement.ontouchstart
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = new Dropdown(btnDropdown)
+ const defaultValueOnTouchStart = document.documentElement.ontouchstart
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(btnDropdown)
- document.documentElement.ontouchstart = noop
- spyOn(EventHandler, 'off')
+ document.documentElement.ontouchstart = noop
+ const spy = spyOn(EventHandler, 'off')
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- dropdown.hide()
- })
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ dropdown.hide()
+ })
- btnDropdown.addEventListener('hidden.bs.dropdown', () => {
- expect(btnDropdown.classList.contains('show')).toEqual(false)
- expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
- expect(EventHandler.off).toHaveBeenCalled()
+ btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+ expect(btnDropdown).not.toHaveClass('show')
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
+ expect(spy).toHaveBeenCalled()
- document.documentElement.ontouchstart = defaultValueOnTouchStart
- done()
- })
+ document.documentElement.ontouchstart = defaultValueOnTouchStart
+ resolve()
+ })
- dropdown.show()
+ dropdown.show()
+ })
})
})
@@ -927,13 +1062,13 @@ describe('Dropdown', () => {
expect(dropdown._popper).toBeNull()
expect(dropdown._menu).not.toBeNull()
expect(dropdown._element).not.toBeNull()
- spyOn(EventHandler, 'off')
+ const spy = spyOn(EventHandler, 'off')
dropdown.dispose()
expect(dropdown._menu).toBeNull()
expect(dropdown._element).toBeNull()
- expect(EventHandler.off).toHaveBeenCalledWith(btnDropdown, Dropdown.EVENT_KEY)
+ expect(spy).toHaveBeenCalledWith(btnDropdown, Dropdown.EVENT_KEY)
})
it('should dispose dropdown with Popper', () => {
@@ -981,13 +1116,13 @@ describe('Dropdown', () => {
expect(dropdown._popper).not.toBeNull()
- spyOn(dropdown._popper, 'update')
- spyOn(dropdown, '_detectNavbar')
+ const spyUpdate = spyOn(dropdown._popper, 'update')
+ const spyDetect = spyOn(dropdown, '_detectNavbar')
dropdown.update()
- expect(dropdown._popper.update).toHaveBeenCalled()
- expect(dropdown._detectNavbar).toHaveBeenCalled()
+ expect(spyUpdate).toHaveBeenCalled()
+ expect(spyDetect).toHaveBeenCalled()
})
it('should just detect navbar on update', () => {
@@ -1003,904 +1138,1083 @@ describe('Dropdown', () => {
const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdown = new Dropdown(btnDropdown)
- spyOn(dropdown, '_detectNavbar')
+ const spy = spyOn(dropdown, '_detectNavbar')
dropdown.update()
expect(dropdown._popper).toBeNull()
- expect(dropdown._detectNavbar).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
})
})
describe('data-api', () => {
- it('should show and hide a dropdown', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
-
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- let showEventTriggered = false
- let hideEventTriggered = false
+ it('should show and hide a dropdown', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ let showEventTriggered = false
+ let hideEventTriggered = false
+
+ btnDropdown.addEventListener('show.bs.dropdown', () => {
+ showEventTriggered = true
+ })
- btnDropdown.addEventListener('show.bs.dropdown', () => {
- showEventTriggered = true
- })
+ btnDropdown.addEventListener('shown.bs.dropdown', event => setTimeout(() => {
+ expect(btnDropdown).toHaveClass('show')
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+ expect(showEventTriggered).toBeTrue()
+ expect(event.relatedTarget).toEqual(btnDropdown)
+ document.body.click()
+ }))
- btnDropdown.addEventListener('shown.bs.dropdown', event => setTimeout(() => {
- expect(btnDropdown.classList.contains('show')).toEqual(true)
- expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
- expect(showEventTriggered).toEqual(true)
- expect(event.relatedTarget).toEqual(btnDropdown)
- document.body.click()
- }))
+ btnDropdown.addEventListener('hide.bs.dropdown', () => {
+ hideEventTriggered = true
+ })
- btnDropdown.addEventListener('hide.bs.dropdown', () => {
- hideEventTriggered = true
- })
+ btnDropdown.addEventListener('hidden.bs.dropdown', event => {
+ expect(btnDropdown).not.toHaveClass('show')
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
+ expect(hideEventTriggered).toBeTrue()
+ expect(event.relatedTarget).toEqual(btnDropdown)
+ resolve()
+ })
- btnDropdown.addEventListener('hidden.bs.dropdown', event => {
- expect(btnDropdown.classList.contains('show')).toEqual(false)
- expect(btnDropdown.getAttribute('aria-expanded')).toEqual('false')
- expect(hideEventTriggered).toEqual(true)
- expect(event.relatedTarget).toEqual(btnDropdown)
- done()
+ btnDropdown.click()
})
-
- btnDropdown.click()
})
- it('should not use Popper in navbar', done => {
- fixtureEl.innerHTML = [
- '<nav class="navbar navbar-expand-md navbar-light bg-light">',
- ' <div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- ' </div>',
- '</nav>'
- ].join('')
+ it('should not use "static" Popper in navbar', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<nav class="navbar navbar-expand-md bg-light">',
+ ' <div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ ' </div>',
+ '</nav>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+ const dropdown = new Dropdown(btnDropdown)
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- expect(dropdown._popper).toBeNull()
- expect(dropdownMenu.getAttribute('style')).toEqual(null, 'no inline style applied by Popper')
- done()
- })
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ expect(dropdown._popper).not.toBeNull()
+ expect(dropdownMenu.getAttribute('data-bs-popper')).toEqual('static')
+ resolve()
+ })
- dropdown.show()
+ dropdown.show()
+ })
})
- it('should not collapse the dropdown when clicking a select option nested in the dropdown', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <select>',
- ' <option selected>Open this select menu</option>',
- ' <option value="1">One</option>',
- ' </select>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should not collapse the dropdown when clicking a select option nested in the dropdown', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <select>',
+ ' <option selected>Open this select menu</option>',
+ ' <option value="1">One</option>',
+ ' </select>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+ const dropdown = new Dropdown(btnDropdown)
- const hideSpy = spyOn(dropdown, '_completeHide')
+ const hideSpy = spyOn(dropdown, '_completeHide')
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- const clickEvent = new MouseEvent('click', {
- bubbles: true
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ const clickEvent = new MouseEvent('click', {
+ bubbles: true
+ })
+
+ dropdownMenu.querySelector('option').dispatchEvent(clickEvent)
})
- dropdownMenu.querySelector('option').dispatchEvent(clickEvent)
- })
+ dropdownMenu.addEventListener('click', event => {
+ expect(event.target.tagName).toMatch(/select|option/i)
- dropdownMenu.addEventListener('click', event => {
- expect(event.target.tagName).toMatch(/select|option/i)
+ Dropdown.clearMenus(event)
- Dropdown.clearMenus(event)
+ setTimeout(() => {
+ expect(hideSpy).not.toHaveBeenCalled()
+ resolve()
+ }, 10)
+ })
- setTimeout(() => {
- expect(hideSpy).not.toHaveBeenCalled()
- done()
- }, 10)
+ dropdown.show()
})
-
- dropdown.show()
})
- it('should manage bs attribute `data-bs-popper`="none" when dropdown is in navbar', done => {
- fixtureEl.innerHTML = [
- '<nav class="navbar navbar-expand-md navbar-light bg-light">',
- ' <div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- ' </div>',
- '</nav>'
- ].join('')
+ it('should manage bs attribute `data-bs-popper`="static" when dropdown is in navbar', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<nav class="navbar navbar-expand-md bg-light">',
+ ' <div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ ' </div>',
+ '</nav>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+ const dropdown = new Dropdown(btnDropdown)
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- expect(dropdownMenu.getAttribute('data-bs-popper')).toEqual('none')
- dropdown.hide()
- })
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ expect(dropdownMenu.getAttribute('data-bs-popper')).toEqual('static')
+ dropdown.hide()
+ })
- btnDropdown.addEventListener('hidden.bs.dropdown', () => {
- expect(dropdownMenu.getAttribute('data-bs-popper')).toBeNull()
- done()
- })
+ btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+ expect(dropdownMenu.getAttribute('data-bs-popper')).toBeNull()
+ resolve()
+ })
- dropdown.show()
+ dropdown.show()
+ })
})
- it('should not use Popper if display set to static', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-display="static">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should not use Popper if display set to static', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-display="static">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- // Popper adds this attribute when we use it
- expect(dropdownMenu.getAttribute('data-popper-placement')).toEqual(null)
- done()
- })
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ // Popper adds this attribute when we use it
+ expect(dropdownMenu.getAttribute('data-popper-placement')).toBeNull()
+ resolve()
+ })
- btnDropdown.click()
+ btnDropdown.click()
+ })
})
- it('should manage bs attribute `data-bs-popper`="static" when display set to static', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-display="static">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should manage bs attribute `data-bs-popper`="static" when display set to static', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-display="static">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
- const dropdown = new Dropdown(btnDropdown)
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+ const dropdown = new Dropdown(btnDropdown)
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- expect(dropdownMenu.getAttribute('data-bs-popper')).toEqual('static')
- dropdown.hide()
- })
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ expect(dropdownMenu.getAttribute('data-bs-popper')).toEqual('static')
+ dropdown.hide()
+ })
- btnDropdown.addEventListener('hidden.bs.dropdown', () => {
- expect(dropdownMenu.getAttribute('data-bs-popper')).toBeNull()
- done()
- })
+ btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+ expect(dropdownMenu.getAttribute('data-bs-popper')).toBeNull()
+ resolve()
+ })
- dropdown.show()
+ dropdown.show()
+ })
})
- it('should remove "show" class if tabbing outside of menu', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should remove "show" class if tabbing outside of menu', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- btnDropdown.addEventListener('shown.bs.dropdown', () => {
- expect(btnDropdown.classList.contains('show')).toEqual(true)
+ btnDropdown.addEventListener('shown.bs.dropdown', () => {
+ expect(btnDropdown).toHaveClass('show')
- const keyup = createEvent('keyup')
+ const keyup = createEvent('keyup')
- keyup.key = 'Tab'
- document.dispatchEvent(keyup)
- })
+ keyup.key = 'Tab'
+ document.dispatchEvent(keyup)
+ })
- btnDropdown.addEventListener('hidden.bs.dropdown', () => {
- expect(btnDropdown.classList.contains('show')).toEqual(false)
- done()
- })
+ btnDropdown.addEventListener('hidden.bs.dropdown', () => {
+ expect(btnDropdown).not.toHaveClass('show')
+ resolve()
+ })
- btnDropdown.click()
+ btnDropdown.click()
+ })
})
- it('should remove "show" class if body is clicked, with multiple dropdowns', done => {
- fixtureEl.innerHTML = [
- '<div class="nav">',
- ' <div class="dropdown" id="testmenu">',
- ' <a class="dropdown-toggle" data-bs-toggle="dropdown" href="#testmenu">Test menu</a>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#sub1">Submenu 1</a>',
- ' </div>',
- ' </div>',
- '</div>',
- '<div class="btn-group">',
- ' <button class="btn">Actions</button>',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown"></button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Action 1</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should remove "show" class if body is clicked, with multiple dropdowns', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="nav">',
+ ' <div class="dropdown" id="testmenu">',
+ ' <a class="dropdown-toggle" data-bs-toggle="dropdown" href="#testmenu">Test menu</a>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#sub1">Submenu 1</a>',
+ ' </div>',
+ ' </div>',
+ '</div>',
+ '<div class="btn-group">',
+ ' <button class="btn">Actions</button>',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown"></button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Action 1</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const triggerDropdownList = fixtureEl.querySelectorAll('[data-bs-toggle="dropdown"]')
+ const triggerDropdownList = fixtureEl.querySelectorAll('[data-bs-toggle="dropdown"]')
- expect(triggerDropdownList.length).toEqual(2)
+ expect(triggerDropdownList).toHaveSize(2)
- const [triggerDropdownFirst, triggerDropdownLast] = triggerDropdownList
+ const [triggerDropdownFirst, triggerDropdownLast] = triggerDropdownList
- triggerDropdownFirst.addEventListener('shown.bs.dropdown', () => {
- expect(triggerDropdownFirst.classList.contains('show')).toEqual(true)
- expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(1)
- document.body.click()
- })
+ triggerDropdownFirst.addEventListener('shown.bs.dropdown', () => {
+ expect(triggerDropdownFirst).toHaveClass('show')
+ expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(1)
+ document.body.click()
+ })
- triggerDropdownFirst.addEventListener('hidden.bs.dropdown', () => {
- expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(0)
- triggerDropdownLast.click()
- })
+ triggerDropdownFirst.addEventListener('hidden.bs.dropdown', () => {
+ expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(0)
+ triggerDropdownLast.click()
+ })
- triggerDropdownLast.addEventListener('shown.bs.dropdown', () => {
- expect(triggerDropdownLast.classList.contains('show')).toEqual(true)
- expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(1)
- document.body.click()
- })
+ triggerDropdownLast.addEventListener('shown.bs.dropdown', () => {
+ expect(triggerDropdownLast).toHaveClass('show')
+ expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(1)
+ document.body.click()
+ })
- triggerDropdownLast.addEventListener('hidden.bs.dropdown', () => {
- expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(0)
- done()
- })
+ triggerDropdownLast.addEventListener('hidden.bs.dropdown', () => {
+ expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(0)
+ resolve()
+ })
- triggerDropdownFirst.click()
+ triggerDropdownFirst.click()
+ })
})
- it('should remove "show" class if body if tabbing outside of menu, with multiple dropdowns', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <a class="dropdown-toggle" data-bs-toggle="dropdown" href="#testmenu">Test menu</a>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#sub1">Submenu 1</a>',
- ' </div>',
- '</div>',
- '<div class="btn-group">',
- ' <button class="btn">Actions</button>',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown"></button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Action 1</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should remove "show" class if body if tabbing outside of menu, with multiple dropdowns', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <a class="dropdown-toggle" data-bs-toggle="dropdown" href="#testmenu">Test menu</a>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#sub1">Submenu 1</a>',
+ ' </div>',
+ '</div>',
+ '<div class="btn-group">',
+ ' <button class="btn">Actions</button>',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown"></button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Action 1</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const triggerDropdownList = fixtureEl.querySelectorAll('[data-bs-toggle="dropdown"]')
+ const triggerDropdownList = fixtureEl.querySelectorAll('[data-bs-toggle="dropdown"]')
- expect(triggerDropdownList.length).toEqual(2)
+ expect(triggerDropdownList).toHaveSize(2)
- const [triggerDropdownFirst, triggerDropdownLast] = triggerDropdownList
+ const [triggerDropdownFirst, triggerDropdownLast] = triggerDropdownList
- triggerDropdownFirst.addEventListener('shown.bs.dropdown', () => {
- expect(triggerDropdownFirst.classList.contains('show')).toEqual(true, '"show" class added on click')
- expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(1, 'only one dropdown is shown')
+ triggerDropdownFirst.addEventListener('shown.bs.dropdown', () => {
+ expect(triggerDropdownFirst).toHaveClass('show')
+ expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(1)
- const keyup = createEvent('keyup')
- keyup.key = 'Tab'
+ const keyup = createEvent('keyup')
+ keyup.key = 'Tab'
- document.dispatchEvent(keyup)
- })
+ document.dispatchEvent(keyup)
+ })
- triggerDropdownFirst.addEventListener('hidden.bs.dropdown', () => {
- expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(0, '"show" class removed')
- triggerDropdownLast.click()
- })
+ triggerDropdownFirst.addEventListener('hidden.bs.dropdown', () => {
+ expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(0)
+ triggerDropdownLast.click()
+ })
- triggerDropdownLast.addEventListener('shown.bs.dropdown', () => {
- expect(triggerDropdownLast.classList.contains('show')).toEqual(true, '"show" class added on click')
- expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(1, 'only one dropdown is shown')
+ triggerDropdownLast.addEventListener('shown.bs.dropdown', () => {
+ expect(triggerDropdownLast).toHaveClass('show')
+ expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(1)
- const keyup = createEvent('keyup')
- keyup.key = 'Tab'
+ const keyup = createEvent('keyup')
+ keyup.key = 'Tab'
- document.dispatchEvent(keyup)
- })
+ document.dispatchEvent(keyup)
+ })
- triggerDropdownLast.addEventListener('hidden.bs.dropdown', () => {
- expect(fixtureEl.querySelectorAll('.dropdown-menu.show').length).toEqual(0, '"show" class removed')
- done()
- })
+ triggerDropdownLast.addEventListener('hidden.bs.dropdown', () => {
+ expect(fixtureEl.querySelectorAll('.dropdown-menu.show')).toHaveSize(0)
+ resolve()
+ })
- triggerDropdownFirst.click()
+ triggerDropdownFirst.click()
+ })
})
- it('should fire hide and hidden event without a clickEvent if event type is not click', done => {
+ it('should be able to identify clicked dropdown, even with multiple dropdowns in the same tag', () => {
fixtureEl.innerHTML = [
'<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#sub1">Submenu 1</a>',
+ ' <button id="dropdown1" class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown toggle</button>',
+ ' <div id="menu1" class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Dropdown item</a>',
+ ' </div>',
+ ' <button id="dropdown2" class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown toggle</button>',
+ ' <div id="menu2" class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Dropdown item</a>',
' </div>',
'</div>'
].join('')
- const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
-
- triggerDropdown.addEventListener('hide.bs.dropdown', event => {
- expect(event.clickEvent).toBeUndefined()
- })
+ const dropdownToggle1 = fixtureEl.querySelector('#dropdown1')
+ const dropdownToggle2 = fixtureEl.querySelector('#dropdown2')
+ const dropdownMenu1 = fixtureEl.querySelector('#menu1')
+ const dropdownMenu2 = fixtureEl.querySelector('#menu2')
+ const spy = spyOn(Dropdown, 'getOrCreateInstance').and.callThrough()
- triggerDropdown.addEventListener('hidden.bs.dropdown', event => {
- expect(event.clickEvent).toBeUndefined()
- done()
- })
+ dropdownToggle1.click()
+ expect(spy).toHaveBeenCalledWith(dropdownToggle1)
- triggerDropdown.addEventListener('shown.bs.dropdown', () => {
- const keydown = createEvent('keydown')
+ dropdownToggle2.click()
+ expect(spy).toHaveBeenCalledWith(dropdownToggle2)
- keydown.key = 'Escape'
- triggerDropdown.dispatchEvent(keydown)
- })
+ dropdownMenu1.click()
+ expect(spy).toHaveBeenCalledWith(dropdownToggle1)
- triggerDropdown.click()
+ dropdownMenu2.click()
+ expect(spy).toHaveBeenCalledWith(dropdownToggle2)
})
- it('should bubble up the events to the parent elements', done => {
+ it('should be able to show the proper menu, even with multiple dropdowns in the same tag', () => {
fixtureEl.innerHTML = [
'<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#subMenu">Sub menu</a>',
+ ' <button id="dropdown1" class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown toggle</button>',
+ ' <div id="menu1" class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Dropdown item</a>',
+ ' </div>',
+ ' <button id="dropdown2" class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown toggle</button>',
+ ' <div id="menu2" class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Dropdown item</a>',
' </div>',
'</div>'
].join('')
- const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdownParent = fixtureEl.querySelector('.dropdown')
- const dropdown = new Dropdown(triggerDropdown)
+ const dropdownToggle1 = fixtureEl.querySelector('#dropdown1')
+ const dropdownToggle2 = fixtureEl.querySelector('#dropdown2')
+ const dropdownMenu1 = fixtureEl.querySelector('#menu1')
+ const dropdownMenu2 = fixtureEl.querySelector('#menu2')
- const showFunction = jasmine.createSpy('showFunction')
- dropdownParent.addEventListener('show.bs.dropdown', showFunction)
+ dropdownToggle1.click()
+ expect(dropdownMenu1).toHaveClass('show')
+ expect(dropdownMenu2).not.toHaveClass('show')
- const shownFunction = jasmine.createSpy('shownFunction')
- dropdownParent.addEventListener('shown.bs.dropdown', () => {
- shownFunction()
- dropdown.hide()
- })
+ dropdownToggle2.click()
+ expect(dropdownMenu1).not.toHaveClass('show')
+ expect(dropdownMenu2).toHaveClass('show')
+ })
- const hideFunction = jasmine.createSpy('hideFunction')
- dropdownParent.addEventListener('hide.bs.dropdown', hideFunction)
+ it('should fire hide and hidden event without a clickEvent if event type is not click', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#sub1">Submenu 1</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- dropdownParent.addEventListener('hidden.bs.dropdown', () => {
- expect(showFunction).toHaveBeenCalled()
- expect(shownFunction).toHaveBeenCalled()
- expect(hideFunction).toHaveBeenCalled()
- done()
- })
+ const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+
+ triggerDropdown.addEventListener('hide.bs.dropdown', event => {
+ expect(event.clickEvent).toBeUndefined()
+ })
+
+ triggerDropdown.addEventListener('hidden.bs.dropdown', event => {
+ expect(event.clickEvent).toBeUndefined()
+ resolve()
+ })
+
+ triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+ const keydown = createEvent('keydown')
- dropdown.show()
+ keydown.key = 'Escape'
+ triggerDropdown.dispatchEvent(keydown)
+ })
+
+ triggerDropdown.click()
+ })
})
- it('should ignore keyboard events within <input>s and <textarea>s', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#sub1">Submenu 1</a>',
- ' <input type="text">',
- ' <textarea></textarea>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should bubble up the events to the parent elements', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#subMenu">Sub menu</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const input = fixtureEl.querySelector('input')
- const textarea = fixtureEl.querySelector('textarea')
+ const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdownParent = fixtureEl.querySelector('.dropdown')
+ const dropdown = new Dropdown(triggerDropdown)
- triggerDropdown.addEventListener('shown.bs.dropdown', () => {
- input.focus()
- const keydown = createEvent('keydown')
+ const showFunction = jasmine.createSpy('showFunction')
+ dropdownParent.addEventListener('show.bs.dropdown', showFunction)
- keydown.key = 'ArrowUp'
- input.dispatchEvent(keydown)
+ const shownFunction = jasmine.createSpy('shownFunction')
+ dropdownParent.addEventListener('shown.bs.dropdown', () => {
+ shownFunction()
+ dropdown.hide()
+ })
- expect(document.activeElement).toEqual(input, 'input still focused')
+ const hideFunction = jasmine.createSpy('hideFunction')
+ dropdownParent.addEventListener('hide.bs.dropdown', hideFunction)
- textarea.focus()
- textarea.dispatchEvent(keydown)
+ dropdownParent.addEventListener('hidden.bs.dropdown', () => {
+ expect(showFunction).toHaveBeenCalled()
+ expect(shownFunction).toHaveBeenCalled()
+ expect(hideFunction).toHaveBeenCalled()
+ resolve()
+ })
- expect(document.activeElement).toEqual(textarea, 'textarea still focused')
- done()
+ dropdown.show()
})
-
- triggerDropdown.click()
})
- it('should skip disabled element when using keyboard navigation', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item disabled" href="#sub1">Submenu 1</a>',
- ' <button class="dropdown-item" type="button" disabled>Disabled button</button>',
- ' <a id="item1" class="dropdown-item" href="#">Another link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should ignore keyboard events within <input>s and <textarea>s', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#sub1">Submenu 1</a>',
+ ' <input type="text">',
+ ' <textarea></textarea>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const input = fixtureEl.querySelector('input')
+ const textarea = fixtureEl.querySelector('textarea')
- triggerDropdown.addEventListener('shown.bs.dropdown', () => {
- const keydown = createEvent('keydown')
- keydown.key = 'ArrowDown'
+ triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+ input.focus()
+ const keydown = createEvent('keydown')
- triggerDropdown.dispatchEvent(keydown)
- triggerDropdown.dispatchEvent(keydown)
+ keydown.key = 'ArrowUp'
+ input.dispatchEvent(keydown)
- expect(document.activeElement.classList.contains('disabled')).toEqual(false, '.disabled not focused')
- expect(document.activeElement.hasAttribute('disabled')).toEqual(false, ':disabled not focused')
- done()
- })
+ expect(document.activeElement).toEqual(input, 'input still focused')
+
+ textarea.focus()
+ textarea.dispatchEvent(keydown)
+
+ expect(document.activeElement).toEqual(textarea, 'textarea still focused')
+ resolve()
+ })
- triggerDropdown.click()
+ triggerDropdown.click()
+ })
})
- it('should skip hidden element when using keyboard navigation', done => {
- fixtureEl.innerHTML = [
- '<style>',
- ' .d-none {',
- ' display: none;',
- ' }',
- '</style>',
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <button class="dropdown-item d-none" type="button">Hidden button by class</button>',
- ' <a class="dropdown-item" href="#sub1" style="display: none">Hidden link</a>',
- ' <a class="dropdown-item" href="#sub1" style="visibility: hidden">Hidden link</a>',
- ' <a id="item1" class="dropdown-item" href="#">Another link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should skip disabled element when using keyboard navigation', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item disabled" href="#sub1">Submenu 1</a>',
+ ' <button class="dropdown-item" type="button" disabled>Disabled button</button>',
+ ' <a id="item1" class="dropdown-item" href="#">Another link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- triggerDropdown.addEventListener('shown.bs.dropdown', () => {
- const keydown = createEvent('keydown')
- keydown.key = 'ArrowDown'
+ triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+ const keydown = createEvent('keydown')
+ keydown.key = 'ArrowDown'
- triggerDropdown.dispatchEvent(keydown)
+ triggerDropdown.dispatchEvent(keydown)
+ triggerDropdown.dispatchEvent(keydown)
- expect(document.activeElement.classList.contains('d-none')).toEqual(false, '.d-none not focused')
- expect(document.activeElement.style.display).not.toBe('none', '"display: none" not focused')
- expect(document.activeElement.style.visibility).not.toBe('hidden', '"visibility: hidden" not focused')
+ expect(document.activeElement).not.toHaveClass('disabled')
+ expect(document.activeElement.hasAttribute('disabled')).toBeFalse()
+ resolve()
+ })
- done()
+ triggerDropdown.click()
})
-
- triggerDropdown.click()
})
- it('should focus next/previous element when using keyboard navigation', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a id="item1" class="dropdown-item" href="#">A link</a>',
- ' <a id="item2" class="dropdown-item" href="#">Another link</a>',
- ' </div>',
- '</div>'
- ].join('')
-
- const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const item1 = fixtureEl.querySelector('#item1')
- const item2 = fixtureEl.querySelector('#item2')
+ it('should skip hidden element when using keyboard navigation', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<style>',
+ ' .d-none {',
+ ' display: none;',
+ ' }',
+ '</style>',
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <button class="dropdown-item d-none" type="button">Hidden button by class</button>',
+ ' <a class="dropdown-item" href="#sub1" style="display: none">Hidden link</a>',
+ ' <a class="dropdown-item" href="#sub1" style="visibility: hidden">Hidden link</a>',
+ ' <a id="item1" class="dropdown-item" href="#">Another link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- triggerDropdown.addEventListener('shown.bs.dropdown', () => {
- const keydownArrowDown = createEvent('keydown')
- keydownArrowDown.key = 'ArrowDown'
+ const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- triggerDropdown.dispatchEvent(keydownArrowDown)
- expect(document.activeElement).toEqual(item1, 'item1 is focused')
+ triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+ const keydown = createEvent('keydown')
+ keydown.key = 'ArrowDown'
- document.activeElement.dispatchEvent(keydownArrowDown)
- expect(document.activeElement).toEqual(item2, 'item2 is focused')
+ triggerDropdown.dispatchEvent(keydown)
- const keydownArrowUp = createEvent('keydown')
- keydownArrowUp.key = 'ArrowUp'
+ expect(document.activeElement).not.toHaveClass('d-none')
+ expect(document.activeElement.style.display).not.toEqual('none')
+ expect(document.activeElement.style.visibility).not.toEqual('hidden')
- document.activeElement.dispatchEvent(keydownArrowUp)
- expect(document.activeElement).toEqual(item1, 'item1 is focused')
+ resolve()
+ })
- done()
+ triggerDropdown.click()
})
-
- triggerDropdown.click()
})
- it('should open the dropdown and focus on the last item when using ArrowUp for the first time', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a id="item1" class="dropdown-item" href="#">A link</a>',
- ' <a id="item2" class="dropdown-item" href="#">Another link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should focus next/previous element when using keyboard navigation', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a id="item1" class="dropdown-item" href="#">A link</a>',
+ ' <a id="item2" class="dropdown-item" href="#">Another link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const lastItem = fixtureEl.querySelector('#item2')
+ const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const item1 = fixtureEl.querySelector('#item1')
+ const item2 = fixtureEl.querySelector('#item2')
- triggerDropdown.addEventListener('shown.bs.dropdown', () => {
- setTimeout(() => {
- expect(document.activeElement).toEqual(lastItem, 'item2 is focused')
- done()
+ triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+ const keydownArrowDown = createEvent('keydown')
+ keydownArrowDown.key = 'ArrowDown'
+
+ triggerDropdown.dispatchEvent(keydownArrowDown)
+ expect(document.activeElement).toEqual(item1, 'item1 is focused')
+
+ document.activeElement.dispatchEvent(keydownArrowDown)
+ expect(document.activeElement).toEqual(item2, 'item2 is focused')
+
+ const keydownArrowUp = createEvent('keydown')
+ keydownArrowUp.key = 'ArrowUp'
+
+ document.activeElement.dispatchEvent(keydownArrowUp)
+ expect(document.activeElement).toEqual(item1, 'item1 is focused')
+
+ resolve()
})
- })
- const keydown = createEvent('keydown')
- keydown.key = 'ArrowUp'
- triggerDropdown.dispatchEvent(keydown)
+ triggerDropdown.click()
+ })
})
- it('should open the dropdown and focus on the first item when using ArrowDown for the first time', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a id="item1" class="dropdown-item" href="#">A link</a>',
- ' <a id="item2" class="dropdown-item" href="#">Another link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should open the dropdown and focus on the last item when using ArrowUp for the first time', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a id="item1" class="dropdown-item" href="#">A link</a>',
+ ' <a id="item2" class="dropdown-item" href="#">Another link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const firstItem = fixtureEl.querySelector('#item1')
+ const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const lastItem = fixtureEl.querySelector('#item2')
- triggerDropdown.addEventListener('shown.bs.dropdown', () => {
- setTimeout(() => {
- expect(document.activeElement).toEqual(firstItem, 'item1 is focused')
- done()
+ triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+ setTimeout(() => {
+ expect(document.activeElement).toEqual(lastItem, 'item2 is focused')
+ resolve()
+ })
})
- })
- const keydown = createEvent('keydown')
- keydown.key = 'ArrowDown'
- triggerDropdown.dispatchEvent(keydown)
+ const keydown = createEvent('keydown')
+ keydown.key = 'ArrowUp'
+ triggerDropdown.dispatchEvent(keydown)
+ })
})
- it('should not close the dropdown if the user clicks on a text field within dropdown-menu', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <input type="text">',
- ' </div>',
- '</div>'
- ].join('')
+ it('should open the dropdown and focus on the first item when using ArrowDown for the first time', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a id="item1" class="dropdown-item" href="#">A link</a>',
+ ' <a id="item2" class="dropdown-item" href="#">Another link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const input = fixtureEl.querySelector('input')
+ const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const firstItem = fixtureEl.querySelector('#item1')
- input.addEventListener('click', () => {
- expect(triggerDropdown.classList.contains('show')).toEqual(true, 'dropdown menu is shown')
- done()
- })
+ triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+ setTimeout(() => {
+ expect(document.activeElement).toEqual(firstItem, 'item1 is focused')
+ resolve()
+ })
+ })
- triggerDropdown.addEventListener('shown.bs.dropdown', () => {
- expect(triggerDropdown.classList.contains('show')).toEqual(true, 'dropdown menu is shown')
- input.dispatchEvent(createEvent('click'))
+ const keydown = createEvent('keydown')
+ keydown.key = 'ArrowDown'
+ triggerDropdown.dispatchEvent(keydown)
})
-
- triggerDropdown.click()
})
- it('should not close the dropdown if the user clicks on a textarea within dropdown-menu', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <textarea></textarea>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should not close the dropdown if the user clicks on a text field within dropdown-menu', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <input type="text">',
+ ' </div>',
+ '</div>'
+ ].join('')
- const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const textarea = fixtureEl.querySelector('textarea')
+ const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const input = fixtureEl.querySelector('input')
- textarea.addEventListener('click', () => {
- expect(triggerDropdown.classList.contains('show')).toEqual(true, 'dropdown menu is shown')
- done()
- })
+ input.addEventListener('click', () => {
+ expect(triggerDropdown).toHaveClass('show')
+ resolve()
+ })
- triggerDropdown.addEventListener('shown.bs.dropdown', () => {
- expect(triggerDropdown.classList.contains('show')).toEqual(true, 'dropdown menu is shown')
- textarea.dispatchEvent(createEvent('click'))
- })
+ triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+ expect(triggerDropdown).toHaveClass('show')
+ input.dispatchEvent(createEvent('click'))
+ })
- triggerDropdown.click()
+ triggerDropdown.click()
+ })
})
- it('should close the dropdown if the user clicks on a text field that is not contained within dropdown-menu', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' </div>',
- '</div>',
- '<input type="text">'
- ]
+ it('should not close the dropdown if the user clicks on a textarea within dropdown-menu', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <textarea></textarea>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const input = fixtureEl.querySelector('input')
+ const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const textarea = fixtureEl.querySelector('textarea')
- triggerDropdown.addEventListener('hidden.bs.dropdown', () => {
- expect().nothing()
- done()
- })
+ textarea.addEventListener('click', () => {
+ expect(triggerDropdown).toHaveClass('show')
+ resolve()
+ })
- triggerDropdown.addEventListener('shown.bs.dropdown', () => {
- input.dispatchEvent(createEvent('click', {
- bubbles: true
- }))
- })
+ triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+ expect(triggerDropdown).toHaveClass('show')
+ textarea.dispatchEvent(createEvent('click'))
+ })
- triggerDropdown.click()
+ triggerDropdown.click()
+ })
})
- it('should ignore keyboard events for <input>s and <textarea>s within dropdown-menu, except for escape key', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#sub1">Submenu 1</a>',
- ' <input type="text">',
- ' <textarea></textarea>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should close the dropdown if the user clicks on a text field that is not contained within dropdown-menu', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' </div>',
+ '</div>',
+ '<input type="text">'
+ ].join('')
- const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const input = fixtureEl.querySelector('input')
- const textarea = fixtureEl.querySelector('textarea')
+ const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const input = fixtureEl.querySelector('input')
- const keydownSpace = createEvent('keydown')
- keydownSpace.key = 'Space'
+ triggerDropdown.addEventListener('hidden.bs.dropdown', () => {
+ expect().nothing()
+ resolve()
+ })
+
+ triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+ input.dispatchEvent(createEvent('click', {
+ bubbles: true
+ }))
+ })
- const keydownArrowUp = createEvent('keydown')
- keydownArrowUp.key = 'ArrowUp'
+ triggerDropdown.click()
+ })
+ })
+
+ it('should ignore keyboard events for <input>s and <textarea>s within dropdown-menu, except for escape key', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#sub1">Submenu 1</a>',
+ ' <input type="text">',
+ ' <textarea></textarea>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const input = fixtureEl.querySelector('input')
+ const textarea = fixtureEl.querySelector('textarea')
+
+ const test = (eventKey, elementToDispatch) => {
+ const event = createEvent('keydown')
+ event.key = eventKey
+ elementToDispatch.focus()
+ elementToDispatch.dispatchEvent(event)
+ expect(document.activeElement).toEqual(elementToDispatch, `${elementToDispatch.tagName} still focused`)
+ }
- const keydownArrowDown = createEvent('keydown')
- keydownArrowDown.key = 'ArrowDown'
+ const keydownEscape = createEvent('keydown')
+ keydownEscape.key = 'Escape'
- const keydownEscape = createEvent('keydown')
- keydownEscape.key = 'Escape'
+ triggerDropdown.addEventListener('shown.bs.dropdown', () => {
+ // Key Space
+ test('Space', input)
- triggerDropdown.addEventListener('shown.bs.dropdown', () => {
- // Key Space
- input.focus()
- input.dispatchEvent(keydownSpace)
+ test('Space', textarea)
- expect(document.activeElement).toEqual(input, 'input still focused')
+ // Key ArrowUp
+ test('ArrowUp', input)
- textarea.focus()
- textarea.dispatchEvent(keydownSpace)
+ test('ArrowUp', textarea)
- expect(document.activeElement).toEqual(textarea, 'textarea still focused')
+ // Key ArrowDown
+ test('ArrowDown', input)
- // Key ArrowUp
- input.focus()
- input.dispatchEvent(keydownArrowUp)
+ test('ArrowDown', textarea)
- expect(document.activeElement).toEqual(input, 'input still focused')
+ // Key Escape
+ input.focus()
+ input.dispatchEvent(keydownEscape)
- textarea.focus()
- textarea.dispatchEvent(keydownArrowUp)
+ expect(triggerDropdown).not.toHaveClass('show')
+ resolve()
+ })
- expect(document.activeElement).toEqual(textarea, 'textarea still focused')
+ triggerDropdown.click()
+ })
+ })
- // Key ArrowDown
- input.focus()
- input.dispatchEvent(keydownArrowDown)
+ it('should not open dropdown if escape key was pressed on the toggle', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="tabs">',
+ ' <div class="dropdown">',
+ ' <button disabled class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Secondary link</a>',
+ ' <a class="dropdown-item" href="#">Something else here</a>',
+ ' <div class="divider"></div>',
+ ' <a class="dropdown-item" href="#">Another link</a>',
+ ' </div>',
+ ' </div>',
+ '</div>'
+ ].join('')
- expect(document.activeElement).toEqual(input, 'input still focused')
+ const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = new Dropdown(triggerDropdown)
+ const button = fixtureEl.querySelector('button[data-bs-toggle="dropdown"]')
- textarea.focus()
- textarea.dispatchEvent(keydownArrowDown)
+ const spy = spyOn(dropdown, 'toggle')
- expect(document.activeElement).toEqual(textarea, 'textarea still focused')
+ // Key escape
+ button.focus()
+ // Key escape
+ const keydownEscape = createEvent('keydown')
+ keydownEscape.key = 'Escape'
+ button.dispatchEvent(keydownEscape)
- // Key Escape
- input.focus()
- input.dispatchEvent(keydownEscape)
+ setTimeout(() => {
+ expect(spy).not.toHaveBeenCalled()
+ expect(triggerDropdown).not.toHaveClass('show')
+ resolve()
+ }, 20)
+ })
+ })
+
+ it('should propagate escape key events if dropdown is closed', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="parent">',
+ ' <div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Some Item</a>',
+ ' </div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const parent = fixtureEl.querySelector('.parent')
+ const toggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+
+ const parentKeyHandler = jasmine.createSpy('parentKeyHandler')
+
+ parent.addEventListener('keydown', parentKeyHandler)
+ parent.addEventListener('keyup', () => {
+ expect(parentKeyHandler).toHaveBeenCalled()
+ resolve()
+ })
- expect(triggerDropdown.classList.contains('show')).toEqual(false, 'dropdown menu is not shown')
- done()
- })
+ const keydownEscape = createEvent('keydown', { bubbles: true })
+ keydownEscape.key = 'Escape'
+ const keyupEscape = createEvent('keyup', { bubbles: true })
+ keyupEscape.key = 'Escape'
- triggerDropdown.click()
+ toggle.focus()
+ toggle.dispatchEvent(keydownEscape)
+ toggle.dispatchEvent(keyupEscape)
+ })
})
- it('should not open dropdown if escape key was pressed on the toggle', done => {
- fixtureEl.innerHTML = [
- '<div class="tabs">',
- ' <div class="dropdown">',
- ' <button disabled class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Secondary link</a>',
- ' <a class="dropdown-item" href="#">Something else here</a>',
- ' <div class="divider"></div>',
- ' <a class="dropdown-item" href="#">Another link</a>',
- ' </div>',
- ' </div>',
- '</div>'
- ]
+ it('should not propagate escape key events if dropdown is open', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="parent">',
+ ' <div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Some Item</a>',
+ ' </div>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = new Dropdown(triggerDropdown)
- const button = fixtureEl.querySelector('button[data-bs-toggle="dropdown"]')
+ const parent = fixtureEl.querySelector('.parent')
+ const toggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- spyOn(dropdown, 'toggle')
+ const parentKeyHandler = jasmine.createSpy('parentKeyHandler')
- // Key escape
- button.focus()
- // Key escape
- const keydownEscape = createEvent('keydown')
- keydownEscape.key = 'Escape'
- button.dispatchEvent(keydownEscape)
+ parent.addEventListener('keydown', parentKeyHandler)
+ parent.addEventListener('keyup', () => {
+ expect(parentKeyHandler).not.toHaveBeenCalled()
+ resolve()
+ })
+
+ const keydownEscape = createEvent('keydown', { bubbles: true })
+ keydownEscape.key = 'Escape'
+ const keyupEscape = createEvent('keyup', { bubbles: true })
+ keyupEscape.key = 'Escape'
- setTimeout(() => {
- expect(dropdown.toggle).not.toHaveBeenCalled()
- expect(triggerDropdown.classList.contains('show')).toEqual(false)
- done()
- }, 20)
+ toggle.click()
+ toggle.dispatchEvent(keydownEscape)
+ toggle.dispatchEvent(keyupEscape)
+ })
})
- it('should propagate escape key events if dropdown is closed', done => {
- fixtureEl.innerHTML = [
- '<div class="parent">',
- ' <div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Some Item</a>',
- ' </div>',
- ' </div>',
- '</div>'
- ]
+ it('should close dropdown using `escape` button, and return focus to its trigger', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Some Item</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const toggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const parent = fixtureEl.querySelector('.parent')
- const toggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ toggle.addEventListener('shown.bs.dropdown', () => {
+ const keydownEvent = createEvent('keydown', { bubbles: true })
+ keydownEvent.key = 'ArrowDown'
+ toggle.dispatchEvent(keydownEvent)
+ keydownEvent.key = 'Escape'
+ toggle.dispatchEvent(keydownEvent)
+ })
- const parentKeyHandler = jasmine.createSpy('parentKeyHandler')
+ toggle.addEventListener('hidden.bs.dropdown', () => setTimeout(() => {
+ expect(document.activeElement).toEqual(toggle)
+ resolve()
+ }))
- parent.addEventListener('keydown', parentKeyHandler)
- parent.addEventListener('keyup', () => {
- expect(parentKeyHandler).toHaveBeenCalled()
- done()
+ toggle.click()
})
+ })
- const keydownEscape = createEvent('keydown', { bubbles: true })
- keydownEscape.key = 'Escape'
- const keyupEscape = createEvent('keyup', { bubbles: true })
- keyupEscape.key = 'Escape'
+ it('should close dropdown (only) by clicking inside the dropdown menu when it has data-attribute `data-bs-auto-close="inside"`', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="inside">Dropdown toggle</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Dropdown item</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- toggle.focus()
- toggle.dispatchEvent(keydownEscape)
- toggle.dispatchEvent(keyupEscape)
- })
+ const dropdownToggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
- it('should close dropdown (only) by clicking inside the dropdown menu when it has data-attribute `data-bs-auto-close="inside"`', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="inside">Dropdown toggle</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Dropdown item</a>',
- ' </div>',
- '</div>'
- ]
+ const expectDropdownToBeOpened = () => setTimeout(() => {
+ expect(dropdownToggle).toHaveClass('show')
+ dropdownMenu.click()
+ }, 150)
- const dropdownToggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+ dropdownToggle.addEventListener('shown.bs.dropdown', () => {
+ document.documentElement.click()
+ expectDropdownToBeOpened()
+ })
- const expectDropdownToBeOpened = () => setTimeout(() => {
- expect(dropdownToggle.classList.contains('show')).toEqual(true)
- dropdownMenu.click()
- }, 150)
+ dropdownToggle.addEventListener('hidden.bs.dropdown', () => setTimeout(() => {
+ expect(dropdownToggle).not.toHaveClass('show')
+ resolve()
+ }))
- dropdownToggle.addEventListener('shown.bs.dropdown', () => {
- document.documentElement.click()
- expectDropdownToBeOpened()
+ dropdownToggle.click()
})
+ })
- dropdownToggle.addEventListener('hidden.bs.dropdown', () => setTimeout(() => {
- expect(dropdownToggle.classList.contains('show')).toEqual(false)
- done()
- }))
+ it('should close dropdown (only) by clicking outside the dropdown menu when it has data-attribute `data-bs-auto-close="outside"`', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="outside">Dropdown toggle</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Dropdown item</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- dropdownToggle.click()
- })
+ const dropdownToggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
- it('should close dropdown (only) by clicking outside the dropdown menu when it has data-attribute `data-bs-auto-close="outside"`', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="outside">Dropdown toggle</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#">Dropdown item</a>',
- ' </div>',
- '</div>'
- ]
+ const expectDropdownToBeOpened = () => setTimeout(() => {
+ expect(dropdownToggle).toHaveClass('show')
+ document.documentElement.click()
+ }, 150)
- const dropdownToggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+ dropdownToggle.addEventListener('shown.bs.dropdown', () => {
+ dropdownMenu.click()
+ expectDropdownToBeOpened()
+ })
- const expectDropdownToBeOpened = () => setTimeout(() => {
- expect(dropdownToggle.classList.contains('show')).toEqual(true)
- document.documentElement.click()
- }, 150)
+ dropdownToggle.addEventListener('hidden.bs.dropdown', () => {
+ expect(dropdownToggle).not.toHaveClass('show')
+ resolve()
+ })
- dropdownToggle.addEventListener('shown.bs.dropdown', () => {
- dropdownMenu.click()
- expectDropdownToBeOpened()
+ dropdownToggle.click()
})
+ })
- dropdownToggle.addEventListener('hidden.bs.dropdown', () => {
- expect(dropdownToggle.classList.contains('show')).toEqual(false)
- done()
- })
+ it('should not close dropdown by clicking inside or outside the dropdown menu when it has data-attribute `data-bs-auto-close="false"`', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="false">Dropdown toggle</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#">Dropdown item</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- dropdownToggle.click()
+ const dropdownToggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
+
+ const expectDropdownToBeOpened = (shouldTriggerClick = true) => setTimeout(() => {
+ expect(dropdownToggle).toHaveClass('show')
+ if (shouldTriggerClick) {
+ document.documentElement.click()
+ } else {
+ resolve()
+ }
+
+ expectDropdownToBeOpened(false)
+ }, 150)
+
+ dropdownToggle.addEventListener('shown.bs.dropdown', () => {
+ dropdownMenu.click()
+ expectDropdownToBeOpened()
+ })
+
+ dropdownToggle.click()
+ })
})
- it('should not close dropdown by clicking inside or outside the dropdown menu when it has data-attribute `data-bs-auto-close="false"`', done => {
+ it('should be able to identify clicked dropdown, no matter the markup order', () => {
fixtureEl.innerHTML = [
'<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown" data-bs-auto-close="false">Dropdown toggle</button>',
' <div class="dropdown-menu">',
' <a class="dropdown-item" href="#">Dropdown item</a>',
- ' </div>',
+ ' </div>',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown toggle</button>',
'</div>'
- ]
+ ].join('')
const dropdownToggle = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
const dropdownMenu = fixtureEl.querySelector('.dropdown-menu')
-
- const expectDropdownToBeOpened = (shouldTriggerClick = true) => setTimeout(() => {
- expect(dropdownToggle.classList.contains('show')).toEqual(true)
- if (shouldTriggerClick) {
- document.documentElement.click()
- } else {
- done()
- }
-
- expectDropdownToBeOpened(false)
- }, 150)
-
- dropdownToggle.addEventListener('shown.bs.dropdown', () => {
- dropdownMenu.click()
- expectDropdownToBeOpened()
- })
+ const spy = spyOn(Dropdown, 'getOrCreateInstance').and.callThrough()
dropdownToggle.click()
+ expect(spy).toHaveBeenCalledWith(dropdownToggle)
+ dropdownMenu.click()
+ expect(spy).toHaveBeenCalledWith(dropdownToggle)
})
})
@@ -1963,7 +2277,7 @@ describe('Dropdown', () => {
const div = fixtureEl.querySelector('div')
- expect(Dropdown.getInstance(div)).toEqual(null)
+ expect(Dropdown.getInstance(div)).toBeNull()
})
})
@@ -1984,7 +2298,7 @@ describe('Dropdown', () => {
const div = fixtureEl.querySelector('div')
- expect(Dropdown.getInstance(div)).toEqual(null)
+ expect(Dropdown.getInstance(div)).toBeNull()
expect(Dropdown.getOrCreateInstance(div)).toBeInstanceOf(Dropdown)
})
@@ -1993,7 +2307,7 @@ describe('Dropdown', () => {
const div = fixtureEl.querySelector('div')
- expect(Dropdown.getInstance(div)).toEqual(null)
+ expect(Dropdown.getInstance(div)).toBeNull()
const dropdown = Dropdown.getOrCreateInstance(div, {
display: 'dynamic'
})
@@ -2021,52 +2335,54 @@ describe('Dropdown', () => {
})
})
- it('should open dropdown when pressing keydown or keyup', done => {
- fixtureEl.innerHTML = [
- '<div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item disabled" href="#sub1">Submenu 1</a>',
- ' <button class="dropdown-item" type="button" disabled>Disabled button</button>',
- ' <a id="item1" class="dropdown-item" href="#">Another link</a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should open dropdown when pressing keydown or keyup', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown">Dropdown</button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item disabled" href="#sub1">Submenu 1</a>',
+ ' <button class="dropdown-item" type="button" disabled>Disabled button</button>',
+ ' <a id="item1" class="dropdown-item" href="#">Another link</a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const dropdown = fixtureEl.querySelector('.dropdown')
+ const triggerDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const dropdown = fixtureEl.querySelector('.dropdown')
- const keydown = createEvent('keydown')
- keydown.key = 'ArrowDown'
+ const keydown = createEvent('keydown')
+ keydown.key = 'ArrowDown'
- const keyup = createEvent('keyup')
- keyup.key = 'ArrowUp'
+ const keyup = createEvent('keyup')
+ keyup.key = 'ArrowUp'
- const handleArrowDown = () => {
- expect(triggerDropdown.classList.contains('show')).toEqual(true)
- expect(triggerDropdown.getAttribute('aria-expanded')).toEqual('true')
- setTimeout(() => {
- dropdown.hide()
- keydown.key = 'ArrowUp'
- triggerDropdown.dispatchEvent(keyup)
- }, 20)
- }
-
- const handleArrowUp = () => {
- expect(triggerDropdown.classList.contains('show')).toEqual(true)
- expect(triggerDropdown.getAttribute('aria-expanded')).toEqual('true')
- done()
- }
-
- dropdown.addEventListener('shown.bs.dropdown', event => {
- if (event.target.key === 'ArrowDown') {
- handleArrowDown()
- } else {
- handleArrowUp()
+ const handleArrowDown = () => {
+ expect(triggerDropdown).toHaveClass('show')
+ expect(triggerDropdown.getAttribute('aria-expanded')).toEqual('true')
+ setTimeout(() => {
+ dropdown.hide()
+ keydown.key = 'ArrowUp'
+ triggerDropdown.dispatchEvent(keyup)
+ }, 20)
}
- })
- triggerDropdown.dispatchEvent(keydown)
+ const handleArrowUp = () => {
+ expect(triggerDropdown).toHaveClass('show')
+ expect(triggerDropdown.getAttribute('aria-expanded')).toEqual('true')
+ resolve()
+ }
+
+ dropdown.addEventListener('shown.bs.dropdown', event => {
+ if (event.target.key === 'ArrowDown') {
+ handleArrowDown()
+ } else {
+ handleArrowUp()
+ }
+ })
+
+ triggerDropdown.dispatchEvent(keydown)
+ })
})
it('should allow `data-bs-toggle="dropdown"` click events to bubble up', () => {
@@ -2092,27 +2408,29 @@ describe('Dropdown', () => {
expect(delegatedClickListener).toHaveBeenCalled()
})
- it('should open the dropdown when clicking the child element inside `data-bs-toggle="dropdown"`', done => {
- fixtureEl.innerHTML = [
- '<div class="container">',
- ' <div class="dropdown">',
- ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown"><span id="childElement">Dropdown</span></button>',
- ' <div class="dropdown-menu">',
- ' <a class="dropdown-item" href="#subMenu">Sub menu</a>',
- ' </div>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should open the dropdown when clicking the child element inside `data-bs-toggle="dropdown"`', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="container">',
+ ' <div class="dropdown">',
+ ' <button class="btn dropdown-toggle" data-bs-toggle="dropdown"><span id="childElement">Dropdown</span></button>',
+ ' <div class="dropdown-menu">',
+ ' <a class="dropdown-item" href="#subMenu">Sub menu</a>',
+ ' </div>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
- const childElement = fixtureEl.querySelector('#childElement')
+ const btnDropdown = fixtureEl.querySelector('[data-bs-toggle="dropdown"]')
+ const childElement = fixtureEl.querySelector('#childElement')
- btnDropdown.addEventListener('shown.bs.dropdown', () => setTimeout(() => {
- expect(btnDropdown.classList.contains('show')).toEqual(true)
- expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
- done()
- }))
+ btnDropdown.addEventListener('shown.bs.dropdown', () => setTimeout(() => {
+ expect(btnDropdown).toHaveClass('show')
+ expect(btnDropdown.getAttribute('aria-expanded')).toEqual('true')
+ resolve()
+ }))
- childElement.click()
+ childElement.click()
+ })
})
})
diff --git a/js/tests/unit/jquery.spec.js b/js/tests/unit/jquery.spec.js
index 1c9258bd1..7d7f29dc7 100644
--- a/js/tests/unit/jquery.spec.js
+++ b/js/tests/unit/jquery.spec.js
@@ -1,18 +1,18 @@
/* eslint-env jquery */
-import Alert from '../../src/alert'
-import Button from '../../src/button'
-import Carousel from '../../src/carousel'
-import Collapse from '../../src/collapse'
-import Dropdown from '../../src/dropdown'
-import Modal from '../../src/modal'
-import Offcanvas from '../../src/offcanvas'
-import Popover from '../../src/popover'
-import ScrollSpy from '../../src/scrollspy'
-import Tab from '../../src/tab'
-import Toast from '../../src/toast'
-import Tooltip from '../../src/tooltip'
-import { getFixture, clearFixture } from '../helpers/fixture'
+import Alert from '../../src/alert.js'
+import Button from '../../src/button.js'
+import Carousel from '../../src/carousel.js'
+import Collapse from '../../src/collapse.js'
+import Dropdown from '../../src/dropdown.js'
+import Modal from '../../src/modal.js'
+import Offcanvas from '../../src/offcanvas.js'
+import Popover from '../../src/popover.js'
+import ScrollSpy from '../../src/scrollspy.js'
+import Tab from '../../src/tab.js'
+import Toast from '../../src/toast.js'
+import Tooltip from '../../src/tooltip.js'
+import { clearFixture, getFixture } from '../helpers/fixture.js'
describe('jQuery', () => {
let fixtureEl
@@ -40,19 +40,21 @@ describe('jQuery', () => {
expect(Tooltip.jQueryInterface).toEqual(jQuery.fn.tooltip)
})
- it('should use jQuery event system', done => {
- fixtureEl.innerHTML = [
- '<div class="alert">',
- ' <button type="button" data-bs-dismiss="alert">x</button>',
- '</div>'
- ].join('')
-
- $(fixtureEl).find('.alert')
- .one('closed.bs.alert', () => {
- expect($(fixtureEl).find('.alert').length).toEqual(0)
- done()
- })
-
- $(fixtureEl).find('button').trigger('click')
+ it('should use jQuery event system', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="alert">',
+ ' <button type="button" data-bs-dismiss="alert">x</button>',
+ '</div>'
+ ].join('')
+
+ $(fixtureEl).find('.alert')
+ .one('closed.bs.alert', () => {
+ expect($(fixtureEl).find('.alert')).toHaveSize(0)
+ resolve()
+ })
+
+ $(fixtureEl).find('button').trigger('click')
+ })
})
})
diff --git a/js/tests/unit/modal.spec.js b/js/tests/unit/modal.spec.js
index 613b0a0a1..2aa0b7655 100644
--- a/js/tests/unit/modal.spec.js
+++ b/js/tests/unit/modal.spec.js
@@ -1,7 +1,9 @@
-import Modal from '../../src/modal'
-import EventHandler from '../../src/dom/event-handler'
-import ScrollBarHelper from '../../src/util/scrollbar'
-import { clearBodyAndDocument, clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
+import EventHandler from '../../src/dom/event-handler.js'
+import Modal from '../../src/modal.js'
+import ScrollBarHelper from '../../src/util/scrollbar.js'
+import {
+ clearBodyAndDocument, clearFixture, createEvent, getFixture, jQueryMock
+} from '../helpers/fixture.js'
describe('Modal', () => {
let fixtureEl
@@ -56,95 +58,101 @@ describe('Modal', () => {
})
describe('toggle', () => {
- it('should call ScrollBarHelper to handle scrollBar on body', done => {
- fixtureEl.innerHTML = [
- '<div class="modal"><div class="modal-dialog"></div></div>'
- ].join('')
+ it('should call ScrollBarHelper to handle scrollBar on body', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+
+ const spyHide = spyOn(ScrollBarHelper.prototype, 'hide').and.callThrough()
+ const spyReset = spyOn(ScrollBarHelper.prototype, 'reset').and.callThrough()
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl)
+
+ modalEl.addEventListener('shown.bs.modal', () => {
+ expect(spyHide).toHaveBeenCalled()
+ modal.toggle()
+ })
- spyOn(ScrollBarHelper.prototype, 'hide').and.callThrough()
- spyOn(ScrollBarHelper.prototype, 'reset').and.callThrough()
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl)
+ modalEl.addEventListener('hidden.bs.modal', () => {
+ expect(spyReset).toHaveBeenCalled()
+ resolve()
+ })
- modalEl.addEventListener('shown.bs.modal', () => {
- expect(ScrollBarHelper.prototype.hide).toHaveBeenCalled()
modal.toggle()
})
-
- modalEl.addEventListener('hidden.bs.modal', () => {
- expect(ScrollBarHelper.prototype.reset).toHaveBeenCalled()
- done()
- })
-
- modal.toggle()
})
})
describe('show', () => {
- it('should show a modal', done => {
- fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+ it('should show a modal', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl)
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl)
- modalEl.addEventListener('show.bs.modal', event => {
- expect(event).toBeDefined()
- })
+ modalEl.addEventListener('show.bs.modal', event => {
+ expect(event).toBeDefined()
+ })
- modalEl.addEventListener('shown.bs.modal', () => {
- expect(modalEl.getAttribute('aria-modal')).toEqual('true')
- expect(modalEl.getAttribute('role')).toEqual('dialog')
- expect(modalEl.getAttribute('aria-hidden')).toBeNull()
- expect(modalEl.style.display).toEqual('block')
- expect(document.querySelector('.modal-backdrop')).not.toBeNull()
- done()
- })
+ modalEl.addEventListener('shown.bs.modal', () => {
+ expect(modalEl.getAttribute('aria-modal')).toEqual('true')
+ expect(modalEl.getAttribute('role')).toEqual('dialog')
+ expect(modalEl.getAttribute('aria-hidden')).toBeNull()
+ expect(modalEl.style.display).toEqual('block')
+ expect(document.querySelector('.modal-backdrop')).not.toBeNull()
+ resolve()
+ })
- modal.show()
+ modal.show()
+ })
})
- it('should show a modal without backdrop', done => {
- fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+ it('should show a modal without backdrop', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl, {
- backdrop: false
- })
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl, {
+ backdrop: false
+ })
- modalEl.addEventListener('show.bs.modal', event => {
- expect(event).toBeDefined()
- })
+ modalEl.addEventListener('show.bs.modal', event => {
+ expect(event).toBeDefined()
+ })
- modalEl.addEventListener('shown.bs.modal', () => {
- expect(modalEl.getAttribute('aria-modal')).toEqual('true')
- expect(modalEl.getAttribute('role')).toEqual('dialog')
- expect(modalEl.getAttribute('aria-hidden')).toBeNull()
- expect(modalEl.style.display).toEqual('block')
- expect(document.querySelector('.modal-backdrop')).toBeNull()
- done()
- })
+ modalEl.addEventListener('shown.bs.modal', () => {
+ expect(modalEl.getAttribute('aria-modal')).toEqual('true')
+ expect(modalEl.getAttribute('role')).toEqual('dialog')
+ expect(modalEl.getAttribute('aria-hidden')).toBeNull()
+ expect(modalEl.style.display).toEqual('block')
+ expect(document.querySelector('.modal-backdrop')).toBeNull()
+ resolve()
+ })
- modal.show()
+ modal.show()
+ })
})
- it('should show a modal and append the element', done => {
- const modalEl = document.createElement('div')
- const id = 'dynamicModal'
+ it('should show a modal and append the element', () => {
+ return new Promise(resolve => {
+ const modalEl = document.createElement('div')
+ const id = 'dynamicModal'
- modalEl.setAttribute('id', id)
- modalEl.classList.add('modal')
- modalEl.innerHTML = '<div class="modal-dialog"></div>'
+ modalEl.setAttribute('id', id)
+ modalEl.classList.add('modal')
+ modalEl.innerHTML = '<div class="modal-dialog"></div>'
- const modal = new Modal(modalEl)
+ const modal = new Modal(modalEl)
- modalEl.addEventListener('shown.bs.modal', () => {
- const dynamicModal = document.getElementById(id)
- expect(dynamicModal).not.toBeNull()
- dynamicModal.remove()
- done()
- })
+ modalEl.addEventListener('shown.bs.modal', () => {
+ const dynamicModal = document.getElementById(id)
+ expect(dynamicModal).not.toBeNull()
+ dynamicModal.remove()
+ resolve()
+ })
- modal.show()
+ modal.show()
+ })
})
it('should do nothing if a modal is shown', () => {
@@ -153,12 +161,12 @@ describe('Modal', () => {
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
- spyOn(EventHandler, 'trigger')
+ const spy = spyOn(EventHandler, 'trigger')
modal._isShown = true
modal.show()
- expect(EventHandler.trigger).not.toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
})
it('should do nothing if a modal is transitioning', () => {
@@ -167,521 +175,595 @@ describe('Modal', () => {
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
- spyOn(EventHandler, 'trigger')
+ const spy = spyOn(EventHandler, 'trigger')
modal._isTransitioning = true
modal.show()
- expect(EventHandler.trigger).not.toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
})
- it('should not fire shown event when show is prevented', done => {
- fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+ it('should not fire shown event when show is prevented', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl)
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl)
- modalEl.addEventListener('show.bs.modal', event => {
- event.preventDefault()
+ modalEl.addEventListener('show.bs.modal', event => {
+ event.preventDefault()
- const expectedDone = () => {
- expect().nothing()
- done()
- }
+ const expectedDone = () => {
+ expect().nothing()
+ resolve()
+ }
- setTimeout(expectedDone, 10)
- })
+ setTimeout(expectedDone, 10)
+ })
- modalEl.addEventListener('shown.bs.modal', () => {
- throw new Error('shown event triggered')
- })
+ modalEl.addEventListener('shown.bs.modal', () => {
+ reject(new Error('shown event triggered'))
+ })
- modal.show()
+ modal.show()
+ })
})
- it('should be shown after the first call to show() has been prevented while fading is enabled ', done => {
- fixtureEl.innerHTML = '<div class="modal fade"><div class="modal-dialog"></div></div>'
+ it('should be shown after the first call to show() has been prevented while fading is enabled ', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="modal fade"><div class="modal-dialog"></div></div>'
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl)
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl)
- let prevented = false
- modalEl.addEventListener('show.bs.modal', event => {
- if (!prevented) {
- event.preventDefault()
- prevented = true
+ let prevented = false
+ modalEl.addEventListener('show.bs.modal', event => {
+ if (!prevented) {
+ event.preventDefault()
+ prevented = true
- setTimeout(() => {
- modal.show()
- })
- }
- })
+ setTimeout(() => {
+ modal.show()
+ })
+ }
+ })
- modalEl.addEventListener('shown.bs.modal', () => {
- expect(prevented).toBeTrue()
- expect(modal._isAnimated()).toBeTrue()
- done()
- })
+ modalEl.addEventListener('shown.bs.modal', () => {
+ expect(prevented).toBeTrue()
+ expect(modal._isAnimated()).toBeTrue()
+ resolve()
+ })
- modal.show()
+ modal.show()
+ })
})
+ it('should set is transitioning if fade class is present', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="modal fade"><div class="modal-dialog"></div></div>'
- it('should set is transitioning if fade class is present', done => {
- fixtureEl.innerHTML = '<div class="modal fade"><div class="modal-dialog"></div></div>'
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl)
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl)
+ modalEl.addEventListener('show.bs.modal', () => {
+ setTimeout(() => {
+ expect(modal._isTransitioning).toBeTrue()
+ })
+ })
- modalEl.addEventListener('show.bs.modal', () => {
- setTimeout(() => {
- expect(modal._isTransitioning).toEqual(true)
+ modalEl.addEventListener('shown.bs.modal', () => {
+ expect(modal._isTransitioning).toBeFalse()
+ resolve()
})
- })
- modalEl.addEventListener('shown.bs.modal', () => {
- expect(modal._isTransitioning).toEqual(false)
- done()
+ modal.show()
})
-
- modal.show()
})
- it('should close modal when a click occurred on data-bs-dismiss="modal" inside modal', done => {
- fixtureEl.innerHTML = [
- '<div class="modal fade">',
- ' <div class="modal-dialog">',
- ' <div class="modal-header">',
- ' <button type="button" data-bs-dismiss="modal"></button>',
- ' </div>',
- ' </div>',
- '</div>'
- ].join('')
-
- const modalEl = fixtureEl.querySelector('.modal')
- const btnClose = fixtureEl.querySelector('[data-bs-dismiss="modal"]')
- const modal = new Modal(modalEl)
-
- spyOn(modal, 'hide').and.callThrough()
+ it('should close modal when a click occurred on data-bs-dismiss="modal" inside modal', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="modal fade">',
+ ' <div class="modal-dialog">',
+ ' <div class="modal-header">',
+ ' <button type="button" data-bs-dismiss="modal"></button>',
+ ' </div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const modalEl = fixtureEl.querySelector('.modal')
+ const btnClose = fixtureEl.querySelector('[data-bs-dismiss="modal"]')
+ const modal = new Modal(modalEl)
+
+ const spy = spyOn(modal, 'hide').and.callThrough()
+
+ modalEl.addEventListener('shown.bs.modal', () => {
+ btnClose.click()
+ })
- modalEl.addEventListener('shown.bs.modal', () => {
- btnClose.click()
- })
+ modalEl.addEventListener('hidden.bs.modal', () => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- modalEl.addEventListener('hidden.bs.modal', () => {
- expect(modal.hide).toHaveBeenCalled()
- done()
+ modal.show()
})
-
- modal.show()
})
- it('should close modal when a click occurred on a data-bs-dismiss="modal" with "bs-target" outside of modal element', done => {
- fixtureEl.innerHTML = [
- '<button type="button" data-bs-dismiss="modal" data-bs-target="#modal1"></button>',
- '<div id="modal1" class="modal fade">',
- ' <div class="modal-dialog">',
- ' </div>',
- '</div>'
- ].join('')
+ it('should close modal when a click occurred on a data-bs-dismiss="modal" with "bs-target" outside of modal element', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<button type="button" data-bs-dismiss="modal" data-bs-target="#modal1"></button>',
+ '<div id="modal1" class="modal fade">',
+ ' <div class="modal-dialog"></div>',
+ '</div>'
+ ].join('')
- const modalEl = fixtureEl.querySelector('.modal')
- const btnClose = fixtureEl.querySelector('[data-bs-dismiss="modal"]')
- const modal = new Modal(modalEl)
+ const modalEl = fixtureEl.querySelector('.modal')
+ const btnClose = fixtureEl.querySelector('[data-bs-dismiss="modal"]')
+ const modal = new Modal(modalEl)
- spyOn(modal, 'hide').and.callThrough()
+ const spy = spyOn(modal, 'hide').and.callThrough()
- modalEl.addEventListener('shown.bs.modal', () => {
- btnClose.click()
- })
+ modalEl.addEventListener('shown.bs.modal', () => {
+ btnClose.click()
+ })
- modalEl.addEventListener('hidden.bs.modal', () => {
- expect(modal.hide).toHaveBeenCalled()
- done()
- })
+ modalEl.addEventListener('hidden.bs.modal', () => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- modal.show()
+ modal.show()
+ })
})
- it('should set .modal\'s scroll top to 0', done => {
- fixtureEl.innerHTML = [
- '<div class="modal fade">',
- ' <div class="modal-dialog">',
- ' </div>',
- '</div>'
- ].join('')
+ it('should set .modal\'s scroll top to 0', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="modal fade">',
+ ' <div class="modal-dialog"></div>',
+ '</div>'
+ ].join('')
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl)
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl)
- modalEl.addEventListener('shown.bs.modal', () => {
- expect(modalEl.scrollTop).toEqual(0)
- done()
- })
+ modalEl.addEventListener('shown.bs.modal', () => {
+ expect(modalEl.scrollTop).toEqual(0)
+ resolve()
+ })
- modal.show()
+ modal.show()
+ })
})
- it('should set modal body scroll top to 0 if modal body do not exists', done => {
- fixtureEl.innerHTML = [
- '<div class="modal fade">',
- ' <div class="modal-dialog">',
- ' <div class="modal-body"></div>',
- ' </div>',
- '</div>'
- ].join('')
-
- const modalEl = fixtureEl.querySelector('.modal')
- const modalBody = modalEl.querySelector('.modal-body')
- const modal = new Modal(modalEl)
+ it('should set modal body scroll top to 0 if modal body do not exists', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="modal fade">',
+ ' <div class="modal-dialog">',
+ ' <div class="modal-body"></div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modalBody = modalEl.querySelector('.modal-body')
+ const modal = new Modal(modalEl)
+
+ modalEl.addEventListener('shown.bs.modal', () => {
+ expect(modalBody.scrollTop).toEqual(0)
+ resolve()
+ })
- modalEl.addEventListener('shown.bs.modal', () => {
- expect(modalBody.scrollTop).toEqual(0)
- done()
+ modal.show()
})
-
- modal.show()
})
- it('should not trap focus if focus equal to false', done => {
- fixtureEl.innerHTML = '<div class="modal fade"><div class="modal-dialog"></div></div>'
+ it('should not trap focus if focus equal to false', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="modal fade"><div class="modal-dialog"></div></div>'
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl, {
- focus: false
- })
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl, {
+ focus: false
+ })
- spyOn(modal._focustrap, 'activate').and.callThrough()
+ const spy = spyOn(modal._focustrap, 'activate').and.callThrough()
- modalEl.addEventListener('shown.bs.modal', () => {
- expect(modal._focustrap.activate).not.toHaveBeenCalled()
- done()
- })
+ modalEl.addEventListener('shown.bs.modal', () => {
+ expect(spy).not.toHaveBeenCalled()
+ resolve()
+ })
- modal.show()
+ modal.show()
+ })
})
- it('should add listener when escape touch is pressed', done => {
- fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+ it('should add listener when escape touch is pressed', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl)
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl)
- spyOn(modal, 'hide').and.callThrough()
+ const spy = spyOn(modal, 'hide').and.callThrough()
- modalEl.addEventListener('shown.bs.modal', () => {
- const keydownEscape = createEvent('keydown')
- keydownEscape.key = 'Escape'
+ modalEl.addEventListener('shown.bs.modal', () => {
+ const keydownEscape = createEvent('keydown')
+ keydownEscape.key = 'Escape'
- modalEl.dispatchEvent(keydownEscape)
- })
+ modalEl.dispatchEvent(keydownEscape)
+ })
- modalEl.addEventListener('hidden.bs.modal', () => {
- expect(modal.hide).toHaveBeenCalled()
- done()
- })
+ modalEl.addEventListener('hidden.bs.modal', () => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- modal.show()
+ modal.show()
+ })
})
- it('should do nothing when the pressed key is not escape', done => {
- fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+ it('should do nothing when the pressed key is not escape', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl)
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl)
- spyOn(modal, 'hide')
+ const spy = spyOn(modal, 'hide')
- const expectDone = () => {
- expect(modal.hide).not.toHaveBeenCalled()
+ const expectDone = () => {
+ expect(spy).not.toHaveBeenCalled()
- done()
- }
+ resolve()
+ }
- modalEl.addEventListener('shown.bs.modal', () => {
- const keydownTab = createEvent('keydown')
- keydownTab.key = 'Tab'
+ modalEl.addEventListener('shown.bs.modal', () => {
+ const keydownTab = createEvent('keydown')
+ keydownTab.key = 'Tab'
- modalEl.dispatchEvent(keydownTab)
- setTimeout(expectDone, 30)
- })
+ modalEl.dispatchEvent(keydownTab)
+ setTimeout(expectDone, 30)
+ })
- modal.show()
+ modal.show()
+ })
})
- it('should adjust dialog on resize', done => {
- fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+ it('should adjust dialog on resize', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl)
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl)
- spyOn(modal, '_adjustDialog').and.callThrough()
+ const spy = spyOn(modal, '_adjustDialog').and.callThrough()
- const expectDone = () => {
- expect(modal._adjustDialog).toHaveBeenCalled()
+ const expectDone = () => {
+ expect(spy).toHaveBeenCalled()
- done()
- }
+ resolve()
+ }
- modalEl.addEventListener('shown.bs.modal', () => {
- const resizeEvent = createEvent('resize')
+ modalEl.addEventListener('shown.bs.modal', () => {
+ const resizeEvent = createEvent('resize')
- window.dispatchEvent(resizeEvent)
- setTimeout(expectDone, 10)
- })
+ window.dispatchEvent(resizeEvent)
+ setTimeout(expectDone, 10)
+ })
- modal.show()
+ modal.show()
+ })
})
- it('should not close modal when clicking on modal-content', done => {
- fixtureEl.innerHTML = [
- '<div class="modal">',
- ' <div class="modal-dialog">',
- ' <div class="modal-content"></div>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should not close modal when clicking on modal-content', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<div class="modal">',
+ ' <div class="modal-dialog">',
+ ' <div class="modal-content"></div>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl)
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl)
- const shownCallback = () => {
- setTimeout(() => {
- expect(modal._isShown).toEqual(true)
- done()
- }, 10)
- }
-
- modalEl.addEventListener('shown.bs.modal', () => {
- fixtureEl.querySelector('.modal-dialog').click()
- fixtureEl.querySelector('.modal-content').click()
- shownCallback()
- })
-
- modalEl.addEventListener('hidden.bs.modal', () => {
- throw new Error('Should not hide a modal')
- })
+ const shownCallback = () => {
+ setTimeout(() => {
+ expect(modal._isShown).toEqual(true)
+ resolve()
+ }, 10)
+ }
- modal.show()
- })
+ modalEl.addEventListener('shown.bs.modal', () => {
+ fixtureEl.querySelector('.modal-dialog').click()
+ fixtureEl.querySelector('.modal-content').click()
+ shownCallback()
+ })
- it('should not close modal when clicking outside of modal-content if backdrop = false', done => {
- fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+ modalEl.addEventListener('hidden.bs.modal', () => {
+ reject(new Error('Should not hide a modal'))
+ })
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl, {
- backdrop: false
+ modal.show()
})
+ })
- const shownCallback = () => {
- setTimeout(() => {
- expect(modal._isShown).toEqual(true)
- done()
- }, 10)
- }
+ it('should not close modal when clicking outside of modal-content if backdrop = false', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
- modalEl.addEventListener('shown.bs.modal', () => {
- modalEl.click()
- shownCallback()
- })
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl, {
+ backdrop: false
+ })
- modalEl.addEventListener('hidden.bs.modal', () => {
- throw new Error('Should not hide a modal')
- })
+ const shownCallback = () => {
+ setTimeout(() => {
+ expect(modal._isShown).toBeTrue()
+ resolve()
+ }, 10)
+ }
- modal.show()
- })
+ modalEl.addEventListener('shown.bs.modal', () => {
+ modalEl.click()
+ shownCallback()
+ })
- it('should not close modal when clicking outside of modal-content if backdrop = static', done => {
- fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+ modalEl.addEventListener('hidden.bs.modal', () => {
+ reject(new Error('Should not hide a modal'))
+ })
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl, {
- backdrop: 'static'
+ modal.show()
})
+ })
- const shownCallback = () => {
- setTimeout(() => {
- expect(modal._isShown).toEqual(true)
- done()
- }, 10)
- }
+ it('should not close modal when clicking outside of modal-content if backdrop = static', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
- modalEl.addEventListener('shown.bs.modal', () => {
- modalEl.click()
- shownCallback()
- })
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl, {
+ backdrop: 'static'
+ })
- modalEl.addEventListener('hidden.bs.modal', () => {
- throw new Error('Should not hide a modal')
- })
+ const shownCallback = () => {
+ setTimeout(() => {
+ expect(modal._isShown).toBeTrue()
+ resolve()
+ }, 10)
+ }
- modal.show()
- })
+ modalEl.addEventListener('shown.bs.modal', () => {
+ modalEl.click()
+ shownCallback()
+ })
- it('should close modal when escape key is pressed with keyboard = true and backdrop is static', done => {
- fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+ modalEl.addEventListener('hidden.bs.modal', () => {
+ reject(new Error('Should not hide a modal'))
+ })
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl, {
- backdrop: 'static',
- keyboard: true
+ modal.show()
})
+ })
+ it('should close modal when escape key is pressed with keyboard = true and backdrop is static', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl, {
+ backdrop: 'static',
+ keyboard: true
+ })
- const shownCallback = () => {
- setTimeout(() => {
- expect(modal._isShown).toEqual(false)
- done()
- }, 10)
- }
+ const shownCallback = () => {
+ setTimeout(() => {
+ expect(modal._isShown).toBeFalse()
+ resolve()
+ }, 10)
+ }
- modalEl.addEventListener('shown.bs.modal', () => {
- const keydownEscape = createEvent('keydown')
- keydownEscape.key = 'Escape'
+ modalEl.addEventListener('shown.bs.modal', () => {
+ const keydownEscape = createEvent('keydown')
+ keydownEscape.key = 'Escape'
- modalEl.dispatchEvent(keydownEscape)
- shownCallback()
- })
+ modalEl.dispatchEvent(keydownEscape)
+ shownCallback()
+ })
- modal.show()
+ modal.show()
+ })
})
- it('should not close modal when escape key is pressed with keyboard = false', done => {
- fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+ it('should not close modal when escape key is pressed with keyboard = false', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl, {
- keyboard: false
- })
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl, {
+ keyboard: false
+ })
- const shownCallback = () => {
- setTimeout(() => {
- expect(modal._isShown).toEqual(true)
- done()
- }, 10)
- }
+ const shownCallback = () => {
+ setTimeout(() => {
+ expect(modal._isShown).toBeTrue()
+ resolve()
+ }, 10)
+ }
- modalEl.addEventListener('shown.bs.modal', () => {
- const keydownEscape = createEvent('keydown')
- keydownEscape.key = 'Escape'
+ modalEl.addEventListener('shown.bs.modal', () => {
+ const keydownEscape = createEvent('keydown')
+ keydownEscape.key = 'Escape'
- modalEl.dispatchEvent(keydownEscape)
- shownCallback()
- })
+ modalEl.dispatchEvent(keydownEscape)
+ shownCallback()
+ })
- modalEl.addEventListener('hidden.bs.modal', () => {
- throw new Error('Should not hide a modal')
- })
+ modalEl.addEventListener('hidden.bs.modal', () => {
+ reject(new Error('Should not hide a modal'))
+ })
- modal.show()
+ modal.show()
+ })
})
- it('should not overflow when clicking outside of modal-content if backdrop = static', done => {
- fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" style="transition-duration: 20ms;"></div></div>'
+ it('should not overflow when clicking outside of modal-content if backdrop = static', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" style="transition-duration: 20ms;"></div></div>'
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl, {
- backdrop: 'static'
- })
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl, {
+ backdrop: 'static'
+ })
- modalEl.addEventListener('shown.bs.modal', () => {
- modalEl.click()
- setTimeout(() => {
- expect(modalEl.clientHeight).toEqual(modalEl.scrollHeight)
- done()
- }, 20)
- })
+ modalEl.addEventListener('shown.bs.modal', () => {
+ modalEl.click()
+ setTimeout(() => {
+ expect(modalEl.clientHeight).toEqual(modalEl.scrollHeight)
+ resolve()
+ }, 20)
+ })
- modal.show()
+ modal.show()
+ })
})
- it('should not queue multiple callbacks when clicking outside of modal-content and backdrop = static', done => {
- fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" style="transition-duration: 50ms;"></div></div>'
+ it('should not queue multiple callbacks when clicking outside of modal-content and backdrop = static', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" style="transition-duration: 50ms;"></div></div>'
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl, {
- backdrop: 'static'
- })
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl, {
+ backdrop: 'static'
+ })
- modalEl.addEventListener('shown.bs.modal', () => {
- const spy = spyOn(modal, '_queueCallback').and.callThrough()
+ modalEl.addEventListener('shown.bs.modal', () => {
+ const spy = spyOn(modal, '_queueCallback').and.callThrough()
+ const mouseDown = createEvent('mousedown')
- modalEl.click()
- modalEl.click()
+ modalEl.dispatchEvent(mouseDown)
+ modalEl.click()
+ modalEl.dispatchEvent(mouseDown)
+ modalEl.click()
- setTimeout(() => {
- expect(spy).toHaveBeenCalledTimes(1)
- done()
- }, 20)
- })
+ setTimeout(() => {
+ expect(spy).toHaveBeenCalledTimes(1)
+ resolve()
+ }, 20)
+ })
- modal.show()
+ modal.show()
+ })
})
- it('should trap focus', done => {
- fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+ it('should trap focus', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl)
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl)
- spyOn(modal._focustrap, 'activate').and.callThrough()
+ const spy = spyOn(modal._focustrap, 'activate').and.callThrough()
- modalEl.addEventListener('shown.bs.modal', () => {
- expect(modal._focustrap.activate).toHaveBeenCalled()
- done()
- })
+ modalEl.addEventListener('shown.bs.modal', () => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- modal.show()
+ modal.show()
+ })
})
})
describe('hide', () => {
- it('should hide a modal', done => {
- fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+ it('should hide a modal', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl)
- const backdropSpy = spyOn(modal._backdrop, 'hide').and.callThrough()
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl)
+ const backdropSpy = spyOn(modal._backdrop, 'hide').and.callThrough()
- modalEl.addEventListener('shown.bs.modal', () => {
- modal.hide()
- })
+ modalEl.addEventListener('shown.bs.modal', () => {
+ modal.hide()
+ })
- modalEl.addEventListener('hide.bs.modal', event => {
- expect(event).toBeDefined()
- })
+ modalEl.addEventListener('hide.bs.modal', event => {
+ expect(event).toBeDefined()
+ })
- modalEl.addEventListener('hidden.bs.modal', () => {
- expect(modalEl.getAttribute('aria-modal')).toBeNull()
- expect(modalEl.getAttribute('role')).toBeNull()
- expect(modalEl.getAttribute('aria-hidden')).toEqual('true')
- expect(modalEl.style.display).toEqual('none')
- expect(backdropSpy).toHaveBeenCalled()
- done()
- })
+ modalEl.addEventListener('hidden.bs.modal', () => {
+ expect(modalEl.getAttribute('aria-modal')).toBeNull()
+ expect(modalEl.getAttribute('role')).toBeNull()
+ expect(modalEl.getAttribute('aria-hidden')).toEqual('true')
+ expect(modalEl.style.display).toEqual('none')
+ expect(backdropSpy).toHaveBeenCalled()
+ resolve()
+ })
- modal.show()
+ modal.show()
+ })
})
- it('should close modal when clicking outside of modal-content', done => {
- fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+ it('should close modal when clicking outside of modal-content', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl)
+ const modalEl = fixtureEl.querySelector('.modal')
+ const dialogEl = modalEl.querySelector('.modal-dialog')
+ const modal = new Modal(modalEl)
- modalEl.addEventListener('shown.bs.modal', () => {
- modalEl.click()
- })
+ const spy = spyOn(modal, 'hide')
+
+ modalEl.addEventListener('shown.bs.modal', () => {
+ const mouseDown = createEvent('mousedown')
+
+ dialogEl.dispatchEvent(mouseDown)
+ modalEl.click()
+ expect(spy).not.toHaveBeenCalled()
+
+ modalEl.dispatchEvent(mouseDown)
+ modalEl.click()
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- modalEl.addEventListener('hidden.bs.modal', () => {
- expect(modalEl.getAttribute('aria-modal')).toBeNull()
- expect(modalEl.getAttribute('role')).toBeNull()
- expect(modalEl.getAttribute('aria-hidden')).toEqual('true')
- expect(modalEl.style.display).toEqual('none')
- expect(document.querySelector('.modal-backdrop')).toBeNull()
- done()
+ modal.show()
})
+ })
- modal.show()
+ it('should not close modal when clicking on an element removed from modal content', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="modal">',
+ ' <div class="modal-dialog">',
+ ' <button class="btn">BTN</button>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const modalEl = fixtureEl.querySelector('.modal')
+ const buttonEl = modalEl.querySelector('.btn')
+ const modal = new Modal(modalEl)
+
+ const spy = spyOn(modal, 'hide')
+ buttonEl.addEventListener('click', () => {
+ buttonEl.remove()
+ })
+
+ modalEl.addEventListener('shown.bs.modal', () => {
+ modalEl.dispatchEvent(createEvent('mousedown'))
+ buttonEl.click()
+ expect(spy).not.toHaveBeenCalled()
+ resolve()
+ })
+
+ modal.show()
+ })
})
it('should do nothing is the modal is not shown', () => {
@@ -707,52 +789,56 @@ describe('Modal', () => {
expect().nothing()
})
- it('should not hide a modal if hide is prevented', done => {
- fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+ it('should not hide a modal if hide is prevented', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl)
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl)
- modalEl.addEventListener('shown.bs.modal', () => {
- modal.hide()
- })
+ modalEl.addEventListener('shown.bs.modal', () => {
+ modal.hide()
+ })
- const hideCallback = () => {
- setTimeout(() => {
- expect(modal._isShown).toEqual(true)
- done()
- }, 10)
- }
+ const hideCallback = () => {
+ setTimeout(() => {
+ expect(modal._isShown).toBeTrue()
+ resolve()
+ }, 10)
+ }
- modalEl.addEventListener('hide.bs.modal', event => {
- event.preventDefault()
- hideCallback()
- })
+ modalEl.addEventListener('hide.bs.modal', event => {
+ event.preventDefault()
+ hideCallback()
+ })
- modalEl.addEventListener('hidden.bs.modal', () => {
- throw new Error('should not trigger hidden')
- })
+ modalEl.addEventListener('hidden.bs.modal', () => {
+ reject(new Error('should not trigger hidden'))
+ })
- modal.show()
+ modal.show()
+ })
})
- it('should release focus trap', done => {
- fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
+ it('should release focus trap', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl)
- spyOn(modal._focustrap, 'deactivate').and.callThrough()
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl)
+ const spy = spyOn(modal._focustrap, 'deactivate').and.callThrough()
- modalEl.addEventListener('shown.bs.modal', () => {
- modal.hide()
- })
+ modalEl.addEventListener('shown.bs.modal', () => {
+ modal.hide()
+ })
- modalEl.addEventListener('hidden.bs.modal', () => {
- expect(modal._focustrap.deactivate).toHaveBeenCalled()
- done()
- })
+ modalEl.addEventListener('hidden.bs.modal', () => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- modal.show()
+ modal.show()
+ })
})
})
@@ -763,17 +849,17 @@ describe('Modal', () => {
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
const focustrap = modal._focustrap
- spyOn(focustrap, 'deactivate').and.callThrough()
+ const spyDeactivate = spyOn(focustrap, 'deactivate').and.callThrough()
expect(Modal.getInstance(modalEl)).toEqual(modal)
- spyOn(EventHandler, 'off')
+ const spyOff = spyOn(EventHandler, 'off')
modal.dispose()
expect(Modal.getInstance(modalEl)).toBeNull()
- expect(EventHandler.off).toHaveBeenCalledTimes(3)
- expect(focustrap.deactivate).toHaveBeenCalled()
+ expect(spyOff).toHaveBeenCalledTimes(3)
+ expect(spyDeactivate).toHaveBeenCalled()
})
})
@@ -784,255 +870,298 @@ describe('Modal', () => {
const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl)
- spyOn(modal, '_adjustDialog')
+ const spy = spyOn(modal, '_adjustDialog')
modal.handleUpdate()
- expect(modal._adjustDialog).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
})
})
describe('data-api', () => {
- it('should toggle modal', done => {
- fixtureEl.innerHTML = [
- '<button type="button" data-bs-toggle="modal" data-bs-target="#exampleModal"></button>',
- '<div id="exampleModal" class="modal"><div class="modal-dialog"></div></div>'
- ].join('')
+ it('should toggle modal', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<button type="button" data-bs-toggle="modal" data-bs-target="#exampleModal"></button>',
+ '<div id="exampleModal" class="modal"><div class="modal-dialog"></div></div>'
+ ].join('')
+
+ const modalEl = fixtureEl.querySelector('.modal')
+ const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
+
+ modalEl.addEventListener('shown.bs.modal', () => {
+ expect(modalEl.getAttribute('aria-modal')).toEqual('true')
+ expect(modalEl.getAttribute('role')).toEqual('dialog')
+ expect(modalEl.getAttribute('aria-hidden')).toBeNull()
+ expect(modalEl.style.display).toEqual('block')
+ expect(document.querySelector('.modal-backdrop')).not.toBeNull()
+ setTimeout(() => trigger.click(), 10)
+ })
- const modalEl = fixtureEl.querySelector('.modal')
- const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
-
- modalEl.addEventListener('shown.bs.modal', () => {
- expect(modalEl.getAttribute('aria-modal')).toEqual('true')
- expect(modalEl.getAttribute('role')).toEqual('dialog')
- expect(modalEl.getAttribute('aria-hidden')).toBeNull()
- expect(modalEl.style.display).toEqual('block')
- expect(document.querySelector('.modal-backdrop')).not.toBeNull()
- setTimeout(() => trigger.click(), 10)
- })
+ modalEl.addEventListener('hidden.bs.modal', () => {
+ expect(modalEl.getAttribute('aria-modal')).toBeNull()
+ expect(modalEl.getAttribute('role')).toBeNull()
+ expect(modalEl.getAttribute('aria-hidden')).toEqual('true')
+ expect(modalEl.style.display).toEqual('none')
+ expect(document.querySelector('.modal-backdrop')).toBeNull()
+ resolve()
+ })
- modalEl.addEventListener('hidden.bs.modal', () => {
- expect(modalEl.getAttribute('aria-modal')).toBeNull()
- expect(modalEl.getAttribute('role')).toBeNull()
- expect(modalEl.getAttribute('aria-hidden')).toEqual('true')
- expect(modalEl.style.display).toEqual('none')
- expect(document.querySelector('.modal-backdrop')).toBeNull()
- done()
+ trigger.click()
})
-
- trigger.click()
})
- it('should not recreate a new modal', done => {
- fixtureEl.innerHTML = [
- '<button type="button" data-bs-toggle="modal" data-bs-target="#exampleModal"></button>',
- '<div id="exampleModal" class="modal"><div class="modal-dialog"></div></div>'
- ].join('')
+ it('should not recreate a new modal', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<button type="button" data-bs-toggle="modal" data-bs-target="#exampleModal"></button>',
+ '<div id="exampleModal" class="modal"><div class="modal-dialog"></div></div>'
+ ].join('')
- const modalEl = fixtureEl.querySelector('.modal')
- const modal = new Modal(modalEl)
- const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
+ const modalEl = fixtureEl.querySelector('.modal')
+ const modal = new Modal(modalEl)
+ const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
- spyOn(modal, 'show').and.callThrough()
+ const spy = spyOn(modal, 'show').and.callThrough()
- modalEl.addEventListener('shown.bs.modal', () => {
- expect(modal.show).toHaveBeenCalled()
- done()
- })
+ modalEl.addEventListener('shown.bs.modal', () => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- trigger.click()
+ trigger.click()
+ })
})
- it('should prevent default when the trigger is <a> or <area>', done => {
- fixtureEl.innerHTML = [
- '<a data-bs-toggle="modal" href="#" data-bs-target="#exampleModal"></a>',
- '<div id="exampleModal" class="modal"><div class="modal-dialog"></div></div>'
- ].join('')
+ it('should prevent default when the trigger is <a> or <area>', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<a data-bs-toggle="modal" href="#" data-bs-target="#exampleModal"></a>',
+ '<div id="exampleModal" class="modal"><div class="modal-dialog"></div></div>'
+ ].join('')
+
+ const modalEl = fixtureEl.querySelector('.modal')
+ const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
+
+ const spy = spyOn(Event.prototype, 'preventDefault').and.callThrough()
+
+ modalEl.addEventListener('shown.bs.modal', () => {
+ expect(modalEl.getAttribute('aria-modal')).toEqual('true')
+ expect(modalEl.getAttribute('role')).toEqual('dialog')
+ expect(modalEl.getAttribute('aria-hidden')).toBeNull()
+ expect(modalEl.style.display).toEqual('block')
+ expect(document.querySelector('.modal-backdrop')).not.toBeNull()
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- const modalEl = fixtureEl.querySelector('.modal')
- const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
-
- spyOn(Event.prototype, 'preventDefault').and.callThrough()
-
- modalEl.addEventListener('shown.bs.modal', () => {
- expect(modalEl.getAttribute('aria-modal')).toEqual('true')
- expect(modalEl.getAttribute('role')).toEqual('dialog')
- expect(modalEl.getAttribute('aria-hidden')).toBeNull()
- expect(modalEl.style.display).toEqual('block')
- expect(document.querySelector('.modal-backdrop')).not.toBeNull()
- expect(Event.prototype.preventDefault).toHaveBeenCalled()
- done()
+ trigger.click()
})
-
- trigger.click()
})
- it('should focus the trigger on hide', done => {
- fixtureEl.innerHTML = [
- '<a data-bs-toggle="modal" href="#" data-bs-target="#exampleModal"></a>',
- '<div id="exampleModal" class="modal"><div class="modal-dialog"></div></div>'
- ].join('')
+ it('should focus the trigger on hide', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<a data-bs-toggle="modal" href="#" data-bs-target="#exampleModal"></a>',
+ '<div id="exampleModal" class="modal"><div class="modal-dialog"></div></div>'
+ ].join('')
- const modalEl = fixtureEl.querySelector('.modal')
- const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
+ const modalEl = fixtureEl.querySelector('.modal')
+ const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
- spyOn(trigger, 'focus')
+ const spy = spyOn(trigger, 'focus')
- modalEl.addEventListener('shown.bs.modal', () => {
- const modal = Modal.getInstance(modalEl)
+ modalEl.addEventListener('shown.bs.modal', () => {
+ const modal = Modal.getInstance(modalEl)
- modal.hide()
- })
+ modal.hide()
+ })
- const hideListener = () => {
- setTimeout(() => {
- expect(trigger.focus).toHaveBeenCalled()
- done()
- }, 20)
- }
+ const hideListener = () => {
+ setTimeout(() => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ }, 20)
+ }
- modalEl.addEventListener('hidden.bs.modal', () => {
- hideListener()
+ modalEl.addEventListener('hidden.bs.modal', () => {
+ hideListener()
+ })
+
+ trigger.click()
})
+ })
+
+ it('should open modal, having special characters in its id', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<button class="btn btn-primary" data-bs-toggle="modal" data-bs-target="#j_id22:exampleModal">',
+ ' Launch demo modal',
+ '</button>',
+ '<div class="modal fade" id="j_id22:exampleModal" aria-labelledby="exampleModalLabel" aria-hidden="true">',
+ ' <div class="modal-dialog">',
+ ' <div class="modal-content">',
+ ' <div class="modal-body">',
+ ' <p>modal body</p>',
+ ' </div>',
+ ' </div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const modalEl = fixtureEl.querySelector('.modal')
+ const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
+
+ modalEl.addEventListener('shown.bs.modal', () => {
+ resolve()
+ })
- trigger.click()
+ trigger.click()
+ })
})
- it('should not prevent default when a click occurred on data-bs-dismiss="modal" where tagName is DIFFERENT than <a> or <area>', done => {
- fixtureEl.innerHTML = [
- '<div class="modal">',
- ' <div class="modal-dialog">',
- ' <button type="button" data-bs-dismiss="modal"></button>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should not prevent default when a click occurred on data-bs-dismiss="modal" where tagName is DIFFERENT than <a> or <area>', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="modal">',
+ ' <div class="modal-dialog">',
+ ' <button type="button" data-bs-dismiss="modal"></button>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const modalEl = fixtureEl.querySelector('.modal')
- const btnClose = fixtureEl.querySelector('button[data-bs-dismiss="modal"]')
- const modal = new Modal(modalEl)
+ const modalEl = fixtureEl.querySelector('.modal')
+ const btnClose = fixtureEl.querySelector('button[data-bs-dismiss="modal"]')
+ const modal = new Modal(modalEl)
- spyOn(Event.prototype, 'preventDefault').and.callThrough()
+ const spy = spyOn(Event.prototype, 'preventDefault').and.callThrough()
- modalEl.addEventListener('shown.bs.modal', () => {
- btnClose.click()
- })
+ modalEl.addEventListener('shown.bs.modal', () => {
+ btnClose.click()
+ })
- modalEl.addEventListener('hidden.bs.modal', () => {
- expect(Event.prototype.preventDefault).not.toHaveBeenCalled()
- done()
- })
+ modalEl.addEventListener('hidden.bs.modal', () => {
+ expect(spy).not.toHaveBeenCalled()
+ resolve()
+ })
- modal.show()
+ modal.show()
+ })
})
- it('should prevent default when a click occurred on data-bs-dismiss="modal" where tagName is <a> or <area>', done => {
- fixtureEl.innerHTML = [
- '<div class="modal">',
- ' <div class="modal-dialog">',
- ' <a type="button" data-bs-dismiss="modal"></a>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should prevent default when a click occurred on data-bs-dismiss="modal" where tagName is <a> or <area>', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="modal">',
+ ' <div class="modal-dialog">',
+ ' <a type="button" data-bs-dismiss="modal"></a>',
+ ' </div>',
+ '</div>'
+ ].join('')
- const modalEl = fixtureEl.querySelector('.modal')
- const btnClose = fixtureEl.querySelector('a[data-bs-dismiss="modal"]')
- const modal = new Modal(modalEl)
+ const modalEl = fixtureEl.querySelector('.modal')
+ const btnClose = fixtureEl.querySelector('a[data-bs-dismiss="modal"]')
+ const modal = new Modal(modalEl)
- spyOn(Event.prototype, 'preventDefault').and.callThrough()
+ const spy = spyOn(Event.prototype, 'preventDefault').and.callThrough()
- modalEl.addEventListener('shown.bs.modal', () => {
- btnClose.click()
- })
+ modalEl.addEventListener('shown.bs.modal', () => {
+ btnClose.click()
+ })
- modalEl.addEventListener('hidden.bs.modal', () => {
- expect(Event.prototype.preventDefault).toHaveBeenCalled()
- done()
- })
+ modalEl.addEventListener('hidden.bs.modal', () => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- modal.show()
+ modal.show()
+ })
})
+ it('should not focus the trigger if the modal is not visible', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<a data-bs-toggle="modal" href="#" data-bs-target="#exampleModal" style="display: none;"></a>',
+ '<div id="exampleModal" class="modal" style="display: none;"><div class="modal-dialog"></div></div>'
+ ].join('')
- it('should not focus the trigger if the modal is not visible', done => {
- fixtureEl.innerHTML = [
- '<a data-bs-toggle="modal" href="#" data-bs-target="#exampleModal" style="display: none;"></a>',
- '<div id="exampleModal" class="modal" style="display: none;"><div class="modal-dialog"></div></div>'
- ].join('')
+ const modalEl = fixtureEl.querySelector('.modal')
+ const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
- const modalEl = fixtureEl.querySelector('.modal')
- const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
+ const spy = spyOn(trigger, 'focus')
- spyOn(trigger, 'focus')
+ modalEl.addEventListener('shown.bs.modal', () => {
+ const modal = Modal.getInstance(modalEl)
- modalEl.addEventListener('shown.bs.modal', () => {
- const modal = Modal.getInstance(modalEl)
+ modal.hide()
+ })
- modal.hide()
- })
+ const hideListener = () => {
+ setTimeout(() => {
+ expect(spy).not.toHaveBeenCalled()
+ resolve()
+ }, 20)
+ }
- const hideListener = () => {
- setTimeout(() => {
- expect(trigger.focus).not.toHaveBeenCalled()
- done()
- }, 20)
- }
+ modalEl.addEventListener('hidden.bs.modal', () => {
+ hideListener()
+ })
- modalEl.addEventListener('hidden.bs.modal', () => {
- hideListener()
+ trigger.click()
})
-
- trigger.click()
})
+ it('should not focus the trigger if the modal is not shown', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<a data-bs-toggle="modal" href="#" data-bs-target="#exampleModal"></a>',
+ '<div id="exampleModal" class="modal"><div class="modal-dialog"></div></div>'
+ ].join('')
- it('should not focus the trigger if the modal is not shown', done => {
- fixtureEl.innerHTML = [
- '<a data-bs-toggle="modal" href="#" data-bs-target="#exampleModal"></a>',
- '<div id="exampleModal" class="modal"><div class="modal-dialog"></div></div>'
- ].join('')
+ const modalEl = fixtureEl.querySelector('.modal')
+ const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
- const modalEl = fixtureEl.querySelector('.modal')
- const trigger = fixtureEl.querySelector('[data-bs-toggle="modal"]')
+ const spy = spyOn(trigger, 'focus')
- spyOn(trigger, 'focus')
+ const showListener = () => {
+ setTimeout(() => {
+ expect(spy).not.toHaveBeenCalled()
+ resolve()
+ }, 10)
+ }
- const showListener = () => {
- setTimeout(() => {
- expect(trigger.focus).not.toHaveBeenCalled()
- done()
- }, 10)
- }
+ modalEl.addEventListener('show.bs.modal', event => {
+ event.preventDefault()
+ showListener()
+ })
- modalEl.addEventListener('show.bs.modal', event => {
- event.preventDefault()
- showListener()
+ trigger.click()
})
-
- trigger.click()
})
- it('should call hide first, if another modal is open', done => {
- fixtureEl.innerHTML = [
- '<button data-bs-toggle="modal" data-bs-target="#modal2"></button>',
- '<div id="modal1" class="modal fade"><div class="modal-dialog"></div></div>',
- '<div id="modal2" class="modal"><div class="modal-dialog"></div></div>'
- ].join('')
-
- const trigger2 = fixtureEl.querySelector('button')
- const modalEl1 = document.querySelector('#modal1')
- const modalEl2 = document.querySelector('#modal2')
- const modal1 = new Modal(modalEl1)
-
- modalEl1.addEventListener('shown.bs.modal', () => {
- trigger2.click()
- })
- modalEl1.addEventListener('hidden.bs.modal', () => {
- expect(Modal.getInstance(modalEl2)).not.toBeNull()
- expect(modalEl2.classList.contains('show')).toBeTrue()
- done()
+ it('should call hide first, if another modal is open', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<button data-bs-toggle="modal" data-bs-target="#modal2"></button>',
+ '<div id="modal1" class="modal fade"><div class="modal-dialog"></div></div>',
+ '<div id="modal2" class="modal"><div class="modal-dialog"></div></div>'
+ ].join('')
+
+ const trigger2 = fixtureEl.querySelector('button')
+ const modalEl1 = document.querySelector('#modal1')
+ const modalEl2 = document.querySelector('#modal2')
+ const modal1 = new Modal(modalEl1)
+
+ modalEl1.addEventListener('shown.bs.modal', () => {
+ trigger2.click()
+ })
+ modalEl1.addEventListener('hidden.bs.modal', () => {
+ expect(Modal.getInstance(modalEl2)).not.toBeNull()
+ expect(modalEl2).toHaveClass('show')
+ resolve()
+ })
+ modal1.show()
})
- modal1.show()
})
})
-
describe('jQueryInterface', () => {
it('should create a modal', () => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog"></div></div>'
@@ -1056,12 +1185,12 @@ describe('Modal', () => {
jQueryMock.elements = [div]
jQueryMock.fn.modal.call(jQueryMock, { keyboard: false })
- spyOn(Modal.prototype, 'constructor')
- expect(Modal.prototype.constructor).not.toHaveBeenCalledWith(div, { keyboard: false })
+ const spy = spyOn(Modal.prototype, 'constructor')
+ expect(spy).not.toHaveBeenCalledWith(div, { keyboard: false })
const modal = Modal.getInstance(div)
expect(modal).not.toBeNull()
- expect(modal._config.keyboard).toBe(false)
+ expect(modal._config.keyboard).toBeFalse()
})
it('should not re create a modal', () => {
@@ -1101,11 +1230,11 @@ describe('Modal', () => {
jQueryMock.fn.modal = Modal.jQueryInterface
jQueryMock.elements = [div]
- spyOn(modal, 'show')
+ const spy = spyOn(modal, 'show')
jQueryMock.fn.modal.call(jQueryMock, 'show')
- expect(modal.show).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
})
it('should not call show method', () => {
@@ -1116,11 +1245,11 @@ describe('Modal', () => {
jQueryMock.fn.modal = Modal.jQueryInterface
jQueryMock.elements = [div]
- spyOn(Modal.prototype, 'show')
+ const spy = spyOn(Modal.prototype, 'show')
jQueryMock.fn.modal.call(jQueryMock)
- expect(Modal.prototype.show).not.toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
})
})
@@ -1161,7 +1290,7 @@ describe('Modal', () => {
const div = fixtureEl.querySelector('div')
- expect(Modal.getInstance(div)).toEqual(null)
+ expect(Modal.getInstance(div)).toBeNull()
expect(Modal.getOrCreateInstance(div)).toBeInstanceOf(Modal)
})
@@ -1170,13 +1299,13 @@ describe('Modal', () => {
const div = fixtureEl.querySelector('div')
- expect(Modal.getInstance(div)).toEqual(null)
+ expect(Modal.getInstance(div)).toBeNull()
const modal = Modal.getOrCreateInstance(div, {
backdrop: true
})
expect(modal).toBeInstanceOf(Modal)
- expect(modal._config.backdrop).toEqual(true)
+ expect(modal._config.backdrop).toBeTrue()
})
it('should return the instance when exists without given configuration', () => {
@@ -1194,7 +1323,7 @@ describe('Modal', () => {
expect(modal).toBeInstanceOf(Modal)
expect(modal2).toEqual(modal)
- expect(modal2._config.backdrop).toEqual(true)
+ expect(modal2._config.backdrop).toBeTrue()
})
})
})
diff --git a/js/tests/unit/offcanvas.spec.js b/js/tests/unit/offcanvas.spec.js
index 3eda50520..3b6c98c10 100644
--- a/js/tests/unit/offcanvas.spec.js
+++ b/js/tests/unit/offcanvas.spec.js
@@ -1,8 +1,10 @@
-import Offcanvas from '../../src/offcanvas'
-import EventHandler from '../../src/dom/event-handler'
-import { clearBodyAndDocument, clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
-import { isVisible } from '../../src/util/index'
-import ScrollBarHelper from '../../src/util/scrollbar'
+import EventHandler from '../../src/dom/event-handler.js'
+import Offcanvas from '../../src/offcanvas.js'
+import { isVisible } from '../../src/util/index.js'
+import ScrollBarHelper from '../../src/util/scrollbar.js'
+import {
+ clearBodyAndDocument, clearFixture, createEvent, getFixture, jQueryMock
+} from '../helpers/fixture.js'
describe('Offcanvas', () => {
let fixtureEl
@@ -51,12 +53,12 @@ describe('Offcanvas', () => {
const closeEl = fixtureEl.querySelector('a')
const offCanvas = new Offcanvas(offCanvasEl)
- spyOn(offCanvas, 'hide')
+ const spy = spyOn(offCanvas, 'hide')
closeEl.click()
- expect(offCanvas._config.keyboard).toBe(true)
- expect(offCanvas.hide).toHaveBeenCalled()
+ expect(offCanvas._config.keyboard).toBeTrue()
+ expect(spy).toHaveBeenCalled()
})
it('should hide if esc is pressed', () => {
@@ -67,11 +69,26 @@ describe('Offcanvas', () => {
const keyDownEsc = createEvent('keydown')
keyDownEsc.key = 'Escape'
- spyOn(offCanvas, 'hide')
+ const spy = spyOn(offCanvas, 'hide')
offCanvasEl.dispatchEvent(keyDownEsc)
- expect(offCanvas.hide).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
+ })
+
+ it('should hide if esc is pressed and backdrop is static', () => {
+ fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+
+ const offCanvasEl = fixtureEl.querySelector('.offcanvas')
+ const offCanvas = new Offcanvas(offCanvasEl, { backdrop: 'static' })
+ const keyDownEsc = createEvent('keydown')
+ keyDownEsc.key = 'Escape'
+
+ const spy = spyOn(offCanvas, 'hide')
+
+ offCanvasEl.dispatchEvent(keyDownEsc)
+
+ expect(spy).toHaveBeenCalled()
})
it('should not hide if esc is not pressed', () => {
@@ -82,66 +99,115 @@ describe('Offcanvas', () => {
const keydownTab = createEvent('keydown')
keydownTab.key = 'Tab'
- spyOn(offCanvas, 'hide')
+ const spy = spyOn(offCanvas, 'hide')
- document.dispatchEvent(keydownTab)
+ offCanvasEl.dispatchEvent(keydownTab)
- expect(offCanvas.hide).not.toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
})
it('should not hide if esc is pressed but with keyboard = false', () => {
- fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="offcanvas"></div>'
- const offCanvasEl = fixtureEl.querySelector('.offcanvas')
- const offCanvas = new Offcanvas(offCanvasEl, { keyboard: false })
- const keyDownEsc = createEvent('keydown')
- keyDownEsc.key = 'Escape'
+ const offCanvasEl = fixtureEl.querySelector('.offcanvas')
+ const offCanvas = new Offcanvas(offCanvasEl, { keyboard: false })
+ const keyDownEsc = createEvent('keydown')
+ keyDownEsc.key = 'Escape'
+
+ const spy = spyOn(offCanvas, 'hide')
+ const hidePreventedSpy = jasmine.createSpy('hidePrevented')
+ offCanvasEl.addEventListener('hidePrevented.bs.offcanvas', hidePreventedSpy)
+
+ offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+ expect(offCanvas._config.keyboard).toBeFalse()
+ offCanvasEl.dispatchEvent(keyDownEsc)
+
+ expect(hidePreventedSpy).toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
+ resolve()
+ })
+
+ offCanvas.show()
+ })
+ })
- spyOn(offCanvas, 'hide')
+ it('should not hide if user clicks on static backdrop', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="offcanvas"></div>'
- document.dispatchEvent(keyDownEsc)
+ const offCanvasEl = fixtureEl.querySelector('div')
+ const offCanvas = new Offcanvas(offCanvasEl, { backdrop: 'static' })
- expect(offCanvas._config.keyboard).toBe(false)
- expect(offCanvas.hide).not.toHaveBeenCalled()
+ const clickEvent = new Event('mousedown', { bubbles: true, cancelable: true })
+ const spyClick = spyOn(offCanvas._backdrop._config, 'clickCallback').and.callThrough()
+ const spyHide = spyOn(offCanvas._backdrop, 'hide').and.callThrough()
+ const hidePreventedSpy = jasmine.createSpy('hidePrevented')
+ offCanvasEl.addEventListener('hidePrevented.bs.offcanvas', hidePreventedSpy)
+
+ offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+ expect(spyClick).toEqual(jasmine.any(Function))
+
+ offCanvas._backdrop._getElement().dispatchEvent(clickEvent)
+ expect(hidePreventedSpy).toHaveBeenCalled()
+ expect(spyHide).not.toHaveBeenCalled()
+ resolve()
+ })
+
+ offCanvas.show()
+ })
+ })
+
+ it('should call `hide` on resize, if element\'s position is not fixed any more', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="offcanvas-lg"></div>'
+
+ const offCanvasEl = fixtureEl.querySelector('div')
+ const offCanvas = new Offcanvas(offCanvasEl)
+
+ const spy = spyOn(offCanvas, 'hide').and.callThrough()
+
+ offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+ const resizeEvent = createEvent('resize')
+ offCanvasEl.style.removeProperty('position')
+
+ window.dispatchEvent(resizeEvent)
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
+
+ offCanvas.show()
+ })
})
})
describe('config', () => {
it('should have default values', () => {
- fixtureEl.innerHTML = [
- '<div class="offcanvas">',
- '</div>'
- ].join('')
+ fixtureEl.innerHTML = '<div class="offcanvas"></div>'
const offCanvasEl = fixtureEl.querySelector('.offcanvas')
const offCanvas = new Offcanvas(offCanvasEl)
- expect(offCanvas._config.backdrop).toEqual(true)
- expect(offCanvas._backdrop._config.isVisible).toEqual(true)
- expect(offCanvas._config.keyboard).toEqual(true)
- expect(offCanvas._config.scroll).toEqual(false)
+ expect(offCanvas._config.backdrop).toBeTrue()
+ expect(offCanvas._backdrop._config.isVisible).toBeTrue()
+ expect(offCanvas._config.keyboard).toBeTrue()
+ expect(offCanvas._config.scroll).toBeFalse()
})
it('should read data attributes and override default config', () => {
- fixtureEl.innerHTML = [
- '<div class="offcanvas" data-bs-scroll="true" data-bs-backdrop="false" data-bs-keyboard="false">',
- '</div>'
- ].join('')
+ fixtureEl.innerHTML = '<div class="offcanvas" data-bs-scroll="true" data-bs-backdrop="false" data-bs-keyboard="false"></div>'
const offCanvasEl = fixtureEl.querySelector('.offcanvas')
const offCanvas = new Offcanvas(offCanvasEl)
- expect(offCanvas._config.backdrop).toEqual(false)
- expect(offCanvas._backdrop._config.isVisible).toEqual(false)
- expect(offCanvas._config.keyboard).toEqual(false)
- expect(offCanvas._config.scroll).toEqual(true)
+ expect(offCanvas._config.backdrop).toBeFalse()
+ expect(offCanvas._backdrop._config.isVisible).toBeFalse()
+ expect(offCanvas._config.keyboard).toBeFalse()
+ expect(offCanvas._config.scroll).toBeTrue()
})
it('given a config object must override data attributes', () => {
- fixtureEl.innerHTML = [
- '<div class="offcanvas" data-bs-scroll="true" data-bs-backdrop="false" data-bs-keyboard="false">',
- '</div>'
- ].join('')
+ fixtureEl.innerHTML = '<div class="offcanvas" data-bs-scroll="true" data-bs-backdrop="false" data-bs-keyboard="false"></div>'
const offCanvasEl = fixtureEl.querySelector('.offcanvas')
const offCanvas = new Offcanvas(offCanvasEl, {
@@ -149,90 +215,120 @@ describe('Offcanvas', () => {
keyboard: true,
scroll: false
})
- expect(offCanvas._config.backdrop).toEqual(true)
- expect(offCanvas._config.keyboard).toEqual(true)
- expect(offCanvas._config.scroll).toEqual(false)
+ expect(offCanvas._config.backdrop).toBeTrue()
+ expect(offCanvas._config.keyboard).toBeTrue()
+ expect(offCanvas._config.scroll).toBeFalse()
})
})
- describe('options', () => {
- it('if scroll is enabled, should allow body to scroll while offcanvas is open', done => {
- fixtureEl.innerHTML = '<div class="offcanvas"></div>'
-
- spyOn(ScrollBarHelper.prototype, 'hide').and.callThrough()
- spyOn(ScrollBarHelper.prototype, 'reset').and.callThrough()
- const offCanvasEl = fixtureEl.querySelector('.offcanvas')
- const offCanvas = new Offcanvas(offCanvasEl, { scroll: true })
- offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
- expect(ScrollBarHelper.prototype.hide).not.toHaveBeenCalled()
- offCanvas.hide()
+ describe('options', () => {
+ it('if scroll is enabled, should allow body to scroll while offcanvas is open', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+
+ const spyHide = spyOn(ScrollBarHelper.prototype, 'hide').and.callThrough()
+ const spyReset = spyOn(ScrollBarHelper.prototype, 'reset').and.callThrough()
+ const offCanvasEl = fixtureEl.querySelector('.offcanvas')
+ const offCanvas = new Offcanvas(offCanvasEl, { scroll: true })
+
+ offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+ expect(spyHide).not.toHaveBeenCalled()
+ offCanvas.hide()
+ })
+ offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
+ expect(spyReset).not.toHaveBeenCalled()
+ resolve()
+ })
+ offCanvas.show()
})
- offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
- expect(ScrollBarHelper.prototype.reset).not.toHaveBeenCalled()
- done()
+ })
+
+ it('if scroll is disabled, should call ScrollBarHelper to handle scrollBar on body', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+
+ const spyHide = spyOn(ScrollBarHelper.prototype, 'hide').and.callThrough()
+ const spyReset = spyOn(ScrollBarHelper.prototype, 'reset').and.callThrough()
+ const offCanvasEl = fixtureEl.querySelector('.offcanvas')
+ const offCanvas = new Offcanvas(offCanvasEl, { scroll: false })
+
+ offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+ expect(spyHide).toHaveBeenCalled()
+ offCanvas.hide()
+ })
+ offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
+ expect(spyReset).toHaveBeenCalled()
+ resolve()
+ })
+ offCanvas.show()
})
- offCanvas.show()
})
- it('if scroll is disabled, should call ScrollBarHelper to handle scrollBar on body', done => {
- fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+ it('should hide a shown element if user click on backdrop', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="offcanvas"></div>'
- spyOn(ScrollBarHelper.prototype, 'hide').and.callThrough()
- spyOn(ScrollBarHelper.prototype, 'reset').and.callThrough()
- const offCanvasEl = fixtureEl.querySelector('.offcanvas')
- const offCanvas = new Offcanvas(offCanvasEl, { scroll: false })
+ const offCanvasEl = fixtureEl.querySelector('div')
+ const offCanvas = new Offcanvas(offCanvasEl, { backdrop: true })
- offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
- expect(ScrollBarHelper.prototype.hide).toHaveBeenCalled()
- offCanvas.hide()
- })
- offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
- expect(ScrollBarHelper.prototype.reset).toHaveBeenCalled()
- done()
+ const clickEvent = new Event('mousedown', { bubbles: true, cancelable: true })
+ const spy = spyOn(offCanvas._backdrop._config, 'clickCallback').and.callThrough()
+
+ offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+ expect(offCanvas._backdrop._config.clickCallback).toEqual(jasmine.any(Function))
+
+ offCanvas._backdrop._getElement().dispatchEvent(clickEvent)
+ })
+
+ offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
+
+ offCanvas.show()
})
- offCanvas.show()
})
- it('should hide a shown element if user click on backdrop', done => {
- fixtureEl.innerHTML = '<div class="offcanvas"></div>'
-
- const offCanvasEl = fixtureEl.querySelector('div')
- const offCanvas = new Offcanvas(offCanvasEl, { backdrop: true })
+ it('should not trap focus if scroll is allowed', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="offcanvas"></div>'
- const clickEvent = document.createEvent('MouseEvents')
- clickEvent.initEvent('mousedown', true, true)
- spyOn(offCanvas._backdrop._config, 'clickCallback').and.callThrough()
+ const offCanvasEl = fixtureEl.querySelector('.offcanvas')
+ const offCanvas = new Offcanvas(offCanvasEl, {
+ scroll: true,
+ backdrop: false
+ })
- offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
- expect(typeof offCanvas._backdrop._config.clickCallback).toBe('function')
+ const spy = spyOn(offCanvas._focustrap, 'activate').and.callThrough()
- offCanvas._backdrop._getElement().dispatchEvent(clickEvent)
- })
+ offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+ expect(spy).not.toHaveBeenCalled()
+ resolve()
+ })
- offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
- expect(offCanvas._backdrop._config.clickCallback).toHaveBeenCalled()
- done()
+ offCanvas.show()
})
-
- offCanvas.show()
})
- it('should not trap focus if scroll is allowed', done => {
- fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+ it('should trap focus if scroll is allowed OR backdrop is enabled', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="offcanvas"></div>'
- const offCanvasEl = fixtureEl.querySelector('.offcanvas')
- const offCanvas = new Offcanvas(offCanvasEl, {
- scroll: true
- })
+ const offCanvasEl = fixtureEl.querySelector('.offcanvas')
+ const offCanvas = new Offcanvas(offCanvasEl, {
+ scroll: true,
+ backdrop: true
+ })
- spyOn(offCanvas._focustrap, 'activate').and.callThrough()
+ const spy = spyOn(offCanvas._focustrap, 'activate').and.callThrough()
- offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
- expect(offCanvas._focustrap.activate).not.toHaveBeenCalled()
- done()
- })
+ offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- offCanvas.show()
+ offCanvas.show()
+ })
})
})
@@ -243,30 +339,57 @@ describe('Offcanvas', () => {
const offCanvasEl = fixtureEl.querySelector('.offcanvas')
const offCanvas = new Offcanvas(offCanvasEl)
- spyOn(offCanvas, 'show')
+ const spy = spyOn(offCanvas, 'show')
offCanvas.toggle()
- expect(offCanvas.show).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
})
it('should call hide method if show class is present', () => {
- fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="offcanvas"></div>'
- const offCanvasEl = fixtureEl.querySelector('.offcanvas')
- const offCanvas = new Offcanvas(offCanvasEl)
- offCanvas.show()
- expect(offCanvasEl.classList.contains('show')).toBe(true)
+ const offCanvasEl = fixtureEl.querySelector('.offcanvas')
+ const offCanvas = new Offcanvas(offCanvasEl)
- spyOn(offCanvas, 'hide')
+ offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+ expect(offCanvasEl).toHaveClass('show')
+ const spy = spyOn(offCanvas, 'hide')
- offCanvas.toggle()
+ offCanvas.toggle()
+
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- expect(offCanvas.hide).toHaveBeenCalled()
+ offCanvas.show()
+ })
})
})
describe('show', () => {
+ it('should add `showing` class during opening and `show` class on end', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+ const offCanvasEl = fixtureEl.querySelector('.offcanvas')
+ const offCanvas = new Offcanvas(offCanvasEl)
+
+ offCanvasEl.addEventListener('show.bs.offcanvas', () => {
+ expect(offCanvasEl).not.toHaveClass('show')
+ })
+
+ offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+ expect(offCanvasEl).not.toHaveClass('showing')
+ expect(offCanvasEl).toHaveClass('show')
+ resolve()
+ })
+
+ offCanvas.show()
+ expect(offCanvasEl).toHaveClass('showing')
+ })
+ })
+
it('should do nothing if already shown', () => {
fixtureEl.innerHTML = '<div class="offcanvas show"></div>'
@@ -274,166 +397,206 @@ describe('Offcanvas', () => {
const offCanvas = new Offcanvas(offCanvasEl)
offCanvas.show()
- expect(offCanvasEl.classList.contains('show')).toBe(true)
+ expect(offCanvasEl).toHaveClass('show')
- spyOn(offCanvas._backdrop, 'show').and.callThrough()
- spyOn(EventHandler, 'trigger').and.callThrough()
+ const spyShow = spyOn(offCanvas._backdrop, 'show').and.callThrough()
+ const spyTrigger = spyOn(EventHandler, 'trigger').and.callThrough()
offCanvas.show()
- expect(EventHandler.trigger).not.toHaveBeenCalled()
- expect(offCanvas._backdrop.show).not.toHaveBeenCalled()
+ expect(spyTrigger).not.toHaveBeenCalled()
+ expect(spyShow).not.toHaveBeenCalled()
})
- it('should show a hidden element', done => {
- fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+ it('should show a hidden element', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="offcanvas"></div>'
- const offCanvasEl = fixtureEl.querySelector('div')
- const offCanvas = new Offcanvas(offCanvasEl)
- spyOn(offCanvas._backdrop, 'show').and.callThrough()
+ const offCanvasEl = fixtureEl.querySelector('div')
+ const offCanvas = new Offcanvas(offCanvasEl)
+ const spy = spyOn(offCanvas._backdrop, 'show').and.callThrough()
- offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
- expect(offCanvasEl.classList.contains('show')).toEqual(true)
- expect(offCanvas._backdrop.show).toHaveBeenCalled()
- done()
- })
+ offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+ expect(offCanvasEl).toHaveClass('show')
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- offCanvas.show()
+ offCanvas.show()
+ })
})
- it('should not fire shown when show is prevented', done => {
- fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+ it('should not fire shown when show is prevented', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = '<div class="offcanvas"></div>'
- const offCanvasEl = fixtureEl.querySelector('div')
- const offCanvas = new Offcanvas(offCanvasEl)
- spyOn(offCanvas._backdrop, 'show').and.callThrough()
-
- const expectEnd = () => {
- setTimeout(() => {
- expect(offCanvas._backdrop.show).not.toHaveBeenCalled()
- done()
- }, 10)
- }
-
- offCanvasEl.addEventListener('show.bs.offcanvas', event => {
- event.preventDefault()
- expectEnd()
- })
+ const offCanvasEl = fixtureEl.querySelector('div')
+ const offCanvas = new Offcanvas(offCanvasEl)
+ const spy = spyOn(offCanvas._backdrop, 'show').and.callThrough()
- offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
- throw new Error('should not fire shown event')
- })
+ const expectEnd = () => {
+ setTimeout(() => {
+ expect(spy).not.toHaveBeenCalled()
+ resolve()
+ }, 10)
+ }
- offCanvas.show()
+ offCanvasEl.addEventListener('show.bs.offcanvas', event => {
+ event.preventDefault()
+ expectEnd()
+ })
+
+ offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+ reject(new Error('should not fire shown event'))
+ })
+
+ offCanvas.show()
+ })
})
- it('on window load, should make visible an offcanvas element, if its markup contains class "show"', done => {
- fixtureEl.innerHTML = '<div class="offcanvas show"></div>'
+ it('on window load, should make visible an offcanvas element, if its markup contains class "show"', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="offcanvas show"></div>'
- const offCanvasEl = fixtureEl.querySelector('div')
- spyOn(Offcanvas.prototype, 'show').and.callThrough()
+ const offCanvasEl = fixtureEl.querySelector('div')
+ const spy = spyOn(Offcanvas.prototype, 'show').and.callThrough()
- offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
- done()
- })
+ offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+ resolve()
+ })
- window.dispatchEvent(createEvent('load'))
+ window.dispatchEvent(createEvent('load'))
- const instance = Offcanvas.getInstance(offCanvasEl)
- expect(instance).not.toBeNull()
- expect(Offcanvas.prototype.show).toHaveBeenCalled()
+ const instance = Offcanvas.getInstance(offCanvasEl)
+ expect(instance).not.toBeNull()
+ expect(spy).toHaveBeenCalled()
+ })
})
- it('should trap focus', done => {
- fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+ it('should trap focus', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="offcanvas"></div>'
- const offCanvasEl = fixtureEl.querySelector('.offcanvas')
- const offCanvas = new Offcanvas(offCanvasEl)
+ const offCanvasEl = fixtureEl.querySelector('.offcanvas')
+ const offCanvas = new Offcanvas(offCanvasEl)
- spyOn(offCanvas._focustrap, 'activate').and.callThrough()
+ const spy = spyOn(offCanvas._focustrap, 'activate').and.callThrough()
- offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
- expect(offCanvas._focustrap.activate).toHaveBeenCalled()
- done()
- })
+ offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- offCanvas.show()
+ offCanvas.show()
+ })
})
})
describe('hide', () => {
+ it('should add `hiding` class during closing and remover `show` & `hiding` classes on end', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+ const offCanvasEl = fixtureEl.querySelector('.offcanvas')
+ const offCanvas = new Offcanvas(offCanvasEl)
+
+ offCanvasEl.addEventListener('hide.bs.offcanvas', () => {
+ expect(offCanvasEl).not.toHaveClass('showing')
+ expect(offCanvasEl).toHaveClass('show')
+ })
+
+ offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
+ expect(offCanvasEl).not.toHaveClass('hiding')
+ expect(offCanvasEl).not.toHaveClass('show')
+ resolve()
+ })
+
+ offCanvas.show()
+ offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+ offCanvas.hide()
+ expect(offCanvasEl).not.toHaveClass('showing')
+ expect(offCanvasEl).toHaveClass('hiding')
+ })
+ })
+ })
+
it('should do nothing if already shown', () => {
fixtureEl.innerHTML = '<div class="offcanvas"></div>'
- spyOn(EventHandler, 'trigger').and.callThrough()
+ const spyTrigger = spyOn(EventHandler, 'trigger').and.callThrough()
const offCanvasEl = fixtureEl.querySelector('div')
const offCanvas = new Offcanvas(offCanvasEl)
- spyOn(offCanvas._backdrop, 'hide').and.callThrough()
+ const spyHide = spyOn(offCanvas._backdrop, 'hide').and.callThrough()
offCanvas.hide()
- expect(offCanvas._backdrop.hide).not.toHaveBeenCalled()
- expect(EventHandler.trigger).not.toHaveBeenCalled()
+ expect(spyHide).not.toHaveBeenCalled()
+ expect(spyTrigger).not.toHaveBeenCalled()
})
- it('should hide a shown element', done => {
- fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+ it('should hide a shown element', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="offcanvas"></div>'
- const offCanvasEl = fixtureEl.querySelector('div')
- const offCanvas = new Offcanvas(offCanvasEl)
- spyOn(offCanvas._backdrop, 'hide').and.callThrough()
- offCanvas.show()
+ const offCanvasEl = fixtureEl.querySelector('div')
+ const offCanvas = new Offcanvas(offCanvasEl)
+ const spy = spyOn(offCanvas._backdrop, 'hide').and.callThrough()
+ offCanvas.show()
- offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
- expect(offCanvasEl.classList.contains('show')).toEqual(false)
- expect(offCanvas._backdrop.hide).toHaveBeenCalled()
- done()
- })
+ offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
+ expect(offCanvasEl).not.toHaveClass('show')
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- offCanvas.hide()
+ offCanvas.hide()
+ })
})
- it('should not fire hidden when hide is prevented', done => {
- fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+ it('should not fire hidden when hide is prevented', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = '<div class="offcanvas"></div>'
- const offCanvasEl = fixtureEl.querySelector('div')
- const offCanvas = new Offcanvas(offCanvasEl)
- spyOn(offCanvas._backdrop, 'hide').and.callThrough()
+ const offCanvasEl = fixtureEl.querySelector('div')
+ const offCanvas = new Offcanvas(offCanvasEl)
+ const spy = spyOn(offCanvas._backdrop, 'hide').and.callThrough()
- offCanvas.show()
+ offCanvas.show()
- const expectEnd = () => {
- setTimeout(() => {
- expect(offCanvas._backdrop.hide).not.toHaveBeenCalled()
- done()
- }, 10)
- }
+ const expectEnd = () => {
+ setTimeout(() => {
+ expect(spy).not.toHaveBeenCalled()
+ resolve()
+ }, 10)
+ }
- offCanvasEl.addEventListener('hide.bs.offcanvas', event => {
- event.preventDefault()
- expectEnd()
- })
+ offCanvasEl.addEventListener('hide.bs.offcanvas', event => {
+ event.preventDefault()
+ expectEnd()
+ })
- offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
- throw new Error('should not fire hidden event')
- })
+ offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
+ reject(new Error('should not fire hidden event'))
+ })
- offCanvas.hide()
+ offCanvas.hide()
+ })
})
- it('should release focus trap', done => {
- fixtureEl.innerHTML = '<div class="offcanvas"></div>'
+ it('should release focus trap', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div class="offcanvas"></div>'
- const offCanvasEl = fixtureEl.querySelector('div')
- const offCanvas = new Offcanvas(offCanvasEl)
- spyOn(offCanvas._focustrap, 'deactivate').and.callThrough()
- offCanvas.show()
+ const offCanvasEl = fixtureEl.querySelector('div')
+ const offCanvas = new Offcanvas(offCanvasEl)
+ const spy = spyOn(offCanvas._focustrap, 'deactivate').and.callThrough()
+ offCanvas.show()
- offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
- expect(offCanvas._focustrap.deactivate).toHaveBeenCalled()
- done()
- })
+ offCanvasEl.addEventListener('hidden.bs.offcanvas', () => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- offCanvas.hide()
+ offCanvas.hide()
+ })
})
})
@@ -444,41 +607,41 @@ describe('Offcanvas', () => {
const offCanvasEl = fixtureEl.querySelector('div')
const offCanvas = new Offcanvas(offCanvasEl)
const backdrop = offCanvas._backdrop
- spyOn(backdrop, 'dispose').and.callThrough()
+ const spyDispose = spyOn(backdrop, 'dispose').and.callThrough()
const focustrap = offCanvas._focustrap
- spyOn(focustrap, 'deactivate').and.callThrough()
+ const spyDeactivate = spyOn(focustrap, 'deactivate').and.callThrough()
expect(Offcanvas.getInstance(offCanvasEl)).toEqual(offCanvas)
- spyOn(EventHandler, 'off')
-
offCanvas.dispose()
- expect(backdrop.dispose).toHaveBeenCalled()
+ expect(spyDispose).toHaveBeenCalled()
expect(offCanvas._backdrop).toBeNull()
- expect(focustrap.deactivate).toHaveBeenCalled()
+ expect(spyDeactivate).toHaveBeenCalled()
expect(offCanvas._focustrap).toBeNull()
- expect(Offcanvas.getInstance(offCanvasEl)).toEqual(null)
+ expect(Offcanvas.getInstance(offCanvasEl)).toBeNull()
})
})
describe('data-api', () => {
- it('should not prevent event for input', done => {
- fixtureEl.innerHTML = [
- '<input type="checkbox" data-bs-toggle="offcanvas" data-bs-target="#offcanvasdiv1" />',
- '<div id="offcanvasdiv1" class="offcanvas"></div>'
- ].join('')
-
- const target = fixtureEl.querySelector('input')
- const offCanvasEl = fixtureEl.querySelector('#offcanvasdiv1')
-
- offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
- expect(offCanvasEl.classList.contains('show')).toEqual(true)
- expect(target.checked).toEqual(true)
- done()
+ it('should not prevent event for input', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<input type="checkbox" data-bs-toggle="offcanvas" data-bs-target="#offcanvasdiv1">',
+ '<div id="offcanvasdiv1" class="offcanvas"></div>'
+ ].join('')
+
+ const target = fixtureEl.querySelector('input')
+ const offCanvasEl = fixtureEl.querySelector('#offcanvasdiv1')
+
+ offCanvasEl.addEventListener('shown.bs.offcanvas', () => {
+ expect(offCanvasEl).toHaveClass('show')
+ expect(target.checked).toBeTrue()
+ resolve()
+ })
+
+ target.click()
})
-
- target.click()
})
it('should not call toggle on disabled elements', () => {
@@ -489,83 +652,89 @@ describe('Offcanvas', () => {
const target = fixtureEl.querySelector('a')
- spyOn(Offcanvas.prototype, 'toggle')
+ const spy = spyOn(Offcanvas.prototype, 'toggle')
target.click()
- expect(Offcanvas.prototype.toggle).not.toHaveBeenCalled()
- })
-
- it('should call hide first, if another offcanvas is open', done => {
- fixtureEl.innerHTML = [
- '<button id="btn2" data-bs-toggle="offcanvas" data-bs-target="#offcanvas2" ></button>',
- '<div id="offcanvas1" class="offcanvas"></div>',
- '<div id="offcanvas2" class="offcanvas"></div>'
- ].join('')
-
- const trigger2 = fixtureEl.querySelector('#btn2')
- const offcanvasEl1 = document.querySelector('#offcanvas1')
- const offcanvasEl2 = document.querySelector('#offcanvas2')
- const offcanvas1 = new Offcanvas(offcanvasEl1)
-
- offcanvasEl1.addEventListener('shown.bs.offcanvas', () => {
- trigger2.click()
+ expect(spy).not.toHaveBeenCalled()
+ })
+
+ it('should call hide first, if another offcanvas is open', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<button id="btn2" data-bs-toggle="offcanvas" data-bs-target="#offcanvas2"></button>',
+ '<div id="offcanvas1" class="offcanvas"></div>',
+ '<div id="offcanvas2" class="offcanvas"></div>'
+ ].join('')
+
+ const trigger2 = fixtureEl.querySelector('#btn2')
+ const offcanvasEl1 = document.querySelector('#offcanvas1')
+ const offcanvasEl2 = document.querySelector('#offcanvas2')
+ const offcanvas1 = new Offcanvas(offcanvasEl1)
+
+ offcanvasEl1.addEventListener('shown.bs.offcanvas', () => {
+ trigger2.click()
+ })
+ offcanvasEl1.addEventListener('hidden.bs.offcanvas', () => {
+ expect(Offcanvas.getInstance(offcanvasEl2)).not.toBeNull()
+ resolve()
+ })
+ offcanvas1.show()
})
- offcanvasEl1.addEventListener('hidden.bs.offcanvas', () => {
- expect(Offcanvas.getInstance(offcanvasEl2)).not.toBeNull()
- done()
- })
- offcanvas1.show()
})
- it('should focus on trigger element after closing offcanvas', done => {
- fixtureEl.innerHTML = [
- '<button id="btn" data-bs-toggle="offcanvas" data-bs-target="#offcanvas" ></button>',
- '<div id="offcanvas" class="offcanvas"></div>'
- ].join('')
-
- const trigger = fixtureEl.querySelector('#btn')
- const offcanvasEl = fixtureEl.querySelector('#offcanvas')
- const offcanvas = new Offcanvas(offcanvasEl)
- spyOn(trigger, 'focus')
-
- offcanvasEl.addEventListener('shown.bs.offcanvas', () => {
- offcanvas.hide()
+ it('should focus on trigger element after closing offcanvas', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<button id="btn" data-bs-toggle="offcanvas" data-bs-target="#offcanvas"></button>',
+ '<div id="offcanvas" class="offcanvas"></div>'
+ ].join('')
+
+ const trigger = fixtureEl.querySelector('#btn')
+ const offcanvasEl = fixtureEl.querySelector('#offcanvas')
+ const offcanvas = new Offcanvas(offcanvasEl)
+ const spy = spyOn(trigger, 'focus')
+
+ offcanvasEl.addEventListener('shown.bs.offcanvas', () => {
+ offcanvas.hide()
+ })
+ offcanvasEl.addEventListener('hidden.bs.offcanvas', () => {
+ setTimeout(() => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ }, 5)
+ })
+
+ trigger.click()
})
- offcanvasEl.addEventListener('hidden.bs.offcanvas', () => {
- setTimeout(() => {
- expect(trigger.focus).toHaveBeenCalled()
- done()
- }, 5)
- })
-
- trigger.click()
})
- it('should not focus on trigger element after closing offcanvas, if it is not visible', done => {
- fixtureEl.innerHTML = [
- '<button id="btn" data-bs-toggle="offcanvas" data-bs-target="#offcanvas" ></button>',
- '<div id="offcanvas" class="offcanvas"></div>'
- ].join('')
-
- const trigger = fixtureEl.querySelector('#btn')
- const offcanvasEl = fixtureEl.querySelector('#offcanvas')
- const offcanvas = new Offcanvas(offcanvasEl)
- spyOn(trigger, 'focus')
-
- offcanvasEl.addEventListener('shown.bs.offcanvas', () => {
- trigger.style.display = 'none'
- offcanvas.hide()
- })
- offcanvasEl.addEventListener('hidden.bs.offcanvas', () => {
- setTimeout(() => {
- expect(isVisible(trigger)).toBe(false)
- expect(trigger.focus).not.toHaveBeenCalled()
- done()
- }, 5)
+ it('should not focus on trigger element after closing offcanvas, if it is not visible', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<button id="btn" data-bs-toggle="offcanvas" data-bs-target="#offcanvas"></button>',
+ '<div id="offcanvas" class="offcanvas"></div>'
+ ].join('')
+
+ const trigger = fixtureEl.querySelector('#btn')
+ const offcanvasEl = fixtureEl.querySelector('#offcanvas')
+ const offcanvas = new Offcanvas(offcanvasEl)
+ const spy = spyOn(trigger, 'focus')
+
+ offcanvasEl.addEventListener('shown.bs.offcanvas', () => {
+ trigger.style.display = 'none'
+ offcanvas.hide()
+ })
+ offcanvasEl.addEventListener('hidden.bs.offcanvas', () => {
+ setTimeout(() => {
+ expect(isVisible(trigger)).toBeFalse()
+ expect(spy).not.toHaveBeenCalled()
+ resolve()
+ }, 5)
+ })
+
+ trigger.click()
})
-
- trigger.click()
})
})
@@ -644,13 +813,13 @@ describe('Offcanvas', () => {
const div = fixtureEl.querySelector('div')
- spyOn(Offcanvas.prototype, 'show')
+ const spy = spyOn(Offcanvas.prototype, 'show')
jQueryMock.fn.offcanvas = Offcanvas.jQueryInterface
jQueryMock.elements = [div]
jQueryMock.fn.offcanvas.call(jQueryMock, 'show')
- expect(Offcanvas.prototype.show).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
})
it('should create a offcanvas with given config', () => {
@@ -665,7 +834,7 @@ describe('Offcanvas', () => {
const offcanvas = Offcanvas.getInstance(div)
expect(offcanvas).not.toBeNull()
- expect(offcanvas._config.scroll).toBe(true)
+ expect(offcanvas._config.scroll).toBeTrue()
})
})
@@ -706,7 +875,7 @@ describe('Offcanvas', () => {
const div = fixtureEl.querySelector('div')
- expect(Offcanvas.getInstance(div)).toEqual(null)
+ expect(Offcanvas.getInstance(div)).toBeNull()
expect(Offcanvas.getOrCreateInstance(div)).toBeInstanceOf(Offcanvas)
})
@@ -715,13 +884,13 @@ describe('Offcanvas', () => {
const div = fixtureEl.querySelector('div')
- expect(Offcanvas.getInstance(div)).toEqual(null)
+ expect(Offcanvas.getInstance(div)).toBeNull()
const offcanvas = Offcanvas.getOrCreateInstance(div, {
scroll: true
})
expect(offcanvas).toBeInstanceOf(Offcanvas)
- expect(offcanvas._config.scroll).toEqual(true)
+ expect(offcanvas._config.scroll).toBeTrue()
})
it('should return the instance when exists without given configuration', () => {
@@ -739,7 +908,7 @@ describe('Offcanvas', () => {
expect(offcanvas).toBeInstanceOf(Offcanvas)
expect(offcanvas2).toEqual(offcanvas)
- expect(offcanvas2._config.scroll).toEqual(true)
+ expect(offcanvas2._config.scroll).toBeTrue()
})
})
})
diff --git a/js/tests/unit/popover.spec.js b/js/tests/unit/popover.spec.js
index b3bba3180..ba38ebe06 100644
--- a/js/tests/unit/popover.spec.js
+++ b/js/tests/unit/popover.spec.js
@@ -1,5 +1,6 @@
-import Popover from '../../src/popover'
-import { clearFixture, getFixture, jQueryMock } from '../helpers/fixture'
+import EventHandler from '../../src/dom/event-handler.js'
+import Popover from '../../src/popover.js'
+import { clearFixture, getFixture, jQueryMock } from '../helpers/fixture.js'
describe('Popover', () => {
let fixtureEl
@@ -42,12 +43,6 @@ describe('Popover', () => {
})
})
- describe('Event', () => {
- it('should return plugin events', () => {
- expect(Popover.Event).toEqual(jasmine.any(Object))
- })
- })
-
describe('EVENT_KEY', () => {
it('should return plugin event key', () => {
expect(Popover.EVENT_KEY).toEqual('.bs.popover')
@@ -61,166 +56,264 @@ describe('Popover', () => {
})
describe('show', () => {
- it('should show a popover', done => {
- fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://twitter.com/getbootstrap">BS twitter</a>'
+ it('should show a popover', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://twitter.com/getbootstrap">BS twitter</a>'
- const popoverEl = fixtureEl.querySelector('a')
- const popover = new Popover(popoverEl)
+ const popoverEl = fixtureEl.querySelector('a')
+ const popover = new Popover(popoverEl)
- popoverEl.addEventListener('shown.bs.popover', () => {
- expect(document.querySelector('.popover')).not.toBeNull()
- done()
- })
+ popoverEl.addEventListener('shown.bs.popover', () => {
+ expect(document.querySelector('.popover')).not.toBeNull()
+ resolve()
+ })
- popover.show()
+ popover.show()
+ })
})
- it('should set title and content from functions', done => {
- fixtureEl.innerHTML = '<a href="#">BS twitter</a>'
+ it('should set title and content from functions', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#">BS twitter</a>'
- const popoverEl = fixtureEl.querySelector('a')
- const popover = new Popover(popoverEl, {
- title: () => 'Bootstrap',
- content: () => 'loves writing tests (╯°□°)╯︵ ┻━┻'
- })
+ const popoverEl = fixtureEl.querySelector('a')
+ const popover = new Popover(popoverEl, {
+ title: () => 'Bootstrap',
+ content: () => 'loves writing tests (╯°□°)╯︵ ┻━┻'
+ })
- popoverEl.addEventListener('shown.bs.popover', () => {
- const popoverDisplayed = document.querySelector('.popover')
+ popoverEl.addEventListener('shown.bs.popover', () => {
+ const popoverDisplayed = document.querySelector('.popover')
- expect(popoverDisplayed).not.toBeNull()
- expect(popoverDisplayed.querySelector('.popover-header').textContent).toEqual('Bootstrap')
- expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('loves writing tests (╯°□°)╯︵ ┻━┻')
- done()
- })
+ expect(popoverDisplayed).not.toBeNull()
+ expect(popoverDisplayed.querySelector('.popover-header').textContent).toEqual('Bootstrap')
+ expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('loves writing tests (╯°□°)╯︵ ┻━┻')
+ resolve()
+ })
- popover.show()
+ popover.show()
+ })
})
- it('should show a popover with just content', done => {
- fixtureEl.innerHTML = '<a href="#">BS twitter</a>'
+ it('should call content and title functions with trigger element', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" data-foo="bar">BS twitter</a>'
+
+ const popoverEl = fixtureEl.querySelector('a')
+ const popover = new Popover(popoverEl, {
+ title(el) {
+ return el.dataset.foo
+ },
+ content(el) {
+ return el.dataset.foo
+ }
+ })
+
+ popoverEl.addEventListener('shown.bs.popover', () => {
+ const popoverDisplayed = document.querySelector('.popover')
+
+ expect(popoverDisplayed).not.toBeNull()
+ expect(popoverDisplayed.querySelector('.popover-header').textContent).toEqual('bar')
+ expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('bar')
+ resolve()
+ })
- const popoverEl = fixtureEl.querySelector('a')
- const popover = new Popover(popoverEl, {
- content: 'Popover content'
+ popover.show()
})
+ })
- popoverEl.addEventListener('shown.bs.popover', () => {
- const popoverDisplayed = document.querySelector('.popover')
+ it('should call content and title functions with correct this value', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" data-foo="bar">BS twitter</a>'
+
+ const popoverEl = fixtureEl.querySelector('a')
+ const popover = new Popover(popoverEl, {
+ title() {
+ return this.dataset.foo
+ },
+ content() {
+ return this.dataset.foo
+ }
+ })
+
+ popoverEl.addEventListener('shown.bs.popover', () => {
+ const popoverDisplayed = document.querySelector('.popover')
+
+ expect(popoverDisplayed).not.toBeNull()
+ expect(popoverDisplayed.querySelector('.popover-header').textContent).toEqual('bar')
+ expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('bar')
+ resolve()
+ })
- expect(popoverDisplayed).not.toBeNull()
- expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('Popover content')
- done()
+ popover.show()
})
-
- popover.show()
})
- it('should show a popover with just content without having header', done => {
- fixtureEl.innerHTML = '<a href="#">Nice link</a>'
+ it('should show a popover with just content without having header', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#">Nice link</a>'
- const popoverEl = fixtureEl.querySelector('a')
- const popover = new Popover(popoverEl, {
- content: 'Some beautiful content :)'
- })
+ const popoverEl = fixtureEl.querySelector('a')
+ const popover = new Popover(popoverEl, {
+ content: 'Some beautiful content :)'
+ })
- popoverEl.addEventListener('shown.bs.popover', () => {
- const popoverDisplayed = document.querySelector('.popover')
+ popoverEl.addEventListener('shown.bs.popover', () => {
+ const popoverDisplayed = document.querySelector('.popover')
- expect(popoverDisplayed).not.toBeNull()
- expect(popoverDisplayed.querySelector('.popover-header')).toBeNull()
- expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('Some beautiful content :)')
- done()
- })
+ expect(popoverDisplayed).not.toBeNull()
+ expect(popoverDisplayed.querySelector('.popover-header')).toBeNull()
+ expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('Some beautiful content :)')
+ resolve()
+ })
- popover.show()
+ popover.show()
+ })
})
- it('should show a popover with just title without having body', done => {
- fixtureEl.innerHTML = '<a href="#">Nice link</a>'
+ it('should show a popover with just title without having body', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#">Nice link</a>'
- const popoverEl = fixtureEl.querySelector('a')
- const popover = new Popover(popoverEl, {
- title: 'Title, which does not require content'
- })
+ const popoverEl = fixtureEl.querySelector('a')
+ const popover = new Popover(popoverEl, {
+ title: 'Title which does not require content'
+ })
- popoverEl.addEventListener('shown.bs.popover', () => {
- const popoverDisplayed = document.querySelector('.popover')
+ popoverEl.addEventListener('shown.bs.popover', () => {
+ const popoverDisplayed = document.querySelector('.popover')
- expect(popoverDisplayed).not.toBeNull()
- expect(popoverDisplayed.querySelector('.popover-body')).toBeNull()
- expect(popoverDisplayed.querySelector('.popover-header').textContent).toEqual('Title, which does not require content')
- done()
- })
+ expect(popoverDisplayed).not.toBeNull()
+ expect(popoverDisplayed.querySelector('.popover-body')).toBeNull()
+ expect(popoverDisplayed.querySelector('.popover-header').textContent).toEqual('Title which does not require content')
+ resolve()
+ })
- popover.show()
+ popover.show()
+ })
})
- it('should call setContent once', done => {
- fixtureEl.innerHTML = '<a href="#">BS twitter</a>'
+ it('should show a popover with just title without having body using data-attribute to get config', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" data-bs-content="" title="Title which does not require content">Nice link</a>'
- const popoverEl = fixtureEl.querySelector('a')
- const popover = new Popover(popoverEl, {
- content: 'Popover content'
- })
- expect(popover._templateFactory).toBeNull()
- let spy = null
- let times = 1
+ const popoverEl = fixtureEl.querySelector('a')
+ const popover = new Popover(popoverEl)
+
+ popoverEl.addEventListener('shown.bs.popover', () => {
+ const popoverDisplayed = document.querySelector('.popover')
+
+ expect(popoverDisplayed).not.toBeNull()
+ expect(popoverDisplayed.querySelector('.popover-body')).toBeNull()
+ expect(popoverDisplayed.querySelector('.popover-header').textContent).toEqual('Title which does not require content')
+ resolve()
+ })
- popoverEl.addEventListener('hidden.bs.popover', () => {
popover.show()
})
+ })
- popoverEl.addEventListener('shown.bs.popover', () => {
- spy = spy || spyOn(popover._templateFactory, 'constructor').and.callThrough()
- const popoverDisplayed = document.querySelector('.popover')
+ it('should NOT show a popover without `title` and `content`', () => {
+ fixtureEl.innerHTML = '<a href="#" data-bs-content="" title="">Nice link</a>'
- expect(popoverDisplayed).not.toBeNull()
- expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('Popover content')
- expect(spy).toHaveBeenCalledTimes(0)
- if (times > 1) {
- done()
- }
+ const popoverEl = fixtureEl.querySelector('a')
+ const popover = new Popover(popoverEl, { animation: false })
+ const spy = spyOn(EventHandler, 'trigger').and.callThrough()
- times++
- popover.hide()
- })
popover.show()
+
+ expect(spy).not.toHaveBeenCalledWith(popoverEl, Popover.eventName('show'))
+ expect(document.querySelector('.popover')).toBeNull()
})
- it('should show a popover with provided custom class', done => {
+ it('"setContent" should keep the initial template', () => {
fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://twitter.com/getbootstrap" data-bs-custom-class="custom-class">BS twitter</a>'
const popoverEl = fixtureEl.querySelector('a')
const popover = new Popover(popoverEl)
- popoverEl.addEventListener('shown.bs.popover', () => {
- const tip = document.querySelector('.popover')
- expect(tip).not.toBeNull()
- expect(tip.classList.contains('custom-class')).toBeTrue()
- done()
+ popover.setContent({ '.tooltip-inner': 'foo' })
+ const tip = popover._getTipElement()
+
+ expect(tip).toHaveClass('popover')
+ expect(tip).toHaveClass('bs-popover-auto')
+ expect(tip.querySelector('.popover-arrow')).not.toBeNull()
+ expect(tip.querySelector('.popover-header')).not.toBeNull()
+ expect(tip.querySelector('.popover-body')).not.toBeNull()
+ })
+
+ it('should call setContent once', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#">BS twitter</a>'
+
+ const popoverEl = fixtureEl.querySelector('a')
+ const popover = new Popover(popoverEl, {
+ content: 'Popover content'
+ })
+ expect(popover._templateFactory).toBeNull()
+ let spy = null
+ let times = 1
+
+ popoverEl.addEventListener('hidden.bs.popover', () => {
+ popover.show()
+ })
+
+ popoverEl.addEventListener('shown.bs.popover', () => {
+ spy = spy || spyOn(popover._templateFactory, 'constructor').and.callThrough()
+ const popoverDisplayed = document.querySelector('.popover')
+
+ expect(popoverDisplayed).not.toBeNull()
+ expect(popoverDisplayed.querySelector('.popover-body').textContent).toEqual('Popover content')
+ expect(spy).toHaveBeenCalledTimes(0)
+ if (times > 1) {
+ resolve()
+ }
+
+ times++
+ popover.hide()
+ })
+ popover.show()
})
+ })
- popover.show()
+ it('should show a popover with provided custom class', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://twitter.com/getbootstrap" data-bs-custom-class="custom-class">BS twitter</a>'
+
+ const popoverEl = fixtureEl.querySelector('a')
+ const popover = new Popover(popoverEl)
+
+ popoverEl.addEventListener('shown.bs.popover', () => {
+ const tip = document.querySelector('.popover')
+ expect(tip).not.toBeNull()
+ expect(tip).toHaveClass('custom-class')
+ resolve()
+ })
+
+ popover.show()
+ })
})
})
describe('hide', () => {
- it('should hide a popover', done => {
- fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://twitter.com/getbootstrap">BS twitter</a>'
+ it('should hide a popover', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" title="Popover" data-bs-content="https://twitter.com/getbootstrap">BS twitter</a>'
- const popoverEl = fixtureEl.querySelector('a')
- const popover = new Popover(popoverEl)
+ const popoverEl = fixtureEl.querySelector('a')
+ const popover = new Popover(popoverEl)
- popoverEl.addEventListener('shown.bs.popover', () => {
- popover.hide()
- })
+ popoverEl.addEventListener('shown.bs.popover', () => {
+ popover.hide()
+ })
- popoverEl.addEventListener('hidden.bs.popover', () => {
- expect(document.querySelector('.popover')).toBeNull()
- done()
- })
+ popoverEl.addEventListener('hidden.bs.popover', () => {
+ expect(document.querySelector('.popover')).toBeNull()
+ resolve()
+ })
- popover.show()
+ popover.show()
+ })
})
})
@@ -290,11 +383,11 @@ describe('Popover', () => {
jQueryMock.fn.popover = Popover.jQueryInterface
jQueryMock.elements = [popoverEl]
- spyOn(popover, 'show')
+ const spy = spyOn(popover, 'show')
jQueryMock.fn.popover.call(jQueryMock, 'show')
- expect(popover.show).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
})
})
@@ -314,7 +407,7 @@ describe('Popover', () => {
const popoverEl = fixtureEl.querySelector('a')
- expect(Popover.getInstance(popoverEl)).toEqual(null)
+ expect(Popover.getInstance(popoverEl)).toBeNull()
})
})
@@ -335,7 +428,7 @@ describe('Popover', () => {
const div = fixtureEl.querySelector('div')
- expect(Popover.getInstance(div)).toEqual(null)
+ expect(Popover.getInstance(div)).toBeNull()
expect(Popover.getOrCreateInstance(div)).toBeInstanceOf(Popover)
})
@@ -344,7 +437,7 @@ describe('Popover', () => {
const div = fixtureEl.querySelector('div')
- expect(Popover.getInstance(div)).toEqual(null)
+ expect(Popover.getInstance(div)).toBeNull()
const popover = Popover.getOrCreateInstance(div, {
placement: 'top'
})
diff --git a/js/tests/unit/scrollspy.spec.js b/js/tests/unit/scrollspy.spec.js
index f64b8f1dc..fc44471c4 100644
--- a/js/tests/unit/scrollspy.spec.js
+++ b/js/tests/unit/scrollspy.spec.js
@@ -1,28 +1,71 @@
-import ScrollSpy from '../../src/scrollspy'
-import Manipulator from '../../src/dom/manipulator'
-import { getFixture, clearFixture, createEvent, jQueryMock } from '../helpers/fixture'
+import EventHandler from '../../src/dom/event-handler.js'
+import ScrollSpy from '../../src/scrollspy.js'
+import {
+ clearFixture, createEvent, getFixture, jQueryMock
+} from '../helpers/fixture.js'
describe('ScrollSpy', () => {
let fixtureEl
- const testElementIsActiveAfterScroll = ({ elementSelector, targetSelector, contentEl, scrollSpy, spy, cb }) => {
+ const getElementScrollSpy = element => element.scrollTo ?
+ spyOn(element, 'scrollTo').and.callThrough() :
+ spyOnProperty(element, 'scrollTop', 'set').and.callThrough()
+
+ const scrollTo = (el, height) => {
+ el.scrollTop = height
+ }
+
+ const onScrollStop = (callback, element, timeout = 30) => {
+ let handle = null
+ const onScroll = function () {
+ if (handle) {
+ window.clearTimeout(handle)
+ }
+
+ handle = setTimeout(() => {
+ element.removeEventListener('scroll', onScroll)
+ callback()
+ }, timeout + 1)
+ }
+
+ element.addEventListener('scroll', onScroll)
+ }
+
+ const getDummyFixture = () => {
+ return [
+ '<nav id="navBar" class="navbar">',
+ ' <ul class="nav">',
+ ' <li class="nav-item"><a id="li-jsm-1" class="nav-link" href="#div-jsm-1">div 1</a></li>',
+ ' </ul>',
+ '</nav>',
+ '<div class="content" data-bs-target="#navBar" style="overflow-y: auto">',
+ ' <div id="div-jsm-1">div 1</div>',
+ '</div>'
+ ].join('')
+ }
+
+ const testElementIsActiveAfterScroll = ({ elementSelector, targetSelector, contentEl, scrollSpy, cb }) => {
const element = fixtureEl.querySelector(elementSelector)
const target = fixtureEl.querySelector(targetSelector)
-
// add top padding to fix Chrome on Android failures
- const paddingTop = 5
- const scrollHeight = Math.ceil(contentEl.scrollTop + Manipulator.position(target).top) + paddingTop
-
- function listener() {
- expect(element.classList.contains('active')).toEqual(true)
- contentEl.removeEventListener('scroll', listener)
- expect(scrollSpy._process).toHaveBeenCalled()
- spy.calls.reset()
+ const paddingTop = 0
+ const parentOffset = getComputedStyle(contentEl).getPropertyValue('position') === 'relative' ? 0 : contentEl.offsetTop
+ const scrollHeight = (target.offsetTop - parentOffset) + paddingTop
+
+ contentEl.addEventListener('activate.bs.scrollspy', event => {
+ if (scrollSpy._activeTarget !== element) {
+ return
+ }
+
+ expect(element).toHaveClass('active')
+ expect(scrollSpy._activeTarget).toEqual(element)
+ expect(event.relatedTarget).toEqual(element)
cb()
- }
+ })
- contentEl.addEventListener('scroll', listener)
- contentEl.scrollTop = scrollHeight
+ setTimeout(() => { // in case we scroll something before the test
+ scrollTo(contentEl, scrollHeight)
+ }, 100)
}
beforeAll(() => {
@@ -53,28 +96,25 @@ describe('ScrollSpy', () => {
describe('constructor', () => {
it('should take care of element either passed as a CSS selector or DOM element', () => {
- fixtureEl.innerHTML = '<nav id="navigation"></nav><div class="content"></div>'
+ fixtureEl.innerHTML = getDummyFixture()
- const sSpyEl = fixtureEl.querySelector('#navigation')
- const sSpyBySelector = new ScrollSpy('#navigation')
+ const sSpyEl = fixtureEl.querySelector('.content')
+ const sSpyBySelector = new ScrollSpy('.content')
const sSpyByElement = new ScrollSpy(sSpyEl)
expect(sSpyBySelector._element).toEqual(sSpyEl)
expect(sSpyByElement._element).toEqual(sSpyEl)
})
- it('should not process element without target', () => {
+ it('should null, if element is not scrollable', () => {
fixtureEl.innerHTML = [
'<nav id="navigation" class="navbar">',
- ' <ul class="navbar-nav">',
- ' <li class="nav-item"><a class="nav-link active" id="one-link" href="#">One</a></li>',
- ' <li class="nav-item"><a class="nav-link" id="two-link" href="#two">Two</a></li>',
- ' <li class="nav-item"><a class="nav-link" id="three-link" href="#three">Three</a></li>',
+ ' <ul class="navbar-nav">' +
+ ' <li class="nav-item"><a class="nav-link active" id="one-link" href="#">One</a></li>' +
' </ul>',
'</nav>',
- '<div id="content" style="height: 200px; overflow-y: auto;">',
- ' <div id="two" style="height: 300px;"></div>',
- ' <div id="three" style="height: 10px;"></div>',
+ '<div id="content">',
+ ' <div id="1" style="height: 300px;">test</div>',
'</div>'
].join('')
@@ -82,533 +122,567 @@ describe('ScrollSpy', () => {
target: '#navigation'
})
- expect(scrollSpy._targets.length).toEqual(2)
+ expect(scrollSpy._observer.root).toBeNull()
+ expect(scrollSpy._rootElement).toBeNull()
})
- it('should only switch "active" class on current target', done => {
+ it('should respect threshold option', () => {
fixtureEl.innerHTML = [
- '<div id="root" class="active" style="display: block">',
- ' <div class="topbar">',
- ' <div class="topbar-inner">',
- ' <div class="container" id="ss-target">',
- ' <ul class="nav">',
- ' <li class="nav-item"><a href="#masthead">Overview</a></li>',
- ' <li class="nav-item"><a href="#detail">Detail</a></li>',
- ' </ul>',
- ' </div>',
- ' </div>',
- ' </div>',
- ' <div id="scrollspy-example" style="height: 100px; overflow: auto;">',
- ' <div style="height: 200px;">',
- ' <h4 id="masthead">Overview</h4>',
- ' <p style="height: 200px;"></p>',
- ' </div>',
- ' <div style="height: 200px;">',
- ' <h4 id="detail">Detail</h4>',
- ' <p style="height: 200px;"></p>',
- ' </div>',
- ' </div>',
+ '<ul id="navigation" class="navbar">',
+ ' <a class="nav-link active" id="one-link" href="#">One</a>' +
+ '</ul>',
+ '<div id="content">',
+ ' <div id="one-link">test</div>',
'</div>'
].join('')
- const scrollSpyEl = fixtureEl.querySelector('#scrollspy-example')
- const rootEl = fixtureEl.querySelector('#root')
- const scrollSpy = new ScrollSpy(scrollSpyEl, {
- target: 'ss-target'
- })
-
- spyOn(scrollSpy, '_process').and.callThrough()
-
- scrollSpyEl.addEventListener('scroll', () => {
- expect(rootEl.classList.contains('active')).toEqual(true)
- expect(scrollSpy._process).toHaveBeenCalled()
- done()
+ const scrollSpy = new ScrollSpy('#content', {
+ target: '#navigation',
+ threshold: [1]
})
- scrollSpyEl.scrollTop = 350
+ expect(scrollSpy._observer.thresholds).toEqual([1])
})
- it('should only switch "active" class on current target specified w element', done => {
+ it('should respect threshold option markup', () => {
fixtureEl.innerHTML = [
- '<div id="root" class="active" style="display: block">',
- ' <div class="topbar">',
- ' <div class="topbar-inner">',
- ' <div class="container" id="ss-target">',
- ' <ul class="nav">',
- ' <li class="nav-item"><a href="#masthead">Overview</a></li>',
- ' <li class="nav-item"><a href="#detail">Detail</a></li>',
- ' </ul>',
- ' </div>',
- ' </div>',
- ' </div>',
- ' <div id="scrollspy-example" style="height: 100px; overflow: auto;">',
- ' <div style="height: 200px;">',
- ' <h4 id="masthead">Overview</h4>',
- ' <p style="height: 200px;"></p>',
- ' </div>',
- ' <div style="height: 200px;">',
- ' <h4 id="detail">Detail</h4>',
- ' <p style="height: 200px;"></p>',
- ' </div>',
- ' </div>',
+ '<ul id="navigation" class="navbar">',
+ ' <a class="nav-link active" id="one-link" href="#">One</a>' +
+ '</ul>',
+ '<div id="content" data-bs-threshold="0,0.2,1">',
+ ' <div id="one-link">test</div>',
'</div>'
].join('')
- const scrollSpyEl = fixtureEl.querySelector('#scrollspy-example')
- const rootEl = fixtureEl.querySelector('#root')
- const scrollSpy = new ScrollSpy(scrollSpyEl, {
- target: fixtureEl.querySelector('#ss-target')
+ const scrollSpy = new ScrollSpy('#content', {
+ target: '#navigation'
})
- spyOn(scrollSpy, '_process').and.callThrough()
-
- scrollSpyEl.addEventListener('scroll', () => {
- expect(rootEl.classList.contains('active')).toEqual(true)
- expect(scrollSpy._process).toHaveBeenCalled()
- done()
- })
+ // See https://stackoverflow.com/a/45592926
+ const expectToBeCloseToArray = (actual, expected) => {
+ expect(actual.length).toBe(expected.length)
+ for (const x of actual) {
+ const i = actual.indexOf(x)
+ expect(x).withContext(`[${i}]`).toBeCloseTo(expected[i])
+ }
+ }
- scrollSpyEl.scrollTop = 350
+ expectToBeCloseToArray(scrollSpy._observer.thresholds, [0, 0.2, 1])
})
- it('should correctly select middle navigation option when large offset is used', done => {
+ it('should not take count to not visible sections', () => {
fixtureEl.innerHTML = [
- '<div id="header" style="height: 500px;"></div>',
'<nav id="navigation" class="navbar">',
- ' <ul class="navbar-nav">',
- ' <li class="nav-item"><a class="nav-link active" id="one-link" href="#one">One</a></li>',
- ' <li class="nav-item"><a class="nav-link" id="two-link" href="#two">Two</a></li>',
- ' <li class="nav-item"><a class="nav-link" id="three-link" href="#three">Three</a></li>',
- ' </ul>',
+ ' <ul class="navbar-nav">',
+ ' <li class="nav-item"><a class="nav-link active" id="one-link" href="#one">One</a></li>',
+ ' <li class="nav-item"><a class="nav-link" id="two-link" href="#two">Two</a></li>',
+ ' <li class="nav-item"><a class="nav-link" id="three-link" href="#three">Three</a></li>',
+ ' </ul>',
'</nav>',
'<div id="content" style="height: 200px; overflow-y: auto;">',
- ' <div id="one" style="height: 500px;"></div>',
- ' <div id="two" style="height: 300px;"></div>',
- ' <div id="three" style="height: 10px;"></div>',
+ ' <div id="one" style="height: 300px;">test</div>',
+ ' <div id="two" hidden style="height: 300px;">test</div>',
+ ' <div id="three" style="display: none;">test</div>',
'</div>'
].join('')
- const contentEl = fixtureEl.querySelector('#content')
- const scrollSpy = new ScrollSpy(contentEl, {
- target: '#navigation',
- offset: Manipulator.position(contentEl).top
- })
-
- spyOn(scrollSpy, '_process').and.callThrough()
-
- contentEl.addEventListener('scroll', () => {
- expect(fixtureEl.querySelector('#one-link').classList.contains('active')).toEqual(false)
- expect(fixtureEl.querySelector('#two-link').classList.contains('active')).toEqual(true)
- expect(fixtureEl.querySelector('#three-link').classList.contains('active')).toEqual(false)
- expect(scrollSpy._process).toHaveBeenCalled()
- done()
+ const scrollSpy = new ScrollSpy(fixtureEl.querySelector('#content'), {
+ target: '#navigation'
})
- contentEl.scrollTop = 550
+ expect(scrollSpy._observableSections.size).toBe(1)
+ expect(scrollSpy._targetLinks.size).toBe(1)
})
- it('should add the active class to the correct element', done => {
+ it('should not process element without target', () => {
fixtureEl.innerHTML = [
- '<nav class="navbar">',
- ' <ul class="nav">',
- ' <li class="nav-item"><a class="nav-link" id="a-1" href="#div-1">div 1</a></li>',
- ' <li class="nav-item"><a class="nav-link" id="a-2" href="#div-2">div 2</a></li>',
+ '<nav id="navigation" class="navbar">',
+ ' <ul class="navbar-nav">',
+ ' <li class="nav-item"><a class="nav-link active" id="one-link" href="#">One</a></li>',
+ ' <li class="nav-item"><a class="nav-link" id="two-link" href="#two">Two</a></li>',
+ ' <li class="nav-item"><a class="nav-link" id="three-link" href="#three">Three</a></li>',
' </ul>',
'</nav>',
- '<div class="content" style="overflow: auto; height: 50px">',
- ' <div id="div-1" style="height: 100px; padding: 0; margin: 0">div 1</div>',
- ' <div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>',
+ '<div id="content" style="height: 200px; overflow-y: auto;">',
+ ' <div id="two" style="height: 300px;">test</div>',
+ ' <div id="three" style="height: 10px;">test2</div>',
'</div>'
].join('')
- const contentEl = fixtureEl.querySelector('.content')
- const scrollSpy = new ScrollSpy(contentEl, {
- offset: 0,
- target: '.navbar'
- })
- const spy = spyOn(scrollSpy, '_process').and.callThrough()
-
- testElementIsActiveAfterScroll({
- elementSelector: '#a-1',
- targetSelector: '#div-1',
- contentEl,
- scrollSpy,
- spy,
- cb: () => {
- testElementIsActiveAfterScroll({
- elementSelector: '#a-2',
- targetSelector: '#div-2',
- contentEl,
- scrollSpy,
- spy,
- cb: () => done()
- })
- }
+ const scrollSpy = new ScrollSpy(fixtureEl.querySelector('#content'), {
+ target: '#navigation'
})
- })
- it('should add the active class to the correct element (nav markup)', done => {
- fixtureEl.innerHTML = [
- '<nav class="navbar">',
- ' <nav class="nav">',
- ' <a class="nav-link" id="a-1" href="#div-1">div 1</a>',
- ' <a class="nav-link" id="a-2" href="#div-2">div 2</a>',
- ' </nav>',
- '</nav>',
- '<div class="content" style="overflow: auto; height: 50px">',
- ' <div id="div-1" style="height: 100px; padding: 0; margin: 0">div 1</div>',
- ' <div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>',
- '</div>'
- ].join('')
+ expect(scrollSpy._targetLinks).toHaveSize(2)
+ })
- const contentEl = fixtureEl.querySelector('.content')
- const scrollSpy = new ScrollSpy(contentEl, {
- offset: 0,
- target: '.navbar'
- })
- const spy = spyOn(scrollSpy, '_process').and.callThrough()
-
- testElementIsActiveAfterScroll({
- elementSelector: '#a-1',
- targetSelector: '#div-1',
- contentEl,
- scrollSpy,
- spy,
- cb: () => {
- testElementIsActiveAfterScroll({
- elementSelector: '#a-2',
- targetSelector: '#div-2',
- contentEl,
- scrollSpy,
- spy,
- cb: () => done()
- })
- }
+ it('should only switch "active" class on current target', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="root" class="active" style="display: block">',
+ ' <div class="topbar">',
+ ' <div class="topbar-inner">',
+ ' <div class="container" id="ss-target">',
+ ' <ul class="nav">',
+ ' <li class="nav-item"><a href="#masthead">Overview</a></li>',
+ ' <li class="nav-item"><a href="#detail">Detail</a></li>',
+ ' </ul>',
+ ' </div>',
+ ' </div>',
+ ' </div>',
+ ' <div id="scrollspy-example" style="height: 100px; overflow: auto;">',
+ ' <div style="height: 200px;" id="masthead">Overview</div>',
+ ' <div style="height: 200px;" id="detail">Detail</div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const scrollSpyEl = fixtureEl.querySelector('#scrollspy-example')
+ const rootEl = fixtureEl.querySelector('#root')
+ const scrollSpy = new ScrollSpy(scrollSpyEl, {
+ target: 'ss-target'
+ })
+
+ const spy = spyOn(scrollSpy, '_process').and.callThrough()
+
+ onScrollStop(() => {
+ expect(rootEl).toHaveClass('active')
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ }, scrollSpyEl)
+
+ scrollTo(scrollSpyEl, 350)
})
})
- it('should add the active class to the correct element (list-group markup)', done => {
- fixtureEl.innerHTML = [
- '<nav class="navbar">',
- ' <div class="list-group">',
- ' <a class="list-group-item" id="a-1" href="#div-1">div 1</a>',
- ' <a class="list-group-item" id="a-2" href="#div-2">div 2</a>',
- ' </div>',
- '</nav>',
- '<div class="content" style="overflow: auto; height: 50px">',
- ' <div id="div-1" style="height: 100px; padding: 0; margin: 0">div 1</div>',
- ' <div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>',
- '</div>'
- ].join('')
-
- const contentEl = fixtureEl.querySelector('.content')
- const scrollSpy = new ScrollSpy(contentEl, {
- offset: 0,
- target: '.navbar'
- })
- const spy = spyOn(scrollSpy, '_process').and.callThrough()
-
- testElementIsActiveAfterScroll({
- elementSelector: '#a-1',
- targetSelector: '#div-1',
- contentEl,
- scrollSpy,
- spy,
- cb: () => {
- testElementIsActiveAfterScroll({
- elementSelector: '#a-2',
- targetSelector: '#div-2',
- contentEl,
- scrollSpy,
- spy,
- cb: () => done()
- })
- }
+ it('should not process data if `activeTarget` is same as given target', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<nav class="navbar">',
+ ' <ul class="nav">',
+ ' <li class="nav-item"><a class="nav-link" id="a-1" href="#div-1">div 1</a></li>',
+ ' <li class="nav-item"><a class="nav-link" id="a-2" href="#div-2">div 2</a></li>',
+ ' </ul>',
+ '</nav>',
+ '<div class="content" style="overflow: auto; height: 50px">',
+ ' <div id="div-1" style="height: 100px; padding: 0; margin: 0">div 1</div>',
+ ' <div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>',
+ '</div>'
+ ].join('')
+
+ const contentEl = fixtureEl.querySelector('.content')
+ const scrollSpy = new ScrollSpy(contentEl, {
+ offset: 0,
+ target: '.navbar'
+ })
+
+ const triggerSpy = spyOn(EventHandler, 'trigger').and.callThrough()
+
+ scrollSpy._activeTarget = fixtureEl.querySelector('#a-1')
+ testElementIsActiveAfterScroll({
+ elementSelector: '#a-1',
+ targetSelector: '#div-1',
+ contentEl,
+ scrollSpy,
+ cb: reject
+ })
+
+ setTimeout(() => {
+ expect(triggerSpy).not.toHaveBeenCalled()
+ resolve()
+ }, 100)
})
})
- it('should clear selection if above the first section', done => {
- fixtureEl.innerHTML = [
- '<div id="header" style="height: 500px;"></div>',
- '<nav id="navigation" class="navbar">',
- ' <ul class="navbar-nav">',
- ' <li class="nav-item"><a id="one-link" class="nav-link active" href="#one">One</a></li>',
- ' <li class="nav-item"><a id="two-link" class="nav-link" href="#two">Two</a></li>',
- ' <li class="nav-item"><a id="three-link" class="nav-link" href="#three">Three</a></li>',
- ' </ul>',
- '</nav>',
- '<div id="content" style="height: 200px; overflow-y: auto;">',
- ' <div id="spacer" style="height: 100px;"></div>',
- ' <div id="one" style="height: 100px;"></div>',
- ' <div id="two" style="height: 100px;"></div>',
- ' <div id="three" style="height: 100px;"></div>',
- ' <div id="spacer" style="height: 100px;"></div>',
- '</div>'
- ].join('')
-
- const contentEl = fixtureEl.querySelector('#content')
- const scrollSpy = new ScrollSpy(contentEl, {
- target: '#navigation',
- offset: Manipulator.position(contentEl).top
+ it('should only switch "active" class on current target specified w element', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="root" class="active" style="display: block">',
+ ' <div class="topbar">',
+ ' <div class="topbar-inner">',
+ ' <div class="container" id="ss-target">',
+ ' <ul class="nav">',
+ ' <li class="nav-item"><a href="#masthead">Overview</a></li>',
+ ' <li class="nav-item"><a href="#detail">Detail</a></li>',
+ ' </ul>',
+ ' </div>',
+ ' </div>',
+ ' </div>',
+ ' <div id="scrollspy-example" style="height: 100px; overflow: auto;">',
+ ' <div style="height: 200px;" id="masthead">Overview</div>',
+ ' <div style="height: 200px;" id="detail">Detail</div>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const scrollSpyEl = fixtureEl.querySelector('#scrollspy-example')
+ const rootEl = fixtureEl.querySelector('#root')
+ const scrollSpy = new ScrollSpy(scrollSpyEl, {
+ target: fixtureEl.querySelector('#ss-target')
+ })
+
+ const spy = spyOn(scrollSpy, '_process').and.callThrough()
+
+ onScrollStop(() => {
+ expect(rootEl).toHaveClass('active')
+ expect(scrollSpy._activeTarget).toEqual(fixtureEl.querySelector('[href="#detail"]'))
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ }, scrollSpyEl)
+
+ scrollTo(scrollSpyEl, 350)
})
- const spy = spyOn(scrollSpy, '_process').and.callThrough()
-
- let firstTime = true
-
- contentEl.addEventListener('scroll', () => {
- const active = fixtureEl.querySelector('.active')
+ })
- expect(spy).toHaveBeenCalled()
- spy.calls.reset()
- if (firstTime) {
- expect(fixtureEl.querySelectorAll('.active').length).toEqual(1)
- expect(active.getAttribute('id')).toEqual('two-link')
- firstTime = false
- contentEl.scrollTop = 0
- } else {
- expect(active).toBeNull()
- done()
- }
+ it('should add the active class to the correct element', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<nav class="navbar">',
+ ' <ul class="nav">',
+ ' <li class="nav-item"><a class="nav-link" id="a-1" href="#div-1">div 1</a></li>',
+ ' <li class="nav-item"><a class="nav-link" id="a-2" href="#div-2">div 2</a></li>',
+ ' </ul>',
+ '</nav>',
+ '<div class="content" style="overflow: auto; height: 50px">',
+ ' <div id="div-1" style="height: 100px; padding: 0; margin: 0">div 1</div>',
+ ' <div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>',
+ '</div>'
+ ].join('')
+
+ const contentEl = fixtureEl.querySelector('.content')
+ const scrollSpy = new ScrollSpy(contentEl, {
+ offset: 0,
+ target: '.navbar'
+ })
+
+ testElementIsActiveAfterScroll({
+ elementSelector: '#a-1',
+ targetSelector: '#div-1',
+ contentEl,
+ scrollSpy,
+ cb() {
+ testElementIsActiveAfterScroll({
+ elementSelector: '#a-2',
+ targetSelector: '#div-2',
+ contentEl,
+ scrollSpy,
+ cb: resolve
+ })
+ }
+ })
})
-
- contentEl.scrollTop = 201
})
- it('should not clear selection if above the first section and first section is at the top', done => {
- fixtureEl.innerHTML = [
- '<div id="header" style="height: 500px;"></div>',
- '<nav id="navigation" class="navbar">',
- ' <ul class="navbar-nav">',
- ' <li class="nav-item"><a id="one-link" class="nav-link active" href="#one">One</a></li>',
- ' <li class="nav-item"><a id="two-link" class="nav-link" href="#two">Two</a></li>',
- ' <li class="nav-item"><a id="three-link" class="nav-link" href="#three">Three</a></li>',
- ' </ul>',
- '</nav>',
- '<div id="content" style="height: 200px; overflow-y: auto;">',
- ' <div id="one" style="height: 100px;"></div>',
- ' <div id="two" style="height: 100px;"></div>',
- ' <div id="three" style="height: 100px;"></div>',
- ' <div id="spacer" style="height: 100px;"></div>',
- '</div>'
- ].join('')
-
- const negativeHeight = -10
- const startOfSectionTwo = 101
- const contentEl = fixtureEl.querySelector('#content')
- const scrollSpy = new ScrollSpy(contentEl, {
- target: '#navigation',
- offset: contentEl.offsetTop
+ it('should add to nav the active class to the correct element (nav markup)', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<nav class="navbar">',
+ ' <nav class="nav">',
+ ' <a class="nav-link" id="a-1" href="#div-1">div 1</a>',
+ ' <a class="nav-link" id="a-2" href="#div-2">div 2</a>',
+ ' </nav>',
+ '</nav>',
+ '<div class="content" style="overflow: auto; height: 50px">',
+ ' <div id="div-1" style="height: 100px; padding: 0; margin: 0">div 1</div>',
+ ' <div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>',
+ '</div>'
+ ].join('')
+
+ const contentEl = fixtureEl.querySelector('.content')
+ const scrollSpy = new ScrollSpy(contentEl, {
+ offset: 0,
+ target: '.navbar'
+ })
+
+ testElementIsActiveAfterScroll({
+ elementSelector: '#a-1',
+ targetSelector: '#div-1',
+ contentEl,
+ scrollSpy,
+ cb() {
+ testElementIsActiveAfterScroll({
+ elementSelector: '#a-2',
+ targetSelector: '#div-2',
+ contentEl,
+ scrollSpy,
+ cb: resolve
+ })
+ }
+ })
})
- const spy = spyOn(scrollSpy, '_process').and.callThrough()
-
- let firstTime = true
-
- contentEl.addEventListener('scroll', () => {
- const active = fixtureEl.querySelector('.active')
+ })
- expect(spy).toHaveBeenCalled()
- spy.calls.reset()
- if (firstTime) {
- expect(fixtureEl.querySelectorAll('.active').length).toEqual(1)
- expect(active.getAttribute('id')).toEqual('two-link')
- firstTime = false
- contentEl.scrollTop = negativeHeight
- } else {
- expect(fixtureEl.querySelectorAll('.active').length).toEqual(1)
- expect(active.getAttribute('id')).toEqual('one-link')
- done()
- }
+ it('should add to list-group, the active class to the correct element (list-group markup)', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<nav class="navbar">',
+ ' <div class="list-group">',
+ ' <a class="list-group-item" id="a-1" href="#div-1">div 1</a>',
+ ' <a class="list-group-item" id="a-2" href="#div-2">div 2</a>',
+ ' </div>',
+ '</nav>',
+ '<div class="content" style="overflow: auto; height: 50px">',
+ ' <div id="div-1" style="height: 100px; padding: 0; margin: 0">div 1</div>',
+ ' <div id="div-2" style="height: 200px; padding: 0; margin: 0">div 2</div>',
+ '</div>'
+ ].join('')
+
+ const contentEl = fixtureEl.querySelector('.content')
+ const scrollSpy = new ScrollSpy(contentEl, {
+ offset: 0,
+ target: '.navbar'
+ })
+
+ testElementIsActiveAfterScroll({
+ elementSelector: '#a-1',
+ targetSelector: '#div-1',
+ contentEl,
+ scrollSpy,
+ cb() {
+ testElementIsActiveAfterScroll({
+ elementSelector: '#a-2',
+ targetSelector: '#div-2',
+ contentEl,
+ scrollSpy,
+ cb: resolve
+ })
+ }
+ })
})
-
- contentEl.scrollTop = startOfSectionTwo
})
- it('should correctly select navigation element on backward scrolling when each target section height is 100%', done => {
- fixtureEl.innerHTML = [
- '<nav class="navbar">',
- ' <ul class="nav">',
- ' <li class="nav-item"><a id="li-100-1" class="nav-link" href="#div-100-1">div 1</a></li>',
- ' <li class="nav-item"><a id="li-100-2" class="nav-link" href="#div-100-2">div 2</a></li>',
- ' <li class="nav-item"><a id="li-100-3" class="nav-link" href="#div-100-3">div 3</a></li>',
- ' <li class="nav-item"><a id="li-100-4" class="nav-link" href="#div-100-4">div 4</a></li>',
- ' <li class="nav-item"><a id="li-100-5" class="nav-link" href="#div-100-5">div 5</a></li>',
- ' </ul>',
- '</nav>',
- '<div class="content" style="position: relative; overflow: auto; height: 100px">',
- ' <div id="div-100-1" style="position: relative; height: 100%; padding: 0; margin: 0">div 1</div>',
- ' <div id="div-100-2" style="position: relative; height: 100%; padding: 0; margin: 0">div 2</div>',
- ' <div id="div-100-3" style="position: relative; height: 100%; padding: 0; margin: 0">div 3</div>',
- ' <div id="div-100-4" style="position: relative; height: 100%; padding: 0; margin: 0">div 4</div>',
- ' <div id="div-100-5" style="position: relative; height: 100%; padding: 0; margin: 0">div 5</div>',
- '</div>'
- ].join('')
-
- const contentEl = fixtureEl.querySelector('.content')
- const scrollSpy = new ScrollSpy(contentEl, {
- offset: 0,
- target: '.navbar'
- })
- const spy = spyOn(scrollSpy, '_process').and.callThrough()
-
- testElementIsActiveAfterScroll({
- elementSelector: '#li-100-5',
- targetSelector: '#div-100-5',
- scrollSpy,
- spy,
- contentEl,
- cb() {
- contentEl.scrollTop = 0
- testElementIsActiveAfterScroll({
- elementSelector: '#li-100-4',
- targetSelector: '#div-100-4',
- scrollSpy,
- spy,
- contentEl,
- cb() {
- contentEl.scrollTop = 0
- testElementIsActiveAfterScroll({
- elementSelector: '#li-100-3',
- targetSelector: '#div-100-3',
- scrollSpy,
- spy,
- contentEl,
- cb() {
- contentEl.scrollTop = 0
- testElementIsActiveAfterScroll({
- elementSelector: '#li-100-2',
- targetSelector: '#div-100-2',
- scrollSpy,
- spy,
- contentEl,
- cb() {
- contentEl.scrollTop = 0
- testElementIsActiveAfterScroll({
- elementSelector: '#li-100-1',
- targetSelector: '#div-100-1',
- scrollSpy,
- spy,
- contentEl,
- cb: done
- })
- }
- })
- }
- })
- }
- })
- }
+ it('should clear selection if above the first section', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="header" style="height: 500px;"></div>',
+ '<nav id="navigation" class="navbar">',
+ ' <ul class="navbar-nav">',
+ ' <li class="nav-item"><a id="one-link" class="nav-link active" href="#one">One</a></li>',
+ ' <li class="nav-item"><a id="two-link" class="nav-link" href="#two">Two</a></li>',
+ ' <li class="nav-item"><a id="three-link" class="nav-link" href="#three">Three</a></li>',
+ ' </ul>',
+ '</nav>',
+ '<div id="content" style="height: 200px; overflow-y: auto;">',
+ ' <div id="spacer" style="height: 200px;"></div>',
+ ' <div id="one" style="height: 100px;">text</div>',
+ ' <div id="two" style="height: 100px;">text</div>',
+ ' <div id="three" style="height: 100px;">text</div>',
+ ' <div id="spacer" style="height: 100px;"></div>',
+ '</div>'
+ ].join('')
+
+ const contentEl = fixtureEl.querySelector('#content')
+ const scrollSpy = new ScrollSpy(contentEl, {
+ target: '#navigation',
+ offset: contentEl.offsetTop
+ })
+ const spy = spyOn(scrollSpy, '_process').and.callThrough()
+
+ onScrollStop(() => {
+ const active = () => fixtureEl.querySelector('.active')
+ expect(spy).toHaveBeenCalled()
+
+ expect(fixtureEl.querySelectorAll('.active')).toHaveSize(1)
+ expect(active().getAttribute('id')).toEqual('two-link')
+ onScrollStop(() => {
+ expect(active()).toBeNull()
+ resolve()
+ }, contentEl)
+ scrollTo(contentEl, 0)
+ }, contentEl)
+
+ scrollTo(contentEl, 200)
})
})
- it('should allow passed in option offset method: offset', () => {
- fixtureEl.innerHTML = [
- '<nav class="navbar">',
- ' <ul class="nav">',
- ' <li class="nav-item"><a id="li-jsm-1" class="nav-link" href="#div-jsm-1">div 1</a></li>',
- ' <li class="nav-item"><a id="li-jsm-2" class="nav-link" href="#div-jsm-2">div 2</a></li>',
- ' <li class="nav-item"><a id="li-jsm-3" class="nav-link" href="#div-jsm-3">div 3</a></li>',
- ' </ul>',
- '</nav>',
- '<div class="content" style="position: relative; overflow: auto; height: 100px">',
- ' <div id="div-jsm-1" style="position: relative; height: 200px; padding: 0; margin: 0">div 1</div>',
- ' <div id="div-jsm-2" style="position: relative; height: 150px; padding: 0; margin: 0">div 2</div>',
- ' <div id="div-jsm-3" style="position: relative; height: 250px; padding: 0; margin: 0">div 3</div>',
- '</div>'
- ].join('')
-
- const contentEl = fixtureEl.querySelector('.content')
- const targetEl = fixtureEl.querySelector('#div-jsm-2')
- const scrollSpy = new ScrollSpy(contentEl, {
- target: '.navbar',
- offset: 0,
- method: 'offset'
+ it('should not clear selection if above the first section and first section is at the top', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div id="header" style="height: 500px;"></div>',
+ '<nav id="navigation" class="navbar">',
+ ' <ul class="navbar-nav">',
+ ' <li class="nav-item"><a id="one-link" class="nav-link active" href="#one">One</a></li>',
+ ' <li class="nav-item"><a id="two-link" class="nav-link" href="#two">Two</a></li>',
+ ' <li class="nav-item"><a id="three-link" class="nav-link" href="#three">Three</a></li>',
+ ' </ul>',
+ '</nav>',
+ '<div id="content" style="height: 150px; overflow-y: auto;">',
+ ' <div id="one" style="height: 100px;">test</div>',
+ ' <div id="two" style="height: 100px;">test</div>',
+ ' <div id="three" style="height: 100px;">test</div>',
+ ' <div id="spacer" style="height: 100px;">test</div>',
+ '</div>'
+ ].join('')
+
+ const negativeHeight = 0
+ const startOfSectionTwo = 101
+ const contentEl = fixtureEl.querySelector('#content')
+ // eslint-disable-next-line no-unused-vars
+ const scrollSpy = new ScrollSpy(contentEl, {
+ target: '#navigation',
+ rootMargin: '0px 0px -50%'
+ })
+
+ onScrollStop(() => {
+ const activeId = () => fixtureEl.querySelector('.active').getAttribute('id')
+
+ expect(fixtureEl.querySelectorAll('.active')).toHaveSize(1)
+ expect(activeId()).toEqual('two-link')
+ scrollTo(contentEl, negativeHeight)
+
+ onScrollStop(() => {
+ expect(fixtureEl.querySelectorAll('.active')).toHaveSize(1)
+ expect(activeId()).toEqual('one-link')
+ resolve()
+ }, contentEl)
+
+ scrollTo(contentEl, 0)
+ }, contentEl)
+
+ scrollTo(contentEl, startOfSectionTwo)
})
+ })
- expect(scrollSpy._offsets[1]).toEqual(Manipulator.offset(targetEl).top)
- expect(scrollSpy._offsets[1]).not.toEqual(Manipulator.position(targetEl).top)
+ it('should correctly select navigation element on backward scrolling when each target section height is 100%', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<nav class="navbar">',
+ ' <ul class="nav">',
+ ' <li class="nav-item"><a id="li-100-1" class="nav-link" href="#div-100-1">div 1</a></li>',
+ ' <li class="nav-item"><a id="li-100-2" class="nav-link" href="#div-100-2">div 2</a></li>',
+ ' <li class="nav-item"><a id="li-100-3" class="nav-link" href="#div-100-3">div 3</a></li>',
+ ' <li class="nav-item"><a id="li-100-4" class="nav-link" href="#div-100-4">div 4</a></li>',
+ ' <li class="nav-item"><a id="li-100-5" class="nav-link" href="#div-100-5">div 5</a></li>',
+ ' </ul>',
+ '</nav>',
+ '<div class="content" style="position: relative; overflow: auto; height: 100px">',
+ ' <div id="div-100-1" style="position: relative; height: 100%; padding: 0; margin: 0">div 1</div>',
+ ' <div id="div-100-2" style="position: relative; height: 100%; padding: 0; margin: 0">div 2</div>',
+ ' <div id="div-100-3" style="position: relative; height: 100%; padding: 0; margin: 0">div 3</div>',
+ ' <div id="div-100-4" style="position: relative; height: 100%; padding: 0; margin: 0">div 4</div>',
+ ' <div id="div-100-5" style="position: relative; height: 100%; padding: 0; margin: 0">div 5</div>',
+ '</div>'
+ ].join('')
+
+ const contentEl = fixtureEl.querySelector('.content')
+ const scrollSpy = new ScrollSpy(contentEl, {
+ offset: 0,
+ target: '.navbar'
+ })
+
+ scrollTo(contentEl, 0)
+ testElementIsActiveAfterScroll({
+ elementSelector: '#li-100-5',
+ targetSelector: '#div-100-5',
+ contentEl,
+ scrollSpy,
+ cb() {
+ scrollTo(contentEl, 0)
+ testElementIsActiveAfterScroll({
+ elementSelector: '#li-100-2',
+ targetSelector: '#div-100-2',
+ contentEl,
+ scrollSpy,
+ cb() {
+ scrollTo(contentEl, 0)
+ testElementIsActiveAfterScroll({
+ elementSelector: '#li-100-3',
+ targetSelector: '#div-100-3',
+ contentEl,
+ scrollSpy,
+ cb() {
+ scrollTo(contentEl, 0)
+ testElementIsActiveAfterScroll({
+ elementSelector: '#li-100-2',
+ targetSelector: '#div-100-2',
+ contentEl,
+ scrollSpy,
+ cb() {
+ scrollTo(contentEl, 0)
+ testElementIsActiveAfterScroll({
+ elementSelector: '#li-100-1',
+ targetSelector: '#div-100-1',
+ contentEl,
+ scrollSpy,
+ cb: resolve
+ })
+ }
+ })
+ }
+ })
+ }
+ })
+ }
+ })
+ })
})
+ })
- it('should allow passed in option offset method: position', () => {
- fixtureEl.innerHTML = [
- '<nav class="navbar">',
- ' <ul class="nav">',
- ' <li class="nav-item"><a id="li-jsm-1" class="nav-link" href="#div-jsm-1">div 1</a></li>',
- ' <li class="nav-item"><a id="li-jsm-2" class="nav-link" href="#div-jsm-2">div 2</a></li>',
- ' <li class="nav-item"><a id="li-jsm-3" class="nav-link" href="#div-jsm-3">div 3</a></li>',
- ' </ul>',
- '</nav>',
- '<div class="content" style="position: relative; overflow: auto; height: 100px">',
- ' <div id="div-jsm-1" style="position: relative; height: 200px; padding: 0; margin: 0">div 1</div>',
- ' <div id="div-jsm-2" style="position: relative; height: 150px; padding: 0; margin: 0">div 2</div>',
- ' <div id="div-jsm-3" style="position: relative; height: 250px; padding: 0; margin: 0">div 3</div>',
- '</div>'
- ].join('')
+ describe('refresh', () => {
+ it('should disconnect existing observer', () => {
+ fixtureEl.innerHTML = getDummyFixture()
- const contentEl = fixtureEl.querySelector('.content')
- const targetEl = fixtureEl.querySelector('#div-jsm-2')
- const scrollSpy = new ScrollSpy(contentEl, {
- target: '.navbar',
- offset: 0,
- method: 'position'
- })
+ const el = fixtureEl.querySelector('.content')
+ const scrollSpy = new ScrollSpy(el)
+
+ const spy = spyOn(scrollSpy._observer, 'disconnect')
+
+ scrollSpy.refresh()
- expect(scrollSpy._offsets[1]).not.toEqual(Manipulator.offset(targetEl).top)
- expect(scrollSpy._offsets[1]).toEqual(Manipulator.position(targetEl).top)
+ expect(spy).toHaveBeenCalled()
})
})
describe('dispose', () => {
it('should dispose a scrollspy', () => {
- fixtureEl.innerHTML = '<div style="display: none;"></div>'
+ fixtureEl.innerHTML = getDummyFixture()
- const divEl = fixtureEl.querySelector('div')
- spyOn(divEl, 'addEventListener').and.callThrough()
- spyOn(divEl, 'removeEventListener').and.callThrough()
+ const el = fixtureEl.querySelector('.content')
+ const scrollSpy = new ScrollSpy(el)
- const scrollSpy = new ScrollSpy(divEl)
- expect(divEl.addEventListener).toHaveBeenCalledWith('scroll', jasmine.any(Function), jasmine.any(Boolean))
+ expect(ScrollSpy.getInstance(el)).not.toBeNull()
scrollSpy.dispose()
- expect(divEl.removeEventListener).toHaveBeenCalledWith('scroll', jasmine.any(Function), jasmine.any(Boolean))
+ expect(ScrollSpy.getInstance(el)).toBeNull()
})
})
describe('jQueryInterface', () => {
it('should create a scrollspy', () => {
- fixtureEl.innerHTML = '<div></div>'
+ fixtureEl.innerHTML = getDummyFixture()
- const div = fixtureEl.querySelector('div')
+ const div = fixtureEl.querySelector('.content')
jQueryMock.fn.scrollspy = ScrollSpy.jQueryInterface
jQueryMock.elements = [div]
- jQueryMock.fn.scrollspy.call(jQueryMock)
+ jQueryMock.fn.scrollspy.call(jQueryMock, { target: '#navBar' })
expect(ScrollSpy.getInstance(div)).not.toBeNull()
})
it('should create a scrollspy with given config', () => {
- fixtureEl.innerHTML = '<div></div>'
+ fixtureEl.innerHTML = getDummyFixture()
- const div = fixtureEl.querySelector('div')
+ const div = fixtureEl.querySelector('.content')
jQueryMock.fn.scrollspy = ScrollSpy.jQueryInterface
jQueryMock.elements = [div]
- jQueryMock.fn.scrollspy.call(jQueryMock, { offset: 15 })
- spyOn(ScrollSpy.prototype, 'constructor')
- expect(ScrollSpy.prototype.constructor).not.toHaveBeenCalledWith(div, { offset: 15 })
+ jQueryMock.fn.scrollspy.call(jQueryMock, { rootMargin: '100px' })
+ const spy = spyOn(ScrollSpy.prototype, 'constructor')
+ expect(spy).not.toHaveBeenCalledWith(div, { rootMargin: '100px' })
const scrollspy = ScrollSpy.getInstance(div)
expect(scrollspy).not.toBeNull()
- expect(scrollspy._config.offset).toBe(15)
+ expect(scrollspy._config.rootMargin).toEqual('100px')
})
it('should not re create a scrollspy', () => {
- fixtureEl.innerHTML = '<div></div>'
+ fixtureEl.innerHTML = getDummyFixture()
- const div = fixtureEl.querySelector('div')
+ const div = fixtureEl.querySelector('.content')
const scrollSpy = new ScrollSpy(div)
jQueryMock.fn.scrollspy = ScrollSpy.jQueryInterface
@@ -620,12 +694,12 @@ describe('ScrollSpy', () => {
})
it('should call a scrollspy method', () => {
- fixtureEl.innerHTML = '<div></div>'
+ fixtureEl.innerHTML = getDummyFixture()
- const div = fixtureEl.querySelector('div')
+ const div = fixtureEl.querySelector('.content')
const scrollSpy = new ScrollSpy(div)
- spyOn(scrollSpy, 'refresh')
+ const spy = spyOn(scrollSpy, 'refresh')
jQueryMock.fn.scrollspy = ScrollSpy.jQueryInterface
jQueryMock.elements = [div]
@@ -633,13 +707,13 @@ describe('ScrollSpy', () => {
jQueryMock.fn.scrollspy.call(jQueryMock, 'refresh')
expect(ScrollSpy.getInstance(div)).toEqual(scrollSpy)
- expect(scrollSpy.refresh).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
})
it('should throw error on undefined method', () => {
- fixtureEl.innerHTML = '<div></div>'
+ fixtureEl.innerHTML = getDummyFixture()
- const div = fixtureEl.querySelector('div')
+ const div = fixtureEl.querySelector('.content')
const action = 'undefinedMethod'
jQueryMock.fn.scrollspy = ScrollSpy.jQueryInterface
@@ -649,29 +723,60 @@ describe('ScrollSpy', () => {
jQueryMock.fn.scrollspy.call(jQueryMock, action)
}).toThrowError(TypeError, `No method named "${action}"`)
})
+
+ it('should throw error on protected method', () => {
+ fixtureEl.innerHTML = getDummyFixture()
+
+ const div = fixtureEl.querySelector('.content')
+ const action = '_getConfig'
+
+ jQueryMock.fn.scrollspy = ScrollSpy.jQueryInterface
+ jQueryMock.elements = [div]
+
+ expect(() => {
+ jQueryMock.fn.scrollspy.call(jQueryMock, action)
+ }).toThrowError(TypeError, `No method named "${action}"`)
+ })
+
+ it('should throw error if method "constructor" is being called', () => {
+ fixtureEl.innerHTML = getDummyFixture()
+
+ const div = fixtureEl.querySelector('.content')
+ const action = 'constructor'
+
+ jQueryMock.fn.scrollspy = ScrollSpy.jQueryInterface
+ jQueryMock.elements = [div]
+
+ expect(() => {
+ jQueryMock.fn.scrollspy.call(jQueryMock, action)
+ }).toThrowError(TypeError, `No method named "${action}"`)
+ })
})
describe('getInstance', () => {
it('should return scrollspy instance', () => {
- fixtureEl.innerHTML = '<div></div>'
+ fixtureEl.innerHTML = getDummyFixture()
- const div = fixtureEl.querySelector('div')
- const scrollSpy = new ScrollSpy(div)
+ const div = fixtureEl.querySelector('.content')
+ const scrollSpy = new ScrollSpy(div, { target: fixtureEl.querySelector('#navBar') })
expect(ScrollSpy.getInstance(div)).toEqual(scrollSpy)
expect(ScrollSpy.getInstance(div)).toBeInstanceOf(ScrollSpy)
})
it('should return null if there is no instance', () => {
- expect(ScrollSpy.getInstance(fixtureEl)).toEqual(null)
+ fixtureEl.innerHTML = getDummyFixture()
+
+ const div = fixtureEl.querySelector('.content')
+ expect(ScrollSpy.getInstance(div)).toBeNull()
})
})
describe('getOrCreateInstance', () => {
it('should return scrollspy instance', () => {
- fixtureEl.innerHTML = '<div></div>'
+ fixtureEl.innerHTML = getDummyFixture()
- const div = fixtureEl.querySelector('div')
+ const div = fixtureEl.querySelector('.content')
const scrollspy = new ScrollSpy(div)
expect(ScrollSpy.getOrCreateInstance(div)).toEqual(scrollspy)
@@ -680,20 +785,20 @@ describe('ScrollSpy', () => {
})
it('should return new instance when there is no scrollspy instance', () => {
- fixtureEl.innerHTML = '<div></div>'
+ fixtureEl.innerHTML = getDummyFixture()
- const div = fixtureEl.querySelector('div')
+ const div = fixtureEl.querySelector('.content')
- expect(ScrollSpy.getInstance(div)).toEqual(null)
+ expect(ScrollSpy.getInstance(div)).toBeNull()
expect(ScrollSpy.getOrCreateInstance(div)).toBeInstanceOf(ScrollSpy)
})
it('should return new instance when there is no scrollspy instance with given configuration', () => {
- fixtureEl.innerHTML = '<div></div>'
+ fixtureEl.innerHTML = getDummyFixture()
- const div = fixtureEl.querySelector('div')
+ const div = fixtureEl.querySelector('.content')
- expect(ScrollSpy.getInstance(div)).toEqual(null)
+ expect(ScrollSpy.getInstance(div)).toBeNull()
const scrollspy = ScrollSpy.getOrCreateInstance(div, {
offset: 1
})
@@ -703,9 +808,9 @@ describe('ScrollSpy', () => {
})
it('should return the instance when exists without given configuration', () => {
- fixtureEl.innerHTML = '<div></div>'
+ fixtureEl.innerHTML = getDummyFixture()
- const div = fixtureEl.querySelector('div')
+ const div = fixtureEl.querySelector('.content')
const scrollspy = new ScrollSpy(div, {
offset: 1
})
@@ -723,13 +828,153 @@ describe('ScrollSpy', () => {
describe('event handler', () => {
it('should create scrollspy on window load event', () => {
- fixtureEl.innerHTML = '<div data-bs-spy="scroll"></div>'
+ fixtureEl.innerHTML = [
+ '<div id="nav"></div>' +
+ '<div id="wrapper" data-bs-spy="scroll" data-bs-target="#nav" style="overflow-y: auto"></div>'
+ ].join('')
- const scrollSpyEl = fixtureEl.querySelector('div')
+ const scrollSpyEl = fixtureEl.querySelector('#wrapper')
window.dispatchEvent(createEvent('load'))
expect(ScrollSpy.getInstance(scrollSpyEl)).not.toBeNull()
})
})
+
+ describe('SmoothScroll', () => {
+ it('should not enable smoothScroll', () => {
+ fixtureEl.innerHTML = getDummyFixture()
+ const offSpy = spyOn(EventHandler, 'off').and.callThrough()
+ const onSpy = spyOn(EventHandler, 'on').and.callThrough()
+
+ const div = fixtureEl.querySelector('.content')
+ const target = fixtureEl.querySelector('#navBar')
+ // eslint-disable-next-line no-new
+ new ScrollSpy(div, {
+ offset: 1
+ })
+
+ expect(offSpy).not.toHaveBeenCalledWith(target, 'click.bs.scrollspy')
+ expect(onSpy).not.toHaveBeenCalledWith(target, 'click.bs.scrollspy')
+ })
+
+ it('should enable smoothScroll', () => {
+ fixtureEl.innerHTML = getDummyFixture()
+ const offSpy = spyOn(EventHandler, 'off').and.callThrough()
+ const onSpy = spyOn(EventHandler, 'on').and.callThrough()
+
+ const div = fixtureEl.querySelector('.content')
+ const target = fixtureEl.querySelector('#navBar')
+ // eslint-disable-next-line no-new
+ new ScrollSpy(div, {
+ offset: 1,
+ smoothScroll: true
+ })
+
+ expect(offSpy).toHaveBeenCalledWith(target, 'click.bs.scrollspy')
+ expect(onSpy).toHaveBeenCalledWith(target, 'click.bs.scrollspy', '[href]', jasmine.any(Function))
+ })
+
+ it('should not smoothScroll to element if it not handles a scrollspy section', () => {
+ fixtureEl.innerHTML = [
+ '<nav id="navBar" class="navbar">',
+ ' <ul class="nav">',
+ ' <a id="anchor-1" href="#div-jsm-1">div 1</a></li>',
+ ' <a id="anchor-2" href="#foo">div 2</a></li>',
+ ' </ul>',
+ '</nav>',
+ '<div class="content" data-bs-target="#navBar" style="overflow-y: auto">',
+ ' <div id="div-jsm-1">div 1</div>',
+ '</div>'
+ ].join('')
+
+ const div = fixtureEl.querySelector('.content')
+ // eslint-disable-next-line no-new
+ new ScrollSpy(div, {
+ offset: 1,
+ smoothScroll: true
+ })
+
+ const clickSpy = getElementScrollSpy(div)
+
+ fixtureEl.querySelector('#anchor-2').click()
+ expect(clickSpy).not.toHaveBeenCalled()
+ })
+
+ it('should call `scrollTop` if element doesn\'t not support `scrollTo`', () => {
+ fixtureEl.innerHTML = getDummyFixture()
+
+ const div = fixtureEl.querySelector('.content')
+ const link = fixtureEl.querySelector('[href="#div-jsm-1"]')
+ delete div.scrollTo
+ const clickSpy = getElementScrollSpy(div)
+ // eslint-disable-next-line no-new
+ new ScrollSpy(div, {
+ offset: 1,
+ smoothScroll: true
+ })
+
+ link.click()
+ expect(clickSpy).toHaveBeenCalled()
+ })
+
+ it('should smoothScroll to the proper observable element on anchor click', done => {
+ fixtureEl.innerHTML = getDummyFixture()
+
+ const div = fixtureEl.querySelector('.content')
+ const link = fixtureEl.querySelector('[href="#div-jsm-1"]')
+ const observable = fixtureEl.querySelector('#div-jsm-1')
+ const clickSpy = getElementScrollSpy(div)
+ // eslint-disable-next-line no-new
+ new ScrollSpy(div, {
+ offset: 1,
+ smoothScroll: true
+ })
+
+ setTimeout(() => {
+ if (div.scrollTo) {
+ expect(clickSpy).toHaveBeenCalledWith({ top: observable.offsetTop - div.offsetTop, behavior: 'smooth' })
+ } else {
+ expect(clickSpy).toHaveBeenCalledWith(observable.offsetTop - div.offsetTop)
+ }
+
+ done()
+ }, 100)
+ link.click()
+ })
+
+ it('should smoothscroll to observable with anchor link that contains a french word as id', done => {
+ fixtureEl.innerHTML = [
+ '<nav id="navBar" class="navbar">',
+ ' <ul class="nav">',
+ ' <li class="nav-item"><a id="li-jsm-1" class="nav-link" href="#présentation">div 1</a></li>',
+ ' </ul>',
+ '</nav>',
+ '<div class="content" data-bs-target="#navBar" style="overflow-y: auto">',
+ ' <div id="présentation">div 1</div>',
+ '</div>'
+ ].join('')
+
+ const div = fixtureEl.querySelector('.content')
+ const link = fixtureEl.querySelector('[href="#présentation"]')
+ const observable = fixtureEl.querySelector('#présentation')
+ const clickSpy = getElementScrollSpy(div)
+ // eslint-disable-next-line no-new
+ new ScrollSpy(div, {
+ offset: 1,
+ smoothScroll: true
+ })
+
+ setTimeout(() => {
+ if (div.scrollTo) {
+ expect(clickSpy).toHaveBeenCalledWith({ top: observable.offsetTop - div.offsetTop, behavior: 'smooth' })
+ } else {
+ expect(clickSpy).toHaveBeenCalledWith(observable.offsetTop - div.offsetTop)
+ }
+
+ done()
+ }, 100)
+ link.click()
+ })
+ })
})
diff --git a/js/tests/unit/tab.spec.js b/js/tests/unit/tab.spec.js
index 05f9db2ec..4fcf8ee0f 100644
--- a/js/tests/unit/tab.spec.js
+++ b/js/tests/unit/tab.spec.js
@@ -1,5 +1,7 @@
-import Tab from '../../src/tab'
-import { getFixture, clearFixture, jQueryMock } from '../helpers/fixture'
+import Tab from '../../src/tab.js'
+import {
+ clearFixture, createEvent, getFixture, jQueryMock
+} from '../helpers/fixture.js'
describe('Tab', () => {
let fixtureEl
@@ -21,8 +23,12 @@ describe('Tab', () => {
describe('constructor', () => {
it('should take care of element either passed as a CSS selector or DOM element', () => {
fixtureEl.innerHTML = [
- '<ul class="nav"><li><a href="#home" role="tab">Home</a></li></ul>',
- '<ul><li id="home"></li></ul>'
+ '<ul class="nav">',
+ ' <li><a href="#home" role="tab">Home</a></li>',
+ '</ul>',
+ '<ul>',
+ ' <li id="home"></li>',
+ '</ul>'
].join('')
const tabEl = fixtureEl.querySelector('[href="#home"]')
@@ -32,334 +38,800 @@ describe('Tab', () => {
expect(tabBySelector._element).toEqual(tabEl)
expect(tabByElement._element).toEqual(tabEl)
})
- })
- describe('show', () => {
- it('should activate element by tab id (using buttons, the preferred semantic way)', done => {
+ it('Do not Throw exception if not parent', () => {
fixtureEl.innerHTML = [
- '<ul class="nav" role="tablist">',
- ' <li><button type="button" data-bs-target="#home" role="tab">Home</button></li>',
- ' <li><button type="button" id="triggerProfile" data-bs-target="#profile" role="tab">Profile</button></li>',
- '</ul>',
- '<ul>',
- ' <li id="home" role="tabpanel"></li>',
- ' <li id="profile" role="tabpanel"></li>',
- '</ul>'
+ fixtureEl.innerHTML = '<div class=""><div class="nav-link"></div></div>'
].join('')
+ const navEl = fixtureEl.querySelector('.nav-link')
- const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
- const tab = new Tab(profileTriggerEl)
+ expect(() => {
+ new Tab(navEl) // eslint-disable-line no-new
+ }).not.toThrowError(TypeError)
+ })
+ })
- profileTriggerEl.addEventListener('shown.bs.tab', () => {
- expect(fixtureEl.querySelector('#profile').classList.contains('active')).toEqual(true)
- expect(profileTriggerEl.getAttribute('aria-selected')).toEqual('true')
- done()
+ describe('show', () => {
+ it('should activate element by tab id (using buttons, the preferred semantic way)', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<ul class="nav" role="tablist">',
+ ' <li><button type="button" data-bs-target="#home" role="tab">Home</button></li>',
+ ' <li><button type="button" id="triggerProfile" data-bs-target="#profile" role="tab">Profile</button></li>',
+ '</ul>',
+ '<ul>',
+ ' <li id="home" role="tabpanel"></li>',
+ ' <li id="profile" role="tabpanel"></li>',
+ '</ul>'
+ ].join('')
+
+ const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
+ const tab = new Tab(profileTriggerEl)
+
+ profileTriggerEl.addEventListener('shown.bs.tab', () => {
+ expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
+ expect(profileTriggerEl.getAttribute('aria-selected')).toEqual('true')
+ resolve()
+ })
+
+ tab.show()
})
+ })
- tab.show()
+ it('should activate element by tab id (using links for tabs - not ideal, but still supported)', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<ul class="nav" role="tablist">',
+ ' <li><a href="#home" role="tab">Home</a></li>',
+ ' <li><a id="triggerProfile" href="#profile" role="tab">Profile</a></li>',
+ '</ul>',
+ '<ul>',
+ ' <li id="home" role="tabpanel"></li>',
+ ' <li id="profile" role="tabpanel"></li>',
+ '</ul>'
+ ].join('')
+
+ const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
+ const tab = new Tab(profileTriggerEl)
+
+ profileTriggerEl.addEventListener('shown.bs.tab', () => {
+ expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
+ expect(profileTriggerEl.getAttribute('aria-selected')).toEqual('true')
+ resolve()
+ })
+
+ tab.show()
+ })
})
- it('should activate element by tab id (using links for tabs - not ideal, but still supported)', done => {
- fixtureEl.innerHTML = [
- '<ul class="nav" role="tablist">',
- ' <li><a href="#home" role="tab">Home</a></li>',
- ' <li><a id="triggerProfile" href="#profile" role="tab">Profile</a></li>',
- '</ul>',
- '<ul>',
- ' <li id="home" role="tabpanel"></li>',
- ' <li id="profile" role="tabpanel"></li>',
- '</ul>'
- ].join('')
+ it('should activate element by tab id in ordered list', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<ol class="nav nav-pills">',
+ ' <li><button type="button" data-bs-target="#home" role="tab">Home</button></li>',
+ ' <li><button type="button" id="triggerProfile" href="#profile" role="tab">Profile</button></li>',
+ '</ol>',
+ '<ol>',
+ ' <li id="home" role="tabpanel"></li>',
+ ' <li id="profile" role="tabpanel"></li>',
+ '</ol>'
+ ].join('')
+
+ const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
+ const tab = new Tab(profileTriggerEl)
+
+ profileTriggerEl.addEventListener('shown.bs.tab', () => {
+ expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
+ resolve()
+ })
- const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
- const tab = new Tab(profileTriggerEl)
+ tab.show()
+ })
+ })
- profileTriggerEl.addEventListener('shown.bs.tab', () => {
- expect(fixtureEl.querySelector('#profile').classList.contains('active')).toEqual(true)
- expect(profileTriggerEl.getAttribute('aria-selected')).toEqual('true')
- done()
+ it('should activate element by tab id in nav list', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<nav class="nav">',
+ ' <button type="button" data-bs-target="#home" role="tab">Home</button>',
+ ' <button type="button" id="triggerProfile" data-bs-target="#profile" role="tab">Profile</button>',
+ '</nav>',
+ '<div>',
+ ' <div id="home" role="tabpanel"></div>',
+ ' <div id="profile" role="tabpanel"></div>',
+ '</div>'
+ ].join('')
+
+ const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
+ const tab = new Tab(profileTriggerEl)
+
+ profileTriggerEl.addEventListener('shown.bs.tab', () => {
+ expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
+ resolve()
+ })
+
+ tab.show()
})
+ })
- tab.show()
+ it('should activate element by tab id in list group', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="list-group" role="tablist">',
+ ' <button type="button" data-bs-target="#home" role="tab">Home</button>',
+ ' <button type="button" id="triggerProfile" data-bs-target="#profile" role="tab">Profile</button>',
+ '</div>',
+ '<div>',
+ ' <div id="home" role="tabpanel"></div>',
+ ' <div id="profile" role="tabpanel"></div>',
+ '</div>'
+ ].join('')
+
+ const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
+ const tab = new Tab(profileTriggerEl)
+
+ profileTriggerEl.addEventListener('shown.bs.tab', () => {
+ expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
+ resolve()
+ })
+
+ tab.show()
+ })
})
- it('should activate element by tab id in ordered list', done => {
+ it('should work with tab id being an int', done => {
fixtureEl.innerHTML = [
- '<ol class="nav nav-pills">',
- ' <li><button type="button" data-bs-target="#home" role="tab">Home</button></li>',
- ' <li><button type="button" id="triggerProfile" href="#profile" role="tab">Profile</button></li>',
- '</ol>',
- '<ol>',
- ' <li id="home" role="tabpanel"></li>',
- ' <li id="profile" role="tabpanel"></li>',
- '</ol>'
+ '<div class="card-header d-block d-inline-block">',
+ ' <ul class="nav nav-tabs card-header-tabs" id="page_tabs">',
+ ' <li class="nav-item">',
+ ' <a class="nav-link" draggable="false" data-toggle="tab" href="#tab1">',
+ ' Working Tab 1 (#tab1)',
+ ' </a>',
+ ' </li>',
+ ' <li class="nav-item">',
+ ' <a id="trigger2" class="nav-link" draggable="false" data-toggle="tab" href="#2">',
+ ' Tab with numeric ID should work (#2)',
+ ' </a>',
+ ' </li>',
+ ' </ul>',
+ '</div>',
+ '<div class="card-body">',
+ ' <div class="tab-content" id="page_content">',
+ ' <div class="tab-pane fade" id="tab1">',
+ ' Working Tab 1 (#tab1) Content Here',
+ ' </div>',
+ ' <div class="tab-pane fade" id="2">',
+ ' Working Tab 2 (#2) with numeric ID',
+ ' </div>',
+ '</div>'
].join('')
-
- const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
+ const profileTriggerEl = fixtureEl.querySelector('#trigger2')
const tab = new Tab(profileTriggerEl)
profileTriggerEl.addEventListener('shown.bs.tab', () => {
- expect(fixtureEl.querySelector('#profile').classList.contains('active')).toEqual(true)
+ expect(fixtureEl.querySelector(`#${CSS.escape('2')}`)).toHaveClass('active')
done()
})
tab.show()
})
- it('should activate element by tab id in nav list', done => {
- fixtureEl.innerHTML = [
- '<nav class="nav">',
- ' <button type="button" data-bs-target="#home" role="tab">Home</button>',
- ' <button type="button" id="triggerProfile" data-bs-target="#profile" role="tab">Profile</a>',
- '</nav>',
- '<div><div id="home" role="tabpanel"></div><div id="profile" role="tabpanel"></div></div>'
- ].join('')
+ it('should not fire shown when show is prevented', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = '<div class="nav"><div class="nav-link"></div></div>'
+
+ const navEl = fixtureEl.querySelector('.nav > div')
+ const tab = new Tab(navEl)
+ const expectDone = () => {
+ setTimeout(() => {
+ expect().nothing()
+ resolve()
+ }, 30)
+ }
+
+ navEl.addEventListener('show.bs.tab', ev => {
+ ev.preventDefault()
+ expectDone()
+ })
- const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
- const tab = new Tab(profileTriggerEl)
+ navEl.addEventListener('shown.bs.tab', () => {
+ reject(new Error('should not trigger shown event'))
+ })
- profileTriggerEl.addEventListener('shown.bs.tab', () => {
- expect(fixtureEl.querySelector('#profile').classList.contains('active')).toEqual(true)
- done()
+ tab.show()
})
+ })
- tab.show()
+ it('should not fire shown when tab is already active', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<ul class="nav nav-tabs" role="tablist">',
+ ' <li class="nav-item" role="presentation"><button type="button" data-bs-target="#home" class="nav-link active" role="tab" aria-selected="true">Home</button></li>',
+ ' <li class="nav-item" role="presentation"><button type="button" data-bs-target="#profile" class="nav-link" role="tab">Profile</button></li>',
+ '</ul>',
+ '<div class="tab-content">',
+ ' <div class="tab-pane active" id="home" role="tabpanel"></div>',
+ ' <div class="tab-pane" id="profile" role="tabpanel"></div>',
+ '</div>'
+ ].join('')
+
+ const triggerActive = fixtureEl.querySelector('button.active')
+ const tab = new Tab(triggerActive)
+
+ triggerActive.addEventListener('shown.bs.tab', () => {
+ reject(new Error('should not trigger shown event'))
+ })
+
+ tab.show()
+ setTimeout(() => {
+ expect().nothing()
+ resolve()
+ }, 30)
+ })
})
- it('should activate element by tab id in list group', done => {
- fixtureEl.innerHTML = [
- '<div class="list-group" role="tablist">',
- ' <button type="button" data-bs-target="#home" role="tab">Home</button>',
- ' <button type="button" id="triggerProfile" data-bs-target="#profile" role="tab">Profile</button>',
- '</div>',
- '<div><div id="home" role="tabpanel"></div><div id="profile" role="tabpanel"></div></div>'
- ].join('')
+ it('show and shown events should reference correct relatedTarget', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<ul class="nav nav-tabs" role="tablist">',
+ ' <li class="nav-item" role="presentation"><button type="button" data-bs-target="#home" class="nav-link active" role="tab" aria-selected="true">Home</button></li>',
+ ' <li class="nav-item" role="presentation"><button type="button" id="triggerProfile" data-bs-target="#profile" class="nav-link" role="tab">Profile</button></li>',
+ '</ul>',
+ '<div class="tab-content">',
+ ' <div class="tab-pane active" id="home" role="tabpanel"></div>',
+ ' <div class="tab-pane" id="profile" role="tabpanel"></div>',
+ '</div>'
+ ].join('')
+
+ const secondTabTrigger = fixtureEl.querySelector('#triggerProfile')
+ const secondTab = new Tab(secondTabTrigger)
+
+ secondTabTrigger.addEventListener('show.bs.tab', ev => {
+ expect(ev.relatedTarget.getAttribute('data-bs-target')).toEqual('#home')
+ })
- const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
- const tab = new Tab(profileTriggerEl)
+ secondTabTrigger.addEventListener('shown.bs.tab', ev => {
+ expect(ev.relatedTarget.getAttribute('data-bs-target')).toEqual('#home')
+ expect(secondTabTrigger.getAttribute('aria-selected')).toEqual('true')
+ expect(fixtureEl.querySelector('button:not(.active)').getAttribute('aria-selected')).toEqual('false')
+ resolve()
+ })
- profileTriggerEl.addEventListener('shown.bs.tab', () => {
- expect(fixtureEl.querySelector('#profile').classList.contains('active')).toEqual(true)
- done()
+ secondTab.show()
})
+ })
- tab.show()
+ it('should fire hide and hidden events', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<ul class="nav" role="tablist">',
+ ' <li><button type="button" data-bs-target="#home" role="tab">Home</button></li>',
+ ' <li><button type="button" data-bs-target="#profile" role="tab">Profile</button></li>',
+ '</ul>'
+ ].join('')
+
+ const triggerList = fixtureEl.querySelectorAll('button')
+ const firstTab = new Tab(triggerList[0])
+ const secondTab = new Tab(triggerList[1])
+
+ let hideCalled = false
+ triggerList[0].addEventListener('shown.bs.tab', () => {
+ secondTab.show()
+ })
+
+ triggerList[0].addEventListener('hide.bs.tab', ev => {
+ hideCalled = true
+ expect(ev.relatedTarget.getAttribute('data-bs-target')).toEqual('#profile')
+ })
+
+ triggerList[0].addEventListener('hidden.bs.tab', ev => {
+ expect(hideCalled).toBeTrue()
+ expect(ev.relatedTarget.getAttribute('data-bs-target')).toEqual('#profile')
+ resolve()
+ })
+
+ firstTab.show()
+ })
})
- it('should not fire shown when show is prevented', done => {
- fixtureEl.innerHTML = '<div class="nav"></div>'
+ it('should not fire hidden when hide is prevented', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<ul class="nav" role="tablist">',
+ ' <li><button type="button" data-bs-target="#home" role="tab">Home</button></li>',
+ ' <li><button type="button" data-bs-target="#profile" role="tab">Profile</button></li>',
+ '</ul>'
+ ].join('')
+
+ const triggerList = fixtureEl.querySelectorAll('button')
+ const firstTab = new Tab(triggerList[0])
+ const secondTab = new Tab(triggerList[1])
+ const expectDone = () => {
+ setTimeout(() => {
+ expect().nothing()
+ resolve()
+ }, 30)
+ }
+
+ triggerList[0].addEventListener('shown.bs.tab', () => {
+ secondTab.show()
+ })
- const navEl = fixtureEl.querySelector('div')
- const tab = new Tab(navEl)
- const expectDone = () => {
- setTimeout(() => {
- expect().nothing()
- done()
- }, 30)
- }
+ triggerList[0].addEventListener('hide.bs.tab', ev => {
+ ev.preventDefault()
+ expectDone()
+ })
+
+ triggerList[0].addEventListener('hidden.bs.tab', () => {
+ reject(new Error('should not trigger hidden'))
+ })
- navEl.addEventListener('show.bs.tab', ev => {
- ev.preventDefault()
- expectDone()
+ firstTab.show()
})
+ })
+
+ it('should handle removed tabs', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<ul class="nav nav-tabs" role="tablist">',
+ ' <li class="nav-item" role="presentation">',
+ ' <a class="nav-link nav-tab" href="#profile" role="tab" data-bs-toggle="tab">',
+ ' <button class="btn-close" aria-label="Close"></button>',
+ ' </a>',
+ ' </li>',
+ ' <li class="nav-item" role="presentation">',
+ ' <a id="secondNav" class="nav-link nav-tab" href="#buzz" role="tab" data-bs-toggle="tab">',
+ ' <button class="btn-close" aria-label="Close"></button>',
+ ' </a>',
+ ' </li>',
+ ' <li class="nav-item" role="presentation">',
+ ' <a class="nav-link nav-tab" href="#references" role="tab" data-bs-toggle="tab">',
+ ' <button id="btnClose" class="btn-close" aria-label="Close"></button>',
+ ' </a>',
+ ' </li>',
+ '</ul>',
+ '<div class="tab-content">',
+ ' <div role="tabpanel" class="tab-pane fade show active" id="profile">test 1</div>',
+ ' <div role="tabpanel" class="tab-pane fade" id="buzz">test 2</div>',
+ ' <div role="tabpanel" class="tab-pane fade" id="references">test 3</div>',
+ '</div>'
+ ].join('')
+
+ const secondNavEl = fixtureEl.querySelector('#secondNav')
+ const btnCloseEl = fixtureEl.querySelector('#btnClose')
+ const secondNavTab = new Tab(secondNavEl)
+
+ secondNavEl.addEventListener('shown.bs.tab', () => {
+ expect(fixtureEl.querySelectorAll('.nav-tab')).toHaveSize(2)
+ resolve()
+ })
- navEl.addEventListener('shown.bs.tab', () => {
- throw new Error('should not trigger shown event')
+ btnCloseEl.addEventListener('click', () => {
+ const linkEl = btnCloseEl.parentNode
+ const liEl = linkEl.parentNode
+ const tabId = linkEl.getAttribute('href')
+ const tabIdEl = fixtureEl.querySelector(tabId)
+
+ liEl.remove()
+ tabIdEl.remove()
+ secondNavTab.show()
+ })
+
+ btnCloseEl.click()
})
+ })
- tab.show()
+ it('should not focus on opened tab', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<ul class="nav" role="tablist">',
+ ' <li><button type="button" id="home" data-bs-target="#home" role="tab">Home</button></li>',
+ ' <li><button type="button" id="triggerProfile" data-bs-target="#profile" role="tab">Profile</button></li>',
+ '</ul>',
+ '<ul>',
+ ' <li id="home" role="tabpanel"></li>',
+ ' <li id="profile" role="tabpanel"></li>',
+ '</ul>'
+ ].join('')
+
+ const firstTab = fixtureEl.querySelector('#home')
+ firstTab.focus()
+
+ const profileTriggerEl = fixtureEl.querySelector('#triggerProfile')
+ const tab = new Tab(profileTriggerEl)
+
+ profileTriggerEl.addEventListener('shown.bs.tab', () => {
+ expect(document.activeElement).toBe(firstTab)
+ expect(document.activeElement).not.toBe(profileTriggerEl)
+ resolve()
+ })
+
+ tab.show()
+ })
})
+ })
+
+ describe('dispose', () => {
+ it('should dispose a tab', () => {
+ fixtureEl.innerHTML = '<div class="nav"><div class="nav-link"></div></div>'
+
+ const el = fixtureEl.querySelector('.nav > div')
+ const tab = new Tab(fixtureEl.querySelector('.nav > div'))
+
+ expect(Tab.getInstance(el)).not.toBeNull()
- it('should not fire shown when tab is already active', done => {
+ tab.dispose()
+
+ expect(Tab.getInstance(el)).toBeNull()
+ })
+ })
+
+ describe('_activate', () => {
+ it('should not be called if element argument is null', () => {
+ fixtureEl.innerHTML = [
+ '<ul class="nav" role="tablist">',
+ ' <li class="nav-link"></li>',
+ '</ul>'
+ ].join('')
+
+ const tabEl = fixtureEl.querySelector('.nav-link')
+ const tab = new Tab(tabEl)
+ const spy = jasmine.createSpy('spy')
+
+ const spyQueue = spyOn(tab, '_queueCallback')
+ tab._activate(null, spy)
+ expect(spyQueue).not.toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
+ })
+ })
+
+ describe('_setInitialAttributes', () => {
+ it('should put aria attributes', () => {
fixtureEl.innerHTML = [
- '<ul class="nav nav-tabs" role="tablist">',
- ' <li class="nav-item" role="presentation"><button type="button" data-bs-target="#home" class="nav-link active" role="tab" aria-selected="true">Home</button></li>',
- ' <li class="nav-item" role="presentation"><button type="button" data-bs-target="#profile" class="nav-link" role="tab">Profile</button></li>',
+ '<ul class="nav">',
+ ' <li class="nav-link" id="foo" data-bs-target="#panel"></li>',
+ ' <li class="nav-link" data-bs-target="#panel2"></li>',
'</ul>',
- '<div class="tab-content">',
- ' <div class="tab-pane active" id="home" role="tabpanel"></div>',
- ' <div class="tab-pane" id="profile" role="tabpanel"></div>',
- '</div>'
+ '<div id="panel"></div>',
+ '<div id="panel2"></div>'
].join('')
- const triggerActive = fixtureEl.querySelector('button.active')
- const tab = new Tab(triggerActive)
+ const tabEl = fixtureEl.querySelector('.nav-link')
+ const parent = fixtureEl.querySelector('.nav')
+ const children = fixtureEl.querySelectorAll('.nav-link')
+ const tabPanel = fixtureEl.querySelector('#panel')
+ const tabPanel2 = fixtureEl.querySelector('#panel2')
- triggerActive.addEventListener('shown.bs.tab', () => {
- throw new Error('should not trigger shown event')
- })
+ expect(parent.getAttribute('role')).toEqual(null)
+ expect(tabEl.getAttribute('role')).toEqual(null)
+ expect(tabPanel.getAttribute('role')).toEqual(null)
+ const tab = new Tab(tabEl)
+ tab._setInitialAttributes(parent, children)
- tab.show()
- setTimeout(() => {
- expect().nothing()
- done()
- }, 30)
+ expect(parent.getAttribute('role')).toEqual('tablist')
+ expect(tabEl.getAttribute('role')).toEqual('tab')
+
+ expect(tabPanel.getAttribute('role')).toEqual('tabpanel')
+ expect(tabPanel2.getAttribute('role')).toEqual('tabpanel')
+ expect(tabPanel.hasAttribute('tabindex')).toBeFalse()
+ expect(tabPanel.hasAttribute('tabindex2')).toBeFalse()
+
+ expect(tabPanel.getAttribute('aria-labelledby')).toEqual('foo')
+ expect(tabPanel2.hasAttribute('aria-labelledby')).toBeFalse()
})
+ })
- it('show and shown events should reference correct relatedTarget', done => {
+ describe('_keydown', () => {
+ it('if event is not one of left/right/up/down arrow, ignore it', () => {
fixtureEl.innerHTML = [
- '<ul class="nav nav-tabs" role="tablist">',
- ' <li class="nav-item" role="presentation"><button type="button" data-bs-target="#home" class="nav-link active" role="tab" aria-selected="true">Home</button></li>',
- ' <li class="nav-item" role="presentation"><button type="button" id="triggerProfile" data-bs-target="#profile" class="nav-link" role="tab">Profile</button></li>',
- '</ul>',
- '<div class="tab-content">',
- ' <div class="tab-pane active" id="home" role="tabpanel"></div>',
- ' <div class="tab-pane" id="profile" role="tabpanel"></div>',
+ '<ul class="nav">',
+ ' <li class="nav-link" data-bs-toggle="tab"></li>',
+ '</ul>'
+ ].join('')
+
+ const tabEl = fixtureEl.querySelector('.nav-link')
+ const tab = new Tab(tabEl)
+
+ const keydown = createEvent('keydown')
+ keydown.key = 'Enter'
+ const spyStop = spyOn(Event.prototype, 'stopPropagation').and.callThrough()
+ const spyPrevent = spyOn(Event.prototype, 'preventDefault').and.callThrough()
+ const spyKeydown = spyOn(tab, '_keydown')
+ const spyGet = spyOn(tab, '_getChildren')
+
+ tabEl.dispatchEvent(keydown)
+ expect(spyKeydown).toHaveBeenCalled()
+ expect(spyGet).not.toHaveBeenCalled()
+
+ expect(spyStop).not.toHaveBeenCalled()
+ expect(spyPrevent).not.toHaveBeenCalled()
+ })
+
+ it('if keydown event is right/down arrow, handle it', () => {
+ fixtureEl.innerHTML = [
+ '<div class="nav">',
+ ' <span id="tab1" class="nav-link" data-bs-toggle="tab"></span>',
+ ' <span id="tab2" class="nav-link" data-bs-toggle="tab"></span>',
+ ' <span id="tab3" class="nav-link" data-bs-toggle="tab"></span>',
'</div>'
].join('')
- const secondTabTrigger = fixtureEl.querySelector('#triggerProfile')
- const secondTab = new Tab(secondTabTrigger)
+ const tabEl1 = fixtureEl.querySelector('#tab1')
+ const tabEl2 = fixtureEl.querySelector('#tab2')
+ const tabEl3 = fixtureEl.querySelector('#tab3')
+ const tab1 = new Tab(tabEl1)
+ const tab2 = new Tab(tabEl2)
+ const tab3 = new Tab(tabEl3)
+ const spyShow1 = spyOn(tab1, 'show').and.callThrough()
+ const spyShow2 = spyOn(tab2, 'show').and.callThrough()
+ const spyShow3 = spyOn(tab3, 'show').and.callThrough()
+ const spyFocus1 = spyOn(tabEl1, 'focus').and.callThrough()
+ const spyFocus2 = spyOn(tabEl2, 'focus').and.callThrough()
+ const spyFocus3 = spyOn(tabEl3, 'focus').and.callThrough()
+
+ const spyStop = spyOn(Event.prototype, 'stopPropagation').and.callThrough()
+ const spyPrevent = spyOn(Event.prototype, 'preventDefault').and.callThrough()
+
+ let keydown = createEvent('keydown')
+ keydown.key = 'ArrowRight'
+
+ tabEl1.dispatchEvent(keydown)
+ expect(spyShow2).toHaveBeenCalled()
+ expect(spyFocus2).toHaveBeenCalled()
+
+ keydown = createEvent('keydown')
+ keydown.key = 'ArrowDown'
+
+ tabEl2.dispatchEvent(keydown)
+ expect(spyShow3).toHaveBeenCalled()
+ expect(spyFocus3).toHaveBeenCalled()
+
+ tabEl3.dispatchEvent(keydown)
+ expect(spyShow1).toHaveBeenCalled()
+ expect(spyFocus1).toHaveBeenCalled()
+
+ expect(spyStop).toHaveBeenCalledTimes(3)
+ expect(spyPrevent).toHaveBeenCalledTimes(3)
+ })
- secondTabTrigger.addEventListener('show.bs.tab', ev => {
- expect(ev.relatedTarget.getAttribute('data-bs-target')).toEqual('#home')
- })
+ it('if keydown event is left arrow, handle it', () => {
+ fixtureEl.innerHTML = [
+ '<div class="nav">',
+ ' <span id="tab1" class="nav-link" data-bs-toggle="tab"></span>',
+ ' <span id="tab2" class="nav-link" data-bs-toggle="tab"></span>',
+ '</div>'
+ ].join('')
- secondTabTrigger.addEventListener('shown.bs.tab', ev => {
- expect(ev.relatedTarget.getAttribute('data-bs-target')).toEqual('#home')
- expect(secondTabTrigger.getAttribute('aria-selected')).toEqual('true')
- expect(fixtureEl.querySelector('button:not(.active)').getAttribute('aria-selected')).toEqual('false')
- done()
- })
+ const tabEl1 = fixtureEl.querySelector('#tab1')
+ const tabEl2 = fixtureEl.querySelector('#tab2')
+ const tab1 = new Tab(tabEl1)
+ const tab2 = new Tab(tabEl2)
+ const spyShow1 = spyOn(tab1, 'show').and.callThrough()
+ const spyShow2 = spyOn(tab2, 'show').and.callThrough()
+ const spyFocus1 = spyOn(tabEl1, 'focus').and.callThrough()
+ const spyFocus2 = spyOn(tabEl2, 'focus').and.callThrough()
+
+ const spyStop = spyOn(Event.prototype, 'stopPropagation').and.callThrough()
+ const spyPrevent = spyOn(Event.prototype, 'preventDefault').and.callThrough()
+
+ let keydown = createEvent('keydown')
+ keydown.key = 'ArrowLeft'
+
+ tabEl2.dispatchEvent(keydown)
+ expect(spyShow1).toHaveBeenCalled()
+ expect(spyFocus1).toHaveBeenCalled()
+
+ keydown = createEvent('keydown')
+ keydown.key = 'ArrowUp'
+
+ tabEl1.dispatchEvent(keydown)
+ expect(spyShow2).toHaveBeenCalled()
+ expect(spyFocus2).toHaveBeenCalled()
- secondTab.show()
+ expect(spyStop).toHaveBeenCalledTimes(2)
+ expect(spyPrevent).toHaveBeenCalledTimes(2)
})
- it('should fire hide and hidden events', done => {
+ it('if keydown event is Home, handle it', () => {
fixtureEl.innerHTML = [
- '<ul class="nav" role="tablist">',
- ' <li><button type="button" data-bs-target="#home" role="tab">Home</button></li>',
- ' <li><button type="button" data-bs-target="#profile">Profile</button></li>',
- '</ul>'
+ '<div class="nav">',
+ ' <span id="tab1" class="nav-link" data-bs-toggle="tab"></span>',
+ ' <span id="tab2" class="nav-link" data-bs-toggle="tab"></span>',
+ ' <span id="tab3" class="nav-link" data-bs-toggle="tab"></span>',
+ '</div>'
].join('')
- const triggerList = fixtureEl.querySelectorAll('button')
- const firstTab = new Tab(triggerList[0])
- const secondTab = new Tab(triggerList[1])
+ const tabEl1 = fixtureEl.querySelector('#tab1')
+ const tabEl3 = fixtureEl.querySelector('#tab3')
- let hideCalled = false
- triggerList[0].addEventListener('shown.bs.tab', () => {
- secondTab.show()
- })
+ const tab3 = new Tab(tabEl3)
+ tab3.show()
- triggerList[0].addEventListener('hide.bs.tab', ev => {
- hideCalled = true
- expect(ev.relatedTarget.getAttribute('data-bs-target')).toEqual('#profile')
- })
+ const spyShown = jasmine.createSpy()
+ tabEl1.addEventListener('shown.bs.tab', spyShown)
- triggerList[0].addEventListener('hidden.bs.tab', ev => {
- expect(hideCalled).toEqual(true)
- expect(ev.relatedTarget.getAttribute('data-bs-target')).toEqual('#profile')
- done()
- })
+ const keydown = createEvent('keydown')
+ keydown.key = 'Home'
+
+ tabEl3.dispatchEvent(keydown)
- firstTab.show()
+ expect(spyShown).toHaveBeenCalled()
})
- it('should not fire hidden when hide is prevented', done => {
+ it('if keydown event is End, handle it', () => {
fixtureEl.innerHTML = [
- '<ul class="nav" role="tablist">',
- ' <li><button type="button" data-bs-target="#home" role="tab">Home</button></li>',
- ' <li><button type="button" data-bs-target="#profile" role="tab">Profile</button></li>',
- '</ul>'
+ '<div class="nav">',
+ ' <span id="tab1" class="nav-link" data-bs-toggle="tab"></span>',
+ ' <span id="tab2" class="nav-link" data-bs-toggle="tab"></span>',
+ ' <span id="tab3" class="nav-link" data-bs-toggle="tab"></span>',
+ '</div>'
].join('')
- const triggerList = fixtureEl.querySelectorAll('button')
- const firstTab = new Tab(triggerList[0])
- const secondTab = new Tab(triggerList[1])
- const expectDone = () => {
- setTimeout(() => {
- expect().nothing()
- done()
- }, 30)
- }
+ const tabEl1 = fixtureEl.querySelector('#tab1')
+ const tabEl3 = fixtureEl.querySelector('#tab3')
- triggerList[0].addEventListener('shown.bs.tab', () => {
- secondTab.show()
- })
+ const tab1 = new Tab(tabEl1)
+ tab1.show()
- triggerList[0].addEventListener('hide.bs.tab', ev => {
- ev.preventDefault()
- expectDone()
- })
+ const spyShown = jasmine.createSpy()
+ tabEl3.addEventListener('shown.bs.tab', spyShown)
- triggerList[0].addEventListener('hidden.bs.tab', () => {
- throw new Error('should not trigger hidden')
- })
+ const keydown = createEvent('keydown')
+ keydown.key = 'End'
+
+ tabEl1.dispatchEvent(keydown)
- firstTab.show()
+ expect(spyShown).toHaveBeenCalled()
})
- it('should handle removed tabs', done => {
+ it('if keydown event is right arrow and next element is disabled', () => {
fixtureEl.innerHTML = [
- '<ul class="nav nav-tabs" role="tablist">',
- ' <li class="nav-item" role="presentation">',
- ' <a class="nav-link nav-tab" href="#profile" role="tab" data-bs-toggle="tab">',
- ' <button class="btn-close" aria-label="Close"></button>',
- ' </a>',
- ' </li>',
- ' <li class="nav-item" role="presentation">',
- ' <a id="secondNav" class="nav-link nav-tab" href="#buzz" role="tab" data-bs-toggle="tab">',
- ' <button class="btn-close" aria-label="Close"></button>',
- ' </a>',
- ' </li>',
- ' <li class="nav-item" role="presentation">',
- ' <a class="nav-link nav-tab" href="#references" role="tab" data-bs-toggle="tab">',
- ' <button id="btnClose" class="btn-close" aria-label="Close"></button>',
- ' </a>',
- ' </li>',
- '</ul>',
- '<div class="tab-content">',
- ' <div role="tabpanel" class="tab-pane fade show active" id="profile">test 1</div>',
- ' <div role="tabpanel" class="tab-pane fade" id="buzz">test 2</div>',
- ' <div role="tabpanel" class="tab-pane fade" id="references">test 3</div>',
+ '<div class="nav">',
+ ' <span id="tab1" class="nav-link" data-bs-toggle="tab"></span>',
+ ' <span id="tab2" class="nav-link" data-bs-toggle="tab" disabled></span>',
+ ' <span id="tab3" class="nav-link disabled" data-bs-toggle="tab"></span>',
+ ' <span id="tab4" class="nav-link" data-bs-toggle="tab"></span>',
'</div>'
].join('')
- const secondNavEl = fixtureEl.querySelector('#secondNav')
- const btnCloseEl = fixtureEl.querySelector('#btnClose')
- const secondNavTab = new Tab(secondNavEl)
+ const tabEl1 = fixtureEl.querySelector('#tab1')
+ const tabEl2 = fixtureEl.querySelector('#tab2')
+ const tabEl3 = fixtureEl.querySelector('#tab3')
+ const tabEl4 = fixtureEl.querySelector('#tab4')
+ const tab1 = new Tab(tabEl1)
+ const tab2 = new Tab(tabEl2)
+ const tab3 = new Tab(tabEl3)
+ const tab4 = new Tab(tabEl4)
+ const spy1 = spyOn(tab1, 'show').and.callThrough()
+ const spy2 = spyOn(tab2, 'show').and.callThrough()
+ const spy3 = spyOn(tab3, 'show').and.callThrough()
+ const spy4 = spyOn(tab4, 'show').and.callThrough()
+ const spyFocus1 = spyOn(tabEl1, 'focus').and.callThrough()
+ const spyFocus2 = spyOn(tabEl2, 'focus').and.callThrough()
+ const spyFocus3 = spyOn(tabEl3, 'focus').and.callThrough()
+ const spyFocus4 = spyOn(tabEl4, 'focus').and.callThrough()
+
+ const keydown = createEvent('keydown')
+ keydown.key = 'ArrowRight'
+
+ tabEl1.dispatchEvent(keydown)
+ expect(spy1).not.toHaveBeenCalled()
+ expect(spy2).not.toHaveBeenCalled()
+ expect(spy3).not.toHaveBeenCalled()
+ expect(spy4).toHaveBeenCalledTimes(1)
+ expect(spyFocus1).not.toHaveBeenCalled()
+ expect(spyFocus2).not.toHaveBeenCalled()
+ expect(spyFocus3).not.toHaveBeenCalled()
+ expect(spyFocus4).toHaveBeenCalledTimes(1)
+ })
- secondNavEl.addEventListener('shown.bs.tab', () => {
- expect(fixtureEl.querySelectorAll('.nav-tab').length).toEqual(2)
- done()
- })
+ it('if keydown event is left arrow and next element is disabled', () => {
+ fixtureEl.innerHTML = [
+ '<div class="nav">',
+ ' <span id="tab1" class="nav-link" data-bs-toggle="tab"></span>',
+ ' <span id="tab2" class="nav-link" data-bs-toggle="tab" disabled></span>',
+ ' <span id="tab3" class="nav-link disabled" data-bs-toggle="tab"></span>',
+ ' <span id="tab4" class="nav-link" data-bs-toggle="tab"></span>',
+ '</div>'
+ ].join('')
- btnCloseEl.addEventListener('click', () => {
- const linkEl = btnCloseEl.parentNode
- const liEl = linkEl.parentNode
- const tabId = linkEl.getAttribute('href')
- const tabIdEl = fixtureEl.querySelector(tabId)
+ const tabEl1 = fixtureEl.querySelector('#tab1')
+ const tabEl2 = fixtureEl.querySelector('#tab2')
+ const tabEl3 = fixtureEl.querySelector('#tab3')
+ const tabEl4 = fixtureEl.querySelector('#tab4')
+ const tab1 = new Tab(tabEl1)
+ const tab2 = new Tab(tabEl2)
+ const tab3 = new Tab(tabEl3)
+ const tab4 = new Tab(tabEl4)
+ const spy1 = spyOn(tab1, 'show').and.callThrough()
+ const spy2 = spyOn(tab2, 'show').and.callThrough()
+ const spy3 = spyOn(tab3, 'show').and.callThrough()
+ const spy4 = spyOn(tab4, 'show').and.callThrough()
+ const spyFocus1 = spyOn(tabEl1, 'focus').and.callThrough()
+ const spyFocus2 = spyOn(tabEl2, 'focus').and.callThrough()
+ const spyFocus3 = spyOn(tabEl3, 'focus').and.callThrough()
+ const spyFocus4 = spyOn(tabEl4, 'focus').and.callThrough()
+
+ const keydown = createEvent('keydown')
+ keydown.key = 'ArrowLeft'
+
+ tabEl4.dispatchEvent(keydown)
+ expect(spy4).not.toHaveBeenCalled()
+ expect(spy3).not.toHaveBeenCalled()
+ expect(spy2).not.toHaveBeenCalled()
+ expect(spy1).toHaveBeenCalledTimes(1)
+ expect(spyFocus4).not.toHaveBeenCalled()
+ expect(spyFocus3).not.toHaveBeenCalled()
+ expect(spyFocus2).not.toHaveBeenCalled()
+ expect(spyFocus1).toHaveBeenCalledTimes(1)
+ })
- liEl.remove()
- tabIdEl.remove()
- secondNavTab.show()
- })
+ it('if keydown event is Home and first element is disabled', () => {
+ fixtureEl.innerHTML = [
+ '<div class="nav">',
+ ' <span id="tab1" class="nav-link disabled" data-bs-toggle="tab" disabled></span>',
+ ' <span id="tab2" class="nav-link" data-bs-toggle="tab"></span>',
+ ' <span id="tab3" class="nav-link" data-bs-toggle="tab"></span>',
+ '</div>'
+ ].join('')
+
+ const tabEl1 = fixtureEl.querySelector('#tab1')
+ const tabEl2 = fixtureEl.querySelector('#tab2')
+ const tabEl3 = fixtureEl.querySelector('#tab3')
+ const tab3 = new Tab(tabEl3)
- btnCloseEl.click()
+ tab3.show()
+
+ const spyShown1 = jasmine.createSpy()
+ const spyShown2 = jasmine.createSpy()
+ tabEl1.addEventListener('shown.bs.tab', spyShown1)
+ tabEl2.addEventListener('shown.bs.tab', spyShown2)
+
+ const keydown = createEvent('keydown')
+ keydown.key = 'Home'
+
+ tabEl3.dispatchEvent(keydown)
+
+ expect(spyShown1).not.toHaveBeenCalled()
+ expect(spyShown2).toHaveBeenCalled()
})
- })
- describe('dispose', () => {
- it('should dispose a tab', () => {
- fixtureEl.innerHTML = '<div></div>'
+ it('if keydown event is End and last element is disabled', () => {
+ fixtureEl.innerHTML = [
+ '<div class="nav">',
+ ' <span id="tab1" class="nav-link" data-bs-toggle="tab"></span>',
+ ' <span id="tab2" class="nav-link" data-bs-toggle="tab"></span>',
+ ' <span id="tab3" class="nav-link" data-bs-toggle="tab" disabled></span>',
+ '</div>'
+ ].join('')
- const el = fixtureEl.querySelector('div')
- const tab = new Tab(fixtureEl.querySelector('div'))
+ const tabEl1 = fixtureEl.querySelector('#tab1')
+ const tabEl2 = fixtureEl.querySelector('#tab2')
+ const tabEl3 = fixtureEl.querySelector('#tab3')
+ const tab1 = new Tab(tabEl1)
- expect(Tab.getInstance(el)).not.toBeNull()
+ tab1.show()
- tab.dispose()
+ const spyShown2 = jasmine.createSpy()
+ const spyShown3 = jasmine.createSpy()
+ tabEl2.addEventListener('shown.bs.tab', spyShown2)
+ tabEl3.addEventListener('shown.bs.tab', spyShown3)
- expect(Tab.getInstance(el)).toBeNull()
+ const keydown = createEvent('keydown')
+ keydown.key = 'End'
+
+ tabEl1.dispatchEvent(keydown)
+
+ expect(spyShown3).not.toHaveBeenCalled()
+ expect(spyShown2).toHaveBeenCalled()
})
})
describe('jQueryInterface', () => {
it('should create a tab', () => {
- fixtureEl.innerHTML = '<div></div>'
+ fixtureEl.innerHTML = '<div class="nav"><div class="nav-link"></div></div>'
- const div = fixtureEl.querySelector('div')
+ const div = fixtureEl.querySelector('.nav > div')
jQueryMock.fn.tab = Tab.jQueryInterface
jQueryMock.elements = [div]
@@ -370,9 +842,9 @@ describe('Tab', () => {
})
it('should not re create a tab', () => {
- fixtureEl.innerHTML = '<div></div>'
+ fixtureEl.innerHTML = '<div class="nav"><div class="nav-link"></div></div>'
- const div = fixtureEl.querySelector('div')
+ const div = fixtureEl.querySelector('.nav > div')
const tab = new Tab(div)
jQueryMock.fn.tab = Tab.jQueryInterface
@@ -384,12 +856,12 @@ describe('Tab', () => {
})
it('should call a tab method', () => {
- fixtureEl.innerHTML = '<div></div>'
+ fixtureEl.innerHTML = '<div class="nav"><div class="nav-link"></div></div>'
- const div = fixtureEl.querySelector('div')
+ const div = fixtureEl.querySelector('.nav > div')
const tab = new Tab(div)
- spyOn(tab, 'show')
+ const spy = spyOn(tab, 'show')
jQueryMock.fn.tab = Tab.jQueryInterface
jQueryMock.elements = [div]
@@ -397,13 +869,13 @@ describe('Tab', () => {
jQueryMock.fn.tab.call(jQueryMock, 'show')
expect(Tab.getInstance(div)).toEqual(tab)
- expect(tab.show).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
})
it('should throw error on undefined method', () => {
- fixtureEl.innerHTML = '<div></div>'
+ fixtureEl.innerHTML = '<div class="nav"><div class="nav-link"></div></div>'
- const div = fixtureEl.querySelector('div')
+ const div = fixtureEl.querySelector('.nav > div')
const action = 'undefinedMethod'
jQueryMock.fn.tab = Tab.jQueryInterface
@@ -417,13 +889,13 @@ describe('Tab', () => {
describe('getInstance', () => {
it('should return null if there is no instance', () => {
- expect(Tab.getInstance(fixtureEl)).toEqual(null)
+ expect(Tab.getInstance(fixtureEl)).toBeNull()
})
it('should return this instance', () => {
- fixtureEl.innerHTML = '<div></div>'
+ fixtureEl.innerHTML = '<div class="nav"><div class="nav-link"></div></div>'
- const divEl = fixtureEl.querySelector('div')
+ const divEl = fixtureEl.querySelector('.nav > div')
const tab = new Tab(divEl)
expect(Tab.getInstance(divEl)).toEqual(tab)
@@ -433,7 +905,7 @@ describe('Tab', () => {
describe('getOrCreateInstance', () => {
it('should return tab instance', () => {
- fixtureEl.innerHTML = '<div></div>'
+ fixtureEl.innerHTML = '<div class="nav"><div class="nav-link"></div></div>'
const div = fixtureEl.querySelector('div')
const tab = new Tab(div)
@@ -444,37 +916,39 @@ describe('Tab', () => {
})
it('should return new instance when there is no tab instance', () => {
- fixtureEl.innerHTML = '<div></div>'
+ fixtureEl.innerHTML = '<div class="nav"><div class="nav-link"></div></div>'
const div = fixtureEl.querySelector('div')
- expect(Tab.getInstance(div)).toEqual(null)
+ expect(Tab.getInstance(div)).toBeNull()
expect(Tab.getOrCreateInstance(div)).toBeInstanceOf(Tab)
})
})
describe('data-api', () => {
- it('should create dynamically a tab', done => {
- fixtureEl.innerHTML = [
- '<ul class="nav nav-tabs" role="tablist">',
- ' <li class="nav-item" role="presentation"><button type="button" data-bs-target="#home" class="nav-link active" role="tab" aria-selected="true">Home</button></li>',
- ' <li class="nav-item" role="presentation"><button type="button" id="triggerProfile" data-bs-toggle="tab" data-bs-target="#profile" class="nav-link" role="tab">Profile</button></li>',
- '</ul>',
- '<div class="tab-content">',
- ' <div class="tab-pane active" id="home" role="tabpanel"></div>',
- ' <div class="tab-pane" id="profile" role="tabpanel"></div>',
- '</div>'
- ].join('')
-
- const secondTabTrigger = fixtureEl.querySelector('#triggerProfile')
+ it('should create dynamically a tab', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<ul class="nav nav-tabs" role="tablist">',
+ ' <li class="nav-item" role="presentation"><button type="button" data-bs-target="#home" class="nav-link active" role="tab" aria-selected="true">Home</button></li>',
+ ' <li class="nav-item" role="presentation"><button type="button" id="triggerProfile" data-bs-toggle="tab" data-bs-target="#profile" class="nav-link" role="tab">Profile</button></li>',
+ '</ul>',
+ '<div class="tab-content">',
+ ' <div class="tab-pane active" id="home" role="tabpanel"></div>',
+ ' <div class="tab-pane" id="profile" role="tabpanel"></div>',
+ '</div>'
+ ].join('')
+
+ const secondTabTrigger = fixtureEl.querySelector('#triggerProfile')
+
+ secondTabTrigger.addEventListener('shown.bs.tab', () => {
+ expect(secondTabTrigger).toHaveClass('active')
+ expect(fixtureEl.querySelector('#profile')).toHaveClass('active')
+ resolve()
+ })
- secondTabTrigger.addEventListener('shown.bs.tab', () => {
- expect(secondTabTrigger.classList.contains('active')).toEqual(true)
- expect(fixtureEl.querySelector('#profile').classList.contains('active')).toEqual(true)
- done()
+ secondTabTrigger.click()
})
-
- secondTabTrigger.click()
})
it('selected tab should deactivate previous selected link in dropdown', () => {
@@ -495,9 +969,9 @@ describe('Tab', () => {
const firstLiLinkEl = fixtureEl.querySelector('li:first-child a')
firstLiLinkEl.click()
- expect(firstLiLinkEl.classList.contains('active')).toEqual(true)
- expect(fixtureEl.querySelector('li:last-child a').classList.contains('active')).toEqual(false)
- expect(fixtureEl.querySelector('li:last-child .dropdown-menu a:first-child').classList.contains('active')).toEqual(false)
+ expect(firstLiLinkEl).toHaveClass('active')
+ expect(fixtureEl.querySelector('li:last-child a')).not.toHaveClass('active')
+ expect(fixtureEl.querySelector('li:last-child .dropdown-menu a:first-child')).not.toHaveClass('active')
})
it('selecting a dropdown tab does not activate another', () => {
@@ -529,10 +1003,10 @@ describe('Tab', () => {
const firstDropItem = fixtureEl.querySelector('#nav1 .dropdown-item')
firstDropItem.click()
- expect(firstDropItem.classList.contains('active')).toEqual(true)
- expect(fixtureEl.querySelector('#nav1 .dropdown-toggle').classList.contains('active')).toEqual(true)
- expect(fixtureEl.querySelector('#nav2 .dropdown-toggle').classList.contains('active')).toEqual(false)
- expect(fixtureEl.querySelector('#nav2 .dropdown-item').classList.contains('active')).toEqual(false)
+ expect(firstDropItem).toHaveClass('active')
+ expect(fixtureEl.querySelector('#nav1 .dropdown-toggle')).toHaveClass('active')
+ expect(fixtureEl.querySelector('#nav2 .dropdown-toggle')).not.toHaveClass('active')
+ expect(fixtureEl.querySelector('#nav2 .dropdown-item')).not.toHaveClass('active')
})
it('should support li > .dropdown-item', () => {
@@ -550,209 +1024,229 @@ describe('Tab', () => {
'</ul>'
].join('')
- const firstDropItem = fixtureEl.querySelector('.dropdown-item')
+ const dropItems = fixtureEl.querySelectorAll('.dropdown-item')
- firstDropItem.click()
- expect(firstDropItem.classList.contains('active')).toEqual(true)
- expect(fixtureEl.querySelector('.nav-link').classList.contains('active')).toEqual(false)
+ dropItems[1].click()
+ expect(dropItems[0]).not.toHaveClass('active')
+ expect(dropItems[1]).toHaveClass('active')
+ expect(fixtureEl.querySelector('.nav-link')).not.toHaveClass('active')
})
- it('should handle nested tabs', done => {
- fixtureEl.innerHTML = [
- '<nav class="nav nav-tabs" role="tablist">',
- ' <button type="button" id="tab1" data-bs-target="#x-tab1" class="nav-link" data-bs-toggle="tab" role="tab" aria-controls="x-tab1">Tab 1</button>',
- ' <button type="button" data-bs-target="#x-tab2" class="nav-link active" data-bs-toggle="tab" role="tab" aria-controls="x-tab2" aria-selected="true">Tab 2</button>',
- ' <button type="button" data-bs-target="#x-tab3" class="nav-link" data-bs-toggle="tab" role="tab" aria-controls="x-tab3">Tab 3</button>',
- '</nav>',
- '<div class="tab-content">',
- ' <div class="tab-pane" id="x-tab1" role="tabpanel">',
- ' <nav class="nav nav-tabs" role="tablist">',
- ' <button type="button" data-bs-target="#nested-tab1" class="nav-link active" data-bs-toggle="tab" role="tab" aria-controls="x-tab1" aria-selected="true">Nested Tab 1</button>',
- ' <button type="button" id="tabNested2" data-bs-target="#nested-tab2" class="nav-link" data-bs-toggle="tab" role="tab" aria-controls="x-profile">Nested Tab2</button>',
- ' </nav>',
- ' <div class="tab-content">',
- ' <div class="tab-pane active" id="nested-tab1" role="tabpanel">Nested Tab1 Content</div>',
- ' <div class="tab-pane" id="nested-tab2" role="tabpanel">Nested Tab2 Content</div>',
- ' </div>',
- ' </div>',
- ' <div class="tab-pane active" id="x-tab2" role="tabpanel">Tab2 Content</div>',
- ' <div class="tab-pane" id="x-tab3" role="tabpanel">Tab3 Content</div>',
- '</div>'
- ].join('')
-
- const tab1El = fixtureEl.querySelector('#tab1')
- const tabNested2El = fixtureEl.querySelector('#tabNested2')
- const xTab1El = fixtureEl.querySelector('#x-tab1')
+ it('should handle nested tabs', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<nav class="nav nav-tabs" role="tablist">',
+ ' <button type="button" id="tab1" data-bs-target="#x-tab1" class="nav-link" data-bs-toggle="tab" role="tab" aria-controls="x-tab1">Tab 1</button>',
+ ' <button type="button" data-bs-target="#x-tab2" class="nav-link active" data-bs-toggle="tab" role="tab" aria-controls="x-tab2" aria-selected="true">Tab 2</button>',
+ ' <button type="button" data-bs-target="#x-tab3" class="nav-link" data-bs-toggle="tab" role="tab" aria-controls="x-tab3">Tab 3</button>',
+ '</nav>',
+ '<div class="tab-content">',
+ ' <div class="tab-pane" id="x-tab1" role="tabpanel">',
+ ' <nav class="nav nav-tabs" role="tablist">',
+ ' <button type="button" data-bs-target="#nested-tab1" class="nav-link active" data-bs-toggle="tab" role="tab" aria-controls="x-tab1" aria-selected="true">Nested Tab 1</button>',
+ ' <button type="button" id="tabNested2" data-bs-target="#nested-tab2" class="nav-link" data-bs-toggle="tab" role="tab" aria-controls="x-profile">Nested Tab2</button>',
+ ' </nav>',
+ ' <div class="tab-content">',
+ ' <div class="tab-pane active" id="nested-tab1" role="tabpanel">Nested Tab1 Content</div>',
+ ' <div class="tab-pane" id="nested-tab2" role="tabpanel">Nested Tab2 Content</div>',
+ ' </div>',
+ ' </div>',
+ ' <div class="tab-pane active" id="x-tab2" role="tabpanel">Tab2 Content</div>',
+ ' <div class="tab-pane" id="x-tab3" role="tabpanel">Tab3 Content</div>',
+ '</div>'
+ ].join('')
+
+ const tab1El = fixtureEl.querySelector('#tab1')
+ const tabNested2El = fixtureEl.querySelector('#tabNested2')
+ const xTab1El = fixtureEl.querySelector('#x-tab1')
+
+ tabNested2El.addEventListener('shown.bs.tab', () => {
+ expect(xTab1El).toHaveClass('active')
+ resolve()
+ })
- tabNested2El.addEventListener('shown.bs.tab', () => {
- expect(xTab1El.classList.contains('active')).toEqual(true)
- done()
- })
+ tab1El.addEventListener('shown.bs.tab', () => {
+ expect(xTab1El).toHaveClass('active')
+ tabNested2El.click()
+ })
- tab1El.addEventListener('shown.bs.tab', () => {
- expect(xTab1El.classList.contains('active')).toEqual(true)
- tabNested2El.click()
+ tab1El.click()
})
-
- tab1El.click()
})
- it('should not remove fade class if no active pane is present', done => {
- fixtureEl.innerHTML = [
- '<ul class="nav nav-tabs" role="tablist">',
- ' <li class="nav-item" role="presentation"><button type="button" id="tab-home" data-bs-target="#home" class="nav-link" data-bs-toggle="tab" role="tab">Home</button></li>',
- ' <li class="nav-item" role="presentation"><button type="button" id="tab-profile" data-bs-target="#profile" class="nav-link" data-bs-toggle="tab" role="tab">Profile</button></li>',
- '</ul>',
- '<div class="tab-content">',
- ' <div class="tab-pane fade" id="home" role="tabpanel"></div>',
- ' <div class="tab-pane fade" id="profile" role="tabpanel"></div>',
- '</div>'
- ].join('')
-
- const triggerTabProfileEl = fixtureEl.querySelector('#tab-profile')
- const triggerTabHomeEl = fixtureEl.querySelector('#tab-home')
- const tabProfileEl = fixtureEl.querySelector('#profile')
- const tabHomeEl = fixtureEl.querySelector('#home')
-
- triggerTabProfileEl.addEventListener('shown.bs.tab', () => {
- expect(tabProfileEl.classList.contains('fade')).toEqual(true)
- expect(tabProfileEl.classList.contains('show')).toEqual(true)
+ it('should not remove fade class if no active pane is present', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<ul class="nav nav-tabs" role="tablist">',
+ ' <li class="nav-item" role="presentation"><button type="button" id="tab-home" data-bs-target="#home" class="nav-link" data-bs-toggle="tab" role="tab">Home</button></li>',
+ ' <li class="nav-item" role="presentation"><button type="button" id="tab-profile" data-bs-target="#profile" class="nav-link" data-bs-toggle="tab" role="tab">Profile</button></li>',
+ '</ul>',
+ '<div class="tab-content">',
+ ' <div class="tab-pane fade" id="home" role="tabpanel"></div>',
+ ' <div class="tab-pane fade" id="profile" role="tabpanel"></div>',
+ '</div>'
+ ].join('')
+
+ const triggerTabProfileEl = fixtureEl.querySelector('#tab-profile')
+ const triggerTabHomeEl = fixtureEl.querySelector('#tab-home')
+ const tabProfileEl = fixtureEl.querySelector('#profile')
+ const tabHomeEl = fixtureEl.querySelector('#home')
triggerTabHomeEl.addEventListener('shown.bs.tab', () => {
- expect(tabProfileEl.classList.contains('fade')).toEqual(true)
- expect(tabProfileEl.classList.contains('show')).toEqual(false)
+ setTimeout(() => {
+ expect(tabProfileEl).toHaveClass('fade')
+ expect(tabProfileEl).not.toHaveClass('show')
- expect(tabHomeEl.classList.contains('fade')).toEqual(true)
- expect(tabHomeEl.classList.contains('show')).toEqual(true)
+ expect(tabHomeEl).toHaveClass('fade')
+ expect(tabHomeEl).toHaveClass('show')
- done()
+ resolve()
+ }, 10)
})
- triggerTabHomeEl.click()
- })
+ triggerTabProfileEl.addEventListener('shown.bs.tab', () => {
+ setTimeout(() => {
+ expect(tabProfileEl).toHaveClass('fade')
+ expect(tabProfileEl).toHaveClass('show')
+ triggerTabHomeEl.click()
+ }, 10)
+ })
- triggerTabProfileEl.click()
+ triggerTabProfileEl.click()
+ })
})
- it('should not add show class to tab panes if there is no `.fade` class', done => {
- fixtureEl.innerHTML = [
- '<ul class="nav nav-tabs" role="tablist">',
- ' <li class="nav-item" role="presentation">',
- ' <button type="button" class="nav-link nav-tab" data-bs-target="#home" role="tab" data-bs-toggle="tab">Home</button>',
- ' </li>',
- ' <li class="nav-item" role="presentation">',
- ' <button type="button" id="secondNav" class="nav-link nav-tab" data-bs-target="#profile" role="tab" data-bs-toggle="tab">Profile</button>',
- ' </li>',
- '</ul>',
- '<div class="tab-content">',
- ' <div role="tabpanel" class="tab-pane" id="home">test 1</div>',
- ' <div role="tabpanel" class="tab-pane" id="profile">test 2</div>',
- '</div>'
- ].join('')
-
- const secondNavEl = fixtureEl.querySelector('#secondNav')
+ it('should add `show` class to tab panes if there is no `.fade` class', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<ul class="nav nav-tabs" role="tablist">',
+ ' <li class="nav-item" role="presentation">',
+ ' <button type="button" class="nav-link nav-tab" data-bs-target="#home" role="tab" data-bs-toggle="tab">Home</button>',
+ ' </li>',
+ ' <li class="nav-item" role="presentation">',
+ ' <button type="button" id="secondNav" class="nav-link nav-tab" data-bs-target="#profile" role="tab" data-bs-toggle="tab">Profile</button>',
+ ' </li>',
+ '</ul>',
+ '<div class="tab-content">',
+ ' <div role="tabpanel" class="tab-pane" id="home">test 1</div>',
+ ' <div role="tabpanel" class="tab-pane" id="profile">test 2</div>',
+ '</div>'
+ ].join('')
+
+ const secondNavEl = fixtureEl.querySelector('#secondNav')
+
+ secondNavEl.addEventListener('shown.bs.tab', () => {
+ expect(fixtureEl.querySelectorAll('.tab-content .show')).toHaveSize(1)
+ resolve()
+ })
- secondNavEl.addEventListener('shown.bs.tab', () => {
- expect(fixtureEl.querySelectorAll('.show').length).toEqual(0)
- done()
+ secondNavEl.click()
})
-
- secondNavEl.click()
})
- it('should add show class to tab panes if there is a `.fade` class', done => {
- fixtureEl.innerHTML = [
- '<ul class="nav nav-tabs" role="tablist">',
- ' <li class="nav-item" role="presentation">',
- ' <button type="button" class="nav-link nav-tab" data-bs-target="#home" role="tab" data-bs-toggle="tab">Home</button>',
- ' </li>',
- ' <li class="nav-item" role="presentation">',
- ' <button type="button" id="secondNav" class="nav-link nav-tab" data-bs-target="#profile" role="tab" data-bs-toggle="tab">Profile</button>',
- ' </li>',
- '</ul>',
- '<div class="tab-content">',
- ' <div role="tabpanel" class="tab-pane fade" id="home">test 1</div>',
- ' <div role="tabpanel" class="tab-pane fade" id="profile">test 2</div>',
- '</div>'
- ].join('')
-
- const secondNavEl = fixtureEl.querySelector('#secondNav')
+ it('should add show class to tab panes if there is a `.fade` class', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<ul class="nav nav-tabs" role="tablist">',
+ ' <li class="nav-item" role="presentation">',
+ ' <button type="button" class="nav-link nav-tab" data-bs-target="#home" role="tab" data-bs-toggle="tab">Home</button>',
+ ' </li>',
+ ' <li class="nav-item" role="presentation">',
+ ' <button type="button" id="secondNav" class="nav-link nav-tab" data-bs-target="#profile" role="tab" data-bs-toggle="tab">Profile</button>',
+ ' </li>',
+ '</ul>',
+ '<div class="tab-content">',
+ ' <div role="tabpanel" class="tab-pane fade" id="home">test 1</div>',
+ ' <div role="tabpanel" class="tab-pane fade" id="profile">test 2</div>',
+ '</div>'
+ ].join('')
+
+ const secondNavEl = fixtureEl.querySelector('#secondNav')
+
+ secondNavEl.addEventListener('shown.bs.tab', () => {
+ setTimeout(() => {
+ expect(fixtureEl.querySelectorAll('.show')).toHaveSize(1)
+ resolve()
+ }, 10)
+ })
- secondNavEl.addEventListener('shown.bs.tab', () => {
- expect(fixtureEl.querySelectorAll('.show').length).toEqual(1)
- done()
+ secondNavEl.click()
})
-
- secondNavEl.click()
})
- it('should prevent default when the trigger is <a> or <area>', done => {
- fixtureEl.innerHTML = [
- '<ul class="nav" role="tablist">',
- ' <li><a type="button" href="#test" class="active" role="tab" data-bs-toggle="tab">Home</a></li>',
- ' <li><a type="button" href="#test2" role="tab" data-bs-toggle="tab">Home</a></li>',
- '</ul>'
- ].join('')
-
- const tabEl = fixtureEl.querySelector('[href="#test2"]')
- spyOn(Event.prototype, 'preventDefault').and.callThrough()
+ it('should prevent default when the trigger is <a> or <area>', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<ul class="nav" role="tablist">',
+ ' <li><a type="button" href="#test" class="active" role="tab" data-bs-toggle="tab">Home</a></li>',
+ ' <li><a type="button" href="#test2" role="tab" data-bs-toggle="tab">Home</a></li>',
+ '</ul>'
+ ].join('')
+
+ const tabEl = fixtureEl.querySelector('[href="#test2"]')
+ const spy = spyOn(Event.prototype, 'preventDefault').and.callThrough()
+
+ tabEl.addEventListener('shown.bs.tab', () => {
+ expect(tabEl).toHaveClass('active')
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- tabEl.addEventListener('shown.bs.tab', () => {
- expect(tabEl.classList.contains('active')).toEqual(true)
- expect(Event.prototype.preventDefault).toHaveBeenCalled()
- done()
+ tabEl.click()
})
-
- tabEl.click()
})
- it('should not fire shown when tab has disabled attribute', done => {
- fixtureEl.innerHTML = [
- '<ul class="nav nav-tabs" role="tablist">',
- ' <li class="nav-item" role="presentation"><button type="button" data-bs-target="#home" class="nav-link active" role="tab" aria-selected="true">Home</button></li>',
- ' <li class="nav-item" role="presentation"><button type="button" data-bs-target="#profile" class="nav-link" disabled role="tab">Profile</button></li>',
- '</ul>',
- '<div class="tab-content">',
- ' <div class="tab-pane active" id="home" role="tabpanel"></div>',
- ' <div class="tab-pane" id="profile" role="tabpanel"></div>',
- '</div>'
- ].join('')
+ it('should not fire shown when tab has disabled attribute', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<ul class="nav nav-tabs" role="tablist">',
+ ' <li class="nav-item" role="presentation"><button type="button" data-bs-target="#home" class="nav-link active" role="tab" aria-selected="true">Home</button></li>',
+ ' <li class="nav-item" role="presentation"><button type="button" data-bs-target="#profile" class="nav-link" disabled role="tab">Profile</button></li>',
+ '</ul>',
+ '<div class="tab-content">',
+ ' <div class="tab-pane active" id="home" role="tabpanel"></div>',
+ ' <div class="tab-pane" id="profile" role="tabpanel"></div>',
+ '</div>'
+ ].join('')
+
+ const triggerDisabled = fixtureEl.querySelector('button[disabled]')
+ triggerDisabled.addEventListener('shown.bs.tab', () => {
+ reject(new Error('should not trigger shown event'))
+ })
- const triggerDisabled = fixtureEl.querySelector('button[disabled]')
- triggerDisabled.addEventListener('shown.bs.tab', () => {
- throw new Error('should not trigger shown event')
+ triggerDisabled.click()
+ setTimeout(() => {
+ expect().nothing()
+ resolve()
+ }, 30)
})
-
- triggerDisabled.click()
- setTimeout(() => {
- expect().nothing()
- done()
- }, 30)
})
- it('should not fire shown when tab has disabled class', done => {
- fixtureEl.innerHTML = [
- '<ul class="nav nav-tabs" role="tablist">',
- ' <li class="nav-item" role="presentation"><a href="#home" class="nav-link active" role="tab" aria-selected="true">Home</a></li>',
- ' <li class="nav-item" role="presentation"><a href="#profile" class="nav-link disabled" role="tab">Profile</a></li>',
- '</ul>',
- '<div class="tab-content">',
- ' <div class="tab-pane active" id="home" role="tabpanel"></div>',
- ' <div class="tab-pane" id="profile" role="tabpanel"></div>',
- '</div>'
- ].join('')
-
- const triggerDisabled = fixtureEl.querySelector('a.disabled')
+ it('should not fire shown when tab has disabled class', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<ul class="nav nav-tabs" role="tablist">',
+ ' <li class="nav-item" role="presentation"><a href="#home" class="nav-link active" role="tab" aria-selected="true">Home</a></li>',
+ ' <li class="nav-item" role="presentation"><a href="#profile" class="nav-link disabled" role="tab">Profile</a></li>',
+ '</ul>',
+ '<div class="tab-content">',
+ ' <div class="tab-pane active" id="home" role="tabpanel"></div>',
+ ' <div class="tab-pane" id="profile" role="tabpanel"></div>',
+ '</div>'
+ ].join('')
+
+ const triggerDisabled = fixtureEl.querySelector('a.disabled')
+
+ triggerDisabled.addEventListener('shown.bs.tab', () => {
+ reject(new Error('should not trigger shown event'))
+ })
- triggerDisabled.addEventListener('shown.bs.tab', () => {
- throw new Error('should not trigger shown event')
+ triggerDisabled.click()
+ setTimeout(() => {
+ expect().nothing()
+ resolve()
+ }, 30)
})
-
- triggerDisabled.click()
- setTimeout(() => {
- expect().nothing()
- done()
- }, 30)
})
})
})
diff --git a/js/tests/unit/toast.spec.js b/js/tests/unit/toast.spec.js
index 4b84bf2c5..200fe3e40 100644
--- a/js/tests/unit/toast.spec.js
+++ b/js/tests/unit/toast.spec.js
@@ -1,5 +1,7 @@
-import Toast from '../../src/toast'
-import { getFixture, clearFixture, createEvent, jQueryMock } from '../helpers/fixture'
+import Toast from '../../src/toast.js'
+import {
+ clearFixture, createEvent, getFixture, jQueryMock
+} from '../helpers/fixture.js'
describe('Toast', () => {
let fixtureEl
@@ -36,52 +38,56 @@ describe('Toast', () => {
expect(toastByElement._element).toEqual(toastEl)
})
- it('should allow to config in js', done => {
- fixtureEl.innerHTML = [
- '<div class="toast">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' </div>',
- '</div>'
- ].join('')
+ it('should allow to config in js', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="toast">',
+ ' <div class="toast-body">',
+ ' a simple toast',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const toastEl = fixtureEl.querySelector('div')
+ const toast = new Toast(toastEl, {
+ delay: 1
+ })
- const toastEl = fixtureEl.querySelector('div')
- const toast = new Toast(toastEl, {
- delay: 1
- })
+ toastEl.addEventListener('shown.bs.toast', () => {
+ expect(toastEl).toHaveClass('show')
+ resolve()
+ })
- toastEl.addEventListener('shown.bs.toast', () => {
- expect(toastEl.classList.contains('show')).toEqual(true)
- done()
+ toast.show()
})
-
- toast.show()
})
- it('should close toast when close element with data-bs-dismiss attribute is set', done => {
- fixtureEl.innerHTML = [
- '<div class="toast" data-bs-delay="1" data-bs-autohide="false" data-bs-animation="false">',
- ' <button type="button" class="ms-2 mb-1 btn-close" data-bs-dismiss="toast" aria-label="Close"></button>',
- '</div>'
- ].join('')
+ it('should close toast when close element with data-bs-dismiss attribute is set', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="toast" data-bs-delay="1" data-bs-autohide="false" data-bs-animation="false">',
+ ' <button type="button" class="ms-2 mb-1 btn-close" data-bs-dismiss="toast" aria-label="Close"></button>',
+ '</div>'
+ ].join('')
- const toastEl = fixtureEl.querySelector('div')
- const toast = new Toast(toastEl)
+ const toastEl = fixtureEl.querySelector('div')
+ const toast = new Toast(toastEl)
- toastEl.addEventListener('shown.bs.toast', () => {
- expect(toastEl.classList.contains('show')).toEqual(true)
+ toastEl.addEventListener('shown.bs.toast', () => {
+ expect(toastEl).toHaveClass('show')
- const button = toastEl.querySelector('.btn-close')
+ const button = toastEl.querySelector('.btn-close')
- button.click()
- })
+ button.click()
+ })
- toastEl.addEventListener('hidden.bs.toast', () => {
- expect(toastEl.classList.contains('show')).toEqual(false)
- done()
- })
+ toastEl.addEventListener('hidden.bs.toast', () => {
+ expect(toastEl).not.toHaveClass('show')
+ resolve()
+ })
- toast.show()
+ toast.show()
+ })
})
})
@@ -111,304 +117,324 @@ describe('Toast', () => {
})
describe('show', () => {
- it('should auto hide', done => {
- fixtureEl.innerHTML = [
- '<div class="toast" data-bs-delay="1">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' </div>',
- '</div>'
- ].join('')
-
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
-
- toastEl.addEventListener('hidden.bs.toast', () => {
- expect(toastEl.classList.contains('show')).toEqual(false)
- done()
- })
-
- toast.show()
- })
-
- it('should not add fade class', done => {
- fixtureEl.innerHTML = [
- '<div class="toast" data-bs-delay="1" data-bs-animation="false">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' </div>',
- '</div>'
- ].join('')
-
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
+ it('should auto hide', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="toast" data-bs-delay="1">',
+ ' <div class="toast-body">',
+ ' a simple toast',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const toastEl = fixtureEl.querySelector('.toast')
+ const toast = new Toast(toastEl)
+
+ toastEl.addEventListener('hidden.bs.toast', () => {
+ expect(toastEl).not.toHaveClass('show')
+ resolve()
+ })
- toastEl.addEventListener('shown.bs.toast', () => {
- expect(toastEl.classList.contains('fade')).toEqual(false)
- done()
+ toast.show()
})
-
- toast.show()
})
- it('should not trigger shown if show is prevented', done => {
- fixtureEl.innerHTML = [
- '<div class="toast" data-bs-delay="1" data-bs-animation="false">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' </div>',
- '</div>'
- ].join('')
+ it('should not add fade class', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="toast" data-bs-delay="1" data-bs-animation="false">',
+ ' <div class="toast-body">',
+ ' a simple toast',
+ ' </div>',
+ '</div>'
+ ].join('')
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
+ const toastEl = fixtureEl.querySelector('.toast')
+ const toast = new Toast(toastEl)
- const assertDone = () => {
- setTimeout(() => {
- expect(toastEl.classList.contains('show')).toEqual(false)
- done()
- }, 20)
- }
-
- toastEl.addEventListener('show.bs.toast', event => {
- event.preventDefault()
- assertDone()
- })
+ toastEl.addEventListener('shown.bs.toast', () => {
+ expect(toastEl).not.toHaveClass('fade')
+ resolve()
+ })
- toastEl.addEventListener('shown.bs.toast', () => {
- throw new Error('shown event should not be triggered if show is prevented')
+ toast.show()
})
-
- toast.show()
})
- it('should clear timeout if toast is shown again before it is hidden', done => {
- fixtureEl.innerHTML = [
- '<div class="toast">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' </div>',
- '</div>'
- ].join('')
-
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
+ it('should not trigger shown if show is prevented', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<div class="toast" data-bs-delay="1" data-bs-animation="false">',
+ ' <div class="toast-body">',
+ ' a simple toast',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const toastEl = fixtureEl.querySelector('.toast')
+ const toast = new Toast(toastEl)
+
+ const assertDone = () => {
+ setTimeout(() => {
+ expect(toastEl).not.toHaveClass('show')
+ resolve()
+ }, 20)
+ }
+
+ toastEl.addEventListener('show.bs.toast', event => {
+ event.preventDefault()
+ assertDone()
+ })
- setTimeout(() => {
- toast._config.autohide = false
toastEl.addEventListener('shown.bs.toast', () => {
- expect(toast._clearTimeout).toHaveBeenCalled()
- expect(toast._timeout).toBeNull()
- done()
+ reject(new Error('shown event should not be triggered if show is prevented'))
})
- toast.show()
- }, toast._config.delay / 2)
- spyOn(toast, '_clearTimeout').and.callThrough()
-
- toast.show()
+ toast.show()
+ })
})
- it('should clear timeout if toast is interacted with mouse', done => {
- fixtureEl.innerHTML = [
- '<div class="toast">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' </div>',
- '</div>'
- ].join('')
-
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
- const spy = spyOn(toast, '_clearTimeout').and.callThrough()
+ it('should clear timeout if toast is shown again before it is hidden', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="toast">',
+ ' <div class="toast-body">',
+ ' a simple toast',
+ ' </div>',
+ '</div>'
+ ].join('')
- setTimeout(() => {
- spy.calls.reset()
+ const toastEl = fixtureEl.querySelector('.toast')
+ const toast = new Toast(toastEl)
- toastEl.addEventListener('mouseover', () => {
- expect(toast._clearTimeout).toHaveBeenCalledTimes(1)
- expect(toast._timeout).toBeNull()
- done()
- })
+ setTimeout(() => {
+ toast._config.autohide = false
+ toastEl.addEventListener('shown.bs.toast', () => {
+ expect(spy).toHaveBeenCalled()
+ expect(toast._timeout).toBeNull()
+ resolve()
+ })
+ toast.show()
+ }, toast._config.delay / 2)
- const mouseOverEvent = createEvent('mouseover')
- toastEl.dispatchEvent(mouseOverEvent)
- }, toast._config.delay / 2)
+ const spy = spyOn(toast, '_clearTimeout').and.callThrough()
- toast.show()
+ toast.show()
+ })
})
- it('should clear timeout if toast is interacted with keyboard', done => {
- fixtureEl.innerHTML = [
- '<button id="outside-focusable">outside focusable</button>',
- '<div class="toast">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' <button>with a button</button>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should clear timeout if toast is interacted with mouse', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="toast">',
+ ' <div class="toast-body">',
+ ' a simple toast',
+ ' </div>',
+ '</div>'
+ ].join('')
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
- const spy = spyOn(toast, '_clearTimeout').and.callThrough()
+ const toastEl = fixtureEl.querySelector('.toast')
+ const toast = new Toast(toastEl)
+ const spy = spyOn(toast, '_clearTimeout').and.callThrough()
- setTimeout(() => {
- spy.calls.reset()
+ setTimeout(() => {
+ spy.calls.reset()
- toastEl.addEventListener('focusin', () => {
- expect(toast._clearTimeout).toHaveBeenCalledTimes(1)
- expect(toast._timeout).toBeNull()
- done()
- })
+ toastEl.addEventListener('mouseover', () => {
+ expect(toast._clearTimeout).toHaveBeenCalledTimes(1)
+ expect(toast._timeout).toBeNull()
+ resolve()
+ })
- const insideFocusable = toastEl.querySelector('button')
- insideFocusable.focus()
- }, toast._config.delay / 2)
+ const mouseOverEvent = createEvent('mouseover')
+ toastEl.dispatchEvent(mouseOverEvent)
+ }, toast._config.delay / 2)
- toast.show()
+ toast.show()
+ })
})
- it('should still auto hide after being interacted with mouse and keyboard', done => {
- fixtureEl.innerHTML = [
- '<button id="outside-focusable">outside focusable</button>',
- '<div class="toast">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' <button>with a button</button>',
- ' </div>',
- '</div>'
- ].join('')
+ it('should clear timeout if toast is interacted with keyboard', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<button id="outside-focusable">outside focusable</button>',
+ '<div class="toast">',
+ ' <div class="toast-body">',
+ ' a simple toast',
+ ' <button>with a button</button>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const toastEl = fixtureEl.querySelector('.toast')
+ const toast = new Toast(toastEl)
+ const spy = spyOn(toast, '_clearTimeout').and.callThrough()
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
+ setTimeout(() => {
+ spy.calls.reset()
+
+ toastEl.addEventListener('focusin', () => {
+ expect(toast._clearTimeout).toHaveBeenCalledTimes(1)
+ expect(toast._timeout).toBeNull()
+ resolve()
+ })
- setTimeout(() => {
- toastEl.addEventListener('mouseover', () => {
const insideFocusable = toastEl.querySelector('button')
insideFocusable.focus()
- })
+ }, toast._config.delay / 2)
- toastEl.addEventListener('focusin', () => {
- const mouseOutEvent = createEvent('mouseout')
- toastEl.dispatchEvent(mouseOutEvent)
- })
-
- toastEl.addEventListener('mouseout', () => {
- const outsideFocusable = document.getElementById('outside-focusable')
- outsideFocusable.focus()
- })
+ toast.show()
+ })
+ })
- toastEl.addEventListener('focusout', () => {
- expect(toast._timeout).not.toBeNull()
- done()
- })
+ it('should still auto hide after being interacted with mouse and keyboard', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<button id="outside-focusable">outside focusable</button>',
+ '<div class="toast">',
+ ' <div class="toast-body">',
+ ' a simple toast',
+ ' <button>with a button</button>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const toastEl = fixtureEl.querySelector('.toast')
+ const toast = new Toast(toastEl)
- const mouseOverEvent = createEvent('mouseover')
- toastEl.dispatchEvent(mouseOverEvent)
- }, toast._config.delay / 2)
+ setTimeout(() => {
+ toastEl.addEventListener('mouseover', () => {
+ const insideFocusable = toastEl.querySelector('button')
+ insideFocusable.focus()
+ })
+
+ toastEl.addEventListener('focusin', () => {
+ const mouseOutEvent = createEvent('mouseout')
+ toastEl.dispatchEvent(mouseOutEvent)
+ })
+
+ toastEl.addEventListener('mouseout', () => {
+ const outsideFocusable = document.getElementById('outside-focusable')
+ outsideFocusable.focus()
+ })
+
+ toastEl.addEventListener('focusout', () => {
+ expect(toast._timeout).not.toBeNull()
+ resolve()
+ })
+
+ const mouseOverEvent = createEvent('mouseover')
+ toastEl.dispatchEvent(mouseOverEvent)
+ }, toast._config.delay / 2)
- toast.show()
+ toast.show()
+ })
})
- it('should not auto hide if focus leaves but mouse pointer remains inside', done => {
- fixtureEl.innerHTML = [
- '<button id="outside-focusable">outside focusable</button>',
- '<div class="toast">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' <button>with a button</button>',
- ' </div>',
- '</div>'
- ].join('')
-
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
+ it('should not auto hide if focus leaves but mouse pointer remains inside', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<button id="outside-focusable">outside focusable</button>',
+ '<div class="toast">',
+ ' <div class="toast-body">',
+ ' a simple toast',
+ ' <button>with a button</button>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const toastEl = fixtureEl.querySelector('.toast')
+ const toast = new Toast(toastEl)
- setTimeout(() => {
- toastEl.addEventListener('mouseover', () => {
- const insideFocusable = toastEl.querySelector('button')
- insideFocusable.focus()
- })
+ setTimeout(() => {
+ toastEl.addEventListener('mouseover', () => {
+ const insideFocusable = toastEl.querySelector('button')
+ insideFocusable.focus()
+ })
- toastEl.addEventListener('focusin', () => {
- const outsideFocusable = document.getElementById('outside-focusable')
- outsideFocusable.focus()
- })
+ toastEl.addEventListener('focusin', () => {
+ const outsideFocusable = document.getElementById('outside-focusable')
+ outsideFocusable.focus()
+ })
- toastEl.addEventListener('focusout', () => {
- expect(toast._timeout).toBeNull()
- done()
- })
+ toastEl.addEventListener('focusout', () => {
+ expect(toast._timeout).toBeNull()
+ resolve()
+ })
- const mouseOverEvent = createEvent('mouseover')
- toastEl.dispatchEvent(mouseOverEvent)
- }, toast._config.delay / 2)
+ const mouseOverEvent = createEvent('mouseover')
+ toastEl.dispatchEvent(mouseOverEvent)
+ }, toast._config.delay / 2)
- toast.show()
+ toast.show()
+ })
})
- it('should not auto hide if mouse pointer leaves but focus remains inside', done => {
- fixtureEl.innerHTML = [
- '<button id="outside-focusable">outside focusable</button>',
- '<div class="toast">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' <button>with a button</button>',
- ' </div>',
- '</div>'
- ].join('')
-
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
+ it('should not auto hide if mouse pointer leaves but focus remains inside', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<button id="outside-focusable">outside focusable</button>',
+ '<div class="toast">',
+ ' <div class="toast-body">',
+ ' a simple toast',
+ ' <button>with a button</button>',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const toastEl = fixtureEl.querySelector('.toast')
+ const toast = new Toast(toastEl)
- setTimeout(() => {
- toastEl.addEventListener('mouseover', () => {
- const insideFocusable = toastEl.querySelector('button')
- insideFocusable.focus()
- })
+ setTimeout(() => {
+ toastEl.addEventListener('mouseover', () => {
+ const insideFocusable = toastEl.querySelector('button')
+ insideFocusable.focus()
+ })
- toastEl.addEventListener('focusin', () => {
- const mouseOutEvent = createEvent('mouseout')
- toastEl.dispatchEvent(mouseOutEvent)
- })
+ toastEl.addEventListener('focusin', () => {
+ const mouseOutEvent = createEvent('mouseout')
+ toastEl.dispatchEvent(mouseOutEvent)
+ })
- toastEl.addEventListener('mouseout', () => {
- expect(toast._timeout).toBeNull()
- done()
- })
+ toastEl.addEventListener('mouseout', () => {
+ expect(toast._timeout).toBeNull()
+ resolve()
+ })
- const mouseOverEvent = createEvent('mouseover')
- toastEl.dispatchEvent(mouseOverEvent)
- }, toast._config.delay / 2)
+ const mouseOverEvent = createEvent('mouseover')
+ toastEl.dispatchEvent(mouseOverEvent)
+ }, toast._config.delay / 2)
- toast.show()
+ toast.show()
+ })
})
})
describe('hide', () => {
- it('should allow to hide toast manually', done => {
- fixtureEl.innerHTML = [
- '<div class="toast" data-bs-delay="1" data-bs-autohide="false">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' </div>',
- ' </div>'
- ].join('')
+ it('should allow to hide toast manually', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="toast" data-bs-delay="1" data-bs-autohide="false">',
+ ' <div class="toast-body">',
+ ' a simple toast',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const toastEl = fixtureEl.querySelector('.toast')
+ const toast = new Toast(toastEl)
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
+ toastEl.addEventListener('shown.bs.toast', () => {
+ toast.hide()
+ })
- toastEl.addEventListener('shown.bs.toast', () => {
- toast.hide()
- })
+ toastEl.addEventListener('hidden.bs.toast', () => {
+ expect(toastEl).not.toHaveClass('show')
+ resolve()
+ })
- toastEl.addEventListener('hidden.bs.toast', () => {
- expect(toastEl.classList.contains('show')).toEqual(false)
- done()
+ toast.show()
})
-
- toast.show()
})
it('should do nothing when we call hide on a non shown toast', () => {
@@ -417,46 +443,48 @@ describe('Toast', () => {
const toastEl = fixtureEl.querySelector('div')
const toast = new Toast(toastEl)
- spyOn(toastEl.classList, 'contains')
+ const spy = spyOn(toastEl.classList, 'contains')
toast.hide()
- expect(toastEl.classList.contains).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
})
- it('should not trigger hidden if hide is prevented', done => {
- fixtureEl.innerHTML = [
- '<div class="toast" data-bs-delay="1" data-bs-animation="false">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' </div>',
- '</div>'
- ].join('')
+ it('should not trigger hidden if hide is prevented', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = [
+ '<div class="toast" data-bs-delay="1" data-bs-animation="false">',
+ ' <div class="toast-body">',
+ ' a simple toast',
+ ' </div>',
+ '</div>'
+ ].join('')
+
+ const toastEl = fixtureEl.querySelector('.toast')
+ const toast = new Toast(toastEl)
+
+ const assertDone = () => {
+ setTimeout(() => {
+ expect(toastEl).toHaveClass('show')
+ resolve()
+ }, 20)
+ }
- const toastEl = fixtureEl.querySelector('.toast')
- const toast = new Toast(toastEl)
-
- const assertDone = () => {
- setTimeout(() => {
- expect(toastEl.classList.contains('show')).toEqual(true)
- done()
- }, 20)
- }
+ toastEl.addEventListener('shown.bs.toast', () => {
+ toast.hide()
+ })
- toastEl.addEventListener('shown.bs.toast', () => {
- toast.hide()
- })
+ toastEl.addEventListener('hide.bs.toast', event => {
+ event.preventDefault()
+ assertDone()
+ })
- toastEl.addEventListener('hide.bs.toast', event => {
- event.preventDefault()
- assertDone()
- })
+ toastEl.addEventListener('hidden.bs.toast', () => {
+ reject(new Error('hidden event should not be triggered if hide is prevented'))
+ })
- toastEl.addEventListener('hidden.bs.toast', () => {
- throw new Error('hidden event should not be triggered if hide is prevented')
+ toast.show()
})
-
- toast.show()
})
})
@@ -475,34 +503,36 @@ describe('Toast', () => {
expect(Toast.getInstance(toastEl)).toBeNull()
})
- it('should allow to destroy toast and hide it before that', done => {
- fixtureEl.innerHTML = [
- '<div class="toast" data-bs-delay="0" data-bs-autohide="false">',
- ' <div class="toast-body">',
- ' a simple toast',
- ' </div>',
- '</div>'
- ].join('')
+ it('should allow to destroy toast and hide it before that', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="toast" data-bs-delay="0" data-bs-autohide="false">',
+ ' <div class="toast-body">',
+ ' a simple toast',
+ ' </div>',
+ '</div>'
+ ].join('')
- const toastEl = fixtureEl.querySelector('div')
- const toast = new Toast(toastEl)
- const expected = () => {
- expect(toastEl.classList.contains('show')).toEqual(true)
- expect(Toast.getInstance(toastEl)).not.toBeNull()
+ const toastEl = fixtureEl.querySelector('div')
+ const toast = new Toast(toastEl)
+ const expected = () => {
+ expect(toastEl).toHaveClass('show')
+ expect(Toast.getInstance(toastEl)).not.toBeNull()
- toast.dispose()
+ toast.dispose()
- expect(Toast.getInstance(toastEl)).toBeNull()
- expect(toastEl.classList.contains('show')).toEqual(false)
+ expect(Toast.getInstance(toastEl)).toBeNull()
+ expect(toastEl).not.toHaveClass('show')
- done()
- }
+ resolve()
+ }
- toastEl.addEventListener('shown.bs.toast', () => {
- setTimeout(expected, 1)
- })
+ toastEl.addEventListener('shown.bs.toast', () => {
+ setTimeout(expected, 1)
+ })
- toast.show()
+ toast.show()
+ })
})
})
@@ -540,7 +570,7 @@ describe('Toast', () => {
const div = fixtureEl.querySelector('div')
const toast = new Toast(div)
- spyOn(toast, 'show')
+ const spy = spyOn(toast, 'show')
jQueryMock.fn.toast = Toast.jQueryInterface
jQueryMock.elements = [div]
@@ -548,7 +578,7 @@ describe('Toast', () => {
jQueryMock.fn.toast.call(jQueryMock, 'show')
expect(Toast.getInstance(div)).toEqual(toast)
- expect(toast.show).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
})
it('should throw error on undefined method', () => {
@@ -582,7 +612,7 @@ describe('Toast', () => {
const div = fixtureEl.querySelector('div')
- expect(Toast.getInstance(div)).toEqual(null)
+ expect(Toast.getInstance(div)).toBeNull()
})
})
@@ -603,7 +633,7 @@ describe('Toast', () => {
const div = fixtureEl.querySelector('div')
- expect(Toast.getInstance(div)).toEqual(null)
+ expect(Toast.getInstance(div)).toBeNull()
expect(Toast.getOrCreateInstance(div)).toBeInstanceOf(Toast)
})
@@ -612,7 +642,7 @@ describe('Toast', () => {
const div = fixtureEl.querySelector('div')
- expect(Toast.getInstance(div)).toEqual(null)
+ expect(Toast.getInstance(div)).toBeNull()
const toast = Toast.getOrCreateInstance(div, {
delay: 1
})
diff --git a/js/tests/unit/tooltip.spec.js b/js/tests/unit/tooltip.spec.js
index 4a7022234..37f2c230d 100644
--- a/js/tests/unit/tooltip.spec.js
+++ b/js/tests/unit/tooltip.spec.js
@@ -1,7 +1,9 @@
-import Tooltip from '../../src/tooltip'
-import EventHandler from '../../src/dom/event-handler'
-import { noop } from '../../src/util/index'
-import { clearFixture, createEvent, getFixture, jQueryMock } from '../helpers/fixture'
+import EventHandler from '../../src/dom/event-handler.js'
+import Tooltip from '../../src/tooltip.js'
+import { noop } from '../../src/util/index.js'
+import {
+ clearFixture, createEvent, getFixture, jQueryMock
+} from '../helpers/fixture.js'
describe('Tooltip', () => {
let fixtureEl
@@ -42,12 +44,6 @@ describe('Tooltip', () => {
})
})
- describe('Event', () => {
- it('should return plugin events', () => {
- expect(Tooltip.Event).toEqual(jasmine.any(Object))
- })
- })
-
describe('EVENT_KEY', () => {
it('should return plugin event key', () => {
expect(Tooltip.EVENT_KEY).toEqual('.bs.tooltip')
@@ -62,7 +58,7 @@ describe('Tooltip', () => {
describe('constructor', () => {
it('should take care of element either passed as a CSS selector or DOM element', () => {
- fixtureEl.innerHTML = '<a href="#" id="tooltipEl" rel="tooltip" title="Nice and short title">'
+ fixtureEl.innerHTML = '<a href="#" id="tooltipEl" rel="tooltip" title="Nice and short title"></a>'
const tooltipEl = fixtureEl.querySelector('#tooltipEl')
const tooltipBySelector = new Tooltip('#tooltipEl')
@@ -73,16 +69,16 @@ describe('Tooltip', () => {
})
it('should not take care of disallowed data attributes', () => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-sanitize="false" title="Another tooltip">'
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-sanitize="false" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
- expect(tooltip._config.sanitize).toEqual(true)
+ expect(tooltip._config.sanitize).toBeTrue()
})
it('should convert title and content to string if numbers', () => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip">'
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl, {
@@ -94,56 +90,60 @@ describe('Tooltip', () => {
expect(tooltip._config.content).toEqual('7')
})
- it('should enable selector delegation', done => {
- fixtureEl.innerHTML = '<div></div>'
+ it('should enable selector delegation', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div></div>'
- const containerEl = fixtureEl.querySelector('div')
- const tooltipContainer = new Tooltip(containerEl, {
- selector: 'a[rel="tooltip"]',
- trigger: 'click'
- })
+ const containerEl = fixtureEl.querySelector('div')
+ const tooltipContainer = new Tooltip(containerEl, {
+ selector: 'a[rel="tooltip"]',
+ trigger: 'click'
+ })
- containerEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ containerEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipInContainerEl = containerEl.querySelector('a')
+ const tooltipInContainerEl = containerEl.querySelector('a')
- tooltipInContainerEl.addEventListener('shown.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).not.toBeNull()
- tooltipContainer.dispose()
- done()
- })
+ tooltipInContainerEl.addEventListener('shown.bs.tooltip', () => {
+ expect(document.querySelector('.tooltip')).not.toBeNull()
+ tooltipContainer.dispose()
+ resolve()
+ })
- tooltipInContainerEl.click()
+ tooltipInContainerEl.click()
+ })
})
- it('should create offset modifier when offset is passed as a function', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Offset from function">'
+ it('should create offset modifier when offset is passed as a function', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Offset from function"></a>'
- const getOffset = jasmine.createSpy('getOffset').and.returnValue([10, 20])
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- offset: getOffset,
- popperConfig: {
- onFirstUpdate: state => {
- expect(getOffset).toHaveBeenCalledWith({
- popper: state.rects.popper,
- reference: state.rects.reference,
- placement: state.placement
- }, tooltipEl)
- done()
+ const getOffset = jasmine.createSpy('getOffset').and.returnValue([10, 20])
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl, {
+ offset: getOffset,
+ popperConfig: {
+ onFirstUpdate(state) {
+ expect(getOffset).toHaveBeenCalledWith({
+ popper: state.rects.popper,
+ reference: state.rects.reference,
+ placement: state.placement
+ }, tooltipEl)
+ resolve()
+ }
}
- }
- })
+ })
- const offset = tooltip._getOffset()
+ const offset = tooltip._getOffset()
- expect(typeof offset).toEqual('function')
+ expect(offset).toEqual(jasmine.any(Function))
- tooltip.show()
+ tooltip.show()
+ })
})
it('should create offset modifier when offset option is passed in data attribute', () => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-offset="10,20" title="Another tooltip">'
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-offset="10,20" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@@ -152,7 +152,7 @@ describe('Tooltip', () => {
})
it('should allow to pass config to Popper with `popperConfig`', () => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip">'
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl, {
@@ -167,7 +167,7 @@ describe('Tooltip', () => {
})
it('should allow to pass config to Popper with `popperConfig` as a function', () => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip">'
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const getPopperConfig = jasmine.createSpy('getPopperConfig').and.returnValue({ placement: 'left' })
@@ -177,163 +177,189 @@ describe('Tooltip', () => {
const popperConfig = tooltip._getPopperConfig('top')
- expect(getPopperConfig).toHaveBeenCalled()
+ // Ensure that the function was called with the default config.
+ expect(getPopperConfig).toHaveBeenCalledWith(jasmine.objectContaining({
+ placement: jasmine.any(String)
+ }))
expect(popperConfig.placement).toEqual('left')
})
- })
- describe('enable', () => {
- it('should enable a tooltip', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should use original title, if not "data-bs-title" is given', () => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
- tooltip.enable()
+ expect(tooltip._getTitle()).toEqual('Another tooltip')
+ })
+ })
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).not.toBeNull()
- done()
- })
+ describe('enable', () => {
+ it('should enable a tooltip', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- tooltip.show()
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
+
+ tooltip.enable()
+
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ expect(document.querySelector('.tooltip')).not.toBeNull()
+ resolve()
+ })
+
+ tooltip.show()
+ })
})
})
describe('disable', () => {
- it('should disable tooltip', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should disable tooltip', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
- tooltip.disable()
+ tooltip.disable()
- tooltipEl.addEventListener('show.bs.tooltip', () => {
- throw new Error('should not show a disabled tooltip')
- })
+ tooltipEl.addEventListener('show.bs.tooltip', () => {
+ reject(new Error('should not show a disabled tooltip'))
+ })
- tooltip.show()
+ tooltip.show()
- setTimeout(() => {
- expect().nothing()
- done()
- }, 10)
+ setTimeout(() => {
+ expect().nothing()
+ resolve()
+ }, 10)
+ })
})
})
describe('toggleEnabled', () => {
it('should toggle enabled', () => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
- expect(tooltip._isEnabled).toEqual(true)
+ expect(tooltip._isEnabled).toBeTrue()
tooltip.toggleEnabled()
- expect(tooltip._isEnabled).toEqual(false)
+ expect(tooltip._isEnabled).toBeFalse()
})
})
describe('toggle', () => {
- it('should do nothing if disabled', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should do nothing if disabled', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
- tooltip.disable()
+ tooltip.disable()
- tooltipEl.addEventListener('show.bs.tooltip', () => {
- throw new Error('should not show a disabled tooltip')
- })
+ tooltipEl.addEventListener('show.bs.tooltip', () => {
+ reject(new Error('should not show a disabled tooltip'))
+ })
- tooltip.toggle()
+ tooltip.toggle()
- setTimeout(() => {
- expect().nothing()
- done()
- }, 10)
+ setTimeout(() => {
+ expect().nothing()
+ resolve()
+ }, 10)
+ })
})
- it('should show a tooltip', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should show a tooltip', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).not.toBeNull()
- done()
- })
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ expect(document.querySelector('.tooltip')).not.toBeNull()
+ resolve()
+ })
- tooltip.toggle()
+ tooltip.toggle()
+ })
})
- it('should call toggle and show the tooltip when trigger is "click"', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should call toggle and show the tooltip when trigger is "click"', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- trigger: 'click'
- })
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl, {
+ trigger: 'click'
+ })
- spyOn(tooltip, 'toggle').and.callThrough()
+ const spy = spyOn(tooltip, 'toggle').and.callThrough()
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(tooltip.toggle).toHaveBeenCalled()
- done()
- })
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- tooltipEl.click()
+ tooltipEl.click()
+ })
})
- it('should hide a tooltip', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should hide a tooltip', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
+
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ tooltip.toggle()
+ })
+
+ tooltipEl.addEventListener('hidden.bs.tooltip', () => {
+ expect(document.querySelector('.tooltip')).toBeNull()
+ resolve()
+ })
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
tooltip.toggle()
})
+ })
- tooltipEl.addEventListener('hidden.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).toBeNull()
- done()
- })
+ it('should call toggle and hide the tooltip when trigger is "click"', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- tooltip.toggle()
- })
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl, {
+ trigger: 'click'
+ })
- it('should call toggle and hide the tooltip when trigger is "click"', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ const spy = spyOn(tooltip, 'toggle').and.callThrough()
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- trigger: 'click'
- })
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ tooltipEl.click()
+ })
- spyOn(tooltip, 'toggle').and.callThrough()
+ tooltipEl.addEventListener('hidden.bs.tooltip', () => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
tooltipEl.click()
})
-
- tooltipEl.addEventListener('hidden.bs.tooltip', () => {
- expect(tooltip.toggle).toHaveBeenCalled()
- done()
- })
-
- tooltipEl.click()
})
})
describe('dispose', () => {
it('should destroy a tooltip', () => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const addEventSpy = spyOn(tooltipEl, 'addEventListener').and.callThrough()
@@ -354,246 +380,290 @@ describe('Tooltip', () => {
tooltip.dispose()
- expect(Tooltip.getInstance(tooltipEl)).toEqual(null)
+ expect(Tooltip.getInstance(tooltipEl)).toBeNull()
expect(removeEventSpy.calls.allArgs()).toEqual(expectedArgs)
})
- it('should destroy a tooltip after it is shown and hidden', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should destroy a tooltip after it is shown and hidden', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- tooltip.hide()
- })
- tooltipEl.addEventListener('hidden.bs.tooltip', () => {
- tooltip.dispose()
- expect(tooltip.tip).toEqual(null)
- expect(Tooltip.getInstance(tooltipEl)).toEqual(null)
- done()
- })
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ tooltip.hide()
+ })
+ tooltipEl.addEventListener('hidden.bs.tooltip', () => {
+ tooltip.dispose()
+ expect(tooltip.tip).toBeNull()
+ expect(Tooltip.getInstance(tooltipEl)).toBeNull()
+ resolve()
+ })
- tooltip.show()
+ tooltip.show()
+ })
})
- it('should destroy a tooltip and remove it from the dom', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should destroy a tooltip and remove it from the dom', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
+
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ expect(document.querySelector('.tooltip')).not.toBeNull()
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).not.toBeNull()
+ tooltip.dispose()
- tooltip.dispose()
+ expect(document.querySelector('.tooltip')).toBeNull()
+ resolve()
+ })
- expect(document.querySelector('.tooltip')).toBeNull()
- done()
+ tooltip.show()
})
+ })
- tooltip.show()
+ it('should destroy a tooltip and reset it\'s initial title', () => {
+ fixtureEl.innerHTML = [
+ '<span id="tooltipWithTitle" rel="tooltip" title="tooltipTitle"></span>',
+ '<span id="tooltipWithoutTitle" rel="tooltip" data-bs-title="tooltipTitle"></span>'
+ ].join('')
+
+ const tooltipWithTitleEl = fixtureEl.querySelector('#tooltipWithTitle')
+ const tooltip = new Tooltip('#tooltipWithTitle')
+ expect(tooltipWithTitleEl.getAttribute('title')).toBeNull()
+ tooltip.dispose()
+ expect(tooltipWithTitleEl.getAttribute('title')).toBe('tooltipTitle')
+
+ const tooltipWithoutTitleEl = fixtureEl.querySelector('#tooltipWithoutTitle')
+ const tooltip2 = new Tooltip('#tooltipWithTitle')
+ expect(tooltipWithoutTitleEl.getAttribute('title')).toBeNull()
+ tooltip2.dispose()
+ expect(tooltipWithoutTitleEl.getAttribute('title')).toBeNull()
})
})
describe('show', () => {
- it('should show a tooltip', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should show a tooltip', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- const tooltipShown = document.querySelector('.tooltip')
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ const tooltipShown = document.querySelector('.tooltip')
- expect(tooltipShown).not.toBeNull()
- expect(tooltipEl.getAttribute('aria-describedby')).toEqual(tooltipShown.getAttribute('id'))
- expect(tooltipShown.getAttribute('id')).toContain('tooltip')
- done()
- })
+ expect(tooltipShown).not.toBeNull()
+ expect(tooltipEl.getAttribute('aria-describedby')).toEqual(tooltipShown.getAttribute('id'))
+ expect(tooltipShown.getAttribute('id')).toContain('tooltip')
+ resolve()
+ })
- tooltip.show()
+ tooltip.show()
+ })
})
- it('should show a tooltip when hovering a children element', done => {
- fixtureEl.innerHTML =
- '<a href="#" rel="tooltip" title="Another tooltip">' +
- '<svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 100 100">' +
- '<rect width="100%" fill="#563d7c"/>' +
- '<circle cx="50" cy="50" r="30" fill="#fff"/>' +
- '</svg>' +
- '</a>'
+ it('should show a tooltip when hovering a child element', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<a href="#" rel="tooltip" title="Another tooltip">',
+ ' <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" viewBox="0 0 100 100">',
+ ' <rect width="100%" fill="#563d7c"/>',
+ ' <circle cx="50" cy="50" r="30" fill="#fff"/>',
+ ' </svg>',
+ '</a>'
+ ].join('')
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
- spyOn(tooltip, 'show')
+ const spy = spyOn(tooltip, 'show')
- tooltipEl.querySelector('rect').dispatchEvent(createEvent('mouseover', { bubbles: true }))
+ tooltipEl.querySelector('rect').dispatchEvent(createEvent('mouseover', { bubbles: true }))
- setTimeout(() => {
- expect(tooltip.show).toHaveBeenCalled()
- done()
- }, 0)
+ setTimeout(() => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ }, 0)
+ })
})
- it('should show a tooltip on mobile', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should show a tooltip on mobile', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
- document.documentElement.ontouchstart = noop
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
+ document.documentElement.ontouchstart = noop
- spyOn(EventHandler, 'on').and.callThrough()
+ const spy = spyOn(EventHandler, 'on').and.callThrough()
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).not.toBeNull()
- expect(EventHandler.on).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
- document.documentElement.ontouchstart = undefined
- done()
- })
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ expect(document.querySelector('.tooltip')).not.toBeNull()
+ expect(spy).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
+ document.documentElement.ontouchstart = undefined
+ resolve()
+ })
- tooltip.show()
+ tooltip.show()
+ })
})
- it('should show a tooltip relative to placement option', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should show a tooltip relative to placement option', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- placement: 'bottom'
- })
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl, {
+ placement: 'bottom'
+ })
- tooltipEl.addEventListener('inserted.bs.tooltip', () => {
- expect(tooltip.getTipElement().classList.contains('bs-tooltip-auto')).toEqual(true)
- })
+ tooltipEl.addEventListener('inserted.bs.tooltip', () => {
+ expect(tooltip._getTipElement()).toHaveClass('bs-tooltip-auto')
+ })
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(tooltip.getTipElement().classList.contains('bs-tooltip-auto')).toEqual(true)
- expect(tooltip.getTipElement().getAttribute('data-popper-placement')).toEqual('bottom')
- done()
- })
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ expect(tooltip._getTipElement()).toHaveClass('bs-tooltip-auto')
+ expect(tooltip._getTipElement().getAttribute('data-popper-placement')).toEqual('bottom')
+ resolve()
+ })
- tooltip.show()
+ tooltip.show()
+ })
})
- it('should not error when trying to show a tooltip that has been removed from the dom', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should not error when trying to show a tooltip that has been removed from the dom', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
- const firstCallback = () => {
- tooltipEl.removeEventListener('shown.bs.tooltip', firstCallback)
- let tooltipShown = document.querySelector('.tooltip')
+ const firstCallback = () => {
+ tooltipEl.removeEventListener('shown.bs.tooltip', firstCallback)
+ let tooltipShown = document.querySelector('.tooltip')
- tooltipShown.remove()
+ tooltipShown.remove()
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- tooltipShown = document.querySelector('.tooltip')
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ tooltipShown = document.querySelector('.tooltip')
- expect(tooltipShown).not.toBeNull()
- done()
- })
+ expect(tooltipShown).not.toBeNull()
+ resolve()
+ })
- tooltip.show()
- }
+ tooltip.show()
+ }
- tooltipEl.addEventListener('shown.bs.tooltip', firstCallback)
+ tooltipEl.addEventListener('shown.bs.tooltip', firstCallback)
- tooltip.show()
+ tooltip.show()
+ })
})
- it('should show a tooltip with a dom element container', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should show a tooltip with a dom element container', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- container: fixtureEl
- })
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl, {
+ container: fixtureEl
+ })
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(fixtureEl.querySelector('.tooltip')).not.toBeNull()
- done()
- })
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ expect(fixtureEl.querySelector('.tooltip')).not.toBeNull()
+ resolve()
+ })
- tooltip.show()
+ tooltip.show()
+ })
})
- it('should show a tooltip with a jquery element container', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should show a tooltip with a jquery element container', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- container: {
- 0: fixtureEl,
- jquery: 'jQuery'
- }
- })
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl, {
+ container: {
+ 0: fixtureEl,
+ jquery: 'jQuery'
+ }
+ })
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(fixtureEl.querySelector('.tooltip')).not.toBeNull()
- done()
- })
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ expect(fixtureEl.querySelector('.tooltip')).not.toBeNull()
+ resolve()
+ })
- tooltip.show()
+ tooltip.show()
+ })
})
- it('should show a tooltip with a selector in container', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should show a tooltip with a selector in container', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- container: '#fixture'
- })
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl, {
+ container: '#fixture'
+ })
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(fixtureEl.querySelector('.tooltip')).not.toBeNull()
- done()
- })
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ expect(fixtureEl.querySelector('.tooltip')).not.toBeNull()
+ resolve()
+ })
- tooltip.show()
+ tooltip.show()
+ })
})
- it('should show a tooltip with placement as a function', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should show a tooltip with placement as a function', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const spy = jasmine.createSpy('placement').and.returnValue('top')
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- placement: spy
- })
+ const spy = jasmine.createSpy('placement').and.returnValue('top')
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl, {
+ placement: spy
+ })
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).not.toBeNull()
- expect(spy).toHaveBeenCalled()
- done()
- })
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ expect(document.querySelector('.tooltip')).not.toBeNull()
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- tooltip.show()
+ tooltip.show()
+ })
})
- it('should show a tooltip without the animation', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should show a tooltip without the animation', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- animation: false
- })
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl, {
+ animation: false
+ })
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- const tip = document.querySelector('.tooltip')
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ const tip = document.querySelector('.tooltip')
- expect(tip).not.toBeNull()
- expect(tip.classList.contains('fade')).toEqual(false)
- done()
- })
+ expect(tip).not.toBeNull()
+ expect(tip).not.toHaveClass('fade')
+ resolve()
+ })
- tooltip.show()
+ tooltip.show()
+ })
})
it('should throw an error the element is not visible', () => {
- fixtureEl.innerHTML = '<a href="#" style="display: none" rel="tooltip" title="Another tooltip">'
+ fixtureEl.innerHTML = '<a href="#" style="display: none" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@@ -605,339 +675,382 @@ describe('Tooltip', () => {
}
})
- it('should not show a tooltip if show.bs.tooltip is prevented', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- const expectedDone = () => {
- setTimeout(() => {
- expect(document.querySelector('.tooltip')).toBeNull()
- done()
- }, 10)
- }
+ it('should not show a tooltip if show.bs.tooltip is prevented', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- tooltipEl.addEventListener('show.bs.tooltip', ev => {
- ev.preventDefault()
- expectedDone()
- })
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- throw new Error('Tooltip should not be shown')
- })
+ const expectedDone = () => {
+ setTimeout(() => {
+ expect(document.querySelector('.tooltip')).toBeNull()
+ resolve()
+ }, 10)
+ }
- tooltip.show()
- })
+ tooltipEl.addEventListener('show.bs.tooltip', ev => {
+ ev.preventDefault()
+ expectedDone()
+ })
- it('should show tooltip if leave event hasn\'t occurred before delay expires', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ reject(new Error('Tooltip should not be shown'))
+ })
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- delay: 150
+ tooltip.show()
})
-
- spyOn(tooltip, 'show')
-
- setTimeout(() => {
- expect(tooltip.show).not.toHaveBeenCalled()
- }, 100)
-
- setTimeout(() => {
- expect(tooltip.show).toHaveBeenCalled()
- done()
- }, 200)
-
- tooltipEl.dispatchEvent(createEvent('mouseover'))
})
- it('should not show tooltip if leave event occurs before delay expires', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should show tooltip if leave event hasn\'t occurred before delay expires', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- delay: 150
- })
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl, {
+ delay: 150
+ })
- spyOn(tooltip, 'show')
+ const spy = spyOn(tooltip, 'show')
- setTimeout(() => {
- expect(tooltip.show).not.toHaveBeenCalled()
- tooltipEl.dispatchEvent(createEvent('mouseover'))
- }, 100)
+ setTimeout(() => {
+ expect(spy).not.toHaveBeenCalled()
+ }, 100)
- setTimeout(() => {
- expect(tooltip.show).toHaveBeenCalled()
- expect(document.querySelectorAll('.tooltip').length).toEqual(0)
- done()
- }, 200)
+ setTimeout(() => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ }, 200)
- tooltipEl.dispatchEvent(createEvent('mouseover'))
+ tooltipEl.dispatchEvent(createEvent('mouseover'))
+ })
})
- it('should not hide tooltip if leave event occurs and enter event occurs within the hide delay', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should not show tooltip if leave event occurs before delay expires', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- delay: {
- show: 0,
- hide: 150
- }
- })
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl, {
+ delay: 150
+ })
- setTimeout(() => {
- expect(tooltip.getTipElement().classList.contains('show')).toEqual(true)
- tooltipEl.dispatchEvent(createEvent('mouseout'))
+ const spy = spyOn(tooltip, 'show')
setTimeout(() => {
- expect(tooltip.getTipElement().classList.contains('show')).toEqual(true)
+ expect(spy).not.toHaveBeenCalled()
tooltipEl.dispatchEvent(createEvent('mouseover'))
}, 100)
setTimeout(() => {
- expect(tooltip.getTipElement().classList.contains('show')).toEqual(true)
- expect(document.querySelectorAll('.tooltip').length).toEqual(1)
- done()
+ expect(spy).toHaveBeenCalled()
+ expect(document.querySelectorAll('.tooltip')).toHaveSize(0)
+ resolve()
}, 200)
- }, 0)
- tooltipEl.dispatchEvent(createEvent('mouseover'))
+ tooltipEl.dispatchEvent(createEvent('mouseover'))
+ })
})
- it('should not hide tooltip if leave event occurs and interaction remains inside trigger', done => {
- fixtureEl.innerHTML = [
- '<a href="#" rel="tooltip" title="Another tooltip">',
- '<b>Trigger</b>',
- 'the tooltip',
- '</a>'
- ]
+ it('should not hide tooltip if leave event occurs and enter event occurs within the hide delay', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip" data-bs-delay=\'{"show":0,"hide":150}\'>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
- const triggerChild = tooltipEl.querySelector('b')
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
- spyOn(tooltip, 'hide').and.callThrough()
+ expect(tooltip._config.delay).toEqual({ show: 0, hide: 150 })
- tooltipEl.addEventListener('mouseover', () => {
- const moveMouseToChildEvent = createEvent('mouseout')
- Object.defineProperty(moveMouseToChildEvent, 'relatedTarget', {
- value: triggerChild
- })
+ setTimeout(() => {
+ expect(tooltip._getTipElement()).toHaveClass('show')
+ tooltipEl.dispatchEvent(createEvent('mouseout'))
+
+ setTimeout(() => {
+ expect(tooltip._getTipElement()).toHaveClass('show')
+ tooltipEl.dispatchEvent(createEvent('mouseover'))
+ }, 100)
+
+ setTimeout(() => {
+ expect(tooltip._getTipElement()).toHaveClass('show')
+ expect(document.querySelectorAll('.tooltip')).toHaveSize(1)
+ resolve()
+ }, 200)
+ }, 10)
- tooltipEl.dispatchEvent(moveMouseToChildEvent)
+ tooltipEl.dispatchEvent(createEvent('mouseover'))
})
+ })
- tooltipEl.addEventListener('mouseout', () => {
- expect(tooltip.hide).not.toHaveBeenCalled()
- done()
- })
+ it('should not hide tooltip if leave event occurs and interaction remains inside trigger', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<a href="#" rel="tooltip" title="Another tooltip">',
+ '<b>Trigger</b>',
+ 'the tooltip',
+ '</a>'
+ ].join('')
- tooltipEl.dispatchEvent(createEvent('mouseover'))
- })
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
+ const triggerChild = tooltipEl.querySelector('b')
- it('should properly maintain tooltip state if leave event occurs and enter event occurs during hide transition', done => {
- // Style this tooltip to give it plenty of room for popper to do what it wants
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip" data-bs-placement="top" style="position:fixed;left:50%;top:50%;">Trigger</a>'
+ const spy = spyOn(tooltip, 'hide').and.callThrough()
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
+ tooltipEl.addEventListener('mouseover', () => {
+ const moveMouseToChildEvent = createEvent('mouseout')
+ Object.defineProperty(moveMouseToChildEvent, 'relatedTarget', {
+ value: triggerChild
+ })
+
+ tooltipEl.dispatchEvent(moveMouseToChildEvent)
+ })
+
+ tooltipEl.addEventListener('mouseout', () => {
+ expect(spy).not.toHaveBeenCalled()
+ resolve()
+ })
- spyOn(window, 'getComputedStyle').and.returnValue({
- transitionDuration: '0.15s',
- transitionDelay: '0s'
+ tooltipEl.dispatchEvent(createEvent('mouseover'))
})
+ })
- setTimeout(() => {
- expect(tooltip._popper).not.toBeNull()
- expect(tooltip.getTipElement().getAttribute('data-popper-placement')).toBe('top')
- tooltipEl.dispatchEvent(createEvent('mouseout'))
+ it('should properly maintain tooltip state if leave event occurs and enter event occurs during hide transition', () => {
+ return new Promise(resolve => {
+ // Style this tooltip to give it plenty of room for popper to do what it wants
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip" data-bs-placement="top" style="position:fixed;left:50%;top:50%;">Trigger</a>'
- setTimeout(() => {
- expect(tooltip.getTipElement().classList.contains('show')).toEqual(false)
- tooltipEl.dispatchEvent(createEvent('mouseover'))
- }, 100)
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
+
+ spyOn(window, 'getComputedStyle').and.returnValue({
+ transitionDuration: '0.15s',
+ transitionDelay: '0s'
+ })
setTimeout(() => {
expect(tooltip._popper).not.toBeNull()
- expect(tooltip.getTipElement().getAttribute('data-popper-placement')).toBe('top')
- done()
- }, 200)
- }, 0)
+ expect(tooltip._getTipElement().getAttribute('data-popper-placement')).toEqual('top')
+ tooltipEl.dispatchEvent(createEvent('mouseout'))
+
+ setTimeout(() => {
+ expect(tooltip._getTipElement()).not.toHaveClass('show')
+ tooltipEl.dispatchEvent(createEvent('mouseover'))
+ }, 100)
+
+ setTimeout(() => {
+ expect(tooltip._popper).not.toBeNull()
+ expect(tooltip._getTipElement().getAttribute('data-popper-placement')).toEqual('top')
+ resolve()
+ }, 200)
+ }, 10)
- tooltipEl.dispatchEvent(createEvent('mouseover'))
+ tooltipEl.dispatchEvent(createEvent('mouseover'))
+ })
})
- it('should only trigger inserted event if a new tooltip element was created', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should only trigger inserted event if a new tooltip element was created', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
-
- spyOn(window, 'getComputedStyle').and.returnValue({
- transitionDuration: '0.15s',
- transitionDelay: '0s'
- })
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
- const insertedFunc = jasmine.createSpy()
- tooltipEl.addEventListener('inserted.bs.tooltip', insertedFunc)
+ spyOn(window, 'getComputedStyle').and.returnValue({
+ transitionDuration: '0.15s',
+ transitionDelay: '0s'
+ })
- setTimeout(() => {
- expect(insertedFunc).toHaveBeenCalledTimes(1)
- tooltip.hide()
-
- setTimeout(() => {
- tooltip.show()
- }, 100)
+ const insertedFunc = jasmine.createSpy()
+ tooltipEl.addEventListener('inserted.bs.tooltip', insertedFunc)
setTimeout(() => {
expect(insertedFunc).toHaveBeenCalledTimes(1)
- done()
- }, 200)
- }, 0)
+ tooltip.hide()
- tooltip.show()
+ setTimeout(() => {
+ tooltip.show()
+ }, 100)
+
+ setTimeout(() => {
+ expect(insertedFunc).toHaveBeenCalledTimes(2)
+ resolve()
+ }, 200)
+ }, 0)
+
+ tooltip.show()
+ })
})
- it('should show a tooltip with custom class provided in data attributes', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip" data-bs-custom-class="custom-class">'
+ it('should show a tooltip with custom class provided in data attributes', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip" data-bs-custom-class="custom-class"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- const tip = document.querySelector('.tooltip')
- expect(tip).not.toBeNull()
- expect(tip.classList.contains('custom-class')).toBeTrue()
- done()
- })
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ const tip = document.querySelector('.tooltip')
+ expect(tip).not.toBeNull()
+ expect(tip).toHaveClass('custom-class')
+ resolve()
+ })
- tooltip.show()
+ tooltip.show()
+ })
})
- it('should show a tooltip with custom class provided as a string in config', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should show a tooltip with custom class provided as a string in config', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- customClass: 'custom-class custom-class-2'
- })
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl, {
+ customClass: 'custom-class custom-class-2'
+ })
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- const tip = document.querySelector('.tooltip')
- expect(tip).not.toBeNull()
- expect(tip.classList.contains('custom-class')).toBeTrue()
- expect(tip.classList.contains('custom-class-2')).toBeTrue()
- done()
- })
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ const tip = document.querySelector('.tooltip')
+ expect(tip).not.toBeNull()
+ expect(tip).toHaveClass('custom-class')
+ expect(tip).toHaveClass('custom-class-2')
+ resolve()
+ })
- tooltip.show()
+ tooltip.show()
+ })
})
- it('should show a tooltip with custom class provided as a function in config', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should show a tooltip with custom class provided as a function in config', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip" data-class-a="custom-class-a" data-class-b="custom-class-b"></a>'
- const spy = jasmine.createSpy('customClass').and.returnValue('custom-class')
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- customClass: spy
- })
+ const tooltipEl = fixtureEl.querySelector('a')
+ const spy = jasmine.createSpy('customClass').and.callFake(function (el) {
+ return `${el.dataset.classA} ${this.dataset.classB}`
+ })
+ const tooltip = new Tooltip(tooltipEl, {
+ customClass: spy
+ })
+
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ const tip = document.querySelector('.tooltip')
+ expect(tip).not.toBeNull()
+ expect(spy).toHaveBeenCalled()
+ expect(tip).toHaveClass('custom-class-a')
+ expect(tip).toHaveClass('custom-class-b')
+ resolve()
+ })
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- const tip = document.querySelector('.tooltip')
- expect(tip).not.toBeNull()
- expect(spy).toHaveBeenCalled()
- expect(tip.classList.contains('custom-class')).toBeTrue()
- done()
+ tooltip.show()
})
+ })
- tooltip.show()
+ it('should remove `title` attribute if exists', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
+
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
+
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ expect(tooltipEl.getAttribute('title')).toBeNull()
+ resolve()
+ })
+ tooltip.show()
+ })
})
})
describe('hide', () => {
- it('should hide a tooltip', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should hide a tooltip', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
- tooltipEl.addEventListener('shown.bs.tooltip', () => tooltip.hide())
- tooltipEl.addEventListener('hidden.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).toBeNull()
- expect(tooltipEl.getAttribute('aria-describedby')).toBeNull()
- done()
- })
+ tooltipEl.addEventListener('shown.bs.tooltip', () => tooltip.hide())
+ tooltipEl.addEventListener('hidden.bs.tooltip', () => {
+ expect(document.querySelector('.tooltip')).toBeNull()
+ expect(tooltipEl.getAttribute('aria-describedby')).toBeNull()
+ resolve()
+ })
- tooltip.show()
+ tooltip.show()
+ })
})
- it('should hide a tooltip on mobile', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should hide a tooltip on mobile', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
+ const spy = spyOn(EventHandler, 'off')
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- document.documentElement.ontouchstart = noop
- spyOn(EventHandler, 'off')
- tooltip.hide()
- })
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ document.documentElement.ontouchstart = noop
+ tooltip.hide()
+ })
- tooltipEl.addEventListener('hidden.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).toBeNull()
- expect(EventHandler.off).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
- document.documentElement.ontouchstart = undefined
- done()
- })
+ tooltipEl.addEventListener('hidden.bs.tooltip', () => {
+ expect(document.querySelector('.tooltip')).toBeNull()
+ expect(spy).toHaveBeenCalledWith(jasmine.any(Object), 'mouseover', noop)
+ document.documentElement.ontouchstart = undefined
+ resolve()
+ })
- tooltip.show()
+ tooltip.show()
+ })
})
- it('should hide a tooltip without animation', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should hide a tooltip without animation', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- animation: false
- })
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl, {
+ animation: false
+ })
- tooltipEl.addEventListener('shown.bs.tooltip', () => tooltip.hide())
- tooltipEl.addEventListener('hidden.bs.tooltip', () => {
- expect(document.querySelector('.tooltip')).toBeNull()
- expect(tooltipEl.getAttribute('aria-describedby')).toBeNull()
- done()
- })
+ tooltipEl.addEventListener('shown.bs.tooltip', () => tooltip.hide())
+ tooltipEl.addEventListener('hidden.bs.tooltip', () => {
+ expect(document.querySelector('.tooltip')).toBeNull()
+ expect(tooltipEl.getAttribute('aria-describedby')).toBeNull()
+ resolve()
+ })
- tooltip.show()
+ tooltip.show()
+ })
})
- it('should not hide a tooltip if hide event is prevented', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should not hide a tooltip if hide event is prevented', () => {
+ return new Promise((resolve, reject) => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const assertDone = () => {
- setTimeout(() => {
- expect(document.querySelector('.tooltip')).not.toBeNull()
- done()
- }, 20)
- }
+ const assertDone = () => {
+ setTimeout(() => {
+ expect(document.querySelector('.tooltip')).not.toBeNull()
+ resolve()
+ }, 20)
+ }
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- animation: false
- })
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl, {
+ animation: false
+ })
- tooltipEl.addEventListener('shown.bs.tooltip', () => tooltip.hide())
- tooltipEl.addEventListener('hide.bs.tooltip', event => {
- event.preventDefault()
- assertDone()
- })
- tooltipEl.addEventListener('hidden.bs.tooltip', () => {
- throw new Error('should not trigger hidden event')
- })
+ tooltipEl.addEventListener('shown.bs.tooltip', () => tooltip.hide())
+ tooltipEl.addEventListener('hide.bs.tooltip', event => {
+ event.preventDefault()
+ assertDone()
+ })
+ tooltipEl.addEventListener('hidden.bs.tooltip', () => {
+ reject(new Error('should not trigger hidden event'))
+ })
- tooltip.show()
+ tooltip.show()
+ })
})
it('should not throw error running hide if popper hasn\'t been shown', () => {
@@ -956,26 +1069,28 @@ describe('Tooltip', () => {
})
describe('update', () => {
- it('should call popper update', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('should call popper update', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- spyOn(tooltip._popper, 'update')
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ const spy = spyOn(tooltip._popper, 'update')
- tooltip.update()
+ tooltip.update()
- expect(tooltip._popper.update).toHaveBeenCalled()
- done()
- })
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- tooltip.show()
+ tooltip.show()
+ })
})
it('should do nothing if the tooltip is not shown', () => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@@ -985,140 +1100,122 @@ describe('Tooltip', () => {
})
})
- describe('isWithContent', () => {
+ describe('_isWithContent', () => {
it('should return true if there is content', () => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
- expect(tooltip.isWithContent()).toEqual(true)
+ expect(tooltip._isWithContent()).toBeTrue()
})
it('should return false if there is no content', () => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="">'
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title=""></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
- expect(tooltip.isWithContent()).toEqual(false)
+ expect(tooltip._isWithContent()).toBeFalse()
})
})
- describe('getTipElement', () => {
+ describe('_getTipElement', () => {
it('should create the tip element and return it', () => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
- spyOn(document, 'createElement').and.callThrough()
+ const spy = spyOn(document, 'createElement').and.callThrough()
- expect(tooltip.getTipElement()).toBeDefined()
- expect(document.createElement).toHaveBeenCalled()
+ expect(tooltip._getTipElement()).toBeDefined()
+ expect(spy).toHaveBeenCalled()
})
it('should return the created tip element', () => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
const spy = spyOn(document, 'createElement').and.callThrough()
- expect(tooltip.getTipElement()).toBeDefined()
+ expect(tooltip._getTipElement()).toBeDefined()
expect(spy).toHaveBeenCalled()
spy.calls.reset()
- expect(tooltip.getTipElement()).toBeDefined()
+ expect(tooltip._getTipElement()).toBeDefined()
expect(spy).not.toHaveBeenCalled()
})
})
describe('setContent', () => {
it('should set tip content', () => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl, { animation: false })
- const tip = tooltip.getTipElement()
+ const tip = tooltip._getTipElement()
tooltip.setContent(tip)
- expect(tip.classList.contains('show')).toEqual(false)
- expect(tip.classList.contains('fade')).toEqual(false)
+ expect(tip).not.toHaveClass('show')
+ expect(tip).not.toHaveClass('fade')
expect(tip.querySelector('.tooltip-inner').textContent).toEqual('Another tooltip')
})
it('should re-show tip if it was already shown', () => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-title="Another tooltip">'
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
tooltip.show()
- const tip = () => tooltip.getTipElement()
+ const tip = () => tooltip._getTipElement()
- expect(tip().classList.contains('show')).toEqual(true)
+ expect(tip()).toHaveClass('show')
tooltip.setContent({ '.tooltip-inner': 'foo' })
- expect(tip().classList.contains('show')).toEqual(true)
+ expect(tip()).toHaveClass('show')
expect(tip().querySelector('.tooltip-inner').textContent).toEqual('foo')
})
it('should keep tip hidden, if it was already hidden before', () => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-title="Another tooltip">'
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-bs-title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
- const tip = () => tooltip.getTipElement()
+ const tip = () => tooltip._getTipElement()
- expect(tip().classList.contains('show')).toEqual(false)
+ expect(tip()).not.toHaveClass('show')
tooltip.setContent({ '.tooltip-inner': 'foo' })
- expect(tip().classList.contains('show')).toEqual(false)
- expect(tip().querySelector('.tooltip-inner').textContent).toEqual('foo')
- })
- })
-
- describe('updateAttachment', () => {
- it('should use end class name when right placement specified', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
-
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- placement: 'right'
- })
-
- tooltipEl.addEventListener('inserted.bs.tooltip', () => {
- expect(tooltip.getTipElement().classList.contains('bs-tooltip-auto')).toEqual(true)
- done()
- })
-
+ expect(tip()).not.toHaveClass('show')
tooltip.show()
+ expect(tip().querySelector('.tooltip-inner').textContent).toEqual('foo')
})
- it('should use start class name when left placement specified', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ it('"setContent" should keep the initial template', () => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl, {
- placement: 'left'
- })
+ const tooltip = new Tooltip(tooltipEl)
- tooltipEl.addEventListener('inserted.bs.tooltip', () => {
- expect(tooltip.getTipElement().classList.contains('bs-tooltip-auto')).toEqual(true)
- done()
- })
+ tooltip.setContent({ '.tooltip-inner': 'foo' })
+ const tip = tooltip._getTipElement()
- tooltip.show()
+ expect(tip).toHaveClass('tooltip')
+ expect(tip).toHaveClass('bs-tooltip-auto')
+ expect(tip.querySelector('.tooltip-arrow')).not.toBeNull()
+ expect(tip.querySelector('.tooltip-inner')).not.toBeNull()
})
})
describe('setContent', () => {
it('should do nothing if the element is null', () => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
@@ -1130,7 +1227,8 @@ describe('Tooltip', () => {
it('should do nothing if the content is a child of the element', () => {
fixtureEl.innerHTML = [
'<a href="#" rel="tooltip" title="Another tooltip">',
- '<div id="childContent"></div>'
+ ' <div id="childContent"></div>',
+ '</a>'
].join('')
const tooltipEl = fixtureEl.querySelector('a')
@@ -1139,7 +1237,7 @@ describe('Tooltip', () => {
html: true
})
- tooltip.getTipElement().append(childContent)
+ tooltip._getTipElement().append(childContent)
tooltip.setContent({ '.tooltip': childContent })
expect().nothing()
@@ -1148,7 +1246,8 @@ describe('Tooltip', () => {
it('should add the content as a child of the element for jQuery elements', () => {
fixtureEl.innerHTML = [
'<a href="#" rel="tooltip" title="Another tooltip">',
- '<div id="childContent"></div>'
+ ' <div id="childContent"></div>',
+ '</a>'
].join('')
const tooltipEl = fixtureEl.querySelector('a')
@@ -1158,14 +1257,16 @@ describe('Tooltip', () => {
})
tooltip.setContent({ '.tooltip': { 0: childContent, jquery: 'jQuery' } })
+ tooltip.show()
- expect(childContent.parentNode).toEqual(tooltip.getTipElement())
+ expect(childContent.parentNode).toEqual(tooltip._getTipElement())
})
it('should add the child text content in the element', () => {
fixtureEl.innerHTML = [
'<a href="#" rel="tooltip" title="Another tooltip">',
- '<div id="childContent">Tooltip</div>'
+ ' <div id="childContent">Tooltip</div>',
+ '</a>'
].join('')
const tooltipEl = fixtureEl.querySelector('a')
@@ -1174,11 +1275,11 @@ describe('Tooltip', () => {
tooltip.setContent({ '.tooltip': childContent })
- expect(childContent.textContent).toEqual(tooltip.getTipElement().textContent)
+ expect(childContent.textContent).toEqual(tooltip._getTipElement().textContent)
})
it('should add html without sanitize it', () => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl, {
@@ -1188,11 +1289,11 @@ describe('Tooltip', () => {
tooltip.setContent({ '.tooltip': '<div id="childContent">Tooltip</div>' })
- expect(tooltip.getTipElement().querySelector('div').id).toEqual('childContent')
+ expect(tooltip._getTipElement().querySelector('div').id).toEqual('childContent')
})
it('should add html sanitized', () => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl, {
@@ -1201,35 +1302,35 @@ describe('Tooltip', () => {
const content = [
'<div id="childContent">',
- ' <button type="button">test btn</button>',
+ ' <button type="button">test btn</button>',
'</div>'
].join('')
tooltip.setContent({ '.tooltip': content })
- expect(tooltip.getTipElement().querySelector('div').id).toEqual('childContent')
- expect(tooltip.getTipElement().querySelector('button')).toEqual(null)
+ expect(tooltip._getTipElement().querySelector('div').id).toEqual('childContent')
+ expect(tooltip._getTipElement().querySelector('button')).toBeNull()
})
it('should add text content', () => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
tooltip.setContent({ '.tooltip': 'test' })
- expect(tooltip.getTipElement().textContent).toEqual('test')
+ expect(tooltip._getTipElement().textContent).toEqual('test')
})
})
- describe('getTitle', () => {
+ describe('_getTitle', () => {
it('should return the title', () => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">'
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
const tooltipEl = fixtureEl.querySelector('a')
const tooltip = new Tooltip(tooltipEl)
- expect(tooltip.getTitle()).toEqual('Another tooltip')
+ expect(tooltip._getTitle()).toEqual('Another tooltip')
})
it('should call title function', () => {
@@ -1240,7 +1341,33 @@ describe('Tooltip', () => {
title: () => 'test'
})
- expect(tooltip.getTitle()).toEqual('test')
+ expect(tooltip._getTitle()).toEqual('test')
+ })
+
+ it('should call title function with trigger element', () => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-foo="bar"></a>'
+
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl, {
+ title(el) {
+ return el.dataset.foo
+ }
+ })
+
+ expect(tooltip._getTitle()).toEqual('bar')
+ })
+
+ it('should call title function with correct this value', () => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" data-foo="bar"></a>'
+
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl, {
+ title() {
+ return this.dataset.foo
+ }
+ })
+
+ expect(tooltip._getTitle()).toEqual('bar')
})
})
@@ -1260,60 +1387,85 @@ describe('Tooltip', () => {
const div = fixtureEl.querySelector('div')
- expect(Tooltip.getInstance(div)).toEqual(null)
+ expect(Tooltip.getInstance(div)).toBeNull()
})
})
describe('aria-label', () => {
- it('should add the aria-label attribute for referencing original title', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
+ it('should add the aria-label attribute for referencing original title', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- const tooltipShown = document.querySelector('.tooltip')
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ const tooltipShown = document.querySelector('.tooltip')
- expect(tooltipShown).not.toBeNull()
- expect(tooltipEl.getAttribute('aria-label')).toEqual('Another tooltip')
- done()
- })
+ expect(tooltipShown).not.toBeNull()
+ expect(tooltipEl.getAttribute('aria-label')).toEqual('Another tooltip')
+ resolve()
+ })
- tooltip.show()
+ tooltip.show()
+ })
})
- it('should not add the aria-label attribute if the attribute already exists', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" aria-label="Different label" title="Another tooltip"></a>'
+ it('should add the aria-label attribute when element text content is a whitespace string', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="A tooltip"><span> </span></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- const tooltipShown = document.querySelector('.tooltip')
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ const tooltipShown = document.querySelector('.tooltip')
- expect(tooltipShown).not.toBeNull()
- expect(tooltipEl.getAttribute('aria-label')).toEqual('Different label')
- done()
- })
+ expect(tooltipShown).not.toBeNull()
+ expect(tooltipEl.getAttribute('aria-label')).toEqual('A tooltip')
+ resolve()
+ })
- tooltip.show()
+ tooltip.show()
+ })
})
- it('should not add the aria-label attribute if the element has text content', done => {
- fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">text content</a>'
+ it('should not add the aria-label attribute if the attribute already exists', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" aria-label="Different label" title="Another tooltip"></a>'
- const tooltipEl = fixtureEl.querySelector('a')
- const tooltip = new Tooltip(tooltipEl)
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
+
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ const tooltipShown = document.querySelector('.tooltip')
- tooltipEl.addEventListener('shown.bs.tooltip', () => {
- const tooltipShown = document.querySelector('.tooltip')
+ expect(tooltipShown).not.toBeNull()
+ expect(tooltipEl.getAttribute('aria-label')).toEqual('Different label')
+ resolve()
+ })
- expect(tooltipShown).not.toBeNull()
- expect(tooltipEl.getAttribute('aria-label')).toBeNull()
- done()
+ tooltip.show()
})
+ })
- tooltip.show()
+ it('should not add the aria-label attribute if the element has text content', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<a href="#" rel="tooltip" title="Another tooltip">text content</a>'
+
+ const tooltipEl = fixtureEl.querySelector('a')
+ const tooltip = new Tooltip(tooltipEl)
+
+ tooltipEl.addEventListener('shown.bs.tooltip', () => {
+ const tooltipShown = document.querySelector('.tooltip')
+
+ expect(tooltipShown).not.toBeNull()
+ expect(tooltipEl.getAttribute('aria-label')).toBeNull()
+ resolve()
+ })
+
+ tooltip.show()
+ })
})
})
@@ -1334,7 +1486,7 @@ describe('Tooltip', () => {
const div = fixtureEl.querySelector('div')
- expect(Tooltip.getInstance(div)).toEqual(null)
+ expect(Tooltip.getInstance(div)).toBeNull()
expect(Tooltip.getOrCreateInstance(div)).toBeInstanceOf(Tooltip)
})
@@ -1343,13 +1495,13 @@ describe('Tooltip', () => {
const div = fixtureEl.querySelector('div')
- expect(Tooltip.getInstance(div)).toEqual(null)
+ expect(Tooltip.getInstance(div)).toBeNull()
const tooltip = Tooltip.getOrCreateInstance(div, {
title: () => 'test'
})
expect(tooltip).toBeInstanceOf(Tooltip)
- expect(tooltip.getTitle()).toEqual('test')
+ expect(tooltip._getTitle()).toEqual('test')
})
it('should return the instance when exists without given configuration', () => {
@@ -1367,7 +1519,7 @@ describe('Tooltip', () => {
expect(tooltip).toBeInstanceOf(Tooltip)
expect(tooltip2).toEqual(tooltip)
- expect(tooltip2.getTitle()).toEqual('nothing')
+ expect(tooltip2._getTitle()).toEqual('nothing')
})
})
@@ -1405,7 +1557,7 @@ describe('Tooltip', () => {
const div = fixtureEl.querySelector('div')
const tooltip = new Tooltip(div)
- spyOn(tooltip, 'show')
+ const spy = spyOn(tooltip, 'show')
jQueryMock.fn.tooltip = Tooltip.jQueryInterface
jQueryMock.elements = [div]
@@ -1413,7 +1565,7 @@ describe('Tooltip', () => {
jQueryMock.fn.tooltip.call(jQueryMock, 'show')
expect(Tooltip.getInstance(div)).toEqual(tooltip)
- expect(tooltip.show).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
})
it('should throw error on undefined method', () => {
diff --git a/js/tests/unit/util/backdrop.spec.js b/js/tests/unit/util/backdrop.spec.js
index 818ddf221..0faaac6a5 100644
--- a/js/tests/unit/util/backdrop.spec.js
+++ b/js/tests/unit/util/backdrop.spec.js
@@ -1,6 +1,6 @@
-import Backdrop from '../../../src/util/backdrop'
-import { getTransitionDurationFromElement } from '../../../src/util/index'
-import { clearFixture, getFixture } from '../../helpers/fixture'
+import Backdrop from '../../../src/util/backdrop.js'
+import { getTransitionDurationFromElement } from '../../../src/util/index.js'
+import { clearFixture, getFixture } from '../../helpers/fixture.js'
const CLASS_BACKDROP = '.modal-backdrop'
const CLASS_NAME_FADE = 'fade'
@@ -23,270 +23,297 @@ describe('Backdrop', () => {
})
describe('show', () => {
- it('if it is "shown", should append the backdrop html once, on show, and contain "show" class', done => {
- const instance = new Backdrop({
- isVisible: true,
- isAnimated: false
- })
- const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
+ it('should append the backdrop html once on show and include the "show" class if it is "shown"', () => {
+ return new Promise(resolve => {
+ const instance = new Backdrop({
+ isVisible: true,
+ isAnimated: false
+ })
+ const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
- expect(getElements().length).toEqual(0)
+ expect(getElements()).toHaveSize(0)
- instance.show()
- instance.show(() => {
- expect(getElements().length).toEqual(1)
- for (const el of getElements()) {
- expect(el.classList.contains(CLASS_NAME_SHOW)).toEqual(true)
- }
+ instance.show()
+ instance.show(() => {
+ expect(getElements()).toHaveSize(1)
+ for (const el of getElements()) {
+ expect(el).toHaveClass(CLASS_NAME_SHOW)
+ }
- done()
+ resolve()
+ })
})
})
- it('if it is not "shown", should not append the backdrop html', done => {
- const instance = new Backdrop({
- isVisible: false,
- isAnimated: true
- })
- const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
+ it('should not append the backdrop html if it is not "shown"', () => {
+ return new Promise(resolve => {
+ const instance = new Backdrop({
+ isVisible: false,
+ isAnimated: true
+ })
+ const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
- expect(getElements().length).toEqual(0)
- instance.show(() => {
- expect(getElements().length).toEqual(0)
- done()
+ expect(getElements()).toHaveSize(0)
+ instance.show(() => {
+ expect(getElements()).toHaveSize(0)
+ resolve()
+ })
})
})
- it('if it is "shown" and "animated", should append the backdrop html once, and contain "fade" class', done => {
- const instance = new Backdrop({
- isVisible: true,
- isAnimated: true
- })
- const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
+ it('should append the backdrop html once and include the "fade" class if it is "shown" and "animated"', () => {
+ return new Promise(resolve => {
+ const instance = new Backdrop({
+ isVisible: true,
+ isAnimated: true
+ })
+ const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
- expect(getElements().length).toEqual(0)
+ expect(getElements()).toHaveSize(0)
- instance.show(() => {
- expect(getElements().length).toEqual(1)
- for (const el of getElements()) {
- expect(el.classList.contains(CLASS_NAME_FADE)).toEqual(true)
- }
+ instance.show(() => {
+ expect(getElements()).toHaveSize(1)
+ for (const el of getElements()) {
+ expect(el).toHaveClass(CLASS_NAME_FADE)
+ }
- done()
+ resolve()
+ })
})
})
})
describe('hide', () => {
- it('should remove the backdrop html', done => {
- const instance = new Backdrop({
- isVisible: true,
- isAnimated: true
- })
+ it('should remove the backdrop html', () => {
+ return new Promise(resolve => {
+ const instance = new Backdrop({
+ isVisible: true,
+ isAnimated: true
+ })
- const getElements = () => document.body.querySelectorAll(CLASS_BACKDROP)
+ const getElements = () => document.body.querySelectorAll(CLASS_BACKDROP)
- expect(getElements().length).toEqual(0)
- instance.show(() => {
- expect(getElements().length).toEqual(1)
- instance.hide(() => {
- expect(getElements().length).toEqual(0)
- done()
+ expect(getElements()).toHaveSize(0)
+ instance.show(() => {
+ expect(getElements()).toHaveSize(1)
+ instance.hide(() => {
+ expect(getElements()).toHaveSize(0)
+ resolve()
+ })
})
})
})
- it('should remove "show" class', done => {
- const instance = new Backdrop({
- isVisible: true,
- isAnimated: true
- })
- const elem = instance._getElement()
+ it('should remove the "show" class', () => {
+ return new Promise(resolve => {
+ const instance = new Backdrop({
+ isVisible: true,
+ isAnimated: true
+ })
+ const elem = instance._getElement()
- instance.show()
- instance.hide(() => {
- expect(elem.classList.contains(CLASS_NAME_SHOW)).toEqual(false)
- done()
+ instance.show()
+ instance.hide(() => {
+ expect(elem).not.toHaveClass(CLASS_NAME_SHOW)
+ resolve()
+ })
})
})
- it('if it is not "shown", should not try to remove Node on remove method', done => {
- const instance = new Backdrop({
- isVisible: false,
- isAnimated: true
- })
- const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
- const spy = spyOn(instance, 'dispose').and.callThrough()
+ it('should not try to remove Node on remove method if it is not "shown"', () => {
+ return new Promise(resolve => {
+ const instance = new Backdrop({
+ isVisible: false,
+ isAnimated: true
+ })
+ const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
+ const spy = spyOn(instance, 'dispose').and.callThrough()
- expect(getElements().length).toEqual(0)
- expect(instance._isAppended).toEqual(false)
- instance.show(() => {
- instance.hide(() => {
- expect(getElements().length).toEqual(0)
- expect(spy).not.toHaveBeenCalled()
- expect(instance._isAppended).toEqual(false)
- done()
+ expect(getElements()).toHaveSize(0)
+ expect(instance._isAppended).toBeFalse()
+ instance.show(() => {
+ instance.hide(() => {
+ expect(getElements()).toHaveSize(0)
+ expect(spy).not.toHaveBeenCalled()
+ expect(instance._isAppended).toBeFalse()
+ resolve()
+ })
})
})
})
- it('should not error if the backdrop no longer has a parent', done => {
- fixtureEl.innerHTML = '<div id="wrapper"></div>'
+ it('should not error if the backdrop no longer has a parent', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div id="wrapper"></div>'
- const wrapper = fixtureEl.querySelector('#wrapper')
- const instance = new Backdrop({
- isVisible: true,
- isAnimated: true,
- rootElement: wrapper
- })
+ const wrapper = fixtureEl.querySelector('#wrapper')
+ const instance = new Backdrop({
+ isVisible: true,
+ isAnimated: true,
+ rootElement: wrapper
+ })
- const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
+ const getElements = () => document.querySelectorAll(CLASS_BACKDROP)
- instance.show(() => {
- wrapper.remove()
- instance.hide(() => {
- expect(getElements().length).toEqual(0)
- done()
+ instance.show(() => {
+ wrapper.remove()
+ instance.hide(() => {
+ expect(getElements()).toHaveSize(0)
+ resolve()
+ })
})
})
})
})
describe('click callback', () => {
- it('it should execute callback on click', done => {
- const spy = jasmine.createSpy('spy')
+ it('should execute callback on click', () => {
+ return new Promise(resolve => {
+ const spy = jasmine.createSpy('spy')
- const instance = new Backdrop({
- isVisible: true,
- isAnimated: false,
- clickCallback: () => spy()
- })
- const endTest = () => {
- setTimeout(() => {
- expect(spy).toHaveBeenCalled()
- done()
- }, 10)
- }
-
- instance.show(() => {
- const clickEvent = document.createEvent('MouseEvents')
- clickEvent.initEvent('mousedown', true, true)
- document.querySelector(CLASS_BACKDROP).dispatchEvent(clickEvent)
- endTest()
- })
- })
- })
-
- describe('animation callbacks', () => {
- it('if it is animated, should show and hide backdrop after counting transition duration', done => {
- const instance = new Backdrop({
- isVisible: true,
- isAnimated: true
- })
- const spy2 = jasmine.createSpy('spy2')
-
- const execDone = () => {
- setTimeout(() => {
- expect(spy2).toHaveBeenCalledTimes(2)
- done()
- }, 10)
- }
-
- instance.show(spy2)
- instance.hide(() => {
- spy2()
- execDone()
- })
- expect(spy2).not.toHaveBeenCalled()
- })
+ const instance = new Backdrop({
+ isVisible: true,
+ isAnimated: false,
+ clickCallback: () => spy()
+ })
+ const endTest = () => {
+ setTimeout(() => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ }, 10)
+ }
- it('if it is not animated, should show and hide backdrop without delay', done => {
- const spy = jasmine.createSpy('spy', getTransitionDurationFromElement)
- const instance = new Backdrop({
- isVisible: true,
- isAnimated: false
+ instance.show(() => {
+ const clickEvent = new Event('mousedown', { bubbles: true, cancelable: true })
+ document.querySelector(CLASS_BACKDROP).dispatchEvent(clickEvent)
+ endTest()
+ })
})
- const spy2 = jasmine.createSpy('spy2')
-
- instance.show(spy2)
- instance.hide(spy2)
-
- setTimeout(() => {
- expect(spy2).toHaveBeenCalled()
- expect(spy).not.toHaveBeenCalled()
- done()
- }, 10)
})
- it('if it is not "shown", should not call delay callbacks', done => {
- const instance = new Backdrop({
- isVisible: false,
- isAnimated: true
+ describe('animation callbacks', () => {
+ it('should show and hide backdrop after counting transition duration if it is animated', () => {
+ return new Promise(resolve => {
+ const instance = new Backdrop({
+ isVisible: true,
+ isAnimated: true
+ })
+ const spy2 = jasmine.createSpy('spy2')
+
+ const execDone = () => {
+ setTimeout(() => {
+ expect(spy2).toHaveBeenCalledTimes(2)
+ resolve()
+ }, 10)
+ }
+
+ instance.show(spy2)
+ instance.hide(() => {
+ spy2()
+ execDone()
+ })
+ expect(spy2).not.toHaveBeenCalled()
+ })
})
- const spy = jasmine.createSpy('spy', getTransitionDurationFromElement)
- instance.show()
- instance.hide(() => {
- expect(spy).not.toHaveBeenCalled()
- done()
- })
- })
- })
- describe('Config', () => {
- describe('rootElement initialization', () => {
- it('Should be appended on "document.body" by default', done => {
- const instance = new Backdrop({
- isVisible: true
- })
- const getElement = () => document.querySelector(CLASS_BACKDROP)
- instance.show(() => {
- expect(getElement().parentElement).toEqual(document.body)
- done()
+ it('should show and hide backdrop without a delay if it is not animated', () => {
+ return new Promise(resolve => {
+ const spy = jasmine.createSpy('spy', getTransitionDurationFromElement)
+ const instance = new Backdrop({
+ isVisible: true,
+ isAnimated: false
+ })
+ const spy2 = jasmine.createSpy('spy2')
+
+ instance.show(spy2)
+ instance.hide(spy2)
+
+ setTimeout(() => {
+ expect(spy2).toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
+ resolve()
+ }, 10)
})
})
- it('Should find the rootElement if passed as a string', done => {
- const instance = new Backdrop({
- isVisible: true,
- rootElement: 'body'
- })
- const getElement = () => document.querySelector(CLASS_BACKDROP)
- instance.show(() => {
- expect(getElement().parentElement).toEqual(document.body)
- done()
+ it('should not call delay callbacks if it is not "shown"', () => {
+ return new Promise(resolve => {
+ const instance = new Backdrop({
+ isVisible: false,
+ isAnimated: true
+ })
+ const spy = jasmine.createSpy('spy', getTransitionDurationFromElement)
+
+ instance.show()
+ instance.hide(() => {
+ expect(spy).not.toHaveBeenCalled()
+ resolve()
+ })
})
})
+ })
- it('Should appended on any element given by the proper config', done => {
- fixtureEl.innerHTML = [
- '<div id="wrapper">',
- '</div>'
- ].join('')
+ describe('Config', () => {
+ describe('rootElement initialization', () => {
+ it('should be appended on "document.body" by default', () => {
+ return new Promise(resolve => {
+ const instance = new Backdrop({
+ isVisible: true
+ })
+ const getElement = () => document.querySelector(CLASS_BACKDROP)
+ instance.show(() => {
+ expect(getElement().parentElement).toEqual(document.body)
+ resolve()
+ })
+ })
+ })
- const wrapper = fixtureEl.querySelector('#wrapper')
- const instance = new Backdrop({
- isVisible: true,
- rootElement: wrapper
+ it('should find the rootElement if passed as a string', () => {
+ return new Promise(resolve => {
+ const instance = new Backdrop({
+ isVisible: true,
+ rootElement: 'body'
+ })
+ const getElement = () => document.querySelector(CLASS_BACKDROP)
+ instance.show(() => {
+ expect(getElement().parentElement).toEqual(document.body)
+ resolve()
+ })
+ })
})
- const getElement = () => document.querySelector(CLASS_BACKDROP)
- instance.show(() => {
- expect(getElement().parentElement).toEqual(wrapper)
- done()
+
+ it('should be appended on any element given by the proper config', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div id="wrapper"></div>'
+
+ const wrapper = fixtureEl.querySelector('#wrapper')
+ const instance = new Backdrop({
+ isVisible: true,
+ rootElement: wrapper
+ })
+ const getElement = () => document.querySelector(CLASS_BACKDROP)
+ instance.show(() => {
+ expect(getElement().parentElement).toEqual(wrapper)
+ resolve()
+ })
+ })
})
})
- })
- describe('ClassName', () => {
- it('Should be able to have different classNames than default', done => {
- const instance = new Backdrop({
- isVisible: true,
- className: 'foo'
- })
- const getElement = () => document.querySelector('.foo')
- instance.show(() => {
- expect(getElement()).toEqual(instance._getElement())
- instance.dispose()
- done()
+ describe('ClassName', () => {
+ it('should allow configuring className', () => {
+ return new Promise(resolve => {
+ const instance = new Backdrop({
+ isVisible: true,
+ className: 'foo'
+ })
+ const getElement = () => document.querySelector('.foo')
+ instance.show(() => {
+ expect(getElement()).toEqual(instance._getElement())
+ instance.dispose()
+ resolve()
+ })
+ })
})
})
})
diff --git a/js/tests/unit/util/component-functions.spec.js b/js/tests/unit/util/component-functions.spec.js
index edaedd32e..ce83785e2 100644
--- a/js/tests/unit/util/component-functions.spec.js
+++ b/js/tests/unit/util/component-functions.spec.js
@@ -1,8 +1,6 @@
-/* Test helpers */
-
-import { clearFixture, createEvent, getFixture } from '../../helpers/fixture'
-import { enableDismissTrigger } from '../../../src/util/component-functions'
-import BaseComponent from '../../../src/base-component'
+import BaseComponent from '../../../src/base-component.js'
+import { enableDismissTrigger } from '../../../src/util/component-functions.js'
+import { clearFixture, createEvent, getFixture } from '../../helpers/fixture.js'
class DummyClass2 extends BaseComponent {
static get NAME() {
@@ -33,12 +31,12 @@ describe('Plugin functions', () => {
it('should get Plugin and execute the given method, when a click occurred on data-bs-dismiss="PluginName"', () => {
fixtureEl.innerHTML = [
'<div id="foo" class="test">',
- ' <button type="button" data-bs-dismiss="test" data-bs-target="#foo"></button>',
+ ' <button type="button" data-bs-dismiss="test" data-bs-target="#foo"></button>',
'</div>'
].join('')
- spyOn(DummyClass2, 'getOrCreateInstance').and.callThrough()
- spyOn(DummyClass2.prototype, 'testMethod')
+ const spyGet = spyOn(DummyClass2, 'getOrCreateInstance').and.callThrough()
+ const spyTest = spyOn(DummyClass2.prototype, 'testMethod')
const componentWrapper = fixtureEl.querySelector('#foo')
const btnClose = fixtureEl.querySelector('[data-bs-dismiss="test"]')
const event = createEvent('click')
@@ -46,19 +44,19 @@ describe('Plugin functions', () => {
enableDismissTrigger(DummyClass2, 'testMethod')
btnClose.dispatchEvent(event)
- expect(DummyClass2.getOrCreateInstance).toHaveBeenCalledWith(componentWrapper)
- expect(DummyClass2.prototype.testMethod).toHaveBeenCalled()
+ expect(spyGet).toHaveBeenCalledWith(componentWrapper)
+ expect(spyTest).toHaveBeenCalled()
})
it('if data-bs-dismiss="PluginName" hasn\'t got "data-bs-target", "getOrCreateInstance" has to be initialized by closest "plugin.Name" class', () => {
fixtureEl.innerHTML = [
'<div id="foo" class="test">',
- ' <button type="button" data-bs-dismiss="test"></button>',
+ ' <button type="button" data-bs-dismiss="test"></button>',
'</div>'
].join('')
- spyOn(DummyClass2, 'getOrCreateInstance').and.callThrough()
- spyOn(DummyClass2.prototype, 'hide')
+ const spyGet = spyOn(DummyClass2, 'getOrCreateInstance').and.callThrough()
+ const spyHide = spyOn(DummyClass2.prototype, 'hide')
const componentWrapper = fixtureEl.querySelector('#foo')
const btnClose = fixtureEl.querySelector('[data-bs-dismiss="test"]')
const event = createEvent('click')
@@ -66,31 +64,31 @@ describe('Plugin functions', () => {
enableDismissTrigger(DummyClass2)
btnClose.dispatchEvent(event)
- expect(DummyClass2.getOrCreateInstance).toHaveBeenCalledWith(componentWrapper)
- expect(DummyClass2.prototype.hide).toHaveBeenCalled()
+ expect(spyGet).toHaveBeenCalledWith(componentWrapper)
+ expect(spyHide).toHaveBeenCalled()
})
it('if data-bs-dismiss="PluginName" is disabled, must not trigger function', () => {
fixtureEl.innerHTML = [
'<div id="foo" class="test">',
- ' <button type="button" disabled data-bs-dismiss="test"></button>',
+ ' <button type="button" disabled data-bs-dismiss="test"></button>',
'</div>'
].join('')
- spyOn(DummyClass2, 'getOrCreateInstance').and.callThrough()
+ const spy = spyOn(DummyClass2, 'getOrCreateInstance').and.callThrough()
const btnClose = fixtureEl.querySelector('[data-bs-dismiss="test"]')
const event = createEvent('click')
enableDismissTrigger(DummyClass2)
btnClose.dispatchEvent(event)
- expect(DummyClass2.getOrCreateInstance).not.toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
})
it('should prevent default when the trigger is <a> or <area>', () => {
fixtureEl.innerHTML = [
'<div id="foo" class="test">',
- ' <a type="button" data-bs-dismiss="test"></a>',
+ ' <a type="button" data-bs-dismiss="test"></a>',
'</div>'
].join('')
@@ -98,11 +96,11 @@ describe('Plugin functions', () => {
const event = createEvent('click')
enableDismissTrigger(DummyClass2)
- spyOn(Event.prototype, 'preventDefault').and.callThrough()
+ const spy = spyOn(Event.prototype, 'preventDefault').and.callThrough()
btnClose.dispatchEvent(event)
- expect(Event.prototype.preventDefault).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
})
})
})
diff --git a/js/tests/unit/util/config.spec.js b/js/tests/unit/util/config.spec.js
new file mode 100644
index 000000000..93987a74a
--- /dev/null
+++ b/js/tests/unit/util/config.spec.js
@@ -0,0 +1,166 @@
+import Config from '../../../src/util/config.js'
+import { clearFixture, getFixture } from '../../helpers/fixture.js'
+
+class DummyConfigClass extends Config {
+ static get NAME() {
+ return 'dummy'
+ }
+}
+
+describe('Config', () => {
+ let fixtureEl
+ const name = 'dummy'
+
+ beforeAll(() => {
+ fixtureEl = getFixture()
+ })
+
+ afterEach(() => {
+ clearFixture()
+ })
+
+ describe('NAME', () => {
+ it('should return plugin NAME', () => {
+ expect(DummyConfigClass.NAME).toEqual(name)
+ })
+ })
+
+ describe('DefaultType', () => {
+ it('should return plugin default type', () => {
+ expect(DummyConfigClass.DefaultType).toEqual(jasmine.any(Object))
+ })
+ })
+
+ describe('Default', () => {
+ it('should return plugin defaults', () => {
+ expect(DummyConfigClass.Default).toEqual(jasmine.any(Object))
+ })
+ })
+
+ describe('mergeConfigObj', () => {
+ it('should parse element\'s data attributes and merge it with default config. Element\'s data attributes must excel Defaults', () => {
+ fixtureEl.innerHTML = '<div id="test" data-bs-test-bool="false" data-bs-test-int="8" data-bs-test-string1="bar"></div>'
+
+ spyOnProperty(DummyConfigClass, 'Default', 'get').and.returnValue({
+ testBool: true,
+ testString: 'foo',
+ testString1: 'foo',
+ testInt: 7
+ })
+ const instance = new DummyConfigClass()
+ const configResult = instance._mergeConfigObj({}, fixtureEl.querySelector('#test'))
+
+ expect(configResult.testBool).toEqual(false)
+ expect(configResult.testString).toEqual('foo')
+ expect(configResult.testString1).toEqual('bar')
+ expect(configResult.testInt).toEqual(8)
+ })
+
+ it('should parse element\'s data attributes and merge it with default config, plug these given during method call. The programmatically given should excel all', () => {
+ fixtureEl.innerHTML = '<div id="test" data-bs-test-bool="false" data-bs-test-int="8" data-bs-test-string-1="bar"></div>'
+
+ spyOnProperty(DummyConfigClass, 'Default', 'get').and.returnValue({
+ testBool: true,
+ testString: 'foo',
+ testString1: 'foo',
+ testInt: 7
+ })
+ const instance = new DummyConfigClass()
+ const configResult = instance._mergeConfigObj({
+ testString1: 'test',
+ testInt: 3
+ }, fixtureEl.querySelector('#test'))
+
+ expect(configResult.testBool).toEqual(false)
+ expect(configResult.testString).toEqual('foo')
+ expect(configResult.testString1).toEqual('test')
+ expect(configResult.testInt).toEqual(3)
+ })
+
+ it('should parse element\'s data attribute `config` and any rest attributes. The programmatically given should excel all. Data attribute `config` should excel only Defaults', () => {
+ fixtureEl.innerHTML = '<div id="test" data-bs-config=\'{"testBool":false,"testInt":50,"testInt2":100}\' data-bs-test-int="8" data-bs-test-string-1="bar"></div>'
+
+ spyOnProperty(DummyConfigClass, 'Default', 'get').and.returnValue({
+ testBool: true,
+ testString: 'foo',
+ testString1: 'foo',
+ testInt: 7,
+ testInt2: 600
+ })
+ const instance = new DummyConfigClass()
+ const configResult = instance._mergeConfigObj({
+ testString1: 'test'
+ }, fixtureEl.querySelector('#test'))
+
+ expect(configResult.testBool).toEqual(false)
+ expect(configResult.testString).toEqual('foo')
+ expect(configResult.testString1).toEqual('test')
+ expect(configResult.testInt).toEqual(8)
+ expect(configResult.testInt2).toEqual(100)
+ })
+
+ it('should omit element\'s data attribute `config` if is not an object', () => {
+ fixtureEl.innerHTML = '<div id="test" data-bs-config="foo" data-bs-test-int="8"></div>'
+
+ spyOnProperty(DummyConfigClass, 'Default', 'get').and.returnValue({
+ testInt: 7,
+ testInt2: 79
+ })
+ const instance = new DummyConfigClass()
+ const configResult = instance._mergeConfigObj({}, fixtureEl.querySelector('#test'))
+
+ expect(configResult.testInt).toEqual(8)
+ expect(configResult.testInt2).toEqual(79)
+ })
+ })
+
+ describe('typeCheckConfig', () => {
+ it('should check type of the config object', () => {
+ spyOnProperty(DummyConfigClass, 'DefaultType', 'get').and.returnValue({
+ toggle: 'boolean',
+ parent: '(string|element)'
+ })
+ const config = {
+ toggle: true,
+ parent: 777
+ }
+
+ const obj = new DummyConfigClass()
+ expect(() => {
+ obj._typeCheckConfig(config)
+ }).toThrowError(TypeError, `${obj.constructor.NAME.toUpperCase()}: Option "parent" provided type "number" but expected type "(string|element)".`)
+ })
+
+ it('should return null stringified when null is passed', () => {
+ spyOnProperty(DummyConfigClass, 'DefaultType', 'get').and.returnValue({
+ toggle: 'boolean',
+ parent: '(null|element)'
+ })
+
+ const obj = new DummyConfigClass()
+ const config = {
+ toggle: true,
+ parent: null
+ }
+
+ obj._typeCheckConfig(config)
+ expect().nothing()
+ })
+
+ it('should return undefined stringified when undefined is passed', () => {
+ spyOnProperty(DummyConfigClass, 'DefaultType', 'get').and.returnValue({
+ toggle: 'boolean',
+ parent: '(undefined|element)'
+ })
+
+ const obj = new DummyConfigClass()
+ const config = {
+ toggle: true,
+ parent: undefined
+ }
+
+ obj._typeCheckConfig(config)
+ expect().nothing()
+ })
+ })
+})
diff --git a/js/tests/unit/util/focustrap.spec.js b/js/tests/unit/util/focustrap.spec.js
index 99bc95fca..0a20017d5 100644
--- a/js/tests/unit/util/focustrap.spec.js
+++ b/js/tests/unit/util/focustrap.spec.js
@@ -1,7 +1,7 @@
-import FocusTrap from '../../../src/util/focustrap'
-import EventHandler from '../../../src/dom/event-handler'
-import SelectorEngine from '../../../src/dom/selector-engine'
-import { clearFixture, getFixture, createEvent } from '../../helpers/fixture'
+import EventHandler from '../../../src/dom/event-handler.js'
+import SelectorEngine from '../../../src/dom/selector-engine.js'
+import FocusTrap from '../../../src/util/focustrap.js'
+import { clearFixture, createEvent, getFixture } from '../../helpers/fixture.js'
describe('FocusTrap', () => {
let fixtureEl
@@ -20,12 +20,12 @@ describe('FocusTrap', () => {
const trapElement = fixtureEl.querySelector('div')
- spyOn(trapElement, 'focus')
+ const spy = spyOn(trapElement, 'focus')
const focustrap = new FocusTrap({ trapElement })
focustrap.activate()
- expect(trapElement.focus).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
})
it('if configured not to autofocus, should not autofocus itself', () => {
@@ -33,148 +33,156 @@ describe('FocusTrap', () => {
const trapElement = fixtureEl.querySelector('div')
- spyOn(trapElement, 'focus')
+ const spy = spyOn(trapElement, 'focus')
const focustrap = new FocusTrap({ trapElement, autofocus: false })
focustrap.activate()
- expect(trapElement.focus).not.toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
})
- it('should force focus inside focus trap if it can', done => {
- fixtureEl.innerHTML = [
- '<a href="#" id="outside">outside</a>',
- '<div id="focustrap" tabindex="-1">',
- ' <a href="#" id="inside">inside</a>',
- '</div>'
- ].join('')
+ it('should force focus inside focus trap if it can', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<a href="#" id="outside">outside</a>',
+ '<div id="focustrap" tabindex="-1">',
+ ' <a href="#" id="inside">inside</a>',
+ '</div>'
+ ].join('')
- const trapElement = fixtureEl.querySelector('div')
- const focustrap = new FocusTrap({ trapElement })
- focustrap.activate()
+ const trapElement = fixtureEl.querySelector('div')
+ const focustrap = new FocusTrap({ trapElement })
+ focustrap.activate()
- const inside = document.getElementById('inside')
+ const inside = document.getElementById('inside')
- const focusInListener = () => {
- expect(inside.focus).toHaveBeenCalled()
- document.removeEventListener('focusin', focusInListener)
- done()
- }
+ const focusInListener = () => {
+ expect(spy).toHaveBeenCalled()
+ document.removeEventListener('focusin', focusInListener)
+ resolve()
+ }
- spyOn(inside, 'focus')
- spyOn(SelectorEngine, 'focusableChildren').and.callFake(() => [inside])
+ const spy = spyOn(inside, 'focus')
+ spyOn(SelectorEngine, 'focusableChildren').and.callFake(() => [inside])
- document.addEventListener('focusin', focusInListener)
+ document.addEventListener('focusin', focusInListener)
- const focusInEvent = createEvent('focusin', { bubbles: true })
- Object.defineProperty(focusInEvent, 'target', {
- value: document.getElementById('outside')
- })
+ const focusInEvent = createEvent('focusin', { bubbles: true })
+ Object.defineProperty(focusInEvent, 'target', {
+ value: document.getElementById('outside')
+ })
- document.dispatchEvent(focusInEvent)
+ document.dispatchEvent(focusInEvent)
+ })
})
- it('should wrap focus around forward on tab', done => {
- fixtureEl.innerHTML = [
- '<a href="#" id="outside">outside</a>',
- '<div id="focustrap" tabindex="-1">',
- ' <a href="#" id="first">first</a>',
- ' <a href="#" id="inside">inside</a>',
- ' <a href="#" id="last">last</a>',
- '</div>'
- ].join('')
-
- const trapElement = fixtureEl.querySelector('div')
- const focustrap = new FocusTrap({ trapElement })
- focustrap.activate()
-
- const first = document.getElementById('first')
- const inside = document.getElementById('inside')
- const last = document.getElementById('last')
- const outside = document.getElementById('outside')
-
- spyOn(SelectorEngine, 'focusableChildren').and.callFake(() => [first, inside, last])
- spyOn(first, 'focus').and.callThrough()
-
- const focusInListener = () => {
- expect(first.focus).toHaveBeenCalled()
- first.removeEventListener('focusin', focusInListener)
- done()
- }
-
- first.addEventListener('focusin', focusInListener)
-
- const keydown = createEvent('keydown')
- keydown.key = 'Tab'
-
- document.dispatchEvent(keydown)
- outside.focus()
+ it('should wrap focus around forward on tab', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<a href="#" id="outside">outside</a>',
+ '<div id="focustrap" tabindex="-1">',
+ ' <a href="#" id="first">first</a>',
+ ' <a href="#" id="inside">inside</a>',
+ ' <a href="#" id="last">last</a>',
+ '</div>'
+ ].join('')
+
+ const trapElement = fixtureEl.querySelector('div')
+ const focustrap = new FocusTrap({ trapElement })
+ focustrap.activate()
+
+ const first = document.getElementById('first')
+ const inside = document.getElementById('inside')
+ const last = document.getElementById('last')
+ const outside = document.getElementById('outside')
+
+ spyOn(SelectorEngine, 'focusableChildren').and.callFake(() => [first, inside, last])
+ const spy = spyOn(first, 'focus').and.callThrough()
+
+ const focusInListener = () => {
+ expect(spy).toHaveBeenCalled()
+ first.removeEventListener('focusin', focusInListener)
+ resolve()
+ }
+
+ first.addEventListener('focusin', focusInListener)
+
+ const keydown = createEvent('keydown')
+ keydown.key = 'Tab'
+
+ document.dispatchEvent(keydown)
+ outside.focus()
+ })
})
- it('should wrap focus around backwards on shift-tab', done => {
- fixtureEl.innerHTML = [
- '<a href="#" id="outside">outside</a>',
- '<div id="focustrap" tabindex="-1">',
- ' <a href="#" id="first">first</a>',
- ' <a href="#" id="inside">inside</a>',
- ' <a href="#" id="last">last</a>',
- '</div>'
- ].join('')
-
- const trapElement = fixtureEl.querySelector('div')
- const focustrap = new FocusTrap({ trapElement })
- focustrap.activate()
-
- const first = document.getElementById('first')
- const inside = document.getElementById('inside')
- const last = document.getElementById('last')
- const outside = document.getElementById('outside')
-
- spyOn(SelectorEngine, 'focusableChildren').and.callFake(() => [first, inside, last])
- spyOn(last, 'focus').and.callThrough()
-
- const focusInListener = () => {
- expect(last.focus).toHaveBeenCalled()
- last.removeEventListener('focusin', focusInListener)
- done()
- }
-
- last.addEventListener('focusin', focusInListener)
-
- const keydown = createEvent('keydown')
- keydown.key = 'Tab'
- keydown.shiftKey = true
-
- document.dispatchEvent(keydown)
- outside.focus()
+ it('should wrap focus around backwards on shift-tab', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<a href="#" id="outside">outside</a>',
+ '<div id="focustrap" tabindex="-1">',
+ ' <a href="#" id="first">first</a>',
+ ' <a href="#" id="inside">inside</a>',
+ ' <a href="#" id="last">last</a>',
+ '</div>'
+ ].join('')
+
+ const trapElement = fixtureEl.querySelector('div')
+ const focustrap = new FocusTrap({ trapElement })
+ focustrap.activate()
+
+ const first = document.getElementById('first')
+ const inside = document.getElementById('inside')
+ const last = document.getElementById('last')
+ const outside = document.getElementById('outside')
+
+ spyOn(SelectorEngine, 'focusableChildren').and.callFake(() => [first, inside, last])
+ const spy = spyOn(last, 'focus').and.callThrough()
+
+ const focusInListener = () => {
+ expect(spy).toHaveBeenCalled()
+ last.removeEventListener('focusin', focusInListener)
+ resolve()
+ }
+
+ last.addEventListener('focusin', focusInListener)
+
+ const keydown = createEvent('keydown')
+ keydown.key = 'Tab'
+ keydown.shiftKey = true
+
+ document.dispatchEvent(keydown)
+ outside.focus()
+ })
})
- it('should force focus on itself if there is no focusable content', done => {
- fixtureEl.innerHTML = [
- '<a href="#" id="outside">outside</a>',
- '<div id="focustrap" tabindex="-1"></div>'
- ].join('')
+ it('should force focus on itself if there is no focusable content', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<a href="#" id="outside">outside</a>',
+ '<div id="focustrap" tabindex="-1"></div>'
+ ].join('')
- const trapElement = fixtureEl.querySelector('div')
- const focustrap = new FocusTrap({ trapElement })
- focustrap.activate()
+ const trapElement = fixtureEl.querySelector('div')
+ const focustrap = new FocusTrap({ trapElement })
+ focustrap.activate()
- const focusInListener = () => {
- expect(focustrap._config.trapElement.focus).toHaveBeenCalled()
- document.removeEventListener('focusin', focusInListener)
- done()
- }
+ const focusInListener = () => {
+ expect(spy).toHaveBeenCalled()
+ document.removeEventListener('focusin', focusInListener)
+ resolve()
+ }
- spyOn(focustrap._config.trapElement, 'focus')
+ const spy = spyOn(focustrap._config.trapElement, 'focus')
- document.addEventListener('focusin', focusInListener)
+ document.addEventListener('focusin', focusInListener)
- const focusInEvent = createEvent('focusin', { bubbles: true })
- Object.defineProperty(focusInEvent, 'target', {
- value: document.getElementById('outside')
- })
+ const focusInEvent = createEvent('focusin', { bubbles: true })
+ Object.defineProperty(focusInEvent, 'target', {
+ value: document.getElementById('outside')
+ })
- document.dispatchEvent(focusInEvent)
+ document.dispatchEvent(focusInEvent)
+ })
})
})
@@ -182,29 +190,29 @@ describe('FocusTrap', () => {
it('should flag itself as no longer active', () => {
const focustrap = new FocusTrap({ trapElement: fixtureEl })
focustrap.activate()
- expect(focustrap._isActive).toBe(true)
+ expect(focustrap._isActive).toBeTrue()
focustrap.deactivate()
- expect(focustrap._isActive).toBe(false)
+ expect(focustrap._isActive).toBeFalse()
})
it('should remove all event listeners', () => {
const focustrap = new FocusTrap({ trapElement: fixtureEl })
focustrap.activate()
- spyOn(EventHandler, 'off')
+ const spy = spyOn(EventHandler, 'off')
focustrap.deactivate()
- expect(EventHandler.off).toHaveBeenCalled()
+ expect(spy).toHaveBeenCalled()
})
it('doesn\'t try removing event listeners unless it needs to (in case it hasn\'t been activated)', () => {
const focustrap = new FocusTrap({ trapElement: fixtureEl })
- spyOn(EventHandler, 'off')
+ const spy = spyOn(EventHandler, 'off')
focustrap.deactivate()
- expect(EventHandler.off).not.toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
})
})
})
diff --git a/js/tests/unit/util/index.spec.js b/js/tests/unit/util/index.spec.js
index ccfe5e2c2..9e154818f 100644
--- a/js/tests/unit/util/index.spec.js
+++ b/js/tests/unit/util/index.spec.js
@@ -1,5 +1,6 @@
-import * as Util from '../../../src/util/index'
-import { clearFixture, getFixture } from '../../helpers/fixture'
+import * as Util from '../../../src/util/index.js'
+import { noop } from '../../../src/util/index.js'
+import { clearFixture, getFixture } from '../../helpers/fixture.js'
describe('Util', () => {
let fixtureEl
@@ -21,119 +22,6 @@ describe('Util', () => {
})
})
- describe('getSelectorFromElement', () => {
- it('should get selector from data-bs-target', () => {
- fixtureEl.innerHTML = [
- '<div id="test" data-bs-target=".target"></div>',
- '<div class="target"></div>'
- ].join('')
-
- const testEl = fixtureEl.querySelector('#test')
-
- expect(Util.getSelectorFromElement(testEl)).toEqual('.target')
- })
-
- it('should get selector from href if no data-bs-target set', () => {
- fixtureEl.innerHTML = [
- '<a id="test" href=".target"></a>',
- '<div class="target"></div>'
- ].join('')
-
- const testEl = fixtureEl.querySelector('#test')
-
- expect(Util.getSelectorFromElement(testEl)).toEqual('.target')
- })
-
- it('should get selector from href if data-bs-target equal to #', () => {
- fixtureEl.innerHTML = [
- '<a id="test" data-bs-target="#" href=".target"></a>',
- '<div class="target"></div>'
- ].join('')
-
- const testEl = fixtureEl.querySelector('#test')
-
- expect(Util.getSelectorFromElement(testEl)).toEqual('.target')
- })
-
- it('should return null if a selector from a href is a url without an anchor', () => {
- fixtureEl.innerHTML = [
- '<a id="test" data-bs-target="#" href="foo/bar.html"></a>',
- '<div class="target"></div>'
- ].join('')
-
- const testEl = fixtureEl.querySelector('#test')
-
- expect(Util.getSelectorFromElement(testEl)).toBeNull()
- })
-
- it('should return the anchor if a selector from a href is a url', () => {
- fixtureEl.innerHTML = [
- '<a id="test" data-bs-target="#" href="foo/bar.html#target"></a>',
- '<div id="target"></div>'
- ].join('')
-
- const testEl = fixtureEl.querySelector('#test')
-
- expect(Util.getSelectorFromElement(testEl)).toEqual('#target')
- })
-
- it('should return null if selector not found', () => {
- fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
-
- const testEl = fixtureEl.querySelector('#test')
-
- expect(Util.getSelectorFromElement(testEl)).toBeNull()
- })
-
- it('should return null if no selector', () => {
- fixtureEl.innerHTML = '<div></div>'
-
- const testEl = fixtureEl.querySelector('div')
-
- expect(Util.getSelectorFromElement(testEl)).toBeNull()
- })
- })
-
- describe('getElementFromSelector', () => {
- it('should get element from data-bs-target', () => {
- fixtureEl.innerHTML = [
- '<div id="test" data-bs-target=".target"></div>',
- '<div class="target"></div>'
- ].join('')
-
- const testEl = fixtureEl.querySelector('#test')
-
- expect(Util.getElementFromSelector(testEl)).toEqual(fixtureEl.querySelector('.target'))
- })
-
- it('should get element from href if no data-bs-target set', () => {
- fixtureEl.innerHTML = [
- '<a id="test" href=".target"></a>',
- '<div class="target"></div>'
- ].join('')
-
- const testEl = fixtureEl.querySelector('#test')
-
- expect(Util.getElementFromSelector(testEl)).toEqual(fixtureEl.querySelector('.target'))
- })
-
- it('should return null if element not found', () => {
- fixtureEl.innerHTML = '<a id="test" href=".target"></a>'
-
- const testEl = fixtureEl.querySelector('#test')
-
- expect(Util.getElementFromSelector(testEl)).toBeNull()
- })
-
- it('should return null if no selector', () => {
- fixtureEl.innerHTML = '<div></div>'
-
- const testEl = fixtureEl.querySelector('div')
-
- expect(Util.getElementFromSelector(testEl)).toBeNull()
- })
- })
-
describe('getTransitionDurationFromElement', () => {
it('should get transition from element', () => {
fixtureEl.innerHTML = '<div style="transition: all 300ms ease-out;"></div>'
@@ -154,34 +42,35 @@ describe('Util', () => {
})
describe('triggerTransitionEnd', () => {
- it('should trigger transitionend event', done => {
- fixtureEl.innerHTML = '<div></div>'
+ it('should trigger transitionend event', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = '<div></div>'
- const el = fixtureEl.querySelector('div')
- const spy = spyOn(el, 'dispatchEvent').and.callThrough()
+ const el = fixtureEl.querySelector('div')
+ const spy = spyOn(el, 'dispatchEvent').and.callThrough()
- el.addEventListener('transitionend', () => {
- expect(spy).toHaveBeenCalled()
- done()
- })
+ el.addEventListener('transitionend', () => {
+ expect(spy).toHaveBeenCalled()
+ resolve()
+ })
- Util.triggerTransitionEnd(el)
+ Util.triggerTransitionEnd(el)
+ })
})
})
describe('isElement', () => {
it('should detect if the parameter is an element or not and return Boolean', () => {
- fixtureEl.innerHTML =
- [
- '<div id="foo" class="test"></div>',
- '<div id="bar" class="test"></div>'
- ].join('')
+ fixtureEl.innerHTML = [
+ '<div id="foo" class="test"></div>',
+ '<div id="bar" class="test"></div>'
+ ].join('')
const el = fixtureEl.querySelector('#foo')
- expect(Util.isElement(el)).toEqual(true)
- expect(Util.isElement({})).toEqual(false)
- expect(Util.isElement(fixtureEl.querySelectorAll('.test'))).toEqual(false)
+ expect(Util.isElement(el)).toBeTrue()
+ expect(Util.isElement({})).toBeFalse()
+ expect(Util.isElement(fixtureEl.querySelectorAll('.test'))).toBeFalse()
})
it('should detect jQuery element', () => {
@@ -193,17 +82,16 @@ describe('Util', () => {
jquery: 'foo'
}
- expect(Util.isElement(fakejQuery)).toEqual(true)
+ expect(Util.isElement(fakejQuery)).toBeTrue()
})
})
describe('getElement', () => {
it('should try to parse element', () => {
- fixtureEl.innerHTML =
- [
- '<div id="foo" class="test"></div>',
- '<div id="bar" class="test"></div>'
- ].join('')
+ fixtureEl.innerHTML = [
+ '<div id="foo" class="test"></div>',
+ '<div id="bar" class="test"></div>'
+ ].join('')
const el = fixtureEl.querySelector('div')
@@ -225,61 +113,14 @@ describe('Util', () => {
})
})
- describe('typeCheckConfig', () => {
- const namePlugin = 'collapse'
-
- it('should check type of the config object', () => {
- const defaultType = {
- toggle: 'boolean',
- parent: '(string|element)'
- }
- const config = {
- toggle: true,
- parent: 777
- }
-
- expect(() => {
- Util.typeCheckConfig(namePlugin, config, defaultType)
- }).toThrowError(TypeError, 'COLLAPSE: Option "parent" provided type "number" but expected type "(string|element)".')
- })
-
- it('should return null stringified when null is passed', () => {
- const defaultType = {
- toggle: 'boolean',
- parent: '(null|element)'
- }
- const config = {
- toggle: true,
- parent: null
- }
-
- Util.typeCheckConfig(namePlugin, config, defaultType)
- expect().nothing()
- })
-
- it('should return undefined stringified when undefined is passed', () => {
- const defaultType = {
- toggle: 'boolean',
- parent: '(undefined|element)'
- }
- const config = {
- toggle: true,
- parent: undefined
- }
-
- Util.typeCheckConfig(namePlugin, config, defaultType)
- expect().nothing()
- })
- })
-
describe('isVisible', () => {
it('should return false if the element is not defined', () => {
- expect(Util.isVisible(null)).toEqual(false)
- expect(Util.isVisible(undefined)).toEqual(false)
+ expect(Util.isVisible(null)).toBeFalse()
+ expect(Util.isVisible(undefined)).toBeFalse()
})
it('should return false if the element provided is not a dom element', () => {
- expect(Util.isVisible({})).toEqual(false)
+ expect(Util.isVisible({})).toBeFalse()
})
it('should return false if the element is not visible with display none', () => {
@@ -287,7 +128,7 @@ describe('Util', () => {
const div = fixtureEl.querySelector('div')
- expect(Util.isVisible(div)).toEqual(false)
+ expect(Util.isVisible(div)).toBeFalse()
})
it('should return false if the element is not visible with visibility hidden', () => {
@@ -295,7 +136,7 @@ describe('Util', () => {
const div = fixtureEl.querySelector('div')
- expect(Util.isVisible(div)).toEqual(false)
+ expect(Util.isVisible(div)).toBeFalse()
})
it('should return false if an ancestor element is display none', () => {
@@ -311,7 +152,7 @@ describe('Util', () => {
const div = fixtureEl.querySelector('.content')
- expect(Util.isVisible(div)).toEqual(false)
+ expect(Util.isVisible(div)).toBeFalse()
})
it('should return false if an ancestor element is visibility hidden', () => {
@@ -327,7 +168,7 @@ describe('Util', () => {
const div = fixtureEl.querySelector('.content')
- expect(Util.isVisible(div)).toEqual(false)
+ expect(Util.isVisible(div)).toBeFalse()
})
it('should return true if an ancestor element is visibility hidden, but reverted', () => {
@@ -343,7 +184,7 @@ describe('Util', () => {
const div = fixtureEl.querySelector('.content')
- expect(Util.isVisible(div)).toEqual(true)
+ expect(Util.isVisible(div)).toBeTrue()
})
it('should return true if the element is visible', () => {
@@ -355,7 +196,7 @@ describe('Util', () => {
const div = fixtureEl.querySelector('#element')
- expect(Util.isVisible(div)).toEqual(true)
+ expect(Util.isVisible(div)).toBeTrue()
})
it('should return false if the element is hidden, but not via display or visibility', () => {
@@ -367,20 +208,56 @@ describe('Util', () => {
const div = fixtureEl.querySelector('#element')
- expect(Util.isVisible(div)).toEqual(false)
+ expect(Util.isVisible(div)).toBeFalse()
+ })
+
+ it('should return true if its a closed details element', () => {
+ fixtureEl.innerHTML = '<details id="element"></details>'
+
+ const div = fixtureEl.querySelector('#element')
+
+ expect(Util.isVisible(div)).toBeTrue()
+ })
+
+ it('should return true if the element is visible inside an open details element', () => {
+ fixtureEl.innerHTML = [
+ '<details open>',
+ ' <div id="element"></div>',
+ '</details>'
+ ].join('')
+
+ const div = fixtureEl.querySelector('#element')
+
+ expect(Util.isVisible(div)).toBeTrue()
+ })
+
+ it('should return true if the element is a visible summary in a closed details element', () => {
+ fixtureEl.innerHTML = [
+ '<details>',
+ ' <summary id="element-1">',
+ ' <span id="element-2"></span>',
+ ' </summary>',
+ '</details>'
+ ].join('')
+
+ const element1 = fixtureEl.querySelector('#element-1')
+ const element2 = fixtureEl.querySelector('#element-2')
+
+ expect(Util.isVisible(element1)).toBeTrue()
+ expect(Util.isVisible(element2)).toBeTrue()
})
})
describe('isDisabled', () => {
it('should return true if the element is not defined', () => {
- expect(Util.isDisabled(null)).toEqual(true)
- expect(Util.isDisabled(undefined)).toEqual(true)
- expect(Util.isDisabled()).toEqual(true)
+ expect(Util.isDisabled(null)).toBeTrue()
+ expect(Util.isDisabled(undefined)).toBeTrue()
+ expect(Util.isDisabled()).toBeTrue()
})
it('should return true if the element provided is not a dom element', () => {
- expect(Util.isDisabled({})).toEqual(true)
- expect(Util.isDisabled('test')).toEqual(true)
+ expect(Util.isDisabled({})).toBeTrue()
+ expect(Util.isDisabled('test')).toBeTrue()
})
it('should return true if the element has disabled attribute', () => {
@@ -396,9 +273,9 @@ describe('Util', () => {
const div1 = fixtureEl.querySelector('#element1')
const div2 = fixtureEl.querySelector('#element2')
- expect(Util.isDisabled(div)).toEqual(true)
- expect(Util.isDisabled(div1)).toEqual(true)
- expect(Util.isDisabled(div2)).toEqual(true)
+ expect(Util.isDisabled(div)).toBeTrue()
+ expect(Util.isDisabled(div1)).toBeTrue()
+ expect(Util.isDisabled(div2)).toBeTrue()
})
it('should return false if the element has disabled attribute with "false" value, or doesn\'t have attribute', () => {
@@ -412,8 +289,8 @@ describe('Util', () => {
const div = fixtureEl.querySelector('#element')
const div1 = fixtureEl.querySelector('#element1')
- expect(Util.isDisabled(div)).toEqual(false)
- expect(Util.isDisabled(div1)).toEqual(false)
+ expect(Util.isDisabled(div)).toBeFalse()
+ expect(Util.isDisabled(div1)).toBeFalse()
})
it('should return false if the element is not disabled ', () => {
@@ -427,15 +304,16 @@ describe('Util', () => {
const el = selector => fixtureEl.querySelector(selector)
- expect(Util.isDisabled(el('#button'))).toEqual(false)
- expect(Util.isDisabled(el('#select'))).toEqual(false)
- expect(Util.isDisabled(el('#input'))).toEqual(false)
+ expect(Util.isDisabled(el('#button'))).toBeFalse()
+ expect(Util.isDisabled(el('#select'))).toBeFalse()
+ expect(Util.isDisabled(el('#input'))).toBeFalse()
})
+
it('should return true if the element has disabled attribute', () => {
fixtureEl.innerHTML = [
'<div>',
- ' <input id="input" disabled="disabled"/>',
- ' <input id="input1" disabled="disabled"/>',
+ ' <input id="input" disabled="disabled">',
+ ' <input id="input1" disabled="disabled">',
' <button id="button" disabled="true"></button>',
' <button id="button1" disabled="disabled"></button>',
' <button id="button2" disabled></button>',
@@ -446,12 +324,12 @@ describe('Util', () => {
const el = selector => fixtureEl.querySelector(selector)
- expect(Util.isDisabled(el('#input'))).toEqual(true)
- expect(Util.isDisabled(el('#input1'))).toEqual(true)
- expect(Util.isDisabled(el('#button'))).toEqual(true)
- expect(Util.isDisabled(el('#button1'))).toEqual(true)
- expect(Util.isDisabled(el('#button2'))).toEqual(true)
- expect(Util.isDisabled(el('#input'))).toEqual(true)
+ expect(Util.isDisabled(el('#input'))).toBeTrue()
+ expect(Util.isDisabled(el('#input1'))).toBeTrue()
+ expect(Util.isDisabled(el('#button'))).toBeTrue()
+ expect(Util.isDisabled(el('#button1'))).toBeTrue()
+ expect(Util.isDisabled(el('#button2'))).toBeTrue()
+ expect(Util.isDisabled(el('#input'))).toBeTrue()
})
it('should return true if the element has class "disabled"', () => {
@@ -463,19 +341,19 @@ describe('Util', () => {
const div = fixtureEl.querySelector('#element')
- expect(Util.isDisabled(div)).toEqual(true)
+ expect(Util.isDisabled(div)).toBeTrue()
})
it('should return true if the element has class "disabled" but disabled attribute is false', () => {
fixtureEl.innerHTML = [
'<div>',
- ' <input id="input" class="disabled" disabled="false"/>',
+ ' <input id="input" class="disabled" disabled="false">',
'</div>'
].join('')
const div = fixtureEl.querySelector('#input')
- expect(Util.isDisabled(div)).toEqual(true)
+ expect(Util.isDisabled(div)).toBeTrue()
})
})
@@ -493,7 +371,7 @@ describe('Util', () => {
spyOn(document.documentElement, 'attachShadow').and.returnValue(null)
- expect(Util.findShadowRoot(div)).toEqual(null)
+ expect(Util.findShadowRoot(div)).toBeNull()
})
it('should return null when we do not find a shadow root', () => {
@@ -505,7 +383,7 @@ describe('Util', () => {
spyOn(document, 'getRootNode').and.returnValue(undefined)
- expect(Util.findShadowRoot(document)).toEqual(null)
+ expect(Util.findShadowRoot(document)).toBeNull()
})
it('should return the shadow root when found', () => {
@@ -532,7 +410,7 @@ describe('Util', () => {
describe('noop', () => {
it('should be a function', () => {
- expect(typeof Util.noop).toEqual('function')
+ expect(Util.noop).toEqual(jasmine.any(Function))
})
})
@@ -569,14 +447,14 @@ describe('Util', () => {
document.body.setAttribute('data-bs-no-jquery', '')
expect(window.jQuery).toEqual(fakejQuery)
- expect(Util.getjQuery()).toEqual(null)
+ expect(Util.getjQuery()).toBeNull()
document.body.removeAttribute('data-bs-no-jquery')
})
it('should not return jQuery if not present', () => {
window.jQuery = undefined
- expect(Util.getjQuery()).toEqual(null)
+ expect(Util.getjQuery()).toBeNull()
})
})
@@ -585,7 +463,7 @@ describe('Util', () => {
const spy = jasmine.createSpy()
const spy2 = jasmine.createSpy()
- spyOn(document, 'addEventListener').and.callThrough()
+ const spyAdd = spyOn(document, 'addEventListener').and.callThrough()
spyOnProperty(document, 'readyState').and.returnValue('loading')
Util.onDOMContentLoaded(spy)
@@ -598,7 +476,7 @@ describe('Util', () => {
expect(spy).toHaveBeenCalled()
expect(spy2).toHaveBeenCalled()
- expect(document.addEventListener).toHaveBeenCalledTimes(1)
+ expect(spyAdd).toHaveBeenCalledTimes(1)
})
it('should execute callback if readyState is not "loading"', () => {
@@ -623,14 +501,14 @@ describe('Util', () => {
})
it('should define a plugin on the jQuery instance', () => {
- const pluginMock = function () {}
+ const pluginMock = Util.noop
pluginMock.NAME = 'test'
- pluginMock.jQueryInterface = function () {}
+ pluginMock.jQueryInterface = Util.noop
Util.defineJQueryPlugin(pluginMock)
- expect(fakejQuery.fn.test).toBe(pluginMock.jQueryInterface)
- expect(fakejQuery.fn.test.Constructor).toBe(pluginMock)
- expect(typeof fakejQuery.fn.test.noConflict).toEqual('function')
+ expect(fakejQuery.fn.test).toEqual(pluginMock.jQueryInterface)
+ expect(fakejQuery.fn.test.Constructor).toEqual(pluginMock)
+ expect(fakejQuery.fn.test.noConflict).toEqual(jasmine.any(Function))
})
})
@@ -640,6 +518,25 @@ describe('Util', () => {
Util.execute(spy)
expect(spy).toHaveBeenCalled()
})
+
+ it('should execute if arg is function & return the result', () => {
+ const functionFoo = (num1, num2 = 10) => num1 + num2
+ const resultFoo = Util.execute(functionFoo, [undefined, 4, 5])
+ expect(resultFoo).toBe(9)
+
+ const resultFoo1 = Util.execute(functionFoo, [undefined, 4])
+ expect(resultFoo1).toBe(14)
+
+ const functionBar = () => 'foo'
+ const resultBar = Util.execute(functionBar)
+ expect(resultBar).toBe('foo')
+ })
+
+ it('should not execute if arg is not function & return default argument', () => {
+ const foo = 'bar'
+ expect(Util.execute(foo)).toBe('bar')
+ expect(Util.execute(foo, [], 4)).toBe(4)
+ })
})
describe('executeAfterTransition', () => {
@@ -670,96 +567,104 @@ describe('Util', () => {
expect(callbackSpy).toHaveBeenCalled()
})
- it('should execute a function after a computed CSS transition duration and there was no transitionend event dispatched', done => {
- const el = document.createElement('div')
- const callbackSpy = jasmine.createSpy('callback spy')
+ it('should execute a function after a computed CSS transition duration and there was no transitionend event dispatched', () => {
+ return new Promise(resolve => {
+ const el = document.createElement('div')
+ const callbackSpy = jasmine.createSpy('callback spy')
- spyOn(window, 'getComputedStyle').and.returnValue({
- transitionDuration: '0.05s',
- transitionDelay: '0s'
- })
+ spyOn(window, 'getComputedStyle').and.returnValue({
+ transitionDuration: '0.05s',
+ transitionDelay: '0s'
+ })
- Util.executeAfterTransition(callbackSpy, el)
+ Util.executeAfterTransition(callbackSpy, el)
- setTimeout(() => {
- expect(callbackSpy).toHaveBeenCalled()
- done()
- }, 70)
+ setTimeout(() => {
+ expect(callbackSpy).toHaveBeenCalled()
+ resolve()
+ }, 70)
+ })
})
- it('should not execute a function a second time after a computed CSS transition duration and if a transitionend event has already been dispatched', done => {
- const el = document.createElement('div')
- const callbackSpy = jasmine.createSpy('callback spy')
+ it('should not execute a function a second time after a computed CSS transition duration and if a transitionend event has already been dispatched', () => {
+ return new Promise(resolve => {
+ const el = document.createElement('div')
+ const callbackSpy = jasmine.createSpy('callback spy')
- spyOn(window, 'getComputedStyle').and.returnValue({
- transitionDuration: '0.05s',
- transitionDelay: '0s'
- })
+ spyOn(window, 'getComputedStyle').and.returnValue({
+ transitionDuration: '0.05s',
+ transitionDelay: '0s'
+ })
- Util.executeAfterTransition(callbackSpy, el)
+ Util.executeAfterTransition(callbackSpy, el)
- setTimeout(() => {
- el.dispatchEvent(new TransitionEvent('transitionend'))
- }, 50)
+ setTimeout(() => {
+ el.dispatchEvent(new TransitionEvent('transitionend'))
+ }, 50)
- setTimeout(() => {
- expect(callbackSpy).toHaveBeenCalledTimes(1)
- done()
- }, 70)
+ setTimeout(() => {
+ expect(callbackSpy).toHaveBeenCalledTimes(1)
+ resolve()
+ }, 70)
+ })
})
- it('should not trigger a transitionend event if another transitionend event had already happened', done => {
- const el = document.createElement('div')
+ it('should not trigger a transitionend event if another transitionend event had already happened', () => {
+ return new Promise(resolve => {
+ const el = document.createElement('div')
- spyOn(window, 'getComputedStyle').and.returnValue({
- transitionDuration: '0.05s',
- transitionDelay: '0s'
- })
+ spyOn(window, 'getComputedStyle').and.returnValue({
+ transitionDuration: '0.05s',
+ transitionDelay: '0s'
+ })
- Util.executeAfterTransition(() => {}, el)
+ Util.executeAfterTransition(noop, el)
- // simulate a event dispatched by the browser
- el.dispatchEvent(new TransitionEvent('transitionend'))
+ // simulate a event dispatched by the browser
+ el.dispatchEvent(new TransitionEvent('transitionend'))
- const dispatchSpy = spyOn(el, 'dispatchEvent').and.callThrough()
+ const dispatchSpy = spyOn(el, 'dispatchEvent').and.callThrough()
- setTimeout(() => {
- // setTimeout should not have triggered another transitionend event.
- expect(dispatchSpy).not.toHaveBeenCalled()
- done()
- }, 70)
+ setTimeout(() => {
+ // setTimeout should not have triggered another transitionend event.
+ expect(dispatchSpy).not.toHaveBeenCalled()
+ resolve()
+ }, 70)
+ })
})
- it('should ignore transitionend events from nested elements', done => {
- fixtureEl.innerHTML = [
- '<div class="outer">',
- ' <div class="nested"></div>',
- '</div>'
- ].join('')
+ it('should ignore transitionend events from nested elements', () => {
+ return new Promise(resolve => {
+ fixtureEl.innerHTML = [
+ '<div class="outer">',
+ ' <div class="nested"></div>',
+ '</div>'
+ ].join('')
- const outer = fixtureEl.querySelector('.outer')
- const nested = fixtureEl.querySelector('.nested')
- const callbackSpy = jasmine.createSpy('callback spy')
+ const outer = fixtureEl.querySelector('.outer')
+ const nested = fixtureEl.querySelector('.nested')
+ const callbackSpy = jasmine.createSpy('callback spy')
- spyOn(window, 'getComputedStyle').and.returnValue({
- transitionDuration: '0.05s',
- transitionDelay: '0s'
- })
+ spyOn(window, 'getComputedStyle').and.returnValue({
+ transitionDuration: '0.05s',
+ transitionDelay: '0s'
+ })
- Util.executeAfterTransition(callbackSpy, outer)
+ Util.executeAfterTransition(callbackSpy, outer)
- nested.dispatchEvent(new TransitionEvent('transitionend', {
- bubbles: true
- }))
+ nested.dispatchEvent(new TransitionEvent('transitionend', {
+ bubbles: true
+ }))
- setTimeout(() => {
- expect(callbackSpy).not.toHaveBeenCalled()
- }, 20)
+ setTimeout(() => {
+ expect(callbackSpy).not.toHaveBeenCalled()
+ }, 20)
- setTimeout(() => {
- expect(callbackSpy).toHaveBeenCalled()
- done()
- }, 70)
+ setTimeout(() => {
+ expect(callbackSpy).toHaveBeenCalled()
+ resolve()
+ }, 70)
+ })
})
})
diff --git a/js/tests/unit/util/sanitizer.spec.js b/js/tests/unit/util/sanitizer.spec.js
index 28d624c87..2b21ef2e1 100644
--- a/js/tests/unit/util/sanitizer.spec.js
+++ b/js/tests/unit/util/sanitizer.spec.js
@@ -1,4 +1,4 @@
-import { DefaultAllowlist, sanitizeHtml } from '../../../src/util/sanitizer'
+import { DefaultAllowlist, sanitizeHtml } from '../../../src/util/sanitizer.js'
describe('Sanitizer', () => {
describe('sanitizeHtml', () => {
@@ -10,17 +10,75 @@ describe('Sanitizer', () => {
expect(result).toEqual(empty)
})
- it('should sanitize template by removing tags with XSS', () => {
- const template = [
- '<div>',
- ' <a href="javascript:alert(7)">Click me</a>',
- ' <span>Some content</span>',
- '</div>'
- ].join('')
-
- const result = sanitizeHtml(template, DefaultAllowlist, null)
+ it('should retain tags with valid URLs', () => {
+ const validUrls = [
+ '',
+ 'http://abc',
+ 'HTTP://abc',
+ 'https://abc',
+ 'HTTPS://abc',
+ 'ftp://abc',
+ 'FTP://abc',
+ 'mailto:[email protected]',
+ 'MAILTO:[email protected]',
+ 'tel:123-123-1234',
+ 'TEL:123-123-1234',
+ '#anchor',
+ '/page1.md',
+ 'http://JavaScript/my.js',
+ 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/', // Truncated.
+ 'data:video/webm;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/',
+ 'data:audio/opus;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/',
+ 'unknown-scheme:abc'
+ ]
+
+ for (const url of validUrls) {
+ const template = [
+ '<div>',
+ ` <a href="${url}">Click me</a>`,
+ ' <span>Some content</span>',
+ '</div>'
+ ].join('')
+
+ const result = sanitizeHtml(template, DefaultAllowlist, null)
+
+ expect(result).toContain(`href="${url}"`)
+ }
+ })
- expect(result).not.toContain('href="javascript:alert(7)')
+ it('should sanitize template by removing tags with XSS', () => {
+ const invalidUrls = [
+ // eslint-disable-next-line no-script-url
+ 'javascript:alert(7)',
+ // eslint-disable-next-line no-script-url
+ 'javascript:evil()',
+ // eslint-disable-next-line no-script-url
+ 'JavaScript:abc',
+ ' javascript:abc',
+ ' \n Java\n Script:abc',
+ '&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;',
+ '&#106&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;',
+ '&#106 &#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;',
+ '&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058',
+ '&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A;',
+ 'jav&#x09;ascript:alert();',
+ 'jav\u0000ascript:alert();'
+ ]
+
+ for (const url of invalidUrls) {
+ const template = [
+ '<div>',
+ ` <a href="${url}">Click me</a>`,
+ ' <span>Some content</span>',
+ '</div>'
+ ].join('')
+
+ const result = sanitizeHtml(template, DefaultAllowlist, null)
+
+ expect(result).not.toContain(`href="${url}"`)
+ }
})
it('should sanitize template and work with multiple regex', () => {
@@ -84,12 +142,12 @@ describe('Sanitizer', () => {
return htmlUnsafe
}
- spyOn(DOMParser.prototype, 'parseFromString')
+ const spy = spyOn(DOMParser.prototype, 'parseFromString')
const result = sanitizeHtml(template, DefaultAllowlist, mySanitize)
expect(result).toEqual(template)
- expect(DOMParser.prototype.parseFromString).not.toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
})
it('should allow multiple sanitation passes of the same template', () => {
diff --git a/js/tests/unit/util/scrollbar.spec.js b/js/tests/unit/util/scrollbar.spec.js
index 280adb8e5..6dadfcdd1 100644
--- a/js/tests/unit/util/scrollbar.spec.js
+++ b/js/tests/unit/util/scrollbar.spec.js
@@ -1,13 +1,13 @@
-import { clearBodyAndDocument, clearFixture, getFixture } from '../../helpers/fixture'
-import Manipulator from '../../../src/dom/manipulator'
-import ScrollBarHelper from '../../../src/util/scrollbar'
+import Manipulator from '../../../src/dom/manipulator.js'
+import ScrollBarHelper from '../../../src/util/scrollbar.js'
+import { clearBodyAndDocument, clearFixture, getFixture } from '../../helpers/fixture.js'
describe('ScrollBar', () => {
let fixtureEl
const doc = document.documentElement
- const parseInt = arg => Number.parseInt(arg, 10)
- const getPaddingX = el => parseInt(window.getComputedStyle(el).paddingRight)
- const getMarginX = el => parseInt(window.getComputedStyle(el).marginRight)
+ const parseIntDecimal = arg => Number.parseInt(arg, 10)
+ const getPaddingX = el => parseIntDecimal(window.getComputedStyle(el).paddingRight)
+ const getMarginX = el => parseIntDecimal(window.getComputedStyle(el).marginRight)
const getOverFlow = el => el.style.overflow
const getPaddingAttr = el => Manipulator.getDataAttribute(el, 'padding-right')
const getMarginAttr = el => Manipulator.getDataAttribute(el, 'margin-right')
@@ -24,7 +24,9 @@ describe('ScrollBar', () => {
}
}
- const isScrollBarHidden = () => { // IOS devices, Android devices and Browsers on Mac, hide scrollbar by default and appear it, only while scrolling. So the tests for scrollbar would fail
+ // iOS, Android devices and macOS browsers hide scrollbar by default and show it only while scrolling.
+ // So the tests for scrollbar would fail
+ const isScrollBarHidden = () => {
const calc = windowCalculations()
return calc.htmlClient === calc.htmlOffset && calc.htmlClient === calc.window
}
@@ -52,28 +54,24 @@ describe('ScrollBar', () => {
it('should return true if body is overflowing', () => {
document.documentElement.style.overflowY = 'scroll'
document.body.style.overflowY = 'scroll'
- fixtureEl.innerHTML = [
- '<div style="height: 110vh; width: 100%"></div>'
- ].join('')
+ fixtureEl.innerHTML = '<div style="height: 110vh; width: 100%"></div>'
const result = new ScrollBarHelper().isOverflowing()
if (isScrollBarHidden()) {
- expect(result).toEqual(false)
+ expect(result).toBeFalse()
} else {
- expect(result).toEqual(true)
+ expect(result).toBeTrue()
}
})
it('should return false if body is not overflowing', () => {
doc.style.overflowY = 'hidden'
document.body.style.overflowY = 'hidden'
- fixtureEl.innerHTML = [
- '<div style="height: 110vh; width: 100%"></div>'
- ].join('')
+ fixtureEl.innerHTML = '<div style="height: 110vh; width: 100%"></div>'
const scrollBar = new ScrollBarHelper()
const result = scrollBar.isOverflowing()
- expect(result).toEqual(false)
+ expect(result).toBeFalse()
})
})
@@ -81,13 +79,11 @@ describe('ScrollBar', () => {
it('should return an integer greater than zero, if body is overflowing', () => {
doc.style.overflowY = 'scroll'
document.body.style.overflowY = 'scroll'
- fixtureEl.innerHTML = [
- '<div style="height: 110vh; width: 100%"></div>'
- ].join('')
+ fixtureEl.innerHTML = '<div style="height: 110vh; width: 100%"></div>'
const result = new ScrollBarHelper().getWidth()
if (isScrollBarHidden()) {
- expect(result).toBe(0)
+ expect(result).toEqual(0)
} else {
expect(result).toBeGreaterThan(1)
}
@@ -96,9 +92,7 @@ describe('ScrollBar', () => {
it('should return 0 if body is not overflowing', () => {
document.documentElement.style.overflowY = 'hidden'
document.body.style.overflowY = 'hidden'
- fixtureEl.innerHTML = [
- '<div style="height: 110vh; width: 100%"></div>'
- ].join('')
+ fixtureEl.innerHTML = '<div style="height: 110vh; width: 100%"></div>'
const result = new ScrollBarHelper().getWidth()
@@ -107,11 +101,11 @@ describe('ScrollBar', () => {
})
describe('hide - reset', () => {
- it('should adjust the inline padding of fixed elements which are full-width', done => {
+ it('should adjust the inline padding of fixed elements which are full-width', () => {
fixtureEl.innerHTML = [
- '<div style="height: 110vh; width: 100%">' +
- '<div class="fixed-top" id="fixed1" style="padding-right: 0px; width: 100vw"></div>',
- '<div class="fixed-top" id="fixed2" style="padding-right: 5px; width: 100vw"></div>',
+ '<div style="height: 110vh; width: 100%">',
+ ' <div class="fixed-top" id="fixed1" style="padding-right: 0px; width: 100vw"></div>',
+ ' <div class="fixed-top" id="fixed2" style="padding-right: 5px; width: 100vw"></div>',
'</div>'
].join('')
doc.style.overflowY = 'scroll'
@@ -128,25 +122,44 @@ describe('ScrollBar', () => {
let currentPadding = getPaddingX(fixedEl)
let currentPadding2 = getPaddingX(fixedEl2)
- expect(getPaddingAttr(fixedEl)).toEqual(`${originalPadding}px`, 'original fixed element padding should be stored in data-bs-padding-right')
- expect(getPaddingAttr(fixedEl2)).toEqual(`${originalPadding2}px`, 'original fixed element padding should be stored in data-bs-padding-right')
- expect(currentPadding).toEqual(expectedPadding, 'fixed element padding should be adjusted while opening')
- expect(currentPadding2).toEqual(expectedPadding2, 'fixed element padding should be adjusted while opening')
+ expect(getPaddingAttr(fixedEl)).toEqual(`${originalPadding}px`)
+ expect(getPaddingAttr(fixedEl2)).toEqual(`${originalPadding2}px`)
+ expect(currentPadding).toEqual(expectedPadding)
+ expect(currentPadding2).toEqual(expectedPadding2)
scrollBar.reset()
currentPadding = getPaddingX(fixedEl)
currentPadding2 = getPaddingX(fixedEl2)
- expect(getPaddingAttr(fixedEl)).toEqual(null, 'data-bs-padding-right should be cleared after closing')
- expect(getPaddingAttr(fixedEl2)).toEqual(null, 'data-bs-padding-right should be cleared after closing')
- expect(currentPadding).toEqual(originalPadding, 'fixed element padding should be reset after closing')
- expect(currentPadding2).toEqual(originalPadding2, 'fixed element padding should be reset after closing')
- done()
+ expect(getPaddingAttr(fixedEl)).toBeNull()
+ expect(getPaddingAttr(fixedEl2)).toBeNull()
+ expect(currentPadding).toEqual(originalPadding)
+ expect(currentPadding2).toEqual(originalPadding2)
})
- it('should adjust the inline margin and padding of sticky elements', done => {
+ it('should remove padding & margin if not existed before adjustment', () => {
fixtureEl.innerHTML = [
- '<div style="height: 110vh">' +
- '<div class="sticky-top" style="margin-right: 10px; padding-right: 20px; width: 100vw; height: 10px"></div>',
+ '<div style="height: 110vh; width: 100%">',
+ ' <div class="fixed" id="fixed" style="width: 100vw;"></div>',
+ ' <div class="sticky-top" id="sticky" style=" width: 100vw;"></div>',
+ '</div>'
+ ].join('')
+ doc.style.overflowY = 'scroll'
+
+ const fixedEl = fixtureEl.querySelector('#fixed')
+ const stickyEl = fixtureEl.querySelector('#sticky')
+ const scrollBar = new ScrollBarHelper()
+
+ scrollBar.hide()
+ scrollBar.reset()
+
+ expect(fixedEl.getAttribute('style').includes('padding-right')).toBeFalse()
+ expect(stickyEl.getAttribute('style').includes('margin-right')).toBeFalse()
+ })
+
+ it('should adjust the inline margin and padding of sticky elements', () => {
+ fixtureEl.innerHTML = [
+ '<div style="height: 110vh">',
+ ' <div class="sticky-top" style="margin-right: 10px; padding-right: 20px; width: 100vw; height: 10px"></div>',
'</div>'
].join('')
doc.style.overflowY = 'scroll'
@@ -159,23 +172,20 @@ describe('ScrollBar', () => {
const expectedPadding = originalPadding + scrollBar.getWidth()
scrollBar.hide()
- expect(getMarginAttr(stickyTopEl)).toEqual(`${originalMargin}px`, 'original sticky element margin should be stored in data-bs-margin-right')
- expect(getMarginX(stickyTopEl)).toEqual(expectedMargin, 'sticky element margin should be adjusted while opening')
- expect(getPaddingAttr(stickyTopEl)).toEqual(`${originalPadding}px`, 'original sticky element margin should be stored in data-bs-margin-right')
- expect(getPaddingX(stickyTopEl)).toEqual(expectedPadding, 'sticky element margin should be adjusted while opening')
+ expect(getMarginAttr(stickyTopEl)).toEqual(`${originalMargin}px`)
+ expect(getMarginX(stickyTopEl)).toEqual(expectedMargin)
+ expect(getPaddingAttr(stickyTopEl)).toEqual(`${originalPadding}px`)
+ expect(getPaddingX(stickyTopEl)).toEqual(expectedPadding)
scrollBar.reset()
- expect(getMarginAttr(stickyTopEl)).toEqual(null, 'data-bs-margin-right should be cleared after closing')
- expect(getMarginX(stickyTopEl)).toEqual(originalMargin, 'sticky element margin should be reset after closing')
- expect(getPaddingAttr(stickyTopEl)).toEqual(null, 'data-bs-margin-right should be cleared after closing')
- expect(getPaddingX(stickyTopEl)).toEqual(originalPadding, 'sticky element margin should be reset after closing')
- done()
+ expect(getMarginAttr(stickyTopEl)).toBeNull()
+ expect(getMarginX(stickyTopEl)).toEqual(originalMargin)
+ expect(getPaddingAttr(stickyTopEl)).toBeNull()
+ expect(getPaddingX(stickyTopEl)).toEqual(originalPadding)
})
it('should not adjust the inline margin and padding of sticky and fixed elements when element do not have full width', () => {
- fixtureEl.innerHTML = [
- '<div class="sticky-top" style="margin-right: 0px; padding-right: 0px; width: 50vw"></div>'
- ].join('')
+ fixtureEl.innerHTML = '<div class="sticky-top" style="margin-right: 0px; padding-right: 0px; width: 50vw"></div>'
const stickyTopEl = fixtureEl.querySelector('.sticky-top')
const originalMargin = getMarginX(stickyTopEl)
@@ -187,16 +197,16 @@ describe('ScrollBar', () => {
const currentMargin = getMarginX(stickyTopEl)
const currentPadding = getPaddingX(stickyTopEl)
- expect(currentMargin).toEqual(originalMargin, 'sticky element\'s margin should not be adjusted while opening')
- expect(currentPadding).toEqual(originalPadding, 'sticky element\'s padding should not be adjusted while opening')
+ expect(currentMargin).toEqual(originalMargin)
+ expect(currentPadding).toEqual(originalPadding)
scrollBar.reset()
})
it('should not put data-attribute if element doesn\'t have the proper style property, should just remove style property if element didn\'t had one', () => {
fixtureEl.innerHTML = [
- '<div style="height: 110vh; width: 100%">' +
- '<div class="sticky-top" id="sticky" style="width: 100vw"></div>',
+ '<div style="height: 110vh; width: 100%">',
+ ' <div class="sticky-top" id="sticky" style="width: 100vw"></div>',
'</div>'
].join('')
@@ -232,8 +242,8 @@ describe('ScrollBar', () => {
const scrollBarWidth = scrollBar.getWidth()
scrollBar.hide()
- expect(getPaddingX(document.body)).toEqual(scrollBarWidth, 'body does not have inline padding set')
- expect(document.body.style.color).toEqual('red', 'body still has other inline styles set')
+ expect(getPaddingX(document.body)).toEqual(scrollBarWidth)
+ expect(document.body.style.color).toEqual('red')
scrollBar.reset()
})
@@ -243,7 +253,7 @@ describe('ScrollBar', () => {
fixtureEl.innerHTML = [
'<style>',
' body {',
- ` padding-right: ${styleSheetPadding} }`,
+ ` padding-right: ${styleSheetPadding}`,
' }',
'</style>'
].join('')
@@ -253,7 +263,7 @@ describe('ScrollBar', () => {
el.style.paddingRight = inlineStylePadding
const originalPadding = getPaddingX(el)
- expect(originalPadding).toEqual(parseInt(inlineStylePadding)) // Respect only the inline style as it has prevails this of css
+ expect(originalPadding).toEqual(parseIntDecimal(inlineStylePadding)) // Respect only the inline style as it has prevails this of css
const originalOverFlow = 'auto'
el.style.overflow = originalOverFlow
const scrollBar = new ScrollBarHelper()
@@ -264,7 +274,7 @@ describe('ScrollBar', () => {
const currentPadding = getPaddingX(el)
expect(currentPadding).toEqual(scrollBarWidth + originalPadding)
- expect(currentPadding).toEqual(scrollBarWidth + parseInt(inlineStylePadding))
+ expect(currentPadding).toEqual(scrollBarWidth + parseIntDecimal(inlineStylePadding))
expect(getPaddingAttr(el)).toEqual(inlineStylePadding)
expect(getOverFlow(el)).toEqual('hidden')
expect(getOverFlowAttr(el)).toEqual(originalOverFlow)
@@ -273,9 +283,9 @@ describe('ScrollBar', () => {
const currentPadding1 = getPaddingX(el)
expect(currentPadding1).toEqual(originalPadding)
- expect(getPaddingAttr(el)).toEqual(null)
+ expect(getPaddingAttr(el)).toBeNull()
expect(getOverFlow(el)).toEqual(originalOverFlow)
- expect(getOverFlowAttr(el)).toEqual(null)
+ expect(getOverFlowAttr(el)).toBeNull()
})
it('should hide scrollbar and reset it to its initial value - respecting css rules', () => {
@@ -283,7 +293,7 @@ describe('ScrollBar', () => {
fixtureEl.innerHTML = [
'<style>',
' body {',
- ` padding-right: ${styleSheetPadding} }`,
+ ` padding-right: ${styleSheetPadding}`,
' }',
'</style>'
].join('')
@@ -299,7 +309,7 @@ describe('ScrollBar', () => {
const currentPadding = getPaddingX(el)
expect(currentPadding).toEqual(scrollBarWidth + originalPadding)
- expect(currentPadding).toEqual(scrollBarWidth + parseInt(styleSheetPadding))
+ expect(currentPadding).toEqual(scrollBarWidth + parseIntDecimal(styleSheetPadding))
expect(getPaddingAttr(el)).toBeNull() // We do not have to keep css padding
expect(getOverFlow(el)).toEqual('hidden')
expect(getOverFlowAttr(el)).toEqual(originalOverFlow)
@@ -308,9 +318,9 @@ describe('ScrollBar', () => {
const currentPadding1 = getPaddingX(el)
expect(currentPadding1).toEqual(originalPadding)
- expect(getPaddingAttr(el)).toEqual(null)
+ expect(getPaddingAttr(el)).toBeNull()
expect(getOverFlow(el)).toEqual(originalOverFlow)
- expect(getOverFlowAttr(el)).toEqual(null)
+ expect(getOverFlowAttr(el)).toBeNull()
})
it('should not adjust the inline body padding when it does not overflow', () => {
@@ -324,7 +334,7 @@ describe('ScrollBar', () => {
scrollBar.hide()
const currentPadding = getPaddingX(document.body)
- expect(currentPadding).toEqual(originalPadding, 'body padding should not be adjusted')
+ expect(currentPadding).toEqual(originalPadding)
scrollBar.reset()
})
@@ -344,7 +354,7 @@ describe('ScrollBar', () => {
const currentPadding = getPaddingX(document.body)
- expect(currentPadding).toEqual(originalPadding, 'body padding should not be adjusted')
+ expect(currentPadding).toEqual(originalPadding)
scrollBar.reset()
})
diff --git a/js/tests/unit/util/swipe.spec.js b/js/tests/unit/util/swipe.spec.js
index 474e34f65..9252d312b 100644
--- a/js/tests/unit/util/swipe.spec.js
+++ b/js/tests/unit/util/swipe.spec.js
@@ -1,7 +1,7 @@
-import { clearFixture, getFixture } from '../../helpers/fixture'
-import EventHandler from '../../../src/dom/event-handler'
-import Swipe from '../../../src/util/swipe'
-import { noop } from '../../../src/util'
+import EventHandler from '../../../src/dom/event-handler.js'
+import { noop } from '../../../src/util/index.js'
+import Swipe from '../../../src/util/swipe.js'
+import { clearFixture, getFixture } from '../../helpers/fixture.js'
describe('Swipe', () => {
const { Simulator, PointerEvent } = window
@@ -39,17 +39,17 @@ describe('Swipe', () => {
fixtureEl = getFixture()
const cssStyle = [
'<style>',
- ' #fixture .pointer-event {',
- ' touch-action: pan-y;',
+ ' #fixture .pointer-event {',
+ ' touch-action: pan-y;',
' }',
- ' #fixture div {',
- ' width: 300px;',
- ' height: 300px;',
+ ' #fixture div {',
+ ' width: 300px;',
+ ' height: 300px;',
' }',
'</style>'
].join('')
- fixtureEl.innerHTML = '<div id="swipeEl"></div>' + cssStyle
+ fixtureEl.innerHTML = `<div id="swipeEl"></div>${cssStyle}`
swipeEl = fixtureEl.querySelector('div')
})
@@ -78,74 +78,80 @@ describe('Swipe', () => {
})
describe('Config', () => {
- it('Test leftCallback', done => {
- const spyRight = jasmine.createSpy('spy')
- clearPointerEvents()
- defineDocumentElementOntouchstart()
- // eslint-disable-next-line no-new
- new Swipe(swipeEl, {
- leftCallback: () => {
- expect(spyRight).not.toHaveBeenCalled()
- restorePointerEvents()
- done()
- },
- rightCallback: spyRight
- })
-
- mockSwipeGesture(swipeEl, {
- pos: [300, 10],
- deltaX: -300
+ it('Test leftCallback', () => {
+ return new Promise(resolve => {
+ const spyRight = jasmine.createSpy('spy')
+ clearPointerEvents()
+ defineDocumentElementOntouchstart()
+ // eslint-disable-next-line no-new
+ new Swipe(swipeEl, {
+ leftCallback() {
+ expect(spyRight).not.toHaveBeenCalled()
+ restorePointerEvents()
+ resolve()
+ },
+ rightCallback: spyRight
+ })
+
+ mockSwipeGesture(swipeEl, {
+ pos: [300, 10],
+ deltaX: -300
+ })
})
})
- it('Test rightCallback', done => {
- const spyLeft = jasmine.createSpy('spy')
- clearPointerEvents()
- defineDocumentElementOntouchstart()
- // eslint-disable-next-line no-new
- new Swipe(swipeEl, {
- rightCallback: () => {
- expect(spyLeft).not.toHaveBeenCalled()
- restorePointerEvents()
- done()
- },
- leftCallback: spyLeft
- })
-
- mockSwipeGesture(swipeEl, {
- pos: [10, 10],
- deltaX: 300
+ it('Test rightCallback', () => {
+ return new Promise(resolve => {
+ const spyLeft = jasmine.createSpy('spy')
+ clearPointerEvents()
+ defineDocumentElementOntouchstart()
+ // eslint-disable-next-line no-new
+ new Swipe(swipeEl, {
+ rightCallback() {
+ expect(spyLeft).not.toHaveBeenCalled()
+ restorePointerEvents()
+ resolve()
+ },
+ leftCallback: spyLeft
+ })
+
+ mockSwipeGesture(swipeEl, {
+ pos: [10, 10],
+ deltaX: 300
+ })
})
})
- it('Test endCallback', done => {
- clearPointerEvents()
- defineDocumentElementOntouchstart()
- let isFirstTime = true
-
- const callback = () => {
- if (isFirstTime) {
- isFirstTime = false
- return
- }
+ it('Test endCallback', () => {
+ return new Promise(resolve => {
+ clearPointerEvents()
+ defineDocumentElementOntouchstart()
+ let isFirstTime = true
- expect().nothing()
- restorePointerEvents()
- done()
- }
+ const callback = () => {
+ if (isFirstTime) {
+ isFirstTime = false
+ return
+ }
- // eslint-disable-next-line no-new
- new Swipe(swipeEl, {
- endCallback: callback
- })
- mockSwipeGesture(swipeEl, {
- pos: [10, 10],
- deltaX: 300
- })
+ expect().nothing()
+ restorePointerEvents()
+ resolve()
+ }
- mockSwipeGesture(swipeEl, {
- pos: [300, 10],
- deltaX: -300
+ // eslint-disable-next-line no-new
+ new Swipe(swipeEl, {
+ endCallback: callback
+ })
+ mockSwipeGesture(swipeEl, {
+ pos: [10, 10],
+ deltaX: 300
+ })
+
+ mockSwipeGesture(swipeEl, {
+ pos: [300, 10],
+ deltaX: -300
+ })
})
})
})
@@ -157,7 +163,7 @@ describe('Swipe', () => {
deleteDocumentElementOntouchstart()
const swipe = new Swipe(swipeEl)
- spyOn(swipe, '_handleSwipe')
+ const spy = spyOn(swipe, '_handleSwipe')
mockSwipeGesture(swipeEl, {
pos: [300, 10],
@@ -167,56 +173,60 @@ describe('Swipe', () => {
})
restorePointerEvents()
- expect(swipe._handleSwipe).not.toHaveBeenCalled()
+ expect(spy).not.toHaveBeenCalled()
})
- it('should allow swipeRight and call "rightCallback" with pointer events', done => {
- if (!supportPointerEvent) {
- expect().nothing()
- done()
- return
- }
-
- const style = '#fixture .pointer-event { touch-action: none !important; }'
- fixtureEl.innerHTML += style
-
- defineDocumentElementOntouchstart()
- // eslint-disable-next-line no-new
- new Swipe(swipeEl, {
- rightCallback: () => {
- deleteDocumentElementOntouchstart()
+ it('should allow swipeRight and call "rightCallback" with pointer events', () => {
+ return new Promise(resolve => {
+ if (!supportPointerEvent) {
expect().nothing()
- done()
+ resolve()
+ return
}
- })
- mockSwipeGesture(swipeEl, { deltaX: 300 }, 'pointer')
- })
+ const style = '#fixture .pointer-event { touch-action: none !important; }'
+ fixtureEl.innerHTML += style
- it('should allow swipeLeft and call "leftCallback" with pointer events', done => {
- if (!supportPointerEvent) {
- expect().nothing()
- done()
- return
- }
+ defineDocumentElementOntouchstart()
+ // eslint-disable-next-line no-new
+ new Swipe(swipeEl, {
+ rightCallback() {
+ deleteDocumentElementOntouchstart()
+ expect().nothing()
+ resolve()
+ }
+ })
- const style = '#fixture .pointer-event { touch-action: none !important; }'
- fixtureEl.innerHTML += style
+ mockSwipeGesture(swipeEl, { deltaX: 300 }, 'pointer')
+ })
+ })
- defineDocumentElementOntouchstart()
- // eslint-disable-next-line no-new
- new Swipe(swipeEl, {
- leftCallback: () => {
+ it('should allow swipeLeft and call "leftCallback" with pointer events', () => {
+ return new Promise(resolve => {
+ if (!supportPointerEvent) {
expect().nothing()
- deleteDocumentElementOntouchstart()
- done()
+ resolve()
+ return
}
- })
- mockSwipeGesture(swipeEl, {
- pos: [300, 10],
- deltaX: -300
- }, 'pointer')
+ const style = '#fixture .pointer-event { touch-action: none !important; }'
+ fixtureEl.innerHTML += style
+
+ defineDocumentElementOntouchstart()
+ // eslint-disable-next-line no-new
+ new Swipe(swipeEl, {
+ leftCallback() {
+ expect().nothing()
+ deleteDocumentElementOntouchstart()
+ resolve()
+ }
+ })
+
+ mockSwipeGesture(swipeEl, {
+ pos: [300, 10],
+ deltaX: -300
+ }, 'pointer')
+ })
})
})
@@ -266,7 +276,7 @@ describe('Swipe', () => {
expect(Swipe.isSupported()).toBeTrue()
})
- it('should return "false" if "touchstart" not exists in document element and "navigator.maxTouchPoints" are zero (0)', () => {
+ it('should return "false" if "touchstart" not exists in document element and "navigator.maxTouchPoints" are zero (0)', () => {
Object.defineProperty(window.navigator, 'maxTouchPoints', () => 0)
deleteDocumentElementOntouchstart()
diff --git a/js/tests/unit/util/template-factory.spec.js b/js/tests/unit/util/template-factory.spec.js
index 842c480c2..07f4d91c7 100644
--- a/js/tests/unit/util/template-factory.spec.js
+++ b/js/tests/unit/util/template-factory.spec.js
@@ -1,5 +1,5 @@
-import { clearFixture, getFixture } from '../../helpers/fixture'
-import TemplateFactory from '../../../src/util/template-factory'
+import TemplateFactory from '../../../src/util/template-factory.js'
+import { clearFixture, getFixture } from '../../helpers/fixture.js'
describe('TemplateFactory', () => {
let fixtureEl
@@ -86,26 +86,26 @@ describe('TemplateFactory', () => {
const factory = new TemplateFactory({
extraClass: 'testClass'
})
- expect(factory.toHtml().classList.contains('testClass')).toBeTrue()
+ expect(factory.toHtml()).toHaveClass('testClass')
})
it('should add extra classes', () => {
const factory = new TemplateFactory({
extraClass: 'testClass testClass2'
})
- expect(factory.toHtml().classList.contains('testClass')).toBeTrue()
- expect(factory.toHtml().classList.contains('testClass2')).toBeTrue()
+ expect(factory.toHtml()).toHaveClass('testClass')
+ expect(factory.toHtml()).toHaveClass('testClass2')
})
it('should resolve class if function is given', () => {
const factory = new TemplateFactory({
- extraClass: arg => {
+ extraClass(arg) {
expect(arg).toEqual(factory)
return 'testClass'
}
})
- expect(factory.toHtml().classList.contains('testClass')).toBeTrue()
+ expect(factory.toHtml()).toHaveClass('testClass')
})
})
})
@@ -113,11 +113,11 @@ describe('TemplateFactory', () => {
describe('Content', () => {
it('add simple text content', () => {
const template = [
- '<div>' +
- '<div class="foo"></div>' +
- '<div class="foo2"></div>' +
+ '<div>',
+ ' <div class="foo"></div>',
+ ' <div class="foo2"></div>',
'</div>'
- ].join(' ')
+ ].join('')
const factory = new TemplateFactory({
template,
@@ -128,8 +128,8 @@ describe('TemplateFactory', () => {
})
const html = factory.toHtml()
- expect(html.querySelector('.foo').textContent).toBe('bar')
- expect(html.querySelector('.foo2').textContent).toBe('bar2')
+ expect(html.querySelector('.foo').textContent).toEqual('bar')
+ expect(html.querySelector('.foo2').textContent).toEqual('bar2')
})
it('should not fill template if selector not exists', () => {
@@ -140,7 +140,7 @@ describe('TemplateFactory', () => {
content: { '#bar': 'test' }
})
- expect(factory.toHtml().outerHTML).toBe('<div id="foo"></div>')
+ expect(factory.toHtml().outerHTML).toEqual('<div id="foo"></div>')
})
it('should remove template selector, if content is null', () => {
@@ -151,7 +151,7 @@ describe('TemplateFactory', () => {
content: { '#foo': null }
})
- expect(factory.toHtml().outerHTML).toBe('<div></div>')
+ expect(factory.toHtml().outerHTML).toEqual('<div></div>')
})
it('should resolve content if is function', () => {
@@ -162,7 +162,7 @@ describe('TemplateFactory', () => {
content: { '#foo': () => null }
})
- expect(factory.toHtml().outerHTML).toBe('<div></div>')
+ expect(factory.toHtml().outerHTML).toEqual('<div></div>')
})
it('if content is element and "config.html=false", should put content\'s textContent', () => {
@@ -176,9 +176,9 @@ describe('TemplateFactory', () => {
})
const fooEl = factory.toHtml().querySelector('#foo')
- expect(fooEl.innerHTML).not.toBe(contentElement.innerHTML)
- expect(fooEl.textContent).toBe(contentElement.textContent)
- expect(fooEl.textContent).toBe('foobar')
+ expect(fooEl.innerHTML).not.toEqual(contentElement.innerHTML)
+ expect(fooEl.textContent).toEqual(contentElement.textContent)
+ expect(fooEl.textContent).toEqual('foobar')
})
it('if content is element and "config.html=true", should put content\'s outerHtml as child', () => {
@@ -192,8 +192,8 @@ describe('TemplateFactory', () => {
})
const fooEl = factory.toHtml().querySelector('#foo')
- expect(fooEl.innerHTML).toBe(contentElement.outerHTML)
- expect(fooEl.textContent).toBe(contentElement.textContent)
+ expect(fooEl.innerHTML).toEqual(contentElement.outerHTML)
+ expect(fooEl.textContent).toEqual(contentElement.textContent)
})
})
@@ -245,14 +245,15 @@ describe('TemplateFactory', () => {
expect(factory.hasContent()).toBeFalse()
})
})
+
describe('changeContent', () => {
it('should change Content', () => {
const template = [
- '<div>' +
- '<div class="foo"></div>' +
- '<div class="foo2"></div>' +
+ '<div>',
+ ' <div class="foo"></div>',
+ ' <div class="foo2"></div>',
'</div>'
- ].join(' ')
+ ].join('')
const factory = new TemplateFactory({
template,
@@ -276,11 +277,11 @@ describe('TemplateFactory', () => {
it('should change only the given, content', () => {
const template = [
- '<div>' +
- '<div class="foo"></div>' +
- '<div class="foo2"></div>' +
+ '<div>',
+ ' <div class="foo"></div>',
+ ' <div class="foo2"></div>',
'</div>'
- ].join(' ')
+ ].join('')
const factory = new TemplateFactory({
template,
diff --git a/js/tests/visual/alert.html b/js/tests/visual/alert.html
index c5e2efafa..2433b9e8e 100644
--- a/js/tests/visual/alert.html
+++ b/js/tests/visual/alert.html
@@ -43,10 +43,6 @@
</div>
</div>
- <script src="../../dist/dom/event-handler.js"></script>
- <script src="../../dist/dom/selector-engine.js"></script>
- <script src="../../dist/dom/data.js"></script>
- <script src="../../dist/base-component.js"></script>
- <script src="../../dist/alert.js"></script>
+ <script src="../../../dist/js/bootstrap.bundle.js"></script>
</body>
</html>
diff --git a/js/tests/visual/button.html b/js/tests/visual/button.html
index 0bd2f1097..47c50889c 100644
--- a/js/tests/visual/button.html
+++ b/js/tests/visual/button.html
@@ -15,7 +15,7 @@
</button>
<p>For checkboxes and radio buttons, ensure that keyboard behavior is functioning correctly.</p>
- <p>Navigate to the checkboxes with the keyboard (generally, using <kbd>TAB</kbd> / <kbd>SHIFT + TAB</kbd>), and ensure that <kbd>SPACE</kbd> toggles the currently focused checkbox. Click on one of the checkboxes using the mouse, ensure that focus was correctly set on the actual checkbox, and that <kbd>SPACE</kbd> toggles the checkbox again.</p>
+ <p>Navigate to the checkboxes with the keyboard (generally, using <kbd>Tab</kbd> / <kbd><kbd>Shift</kbd> + <kbd>Tab</kbd></kbd>), and ensure that <kbd>Space</kbd> toggles the currently focused checkbox. Click on one of the checkboxes using the mouse, ensure that focus was correctly set on the actual checkbox, and that <kbd>Space</kbd> toggles the checkbox again.</p>
<div class="btn-group" data-bs-toggle="buttons">
<label class="btn btn-primary active">
@@ -29,7 +29,7 @@
</label>
</div>
- <p>Navigate to the radio button group with the keyboard (generally, using <kbd>TAB</kbd> / <kbd>SHIFT + TAB</kbd>). If no radio button was initially set to be selected, the first/last radio button should receive focus (depending on whether you navigated "forward" to the group with <kbd>TAB</kbd> or "backwards" using <kbd>SHIFT + TAB</kbd>). If a radio button was already selected, navigating with the keyboard should set focus to that particular radio button. Only one radio button in a group should receive focus at any given time. Ensure that the selected radio button can be changed by using the <kbd>←</kbd> and <kbd>→</kbd> arrow keys. Click on one of the radio buttons with the mouse, ensure that focus was correctly set on the actual radio button, and that <kbd>←</kbd> and <kbd>→</kbd> change the selected radio button again.</p>
+ <p>Navigate to the radio button group with the keyboard (generally, using <kbd>Tab</kbd> / <kbd><kbd>Shift</kbd> + <kbd>Tab</kbd></kbd>). If no radio button was initially set to be selected, the first/last radio button should receive focus (depending on whether you navigated "forward" to the group with <kbd>Tab</kbd> or "backwards" using <kbd><kbd>Shift</kbd> + <kbd>Tab</kbd></kbd>). If a radio button was already selected, navigating with the keyboard should set focus to that particular radio button. Only one radio button in a group should receive focus at any given time. Ensure that the selected radio button can be changed by using the <kbd>←</kbd> and <kbd>→</kbd> arrow keys. Click on one of the radio buttons with the mouse, ensure that focus was correctly set on the actual radio button, and that <kbd>←</kbd> and <kbd>→</kbd> change the selected radio button again.</p>
<div class="btn-group" data-bs-toggle="buttons">
<label class="btn btn-primary active">
@@ -44,10 +44,6 @@
</div>
</div>
- <script src="../../dist/dom/event-handler.js"></script>
- <script src="../../dist/dom/selector-engine.js"></script>
- <script src="../../dist/dom/data.js"></script>
- <script src="../../dist/base-component.js"></script>
- <script src="../../dist/button.js"></script>
+ <script src="../../../dist/js/bootstrap.bundle.js"></script>
</body>
</html>
diff --git a/js/tests/visual/carousel.html b/js/tests/visual/carousel.html
index 561b6ce5a..1b2de5291 100644
--- a/js/tests/visual/carousel.html
+++ b/js/tests/visual/carousel.html
@@ -45,24 +45,19 @@
</div>
</div>
- <script src="../../dist/dom/event-handler.js"></script>
- <script src="../../dist/dom/selector-engine.js"></script>
- <script src="../../dist/dom/manipulator.js"></script>
- <script src="../../dist/dom/data.js"></script>
- <script src="../../dist/base-component.js"></script>
- <script src="../../dist/carousel.js"></script>
+ <script src="../../../dist/js/bootstrap.bundle.js"></script>
<script>
- var t0
- var t1
- var carousel = document.getElementById('carousel-example-generic')
+ let t0
+ let t1
+ const carousel = document.getElementById('carousel-example-generic')
// Test to show that the carousel doesn't slide when the current tab isn't visible
// Test to show that transition-duration can be changed with css
- carousel.addEventListener('slid.bs.carousel', function (event) {
+ carousel.addEventListener('slid.bs.carousel', event => {
t1 = performance.now()
- console.log('transition-duration took ' + (t1 - t0) + 'ms, slid at ' + event.timeStamp)
+ console.log(`transition-duration took ${t1 - t0}ms, slid at ${event.timeStamp}`)
})
- carousel.addEventListener('slide.bs.carousel', function () {
+ carousel.addEventListener('slide.bs.carousel', () => {
t0 = performance.now()
})
</script>
diff --git a/js/tests/visual/collapse.html b/js/tests/visual/collapse.html
index e4e534bcf..2782c56b6 100644
--- a/js/tests/visual/collapse.html
+++ b/js/tests/visual/collapse.html
@@ -71,11 +71,6 @@
</div>
</div>
- <script src="../../dist/dom/event-handler.js"></script>
- <script src="../../dist/dom/selector-engine.js"></script>
- <script src="../../dist/dom/manipulator.js"></script>
- <script src="../../dist/dom/data.js"></script>
- <script src="../../dist/base-component.js"></script>
- <script src="../../dist/collapse.js"></script>
+ <script src="../../../dist/js/bootstrap.bundle.js"></script>
</body>
</html>
diff --git a/js/tests/visual/dropdown.html b/js/tests/visual/dropdown.html
index f1dd705f3..04cf06d7b 100644
--- a/js/tests/visual/dropdown.html
+++ b/js/tests/visual/dropdown.html
@@ -10,7 +10,7 @@
<div class="container">
<h1>Dropdown <small>Bootstrap Visual Test</small></h1>
- <nav class="navbar navbar-expand-md navbar-light bg-light">
+ <nav class="navbar navbar-expand-md bg-light">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
@@ -28,8 +28,8 @@
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item dropdown">
- <a class="nav-link dropdown-toggle" href="#" id="dropdown" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</a>
- <ul class="dropdown-menu" aria-labelledby="dropdown">
+ <a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</a>
+ <ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
@@ -50,8 +50,8 @@
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item dropdown">
- <a class="nav-link dropdown-toggle" href="#" id="dropdown2" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</a>
- <ul class="dropdown-menu" aria-labelledby="dropdown2">
+ <a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</a>
+ <ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
@@ -103,7 +103,7 @@
</div>
<div class="col-sm-12 mt-4">
- <div class="btn-group dropup" role="group">
+ <div class="btn-group dropup">
<a href="#" class="btn btn-secondary">Dropup split align end</a>
<button type="button" id="dropdown-page-subheader-button-3" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Product actions</span>
@@ -125,7 +125,7 @@
</div>
<div class="col-sm-12 mt-4">
- <div class="btn-group dropend" role="group">
+ <div class="btn-group dropend">
<a href="#" class="btn btn-secondary">Dropend split</a>
<button type="button" id="dropdown-page-subheader-button-4" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Product actions</span>
@@ -137,17 +137,17 @@
</div>
</div>
<div class="btn-group dropend">
- <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuRight" data-bs-toggle="dropdown" aria-expanded="false">
+ <button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
Dropend
</button>
- <div class="dropdown-menu" aria-labelledby="dropdownMenuRight">
+ <div class="dropdown-menu">
<button class="dropdown-item" type="button">Action</button>
<button class="dropdown-item" type="button">Another action</button>
<button class="dropdown-item" type="button">Something else here</button>
</div>
</div>
<!-- dropstart -->
- <div class="btn-group dropstart" role="group">
+ <div class="btn-group dropstart">
<a href="#" class="btn btn-secondary">Dropstart split</a>
<button type="button" id="dropdown-page-subheader-button-5" class="btn btn-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
<span class="visually-hidden">Product actions</span>
@@ -159,10 +159,10 @@
</div>
</div>
<div class="btn-group dropstart">
- <button class="btn btn-secondary dropdown-toggle" type="button" id="dropstartMenu" data-bs-toggle="dropdown" aria-expanded="false">
+ <button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
Dropstart
</button>
- <div class="dropdown-menu" aria-labelledby="dropstartMenu">
+ <div class="dropdown-menu">
<button class="dropdown-item" type="button">Action</button>
<button class="dropdown-item" type="button">Another action</button>
<button class="dropdown-item" type="button">Something else here</button>
@@ -187,10 +187,10 @@
</div>
<div class="col-sm-3 mt-4">
<div class="dropdown">
- <button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownMenuButton" data-bs-toggle="dropdown" data-bs-display="static" aria-expanded="false">
+ <button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" data-bs-display="static" aria-expanded="false">
Dropdown menu without Popper
</button>
- <ul class="dropdown-menu" aria-labelledby="dropdownMenuButton">
+ <ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
@@ -200,13 +200,6 @@
</div>
</div>
- <script src="../../../node_modules/@popperjs/core/dist/umd/popper.min.js"></script>
- <script src="../../dist/dom/event-handler.js"></script>
- <script src="../../dist/dom/selector-engine.js"></script>
- <script src="../../dist/dom/data.js"></script>
- <script src="../../dist/dom/manipulator.js"></script>
- <script src="../../dist/base-component.js"></script>
- <script src="../../dist/dropdown.js"></script>
- <script src="../../dist/collapse.js"></script>
+ <script src="../../../dist/js/bootstrap.bundle.js"></script>
</body>
</html>
diff --git a/js/tests/visual/floating-label.html b/js/tests/visual/floating-label.html
new file mode 100644
index 000000000..f78442eff
--- /dev/null
+++ b/js/tests/visual/floating-label.html
@@ -0,0 +1,337 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <link href="../../../dist/css/bootstrap.min.css" rel="stylesheet">
+ <title>Form</title>
+ <style></style>
+ </head>
+ <body>
+ <div class="container py-5 d-flex flex-column gap-3">
+ <div class="form-floating">
+ <input type="email" class="form-control" id="floatingInput" placeholder="[email protected]">
+ <label for="floatingInput">Email address</label>
+ </div>
+ <div class="form-floating">
+ <input type="email" class="form-control" id="floatingInput1" placeholder="[email protected]">
+ <label for="floatingInput1">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <form class="form-floating">
+ <input type="email" class="form-control" id="floatingInputValue" placeholder="[email protected]" value="[email protected]">
+ <label for="floatingInputValue">Input with value</label>
+ </form>
+ <form class="form-floating">
+ <input type="email" class="form-control" id="floatingInputValue1" placeholder="[email protected]" value="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.">
+ <label for="floatingInputValue1">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </form>
+ <form class="form-floating">
+ <input type="email" class="form-control is-invalid" id="floatingInputInvalid" placeholder="[email protected]" value="[email protected]">
+ <label for="floatingInputInvalid">Invalid input</label>
+ </form>
+ <form class="form-floating">
+ <input type="email" class="form-control is-invalid" id="floatingInputInvalid1" placeholder="[email protected]" value="[email protected]">
+ <label for="floatingInputInvalid1">Invalid input</label>
+ </form>
+ <form class="form-floating">
+ <input type="email" class="form-control is-invalid" id="floatingInputInvalid2" placeholder="[email protected]">
+ <label for="floatingInputInvalid2">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </form>
+ <form class="form-floating">
+ <input type="email" class="form-control is-invalid" id="floatingInputInvalid3" placeholder="[email protected]" value="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.">
+ <label for="floatingInputInvalid3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </form>
+ <div class="form-floating">
+ <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea"></textarea>
+ <label for="floatingTextarea">Comments</label>
+ </div>
+ <div class="form-floating">
+ <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea1"></textarea>
+ <label for="floatingTextarea1">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating">
+ <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea2" style="height: 50px"></textarea>
+ <label for="floatingTextarea2">Comments</label>
+ </div>
+ <div class="form-floating">
+ <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea3" style="height: 50px"></textarea>
+ <label for="floatingTextarea3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating">
+ <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea4" style="height: 50px">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</textarea>
+ <label for="floatingTextarea4">Comments</label>
+ </div>
+ <div class="form-floating">
+ <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea5" style="height: 50px">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</textarea>
+ <label for="floatingTextarea5">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating">
+ <textarea class="form-control is-invalid" placeholder="Leave a comment here" id="floatingTextarea6" style="height: 50px">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</textarea>
+ <label for="floatingTextarea6">Comments</label>
+ </div>
+ <div class="form-floating">
+ <textarea class="form-control is-invalid" placeholder="Leave a comment here" id="floatingTextarea7" style="height: 50px">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</textarea>
+ <label for="floatingTextarea7">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating">
+ <select class="form-select" id="floatingSelect" aria-label="Floating label select example">
+ <option selected>Open this select menu</option>
+ <option value="1">One</option>
+ <option value="2">Two</option>
+ <option value="3">Three</option>
+ </select>
+ <label for="floatingSelect">Works with selects</label>
+ </div>
+ <div class="form-floating">
+ <select class="form-select is-invalid" id="floatingSelect1" aria-label="Floating label select example">
+ <option selected>Open this select menu</option>
+ <option value="1">One</option>
+ <option value="2">Two</option>
+ <option value="3">Three</option>
+ </select>
+ <label for="floatingSelect1">Works with selects</label>
+ </div>
+ <div class="form-floating">
+ <select class="form-select" id="floatingSelect2" aria-label="Floating label select example">
+ <option selected>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ <option value="1">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ <option value="2">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ <option value="3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ </select>
+ <label for="floatingSelect2">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating">
+ <select class="form-select is-invalid" id="floatingSelect3" aria-label="Floating label select example">
+ <option selected>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ <option value="1">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ <option value="2">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ <option value="3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ </select>
+ <label for="floatingSelect3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating mb-3">
+ <input type="email" class="form-control" id="floatingInputDisabled" placeholder="[email protected]" disabled>
+ <label for="floatingInputDisabled">Email address</label>
+ </div>
+ <div class="form-floating mb-3">
+ <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextareaDisabled" disabled></textarea>
+ <label for="floatingTextareaDisabled">Comments</label>
+ </div>
+ <div class="form-floating mb-3">
+ <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextareaDisabled1" style="height: 50px" disabled>Disabled textarea with some text inside</textarea>
+ <label for="floatingTextareaDisabled1">Comments</label>
+ </div>
+ <div class="form-floating">
+ <select class="form-select" id="floatingSelectDisabled" aria-label="Floating label disabled select example" disabled>
+ <option selected>Open this select menu</option>
+ <option value="1">One</option>
+ <option value="2">Two</option>
+ <option value="3">Three</option>
+ </select>
+ <label for="floatingSelectDisabled">Works with selects</label>
+ </div>
+ <div class="form-floating mb-3">
+ <input type="email" class="form-control" id="floatingInputDisabled1" placeholder="[email protected]" disabled>
+ <label for="floatingInputDisabled1">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating mb-3">
+ <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextareaDisabled2" disabled></textarea>
+ <label for="floatingTextareaDisabled2">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating mb-3">
+ <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextareaDisabled3" style="height: 50px" disabled>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</textarea>
+ <label for="floatingTextareaDisabled3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating">
+ <select class="form-select" id="floatingSelectDisabled1" aria-label="Floating label disabled select example" disabled>
+ <option selected>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ <option value="1">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ <option value="2">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ <option value="3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ </select>
+ <label for="floatingSelectDisabled1">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating mb-3">
+ <input type="email" readonly class="form-control-plaintext" id="floatingEmptyPlaintextInput" placeholder="[email protected]">
+ <label for="floatingEmptyPlaintextInput">Empty input</label>
+ </div>
+ <div class="form-floating mb-3">
+ <input type="email" readonly class="form-control-plaintext" id="floatingPlaintextInput" placeholder="[email protected]" value="[email protected]">
+ <label for="floatingPlaintextInput">Input with value</label>
+ </div>
+ <div class="form-floating mb-3">
+ <input type="email" readonly class="form-control-plaintext" id="floatingEmptyPlaintextInput1" placeholder="[email protected]">
+ <label for="floatingEmptyPlaintextInput1">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating mb-3">
+ <input type="email" readonly class="form-control-plaintext" id="floatingPlaintextInput1" placeholder="[email protected]" value="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.">
+ <label for="floatingPlaintextInput1">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ </div>
+
+ <div class="container-fluid bg-body" data-bs-theme="dark">
+ <div class="container py-5 d-flex flex-column gap-3">
+ <div class="form-floating">
+ <input type="email" class="form-control" id="floatingInput2" placeholder="[email protected]">
+ <label for="floatingInput2">Email address</label>
+ </div>
+ <div class="form-floating">
+ <input type="email" class="form-control" id="floatingInput3" placeholder="[email protected]">
+ <label for="floatingInput3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <form class="form-floating">
+ <input type="email" class="form-control" id="floatingInputValue2" placeholder="[email protected]" value="[email protected]">
+ <label for="floatingInputValue2">Input with value</label>
+ </form>
+ <form class="form-floating">
+ <input type="email" class="form-control" id="floatingInputValue3" placeholder="[email protected]" value="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.">
+ <label for="floatingInputValue3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </form>
+ <form class="form-floating">
+ <input type="email" class="form-control is-invalid" id="floatingInputInvalid4" placeholder="[email protected]" value="[email protected]">
+ <label for="floatingInputInvalid4">Invalid input</label>
+ </form>
+ <form class="form-floating">
+ <input type="email" class="form-control is-invalid" id="floatingInputInvalid5" placeholder="[email protected]" value="[email protected]">
+ <label for="floatingInputInvalid5">Invalid input</label>
+ </form>
+ <form class="form-floating">
+ <input type="email" class="form-control is-invalid" id="floatingInputInvalid6" placeholder="[email protected]">
+ <label for="floatingInputInvalid6">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </form>
+ <form class="form-floating">
+ <input type="email" class="form-control is-invalid" id="floatingInputInvalid7" placeholder="[email protected]" value="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.">
+ <label for="floatingInputInvalid7">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </form>
+ <div class="form-floating">
+ <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea8"></textarea>
+ <label for="floatingTextarea8">Comments</label>
+ </div>
+ <div class="form-floating">
+ <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea9"></textarea>
+ <label for="floatingTextarea9">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating">
+ <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea10" style="height: 50px"></textarea>
+ <label for="floatingTextarea10">Comments</label>
+ </div>
+ <div class="form-floating">
+ <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea11" style="height: 50px"></textarea>
+ <label for="floatingTextarea11">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating">
+ <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea12" style="height: 50px">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</textarea>
+ <label for="floatingTextarea12">Comments</label>
+ </div>
+ <div class="form-floating">
+ <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextarea13" style="height: 50px">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</textarea>
+ <label for="floatingTextarea13">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating">
+ <textarea class="form-control is-invalid" placeholder="Leave a comment here" id="floatingTextarea14" style="height: 50px">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</textarea>
+ <label for="floatingTextarea14">Comments</label>
+ </div>
+ <div class="form-floating">
+ <textarea class="form-control is-invalid" placeholder="Leave a comment here" id="floatingTextarea15" style="height: 50px">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</textarea>
+ <label for="floatingTextarea15">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating">
+ <select class="form-select" id="floatingSelect4" aria-label="Floating label select example">
+ <option selected>Open this select menu</option>
+ <option value="1">One</option>
+ <option value="2">Two</option>
+ <option value="3">Three</option>
+ </select>
+ <label for="floatingSelect4">Works with selects</label>
+ </div>
+ <div class="form-floating">
+ <select class="form-select is-invalid" id="floatingSelect5" aria-label="Floating label select example">
+ <option selected>Open this select menu</option>
+ <option value="1">One</option>
+ <option value="2">Two</option>
+ <option value="3">Three</option>
+ </select>
+ <label for="floatingSelect5">Works with selects</label>
+ </div>
+ <div class="form-floating">
+ <select class="form-select" id="floatingSelect6" aria-label="Floating label select example">
+ <option selected>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ <option value="1">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ <option value="2">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ <option value="3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ </select>
+ <label for="floatingSelect6">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating">
+ <select class="form-select is-invalid" id="floatingSelect7" aria-label="Floating label select example">
+ <option selected>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ <option value="1">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ <option value="2">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ <option value="3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ </select>
+ <label for="floatingSelect7">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating mb-3">
+ <input type="email" class="form-control" id="floatingInputDisabled2" placeholder="[email protected]" disabled>
+ <label for="floatingInputDisabled2">Email address</label>
+ </div>
+ <div class="form-floating mb-3">
+ <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextareaDisabled4" disabled></textarea>
+ <label for="floatingTextareaDisabled4">Comments</label>
+ </div>
+ <div class="form-floating mb-3">
+ <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextareaDisabled5" style="height: 50px" disabled>Disabled textarea with some text inside</textarea>
+ <label for="floatingTextareaDisabled5">Comments</label>
+ </div>
+ <div class="form-floating">
+ <select class="form-select" id="floatingSelectDisabled2" aria-label="Floating label disabled select example" disabled>
+ <option selected>Open this select menu</option>
+ <option value="1">One</option>
+ <option value="2">Two</option>
+ <option value="3">Three</option>
+ </select>
+ <label for="floatingSelectDisabled2">Works with selects</label>
+ </div>
+ <div class="form-floating mb-3">
+ <input type="email" class="form-control" id="floatingInputDisabled3" placeholder="[email protected]" disabled>
+ <label for="floatingInputDisabled3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating mb-3">
+ <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextareaDisabled6" disabled></textarea>
+ <label for="floatingTextareaDisabled6">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating mb-3">
+ <textarea class="form-control" placeholder="Leave a comment here" id="floatingTextareaDisabled7" style="height: 50px" disabled>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</textarea>
+ <label for="floatingTextareaDisabled7">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating">
+ <select class="form-select" id="floatingSelectDisabled3" aria-label="Floating label disabled select example" disabled>
+ <option selected>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ <option value="1">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ <option value="2">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ <option value="3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</option>
+ </select>
+ <label for="floatingSelectDisabled3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating mb-3">
+ <input type="email" readonly class="form-control-plaintext" id="floatingEmptyPlaintextInput2" placeholder="[email protected]">
+ <label for="floatingEmptyPlaintextInput2">Empty input</label>
+ </div>
+ <div class="form-floating mb-3">
+ <input type="email" readonly class="form-control-plaintext" id="floatingPlaintextInput2" placeholder="[email protected]" value="[email protected]">
+ <label for="floatingPlaintextInput2">Input with value</label>
+ </div>
+ <div class="form-floating mb-3">
+ <input type="email" readonly class="form-control-plaintext" id="floatingEmptyPlaintextInput3" placeholder="[email protected]">
+ <label for="floatingEmptyPlaintextInput3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ <div class="form-floating mb-3">
+ <input type="email" readonly class="form-control-plaintext" id="floatingPlaintextInput3" placeholder="[email protected]" value="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.">
+ <label for="floatingPlaintextInput3">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum mattis mi at lobortis rutrum. Phasellus varius a risus non lobortis. Ut id congue enim. Quisque facilisis elit ac elit dapibus aliquet nec sit amet arcu. Morbi vitae ultricies eros. Proin varius augue in tristique pretium. Morbi at ullamcorper elit, at ullamcorper massa. Vivamus suscipit quam quis viverra eleifend.</label>
+ </div>
+ </div>
+ </div>
+
+ <script src="../../../dist/js/bootstrap.bundle.js"></script>
+ </body>
+</html>
diff --git a/js/tests/visual/input.html b/js/tests/visual/input.html
new file mode 100644
index 000000000..1e5eec2d1
--- /dev/null
+++ b/js/tests/visual/input.html
@@ -0,0 +1,78 @@
+<!doctype html>
+<html lang="en">
+ <head>
+ <meta charset="utf-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1">
+ <link href="../../../dist/css/bootstrap.min.css" rel="stylesheet">
+ <title>Form</title>
+ <style></style>
+ </head>
+ <body>
+ <div class="container">
+ <h1>Input <small>Bootstrap Visual Test</small></h1>
+
+ <h2>No layout</h2>
+
+ <div class="mb-3">
+ Text
+ <input class="form-control">
+ </div>
+ <div class="mb-3">
+ Email
+ <input type="email" class="form-control">
+ </div>
+ <div class="mb-3">
+ Number
+ <input type="number" class="form-control">
+ </div>
+ <div class="mb-3">
+ Date
+ <input type="date" class="form-control">
+ </div>
+
+ <h2>Flex</h2>
+
+ <div class="d-flex flex-wrap gap-3 mb-3">
+ <div>
+ Text
+ <input class="form-control">
+ </div>
+ <div>
+ Email
+ <input type="email" class="form-control">
+ </div>
+ <div>
+ Number
+ <input type="number" class="form-control">
+ </div>
+ <div>
+ Date
+ <input type="date" class="form-control">
+ </div>
+ </div>
+
+ <h2>Grid</h2>
+
+ <div class="row mb-3">
+ <div class="col">
+ Text
+ <input class="form-control">
+ </div>
+ <div class="col">
+ Email
+ <input type="email" class="form-control">
+ </div>
+ <div class="col">
+ Number
+ <input type="number" class="form-control">
+ </div>
+ <div class="col">
+ Date
+ <input type="date" class="form-control">
+ </div>
+ </div>
+ </div>
+
+ <script src="../../../dist/js/bootstrap.bundle.js"></script>
+ </body>
+</html>
diff --git a/js/tests/visual/modal.html b/js/tests/visual/modal.html
index 719e9cc10..efb5127b5 100644
--- a/js/tests/visual/modal.html
+++ b/js/tests/visual/modal.html
@@ -13,21 +13,25 @@
</style>
</head>
<body>
- <nav class="navbar navbar-full navbar-dark bg-dark">
- <button class="navbar-toggler hidden-lg-up" type="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation"></button>
- <div class="collapse navbar-expand-md" id="navbarResponsive">
- <a class="navbar-brand" href="#">This shouldn't jump!</a>
- <ul class="navbar-nav">
- <li class="nav-item">
- <a class="nav-link active" href="#" aria-current="page">Home</a>
- </li>
- <li class="nav-item">
- <a class="nav-link" href="#">Link</a>
- </li>
- <li class="nav-item">
- <a class="nav-link" href="#">Link</a>
- </li>
- </ul>
+ <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
+ <div class="container-fluid">
+ <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarResponsive" aria-controls="navbarResponsive" aria-expanded="false" aria-label="Toggle navigation">
+ <span class="navbar-toggler-icon"></span>
+ </button>
+ <div class="collapse navbar-collapse" id="navbarResponsive">
+ <a class="navbar-brand" href="#">This shouldn't jump!</a>
+ <ul class="navbar-nav">
+ <li class="nav-item">
+ <a class="nav-link active" href="#" aria-current="page">Home</a>
+ </li>
+ <li class="nav-item">
+ <a class="nav-link" href="#">Link</a>
+ </li>
+ <li class="nav-item">
+ <a class="nav-link" href="#">Link</a>
+ </li>
+ </ul>
+ </div>
</div>
</nav>
@@ -38,7 +42,7 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
- <h4 class="modal-title" id="myModalLabel">Modal title</h4>
+ <h1 class="modal-title fs-4" id="myModalLabel">Modal title</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
@@ -123,7 +127,7 @@
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
- <h4 class="modal-title" id="firefoxModalLabel">Firefox Bug Test</h4>
+ <h1 class="modal-title fs-4" id="firefoxModalLabel">Firefox Bug Test</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
@@ -147,7 +151,7 @@
<div class="modal-dialog" style="transition-duration: inherit;">
<div class="modal-content">
<div class="modal-header">
- <h4 class="modal-title" id="slowModalLabel">Lorem slowly</h4>
+ <h1 class="modal-title fs-4" id="slowModalLabel">Lorem slowly</h1>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
@@ -184,11 +188,11 @@
<br><br>
- <div class="bg-dark text-white p-2" id="tall" style="display: none;">
+ <div class="text-bg-dark p-2" id="tall" style="display: none;">
Tall body content to force the page to have a scrollbar.
</div>
- <button type="button" class="btn btn-secondary btn-lg" data-bs-toggle="modal" data-bs-target="&#x3C;div class=&#x22;modal fade the-bad&#x22; tabindex=&#x22;-1&#x22;&#x3E;&#x3C;div class=&#x22;modal-dialog&#x22;&#x3E;&#x3C;div class=&#x22;modal-content&#x22;&#x3E;&#x3C;div class=&#x22;modal-header&#x22;&#x3E;&#x3C;button type=&#x22;button&#x22; class=&#x22;btn-close&#x22; data-bs-dismiss=&#x22;modal&#x22; aria-label=&#x22;Close&#x22;&#x3E;&#x3C;span aria-hidden=&#x22;true&#x22;&#x3E;&#x26;times;&#x3C;/span&#x3E;&#x3C;/button&#x3E;&#x3C;h4 class=&#x22;modal-title&#x22;&#x3E;The Bad Modal&#x3C;/h4&#x3E;&#x3C;/div&#x3E;&#x3C;div class=&#x22;modal-body&#x22;&#x3E;This modal&#x27;s HTTML source code is declared inline, inside the data-bs-target attribute of it&#x27;s show-button&#x3C;/div&#x3E;&#x3C;/div&#x3E;&#x3C;/div&#x3E;&#x3C;/div&#x3E;">
+ <button type="button" class="btn btn-secondary btn-lg" data-bs-toggle="modal" data-bs-target="&#x3C;div class=&#x22;modal fade the-bad&#x22; tabindex=&#x22;-1&#x22;&#x3E;&#x3C;div class=&#x22;modal-dialog&#x22;&#x3E;&#x3C;div class=&#x22;modal-content&#x22;&#x3E;&#x3C;div class=&#x22;modal-header&#x22;&#x3E;&#x3C;button type=&#x22;button&#x22; class=&#x22;btn-close&#x22; data-bs-dismiss=&#x22;modal&#x22; aria-label=&#x22;Close&#x22;&#x3E;&#x3C;span aria-hidden=&#x22;true&#x22;&#x3E;&#x26;times;&#x3C;/span&#x3E;&#x3C;/button&#x3E;&#x3C;h1 class=&#x22;modal-title fs-4&#x22;&#x3E;The Bad Modal&#x3C;/h1&#x3E;&#x3C;/div&#x3E;&#x3C;div class=&#x22;modal-body&#x22;&#x3E;This modal&#x27;s HTTML source code is declared inline, inside the data-bs-target attribute of it&#x27;s show-button&#x3C;/div&#x3E;&#x3C;/div&#x3E;&#x3C;/div&#x3E;&#x3C;/div&#x3E;">
Modal with an XSS inside the data-bs-target
</button>
@@ -199,19 +203,12 @@
</button>
</div>
- <script src="../../../node_modules/@popperjs/core/dist/umd/popper.min.js"></script>
- <script src="../../dist/dom/event-handler.js"></script>
- <script src="../../dist/dom/selector-engine.js"></script>
- <script src="../../dist/dom/data.js"></script>
- <script src="../../dist/dom/manipulator.js"></script>
- <script src="../../dist/base-component.js"></script>
- <script src="../../dist/modal.js"></script>
- <script src="../../dist/collapse.js"></script>
- <script src="../../dist/tooltip.js"></script>
- <script src="../../dist/popover.js"></script>
+ <script src="../../../dist/js/bootstrap.bundle.js"></script>
<script>
- var ffBugTestResult = document.getElementById('ff-bug-test-result')
- var firefoxTestDone = false
+ /* global bootstrap: false */
+
+ const ffBugTestResult = document.getElementById('ff-bug-test-result')
+ const firefoxTestDone = false
function reportFirefoxTestResult(result) {
if (!firefoxTestDone) {
@@ -220,44 +217,34 @@
}
}
- var popoverElements = document.querySelectorAll('[data-bs-toggle="popover"]')
- for (const popoverEl of popoverElements) {
- new Popover(popoverEl)
- }
- var tooltipElements = document.querySelectorAll('[data-bs-toggle="tooltip"]')
- for (const tooltipEl of tooltipElements) {
- new Tooltip(tooltipEl)
- }
+ document.querySelectorAll('[data-bs-toggle="popover"]').forEach(popoverEl => new bootstrap.Popover(popoverEl))
- var tallToggle = document.getElementById('tall-toggle')
- var tall = document.getElementById('tall')
- tallToggle.addEventListener('click', function () {
- if (tall.style.display === 'none') {
- tall.style.display = 'block'
- } else {
- tall.style.display = 'none'
- }
+ document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(tooltipEl => new bootstrap.Tooltip(tooltipEl))
+
+ const tall = document.getElementById('tall')
+ document.getElementById('tall-toggle').addEventListener('click', () => {
+ tall.style.display = tall.style.display === 'none' ? 'block' : 'none'
})
- var ffBugInput = document.getElementById('ff-bug-input')
- var firefoxModal = document.getElementById('firefoxModal')
+ const ffBugInput = document.getElementById('ff-bug-input')
+ const firefoxModal = document.getElementById('firefoxModal')
function handlerClickFfBugInput() {
firefoxModal.addEventListener('focus', reportFirefoxTestResult.bind(false))
ffBugInput.addEventListener('focus', reportFirefoxTestResult.bind(true))
ffBugInput.removeEventListener('focus', handlerClickFfBugInput)
}
+
ffBugInput.addEventListener('focus', handlerClickFfBugInput)
- var btnPreventModal = document.getElementById('btnPreventModal')
- var modalFf = new Modal(firefoxModal)
+ const modalFf = new bootstrap.Modal(firefoxModal)
- btnPreventModal.addEventListener('click', function () {
- function shownFirefoxModal() {
+ document.getElementById('btnPreventModal').addEventListener('click', () => {
+ const shownFirefoxModal = () => {
modalFf.hide()
firefoxModal.removeEventListener('shown.bs.modal', hideFirefoxModal)
}
- function hideFirefoxModal(event) {
+ const hideFirefoxModal = event => {
event.preventDefault()
firefoxModal.removeEventListener('hide.bs.modal', hideFirefoxModal)
@@ -275,16 +262,16 @@
})
// Test transition duration
- var t0
- var t1
- var slowModal = document.getElementById('slowModal')
+ let t0
+ let t1
+ const slowModal = document.getElementById('slowModal')
- slowModal.addEventListener('shown.bs.modal', function () {
+ slowModal.addEventListener('shown.bs.modal', () => {
t1 = performance.now()
- console.log('transition-duration took ' + (t1 - t0) + 'ms.')
+ console.log(`transition-duration took ${t1 - t0}ms.`)
})
- slowModal.addEventListener('show.bs.modal', function () {
+ slowModal.addEventListener('show.bs.modal', () => {
t0 = performance.now()
})
</script>
diff --git a/js/tests/visual/popover.html b/js/tests/visual/popover.html
index 3503271ae..73edf998d 100644
--- a/js/tests/visual/popover.html
+++ b/js/tests/visual/popover.html
@@ -31,19 +31,11 @@
</button>
</div>
- <script src="../../../node_modules/@popperjs/core/dist/umd/popper.min.js"></script>
- <script src="../../dist/dom/event-handler.js"></script>
- <script src="../../dist/dom/selector-engine.js"></script>
- <script src="../../dist/dom/manipulator.js"></script>
- <script src="../../dist/dom/data.js"></script>
- <script src="../../dist/base-component.js"></script>
- <script src="../../dist/tooltip.js"></script>
- <script src="../../dist/popover.js"></script>
+ <script src="../../../dist/js/bootstrap.bundle.js"></script>
<script>
- var popoverElements = document.querySelectorAll('[data-bs-toggle="popover"]')
- for (const popoverEl of popoverElements) {
- new Popover(popoverEl)
- }
+ /* global bootstrap: false */
+
+ document.querySelectorAll('[data-bs-toggle="popover"]').forEach(popoverEl => new bootstrap.Popover(popoverEl))
</script>
</body>
</html>
diff --git a/js/tests/visual/scrollspy.html b/js/tests/visual/scrollspy.html
index 1e3215b6f..541028478 100644
--- a/js/tests/visual/scrollspy.html
+++ b/js/tests/visual/scrollspy.html
@@ -24,11 +24,12 @@
<a class="nav-link" href="#mdo">@mdo</a>
</li>
<li class="nav-item dropdown">
- <a class="nav-link dropdown-toggle" href="#" id="dropdown" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</a>
- <ul class="dropdown-menu" aria-labelledby="dropdown">
+ <a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</a>
+ <ul class="dropdown-menu">
<li><a class="dropdown-item" href="#one">One</a></li>
<li><a class="dropdown-item" href="#two">Two</a></li>
<li><a class="dropdown-item" href="#three">Three</a></li>
+ <li><a class="dropdown-item" href="#présentation">Présentation</a></li>
</ul>
</li>
<li class="nav-item">
@@ -82,17 +83,18 @@
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
<p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
<hr>
+ <h2 id="présentation">Présentation</h2>
+ <p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
+ <p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
+ <p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
+ <p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
+ <p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
+ <p>Ad leggings keytar, brunch id art party dolor labore. Pitchfork yr enim lo-fi before they sold out qui. Tumblr farm-to-table bicycle rights whatever. Anim keffiyeh carles cardigan. Velit seitan mcsweeney's photo booth 3 wolf moon irure. Cosby sweater lomo jean shorts, williamsburg hoodie minim qui you probably haven't heard of them et cardigan trust fund culpa biodiesel wes anderson aesthetic. Nihil tattooed accusamus, cred irony biodiesel keffiyeh artisan ullamco consequat.</p>
+ <hr>
<h2 id="final">Final section</h2>
<p>Ad leggings keytar, brunch id art party dolor labore.</p>
</div>
- <script src="../../dist/dom/data.js"></script>
- <script src="../../dist/dom/event-handler.js"></script>
- <script src="../../dist/dom/selector-engine.js"></script>
- <script src="../../dist/dom/manipulator.js"></script>
- <script src="../../dist/base-component.js"></script>
- <script src="../../dist/scrollspy.js"></script>
- <script src="../../dist/dropdown.js"></script>
- <script src="../../dist/collapse.js"></script>
+ <script src="../../../dist/js/bootstrap.bundle.js"></script>
</body>
</html>
diff --git a/js/tests/visual/tab.html b/js/tests/visual/tab.html
index 0ce8be03c..a424175b0 100644
--- a/js/tests/visual/tab.html
+++ b/js/tests/visual/tab.html
@@ -161,12 +161,12 @@
<h4>Tabs with nav and using links (with fade)</h4>
<nav>
- <div class="nav nav-pills" id="nav-tab" role="tablist">
+ <div class="nav nav-pills" id="nav-tab" role="tablist">
<a class="nav-link nav-item active" role="tab" data-bs-toggle="tab" href="#home5">Home</a>
<a class="nav-link nav-item" role="tab" data-bs-toggle="tab" href="#profile5">Profile</a>
<a class="nav-link nav-item" role="tab" data-bs-toggle="tab" href="#fat5">@fat</a>
<a class="nav-link nav-item" role="tab" data-bs-toggle="tab" href="#mdo5">@mdo</a>
- <a class="nav-link nav-item disabled" role="tab" href="#">Disabled</a>
+ <a class="nav-link nav-item disabled" role="tab" href="#" aria-disabled="true">Disabled</a>
</div>
</nav>
@@ -218,11 +218,6 @@
</div>
</div>
- <script src="../../dist/dom/event-handler.js"></script>
- <script src="../../dist/dom/selector-engine.js"></script>
- <script src="../../dist/dom/data.js"></script>
- <script src="../../dist/dom/manipulator.js"></script>
- <script src="../../dist/base-component.js"></script>
- <script src="../../dist/tab.js"></script>
+ <script src="../../../dist/js/bootstrap.bundle.js"></script>
</body>
</html>
diff --git a/js/tests/visual/toast.html b/js/tests/visual/toast.html
index f9e1741cf..e64fd1d88 100644
--- a/js/tests/visual/toast.html
+++ b/js/tests/visual/toast.html
@@ -41,7 +41,7 @@
<div class="toast-header">
<span class="d-block bg-primary rounded me-2" style="width: 20px; height: 20px;"></span>
<strong class="me-auto">Bootstrap</strong>
- <small class="text-muted">2 seconds ago</small>
+ <small class="text-body-secondary">2 seconds ago</small>
<button type="button" class="ms-2 mb-1 btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
</div>
<div class="toast-body">
@@ -50,32 +50,19 @@
</div>
</div>
- <script src="../../dist/dom/event-handler.js"></script>
- <script src="../../dist/dom/manipulator.js"></script>
- <script src="../../dist/dom/data.js"></script>
- <script src="../../dist/base-component.js"></script>
- <script src="../../dist/toast.js"></script>
+ <script src="../../../dist/js/bootstrap.bundle.js"></script>
<script>
- window.addEventListener('load', function () {
- var toastElements = document.querySelectorAll('.toast')
- for (const toastEl of toastElements) {
- new Toast(toastEl)
- }
+ /* global bootstrap: false */
- document.getElementById('btnShowToast').addEventListener('click', function () {
- var toastElements = document.querySelectorAll('.toast')
- for (const toastEl of toastElements) {
- var toast = Toast.getInstance(toastEl)
- toast.show()
- }
+ window.addEventListener('load', () => {
+ document.querySelectorAll('.toast').forEach(toastEl => new bootstrap.Toast(toastEl))
+
+ document.getElementById('btnShowToast').addEventListener('click', () => {
+ document.querySelectorAll('.toast').forEach(toastEl => bootstrap.Toast.getInstance(toastEl).show())
})
- document.getElementById('btnHideToast').addEventListener('click', function () {
- var toastElements = document.querySelectorAll('.toast')
- for (const toastEl of toastElements) {
- var toast = Toast.getInstance(toastEl)
- toast.hide()
- }
+ document.getElementById('btnHideToast').addEventListener('click', () => {
+ document.querySelectorAll('.toast').forEach(toastEl => bootstrap.Toast.getInstance(toastEl).hide())
})
})
</script>
diff --git a/js/tests/visual/tooltip.html b/js/tests/visual/tooltip.html
index 448841e58..09144b572 100644
--- a/js/tests/visual/tooltip.html
+++ b/js/tests/visual/tooltip.html
@@ -20,7 +20,7 @@
<div class="container">
<h1>Tooltip <small>Bootstrap Visual Test</small></h1>
- <p class="text-muted">Tight pants next level keffiyeh <a href="#" data-bs-toggle="tooltip" title="Default tooltip">you probably</a> haven't heard of them. Photo booth beard raw denim letterpress vegan messenger bag stumptown. Farm-to-table seitan, mcsweeney's fixie sustainable quinoa 8-bit american apparel <a href="#" data-bs-toggle="tooltip" title="Another tooltip">have a</a> terry richardson vinyl chambray. Beard stumptown, cardigans banh mi lomo thundercats. Tofu biodiesel williamsburg marfa, four loko mcsweeney's cleanse vegan chambray. A really ironic artisan <a href="#" data-bs-toggle="tooltip" title="Another one here too">whatever keytar</a>, scenester farm-to-table banksy Austin <a href="#" data-bs-toggle="tooltip" title="The last tip!">twitter handle</a> freegan cred raw denim single-origin coffee viral.</p>
+ <p class="text-body-secondary">Tight pants next level keffiyeh <a href="#" data-bs-toggle="tooltip" title="Default tooltip">you probably</a> haven't heard of them. Photo booth beard raw denim letterpress vegan messenger bag stumptown. Farm-to-table seitan, mcsweeney's fixie sustainable quinoa 8-bit american apparel <a href="#" data-bs-toggle="tooltip" title="Another tooltip">have a</a> terry richardson vinyl chambray. Beard stumptown, cardigans banh mi lomo thundercats. Tofu biodiesel williamsburg marfa, four loko mcsweeney's cleanse vegan chambray. A really ironic artisan <a href="#" data-bs-toggle="tooltip" title="Another one here too">whatever keytar</a>, scenester farm-to-table banksy Austin <a href="#" data-bs-toggle="tooltip" title="The last tip!">twitter handle</a> freegan cred raw denim single-origin coffee viral.</p>
<hr>
@@ -66,48 +66,73 @@
<div id="shadow" class="pt-5"></div>
</div>
<div id="customContainer"></div>
+
+ <div class="row mt-4 border-top">
+ <hr>
+ <div class="h4">Test Selector triggered tooltips</div>
+ <div id="wrapperTriggeredBySelector">
+ <div class="py-2 selectorButtonsBlock">
+ <button type="button" class="btn btn-secondary bs-dynamic-tooltip" title="random title">Using title</button>
+ <button type="button" class="btn btn-secondary bs-dynamic-tooltip" data-bs-title="random title">Using bs-title</button>
+ </div>
+
+ </div>
+ <div class="mt-3">
+ <button type="button" class="btn btn-primary" onclick="duplicateButtons()">Duplicate above two buttons</button>
+ </div>
+ </div>
</div>
- <script src="../../../node_modules/@popperjs/core/dist/umd/popper.min.js"></script>
- <script src="../../dist/dom/selector-engine.js"></script>
- <script src="../../dist/dom/event-handler.js"></script>
- <script src="../../dist/dom/manipulator.js"></script>
- <script src="../../dist/dom/data.js"></script>
- <script src="../../dist/base-component.js"></script>
- <script src="../../dist/tooltip.js"></script>
+ <script src="../../../dist/js/bootstrap.bundle.js"></script>
<script>
+ /* global bootstrap: false */
+
if (typeof document.body.attachShadow === 'function') {
- var shadowRoot = document.getElementById('shadow').attachShadow({ mode: 'open' })
+ const shadowRoot = document.getElementById('shadow').attachShadow({ mode: 'open' })
shadowRoot.innerHTML =
- '<button type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-placement="top" title="Tooltip on top in a shadow dom">' +
+ '<button id="firstShadowTooltip" type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-placement="top" title="Tooltip on top in a shadow dom">' +
' Tooltip on top in a shadow dom' +
'</button>' +
- '<button id="secondTooltip" type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-placement="top" title="Tooltip on top in a shadow dom with container option">' +
+ '<button id="secondShadowTooltip" type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-placement="top" title="Tooltip on top in a shadow dom with container option">' +
' Tooltip on top in a shadow dom' +
'</button>'
- var firstChildTooltip = new Tooltip(shadowRoot.firstChild)
- var secondChildTooltip = new Tooltip(shadowRoot.getElementById('secondTooltip'), {
+ new bootstrap.Tooltip(shadowRoot.firstChild)
+ new bootstrap.Tooltip(shadowRoot.getElementById('secondShadowTooltip'), {
container: shadowRoot
})
}
- var tooltipElements = document.querySelectorAll('[data-bs-toggle="tooltip"]')
- for (const tooltipEl of tooltipElements) {
- new Tooltip(tooltipEl)
- }
-
- var tooltipElement = document.getElementById('tooltipElement')
- var tooltipElementInstance = new Tooltip(tooltipElement, {
- container: document.getElementById('customContainer')
+ new bootstrap.Tooltip('#tooltipElement', {
+ container: '#customContainer'
})
- var target = document.getElementById('target')
- var targetTooltip = new Tooltip(target, {
- placement : 'top',
- trigger : 'manual'
+ const targetTooltip = new bootstrap.Tooltip('#target', {
+ placement: 'top',
+ trigger: 'manual'
})
targetTooltip.show()
+
+ document.querySelectorAll('[data-bs-toggle="tooltip"]').forEach(tooltipEl => new bootstrap.Tooltip(tooltipEl))
</script>
+
+ <script>
+ /* global bootstrap: false */
+
+ new bootstrap.Tooltip('#wrapperTriggeredBySelector', {
+ animation: false,
+ selector: '.bs-dynamic-tooltip'
+ })
+
+ /* eslint-disable no-unused-vars */
+ function duplicateButtons() {
+ const buttonsBlock = document.querySelector('.selectorButtonsBlock')// get first
+ const buttonsBlockClone = buttonsBlock.cloneNode(true)
+ buttonsBlockClone.innerHTML += new Date().toLocaleString()
+ document.querySelector('#wrapperTriggeredBySelector').append(buttonsBlockClone)
+ }
+ /* eslint-enable no-unused-vars */
+ </script>
+
</body>
</html>