summaryrefslogtreecommitdiffstats
path: root/js/src/util/index.js
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2023-01-24 12:33:51 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2023-01-24 12:33:51 +0000
commit3ea39841c8049525e31e9f4d6300f0c60cdb42de (patch)
tree855de60a8872eafb5911acd303aedcdbfe713a73 /js/src/util/index.js
parentInital commit. (diff)
downloadbootstrap-html-3ea39841c8049525e31e9f4d6300f0c60cdb42de.tar.xz
bootstrap-html-3ea39841c8049525e31e9f4d6300f0c60cdb42de.zip
Adding upstream version 5.2.3+dfsg.upstream/5.2.3+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/util/index.js')
-rw-r--r--js/src/util/index.js336
1 files changed, 336 insertions, 0 deletions
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
+}