Comply to the new rules.

This commit is contained in:
XhmikosR 2019-02-26 13:20:34 +02:00
parent 44e6abcba5
commit 46c037410b
44 changed files with 854 additions and 771 deletions

View File

@ -7,16 +7,18 @@
'use strict' 'use strict'
const path = require('path') const path = require('path')
const rollup = require('rollup') const rollup = require('rollup')
const babel = require('rollup-plugin-babel') const babel = require('rollup-plugin-babel')
const banner = require('./banner.js') const banner = require('./banner.js')
const TEST = process.env.NODE_ENV === 'test' const TEST = process.env.NODE_ENV === 'test'
const plugins = [ const plugins = [
babel({ babel({
exclude: 'node_modules/**', // Only transpile our source code // Only transpile our source code
externalHelpersWhitelist: [ // Include only required helpers exclude: 'node_modules/**',
// Include only required helpers
externalHelpersWhitelist: [
'defineProperties', 'defineProperties',
'createClass', 'createClass',
'inheritsLoose', 'inheritsLoose',
@ -147,9 +149,7 @@ function getConfigByPluginKey(pluginKey) {
function build(plugin) { function build(plugin) {
console.log(`Building ${plugin} plugin...`) console.log(`Building ${plugin} plugin...`)
const config = getConfigByPluginKey(plugin) const { external, globals } = getConfigByPluginKey(plugin)
const external = config.external
const globals = config.globals
let pluginPath = rootPath let pluginPath = rootPath
const utilObjects = [ const utilObjects = [
@ -179,7 +179,7 @@ function build(plugin) {
input: bsPlugins[plugin], input: bsPlugins[plugin],
plugins, plugins,
external external
}).then((bundle) => { }).then(bundle => {
bundle.write({ bundle.write({
banner: banner(pluginFilename), banner: banner(pluginFilename),
format: 'umd', format: 'umd',
@ -189,8 +189,8 @@ function build(plugin) {
file: path.resolve(__dirname, `${pluginPath}${pluginFilename}`) file: path.resolve(__dirname, `${pluginPath}${pluginFilename}`)
}) })
.then(() => console.log(`Building ${plugin} plugin... Done!`)) .then(() => console.log(`Building ${plugin} plugin... Done!`))
.catch((err) => console.error(`${plugin}: ${err}`)) .catch(error => console.error(`${plugin}: ${error}`))
}) })
} }
Object.keys(bsPlugins).forEach((plugin) => build(plugin)) Object.keys(bsPlugins).forEach(plugin => build(plugin))

View File

@ -30,18 +30,21 @@ function walkAsync(directory, excludedDirectories, fileCallback, errback) {
if (excludedDirectories.has(path.parse(directory).base)) { if (excludedDirectories.has(path.parse(directory).base)) {
return return
} }
fs.readdir(directory, (err, names) => { fs.readdir(directory, (err, names) => {
if (err) { if (err) {
errback(err) errback(err)
return return
} }
names.forEach((name) => {
names.forEach(name => {
const filepath = path.join(directory, name) const filepath = path.join(directory, name)
fs.lstat(filepath, (err, stats) => { fs.lstat(filepath, (err, stats) => {
if (err) { if (err) {
process.nextTick(errback, err) process.nextTick(errback, err)
return return
} }
if (stats.isDirectory()) { if (stats.isDirectory()) {
process.nextTick(walkAsync, filepath, excludedDirectories, fileCallback, errback) process.nextTick(walkAsync, filepath, excludedDirectories, fileCallback, errback)
} else if (stats.isFile()) { } else if (stats.isFile()) {
@ -55,18 +58,21 @@ function walkAsync(directory, excludedDirectories, fileCallback, errback) {
function replaceRecursively(directory, excludedDirectories, allowedExtensions, original, replacement) { function replaceRecursively(directory, excludedDirectories, allowedExtensions, original, replacement) {
original = new RegExp(regExpQuote(original), 'g') original = new RegExp(regExpQuote(original), 'g')
replacement = regExpQuoteReplacement(replacement) replacement = regExpQuoteReplacement(replacement)
const updateFile = DRY_RUN ? (filepath) => { const updateFile = DRY_RUN ?
if (allowedExtensions.has(path.parse(filepath).ext)) { filepath => {
console.log(`FILE: ${filepath}`) if (allowedExtensions.has(path.parse(filepath).ext)) {
} else { console.log(`FILE: ${filepath}`)
console.log(`EXCLUDED:${filepath}`) } else {
console.log(`EXCLUDED:${filepath}`)
}
} :
filepath => {
if (allowedExtensions.has(path.parse(filepath).ext)) {
sh.sed('-i', original, replacement, filepath)
}
} }
} : (filepath) => {
if (allowedExtensions.has(path.parse(filepath).ext)) { walkAsync(directory, excludedDirectories, updateFile, err => {
sh.sed('-i', original, replacement, filepath)
}
}
walkAsync(directory, excludedDirectories, updateFile, (err) => {
console.error('ERROR while traversing directory!:') console.error('ERROR while traversing directory!:')
console.error(err) console.error(err)
process.exit(1) process.exit(1)
@ -79,6 +85,7 @@ function main(args) {
console.error('Got arguments:', args) console.error('Got arguments:', args)
process.exit(1) process.exit(1)
} }
const oldVersion = args[0] const oldVersion = args[0]
const newVersion = args[1] const newVersion = args[1]
const EXCLUDED_DIRS = new Set([ const EXCLUDED_DIRS = new Set([

View File

@ -43,7 +43,7 @@ const files = [
} }
] ]
files.forEach((file) => { files.forEach(file => {
fs.readFile(file.file, 'utf8', (err, data) => { fs.readFile(file.file, 'utf8', (err, data) => {
if (err) { if (err) {
throw err throw err

View File

@ -1,11 +1,13 @@
'use strict' 'use strict'
module.exports = (ctx) => ({ module.exports = ctx => ({
map: ctx.file.dirname.includes('examples') ? false : { map: ctx.file.dirname.includes('examples') ?
inline: false, false :
annotation: true, {
sourcesContent: true inline: false,
}, annotation: true,
sourcesContent: true
},
plugins: { plugins: {
autoprefixer: { autoprefixer: {
cascade: false cascade: false

View File

@ -1,18 +1,20 @@
'use strict' 'use strict'
const path = require('path') const path = require('path')
const babel = require('rollup-plugin-babel') const babel = require('rollup-plugin-babel')
const resolve = require('rollup-plugin-node-resolve') const resolve = require('rollup-plugin-node-resolve')
const banner = require('./banner.js') const banner = require('./banner.js')
const BUNDLE = process.env.BUNDLE === 'true' const BUNDLE = process.env.BUNDLE === 'true'
let fileDest = 'bootstrap.js' let fileDest = 'bootstrap.js'
const external = ['popper.js'] const external = ['popper.js']
const plugins = [ const plugins = [
babel({ babel({
exclude: 'node_modules/**', // Only transpile our source code // Only transpile our source code
externalHelpersWhitelist: [ // Include only required helpers exclude: 'node_modules/**',
// Include only required helpers
externalHelpersWhitelist: [
'defineProperties', 'defineProperties',
'createClass', 'createClass',
'inheritsLoose', 'inheritsLoose',

View File

@ -22,26 +22,26 @@ import SelectorEngine from './dom/selectorEngine'
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
const NAME = 'alert' const NAME = 'alert'
const VERSION = '4.3.1' const VERSION = '4.3.1'
const DATA_KEY = 'bs.alert' const DATA_KEY = 'bs.alert'
const EVENT_KEY = `.${DATA_KEY}` const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api' const DATA_API_KEY = '.data-api'
const Selector = { const Selector = {
DISMISS : '[data-dismiss="alert"]' DISMISS: '[data-dismiss="alert"]'
} }
const Event = { const Event = {
CLOSE : `close${EVENT_KEY}`, CLOSE: `close${EVENT_KEY}`,
CLOSED : `closed${EVENT_KEY}`, CLOSED: `closed${EVENT_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}` CLICK_DATA_API: `click${EVENT_KEY}${DATA_API_KEY}`
} }
const ClassName = { const ClassName = {
ALERT : 'alert', ALERT: 'alert',
FADE : 'fade', FADE: 'fade',
SHOW : 'show' SHOW: 'show'
} }
/** /**
@ -90,7 +90,7 @@ class Alert {
_getRootElement(element) { _getRootElement(element) {
const selector = getSelectorFromElement(element) const selector = getSelectorFromElement(element)
let parent = false let parent = false
if (selector) { if (selector) {
parent = SelectorEngine.findOne(selector) parent = SelectorEngine.findOne(selector)
@ -118,7 +118,7 @@ class Alert {
const transitionDuration = getTransitionDurationFromElement(element) const transitionDuration = getTransitionDurationFromElement(element)
EventHandler EventHandler
.one(element, TRANSITION_END, (event) => this._destroyElement(element, event)) .one(element, TRANSITION_END, event => this._destroyElement(element, event))
emulateTransitionEnd(element, transitionDuration) emulateTransitionEnd(element, transitionDuration)
} }
@ -178,9 +178,9 @@ EventHandler
if (typeof $ !== 'undefined') { if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Alert._jQueryInterface $.fn[NAME] = Alert._jQueryInterface
$.fn[NAME].Constructor = Alert $.fn[NAME].Constructor = Alert
$.fn[NAME].noConflict = () => { $.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT $.fn[NAME] = JQUERY_NO_CONFLICT
return Alert._jQueryInterface return Alert._jQueryInterface
} }

View File

@ -18,30 +18,30 @@ import SelectorEngine from './dom/selectorEngine'
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
const NAME = 'button' const NAME = 'button'
const VERSION = '4.3.1' const VERSION = '4.3.1'
const DATA_KEY = 'bs.button' const DATA_KEY = 'bs.button'
const EVENT_KEY = `.${DATA_KEY}` const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api' const DATA_API_KEY = '.data-api'
const ClassName = { const ClassName = {
ACTIVE : 'active', ACTIVE: 'active',
BUTTON : 'btn', BUTTON: 'btn',
FOCUS : 'focus' FOCUS: 'focus'
} }
const Selector = { const Selector = {
DATA_TOGGLE_CARROT : '[data-toggle^="button"]', DATA_TOGGLE_CARROT: '[data-toggle^="button"]',
DATA_TOGGLE : '[data-toggle="buttons"]', DATA_TOGGLE: '[data-toggle="buttons"]',
INPUT : 'input:not([type="hidden"])', INPUT: 'input:not([type="hidden"])',
ACTIVE : '.active', ACTIVE: '.active',
BUTTON : '.btn' BUTTON: '.btn'
} }
const Event = { const Event = {
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`, CLICK_DATA_API: `click${EVENT_KEY}${DATA_API_KEY}`,
FOCUS_DATA_API : `focus${EVENT_KEY}${DATA_API_KEY}`, FOCUS_DATA_API: `focus${EVENT_KEY}${DATA_API_KEY}`,
BLUR_DATA_API : `blur${EVENT_KEY}${DATA_API_KEY}` BLUR_DATA_API: `blur${EVENT_KEY}${DATA_API_KEY}`
} }
/** /**
@ -149,7 +149,7 @@ class Button {
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => { EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE_CARROT, event => {
event.preventDefault() event.preventDefault()
let button = event.target let button = event.target
@ -162,15 +162,16 @@ EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE_CARROT, (ev
data = new Button(button) data = new Button(button)
Data.setData(button, DATA_KEY, data) Data.setData(button, DATA_KEY, data)
} }
data.toggle() data.toggle()
}) })
EventHandler.on(document, Event.FOCUS_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => { EventHandler.on(document, Event.FOCUS_DATA_API, Selector.DATA_TOGGLE_CARROT, event => {
const button = SelectorEngine.closest(event.target, Selector.BUTTON) const button = SelectorEngine.closest(event.target, Selector.BUTTON)
button.classList.add(ClassName.FOCUS) button.classList.add(ClassName.FOCUS)
}) })
EventHandler.on(document, Event.BLUR_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => { EventHandler.on(document, Event.BLUR_DATA_API, Selector.DATA_TOGGLE_CARROT, event => {
const button = SelectorEngine.closest(event.target, Selector.BUTTON) const button = SelectorEngine.closest(event.target, Selector.BUTTON)
button.classList.remove(ClassName.FOCUS) button.classList.remove(ClassName.FOCUS)
}) })
@ -183,11 +184,11 @@ EventHandler.on(document, Event.BLUR_DATA_API, Selector.DATA_TOGGLE_CARROT, (eve
*/ */
if (typeof $ !== 'undefined') { if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Button._jQueryInterface $.fn[NAME] = Button._jQueryInterface
$.fn[NAME].Constructor = Button $.fn[NAME].Constructor = Button
$.fn[NAME].noConflict = () => { $.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT $.fn[NAME] = JQUERY_NO_CONFLICT
return Button._jQueryInterface return Button._jQueryInterface
} }

View File

@ -28,83 +28,83 @@ import SelectorEngine from './dom/selectorEngine'
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
const NAME = 'carousel' const NAME = 'carousel'
const VERSION = '4.3.1' const VERSION = '4.3.1'
const DATA_KEY = 'bs.carousel' const DATA_KEY = 'bs.carousel'
const EVENT_KEY = `.${DATA_KEY}` const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api' const DATA_API_KEY = '.data-api'
const ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key const ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key
const ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key const ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key
const TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch const TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch
const SWIPE_THRESHOLD = 40 const SWIPE_THRESHOLD = 40
const Default = { const Default = {
interval : 5000, interval: 5000,
keyboard : true, keyboard: true,
slide : false, slide: false,
pause : 'hover', pause: 'hover',
wrap : true, wrap: true,
touch : true touch: true
} }
const DefaultType = { const DefaultType = {
interval : '(number|boolean)', interval: '(number|boolean)',
keyboard : 'boolean', keyboard: 'boolean',
slide : '(boolean|string)', slide: '(boolean|string)',
pause : '(string|boolean)', pause: '(string|boolean)',
wrap : 'boolean', wrap: 'boolean',
touch : 'boolean' touch: 'boolean'
} }
const Direction = { const Direction = {
NEXT : 'next', NEXT: 'next',
PREV : 'prev', PREV: 'prev',
LEFT : 'left', LEFT: 'left',
RIGHT : 'right' RIGHT: 'right'
} }
const Event = { const Event = {
SLIDE : `slide${EVENT_KEY}`, SLIDE: `slide${EVENT_KEY}`,
SLID : `slid${EVENT_KEY}`, SLID: `slid${EVENT_KEY}`,
KEYDOWN : `keydown${EVENT_KEY}`, KEYDOWN: `keydown${EVENT_KEY}`,
MOUSEENTER : `mouseenter${EVENT_KEY}`, MOUSEENTER: `mouseenter${EVENT_KEY}`,
MOUSELEAVE : `mouseleave${EVENT_KEY}`, MOUSELEAVE: `mouseleave${EVENT_KEY}`,
TOUCHSTART : `touchstart${EVENT_KEY}`, TOUCHSTART: `touchstart${EVENT_KEY}`,
TOUCHMOVE : `touchmove${EVENT_KEY}`, TOUCHMOVE: `touchmove${EVENT_KEY}`,
TOUCHEND : `touchend${EVENT_KEY}`, TOUCHEND: `touchend${EVENT_KEY}`,
POINTERDOWN : `pointerdown${EVENT_KEY}`, POINTERDOWN: `pointerdown${EVENT_KEY}`,
POINTERUP : `pointerup${EVENT_KEY}`, POINTERUP: `pointerup${EVENT_KEY}`,
DRAG_START : `dragstart${EVENT_KEY}`, DRAG_START: `dragstart${EVENT_KEY}`,
LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`, LOAD_DATA_API: `load${EVENT_KEY}${DATA_API_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}` CLICK_DATA_API: `click${EVENT_KEY}${DATA_API_KEY}`
} }
const ClassName = { const ClassName = {
CAROUSEL : 'carousel', CAROUSEL: 'carousel',
ACTIVE : 'active', ACTIVE: 'active',
SLIDE : 'slide', SLIDE: 'slide',
RIGHT : 'carousel-item-right', RIGHT: 'carousel-item-right',
LEFT : 'carousel-item-left', LEFT: 'carousel-item-left',
NEXT : 'carousel-item-next', NEXT: 'carousel-item-next',
PREV : 'carousel-item-prev', PREV: 'carousel-item-prev',
ITEM : 'carousel-item', ITEM: 'carousel-item',
POINTER_EVENT : 'pointer-event' POINTER_EVENT: 'pointer-event'
} }
const Selector = { const Selector = {
ACTIVE : '.active', ACTIVE: '.active',
ACTIVE_ITEM : '.active.carousel-item', ACTIVE_ITEM: '.active.carousel-item',
ITEM : '.carousel-item', ITEM: '.carousel-item',
ITEM_IMG : '.carousel-item img', ITEM_IMG: '.carousel-item img',
NEXT_PREV : '.carousel-item-next, .carousel-item-prev', NEXT_PREV: '.carousel-item-next, .carousel-item-prev',
INDICATORS : '.carousel-indicators', INDICATORS: '.carousel-indicators',
DATA_SLIDE : '[data-slide], [data-slide-to]', DATA_SLIDE: '[data-slide], [data-slide-to]',
DATA_RIDE : '[data-ride="carousel"]' DATA_RIDE: '[data-ride="carousel"]'
} }
const PointerType = { const PointerType = {
TOUCH : 'touch', TOUCH: 'touch',
PEN : 'pen' PEN: 'pen'
} }
/** /**
@ -114,20 +114,20 @@ const PointerType = {
*/ */
class Carousel { class Carousel {
constructor(element, config) { constructor(element, config) {
this._items = null this._items = null
this._interval = null this._interval = null
this._activeElement = null this._activeElement = null
this._isPaused = false this._isPaused = false
this._isSliding = false this._isSliding = false
this.touchTimeout = null this.touchTimeout = null
this.touchStartX = 0 this.touchStartX = 0
this.touchDeltaX = 0 this.touchDeltaX = 0
this._config = this._getConfig(config) this._config = this._getConfig(config)
this._element = element this._element = element
this._indicatorsElement = SelectorEngine.findOne(Selector.INDICATORS, this._element) this._indicatorsElement = SelectorEngine.findOne(Selector.INDICATORS, this._element)
this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0 this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0
this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent) this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent)
this._addEventListeners() this._addEventListeners()
Data.setData(element, DATA_KEY, this) Data.setData(element, DATA_KEY, this)
@ -216,9 +216,9 @@ class Carousel {
return return
} }
const direction = index > activeIndex const direction = index > activeIndex ?
? Direction.NEXT Direction.NEXT :
: Direction.PREV Direction.PREV
this._slide(direction, this._items[index]) this._slide(direction, this._items[index])
} }
@ -227,13 +227,13 @@ class Carousel {
EventHandler.off(this._element, EVENT_KEY) EventHandler.off(this._element, EVENT_KEY)
Data.removeData(this._element, DATA_KEY) Data.removeData(this._element, DATA_KEY)
this._items = null this._items = null
this._config = null this._config = null
this._element = null this._element = null
this._interval = null this._interval = null
this._isPaused = null this._isPaused = null
this._isSliding = null this._isSliding = null
this._activeElement = null this._activeElement = null
this._indicatorsElement = null this._indicatorsElement = null
} }
@ -271,14 +271,14 @@ class Carousel {
_addEventListeners() { _addEventListeners() {
if (this._config.keyboard) { if (this._config.keyboard) {
EventHandler EventHandler
.on(this._element, Event.KEYDOWN, (event) => this._keydown(event)) .on(this._element, Event.KEYDOWN, event => this._keydown(event))
} }
if (this._config.pause === 'hover') { if (this._config.pause === 'hover') {
EventHandler EventHandler
.on(this._element, Event.MOUSEENTER, (event) => this.pause(event)) .on(this._element, Event.MOUSEENTER, event => this.pause(event))
EventHandler EventHandler
.on(this._element, Event.MOUSELEAVE, (event) => this.cycle(event)) .on(this._element, Event.MOUSELEAVE, event => this.cycle(event))
} }
if (this._config.touch) { if (this._config.touch) {
@ -291,7 +291,7 @@ class Carousel {
return return
} }
const start = (event) => { const start = event => {
if (this._pointerEvent && PointerType[event.pointerType.toUpperCase()]) { if (this._pointerEvent && PointerType[event.pointerType.toUpperCase()]) {
this.touchStartX = event.clientX this.touchStartX = event.clientX
} else if (!this._pointerEvent) { } else if (!this._pointerEvent) {
@ -299,7 +299,7 @@ class Carousel {
} }
} }
const move = (event) => { const move = event => {
// ensure swiping with one touch and not pinching // ensure swiping with one touch and not pinching
if (event.touches && event.touches.length > 1) { if (event.touches && event.touches.length > 1) {
this.touchDeltaX = 0 this.touchDeltaX = 0
@ -308,7 +308,7 @@ class Carousel {
} }
} }
const end = (event) => { const end = event => {
if (this._pointerEvent && PointerType[event.pointerType.toUpperCase()]) { if (this._pointerEvent && PointerType[event.pointerType.toUpperCase()]) {
this.touchDeltaX = event.clientX - this.touchStartX this.touchDeltaX = event.clientX - this.touchStartX
} }
@ -327,23 +327,24 @@ class Carousel {
if (this.touchTimeout) { if (this.touchTimeout) {
clearTimeout(this.touchTimeout) clearTimeout(this.touchTimeout)
} }
this.touchTimeout = setTimeout((event) => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)
this.touchTimeout = setTimeout(event => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)
} }
} }
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()) EventHandler.on(itemImg, Event.DRAG_START, e => e.preventDefault())
}) })
if (this._pointerEvent) { if (this._pointerEvent) {
EventHandler.on(this._element, Event.POINTERDOWN, (event) => start(event)) EventHandler.on(this._element, Event.POINTERDOWN, event => start(event))
EventHandler.on(this._element, Event.POINTERUP, (event) => end(event)) EventHandler.on(this._element, Event.POINTERUP, event => end(event))
this._element.classList.add(ClassName.POINTER_EVENT) this._element.classList.add(ClassName.POINTER_EVENT)
} else { } else {
EventHandler.on(this._element, Event.TOUCHSTART, (event) => start(event)) EventHandler.on(this._element, Event.TOUCHSTART, event => start(event))
EventHandler.on(this._element, Event.TOUCHMOVE, (event) => move(event)) EventHandler.on(this._element, Event.TOUCHMOVE, event => move(event))
EventHandler.on(this._element, Event.TOUCHEND, (event) => end(event)) EventHandler.on(this._element, Event.TOUCHEND, event => end(event))
} }
} }
@ -366,9 +367,9 @@ class Carousel {
} }
_getItemIndex(element) { _getItemIndex(element) {
this._items = element && element.parentNode this._items = element && element.parentNode ?
? makeArray(SelectorEngine.find(Selector.ITEM, element.parentNode)) makeArray(SelectorEngine.find(Selector.ITEM, element.parentNode)) :
: [] []
return this._items.indexOf(element) return this._items.indexOf(element)
} }
@ -376,20 +377,21 @@ class Carousel {
_getItemByDirection(direction, activeElement) { _getItemByDirection(direction, activeElement) {
const isNextDirection = direction === Direction.NEXT const isNextDirection = direction === Direction.NEXT
const isPrevDirection = direction === Direction.PREV const isPrevDirection = direction === Direction.PREV
const activeIndex = this._getItemIndex(activeElement) const activeIndex = this._getItemIndex(activeElement)
const lastItemIndex = this._items.length - 1 const lastItemIndex = this._items.length - 1
const isGoingToWrap = isPrevDirection && activeIndex === 0 || const isGoingToWrap = isPrevDirection && activeIndex === 0 ||
isNextDirection && activeIndex === lastItemIndex isNextDirection && activeIndex === lastItemIndex
if (isGoingToWrap && !this._config.wrap) { if (isGoingToWrap && !this._config.wrap) {
return activeElement return activeElement
} }
const delta = direction === Direction.PREV ? -1 : 1 const delta = direction === Direction.PREV ? -1 : 1
const itemIndex = (activeIndex + delta) % this._items.length const itemIndex = (activeIndex + delta) % this._items.length
return itemIndex === -1 return itemIndex === -1 ?
? this._items[this._items.length - 1] : this._items[itemIndex] this._items[this._items.length - 1] :
this._items[itemIndex]
} }
_triggerSlideEvent(relatedTarget, eventDirectionName) { _triggerSlideEvent(relatedTarget, eventDirectionName) {
@ -424,7 +426,7 @@ class Carousel {
_slide(direction, element) { _slide(direction, element) {
const activeElement = SelectorEngine.findOne(Selector.ACTIVE_ITEM, this._element) const activeElement = SelectorEngine.findOne(Selector.ACTIVE_ITEM, this._element)
const activeElementIndex = this._getItemIndex(activeElement) const activeElementIndex = this._getItemIndex(activeElement)
const nextElement = element || activeElement && const nextElement = element || activeElement &&
this._getItemByDirection(direction, activeElement) this._getItemByDirection(direction, activeElement)
const nextElementIndex = this._getItemIndex(nextElement) const nextElementIndex = this._getItemIndex(nextElement)
@ -529,7 +531,7 @@ class Carousel {
// Static // Static
static _carouselInterface(element, config) { static _carouselInterface(element, config) {
let data = Data.getData(element, DATA_KEY) let data = Data.getData(element, DATA_KEY)
let _config = { let _config = {
...Default, ...Default,
...Manipulator.getDataAttributes(element) ...Manipulator.getDataAttributes(element)
@ -552,8 +554,9 @@ class Carousel {
data.to(config) data.to(config)
} else if (typeof action === 'string') { } else if (typeof action === 'string') {
if (typeof data[action] === 'undefined') { if (typeof data[action] === 'undefined') {
throw new Error(`No method named "${action}"`) throw new TypeError(`No method named "${action}"`)
} }
data[action]() data[action]()
} else if (_config.interval && _config.ride) { } else if (_config.interval && _config.ride) {
data.pause() data.pause()
@ -629,9 +632,9 @@ EventHandler.on(window, Event.LOAD_DATA_API, () => {
if (typeof $ !== 'undefined') { if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Carousel._jQueryInterface $.fn[NAME] = Carousel._jQueryInterface
$.fn[NAME].Constructor = Carousel $.fn[NAME].Constructor = Carousel
$.fn[NAME].noConflict = () => { $.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT $.fn[NAME] = JQUERY_NO_CONFLICT
return Carousel._jQueryInterface return Carousel._jQueryInterface
} }

View File

@ -27,45 +27,45 @@ import SelectorEngine from './dom/selectorEngine'
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
const NAME = 'collapse' const NAME = 'collapse'
const VERSION = '4.3.1' const VERSION = '4.3.1'
const DATA_KEY = 'bs.collapse' const DATA_KEY = 'bs.collapse'
const EVENT_KEY = `.${DATA_KEY}` const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api' const DATA_API_KEY = '.data-api'
const Default = { const Default = {
toggle : true, toggle: true,
parent : '' parent: ''
} }
const DefaultType = { const DefaultType = {
toggle : 'boolean', toggle: 'boolean',
parent : '(string|element)' parent: '(string|element)'
} }
const Event = { const Event = {
SHOW : `show${EVENT_KEY}`, SHOW: `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}`, SHOWN: `shown${EVENT_KEY}`,
HIDE : `hide${EVENT_KEY}`, HIDE: `hide${EVENT_KEY}`,
HIDDEN : `hidden${EVENT_KEY}`, HIDDEN: `hidden${EVENT_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}` CLICK_DATA_API: `click${EVENT_KEY}${DATA_API_KEY}`
} }
const ClassName = { const ClassName = {
SHOW : 'show', SHOW: 'show',
COLLAPSE : 'collapse', COLLAPSE: 'collapse',
COLLAPSING : 'collapsing', COLLAPSING: 'collapsing',
COLLAPSED : 'collapsed' COLLAPSED: 'collapsed'
} }
const Dimension = { const Dimension = {
WIDTH : 'width', WIDTH: 'width',
HEIGHT : 'height' HEIGHT: 'height'
} }
const Selector = { const Selector = {
ACTIVES : '.show, .collapsing', ACTIVES: '.show, .collapsing',
DATA_TOGGLE : '[data-toggle="collapse"]' DATA_TOGGLE: '[data-toggle="collapse"]'
} }
/** /**
@ -77,9 +77,9 @@ const Selector = {
class Collapse { class Collapse {
constructor(element, config) { constructor(element, config) {
this._isTransitioning = false this._isTransitioning = false
this._element = element this._element = element
this._config = this._getConfig(config) this._config = this._getConfig(config)
this._triggerArray = makeArray(SelectorEngine.find( this._triggerArray = makeArray(SelectorEngine.find(
`[data-toggle="collapse"][href="#${element.id}"],` + `[data-toggle="collapse"][href="#${element.id}"],` +
`[data-toggle="collapse"][data-target="#${element.id}"]` `[data-toggle="collapse"][data-target="#${element.id}"]`
)) ))
@ -89,7 +89,7 @@ class Collapse {
const elem = toggleList[i] const elem = toggleList[i]
const selector = getSelectorFromElement(elem) const selector = getSelectorFromElement(elem)
const filterElement = makeArray(SelectorEngine.find(selector)) const filterElement = makeArray(SelectorEngine.find(selector))
.filter((foundElem) => foundElem === element) .filter(foundElem => foundElem === element)
if (selector !== null && filterElement.length) { if (selector !== null && filterElement.length) {
this._selector = selector this._selector = selector
@ -141,7 +141,7 @@ class Collapse {
if (this._parent) { if (this._parent) {
actives = makeArray(SelectorEngine.find(Selector.ACTIVES, this._parent)) actives = makeArray(SelectorEngine.find(Selector.ACTIVES, this._parent))
.filter((elem) => { .filter(elem => {
if (typeof this._config.parent === 'string') { if (typeof this._config.parent === 'string') {
return elem.getAttribute('data-parent') === this._config.parent return elem.getAttribute('data-parent') === this._config.parent
} }
@ -156,7 +156,7 @@ class Collapse {
const container = SelectorEngine.findOne(this._selector) const container = SelectorEngine.findOne(this._selector)
if (actives) { if (actives) {
const tempActiveData = actives.filter((elem) => container !== elem) const tempActiveData = actives.filter(elem => container !== elem)
activesData = tempActiveData[0] ? Data.getData(tempActiveData[0], DATA_KEY) : null activesData = tempActiveData[0] ? Data.getData(tempActiveData[0], DATA_KEY) : null
if (activesData && activesData._isTransitioning) { if (activesData && activesData._isTransitioning) {
@ -170,7 +170,7 @@ class Collapse {
} }
if (actives) { if (actives) {
actives.forEach((elemActive) => { actives.forEach(elemActive => {
if (container !== elemActive) { if (container !== elemActive) {
Collapse._collapseInterface(elemActive, 'hide') Collapse._collapseInterface(elemActive, 'hide')
} }
@ -189,7 +189,7 @@ class Collapse {
this._element.style[dimension] = 0 this._element.style[dimension] = 0
if (this._triggerArray.length) { if (this._triggerArray.length) {
this._triggerArray.forEach((element) => { this._triggerArray.forEach(element => {
element.classList.remove(ClassName.COLLAPSED) element.classList.remove(ClassName.COLLAPSED)
element.setAttribute('aria-expanded', true) element.setAttribute('aria-expanded', true)
}) })
@ -280,10 +280,10 @@ class Collapse {
dispose() { dispose() {
Data.removeData(this._element, DATA_KEY) Data.removeData(this._element, DATA_KEY)
this._config = null this._config = null
this._parent = null this._parent = null
this._element = null this._element = null
this._triggerArray = null this._triggerArray = null
this._isTransitioning = null this._isTransitioning = null
} }
@ -308,7 +308,7 @@ class Collapse {
let parent let parent
if (isElement(this._config.parent)) { if (isElement(this._config.parent)) {
parent = this._config.parent { parent } = this._config
// it's a jQuery object // it's a jQuery object
if (typeof this._config.parent.jquery !== 'undefined' || typeof this._config.parent[0] !== 'undefined') { if (typeof this._config.parent.jquery !== 'undefined' || typeof this._config.parent[0] !== 'undefined') {
@ -318,11 +318,10 @@ class Collapse {
parent = SelectorEngine.findOne(this._config.parent) parent = SelectorEngine.findOne(this._config.parent)
} }
const selector = const selector = `[data-toggle="collapse"][data-parent="${parent}"]`
`[data-toggle="collapse"][data-parent="${this._config.parent}"]`
makeArray(SelectorEngine.find(selector, parent)) makeArray(SelectorEngine.find(selector, parent))
.forEach((element) => { .forEach(element => {
this._addAriaAndCollapsedClass( this._addAriaAndCollapsedClass(
Collapse._getTargetFromElement(element), Collapse._getTargetFromElement(element),
[element] [element]
@ -337,12 +336,13 @@ class Collapse {
const isOpen = element.classList.contains(ClassName.SHOW) const isOpen = element.classList.contains(ClassName.SHOW)
if (triggerArray.length) { if (triggerArray.length) {
triggerArray.forEach((elem) => { triggerArray.forEach(elem => {
if (!isOpen) { if (isOpen) {
elem.classList.add(ClassName.COLLAPSED)
} else {
elem.classList.remove(ClassName.COLLAPSED) elem.classList.remove(ClassName.COLLAPSED)
} else {
elem.classList.add(ClassName.COLLAPSED)
} }
elem.setAttribute('aria-expanded', isOpen) elem.setAttribute('aria-expanded', isOpen)
}) })
} }
@ -357,7 +357,7 @@ class Collapse {
} }
static _collapseInterface(element, config) { static _collapseInterface(element, config) {
let data = Data.getData(element, DATA_KEY) let data = Data.getData(element, DATA_KEY)
const _config = { const _config = {
...Default, ...Default,
...Manipulator.getDataAttributes(element), ...Manipulator.getDataAttributes(element),
@ -374,8 +374,9 @@ class Collapse {
if (typeof config === 'string') { if (typeof config === 'string') {
if (typeof data[config] === 'undefined') { if (typeof data[config] === 'undefined') {
throw new Error(`No method named "${config}"`) throw new TypeError(`No method named "${config}"`)
} }
data[config]() data[config]()
} }
} }
@ -403,11 +404,11 @@ EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (
event.preventDefault() event.preventDefault()
} }
const triggerData = Manipulator.getDataAttributes(this) const triggerData = Manipulator.getDataAttributes(this)
const selector = getSelectorFromElement(this) const selector = getSelectorFromElement(this)
const selectorElements = makeArray(SelectorEngine.find(selector)) const selectorElements = makeArray(SelectorEngine.find(selector))
selectorElements.forEach((element) => { selectorElements.forEach(element => {
const data = Data.getData(element, DATA_KEY) const data = Data.getData(element, DATA_KEY)
let config let config
if (data) { if (data) {
@ -416,6 +417,7 @@ EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (
data._config.parent = triggerData.parent data._config.parent = triggerData.parent
data._parent = data._getParent() data._parent = data._getParent()
} }
config = 'toggle' config = 'toggle'
} else { } else {
config = triggerData config = triggerData
@ -433,10 +435,10 @@ EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (
*/ */
if (typeof $ !== 'undefined') { if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Collapse._jQueryInterface $.fn[NAME] = Collapse._jQueryInterface
$.fn[NAME].Constructor = Collapse $.fn[NAME].Constructor = Collapse
$.fn[NAME].noConflict = () => { $.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT $.fn[NAME] = JQUERY_NO_CONFLICT
return Collapse._jQueryInterface return Collapse._jQueryInterface
} }

View File

@ -35,6 +35,7 @@ const mapData = (() => {
if (keyProperties.key === key) { if (keyProperties.key === key) {
return storeData[keyProperties.id] return storeData[keyProperties.id]
} }
return null return null
}, },
delete(element, key) { delete(element, key) {

View File

@ -18,26 +18,61 @@ import Polyfill from './polyfill'
const namespaceRegex = /[^.]*(?=\..*)\.|.*/ const namespaceRegex = /[^.]*(?=\..*)\.|.*/
const stripNameRegex = /\..*/ const stripNameRegex = /\..*/
const keyEventRegex = /^key/ const keyEventRegex = /^key/
const stripUidRegex = /::\d+$/ const stripUidRegex = /::\d+$/
const eventRegistry = {} // Events storage const eventRegistry = {} // Events storage
let uidEvent = 1 let uidEvent = 1
const customEvents = { const customEvents = {
mouseenter: 'mouseover', mouseenter: 'mouseover',
mouseleave: 'mouseout' mouseleave: 'mouseout'
} }
const nativeEvents = [ const nativeEvents = [
'click', 'dblclick', 'mouseup', 'mousedown', 'contextmenu', 'click',
'mousewheel', 'DOMMouseScroll', 'dblclick',
'mouseover', 'mouseout', 'mousemove', 'selectstart', 'selectend', 'mouseup',
'keydown', 'keypress', 'keyup', 'mousedown',
'contextmenu',
'mousewheel',
'DOMMouseScroll',
'mouseover',
'mouseout',
'mousemove',
'selectstart',
'selectend',
'keydown',
'keypress',
'keyup',
'orientationchange', 'orientationchange',
'touchstart', 'touchmove', 'touchend', 'touchcancel', 'touchstart',
'pointerdown', 'pointermove', 'pointerup', 'pointerleave', 'pointercancel', 'touchmove',
'gesturestart', 'gesturechange', 'gestureend', 'touchend',
'focus', 'blur', 'change', 'reset', 'select', 'submit', 'focusin', 'focusout', 'touchcancel',
'load', 'unload', 'beforeunload', 'resize', 'move', 'DOMContentLoaded', 'readystatechange', 'pointerdown',
'error', 'abort', 'scroll' 'pointermove',
'pointerup',
'pointerleave',
'pointercancel',
'gesturestart',
'gesturechange',
'gestureend',
'focus',
'blur',
'change',
'reset',
'select',
'submit',
'focusin',
'focusout',
'load',
'unload',
'beforeunload',
'resize',
'move',
'DOMContentLoaded',
'readystatechange',
'error',
'abort',
'scroll'
] ]
/** /**
@ -60,7 +95,7 @@ function getEvent(element) {
function fixEvent(event, element) { function fixEvent(event, element) {
// Add which for key events // Add which for key events
if (event.which === null && keyEventRegex.test(event.type)) { if (event.which === null && keyEventRegex.test(event.type)) {
event.which = event.charCode !== null ? event.charCode : event.keyCode event.which = event.charCode === null ? event.keyCode : event.charCode
} }
event.delegateTarget = element event.delegateTarget = element
@ -81,7 +116,7 @@ function bootstrapDelegationHandler(element, selector, fn) {
return function handler(event) { return function handler(event) {
const domElements = element.querySelectorAll(selector) const domElements = element.querySelectorAll(selector)
for (let target = event.target; target && target !== this; target = target.parentNode) { for (let { target } = event; target && target !== this; target = target.parentNode) {
for (let i = domElements.length; i--;) { for (let i = domElements.length; i--;) {
if (domElements[i] === target) { if (domElements[i] === target) {
fixEvent(event, target) fixEvent(event, target)
@ -147,8 +182,8 @@ function addHandler(element, originalTypeEvent, handler, delegationFn, oneOff) {
} }
const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn) const [delegation, originalHandler, typeEvent] = normalizeParams(originalTypeEvent, handler, delegationFn)
const events = getEvent(element) const events = getEvent(element)
const handlers = events[typeEvent] || (events[typeEvent] = {}) const handlers = events[typeEvent] || (events[typeEvent] = {})
const previousFn = findHandler(handlers, originalHandler, delegation ? handler : null) const previousFn = findHandler(handlers, originalHandler, delegation ? handler : null)
if (previousFn) { if (previousFn) {
@ -158,7 +193,7 @@ function addHandler(element, originalTypeEvent, handler, delegationFn, oneOff) {
} }
const uid = getUidEvent(originalHandler, originalTypeEvent.replace(namespaceRegex, '')) const uid = getUidEvent(originalHandler, originalTypeEvent.replace(namespaceRegex, ''))
const fn = !delegation ? bootstrapHandler(element, handler) : bootstrapDelegationHandler(element, handler, delegationFn) const fn = delegation ? bootstrapDelegationHandler(element, handler, delegationFn) : bootstrapHandler(element, handler)
fn.delegationSelector = delegation ? handler : null fn.delegationSelector = delegation ? handler : null
fn.originalHandler = originalHandler fn.originalHandler = originalHandler
@ -184,7 +219,7 @@ function removeNamespacedHandlers(element, events, typeEvent, namespace) {
const storeElementEvent = events[typeEvent] || {} const storeElementEvent = events[typeEvent] || {}
Object.keys(storeElementEvent) Object.keys(storeElementEvent)
.forEach((handlerKey) => { .forEach(handlerKey => {
if (handlerKey.indexOf(namespace) > -1) { if (handlerKey.indexOf(namespace) > -1) {
const event = storeElementEvent[handlerKey] const event = storeElementEvent[handlerKey]
@ -224,14 +259,14 @@ const EventHandler = {
if (isNamespace) { if (isNamespace) {
Object.keys(events) Object.keys(events)
.forEach((elementEvent) => { .forEach(elementEvent => {
removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.substr(1)) removeNamespacedHandlers(element, events, elementEvent, originalTypeEvent.substr(1))
}) })
} }
const storeElementEvent = events[typeEvent] || {} const storeElementEvent = events[typeEvent] || {}
Object.keys(storeElementEvent) Object.keys(storeElementEvent)
.forEach((keyHandlers) => { .forEach(keyHandlers => {
const handlerKey = keyHandlers.replace(stripUidRegex, '') const handlerKey = keyHandlers.replace(stripUidRegex, '')
if (!inNamespace || originalTypeEvent.indexOf(handlerKey) > -1) { if (!inNamespace || originalTypeEvent.indexOf(handlerKey) > -1) {
@ -247,9 +282,9 @@ const EventHandler = {
return null return null
} }
const typeEvent = event.replace(stripNameRegex, '') const typeEvent = event.replace(stripNameRegex, '')
const inNamespace = event !== typeEvent const inNamespace = event !== typeEvent
const isNative = nativeEvents.indexOf(typeEvent) > -1 const isNative = nativeEvents.indexOf(typeEvent) > -1
let jQueryEvent let jQueryEvent
let bubbles = true let bubbles = true
@ -279,7 +314,7 @@ const EventHandler = {
// merge custom informations in our event // merge custom informations in our event
if (typeof args !== 'undefined') { if (typeof args !== 'undefined') {
Object.keys(args) Object.keys(args)
.forEach((key) => { .forEach(key => {
Object.defineProperty(evt, key, { Object.defineProperty(evt, key, {
get() { get() {
return args[key] return args[key]

View File

@ -26,7 +26,7 @@ function normalizeData(val) {
} }
function normalizeDataKey(key) { function normalizeDataKey(key) {
return key.replace(/[A-Z]/g, (chr) => chr.toLowerCase()) return key.replace(/[A-Z]/g, chr => chr.toLowerCase())
} }
const Manipulator = { const Manipulator = {
@ -47,7 +47,7 @@ const Manipulator = {
...element.dataset ...element.dataset
} }
Object.keys(attributes).forEach((key) => { Object.keys(attributes).forEach(key => {
attributes[key] = normalizeData(attributes[key]) attributes[key] = normalizeData(attributes[key])
}) })

View File

@ -34,7 +34,7 @@ const Polyfill = (() => {
try { try {
element.querySelectorAll(':scope *') element.querySelectorAll(':scope *')
} catch (e) { } catch (error) {
return false return false
} }

View File

@ -16,8 +16,7 @@ import {
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
const findFn = Polyfill.find const { find: findFn, findOne } = Polyfill
const findOne = Polyfill.findOne
const NODE_TEXT = 3 const NODE_TEXT = 3
const SelectorEngine = { const SelectorEngine = {
@ -48,7 +47,7 @@ const SelectorEngine = {
const children = makeArray(element.children) const children = makeArray(element.children)
return children.filter((child) => this.matches(child, selector)) return children.filter(child => this.matches(child, selector))
}, },
parents(element, selector) { parents(element, selector) {

View File

@ -25,74 +25,74 @@ import SelectorEngine from './dom/selectorEngine'
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
const NAME = 'dropdown' const NAME = 'dropdown'
const VERSION = '4.3.1' const VERSION = '4.3.1'
const DATA_KEY = 'bs.dropdown' const DATA_KEY = 'bs.dropdown'
const EVENT_KEY = `.${DATA_KEY}` const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api' const DATA_API_KEY = '.data-api'
const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key
const SPACE_KEYCODE = 32 // KeyboardEvent.which value for space key const SPACE_KEYCODE = 32 // KeyboardEvent.which value for space key
const TAB_KEYCODE = 9 // KeyboardEvent.which value for tab key const TAB_KEYCODE = 9 // KeyboardEvent.which value for tab key
const ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key const ARROW_UP_KEYCODE = 38 // KeyboardEvent.which value for up arrow key
const ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key const ARROW_DOWN_KEYCODE = 40 // KeyboardEvent.which value for down arrow key
const RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse) const RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse)
const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}`) const REGEXP_KEYDOWN = new RegExp(`${ARROW_UP_KEYCODE}|${ARROW_DOWN_KEYCODE}|${ESCAPE_KEYCODE}`)
const Event = { const Event = {
HIDE : `hide${EVENT_KEY}`, HIDE: `hide${EVENT_KEY}`,
HIDDEN : `hidden${EVENT_KEY}`, HIDDEN: `hidden${EVENT_KEY}`,
SHOW : `show${EVENT_KEY}`, SHOW: `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}`, SHOWN: `shown${EVENT_KEY}`,
CLICK : `click${EVENT_KEY}`, CLICK: `click${EVENT_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`, CLICK_DATA_API: `click${EVENT_KEY}${DATA_API_KEY}`,
KEYDOWN_DATA_API : `keydown${EVENT_KEY}${DATA_API_KEY}`, KEYDOWN_DATA_API: `keydown${EVENT_KEY}${DATA_API_KEY}`,
KEYUP_DATA_API : `keyup${EVENT_KEY}${DATA_API_KEY}` KEYUP_DATA_API: `keyup${EVENT_KEY}${DATA_API_KEY}`
} }
const ClassName = { const ClassName = {
DISABLED : 'disabled', DISABLED: 'disabled',
SHOW : 'show', SHOW: 'show',
DROPUP : 'dropup', DROPUP: 'dropup',
DROPRIGHT : 'dropright', DROPRIGHT: 'dropright',
DROPLEFT : 'dropleft', DROPLEFT: 'dropleft',
MENURIGHT : 'dropdown-menu-right', MENURIGHT: 'dropdown-menu-right',
MENULEFT : 'dropdown-menu-left', MENULEFT: 'dropdown-menu-left',
POSITION_STATIC : 'position-static' POSITION_STATIC: 'position-static'
} }
const Selector = { const Selector = {
DATA_TOGGLE : '[data-toggle="dropdown"]', DATA_TOGGLE: '[data-toggle="dropdown"]',
FORM_CHILD : '.dropdown form', FORM_CHILD: '.dropdown form',
MENU : '.dropdown-menu', MENU: '.dropdown-menu',
NAVBAR_NAV : '.navbar-nav', NAVBAR_NAV: '.navbar-nav',
VISIBLE_ITEMS : '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)' VISIBLE_ITEMS: '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'
} }
const AttachmentMap = { const AttachmentMap = {
TOP : 'top-start', TOP: 'top-start',
TOPEND : 'top-end', TOPEND: 'top-end',
BOTTOM : 'bottom-start', BOTTOM: 'bottom-start',
BOTTOMEND : 'bottom-end', BOTTOMEND: 'bottom-end',
RIGHT : 'right-start', RIGHT: 'right-start',
RIGHTEND : 'right-end', RIGHTEND: 'right-end',
LEFT : 'left-start', LEFT: 'left-start',
LEFTEND : 'left-end' LEFTEND: 'left-end'
} }
const Default = { const Default = {
offset : 0, offset: 0,
flip : true, flip: true,
boundary : 'scrollParent', boundary: 'scrollParent',
reference : 'toggle', reference: 'toggle',
display : 'dynamic' display: 'dynamic'
} }
const DefaultType = { const DefaultType = {
offset : '(number|string|function)', offset: '(number|string|function)',
flip : 'boolean', flip: 'boolean',
boundary : '(string|element)', boundary: '(string|element)',
reference : '(string|element)', reference: '(string|element)',
display : 'string' display: 'string'
} }
/** /**
@ -103,10 +103,10 @@ const DefaultType = {
class Dropdown { class Dropdown {
constructor(element, config) { constructor(element, config) {
this._element = element this._element = element
this._popper = null this._popper = null
this._config = this._getConfig(config) this._config = this._getConfig(config)
this._menu = this._getMenuElement() this._menu = this._getMenuElement()
this._inNavbar = this._detectNavbar() this._inNavbar = this._detectNavbar()
this._addEventListeners() this._addEventListeners()
@ -134,7 +134,7 @@ class Dropdown {
return return
} }
const parent = Dropdown._getParentFromElement(this._element) const parent = Dropdown._getParentFromElement(this._element)
const isActive = this._menu.classList.contains(ClassName.SHOW) const isActive = this._menu.classList.contains(ClassName.SHOW)
Dropdown._clearMenus() Dropdown._clearMenus()
@ -181,6 +181,7 @@ class Dropdown {
if (this._config.boundary !== 'scrollParent') { if (this._config.boundary !== 'scrollParent') {
parent.classList.add(ClassName.POSITION_STATIC) parent.classList.add(ClassName.POSITION_STATIC)
} }
this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig()) this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig())
} }
@ -191,7 +192,7 @@ class Dropdown {
if ('ontouchstart' in document.documentElement && if ('ontouchstart' in document.documentElement &&
!makeArray(SelectorEngine.closest(parent, Selector.NAVBAR_NAV)).length) { !makeArray(SelectorEngine.closest(parent, Selector.NAVBAR_NAV)).length) {
makeArray(document.body.children) makeArray(document.body.children)
.forEach((elem) => EventHandler.on(elem, 'mouseover', null, noop())) .forEach(elem => EventHandler.on(elem, 'mouseover', null, noop()))
} }
this._element.focus() this._element.focus()
@ -265,7 +266,7 @@ class Dropdown {
// Private // Private
_addEventListeners() { _addEventListeners() {
EventHandler.on(this._element, Event.CLICK, (event) => { EventHandler.on(this._element, Event.CLICK, event => {
event.preventDefault() event.preventDefault()
event.stopPropagation() event.stopPropagation()
this.toggle() this.toggle()
@ -296,12 +297,13 @@ class Dropdown {
this._menu = SelectorEngine.findOne(Selector.MENU, parent) this._menu = SelectorEngine.findOne(Selector.MENU, parent)
} }
} }
return this._menu return this._menu
} }
_getPlacement() { _getPlacement() {
const parentDropdown = this._element.parentNode const parentDropdown = this._element.parentNode
let placement = AttachmentMap.BOTTOM let placement = AttachmentMap.BOTTOM
// Handle dropup // Handle dropup
if (parentDropdown.classList.contains(ClassName.DROPUP)) { if (parentDropdown.classList.contains(ClassName.DROPUP)) {
@ -316,6 +318,7 @@ class Dropdown {
} else if (this._menu.classList.contains(ClassName.MENURIGHT)) { } else if (this._menu.classList.contains(ClassName.MENURIGHT)) {
placement = AttachmentMap.BOTTOMEND placement = AttachmentMap.BOTTOMEND
} }
return placement return placement
} }
@ -327,7 +330,7 @@ class Dropdown {
const offset = {} const offset = {}
if (typeof this._config.offset === 'function') { if (typeof this._config.offset === 'function') {
offset.fn = (data) => { offset.fn = data => {
data.offsets = { data.offsets = {
...data.offsets, ...data.offsets,
...this._config.offset(data.offsets, this._element) || {} ...this._config.offset(data.offsets, this._element) || {}
@ -378,8 +381,9 @@ class Dropdown {
if (typeof config === 'string') { if (typeof config === 'string') {
if (typeof data[config] === 'undefined') { if (typeof data[config] === 'undefined') {
throw new Error(`No method named "${config}"`) throw new TypeError(`No method named "${config}"`)
} }
data[config]() data[config]()
} }
} }
@ -398,8 +402,8 @@ class Dropdown {
const toggles = makeArray(SelectorEngine.find(Selector.DATA_TOGGLE)) const toggles = makeArray(SelectorEngine.find(Selector.DATA_TOGGLE))
for (let i = 0, len = toggles.length; i < len; i++) { for (let i = 0, len = toggles.length; i < len; i++) {
const parent = Dropdown._getParentFromElement(toggles[i]) const parent = Dropdown._getParentFromElement(toggles[i])
const context = Data.getData(toggles[i], DATA_KEY) const context = Data.getData(toggles[i], DATA_KEY)
const relatedTarget = { const relatedTarget = {
relatedTarget: toggles[i] relatedTarget: toggles[i]
} }
@ -433,7 +437,7 @@ class Dropdown {
// empty mouseover listeners we added for iOS support // empty mouseover listeners we added for iOS support
if ('ontouchstart' in document.documentElement) { if ('ontouchstart' in document.documentElement) {
makeArray(document.body.children) makeArray(document.body.children)
.forEach((elem) => EventHandler.off(elem, 'mouseover', null, noop())) .forEach(elem => EventHandler.off(elem, 'mouseover', null, noop()))
} }
toggles[i].setAttribute('aria-expanded', 'false') toggles[i].setAttribute('aria-expanded', 'false')
@ -463,10 +467,11 @@ class Dropdown {
// - If key is other than escape // - If key is other than escape
// - If key is not up or down => not a dropdown command // - If key is not up or down => not a dropdown command
// - If trigger inside the menu => not a dropdown command // - If trigger inside the menu => not a dropdown command
if (/input|textarea/i.test(event.target.tagName) if (/input|textarea/i.test(event.target.tagName) ?
? event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE && event.which === SPACE_KEYCODE || event.which !== ESCAPE_KEYCODE &&
(event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE || (event.which !== ARROW_DOWN_KEYCODE && event.which !== ARROW_UP_KEYCODE ||
SelectorEngine.closest(event.target, Selector.MENU)) : !REGEXP_KEYDOWN.test(event.which)) { SelectorEngine.closest(event.target, Selector.MENU)) :
!REGEXP_KEYDOWN.test(event.which)) {
return return
} }
@ -477,7 +482,7 @@ class Dropdown {
return return
} }
const parent = Dropdown._getParentFromElement(this) const parent = Dropdown._getParentFromElement(this)
const isActive = parent.classList.contains(ClassName.SHOW) const isActive = parent.classList.contains(ClassName.SHOW)
if (!isActive || isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) { if (!isActive || isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE)) {
@ -533,7 +538,7 @@ EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (
Dropdown._dropdownInterface(this, 'toggle') Dropdown._dropdownInterface(this, 'toggle')
}) })
EventHandler EventHandler
.on(document, Event.CLICK_DATA_API, Selector.FORM_CHILD, (e) => e.stopPropagation()) .on(document, Event.CLICK_DATA_API, Selector.FORM_CHILD, e => e.stopPropagation())
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
@ -544,9 +549,9 @@ EventHandler
if (typeof $ !== 'undefined') { if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Dropdown._jQueryInterface $.fn[NAME] = Dropdown._jQueryInterface
$.fn[NAME].Constructor = Dropdown $.fn[NAME].Constructor = Dropdown
$.fn[NAME].noConflict = () => { $.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT $.fn[NAME] = JQUERY_NO_CONFLICT
return Dropdown._jQueryInterface return Dropdown._jQueryInterface
} }

View File

@ -27,57 +27,57 @@ import SelectorEngine from './dom/selectorEngine'
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
const NAME = 'modal' const NAME = 'modal'
const VERSION = '4.3.1' const VERSION = '4.3.1'
const DATA_KEY = 'bs.modal' const DATA_KEY = 'bs.modal'
const EVENT_KEY = `.${DATA_KEY}` const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api' const DATA_API_KEY = '.data-api'
const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key
const Default = { const Default = {
backdrop : true, backdrop: true,
keyboard : true, keyboard: true,
focus : true, focus: true,
show : true show: true
} }
const DefaultType = { const DefaultType = {
backdrop : '(boolean|string)', backdrop: '(boolean|string)',
keyboard : 'boolean', keyboard: 'boolean',
focus : 'boolean', focus: 'boolean',
show : 'boolean' show: 'boolean'
} }
const Event = { const Event = {
HIDE : `hide${EVENT_KEY}`, HIDE: `hide${EVENT_KEY}`,
HIDDEN : `hidden${EVENT_KEY}`, HIDDEN: `hidden${EVENT_KEY}`,
SHOW : `show${EVENT_KEY}`, SHOW: `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}`, SHOWN: `shown${EVENT_KEY}`,
FOCUSIN : `focusin${EVENT_KEY}`, FOCUSIN: `focusin${EVENT_KEY}`,
RESIZE : `resize${EVENT_KEY}`, RESIZE: `resize${EVENT_KEY}`,
CLICK_DISMISS : `click.dismiss${EVENT_KEY}`, CLICK_DISMISS: `click.dismiss${EVENT_KEY}`,
KEYDOWN_DISMISS : `keydown.dismiss${EVENT_KEY}`, KEYDOWN_DISMISS: `keydown.dismiss${EVENT_KEY}`,
MOUSEUP_DISMISS : `mouseup.dismiss${EVENT_KEY}`, MOUSEUP_DISMISS: `mouseup.dismiss${EVENT_KEY}`,
MOUSEDOWN_DISMISS : `mousedown.dismiss${EVENT_KEY}`, MOUSEDOWN_DISMISS: `mousedown.dismiss${EVENT_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}` CLICK_DATA_API: `click${EVENT_KEY}${DATA_API_KEY}`
} }
const ClassName = { const ClassName = {
SCROLLABLE : 'modal-dialog-scrollable', SCROLLABLE: 'modal-dialog-scrollable',
SCROLLBAR_MEASURER : 'modal-scrollbar-measure', SCROLLBAR_MEASURER: 'modal-scrollbar-measure',
BACKDROP : 'modal-backdrop', BACKDROP: 'modal-backdrop',
OPEN : 'modal-open', OPEN: 'modal-open',
FADE : 'fade', FADE: 'fade',
SHOW : 'show' SHOW: 'show'
} }
const Selector = { const Selector = {
DIALOG : '.modal-dialog', DIALOG: '.modal-dialog',
MODAL_BODY : '.modal-body', MODAL_BODY: '.modal-body',
DATA_TOGGLE : '[data-toggle="modal"]', DATA_TOGGLE: '[data-toggle="modal"]',
DATA_DISMISS : '[data-dismiss="modal"]', DATA_DISMISS: '[data-dismiss="modal"]',
FIXED_CONTENT : '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top', FIXED_CONTENT: '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top',
STICKY_CONTENT : '.sticky-top' STICKY_CONTENT: '.sticky-top'
} }
/** /**
@ -88,15 +88,15 @@ const Selector = {
class Modal { class Modal {
constructor(element, config) { constructor(element, config) {
this._config = this._getConfig(config) this._config = this._getConfig(config)
this._element = element this._element = element
this._dialog = SelectorEngine.findOne(Selector.DIALOG, element) this._dialog = SelectorEngine.findOne(Selector.DIALOG, element)
this._backdrop = null this._backdrop = null
this._isShown = false this._isShown = false
this._isBodyOverflowing = false this._isBodyOverflowing = false
this._ignoreBackdropClick = false this._ignoreBackdropClick = false
this._isTransitioning = false this._isTransitioning = false
this._scrollbarWidth = 0 this._scrollbarWidth = 0
Data.setData(element, DATA_KEY, this) Data.setData(element, DATA_KEY, this)
} }
@ -146,11 +146,11 @@ class Modal {
EventHandler.on(this._element, EventHandler.on(this._element,
Event.CLICK_DISMISS, Event.CLICK_DISMISS,
Selector.DATA_DISMISS, Selector.DATA_DISMISS,
(event) => this.hide(event) event => this.hide(event)
) )
EventHandler.on(this._dialog, Event.MOUSEDOWN_DISMISS, () => { EventHandler.on(this._dialog, Event.MOUSEDOWN_DISMISS, () => {
EventHandler.one(this._element, Event.MOUSEUP_DISMISS, (event) => { EventHandler.one(this._element, Event.MOUSEUP_DISMISS, event => {
if (event.target === this._element) { if (event.target === this._element) {
this._ignoreBackdropClick = true this._ignoreBackdropClick = true
} }
@ -192,11 +192,10 @@ class Modal {
EventHandler.off(this._element, Event.CLICK_DISMISS) EventHandler.off(this._element, Event.CLICK_DISMISS)
EventHandler.off(this._dialog, Event.MOUSEDOWN_DISMISS) EventHandler.off(this._dialog, Event.MOUSEDOWN_DISMISS)
if (transition) { if (transition) {
const transitionDuration = getTransitionDurationFromElement(this._element) const transitionDuration = getTransitionDurationFromElement(this._element)
EventHandler.one(this._element, TRANSITION_END, (event) => this._hideModal(event)) EventHandler.one(this._element, TRANSITION_END, event => this._hideModal(event))
emulateTransitionEnd(this._element, transitionDuration) emulateTransitionEnd(this._element, transitionDuration)
} else { } else {
this._hideModal() this._hideModal()
@ -205,7 +204,7 @@ class Modal {
dispose() { dispose() {
[window, this._element, this._dialog] [window, this._element, this._dialog]
.forEach((htmlElement) => EventHandler.off(htmlElement, EVENT_KEY)) .forEach(htmlElement => EventHandler.off(htmlElement, EVENT_KEY))
/** /**
* `document` has 2 events `Event.FOCUSIN` and `Event.CLICK_DATA_API` * `document` has 2 events `Event.FOCUSIN` and `Event.CLICK_DATA_API`
@ -216,15 +215,15 @@ class Modal {
Data.removeData(this._element, DATA_KEY) Data.removeData(this._element, DATA_KEY)
this._config = null this._config = null
this._element = null this._element = null
this._dialog = null this._dialog = null
this._backdrop = null this._backdrop = null
this._isShown = null this._isShown = null
this._isBodyOverflowing = null this._isBodyOverflowing = null
this._ignoreBackdropClick = null this._ignoreBackdropClick = null
this._isTransitioning = null this._isTransitioning = null
this._scrollbarWidth = null this._scrollbarWidth = null
} }
handleUpdate() { handleUpdate() {
@ -275,6 +274,7 @@ class Modal {
if (this._config.focus) { if (this._config.focus) {
this._element.focus() this._element.focus()
} }
this._isTransitioning = false this._isTransitioning = false
EventHandler.trigger(this._element, Event.SHOWN, { EventHandler.trigger(this._element, Event.SHOWN, {
relatedTarget relatedTarget
@ -282,7 +282,7 @@ class Modal {
} }
if (transition) { if (transition) {
const transitionDuration = getTransitionDurationFromElement(this._dialog) const transitionDuration = getTransitionDurationFromElement(this._dialog)
EventHandler.one(this._dialog, TRANSITION_END, transitionComplete) EventHandler.one(this._dialog, TRANSITION_END, transitionComplete)
emulateTransitionEnd(this._dialog, transitionDuration) emulateTransitionEnd(this._dialog, transitionDuration)
@ -293,7 +293,7 @@ class Modal {
_enforceFocus() { _enforceFocus() {
EventHandler.off(document, Event.FOCUSIN) // guard against infinite focus loop EventHandler.off(document, Event.FOCUSIN) // guard against infinite focus loop
EventHandler.on(document, Event.FOCUSIN, (event) => { EventHandler.on(document, Event.FOCUSIN, event => {
if (document !== event.target && if (document !== event.target &&
this._element !== event.target && this._element !== event.target &&
!this._element.contains(event.target)) { !this._element.contains(event.target)) {
@ -304,7 +304,7 @@ class Modal {
_setEscapeEvent() { _setEscapeEvent() {
if (this._isShown && this._config.keyboard) { if (this._isShown && this._config.keyboard) {
EventHandler.on(this._element, Event.KEYDOWN_DISMISS, (event) => { EventHandler.on(this._element, Event.KEYDOWN_DISMISS, event => {
if (event.which === ESCAPE_KEYCODE) { if (event.which === ESCAPE_KEYCODE) {
event.preventDefault() event.preventDefault()
this.hide() this.hide()
@ -317,7 +317,7 @@ class Modal {
_setResizeEvent() { _setResizeEvent() {
if (this._isShown) { if (this._isShown) {
EventHandler.on(window, Event.RESIZE, (event) => this.handleUpdate(event)) EventHandler.on(window, Event.RESIZE, event => this.handleUpdate(event))
} else { } else {
EventHandler.off(window, Event.RESIZE) EventHandler.off(window, Event.RESIZE)
} }
@ -344,9 +344,9 @@ class Modal {
} }
_showBackdrop(callback) { _showBackdrop(callback) {
const animate = this._element.classList.contains(ClassName.FADE) const animate = this._element.classList.contains(ClassName.FADE) ?
? ClassName.FADE ClassName.FADE :
: '' ''
if (this._isShown && this._config.backdrop) { if (this._isShown && this._config.backdrop) {
this._backdrop = document.createElement('div') this._backdrop = document.createElement('div')
@ -358,14 +358,16 @@ class Modal {
document.body.appendChild(this._backdrop) document.body.appendChild(this._backdrop)
EventHandler.on(this._element, Event.CLICK_DISMISS, (event) => { EventHandler.on(this._element, Event.CLICK_DISMISS, event => {
if (this._ignoreBackdropClick) { if (this._ignoreBackdropClick) {
this._ignoreBackdropClick = false this._ignoreBackdropClick = false
return return
} }
if (event.target !== event.currentTarget) { if (event.target !== event.currentTarget) {
return return
} }
if (this._config.backdrop === 'static') { if (this._config.backdrop === 'static') {
this._element.focus() this._element.focus()
} else { } else {
@ -450,7 +452,7 @@ class Modal {
// Adjust fixed content padding // Adjust fixed content padding
makeArray(SelectorEngine.find(Selector.FIXED_CONTENT)) makeArray(SelectorEngine.find(Selector.FIXED_CONTENT))
.forEach((element) => { .forEach(element => {
const actualPadding = element.style.paddingRight const actualPadding = element.style.paddingRight
const calculatedPadding = window.getComputedStyle(element)['padding-right'] const calculatedPadding = window.getComputedStyle(element)['padding-right']
Manipulator.setDataAttribute(element, 'padding-right', actualPadding) Manipulator.setDataAttribute(element, 'padding-right', actualPadding)
@ -459,7 +461,7 @@ class Modal {
// Adjust sticky content margin // Adjust sticky content margin
makeArray(SelectorEngine.find(Selector.STICKY_CONTENT)) makeArray(SelectorEngine.find(Selector.STICKY_CONTENT))
.forEach((element) => { .forEach(element => {
const actualMargin = element.style.marginRight const actualMargin = element.style.marginRight
const calculatedMargin = window.getComputedStyle(element)['margin-right'] const calculatedMargin = window.getComputedStyle(element)['margin-right']
Manipulator.setDataAttribute(element, 'margin-right', actualMargin) Manipulator.setDataAttribute(element, 'margin-right', actualMargin)
@ -480,7 +482,7 @@ class Modal {
_resetScrollbar() { _resetScrollbar() {
// Restore fixed content padding // Restore fixed content padding
makeArray(SelectorEngine.find(Selector.FIXED_CONTENT)) makeArray(SelectorEngine.find(Selector.FIXED_CONTENT))
.forEach((element) => { .forEach(element => {
const padding = Manipulator.getDataAttribute(element, 'padding-right') const padding = Manipulator.getDataAttribute(element, 'padding-right')
if (typeof padding !== 'undefined') { if (typeof padding !== 'undefined') {
Manipulator.removeDataAttribute(element, 'padding-right') Manipulator.removeDataAttribute(element, 'padding-right')
@ -490,7 +492,7 @@ class Modal {
// Restore sticky content and navbar-toggler margin // Restore sticky content and navbar-toggler margin
makeArray(SelectorEngine.find(`${Selector.STICKY_CONTENT}`)) makeArray(SelectorEngine.find(`${Selector.STICKY_CONTENT}`))
.forEach((element) => { .forEach(element => {
const margin = Manipulator.getDataAttribute(element, 'margin-right') const margin = Manipulator.getDataAttribute(element, 'margin-right')
if (typeof margin !== 'undefined') { if (typeof margin !== 'undefined') {
Manipulator.removeDataAttribute(element, 'margin-right') Manipulator.removeDataAttribute(element, 'margin-right')
@ -500,11 +502,11 @@ class Modal {
// Restore body padding // Restore body padding
const padding = Manipulator.getDataAttribute(document.body, 'padding-right') const padding = Manipulator.getDataAttribute(document.body, 'padding-right')
if (typeof padding !== 'undefined') { if (typeof padding === 'undefined') {
document.body.style.paddingRight = ''
} else {
Manipulator.removeDataAttribute(document.body, 'padding-right') Manipulator.removeDataAttribute(document.body, 'padding-right')
document.body.style.paddingRight = padding document.body.style.paddingRight = padding
} else {
document.body.style.paddingRight = ''
} }
} }
@ -536,6 +538,7 @@ class Modal {
if (typeof data[config] === 'undefined') { if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`) throw new TypeError(`No method named "${config}"`)
} }
data[config](relatedTarget) data[config](relatedTarget)
} else if (_config.show) { } else if (_config.show) {
data.show(relatedTarget) data.show(relatedTarget)
@ -562,8 +565,9 @@ EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (
target = SelectorEngine.findOne(selector) target = SelectorEngine.findOne(selector)
} }
const config = Data.getData(target, DATA_KEY) const config = Data.getData(target, DATA_KEY) ?
? 'toggle' : { 'toggle' :
{
...Manipulator.getDataAttributes(target), ...Manipulator.getDataAttributes(target),
...Manipulator.getDataAttributes(this) ...Manipulator.getDataAttributes(this)
} }
@ -572,7 +576,7 @@ EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (
event.preventDefault() event.preventDefault()
} }
EventHandler.one(target, Event.SHOW, (showEvent) => { EventHandler.one(target, Event.SHOW, showEvent => {
if (showEvent.defaultPrevented) { if (showEvent.defaultPrevented) {
// only register focus restorer if modal will actually get shown // only register focus restorer if modal will actually get shown
return return
@ -601,9 +605,9 @@ EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (
if (typeof $ !== 'undefined') { if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Modal._jQueryInterface $.fn[NAME] = Modal._jQueryInterface
$.fn[NAME].Constructor = Modal $.fn[NAME].Constructor = Modal
$.fn[NAME].noConflict = () => { $.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT $.fn[NAME] = JQUERY_NO_CONFLICT
return Modal._jQueryInterface return Modal._jQueryInterface
} }

View File

@ -18,19 +18,19 @@ import Tooltip from './tooltip'
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
const NAME = 'popover' const NAME = 'popover'
const VERSION = '4.3.1' const VERSION = '4.3.1'
const DATA_KEY = 'bs.popover' const DATA_KEY = 'bs.popover'
const EVENT_KEY = `.${DATA_KEY}` const EVENT_KEY = `.${DATA_KEY}`
const CLASS_PREFIX = 'bs-popover' const CLASS_PREFIX = 'bs-popover'
const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g') const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g')
const Default = { const Default = {
...Tooltip.Default, ...Tooltip.Default,
placement : 'right', placement: 'right',
trigger : 'click', trigger: 'click',
content : '', content: '',
template : '<div class="popover" role="tooltip">' + template: '<div class="popover" role="tooltip">' +
'<div class="popover-arrow"></div>' + '<div class="popover-arrow"></div>' +
'<h3 class="popover-header"></h3>' + '<h3 class="popover-header"></h3>' +
'<div class="popover-body"></div></div>' '<div class="popover-body"></div></div>'
@ -38,30 +38,30 @@ const Default = {
const DefaultType = { const DefaultType = {
...Tooltip.DefaultType, ...Tooltip.DefaultType,
content : '(string|element|function)' content: '(string|element|function)'
} }
const ClassName = { const ClassName = {
FADE : 'fade', FADE: 'fade',
SHOW : 'show' SHOW: 'show'
} }
const Selector = { const Selector = {
TITLE : '.popover-header', TITLE: '.popover-header',
CONTENT : '.popover-body' CONTENT: '.popover-body'
} }
const Event = { const Event = {
HIDE : `hide${EVENT_KEY}`, HIDE: `hide${EVENT_KEY}`,
HIDDEN : `hidden${EVENT_KEY}`, HIDDEN: `hidden${EVENT_KEY}`,
SHOW : `show${EVENT_KEY}`, SHOW: `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}`, SHOWN: `shown${EVENT_KEY}`,
INSERTED : `inserted${EVENT_KEY}`, INSERTED: `inserted${EVENT_KEY}`,
CLICK : `click${EVENT_KEY}`, CLICK: `click${EVENT_KEY}`,
FOCUSIN : `focusin${EVENT_KEY}`, FOCUSIN: `focusin${EVENT_KEY}`,
FOCUSOUT : `focusout${EVENT_KEY}`, FOCUSOUT: `focusout${EVENT_KEY}`,
MOUSEENTER : `mouseenter${EVENT_KEY}`, MOUSEENTER: `mouseenter${EVENT_KEY}`,
MOUSELEAVE : `mouseleave${EVENT_KEY}` MOUSELEAVE: `mouseleave${EVENT_KEY}`
} }
/** /**
@ -120,6 +120,7 @@ class Popover extends Tooltip {
if (typeof content === 'function') { if (typeof content === 'function') {
content = content.call(this.element) content = content.call(this.element)
} }
this.setElementContent(SelectorEngine.findOne(Selector.CONTENT, tip), content) this.setElementContent(SelectorEngine.findOne(Selector.CONTENT, tip), content)
tip.classList.remove(ClassName.FADE) tip.classList.remove(ClassName.FADE)
@ -138,8 +139,8 @@ class Popover extends Tooltip {
const tabClass = tip.getAttribute('class').match(BSCLS_PREFIX_REGEX) const tabClass = tip.getAttribute('class').match(BSCLS_PREFIX_REGEX)
if (tabClass !== null && tabClass.length > 0) { if (tabClass !== null && tabClass.length > 0) {
tabClass.map((token) => token.trim()) tabClass.map(token => token.trim())
.forEach((tClass) => tip.classList.remove(tClass)) .forEach(tClass => tip.classList.remove(tClass))
} }
} }
@ -147,7 +148,7 @@ class Popover extends Tooltip {
static _jQueryInterface(config) { static _jQueryInterface(config) {
return this.each(function () { return this.each(function () {
let data = Data.getData(this, DATA_KEY) let data = Data.getData(this, DATA_KEY)
const _config = typeof config === 'object' ? config : null const _config = typeof config === 'object' ? config : null
if (!data && /dispose|hide/.test(config)) { if (!data && /dispose|hide/.test(config)) {
@ -163,6 +164,7 @@ class Popover extends Tooltip {
if (typeof data[config] === 'undefined') { if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`) throw new TypeError(`No method named "${config}"`)
} }
data[config]() data[config]()
} }
}) })
@ -181,9 +183,9 @@ class Popover extends Tooltip {
if (typeof $ !== 'undefined') { if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Popover._jQueryInterface $.fn[NAME] = Popover._jQueryInterface
$.fn[NAME].Constructor = Popover $.fn[NAME].Constructor = Popover
$.fn[NAME].noConflict = () => { $.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT $.fn[NAME] = JQUERY_NO_CONFLICT
return Popover._jQueryInterface return Popover._jQueryInterface
} }

View File

@ -23,51 +23,51 @@ import SelectorEngine from './dom/selectorEngine'
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
const NAME = 'scrollspy' const NAME = 'scrollspy'
const VERSION = '4.3.1' const VERSION = '4.3.1'
const DATA_KEY = 'bs.scrollspy' const DATA_KEY = 'bs.scrollspy'
const EVENT_KEY = `.${DATA_KEY}` const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api' const DATA_API_KEY = '.data-api'
const Default = { const Default = {
offset : 10, offset: 10,
method : 'auto', method: 'auto',
target : '' target: ''
} }
const DefaultType = { const DefaultType = {
offset : 'number', offset: 'number',
method : 'string', method: 'string',
target : '(string|element)' target: '(string|element)'
} }
const Event = { const Event = {
ACTIVATE : `activate${EVENT_KEY}`, ACTIVATE: `activate${EVENT_KEY}`,
SCROLL : `scroll${EVENT_KEY}`, SCROLL: `scroll${EVENT_KEY}`,
LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}` LOAD_DATA_API: `load${EVENT_KEY}${DATA_API_KEY}`
} }
const ClassName = { const ClassName = {
DROPDOWN_ITEM : 'dropdown-item', DROPDOWN_ITEM: 'dropdown-item',
DROPDOWN_MENU : 'dropdown-menu', DROPDOWN_MENU: 'dropdown-menu',
ACTIVE : 'active' ACTIVE: 'active'
} }
const Selector = { const Selector = {
DATA_SPY : '[data-spy="scroll"]', DATA_SPY: '[data-spy="scroll"]',
ACTIVE : '.active', ACTIVE: '.active',
NAV_LIST_GROUP : '.nav, .list-group', NAV_LIST_GROUP: '.nav, .list-group',
NAV_LINKS : '.nav-link', NAV_LINKS: '.nav-link',
NAV_ITEMS : '.nav-item', NAV_ITEMS: '.nav-item',
LIST_ITEMS : '.list-group-item', LIST_ITEMS: '.list-group-item',
DROPDOWN : '.dropdown', DROPDOWN: '.dropdown',
DROPDOWN_ITEMS : '.dropdown-item', DROPDOWN_ITEMS: '.dropdown-item',
DROPDOWN_TOGGLE : '.dropdown-toggle' DROPDOWN_TOGGLE: '.dropdown-toggle'
} }
const OffsetMethod = { const OffsetMethod = {
OFFSET : 'offset', OFFSET: 'offset',
POSITION : 'position' POSITION: 'position'
} }
/** /**
@ -78,18 +78,18 @@ const OffsetMethod = {
class ScrollSpy { class ScrollSpy {
constructor(element, config) { constructor(element, config) {
this._element = element this._element = element
this._scrollElement = element.tagName === 'BODY' ? window : element this._scrollElement = element.tagName === 'BODY' ? window : element
this._config = this._getConfig(config) this._config = this._getConfig(config)
this._selector = `${this._config.target} ${Selector.NAV_LINKS},` + this._selector = `${this._config.target} ${Selector.NAV_LINKS},` +
`${this._config.target} ${Selector.LIST_ITEMS},` + `${this._config.target} ${Selector.LIST_ITEMS},` +
`${this._config.target} ${Selector.DROPDOWN_ITEMS}` `${this._config.target} ${Selector.DROPDOWN_ITEMS}`
this._offsets = [] this._offsets = []
this._targets = [] this._targets = []
this._activeTarget = null this._activeTarget = null
this._scrollHeight = 0 this._scrollHeight = 0
EventHandler.on(this._scrollElement, Event.SCROLL, (event) => this._process(event)) EventHandler.on(this._scrollElement, Event.SCROLL, event => this._process(event))
this.refresh() this.refresh()
this._process() this._process()
@ -110,14 +110,17 @@ class ScrollSpy {
// Public // Public
refresh() { refresh() {
const autoMethod = this._scrollElement === this._scrollElement.window const autoMethod = this._scrollElement === this._scrollElement.window ?
? OffsetMethod.OFFSET : OffsetMethod.POSITION OffsetMethod.OFFSET :
OffsetMethod.POSITION
const offsetMethod = this._config.method === 'auto' const offsetMethod = this._config.method === 'auto' ?
? autoMethod : this._config.method autoMethod :
this._config.method
const offsetBase = offsetMethod === OffsetMethod.POSITION const offsetBase = offsetMethod === OffsetMethod.POSITION ?
? this._getScrollTop() : 0 this._getScrollTop() :
0
this._offsets = [] this._offsets = []
this._targets = [] this._targets = []
@ -127,7 +130,7 @@ class ScrollSpy {
const targets = makeArray(SelectorEngine.find(this._selector)) const targets = makeArray(SelectorEngine.find(this._selector))
targets targets
.map((element) => { .map(element => {
let target let target
const targetSelector = getSelectorFromElement(element) const targetSelector = getSelectorFromElement(element)
@ -145,11 +148,12 @@ class ScrollSpy {
] ]
} }
} }
return null return null
}) })
.filter((item) => item) .filter(item => item)
.sort((a, b) => a[0] - b[0]) .sort((a, b) => a[0] - b[0])
.forEach((item) => { .forEach(item => {
this._offsets.push(item[0]) this._offsets.push(item[0])
this._targets.push(item[1]) this._targets.push(item[1])
}) })
@ -159,14 +163,14 @@ class ScrollSpy {
Data.removeData(this._element, DATA_KEY) Data.removeData(this._element, DATA_KEY)
EventHandler.off(this._scrollElement, EVENT_KEY) EventHandler.off(this._scrollElement, EVENT_KEY)
this._element = null this._element = null
this._scrollElement = null this._scrollElement = null
this._config = null this._config = null
this._selector = null this._selector = null
this._offsets = null this._offsets = null
this._targets = null this._targets = null
this._activeTarget = null this._activeTarget = null
this._scrollHeight = null this._scrollHeight = null
} }
// Private // Private
@ -178,11 +182,12 @@ class ScrollSpy {
} }
if (typeof config.target !== 'string') { if (typeof config.target !== 'string') {
let id = config.target.id let { id } = config.target
if (!id) { if (!id) {
id = getUID(NAME) id = getUID(NAME)
config.target.id = id config.target.id = id
} }
config.target = `#${id}` config.target = `#${id}`
} }
@ -192,8 +197,9 @@ class ScrollSpy {
} }
_getScrollTop() { _getScrollTop() {
return this._scrollElement === window return this._scrollElement === window ?
? this._scrollElement.pageYOffset : this._scrollElement.scrollTop this._scrollElement.pageYOffset :
this._scrollElement.scrollTop
} }
_getScrollHeight() { _getScrollHeight() {
@ -204,14 +210,15 @@ class ScrollSpy {
} }
_getOffsetHeight() { _getOffsetHeight() {
return this._scrollElement === window return this._scrollElement === window ?
? window.innerHeight : this._scrollElement.getBoundingClientRect().height window.innerHeight :
this._scrollElement.getBoundingClientRect().height
} }
_process() { _process() {
const scrollTop = this._getScrollTop() + this._config.offset const scrollTop = this._getScrollTop() + this._config.offset
const scrollHeight = this._getScrollHeight() const scrollHeight = this._getScrollHeight()
const maxScroll = this._config.offset + const maxScroll = this._config.offset +
scrollHeight - scrollHeight -
this._getOffsetHeight() this._getOffsetHeight()
@ -225,6 +232,7 @@ class ScrollSpy {
if (this._activeTarget !== target) { if (this._activeTarget !== target) {
this._activate(target) this._activate(target)
} }
return return
} }
@ -253,7 +261,7 @@ class ScrollSpy {
this._clear() this._clear()
const queries = this._selector.split(',') const queries = this._selector.split(',')
.map((selector) => `${selector}[data-target="${target}"],${selector}[href="${target}"]`) .map(selector => `${selector}[data-target="${target}"],${selector}[href="${target}"]`)
const link = SelectorEngine.findOne(queries.join(',')) const link = SelectorEngine.findOne(queries.join(','))
@ -269,17 +277,17 @@ class ScrollSpy {
SelectorEngine SelectorEngine
.parents(link, Selector.NAV_LIST_GROUP) .parents(link, Selector.NAV_LIST_GROUP)
.forEach((listGroup) => { .forEach(listGroup => {
// Set triggered links parents as active // Set triggered links parents as active
// With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor // With both <ul> and <nav> markup a parent is the previous sibling of any nav ancestor
SelectorEngine.prev(listGroup, `${Selector.NAV_LINKS}, ${Selector.LIST_ITEMS}`) SelectorEngine.prev(listGroup, `${Selector.NAV_LINKS}, ${Selector.LIST_ITEMS}`)
.forEach((item) => item.classList.add(ClassName.ACTIVE)) .forEach(item => item.classList.add(ClassName.ACTIVE))
// Handle special case when .nav-link is inside .nav-item // Handle special case when .nav-link is inside .nav-item
SelectorEngine.prev(listGroup, Selector.NAV_ITEMS) SelectorEngine.prev(listGroup, Selector.NAV_ITEMS)
.forEach((navItem) => { .forEach(navItem => {
SelectorEngine.children(navItem, Selector.NAV_LINKS) SelectorEngine.children(navItem, Selector.NAV_LINKS)
.forEach((item) => item.classList.add(ClassName.ACTIVE)) .forEach(item => item.classList.add(ClassName.ACTIVE))
}) })
}) })
} }
@ -291,8 +299,8 @@ class ScrollSpy {
_clear() { _clear() {
makeArray(SelectorEngine.find(this._selector)) makeArray(SelectorEngine.find(this._selector))
.filter((node) => node.classList.contains(ClassName.ACTIVE)) .filter(node => node.classList.contains(ClassName.ACTIVE))
.forEach((node) => node.classList.remove(ClassName.ACTIVE)) .forEach(node => node.classList.remove(ClassName.ACTIVE))
} }
// Static // Static
@ -310,6 +318,7 @@ class ScrollSpy {
if (typeof data[config] === 'undefined') { if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`) throw new TypeError(`No method named "${config}"`)
} }
data[config]() data[config]()
} }
}) })
@ -328,7 +337,7 @@ class ScrollSpy {
EventHandler.on(window, Event.LOAD_DATA_API, () => { EventHandler.on(window, Event.LOAD_DATA_API, () => {
makeArray(SelectorEngine.find(Selector.DATA_SPY)) makeArray(SelectorEngine.find(Selector.DATA_SPY))
.forEach((spy) => new ScrollSpy(spy, Manipulator.getDataAttributes(spy))) .forEach(spy => new ScrollSpy(spy, Manipulator.getDataAttributes(spy)))
}) })
/** /**
@ -339,9 +348,9 @@ EventHandler.on(window, Event.LOAD_DATA_API, () => {
if (typeof $ !== 'undefined') { if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = ScrollSpy._jQueryInterface $.fn[NAME] = ScrollSpy._jQueryInterface
$.fn[NAME].Constructor = ScrollSpy $.fn[NAME].Constructor = ScrollSpy
$.fn[NAME].noConflict = () => { $.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT $.fn[NAME] = JQUERY_NO_CONFLICT
return ScrollSpy._jQueryInterface return ScrollSpy._jQueryInterface
} }

View File

@ -24,36 +24,36 @@ import SelectorEngine from './dom/selectorEngine'
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
const NAME = 'tab' const NAME = 'tab'
const VERSION = '4.3.1' const VERSION = '4.3.1'
const DATA_KEY = 'bs.tab' const DATA_KEY = 'bs.tab'
const EVENT_KEY = `.${DATA_KEY}` const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api' const DATA_API_KEY = '.data-api'
const Event = { const Event = {
HIDE : `hide${EVENT_KEY}`, HIDE: `hide${EVENT_KEY}`,
HIDDEN : `hidden${EVENT_KEY}`, HIDDEN: `hidden${EVENT_KEY}`,
SHOW : `show${EVENT_KEY}`, SHOW: `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}`, SHOWN: `shown${EVENT_KEY}`,
CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}` CLICK_DATA_API: `click${EVENT_KEY}${DATA_API_KEY}`
} }
const ClassName = { const ClassName = {
DROPDOWN_MENU : 'dropdown-menu', DROPDOWN_MENU: 'dropdown-menu',
ACTIVE : 'active', ACTIVE: 'active',
DISABLED : 'disabled', DISABLED: 'disabled',
FADE : 'fade', FADE: 'fade',
SHOW : 'show' SHOW: 'show'
} }
const Selector = { const Selector = {
DROPDOWN : '.dropdown', DROPDOWN: '.dropdown',
NAV_LIST_GROUP : '.nav, .list-group', NAV_LIST_GROUP: '.nav, .list-group',
ACTIVE : '.active', ACTIVE: '.active',
ACTIVE_UL : ':scope > li > .active', ACTIVE_UL: ':scope > li > .active',
DATA_TOGGLE : '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]', DATA_TOGGLE: '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]',
DROPDOWN_TOGGLE : '.dropdown-toggle', DROPDOWN_TOGGLE: '.dropdown-toggle',
DROPDOWN_ACTIVE_CHILD : ':scope > .dropdown-menu .active' DROPDOWN_ACTIVE_CHILD: ':scope > .dropdown-menu .active'
} }
/** /**
@ -146,11 +146,11 @@ class Tab {
// Private // Private
_activate(element, container, callback) { _activate(element, container, callback) {
const activeElements = container && (container.nodeName === 'UL' || container.nodeName === 'OL') const activeElements = container && (container.nodeName === 'UL' || container.nodeName === 'OL') ?
? SelectorEngine.find(Selector.ACTIVE_UL, container) SelectorEngine.find(Selector.ACTIVE_UL, container) :
: SelectorEngine.children(container, Selector.ACTIVE) SelectorEngine.children(container, Selector.ACTIVE)
const active = activeElements[0] const active = activeElements[0]
const isTransitioning = callback && const isTransitioning = callback &&
(active && active.classList.contains(ClassName.FADE)) (active && active.classList.contains(ClassName.FADE))
@ -202,7 +202,7 @@ class Tab {
if (dropdownElement) { if (dropdownElement) {
makeArray(SelectorEngine.find(Selector.DROPDOWN_TOGGLE)) makeArray(SelectorEngine.find(Selector.DROPDOWN_TOGGLE))
.forEach((dropdown) => dropdown.classList.add(ClassName.ACTIVE)) .forEach(dropdown => dropdown.classList.add(ClassName.ACTIVE))
} }
element.setAttribute('aria-expanded', true) element.setAttribute('aria-expanded', true)
@ -223,6 +223,7 @@ class Tab {
if (typeof data[config] === 'undefined') { if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`) throw new TypeError(`No method named "${config}"`)
} }
data[config]() data[config]()
} }
}) })
@ -255,9 +256,9 @@ EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (
if (typeof $ !== 'undefined') { if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Tab._jQueryInterface $.fn[NAME] = Tab._jQueryInterface
$.fn[NAME].Constructor = Tab $.fn[NAME].Constructor = Tab
$.fn[NAME].noConflict = () => { $.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT $.fn[NAME] = JQUERY_NO_CONFLICT
return Tab._jQueryInterface return Tab._jQueryInterface
} }

View File

@ -22,40 +22,40 @@ import Manipulator from './dom/manipulator'
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
const NAME = 'toast' const NAME = 'toast'
const VERSION = '4.3.1' const VERSION = '4.3.1'
const DATA_KEY = 'bs.toast' const DATA_KEY = 'bs.toast'
const EVENT_KEY = `.${DATA_KEY}` const EVENT_KEY = `.${DATA_KEY}`
const Event = { const Event = {
CLICK_DISMISS : `click.dismiss${EVENT_KEY}`, CLICK_DISMISS: `click.dismiss${EVENT_KEY}`,
HIDE : `hide${EVENT_KEY}`, HIDE: `hide${EVENT_KEY}`,
HIDDEN : `hidden${EVENT_KEY}`, HIDDEN: `hidden${EVENT_KEY}`,
SHOW : `show${EVENT_KEY}`, SHOW: `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}` SHOWN: `shown${EVENT_KEY}`
} }
const ClassName = { const ClassName = {
FADE : 'fade', FADE: 'fade',
HIDE : 'hide', HIDE: 'hide',
SHOW : 'show', SHOW: 'show',
SHOWING : 'showing' SHOWING: 'showing'
} }
const DefaultType = { const DefaultType = {
animation : 'boolean', animation: 'boolean',
autohide : 'boolean', autohide: 'boolean',
delay : 'number' delay: 'number'
} }
const Default = { const Default = {
animation : true, animation: true,
autohide : true, autohide: true,
delay : 500 delay: 500
} }
const Selector = { const Selector = {
DATA_DISMISS : '[data-dismiss="toast"]' DATA_DISMISS: '[data-dismiss="toast"]'
} }
/** /**
@ -67,7 +67,7 @@ const Selector = {
class Toast { class Toast {
constructor(element, config) { constructor(element, config) {
this._element = element this._element = element
this._config = this._getConfig(config) this._config = this._getConfig(config)
this._timeout = null this._timeout = null
this._setListeners() this._setListeners()
Data.setData(element, DATA_KEY, this) Data.setData(element, DATA_KEY, this)
@ -147,7 +147,7 @@ class Toast {
Data.removeData(this._element, DATA_KEY) Data.removeData(this._element, DATA_KEY)
this._element = null this._element = null
this._config = null this._config = null
} }
// Private // Private
@ -198,8 +198,8 @@ class Toast {
static _jQueryInterface(config) { static _jQueryInterface(config) {
return this.each(function () { return this.each(function () {
let data = Data.getData(this, DATA_KEY) let data = Data.getData(this, DATA_KEY)
const _config = typeof config === 'object' && config const _config = typeof config === 'object' && config
if (!data) { if (!data) {
data = new Toast(this, _config) data = new Toast(this, _config)
@ -229,9 +229,9 @@ class Toast {
if (typeof $ !== 'undefined') { if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Toast._jQueryInterface $.fn[NAME] = Toast._jQueryInterface
$.fn[NAME].Constructor = Toast $.fn[NAME].Constructor = Toast
$.fn[NAME].noConflict = () => { $.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT $.fn[NAME] = JQUERY_NO_CONFLICT
return Toast._jQueryInterface return Toast._jQueryInterface
} }

View File

@ -33,98 +33,96 @@ import SelectorEngine from './dom/selectorEngine'
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
const NAME = 'tooltip' const NAME = 'tooltip'
const VERSION = '4.3.1' const VERSION = '4.3.1'
const DATA_KEY = 'bs.tooltip' const DATA_KEY = 'bs.tooltip'
const EVENT_KEY = `.${DATA_KEY}` const EVENT_KEY = `.${DATA_KEY}`
const CLASS_PREFIX = 'bs-tooltip' const CLASS_PREFIX = 'bs-tooltip'
const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g') const BSCLS_PREFIX_REGEX = new RegExp(`(^|\\s)${CLASS_PREFIX}\\S+`, 'g')
const DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn'] const DISALLOWED_ATTRIBUTES = ['sanitize', 'whiteList', 'sanitizeFn']
const DefaultType = { const DefaultType = {
animation : 'boolean', animation: 'boolean',
template : 'string', template: 'string',
title : '(string|element|function)', title: '(string|element|function)',
trigger : 'string', trigger: 'string',
delay : '(number|object)', delay: '(number|object)',
html : 'boolean', html: 'boolean',
selector : '(string|boolean)', selector: '(string|boolean)',
placement : '(string|function)', placement: '(string|function)',
offset : '(number|string|function)', offset: '(number|string|function)',
container : '(string|element|boolean)', container: '(string|element|boolean)',
fallbackPlacement : '(string|array)', fallbackPlacement: '(string|array)',
boundary : '(string|element)', boundary: '(string|element)',
sanitize : 'boolean', sanitize: 'boolean',
sanitizeFn : '(null|function)', sanitizeFn: '(null|function)',
whiteList : 'object' whiteList: 'object'
} }
const AttachmentMap = { const AttachmentMap = {
AUTO : 'auto', AUTO: 'auto',
TOP : 'top', TOP: 'top',
RIGHT : 'right', RIGHT: 'right',
BOTTOM : 'bottom', BOTTOM: 'bottom',
LEFT : 'left' LEFT: 'left'
} }
const Default = { const Default = {
animation : true, animation: true,
template : '<div class="tooltip" role="tooltip">' + template: '<div class="tooltip" role="tooltip">' +
'<div class="tooltip-arrow"></div>' + '<div class="tooltip-arrow"></div>' +
'<div class="tooltip-inner"></div></div>', '<div class="tooltip-inner"></div></div>',
trigger : 'hover focus', trigger: 'hover focus',
title : '', title: '',
delay : 0, delay: 0,
html : false, html: false,
selector : false, selector: false,
placement : 'top', placement: 'top',
offset : 0, offset: 0,
container : false, container: false,
fallbackPlacement : 'flip', fallbackPlacement: 'flip',
boundary : 'scrollParent', boundary: 'scrollParent',
sanitize : true, sanitize: true,
sanitizeFn : null, sanitizeFn: null,
whiteList : DefaultWhitelist whiteList: DefaultWhitelist
} }
const HoverState = { const HoverState = {
SHOW : 'show', SHOW: 'show',
OUT : 'out' OUT: 'out'
} }
const Event = { const Event = {
HIDE : `hide${EVENT_KEY}`, HIDE: `hide${EVENT_KEY}`,
HIDDEN : `hidden${EVENT_KEY}`, HIDDEN: `hidden${EVENT_KEY}`,
SHOW : `show${EVENT_KEY}`, SHOW: `show${EVENT_KEY}`,
SHOWN : `shown${EVENT_KEY}`, SHOWN: `shown${EVENT_KEY}`,
INSERTED : `inserted${EVENT_KEY}`, INSERTED: `inserted${EVENT_KEY}`,
CLICK : `click${EVENT_KEY}`, CLICK: `click${EVENT_KEY}`,
FOCUSIN : `focusin${EVENT_KEY}`, FOCUSIN: `focusin${EVENT_KEY}`,
FOCUSOUT : `focusout${EVENT_KEY}`, FOCUSOUT: `focusout${EVENT_KEY}`,
MOUSEENTER : `mouseenter${EVENT_KEY}`, MOUSEENTER: `mouseenter${EVENT_KEY}`,
MOUSELEAVE : `mouseleave${EVENT_KEY}` MOUSELEAVE: `mouseleave${EVENT_KEY}`
} }
const ClassName = { const ClassName = {
FADE : 'fade', FADE: 'fade',
SHOW : 'show' SHOW: 'show'
} }
const Selector = { const Selector = {
TOOLTIP : '.tooltip', TOOLTIP: '.tooltip',
TOOLTIP_INNER : '.tooltip-inner', TOOLTIP_INNER: '.tooltip-inner',
TOOLTIP_ARROW : '.tooltip-arrow' TOOLTIP_ARROW: '.tooltip-arrow'
} }
const Trigger = { const Trigger = {
HOVER : 'hover', HOVER: 'hover',
FOCUS : 'focus', FOCUS: 'focus',
CLICK : 'click', CLICK: 'click',
MANUAL : 'manual' MANUAL: 'manual'
} }
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
* Class Definition * Class Definition
@ -142,16 +140,16 @@ class Tooltip {
} }
// private // private
this._isEnabled = true this._isEnabled = true
this._timeout = 0 this._timeout = 0
this._hoverState = '' this._hoverState = ''
this._activeTrigger = {} this._activeTrigger = {}
this._popper = null this._popper = null
// Protected // Protected
this.element = element this.element = element
this.config = this._getConfig(config) this.config = this._getConfig(config)
this.tip = null this.tip = null
this._setListeners() this._setListeners()
Data.setData(element, this.constructor.DATA_KEY, this) Data.setData(element, this.constructor.DATA_KEY, this)
@ -247,9 +245,9 @@ class Tooltip {
this.tip.parentNode.removeChild(this.tip) this.tip.parentNode.removeChild(this.tip)
} }
this._isEnabled = null this._isEnabled = null
this._timeout = null this._timeout = null
this._hoverState = null this._hoverState = null
this._activeTrigger = null this._activeTrigger = null
if (this._popper !== null) { if (this._popper !== null) {
this._popper.destroy() this._popper.destroy()
@ -257,8 +255,8 @@ class Tooltip {
this._popper = null this._popper = null
this.element = null this.element = null
this.config = null this.config = null
this.tip = null this.tip = null
} }
show() { show() {
@ -269,15 +267,15 @@ class Tooltip {
if (this.isWithContent() && this._isEnabled) { if (this.isWithContent() && this._isEnabled) {
const showEvent = EventHandler.trigger(this.element, this.constructor.Event.SHOW) const showEvent = EventHandler.trigger(this.element, this.constructor.Event.SHOW)
const shadowRoot = findShadowRoot(this.element) const shadowRoot = findShadowRoot(this.element)
const isInTheDom = shadowRoot !== null const isInTheDom = shadowRoot === null ?
? shadowRoot.contains(this.element) this.element.ownerDocument.documentElement.contains(this.element) :
: this.element.ownerDocument.documentElement.contains(this.element) shadowRoot.contains(this.element)
if (showEvent.defaultPrevented || !isInTheDom) { if (showEvent.defaultPrevented || !isInTheDom) {
return return
} }
const tip = this.getTipElement() const tip = this.getTipElement()
const tipId = getUID(this.constructor.NAME) const tipId = getUID(this.constructor.NAME)
tip.setAttribute('id', tipId) tip.setAttribute('id', tipId)
@ -289,9 +287,9 @@ class Tooltip {
tip.classList.add(ClassName.FADE) tip.classList.add(ClassName.FADE)
} }
const placement = typeof this.config.placement === 'function' const placement = typeof this.config.placement === 'function' ?
? this.config.placement.call(this, tip, this.element) this.config.placement.call(this, tip, this.element) :
: this.config.placement this.config.placement
const attachment = this._getAttachment(placement) const attachment = this._getAttachment(placement)
this.addAttachmentClass(attachment) this.addAttachmentClass(attachment)
@ -319,12 +317,12 @@ class Tooltip {
boundariesElement: this.config.boundary boundariesElement: this.config.boundary
} }
}, },
onCreate: (data) => { onCreate: data => {
if (data.originalPlacement !== data.placement) { if (data.originalPlacement !== data.placement) {
this._handlePopperPlacementChange(data) this._handlePopperPlacementChange(data)
} }
}, },
onUpdate: (data) => this._handlePopperPlacementChange(data) onUpdate: data => this._handlePopperPlacementChange(data)
}) })
tip.classList.add(ClassName.SHOW) tip.classList.add(ClassName.SHOW)
@ -334,7 +332,7 @@ class Tooltip {
// only needed because of broken event delegation on iOS // only needed because of broken event delegation on iOS
// https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html // https://www.quirksmode.org/blog/archives/2014/02/mouse_event_bub.html
if ('ontouchstart' in document.documentElement) { if ('ontouchstart' in document.documentElement) {
makeArray(document.body.children).forEach((element) => { makeArray(document.body.children).forEach(element => {
EventHandler.on(element, 'mouseover', noop()) EventHandler.on(element, 'mouseover', noop())
}) })
} }
@ -343,8 +341,9 @@ class Tooltip {
if (this.config.animation) { if (this.config.animation) {
this._fixTransition() this._fixTransition()
} }
const prevHoverState = this._hoverState const prevHoverState = this._hoverState
this._hoverState = null this._hoverState = null
EventHandler.trigger(this.element, this.constructor.Event.SHOWN) EventHandler.trigger(this.element, this.constructor.Event.SHOWN)
@ -364,8 +363,8 @@ class Tooltip {
} }
hide(callback) { hide(callback) {
const tip = this.getTipElement() const tip = this.getTipElement()
const complete = () => { const complete = () => {
if (this._hoverState !== HoverState.SHOW && tip.parentNode) { if (this._hoverState !== HoverState.SHOW && tip.parentNode) {
tip.parentNode.removeChild(tip) tip.parentNode.removeChild(tip)
} }
@ -393,7 +392,7 @@ class Tooltip {
// empty mouseover listeners we added for iOS support // empty mouseover listeners we added for iOS support
if ('ontouchstart' in document.documentElement) { if ('ontouchstart' in document.documentElement) {
makeArray(document.body.children) makeArray(document.body.children)
.forEach((element) => EventHandler.off(element, 'mouseover', noop)) .forEach(element => EventHandler.off(element, 'mouseover', noop))
} }
this._activeTrigger[Trigger.CLICK] = false this._activeTrigger[Trigger.CLICK] = false
@ -485,9 +484,9 @@ class Tooltip {
let title = this.element.getAttribute('data-original-title') let title = this.element.getAttribute('data-original-title')
if (!title) { if (!title) {
title = typeof this.config.title === 'function' title = typeof this.config.title === 'function' ?
? this.config.title.call(this.element) this.config.title.call(this.element) :
: this.config.title this.config.title
} }
return title return title
@ -499,7 +498,7 @@ class Tooltip {
const offset = {} const offset = {}
if (typeof this.config.offset === 'function') { if (typeof this.config.offset === 'function') {
offset.fn = (data) => { offset.fn = data => {
data.offsets = { data.offsets = {
...data.offsets, ...data.offsets,
...this.config.offset(data.offsets, this.element) || {} ...this.config.offset(data.offsets, this.element) || {}
@ -533,30 +532,30 @@ class Tooltip {
_setListeners() { _setListeners() {
const triggers = this.config.trigger.split(' ') const triggers = this.config.trigger.split(' ')
triggers.forEach((trigger) => { triggers.forEach(trigger => {
if (trigger === 'click') { if (trigger === 'click') {
EventHandler.on(this.element, EventHandler.on(this.element,
this.constructor.Event.CLICK, this.constructor.Event.CLICK,
this.config.selector, this.config.selector,
(event) => this.toggle(event) event => this.toggle(event)
) )
} else if (trigger !== Trigger.MANUAL) { } else if (trigger !== Trigger.MANUAL) {
const eventIn = trigger === Trigger.HOVER const eventIn = trigger === Trigger.HOVER ?
? this.constructor.Event.MOUSEENTER this.constructor.Event.MOUSEENTER :
: this.constructor.Event.FOCUSIN this.constructor.Event.FOCUSIN
const eventOut = trigger === Trigger.HOVER const eventOut = trigger === Trigger.HOVER ?
? this.constructor.Event.MOUSELEAVE this.constructor.Event.MOUSELEAVE :
: this.constructor.Event.FOCUSOUT this.constructor.Event.FOCUSOUT
EventHandler.on(this.element, EventHandler.on(this.element,
eventIn, eventIn,
this.config.selector, this.config.selector,
(event) => this._enter(event) event => this._enter(event)
) )
EventHandler.on(this.element, EventHandler.on(this.element,
eventOut, eventOut,
this.config.selector, this.config.selector,
(event) => this._leave(event) event => this._leave(event)
) )
} }
}) })
@ -686,7 +685,7 @@ class Tooltip {
const dataAttributes = Manipulator.getDataAttributes(this.element) const dataAttributes = Manipulator.getDataAttributes(this.element)
Object.keys(dataAttributes) Object.keys(dataAttributes)
.forEach((dataAttr) => { .forEach(dataAttr => {
if (DISALLOWED_ATTRIBUTES.indexOf(dataAttr) !== -1) { if (DISALLOWED_ATTRIBUTES.indexOf(dataAttr) !== -1) {
delete dataAttributes[dataAttr] delete dataAttributes[dataAttr]
} }
@ -749,8 +748,8 @@ class Tooltip {
const tabClass = tip.getAttribute('class').match(BSCLS_PREFIX_REGEX) const tabClass = tip.getAttribute('class').match(BSCLS_PREFIX_REGEX)
if (tabClass !== null && tabClass.length) { if (tabClass !== null && tabClass.length) {
tabClass tabClass
.map((token) => token.trim()) .map(token => token.trim())
.forEach((tClass) => tip.classList.remove(tClass)) .forEach(tClass => tip.classList.remove(tClass))
} }
} }
@ -767,6 +766,7 @@ class Tooltip {
if (tip.getAttribute('x-placement') !== null) { if (tip.getAttribute('x-placement') !== null) {
return return
} }
tip.classList.remove(ClassName.FADE) tip.classList.remove(ClassName.FADE)
this.config.animation = false this.config.animation = false
this.hide() this.hide()
@ -778,7 +778,7 @@ class Tooltip {
static _jQueryInterface(config) { static _jQueryInterface(config) {
return this.each(function () { return this.each(function () {
let data = Data.getData(this, DATA_KEY) let data = Data.getData(this, DATA_KEY)
const _config = typeof config === 'object' && config const _config = typeof config === 'object' && config
if (!data && /dispose|hide/.test(config)) { if (!data && /dispose|hide/.test(config)) {
@ -793,6 +793,7 @@ class Tooltip {
if (typeof data[config] === 'undefined') { if (typeof data[config] === 'undefined') {
throw new TypeError(`No method named "${config}"`) throw new TypeError(`No method named "${config}"`)
} }
data[config]() data[config]()
} }
}) })
@ -811,10 +812,10 @@ class Tooltip {
*/ */
if (typeof $ !== 'undefined') { if (typeof $ !== 'undefined') {
const JQUERY_NO_CONFLICT = $.fn[NAME] const JQUERY_NO_CONFLICT = $.fn[NAME]
$.fn[NAME] = Tooltip._jQueryInterface $.fn[NAME] = Tooltip._jQueryInterface
$.fn[NAME].Constructor = Tooltip $.fn[NAME].Constructor = Tooltip
$.fn[NAME].noConflict = () => { $.fn[NAME].noConflict = () => {
$.fn[NAME] = JQUERY_NO_CONFLICT $.fn[NAME] = JQUERY_NO_CONFLICT
return Tooltip._jQueryInterface return Tooltip._jQueryInterface
} }

View File

@ -8,10 +8,10 @@
const MAX_UID = 1000000 const MAX_UID = 1000000
const MILLISECONDS_MULTIPLIER = 1000 const MILLISECONDS_MULTIPLIER = 1000
const TRANSITION_END = 'transitionend' const TRANSITION_END = 'transitionend'
const jQuery = window.jQuery const { jQuery } = window
// Shoutout AngusCroll (https://goo.gl/pxwQGp) // Shoutout AngusCroll (https://goo.gl/pxwQGp)
const toType = (obj) => ({}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase()) const toType = obj => ({}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase())
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
@ -19,15 +19,16 @@ const toType = (obj) => ({}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCa
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
*/ */
const getUID = (prefix) => { const getUID = prefix => {
do { do {
// eslint-disable-next-line no-bitwise // eslint-disable-next-line no-bitwise
prefix += ~~(Math.random() * MAX_UID) // "~~" acts like a faster Math.floor() here prefix += ~~(Math.random() * MAX_UID) // "~~" acts like a faster Math.floor() here
} while (document.getElementById(prefix)) } while (document.getElementById(prefix))
return prefix return prefix
} }
const getSelectorFromElement = (element) => { const getSelectorFromElement = element => {
let selector = element.getAttribute('data-target') let selector = element.getAttribute('data-target')
if (!selector || selector === '#') { if (!selector || selector === '#') {
@ -38,12 +39,12 @@ const getSelectorFromElement = (element) => {
try { try {
return document.querySelector(selector) ? selector : null return document.querySelector(selector) ? selector : null
} catch (err) { } catch (error) {
return null return null
} }
} }
const getTransitionDurationFromElement = (element) => { const getTransitionDurationFromElement = element => {
if (!element) { if (!element) {
return 0 return 0
} }
@ -69,11 +70,11 @@ const getTransitionDurationFromElement = (element) => {
return (parseFloat(transitionDuration) + parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER return (parseFloat(transitionDuration) + parseFloat(transitionDelay)) * MILLISECONDS_MULTIPLIER
} }
const triggerTransitionEnd = (element) => { const triggerTransitionEnd = element => {
element.dispatchEvent(new Event(TRANSITION_END)) element.dispatchEvent(new Event(TRANSITION_END))
} }
const isElement = (obj) => (obj[0] || obj).nodeType const isElement = obj => (obj[0] || obj).nodeType
const emulateTransitionEnd = (element, duration) => { const emulateTransitionEnd = (element, duration) => {
let called = false let called = false
@ -94,11 +95,12 @@ const emulateTransitionEnd = (element, duration) => {
const typeCheckConfig = (componentName, config, configTypes) => { const typeCheckConfig = (componentName, config, configTypes) => {
Object.keys(configTypes) Object.keys(configTypes)
.forEach((property) => { .forEach(property => {
const expectedTypes = configTypes[property] const expectedTypes = configTypes[property]
const value = config[property] const value = config[property]
const valueType = value && isElement(value) const valueType = value && isElement(value) ?
? 'element' : toType(value) 'element' :
toType(value)
if (!new RegExp(expectedTypes).test(valueType)) { if (!new RegExp(expectedTypes).test(valueType)) {
throw new Error( throw new Error(
@ -109,7 +111,7 @@ const typeCheckConfig = (componentName, config, configTypes) => {
}) })
} }
const makeArray = (nodeList) => { const makeArray = nodeList => {
if (!nodeList) { if (!nodeList) {
return [] return []
} }
@ -117,7 +119,7 @@ const makeArray = (nodeList) => {
return [].slice.call(nodeList) return [].slice.call(nodeList)
} }
const isVisible = (element) => { const isVisible = element => {
if (!element) { if (!element) {
return false return false
} }
@ -131,7 +133,7 @@ const isVisible = (element) => {
return false return false
} }
const findShadowRoot = (element) => { const findShadowRoot = element => {
if (!document.documentElement.attachShadow) { if (!document.documentElement.attachShadow) {
return null return null
} }
@ -157,7 +159,7 @@ const findShadowRoot = (element) => {
// eslint-disable-next-line no-empty-function // eslint-disable-next-line no-empty-function
const noop = () => function () {} const noop = () => function () {}
const reflow = (element) => element.offsetHeight const reflow = element => element.offsetHeight
export { export {
jQuery, jQuery,

View File

@ -47,7 +47,7 @@ const allowedAttribute = (attr, allowedAttributeList) => {
return true return true
} }
const regExp = allowedAttributeList.filter((attrRegex) => attrRegex instanceof RegExp) const regExp = allowedAttributeList.filter(attrRegex => attrRegex instanceof RegExp)
// Check if a regular expression validates the attribute. // Check if a regular expression validates the attribute.
for (let i = 0, l = regExp.length; i < l; i++) { for (let i = 0, l = regExp.length; i < l; i++) {
@ -120,7 +120,7 @@ export function sanitizeHtml(unsafeHtml, whiteList, sanitizeFn) {
const attributeList = makeArray(el.attributes) const attributeList = makeArray(el.attributes)
const whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || []) const whitelistedAttributes = [].concat(whiteList['*'] || [], whiteList[elName] || [])
attributeList.forEach((attr) => { attributeList.forEach(attr => {
if (!allowedAttribute(attr, whitelistedAttributes)) { if (!allowedAttribute(attr, whitelistedAttributes)) {
el.removeAttribute(attr.nodeName) el.removeAttribute(attr.nodeName)
} }

View File

@ -13,8 +13,8 @@ const browsers = {
base: 'BrowserStack', base: 'BrowserStack',
os: 'OS X', os: 'OS X',
os_version: 'High Sierra', os_version: 'High Sierra',
browser : 'Chrome', browser: 'Chrome',
browser_version : 'latest' browser_version: 'latest'
}, },
firefoxMac: { firefoxMac: {
base: 'BrowserStack', base: 'BrowserStack',

View File

@ -2,6 +2,6 @@ import 'popper.js'
import bootstrap from '../../../dist/js/bootstrap' import bootstrap from '../../../dist/js/bootstrap'
window.addEventListener('load', () => { window.addEventListener('load', () => {
Array.from(document.querySelectorAll('[data-toggle="tooltip"]')) [...document.querySelectorAll('[data-toggle="tooltip"]')]
.map((tooltipNode) => new bootstrap.Tooltip(tooltipNode)) .map(tooltipNode => new bootstrap.Tooltip(tooltipNode))
}) })

View File

@ -8,7 +8,7 @@ const {
browsersKeys browsersKeys
} = require('./browsers') } = require('./browsers')
const env = process.env const { env } = process
const bundle = env.BUNDLE === 'true' const bundle = env.BUNDLE === 'true'
const browserStack = env.BROWSER === 'true' const browserStack = env.BROWSER === 'true'
const debug = env.DEBUG === 'true' const debug = env.DEBUG === 'true'
@ -173,7 +173,7 @@ conf.plugins = plugins
conf.reporters = reporters conf.reporters = reporters
conf.files = files conf.files = files
module.exports = (karmaConfig) => { module.exports = karmaConfig => {
// possible values: karmaConfig.LOG_DISABLE || karmaConfig.LOG_ERROR || karmaConfig.LOG_WARN || karmaConfig.LOG_INFO || karmaConfig.LOG_DEBUG // possible values: karmaConfig.LOG_DISABLE || karmaConfig.LOG_ERROR || karmaConfig.LOG_WARN || karmaConfig.LOG_INFO || karmaConfig.LOG_DEBUG
conf.logLevel = karmaConfig.LOG_ERROR || karmaConfig.LOG_WARN conf.logLevel = karmaConfig.LOG_ERROR || karmaConfig.LOG_WARN
karmaConfig.set(conf) karmaConfig.set(conf)

View File

@ -1,7 +1,7 @@
$(function () { $(function () {
'use strict' 'use strict'
var Alert = typeof window.bootstrap !== 'undefined' ? window.bootstrap.Alert : window.Alert var Alert = typeof window.bootstrap === 'undefined' ? window.Alert : window.bootstrap.Alert
QUnit.module('alert plugin') QUnit.module('alert plugin')

View File

@ -1,7 +1,7 @@
$(function () { $(function () {
'use strict' 'use strict'
var Button = typeof window.bootstrap !== 'undefined' ? window.bootstrap.Button : window.Button var Button = typeof window.bootstrap === 'undefined' ? window.Button : window.bootstrap.Button
QUnit.module('button plugin') QUnit.module('button plugin')

View File

@ -1,7 +1,7 @@
$(function () { $(function () {
'use strict' 'use strict'
window.Carousel = typeof bootstrap !== 'undefined' ? bootstrap.Carousel : Carousel window.Carousel = typeof bootstrap === 'undefined' ? Carousel : bootstrap.Carousel
var originWinPointerEvent = window.PointerEvent var originWinPointerEvent = window.PointerEvent
window.MSPointerEvent = null window.MSPointerEvent = null
@ -65,8 +65,8 @@ $(function () {
$el.bootstrapCarousel() $el.bootstrapCarousel()
try { try {
$el.bootstrapCarousel('noMethod') $el.bootstrapCarousel('noMethod')
} catch (err) { } catch (error) {
assert.strictEqual(err.message, 'No method named "noMethod"') assert.strictEqual(error.message, 'No method named "noMethod"')
} }
}) })
@ -89,8 +89,8 @@ $(function () {
try { try {
$('<div/>').bootstrapCarousel(config) $('<div/>').bootstrapCarousel(config)
} catch (err) { } catch (error) {
message = err.message message = error.message
} }
assert.ok(message === expectedMessage, 'correct error message') assert.ok(message === expectedMessage, 'correct error message')
@ -102,8 +102,8 @@ $(function () {
try { try {
$('<div/>').bootstrapCarousel(config) $('<div/>').bootstrapCarousel(config)
} catch (err) { } catch (error) {
message = err.message message = error.message
} }
assert.ok(message === expectedMessage, 'correct error message') assert.ok(message === expectedMessage, 'correct error message')
@ -161,6 +161,7 @@ $(function () {
}, 0) }, 0)
$carousel[0].removeEventListener('slide.bs.carousel', onSlide) $carousel[0].removeEventListener('slide.bs.carousel', onSlide)
} }
$carousel[0].addEventListener('slide.bs.carousel', onSlide) $carousel[0].addEventListener('slide.bs.carousel', onSlide)
function onSlid() { function onSlid() {
@ -173,6 +174,7 @@ $(function () {
}, 0) }, 0)
$carousel[0].removeEventListener('slid.bs.carousel', onSlid) $carousel[0].removeEventListener('slid.bs.carousel', onSlid)
} }
$carousel[0].addEventListener('slid.bs.carousel', onSlid) $carousel[0].addEventListener('slid.bs.carousel', onSlid)
$carousel.bootstrapCarousel('next') $carousel.bootstrapCarousel('next')
@ -682,6 +684,7 @@ $(function () {
assert.strictEqual(event.defaultPrevented, false) assert.strictEqual(event.defaultPrevented, false)
$template[0].removeEventListener('keydown', handlerKeydown) $template[0].removeEventListener('keydown', handlerKeydown)
} }
$template[0].addEventListener('keydown', handlerKeydown) $template[0].addEventListener('keydown', handlerKeydown)
// arrow down // arrow down
@ -694,6 +697,7 @@ $(function () {
$template[0].addEventListener('keydown', handlerKeydown2) $template[0].addEventListener('keydown', handlerKeydown2)
done() done()
} }
$template[0].addEventListener('keydown', handlerKeydown2) $template[0].addEventListener('keydown', handlerKeydown2)
// arrow up // arrow up

View File

@ -1,7 +1,7 @@
$(function () { $(function () {
'use strict' 'use strict'
var Collapse = typeof window.bootstrap !== 'undefined' ? window.bootstrap.Collapse : window.Collapse var Collapse = typeof window.bootstrap === 'undefined' ? window.Collapse : window.bootstrap.Collapse
QUnit.module('collapse plugin') QUnit.module('collapse plugin')
@ -33,8 +33,8 @@ $(function () {
$el.bootstrapCollapse() $el.bootstrapCollapse()
try { try {
$el.bootstrapCollapse('noMethod') $el.bootstrapCollapse('noMethod')
} catch (err) { } catch (error) {
assert.strictEqual(err.message, 'No method named "noMethod"') assert.strictEqual(error.message, 'No method named "noMethod"')
} }
}) })
@ -459,7 +459,7 @@ $(function () {
'<div class="card"/>' + '<div class="card"/>' +
'</div>' '</div>'
var showFired = false var showFired = false
var $groups = $(accordionHTML).appendTo('#qunit-fixture').find('.card') var $groups = $(accordionHTML).appendTo('#qunit-fixture').find('.card')
var $target1 = $('<a role="button" data-toggle="collapse" href="#body1"/>').appendTo($groups.eq(0)) var $target1 = $('<a role="button" data-toggle="collapse" href="#body1"/>').appendTo($groups.eq(0))
@ -470,7 +470,7 @@ $(function () {
}) })
var $target2 = $('<a role="button" data-toggle="collapse" href="#body2"/>').appendTo($groups.eq(1)) var $target2 = $('<a role="button" data-toggle="collapse" href="#body2"/>').appendTo($groups.eq(1))
var $body2 = $('<div id="body2" class="collapse" data-parent="#accordion"/>').appendTo($groups.eq(1)) var $body2 = $('<div id="body2" class="collapse" data-parent="#accordion"/>').appendTo($groups.eq(1))
$target2[0].dispatchEvent(new Event('click')) $target2[0].dispatchEvent(new Event('click'))
@ -607,8 +607,8 @@ $(function () {
var $collapseTwoOne = $('#collapseTwoOne') var $collapseTwoOne = $('#collapseTwoOne')
var $collapseTwoTwo = $('#collapseTwoTwo') var $collapseTwoTwo = $('#collapseTwoTwo')
var collapsedElements = { var collapsedElements = {
one : false, one: false,
two : false two: false
} }
function firstTest() { function firstTest() {
@ -838,7 +838,7 @@ $(function () {
parent: $('.my-collapse') parent: $('.my-collapse')
}) })
assert.ok(true, 'collapse correctly created') assert.ok(true, 'collapse correctly created')
} catch (err) { } catch (error) {
assert.ok(false, 'collapse not created') assert.ok(false, 'collapse not created')
} }
}) })
@ -859,7 +859,7 @@ $(function () {
parent: $('.my-collapse')[0] parent: $('.my-collapse')[0]
}) })
assert.ok(true, 'collapse correctly created') assert.ok(true, 'collapse correctly created')
} catch (err) { } catch (error) {
assert.ok(false, 'collapse not created') assert.ok(false, 'collapse not created')
} }
}) })

View File

@ -1,7 +1,7 @@
$(function () { $(function () {
'use strict' 'use strict'
var Dropdown = typeof window.bootstrap !== 'undefined' ? window.bootstrap.Dropdown : window.Dropdown var Dropdown = typeof window.bootstrap === 'undefined' ? window.Dropdown : window.bootstrap.Dropdown
QUnit.module('dropdowns plugin') QUnit.module('dropdowns plugin')
@ -34,8 +34,8 @@ $(function () {
$el.bootstrapDropdown() $el.bootstrapDropdown()
try { try {
$el.bootstrapDropdown('noMethod') $el.bootstrapDropdown('noMethod')
} catch (err) { } catch (error) {
assert.strictEqual(err.message, 'No method named "noMethod"') assert.strictEqual(error.message, 'No method named "noMethod"')
} }
}) })

View File

@ -1,8 +1,8 @@
$(function () { $(function () {
'use strict' 'use strict'
window.Util = typeof bootstrap !== 'undefined' ? bootstrap.Util : Util window.Util = typeof bootstrap === 'undefined' ? Util : bootstrap.Util
var Modal = typeof window.bootstrap !== 'undefined' ? window.bootstrap.Modal : window.Modal var Modal = typeof window.bootstrap === 'undefined' ? window.Modal : window.bootstrap.Modal
QUnit.module('modal plugin') QUnit.module('modal plugin')
@ -45,8 +45,8 @@ $(function () {
$el.bootstrapModal() $el.bootstrapModal()
try { try {
$el.bootstrapModal('noMethod') $el.bootstrapModal('noMethod')
} catch (err) { } catch (error) {
assert.strictEqual(err.message, 'No method named "noMethod"') assert.strictEqual(error.message, 'No method named "noMethod"')
} }
}) })
@ -460,8 +460,8 @@ $(function () {
var originalPadding = $body.css('padding-right') var originalPadding = $body.css('padding-right')
// Hide scrollbars to prevent the body overflowing // Hide scrollbars to prevent the body overflowing
$body.css('overflow', 'hidden') // Real scrollbar (for in-browser testing) $body.css('overflow', 'hidden') // Real scrollbar (for in-browser testing)
$('html').css('padding-right', '0px') // Simulated scrollbar (for PhantomJS) $('html').css('padding-right', '0px') // Simulated scrollbar (for PhantomJS)
$('<div id="modal-test"><div class="modal-dialog" /></div>') $('<div id="modal-test"><div class="modal-dialog" /></div>')
.on('shown.bs.modal', function () { .on('shown.bs.modal', function () {
@ -758,6 +758,7 @@ $(function () {
document.removeEventListener('focusin', focusInListener) document.removeEventListener('focusin', focusInListener)
done() done()
} }
document.addEventListener('focusin', focusInListener) document.addEventListener('focusin', focusInListener)
var focusInEvent = new Event('focusin') var focusInEvent = new Event('focusin')

View File

@ -1,7 +1,7 @@
$(function () { $(function () {
'use strict' 'use strict'
var Popover = typeof window.bootstrap !== 'undefined' ? window.bootstrap.Popover : window.Popover var Popover = typeof window.bootstrap === 'undefined' ? window.Popover : window.bootstrap.Popover
QUnit.module('popover plugin') QUnit.module('popover plugin')
@ -34,8 +34,8 @@ $(function () {
$el.bootstrapPopover() $el.bootstrapPopover()
try { try {
$el.bootstrapPopover('noMethod') $el.bootstrapPopover('noMethod')
} catch (err) { } catch (error) {
assert.strictEqual(err.message, 'No method named "noMethod"') assert.strictEqual(error.message, 'No method named "noMethod"')
} }
}) })
@ -367,8 +367,8 @@ $(function () {
try { try {
$('<div data-toggle="popover" data-title="some title" data-content="@Johann-S" style="display: none"/>').bootstrapPopover('show') $('<div data-toggle="popover" data-title="some title" data-content="@Johann-S" style="display: none"/>').bootstrapPopover('show')
} catch (err) { } catch (error) {
assert.strictEqual(err.message, 'Please use show on visible elements') assert.strictEqual(error.message, 'Please use show on visible elements')
done() done()
} }
}) })

View File

@ -1,7 +1,7 @@
$(function () { $(function () {
'use strict' 'use strict'
var ScrollSpy = typeof window.bootstrap !== 'undefined' ? window.bootstrap.ScrollSpy : window.ScrollSpy var ScrollSpy = typeof window.bootstrap === 'undefined' ? window.ScrollSpy : window.bootstrap.ScrollSpy
QUnit.module('scrollspy plugin') QUnit.module('scrollspy plugin')
@ -33,8 +33,8 @@ $(function () {
$el.bootstrapScrollspy() $el.bootstrapScrollspy()
try { try {
$el.bootstrapScrollspy('noMethod') $el.bootstrapScrollspy('noMethod')
} catch (err) { } catch (error) {
assert.strictEqual(err.message, 'No method named "noMethod"') assert.strictEqual(error.message, 'No method named "noMethod"')
} }
}) })

View File

@ -33,8 +33,8 @@ $(function () {
$el.bootstrapTab() $el.bootstrapTab()
try { try {
$el.bootstrapTab('noMethod') $el.bootstrapTab('noMethod')
} catch (err) { } catch (error) {
assert.strictEqual(err.message, 'No method named "noMethod"') assert.strictEqual(error.message, 'No method named "noMethod"')
} }
}) })
@ -96,7 +96,7 @@ $(function () {
QUnit.test('should activate element by tab id in nav list', function (assert) { QUnit.test('should activate element by tab id in nav list', function (assert) {
assert.expect(2) assert.expect(2)
var tabsHTML = '<nav class="nav">' + var tabsHTML = '<nav class="nav">' +
'<a href="#home">Home</a>' + '<a href="#home">Home</a>' +
'<a href="#profile">Profile</a>' + '<a href="#profile">Profile</a>' +
'</nav>' '</nav>'
@ -112,7 +112,7 @@ $(function () {
QUnit.test('should activate element by tab id in list group', function (assert) { QUnit.test('should activate element by tab id in list group', function (assert) {
assert.expect(2) assert.expect(2)
var tabsHTML = '<div class="list-group">' + var tabsHTML = '<div class="list-group">' +
'<a href="#home">Home</a>' + '<a href="#home">Home</a>' +
'<a href="#profile">Profile</a>' + '<a href="#profile">Profile</a>' +
'</div>' '</div>'

View File

@ -41,8 +41,8 @@ $(function () {
try { try {
$el.bootstrapToast('noMethod') $el.bootstrapToast('noMethod')
} catch (err) { } catch (error) {
assert.strictEqual(err.message, 'No method named "noMethod"') assert.strictEqual(error.message, 'No method named "noMethod"')
} }
}) })
@ -208,7 +208,6 @@ $(function () {
.bootstrapToast('show') .bootstrapToast('show')
}) })
QUnit.test('should close toast when close element with data-dismiss attribute is set', function (assert) { QUnit.test('should close toast when close element with data-dismiss attribute is set', function (assert) {
assert.expect(2) assert.expect(2)
var done = assert.async() var done = assert.async()

View File

@ -1,7 +1,7 @@
$(function () { $(function () {
'use strict' 'use strict'
var Tooltip = typeof window.bootstrap !== 'undefined' ? window.bootstrap.Tooltip : window.Tooltip var Tooltip = typeof window.bootstrap === 'undefined' ? window.Tooltip : window.bootstrap.Tooltip
QUnit.module('tooltip plugin') QUnit.module('tooltip plugin')
@ -34,8 +34,8 @@ $(function () {
$el.bootstrapTooltip() $el.bootstrapTooltip()
try { try {
$el.bootstrapTooltip('noMethod') $el.bootstrapTooltip('noMethod')
} catch (err) { } catch (error) {
assert.strictEqual(err.message, 'No method named "noMethod"') assert.strictEqual(error.message, 'No method named "noMethod"')
} }
}) })
@ -231,8 +231,8 @@ $(function () {
try { try {
$('<div title="tooltip title" style="display: none"/>').bootstrapTooltip('show') $('<div title="tooltip title" style="display: none"/>').bootstrapTooltip('show')
} catch (err) { } catch (error) {
assert.strictEqual(err.message, 'Please use show on visible elements') assert.strictEqual(error.message, 'Please use show on visible elements')
done() done()
} }
}) })
@ -336,7 +336,7 @@ $(function () {
assert.expect(3) assert.expect(3)
var $tooltip = $('<div/>') var $tooltip = $('<div/>')
.bootstrapTooltip() .bootstrapTooltip()
.on('click.foo', function () {}) // eslint-disable-line no-empty-function .on('click.foo', function () {}) // eslint-disable-line no-empty-function
assert.ok(Tooltip._getInstance($tooltip[0]), 'tooltip has data') assert.ok(Tooltip._getInstance($tooltip[0]), 'tooltip has data')
@ -557,7 +557,7 @@ $(function () {
try { try {
$tooltip.bootstrapTooltip('show') $tooltip.bootstrapTooltip('show')
} catch (err) { } catch (error) {
passed = false passed = false
} }

View File

@ -44,8 +44,8 @@ $(function () {
try { try {
Util.typeCheckConfig(namePlugin, config, defaultType) Util.typeCheckConfig(namePlugin, config, defaultType)
} catch (err) { } catch (error) {
assert.strictEqual(err.message, 'COLLAPSE: Option "parent" provided type "number" but expected type "(string|element)".') assert.strictEqual(error.message, 'COLLAPSE: Option "parent" provided type "number" but expected type "(string|element)".')
} }
}) })
@ -139,9 +139,7 @@ $(function () {
assert.expect(1) assert.expect(1)
var $div = $('<div id="test"></div>').appendTo($('#qunit-fixture')) var $div = $('<div id="test"></div>').appendTo($('#qunit-fixture'))
if (!document.documentElement.attachShadow) { if (document.documentElement.attachShadow) {
assert.equal(null, Util.findShadowRoot($div[0]))
} else {
var sandbox = sinon.createSandbox() var sandbox = sinon.createSandbox()
sandbox.replace(document.documentElement, 'attachShadow', function () { sandbox.replace(document.documentElement, 'attachShadow', function () {
@ -151,6 +149,8 @@ $(function () {
assert.equal(null, Util.findShadowRoot($div[0])) assert.equal(null, Util.findShadowRoot($div[0]))
sandbox.restore() sandbox.restore()
} else {
assert.equal(null, Util.findShadowRoot($div[0]))
} }
}) })

View File

@ -156,4 +156,4 @@
}) })
bsCustomFileInput.init() bsCustomFileInput.init()
}()) })()

View File

@ -52,4 +52,4 @@
}, },
debug: false // Set debug to true if you want to inspect the dropdown debug: false // Set debug to true if you want to inspect the dropdown
}) })
}()) })()

View File

@ -13,8 +13,9 @@
event.preventDefault() event.preventDefault()
event.stopPropagation() event.stopPropagation()
} }
form.classList.add('was-validated') form.classList.add('was-validated')
}, false) }, false)
}) })
}, false) }, false)
}()) })()

View File

@ -50,4 +50,4 @@
} }
} }
}) })
}()) })()

View File

@ -4,4 +4,4 @@
document.querySelector('[data-toggle="offcanvas"]').addEventListener('click', function () { document.querySelector('[data-toggle="offcanvas"]').addEventListener('click', function () {
document.querySelector('.offcanvas-collapse').classList.toggle('open') document.querySelector('.offcanvas-collapse').classList.toggle('open')
}) })
}()) })()

View File

@ -2,6 +2,8 @@
// IT'S ALL JUST JUNK FOR OUR DOCS! // IT'S ALL JUST JUNK FOR OUR DOCS!
// ++++++++++++++++++++++++++++++++++++++++++ // ++++++++++++++++++++++++++++++++++++++++++
/* eslint-disable max-nested-callbacks */
(function () { (function () {
'use strict' 'use strict'
@ -24,4 +26,4 @@
}) })
}) })
} }
}()) })()