]> git.ipfire.org Git - thirdparty/bootstrap.git/commitdiff
Fix TypeError when Bootstrap is included in `head` (#32024)
authorSascha <mail@sascha.dev>
Sun, 1 Nov 2020 13:32:36 +0000 (14:32 +0100)
committerGitHub <noreply@github.com>
Sun, 1 Nov 2020 13:32:36 +0000 (15:32 +0200)
* extend jquery after domContentLoaded event is fired

* add unittest for util onDOMContentLoaded

* wait for trigger jquery event after domContentLoaded

* remove domcontentready from eventHandler

* move istanbul ignore statements to correct line

Co-authored-by: XhmikosR <xhmikosr@gmail.com>
14 files changed:
js/src/alert.js
js/src/button.js
js/src/carousel.js
js/src/collapse.js
js/src/dom/event-handler.js
js/src/dropdown.js
js/src/modal.js
js/src/popover.js
js/src/scrollspy.js
js/src/tab.js
js/src/toast.js
js/src/tooltip.js
js/src/util/index.js
js/tests/unit/util/index.spec.js

index b337fbc577d54babb21553e9f3263847eae77aed..f3b4245afa27f3182df13d27d949bcc1a6e72949 100644 (file)
@@ -7,6 +7,7 @@
 
 import {
   getjQuery,
+  onDOMContentLoaded,
   TRANSITION_END,
   emulateTransitionEnd,
   getElementFromSelector,
@@ -146,8 +147,6 @@ class Alert {
  */
 EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DISMISS, Alert.handleDismiss(new Alert()))
 
-const $ = getjQuery()
-
 /**
  * ------------------------------------------------------------------------
  * jQuery
@@ -155,15 +154,18 @@ const $ = getjQuery()
  * add .alert to jQuery only if jQuery is present
  */
 
-/* istanbul ignore if */
-if ($) {
-  const JQUERY_NO_CONFLICT = $.fn[NAME]
-  $.fn[NAME] = Alert.jQueryInterface
-  $.fn[NAME].Constructor = Alert
-  $.fn[NAME].noConflict = () => {
-    $.fn[NAME] = JQUERY_NO_CONFLICT
-    return Alert.jQueryInterface
+onDOMContentLoaded(() => {
+  const $ = getjQuery()
+  /* istanbul ignore if */
+  if ($) {
+    const JQUERY_NO_CONFLICT = $.fn[NAME]
+    $.fn[NAME] = Alert.jQueryInterface
+    $.fn[NAME].Constructor = Alert
+    $.fn[NAME].noConflict = () => {
+      $.fn[NAME] = JQUERY_NO_CONFLICT
+      return Alert.jQueryInterface
+    }
   }
-}
+})
 
 export default Alert
index f80b5e28cf3a1e53bd86263c0306665bb54727bc..c520aa117d0e0c03fc970312b70ad93ba42d5f0c 100644 (file)
@@ -5,7 +5,7 @@
  * --------------------------------------------------------------------------
  */
 
-import { getjQuery } from './util/index'
+import { getjQuery, onDOMContentLoaded } from './util/index'
 import Data from './dom/data'
 import EventHandler from './dom/event-handler'
 
@@ -97,24 +97,26 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, event => {
   data.toggle()
 })
 
-const $ = getjQuery()
-
 /**
  * ------------------------------------------------------------------------
  * jQuery
  * ------------------------------------------------------------------------
  * add .button to jQuery only if jQuery is present
  */
-/* istanbul ignore if */
-if ($) {
-  const JQUERY_NO_CONFLICT = $.fn[NAME]
-  $.fn[NAME] = Button.jQueryInterface
-  $.fn[NAME].Constructor = Button
-
-  $.fn[NAME].noConflict = () => {
-    $.fn[NAME] = JQUERY_NO_CONFLICT
-    return Button.jQueryInterface
+
+onDOMContentLoaded(() => {
+  const $ = getjQuery()
+  /* istanbul ignore if */
+  if ($) {
+    const JQUERY_NO_CONFLICT = $.fn[NAME]
+    $.fn[NAME] = Button.jQueryInterface
+    $.fn[NAME].Constructor = Button
+
+    $.fn[NAME].noConflict = () => {
+      $.fn[NAME] = JQUERY_NO_CONFLICT
+      return Button.jQueryInterface
+    }
   }
-}
+})
 
 export default Button
index ed8e2b89a67ce4da725ac647403f09f33dae3d41..b9bb5043616bfd6213aaaceb0c78d62ef4377e83 100644 (file)
@@ -7,6 +7,7 @@
 
 import {
   getjQuery,
+  onDOMContentLoaded,
   TRANSITION_END,
   emulateTransitionEnd,
   getElementFromSelector,
@@ -611,23 +612,25 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
   }
 })
 
-const $ = getjQuery()
-
 /**
  * ------------------------------------------------------------------------
  * jQuery
  * ------------------------------------------------------------------------
  * add .carousel to jQuery only if jQuery is present
  */
-/* istanbul ignore if */
-if ($) {
-  const JQUERY_NO_CONFLICT = $.fn[NAME]
-  $.fn[NAME] = Carousel.jQueryInterface
-  $.fn[NAME].Constructor = Carousel
-  $.fn[NAME].noConflict = () => {
-    $.fn[NAME] = JQUERY_NO_CONFLICT
-    return Carousel.jQueryInterface
+
+onDOMContentLoaded(() => {
+  const $ = getjQuery()
+  /* istanbul ignore if */
+  if ($) {
+    const JQUERY_NO_CONFLICT = $.fn[NAME]
+    $.fn[NAME] = Carousel.jQueryInterface
+    $.fn[NAME].Constructor = Carousel
+    $.fn[NAME].noConflict = () => {
+      $.fn[NAME] = JQUERY_NO_CONFLICT
+      return Carousel.jQueryInterface
+    }
   }
-}
+})
 
 export default Carousel
index f90238bc518d3b9b0cf98a486a7b178f6b4d3c40..7788d0025a24d95763e73601d795848d0185c4e3 100644 (file)
@@ -7,6 +7,7 @@
 
 import {
   getjQuery,
+  onDOMContentLoaded,
   TRANSITION_END,
   emulateTransitionEnd,
   getSelectorFromElement,
@@ -408,23 +409,25 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
   })
 })
 
-const $ = getjQuery()
-
 /**
  * ------------------------------------------------------------------------
  * jQuery
  * ------------------------------------------------------------------------
  * add .collapse to jQuery only if jQuery is present
  */
-/* istanbul ignore if */
-if ($) {
-  const JQUERY_NO_CONFLICT = $.fn[NAME]
-  $.fn[NAME] = Collapse.jQueryInterface
-  $.fn[NAME].Constructor = Collapse
-  $.fn[NAME].noConflict = () => {
-    $.fn[NAME] = JQUERY_NO_CONFLICT
-    return Collapse.jQueryInterface
+
+onDOMContentLoaded(() => {
+  const $ = getjQuery()
+  /* istanbul ignore if */
+  if ($) {
+    const JQUERY_NO_CONFLICT = $.fn[NAME]
+    $.fn[NAME] = Collapse.jQueryInterface
+    $.fn[NAME].Constructor = Collapse
+    $.fn[NAME].noConflict = () => {
+      $.fn[NAME] = JQUERY_NO_CONFLICT
+      return Collapse.jQueryInterface
+    }
   }
-}
+})
 
 export default Collapse
index dced8d9dee8a9ca59a04e52d642cf42addc09348..1d109c57ec2dfaabf159d7ea82dfc302566dedba 100644 (file)
@@ -14,7 +14,6 @@ import { defaultPreventedPreservedOnDispatch } from './polyfill'
  * ------------------------------------------------------------------------
  */
 
-const $ = getjQuery()
 const namespaceRegex = /[^.]*(?=\..*)\.|.*/
 const stripNameRegex = /\..*/
 const stripUidRegex = /::\d+$/
@@ -272,6 +271,7 @@ const EventHandler = {
       return null
     }
 
+    const $ = getjQuery()
     const typeEvent = event.replace(stripNameRegex, '')
     const inNamespace = event !== typeEvent
     const isNative = nativeEvents.indexOf(typeEvent) > -1
index 28d8b7299230c69f0d864847f1306c300f9f8121..46311ee8ad94e852f8a07cbe48ca511145aa3bf2 100644 (file)
@@ -7,6 +7,7 @@
 
 import {
   getjQuery,
+  onDOMContentLoaded,
   getElementFromSelector,
   isElement,
   isVisible,
@@ -512,23 +513,25 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
 })
 EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_FORM_CHILD, e => e.stopPropagation())
 
-const $ = getjQuery()
-
 /**
  * ------------------------------------------------------------------------
  * jQuery
  * ------------------------------------------------------------------------
  * add .dropdown to jQuery only if jQuery is present
  */
-/* istanbul ignore if */
-if ($) {
-  const JQUERY_NO_CONFLICT = $.fn[NAME]
-  $.fn[NAME] = Dropdown.jQueryInterface
-  $.fn[NAME].Constructor = Dropdown
-  $.fn[NAME].noConflict = () => {
-    $.fn[NAME] = JQUERY_NO_CONFLICT
-    return Dropdown.jQueryInterface
+
+onDOMContentLoaded(() => {
+  const $ = getjQuery()
+  /* istanbul ignore if */
+  if ($) {
+    const JQUERY_NO_CONFLICT = $.fn[NAME]
+    $.fn[NAME] = Dropdown.jQueryInterface
+    $.fn[NAME].Constructor = Dropdown
+    $.fn[NAME].noConflict = () => {
+      $.fn[NAME] = JQUERY_NO_CONFLICT
+      return Dropdown.jQueryInterface
+    }
   }
-}
+})
 
 export default Dropdown
index 4c1db3d5ca38527b2b78885b5e063793d50bde6f..adddb62e28eccf64f76da81de01fe03b064faf68 100644 (file)
@@ -7,6 +7,7 @@
 
 import {
   getjQuery,
+  onDOMContentLoaded,
   TRANSITION_END,
   emulateTransitionEnd,
   getElementFromSelector,
@@ -607,23 +608,25 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
   data.show(this)
 })
 
-const $ = getjQuery()
-
 /**
  * ------------------------------------------------------------------------
  * jQuery
  * ------------------------------------------------------------------------
  * add .modal to jQuery only if jQuery is present
  */
-/* istanbul ignore if */
-if ($) {
-  const JQUERY_NO_CONFLICT = $.fn[NAME]
-  $.fn[NAME] = Modal.jQueryInterface
-  $.fn[NAME].Constructor = Modal
-  $.fn[NAME].noConflict = () => {
-    $.fn[NAME] = JQUERY_NO_CONFLICT
-    return Modal.jQueryInterface
+
+onDOMContentLoaded(() => {
+  const $ = getjQuery()
+  /* istanbul ignore if */
+  if ($) {
+    const JQUERY_NO_CONFLICT = $.fn[NAME]
+    $.fn[NAME] = Modal.jQueryInterface
+    $.fn[NAME].Constructor = Modal
+    $.fn[NAME].noConflict = () => {
+      $.fn[NAME] = JQUERY_NO_CONFLICT
+      return Modal.jQueryInterface
+    }
   }
-}
+})
 
 export default Modal
index 54abe1f9619af07b106cf8f55af625dd6078d829..a4980a19e95e409000389b6c327b55d3aa7bd127 100644 (file)
@@ -5,7 +5,7 @@
  * --------------------------------------------------------------------------
  */
 
-import { getjQuery } from './util/index'
+import { getjQuery, onDOMContentLoaded } from './util/index'
 import Data from './dom/data'
 import SelectorEngine from './dom/selector-engine'
 import Tooltip from './tooltip'
@@ -167,22 +167,24 @@ class Popover extends Tooltip {
   }
 }
 
-const $ = getjQuery()
-
 /**
  * ------------------------------------------------------------------------
  * jQuery
  * ------------------------------------------------------------------------
  */
-/* istanbul ignore if */
-if ($) {
-  const JQUERY_NO_CONFLICT = $.fn[NAME]
-  $.fn[NAME] = Popover.jQueryInterface
-  $.fn[NAME].Constructor = Popover
-  $.fn[NAME].noConflict = () => {
-    $.fn[NAME] = JQUERY_NO_CONFLICT
-    return Popover.jQueryInterface
+
+onDOMContentLoaded(() => {
+  const $ = getjQuery()
+  /* istanbul ignore if */
+  if ($) {
+    const JQUERY_NO_CONFLICT = $.fn[NAME]
+    $.fn[NAME] = Popover.jQueryInterface
+    $.fn[NAME].Constructor = Popover
+    $.fn[NAME].noConflict = () => {
+      $.fn[NAME] = JQUERY_NO_CONFLICT
+      return Popover.jQueryInterface
+    }
   }
-}
+})
 
 export default Popover
index d37ca923dc3f893d843f217ab3b48ebe09bdff60..a0061b7e1e29d3c7906bdc935a55e977c3a9924a 100644 (file)
@@ -7,6 +7,7 @@
 
 import {
   getjQuery,
+  onDOMContentLoaded,
   getSelectorFromElement,
   getUID,
   isElement,
@@ -317,22 +318,24 @@ EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
     .forEach(spy => new ScrollSpy(spy, Manipulator.getDataAttributes(spy)))
 })
 
-const $ = getjQuery()
-
 /**
  * ------------------------------------------------------------------------
  * jQuery
  * ------------------------------------------------------------------------
  */
-/* istanbul ignore if */
-if ($) {
-  const JQUERY_NO_CONFLICT = $.fn[NAME]
-  $.fn[NAME] = ScrollSpy.jQueryInterface
-  $.fn[NAME].Constructor = ScrollSpy
-  $.fn[NAME].noConflict = () => {
-    $.fn[NAME] = JQUERY_NO_CONFLICT
-    return ScrollSpy.jQueryInterface
+
+onDOMContentLoaded(() => {
+  const $ = getjQuery()
+  /* istanbul ignore if */
+  if ($) {
+    const JQUERY_NO_CONFLICT = $.fn[NAME]
+    $.fn[NAME] = ScrollSpy.jQueryInterface
+    $.fn[NAME].Constructor = ScrollSpy
+    $.fn[NAME].noConflict = () => {
+      $.fn[NAME] = JQUERY_NO_CONFLICT
+      return ScrollSpy.jQueryInterface
+    }
   }
-}
+})
 
 export default ScrollSpy
index 76895b8ddbef442f19d66955f01b13aee052c334..af0dfc97aa51912b9d57292fbcbc0dc5d70a73fa 100644 (file)
@@ -7,6 +7,7 @@
 
 import {
   getjQuery,
+  onDOMContentLoaded,
   TRANSITION_END,
   emulateTransitionEnd,
   getElementFromSelector,
@@ -235,23 +236,25 @@ EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (
   data.show()
 })
 
-const $ = getjQuery()
-
 /**
  * ------------------------------------------------------------------------
  * jQuery
  * ------------------------------------------------------------------------
  * add .tab to jQuery only if jQuery is present
  */
-/* istanbul ignore if */
-if ($) {
-  const JQUERY_NO_CONFLICT = $.fn[NAME]
-  $.fn[NAME] = Tab.jQueryInterface
-  $.fn[NAME].Constructor = Tab
-  $.fn[NAME].noConflict = () => {
-    $.fn[NAME] = JQUERY_NO_CONFLICT
-    return Tab.jQueryInterface
+
+onDOMContentLoaded(() => {
+  const $ = getjQuery()
+  /* istanbul ignore if */
+  if ($) {
+    const JQUERY_NO_CONFLICT = $.fn[NAME]
+    $.fn[NAME] = Tab.jQueryInterface
+    $.fn[NAME].Constructor = Tab
+    $.fn[NAME].noConflict = () => {
+      $.fn[NAME] = JQUERY_NO_CONFLICT
+      return Tab.jQueryInterface
+    }
   }
-}
+})
 
 export default Tab
index 91eaba53f5f6c481e249a21c0dc0465600411ca2..308a5d879b561b382af42fa39aa88a248f78e9dd 100644 (file)
@@ -7,6 +7,7 @@
 
 import {
   getjQuery,
+  onDOMContentLoaded,
   TRANSITION_END,
   emulateTransitionEnd,
   getTransitionDurationFromElement,
@@ -213,23 +214,25 @@ class Toast {
   }
 }
 
-const $ = getjQuery()
-
 /**
  * ------------------------------------------------------------------------
  * jQuery
  * ------------------------------------------------------------------------
  *  add .toast to jQuery only if jQuery is present
  */
-/* istanbul ignore if */
-if ($) {
-  const JQUERY_NO_CONFLICT = $.fn[NAME]
-  $.fn[NAME] = Toast.jQueryInterface
-  $.fn[NAME].Constructor = Toast
-  $.fn[NAME].noConflict = () => {
-    $.fn[NAME] = JQUERY_NO_CONFLICT
-    return Toast.jQueryInterface
+
+onDOMContentLoaded(() => {
+  const $ = getjQuery()
+  /* istanbul ignore if */
+  if ($) {
+    const JQUERY_NO_CONFLICT = $.fn[NAME]
+    $.fn[NAME] = Toast.jQueryInterface
+    $.fn[NAME].Constructor = Toast
+    $.fn[NAME].noConflict = () => {
+      $.fn[NAME] = JQUERY_NO_CONFLICT
+      return Toast.jQueryInterface
+    }
   }
-}
+})
 
 export default Toast
index 1203142ed04f5c59b908b7d759d7a62d84bfdd37..4d9d3c4a2d846a9f096f2e2e2ce73312f08ae170 100644 (file)
@@ -7,6 +7,7 @@
 
 import {
   getjQuery,
+  onDOMContentLoaded,
   TRANSITION_END,
   emulateTransitionEnd,
   findShadowRoot,
@@ -793,23 +794,25 @@ class Tooltip {
   }
 }
 
-const $ = getjQuery()
-
 /**
  * ------------------------------------------------------------------------
  * jQuery
  * ------------------------------------------------------------------------
  * add .tooltip to jQuery only if jQuery is present
  */
-/* istanbul ignore if */
-if ($) {
-  const JQUERY_NO_CONFLICT = $.fn[NAME]
-  $.fn[NAME] = Tooltip.jQueryInterface
-  $.fn[NAME].Constructor = Tooltip
-  $.fn[NAME].noConflict = () => {
-    $.fn[NAME] = JQUERY_NO_CONFLICT
-    return Tooltip.jQueryInterface
+
+onDOMContentLoaded(() => {
+  const $ = getjQuery()
+  /* istanbul ignore if */
+  if ($) {
+    const JQUERY_NO_CONFLICT = $.fn[NAME]
+    $.fn[NAME] = Tooltip.jQueryInterface
+    $.fn[NAME].Constructor = Tooltip
+    $.fn[NAME].noConflict = () => {
+      $.fn[NAME] = JQUERY_NO_CONFLICT
+      return Tooltip.jQueryInterface
+    }
   }
-}
+})
 
 export default Tooltip
index 457b2e027347afc76ed08db77d1057072e2c6a76..0fd78c848da9c31578b89e13345d2da474302d8f 100644 (file)
@@ -180,8 +180,15 @@ const getjQuery = () => {
   return null
 }
 
+const onDOMContentLoaded = callback => {
+  if (document.readyState === 'loading') {
+    document.addEventListener('DOMContentLoaded', callback)
+  } else {
+    callback()
+  }
+}
+
 export {
-  getjQuery,
   TRANSITION_END,
   getUID,
   getSelectorFromElement,
@@ -194,5 +201,7 @@ export {
   isVisible,
   findShadowRoot,
   noop,
-  reflow
+  reflow,
+  getjQuery,
+  onDOMContentLoaded
 }
index 541c10baa785be15b46773887be78ea4a7532d26..f7cc3797778ba8f8511626a918d581e065632082 100644 (file)
@@ -394,4 +394,23 @@ describe('Util', () => {
       expect(Util.getjQuery()).toEqual(null)
     })
   })
+
+  describe('onDOMContentLoaded', () => {
+    it('should execute callback when DOMContentLoaded is fired', () => {
+      const spy = jasmine.createSpy()
+      spyOnProperty(document, 'readyState').and.returnValue('loading')
+      Util.onDOMContentLoaded(spy)
+      window.document.dispatchEvent(new Event('DOMContentLoaded', {
+        bubbles: true,
+        cancelable: true
+      }))
+      expect(spy).toHaveBeenCalled()
+    })
+
+    it('should execute callback if readyState is not "loading"', () => {
+      const spy = jasmine.createSpy()
+      Util.onDOMContentLoaded(spy)
+      expect(spy).toHaveBeenCalled()
+    })
+  })
 })