diff options
Diffstat (limited to 'vendor/twbs/bootstrap/js/src')
-rw-r--r-- | vendor/twbs/bootstrap/js/src/alert.js | 101 | ||||
-rw-r--r-- | vendor/twbs/bootstrap/js/src/button.js | 158 | ||||
-rw-r--r-- | vendor/twbs/bootstrap/js/src/carousel.js | 424 | ||||
-rw-r--r-- | vendor/twbs/bootstrap/js/src/collapse.js | 302 | ||||
-rw-r--r-- | vendor/twbs/bootstrap/js/src/dropdown.js | 531 | ||||
-rw-r--r-- | vendor/twbs/bootstrap/js/src/modal.js | 505 | ||||
-rw-r--r-- | vendor/twbs/bootstrap/js/src/popover.js | 89 | ||||
-rw-r--r-- | vendor/twbs/bootstrap/js/src/scrollspy.js | 204 | ||||
-rw-r--r-- | vendor/twbs/bootstrap/js/src/tab.js | 168 | ||||
-rw-r--r-- | vendor/twbs/bootstrap/js/src/toast.js | 158 | ||||
-rw-r--r-- | vendor/twbs/bootstrap/js/src/tools/sanitizer.js | 127 | ||||
-rw-r--r-- | vendor/twbs/bootstrap/js/src/tooltip.js | 594 | ||||
-rw-r--r-- | vendor/twbs/bootstrap/js/src/util.js | 198 |
13 files changed, 1403 insertions, 2156 deletions
diff --git a/vendor/twbs/bootstrap/js/src/alert.js b/vendor/twbs/bootstrap/js/src/alert.js index afd7736c7..87cc7e731 100644 --- a/vendor/twbs/bootstrap/js/src/alert.js +++ b/vendor/twbs/bootstrap/js/src/alert.js @@ -1,12 +1,17 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): alert.js + * Bootstrap (v5.0.1): alert.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -import $ from 'jquery' -import Util from './util' +import { + defineJQueryPlugin, + getElementFromSelector +} from './util/index' +import Data from './dom/data' +import EventHandler from './dom/event-handler' +import BaseComponent from './base-component' /** * ------------------------------------------------------------------------ @@ -15,13 +20,11 @@ import Util from './util' */ const NAME = 'alert' -const VERSION = '4.6.0' const DATA_KEY = 'bs.alert' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const JQUERY_NO_CONFLICT = $.fn[NAME] -const SELECTOR_DISMISS = '[data-dismiss="alert"]' +const SELECTOR_DISMISS = '[data-bs-dismiss="alert"]' const EVENT_CLOSE = `close${EVENT_KEY}` const EVENT_CLOSED = `closed${EVENT_KEY}` @@ -37,95 +40,59 @@ const CLASS_NAME_SHOW = 'show' * ------------------------------------------------------------------------ */ -class Alert { - constructor(element) { - this._element = element - } - +class Alert extends BaseComponent { // Getters - static get VERSION() { - return VERSION + static get NAME() { + return NAME } // Public close(element) { - let rootElement = this._element - if (element) { - rootElement = this._getRootElement(element) - } - + const rootElement = element ? this._getRootElement(element) : this._element const customEvent = this._triggerCloseEvent(rootElement) - if (customEvent.isDefaultPrevented()) { + if (customEvent === null || customEvent.defaultPrevented) { return } this._removeElement(rootElement) } - dispose() { - $.removeData(this._element, DATA_KEY) - this._element = null - } - // Private _getRootElement(element) { - const selector = Util.getSelectorFromElement(element) - let parent = false - - if (selector) { - parent = document.querySelector(selector) - } - - if (!parent) { - parent = $(element).closest(`.${CLASS_NAME_ALERT}`)[0] - } - - return parent + return getElementFromSelector(element) || element.closest(`.${CLASS_NAME_ALERT}`) } _triggerCloseEvent(element) { - const closeEvent = $.Event(EVENT_CLOSE) - - $(element).trigger(closeEvent) - return closeEvent + return EventHandler.trigger(element, EVENT_CLOSE) } _removeElement(element) { - $(element).removeClass(CLASS_NAME_SHOW) - - if (!$(element).hasClass(CLASS_NAME_FADE)) { - this._destroyElement(element) - return - } + element.classList.remove(CLASS_NAME_SHOW) - const transitionDuration = Util.getTransitionDurationFromElement(element) - - $(element) - .one(Util.TRANSITION_END, event => this._destroyElement(element, event)) - .emulateTransitionEnd(transitionDuration) + const isAnimated = element.classList.contains(CLASS_NAME_FADE) + this._queueCallback(() => this._destroyElement(element), element, isAnimated) } _destroyElement(element) { - $(element) - .detach() - .trigger(EVENT_CLOSED) - .remove() + if (element.parentNode) { + element.parentNode.removeChild(element) + } + + EventHandler.trigger(element, EVENT_CLOSED) } // Static - static _jQueryInterface(config) { + static jQueryInterface(config) { return this.each(function () { - const $element = $(this) - let data = $element.data(DATA_KEY) + let data = Data.get(this, DATA_KEY) if (!data) { data = new Alert(this) - $element.data(DATA_KEY, data) } if (config === 'close') { @@ -134,7 +101,7 @@ class Alert { }) } - static _handleDismiss(alertInstance) { + static handleDismiss(alertInstance) { return function (event) { if (event) { event.preventDefault() @@ -151,23 +118,15 @@ class Alert { * ------------------------------------------------------------------------ */ -$(document).on( - EVENT_CLICK_DATA_API, - SELECTOR_DISMISS, - Alert._handleDismiss(new Alert()) -) +EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DISMISS, Alert.handleDismiss(new Alert())) /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ + * add .Alert to jQuery only if jQuery is present */ -$.fn[NAME] = Alert._jQueryInterface -$.fn[NAME].Constructor = Alert -$.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Alert._jQueryInterface -} +defineJQueryPlugin(Alert) export default Alert diff --git a/vendor/twbs/bootstrap/js/src/button.js b/vendor/twbs/bootstrap/js/src/button.js index 316387e8e..6ef753136 100644 --- a/vendor/twbs/bootstrap/js/src/button.js +++ b/vendor/twbs/bootstrap/js/src/button.js @@ -1,11 +1,14 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): button.js + * Bootstrap (v5.0.1): button.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -import $ from 'jquery' +import { defineJQueryPlugin } from './util/index' +import Data from './dom/data' +import EventHandler from './dom/event-handler' +import BaseComponent from './base-component' /** * ------------------------------------------------------------------------ @@ -14,28 +17,15 @@ import $ from 'jquery' */ const NAME = 'button' -const VERSION = '4.6.0' const DATA_KEY = 'bs.button' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const JQUERY_NO_CONFLICT = $.fn[NAME] const CLASS_NAME_ACTIVE = 'active' -const CLASS_NAME_BUTTON = 'btn' -const CLASS_NAME_FOCUS = 'focus' -const SELECTOR_DATA_TOGGLE_CARROT = '[data-toggle^="button"]' -const SELECTOR_DATA_TOGGLES = '[data-toggle="buttons"]' -const SELECTOR_DATA_TOGGLE = '[data-toggle="button"]' -const SELECTOR_DATA_TOGGLES_BUTTONS = '[data-toggle="buttons"] .btn' -const SELECTOR_INPUT = 'input:not([type="hidden"])' -const SELECTOR_ACTIVE = '.active' -const SELECTOR_BUTTON = '.btn' +const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="button"]' const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}` -const EVENT_FOCUS_BLUR_DATA_API = `focus${EVENT_KEY}${DATA_API_KEY} ` + - `blur${EVENT_KEY}${DATA_API_KEY}` -const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}` /** * ------------------------------------------------------------------------ @@ -43,87 +33,30 @@ const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}` * ------------------------------------------------------------------------ */ -class Button { - constructor(element) { - this._element = element - this.shouldAvoidTriggerChange = false - } - +class Button extends BaseComponent { // Getters - static get VERSION() { - return VERSION + static get NAME() { + return NAME } // Public toggle() { - let triggerChangeEvent = true - let addAriaPressed = true - const rootElement = $(this._element).closest(SELECTOR_DATA_TOGGLES)[0] - - if (rootElement) { - const input = this._element.querySelector(SELECTOR_INPUT) - - if (input) { - if (input.type === 'radio') { - if (input.checked && this._element.classList.contains(CLASS_NAME_ACTIVE)) { - triggerChangeEvent = false - } else { - const activeElement = rootElement.querySelector(SELECTOR_ACTIVE) - - if (activeElement) { - $(activeElement).removeClass(CLASS_NAME_ACTIVE) - } - } - } - - if (triggerChangeEvent) { - // if it's not a radio button or checkbox don't add a pointless/invalid checked property to the input - if (input.type === 'checkbox' || input.type === 'radio') { - input.checked = !this._element.classList.contains(CLASS_NAME_ACTIVE) - } - - if (!this.shouldAvoidTriggerChange) { - $(input).trigger('change') - } - } - - input.focus() - addAriaPressed = false - } - } - - if (!(this._element.hasAttribute('disabled') || this._element.classList.contains('disabled'))) { - if (addAriaPressed) { - this._element.setAttribute('aria-pressed', !this._element.classList.contains(CLASS_NAME_ACTIVE)) - } - - if (triggerChangeEvent) { - $(this._element).toggleClass(CLASS_NAME_ACTIVE) - } - } - } - - dispose() { - $.removeData(this._element, DATA_KEY) - this._element = null + // 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 _jQueryInterface(config, avoidTriggerChange) { + static jQueryInterface(config) { return this.each(function () { - const $element = $(this) - let data = $element.data(DATA_KEY) + let data = Data.get(this, DATA_KEY) if (!data) { data = new Button(this) - $element.data(DATA_KEY, data) } - data.shouldAvoidTriggerChange = avoidTriggerChange - if (config === 'toggle') { data[config]() } @@ -137,73 +70,26 @@ class Button { * ------------------------------------------------------------------------ */ -$(document) - .on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE_CARROT, event => { - let button = event.target - const initialButton = button - - if (!$(button).hasClass(CLASS_NAME_BUTTON)) { - button = $(button).closest(SELECTOR_BUTTON)[0] - } - - if (!button || button.hasAttribute('disabled') || button.classList.contains('disabled')) { - event.preventDefault() // work around Firefox bug #1540995 - } else { - const inputBtn = button.querySelector(SELECTOR_INPUT) +EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => { + event.preventDefault() - if (inputBtn && (inputBtn.hasAttribute('disabled') || inputBtn.classList.contains('disabled'))) { - event.preventDefault() // work around Firefox bug #1540995 - return - } + const button = event.target.closest(SELECTOR_DATA_TOGGLE) - if (initialButton.tagName === 'INPUT' || button.tagName !== 'LABEL') { - Button._jQueryInterface.call($(button), 'toggle', initialButton.tagName === 'INPUT') - } - } - }) - .on(EVENT_FOCUS_BLUR_DATA_API, SELECTOR_DATA_TOGGLE_CARROT, event => { - const button = $(event.target).closest(SELECTOR_BUTTON)[0] - $(button).toggleClass(CLASS_NAME_FOCUS, /^focus(in)?$/.test(event.type)) - }) - -$(window).on(EVENT_LOAD_DATA_API, () => { - // ensure correct active class is set to match the controls' actual values/states - - // find all checkboxes/readio buttons inside data-toggle groups - let buttons = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLES_BUTTONS)) - for (let i = 0, len = buttons.length; i < len; i++) { - const button = buttons[i] - const input = button.querySelector(SELECTOR_INPUT) - if (input.checked || input.hasAttribute('checked')) { - button.classList.add(CLASS_NAME_ACTIVE) - } else { - button.classList.remove(CLASS_NAME_ACTIVE) - } + let data = Data.get(button, DATA_KEY) + if (!data) { + data = new Button(button) } - // find all button toggles - buttons = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE)) - for (let i = 0, len = buttons.length; i < len; i++) { - const button = buttons[i] - if (button.getAttribute('aria-pressed') === 'true') { - button.classList.add(CLASS_NAME_ACTIVE) - } else { - button.classList.remove(CLASS_NAME_ACTIVE) - } - } + data.toggle() }) /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ + * add .Button to jQuery only if jQuery is present */ -$.fn[NAME] = Button._jQueryInterface -$.fn[NAME].Constructor = Button -$.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Button._jQueryInterface -} +defineJQueryPlugin(Button) export default Button diff --git a/vendor/twbs/bootstrap/js/src/carousel.js b/vendor/twbs/bootstrap/js/src/carousel.js index b63d406bd..bb894e9c3 100644 --- a/vendor/twbs/bootstrap/js/src/carousel.js +++ b/vendor/twbs/bootstrap/js/src/carousel.js @@ -1,12 +1,24 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): carousel.js + * Bootstrap (v5.0.1): carousel.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -import $ from 'jquery' -import Util from './util' +import { + defineJQueryPlugin, + getElementFromSelector, + isRTL, + isVisible, + reflow, + triggerTransitionEnd, + typeCheckConfig +} from './util/index' +import Data from './dom/data' +import EventHandler from './dom/event-handler' +import Manipulator from './dom/manipulator' +import SelectorEngine from './dom/selector-engine' +import BaseComponent from './base-component' /** * ------------------------------------------------------------------------ @@ -15,13 +27,12 @@ import Util from './util' */ const NAME = 'carousel' -const VERSION = '4.6.0' const DATA_KEY = 'bs.carousel' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const JQUERY_NO_CONFLICT = $.fn[NAME] -const ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key -const ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key + +const ARROW_LEFT_KEY = 'ArrowLeft' +const ARROW_RIGHT_KEY = 'ArrowRight' const TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch const SWIPE_THRESHOLD = 40 @@ -43,8 +54,8 @@ const DefaultType = { touch: 'boolean' } -const DIRECTION_NEXT = 'next' -const DIRECTION_PREV = 'prev' +const ORDER_NEXT = 'next' +const ORDER_PREV = 'prev' const DIRECTION_LEFT = 'left' const DIRECTION_RIGHT = 'right' @@ -65,8 +76,8 @@ const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}` const CLASS_NAME_CAROUSEL = 'carousel' const CLASS_NAME_ACTIVE = 'active' const CLASS_NAME_SLIDE = 'slide' -const CLASS_NAME_RIGHT = 'carousel-item-right' -const CLASS_NAME_LEFT = 'carousel-item-left' +const CLASS_NAME_END = 'carousel-item-end' +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' @@ -77,21 +88,22 @@ const SELECTOR_ITEM = '.carousel-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_DATA_SLIDE = '[data-slide], [data-slide-to]' -const SELECTOR_DATA_RIDE = '[data-ride="carousel"]' +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 PointerType = { - TOUCH: 'touch', - PEN: 'pen' -} +const POINTER_TYPE_TOUCH = 'touch' +const POINTER_TYPE_PEN = 'pen' /** * ------------------------------------------------------------------------ * Class Definition * ------------------------------------------------------------------------ */ -class Carousel { +class Carousel extends BaseComponent { constructor(element, config) { + super(element) + this._items = null this._interval = null this._activeElement = null @@ -102,45 +114,42 @@ class Carousel { this.touchDeltaX = 0 this._config = this._getConfig(config) - this._element = element - this._indicatorsElement = this._element.querySelector(SELECTOR_INDICATORS) + this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element) this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0 - this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent) + this._pointerEvent = Boolean(window.PointerEvent) this._addEventListeners() } // Getters - static get VERSION() { - return VERSION - } - static get Default() { return Default } + static get NAME() { + return NAME + } + // Public next() { if (!this._isSliding) { - this._slide(DIRECTION_NEXT) + this._slide(ORDER_NEXT) } } nextWhenVisible() { - const $element = $(this._element) // Don't call next when the page isn't visible // or the carousel or its parent isn't visible - if (!document.hidden && - ($element.is(':visible') && $element.css('visibility') !== 'hidden')) { + if (!document.hidden && isVisible(this._element)) { this.next() } } prev() { if (!this._isSliding) { - this._slide(DIRECTION_PREV) + this._slide(ORDER_PREV) } } @@ -149,8 +158,8 @@ class Carousel { this._isPaused = true } - if (this._element.querySelector(SELECTOR_NEXT_PREV)) { - Util.triggerTransitionEnd(this._element) + if (SelectorEngine.findOne(SELECTOR_NEXT_PREV, this._element)) { + triggerTransitionEnd(this._element) this.cycle(true) } @@ -168,7 +177,7 @@ class Carousel { this._interval = null } - if (this._config.interval && !this._isPaused) { + if (this._config && this._config.interval && !this._isPaused) { this._updateInterval() this._interval = setInterval( @@ -179,8 +188,7 @@ class Carousel { } to(index) { - this._activeElement = this._element.querySelector(SELECTOR_ACTIVE_ITEM) - + this._activeElement = SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element) const activeIndex = this._getItemIndex(this._activeElement) if (index > this._items.length - 1 || index < 0) { @@ -188,7 +196,7 @@ class Carousel { } if (this._isSliding) { - $(this._element).one(EVENT_SLID, () => this.to(index)) + EventHandler.one(this._element, EVENT_SLID, () => this.to(index)) return } @@ -198,25 +206,11 @@ class Carousel { return } - const direction = index > activeIndex ? - DIRECTION_NEXT : - DIRECTION_PREV + const order = index > activeIndex ? + ORDER_NEXT : + ORDER_PREV - this._slide(direction, this._items[index]) - } - - dispose() { - $(this._element).off(EVENT_KEY) - $.removeData(this._element, DATA_KEY) - - this._items = null - this._config = null - this._element = null - this._interval = null - this._isPaused = null - this._isSliding = null - this._activeElement = null - this._indicatorsElement = null + this._slide(order, this._items[index]) } // Private @@ -226,7 +220,7 @@ class Carousel { ...Default, ...config } - Util.typeCheckConfig(NAME, config, DefaultType) + typeCheckConfig(NAME, config, DefaultType) return config } @@ -241,58 +235,47 @@ class Carousel { this.touchDeltaX = 0 - // swipe left - if (direction > 0) { - this.prev() + if (!direction) { + return } - // swipe right - if (direction < 0) { - this.next() - } + this._slide(direction > 0 ? DIRECTION_RIGHT : DIRECTION_LEFT) } _addEventListeners() { if (this._config.keyboard) { - $(this._element).on(EVENT_KEYDOWN, event => this._keydown(event)) + EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event)) } if (this._config.pause === 'hover') { - $(this._element) - .on(EVENT_MOUSEENTER, event => this.pause(event)) - .on(EVENT_MOUSELEAVE, event => this.cycle(event)) + EventHandler.on(this._element, EVENT_MOUSEENTER, event => this.pause(event)) + EventHandler.on(this._element, EVENT_MOUSELEAVE, event => this.cycle(event)) } - if (this._config.touch) { + if (this._config.touch && this._touchSupported) { this._addTouchEventListeners() } } _addTouchEventListeners() { - if (!this._touchSupported) { - return - } - const start = event => { - if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) { - this.touchStartX = event.originalEvent.clientX + if (this._pointerEvent && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH)) { + this.touchStartX = event.clientX } else if (!this._pointerEvent) { - this.touchStartX = event.originalEvent.touches[0].clientX + this.touchStartX = event.touches[0].clientX } } const move = event => { // ensure swiping with one touch and not pinching - if (event.originalEvent.touches && event.originalEvent.touches.length > 1) { - this.touchDeltaX = 0 - } else { - this.touchDeltaX = event.originalEvent.touches[0].clientX - this.touchStartX - } + this.touchDeltaX = event.touches && event.touches.length > 1 ? + 0 : + event.touches[0].clientX - this.touchStartX } const end = event => { - if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) { - this.touchDeltaX = event.originalEvent.clientX - this.touchStartX + if (this._pointerEvent && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH)) { + this.touchDeltaX = event.clientX - this.touchStartX } this._handleSwipe() @@ -314,18 +297,19 @@ class Carousel { } } - $(this._element.querySelectorAll(SELECTOR_ITEM_IMG)) - .on(EVENT_DRAG_START, e => e.preventDefault()) + SelectorEngine.find(SELECTOR_ITEM_IMG, this._element).forEach(itemImg => { + EventHandler.on(itemImg, EVENT_DRAG_START, e => e.preventDefault()) + }) if (this._pointerEvent) { - $(this._element).on(EVENT_POINTERDOWN, event => start(event)) - $(this._element).on(EVENT_POINTERUP, event => end(event)) + EventHandler.on(this._element, EVENT_POINTERDOWN, event => start(event)) + EventHandler.on(this._element, EVENT_POINTERUP, event => end(event)) this._element.classList.add(CLASS_NAME_POINTER_EVENT) } else { - $(this._element).on(EVENT_TOUCHSTART, event => start(event)) - $(this._element).on(EVENT_TOUCHMOVE, event => move(event)) - $(this._element).on(EVENT_TOUCHEND, event => end(event)) + EventHandler.on(this._element, EVENT_TOUCHSTART, event => start(event)) + EventHandler.on(this._element, EVENT_TOUCHMOVE, event => move(event)) + EventHandler.on(this._element, EVENT_TOUCHEND, event => end(event)) } } @@ -334,83 +318,81 @@ class Carousel { return } - switch (event.which) { - case ARROW_LEFT_KEYCODE: - event.preventDefault() - this.prev() - break - case ARROW_RIGHT_KEYCODE: - event.preventDefault() - this.next() - break - default: + if (event.key === ARROW_LEFT_KEY) { + event.preventDefault() + this._slide(DIRECTION_RIGHT) + } else if (event.key === ARROW_RIGHT_KEY) { + event.preventDefault() + this._slide(DIRECTION_LEFT) } } _getItemIndex(element) { this._items = element && element.parentNode ? - [].slice.call(element.parentNode.querySelectorAll(SELECTOR_ITEM)) : + SelectorEngine.find(SELECTOR_ITEM, element.parentNode) : [] + return this._items.indexOf(element) } - _getItemByDirection(direction, activeElement) { - const isNextDirection = direction === DIRECTION_NEXT - const isPrevDirection = direction === DIRECTION_PREV + _getItemByOrder(order, activeElement) { + const isNext = order === ORDER_NEXT + const isPrev = order === ORDER_PREV const activeIndex = this._getItemIndex(activeElement) const lastItemIndex = this._items.length - 1 - const isGoingToWrap = isPrevDirection && activeIndex === 0 || - isNextDirection && activeIndex === lastItemIndex + const isGoingToWrap = (isPrev && activeIndex === 0) || (isNext && activeIndex === lastItemIndex) if (isGoingToWrap && !this._config.wrap) { return activeElement } - const delta = direction === DIRECTION_PREV ? -1 : 1 + const delta = isPrev ? -1 : 1 const itemIndex = (activeIndex + delta) % this._items.length return itemIndex === -1 ? - this._items[this._items.length - 1] : this._items[itemIndex] + this._items[this._items.length - 1] : + this._items[itemIndex] } _triggerSlideEvent(relatedTarget, eventDirectionName) { const targetIndex = this._getItemIndex(relatedTarget) - const fromIndex = this._getItemIndex(this._element.querySelector(SELECTOR_ACTIVE_ITEM)) - const slideEvent = $.Event(EVENT_SLIDE, { + 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 }) - - $(this._element).trigger(slideEvent) - - return slideEvent } _setActiveIndicatorElement(element) { if (this._indicatorsElement) { - const indicators = [].slice.call(this._indicatorsElement.querySelectorAll(SELECTOR_ACTIVE)) - $(indicators).removeClass(CLASS_NAME_ACTIVE) + const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement) - const nextIndicator = this._indicatorsElement.children[ - this._getItemIndex(element) - ] + activeIndicator.classList.remove(CLASS_NAME_ACTIVE) + activeIndicator.removeAttribute('aria-current') - if (nextIndicator) { - $(nextIndicator).addClass(CLASS_NAME_ACTIVE) + const indicators = SelectorEngine.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 + } } } } _updateInterval() { - const element = this._activeElement || this._element.querySelector(SELECTOR_ACTIVE_ITEM) + const element = this._activeElement || SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element) if (!element) { return } - const elementInterval = parseInt(element.getAttribute('data-interval'), 10) + const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10) if (elementInterval) { this._config.defaultInterval = this._config.defaultInterval || this._config.interval @@ -420,35 +402,27 @@ class Carousel { } } - _slide(direction, element) { - const activeElement = this._element.querySelector(SELECTOR_ACTIVE_ITEM) + _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 || activeElement && - this._getItemByDirection(direction, activeElement) + const nextElement = element || this._getItemByOrder(order, activeElement) + const nextElementIndex = this._getItemIndex(nextElement) const isCycling = Boolean(this._interval) - let directionalClassName - let orderClassName - let eventDirectionName - - if (direction === DIRECTION_NEXT) { - directionalClassName = CLASS_NAME_LEFT - orderClassName = CLASS_NAME_NEXT - eventDirectionName = DIRECTION_LEFT - } else { - directionalClassName = CLASS_NAME_RIGHT - orderClassName = CLASS_NAME_PREV - eventDirectionName = DIRECTION_RIGHT - } + 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).hasClass(CLASS_NAME_ACTIVE)) { + if (nextElement && nextElement.classList.contains(CLASS_NAME_ACTIVE)) { this._isSliding = false return } const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName) - if (slideEvent.isDefaultPrevented()) { + if (slideEvent.defaultPrevented) { return } @@ -466,42 +440,41 @@ class Carousel { this._setActiveIndicatorElement(nextElement) this._activeElement = nextElement - const slidEvent = $.Event(EVENT_SLID, { - relatedTarget: nextElement, - direction: eventDirectionName, - from: activeElementIndex, - to: nextElementIndex - }) + const triggerSlidEvent = () => { + EventHandler.trigger(this._element, EVENT_SLID, { + relatedTarget: nextElement, + direction: eventDirectionName, + from: activeElementIndex, + to: nextElementIndex + }) + } - if ($(this._element).hasClass(CLASS_NAME_SLIDE)) { - $(nextElement).addClass(orderClassName) + if (this._element.classList.contains(CLASS_NAME_SLIDE)) { + nextElement.classList.add(orderClassName) - Util.reflow(nextElement) + reflow(nextElement) - $(activeElement).addClass(directionalClassName) - $(nextElement).addClass(directionalClassName) + activeElement.classList.add(directionalClassName) + nextElement.classList.add(directionalClassName) - const transitionDuration = Util.getTransitionDurationFromElement(activeElement) + const completeCallBack = () => { + nextElement.classList.remove(directionalClassName, orderClassName) + nextElement.classList.add(CLASS_NAME_ACTIVE) - $(activeElement) - .one(Util.TRANSITION_END, () => { - $(nextElement) - .removeClass(`${directionalClassName} ${orderClassName}`) - .addClass(CLASS_NAME_ACTIVE) + activeElement.classList.remove(CLASS_NAME_ACTIVE, orderClassName, directionalClassName) - $(activeElement).removeClass(`${CLASS_NAME_ACTIVE} ${orderClassName} ${directionalClassName}`) + this._isSliding = false - this._isSliding = false + setTimeout(triggerSlidEvent, 0) + } - setTimeout(() => $(this._element).trigger(slidEvent), 0) - }) - .emulateTransitionEnd(transitionDuration) + this._queueCallback(completeCallBack, activeElement, true) } else { - $(activeElement).removeClass(CLASS_NAME_ACTIVE) - $(nextElement).addClass(CLASS_NAME_ACTIVE) + activeElement.classList.remove(CLASS_NAME_ACTIVE) + nextElement.classList.add(CLASS_NAME_ACTIVE) this._isSliding = false - $(this._element).trigger(slidEvent) + triggerSlidEvent() } if (isCycling) { @@ -509,72 +482,93 @@ class Carousel { } } + _directionToOrder(direction) { + if (![DIRECTION_RIGHT, DIRECTION_LEFT].includes(direction)) { + return direction + } + + if (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()) { + return order === ORDER_PREV ? DIRECTION_LEFT : DIRECTION_RIGHT + } + + return order === ORDER_PREV ? DIRECTION_RIGHT : DIRECTION_LEFT + } + // Static - static _jQueryInterface(config) { - return this.each(function () { - let data = $(this).data(DATA_KEY) - let _config = { - ...Default, - ...$(this).data() - } + static carouselInterface(element, config) { + let data = Data.get(element, DATA_KEY) + let _config = { + ...Default, + ...Manipulator.getDataAttributes(element) + } - if (typeof config === 'object') { - _config = { - ..._config, - ...config - } + if (typeof config === 'object') { + _config = { + ..._config, + ...config } + } + + const action = typeof config === 'string' ? config : _config.slide - const action = typeof config === 'string' ? config : _config.slide + if (!data) { + data = new Carousel(element, _config) + } - if (!data) { - data = new Carousel(this, _config) - $(this).data(DATA_KEY, data) + 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) - } 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() + } + } - data[action]() - } else if (_config.interval && _config.ride) { - data.pause() - data.cycle() - } + static jQueryInterface(config) { + return this.each(function () { + Carousel.carouselInterface(this, config) }) } - static _dataApiClickHandler(event) { - const selector = Util.getSelectorFromElement(this) - - if (!selector) { - return - } - - const target = $(selector)[0] + static dataApiClickHandler(event) { + const target = getElementFromSelector(this) - if (!target || !$(target).hasClass(CLASS_NAME_CAROUSEL)) { + if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) { return } const config = { - ...$(target).data(), - ...$(this).data() + ...Manipulator.getDataAttributes(target), + ...Manipulator.getDataAttributes(this) } - const slideIndex = this.getAttribute('data-slide-to') + const slideIndex = this.getAttribute('data-bs-slide-to') if (slideIndex) { config.interval = false } - Carousel._jQueryInterface.call($(target), config) + Carousel.carouselInterface(target, config) if (slideIndex) { - $(target).data(DATA_KEY).to(slideIndex) + Data.get(target, DATA_KEY).to(slideIndex) } event.preventDefault() @@ -587,13 +581,13 @@ class Carousel { * ------------------------------------------------------------------------ */ -$(document).on(EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, Carousel._dataApiClickHandler) +EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, Carousel.dataApiClickHandler) + +EventHandler.on(window, EVENT_LOAD_DATA_API, () => { + const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE) -$(window).on(EVENT_LOAD_DATA_API, () => { - const carousels = [].slice.call(document.querySelectorAll(SELECTOR_DATA_RIDE)) for (let i = 0, len = carousels.length; i < len; i++) { - const $carousel = $(carousels[i]) - Carousel._jQueryInterface.call($carousel, $carousel.data()) + Carousel.carouselInterface(carousels[i], Data.get(carousels[i], DATA_KEY)) } }) @@ -601,13 +595,9 @@ $(window).on(EVENT_LOAD_DATA_API, () => { * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ + * add .Carousel to jQuery only if jQuery is present */ -$.fn[NAME] = Carousel._jQueryInterface -$.fn[NAME].Constructor = Carousel -$.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Carousel._jQueryInterface -} +defineJQueryPlugin(Carousel) export default Carousel diff --git a/vendor/twbs/bootstrap/js/src/collapse.js b/vendor/twbs/bootstrap/js/src/collapse.js index af3163be9..fd85fbde2 100644 --- a/vendor/twbs/bootstrap/js/src/collapse.js +++ b/vendor/twbs/bootstrap/js/src/collapse.js @@ -1,12 +1,23 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): collapse.js + * Bootstrap (v5.0.1): collapse.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -import $ from 'jquery' -import Util from './util' +import { + defineJQueryPlugin, + getElement, + getSelectorFromElement, + getElementFromSelector, + reflow, + typeCheckConfig +} from './util/index' +import Data from './dom/data' +import EventHandler from './dom/event-handler' +import Manipulator from './dom/manipulator' +import SelectorEngine from './dom/selector-engine' +import BaseComponent from './base-component' /** * ------------------------------------------------------------------------ @@ -15,11 +26,9 @@ import Util from './util' */ const NAME = 'collapse' -const VERSION = '4.6.0' const DATA_KEY = 'bs.collapse' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const JQUERY_NO_CONFLICT = $.fn[NAME] const Default = { toggle: true, @@ -42,11 +51,11 @@ const CLASS_NAME_COLLAPSE = 'collapse' const CLASS_NAME_COLLAPSING = 'collapsing' const CLASS_NAME_COLLAPSED = 'collapsed' -const DIMENSION_WIDTH = 'width' -const DIMENSION_HEIGHT = 'height' +const WIDTH = 'width' +const HEIGHT = 'height' const SELECTOR_ACTIVES = '.show, .collapsing' -const SELECTOR_DATA_TOGGLE = '[data-toggle="collapse"]' +const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="collapse"]' /** * ------------------------------------------------------------------------ @@ -54,24 +63,26 @@ const SELECTOR_DATA_TOGGLE = '[data-toggle="collapse"]' * ------------------------------------------------------------------------ */ -class Collapse { +class Collapse extends BaseComponent { constructor(element, config) { + super(element) + this._isTransitioning = false - this._element = element this._config = this._getConfig(config) - this._triggerArray = [].slice.call(document.querySelectorAll( - `[data-toggle="collapse"][href="#${element.id}"],` + - `[data-toggle="collapse"][data-target="#${element.id}"]` - )) + this._triggerArray = SelectorEngine.find( + `${SELECTOR_DATA_TOGGLE}[href="#${this._element.id}"],` + + `${SELECTOR_DATA_TOGGLE}[data-bs-target="#${this._element.id}"]` + ) + + const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE) - const toggleList = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE)) for (let i = 0, len = toggleList.length; i < len; i++) { const elem = toggleList[i] - const selector = Util.getSelectorFromElement(elem) - const filterElement = [].slice.call(document.querySelectorAll(selector)) - .filter(foundElem => foundElem === element) + const selector = getSelectorFromElement(elem) + const filterElement = SelectorEngine.find(selector) + .filter(foundElem => foundElem === this._element) - if (selector !== null && filterElement.length > 0) { + if (selector !== null && filterElement.length) { this._selector = selector this._triggerArray.push(elem) } @@ -90,18 +101,18 @@ class Collapse { // Getters - static get VERSION() { - return VERSION - } - static get Default() { return Default } + static get NAME() { + return NAME + } + // Public toggle() { - if ($(this._element).hasClass(CLASS_NAME_SHOW)) { + if (this._element.classList.contains(CLASS_NAME_SHOW)) { this.hide() } else { this.show() @@ -109,8 +120,7 @@ class Collapse { } show() { - if (this._isTransitioning || - $(this._element).hasClass(CLASS_NAME_SHOW)) { + if (this._isTransitioning || this._element.classList.contains(CLASS_NAME_SHOW)) { return } @@ -118,10 +128,10 @@ class Collapse { let activesData if (this._parent) { - actives = [].slice.call(this._parent.querySelectorAll(SELECTOR_ACTIVES)) + actives = SelectorEngine.find(SELECTOR_ACTIVES, this._parent) .filter(elem => { if (typeof this._config.parent === 'string') { - return elem.getAttribute('data-parent') === this._config.parent + return elem.getAttribute('data-bs-parent') === this._config.parent } return elem.classList.contains(CLASS_NAME_COLLAPSE) @@ -132,74 +142,74 @@ class Collapse { } } + const container = SelectorEngine.findOne(this._selector) if (actives) { - activesData = $(actives).not(this._selector).data(DATA_KEY) + const tempActiveData = actives.find(elem => container !== elem) + activesData = tempActiveData ? Data.get(tempActiveData, DATA_KEY) : null + if (activesData && activesData._isTransitioning) { return } } - const startEvent = $.Event(EVENT_SHOW) - $(this._element).trigger(startEvent) - if (startEvent.isDefaultPrevented()) { + const startEvent = EventHandler.trigger(this._element, EVENT_SHOW) + if (startEvent.defaultPrevented) { return } if (actives) { - Collapse._jQueryInterface.call($(actives).not(this._selector), 'hide') - if (!activesData) { - $(actives).data(DATA_KEY, null) - } + actives.forEach(elemActive => { + if (container !== elemActive) { + Collapse.collapseInterface(elemActive, 'hide') + } + + if (!activesData) { + Data.set(elemActive, DATA_KEY, null) + } + }) } const dimension = this._getDimension() - $(this._element) - .removeClass(CLASS_NAME_COLLAPSE) - .addClass(CLASS_NAME_COLLAPSING) + this._element.classList.remove(CLASS_NAME_COLLAPSE) + this._element.classList.add(CLASS_NAME_COLLAPSING) this._element.style[dimension] = 0 if (this._triggerArray.length) { - $(this._triggerArray) - .removeClass(CLASS_NAME_COLLAPSED) - .attr('aria-expanded', true) + this._triggerArray.forEach(element => { + element.classList.remove(CLASS_NAME_COLLAPSED) + element.setAttribute('aria-expanded', true) + }) } this.setTransitioning(true) const complete = () => { - $(this._element) - .removeClass(CLASS_NAME_COLLAPSING) - .addClass(`${CLASS_NAME_COLLAPSE} ${CLASS_NAME_SHOW}`) + this._element.classList.remove(CLASS_NAME_COLLAPSING) + this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW) this._element.style[dimension] = '' this.setTransitioning(false) - $(this._element).trigger(EVENT_SHOWN) + EventHandler.trigger(this._element, EVENT_SHOWN) } const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1) const scrollSize = `scroll${capitalizedDimension}` - const transitionDuration = Util.getTransitionDurationFromElement(this._element) - - $(this._element) - .one(Util.TRANSITION_END, complete) - .emulateTransitionEnd(transitionDuration) + this._queueCallback(complete, this._element, true) this._element.style[dimension] = `${this._element[scrollSize]}px` } hide() { - if (this._isTransitioning || - !$(this._element).hasClass(CLASS_NAME_SHOW)) { + if (this._isTransitioning || !this._element.classList.contains(CLASS_NAME_SHOW)) { return } - const startEvent = $.Event(EVENT_HIDE) - $(this._element).trigger(startEvent) - if (startEvent.isDefaultPrevented()) { + const startEvent = EventHandler.trigger(this._element, EVENT_HIDE) + if (startEvent.defaultPrevented) { return } @@ -207,24 +217,20 @@ class Collapse { this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px` - Util.reflow(this._element) + reflow(this._element) - $(this._element) - .addClass(CLASS_NAME_COLLAPSING) - .removeClass(`${CLASS_NAME_COLLAPSE} ${CLASS_NAME_SHOW}`) + this._element.classList.add(CLASS_NAME_COLLAPSING) + this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW) const triggerArrayLength = this._triggerArray.length if (triggerArrayLength > 0) { for (let i = 0; i < triggerArrayLength; i++) { const trigger = this._triggerArray[i] - const selector = Util.getSelectorFromElement(trigger) + const elem = getElementFromSelector(trigger) - if (selector !== null) { - const $elem = $([].slice.call(document.querySelectorAll(selector))) - if (!$elem.hasClass(CLASS_NAME_SHOW)) { - $(trigger).addClass(CLASS_NAME_COLLAPSED) - .attr('aria-expanded', false) - } + if (elem && !elem.classList.contains(CLASS_NAME_SHOW)) { + trigger.classList.add(CLASS_NAME_COLLAPSED) + trigger.setAttribute('aria-expanded', false) } } } @@ -233,34 +239,20 @@ class Collapse { const complete = () => { this.setTransitioning(false) - $(this._element) - .removeClass(CLASS_NAME_COLLAPSING) - .addClass(CLASS_NAME_COLLAPSE) - .trigger(EVENT_HIDDEN) + this._element.classList.remove(CLASS_NAME_COLLAPSING) + this._element.classList.add(CLASS_NAME_COLLAPSE) + EventHandler.trigger(this._element, EVENT_HIDDEN) } this._element.style[dimension] = '' - const transitionDuration = Util.getTransitionDurationFromElement(this._element) - $(this._element) - .one(Util.TRANSITION_END, complete) - .emulateTransitionEnd(transitionDuration) + this._queueCallback(complete, this._element, true) } setTransitioning(isTransitioning) { this._isTransitioning = isTransitioning } - dispose() { - $.removeData(this._element, DATA_KEY) - - this._config = null - this._parent = null - this._element = null - this._triggerArray = null - this._isTransitioning = null - } - // Private _getConfig(config) { @@ -269,85 +261,82 @@ class Collapse { ...config } config.toggle = Boolean(config.toggle) // Coerce string values - Util.typeCheckConfig(NAME, config, DefaultType) + typeCheckConfig(NAME, config, DefaultType) return config } _getDimension() { - const hasWidth = $(this._element).hasClass(DIMENSION_WIDTH) - return hasWidth ? DIMENSION_WIDTH : DIMENSION_HEIGHT + return this._element.classList.contains(WIDTH) ? WIDTH : HEIGHT } _getParent() { - let parent + let { parent } = this._config - if (Util.isElement(this._config.parent)) { - parent = this._config.parent + parent = getElement(parent) - // It's a jQuery object - if (typeof this._config.parent.jquery !== 'undefined') { - parent = this._config.parent[0] - } - } else { - parent = document.querySelector(this._config.parent) - } + const selector = `${SELECTOR_DATA_TOGGLE}[data-bs-parent="${parent}"]` - const selector = `[data-toggle="collapse"][data-parent="${this._config.parent}"]` - const children = [].slice.call(parent.querySelectorAll(selector)) + SelectorEngine.find(selector, parent) + .forEach(element => { + const selected = getElementFromSelector(element) - $(children).each((i, element) => { - this._addAriaAndCollapsedClass( - Collapse._getTargetFromElement(element), - [element] - ) - }) + this._addAriaAndCollapsedClass( + selected, + [element] + ) + }) return parent } _addAriaAndCollapsedClass(element, triggerArray) { - const isOpen = $(element).hasClass(CLASS_NAME_SHOW) - - if (triggerArray.length) { - $(triggerArray) - .toggleClass(CLASS_NAME_COLLAPSED, !isOpen) - .attr('aria-expanded', isOpen) + if (!element || !triggerArray.length) { + return } + + const isOpen = element.classList.contains(CLASS_NAME_SHOW) + + 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 _getTargetFromElement(element) { - const selector = Util.getSelectorFromElement(element) - return selector ? document.querySelector(selector) : null - } + static collapseInterface(element, config) { + let data = Data.get(element, DATA_KEY) + const _config = { + ...Default, + ...Manipulator.getDataAttributes(element), + ...(typeof config === 'object' && config ? config : {}) + } - static _jQueryInterface(config) { - return this.each(function () { - const $element = $(this) - let data = $element.data(DATA_KEY) - const _config = { - ...Default, - ...$element.data(), - ...(typeof config === 'object' && config ? config : {}) - } + if (!data && _config.toggle && typeof config === 'string' && /show|hide/.test(config)) { + _config.toggle = false + } - if (!data && _config.toggle && typeof config === 'string' && /show|hide/.test(config)) { - _config.toggle = false - } + if (!data) { + data = new Collapse(element, _config) + } - if (!data) { - data = new Collapse(this, _config) - $element.data(DATA_KEY, data) + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`) } - if (typeof config === 'string') { - if (typeof data[config] === 'undefined') { - throw new TypeError(`No method named "${config}"`) - } + data[config]() + } + } - data[config]() - } + static jQueryInterface(config) { + return this.each(function () { + Collapse.collapseInterface(this, config) }) } } @@ -358,21 +347,32 @@ class Collapse { * ------------------------------------------------------------------------ */ -$(document).on(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.currentTarget.tagName === 'A') { + if (event.target.tagName === 'A' || (event.delegateTarget && event.delegateTarget.tagName === 'A')) { event.preventDefault() } - const $trigger = $(this) - const selector = Util.getSelectorFromElement(this) - const selectors = [].slice.call(document.querySelectorAll(selector)) + const triggerData = Manipulator.getDataAttributes(this) + const selector = getSelectorFromElement(this) + const selectorElements = SelectorEngine.find(selector) + + selectorElements.forEach(element => { + const data = Data.get(element, DATA_KEY) + let config + if (data) { + // update parent attribute + if (data._parent === null && typeof triggerData.parent === 'string') { + data._config.parent = triggerData.parent + data._parent = data._getParent() + } - $(selectors).each(function () { - const $target = $(this) - const data = $target.data(DATA_KEY) - const config = data ? 'toggle' : $trigger.data() - Collapse._jQueryInterface.call($target, config) + config = 'toggle' + } else { + config = triggerData + } + + Collapse.collapseInterface(element, config) }) }) @@ -380,13 +380,9 @@ $(document).on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ + * add .Collapse to jQuery only if jQuery is present */ -$.fn[NAME] = Collapse._jQueryInterface -$.fn[NAME].Constructor = Collapse -$.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Collapse._jQueryInterface -} +defineJQueryPlugin(Collapse) export default Collapse diff --git a/vendor/twbs/bootstrap/js/src/dropdown.js b/vendor/twbs/bootstrap/js/src/dropdown.js index 76dda8f57..565edb892 100644 --- a/vendor/twbs/bootstrap/js/src/dropdown.js +++ b/vendor/twbs/bootstrap/js/src/dropdown.js @@ -1,13 +1,28 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): dropdown.js + * Bootstrap (v5.0.1): dropdown.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -import $ from 'jquery' -import Popper from 'popper.js' -import Util from './util' +import * as Popper from '@popperjs/core' + +import { + defineJQueryPlugin, + getElement, + getElementFromSelector, + isDisabled, + isElement, + isVisible, + isRTL, + noop, + typeCheckConfig +} from './util/index' +import Data from './dom/data' +import EventHandler from './dom/event-handler' +import Manipulator from './dom/manipulator' +import SelectorEngine from './dom/selector-engine' +import BaseComponent from './base-component' /** * ------------------------------------------------------------------------ @@ -16,18 +31,18 @@ import Util from './util' */ const NAME = 'dropdown' -const VERSION = '4.6.0' const DATA_KEY = 'bs.dropdown' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const JQUERY_NO_CONFLICT = $.fn[NAME] -const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key -const SPACE_KEYCODE = 32 // KeyboardEvent.which value for space key -const TAB_KEYCODE = 9 // KeyboardEvent.which value for tab key -const ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key -const ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key -const RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse) -const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}`) + +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}` @@ -38,43 +53,40 @@ const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}` const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY}${DATA_API_KEY}` const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}` -const CLASS_NAME_DISABLED = 'disabled' const CLASS_NAME_SHOW = 'show' const CLASS_NAME_DROPUP = 'dropup' -const CLASS_NAME_DROPRIGHT = 'dropright' -const CLASS_NAME_DROPLEFT = 'dropleft' -const CLASS_NAME_MENURIGHT = 'dropdown-menu-right' -const CLASS_NAME_POSITION_STATIC = 'position-static' +const CLASS_NAME_DROPEND = 'dropend' +const CLASS_NAME_DROPSTART = 'dropstart' +const CLASS_NAME_NAVBAR = 'navbar' -const SELECTOR_DATA_TOGGLE = '[data-toggle="dropdown"]' -const SELECTOR_FORM_CHILD = '.dropdown form' +const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="dropdown"]' const SELECTOR_MENU = '.dropdown-menu' const SELECTOR_NAVBAR_NAV = '.navbar-nav' const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)' -const PLACEMENT_TOP = 'top-start' -const PLACEMENT_TOPEND = 'top-end' -const PLACEMENT_BOTTOM = 'bottom-start' -const PLACEMENT_BOTTOMEND = 'bottom-end' -const PLACEMENT_RIGHT = 'right-start' -const PLACEMENT_LEFT = 'left-start' +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 Default = { - offset: 0, - flip: true, - boundary: 'scrollParent', + offset: [0, 2], + boundary: 'clippingParents', reference: 'toggle', display: 'dynamic', - popperConfig: null + popperConfig: null, + autoClose: true } const DefaultType = { - offset: '(number|string|function)', - flip: 'boolean', + offset: '(array|string|function)', boundary: '(string|element)', - reference: '(string|element)', + reference: '(string|element|object)', display: 'string', - popperConfig: '(null|object)' + popperConfig: '(null|object|function)', + autoClose: '(boolean|string)' } /** @@ -83,9 +95,10 @@ const DefaultType = { * ------------------------------------------------------------------------ */ -class Dropdown { +class Dropdown extends BaseComponent { constructor(element, config) { - this._element = element + super(element) + this._popper = null this._config = this._getConfig(config) this._menu = this._getMenuElement() @@ -96,10 +109,6 @@ class Dropdown { // Getters - static get VERSION() { - return VERSION - } - static get Default() { return Default } @@ -108,47 +117,47 @@ class Dropdown { return DefaultType } + static get NAME() { + return NAME + } + // Public toggle() { - if (this._element.disabled || $(this._element).hasClass(CLASS_NAME_DISABLED)) { + if (isDisabled(this._element)) { return } - const isActive = $(this._menu).hasClass(CLASS_NAME_SHOW) - - Dropdown._clearMenus() + const isActive = this._element.classList.contains(CLASS_NAME_SHOW) if (isActive) { + this.hide() return } - this.show(true) + this.show() } - show(usePopper = false) { - if (this._element.disabled || $(this._element).hasClass(CLASS_NAME_DISABLED) || $(this._menu).hasClass(CLASS_NAME_SHOW)) { + show() { + if (isDisabled(this._element) || this._menu.classList.contains(CLASS_NAME_SHOW)) { return } + const parent = Dropdown.getParentFromElement(this._element) const relatedTarget = { relatedTarget: this._element } - const showEvent = $.Event(EVENT_SHOW, relatedTarget) - const parent = Dropdown._getParentFromElement(this._element) - $(parent).trigger(showEvent) + const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, relatedTarget) - if (showEvent.isDefaultPrevented()) { + if (showEvent.defaultPrevented) { return } // Totally disable Popper for Dropdowns in Navbar - if (!this._inNavbar && usePopper) { - /** - * Check for Popper dependency - * Popper - https://popper.js.org - */ + if (this._inNavbar) { + Manipulator.setDataAttribute(this._menu, 'popper', 'none') + } else { if (typeof Popper === 'undefined') { throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org)') } @@ -157,23 +166,20 @@ class Dropdown { if (this._config.reference === 'parent') { referenceElement = parent - } else if (Util.isElement(this._config.reference)) { + } else if (isElement(this._config.reference)) { + referenceElement = getElement(this._config.reference) + } else if (typeof this._config.reference === 'object') { referenceElement = this._config.reference - - // Check if it's jQuery element - if (typeof this._config.reference.jquery !== 'undefined') { - referenceElement = this._config.reference[0] - } } - // If boundary is not `scrollParent`, then set position to `static` - // to allow the menu to "escape" the scroll parent's boundaries - // https://github.com/twbs/bootstrap/issues/24251 - if (this._config.boundary !== 'scrollParent') { - $(parent).addClass(CLASS_NAME_POSITION_STATIC) - } + const popperConfig = this._getPopperConfig() + const isDisplayStatic = popperConfig.modifiers.find(modifier => modifier.name === 'applyStyles' && modifier.enabled === false) - this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig()) + this._popper = Popper.createPopper(referenceElement, this._menu, popperConfig) + + if (isDisplayStatic) { + Manipulator.setDataAttribute(this._menu, 'popper', 'static') + } } // If this is a touch-enabled device we add extra @@ -181,266 +187,273 @@ class Dropdown { // 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).length === 0) { - $(document.body).children().on('mouseover', null, $.noop) + !parent.closest(SELECTOR_NAVBAR_NAV)) { + [].concat(...document.body.children) + .forEach(elem => EventHandler.on(elem, 'mouseover', noop)) } this._element.focus() this._element.setAttribute('aria-expanded', true) - $(this._menu).toggleClass(CLASS_NAME_SHOW) - $(parent) - .toggleClass(CLASS_NAME_SHOW) - .trigger($.Event(EVENT_SHOWN, relatedTarget)) + this._menu.classList.toggle(CLASS_NAME_SHOW) + this._element.classList.toggle(CLASS_NAME_SHOW) + EventHandler.trigger(this._element, EVENT_SHOWN, relatedTarget) } hide() { - if (this._element.disabled || $(this._element).hasClass(CLASS_NAME_DISABLED) || !$(this._menu).hasClass(CLASS_NAME_SHOW)) { + if (isDisabled(this._element) || !this._menu.classList.contains(CLASS_NAME_SHOW)) { return } const relatedTarget = { relatedTarget: this._element } - const hideEvent = $.Event(EVENT_HIDE, relatedTarget) - const parent = Dropdown._getParentFromElement(this._element) - - $(parent).trigger(hideEvent) - if (hideEvent.isDefaultPrevented()) { - return - } - - if (this._popper) { - this._popper.destroy() - } - - $(this._menu).toggleClass(CLASS_NAME_SHOW) - $(parent) - .toggleClass(CLASS_NAME_SHOW) - .trigger($.Event(EVENT_HIDDEN, relatedTarget)) + this._completeHide(relatedTarget) } dispose() { - $.removeData(this._element, DATA_KEY) - $(this._element).off(EVENT_KEY) - this._element = null - this._menu = null - if (this._popper !== null) { + if (this._popper) { this._popper.destroy() - this._popper = null } + + super.dispose() } update() { this._inNavbar = this._detectNavbar() - if (this._popper !== null) { - this._popper.scheduleUpdate() + if (this._popper) { + this._popper.update() } } // Private _addEventListeners() { - $(this._element).on(EVENT_CLICK, event => { + EventHandler.on(this._element, EVENT_CLICK, event => { event.preventDefault() - event.stopPropagation() this.toggle() }) } + _completeHide(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 ('ontouchstart' in document.documentElement) { + [].concat(...document.body.children) + .forEach(elem => EventHandler.off(elem, 'mouseover', 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.removeDataAttribute(this._menu, 'popper') + EventHandler.trigger(this._element, EVENT_HIDDEN, relatedTarget) + } + _getConfig(config) { config = { ...this.constructor.Default, - ...$(this._element).data(), + ...Manipulator.getDataAttributes(this._element), ...config } - Util.typeCheckConfig( - NAME, - config, - this.constructor.DefaultType - ) + typeCheckConfig(NAME, config, this.constructor.DefaultType) + + if (typeof config.reference === 'object' && !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 } _getMenuElement() { - if (!this._menu) { - const parent = Dropdown._getParentFromElement(this._element) + return SelectorEngine.next(this._element, SELECTOR_MENU)[0] + } - if (parent) { - this._menu = parent.querySelector(SELECTOR_MENU) - } + _getPlacement() { + const parentDropdown = this._element.parentNode + + if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) { + return PLACEMENT_RIGHT } - return this._menu - } + if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) { + return PLACEMENT_LEFT + } - _getPlacement() { - const $parentDropdown = $(this._element.parentNode) - let placement = PLACEMENT_BOTTOM - - // Handle dropup - if ($parentDropdown.hasClass(CLASS_NAME_DROPUP)) { - placement = $(this._menu).hasClass(CLASS_NAME_MENURIGHT) ? - PLACEMENT_TOPEND : - PLACEMENT_TOP - } else if ($parentDropdown.hasClass(CLASS_NAME_DROPRIGHT)) { - placement = PLACEMENT_RIGHT - } else if ($parentDropdown.hasClass(CLASS_NAME_DROPLEFT)) { - placement = PLACEMENT_LEFT - } else if ($(this._menu).hasClass(CLASS_NAME_MENURIGHT)) { - placement = PLACEMENT_BOTTOMEND - } - - return placement + // 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('.navbar').length > 0 + return this._element.closest(`.${CLASS_NAME_NAVBAR}`) !== null } _getOffset() { - const offset = {} + const { offset } = this._config - if (typeof this._config.offset === 'function') { - offset.fn = data => { - data.offsets = { - ...data.offsets, - ...(this._config.offset(data.offsets, this._element) || {}) - } + if (typeof offset === 'string') { + return offset.split(',').map(val => Number.parseInt(val, 10)) + } - return data - } - } else { - offset.offset = this._config.offset + if (typeof offset === 'function') { + return popperData => offset(popperData, this._element) } return offset } _getPopperConfig() { - const popperConfig = { + const defaultBsPopperConfig = { placement: this._getPlacement(), - modifiers: { - offset: this._getOffset(), - flip: { - enabled: this._config.flip - }, - preventOverflow: { - boundariesElement: this._config.boundary + modifiers: [{ + name: 'preventOverflow', + options: { + boundary: this._config.boundary } - } + }, + { + name: 'offset', + options: { + offset: this._getOffset() + } + }] } // Disable Popper if we have a static display if (this._config.display === 'static') { - popperConfig.modifiers.applyStyle = { + defaultBsPopperConfig.modifiers = [{ + name: 'applyStyles', enabled: false - } + }] } return { - ...popperConfig, - ...this._config.popperConfig + ...defaultBsPopperConfig, + ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig) } } + _selectMenuItem(event) { + const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(isVisible) + + if (!items.length) { + return + } + + let index = items.indexOf(event.target) + + // Up + if (event.key === ARROW_UP_KEY && index > 0) { + index-- + } + + // Down + if (event.key === ARROW_DOWN_KEY && index < items.length - 1) { + index++ + } + + // index is -1 if the first keydown is an ArrowUp + index = index === -1 ? 0 : index + + items[index].focus() + } + // Static - static _jQueryInterface(config) { - return this.each(function () { - let data = $(this).data(DATA_KEY) - const _config = typeof config === 'object' ? config : null + static dropdownInterface(element, config) { + let data = Data.get(element, DATA_KEY) + const _config = typeof config === 'object' ? config : null + + if (!data) { + data = new Dropdown(element, _config) + } - if (!data) { - data = new Dropdown(this, _config) - $(this).data(DATA_KEY, data) + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`) } - if (typeof config === 'string') { - if (typeof data[config] === 'undefined') { - throw new TypeError(`No method named "${config}"`) - } + data[config]() + } + } - data[config]() - } + static jQueryInterface(config) { + return this.each(function () { + Dropdown.dropdownInterface(this, config) }) } - static _clearMenus(event) { - if (event && (event.which === RIGHT_MOUSE_BUTTON_WHICH || - event.type === 'keyup' && event.which !== TAB_KEYCODE)) { + static clearMenus(event) { + if (event && (event.button === RIGHT_MOUSE_BUTTON || (event.type === 'keyup' && event.key !== TAB_KEY))) { return } - const toggles = [].slice.call(document.querySelectorAll(SELECTOR_DATA_TOGGLE)) + const toggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE) for (let i = 0, len = toggles.length; i < len; i++) { - const parent = Dropdown._getParentFromElement(toggles[i]) - const context = $(toggles[i]).data(DATA_KEY) - const relatedTarget = { - relatedTarget: toggles[i] - } - - if (event && event.type === 'click') { - relatedTarget.clickEvent = event - } - - if (!context) { - continue - } - - const dropdownMenu = context._menu - if (!$(parent).hasClass(CLASS_NAME_SHOW)) { + const context = Data.get(toggles[i], DATA_KEY) + if (!context || context._config.autoClose === false) { continue } - if (event && (event.type === 'click' && - /input|textarea/i.test(event.target.tagName) || event.type === 'keyup' && event.which === TAB_KEYCODE) && - $.contains(parent, event.target)) { + if (!context._element.classList.contains(CLASS_NAME_SHOW)) { continue } - const hideEvent = $.Event(EVENT_HIDE, relatedTarget) - $(parent).trigger(hideEvent) - if (hideEvent.isDefaultPrevented()) { - continue + const relatedTarget = { + relatedTarget: context._element } - // If this is a touch-enabled device we remove the extra - // empty mouseover listeners we added for iOS support - if ('ontouchstart' in document.documentElement) { - $(document.body).children().off('mouseover', null, $.noop) - } + 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 + } - toggles[i].setAttribute('aria-expanded', 'false') + // 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 (context._popper) { - context._popper.destroy() + if (event.type === 'click') { + relatedTarget.clickEvent = event + } } - $(dropdownMenu).removeClass(CLASS_NAME_SHOW) - $(parent) - .removeClass(CLASS_NAME_SHOW) - .trigger($.Event(EVENT_HIDDEN, relatedTarget)) + context._completeHide(relatedTarget) } } - static _getParentFromElement(element) { - let parent - const selector = Util.getSelectorFromElement(element) - - if (selector) { - parent = document.querySelector(selector) - } - - return parent || element.parentNode + static getParentFromElement(element) { + return getElementFromSelector(element) || element.parentNode } - // eslint-disable-next-line complexity - static _dataApiKeydownHandler(event) { + static dataApiKeydownHandler(event) { // If not input/textarea: // - And not a key in REGEXP_KEYDOWN => not a dropdown command // If input/textarea: @@ -449,57 +462,45 @@ class Dropdown { // - 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.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE && - (event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE || - $(event.target).closest(SELECTOR_MENU).length) : !REGEXP_KEYDOWN.test(event.which)) { - return - } - - if (this.disabled || $(this).hasClass(CLASS_NAME_DISABLED)) { + 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 parent = Dropdown._getParentFromElement(this) - const isActive = $(parent).hasClass(CLASS_NAME_SHOW) + const isActive = this.classList.contains(CLASS_NAME_SHOW) - if (!isActive && event.which === ESCAPE_KEYCODE) { + if (!isActive && event.key === ESCAPE_KEY) { return } event.preventDefault() event.stopPropagation() - if (!isActive || (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) { - if (event.which === ESCAPE_KEYCODE) { - $(parent.querySelector(SELECTOR_DATA_TOGGLE)).trigger('focus') - } - - $(this).trigger('click') + if (isDisabled(this)) { return } - const items = [].slice.call(parent.querySelectorAll(SELECTOR_VISIBLE_ITEMS)) - .filter(item => $(item).is(':visible')) + const getToggleButton = () => this.matches(SELECTOR_DATA_TOGGLE) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE)[0] - if (items.length === 0) { + if (event.key === ESCAPE_KEY) { + getToggleButton().focus() + Dropdown.clearMenus() return } - let index = items.indexOf(event.target) - - if (event.which === ARROW_UP_KEYCODE && index > 0) { // Up - index-- - } - - if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) { // Down - index++ + if (!isActive && (event.key === ARROW_UP_KEY || event.key === ARROW_DOWN_KEY)) { + getToggleButton().click() + return } - if (index < 0) { - index = 0 + if (!isActive || event.key === SPACE_KEY) { + Dropdown.clearMenus() + return } - items[index].focus() + Dropdown.getInstance(getToggleButton())._selectMenuItem(event) } } @@ -509,30 +510,22 @@ class Dropdown { * ------------------------------------------------------------------------ */ -$(document) - .on(EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown._dataApiKeydownHandler) - .on(EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown._dataApiKeydownHandler) - .on(`${EVENT_CLICK_DATA_API} ${EVENT_KEYUP_DATA_API}`, Dropdown._clearMenus) - .on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { - event.preventDefault() - event.stopPropagation() - Dropdown._jQueryInterface.call($(this), 'toggle') - }) - .on(EVENT_CLICK_DATA_API, SELECTOR_FORM_CHILD, e => { - e.stopPropagation() - }) +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.dropdownInterface(this) +}) /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ + * add .Dropdown to jQuery only if jQuery is present */ -$.fn[NAME] = Dropdown._jQueryInterface -$.fn[NAME].Constructor = Dropdown -$.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Dropdown._jQueryInterface -} +defineJQueryPlugin(Dropdown) export default Dropdown diff --git a/vendor/twbs/bootstrap/js/src/modal.js b/vendor/twbs/bootstrap/js/src/modal.js index 2e3017024..d4436bf4f 100644 --- a/vendor/twbs/bootstrap/js/src/modal.js +++ b/vendor/twbs/bootstrap/js/src/modal.js @@ -1,12 +1,26 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): modal.js + * Bootstrap (v5.0.1): modal.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -import $ from 'jquery' -import Util from './util' +import { + defineJQueryPlugin, + emulateTransitionEnd, + getElementFromSelector, + getTransitionDurationFromElement, + 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 { getWidth as getScrollBarWidth, hide as scrollBarHide, reset as scrollBarReset } from './util/scrollbar' +import BaseComponent from './base-component' +import Backdrop from './util/backdrop' /** * ------------------------------------------------------------------------ @@ -15,25 +29,21 @@ import Util from './util' */ const NAME = 'modal' -const VERSION = '4.6.0' const DATA_KEY = 'bs.modal' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const JQUERY_NO_CONFLICT = $.fn[NAME] -const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key +const ESCAPE_KEY = 'Escape' const Default = { backdrop: true, keyboard: true, - focus: true, - show: true + focus: true } const DefaultType = { backdrop: '(boolean|string)', keyboard: 'boolean', - focus: 'boolean', - show: 'boolean' + focus: 'boolean' } const EVENT_HIDE = `hide${EVENT_KEY}` @@ -49,9 +59,6 @@ const EVENT_MOUSEUP_DISMISS = `mouseup.dismiss${EVENT_KEY}` const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}` const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}` -const CLASS_NAME_SCROLLABLE = 'modal-dialog-scrollable' -const CLASS_NAME_SCROLLBAR_MEASURER = 'modal-scrollbar-measure' -const CLASS_NAME_BACKDROP = 'modal-backdrop' const CLASS_NAME_OPEN = 'modal-open' const CLASS_NAME_FADE = 'fade' const CLASS_NAME_SHOW = 'show' @@ -59,10 +66,8 @@ const CLASS_NAME_STATIC = 'modal-static' const SELECTOR_DIALOG = '.modal-dialog' const SELECTOR_MODAL_BODY = '.modal-body' -const SELECTOR_DATA_TOGGLE = '[data-toggle="modal"]' -const SELECTOR_DATA_DISMISS = '[data-dismiss="modal"]' -const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top' -const SELECTOR_STICKY_CONTENT = '.sticky-top' +const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="modal"]' +const SELECTOR_DATA_DISMISS = '[data-bs-dismiss="modal"]' /** * ------------------------------------------------------------------------ @@ -70,29 +75,28 @@ const SELECTOR_STICKY_CONTENT = '.sticky-top' * ------------------------------------------------------------------------ */ -class Modal { +class Modal extends BaseComponent { constructor(element, config) { + super(element) + this._config = this._getConfig(config) - this._element = element - this._dialog = element.querySelector(SELECTOR_DIALOG) - this._backdrop = null + this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element) + this._backdrop = this._initializeBackDrop() this._isShown = false - this._isBodyOverflowing = false this._ignoreBackdropClick = false this._isTransitioning = false - this._scrollbarWidth = 0 } // Getters - static get VERSION() { - return VERSION - } - static get Default() { return Default } + static get NAME() { + return NAME + } + // Public toggle(relatedTarget) { @@ -104,39 +108,34 @@ class Modal { return } - if ($(this._element).hasClass(CLASS_NAME_FADE)) { + if (this._isAnimated()) { this._isTransitioning = true } - const showEvent = $.Event(EVENT_SHOW, { + const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, { relatedTarget }) - $(this._element).trigger(showEvent) - - if (this._isShown || showEvent.isDefaultPrevented()) { + if (this._isShown || showEvent.defaultPrevented) { return } this._isShown = true - this._checkScrollbar() - this._setScrollbar() + scrollBarHide() + + document.body.classList.add(CLASS_NAME_OPEN) this._adjustDialog() this._setEscapeEvent() this._setResizeEvent() - $(this._element).on( - EVENT_CLICK_DISMISS, - SELECTOR_DATA_DISMISS, - event => this.hide(event) - ) + EventHandler.on(this._element, EVENT_CLICK_DISMISS, SELECTOR_DATA_DISMISS, event => this.hide(event)) - $(this._dialog).on(EVENT_MOUSEDOWN_DISMISS, () => { - $(this._element).one(EVENT_MOUSEUP_DISMISS, event => { - if ($(event.target).is(this._element)) { + EventHandler.on(this._dialog, EVENT_MOUSEDOWN_DISMISS, () => { + EventHandler.one(this._element, EVENT_MOUSEUP_DISMISS, event => { + if (event.target === this._element) { this._ignoreBackdropClick = true } }) @@ -154,64 +153,45 @@ class Modal { return } - const hideEvent = $.Event(EVENT_HIDE) - - $(this._element).trigger(hideEvent) + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE) - if (!this._isShown || hideEvent.isDefaultPrevented()) { + if (hideEvent.defaultPrevented) { return } this._isShown = false - const transition = $(this._element).hasClass(CLASS_NAME_FADE) + const isAnimated = this._isAnimated() - if (transition) { + if (isAnimated) { this._isTransitioning = true } this._setEscapeEvent() this._setResizeEvent() - $(document).off(EVENT_FOCUSIN) + EventHandler.off(document, EVENT_FOCUSIN) - $(this._element).removeClass(CLASS_NAME_SHOW) + this._element.classList.remove(CLASS_NAME_SHOW) - $(this._element).off(EVENT_CLICK_DISMISS) - $(this._dialog).off(EVENT_MOUSEDOWN_DISMISS) + EventHandler.off(this._element, EVENT_CLICK_DISMISS) + EventHandler.off(this._dialog, EVENT_MOUSEDOWN_DISMISS) - if (transition) { - const transitionDuration = Util.getTransitionDurationFromElement(this._element) - - $(this._element) - .one(Util.TRANSITION_END, event => this._hideModal(event)) - .emulateTransitionEnd(transitionDuration) - } else { - this._hideModal() - } + this._queueCallback(() => this._hideModal(), this._element, isAnimated) } dispose() { - [window, this._element, this._dialog] - .forEach(htmlElement => $(htmlElement).off(EVENT_KEY)) + [window, this._dialog] + .forEach(htmlElement => EventHandler.off(htmlElement, EVENT_KEY)) + + this._backdrop.dispose() + super.dispose() /** * `document` has 2 events `EVENT_FOCUSIN` and `EVENT_CLICK_DATA_API` * Do not move `document` in `htmlElements` array * It will remove `EVENT_CLICK_DATA_API` event that should remain */ - $(document).off(EVENT_FOCUSIN) - - $.removeData(this._element, DATA_KEY) - - this._config = null - this._element = null - this._dialog = null - this._backdrop = null - this._isShown = null - this._isBodyOverflowing = null - this._ignoreBackdropClick = null - this._isTransitioning = null - this._scrollbarWidth = null + EventHandler.off(document, EVENT_FOCUSIN) } handleUpdate() { @@ -220,53 +200,28 @@ class Modal { // Private + _initializeBackDrop() { + return new Backdrop({ + isVisible: Boolean(this._config.backdrop), // 'static' option will be translated to true, and booleans will keep their value + isAnimated: this._isAnimated() + }) + } + _getConfig(config) { config = { ...Default, + ...Manipulator.getDataAttributes(this._element), ...config } - Util.typeCheckConfig(NAME, config, DefaultType) + typeCheckConfig(NAME, config, DefaultType) return config } - _triggerBackdropTransition() { - const hideEventPrevented = $.Event(EVENT_HIDE_PREVENTED) - - $(this._element).trigger(hideEventPrevented) - if (hideEventPrevented.isDefaultPrevented()) { - return - } - - const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight - - if (!isModalOverflowing) { - this._element.style.overflowY = 'hidden' - } - - this._element.classList.add(CLASS_NAME_STATIC) - - const modalTransitionDuration = Util.getTransitionDurationFromElement(this._dialog) - $(this._element).off(Util.TRANSITION_END) - - $(this._element).one(Util.TRANSITION_END, () => { - this._element.classList.remove(CLASS_NAME_STATIC) - if (!isModalOverflowing) { - $(this._element).one(Util.TRANSITION_END, () => { - this._element.style.overflowY = '' - }) - .emulateTransitionEnd(this._element, modalTransitionDuration) - } - }) - .emulateTransitionEnd(modalTransitionDuration) - this._element.focus() - } - _showElement(relatedTarget) { - const transition = $(this._element).hasClass(CLASS_NAME_FADE) - const modalBody = this._dialog ? this._dialog.querySelector(SELECTOR_MODAL_BODY) : null + const isAnimated = this._isAnimated() + const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog) - if (!this._element.parentNode || - this._element.parentNode.nodeType !== Node.ELEMENT_NODE) { + if (!this._element.parentNode || this._element.parentNode.nodeType !== Node.ELEMENT_NODE) { // Don't move modal's DOM position document.body.appendChild(this._element) } @@ -275,79 +230,67 @@ class Modal { this._element.removeAttribute('aria-hidden') this._element.setAttribute('aria-modal', true) this._element.setAttribute('role', 'dialog') + this._element.scrollTop = 0 - if ($(this._dialog).hasClass(CLASS_NAME_SCROLLABLE) && modalBody) { + if (modalBody) { modalBody.scrollTop = 0 - } else { - this._element.scrollTop = 0 } - if (transition) { - Util.reflow(this._element) + if (isAnimated) { + reflow(this._element) } - $(this._element).addClass(CLASS_NAME_SHOW) + this._element.classList.add(CLASS_NAME_SHOW) if (this._config.focus) { this._enforceFocus() } - const shownEvent = $.Event(EVENT_SHOWN, { - relatedTarget - }) - const transitionComplete = () => { if (this._config.focus) { this._element.focus() } this._isTransitioning = false - $(this._element).trigger(shownEvent) + EventHandler.trigger(this._element, EVENT_SHOWN, { + relatedTarget + }) } - if (transition) { - const transitionDuration = Util.getTransitionDurationFromElement(this._dialog) - - $(this._dialog) - .one(Util.TRANSITION_END, transitionComplete) - .emulateTransitionEnd(transitionDuration) - } else { - transitionComplete() - } + this._queueCallback(transitionComplete, this._dialog, isAnimated) } _enforceFocus() { - $(document) - .off(EVENT_FOCUSIN) // Guard against infinite focus loop - .on(EVENT_FOCUSIN, event => { - if (document !== event.target && - this._element !== event.target && - $(this._element).has(event.target).length === 0) { - this._element.focus() - } - }) + EventHandler.off(document, EVENT_FOCUSIN) // guard against infinite focus loop + EventHandler.on(document, EVENT_FOCUSIN, event => { + if (document !== event.target && + this._element !== event.target && + !this._element.contains(event.target)) { + this._element.focus() + } + }) } _setEscapeEvent() { if (this._isShown) { - $(this._element).on(EVENT_KEYDOWN_DISMISS, event => { - if (this._config.keyboard && event.which === ESCAPE_KEYCODE) { + EventHandler.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.which === ESCAPE_KEYCODE) { + } else if (!this._config.keyboard && event.key === ESCAPE_KEY) { this._triggerBackdropTransition() } }) - } else if (!this._isShown) { - $(this._element).off(EVENT_KEYDOWN_DISMISS) + } else { + EventHandler.off(this._element, EVENT_KEYDOWN_DISMISS) } } _setResizeEvent() { if (this._isShown) { - $(window).on(EVENT_RESIZE, event => this.handleUpdate(event)) + EventHandler.on(window, EVENT_RESIZE, () => this._adjustDialog()) } else { - $(window).off(EVENT_RESIZE) + EventHandler.off(window, EVENT_RESIZE) } } @@ -357,110 +300,82 @@ class Modal { this._element.removeAttribute('aria-modal') this._element.removeAttribute('role') this._isTransitioning = false - this._showBackdrop(() => { - $(document.body).removeClass(CLASS_NAME_OPEN) + this._backdrop.hide(() => { + document.body.classList.remove(CLASS_NAME_OPEN) this._resetAdjustments() - this._resetScrollbar() - $(this._element).trigger(EVENT_HIDDEN) + scrollBarReset() + EventHandler.trigger(this._element, EVENT_HIDDEN) }) } - _removeBackdrop() { - if (this._backdrop) { - $(this._backdrop).remove() - this._backdrop = null - } - } - _showBackdrop(callback) { - const animate = $(this._element).hasClass(CLASS_NAME_FADE) ? - CLASS_NAME_FADE : '' - - if (this._isShown && this._config.backdrop) { - this._backdrop = document.createElement('div') - this._backdrop.className = CLASS_NAME_BACKDROP - - if (animate) { - this._backdrop.classList.add(animate) - } - - $(this._backdrop).appendTo(document.body) - - $(this._element).on(EVENT_CLICK_DISMISS, event => { - if (this._ignoreBackdropClick) { - this._ignoreBackdropClick = false - return - } - - if (event.target !== event.currentTarget) { - return - } - - if (this._config.backdrop === 'static') { - this._triggerBackdropTransition() - } else { - this.hide() - } - }) - - if (animate) { - Util.reflow(this._backdrop) + EventHandler.on(this._element, EVENT_CLICK_DISMISS, event => { + if (this._ignoreBackdropClick) { + this._ignoreBackdropClick = false + return } - $(this._backdrop).addClass(CLASS_NAME_SHOW) - - if (!callback) { + if (event.target !== event.currentTarget) { return } - if (!animate) { - callback() - return + if (this._config.backdrop === true) { + this.hide() + } else if (this._config.backdrop === 'static') { + this._triggerBackdropTransition() } + }) - const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop) + this._backdrop.show(callback) + } - $(this._backdrop) - .one(Util.TRANSITION_END, callback) - .emulateTransitionEnd(backdropTransitionDuration) - } else if (!this._isShown && this._backdrop) { - $(this._backdrop).removeClass(CLASS_NAME_SHOW) + _isAnimated() { + return this._element.classList.contains(CLASS_NAME_FADE) + } - const callbackRemove = () => { - this._removeBackdrop() - if (callback) { - callback() - } - } + _triggerBackdropTransition() { + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED) + if (hideEvent.defaultPrevented) { + return + } - if ($(this._element).hasClass(CLASS_NAME_FADE)) { - const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop) + const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight - $(this._backdrop) - .one(Util.TRANSITION_END, callbackRemove) - .emulateTransitionEnd(backdropTransitionDuration) - } else { - callbackRemove() - } - } else if (callback) { - callback() + if (!isModalOverflowing) { + this._element.style.overflowY = 'hidden' } + + this._element.classList.add(CLASS_NAME_STATIC) + const modalTransitionDuration = getTransitionDurationFromElement(this._dialog) + EventHandler.off(this._element, 'transitionend') + EventHandler.one(this._element, 'transitionend', () => { + this._element.classList.remove(CLASS_NAME_STATIC) + if (!isModalOverflowing) { + EventHandler.one(this._element, 'transitionend', () => { + this._element.style.overflowY = '' + }) + emulateTransitionEnd(this._element, modalTransitionDuration) + } + }) + emulateTransitionEnd(this._element, modalTransitionDuration) + this._element.focus() } // ---------------------------------------------------------------------- // the following methods are used to handle overflowing modals - // todo (fat): these should probably be refactored out of modal.js // ---------------------------------------------------------------------- _adjustDialog() { const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight + const scrollbarWidth = getScrollBarWidth() + const isBodyOverflowing = scrollbarWidth > 0 - if (!this._isBodyOverflowing && isModalOverflowing) { - this._element.style.paddingLeft = `${this._scrollbarWidth}px` + if ((!isBodyOverflowing && isModalOverflowing && !isRTL()) || (isBodyOverflowing && !isModalOverflowing && isRTL())) { + this._element.style.paddingLeft = `${scrollbarWidth}px` } - if (this._isBodyOverflowing && !isModalOverflowing) { - this._element.style.paddingRight = `${this._scrollbarWidth}px` + if ((isBodyOverflowing && !isModalOverflowing && !isRTL()) || (!isBodyOverflowing && isModalOverflowing && isRTL())) { + this._element.style.paddingRight = `${scrollbarWidth}px` } } @@ -469,106 +384,21 @@ class Modal { this._element.style.paddingRight = '' } - _checkScrollbar() { - const rect = document.body.getBoundingClientRect() - this._isBodyOverflowing = Math.round(rect.left + rect.right) < window.innerWidth - this._scrollbarWidth = this._getScrollbarWidth() - } - - _setScrollbar() { - if (this._isBodyOverflowing) { - // Note: DOMNode.style.paddingRight returns the actual value or '' if not set - // while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set - const fixedContent = [].slice.call(document.querySelectorAll(SELECTOR_FIXED_CONTENT)) - const stickyContent = [].slice.call(document.querySelectorAll(SELECTOR_STICKY_CONTENT)) - - // Adjust fixed content padding - $(fixedContent).each((index, element) => { - const actualPadding = element.style.paddingRight - const calculatedPadding = $(element).css('padding-right') - $(element) - .data('padding-right', actualPadding) - .css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`) - }) - - // Adjust sticky content margin - $(stickyContent).each((index, element) => { - const actualMargin = element.style.marginRight - const calculatedMargin = $(element).css('margin-right') - $(element) - .data('margin-right', actualMargin) - .css('margin-right', `${parseFloat(calculatedMargin) - this._scrollbarWidth}px`) - }) - - // Adjust body padding - const actualPadding = document.body.style.paddingRight - const calculatedPadding = $(document.body).css('padding-right') - $(document.body) - .data('padding-right', actualPadding) - .css('padding-right', `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`) - } - - $(document.body).addClass(CLASS_NAME_OPEN) - } - - _resetScrollbar() { - // Restore fixed content padding - const fixedContent = [].slice.call(document.querySelectorAll(SELECTOR_FIXED_CONTENT)) - $(fixedContent).each((index, element) => { - const padding = $(element).data('padding-right') - $(element).removeData('padding-right') - element.style.paddingRight = padding ? padding : '' - }) - - // Restore sticky content - const elements = [].slice.call(document.querySelectorAll(`${SELECTOR_STICKY_CONTENT}`)) - $(elements).each((index, element) => { - const margin = $(element).data('margin-right') - if (typeof margin !== 'undefined') { - $(element).css('margin-right', margin).removeData('margin-right') - } - }) - - // Restore body padding - const padding = $(document.body).data('padding-right') - $(document.body).removeData('padding-right') - document.body.style.paddingRight = padding ? padding : '' - } - - _getScrollbarWidth() { // thx d.walsh - const scrollDiv = document.createElement('div') - scrollDiv.className = CLASS_NAME_SCROLLBAR_MEASURER - document.body.appendChild(scrollDiv) - const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth - document.body.removeChild(scrollDiv) - return scrollbarWidth - } - // Static - static _jQueryInterface(config, relatedTarget) { + static jQueryInterface(config, relatedTarget) { return this.each(function () { - let data = $(this).data(DATA_KEY) - const _config = { - ...Default, - ...$(this).data(), - ...(typeof config === 'object' && config ? config : {}) - } + const data = Modal.getInstance(this) || new Modal(this, typeof config === 'object' ? config : {}) - if (!data) { - data = new Modal(this, _config) - $(this).data(DATA_KEY, data) + if (typeof config !== 'string') { + return } - if (typeof config === 'string') { - if (typeof data[config] === 'undefined') { - throw new TypeError(`No method named "${config}"`) - } - - data[config](relatedTarget) - } else if (_config.show) { - data.show(relatedTarget) + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`) } + + data[config](relatedTarget) }) } } @@ -579,51 +409,38 @@ class Modal { * ------------------------------------------------------------------------ */ -$(document).on(EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { - let target - const selector = Util.getSelectorFromElement(this) - - if (selector) { - target = document.querySelector(selector) - } +EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { + const target = getElementFromSelector(this) - const config = $(target).data(DATA_KEY) ? - 'toggle' : { - ...$(target).data(), - ...$(this).data() - } - - if (this.tagName === 'A' || this.tagName === 'AREA') { + if (['A', 'AREA'].includes(this.tagName)) { event.preventDefault() } - const $target = $(target).one(EVENT_SHOW, showEvent => { - if (showEvent.isDefaultPrevented()) { - // Only register focus restorer if modal will actually get shown + EventHandler.one(target, EVENT_SHOW, showEvent => { + if (showEvent.defaultPrevented) { + // only register focus restorer if modal will actually get shown return } - $target.one(EVENT_HIDDEN, () => { - if ($(this).is(':visible')) { + EventHandler.one(target, EVENT_HIDDEN, () => { + if (isVisible(this)) { this.focus() } }) }) - Modal._jQueryInterface.call($(target), config, this) + const data = Modal.getInstance(target) || new Modal(target) + + data.toggle(this) }) /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ + * add .Modal to jQuery only if jQuery is present */ -$.fn[NAME] = Modal._jQueryInterface -$.fn[NAME].Constructor = Modal -$.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Modal._jQueryInterface -} +defineJQueryPlugin(Modal) export default Modal diff --git a/vendor/twbs/bootstrap/js/src/popover.js b/vendor/twbs/bootstrap/js/src/popover.js index 4e2c260b9..b39985124 100644 --- a/vendor/twbs/bootstrap/js/src/popover.js +++ b/vendor/twbs/bootstrap/js/src/popover.js @@ -1,11 +1,13 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): popover.js + * Bootstrap (v5.0.1): popover.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -import $ from 'jquery' +import { defineJQueryPlugin } from './util/index' +import Data from './dom/data' +import SelectorEngine from './dom/selector-engine' import Tooltip from './tooltip' /** @@ -15,22 +17,22 @@ import Tooltip from './tooltip' */ const NAME = 'popover' -const VERSION = '4.6.0' const DATA_KEY = 'bs.popover' const EVENT_KEY = `.${DATA_KEY}` -const JQUERY_NO_CONFLICT = $.fn[NAME] const CLASS_PREFIX = 'bs-popover' const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g') const Default = { ...Tooltip.Default, placement: 'right', + offset: [0, 8], trigger: 'click', content: '', template: '<div class="popover" role="tooltip">' + - '<div class="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>' } const DefaultType = { @@ -38,12 +40,6 @@ const DefaultType = { content: '(string|element|function)' } -const CLASS_NAME_FADE = 'fade' -const CLASS_NAME_SHOW = 'show' - -const SELECTOR_TITLE = '.popover-header' -const SELECTOR_CONTENT = '.popover-body' - const Event = { HIDE: `hide${EVENT_KEY}`, HIDDEN: `hidden${EVENT_KEY}`, @@ -57,6 +53,12 @@ const Event = { MOUSELEAVE: `mouseleave${EVENT_KEY}` } +const CLASS_NAME_FADE = 'fade' +const CLASS_NAME_SHOW = 'show' + +const SELECTOR_TITLE = '.popover-header' +const SELECTOR_CONTENT = '.popover-body' + /** * ------------------------------------------------------------------------ * Class Definition @@ -66,10 +68,6 @@ const Event = { class Popover extends Tooltip { // Getters - static get VERSION() { - return VERSION - } - static get Default() { return Default } @@ -78,18 +76,10 @@ class Popover extends Tooltip { return NAME } - static get DATA_KEY() { - return DATA_KEY - } - static get Event() { return Event } - static get EVENT_KEY() { - return EVENT_KEY - } - static get DefaultType() { return DefaultType } @@ -100,50 +90,45 @@ class Popover extends Tooltip { return this.getTitle() || this._getContent() } - addAttachmentClass(attachment) { - $(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`) - } - - getTipElement() { - this.tip = this.tip || $(this.config.template)[0] - return this.tip - } - setContent() { - const $tip = $(this.getTipElement()) + const tip = this.getTipElement() - // We use append for html objects to maintain js events - this.setElementContent($tip.find(SELECTOR_TITLE), this.getTitle()) + // we use append for html objects to maintain js events + this.setElementContent(SelectorEngine.findOne(SELECTOR_TITLE, tip), this.getTitle()) let content = this._getContent() if (typeof content === 'function') { - content = content.call(this.element) + content = content.call(this._element) } - this.setElementContent($tip.find(SELECTOR_CONTENT), content) + this.setElementContent(SelectorEngine.findOne(SELECTOR_CONTENT, tip), content) - $tip.removeClass(`${CLASS_NAME_FADE} ${CLASS_NAME_SHOW}`) + tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW) } // Private + _addAttachmentClass(attachment) { + this.getTipElement().classList.add(`${CLASS_PREFIX}-${this.updateAttachment(attachment)}`) + } + _getContent() { - return this.element.getAttribute('data-content') || - this.config.content + return this._element.getAttribute('data-bs-content') || this._config.content } _cleanTipClass() { - const $tip = $(this.getTipElement()) - const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX) + const tip = this.getTipElement() + const tabClass = tip.getAttribute('class').match(BSCLS_PREFIX_REGEX) if (tabClass !== null && tabClass.length > 0) { - $tip.removeClass(tabClass.join('')) + tabClass.map(token => token.trim()) + .forEach(tClass => tip.classList.remove(tClass)) } } // Static - static _jQueryInterface(config) { + static jQueryInterface(config) { return this.each(function () { - let data = $(this).data(DATA_KEY) + let data = Data.get(this, DATA_KEY) const _config = typeof config === 'object' ? config : null if (!data && /dispose|hide/.test(config)) { @@ -152,7 +137,7 @@ class Popover extends Tooltip { if (!data) { data = new Popover(this, _config) - $(this).data(DATA_KEY, data) + Data.set(this, DATA_KEY, data) } if (typeof config === 'string') { @@ -170,13 +155,9 @@ class Popover extends Tooltip { * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ + * add .Popover to jQuery only if jQuery is present */ -$.fn[NAME] = Popover._jQueryInterface -$.fn[NAME].Constructor = Popover -$.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Popover._jQueryInterface -} +defineJQueryPlugin(Popover) export default Popover diff --git a/vendor/twbs/bootstrap/js/src/scrollspy.js b/vendor/twbs/bootstrap/js/src/scrollspy.js index 351df0649..3297c45c2 100644 --- a/vendor/twbs/bootstrap/js/src/scrollspy.js +++ b/vendor/twbs/bootstrap/js/src/scrollspy.js @@ -1,12 +1,21 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): scrollspy.js + * Bootstrap (v5.0.1): scrollspy.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -import $ from 'jquery' -import Util from './util' +import { + defineJQueryPlugin, + getSelectorFromElement, + getUID, + isElement, + 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' /** * ------------------------------------------------------------------------ @@ -15,11 +24,9 @@ import Util from './util' */ const NAME = 'scrollspy' -const VERSION = '4.6.0' const DATA_KEY = 'bs.scrollspy' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const JQUERY_NO_CONFLICT = $.fn[NAME] const Default = { offset: 10, @@ -40,13 +47,12 @@ 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-spy="scroll"]' +const SELECTOR_DATA_SPY = '[data-bs-spy="scroll"]' 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_DROPDOWN = '.dropdown' -const SELECTOR_DROPDOWN_ITEMS = '.dropdown-item' const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle' const METHOD_OFFSET = 'offset' @@ -58,20 +64,18 @@ const METHOD_POSITION = 'position' * ------------------------------------------------------------------------ */ -class ScrollSpy { +class ScrollSpy extends BaseComponent { constructor(element, config) { - this._element = element - this._scrollElement = element.tagName === 'BODY' ? window : element + super(element) + this._scrollElement = this._element.tagName === 'BODY' ? window : this._element this._config = this._getConfig(config) - this._selector = `${this._config.target} ${SELECTOR_NAV_LINKS},` + - `${this._config.target} ${SELECTOR_LIST_ITEMS},` + - `${this._config.target} ${SELECTOR_DROPDOWN_ITEMS}` + this._selector = `${this._config.target} ${SELECTOR_NAV_LINKS}, ${this._config.target} ${SELECTOR_LIST_ITEMS}, ${this._config.target} .${CLASS_NAME_DROPDOWN_ITEM}` this._offsets = [] this._targets = [] this._activeTarget = null this._scrollHeight = 0 - $(this._scrollElement).on(EVENT_SCROLL, event => this._process(event)) + EventHandler.on(this._scrollElement, EVENT_SCROLL, () => this._process()) this.refresh() this._process() @@ -79,55 +83,51 @@ class ScrollSpy { // Getters - static get VERSION() { - return VERSION - } - static get Default() { return Default } + static get NAME() { + return NAME + } + // Public refresh() { const autoMethod = this._scrollElement === this._scrollElement.window ? - METHOD_OFFSET : METHOD_POSITION + METHOD_OFFSET : + METHOD_POSITION const offsetMethod = this._config.method === 'auto' ? - autoMethod : this._config.method + autoMethod : + this._config.method const offsetBase = offsetMethod === METHOD_POSITION ? - this._getScrollTop() : 0 + this._getScrollTop() : + 0 this._offsets = [] this._targets = [] - this._scrollHeight = this._getScrollHeight() - const targets = [].slice.call(document.querySelectorAll(this._selector)) + const targets = SelectorEngine.find(this._selector) - targets - .map(element => { - let target - const targetSelector = Util.getSelectorFromElement(element) + targets.map(element => { + const targetSelector = getSelectorFromElement(element) + const target = targetSelector ? SelectorEngine.findOne(targetSelector) : null - if (targetSelector) { - target = document.querySelector(targetSelector) - } - - if (target) { - const targetBCR = target.getBoundingClientRect() - if (targetBCR.width || targetBCR.height) { - // TODO (fat): remove sketch reliance on jQuery position/offset - return [ - $(target)[offsetMethod]().top + offsetBase, - targetSelector - ] - } + if (target) { + const targetBCR = target.getBoundingClientRect() + if (targetBCR.width || targetBCR.height) { + return [ + Manipulator[offsetMethod](target).top + offsetBase, + targetSelector + ] } + } - return null - }) + return null + }) .filter(item => item) .sort((a, b) => a[0] - b[0]) .forEach(item => { @@ -137,17 +137,8 @@ class ScrollSpy { } dispose() { - $.removeData(this._element, DATA_KEY) - $(this._scrollElement).off(EVENT_KEY) - - this._element = null - this._scrollElement = null - this._config = null - this._selector = null - this._offsets = null - this._targets = null - this._activeTarget = null - this._scrollHeight = null + EventHandler.off(this._scrollElement, EVENT_KEY) + super.dispose() } // Private @@ -155,27 +146,29 @@ class ScrollSpy { _getConfig(config) { config = { ...Default, + ...Manipulator.getDataAttributes(this._element), ...(typeof config === 'object' && config ? config : {}) } - if (typeof config.target !== 'string' && Util.isElement(config.target)) { - let id = $(config.target).attr('id') + if (typeof config.target !== 'string' && isElement(config.target)) { + let { id } = config.target if (!id) { - id = Util.getUID(NAME) - $(config.target).attr('id', id) + id = getUID(NAME) + config.target.id = id } config.target = `#${id}` } - Util.typeCheckConfig(NAME, config, DefaultType) + typeCheckConfig(NAME, config, DefaultType) return config } _getScrollTop() { return this._scrollElement === window ? - this._scrollElement.pageYOffset : this._scrollElement.scrollTop + this._scrollElement.pageYOffset : + this._scrollElement.scrollTop } _getScrollHeight() { @@ -187,7 +180,8 @@ class ScrollSpy { _getOffsetHeight() { return this._scrollElement === window ? - window.innerHeight : this._scrollElement.getBoundingClientRect().height + window.innerHeight : + this._scrollElement.getBoundingClientRect().height } _process() { @@ -218,8 +212,7 @@ class ScrollSpy { 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]) + (typeof this._offsets[i + 1] === 'undefined' || scrollTop < this._offsets[i + 1]) if (isActiveTarget) { this._activate(this._targets[i]) @@ -232,62 +225,62 @@ class ScrollSpy { this._clear() - const queries = this._selector - .split(',') - .map(selector => `${selector}[data-target="${target}"],${selector}[href="${target}"]`) + const queries = this._selector.split(',') + .map(selector => `${selector}[data-bs-target="${target}"],${selector}[href="${target}"]`) + + const link = SelectorEngine.findOne(queries.join(',')) - const $link = $([].slice.call(document.querySelectorAll(queries.join(',')))) + if (link.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) { + SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE, link.closest(SELECTOR_DROPDOWN)) + .classList.add(CLASS_NAME_ACTIVE) - if ($link.hasClass(CLASS_NAME_DROPDOWN_ITEM)) { - $link.closest(SELECTOR_DROPDOWN) - .find(SELECTOR_DROPDOWN_TOGGLE) - .addClass(CLASS_NAME_ACTIVE) - $link.addClass(CLASS_NAME_ACTIVE) + link.classList.add(CLASS_NAME_ACTIVE) } else { // Set triggered link as active - $link.addClass(CLASS_NAME_ACTIVE) - // Set triggered links parents as active - // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor - $link.parents(SELECTOR_NAV_LIST_GROUP) - .prev(`${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`) - .addClass(CLASS_NAME_ACTIVE) - // Handle special case when .nav-link is inside .nav-item - $link.parents(SELECTOR_NAV_LIST_GROUP) - .prev(SELECTOR_NAV_ITEMS) - .children(SELECTOR_NAV_LINKS) - .addClass(CLASS_NAME_ACTIVE) + link.classList.add(CLASS_NAME_ACTIVE) + + SelectorEngine.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.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.prev(listGroup, SELECTOR_NAV_ITEMS) + .forEach(navItem => { + SelectorEngine.children(navItem, SELECTOR_NAV_LINKS) + .forEach(item => item.classList.add(CLASS_NAME_ACTIVE)) + }) + }) } - $(this._scrollElement).trigger(EVENT_ACTIVATE, { + EventHandler.trigger(this._scrollElement, EVENT_ACTIVATE, { relatedTarget: target }) } _clear() { - [].slice.call(document.querySelectorAll(this._selector)) + SelectorEngine.find(this._selector) .filter(node => node.classList.contains(CLASS_NAME_ACTIVE)) .forEach(node => node.classList.remove(CLASS_NAME_ACTIVE)) } // Static - static _jQueryInterface(config) { + static jQueryInterface(config) { return this.each(function () { - let data = $(this).data(DATA_KEY) - const _config = typeof config === 'object' && config + const data = ScrollSpy.getInstance(this) || new ScrollSpy(this, typeof config === 'object' ? config : {}) - if (!data) { - data = new ScrollSpy(this, _config) - $(this).data(DATA_KEY, data) + if (typeof config !== 'string') { + return } - if (typeof config === 'string') { - if (typeof data[config] === 'undefined') { - throw new TypeError(`No method named "${config}"`) - } - - data[config]() + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`) } + + data[config]() }) } } @@ -298,27 +291,18 @@ class ScrollSpy { * ------------------------------------------------------------------------ */ -$(window).on(EVENT_LOAD_DATA_API, () => { - const scrollSpys = [].slice.call(document.querySelectorAll(SELECTOR_DATA_SPY)) - const scrollSpysLength = scrollSpys.length - - for (let i = scrollSpysLength; i--;) { - const $spy = $(scrollSpys[i]) - ScrollSpy._jQueryInterface.call($spy, $spy.data()) - } +EventHandler.on(window, EVENT_LOAD_DATA_API, () => { + SelectorEngine.find(SELECTOR_DATA_SPY) + .forEach(spy => new ScrollSpy(spy)) }) /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ + * add .ScrollSpy to jQuery only if jQuery is present */ -$.fn[NAME] = ScrollSpy._jQueryInterface -$.fn[NAME].Constructor = ScrollSpy -$.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return ScrollSpy._jQueryInterface -} +defineJQueryPlugin(ScrollSpy) export default ScrollSpy diff --git a/vendor/twbs/bootstrap/js/src/tab.js b/vendor/twbs/bootstrap/js/src/tab.js index e9a6f555f..51deb170f 100644 --- a/vendor/twbs/bootstrap/js/src/tab.js +++ b/vendor/twbs/bootstrap/js/src/tab.js @@ -1,12 +1,20 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): tab.js + * Bootstrap (v5.0.1): tab.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -import $ from 'jquery' -import Util from './util' +import { + defineJQueryPlugin, + getElementFromSelector, + isDisabled, + reflow +} from './util/index' +import Data from './dom/data' +import EventHandler from './dom/event-handler' +import SelectorEngine from './dom/selector-engine' +import BaseComponent from './base-component' /** * ------------------------------------------------------------------------ @@ -15,11 +23,9 @@ import Util from './util' */ const NAME = 'tab' -const VERSION = '4.6.0' const DATA_KEY = 'bs.tab' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' -const JQUERY_NO_CONFLICT = $.fn[NAME] const EVENT_HIDE = `hide${EVENT_KEY}` const EVENT_HIDDEN = `hidden${EVENT_KEY}` @@ -29,17 +35,16 @@ const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}` const CLASS_NAME_DROPDOWN_MENU = 'dropdown-menu' const CLASS_NAME_ACTIVE = 'active' -const CLASS_NAME_DISABLED = 'disabled' 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 = '> li > .active' -const SELECTOR_DATA_TOGGLE = '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]' +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 = '> .dropdown-menu .active' +const SELECTOR_DROPDOWN_ACTIVE_CHILD = ':scope > .dropdown-menu .active' /** * ------------------------------------------------------------------------ @@ -47,77 +52,55 @@ const SELECTOR_DROPDOWN_ACTIVE_CHILD = '> .dropdown-menu .active' * ------------------------------------------------------------------------ */ -class Tab { - constructor(element) { - this._element = element - } - +class Tab extends BaseComponent { // Getters - static get VERSION() { - return VERSION + static get NAME() { + return NAME } // Public show() { - if (this._element.parentNode && - this._element.parentNode.nodeType === Node.ELEMENT_NODE && - $(this._element).hasClass(CLASS_NAME_ACTIVE) || - $(this._element).hasClass(CLASS_NAME_DISABLED)) { + if ((this._element.parentNode && + this._element.parentNode.nodeType === Node.ELEMENT_NODE && + this._element.classList.contains(CLASS_NAME_ACTIVE))) { return } - let target let previous - const listElement = $(this._element).closest(SELECTOR_NAV_LIST_GROUP)[0] - const selector = Util.getSelectorFromElement(this._element) + 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 = $.makeArray($(listElement).find(itemSelector)) + previous = SelectorEngine.find(itemSelector, listElement) previous = previous[previous.length - 1] } - const hideEvent = $.Event(EVENT_HIDE, { - relatedTarget: this._element - }) + const hideEvent = previous ? + EventHandler.trigger(previous, EVENT_HIDE, { + relatedTarget: this._element + }) : + null - const showEvent = $.Event(EVENT_SHOW, { + const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, { relatedTarget: previous }) - if (previous) { - $(previous).trigger(hideEvent) - } - - $(this._element).trigger(showEvent) - - if (showEvent.isDefaultPrevented() || - hideEvent.isDefaultPrevented()) { + if (showEvent.defaultPrevented || (hideEvent !== null && hideEvent.defaultPrevented)) { return } - if (selector) { - target = document.querySelector(selector) - } - - this._activate( - this._element, - listElement - ) + this._activate(this._element, listElement) const complete = () => { - const hiddenEvent = $.Event(EVENT_HIDDEN, { + EventHandler.trigger(previous, EVENT_HIDDEN, { relatedTarget: this._element }) - - const shownEvent = $.Event(EVENT_SHOWN, { + EventHandler.trigger(this._element, EVENT_SHOWN, { relatedTarget: previous }) - - $(previous).trigger(hiddenEvent) - $(this._element).trigger(shownEvent) } if (target) { @@ -127,33 +110,21 @@ class Tab { } } - dispose() { - $.removeData(this._element, DATA_KEY) - this._element = null - } - // Private _activate(element, container, callback) { const activeElements = container && (container.nodeName === 'UL' || container.nodeName === 'OL') ? - $(container).find(SELECTOR_ACTIVE_UL) : - $(container).children(SELECTOR_ACTIVE) + SelectorEngine.find(SELECTOR_ACTIVE_UL, container) : + SelectorEngine.children(container, SELECTOR_ACTIVE) const active = activeElements[0] - const isTransitioning = callback && (active && $(active).hasClass(CLASS_NAME_FADE)) - const complete = () => this._transitionComplete( - element, - active, - callback - ) + const isTransitioning = callback && (active && active.classList.contains(CLASS_NAME_FADE)) - if (active && isTransitioning) { - const transitionDuration = Util.getTransitionDurationFromElement(active) + const complete = () => this._transitionComplete(element, active, callback) - $(active) - .removeClass(CLASS_NAME_SHOW) - .one(Util.TRANSITION_END, complete) - .emulateTransitionEnd(transitionDuration) + if (active && isTransitioning) { + active.classList.remove(CLASS_NAME_SHOW) + this._queueCallback(complete, element, true) } else { complete() } @@ -161,14 +132,12 @@ class Tab { _transitionComplete(element, active, callback) { if (active) { - $(active).removeClass(CLASS_NAME_ACTIVE) + active.classList.remove(CLASS_NAME_ACTIVE) - const dropdownChild = $(active.parentNode).find( - SELECTOR_DROPDOWN_ACTIVE_CHILD - )[0] + const dropdownChild = SelectorEngine.findOne(SELECTOR_DROPDOWN_ACTIVE_CHILD, active.parentNode) if (dropdownChild) { - $(dropdownChild).removeClass(CLASS_NAME_ACTIVE) + dropdownChild.classList.remove(CLASS_NAME_ACTIVE) } if (active.getAttribute('role') === 'tab') { @@ -176,24 +145,28 @@ class Tab { } } - $(element).addClass(CLASS_NAME_ACTIVE) + element.classList.add(CLASS_NAME_ACTIVE) if (element.getAttribute('role') === 'tab') { element.setAttribute('aria-selected', true) } - Util.reflow(element) + reflow(element) if (element.classList.contains(CLASS_NAME_FADE)) { element.classList.add(CLASS_NAME_SHOW) } - if (element.parentNode && $(element.parentNode).hasClass(CLASS_NAME_DROPDOWN_MENU)) { - const dropdownElement = $(element).closest(SELECTOR_DROPDOWN)[0] + let parent = element.parentNode + if (parent && parent.nodeName === 'LI') { + parent = parent.parentNode + } + + if (parent && parent.classList.contains(CLASS_NAME_DROPDOWN_MENU)) { + const dropdownElement = element.closest(SELECTOR_DROPDOWN) if (dropdownElement) { - const dropdownToggleList = [].slice.call(dropdownElement.querySelectorAll(SELECTOR_DROPDOWN_TOGGLE)) - - $(dropdownToggleList).addClass(CLASS_NAME_ACTIVE) + SelectorEngine.find(SELECTOR_DROPDOWN_TOGGLE, dropdownElement) + .forEach(dropdown => dropdown.classList.add(CLASS_NAME_ACTIVE)) } element.setAttribute('aria-expanded', true) @@ -206,15 +179,9 @@ class Tab { // Static - static _jQueryInterface(config) { + static jQueryInterface(config) { return this.each(function () { - const $this = $(this) - let data = $this.data(DATA_KEY) - - if (!data) { - data = new Tab(this) - $this.data(DATA_KEY, data) - } + const data = Data.get(this, DATA_KEY) || new Tab(this) if (typeof config === 'string') { if (typeof data[config] === 'undefined') { @@ -233,23 +200,26 @@ class Tab { * ------------------------------------------------------------------------ */ -$(document) - .on(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() - Tab._jQueryInterface.call($(this), 'show') - }) + } + + if (isDisabled(this)) { + return + } + + const data = Data.get(this, DATA_KEY) || new Tab(this) + data.show() +}) /** * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ + * add .Tab to jQuery only if jQuery is present */ -$.fn[NAME] = Tab._jQueryInterface -$.fn[NAME].Constructor = Tab -$.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Tab._jQueryInterface -} +defineJQueryPlugin(Tab) export default Tab diff --git a/vendor/twbs/bootstrap/js/src/toast.js b/vendor/twbs/bootstrap/js/src/toast.js index 0c2186908..40cd40d9a 100644 --- a/vendor/twbs/bootstrap/js/src/toast.js +++ b/vendor/twbs/bootstrap/js/src/toast.js @@ -1,12 +1,19 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): toast.js + * Bootstrap (v5.0.1): toast.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ -import $ from 'jquery' -import Util from './util' +import { + defineJQueryPlugin, + reflow, + typeCheckConfig +} from './util/index' +import Data from './dom/data' +import EventHandler from './dom/event-handler' +import Manipulator from './dom/manipulator' +import BaseComponent from './base-component' /** * ------------------------------------------------------------------------ @@ -15,12 +22,14 @@ import Util from './util' */ const NAME = 'toast' -const VERSION = '4.6.0' const DATA_KEY = 'bs.toast' const EVENT_KEY = `.${DATA_KEY}` -const JQUERY_NO_CONFLICT = $.fn[NAME] const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}` +const EVENT_MOUSEOVER = `mouseover${EVENT_KEY}` +const EVENT_MOUSEOUT = `mouseout${EVENT_KEY}` +const EVENT_FOCUSIN = `focusin${EVENT_KEY}` +const EVENT_FOCUSOUT = `focusout${EVENT_KEY}` const EVENT_HIDE = `hide${EVENT_KEY}` const EVENT_HIDDEN = `hidden${EVENT_KEY}` const EVENT_SHOW = `show${EVENT_KEY}` @@ -40,10 +49,10 @@ const DefaultType = { const Default = { animation: true, autohide: true, - delay: 500 + delay: 5000 } -const SELECTOR_DATA_DISMISS = '[data-dismiss="toast"]' +const SELECTOR_DATA_DISMISS = '[data-bs-dismiss="toast"]' /** * ------------------------------------------------------------------------ @@ -51,20 +60,19 @@ const SELECTOR_DATA_DISMISS = '[data-dismiss="toast"]' * ------------------------------------------------------------------------ */ -class Toast { +class Toast extends BaseComponent { constructor(element, config) { - this._element = element + super(element) + this._config = this._getConfig(config) this._timeout = null + this._hasMouseInteraction = false + this._hasKeyboardInteraction = false this._setListeners() } // Getters - static get VERSION() { - return VERSION - } - static get DefaultType() { return DefaultType } @@ -73,13 +81,16 @@ class Toast { return Default } + static get NAME() { + return NAME + } + // Public show() { - const showEvent = $.Event(EVENT_SHOW) + const showEvent = EventHandler.trigger(this._element, EVENT_SHOW) - $(this._element).trigger(showEvent) - if (showEvent.isDefaultPrevented()) { + if (showEvent.defaultPrevented) { return } @@ -93,27 +104,16 @@ class Toast { this._element.classList.remove(CLASS_NAME_SHOWING) this._element.classList.add(CLASS_NAME_SHOW) - $(this._element).trigger(EVENT_SHOWN) + EventHandler.trigger(this._element, EVENT_SHOWN) - if (this._config.autohide) { - this._timeout = setTimeout(() => { - this.hide() - }, this._config.delay) - } + this._maybeScheduleHide() } this._element.classList.remove(CLASS_NAME_HIDE) - Util.reflow(this._element) + reflow(this._element) this._element.classList.add(CLASS_NAME_SHOWING) - if (this._config.animation) { - const transitionDuration = Util.getTransitionDurationFromElement(this._element) - $(this._element) - .one(Util.TRANSITION_END, complete) - .emulateTransitionEnd(transitionDuration) - } else { - complete() - } + this._queueCallback(complete, this._element, this._config.animation) } hide() { @@ -121,14 +121,19 @@ class Toast { return } - const hideEvent = $.Event(EVENT_HIDE) + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE) - $(this._element).trigger(hideEvent) - if (hideEvent.isDefaultPrevented()) { + if (hideEvent.defaultPrevented) { return } - this._close() + const complete = () => { + this._element.classList.add(CLASS_NAME_HIDE) + EventHandler.trigger(this._element, EVENT_HIDDEN) + } + + this._element.classList.remove(CLASS_NAME_SHOW) + this._queueCallback(complete, this._element, this._config.animation) } dispose() { @@ -138,11 +143,7 @@ class Toast { this._element.classList.remove(CLASS_NAME_SHOW) } - $(this._element).off(EVENT_CLICK_DISMISS) - - $.removeData(this._element, DATA_KEY) - this._element = null - this._config = null + super.dispose() } // Private @@ -150,39 +151,62 @@ class Toast { _getConfig(config) { config = { ...Default, - ...$(this._element).data(), + ...Manipulator.getDataAttributes(this._element), ...(typeof config === 'object' && config ? config : {}) } - Util.typeCheckConfig( - NAME, - config, - this.constructor.DefaultType - ) + typeCheckConfig(NAME, config, this.constructor.DefaultType) return config } - _setListeners() { - $(this._element).on(EVENT_CLICK_DISMISS, SELECTOR_DATA_DISMISS, () => this.hide()) + _maybeScheduleHide() { + if (!this._config.autohide) { + return + } + + if (this._hasMouseInteraction || this._hasKeyboardInteraction) { + return + } + + this._timeout = setTimeout(() => { + this.hide() + }, this._config.delay) } - _close() { - const complete = () => { - this._element.classList.add(CLASS_NAME_HIDE) - $(this._element).trigger(EVENT_HIDDEN) + _onInteraction(event, isInteracting) { + switch (event.type) { + case 'mouseover': + case 'mouseout': + this._hasMouseInteraction = isInteracting + break + case 'focusin': + case 'focusout': + this._hasKeyboardInteraction = isInteracting + break + default: + break } - this._element.classList.remove(CLASS_NAME_SHOW) - if (this._config.animation) { - const transitionDuration = Util.getTransitionDurationFromElement(this._element) + if (isInteracting) { + this._clearTimeout() + return + } - $(this._element) - .one(Util.TRANSITION_END, complete) - .emulateTransitionEnd(transitionDuration) - } else { - complete() + const nextElement = event.relatedTarget + if (this._element === nextElement || this._element.contains(nextElement)) { + return } + + this._maybeScheduleHide() + } + + _setListeners() { + EventHandler.on(this._element, EVENT_CLICK_DISMISS, SELECTOR_DATA_DISMISS, () => this.hide()) + 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() { @@ -192,15 +216,13 @@ class Toast { // Static - static _jQueryInterface(config) { + static jQueryInterface(config) { return this.each(function () { - const $element = $(this) - let data = $element.data(DATA_KEY) + let data = Data.get(this, DATA_KEY) const _config = typeof config === 'object' && config if (!data) { data = new Toast(this, _config) - $element.data(DATA_KEY, data) } if (typeof config === 'string') { @@ -218,13 +240,9 @@ class Toast { * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ + * add .Toast to jQuery only if jQuery is present */ -$.fn[NAME] = Toast._jQueryInterface -$.fn[NAME].Constructor = Toast -$.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Toast._jQueryInterface -} +defineJQueryPlugin(Toast) export default Toast diff --git a/vendor/twbs/bootstrap/js/src/tools/sanitizer.js b/vendor/twbs/bootstrap/js/src/tools/sanitizer.js deleted file mode 100644 index 45fd6106c..000000000 --- a/vendor/twbs/bootstrap/js/src/tools/sanitizer.js +++ /dev/null @@ -1,127 +0,0 @@ -/** - * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): tools/sanitizer.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - -const uriAttrs = [ - 'background', - 'cite', - 'href', - 'itemtype', - 'longdesc', - 'poster', - 'src', - 'xlink:href' -] - -const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i - -export const DefaultWhitelist = { - // 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: [] -} - -/** - * A pattern that recognizes a commonly useful subset of URLs that are safe. - * - * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/packages/core/src/sanitization/url_sanitizer.ts - */ -const SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^#&/:?]*(?:[#/?]|$))/gi - -/** - * A pattern that matches safe data URLs. Only matches image, video and audio types. - * - * Shoutout to Angular 7 https://github.com/angular/angular/blob/7.2.4/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 - -function allowedAttribute(attr, allowedAttributeList) { - const attrName = attr.nodeName.toLowerCase() - - if (allowedAttributeList.indexOf(attrName) !== -1) { - if (uriAttrs.indexOf(attrName) !== -1) { - return Boolean(attr.nodeValue.match(SAFE_URL_PATTERN) || attr.nodeValue.match(DATA_URL_PATTERN)) - } - - return true - } - - const regExp = allowedAttributeList.filter(attrRegex => attrRegex instanceof RegExp) - - // Check if a regular expression validates the attribute. - for (let i = 0, len = regExp.length; i < len; i++) { - if (attrName.match(regExp[i])) { - return true - } - } - - return false -} - -export function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) { - if (unsafeHtml.length === 0) { - return unsafeHtml - } - - if (sanitizeFn && typeof sanitizeFn === 'function') { - return sanitizeFn(unsafeHtml) - } - - const domParser = new window.DOMParser() - const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html') - const whitelistKeys = Object.keys(whiteList) - const elements = [].slice.call(createdDocument.body.querySelectorAll('*')) - - for (let i = 0, len = elements.length; i < len; i++) { - const el = elements[i] - const elName = el.nodeName.toLowerCase() - - if (whitelistKeys.indexOf(el.nodeName.toLowerCase()) === -1) { - el.parentNode.removeChild(el) - - continue - } - - const attributeList = [].slice.call(el.attributes) - const whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || []) - - attributeList.forEach(attr => { - if (!allowedAttribute(attr, whitelistedAttributes)) { - el.removeAttribute(attr.nodeName) - } - }) - } - - return createdDocument.body.innerHTML -} diff --git a/vendor/twbs/bootstrap/js/src/tooltip.js b/vendor/twbs/bootstrap/js/src/tooltip.js index fd6ceea67..d164da2b3 100644 --- a/vendor/twbs/bootstrap/js/src/tooltip.js +++ b/vendor/twbs/bootstrap/js/src/tooltip.js @@ -1,17 +1,31 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): tooltip.js + * Bootstrap (v5.0.1): tooltip.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) * -------------------------------------------------------------------------- */ +import * as Popper from '@popperjs/core' + +import { + defineJQueryPlugin, + findShadowRoot, + getElement, + getUID, + isElement, + isRTL, + noop, + typeCheckConfig +} from './util/index' import { - DefaultWhitelist, + DefaultAllowlist, sanitizeHtml -} from './tools/sanitizer' -import $ from 'jquery' -import Popper from 'popper.js' -import Util from './util' +} from './util/sanitizer' +import Data from './dom/data' +import EventHandler from './dom/event-handler' +import Manipulator from './dom/manipulator' +import SelectorEngine from './dom/selector-engine' +import BaseComponent from './base-component' /** * ------------------------------------------------------------------------ @@ -20,13 +34,11 @@ import Util from './util' */ const NAME = 'tooltip' -const VERSION = '4.6.0' const DATA_KEY = 'bs.tooltip' const EVENT_KEY = `.${DATA_KEY}` -const JQUERY_NO_CONFLICT = $.fn[NAME] const CLASS_PREFIX = 'bs-tooltip' const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g') -const DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn'] +const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']) const DefaultType = { animation: 'boolean', @@ -37,50 +49,48 @@ const DefaultType = { html: 'boolean', selector: '(string|boolean)', placement: '(string|function)', - offset: '(number|string|function)', + offset: '(array|string|function)', container: '(string|element|boolean)', - fallbackPlacement: '(string|array)', + fallbackPlacements: 'array', boundary: '(string|element)', customClass: '(string|function)', sanitize: 'boolean', sanitizeFn: '(null|function)', - whiteList: 'object', - popperConfig: '(null|object)' + allowList: 'object', + popperConfig: '(null|object|function)' } const AttachmentMap = { AUTO: 'auto', TOP: 'top', - RIGHT: 'right', + RIGHT: isRTL() ? 'left' : 'right', BOTTOM: 'bottom', - LEFT: 'left' + LEFT: isRTL() ? 'right' : 'left' } const Default = { animation: true, template: '<div class="tooltip" role="tooltip">' + - '<div class="arrow"></div>' + - '<div class="tooltip-inner"></div></div>', + '<div class="tooltip-arrow"></div>' + + '<div class="tooltip-inner"></div>' + + '</div>', trigger: 'hover focus', title: '', delay: 0, html: false, selector: false, placement: 'top', - offset: 0, + offset: [0, 0], container: false, - fallbackPlacement: 'flip', - boundary: 'scrollParent', + fallbackPlacements: ['top', 'right', 'bottom', 'left'], + boundary: 'clippingParents', customClass: '', sanitize: true, sanitizeFn: null, - whiteList: DefaultWhitelist, + allowList: DefaultAllowlist, popperConfig: null } -const HOVER_STATE_SHOW = 'show' -const HOVER_STATE_OUT = 'out' - const Event = { HIDE: `hide${EVENT_KEY}`, HIDDEN: `hidden${EVENT_KEY}`, @@ -95,10 +105,13 @@ const Event = { } 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_ARROW = '.arrow' const TRIGGER_HOVER = 'hover' const TRIGGER_FOCUS = 'focus' @@ -111,12 +124,14 @@ const TRIGGER_MANUAL = 'manual' * ------------------------------------------------------------------------ */ -class Tooltip { +class Tooltip extends BaseComponent { constructor(element, config) { if (typeof Popper === 'undefined') { throw new TypeError('Bootstrap\'s tooltips require Popper (https://popper.js.org)') } + super(element) + // private this._isEnabled = true this._timeout = 0 @@ -125,8 +140,7 @@ class Tooltip { this._popper = null // Protected - this.element = element - this.config = this._getConfig(config) + this._config = this._getConfig(config) this.tip = null this._setListeners() @@ -134,10 +148,6 @@ class Tooltip { // Getters - static get VERSION() { - return VERSION - } - static get Default() { return Default } @@ -146,18 +156,10 @@ class Tooltip { return NAME } - static get DATA_KEY() { - return DATA_KEY - } - static get Event() { return Event } - static get EVENT_KEY() { - return EVENT_KEY - } - static get DefaultType() { return DefaultType } @@ -182,16 +184,7 @@ class Tooltip { } if (event) { - const dataKey = this.constructor.DATA_KEY - let context = $(event.currentTarget).data(dataKey) - - if (!context) { - context = new this.constructor( - event.currentTarget, - this._getDelegateConfig() - ) - $(event.currentTarget).data(dataKey, context) - } + const context = this._initializeOnDelegatedTarget(event) context._activeTrigger.click = !context._activeTrigger.click @@ -201,7 +194,7 @@ class Tooltip { context._leave(null, context) } } else { - if ($(this.getTipElement()).hasClass(CLASS_NAME_SHOW)) { + if (this.getTipElement().classList.contains(CLASS_NAME_SHOW)) { this._leave(null, this) return } @@ -213,170 +206,154 @@ class Tooltip { dispose() { clearTimeout(this._timeout) - $.removeData(this.element, this.constructor.DATA_KEY) + EventHandler.off(this._element.closest(`.${CLASS_NAME_MODAL}`), 'hide.bs.modal', this._hideModalHandler) - $(this.element).off(this.constructor.EVENT_KEY) - $(this.element).closest('.modal').off('hide.bs.modal', this._hideModalHandler) - - if (this.tip) { - $(this.tip).remove() + if (this.tip && this.tip.parentNode) { + this.tip.parentNode.removeChild(this.tip) } - this._isEnabled = null - this._timeout = null - this._hoverState = null - this._activeTrigger = null if (this._popper) { this._popper.destroy() } - this._popper = null - this.element = null - this.config = null - this.tip = null + super.dispose() } show() { - if ($(this.element).css('display') === 'none') { + if (this._element.style.display === 'none') { throw new Error('Please use show on visible elements') } - const showEvent = $.Event(this.constructor.Event.SHOW) - if (this.isWithContent() && this._isEnabled) { - $(this.element).trigger(showEvent) - - const shadowRoot = Util.findShadowRoot(this.element) - const isInTheDom = $.contains( - shadowRoot !== null ? shadowRoot : this.element.ownerDocument.documentElement, - this.element - ) + if (!(this.isWithContent() && this._isEnabled)) { + return + } - if (showEvent.isDefaultPrevented() || !isInTheDom) { - return - } + const showEvent = EventHandler.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 tip = this.getTipElement() - const tipId = Util.getUID(this.constructor.NAME) + if (showEvent.defaultPrevented || !isInTheDom) { + return + } - tip.setAttribute('id', tipId) - this.element.setAttribute('aria-describedby', tipId) + const tip = this.getTipElement() + const tipId = getUID(this.constructor.NAME) - this.setContent() + tip.setAttribute('id', tipId) + this._element.setAttribute('aria-describedby', tipId) - if (this.config.animation) { - $(tip).addClass(CLASS_NAME_FADE) - } + this.setContent() - const placement = typeof this.config.placement === 'function' ? - this.config.placement.call(this, tip, this.element) : - this.config.placement + if (this._config.animation) { + tip.classList.add(CLASS_NAME_FADE) + } - const attachment = this._getAttachment(placement) - this.addAttachmentClass(attachment) + const placement = typeof this._config.placement === 'function' ? + this._config.placement.call(this, tip, this._element) : + this._config.placement - const container = this._getContainer() - $(tip).data(this.constructor.DATA_KEY, this) + const attachment = this._getAttachment(placement) + this._addAttachmentClass(attachment) - if (!$.contains(this.element.ownerDocument.documentElement, this.tip)) { - $(tip).appendTo(container) - } + const { container } = this._config + Data.set(tip, this.constructor.DATA_KEY, this) - $(this.element).trigger(this.constructor.Event.INSERTED) + if (!this._element.ownerDocument.documentElement.contains(this.tip)) { + container.appendChild(tip) + EventHandler.trigger(this._element, this.constructor.Event.INSERTED) + } - this._popper = new Popper(this.element, tip, this._getPopperConfig(attachment)) + if (this._popper) { + this._popper.update() + } else { + this._popper = Popper.createPopper(this._element, tip, this._getPopperConfig(attachment)) + } - $(tip).addClass(CLASS_NAME_SHOW) - $(tip).addClass(this.config.customClass) + tip.classList.add(CLASS_NAME_SHOW) - // 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) { - $(document.body).children().on('mouseover', null, $.noop) - } + const customClass = typeof this._config.customClass === 'function' ? this._config.customClass() : this._config.customClass + if (customClass) { + tip.classList.add(...customClass.split(' ')) + } - const complete = () => { - if (this.config.animation) { - this._fixTransition() - } + // 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.on(element, 'mouseover', noop) + }) + } - const prevHoverState = this._hoverState - this._hoverState = null + const complete = () => { + const prevHoverState = this._hoverState - $(this.element).trigger(this.constructor.Event.SHOWN) + this._hoverState = null + EventHandler.trigger(this._element, this.constructor.Event.SHOWN) - if (prevHoverState === HOVER_STATE_OUT) { - this._leave(null, this) - } + if (prevHoverState === HOVER_STATE_OUT) { + this._leave(null, this) } + } - if ($(this.tip).hasClass(CLASS_NAME_FADE)) { - const transitionDuration = Util.getTransitionDurationFromElement(this.tip) + const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE) + this._queueCallback(complete, this.tip, isAnimated) + } - $(this.tip) - .one(Util.TRANSITION_END, complete) - .emulateTransitionEnd(transitionDuration) - } else { - complete() - } + hide() { + if (!this._popper) { + return } - } - hide(callback) { const tip = this.getTipElement() - const hideEvent = $.Event(this.constructor.Event.HIDE) const complete = () => { + if (this._isWithActiveTrigger()) { + return + } + if (this._hoverState !== HOVER_STATE_SHOW && tip.parentNode) { tip.parentNode.removeChild(tip) } this._cleanTipClass() - this.element.removeAttribute('aria-describedby') - $(this.element).trigger(this.constructor.Event.HIDDEN) - if (this._popper !== null) { - this._popper.destroy() - } + this._element.removeAttribute('aria-describedby') + EventHandler.trigger(this._element, this.constructor.Event.HIDDEN) - if (callback) { - callback() + if (this._popper) { + this._popper.destroy() + this._popper = null } } - $(this.element).trigger(hideEvent) - - if (hideEvent.isDefaultPrevented()) { + const hideEvent = EventHandler.trigger(this._element, this.constructor.Event.HIDE) + if (hideEvent.defaultPrevented) { return } - $(tip).removeClass(CLASS_NAME_SHOW) + tip.classList.remove(CLASS_NAME_SHOW) // If this is a touch-enabled device we remove the extra // empty mouseover listeners we added for iOS support if ('ontouchstart' in document.documentElement) { - $(document.body).children().off('mouseover', null, $.noop) + [].concat(...document.body.children) + .forEach(element => EventHandler.off(element, 'mouseover', noop)) } this._activeTrigger[TRIGGER_CLICK] = false this._activeTrigger[TRIGGER_FOCUS] = false this._activeTrigger[TRIGGER_HOVER] = false - if ($(this.tip).hasClass(CLASS_NAME_FADE)) { - const transitionDuration = Util.getTransitionDurationFromElement(tip) - - $(tip) - .one(Util.TRANSITION_END, complete) - .emulateTransitionEnd(transitionDuration) - } else { - complete() - } - + const isAnimated = this.tip.classList.contains(CLASS_NAME_FADE) + this._queueCallback(complete, this.tip, isAnimated) this._hoverState = '' } update() { if (this._popper !== null) { - this._popper.scheduleUpdate() + this._popper.update() } } @@ -386,118 +363,158 @@ class Tooltip { return Boolean(this.getTitle()) } - addAttachmentClass(attachment) { - $(this.getTipElement()).addClass(`${CLASS_PREFIX}-${attachment}`) - } - getTipElement() { - this.tip = this.tip || $(this.config.template)[0] + if (this.tip) { + return this.tip + } + + const element = document.createElement('div') + element.innerHTML = this._config.template + + this.tip = element.children[0] return this.tip } setContent() { const tip = this.getTipElement() - this.setElementContent($(tip.querySelectorAll(SELECTOR_TOOLTIP_INNER)), this.getTitle()) - $(tip).removeClass(`${CLASS_NAME_FADE} ${CLASS_NAME_SHOW}`) + this.setElementContent(SelectorEngine.findOne(SELECTOR_TOOLTIP_INNER, tip), this.getTitle()) + tip.classList.remove(CLASS_NAME_FADE, CLASS_NAME_SHOW) } - setElementContent($element, content) { - if (typeof content === 'object' && (content.nodeType || content.jquery)) { - // Content is a DOM node or a jQuery - if (this.config.html) { - if (!$(content).parent().is($element)) { - $element.empty().append(content) + setElementContent(element, content) { + if (element === null) { + return + } + + 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.appendChild(content) } } else { - $element.text($(content).text()) + element.textContent = content.textContent } return } - if (this.config.html) { - if (this.config.sanitize) { - content = sanitizeHtml(content, this.config.whiteList, this.config.sanitizeFn) + if (this._config.html) { + if (this._config.sanitize) { + content = sanitizeHtml(content, this._config.allowList, this._config.sanitizeFn) } - $element.html(content) + element.innerHTML = content } else { - $element.text(content) + element.textContent = content } } getTitle() { - let title = this.element.getAttribute('data-original-title') + let title = this._element.getAttribute('data-bs-original-title') if (!title) { - title = typeof this.config.title === 'function' ? - this.config.title.call(this.element) : - this.config.title + title = typeof this._config.title === 'function' ? + this._config.title.call(this._element) : + this._config.title } return title } - // Private + updateAttachment(attachment) { + if (attachment === 'right') { + return 'end' + } - _getPopperConfig(attachment) { - const defaultBsConfig = { - placement: attachment, - modifiers: { - offset: this._getOffset(), - flip: { - behavior: this.config.fallbackPlacement - }, - arrow: { - element: SELECTOR_ARROW - }, - preventOverflow: { - boundariesElement: this.config.boundary - } - }, - onCreate: data => { - if (data.originalPlacement !== data.placement) { - this._handlePopperPlacementChange(data) - } - }, - onUpdate: data => this._handlePopperPlacementChange(data) + if (attachment === 'left') { + return 'start' } - return { - ...defaultBsConfig, - ...this.config.popperConfig + return attachment + } + + // Private + + _initializeOnDelegatedTarget(event, context) { + const dataKey = this.constructor.DATA_KEY + context = context || Data.get(event.delegateTarget, dataKey) + + if (!context) { + context = new this.constructor(event.delegateTarget, this._getDelegateConfig()) + Data.set(event.delegateTarget, dataKey, context) } + + return context } _getOffset() { - const offset = {} + const { offset } = this._config - if (typeof this.config.offset === 'function') { - offset.fn = data => { - data.offsets = { - ...data.offsets, - ...(this.config.offset(data.offsets, this.element) || {}) - } + if (typeof offset === 'string') { + return offset.split(',').map(val => Number.parseInt(val, 10)) + } - return data - } - } else { - offset.offset = this.config.offset + if (typeof offset === 'function') { + return popperData => offset(popperData, this._element) } return offset } - _getContainer() { - if (this.config.container === false) { - return document.body + _getPopperConfig(attachment) { + const defaultBsPopperConfig = { + placement: attachment, + modifiers: [ + { + name: 'flip', + options: { + fallbackPlacements: this._config.fallbackPlacements + } + }, + { + name: 'offset', + options: { + offset: this._getOffset() + } + }, + { + name: 'preventOverflow', + options: { + boundary: this._config.boundary + } + }, + { + name: 'arrow', + options: { + element: `.${this.constructor.NAME}-arrow` + } + }, + { + name: 'onChange', + enabled: true, + phase: 'afterWrite', + fn: data => this._handlePopperPlacementChange(data) + } + ], + onFirstUpdate: data => { + if (data.options.placement !== data.placement) { + this._handlePopperPlacementChange(data) + } + } } - if (Util.isElement(this.config.container)) { - return $(this.config.container) + return { + ...defaultBsPopperConfig, + ...(typeof this._config.popperConfig === 'function' ? this._config.popperConfig(defaultBsPopperConfig) : this._config.popperConfig) } + } - return $(document).find(this.config.container) + _addAttachmentClass(attachment) { + this.getTipElement().classList.add(`${CLASS_PREFIX}-${this.updateAttachment(attachment)}`) } _getAttachment(placement) { @@ -505,15 +522,11 @@ class Tooltip { } _setListeners() { - const triggers = this.config.trigger.split(' ') + const triggers = this._config.trigger.split(' ') triggers.forEach(trigger => { if (trigger === 'click') { - $(this.element).on( - this.constructor.Event.CLICK, - this.config.selector, - event => this.toggle(event) - ) + EventHandler.on(this._element, this.constructor.Event.CLICK, this._config.selector, event => this.toggle(event)) } else if (trigger !== TRIGGER_MANUAL) { const eventIn = trigger === TRIGGER_HOVER ? this.constructor.Event.MOUSEENTER : @@ -522,23 +535,22 @@ class Tooltip { this.constructor.Event.MOUSELEAVE : this.constructor.Event.FOCUSOUT - $(this.element) - .on(eventIn, this.config.selector, event => this._enter(event)) - .on(eventOut, this.config.selector, event => this._leave(event)) + 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._hideModalHandler = () => { - if (this.element) { + if (this._element) { this.hide() } } - $(this.element).closest('.modal').on('hide.bs.modal', this._hideModalHandler) + EventHandler.on(this._element.closest(`.${CLASS_NAME_MODAL}`), 'hide.bs.modal', this._hideModalHandler) - if (this.config.selector) { - this.config = { - ...this.config, + if (this._config.selector) { + this._config = { + ...this._config, trigger: 'manual', selector: '' } @@ -548,29 +560,21 @@ class Tooltip { } _fixTitle() { - const titleType = typeof this.element.getAttribute('data-original-title') + const title = this._element.getAttribute('title') + const originalTitleType = typeof this._element.getAttribute('data-bs-original-title') - if (this.element.getAttribute('title') || titleType !== 'string') { - this.element.setAttribute( - 'data-original-title', - this.element.getAttribute('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', '') + this._element.setAttribute('title', '') } } _enter(event, context) { - const dataKey = this.constructor.DATA_KEY - context = context || $(event.currentTarget).data(dataKey) - - if (!context) { - context = new this.constructor( - event.currentTarget, - this._getDelegateConfig() - ) - $(event.currentTarget).data(dataKey, context) - } + context = this._initializeOnDelegatedTarget(event, context) if (event) { context._activeTrigger[ @@ -578,7 +582,7 @@ class Tooltip { ] = true } - if ($(context.getTipElement()).hasClass(CLASS_NAME_SHOW) || context._hoverState === HOVER_STATE_SHOW) { + if (context.getTipElement().classList.contains(CLASS_NAME_SHOW) || context._hoverState === HOVER_STATE_SHOW) { context._hoverState = HOVER_STATE_SHOW return } @@ -587,7 +591,7 @@ class Tooltip { context._hoverState = HOVER_STATE_SHOW - if (!context.config.delay || !context.config.delay.show) { + if (!context._config.delay || !context._config.delay.show) { context.show() return } @@ -596,25 +600,16 @@ class Tooltip { if (context._hoverState === HOVER_STATE_SHOW) { context.show() } - }, context.config.delay.show) + }, context._config.delay.show) } _leave(event, context) { - const dataKey = this.constructor.DATA_KEY - context = context || $(event.currentTarget).data(dataKey) - - if (!context) { - context = new this.constructor( - event.currentTarget, - this._getDelegateConfig() - ) - $(event.currentTarget).data(dataKey, context) - } + context = this._initializeOnDelegatedTarget(event, context) if (event) { context._activeTrigger[ event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER - ] = false + ] = context._element.contains(event.relatedTarget) } if (context._isWithActiveTrigger()) { @@ -625,7 +620,7 @@ class Tooltip { context._hoverState = HOVER_STATE_OUT - if (!context.config.delay || !context.config.delay.hide) { + if (!context._config.delay || !context._config.delay.hide) { context.hide() return } @@ -634,7 +629,7 @@ class Tooltip { if (context._hoverState === HOVER_STATE_OUT) { context.hide() } - }, context.config.delay.hide) + }, context._config.delay.hide) } _isWithActiveTrigger() { @@ -648,14 +643,13 @@ class Tooltip { } _getConfig(config) { - const dataAttributes = $(this.element).data() + const dataAttributes = Manipulator.getDataAttributes(this._element) - Object.keys(dataAttributes) - .forEach(dataAttr => { - if (DISALLOWED_ATTRIBUTES.indexOf(dataAttr) !== -1) { - delete dataAttributes[dataAttr] - } - }) + Object.keys(dataAttributes).forEach(dataAttr => { + if (DISALLOWED_ATTRIBUTES.has(dataAttr)) { + delete dataAttributes[dataAttr] + } + }) config = { ...this.constructor.Default, @@ -663,6 +657,8 @@ class Tooltip { ...(typeof config === 'object' && config ? config : {}) } + config.container = config.container === false ? document.body : getElement(config.container) + if (typeof config.delay === 'number') { config.delay = { show: config.delay, @@ -678,14 +674,10 @@ class Tooltip { config.content = config.content.toString() } - Util.typeCheckConfig( - NAME, - config, - this.constructor.DefaultType - ) + typeCheckConfig(NAME, config, this.constructor.DefaultType) if (config.sanitize) { - config.template = sanitizeHtml(config.template, config.whiteList, config.sanitizeFn) + config.template = sanitizeHtml(config.template, config.allowList, config.sanitizeFn) } return config @@ -694,10 +686,10 @@ class Tooltip { _getDelegateConfig() { const config = {} - if (this.config) { - for (const key in this.config) { - if (this.constructor.Default[key] !== this.config[key]) { - config[key] = this.config[key] + if (this._config) { + for (const key in this._config) { + if (this.constructor.Default[key] !== this._config[key]) { + config[key] = this._config[key] } } } @@ -706,40 +698,31 @@ class Tooltip { } _cleanTipClass() { - const $tip = $(this.getTipElement()) - const tabClass = $tip.attr('class').match(BSCLS_PREFIX_REGEX) - if (tabClass !== null && tabClass.length) { - $tip.removeClass(tabClass.join('')) + const tip = this.getTipElement() + const tabClass = tip.getAttribute('class').match(BSCLS_PREFIX_REGEX) + if (tabClass !== null && tabClass.length > 0) { + tabClass.map(token => token.trim()) + .forEach(tClass => tip.classList.remove(tClass)) } } _handlePopperPlacementChange(popperData) { - this.tip = popperData.instance.popper - this._cleanTipClass() - this.addAttachmentClass(this._getAttachment(popperData.placement)) - } + const { state } = popperData - _fixTransition() { - const tip = this.getTipElement() - const initConfigAnimation = this.config.animation - - if (tip.getAttribute('x-placement') !== null) { + if (!state) { return } - $(tip).removeClass(CLASS_NAME_FADE) - this.config.animation = false - this.hide() - this.show() - this.config.animation = initConfigAnimation + this.tip = state.elements.popper + this._cleanTipClass() + this._addAttachmentClass(this._getAttachment(state.placement)) } // Static - static _jQueryInterface(config) { + static jQueryInterface(config) { return this.each(function () { - const $element = $(this) - let data = $element.data(DATA_KEY) + let data = Data.get(this, DATA_KEY) const _config = typeof config === 'object' && config if (!data && /dispose|hide/.test(config)) { @@ -748,7 +731,6 @@ class Tooltip { if (!data) { data = new Tooltip(this, _config) - $element.data(DATA_KEY, data) } if (typeof config === 'string') { @@ -766,13 +748,9 @@ class Tooltip { * ------------------------------------------------------------------------ * jQuery * ------------------------------------------------------------------------ + * add .Tooltip to jQuery only if jQuery is present */ -$.fn[NAME] = Tooltip._jQueryInterface -$.fn[NAME].Constructor = Tooltip -$.fn[NAME].noConflict = () => { - $.fn[NAME] = JQUERY_NO_CONFLICT - return Tooltip._jQueryInterface -} +defineJQueryPlugin(Tooltip) export default Tooltip diff --git a/vendor/twbs/bootstrap/js/src/util.js b/vendor/twbs/bootstrap/js/src/util.js deleted file mode 100644 index cd9d5e3b8..000000000 --- a/vendor/twbs/bootstrap/js/src/util.js +++ /dev/null @@ -1,198 +0,0 @@ -/** - * -------------------------------------------------------------------------- - * Bootstrap (v4.6.0): util.js - * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) - * -------------------------------------------------------------------------- - */ - -import $ from 'jquery' - -/** - * ------------------------------------------------------------------------ - * Private TransitionEnd Helpers - * ------------------------------------------------------------------------ - */ - -const TRANSITION_END = 'transitionend' -const MAX_UID = 1000000 -const MILLISECONDS_MULTIPLIER = 1000 - -// Shoutout AngusCroll (https://goo.gl/pxwQGp) -function toType(obj) { - if (obj === null || typeof obj === 'undefined') { - return `${obj}` - } - - return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase() -} - -function getSpecialTransitionEndEvent() { - return { - bindType: TRANSITION_END, - delegateType: TRANSITION_END, - handle(event) { - if ($(event.target).is(this)) { - return event.handleObj.handler.apply(this, arguments) // eslint-disable-line prefer-rest-params - } - - return undefined - } - } -} - -function transitionEndEmulator(duration) { - let called = false - - $(this).one(Util.TRANSITION_END, () => { - called = true - }) - - setTimeout(() => { - if (!called) { - Util.triggerTransitionEnd(this) - } - }, duration) - - return this -} - -function setTransitionEndSupport() { - $.fn.emulateTransitionEnd = transitionEndEmulator - $.event.special[Util.TRANSITION_END] = getSpecialTransitionEndEvent() -} - -/** - * -------------------------------------------------------------------------- - * Public Util Api - * -------------------------------------------------------------------------- - */ - -const Util = { - TRANSITION_END: 'bsTransitionEnd', - - getUID(prefix) { - do { - prefix += ~~(Math.random() * MAX_UID) // "~~" acts like a faster Math.floor() here - } while (document.getElementById(prefix)) - - return prefix - }, - - getSelectorFromElement(element) { - let selector = element.getAttribute('data-target') - - if (!selector || selector === '#') { - const hrefAttr = element.getAttribute('href') - selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : '' - } - - try { - return document.querySelector(selector) ? selector : null - } catch (_) { - return null - } - }, - - getTransitionDurationFromElement(element) { - if (!element) { - return 0 - } - - // Get transition-duration of the element - let transitionDuration = $(element).css('transition-duration') - let transitionDelay = $(element).css('transition-delay') - - const floatTransitionDuration = parseFloat(transitionDuration) - const floatTransitionDelay = 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 (parseFloat(transitionDuration) + parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER - }, - - reflow(element) { - return element.offsetHeight - }, - - triggerTransitionEnd(element) { - $(element).trigger(TRANSITION_END) - }, - - supportsTransitionEnd() { - return Boolean(TRANSITION_END) - }, - - isElement(obj) { - return (obj[0] || obj).nodeType - }, - - typeCheckConfig(componentName, config, configTypes) { - for (const property in configTypes) { - if (Object.prototype.hasOwnProperty.call(configTypes, property)) { - const expectedTypes = configTypes[property] - const value = config[property] - const valueType = value && Util.isElement(value) ? - 'element' : toType(value) - - if (!new RegExp(expectedTypes).test(valueType)) { - throw new Error( - `${componentName.toUpperCase()}: ` + - `Option "${property}" provided type "${valueType}" ` + - `but expected type "${expectedTypes}".`) - } - } - } - }, - - 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 Util.findShadowRoot(element.parentNode) - }, - - jQueryDetection() { - if (typeof $ === 'undefined') { - throw new TypeError('Bootstrap\'s JavaScript requires jQuery. jQuery must be included before Bootstrap\'s JavaScript.') - } - - const version = $.fn.jquery.split(' ')[0].split('.') - const minMajor = 1 - const ltMajor = 2 - const minMinor = 9 - const minPatch = 1 - const maxMajor = 4 - - if (version[0] < ltMajor && version[1] < minMinor || version[0] === minMajor && version[1] === minMinor && version[2] < minPatch || version[0] >= maxMajor) { - throw new Error('Bootstrap\'s JavaScript requires at least jQuery v1.9.1 but less than v4.0.0') - } - } -} - -Util.jQueryDetection() -setTransitionEndSupport() - -export default Util |