Commit 8a37045b authored by Johann-S's avatar Johann-S Committed by XhmikosR
Browse files

move util in a util folder with the sanitizer

parent 8affe84c
main cleanup-floating-forms cssvar-function dependabot/npm_and_yarn/stylelint-and-stylelint-config-twbs-bootstrap-15.3.0 extend-snippets feat/data-target floating-always-visible floating-labels-icons fod-main-banner form-controls-with-icons github/fork/719media/patch-13 github/fork/719media/patch-14 github/fork/719media/patch-9 github/fork/ChellyAhmed/fix-typo-reboot.md github/fork/ChellyAhmed/offcanvas-scroll-back github/fork/CtrlAltLilith/main github/fork/Elysiome/offcanvas-optional-window-resizing github/fork/JanSargsyan/main github/fork/LunicLynx/support-different-line-height-for-buttons github/fork/Psixodelik/main github/fork/Ronid1/ronid1/offcanvas_static_backdrop github/fork/RyanBerliner/tooltip-accessibility github/fork/SantiagoPVazquez/Feature-default-border-bottom-to-dropdown-item github/fork/Sir-Genius/utils github/fork/Sumit-Singh-8/main github/fork/Viktor-VERA2020/offcanvas-slide github/fork/Zivangu9/input-group-for-form-control-plaintext github/fork/alpadev/alpadev/call-dispose-on-component-reinstantiation github/fork/astagi/fix/tree-shake-modules github/fork/compnerd/dark-accordion-icon github/fork/derSascha/dropdown-dont-close-on-input-click github/fork/dev-ph1l/main github/fork/donquixote/issue-33861-utl-mixin github/fork/florianlacreuse/mixin-make-row-gutter-y github/fork/gregorw/main github/fork/iteggmbh/transitionend-dispose-race github/fork/jdelStrother/patch-1 github/fork/jonnysp/form-floating github/fork/jonnysp/independent-offcanvas github/fork/jonnysp/theme-dark-on-card-and-modal-fix github/fork/josefdlange/floating-label-placeholder-opacity github/fork/julien-deramond/enhance-change-version.js github/fork/julien-deramond/main-jd-fix-offset-content github/fork/julien-deramond/main-jd-issue-with-utitlies github/fork/julien-deramond/main-xmr-pa11y-ci-jd-add-hideElements github/fork/kyletsang/fix-tooltip-padding github/fork/lacutah/CheckboxCenteringDocumentation github/fork/lekoala/patch-3 github/fork/louismaximepiton/main-kld-lmp-collapse-proposal github/fork/louismaximepiton/main-lmp-card-inner-border-radius-fix github/fork/louismaximepiton/main-lmp-carousel-multiple-images github/fork/louismaximepiton/main-lmp-css-var-init github/fork/louismaximepiton/main-lmp-disabled-floating-label-fix github/fork/louismaximepiton/main-lmp-input-range-fix github/fork/louismaximepiton/main-lmp-shift-color github/fork/louismaximepiton/main-lmp-table-active-tr-fix github/fork/maciek-szn/switch github/fork/michael-roth/feature/19964-multiple-tab-targets github/fork/mistic100/dom-utils github/fork/nkdas91/accordion github/fork/nstungcom/fix-missing-modal-open-class github/fork/oraliahdz/animation-utilities github/fork/pine3ree/patch-7 github/fork/pouwerkerk/unindent-scss-docs-shortcode github/fork/smares/smares-no-scolling-on-modal-close github/fork/tgm-git/patch-1 gs-forms gs-toasts-with-animated-progress-bar gs/add-history-helper gs/change-version-dir-on-docs gs/data-must-set-onlu-one-instance gs/docs/fix-drop-down-error gs/event-handler-2 gs/make-docs-js-build gs/make-simple-attribute-toggler gs/popover-fix-doc gs/provide-steConfig-method gs/scrollspy-smoothscroll-option-use-browser-history gs/streamline-jqueryInterface gs/support-drop-down-in-navbar gs/test-js-generic-trigger gs/try-web-components gs/tweak-collapse-js-selector gs/use-event-handler-in-cocmponent gs/use-rollup-replace-for-version jo-docs-thanks-page jo-ssr-friendly logical-props-spacing-utils main-fod-disabled-form-check-label main-fod-nested-accordion main-fod-simpler-table-structure main-fod-table-separator main-fod-utilities-contrast main-jd-abbr-title main-jd-add-chips main-jd-add-doc-for-sass-custom-colors main-jd-add-enable-host-to-handle-web-components main-jd-browserstack-fine-tune main-jd-browserstack-updates main-jd-docs-consistent-usage-of-css-sections-step-2 main-jd-fix-docs-headers-in-white main-jd-fix-highlight-docs-border-radius main-jd-fix-placeholder-color-background-params-for-img-markup main-jd-glossary-experiment main-jd-postcss-drop-empty-css-vars main-jd-proto-doc-astro main-jd-skip-navigation-component main-jd-stackblitz-for-examples main-jd-upgrade-browserlistrc main-jd-use-host main-lmp-dark-theme-customization main-lmp-handle-scroll-target main-lmp-tab-fix main-mc-opensearch main-xmr-bundlewatch-action main-xmr-eslint-plugin-compat main-xmr-hugo-docs-vendor main-xmr-hugo-rm-ver main-xmr-linkinator-prod main-xmr-min-mangle main-xmr-pa11y-ci more-darkmode-examples nested-dropdowns patrickhlauke-issue37428 patrickhlauke-use-of-color-tweaks pr/34102 pr/37590 previous-next-docs-links sticky-thead utilities-functions-mixin v530-dev v6-postcss-custom-media v6-spinner-dots v6/gs/use-floating-ui-in-place-of-popper xmr/dev xmr/docs-png xmr/docs-svgs xmr/hugo-reorg-files xmr/js-2 xmr/markdownlint xmr/prepare-530-alpha2 xmr/xo v5.3.0-alpha1 v5.2.3 v5.2.2 v5.2.1 v5.2.0 v5.2.0-beta1 v5.1.3 v5.1.2 v5.1.1 v5.1.0 v5.0.2 v5.0.1 v5.0.0 v5.0.0-beta3 v5.0.0-beta2 v5.0.0-beta1 v5.0.0-alpha3 v5.0.0-alpha2 v5.0.0-alpha1
5 merge requests!31948Examples/Floating-labels: fix bad behavior with autofill,!30064test,!29779Responsive sizing,!28882fix custom-select-indicator in IE10,!28721Hot test
Showing with 459 additions and 381 deletions
+459 -381
......@@ -130,7 +130,7 @@
"func-call-spacing": "error",
"func-name-matching": "error",
"func-names": "off",
"func-style": ["error", "declaration"],
"func-style": "off",
"id-blacklist": "error",
"id-length": "off",
"id-match": "error",
......
......@@ -41,23 +41,25 @@ const bsPlugins = {
ScrollSpy: path.resolve(__dirname, '../js/src/scrollspy.js'),
Tab: path.resolve(__dirname, '../js/src/tab.js'),
Toast: path.resolve(__dirname, '../js/src/toast.js'),
Tooltip: path.resolve(__dirname, '../js/src/tooltip.js'),
Util: path.resolve(__dirname, '../js/src/util.js')
Tooltip: path.resolve(__dirname, '../js/src/tooltip.js')
}
const rootPath = TEST ? '../js/coverage/dist/' : '../js/dist/'
if (TEST) {
bsPlugins.Util = path.resolve(__dirname, '../js/src/util/index.js')
bsPlugins.Sanitizer = path.resolve(__dirname, '../js/src/util/sanitizer.js')
}
const defaultPluginConfig = {
external: [
bsPlugins.Data,
bsPlugins.EventHandler,
bsPlugins.SelectorEngine,
bsPlugins.Util
bsPlugins.SelectorEngine
],
globals: {
[bsPlugins.Data]: 'Data',
[bsPlugins.EventHandler]: 'EventHandler',
[bsPlugins.SelectorEngine]: 'SelectorEngine',
[bsPlugins.Util]: 'Util'
[bsPlugins.SelectorEngine]: 'SelectorEngine'
}
}
......@@ -65,7 +67,9 @@ function getConfigByPluginKey(pluginKey) {
if (
pluginKey === 'Data' ||
pluginKey === 'Manipulator' ||
pluginKey === 'Util'
pluginKey === 'Polyfill' ||
pluginKey === 'Util' ||
pluginKey === 'Sanitizer'
) {
return {
external: [],
......@@ -76,21 +80,10 @@ function getConfigByPluginKey(pluginKey) {
if (pluginKey === 'EventHandler' || pluginKey === 'SelectorEngine') {
return {
external: [
bsPlugins.Polyfill,
bsPlugins.Util
bsPlugins.Polyfill
],
globals: {
[bsPlugins.Polyfill]: 'Polyfill',
[bsPlugins.Util]: 'Util'
}
}
}
if (pluginKey === 'Polyfill') {
return {
external: [bsPlugins.Util],
globals: {
[bsPlugins.Util]: 'Util'
[bsPlugins.Polyfill]: 'Polyfill'
}
}
}
......@@ -125,14 +118,12 @@ function getConfigByPluginKey(pluginKey) {
external: [
bsPlugins.Data,
bsPlugins.SelectorEngine,
bsPlugins.Tooltip,
bsPlugins.Util
bsPlugins.Tooltip
],
globals: {
[bsPlugins.Data]: 'Data',
[bsPlugins.SelectorEngine]: 'SelectorEngine',
[bsPlugins.Tooltip]: 'Tooltip',
[bsPlugins.Util]: 'Util'
[bsPlugins.Tooltip]: 'Tooltip'
}
}
}
......@@ -142,14 +133,12 @@ function getConfigByPluginKey(pluginKey) {
external: [
bsPlugins.Data,
bsPlugins.EventHandler,
bsPlugins.Manipulator,
bsPlugins.Util
bsPlugins.Manipulator
],
globals: {
[bsPlugins.Data]: 'Data',
[bsPlugins.EventHandler]: 'EventHandler',
[bsPlugins.Manipulator]: 'Manipulator',
[bsPlugins.Util]: 'Util'
[bsPlugins.Manipulator]: 'Manipulator'
}
}
}
......@@ -161,14 +150,28 @@ function build(plugin) {
const config = getConfigByPluginKey(plugin)
const external = config.external
const globals = config.globals
let pluginPath = rootPath
const utilObjects = [
'Util',
'Sanitizer'
]
const pluginPath = [
const domObjects = [
'Data',
'EventHandler',
'Manipulator',
'Polyfill',
'SelectorEngine'
].includes(plugin) ? `${rootPath}/dom/` : rootPath
]
if (utilObjects.includes(plugin)) {
pluginPath = `${rootPath}/util/`
}
if (domObjects.includes(plugin)) {
pluginPath = `${rootPath}/dom/`
}
const pluginFilename = `${plugin.toLowerCase()}.js`
......
......@@ -5,10 +5,16 @@
* --------------------------------------------------------------------------
*/
import {
jQuery as $,
TRANSITION_END,
emulateTransitionEnd,
getSelectorFromElement,
getTransitionDurationFromElement
} from './util/index'
import Data from './dom/data'
import EventHandler from './dom/eventHandler'
import SelectorEngine from './dom/selectorEngine'
import Util from './util'
/**
* ------------------------------------------------------------------------
......@@ -83,7 +89,7 @@ class Alert {
// Private
_getRootElement(element) {
const selector = Util.getSelectorFromElement(element)
const selector = getSelectorFromElement(element)
let parent = false
if (selector) {
......@@ -109,11 +115,11 @@ class Alert {
return
}
const transitionDuration = Util.getTransitionDurationFromElement(element)
const transitionDuration = getTransitionDurationFromElement(element)
EventHandler
.one(element, Util.TRANSITION_END, (event) => this._destroyElement(element, event))
Util.emulateTransitionEnd(element, transitionDuration)
.one(element, TRANSITION_END, (event) => this._destroyElement(element, event))
emulateTransitionEnd(element, transitionDuration)
}
_destroyElement(element) {
......@@ -170,7 +176,6 @@ EventHandler
* add .alert to jQuery only if jQuery is present
*/
const $ = Util.jQuery
if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Alert._jQueryInterface
......
......@@ -5,10 +5,12 @@
* --------------------------------------------------------------------------
*/
import {
jQuery as $
} from './util/index'
import Data from './dom/data'
import EventHandler from './dom/eventHandler'
import SelectorEngine from './dom/selectorEngine'
import Util from './util'
/**
* ------------------------------------------------------------------------
......@@ -180,7 +182,6 @@ EventHandler.on(document, Event.BLUR_DATA_API, Selector.DATA_TOGGLE_CARROT, (eve
* add .button to jQuery only if jQuery is present
*/
const $ = Util.jQuery
if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Button._jQueryInterface
......
......@@ -5,11 +5,22 @@
* --------------------------------------------------------------------------
*/
import {
jQuery as $,
TRANSITION_END,
emulateTransitionEnd,
getSelectorFromElement,
getTransitionDurationFromElement,
isVisible,
makeArray,
reflow,
triggerTransitionEnd,
typeCheckConfig
} from './util/index'
import Data from './dom/data'
import EventHandler from './dom/eventHandler'
import Manipulator from './dom/manipulator'
import SelectorEngine from './dom/selectorEngine'
import Util from './util'
/**
* ------------------------------------------------------------------------
......@@ -143,7 +154,7 @@ class Carousel {
nextWhenVisible() {
// Don't call next when the page isn't visible
// or the carousel or its parent isn't visible
if (!document.hidden && Util.isVisible(this._element)) {
if (!document.hidden && isVisible(this._element)) {
this.next()
}
}
......@@ -160,7 +171,7 @@ class Carousel {
}
if (SelectorEngine.findOne(Selector.NEXT_PREV, this._element)) {
Util.triggerTransitionEnd(this._element)
triggerTransitionEnd(this._element)
this.cycle(true)
}
......@@ -233,7 +244,7 @@ class Carousel {
...Default,
...config
}
Util.typeCheckConfig(NAME, config, DefaultType)
typeCheckConfig(NAME, config, DefaultType)
return config
}
......@@ -320,7 +331,7 @@ class Carousel {
}
}
Util.makeArray(SelectorEngine.find(Selector.ITEM_IMG, this._element)).forEach((itemImg) => {
makeArray(SelectorEngine.find(Selector.ITEM_IMG, this._element)).forEach((itemImg) => {
EventHandler.on(itemImg, Event.DRAG_START, (e) => e.preventDefault())
})
......@@ -356,7 +367,7 @@ class Carousel {
_getItemIndex(element) {
this._items = element && element.parentNode
? Util.makeArray(SelectorEngine.find(Selector.ITEM, element.parentNode))
? makeArray(SelectorEngine.find(Selector.ITEM, element.parentNode))
: []
return this._items.indexOf(element)
......@@ -459,7 +470,7 @@ class Carousel {
if (this._element.classList.contains(ClassName.SLIDE)) {
nextElement.classList.add(orderClassName)
Util.reflow(nextElement)
reflow(nextElement)
activeElement.classList.add(directionalClassName)
nextElement.classList.add(directionalClassName)
......@@ -472,10 +483,10 @@ class Carousel {
this._config.interval = this._config.defaultInterval || this._config.interval
}
const transitionDuration = Util.getTransitionDurationFromElement(activeElement)
const transitionDuration = getTransitionDurationFromElement(activeElement)
EventHandler
.one(activeElement, Util.TRANSITION_END, () => {
.one(activeElement, TRANSITION_END, () => {
nextElement.classList.remove(directionalClassName)
nextElement.classList.remove(orderClassName)
nextElement.classList.add(ClassName.ACTIVE)
......@@ -496,7 +507,7 @@ class Carousel {
}, 0)
})
Util.emulateTransitionEnd(activeElement, transitionDuration)
emulateTransitionEnd(activeElement, transitionDuration)
} else {
activeElement.classList.remove(ClassName.ACTIVE)
nextElement.classList.add(ClassName.ACTIVE)
......@@ -557,7 +568,7 @@ class Carousel {
}
static _dataApiClickHandler(event) {
const selector = Util.getSelectorFromElement(this)
const selector = getSelectorFromElement(this)
if (!selector) {
return
......@@ -603,7 +614,7 @@ EventHandler
.on(document, Event.CLICK_DATA_API, Selector.DATA_SLIDE, Carousel._dataApiClickHandler)
EventHandler.on(window, Event.LOAD_DATA_API, () => {
const carousels = Util.makeArray(SelectorEngine.find(Selector.DATA_RIDE))
const carousels = makeArray(SelectorEngine.find(Selector.DATA_RIDE))
for (let i = 0, len = carousels.length; i < len; i++) {
Carousel._carouselInterface(carousels[i], Data.getData(carousels[i], DATA_KEY))
}
......@@ -616,7 +627,6 @@ EventHandler.on(window, Event.LOAD_DATA_API, () => {
* add .carousel to jQuery only if jQuery is present
*/
const $ = Util.jQuery
if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Carousel._jQueryInterface
......
......@@ -5,11 +5,21 @@
* --------------------------------------------------------------------------
*/
import {
jQuery as $,
TRANSITION_END,
emulateTransitionEnd,
getSelectorFromElement,
getTransitionDurationFromElement,
isElement,
makeArray,
reflow,
typeCheckConfig
} from './util/index'
import Data from './dom/data'
import EventHandler from './dom/eventHandler'
import Manipulator from './dom/manipulator'
import SelectorEngine from './dom/selectorEngine'
import Util from './util'
/**
* ------------------------------------------------------------------------
......@@ -69,16 +79,16 @@ class Collapse {
this._isTransitioning = false
this._element = element
this._config = this._getConfig(config)
this._triggerArray = Util.makeArray(SelectorEngine.find(
this._triggerArray = makeArray(SelectorEngine.find(
`[data-toggle="collapse"][href="#${element.id}"],` +
`[data-toggle="collapse"][data-target="#${element.id}"]`
))
const toggleList = Util.makeArray(SelectorEngine.find(Selector.DATA_TOGGLE))
const toggleList = makeArray(SelectorEngine.find(Selector.DATA_TOGGLE))
for (let i = 0, len = toggleList.length; i < len; i++) {
const elem = toggleList[i]
const selector = Util.getSelectorFromElement(elem)
const filterElement = Util.makeArray(SelectorEngine.find(selector))
const selector = getSelectorFromElement(elem)
const filterElement = makeArray(SelectorEngine.find(selector))
.filter((foundElem) => foundElem === element)
if (selector !== null && filterElement.length) {
......@@ -130,7 +140,7 @@ class Collapse {
let activesData
if (this._parent) {
actives = Util.makeArray(SelectorEngine.find(Selector.ACTIVES, this._parent))
actives = makeArray(SelectorEngine.find(Selector.ACTIVES, this._parent))
.filter((elem) => {
if (typeof this._config.parent === 'string') {
return elem.getAttribute('data-parent') === this._config.parent
......@@ -201,11 +211,11 @@ class Collapse {
const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)
const scrollSize = `scroll${capitalizedDimension}`
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
const transitionDuration = getTransitionDurationFromElement(this._element)
EventHandler.one(this._element, Util.TRANSITION_END, complete)
EventHandler.one(this._element, TRANSITION_END, complete)
Util.emulateTransitionEnd(this._element, transitionDuration)
emulateTransitionEnd(this._element, transitionDuration)
this._element.style[dimension] = `${this._element[scrollSize]}px`
}
......@@ -224,7 +234,7 @@ class Collapse {
this._element.style[dimension] = `${this._element.getBoundingClientRect()[dimension]}px`
Util.reflow(this._element)
reflow(this._element)
this._element.classList.add(ClassName.COLLAPSING)
this._element.classList.remove(ClassName.COLLAPSE)
......@@ -234,7 +244,7 @@ class Collapse {
if (triggerArrayLength > 0) {
for (let i = 0; i < triggerArrayLength; i++) {
const trigger = this._triggerArray[i]
const selector = Util.getSelectorFromElement(trigger)
const selector = getSelectorFromElement(trigger)
if (selector !== null) {
const elem = SelectorEngine.findOne(selector)
......@@ -257,10 +267,10 @@ class Collapse {
}
this._element.style[dimension] = ''
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
const transitionDuration = getTransitionDurationFromElement(this._element)
EventHandler.one(this._element, Util.TRANSITION_END, complete)
Util.emulateTransitionEnd(this._element, transitionDuration)
EventHandler.one(this._element, TRANSITION_END, complete)
emulateTransitionEnd(this._element, transitionDuration)
}
setTransitioning(isTransitioning) {
......@@ -285,7 +295,7 @@ class Collapse {
...config
}
config.toggle = Boolean(config.toggle) // Coerce string values
Util.typeCheckConfig(NAME, config, DefaultType)
typeCheckConfig(NAME, config, DefaultType)
return config
}
......@@ -297,7 +307,7 @@ class Collapse {
_getParent() {
let parent
if (Util.isElement(this._config.parent)) {
if (isElement(this._config.parent)) {
parent = this._config.parent
// it's a jQuery object
......@@ -311,7 +321,7 @@ class Collapse {
const selector =
`[data-toggle="collapse"][data-parent="${this._config.parent}"]`
Util.makeArray(SelectorEngine.find(selector, parent))
makeArray(SelectorEngine.find(selector, parent))
.forEach((element) => {
this._addAriaAndCollapsedClass(
Collapse._getTargetFromElement(element),
......@@ -342,7 +352,7 @@ class Collapse {
// Static
static _getTargetFromElement(element) {
const selector = Util.getSelectorFromElement(element)
const selector = getSelectorFromElement(element)
return selector ? SelectorEngine.findOne(selector) : null
}
......@@ -394,8 +404,8 @@ EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (
}
const triggerData = Manipulator.getDataAttributes(this)
const selector = Util.getSelectorFromElement(this)
const selectorElements = Util.makeArray(SelectorEngine.find(selector))
const selector = getSelectorFromElement(this)
const selectorElements = makeArray(SelectorEngine.find(selector))
selectorElements.forEach((element) => {
const data = Data.getData(element, DATA_KEY)
......@@ -422,7 +432,6 @@ EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (
* add .collapse to jQuery only if jQuery is present
*/
const $ = Util.jQuery
if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Collapse._jQueryInterface
......
......@@ -5,8 +5,10 @@
* --------------------------------------------------------------------------
*/
import {
jQuery as $
} from '../util/index'
import Polyfill from './polyfill'
import Util from '../util'
/**
* ------------------------------------------------------------------------
......@@ -248,7 +250,6 @@ const EventHandler = {
const typeEvent = event.replace(stripNameRegex, '')
const inNamespace = event !== typeEvent
const isNative = nativeEvents.indexOf(typeEvent) > -1
const $ = Util.jQuery
let jQueryEvent
let bubbles = true
......
......@@ -5,7 +5,9 @@
* --------------------------------------------------------------------------
*/
import Util from '../util'
import {
getUID
} from '../util/index'
/* istanbul ignore next */
const Polyfill = (() => {
......@@ -48,7 +50,7 @@ const Polyfill = (() => {
const hasId = Boolean(this.id)
if (!hasId) {
this.id = Util.getUID('scope')
this.id = getUID('scope')
}
let nodeList = null
......
......@@ -6,7 +6,9 @@
*/
import Polyfill from './polyfill'
import Util from '../util'
import {
makeArray
} from '../util/index'
/**
* ------------------------------------------------------------------------
......@@ -44,7 +46,7 @@ const SelectorEngine = {
return null
}
const children = Util.makeArray(element.children)
const children = makeArray(element.children)
return children.filter((child) => this.matches(child, selector))
},
......
......@@ -5,12 +5,19 @@
* --------------------------------------------------------------------------
*/
import {
jQuery as $,
getSelectorFromElement,
isElement,
makeArray,
noop,
typeCheckConfig
} from './util/index'
import Data from './dom/data'
import EventHandler from './dom/eventHandler'
import Manipulator from './dom/manipulator'
import Popper from 'popper.js'
import SelectorEngine from './dom/selectorEngine'
import Util from './util'
/**
* ------------------------------------------------------------------------
......@@ -159,7 +166,7 @@ class Dropdown {
if (this._config.reference === 'parent') {
referenceElement = parent
} else if (Util.isElement(this._config.reference)) {
} else if (isElement(this._config.reference)) {
referenceElement = this._config.reference
// Check if it's jQuery element
......@@ -182,9 +189,9 @@ class Dropdown {
// 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 &&
!Util.makeArray(SelectorEngine.closest(parent, Selector.NAVBAR_NAV)).length) {
Util.makeArray(document.body.children)
.forEach((elem) => EventHandler.on(elem, 'mouseover', null, Util.noop()))
!makeArray(SelectorEngine.closest(parent, Selector.NAVBAR_NAV)).length) {
makeArray(document.body.children)
.forEach((elem) => EventHandler.on(elem, 'mouseover', null, noop()))
}
this._element.focus()
......@@ -272,7 +279,7 @@ class Dropdown {
...config
}
Util.typeCheckConfig(
typeCheckConfig(
NAME,
config,
this.constructor.DefaultType
......@@ -389,7 +396,7 @@ class Dropdown {
return
}
const toggles = Util.makeArray(SelectorEngine.find(Selector.DATA_TOGGLE))
const toggles = makeArray(SelectorEngine.find(Selector.DATA_TOGGLE))
for (let i = 0, len = toggles.length; i < len; i++) {
const parent = Dropdown._getParentFromElement(toggles[i])
const context = Data.getData(toggles[i], DATA_KEY)
......@@ -425,8 +432,8 @@ class Dropdown {
// If this is a touch-enabled device we remove the extra
// empty mouseover listeners we added for iOS support
if ('ontouchstart' in document.documentElement) {
Util.makeArray(document.body.children)
.forEach((elem) => EventHandler.off(elem, 'mouseover', null, Util.noop()))
makeArray(document.body.children)
.forEach((elem) => EventHandler.off(elem, 'mouseover', null, noop()))
}
toggles[i].setAttribute('aria-expanded', 'false')
......@@ -439,7 +446,7 @@ class Dropdown {
static _getParentFromElement(element) {
let parent
const selector = Util.getSelectorFromElement(element)
const selector = getSelectorFromElement(element)
if (selector) {
parent = SelectorEngine.findOne(selector)
......@@ -482,7 +489,7 @@ class Dropdown {
return
}
const items = Util.makeArray(SelectorEngine.find(Selector.VISIBLE_ITEMS, parent))
const items = makeArray(SelectorEngine.find(Selector.VISIBLE_ITEMS, parent))
if (!items.length) {
return
......@@ -535,7 +542,6 @@ EventHandler
* add .dropdown to jQuery only if jQuery is present
*/
const $ = Util.jQuery
if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Dropdown._jQueryInterface
......
......@@ -16,10 +16,8 @@ import ScrollSpy from './scrollspy'
import Tab from './tab'
import Toast from './toast'
import Tooltip from './tooltip'
import Util from './util'
export {
Util,
Alert,
Button,
Carousel,
......
......@@ -5,11 +5,21 @@
* --------------------------------------------------------------------------
*/
import {
jQuery as $,
TRANSITION_END,
emulateTransitionEnd,
getSelectorFromElement,
getTransitionDurationFromElement,
isVisible,
makeArray,
reflow,
typeCheckConfig
} from './util/index'
import Data from './dom/data'
import EventHandler from './dom/eventHandler'
import Manipulator from './dom/manipulator'
import SelectorEngine from './dom/selectorEngine'
import Util from './util'
/**
* ------------------------------------------------------------------------
......@@ -184,10 +194,10 @@ class Modal {
if (transition) {
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
const transitionDuration = getTransitionDurationFromElement(this._element)
EventHandler.one(this._element, Util.TRANSITION_END, (event) => this._hideModal(event))
Util.emulateTransitionEnd(this._element, transitionDuration)
EventHandler.one(this._element, TRANSITION_END, (event) => this._hideModal(event))
emulateTransitionEnd(this._element, transitionDuration)
} else {
this._hideModal()
}
......@@ -228,7 +238,7 @@ class Modal {
...Default,
...config
}
Util.typeCheckConfig(NAME, config, DefaultType)
typeCheckConfig(NAME, config, DefaultType)
return config
}
......@@ -252,7 +262,7 @@ class Modal {
}
if (transition) {
Util.reflow(this._element)
reflow(this._element)
}
this._element.classList.add(ClassName.SHOW)
......@@ -272,10 +282,10 @@ class Modal {
}
if (transition) {
const transitionDuration = Util.getTransitionDurationFromElement(this._dialog)
const transitionDuration = getTransitionDurationFromElement(this._dialog)
EventHandler.one(this._dialog, Util.TRANSITION_END, transitionComplete)
Util.emulateTransitionEnd(this._dialog, transitionDuration)
EventHandler.one(this._dialog, TRANSITION_END, transitionComplete)
emulateTransitionEnd(this._dialog, transitionDuration)
} else {
transitionComplete()
}
......@@ -364,7 +374,7 @@ class Modal {
})
if (animate) {
Util.reflow(this._backdrop)
reflow(this._backdrop)
}
this._backdrop.classList.add(ClassName.SHOW)
......@@ -378,10 +388,10 @@ class Modal {
return
}
const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop)
const backdropTransitionDuration = getTransitionDurationFromElement(this._backdrop)
EventHandler.one(this._backdrop, Util.TRANSITION_END, callback)
Util.emulateTransitionEnd(this._backdrop, backdropTransitionDuration)
EventHandler.one(this._backdrop, TRANSITION_END, callback)
emulateTransitionEnd(this._backdrop, backdropTransitionDuration)
} else if (!this._isShown && this._backdrop) {
this._backdrop.classList.remove(ClassName.SHOW)
......@@ -393,9 +403,9 @@ class Modal {
}
if (this._element.classList.contains(ClassName.FADE)) {
const backdropTransitionDuration = Util.getTransitionDurationFromElement(this._backdrop)
EventHandler.one(this._backdrop, Util.TRANSITION_END, callbackRemove)
Util.emulateTransitionEnd(this._backdrop, backdropTransitionDuration)
const backdropTransitionDuration = getTransitionDurationFromElement(this._backdrop)
EventHandler.one(this._backdrop, TRANSITION_END, callbackRemove)
emulateTransitionEnd(this._backdrop, backdropTransitionDuration)
} else {
callbackRemove()
}
......@@ -439,7 +449,7 @@ class Modal {
// while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set
// Adjust fixed content padding
Util.makeArray(SelectorEngine.find(Selector.FIXED_CONTENT))
makeArray(SelectorEngine.find(Selector.FIXED_CONTENT))
.forEach((element) => {
const actualPadding = element.style.paddingRight
const calculatedPadding = window.getComputedStyle(element)['padding-right']
......@@ -448,7 +458,7 @@ class Modal {
})
// Adjust sticky content margin
Util.makeArray(SelectorEngine.find(Selector.STICKY_CONTENT))
makeArray(SelectorEngine.find(Selector.STICKY_CONTENT))
.forEach((element) => {
const actualMargin = element.style.marginRight
const calculatedMargin = window.getComputedStyle(element)['margin-right']
......@@ -469,7 +479,7 @@ class Modal {
_resetScrollbar() {
// Restore fixed content padding
Util.makeArray(SelectorEngine.find(Selector.FIXED_CONTENT))
makeArray(SelectorEngine.find(Selector.FIXED_CONTENT))
.forEach((element) => {
const padding = Manipulator.getDataAttribute(element, 'padding-right')
if (typeof padding !== 'undefined') {
......@@ -479,7 +489,7 @@ class Modal {
})
// Restore sticky content and navbar-toggler margin
Util.makeArray(SelectorEngine.find(`${Selector.STICKY_CONTENT}`))
makeArray(SelectorEngine.find(`${Selector.STICKY_CONTENT}`))
.forEach((element) => {
const margin = Manipulator.getDataAttribute(element, 'margin-right')
if (typeof margin !== 'undefined') {
......@@ -546,7 +556,7 @@ class Modal {
EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) {
let target
const selector = Util.getSelectorFromElement(this)
const selector = getSelectorFromElement(this)
if (selector) {
target = SelectorEngine.findOne(selector)
......@@ -569,7 +579,7 @@ EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (
}
EventHandler.one(target, Event.HIDDEN, () => {
if (Util.isVisible(this)) {
if (isVisible(this)) {
this.focus()
}
})
......@@ -589,7 +599,6 @@ EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (
* ------------------------------------------------------------------------
*/
const $ = Util.jQuery
if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Modal._jQueryInterface
......
......@@ -5,10 +5,12 @@
* --------------------------------------------------------------------------
*/
import {
jQuery as $
} from './util/index'
import Data from './dom/data'
import SelectorEngine from './dom/selectorEngine'
import Tooltip from './tooltip'
import Util from './util'
/**
* ------------------------------------------------------------------------
......@@ -177,7 +179,6 @@ class Popover extends Tooltip {
* ------------------------------------------------------------------------
*/
const $ = Util.jQuery
if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Popover._jQueryInterface
......
......@@ -5,11 +5,17 @@
* --------------------------------------------------------------------------
*/
import {
jQuery as $,
getSelectorFromElement,
getUID,
makeArray,
typeCheckConfig
} from './util/index'
import Data from './dom/data'
import EventHandler from './dom/eventHandler'
import Manipulator from './dom/manipulator'
import SelectorEngine from './dom/selectorEngine'
import Util from './util'
/**
* ------------------------------------------------------------------------
......@@ -118,12 +124,12 @@ class ScrollSpy {
this._scrollHeight = this._getScrollHeight()
const targets = Util.makeArray(SelectorEngine.find(this._selector))
const targets = makeArray(SelectorEngine.find(this._selector))
targets
.map((element) => {
let target
const targetSelector = Util.getSelectorFromElement(element)
const targetSelector = getSelectorFromElement(element)
if (targetSelector) {
target = SelectorEngine.findOne(targetSelector)
......@@ -174,13 +180,13 @@ class ScrollSpy {
if (typeof config.target !== 'string') {
let id = config.target.id
if (!id) {
id = Util.getUID(NAME)
id = getUID(NAME)
config.target.id = id
}
config.target = `#${id}`
}
Util.typeCheckConfig(NAME, config, DefaultType)
typeCheckConfig(NAME, config, DefaultType)
return config
}
......@@ -284,7 +290,7 @@ class ScrollSpy {
}
_clear() {
Util.makeArray(SelectorEngine.find(this._selector))
makeArray(SelectorEngine.find(this._selector))
.filter((node) => node.classList.contains(ClassName.ACTIVE))
.forEach((node) => node.classList.remove(ClassName.ACTIVE))
}
......@@ -321,7 +327,7 @@ class ScrollSpy {
*/
EventHandler.on(window, Event.LOAD_DATA_API, () => {
Util.makeArray(SelectorEngine.find(Selector.DATA_SPY))
makeArray(SelectorEngine.find(Selector.DATA_SPY))
.forEach((spy) => new ScrollSpy(spy, Manipulator.getDataAttributes(spy)))
})
......@@ -331,7 +337,6 @@ EventHandler.on(window, Event.LOAD_DATA_API, () => {
* ------------------------------------------------------------------------
*/
const $ = Util.jQuery
if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = ScrollSpy._jQueryInterface
......
......@@ -5,10 +5,18 @@
* --------------------------------------------------------------------------
*/
import {
jQuery as $,
TRANSITION_END,
emulateTransitionEnd,
getSelectorFromElement,
getTransitionDurationFromElement,
makeArray,
reflow
} from './util/index'
import Data from './dom/data'
import EventHandler from './dom/eventHandler'
import SelectorEngine from './dom/selectorEngine'
import Util from './util'
/**
* ------------------------------------------------------------------------
......@@ -80,11 +88,11 @@ class Tab {
let target
let previous
const listElement = SelectorEngine.closest(this._element, Selector.NAV_LIST_GROUP)
const selector = Util.getSelectorFromElement(this._element)
const selector = getSelectorFromElement(this._element)
if (listElement) {
const itemSelector = listElement.nodeName === 'UL' || listElement.nodeName === 'OL' ? Selector.ACTIVE_UL : Selector.ACTIVE
previous = Util.makeArray(SelectorEngine.find(itemSelector, listElement))
previous = makeArray(SelectorEngine.find(itemSelector, listElement))
previous = previous[previous.length - 1]
}
......@@ -153,11 +161,11 @@ class Tab {
)
if (active && isTransitioning) {
const transitionDuration = Util.getTransitionDurationFromElement(active)
const transitionDuration = getTransitionDurationFromElement(active)
active.classList.remove(ClassName.SHOW)
EventHandler.one(active, Util.TRANSITION_END, complete)
Util.emulateTransitionEnd(active, transitionDuration)
EventHandler.one(active, TRANSITION_END, complete)
emulateTransitionEnd(active, transitionDuration)
} else {
complete()
}
......@@ -183,7 +191,7 @@ class Tab {
element.setAttribute('aria-selected', true)
}
Util.reflow(element)
reflow(element)
if (element.classList.contains(ClassName.FADE)) {
element.classList.add(ClassName.SHOW)
......@@ -193,7 +201,7 @@ class Tab {
const dropdownElement = SelectorEngine.closest(element, Selector.DROPDOWN)
if (dropdownElement) {
Util.makeArray(SelectorEngine.find(Selector.DROPDOWN_TOGGLE))
makeArray(SelectorEngine.find(Selector.DROPDOWN_TOGGLE))
.forEach((dropdown) => dropdown.classList.add(ClassName.ACTIVE))
}
......@@ -242,9 +250,9 @@ EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
* add .tab to jQuery only if jQuery is present
*/
const $ = Util.jQuery
if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Tab._jQueryInterface
......
......@@ -5,10 +5,16 @@
* --------------------------------------------------------------------------
*/
import {
jQuery as $,
TRANSITION_END,
emulateTransitionEnd,
getTransitionDurationFromElement,
typeCheckConfig
} from './util/index'
import Data from './dom/data'
import EventHandler from './dom/eventHandler'
import Manipulator from './dom/manipulator'
import Util from './util'
/**
* ------------------------------------------------------------------------
......@@ -104,10 +110,10 @@ class Toast {
this._element.classList.remove(ClassName.HIDE)
this._element.classList.add(ClassName.SHOWING)
if (this._config.animation) {
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
const transitionDuration = getTransitionDurationFromElement(this._element)
EventHandler.one(this._element, Util.TRANSITION_END, complete)
Util.emulateTransitionEnd(this._element, transitionDuration)
EventHandler.one(this._element, TRANSITION_END, complete)
emulateTransitionEnd(this._element, transitionDuration)
} else {
complete()
}
......@@ -153,7 +159,7 @@ class Toast {
...typeof config === 'object' && config ? config : {}
}
Util.typeCheckConfig(
typeCheckConfig(
NAME,
config,
this.constructor.DefaultType
......@@ -179,10 +185,10 @@ class Toast {
this._element.classList.remove(ClassName.SHOW)
if (this._config.animation) {
const transitionDuration = Util.getTransitionDurationFromElement(this._element)
const transitionDuration = getTransitionDurationFromElement(this._element)
EventHandler.one(this._element, Util.TRANSITION_END, complete)
Util.emulateTransitionEnd(this._element, transitionDuration)
EventHandler.one(this._element, TRANSITION_END, complete)
emulateTransitionEnd(this._element, transitionDuration)
} else {
complete()
}
......@@ -221,7 +227,6 @@ class Toast {
* add .toast to jQuery only if jQuery is present
*/
const $ = Util.jQuery
if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Toast._jQueryInterface
......
......@@ -5,16 +5,27 @@
* --------------------------------------------------------------------------
*/
import {
jQuery as $,
TRANSITION_END,
emulateTransitionEnd,
findShadowRoot,
getTransitionDurationFromElement,
getUID,
isElement,
makeArray,
noop,
typeCheckConfig
} from './util/index'
import {
DefaultWhitelist,
sanitizeHtml
} from './tools/sanitizer'
} from './util/sanitizer'
import Data from './dom/data'
import EventHandler from './dom/eventHandler'
import Manipulator from './dom/manipulator'
import Popper from 'popper.js'
import SelectorEngine from './dom/selectorEngine'
import Util from './util'
/**
* ------------------------------------------------------------------------
......@@ -257,7 +268,7 @@ class Tooltip {
if (this.isWithContent() && this._isEnabled) {
const showEvent = EventHandler.trigger(this.element, this.constructor.Event.SHOW)
const shadowRoot = Util.findShadowRoot(this.element)
const shadowRoot = findShadowRoot(this.element)
const isInTheDom = shadowRoot !== null
? shadowRoot.contains(this.element)
: this.element.ownerDocument.documentElement.contains(this.element)
......@@ -267,7 +278,7 @@ class Tooltip {
}
const tip = this.getTipElement()
const tipId = Util.getUID(this.constructor.NAME)
const tipId = getUID(this.constructor.NAME)
tip.setAttribute('id', tipId)
this.element.setAttribute('aria-describedby', tipId)
......@@ -323,8 +334,8 @@ class Tooltip {
// 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) {
Util.makeArray(document.body.children).forEach((element) => {
EventHandler.on(element, 'mouseover', Util.noop())
makeArray(document.body.children).forEach((element) => {
EventHandler.on(element, 'mouseover', noop())
})
}
......@@ -343,9 +354,9 @@ class Tooltip {
}
if (this.tip.classList.contains(ClassName.FADE)) {
const transitionDuration = Util.getTransitionDurationFromElement(this.tip)
EventHandler.one(this.tip, Util.TRANSITION_END, complete)
Util.emulateTransitionEnd(this.tip, transitionDuration)
const transitionDuration = getTransitionDurationFromElement(this.tip)
EventHandler.one(this.tip, TRANSITION_END, complete)
emulateTransitionEnd(this.tip, transitionDuration)
} else {
complete()
}
......@@ -381,8 +392,8 @@ class Tooltip {
// If this is a touch-enabled device we remove the extra
// empty mouseover listeners we added for iOS support
if ('ontouchstart' in document.documentElement) {
Util.makeArray(document.body.children)
.forEach((element) => EventHandler.off(element, 'mouseover', Util.noop))
makeArray(document.body.children)
.forEach((element) => EventHandler.off(element, 'mouseover', noop))
}
this._activeTrigger[Trigger.CLICK] = false
......@@ -390,9 +401,10 @@ class Tooltip {
this._activeTrigger[Trigger.HOVER] = false
if (this.tip.classList.contains(ClassName.FADE)) {
const transitionDuration = Util.getTransitionDurationFromElement(tip)
EventHandler.one(tip, Util.TRANSITION_END, complete)
Util.emulateTransitionEnd(tip, transitionDuration)
const transitionDuration = getTransitionDurationFromElement(tip)
EventHandler.one(tip, TRANSITION_END, complete)
emulateTransitionEnd(tip, transitionDuration)
} else {
complete()
}
......@@ -507,7 +519,7 @@ class Tooltip {
return document.body
}
if (Util.isElement(this.config.container)) {
if (isElement(this.config.container)) {
return this.config.container
}
......@@ -705,7 +717,7 @@ class Tooltip {
config.content = config.content.toString()
}
Util.typeCheckConfig(
typeCheckConfig(
NAME,
config,
this.constructor.DefaultType
......@@ -795,8 +807,9 @@ class Tooltip {
* ------------------------------------------------------------------------
* jQuery
* ------------------------------------------------------------------------
* add .tooltip to jQuery only if jQuery is present
*/
const $ = Util.jQuery
if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Tooltip._jQueryInterface
......
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.3.1): util.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
/**
* ------------------------------------------------------------------------
* Private TransitionEnd Helpers
* ------------------------------------------------------------------------
*/
const MAX_UID = 1000000
const MILLISECONDS_MULTIPLIER = 1000
// Shoutout AngusCroll (https://goo.gl/pxwQGp)
function toType(obj) {
return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase()
}
/**
* --------------------------------------------------------------------------
* Public Util Api
* --------------------------------------------------------------------------
*/
const Util = {
TRANSITION_END: 'transitionend',
getUID(prefix) {
do {
// eslint-disable-next-line no-bitwise
prefix += ~~(Math.random() * MAX_UID) // "~~" acts like a faster Math.floor() here
} while (document.getElementById(prefix))
return prefix
},
getSelectorFromElement(element) {
let selector = element.getAttribute('data-target')
if (!selector || selector === '#') {
const hrefAttr = element.getAttribute('href')
selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : ''
}
try {
return document.querySelector(selector) ? selector : null
} catch (err) {
return null
}
},
getTransitionDurationFromElement(element) {
if (!element) {
return 0
}
// Get transition-duration of the element
let transitionDuration = window.getComputedStyle(element).transitionDuration
let transitionDelay = window.getComputedStyle(element).transitionDelay
const floatTransitionDuration = parseFloat(transitionDuration)
const floatTransitionDelay = 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 (parseFloat(transitionDuration) + parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER
},
reflow(element) {
return element.offsetHeight
},
triggerTransitionEnd(element) {
element.dispatchEvent(new Event(Util.TRANSITION_END))
},
isElement(obj) {
return (obj[0] || obj).nodeType
},
emulateTransitionEnd(element, duration) {
let called = false
const durationPadding = 5
const emulatedDuration = duration + durationPadding
function listener() {
called = true
element.removeEventListener(Util.TRANSITION_END, listener)
}
element.addEventListener(Util.TRANSITION_END, listener)
setTimeout(() => {
if (!called) {
Util.triggerTransitionEnd(element)
}
}, emulatedDuration)
},
typeCheckConfig(componentName, config, configTypes) {
for (const property in configTypes) {
if (Object.prototype.hasOwnProperty.call(configTypes, property)) {
const expectedTypes = configTypes[property]
const value = config[property]
const valueType = value && Util.isElement(value)
? 'element' : toType(value)
if (!new RegExp(expectedTypes).test(valueType)) {
throw new Error(
`${componentName.toUpperCase()}: ` +
`Option "${property}" provided type "${valueType}" ` +
`but expected type "${expectedTypes}".`)
}
}
}
},
makeArray(nodeList) {
if (!nodeList) {
return []
}
return [].slice.call(nodeList)
},
isVisible(element) {
if (!element) {
return false
}
if (element.style !== null && element.parentNode !== null && typeof element.parentNode.style !== 'undefined') {
return element.style.display !== 'none' &&
element.parentNode.style.display !== 'none' &&
element.style.visibility !== 'hidden'
}
return false
},
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 Util.findShadowRoot(element.parentNode)
},
noop() {
// eslint-disable-next-line no-empty-function
return function () {}
},
get jQuery() {
return window.jQuery
}
}
export default Util
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.3.1): util/index.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
const MAX_UID = 1000000
const MILLISECONDS_MULTIPLIER = 1000
const TRANSITION_END = 'transitionend'
const jQuery = window.jQuery
// Shoutout AngusCroll (https://goo.gl/pxwQGp)
const toType = (obj) => ({}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase())
/**
* --------------------------------------------------------------------------
* Public Util Api
* --------------------------------------------------------------------------
*/
const getUID = (prefix) => {
do {
// eslint-disable-next-line no-bitwise
prefix += ~~(Math.random() * MAX_UID) // "~~" acts like a faster Math.floor() here
} while (document.getElementById(prefix))
return prefix
}
const getSelectorFromElement = (element) => {
let selector = element.getAttribute('data-target')
if (!selector || selector === '#') {
const hrefAttr = element.getAttribute('href')
selector = hrefAttr && hrefAttr !== '#' ? hrefAttr.trim() : ''
}
try {
return document.querySelector(selector) ? selector : null
} catch (err) {
return null
}
}
const getTransitionDurationFromElement = (element) => {
if (!element) {
return 0
}
// Get transition-duration of the element
let {
transitionDuration,
transitionDelay
} = window.getComputedStyle(element)
const floatTransitionDuration = parseFloat(transitionDuration)
const floatTransitionDelay = 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 (parseFloat(transitionDuration) + parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER
}
const triggerTransitionEnd = (element) => {
element.dispatchEvent(new Event(TRANSITION_END))
}
const isElement = (obj) => (obj[0] || obj).nodeType
const emulateTransitionEnd = (element, duration) => {
let called = false
const durationPadding = 5
const emulatedDuration = duration + durationPadding
function listener() {
called = true
element.removeEventListener(TRANSITION_END, listener)
}
element.addEventListener(TRANSITION_END, listener)
setTimeout(() => {
if (!called) {
triggerTransitionEnd(element)
}
}, emulatedDuration)
}
const typeCheckConfig = (componentName, config, configTypes) => {
Object.keys(configTypes)
.forEach((property) => {
const expectedTypes = configTypes[property]
const value = config[property]
const valueType = value && isElement(value)
? 'element' : toType(value)
if (!new RegExp(expectedTypes).test(valueType)) {
throw new Error(
`${componentName.toUpperCase()}: ` +
`Option "${property}" provided type "${valueType}" ` +
`but expected type "${expectedTypes}".`)
}
})
}
const makeArray = (nodeList) => {
if (!nodeList) {
return []
}
return [].slice.call(nodeList)
}
const isVisible = (element) => {
if (!element) {
return false
}
if (element.style && element.parentNode && element.parentNode.style) {
return element.style.display !== 'none' &&
element.parentNode.style.display !== 'none' &&
element.style.visibility !== 'hidden'
}
return 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)
}
// eslint-disable-next-line no-empty-function
const noop = () => function () {}
const reflow = (element) => element.offsetHeight
export {
jQuery,
TRANSITION_END,
getUID,
getSelectorFromElement,
getTransitionDurationFromElement,
triggerTransitionEnd,
isElement,
emulateTransitionEnd,
typeCheckConfig,
makeArray,
isVisible,
findShadowRoot,
noop,
reflow
}
/**
* --------------------------------------------------------------------------
* Bootstrap (v4.3.1): tools/sanitizer.js
* Bootstrap (v4.3.1): util/sanitizer.js
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
* --------------------------------------------------------------------------
*/
import Util from '../util'
import {
makeArray
} from './index'
const uriAttrs = [
'background',
......@@ -20,40 +22,6 @@ const uriAttrs = [
const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i
export const DefaultWhitelist = {
// 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', 'alt', 'title', 'width', 'height'],
li: [],
ol: [],
p: [],
pre: [],
s: [],
small: [],
span: [],
sub: [],
sup: [],
strong: [],
u: [],
ul: []
}
/**
* A pattern that recognizes a commonly useful subset of URLs that are safe.
*
......@@ -68,7 +36,7 @@ const SAFE_URL_PATTERN = /^(?:(?:https?|mailto|ftp|tel|file):|[^&:/?#]*(?:[/?#]|
*/
const DATA_URL_PATTERN = /^data:(?:image\/(?:bmp|gif|jpeg|jpg|png|tiff|webp)|video\/(?:mpeg|mp4|ogg|webm)|audio\/(?:mp3|oga|ogg|opus));base64,[a-z0-9+/]+=*$/i
function allowedAttribute(attr, allowedAttributeList) {
const allowedAttribute = (attr, allowedAttributeList) => {
const attrName = attr.nodeName.toLowerCase()
if (allowedAttributeList.indexOf(attrName) !== -1) {
......@@ -91,8 +59,42 @@ function allowedAttribute(attr, allowedAttributeList) {
return false
}
export const DefaultWhitelist = {
// 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', 'alt', 'title', 'width', 'height'],
li: [],
ol: [],
p: [],
pre: [],
s: [],
small: [],
span: [],
sub: [],
sup: [],
strong: [],
u: [],
ul: []
}
export function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) {
if (unsafeHtml.length === 0) {
if (!unsafeHtml.length) {
return unsafeHtml
}
......@@ -103,19 +105,19 @@ export function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) {
const domParser = new window.DOMParser()
const createdDocument = domParser.parseFromString(unsafeHtml, 'text/html')
const whitelistKeys = Object.keys(whiteList)
const elements = Util.makeArray(createdDocument.body.querySelectorAll('*'))
const elements = makeArray(createdDocument.body.querySelectorAll('*'))
for (let i = 0, len = elements.length; i < len; i++) {
const el = elements[i]
const elName = el.nodeName.toLowerCase()
if (whitelistKeys.indexOf(el.nodeName.toLowerCase()) === -1) {
if (whitelistKeys.indexOf(elName) === -1) {
el.parentNode.removeChild(el)
continue
}
const attributeList = Util.makeArray(el.attributes)
const attributeList = makeArray(el.attributes)
const whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || [])
attributeList.forEach((attr) => {
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment