diff options
author | BlackDex <[email protected]> | 2023-07-27 22:51:22 +0200 |
---|---|---|
committer | BlackDex <[email protected]> | 2023-08-31 21:14:53 +0200 |
commit | 83d5432cbf89bdf8d6af533ec734785391d61a6b (patch) | |
tree | 3601853be4246d437b129c9cb589a755f25838c6 /src/static | |
parent | f579a4154cd1a016bb4d868926b1e601c2e80eaa (diff) | |
download | vaultwarden-83d5432cbf89bdf8d6af533ec734785391d61a6b.tar.gz vaultwarden-83d5432cbf89bdf8d6af533ec734785391d61a6b.zip |
Update admin interface
- Updated the admin interface dependencies.
- Replace bootstrap-native with bootstrap
- Added auto theme with an option to switch to dark/light
- Some small color changes
- Added an dev only function to always load static files from disk
Diffstat (limited to 'src/static')
-rw-r--r-- | src/static/scripts/admin.js | 85 | ||||
-rw-r--r-- | src/static/scripts/admin_diagnostics.js | 4 | ||||
-rw-r--r-- | src/static/scripts/admin_users.js | 14 | ||||
-rw-r--r-- | src/static/scripts/bootstrap-native.js | 5991 | ||||
-rw-r--r-- | src/static/scripts/bootstrap.bundle.js | 6313 | ||||
-rw-r--r-- | src/static/scripts/bootstrap.css | 2377 | ||||
-rw-r--r-- | src/static/scripts/datatables.css | 47 | ||||
-rw-r--r-- | src/static/scripts/datatables.js | 93 | ||||
-rw-r--r-- | src/static/scripts/jquery-3.7.0.slim.js (renamed from src/static/scripts/jquery-3.6.4.slim.js) | 1837 | ||||
-rw-r--r-- | src/static/templates/admin/base.hbs | 58 | ||||
-rw-r--r-- | src/static/templates/admin/diagnostics.hbs | 6 | ||||
-rw-r--r-- | src/static/templates/admin/login.hbs | 10 | ||||
-rw-r--r-- | src/static/templates/admin/organizations.hbs | 4 | ||||
-rw-r--r-- | src/static/templates/admin/settings.hbs | 6 | ||||
-rw-r--r-- | src/static/templates/admin/users.hbs | 6 |
15 files changed, 9124 insertions, 7727 deletions
diff --git a/src/static/scripts/admin.js b/src/static/scripts/admin.js index a9c19739..b35f3fb1 100644 --- a/src/static/scripts/admin.js +++ b/src/static/scripts/admin.js @@ -37,36 +37,107 @@ function _post(url, successMsg, errMsg, body, reload_page = true) { mode: "same-origin", credentials: "same-origin", headers: { "Content-Type": "application/json" } - }).then( resp => { + }).then(resp => { if (resp.ok) { msg(successMsg, reload_page); // Abuse the catch handler by setting error to false and continue - return Promise.reject({error: false}); + return Promise.reject({ error: false }); } respStatus = resp.status; respStatusText = resp.statusText; return resp.text(); - }).then( respText => { + }).then(respText => { try { const respJson = JSON.parse(respText); if (respJson.ErrorModel && respJson.ErrorModel.Message) { return respJson.ErrorModel.Message; } else { - return Promise.reject({body:`${respStatus} - ${respStatusText}\n\nUnknown error`, error: true}); + return Promise.reject({ body: `${respStatus} - ${respStatusText}\n\nUnknown error`, error: true }); } } catch (e) { - return Promise.reject({body:`${respStatus} - ${respStatusText}\n\n[Catch] ${e}`, error: true}); + return Promise.reject({ body: `${respStatus} - ${respStatusText}\n\n[Catch] ${e}`, error: true }); } - }).then( apiMsg => { + }).then(apiMsg => { msg(`${errMsg}\n${apiMsg}`, reload_page); - }).catch( e => { + }).catch(e => { if (e.error === false) { return true; } else { msg(`${errMsg}\n${e.body}`, reload_page); } }); } +// Bootstrap Theme Selector +const getStoredTheme = () => localStorage.getItem("theme"); +const setStoredTheme = theme => localStorage.setItem("theme", theme); + +const getPreferredTheme = () => { + const storedTheme = getStoredTheme(); + if (storedTheme) { + return storedTheme; + } + + return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"; +}; + +const setTheme = theme => { + if (theme === "auto" && window.matchMedia("(prefers-color-scheme: dark)").matches) { + document.documentElement.setAttribute("data-bs-theme", "dark"); + } else { + document.documentElement.setAttribute("data-bs-theme", theme); + } +}; + +setTheme(getPreferredTheme()); + +const showActiveTheme = (theme, focus = false) => { + const themeSwitcher = document.querySelector("#bd-theme"); + + if (!themeSwitcher) { + return; + } + + const themeSwitcherText = document.querySelector("#bd-theme-text"); + const activeThemeIcon = document.querySelector(".theme-icon-active use"); + const btnToActive = document.querySelector(`[data-bs-theme-value="${theme}"]`); + const svgOfActiveBtn = btnToActive.querySelector("span use").innerText; + + document.querySelectorAll("[data-bs-theme-value]").forEach(element => { + element.classList.remove("active"); + element.setAttribute("aria-pressed", "false"); + }); + + btnToActive.classList.add("active"); + btnToActive.setAttribute("aria-pressed", "true"); + activeThemeIcon.innerText = svgOfActiveBtn; + const themeSwitcherLabel = `${themeSwitcherText.textContent} (${btnToActive.dataset.bsThemeValue})`; + themeSwitcher.setAttribute("aria-label", themeSwitcherLabel); + + if (focus) { + themeSwitcher.focus(); + } +}; + +window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change", () => { + const storedTheme = getStoredTheme(); + if (storedTheme !== "light" && storedTheme !== "dark") { + setTheme(getPreferredTheme()); + } +}); + + // onLoad events document.addEventListener("DOMContentLoaded", (/*event*/) => { + showActiveTheme(getPreferredTheme()); + + document.querySelectorAll("[data-bs-theme-value]") + .forEach(toggle => { + toggle.addEventListener("click", () => { + const theme = toggle.getAttribute("data-bs-theme-value"); + setStoredTheme(theme); + setTheme(theme); + showActiveTheme(theme, true); + }); + }); + // get current URL path and assign "active" class to the correct nav-item const pathname = window.location.pathname; if (pathname === "") return; diff --git a/src/static/scripts/admin_diagnostics.js b/src/static/scripts/admin_diagnostics.js index 5fbed2da..0b1d0622 100644 --- a/src/static/scripts/admin_diagnostics.js +++ b/src/static/scripts/admin_diagnostics.js @@ -1,6 +1,6 @@ "use strict"; /* eslint-env es2017, browser */ -/* global BASE_URL:readable, BSN:readable */ +/* global BASE_URL:readable, bootstrap:readable */ var dnsCheck = false; var timeCheck = false; @@ -135,7 +135,7 @@ function copyToClipboard(event) { document.execCommand("copy"); tmpCopyEl.remove(); - new BSN.Toast("#toastClipboardCopy").show(); + new bootstrap.Toast("#toastClipboardCopy").show(); } function checkTimeDrift(utcTimeA, utcTimeB, statusPrefix) { diff --git a/src/static/scripts/admin_users.js b/src/static/scripts/admin_users.js index a931a4a9..8b569296 100644 --- a/src/static/scripts/admin_users.js +++ b/src/static/scripts/admin_users.js @@ -141,19 +141,20 @@ function resendUserInvite (event) { const ORG_TYPES = { "0": { "name": "Owner", - "color": "orange" + "bg": "orange", + "font": "black" }, "1": { "name": "Admin", - "color": "blueviolet" + "bg": "blueviolet" }, "2": { "name": "User", - "color": "blue" + "bg": "blue" }, "3": { "name": "Manager", - "color": "green" + "bg": "green" }, }; @@ -227,7 +228,10 @@ function initUserTable() { // Color all the org buttons per type document.querySelectorAll("button[data-vw-org-type]").forEach(function(e) { const orgType = ORG_TYPES[e.dataset.vwOrgType]; - e.style.backgroundColor = orgType.color; + e.style.backgroundColor = orgType.bg; + if (orgType.font !== undefined) { + e.style.color = orgType.font; + } e.title = orgType.name; }); diff --git a/src/static/scripts/bootstrap-native.js b/src/static/scripts/bootstrap-native.js deleted file mode 100644 index bf26cef8..00000000 --- a/src/static/scripts/bootstrap-native.js +++ /dev/null @@ -1,5991 +0,0 @@ -/*! - * Native JavaScript for Bootstrap v4.2.0 (https://thednp.github.io/bootstrap.native/) - * Copyright 2015-2022 © dnp_theme - * Licensed under MIT (https://github.com/thednp/bootstrap.native/blob/master/LICENSE) - */ -(function (global, factory) { - typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : - typeof define === 'function' && define.amd ? define(factory) : - (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.BSN = factory()); -})(this, (function () { 'use strict'; - - /** @type {Record<string, any>} */ - const EventRegistry = {}; - - /** - * The global event listener. - * - * @type {EventListener} - * @this {EventTarget} - */ - function globalListener(e) { - const that = this; - const { type } = e; - - [...EventRegistry[type]].forEach((elementsMap) => { - const [element, listenersMap] = elementsMap; - /* istanbul ignore else */ - if (element === that) { - [...listenersMap].forEach((listenerMap) => { - const [listener, options] = listenerMap; - listener.apply(element, [e]); - - if (options && options.once) { - removeListener(element, type, listener, options); - } - }); - } - }); - } - - /** - * Register a new listener with its options and attach the `globalListener` - * to the target if this is the first listener. - * - * @type {Listener.ListenerAction<EventTarget>} - */ - const addListener = (element, eventType, listener, options) => { - // get element listeners first - if (!EventRegistry[eventType]) { - EventRegistry[eventType] = new Map(); - } - const oneEventMap = EventRegistry[eventType]; - - if (!oneEventMap.has(element)) { - oneEventMap.set(element, new Map()); - } - const oneElementMap = oneEventMap.get(element); - - // get listeners size - const { size } = oneElementMap; - - // register listener with its options - oneElementMap.set(listener, options); - - // add listener last - if (!size) { - element.addEventListener(eventType, globalListener, options); - } - }; - - /** - * Remove a listener from registry and detach the `globalListener` - * if no listeners are found in the registry. - * - * @type {Listener.ListenerAction<EventTarget>} - */ - const removeListener = (element, eventType, listener, options) => { - // get listener first - const oneEventMap = EventRegistry[eventType]; - const oneElementMap = oneEventMap && oneEventMap.get(element); - const savedOptions = oneElementMap && oneElementMap.get(listener); - - // also recover initial options - const { options: eventOptions } = savedOptions !== undefined - ? savedOptions - : { options }; - - // unsubscribe second, remove from registry - if (oneElementMap && oneElementMap.has(listener)) oneElementMap.delete(listener); - if (oneEventMap && (!oneElementMap || !oneElementMap.size)) oneEventMap.delete(element); - if (!oneEventMap || !oneEventMap.size) delete EventRegistry[eventType]; - - // remove listener last - /* istanbul ignore else */ - if (!oneElementMap || !oneElementMap.size) { - element.removeEventListener(eventType, globalListener, eventOptions); - } - }; - - /** - * Advanced event listener based on subscribe / publish pattern. - * @see https://www.patterns.dev/posts/classic-design-patterns/#observerpatternjavascript - * @see https://gist.github.com/shystruk/d16c0ee7ac7d194da9644e5d740c8338#file-subpub-js - * @see https://hackernoon.com/do-you-still-register-window-event-listeners-in-each-component-react-in-example-31a4b1f6f1c8 - */ - const Listener = { - on: addListener, - off: removeListener, - globalListener, - registry: EventRegistry, - }; - - /** - * A global namespace for `click` event. - * @type {string} - */ - const mouseclickEvent = 'click'; - - /** - * A global namespace for 'transitionend' string. - * @type {string} - */ - const transitionEndEvent = 'transitionend'; - - /** - * A global namespace for 'transitionDelay' string. - * @type {string} - */ - const transitionDelay = 'transitionDelay'; - - /** - * A global namespace for `transitionProperty` string for modern browsers. - * - * @type {string} - */ - const transitionProperty = 'transitionProperty'; - - /** - * Shortcut for `window.getComputedStyle(element).propertyName` - * static method. - * - * * If `element` parameter is not an `HTMLElement`, `getComputedStyle` - * throws a `ReferenceError`. - * - * @param {HTMLElement} element target - * @param {string} property the css property - * @return {string} the css property value - */ - function getElementStyle(element, property) { - const computedStyle = getComputedStyle(element); - - // must use camelcase strings, - // or non-camelcase strings with `getPropertyValue` - return property.includes('--') - ? computedStyle.getPropertyValue(property) - : computedStyle[property]; - } - - /** - * Utility to get the computed `transitionDelay` - * from Element in miliseconds. - * - * @param {HTMLElement} element target - * @return {number} the value in miliseconds - */ - function getElementTransitionDelay(element) { - const propertyValue = getElementStyle(element, transitionProperty); - const delayValue = getElementStyle(element, transitionDelay); - const delayScale = delayValue.includes('ms') ? /* istanbul ignore next */1 : 1000; - const duration = propertyValue && propertyValue !== 'none' - ? parseFloat(delayValue) * delayScale : 0; - - return !Number.isNaN(duration) ? duration : /* istanbul ignore next */0; - } - - /** - * A global namespace for 'transitionDuration' string. - * @type {string} - */ - const transitionDuration = 'transitionDuration'; - - /** - * Utility to get the computed `transitionDuration` - * from Element in miliseconds. - * - * @param {HTMLElement} element target - * @return {number} the value in miliseconds - */ - function getElementTransitionDuration(element) { - const propertyValue = getElementStyle(element, transitionProperty); - const durationValue = getElementStyle(element, transitionDuration); - const durationScale = durationValue.includes('ms') ? /* istanbul ignore next */1 : 1000; - const duration = propertyValue && propertyValue !== 'none' - ? parseFloat(durationValue) * durationScale : 0; - - return !Number.isNaN(duration) ? duration : /* istanbul ignore next */0; - } - - /** - * Shortcut for the `Element.dispatchEvent(Event)` method. - * - * @param {HTMLElement} element is the target - * @param {Event} event is the `Event` object - */ - const dispatchEvent = (element, event) => element.dispatchEvent(event); - - /** - * Utility to make sure callbacks are consistently - * called when transition ends. - * - * @param {HTMLElement} element target - * @param {EventListener} handler `transitionend` callback - */ - function emulateTransitionEnd(element, handler) { - let called = 0; - const endEvent = new Event(transitionEndEvent); - const duration = getElementTransitionDuration(element); - const delay = getElementTransitionDelay(element); - - if (duration) { - /** - * Wrap the handler in on -> off callback - * @type {EventListener} e Event object - */ - const transitionEndWrapper = (e) => { - /* istanbul ignore else */ - if (e.target === element) { - handler.apply(element, [e]); - element.removeEventListener(transitionEndEvent, transitionEndWrapper); - called = 1; - } - }; - element.addEventListener(transitionEndEvent, transitionEndWrapper); - setTimeout(() => { - /* istanbul ignore next */ - if (!called) dispatchEvent(element, endEvent); - }, duration + delay + 17); - } else { - handler.apply(element, [endEvent]); - } - } - - /** - * Checks if an object is a `Node`. - * - * @param {any} node the target object - * @returns {boolean} the query result - */ - const isNode = (element) => (element && [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11] - .some((x) => +element.nodeType === x)) || false; - - /** - * Check if a target object is `Window`. - * => equivalent to `object instanceof Window` - * - * @param {any} object the target object - * @returns {boolean} the query result - */ - const isWindow = (object) => (object && object.constructor.name === 'Window') || false; - - /** - * Checks if an object is a `Document`. - * @see https://dom.spec.whatwg.org/#node - * - * @param {any} object the target object - * @returns {boolean} the query result - */ - const isDocument = (object) => (object && object.nodeType === 9) || false; - - /** - * Returns the `document` or the `#document` element. - * @see https://github.com/floating-ui/floating-ui - * @param {(Node | Window)=} node - * @returns {Document} - */ - function getDocument(node) { - // node instanceof Document - if (isDocument(node)) return node; - // node instanceof Node - if (isNode(node)) return node.ownerDocument; - // node instanceof Window - if (isWindow(node)) return node.document; - // node is undefined | NULL - return window.document; - } - - /** - * Utility to check if target is typeof `HTMLElement`, `Element`, `Node` - * or find one that matches a selector. - * - * @param {Node | string} selector the input selector or target element - * @param {ParentNode=} parent optional node to look into - * @return {HTMLElement?} the `HTMLElement` or `querySelector` result - */ - function querySelector(selector, parent) { - if (isNode(selector)) { - return selector; - } - const lookUp = isNode(parent) ? parent : getDocument(); - - return lookUp.querySelector(selector); - } - - /** - * Shortcut for `HTMLElement.closest` method which also works - * with children of `ShadowRoot`. The order of the parameters - * is intentional since they're both required. - * - * @see https://stackoverflow.com/q/54520554/803358 - * - * @param {HTMLElement} element Element to look into - * @param {string} selector the selector name - * @return {HTMLElement?} the query result - */ - function closest(element, selector) { - return element ? (element.closest(selector) - // break out of `ShadowRoot` - || closest(element.getRootNode().host, selector)) : null; - } - - /** - * Shortcut for `Object.assign()` static method. - * @param {Record<string, any>} obj a target object - * @param {Record<string, any>} source a source object - */ - const ObjectAssign = (obj, source) => Object.assign(obj, source); - - /** - * Check class in `HTMLElement.classList`. - * - * @param {HTMLElement} element target - * @param {string} classNAME to check - * @returns {boolean} - */ - function hasClass(element, classNAME) { - return element.classList.contains(classNAME); - } - - /** - * Remove class from `HTMLElement.classList`. - * - * @param {HTMLElement} element target - * @param {string} classNAME to remove - * @returns {void} - */ - function removeClass(element, classNAME) { - element.classList.remove(classNAME); - } - - /** - * Checks if an element is an `HTMLElement`. - * @see https://dom.spec.whatwg.org/#node - * - * @param {any} element the target object - * @returns {boolean} the query result - */ - const isHTMLElement = (element) => (element && element.nodeType === 1) || false; - - /** @type {Map<string, Map<HTMLElement, Record<string, any>>>} */ - const componentData = new Map(); - /** - * An interface for web components background data. - * @see https://github.com/thednp/bootstrap.native/blob/master/src/components/base-component.js - */ - const Data = { - /** - * Sets web components data. - * @param {HTMLElement} element target element - * @param {string} component the component's name or a unique key - * @param {Record<string, any>} instance the component instance - */ - set: (element, component, instance) => { - if (!isHTMLElement(element)) return; - - /* istanbul ignore else */ - if (!componentData.has(component)) { - componentData.set(component, new Map()); - } - - const instanceMap = componentData.get(component); - // not undefined, but defined right above - instanceMap.set(element, instance); - }, - - /** - * Returns all instances for specified component. - * @param {string} component the component's name or a unique key - * @returns {Map<HTMLElement, Record<string, any>>?} all the component instances - */ - getAllFor: (component) => { - const instanceMap = componentData.get(component); - - return instanceMap || null; - }, - - /** - * Returns the instance associated with the target. - * @param {HTMLElement} element target element - * @param {string} component the component's name or a unique key - * @returns {Record<string, any>?} the instance - */ - get: (element, component) => { - if (!isHTMLElement(element) || !component) return null; - const allForC = Data.getAllFor(component); - const instance = element && allForC && allForC.get(element); - - return instance || null; - }, - - /** - * Removes web components data. - * @param {HTMLElement} element target element - * @param {string} component the component's name or a unique key - */ - remove: (element, component) => { - const instanceMap = componentData.get(component); - if (!instanceMap || !isHTMLElement(element)) return; - - instanceMap.delete(element); - - /* istanbul ignore else */ - if (instanceMap.size === 0) { - componentData.delete(component); - } - }, - }; - - /** - * An alias for `Data.get()`. - * @type {SHORTY.getInstance<any>} - */ - const getInstance = (target, component) => Data.get(target, component); - - /** - * Checks if an object is an `Object`. - * - * @param {any} obj the target object - * @returns {boolean} the query result - */ - const isObject = (obj) => (typeof obj === 'object') || false; - - /** - * Returns a namespaced `CustomEvent` specific to each component. - * @param {string} EventType Event.type - * @param {Record<string, any>=} config Event.options | Event.properties - * @returns {SHORTY.OriginalEvent} a new namespaced event - */ - function OriginalEvent(EventType, config) { - const OriginalCustomEvent = new CustomEvent(EventType, { - cancelable: true, bubbles: true, - }); - - /* istanbul ignore else */ - if (isObject(config)) { - ObjectAssign(OriginalCustomEvent, config); - } - return OriginalCustomEvent; - } - - /** - * Global namespace for most components `fade` class. - */ - const fadeClass = 'fade'; - - /** - * Global namespace for most components `show` class. - */ - const showClass = 'show'; - - /** - * Global namespace for most components `dismiss` option. - */ - const dataBsDismiss = 'data-bs-dismiss'; - - /** @type {string} */ - const alertString = 'alert'; - - /** @type {string} */ - const alertComponent = 'Alert'; - - /** - * Shortcut for `HTMLElement.getAttribute()` method. - * @param {HTMLElement} element target element - * @param {string} attribute attribute name - * @returns {string?} attribute value - */ - const getAttribute = (element, attribute) => element.getAttribute(attribute); - - /** - * The raw value or a given component option. - * - * @typedef {string | HTMLElement | Function | number | boolean | null} niceValue - */ - - /** - * Utility to normalize component options - * - * @param {any} value the input value - * @return {niceValue} the normalized value - */ - function normalizeValue(value) { - if (['true', true].includes(value)) { // boolean - // if ('true' === value) { // boolean - return true; - } - - if (['false', false].includes(value)) { // boolean - // if ('false' === value) { // boolean - return false; - } - - if (value === '' || value === 'null') { // null - return null; - } - - if (value !== '' && !Number.isNaN(+value)) { // number - return +value; - } - - // string / function / HTMLElement / object - return value; - } - - /** - * Shortcut for `Object.keys()` static method. - * @param {Record<string, any>} obj a target object - * @returns {string[]} - */ - const ObjectKeys = (obj) => Object.keys(obj); - - /** - * Shortcut for `String.toLowerCase()`. - * - * @param {string} source input string - * @returns {string} lowercase output string - */ - const toLowerCase = (source) => source.toLowerCase(); - - /** - * Utility to normalize component options. - * - * @param {HTMLElement} element target - * @param {Record<string, any>} defaultOps component default options - * @param {Record<string, any>} inputOps component instance options - * @param {string=} ns component namespace - * @return {Record<string, any>} normalized component options object - */ - function normalizeOptions(element, defaultOps, inputOps, ns) { - const data = { ...element.dataset }; - /** @type {Record<string, any>} */ - const normalOps = {}; - /** @type {Record<string, any>} */ - const dataOps = {}; - const title = 'title'; - - ObjectKeys(data).forEach((k) => { - const key = ns && k.includes(ns) - ? k.replace(ns, '').replace(/[A-Z]/, (match) => toLowerCase(match)) - : k; - - dataOps[key] = normalizeValue(data[k]); - }); - - ObjectKeys(inputOps).forEach((k) => { - inputOps[k] = normalizeValue(inputOps[k]); - }); - - ObjectKeys(defaultOps).forEach((k) => { - /* istanbul ignore else */ - if (k in inputOps) { - normalOps[k] = inputOps[k]; - } else if (k in dataOps) { - normalOps[k] = dataOps[k]; - } else { - normalOps[k] = k === title - ? getAttribute(element, title) - : defaultOps[k]; - } - }); - - return normalOps; - } - - var version = "4.2.0"; - - const Version = version; - - /* Native JavaScript for Bootstrap 5 | Base Component - ----------------------------------------------------- */ - - /** Returns a new `BaseComponent` instance. */ - class BaseComponent { - /** - * @param {HTMLElement | string} target `Element` or selector string - * @param {BSN.ComponentOptions=} config component instance options - */ - constructor(target, config) { - const self = this; - const element = querySelector(target); - - if (!element) { - throw Error(`${self.name} Error: "${target}" is not a valid selector.`); - } - - /** @static @type {BSN.ComponentOptions} */ - self.options = {}; - - const prevInstance = Data.get(element, self.name); - if (prevInstance) prevInstance.dispose(); - - /** @type {HTMLElement} */ - self.element = element; - - /* istanbul ignore else */ - if (self.defaults && ObjectKeys(self.defaults).length) { - self.options = normalizeOptions(element, self.defaults, (config || {}), 'bs'); - } - - Data.set(element, self.name, self); - } - - /* eslint-disable */ - /* istanbul ignore next */ - /** @static */ - get version() { return Version; } - - /* eslint-enable */ - /* istanbul ignore next */ - /** @static */ - get name() { return this.constructor.name; } - - /* istanbul ignore next */ - /** @static */ - get defaults() { return this.constructor.defaults; } - - /** - * Removes component from target element; - */ - dispose() { - const self = this; - Data.remove(self.element, self.name); - ObjectKeys(self).forEach((prop) => { self[prop] = null; }); - } - } - - /* Native JavaScript for Bootstrap 5 | Alert - -------------------------------------------- */ - - // ALERT PRIVATE GC - // ================ - const alertSelector = `.${alertString}`; - const alertDismissSelector = `[${dataBsDismiss}="${alertString}"]`; - - /** - * Static method which returns an existing `Alert` instance associated - * to a target `Element`. - * - * @type {BSN.GetInstance<Alert>} - */ - const getAlertInstance = (element) => getInstance(element, alertComponent); - - /** - * An `Alert` initialization callback. - * @type {BSN.InitCallback<Alert>} - */ - const alertInitCallback = (element) => new Alert(element); - - // ALERT CUSTOM EVENTS - // =================== - const closeAlertEvent = OriginalEvent(`close.bs.${alertString}`); - const closedAlertEvent = OriginalEvent(`closed.bs.${alertString}`); - - // ALERT EVENT HANDLER - // =================== - /** - * Alert `transitionend` callback. - * @param {Alert} self target Alert instance - */ - function alertTransitionEnd(self) { - const { element } = self; - toggleAlertHandler(self); - - dispatchEvent(element, closedAlertEvent); - - self.dispose(); - element.remove(); - } - - // ALERT PRIVATE METHOD - // ==================== - /** - * Toggle on / off the `click` event listener. - * @param {Alert} self the target alert instance - * @param {boolean=} add when `true`, event listener is added - */ - function toggleAlertHandler(self, add) { - const action = add ? addListener : removeListener; - const { dismiss } = self; - /* istanbul ignore else */ - if (dismiss) action(dismiss, mouseclickEvent, self.close); - } - - // ALERT DEFINITION - // ================ - /** Creates a new Alert instance. */ - class Alert extends BaseComponent { - /** @param {HTMLElement | string} target element or selector */ - constructor(target) { - super(target); - // bind - const self = this; - - // initialization element - const { element } = self; - - // the dismiss button - /** @static @type {HTMLElement?} */ - self.dismiss = querySelector(alertDismissSelector, element); - - // add event listener - toggleAlertHandler(self, true); - } - - /* eslint-disable */ - /** - * Returns component name string. - */ - get name() { return alertComponent; } - /* eslint-enable */ - - // ALERT PUBLIC METHODS - // ==================== - /** - * Public method that hides the `.alert` element from the user, - * disposes the instance once animation is complete, then - * removes the element from the DOM. - * - * @param {Event=} e most likely the `click` event - * @this {Alert} the `Alert` instance or `EventTarget` - */ - close(e) { - const self = e ? getAlertInstance(closest(this, alertSelector)) : this; - const { element } = self; - - /* istanbul ignore else */ - if (element && hasClass(element, showClass)) { - dispatchEvent(element, closeAlertEvent); - if (closeAlertEvent.defaultPrevented) return; - - removeClass(element, showClass); - - if (hasClass(element, fadeClass)) { - emulateTransitionEnd(element, () => alertTransitionEnd(self)); - } else alertTransitionEnd(self); - } - } - - /** Remove the component from target element. */ - dispose() { - toggleAlertHandler(this); - super.dispose(); - } - } - - ObjectAssign(Alert, { - selector: alertSelector, - init: alertInitCallback, - getInstance: getAlertInstance, - }); - - /** - * A global namespace for aria-pressed. - * @type {string} - */ - const ariaPressed = 'aria-pressed'; - - /** - * Shortcut for `HTMLElement.setAttribute()` method. - * @param {HTMLElement} element target element - * @param {string} attribute attribute name - * @param {string} value attribute value - * @returns {void} - */ - const setAttribute = (element, attribute, value) => element.setAttribute(attribute, value); - - /** - * Add class to `HTMLElement.classList`. - * - * @param {HTMLElement} element target - * @param {string} classNAME to add - * @returns {void} - */ - function addClass(element, classNAME) { - element.classList.add(classNAME); - } - - /** - * Global namespace for most components active class. - */ - const activeClass = 'active'; - - /** - * Global namespace for most components `toggle` option. - */ - const dataBsToggle = 'data-bs-toggle'; - - /** @type {string} */ - const buttonString = 'button'; - - /** @type {string} */ - const buttonComponent = 'Button'; - - /* Native JavaScript for Bootstrap 5 | Button - ---------------------------------------------*/ - - // BUTTON PRIVATE GC - // ================= - const buttonSelector = `[${dataBsToggle}="${buttonString}"]`; - - /** - * Static method which returns an existing `Button` instance associated - * to a target `Element`. - * - * @type {BSN.GetInstance<Button>} - */ - const getButtonInstance = (element) => getInstance(element, buttonComponent); - - /** - * A `Button` initialization callback. - * @type {BSN.InitCallback<Button>} - */ - const buttonInitCallback = (element) => new Button(element); - - // BUTTON PRIVATE METHOD - // ===================== - /** - * Toggles on/off the `click` event listener. - * @param {Button} self the `Button` instance - * @param {boolean=} add when `true`, event listener is added - */ - function toggleButtonHandler(self, add) { - const action = add ? addListener : removeListener; - action(self.element, mouseclickEvent, self.toggle); - } - - // BUTTON DEFINITION - // ================= - /** Creates a new `Button` instance. */ - class Button extends BaseComponent { - /** - * @param {HTMLElement | string} target usually a `.btn` element - */ - constructor(target) { - super(target); - const self = this; - - // initialization element - const { element } = self; - - // set initial state - /** @type {boolean} */ - self.isActive = hasClass(element, activeClass); - setAttribute(element, ariaPressed, `${!!self.isActive}`); - - // add event listener - toggleButtonHandler(self, true); - } - - /* eslint-disable */ - /** - * Returns component name string. - */ - get name() { return buttonComponent; } - /* eslint-enable */ - - // BUTTON PUBLIC METHODS - // ===================== - /** - * Toggles the state of the target button. - * @param {MouseEvent} e usually `click` Event object - */ - toggle(e) { - if (e) e.preventDefault(); - const self = e ? getButtonInstance(this) : this; - if (!self.element) return; - const { element, isActive } = self; - - if (hasClass(element, 'disabled')) return; - - const action = isActive ? removeClass : addClass; - action(element, activeClass); - setAttribute(element, ariaPressed, isActive ? 'false' : 'true'); - self.isActive = hasClass(element, activeClass); - } - - /** Removes the `Button` component from the target element. */ - dispose() { - toggleButtonHandler(this); - super.dispose(); - } - } - - ObjectAssign(Button, { - selector: buttonSelector, - init: buttonInitCallback, - getInstance: getButtonInstance, - }); - - /** - * A global namespace for `mouseenter` event. - * @type {string} - */ - const mouseenterEvent = 'mouseenter'; - - /** - * A global namespace for `mouseleave` event. - * @type {string} - */ - const mouseleaveEvent = 'mouseleave'; - - /** - * A global namespace for `keydown` event. - * @type {string} - */ - const keydownEvent = 'keydown'; - - /** - * A global namespace for `ArrowLeft` key. - * @type {string} e.which = 37 equivalent - */ - const keyArrowLeft = 'ArrowLeft'; - - /** - * A global namespace for `ArrowRight` key. - * @type {string} e.which = 39 equivalent - */ - const keyArrowRight = 'ArrowRight'; - - /** - * A global namespace for `pointerdown` event. - * @type {string} - */ - const pointerdownEvent = 'pointerdown'; - - /** - * A global namespace for `pointermove` event. - * @type {string} - */ - const pointermoveEvent = 'pointermove'; - - /** - * A global namespace for `pointerup` event. - * @type {string} - */ - const pointerupEvent = 'pointerup'; - - /** - * Returns the bounding client rect of a target `HTMLElement`. - * - * @see https://github.com/floating-ui/floating-ui - * - * @param {HTMLElement} element event.target - * @param {boolean=} includeScale when *true*, the target scale is also computed - * @returns {SHORTY.BoundingClientRect} the bounding client rect object - */ - function getBoundingClientRect(element, includeScale) { - const { - width, height, top, right, bottom, left, - } = element.getBoundingClientRect(); - let scaleX = 1; - let scaleY = 1; - - if (includeScale && isHTMLElement(element)) { - const { offsetWidth, offsetHeight } = element; - scaleX = offsetWidth > 0 ? Math.round(width) / offsetWidth - : /* istanbul ignore next */1; - scaleY = offsetHeight > 0 ? Math.round(height) / offsetHeight - : /* istanbul ignore next */1; - } - - return { - width: width / scaleX, - height: height / scaleY, - top: top / scaleY, - right: right / scaleX, - bottom: bottom / scaleY, - left: left / scaleX, - x: left / scaleX, - y: top / scaleY, - }; - } - - /** - * Returns the `document.documentElement` or the `<html>` element. - * - * @param {(Node | Window)=} node - * @returns {HTMLHtmlElement} - */ - function getDocumentElement(node) { - return getDocument(node).documentElement; - } - - /** - * Utility to determine if an `HTMLElement` - * is partially visible in viewport. - * - * @param {HTMLElement} element target - * @return {boolean} the query result - */ - const isElementInScrollRange = (element) => { - if (!element || !isNode(element)) return false; - - const { top, bottom } = getBoundingClientRect(element); - const { clientHeight } = getDocumentElement(element); - return top <= clientHeight && bottom >= 0; - }; - - /** - * Checks if a page is Right To Left. - * @param {HTMLElement=} node the target - * @returns {boolean} the query result - */ - const isRTL = (node) => getDocumentElement(node).dir === 'rtl'; - - /** - * A shortcut for `(document|Element).querySelectorAll`. - * - * @param {string} selector the input selector - * @param {ParentNode=} parent optional node to look into - * @return {NodeListOf<HTMLElement>} the query result - */ - function querySelectorAll(selector, parent) { - const lookUp = isNode(parent) ? parent : getDocument(); - return lookUp.querySelectorAll(selector); - } - - /** - * Shortcut for `HTMLElement.getElementsByClassName` method. Some `Node` elements - * like `ShadowRoot` do not support `getElementsByClassName`. - * - * @param {string} selector the class name - * @param {ParentNode=} parent optional Element to look into - * @return {HTMLCollectionOf<HTMLElement>} the 'HTMLCollection' - */ - function getElementsByClassName(selector, parent) { - const lookUp = isNode(parent) ? parent : getDocument(); - return lookUp.getElementsByClassName(selector); - } - - /** @type {Map<HTMLElement, any>} */ - const TimeCache = new Map(); - /** - * An interface for one or more `TimerHandler`s per `Element`. - * @see https://github.com/thednp/navbar.js/ - */ - const Timer = { - /** - * Sets a new timeout timer for an element, or element -> key association. - * @param {HTMLElement} element target element - * @param {ReturnType<TimerHandler>} callback the callback - * @param {number} delay the execution delay - * @param {string=} key a unique key - */ - set: (element, callback, delay, key) => { - if (!isHTMLElement(element)) return; - - /* istanbul ignore else */ - if (key && key.length) { - /* istanbul ignore else */ - if (!TimeCache.has(element)) { - TimeCache.set(element, new Map()); - } - const keyTimers = TimeCache.get(element); - keyTimers.set(key, setTimeout(callback, delay)); - } else { - TimeCache.set(element, setTimeout(callback, delay)); - } - }, - - /** - * Returns the timer associated with the target. - * @param {HTMLElement} element target element - * @param {string=} key a unique - * @returns {number?} the timer - */ - get: (element, key) => { - if (!isHTMLElement(element)) return null; - const keyTimers = TimeCache.get(element); - - if (key && key.length && keyTimers && keyTimers.get) { - return keyTimers.get(key) || /* istanbul ignore next */null; - } - return keyTimers || null; - }, - - /** - * Clears the element's timer. - * @param {HTMLElement} element target element - * @param {string=} key a unique key - */ - clear: (element, key) => { - if (!isHTMLElement(element)) return; - - if (key && key.length) { - const keyTimers = TimeCache.get(element); - /* istanbul ignore else */ - if (keyTimers && keyTimers.get) { - clearTimeout(keyTimers.get(key)); - keyTimers.delete(key); - /* istanbul ignore else */ - if (keyTimers.size === 0) { - TimeCache.delete(element); - } - } - } else { - clearTimeout(TimeCache.get(element)); - TimeCache.delete(element); - } - }, - }; - - /** - * Utility to force re-paint of an `HTMLElement` target. - * - * @param {HTMLElement} element is the target - * @return {number} the `Element.offsetHeight` value - */ - const reflow = (element) => element.offsetHeight; - - /** - * A global namespace for most scroll event listeners. - * @type {Partial<AddEventListenerOptions>} - */ - const passiveHandler = { passive: true }; - - /** - * Global namespace for most components `target` option. - */ - const dataBsTarget = 'data-bs-target'; - - /** @type {string} */ - const carouselString = 'carousel'; - - /** @type {string} */ - const carouselComponent = 'Carousel'; - - /** - * Global namespace for most components `parent` option. - */ - const dataBsParent = 'data-bs-parent'; - - /** - * Global namespace for most components `container` option. - */ - const dataBsContainer = 'data-bs-container'; - - /** - * Returns the `Element` that THIS one targets - * via `data-bs-target`, `href`, `data-bs-parent` or `data-bs-container`. - * - * @param {HTMLElement} element the target element - * @returns {HTMLElement?} the query result - */ - function getTargetElement(element) { - const targetAttr = [dataBsTarget, dataBsParent, dataBsContainer, 'href']; - const doc = getDocument(element); - - return targetAttr.map((att) => { - const attValue = getAttribute(element, att); - if (attValue) { - return att === dataBsParent ? closest(element, attValue) : querySelector(attValue, doc); - } - return null; - }).filter((x) => x)[0]; - } - - /* Native JavaScript for Bootstrap 5 | Carousel - ----------------------------------------------- */ - - // CAROUSEL PRIVATE GC - // =================== - const carouselSelector = `[data-bs-ride="${carouselString}"]`; - const carouselItem = `${carouselString}-item`; - const dataBsSlideTo = 'data-bs-slide-to'; - const dataBsSlide = 'data-bs-slide'; - const pausedClass = 'paused'; - - const carouselDefaults = { - pause: 'hover', - keyboard: false, - touch: true, - interval: 5000, - }; - - /** - * Static method which returns an existing `Carousel` instance associated - * to a target `Element`. - * - * @type {BSN.GetInstance<Carousel>} - */ - const getCarouselInstance = (element) => getInstance(element, carouselComponent); - - /** - * A `Carousel` initialization callback. - * @type {BSN.InitCallback<Carousel>} - */ - const carouselInitCallback = (element) => new Carousel(element); - - let startX = 0; - let currentX = 0; - let endX = 0; - - // CAROUSEL CUSTOM EVENTS - // ====================== - const carouselSlideEvent = OriginalEvent(`slide.bs.${carouselString}`); - const carouselSlidEvent = OriginalEvent(`slid.bs.${carouselString}`); - - // CAROUSEL EVENT HANDLERS - // ======================= - /** - * The `transitionend` event listener of the `Carousel`. - * @param {Carousel} self the `Carousel` instance - */ - function carouselTransitionEndHandler(self) { - const { - index, direction, element, slides, options, - } = self; - - // discontinue disposed instances - /* istanbul ignore else */ - if (self.isAnimating && getCarouselInstance(element)) { - const activeItem = getActiveIndex(self); - const orientation = direction === 'left' ? 'next' : 'prev'; - const directionClass = direction === 'left' ? 'start' : 'end'; - - addClass(slides[index], activeClass); - removeClass(slides[index], `${carouselItem}-${orientation}`); - removeClass(slides[index], `${carouselItem}-${directionClass}`); - - removeClass(slides[activeItem], activeClass); - removeClass(slides[activeItem], `${carouselItem}-${directionClass}`); - - dispatchEvent(element, carouselSlidEvent); - Timer.clear(element, dataBsSlide); - - // check for element, might have been disposed - if (!getDocument(element).hidden && options.interval - && !self.isPaused) { - self.cycle(); - } - } - } - - /** - * Handles the `mouseenter` events when *options.pause* - * is set to `hover`. - * - * @this {HTMLElement} - */ - function carouselPauseHandler() { - const element = this; - const self = getCarouselInstance(element); - /* istanbul ignore else */ - if (self && !self.isPaused && !Timer.get(element, pausedClass)) { - addClass(element, pausedClass); - } - } - - /** - * Handles the `mouseleave` events when *options.pause* - * is set to `hover`. - * - * @this {HTMLElement} - */ - function carouselResumeHandler() { - const element = this; - const self = getCarouselInstance(element); - /* istanbul ignore else */ - if (self && self.isPaused && !Timer.get(element, pausedClass)) { - self.cycle(); - } - } - - /** - * Handles the `click` event for the `Carousel` indicators. - * - * @this {HTMLElement} - * @param {MouseEvent} e the `Event` object - */ - function carouselIndicatorHandler(e) { - e.preventDefault(); - const indicator = this; - const element = closest(indicator, carouselSelector) || getTargetElement(indicator); - const self = getCarouselInstance(element); - - if (!self || self.isAnimating) return; - - const newIndex = +getAttribute(indicator, dataBsSlideTo); - - if (indicator && !hasClass(indicator, activeClass) // event target is not active - && !Number.isNaN(newIndex)) { // AND has the specific attribute - self.to(newIndex); // do the slide - } - } - - /** - * Handles the `click` event for the `Carousel` arrows. - * - * @this {HTMLElement} - * @param {MouseEvent} e the `Event` object - */ - function carouselControlsHandler(e) { - e.preventDefault(); - const control = this; - const element = closest(control, carouselSelector) || getTargetElement(control); - const self = getCarouselInstance(element); - - if (!self || self.isAnimating) return; - const orientation = getAttribute(control, dataBsSlide); - - /* istanbul ignore else */ - if (orientation === 'next') { - self.next(); - } else if (orientation === 'prev') { - self.prev(); - } - } - - /** - * Handles the keyboard `keydown` event for the visible `Carousel` elements. - * - * @param {KeyboardEvent} e the `Event` object - */ - function carouselKeyHandler({ code, target }) { - const doc = getDocument(target); - const [element] = [...querySelectorAll(carouselSelector, doc)] - .filter((x) => isElementInScrollRange(x)); - const self = getCarouselInstance(element); - - /* istanbul ignore next */ - if (!self || self.isAnimating || /textarea|input/i.test(target.tagName)) return; - const RTL = isRTL(element); - const arrowKeyNext = !RTL ? keyArrowRight : keyArrowLeft; - const arrowKeyPrev = !RTL ? keyArrowLeft : keyArrowRight; - - /* istanbul ignore else */ - if (code === arrowKeyPrev) self.prev(); - else if (code === arrowKeyNext) self.next(); - } - - // CAROUSEL TOUCH HANDLERS - // ======================= - /** - * Handles the `pointerdown` event for the `Carousel` element. - * - * @this {HTMLElement} - * @param {PointerEvent} e the `Event` object - */ - function carouselPointerDownHandler(e) { - const element = this; - const { target } = e; - const self = getCarouselInstance(element); - - // filter pointer event on controls & indicators - const { controls, indicators } = self; - if ([...controls, ...indicators].some((el) => (el === target || el.contains(target)))) { - return; - } - - if (!self || self.isAnimating || self.isTouch) { return; } - - startX = e.pageX; - - /* istanbul ignore else */ - if (element.contains(target)) { - self.isTouch = true; - toggleCarouselTouchHandlers(self, true); - } - } - - /** - * Handles the `pointermove` event for the `Carousel` element. - * - * @this {HTMLElement} - * @param {PointerEvent} e - */ - function carouselPointerMoveHandler(e) { - // const self = getCarouselInstance(this); - - // if (!self || !self.isTouch) { return; } - - currentX = e.pageX; - } - - /** - * Handles the `pointerup` event for the `Carousel` element. - * - * @this {HTMLElement} - - * @param {PointerEvent} e - */ - function carouselPointerUpHandler(e) { - const { target } = e; - const doc = getDocument(target); - const self = [...querySelectorAll(carouselSelector, doc)] - .map((c) => getCarouselInstance(c)).find((i) => i.isTouch); - - // impossible to satisfy - /* istanbul ignore next */ - if (!self) { return; } - - const { element, index } = self; - const RTL = isRTL(target); - - self.isTouch = false; - toggleCarouselTouchHandlers(self); - - if (doc.getSelection().toString().length) { - // reset pointer position - startX = 0; currentX = 0; endX = 0; - return; - } - - endX = e.pageX; - - // the event target is outside the carousel context - // OR swipe distance is less than 120px - /* istanbul ignore else */ - if (!element.contains(target) || Math.abs(startX - endX) < 120) { - // reset pointer position - startX = 0; currentX = 0; endX = 0; - return; - } - // OR determine next index to slide to - /* istanbul ignore else */ - if (currentX < startX) { - self.to(index + (RTL ? -1 : 1)); - } else if (currentX > startX) { - self.to(index + (RTL ? 1 : -1)); - } - // reset pointer position - startX = 0; currentX = 0; endX = 0; - } - - // CAROUSEL PRIVATE METHODS - // ======================== - /** - * Sets active indicator for the `Carousel` instance. - * @param {Carousel} self the `Carousel` instance - * @param {number} pageIndex the index of the new active indicator - */ - function activateCarouselIndicator(self, pageIndex) { - const { indicators } = self; - [...indicators].forEach((x) => removeClass(x, activeClass)); - - /* istanbul ignore else */ - if (self.indicators[pageIndex]) addClass(indicators[pageIndex], activeClass); - } - - /** - * Toggles the pointer event listeners for a given `Carousel` instance. - * @param {Carousel} self the `Carousel` instance - * @param {boolean=} add when `TRUE` event listeners are added - */ - function toggleCarouselTouchHandlers(self, add) { - const { element } = self; - const action = add ? addListener : removeListener; - action(getDocument(element), pointermoveEvent, carouselPointerMoveHandler, passiveHandler); - action(getDocument(element), pointerupEvent, carouselPointerUpHandler, passiveHandler); - } - - /** - * Toggles all event listeners for a given `Carousel` instance. - * @param {Carousel} self the `Carousel` instance - * @param {boolean=} add when `TRUE` event listeners are added - */ - function toggleCarouselHandlers(self, add) { - const { - element, options, slides, controls, indicators, - } = self; - const { - touch, pause, interval, keyboard, - } = options; - const action = add ? addListener : removeListener; - - if (pause && interval) { - action(element, mouseenterEvent, carouselPauseHandler); - action(element, mouseleaveEvent, carouselResumeHandler); - } - - if (touch && slides.length > 2) { - action(element, pointerdownEvent, carouselPointerDownHandler, passiveHandler); - } - - /* istanbul ignore else */ - if (controls.length) { - controls.forEach((arrow) => { - /* istanbul ignore else */ - if (arrow) action(arrow, mouseclickEvent, carouselControlsHandler); - }); - } - - /* istanbul ignore else */ - if (indicators.length) { - indicators.forEach((indicator) => { - action(indicator, mouseclickEvent, carouselIndicatorHandler); - }); - } - - if (keyboard) action(getDocument(element), keydownEvent, carouselKeyHandler); - } - - /** - * Returns the index of the current active item. - * @param {Carousel} self the `Carousel` instance - * @returns {number} the query result - */ - function getActiveIndex(self) { - const { slides, element } = self; - const activeItem = querySelector(`.${carouselItem}.${activeClass}`, element); - return [...slides].indexOf(activeItem); - } - - // CAROUSEL DEFINITION - // =================== - /** Creates a new `Carousel` instance. */ - class Carousel extends BaseComponent { - /** - * @param {HTMLElement | string} target mostly a `.carousel` element - * @param {BSN.Options.Carousel=} config instance options - */ - constructor(target, config) { - super(target, config); - // bind - const self = this; - // initialization element - const { element } = self; - - // additional properties - /** @type {string} */ - self.direction = isRTL(element) ? 'right' : 'left'; - /** @type {number} */ - self.index = 0; - /** @type {boolean} */ - self.isTouch = false; - - // carousel elements - // a LIVE collection is prefferable - self.slides = getElementsByClassName(carouselItem, element); - const { slides } = self; - - // invalidate when not enough items - // no need to go further - if (slides.length < 2) { return; } - // external controls must be within same document context - const doc = getDocument(element); - - self.controls = [ - ...querySelectorAll(`[${dataBsSlide}]`, element), - ...querySelectorAll(`[${dataBsSlide}][${dataBsTarget}="#${element.id}"]`, doc), - ]; - - /** @type {HTMLElement?} */ - self.indicator = querySelector(`.${carouselString}-indicators`, element); - - // a LIVE collection is prefferable - /** @type {HTMLElement[]} */ - self.indicators = [ - ...(self.indicator ? querySelectorAll(`[${dataBsSlideTo}]`, self.indicator) : []), - ...querySelectorAll(`[${dataBsSlideTo}][${dataBsTarget}="#${element.id}"]`, doc), - ]; - - // set JavaScript and DATA API options - const { options } = self; - - // don't use TRUE as interval, it's actually 0, use the default 5000ms better - self.options.interval = options.interval === true - ? carouselDefaults.interval - : options.interval; - - // set first slide active if none - /* istanbul ignore else */ - if (getActiveIndex(self) < 0) { - addClass(slides[0], activeClass); - /* istanbul ignore else */ - if (self.indicators.length) activateCarouselIndicator(self, 0); - } - - // attach event handlers - toggleCarouselHandlers(self, true); - - // start to cycle if interval is set - if (options.interval) self.cycle(); - } - - /* eslint-disable */ - /** - * Returns component name string. - */ - get name() { return carouselComponent; } - /** - * Returns component default options. - */ - get defaults() { return carouselDefaults; } - /* eslint-enable */ - - /** - * Check if instance is paused. - * @returns {boolean} - */ - get isPaused() { - return hasClass(this.element, pausedClass); - } - - /** - * Check if instance is animating. - * @returns {boolean} - */ - get isAnimating() { - return querySelector(`.${carouselItem}-next,.${carouselItem}-prev`, this.element) !== null; - } - - // CAROUSEL PUBLIC METHODS - // ======================= - /** Slide automatically through items. */ - cycle() { - const self = this; - const { - element, options, isPaused, index, - } = self; - - Timer.clear(element, carouselString); - if (isPaused) { - Timer.clear(element, pausedClass); - removeClass(element, pausedClass); - } - - Timer.set(element, () => { - // it's very important to check self.element - // where instance might have been disposed - /* istanbul ignore else */ - if (self.element && !self.isPaused && !self.isTouch - && isElementInScrollRange(element)) { - self.to(index + 1); - } - }, options.interval, carouselString); - } - - /** Pause the automatic cycle. */ - pause() { - const self = this; - const { element, options } = self; - /* istanbul ignore else */ - if (!self.isPaused && options.interval) { - addClass(element, pausedClass); - Timer.set(element, () => {}, 1, pausedClass); - } - } - - /** Slide to the next item. */ - next() { - const self = this; - /* istanbul ignore else */ - if (!self.isAnimating) { self.to(self.index + 1); } - } - - /** Slide to the previous item. */ - prev() { - const self = this; - /* istanbul ignore else */ - if (!self.isAnimating) { self.to(self.index - 1); } - } - - /** - * Jump to the item with the `idx` index. - * @param {number} idx the index of the item to jump to - */ - to(idx) { - const self = this; - const { - element, slides, options, - } = self; - const activeItem = getActiveIndex(self); - const RTL = isRTL(element); - let next = idx; - - // when controled via methods, make sure to check again - // first return if we're on the same item #227 - // `to()` must be SPAM protected by Timer - if (self.isAnimating || activeItem === next || Timer.get(element, dataBsSlide)) return; - - // determine transition direction - /* istanbul ignore else */ - if ((activeItem < next) || (activeItem === 0 && next === slides.length - 1)) { - self.direction = RTL ? 'right' : 'left'; // next - } else if ((activeItem > next) || (activeItem === slides.length - 1 && next === 0)) { - self.direction = RTL ? 'left' : 'right'; // prev - } - const { direction } = self; - - // find the right next index - if (next < 0) { next = slides.length - 1; } else if (next >= slides.length) { next = 0; } - - // orientation, class name, eventProperties - const orientation = direction === 'left' ? 'next' : 'prev'; - const directionClass = direction === 'left' ? 'start' : 'end'; - - const eventProperties = { - relatedTarget: slides[next], - from: activeItem, - to: next, - direction, - }; - - // update event properties - ObjectAssign(carouselSlideEvent, eventProperties); - ObjectAssign(carouselSlidEvent, eventProperties); - - // discontinue when prevented - dispatchEvent(element, carouselSlideEvent); - if (carouselSlideEvent.defaultPrevented) return; - - // update index - self.index = next; - activateCarouselIndicator(self, next); - - if (getElementTransitionDuration(slides[next]) && hasClass(element, 'slide')) { - Timer.set(element, () => { - addClass(slides[next], `${carouselItem}-${orientation}`); - reflow(slides[next]); - addClass(slides[next], `${carouselItem}-${directionClass}`); - addClass(slides[activeItem], `${carouselItem}-${directionClass}`); - - emulateTransitionEnd(slides[next], () => carouselTransitionEndHandler(self)); - }, 0, dataBsSlide); - } else { - addClass(slides[next], activeClass); - removeClass(slides[activeItem], activeClass); - - Timer.set(element, () => { - Timer.clear(element, dataBsSlide); - // check for element, might have been disposed - /* istanbul ignore else */ - if (element && options.interval && !self.isPaused) { - self.cycle(); - } - - dispatchEvent(element, carouselSlidEvent); - }, 0, dataBsSlide); - } - } - - /** Remove `Carousel` component from target. */ - dispose() { - const self = this; - const { slides } = self; - const itemClasses = ['start', 'end', 'prev', 'next']; - - [...slides].forEach((slide, idx) => { - if (hasClass(slide, activeClass)) activateCarouselIndicator(self, idx); - itemClasses.forEach((c) => removeClass(slide, `${carouselItem}-${c}`)); - }); - - toggleCarouselHandlers(self); - super.dispose(); - } - } - - ObjectAssign(Carousel, { - selector: carouselSelector, - init: carouselInitCallback, - getInstance: getCarouselInstance, - }); - - /** - * A global namespace for aria-expanded. - * @type {string} - */ - const ariaExpanded = 'aria-expanded'; - - /** - * Shortcut for `Object.entries()` static method. - * @param {Record<string, any>} obj a target object - * @returns {[string, any][]} - */ - const ObjectEntries = (obj) => Object.entries(obj); - - /** - * Shortcut for multiple uses of `HTMLElement.style.propertyName` method. - * @param {HTMLElement} element target element - * @param {Partial<CSSStyleDeclaration>} styles attribute value - */ - const setElementStyle = (element, styles) => { - ObjectEntries(styles).forEach(([key, value]) => { - if (key.includes('--')) { - element.style.setProperty(key, value); - } else { - const propObject = {}; propObject[key] = value; - ObjectAssign(element.style, propObject); - } - }); - }; - - /** - * Global namespace for most components `collapsing` class. - * As used by `Collapse` / `Tab`. - */ - const collapsingClass = 'collapsing'; - - /** @type {string} */ - const collapseString = 'collapse'; - - /** @type {string} */ - const collapseComponent = 'Collapse'; - - /* Native JavaScript for Bootstrap 5 | Collapse - ----------------------------------------------- */ - - // COLLAPSE GC - // =========== - const collapseSelector = `.${collapseString}`; - const collapseToggleSelector = `[${dataBsToggle}="${collapseString}"]`; - const collapseDefaults = { parent: null }; - - /** - * Static method which returns an existing `Collapse` instance associated - * to a target `Element`. - * - * @type {BSN.GetInstance<Collapse>} - */ - const getCollapseInstance = (element) => getInstance(element, collapseComponent); - - /** - * A `Collapse` initialization callback. - * @type {BSN.InitCallback<Collapse>} - */ - const collapseInitCallback = (element) => new Collapse(element); - - // COLLAPSE CUSTOM EVENTS - // ====================== - const showCollapseEvent = OriginalEvent(`show.bs.${collapseString}`); - const shownCollapseEvent = OriginalEvent(`shown.bs.${collapseString}`); - const hideCollapseEvent = OriginalEvent(`hide.bs.${collapseString}`); - const hiddenCollapseEvent = OriginalEvent(`hidden.bs.${collapseString}`); - - // COLLAPSE PRIVATE METHODS - // ======================== - /** - * Expand the designated `Element`. - * @param {Collapse} self the `Collapse` instance - */ - function expandCollapse(self) { - const { - element, parent, triggers, - } = self; - - dispatchEvent(element, showCollapseEvent); - if (showCollapseEvent.defaultPrevented) return; - - Timer.set(element, () => {}, 17); - if (parent) Timer.set(parent, () => {}, 17); - - addClass(element, collapsingClass); - removeClass(element, collapseString); - - setElementStyle(element, { height: `${element.scrollHeight}px` }); - - emulateTransitionEnd(element, () => { - Timer.clear(element); - if (parent) Timer.clear(parent); - - triggers.forEach((btn) => setAttribute(btn, ariaExpanded, 'true')); - - removeClass(element, collapsingClass); - addClass(element, collapseString); - addClass(element, showClass); - - setElementStyle(element, { height: '' }); - - dispatchEvent(element, shownCollapseEvent); - }); - } - - /** - * Collapse the designated `Element`. - * @param {Collapse} self the `Collapse` instance - */ - function collapseContent(self) { - const { - element, parent, triggers, - } = self; - - dispatchEvent(element, hideCollapseEvent); - - if (hideCollapseEvent.defaultPrevented) return; - - Timer.set(element, () => {}, 17); - if (parent) Timer.set(parent, () => {}, 17); - - setElementStyle(element, { height: `${element.scrollHeight}px` }); - - removeClass(element, collapseString); - removeClass(element, showClass); - addClass(element, collapsingClass); - - reflow(element); - setElementStyle(element, { height: '0px' }); - - emulateTransitionEnd(element, () => { - Timer.clear(element); - /* istanbul ignore else */ - if (parent) Timer.clear(parent); - - triggers.forEach((btn) => setAttribute(btn, ariaExpanded, 'false')); - - removeClass(element, collapsingClass); - addClass(element, collapseString); - - setElementStyle(element, { height: '' }); - - dispatchEvent(element, hiddenCollapseEvent); - }); - } - - /** - * Toggles on/off the event listener(s) of the `Collapse` instance. - * @param {Collapse} self the `Collapse` instance - * @param {boolean=} add when `true`, the event listener is added - */ - function toggleCollapseHandler(self, add) { - const action = add ? addListener : removeListener; - const { triggers } = self; - - /* istanbul ignore else */ - if (triggers.length) { - triggers.forEach((btn) => action(btn, mouseclickEvent, collapseClickHandler)); - } - } - - // COLLAPSE EVENT HANDLER - // ====================== - /** - * Handles the `click` event for the `Collapse` instance. - * @param {MouseEvent} e the `Event` object - */ - function collapseClickHandler(e) { - const { target } = e; // our target is `HTMLElement` - const trigger = target && closest(target, collapseToggleSelector); - const element = trigger && getTargetElement(trigger); - const self = element && getCollapseInstance(element); - /* istanbul ignore else */ - if (self) self.toggle(); - - // event target is anchor link #398 - if (trigger && trigger.tagName === 'A') e.preventDefault(); - } - - // COLLAPSE DEFINITION - // =================== - - /** Returns a new `Colapse` instance. */ - class Collapse extends BaseComponent { - /** - * @param {HTMLElement | string} target and `Element` that matches the selector - * @param {BSN.Options.Collapse=} config instance options - */ - constructor(target, config) { - super(target, config); - // bind - const self = this; - - // initialization element - const { element, options } = self; - const doc = getDocument(element); - - // set triggering elements - /** @type {HTMLElement[]} */ - self.triggers = [...querySelectorAll(collapseToggleSelector, doc)] - .filter((btn) => getTargetElement(btn) === element); - - // set parent accordion - /** @type {HTMLElement?} */ - self.parent = querySelector(options.parent, doc) - || getTargetElement(element) || null; - - // add event listeners - toggleCollapseHandler(self, true); - } - - /* eslint-disable */ - /** - * Returns component name string. - */ - get name() { return collapseComponent; } - /** - * Returns component default options. - */ - get defaults() { return collapseDefaults; } - /* eslint-enable */ - - // COLLAPSE PUBLIC METHODS - // ======================= - /** Toggles the visibility of the collapse. */ - toggle() { - const self = this; - if (!hasClass(self.element, showClass)) self.show(); - else self.hide(); - } - - /** Hides the collapse. */ - hide() { - const self = this; - const { triggers, element } = self; - if (Timer.get(element)) return; - - collapseContent(self); - /* istanbul ignore else */ - if (triggers.length) { - triggers.forEach((btn) => addClass(btn, `${collapseString}d`)); - } - } - - /** Shows the collapse. */ - show() { - const self = this; - const { - element, parent, triggers, - } = self; - let activeCollapse; - let activeCollapseInstance; - - if (parent) { - activeCollapse = [...querySelectorAll(`.${collapseString}.${showClass}`, parent)] - .find((i) => getCollapseInstance(i)); - activeCollapseInstance = activeCollapse && getCollapseInstance(activeCollapse); - } - - if ((!parent || !Timer.get(parent)) && !Timer.get(element)) { - if (activeCollapseInstance && activeCollapse !== element) { - collapseContent(activeCollapseInstance); - activeCollapseInstance.triggers.forEach((btn) => { - addClass(btn, `${collapseString}d`); - }); - } - - expandCollapse(self); - /* istanbul ignore else */ - if (triggers.length) { - triggers.forEach((btn) => removeClass(btn, `${collapseString}d`)); - } - } - } - - /** Remove the `Collapse` component from the target `Element`. */ - dispose() { - const self = this; - toggleCollapseHandler(self); - - super.dispose(); - } - } - - ObjectAssign(Collapse, { - selector: collapseSelector, - init: collapseInitCallback, - getInstance: getCollapseInstance, - }); - - /** - * A global namespace for `focus` event. - * @type {string} - */ - const focusEvent = 'focus'; - - /** - * A global namespace for `keyup` event. - * @type {string} - */ - const keyupEvent = 'keyup'; - - /** - * A global namespace for `scroll` event. - * @type {string} - */ - const scrollEvent = 'scroll'; - - /** - * A global namespace for `resize` event. - * @type {string} - */ - const resizeEvent = 'resize'; - - /** - * A global namespace for `ArrowUp` key. - * @type {string} e.which = 38 equivalent - */ - const keyArrowUp = 'ArrowUp'; - - /** - * A global namespace for `ArrowDown` key. - * @type {string} e.which = 40 equivalent - */ - const keyArrowDown = 'ArrowDown'; - - /** - * A global namespace for `Escape` key. - * @type {string} e.which = 27 equivalent - */ - const keyEscape = 'Escape'; - - /** - * Shortcut for `HTMLElement.hasAttribute()` method. - * @param {HTMLElement} element target element - * @param {string} attribute attribute name - * @returns {boolean} the query result - */ - const hasAttribute = (element, attribute) => element.hasAttribute(attribute); - - /** - * Utility to focus an `HTMLElement` target. - * - * @param {HTMLElement} element is the target - */ - const focus = (element) => element.focus(); - - /** - * Returns the `Window` object of a target node. - * @see https://github.com/floating-ui/floating-ui - * - * @param {(Node | Window)=} node target node - * @returns {Window} the `Window` object - */ - function getWindow(node) { - // node is undefined | NULL - if (!node) return window; - // node instanceof Document - if (isDocument(node)) return node.defaultView; - // node instanceof Node - if (isNode(node)) return node.ownerDocument.defaultView; - // node is instanceof Window - return node; - } - - /** - * Global namespace for `Dropdown` types / classes. - */ - const dropdownMenuClasses = ['dropdown', 'dropup', 'dropstart', 'dropend']; - - /** @type {string} */ - const dropdownComponent = 'Dropdown'; - - /** - * Global namespace for `.dropdown-menu`. - */ - const dropdownMenuClass = 'dropdown-menu'; - - /** - * Checks if an *event.target* or its parent has an `href="#"` value. - * We need to prevent jumping around onclick, don't we? - * - * @param {Node} element the target element - * @returns {boolean} the query result - */ - function isEmptyAnchor(element) { - // `EventTarget` must be `HTMLElement` - const parentAnchor = closest(element, 'A'); - return isHTMLElement(element) - // anchor href starts with # - && ((hasAttribute(element, 'href') && element.href.slice(-1) === '#') - // OR a child of an anchor with href starts with # - || (parentAnchor && hasAttribute(parentAnchor, 'href') - && parentAnchor.href.slice(-1) === '#')); - } - - /* Native JavaScript for Bootstrap 5 | Dropdown - ----------------------------------------------- */ - - // DROPDOWN PRIVATE GC - // =================== - const [ - dropdownString, - dropupString, - dropstartString, - dropendString, - ] = dropdownMenuClasses; - const dropdownSelector = `[${dataBsToggle}="${dropdownString}"]`; - - /** - * Static method which returns an existing `Dropdown` instance associated - * to a target `Element`. - * - * @type {BSN.GetInstance<Dropdown>} - */ - const getDropdownInstance = (element) => getInstance(element, dropdownComponent); - - /** - * A `Dropdown` initialization callback. - * @type {BSN.InitCallback<Dropdown>} - */ - const dropdownInitCallback = (element) => new Dropdown(element); - - // DROPDOWN PRIVATE GC - // =================== - // const dropdownMenuStartClass = `${dropdownMenuClass}-start`; - const dropdownMenuEndClass = `${dropdownMenuClass}-end`; - const verticalClass = [dropdownString, dropupString]; - const horizontalClass = [dropstartString, dropendString]; - const menuFocusTags = ['A', 'BUTTON']; - - const dropdownDefaults = { - offset: 5, // [number] 5(px) - display: 'dynamic', // [dynamic|static] - }; - - // DROPDOWN CUSTOM EVENTS - // ====================== - const showDropdownEvent = OriginalEvent(`show.bs.${dropdownString}`); - const shownDropdownEvent = OriginalEvent(`shown.bs.${dropdownString}`); - const hideDropdownEvent = OriginalEvent(`hide.bs.${dropdownString}`); - const hiddenDropdownEvent = OriginalEvent(`hidden.bs.${dropdownString}`); - - // DROPDOWN PRIVATE METHODS - // ======================== - /** - * Apply specific style or class names to a `.dropdown-menu` to automatically - * accomodate the layout and the page scroll. - * - * @param {Dropdown} self the `Dropdown` instance - */ - function styleDropdown(self) { - const { - element, menu, parentElement, options, - } = self; - const { offset } = options; - - // don't apply any style on mobile view - /* istanbul ignore next: this test requires a navbar */ - if (getElementStyle(menu, 'position') === 'static') return; - - const RTL = isRTL(element); - // const menuStart = hasClass(menu, dropdownMenuStartClass); - const menuEnd = hasClass(menu, dropdownMenuEndClass); - - // reset menu offset and position - const resetProps = ['margin', 'top', 'bottom', 'left', 'right']; - resetProps.forEach((p) => { menu.style[p] = ''; }); - - // set initial position class - // take into account .btn-group parent as .dropdown - // this requires navbar/btn-group/input-group - let positionClass = dropdownMenuClasses.find((c) => hasClass(parentElement, c)) - || /* istanbul ignore next: fallback position */ dropdownString; - - /** @type {Record<string, Record<string, any>>} */ - let dropdownMargin = { - dropdown: [offset, 0, 0], - dropup: [0, 0, offset], - dropstart: RTL ? [-1, 0, 0, offset] : [-1, offset, 0], - dropend: RTL ? [-1, offset, 0] : [-1, 0, 0, offset], - }; - - /** @type {Record<string, Record<string, any>>} */ - const dropdownPosition = { - dropdown: { top: '100%' }, - dropup: { top: 'auto', bottom: '100%' }, - dropstart: RTL ? { left: '100%', right: 'auto' } : { left: 'auto', right: '100%' }, - dropend: RTL ? { left: 'auto', right: '100%' } : { left: '100%', right: 'auto' }, - menuStart: RTL ? { right: 0, left: 'auto' } : { right: 'auto', left: 0 }, - menuEnd: RTL ? { right: 'auto', left: 0 } : { right: 0, left: 'auto' }, - }; - - const { offsetWidth: menuWidth, offsetHeight: menuHeight } = menu; - - const { clientWidth, clientHeight } = getDocumentElement(element); - const { - left: targetLeft, top: targetTop, - width: targetWidth, height: targetHeight, - } = getBoundingClientRect(element); - - // dropstart | dropend - const leftFullExceed = targetLeft - menuWidth - offset < 0; - // dropend - const rightFullExceed = targetLeft + menuWidth + targetWidth + offset >= clientWidth; - // dropstart | dropend - const bottomExceed = targetTop + menuHeight + offset >= clientHeight; - // dropdown - const bottomFullExceed = targetTop + menuHeight + targetHeight + offset >= clientHeight; - // dropup - const topExceed = targetTop - menuHeight - offset < 0; - // dropdown / dropup - const leftExceed = ((!RTL && menuEnd) || (RTL && !menuEnd)) - && targetLeft + targetWidth - menuWidth < 0; - const rightExceed = ((RTL && menuEnd) || (!RTL && !menuEnd)) - && targetLeft + menuWidth >= clientWidth; - - // recompute position - // handle RTL as well - if (horizontalClass.includes(positionClass) && leftFullExceed && rightFullExceed) { - positionClass = dropdownString; - } - if (positionClass === dropstartString && (!RTL ? leftFullExceed : rightFullExceed)) { - positionClass = dropendString; - } - if (positionClass === dropendString && (RTL ? leftFullExceed : rightFullExceed)) { - positionClass = dropstartString; - } - if (positionClass === dropupString && topExceed && !bottomFullExceed) { - positionClass = dropdownString; - } - if (positionClass === dropdownString && bottomFullExceed && !topExceed) { - positionClass = dropupString; - } - - // override position for horizontal classes - if (horizontalClass.includes(positionClass) && bottomExceed) { - ObjectAssign(dropdownPosition[positionClass], { - top: 'auto', bottom: 0, - }); - } - - // override position for vertical classes - if (verticalClass.includes(positionClass) && (leftExceed || rightExceed)) { - // don't realign when menu is wider than window - // in both RTL and non-RTL readability is KING - let posAjust; - if (!leftExceed && rightExceed && !RTL) posAjust = { left: 'auto', right: 0 }; - if (leftExceed && !rightExceed && RTL) posAjust = { left: 0, right: 'auto' }; - if (posAjust) ObjectAssign(dropdownPosition[positionClass], posAjust); - } - - dropdownMargin = dropdownMargin[positionClass]; - setElementStyle(menu, { - ...dropdownPosition[positionClass], - margin: `${dropdownMargin.map((x) => (x ? `${x}px` : x)).join(' ')}`, - }); - - // override dropdown-menu-start | dropdown-menu-end - if (verticalClass.includes(positionClass) && menuEnd) { - /* istanbul ignore else */ - if (menuEnd) { - const endAdjust = (!RTL && leftExceed) || (RTL && rightExceed) - ? 'menuStart' : /* istanbul ignore next */'menuEnd'; - setElementStyle(menu, dropdownPosition[endAdjust]); - } - } - } - - /** - * Returns an `Array` of focusable items in the given dropdown-menu. - * @param {HTMLElement} menu - * @returns {HTMLElement[]} - */ - function getMenuItems(menu) { - return [...menu.children].map((c) => { - if (c && menuFocusTags.includes(c.tagName)) return c; - const { firstElementChild } = c; - if (firstElementChild && menuFocusTags.includes(firstElementChild.tagName)) { - return firstElementChild; - } - return null; - }).filter((c) => c); - } - - /** - * Toggles on/off the listeners for the events that close the dropdown - * as well as event that request a new position for the dropdown. - * - * @param {Dropdown} self the `Dropdown` instance - */ - function toggleDropdownDismiss(self) { - const { element, options } = self; - const action = self.open ? addListener : removeListener; - const doc = getDocument(element); - - action(doc, mouseclickEvent, dropdownDismissHandler); - action(doc, focusEvent, dropdownDismissHandler); - action(doc, keydownEvent, dropdownPreventScroll); - action(doc, keyupEvent, dropdownKeyHandler); - - /* istanbul ignore else */ - if (options.display === 'dynamic') { - [scrollEvent, resizeEvent].forEach((ev) => { - action(getWindow(element), ev, dropdownLayoutHandler, passiveHandler); - }); - } - } - - /** - * Toggles on/off the `click` event listener of the `Dropdown`. - * - * @param {Dropdown} self the `Dropdown` instance - * @param {boolean=} add when `true`, it will add the event listener - */ - function toggleDropdownHandler(self, add) { - const action = add ? addListener : removeListener; - action(self.element, mouseclickEvent, dropdownClickHandler); - } - - /** - * Returns the currently open `.dropdown` element. - * - * @param {(Node | Window)=} element target - * @returns {HTMLElement?} the query result - */ - function getCurrentOpenDropdown(element) { - const currentParent = [...dropdownMenuClasses, 'btn-group', 'input-group'] - .map((c) => getElementsByClassName(`${c} ${showClass}`, getDocument(element))) - .find((x) => x.length); - - if (currentParent && currentParent.length) { - return [...currentParent[0].children] - .find((x) => hasAttribute(x, dataBsToggle)); - } - return null; - } - - // DROPDOWN EVENT HANDLERS - // ======================= - /** - * Handles the `click` event for the `Dropdown` instance. - * - * @param {MouseEvent} e event object - * @this {Document} - */ - function dropdownDismissHandler(e) { - const { target, type } = e; - - /* istanbul ignore next: impossible to satisfy */ - if (!target || !target.closest) return; // some weird FF bug #409 - - const element = getCurrentOpenDropdown(target); - const self = getDropdownInstance(element); - - /* istanbul ignore next */ - if (!self) return; - - const { parentElement, menu } = self; - - const hasData = closest(target, dropdownSelector) !== null; - const isForm = parentElement && parentElement.contains(target) - && (target.tagName === 'form' || closest(target, 'form') !== null); - - if (type === mouseclickEvent && isEmptyAnchor(target)) { - e.preventDefault(); - } - if (type === focusEvent - && (target === element || target === menu || menu.contains(target))) { - return; - } - - /* istanbul ignore else */ - if (isForm || hasData) ; else if (self) { - self.hide(); - } - } - - /** - * Handles `click` event listener for `Dropdown`. - * @this {HTMLElement} - * @param {MouseEvent} e event object - */ - function dropdownClickHandler(e) { - const element = this; - const { target } = e; - const self = getDropdownInstance(element); - - /* istanbul ignore else */ - if (self) { - self.toggle(); - /* istanbul ignore else */ - if (target && isEmptyAnchor(target)) e.preventDefault(); - } - } - - /** - * Prevents scroll when dropdown-menu is visible. - * @param {KeyboardEvent} e event object - */ - function dropdownPreventScroll(e) { - /* istanbul ignore else */ - if ([keyArrowDown, keyArrowUp].includes(e.code)) e.preventDefault(); - } - - /** - * Handles keyboard `keydown` events for `Dropdown`. - * @param {KeyboardEvent} e keyboard key - * @this {Document} - */ - function dropdownKeyHandler(e) { - const { code } = e; - const element = getCurrentOpenDropdown(this); - const self = element && getDropdownInstance(element); - const { activeElement } = element && getDocument(element); - /* istanbul ignore next: impossible to satisfy */ - if (!self || !activeElement) return; - const { menu, open } = self; - const menuItems = getMenuItems(menu); - - // arrow up & down - if (menuItems && menuItems.length && [keyArrowDown, keyArrowUp].includes(code)) { - let idx = menuItems.indexOf(activeElement); - /* istanbul ignore else */ - if (activeElement === element) { - idx = 0; - } else if (code === keyArrowUp) { - idx = idx > 1 ? idx - 1 : 0; - } else if (code === keyArrowDown) { - idx = idx < menuItems.length - 1 ? idx + 1 : idx; - } - /* istanbul ignore else */ - if (menuItems[idx]) focus(menuItems[idx]); - } - - if (keyEscape === code && open) { - self.toggle(); - focus(element); - } - } - - /** - * @this {globalThis} - * @returns {void} - */ - function dropdownLayoutHandler() { - const element = getCurrentOpenDropdown(this); - const self = element && getDropdownInstance(element); - - /* istanbul ignore else */ - if (self && self.open) styleDropdown(self); - } - - // DROPDOWN DEFINITION - // =================== - /** Returns a new Dropdown instance. */ - class Dropdown extends BaseComponent { - /** - * @param {HTMLElement | string} target Element or string selector - * @param {BSN.Options.Dropdown=} config the instance options - */ - constructor(target, config) { - super(target, config); - // bind - const self = this; - - // initialization element - const { element } = self; - const { parentElement } = element; - - // set targets - /** @type {(Element | HTMLElement)} */ - self.parentElement = parentElement; - /** @type {(Element | HTMLElement)} */ - self.menu = querySelector(`.${dropdownMenuClass}`, parentElement); - - // set initial state to closed - /** @type {boolean} */ - self.open = false; - - // add event listener - toggleDropdownHandler(self, true); - } - - /* eslint-disable */ - /** - * Returns component name string. - */ - get name() { return dropdownComponent; } - /** - * Returns component default options. - */ - get defaults() { return dropdownDefaults; } - /* eslint-enable */ - - // DROPDOWN PUBLIC METHODS - // ======================= - /** Shows/hides the dropdown menu to the user. */ - toggle() { - const self = this; - - if (self.open) self.hide(); - else self.show(); - } - - /** Shows the dropdown menu to the user. */ - show() { - const self = this; - const { - element, open, menu, parentElement, - } = self; - - /* istanbul ignore next */ - if (open) return; - - const currentElement = getCurrentOpenDropdown(element); - const currentInstance = currentElement && getDropdownInstance(currentElement); - if (currentInstance) currentInstance.hide(); - - // dispatch event - [showDropdownEvent, shownDropdownEvent].forEach((e) => { - e.relatedTarget = element; - }); - dispatchEvent(parentElement, showDropdownEvent); - if (showDropdownEvent.defaultPrevented) return; - - addClass(menu, showClass); - addClass(parentElement, showClass); - setAttribute(element, ariaExpanded, 'true'); - - // change menu position - styleDropdown(self); - - self.open = !open; - - focus(element); // focus the element - toggleDropdownDismiss(self); - dispatchEvent(parentElement, shownDropdownEvent); - } - - /** Hides the dropdown menu from the user. */ - hide() { - const self = this; - const { - element, open, menu, parentElement, - } = self; - - /* istanbul ignore next */ - if (!open) return; - - [hideDropdownEvent, hiddenDropdownEvent].forEach((e) => { - e.relatedTarget = element; - }); - dispatchEvent(parentElement, hideDropdownEvent); - if (hideDropdownEvent.defaultPrevented) return; - - removeClass(menu, showClass); - removeClass(parentElement, showClass); - setAttribute(element, ariaExpanded, 'false'); - - self.open = !open; - // only re-attach handler if the instance is not disposed - toggleDropdownDismiss(self); - dispatchEvent(parentElement, hiddenDropdownEvent); - } - - /** Removes the `Dropdown` component from the target element. */ - dispose() { - const self = this; - if (self.open) self.hide(); - - toggleDropdownHandler(self); - - super.dispose(); - } - } - - ObjectAssign(Dropdown, { - selector: dropdownSelector, - init: dropdownInitCallback, - getInstance: getDropdownInstance, - }); - - /** - * A global namespace for aria-hidden. - * @type {string} - */ - const ariaHidden = 'aria-hidden'; - - /** - * A global namespace for aria-modal. - * @type {string} - */ - const ariaModal = 'aria-modal'; - - /** - * Shortcut for `HTMLElement.removeAttribute()` method. - * @param {HTMLElement} element target element - * @param {string} attribute attribute name - * @returns {void} - */ - const removeAttribute = (element, attribute) => element.removeAttribute(attribute); - - /** - * Returns the `document.body` or the `<body>` element. - * - * @param {(Node | Window)=} node - * @returns {HTMLBodyElement} - */ - function getDocumentBody(node) { - return getDocument(node).body; - } - - /** @type {string} */ - const modalString = 'modal'; - - /** @type {string} */ - const modalComponent = 'Modal'; - - /** - * Check if target is a `ShadowRoot`. - * - * @param {any} element target - * @returns {boolean} the query result - */ - const isShadowRoot = (element) => (element && element.constructor.name === 'ShadowRoot') - || false; - - /** - * Returns the `parentNode` also going through `ShadowRoot`. - * @see https://github.com/floating-ui/floating-ui - * - * @param {Node} node the target node - * @returns {Node} the apropriate parent node - */ - function getParentNode(node) { - if (node.nodeName === 'HTML') { - return node; - } - - // this is a quicker (but less type safe) way to save quite some bytes from the bundle - return ( - node.assignedSlot // step into the shadow DOM of the parent of a slotted node - || node.parentNode // DOM Element detected - || (isShadowRoot(node) && node.host) // ShadowRoot detected - || getDocumentElement(node) // fallback - ); - } - - /** - * Check if a target element is a `<table>`, `<td>` or `<th>`. - * This specific check is important for determining - * the `offsetParent` of a given element. - * - * @param {any} element the target element - * @returns {boolean} the query result - */ - const isTableElement = (element) => (element && ['TABLE', 'TD', 'TH'].includes(element.tagName)) - || false; - - /** - * Returns an `HTMLElement` to be used as default value for *options.container* - * for `Tooltip` / `Popover` components. - * - * When `getOffset` is *true*, it returns the `offsetParent` for tooltip/popover - * offsets computation similar to **floating-ui**. - * @see https://github.com/floating-ui/floating-ui - * - * @param {HTMLElement} element the target - * @param {boolean=} getOffset when *true* it will return an `offsetParent` - * @returns {ParentNode | Window} the query result - */ - function getElementContainer(element, getOffset) { - const majorBlockTags = ['HTML', 'BODY']; - - if (getOffset) { - /** @type {any} */ - let { offsetParent } = element; - const win = getWindow(element); - - while (offsetParent && (isTableElement(offsetParent) - || (isHTMLElement(offsetParent) - // we must count for both fixed & sticky - && !['sticky', 'fixed'].includes(getElementStyle(offsetParent, 'position'))))) { - offsetParent = offsetParent.offsetParent; - } - - if (!offsetParent || (majorBlockTags.includes(offsetParent.tagName) - || getElementStyle(offsetParent, 'position') === 'static')) { - offsetParent = win; - } - return offsetParent; - } - - /** @type {ParentNode[]} */ - const containers = []; - /** @type {ParentNode} */ - let { parentNode } = element; - - while (parentNode && !majorBlockTags.includes(parentNode.nodeName)) { - parentNode = getParentNode(parentNode); - /* istanbul ignore else */ - if (!(isShadowRoot(parentNode) || !!parentNode.shadowRoot - || isTableElement(parentNode))) { - containers.push(parentNode); - } - } - - return containers.find((c, i) => { - if (getElementStyle(c, 'position') !== 'relative' - && containers.slice(i + 1).every((r) => getElementStyle(r, 'position') === 'static')) { - return c; - } - return null; - }) || getDocumentBody(element); - } - - /** - * Global namespace for components `fixed-top` class. - */ - const fixedTopClass = 'fixed-top'; - - /** - * Global namespace for components `fixed-bottom` class. - */ - const fixedBottomClass = 'fixed-bottom'; - - /** - * Global namespace for components `sticky-top` class. - */ - const stickyTopClass = 'sticky-top'; - - /** - * Global namespace for components `position-sticky` class. - */ - const positionStickyClass = 'position-sticky'; - - /** @param {(HTMLElement | Document)=} parent */ - const getFixedItems = (parent) => [ - ...getElementsByClassName(fixedTopClass, parent), - ...getElementsByClassName(fixedBottomClass, parent), - ...getElementsByClassName(stickyTopClass, parent), - ...getElementsByClassName(positionStickyClass, parent), - ...getElementsByClassName('is-fixed', parent), - ]; - - /** - * Removes *padding* and *overflow* from the `<body>` - * and all spacing from fixed items. - * @param {HTMLElement=} element the target modal/offcanvas - */ - function resetScrollbar(element) { - const bd = getDocumentBody(element); - setElementStyle(bd, { - paddingRight: '', - overflow: '', - }); - - const fixedItems = getFixedItems(bd); - - if (fixedItems.length) { - fixedItems.forEach((fixed) => { - setElementStyle(fixed, { - paddingRight: '', - marginRight: '', - }); - }); - } - } - - /** - * Returns the scrollbar width if the body does overflow - * the window. - * @param {HTMLElement=} element - * @returns {number} the value - */ - function measureScrollbar(element) { - const { clientWidth } = getDocumentElement(element); - const { innerWidth } = getWindow(element); - return Math.abs(innerWidth - clientWidth); - } - - /** - * Sets the `<body>` and fixed items style when modal / offcanvas - * is shown to the user. - * - * @param {HTMLElement} element the target modal/offcanvas - * @param {boolean=} overflow body does overflow or not - */ - function setScrollbar(element, overflow) { - const bd = getDocumentBody(element); - const bodyPad = parseInt(getElementStyle(bd, 'paddingRight'), 10); - const isOpen = getElementStyle(bd, 'overflow') === 'hidden'; - const sbWidth = isOpen && bodyPad ? 0 : measureScrollbar(element); - const fixedItems = getFixedItems(bd); - - /* istanbul ignore else */ - if (overflow) { - setElementStyle(bd, { - overflow: 'hidden', - paddingRight: `${bodyPad + sbWidth}px`, - }); - - /* istanbul ignore else */ - if (fixedItems.length) { - fixedItems.forEach((fixed) => { - const itemPadValue = getElementStyle(fixed, 'paddingRight'); - fixed.style.paddingRight = `${parseInt(itemPadValue, 10) + sbWidth}px`; - /* istanbul ignore else */ - if ([stickyTopClass, positionStickyClass].some((c) => hasClass(fixed, c))) { - const itemMValue = getElementStyle(fixed, 'marginRight'); - fixed.style.marginRight = `${parseInt(itemMValue, 10) - sbWidth}px`; - } - }); - } - } - } - - /** - * This is a shortie for `document.createElement` method - * which allows you to create a new `HTMLElement` for a given `tagName` - * or based on an object with specific non-readonly attributes: - * `id`, `className`, `textContent`, `style`, etc. - * @see https://developer.mozilla.org/en-US/docs/Web/API/Document/createElement - * - * @param {Record<string, string> | string} param `tagName` or object - * @return {HTMLElement} a new `HTMLElement` or `Element` - */ - function createElement(param) { - if (!param) return null; - - if (typeof param === 'string') { - return getDocument().createElement(param); - } - - const { tagName } = param; - const attr = { ...param }; - const newElement = createElement(tagName); - delete attr.tagName; - ObjectAssign(newElement, attr); - return newElement; - } - - /** @type {string} */ - const offcanvasString = 'offcanvas'; - - const backdropString = 'backdrop'; - const modalBackdropClass = `${modalString}-${backdropString}`; - const offcanvasBackdropClass = `${offcanvasString}-${backdropString}`; - const modalActiveSelector = `.${modalString}.${showClass}`; - const offcanvasActiveSelector = `.${offcanvasString}.${showClass}`; - - // any document would suffice - const overlay = createElement('div'); - - /** - * Returns the current active modal / offcancas element. - * @param {HTMLElement=} element the context element - * @returns {HTMLElement?} the requested element - */ - function getCurrentOpen(element) { - return querySelector(`${modalActiveSelector},${offcanvasActiveSelector}`, getDocument(element)); - } - - /** - * Toogles from a Modal overlay to an Offcanvas, or vice-versa. - * @param {boolean=} isModal - */ - function toggleOverlayType(isModal) { - const targetClass = isModal ? modalBackdropClass : offcanvasBackdropClass; - [modalBackdropClass, offcanvasBackdropClass].forEach((c) => { - removeClass(overlay, c); - }); - addClass(overlay, targetClass); - } - - /** - * Append the overlay to DOM. - * @param {HTMLElement} container - * @param {boolean} hasFade - * @param {boolean=} isModal - */ - function appendOverlay(container, hasFade, isModal) { - toggleOverlayType(isModal); - container.append(overlay); - if (hasFade) addClass(overlay, fadeClass); - } - - /** - * Shows the overlay to the user. - */ - function showOverlay() { - if (!hasClass(overlay, showClass)) { - addClass(overlay, showClass); - reflow(overlay); - } - } - - /** - * Hides the overlay from the user. - */ - function hideOverlay() { - removeClass(overlay, showClass); - } - - /** - * Removes the overlay from DOM. - * @param {HTMLElement=} element - */ - function removeOverlay(element) { - if (!getCurrentOpen(element)) { - removeClass(overlay, fadeClass); - overlay.remove(); - resetScrollbar(element); - } - } - - /** - * @param {HTMLElement} element target - * @returns {boolean} - */ - function isVisible(element) { - return isHTMLElement(element) - && getElementStyle(element, 'visibility') !== 'hidden' - && element.offsetParent !== null; - } - - /* Native JavaScript for Bootstrap 5 | Modal - -------------------------------------------- */ - - // MODAL PRIVATE GC - // ================ - const modalSelector = `.${modalString}`; - const modalToggleSelector = `[${dataBsToggle}="${modalString}"]`; - const modalDismissSelector = `[${dataBsDismiss}="${modalString}"]`; - const modalStaticClass = `${modalString}-static`; - - const modalDefaults = { - backdrop: true, // boolean|string - keyboard: true, // boolean - }; - - /** - * Static method which returns an existing `Modal` instance associated - * to a target `Element`. - * - * @type {BSN.GetInstance<Modal>} - */ - const getModalInstance = (element) => getInstance(element, modalComponent); - - /** - * A `Modal` initialization callback. - * @type {BSN.InitCallback<Modal>} - */ - const modalInitCallback = (element) => new Modal(element); - - // MODAL CUSTOM EVENTS - // =================== - const showModalEvent = OriginalEvent(`show.bs.${modalString}`); - const shownModalEvent = OriginalEvent(`shown.bs.${modalString}`); - const hideModalEvent = OriginalEvent(`hide.bs.${modalString}`); - const hiddenModalEvent = OriginalEvent(`hidden.bs.${modalString}`); - - // MODAL PRIVATE METHODS - // ===================== - /** - * Applies special style for the `<body>` and fixed elements - * when a modal instance is shown to the user. - * - * @param {Modal} self the `Modal` instance - */ - function setModalScrollbar(self) { - const { element } = self; - const scrollbarWidth = measureScrollbar(element); - const { clientHeight, scrollHeight } = getDocumentElement(element); - const { clientHeight: modalHeight, scrollHeight: modalScrollHeight } = element; - const modalOverflow = modalHeight !== modalScrollHeight; - - /* istanbul ignore else */ - if (!modalOverflow && scrollbarWidth) { - const pad = !isRTL(element) ? 'paddingRight' : /* istanbul ignore next */'paddingLeft'; - const padStyle = {}; - padStyle[pad] = `${scrollbarWidth}px`; - setElementStyle(element, padStyle); - } - setScrollbar(element, (modalOverflow || clientHeight !== scrollHeight)); - } - - /** - * Toggles on/off the listeners of events that close the modal. - * - * @param {Modal} self the `Modal` instance - * @param {boolean=} add when `true`, event listeners are added - */ - function toggleModalDismiss(self, add) { - const action = add ? addListener : removeListener; - const { element } = self; - action(element, mouseclickEvent, modalDismissHandler); - action(getWindow(element), resizeEvent, self.update, passiveHandler); - action(getDocument(element), keydownEvent, modalKeyHandler); - } - - /** - * Toggles on/off the `click` event listener of the `Modal` instance. - * @param {Modal} self the `Modal` instance - * @param {boolean=} add when `true`, event listener is added - */ - function toggleModalHandler(self, add) { - const action = add ? addListener : removeListener; - const { triggers } = self; - - /* istanbul ignore else */ - if (triggers.length) { - triggers.forEach((btn) => action(btn, mouseclickEvent, modalClickHandler)); - } - } - - /** - * Executes after a modal is hidden to the user. - * @param {Modal} self the `Modal` instance - * @param {Function} callback the `Modal` instance - */ - function afterModalHide(self, callback) { - const { triggers, element, relatedTarget } = self; - removeOverlay(element); - setElementStyle(element, { paddingRight: '', display: '' }); - toggleModalDismiss(self); - - const focusElement = showModalEvent.relatedTarget || triggers.find(isVisible); - /* istanbul ignore else */ - if (focusElement) focus(focusElement); - - /* istanbul ignore else */ - if (callback) callback(); - - hiddenModalEvent.relatedTarget = relatedTarget; - dispatchEvent(element, hiddenModalEvent); - } - - /** - * Executes after a modal is shown to the user. - * @param {Modal} self the `Modal` instance - */ - function afterModalShow(self) { - const { element, relatedTarget } = self; - focus(element); - toggleModalDismiss(self, true); - - shownModalEvent.relatedTarget = relatedTarget; - dispatchEvent(element, shownModalEvent); - } - - /** - * Executes before a modal is shown to the user. - * @param {Modal} self the `Modal` instance - */ - function beforeModalShow(self) { - const { element, hasFade } = self; - setElementStyle(element, { display: 'block' }); - - setModalScrollbar(self); - /* istanbul ignore else */ - if (!getCurrentOpen(element)) { - setElementStyle(getDocumentBody(element), { overflow: 'hidden' }); - } - - addClass(element, showClass); - removeAttribute(element, ariaHidden); - setAttribute(element, ariaModal, 'true'); - - if (hasFade) emulateTransitionEnd(element, () => afterModalShow(self)); - else afterModalShow(self); - } - - /** - * Executes before a modal is hidden to the user. - * @param {Modal} self the `Modal` instance - * @param {Function=} callback when `true` skip animation - */ - function beforeModalHide(self, callback) { - const { - element, options, hasFade, - } = self; - - // callback can also be the transitionEvent object, we wanna make sure it's not - // call is not forced and overlay is visible - if (options.backdrop && !callback && hasFade && hasClass(overlay, showClass) - && !getCurrentOpen(element)) { // AND no modal is visible - hideOverlay(); - emulateTransitionEnd(overlay, () => afterModalHide(self)); - } else { - afterModalHide(self, callback); - } - } - - // MODAL EVENT HANDLERS - // ==================== - /** - * Handles the `click` event listener for modal. - * @param {MouseEvent} e the `Event` object - */ - function modalClickHandler(e) { - const { target } = e; - - const trigger = target && closest(target, modalToggleSelector); - const element = trigger && getTargetElement(trigger); - const self = element && getModalInstance(element); - - /* istanbul ignore else */ - if (trigger && trigger.tagName === 'A') e.preventDefault(); - self.relatedTarget = trigger; - self.toggle(); - } - - /** - * Handles the `keydown` event listener for modal - * to hide the modal when user type the `ESC` key. - * - * @param {KeyboardEvent} e the `Event` object - */ - function modalKeyHandler({ code, target }) { - const element = querySelector(modalActiveSelector, getDocument(target)); - const self = element && getModalInstance(element); - - const { options } = self; - /* istanbul ignore else */ - if (options.keyboard && code === keyEscape // the keyboard option is enabled and the key is 27 - && hasClass(element, showClass)) { // the modal is not visible - self.relatedTarget = null; - self.hide(); - } - } - - /** - * Handles the `click` event listeners that hide the modal. - * - * @this {HTMLElement} - * @param {MouseEvent} e the `Event` object - */ - function modalDismissHandler(e) { - const element = this; - const self = getModalInstance(element); - - // this timer is needed - /* istanbul ignore next: must have a filter */ - if (!self || Timer.get(element)) return; - - const { options, isStatic, modalDialog } = self; - const { backdrop } = options; - const { target } = e; - - const selectedText = getDocument(element).getSelection().toString().length; - const targetInsideDialog = modalDialog.contains(target); - const dismiss = target && closest(target, modalDismissSelector); - - /* istanbul ignore else */ - if (isStatic && !targetInsideDialog) { - Timer.set(element, () => { - addClass(element, modalStaticClass); - emulateTransitionEnd(modalDialog, () => staticTransitionEnd(self)); - }, 17); - } else if (dismiss || (!selectedText && !isStatic && !targetInsideDialog && backdrop)) { - self.relatedTarget = dismiss || null; - self.hide(); - e.preventDefault(); - } - } - - /** - * Handles the `transitionend` event listeners for `Modal`. - * - * @param {Modal} self the `Modal` instance - */ - function staticTransitionEnd(self) { - const { element, modalDialog } = self; - const duration = getElementTransitionDuration(modalDialog) + 17; - removeClass(element, modalStaticClass); - // user must wait for zoom out transition - Timer.set(element, () => Timer.clear(element), duration); - } - - // MODAL DEFINITION - // ================ - /** Returns a new `Modal` instance. */ - class Modal extends BaseComponent { - /** - * @param {HTMLElement | string} target usually the `.modal` element - * @param {BSN.Options.Modal=} config instance options - */ - constructor(target, config) { - super(target, config); - - // bind - const self = this; - - // the modal - const { element } = self; - - // the modal-dialog - /** @type {(HTMLElement)} */ - self.modalDialog = querySelector(`.${modalString}-dialog`, element); - - // modal can have multiple triggering elements - /** @type {HTMLElement[]} */ - self.triggers = [...querySelectorAll(modalToggleSelector, getDocument(element))] - .filter((btn) => getTargetElement(btn) === element); - - // additional internals - /** @type {boolean} */ - self.isStatic = self.options.backdrop === 'static'; - /** @type {boolean} */ - self.hasFade = hasClass(element, fadeClass); - /** @type {HTMLElement?} */ - self.relatedTarget = null; - /** @type {HTMLBodyElement | HTMLElement} */ - self.container = getElementContainer(element); - - // attach event listeners - toggleModalHandler(self, true); - - // bind - self.update = self.update.bind(self); - } - - /* eslint-disable */ - /** - * Returns component name string. - */ - get name() { return modalComponent; } - /** - * Returns component default options. - */ - get defaults() { return modalDefaults; } - /* eslint-enable */ - - // MODAL PUBLIC METHODS - // ==================== - /** Toggles the visibility of the modal. */ - toggle() { - const self = this; - if (hasClass(self.element, showClass)) self.hide(); - else self.show(); - } - - /** Shows the modal to the user. */ - show() { - const self = this; - const { - element, options, hasFade, relatedTarget, container, - } = self; - const { backdrop } = options; - let overlayDelay = 0; - - if (hasClass(element, showClass)) return; - - showModalEvent.relatedTarget = relatedTarget || null; - dispatchEvent(element, showModalEvent); - if (showModalEvent.defaultPrevented) return; - - // we elegantly hide any opened modal/offcanvas - const currentOpen = getCurrentOpen(element); - if (currentOpen && currentOpen !== element) { - const this1 = getModalInstance(currentOpen); - const that1 = this1 - || /* istanbul ignore next */getInstance(currentOpen, 'Offcanvas'); - that1.hide(); - } - - if (backdrop) { - if (!container.contains(overlay)) { - appendOverlay(container, hasFade, true); - } else { - toggleOverlayType(true); - } - - overlayDelay = getElementTransitionDuration(overlay); - - showOverlay(); - setTimeout(() => beforeModalShow(self), overlayDelay); - } else { - beforeModalShow(self); - /* istanbul ignore else */ - if (currentOpen && hasClass(overlay, showClass)) { - hideOverlay(); - } - } - } - - /** - * Hide the modal from the user. - * @param {Function=} callback when defined it will skip animation - */ - hide(callback) { - const self = this; - const { - element, hasFade, relatedTarget, - } = self; - - if (!hasClass(element, showClass)) return; - - hideModalEvent.relatedTarget = relatedTarget || null; - dispatchEvent(element, hideModalEvent); - if (hideModalEvent.defaultPrevented) return; - removeClass(element, showClass); - setAttribute(element, ariaHidden, 'true'); - removeAttribute(element, ariaModal); - - // if (hasFade && callback) { - /* istanbul ignore else */ - if (hasFade) { - emulateTransitionEnd(element, () => beforeModalHide(self, callback)); - } else { - beforeModalHide(self, callback); - } - } - - /** - * Updates the modal layout. - * @this {Modal} the modal instance - */ - update() { - const self = this; - /* istanbul ignore else */ - if (hasClass(self.element, showClass)) setModalScrollbar(self); - } - - /** Removes the `Modal` component from target element. */ - dispose() { - const self = this; - toggleModalHandler(self); - // use callback - self.hide(() => super.dispose()); - } - } - - ObjectAssign(Modal, { - selector: modalSelector, - init: modalInitCallback, - getInstance: getModalInstance, - }); - - /** @type {string} */ - const offcanvasComponent = 'Offcanvas'; - - /* Native JavaScript for Bootstrap 5 | OffCanvas - ------------------------------------------------ */ - - // OFFCANVAS PRIVATE GC - // ==================== - const offcanvasSelector = `.${offcanvasString}`; - const offcanvasToggleSelector = `[${dataBsToggle}="${offcanvasString}"]`; - const offcanvasDismissSelector = `[${dataBsDismiss}="${offcanvasString}"]`; - const offcanvasTogglingClass = `${offcanvasString}-toggling`; - - const offcanvasDefaults = { - backdrop: true, // boolean - keyboard: true, // boolean - scroll: false, // boolean - }; - - /** - * Static method which returns an existing `Offcanvas` instance associated - * to a target `Element`. - * - * @type {BSN.GetInstance<Offcanvas>} - */ - const getOffcanvasInstance = (element) => getInstance(element, offcanvasComponent); - - /** - * An `Offcanvas` initialization callback. - * @type {BSN.InitCallback<Offcanvas>} - */ - const offcanvasInitCallback = (element) => new Offcanvas(element); - - // OFFCANVAS CUSTOM EVENTS - // ======================= - const showOffcanvasEvent = OriginalEvent(`show.bs.${offcanvasString}`); - const shownOffcanvasEvent = OriginalEvent(`shown.bs.${offcanvasString}`); - const hideOffcanvasEvent = OriginalEvent(`hide.bs.${offcanvasString}`); - const hiddenOffcanvasEvent = OriginalEvent(`hidden.bs.${offcanvasString}`); - - // OFFCANVAS PRIVATE METHODS - // ========================= - /** - * Sets additional style for the `<body>` and other elements - * when showing an offcanvas to the user. - * - * @param {Offcanvas} self the `Offcanvas` instance - */ - function setOffCanvasScrollbar(self) { - const { element } = self; - const { clientHeight, scrollHeight } = getDocumentElement(element); - setScrollbar(element, clientHeight !== scrollHeight); - } - - /** - * Toggles on/off the `click` event listeners. - * - * @param {Offcanvas} self the `Offcanvas` instance - * @param {boolean=} add when *true*, listeners are added - */ - function toggleOffcanvasEvents(self, add) { - const action = add ? addListener : removeListener; - self.triggers.forEach((btn) => action(btn, mouseclickEvent, offcanvasTriggerHandler)); - } - - /** - * Toggles on/off the listeners of the events that close the offcanvas. - * - * @param {Offcanvas} self the `Offcanvas` instance - * @param {boolean=} add when *true* listeners are added - */ - function toggleOffCanvasDismiss(self, add) { - const action = add ? addListener : removeListener; - const doc = getDocument(self.element); - action(doc, keydownEvent, offcanvasKeyDismissHandler); - action(doc, mouseclickEvent, offcanvasDismissHandler); - } - - /** - * Executes before showing the offcanvas. - * - * @param {Offcanvas} self the `Offcanvas` instance - */ - function beforeOffcanvasShow(self) { - const { element, options } = self; - - /* istanbul ignore else */ - if (!options.scroll) { - setOffCanvasScrollbar(self); - setElementStyle(getDocumentBody(element), { overflow: 'hidden' }); - } - - addClass(element, offcanvasTogglingClass); - addClass(element, showClass); - setElementStyle(element, { visibility: 'visible' }); - - emulateTransitionEnd(element, () => showOffcanvasComplete(self)); - } - - /** - * Executes before hiding the offcanvas. - * - * @param {Offcanvas} self the `Offcanvas` instance - * @param {Function=} callback the hide callback - */ - function beforeOffcanvasHide(self, callback) { - const { element, options } = self; - const currentOpen = getCurrentOpen(element); - - element.blur(); - - if (!currentOpen && options.backdrop && hasClass(overlay, showClass)) { - hideOverlay(); - emulateTransitionEnd(overlay, () => hideOffcanvasComplete(self, callback)); - } else hideOffcanvasComplete(self, callback); - } - - // OFFCANVAS EVENT HANDLERS - // ======================== - /** - * Handles the `click` event listeners. - * - * @this {HTMLElement} - * @param {MouseEvent} e the `Event` object - */ - function offcanvasTriggerHandler(e) { - const trigger = closest(this, offcanvasToggleSelector); - const element = trigger && getTargetElement(trigger); - const self = element && getOffcanvasInstance(element); - - /* istanbul ignore else */ - if (self) { - self.relatedTarget = trigger; - self.toggle(); - /* istanbul ignore else */ - if (trigger && trigger.tagName === 'A') { - e.preventDefault(); - } - } - } - - /** - * Handles the event listeners that close the offcanvas. - * - * @param {MouseEvent} e the `Event` object - */ - function offcanvasDismissHandler(e) { - const { target } = e; - const element = querySelector(offcanvasActiveSelector, getDocument(target)); - const offCanvasDismiss = querySelector(offcanvasDismissSelector, element); - const self = getOffcanvasInstance(element); - - /* istanbul ignore next: must have a filter */ - if (!self) return; - - const { options, triggers } = self; - const { backdrop } = options; - const trigger = closest(target, offcanvasToggleSelector); - const selection = getDocument(element).getSelection(); - - if (overlay.contains(target) && backdrop === 'static') return; - - /* istanbul ignore else */ - if (!(selection && selection.toString().length) - && ((!element.contains(target) && backdrop - && /* istanbul ignore next */(!trigger || triggers.includes(target))) - || (offCanvasDismiss && offCanvasDismiss.contains(target)))) { - self.relatedTarget = offCanvasDismiss && offCanvasDismiss.contains(target) - ? offCanvasDismiss : null; - self.hide(); - } - - /* istanbul ignore next */ - if (trigger && trigger.tagName === 'A') e.preventDefault(); - } - - /** - * Handles the `keydown` event listener for offcanvas - * to hide it when user type the `ESC` key. - * - * @param {KeyboardEvent} e the `Event` object - */ - function offcanvasKeyDismissHandler({ code, target }) { - const element = querySelector(offcanvasActiveSelector, getDocument(target)); - - const self = getOffcanvasInstance(element); - - /* istanbul ignore next: must filter */ - if (!self) return; - - /* istanbul ignore else */ - if (self.options.keyboard && code === keyEscape) { - self.relatedTarget = null; - self.hide(); - } - } - - /** - * Handles the `transitionend` when showing the offcanvas. - * - * @param {Offcanvas} self the `Offcanvas` instance - */ - function showOffcanvasComplete(self) { - const { element } = self; - removeClass(element, offcanvasTogglingClass); - - removeAttribute(element, ariaHidden); - setAttribute(element, ariaModal, 'true'); - setAttribute(element, 'role', 'dialog'); - - dispatchEvent(element, shownOffcanvasEvent); - - toggleOffCanvasDismiss(self, true); - focus(element); - } - - /** - * Handles the `transitionend` when hiding the offcanvas. - * - * @param {Offcanvas} self the `Offcanvas` instance - * @param {Function} callback the hide callback - */ - function hideOffcanvasComplete(self, callback) { - const { element, triggers } = self; - - setAttribute(element, ariaHidden, 'true'); - removeAttribute(element, ariaModal); - removeAttribute(element, 'role'); - setElementStyle(element, { visibility: '' }); - - const visibleTrigger = showOffcanvasEvent.relatedTarget || triggers.find((x) => isVisible(x)); - /* istanbul ignore else */ - if (visibleTrigger) focus(visibleTrigger); - - removeOverlay(element); - - dispatchEvent(element, hiddenOffcanvasEvent); - removeClass(element, offcanvasTogglingClass); - - // must check for open instances - if (!getCurrentOpen(element)) { - toggleOffCanvasDismiss(self); - } - // callback - if (callback) callback(); - } - - // OFFCANVAS DEFINITION - // ==================== - /** Returns a new `Offcanvas` instance. */ - class Offcanvas extends BaseComponent { - /** - * @param {HTMLElement | string} target usually an `.offcanvas` element - * @param {BSN.Options.Offcanvas=} config instance options - */ - constructor(target, config) { - super(target, config); - const self = this; - - // instance element - const { element } = self; - - // all the triggering buttons - /** @type {HTMLElement[]} */ - self.triggers = [...querySelectorAll(offcanvasToggleSelector, getDocument(element))] - .filter((btn) => getTargetElement(btn) === element); - - // additional instance property - /** @type {HTMLBodyElement | HTMLElement} */ - self.container = getElementContainer(element); - /** @type {HTMLElement?} */ - self.relatedTarget = null; - - // attach event listeners - toggleOffcanvasEvents(self, true); - } - - /* eslint-disable */ - /** - * Returns component name string. - */ - get name() { return offcanvasComponent; } - /** - * Returns component default options. - */ - get defaults() { return offcanvasDefaults; } - /* eslint-enable */ - - // OFFCANVAS PUBLIC METHODS - // ======================== - /** Shows or hides the offcanvas from the user. */ - toggle() { - const self = this; - if (hasClass(self.element, showClass)) self.hide(); - else self.show(); - } - - /** Shows the offcanvas to the user. */ - show() { - const self = this; - const { - element, options, container, relatedTarget, - } = self; - let overlayDelay = 0; - - if (hasClass(element, showClass)) return; - - showOffcanvasEvent.relatedTarget = relatedTarget; - shownOffcanvasEvent.relatedTarget = relatedTarget; - dispatchEvent(element, showOffcanvasEvent); - if (showOffcanvasEvent.defaultPrevented) return; - - // we elegantly hide any opened modal/offcanvas - const currentOpen = getCurrentOpen(element); - if (currentOpen && currentOpen !== element) { - const this1 = getOffcanvasInstance(currentOpen); - const that1 = this1 - || /* istanbul ignore next */getInstance(currentOpen, 'Modal'); - that1.hide(); - } - - if (options.backdrop) { - if (!container.contains(overlay)) { - appendOverlay(container, true); - } else { - toggleOverlayType(); - } - - overlayDelay = getElementTransitionDuration(overlay); - showOverlay(); - - setTimeout(() => beforeOffcanvasShow(self), overlayDelay); - } else { - beforeOffcanvasShow(self); - /* istanbul ignore else */ - if (currentOpen && hasClass(overlay, showClass)) { - hideOverlay(); - } - } - } - - /** - * Hides the offcanvas from the user. - * @param {Function=} callback when `true` it will skip animation - */ - hide(callback) { - const self = this; - const { element, relatedTarget } = self; - - if (!hasClass(element, showClass)) return; - - hideOffcanvasEvent.relatedTarget = relatedTarget; - hiddenOffcanvasEvent.relatedTarget = relatedTarget; - dispatchEvent(element, hideOffcanvasEvent); - if (hideOffcanvasEvent.defaultPrevented) return; - - addClass(element, offcanvasTogglingClass); - removeClass(element, showClass); - - if (!callback) { - emulateTransitionEnd(element, () => beforeOffcanvasHide(self, callback)); - } else beforeOffcanvasHide(self, callback); - } - - /** Removes the `Offcanvas` from the target element. */ - dispose() { - const self = this; - toggleOffcanvasEvents(self); - self.hide(() => super.dispose()); - } - } - - ObjectAssign(Offcanvas, { - selector: offcanvasSelector, - init: offcanvasInitCallback, - getInstance: getOffcanvasInstance, - }); - - /** @type {string} */ - const popoverString = 'popover'; - - /** @type {string} */ - const popoverComponent = 'Popover'; - - /** @type {string} */ - const tooltipString = 'tooltip'; - - /** - * Returns a template for Popover / Tooltip. - * - * @param {string} tipType the expected markup type - * @returns {string} the template markup - */ - function getTipTemplate(tipType) { - const isTooltip = tipType === tooltipString; - const bodyClass = isTooltip ? `${tipType}-inner` : `${tipType}-body`; - const header = !isTooltip ? `<h3 class="${tipType}-header"></h3>` : ''; - const arrow = `<div class="${tipType}-arrow"></div>`; - const body = `<div class="${bodyClass}"></div>`; - return `<div class="${tipType}" role="${tooltipString}">${header + arrow + body}</div>`; - } - - /** - * Checks if an element is an `<svg>` (or any type of SVG element), - * `<img>` or `<video>`. - * - * *Tooltip* / *Popover* works different with media elements. - * @param {any} element the target element - * @returns {boolean} the query result - */ - - const isMedia = (element) => ( - element - && element.nodeType === 1 - && ['SVG', 'Image', 'Video'].some((s) => element.constructor.name.includes(s))) || false; - - /** - * Returns an `{x,y}` object with the target - * `HTMLElement` / `Node` scroll position. - * - * @see https://github.com/floating-ui/floating-ui - * - * @param {HTMLElement | Window} element target node / element - * @returns {{x: number, y: number}} the scroll tuple - */ - function getNodeScroll(element) { - const isWin = 'scrollX' in element; - const x = isWin ? element.scrollX : element.scrollLeft; - const y = isWin ? element.scrollY : element.scrollTop; - - return { x, y }; - } - - /** - * Checks if a target `HTMLElement` is affected by scale. - * @see https://github.com/floating-ui/floating-ui - * - * @param {HTMLElement} element target - * @returns {boolean} the query result - */ - function isScaledElement(element) { - if (!element || !isHTMLElement(element)) return false; - const { width, height } = getBoundingClientRect(element); - const { offsetWidth, offsetHeight } = element; - return Math.round(width) !== offsetWidth - || Math.round(height) !== offsetHeight; - } - - /** - * Returns the rect relative to an offset parent. - * @see https://github.com/floating-ui/floating-ui - * - * @param {HTMLElement} element target - * @param {ParentNode | Window} offsetParent the container / offset parent - * @param {{x: number, y: number}} scroll the offsetParent scroll position - * @returns {SHORTY.OffsetRect} - */ - function getRectRelativeToOffsetParent(element, offsetParent, scroll) { - const isParentAnElement = isHTMLElement(offsetParent); - const rect = getBoundingClientRect(element, isParentAnElement && isScaledElement(offsetParent)); - const offsets = { x: 0, y: 0 }; - - /* istanbul ignore next */ - if (isParentAnElement) { - const offsetRect = getBoundingClientRect(offsetParent, true); - offsets.x = offsetRect.x + offsetParent.clientLeft; - offsets.y = offsetRect.y + offsetParent.clientTop; - } - - return { - x: rect.left + scroll.x - offsets.x, - y: rect.top + scroll.y - offsets.y, - width: rect.width, - height: rect.height, - }; - } - - /** @type {Record<string, string>} */ - const tipClassPositions = { - top: 'top', - bottom: 'bottom', - left: 'start', - right: 'end', - }; - - /** - * Style popovers and tooltips. - * @param {BSN.Tooltip | BSN.Popover} self the `Popover` / `Tooltip` instance - * @param {PointerEvent=} e event object - */ - function styleTip(self, e) { - const tipClasses = /\b(top|bottom|start|end)+/; - const { - element, tooltip, options, arrow, offsetParent, - } = self; - const tipPositions = { ...tipClassPositions }; - - const RTL = isRTL(element); - if (RTL) { - tipPositions.left = 'end'; - tipPositions.right = 'start'; - } - - // reset tooltip style (top: 0, left: 0 works best) - setElementStyle(tooltip, { - // top: '0px', left: '0px', right: '', bottom: '', - top: '', left: '', right: '', bottom: '', - }); - const isPopover = self.name === popoverComponent; - const { - offsetWidth: tipWidth, offsetHeight: tipHeight, - } = tooltip; - const { - clientWidth: htmlcw, clientHeight: htmlch, - } = getDocumentElement(element); - const { container } = options; - let { placement } = options; - const { - left: parentLeft, right: parentRight, top: parentTop, - } = getBoundingClientRect(container, true); - const { - clientWidth: parentCWidth, offsetWidth: parentOWidth, - } = container; - const scrollbarWidth = Math.abs(parentCWidth - parentOWidth); - // const tipAbsolute = getElementStyle(tooltip, 'position') === 'absolute'; - const parentPosition = getElementStyle(container, 'position'); - // const absoluteParent = parentPosition === 'absolute'; - const fixedParent = parentPosition === 'fixed'; - const staticParent = parentPosition === 'static'; - const stickyParent = parentPosition === 'sticky'; - const isSticky = stickyParent && parentTop === parseFloat(getElementStyle(container, 'top')); - // const absoluteTarget = getElementStyle(element, 'position') === 'absolute'; - // const stickyFixedParent = ['sticky', 'fixed'].includes(parentPosition); - const leftBoundry = RTL && fixedParent ? scrollbarWidth : 0; - const rightBoundry = fixedParent ? parentCWidth + parentLeft + (RTL ? scrollbarWidth : 0) - : parentCWidth + parentLeft + (htmlcw - parentRight) - 1; - const { - width: elemWidth, - height: elemHeight, - left: elemRectLeft, - right: elemRectRight, - top: elemRectTop, - } = getBoundingClientRect(element, true); - - const scroll = getNodeScroll(offsetParent); - const { x, y } = getRectRelativeToOffsetParent(element, offsetParent, scroll); - // reset arrow style - setElementStyle(arrow, { - top: '', left: '', right: '', bottom: '', - }); - let topPosition; - let leftPosition; - let rightPosition; - let arrowTop; - let arrowLeft; - let arrowRight; - - const arrowWidth = arrow.offsetWidth || 0; - const arrowHeight = arrow.offsetHeight || 0; - const arrowAdjust = arrowWidth / 2; - - // check placement - let topExceed = elemRectTop - tipHeight - arrowHeight < 0; - let bottomExceed = elemRectTop + tipHeight + elemHeight - + arrowHeight >= htmlch; - let leftExceed = elemRectLeft - tipWidth - arrowWidth < leftBoundry; - let rightExceed = elemRectLeft + tipWidth + elemWidth - + arrowWidth >= rightBoundry; - - const horizontal = ['left', 'right']; - const vertical = ['top', 'bottom']; - - topExceed = horizontal.includes(placement) - ? elemRectTop + elemHeight / 2 - tipHeight / 2 - arrowHeight < 0 - : topExceed; - bottomExceed = horizontal.includes(placement) - ? elemRectTop + tipHeight / 2 + elemHeight / 2 + arrowHeight >= htmlch - : bottomExceed; - leftExceed = vertical.includes(placement) - ? elemRectLeft + elemWidth / 2 - tipWidth / 2 < leftBoundry - : leftExceed; - rightExceed = vertical.includes(placement) - ? elemRectLeft + tipWidth / 2 + elemWidth / 2 >= rightBoundry - : rightExceed; - - // first remove side positions if both left and right limits are exceeded - // we usually fall back to top|bottom - placement = (horizontal.includes(placement)) && leftExceed && rightExceed ? 'top' : placement; - // second, recompute placement - placement = placement === 'top' && topExceed ? 'bottom' : placement; - placement = placement === 'bottom' && bottomExceed ? 'top' : placement; - placement = placement === 'left' && leftExceed ? 'right' : placement; - placement = placement === 'right' && rightExceed ? 'left' : placement; - - // update tooltip/popover class - if (!tooltip.className.includes(placement)) { - tooltip.className = tooltip.className.replace(tipClasses, tipPositions[placement]); - } - - // compute tooltip / popover coordinates - /* istanbul ignore else */ - if (horizontal.includes(placement)) { // secondary|side positions - if (placement === 'left') { // LEFT - leftPosition = x - tipWidth - (isPopover ? arrowWidth : 0); - } else { // RIGHT - leftPosition = x + elemWidth + (isPopover ? arrowWidth : 0); - } - - // adjust top and arrow - if (topExceed) { - topPosition = y; - topPosition += (isSticky ? -parentTop - scroll.y : 0); - - arrowTop = elemHeight / 2 - arrowWidth; - } else if (bottomExceed) { - topPosition = y - tipHeight + elemHeight; - topPosition += (isSticky ? -parentTop - scroll.y : 0); - - arrowTop = tipHeight - elemHeight / 2 - arrowWidth; - } else { - topPosition = y - tipHeight / 2 + elemHeight / 2; - topPosition += (isSticky ? -parentTop - scroll.y : 0); - - arrowTop = tipHeight / 2 - arrowHeight / 2; - } - } else if (vertical.includes(placement)) { - if (e && isMedia(element)) { - let eX = 0; - let eY = 0; - if (staticParent) { - eX = e.pageX; - eY = e.pageY; - } else { // fixedParent | stickyParent - eX = e.clientX - parentLeft + (fixedParent ? scroll.x : 0); - eY = e.clientY - parentTop + (fixedParent ? scroll.y : 0); - } - - // some weird RTL bug - eX -= RTL && fixedParent && scrollbarWidth ? scrollbarWidth : 0; - - if (placement === 'top') { - topPosition = eY - tipHeight - arrowWidth; - } else { - topPosition = eY + arrowWidth; - } - - // adjust (left | right) and also the arrow - if (e.clientX - tipWidth / 2 < leftBoundry) { - leftPosition = 0; - arrowLeft = eX - arrowAdjust; - } else if (e.clientX + tipWidth / 2 > rightBoundry) { - leftPosition = 'auto'; - rightPosition = 0; - arrowRight = rightBoundry - eX - arrowAdjust; - arrowRight -= fixedParent ? parentLeft + (RTL ? scrollbarWidth : 0) : 0; - - // normal top/bottom - } else { - leftPosition = eX - tipWidth / 2; - arrowLeft = tipWidth / 2 - arrowAdjust; - } - } else { - if (placement === 'top') { - topPosition = y - tipHeight - (isPopover ? arrowHeight : 0); - } else { // BOTTOM - topPosition = y + elemHeight + (isPopover ? arrowHeight : 0); - } - - // adjust left | right and also the arrow - if (leftExceed) { - leftPosition = 0; - arrowLeft = x + elemWidth / 2 - arrowAdjust; - } else if (rightExceed) { - leftPosition = 'auto'; - rightPosition = 0; - arrowRight = elemWidth / 2 + rightBoundry - elemRectRight - arrowAdjust; - } else { - leftPosition = x - tipWidth / 2 + elemWidth / 2; - arrowLeft = tipWidth / 2 - arrowAdjust; - } - } - } - - // apply style to tooltip/popover - setElementStyle(tooltip, { - top: `${topPosition}px`, - left: leftPosition === 'auto' ? leftPosition : `${leftPosition}px`, - right: rightPosition !== undefined ? `${rightPosition}px` : '', - }); - - // update arrow placement - /* istanbul ignore else */ - if (isHTMLElement(arrow)) { - if (arrowTop !== undefined) { - arrow.style.top = `${arrowTop}px`; - } - if (arrowLeft !== undefined) { - arrow.style.left = `${arrowLeft}px`; - } else if (arrowRight !== undefined) { - arrow.style.right = `${arrowRight}px`; - } - } - } - - const tooltipDefaults = { - /** @type {string} */ - template: getTipTemplate(tooltipString), - /** @type {string?} */ - title: null, // string - /** @type {string?} */ - customClass: null, // string | null - /** @type {string} */ - trigger: 'hover focus', - /** @type {string?} */ - placement: 'top', // string - /** @type {((c:string)=>string)?} */ - sanitizeFn: null, // function - /** @type {boolean} */ - animation: true, // bool - /** @type {number} */ - delay: 200, // number - /** @type {HTMLElement?} */ - container: null, - }; - - /** - * A global namespace for aria-describedby. - * @type {string} - */ - const ariaDescribedBy = 'aria-describedby'; - - /** - * A global namespace for `mousedown` event. - * @type {string} - */ - const mousedownEvent = 'mousedown'; - - /** - * A global namespace for `mousemove` event. - * @type {string} - */ - const mousemoveEvent = 'mousemove'; - - /** - * A global namespace for `focusin` event. - * @type {string} - */ - const focusinEvent = 'focusin'; - - /** - * A global namespace for `focusout` event. - * @type {string} - */ - const focusoutEvent = 'focusout'; - - /** - * A global namespace for `hover` event. - * @type {string} - */ - const mousehoverEvent = 'hover'; - - /** - * A global namespace for `touchstart` event. - * @type {string} - */ - const touchstartEvent = 'touchstart'; - - let elementUID = 0; - let elementMapUID = 0; - const elementIDMap = new Map(); - - /** - * Returns a unique identifier for popover, tooltip, scrollspy. - * - * @param {HTMLElement} element target element - * @param {string=} key predefined key - * @returns {number} an existing or new unique ID - */ - function getUID(element, key) { - let result = key ? elementUID : elementMapUID; - - if (key) { - const elID = getUID(element); - const elMap = elementIDMap.get(elID) || new Map(); - if (!elementIDMap.has(elID)) { - elementIDMap.set(elID, elMap); - } - if (!elMap.has(key)) { - elMap.set(key, result); - elementUID += 1; - } else result = elMap.get(key); - } else { - const elkey = element.id || element; - - if (!elementIDMap.has(elkey)) { - elementIDMap.set(elkey, result); - elementMapUID += 1; - } else result = elementIDMap.get(elkey); - } - return result; - } - - /** - * Checks if an object is a `Function`. - * - * @param {any} fn the target object - * @returns {boolean} the query result - */ - const isFunction = (fn) => (fn && fn.constructor.name === 'Function') || false; - - const { userAgentData: uaDATA } = navigator; - - /** - * A global namespace for `userAgentData` object. - */ - const userAgentData = uaDATA; - - const { userAgent: userAgentString } = navigator; - - /** - * A global namespace for `navigator.userAgent` string. - */ - const userAgent = userAgentString; - - const appleBrands = /(iPhone|iPod|iPad)/; - - /** - * A global `boolean` for Apple browsers. - * @type {boolean} - */ - const isApple = userAgentData ? userAgentData.brands.some((x) => appleBrands.test(x.brand)) - : /* istanbul ignore next */appleBrands.test(userAgent); - - /** - * Global namespace for `data-bs-title` attribute. - */ - const dataOriginalTitle = 'data-original-title'; - - /** @type {string} */ - const tooltipComponent = 'Tooltip'; - - /** - * Checks if an object is a `NodeList`. - * => equivalent to `object instanceof NodeList` - * - * @param {any} object the target object - * @returns {boolean} the query result - */ - const isNodeList = (object) => (object && object.constructor.name === 'NodeList') || false; - - /** - * Shortcut for `typeof SOMETHING === "string"`. - * - * @param {any} str input value - * @returns {boolean} the query result - */ - const isString = (str) => typeof str === 'string'; - - /** - * Shortcut for `Array.isArray()` static method. - * - * @param {any} arr array-like iterable object - * @returns {boolean} the query result - */ - const isArray = (arr) => Array.isArray(arr); - - /** - * Append an existing `Element` to Popover / Tooltip component or HTML - * markup string to be parsed & sanitized to be used as popover / tooltip content. - * - * @param {HTMLElement} element target - * @param {Node | string} content the `Element` to append / string - * @param {ReturnType<any>} sanitizeFn a function to sanitize string content - */ - function setHtml(element, content, sanitizeFn) { - /* istanbul ignore next */ - if (!isHTMLElement(element) || (isString(content) && !content.length)) return; - - /* istanbul ignore else */ - if (isString(content)) { - let dirty = content.trim(); // fixing #233 - if (isFunction(sanitizeFn)) dirty = sanitizeFn(dirty); - - const win = getWindow(element); - const domParser = new win.DOMParser(); - const tempDocument = domParser.parseFromString(dirty, 'text/html'); - element.append(...[...tempDocument.body.childNodes]); - } else if (isHTMLElement(content)) { - element.append(content); - } else if (isNodeList(content) - || (isArray(content) && content.every(isNode))) { - element.append(...[...content]); - } - } - - /** - * Creates a new tooltip / popover. - * - * @param {BSN.Popover | BSN.Tooltip} self the `Tooltip` / `Popover` instance - */ - function createTip(self) { - const { id, element, options } = self; - const { - animation, customClass, sanitizeFn, placement, dismissible, - title, content, template, btnClose, - } = options; - const isTooltip = self.name === tooltipComponent; - const tipString = isTooltip ? tooltipString : popoverString; - const tipPositions = { ...tipClassPositions }; - let titleParts = []; - let contentParts = []; - - if (isRTL(element)) { - tipPositions.left = 'end'; - tipPositions.right = 'start'; - } - - // set initial popover class - const placementClass = `bs-${tipString}-${tipPositions[placement]}`; - - // load template - /** @type {HTMLElement?} */ - let tooltipTemplate; - if (isHTMLElement(template)) { - tooltipTemplate = template; - } else { - const htmlMarkup = createElement('div'); - setHtml(htmlMarkup, template, sanitizeFn); - tooltipTemplate = htmlMarkup.firstChild; - } - - // set popover markup - self.tooltip = isHTMLElement(tooltipTemplate) && tooltipTemplate.cloneNode(true); - - const { tooltip } = self; - - // set id and role attributes - setAttribute(tooltip, 'id', id); - setAttribute(tooltip, 'role', tooltipString); - - const bodyClass = isTooltip ? `${tooltipString}-inner` : `${popoverString}-body`; - const tooltipHeader = isTooltip ? null : querySelector(`.${popoverString}-header`, tooltip); - const tooltipBody = querySelector(`.${bodyClass}`, tooltip); - - // set arrow and enable access for styleTip - self.arrow = querySelector(`.${tipString}-arrow`, tooltip); - const { arrow } = self; - - if (isHTMLElement(title)) titleParts = [title.cloneNode(true)]; - else { - const tempTitle = createElement('div'); - setHtml(tempTitle, title, sanitizeFn); - titleParts = [...[...tempTitle.childNodes]]; - } - - if (isHTMLElement(content)) contentParts = [content.cloneNode(true)]; - else { - const tempContent = createElement('div'); - setHtml(tempContent, content, sanitizeFn); - contentParts = [...[...tempContent.childNodes]]; - } - - // set dismissible button - if (dismissible) { - if (title) { - if (isHTMLElement(btnClose)) titleParts = [...titleParts, btnClose.cloneNode(true)]; - else { - const tempBtn = createElement('div'); - setHtml(tempBtn, btnClose, sanitizeFn); - titleParts = [...titleParts, tempBtn.firstChild]; - } - } else { - /* istanbul ignore else */ - if (tooltipHeader) tooltipHeader.remove(); - if (isHTMLElement(btnClose)) contentParts = [...contentParts, btnClose.cloneNode(true)]; - else { - const tempBtn = createElement('div'); - setHtml(tempBtn, btnClose, sanitizeFn); - contentParts = [...contentParts, tempBtn.firstChild]; - } - } - } - - // fill the template with content from options / data attributes - // also sanitize title && content - /* istanbul ignore else */ - if (!isTooltip) { - /* istanbul ignore else */ - if (title && tooltipHeader) setHtml(tooltipHeader, titleParts, sanitizeFn); - /* istanbul ignore else */ - if (content && tooltipBody) setHtml(tooltipBody, contentParts, sanitizeFn); - // set btn - self.btn = querySelector('.btn-close', tooltip); - } else if (title && tooltipBody) setHtml(tooltipBody, title, sanitizeFn); - - // Bootstrap 5.2.x - addClass(tooltip, 'position-absolute'); - addClass(arrow, 'position-absolute'); - - // set popover animation and placement - /* istanbul ignore else */ - if (!hasClass(tooltip, tipString)) addClass(tooltip, tipString); - /* istanbul ignore else */ - if (animation && !hasClass(tooltip, fadeClass)) addClass(tooltip, fadeClass); - /* istanbul ignore else */ - if (customClass && !hasClass(tooltip, customClass)) { - addClass(tooltip, customClass); - } - /* istanbul ignore else */ - if (!hasClass(tooltip, placementClass)) addClass(tooltip, placementClass); - } - - /** - * @param {HTMLElement} tip target - * @param {ParentNode} container parent container - * @returns {boolean} - */ - function isVisibleTip(tip, container) { - return isHTMLElement(tip) && container.contains(tip); - } - - /* Native JavaScript for Bootstrap 5 | Tooltip - ---------------------------------------------- */ - - // TOOLTIP PRIVATE GC - // ================== - const tooltipSelector = `[${dataBsToggle}="${tooltipString}"],[data-tip="${tooltipString}"]`; - const titleAttr = 'title'; - - /** - * Static method which returns an existing `Tooltip` instance associated - * to a target `Element`. - * - * @type {BSN.GetInstance<Tooltip>} - */ - let getTooltipInstance = (element) => getInstance(element, tooltipComponent); - - /** - * A `Tooltip` initialization callback. - * @type {BSN.InitCallback<Tooltip>} - */ - const tooltipInitCallback = (element) => new Tooltip(element); - - // TOOLTIP PRIVATE METHODS - // ======================= - /** - * Removes the tooltip from the DOM. - * - * @param {Tooltip} self the `Tooltip` instance - */ - function removeTooltip(self) { - const { element, tooltip } = self; - removeAttribute(element, ariaDescribedBy); - tooltip.remove(); - } - - /** - * Executes after the instance has been disposed. - * - * @param {Tooltip} self the `Tooltip` instance - * @param {Function=} callback the parent dispose callback - */ - function disposeTooltipComplete(self, callback) { - const { element } = self; - toggleTooltipHandlers(self); - - /* istanbul ignore else */ - if (hasAttribute(element, dataOriginalTitle) && self.name === tooltipComponent) { - toggleTooltipTitle(self); - } - /* istanbul ignore else */ - if (callback) callback(); - } - - /** - * Toggles on/off the special `Tooltip` event listeners. - * - * @param {Tooltip} self the `Tooltip` instance - * @param {boolean=} add when `true`, event listeners are added - */ - function toggleTooltipAction(self, add) { - const action = add ? addListener : removeListener; - const { element } = self; - - action(getDocument(element), touchstartEvent, self.handleTouch, passiveHandler); - - /* istanbul ignore else */ - if (!isMedia(element)) { - [scrollEvent, resizeEvent].forEach((ev) => { - action(getWindow(element), ev, self.update, passiveHandler); - }); - } - } - - /** - * Executes after the tooltip was shown to the user. - * - * @param {Tooltip} self the `Tooltip` instance - */ - function tooltipShownAction(self) { - const { element } = self; - const shownTooltipEvent = OriginalEvent(`shown.bs.${toLowerCase(self.name)}`); - - toggleTooltipAction(self, true); - dispatchEvent(element, shownTooltipEvent); - Timer.clear(element, 'in'); - } - - /** - * Executes after the tooltip was hidden to the user. - * - * @param {Tooltip} self the `Tooltip` instance - * @param {Function=} callback the dispose callback - */ - function tooltipHiddenAction(self, callback) { - const { element } = self; - const hiddenTooltipEvent = OriginalEvent(`hidden.bs.${toLowerCase(self.name)}`); - - toggleTooltipAction(self); - removeTooltip(self); - dispatchEvent(element, hiddenTooltipEvent); - if (isFunction(callback)) callback(); - Timer.clear(element, 'out'); - } - - /** - * Toggles on/off the `Tooltip` event listeners. - * - * @param {Tooltip} self the `Tooltip` instance - * @param {boolean=} add when `true`, event listeners are added - */ - function toggleTooltipHandlers(self, add) { - const action = add ? addListener : removeListener; - // btn is only for dismissible popover - const { element, options, btn } = self; - const { trigger, dismissible } = options; - - if (trigger.includes('manual')) return; - - self.enabled = !!add; - - /** @type {string[]} */ - const triggerOptions = trigger.split(' '); - const elemIsMedia = isMedia(element); - - if (elemIsMedia) { - action(element, mousemoveEvent, self.update, passiveHandler); - } - - triggerOptions.forEach((tr) => { - /* istanbul ignore else */ - if (elemIsMedia || tr === mousehoverEvent) { - action(element, mousedownEvent, self.show); - action(element, mouseenterEvent, self.show); - - /* istanbul ignore else */ - if (dismissible && btn) { - action(btn, mouseclickEvent, self.hide); - } else { - action(element, mouseleaveEvent, self.hide); - action(getDocument(element), touchstartEvent, self.handleTouch, passiveHandler); - } - } else if (tr === mouseclickEvent) { - action(element, tr, (!dismissible ? self.toggle : self.show)); - } else if (tr === focusEvent) { - action(element, focusinEvent, self.show); - /* istanbul ignore else */ - if (!dismissible) action(element, focusoutEvent, self.hide); - /* istanbul ignore else */ - if (isApple) { - action(element, mouseclickEvent, () => focus(element)); - } - } - }); - } - - /** - * Toggles on/off the `Tooltip` event listeners that hide/update the tooltip. - * - * @param {Tooltip} self the `Tooltip` instance - * @param {boolean=} add when `true`, event listeners are added - */ - function toggleTooltipOpenHandlers(self, add) { - const action = add ? addListener : removeListener; - const { element, options, offsetParent } = self; - const { container } = options; - const { offsetHeight, scrollHeight } = container; - const parentModal = closest(element, `.${modalString}`); - const parentOffcanvas = closest(element, `.${offcanvasString}`); - - /* istanbul ignore else */ - if (!isMedia(element)) { - const win = getWindow(element); - const overflow = offsetHeight !== scrollHeight; - const scrollTarget = overflow || offsetParent !== win ? container : win; - action(win, resizeEvent, self.update, passiveHandler); - action(scrollTarget, scrollEvent, self.update, passiveHandler); - } - - // dismiss tooltips inside modal / offcanvas - if (parentModal) action(parentModal, `hide.bs.${modalString}`, self.hide); - if (parentOffcanvas) action(parentOffcanvas, `hide.bs.${offcanvasString}`, self.hide); - } - - /** - * Toggles the `title` and `data-original-title` attributes. - * - * @param {Tooltip} self the `Tooltip` instance - * @param {string=} content when `true`, event listeners are added - */ - function toggleTooltipTitle(self, content) { - // [0 - add, 1 - remove] | [0 - remove, 1 - add] - const titleAtt = [dataOriginalTitle, titleAttr]; - const { element } = self; - - setAttribute(element, titleAtt[content ? 0 : 1], - (content || getAttribute(element, titleAtt[0]))); - removeAttribute(element, titleAtt[content ? 1 : 0]); - } - - // TOOLTIP DEFINITION - // ================== - /** Creates a new `Tooltip` instance. */ - class Tooltip extends BaseComponent { - /** - * @param {HTMLElement | string} target the target element - * @param {BSN.Options.Tooltip=} config the instance options - */ - constructor(target, config) { - super(target, config); - - // bind - const self = this; - const { element } = self; - const isTooltip = self.name === tooltipComponent; - const tipString = isTooltip ? tooltipString : popoverString; - const tipComponent = isTooltip ? tooltipComponent : popoverComponent; - - /* istanbul ignore next: this is to set Popover too */ - getTooltipInstance = (elem) => getInstance(elem, tipComponent); - - // additional properties - /** @type {any} */ - self.tooltip = {}; - if (!isTooltip) { - /** @type {any?} */ - self.btn = null; - } - /** @type {any} */ - self.arrow = {}; - /** @type {any} */ - self.offsetParent = {}; - /** @type {boolean} */ - self.enabled = true; - /** @type {string} Set unique ID for `aria-describedby`. */ - self.id = `${tipString}-${getUID(element, tipString)}`; - - // instance options - const { options } = self; - - // invalidate - if ((!options.title && isTooltip) || (!isTooltip && !options.content)) { - // throw Error(`${this.name} Error: target has no content set.`); - return; - } - - const container = querySelector(options.container, getDocument(element)); - const idealContainer = getElementContainer(element); - - // bypass container option when its position is static/relative - self.options.container = !container || (container - && ['static', 'relative'].includes(getElementStyle(container, 'position'))) - ? idealContainer - : /* istanbul ignore next */container || getDocumentBody(element); - - // reset default options - tooltipDefaults[titleAttr] = null; - - // all functions bind - self.handleTouch = self.handleTouch.bind(self); - self.update = self.update.bind(self); - self.show = self.show.bind(self); - self.hide = self.hide.bind(self); - self.toggle = self.toggle.bind(self); - - // set title attributes and add event listeners - /* istanbul ignore else */ - if (hasAttribute(element, titleAttr) && isTooltip) { - toggleTooltipTitle(self, options.title); - } - - // create tooltip here - createTip(self); - - // attach events - toggleTooltipHandlers(self, true); - } - - /* eslint-disable */ - /** - * Returns component name string. - */ - get name() { return tooltipComponent; } - /** - * Returns component default options. - */ - get defaults() { return tooltipDefaults; } - /* eslint-enable */ - - // TOOLTIP PUBLIC METHODS - // ====================== - /** - * Shows the tooltip. - * - * @param {Event=} e the `Event` object - * @this {Tooltip} - */ - show(e) { - const self = this; - const { - options, tooltip, element, id, - } = self; - const { container, animation } = options; - const outTimer = Timer.get(element, 'out'); - - Timer.clear(element, 'out'); - - if (tooltip && !outTimer && !isVisibleTip(tooltip, container)) { - Timer.set(element, () => { - const showTooltipEvent = OriginalEvent(`show.bs.${toLowerCase(self.name)}`); - dispatchEvent(element, showTooltipEvent); - if (showTooltipEvent.defaultPrevented) return; - - // append to container - container.append(tooltip); - setAttribute(element, ariaDescribedBy, `#${id}`); - // set offsetParent - self.offsetParent = getElementContainer(tooltip, true); - - self.update(e); - toggleTooltipOpenHandlers(self, true); - - /* istanbul ignore else */ - if (!hasClass(tooltip, showClass)) addClass(tooltip, showClass); - /* istanbul ignore else */ - if (animation) emulateTransitionEnd(tooltip, () => tooltipShownAction(self)); - else tooltipShownAction(self); - }, 17, 'in'); - } - } - - /** - * Hides the tooltip. - * - * @this {Tooltip} the Tooltip instance - * @param {Function=} callback the dispose callback - */ - hide(callback) { - const self = this; - const { options, tooltip, element } = self; - const { container, animation, delay } = options; - - Timer.clear(element, 'in'); - - /* istanbul ignore else */ - if (tooltip && isVisibleTip(tooltip, container)) { - Timer.set(element, () => { - const hideTooltipEvent = OriginalEvent(`hide.bs.${toLowerCase(self.name)}`); - dispatchEvent(element, hideTooltipEvent); - - if (hideTooltipEvent.defaultPrevented) return; - - removeClass(tooltip, showClass); - toggleTooltipOpenHandlers(self); - - /* istanbul ignore else */ - if (animation) emulateTransitionEnd(tooltip, () => tooltipHiddenAction(self, callback)); - else tooltipHiddenAction(self, callback); - }, delay + 17, 'out'); - } - } - - /** - * Updates the tooltip position. - * - * @param {Event=} e the `Event` object - * @this {Tooltip} the `Tooltip` instance - */ - update(e) { - styleTip(this, e); - } - - /** - * Toggles the tooltip visibility. - * - * @param {Event=} e the `Event` object - * @this {Tooltip} the instance - */ - toggle(e) { - const self = this; - const { tooltip, options } = self; - - if (!isVisibleTip(tooltip, options.container)) self.show(e); - else self.hide(); - } - - /** Enables the tooltip. */ - enable() { - const self = this; - const { enabled } = self; - /* istanbul ignore else */ - if (!enabled) { - toggleTooltipHandlers(self, true); - self.enabled = !enabled; - } - } - - /** Disables the tooltip. */ - disable() { - const self = this; - const { - tooltip, options, enabled, - } = self; - const { animation, container } = options; - /* istanbul ignore else */ - if (enabled) { - if (isVisibleTip(tooltip, container) && animation) { - self.hide(() => toggleTooltipHandlers(self)); - } else { - toggleTooltipHandlers(self); - } - self.enabled = !enabled; - } - } - - /** Toggles the `disabled` property. */ - toggleEnabled() { - const self = this; - if (!self.enabled) self.enable(); - else self.disable(); - } - - /** - * Handles the `touchstart` event listener for `Tooltip` - * @this {Tooltip} - * @param {TouchEvent} e the `Event` object - */ - handleTouch({ target }) { - const { tooltip, element } = this; - - /* istanbul ignore next */ - if (tooltip.contains(target) || target === element - || (target && element.contains(target))) ; else { - this.hide(); - } - } - - /** Removes the `Tooltip` from the target element. */ - dispose() { - const self = this; - const { tooltip, options } = self; - const callback = () => disposeTooltipComplete(self, () => super.dispose()); - - if (options.animation && isVisibleTip(tooltip, options.container)) { - self.options.delay = 0; // reset delay - self.hide(callback); - } else { - callback(); - } - } - } - - ObjectAssign(Tooltip, { - selector: tooltipSelector, - init: tooltipInitCallback, - getInstance: getTooltipInstance, - styleTip, - }); - - /* Native JavaScript for Bootstrap 5 | Popover - ---------------------------------------------- */ - - // POPOVER PRIVATE GC - // ================== - const popoverSelector = `[${dataBsToggle}="${popoverString}"],[data-tip="${popoverString}"]`; - - const popoverDefaults = { - ...tooltipDefaults, - /** @type {string} */ - template: getTipTemplate(popoverString), - /** @type {string} */ - btnClose: '<button class="btn-close" aria-label="Close"></button>', - /** @type {boolean} */ - dismissible: false, - /** @type {string?} */ - content: null, - }; - - // POPOVER DEFINITION - // ================== - /** Returns a new `Popover` instance. */ - class Popover extends Tooltip { - /* eslint-disable -- we want to specify Popover Options */ - /** - * @param {HTMLElement | string} target the target element - * @param {BSN.Options.Popover=} config the instance options - */ - constructor(target, config) { - super(target, config); - } - /** - * Returns component name string. - */ - get name() { return popoverComponent; } - /** - * Returns component default options. - */ - get defaults() { return popoverDefaults; } - /* eslint-enable */ - - /* extend original `show()` */ - show() { - super.show(); - // btn only exists within dismissible popover - const { options, btn } = this; - /* istanbul ignore else */ - if (options.dismissible && btn) setTimeout(() => focus(btn), 17); - } - } - - /** - * Static method which returns an existing `Popover` instance associated - * to a target `Element`. - * - * @type {BSN.GetInstance<Popover>} - */ - const getPopoverInstance = (element) => getInstance(element, popoverComponent); - - /** - * A `Popover` initialization callback. - * @type {BSN.InitCallback<Popover>} - */ - const popoverInitCallback = (element) => new Popover(element); - - ObjectAssign(Popover, { - selector: popoverSelector, - init: popoverInitCallback, - getInstance: getPopoverInstance, - styleTip, - }); - - /** - * Shortcut for `HTMLElement.getElementsByTagName` method. Some `Node` elements - * like `ShadowRoot` do not support `getElementsByTagName`. - * - * @param {string} selector the tag name - * @param {ParentNode=} parent optional Element to look into - * @return {HTMLCollectionOf<HTMLElement>} the 'HTMLCollection' - */ - function getElementsByTagName(selector, parent) { - const lookUp = isNode(parent) ? parent : getDocument(); - return lookUp.getElementsByTagName(selector); - } - - /** @type {string} */ - const scrollspyString = 'scrollspy'; - - /** @type {string} */ - const scrollspyComponent = 'ScrollSpy'; - - /* Native JavaScript for Bootstrap 5 | ScrollSpy - ------------------------------------------------ */ - - // SCROLLSPY PRIVATE GC - // ==================== - const scrollspySelector = '[data-bs-spy="scroll"]'; - - const scrollspyDefaults = { - offset: 10, - target: null, - }; - - /** - * Static method which returns an existing `ScrollSpy` instance associated - * to a target `Element`. - * - * @type {BSN.GetInstance<ScrollSpy>} - */ - const getScrollSpyInstance = (element) => getInstance(element, scrollspyComponent); - - /** - * A `ScrollSpy` initialization callback. - * @type {BSN.InitCallback<ScrollSpy>} - */ - const scrollspyInitCallback = (element) => new ScrollSpy(element); - - // SCROLLSPY CUSTOM EVENT - // ====================== - const activateScrollSpy = OriginalEvent(`activate.bs.${scrollspyString}`); - - // SCROLLSPY PRIVATE METHODS - // ========================= - /** - * Update the state of all items. - * @param {ScrollSpy} self the `ScrollSpy` instance - */ - function updateSpyTargets(self) { - const { - target, scrollTarget, options, itemsLength, scrollHeight, element, - } = self; - const { offset } = options; - const isWin = isWindow(scrollTarget); - - const links = target && getElementsByTagName('A', target); - const scrollHEIGHT = scrollTarget && getScrollHeight(scrollTarget); - - self.scrollTop = isWin ? scrollTarget.scrollY : scrollTarget.scrollTop; - - // only update items/offsets once or with each mutation - /* istanbul ignore else */ - if (links && (itemsLength !== links.length || scrollHEIGHT !== scrollHeight)) { - let href; - let targetItem; - let rect; - - // reset arrays & update - self.items = []; - self.offsets = []; - self.scrollHeight = scrollHEIGHT; - self.maxScroll = self.scrollHeight - getOffsetHeight(self); - - [...links].forEach((link) => { - href = getAttribute(link, 'href'); - targetItem = href && href.charAt(0) === '#' && href.slice(-1) !== '#' - && querySelector(href, getDocument(element)); - - if (targetItem) { - self.items.push(link); - rect = getBoundingClientRect(targetItem); - self.offsets.push((isWin ? rect.top + self.scrollTop : targetItem.offsetTop) - offset); - } - }); - self.itemsLength = self.items.length; - } - } - - /** - * Returns the `scrollHeight` property of the scrolling element. - * @param {Node | Window} scrollTarget the `ScrollSpy` instance - * @return {number} `scrollTarget` height - */ - function getScrollHeight(scrollTarget) { - return isHTMLElement(scrollTarget) - ? scrollTarget.scrollHeight - : getDocumentElement(scrollTarget).scrollHeight; - } - - /** - * Returns the height property of the scrolling element. - * @param {ScrollSpy} params the `ScrollSpy` instance - * @returns {number} - */ - function getOffsetHeight({ element, scrollTarget }) { - return (isWindow(scrollTarget)) - ? scrollTarget.innerHeight - : getBoundingClientRect(element).height; - } - - /** - * Clear all items of the target. - * @param {HTMLElement} target a single item - */ - function clear(target) { - [...getElementsByTagName('A', target)].forEach((item) => { - if (hasClass(item, activeClass)) removeClass(item, activeClass); - }); - } - - /** - * Activates a new item. - * @param {ScrollSpy} self the `ScrollSpy` instance - * @param {HTMLElement} item a single item - */ - function activate(self, item) { - const { target, element } = self; - clear(target); - self.activeItem = item; - addClass(item, activeClass); - - // activate all parents - const parents = []; - let parentItem = item; - while (parentItem !== getDocumentBody(element)) { - parentItem = parentItem.parentElement; - if (hasClass(parentItem, 'nav') || hasClass(parentItem, 'dropdown-menu')) parents.push(parentItem); - } - - parents.forEach((menuItem) => { - /** @type {HTMLElement?} */ - const parentLink = menuItem.previousElementSibling; - - /* istanbul ignore else */ - if (parentLink && !hasClass(parentLink, activeClass)) { - addClass(parentLink, activeClass); - } - }); - - // dispatch - activateScrollSpy.relatedTarget = item; - dispatchEvent(element, activateScrollSpy); - } - - /** - * Toggles on/off the component event listener. - * @param {ScrollSpy} self the `ScrollSpy` instance - * @param {boolean=} add when `true`, listener is added - */ - function toggleSpyHandlers(self, add) { - const action = add ? addListener : removeListener; - action(self.scrollTarget, scrollEvent, self.refresh, passiveHandler); - } - - // SCROLLSPY DEFINITION - // ==================== - /** Returns a new `ScrollSpy` instance. */ - class ScrollSpy extends BaseComponent { - /** - * @param {HTMLElement | string} target the target element - * @param {BSN.Options.ScrollSpy=} config the instance options - */ - constructor(target, config) { - super(target, config); - // bind - const self = this; - - // initialization element & options - const { element, options } = self; - - // additional properties - /** @type {HTMLElement?} */ - self.target = querySelector(options.target, getDocument(element)); - - // invalidate - if (!self.target) return; - - // set initial state - /** @type {HTMLElement | Window} */ - self.scrollTarget = element.clientHeight < element.scrollHeight - ? element : getWindow(element); - /** @type {number} */ - self.scrollTop = 0; - /** @type {number} */ - self.maxScroll = 0; - /** @type {number} */ - self.scrollHeight = 0; - /** @type {HTMLElement?} */ - self.activeItem = null; - /** @type {HTMLElement[]} */ - self.items = []; - /** @type {number} */ - self.itemsLength = 0; - /** @type {number[]} */ - self.offsets = []; - - // bind events - self.refresh = self.refresh.bind(self); - - // add event handlers - toggleSpyHandlers(self, true); - - self.refresh(); - } - - /* eslint-disable */ - /** - * Returns component name string. - */ - get name() { return scrollspyComponent; } - /** - * Returns component default options. - */ - get defaults() { return scrollspyDefaults; } - /* eslint-enable */ - - // SCROLLSPY PUBLIC METHODS - // ======================== - /** Updates all items. */ - refresh() { - const self = this; - const { target } = self; - - // check if target is visible and invalidate - /* istanbul ignore next */ - if (target.offsetHeight === 0) return; - - updateSpyTargets(self); - - const { - scrollTop, maxScroll, itemsLength, items, activeItem, - } = self; - - if (scrollTop >= maxScroll) { - const newActiveItem = items[itemsLength - 1]; - - /* istanbul ignore else */ - if (activeItem !== newActiveItem) { - activate(self, newActiveItem); - } - return; - } - - const { offsets } = self; - - if (activeItem && scrollTop < offsets[0] && offsets[0] > 0) { - self.activeItem = null; - clear(target); - return; - } - - items.forEach((item, i) => { - if (activeItem !== item && scrollTop >= offsets[i] - && (typeof offsets[i + 1] === 'undefined' || scrollTop < offsets[i + 1])) { - activate(self, item); - } - }); - } - - /** Removes `ScrollSpy` from the target element. */ - dispose() { - toggleSpyHandlers(this); - super.dispose(); - } - } - - ObjectAssign(ScrollSpy, { - selector: scrollspySelector, - init: scrollspyInitCallback, - getInstance: getScrollSpyInstance, - }); - - /** - * A global namespace for aria-selected. - * @type {string} - */ - const ariaSelected = 'aria-selected'; - - /** @type {string} */ - const tabString = 'tab'; - - /** @type {string} */ - const tabComponent = 'Tab'; - - /* Native JavaScript for Bootstrap 5 | Tab - ------------------------------------------ */ - - // TAB PRIVATE GC - // ================ - const tabSelector = `[${dataBsToggle}="${tabString}"]`; - - /** - * Static method which returns an existing `Tab` instance associated - * to a target `Element`. - * - * @type {BSN.GetInstance<Tab>} - */ - const getTabInstance = (element) => getInstance(element, tabComponent); - - /** - * A `Tab` initialization callback. - * @type {BSN.InitCallback<Tab>} - */ - const tabInitCallback = (element) => new Tab(element); - - // TAB CUSTOM EVENTS - // ================= - const showTabEvent = OriginalEvent(`show.bs.${tabString}`); - const shownTabEvent = OriginalEvent(`shown.bs.${tabString}`); - const hideTabEvent = OriginalEvent(`hide.bs.${tabString}`); - const hiddenTabEvent = OriginalEvent(`hidden.bs.${tabString}`); - - /** - * Stores the current active tab and its content - * for a given `.nav` element. - * @type {Map<HTMLElement, any>} - */ - const tabPrivate = new Map(); - - // TAB PRIVATE METHODS - // =================== - /** - * Executes after tab transition has finished. - * @param {Tab} self the `Tab` instance - */ - function triggerTabEnd(self) { - const { tabContent, nav } = self; - - /* istanbul ignore else */ - if (tabContent && hasClass(tabContent, collapsingClass)) { - tabContent.style.height = ''; - removeClass(tabContent, collapsingClass); - } - - /* istanbul ignore else */ - if (nav) Timer.clear(nav); - } - - /** - * Executes before showing the tab content. - * @param {Tab} self the `Tab` instance - */ - function triggerTabShow(self) { - const { - element, tabContent, content: nextContent, nav, - } = self; - const { tab } = nav && tabPrivate.get(nav); - - /* istanbul ignore else */ - if (tabContent && hasClass(nextContent, fadeClass)) { - const { currentHeight, nextHeight } = tabPrivate.get(element); - if (currentHeight === nextHeight) { - triggerTabEnd(self); - } else { - // enables height animation - setTimeout(() => { - tabContent.style.height = `${nextHeight}px`; - reflow(tabContent); - emulateTransitionEnd(tabContent, () => triggerTabEnd(self)); - }, 50); - } - } else if (nav) Timer.clear(nav); - - shownTabEvent.relatedTarget = tab; - dispatchEvent(element, shownTabEvent); - } - - /** - * Executes before hiding the tab. - * @param {Tab} self the `Tab` instance - */ - function triggerTabHide(self) { - const { - element, content: nextContent, tabContent, nav, - } = self; - const { tab, content } = nav && tabPrivate.get(nav); - let currentHeight = 0; - - /* istanbul ignore else */ - if (tabContent && hasClass(nextContent, fadeClass)) { - [content, nextContent].forEach((c) => { - addClass(c, 'overflow-hidden'); - }); - currentHeight = content.scrollHeight || /* istanbul ignore next */0; - } - - // update relatedTarget and dispatch event - showTabEvent.relatedTarget = tab; - hiddenTabEvent.relatedTarget = element; - dispatchEvent(element, showTabEvent); - if (showTabEvent.defaultPrevented) return; - - addClass(nextContent, activeClass); - removeClass(content, activeClass); - - /* istanbul ignore else */ - if (tabContent && hasClass(nextContent, fadeClass)) { - const nextHeight = nextContent.scrollHeight; - tabPrivate.set(element, { currentHeight, nextHeight }); - - addClass(tabContent, collapsingClass); - tabContent.style.height = `${currentHeight}px`; - reflow(tabContent); - [content, nextContent].forEach((c) => { - removeClass(c, 'overflow-hidden'); - }); - } - - if (nextContent && hasClass(nextContent, fadeClass)) { - setTimeout(() => { - addClass(nextContent, showClass); - emulateTransitionEnd(nextContent, () => { - triggerTabShow(self); - }); - }, 1); - } else { - addClass(nextContent, showClass); - triggerTabShow(self); - } - - dispatchEvent(tab, hiddenTabEvent); - } - - /** - * Returns the current active tab and its target content. - * @param {Tab} self the `Tab` instance - * @returns {Record<string, any>} the query result - */ - function getActiveTab(self) { - const { nav } = self; - - const activeTabs = getElementsByClassName(activeClass, nav); - /** @type {(HTMLElement)=} */ - let tab; - /* istanbul ignore else */ - if (activeTabs.length === 1 - && !dropdownMenuClasses.some((c) => hasClass(activeTabs[0].parentElement, c))) { - [tab] = activeTabs; - } else if (activeTabs.length > 1) { - tab = activeTabs[activeTabs.length - 1]; - } - const content = tab ? getTargetElement(tab) : null; - return { tab, content }; - } - - /** - * Returns a parent dropdown. - * @param {HTMLElement} element the `Tab` element - * @returns {HTMLElement?} the parent dropdown - */ - function getParentDropdown(element) { - const dropdown = closest(element, `.${dropdownMenuClasses.join(',.')}`); - return dropdown ? querySelector(`.${dropdownMenuClasses[0]}-toggle`, dropdown) : null; - } - - /** - * Toggles on/off the `click` event listener. - * @param {Tab} self the `Tab` instance - * @param {boolean=} add when `true`, event listener is added - */ - function toggleTabHandler(self, add) { - const action = add ? addListener : removeListener; - action(self.element, mouseclickEvent, tabClickHandler); - } - - // TAB EVENT HANDLER - // ================= - /** - * Handles the `click` event listener. - * @this {HTMLElement} - * @param {MouseEvent} e the `Event` object - */ - function tabClickHandler(e) { - const self = getTabInstance(this); - /* istanbul ignore next: must filter */ - if (!self) return; - e.preventDefault(); - - self.show(); - } - - // TAB DEFINITION - // ============== - /** Creates a new `Tab` instance. */ - class Tab extends BaseComponent { - /** - * @param {HTMLElement | string} target the target element - */ - constructor(target) { - super(target); - // bind - const self = this; - - // initialization element - const { element } = self; - const content = getTargetElement(element); - - // no point initializing a tab without a corresponding content - if (!content) return; - - const nav = closest(element, '.nav'); - const container = closest(content, '.tab-content'); - - /** @type {HTMLElement?} */ - self.nav = nav; - /** @type {HTMLElement} */ - self.content = content; - /** @type {HTMLElement?} */ - self.tabContent = container; - - // event targets - /** @type {HTMLElement?} */ - self.dropdown = getParentDropdown(element); - - // show first Tab instance of none is shown - // suggested on #432 - const { tab } = getActiveTab(self); - if (nav && !tab) { - const firstTab = querySelector(tabSelector, nav); - const firstTabContent = firstTab && getTargetElement(firstTab); - - /* istanbul ignore else */ - if (firstTabContent) { - addClass(firstTab, activeClass); - addClass(firstTabContent, showClass); - addClass(firstTabContent, activeClass); - setAttribute(element, ariaSelected, 'true'); - } - } - - // add event listener - toggleTabHandler(self, true); - } - - /* eslint-disable */ - /** - * Returns component name string. - */ - get name() { return tabComponent; } - /* eslint-enable */ - - // TAB PUBLIC METHODS - // ================== - /** Shows the tab to the user. */ - show() { - const self = this; - const { - element, content: nextContent, nav, dropdown, - } = self; - - /* istanbul ignore else */ - if (!(nav && Timer.get(nav)) && !hasClass(element, activeClass)) { - const { tab, content } = getActiveTab(self); - - /* istanbul ignore else */ - if (nav) tabPrivate.set(nav, { tab, content }); - - // update relatedTarget and dispatch - hideTabEvent.relatedTarget = element; - - dispatchEvent(tab, hideTabEvent); - if (hideTabEvent.defaultPrevented) return; - - addClass(element, activeClass); - setAttribute(element, ariaSelected, 'true'); - - const activeDropdown = getParentDropdown(tab); - if (activeDropdown && hasClass(activeDropdown, activeClass)) { - removeClass(activeDropdown, activeClass); - } - - /* istanbul ignore else */ - if (nav) { - const toggleTab = () => { - removeClass(tab, activeClass); - setAttribute(tab, ariaSelected, 'false'); - if (dropdown && !hasClass(dropdown, activeClass)) addClass(dropdown, activeClass); - }; - - if (hasClass(content, fadeClass) || hasClass(nextContent, fadeClass)) { - Timer.set(nav, toggleTab, 1); - } else toggleTab(); - } - - removeClass(content, showClass); - if (hasClass(content, fadeClass)) { - emulateTransitionEnd(content, () => triggerTabHide(self)); - } else { - triggerTabHide(self); - } - } - } - - /** Removes the `Tab` component from the target element. */ - dispose() { - toggleTabHandler(this); - super.dispose(); - } - } - - ObjectAssign(Tab, { - selector: tabSelector, - init: tabInitCallback, - getInstance: getTabInstance, - }); - - /** @type {string} */ - const toastString = 'toast'; - - /** @type {string} */ - const toastComponent = 'Toast'; - - /* Native JavaScript for Bootstrap 5 | Toast - -------------------------------------------- */ - - // TOAST PRIVATE GC - // ================ - const toastSelector = `.${toastString}`; - const toastDismissSelector = `[${dataBsDismiss}="${toastString}"]`; - const toastToggleSelector = `[${dataBsToggle}="${toastString}"]`; - const showingClass = 'showing'; - /** @deprecated */ - const hideClass = 'hide'; - - const toastDefaults = { - animation: true, - autohide: true, - delay: 5000, - }; - - /** - * Static method which returns an existing `Toast` instance associated - * to a target `Element`. - * - * @type {BSN.GetInstance<Toast>} - */ - const getToastInstance = (element) => getInstance(element, toastComponent); - - /** - * A `Toast` initialization callback. - * @type {BSN.InitCallback<Toast>} - */ - const toastInitCallback = (element) => new Toast(element); - - // TOAST CUSTOM EVENTS - // =================== - const showToastEvent = OriginalEvent(`show.bs.${toastString}`); - const shownToastEvent = OriginalEvent(`shown.bs.${toastString}`); - const hideToastEvent = OriginalEvent(`hide.bs.${toastString}`); - const hiddenToastEvent = OriginalEvent(`hidden.bs.${toastString}`); - - // TOAST PRIVATE METHODS - // ===================== - /** - * Executes after the toast is shown to the user. - * @param {Toast} self the `Toast` instance - */ - function showToastComplete(self) { - const { element, options } = self; - removeClass(element, showingClass); - Timer.clear(element, showingClass); - - dispatchEvent(element, shownToastEvent); - /* istanbul ignore else */ - if (options.autohide) { - Timer.set(element, () => self.hide(), options.delay, toastString); - } - } - - /** - * Executes after the toast is hidden to the user. - * @param {Toast} self the `Toast` instance - */ - function hideToastComplete(self) { - const { element } = self; - removeClass(element, showingClass); - removeClass(element, showClass); - addClass(element, hideClass); // B/C - Timer.clear(element, toastString); - dispatchEvent(element, hiddenToastEvent); - } - - /** - * Executes before hiding the toast. - * @param {Toast} self the `Toast` instance - */ - function hideToast(self) { - const { element, options } = self; - addClass(element, showingClass); - - if (options.animation) { - reflow(element); - emulateTransitionEnd(element, () => hideToastComplete(self)); - } else { - hideToastComplete(self); - } - } - - /** - * Executes before showing the toast. - * @param {Toast} self the `Toast` instance - */ - function showToast(self) { - const { element, options } = self; - Timer.set(element, () => { - removeClass(element, hideClass); // B/C - reflow(element); - addClass(element, showClass); - addClass(element, showingClass); - - if (options.animation) { - emulateTransitionEnd(element, () => showToastComplete(self)); - } else { - showToastComplete(self); - } - }, 17, showingClass); - } - - /** - * Toggles on/off the `click` event listener. - * @param {Toast} self the `Toast` instance - * @param {boolean=} add when `true`, it will add the listener - */ - function toggleToastHandlers(self, add) { - const action = add ? addListener : removeListener; - const { - element, triggers, dismiss, options, - } = self; - - /* istanbul ignore else */ - if (dismiss) { - action(dismiss, mouseclickEvent, self.hide); - } - - /* istanbul ignore else */ - if (options.autohide) { - [focusinEvent, focusoutEvent, mouseenterEvent, mouseleaveEvent] - .forEach((e) => action(element, e, interactiveToastHandler)); - } - /* istanbul ignore else */ - if (triggers.length) { - triggers.forEach((btn) => action(btn, mouseclickEvent, toastClickHandler)); - } - } - - // TOAST EVENT HANDLERS - // ==================== - /** - * Executes after the instance has been disposed. - * @param {Toast} self the `Toast` instance - */ - function completeDisposeToast(self) { - Timer.clear(self.element, toastString); - toggleToastHandlers(self); - } - - /** - * Handles the `click` event listener for toast. - * @param {MouseEvent} e the `Event` object - */ - function toastClickHandler(e) { - const { target } = e; - - const trigger = target && closest(target, toastToggleSelector); - const element = trigger && getTargetElement(trigger); - const self = element && getToastInstance(element); - - /* istanbul ignore else */ - if (trigger && trigger.tagName === 'A') e.preventDefault(); - self.relatedTarget = trigger; - self.show(); - } - - /** - * Executes when user interacts with the toast without closing it, - * usually by hovering or focusing it. - * - * @this {HTMLElement} - * @param {MouseEvent} e the `Toast` instance - */ - function interactiveToastHandler(e) { - const element = this; - const self = getToastInstance(element); - const { type, relatedTarget } = e; - - /* istanbul ignore next: a solid filter is required */ - if (!self || (element === relatedTarget || element.contains(relatedTarget))) return; - - if ([mouseenterEvent, focusinEvent].includes(type)) { - Timer.clear(element, toastString); - } else { - Timer.set(element, () => self.hide(), self.options.delay, toastString); - } - } - - // TOAST DEFINITION - // ================ - /** Creates a new `Toast` instance. */ - class Toast extends BaseComponent { - /** - * @param {HTMLElement | string} target the target `.toast` element - * @param {BSN.Options.Toast=} config the instance options - */ - constructor(target, config) { - super(target, config); - // bind - const self = this; - const { element, options } = self; - - // set fadeClass, the options.animation will override the markup - if (options.animation && !hasClass(element, fadeClass)) addClass(element, fadeClass); - else if (!options.animation && hasClass(element, fadeClass)) removeClass(element, fadeClass); - - // dismiss button - /** @type {HTMLElement?} */ - self.dismiss = querySelector(toastDismissSelector, element); - - // toast can have multiple triggering elements - /** @type {HTMLElement[]} */ - self.triggers = [...querySelectorAll(toastToggleSelector, getDocument(element))] - .filter((btn) => getTargetElement(btn) === element); - - // bind - self.show = self.show.bind(self); - self.hide = self.hide.bind(self); - - // add event listener - toggleToastHandlers(self, true); - } - - /* eslint-disable */ - /** - * Returns component name string. - */ - get name() { return toastComponent; } - /** - * Returns component default options. - */ - get defaults() { return toastDefaults; } - /* eslint-enable */ - - /** - * Returns *true* when toast is visible. - */ - get isShown() { return hasClass(this.element, showClass); } - - // TOAST PUBLIC METHODS - // ==================== - /** Shows the toast. */ - show() { - const self = this; - const { element, isShown } = self; - - /* istanbul ignore else */ - if (element && !isShown) { - dispatchEvent(element, showToastEvent); - if (showToastEvent.defaultPrevented) return; - - showToast(self); - } - } - - /** Hides the toast. */ - hide() { - const self = this; - const { element, isShown } = self; - - /* istanbul ignore else */ - if (element && isShown) { - dispatchEvent(element, hideToastEvent); - if (hideToastEvent.defaultPrevented) return; - hideToast(self); - } - } - - /** Removes the `Toast` component from the target element. */ - dispose() { - const self = this; - const { element, isShown } = self; - - /* istanbul ignore else */ - if (isShown) { - removeClass(element, showClass); - } - - completeDisposeToast(self); - - super.dispose(); - } - } - - ObjectAssign(Toast, { - selector: toastSelector, - init: toastInitCallback, - getInstance: getToastInstance, - }); - - /** - * Check if element matches a CSS selector. - * - * @param {HTMLElement} target - * @param {string} selector - * @returns {boolean} - */ - function matches(target, selector) { - return target.matches(selector); - } - - /** @type {Record<string, any>} */ - const componentsList = { - Alert, - Button, - Carousel, - Collapse, - Dropdown, - Modal, - Offcanvas, - Popover, - ScrollSpy, - Tab, - Toast, - Tooltip, - }; - - /** - * Initialize all matched `Element`s for one component. - * @param {BSN.InitCallback<any>} callback - * @param {NodeList | Node[]} collection - */ - function initComponentDataAPI(callback, collection) { - [...collection].forEach((x) => callback(x)); - } - - /** - * Remove one component from a target container element or all in the page. - * @param {string} component the component name - * @param {ParentNode} context parent `Node` - */ - function removeComponentDataAPI(component, context) { - const compData = Data.getAllFor(component); - - if (compData) { - [...compData].forEach((x) => { - const [element, instance] = x; - if (context.contains(element)) instance.dispose(); - }); - } - } - - /** - * Initialize all BSN components for a target container. - * @param {ParentNode=} context parent `Node` - */ - function initCallback(context) { - const lookUp = context && context.nodeName ? context : document; - const elemCollection = [...getElementsByTagName('*', lookUp)]; - - ObjectKeys(componentsList).forEach((comp) => { - const { init, selector } = componentsList[comp]; - initComponentDataAPI(init, elemCollection.filter((item) => matches(item, selector))); - }); - } - - /** - * Remove all BSN components for a target container. - * @param {ParentNode=} context parent `Node` - */ - function removeDataAPI(context) { - const lookUp = context && context.nodeName ? context : document; - - ObjectKeys(componentsList).forEach((comp) => { - removeComponentDataAPI(comp, lookUp); - }); - } - - // bulk initialize all components - if (document.body) initCallback(); - else { - addListener(document, 'DOMContentLoaded', () => initCallback(), { once: true }); - } - - const BSN = { - Alert, - Button, - Carousel, - Collapse, - Dropdown, - Modal, - Offcanvas, - Popover, - ScrollSpy, - Tab, - Toast, - Tooltip, - - initCallback, - removeDataAPI, - Version, - EventListener: Listener, - }; - - return BSN; - -})); diff --git a/src/static/scripts/bootstrap.bundle.js b/src/static/scripts/bootstrap.bundle.js new file mode 100644 index 00000000..3a02ceb3 --- /dev/null +++ b/src/static/scripts/bootstrap.bundle.js @@ -0,0 +1,6313 @@ +/*! + * Bootstrap v5.3.1 (https://getbootstrap.com/) + * Copyright 2011-2023 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors) + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + */ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.bootstrap = factory()); +})(this, (function () { 'use strict'; + + /** + * -------------------------------------------------------------------------- + * Bootstrap dom/data.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + /** + * Constants + */ + + const elementMap = new Map(); + const Data = { + set(element, key, instance) { + if (!elementMap.has(element)) { + elementMap.set(element, new Map()); + } + const instanceMap = elementMap.get(element); + + // make it clear we only want one instance per element + // can be removed later when multiple key/instances are fine to be used + if (!instanceMap.has(key) && instanceMap.size !== 0) { + // eslint-disable-next-line no-console + console.error(`Bootstrap doesn't allow more than one instance per element. Bound instance: ${Array.from(instanceMap.keys())[0]}.`); + return; + } + instanceMap.set(key, instance); + }, + get(element, key) { + if (elementMap.has(element)) { + return elementMap.get(element).get(key) || null; + } + return null; + }, + remove(element, key) { + if (!elementMap.has(element)) { + return; + } + const instanceMap = elementMap.get(element); + instanceMap.delete(key); + + // free up element references if there are no instances left for an element + if (instanceMap.size === 0) { + elementMap.delete(element); + } + } + }; + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/index.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + const MAX_UID = 1000000; + const MILLISECONDS_MULTIPLIER = 1000; + const TRANSITION_END = 'transitionend'; + + /** + * Properly escape IDs selectors to handle weird IDs + * @param {string} selector + * @returns {string} + */ + const parseSelector = selector => { + if (selector && window.CSS && window.CSS.escape) { + // document.querySelector needs escaping to handle IDs (html5+) containing for instance / + selector = selector.replace(/#([^\s"#']+)/g, (match, id) => `#${CSS.escape(id)}`); + } + return selector; + }; + + // Shout-out Angus Croll (https://goo.gl/pxwQGp) + const toType = object => { + if (object === null || object === undefined) { + return `${object}`; + } + return Object.prototype.toString.call(object).match(/\s([a-z]+)/i)[1].toLowerCase(); + }; + + /** + * Public Util API + */ + + const getUID = prefix => { + do { + prefix += Math.floor(Math.random() * MAX_UID); + } while (document.getElementById(prefix)); + return prefix; + }; + const getTransitionDurationFromElement = element => { + if (!element) { + return 0; + } + + // Get transition-duration of the element + let { + transitionDuration, + transitionDelay + } = window.getComputedStyle(element); + const floatTransitionDuration = Number.parseFloat(transitionDuration); + const floatTransitionDelay = Number.parseFloat(transitionDelay); + + // Return 0 if element or transition duration is not found + if (!floatTransitionDuration && !floatTransitionDelay) { + return 0; + } + + // If multiple durations are defined, take the first + transitionDuration = transitionDuration.split(',')[0]; + transitionDelay = transitionDelay.split(',')[0]; + return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER; + }; + const triggerTransitionEnd = element => { + element.dispatchEvent(new Event(TRANSITION_END)); + }; + const isElement$1 = object => { + if (!object || typeof object !== 'object') { + return false; + } + if (typeof object.jquery !== 'undefined') { + object = object[0]; + } + return typeof object.nodeType !== 'undefined'; + }; + const getElement = object => { + // it's a jQuery object or a node element + if (isElement$1(object)) { + return object.jquery ? object[0] : object; + } + if (typeof object === 'string' && object.length > 0) { + return document.querySelector(parseSelector(object)); + } + return null; + }; + const isVisible = element => { + if (!isElement$1(element) || element.getClientRects().length === 0) { + return false; + } + const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible'; + // Handle `details` element as its content may falsie appear visible when it is closed + const closedDetails = element.closest('details:not([open])'); + if (!closedDetails) { + return elementIsVisible; + } + if (closedDetails !== element) { + const summary = element.closest('summary'); + if (summary && summary.parentNode !== closedDetails) { + return false; + } + if (summary === null) { + return false; + } + } + return elementIsVisible; + }; + const isDisabled = element => { + if (!element || element.nodeType !== Node.ELEMENT_NODE) { + return true; + } + if (element.classList.contains('disabled')) { + return true; + } + if (typeof element.disabled !== 'undefined') { + return element.disabled; + } + return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'; + }; + const findShadowRoot = element => { + if (!document.documentElement.attachShadow) { + return null; + } + + // Can find the shadow root otherwise it'll return the document + if (typeof element.getRootNode === 'function') { + const root = element.getRootNode(); + return root instanceof ShadowRoot ? root : null; + } + if (element instanceof ShadowRoot) { + return element; + } + + // when we don't find a shadow root + if (!element.parentNode) { + return null; + } + return findShadowRoot(element.parentNode); + }; + const noop = () => {}; + + /** + * Trick to restart an element's animation + * + * @param {HTMLElement} element + * @return void + * + * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation + */ + const reflow = element => { + element.offsetHeight; // eslint-disable-line no-unused-expressions + }; + + const getjQuery = () => { + if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) { + return window.jQuery; + } + return null; + }; + const DOMContentLoadedCallbacks = []; + const onDOMContentLoaded = callback => { + if (document.readyState === 'loading') { + // add listener on the first call when the document is in loading state + if (!DOMContentLoadedCallbacks.length) { + document.addEventListener('DOMContentLoaded', () => { + for (const callback of DOMContentLoadedCallbacks) { + callback(); + } + }); + } + DOMContentLoadedCallbacks.push(callback); + } else { + callback(); + } + }; + const isRTL = () => document.documentElement.dir === 'rtl'; + const defineJQueryPlugin = plugin => { + onDOMContentLoaded(() => { + const $ = getjQuery(); + /* istanbul ignore if */ + if ($) { + const name = plugin.NAME; + const JQUERY_NO_CONFLICT = $.fn[name]; + $.fn[name] = plugin.jQueryInterface; + $.fn[name].Constructor = plugin; + $.fn[name].noConflict = () => { + $.fn[name] = JQUERY_NO_CONFLICT; + return plugin.jQueryInterface; + }; + } + }); + }; + const execute = (possibleCallback, args = [], defaultValue = possibleCallback) => { + return typeof possibleCallback === 'function' ? possibleCallback(...args) : defaultValue; + }; + const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => { + if (!waitForTransition) { + execute(callback); + return; + } + const durationPadding = 5; + const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding; + let called = false; + const handler = ({ + target + }) => { + if (target !== transitionElement) { + return; + } + called = true; + transitionElement.removeEventListener(TRANSITION_END, handler); + execute(callback); + }; + transitionElement.addEventListener(TRANSITION_END, handler); + setTimeout(() => { + if (!called) { + triggerTransitionEnd(transitionElement); + } + }, emulatedDuration); + }; + + /** + * Return the previous/next element of a list. + * + * @param {array} list The list of elements + * @param activeElement The active element + * @param shouldGetNext Choose to get next or previous element + * @param isCycleAllowed + * @return {Element|elem} The proper element + */ + const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => { + const listLength = list.length; + let index = list.indexOf(activeElement); + + // if the element does not exist in the list return an element + // depending on the direction and if cycle is allowed + if (index === -1) { + return !shouldGetNext && isCycleAllowed ? list[listLength - 1] : list[0]; + } + index += shouldGetNext ? 1 : -1; + if (isCycleAllowed) { + index = (index + listLength) % listLength; + } + return list[Math.max(0, Math.min(index, listLength - 1))]; + }; + + /** + * -------------------------------------------------------------------------- + * Bootstrap dom/event-handler.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const namespaceRegex = /[^.]*(?=\..*)\.|.*/; + const stripNameRegex = /\..*/; + const stripUidRegex = /::\d+$/; + const eventRegistry = {}; // Events storage + let uidEvent = 1; + const customEvents = { + mouseenter: 'mouseover', + mouseleave: 'mouseout' + }; + const nativeEvents = new Set(['click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'mousewheel', 'DOMMouseScroll', 'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'keydown', 'keypress', 'keyup', 'orientationchange', 'touchstart', 'touchmove', 'touchend', 'touchcancel', 'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'gesturestart', 'gesturechange', 'gestureend', 'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'error', 'abort', 'scroll']); + + /** + * Private methods + */ + + function makeEventUid(element, uid) { + return uid && `${uid}::${uidEvent++}` || element.uidEvent || uidEvent++; + } + function getElementEvents(element) { + const uid = makeEventUid(element); + element.uidEvent = uid; + eventRegistry[uid] = eventRegistry[uid] || {}; + return eventRegistry[uid]; + } + function bootstrapHandler(element, fn) { + return function handler(event) { + hydrateObj(event, { + delegateTarget: element + }); + if (handler.oneOff) { + EventHandler.off(element, event.type, fn); + } + return fn.apply(element, [event]); + }; + } + function bootstrapDelegationHandler(element, selector, fn) { + return function handler(event) { + const domElements = element.querySelectorAll(selector); + for (let { + target + } = event; target && target !== this; target = target.parentNode) { + for (const domElement of domElements) { + if (domElement !== target) { + continue; + } + hydrateObj(event, { + delegateTarget: target + }); + if (handler.oneOff) { + EventHandler.off(element, event.type, selector, fn); + } + return fn.apply(target, [event]); + } + } + }; + } + function findHandler(events, callable, delegationSelector = null) { + return Object.values(events).find(event => event.callable === callable && event.delegationSelector === delegationSelector); + } + function normalizeParameters(originalTypeEvent, handler, delegationFunction) { + const isDelegated = typeof handler === 'string'; + // TODO: tooltip passes `false` instead of selector, so we need to check + const callable = isDelegated ? delegationFunction : handler || delegationFunction; + let typeEvent = getTypeEvent(originalTypeEvent); + if (!nativeEvents.has(typeEvent)) { + typeEvent = originalTypeEvent; + } + return [isDelegated, callable, typeEvent]; + } + function addHandler(element, originalTypeEvent, handler, delegationFunction, oneOff) { + if (typeof originalTypeEvent !== 'string' || !element) { + return; + } + let [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction); + + // in case of mouseenter or mouseleave wrap the handler within a function that checks for its DOM position + // this prevents the handler from being dispatched the same way as mouseover or mouseout does + if (originalTypeEvent in customEvents) { + const wrapFunction = fn => { + return function (event) { + if (!event.relatedTarget || event.relatedTarget !== event.delegateTarget && !event.delegateTarget.contains(event.relatedTarget)) { + return fn.call(this, event); + } + }; + }; + callable = wrapFunction(callable); + } + const events = getElementEvents(element); + const handlers = events[typeEvent] || (events[typeEvent] = {}); + const previousFunction = findHandler(handlers, callable, isDelegated ? handler : null); + if (previousFunction) { + previousFunction.oneOff = previousFunction.oneOff && oneOff; + return; + } + const uid = makeEventUid(callable, originalTypeEvent.replace(namespaceRegex, '')); + const fn = isDelegated ? bootstrapDelegationHandler(element, handler, callable) : bootstrapHandler(element, callable); + fn.delegationSelector = isDelegated ? handler : null; + fn.callable = callable; + fn.oneOff = oneOff; + fn.uidEvent = uid; + handlers[uid] = fn; + element.addEventListener(typeEvent, fn, isDelegated); + } + function removeHandler(element, events, typeEvent, handler, delegationSelector) { + const fn = findHandler(events[typeEvent], handler, delegationSelector); + if (!fn) { + return; + } + element.removeEventListener(typeEvent, fn, Boolean(delegationSelector)); + delete events[typeEvent][fn.uidEvent]; + } + function removeNamespacedHandlers(element, events, typeEvent, namespace) { + const storeElementEvent = events[typeEvent] || {}; + for (const [handlerKey, event] of Object.entries(storeElementEvent)) { + if (handlerKey.includes(namespace)) { + removeHandler(element, events, typeEvent, event.callable, event.delegationSelector); + } + } + } + function getTypeEvent(event) { + // allow to get the native events from namespaced events ('click.bs.button' --> 'click') + event = event.replace(stripNameRegex, ''); + return customEvents[event] || event; + } + const EventHandler = { + on(element, event, handler, delegationFunction) { + addHandler(element, event, handler, delegationFunction, false); + }, + one(element, event, handler, delegationFunction) { + addHandler(element, event, handler, delegationFunction, true); + }, + off(element, originalTypeEvent, handler, delegationFunction) { + if (typeof originalTypeEvent !== 'string' || !element) { + return; + } + const [isDelegated, callable, typeEvent] = normalizeParameters(originalTypeEvent, handler, delegationFunction); + const inNamespace = typeEvent !== originalTypeEvent; + const events = getElementEvents(element); + const storeElementEvent = events[typeEvent] || {}; + const isNamespace = originalTypeEvent.startsWith('.'); + if (typeof callable !== 'undefined') { + // Simplest case: handler is passed, remove that listener ONLY. + if (!Object.keys(storeElementEvent).length) { + return; + } + removeHandler(element, events, typeEvent, callable, isDelegated ? handler : null); + return; + } + if (isNamespace) { + for (const elementEvent of Object.keys(events)) { + removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.slice(1)); + } + } + for (const [keyHandlers, event] of Object.entries(storeElementEvent)) { + const handlerKey = keyHandlers.replace(stripUidRegex, ''); + if (!inNamespace || originalTypeEvent.includes(handlerKey)) { + removeHandler(element, events, typeEvent, event.callable, event.delegationSelector); + } + } + }, + trigger(element, event, args) { + if (typeof event !== 'string' || !element) { + return null; + } + const $ = getjQuery(); + const typeEvent = getTypeEvent(event); + const inNamespace = event !== typeEvent; + let jQueryEvent = null; + let bubbles = true; + let nativeDispatch = true; + let defaultPrevented = false; + if (inNamespace && $) { + jQueryEvent = $.Event(event, args); + $(element).trigger(jQueryEvent); + bubbles = !jQueryEvent.isPropagationStopped(); + nativeDispatch = !jQueryEvent.isImmediatePropagationStopped(); + defaultPrevented = jQueryEvent.isDefaultPrevented(); + } + const evt = hydrateObj(new Event(event, { + bubbles, + cancelable: true + }), args); + if (defaultPrevented) { + evt.preventDefault(); + } + if (nativeDispatch) { + element.dispatchEvent(evt); + } + if (evt.defaultPrevented && jQueryEvent) { + jQueryEvent.preventDefault(); + } + return evt; + } + }; + function hydrateObj(obj, meta = {}) { + for (const [key, value] of Object.entries(meta)) { + try { + obj[key] = value; + } catch (_unused) { + Object.defineProperty(obj, key, { + configurable: true, + get() { + return value; + } + }); + } + } + return obj; + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap dom/manipulator.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + function normalizeData(value) { + if (value === 'true') { + return true; + } + if (value === 'false') { + return false; + } + if (value === Number(value).toString()) { + return Number(value); + } + if (value === '' || value === 'null') { + return null; + } + if (typeof value !== 'string') { + return value; + } + try { + return JSON.parse(decodeURIComponent(value)); + } catch (_unused) { + return value; + } + } + function normalizeDataKey(key) { + return key.replace(/[A-Z]/g, chr => `-${chr.toLowerCase()}`); + } + const Manipulator = { + setDataAttribute(element, key, value) { + element.setAttribute(`data-bs-${normalizeDataKey(key)}`, value); + }, + removeDataAttribute(element, key) { + element.removeAttribute(`data-bs-${normalizeDataKey(key)}`); + }, + getDataAttributes(element) { + if (!element) { + return {}; + } + const attributes = {}; + const bsKeys = Object.keys(element.dataset).filter(key => key.startsWith('bs') && !key.startsWith('bsConfig')); + for (const key of bsKeys) { + let pureKey = key.replace(/^bs/, ''); + pureKey = pureKey.charAt(0).toLowerCase() + pureKey.slice(1, pureKey.length); + attributes[pureKey] = normalizeData(element.dataset[key]); + } + return attributes; + }, + getDataAttribute(element, key) { + return normalizeData(element.getAttribute(`data-bs-${normalizeDataKey(key)}`)); + } + }; + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/config.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Class definition + */ + + class Config { + // Getters + static get Default() { + return {}; + } + static get DefaultType() { + return {}; + } + static get NAME() { + throw new Error('You have to implement the static method "NAME", for each component!'); + } + _getConfig(config) { + config = this._mergeConfigObj(config); + config = this._configAfterMerge(config); + this._typeCheckConfig(config); + return config; + } + _configAfterMerge(config) { + return config; + } + _mergeConfigObj(config, element) { + const jsonConfig = isElement$1(element) ? Manipulator.getDataAttribute(element, 'config') : {}; // try to parse + + return { + ...this.constructor.Default, + ...(typeof jsonConfig === 'object' ? jsonConfig : {}), + ...(isElement$1(element) ? Manipulator.getDataAttributes(element) : {}), + ...(typeof config === 'object' ? config : {}) + }; + } + _typeCheckConfig(config, configTypes = this.constructor.DefaultType) { + for (const [property, expectedTypes] of Object.entries(configTypes)) { + const value = config[property]; + const valueType = isElement$1(value) ? 'element' : toType(value); + if (!new RegExp(expectedTypes).test(valueType)) { + throw new TypeError(`${this.constructor.NAME.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`); + } + } + } + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap base-component.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const VERSION = '5.3.1'; + + /** + * Class definition + */ + + class BaseComponent extends Config { + constructor(element, config) { + super(); + element = getElement(element); + if (!element) { + return; + } + this._element = element; + this._config = this._getConfig(config); + Data.set(this._element, this.constructor.DATA_KEY, this); + } + + // Public + dispose() { + Data.remove(this._element, this.constructor.DATA_KEY); + EventHandler.off(this._element, this.constructor.EVENT_KEY); + for (const propertyName of Object.getOwnPropertyNames(this)) { + this[propertyName] = null; + } + } + _queueCallback(callback, element, isAnimated = true) { + executeAfterTransition(callback, element, isAnimated); + } + _getConfig(config) { + config = this._mergeConfigObj(config, this._element); + config = this._configAfterMerge(config); + this._typeCheckConfig(config); + return config; + } + + // Static + static getInstance(element) { + return Data.get(getElement(element), this.DATA_KEY); + } + static getOrCreateInstance(element, config = {}) { + return this.getInstance(element) || new this(element, typeof config === 'object' ? config : null); + } + static get VERSION() { + return VERSION; + } + static get DATA_KEY() { + return `bs.${this.NAME}`; + } + static get EVENT_KEY() { + return `.${this.DATA_KEY}`; + } + static eventName(name) { + return `${name}${this.EVENT_KEY}`; + } + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap dom/selector-engine.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + const getSelector = element => { + let selector = element.getAttribute('data-bs-target'); + if (!selector || selector === '#') { + let hrefAttribute = element.getAttribute('href'); + + // The only valid content that could double as a selector are IDs or classes, + // so everything starting with `#` or `.`. If a "real" URL is used as the selector, + // `document.querySelector` will rightfully complain it is invalid. + // See https://github.com/twbs/bootstrap/issues/32273 + if (!hrefAttribute || !hrefAttribute.includes('#') && !hrefAttribute.startsWith('.')) { + return null; + } + + // Just in case some CMS puts out a full URL with the anchor appended + if (hrefAttribute.includes('#') && !hrefAttribute.startsWith('#')) { + hrefAttribute = `#${hrefAttribute.split('#')[1]}`; + } + selector = hrefAttribute && hrefAttribute !== '#' ? hrefAttribute.trim() : null; + } + return parseSelector(selector); + }; + const SelectorEngine = { + find(selector, element = document.documentElement) { + return [].concat(...Element.prototype.querySelectorAll.call(element, selector)); + }, + findOne(selector, element = document.documentElement) { + return Element.prototype.querySelector.call(element, selector); + }, + children(element, selector) { + return [].concat(...element.children).filter(child => child.matches(selector)); + }, + parents(element, selector) { + const parents = []; + let ancestor = element.parentNode.closest(selector); + while (ancestor) { + parents.push(ancestor); + ancestor = ancestor.parentNode.closest(selector); + } + return parents; + }, + prev(element, selector) { + let previous = element.previousElementSibling; + while (previous) { + if (previous.matches(selector)) { + return [previous]; + } + previous = previous.previousElementSibling; + } + return []; + }, + // TODO: this is now unused; remove later along with prev() + next(element, selector) { + let next = element.nextElementSibling; + while (next) { + if (next.matches(selector)) { + return [next]; + } + next = next.nextElementSibling; + } + return []; + }, + focusableChildren(element) { + const focusables = ['a', 'button', 'input', 'textarea', 'select', 'details', '[tabindex]', '[contenteditable="true"]'].map(selector => `${selector}:not([tabindex^="-"])`).join(','); + return this.find(focusables, element).filter(el => !isDisabled(el) && isVisible(el)); + }, + getSelectorFromElement(element) { + const selector = getSelector(element); + if (selector) { + return SelectorEngine.findOne(selector) ? selector : null; + } + return null; + }, + getElementFromSelector(element) { + const selector = getSelector(element); + return selector ? SelectorEngine.findOne(selector) : null; + }, + getMultipleElementsFromSelector(element) { + const selector = getSelector(element); + return selector ? SelectorEngine.find(selector) : []; + } + }; + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/component-functions.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + const enableDismissTrigger = (component, method = 'hide') => { + const clickEvent = `click.dismiss${component.EVENT_KEY}`; + const name = component.NAME; + EventHandler.on(document, clickEvent, `[data-bs-dismiss="${name}"]`, function (event) { + if (['A', 'AREA'].includes(this.tagName)) { + event.preventDefault(); + } + if (isDisabled(this)) { + return; + } + const target = SelectorEngine.getElementFromSelector(this) || this.closest(`.${name}`); + const instance = component.getOrCreateInstance(target); + + // Method argument is left, for Alert and only, as it doesn't implement the 'hide' method + instance[method](); + }); + }; + + /** + * -------------------------------------------------------------------------- + * Bootstrap alert.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$f = 'alert'; + const DATA_KEY$a = 'bs.alert'; + const EVENT_KEY$b = `.${DATA_KEY$a}`; + const EVENT_CLOSE = `close${EVENT_KEY$b}`; + const EVENT_CLOSED = `closed${EVENT_KEY$b}`; + const CLASS_NAME_FADE$5 = 'fade'; + const CLASS_NAME_SHOW$8 = 'show'; + + /** + * Class definition + */ + + class Alert extends BaseComponent { + // Getters + static get NAME() { + return NAME$f; + } + + // Public + close() { + const closeEvent = EventHandler.trigger(this._element, EVENT_CLOSE); + if (closeEvent.defaultPrevented) { + return; + } + this._element.classList.remove(CLASS_NAME_SHOW$8); + const isAnimated = this._element.classList.contains(CLASS_NAME_FADE$5); + this._queueCallback(() => this._destroyElement(), this._element, isAnimated); + } + + // Private + _destroyElement() { + this._element.remove(); + EventHandler.trigger(this._element, EVENT_CLOSED); + this.dispose(); + } + + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Alert.getOrCreateInstance(this); + if (typeof config !== 'string') { + return; + } + if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { + throw new TypeError(`No method named "${config}"`); + } + data[config](this); + }); + } + } + + /** + * Data API implementation + */ + + enableDismissTrigger(Alert, 'close'); + + /** + * jQuery + */ + + defineJQueryPlugin(Alert); + + /** + * -------------------------------------------------------------------------- + * Bootstrap button.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$e = 'button'; + const DATA_KEY$9 = 'bs.button'; + const EVENT_KEY$a = `.${DATA_KEY$9}`; + const DATA_API_KEY$6 = '.data-api'; + const CLASS_NAME_ACTIVE$3 = 'active'; + const SELECTOR_DATA_TOGGLE$5 = '[data-bs-toggle="button"]'; + const EVENT_CLICK_DATA_API$6 = `click${EVENT_KEY$a}${DATA_API_KEY$6}`; + + /** + * Class definition + */ + + class Button extends BaseComponent { + // Getters + static get NAME() { + return NAME$e; + } + + // Public + toggle() { + // Toggle class and sync the `aria-pressed` attribute with the return value of the `.toggle()` method + this._element.setAttribute('aria-pressed', this._element.classList.toggle(CLASS_NAME_ACTIVE$3)); + } + + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Button.getOrCreateInstance(this); + if (config === 'toggle') { + data[config](); + } + }); + } + } + + /** + * Data API implementation + */ + + EventHandler.on(document, EVENT_CLICK_DATA_API$6, SELECTOR_DATA_TOGGLE$5, event => { + event.preventDefault(); + const button = event.target.closest(SELECTOR_DATA_TOGGLE$5); + const data = Button.getOrCreateInstance(button); + data.toggle(); + }); + + /** + * jQuery + */ + + defineJQueryPlugin(Button); + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/swipe.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$d = 'swipe'; + const EVENT_KEY$9 = '.bs.swipe'; + const EVENT_TOUCHSTART = `touchstart${EVENT_KEY$9}`; + const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY$9}`; + const EVENT_TOUCHEND = `touchend${EVENT_KEY$9}`; + const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY$9}`; + const EVENT_POINTERUP = `pointerup${EVENT_KEY$9}`; + const POINTER_TYPE_TOUCH = 'touch'; + const POINTER_TYPE_PEN = 'pen'; + const CLASS_NAME_POINTER_EVENT = 'pointer-event'; + const SWIPE_THRESHOLD = 40; + const Default$c = { + endCallback: null, + leftCallback: null, + rightCallback: null + }; + const DefaultType$c = { + endCallback: '(function|null)', + leftCallback: '(function|null)', + rightCallback: '(function|null)' + }; + + /** + * Class definition + */ + + class Swipe extends Config { + constructor(element, config) { + super(); + this._element = element; + if (!element || !Swipe.isSupported()) { + return; + } + this._config = this._getConfig(config); + this._deltaX = 0; + this._supportPointerEvents = Boolean(window.PointerEvent); + this._initEvents(); + } + + // Getters + static get Default() { + return Default$c; + } + static get DefaultType() { + return DefaultType$c; + } + static get NAME() { + return NAME$d; + } + + // Public + dispose() { + EventHandler.off(this._element, EVENT_KEY$9); + } + + // Private + _start(event) { + if (!this._supportPointerEvents) { + this._deltaX = event.touches[0].clientX; + return; + } + if (this._eventIsPointerPenTouch(event)) { + this._deltaX = event.clientX; + } + } + _end(event) { + if (this._eventIsPointerPenTouch(event)) { + this._deltaX = event.clientX - this._deltaX; + } + this._handleSwipe(); + execute(this._config.endCallback); + } + _move(event) { + this._deltaX = event.touches && event.touches.length > 1 ? 0 : event.touches[0].clientX - this._deltaX; + } + _handleSwipe() { + const absDeltaX = Math.abs(this._deltaX); + if (absDeltaX <= SWIPE_THRESHOLD) { + return; + } + const direction = absDeltaX / this._deltaX; + this._deltaX = 0; + if (!direction) { + return; + } + execute(direction > 0 ? this._config.rightCallback : this._config.leftCallback); + } + _initEvents() { + if (this._supportPointerEvents) { + EventHandler.on(this._element, EVENT_POINTERDOWN, event => this._start(event)); + EventHandler.on(this._element, EVENT_POINTERUP, event => this._end(event)); + this._element.classList.add(CLASS_NAME_POINTER_EVENT); + } else { + EventHandler.on(this._element, EVENT_TOUCHSTART, event => this._start(event)); + EventHandler.on(this._element, EVENT_TOUCHMOVE, event => this._move(event)); + EventHandler.on(this._element, EVENT_TOUCHEND, event => this._end(event)); + } + } + _eventIsPointerPenTouch(event) { + return this._supportPointerEvents && (event.pointerType === POINTER_TYPE_PEN || event.pointerType === POINTER_TYPE_TOUCH); + } + + // Static + static isSupported() { + return 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0; + } + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap carousel.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$c = 'carousel'; + const DATA_KEY$8 = 'bs.carousel'; + const EVENT_KEY$8 = `.${DATA_KEY$8}`; + const DATA_API_KEY$5 = '.data-api'; + const ARROW_LEFT_KEY$1 = 'ArrowLeft'; + const ARROW_RIGHT_KEY$1 = 'ArrowRight'; + const TOUCHEVENT_COMPAT_WAIT = 500; // Time for mouse compat events to fire after touch + + const ORDER_NEXT = 'next'; + const ORDER_PREV = 'prev'; + const DIRECTION_LEFT = 'left'; + const DIRECTION_RIGHT = 'right'; + const EVENT_SLIDE = `slide${EVENT_KEY$8}`; + const EVENT_SLID = `slid${EVENT_KEY$8}`; + const EVENT_KEYDOWN$1 = `keydown${EVENT_KEY$8}`; + const EVENT_MOUSEENTER$1 = `mouseenter${EVENT_KEY$8}`; + const EVENT_MOUSELEAVE$1 = `mouseleave${EVENT_KEY$8}`; + const EVENT_DRAG_START = `dragstart${EVENT_KEY$8}`; + const EVENT_LOAD_DATA_API$3 = `load${EVENT_KEY$8}${DATA_API_KEY$5}`; + const EVENT_CLICK_DATA_API$5 = `click${EVENT_KEY$8}${DATA_API_KEY$5}`; + const CLASS_NAME_CAROUSEL = 'carousel'; + const CLASS_NAME_ACTIVE$2 = 'active'; + const CLASS_NAME_SLIDE = 'slide'; + 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 SELECTOR_ACTIVE = '.active'; + const SELECTOR_ITEM = '.carousel-item'; + const SELECTOR_ACTIVE_ITEM = SELECTOR_ACTIVE + SELECTOR_ITEM; + const SELECTOR_ITEM_IMG = '.carousel-item img'; + const SELECTOR_INDICATORS = '.carousel-indicators'; + const SELECTOR_DATA_SLIDE = '[data-bs-slide], [data-bs-slide-to]'; + const SELECTOR_DATA_RIDE = '[data-bs-ride="carousel"]'; + const KEY_TO_DIRECTION = { + [ARROW_LEFT_KEY$1]: DIRECTION_RIGHT, + [ARROW_RIGHT_KEY$1]: DIRECTION_LEFT + }; + const Default$b = { + interval: 5000, + keyboard: true, + pause: 'hover', + ride: false, + touch: true, + wrap: true + }; + const DefaultType$b = { + interval: '(number|boolean)', + // TODO:v6 remove boolean support + keyboard: 'boolean', + pause: '(string|boolean)', + ride: '(boolean|string)', + touch: 'boolean', + wrap: 'boolean' + }; + + /** + * Class definition + */ + + class Carousel extends BaseComponent { + constructor(element, config) { + super(element, config); + this._interval = null; + this._activeElement = null; + this._isSliding = false; + this.touchTimeout = null; + this._swipeHelper = null; + this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element); + this._addEventListeners(); + if (this._config.ride === CLASS_NAME_CAROUSEL) { + this.cycle(); + } + } + + // Getters + static get Default() { + return Default$b; + } + static get DefaultType() { + return DefaultType$b; + } + static get NAME() { + return NAME$c; + } + + // Public + next() { + this._slide(ORDER_NEXT); + } + nextWhenVisible() { + // FIXME TODO use `document.visibilityState` + // Don't call next when the page isn't visible + // or the carousel or its parent isn't visible + if (!document.hidden && isVisible(this._element)) { + this.next(); + } + } + prev() { + this._slide(ORDER_PREV); + } + pause() { + if (this._isSliding) { + triggerTransitionEnd(this._element); + } + this._clearInterval(); + } + cycle() { + this._clearInterval(); + this._updateInterval(); + this._interval = setInterval(() => this.nextWhenVisible(), this._config.interval); + } + _maybeEnableCycle() { + if (!this._config.ride) { + return; + } + if (this._isSliding) { + EventHandler.one(this._element, EVENT_SLID, () => this.cycle()); + return; + } + this.cycle(); + } + to(index) { + const items = this._getItems(); + if (index > items.length - 1 || index < 0) { + return; + } + if (this._isSliding) { + EventHandler.one(this._element, EVENT_SLID, () => this.to(index)); + return; + } + const activeIndex = this._getItemIndex(this._getActive()); + if (activeIndex === index) { + return; + } + const order = index > activeIndex ? ORDER_NEXT : ORDER_PREV; + this._slide(order, items[index]); + } + dispose() { + if (this._swipeHelper) { + this._swipeHelper.dispose(); + } + super.dispose(); + } + + // Private + _configAfterMerge(config) { + config.defaultInterval = config.interval; + return config; + } + _addEventListeners() { + if (this._config.keyboard) { + EventHandler.on(this._element, EVENT_KEYDOWN$1, event => this._keydown(event)); + } + if (this._config.pause === 'hover') { + EventHandler.on(this._element, EVENT_MOUSEENTER$1, () => this.pause()); + EventHandler.on(this._element, EVENT_MOUSELEAVE$1, () => this._maybeEnableCycle()); + } + if (this._config.touch && Swipe.isSupported()) { + this._addTouchEventListeners(); + } + } + _addTouchEventListeners() { + for (const img of SelectorEngine.find(SELECTOR_ITEM_IMG, this._element)) { + EventHandler.on(img, EVENT_DRAG_START, event => event.preventDefault()); + } + const endCallBack = () => { + if (this._config.pause !== 'hover') { + return; + } + + // If it's a touch-enabled device, mouseenter/leave are fired as + // part of the mouse compatibility events on first tap - the carousel + // would stop cycling until user tapped out of it; + // here, we listen for touchend, explicitly pause the carousel + // (as if it's the second time we tap on it, mouseenter compat event + // is NOT fired) and after a timeout (to allow for mouse compatibility + // events to fire) we explicitly restart cycling + + this.pause(); + if (this.touchTimeout) { + clearTimeout(this.touchTimeout); + } + this.touchTimeout = setTimeout(() => this._maybeEnableCycle(), TOUCHEVENT_COMPAT_WAIT + this._config.interval); + }; + const swipeConfig = { + leftCallback: () => this._slide(this._directionToOrder(DIRECTION_LEFT)), + rightCallback: () => this._slide(this._directionToOrder(DIRECTION_RIGHT)), + endCallback: endCallBack + }; + this._swipeHelper = new Swipe(this._element, swipeConfig); + } + _keydown(event) { + if (/input|textarea/i.test(event.target.tagName)) { + return; + } + const direction = KEY_TO_DIRECTION[event.key]; + if (direction) { + event.preventDefault(); + this._slide(this._directionToOrder(direction)); + } + } + _getItemIndex(element) { + return this._getItems().indexOf(element); + } + _setActiveIndicatorElement(index) { + if (!this._indicatorsElement) { + return; + } + const activeIndicator = SelectorEngine.findOne(SELECTOR_ACTIVE, this._indicatorsElement); + activeIndicator.classList.remove(CLASS_NAME_ACTIVE$2); + activeIndicator.removeAttribute('aria-current'); + const newActiveIndicator = SelectorEngine.findOne(`[data-bs-slide-to="${index}"]`, this._indicatorsElement); + if (newActiveIndicator) { + newActiveIndicator.classList.add(CLASS_NAME_ACTIVE$2); + newActiveIndicator.setAttribute('aria-current', 'true'); + } + } + _updateInterval() { + const element = this._activeElement || this._getActive(); + if (!element) { + return; + } + const elementInterval = Number.parseInt(element.getAttribute('data-bs-interval'), 10); + this._config.interval = elementInterval || this._config.defaultInterval; + } + _slide(order, element = null) { + if (this._isSliding) { + return; + } + const activeElement = this._getActive(); + const isNext = order === ORDER_NEXT; + const nextElement = element || getNextActiveElement(this._getItems(), activeElement, isNext, this._config.wrap); + if (nextElement === activeElement) { + return; + } + const nextElementIndex = this._getItemIndex(nextElement); + const triggerEvent = eventName => { + return EventHandler.trigger(this._element, eventName, { + relatedTarget: nextElement, + direction: this._orderToDirection(order), + from: this._getItemIndex(activeElement), + to: nextElementIndex + }); + }; + const slideEvent = triggerEvent(EVENT_SLIDE); + if (slideEvent.defaultPrevented) { + return; + } + if (!activeElement || !nextElement) { + // Some weirdness is happening, so we bail + // TODO: change tests that use empty divs to avoid this check + return; + } + const isCycling = Boolean(this._interval); + this.pause(); + this._isSliding = true; + this._setActiveIndicatorElement(nextElementIndex); + this._activeElement = nextElement; + const directionalClassName = isNext ? CLASS_NAME_START : CLASS_NAME_END; + const orderClassName = isNext ? CLASS_NAME_NEXT : CLASS_NAME_PREV; + nextElement.classList.add(orderClassName); + reflow(nextElement); + activeElement.classList.add(directionalClassName); + nextElement.classList.add(directionalClassName); + const completeCallBack = () => { + nextElement.classList.remove(directionalClassName, orderClassName); + nextElement.classList.add(CLASS_NAME_ACTIVE$2); + activeElement.classList.remove(CLASS_NAME_ACTIVE$2, orderClassName, directionalClassName); + this._isSliding = false; + triggerEvent(EVENT_SLID); + }; + this._queueCallback(completeCallBack, activeElement, this._isAnimated()); + if (isCycling) { + this.cycle(); + } + } + _isAnimated() { + return this._element.classList.contains(CLASS_NAME_SLIDE); + } + _getActive() { + return SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element); + } + _getItems() { + return SelectorEngine.find(SELECTOR_ITEM, this._element); + } + _clearInterval() { + if (this._interval) { + clearInterval(this._interval); + this._interval = null; + } + } + _directionToOrder(direction) { + if (isRTL()) { + return direction === DIRECTION_LEFT ? ORDER_PREV : ORDER_NEXT; + } + return direction === DIRECTION_LEFT ? ORDER_NEXT : ORDER_PREV; + } + _orderToDirection(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 () { + const data = Carousel.getOrCreateInstance(this, config); + if (typeof config === 'number') { + data.to(config); + return; + } + if (typeof config === 'string') { + if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { + throw new TypeError(`No method named "${config}"`); + } + data[config](); + } + }); + } + } + + /** + * Data API implementation + */ + + EventHandler.on(document, EVENT_CLICK_DATA_API$5, SELECTOR_DATA_SLIDE, function (event) { + const target = SelectorEngine.getElementFromSelector(this); + if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) { + return; + } + event.preventDefault(); + const carousel = Carousel.getOrCreateInstance(target); + const slideIndex = this.getAttribute('data-bs-slide-to'); + if (slideIndex) { + carousel.to(slideIndex); + carousel._maybeEnableCycle(); + return; + } + if (Manipulator.getDataAttribute(this, 'slide') === 'next') { + carousel.next(); + carousel._maybeEnableCycle(); + return; + } + carousel.prev(); + carousel._maybeEnableCycle(); + }); + EventHandler.on(window, EVENT_LOAD_DATA_API$3, () => { + const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE); + for (const carousel of carousels) { + Carousel.getOrCreateInstance(carousel); + } + }); + + /** + * jQuery + */ + + defineJQueryPlugin(Carousel); + + /** + * -------------------------------------------------------------------------- + * Bootstrap collapse.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$b = 'collapse'; + const DATA_KEY$7 = 'bs.collapse'; + const EVENT_KEY$7 = `.${DATA_KEY$7}`; + const DATA_API_KEY$4 = '.data-api'; + const EVENT_SHOW$6 = `show${EVENT_KEY$7}`; + const EVENT_SHOWN$6 = `shown${EVENT_KEY$7}`; + const EVENT_HIDE$6 = `hide${EVENT_KEY$7}`; + const EVENT_HIDDEN$6 = `hidden${EVENT_KEY$7}`; + const EVENT_CLICK_DATA_API$4 = `click${EVENT_KEY$7}${DATA_API_KEY$4}`; + const CLASS_NAME_SHOW$7 = 'show'; + const CLASS_NAME_COLLAPSE = 'collapse'; + const CLASS_NAME_COLLAPSING = 'collapsing'; + const CLASS_NAME_COLLAPSED = 'collapsed'; + const CLASS_NAME_DEEPER_CHILDREN = `:scope .${CLASS_NAME_COLLAPSE} .${CLASS_NAME_COLLAPSE}`; + const CLASS_NAME_HORIZONTAL = 'collapse-horizontal'; + const WIDTH = 'width'; + const HEIGHT = 'height'; + const SELECTOR_ACTIVES = '.collapse.show, .collapse.collapsing'; + const SELECTOR_DATA_TOGGLE$4 = '[data-bs-toggle="collapse"]'; + const Default$a = { + parent: null, + toggle: true + }; + const DefaultType$a = { + parent: '(null|element)', + toggle: 'boolean' + }; + + /** + * Class definition + */ + + class Collapse extends BaseComponent { + constructor(element, config) { + super(element, config); + this._isTransitioning = false; + this._triggerArray = []; + const toggleList = SelectorEngine.find(SELECTOR_DATA_TOGGLE$4); + for (const elem of toggleList) { + const selector = SelectorEngine.getSelectorFromElement(elem); + const filterElement = SelectorEngine.find(selector).filter(foundElement => foundElement === this._element); + if (selector !== null && filterElement.length) { + this._triggerArray.push(elem); + } + } + this._initializeChildren(); + if (!this._config.parent) { + this._addAriaAndCollapsedClass(this._triggerArray, this._isShown()); + } + if (this._config.toggle) { + this.toggle(); + } + } + + // Getters + static get Default() { + return Default$a; + } + static get DefaultType() { + return DefaultType$a; + } + static get NAME() { + return NAME$b; + } + + // Public + toggle() { + if (this._isShown()) { + this.hide(); + } else { + this.show(); + } + } + show() { + if (this._isTransitioning || this._isShown()) { + return; + } + let activeChildren = []; + + // find active children + if (this._config.parent) { + activeChildren = this._getFirstLevelChildren(SELECTOR_ACTIVES).filter(element => element !== this._element).map(element => Collapse.getOrCreateInstance(element, { + toggle: false + })); + } + if (activeChildren.length && activeChildren[0]._isTransitioning) { + return; + } + const startEvent = EventHandler.trigger(this._element, EVENT_SHOW$6); + if (startEvent.defaultPrevented) { + return; + } + for (const activeInstance of activeChildren) { + activeInstance.hide(); + } + const dimension = this._getDimension(); + this._element.classList.remove(CLASS_NAME_COLLAPSE); + this._element.classList.add(CLASS_NAME_COLLAPSING); + this._element.style[dimension] = 0; + this._addAriaAndCollapsedClass(this._triggerArray, true); + this._isTransitioning = true; + const complete = () => { + this._isTransitioning = false; + this._element.classList.remove(CLASS_NAME_COLLAPSING); + this._element.classList.add(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7); + this._element.style[dimension] = ''; + EventHandler.trigger(this._element, EVENT_SHOWN$6); + }; + const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1); + const scrollSize = `scroll${capitalizedDimension}`; + this._queueCallback(complete, this._element, true); + this._element.style[dimension] = `${this._element[scrollSize]}px`; + } + hide() { + if (this._isTransitioning || !this._isShown()) { + return; + } + const startEvent = EventHandler.trigger(this._element, EVENT_HIDE$6); + if (startEvent.defaultPrevented) { + return; + } + const dimension = this._getDimension(); + this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`; + reflow(this._element); + this._element.classList.add(CLASS_NAME_COLLAPSING); + this._element.classList.remove(CLASS_NAME_COLLAPSE, CLASS_NAME_SHOW$7); + for (const trigger of this._triggerArray) { + const element = SelectorEngine.getElementFromSelector(trigger); + if (element && !this._isShown(element)) { + this._addAriaAndCollapsedClass([trigger], false); + } + } + this._isTransitioning = true; + const complete = () => { + this._isTransitioning = false; + this._element.classList.remove(CLASS_NAME_COLLAPSING); + this._element.classList.add(CLASS_NAME_COLLAPSE); + EventHandler.trigger(this._element, EVENT_HIDDEN$6); + }; + this._element.style[dimension] = ''; + this._queueCallback(complete, this._element, true); + } + _isShown(element = this._element) { + return element.classList.contains(CLASS_NAME_SHOW$7); + } + + // Private + _configAfterMerge(config) { + config.toggle = Boolean(config.toggle); // Coerce string values + config.parent = getElement(config.parent); + return config; + } + _getDimension() { + return this._element.classList.contains(CLASS_NAME_HORIZONTAL) ? WIDTH : HEIGHT; + } + _initializeChildren() { + if (!this._config.parent) { + return; + } + const children = this._getFirstLevelChildren(SELECTOR_DATA_TOGGLE$4); + for (const element of children) { + const selected = SelectorEngine.getElementFromSelector(element); + if (selected) { + this._addAriaAndCollapsedClass([element], this._isShown(selected)); + } + } + } + _getFirstLevelChildren(selector) { + const children = SelectorEngine.find(CLASS_NAME_DEEPER_CHILDREN, this._config.parent); + // remove children if greater depth + return SelectorEngine.find(selector, this._config.parent).filter(element => !children.includes(element)); + } + _addAriaAndCollapsedClass(triggerArray, isOpen) { + if (!triggerArray.length) { + return; + } + for (const element of triggerArray) { + element.classList.toggle(CLASS_NAME_COLLAPSED, !isOpen); + element.setAttribute('aria-expanded', isOpen); + } + } + + // Static + static jQueryInterface(config) { + const _config = {}; + if (typeof config === 'string' && /show|hide/.test(config)) { + _config.toggle = false; + } + return this.each(function () { + const data = Collapse.getOrCreateInstance(this, _config); + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`); + } + data[config](); + } + }); + } + } + + /** + * Data API implementation + */ + + EventHandler.on(document, EVENT_CLICK_DATA_API$4, SELECTOR_DATA_TOGGLE$4, function (event) { + // preventDefault only for <a> elements (which change the URL) not inside the collapsible element + if (event.target.tagName === 'A' || event.delegateTarget && event.delegateTarget.tagName === 'A') { + event.preventDefault(); + } + for (const element of SelectorEngine.getMultipleElementsFromSelector(this)) { + Collapse.getOrCreateInstance(element, { + toggle: false + }).toggle(); + } + }); + + /** + * jQuery + */ + + defineJQueryPlugin(Collapse); + + var top = 'top'; + var bottom = 'bottom'; + var right = 'right'; + var left = 'left'; + var auto = 'auto'; + var basePlacements = [top, bottom, right, left]; + var start = 'start'; + var end = 'end'; + var clippingParents = 'clippingParents'; + var viewport = 'viewport'; + var popper = 'popper'; + var reference = 'reference'; + var variationPlacements = /*#__PURE__*/basePlacements.reduce(function (acc, placement) { + return acc.concat([placement + "-" + start, placement + "-" + end]); + }, []); + var placements = /*#__PURE__*/[].concat(basePlacements, [auto]).reduce(function (acc, placement) { + return acc.concat([placement, placement + "-" + start, placement + "-" + end]); + }, []); // modifiers that need to read the DOM + + var beforeRead = 'beforeRead'; + var read = 'read'; + var afterRead = 'afterRead'; // pure-logic modifiers + + var beforeMain = 'beforeMain'; + var main = 'main'; + var afterMain = 'afterMain'; // modifier with the purpose to write to the DOM (or write into a framework state) + + var beforeWrite = 'beforeWrite'; + var write = 'write'; + var afterWrite = 'afterWrite'; + var modifierPhases = [beforeRead, read, afterRead, beforeMain, main, afterMain, beforeWrite, write, afterWrite]; + + function getNodeName(element) { + return element ? (element.nodeName || '').toLowerCase() : null; + } + + function getWindow(node) { + if (node == null) { + return window; + } + + if (node.toString() !== '[object Window]') { + var ownerDocument = node.ownerDocument; + return ownerDocument ? ownerDocument.defaultView || window : window; + } + + return node; + } + + function isElement(node) { + var OwnElement = getWindow(node).Element; + return node instanceof OwnElement || node instanceof Element; + } + + function isHTMLElement(node) { + var OwnElement = getWindow(node).HTMLElement; + return node instanceof OwnElement || node instanceof HTMLElement; + } + + function isShadowRoot(node) { + // IE 11 has no ShadowRoot + if (typeof ShadowRoot === 'undefined') { + return false; + } + + var OwnElement = getWindow(node).ShadowRoot; + return node instanceof OwnElement || node instanceof ShadowRoot; + } + + // and applies them to the HTMLElements such as popper and arrow + + function applyStyles(_ref) { + var state = _ref.state; + Object.keys(state.elements).forEach(function (name) { + var style = state.styles[name] || {}; + var attributes = state.attributes[name] || {}; + var element = state.elements[name]; // arrow is optional + virtual elements + + if (!isHTMLElement(element) || !getNodeName(element)) { + return; + } // Flow doesn't support to extend this property, but it's the most + // effective way to apply styles to an HTMLElement + // $FlowFixMe[cannot-write] + + + Object.assign(element.style, style); + Object.keys(attributes).forEach(function (name) { + var value = attributes[name]; + + if (value === false) { + element.removeAttribute(name); + } else { + element.setAttribute(name, value === true ? '' : value); + } + }); + }); + } + + function effect$2(_ref2) { + var state = _ref2.state; + var initialStyles = { + popper: { + position: state.options.strategy, + left: '0', + top: '0', + margin: '0' + }, + arrow: { + position: 'absolute' + }, + reference: {} + }; + Object.assign(state.elements.popper.style, initialStyles.popper); + state.styles = initialStyles; + + if (state.elements.arrow) { + Object.assign(state.elements.arrow.style, initialStyles.arrow); + } + + return function () { + Object.keys(state.elements).forEach(function (name) { + var element = state.elements[name]; + var attributes = state.attributes[name] || {}; + var styleProperties = Object.keys(state.styles.hasOwnProperty(name) ? state.styles[name] : initialStyles[name]); // Set all values to an empty string to unset them + + var style = styleProperties.reduce(function (style, property) { + style[property] = ''; + return style; + }, {}); // arrow is optional + virtual elements + + if (!isHTMLElement(element) || !getNodeName(element)) { + return; + } + + Object.assign(element.style, style); + Object.keys(attributes).forEach(function (attribute) { + element.removeAttribute(attribute); + }); + }); + }; + } // eslint-disable-next-line import/no-unused-modules + + + const applyStyles$1 = { + name: 'applyStyles', + enabled: true, + phase: 'write', + fn: applyStyles, + effect: effect$2, + requires: ['computeStyles'] + }; + + function getBasePlacement(placement) { + return placement.split('-')[0]; + } + + var max = Math.max; + var min = Math.min; + var round = Math.round; + + function getUAString() { + var uaData = navigator.userAgentData; + + if (uaData != null && uaData.brands && Array.isArray(uaData.brands)) { + return uaData.brands.map(function (item) { + return item.brand + "/" + item.version; + }).join(' '); + } + + return navigator.userAgent; + } + + function isLayoutViewport() { + return !/^((?!chrome|android).)*safari/i.test(getUAString()); + } + + function getBoundingClientRect(element, includeScale, isFixedStrategy) { + if (includeScale === void 0) { + includeScale = false; + } + + if (isFixedStrategy === void 0) { + isFixedStrategy = false; + } + + var clientRect = element.getBoundingClientRect(); + var scaleX = 1; + var scaleY = 1; + + if (includeScale && isHTMLElement(element)) { + scaleX = element.offsetWidth > 0 ? round(clientRect.width) / element.offsetWidth || 1 : 1; + scaleY = element.offsetHeight > 0 ? round(clientRect.height) / element.offsetHeight || 1 : 1; + } + + var _ref = isElement(element) ? getWindow(element) : window, + visualViewport = _ref.visualViewport; + + var addVisualOffsets = !isLayoutViewport() && isFixedStrategy; + var x = (clientRect.left + (addVisualOffsets && visualViewport ? visualViewport.offsetLeft : 0)) / scaleX; + var y = (clientRect.top + (addVisualOffsets && visualViewport ? visualViewport.offsetTop : 0)) / scaleY; + var width = clientRect.width / scaleX; + var height = clientRect.height / scaleY; + return { + width: width, + height: height, + top: y, + right: x + width, + bottom: y + height, + left: x, + x: x, + y: y + }; + } + + // means it doesn't take into account transforms. + + function getLayoutRect(element) { + var clientRect = getBoundingClientRect(element); // Use the clientRect sizes if it's not been transformed. + // Fixes https://github.com/popperjs/popper-core/issues/1223 + + var width = element.offsetWidth; + var height = element.offsetHeight; + + if (Math.abs(clientRect.width - width) <= 1) { + width = clientRect.width; + } + + if (Math.abs(clientRect.height - height) <= 1) { + height = clientRect.height; + } + + return { + x: element.offsetLeft, + y: element.offsetTop, + width: width, + height: height + }; + } + + function contains(parent, child) { + var rootNode = child.getRootNode && child.getRootNode(); // First, attempt with faster native method + + if (parent.contains(child)) { + return true; + } // then fallback to custom implementation with Shadow DOM support + else if (rootNode && isShadowRoot(rootNode)) { + var next = child; + + do { + if (next && parent.isSameNode(next)) { + return true; + } // $FlowFixMe[prop-missing]: need a better way to handle this... + + + next = next.parentNode || next.host; + } while (next); + } // Give up, the result is false + + + return false; + } + + function getComputedStyle$1(element) { + return getWindow(element).getComputedStyle(element); + } + + function isTableElement(element) { + return ['table', 'td', 'th'].indexOf(getNodeName(element)) >= 0; + } + + function getDocumentElement(element) { + // $FlowFixMe[incompatible-return]: assume body is always available + return ((isElement(element) ? element.ownerDocument : // $FlowFixMe[prop-missing] + element.document) || window.document).documentElement; + } + + function getParentNode(element) { + if (getNodeName(element) === 'html') { + return element; + } + + return (// this is a quicker (but less type safe) way to save quite some bytes from the bundle + // $FlowFixMe[incompatible-return] + // $FlowFixMe[prop-missing] + element.assignedSlot || // step into the shadow DOM of the parent of a slotted node + element.parentNode || ( // DOM Element detected + isShadowRoot(element) ? element.host : null) || // ShadowRoot detected + // $FlowFixMe[incompatible-call]: HTMLElement is a Node + getDocumentElement(element) // fallback + + ); + } + + function getTrueOffsetParent(element) { + if (!isHTMLElement(element) || // https://github.com/popperjs/popper-core/issues/837 + getComputedStyle$1(element).position === 'fixed') { + return null; + } + + return element.offsetParent; + } // `.offsetParent` reports `null` for fixed elements, while absolute elements + // return the containing block + + + function getContainingBlock(element) { + var isFirefox = /firefox/i.test(getUAString()); + var isIE = /Trident/i.test(getUAString()); + + if (isIE && isHTMLElement(element)) { + // In IE 9, 10 and 11 fixed elements containing block is always established by the viewport + var elementCss = getComputedStyle$1(element); + + if (elementCss.position === 'fixed') { + return null; + } + } + + var currentNode = getParentNode(element); + + if (isShadowRoot(currentNode)) { + currentNode = currentNode.host; + } + + while (isHTMLElement(currentNode) && ['html', 'body'].indexOf(getNodeName(currentNode)) < 0) { + var css = getComputedStyle$1(currentNode); // This is non-exhaustive but covers the most common CSS properties that + // create a containing block. + // https://developer.mozilla.org/en-US/docs/Web/CSS/Containing_block#identifying_the_containing_block + + if (css.transform !== 'none' || css.perspective !== 'none' || css.contain === 'paint' || ['transform', 'perspective'].indexOf(css.willChange) !== -1 || isFirefox && css.willChange === 'filter' || isFirefox && css.filter && css.filter !== 'none') { + return currentNode; + } else { + currentNode = currentNode.parentNode; + } + } + + return null; + } // Gets the closest ancestor positioned element. Handles some edge cases, + // such as table ancestors and cross browser bugs. + + + function getOffsetParent(element) { + var window = getWindow(element); + var offsetParent = getTrueOffsetParent(element); + + while (offsetParent && isTableElement(offsetParent) && getComputedStyle$1(offsetParent).position === 'static') { + offsetParent = getTrueOffsetParent(offsetParent); + } + + if (offsetParent && (getNodeName(offsetParent) === 'html' || getNodeName(offsetParent) === 'body' && getComputedStyle$1(offsetParent).position === 'static')) { + return window; + } + + return offsetParent || getContainingBlock(element) || window; + } + + function getMainAxisFromPlacement(placement) { + return ['top', 'bottom'].indexOf(placement) >= 0 ? 'x' : 'y'; + } + + function within(min$1, value, max$1) { + return max(min$1, min(value, max$1)); + } + function withinMaxClamp(min, value, max) { + var v = within(min, value, max); + return v > max ? max : v; + } + + function getFreshSideObject() { + return { + top: 0, + right: 0, + bottom: 0, + left: 0 + }; + } + + function mergePaddingObject(paddingObject) { + return Object.assign({}, getFreshSideObject(), paddingObject); + } + + function expandToHashMap(value, keys) { + return keys.reduce(function (hashMap, key) { + hashMap[key] = value; + return hashMap; + }, {}); + } + + var toPaddingObject = function toPaddingObject(padding, state) { + padding = typeof padding === 'function' ? padding(Object.assign({}, state.rects, { + placement: state.placement + })) : padding; + return mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements)); + }; + + function arrow(_ref) { + var _state$modifiersData$; + + var state = _ref.state, + name = _ref.name, + options = _ref.options; + var arrowElement = state.elements.arrow; + var popperOffsets = state.modifiersData.popperOffsets; + var basePlacement = getBasePlacement(state.placement); + var axis = getMainAxisFromPlacement(basePlacement); + var isVertical = [left, right].indexOf(basePlacement) >= 0; + var len = isVertical ? 'height' : 'width'; + + if (!arrowElement || !popperOffsets) { + return; + } + + var paddingObject = toPaddingObject(options.padding, state); + var arrowRect = getLayoutRect(arrowElement); + var minProp = axis === 'y' ? top : left; + var maxProp = axis === 'y' ? bottom : right; + var endDiff = state.rects.reference[len] + state.rects.reference[axis] - popperOffsets[axis] - state.rects.popper[len]; + var startDiff = popperOffsets[axis] - state.rects.reference[axis]; + var arrowOffsetParent = getOffsetParent(arrowElement); + var clientSize = arrowOffsetParent ? axis === 'y' ? arrowOffsetParent.clientHeight || 0 : arrowOffsetParent.clientWidth || 0 : 0; + var centerToReference = endDiff / 2 - startDiff / 2; // Make sure the arrow doesn't overflow the popper if the center point is + // outside of the popper bounds + + var min = paddingObject[minProp]; + var max = clientSize - arrowRect[len] - paddingObject[maxProp]; + var center = clientSize / 2 - arrowRect[len] / 2 + centerToReference; + var offset = within(min, center, max); // Prevents breaking syntax highlighting... + + var axisProp = axis; + state.modifiersData[name] = (_state$modifiersData$ = {}, _state$modifiersData$[axisProp] = offset, _state$modifiersData$.centerOffset = offset - center, _state$modifiersData$); + } + + function effect$1(_ref2) { + var state = _ref2.state, + options = _ref2.options; + var _options$element = options.element, + arrowElement = _options$element === void 0 ? '[data-popper-arrow]' : _options$element; + + if (arrowElement == null) { + return; + } // CSS selector + + + if (typeof arrowElement === 'string') { + arrowElement = state.elements.popper.querySelector(arrowElement); + + if (!arrowElement) { + return; + } + } + + if (!contains(state.elements.popper, arrowElement)) { + return; + } + + state.elements.arrow = arrowElement; + } // eslint-disable-next-line import/no-unused-modules + + + const arrow$1 = { + name: 'arrow', + enabled: true, + phase: 'main', + fn: arrow, + effect: effect$1, + requires: ['popperOffsets'], + requiresIfExists: ['preventOverflow'] + }; + + function getVariation(placement) { + return placement.split('-')[1]; + } + + var unsetSides = { + top: 'auto', + right: 'auto', + bottom: 'auto', + left: 'auto' + }; // Round the offsets to the nearest suitable subpixel based on the DPR. + // Zooming can change the DPR, but it seems to report a value that will + // cleanly divide the values into the appropriate subpixels. + + function roundOffsetsByDPR(_ref, win) { + var x = _ref.x, + y = _ref.y; + var dpr = win.devicePixelRatio || 1; + return { + x: round(x * dpr) / dpr || 0, + y: round(y * dpr) / dpr || 0 + }; + } + + function mapToStyles(_ref2) { + var _Object$assign2; + + var popper = _ref2.popper, + popperRect = _ref2.popperRect, + placement = _ref2.placement, + variation = _ref2.variation, + offsets = _ref2.offsets, + position = _ref2.position, + gpuAcceleration = _ref2.gpuAcceleration, + adaptive = _ref2.adaptive, + roundOffsets = _ref2.roundOffsets, + isFixed = _ref2.isFixed; + var _offsets$x = offsets.x, + x = _offsets$x === void 0 ? 0 : _offsets$x, + _offsets$y = offsets.y, + y = _offsets$y === void 0 ? 0 : _offsets$y; + + var _ref3 = typeof roundOffsets === 'function' ? roundOffsets({ + x: x, + y: y + }) : { + x: x, + y: y + }; + + x = _ref3.x; + y = _ref3.y; + var hasX = offsets.hasOwnProperty('x'); + var hasY = offsets.hasOwnProperty('y'); + var sideX = left; + var sideY = top; + var win = window; + + if (adaptive) { + var offsetParent = getOffsetParent(popper); + var heightProp = 'clientHeight'; + var widthProp = 'clientWidth'; + + if (offsetParent === getWindow(popper)) { + offsetParent = getDocumentElement(popper); + + if (getComputedStyle$1(offsetParent).position !== 'static' && position === 'absolute') { + heightProp = 'scrollHeight'; + widthProp = 'scrollWidth'; + } + } // $FlowFixMe[incompatible-cast]: force type refinement, we compare offsetParent with window above, but Flow doesn't detect it + + + offsetParent = offsetParent; + + if (placement === top || (placement === left || placement === right) && variation === end) { + sideY = bottom; + var offsetY = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.height : // $FlowFixMe[prop-missing] + offsetParent[heightProp]; + y -= offsetY - popperRect.height; + y *= gpuAcceleration ? 1 : -1; + } + + if (placement === left || (placement === top || placement === bottom) && variation === end) { + sideX = right; + var offsetX = isFixed && offsetParent === win && win.visualViewport ? win.visualViewport.width : // $FlowFixMe[prop-missing] + offsetParent[widthProp]; + x -= offsetX - popperRect.width; + x *= gpuAcceleration ? 1 : -1; + } + } + + var commonStyles = Object.assign({ + position: position + }, adaptive && unsetSides); + + var _ref4 = roundOffsets === true ? roundOffsetsByDPR({ + x: x, + y: y + }, getWindow(popper)) : { + x: x, + y: y + }; + + x = _ref4.x; + y = _ref4.y; + + if (gpuAcceleration) { + var _Object$assign; + + return Object.assign({}, commonStyles, (_Object$assign = {}, _Object$assign[sideY] = hasY ? '0' : '', _Object$assign[sideX] = hasX ? '0' : '', _Object$assign.transform = (win.devicePixelRatio || 1) <= 1 ? "translate(" + x + "px, " + y + "px)" : "translate3d(" + x + "px, " + y + "px, 0)", _Object$assign)); + } + + return Object.assign({}, commonStyles, (_Object$assign2 = {}, _Object$assign2[sideY] = hasY ? y + "px" : '', _Object$assign2[sideX] = hasX ? x + "px" : '', _Object$assign2.transform = '', _Object$assign2)); + } + + function computeStyles(_ref5) { + var state = _ref5.state, + options = _ref5.options; + var _options$gpuAccelerat = options.gpuAcceleration, + gpuAcceleration = _options$gpuAccelerat === void 0 ? true : _options$gpuAccelerat, + _options$adaptive = options.adaptive, + adaptive = _options$adaptive === void 0 ? true : _options$adaptive, + _options$roundOffsets = options.roundOffsets, + roundOffsets = _options$roundOffsets === void 0 ? true : _options$roundOffsets; + var commonStyles = { + placement: getBasePlacement(state.placement), + variation: getVariation(state.placement), + popper: state.elements.popper, + popperRect: state.rects.popper, + gpuAcceleration: gpuAcceleration, + isFixed: state.options.strategy === 'fixed' + }; + + if (state.modifiersData.popperOffsets != null) { + state.styles.popper = Object.assign({}, state.styles.popper, mapToStyles(Object.assign({}, commonStyles, { + offsets: state.modifiersData.popperOffsets, + position: state.options.strategy, + adaptive: adaptive, + roundOffsets: roundOffsets + }))); + } + + if (state.modifiersData.arrow != null) { + state.styles.arrow = Object.assign({}, state.styles.arrow, mapToStyles(Object.assign({}, commonStyles, { + offsets: state.modifiersData.arrow, + position: 'absolute', + adaptive: false, + roundOffsets: roundOffsets + }))); + } + + state.attributes.popper = Object.assign({}, state.attributes.popper, { + 'data-popper-placement': state.placement + }); + } // eslint-disable-next-line import/no-unused-modules + + + const computeStyles$1 = { + name: 'computeStyles', + enabled: true, + phase: 'beforeWrite', + fn: computeStyles, + data: {} + }; + + var passive = { + passive: true + }; + + function effect(_ref) { + var state = _ref.state, + instance = _ref.instance, + options = _ref.options; + var _options$scroll = options.scroll, + scroll = _options$scroll === void 0 ? true : _options$scroll, + _options$resize = options.resize, + resize = _options$resize === void 0 ? true : _options$resize; + var window = getWindow(state.elements.popper); + var scrollParents = [].concat(state.scrollParents.reference, state.scrollParents.popper); + + if (scroll) { + scrollParents.forEach(function (scrollParent) { + scrollParent.addEventListener('scroll', instance.update, passive); + }); + } + + if (resize) { + window.addEventListener('resize', instance.update, passive); + } + + return function () { + if (scroll) { + scrollParents.forEach(function (scrollParent) { + scrollParent.removeEventListener('scroll', instance.update, passive); + }); + } + + if (resize) { + window.removeEventListener('resize', instance.update, passive); + } + }; + } // eslint-disable-next-line import/no-unused-modules + + + const eventListeners = { + name: 'eventListeners', + enabled: true, + phase: 'write', + fn: function fn() {}, + effect: effect, + data: {} + }; + + var hash$1 = { + left: 'right', + right: 'left', + bottom: 'top', + top: 'bottom' + }; + function getOppositePlacement(placement) { + return placement.replace(/left|right|bottom|top/g, function (matched) { + return hash$1[matched]; + }); + } + + var hash = { + start: 'end', + end: 'start' + }; + function getOppositeVariationPlacement(placement) { + return placement.replace(/start|end/g, function (matched) { + return hash[matched]; + }); + } + + function getWindowScroll(node) { + var win = getWindow(node); + var scrollLeft = win.pageXOffset; + var scrollTop = win.pageYOffset; + return { + scrollLeft: scrollLeft, + scrollTop: scrollTop + }; + } + + function getWindowScrollBarX(element) { + // If <html> has a CSS width greater than the viewport, then this will be + // incorrect for RTL. + // Popper 1 is broken in this case and never had a bug report so let's assume + // it's not an issue. I don't think anyone ever specifies width on <html> + // anyway. + // Browsers where the left scrollbar doesn't cause an issue report `0` for + // this (e.g. Edge 2019, IE11, Safari) + return getBoundingClientRect(getDocumentElement(element)).left + getWindowScroll(element).scrollLeft; + } + + function getViewportRect(element, strategy) { + var win = getWindow(element); + var html = getDocumentElement(element); + var visualViewport = win.visualViewport; + var width = html.clientWidth; + var height = html.clientHeight; + var x = 0; + var y = 0; + + if (visualViewport) { + width = visualViewport.width; + height = visualViewport.height; + var layoutViewport = isLayoutViewport(); + + if (layoutViewport || !layoutViewport && strategy === 'fixed') { + x = visualViewport.offsetLeft; + y = visualViewport.offsetTop; + } + } + + return { + width: width, + height: height, + x: x + getWindowScrollBarX(element), + y: y + }; + } + + // of the `<html>` and `<body>` rect bounds if horizontally scrollable + + function getDocumentRect(element) { + var _element$ownerDocumen; + + var html = getDocumentElement(element); + var winScroll = getWindowScroll(element); + var body = (_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body; + var width = max(html.scrollWidth, html.clientWidth, body ? body.scrollWidth : 0, body ? body.clientWidth : 0); + var height = max(html.scrollHeight, html.clientHeight, body ? body.scrollHeight : 0, body ? body.clientHeight : 0); + var x = -winScroll.scrollLeft + getWindowScrollBarX(element); + var y = -winScroll.scrollTop; + + if (getComputedStyle$1(body || html).direction === 'rtl') { + x += max(html.clientWidth, body ? body.clientWidth : 0) - width; + } + + return { + width: width, + height: height, + x: x, + y: y + }; + } + + function isScrollParent(element) { + // Firefox wants us to check `-x` and `-y` variations as well + var _getComputedStyle = getComputedStyle$1(element), + overflow = _getComputedStyle.overflow, + overflowX = _getComputedStyle.overflowX, + overflowY = _getComputedStyle.overflowY; + + return /auto|scroll|overlay|hidden/.test(overflow + overflowY + overflowX); + } + + function getScrollParent(node) { + if (['html', 'body', '#document'].indexOf(getNodeName(node)) >= 0) { + // $FlowFixMe[incompatible-return]: assume body is always available + return node.ownerDocument.body; + } + + if (isHTMLElement(node) && isScrollParent(node)) { + return node; + } + + return getScrollParent(getParentNode(node)); + } + + /* + given a DOM element, return the list of all scroll parents, up the list of ancesors + until we get to the top window object. This list is what we attach scroll listeners + to, because if any of these parent elements scroll, we'll need to re-calculate the + reference element's position. + */ + + function listScrollParents(element, list) { + var _element$ownerDocumen; + + if (list === void 0) { + list = []; + } + + var scrollParent = getScrollParent(element); + var isBody = scrollParent === ((_element$ownerDocumen = element.ownerDocument) == null ? void 0 : _element$ownerDocumen.body); + var win = getWindow(scrollParent); + var target = isBody ? [win].concat(win.visualViewport || [], isScrollParent(scrollParent) ? scrollParent : []) : scrollParent; + var updatedList = list.concat(target); + return isBody ? updatedList : // $FlowFixMe[incompatible-call]: isBody tells us target will be an HTMLElement here + updatedList.concat(listScrollParents(getParentNode(target))); + } + + function rectToClientRect(rect) { + return Object.assign({}, rect, { + left: rect.x, + top: rect.y, + right: rect.x + rect.width, + bottom: rect.y + rect.height + }); + } + + function getInnerBoundingClientRect(element, strategy) { + var rect = getBoundingClientRect(element, false, strategy === 'fixed'); + rect.top = rect.top + element.clientTop; + rect.left = rect.left + element.clientLeft; + rect.bottom = rect.top + element.clientHeight; + rect.right = rect.left + element.clientWidth; + rect.width = element.clientWidth; + rect.height = element.clientHeight; + rect.x = rect.left; + rect.y = rect.top; + return rect; + } + + function getClientRectFromMixedType(element, clippingParent, strategy) { + return clippingParent === viewport ? rectToClientRect(getViewportRect(element, strategy)) : isElement(clippingParent) ? getInnerBoundingClientRect(clippingParent, strategy) : rectToClientRect(getDocumentRect(getDocumentElement(element))); + } // A "clipping parent" is an overflowable container with the characteristic of + // clipping (or hiding) overflowing elements with a position different from + // `initial` + + + function getClippingParents(element) { + var clippingParents = listScrollParents(getParentNode(element)); + var canEscapeClipping = ['absolute', 'fixed'].indexOf(getComputedStyle$1(element).position) >= 0; + var clipperElement = canEscapeClipping && isHTMLElement(element) ? getOffsetParent(element) : element; + + if (!isElement(clipperElement)) { + return []; + } // $FlowFixMe[incompatible-return]: https://github.com/facebook/flow/issues/1414 + + + return clippingParents.filter(function (clippingParent) { + return isElement(clippingParent) && contains(clippingParent, clipperElement) && getNodeName(clippingParent) !== 'body'; + }); + } // Gets the maximum area that the element is visible in due to any number of + // clipping parents + + + function getClippingRect(element, boundary, rootBoundary, strategy) { + var mainClippingParents = boundary === 'clippingParents' ? getClippingParents(element) : [].concat(boundary); + var clippingParents = [].concat(mainClippingParents, [rootBoundary]); + var firstClippingParent = clippingParents[0]; + var clippingRect = clippingParents.reduce(function (accRect, clippingParent) { + var rect = getClientRectFromMixedType(element, clippingParent, strategy); + accRect.top = max(rect.top, accRect.top); + accRect.right = min(rect.right, accRect.right); + accRect.bottom = min(rect.bottom, accRect.bottom); + accRect.left = max(rect.left, accRect.left); + return accRect; + }, getClientRectFromMixedType(element, firstClippingParent, strategy)); + clippingRect.width = clippingRect.right - clippingRect.left; + clippingRect.height = clippingRect.bottom - clippingRect.top; + clippingRect.x = clippingRect.left; + clippingRect.y = clippingRect.top; + return clippingRect; + } + + function computeOffsets(_ref) { + var reference = _ref.reference, + element = _ref.element, + placement = _ref.placement; + var basePlacement = placement ? getBasePlacement(placement) : null; + var variation = placement ? getVariation(placement) : null; + var commonX = reference.x + reference.width / 2 - element.width / 2; + var commonY = reference.y + reference.height / 2 - element.height / 2; + var offsets; + + switch (basePlacement) { + case top: + offsets = { + x: commonX, + y: reference.y - element.height + }; + break; + + case bottom: + offsets = { + x: commonX, + y: reference.y + reference.height + }; + break; + + case right: + offsets = { + x: reference.x + reference.width, + y: commonY + }; + break; + + case left: + offsets = { + x: reference.x - element.width, + y: commonY + }; + break; + + default: + offsets = { + x: reference.x, + y: reference.y + }; + } + + var mainAxis = basePlacement ? getMainAxisFromPlacement(basePlacement) : null; + + if (mainAxis != null) { + var len = mainAxis === 'y' ? 'height' : 'width'; + + switch (variation) { + case start: + offsets[mainAxis] = offsets[mainAxis] - (reference[len] / 2 - element[len] / 2); + break; + + case end: + offsets[mainAxis] = offsets[mainAxis] + (reference[len] / 2 - element[len] / 2); + break; + } + } + + return offsets; + } + + function detectOverflow(state, options) { + if (options === void 0) { + options = {}; + } + + var _options = options, + _options$placement = _options.placement, + placement = _options$placement === void 0 ? state.placement : _options$placement, + _options$strategy = _options.strategy, + strategy = _options$strategy === void 0 ? state.strategy : _options$strategy, + _options$boundary = _options.boundary, + boundary = _options$boundary === void 0 ? clippingParents : _options$boundary, + _options$rootBoundary = _options.rootBoundary, + rootBoundary = _options$rootBoundary === void 0 ? viewport : _options$rootBoundary, + _options$elementConte = _options.elementContext, + elementContext = _options$elementConte === void 0 ? popper : _options$elementConte, + _options$altBoundary = _options.altBoundary, + altBoundary = _options$altBoundary === void 0 ? false : _options$altBoundary, + _options$padding = _options.padding, + padding = _options$padding === void 0 ? 0 : _options$padding; + var paddingObject = mergePaddingObject(typeof padding !== 'number' ? padding : expandToHashMap(padding, basePlacements)); + var altContext = elementContext === popper ? reference : popper; + var popperRect = state.rects.popper; + var element = state.elements[altBoundary ? altContext : elementContext]; + var clippingClientRect = getClippingRect(isElement(element) ? element : element.contextElement || getDocumentElement(state.elements.popper), boundary, rootBoundary, strategy); + var referenceClientRect = getBoundingClientRect(state.elements.reference); + var popperOffsets = computeOffsets({ + reference: referenceClientRect, + element: popperRect, + strategy: 'absolute', + placement: placement + }); + var popperClientRect = rectToClientRect(Object.assign({}, popperRect, popperOffsets)); + var elementClientRect = elementContext === popper ? popperClientRect : referenceClientRect; // positive = overflowing the clipping rect + // 0 or negative = within the clipping rect + + var overflowOffsets = { + top: clippingClientRect.top - elementClientRect.top + paddingObject.top, + bottom: elementClientRect.bottom - clippingClientRect.bottom + paddingObject.bottom, + left: clippingClientRect.left - elementClientRect.left + paddingObject.left, + right: elementClientRect.right - clippingClientRect.right + paddingObject.right + }; + var offsetData = state.modifiersData.offset; // Offsets can be applied only to the popper element + + if (elementContext === popper && offsetData) { + var offset = offsetData[placement]; + Object.keys(overflowOffsets).forEach(function (key) { + var multiply = [right, bottom].indexOf(key) >= 0 ? 1 : -1; + var axis = [top, bottom].indexOf(key) >= 0 ? 'y' : 'x'; + overflowOffsets[key] += offset[axis] * multiply; + }); + } + + return overflowOffsets; + } + + function computeAutoPlacement(state, options) { + if (options === void 0) { + options = {}; + } + + var _options = options, + placement = _options.placement, + boundary = _options.boundary, + rootBoundary = _options.rootBoundary, + padding = _options.padding, + flipVariations = _options.flipVariations, + _options$allowedAutoP = _options.allowedAutoPlacements, + allowedAutoPlacements = _options$allowedAutoP === void 0 ? placements : _options$allowedAutoP; + var variation = getVariation(placement); + var placements$1 = variation ? flipVariations ? variationPlacements : variationPlacements.filter(function (placement) { + return getVariation(placement) === variation; + }) : basePlacements; + var allowedPlacements = placements$1.filter(function (placement) { + return allowedAutoPlacements.indexOf(placement) >= 0; + }); + + if (allowedPlacements.length === 0) { + allowedPlacements = placements$1; + } // $FlowFixMe[incompatible-type]: Flow seems to have problems with two array unions... + + + var overflows = allowedPlacements.reduce(function (acc, placement) { + acc[placement] = detectOverflow(state, { + placement: placement, + boundary: boundary, + rootBoundary: rootBoundary, + padding: padding + })[getBasePlacement(placement)]; + return acc; + }, {}); + return Object.keys(overflows).sort(function (a, b) { + return overflows[a] - overflows[b]; + }); + } + + function getExpandedFallbackPlacements(placement) { + if (getBasePlacement(placement) === auto) { + return []; + } + + var oppositePlacement = getOppositePlacement(placement); + return [getOppositeVariationPlacement(placement), oppositePlacement, getOppositeVariationPlacement(oppositePlacement)]; + } + + function flip(_ref) { + var state = _ref.state, + options = _ref.options, + name = _ref.name; + + if (state.modifiersData[name]._skip) { + return; + } + + var _options$mainAxis = options.mainAxis, + checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis, + _options$altAxis = options.altAxis, + checkAltAxis = _options$altAxis === void 0 ? true : _options$altAxis, + specifiedFallbackPlacements = options.fallbackPlacements, + padding = options.padding, + boundary = options.boundary, + rootBoundary = options.rootBoundary, + altBoundary = options.altBoundary, + _options$flipVariatio = options.flipVariations, + flipVariations = _options$flipVariatio === void 0 ? true : _options$flipVariatio, + allowedAutoPlacements = options.allowedAutoPlacements; + var preferredPlacement = state.options.placement; + var basePlacement = getBasePlacement(preferredPlacement); + var isBasePlacement = basePlacement === preferredPlacement; + var fallbackPlacements = specifiedFallbackPlacements || (isBasePlacement || !flipVariations ? [getOppositePlacement(preferredPlacement)] : getExpandedFallbackPlacements(preferredPlacement)); + var placements = [preferredPlacement].concat(fallbackPlacements).reduce(function (acc, placement) { + return acc.concat(getBasePlacement(placement) === auto ? computeAutoPlacement(state, { + placement: placement, + boundary: boundary, + rootBoundary: rootBoundary, + padding: padding, + flipVariations: flipVariations, + allowedAutoPlacements: allowedAutoPlacements + }) : placement); + }, []); + var referenceRect = state.rects.reference; + var popperRect = state.rects.popper; + var checksMap = new Map(); + var makeFallbackChecks = true; + var firstFittingPlacement = placements[0]; + + for (var i = 0; i < placements.length; i++) { + var placement = placements[i]; + + var _basePlacement = getBasePlacement(placement); + + var isStartVariation = getVariation(placement) === start; + var isVertical = [top, bottom].indexOf(_basePlacement) >= 0; + var len = isVertical ? 'width' : 'height'; + var overflow = detectOverflow(state, { + placement: placement, + boundary: boundary, + rootBoundary: rootBoundary, + altBoundary: altBoundary, + padding: padding + }); + var mainVariationSide = isVertical ? isStartVariation ? right : left : isStartVariation ? bottom : top; + + if (referenceRect[len] > popperRect[len]) { + mainVariationSide = getOppositePlacement(mainVariationSide); + } + + var altVariationSide = getOppositePlacement(mainVariationSide); + var checks = []; + + if (checkMainAxis) { + checks.push(overflow[_basePlacement] <= 0); + } + + if (checkAltAxis) { + checks.push(overflow[mainVariationSide] <= 0, overflow[altVariationSide] <= 0); + } + + if (checks.every(function (check) { + return check; + })) { + firstFittingPlacement = placement; + makeFallbackChecks = false; + break; + } + + checksMap.set(placement, checks); + } + + if (makeFallbackChecks) { + // `2` may be desired in some cases – research later + var numberOfChecks = flipVariations ? 3 : 1; + + var _loop = function _loop(_i) { + var fittingPlacement = placements.find(function (placement) { + var checks = checksMap.get(placement); + + if (checks) { + return checks.slice(0, _i).every(function (check) { + return check; + }); + } + }); + + if (fittingPlacement) { + firstFittingPlacement = fittingPlacement; + return "break"; + } + }; + + for (var _i = numberOfChecks; _i > 0; _i--) { + var _ret = _loop(_i); + + if (_ret === "break") break; + } + } + + if (state.placement !== firstFittingPlacement) { + state.modifiersData[name]._skip = true; + state.placement = firstFittingPlacement; + state.reset = true; + } + } // eslint-disable-next-line import/no-unused-modules + + + const flip$1 = { + name: 'flip', + enabled: true, + phase: 'main', + fn: flip, + requiresIfExists: ['offset'], + data: { + _skip: false + } + }; + + function getSideOffsets(overflow, rect, preventedOffsets) { + if (preventedOffsets === void 0) { + preventedOffsets = { + x: 0, + y: 0 + }; + } + + return { + top: overflow.top - rect.height - preventedOffsets.y, + right: overflow.right - rect.width + preventedOffsets.x, + bottom: overflow.bottom - rect.height + preventedOffsets.y, + left: overflow.left - rect.width - preventedOffsets.x + }; + } + + function isAnySideFullyClipped(overflow) { + return [top, right, bottom, left].some(function (side) { + return overflow[side] >= 0; + }); + } + + function hide(_ref) { + var state = _ref.state, + name = _ref.name; + var referenceRect = state.rects.reference; + var popperRect = state.rects.popper; + var preventedOffsets = state.modifiersData.preventOverflow; + var referenceOverflow = detectOverflow(state, { + elementContext: 'reference' + }); + var popperAltOverflow = detectOverflow(state, { + altBoundary: true + }); + var referenceClippingOffsets = getSideOffsets(referenceOverflow, referenceRect); + var popperEscapeOffsets = getSideOffsets(popperAltOverflow, popperRect, preventedOffsets); + var isReferenceHidden = isAnySideFullyClipped(referenceClippingOffsets); + var hasPopperEscaped = isAnySideFullyClipped(popperEscapeOffsets); + state.modifiersData[name] = { + referenceClippingOffsets: referenceClippingOffsets, + popperEscapeOffsets: popperEscapeOffsets, + isReferenceHidden: isReferenceHidden, + hasPopperEscaped: hasPopperEscaped + }; + state.attributes.popper = Object.assign({}, state.attributes.popper, { + 'data-popper-reference-hidden': isReferenceHidden, + 'data-popper-escaped': hasPopperEscaped + }); + } // eslint-disable-next-line import/no-unused-modules + + + const hide$1 = { + name: 'hide', + enabled: true, + phase: 'main', + requiresIfExists: ['preventOverflow'], + fn: hide + }; + + function distanceAndSkiddingToXY(placement, rects, offset) { + var basePlacement = getBasePlacement(placement); + var invertDistance = [left, top].indexOf(basePlacement) >= 0 ? -1 : 1; + + var _ref = typeof offset === 'function' ? offset(Object.assign({}, rects, { + placement: placement + })) : offset, + skidding = _ref[0], + distance = _ref[1]; + + skidding = skidding || 0; + distance = (distance || 0) * invertDistance; + return [left, right].indexOf(basePlacement) >= 0 ? { + x: distance, + y: skidding + } : { + x: skidding, + y: distance + }; + } + + function offset(_ref2) { + var state = _ref2.state, + options = _ref2.options, + name = _ref2.name; + var _options$offset = options.offset, + offset = _options$offset === void 0 ? [0, 0] : _options$offset; + var data = placements.reduce(function (acc, placement) { + acc[placement] = distanceAndSkiddingToXY(placement, state.rects, offset); + return acc; + }, {}); + var _data$state$placement = data[state.placement], + x = _data$state$placement.x, + y = _data$state$placement.y; + + if (state.modifiersData.popperOffsets != null) { + state.modifiersData.popperOffsets.x += x; + state.modifiersData.popperOffsets.y += y; + } + + state.modifiersData[name] = data; + } // eslint-disable-next-line import/no-unused-modules + + + const offset$1 = { + name: 'offset', + enabled: true, + phase: 'main', + requires: ['popperOffsets'], + fn: offset + }; + + function popperOffsets(_ref) { + var state = _ref.state, + name = _ref.name; + // Offsets are the actual position the popper needs to have to be + // properly positioned near its reference element + // This is the most basic placement, and will be adjusted by + // the modifiers in the next step + state.modifiersData[name] = computeOffsets({ + reference: state.rects.reference, + element: state.rects.popper, + strategy: 'absolute', + placement: state.placement + }); + } // eslint-disable-next-line import/no-unused-modules + + + const popperOffsets$1 = { + name: 'popperOffsets', + enabled: true, + phase: 'read', + fn: popperOffsets, + data: {} + }; + + function getAltAxis(axis) { + return axis === 'x' ? 'y' : 'x'; + } + + function preventOverflow(_ref) { + var state = _ref.state, + options = _ref.options, + name = _ref.name; + var _options$mainAxis = options.mainAxis, + checkMainAxis = _options$mainAxis === void 0 ? true : _options$mainAxis, + _options$altAxis = options.altAxis, + checkAltAxis = _options$altAxis === void 0 ? false : _options$altAxis, + boundary = options.boundary, + rootBoundary = options.rootBoundary, + altBoundary = options.altBoundary, + padding = options.padding, + _options$tether = options.tether, + tether = _options$tether === void 0 ? true : _options$tether, + _options$tetherOffset = options.tetherOffset, + tetherOffset = _options$tetherOffset === void 0 ? 0 : _options$tetherOffset; + var overflow = detectOverflow(state, { + boundary: boundary, + rootBoundary: rootBoundary, + padding: padding, + altBoundary: altBoundary + }); + var basePlacement = getBasePlacement(state.placement); + var variation = getVariation(state.placement); + var isBasePlacement = !variation; + var mainAxis = getMainAxisFromPlacement(basePlacement); + var altAxis = getAltAxis(mainAxis); + var popperOffsets = state.modifiersData.popperOffsets; + var referenceRect = state.rects.reference; + var popperRect = state.rects.popper; + var tetherOffsetValue = typeof tetherOffset === 'function' ? tetherOffset(Object.assign({}, state.rects, { + placement: state.placement + })) : tetherOffset; + var normalizedTetherOffsetValue = typeof tetherOffsetValue === 'number' ? { + mainAxis: tetherOffsetValue, + altAxis: tetherOffsetValue + } : Object.assign({ + mainAxis: 0, + altAxis: 0 + }, tetherOffsetValue); + var offsetModifierState = state.modifiersData.offset ? state.modifiersData.offset[state.placement] : null; + var data = { + x: 0, + y: 0 + }; + + if (!popperOffsets) { + return; + } + + if (checkMainAxis) { + var _offsetModifierState$; + + var mainSide = mainAxis === 'y' ? top : left; + var altSide = mainAxis === 'y' ? bottom : right; + var len = mainAxis === 'y' ? 'height' : 'width'; + var offset = popperOffsets[mainAxis]; + var min$1 = offset + overflow[mainSide]; + var max$1 = offset - overflow[altSide]; + var additive = tether ? -popperRect[len] / 2 : 0; + var minLen = variation === start ? referenceRect[len] : popperRect[len]; + var maxLen = variation === start ? -popperRect[len] : -referenceRect[len]; // We need to include the arrow in the calculation so the arrow doesn't go + // outside the reference bounds + + var arrowElement = state.elements.arrow; + var arrowRect = tether && arrowElement ? getLayoutRect(arrowElement) : { + width: 0, + height: 0 + }; + var arrowPaddingObject = state.modifiersData['arrow#persistent'] ? state.modifiersData['arrow#persistent'].padding : getFreshSideObject(); + var arrowPaddingMin = arrowPaddingObject[mainSide]; + var arrowPaddingMax = arrowPaddingObject[altSide]; // If the reference length is smaller than the arrow length, we don't want + // to include its full size in the calculation. If the reference is small + // and near the edge of a boundary, the popper can overflow even if the + // reference is not overflowing as well (e.g. virtual elements with no + // width or height) + + var arrowLen = within(0, referenceRect[len], arrowRect[len]); + var minOffset = isBasePlacement ? referenceRect[len] / 2 - additive - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis : minLen - arrowLen - arrowPaddingMin - normalizedTetherOffsetValue.mainAxis; + var maxOffset = isBasePlacement ? -referenceRect[len] / 2 + additive + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis : maxLen + arrowLen + arrowPaddingMax + normalizedTetherOffsetValue.mainAxis; + var arrowOffsetParent = state.elements.arrow && getOffsetParent(state.elements.arrow); + var clientOffset = arrowOffsetParent ? mainAxis === 'y' ? arrowOffsetParent.clientTop || 0 : arrowOffsetParent.clientLeft || 0 : 0; + var offsetModifierValue = (_offsetModifierState$ = offsetModifierState == null ? void 0 : offsetModifierState[mainAxis]) != null ? _offsetModifierState$ : 0; + var tetherMin = offset + minOffset - offsetModifierValue - clientOffset; + var tetherMax = offset + maxOffset - offsetModifierValue; + var preventedOffset = within(tether ? min(min$1, tetherMin) : min$1, offset, tether ? max(max$1, tetherMax) : max$1); + popperOffsets[mainAxis] = preventedOffset; + data[mainAxis] = preventedOffset - offset; + } + + if (checkAltAxis) { + var _offsetModifierState$2; + + var _mainSide = mainAxis === 'x' ? top : left; + + var _altSide = mainAxis === 'x' ? bottom : right; + + var _offset = popperOffsets[altAxis]; + + var _len = altAxis === 'y' ? 'height' : 'width'; + + var _min = _offset + overflow[_mainSide]; + + var _max = _offset - overflow[_altSide]; + + var isOriginSide = [top, left].indexOf(basePlacement) !== -1; + + var _offsetModifierValue = (_offsetModifierState$2 = offsetModifierState == null ? void 0 : offsetModifierState[altAxis]) != null ? _offsetModifierState$2 : 0; + + var _tetherMin = isOriginSide ? _min : _offset - referenceRect[_len] - popperRect[_len] - _offsetModifierValue + normalizedTetherOffsetValue.altAxis; + + var _tetherMax = isOriginSide ? _offset + referenceRect[_len] + popperRect[_len] - _offsetModifierValue - normalizedTetherOffsetValue.altAxis : _max; + + var _preventedOffset = tether && isOriginSide ? withinMaxClamp(_tetherMin, _offset, _tetherMax) : within(tether ? _tetherMin : _min, _offset, tether ? _tetherMax : _max); + + popperOffsets[altAxis] = _preventedOffset; + data[altAxis] = _preventedOffset - _offset; + } + + state.modifiersData[name] = data; + } // eslint-disable-next-line import/no-unused-modules + + + const preventOverflow$1 = { + name: 'preventOverflow', + enabled: true, + phase: 'main', + fn: preventOverflow, + requiresIfExists: ['offset'] + }; + + function getHTMLElementScroll(element) { + return { + scrollLeft: element.scrollLeft, + scrollTop: element.scrollTop + }; + } + + function getNodeScroll(node) { + if (node === getWindow(node) || !isHTMLElement(node)) { + return getWindowScroll(node); + } else { + return getHTMLElementScroll(node); + } + } + + function isElementScaled(element) { + var rect = element.getBoundingClientRect(); + var scaleX = round(rect.width) / element.offsetWidth || 1; + var scaleY = round(rect.height) / element.offsetHeight || 1; + return scaleX !== 1 || scaleY !== 1; + } // Returns the composite rect of an element relative to its offsetParent. + // Composite means it takes into account transforms as well as layout. + + + function getCompositeRect(elementOrVirtualElement, offsetParent, isFixed) { + if (isFixed === void 0) { + isFixed = false; + } + + var isOffsetParentAnElement = isHTMLElement(offsetParent); + var offsetParentIsScaled = isHTMLElement(offsetParent) && isElementScaled(offsetParent); + var documentElement = getDocumentElement(offsetParent); + var rect = getBoundingClientRect(elementOrVirtualElement, offsetParentIsScaled, isFixed); + var scroll = { + scrollLeft: 0, + scrollTop: 0 + }; + var offsets = { + x: 0, + y: 0 + }; + + if (isOffsetParentAnElement || !isOffsetParentAnElement && !isFixed) { + if (getNodeName(offsetParent) !== 'body' || // https://github.com/popperjs/popper-core/issues/1078 + isScrollParent(documentElement)) { + scroll = getNodeScroll(offsetParent); + } + + if (isHTMLElement(offsetParent)) { + offsets = getBoundingClientRect(offsetParent, true); + offsets.x += offsetParent.clientLeft; + offsets.y += offsetParent.clientTop; + } else if (documentElement) { + offsets.x = getWindowScrollBarX(documentElement); + } + } + + return { + x: rect.left + scroll.scrollLeft - offsets.x, + y: rect.top + scroll.scrollTop - offsets.y, + width: rect.width, + height: rect.height + }; + } + + function order(modifiers) { + var map = new Map(); + var visited = new Set(); + var result = []; + modifiers.forEach(function (modifier) { + map.set(modifier.name, modifier); + }); // On visiting object, check for its dependencies and visit them recursively + + function sort(modifier) { + visited.add(modifier.name); + var requires = [].concat(modifier.requires || [], modifier.requiresIfExists || []); + requires.forEach(function (dep) { + if (!visited.has(dep)) { + var depModifier = map.get(dep); + + if (depModifier) { + sort(depModifier); + } + } + }); + result.push(modifier); + } + + modifiers.forEach(function (modifier) { + if (!visited.has(modifier.name)) { + // check for visited object + sort(modifier); + } + }); + return result; + } + + function orderModifiers(modifiers) { + // order based on dependencies + var orderedModifiers = order(modifiers); // order based on phase + + return modifierPhases.reduce(function (acc, phase) { + return acc.concat(orderedModifiers.filter(function (modifier) { + return modifier.phase === phase; + })); + }, []); + } + + function debounce(fn) { + var pending; + return function () { + if (!pending) { + pending = new Promise(function (resolve) { + Promise.resolve().then(function () { + pending = undefined; + resolve(fn()); + }); + }); + } + + return pending; + }; + } + + function mergeByName(modifiers) { + var merged = modifiers.reduce(function (merged, current) { + var existing = merged[current.name]; + merged[current.name] = existing ? Object.assign({}, existing, current, { + options: Object.assign({}, existing.options, current.options), + data: Object.assign({}, existing.data, current.data) + }) : current; + return merged; + }, {}); // IE11 does not support Object.values + + return Object.keys(merged).map(function (key) { + return merged[key]; + }); + } + + var DEFAULT_OPTIONS = { + placement: 'bottom', + modifiers: [], + strategy: 'absolute' + }; + + function areValidElements() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + return !args.some(function (element) { + return !(element && typeof element.getBoundingClientRect === 'function'); + }); + } + + function popperGenerator(generatorOptions) { + if (generatorOptions === void 0) { + generatorOptions = {}; + } + + var _generatorOptions = generatorOptions, + _generatorOptions$def = _generatorOptions.defaultModifiers, + defaultModifiers = _generatorOptions$def === void 0 ? [] : _generatorOptions$def, + _generatorOptions$def2 = _generatorOptions.defaultOptions, + defaultOptions = _generatorOptions$def2 === void 0 ? DEFAULT_OPTIONS : _generatorOptions$def2; + return function createPopper(reference, popper, options) { + if (options === void 0) { + options = defaultOptions; + } + + var state = { + placement: 'bottom', + orderedModifiers: [], + options: Object.assign({}, DEFAULT_OPTIONS, defaultOptions), + modifiersData: {}, + elements: { + reference: reference, + popper: popper + }, + attributes: {}, + styles: {} + }; + var effectCleanupFns = []; + var isDestroyed = false; + var instance = { + state: state, + setOptions: function setOptions(setOptionsAction) { + var options = typeof setOptionsAction === 'function' ? setOptionsAction(state.options) : setOptionsAction; + cleanupModifierEffects(); + state.options = Object.assign({}, defaultOptions, state.options, options); + state.scrollParents = { + reference: isElement(reference) ? listScrollParents(reference) : reference.contextElement ? listScrollParents(reference.contextElement) : [], + popper: listScrollParents(popper) + }; // Orders the modifiers based on their dependencies and `phase` + // properties + + var orderedModifiers = orderModifiers(mergeByName([].concat(defaultModifiers, state.options.modifiers))); // Strip out disabled modifiers + + state.orderedModifiers = orderedModifiers.filter(function (m) { + return m.enabled; + }); + runModifierEffects(); + return instance.update(); + }, + // Sync update – it will always be executed, even if not necessary. This + // is useful for low frequency updates where sync behavior simplifies the + // logic. + // For high frequency updates (e.g. `resize` and `scroll` events), always + // prefer the async Popper#update method + forceUpdate: function forceUpdate() { + if (isDestroyed) { + return; + } + + var _state$elements = state.elements, + reference = _state$elements.reference, + popper = _state$elements.popper; // Don't proceed if `reference` or `popper` are not valid elements + // anymore + + if (!areValidElements(reference, popper)) { + return; + } // Store the reference and popper rects to be read by modifiers + + + state.rects = { + reference: getCompositeRect(reference, getOffsetParent(popper), state.options.strategy === 'fixed'), + popper: getLayoutRect(popper) + }; // Modifiers have the ability to reset the current update cycle. The + // most common use case for this is the `flip` modifier changing the + // placement, which then needs to re-run all the modifiers, because the + // logic was previously ran for the previous placement and is therefore + // stale/incorrect + + state.reset = false; + state.placement = state.options.placement; // On each update cycle, the `modifiersData` property for each modifier + // is filled with the initial data specified by the modifier. This means + // it doesn't persist and is fresh on each update. + // To ensure persistent data, use `${name}#persistent` + + state.orderedModifiers.forEach(function (modifier) { + return state.modifiersData[modifier.name] = Object.assign({}, modifier.data); + }); + + for (var index = 0; index < state.orderedModifiers.length; index++) { + if (state.reset === true) { + state.reset = false; + index = -1; + continue; + } + + var _state$orderedModifie = state.orderedModifiers[index], + fn = _state$orderedModifie.fn, + _state$orderedModifie2 = _state$orderedModifie.options, + _options = _state$orderedModifie2 === void 0 ? {} : _state$orderedModifie2, + name = _state$orderedModifie.name; + + if (typeof fn === 'function') { + state = fn({ + state: state, + options: _options, + name: name, + instance: instance + }) || state; + } + } + }, + // Async and optimistically optimized update – it will not be executed if + // not necessary (debounced to run at most once-per-tick) + update: debounce(function () { + return new Promise(function (resolve) { + instance.forceUpdate(); + resolve(state); + }); + }), + destroy: function destroy() { + cleanupModifierEffects(); + isDestroyed = true; + } + }; + + if (!areValidElements(reference, popper)) { + return instance; + } + + instance.setOptions(options).then(function (state) { + if (!isDestroyed && options.onFirstUpdate) { + options.onFirstUpdate(state); + } + }); // Modifiers have the ability to execute arbitrary code before the first + // update cycle runs. They will be executed in the same order as the update + // cycle. This is useful when a modifier adds some persistent data that + // other modifiers need to use, but the modifier is run after the dependent + // one. + + function runModifierEffects() { + state.orderedModifiers.forEach(function (_ref) { + var name = _ref.name, + _ref$options = _ref.options, + options = _ref$options === void 0 ? {} : _ref$options, + effect = _ref.effect; + + if (typeof effect === 'function') { + var cleanupFn = effect({ + state: state, + name: name, + instance: instance, + options: options + }); + + var noopFn = function noopFn() {}; + + effectCleanupFns.push(cleanupFn || noopFn); + } + }); + } + + function cleanupModifierEffects() { + effectCleanupFns.forEach(function (fn) { + return fn(); + }); + effectCleanupFns = []; + } + + return instance; + }; + } + var createPopper$2 = /*#__PURE__*/popperGenerator(); // eslint-disable-next-line import/no-unused-modules + + var defaultModifiers$1 = [eventListeners, popperOffsets$1, computeStyles$1, applyStyles$1]; + var createPopper$1 = /*#__PURE__*/popperGenerator({ + defaultModifiers: defaultModifiers$1 + }); // eslint-disable-next-line import/no-unused-modules + + var defaultModifiers = [eventListeners, popperOffsets$1, computeStyles$1, applyStyles$1, offset$1, flip$1, preventOverflow$1, arrow$1, hide$1]; + var createPopper = /*#__PURE__*/popperGenerator({ + defaultModifiers: defaultModifiers + }); // eslint-disable-next-line import/no-unused-modules + + const Popper = /*#__PURE__*/Object.freeze(/*#__PURE__*/Object.defineProperty({ + __proto__: null, + afterMain, + afterRead, + afterWrite, + applyStyles: applyStyles$1, + arrow: arrow$1, + auto, + basePlacements, + beforeMain, + beforeRead, + beforeWrite, + bottom, + clippingParents, + computeStyles: computeStyles$1, + createPopper, + createPopperBase: createPopper$2, + createPopperLite: createPopper$1, + detectOverflow, + end, + eventListeners, + flip: flip$1, + hide: hide$1, + left, + main, + modifierPhases, + offset: offset$1, + placements, + popper, + popperGenerator, + popperOffsets: popperOffsets$1, + preventOverflow: preventOverflow$1, + read, + reference, + right, + start, + top, + variationPlacements, + viewport, + write + }, Symbol.toStringTag, { value: 'Module' })); + + /** + * -------------------------------------------------------------------------- + * Bootstrap dropdown.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$a = 'dropdown'; + const DATA_KEY$6 = 'bs.dropdown'; + const EVENT_KEY$6 = `.${DATA_KEY$6}`; + const DATA_API_KEY$3 = '.data-api'; + const ESCAPE_KEY$2 = 'Escape'; + const TAB_KEY$1 = 'Tab'; + const ARROW_UP_KEY$1 = 'ArrowUp'; + const ARROW_DOWN_KEY$1 = 'ArrowDown'; + const RIGHT_MOUSE_BUTTON = 2; // MouseEvent.button value for the secondary button, usually the right button + + const EVENT_HIDE$5 = `hide${EVENT_KEY$6}`; + const EVENT_HIDDEN$5 = `hidden${EVENT_KEY$6}`; + const EVENT_SHOW$5 = `show${EVENT_KEY$6}`; + const EVENT_SHOWN$5 = `shown${EVENT_KEY$6}`; + const EVENT_CLICK_DATA_API$3 = `click${EVENT_KEY$6}${DATA_API_KEY$3}`; + const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY$6}${DATA_API_KEY$3}`; + const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY$6}${DATA_API_KEY$3}`; + const CLASS_NAME_SHOW$6 = 'show'; + const CLASS_NAME_DROPUP = 'dropup'; + const CLASS_NAME_DROPEND = 'dropend'; + const CLASS_NAME_DROPSTART = 'dropstart'; + const CLASS_NAME_DROPUP_CENTER = 'dropup-center'; + const CLASS_NAME_DROPDOWN_CENTER = 'dropdown-center'; + const SELECTOR_DATA_TOGGLE$3 = '[data-bs-toggle="dropdown"]:not(.disabled):not(:disabled)'; + const SELECTOR_DATA_TOGGLE_SHOWN = `${SELECTOR_DATA_TOGGLE$3}.${CLASS_NAME_SHOW$6}`; + const SELECTOR_MENU = '.dropdown-menu'; + const SELECTOR_NAVBAR = '.navbar'; + const SELECTOR_NAVBAR_NAV = '.navbar-nav'; + const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'; + const PLACEMENT_TOP = isRTL() ? 'top-end' : 'top-start'; + const PLACEMENT_TOPEND = isRTL() ? 'top-start' : 'top-end'; + const PLACEMENT_BOTTOM = isRTL() ? 'bottom-end' : 'bottom-start'; + const PLACEMENT_BOTTOMEND = isRTL() ? 'bottom-start' : 'bottom-end'; + const PLACEMENT_RIGHT = isRTL() ? 'left-start' : 'right-start'; + const PLACEMENT_LEFT = isRTL() ? 'right-start' : 'left-start'; + const PLACEMENT_TOPCENTER = 'top'; + const PLACEMENT_BOTTOMCENTER = 'bottom'; + const Default$9 = { + autoClose: true, + boundary: 'clippingParents', + display: 'dynamic', + offset: [0, 2], + popperConfig: null, + reference: 'toggle' + }; + const DefaultType$9 = { + autoClose: '(boolean|string)', + boundary: '(string|element)', + display: 'string', + offset: '(array|string|function)', + popperConfig: '(null|object|function)', + reference: '(string|element|object)' + }; + + /** + * Class definition + */ + + class Dropdown extends BaseComponent { + constructor(element, config) { + super(element, config); + this._popper = null; + this._parent = this._element.parentNode; // dropdown wrapper + // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/ + this._menu = SelectorEngine.next(this._element, SELECTOR_MENU)[0] || SelectorEngine.prev(this._element, SELECTOR_MENU)[0] || SelectorEngine.findOne(SELECTOR_MENU, this._parent); + this._inNavbar = this._detectNavbar(); + } + + // Getters + static get Default() { + return Default$9; + } + static get DefaultType() { + return DefaultType$9; + } + static get NAME() { + return NAME$a; + } + + // Public + toggle() { + return this._isShown() ? this.hide() : this.show(); + } + show() { + if (isDisabled(this._element) || this._isShown()) { + return; + } + const relatedTarget = { + relatedTarget: this._element + }; + const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$5, relatedTarget); + if (showEvent.defaultPrevented) { + return; + } + this._createPopper(); + + // If this is a touch-enabled device we add extra + // empty mouseover listeners to the body's immediate children; + // only needed because of broken event delegation on iOS + // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html + if ('ontouchstart' in document.documentElement && !this._parent.closest(SELECTOR_NAVBAR_NAV)) { + for (const element of [].concat(...document.body.children)) { + EventHandler.on(element, 'mouseover', noop); + } + } + this._element.focus(); + this._element.setAttribute('aria-expanded', true); + this._menu.classList.add(CLASS_NAME_SHOW$6); + this._element.classList.add(CLASS_NAME_SHOW$6); + EventHandler.trigger(this._element, EVENT_SHOWN$5, relatedTarget); + } + hide() { + if (isDisabled(this._element) || !this._isShown()) { + return; + } + const relatedTarget = { + relatedTarget: this._element + }; + this._completeHide(relatedTarget); + } + dispose() { + if (this._popper) { + this._popper.destroy(); + } + super.dispose(); + } + update() { + this._inNavbar = this._detectNavbar(); + if (this._popper) { + this._popper.update(); + } + } + + // Private + _completeHide(relatedTarget) { + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$5, 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) { + for (const element of [].concat(...document.body.children)) { + EventHandler.off(element, 'mouseover', noop); + } + } + if (this._popper) { + this._popper.destroy(); + } + this._menu.classList.remove(CLASS_NAME_SHOW$6); + this._element.classList.remove(CLASS_NAME_SHOW$6); + this._element.setAttribute('aria-expanded', 'false'); + Manipulator.removeDataAttribute(this._menu, 'popper'); + EventHandler.trigger(this._element, EVENT_HIDDEN$5, relatedTarget); + } + _getConfig(config) { + config = super._getConfig(config); + if (typeof config.reference === 'object' && !isElement$1(config.reference) && typeof config.reference.getBoundingClientRect !== 'function') { + // Popper virtual elements require a getBoundingClientRect method + throw new TypeError(`${NAME$a.toUpperCase()}: Option "reference" provided type "object" without a required "getBoundingClientRect" method.`); + } + return config; + } + _createPopper() { + if (typeof Popper === 'undefined') { + throw new TypeError('Bootstrap\'s dropdowns require Popper (https://popper.js.org)'); + } + let referenceElement = this._element; + if (this._config.reference === 'parent') { + referenceElement = this._parent; + } else if (isElement$1(this._config.reference)) { + referenceElement = getElement(this._config.reference); + } else if (typeof this._config.reference === 'object') { + referenceElement = this._config.reference; + } + const popperConfig = this._getPopperConfig(); + this._popper = createPopper(referenceElement, this._menu, popperConfig); + } + _isShown() { + return this._menu.classList.contains(CLASS_NAME_SHOW$6); + } + _getPlacement() { + const parentDropdown = this._parent; + if (parentDropdown.classList.contains(CLASS_NAME_DROPEND)) { + return PLACEMENT_RIGHT; + } + if (parentDropdown.classList.contains(CLASS_NAME_DROPSTART)) { + return PLACEMENT_LEFT; + } + if (parentDropdown.classList.contains(CLASS_NAME_DROPUP_CENTER)) { + return PLACEMENT_TOPCENTER; + } + if (parentDropdown.classList.contains(CLASS_NAME_DROPDOWN_CENTER)) { + return PLACEMENT_BOTTOMCENTER; + } + + // We need to trim the value because custom properties can also include spaces + const isEnd = getComputedStyle(this._menu).getPropertyValue('--bs-position').trim() === 'end'; + if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) { + return isEnd ? PLACEMENT_TOPEND : PLACEMENT_TOP; + } + return isEnd ? PLACEMENT_BOTTOMEND : PLACEMENT_BOTTOM; + } + _detectNavbar() { + return this._element.closest(SELECTOR_NAVBAR) !== null; + } + _getOffset() { + const { + offset + } = this._config; + if (typeof offset === 'string') { + return offset.split(',').map(value => Number.parseInt(value, 10)); + } + if (typeof offset === 'function') { + return popperData => offset(popperData, this._element); + } + return offset; + } + _getPopperConfig() { + const defaultBsPopperConfig = { + placement: this._getPlacement(), + modifiers: [{ + name: 'preventOverflow', + options: { + boundary: this._config.boundary + } + }, { + name: 'offset', + options: { + offset: this._getOffset() + } + }] + }; + + // Disable Popper if we have a static display or Dropdown is in Navbar + if (this._inNavbar || this._config.display === 'static') { + Manipulator.setDataAttribute(this._menu, 'popper', 'static'); // TODO: v6 remove + defaultBsPopperConfig.modifiers = [{ + name: 'applyStyles', + enabled: false + }]; + } + return { + ...defaultBsPopperConfig, + ...execute(this._config.popperConfig, [defaultBsPopperConfig]) + }; + } + _selectMenuItem({ + key, + target + }) { + const items = SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, this._menu).filter(element => isVisible(element)); + if (!items.length) { + return; + } + + // if target isn't included in items (e.g. when expanding the dropdown) + // allow cycling to get the last item in case key equals ARROW_UP_KEY + getNextActiveElement(items, target, key === ARROW_DOWN_KEY$1, !items.includes(target)).focus(); + } + + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Dropdown.getOrCreateInstance(this, config); + if (typeof config !== 'string') { + return; + } + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`); + } + data[config](); + }); + } + static clearMenus(event) { + if (event.button === RIGHT_MOUSE_BUTTON || event.type === 'keyup' && event.key !== TAB_KEY$1) { + return; + } + const openToggles = SelectorEngine.find(SELECTOR_DATA_TOGGLE_SHOWN); + for (const toggle of openToggles) { + const context = Dropdown.getInstance(toggle); + if (!context || context._config.autoClose === false) { + continue; + } + const composedPath = event.composedPath(); + const isMenuTarget = composedPath.includes(context._menu); + if (composedPath.includes(context._element) || context._config.autoClose === 'inside' && !isMenuTarget || context._config.autoClose === 'outside' && isMenuTarget) { + continue; + } + + // Tab navigation through the dropdown menu or events from contained inputs shouldn't close the menu + if (context._menu.contains(event.target) && (event.type === 'keyup' && event.key === TAB_KEY$1 || /input|select|option|textarea|form/i.test(event.target.tagName))) { + continue; + } + const relatedTarget = { + relatedTarget: context._element + }; + if (event.type === 'click') { + relatedTarget.clickEvent = event; + } + context._completeHide(relatedTarget); + } + } + static dataApiKeydownHandler(event) { + // If not an UP | DOWN | ESCAPE key => not a dropdown command + // If input/textarea && if key is other than ESCAPE => not a dropdown command + + const isInput = /input|textarea/i.test(event.target.tagName); + const isEscapeEvent = event.key === ESCAPE_KEY$2; + const isUpOrDownEvent = [ARROW_UP_KEY$1, ARROW_DOWN_KEY$1].includes(event.key); + if (!isUpOrDownEvent && !isEscapeEvent) { + return; + } + if (isInput && !isEscapeEvent) { + return; + } + event.preventDefault(); + + // TODO: v6 revert #37011 & change markup https://getbootstrap.com/docs/5.3/forms/input-group/ + const getToggleButton = this.matches(SELECTOR_DATA_TOGGLE$3) ? this : SelectorEngine.prev(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.next(this, SELECTOR_DATA_TOGGLE$3)[0] || SelectorEngine.findOne(SELECTOR_DATA_TOGGLE$3, event.delegateTarget.parentNode); + const instance = Dropdown.getOrCreateInstance(getToggleButton); + if (isUpOrDownEvent) { + event.stopPropagation(); + instance.show(); + instance._selectMenuItem(event); + return; + } + if (instance._isShown()) { + // else is escape and we check if it is shown + event.stopPropagation(); + instance.hide(); + getToggleButton.focus(); + } + } + } + + /** + * Data API implementation + */ + + EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE$3, Dropdown.dataApiKeydownHandler); + EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler); + EventHandler.on(document, EVENT_CLICK_DATA_API$3, Dropdown.clearMenus); + EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus); + EventHandler.on(document, EVENT_CLICK_DATA_API$3, SELECTOR_DATA_TOGGLE$3, function (event) { + event.preventDefault(); + Dropdown.getOrCreateInstance(this).toggle(); + }); + + /** + * jQuery + */ + + defineJQueryPlugin(Dropdown); + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/backdrop.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$9 = 'backdrop'; + const CLASS_NAME_FADE$4 = 'fade'; + const CLASS_NAME_SHOW$5 = 'show'; + const EVENT_MOUSEDOWN = `mousedown.bs.${NAME$9}`; + const Default$8 = { + className: 'modal-backdrop', + clickCallback: null, + isAnimated: false, + isVisible: true, + // if false, we use the backdrop helper without adding any element to the dom + rootElement: 'body' // give the choice to place backdrop under different elements + }; + + const DefaultType$8 = { + className: 'string', + clickCallback: '(function|null)', + isAnimated: 'boolean', + isVisible: 'boolean', + rootElement: '(element|string)' + }; + + /** + * Class definition + */ + + class Backdrop extends Config { + constructor(config) { + super(); + this._config = this._getConfig(config); + this._isAppended = false; + this._element = null; + } + + // Getters + static get Default() { + return Default$8; + } + static get DefaultType() { + return DefaultType$8; + } + static get NAME() { + return NAME$9; + } + + // Public + show(callback) { + if (!this._config.isVisible) { + execute(callback); + return; + } + this._append(); + const element = this._getElement(); + if (this._config.isAnimated) { + reflow(element); + } + element.classList.add(CLASS_NAME_SHOW$5); + this._emulateAnimation(() => { + execute(callback); + }); + } + hide(callback) { + if (!this._config.isVisible) { + execute(callback); + return; + } + this._getElement().classList.remove(CLASS_NAME_SHOW$5); + this._emulateAnimation(() => { + this.dispose(); + execute(callback); + }); + } + dispose() { + if (!this._isAppended) { + return; + } + EventHandler.off(this._element, EVENT_MOUSEDOWN); + this._element.remove(); + this._isAppended = false; + } + + // Private + _getElement() { + if (!this._element) { + const backdrop = document.createElement('div'); + backdrop.className = this._config.className; + if (this._config.isAnimated) { + backdrop.classList.add(CLASS_NAME_FADE$4); + } + this._element = backdrop; + } + return this._element; + } + _configAfterMerge(config) { + // use getElement() with the default "body" to get a fresh Element on each instantiation + config.rootElement = getElement(config.rootElement); + return config; + } + _append() { + if (this._isAppended) { + return; + } + const element = this._getElement(); + this._config.rootElement.append(element); + EventHandler.on(element, EVENT_MOUSEDOWN, () => { + execute(this._config.clickCallback); + }); + this._isAppended = true; + } + _emulateAnimation(callback) { + executeAfterTransition(callback, this._getElement(), this._config.isAnimated); + } + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/focustrap.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$8 = 'focustrap'; + const DATA_KEY$5 = 'bs.focustrap'; + const EVENT_KEY$5 = `.${DATA_KEY$5}`; + const EVENT_FOCUSIN$2 = `focusin${EVENT_KEY$5}`; + const EVENT_KEYDOWN_TAB = `keydown.tab${EVENT_KEY$5}`; + const TAB_KEY = 'Tab'; + const TAB_NAV_FORWARD = 'forward'; + const TAB_NAV_BACKWARD = 'backward'; + const Default$7 = { + autofocus: true, + trapElement: null // The element to trap focus inside of + }; + + const DefaultType$7 = { + autofocus: 'boolean', + trapElement: 'element' + }; + + /** + * Class definition + */ + + class FocusTrap extends Config { + constructor(config) { + super(); + this._config = this._getConfig(config); + this._isActive = false; + this._lastTabNavDirection = null; + } + + // Getters + static get Default() { + return Default$7; + } + static get DefaultType() { + return DefaultType$7; + } + static get NAME() { + return NAME$8; + } + + // Public + activate() { + if (this._isActive) { + return; + } + if (this._config.autofocus) { + this._config.trapElement.focus(); + } + EventHandler.off(document, EVENT_KEY$5); // guard against infinite focus loop + EventHandler.on(document, EVENT_FOCUSIN$2, event => this._handleFocusin(event)); + EventHandler.on(document, EVENT_KEYDOWN_TAB, event => this._handleKeydown(event)); + this._isActive = true; + } + deactivate() { + if (!this._isActive) { + return; + } + this._isActive = false; + EventHandler.off(document, EVENT_KEY$5); + } + + // Private + _handleFocusin(event) { + const { + trapElement + } = this._config; + if (event.target === document || event.target === trapElement || trapElement.contains(event.target)) { + return; + } + const elements = SelectorEngine.focusableChildren(trapElement); + if (elements.length === 0) { + trapElement.focus(); + } else if (this._lastTabNavDirection === TAB_NAV_BACKWARD) { + elements[elements.length - 1].focus(); + } else { + elements[0].focus(); + } + } + _handleKeydown(event) { + if (event.key !== TAB_KEY) { + return; + } + this._lastTabNavDirection = event.shiftKey ? TAB_NAV_BACKWARD : TAB_NAV_FORWARD; + } + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/scrollBar.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'; + const SELECTOR_STICKY_CONTENT = '.sticky-top'; + const PROPERTY_PADDING = 'padding-right'; + const PROPERTY_MARGIN = 'margin-right'; + + /** + * Class definition + */ + + class ScrollBarHelper { + constructor() { + this._element = document.body; + } + + // Public + getWidth() { + // https://developer.mozilla.org/en-US/docs/Web/API/Window/innerWidth#usage_notes + const documentWidth = document.documentElement.clientWidth; + return Math.abs(window.innerWidth - documentWidth); + } + hide() { + const width = this.getWidth(); + this._disableOverFlow(); + // give padding to element to balance the hidden scrollbar width + this._setElementAttributes(this._element, PROPERTY_PADDING, calculatedValue => calculatedValue + width); + // trick: We adjust positive paddingRight and negative marginRight to sticky-top elements to keep showing fullwidth + this._setElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING, calculatedValue => calculatedValue + width); + this._setElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN, calculatedValue => calculatedValue - width); + } + reset() { + this._resetElementAttributes(this._element, 'overflow'); + this._resetElementAttributes(this._element, PROPERTY_PADDING); + this._resetElementAttributes(SELECTOR_FIXED_CONTENT, PROPERTY_PADDING); + this._resetElementAttributes(SELECTOR_STICKY_CONTENT, PROPERTY_MARGIN); + } + isOverflowing() { + return this.getWidth() > 0; + } + + // Private + _disableOverFlow() { + this._saveInitialAttribute(this._element, 'overflow'); + this._element.style.overflow = 'hidden'; + } + _setElementAttributes(selector, styleProperty, callback) { + const scrollbarWidth = this.getWidth(); + const manipulationCallBack = element => { + if (element !== this._element && window.innerWidth > element.clientWidth + scrollbarWidth) { + return; + } + this._saveInitialAttribute(element, styleProperty); + const calculatedValue = window.getComputedStyle(element).getPropertyValue(styleProperty); + element.style.setProperty(styleProperty, `${callback(Number.parseFloat(calculatedValue))}px`); + }; + this._applyManipulationCallback(selector, manipulationCallBack); + } + _saveInitialAttribute(element, styleProperty) { + const actualValue = element.style.getPropertyValue(styleProperty); + if (actualValue) { + Manipulator.setDataAttribute(element, styleProperty, actualValue); + } + } + _resetElementAttributes(selector, styleProperty) { + const manipulationCallBack = element => { + const value = Manipulator.getDataAttribute(element, styleProperty); + // We only want to remove the property if the value is `null`; the value can also be zero + if (value === null) { + element.style.removeProperty(styleProperty); + return; + } + Manipulator.removeDataAttribute(element, styleProperty); + element.style.setProperty(styleProperty, value); + }; + this._applyManipulationCallback(selector, manipulationCallBack); + } + _applyManipulationCallback(selector, callBack) { + if (isElement$1(selector)) { + callBack(selector); + return; + } + for (const sel of SelectorEngine.find(selector, this._element)) { + callBack(sel); + } + } + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap modal.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$7 = 'modal'; + const DATA_KEY$4 = 'bs.modal'; + const EVENT_KEY$4 = `.${DATA_KEY$4}`; + const DATA_API_KEY$2 = '.data-api'; + const ESCAPE_KEY$1 = 'Escape'; + const EVENT_HIDE$4 = `hide${EVENT_KEY$4}`; + const EVENT_HIDE_PREVENTED$1 = `hidePrevented${EVENT_KEY$4}`; + const EVENT_HIDDEN$4 = `hidden${EVENT_KEY$4}`; + const EVENT_SHOW$4 = `show${EVENT_KEY$4}`; + const EVENT_SHOWN$4 = `shown${EVENT_KEY$4}`; + const EVENT_RESIZE$1 = `resize${EVENT_KEY$4}`; + const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY$4}`; + const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY$4}`; + const EVENT_KEYDOWN_DISMISS$1 = `keydown.dismiss${EVENT_KEY$4}`; + const EVENT_CLICK_DATA_API$2 = `click${EVENT_KEY$4}${DATA_API_KEY$2}`; + const CLASS_NAME_OPEN = 'modal-open'; + const CLASS_NAME_FADE$3 = 'fade'; + const CLASS_NAME_SHOW$4 = 'show'; + const CLASS_NAME_STATIC = 'modal-static'; + const OPEN_SELECTOR$1 = '.modal.show'; + const SELECTOR_DIALOG = '.modal-dialog'; + const SELECTOR_MODAL_BODY = '.modal-body'; + const SELECTOR_DATA_TOGGLE$2 = '[data-bs-toggle="modal"]'; + const Default$6 = { + backdrop: true, + focus: true, + keyboard: true + }; + const DefaultType$6 = { + backdrop: '(boolean|string)', + focus: 'boolean', + keyboard: 'boolean' + }; + + /** + * Class definition + */ + + class Modal extends BaseComponent { + constructor(element, config) { + super(element, config); + this._dialog = SelectorEngine.findOne(SELECTOR_DIALOG, this._element); + this._backdrop = this._initializeBackDrop(); + this._focustrap = this._initializeFocusTrap(); + this._isShown = false; + this._isTransitioning = false; + this._scrollBar = new ScrollBarHelper(); + this._addEventListeners(); + } + + // Getters + static get Default() { + return Default$6; + } + static get DefaultType() { + return DefaultType$6; + } + static get NAME() { + return NAME$7; + } + + // Public + toggle(relatedTarget) { + return this._isShown ? this.hide() : this.show(relatedTarget); + } + show(relatedTarget) { + if (this._isShown || this._isTransitioning) { + return; + } + const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$4, { + relatedTarget + }); + if (showEvent.defaultPrevented) { + return; + } + this._isShown = true; + this._isTransitioning = true; + this._scrollBar.hide(); + document.body.classList.add(CLASS_NAME_OPEN); + this._adjustDialog(); + this._backdrop.show(() => this._showElement(relatedTarget)); + } + hide() { + if (!this._isShown || this._isTransitioning) { + return; + } + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$4); + if (hideEvent.defaultPrevented) { + return; + } + this._isShown = false; + this._isTransitioning = true; + this._focustrap.deactivate(); + this._element.classList.remove(CLASS_NAME_SHOW$4); + this._queueCallback(() => this._hideModal(), this._element, this._isAnimated()); + } + dispose() { + EventHandler.off(window, EVENT_KEY$4); + EventHandler.off(this._dialog, EVENT_KEY$4); + this._backdrop.dispose(); + this._focustrap.deactivate(); + super.dispose(); + } + handleUpdate() { + this._adjustDialog(); + } + + // 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() + }); + } + _initializeFocusTrap() { + return new FocusTrap({ + trapElement: this._element + }); + } + _showElement(relatedTarget) { + // try to append dynamic modal + if (!document.body.contains(this._element)) { + document.body.append(this._element); + } + this._element.style.display = 'block'; + this._element.removeAttribute('aria-hidden'); + this._element.setAttribute('aria-modal', true); + this._element.setAttribute('role', 'dialog'); + this._element.scrollTop = 0; + const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog); + if (modalBody) { + modalBody.scrollTop = 0; + } + reflow(this._element); + this._element.classList.add(CLASS_NAME_SHOW$4); + const transitionComplete = () => { + if (this._config.focus) { + this._focustrap.activate(); + } + this._isTransitioning = false; + EventHandler.trigger(this._element, EVENT_SHOWN$4, { + relatedTarget + }); + }; + this._queueCallback(transitionComplete, this._dialog, this._isAnimated()); + } + _addEventListeners() { + EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS$1, event => { + if (event.key !== ESCAPE_KEY$1) { + return; + } + if (this._config.keyboard) { + this.hide(); + return; + } + this._triggerBackdropTransition(); + }); + EventHandler.on(window, EVENT_RESIZE$1, () => { + if (this._isShown && !this._isTransitioning) { + this._adjustDialog(); + } + }); + EventHandler.on(this._element, EVENT_MOUSEDOWN_DISMISS, event => { + // a bad trick to segregate clicks that may start inside dialog but end outside, and avoid listen to scrollbar clicks + EventHandler.one(this._element, EVENT_CLICK_DISMISS, event2 => { + if (this._element !== event.target || this._element !== event2.target) { + return; + } + if (this._config.backdrop === 'static') { + this._triggerBackdropTransition(); + return; + } + if (this._config.backdrop) { + this.hide(); + } + }); + }); + } + _hideModal() { + this._element.style.display = 'none'; + this._element.setAttribute('aria-hidden', true); + this._element.removeAttribute('aria-modal'); + this._element.removeAttribute('role'); + this._isTransitioning = false; + this._backdrop.hide(() => { + document.body.classList.remove(CLASS_NAME_OPEN); + this._resetAdjustments(); + this._scrollBar.reset(); + EventHandler.trigger(this._element, EVENT_HIDDEN$4); + }); + } + _isAnimated() { + return this._element.classList.contains(CLASS_NAME_FADE$3); + } + _triggerBackdropTransition() { + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED$1); + if (hideEvent.defaultPrevented) { + return; + } + const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight; + const initialOverflowY = this._element.style.overflowY; + // return if the following background transition hasn't yet completed + if (initialOverflowY === 'hidden' || this._element.classList.contains(CLASS_NAME_STATIC)) { + return; + } + if (!isModalOverflowing) { + this._element.style.overflowY = 'hidden'; + } + this._element.classList.add(CLASS_NAME_STATIC); + this._queueCallback(() => { + this._element.classList.remove(CLASS_NAME_STATIC); + this._queueCallback(() => { + this._element.style.overflowY = initialOverflowY; + }, this._dialog); + }, this._dialog); + this._element.focus(); + } + + /** + * The following methods are used to handle overflowing modals + */ + + _adjustDialog() { + const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight; + const scrollbarWidth = this._scrollBar.getWidth(); + const isBodyOverflowing = scrollbarWidth > 0; + if (isBodyOverflowing && !isModalOverflowing) { + const property = isRTL() ? 'paddingLeft' : 'paddingRight'; + this._element.style[property] = `${scrollbarWidth}px`; + } + if (!isBodyOverflowing && isModalOverflowing) { + const property = isRTL() ? 'paddingRight' : 'paddingLeft'; + this._element.style[property] = `${scrollbarWidth}px`; + } + } + _resetAdjustments() { + this._element.style.paddingLeft = ''; + this._element.style.paddingRight = ''; + } + + // Static + static jQueryInterface(config, relatedTarget) { + return this.each(function () { + const data = Modal.getOrCreateInstance(this, config); + if (typeof config !== 'string') { + return; + } + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`); + } + data[config](relatedTarget); + }); + } + } + + /** + * Data API implementation + */ + + EventHandler.on(document, EVENT_CLICK_DATA_API$2, SELECTOR_DATA_TOGGLE$2, function (event) { + const target = SelectorEngine.getElementFromSelector(this); + if (['A', 'AREA'].includes(this.tagName)) { + event.preventDefault(); + } + EventHandler.one(target, EVENT_SHOW$4, showEvent => { + if (showEvent.defaultPrevented) { + // only register focus restorer if modal will actually get shown + return; + } + EventHandler.one(target, EVENT_HIDDEN$4, () => { + if (isVisible(this)) { + this.focus(); + } + }); + }); + + // avoid conflict when clicking modal toggler while another one is open + const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR$1); + if (alreadyOpen) { + Modal.getInstance(alreadyOpen).hide(); + } + const data = Modal.getOrCreateInstance(target); + data.toggle(this); + }); + enableDismissTrigger(Modal); + + /** + * jQuery + */ + + defineJQueryPlugin(Modal); + + /** + * -------------------------------------------------------------------------- + * Bootstrap offcanvas.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$6 = 'offcanvas'; + const DATA_KEY$3 = 'bs.offcanvas'; + const EVENT_KEY$3 = `.${DATA_KEY$3}`; + const DATA_API_KEY$1 = '.data-api'; + const EVENT_LOAD_DATA_API$2 = `load${EVENT_KEY$3}${DATA_API_KEY$1}`; + const ESCAPE_KEY = 'Escape'; + const CLASS_NAME_SHOW$3 = 'show'; + const CLASS_NAME_SHOWING$1 = 'showing'; + const CLASS_NAME_HIDING = 'hiding'; + const CLASS_NAME_BACKDROP = 'offcanvas-backdrop'; + const OPEN_SELECTOR = '.offcanvas.show'; + const EVENT_SHOW$3 = `show${EVENT_KEY$3}`; + const EVENT_SHOWN$3 = `shown${EVENT_KEY$3}`; + const EVENT_HIDE$3 = `hide${EVENT_KEY$3}`; + const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY$3}`; + const EVENT_HIDDEN$3 = `hidden${EVENT_KEY$3}`; + const EVENT_RESIZE = `resize${EVENT_KEY$3}`; + const EVENT_CLICK_DATA_API$1 = `click${EVENT_KEY$3}${DATA_API_KEY$1}`; + const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY$3}`; + const SELECTOR_DATA_TOGGLE$1 = '[data-bs-toggle="offcanvas"]'; + const Default$5 = { + backdrop: true, + keyboard: true, + scroll: false + }; + const DefaultType$5 = { + backdrop: '(boolean|string)', + keyboard: 'boolean', + scroll: 'boolean' + }; + + /** + * Class definition + */ + + class Offcanvas extends BaseComponent { + constructor(element, config) { + super(element, config); + this._isShown = false; + this._backdrop = this._initializeBackDrop(); + this._focustrap = this._initializeFocusTrap(); + this._addEventListeners(); + } + + // Getters + static get Default() { + return Default$5; + } + static get DefaultType() { + return DefaultType$5; + } + static get NAME() { + return NAME$6; + } + + // Public + toggle(relatedTarget) { + return this._isShown ? this.hide() : this.show(relatedTarget); + } + show(relatedTarget) { + if (this._isShown) { + return; + } + const showEvent = EventHandler.trigger(this._element, EVENT_SHOW$3, { + relatedTarget + }); + if (showEvent.defaultPrevented) { + return; + } + this._isShown = true; + this._backdrop.show(); + if (!this._config.scroll) { + new ScrollBarHelper().hide(); + } + this._element.setAttribute('aria-modal', true); + this._element.setAttribute('role', 'dialog'); + this._element.classList.add(CLASS_NAME_SHOWING$1); + const completeCallBack = () => { + if (!this._config.scroll || this._config.backdrop) { + this._focustrap.activate(); + } + this._element.classList.add(CLASS_NAME_SHOW$3); + this._element.classList.remove(CLASS_NAME_SHOWING$1); + EventHandler.trigger(this._element, EVENT_SHOWN$3, { + relatedTarget + }); + }; + this._queueCallback(completeCallBack, this._element, true); + } + hide() { + if (!this._isShown) { + return; + } + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE$3); + if (hideEvent.defaultPrevented) { + return; + } + this._focustrap.deactivate(); + this._element.blur(); + this._isShown = false; + this._element.classList.add(CLASS_NAME_HIDING); + this._backdrop.hide(); + const completeCallback = () => { + this._element.classList.remove(CLASS_NAME_SHOW$3, CLASS_NAME_HIDING); + this._element.removeAttribute('aria-modal'); + this._element.removeAttribute('role'); + if (!this._config.scroll) { + new ScrollBarHelper().reset(); + } + EventHandler.trigger(this._element, EVENT_HIDDEN$3); + }; + this._queueCallback(completeCallback, this._element, true); + } + dispose() { + this._backdrop.dispose(); + this._focustrap.deactivate(); + super.dispose(); + } + + // Private + _initializeBackDrop() { + const clickCallback = () => { + if (this._config.backdrop === 'static') { + EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED); + return; + } + this.hide(); + }; + + // 'static' option will be translated to true, and booleans will keep their value + const isVisible = Boolean(this._config.backdrop); + return new Backdrop({ + className: CLASS_NAME_BACKDROP, + isVisible, + isAnimated: true, + rootElement: this._element.parentNode, + clickCallback: isVisible ? clickCallback : null + }); + } + _initializeFocusTrap() { + return new FocusTrap({ + trapElement: this._element + }); + } + _addEventListeners() { + EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => { + if (event.key !== ESCAPE_KEY) { + return; + } + if (this._config.keyboard) { + this.hide(); + return; + } + EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED); + }); + } + + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Offcanvas.getOrCreateInstance(this, config); + if (typeof config !== 'string') { + return; + } + if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { + throw new TypeError(`No method named "${config}"`); + } + data[config](this); + }); + } + } + + /** + * Data API implementation + */ + + EventHandler.on(document, EVENT_CLICK_DATA_API$1, SELECTOR_DATA_TOGGLE$1, function (event) { + const target = SelectorEngine.getElementFromSelector(this); + if (['A', 'AREA'].includes(this.tagName)) { + event.preventDefault(); + } + if (isDisabled(this)) { + return; + } + EventHandler.one(target, EVENT_HIDDEN$3, () => { + // focus on trigger when it is closed + if (isVisible(this)) { + this.focus(); + } + }); + + // avoid conflict when clicking a toggler of an offcanvas, while another is open + const alreadyOpen = SelectorEngine.findOne(OPEN_SELECTOR); + if (alreadyOpen && alreadyOpen !== target) { + Offcanvas.getInstance(alreadyOpen).hide(); + } + const data = Offcanvas.getOrCreateInstance(target); + data.toggle(this); + }); + EventHandler.on(window, EVENT_LOAD_DATA_API$2, () => { + for (const selector of SelectorEngine.find(OPEN_SELECTOR)) { + Offcanvas.getOrCreateInstance(selector).show(); + } + }); + EventHandler.on(window, EVENT_RESIZE, () => { + for (const element of SelectorEngine.find('[aria-modal][class*=show][class*=offcanvas-]')) { + if (getComputedStyle(element).position !== 'fixed') { + Offcanvas.getOrCreateInstance(element).hide(); + } + } + }); + enableDismissTrigger(Offcanvas); + + /** + * jQuery + */ + + defineJQueryPlugin(Offcanvas); + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/sanitizer.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + // js-docs-start allow-list + const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i; + const DefaultAllowlist = { + // Global attributes allowed on any supplied element below. + '*': ['class', 'dir', 'id', 'lang', 'role', ARIA_ATTRIBUTE_PATTERN], + a: ['target', 'href', 'title', 'rel'], + area: [], + b: [], + br: [], + col: [], + code: [], + 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: [] + }; + // js-docs-end allow-list + + const uriAttributes = new Set(['background', 'cite', 'href', 'itemtype', 'longdesc', 'poster', 'src', 'xlink:href']); + + /** + * A pattern that recognizes URLs that are safe wrt. XSS in URL navigation + * contexts. + * + * Shout-out to Angular https://github.com/angular/angular/blob/15.2.8/packages/core/src/sanitization/url_sanitizer.ts#L38 + */ + // eslint-disable-next-line unicorn/better-regex + const SAFE_URL_PATTERN = /^(?!javascript:)(?:[a-z0-9+.-]+:|[^&:/?#]*(?:[/?#]|$))/i; + const allowedAttribute = (attribute, allowedAttributeList) => { + const attributeName = attribute.nodeName.toLowerCase(); + if (allowedAttributeList.includes(attributeName)) { + if (uriAttributes.has(attributeName)) { + return Boolean(SAFE_URL_PATTERN.test(attribute.nodeValue)); + } + return true; + } + + // Check if a regular expression validates the attribute. + return allowedAttributeList.filter(attributeRegex => attributeRegex instanceof RegExp).some(regex => regex.test(attributeName)); + }; + function sanitizeHtml(unsafeHtml, allowList, sanitizeFunction) { + if (!unsafeHtml.length) { + return unsafeHtml; + } + if (sanitizeFunction && typeof sanitizeFunction === 'function') { + return sanitizeFunction(unsafeHtml); + } + const domParser = new window.DOMParser(); + const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html'); + const elements = [].concat(...createdDocument.body.querySelectorAll('*')); + for (const element of elements) { + const elementName = element.nodeName.toLowerCase(); + if (!Object.keys(allowList).includes(elementName)) { + element.remove(); + continue; + } + const attributeList = [].concat(...element.attributes); + const allowedAttributes = [].concat(allowList['*'] || [], allowList[elementName] || []); + for (const attribute of attributeList) { + if (!allowedAttribute(attribute, allowedAttributes)) { + element.removeAttribute(attribute.nodeName); + } + } + } + return createdDocument.body.innerHTML; + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap util/template-factory.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$5 = 'TemplateFactory'; + const Default$4 = { + allowList: DefaultAllowlist, + content: {}, + // { selector : text , selector2 : text2 , } + extraClass: '', + html: false, + sanitize: true, + sanitizeFn: null, + template: '<div></div>' + }; + const DefaultType$4 = { + allowList: 'object', + content: 'object', + extraClass: '(string|function)', + html: 'boolean', + sanitize: 'boolean', + sanitizeFn: '(null|function)', + template: 'string' + }; + const DefaultContentType = { + entry: '(string|element|function|null)', + selector: '(string|element)' + }; + + /** + * Class definition + */ + + class TemplateFactory extends Config { + constructor(config) { + super(); + this._config = this._getConfig(config); + } + + // Getters + static get Default() { + return Default$4; + } + static get DefaultType() { + return DefaultType$4; + } + static get NAME() { + return NAME$5; + } + + // Public + getContent() { + return Object.values(this._config.content).map(config => this._resolvePossibleFunction(config)).filter(Boolean); + } + hasContent() { + return this.getContent().length > 0; + } + changeContent(content) { + this._checkContent(content); + this._config.content = { + ...this._config.content, + ...content + }; + return this; + } + toHtml() { + const templateWrapper = document.createElement('div'); + templateWrapper.innerHTML = this._maybeSanitize(this._config.template); + for (const [selector, text] of Object.entries(this._config.content)) { + this._setContent(templateWrapper, text, selector); + } + const template = templateWrapper.children[0]; + const extraClass = this._resolvePossibleFunction(this._config.extraClass); + if (extraClass) { + template.classList.add(...extraClass.split(' ')); + } + return template; + } + + // Private + _typeCheckConfig(config) { + super._typeCheckConfig(config); + this._checkContent(config.content); + } + _checkContent(arg) { + for (const [selector, content] of Object.entries(arg)) { + super._typeCheckConfig({ + selector, + entry: content + }, DefaultContentType); + } + } + _setContent(template, content, selector) { + const templateElement = SelectorEngine.findOne(selector, template); + if (!templateElement) { + return; + } + content = this._resolvePossibleFunction(content); + if (!content) { + templateElement.remove(); + return; + } + if (isElement$1(content)) { + this._putElementInTemplate(getElement(content), templateElement); + return; + } + if (this._config.html) { + templateElement.innerHTML = this._maybeSanitize(content); + return; + } + templateElement.textContent = content; + } + _maybeSanitize(arg) { + return this._config.sanitize ? sanitizeHtml(arg, this._config.allowList, this._config.sanitizeFn) : arg; + } + _resolvePossibleFunction(arg) { + return execute(arg, [this]); + } + _putElementInTemplate(element, templateElement) { + if (this._config.html) { + templateElement.innerHTML = ''; + templateElement.append(element); + return; + } + templateElement.textContent = element.textContent; + } + } + + /** + * -------------------------------------------------------------------------- + * Bootstrap tooltip.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$4 = 'tooltip'; + const DISALLOWED_ATTRIBUTES = new Set(['sanitize', 'allowList', 'sanitizeFn']); + const CLASS_NAME_FADE$2 = 'fade'; + const CLASS_NAME_MODAL = 'modal'; + const CLASS_NAME_SHOW$2 = 'show'; + const SELECTOR_TOOLTIP_INNER = '.tooltip-inner'; + const SELECTOR_MODAL = `.${CLASS_NAME_MODAL}`; + const EVENT_MODAL_HIDE = 'hide.bs.modal'; + const TRIGGER_HOVER = 'hover'; + const TRIGGER_FOCUS = 'focus'; + const TRIGGER_CLICK = 'click'; + const TRIGGER_MANUAL = 'manual'; + const EVENT_HIDE$2 = 'hide'; + const EVENT_HIDDEN$2 = 'hidden'; + const EVENT_SHOW$2 = 'show'; + const EVENT_SHOWN$2 = 'shown'; + const EVENT_INSERTED = 'inserted'; + const EVENT_CLICK$1 = 'click'; + const EVENT_FOCUSIN$1 = 'focusin'; + const EVENT_FOCUSOUT$1 = 'focusout'; + const EVENT_MOUSEENTER = 'mouseenter'; + const EVENT_MOUSELEAVE = 'mouseleave'; + const AttachmentMap = { + AUTO: 'auto', + TOP: 'top', + RIGHT: isRTL() ? 'left' : 'right', + BOTTOM: 'bottom', + LEFT: isRTL() ? 'right' : 'left' + }; + const Default$3 = { + allowList: DefaultAllowlist, + animation: true, + boundary: 'clippingParents', + container: false, + customClass: '', + delay: 0, + fallbackPlacements: ['top', 'right', 'bottom', 'left'], + html: false, + offset: [0, 6], + placement: 'top', + popperConfig: null, + sanitize: true, + sanitizeFn: null, + selector: false, + template: '<div class="tooltip" role="tooltip">' + '<div class="tooltip-arrow"></div>' + '<div class="tooltip-inner"></div>' + '</div>', + title: '', + trigger: 'hover focus' + }; + const DefaultType$3 = { + allowList: 'object', + animation: 'boolean', + boundary: '(string|element)', + container: '(string|element|boolean)', + customClass: '(string|function)', + delay: '(number|object)', + fallbackPlacements: 'array', + html: 'boolean', + offset: '(array|string|function)', + placement: '(string|function)', + popperConfig: '(null|object|function)', + sanitize: 'boolean', + sanitizeFn: '(null|function)', + selector: '(string|boolean)', + template: 'string', + title: '(string|element|function)', + trigger: 'string' + }; + + /** + * Class definition + */ + + 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, config); + + // Private + this._isEnabled = true; + this._timeout = 0; + this._isHovered = null; + this._activeTrigger = {}; + this._popper = null; + this._templateFactory = null; + this._newContent = null; + + // Protected + this.tip = null; + this._setListeners(); + if (!this._config.selector) { + this._fixTitle(); + } + } + + // Getters + static get Default() { + return Default$3; + } + static get DefaultType() { + return DefaultType$3; + } + static get NAME() { + return NAME$4; + } + + // Public + enable() { + this._isEnabled = true; + } + disable() { + this._isEnabled = false; + } + toggleEnabled() { + this._isEnabled = !this._isEnabled; + } + toggle() { + if (!this._isEnabled) { + return; + } + this._activeTrigger.click = !this._activeTrigger.click; + if (this._isShown()) { + this._leave(); + return; + } + this._enter(); + } + dispose() { + clearTimeout(this._timeout); + EventHandler.off(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler); + if (this._element.getAttribute('data-bs-original-title')) { + this._element.setAttribute('title', this._element.getAttribute('data-bs-original-title')); + } + this._disposePopper(); + super.dispose(); + } + show() { + if (this._element.style.display === 'none') { + throw new Error('Please use show on visible elements'); + } + if (!(this._isWithContent() && this._isEnabled)) { + return; + } + const showEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOW$2)); + const shadowRoot = findShadowRoot(this._element); + const isInTheDom = (shadowRoot || this._element.ownerDocument.documentElement).contains(this._element); + if (showEvent.defaultPrevented || !isInTheDom) { + return; + } + + // TODO: v6 remove this or make it optional + this._disposePopper(); + const tip = this._getTipElement(); + this._element.setAttribute('aria-describedby', tip.getAttribute('id')); + const { + container + } = this._config; + if (!this._element.ownerDocument.documentElement.contains(this.tip)) { + container.append(tip); + EventHandler.trigger(this._element, this.constructor.eventName(EVENT_INSERTED)); + } + this._popper = this._createPopper(tip); + tip.classList.add(CLASS_NAME_SHOW$2); + + // 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) { + for (const element of [].concat(...document.body.children)) { + EventHandler.on(element, 'mouseover', noop); + } + } + const complete = () => { + EventHandler.trigger(this._element, this.constructor.eventName(EVENT_SHOWN$2)); + if (this._isHovered === false) { + this._leave(); + } + this._isHovered = false; + }; + this._queueCallback(complete, this.tip, this._isAnimated()); + } + hide() { + if (!this._isShown()) { + return; + } + const hideEvent = EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDE$2)); + if (hideEvent.defaultPrevented) { + return; + } + const tip = this._getTipElement(); + tip.classList.remove(CLASS_NAME_SHOW$2); + + // If this is a touch-enabled device we remove the extra + // empty mouseover listeners we added for iOS support + if ('ontouchstart' in document.documentElement) { + for (const element of [].concat(...document.body.children)) { + EventHandler.off(element, 'mouseover', noop); + } + } + this._activeTrigger[TRIGGER_CLICK] = false; + this._activeTrigger[TRIGGER_FOCUS] = false; + this._activeTrigger[TRIGGER_HOVER] = false; + this._isHovered = null; // it is a trick to support manual triggering + + const complete = () => { + if (this._isWithActiveTrigger()) { + return; + } + if (!this._isHovered) { + this._disposePopper(); + } + this._element.removeAttribute('aria-describedby'); + EventHandler.trigger(this._element, this.constructor.eventName(EVENT_HIDDEN$2)); + }; + this._queueCallback(complete, this.tip, this._isAnimated()); + } + update() { + if (this._popper) { + this._popper.update(); + } + } + + // Protected + _isWithContent() { + return Boolean(this._getTitle()); + } + _getTipElement() { + if (!this.tip) { + this.tip = this._createTipElement(this._newContent || this._getContentForTemplate()); + } + return this.tip; + } + _createTipElement(content) { + const tip = this._getTemplateFactory(content).toHtml(); + + // TODO: remove this check in v6 + if (!tip) { + return null; + } + tip.classList.remove(CLASS_NAME_FADE$2, CLASS_NAME_SHOW$2); + // TODO: v6 the following can be achieved with CSS only + tip.classList.add(`bs-${this.constructor.NAME}-auto`); + const tipId = getUID(this.constructor.NAME).toString(); + tip.setAttribute('id', tipId); + if (this._isAnimated()) { + tip.classList.add(CLASS_NAME_FADE$2); + } + return tip; + } + setContent(content) { + this._newContent = content; + if (this._isShown()) { + this._disposePopper(); + this.show(); + } + } + _getTemplateFactory(content) { + if (this._templateFactory) { + this._templateFactory.changeContent(content); + } else { + this._templateFactory = new TemplateFactory({ + ...this._config, + // the `content` var has to be after `this._config` + // to override config.content in case of popover + content, + extraClass: this._resolvePossibleFunction(this._config.customClass) + }); + } + return this._templateFactory; + } + _getContentForTemplate() { + return { + [SELECTOR_TOOLTIP_INNER]: this._getTitle() + }; + } + _getTitle() { + return this._resolvePossibleFunction(this._config.title) || this._element.getAttribute('data-bs-original-title'); + } + + // Private + _initializeOnDelegatedTarget(event) { + return this.constructor.getOrCreateInstance(event.delegateTarget, this._getDelegateConfig()); + } + _isAnimated() { + return this._config.animation || this.tip && this.tip.classList.contains(CLASS_NAME_FADE$2); + } + _isShown() { + return this.tip && this.tip.classList.contains(CLASS_NAME_SHOW$2); + } + _createPopper(tip) { + const placement = execute(this._config.placement, [this, tip, this._element]); + const attachment = AttachmentMap[placement.toUpperCase()]; + return createPopper(this._element, tip, this._getPopperConfig(attachment)); + } + _getOffset() { + const { + offset + } = this._config; + if (typeof offset === 'string') { + return offset.split(',').map(value => Number.parseInt(value, 10)); + } + if (typeof offset === 'function') { + return popperData => offset(popperData, this._element); + } + return offset; + } + _resolvePossibleFunction(arg) { + return execute(arg, [this._element]); + } + _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: 'preSetPlacement', + enabled: true, + phase: 'beforeMain', + fn: data => { + // Pre-set Popper's placement attribute in order to read the arrow sizes properly. + // Otherwise, Popper mixes up the width and height dimensions since the initial arrow style is for top placement + this._getTipElement().setAttribute('data-popper-placement', data.state.placement); + } + }] + }; + return { + ...defaultBsPopperConfig, + ...execute(this._config.popperConfig, [defaultBsPopperConfig]) + }; + } + _setListeners() { + const triggers = this._config.trigger.split(' '); + for (const trigger of triggers) { + if (trigger === 'click') { + EventHandler.on(this._element, this.constructor.eventName(EVENT_CLICK$1), this._config.selector, event => { + const context = this._initializeOnDelegatedTarget(event); + context.toggle(); + }); + } else if (trigger !== TRIGGER_MANUAL) { + const eventIn = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSEENTER) : this.constructor.eventName(EVENT_FOCUSIN$1); + const eventOut = trigger === TRIGGER_HOVER ? this.constructor.eventName(EVENT_MOUSELEAVE) : this.constructor.eventName(EVENT_FOCUSOUT$1); + EventHandler.on(this._element, eventIn, this._config.selector, event => { + const context = this._initializeOnDelegatedTarget(event); + context._activeTrigger[event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER] = true; + context._enter(); + }); + EventHandler.on(this._element, eventOut, this._config.selector, event => { + const context = this._initializeOnDelegatedTarget(event); + context._activeTrigger[event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER] = context._element.contains(event.relatedTarget); + context._leave(); + }); + } + } + this._hideModalHandler = () => { + if (this._element) { + this.hide(); + } + }; + EventHandler.on(this._element.closest(SELECTOR_MODAL), EVENT_MODAL_HIDE, this._hideModalHandler); + } + _fixTitle() { + const title = this._element.getAttribute('title'); + if (!title) { + return; + } + if (!this._element.getAttribute('aria-label') && !this._element.textContent.trim()) { + this._element.setAttribute('aria-label', title); + } + this._element.setAttribute('data-bs-original-title', title); // DO NOT USE IT. Is only for backwards compatibility + this._element.removeAttribute('title'); + } + _enter() { + if (this._isShown() || this._isHovered) { + this._isHovered = true; + return; + } + this._isHovered = true; + this._setTimeout(() => { + if (this._isHovered) { + this.show(); + } + }, this._config.delay.show); + } + _leave() { + if (this._isWithActiveTrigger()) { + return; + } + this._isHovered = false; + this._setTimeout(() => { + if (!this._isHovered) { + this.hide(); + } + }, this._config.delay.hide); + } + _setTimeout(handler, timeout) { + clearTimeout(this._timeout); + this._timeout = setTimeout(handler, timeout); + } + _isWithActiveTrigger() { + return Object.values(this._activeTrigger).includes(true); + } + _getConfig(config) { + const dataAttributes = Manipulator.getDataAttributes(this._element); + for (const dataAttribute of Object.keys(dataAttributes)) { + if (DISALLOWED_ATTRIBUTES.has(dataAttribute)) { + delete dataAttributes[dataAttribute]; + } + } + config = { + ...dataAttributes, + ...(typeof config === 'object' && config ? config : {}) + }; + config = this._mergeConfigObj(config); + config = this._configAfterMerge(config); + this._typeCheckConfig(config); + return config; + } + _configAfterMerge(config) { + config.container = config.container === false ? document.body : getElement(config.container); + if (typeof config.delay === 'number') { + config.delay = { + show: config.delay, + hide: config.delay + }; + } + if (typeof config.title === 'number') { + config.title = config.title.toString(); + } + if (typeof config.content === 'number') { + config.content = config.content.toString(); + } + return config; + } + _getDelegateConfig() { + const config = {}; + for (const [key, value] of Object.entries(this._config)) { + if (this.constructor.Default[key] !== value) { + config[key] = value; + } + } + config.selector = false; + config.trigger = 'manual'; + + // In the future can be replaced with: + // const keysWithDifferentValues = Object.entries(this._config).filter(entry => this.constructor.Default[entry[0]] !== this._config[entry[0]]) + // `Object.fromEntries(keysWithDifferentValues)` + return config; + } + _disposePopper() { + if (this._popper) { + this._popper.destroy(); + this._popper = null; + } + if (this.tip) { + this.tip.remove(); + this.tip = null; + } + } + + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Tooltip.getOrCreateInstance(this, config); + if (typeof config !== 'string') { + return; + } + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`); + } + data[config](); + }); + } + } + + /** + * jQuery + */ + + defineJQueryPlugin(Tooltip); + + /** + * -------------------------------------------------------------------------- + * Bootstrap popover.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$3 = 'popover'; + const SELECTOR_TITLE = '.popover-header'; + const SELECTOR_CONTENT = '.popover-body'; + const Default$2 = { + ...Tooltip.Default, + content: '', + offset: [0, 8], + placement: 'right', + template: '<div class="popover" role="tooltip">' + '<div class="popover-arrow"></div>' + '<h3 class="popover-header"></h3>' + '<div class="popover-body"></div>' + '</div>', + trigger: 'click' + }; + const DefaultType$2 = { + ...Tooltip.DefaultType, + content: '(null|string|element|function)' + }; + + /** + * Class definition + */ + + class Popover extends Tooltip { + // Getters + static get Default() { + return Default$2; + } + static get DefaultType() { + return DefaultType$2; + } + static get NAME() { + return NAME$3; + } + + // Overrides + _isWithContent() { + return this._getTitle() || this._getContent(); + } + + // Private + _getContentForTemplate() { + return { + [SELECTOR_TITLE]: this._getTitle(), + [SELECTOR_CONTENT]: this._getContent() + }; + } + _getContent() { + return this._resolvePossibleFunction(this._config.content); + } + + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Popover.getOrCreateInstance(this, config); + if (typeof config !== 'string') { + return; + } + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`); + } + data[config](); + }); + } + } + + /** + * jQuery + */ + + defineJQueryPlugin(Popover); + + /** + * -------------------------------------------------------------------------- + * Bootstrap scrollspy.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$2 = 'scrollspy'; + const DATA_KEY$2 = 'bs.scrollspy'; + const EVENT_KEY$2 = `.${DATA_KEY$2}`; + const DATA_API_KEY = '.data-api'; + const EVENT_ACTIVATE = `activate${EVENT_KEY$2}`; + const EVENT_CLICK = `click${EVENT_KEY$2}`; + const EVENT_LOAD_DATA_API$1 = `load${EVENT_KEY$2}${DATA_API_KEY}`; + const CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item'; + const CLASS_NAME_ACTIVE$1 = 'active'; + const SELECTOR_DATA_SPY = '[data-bs-spy="scroll"]'; + const SELECTOR_TARGET_LINKS = '[href]'; + const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'; + const SELECTOR_NAV_LINKS = '.nav-link'; + const SELECTOR_NAV_ITEMS = '.nav-item'; + const SELECTOR_LIST_ITEMS = '.list-group-item'; + const SELECTOR_LINK_ITEMS = `${SELECTOR_NAV_LINKS}, ${SELECTOR_NAV_ITEMS} > ${SELECTOR_NAV_LINKS}, ${SELECTOR_LIST_ITEMS}`; + const SELECTOR_DROPDOWN = '.dropdown'; + const SELECTOR_DROPDOWN_TOGGLE$1 = '.dropdown-toggle'; + const Default$1 = { + offset: null, + // TODO: v6 @deprecated, keep it for backwards compatibility reasons + rootMargin: '0px 0px -25%', + smoothScroll: false, + target: null, + threshold: [0.1, 0.5, 1] + }; + const DefaultType$1 = { + offset: '(number|null)', + // TODO v6 @deprecated, keep it for backwards compatibility reasons + rootMargin: 'string', + smoothScroll: 'boolean', + target: 'element', + threshold: 'array' + }; + + /** + * Class definition + */ + + class ScrollSpy extends BaseComponent { + constructor(element, config) { + super(element, config); + + // this._element is the observablesContainer and config.target the menu links wrapper + this._targetLinks = new Map(); + this._observableSections = new Map(); + this._rootElement = getComputedStyle(this._element).overflowY === 'visible' ? null : this._element; + this._activeTarget = null; + this._observer = null; + this._previousScrollData = { + visibleEntryTop: 0, + parentScrollTop: 0 + }; + this.refresh(); // initialize + } + + // Getters + static get Default() { + return Default$1; + } + static get DefaultType() { + return DefaultType$1; + } + static get NAME() { + return NAME$2; + } + + // Public + refresh() { + this._initializeTargetsAndObservables(); + this._maybeEnableSmoothScroll(); + if (this._observer) { + this._observer.disconnect(); + } else { + this._observer = this._getNewObserver(); + } + for (const section of this._observableSections.values()) { + this._observer.observe(section); + } + } + dispose() { + this._observer.disconnect(); + super.dispose(); + } + + // Private + _configAfterMerge(config) { + // TODO: on v6 target should be given explicitly & remove the {target: 'ss-target'} case + config.target = getElement(config.target) || document.body; + + // TODO: v6 Only for backwards compatibility reasons. Use rootMargin only + config.rootMargin = config.offset ? `${config.offset}px 0px -30%` : config.rootMargin; + if (typeof config.threshold === 'string') { + config.threshold = config.threshold.split(',').map(value => Number.parseFloat(value)); + } + return config; + } + _maybeEnableSmoothScroll() { + if (!this._config.smoothScroll) { + return; + } + + // unregister any previous listeners + EventHandler.off(this._config.target, EVENT_CLICK); + EventHandler.on(this._config.target, EVENT_CLICK, SELECTOR_TARGET_LINKS, event => { + const observableSection = this._observableSections.get(event.target.hash); + if (observableSection) { + event.preventDefault(); + const root = this._rootElement || window; + const height = observableSection.offsetTop - this._element.offsetTop; + if (root.scrollTo) { + root.scrollTo({ + top: height, + behavior: 'smooth' + }); + return; + } + + // Chrome 60 doesn't support `scrollTo` + root.scrollTop = height; + } + }); + } + _getNewObserver() { + const options = { + root: this._rootElement, + threshold: this._config.threshold, + rootMargin: this._config.rootMargin + }; + return new IntersectionObserver(entries => this._observerCallback(entries), options); + } + + // The logic of selection + _observerCallback(entries) { + const targetElement = entry => this._targetLinks.get(`#${entry.target.id}`); + const activate = entry => { + this._previousScrollData.visibleEntryTop = entry.target.offsetTop; + this._process(targetElement(entry)); + }; + const parentScrollTop = (this._rootElement || document.documentElement).scrollTop; + const userScrollsDown = parentScrollTop >= this._previousScrollData.parentScrollTop; + this._previousScrollData.parentScrollTop = parentScrollTop; + for (const entry of entries) { + if (!entry.isIntersecting) { + this._activeTarget = null; + this._clearActiveClass(targetElement(entry)); + continue; + } + const entryIsLowerThanPrevious = entry.target.offsetTop >= this._previousScrollData.visibleEntryTop; + // if we are scrolling down, pick the bigger offsetTop + if (userScrollsDown && entryIsLowerThanPrevious) { + activate(entry); + // if parent isn't scrolled, let's keep the first visible item, breaking the iteration + if (!parentScrollTop) { + return; + } + continue; + } + + // if we are scrolling up, pick the smallest offsetTop + if (!userScrollsDown && !entryIsLowerThanPrevious) { + activate(entry); + } + } + } + _initializeTargetsAndObservables() { + this._targetLinks = new Map(); + this._observableSections = new Map(); + const targetLinks = SelectorEngine.find(SELECTOR_TARGET_LINKS, this._config.target); + for (const anchor of targetLinks) { + // ensure that the anchor has an id and is not disabled + if (!anchor.hash || isDisabled(anchor)) { + continue; + } + const observableSection = SelectorEngine.findOne(decodeURI(anchor.hash), this._element); + + // ensure that the observableSection exists & is visible + if (isVisible(observableSection)) { + this._targetLinks.set(decodeURI(anchor.hash), anchor); + this._observableSections.set(anchor.hash, observableSection); + } + } + } + _process(target) { + if (this._activeTarget === target) { + return; + } + this._clearActiveClass(this._config.target); + this._activeTarget = target; + target.classList.add(CLASS_NAME_ACTIVE$1); + this._activateParents(target); + EventHandler.trigger(this._element, EVENT_ACTIVATE, { + relatedTarget: target + }); + } + _activateParents(target) { + // Activate dropdown parents + if (target.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) { + SelectorEngine.findOne(SELECTOR_DROPDOWN_TOGGLE$1, target.closest(SELECTOR_DROPDOWN)).classList.add(CLASS_NAME_ACTIVE$1); + return; + } + for (const listGroup of SelectorEngine.parents(target, SELECTOR_NAV_LIST_GROUP)) { + // Set triggered links parents as active + // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor + for (const item of SelectorEngine.prev(listGroup, SELECTOR_LINK_ITEMS)) { + item.classList.add(CLASS_NAME_ACTIVE$1); + } + } + } + _clearActiveClass(parent) { + parent.classList.remove(CLASS_NAME_ACTIVE$1); + const activeNodes = SelectorEngine.find(`${SELECTOR_TARGET_LINKS}.${CLASS_NAME_ACTIVE$1}`, parent); + for (const node of activeNodes) { + node.classList.remove(CLASS_NAME_ACTIVE$1); + } + } + + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = ScrollSpy.getOrCreateInstance(this, config); + if (typeof config !== 'string') { + return; + } + if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { + throw new TypeError(`No method named "${config}"`); + } + data[config](); + }); + } + } + + /** + * Data API implementation + */ + + EventHandler.on(window, EVENT_LOAD_DATA_API$1, () => { + for (const spy of SelectorEngine.find(SELECTOR_DATA_SPY)) { + ScrollSpy.getOrCreateInstance(spy); + } + }); + + /** + * jQuery + */ + + defineJQueryPlugin(ScrollSpy); + + /** + * -------------------------------------------------------------------------- + * Bootstrap tab.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME$1 = 'tab'; + const DATA_KEY$1 = 'bs.tab'; + const EVENT_KEY$1 = `.${DATA_KEY$1}`; + const EVENT_HIDE$1 = `hide${EVENT_KEY$1}`; + const EVENT_HIDDEN$1 = `hidden${EVENT_KEY$1}`; + const EVENT_SHOW$1 = `show${EVENT_KEY$1}`; + const EVENT_SHOWN$1 = `shown${EVENT_KEY$1}`; + const EVENT_CLICK_DATA_API = `click${EVENT_KEY$1}`; + const EVENT_KEYDOWN = `keydown${EVENT_KEY$1}`; + const EVENT_LOAD_DATA_API = `load${EVENT_KEY$1}`; + const ARROW_LEFT_KEY = 'ArrowLeft'; + const ARROW_RIGHT_KEY = 'ArrowRight'; + const ARROW_UP_KEY = 'ArrowUp'; + const ARROW_DOWN_KEY = 'ArrowDown'; + const HOME_KEY = 'Home'; + const END_KEY = 'End'; + const CLASS_NAME_ACTIVE = 'active'; + const CLASS_NAME_FADE$1 = 'fade'; + const CLASS_NAME_SHOW$1 = 'show'; + const CLASS_DROPDOWN = 'dropdown'; + const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'; + const SELECTOR_DROPDOWN_MENU = '.dropdown-menu'; + const NOT_SELECTOR_DROPDOWN_TOGGLE = ':not(.dropdown-toggle)'; + const SELECTOR_TAB_PANEL = '.list-group, .nav, [role="tablist"]'; + const SELECTOR_OUTER = '.nav-item, .list-group-item'; + const SELECTOR_INNER = `.nav-link${NOT_SELECTOR_DROPDOWN_TOGGLE}, .list-group-item${NOT_SELECTOR_DROPDOWN_TOGGLE}, [role="tab"]${NOT_SELECTOR_DROPDOWN_TOGGLE}`; + const SELECTOR_DATA_TOGGLE = '[data-bs-toggle="tab"], [data-bs-toggle="pill"], [data-bs-toggle="list"]'; // TODO: could only be `tab` in v6 + const SELECTOR_INNER_ELEM = `${SELECTOR_INNER}, ${SELECTOR_DATA_TOGGLE}`; + const SELECTOR_DATA_TOGGLE_ACTIVE = `.${CLASS_NAME_ACTIVE}[data-bs-toggle="tab"], .${CLASS_NAME_ACTIVE}[data-bs-toggle="pill"], .${CLASS_NAME_ACTIVE}[data-bs-toggle="list"]`; + + /** + * Class definition + */ + + class Tab extends BaseComponent { + constructor(element) { + super(element); + this._parent = this._element.closest(SELECTOR_TAB_PANEL); + if (!this._parent) { + return; + // TODO: should throw exception in v6 + // throw new TypeError(`${element.outerHTML} has not a valid parent ${SELECTOR_INNER_ELEM}`) + } + + // Set up initial aria attributes + this._setInitialAttributes(this._parent, this._getChildren()); + EventHandler.on(this._element, EVENT_KEYDOWN, event => this._keydown(event)); + } + + // Getters + static get NAME() { + return NAME$1; + } + + // Public + show() { + // Shows this elem and deactivate the active sibling if exists + const innerElem = this._element; + if (this._elemIsActive(innerElem)) { + return; + } + + // Search for active tab on same parent to deactivate it + const active = this._getActiveElem(); + const hideEvent = active ? EventHandler.trigger(active, EVENT_HIDE$1, { + relatedTarget: innerElem + }) : null; + const showEvent = EventHandler.trigger(innerElem, EVENT_SHOW$1, { + relatedTarget: active + }); + if (showEvent.defaultPrevented || hideEvent && hideEvent.defaultPrevented) { + return; + } + this._deactivate(active, innerElem); + this._activate(innerElem, active); + } + + // Private + _activate(element, relatedElem) { + if (!element) { + return; + } + element.classList.add(CLASS_NAME_ACTIVE); + this._activate(SelectorEngine.getElementFromSelector(element)); // Search and activate/show the proper section + + const complete = () => { + if (element.getAttribute('role') !== 'tab') { + element.classList.add(CLASS_NAME_SHOW$1); + return; + } + element.removeAttribute('tabindex'); + element.setAttribute('aria-selected', true); + this._toggleDropDown(element, true); + EventHandler.trigger(element, EVENT_SHOWN$1, { + relatedTarget: relatedElem + }); + }; + this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE$1)); + } + _deactivate(element, relatedElem) { + if (!element) { + return; + } + element.classList.remove(CLASS_NAME_ACTIVE); + element.blur(); + this._deactivate(SelectorEngine.getElementFromSelector(element)); // Search and deactivate the shown section too + + const complete = () => { + if (element.getAttribute('role') !== 'tab') { + element.classList.remove(CLASS_NAME_SHOW$1); + return; + } + element.setAttribute('aria-selected', false); + element.setAttribute('tabindex', '-1'); + this._toggleDropDown(element, false); + EventHandler.trigger(element, EVENT_HIDDEN$1, { + relatedTarget: relatedElem + }); + }; + this._queueCallback(complete, element, element.classList.contains(CLASS_NAME_FADE$1)); + } + _keydown(event) { + if (![ARROW_LEFT_KEY, ARROW_RIGHT_KEY, ARROW_UP_KEY, ARROW_DOWN_KEY, HOME_KEY, END_KEY].includes(event.key)) { + return; + } + event.stopPropagation(); // stopPropagation/preventDefault both added to support up/down keys without scrolling the page + event.preventDefault(); + const children = this._getChildren().filter(element => !isDisabled(element)); + let nextActiveElement; + if ([HOME_KEY, END_KEY].includes(event.key)) { + nextActiveElement = children[event.key === HOME_KEY ? 0 : children.length - 1]; + } else { + const isNext = [ARROW_RIGHT_KEY, ARROW_DOWN_KEY].includes(event.key); + nextActiveElement = getNextActiveElement(children, event.target, isNext, true); + } + if (nextActiveElement) { + nextActiveElement.focus({ + preventScroll: true + }); + Tab.getOrCreateInstance(nextActiveElement).show(); + } + } + _getChildren() { + // collection of inner elements + return SelectorEngine.find(SELECTOR_INNER_ELEM, this._parent); + } + _getActiveElem() { + return this._getChildren().find(child => this._elemIsActive(child)) || null; + } + _setInitialAttributes(parent, children) { + this._setAttributeIfNotExists(parent, 'role', 'tablist'); + for (const child of children) { + this._setInitialAttributesOnChild(child); + } + } + _setInitialAttributesOnChild(child) { + child = this._getInnerElement(child); + const isActive = this._elemIsActive(child); + const outerElem = this._getOuterElement(child); + child.setAttribute('aria-selected', isActive); + if (outerElem !== child) { + this._setAttributeIfNotExists(outerElem, 'role', 'presentation'); + } + if (!isActive) { + child.setAttribute('tabindex', '-1'); + } + this._setAttributeIfNotExists(child, 'role', 'tab'); + + // set attributes to the related panel too + this._setInitialAttributesOnTargetPanel(child); + } + _setInitialAttributesOnTargetPanel(child) { + const target = SelectorEngine.getElementFromSelector(child); + if (!target) { + return; + } + this._setAttributeIfNotExists(target, 'role', 'tabpanel'); + if (child.id) { + this._setAttributeIfNotExists(target, 'aria-labelledby', `${child.id}`); + } + } + _toggleDropDown(element, open) { + const outerElem = this._getOuterElement(element); + if (!outerElem.classList.contains(CLASS_DROPDOWN)) { + return; + } + const toggle = (selector, className) => { + const element = SelectorEngine.findOne(selector, outerElem); + if (element) { + element.classList.toggle(className, open); + } + }; + toggle(SELECTOR_DROPDOWN_TOGGLE, CLASS_NAME_ACTIVE); + toggle(SELECTOR_DROPDOWN_MENU, CLASS_NAME_SHOW$1); + outerElem.setAttribute('aria-expanded', open); + } + _setAttributeIfNotExists(element, attribute, value) { + if (!element.hasAttribute(attribute)) { + element.setAttribute(attribute, value); + } + } + _elemIsActive(elem) { + return elem.classList.contains(CLASS_NAME_ACTIVE); + } + + // Try to get the inner element (usually the .nav-link) + _getInnerElement(elem) { + return elem.matches(SELECTOR_INNER_ELEM) ? elem : SelectorEngine.findOne(SELECTOR_INNER_ELEM, elem); + } + + // Try to get the outer element (usually the .nav-item) + _getOuterElement(elem) { + return elem.closest(SELECTOR_OUTER) || elem; + } + + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Tab.getOrCreateInstance(this); + if (typeof config !== 'string') { + return; + } + if (data[config] === undefined || config.startsWith('_') || config === 'constructor') { + throw new TypeError(`No method named "${config}"`); + } + data[config](); + }); + } + } + + /** + * Data API implementation + */ + + EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) { + if (['A', 'AREA'].includes(this.tagName)) { + event.preventDefault(); + } + if (isDisabled(this)) { + return; + } + Tab.getOrCreateInstance(this).show(); + }); + + /** + * Initialize on focus + */ + EventHandler.on(window, EVENT_LOAD_DATA_API, () => { + for (const element of SelectorEngine.find(SELECTOR_DATA_TOGGLE_ACTIVE)) { + Tab.getOrCreateInstance(element); + } + }); + /** + * jQuery + */ + + defineJQueryPlugin(Tab); + + /** + * -------------------------------------------------------------------------- + * Bootstrap toast.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + + /** + * Constants + */ + + const NAME = 'toast'; + const DATA_KEY = 'bs.toast'; + const EVENT_KEY = `.${DATA_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}`; + const EVENT_SHOWN = `shown${EVENT_KEY}`; + const CLASS_NAME_FADE = 'fade'; + const CLASS_NAME_HIDE = 'hide'; // @deprecated - kept here only for backwards compatibility + const CLASS_NAME_SHOW = 'show'; + const CLASS_NAME_SHOWING = 'showing'; + const DefaultType = { + animation: 'boolean', + autohide: 'boolean', + delay: 'number' + }; + const Default = { + animation: true, + autohide: true, + delay: 5000 + }; + + /** + * Class definition + */ + + class Toast extends BaseComponent { + constructor(element, config) { + super(element, config); + this._timeout = null; + this._hasMouseInteraction = false; + this._hasKeyboardInteraction = false; + this._setListeners(); + } + + // Getters + static get Default() { + return Default; + } + static get DefaultType() { + return DefaultType; + } + static get NAME() { + return NAME; + } + + // Public + show() { + const showEvent = EventHandler.trigger(this._element, EVENT_SHOW); + if (showEvent.defaultPrevented) { + return; + } + this._clearTimeout(); + if (this._config.animation) { + this._element.classList.add(CLASS_NAME_FADE); + } + const complete = () => { + this._element.classList.remove(CLASS_NAME_SHOWING); + EventHandler.trigger(this._element, EVENT_SHOWN); + this._maybeScheduleHide(); + }; + this._element.classList.remove(CLASS_NAME_HIDE); // @deprecated + reflow(this._element); + this._element.classList.add(CLASS_NAME_SHOW, CLASS_NAME_SHOWING); + this._queueCallback(complete, this._element, this._config.animation); + } + hide() { + if (!this.isShown()) { + return; + } + const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE); + if (hideEvent.defaultPrevented) { + return; + } + const complete = () => { + this._element.classList.add(CLASS_NAME_HIDE); // @deprecated + this._element.classList.remove(CLASS_NAME_SHOWING, CLASS_NAME_SHOW); + EventHandler.trigger(this._element, EVENT_HIDDEN); + }; + this._element.classList.add(CLASS_NAME_SHOWING); + this._queueCallback(complete, this._element, this._config.animation); + } + dispose() { + this._clearTimeout(); + if (this.isShown()) { + this._element.classList.remove(CLASS_NAME_SHOW); + } + super.dispose(); + } + isShown() { + return this._element.classList.contains(CLASS_NAME_SHOW); + } + + // Private + + _maybeScheduleHide() { + if (!this._config.autohide) { + return; + } + if (this._hasMouseInteraction || this._hasKeyboardInteraction) { + return; + } + this._timeout = setTimeout(() => { + this.hide(); + }, this._config.delay); + } + _onInteraction(event, isInteracting) { + switch (event.type) { + case 'mouseover': + case 'mouseout': + { + this._hasMouseInteraction = isInteracting; + break; + } + case 'focusin': + case 'focusout': + { + this._hasKeyboardInteraction = isInteracting; + break; + } + } + if (isInteracting) { + this._clearTimeout(); + return; + } + const nextElement = event.relatedTarget; + if (this._element === nextElement || this._element.contains(nextElement)) { + return; + } + this._maybeScheduleHide(); + } + _setListeners() { + EventHandler.on(this._element, EVENT_MOUSEOVER, event => this._onInteraction(event, true)); + EventHandler.on(this._element, EVENT_MOUSEOUT, event => this._onInteraction(event, false)); + EventHandler.on(this._element, EVENT_FOCUSIN, event => this._onInteraction(event, true)); + EventHandler.on(this._element, EVENT_FOCUSOUT, event => this._onInteraction(event, false)); + } + _clearTimeout() { + clearTimeout(this._timeout); + this._timeout = null; + } + + // Static + static jQueryInterface(config) { + return this.each(function () { + const data = Toast.getOrCreateInstance(this, config); + if (typeof config === 'string') { + if (typeof data[config] === 'undefined') { + throw new TypeError(`No method named "${config}"`); + } + data[config](this); + } + }); + } + } + + /** + * Data API implementation + */ + + enableDismissTrigger(Toast); + + /** + * jQuery + */ + + defineJQueryPlugin(Toast); + + /** + * -------------------------------------------------------------------------- + * Bootstrap index.umd.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + + const index_umd = { + Alert, + Button, + Carousel, + Collapse, + Dropdown, + Modal, + Offcanvas, + Popover, + ScrollSpy, + Tab, + Toast, + Tooltip + }; + + return index_umd; + +})); diff --git a/src/static/scripts/bootstrap.css b/src/static/scripts/bootstrap.css index 614c226f..11e83fa5 100644 --- a/src/static/scripts/bootstrap.css +++ b/src/static/scripts/bootstrap.css @@ -1,11 +1,11 @@ @charset "UTF-8"; /*! - * Bootstrap v5.2.3 (https://getbootstrap.com/) - * Copyright 2011-2022 The Bootstrap Authors - * Copyright 2011-2022 Twitter, Inc. + * Bootstrap v5.3.1 (https://getbootstrap.com/) + * Copyright 2011-2023 The Bootstrap Authors * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) */ -:root { +:root, +[data-bs-theme=light] { --bs-blue: #0d6efd; --bs-indigo: #6610f2; --bs-purple: #6f42c1; @@ -45,10 +45,32 @@ --bs-danger-rgb: 220, 53, 69; --bs-light-rgb: 248, 249, 250; --bs-dark-rgb: 33, 37, 41; + --bs-primary-text-emphasis: #052c65; + --bs-secondary-text-emphasis: #2b2f32; + --bs-success-text-emphasis: #0a3622; + --bs-info-text-emphasis: #055160; + --bs-warning-text-emphasis: #664d03; + --bs-danger-text-emphasis: #58151c; + --bs-light-text-emphasis: #495057; + --bs-dark-text-emphasis: #495057; + --bs-primary-bg-subtle: #cfe2ff; + --bs-secondary-bg-subtle: #e2e3e5; + --bs-success-bg-subtle: #d1e7dd; + --bs-info-bg-subtle: #cff4fc; + --bs-warning-bg-subtle: #fff3cd; + --bs-danger-bg-subtle: #f8d7da; + --bs-light-bg-subtle: #fcfcfd; + --bs-dark-bg-subtle: #ced4da; + --bs-primary-border-subtle: #9ec5fe; + --bs-secondary-border-subtle: #c4c8cb; + --bs-success-border-subtle: #a3cfbb; + --bs-info-border-subtle: #9eeaf9; + --bs-warning-border-subtle: #ffe69c; + --bs-danger-border-subtle: #f1aeb5; + --bs-light-border-subtle: #e9ecef; + --bs-dark-border-subtle: #adb5bd; --bs-white-rgb: 255, 255, 255; --bs-black-rgb: 0, 0, 0; - --bs-body-color-rgb: 33, 37, 41; - --bs-body-bg-rgb: 255, 255, 255; --bs-font-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; --bs-font-monospace: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; --bs-gradient: linear-gradient(180deg, rgba(255, 255, 255, 0.15), rgba(255, 255, 255, 0)); @@ -57,7 +79,27 @@ --bs-body-font-weight: 400; --bs-body-line-height: 1.5; --bs-body-color: #212529; + --bs-body-color-rgb: 33, 37, 41; --bs-body-bg: #fff; + --bs-body-bg-rgb: 255, 255, 255; + --bs-emphasis-color: #000; + --bs-emphasis-color-rgb: 0, 0, 0; + --bs-secondary-color: rgba(33, 37, 41, 0.75); + --bs-secondary-color-rgb: 33, 37, 41; + --bs-secondary-bg: #e9ecef; + --bs-secondary-bg-rgb: 233, 236, 239; + --bs-tertiary-color: rgba(33, 37, 41, 0.5); + --bs-tertiary-color-rgb: 33, 37, 41; + --bs-tertiary-bg: #f8f9fa; + --bs-tertiary-bg-rgb: 248, 249, 250; + --bs-heading-color: inherit; + --bs-link-color: #0d6efd; + --bs-link-color-rgb: 13, 110, 253; + --bs-link-decoration: underline; + --bs-link-hover-color: #0a58ca; + --bs-link-hover-color-rgb: 10, 88, 202; + --bs-code-color: #d63384; + --bs-highlight-bg: #fff3cd; --bs-border-width: 1px; --bs-border-style: solid; --bs-border-color: #dee2e6; @@ -66,12 +108,74 @@ --bs-border-radius-sm: 0.25rem; --bs-border-radius-lg: 0.5rem; --bs-border-radius-xl: 1rem; - --bs-border-radius-2xl: 2rem; + --bs-border-radius-xxl: 2rem; + --bs-border-radius-2xl: var(--bs-border-radius-xxl); --bs-border-radius-pill: 50rem; - --bs-link-color: #0d6efd; - --bs-link-hover-color: #0a58ca; - --bs-code-color: #d63384; - --bs-highlight-bg: #fff3cd; + --bs-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); + --bs-box-shadow-sm: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); + --bs-box-shadow-lg: 0 1rem 3rem rgba(0, 0, 0, 0.175); + --bs-box-shadow-inset: inset 0 1px 2px rgba(0, 0, 0, 0.075); + --bs-focus-ring-width: 0.25rem; + --bs-focus-ring-opacity: 0.25; + --bs-focus-ring-color: rgba(13, 110, 253, 0.25); + --bs-form-valid-color: #198754; + --bs-form-valid-border-color: #198754; + --bs-form-invalid-color: #dc3545; + --bs-form-invalid-border-color: #dc3545; +} + +[data-bs-theme=dark] { + color-scheme: dark; + --bs-body-color: #dee2e6; + --bs-body-color-rgb: 222, 226, 230; + --bs-body-bg: #212529; + --bs-body-bg-rgb: 33, 37, 41; + --bs-emphasis-color: #fff; + --bs-emphasis-color-rgb: 255, 255, 255; + --bs-secondary-color: rgba(222, 226, 230, 0.75); + --bs-secondary-color-rgb: 222, 226, 230; + --bs-secondary-bg: #343a40; + --bs-secondary-bg-rgb: 52, 58, 64; + --bs-tertiary-color: rgba(222, 226, 230, 0.5); + --bs-tertiary-color-rgb: 222, 226, 230; + --bs-tertiary-bg: #2b3035; + --bs-tertiary-bg-rgb: 43, 48, 53; + --bs-primary-text-emphasis: #6ea8fe; + --bs-secondary-text-emphasis: #a7acb1; + --bs-success-text-emphasis: #75b798; + --bs-info-text-emphasis: #6edff6; + --bs-warning-text-emphasis: #ffda6a; + --bs-danger-text-emphasis: #ea868f; + --bs-light-text-emphasis: #f8f9fa; + --bs-dark-text-emphasis: #dee2e6; + --bs-primary-bg-subtle: #031633; + --bs-secondary-bg-subtle: #161719; + --bs-success-bg-subtle: #051b11; + --bs-info-bg-subtle: #032830; + --bs-warning-bg-subtle: #332701; + --bs-danger-bg-subtle: #2c0b0e; + --bs-light-bg-subtle: #343a40; + --bs-dark-bg-subtle: #1a1d20; + --bs-primary-border-subtle: #084298; + --bs-secondary-border-subtle: #41464b; + --bs-success-border-subtle: #0f5132; + --bs-info-border-subtle: #087990; + --bs-warning-border-subtle: #997404; + --bs-danger-border-subtle: #842029; + --bs-light-border-subtle: #495057; + --bs-dark-border-subtle: #343a40; + --bs-heading-color: inherit; + --bs-link-color: #6ea8fe; + --bs-link-hover-color: #8bb9fe; + --bs-link-color-rgb: 110, 168, 254; + --bs-link-hover-color-rgb: 139, 185, 254; + --bs-code-color: #e685b5; + --bs-border-color: #495057; + --bs-border-color-translucent: rgba(255, 255, 255, 0.15); + --bs-form-valid-color: #75b798; + --bs-form-valid-border-color: #75b798; + --bs-form-invalid-color: #ea868f; + --bs-form-invalid-border-color: #ea868f; } *, @@ -103,7 +207,7 @@ hr { margin: 1rem 0; color: inherit; border: 0; - border-top: 1px solid; + border-top: var(--bs-border-width) solid; opacity: 0.25; } @@ -112,6 +216,7 @@ h6, .h6, h5, .h5, h4, .h4, h3, .h3, h2, .h2, h1, .h1 { margin-bottom: 0.5rem; font-weight: 500; line-height: 1.2; + color: var(--bs-heading-color); } h1, .h1 { @@ -240,11 +345,11 @@ sup { } a { - color: var(--bs-link-color); + color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 1)); text-decoration: underline; } a:hover { - color: var(--bs-link-hover-color); + --bs-link-color-rgb: var(--bs-link-hover-color-rgb); } a:not([href]):not([class]), a:not([href]):not([class]):hover { @@ -311,7 +416,7 @@ table { caption { padding-top: 0.5rem; padding-bottom: 0.5rem; - color: #6c757d; + color: var(--bs-secondary-color); text-align: left; } @@ -435,8 +540,8 @@ legend + * { } [type=search] { - outline-offset: -2px; -webkit-appearance: textfield; + outline-offset: -2px; } /* rtl:raw: @@ -604,9 +709,9 @@ progress { .img-thumbnail { padding: 0.25rem; - background-color: #fff; - border: 1px solid var(--bs-border-color); - border-radius: 0.375rem; + background-color: var(--bs-body-bg); + border: var(--bs-border-width) solid var(--bs-border-color); + border-radius: var(--bs-border-radius); max-width: 100%; height: auto; } @@ -622,7 +727,7 @@ progress { .figure-caption { font-size: 0.875em; - color: #6c757d; + color: var(--bs-secondary-color); } .container, @@ -666,6 +771,15 @@ progress { max-width: 1320px; } } +:root { + --bs-breakpoint-xs: 0; + --bs-breakpoint-sm: 576px; + --bs-breakpoint-md: 768px; + --bs-breakpoint-lg: 992px; + --bs-breakpoint-xl: 1200px; + --bs-breakpoint-xxl: 1400px; +} + .row { --bs-gutter-x: 1.5rem; --bs-gutter-y: 0; @@ -1013,51 +1127,51 @@ progress { margin-left: 91.66666667%; } .g-sm-0, -.gx-sm-0 { + .gx-sm-0 { --bs-gutter-x: 0; } .g-sm-0, -.gy-sm-0 { + .gy-sm-0 { --bs-gutter-y: 0; } .g-sm-1, -.gx-sm-1 { + .gx-sm-1 { --bs-gutter-x: 0.25rem; } .g-sm-1, -.gy-sm-1 { + .gy-sm-1 { --bs-gutter-y: 0.25rem; } .g-sm-2, -.gx-sm-2 { + .gx-sm-2 { --bs-gutter-x: 0.5rem; } .g-sm-2, -.gy-sm-2 { + .gy-sm-2 { --bs-gutter-y: 0.5rem; } .g-sm-3, -.gx-sm-3 { + .gx-sm-3 { --bs-gutter-x: 1rem; } .g-sm-3, -.gy-sm-3 { + .gy-sm-3 { --bs-gutter-y: 1rem; } .g-sm-4, -.gx-sm-4 { + .gx-sm-4 { --bs-gutter-x: 1.5rem; } .g-sm-4, -.gy-sm-4 { + .gy-sm-4 { --bs-gutter-y: 1.5rem; } .g-sm-5, -.gx-sm-5 { + .gx-sm-5 { --bs-gutter-x: 3rem; } .g-sm-5, -.gy-sm-5 { + .gy-sm-5 { --bs-gutter-y: 3rem; } } @@ -1182,51 +1296,51 @@ progress { margin-left: 91.66666667%; } .g-md-0, -.gx-md-0 { + .gx-md-0 { --bs-gutter-x: 0; } .g-md-0, -.gy-md-0 { + .gy-md-0 { --bs-gutter-y: 0; } .g-md-1, -.gx-md-1 { + .gx-md-1 { --bs-gutter-x: 0.25rem; } .g-md-1, -.gy-md-1 { + .gy-md-1 { --bs-gutter-y: 0.25rem; } .g-md-2, -.gx-md-2 { + .gx-md-2 { --bs-gutter-x: 0.5rem; } .g-md-2, -.gy-md-2 { + .gy-md-2 { --bs-gutter-y: 0.5rem; } .g-md-3, -.gx-md-3 { + .gx-md-3 { --bs-gutter-x: 1rem; } .g-md-3, -.gy-md-3 { + .gy-md-3 { --bs-gutter-y: 1rem; } .g-md-4, -.gx-md-4 { + .gx-md-4 { --bs-gutter-x: 1.5rem; } .g-md-4, -.gy-md-4 { + .gy-md-4 { --bs-gutter-y: 1.5rem; } .g-md-5, -.gx-md-5 { + .gx-md-5 { --bs-gutter-x: 3rem; } .g-md-5, -.gy-md-5 { + .gy-md-5 { --bs-gutter-y: 3rem; } } @@ -1351,51 +1465,51 @@ progress { margin-left: 91.66666667%; } .g-lg-0, -.gx-lg-0 { + .gx-lg-0 { --bs-gutter-x: 0; } .g-lg-0, -.gy-lg-0 { + .gy-lg-0 { --bs-gutter-y: 0; } .g-lg-1, -.gx-lg-1 { + .gx-lg-1 { --bs-gutter-x: 0.25rem; } .g-lg-1, -.gy-lg-1 { + .gy-lg-1 { --bs-gutter-y: 0.25rem; } .g-lg-2, -.gx-lg-2 { + .gx-lg-2 { --bs-gutter-x: 0.5rem; } .g-lg-2, -.gy-lg-2 { + .gy-lg-2 { --bs-gutter-y: 0.5rem; } .g-lg-3, -.gx-lg-3 { + .gx-lg-3 { --bs-gutter-x: 1rem; } .g-lg-3, -.gy-lg-3 { + .gy-lg-3 { --bs-gutter-y: 1rem; } .g-lg-4, -.gx-lg-4 { + .gx-lg-4 { --bs-gutter-x: 1.5rem; } .g-lg-4, -.gy-lg-4 { + .gy-lg-4 { --bs-gutter-y: 1.5rem; } .g-lg-5, -.gx-lg-5 { + .gx-lg-5 { --bs-gutter-x: 3rem; } .g-lg-5, -.gy-lg-5 { + .gy-lg-5 { --bs-gutter-y: 3rem; } } @@ -1520,51 +1634,51 @@ progress { margin-left: 91.66666667%; } .g-xl-0, -.gx-xl-0 { + .gx-xl-0 { --bs-gutter-x: 0; } .g-xl-0, -.gy-xl-0 { + .gy-xl-0 { --bs-gutter-y: 0; } .g-xl-1, -.gx-xl-1 { + .gx-xl-1 { --bs-gutter-x: 0.25rem; } .g-xl-1, -.gy-xl-1 { + .gy-xl-1 { --bs-gutter-y: 0.25rem; } .g-xl-2, -.gx-xl-2 { + .gx-xl-2 { --bs-gutter-x: 0.5rem; } .g-xl-2, -.gy-xl-2 { + .gy-xl-2 { --bs-gutter-y: 0.5rem; } .g-xl-3, -.gx-xl-3 { + .gx-xl-3 { --bs-gutter-x: 1rem; } .g-xl-3, -.gy-xl-3 { + .gy-xl-3 { --bs-gutter-y: 1rem; } .g-xl-4, -.gx-xl-4 { + .gx-xl-4 { --bs-gutter-x: 1.5rem; } .g-xl-4, -.gy-xl-4 { + .gy-xl-4 { --bs-gutter-y: 1.5rem; } .g-xl-5, -.gx-xl-5 { + .gx-xl-5 { --bs-gutter-x: 3rem; } .g-xl-5, -.gy-xl-5 { + .gy-xl-5 { --bs-gutter-y: 3rem; } } @@ -1689,57 +1803,61 @@ progress { margin-left: 91.66666667%; } .g-xxl-0, -.gx-xxl-0 { + .gx-xxl-0 { --bs-gutter-x: 0; } .g-xxl-0, -.gy-xxl-0 { + .gy-xxl-0 { --bs-gutter-y: 0; } .g-xxl-1, -.gx-xxl-1 { + .gx-xxl-1 { --bs-gutter-x: 0.25rem; } .g-xxl-1, -.gy-xxl-1 { + .gy-xxl-1 { --bs-gutter-y: 0.25rem; } .g-xxl-2, -.gx-xxl-2 { + .gx-xxl-2 { --bs-gutter-x: 0.5rem; } .g-xxl-2, -.gy-xxl-2 { + .gy-xxl-2 { --bs-gutter-y: 0.5rem; } .g-xxl-3, -.gx-xxl-3 { + .gx-xxl-3 { --bs-gutter-x: 1rem; } .g-xxl-3, -.gy-xxl-3 { + .gy-xxl-3 { --bs-gutter-y: 1rem; } .g-xxl-4, -.gx-xxl-4 { + .gx-xxl-4 { --bs-gutter-x: 1.5rem; } .g-xxl-4, -.gy-xxl-4 { + .gy-xxl-4 { --bs-gutter-y: 1.5rem; } .g-xxl-5, -.gx-xxl-5 { + .gx-xxl-5 { --bs-gutter-x: 3rem; } .g-xxl-5, -.gy-xxl-5 { + .gy-xxl-5 { --bs-gutter-y: 3rem; } } .table { + --bs-table-color-type: initial; + --bs-table-bg-type: initial; + --bs-table-color-state: initial; + --bs-table-bg-state: initial; --bs-table-color: var(--bs-body-color); - --bs-table-bg: transparent; + --bs-table-bg: var(--bs-body-bg); --bs-table-border-color: var(--bs-border-color); --bs-table-accent-bg: transparent; --bs-table-striped-color: var(--bs-body-color); @@ -1750,15 +1868,15 @@ progress { --bs-table-hover-bg: rgba(0, 0, 0, 0.075); width: 100%; margin-bottom: 1rem; - color: var(--bs-table-color); vertical-align: top; border-color: var(--bs-table-border-color); } .table > :not(caption) > * > * { padding: 0.5rem 0.5rem; + color: var(--bs-table-color-state, var(--bs-table-color-type, var(--bs-table-color))); background-color: var(--bs-table-bg); - border-bottom-width: 1px; - box-shadow: inset 0 0 0 9999px var(--bs-table-accent-bg); + border-bottom-width: var(--bs-border-width); + box-shadow: inset 0 0 0 9999px var(--bs-table-bg-state, var(--bs-table-bg-type, var(--bs-table-accent-bg))); } .table > tbody { vertical-align: inherit; @@ -1768,7 +1886,7 @@ progress { } .table-group-divider { - border-top: 2px solid currentcolor; + border-top: calc(var(--bs-border-width) * 2) solid currentcolor; } .caption-top { @@ -1780,10 +1898,10 @@ progress { } .table-bordered > :not(caption) > * { - border-width: 1px 0; + border-width: var(--bs-border-width) 0; } .table-bordered > :not(caption) > * > * { - border-width: 0 1px; + border-width: 0 var(--bs-border-width); } .table-borderless > :not(caption) > * > * { @@ -1794,23 +1912,23 @@ progress { } .table-striped > tbody > tr:nth-of-type(odd) > * { - --bs-table-accent-bg: var(--bs-table-striped-bg); - color: var(--bs-table-striped-color); + --bs-table-color-type: var(--bs-table-striped-color); + --bs-table-bg-type: var(--bs-table-striped-bg); } .table-striped-columns > :not(caption) > tr > :nth-child(even) { - --bs-table-accent-bg: var(--bs-table-striped-bg); - color: var(--bs-table-striped-color); + --bs-table-color-type: var(--bs-table-striped-color); + --bs-table-bg-type: var(--bs-table-striped-bg); } .table-active { - --bs-table-accent-bg: var(--bs-table-active-bg); - color: var(--bs-table-active-color); + --bs-table-color-state: var(--bs-table-active-color); + --bs-table-bg-state: var(--bs-table-active-bg); } .table-hover > tbody > tr:hover > * { - --bs-table-accent-bg: var(--bs-table-hover-bg); - color: var(--bs-table-hover-color); + --bs-table-color-state: var(--bs-table-hover-color); + --bs-table-bg-state: var(--bs-table-hover-bg); } .table-primary { @@ -1965,29 +2083,29 @@ progress { } .col-form-label { - padding-top: calc(0.375rem + 1px); - padding-bottom: calc(0.375rem + 1px); + padding-top: calc(0.375rem + var(--bs-border-width)); + padding-bottom: calc(0.375rem + var(--bs-border-width)); margin-bottom: 0; font-size: inherit; line-height: 1.5; } .col-form-label-lg { - padding-top: calc(0.5rem + 1px); - padding-bottom: calc(0.5rem + 1px); + padding-top: calc(0.5rem + var(--bs-border-width)); + padding-bottom: calc(0.5rem + var(--bs-border-width)); font-size: 1.25rem; } .col-form-label-sm { - padding-top: calc(0.25rem + 1px); - padding-bottom: calc(0.25rem + 1px); + padding-top: calc(0.25rem + var(--bs-border-width)); + padding-bottom: calc(0.25rem + var(--bs-border-width)); font-size: 0.875rem; } .form-text { margin-top: 0.25rem; font-size: 0.875em; - color: #6c757d; + color: var(--bs-secondary-color); } .form-control { @@ -1997,14 +2115,14 @@ progress { font-size: 1rem; font-weight: 400; line-height: 1.5; - color: #212529; - background-color: #fff; - background-clip: padding-box; - border: 1px solid #ced4da; + color: var(--bs-body-color); -webkit-appearance: none; -moz-appearance: none; appearance: none; - border-radius: 0.375rem; + background-color: var(--bs-body-bg); + background-clip: padding-box; + border: var(--bs-border-width) solid var(--bs-border-color); + border-radius: var(--bs-border-radius); transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { @@ -2019,25 +2137,31 @@ progress { cursor: pointer; } .form-control:focus { - color: #212529; - background-color: #fff; + color: var(--bs-body-color); + background-color: var(--bs-body-bg); border-color: #86b7fe; outline: 0; box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); } .form-control::-webkit-date-and-time-value { + min-width: 85px; height: 1.5em; + margin: 0; +} +.form-control::-webkit-datetime-edit { + display: block; + padding: 0; } .form-control::-moz-placeholder { - color: #6c757d; + color: var(--bs-secondary-color); opacity: 1; } .form-control::placeholder { - color: #6c757d; + color: var(--bs-secondary-color); opacity: 1; } .form-control:disabled { - background-color: #e9ecef; + background-color: var(--bs-secondary-bg); opacity: 1; } .form-control::-webkit-file-upload-button { @@ -2045,13 +2169,13 @@ progress { margin: -0.375rem -0.75rem; -webkit-margin-end: 0.75rem; margin-inline-end: 0.75rem; - color: #212529; - background-color: #e9ecef; + color: var(--bs-body-color); + background-color: var(--bs-tertiary-bg); pointer-events: none; border-color: inherit; border-style: solid; border-width: 0; - border-inline-end-width: 1px; + border-inline-end-width: var(--bs-border-width); border-radius: 0; -webkit-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; @@ -2061,13 +2185,13 @@ progress { margin: -0.375rem -0.75rem; -webkit-margin-end: 0.75rem; margin-inline-end: 0.75rem; - color: #212529; - background-color: #e9ecef; + color: var(--bs-body-color); + background-color: var(--bs-tertiary-bg); pointer-events: none; border-color: inherit; border-style: solid; border-width: 0; - border-inline-end-width: 1px; + border-inline-end-width: var(--bs-border-width); border-radius: 0; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; } @@ -2081,10 +2205,10 @@ progress { } } .form-control:hover:not(:disabled):not([readonly])::-webkit-file-upload-button { - background-color: #dde0e3; + background-color: var(--bs-secondary-bg); } .form-control:hover:not(:disabled):not([readonly])::file-selector-button { - background-color: #dde0e3; + background-color: var(--bs-secondary-bg); } .form-control-plaintext { @@ -2093,10 +2217,10 @@ progress { padding: 0.375rem 0; margin-bottom: 0; line-height: 1.5; - color: #212529; + color: var(--bs-body-color); background-color: transparent; border: solid transparent; - border-width: 1px 0; + border-width: var(--bs-border-width) 0; } .form-control-plaintext:focus { outline: 0; @@ -2107,10 +2231,10 @@ progress { } .form-control-sm { - min-height: calc(1.5em + 0.5rem + 2px); + min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); padding: 0.25rem 0.5rem; font-size: 0.875rem; - border-radius: 0.25rem; + border-radius: var(--bs-border-radius-sm); } .form-control-sm::-webkit-file-upload-button { padding: 0.25rem 0.5rem; @@ -2126,10 +2250,10 @@ progress { } .form-control-lg { - min-height: calc(1.5em + 1rem + 2px); + min-height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); padding: 0.5rem 1rem; font-size: 1.25rem; - border-radius: 0.5rem; + border-radius: var(--bs-border-radius-lg); } .form-control-lg::-webkit-file-upload-button { padding: 0.5rem 1rem; @@ -2145,18 +2269,18 @@ progress { } textarea.form-control { - min-height: calc(1.5em + 0.75rem + 2px); + min-height: calc(1.5em + 0.75rem + calc(var(--bs-border-width) * 2)); } textarea.form-control-sm { - min-height: calc(1.5em + 0.5rem + 2px); + min-height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); } textarea.form-control-lg { - min-height: calc(1.5em + 1rem + 2px); + min-height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); } .form-control-color { width: 3rem; - height: calc(1.5em + 0.75rem + 2px); + height: calc(1.5em + 0.75rem + calc(var(--bs-border-width) * 2)); padding: 0.375rem; } .form-control-color:not(:disabled):not([readonly]) { @@ -2164,38 +2288,39 @@ textarea.form-control-lg { } .form-control-color::-moz-color-swatch { border: 0 !important; - border-radius: 0.375rem; + border-radius: var(--bs-border-radius); } .form-control-color::-webkit-color-swatch { - border-radius: 0.375rem; + border: 0 !important; + border-radius: var(--bs-border-radius); } .form-control-color.form-control-sm { - height: calc(1.5em + 0.5rem + 2px); + height: calc(1.5em + 0.5rem + calc(var(--bs-border-width) * 2)); } .form-control-color.form-control-lg { - height: calc(1.5em + 1rem + 2px); + height: calc(1.5em + 1rem + calc(var(--bs-border-width) * 2)); } .form-select { + --bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); display: block; width: 100%; padding: 0.375rem 2.25rem 0.375rem 0.75rem; - -moz-padding-start: calc(0.75rem - 3px); font-size: 1rem; font-weight: 400; line-height: 1.5; - color: #212529; - background-color: #fff; - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); + color: var(--bs-body-color); + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + background-color: var(--bs-body-bg); + background-image: var(--bs-form-select-bg-img), var(--bs-form-select-bg-icon, none); background-repeat: no-repeat; background-position: right 0.75rem center; background-size: 16px 12px; - border: 1px solid #ced4da; - border-radius: 0.375rem; + border: var(--bs-border-width) solid var(--bs-border-color); + border-radius: var(--bs-border-radius); transition: border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - -webkit-appearance: none; - -moz-appearance: none; - appearance: none; } @media (prefers-reduced-motion: reduce) { .form-select { @@ -2212,11 +2337,11 @@ textarea.form-control-lg { background-image: none; } .form-select:disabled { - background-color: #e9ecef; + background-color: var(--bs-secondary-bg); } .form-select:-moz-focusring { color: transparent; - text-shadow: 0 0 0 #212529; + text-shadow: 0 0 0 var(--bs-body-color); } .form-select-sm { @@ -2224,7 +2349,7 @@ textarea.form-control-lg { padding-bottom: 0.25rem; padding-left: 0.5rem; font-size: 0.875rem; - border-radius: 0.25rem; + border-radius: var(--bs-border-radius-sm); } .form-select-lg { @@ -2232,7 +2357,11 @@ textarea.form-control-lg { padding-bottom: 0.5rem; padding-left: 1rem; font-size: 1.25rem; - border-radius: 0.5rem; + border-radius: var(--bs-border-radius-lg); +} + +[data-bs-theme=dark] .form-select { + --bs-form-select-bg-img: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23dee2e6' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"); } .form-check { @@ -2258,18 +2387,20 @@ textarea.form-control-lg { } .form-check-input { + --bs-form-check-bg: var(--bs-body-bg); width: 1em; height: 1em; margin-top: 0.25em; vertical-align: top; - background-color: #fff; - background-repeat: no-repeat; - background-position: center; - background-size: contain; - border: 1px solid rgba(0, 0, 0, 0.25); -webkit-appearance: none; -moz-appearance: none; appearance: none; + background-color: var(--bs-form-check-bg); + background-image: var(--bs-form-check-bg-image); + background-repeat: no-repeat; + background-position: center; + background-size: contain; + border: var(--bs-border-width) solid var(--bs-border-color); -webkit-print-color-adjust: exact; color-adjust: exact; print-color-adjust: exact; @@ -2293,15 +2424,15 @@ textarea.form-control-lg { border-color: #0d6efd; } .form-check-input:checked[type=checkbox] { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e"); + --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e"); } .form-check-input:checked[type=radio] { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e"); + --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%23fff'/%3e%3c/svg%3e"); } .form-check-input[type=checkbox]:indeterminate { background-color: #0d6efd; border-color: #0d6efd; - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e"); + --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e"); } .form-check-input:disabled { pointer-events: none; @@ -2317,9 +2448,10 @@ textarea.form-control-lg { padding-left: 2.5em; } .form-switch .form-check-input { + --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e"); width: 2em; margin-left: -2.5em; - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%280, 0, 0, 0.25%29'/%3e%3c/svg%3e"); + background-image: var(--bs-form-switch-bg); background-position: left center; border-radius: 2em; transition: background-position 0.15s ease-in-out; @@ -2330,11 +2462,11 @@ textarea.form-control-lg { } } .form-switch .form-check-input:focus { - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e"); + --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%2386b7fe'/%3e%3c/svg%3e"); } .form-switch .form-check-input:checked { background-position: right center; - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); + --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23fff'/%3e%3c/svg%3e"); } .form-switch.form-check-reverse { padding-right: 2.5em; @@ -2361,14 +2493,18 @@ textarea.form-control-lg { opacity: 0.65; } +[data-bs-theme=dark] .form-switch .form-check-input:not(:checked):not(:focus) { + --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='rgba%28255, 255, 255, 0.25%29'/%3e%3c/svg%3e"); +} + .form-range { width: 100%; height: 1.5rem; padding: 0; - background-color: transparent; -webkit-appearance: none; -moz-appearance: none; appearance: none; + background-color: transparent; } .form-range:focus { outline: 0; @@ -2386,13 +2522,13 @@ textarea.form-control-lg { width: 1rem; height: 1rem; margin-top: -0.25rem; + -webkit-appearance: none; + appearance: none; background-color: #0d6efd; border: 0; border-radius: 1rem; -webkit-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - -webkit-appearance: none; - appearance: none; } @media (prefers-reduced-motion: reduce) { .form-range::-webkit-slider-thumb { @@ -2408,20 +2544,20 @@ textarea.form-control-lg { height: 0.5rem; color: transparent; cursor: pointer; - background-color: #dee2e6; + background-color: var(--bs-tertiary-bg); border-color: transparent; border-radius: 1rem; } .form-range::-moz-range-thumb { width: 1rem; height: 1rem; + -moz-appearance: none; + appearance: none; background-color: #0d6efd; border: 0; border-radius: 1rem; -moz-transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; transition: background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out; - -moz-appearance: none; - appearance: none; } @media (prefers-reduced-motion: reduce) { .form-range::-moz-range-thumb { @@ -2437,7 +2573,7 @@ textarea.form-control-lg { height: 0.5rem; color: transparent; cursor: pointer; - background-color: #dee2e6; + background-color: var(--bs-tertiary-bg); border-color: transparent; border-radius: 1rem; } @@ -2445,10 +2581,10 @@ textarea.form-control-lg { pointer-events: none; } .form-range:disabled::-webkit-slider-thumb { - background-color: #adb5bd; + background-color: var(--bs-secondary-color); } .form-range:disabled::-moz-range-thumb { - background-color: #adb5bd; + background-color: var(--bs-secondary-color); } .form-floating { @@ -2457,14 +2593,15 @@ textarea.form-control-lg { .form-floating > .form-control, .form-floating > .form-control-plaintext, .form-floating > .form-select { - height: calc(3.5rem + 2px); + height: calc(3.5rem + calc(var(--bs-border-width) * 2)); + min-height: calc(3.5rem + calc(var(--bs-border-width) * 2)); line-height: 1.25; } .form-floating > label { position: absolute; top: 0; left: 0; - width: 100%; + z-index: 2; height: 100%; padding: 1rem 0.75rem; overflow: hidden; @@ -2472,7 +2609,7 @@ textarea.form-control-lg { text-overflow: ellipsis; white-space: nowrap; pointer-events: none; - border: 1px solid transparent; + border: var(--bs-border-width) solid transparent; transform-origin: 0 0; transition: opacity 0.1s ease-in-out, transform 0.1s ease-in-out; } @@ -2512,22 +2649,51 @@ textarea.form-control-lg { padding-bottom: 0.625rem; } .form-floating > .form-control:not(:-moz-placeholder-shown) ~ label { - opacity: 0.65; + color: rgba(var(--bs-body-color-rgb), 0.65); transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); } .form-floating > .form-control:focus ~ label, .form-floating > .form-control:not(:placeholder-shown) ~ label, .form-floating > .form-control-plaintext ~ label, .form-floating > .form-select ~ label { - opacity: 0.65; + color: rgba(var(--bs-body-color-rgb), 0.65); transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); } +.form-floating > .form-control:not(:-moz-placeholder-shown) ~ label::after { + position: absolute; + inset: 1rem 0.375rem; + z-index: -1; + height: 1.5em; + content: ""; + background-color: var(--bs-body-bg); + border-radius: var(--bs-border-radius); +} +.form-floating > .form-control:focus ~ label::after, +.form-floating > .form-control:not(:placeholder-shown) ~ label::after, +.form-floating > .form-control-plaintext ~ label::after, +.form-floating > .form-select ~ label::after { + position: absolute; + inset: 1rem 0.375rem; + z-index: -1; + height: 1.5em; + content: ""; + background-color: var(--bs-body-bg); + border-radius: var(--bs-border-radius); +} .form-floating > .form-control:-webkit-autofill ~ label { - opacity: 0.65; + color: rgba(var(--bs-body-color-rgb), 0.65); transform: scale(0.85) translateY(-0.5rem) translateX(0.15rem); } .form-floating > .form-control-plaintext ~ label { - border-width: 1px 0; + border-width: var(--bs-border-width) 0; +} +.form-floating > :disabled ~ label, +.form-floating > .form-control:disabled ~ label { + color: #6c757d; +} +.form-floating > :disabled ~ label::after, +.form-floating > .form-control:disabled ~ label::after { + background-color: var(--bs-secondary-bg); } .input-group { @@ -2565,12 +2731,12 @@ textarea.form-control-lg { font-size: 1rem; font-weight: 400; line-height: 1.5; - color: #212529; + color: var(--bs-body-color); text-align: center; white-space: nowrap; - background-color: #e9ecef; - border: 1px solid #ced4da; - border-radius: 0.375rem; + background-color: var(--bs-tertiary-bg); + border: var(--bs-border-width) solid var(--bs-border-color); + border-radius: var(--bs-border-radius); } .input-group-lg > .form-control, @@ -2579,7 +2745,7 @@ textarea.form-control-lg { .input-group-lg > .btn { padding: 0.5rem 1rem; font-size: 1.25rem; - border-radius: 0.5rem; + border-radius: var(--bs-border-radius-lg); } .input-group-sm > .form-control, @@ -2588,7 +2754,7 @@ textarea.form-control-lg { .input-group-sm > .btn { padding: 0.25rem 0.5rem; font-size: 0.875rem; - border-radius: 0.25rem; + border-radius: var(--bs-border-radius-sm); } .input-group-lg > .form-select, @@ -2611,7 +2777,7 @@ textarea.form-control-lg { border-bottom-right-radius: 0; } .input-group > :not(:first-child):not(.dropdown-menu):not(.valid-tooltip):not(.valid-feedback):not(.invalid-tooltip):not(.invalid-feedback) { - margin-left: -1px; + margin-left: calc(var(--bs-border-width) * -1); border-top-left-radius: 0; border-bottom-left-radius: 0; } @@ -2626,7 +2792,7 @@ textarea.form-control-lg { width: 100%; margin-top: 0.25rem; font-size: 0.875em; - color: #198754; + color: var(--bs-form-valid-color); } .valid-tooltip { @@ -2639,8 +2805,8 @@ textarea.form-control-lg { margin-top: 0.1rem; font-size: 0.875rem; color: #fff; - background-color: rgba(25, 135, 84, 0.9); - border-radius: 0.375rem; + background-color: var(--bs-success); + border-radius: var(--bs-border-radius); } .was-validated :valid ~ .valid-feedback, @@ -2651,7 +2817,7 @@ textarea.form-control-lg { } .was-validated .form-control:valid, .form-control.is-valid { - border-color: #198754; + border-color: var(--bs-form-valid-border-color); padding-right: calc(1.5em + 0.75rem); background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); background-repeat: no-repeat; @@ -2659,8 +2825,8 @@ textarea.form-control-lg { background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-control:valid:focus, .form-control.is-valid:focus { - border-color: #198754; - box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25); + border-color: var(--bs-form-valid-border-color); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); } .was-validated textarea.form-control:valid, textarea.form-control.is-valid { @@ -2669,17 +2835,17 @@ textarea.form-control-lg { } .was-validated .form-select:valid, .form-select.is-valid { - border-color: #198754; + border-color: var(--bs-form-valid-border-color); } .was-validated .form-select:valid:not([multiple]):not([size]), .was-validated .form-select:valid:not([multiple])[size="1"], .form-select.is-valid:not([multiple]):not([size]), .form-select.is-valid:not([multiple])[size="1"] { + --bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); padding-right: 4.125rem; - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"), url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 8 8'%3e%3cpath fill='%23198754' d='M2.3 6.73.6 4.53c-.4-1.04.46-1.4 1.1-.8l1.1 1.4 3.4-3.8c.6-.63 1.6-.27 1.2.7l-4 4.6c-.43.5-.8.4-1.1.1z'/%3e%3c/svg%3e"); background-position: right 0.75rem center, center right 2.25rem; background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-select:valid:focus, .form-select.is-valid:focus { - border-color: #198754; - box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25); + border-color: var(--bs-form-valid-border-color); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); } .was-validated .form-control-color:valid, .form-control-color.is-valid { @@ -2687,16 +2853,16 @@ textarea.form-control-lg { } .was-validated .form-check-input:valid, .form-check-input.is-valid { - border-color: #198754; + border-color: var(--bs-form-valid-border-color); } .was-validated .form-check-input:valid:checked, .form-check-input.is-valid:checked { - background-color: #198754; + background-color: var(--bs-form-valid-color); } .was-validated .form-check-input:valid:focus, .form-check-input.is-valid:focus { - box-shadow: 0 0 0 0.25rem rgba(25, 135, 84, 0.25); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-success-rgb), 0.25); } .was-validated .form-check-input:valid ~ .form-check-label, .form-check-input.is-valid ~ .form-check-label { - color: #198754; + color: var(--bs-form-valid-color); } .form-check-inline .form-check-input ~ .valid-feedback { @@ -2716,7 +2882,7 @@ textarea.form-control-lg { width: 100%; margin-top: 0.25rem; font-size: 0.875em; - color: #dc3545; + color: var(--bs-form-invalid-color); } .invalid-tooltip { @@ -2729,8 +2895,8 @@ textarea.form-control-lg { margin-top: 0.1rem; font-size: 0.875rem; color: #fff; - background-color: rgba(220, 53, 69, 0.9); - border-radius: 0.375rem; + background-color: var(--bs-danger); + border-radius: var(--bs-border-radius); } .was-validated :invalid ~ .invalid-feedback, @@ -2741,7 +2907,7 @@ textarea.form-control-lg { } .was-validated .form-control:invalid, .form-control.is-invalid { - border-color: #dc3545; + border-color: var(--bs-form-invalid-border-color); padding-right: calc(1.5em + 0.75rem); background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); background-repeat: no-repeat; @@ -2749,8 +2915,8 @@ textarea.form-control-lg { background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-control:invalid:focus, .form-control.is-invalid:focus { - border-color: #dc3545; - box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25); + border-color: var(--bs-form-invalid-border-color); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); } .was-validated textarea.form-control:invalid, textarea.form-control.is-invalid { @@ -2759,17 +2925,17 @@ textarea.form-control-lg { } .was-validated .form-select:invalid, .form-select.is-invalid { - border-color: #dc3545; + border-color: var(--bs-form-invalid-border-color); } .was-validated .form-select:invalid:not([multiple]):not([size]), .was-validated .form-select:invalid:not([multiple])[size="1"], .form-select.is-invalid:not([multiple]):not([size]), .form-select.is-invalid:not([multiple])[size="1"] { + --bs-form-select-bg-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); padding-right: 4.125rem; - background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23343a40' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='m2 5 6 6 6-6'/%3e%3c/svg%3e"), url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' width='12' height='12' fill='none' stroke='%23dc3545'%3e%3ccircle cx='6' cy='6' r='4.5'/%3e%3cpath stroke-linejoin='round' d='M5.8 3.6h.4L6 6.5z'/%3e%3ccircle cx='6' cy='8.2' r='.6' fill='%23dc3545' stroke='none'/%3e%3c/svg%3e"); background-position: right 0.75rem center, center right 2.25rem; background-size: 16px 12px, calc(0.75em + 0.375rem) calc(0.75em + 0.375rem); } .was-validated .form-select:invalid:focus, .form-select.is-invalid:focus { - border-color: #dc3545; - box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25); + border-color: var(--bs-form-invalid-border-color); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); } .was-validated .form-control-color:invalid, .form-control-color.is-invalid { @@ -2777,16 +2943,16 @@ textarea.form-control-lg { } .was-validated .form-check-input:invalid, .form-check-input.is-invalid { - border-color: #dc3545; + border-color: var(--bs-form-invalid-border-color); } .was-validated .form-check-input:invalid:checked, .form-check-input.is-invalid:checked { - background-color: #dc3545; + background-color: var(--bs-form-invalid-color); } .was-validated .form-check-input:invalid:focus, .form-check-input.is-invalid:focus { - box-shadow: 0 0 0 0.25rem rgba(220, 53, 69, 0.25); + box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.25); } .was-validated .form-check-input:invalid ~ .form-check-label, .form-check-input.is-invalid ~ .form-check-label { - color: #dc3545; + color: var(--bs-form-invalid-color); } .form-check-inline .form-check-input ~ .invalid-feedback { @@ -2808,11 +2974,11 @@ textarea.form-control-lg { --bs-btn-font-size: 1rem; --bs-btn-font-weight: 400; --bs-btn-line-height: 1.5; - --bs-btn-color: #212529; + --bs-btn-color: var(--bs-body-color); --bs-btn-bg: transparent; - --bs-btn-border-width: 1px; + --bs-btn-border-width: var(--bs-border-width); --bs-btn-border-color: transparent; - --bs-btn-border-radius: 0.375rem; + --bs-btn-border-radius: var(--bs-border-radius); --bs-btn-hover-border-color: transparent; --bs-btn-box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.15), 0 1px 1px rgba(0, 0, 0, 0.075); --bs-btn-disabled-opacity: 0.65; @@ -3162,7 +3328,7 @@ textarea.form-control-lg { --bs-btn-active-border-color: transparent; --bs-btn-disabled-color: #6c757d; --bs-btn-disabled-border-color: transparent; - --bs-btn-box-shadow: none; + --bs-btn-box-shadow: 0 0 0 #000; --bs-btn-focus-shadow-rgb: 49, 132, 253; text-decoration: underline; } @@ -3177,14 +3343,14 @@ textarea.form-control-lg { --bs-btn-padding-y: 0.5rem; --bs-btn-padding-x: 1rem; --bs-btn-font-size: 1.25rem; - --bs-btn-border-radius: 0.5rem; + --bs-btn-border-radius: var(--bs-border-radius-lg); } .btn-sm, .btn-group-sm > .btn { --bs-btn-padding-y: 0.25rem; --bs-btn-padding-x: 0.5rem; --bs-btn-font-size: 0.875rem; - --bs-btn-border-radius: 0.25rem; + --bs-btn-border-radius: var(--bs-border-radius-sm); } .fade { @@ -3257,21 +3423,21 @@ textarea.form-control-lg { --bs-dropdown-padding-y: 0.5rem; --bs-dropdown-spacer: 0.125rem; --bs-dropdown-font-size: 1rem; - --bs-dropdown-color: #212529; - --bs-dropdown-bg: #fff; + --bs-dropdown-color: var(--bs-body-color); + --bs-dropdown-bg: var(--bs-body-bg); --bs-dropdown-border-color: var(--bs-border-color-translucent); - --bs-dropdown-border-radius: 0.375rem; - --bs-dropdown-border-width: 1px; - --bs-dropdown-inner-border-radius: calc(0.375rem - 1px); + --bs-dropdown-border-radius: var(--bs-border-radius); + --bs-dropdown-border-width: var(--bs-border-width); + --bs-dropdown-inner-border-radius: calc(var(--bs-border-radius) - var(--bs-border-width)); --bs-dropdown-divider-bg: var(--bs-border-color-translucent); --bs-dropdown-divider-margin-y: 0.5rem; --bs-dropdown-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); - --bs-dropdown-link-color: #212529; - --bs-dropdown-link-hover-color: #1e2125; - --bs-dropdown-link-hover-bg: #e9ecef; + --bs-dropdown-link-color: var(--bs-body-color); + --bs-dropdown-link-hover-color: var(--bs-body-color); + --bs-dropdown-link-hover-bg: var(--bs-tertiary-bg); --bs-dropdown-link-active-color: #fff; --bs-dropdown-link-active-bg: #0d6efd; - --bs-dropdown-link-disabled-color: #adb5bd; + --bs-dropdown-link-disabled-color: var(--bs-tertiary-color); --bs-dropdown-item-padding-x: 1rem; --bs-dropdown-item-padding-y: 0.25rem; --bs-dropdown-header-color: #6c757d; @@ -3490,6 +3656,7 @@ textarea.form-control-lg { white-space: nowrap; background-color: transparent; border: 0; + border-radius: var(--bs-dropdown-item-border-radius, 0); } .dropdown-item:hover, .dropdown-item:focus { color: var(--bs-dropdown-link-hover-color); @@ -3576,11 +3743,11 @@ textarea.form-control-lg { } .btn-group { - border-radius: 0.375rem; + border-radius: var(--bs-border-radius); } .btn-group > :not(.btn-check:first-child) + .btn, .btn-group > .btn-group:not(:first-child) { - margin-left: -1px; + margin-left: calc(var(--bs-border-width) * -1); } .btn-group > .btn:not(:last-child):not(.dropdown-toggle), .btn-group > .btn.dropdown-toggle-split:first-child, @@ -3627,7 +3794,7 @@ textarea.form-control-lg { } .btn-group-vertical > .btn:not(:first-child), .btn-group-vertical > .btn-group:not(:first-child) { - margin-top: -1px; + margin-top: calc(var(--bs-border-width) * -1); } .btn-group-vertical > .btn:not(:last-child):not(.dropdown-toggle), .btn-group-vertical > .btn-group:not(:last-child) > .btn { @@ -3646,7 +3813,7 @@ textarea.form-control-lg { --bs-nav-link-font-weight: ; --bs-nav-link-color: var(--bs-link-color); --bs-nav-link-hover-color: var(--bs-link-hover-color); - --bs-nav-link-disabled-color: #6c757d; + --bs-nav-link-disabled-color: var(--bs-secondary-color); display: flex; flex-wrap: wrap; padding-left: 0; @@ -3661,6 +3828,8 @@ textarea.form-control-lg { font-weight: var(--bs-nav-link-font-weight); color: var(--bs-nav-link-color); text-decoration: none; + background: none; + border: 0; transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out; } @media (prefers-reduced-motion: reduce) { @@ -3671,25 +3840,28 @@ textarea.form-control-lg { .nav-link:hover, .nav-link:focus { color: var(--bs-nav-link-hover-color); } -.nav-link.disabled { +.nav-link:focus-visible { + outline: 0; + box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); +} +.nav-link.disabled, .nav-link:disabled { color: var(--bs-nav-link-disabled-color); pointer-events: none; cursor: default; } .nav-tabs { - --bs-nav-tabs-border-width: 1px; - --bs-nav-tabs-border-color: #dee2e6; - --bs-nav-tabs-border-radius: 0.375rem; - --bs-nav-tabs-link-hover-border-color: #e9ecef #e9ecef #dee2e6; - --bs-nav-tabs-link-active-color: #495057; - --bs-nav-tabs-link-active-bg: #fff; - --bs-nav-tabs-link-active-border-color: #dee2e6 #dee2e6 #fff; + --bs-nav-tabs-border-width: var(--bs-border-width); + --bs-nav-tabs-border-color: var(--bs-border-color); + --bs-nav-tabs-border-radius: var(--bs-border-radius); + --bs-nav-tabs-link-hover-border-color: var(--bs-secondary-bg) var(--bs-secondary-bg) var(--bs-border-color); + --bs-nav-tabs-link-active-color: var(--bs-emphasis-color); + --bs-nav-tabs-link-active-bg: var(--bs-body-bg); + --bs-nav-tabs-link-active-border-color: var(--bs-border-color) var(--bs-border-color) var(--bs-body-bg); border-bottom: var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color); } .nav-tabs .nav-link { margin-bottom: calc(-1 * var(--bs-nav-tabs-border-width)); - background: none; border: var(--bs-nav-tabs-border-width) solid transparent; border-top-left-radius: var(--bs-nav-tabs-border-radius); border-top-right-radius: var(--bs-nav-tabs-border-radius); @@ -3698,11 +3870,6 @@ textarea.form-control-lg { isolation: isolate; border-color: var(--bs-nav-tabs-link-hover-border-color); } -.nav-tabs .nav-link.disabled, .nav-tabs .nav-link:disabled { - color: var(--bs-nav-link-disabled-color); - background-color: transparent; - border-color: transparent; -} .nav-tabs .nav-link.active, .nav-tabs .nav-item.show .nav-link { color: var(--bs-nav-tabs-link-active-color); @@ -3716,26 +3883,40 @@ textarea.form-control-lg { } .nav-pills { - --bs-nav-pills-border-radius: 0.375rem; + --bs-nav-pills-border-radius: var(--bs-border-radius); --bs-nav-pills-link-active-color: #fff; --bs-nav-pills-link-active-bg: #0d6efd; } .nav-pills .nav-link { - background: none; - border: 0; border-radius: var(--bs-nav-pills-border-radius); } -.nav-pills .nav-link:disabled { - color: var(--bs-nav-link-disabled-color); - background-color: transparent; - border-color: transparent; -} .nav-pills .nav-link.active, .nav-pills .show > .nav-link { color: var(--bs-nav-pills-link-active-color); background-color: var(--bs-nav-pills-link-active-bg); } +.nav-underline { + --bs-nav-underline-gap: 1rem; + --bs-nav-underline-border-width: 0.125rem; + --bs-nav-underline-link-active-color: var(--bs-emphasis-color); + gap: var(--bs-nav-underline-gap); +} +.nav-underline .nav-link { + padding-right: 0; + padding-left: 0; + border-bottom: var(--bs-nav-underline-border-width) solid transparent; +} +.nav-underline .nav-link:hover, .nav-underline .nav-link:focus { + border-bottom-color: currentcolor; +} +.nav-underline .nav-link.active, +.nav-underline .show > .nav-link { + font-weight: 700; + color: var(--bs-nav-underline-link-active-color); + border-bottom-color: currentcolor; +} + .nav-fill > .nav-link, .nav-fill .nav-item { flex: 1 1 auto; @@ -3764,22 +3945,22 @@ textarea.form-control-lg { .navbar { --bs-navbar-padding-x: 0; --bs-navbar-padding-y: 0.5rem; - --bs-navbar-color: rgba(0, 0, 0, 0.55); - --bs-navbar-hover-color: rgba(0, 0, 0, 0.7); - --bs-navbar-disabled-color: rgba(0, 0, 0, 0.3); - --bs-navbar-active-color: rgba(0, 0, 0, 0.9); + --bs-navbar-color: rgba(var(--bs-emphasis-color-rgb), 0.65); + --bs-navbar-hover-color: rgba(var(--bs-emphasis-color-rgb), 0.8); + --bs-navbar-disabled-color: rgba(var(--bs-emphasis-color-rgb), 0.3); + --bs-navbar-active-color: rgba(var(--bs-emphasis-color-rgb), 1); --bs-navbar-brand-padding-y: 0.3125rem; --bs-navbar-brand-margin-end: 1rem; --bs-navbar-brand-font-size: 1.25rem; - --bs-navbar-brand-color: rgba(0, 0, 0, 0.9); - --bs-navbar-brand-hover-color: rgba(0, 0, 0, 0.9); + --bs-navbar-brand-color: rgba(var(--bs-emphasis-color-rgb), 1); + --bs-navbar-brand-hover-color: rgba(var(--bs-emphasis-color-rgb), 1); --bs-navbar-nav-link-padding-x: 0.5rem; --bs-navbar-toggler-padding-y: 0.25rem; --bs-navbar-toggler-padding-x: 0.75rem; --bs-navbar-toggler-font-size: 1.25rem; - --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%280, 0, 0, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); - --bs-navbar-toggler-border-color: rgba(0, 0, 0, 0.1); - --bs-navbar-toggler-border-radius: 0.375rem; + --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%2833, 37, 41, 0.75%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); + --bs-navbar-toggler-border-color: rgba(var(--bs-emphasis-color-rgb), 0.15); + --bs-navbar-toggler-border-radius: var(--bs-border-radius); --bs-navbar-toggler-focus-width: 0.25rem; --bs-navbar-toggler-transition: box-shadow 0.15s ease-in-out; position: relative; @@ -3827,8 +4008,7 @@ textarea.form-control-lg { margin-bottom: 0; list-style: none; } -.navbar-nav .show > .nav-link, -.navbar-nav .nav-link.active { +.navbar-nav .nav-link.active, .navbar-nav .nav-link.show { color: var(--bs-navbar-active-color); } .navbar-nav .dropdown-menu { @@ -4173,7 +4353,8 @@ textarea.form-control-lg { overflow-y: visible; } -.navbar-dark { +.navbar-dark, +.navbar[data-bs-theme=dark] { --bs-navbar-color: rgba(255, 255, 255, 0.55); --bs-navbar-hover-color: rgba(255, 255, 255, 0.75); --bs-navbar-disabled-color: rgba(255, 255, 255, 0.25); @@ -4184,22 +4365,28 @@ textarea.form-control-lg { --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); } +[data-bs-theme=dark] .navbar-toggler-icon { + --bs-navbar-toggler-icon-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 30 30'%3e%3cpath stroke='rgba%28255, 255, 255, 0.55%29' stroke-linecap='round' stroke-miterlimit='10' stroke-width='2' d='M4 7h22M4 15h22M4 23h22'/%3e%3c/svg%3e"); +} + .card { --bs-card-spacer-y: 1rem; --bs-card-spacer-x: 1rem; --bs-card-title-spacer-y: 0.5rem; - --bs-card-border-width: 1px; + --bs-card-title-color: ; + --bs-card-subtitle-color: ; + --bs-card-border-width: var(--bs-border-width); --bs-card-border-color: var(--bs-border-color-translucent); - --bs-card-border-radius: 0.375rem; + --bs-card-border-radius: var(--bs-border-radius); --bs-card-box-shadow: ; - --bs-card-inner-border-radius: calc(0.375rem - 1px); + --bs-card-inner-border-radius: calc(var(--bs-border-radius) - (var(--bs-border-width))); --bs-card-cap-padding-y: 0.5rem; --bs-card-cap-padding-x: 1rem; - --bs-card-cap-bg: rgba(0, 0, 0, 0.03); + --bs-card-cap-bg: rgba(var(--bs-body-color-rgb), 0.03); --bs-card-cap-color: ; --bs-card-height: ; --bs-card-color: ; - --bs-card-bg: #fff; + --bs-card-bg: var(--bs-body-bg); --bs-card-img-overlay-padding: 1rem; --bs-card-group-margin: 0.75rem; position: relative; @@ -4207,6 +4394,7 @@ textarea.form-control-lg { flex-direction: column; min-width: 0; height: var(--bs-card-height); + color: var(--bs-body-color); word-wrap: break-word; background-color: var(--bs-card-bg); background-clip: border-box; @@ -4244,11 +4432,13 @@ textarea.form-control-lg { .card-title { margin-bottom: var(--bs-card-title-spacer-y); + color: var(--bs-card-title-color); } .card-subtitle { margin-top: calc(-0.5 * var(--bs-card-title-spacer-y)); margin-bottom: 0; + color: var(--bs-card-subtitle-color); } .card-text:last-child { @@ -4345,11 +4535,11 @@ textarea.form-control-lg { border-bottom-right-radius: 0; } .card-group > .card:not(:last-child) .card-img-top, -.card-group > .card:not(:last-child) .card-header { + .card-group > .card:not(:last-child) .card-header { border-top-right-radius: 0; } .card-group > .card:not(:last-child) .card-img-bottom, -.card-group > .card:not(:last-child) .card-footer { + .card-group > .card:not(:last-child) .card-footer { border-bottom-right-radius: 0; } .card-group > .card:not(:first-child) { @@ -4357,38 +4547,38 @@ textarea.form-control-lg { border-bottom-left-radius: 0; } .card-group > .card:not(:first-child) .card-img-top, -.card-group > .card:not(:first-child) .card-header { + .card-group > .card:not(:first-child) .card-header { border-top-left-radius: 0; } .card-group > .card:not(:first-child) .card-img-bottom, -.card-group > .card:not(:first-child) .card-footer { + .card-group > .card:not(:first-child) .card-footer { border-bottom-left-radius: 0; } } .accordion { - --bs-accordion-color: #212529; - --bs-accordion-bg: #fff; + --bs-accordion-color: var(--bs-body-color); + --bs-accordion-bg: var(--bs-body-bg); --bs-accordion-transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out, box-shadow 0.15s ease-in-out, border-radius 0.15s ease; --bs-accordion-border-color: var(--bs-border-color); - --bs-accordion-border-width: 1px; - --bs-accordion-border-radius: 0.375rem; - --bs-accordion-inner-border-radius: calc(0.375rem - 1px); + --bs-accordion-border-width: var(--bs-border-width); + --bs-accordion-border-radius: var(--bs-border-radius); + --bs-accordion-inner-border-radius: calc(var(--bs-border-radius) - (var(--bs-border-width))); --bs-accordion-btn-padding-x: 1.25rem; --bs-accordion-btn-padding-y: 1rem; - --bs-accordion-btn-color: #212529; + --bs-accordion-btn-color: var(--bs-body-color); --bs-accordion-btn-bg: var(--bs-accordion-bg); --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23212529'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); --bs-accordion-btn-icon-width: 1.25rem; --bs-accordion-btn-icon-transform: rotate(-180deg); --bs-accordion-btn-icon-transition: transform 0.2s ease-in-out; - --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%230c63e4'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); + --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23052c65'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); --bs-accordion-btn-focus-border-color: #86b7fe; --bs-accordion-btn-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); --bs-accordion-body-padding-x: 1.25rem; --bs-accordion-body-padding-y: 1rem; - --bs-accordion-active-color: #0c63e4; - --bs-accordion-active-bg: #e7f1ff; + --bs-accordion-active-color: var(--bs-primary-text-emphasis); + --bs-accordion-active-bg: var(--bs-primary-bg-subtle); } .accordion-button { @@ -4501,15 +4691,20 @@ textarea.form-control-lg { border-radius: 0; } +[data-bs-theme=dark] .accordion-button::after { + --bs-accordion-btn-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); + --bs-accordion-btn-active-icon: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%236ea8fe'%3e%3cpath fill-rule='evenodd' d='M1.646 4.646a.5.5 0 0 1 .708 0L8 10.293l5.646-5.647a.5.5 0 0 1 .708.708l-6 6a.5.5 0 0 1-.708 0l-6-6a.5.5 0 0 1 0-.708z'/%3e%3c/svg%3e"); +} + .breadcrumb { --bs-breadcrumb-padding-x: 0; --bs-breadcrumb-padding-y: 0; --bs-breadcrumb-margin-bottom: 1rem; --bs-breadcrumb-bg: ; --bs-breadcrumb-border-radius: ; - --bs-breadcrumb-divider-color: #6c757d; + --bs-breadcrumb-divider-color: var(--bs-secondary-color); --bs-breadcrumb-item-padding-x: 0.5rem; - --bs-breadcrumb-item-active-color: #6c757d; + --bs-breadcrumb-item-active-color: var(--bs-secondary-color); display: flex; flex-wrap: wrap; padding: var(--bs-breadcrumb-padding-y) var(--bs-breadcrumb-padding-x); @@ -4538,22 +4733,22 @@ textarea.form-control-lg { --bs-pagination-padding-y: 0.375rem; --bs-pagination-font-size: 1rem; --bs-pagination-color: var(--bs-link-color); - --bs-pagination-bg: #fff; - --bs-pagination-border-width: 1px; - --bs-pagination-border-color: #dee2e6; - --bs-pagination-border-radius: 0.375rem; + --bs-pagination-bg: var(--bs-body-bg); + --bs-pagination-border-width: var(--bs-border-width); + --bs-pagination-border-color: var(--bs-border-color); + --bs-pagination-border-radius: var(--bs-border-radius); --bs-pagination-hover-color: var(--bs-link-hover-color); - --bs-pagination-hover-bg: #e9ecef; - --bs-pagination-hover-border-color: #dee2e6; + --bs-pagination-hover-bg: var(--bs-tertiary-bg); + --bs-pagination-hover-border-color: var(--bs-border-color); --bs-pagination-focus-color: var(--bs-link-hover-color); - --bs-pagination-focus-bg: #e9ecef; + --bs-pagination-focus-bg: var(--bs-secondary-bg); --bs-pagination-focus-box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); --bs-pagination-active-color: #fff; --bs-pagination-active-bg: #0d6efd; --bs-pagination-active-border-color: #0d6efd; - --bs-pagination-disabled-color: #6c757d; - --bs-pagination-disabled-bg: #fff; - --bs-pagination-disabled-border-color: #dee2e6; + --bs-pagination-disabled-color: var(--bs-secondary-color); + --bs-pagination-disabled-bg: var(--bs-secondary-bg); + --bs-pagination-disabled-border-color: var(--bs-border-color); display: flex; padding-left: 0; list-style: none; @@ -4602,7 +4797,7 @@ textarea.form-control-lg { } .page-item:not(:first-child) .page-link { - margin-left: -1px; + margin-left: calc(var(--bs-border-width) * -1); } .page-item:first-child .page-link { border-top-left-radius: var(--bs-pagination-border-radius); @@ -4617,14 +4812,14 @@ textarea.form-control-lg { --bs-pagination-padding-x: 1.5rem; --bs-pagination-padding-y: 0.75rem; --bs-pagination-font-size: 1.25rem; - --bs-pagination-border-radius: 0.5rem; + --bs-pagination-border-radius: var(--bs-border-radius-lg); } .pagination-sm { --bs-pagination-padding-x: 0.5rem; --bs-pagination-padding-y: 0.25rem; --bs-pagination-font-size: 0.875rem; - --bs-pagination-border-radius: 0.25rem; + --bs-pagination-border-radius: var(--bs-border-radius-sm); } .badge { @@ -4633,7 +4828,7 @@ textarea.form-control-lg { --bs-badge-font-size: 0.75em; --bs-badge-font-weight: 700; --bs-badge-color: #fff; - --bs-badge-border-radius: 0.375rem; + --bs-badge-border-radius: var(--bs-border-radius); display: inline-block; padding: var(--bs-badge-padding-y) var(--bs-badge-padding-x); font-size: var(--bs-badge-font-size); @@ -4661,8 +4856,9 @@ textarea.form-control-lg { --bs-alert-margin-bottom: 1rem; --bs-alert-color: inherit; --bs-alert-border-color: transparent; - --bs-alert-border: 1px solid var(--bs-alert-border-color); - --bs-alert-border-radius: 0.375rem; + --bs-alert-border: var(--bs-border-width) solid var(--bs-alert-border-color); + --bs-alert-border-radius: var(--bs-border-radius); + --bs-alert-link-color: inherit; position: relative; padding: var(--bs-alert-padding-y) var(--bs-alert-padding-x); margin-bottom: var(--bs-alert-margin-bottom); @@ -4678,6 +4874,7 @@ textarea.form-control-lg { .alert-link { font-weight: 700; + color: var(--bs-alert-link-color); } .alert-dismissible { @@ -4692,75 +4889,59 @@ textarea.form-control-lg { } .alert-primary { - --bs-alert-color: #084298; - --bs-alert-bg: #cfe2ff; - --bs-alert-border-color: #b6d4fe; -} -.alert-primary .alert-link { - color: #06357a; + --bs-alert-color: var(--bs-primary-text-emphasis); + --bs-alert-bg: var(--bs-primary-bg-subtle); + --bs-alert-border-color: var(--bs-primary-border-subtle); + --bs-alert-link-color: var(--bs-primary-text-emphasis); } .alert-secondary { - --bs-alert-color: #41464b; - --bs-alert-bg: #e2e3e5; - --bs-alert-border-color: #d3d6d8; -} -.alert-secondary .alert-link { - color: #34383c; + --bs-alert-color: var(--bs-secondary-text-emphasis); + --bs-alert-bg: var(--bs-secondary-bg-subtle); + --bs-alert-border-color: var(--bs-secondary-border-subtle); + --bs-alert-link-color: var(--bs-secondary-text-emphasis); } .alert-success { - --bs-alert-color: #0f5132; - --bs-alert-bg: #d1e7dd; - --bs-alert-border-color: #badbcc; -} -.alert-success .alert-link { - color: #0c4128; + --bs-alert-color: var(--bs-success-text-emphasis); + --bs-alert-bg: var(--bs-success-bg-subtle); + --bs-alert-border-color: var(--bs-success-border-subtle); + --bs-alert-link-color: var(--bs-success-text-emphasis); } .alert-info { - --bs-alert-color: #055160; - --bs-alert-bg: #cff4fc; - --bs-alert-border-color: #b6effb; -} -.alert-info .alert-link { - color: #04414d; + --bs-alert-color: var(--bs-info-text-emphasis); + --bs-alert-bg: var(--bs-info-bg-subtle); + --bs-alert-border-color: var(--bs-info-border-subtle); + --bs-alert-link-color: var(--bs-info-text-emphasis); } .alert-warning { - --bs-alert-color: #664d03; - --bs-alert-bg: #fff3cd; - --bs-alert-border-color: #ffecb5; -} -.alert-warning .alert-link { - color: #523e02; + --bs-alert-color: var(--bs-warning-text-emphasis); + --bs-alert-bg: var(--bs-warning-bg-subtle); + --bs-alert-border-color: var(--bs-warning-border-subtle); + --bs-alert-link-color: var(--bs-warning-text-emphasis); } .alert-danger { - --bs-alert-color: #842029; - --bs-alert-bg: #f8d7da; - --bs-alert-border-color: #f5c2c7; -} -.alert-danger .alert-link { - color: #6a1a21; + --bs-alert-color: var(--bs-danger-text-emphasis); + --bs-alert-bg: var(--bs-danger-bg-subtle); + --bs-alert-border-color: var(--bs-danger-border-subtle); + --bs-alert-link-color: var(--bs-danger-text-emphasis); } .alert-light { - --bs-alert-color: #636464; - --bs-alert-bg: #fefefe; - --bs-alert-border-color: #fdfdfe; -} -.alert-light .alert-link { - color: #4f5050; + --bs-alert-color: var(--bs-light-text-emphasis); + --bs-alert-bg: var(--bs-light-bg-subtle); + --bs-alert-border-color: var(--bs-light-border-subtle); + --bs-alert-link-color: var(--bs-light-text-emphasis); } .alert-dark { - --bs-alert-color: #141619; - --bs-alert-bg: #d3d3d4; - --bs-alert-border-color: #bcbebf; -} -.alert-dark .alert-link { - color: #101214; + --bs-alert-color: var(--bs-dark-text-emphasis); + --bs-alert-bg: var(--bs-dark-bg-subtle); + --bs-alert-border-color: var(--bs-dark-border-subtle); + --bs-alert-link-color: var(--bs-dark-text-emphasis); } @keyframes progress-bar-stripes { @@ -4768,12 +4949,13 @@ textarea.form-control-lg { background-position-x: 1rem; } } -.progress { +.progress, +.progress-stacked { --bs-progress-height: 1rem; --bs-progress-font-size: 0.75rem; - --bs-progress-bg: #e9ecef; - --bs-progress-border-radius: 0.375rem; - --bs-progress-box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.075); + --bs-progress-bg: var(--bs-secondary-bg); + --bs-progress-border-radius: var(--bs-border-radius); + --bs-progress-box-shadow: var(--bs-box-shadow-inset); --bs-progress-bar-color: #fff; --bs-progress-bar-bg: #0d6efd; --bs-progress-bar-transition: width 0.6s ease; @@ -4807,6 +4989,14 @@ textarea.form-control-lg { background-size: var(--bs-progress-height) var(--bs-progress-height); } +.progress-stacked > .progress { + overflow: visible; +} + +.progress-stacked > .progress > .progress-bar { + width: 100%; +} + .progress-bar-animated { animation: 1s linear infinite progress-bar-stripes; } @@ -4817,20 +5007,20 @@ textarea.form-control-lg { } .list-group { - --bs-list-group-color: #212529; - --bs-list-group-bg: #fff; - --bs-list-group-border-color: rgba(0, 0, 0, 0.125); - --bs-list-group-border-width: 1px; - --bs-list-group-border-radius: 0.375rem; + --bs-list-group-color: var(--bs-body-color); + --bs-list-group-bg: var(--bs-body-bg); + --bs-list-group-border-color: var(--bs-border-color); + --bs-list-group-border-width: var(--bs-border-width); + --bs-list-group-border-radius: var(--bs-border-radius); --bs-list-group-item-padding-x: 1rem; --bs-list-group-item-padding-y: 0.5rem; - --bs-list-group-action-color: #495057; - --bs-list-group-action-hover-color: #495057; - --bs-list-group-action-hover-bg: #f8f9fa; - --bs-list-group-action-active-color: #212529; - --bs-list-group-action-active-bg: #e9ecef; - --bs-list-group-disabled-color: #6c757d; - --bs-list-group-disabled-bg: #fff; + --bs-list-group-action-color: var(--bs-secondary-color); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-tertiary-bg); + --bs-list-group-action-active-color: var(--bs-body-color); + --bs-list-group-action-active-bg: var(--bs-secondary-bg); + --bs-list-group-disabled-color: var(--bs-secondary-color); + --bs-list-group-disabled-bg: var(--bs-body-bg); --bs-list-group-active-color: #fff; --bs-list-group-active-bg: #0d6efd; --bs-list-group-active-border-color: #0d6efd; @@ -5056,148 +5246,152 @@ textarea.form-control-lg { } .list-group-item-primary { - color: #084298; - background-color: #cfe2ff; -} -.list-group-item-primary.list-group-item-action:hover, .list-group-item-primary.list-group-item-action:focus { - color: #084298; - background-color: #bacbe6; -} -.list-group-item-primary.list-group-item-action.active { - color: #fff; - background-color: #084298; - border-color: #084298; + --bs-list-group-color: var(--bs-primary-text-emphasis); + --bs-list-group-bg: var(--bs-primary-bg-subtle); + --bs-list-group-border-color: var(--bs-primary-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-primary-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-primary-border-subtle); + --bs-list-group-active-color: var(--bs-primary-bg-subtle); + --bs-list-group-active-bg: var(--bs-primary-text-emphasis); + --bs-list-group-active-border-color: var(--bs-primary-text-emphasis); } .list-group-item-secondary { - color: #41464b; - background-color: #e2e3e5; -} -.list-group-item-secondary.list-group-item-action:hover, .list-group-item-secondary.list-group-item-action:focus { - color: #41464b; - background-color: #cbccce; -} -.list-group-item-secondary.list-group-item-action.active { - color: #fff; - background-color: #41464b; - border-color: #41464b; + --bs-list-group-color: var(--bs-secondary-text-emphasis); + --bs-list-group-bg: var(--bs-secondary-bg-subtle); + --bs-list-group-border-color: var(--bs-secondary-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-secondary-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-secondary-border-subtle); + --bs-list-group-active-color: var(--bs-secondary-bg-subtle); + --bs-list-group-active-bg: var(--bs-secondary-text-emphasis); + --bs-list-group-active-border-color: var(--bs-secondary-text-emphasis); } .list-group-item-success { - color: #0f5132; - background-color: #d1e7dd; -} -.list-group-item-success.list-group-item-action:hover, .list-group-item-success.list-group-item-action:focus { - color: #0f5132; - background-color: #bcd0c7; -} -.list-group-item-success.list-group-item-action.active { - color: #fff; - background-color: #0f5132; - border-color: #0f5132; + --bs-list-group-color: var(--bs-success-text-emphasis); + --bs-list-group-bg: var(--bs-success-bg-subtle); + --bs-list-group-border-color: var(--bs-success-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-success-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-success-border-subtle); + --bs-list-group-active-color: var(--bs-success-bg-subtle); + --bs-list-group-active-bg: var(--bs-success-text-emphasis); + --bs-list-group-active-border-color: var(--bs-success-text-emphasis); } .list-group-item-info { - color: #055160; - background-color: #cff4fc; -} -.list-group-item-info.list-group-item-action:hover, .list-group-item-info.list-group-item-action:focus { - color: #055160; - background-color: #badce3; -} -.list-group-item-info.list-group-item-action.active { - color: #fff; - background-color: #055160; - border-color: #055160; + --bs-list-group-color: var(--bs-info-text-emphasis); + --bs-list-group-bg: var(--bs-info-bg-subtle); + --bs-list-group-border-color: var(--bs-info-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-info-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-info-border-subtle); + --bs-list-group-active-color: var(--bs-info-bg-subtle); + --bs-list-group-active-bg: var(--bs-info-text-emphasis); + --bs-list-group-active-border-color: var(--bs-info-text-emphasis); } .list-group-item-warning { - color: #664d03; - background-color: #fff3cd; -} -.list-group-item-warning.list-group-item-action:hover, .list-group-item-warning.list-group-item-action:focus { - color: #664d03; - background-color: #e6dbb9; -} -.list-group-item-warning.list-group-item-action.active { - color: #fff; - background-color: #664d03; - border-color: #664d03; + --bs-list-group-color: var(--bs-warning-text-emphasis); + --bs-list-group-bg: var(--bs-warning-bg-subtle); + --bs-list-group-border-color: var(--bs-warning-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-warning-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-warning-border-subtle); + --bs-list-group-active-color: var(--bs-warning-bg-subtle); + --bs-list-group-active-bg: var(--bs-warning-text-emphasis); + --bs-list-group-active-border-color: var(--bs-warning-text-emphasis); } .list-group-item-danger { - color: #842029; - background-color: #f8d7da; -} -.list-group-item-danger.list-group-item-action:hover, .list-group-item-danger.list-group-item-action:focus { - color: #842029; - background-color: #dfc2c4; -} -.list-group-item-danger.list-group-item-action.active { - color: #fff; - background-color: #842029; - border-color: #842029; + --bs-list-group-color: var(--bs-danger-text-emphasis); + --bs-list-group-bg: var(--bs-danger-bg-subtle); + --bs-list-group-border-color: var(--bs-danger-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-danger-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-danger-border-subtle); + --bs-list-group-active-color: var(--bs-danger-bg-subtle); + --bs-list-group-active-bg: var(--bs-danger-text-emphasis); + --bs-list-group-active-border-color: var(--bs-danger-text-emphasis); } .list-group-item-light { - color: #636464; - background-color: #fefefe; -} -.list-group-item-light.list-group-item-action:hover, .list-group-item-light.list-group-item-action:focus { - color: #636464; - background-color: #e5e5e5; -} -.list-group-item-light.list-group-item-action.active { - color: #fff; - background-color: #636464; - border-color: #636464; + --bs-list-group-color: var(--bs-light-text-emphasis); + --bs-list-group-bg: var(--bs-light-bg-subtle); + --bs-list-group-border-color: var(--bs-light-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-light-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-light-border-subtle); + --bs-list-group-active-color: var(--bs-light-bg-subtle); + --bs-list-group-active-bg: var(--bs-light-text-emphasis); + --bs-list-group-active-border-color: var(--bs-light-text-emphasis); } .list-group-item-dark { - color: #141619; - background-color: #d3d3d4; -} -.list-group-item-dark.list-group-item-action:hover, .list-group-item-dark.list-group-item-action:focus { - color: #141619; - background-color: #bebebf; -} -.list-group-item-dark.list-group-item-action.active { - color: #fff; - background-color: #141619; - border-color: #141619; + --bs-list-group-color: var(--bs-dark-text-emphasis); + --bs-list-group-bg: var(--bs-dark-bg-subtle); + --bs-list-group-border-color: var(--bs-dark-border-subtle); + --bs-list-group-action-hover-color: var(--bs-emphasis-color); + --bs-list-group-action-hover-bg: var(--bs-dark-border-subtle); + --bs-list-group-action-active-color: var(--bs-emphasis-color); + --bs-list-group-action-active-bg: var(--bs-dark-border-subtle); + --bs-list-group-active-color: var(--bs-dark-bg-subtle); + --bs-list-group-active-bg: var(--bs-dark-text-emphasis); + --bs-list-group-active-border-color: var(--bs-dark-text-emphasis); } .btn-close { + --bs-btn-close-color: #000; + --bs-btn-close-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e"); + --bs-btn-close-opacity: 0.5; + --bs-btn-close-hover-opacity: 0.75; + --bs-btn-close-focus-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); + --bs-btn-close-focus-opacity: 1; + --bs-btn-close-disabled-opacity: 0.25; + --bs-btn-close-white-filter: invert(1) grayscale(100%) brightness(200%); box-sizing: content-box; width: 1em; height: 1em; padding: 0.25em 0.25em; - color: #000; - background: transparent url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='%23000'%3e%3cpath d='M.293.293a1 1 0 0 1 1.414 0L8 6.586 14.293.293a1 1 0 1 1 1.414 1.414L9.414 8l6.293 6.293a1 1 0 0 1-1.414 1.414L8 9.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L6.586 8 .293 1.707a1 1 0 0 1 0-1.414z'/%3e%3c/svg%3e") center/1em auto no-repeat; + color: var(--bs-btn-close-color); + background: transparent var(--bs-btn-close-bg) center/1em auto no-repeat; border: 0; border-radius: 0.375rem; - opacity: 0.5; + opacity: var(--bs-btn-close-opacity); } .btn-close:hover { - color: #000; + color: var(--bs-btn-close-color); text-decoration: none; - opacity: 0.75; + opacity: var(--bs-btn-close-hover-opacity); } .btn-close:focus { outline: 0; - box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25); - opacity: 1; + box-shadow: var(--bs-btn-close-focus-shadow); + opacity: var(--bs-btn-close-focus-opacity); } .btn-close:disabled, .btn-close.disabled { pointer-events: none; -webkit-user-select: none; -moz-user-select: none; user-select: none; - opacity: 0.25; + opacity: var(--bs-btn-close-disabled-opacity); } .btn-close-white { - filter: invert(1) grayscale(100%) brightness(200%); + filter: var(--bs-btn-close-white-filter); +} + +[data-bs-theme=dark] .btn-close { + filter: var(--bs-btn-close-white-filter); } .toast { @@ -5208,14 +5402,14 @@ textarea.form-control-lg { --bs-toast-max-width: 350px; --bs-toast-font-size: 0.875rem; --bs-toast-color: ; - --bs-toast-bg: rgba(255, 255, 255, 0.85); - --bs-toast-border-width: 1px; + --bs-toast-bg: rgba(var(--bs-body-bg-rgb), 0.85); + --bs-toast-border-width: var(--bs-border-width); --bs-toast-border-color: var(--bs-border-color-translucent); - --bs-toast-border-radius: 0.375rem; - --bs-toast-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); - --bs-toast-header-color: #6c757d; - --bs-toast-header-bg: rgba(255, 255, 255, 0.85); - --bs-toast-header-border-color: rgba(0, 0, 0, 0.05); + --bs-toast-border-radius: var(--bs-border-radius); + --bs-toast-box-shadow: var(--bs-box-shadow); + --bs-toast-header-color: var(--bs-secondary-color); + --bs-toast-header-bg: rgba(var(--bs-body-bg-rgb), 0.85); + --bs-toast-header-border-color: var(--bs-border-color-translucent); width: var(--bs-toast-max-width); max-width: 100%; font-size: var(--bs-toast-font-size); @@ -5275,22 +5469,22 @@ textarea.form-control-lg { --bs-modal-padding: 1rem; --bs-modal-margin: 0.5rem; --bs-modal-color: ; - --bs-modal-bg: #fff; + --bs-modal-bg: var(--bs-body-bg); --bs-modal-border-color: var(--bs-border-color-translucent); - --bs-modal-border-width: 1px; - --bs-modal-border-radius: 0.5rem; + --bs-modal-border-width: var(--bs-border-width); + --bs-modal-border-radius: var(--bs-border-radius-lg); --bs-modal-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); - --bs-modal-inner-border-radius: calc(0.5rem - 1px); + --bs-modal-inner-border-radius: calc(var(--bs-border-radius-lg) - (var(--bs-border-width))); --bs-modal-header-padding-x: 1rem; --bs-modal-header-padding-y: 1rem; --bs-modal-header-padding: 1rem 1rem; --bs-modal-header-border-color: var(--bs-border-color); - --bs-modal-header-border-width: 1px; + --bs-modal-header-border-width: var(--bs-border-width); --bs-modal-title-line-height: 1.5; --bs-modal-footer-gap: 0.5rem; --bs-modal-footer-bg: ; --bs-modal-footer-border-color: var(--bs-border-color); - --bs-modal-footer-border-width: 1px; + --bs-modal-footer-border-width: var(--bs-border-width); position: fixed; top: 0; left: 0; @@ -5433,7 +5627,7 @@ textarea.form-control-lg { } @media (min-width: 992px) { .modal-lg, -.modal-xl { + .modal-xl { --bs-modal-width: 800px; } } @@ -5474,7 +5668,7 @@ textarea.form-control-lg { border-radius: 0; } .modal-fullscreen-sm-down .modal-header, -.modal-fullscreen-sm-down .modal-footer { + .modal-fullscreen-sm-down .modal-footer { border-radius: 0; } .modal-fullscreen-sm-down .modal-body { @@ -5494,7 +5688,7 @@ textarea.form-control-lg { border-radius: 0; } .modal-fullscreen-md-down .modal-header, -.modal-fullscreen-md-down .modal-footer { + .modal-fullscreen-md-down .modal-footer { border-radius: 0; } .modal-fullscreen-md-down .modal-body { @@ -5514,7 +5708,7 @@ textarea.form-control-lg { border-radius: 0; } .modal-fullscreen-lg-down .modal-header, -.modal-fullscreen-lg-down .modal-footer { + .modal-fullscreen-lg-down .modal-footer { border-radius: 0; } .modal-fullscreen-lg-down .modal-body { @@ -5534,7 +5728,7 @@ textarea.form-control-lg { border-radius: 0; } .modal-fullscreen-xl-down .modal-header, -.modal-fullscreen-xl-down .modal-footer { + .modal-fullscreen-xl-down .modal-footer { border-radius: 0; } .modal-fullscreen-xl-down .modal-body { @@ -5554,7 +5748,7 @@ textarea.form-control-lg { border-radius: 0; } .modal-fullscreen-xxl-down .modal-header, -.modal-fullscreen-xxl-down .modal-footer { + .modal-fullscreen-xxl-down .modal-footer { border-radius: 0; } .modal-fullscreen-xxl-down .modal-body { @@ -5568,15 +5762,14 @@ textarea.form-control-lg { --bs-tooltip-padding-y: 0.25rem; --bs-tooltip-margin: ; --bs-tooltip-font-size: 0.875rem; - --bs-tooltip-color: #fff; - --bs-tooltip-bg: #000; - --bs-tooltip-border-radius: 0.375rem; + --bs-tooltip-color: var(--bs-body-bg); + --bs-tooltip-bg: var(--bs-emphasis-color); + --bs-tooltip-border-radius: var(--bs-border-radius); --bs-tooltip-opacity: 0.9; --bs-tooltip-arrow-width: 0.8rem; --bs-tooltip-arrow-height: 0.4rem; z-index: var(--bs-tooltip-zindex); display: block; - padding: var(--bs-tooltip-arrow-height); margin: var(--bs-tooltip-margin); font-family: var(--bs-font-sans-serif); font-style: normal; @@ -5612,7 +5805,7 @@ textarea.form-control-lg { } .bs-tooltip-top .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow { - bottom: 0; + bottom: calc(-1 * var(--bs-tooltip-arrow-height)); } .bs-tooltip-top .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=top] .tooltip-arrow::before { top: -1px; @@ -5622,7 +5815,7 @@ textarea.form-control-lg { /* rtl:begin:ignore */ .bs-tooltip-end .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=right] .tooltip-arrow { - left: 0; + left: calc(-1 * var(--bs-tooltip-arrow-height)); width: var(--bs-tooltip-arrow-height); height: var(--bs-tooltip-arrow-width); } @@ -5634,7 +5827,7 @@ textarea.form-control-lg { /* rtl:end:ignore */ .bs-tooltip-bottom .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow { - top: 0; + top: calc(-1 * var(--bs-tooltip-arrow-height)); } .bs-tooltip-bottom .tooltip-arrow::before, .bs-tooltip-auto[data-popper-placement^=bottom] .tooltip-arrow::before { bottom: -1px; @@ -5644,7 +5837,7 @@ textarea.form-control-lg { /* rtl:begin:ignore */ .bs-tooltip-start .tooltip-arrow, .bs-tooltip-auto[data-popper-placement^=left] .tooltip-arrow { - right: 0; + right: calc(-1 * var(--bs-tooltip-arrow-height)); width: var(--bs-tooltip-arrow-height); height: var(--bs-tooltip-arrow-width); } @@ -5668,20 +5861,20 @@ textarea.form-control-lg { --bs-popover-zindex: 1070; --bs-popover-max-width: 276px; --bs-popover-font-size: 0.875rem; - --bs-popover-bg: #fff; - --bs-popover-border-width: 1px; + --bs-popover-bg: var(--bs-body-bg); + --bs-popover-border-width: var(--bs-border-width); --bs-popover-border-color: var(--bs-border-color-translucent); - --bs-popover-border-radius: 0.5rem; - --bs-popover-inner-border-radius: calc(0.5rem - 1px); + --bs-popover-border-radius: var(--bs-border-radius-lg); + --bs-popover-inner-border-radius: calc(var(--bs-border-radius-lg) - var(--bs-border-width)); --bs-popover-box-shadow: 0 0.5rem 1rem rgba(0, 0, 0, 0.15); --bs-popover-header-padding-x: 1rem; --bs-popover-header-padding-y: 0.5rem; --bs-popover-header-font-size: 1rem; - --bs-popover-header-color: ; - --bs-popover-header-bg: #f0f0f0; + --bs-popover-header-color: inherit; + --bs-popover-header-bg: var(--bs-secondary-bg); --bs-popover-body-padding-x: 1rem; --bs-popover-body-padding-y: 1rem; - --bs-popover-body-color: #212529; + --bs-popover-body-color: var(--bs-body-color); --bs-popover-arrow-width: 1rem; --bs-popover-arrow-height: 0.5rem; --bs-popover-arrow-border: var(--bs-popover-border-color); @@ -5890,7 +6083,7 @@ textarea.form-control-lg { } @media (prefers-reduced-motion: reduce) { .carousel-fade .active.carousel-item-start, -.carousel-fade .active.carousel-item-end { + .carousel-fade .active.carousel-item-end { transition: none; } } @@ -5915,7 +6108,7 @@ textarea.form-control-lg { } @media (prefers-reduced-motion: reduce) { .carousel-control-prev, -.carousel-control-next { + .carousel-control-next { transition: none; } } @@ -5974,7 +6167,6 @@ textarea.form-control-lg { margin-right: 15%; margin-bottom: 1rem; margin-left: 15%; - list-style: none; } .carousel-indicators [data-bs-target] { box-sizing: content-box; @@ -6025,6 +6217,18 @@ textarea.form-control-lg { color: #000; } +[data-bs-theme=dark] .carousel .carousel-control-prev-icon, +[data-bs-theme=dark] .carousel .carousel-control-next-icon, [data-bs-theme=dark].carousel .carousel-control-prev-icon, +[data-bs-theme=dark].carousel .carousel-control-next-icon { + filter: invert(1) grayscale(100); +} +[data-bs-theme=dark] .carousel .carousel-indicators [data-bs-target], [data-bs-theme=dark].carousel .carousel-indicators [data-bs-target] { + background-color: #000; +} +[data-bs-theme=dark] .carousel .carousel-caption, [data-bs-theme=dark].carousel .carousel-caption { + color: #000; +} + .spinner-grow, .spinner-border { display: inline-block; @@ -6083,7 +6287,7 @@ textarea.form-control-lg { @media (prefers-reduced-motion: reduce) { .spinner-border, -.spinner-grow { + .spinner-grow { --bs-spinner-animation-speed: 1.5s; } } @@ -6093,11 +6297,13 @@ textarea.form-control-lg { --bs-offcanvas-height: 30vh; --bs-offcanvas-padding-x: 1rem; --bs-offcanvas-padding-y: 1rem; - --bs-offcanvas-color: ; - --bs-offcanvas-bg: #fff; - --bs-offcanvas-border-width: 1px; + --bs-offcanvas-color: var(--bs-body-color); + --bs-offcanvas-bg: var(--bs-body-bg); + --bs-offcanvas-border-width: var(--bs-border-width); --bs-offcanvas-border-color: var(--bs-border-color-translucent); --bs-offcanvas-box-shadow: 0 0.125rem 0.25rem rgba(0, 0, 0, 0.075); + --bs-offcanvas-transition: transform 0.3s ease-in-out; + --bs-offcanvas-title-line-height: 1.5; } @media (max-width: 575.98px) { @@ -6113,7 +6319,7 @@ textarea.form-control-lg { background-color: var(--bs-offcanvas-bg); background-clip: padding-box; outline: 0; - transition: transform 0.3s ease-in-out; + transition: var(--bs-offcanvas-transition); } } @media (max-width: 575.98px) and (prefers-reduced-motion: reduce) { @@ -6129,8 +6335,6 @@ textarea.form-control-lg { border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(-100%); } -} -@media (max-width: 575.98px) { .offcanvas-sm.offcanvas-end { top: 0; right: 0; @@ -6138,8 +6342,6 @@ textarea.form-control-lg { border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(100%); } -} -@media (max-width: 575.98px) { .offcanvas-sm.offcanvas-top { top: 0; right: 0; @@ -6149,8 +6351,6 @@ textarea.form-control-lg { border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(-100%); } -} -@media (max-width: 575.98px) { .offcanvas-sm.offcanvas-bottom { right: 0; left: 0; @@ -6159,13 +6359,9 @@ textarea.form-control-lg { border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(100%); } -} -@media (max-width: 575.98px) { .offcanvas-sm.showing, .offcanvas-sm.show:not(.hiding) { transform: none; } -} -@media (max-width: 575.98px) { .offcanvas-sm.showing, .offcanvas-sm.hiding, .offcanvas-sm.show { visibility: visible; } @@ -6201,7 +6397,7 @@ textarea.form-control-lg { background-color: var(--bs-offcanvas-bg); background-clip: padding-box; outline: 0; - transition: transform 0.3s ease-in-out; + transition: var(--bs-offcanvas-transition); } } @media (max-width: 767.98px) and (prefers-reduced-motion: reduce) { @@ -6217,8 +6413,6 @@ textarea.form-control-lg { border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(-100%); } -} -@media (max-width: 767.98px) { .offcanvas-md.offcanvas-end { top: 0; right: 0; @@ -6226,8 +6420,6 @@ textarea.form-control-lg { border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(100%); } -} -@media (max-width: 767.98px) { .offcanvas-md.offcanvas-top { top: 0; right: 0; @@ -6237,8 +6429,6 @@ textarea.form-control-lg { border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(-100%); } -} -@media (max-width: 767.98px) { .offcanvas-md.offcanvas-bottom { right: 0; left: 0; @@ -6247,13 +6437,9 @@ textarea.form-control-lg { border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(100%); } -} -@media (max-width: 767.98px) { .offcanvas-md.showing, .offcanvas-md.show:not(.hiding) { transform: none; } -} -@media (max-width: 767.98px) { .offcanvas-md.showing, .offcanvas-md.hiding, .offcanvas-md.show { visibility: visible; } @@ -6289,7 +6475,7 @@ textarea.form-control-lg { background-color: var(--bs-offcanvas-bg); background-clip: padding-box; outline: 0; - transition: transform 0.3s ease-in-out; + transition: var(--bs-offcanvas-transition); } } @media (max-width: 991.98px) and (prefers-reduced-motion: reduce) { @@ -6305,8 +6491,6 @@ textarea.form-control-lg { border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(-100%); } -} -@media (max-width: 991.98px) { .offcanvas-lg.offcanvas-end { top: 0; right: 0; @@ -6314,8 +6498,6 @@ textarea.form-control-lg { border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(100%); } -} -@media (max-width: 991.98px) { .offcanvas-lg.offcanvas-top { top: 0; right: 0; @@ -6325,8 +6507,6 @@ textarea.form-control-lg { border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(-100%); } -} -@media (max-width: 991.98px) { .offcanvas-lg.offcanvas-bottom { right: 0; left: 0; @@ -6335,13 +6515,9 @@ textarea.form-control-lg { border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(100%); } -} -@media (max-width: 991.98px) { .offcanvas-lg.showing, .offcanvas-lg.show:not(.hiding) { transform: none; } -} -@media (max-width: 991.98px) { .offcanvas-lg.showing, .offcanvas-lg.hiding, .offcanvas-lg.show { visibility: visible; } @@ -6377,7 +6553,7 @@ textarea.form-control-lg { background-color: var(--bs-offcanvas-bg); background-clip: padding-box; outline: 0; - transition: transform 0.3s ease-in-out; + transition: var(--bs-offcanvas-transition); } } @media (max-width: 1199.98px) and (prefers-reduced-motion: reduce) { @@ -6393,8 +6569,6 @@ textarea.form-control-lg { border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(-100%); } -} -@media (max-width: 1199.98px) { .offcanvas-xl.offcanvas-end { top: 0; right: 0; @@ -6402,8 +6576,6 @@ textarea.form-control-lg { border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(100%); } -} -@media (max-width: 1199.98px) { .offcanvas-xl.offcanvas-top { top: 0; right: 0; @@ -6413,8 +6585,6 @@ textarea.form-control-lg { border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(-100%); } -} -@media (max-width: 1199.98px) { .offcanvas-xl.offcanvas-bottom { right: 0; left: 0; @@ -6423,13 +6593,9 @@ textarea.form-control-lg { border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(100%); } -} -@media (max-width: 1199.98px) { .offcanvas-xl.showing, .offcanvas-xl.show:not(.hiding) { transform: none; } -} -@media (max-width: 1199.98px) { .offcanvas-xl.showing, .offcanvas-xl.hiding, .offcanvas-xl.show { visibility: visible; } @@ -6465,7 +6631,7 @@ textarea.form-control-lg { background-color: var(--bs-offcanvas-bg); background-clip: padding-box; outline: 0; - transition: transform 0.3s ease-in-out; + transition: var(--bs-offcanvas-transition); } } @media (max-width: 1399.98px) and (prefers-reduced-motion: reduce) { @@ -6481,8 +6647,6 @@ textarea.form-control-lg { border-right: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(-100%); } -} -@media (max-width: 1399.98px) { .offcanvas-xxl.offcanvas-end { top: 0; right: 0; @@ -6490,8 +6654,6 @@ textarea.form-control-lg { border-left: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateX(100%); } -} -@media (max-width: 1399.98px) { .offcanvas-xxl.offcanvas-top { top: 0; right: 0; @@ -6501,8 +6663,6 @@ textarea.form-control-lg { border-bottom: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(-100%); } -} -@media (max-width: 1399.98px) { .offcanvas-xxl.offcanvas-bottom { right: 0; left: 0; @@ -6511,13 +6671,9 @@ textarea.form-control-lg { border-top: var(--bs-offcanvas-border-width) solid var(--bs-offcanvas-border-color); transform: translateY(100%); } -} -@media (max-width: 1399.98px) { .offcanvas-xxl.showing, .offcanvas-xxl.show:not(.hiding) { transform: none; } -} -@media (max-width: 1399.98px) { .offcanvas-xxl.showing, .offcanvas-xxl.hiding, .offcanvas-xxl.show { visibility: visible; } @@ -6552,7 +6708,7 @@ textarea.form-control-lg { background-color: var(--bs-offcanvas-bg); background-clip: padding-box; outline: 0; - transition: transform 0.3s ease-in-out; + transition: var(--bs-offcanvas-transition); } @media (prefers-reduced-motion: reduce) { .offcanvas { @@ -6628,7 +6784,7 @@ textarea.form-control-lg { .offcanvas-title { margin-bottom: 0; - line-height: 1.5; + line-height: var(--bs-offcanvas-title-line-height); } .offcanvas-body { @@ -6693,98 +6849,173 @@ textarea.form-control-lg { .text-bg-primary { color: #fff !important; - background-color: RGBA(13, 110, 253, var(--bs-bg-opacity, 1)) !important; + background-color: RGBA(var(--bs-primary-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-secondary { color: #fff !important; - background-color: RGBA(108, 117, 125, var(--bs-bg-opacity, 1)) !important; + background-color: RGBA(var(--bs-secondary-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-success { color: #fff !important; - background-color: RGBA(25, 135, 84, var(--bs-bg-opacity, 1)) !important; + background-color: RGBA(var(--bs-success-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-info { color: #000 !important; - background-color: RGBA(13, 202, 240, var(--bs-bg-opacity, 1)) !important; + background-color: RGBA(var(--bs-info-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-warning { color: #000 !important; - background-color: RGBA(255, 193, 7, var(--bs-bg-opacity, 1)) !important; + background-color: RGBA(var(--bs-warning-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-danger { color: #fff !important; - background-color: RGBA(220, 53, 69, var(--bs-bg-opacity, 1)) !important; + background-color: RGBA(var(--bs-danger-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-light { color: #000 !important; - background-color: RGBA(248, 249, 250, var(--bs-bg-opacity, 1)) !important; + background-color: RGBA(var(--bs-light-rgb), var(--bs-bg-opacity, 1)) !important; } .text-bg-dark { color: #fff !important; - background-color: RGBA(33, 37, 41, var(--bs-bg-opacity, 1)) !important; + background-color: RGBA(var(--bs-dark-rgb), var(--bs-bg-opacity, 1)) !important; } .link-primary { - color: #0d6efd !important; + color: RGBA(var(--bs-primary-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-primary-rgb), var(--bs-link-underline-opacity, 1)) !important; } .link-primary:hover, .link-primary:focus { - color: #0a58ca !important; + color: RGBA(10, 88, 202, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(10, 88, 202, var(--bs-link-underline-opacity, 1)) !important; } .link-secondary { - color: #6c757d !important; + color: RGBA(var(--bs-secondary-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-secondary-rgb), var(--bs-link-underline-opacity, 1)) !important; } .link-secondary:hover, .link-secondary:focus { - color: #565e64 !important; + color: RGBA(86, 94, 100, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(86, 94, 100, var(--bs-link-underline-opacity, 1)) !important; } .link-success { - color: #198754 !important; + color: RGBA(var(--bs-success-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-success-rgb), var(--bs-link-underline-opacity, 1)) !important; } .link-success:hover, .link-success:focus { - color: #146c43 !important; + color: RGBA(20, 108, 67, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(20, 108, 67, var(--bs-link-underline-opacity, 1)) !important; } .link-info { - color: #0dcaf0 !important; + color: RGBA(var(--bs-info-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-info-rgb), var(--bs-link-underline-opacity, 1)) !important; } .link-info:hover, .link-info:focus { - color: #3dd5f3 !important; + color: RGBA(61, 213, 243, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(61, 213, 243, var(--bs-link-underline-opacity, 1)) !important; } .link-warning { - color: #ffc107 !important; + color: RGBA(var(--bs-warning-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-warning-rgb), var(--bs-link-underline-opacity, 1)) !important; } .link-warning:hover, .link-warning:focus { - color: #ffcd39 !important; + color: RGBA(255, 205, 57, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(255, 205, 57, var(--bs-link-underline-opacity, 1)) !important; } .link-danger { - color: #dc3545 !important; + color: RGBA(var(--bs-danger-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-danger-rgb), var(--bs-link-underline-opacity, 1)) !important; } .link-danger:hover, .link-danger:focus { - color: #b02a37 !important; + color: RGBA(176, 42, 55, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(176, 42, 55, var(--bs-link-underline-opacity, 1)) !important; } .link-light { - color: #f8f9fa !important; + color: RGBA(var(--bs-light-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-light-rgb), var(--bs-link-underline-opacity, 1)) !important; } .link-light:hover, .link-light:focus { - color: #f9fafb !important; + color: RGBA(249, 250, 251, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(249, 250, 251, var(--bs-link-underline-opacity, 1)) !important; } .link-dark { - color: #212529 !important; + color: RGBA(var(--bs-dark-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-dark-rgb), var(--bs-link-underline-opacity, 1)) !important; } .link-dark:hover, .link-dark:focus { - color: #1a1e21 !important; + color: RGBA(26, 30, 33, var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(26, 30, 33, var(--bs-link-underline-opacity, 1)) !important; +} + +.link-body-emphasis { + color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 1)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 1)) !important; +} +.link-body-emphasis:hover, .link-body-emphasis:focus { + color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-opacity, 0.75)) !important; + -webkit-text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important; + text-decoration-color: RGBA(var(--bs-emphasis-color-rgb), var(--bs-link-underline-opacity, 0.75)) !important; +} + +.focus-ring:focus { + outline: 0; + box-shadow: var(--bs-focus-ring-x, 0) var(--bs-focus-ring-y, 0) var(--bs-focus-ring-blur, 0) var(--bs-focus-ring-width) var(--bs-focus-ring-color); +} + +.icon-link { + display: inline-flex; + gap: 0.375rem; + align-items: center; + -webkit-text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5)); + text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-opacity, 0.5)); + text-underline-offset: 0.25em; + -webkit-backface-visibility: hidden; + backface-visibility: hidden; +} +.icon-link > .bi { + flex-shrink: 0; + width: 1em; + height: 1em; + fill: currentcolor; + transition: 0.2s ease-in-out transform; +} +@media (prefers-reduced-motion: reduce) { + .icon-link > .bi { + transition: none; + } +} + +.icon-link-hover:hover > .bi, .icon-link-hover:focus-visible > .bi { + transform: var(--bs-icon-link-transform, translate3d(0.25em, 0, 0)); } .ratio { @@ -6936,7 +7167,6 @@ textarea.form-control-lg { .visually-hidden, .visually-hidden-focusable:not(:focus):not(:focus-within) { - position: absolute !important; width: 1px !important; height: 1px !important; padding: 0 !important; @@ -6946,6 +7176,10 @@ textarea.form-control-lg { white-space: nowrap !important; border: 0 !important; } +.visually-hidden:not(caption), +.visually-hidden-focusable:not(:focus):not(:focus-within):not(caption) { + position: absolute !important; +} .stretched-link::after { position: absolute; @@ -6966,7 +7200,7 @@ textarea.form-control-lg { .vr { display: inline-block; align-self: stretch; - width: 1px; + width: var(--bs-border-width); min-height: 1em; background-color: currentcolor; opacity: 0.25; @@ -7008,6 +7242,31 @@ textarea.form-control-lg { float: none !important; } +.object-fit-contain { + -o-object-fit: contain !important; + object-fit: contain !important; +} + +.object-fit-cover { + -o-object-fit: cover !important; + object-fit: cover !important; +} + +.object-fit-fill { + -o-object-fit: fill !important; + object-fit: fill !important; +} + +.object-fit-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; +} + +.object-fit-none { + -o-object-fit: none !important; + object-fit: none !important; +} + .opacity-0 { opacity: 0 !important; } @@ -7044,6 +7303,38 @@ textarea.form-control-lg { overflow: scroll !important; } +.overflow-x-auto { + overflow-x: auto !important; +} + +.overflow-x-hidden { + overflow-x: hidden !important; +} + +.overflow-x-visible { + overflow-x: visible !important; +} + +.overflow-x-scroll { + overflow-x: scroll !important; +} + +.overflow-y-auto { + overflow-y: auto !important; +} + +.overflow-y-hidden { + overflow-y: hidden !important; +} + +.overflow-y-visible { + overflow-y: visible !important; +} + +.overflow-y-scroll { + overflow-y: scroll !important; +} + .d-inline { display: inline !important; } @@ -7060,6 +7351,10 @@ textarea.form-control-lg { display: grid !important; } +.d-inline-grid { + display: inline-grid !important; +} + .d-table { display: table !important; } @@ -7100,6 +7395,38 @@ textarea.form-control-lg { box-shadow: none !important; } +.focus-ring-primary { + --bs-focus-ring-color: rgba(var(--bs-primary-rgb), var(--bs-focus-ring-opacity)); +} + +.focus-ring-secondary { + --bs-focus-ring-color: rgba(var(--bs-secondary-rgb), var(--bs-focus-ring-opacity)); +} + +.focus-ring-success { + --bs-focus-ring-color: rgba(var(--bs-success-rgb), var(--bs-focus-ring-opacity)); +} + +.focus-ring-info { + --bs-focus-ring-color: rgba(var(--bs-info-rgb), var(--bs-focus-ring-opacity)); +} + +.focus-ring-warning { + --bs-focus-ring-color: rgba(var(--bs-warning-rgb), var(--bs-focus-ring-opacity)); +} + +.focus-ring-danger { + --bs-focus-ring-color: rgba(var(--bs-danger-rgb), var(--bs-focus-ring-opacity)); +} + +.focus-ring-light { + --bs-focus-ring-color: rgba(var(--bs-light-rgb), var(--bs-focus-ring-opacity)); +} + +.focus-ring-dark { + --bs-focus-ring-color: rgba(var(--bs-dark-rgb), var(--bs-focus-ring-opacity)); +} + .position-static { position: static !important; } @@ -7261,29 +7588,66 @@ textarea.form-control-lg { border-color: rgba(var(--bs-dark-rgb), var(--bs-border-opacity)) !important; } +.border-black { + --bs-border-opacity: 1; + border-color: rgba(var(--bs-black-rgb), var(--bs-border-opacity)) !important; +} + .border-white { --bs-border-opacity: 1; border-color: rgba(var(--bs-white-rgb), var(--bs-border-opacity)) !important; } +.border-primary-subtle { + border-color: var(--bs-primary-border-subtle) !important; +} + +.border-secondary-subtle { + border-color: var(--bs-secondary-border-subtle) !important; +} + +.border-success-subtle { + border-color: var(--bs-success-border-subtle) !important; +} + +.border-info-subtle { + border-color: var(--bs-info-border-subtle) !important; +} + +.border-warning-subtle { + border-color: var(--bs-warning-border-subtle) !important; +} + +.border-danger-subtle { + border-color: var(--bs-danger-border-subtle) !important; +} + +.border-light-subtle { + border-color: var(--bs-light-border-subtle) !important; +} + +.border-dark-subtle { + border-color: var(--bs-dark-border-subtle) !important; +} + .border-1 { - --bs-border-width: 1px; + border-width: 1px !important; } .border-2 { - --bs-border-width: 2px; + border-width: 2px !important; } .border-3 { - --bs-border-width: 3px; + border-width: 3px !important; } .border-4 { - --bs-border-width: 4px; + border-width: 4px !important; } .border-5 { - --bs-border-width: 5px; + border-width: 5px !important; } .border-opacity-10 { @@ -7956,6 +8320,60 @@ textarea.form-control-lg { gap: 3rem !important; } +.row-gap-0 { + row-gap: 0 !important; +} + +.row-gap-1 { + row-gap: 0.25rem !important; +} + +.row-gap-2 { + row-gap: 0.5rem !important; +} + +.row-gap-3 { + row-gap: 1rem !important; +} + +.row-gap-4 { + row-gap: 1.5rem !important; +} + +.row-gap-5 { + row-gap: 3rem !important; +} + +.column-gap-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; +} + +.column-gap-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; +} + +.column-gap-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; +} + +.column-gap-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; +} + +.column-gap-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; +} + +.column-gap-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; +} + .font-monospace { font-family: var(--bs-font-monospace) !important; } @@ -7992,26 +8410,30 @@ textarea.form-control-lg { font-style: normal !important; } -.fw-light { - font-weight: 300 !important; -} - .fw-lighter { font-weight: lighter !important; } +.fw-light { + font-weight: 300 !important; +} + .fw-normal { font-weight: 400 !important; } -.fw-bold { - font-weight: 700 !important; +.fw-medium { + font-weight: 500 !important; } .fw-semibold { font-weight: 600 !important; } +.fw-bold { + font-weight: 700 !important; +} + .fw-bolder { font-weight: bolder !important; } @@ -8140,7 +8562,7 @@ textarea.form-control-lg { .text-muted { --bs-text-opacity: 1; - color: #6c757d !important; + color: var(--bs-secondary-color) !important; } .text-black-50 { @@ -8153,6 +8575,21 @@ textarea.form-control-lg { color: rgba(255, 255, 255, 0.5) !important; } +.text-body-secondary { + --bs-text-opacity: 1; + color: var(--bs-secondary-color) !important; +} + +.text-body-tertiary { + --bs-text-opacity: 1; + color: var(--bs-tertiary-color) !important; +} + +.text-body-emphasis { + --bs-text-opacity: 1; + color: var(--bs-emphasis-color) !important; +} + .text-reset { --bs-text-opacity: 1; color: inherit !important; @@ -8174,6 +8611,204 @@ textarea.form-control-lg { --bs-text-opacity: 1; } +.text-primary-emphasis { + color: var(--bs-primary-text-emphasis) !important; +} + +.text-secondary-emphasis { + color: var(--bs-secondary-text-emphasis) !important; +} + +.text-success-emphasis { + color: var(--bs-success-text-emphasis) !important; +} + +.text-info-emphasis { + color: var(--bs-info-text-emphasis) !important; +} + +.text-warning-emphasis { + color: var(--bs-warning-text-emphasis) !important; +} + +.text-danger-emphasis { + color: var(--bs-danger-text-emphasis) !important; +} + +.text-light-emphasis { + color: var(--bs-light-text-emphasis) !important; +} + +.text-dark-emphasis { + color: var(--bs-dark-text-emphasis) !important; +} + +.link-opacity-10 { + --bs-link-opacity: 0.1; +} + +.link-opacity-10-hover:hover { + --bs-link-opacity: 0.1; +} + +.link-opacity-25 { + --bs-link-opacity: 0.25; +} + +.link-opacity-25-hover:hover { + --bs-link-opacity: 0.25; +} + +.link-opacity-50 { + --bs-link-opacity: 0.5; +} + +.link-opacity-50-hover:hover { + --bs-link-opacity: 0.5; +} + +.link-opacity-75 { + --bs-link-opacity: 0.75; +} + +.link-opacity-75-hover:hover { + --bs-link-opacity: 0.75; +} + +.link-opacity-100 { + --bs-link-opacity: 1; +} + +.link-opacity-100-hover:hover { + --bs-link-opacity: 1; +} + +.link-offset-1 { + text-underline-offset: 0.125em !important; +} + +.link-offset-1-hover:hover { + text-underline-offset: 0.125em !important; +} + +.link-offset-2 { + text-underline-offset: 0.25em !important; +} + +.link-offset-2-hover:hover { + text-underline-offset: 0.25em !important; +} + +.link-offset-3 { + text-underline-offset: 0.375em !important; +} + +.link-offset-3-hover:hover { + text-underline-offset: 0.375em !important; +} + +.link-underline-primary { + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-primary-rgb), var(--bs-link-underline-opacity)) !important; +} + +.link-underline-secondary { + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-secondary-rgb), var(--bs-link-underline-opacity)) !important; +} + +.link-underline-success { + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-success-rgb), var(--bs-link-underline-opacity)) !important; +} + +.link-underline-info { + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-info-rgb), var(--bs-link-underline-opacity)) !important; +} + +.link-underline-warning { + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-warning-rgb), var(--bs-link-underline-opacity)) !important; +} + +.link-underline-danger { + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-danger-rgb), var(--bs-link-underline-opacity)) !important; +} + +.link-underline-light { + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-light-rgb), var(--bs-link-underline-opacity)) !important; +} + +.link-underline-dark { + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important; + text-decoration-color: rgba(var(--bs-dark-rgb), var(--bs-link-underline-opacity)) !important; +} + +.link-underline { + --bs-link-underline-opacity: 1; + -webkit-text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important; + text-decoration-color: rgba(var(--bs-link-color-rgb), var(--bs-link-underline-opacity, 1)) !important; +} + +.link-underline-opacity-0 { + --bs-link-underline-opacity: 0; +} + +.link-underline-opacity-0-hover:hover { + --bs-link-underline-opacity: 0; +} + +.link-underline-opacity-10 { + --bs-link-underline-opacity: 0.1; +} + +.link-underline-opacity-10-hover:hover { + --bs-link-underline-opacity: 0.1; +} + +.link-underline-opacity-25 { + --bs-link-underline-opacity: 0.25; +} + +.link-underline-opacity-25-hover:hover { + --bs-link-underline-opacity: 0.25; +} + +.link-underline-opacity-50 { + --bs-link-underline-opacity: 0.5; +} + +.link-underline-opacity-50-hover:hover { + --bs-link-underline-opacity: 0.5; +} + +.link-underline-opacity-75 { + --bs-link-underline-opacity: 0.75; +} + +.link-underline-opacity-75-hover:hover { + --bs-link-underline-opacity: 0.75; +} + +.link-underline-opacity-100 { + --bs-link-underline-opacity: 1; +} + +.link-underline-opacity-100-hover:hover { + --bs-link-underline-opacity: 1; +} + .bg-primary { --bs-bg-opacity: 1; background-color: rgba(var(--bs-primary-rgb), var(--bs-bg-opacity)) !important; @@ -8234,6 +8869,16 @@ textarea.form-control-lg { background-color: transparent !important; } +.bg-body-secondary { + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-secondary-bg-rgb), var(--bs-bg-opacity)) !important; +} + +.bg-body-tertiary { + --bs-bg-opacity: 1; + background-color: rgba(var(--bs-tertiary-bg-rgb), var(--bs-bg-opacity)) !important; +} + .bg-opacity-10 { --bs-bg-opacity: 0.1; } @@ -8254,6 +8899,38 @@ textarea.form-control-lg { --bs-bg-opacity: 1; } +.bg-primary-subtle { + background-color: var(--bs-primary-bg-subtle) !important; +} + +.bg-secondary-subtle { + background-color: var(--bs-secondary-bg-subtle) !important; +} + +.bg-success-subtle { + background-color: var(--bs-success-bg-subtle) !important; +} + +.bg-info-subtle { + background-color: var(--bs-info-bg-subtle) !important; +} + +.bg-warning-subtle { + background-color: var(--bs-warning-bg-subtle) !important; +} + +.bg-danger-subtle { + background-color: var(--bs-danger-bg-subtle) !important; +} + +.bg-light-subtle { + background-color: var(--bs-light-bg-subtle) !important; +} + +.bg-dark-subtle { + background-color: var(--bs-dark-bg-subtle) !important; +} + .bg-gradient { background-image: var(--bs-gradient) !important; } @@ -8309,7 +8986,7 @@ textarea.form-control-lg { } .rounded-5 { - border-radius: var(--bs-border-radius-2xl) !important; + border-radius: var(--bs-border-radius-xxl) !important; } .rounded-circle { @@ -8325,21 +9002,181 @@ textarea.form-control-lg { border-top-right-radius: var(--bs-border-radius) !important; } +.rounded-top-0 { + border-top-left-radius: 0 !important; + border-top-right-radius: 0 !important; +} + +.rounded-top-1 { + border-top-left-radius: var(--bs-border-radius-sm) !important; + border-top-right-radius: var(--bs-border-radius-sm) !important; +} + +.rounded-top-2 { + border-top-left-radius: var(--bs-border-radius) !important; + border-top-right-radius: var(--bs-border-radius) !important; +} + +.rounded-top-3 { + border-top-left-radius: var(--bs-border-radius-lg) !important; + border-top-right-radius: var(--bs-border-radius-lg) !important; +} + +.rounded-top-4 { + border-top-left-radius: var(--bs-border-radius-xl) !important; + border-top-right-radius: var(--bs-border-radius-xl) !important; +} + +.rounded-top-5 { + border-top-left-radius: var(--bs-border-radius-xxl) !important; + border-top-right-radius: var(--bs-border-radius-xxl) !important; +} + +.rounded-top-circle { + border-top-left-radius: 50% !important; + border-top-right-radius: 50% !important; +} + +.rounded-top-pill { + border-top-left-radius: var(--bs-border-radius-pill) !important; + border-top-right-radius: var(--bs-border-radius-pill) !important; +} + .rounded-end { border-top-right-radius: var(--bs-border-radius) !important; border-bottom-right-radius: var(--bs-border-radius) !important; } +.rounded-end-0 { + border-top-right-radius: 0 !important; + border-bottom-right-radius: 0 !important; +} + +.rounded-end-1 { + border-top-right-radius: var(--bs-border-radius-sm) !important; + border-bottom-right-radius: var(--bs-border-radius-sm) !important; +} + +.rounded-end-2 { + border-top-right-radius: var(--bs-border-radius) !important; + border-bottom-right-radius: var(--bs-border-radius) !important; +} + +.rounded-end-3 { + border-top-right-radius: var(--bs-border-radius-lg) !important; + border-bottom-right-radius: var(--bs-border-radius-lg) !important; +} + +.rounded-end-4 { + border-top-right-radius: var(--bs-border-radius-xl) !important; + border-bottom-right-radius: var(--bs-border-radius-xl) !important; +} + +.rounded-end-5 { + border-top-right-radius: var(--bs-border-radius-xxl) !important; + border-bottom-right-radius: var(--bs-border-radius-xxl) !important; +} + +.rounded-end-circle { + border-top-right-radius: 50% !important; + border-bottom-right-radius: 50% !important; +} + +.rounded-end-pill { + border-top-right-radius: var(--bs-border-radius-pill) !important; + border-bottom-right-radius: var(--bs-border-radius-pill) !important; +} + .rounded-bottom { border-bottom-right-radius: var(--bs-border-radius) !important; border-bottom-left-radius: var(--bs-border-radius) !important; } +.rounded-bottom-0 { + border-bottom-right-radius: 0 !important; + border-bottom-left-radius: 0 !important; +} + +.rounded-bottom-1 { + border-bottom-right-radius: var(--bs-border-radius-sm) !important; + border-bottom-left-radius: var(--bs-border-radius-sm) !important; +} + +.rounded-bottom-2 { + border-bottom-right-radius: var(--bs-border-radius) !important; + border-bottom-left-radius: var(--bs-border-radius) !important; +} + +.rounded-bottom-3 { + border-bottom-right-radius: var(--bs-border-radius-lg) !important; + border-bottom-left-radius: var(--bs-border-radius-lg) !important; +} + +.rounded-bottom-4 { + border-bottom-right-radius: var(--bs-border-radius-xl) !important; + border-bottom-left-radius: var(--bs-border-radius-xl) !important; +} + +.rounded-bottom-5 { + border-bottom-right-radius: var(--bs-border-radius-xxl) !important; + border-bottom-left-radius: var(--bs-border-radius-xxl) !important; +} + +.rounded-bottom-circle { + border-bottom-right-radius: 50% !important; + border-bottom-left-radius: 50% !important; +} + +.rounded-bottom-pill { + border-bottom-right-radius: var(--bs-border-radius-pill) !important; + border-bottom-left-radius: var(--bs-border-radius-pill) !important; +} + .rounded-start { border-bottom-left-radius: var(--bs-border-radius) !important; border-top-left-radius: var(--bs-border-radius) !important; } +.rounded-start-0 { + border-bottom-left-radius: 0 !important; + border-top-left-radius: 0 !important; +} + +.rounded-start-1 { + border-bottom-left-radius: var(--bs-border-radius-sm) !important; + border-top-left-radius: var(--bs-border-radius-sm) !important; +} + +.rounded-start-2 { + border-bottom-left-radius: var(--bs-border-radius) !important; + border-top-left-radius: var(--bs-border-radius) !important; +} + +.rounded-start-3 { + border-bottom-left-radius: var(--bs-border-radius-lg) !important; + border-top-left-radius: var(--bs-border-radius-lg) !important; +} + +.rounded-start-4 { + border-bottom-left-radius: var(--bs-border-radius-xl) !important; + border-top-left-radius: var(--bs-border-radius-xl) !important; +} + +.rounded-start-5 { + border-bottom-left-radius: var(--bs-border-radius-xxl) !important; + border-top-left-radius: var(--bs-border-radius-xxl) !important; +} + +.rounded-start-circle { + border-bottom-left-radius: 50% !important; + border-top-left-radius: 50% !important; +} + +.rounded-start-pill { + border-bottom-left-radius: var(--bs-border-radius-pill) !important; + border-top-left-radius: var(--bs-border-radius-pill) !important; +} + .visible { visibility: visible !important; } @@ -8348,6 +9185,26 @@ textarea.form-control-lg { visibility: hidden !important; } +.z-n1 { + z-index: -1 !important; +} + +.z-0 { + z-index: 0 !important; +} + +.z-1 { + z-index: 1 !important; +} + +.z-2 { + z-index: 2 !important; +} + +.z-3 { + z-index: 3 !important; +} + @media (min-width: 576px) { .float-sm-start { float: left !important; @@ -8358,6 +9215,26 @@ textarea.form-control-lg { .float-sm-none { float: none !important; } + .object-fit-sm-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + .object-fit-sm-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + .object-fit-sm-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + .object-fit-sm-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + .object-fit-sm-none { + -o-object-fit: none !important; + object-fit: none !important; + } .d-sm-inline { display: inline !important; } @@ -8370,6 +9247,9 @@ textarea.form-control-lg { .d-sm-grid { display: grid !important; } + .d-sm-inline-grid { + display: inline-grid !important; + } .d-sm-table { display: table !important; } @@ -8834,6 +9714,48 @@ textarea.form-control-lg { .gap-sm-5 { gap: 3rem !important; } + .row-gap-sm-0 { + row-gap: 0 !important; + } + .row-gap-sm-1 { + row-gap: 0.25rem !important; + } + .row-gap-sm-2 { + row-gap: 0.5rem !important; + } + .row-gap-sm-3 { + row-gap: 1rem !important; + } + .row-gap-sm-4 { + row-gap: 1.5rem !important; + } + .row-gap-sm-5 { + row-gap: 3rem !important; + } + .column-gap-sm-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + .column-gap-sm-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + .column-gap-sm-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + .column-gap-sm-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + .column-gap-sm-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + .column-gap-sm-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } .text-sm-start { text-align: left !important; } @@ -8854,6 +9776,26 @@ textarea.form-control-lg { .float-md-none { float: none !important; } + .object-fit-md-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + .object-fit-md-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + .object-fit-md-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + .object-fit-md-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + .object-fit-md-none { + -o-object-fit: none !important; + object-fit: none !important; + } .d-md-inline { display: inline !important; } @@ -8866,6 +9808,9 @@ textarea.form-control-lg { .d-md-grid { display: grid !important; } + .d-md-inline-grid { + display: inline-grid !important; + } .d-md-table { display: table !important; } @@ -9330,6 +10275,48 @@ textarea.form-control-lg { .gap-md-5 { gap: 3rem !important; } + .row-gap-md-0 { + row-gap: 0 !important; + } + .row-gap-md-1 { + row-gap: 0.25rem !important; + } + .row-gap-md-2 { + row-gap: 0.5rem !important; + } + .row-gap-md-3 { + row-gap: 1rem !important; + } + .row-gap-md-4 { + row-gap: 1.5rem !important; + } + .row-gap-md-5 { + row-gap: 3rem !important; + } + .column-gap-md-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + .column-gap-md-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + .column-gap-md-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + .column-gap-md-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + .column-gap-md-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + .column-gap-md-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } .text-md-start { text-align: left !important; } @@ -9350,6 +10337,26 @@ textarea.form-control-lg { .float-lg-none { float: none !important; } + .object-fit-lg-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + .object-fit-lg-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + .object-fit-lg-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + .object-fit-lg-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + .object-fit-lg-none { + -o-object-fit: none !important; + object-fit: none !important; + } .d-lg-inline { display: inline !important; } @@ -9362,6 +10369,9 @@ textarea.form-control-lg { .d-lg-grid { display: grid !important; } + .d-lg-inline-grid { + display: inline-grid !important; + } .d-lg-table { display: table !important; } @@ -9826,6 +10836,48 @@ textarea.form-control-lg { .gap-lg-5 { gap: 3rem !important; } + .row-gap-lg-0 { + row-gap: 0 !important; + } + .row-gap-lg-1 { + row-gap: 0.25rem !important; + } + .row-gap-lg-2 { + row-gap: 0.5rem !important; + } + .row-gap-lg-3 { + row-gap: 1rem !important; + } + .row-gap-lg-4 { + row-gap: 1.5rem !important; + } + .row-gap-lg-5 { + row-gap: 3rem !important; + } + .column-gap-lg-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + .column-gap-lg-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + .column-gap-lg-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + .column-gap-lg-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + .column-gap-lg-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + .column-gap-lg-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } .text-lg-start { text-align: left !important; } @@ -9846,6 +10898,26 @@ textarea.form-control-lg { .float-xl-none { float: none !important; } + .object-fit-xl-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + .object-fit-xl-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + .object-fit-xl-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + .object-fit-xl-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + .object-fit-xl-none { + -o-object-fit: none !important; + object-fit: none !important; + } .d-xl-inline { display: inline !important; } @@ -9858,6 +10930,9 @@ textarea.form-control-lg { .d-xl-grid { display: grid !important; } + .d-xl-inline-grid { + display: inline-grid !important; + } .d-xl-table { display: table !important; } @@ -10322,6 +11397,48 @@ textarea.form-control-lg { .gap-xl-5 { gap: 3rem !important; } + .row-gap-xl-0 { + row-gap: 0 !important; + } + .row-gap-xl-1 { + row-gap: 0.25rem !important; + } + .row-gap-xl-2 { + row-gap: 0.5rem !important; + } + .row-gap-xl-3 { + row-gap: 1rem !important; + } + .row-gap-xl-4 { + row-gap: 1.5rem !important; + } + .row-gap-xl-5 { + row-gap: 3rem !important; + } + .column-gap-xl-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + .column-gap-xl-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + .column-gap-xl-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + .column-gap-xl-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + .column-gap-xl-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + .column-gap-xl-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } .text-xl-start { text-align: left !important; } @@ -10342,6 +11459,26 @@ textarea.form-control-lg { .float-xxl-none { float: none !important; } + .object-fit-xxl-contain { + -o-object-fit: contain !important; + object-fit: contain !important; + } + .object-fit-xxl-cover { + -o-object-fit: cover !important; + object-fit: cover !important; + } + .object-fit-xxl-fill { + -o-object-fit: fill !important; + object-fit: fill !important; + } + .object-fit-xxl-scale { + -o-object-fit: scale-down !important; + object-fit: scale-down !important; + } + .object-fit-xxl-none { + -o-object-fit: none !important; + object-fit: none !important; + } .d-xxl-inline { display: inline !important; } @@ -10354,6 +11491,9 @@ textarea.form-control-lg { .d-xxl-grid { display: grid !important; } + .d-xxl-inline-grid { + display: inline-grid !important; + } .d-xxl-table { display: table !important; } @@ -10818,6 +11958,48 @@ textarea.form-control-lg { .gap-xxl-5 { gap: 3rem !important; } + .row-gap-xxl-0 { + row-gap: 0 !important; + } + .row-gap-xxl-1 { + row-gap: 0.25rem !important; + } + .row-gap-xxl-2 { + row-gap: 0.5rem !important; + } + .row-gap-xxl-3 { + row-gap: 1rem !important; + } + .row-gap-xxl-4 { + row-gap: 1.5rem !important; + } + .row-gap-xxl-5 { + row-gap: 3rem !important; + } + .column-gap-xxl-0 { + -moz-column-gap: 0 !important; + column-gap: 0 !important; + } + .column-gap-xxl-1 { + -moz-column-gap: 0.25rem !important; + column-gap: 0.25rem !important; + } + .column-gap-xxl-2 { + -moz-column-gap: 0.5rem !important; + column-gap: 0.5rem !important; + } + .column-gap-xxl-3 { + -moz-column-gap: 1rem !important; + column-gap: 1rem !important; + } + .column-gap-xxl-4 { + -moz-column-gap: 1.5rem !important; + column-gap: 1.5rem !important; + } + .column-gap-xxl-5 { + -moz-column-gap: 3rem !important; + column-gap: 3rem !important; + } .text-xxl-start { text-align: left !important; } @@ -10855,6 +12037,9 @@ textarea.form-control-lg { .d-print-grid { display: grid !important; } + .d-print-inline-grid { + display: inline-grid !important; + } .d-print-table { display: table !important; } diff --git a/src/static/scripts/datatables.css b/src/static/scripts/datatables.css index 0dd6669c..e3205479 100644 --- a/src/static/scripts/datatables.css +++ b/src/static/scripts/datatables.css @@ -4,10 +4,10 @@ * * To rebuild or modify this file with the latest versions of the included * software please visit: - * https://datatables.net/download/#bs5/dt-1.13.4 + * https://datatables.net/download/#bs5/dt-1.13.6 * * Included libraries: - * DataTables 1.13.4 + * DataTables 1.13.6 */ @charset "UTF-8"; @@ -15,6 +15,13 @@ --dt-row-selected: 13, 110, 253; --dt-row-selected-text: 255, 255, 255; --dt-row-selected-link: 9, 10, 11; + --dt-row-stripe: 0, 0, 0; + --dt-row-hover: 0, 0, 0; + --dt-column-ordering: 0, 0, 0; + --dt-html-background: white; +} +:root.dark { + --dt-html-background: rgb(33, 37, 41); } table.dataTable td.dt-control { @@ -22,25 +29,19 @@ table.dataTable td.dt-control { cursor: pointer; } table.dataTable td.dt-control:before { - height: 1em; - width: 1em; - margin-top: -9px; display: inline-block; - color: white; - border: 0.15em solid white; - border-radius: 1em; - box-shadow: 0 0 0.2em #444; - box-sizing: content-box; - text-align: center; - text-indent: 0 !important; - font-family: "Courier New", Courier, monospace; - line-height: 1em; - content: "+"; - background-color: #31b131; + color: rgba(0, 0, 0, 0.5); + content: "►"; } table.dataTable tr.dt-hasChild td.dt-control:before { - content: "-"; - background-color: #d33333; + content: "▼"; +} + +html.dark table.dataTable td.dt-control:before { + color: rgba(255, 255, 255, 0.5); +} +html.dark table.dataTable tr.dt-hasChild td.dt-control:before { + color: rgba(255, 255, 255, 0.5); } table.dataTable thead > tr > th.sorting, table.dataTable thead > tr > th.sorting_asc, table.dataTable thead > tr > th.sorting_desc, table.dataTable thead > tr > th.sorting_asc_disabled, table.dataTable thead > tr > th.sorting_desc_disabled, @@ -303,14 +304,14 @@ table.dataTable > tbody > tr.selected a { color: rgb(var(--dt-row-selected-link)); } table.dataTable.table-striped > tbody > tr.odd > * { - box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.05); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-stripe), 0.05); } table.dataTable.table-striped > tbody > tr.odd.selected > * { box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.95); box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-selected), 0.95); } table.dataTable.table-hover > tbody > tr:hover > * { - box-shadow: inset 0 0 0 9999px rgba(0, 0, 0, 0.075); + box-shadow: inset 0 0 0 9999px rgba(var(--dt-row-hover), 0.075); } table.dataTable.table-hover > tbody > tr.selected:hover > * { box-shadow: inset 0 0 0 9999px rgba(13, 110, 253, 0.975); @@ -441,4 +442,10 @@ div.table-responsive > div.dataTables_wrapper > div.row > div[class^=col-]:last- padding-right: 0; } +:root[data-bs-theme=dark] { + --dt-row-hover: 255, 255, 255; + --dt-row-stripe: 255, 255, 255; + --dt-column-ordering: 255, 255, 255; +} + diff --git a/src/static/scripts/datatables.js b/src/static/scripts/datatables.js index 520de77c..735ba65e 100644 --- a/src/static/scripts/datatables.js +++ b/src/static/scripts/datatables.js @@ -4,20 +4,20 @@ * * To rebuild or modify this file with the latest versions of the included * software please visit: - * https://datatables.net/download/#bs5/dt-1.13.4 + * https://datatables.net/download/#bs5/dt-1.13.6 * * Included libraries: - * DataTables 1.13.4 + * DataTables 1.13.6 */ -/*! DataTables 1.13.4 +/*! DataTables 1.13.6 * ©2008-2023 SpryMedia Ltd - datatables.net/license */ /** * @summary DataTables * @description Paginate, search and order HTML tables - * @version 1.13.4 + * @version 1.13.6 * @author SpryMedia Ltd * @contact www.datatables.net * @copyright SpryMedia Ltd. @@ -50,7 +50,7 @@ // returns a factory function that expects the window object var jq = require('jquery'); - if (typeof window !== 'undefined') { + if (typeof window === 'undefined') { module.exports = function (root, $) { if ( ! root ) { // CommonJS environments without a window global must pass a @@ -1396,7 +1396,7 @@ var _isNumber = function ( d, decimalPoint, formatted ) { - let type = typeof d; + var type = typeof d; var strType = type === 'string'; if ( type === 'number' || type === 'bigint') { @@ -1530,7 +1530,9 @@ var _stripHtml = function ( d ) { - return d.replace( _re_html, '' ); + return d + .replace( _re_html, '' ) // Complete tags + .replace(/<script/i, ''); // Safety for incomplete script tag }; @@ -1904,7 +1906,10 @@ continue; } - if ( data === null || data[ a[i] ] === undefined ) { + if (data === null || data[ a[i] ] === null) { + return null; + } + else if ( data === undefined || data[ a[i] ] === undefined ) { return undefined; } @@ -2351,6 +2356,12 @@ oCol.aDataSort = [ oOptions.iDataSort ]; } _fnMap( oCol, oOptions, "aDataSort" ); + + // Fall back to the aria-label attribute on the table header if no ariaTitle is + // provided. + if (! oCol.ariaTitle) { + oCol.ariaTitle = th.attr("aria-label"); + } } /* Cache the data get and set functions for speed */ @@ -4075,11 +4086,16 @@ settings.iDraw++; _fnProcessingDisplay( settings, true ); + // Keep track of drawHold state to handle scrolling after the Ajax call + var drawHold = settings._drawHold; + _fnBuildAjax( settings, _fnAjaxParameters( settings ), function(json) { + settings._drawHold = drawHold; _fnAjaxUpdateDraw( settings, json ); + settings._drawHold = false; } ); } @@ -4343,7 +4359,7 @@ _fnThrottle( searchFn, searchDelay ) : searchFn ) - .on( 'mouseup', function(e) { + .on( 'mouseup.DT', function(e) { // Edge fix! Edge 17 does not trigger anything other than mouse events when clicking // on the clear icon (Edge bug 17584515). This is safe in other browsers as `searchFn` // checks the value to see if it has changed. In other browsers it won't have. @@ -4409,7 +4425,7 @@ if ( _fnDataSource( oSettings ) != 'ssp' ) { /* Global filter */ - _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive, oInput.return ); + _fnFilter( oSettings, oInput.sSearch, iForce, fnRegex(oInput), oInput.bSmart, oInput.bCaseInsensitive ); fnSaveFilter( oInput ); /* Now do the individual column filter */ @@ -4578,11 +4594,15 @@ * * ^(?=.*?\bone\b)(?=.*?\btwo three\b)(?=.*?\bfour\b).*$ */ - var a = $.map( search.match( /"[^"]+"|[^ ]+/g ) || [''], function ( word ) { + var a = $.map( search.match( /["\u201C][^"\u201D]+["\u201D]|[^ ]+/g ) || [''], function ( word ) { if ( word.charAt(0) === '"' ) { var m = word.match( /^"(.*)"$/ ); word = m ? m[1] : word; } + else if ( word.charAt(0) === '\u201C' ) { + var m = word.match( /^\u201C(.*)\u201D$/ ); + word = m ? m[1] : word; + } return word.replace('"', ''); } ); @@ -9386,7 +9406,8 @@ * Set the jQuery or window object to be used by DataTables * * @param {*} module Library / container object - * @param {string} type Library or container type `lib` or `win`. + * @param {string} [type] Library or container type `lib`, `win` or `datetime`. + * If not provided, automatic detection is attempted. */ DataTable.use = function (module, type) { if (type === 'lib' || module.fn) { @@ -9396,6 +9417,9 @@ window = module; document = module.document; } + else if (type === 'datetime' || module.type === 'DateTime') { + DataTable.DateTime = module; + } } /** @@ -9755,7 +9779,9 @@ resolved._; } - return resolved.replace( '%d', plural ); // nb: plural might be undefined, + return typeof resolved === 'string' + ? resolved.replace( '%d', plural ) // nb: plural might be undefined, + : resolved; } ); /** * Version string for plug-ins to check compatibility. Allowed format is @@ -9765,7 +9791,7 @@ * @type string * @default Version number */ - DataTable.version = "1.13.4"; + DataTable.version = "1.13.6"; /** * Private data store, containing all of the settings objects that are @@ -14189,7 +14215,7 @@ * * @type string */ - build:"bs5/dt-1.13.4", + build:"bs5/dt-1.13.6", /** @@ -14830,7 +14856,7 @@ var btnDisplay, btnClass; var attach = function( container, buttons ) { - var i, ien, node, button, tabIndex; + var i, ien, node, button; var disabledClass = classes.sPageButtonDisabled; var clickHandler = function ( e ) { _fnPageChange( settings, e.data.action, true ); @@ -14845,9 +14871,10 @@ attach( inner, button ); } else { + var disabled = false; + btnDisplay = null; btnClass = button; - tabIndex = settings.iTabIndex; switch ( button ) { case 'ellipsis': @@ -14858,8 +14885,7 @@ btnDisplay = lang.sFirst; if ( page === 0 ) { - tabIndex = -1; - btnClass += ' ' + disabledClass; + disabled = true; } break; @@ -14867,8 +14893,7 @@ btnDisplay = lang.sPrevious; if ( page === 0 ) { - tabIndex = -1; - btnClass += ' ' + disabledClass; + disabled = true; } break; @@ -14876,8 +14901,7 @@ btnDisplay = lang.sNext; if ( pages === 0 || page === pages-1 ) { - tabIndex = -1; - btnClass += ' ' + disabledClass; + disabled = true; } break; @@ -14885,8 +14909,7 @@ btnDisplay = lang.sLast; if ( pages === 0 || page === pages-1 ) { - tabIndex = -1; - btnClass += ' ' + disabledClass; + disabled = true; } break; @@ -14899,18 +14922,20 @@ if ( btnDisplay !== null ) { var tag = settings.oInit.pagingTag || 'a'; - var disabled = btnClass.indexOf(disabledClass) !== -1; - + + if (disabled) { + btnClass += ' ' + disabledClass; + } node = $('<'+tag+'>', { 'class': classes.sPageButton+' '+btnClass, 'aria-controls': settings.sTableId, 'aria-disabled': disabled ? 'true' : null, 'aria-label': aria[ button ], - 'aria-role': 'link', + 'role': 'link', 'aria-current': btnClass === classes.sPageButtonActive ? 'page' : null, 'data-dt-idx': button, - 'tabindex': tabIndex, + 'tabindex': disabled ? -1 : settings.iTabIndex, 'id': idx === 0 && typeof button === 'string' ? settings.sTableId +'_'+ button : null @@ -15041,7 +15066,7 @@ return -Infinity; } - let type = typeof d; + var type = typeof d; if (type === 'number' || type === 'bigint') { return d; @@ -15415,7 +15440,7 @@ var __thousands = ','; var __decimal = '.'; - if (Intl) { + if (window.Intl !== undefined) { try { var num = new Intl.NumberFormat().formatToParts(100000.1); @@ -15718,7 +15743,7 @@ } }; - if (typeof window !== 'undefined') { + if (typeof window === 'undefined') { module.exports = function (root, $) { if ( ! root ) { // CommonJS environments without a window global must pass a @@ -15856,10 +15881,10 @@ DataTable.ext.renderer.pageButton.bootstrap = function ( settings, host, idx, bu 'aria-controls': settings.sTableId, 'aria-disabled': disabled ? 'true' : null, 'aria-label': aria[ button ], - 'aria-role': 'link', + 'role': 'link', 'aria-current': btnClass === 'active' ? 'page' : null, 'data-dt-idx': button, - 'tabindex': settings.iTabIndex, + 'tabindex': disabled ? -1 : settings.iTabIndex, 'class': 'page-link' } ) .html( btnDisplay ) diff --git a/src/static/scripts/jquery-3.6.4.slim.js b/src/static/scripts/jquery-3.7.0.slim.js index edf6ce9c..15a1a291 100644 --- a/src/static/scripts/jquery-3.6.4.slim.js +++ b/src/static/scripts/jquery-3.7.0.slim.js @@ -1,15 +1,12 @@ /*! - * jQuery JavaScript Library v3.6.4 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/animatedSelector,-effects/Tween + * jQuery JavaScript Library v3.7.0 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/animatedSelector,-effects/Tween * https://jquery.com/ * - * Includes Sizzle.js - * https://sizzlejs.com/ - * * Copyright OpenJS Foundation and other contributors * Released under the MIT license * https://jquery.org/license * - * Date: 2023-03-08T15:29Z + * Date: 2023-05-11T18:29Z */ ( function( global, factory ) { @@ -150,8 +147,9 @@ function toType( obj ) { -var - version = "3.6.4 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/animatedSelector,-effects/Tween", +var version = "3.7.0 -ajax,-ajax/jsonp,-ajax/load,-ajax/script,-ajax/var/location,-ajax/var/nonce,-ajax/var/rquery,-ajax/xhr,-manipulation/_evalUrl,-deprecated/ajax-event-alias,-effects,-effects/animatedSelector,-effects/Tween", + + rhtmlSuffix = /HTML$/i, // Define a local copy of jQuery jQuery = function( selector, context ) { @@ -397,6 +395,33 @@ jQuery.extend( { return obj; }, + + // Retrieve the text value of an array of DOM nodes + text: function( elem ) { + var node, + ret = "", + i = 0, + nodeType = elem.nodeType; + + if ( !nodeType ) { + + // If no nodeType, this is expected to be an array + while ( ( node = elem[ i++ ] ) ) { + + // Do not traverse comment nodes + ret += jQuery.text( node ); + } + } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { + return elem.textContent; + } else if ( nodeType === 3 || nodeType === 4 ) { + return elem.nodeValue; + } + + // Do not include comment or processing instruction nodes + + return ret; + }, + // results is for internal usage only makeArray: function( arr, results ) { var ret = results || []; @@ -419,6 +444,15 @@ jQuery.extend( { return arr == null ? -1 : indexOf.call( arr, elem, i ); }, + isXMLDoc: function( elem ) { + var namespace = elem && elem.namespaceURI, + docElem = elem && ( elem.ownerDocument || elem ).documentElement; + + // Assume HTML when documentElement doesn't yet exist, such as inside + // document fragments. + return !rhtmlSuffix.test( namespace || docElem && docElem.nodeName || "HTML" ); + }, + // Support: Android <=4.0 only, PhantomJS 1 only // push.apply(_, arraylike) throws on ancient WebKit merge: function( first, second ) { @@ -520,43 +554,98 @@ function isArrayLike( obj ) { return type === "array" || length === 0 || typeof length === "number" && length > 0 && ( length - 1 ) in obj; } -var Sizzle = -/*! - * Sizzle CSS Selector Engine v2.3.10 - * https://sizzlejs.com/ - * - * Copyright JS Foundation and other contributors - * Released under the MIT license - * https://js.foundation/ - * - * Date: 2023-02-14 - */ -( function( window ) { + + +function nodeName( elem, name ) { + + return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); + +} +var pop = arr.pop; + + +var sort = arr.sort; + + +var splice = arr.splice; + + +var whitespace = "[\\x20\\t\\r\\n\\f]"; + + +var rtrimCSS = new RegExp( + "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", + "g" +); + + + + +// Note: an element does not contain itself +jQuery.contains = function( a, b ) { + var bup = b && b.parentNode; + + return a === bup || !!( bup && bup.nodeType === 1 && ( + + // Support: IE 9 - 11+ + // IE doesn't have `contains` on SVG. + a.contains ? + a.contains( bup ) : + a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 + ) ); +}; + + + + +// CSS string/identifier serialization +// https://drafts.csswg.org/cssom/#common-serializing-idioms +var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g; + +function fcssescape( ch, asCodePoint ) { + if ( asCodePoint ) { + + // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER + if ( ch === "\0" ) { + return "\uFFFD"; + } + + // Control characters and (dependent upon position) numbers get escaped as code points + return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + } + + // Other potentially-special ASCII characters get backslash-escaped + return "\\" + ch; +} + +jQuery.escapeSelector = function( sel ) { + return ( sel + "" ).replace( rcssescape, fcssescape ); +}; + + + + +var preferredDoc = document, + pushNative = push; + +( function() { + var i, - support, Expr, - getText, - isXML, - tokenize, - compile, - select, outermostContext, sortInput, hasDuplicate, + push = pushNative, // Local document vars - setDocument, document, - docElem, + documentElement, documentIsHTML, rbuggyQSA, - rbuggyMatches, matches, - contains, // Instance-specific data - expando = "sizzle" + 1 * new Date(), - preferredDoc = window.document, + expando = jQuery.expando, dirruns = 0, done = 0, classCache = createCache(), @@ -570,47 +659,22 @@ var i, return 0; }, - // Instance methods - hasOwn = ( {} ).hasOwnProperty, - arr = [], - pop = arr.pop, - pushNative = arr.push, - push = arr.push, - slice = arr.slice, - - // Use a stripped-down indexOf as it's faster than native - // https://jsperf.com/thor-indexof-vs-for/5 - indexOf = function( list, elem ) { - var i = 0, - len = list.length; - for ( ; i < len; i++ ) { - if ( list[ i ] === elem ) { - return i; - } - } - return -1; - }, - - booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|" + - "ismap|loop|multiple|open|readonly|required|scoped", + booleans = "checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|" + + "loop|multiple|open|readonly|required|scoped", // Regular expressions - // http://www.w3.org/TR/css3-selectors/#whitespace - whitespace = "[\\x20\\t\\r\\n\\f]", - // https://www.w3.org/TR/css-syntax-3/#ident-token-diagram identifier = "(?:\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\[^\\r\\n\\f]|[\\w-]|[^\0-\\x7f])+", - // Attribute selectors: http://www.w3.org/TR/selectors/#attribute-selectors + // Attribute selectors: https://www.w3.org/TR/selectors/#attribute-selectors attributes = "\\[" + whitespace + "*(" + identifier + ")(?:" + whitespace + // Operator (capture 2) "*([*^$|!~]?=)" + whitespace + - // "Attribute values must be CSS identifiers [capture 5] - // or strings [capture 3 or capture 4]" + // "Attribute values must be CSS identifiers [capture 5] or strings [capture 3 or capture 4]" "*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|(" + identifier + "))|)" + whitespace + "*\\]", @@ -629,101 +693,88 @@ var i, // Leading and non-escaped trailing whitespace, capturing some non-whitespace characters preceding the latter rwhitespace = new RegExp( whitespace + "+", "g" ), - rtrim = new RegExp( "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + - whitespace + "+$", "g" ), rcomma = new RegExp( "^" + whitespace + "*," + whitespace + "*" ), - rleadingCombinator = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + whitespace + - "*" ), + rleadingCombinator = new RegExp( "^" + whitespace + "*([>+~]|" + whitespace + ")" + + whitespace + "*" ), rdescend = new RegExp( whitespace + "|>" ), rpseudo = new RegExp( pseudos ), ridentifier = new RegExp( "^" + identifier + "$" ), matchExpr = { - "ID": new RegExp( "^#(" + identifier + ")" ), - "CLASS": new RegExp( "^\\.(" + identifier + ")" ), - "TAG": new RegExp( "^(" + identifier + "|[*])" ), - "ATTR": new RegExp( "^" + attributes ), - "PSEUDO": new RegExp( "^" + pseudos ), - "CHILD": new RegExp( "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + - whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + - whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), - "bool": new RegExp( "^(?:" + booleans + ")$", "i" ), + ID: new RegExp( "^#(" + identifier + ")" ), + CLASS: new RegExp( "^\\.(" + identifier + ")" ), + TAG: new RegExp( "^(" + identifier + "|[*])" ), + ATTR: new RegExp( "^" + attributes ), + PSEUDO: new RegExp( "^" + pseudos ), + CHILD: new RegExp( + "^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\(" + + whitespace + "*(even|odd|(([+-]|)(\\d*)n|)" + whitespace + "*(?:([+-]|)" + + whitespace + "*(\\d+)|))" + whitespace + "*\\)|)", "i" ), + bool: new RegExp( "^(?:" + booleans + ")$", "i" ), // For use in libraries implementing .is() // We use this for POS matching in `select` - "needsContext": new RegExp( "^" + whitespace + + needsContext: new RegExp( "^" + whitespace + "*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\(" + whitespace + "*((?:-\\d)?\\d*)" + whitespace + "*\\)|)(?=[^-]|$)", "i" ) }, - rhtml = /HTML$/i, rinputs = /^(?:input|select|textarea|button)$/i, rheader = /^h\d$/i, - rnative = /^[^{]+\{\s*\[native \w/, - // Easily-parseable/retrievable ID or TAG or CLASS selectors rquickExpr = /^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/, rsibling = /[+~]/, // CSS escapes - // http://www.w3.org/TR/CSS21/syndata.html#escaped-characters - runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + "?|\\\\([^\\r\\n\\f])", "g" ), + // https://www.w3.org/TR/CSS21/syndata.html#escaped-characters + runescape = new RegExp( "\\\\[\\da-fA-F]{1,6}" + whitespace + + "?|\\\\([^\\r\\n\\f])", "g" ), funescape = function( escape, nonHex ) { var high = "0x" + escape.slice( 1 ) - 0x10000; - return nonHex ? + if ( nonHex ) { // Strip the backslash prefix from a non-hex escape sequence - nonHex : - - // Replace a hexadecimal escape sequence with the encoded Unicode code point - // Support: IE <=11+ - // For values outside the Basic Multilingual Plane (BMP), manually construct a - // surrogate pair - high < 0 ? - String.fromCharCode( high + 0x10000 ) : - String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); - }, - - // CSS string/identifier serialization - // https://drafts.csswg.org/cssom/#common-serializing-idioms - rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g, - fcssescape = function( ch, asCodePoint ) { - if ( asCodePoint ) { - - // U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER - if ( ch === "\0" ) { - return "\uFFFD"; - } - - // Control characters and (dependent upon position) numbers get escaped as code points - return ch.slice( 0, -1 ) + "\\" + - ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " "; + return nonHex; } - // Other potentially-special ASCII characters get backslash-escaped - return "\\" + ch; + // Replace a hexadecimal escape sequence with the encoded Unicode code point + // Support: IE <=11+ + // For values outside the Basic Multilingual Plane (BMP), manually construct a + // surrogate pair + return high < 0 ? + String.fromCharCode( high + 0x10000 ) : + String.fromCharCode( high >> 10 | 0xD800, high & 0x3FF | 0xDC00 ); }, - // Used for iframes - // See setDocument() + // Used for iframes; see `setDocument`. + // Support: IE 9 - 11+, Edge 12 - 18+ // Removing the function wrapper causes a "Permission Denied" - // error in IE + // error in IE/Edge. unloadHandler = function() { setDocument(); }, inDisabledFieldset = addCombinator( function( elem ) { - return elem.disabled === true && elem.nodeName.toLowerCase() === "fieldset"; + return elem.disabled === true && nodeName( elem, "fieldset" ); }, { dir: "parentNode", next: "legend" } ); +// Support: IE <=9 only +// Accessing document.activeElement can throw unexpectedly +// https://bugs.jquery.com/ticket/13393 +function safeActiveElement() { + try { + return document.activeElement; + } catch ( err ) { } +} + // Optimize for push.apply( _, NodeList ) try { push.apply( @@ -731,32 +782,22 @@ try { preferredDoc.childNodes ); - // Support: Android<4.0 + // Support: Android <=4.0 // Detect silently failing push.apply // eslint-disable-next-line no-unused-expressions arr[ preferredDoc.childNodes.length ].nodeType; } catch ( e ) { - push = { apply: arr.length ? - - // Leverage slice if possible - function( target, els ) { + push = { + apply: function( target, els ) { pushNative.apply( target, slice.call( els ) ); - } : - - // Support: IE<9 - // Otherwise append directly - function( target, els ) { - var j = target.length, - i = 0; - - // Can't trust NodeList.length - while ( ( target[ j++ ] = els[ i++ ] ) ) {} - target.length = j - 1; + }, + call: function( target ) { + pushNative.apply( target, slice.call( arguments, 1 ) ); } }; } -function Sizzle( selector, context, results, seed ) { +function find( selector, context, results, seed ) { var m, i, elem, nid, match, groups, newSelector, newContext = context && context.ownerDocument, @@ -790,11 +831,10 @@ function Sizzle( selector, context, results, seed ) { if ( nodeType === 9 ) { if ( ( elem = context.getElementById( m ) ) ) { - // Support: IE, Opera, Webkit - // TODO: identify versions + // Support: IE 9 only // getElementById can match elements by name instead of ID if ( elem.id === m ) { - results.push( elem ); + push.call( results, elem ); return results; } } else { @@ -804,14 +844,13 @@ function Sizzle( selector, context, results, seed ) { // Element context } else { - // Support: IE, Opera, Webkit - // TODO: identify versions + // Support: IE 9 only // getElementById can match elements by name instead of ID if ( newContext && ( elem = newContext.getElementById( m ) ) && - contains( context, elem ) && + find.contains( context, elem ) && elem.id === m ) { - results.push( elem ); + push.call( results, elem ); return results; } } @@ -822,22 +861,15 @@ function Sizzle( selector, context, results, seed ) { return results; // Class selector - } else if ( ( m = match[ 3 ] ) && support.getElementsByClassName && - context.getElementsByClassName ) { - + } else if ( ( m = match[ 3 ] ) && context.getElementsByClassName ) { push.apply( results, context.getElementsByClassName( m ) ); return results; } } // Take advantage of querySelectorAll - if ( support.qsa && - !nonnativeSelectorCache[ selector + " " ] && - ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) && - - // Support: IE 8 only - // Exclude object elements - ( nodeType !== 1 || context.nodeName.toLowerCase() !== "object" ) ) { + if ( !nonnativeSelectorCache[ selector + " " ] && + ( !rbuggyQSA || !rbuggyQSA.test( selector ) ) ) { newSelector = selector; newContext = context; @@ -858,11 +890,15 @@ function Sizzle( selector, context, results, seed ) { // We can use :scope instead of the ID hack if the browser // supports it & if we're not changing the context. - if ( newContext !== context || !support.scope ) { + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when + // strict-comparing two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + if ( newContext != context || !support.scope ) { // Capture the context ID, setting it first if necessary if ( ( nid = context.getAttribute( "id" ) ) ) { - nid = nid.replace( rcssescape, fcssescape ); + nid = jQuery.escapeSelector( nid ); } else { context.setAttribute( "id", ( nid = expando ) ); } @@ -895,7 +931,7 @@ function Sizzle( selector, context, results, seed ) { } // All others - return select( selector.replace( rtrim, "$1" ), context, results, seed ); + return select( selector.replace( rtrimCSS, "$1" ), context, results, seed ); } /** @@ -909,7 +945,8 @@ function createCache() { function cache( key, value ) { - // Use (key + " ") to avoid collision with native prototype properties (see Issue #157) + // Use (key + " ") to avoid collision with native prototype properties + // (see https://github.com/jquery/sizzle/issues/157) if ( keys.push( key + " " ) > Expr.cacheLength ) { // Only keep the most recent entries @@ -921,7 +958,7 @@ function createCache() { } /** - * Mark a function for special use by Sizzle + * Mark a function for special use by jQuery selector module * @param {Function} fn The function to mark */ function markFunction( fn ) { @@ -953,55 +990,12 @@ function assert( fn ) { } /** - * Adds the same handler for all of the specified attrs - * @param {String} attrs Pipe-separated list of attributes - * @param {Function} handler The method that will be applied - */ -function addHandle( attrs, handler ) { - var arr = attrs.split( "|" ), - i = arr.length; - - while ( i-- ) { - Expr.attrHandle[ arr[ i ] ] = handler; - } -} - -/** - * Checks document order of two siblings - * @param {Element} a - * @param {Element} b - * @returns {Number} Returns less than 0 if a precedes b, greater than 0 if a follows b - */ -function siblingCheck( a, b ) { - var cur = b && a, - diff = cur && a.nodeType === 1 && b.nodeType === 1 && - a.sourceIndex - b.sourceIndex; - - // Use IE sourceIndex if available on both nodes - if ( diff ) { - return diff; - } - - // Check if b follows a - if ( cur ) { - while ( ( cur = cur.nextSibling ) ) { - if ( cur === b ) { - return -1; - } - } - } - - return a ? 1 : -1; -} - -/** * Returns a function to use in pseudos for input types * @param {String} type */ function createInputPseudo( type ) { return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === type; + return nodeName( elem, "input" ) && elem.type === type; }; } @@ -1011,8 +1005,8 @@ function createInputPseudo( type ) { */ function createButtonPseudo( type ) { return function( elem ) { - var name = elem.nodeName.toLowerCase(); - return ( name === "input" || name === "button" ) && elem.type === type; + return ( nodeName( elem, "input" ) || nodeName( elem, "button" ) ) && + elem.type === type; }; } @@ -1048,14 +1042,13 @@ function createDisabledPseudo( disabled ) { } } - // Support: IE 6 - 11 + // Support: IE 6 - 11+ // Use the isDisabled shortcut property to check for disabled fieldset ancestors return elem.isDisabled === disabled || // Where there is no isDisabled, check manually - /* jshint -W018 */ elem.isDisabled !== !disabled && - inDisabledFieldset( elem ) === disabled; + inDisabledFieldset( elem ) === disabled; } return elem.disabled === disabled; @@ -1095,7 +1088,7 @@ function createPositionalPseudo( fn ) { } /** - * Checks a node for validity as a Sizzle context + * Checks a node for validity as a jQuery selector context * @param {Element|Object=} context * @returns {Element|Object|Boolean} The input node if acceptable, otherwise a falsy value */ @@ -1103,31 +1096,13 @@ function testContext( context ) { return context && typeof context.getElementsByTagName !== "undefined" && context; } -// Expose support vars for convenience -support = Sizzle.support = {}; - -/** - * Detects XML nodes - * @param {Element|Object} elem An element or a document - * @returns {Boolean} True iff elem is a non-HTML XML node - */ -isXML = Sizzle.isXML = function( elem ) { - var namespace = elem && elem.namespaceURI, - docElem = elem && ( elem.ownerDocument || elem ).documentElement; - - // Support: IE <=8 - // Assume HTML when documentElement doesn't yet exist, such as inside loading iframes - // https://bugs.jquery.com/ticket/4833 - return !rhtml.test( namespace || docElem && docElem.nodeName || "HTML" ); -}; - /** * Sets document-related variables once based on the current document - * @param {Element|Object} [doc] An element or document object to use to set the document + * @param {Element|Object} [node] An element or document object to use to set the document * @returns {Object} Returns the current document */ -setDocument = Sizzle.setDocument = function( node ) { - var hasCompare, subWindow, +function setDocument( node ) { + var subWindow, doc = node ? node.ownerDocument || node : preferredDoc; // Return early if doc is invalid or already selected @@ -1141,11 +1116,17 @@ setDocument = Sizzle.setDocument = function( node ) { // Update global variables document = doc; - docElem = document.documentElement; - documentIsHTML = !isXML( document ); + documentElement = document.documentElement; + documentIsHTML = !jQuery.isXMLDoc( document ); + + // Support: iOS 7 only, IE 9 - 11+ + // Older browsers didn't support unprefixed `matches`. + matches = documentElement.matches || + documentElement.webkitMatchesSelector || + documentElement.msMatchesSelector; // Support: IE 9 - 11+, Edge 12 - 18+ - // Accessing iframe documents after unload throws "permission denied" errors (jQuery #13936) + // Accessing iframe documents after unload throws "permission denied" errors (see trac-13936) // Support: IE 11+, Edge 17 - 18+ // IE/Edge sometimes throw a "Permission denied" error when strict-comparing // two documents; shallow comparisons work. @@ -1153,29 +1134,35 @@ setDocument = Sizzle.setDocument = function( node ) { if ( preferredDoc != document && ( subWindow = document.defaultView ) && subWindow.top !== subWindow ) { - // Support: IE 11, Edge - if ( subWindow.addEventListener ) { - subWindow.addEventListener( "unload", unloadHandler, false ); - - // Support: IE 9 - 10 only - } else if ( subWindow.attachEvent ) { - subWindow.attachEvent( "onunload", unloadHandler ); - } + // Support: IE 9 - 11+, Edge 12 - 18+ + subWindow.addEventListener( "unload", unloadHandler ); } - // Support: IE 8 - 11+, Edge 12 - 18+, Chrome <=16 - 25 only, Firefox <=3.6 - 31 only, - // Safari 4 - 5 only, Opera <=11.6 - 12.x only - // IE/Edge & older browsers don't support the :scope pseudo-class. - // Support: Safari 6.0 only - // Safari 6.0 supports :scope but it's an alias of :root there. - support.scope = assert( function( el ) { - docElem.appendChild( el ).appendChild( document.createElement( "div" ) ); - return typeof el.querySelectorAll !== "undefined" && - !el.querySelectorAll( ":scope fieldset div" ).length; + // Support: IE <10 + // Check if getElementById returns elements by name + // The broken getElementById methods don't pick up programmatically-set names, + // so use a roundabout getElementsByName test + support.getById = assert( function( el ) { + documentElement.appendChild( el ).id = jQuery.expando; + return !document.getElementsByName || + !document.getElementsByName( jQuery.expando ).length; } ); - // Support: Chrome 105 - 110+, Safari 15.4 - 16.3+ - // Make sure the the `:has()` argument is parsed unforgivingly. + // Support: IE 9 only + // Check to see if it's possible to do matchesSelector + // on a disconnected node. + support.disconnectedMatch = assert( function( el ) { + return matches.call( el, "*" ); + } ); + + // Support: IE 9 - 11+, Edge 12 - 18+ + // IE/Edge don't support the :scope pseudo-class. + support.scope = assert( function() { + return document.querySelectorAll( ":scope" ); + } ); + + // Support: Chrome 105 - 111 only, Safari 15.4 - 16.3 only + // Make sure the `:has()` argument is parsed unforgivingly. // We include `*` in the test to detect buggy implementations that are // _selectively_ forgiving (specifically when the list includes at least // one valid selector). @@ -1192,54 +1179,22 @@ setDocument = Sizzle.setDocument = function( node ) { } } ); - /* Attributes - ---------------------------------------------------------------------- */ - - // Support: IE<8 - // Verify that getAttribute really returns attributes and not properties - // (excepting IE8 booleans) - support.attributes = assert( function( el ) { - el.className = "i"; - return !el.getAttribute( "className" ); - } ); - - /* getElement(s)By* - ---------------------------------------------------------------------- */ - - // Check if getElementsByTagName("*") returns only elements - support.getElementsByTagName = assert( function( el ) { - el.appendChild( document.createComment( "" ) ); - return !el.getElementsByTagName( "*" ).length; - } ); - - // Support: IE<9 - support.getElementsByClassName = rnative.test( document.getElementsByClassName ); - - // Support: IE<10 - // Check if getElementById returns elements by name - // The broken getElementById methods don't pick up programmatically-set names, - // so use a roundabout getElementsByName test - support.getById = assert( function( el ) { - docElem.appendChild( el ).id = expando; - return !document.getElementsByName || !document.getElementsByName( expando ).length; - } ); - // ID filter and find if ( support.getById ) { - Expr.filter[ "ID" ] = function( id ) { + Expr.filter.ID = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { return elem.getAttribute( "id" ) === attrId; }; }; - Expr.find[ "ID" ] = function( id, context ) { + Expr.find.ID = function( id, context ) { if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { var elem = context.getElementById( id ); return elem ? [ elem ] : []; } }; } else { - Expr.filter[ "ID" ] = function( id ) { + Expr.filter.ID = function( id ) { var attrId = id.replace( runescape, funescape ); return function( elem ) { var node = typeof elem.getAttributeNode !== "undefined" && @@ -1250,7 +1205,7 @@ setDocument = Sizzle.setDocument = function( node ) { // Support: IE 6 - 7 only // getElementById is not reliable as a find shortcut - Expr.find[ "ID" ] = function( id, context ) { + Expr.find.ID = function( id, context ) { if ( typeof context.getElementById !== "undefined" && documentIsHTML ) { var node, i, elems, elem = context.getElementById( id ); @@ -1280,40 +1235,18 @@ setDocument = Sizzle.setDocument = function( node ) { } // Tag - Expr.find[ "TAG" ] = support.getElementsByTagName ? - function( tag, context ) { - if ( typeof context.getElementsByTagName !== "undefined" ) { - return context.getElementsByTagName( tag ); - - // DocumentFragment nodes don't have gEBTN - } else if ( support.qsa ) { - return context.querySelectorAll( tag ); - } - } : + Expr.find.TAG = function( tag, context ) { + if ( typeof context.getElementsByTagName !== "undefined" ) { + return context.getElementsByTagName( tag ); - function( tag, context ) { - var elem, - tmp = [], - i = 0, - - // By happy coincidence, a (broken) gEBTN appears on DocumentFragment nodes too - results = context.getElementsByTagName( tag ); - - // Filter out possible comments - if ( tag === "*" ) { - while ( ( elem = results[ i++ ] ) ) { - if ( elem.nodeType === 1 ) { - tmp.push( elem ); - } - } - - return tmp; - } - return results; - }; + // DocumentFragment nodes don't have gEBTN + } else { + return context.querySelectorAll( tag ); + } + }; // Class - Expr.find[ "CLASS" ] = support.getElementsByClassName && function( className, context ) { + Expr.find.CLASS = function( className, context ) { if ( typeof context.getElementsByClassName !== "undefined" && documentIsHTML ) { return context.getElementsByClassName( className ); } @@ -1324,139 +1257,75 @@ setDocument = Sizzle.setDocument = function( node ) { // QSA and matchesSelector support - // matchesSelector(:active) reports false when true (IE9/Opera 11.5) - rbuggyMatches = []; - - // qSa(:focus) reports false when true (Chrome 21) - // We allow this because of a bug in IE8/9 that throws an error - // whenever `document.activeElement` is accessed on an iframe - // So, we allow :focus to pass through QSA all the time to avoid the IE error - // See https://bugs.jquery.com/ticket/13378 rbuggyQSA = []; - if ( ( support.qsa = rnative.test( document.querySelectorAll ) ) ) { - - // Build QSA regex - // Regex strategy adopted from Diego Perini - assert( function( el ) { - - var input; - - // Select is set to empty string on purpose - // This is to test IE's treatment of not explicitly - // setting a boolean content attribute, - // since its presence should be enough - // https://bugs.jquery.com/ticket/12359 - docElem.appendChild( el ).innerHTML = "<a id='" + expando + "'></a>" + - "<select id='" + expando + "-\r\\' msallowcapture=''>" + - "<option selected=''></option></select>"; + // Build QSA regex + // Regex strategy adopted from Diego Perini + assert( function( el ) { - // Support: IE8, Opera 11-12.16 - // Nothing should be selected when empty strings follow ^= or $= or *= - // The test attribute must be unknown in Opera but "safe" for WinRT - // https://msdn.microsoft.com/en-us/library/ie/hh465388.aspx#attribute_section - if ( el.querySelectorAll( "[msallowcapture^='']" ).length ) { - rbuggyQSA.push( "[*^$]=" + whitespace + "*(?:''|\"\")" ); - } - - // Support: IE8 - // Boolean attributes and "value" are not treated correctly - if ( !el.querySelectorAll( "[selected]" ).length ) { - rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); - } + var input; - // Support: Chrome<29, Android<4.4, Safari<7.0+, iOS<7.0+, PhantomJS<1.9.8+ - if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { - rbuggyQSA.push( "~=" ); - } + documentElement.appendChild( el ).innerHTML = + "<a id='" + expando + "' href='' disabled='disabled'></a>" + + "<select id='" + expando + "-\r\\' disabled='disabled'>" + + "<option selected=''></option></select>"; - // Support: IE 11+, Edge 15 - 18+ - // IE 11/Edge don't find elements on a `[name='']` query in some cases. - // Adding a temporary attribute to the document before the selection works - // around the issue. - // Interestingly, IE 10 & older don't seem to have the issue. - input = document.createElement( "input" ); - input.setAttribute( "name", "" ); - el.appendChild( input ); - if ( !el.querySelectorAll( "[name='']" ).length ) { - rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + - whitespace + "*(?:''|\"\")" ); - } - - // Webkit/Opera - :checked should return selected option elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - // IE8 throws error here and will not see later tests - if ( !el.querySelectorAll( ":checked" ).length ) { - rbuggyQSA.push( ":checked" ); - } - - // Support: Safari 8+, iOS 8+ - // https://bugs.webkit.org/show_bug.cgi?id=136851 - // In-page `selector#id sibling-combinator selector` fails - if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { - rbuggyQSA.push( ".#.+[+~]" ); - } - - // Support: Firefox <=3.6 - 5 only - // Old Firefox doesn't throw on a badly-escaped identifier. - el.querySelectorAll( "\\\f" ); - rbuggyQSA.push( "[\\r\\n\\f]" ); - } ); - - assert( function( el ) { - el.innerHTML = "<a href='' disabled='disabled'></a>" + - "<select disabled='disabled'><option/></select>"; - - // Support: Windows 8 Native Apps - // The type and name attributes are restricted during .innerHTML assignment - var input = document.createElement( "input" ); - input.setAttribute( "type", "hidden" ); - el.appendChild( input ).setAttribute( "name", "D" ); - - // Support: IE8 - // Enforce case-sensitivity of name attribute - if ( el.querySelectorAll( "[name=d]" ).length ) { - rbuggyQSA.push( "name" + whitespace + "*[*^$|!~]?=" ); - } - - // FF 3.5 - :enabled/:disabled and hidden elements (hidden elements are still enabled) - // IE8 throws error here and will not see later tests - if ( el.querySelectorAll( ":enabled" ).length !== 2 ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Support: IE9-11+ - // IE's :disabled selector does not pick up the children of disabled fieldsets - docElem.appendChild( el ).disabled = true; - if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { - rbuggyQSA.push( ":enabled", ":disabled" ); - } - - // Support: Opera 10 - 11 only - // Opera 10-11 does not throw on post-comma invalid pseudos - el.querySelectorAll( "*,:x" ); - rbuggyQSA.push( ",.*:" ); - } ); - } + // Support: iOS <=7 - 8 only + // Boolean attributes and "value" are not treated correctly in some XML documents + if ( !el.querySelectorAll( "[selected]" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*(?:value|" + booleans + ")" ); + } - if ( ( support.matchesSelector = rnative.test( ( matches = docElem.matches || - docElem.webkitMatchesSelector || - docElem.mozMatchesSelector || - docElem.oMatchesSelector || - docElem.msMatchesSelector ) ) ) ) { + // Support: iOS <=7 - 8 only + if ( !el.querySelectorAll( "[id~=" + expando + "-]" ).length ) { + rbuggyQSA.push( "~=" ); + } - assert( function( el ) { + // Support: iOS 8 only + // https://bugs.webkit.org/show_bug.cgi?id=136851 + // In-page `selector#id sibling-combinator selector` fails + if ( !el.querySelectorAll( "a#" + expando + "+*" ).length ) { + rbuggyQSA.push( ".#.+[+~]" ); + } - // Check to see if it's possible to do matchesSelector - // on a disconnected node (IE 9) - support.disconnectedMatch = matches.call( el, "*" ); + // Support: Chrome <=105+, Firefox <=104+, Safari <=15.4+ + // In some of the document kinds, these selectors wouldn't work natively. + // This is probably OK but for backwards compatibility we want to maintain + // handling them through jQuery traversal in jQuery 3.x. + if ( !el.querySelectorAll( ":checked" ).length ) { + rbuggyQSA.push( ":checked" ); + } - // This should fail with an exception - // Gecko does not error, returns false instead - matches.call( el, "[s!='']:x" ); - rbuggyMatches.push( "!=", pseudos ); - } ); - } + // Support: Windows 8 Native Apps + // The type and name attributes are restricted during .innerHTML assignment + input = document.createElement( "input" ); + input.setAttribute( "type", "hidden" ); + el.appendChild( input ).setAttribute( "name", "D" ); + + // Support: IE 9 - 11+ + // IE's :disabled selector does not pick up the children of disabled fieldsets + // Support: Chrome <=105+, Firefox <=104+, Safari <=15.4+ + // In some of the document kinds, these selectors wouldn't work natively. + // This is probably OK but for backwards compatibility we want to maintain + // handling them through jQuery traversal in jQuery 3.x. + documentElement.appendChild( el ).disabled = true; + if ( el.querySelectorAll( ":disabled" ).length !== 2 ) { + rbuggyQSA.push( ":enabled", ":disabled" ); + } + + // Support: IE 11+, Edge 15 - 18+ + // IE 11/Edge don't find elements on a `[name='']` query in some cases. + // Adding a temporary attribute to the document before the selection works + // around the issue. + // Interestingly, IE 10 & older don't seem to have the issue. + input = document.createElement( "input" ); + input.setAttribute( "name", "" ); + el.appendChild( input ); + if ( !el.querySelectorAll( "[name='']" ).length ) { + rbuggyQSA.push( "\\[" + whitespace + "*name" + whitespace + "*=" + + whitespace + "*(?:''|\"\")" ); + } + } ); if ( !support.cssHas ) { @@ -1470,49 +1339,12 @@ setDocument = Sizzle.setDocument = function( node ) { } rbuggyQSA = rbuggyQSA.length && new RegExp( rbuggyQSA.join( "|" ) ); - rbuggyMatches = rbuggyMatches.length && new RegExp( rbuggyMatches.join( "|" ) ); - - /* Contains - ---------------------------------------------------------------------- */ - hasCompare = rnative.test( docElem.compareDocumentPosition ); - - // Element contains another - // Purposefully self-exclusive - // As in, an element does not contain itself - contains = hasCompare || rnative.test( docElem.contains ) ? - function( a, b ) { - - // Support: IE <9 only - // IE doesn't have `contains` on `document` so we need to check for - // `documentElement` presence. - // We need to fall back to `a` when `documentElement` is missing - // as `ownerDocument` of elements within `<template/>` may have - // a null one - a default behavior of all modern browsers. - var adown = a.nodeType === 9 && a.documentElement || a, - bup = b && b.parentNode; - return a === bup || !!( bup && bup.nodeType === 1 && ( - adown.contains ? - adown.contains( bup ) : - a.compareDocumentPosition && a.compareDocumentPosition( bup ) & 16 - ) ); - } : - function( a, b ) { - if ( b ) { - while ( ( b = b.parentNode ) ) { - if ( b === a ) { - return true; - } - } - } - return false; - }; /* Sorting ---------------------------------------------------------------------- */ // Document order sorting - sortOrder = hasCompare ? - function( a, b ) { + sortOrder = function( a, b ) { // Flag for duplicate removal if ( a === b ) { @@ -1546,8 +1378,8 @@ setDocument = Sizzle.setDocument = function( node ) { // IE/Edge sometimes throw a "Permission denied" error when strict-comparing // two documents; shallow comparisons work. // eslint-disable-next-line eqeqeq - if ( a == document || a.ownerDocument == preferredDoc && - contains( preferredDoc, a ) ) { + if ( a === document || a.ownerDocument == preferredDoc && + find.contains( preferredDoc, a ) ) { return -1; } @@ -1555,100 +1387,33 @@ setDocument = Sizzle.setDocument = function( node ) { // IE/Edge sometimes throw a "Permission denied" error when strict-comparing // two documents; shallow comparisons work. // eslint-disable-next-line eqeqeq - if ( b == document || b.ownerDocument == preferredDoc && - contains( preferredDoc, b ) ) { + if ( b === document || b.ownerDocument == preferredDoc && + find.contains( preferredDoc, b ) ) { return 1; } // Maintain original order return sortInput ? - ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : + ( indexOf.call( sortInput, a ) - indexOf.call( sortInput, b ) ) : 0; } return compare & 4 ? -1 : 1; - } : - function( a, b ) { - - // Exit early if the nodes are identical - if ( a === b ) { - hasDuplicate = true; - return 0; - } - - var cur, - i = 0, - aup = a.parentNode, - bup = b.parentNode, - ap = [ a ], - bp = [ b ]; - - // Parentless nodes are either documents or disconnected - if ( !aup || !bup ) { - - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - /* eslint-disable eqeqeq */ - return a == document ? -1 : - b == document ? 1 : - /* eslint-enable eqeqeq */ - aup ? -1 : - bup ? 1 : - sortInput ? - ( indexOf( sortInput, a ) - indexOf( sortInput, b ) ) : - 0; - - // If the nodes are siblings, we can do a quick check - } else if ( aup === bup ) { - return siblingCheck( a, b ); - } - - // Otherwise we need full lists of their ancestors for comparison - cur = a; - while ( ( cur = cur.parentNode ) ) { - ap.unshift( cur ); - } - cur = b; - while ( ( cur = cur.parentNode ) ) { - bp.unshift( cur ); - } - - // Walk down the tree looking for a discrepancy - while ( ap[ i ] === bp[ i ] ) { - i++; - } - - return i ? - - // Do a sibling check if the nodes have a common ancestor - siblingCheck( ap[ i ], bp[ i ] ) : - - // Otherwise nodes in our document sort first - // Support: IE 11+, Edge 17 - 18+ - // IE/Edge sometimes throw a "Permission denied" error when strict-comparing - // two documents; shallow comparisons work. - /* eslint-disable eqeqeq */ - ap[ i ] == preferredDoc ? -1 : - bp[ i ] == preferredDoc ? 1 : - /* eslint-enable eqeqeq */ - 0; }; return document; -}; +} -Sizzle.matches = function( expr, elements ) { - return Sizzle( expr, null, null, elements ); +find.matches = function( expr, elements ) { + return find( expr, null, null, elements ); }; -Sizzle.matchesSelector = function( elem, expr ) { +find.matchesSelector = function( elem, expr ) { setDocument( elem ); - if ( support.matchesSelector && documentIsHTML && + if ( documentIsHTML && !nonnativeSelectorCache[ expr + " " ] && - ( !rbuggyMatches || !rbuggyMatches.test( expr ) ) && - ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { + ( !rbuggyQSA || !rbuggyQSA.test( expr ) ) ) { try { var ret = matches.call( elem, expr ); @@ -1656,9 +1421,9 @@ Sizzle.matchesSelector = function( elem, expr ) { // IE 9's matchesSelector returns false on disconnected nodes if ( ret || support.disconnectedMatch || - // As well, disconnected nodes are said to be in a document - // fragment in IE 9 - elem.document && elem.document.nodeType !== 11 ) { + // As well, disconnected nodes are said to be in a document + // fragment in IE 9 + elem.document && elem.document.nodeType !== 11 ) { return ret; } } catch ( e ) { @@ -1666,10 +1431,10 @@ Sizzle.matchesSelector = function( elem, expr ) { } } - return Sizzle( expr, document, null, [ elem ] ).length > 0; + return find( expr, document, null, [ elem ] ).length > 0; }; -Sizzle.contains = function( context, elem ) { +find.contains = function( context, elem ) { // Set document vars if needed // Support: IE 11+, Edge 17 - 18+ @@ -1679,10 +1444,11 @@ Sizzle.contains = function( context, elem ) { if ( ( context.ownerDocument || context ) != document ) { setDocument( context ); } - return contains( context, elem ); + return jQuery.contains( context, elem ); }; -Sizzle.attr = function( elem, name ) { + +find.attr = function( elem, name ) { // Set document vars if needed // Support: IE 11+, Edge 17 - 18+ @@ -1695,25 +1461,19 @@ Sizzle.attr = function( elem, name ) { var fn = Expr.attrHandle[ name.toLowerCase() ], - // Don't get fooled by Object.prototype properties (jQuery #13807) + // Don't get fooled by Object.prototype properties (see trac-13807) val = fn && hasOwn.call( Expr.attrHandle, name.toLowerCase() ) ? fn( elem, name, !documentIsHTML ) : undefined; - return val !== undefined ? - val : - support.attributes || !documentIsHTML ? - elem.getAttribute( name ) : - ( val = elem.getAttributeNode( name ) ) && val.specified ? - val.value : - null; -}; + if ( val !== undefined ) { + return val; + } -Sizzle.escape = function( sel ) { - return ( sel + "" ).replace( rcssescape, fcssescape ); + return elem.getAttribute( name ); }; -Sizzle.error = function( msg ) { +find.error = function( msg ) { throw new Error( "Syntax error, unrecognized expression: " + msg ); }; @@ -1721,16 +1481,20 @@ Sizzle.error = function( msg ) { * Document sorting and removing duplicates * @param {ArrayLike} results */ -Sizzle.uniqueSort = function( results ) { +jQuery.uniqueSort = function( results ) { var elem, duplicates = [], j = 0, i = 0; // Unless we *know* we can detect duplicates, assume their presence - hasDuplicate = !support.detectDuplicates; - sortInput = !support.sortStable && results.slice( 0 ); - results.sort( sortOrder ); + // + // Support: Android <=4.0+ + // Testing for detecting duplicates is unpredictable so instead assume we can't + // depend on duplicate detection in all browsers without a stable sort. + hasDuplicate = !support.sortStable; + sortInput = !support.sortStable && slice.call( results, 0 ); + sort.call( results, sortOrder ); if ( hasDuplicate ) { while ( ( elem = results[ i++ ] ) ) { @@ -1739,7 +1503,7 @@ Sizzle.uniqueSort = function( results ) { } } while ( j-- ) { - results.splice( duplicates[ j ], 1 ); + splice.call( results, duplicates[ j ], 1 ); } } @@ -1750,47 +1514,11 @@ Sizzle.uniqueSort = function( results ) { return results; }; -/** - * Utility function for retrieving the text value of an array of DOM nodes - * @param {Array|Element} elem - */ -getText = Sizzle.getText = function( elem ) { - var node, - ret = "", - i = 0, - nodeType = elem.nodeType; - - if ( !nodeType ) { - - // If no nodeType, this is expected to be an array - while ( ( node = elem[ i++ ] ) ) { - - // Do not traverse comment nodes - ret += getText( node ); - } - } else if ( nodeType === 1 || nodeType === 9 || nodeType === 11 ) { - - // Use textContent for elements - // innerText usage removed for consistency of new lines (jQuery #11153) - if ( typeof elem.textContent === "string" ) { - return elem.textContent; - } else { - - // Traverse its children - for ( elem = elem.firstChild; elem; elem = elem.nextSibling ) { - ret += getText( elem ); - } - } - } else if ( nodeType === 3 || nodeType === 4 ) { - return elem.nodeValue; - } - - // Do not include comment or processing instruction nodes - - return ret; +jQuery.fn.uniqueSort = function() { + return this.pushStack( jQuery.uniqueSort( slice.apply( this ) ) ); }; -Expr = Sizzle.selectors = { +Expr = jQuery.expr = { // Can be adjusted by the user cacheLength: 50, @@ -1811,12 +1539,12 @@ Expr = Sizzle.selectors = { }, preFilter: { - "ATTR": function( match ) { + ATTR: function( match ) { match[ 1 ] = match[ 1 ].replace( runescape, funescape ); // Move the given value to match[3] whether quoted or unquoted - match[ 3 ] = ( match[ 3 ] || match[ 4 ] || - match[ 5 ] || "" ).replace( runescape, funescape ); + match[ 3 ] = ( match[ 3 ] || match[ 4 ] || match[ 5 ] || "" ) + .replace( runescape, funescape ); if ( match[ 2 ] === "~=" ) { match[ 3 ] = " " + match[ 3 ] + " "; @@ -1825,7 +1553,7 @@ Expr = Sizzle.selectors = { return match.slice( 0, 4 ); }, - "CHILD": function( match ) { + CHILD: function( match ) { /* matches from matchExpr["CHILD"] 1 type (only|nth|...) @@ -1843,29 +1571,30 @@ Expr = Sizzle.selectors = { // nth-* requires argument if ( !match[ 3 ] ) { - Sizzle.error( match[ 0 ] ); + find.error( match[ 0 ] ); } // numeric x and y parameters for Expr.filter.CHILD // remember that false/true cast respectively to 0/1 match[ 4 ] = +( match[ 4 ] ? match[ 5 ] + ( match[ 6 ] || 1 ) : - 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) ); + 2 * ( match[ 3 ] === "even" || match[ 3 ] === "odd" ) + ); match[ 5 ] = +( ( match[ 7 ] + match[ 8 ] ) || match[ 3 ] === "odd" ); - // other types prohibit arguments + // other types prohibit arguments } else if ( match[ 3 ] ) { - Sizzle.error( match[ 0 ] ); + find.error( match[ 0 ] ); } return match; }, - "PSEUDO": function( match ) { + PSEUDO: function( match ) { var excess, unquoted = !match[ 6 ] && match[ 2 ]; - if ( matchExpr[ "CHILD" ].test( match[ 0 ] ) ) { + if ( matchExpr.CHILD.test( match[ 0 ] ) ) { return null; } @@ -1894,36 +1623,36 @@ Expr = Sizzle.selectors = { filter: { - "TAG": function( nodeNameSelector ) { - var nodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); + TAG: function( nodeNameSelector ) { + var expectedNodeName = nodeNameSelector.replace( runescape, funescape ).toLowerCase(); return nodeNameSelector === "*" ? function() { return true; } : function( elem ) { - return elem.nodeName && elem.nodeName.toLowerCase() === nodeName; + return nodeName( elem, expectedNodeName ); }; }, - "CLASS": function( className ) { + CLASS: function( className ) { var pattern = classCache[ className + " " ]; return pattern || - ( pattern = new RegExp( "(^|" + whitespace + - ")" + className + "(" + whitespace + "|$)" ) ) && classCache( - className, function( elem ) { - return pattern.test( - typeof elem.className === "string" && elem.className || - typeof elem.getAttribute !== "undefined" && - elem.getAttribute( "class" ) || - "" - ); + ( pattern = new RegExp( "(^|" + whitespace + ")" + className + + "(" + whitespace + "|$)" ) ) && + classCache( className, function( elem ) { + return pattern.test( + typeof elem.className === "string" && elem.className || + typeof elem.getAttribute !== "undefined" && + elem.getAttribute( "class" ) || + "" + ); } ); }, - "ATTR": function( name, operator, check ) { + ATTR: function( name, operator, check ) { return function( elem ) { - var result = Sizzle.attr( elem, name ); + var result = find.attr( elem, name ); if ( result == null ) { return operator === "!="; @@ -1934,22 +1663,34 @@ Expr = Sizzle.selectors = { result += ""; - /* eslint-disable max-len */ - - return operator === "=" ? result === check : - operator === "!=" ? result !== check : - operator === "^=" ? check && result.indexOf( check ) === 0 : - operator === "*=" ? check && result.indexOf( check ) > -1 : - operator === "$=" ? check && result.slice( -check.length ) === check : - operator === "~=" ? ( " " + result.replace( rwhitespace, " " ) + " " ).indexOf( check ) > -1 : - operator === "|=" ? result === check || result.slice( 0, check.length + 1 ) === check + "-" : - false; - /* eslint-enable max-len */ + if ( operator === "=" ) { + return result === check; + } + if ( operator === "!=" ) { + return result !== check; + } + if ( operator === "^=" ) { + return check && result.indexOf( check ) === 0; + } + if ( operator === "*=" ) { + return check && result.indexOf( check ) > -1; + } + if ( operator === "$=" ) { + return check && result.slice( -check.length ) === check; + } + if ( operator === "~=" ) { + return ( " " + result.replace( rwhitespace, " " ) + " " ) + .indexOf( check ) > -1; + } + if ( operator === "|=" ) { + return result === check || result.slice( 0, check.length + 1 ) === check + "-"; + } + return false; }; }, - "CHILD": function( type, what, _argument, first, last ) { + CHILD: function( type, what, _argument, first, last ) { var simple = type.slice( 0, 3 ) !== "nth", forward = type.slice( -4 ) !== "last", ofType = what === "of-type"; @@ -1962,7 +1703,7 @@ Expr = Sizzle.selectors = { } : function( elem, _context, xml ) { - var cache, uniqueCache, outerCache, node, nodeIndex, start, + var cache, outerCache, node, nodeIndex, start, dir = simple !== forward ? "nextSibling" : "previousSibling", parent = elem.parentNode, name = ofType && elem.nodeName.toLowerCase(), @@ -1977,7 +1718,7 @@ Expr = Sizzle.selectors = { node = elem; while ( ( node = node[ dir ] ) ) { if ( ofType ? - node.nodeName.toLowerCase() === name : + nodeName( node, name ) : node.nodeType === 1 ) { return false; @@ -1996,17 +1737,8 @@ Expr = Sizzle.selectors = { if ( forward && useCache ) { // Seek `elem` from a previously-cached index - - // ...in a gzip-friendly way - node = parent; - outerCache = node[ expando ] || ( node[ expando ] = {} ); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - ( outerCache[ node.uniqueID ] = {} ); - - cache = uniqueCache[ type ] || []; + outerCache = parent[ expando ] || ( parent[ expando ] = {} ); + cache = outerCache[ type ] || []; nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; diff = nodeIndex && cache[ 2 ]; node = nodeIndex && parent.childNodes[ nodeIndex ]; @@ -2018,7 +1750,7 @@ Expr = Sizzle.selectors = { // When found, cache indexes on `parent` and break if ( node.nodeType === 1 && ++diff && node === elem ) { - uniqueCache[ type ] = [ dirruns, nodeIndex, diff ]; + outerCache[ type ] = [ dirruns, nodeIndex, diff ]; break; } } @@ -2027,17 +1759,8 @@ Expr = Sizzle.selectors = { // Use previously-cached element index if available if ( useCache ) { - - // ...in a gzip-friendly way - node = elem; - outerCache = node[ expando ] || ( node[ expando ] = {} ); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - ( outerCache[ node.uniqueID ] = {} ); - - cache = uniqueCache[ type ] || []; + outerCache = elem[ expando ] || ( elem[ expando ] = {} ); + cache = outerCache[ type ] || []; nodeIndex = cache[ 0 ] === dirruns && cache[ 1 ]; diff = nodeIndex; } @@ -2051,7 +1774,7 @@ Expr = Sizzle.selectors = { ( diff = nodeIndex = 0 ) || start.pop() ) ) { if ( ( ofType ? - node.nodeName.toLowerCase() === name : + nodeName( node, name ) : node.nodeType === 1 ) && ++diff ) { @@ -2059,13 +1782,7 @@ Expr = Sizzle.selectors = { if ( useCache ) { outerCache = node[ expando ] || ( node[ expando ] = {} ); - - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ node.uniqueID ] || - ( outerCache[ node.uniqueID ] = {} ); - - uniqueCache[ type ] = [ dirruns, diff ]; + outerCache[ type ] = [ dirruns, diff ]; } if ( node === elem ) { @@ -2083,19 +1800,19 @@ Expr = Sizzle.selectors = { }; }, - "PSEUDO": function( pseudo, argument ) { + PSEUDO: function( pseudo, argument ) { // pseudo-class names are case-insensitive - // http://www.w3.org/TR/selectors/#pseudo-classes + // https://www.w3.org/TR/selectors/#pseudo-classes // Prioritize by case sensitivity in case custom pseudos are added with uppercase letters // Remember that setFilters inherits from pseudos var args, fn = Expr.pseudos[ pseudo ] || Expr.setFilters[ pseudo.toLowerCase() ] || - Sizzle.error( "unsupported pseudo: " + pseudo ); + find.error( "unsupported pseudo: " + pseudo ); // The user may use createPseudo to indicate that // arguments are needed to create the filter function - // just as Sizzle does + // just as jQuery does if ( fn[ expando ] ) { return fn( argument ); } @@ -2109,7 +1826,7 @@ Expr = Sizzle.selectors = { matched = fn( seed, argument ), i = matched.length; while ( i-- ) { - idx = indexOf( seed, matched[ i ] ); + idx = indexOf.call( seed, matched[ i ] ); seed[ idx ] = !( matches[ idx ] = matched[ i ] ); } } ) : @@ -2125,14 +1842,14 @@ Expr = Sizzle.selectors = { pseudos: { // Potentially complex pseudos - "not": markFunction( function( selector ) { + not: markFunction( function( selector ) { // Trim the selector passed to compile // to avoid treating leading and trailing // spaces as combinators var input = [], results = [], - matcher = compile( selector.replace( rtrim, "$1" ) ); + matcher = compile( selector.replace( rtrimCSS, "$1" ) ); return matcher[ expando ] ? markFunction( function( seed, matches, _context, xml ) { @@ -2151,22 +1868,23 @@ Expr = Sizzle.selectors = { input[ 0 ] = elem; matcher( input, null, xml, results ); - // Don't keep the element (issue #299) + // Don't keep the element + // (see https://github.com/jquery/sizzle/issues/299) input[ 0 ] = null; return !results.pop(); }; } ), - "has": markFunction( function( selector ) { + has: markFunction( function( selector ) { return function( elem ) { - return Sizzle( selector, elem ).length > 0; + return find( selector, elem ).length > 0; }; } ), - "contains": markFunction( function( text ) { + contains: markFunction( function( text ) { text = text.replace( runescape, funescape ); return function( elem ) { - return ( elem.textContent || getText( elem ) ).indexOf( text ) > -1; + return ( elem.textContent || jQuery.text( elem ) ).indexOf( text ) > -1; }; } ), @@ -2176,12 +1894,12 @@ Expr = Sizzle.selectors = { // or beginning with the identifier C immediately followed by "-". // The matching of C against the element's language value is performed case-insensitively. // The identifier C does not have to be a valid language name." - // http://www.w3.org/TR/selectors/#lang-pseudo - "lang": markFunction( function( lang ) { + // https://www.w3.org/TR/selectors/#lang-pseudo + lang: markFunction( function( lang ) { // lang value must be a valid identifier if ( !ridentifier.test( lang || "" ) ) { - Sizzle.error( "unsupported lang: " + lang ); + find.error( "unsupported lang: " + lang ); } lang = lang.replace( runescape, funescape ).toLowerCase(); return function( elem ) { @@ -2200,38 +1918,39 @@ Expr = Sizzle.selectors = { } ), // Miscellaneous - "target": function( elem ) { + target: function( elem ) { var hash = window.location && window.location.hash; return hash && hash.slice( 1 ) === elem.id; }, - "root": function( elem ) { - return elem === docElem; + root: function( elem ) { + return elem === documentElement; }, - "focus": function( elem ) { - return elem === document.activeElement && - ( !document.hasFocus || document.hasFocus() ) && + focus: function( elem ) { + return elem === safeActiveElement() && + document.hasFocus() && !!( elem.type || elem.href || ~elem.tabIndex ); }, // Boolean properties - "enabled": createDisabledPseudo( false ), - "disabled": createDisabledPseudo( true ), + enabled: createDisabledPseudo( false ), + disabled: createDisabledPseudo( true ), - "checked": function( elem ) { + checked: function( elem ) { // In CSS3, :checked should return both checked and selected elements - // http://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked - var nodeName = elem.nodeName.toLowerCase(); - return ( nodeName === "input" && !!elem.checked ) || - ( nodeName === "option" && !!elem.selected ); + // https://www.w3.org/TR/2011/REC-css3-selectors-20110929/#checked + return ( nodeName( elem, "input" ) && !!elem.checked ) || + ( nodeName( elem, "option" ) && !!elem.selected ); }, - "selected": function( elem ) { + selected: function( elem ) { - // Accessing this property makes selected-by-default - // options in Safari work properly + // Support: IE <=11+ + // Accessing the selectedIndex property + // forces the browser to treat the default option as + // selected when in an optgroup. if ( elem.parentNode ) { // eslint-disable-next-line no-unused-expressions elem.parentNode.selectedIndex; @@ -2241,9 +1960,9 @@ Expr = Sizzle.selectors = { }, // Contents - "empty": function( elem ) { + empty: function( elem ) { - // http://www.w3.org/TR/selectors/#empty-pseudo + // https://www.w3.org/TR/selectors/#empty-pseudo // :empty is negated by element (1) or content nodes (text: 3; cdata: 4; entity ref: 5), // but not by others (comment: 8; processing instruction: 7; etc.) // nodeType < 6 works because attributes (2) do not appear as children @@ -2255,49 +1974,49 @@ Expr = Sizzle.selectors = { return true; }, - "parent": function( elem ) { - return !Expr.pseudos[ "empty" ]( elem ); + parent: function( elem ) { + return !Expr.pseudos.empty( elem ); }, // Element/input types - "header": function( elem ) { + header: function( elem ) { return rheader.test( elem.nodeName ); }, - "input": function( elem ) { + input: function( elem ) { return rinputs.test( elem.nodeName ); }, - "button": function( elem ) { - var name = elem.nodeName.toLowerCase(); - return name === "input" && elem.type === "button" || name === "button"; + button: function( elem ) { + return nodeName( elem, "input" ) && elem.type === "button" || + nodeName( elem, "button" ); }, - "text": function( elem ) { + text: function( elem ) { var attr; - return elem.nodeName.toLowerCase() === "input" && - elem.type === "text" && + return nodeName( elem, "input" ) && elem.type === "text" && // Support: IE <10 only - // New HTML5 attribute values (e.g., "search") appear with elem.type === "text" + // New HTML5 attribute values (e.g., "search") appear + // with elem.type === "text" ( ( attr = elem.getAttribute( "type" ) ) == null || attr.toLowerCase() === "text" ); }, // Position-in-collection - "first": createPositionalPseudo( function() { + first: createPositionalPseudo( function() { return [ 0 ]; } ), - "last": createPositionalPseudo( function( _matchIndexes, length ) { + last: createPositionalPseudo( function( _matchIndexes, length ) { return [ length - 1 ]; } ), - "eq": createPositionalPseudo( function( _matchIndexes, length, argument ) { + eq: createPositionalPseudo( function( _matchIndexes, length, argument ) { return [ argument < 0 ? argument + length : argument ]; } ), - "even": createPositionalPseudo( function( matchIndexes, length ) { + even: createPositionalPseudo( function( matchIndexes, length ) { var i = 0; for ( ; i < length; i += 2 ) { matchIndexes.push( i ); @@ -2305,7 +2024,7 @@ Expr = Sizzle.selectors = { return matchIndexes; } ), - "odd": createPositionalPseudo( function( matchIndexes, length ) { + odd: createPositionalPseudo( function( matchIndexes, length ) { var i = 1; for ( ; i < length; i += 2 ) { matchIndexes.push( i ); @@ -2313,19 +2032,24 @@ Expr = Sizzle.selectors = { return matchIndexes; } ), - "lt": createPositionalPseudo( function( matchIndexes, length, argument ) { - var i = argument < 0 ? - argument + length : - argument > length ? - length : - argument; + lt: createPositionalPseudo( function( matchIndexes, length, argument ) { + var i; + + if ( argument < 0 ) { + i = argument + length; + } else if ( argument > length ) { + i = length; + } else { + i = argument; + } + for ( ; --i >= 0; ) { matchIndexes.push( i ); } return matchIndexes; } ), - "gt": createPositionalPseudo( function( matchIndexes, length, argument ) { + gt: createPositionalPseudo( function( matchIndexes, length, argument ) { var i = argument < 0 ? argument + length : argument; for ( ; ++i < length; ) { matchIndexes.push( i ); @@ -2335,7 +2059,7 @@ Expr = Sizzle.selectors = { } }; -Expr.pseudos[ "nth" ] = Expr.pseudos[ "eq" ]; +Expr.pseudos.nth = Expr.pseudos.eq; // Add button/input type pseudos for ( i in { radio: true, checkbox: true, file: true, password: true, image: true } ) { @@ -2350,7 +2074,7 @@ function setFilters() {} setFilters.prototype = Expr.filters = Expr.pseudos; Expr.setFilters = new setFilters(); -tokenize = Sizzle.tokenize = function( selector, parseOnly ) { +function tokenize( selector, parseOnly ) { var matched, match, tokens, type, soFar, groups, preFilters, cached = tokenCache[ selector + " " ]; @@ -2384,7 +2108,7 @@ tokenize = Sizzle.tokenize = function( selector, parseOnly ) { value: matched, // Cast descendant combinators to space - type: match[ 0 ].replace( rtrim, " " ) + type: match[ 0 ].replace( rtrimCSS, " " ) } ); soFar = soFar.slice( matched.length ); } @@ -2411,14 +2135,16 @@ tokenize = Sizzle.tokenize = function( selector, parseOnly ) { // Return the length of the invalid excess // if we're just parsing // Otherwise, throw an error or return tokens - return parseOnly ? - soFar.length : - soFar ? - Sizzle.error( selector ) : + if ( parseOnly ) { + return soFar.length; + } - // Cache the tokens - tokenCache( selector, groups ).slice( 0 ); -}; + return soFar ? + find.error( selector ) : + + // Cache the tokens + tokenCache( selector, groups ).slice( 0 ); +} function toSelector( tokens ) { var i = 0, @@ -2451,7 +2177,7 @@ function addCombinator( matcher, combinator, base ) { // Check against all ancestor/preceding elements function( elem, context, xml ) { - var oldCache, uniqueCache, outerCache, + var oldCache, outerCache, newCache = [ dirruns, doneName ]; // We can't set arbitrary data on XML nodes, so they don't benefit from combinator caching @@ -2468,14 +2194,9 @@ function addCombinator( matcher, combinator, base ) { if ( elem.nodeType === 1 || checkNonElements ) { outerCache = elem[ expando ] || ( elem[ expando ] = {} ); - // Support: IE <9 only - // Defend against cloned attroperties (jQuery gh-1709) - uniqueCache = outerCache[ elem.uniqueID ] || - ( outerCache[ elem.uniqueID ] = {} ); - - if ( skip && skip === elem.nodeName.toLowerCase() ) { + if ( skip && nodeName( elem, skip ) ) { elem = elem[ dir ] || elem; - } else if ( ( oldCache = uniqueCache[ key ] ) && + } else if ( ( oldCache = outerCache[ key ] ) && oldCache[ 0 ] === dirruns && oldCache[ 1 ] === doneName ) { // Assign to newCache so results back-propagate to previous elements @@ -2483,7 +2204,7 @@ function addCombinator( matcher, combinator, base ) { } else { // Reuse newcache so results back-propagate to previous elements - uniqueCache[ key ] = newCache; + outerCache[ key ] = newCache; // A match means we're done; a fail means we have to keep checking if ( ( newCache[ 2 ] = matcher( elem, context, xml ) ) ) { @@ -2515,7 +2236,7 @@ function multipleContexts( selector, contexts, results ) { var i = 0, len = contexts.length; for ( ; i < len; i++ ) { - Sizzle( selector, contexts[ i ], results ); + find( selector, contexts[ i ], results ); } return results; } @@ -2549,38 +2270,37 @@ function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postS postFinder = setMatcher( postFinder, postSelector ); } return markFunction( function( seed, results, context, xml ) { - var temp, i, elem, + var temp, i, elem, matcherOut, preMap = [], postMap = [], preexisting = results.length, // Get initial elements from seed or context - elems = seed || multipleContexts( - selector || "*", - context.nodeType ? [ context ] : context, - [] - ), + elems = seed || + multipleContexts( selector || "*", + context.nodeType ? [ context ] : context, [] ), // Prefilter to get matcher input, preserving a map for seed-results synchronization matcherIn = preFilter && ( seed || !selector ) ? condense( elems, preMap, preFilter, context, xml ) : - elems, + elems; - matcherOut = matcher ? + if ( matcher ) { - // If we have a postFinder, or filtered seed, or non-seed postFilter or preexisting results, - postFinder || ( seed ? preFilter : preexisting || postFilter ) ? + // If we have a postFinder, or filtered seed, or non-seed postFilter + // or preexisting results, + matcherOut = postFinder || ( seed ? preFilter : preexisting || postFilter ) ? - // ...intermediate processing is necessary - [] : + // ...intermediate processing is necessary + [] : - // ...otherwise use results directly - results : - matcherIn; + // ...otherwise use results directly + results; - // Find primary matches - if ( matcher ) { + // Find primary matches matcher( matcherIn, matcherOut, context, xml ); + } else { + matcherOut = matcherIn; } // Apply postFilter @@ -2618,7 +2338,7 @@ function setMatcher( preFilter, selector, matcher, postFilter, postFinder, postS i = matcherOut.length; while ( i-- ) { if ( ( elem = matcherOut[ i ] ) && - ( temp = postFinder ? indexOf( seed, elem ) : preMap[ i ] ) > -1 ) { + ( temp = postFinder ? indexOf.call( seed, elem ) : preMap[ i ] ) > -1 ) { seed[ temp ] = !( results[ temp ] = elem ); } @@ -2653,15 +2373,21 @@ function matcherFromTokens( tokens ) { return elem === checkContext; }, implicitRelative, true ), matchAnyContext = addCombinator( function( elem ) { - return indexOf( checkContext, elem ) > -1; + return indexOf.call( checkContext, elem ) > -1; }, implicitRelative, true ), matchers = [ function( elem, context, xml ) { - var ret = ( !leadingRelative && ( xml || context !== outermostContext ) ) || ( + + // Support: IE 11+, Edge 17 - 18+ + // IE/Edge sometimes throw a "Permission denied" error when strict-comparing + // two documents; shallow comparisons work. + // eslint-disable-next-line eqeqeq + var ret = ( !leadingRelative && ( xml || context != outermostContext ) ) || ( ( checkContext = context ).nodeType ? matchContext( elem, context, xml ) : matchAnyContext( elem, context, xml ) ); - // Avoid hanging onto element (issue #299) + // Avoid hanging onto element + // (see https://github.com/jquery/sizzle/issues/299) checkContext = null; return ret; } ]; @@ -2686,11 +2412,10 @@ function matcherFromTokens( tokens ) { i > 1 && elementMatcher( matchers ), i > 1 && toSelector( - // If the preceding token was a descendant combinator, insert an implicit any-element `*` - tokens - .slice( 0, i - 1 ) - .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) - ).replace( rtrim, "$1" ), + // If the preceding token was a descendant combinator, insert an implicit any-element `*` + tokens.slice( 0, i - 1 ) + .concat( { value: tokens[ i - 2 ].type === " " ? "*" : "" } ) + ).replace( rtrimCSS, "$1" ), matcher, i < j && matcherFromTokens( tokens.slice( i, j ) ), j < len && matcherFromTokens( ( tokens = tokens.slice( j ) ) ), @@ -2716,7 +2441,7 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) { contextBackup = outermostContext, // We must always have either seed elements or outermost context - elems = seed || byElement && Expr.find[ "TAG" ]( "*", outermost ), + elems = seed || byElement && Expr.find.TAG( "*", outermost ), // Use integer dirruns iff this is the outermost matcher dirrunsUnique = ( dirruns += contextBackup == null ? 1 : Math.random() || 0.1 ), @@ -2732,8 +2457,9 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) { } // Add elements passing elementMatchers directly to results - // Support: IE<9, Safari - // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching elements by id + // Support: iOS <=7 - 9 only + // Tolerate NodeList properties (IE: "length"; Safari: <number>) matching + // elements by id. (see trac-14142) for ( ; i !== len && ( elem = elems[ i ] ) != null; i++ ) { if ( byElement && elem ) { j = 0; @@ -2748,7 +2474,7 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) { } while ( ( matcher = elementMatchers[ j++ ] ) ) { if ( matcher( elem, context || document, xml ) ) { - results.push( elem ); + push.call( results, elem ); break; } } @@ -2811,7 +2537,7 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) { if ( outermost && !seed && setMatched.length > 0 && ( matchedCount + setMatchers.length ) > 1 ) { - Sizzle.uniqueSort( results ); + jQuery.uniqueSort( results ); } } @@ -2829,7 +2555,7 @@ function matcherFromGroupMatchers( elementMatchers, setMatchers ) { superMatcher; } -compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { +function compile( selector, match /* Internal Use Only */ ) { var i, setMatchers = [], elementMatchers = [], @@ -2852,27 +2578,25 @@ compile = Sizzle.compile = function( selector, match /* Internal Use Only */ ) { } // Cache the compiled function - cached = compilerCache( - selector, - matcherFromGroupMatchers( elementMatchers, setMatchers ) - ); + cached = compilerCache( selector, + matcherFromGroupMatchers( elementMatchers, setMatchers ) ); // Save selector and tokenization cached.selector = selector; } return cached; -}; +} /** - * A low-level selection function that works with Sizzle's compiled + * A low-level selection function that works with jQuery's compiled * selector functions * @param {String|Function} selector A selector or a pre-compiled - * selector function built with Sizzle.compile + * selector function built with jQuery selector compile * @param {Element} context * @param {Array} [results] * @param {Array} [seed] A set of elements to match against */ -select = Sizzle.select = function( selector, context, results, seed ) { +function select( selector, context, results, seed ) { var i, tokens, token, type, find, compiled = typeof selector === "function" && selector, match = !seed && tokenize( ( selector = compiled.selector || selector ) ); @@ -2886,10 +2610,12 @@ select = Sizzle.select = function( selector, context, results, seed ) { // Reduce context if the leading compound selector is an ID tokens = match[ 0 ] = match[ 0 ].slice( 0 ); if ( tokens.length > 2 && ( token = tokens[ 0 ] ).type === "ID" && - context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { + context.nodeType === 9 && documentIsHTML && Expr.relative[ tokens[ 1 ].type ] ) { - context = ( Expr.find[ "ID" ]( token.matches[ 0 ] - .replace( runescape, funescape ), context ) || [] )[ 0 ]; + context = ( Expr.find.ID( + token.matches[ 0 ].replace( runescape, funescape ), + context + ) || [] )[ 0 ]; if ( !context ) { return results; @@ -2902,7 +2628,7 @@ select = Sizzle.select = function( selector, context, results, seed ) { } // Fetch a seed set for right-to-left matching - i = matchExpr[ "needsContext" ].test( selector ) ? 0 : tokens.length; + i = matchExpr.needsContext.test( selector ) ? 0 : tokens.length; while ( i-- ) { token = tokens[ i ]; @@ -2915,8 +2641,8 @@ select = Sizzle.select = function( selector, context, results, seed ) { // Search, expanding context for leading sibling combinators if ( ( seed = find( token.matches[ 0 ].replace( runescape, funescape ), - rsibling.test( tokens[ 0 ].type ) && testContext( context.parentNode ) || - context + rsibling.test( tokens[ 0 ].type ) && + testContext( context.parentNode ) || context ) ) ) { // If seed is empty or no tokens remain, we can return early @@ -2943,21 +2669,18 @@ select = Sizzle.select = function( selector, context, results, seed ) { !context || rsibling.test( selector ) && testContext( context.parentNode ) || context ); return results; -}; +} // One-time assignments +// Support: Android <=4.0 - 4.1+ // Sort stability support.sortStable = expando.split( "" ).sort( sortOrder ).join( "" ) === expando; -// Support: Chrome 14-35+ -// Always assume duplicates if they aren't passed to the comparison function -support.detectDuplicates = !!hasDuplicate; - // Initialize against the default document setDocument(); -// Support: Webkit<537.32 - Safari 6.0.3/Chrome 25 (fixed in Chrome 27) +// Support: Android <=4.0 - 4.1+ // Detached nodes confoundingly follow *each other* support.sortDetached = assert( function( el ) { @@ -2965,68 +2688,29 @@ support.sortDetached = assert( function( el ) { return el.compareDocumentPosition( document.createElement( "fieldset" ) ) & 1; } ); -// Support: IE<8 -// Prevent attribute/property "interpolation" -// https://msdn.microsoft.com/en-us/library/ms536429%28VS.85%29.aspx -if ( !assert( function( el ) { - el.innerHTML = "<a href='#'></a>"; - return el.firstChild.getAttribute( "href" ) === "#"; -} ) ) { - addHandle( "type|href|height|width", function( elem, name, isXML ) { - if ( !isXML ) { - return elem.getAttribute( name, name.toLowerCase() === "type" ? 1 : 2 ); - } - } ); -} - -// Support: IE<9 -// Use defaultValue in place of getAttribute("value") -if ( !support.attributes || !assert( function( el ) { - el.innerHTML = "<input/>"; - el.firstChild.setAttribute( "value", "" ); - return el.firstChild.getAttribute( "value" ) === ""; -} ) ) { - addHandle( "value", function( elem, _name, isXML ) { - if ( !isXML && elem.nodeName.toLowerCase() === "input" ) { - return elem.defaultValue; - } - } ); -} - -// Support: IE<9 -// Use getAttributeNode to fetch booleans when getAttribute lies -if ( !assert( function( el ) { - return el.getAttribute( "disabled" ) == null; -} ) ) { - addHandle( booleans, function( elem, name, isXML ) { - var val; - if ( !isXML ) { - return elem[ name ] === true ? name.toLowerCase() : - ( val = elem.getAttributeNode( name ) ) && val.specified ? - val.value : - null; - } - } ); -} - -return Sizzle; - -} )( window ); - - - -jQuery.find = Sizzle; -jQuery.expr = Sizzle.selectors; +jQuery.find = find; // Deprecated jQuery.expr[ ":" ] = jQuery.expr.pseudos; -jQuery.uniqueSort = jQuery.unique = Sizzle.uniqueSort; -jQuery.text = Sizzle.getText; -jQuery.isXMLDoc = Sizzle.isXML; -jQuery.contains = Sizzle.contains; -jQuery.escapeSelector = Sizzle.escape; +jQuery.unique = jQuery.uniqueSort; +// These have always been private, but they used to be documented +// as part of Sizzle so let's maintain them in the 3.x line +// for backwards compatibility purposes. +find.compile = compile; +find.select = select; +find.setDocument = setDocument; +find.escape = jQuery.escapeSelector; +find.getText = jQuery.text; +find.isXML = jQuery.isXMLDoc; +find.selectors = jQuery.expr; +find.support = jQuery.support; +find.uniqueSort = jQuery.uniqueSort; + + /* eslint-enable */ + +} )(); var dir = function( elem, dir, until ) { @@ -3060,13 +2744,6 @@ var siblings = function( n, elem ) { var rneedsContext = jQuery.expr.match.needsContext; - - -function nodeName( elem, name ) { - - return elem.nodeName && elem.nodeName.toLowerCase() === name.toLowerCase(); - -} var rsingleTag = ( /^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i ); @@ -3317,7 +2994,7 @@ jQuery.fn.extend( { if ( cur.nodeType < 11 && ( targets ? targets.index( cur ) > -1 : - // Don't pass non-elements to Sizzle + // Don't pass non-elements to jQuery#find cur.nodeType === 1 && jQuery.find.matchesSelector( cur, selectors ) ) ) { @@ -3872,7 +3549,7 @@ jQuery.extend( { if ( jQuery.Deferred.exceptionHook ) { jQuery.Deferred.exceptionHook( e, - process.stackTrace ); + process.error ); } // Support: Promises/A+ section 2.3.3.3.4.1 @@ -3900,10 +3577,17 @@ jQuery.extend( { process(); } else { - // Call an optional hook to record the stack, in case of exception + // Call an optional hook to record the error, in case of exception // since it's otherwise lost when execution goes async - if ( jQuery.Deferred.getStackHook ) { - process.stackTrace = jQuery.Deferred.getStackHook(); + if ( jQuery.Deferred.getErrorHook ) { + process.error = jQuery.Deferred.getErrorHook(); + + // The deprecated alias of the above. While the name suggests + // returning the stack, not an error instance, jQuery just passes + // it directly to `console.warn` so both will work; an instance + // just better cooperates with source maps. + } else if ( jQuery.Deferred.getStackHook ) { + process.error = jQuery.Deferred.getStackHook(); } window.setTimeout( process ); } @@ -4078,12 +3762,16 @@ jQuery.extend( { // warn about them ASAP rather than swallowing them by default. var rerrorNames = /^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/; -jQuery.Deferred.exceptionHook = function( error, stack ) { +// If `jQuery.Deferred.getErrorHook` is defined, `asyncError` is an error +// captured before the async barrier to get the original error cause +// which may otherwise be hidden. +jQuery.Deferred.exceptionHook = function( error, asyncError ) { // Support: IE 8 - 9 only // Console exists when dev tools are open, which can happen at any time if ( window.console && window.console.warn && error && rerrorNames.test( error.name ) ) { - window.console.warn( "jQuery.Deferred exception: " + error.message, error.stack, stack ); + window.console.warn( "jQuery.Deferred exception: " + error.message, + error.stack, asyncError ); } }; @@ -5139,25 +4827,6 @@ function returnFalse() { return false; } -// Support: IE <=9 - 11+ -// focus() and blur() are asynchronous, except when they are no-op. -// So expect focus to be synchronous when the element is already active, -// and blur to be synchronous when the element is not already active. -// (focus and blur are always synchronous in other supported browsers, -// this just defines when we can count on it). -function expectSync( elem, type ) { - return ( elem === safeActiveElement() ) === ( type === "focus" ); -} - -// Support: IE <=9 only -// Accessing document.activeElement can throw unexpectedly -// https://bugs.jquery.com/ticket/13393 -function safeActiveElement() { - try { - return document.activeElement; - } catch ( err ) { } -} - function on( elem, types, selector, data, fn, one ) { var origFn, type; @@ -5595,7 +5264,7 @@ jQuery.event = { el.click && nodeName( el, "input" ) ) { // dataPriv.set( el, "click", ... ) - leverageNative( el, "click", returnTrue ); + leverageNative( el, "click", true ); } // Return false to allow normal processing in the caller @@ -5646,10 +5315,10 @@ jQuery.event = { // synthetic events by interrupting progress until reinvoked in response to // *native* events that it fires directly, ensuring that state changes have // already occurred before other listeners are invoked. -function leverageNative( el, type, expectSync ) { +function leverageNative( el, type, isSetup ) { - // Missing expectSync indicates a trigger call, which must force setup through jQuery.event.add - if ( !expectSync ) { + // Missing `isSetup` indicates a trigger call, which must force setup through jQuery.event.add + if ( !isSetup ) { if ( dataPriv.get( el, type ) === undefined ) { jQuery.event.add( el, type, returnTrue ); } @@ -5661,15 +5330,13 @@ function leverageNative( el, type, expectSync ) { jQuery.event.add( el, type, { namespace: false, handler: function( event ) { - var notAsync, result, + var result, saved = dataPriv.get( this, type ); if ( ( event.isTrigger & 1 ) && this[ type ] ) { // Interrupt processing of the outer synthetic .trigger()ed event - // Saved data should be false in such cases, but might be a leftover capture object - // from an async native handler (gh-4350) - if ( !saved.length ) { + if ( !saved ) { // Store arguments for use when handling the inner native event // There will always be at least one argument (an event object), so this array @@ -5678,33 +5345,22 @@ function leverageNative( el, type, expectSync ) { dataPriv.set( this, type, saved ); // Trigger the native event and capture its result - // Support: IE <=9 - 11+ - // focus() and blur() are asynchronous - notAsync = expectSync( this, type ); this[ type ](); result = dataPriv.get( this, type ); - if ( saved !== result || notAsync ) { - dataPriv.set( this, type, false ); - } else { - result = {}; - } + dataPriv.set( this, type, false ); + if ( saved !== result ) { // Cancel the outer synthetic event event.stopImmediatePropagation(); event.preventDefault(); - // Support: Chrome 86+ - // In Chrome, if an element having a focusout handler is blurred by - // clicking outside of it, it invokes the handler synchronously. If - // that handler calls `.remove()` on the element, the data is cleared, - // leaving `result` undefined. We need to guard against this. - return result && result.value; + return result; } // If this is an inner synthetic event for an event with a bubbling surrogate - // (focus or blur), assume that the surrogate already propagated from triggering the - // native event and prevent that from happening again here. + // (focus or blur), assume that the surrogate already propagated from triggering + // the native event and prevent that from happening again here. // This technically gets the ordering wrong w.r.t. to `.trigger()` (in which the // bubbling surrogate propagates *after* the non-bubbling base), but that seems // less bad than duplication. @@ -5714,22 +5370,25 @@ function leverageNative( el, type, expectSync ) { // If this is a native event triggered above, everything is now in order // Fire an inner synthetic event with the original arguments - } else if ( saved.length ) { + } else if ( saved ) { // ...and capture the result - dataPriv.set( this, type, { - value: jQuery.event.trigger( - - // Support: IE <=9 - 11+ - // Extend with the prototype to reset the above stopImmediatePropagation() - jQuery.extend( saved[ 0 ], jQuery.Event.prototype ), - saved.slice( 1 ), - this - ) - } ); - - // Abort handling of the native event - event.stopImmediatePropagation(); + dataPriv.set( this, type, jQuery.event.trigger( + saved[ 0 ], + saved.slice( 1 ), + this + ) ); + + // Abort handling of the native event by all jQuery handlers while allowing + // native handlers on the same element to run. On target, this is achieved + // by stopping immediate propagation just on the jQuery event. However, + // the native event is re-wrapped by a jQuery one on each level of the + // propagation so the only way to stop it for jQuery is to stop it for + // everyone via native `stopPropagation()`. This is not a problem for + // focus/blur which don't bubble, but it does also stop click on checkboxes + // and radios. We accept this limitation. + event.stopPropagation(); + event.isImmediatePropagationStopped = returnTrue; } } } ); @@ -5868,18 +5527,73 @@ jQuery.each( { }, jQuery.event.addProp ); jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateType ) { + + function focusMappedHandler( nativeEvent ) { + if ( document.documentMode ) { + + // Support: IE 11+ + // Attach a single focusin/focusout handler on the document while someone wants + // focus/blur. This is because the former are synchronous in IE while the latter + // are async. In other browsers, all those handlers are invoked synchronously. + + // `handle` from private data would already wrap the event, but we need + // to change the `type` here. + var handle = dataPriv.get( this, "handle" ), + event = jQuery.event.fix( nativeEvent ); + event.type = nativeEvent.type === "focusin" ? "focus" : "blur"; + event.isSimulated = true; + + // First, handle focusin/focusout + handle( nativeEvent ); + + // ...then, handle focus/blur + // + // focus/blur don't bubble while focusin/focusout do; simulate the former by only + // invoking the handler at the lower level. + if ( event.target === event.currentTarget ) { + + // The setup part calls `leverageNative`, which, in turn, calls + // `jQuery.event.add`, so event handle will already have been set + // by this point. + handle( event ); + } + } else { + + // For non-IE browsers, attach a single capturing handler on the document + // while someone wants focusin/focusout. + jQuery.event.simulate( delegateType, nativeEvent.target, + jQuery.event.fix( nativeEvent ) ); + } + } + jQuery.event.special[ type ] = { // Utilize native event if possible so blur/focus sequence is correct setup: function() { + var attaches; + // Claim the first handler // dataPriv.set( this, "focus", ... ) // dataPriv.set( this, "blur", ... ) - leverageNative( this, type, expectSync ); + leverageNative( this, type, true ); - // Return false to allow normal processing in the caller - return false; + if ( document.documentMode ) { + + // Support: IE 9 - 11+ + // We use the same native handler for focusin & focus (and focusout & blur) + // so we need to coordinate setup & teardown parts between those events. + // Use `delegateType` as the key as `type` is already used by `leverageNative`. + attaches = dataPriv.get( this, delegateType ); + if ( !attaches ) { + this.addEventListener( delegateType, focusMappedHandler ); + } + dataPriv.set( this, delegateType, ( attaches || 0 ) + 1 ); + } else { + + // Return false to allow normal processing in the caller + return false; + } }, trigger: function() { @@ -5890,6 +5604,24 @@ jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateTyp return true; }, + teardown: function() { + var attaches; + + if ( document.documentMode ) { + attaches = dataPriv.get( this, delegateType ) - 1; + if ( !attaches ) { + this.removeEventListener( delegateType, focusMappedHandler ); + dataPriv.remove( this, delegateType ); + } else { + dataPriv.set( this, delegateType, attaches ); + } + } else { + + // Return false to indicate standard teardown should be applied + return false; + } + }, + // Suppress native focus or blur if we're currently inside // a leveraged native-event stack _default: function( event ) { @@ -5898,6 +5630,58 @@ jQuery.each( { focus: "focusin", blur: "focusout" }, function( type, delegateTyp delegateType: delegateType }; + + // Support: Firefox <=44 + // Firefox doesn't have focus(in | out) events + // Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 + // + // Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 + // focus(in | out) events fire after focus & blur events, + // which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order + // Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 + // + // Support: IE 9 - 11+ + // To preserve relative focusin/focus & focusout/blur event order guaranteed on the 3.x branch, + // attach a single handler for both events in IE. + jQuery.event.special[ delegateType ] = { + setup: function() { + + // Handle: regular nodes (via `this.ownerDocument`), window + // (via `this.document`) & document (via `this`). + var doc = this.ownerDocument || this.document || this, + dataHolder = document.documentMode ? this : doc, + attaches = dataPriv.get( dataHolder, delegateType ); + + // Support: IE 9 - 11+ + // We use the same native handler for focusin & focus (and focusout & blur) + // so we need to coordinate setup & teardown parts between those events. + // Use `delegateType` as the key as `type` is already used by `leverageNative`. + if ( !attaches ) { + if ( document.documentMode ) { + this.addEventListener( delegateType, focusMappedHandler ); + } else { + doc.addEventListener( type, focusMappedHandler, true ); + } + } + dataPriv.set( dataHolder, delegateType, ( attaches || 0 ) + 1 ); + }, + teardown: function() { + var doc = this.ownerDocument || this.document || this, + dataHolder = document.documentMode ? this : doc, + attaches = dataPriv.get( dataHolder, delegateType ) - 1; + + if ( !attaches ) { + if ( document.documentMode ) { + this.removeEventListener( delegateType, focusMappedHandler ); + } else { + doc.removeEventListener( type, focusMappedHandler, true ); + } + dataPriv.remove( dataHolder, delegateType ); + } else { + dataPriv.set( dataHolder, delegateType, attaches ); + } + } + }; } ); // Create mouseenter/leave events using mouseover/out and event-time checks @@ -6200,7 +5984,8 @@ jQuery.extend( { if ( !support.noCloneChecked && ( elem.nodeType === 1 || elem.nodeType === 11 ) && !jQuery.isXMLDoc( elem ) ) { - // We eschew Sizzle here for performance reasons: https://jsperf.com/getall-vs-sizzle/2 + // We eschew jQuery#find here for performance reasons: + // https://jsperf.com/getall-vs-sizzle/2 destElements = getAll( clone ); srcElements = getAll( elem ); @@ -6476,15 +6261,6 @@ var swap = function( elem, options, callback ) { var rboxStyle = new RegExp( cssExpand.join( "|" ), "i" ); -var whitespace = "[\\x20\\t\\r\\n\\f]"; - - -var rtrimCSS = new RegExp( - "^" + whitespace + "+|((?:^|[^\\\\])(?:\\\\.)*)" + whitespace + "+$", - "g" -); - - ( function() { @@ -6793,7 +6569,8 @@ function setPositiveNumber( _elem, value, subtract ) { function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computedVal ) { var i = dimension === "width" ? 1 : 0, extra = 0, - delta = 0; + delta = 0, + marginDelta = 0; // Adjustment may not be necessary if ( box === ( isBorderBox ? "border" : "content" ) ) { @@ -6803,8 +6580,10 @@ function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computed for ( ; i < 4; i += 2 ) { // Both box models exclude margin + // Count margin delta separately to only add it after scroll gutter adjustment. + // This is needed to make negative margins work with `outerHeight( true )` (gh-3982). if ( box === "margin" ) { - delta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); + marginDelta += jQuery.css( elem, box + cssExpand[ i ], true, styles ); } // If we get here with a content-box, we're seeking "padding" or "border" or "margin" @@ -6855,7 +6634,7 @@ function boxModelAdjustment( elem, dimension, box, isBorderBox, styles, computed ) ) || 0; } - return delta; + return delta + marginDelta; } function getWidthOrHeight( elem, dimension, extra ) { @@ -6953,26 +6732,35 @@ jQuery.extend( { // Don't automatically add "px" to these possibly-unitless properties cssNumber: { - "animationIterationCount": true, - "columnCount": true, - "fillOpacity": true, - "flexGrow": true, - "flexShrink": true, - "fontWeight": true, - "gridArea": true, - "gridColumn": true, - "gridColumnEnd": true, - "gridColumnStart": true, - "gridRow": true, - "gridRowEnd": true, - "gridRowStart": true, - "lineHeight": true, - "opacity": true, - "order": true, - "orphans": true, - "widows": true, - "zIndex": true, - "zoom": true + animationIterationCount: true, + aspectRatio: true, + borderImageSlice: true, + columnCount: true, + flexGrow: true, + flexShrink: true, + fontWeight: true, + gridArea: true, + gridColumn: true, + gridColumnEnd: true, + gridColumnStart: true, + gridRow: true, + gridRowEnd: true, + gridRowStart: true, + lineHeight: true, + opacity: true, + order: true, + orphans: true, + scale: true, + widows: true, + zIndex: true, + zoom: true, + + // SVG-related + fillOpacity: true, + floodOpacity: true, + stopOpacity: true, + strokeMiterlimit: true, + strokeOpacity: true }, // Add in properties whose names you wish to fix before @@ -7906,7 +7694,31 @@ jQuery.each( [ "radio", "checkbox" ], function() { // Return jQuery for attributes-only inclusion -support.focusin = "onfocusin" in window; +// Cross-browser xml parsing +jQuery.parseXML = function( data ) { + var xml, parserErrorElem; + if ( !data || typeof data !== "string" ) { + return null; + } + + // Support: IE 9 - 11 only + // IE throws on parseFromString with invalid input. + try { + xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); + } catch ( e ) {} + + parserErrorElem = xml && xml.getElementsByTagName( "parsererror" )[ 0 ]; + if ( !xml || parserErrorElem ) { + jQuery.error( "Invalid XML: " + ( + parserErrorElem ? + jQuery.map( parserErrorElem.childNodes, function( el ) { + return el.textContent; + } ).join( "\n" ) : + data + ) ); + } + return xml; +}; var rfocusMorph = /^(?:focusinfocus|focusoutblur)$/, @@ -8094,79 +7906,6 @@ jQuery.fn.extend( { } ); -// Support: Firefox <=44 -// Firefox doesn't have focus(in | out) events -// Related ticket - https://bugzilla.mozilla.org/show_bug.cgi?id=687787 -// -// Support: Chrome <=48 - 49, Safari <=9.0 - 9.1 -// focus(in | out) events fire after focus & blur events, -// which is spec violation - http://www.w3.org/TR/DOM-Level-3-Events/#events-focusevent-event-order -// Related ticket - https://bugs.chromium.org/p/chromium/issues/detail?id=449857 -if ( !support.focusin ) { - jQuery.each( { focus: "focusin", blur: "focusout" }, function( orig, fix ) { - - // Attach a single capturing handler on the document while someone wants focusin/focusout - var handler = function( event ) { - jQuery.event.simulate( fix, event.target, jQuery.event.fix( event ) ); - }; - - jQuery.event.special[ fix ] = { - setup: function() { - - // Handle: regular nodes (via `this.ownerDocument`), window - // (via `this.document`) & document (via `this`). - var doc = this.ownerDocument || this.document || this, - attaches = dataPriv.access( doc, fix ); - - if ( !attaches ) { - doc.addEventListener( orig, handler, true ); - } - dataPriv.access( doc, fix, ( attaches || 0 ) + 1 ); - }, - teardown: function() { - var doc = this.ownerDocument || this.document || this, - attaches = dataPriv.access( doc, fix ) - 1; - - if ( !attaches ) { - doc.removeEventListener( orig, handler, true ); - dataPriv.remove( doc, fix ); - - } else { - dataPriv.access( doc, fix, attaches ); - } - } - }; - } ); -} - - -// Cross-browser xml parsing -jQuery.parseXML = function( data ) { - var xml, parserErrorElem; - if ( !data || typeof data !== "string" ) { - return null; - } - - // Support: IE 9 - 11 only - // IE throws on parseFromString with invalid input. - try { - xml = ( new window.DOMParser() ).parseFromString( data, "text/xml" ); - } catch ( e ) {} - - parserErrorElem = xml && xml.getElementsByTagName( "parsererror" )[ 0 ]; - if ( !xml || parserErrorElem ) { - jQuery.error( "Invalid XML: " + ( - parserErrorElem ? - jQuery.map( parserErrorElem.childNodes, function( el ) { - return el.textContent; - } ).join( "\n" ) : - data - ) ); - } - return xml; -}; - - var rbracket = /\[\]$/, rCRLF = /\r?\n/g, diff --git a/src/static/templates/admin/base.hbs b/src/static/templates/admin/base.hbs index 2fe1ee54..5e713105 100644 --- a/src/static/templates/admin/base.hbs +++ b/src/static/templates/admin/base.hbs @@ -1,5 +1,5 @@ <!DOCTYPE html> -<html lang="en"> +<html lang="en" data-bs-theme="auto"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1" /> @@ -10,17 +10,17 @@ <link rel="stylesheet" href="{{urlpath}}/vw_static/admin.css" /> <script src="{{urlpath}}/vw_static/admin.js"></script> </head> -<body class="bg-light"> +<body> <nav class="navbar navbar-expand-md navbar-dark bg-dark mb-4 shadow fixed-top"> <div class="container-xl"> <a class="navbar-brand" href="{{urlpath}}/admin"><img class="vaultwarden-icon" src="{{urlpath}}/vw_static/vaultwarden-icon.png" alt="V">aultwarden Admin</a> <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" - aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation"> + aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <div class="collapse navbar-collapse" id="navbarCollapse"> <ul class="navbar-nav me-auto"> - {{#if logged_in}} + {{#if logged_in}} <li class="nav-item"> <a class="nav-link" href="{{urlpath}}/admin">Settings</a> </li> @@ -33,15 +33,59 @@ <li class="nav-item"> <a class="nav-link" href="{{urlpath}}/admin/diagnostics">Diagnostics</a> </li> - {{/if}} + {{/if}} <li class="nav-item"> <a class="nav-link" href="{{urlpath}}/" target="_blank" rel="noreferrer">Vault</a> </li> </ul> + <ul class="navbar-nav"> + <li class="nav-item dropdown"> + <button + class="btn btn-link nav-link py-0 px-0 px-md-2 dropdown-toggle d-flex align-items-center" + id="bd-theme" type="button" aria-expanded="false" data-bs-toggle="dropdown" + data-bs-display="static" aria-label="Toggle theme (auto)"> + <span class="my-1 fs-4 theme-icon-active"> + <use>☯</use> + </span> + <span class="d-md-none ms-2" id="bd-theme-text">Toggle theme</span> + </button> + <ul class="dropdown-menu dropdown-menu-end" aria-labelledby="bd-theme-text"> + <li> + <button type="button" class="dropdown-item d-flex align-items-center" + data-bs-theme-value="light" aria-pressed="false"> + <span class="me-2 fs-4 theme-icon"> + <use>☀</use> + </span> + Light + </button> + </li> + <li> + <button type="button" class="dropdown-item d-flex align-items-center" + data-bs-theme-value="dark" aria-pressed="false"> + <span class="me-2 fs-4 theme-icon"> + <use>★</use> + </span> + Dark + </button> + </li> + <li> + <button type="button" class="dropdown-item d-flex align-items-center active" + data-bs-theme-value="auto" aria-pressed="true"> + <span class="me-2 fs-4 theme-icon"> + <use>☯</use> + </span> + Auto + </button> + </li> + </ul> + </li> + </ul> + {{#if logged_in}} - <a class="btn btn-sm btn-secondary" href="{{urlpath}}/admin/logout">Log Out</a> + <a class="btn btn-sm btn-secondary" href="{{urlpath}}/admin/logout">Log Out</a> {{/if}} + </div> </div> </nav> @@ -49,6 +93,6 @@ {{> (lookup this "page_content") }} <!-- This script needs to be at the bottom, else it will fail! --> - <script src="{{urlpath}}/vw_static/bootstrap-native.js"></script> + <script src="{{urlpath}}/vw_static/bootstrap.bundle.js"></script> </body> </html> diff --git a/src/static/templates/admin/diagnostics.hbs b/src/static/templates/admin/diagnostics.hbs index a61d8992..58c2889c 100644 --- a/src/static/templates/admin/diagnostics.hbs +++ b/src/static/templates/admin/diagnostics.hbs @@ -1,5 +1,5 @@ <main class="container-xl"> - <div id="diagnostics-block" class="my-3 p-3 bg-white rounded shadow"> + <div id="diagnostics-block" class="my-3 p-3 rounded shadow"> <h6 class="border-bottom pb-2 mb-2">Diagnostics</h6> <h3>Versions</h3> @@ -8,8 +8,8 @@ <dl class="row"> <dt class="col-sm-5">Server Installed <span class="badge bg-success d-none" id="server-success" title="Latest version is installed.">Ok</span> - <span class="badge bg-warning d-none" id="server-warning" title="There seems to be an update available.">Update</span> - <span class="badge bg-info d-none" id="server-branch" title="This is a branched version.">Branched</span> + <span class="badge bg-warning text-dark d-none" id="server-warning" title="There seems to be an update available.">Update</span> + <span class="badge bg-info text-dark d-none" id="server-branch" title="This is a branched version.">Branched</span> </dt> <dd class="col-sm-7"> <span id="server-installed">{{page_data.current_release}}</span> diff --git a/src/static/templates/admin/login.hbs b/src/static/templates/admin/login.hbs index 61d4daf9..3ea94aec 100644 --- a/src/static/templates/admin/login.hbs +++ b/src/static/templates/admin/login.hbs @@ -1,15 +1,15 @@ <main class="container-xl"> {{#if error}} - <div class="align-items-center p-3 mb-3 text-white-50 bg-warning rounded shadow"> + <div class="align-items-center p-3 mb-3 text-opacity-50 text-dark bg-warning rounded shadow"> <div> - <h6 class="mb-0 text-white">{{error}}</h6> + <h6 class="mb-0 text-dark">{{error}}</h6> </div> </div> {{/if}} - <div class="align-items-center p-3 mb-3 text-white-50 bg-danger rounded shadow"> + <div class="align-items-center p-3 mb-3 text-opacity-75 text-light bg-danger rounded shadow"> <div> - <h6 class="mb-0 text-white">Authentication key needed to continue</h6> + <h6 class="mb-0 text-light">Authentication key needed to continue</h6> <small>Please provide it below:</small> <form class="form-inline" method="post" action="{{urlpath}}/admin"> @@ -17,7 +17,7 @@ {{#if redirect}} <input type="hidden" id="redirect" name="redirect" value="/{{redirect}}"> {{/if}} - <button type="submit" class="btn btn-primary">Enter</button> + <button type="submit" class="btn btn-primary mt-2">Enter</button> </form> </div> </div> diff --git a/src/static/templates/admin/organizations.hbs b/src/static/templates/admin/organizations.hbs index 7ac2b6ba..f0563cec 100644 --- a/src/static/templates/admin/organizations.hbs +++ b/src/static/templates/admin/organizations.hbs @@ -1,5 +1,5 @@ <main class="container-xl"> - <div id="organizations-block" class="my-3 p-3 bg-white rounded shadow"> + <div id="organizations-block" class="my-3 p-3 rounded shadow"> <h6 class="border-bottom pb-2 mb-3">Organizations</h6> <div class="table-responsive-xl small"> <table id="orgs-table" class="table table-sm table-striped table-hover"> @@ -59,7 +59,7 @@ </main> <link rel="stylesheet" href="{{urlpath}}/vw_static/datatables.css" /> -<script src="{{urlpath}}/vw_static/jquery-3.6.4.slim.js"></script> +<script src="{{urlpath}}/vw_static/jquery-3.7.0.slim.js"></script> <script src="{{urlpath}}/vw_static/datatables.js"></script> <script src="{{urlpath}}/vw_static/admin_organizations.js"></script> <script src="{{urlpath}}/vw_static/jdenticon.js"></script> diff --git a/src/static/templates/admin/settings.hbs b/src/static/templates/admin/settings.hbs index b8ee5f4b..fb066cb4 100644 --- a/src/static/templates/admin/settings.hbs +++ b/src/static/templates/admin/settings.hbs @@ -17,7 +17,7 @@ <form class="form needs-validation" id="config-form" novalidate> {{#each page_data.config}} {{#if groupdoc}} - <div class="card bg-light mb-3"> + <div class="card mb-3"> <button id="b_{{group}}" type="button" class="card-header text-start btn btn-link text-decoration-none" aria-expanded="false" aria-controls="g_{{group}}" data-bs-toggle="collapse" data-bs-target="#g_{{group}}">{{groupdoc}}</button> <div id="g_{{group}}" class="card-body collapse"> {{#each elements}} @@ -64,7 +64,7 @@ {{/if}} {{/each}} - <div class="card bg-light mb-3"> + <div class="card mb-3"> <button id="b_readonly" type="button" class="card-header text-start btn btn-link text-decoration-none" aria-expanded="false" aria-controls="g_readonly" data-bs-toggle="collapse" data-bs-target="#g_readonly">Read-Only Config</button> <div id="g_readonly" class="card-body collapse"> @@ -119,7 +119,7 @@ </div> {{#if page_data.can_backup}} - <div class="card bg-light mb-3"> + <div class="card mb-3"> <button id="b_database" type="button" class="card-header text-start btn btn-link text-decoration-none" aria-expanded="false" aria-controls="g_database" data-bs-toggle="collapse" data-bs-target="#g_database">Backup Database</button> <div id="g_database" class="card-body collapse"> diff --git a/src/static/templates/admin/users.hbs b/src/static/templates/admin/users.hbs index 637dfb22..d8b7289f 100644 --- a/src/static/templates/admin/users.hbs +++ b/src/static/templates/admin/users.hbs @@ -1,5 +1,5 @@ <main class="container-xl"> - <div id="users-block" class="my-3 p-3 bg-white rounded shadow"> + <div id="users-block" class="my-3 p-3 rounded shadow"> <h6 class="border-bottom pb-2 mb-3">Registered Users</h6> <div class="table-responsive-xl small"> <table id="users-table" class="table table-sm table-striped table-hover"> @@ -30,7 +30,7 @@ <span class="badge bg-success me-2" title="2FA is enabled">2FA</span> {{/if}} {{#case _Status 1}} - <span class="badge bg-warning me-2" title="User is invited">Invited</span> + <span class="badge bg-warning text-dark me-2" title="User is invited">Invited</span> {{/case}} {{#if EmailVerified}} <span class="badge bg-success me-2" title="Email has been verified">Verified</span> @@ -140,7 +140,7 @@ </main> <link rel="stylesheet" href="{{urlpath}}/vw_static/datatables.css" /> -<script src="{{urlpath}}/vw_static/jquery-3.6.4.slim.js"></script> +<script src="{{urlpath}}/vw_static/jquery-3.7.0.slim.js"></script> <script src="{{urlpath}}/vw_static/datatables.js"></script> <script src="{{urlpath}}/vw_static/admin_users.js"></script> <script src="{{urlpath}}/vw_static/jdenticon.js"></script> |