diff options
author | Johann-S <johann.servoire@gmail.com> | 2019-02-23 01:37:55 +0300 |
---|---|---|
committer | XhmikosR <xhmikosr@gmail.com> | 2019-02-26 14:04:04 +0300 |
commit | 8a37045b798fd66ede9c68774f9bb657e28d956a (patch) | |
tree | 35a1cf1b26701975f9732e99553e53fb295678c7 /js/src | |
parent | 8affe84c722bc459e7152e57d36a4f515f537abf (diff) |
move util in a util folder with the sanitizer
Diffstat (limited to 'js/src')
-rw-r--r-- | js/src/alert.js | 17 | ||||
-rw-r--r-- | js/src/button.js | 5 | ||||
-rw-r--r-- | js/src/carousel.js | 36 | ||||
-rw-r--r-- | js/src/collapse.js | 51 | ||||
-rw-r--r-- | js/src/dom/eventHandler.js | 5 | ||||
-rw-r--r-- | js/src/dom/polyfill.js | 6 | ||||
-rw-r--r-- | js/src/dom/selectorEngine.js | 6 | ||||
-rw-r--r-- | js/src/dropdown.js | 30 | ||||
-rw-r--r-- | js/src/index.js | 2 | ||||
-rw-r--r-- | js/src/modal.js | 55 | ||||
-rw-r--r-- | js/src/popover.js | 5 | ||||
-rw-r--r-- | js/src/scrollspy.js | 21 | ||||
-rw-r--r-- | js/src/tab.js | 26 | ||||
-rw-r--r-- | js/src/toast.js | 23 | ||||
-rw-r--r-- | js/src/tooltip.js | 47 | ||||
-rw-r--r-- | js/src/util.js | 179 | ||||
-rw-r--r-- | js/src/util/index.js | 177 | ||||
-rw-r--r-- | js/src/util/sanitizer.js (renamed from js/src/tools/sanitizer.js) | 84 |
18 files changed, 425 insertions, 350 deletions
diff --git a/js/src/alert.js b/js/src/alert.js index a8d0cc5da9..9d8f69c465 100644 --- a/js/src/alert.js +++ b/js/src/alert.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 diff --git a/js/src/button.js b/js/src/button.js index 2cb6acabae..489fe9de35 100644 --- a/js/src/button.js +++ b/js/src/button.js @@ -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 diff --git a/js/src/carousel.js b/js/src/carousel.js index 0536593141..d253d51dfd 100644 --- a/js/src/carousel.js +++ b/js/src/carousel.js @@ -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 diff --git a/js/src/collapse.js b/js/src/collapse.js index 9c2773754c..847062d8d1 100644 --- a/js/src/collapse.js +++ b/js/src/collapse.js @@ -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 diff --git a/js/src/dom/eventHandler.js b/js/src/dom/eventHandler.js index 3b42388b8e..d92920cdaa 100644 --- a/js/src/dom/eventHandler.js +++ b/js/src/dom/eventHandler.js @@ -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 diff --git a/js/src/dom/polyfill.js b/js/src/dom/polyfill.js index cbae0f668e..579b7a10ed 100644 --- a/js/src/dom/polyfill.js +++ b/js/src/dom/polyfill.js @@ -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 diff --git a/js/src/dom/selectorEngine.js b/js/src/dom/selectorEngine.js index 95b5a9fb50..151bb54c75 100644 --- a/js/src/dom/selectorEngine.js +++ b/js/src/dom/selectorEngine.js @@ -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)) }, diff --git a/js/src/dropdown.js b/js/src/dropdown.js index 46aff8f884..f5150a016f 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -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 diff --git a/js/src/index.js b/js/src/index.js index 8a0044fe53..4d46d84b95 100644 --- a/js/src/index.js +++ b/js/src/index.js @@ -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, diff --git a/js/src/modal.js b/js/src/modal.js index 0897b317ab..7efb24f842 100644 --- a/js/src/modal.js +++ b/js/src/modal.js @@ -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 diff --git a/js/src/popover.js b/js/src/popover.js index ce5edcd5c4..958beaa3d0 100644 --- a/js/src/popover.js +++ b/js/src/popover.js @@ -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 diff --git a/js/src/scrollspy.js b/js/src/scrollspy.js index f5d1008af7..74b60b7348 100644 --- a/js/src/scrollspy.js +++ b/js/src/scrollspy.js @@ -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 diff --git a/js/src/tab.js b/js/src/tab.js index dab38c51c9..4320ee9eab 100644 --- a/js/src/tab.js +++ b/js/src/tab.js @@ -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 diff --git a/js/src/toast.js b/js/src/toast.js index 6e878e5c80..fba559197f 100644 --- a/js/src/toast.js +++ b/js/src/toast.js @@ -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 diff --git a/js/src/tooltip.js b/js/src/tooltip.js index 8c2dbe7b23..13a5c62b7e 100644 --- a/js/src/tooltip.js +++ b/js/src/tooltip.js @@ -6,15 +6,26 @@ */ 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 diff --git a/js/src/util.js b/js/src/util.js deleted file mode 100644 index 8b6226f61c..0000000000 --- a/js/src/util.js +++ /dev/null @@ -1,179 +0,0 @@ -/** - * -------------------------------------------------------------------------- - * 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 diff --git a/js/src/util/index.js b/js/src/util/index.js new file mode 100644 index 0000000000..7c86a95ffa --- /dev/null +++ b/js/src/util/index.js @@ -0,0 +1,177 @@ +/** + * -------------------------------------------------------------------------- + * 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 +} diff --git a/js/src/tools/sanitizer.js b/js/src/util/sanitizer.js index 3c1608fee8..f8bb172a95 100644 --- a/js/src/tools/sanitizer.js +++ b/js/src/util/sanitizer.js @@ -1,11 +1,13 @@ /** * -------------------------------------------------------------------------- - * 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) => { |