From 3ea39841c8049525e31e9f4d6300f0c60cdb42de Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Tue, 24 Jan 2023 13:33:51 +0100 Subject: Adding upstream version 5.2.3+dfsg. Signed-off-by: Daniel Baumann --- js/src/util/index.js | 336 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 336 insertions(+) create mode 100644 js/src/util/index.js (limited to 'js/src/util/index.js') diff --git a/js/src/util/index.js b/js/src/util/index.js new file mode 100644 index 0000000..297e571 --- /dev/null +++ b/js/src/util/index.js @@ -0,0 +1,336 @@ +/** + * -------------------------------------------------------------------------- + * Bootstrap (v5.2.3): util/index.js + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE) + * -------------------------------------------------------------------------- + */ + +const MAX_UID = 1_000_000 +const MILLISECONDS_MULTIPLIER = 1000 +const TRANSITION_END = 'transitionend' + +// 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 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 selector +} + +const getSelectorFromElement = element => { + const selector = getSelector(element) + + if (selector) { + return document.querySelector(selector) ? selector : null + } + + return null +} + +const getElementFromSelector = element => { + const selector = getSelector(element) + + return selector ? document.querySelector(selector) : null +} + +const getTransitionDurationFromElement = element => { + if (!element) { + return 0 + } + + // Get transition-duration of the element + let { transitionDuration, transitionDelay } = window.getComputedStyle(element) + + const floatTransitionDuration = Number.parseFloat(transitionDuration) + const floatTransitionDelay = Number.parseFloat(transitionDelay) + + // Return 0 if element or transition duration is not found + if (!floatTransitionDuration && !floatTransitionDelay) { + return 0 + } + + // If multiple durations are defined, take the first + transitionDuration = transitionDuration.split(',')[0] + transitionDelay = transitionDelay.split(',')[0] + + return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER +} + +const triggerTransitionEnd = element => { + element.dispatchEvent(new Event(TRANSITION_END)) +} + +const isElement = object => { + if (!object || typeof object !== 'object') { + return false + } + + if (typeof object.jquery !== 'undefined') { + object = object[0] + } + + return typeof object.nodeType !== 'undefined' +} + +const getElement = object => { + // it's a jQuery object or a node element + if (isElement(object)) { + return object.jquery ? object[0] : object + } + + if (typeof object === 'string' && object.length > 0) { + return document.querySelector(object) + } + + return null +} + +const isVisible = element => { + if (!isElement(element) || element.getClientRects().length === 0) { + return false + } + + const elementIsVisible = getComputedStyle(element).getPropertyValue('visibility') === 'visible' + // Handle `details` element as its content may falsie appear visible when it is closed + const closedDetails = element.closest('details:not([open])') + + if (!closedDetails) { + return elementIsVisible + } + + if (closedDetails !== element) { + const summary = element.closest('summary') + if (summary && summary.parentNode !== closedDetails) { + return false + } + + if (summary === null) { + return false + } + } + + return elementIsVisible +} + +const isDisabled = element => { + if (!element || element.nodeType !== Node.ELEMENT_NODE) { + return true + } + + if (element.classList.contains('disabled')) { + return true + } + + if (typeof element.disabled !== 'undefined') { + return element.disabled + } + + return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false' +} + +const findShadowRoot = element => { + if (!document.documentElement.attachShadow) { + return null + } + + // Can find the shadow root otherwise it'll return the document + if (typeof element.getRootNode === 'function') { + const root = element.getRootNode() + return root instanceof ShadowRoot ? root : null + } + + if (element instanceof ShadowRoot) { + return element + } + + // when we don't find a shadow root + if (!element.parentNode) { + return null + } + + return findShadowRoot(element.parentNode) +} + +const noop = () => {} + +/** + * Trick to restart an element's animation + * + * @param {HTMLElement} element + * @return void + * + * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation + */ +const reflow = element => { + element.offsetHeight // eslint-disable-line no-unused-expressions +} + +const getjQuery = () => { + if (window.jQuery && !document.body.hasAttribute('data-bs-no-jquery')) { + return window.jQuery + } + + return null +} + +const DOMContentLoadedCallbacks = [] + +const onDOMContentLoaded = callback => { + if (document.readyState === 'loading') { + // add listener on the first call when the document is in loading state + if (!DOMContentLoadedCallbacks.length) { + document.addEventListener('DOMContentLoaded', () => { + for (const callback of DOMContentLoadedCallbacks) { + callback() + } + }) + } + + DOMContentLoadedCallbacks.push(callback) + } else { + callback() + } +} + +const isRTL = () => document.documentElement.dir === 'rtl' + +const defineJQueryPlugin = plugin => { + onDOMContentLoaded(() => { + const $ = getjQuery() + /* istanbul ignore if */ + if ($) { + const name = plugin.NAME + const JQUERY_NO_CONFLICT = $.fn[name] + $.fn[name] = plugin.jQueryInterface + $.fn[name].Constructor = plugin + $.fn[name].noConflict = () => { + $.fn[name] = JQUERY_NO_CONFLICT + return plugin.jQueryInterface + } + } + }) +} + +const execute = callback => { + if (typeof callback === 'function') { + callback() + } +} + +const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => { + if (!waitForTransition) { + execute(callback) + return + } + + const durationPadding = 5 + const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding + + let called = false + + const handler = ({ target }) => { + if (target !== transitionElement) { + return + } + + called = true + transitionElement.removeEventListener(TRANSITION_END, handler) + execute(callback) + } + + transitionElement.addEventListener(TRANSITION_END, handler) + setTimeout(() => { + if (!called) { + triggerTransitionEnd(transitionElement) + } + }, emulatedDuration) +} + +/** + * 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))] +} + +export { + defineJQueryPlugin, + execute, + executeAfterTransition, + findShadowRoot, + getElement, + getElementFromSelector, + getjQuery, + getNextActiveElement, + getSelectorFromElement, + getTransitionDurationFromElement, + getUID, + isDisabled, + isElement, + isRTL, + isVisible, + noop, + onDOMContentLoaded, + reflow, + triggerTransitionEnd, + toType +} -- cgit v1.2.3