0
0
mirror of https://github.com/twbs/bootstrap.git synced 2024-12-02 14:24:19 +01:00

Merge branch 'master' into underline-links

This commit is contained in:
Martijn Cuppens 2020-03-20 12:30:41 +01:00 committed by GitHub
commit 4a37677dda
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
64 changed files with 700 additions and 947 deletions

View File

@ -6,8 +6,8 @@ not dead
Chrome >= 60 Chrome >= 60
Firefox >= 60 Firefox >= 60
Edge >= 16.16299 Edge >= 16.16299
Explorer 11
iOS >= 10 iOS >= 10
Safari >= 10 Safari >= 10
Android >= 6 Android >= 6
not Explorer <= 11
not ExplorerMobile <= 11 not ExplorerMobile <= 11

View File

@ -6,6 +6,6 @@ Before opening:
Bug reports must include: Bug reports must include:
- Operating system and version (Windows, macOS, Android, iOS, Win10 Mobile) - Operating system and version (Windows, macOS, Android, iOS)
- Browser and version (Chrome, Firefox, Safari, IE, MS Edge, Opera 15+, Android Browser) - Browser and version (Chrome, Firefox, Safari, Microsoft Edge, Opera, Android Browser)
- [Reduced test case](https://css-tricks.com/reduced-test-cases/) and suggested fix using [CodePen](https://codepen.io/) or [JS Bin](https://jsbin.com/) - [Reduced test case](https://css-tricks.com/reduced-test-cases/) and suggested fix using [CodePen](https://codepen.io/) or [JS Bin](https://jsbin.com/)

View File

@ -12,6 +12,6 @@ Before opening:
Bug reports must include: Bug reports must include:
- Operating system and version (Windows, macOS, Android, iOS, Win10 Mobile) - Operating system and version (Windows, macOS, Android, iOS)
- Browser and version (Chrome, Firefox, Safari, IE, MS Edge, Opera 15+, Android Browser) - Browser and version (Chrome, Firefox, Safari, Microsoft Edge, Opera, Android Browser)
- [Reduced test case](https://css-tricks.com/reduced-test-cases/) and suggested fix using [CodePen](https://codepen.io/) or [JS Bin](https://jsbin.com/) - [Reduced test case](https://css-tricks.com/reduced-test-cases/) and suggested fix using [CodePen](https://codepen.io/) or [JS Bin](https://jsbin.com/)

View File

@ -37,10 +37,7 @@ childProcess.exec('java -version', (error, stdout, stderr) => {
'The “month” input type is not supported in all browsers.*', 'The “month” input type is not supported in all browsers.*',
'The “color” input type is not supported in all browsers.*', 'The “color” input type is not supported in all browsers.*',
'The “datetime-local” input type is not supported in all browsers.*', 'The “datetime-local” input type is not supported in all browsers.*',
'The “time” input type is not supported in all browsers.*', 'The “time” input type is not supported in all browsers.*'
// IE11 doesn't recognize <main> / give the element an implicit "main" landmark.
// Explicit role="main" is redundant for other modern browsers, but still valid.
'The “main” role is unnecessary for element “main”.'
].join('|') ].join('|')
const args = [ const args = [

View File

@ -38,7 +38,7 @@
}, },
{ {
"path": "./dist/js/bootstrap.bundle.min.js", "path": "./dist/js/bootstrap.bundle.min.js",
"maxSize": "24 kB" "maxSize": "23 kB"
}, },
{ {
"path": "./dist/js/bootstrap.esm.js", "path": "./dist/js/bootstrap.esm.js",
@ -54,7 +54,7 @@
}, },
{ {
"path": "./dist/js/bootstrap.min.js", "path": "./dist/js/bootstrap.min.js",
"maxSize": "17.5 kB" "maxSize": "16.5 kB"
} }
] ]
} }

View File

@ -51,7 +51,7 @@ params:
current_ruby_version: "4.3.1" current_ruby_version: "4.3.1"
rfs_version: "9.0.2" rfs_version: "9.0.2"
docs_version: "4.3" docs_version: "4.3"
repo: "https://github.com/twbs/bootstrap" repo: "https://github.com/twbs"
twitter: "getbootstrap" twitter: "getbootstrap"
slack: "https://bootstrap-slack.herokuapp.com/" slack: "https://bootstrap-slack.herokuapp.com/"
opencollective: "https://opencollective.com/bootstrap" opencollective: "https://opencollective.com/bootstrap"

View File

@ -28,21 +28,15 @@ 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}`, const EVENT_CLOSED = `closed${EVENT_KEY}`
CLOSED: `closed${EVENT_KEY}`, const EVENT_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', const CLASSNAME_FADE = 'fade'
FADE: 'fade', const CLASSNAME_SHOW = 'show'
SHOW: 'show'
}
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
@ -93,20 +87,20 @@ class Alert {
let parent = getElementFromSelector(element) let parent = getElementFromSelector(element)
if (!parent) { if (!parent) {
parent = SelectorEngine.closest(element, `.${ClassName.ALERT}`) parent = SelectorEngine.closest(element, `.${CLASSNAME_ALERT}`)
} }
return parent return parent
} }
_triggerCloseEvent(element) { _triggerCloseEvent(element) {
return EventHandler.trigger(element, Event.CLOSE) return EventHandler.trigger(element, EVENT_CLOSE)
} }
_removeElement(element) { _removeElement(element) {
element.classList.remove(ClassName.SHOW) element.classList.remove(CLASSNAME_SHOW)
if (!element.classList.contains(ClassName.FADE)) { if (!element.classList.contains(CLASSNAME_FADE)) {
this._destroyElement(element) this._destroyElement(element)
return return
} }
@ -123,7 +117,7 @@ class Alert {
element.parentNode.removeChild(element) element.parentNode.removeChild(element)
} }
EventHandler.trigger(element, Event.CLOSED) EventHandler.trigger(element, EVENT_CLOSED)
} }
// Static // Static
@ -163,7 +157,7 @@ class Alert {
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
EventHandler EventHandler
.on(document, Event.CLICK_DATA_API, Selector.DISMISS, Alert.handleDismiss(new Alert())) .on(document, EVENT_CLICK_DATA_API, SELECTOR_DISMISS, Alert.handleDismiss(new Alert()))
const $ = getjQuery() const $ = getjQuery()

View File

@ -22,26 +22,20 @@ 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 CLASS_NAME_ACTIVE = 'active'
ACTIVE: 'active', const CLASS_NAME_BUTTON = 'btn'
BUTTON: 'btn', const CLASS_NAME_DISABLED = 'disabled'
DISABLED: 'disabled', const CLASS_NAME_FOCUS = 'focus'
FOCUS: 'focus'
}
const Selector = { const SELECTOR_DATA_TOGGLE_CARROT = '[data-toggle^="button"]'
DATA_TOGGLE_CARROT: '[data-toggle^="button"]', const SELECTOR_DATA_TOGGLE = '[data-toggle="buttons"]'
DATA_TOGGLE: '[data-toggle="buttons"]', const SELECTOR_INPUT = 'input:not([type="hidden"])'
INPUT: 'input:not([type="hidden"])', const SELECTOR_ACTIVE = '.active'
ACTIVE: '.active', const SELECTOR_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}`, const EVENT_FOCUS_DATA_API = `focus${EVENT_KEY}${DATA_API_KEY}`
FOCUS_DATA_API: `focus${EVENT_KEY}${DATA_API_KEY}`, const EVENT_BLUR_DATA_API = `blur${EVENT_KEY}${DATA_API_KEY}`
BLUR_DATA_API: `blur${EVENT_KEY}${DATA_API_KEY}`
}
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
@ -69,33 +63,33 @@ class Button {
const rootElement = SelectorEngine.closest( const rootElement = SelectorEngine.closest(
this._element, this._element,
Selector.DATA_TOGGLE SELECTOR_DATA_TOGGLE
) )
if (rootElement) { if (rootElement) {
const input = SelectorEngine.findOne(Selector.INPUT, this._element) const input = SelectorEngine.findOne(SELECTOR_INPUT, this._element)
if (input && input.type === 'radio') { if (input && input.type === 'radio') {
if (input.checked && if (input.checked &&
this._element.classList.contains(ClassName.ACTIVE)) { this._element.classList.contains(CLASS_NAME_ACTIVE)) {
triggerChangeEvent = false triggerChangeEvent = false
} else { } else {
const activeElement = SelectorEngine.findOne(Selector.ACTIVE, rootElement) const activeElement = SelectorEngine.findOne(SELECTOR_ACTIVE, rootElement)
if (activeElement) { if (activeElement) {
activeElement.classList.remove(ClassName.ACTIVE) activeElement.classList.remove(CLASS_NAME_ACTIVE)
} }
} }
if (triggerChangeEvent) { if (triggerChangeEvent) {
if (input.hasAttribute('disabled') || if (input.hasAttribute('disabled') ||
rootElement.hasAttribute('disabled') || rootElement.hasAttribute('disabled') ||
input.classList.contains(ClassName.DISABLED) || input.classList.contains(CLASS_NAME_DISABLED) ||
rootElement.classList.contains(ClassName.DISABLED)) { rootElement.classList.contains(CLASS_NAME_DISABLED)) {
return return
} }
input.checked = !this._element.classList.contains(ClassName.ACTIVE) input.checked = !this._element.classList.contains(CLASS_NAME_ACTIVE)
EventHandler.trigger(input, 'change') EventHandler.trigger(input, 'change')
} }
@ -106,11 +100,11 @@ class Button {
if (addAriaPressed) { if (addAriaPressed) {
this._element.setAttribute('aria-pressed', this._element.setAttribute('aria-pressed',
!this._element.classList.contains(ClassName.ACTIVE)) !this._element.classList.contains(CLASS_NAME_ACTIVE))
} }
if (triggerChangeEvent) { if (triggerChangeEvent) {
this._element.classList.toggle(ClassName.ACTIVE) this._element.classList.toggle(CLASS_NAME_ACTIVE)
} }
} }
@ -146,12 +140,12 @@ 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
if (!button.classList.contains(ClassName.BUTTON)) { if (!button.classList.contains(CLASS_NAME_BUTTON)) {
button = SelectorEngine.closest(button, Selector.BUTTON) button = SelectorEngine.closest(button, SELECTOR_BUTTON)
} }
let data = Data.getData(button, DATA_KEY) let data = Data.getData(button, DATA_KEY)
@ -162,19 +156,19 @@ EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE_CARROT, eve
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)
if (button) { if (button) {
button.classList.add(ClassName.FOCUS) button.classList.add(CLASS_NAME_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)
if (button) { if (button) {
button.classList.remove(ClassName.FOCUS) button.classList.remove(CLASS_NAME_FOCUS)
} }
}) })

View File

@ -33,6 +33,7 @@ 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
@ -56,51 +57,42 @@ const DefaultType = {
touch: 'boolean' touch: 'boolean'
} }
const Direction = { const DIRECTION_NEXT = 'next'
NEXT: 'next', const DIRECTION_PREV = 'prev'
PREV: 'prev', const DIRECTION_LEFT = 'left'
LEFT: 'left', const DIRECTION_RIGHT = 'right'
RIGHT: 'right'
}
const Event = { const EVENT_SLIDE = `slide${EVENT_KEY}`
SLIDE: `slide${EVENT_KEY}`, const EVENT_SLID = `slid${EVENT_KEY}`
SLID: `slid${EVENT_KEY}`, const EVENT_KEYDOWN = `keydown${EVENT_KEY}`
KEYDOWN: `keydown${EVENT_KEY}`, const EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}`
MOUSEENTER: `mouseenter${EVENT_KEY}`, const EVENT_MOUSELEAVE = `mouseleave${EVENT_KEY}`
MOUSELEAVE: `mouseleave${EVENT_KEY}`, const EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`
TOUCHSTART: `touchstart${EVENT_KEY}`, const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}`
TOUCHMOVE: `touchmove${EVENT_KEY}`, const EVENT_TOUCHEND = `touchend${EVENT_KEY}`
TOUCHEND: `touchend${EVENT_KEY}`, const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`
POINTERDOWN: `pointerdown${EVENT_KEY}`, const EVENT_POINTERUP = `pointerup${EVENT_KEY}`
POINTERUP: `pointerup${EVENT_KEY}`, const EVENT_DRAG_START = `dragstart${EVENT_KEY}`
DRAG_START: `dragstart${EVENT_KEY}`, const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`
LOAD_DATA_API: `load${EVENT_KEY}${DATA_API_KEY}`, const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
CLICK_DATA_API: `click${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = { const CLASS_NAME_CAROUSEL = 'carousel'
CAROUSEL: 'carousel', const CLASS_NAME_ACTIVE = 'active'
ACTIVE: 'active', const CLASS_NAME_SLIDE = 'slide'
SLIDE: 'slide', const CLASS_NAME_RIGHT = 'carousel-item-right'
RIGHT: 'carousel-item-right', const CLASS_NAME_LEFT = 'carousel-item-left'
LEFT: 'carousel-item-left', const CLASS_NAME_NEXT = 'carousel-item-next'
NEXT: 'carousel-item-next', const CLASS_NAME_PREV = 'carousel-item-prev'
PREV: 'carousel-item-prev', const CLASS_NAME_POINTER_EVENT = 'pointer-event'
ITEM: 'carousel-item',
POINTER_EVENT: 'pointer-event'
}
const Selector = { const SELECTOR_ACTIVE = '.active'
ACTIVE: '.active', const SELECTOR_ACTIVE_ITEM = '.active.carousel-item'
ACTIVE_ITEM: '.active.carousel-item', const SELECTOR_ITEM = '.carousel-item'
ITEM: '.carousel-item', const SELECTOR_ITEM_IMG = '.carousel-item img'
ITEM_IMG: '.carousel-item img', const SELECTOR_NEXT_PREV = '.carousel-item-next, .carousel-item-prev'
NEXT_PREV: '.carousel-item-next, .carousel-item-prev', const SELECTOR_INDICATORS = '.carousel-indicators'
INDICATORS: '.carousel-indicators', const SELECTOR_DATA_SLIDE = '[data-slide], [data-slide-to]'
DATA_SLIDE: '[data-slide], [data-slide-to]', const SELECTOR_DATA_RIDE = '[data-ride="carousel"]'
DATA_RIDE: '[data-ride="carousel"]'
}
const PointerType = { const PointerType = {
TOUCH: 'touch', TOUCH: 'touch',
@ -125,7 +117,7 @@ class Carousel {
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)
@ -147,7 +139,7 @@ class Carousel {
next() { next() {
if (!this._isSliding) { if (!this._isSliding) {
this._slide(Direction.NEXT) this._slide(DIRECTION_NEXT)
} }
} }
@ -161,7 +153,7 @@ class Carousel {
prev() { prev() {
if (!this._isSliding) { if (!this._isSliding) {
this._slide(Direction.PREV) this._slide(DIRECTION_PREV)
} }
} }
@ -170,7 +162,7 @@ class Carousel {
this._isPaused = true this._isPaused = true
} }
if (SelectorEngine.findOne(Selector.NEXT_PREV, this._element)) { if (SelectorEngine.findOne(SELECTOR_NEXT_PREV, this._element)) {
triggerTransitionEnd(this._element) triggerTransitionEnd(this._element)
this.cycle(true) this.cycle(true)
} }
@ -198,7 +190,7 @@ class Carousel {
} }
to(index) { to(index) {
this._activeElement = SelectorEngine.findOne(Selector.ACTIVE_ITEM, this._element) this._activeElement = SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)
const activeIndex = this._getItemIndex(this._activeElement) const activeIndex = this._getItemIndex(this._activeElement)
if (index > this._items.length - 1 || index < 0) { if (index > this._items.length - 1 || index < 0) {
@ -206,7 +198,7 @@ class Carousel {
} }
if (this._isSliding) { if (this._isSliding) {
EventHandler.one(this._element, Event.SLID, () => this.to(index)) EventHandler.one(this._element, EVENT_SLID, () => this.to(index))
return return
} }
@ -217,8 +209,8 @@ class Carousel {
} }
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])
} }
@ -273,14 +265,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 && this._touchSupported) { if (this._config.touch && this._touchSupported) {
@ -330,19 +322,19 @@ class Carousel {
} }
} }
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(CLASS_NAME_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,15 +358,15 @@ 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)
} }
_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) ||
@ -384,7 +376,7 @@ class Carousel {
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 ?
@ -394,9 +386,9 @@ class Carousel {
_triggerSlideEvent(relatedTarget, eventDirectionName) { _triggerSlideEvent(relatedTarget, eventDirectionName) {
const targetIndex = this._getItemIndex(relatedTarget) const targetIndex = this._getItemIndex(relatedTarget)
const fromIndex = this._getItemIndex(SelectorEngine.findOne(Selector.ACTIVE_ITEM, this._element)) const fromIndex = this._getItemIndex(SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element))
return EventHandler.trigger(this._element, Event.SLIDE, { return EventHandler.trigger(this._element, EVENT_SLIDE, {
relatedTarget, relatedTarget,
direction: eventDirectionName, direction: eventDirectionName,
from: fromIndex, from: fromIndex,
@ -406,9 +398,9 @@ class Carousel {
_setActiveIndicatorElement(element) { _setActiveIndicatorElement(element) {
if (this._indicatorsElement) { if (this._indicatorsElement) {
const indicators = SelectorEngine.find(Selector.ACTIVE, this._indicatorsElement) const indicators = SelectorEngine.find(SELECTOR_ACTIVE, this._indicatorsElement)
for (let i = 0; i < indicators.length; i++) { for (let i = 0; i < indicators.length; i++) {
indicators[i].classList.remove(ClassName.ACTIVE) indicators[i].classList.remove(CLASS_NAME_ACTIVE)
} }
const nextIndicator = this._indicatorsElement.children[ const nextIndicator = this._indicatorsElement.children[
@ -416,13 +408,13 @@ class Carousel {
] ]
if (nextIndicator) { if (nextIndicator) {
nextIndicator.classList.add(ClassName.ACTIVE) nextIndicator.classList.add(CLASS_NAME_ACTIVE)
} }
} }
} }
_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))
@ -434,17 +426,17 @@ class Carousel {
let orderClassName let orderClassName
let eventDirectionName let eventDirectionName
if (direction === Direction.NEXT) { if (direction === DIRECTION_NEXT) {
directionalClassName = ClassName.LEFT directionalClassName = CLASS_NAME_LEFT
orderClassName = ClassName.NEXT orderClassName = CLASS_NAME_NEXT
eventDirectionName = Direction.LEFT eventDirectionName = DIRECTION_LEFT
} else { } else {
directionalClassName = ClassName.RIGHT directionalClassName = CLASS_NAME_RIGHT
orderClassName = ClassName.PREV orderClassName = CLASS_NAME_PREV
eventDirectionName = Direction.RIGHT eventDirectionName = DIRECTION_RIGHT
} }
if (nextElement && nextElement.classList.contains(ClassName.ACTIVE)) { if (nextElement && nextElement.classList.contains(CLASS_NAME_ACTIVE)) {
this._isSliding = false this._isSliding = false
return return
} }
@ -467,7 +459,7 @@ class Carousel {
this._setActiveIndicatorElement(nextElement) this._setActiveIndicatorElement(nextElement)
if (this._element.classList.contains(ClassName.SLIDE)) { if (this._element.classList.contains(CLASS_NAME_SLIDE)) {
nextElement.classList.add(orderClassName) nextElement.classList.add(orderClassName)
reflow(nextElement) reflow(nextElement)
@ -489,16 +481,16 @@ class Carousel {
.one(activeElement, TRANSITION_END, () => { .one(activeElement, TRANSITION_END, () => {
nextElement.classList.remove(directionalClassName) nextElement.classList.remove(directionalClassName)
nextElement.classList.remove(orderClassName) nextElement.classList.remove(orderClassName)
nextElement.classList.add(ClassName.ACTIVE) nextElement.classList.add(CLASS_NAME_ACTIVE)
activeElement.classList.remove(ClassName.ACTIVE) activeElement.classList.remove(CLASS_NAME_ACTIVE)
activeElement.classList.remove(orderClassName) activeElement.classList.remove(orderClassName)
activeElement.classList.remove(directionalClassName) activeElement.classList.remove(directionalClassName)
this._isSliding = false this._isSliding = false
setTimeout(() => { setTimeout(() => {
EventHandler.trigger(this._element, Event.SLID, { EventHandler.trigger(this._element, EVENT_SLID, {
relatedTarget: nextElement, relatedTarget: nextElement,
direction: eventDirectionName, direction: eventDirectionName,
from: activeElementIndex, from: activeElementIndex,
@ -509,11 +501,11 @@ class Carousel {
emulateTransitionEnd(activeElement, transitionDuration) emulateTransitionEnd(activeElement, transitionDuration)
} else { } else {
activeElement.classList.remove(ClassName.ACTIVE) activeElement.classList.remove(CLASS_NAME_ACTIVE)
nextElement.classList.add(ClassName.ACTIVE) nextElement.classList.add(CLASS_NAME_ACTIVE)
this._isSliding = false this._isSliding = false
EventHandler.trigger(this._element, Event.SLID, { EventHandler.trigger(this._element, EVENT_SLID, {
relatedTarget: nextElement, relatedTarget: nextElement,
direction: eventDirectionName, direction: eventDirectionName,
from: activeElementIndex, from: activeElementIndex,
@ -571,7 +563,7 @@ class Carousel {
static dataApiClickHandler(event) { static dataApiClickHandler(event) {
const target = getElementFromSelector(this) const target = getElementFromSelector(this)
if (!target || !target.classList.contains(ClassName.CAROUSEL)) { if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {
return return
} }
@ -606,10 +598,10 @@ class Carousel {
*/ */
EventHandler EventHandler
.on(document, Event.CLICK_DATA_API, Selector.DATA_SLIDE, Carousel.dataApiClickHandler) .on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, Carousel.dataApiClickHandler)
EventHandler.on(window, Event.LOAD_DATA_API, () => { EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
const carousels = makeArray(SelectorEngine.find(Selector.DATA_RIDE)) const carousels = makeArray(SelectorEngine.find(SELECTOR_DATA_RIDE))
for (let i = 0, len = carousels.length; i < len; i++) { for (let i = 0, len = carousels.length; i < len; i++) {
Carousel.carouselInterface(carousels[i], Data.getData(carousels[i], DATA_KEY)) Carousel.carouselInterface(carousels[i], Data.getData(carousels[i], DATA_KEY))
} }

View File

@ -44,30 +44,22 @@ const DefaultType = {
parent: '(string|element)' parent: '(string|element)'
} }
const Event = { const EVENT_SHOW = `show${EVENT_KEY}`
SHOW: `show${EVENT_KEY}`, const EVENT_SHOWN = `shown${EVENT_KEY}`
SHOWN: `shown${EVENT_KEY}`, const EVENT_HIDE = `hide${EVENT_KEY}`
HIDE: `hide${EVENT_KEY}`, const EVENT_HIDDEN = `hidden${EVENT_KEY}`
HIDDEN: `hidden${EVENT_KEY}`, const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
CLICK_DATA_API: `click${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = { const CLASS_NAME_SHOW = 'show'
SHOW: 'show', const CLASS_NAME_COLLAPSE = 'collapse'
COLLAPSE: 'collapse', const CLASS_NAME_COLLAPSING = 'collapsing'
COLLAPSING: 'collapsing', const CLASS_NAME_COLLAPSED = 'collapsed'
COLLAPSED: 'collapsed'
}
const Dimension = { const WIDTH = 'width'
WIDTH: 'width', const HEIGHT = 'height'
HEIGHT: 'height'
}
const Selector = { const SELECTOR_ACTIVES = '.show, .collapsing'
ACTIVES: '.show, .collapsing', const SELECTOR_DATA_TOGGLE = '[data-toggle="collapse"]'
DATA_TOGGLE: '[data-toggle="collapse"]'
}
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
@ -81,11 +73,11 @@ class Collapse {
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(
`${Selector.DATA_TOGGLE}[href="#${element.id}"],` + `${SELECTOR_DATA_TOGGLE}[href="#${element.id}"],` +
`${Selector.DATA_TOGGLE}[data-target="#${element.id}"]` `${SELECTOR_DATA_TOGGLE}[data-target="#${element.id}"]`
)) ))
const toggleList = makeArray(SelectorEngine.find(Selector.DATA_TOGGLE)) const toggleList = makeArray(SelectorEngine.find(SELECTOR_DATA_TOGGLE))
for (let i = 0, len = toggleList.length; i < len; i++) { for (let i = 0, len = toggleList.length; i < len; i++) {
const elem = toggleList[i] const elem = toggleList[i]
const selector = getSelectorFromElement(elem) const selector = getSelectorFromElement(elem)
@ -124,7 +116,7 @@ class Collapse {
// Public // Public
toggle() { toggle() {
if (this._element.classList.contains(ClassName.SHOW)) { if (this._element.classList.contains(CLASS_NAME_SHOW)) {
this.hide() this.hide()
} else { } else {
this.show() this.show()
@ -133,7 +125,7 @@ class Collapse {
show() { show() {
if (this._isTransitioning || if (this._isTransitioning ||
this._element.classList.contains(ClassName.SHOW)) { this._element.classList.contains(CLASS_NAME_SHOW)) {
return return
} }
@ -141,13 +133,13 @@ class Collapse {
let activesData let activesData
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
} }
return elem.classList.contains(ClassName.COLLAPSE) return elem.classList.contains(CLASS_NAME_COLLAPSE)
}) })
if (actives.length === 0) { if (actives.length === 0) {
@ -165,7 +157,7 @@ class Collapse {
} }
} }
const startEvent = EventHandler.trigger(this._element, Event.SHOW) const startEvent = EventHandler.trigger(this._element, EVENT_SHOW)
if (startEvent.defaultPrevented) { if (startEvent.defaultPrevented) {
return return
} }
@ -184,14 +176,14 @@ class Collapse {
const dimension = this._getDimension() const dimension = this._getDimension()
this._element.classList.remove(ClassName.COLLAPSE) this._element.classList.remove(CLASS_NAME_COLLAPSE)
this._element.classList.add(ClassName.COLLAPSING) this._element.classList.add(CLASS_NAME_COLLAPSING)
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(CLASS_NAME_COLLAPSED)
element.setAttribute('aria-expanded', true) element.setAttribute('aria-expanded', true)
}) })
} }
@ -199,15 +191,15 @@ class Collapse {
this.setTransitioning(true) this.setTransitioning(true)
const complete = () => { const complete = () => {
this._element.classList.remove(ClassName.COLLAPSING) this._element.classList.remove(CLASS_NAME_COLLAPSING)
this._element.classList.add(ClassName.COLLAPSE) this._element.classList.add(CLASS_NAME_COLLAPSE)
this._element.classList.add(ClassName.SHOW) this._element.classList.add(CLASS_NAME_SHOW)
this._element.style[dimension] = '' this._element.style[dimension] = ''
this.setTransitioning(false) this.setTransitioning(false)
EventHandler.trigger(this._element, Event.SHOWN) EventHandler.trigger(this._element, EVENT_SHOWN)
} }
const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1) const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1)
@ -222,11 +214,11 @@ class Collapse {
hide() { hide() {
if (this._isTransitioning || if (this._isTransitioning ||
!this._element.classList.contains(ClassName.SHOW)) { !this._element.classList.contains(CLASS_NAME_SHOW)) {
return return
} }
const startEvent = EventHandler.trigger(this._element, Event.HIDE) const startEvent = EventHandler.trigger(this._element, EVENT_HIDE)
if (startEvent.defaultPrevented) { if (startEvent.defaultPrevented) {
return return
} }
@ -237,9 +229,9 @@ class Collapse {
reflow(this._element) reflow(this._element)
this._element.classList.add(ClassName.COLLAPSING) this._element.classList.add(CLASS_NAME_COLLAPSING)
this._element.classList.remove(ClassName.COLLAPSE) this._element.classList.remove(CLASS_NAME_COLLAPSE)
this._element.classList.remove(ClassName.SHOW) this._element.classList.remove(CLASS_NAME_SHOW)
const triggerArrayLength = this._triggerArray.length const triggerArrayLength = this._triggerArray.length
if (triggerArrayLength > 0) { if (triggerArrayLength > 0) {
@ -247,8 +239,8 @@ class Collapse {
const trigger = this._triggerArray[i] const trigger = this._triggerArray[i]
const elem = getElementFromSelector(trigger) const elem = getElementFromSelector(trigger)
if (elem && !elem.classList.contains(ClassName.SHOW)) { if (elem && !elem.classList.contains(CLASS_NAME_SHOW)) {
trigger.classList.add(ClassName.COLLAPSED) trigger.classList.add(CLASS_NAME_COLLAPSED)
trigger.setAttribute('aria-expanded', false) trigger.setAttribute('aria-expanded', false)
} }
} }
@ -258,9 +250,9 @@ class Collapse {
const complete = () => { const complete = () => {
this.setTransitioning(false) this.setTransitioning(false)
this._element.classList.remove(ClassName.COLLAPSING) this._element.classList.remove(CLASS_NAME_COLLAPSING)
this._element.classList.add(ClassName.COLLAPSE) this._element.classList.add(CLASS_NAME_COLLAPSE)
EventHandler.trigger(this._element, Event.HIDDEN) EventHandler.trigger(this._element, EVENT_HIDDEN)
} }
this._element.style[dimension] = '' this._element.style[dimension] = ''
@ -297,8 +289,8 @@ class Collapse {
} }
_getDimension() { _getDimension() {
const hasWidth = this._element.classList.contains(Dimension.WIDTH) const hasWidth = this._element.classList.contains(WIDTH)
return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT return hasWidth ? WIDTH : HEIGHT
} }
_getParent() { _getParent() {
@ -313,7 +305,7 @@ class Collapse {
parent = SelectorEngine.findOne(parent) parent = SelectorEngine.findOne(parent)
} }
const selector = `${Selector.DATA_TOGGLE}[data-parent="${parent}"]` const selector = `${SELECTOR_DATA_TOGGLE}[data-parent="${parent}"]`
makeArray(SelectorEngine.find(selector, parent)) makeArray(SelectorEngine.find(selector, parent))
.forEach(element => { .forEach(element => {
@ -330,14 +322,14 @@ class Collapse {
_addAriaAndCollapsedClass(element, triggerArray) { _addAriaAndCollapsedClass(element, triggerArray) {
if (element) { if (element) {
const isOpen = element.classList.contains(ClassName.SHOW) const isOpen = element.classList.contains(CLASS_NAME_SHOW)
if (triggerArray.length) { if (triggerArray.length) {
triggerArray.forEach(elem => { triggerArray.forEach(elem => {
if (isOpen) { if (isOpen) {
elem.classList.remove(ClassName.COLLAPSED) elem.classList.remove(CLASS_NAME_COLLAPSED)
} else { } else {
elem.classList.add(ClassName.COLLAPSED) elem.classList.add(CLASS_NAME_COLLAPSED)
} }
elem.setAttribute('aria-expanded', isOpen) elem.setAttribute('aria-expanded', isOpen)
@ -390,7 +382,7 @@ class Collapse {
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
// preventDefault only for <a> elements (which change the URL) not inside the collapsible element // preventDefault only for <a> elements (which change the URL) not inside the collapsible element
if (event.target.tagName === 'A') { if (event.target.tagName === 'A') {
event.preventDefault() event.preventDefault()

View File

@ -6,7 +6,7 @@
*/ */
import { getjQuery } from '../util/index' import { getjQuery } from '../util/index'
import { createCustomEvent, defaultPreventedPreservedOnDispatch } from './polyfill' import { defaultPreventedPreservedOnDispatch } from './polyfill'
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
@ -307,7 +307,7 @@ const EventHandler = {
evt = document.createEvent('HTMLEvents') evt = document.createEvent('HTMLEvents')
evt.initEvent(typeEvent, bubbles, true) evt.initEvent(typeEvent, bubbles, true)
} else { } else {
evt = createCustomEvent(event, { evt = new CustomEvent(event, {
bubbles, bubbles,
cancelable: true cancelable: true
}) })

View File

@ -9,55 +9,12 @@
import { getUID } from '../util/index' import { getUID } from '../util/index'
let { matches, closest } = Element.prototype
let find = Element.prototype.querySelectorAll let find = Element.prototype.querySelectorAll
let findOne = Element.prototype.querySelector let findOne = Element.prototype.querySelector
let createCustomEvent = (eventName, params) => {
const cEvent = new CustomEvent(eventName, params)
return cEvent
}
if (typeof window.CustomEvent !== 'function') {
createCustomEvent = (eventName, params) => {
params = params || { bubbles: false, cancelable: false, detail: null }
const evt = document.createEvent('CustomEvent')
evt.initCustomEvent(eventName, params.bubbles, params.cancelable, params.detail)
return evt
}
}
const workingDefaultPrevented = (() => {
const e = document.createEvent('CustomEvent')
e.initEvent('Bootstrap', true, true)
e.preventDefault()
return e.defaultPrevented
})()
if (!workingDefaultPrevented) {
const origPreventDefault = Event.prototype.preventDefault
Event.prototype.preventDefault = function () {
if (!this.cancelable) {
return
}
origPreventDefault.call(this)
Object.defineProperty(this, 'defaultPrevented', {
get() {
return true
},
configurable: true
})
}
}
// MSEdge resets defaultPrevented flag upon dispatchEvent call if at least one listener is attached // MSEdge resets defaultPrevented flag upon dispatchEvent call if at least one listener is attached
const defaultPreventedPreservedOnDispatch = (() => { const defaultPreventedPreservedOnDispatch = (() => {
const e = createCustomEvent('Bootstrap', { const e = new CustomEvent('Bootstrap', {
cancelable: true cancelable: true
}) })
@ -69,26 +26,6 @@ const defaultPreventedPreservedOnDispatch = (() => {
return e.defaultPrevented return e.defaultPrevented
})() })()
if (!matches) {
matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector
}
if (!closest) {
closest = function (selector) {
let element = this
do {
if (matches.call(element, selector)) {
return element
}
element = element.parentElement || element.parentNode
} while (element !== null && element.nodeType === 1)
return null
}
}
const scopeSelectorRegex = /:scope\b/ const scopeSelectorRegex = /:scope\b/
const supportScopeQuery = (() => { const supportScopeQuery = (() => {
const element = document.createElement('div') const element = document.createElement('div')
@ -143,10 +80,7 @@ if (!supportScopeQuery) {
} }
export { export {
createCustomEvent,
find, find,
findOne, findOne,
matches,
closest,
defaultPreventedPreservedOnDispatch defaultPreventedPreservedOnDispatch
} }

View File

@ -5,7 +5,7 @@
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
*/ */
import { find as findFn, findOne, matches, closest } from './polyfill' import { find as findFn, findOne } from './polyfill'
import { makeArray } from '../util/index' import { makeArray } from '../util/index'
/** /**
@ -18,7 +18,7 @@ const NODE_TEXT = 3
const SelectorEngine = { const SelectorEngine = {
matches(element, selector) { matches(element, selector) {
return matches.call(element, selector) return element.matches(selector)
}, },
find(selector, element = document.documentElement) { find(selector, element = document.documentElement) {
@ -52,7 +52,7 @@ const SelectorEngine = {
}, },
closest(element, selector) { closest(element, selector) {
return closest.call(element, selector) return element.closest(selector)
}, },
prev(element, selector) { prev(element, selector) {

View File

@ -31,54 +31,46 @@ 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}`, const EVENT_HIDDEN = `hidden${EVENT_KEY}`
HIDDEN: `hidden${EVENT_KEY}`, const EVENT_SHOW = `show${EVENT_KEY}`
SHOW: `show${EVENT_KEY}`, const EVENT_SHOWN = `shown${EVENT_KEY}`
SHOWN: `shown${EVENT_KEY}`, const EVENT_CLICK = `click${EVENT_KEY}`
CLICK: `click${EVENT_KEY}`, const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
CLICK_DATA_API: `click${EVENT_KEY}${DATA_API_KEY}`, const EVENT_KEYDOWN_DATA_API = `keydown${EVENT_KEY}${DATA_API_KEY}`
KEYDOWN_DATA_API: `keydown${EVENT_KEY}${DATA_API_KEY}`, const EVENT_KEYUP_DATA_API = `keyup${EVENT_KEY}${DATA_API_KEY}`
KEYUP_DATA_API: `keyup${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = { const CLASS_NAME_DISABLED = 'disabled'
DISABLED: 'disabled', const CLASS_NAME_SHOW = 'show'
SHOW: 'show', const CLASS_NAME_DROPUP = 'dropup'
DROPUP: 'dropup', const CLASS_NAME_DROPRIGHT = 'dropright'
DROPRIGHT: 'dropright', const CLASS_NAME_DROPLEFT = 'dropleft'
DROPLEFT: 'dropleft', const CLASS_NAME_MENURIGHT = 'dropdown-menu-right'
MENURIGHT: 'dropdown-menu-right', const CLASS_NAME_NAVBAR = 'navbar'
NAVBAR: 'navbar', const CLASS_NAME_POSITION_STATIC = 'position-static'
POSITION_STATIC: 'position-static'
}
const Selector = { const SELECTOR_DATA_TOGGLE = '[data-toggle="dropdown"]'
DATA_TOGGLE: '[data-toggle="dropdown"]', const SELECTOR_FORM_CHILD = '.dropdown form'
FORM_CHILD: '.dropdown form', const SELECTOR_MENU = '.dropdown-menu'
MENU: '.dropdown-menu', const SELECTOR_NAVBAR_NAV = '.navbar-nav'
NAVBAR_NAV: '.navbar-nav', const SELECTOR_VISIBLE_ITEMS = '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'
VISIBLE_ITEMS: '.dropdown-menu .dropdown-item:not(.disabled):not(:disabled)'
}
const AttachmentMap = { const PLACEMENT_TOP = 'top-start'
TOP: 'top-start', const PLACEMENT_TOPEND = 'top-end'
TOPEND: 'top-end', const PLACEMENT_BOTTOM = 'bottom-start'
BOTTOM: 'bottom-start', const PLACEMENT_BOTTOMEND = 'bottom-end'
BOTTOMEND: 'bottom-end', const PLACEMENT_RIGHT = 'right-start'
RIGHT: 'right-start', const PLACEMENT_LEFT = 'left-start'
RIGHTEND: 'right-end',
LEFT: 'left-start',
LEFTEND: 'left-end'
}
const Default = { const Default = {
offset: 0, offset: 0,
@ -133,11 +125,11 @@ class Dropdown {
// Public // Public
toggle() { toggle() {
if (this._element.disabled || this._element.classList.contains(ClassName.DISABLED)) { if (this._element.disabled || this._element.classList.contains(CLASS_NAME_DISABLED)) {
return return
} }
const isActive = this._menu.classList.contains(ClassName.SHOW) const isActive = this._menu.classList.contains(CLASS_NAME_SHOW)
Dropdown.clearMenus() Dropdown.clearMenus()
@ -149,7 +141,7 @@ class Dropdown {
} }
show() { show() {
if (this._element.disabled || this._element.classList.contains(ClassName.DISABLED) || this._menu.classList.contains(ClassName.SHOW)) { if (this._element.disabled || this._element.classList.contains(CLASS_NAME_DISABLED) || this._menu.classList.contains(CLASS_NAME_SHOW)) {
return return
} }
@ -158,7 +150,7 @@ class Dropdown {
relatedTarget: this._element relatedTarget: this._element
} }
const showEvent = EventHandler.trigger(parent, Event.SHOW, relatedTarget) const showEvent = EventHandler.trigger(parent, EVENT_SHOW, relatedTarget)
if (showEvent.defaultPrevented) { if (showEvent.defaultPrevented) {
return return
@ -187,7 +179,7 @@ class Dropdown {
// to allow the menu to "escape" the scroll parent's boundaries // to allow the menu to "escape" the scroll parent's boundaries
// https://github.com/twbs/bootstrap/issues/24251 // https://github.com/twbs/bootstrap/issues/24251
if (this._config.boundary !== 'scrollParent') { if (this._config.boundary !== 'scrollParent') {
parent.classList.add(ClassName.POSITION_STATIC) parent.classList.add(CLASS_NAME_POSITION_STATIC)
} }
this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig()) this._popper = new Popper(referenceElement, this._menu, this._getPopperConfig())
@ -198,7 +190,7 @@ class Dropdown {
// 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(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()))
} }
@ -206,13 +198,13 @@ class Dropdown {
this._element.focus() this._element.focus()
this._element.setAttribute('aria-expanded', true) this._element.setAttribute('aria-expanded', true)
Manipulator.toggleClass(this._menu, ClassName.SHOW) Manipulator.toggleClass(this._menu, CLASS_NAME_SHOW)
Manipulator.toggleClass(parent, ClassName.SHOW) Manipulator.toggleClass(parent, CLASS_NAME_SHOW)
EventHandler.trigger(parent, Event.SHOWN, relatedTarget) EventHandler.trigger(parent, EVENT_SHOWN, relatedTarget)
} }
hide() { hide() {
if (this._element.disabled || this._element.classList.contains(ClassName.DISABLED) || !this._menu.classList.contains(ClassName.SHOW)) { if (this._element.disabled || this._element.classList.contains(CLASS_NAME_DISABLED) || !this._menu.classList.contains(CLASS_NAME_SHOW)) {
return return
} }
@ -221,7 +213,7 @@ class Dropdown {
relatedTarget: this._element relatedTarget: this._element
} }
const hideEvent = EventHandler.trigger(parent, Event.HIDE, relatedTarget) const hideEvent = EventHandler.trigger(parent, EVENT_HIDE, relatedTarget)
if (hideEvent.defaultPrevented) { if (hideEvent.defaultPrevented) {
return return
@ -231,9 +223,9 @@ class Dropdown {
this._popper.destroy() this._popper.destroy()
} }
Manipulator.toggleClass(this._menu, ClassName.SHOW) Manipulator.toggleClass(this._menu, CLASS_NAME_SHOW)
Manipulator.toggleClass(parent, ClassName.SHOW) Manipulator.toggleClass(parent, CLASS_NAME_SHOW)
EventHandler.trigger(parent, Event.HIDDEN, relatedTarget) EventHandler.trigger(parent, EVENT_HIDDEN, relatedTarget)
} }
dispose() { dispose() {
@ -257,7 +249,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()
@ -283,32 +275,32 @@ class Dropdown {
_getMenuElement() { _getMenuElement() {
const parent = Dropdown.getParentFromElement(this._element) const parent = Dropdown.getParentFromElement(this._element)
return SelectorEngine.findOne(Selector.MENU, parent) return SelectorEngine.findOne(SELECTOR_MENU, parent)
} }
_getPlacement() { _getPlacement() {
const parentDropdown = this._element.parentNode const parentDropdown = this._element.parentNode
let placement = AttachmentMap.BOTTOM let placement = PLACEMENT_BOTTOM
// Handle dropup // Handle dropup
if (parentDropdown.classList.contains(ClassName.DROPUP)) { if (parentDropdown.classList.contains(CLASS_NAME_DROPUP)) {
placement = AttachmentMap.TOP placement = PLACEMENT_TOP
if (this._menu.classList.contains(ClassName.MENURIGHT)) { if (this._menu.classList.contains(CLASS_NAME_MENURIGHT)) {
placement = AttachmentMap.TOPEND placement = PLACEMENT_TOPEND
} }
} else if (parentDropdown.classList.contains(ClassName.DROPRIGHT)) { } else if (parentDropdown.classList.contains(CLASS_NAME_DROPRIGHT)) {
placement = AttachmentMap.RIGHT placement = PLACEMENT_RIGHT
} else if (parentDropdown.classList.contains(ClassName.DROPLEFT)) { } else if (parentDropdown.classList.contains(CLASS_NAME_DROPLEFT)) {
placement = AttachmentMap.LEFT placement = PLACEMENT_LEFT
} else if (this._menu.classList.contains(ClassName.MENURIGHT)) { } else if (this._menu.classList.contains(CLASS_NAME_MENURIGHT)) {
placement = AttachmentMap.BOTTOMEND placement = PLACEMENT_BOTTOMEND
} }
return placement return placement
} }
_detectNavbar() { _detectNavbar() {
return Boolean(SelectorEngine.closest(this._element, `.${ClassName.NAVBAR}`)) return Boolean(SelectorEngine.closest(this._element, `.${CLASS_NAME_NAVBAR}`))
} }
_getOffset() { _getOffset() {
@ -388,7 +380,7 @@ class Dropdown {
return return
} }
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)
@ -405,7 +397,7 @@ class Dropdown {
} }
const dropdownMenu = context._menu const dropdownMenu = context._menu
if (!parent.classList.contains(ClassName.SHOW)) { if (!parent.classList.contains(CLASS_NAME_SHOW)) {
continue continue
} }
@ -416,7 +408,7 @@ class Dropdown {
continue continue
} }
const hideEvent = EventHandler.trigger(parent, Event.HIDE, relatedTarget) const hideEvent = EventHandler.trigger(parent, EVENT_HIDE, relatedTarget)
if (hideEvent.defaultPrevented) { if (hideEvent.defaultPrevented) {
continue continue
} }
@ -434,9 +426,9 @@ class Dropdown {
context._popper.destroy() context._popper.destroy()
} }
dropdownMenu.classList.remove(ClassName.SHOW) dropdownMenu.classList.remove(CLASS_NAME_SHOW)
parent.classList.remove(ClassName.SHOW) parent.classList.remove(CLASS_NAME_SHOW)
EventHandler.trigger(parent, Event.HIDDEN, relatedTarget) EventHandler.trigger(parent, EVENT_HIDDEN, relatedTarget)
} }
} }
@ -455,7 +447,7 @@ class Dropdown {
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))) : SelectorEngine.closest(event.target, SELECTOR_MENU))) :
!REGEXP_KEYDOWN.test(event.which)) { !REGEXP_KEYDOWN.test(event.which)) {
return return
} }
@ -463,23 +455,23 @@ class Dropdown {
event.preventDefault() event.preventDefault()
event.stopPropagation() event.stopPropagation()
if (this.disabled || this.classList.contains(ClassName.DISABLED)) { if (this.disabled || this.classList.contains(CLASS_NAME_DISABLED)) {
return return
} }
const parent = Dropdown.getParentFromElement(this) const parent = Dropdown.getParentFromElement(this)
const isActive = parent.classList.contains(ClassName.SHOW) const isActive = parent.classList.contains(CLASS_NAME_SHOW)
if (!isActive || (isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE))) { if (!isActive || (isActive && (event.which === ESCAPE_KEYCODE || event.which === SPACE_KEYCODE))) {
if (event.which === ESCAPE_KEYCODE) { if (event.which === ESCAPE_KEYCODE) {
SelectorEngine.findOne(Selector.DATA_TOGGLE, parent).focus() SelectorEngine.findOne(SELECTOR_DATA_TOGGLE, parent).focus()
} }
Dropdown.clearMenus() Dropdown.clearMenus()
return return
} }
const items = makeArray(SelectorEngine.find(Selector.VISIBLE_ITEMS, parent)) const items = makeArray(SelectorEngine.find(SELECTOR_VISIBLE_ITEMS, parent))
.filter(isVisible) .filter(isVisible)
if (!items.length) { if (!items.length) {
@ -514,17 +506,17 @@ class Dropdown {
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
EventHandler.on(document, Event.KEYDOWN_DATA_API, Selector.DATA_TOGGLE, Dropdown.dataApiKeydownHandler) EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_DATA_TOGGLE, Dropdown.dataApiKeydownHandler)
EventHandler.on(document, Event.KEYDOWN_DATA_API, Selector.MENU, Dropdown.dataApiKeydownHandler) EventHandler.on(document, EVENT_KEYDOWN_DATA_API, SELECTOR_MENU, Dropdown.dataApiKeydownHandler)
EventHandler.on(document, Event.CLICK_DATA_API, Dropdown.clearMenus) EventHandler.on(document, EVENT_CLICK_DATA_API, Dropdown.clearMenus)
EventHandler.on(document, Event.KEYUP_DATA_API, Dropdown.clearMenus) EventHandler.on(document, EVENT_KEYUP_DATA_API, Dropdown.clearMenus)
EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
event.preventDefault() event.preventDefault()
event.stopPropagation() event.stopPropagation()
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())
const $ = getjQuery() const $ = getjQuery()

View File

@ -48,39 +48,33 @@ const DefaultType = {
show: 'boolean' show: 'boolean'
} }
const Event = { const EVENT_HIDE = `hide${EVENT_KEY}`
HIDE: `hide${EVENT_KEY}`, const EVENT_HIDE_PREVENTED = `hidePrevented${EVENT_KEY}`
HIDE_PREVENTED: `hidePrevented${EVENT_KEY}`, const EVENT_HIDDEN = `hidden${EVENT_KEY}`
HIDDEN: `hidden${EVENT_KEY}`, const EVENT_SHOW = `show${EVENT_KEY}`
SHOW: `show${EVENT_KEY}`, const EVENT_SHOWN = `shown${EVENT_KEY}`
SHOWN: `shown${EVENT_KEY}`, const EVENT_FOCUSIN = `focusin${EVENT_KEY}`
FOCUSIN: `focusin${EVENT_KEY}`, const EVENT_RESIZE = `resize${EVENT_KEY}`
RESIZE: `resize${EVENT_KEY}`, const EVENT_CLICK_DISMISS = `click.dismiss${EVENT_KEY}`
CLICK_DISMISS: `click.dismiss${EVENT_KEY}`, const EVENT_KEYDOWN_DISMISS = `keydown.dismiss${EVENT_KEY}`
KEYDOWN_DISMISS: `keydown.dismiss${EVENT_KEY}`, const EVENT_MOUSEUP_DISMISS = `mouseup.dismiss${EVENT_KEY}`
MOUSEUP_DISMISS: `mouseup.dismiss${EVENT_KEY}`, const EVENT_MOUSEDOWN_DISMISS = `mousedown.dismiss${EVENT_KEY}`
MOUSEDOWN_DISMISS: `mousedown.dismiss${EVENT_KEY}`, const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
CLICK_DATA_API: `click${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = { const CLASS_NAME_SCROLLABLE = 'modal-dialog-scrollable'
SCROLLABLE: 'modal-dialog-scrollable', const CLASS_NAME_SCROLLBAR_MEASURER = 'modal-scrollbar-measure'
SCROLLBAR_MEASURER: 'modal-scrollbar-measure', const CLASS_NAME_BACKDROP = 'modal-backdrop'
BACKDROP: 'modal-backdrop', const CLASS_NAME_OPEN = 'modal-open'
OPEN: 'modal-open', const CLASS_NAME_FADE = 'fade'
FADE: 'fade', const CLASS_NAME_SHOW = 'show'
SHOW: 'show', const CLASS_NAME_STATIC = 'modal-static'
STATIC: 'modal-static'
}
const Selector = { const SELECTOR_DIALOG = '.modal-dialog'
DIALOG: '.modal-dialog', const SELECTOR_MODAL_BODY = '.modal-body'
MODAL_BODY: '.modal-body', const SELECTOR_DATA_TOGGLE = '[data-toggle="modal"]'
DATA_TOGGLE: '[data-toggle="modal"]', const SELECTOR_DATA_DISMISS = '[data-dismiss="modal"]'
DATA_DISMISS: '[data-dismiss="modal"]', const SELECTOR_FIXED_CONTENT = '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top'
FIXED_CONTENT: '.fixed-top, .fixed-bottom, .is-fixed, .sticky-top', const SELECTOR_STICKY_CONTENT = '.sticky-top'
STICKY_CONTENT: '.sticky-top'
}
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
@ -92,7 +86,7 @@ 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
@ -123,11 +117,11 @@ class Modal {
return return
} }
if (this._element.classList.contains(ClassName.FADE)) { if (this._element.classList.contains(CLASS_NAME_FADE)) {
this._isTransitioning = true this._isTransitioning = true
} }
const showEvent = EventHandler.trigger(this._element, Event.SHOW, { const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {
relatedTarget relatedTarget
}) })
@ -146,13 +140,13 @@ class Modal {
this._setResizeEvent() this._setResizeEvent()
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
} }
@ -171,14 +165,14 @@ class Modal {
return return
} }
const hideEvent = EventHandler.trigger(this._element, Event.HIDE) const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)
if (hideEvent.defaultPrevented) { if (hideEvent.defaultPrevented) {
return return
} }
this._isShown = false this._isShown = false
const transition = this._element.classList.contains(ClassName.FADE) const transition = this._element.classList.contains(CLASS_NAME_FADE)
if (transition) { if (transition) {
this._isTransitioning = true this._isTransitioning = true
@ -187,12 +181,12 @@ class Modal {
this._setEscapeEvent() this._setEscapeEvent()
this._setResizeEvent() this._setResizeEvent()
EventHandler.off(document, Event.FOCUSIN) EventHandler.off(document, EVENT_FOCUSIN)
this._element.classList.remove(ClassName.SHOW) this._element.classList.remove(CLASS_NAME_SHOW)
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)
@ -209,11 +203,11 @@ class Modal {
.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`
* Do not move `document` in `htmlElements` array * Do not move `document` in `htmlElements` array
* It will remove `Event.CLICK_DATA_API` event that should remain * It will remove `EVENT_CLICK_DATA_API` event that should remain
*/ */
EventHandler.off(document, Event.FOCUSIN) EventHandler.off(document, EVENT_FOCUSIN)
Data.removeData(this._element, DATA_KEY) Data.removeData(this._element, DATA_KEY)
@ -244,8 +238,8 @@ class Modal {
} }
_showElement(relatedTarget) { _showElement(relatedTarget) {
const transition = this._element.classList.contains(ClassName.FADE) const transition = this._element.classList.contains(CLASS_NAME_FADE)
const modalBody = SelectorEngine.findOne(Selector.MODAL_BODY, this._dialog) const modalBody = SelectorEngine.findOne(SELECTOR_MODAL_BODY, this._dialog)
if (!this._element.parentNode || if (!this._element.parentNode ||
this._element.parentNode.nodeType !== Node.ELEMENT_NODE) { this._element.parentNode.nodeType !== Node.ELEMENT_NODE) {
@ -257,7 +251,7 @@ class Modal {
this._element.removeAttribute('aria-hidden') this._element.removeAttribute('aria-hidden')
this._element.setAttribute('aria-modal', true) this._element.setAttribute('aria-modal', true)
if (this._dialog.classList.contains(ClassName.SCROLLABLE) && modalBody) { if (this._dialog.classList.contains(CLASS_NAME_SCROLLABLE) && modalBody) {
modalBody.scrollTop = 0 modalBody.scrollTop = 0
} else { } else {
this._element.scrollTop = 0 this._element.scrollTop = 0
@ -267,7 +261,7 @@ class Modal {
reflow(this._element) reflow(this._element)
} }
this._element.classList.add(ClassName.SHOW) this._element.classList.add(CLASS_NAME_SHOW)
if (this._config.focus) { if (this._config.focus) {
this._enforceFocus() this._enforceFocus()
@ -279,7 +273,7 @@ class Modal {
} }
this._isTransitioning = false this._isTransitioning = false
EventHandler.trigger(this._element, Event.SHOWN, { EventHandler.trigger(this._element, EVENT_SHOWN, {
relatedTarget relatedTarget
}) })
} }
@ -295,8 +289,8 @@ 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)) {
@ -307,7 +301,7 @@ class Modal {
_setEscapeEvent() { _setEscapeEvent() {
if (this._isShown) { if (this._isShown) {
EventHandler.on(this._element, Event.KEYDOWN_DISMISS, event => { EventHandler.on(this._element, EVENT_KEYDOWN_DISMISS, event => {
if (this._config.keyboard && event.which === ESCAPE_KEYCODE) { if (this._config.keyboard && event.which === ESCAPE_KEYCODE) {
event.preventDefault() event.preventDefault()
this.hide() this.hide()
@ -316,15 +310,15 @@ class Modal {
} }
}) })
} else { } else {
EventHandler.off(this._element, Event.KEYDOWN_DISMISS) EventHandler.off(this._element, EVENT_KEYDOWN_DISMISS)
} }
} }
_setResizeEvent() { _setResizeEvent() {
if (this._isShown) { if (this._isShown) {
EventHandler.on(window, Event.RESIZE, () => this._adjustDialog()) EventHandler.on(window, EVENT_RESIZE, () => this._adjustDialog())
} else { } else {
EventHandler.off(window, Event.RESIZE) EventHandler.off(window, EVENT_RESIZE)
} }
} }
@ -334,10 +328,10 @@ class Modal {
this._element.removeAttribute('aria-modal') this._element.removeAttribute('aria-modal')
this._isTransitioning = false this._isTransitioning = false
this._showBackdrop(() => { this._showBackdrop(() => {
document.body.classList.remove(ClassName.OPEN) document.body.classList.remove(CLASS_NAME_OPEN)
this._resetAdjustments() this._resetAdjustments()
this._resetScrollbar() this._resetScrollbar()
EventHandler.trigger(this._element, Event.HIDDEN) EventHandler.trigger(this._element, EVENT_HIDDEN)
}) })
} }
@ -347,13 +341,13 @@ class Modal {
} }
_showBackdrop(callback) { _showBackdrop(callback) {
const animate = this._element.classList.contains(ClassName.FADE) ? const animate = this._element.classList.contains(CLASS_NAME_FADE) ?
ClassName.FADE : CLASS_NAME_FADE :
'' ''
if (this._isShown && this._config.backdrop) { if (this._isShown && this._config.backdrop) {
this._backdrop = document.createElement('div') this._backdrop = document.createElement('div')
this._backdrop.className = ClassName.BACKDROP this._backdrop.className = CLASS_NAME_BACKDROP
if (animate) { if (animate) {
this._backdrop.classList.add(animate) this._backdrop.classList.add(animate)
@ -361,7 +355,7 @@ 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
@ -378,7 +372,7 @@ class Modal {
reflow(this._backdrop) reflow(this._backdrop)
} }
this._backdrop.classList.add(ClassName.SHOW) this._backdrop.classList.add(CLASS_NAME_SHOW)
if (!animate) { if (!animate) {
callback() callback()
@ -390,14 +384,14 @@ class Modal {
EventHandler.one(this._backdrop, TRANSITION_END, callback) EventHandler.one(this._backdrop, TRANSITION_END, callback)
emulateTransitionEnd(this._backdrop, backdropTransitionDuration) emulateTransitionEnd(this._backdrop, backdropTransitionDuration)
} else if (!this._isShown && this._backdrop) { } else if (!this._isShown && this._backdrop) {
this._backdrop.classList.remove(ClassName.SHOW) this._backdrop.classList.remove(CLASS_NAME_SHOW)
const callbackRemove = () => { const callbackRemove = () => {
this._removeBackdrop() this._removeBackdrop()
callback() callback()
} }
if (this._element.classList.contains(ClassName.FADE)) { if (this._element.classList.contains(CLASS_NAME_FADE)) {
const backdropTransitionDuration = getTransitionDurationFromElement(this._backdrop) const backdropTransitionDuration = getTransitionDurationFromElement(this._backdrop)
EventHandler.one(this._backdrop, TRANSITION_END, callbackRemove) EventHandler.one(this._backdrop, TRANSITION_END, callbackRemove)
emulateTransitionEnd(this._backdrop, backdropTransitionDuration) emulateTransitionEnd(this._backdrop, backdropTransitionDuration)
@ -411,15 +405,15 @@ class Modal {
_triggerBackdropTransition() { _triggerBackdropTransition() {
if (this._config.backdrop === 'static') { if (this._config.backdrop === 'static') {
const hideEvent = EventHandler.trigger(this._element, Event.HIDE_PREVENTED) const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE_PREVENTED)
if (hideEvent.defaultPrevented) { if (hideEvent.defaultPrevented) {
return return
} }
this._element.classList.add(ClassName.STATIC) this._element.classList.add(CLASS_NAME_STATIC)
const modalTransitionDuration = getTransitionDurationFromElement(this._element) const modalTransitionDuration = getTransitionDurationFromElement(this._element)
EventHandler.one(this._element, TRANSITION_END, () => { EventHandler.one(this._element, TRANSITION_END, () => {
this._element.classList.remove(ClassName.STATIC) this._element.classList.remove(CLASS_NAME_STATIC)
}) })
emulateTransitionEnd(this._element, modalTransitionDuration) emulateTransitionEnd(this._element, modalTransitionDuration)
this._element.focus() this._element.focus()
@ -462,7 +456,7 @@ class Modal {
// while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set // while $(DOMNode).css('padding-right') returns the calculated value or 0 if not set
// 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']
@ -471,7 +465,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']
@ -487,12 +481,12 @@ class Modal {
document.body.style.paddingRight = `${parseFloat(calculatedPadding) + this._scrollbarWidth}px` document.body.style.paddingRight = `${parseFloat(calculatedPadding) + this._scrollbarWidth}px`
} }
document.body.classList.add(ClassName.OPEN) document.body.classList.add(CLASS_NAME_OPEN)
} }
_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') {
@ -502,7 +496,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') {
@ -523,7 +517,7 @@ class Modal {
_getScrollbarWidth() { // thx d.walsh _getScrollbarWidth() { // thx d.walsh
const scrollDiv = document.createElement('div') const scrollDiv = document.createElement('div')
scrollDiv.className = ClassName.SCROLLBAR_MEASURER scrollDiv.className = CLASS_NAME_SCROLLBAR_MEASURER
document.body.appendChild(scrollDiv) document.body.appendChild(scrollDiv)
const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth const scrollbarWidth = scrollDiv.getBoundingClientRect().width - scrollDiv.clientWidth
document.body.removeChild(scrollDiv) document.body.removeChild(scrollDiv)
@ -568,20 +562,20 @@ class Modal {
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
const target = getElementFromSelector(this) const target = getElementFromSelector(this)
if (this.tagName === 'A' || this.tagName === 'AREA') { if (this.tagName === 'A' || this.tagName === 'AREA') {
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
} }
EventHandler.one(target, Event.HIDDEN, () => { EventHandler.one(target, EVENT_HIDDEN, () => {
if (isVisible(this)) { if (isVisible(this)) {
this.focus() this.focus()
} }

View File

@ -39,16 +39,6 @@ const DefaultType = {
content: '(string|element|function)' content: '(string|element|function)'
} }
const ClassName = {
FADE: 'fade',
SHOW: 'show'
}
const Selector = {
TITLE: '.popover-header',
CONTENT: '.popover-body'
}
const Event = { const Event = {
HIDE: `hide${EVENT_KEY}`, HIDE: `hide${EVENT_KEY}`,
HIDDEN: `hidden${EVENT_KEY}`, HIDDEN: `hidden${EVENT_KEY}`,
@ -62,6 +52,12 @@ const Event = {
MOUSELEAVE: `mouseleave${EVENT_KEY}` MOUSELEAVE: `mouseleave${EVENT_KEY}`
} }
const CLASS_NAME_FADE = 'fade'
const CLASS_NAME_SHOW = 'show'
const SELECTOR_TITLE = '.popover-header'
const SELECTOR_CONTENT = '.popover-body'
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
* Class Definition * Class Definition
@ -109,16 +105,16 @@ class Popover extends Tooltip {
const tip = this.getTipElement() const tip = this.getTipElement()
// we use append for html objects to maintain js events // we use append for html objects to maintain js events
this.setElementContent(SelectorEngine.findOne(Selector.TITLE, tip), this.getTitle()) this.setElementContent(SelectorEngine.findOne(SELECTOR_TITLE, tip), this.getTitle())
let content = this._getContent() let content = this._getContent()
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(CLASS_NAME_FADE)
tip.classList.remove(ClassName.SHOW) tip.classList.remove(CLASS_NAME_SHOW)
} }
_addAttachmentClass(attachment) { _addAttachmentClass(attachment) {

View File

@ -41,31 +41,23 @@ const DefaultType = {
target: '(string|element)' target: '(string|element)'
} }
const Event = { const EVENT_ACTIVATE = `activate${EVENT_KEY}`
ACTIVATE: `activate${EVENT_KEY}`, const EVENT_SCROLL = `scroll${EVENT_KEY}`
SCROLL: `scroll${EVENT_KEY}`, const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`
LOAD_DATA_API: `load${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = { const CLASS_NAME_DROPDOWN_ITEM = 'dropdown-item'
DROPDOWN_ITEM: 'dropdown-item', const CLASS_NAME_ACTIVE = 'active'
ACTIVE: 'active'
}
const Selector = { const SELECTOR_DATA_SPY = '[data-spy="scroll"]'
DATA_SPY: '[data-spy="scroll"]', const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'
NAV_LIST_GROUP: '.nav, .list-group', const SELECTOR_NAV_LINKS = '.nav-link'
NAV_LINKS: '.nav-link', const SELECTOR_NAV_ITEMS = '.nav-item'
NAV_ITEMS: '.nav-item', const SELECTOR_LIST_ITEMS = '.list-group-item'
LIST_ITEMS: '.list-group-item', const SELECTOR_DROPDOWN = '.dropdown'
DROPDOWN: '.dropdown', const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'
DROPDOWN_TOGGLE: '.dropdown-toggle'
}
const OffsetMethod = { const METHOD_OFFSET = 'offset'
OFFSET: 'offset', const METHOD_POSITION = 'position'
POSITION: 'position'
}
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
@ -78,15 +70,15 @@ class ScrollSpy {
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} .${ClassName.DROPDOWN_ITEM}` `${this._config.target} .${CLASS_NAME_DROPDOWN_ITEM}`
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()
@ -108,14 +100,14 @@ class ScrollSpy {
refresh() { refresh() {
const autoMethod = this._scrollElement === this._scrollElement.window ? const autoMethod = this._scrollElement === this._scrollElement.window ?
OffsetMethod.OFFSET : METHOD_OFFSET :
OffsetMethod.POSITION METHOD_POSITION
const offsetMethod = this._config.method === 'auto' ? const offsetMethod = this._config.method === 'auto' ?
autoMethod : autoMethod :
this._config.method this._config.method
const offsetBase = offsetMethod === OffsetMethod.POSITION ? const offsetBase = offsetMethod === METHOD_POSITION ?
this._getScrollTop() : this._getScrollTop() :
0 0
@ -261,42 +253,42 @@ class ScrollSpy {
const link = SelectorEngine.findOne(queries.join(',')) const link = SelectorEngine.findOne(queries.join(','))
if (link.classList.contains(ClassName.DROPDOWN_ITEM)) { if (link.classList.contains(CLASS_NAME_DROPDOWN_ITEM)) {
SelectorEngine SelectorEngine
.findOne(Selector.DROPDOWN_TOGGLE, SelectorEngine.closest(link, Selector.DROPDOWN)) .findOne(SELECTOR_DROPDOWN_TOGGLE, SelectorEngine.closest(link, SELECTOR_DROPDOWN))
.classList.add(ClassName.ACTIVE) .classList.add(CLASS_NAME_ACTIVE)
link.classList.add(ClassName.ACTIVE) link.classList.add(CLASS_NAME_ACTIVE)
} else { } else {
// Set triggered link as active // Set triggered link as active
link.classList.add(ClassName.ACTIVE) link.classList.add(CLASS_NAME_ACTIVE)
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(CLASS_NAME_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(CLASS_NAME_ACTIVE))
}) })
}) })
} }
EventHandler.trigger(this._scrollElement, Event.ACTIVATE, { EventHandler.trigger(this._scrollElement, EVENT_ACTIVATE, {
relatedTarget: target relatedTarget: target
}) })
} }
_clear() { _clear() {
makeArray(SelectorEngine.find(this._selector)) makeArray(SelectorEngine.find(this._selector))
.filter(node => node.classList.contains(ClassName.ACTIVE)) .filter(node => node.classList.contains(CLASS_NAME_ACTIVE))
.forEach(node => node.classList.remove(ClassName.ACTIVE)) .forEach(node => node.classList.remove(CLASS_NAME_ACTIVE))
} }
// Static // Static
@ -331,8 +323,8 @@ 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)))
}) })

View File

@ -30,31 +30,25 @@ 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}`, const EVENT_HIDDEN = `hidden${EVENT_KEY}`
HIDDEN: `hidden${EVENT_KEY}`, const EVENT_SHOW = `show${EVENT_KEY}`
SHOW: `show${EVENT_KEY}`, const EVENT_SHOWN = `shown${EVENT_KEY}`
SHOWN: `shown${EVENT_KEY}`, const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`
CLICK_DATA_API: `click${EVENT_KEY}${DATA_API_KEY}`
}
const ClassName = { const CLASS_NAME_DROPDOWN_MENU = 'dropdown-menu'
DROPDOWN_MENU: 'dropdown-menu', const CLASS_NAME_ACTIVE = 'active'
ACTIVE: 'active', const CLASS_NAME_DISABLED = 'disabled'
DISABLED: 'disabled', const CLASS_NAME_FADE = 'fade'
FADE: 'fade', const CLASS_NAME_SHOW = 'show'
SHOW: 'show'
}
const Selector = { const SELECTOR_DROPDOWN = '.dropdown'
DROPDOWN: '.dropdown', const SELECTOR_NAV_LIST_GROUP = '.nav, .list-group'
NAV_LIST_GROUP: '.nav, .list-group', const SELECTOR_ACTIVE = '.active'
ACTIVE: '.active', const SELECTOR_ACTIVE_UL = ':scope > li > .active'
ACTIVE_UL: ':scope > li > .active', const SELECTOR_DATA_TOGGLE = '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]'
DATA_TOGGLE: '[data-toggle="tab"], [data-toggle="pill"], [data-toggle="list"]', const SELECTOR_DROPDOWN_TOGGLE = '.dropdown-toggle'
DROPDOWN_TOGGLE: '.dropdown-toggle', const SELECTOR_DROPDOWN_ACTIVE_CHILD = ':scope > .dropdown-menu .active'
DROPDOWN_ACTIVE_CHILD: ':scope > .dropdown-menu .active'
}
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
@ -80,17 +74,17 @@ class Tab {
show() { show() {
if ((this._element.parentNode && if ((this._element.parentNode &&
this._element.parentNode.nodeType === Node.ELEMENT_NODE && this._element.parentNode.nodeType === Node.ELEMENT_NODE &&
this._element.classList.contains(ClassName.ACTIVE)) || this._element.classList.contains(CLASS_NAME_ACTIVE)) ||
this._element.classList.contains(ClassName.DISABLED)) { this._element.classList.contains(CLASS_NAME_DISABLED)) {
return return
} }
let previous let previous
const target = getElementFromSelector(this._element) const target = getElementFromSelector(this._element)
const listElement = SelectorEngine.closest(this._element, Selector.NAV_LIST_GROUP) const listElement = SelectorEngine.closest(this._element, SELECTOR_NAV_LIST_GROUP)
if (listElement) { if (listElement) {
const itemSelector = listElement.nodeName === 'UL' || listElement.nodeName === 'OL' ? Selector.ACTIVE_UL : Selector.ACTIVE const itemSelector = listElement.nodeName === 'UL' || listElement.nodeName === 'OL' ? SELECTOR_ACTIVE_UL : SELECTOR_ACTIVE
previous = makeArray(SelectorEngine.find(itemSelector, listElement)) previous = makeArray(SelectorEngine.find(itemSelector, listElement))
previous = previous[previous.length - 1] previous = previous[previous.length - 1]
} }
@ -98,12 +92,12 @@ class Tab {
let hideEvent = null let hideEvent = null
if (previous) { if (previous) {
hideEvent = EventHandler.trigger(previous, Event.HIDE, { hideEvent = EventHandler.trigger(previous, EVENT_HIDE, {
relatedTarget: this._element relatedTarget: this._element
}) })
} }
const showEvent = EventHandler.trigger(this._element, Event.SHOW, { const showEvent = EventHandler.trigger(this._element, EVENT_SHOW, {
relatedTarget: previous relatedTarget: previous
}) })
@ -118,10 +112,10 @@ class Tab {
) )
const complete = () => { const complete = () => {
EventHandler.trigger(previous, Event.HIDDEN, { EventHandler.trigger(previous, EVENT_HIDDEN, {
relatedTarget: this._element relatedTarget: this._element
}) })
EventHandler.trigger(this._element, Event.SHOWN, { EventHandler.trigger(this._element, EVENT_SHOWN, {
relatedTarget: previous relatedTarget: previous
}) })
} }
@ -142,12 +136,12 @@ class Tab {
_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(CLASS_NAME_FADE))
const complete = () => this._transitionComplete( const complete = () => this._transitionComplete(
element, element,
@ -157,7 +151,7 @@ class Tab {
if (active && isTransitioning) { if (active && isTransitioning) {
const transitionDuration = getTransitionDurationFromElement(active) const transitionDuration = getTransitionDurationFromElement(active)
active.classList.remove(ClassName.SHOW) active.classList.remove(CLASS_NAME_SHOW)
EventHandler.one(active, TRANSITION_END, complete) EventHandler.one(active, TRANSITION_END, complete)
emulateTransitionEnd(active, transitionDuration) emulateTransitionEnd(active, transitionDuration)
@ -168,12 +162,12 @@ class Tab {
_transitionComplete(element, active, callback) { _transitionComplete(element, active, callback) {
if (active) { if (active) {
active.classList.remove(ClassName.ACTIVE) active.classList.remove(CLASS_NAME_ACTIVE)
const dropdownChild = SelectorEngine.findOne(Selector.DROPDOWN_ACTIVE_CHILD, active.parentNode) const dropdownChild = SelectorEngine.findOne(SELECTOR_DROPDOWN_ACTIVE_CHILD, active.parentNode)
if (dropdownChild) { if (dropdownChild) {
dropdownChild.classList.remove(ClassName.ACTIVE) dropdownChild.classList.remove(CLASS_NAME_ACTIVE)
} }
if (active.getAttribute('role') === 'tab') { if (active.getAttribute('role') === 'tab') {
@ -181,23 +175,23 @@ class Tab {
} }
} }
element.classList.add(ClassName.ACTIVE) element.classList.add(CLASS_NAME_ACTIVE)
if (element.getAttribute('role') === 'tab') { if (element.getAttribute('role') === 'tab') {
element.setAttribute('aria-selected', true) element.setAttribute('aria-selected', true)
} }
reflow(element) reflow(element)
if (element.classList.contains(ClassName.FADE)) { if (element.classList.contains(CLASS_NAME_FADE)) {
element.classList.add(ClassName.SHOW) element.classList.add(CLASS_NAME_SHOW)
} }
if (element.parentNode && element.parentNode.classList.contains(ClassName.DROPDOWN_MENU)) { if (element.parentNode && element.parentNode.classList.contains(CLASS_NAME_DROPDOWN_MENU)) {
const dropdownElement = SelectorEngine.closest(element, Selector.DROPDOWN) const dropdownElement = SelectorEngine.closest(element, SELECTOR_DROPDOWN)
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(CLASS_NAME_ACTIVE))
} }
element.setAttribute('aria-expanded', true) element.setAttribute('aria-expanded', true)
@ -235,7 +229,7 @@ class Tab {
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
EventHandler.on(document, Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { EventHandler.on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_TOGGLE, function (event) {
event.preventDefault() event.preventDefault()
const data = Data.getData(this, DATA_KEY) || new Tab(this) const data = Data.getData(this, DATA_KEY) || new Tab(this)

View File

@ -28,20 +28,16 @@ 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}`, const EVENT_HIDE = `hide${EVENT_KEY}`
HIDE: `hide${EVENT_KEY}`, const EVENT_HIDDEN = `hidden${EVENT_KEY}`
HIDDEN: `hidden${EVENT_KEY}`, const EVENT_SHOW = `show${EVENT_KEY}`
SHOW: `show${EVENT_KEY}`, const EVENT_SHOWN = `shown${EVENT_KEY}`
SHOWN: `shown${EVENT_KEY}`
}
const ClassName = { const CLASS_NAME_FADE = 'fade'
FADE: 'fade', const CLASS_NAME_HIDE = 'hide'
HIDE: 'hide', const CLASS_NAME_SHOW = 'show'
SHOW: 'show', const CLASS_NAME_SHOWING = 'showing'
SHOWING: 'showing'
}
const DefaultType = { const DefaultType = {
animation: 'boolean', animation: 'boolean',
@ -55,9 +51,7 @@ const Default = {
delay: 500 delay: 500
} }
const Selector = { const SELECTOR_DATA_DISMISS = '[data-dismiss="toast"]'
DATA_DISMISS: '[data-dismiss="toast"]'
}
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
@ -91,21 +85,21 @@ class Toast {
// Public // Public
show() { show() {
const showEvent = EventHandler.trigger(this._element, Event.SHOW) const showEvent = EventHandler.trigger(this._element, EVENT_SHOW)
if (showEvent.defaultPrevented) { if (showEvent.defaultPrevented) {
return return
} }
if (this._config.animation) { if (this._config.animation) {
this._element.classList.add(ClassName.FADE) this._element.classList.add(CLASS_NAME_FADE)
} }
const complete = () => { const complete = () => {
this._element.classList.remove(ClassName.SHOWING) this._element.classList.remove(CLASS_NAME_SHOWING)
this._element.classList.add(ClassName.SHOW) this._element.classList.add(CLASS_NAME_SHOW)
EventHandler.trigger(this._element, Event.SHOWN) EventHandler.trigger(this._element, EVENT_SHOWN)
if (this._config.autohide) { if (this._config.autohide) {
this._timeout = setTimeout(() => { this._timeout = setTimeout(() => {
@ -114,9 +108,9 @@ class Toast {
} }
} }
this._element.classList.remove(ClassName.HIDE) this._element.classList.remove(CLASS_NAME_HIDE)
reflow(this._element) reflow(this._element)
this._element.classList.add(ClassName.SHOWING) this._element.classList.add(CLASS_NAME_SHOWING)
if (this._config.animation) { if (this._config.animation) {
const transitionDuration = getTransitionDurationFromElement(this._element) const transitionDuration = getTransitionDurationFromElement(this._element)
@ -128,22 +122,22 @@ class Toast {
} }
hide() { hide() {
if (!this._element.classList.contains(ClassName.SHOW)) { if (!this._element.classList.contains(CLASS_NAME_SHOW)) {
return return
} }
const hideEvent = EventHandler.trigger(this._element, Event.HIDE) const hideEvent = EventHandler.trigger(this._element, EVENT_HIDE)
if (hideEvent.defaultPrevented) { if (hideEvent.defaultPrevented) {
return return
} }
const complete = () => { const complete = () => {
this._element.classList.add(ClassName.HIDE) this._element.classList.add(CLASS_NAME_HIDE)
EventHandler.trigger(this._element, Event.HIDDEN) EventHandler.trigger(this._element, EVENT_HIDDEN)
} }
this._element.classList.remove(ClassName.SHOW) this._element.classList.remove(CLASS_NAME_SHOW)
if (this._config.animation) { if (this._config.animation) {
const transitionDuration = getTransitionDurationFromElement(this._element) const transitionDuration = getTransitionDurationFromElement(this._element)
@ -158,11 +152,11 @@ class Toast {
clearTimeout(this._timeout) clearTimeout(this._timeout)
this._timeout = null this._timeout = null
if (this._element.classList.contains(ClassName.SHOW)) { if (this._element.classList.contains(CLASS_NAME_SHOW)) {
this._element.classList.remove(ClassName.SHOW) this._element.classList.remove(CLASS_NAME_SHOW)
} }
EventHandler.off(this._element, Event.CLICK_DISMISS) EventHandler.off(this._element, EVENT_CLICK_DISMISS)
Data.removeData(this._element, DATA_KEY) Data.removeData(this._element, DATA_KEY)
this._element = null this._element = null
@ -190,8 +184,8 @@ class Toast {
_setListeners() { _setListeners() {
EventHandler.on( EventHandler.on(
this._element, this._element,
Event.CLICK_DISMISS, EVENT_CLICK_DISMISS,
Selector.DATA_DISMISS, SELECTOR_DATA_DISMISS,
() => this.hide() () => this.hide()
) )
} }

View File

@ -89,11 +89,6 @@ const Default = {
popperConfig: null popperConfig: null
} }
const HoverState = {
SHOW: 'show',
OUT: 'out'
}
const Event = { const Event = {
HIDE: `hide${EVENT_KEY}`, HIDE: `hide${EVENT_KEY}`,
HIDDEN: `hidden${EVENT_KEY}`, HIDDEN: `hidden${EVENT_KEY}`,
@ -107,22 +102,19 @@ const Event = {
MOUSELEAVE: `mouseleave${EVENT_KEY}` MOUSELEAVE: `mouseleave${EVENT_KEY}`
} }
const ClassName = { const CLASS_NAME_FADE = 'fade'
FADE: 'fade', const CLASS_NAME_MODAL = 'modal'
MODAL: 'modal', const CLASS_NAME_SHOW = 'show'
SHOW: 'show'
}
const Selector = { const HOVER_STATE_SHOW = 'show'
TOOLTIP_INNER: '.tooltip-inner' const HOVER_STATE_OUT = 'out'
}
const Trigger = { const SELECTOR_TOOLTIP_INNER = '.tooltip-inner'
HOVER: 'hover',
FOCUS: 'focus', const TRIGGER_HOVER = 'hover'
CLICK: 'click', const TRIGGER_FOCUS = 'focus'
MANUAL: 'manual' const TRIGGER_CLICK = 'click'
} const TRIGGER_MANUAL = 'manual'
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
@ -221,7 +213,7 @@ class Tooltip {
context._leave(null, context) context._leave(null, context)
} }
} else { } else {
if (this.getTipElement().classList.contains(ClassName.SHOW)) { if (this.getTipElement().classList.contains(CLASS_NAME_SHOW)) {
this._leave(null, this) this._leave(null, this)
return return
} }
@ -236,7 +228,7 @@ class Tooltip {
Data.removeData(this.element, this.constructor.DATA_KEY) Data.removeData(this.element, this.constructor.DATA_KEY)
EventHandler.off(this.element, this.constructor.EVENT_KEY) EventHandler.off(this.element, this.constructor.EVENT_KEY)
EventHandler.off(SelectorEngine.closest(this.element, `.${ClassName.MODAL}`), 'hide.bs.modal', this._hideModalHandler) EventHandler.off(SelectorEngine.closest(this.element, `.${CLASS_NAME_MODAL}`), 'hide.bs.modal', this._hideModalHandler)
if (this.tip) { if (this.tip) {
this.tip.parentNode.removeChild(this.tip) this.tip.parentNode.removeChild(this.tip)
@ -281,7 +273,7 @@ class Tooltip {
this.setContent() this.setContent()
if (this.config.animation) { if (this.config.animation) {
tip.classList.add(ClassName.FADE) tip.classList.add(CLASS_NAME_FADE)
} }
const placement = typeof this.config.placement === 'function' ? const placement = typeof this.config.placement === 'function' ?
@ -302,7 +294,7 @@ class Tooltip {
this._popper = new Popper(this.element, tip, this._getPopperConfig(attachment)) this._popper = new Popper(this.element, tip, this._getPopperConfig(attachment))
tip.classList.add(ClassName.SHOW) tip.classList.add(CLASS_NAME_SHOW)
// If this is a touch-enabled device we add extra // If this is a touch-enabled device we add extra
// empty mouseover listeners to the body's immediate children; // empty mouseover listeners to the body's immediate children;
@ -324,12 +316,12 @@ class Tooltip {
EventHandler.trigger(this.element, this.constructor.Event.SHOWN) EventHandler.trigger(this.element, this.constructor.Event.SHOWN)
if (prevHoverState === HoverState.OUT) { if (prevHoverState === HOVER_STATE_OUT) {
this._leave(null, this) this._leave(null, this)
} }
} }
if (this.tip.classList.contains(ClassName.FADE)) { if (this.tip.classList.contains(CLASS_NAME_FADE)) {
const transitionDuration = getTransitionDurationFromElement(this.tip) const transitionDuration = getTransitionDurationFromElement(this.tip)
EventHandler.one(this.tip, TRANSITION_END, complete) EventHandler.one(this.tip, TRANSITION_END, complete)
emulateTransitionEnd(this.tip, transitionDuration) emulateTransitionEnd(this.tip, transitionDuration)
@ -342,7 +334,7 @@ class Tooltip {
hide() { hide() {
const tip = this.getTipElement() const tip = this.getTipElement()
const complete = () => { const complete = () => {
if (this._hoverState !== HoverState.SHOW && tip.parentNode) { if (this._hoverState !== HOVER_STATE_SHOW && tip.parentNode) {
tip.parentNode.removeChild(tip) tip.parentNode.removeChild(tip)
} }
@ -357,7 +349,7 @@ class Tooltip {
return return
} }
tip.classList.remove(ClassName.SHOW) tip.classList.remove(CLASS_NAME_SHOW)
// If this is a touch-enabled device we remove the extra // If this is a touch-enabled device we remove the extra
// empty mouseover listeners we added for iOS support // empty mouseover listeners we added for iOS support
@ -366,11 +358,11 @@ class Tooltip {
.forEach(element => EventHandler.off(element, 'mouseover', noop)) .forEach(element => EventHandler.off(element, 'mouseover', noop))
} }
this._activeTrigger[Trigger.CLICK] = false this._activeTrigger[TRIGGER_CLICK] = false
this._activeTrigger[Trigger.FOCUS] = false this._activeTrigger[TRIGGER_FOCUS] = false
this._activeTrigger[Trigger.HOVER] = false this._activeTrigger[TRIGGER_HOVER] = false
if (this.tip.classList.contains(ClassName.FADE)) { if (this.tip.classList.contains(CLASS_NAME_FADE)) {
const transitionDuration = getTransitionDurationFromElement(tip) const transitionDuration = getTransitionDurationFromElement(tip)
EventHandler.one(tip, TRANSITION_END, complete) EventHandler.one(tip, TRANSITION_END, complete)
@ -408,9 +400,9 @@ class Tooltip {
setContent() { setContent() {
const tip = this.getTipElement() const tip = this.getTipElement()
this.setElementContent(SelectorEngine.findOne(Selector.TOOLTIP_INNER, tip), this.getTitle()) this.setElementContent(SelectorEngine.findOne(SELECTOR_TOOLTIP_INNER, tip), this.getTitle())
tip.classList.remove(ClassName.FADE) tip.classList.remove(CLASS_NAME_FADE)
tip.classList.remove(ClassName.SHOW) tip.classList.remove(CLASS_NAME_SHOW)
} }
setElementContent(element, content) { setElementContent(element, content) {
@ -539,11 +531,11 @@ class Tooltip {
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
@ -566,7 +558,7 @@ class Tooltip {
} }
} }
EventHandler.on(SelectorEngine.closest(this.element, `.${ClassName.MODAL}`), EventHandler.on(SelectorEngine.closest(this.element, `.${CLASS_NAME_MODAL}`),
'hide.bs.modal', 'hide.bs.modal',
this._hideModalHandler this._hideModalHandler
) )
@ -609,19 +601,19 @@ class Tooltip {
if (event) { if (event) {
context._activeTrigger[ context._activeTrigger[
event.type === 'focusin' ? Trigger.FOCUS : Trigger.HOVER event.type === 'focusin' ? TRIGGER_FOCUS : TRIGGER_HOVER
] = true ] = true
} }
if (context.getTipElement().classList.contains(ClassName.SHOW) || if (context.getTipElement().classList.contains(CLASS_NAME_SHOW) ||
context._hoverState === HoverState.SHOW) { context._hoverState === HOVER_STATE_SHOW) {
context._hoverState = HoverState.SHOW context._hoverState = HOVER_STATE_SHOW
return return
} }
clearTimeout(context._timeout) clearTimeout(context._timeout)
context._hoverState = HoverState.SHOW context._hoverState = HOVER_STATE_SHOW
if (!context.config.delay || !context.config.delay.show) { if (!context.config.delay || !context.config.delay.show) {
context.show() context.show()
@ -629,7 +621,7 @@ class Tooltip {
} }
context._timeout = setTimeout(() => { context._timeout = setTimeout(() => {
if (context._hoverState === HoverState.SHOW) { if (context._hoverState === HOVER_STATE_SHOW) {
context.show() context.show()
} }
}, context.config.delay.show) }, context.config.delay.show)
@ -649,7 +641,7 @@ class Tooltip {
if (event) { if (event) {
context._activeTrigger[ context._activeTrigger[
event.type === 'focusout' ? Trigger.FOCUS : Trigger.HOVER event.type === 'focusout' ? TRIGGER_FOCUS : TRIGGER_HOVER
] = false ] = false
} }
@ -659,7 +651,7 @@ class Tooltip {
clearTimeout(context._timeout) clearTimeout(context._timeout)
context._hoverState = HoverState.OUT context._hoverState = HOVER_STATE_OUT
if (!context.config.delay || !context.config.delay.hide) { if (!context.config.delay || !context.config.delay.hide) {
context.hide() context.hide()
@ -667,7 +659,7 @@ class Tooltip {
} }
context._timeout = setTimeout(() => { context._timeout = setTimeout(() => {
if (context._hoverState === HoverState.OUT) { if (context._hoverState === HOVER_STATE_OUT) {
context.hide() context.hide()
} }
}, context.config.delay.hide) }, context.config.delay.hide)
@ -768,7 +760,7 @@ class Tooltip {
return return
} }
tip.classList.remove(ClassName.FADE) tip.classList.remove(CLASS_NAME_FADE)
this.config.animation = false this.config.animation = false
this.hide() this.hide()
this.show() this.show()

View File

@ -10,7 +10,13 @@ const MILLISECONDS_MULTIPLIER = 1000
const TRANSITION_END = 'transitionend' const TRANSITION_END = 'transitionend'
// 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 => {
if (obj === null || obj === undefined) {
return `${obj}`
}
return {}.toString.call(obj).match(/\s([a-z]+)/i)[1].toLowerCase()
}
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
@ -81,10 +87,7 @@ const getTransitionDurationFromElement = element => {
} }
const triggerTransitionEnd = element => { const triggerTransitionEnd = element => {
const evt = document.createEvent('HTMLEvents') element.dispatchEvent(new Event(TRANSITION_END))
evt.initEvent(TRANSITION_END, true, true)
element.dispatchEvent(evt)
} }
const isElement = obj => (obj[0] || obj).nodeType const isElement = obj => (obj[0] || obj).nodeType

View File

@ -30,13 +30,6 @@ const browsers = {
browser: 'Edge', browser: 'Edge',
browser_version: 'latest' browser_version: 'latest'
}, },
ie11Win10: {
base: 'BrowserStack',
os: 'Windows',
os_version: '10',
browser: 'IE',
browser_version: '11.0'
},
chromeWin10: { chromeWin10: {
base: 'BrowserStack', base: 'BrowserStack',
os: 'Windows', os: 'Windows',

View File

@ -653,7 +653,6 @@ describe('Modal', () => {
it('should enforce focus', done => { it('should enforce focus', done => {
fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>' fixtureEl.innerHTML = '<div class="modal"><div class="modal-dialog" /></div>'
const isIE11 = Boolean(window.MSInputMethodContext) && Boolean(document.documentMode)
const modalEl = fixtureEl.querySelector('.modal') const modalEl = fixtureEl.querySelector('.modal')
const modal = new Modal(modalEl) const modal = new Modal(modalEl)
@ -668,11 +667,6 @@ describe('Modal', () => {
modalEl.addEventListener('shown.bs.modal', () => { modalEl.addEventListener('shown.bs.modal', () => {
expect(modal._enforceFocus).toHaveBeenCalled() expect(modal._enforceFocus).toHaveBeenCalled()
if (isIE11) {
done()
return
}
spyOn(modal._element, 'focus') spyOn(modal._element, 'focus')
document.addEventListener('focusin', focusInListener) document.addEventListener('focusin', focusInListener)

View File

@ -198,8 +198,9 @@ describe('Util', () => {
}) })
describe('typeCheckConfig', () => { describe('typeCheckConfig', () => {
const namePlugin = 'collapse'
it('should check type of the config object', () => { it('should check type of the config object', () => {
const namePlugin = 'collapse'
const defaultType = { const defaultType = {
toggle: 'boolean', toggle: 'boolean',
parent: '(string|element)' parent: '(string|element)'
@ -213,6 +214,34 @@ describe('Util', () => {
Util.typeCheckConfig(namePlugin, config, defaultType) Util.typeCheckConfig(namePlugin, config, defaultType)
}).toThrow(new Error('COLLAPSE: Option "parent" provided type "number" but expected type "(string|element)".')) }).toThrow(new Error('COLLAPSE: Option "parent" provided type "number" but expected type "(string|element)".'))
}) })
it('should return null stringified when null is passed', () => {
const defaultType = {
toggle: 'boolean',
parent: '(null|element)'
}
const config = {
toggle: true,
parent: null
}
Util.typeCheckConfig(namePlugin, config, defaultType)
expect().nothing()
})
it('should return undefined stringified when undefined is passed', () => {
const defaultType = {
toggle: 'boolean',
parent: '(undefined|element)'
}
const config = {
toggle: true,
parent: undefined
}
Util.typeCheckConfig(namePlugin, config, defaultType)
expect().nothing()
})
}) })
describe('makeArray', () => { describe('makeArray', () => {

242
package-lock.json generated
View File

@ -82,9 +82,9 @@
} }
}, },
"@babel/generator": { "@babel/generator": {
"version": "7.8.7", "version": "7.8.8",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.7.tgz", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.8.8.tgz",
"integrity": "sha512-DQwjiKJqH4C3qGiyQCAExJHoZssn49JTMJgZ8SANGgVFdkupcUhLOdkAeoC6kmHZCPfoDG5M0b6cFlSN5wW7Ew==", "integrity": "sha512-HKyUVu69cZoclptr8t8U5b6sx6zoWjh8jiUhnuj3MpZuKT2dJ8zPTuiy31luq32swhI0SpwItCIlU8XW7BZeJg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/types": "^7.8.7", "@babel/types": "^7.8.7",
@ -137,14 +137,14 @@
} }
}, },
"@babel/helper-create-regexp-features-plugin": { "@babel/helper-create-regexp-features-plugin": {
"version": "7.8.6", "version": "7.8.8",
"resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.6.tgz", "resolved": "https://registry.npmjs.org/@babel/helper-create-regexp-features-plugin/-/helper-create-regexp-features-plugin-7.8.8.tgz",
"integrity": "sha512-bPyujWfsHhV/ztUkwGHz/RPV1T1TDEsSZDsN42JPehndA+p1KKTh3npvTadux0ZhCrytx9tvjpWNowKby3tM6A==", "integrity": "sha512-LYVPdwkrQEiX9+1R29Ld/wTrmQu1SSKYnuOk3g0CkcZMA1p0gsNxJFj/3gBdaJ7Cg0Fnek5z0DsMULePP7Lrqg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/helper-annotate-as-pure": "^7.8.3", "@babel/helper-annotate-as-pure": "^7.8.3",
"@babel/helper-regex": "^7.8.3", "@babel/helper-regex": "^7.8.3",
"regexpu-core": "^4.6.0" "regexpu-core": "^4.7.0"
} }
}, },
"@babel/helper-define-map": { "@babel/helper-define-map": {
@ -333,9 +333,9 @@
} }
}, },
"@babel/parser": { "@babel/parser": {
"version": "7.8.7", "version": "7.8.8",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.7.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.8.8.tgz",
"integrity": "sha512-9JWls8WilDXFGxs0phaXAZgpxTZhSk/yOYH2hTHC0X1yC7Z78IJfvR1vJ+rmJKq3I35td2XzXzN6ZLYlna+r/A==", "integrity": "sha512-mO5GWzBPsPf6865iIbzNE0AvkKF3NE+2S3eRUpE+FE07BOAkXh6G+GW/Pj01hhXjve1WScbaIO4UlY1JKeqCcA==",
"dev": true "dev": true
}, },
"@babel/plugin-proposal-async-generator-functions": { "@babel/plugin-proposal-async-generator-functions": {
@ -410,12 +410,12 @@
} }
}, },
"@babel/plugin-proposal-unicode-property-regex": { "@babel/plugin-proposal-unicode-property-regex": {
"version": "7.8.3", "version": "7.8.8",
"resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.3.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-unicode-property-regex/-/plugin-proposal-unicode-property-regex-7.8.8.tgz",
"integrity": "sha512-1/1/rEZv2XGweRwwSkLpY+s60za9OZ1hJs4YDqFHCw0kYWYwL5IFljVY1MYBL+weT1l9pokDO2uhSTLVxzoHkQ==", "integrity": "sha512-EVhjVsMpbhLw9ZfHWSx2iy13Q8Z/eg8e8ccVWt23sWQK5l1UdkoLJPN5w69UA4uITGBnEZD2JOe4QOHycYKv8A==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/helper-create-regexp-features-plugin": "^7.8.3", "@babel/helper-create-regexp-features-plugin": "^7.8.8",
"@babel/helper-plugin-utils": "^7.8.3" "@babel/helper-plugin-utils": "^7.8.3"
} }
}, },
@ -556,9 +556,9 @@
} }
}, },
"@babel/plugin-transform-destructuring": { "@babel/plugin-transform-destructuring": {
"version": "7.8.3", "version": "7.8.8",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.3.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.8.8.tgz",
"integrity": "sha512-H4X646nCkiEcHZUZaRkhE2XVsoz0J/1x3VVujnn96pSoGCtKPA99ZZA+va+gK+92Zycd6OBKCD8tDb/731bhgQ==", "integrity": "sha512-eRJu4Vs2rmttFCdhPUM3bV0Yo/xPSdPw6ML9KHs/bjB4bLA5HXlbvYXPOD5yASodGod+krjYx21xm1QmL8dCJQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/helper-plugin-utils": "^7.8.3" "@babel/helper-plugin-utils": "^7.8.3"
@ -704,9 +704,9 @@
} }
}, },
"@babel/plugin-transform-parameters": { "@babel/plugin-transform-parameters": {
"version": "7.8.7", "version": "7.8.8",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.7.tgz", "resolved": "https://registry.npmjs.org/@babel/plugin-transform-parameters/-/plugin-transform-parameters-7.8.8.tgz",
"integrity": "sha512-brYWaEPTRimOctz2NDA3jnBbDi7SVN2T4wYuu0aqSzxC3nozFZngGaw29CJ9ZPweB7k+iFmZuoG3IVPIcXmD2g==", "integrity": "sha512-hC4Ld/Ulpf1psQciWWwdnUspQoQco2bMzSrwU6TmzRlvoYQe4rQFy9vnCZDTlVeCQj0JPfL+1RX0V8hCJvkgBA==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/helper-call-delegate": "^7.8.7", "@babel/helper-call-delegate": "^7.8.7",
@ -1046,9 +1046,9 @@
"dev": true "dev": true
}, },
"@types/node": { "@types/node": {
"version": "13.7.7", "version": "13.9.1",
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.7.7.tgz", "resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.1.tgz",
"integrity": "sha512-Uo4chgKbnPNlxQwoFmYIwctkQVkMMmsAoGGU4JKwLuvBefF0pCq4FybNSnfkfRCpC7ZW7kttcC/TrRtAJsvGtg==", "integrity": "sha512-E6M6N0blf/jiZx8Q3nb0vNaswQeEyn0XlupO+xN6DtJ6r6IT4nXrTry7zhIfYvFCl3/8Cu6WIysmUBKiqV0bqQ==",
"dev": true "dev": true
}, },
"@types/normalize-package-data": { "@types/normalize-package-data": {
@ -1955,11 +1955,13 @@
} }
}, },
"bl": { "bl": {
"version": "4.0.1", "version": "4.0.2",
"resolved": "https://registry.npmjs.org/bl/-/bl-4.0.1.tgz", "resolved": "https://registry.npmjs.org/bl/-/bl-4.0.2.tgz",
"integrity": "sha512-FL/TdvchukRCuWVxT0YMO/7+L5TNeNrVFvRU2IY63aUyv9mpt8splf2NEr6qXtPo5fya5a66YohQKvGNmLrWNA==", "integrity": "sha512-j4OH8f6Qg2bGuWfRiltT2HYGx0e1QcBTrK9KAHNMwMZdQnDZFk0ZSYIpADjYCB3U12nicC5tVJwSIhwOWjb4RQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"buffer": "^5.5.0",
"inherits": "^2.0.4",
"readable-stream": "^3.4.0" "readable-stream": "^3.4.0"
}, },
"dependencies": { "dependencies": {
@ -2385,9 +2387,9 @@
} }
}, },
"caniuse-lite": { "caniuse-lite": {
"version": "1.0.30001032", "version": "1.0.30001035",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001032.tgz", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001035.tgz",
"integrity": "sha512-8joOm7BwcpEN4BfVHtfh0hBXSAPVYk+eUIcNntGtMkUWy/6AKRCDZINCLe3kB1vHhT2vBxBF85Hh9VlPXi/qjA==", "integrity": "sha512-C1ZxgkuA4/bUEdMbU5WrGY4+UhMFFiXrgNAfxiMIqWgFTWfv/xsZCS2xEHT2LMq7xAZfuAnu6mcqyDl0ZR6wLQ==",
"dev": true "dev": true
}, },
"capture-stack-trace": { "capture-stack-trace": {
@ -3633,9 +3635,9 @@
"dev": true "dev": true
}, },
"electron-to-chromium": { "electron-to-chromium": {
"version": "1.3.370", "version": "1.3.378",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.370.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.378.tgz",
"integrity": "sha512-399cXDE9C7qoVF2CUgCA/MLflfvxbo1F0kB/pkB94426freL/JgZ0HNaloomsOfnE+VC/qgTFZqzmivSdaNfPQ==", "integrity": "sha512-nBp/AfhaVIOnfwgL1CZxt80IcqWcyYXiX6v5gflAksxy+SzBVz7A7UWR1Nos92c9ofXW74V9PoapzRb0jJfYXw==",
"dev": true "dev": true
}, },
"emoji-regex": { "emoji-regex": {
@ -3908,9 +3910,9 @@
} }
}, },
"globals": { "globals": {
"version": "12.3.0", "version": "12.4.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-12.3.0.tgz", "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz",
"integrity": "sha512-wAfjdLgFsPZsklLJvOBUBmzYE8/CwhEqSBEMRXA3qxIiNtyqvjYurAtIfDh6chlEPUfmTY3MnZh5Hfh4q0UlIw==", "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==",
"dev": true, "dev": true,
"requires": { "requires": {
"type-fest": "^0.8.1" "type-fest": "^0.8.1"
@ -4227,12 +4229,12 @@
"dev": true "dev": true
}, },
"espree": { "espree": {
"version": "6.2.0", "version": "6.2.1",
"resolved": "https://registry.npmjs.org/espree/-/espree-6.2.0.tgz", "resolved": "https://registry.npmjs.org/espree/-/espree-6.2.1.tgz",
"integrity": "sha512-Xs8airJ7RQolnDIbLtRutmfvSsAe0xqMMAantCN/GMoqf81TFbeI1T7Jpd56qYu1uuh32dOG5W/X9uO+ghPXzA==", "integrity": "sha512-ysCxRQY3WaXJz9tdbWOwuWr5Y/XrPTGX9Kiz3yoUXwW0VZ4w30HTkQLaGx/+ttFjF8i+ACbArnB4ce68a9m5hw==",
"dev": true, "dev": true,
"requires": { "requires": {
"acorn": "^7.1.0", "acorn": "^7.1.1",
"acorn-jsx": "^5.2.0", "acorn-jsx": "^5.2.0",
"eslint-visitor-keys": "^1.1.0" "eslint-visitor-keys": "^1.1.0"
} }
@ -5580,9 +5582,9 @@
} }
}, },
"gaxios": { "gaxios": {
"version": "2.3.2", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/gaxios/-/gaxios-2.3.2.tgz", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-3.0.0.tgz",
"integrity": "sha512-K/+py7UvKRDaEwEKlLiRKrFr+wjGjsMz5qH7Vs549QJS7cpSCOT/BbWL7pzqECflc46FcNPipjSfB+V1m8PAhw==", "integrity": "sha512-9UwpmysNKCwSCSvFqTSTPFZX89RgMpJBFzKknrjpEm6oQGn7QDlFO57mPqJHP9X4llPR8uSt3LAHBZDXoetGnA==",
"dev": true, "dev": true,
"requires": { "requires": {
"abort-controller": "^3.0.0", "abort-controller": "^3.0.0",
@ -6324,9 +6326,9 @@
"dev": true "dev": true
}, },
"in-publish": { "in-publish": {
"version": "2.0.0", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.0.tgz", "resolved": "https://registry.npmjs.org/in-publish/-/in-publish-2.0.1.tgz",
"integrity": "sha1-4g/146KvwmkDILbcVSaCqcf631E=", "integrity": "sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==",
"dev": true "dev": true
}, },
"indent-string": { "indent-string": {
@ -6370,9 +6372,9 @@
"dev": true "dev": true
}, },
"inquirer": { "inquirer": {
"version": "7.0.6", "version": "7.1.0",
"resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.0.6.tgz", "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-7.1.0.tgz",
"integrity": "sha512-7SVO4h+QIdMq6XcqIqrNte3gS5MzCCKZdsq9DO4PJziBFNYzP3PGFbDjgadDb//MCahzgjCxvQ/O2wa7kx9o4w==", "integrity": "sha512-5fJMWEmikSYu0nv/flMc475MhGbB7TSPd/2IpFV4I4rMklboCH2rQjYY5kKiYGHqUF9gvaambupcJFFG9dvReg==",
"dev": true, "dev": true,
"requires": { "requires": {
"ansi-escapes": "^4.2.1", "ansi-escapes": "^4.2.1",
@ -7140,12 +7142,12 @@
"dev": true "dev": true
}, },
"json5": { "json5": {
"version": "2.1.1", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.1.1.tgz", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.2.tgz",
"integrity": "sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==", "integrity": "sha512-MoUOQ4WdiN3yxhm7NEVJSJrieAo5hNSLQ5sj05OTRHPL9HOBy8u4Bu88jsC1jvqAdN+E1bJmsUcZH+1HQxliqQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"minimist": "^1.2.0" "minimist": "^1.2.5"
} }
}, },
"jsonexport": { "jsonexport": {
@ -7583,15 +7585,15 @@
"dev": true "dev": true
}, },
"linkinator": { "linkinator": {
"version": "2.0.3", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/linkinator/-/linkinator-2.0.3.tgz", "resolved": "https://registry.npmjs.org/linkinator/-/linkinator-2.0.4.tgz",
"integrity": "sha512-K1AhPAydXL9l4YX0o/FEYQUcBmjQQon3P6xzEZeZvQD1HSpa6FrAVrqZMxaEaDXUc8N1Y7vCmAKKNC6TX+2a5g==", "integrity": "sha512-5CJbZ2CWa2uK/YWvqtglzGuiy8ccYmoCCh+QEmwLa70KDSaphu5/DDbOlPHOSsy5ExN7fnaWtpatmnJi929pBA==",
"dev": true, "dev": true,
"requires": { "requires": {
"chalk": "^3.0.0", "chalk": "^3.0.0",
"cheerio": "^1.0.0-rc.2", "cheerio": "^1.0.0-rc.2",
"finalhandler": "^1.1.2", "finalhandler": "^1.1.2",
"gaxios": "^2.0.1", "gaxios": "^3.0.0",
"jsonexport": "^2.4.1", "jsonexport": "^2.4.1",
"meow": "^6.0.0", "meow": "^6.0.0",
"p-queue": "^6.2.1", "p-queue": "^6.2.1",
@ -8170,9 +8172,9 @@
} }
}, },
"minimist": { "minimist": {
"version": "1.2.0", "version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
"dev": true "dev": true
}, },
"minimist-options": { "minimist-options": {
@ -8209,20 +8211,12 @@
} }
}, },
"mkdirp": { "mkdirp": {
"version": "0.5.1", "version": "0.5.3",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.3.tgz",
"integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "integrity": "sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==",
"dev": true, "dev": true,
"requires": { "requires": {
"minimist": "0.0.8" "minimist": "^1.2.5"
},
"dependencies": {
"minimist": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
"integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=",
"dev": true
}
} }
}, },
"ms": { "ms": {
@ -8346,9 +8340,9 @@
} }
}, },
"node-releases": { "node-releases": {
"version": "1.1.50", "version": "1.1.52",
"resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.50.tgz", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-1.1.52.tgz",
"integrity": "sha512-lgAmPv9eYZ0bGwUYAKlr8MG6K4CvWliWqnkcT2P8mMAgVrH3lqfBPorFlxiG1pHQnqmavJZ9vbMXUTNyMLbrgQ==", "integrity": "sha512-snSiT1UypkgGt2wxPqS6ImEUICbNCMb31yaxWrOLXjhlt2z2/IBpaOxzONExqSm4y5oLnAqjjRWu+wsDzK5yNQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"semver": "^6.3.0" "semver": "^6.3.0"
@ -10725,24 +10719,24 @@
"dev": true "dev": true
}, },
"regenerate-unicode-properties": { "regenerate-unicode-properties": {
"version": "8.1.0", "version": "8.2.0",
"resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz", "resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.2.0.tgz",
"integrity": "sha512-LGZzkgtLY79GeXLm8Dp0BVLdQlWICzBnJz/ipWUgo59qBaZ+BHtq51P2q1uVZlppMuUAT37SDk39qUbjTWB7bA==", "integrity": "sha512-F9DjY1vKLo/tPePDycuH3dn9H1OTPIkVD9Kz4LODu+F2C75mgjAJ7x/gwy6ZcSNRAAkhNlJSOHRe8k3p+K9WhA==",
"dev": true, "dev": true,
"requires": { "requires": {
"regenerate": "^1.4.0" "regenerate": "^1.4.0"
} }
}, },
"regenerator-runtime": { "regenerator-runtime": {
"version": "0.13.4", "version": "0.13.5",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.4.tgz", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz",
"integrity": "sha512-plpwicqEzfEyTQohIKktWigcLzmNStMGwbOUbykx51/29Z3JOGYldaaNGK7ngNXV+UcoqvIMmloZ48Sr74sd+g==", "integrity": "sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==",
"dev": true "dev": true
}, },
"regenerator-transform": { "regenerator-transform": {
"version": "0.14.2", "version": "0.14.3",
"resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.2.tgz", "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.14.3.tgz",
"integrity": "sha512-V4+lGplCM/ikqi5/mkkpJ06e9Bujq1NFmNLvsCs56zg3ZbzrnUzAtizZ24TXxtRX/W2jcdScwQCnbL0CICTFkQ==", "integrity": "sha512-zXHNKJspmONxBViAb3ZUmFoFPnTBs3zFhCEZJiwp/gkNzxVbTqNJVjYKx6Qk1tQ1P4XLf4TbH9+KBB7wGoAaUw==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/runtime": "^7.8.4", "@babel/runtime": "^7.8.4",
@ -10773,17 +10767,17 @@
"dev": true "dev": true
}, },
"regexpu-core": { "regexpu-core": {
"version": "4.6.0", "version": "4.7.0",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.6.0.tgz", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.7.0.tgz",
"integrity": "sha512-YlVaefl8P5BnFYOITTNzDvan1ulLOiXJzCNZxduTIosN17b87h3bvG9yHMoHaRuo88H4mQ06Aodj5VtYGGGiTg==", "integrity": "sha512-TQ4KXRnIn6tz6tjnrXEkD/sshygKH/j5KzK86X8MkeHyZ8qst/LZ89j3X4/8HEIfHANTFIP/AbXakeRhWIl5YQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"regenerate": "^1.4.0", "regenerate": "^1.4.0",
"regenerate-unicode-properties": "^8.1.0", "regenerate-unicode-properties": "^8.2.0",
"regjsgen": "^0.5.0", "regjsgen": "^0.5.1",
"regjsparser": "^0.6.0", "regjsparser": "^0.6.4",
"unicode-match-property-ecmascript": "^1.0.4", "unicode-match-property-ecmascript": "^1.0.4",
"unicode-match-property-value-ecmascript": "^1.1.0" "unicode-match-property-value-ecmascript": "^1.2.0"
} }
}, },
"registry-auth-token": { "registry-auth-token": {
@ -10811,9 +10805,9 @@
"dev": true "dev": true
}, },
"regjsparser": { "regjsparser": {
"version": "0.6.3", "version": "0.6.4",
"resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.3.tgz", "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.4.tgz",
"integrity": "sha512-8uZvYbnfAtEm9Ab8NTb3hdLwL4g/LQzEYP7Xs27T96abJCCE2d6r3cPZPQEsLKy0vRSGVNG+/zVGtLr86HQduA==", "integrity": "sha512-64O87/dPDgfk8/RQqC4gkZoGyyWFIEUTTh80CU6CWuK5vkCGyekIx+oKcEIYtP/RAxSQltCZHCNu/mdd7fqlJw==",
"dev": true, "dev": true,
"requires": { "requires": {
"jsesc": "~0.5.0" "jsesc": "~0.5.0"
@ -11048,9 +11042,9 @@
} }
}, },
"rollup": { "rollup": {
"version": "2.0.6", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.0.6.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.1.0.tgz",
"integrity": "sha512-P42IlI6a/bxh52ed8hEXXe44LcHfep2f26OZybMJPN1TTQftibvQEl3CWeOmJrzqGbFxOA000QXDWO9WJaOQpA==", "integrity": "sha512-gfE1455AEazVVTJoeQtcOq/U6GSxwoj4XPSWVsuWmgIxj7sBQNLDOSA82PbdMe+cP8ql8fR1jogPFe8Wg8g4SQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"fsevents": "~2.1.2" "fsevents": "~2.1.2"
@ -12745,9 +12739,9 @@
} }
}, },
"stylelint-scss": { "stylelint-scss": {
"version": "3.14.2", "version": "3.15.0",
"resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-3.14.2.tgz", "resolved": "https://registry.npmjs.org/stylelint-scss/-/stylelint-scss-3.15.0.tgz",
"integrity": "sha512-59/BkIEWyFoORiejDIQB2P2kmg0KcqMn7wtj1y5sRvS4N+Qh+Ng3hbKelOzgS+OM2Ezbai0uEev8xckXxkh9TQ==", "integrity": "sha512-c6poL7nsU5XVXxl94jl+RY2Rf3CFfMuL8kWp9PfvDi4A7Op30KGAiIfB5Co1RFKnpIOkzz44EO8ur1T9DEl5mA==",
"dev": true, "dev": true,
"requires": { "requires": {
"lodash": "^4.17.15", "lodash": "^4.17.15",
@ -12857,9 +12851,9 @@
} }
}, },
"tar-stream": { "tar-stream": {
"version": "2.1.1", "version": "2.1.2",
"resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.1.tgz", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.1.2.tgz",
"integrity": "sha512-GZjLk64XcE/58qwIc1ZfXGqTSE4OutPMEkfBE/oh9eJ4x1eMRjYkgrLrav7PzddpvIpSJSGi8FgNNYXdB9Vumg==", "integrity": "sha512-UaF6FoJ32WqALZGOIAApXx+OdxhekNMChu6axLJR85zMMjXKWFGjbIRe+J6P4UnRGg9rAwWvbTT0oI7hD/Un7Q==",
"dev": true, "dev": true,
"requires": { "requires": {
"bl": "^4.0.1", "bl": "^4.0.1",
@ -12889,9 +12883,9 @@
"dev": true "dev": true
}, },
"terser": { "terser": {
"version": "4.6.6", "version": "4.6.7",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.6.6.tgz", "resolved": "https://registry.npmjs.org/terser/-/terser-4.6.7.tgz",
"integrity": "sha512-4lYPyeNmstjIIESr/ysHg2vUPRGf2tzF9z2yYwnowXVuVzLEamPN1Gfrz7f8I9uEPuHcbFlW4PLIAsJoxXyJ1g==", "integrity": "sha512-fmr7M1f7DBly5cX2+rFDvmGBAaaZyPrHYK4mMdHEDAdNTqXSZgSOfqsfGq2HqPGT/1V0foZZuCZFx8CHKgAk3g==",
"dev": true, "dev": true,
"requires": { "requires": {
"commander": "^2.20.0", "commander": "^2.20.0",
@ -13207,15 +13201,15 @@
} }
}, },
"unicode-match-property-value-ecmascript": { "unicode-match-property-value-ecmascript": {
"version": "1.1.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.1.0.tgz", "resolved": "https://registry.npmjs.org/unicode-match-property-value-ecmascript/-/unicode-match-property-value-ecmascript-1.2.0.tgz",
"integrity": "sha512-hDTHvaBk3RmFzvSl0UVrUmC3PuW9wKVnpoUDYH0JDkSIovzw+J5viQmeYHxVSBptubnr7PbH2e0fnpDRQnQl5g==", "integrity": "sha512-wjuQHGQVofmSJv1uVISKLE5zO2rNGzM/KCYZch/QQvez7C1hUhBIuZ701fYXExuufJFMPhv2SyL8CyoIfMLbIQ==",
"dev": true "dev": true
}, },
"unicode-property-aliases-ecmascript": { "unicode-property-aliases-ecmascript": {
"version": "1.0.5", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.0.5.tgz", "resolved": "https://registry.npmjs.org/unicode-property-aliases-ecmascript/-/unicode-property-aliases-ecmascript-1.1.0.tgz",
"integrity": "sha512-L5RAqCfXqAwR3RriF8pM0lU0w4Ryf/GgzONwi6KnL1taJQa7x1TCxdJnILX59WIGOwR57IVxn7Nej0fz1Ny6fw==", "integrity": "sha512-PqSoPh/pWetQ2phoj5RLiaqIk4kCNwoV3CI+LfGmWLKI3rE3kl1h59XpX2BjgDrmbxD9ARtQobPGU1SguCYuQg==",
"dev": true "dev": true
}, },
"unified": { "unified": {
@ -13918,18 +13912,18 @@
"dev": true "dev": true
}, },
"yaml": { "yaml": {
"version": "1.7.2", "version": "1.8.2",
"resolved": "https://registry.npmjs.org/yaml/-/yaml-1.7.2.tgz", "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.8.2.tgz",
"integrity": "sha512-qXROVp90sb83XtAoqE8bP9RwAkTTZbugRUTm5YeFCBfNRPEp2YzTeqWiz7m5OORHzEvrA/qcGS8hp/E+MMROYw==", "integrity": "sha512-omakb0d7FjMo3R1D2EbTKVIk6dAVLRxFXdLZMEUToeAvuqgG/YuHMuQOZ5fgk+vQ8cx+cnGKwyg+8g8PNT0xQg==",
"dev": true, "dev": true,
"requires": { "requires": {
"@babel/runtime": "^7.6.3" "@babel/runtime": "^7.8.7"
} }
}, },
"yargs": { "yargs": {
"version": "15.1.0", "version": "15.3.1",
"resolved": "https://registry.npmjs.org/yargs/-/yargs-15.1.0.tgz", "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.3.1.tgz",
"integrity": "sha512-T39FNN1b6hCW4SOIk1XyTOWxtXdcen0t+XYrysQmChzSipvhBO8Bj0nK1ozAasdk24dNWuMZvr4k24nz+8HHLg==", "integrity": "sha512-92O1HWEjw27sBfgmXiixJWT5hRBp2eobqXicLtPBIDBhYB+1HpwZlXmbW2luivBJHBzki+7VyCLRtAkScbTBQA==",
"dev": true, "dev": true,
"requires": { "requires": {
"cliui": "^6.0.0", "cliui": "^6.0.0",
@ -13942,7 +13936,7 @@
"string-width": "^4.2.0", "string-width": "^4.2.0",
"which-module": "^2.0.0", "which-module": "^2.0.0",
"y18n": "^4.0.0", "y18n": "^4.0.0",
"yargs-parser": "^16.1.0" "yargs-parser": "^18.1.1"
}, },
"dependencies": { "dependencies": {
"ansi-regex": { "ansi-regex": {
@ -14025,6 +14019,16 @@
"requires": { "requires": {
"ansi-regex": "^5.0.0" "ansi-regex": "^5.0.0"
} }
},
"yargs-parser": {
"version": "18.1.1",
"resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.1.tgz",
"integrity": "sha512-KRHEsOM16IX7XuLnMOqImcPNbLVXMNHYAoFc3BKR8Ortl5gzDbtXvvEoGx9imk5E+X1VeNKNlcHr8B8vi+7ipA==",
"dev": true,
"requires": {
"camelcase": "^5.0.0",
"decamelize": "^1.2.0"
}
} }
} }
}, },

View File

@ -37,9 +37,9 @@
"js-compile-plugins": "node build/build-plugins.js", "js-compile-plugins": "node build/build-plugins.js",
"js-lint": "eslint --cache --cache-location .cache/.eslintcache --report-unused-disable-directives .", "js-lint": "eslint --cache --cache-location .cache/.eslintcache --report-unused-disable-directives .",
"js-minify": "npm-run-all --parallel js-minify-*", "js-minify": "npm-run-all --parallel js-minify-*",
"js-minify-standalone": "terser --compress typeofs=false --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.js.map,includeSources,url=bootstrap.min.js.map\" --output dist/js/bootstrap.min.js dist/js/bootstrap.js", "js-minify-standalone": "terser --compress --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.js.map,includeSources,url=bootstrap.min.js.map\" --output dist/js/bootstrap.min.js dist/js/bootstrap.js",
"js-minify-standalone-esm": "terser --compress --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.esm.js.map,includeSources,url=bootstrap.esm.min.js.map\" --output dist/js/bootstrap.esm.min.js dist/js/bootstrap.esm.js", "js-minify-standalone-esm": "terser --compress --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.esm.js.map,includeSources,url=bootstrap.esm.min.js.map\" --output dist/js/bootstrap.esm.min.js dist/js/bootstrap.esm.js",
"js-minify-bundle": "terser --compress typeofs=false --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.bundle.js.map,includeSources,url=bootstrap.bundle.min.js.map\" --output dist/js/bootstrap.bundle.min.js dist/js/bootstrap.bundle.js", "js-minify-bundle": "terser --compress --mangle --comments \"/^!/\" --source-map \"content=dist/js/bootstrap.bundle.js.map,includeSources,url=bootstrap.bundle.min.js.map\" --output dist/js/bootstrap.bundle.min.js dist/js/bootstrap.bundle.js",
"js-test": "npm-run-all --parallel js-test-karma js-test-integration", "js-test": "npm-run-all --parallel js-test-karma js-test-integration",
"js-debug": "cross-env DEBUG=true karma start js/tests/karma.conf.js", "js-debug": "cross-env DEBUG=true karma start js/tests/karma.conf.js",
"js-test-karma": "karma start js/tests/karma.conf.js", "js-test-karma": "karma start js/tests/karma.conf.js",
@ -118,21 +118,21 @@
"karma-jasmine": "^3.1.1", "karma-jasmine": "^3.1.1",
"karma-jasmine-html-reporter": "^1.5.2", "karma-jasmine-html-reporter": "^1.5.2",
"karma-rollup-preprocessor": "^7.0.3", "karma-rollup-preprocessor": "^7.0.3",
"linkinator": "^2.0.3", "linkinator": "^2.0.4",
"lockfile-lint": "^4.1.0", "lockfile-lint": "^4.1.0",
"node-sass": "^4.13.1", "node-sass": "^4.13.1",
"nodemon": "^2.0.2", "nodemon": "^2.0.2",
"npm-run-all": "^4.1.5", "npm-run-all": "^4.1.5",
"popper.js": "^1.16.0", "popper.js": "^1.16.0",
"postcss-cli": "^7.1.0", "postcss-cli": "^7.1.0",
"rollup": "^2.0.6", "rollup": "^2.1.0",
"rollup-plugin-babel": "^4.4.0", "rollup-plugin-babel": "^4.4.0",
"rollup-plugin-istanbul": "^2.0.1", "rollup-plugin-istanbul": "^2.0.1",
"serve": "^11.3.0", "serve": "^11.3.0",
"shelljs": "^0.8.3", "shelljs": "^0.8.3",
"stylelint": "^13.2.1", "stylelint": "^13.2.1",
"stylelint-config-twbs-bootstrap": "^2.0.1", "stylelint-config-twbs-bootstrap": "^2.0.1",
"terser": "^4.6.6", "terser": "^4.6.7",
"vnu-jar": "20.3.16" "vnu-jar": "20.3.16"
}, },
"files": [ "files": [

View File

@ -17,27 +17,13 @@
padding-left: $breadcrumb-item-padding-x; padding-left: $breadcrumb-item-padding-x;
&::before { &::before {
display: inline-block; // Suppress underlining of the separator in modern browsers display: inline-block; // Suppress underlining of the separator
padding-right: $breadcrumb-item-padding-x; padding-right: $breadcrumb-item-padding-x;
color: $breadcrumb-divider-color; color: $breadcrumb-divider-color;
content: escape-svg($breadcrumb-divider); content: escape-svg($breadcrumb-divider);
} }
} }
// IE9-11 hack to properly handle hyperlink underlines for breadcrumbs built
// without `<ul>`s. The `::before` pseudo-element generates an element
// *within* the .breadcrumb-item and thereby inherits the `text-decoration`.
//
// To trick IE into suppressing the underline, we give the pseudo-element an
// underline and then immediately remove it.
+ .breadcrumb-item:hover::before {
text-decoration: underline;
}
// stylelint-disable-next-line no-duplicate-selectors
+ .breadcrumb-item:hover::before {
text-decoration: none;
}
&.active { &.active {
color: $breadcrumb-active-color; color: $breadcrumb-active-color;
} }

View File

@ -36,9 +36,6 @@
// Enable `flex-grow: 1` for decks and groups so that card blocks take up // Enable `flex-grow: 1` for decks and groups so that card blocks take up
// as much space as possible, ensuring footers are aligned to the bottom. // as much space as possible, ensuring footers are aligned to the bottom.
flex: 1 1 auto; flex: 1 1 auto;
// Workaround for the image size bug in IE
// See: https://github.com/twbs/bootstrap/pull/28855
min-height: 1px;
padding: $card-spacer-y $card-spacer-x; padding: $card-spacer-y $card-spacer-x;
color: $card-color; color: $card-color;
} }
@ -135,7 +132,6 @@
.card-img, .card-img,
.card-img-top, .card-img-top,
.card-img-bottom { .card-img-bottom {
flex-shrink: 0; // For IE: https://github.com/twbs/bootstrap/issues/29396
width: 100%; // Required because we use flexbox and this inherently applies align-self: stretch width: 100%; // Required because we use flexbox and this inherently applies align-self: stretch
} }

View File

@ -56,11 +56,9 @@
} }
.modal-dialog-scrollable { .modal-dialog-scrollable {
display: flex; // IE10/11
max-height: subtract(100%, $modal-dialog-margin * 2); max-height: subtract(100%, $modal-dialog-margin * 2);
.modal-content { .modal-content {
max-height: subtract(100vh, $modal-dialog-margin * 2); // IE10/11
overflow: hidden; overflow: hidden;
} }
@ -78,29 +76,6 @@
display: flex; display: flex;
align-items: center; align-items: center;
min-height: subtract(100%, $modal-dialog-margin * 2); min-height: subtract(100%, $modal-dialog-margin * 2);
// Ensure `modal-dialog-centered` extends the full height of the view (IE10/11)
&::before {
display: block; // IE10
height: subtract(100vh, $modal-dialog-margin * 2);
height: min-content; // Reset height to 0 except on IE
content: "";
}
// Ensure `.modal-body` shows scrollbar (IE10/11)
&.modal-dialog-scrollable {
flex-direction: column;
justify-content: center;
height: 100%;
.modal-content {
max-height: none;
}
&::before {
content: none;
}
}
} }
// Actual modal // Actual modal
@ -206,19 +181,10 @@
.modal-dialog-scrollable { .modal-dialog-scrollable {
max-height: subtract(100%, $modal-dialog-margin-y-sm-up * 2); max-height: subtract(100%, $modal-dialog-margin-y-sm-up * 2);
.modal-content {
max-height: subtract(100vh, $modal-dialog-margin-y-sm-up * 2);
}
} }
.modal-dialog-centered { .modal-dialog-centered {
min-height: subtract(100%, $modal-dialog-margin-y-sm-up * 2); min-height: subtract(100%, $modal-dialog-margin-y-sm-up * 2);
&::before {
height: subtract(100vh, $modal-dialog-margin-y-sm-up * 2);
height: min-content;
}
} }
.modal-content { .modal-content {

View File

@ -107,10 +107,10 @@
// the default flexbox row orientation. Requires the use of `flex-wrap: wrap` // the default flexbox row orientation. Requires the use of `flex-wrap: wrap`
// on the `.navbar` parent. // on the `.navbar` parent.
.navbar-collapse { .navbar-collapse {
flex: 1 0 100%;
// For always expanded or extra full navbars, ensure content aligns itself // For always expanded or extra full navbars, ensure content aligns itself
// properly vertically. Can be easily overridden with flex utilities. // properly vertically. Can be easily overridden with flex utilities.
align-items: center; align-items: center;
width: 100%;
} }
// Button for toggling the navbar when in its collapsed state // Button for toggling the navbar when in its collapsed state
@ -173,9 +173,6 @@
.navbar-collapse { .navbar-collapse {
display: flex !important; // stylelint-disable-line declaration-no-important display: flex !important; // stylelint-disable-line declaration-no-important
// Changes flex-bases to auto because of an IE10 bug
flex-basis: auto;
} }
.navbar-toggler { .navbar-toggler {

View File

@ -34,7 +34,7 @@
// //
// 1. Remove the margin in all browsers. // 1. Remove the margin in all browsers.
// 2. As a best practice, apply a default `background-color`. // 2. As a best practice, apply a default `background-color`.
// 3. Prevent adjustments of font size after orientation changes in IE on Windows Phone and in iOS. // 3. Prevent adjustments of font size after orientation changes in iOS.
// 4. Change the default tap highlight to be completely transparent in iOS. // 4. Change the default tap highlight to be completely transparent in iOS.
body { body {
@ -145,7 +145,7 @@ p {
// Abbreviations // Abbreviations
// //
// 1. Duplicate behavior to the data-* attribute for our tooltip plugin // 1. Duplicate behavior to the data-* attribute for our tooltip plugin
// 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. // 2. Add the correct text decoration in Chrome, Edge, Opera, and Safari.
// 3. Add explicit cursor to indicate changed behavior. // 3. Add explicit cursor to indicate changed behavior.
// 4. Prevent the text-decoration to be skipped. // 4. Prevent the text-decoration to be skipped.
@ -344,15 +344,8 @@ figure {
// Images and content // Images and content
img { img,
vertical-align: middle;
}
// 1. Workaround for the SVG overflow bug in IE 11 is still required.
// See https://github.com/twbs/bootstrap/issues/26878
svg { svg {
overflow: hidden; // 1
vertical-align: middle; vertical-align: middle;
} }
@ -399,7 +392,7 @@ button {
border-radius: 0; border-radius: 0;
} }
// Work around a Firefox/IE bug where the transparent `button` background // Work around a Firefox bug where the transparent `button` background
// results in a loss of the default `button` focus styles. // results in a loss of the default `button` focus styles.
// Credit https://github.com/suitcss/base/ // Credit https://github.com/suitcss/base/
@ -487,12 +480,10 @@ input[type="month"] {
-webkit-appearance: textfield; -webkit-appearance: textfield;
} }
// 1. Remove the default vertical scrollbar in IE. // 1. Textareas should really only resize vertically so they don't break their (horizontal) containers.
// 2. Textareas should really only resize vertically so they don't break their (horizontal) containers.
textarea { textarea {
overflow: auto; // 1 resize: vertical; // 1
resize: vertical; // 2
} }
// 1. Browsers set a default `min-width: min-content;` on fieldsets, // 1. Browsers set a default `min-width: min-content;` on fieldsets,
@ -510,8 +501,7 @@ fieldset {
} }
// 1. By using `float: left`, the legend will behave like a block element // 1. By using `float: left`, the legend will behave like a block element
// 2. Correct the color inheritance from `fieldset` elements in IE. // 2. Correct the text wrapping in Edge.
// 3. Correct the text wrapping in Edge and IE.
legend { legend {
float: left; // 1 float: left; // 1
@ -521,8 +511,7 @@ legend {
@include font-size($legend-font-size); @include font-size($legend-font-size);
font-weight: $legend-font-weight; font-weight: $legend-font-weight;
line-height: inherit; line-height: inherit;
color: inherit; // 2 white-space: normal; // 2
white-space: normal; // 3
} }
// Fix height of inputs with a type of datetime-local, date, month, week, or time // Fix height of inputs with a type of datetime-local, date, month, week, or time
@ -590,22 +579,6 @@ summary {
} }
// Template
//
// Add the correct display for template & main in IE 11
template {
display: none;
}
// Main
main {
display: block;
}
// Progress // Progress
// //
// Add the correct vertical alignment in Chrome, Firefox, and Opera. // Add the correct vertical alignment in Chrome, Firefox, and Opera.

View File

@ -720,7 +720,7 @@ $form-range-thumb-border: 0 !default;
$form-range-thumb-border-radius: 1rem !default; $form-range-thumb-border-radius: 1rem !default;
$form-range-thumb-box-shadow: 0 .1rem .25rem rgba($black, .1) !default; $form-range-thumb-box-shadow: 0 .1rem .25rem rgba($black, .1) !default;
$form-range-thumb-focus-box-shadow: 0 0 0 1px $body-bg, $input-focus-box-shadow !default; $form-range-thumb-focus-box-shadow: 0 0 0 1px $body-bg, $input-focus-box-shadow !default;
$form-range-thumb-focus-box-shadow-width: $input-focus-width !default; // For focus box shadow issue in IE/Edge $form-range-thumb-focus-box-shadow-width: $input-focus-width !default; // For focus box shadow issue in Edge
$form-range-thumb-active-bg: lighten($component-active-bg, 35%) !default; $form-range-thumb-active-bg: lighten($component-active-bg, 35%) !default;
$form-range-thumb-disabled-bg: $gray-500 !default; $form-range-thumb-disabled-bg: $gray-500 !default;
$form-range-thumb-transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default; $form-range-thumb-transition: background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;

View File

@ -22,12 +22,6 @@
@include box-shadow($input-box-shadow); @include box-shadow($input-box-shadow);
@include transition($input-transition); @include transition($input-transition);
// Unstyle the caret on `<select>`s in IE10+.
&::-ms-expand {
background-color: transparent;
border: 0;
}
// Customize the `:focus` state to imitate native WebKit styles. // Customize the `:focus` state to imitate native WebKit styles.
&:focus { &:focus {
color: $input-focus-color; color: $input-focus-color;

View File

@ -11,13 +11,6 @@
margin: 0; margin: 0;
opacity: 0; opacity: 0;
// Separate rules for :focus and :focus-within as IE doesn't support the latter, and
// thus ignores the entire ruleset. See https://github.com/twbs/bootstrap/pull/29036.
&:focus ~ .form-file-label {
border-color: $form-file-focus-border-color;
box-shadow: $form-file-focus-box-shadow;
}
&:focus-within ~ .form-file-label { &:focus-within ~ .form-file-label {
border-color: $form-file-focus-border-color; border-color: $form-file-focus-border-color;
box-shadow: $form-file-focus-box-shadow; box-shadow: $form-file-focus-box-shadow;

View File

@ -37,7 +37,7 @@
// For visual consistency with other platforms/browsers, // For visual consistency with other platforms/browsers,
// suppress the default white text on blue background highlight given to // suppress the default white text on blue background highlight given to
// the selected option text when the (still closed) <select> receives focus // the selected option text when the (still closed) <select> receives focus
// in IE and (under certain conditions) Edge. // in Edge.
// See https://github.com/twbs/bootstrap/issues/19398. // See https://github.com/twbs/bootstrap/issues/19398.
color: $input-color; color: $input-color;
background-color: $input-bg; background-color: $input-bg;
@ -57,11 +57,6 @@
border-color: $form-select-disabled-border-color; border-color: $form-select-disabled-border-color;
} }
// Hides the default caret in IE11
&::-ms-expand {
display: none;
}
// Remove outline from select box in FF // Remove outline from select box in FF
&:-moz-focusring { &:-moz-focusring {
color: transparent; color: transparent;

View File

@ -463,7 +463,7 @@ In addition to styling the content within cards, Bootstrap includes a few option
### Card groups ### Card groups
Use card groups to render cards as a single, attached element with equal width and height columns. Card groups use `display: flex;` to achieve their uniform sizing. Use card groups to render cards as a single, attached element with equal width and height columns. Card groups start off stacked and use `display: flex;` to become attached with uniform dimensions starting at the `sm` breakpoint.
{{< example >}} {{< example >}}
<div class="card-group"> <div class="card-group">

View File

@ -459,15 +459,15 @@ Use contextual classes to color table rows or individual cells.
<td>Cell</td> <td>Cell</td>
<td>Cell</td> <td>Cell</td>
</tr> </tr>
{{< table.inline >}} {{< table.inline >}}
{{- range (index $.Site.Data "theme-colors") }} {{- range (index $.Site.Data "theme-colors") }}
<tr class="table-{{ .name }}"> <tr class="table-{{ .name }}">
<th scope="row">{{ .name | title }}</th> <th scope="row">{{ .name | title }}</th>
<td>Cell</td> <td>Cell</td>
<td>Cell</td> <td>Cell</td>
</tr> </tr>
{{- end -}} {{- end -}}
{{< /table.inline >}} {{< /table.inline >}}
</tbody> </tbody>
</table> </table>
</div> </div>

View File

@ -35,7 +35,7 @@ title: Album example
</div> </div>
</header> </header>
<main role="main"> <main>
<section class="py-5 text-center container"> <section class="py-5 text-center container">
<div class="row py-lg-5"> <div class="row py-lg-5">

View File

@ -82,7 +82,7 @@ include_js: false
</div> </div>
</div> </div>
<main role="main" class="container"> <main class="container">
<div class="row"> <div class="row">
<div class="col-md-8 blog-main"> <div class="col-md-8 blog-main">
<h3 class="pb-4 mb-4 font-italic border-bottom"> <h3 class="pb-4 mb-4 font-italic border-bottom">

View File

@ -33,7 +33,7 @@ extra_css:
</nav> </nav>
</header> </header>
<main role="main"> <main>
<div id="myCarousel" class="carousel slide" data-ride="carousel"> <div id="myCarousel" class="carousel slide" data-ride="carousel">
<ol class="carousel-indicators"> <ol class="carousel-indicators">

View File

@ -20,7 +20,7 @@ include_js: false
</div> </div>
</header> </header>
<main role="main" class="px-3"> <main class="px-3">
<h1>Cover your page.</h1> <h1>Cover your page.</h1>
<p class="lead">Cover is a one-page template for building simple and beautiful home pages. Download, edit the text, and add your own fullscreen background photo to make it your own.</p> <p class="lead">Cover is a one-page template for building simple and beautiful home pages. Download, edit the text, and add your own fullscreen background photo to make it your own.</p>
<p class="lead"> <p class="lead">

View File

@ -37,13 +37,6 @@ body {
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */ overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
} }
@supports ((position: -webkit-sticky) or (position: sticky)) {
.sidebar-sticky {
position: -webkit-sticky;
position: sticky;
}
}
.sidebar .nav-link { .sidebar .nav-link {
font-weight: 500; font-weight: 500;
color: #333; color: #333;

View File

@ -27,7 +27,7 @@ extra_js:
<div class="container-fluid"> <div class="container-fluid">
<div class="row"> <div class="row">
<nav id="sidebarMenu" class="col-md-2 d-md-block bg-light sidebar collapse"> <nav id="sidebarMenu" class="col-md-2 d-md-block bg-light sidebar collapse">
<div class="sidebar-sticky"> <div class="position-sticky">
<ul class="nav flex-column"> <ul class="nav flex-column">
<li class="nav-item"> <li class="nav-item">
<a class="nav-link active" aria-current="page" href="#"> <a class="nav-link active" aria-current="page" href="#">
@ -102,7 +102,7 @@ extra_js:
</div> </div>
</nav> </nav>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 px-md-4"> <main class="col-md-9 ml-sm-auto col-lg-10 px-md-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom"> <div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Dashboard</h1> <h1 class="h2">Dashboard</h1>
<div class="btn-toolbar mb-2 mb-md-0"> <div class="btn-toolbar mb-2 mb-md-0">

View File

@ -86,14 +86,3 @@ body {
color: #777; color: #777;
} }
} }
/* Fallback for IE
-------------------------------------------------- */
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
.form-label-group > label {
display: none;
}
.form-label-group input:-ms-input-placeholder {
color: #777;
}
}

View File

@ -31,7 +31,7 @@ extra_css:
</div> </div>
</nav> </nav>
<main role="main" class="container"> <main class="container">
<div class="bg-light p-5 rounded"> <div class="bg-light p-5 rounded">
<h1>Navbar example</h1> <h1>Navbar example</h1>
<p class="lead">This example is a quick exercise to illustrate how fixed to top navbar works. As you scroll, it will remain fixed to the top of your browsers viewport.</p> <p class="lead">This example is a quick exercise to illustrate how fixed to top navbar works. As you scroll, it will remain fixed to the top of your browsers viewport.</p>

View File

@ -31,7 +31,7 @@ extra_css:
</div> </div>
</nav> </nav>
<main role="main" class="container"> <main class="container">
<div class="bg-light p-5 rounded"> <div class="bg-light p-5 rounded">
<h1>Navbar example</h1> <h1>Navbar example</h1>
<p class="lead">This example is a quick exercise to illustrate how the top-aligned navbar works. As you scroll, this navbar remains in its original position and moves with the rest of the page.</p> <p class="lead">This example is a quick exercise to illustrate how the top-aligned navbar works. As you scroll, this navbar remains in its original position and moves with the rest of the page.</p>

View File

@ -365,7 +365,7 @@ extra_css:
</div> </div>
</nav> </nav>
<main role="main"> <main>
<div class="bg-light p-5 rounded"> <div class="bg-light p-5 rounded">
<div class="col-sm-8 mx-auto"> <div class="col-sm-8 mx-auto">
<h1>Navbar examples</h1> <h1>Navbar examples</h1>

View File

@ -63,7 +63,7 @@ body_class: "bg-light"
</nav> </nav>
</div> </div>
<main role="main" class="container"> <main class="container">
<div class="d-flex align-items-center p-3 my-3 text-white-50 bg-purple rounded shadow-sm"> <div class="d-flex align-items-center p-3 my-3 text-white-50 bg-purple rounded shadow-sm">
<img class="mr-3" src="/docs/{{< param docs_version >}}/assets/brand/bootstrap-outline.svg" alt="" width="48" height="48"> <img class="mr-3" src="/docs/{{< param docs_version >}}/assets/brand/bootstrap-outline.svg" alt="" width="48" height="48">
<div class="lh-1"> <div class="lh-1">

View File

@ -40,7 +40,7 @@ extra_css:
</div> </div>
</nav> </nav>
<main role="main" class="container"> <main class="container">
<div class="starter-template text-center py-5 px-3"> <div class="starter-template text-center py-5 px-3">
<h1>Bootstrap starter template</h1> <h1>Bootstrap starter template</h1>

View File

@ -37,7 +37,7 @@ body_class: "d-flex flex-column h-100"
</header> </header>
<!-- Begin page content --> <!-- Begin page content -->
<main role="main" class="flex-shrink-0"> <main class="flex-shrink-0">
<div class="container"> <div class="container">
<h1 class="mt-5">Sticky footer with fixed navbar</h1> <h1 class="mt-5">Sticky footer with fixed navbar</h1>
<p class="lead">Pin a footer to the bottom of the viewport in desktop browsers with this custom HTML and CSS. A fixed navbar has been added with <code class="small">padding-top: 60px;</code> on the <code class="small">main &gt; .container</code>.</p> <p class="lead">Pin a footer to the bottom of the viewport in desktop browsers with this custom HTML and CSS. A fixed navbar has been added with <code class="small">padding-top: 60px;</code> on the <code class="small">main &gt; .container</code>.</p>

View File

@ -9,7 +9,7 @@ include_js: false
--- ---
<!-- Begin page content --> <!-- Begin page content -->
<main role="main" class="flex-shrink-0"> <main class="flex-shrink-0">
<div class="container"> <div class="container">
<h1 class="mt-5">Sticky footer</h1> <h1 class="mt-5">Sticky footer</h1>
<p class="lead">Pin a footer to the bottom of the viewport in desktop browsers with this custom HTML and CSS.</p> <p class="lead">Pin a footer to the bottom of the viewport in desktop browsers with this custom HTML and CSS.</p>

View File

@ -78,8 +78,6 @@ If you want to have `<input readonly>` elements in your form styled as plain tex
## Color ## Color
Keep in mind color inputs are [not supported in IE](https://caniuse.com/#feat=input-color).
{{< example >}} {{< example >}}
<form> <form>
<label for="exampleColorInput">Color picker</label> <label for="exampleColorInput">Color picker</label>

View File

@ -8,7 +8,7 @@ toc: true
## Supported browsers ## Supported browsers
Bootstrap supports the **latest, stable releases** of all major browsers and platforms. On Windows, **we support Internet Explorer 11 / Microsoft Edge**. Bootstrap supports the **latest, stable releases** of all major browsers and platforms. This also includes the latest version of Legacy Edge (EdgeHTML layout engine).
Alternative browsers which use the latest version of WebKit, Blink, or Gecko, whether directly or via the platform's web view API, are not explicitly supported. However, Bootstrap should (in most cases) display and function correctly in these browsers as well. More specific support information is provided below. Alternative browsers which use the latest version of WebKit, Blink, or Gecko, whether directly or via the platform's web view API, are not explicitly supported. However, Bootstrap should (in most cases) display and function correctly in these browsers as well. More specific support information is provided below.
@ -64,7 +64,6 @@ Similarly, the latest versions of most desktop browsers are supported.
<th></th> <th></th>
<th>Chrome</th> <th>Chrome</th>
<th>Firefox</th> <th>Firefox</th>
<th>Internet Explorer</th>
<th>Microsoft Edge</th> <th>Microsoft Edge</th>
<th>Opera</th> <th>Opera</th>
<th>Safari</th> <th>Safari</th>
@ -75,7 +74,6 @@ Similarly, the latest versions of most desktop browsers are supported.
<th scope="row">Mac</th> <th scope="row">Mac</th>
<td>Supported</td> <td>Supported</td>
<td>Supported</td> <td>Supported</td>
<td class="text-muted">&mdash;</td>
<td>Supported</td> <td>Supported</td>
<td>Supported</td> <td>Supported</td>
<td>Supported</td> <td>Supported</td>
@ -84,7 +82,6 @@ Similarly, the latest versions of most desktop browsers are supported.
<th scope="row">Windows</th> <th scope="row">Windows</th>
<td>Supported</td> <td>Supported</td>
<td>Supported</td> <td>Supported</td>
<td>IE11 only</td>
<td>Supported</td> <td>Supported</td>
<td>Supported</td> <td>Supported</td>
<td class="text-muted">&mdash;</td> <td class="text-muted">&mdash;</td>
@ -94,11 +91,11 @@ Similarly, the latest versions of most desktop browsers are supported.
For Firefox, in addition to the latest normal stable release, we also support the latest [Extended Support Release (ESR)](https://www.mozilla.org/en-US/firefox/organizations/#faq) version of Firefox. For Firefox, in addition to the latest normal stable release, we also support the latest [Extended Support Release (ESR)](https://www.mozilla.org/en-US/firefox/organizations/#faq) version of Firefox.
Unofficially, Bootstrap should look and behave well enough in Chromium and Chrome for Linux, Firefox for Linux, and Internet Explorer 9, though they are not officially supported. Unofficially, Bootstrap should look and behave well enough in Chromium and Chrome for Linux, and Firefox for Linux, though they are not officially supported.
## Internet Explorer ## Internet Explorer
Internet Explorer 11 is supported; IE10 and down is not. Please be aware that some CSS3 properties and HTML5 elements are not fully supported in Internet Explorer, or require prefixed properties for full functionality. Visit [Can I use...](https://caniuse.com/) for details on browser support of CSS3 and HTML5 features. **If you require IE10 support, use Bootstrap 4.** Internet Explorer is not supported. **If you require Internet Explorer support, please use Bootstrap v4.**
## Modals and dropdowns on mobile ## Modals and dropdowns on mobile

View File

@ -221,7 +221,7 @@ You can also specify a base color with our color map functions:
#### Escape SVG #### Escape SVG
We use the `escape-svg` function to escape the `<`, `>` and `#` characters for SVG background images. These characters need to be escaped to properly render the background images in IE. We use the `escape-svg` function to escape the `<`, `>` and `#` characters for SVG background images.
#### Add and Subtract functions #### Add and Subtract functions

View File

@ -26,8 +26,6 @@ Position an element at the bottom of the viewport, from edge to edge. Be sure yo
Position an element at the top of the viewport, from edge to edge, but only after you scroll past it. The `.sticky-top` utility uses CSS's `position: sticky`, which isn't fully supported in all browsers. Position an element at the top of the viewport, from edge to edge, but only after you scroll past it. The `.sticky-top` utility uses CSS's `position: sticky`, which isn't fully supported in all browsers.
**IE11 and IE10 will render `position: sticky` as `position: relative`.** As such, we wrap the styles in a `@supports` query, limiting the stickiness to only browsers that can render it properly.
{{< highlight html >}} {{< highlight html >}}
<div class="sticky-top">...</div> <div class="sticky-top">...</div>
{{< /highlight >}} {{< /highlight >}}

View File

@ -520,7 +520,7 @@ In practice, here's how it looks. Note you can continue to use this with all oth
## Alignment ## Alignment
Use flexbox alignment utilities to vertically and horizontally align columns. **Internet Explorer 10-11 do not support vertical alignment of flex items when the flex container has a `min-height` as shown below.** [See Flexbugs #3 for more details.](https://github.com/philipwalton/flexbugs#flexbug-3) Use flexbox alignment utilities to vertically and horizontally align columns.
### Vertical alignment ### Vertical alignment

View File

@ -11,7 +11,7 @@ toc: true
See the browser and devices page for details on what is currently supported in Bootstrap 5. Since v4, here's what's changed to our browser support: See the browser and devices page for details on what is currently supported in Bootstrap 5. Since v4, here's what's changed to our browser support:
- Dropped support for Internet Explorer NN - Dropped support for Internet Explorer 10 and 11
- Dropped support for Firefox NN - MM - Dropped support for Firefox NN - MM
- Dropped support for Safari NN - Dropped support for Safari NN
- Dropped support for iOS Safari NN - Dropped support for iOS Safari NN

View File

@ -296,8 +296,6 @@ Responsive variations also exist for `flex-grow` and `flex-shrink`.
Flexbox can do some pretty awesome things when you mix flex alignments with auto margins. Shown below are three examples of controlling flex items via auto margins: default (no auto margin), pushing two items to the right (`.mr-auto`), and pushing two items to the left (`.ml-auto`). Flexbox can do some pretty awesome things when you mix flex alignments with auto margins. Shown below are three examples of controlling flex items via auto margins: default (no auto margin), pushing two items to the right (`.mr-auto`), and pushing two items to the left (`.ml-auto`).
**Unfortunately, IE10 and IE11 do not properly support auto margins on flex items whose parent has a non-default `justify-content` value.** [See this StackOverflow answer](https://stackoverflow.com/a/37535548) for more details.
{{< example >}} {{< example >}}
<div class="d-flex bd-highlight mb-3"> <div class="d-flex bd-highlight mb-3">
<div class="p-2 bd-highlight">Flex item</div> <div class="p-2 bd-highlight">Flex item</div>

View File

@ -8,7 +8,7 @@
{{ partial "docs-navbar" . }} {{ partial "docs-navbar" . }}
<main class="my-auto p-5" id="content" role="main"> <main class="my-auto p-5" id="content">
{{ .Content }} {{ .Content }}
</main> </main>

View File

@ -15,7 +15,7 @@
{{ partial "docs-sidebar" . }} {{ partial "docs-sidebar" . }}
</div> </div>
<main class="col-md-9 py-md-3 pl-md-5 bd-content" role="main"> <main class="col-md-9 py-md-3 pl-md-5 bd-content">
<h1 class="bd-title" id="content">{{ .Title | markdownify }}</h1> <h1 class="bd-title" id="content">{{ .Title | markdownify }}</h1>
<p class="bd-lead">{{ .Page.Params.Description | markdownify }}</p> <p class="bd-lead">{{ .Page.Params.Description | markdownify }}</p>
{{ partial "ads" . }} {{ partial "ads" . }}

View File

@ -19,7 +19,7 @@
</div> </div>
</header> </header>
<main class="bd-content py-5" id="content" role="main"> <main class="bd-content py-5" id="content">
<div class="container"> <div class="container">
{{ .Content }} {{ .Content }}
</div> </div>

View File

@ -1,3 +1,3 @@
##### Date & color input support ##### Date & color input support
Keep in mind date inputs are [not fully supported](https://caniuse.com/#feat=input-datetime) by IE and Safari. Color inputs also [lack support](https://caniuse.com/#feat=input-color) on IE. Keep in mind date inputs are [not fully supported](https://caniuse.com/#feat=input-datetime) by all browsers, namely Safari.

View File

@ -16,7 +16,7 @@
<a class="nav-link{{ if eq .Page.Title "Examples" }} active" aria-current="true{{ end }}" href="/docs/{{ .Site.Params.docs_version }}/examples/" onclick="ga('send', 'event', 'Navbar', 'Community links', 'Examples');">Examples</a> <a class="nav-link{{ if eq .Page.Title "Examples" }} active" aria-current="true{{ end }}" href="/docs/{{ .Site.Params.docs_version }}/examples/" onclick="ga('send', 'event', 'Navbar', 'Community links', 'Examples');">Examples</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ .Site.Params.icons }}" onclick="ga('send', 'event', 'Navbar', 'Community links', 'Icons');">Icons</a> <a class="nav-link" href="{{ .Site.Params.icons }}" onclick="ga('send', 'event', 'Navbar', 'Community links', 'Icons');" target="_blank" rel="noopener">Icons</a>
</li> </li>
<li class="nav-item"> <li class="nav-item">
<a class="nav-link" href="{{ .Site.Params.themes }}" onclick="ga('send', 'event', 'Navbar', 'Community links', 'Themes');" target="_blank" rel="noopener">Themes</a> <a class="nav-link" href="{{ .Site.Params.themes }}" onclick="ga('send', 'event', 'Navbar', 'Community links', 'Themes');" target="_blank" rel="noopener">Themes</a>

View File

@ -1,4 +1,4 @@
<main class="bd-masthead mb-3 mb-md-5 border-bottom" id="content" role="main"> <main class="bd-masthead mb-3 mb-md-5 border-bottom" id="content">
<div class="container"> <div class="container">
<div class="row"> <div class="row">
<div class="col-6 mx-auto col-md-4 order-md-2 col-lg-5"> <div class="col-6 mx-auto col-md-4 order-md-2 col-lg-5">