Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.
 
 
 
 
 
 

342 lignes
9.9 KiB

  1. /**
  2. * --------------------------------------------------------------------------
  3. * Bootstrap (v5.1.3): util/index.js
  4. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  5. * --------------------------------------------------------------------------
  6. */
  7. define([
  8. "jquery",
  9. 'domReady!'
  10. ], function() {
  11. 'use strict';
  12. const MAX_UID = 1000000
  13. const MILLISECONDS_MULTIPLIER = 1000
  14. const TRANSITION_END = 'transitionend'
  15. // Shoutout AngusCroll (https://goo.gl/pxwQGp)
  16. const toType = obj => {
  17. if (obj === null || obj === undefined) {
  18. return `${obj}`
  19. }
  20. return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase()
  21. }
  22. /**
  23. * --------------------------------------------------------------------------
  24. * Public Util Api
  25. * --------------------------------------------------------------------------
  26. */
  27. const getUID = prefix => {
  28. do {
  29. prefix += Math.floor(Math.random() * MAX_UID)
  30. } while (document.getElementById(prefix))
  31. return prefix
  32. }
  33. const getSelector = element => {
  34. let selector = element.getAttribute('data-bs-target')
  35. if (!selector || selector === '#') {
  36. let hrefAttr = element.getAttribute('href')
  37. // The only valid content that could double as a selector are IDs or classes,
  38. // so everything starting with `#` or `.`. If a "real" URL is used as the selector,
  39. // `document.querySelector` will rightfully complain it is invalid.
  40. // See https://github.com/twbs/bootstrap/issues/32273
  41. if (!hrefAttr || (!hrefAttr.includes('#') && !hrefAttr.startsWith('.'))) {
  42. return null
  43. }
  44. // Just in case some CMS puts out a full URL with the anchor appended
  45. if (hrefAttr.includes('#') && !hrefAttr.startsWith('#')) {
  46. hrefAttr = `#${hrefAttr.split('#')[1]}`
  47. }
  48. selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : null
  49. }
  50. return selector
  51. }
  52. const getSelectorFromElement = element => {
  53. const selector = getSelector(element)
  54. if (selector) {
  55. return document.querySelector(selector) ? selector : null
  56. }
  57. return null
  58. }
  59. const getElementFromSelector = element => {
  60. const selector = getSelector(element)
  61. return selector ? document.querySelector(selector) : null
  62. }
  63. const getTransitionDurationFromElement = element => {
  64. if (!element) {
  65. return 0
  66. }
  67. // Get transition-duration of the element
  68. let {transitionDuration, transitionDelay} = window.getComputedStyle(element)
  69. const floatTransitionDuration = Number.parseFloat(transitionDuration)
  70. const floatTransitionDelay = Number.parseFloat(transitionDelay)
  71. // Return 0 if element or transition duration is not found
  72. if (!floatTransitionDuration && !floatTransitionDelay) {
  73. return 0
  74. }
  75. // If multiple durations are defined, take the first
  76. transitionDuration = transitionDuration.split(',')[0]
  77. transitionDelay = transitionDelay.split(',')[0]
  78. return (Number.parseFloat(transitionDuration) + Number.parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER
  79. }
  80. const triggerTransitionEnd = element => {
  81. element.dispatchEvent(new Event(TRANSITION_END))
  82. }
  83. const isElement = obj => {
  84. if (!obj || typeof obj !== 'object') {
  85. return false
  86. }
  87. if (typeof obj.jquery !== 'undefined') {
  88. obj = obj[0]
  89. }
  90. return typeof obj.nodeType !== 'undefined'
  91. }
  92. const getElement = obj => {
  93. if (isElement(obj)) { // it's a jQuery object or a node element
  94. return obj.jquery ? obj[0] : obj
  95. }
  96. if (typeof obj === 'string' && obj.length > 0) {
  97. return document.querySelector(obj)
  98. }
  99. return null
  100. }
  101. const typeCheckConfig = (componentName, config, configTypes) => {
  102. Object.keys(configTypes).forEach(property => {
  103. const expectedTypes = configTypes[property]
  104. const value = config[property]
  105. const valueType = value && isElement(value) ? 'element' : toType(value)
  106. if (!new RegExp(expectedTypes).test(valueType)) {
  107. throw new TypeError(
  108. `${componentName.toUpperCase()}: Option "${property}" provided type "${valueType}" but expected type "${expectedTypes}".`
  109. )
  110. }
  111. })
  112. }
  113. const isVisible = element => {
  114. if (!isElement(element) || element.getClientRects().length === 0) {
  115. return false
  116. }
  117. return getComputedStyle(element).getPropertyValue('visibility') === 'visible'
  118. }
  119. const isDisabled = element => {
  120. if (!element || element.nodeType !== Node.ELEMENT_NODE) {
  121. return true
  122. }
  123. if (element.classList.contains('disabled')) {
  124. return true
  125. }
  126. if (typeof element.disabled !== 'undefined') {
  127. return element.disabled
  128. }
  129. return element.hasAttribute('disabled') && element.getAttribute('disabled') !== 'false'
  130. }
  131. const findShadowRoot = element => {
  132. if (!document.documentElement.attachShadow) {
  133. return null
  134. }
  135. // Can find the shadow root otherwise it'll return the document
  136. if (typeof element.getRootNode === 'function') {
  137. const root = element.getRootNode()
  138. return root instanceof ShadowRoot ? root : null
  139. }
  140. if (element instanceof ShadowRoot) {
  141. return element
  142. }
  143. // when we don't find a shadow root
  144. if (!element.parentNode) {
  145. return null
  146. }
  147. return findShadowRoot(element.parentNode)
  148. }
  149. const noop = () => {}
  150. /**
  151. * Trick to restart an element's animation
  152. *
  153. * @param {HTMLElement} element
  154. * @return void
  155. *
  156. * @see https://www.charistheo.io/blog/2021/02/restart-a-css-animation-with-javascript/#restarting-a-css-animation
  157. */
  158. const reflow = element => {
  159. // eslint-disable-next-line no-unused-expressions
  160. element.offsetHeight
  161. }
  162. const getjQuery = () => {
  163. const {jQuery} = window
  164. if (jQuery && !document.body.hasAttribute('data-bs-no-jquery')) {
  165. return jQuery
  166. }
  167. return null
  168. }
  169. const DOMContentLoadedCallbacks = []
  170. const onDOMContentLoaded = callback => {
  171. if (document.readyState === 'loading') {
  172. // add listener on the first call when the document is in loading state
  173. if (!DOMContentLoadedCallbacks.length) {
  174. document.addEventListener('DOMContentLoaded', () => {
  175. DOMContentLoadedCallbacks.forEach(callback => callback())
  176. })
  177. }
  178. DOMContentLoadedCallbacks.push(callback)
  179. } else {
  180. callback()
  181. }
  182. }
  183. const isRTL = () => document.documentElement.dir === 'rtl'
  184. const defineJQueryPlugin = plugin => {
  185. onDOMContentLoaded(() => {
  186. const $ = getjQuery()
  187. /* istanbul ignore if */
  188. if ($) {
  189. const name = plugin.NAME
  190. const JQUERY_NO_CONFLICT = $.fn[name]
  191. $.fn[name] = plugin.jQueryInterface
  192. $.fn[name].Constructor = plugin
  193. $.fn[name].noConflict = () => {
  194. $.fn[name] = JQUERY_NO_CONFLICT
  195. return plugin.jQueryInterface
  196. }
  197. }
  198. })
  199. }
  200. const execute = callback => {
  201. if (typeof callback === 'function') {
  202. callback()
  203. }
  204. }
  205. const executeAfterTransition = (callback, transitionElement, waitForTransition = true) => {
  206. if (!waitForTransition) {
  207. execute(callback)
  208. return
  209. }
  210. const durationPadding = 5
  211. const emulatedDuration = getTransitionDurationFromElement(transitionElement) + durationPadding
  212. let called = false
  213. const handler = ({ target }) => {
  214. if (target !== transitionElement) {
  215. return
  216. }
  217. called = true
  218. transitionElement.removeEventListener(TRANSITION_END, handler)
  219. execute(callback)
  220. }
  221. transitionElement.addEventListener(TRANSITION_END, handler)
  222. setTimeout(() => {
  223. if (!called) {
  224. triggerTransitionEnd(transitionElement)
  225. }
  226. }, emulatedDuration)
  227. }
  228. /**
  229. * Return the previous/next element of a list.
  230. *
  231. * @param {array} list The list of elements
  232. * @param activeElement The active element
  233. * @param shouldGetNext Choose to get next or previous element
  234. * @param isCycleAllowed
  235. * @return {Element|elem} The proper element
  236. */
  237. const getNextActiveElement = (list, activeElement, shouldGetNext, isCycleAllowed) => {
  238. let index = list.indexOf(activeElement)
  239. // if the element does not exist in the list return an element depending on the direction and if cycle is allowed
  240. if (index === -1) {
  241. return list[!shouldGetNext && isCycleAllowed ? list.length - 1 : 0]
  242. }
  243. const listLength = list.length
  244. index += shouldGetNext ? 1 : -1
  245. if (isCycleAllowed) {
  246. index = (index + listLength) % listLength
  247. }
  248. return list[Math.max(0, Math.min(index, listLength - 1))]
  249. }
  250. return {
  251. getElement,
  252. getUID,
  253. getSelectorFromElement,
  254. getElementFromSelector,
  255. getTransitionDurationFromElement,
  256. triggerTransitionEnd,
  257. isElement,
  258. typeCheckConfig,
  259. isVisible,
  260. isDisabled,
  261. findShadowRoot,
  262. noop,
  263. getNextActiveElement,
  264. reflow,
  265. getjQuery,
  266. onDOMContentLoaded,
  267. isRTL,
  268. defineJQueryPlugin,
  269. execute,
  270. executeAfterTransition
  271. };
  272. });