diff options
| author | Pierre-Denis Vanduynslager <[email protected]> | 2016-12-28 19:57:38 -0500 |
|---|---|---|
| committer | Pierre-Denis Vanduynslager <[email protected]> | 2016-12-28 19:57:38 -0500 |
| commit | 425d156df27fa6c18e979aa000bfe5a346ee3450 (patch) | |
| tree | 4157dfcbdf8334e9d9fb2bb239f4ae78706bbc71 /js/src | |
| parent | ab2fc63d08b8c53d6f29bcfd73b7f2d5ceaacacd (diff) | |
| parent | e1e621be046a4541a2fd36e445015ee44de3c67e (diff) | |
| download | bootstrap-425d156df27fa6c18e979aa000bfe5a346ee3450.tar.xz bootstrap-425d156df27fa6c18e979aa000bfe5a346ee3450.zip | |
Merge branch 'twbs/v4-dev' into dropdown-keyboard
Diffstat (limited to 'js/src')
| -rw-r--r-- | js/src/alert.js | 24 | ||||
| -rw-r--r-- | js/src/button.js | 14 | ||||
| -rw-r--r-- | js/src/carousel.js | 137 | ||||
| -rw-r--r-- | js/src/collapse.js | 72 | ||||
| -rw-r--r-- | js/src/dropdown.js | 89 | ||||
| -rw-r--r-- | js/src/modal.js | 98 | ||||
| -rw-r--r-- | js/src/popover.js | 22 | ||||
| -rw-r--r-- | js/src/scrollspy.js | 49 | ||||
| -rw-r--r-- | js/src/tab.js | 62 | ||||
| -rw-r--r-- | js/src/tooltip.js | 159 | ||||
| -rw-r--r-- | js/src/util.js | 41 |
11 files changed, 418 insertions, 349 deletions
diff --git a/js/src/alert.js b/js/src/alert.js index 4cb006ea3..884a5dec8 100644 --- a/js/src/alert.js +++ b/js/src/alert.js @@ -3,7 +3,7 @@ import Util from './util' /** * -------------------------------------------------------------------------- - * Bootstrap (v4.0.0-alpha.2): alert.js + * Bootstrap (v4.0.0-alpha.5): alert.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * -------------------------------------------------------------------------- */ @@ -18,7 +18,7 @@ const Alert = (($) => { */ const NAME = 'alert' - const VERSION = '4.0.0-alpha.2' + const VERSION = '4.0.0-alpha.5' const DATA_KEY = 'bs.alert' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' @@ -38,7 +38,7 @@ const Alert = (($) => { const ClassName = { ALERT : 'alert', FADE : 'fade', - IN : 'in' + SHOW : 'show' } @@ -67,8 +67,8 @@ const Alert = (($) => { close(element) { element = element || this._element - let rootElement = this._getRootElement(element) - let customEvent = this._triggerCloseEvent(rootElement) + const rootElement = this._getRootElement(element) + const customEvent = this._triggerCloseEvent(rootElement) if (customEvent.isDefaultPrevented()) { return @@ -86,8 +86,8 @@ const Alert = (($) => { // private _getRootElement(element) { - let selector = Util.getSelectorFromElement(element) - let parent = false + const selector = Util.getSelectorFromElement(element) + let parent = false if (selector) { parent = $(selector)[0] @@ -101,14 +101,14 @@ const Alert = (($) => { } _triggerCloseEvent(element) { - let closeEvent = $.Event(Event.CLOSE) + const closeEvent = $.Event(Event.CLOSE) $(element).trigger(closeEvent) return closeEvent } _removeElement(element) { - $(element).removeClass(ClassName.IN) + $(element).removeClass(ClassName.SHOW) if (!Util.supportsTransitionEnd() || !$(element).hasClass(ClassName.FADE)) { @@ -117,7 +117,7 @@ const Alert = (($) => { } $(element) - .one(Util.TRANSITION_END, $.proxy(this._destroyElement, this, element)) + .one(Util.TRANSITION_END, (event) => this._destroyElement(element, event)) .emulateTransitionEnd(TRANSITION_DURATION) } @@ -133,8 +133,8 @@ const Alert = (($) => { static _jQueryInterface(config) { return this.each(function () { - let $element = $(this) - let data = $element.data(DATA_KEY) + const $element = $(this) + let data = $element.data(DATA_KEY) if (!data) { data = new Alert(this) diff --git a/js/src/button.js b/js/src/button.js index 39e2b3974..45e1424ff 100644 --- a/js/src/button.js +++ b/js/src/button.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.0.0-alpha.2): button.js + * Bootstrap (v4.0.0-alpha.5): button.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * -------------------------------------------------------------------------- */ @@ -15,7 +15,7 @@ const Button = (($) => { */ const NAME = 'button' - const VERSION = '4.0.0-alpha.2' + const VERSION = '4.0.0-alpha.5' const DATA_KEY = 'bs.button' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' @@ -66,12 +66,12 @@ const Button = (($) => { toggle() { let triggerChangeEvent = true - let rootElement = $(this._element).closest( + const rootElement = $(this._element).closest( Selector.DATA_TOGGLE )[0] if (rootElement) { - let input = $(this._element).find(Selector.INPUT)[0] + const input = $(this._element).find(Selector.INPUT)[0] if (input) { if (input.type === 'radio') { @@ -80,7 +80,7 @@ const Button = (($) => { triggerChangeEvent = false } else { - let activeElement = $(rootElement).find(Selector.ACTIVE)[0] + const activeElement = $(rootElement).find(Selector.ACTIVE)[0] if (activeElement) { $(activeElement).removeClass(ClassName.ACTIVE) @@ -90,7 +90,7 @@ const Button = (($) => { if (triggerChangeEvent) { input.checked = !$(this._element).hasClass(ClassName.ACTIVE) - $(this._element).trigger('change') + $(input).trigger('change') } input.focus() @@ -151,7 +151,7 @@ const Button = (($) => { Button._jQueryInterface.call($(button), 'toggle') }) .on(Event.FOCUS_BLUR_DATA_API, Selector.DATA_TOGGLE_CARROT, (event) => { - let button = $(event.target).closest(Selector.BUTTON)[0] + const button = $(event.target).closest(Selector.BUTTON)[0] $(button).toggleClass(ClassName.FOCUS, /^focus(in)?$/.test(event.type)) }) diff --git a/js/src/carousel.js b/js/src/carousel.js index 34cab4391..9a1a668b2 100644 --- a/js/src/carousel.js +++ b/js/src/carousel.js @@ -3,7 +3,7 @@ import Util from './util' /** * -------------------------------------------------------------------------- - * Bootstrap (v4.0.0-alpha.2): carousel.js + * Bootstrap (v4.0.0-alpha.5): carousel.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * -------------------------------------------------------------------------- */ @@ -18,12 +18,14 @@ const Carousel = (($) => { */ const NAME = 'carousel' - const VERSION = '4.0.0-alpha.2' + const VERSION = '4.0.0-alpha.5' const DATA_KEY = 'bs.carousel' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' const JQUERY_NO_CONFLICT = $.fn[NAME] const TRANSITION_DURATION = 600 + 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 Default = { interval : 5000, @@ -43,7 +45,9 @@ const Carousel = (($) => { const Direction = { NEXT : 'next', - PREVIOUS : 'prev' + PREV : 'prev', + LEFT : 'left', + RIGHT : 'right' } const Event = { @@ -60,8 +64,10 @@ const Carousel = (($) => { CAROUSEL : 'carousel', ACTIVE : 'active', SLIDE : 'slide', - RIGHT : 'right', - LEFT : 'left', + RIGHT : 'carousel-item-right', + LEFT : 'carousel-item-left', + NEXT : 'carousel-item-next', + PREV : 'carousel-item-prev', ITEM : 'carousel-item' } @@ -69,7 +75,7 @@ const Carousel = (($) => { ACTIVE : '.active', ACTIVE_ITEM : '.active.carousel-item', ITEM : '.carousel-item', - NEXT_PREV : '.next, .prev', + NEXT_PREV : '.carousel-item-next, .carousel-item-prev', INDICATORS : '.carousel-indicators', DATA_SLIDE : '[data-slide], [data-slide-to]', DATA_RIDE : '[data-ride="carousel"]' @@ -114,9 +120,10 @@ const Carousel = (($) => { // public next() { - if (!this._isSliding) { - this._slide(Direction.NEXT) + if (this._isSliding) { + throw new Error('Carousel is sliding') } + this._slide(Direction.NEXT) } nextWhenVisible() { @@ -127,9 +134,10 @@ const Carousel = (($) => { } prev() { - if (!this._isSliding) { - this._slide(Direction.PREVIOUS) + if (this._isSliding) { + throw new Error('Carousel is sliding') } + this._slide(Direction.PREVIOUS) } pause(event) { @@ -159,7 +167,8 @@ const Carousel = (($) => { if (this._config.interval && !this._isPaused) { this._interval = setInterval( - $.proxy(document.visibilityState ? this.nextWhenVisible : this.next, this), this._config.interval + (document.visibilityState ? this.nextWhenVisible : this.next).bind(this), + this._config.interval ) } } @@ -167,9 +176,9 @@ const Carousel = (($) => { to(index) { this._activeElement = $(this._element).find(Selector.ACTIVE_ITEM)[0] - let 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) { return } @@ -184,7 +193,7 @@ const Carousel = (($) => { return } - let direction = index > activeIndex ? + const direction = index > activeIndex ? Direction.NEXT : Direction.PREVIOUS @@ -217,28 +226,33 @@ const Carousel = (($) => { _addEventListeners() { if (this._config.keyboard) { $(this._element) - .on(Event.KEYDOWN, $.proxy(this._keydown, this)) + .on(Event.KEYDOWN, (event) => this._keydown(event)) } if (this._config.pause === 'hover' && !('ontouchstart' in document.documentElement)) { $(this._element) - .on(Event.MOUSEENTER, $.proxy(this.pause, this)) - .on(Event.MOUSELEAVE, $.proxy(this.cycle, this)) + .on(Event.MOUSEENTER, (event) => this.pause(event)) + .on(Event.MOUSELEAVE, (event) => this.cycle(event)) } } _keydown(event) { - event.preventDefault() - if (/input|textarea/i.test(event.target.tagName)) { return } switch (event.which) { - case 37: this.prev(); break - case 39: this.next(); break - default: return + case ARROW_LEFT_KEYCODE: + event.preventDefault() + this.prev() + break + case ARROW_RIGHT_KEYCODE: + event.preventDefault() + this.next() + break + default: + return } } @@ -248,29 +262,29 @@ const Carousel = (($) => { } _getItemByDirection(direction, activeElement) { - let isNextDirection = direction === Direction.NEXT - let isPrevDirection = direction === Direction.PREVIOUS - let activeIndex = this._getItemIndex(activeElement) - let lastItemIndex = (this._items.length - 1) - let isGoingToWrap = (isPrevDirection && activeIndex === 0) || - (isNextDirection && activeIndex === lastItemIndex) + const isNextDirection = direction === Direction.NEXT + const isPrevDirection = direction === Direction.PREVIOUS + const activeIndex = this._getItemIndex(activeElement) + const lastItemIndex = this._items.length - 1 + const isGoingToWrap = isPrevDirection && activeIndex === 0 || + isNextDirection && activeIndex === lastItemIndex if (isGoingToWrap && !this._config.wrap) { return activeElement } - let delta = direction === Direction.PREVIOUS ? -1 : 1 - let itemIndex = (activeIndex + delta) % this._items.length + const delta = direction === Direction.PREVIOUS ? -1 : 1 + const itemIndex = (activeIndex + delta) % this._items.length return itemIndex === -1 ? this._items[this._items.length - 1] : this._items[itemIndex] } - _triggerSlideEvent(relatedTarget, directionalClassname) { - let slideEvent = $.Event(Event.SLIDE, { + _triggerSlideEvent(relatedTarget, eventDirectionName) { + const slideEvent = $.Event(Event.SLIDE, { relatedTarget, - direction: directionalClassname + direction: eventDirectionName }) $(this._element).trigger(slideEvent) @@ -284,7 +298,7 @@ const Carousel = (($) => { .find(Selector.ACTIVE) .removeClass(ClassName.ACTIVE) - let nextIndicator = this._indicatorsElement.children[ + const nextIndicator = this._indicatorsElement.children[ this._getItemIndex(element) ] @@ -295,22 +309,32 @@ const Carousel = (($) => { } _slide(direction, element) { - let activeElement = $(this._element).find(Selector.ACTIVE_ITEM)[0] - let nextElement = element || activeElement && + const activeElement = $(this._element).find(Selector.ACTIVE_ITEM)[0] + const nextElement = element || activeElement && this._getItemByDirection(direction, activeElement) - let isCycling = Boolean(this._interval) + const isCycling = Boolean(this._interval) - let directionalClassName = direction === Direction.NEXT ? - ClassName.LEFT : - ClassName.RIGHT + let directionalClassName + let orderClassName + let eventDirectionName + + if (direction === Direction.NEXT) { + directionalClassName = ClassName.LEFT + orderClassName = ClassName.NEXT + eventDirectionName = Direction.LEFT + } else { + directionalClassName = ClassName.RIGHT + orderClassName = ClassName.PREV + eventDirectionName = Direction.RIGHT + } if (nextElement && $(nextElement).hasClass(ClassName.ACTIVE)) { this._isSliding = false return } - let slideEvent = this._triggerSlideEvent(nextElement, directionalClassName) + const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName) if (slideEvent.isDefaultPrevented()) { return } @@ -328,15 +352,15 @@ const Carousel = (($) => { this._setActiveIndicatorElement(nextElement) - let slidEvent = $.Event(Event.SLID, { + const slidEvent = $.Event(Event.SLID, { relatedTarget: nextElement, - direction: directionalClassName + direction: eventDirectionName }) if (Util.supportsTransitionEnd() && $(this._element).hasClass(ClassName.SLIDE)) { - $(nextElement).addClass(direction) + $(nextElement).addClass(orderClassName) Util.reflow(nextElement) @@ -346,15 +370,10 @@ const Carousel = (($) => { $(activeElement) .one(Util.TRANSITION_END, () => { $(nextElement) - .removeClass(directionalClassName) - .removeClass(direction) - - $(nextElement).addClass(ClassName.ACTIVE) + .removeClass(`${directionalClassName} ${orderClassName}`) + .addClass(ClassName.ACTIVE) - $(activeElement) - .removeClass(ClassName.ACTIVE) - .removeClass(direction) - .removeClass(directionalClassName) + $(activeElement).removeClass(`${ClassName.ACTIVE} ${orderClassName} ${directionalClassName}`) this._isSliding = false @@ -382,13 +401,13 @@ const Carousel = (($) => { static _jQueryInterface(config) { return this.each(function () { let data = $(this).data(DATA_KEY) - let _config = $.extend({}, Default, $(this).data()) + const _config = $.extend({}, Default, $(this).data()) if (typeof config === 'object') { $.extend(_config, config) } - let action = typeof config === 'string' ? config : _config.slide + const action = typeof config === 'string' ? config : _config.slide if (!data) { data = new Carousel(this, _config) @@ -410,20 +429,20 @@ const Carousel = (($) => { } static _dataApiClickHandler(event) { - let selector = Util.getSelectorFromElement(this) + const selector = Util.getSelectorFromElement(this) if (!selector) { return } - let target = $(selector)[0] + const target = $(selector)[0] if (!target || !$(target).hasClass(ClassName.CAROUSEL)) { return } - let config = $.extend({}, $(target).data(), $(this).data()) - let slideIndex = this.getAttribute('data-slide-to') + const config = $.extend({}, $(target).data(), $(this).data()) + const slideIndex = this.getAttribute('data-slide-to') if (slideIndex) { config.interval = false @@ -452,7 +471,7 @@ const Carousel = (($) => { $(window).on(Event.LOAD_DATA_API, () => { $(Selector.DATA_RIDE).each(function () { - let $carousel = $(this) + const $carousel = $(this) Carousel._jQueryInterface.call($carousel, $carousel.data()) }) }) diff --git a/js/src/collapse.js b/js/src/collapse.js index 3dd07cadf..ad8815993 100644 --- a/js/src/collapse.js +++ b/js/src/collapse.js @@ -3,7 +3,7 @@ import Util from './util' /** * -------------------------------------------------------------------------- - * Bootstrap (v4.0.0-alpha.2): collapse.js + * Bootstrap (v4.0.0-alpha.5): collapse.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * -------------------------------------------------------------------------- */ @@ -18,7 +18,7 @@ const Collapse = (($) => { */ const NAME = 'collapse' - const VERSION = '4.0.0-alpha.2' + const VERSION = '4.0.0-alpha.5' const DATA_KEY = 'bs.collapse' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' @@ -44,7 +44,7 @@ const Collapse = (($) => { } const ClassName = { - IN : 'in', + SHOW : 'show', COLLAPSE : 'collapse', COLLAPSING : 'collapsing', COLLAPSED : 'collapsed' @@ -56,7 +56,7 @@ const Collapse = (($) => { } const Selector = { - ACTIVES : '.panel > .in, .panel > .collapsing', + ACTIVES : '.card > .show, .card > .collapsing', DATA_TOGGLE : '[data-toggle="collapse"]' } @@ -104,7 +104,7 @@ const Collapse = (($) => { // public toggle() { - if ($(this._element).hasClass(ClassName.IN)) { + if ($(this._element).hasClass(ClassName.SHOW)) { this.hide() } else { this.show() @@ -112,8 +112,11 @@ const Collapse = (($) => { } show() { - if (this._isTransitioning || - $(this._element).hasClass(ClassName.IN)) { + if (this._isTransitioning) { + throw new Error('Collapse is transitioning') + } + + if ($(this._element).hasClass(ClassName.SHOW)) { return } @@ -121,7 +124,7 @@ const Collapse = (($) => { let activesData if (this._parent) { - actives = $.makeArray($(Selector.ACTIVES)) + actives = $.makeArray($(this._parent).find(Selector.ACTIVES)) if (!actives.length) { actives = null } @@ -134,7 +137,7 @@ const Collapse = (($) => { } } - let startEvent = $.Event(Event.SHOW) + const startEvent = $.Event(Event.SHOW) $(this._element).trigger(startEvent) if (startEvent.isDefaultPrevented()) { return @@ -147,7 +150,7 @@ const Collapse = (($) => { } } - let dimension = this._getDimension() + const dimension = this._getDimension() $(this._element) .removeClass(ClassName.COLLAPSE) @@ -164,11 +167,11 @@ const Collapse = (($) => { this.setTransitioning(true) - let complete = () => { + const complete = () => { $(this._element) .removeClass(ClassName.COLLAPSING) .addClass(ClassName.COLLAPSE) - .addClass(ClassName.IN) + .addClass(ClassName.SHOW) this._element.style[dimension] = '' @@ -182,8 +185,8 @@ const Collapse = (($) => { return } - let capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1) - let scrollSize = `scroll${capitalizedDimension}` + const capitalizedDimension = dimension[0].toUpperCase() + dimension.slice(1) + const scrollSize = `scroll${capitalizedDimension}` $(this._element) .one(Util.TRANSITION_END, complete) @@ -193,19 +196,22 @@ const Collapse = (($) => { } hide() { - if (this._isTransitioning || - !$(this._element).hasClass(ClassName.IN)) { + if (this._isTransitioning) { + throw new Error('Collapse is transitioning') + } + + if (!$(this._element).hasClass(ClassName.SHOW)) { return } - let startEvent = $.Event(Event.HIDE) + const startEvent = $.Event(Event.HIDE) $(this._element).trigger(startEvent) if (startEvent.isDefaultPrevented()) { return } - let dimension = this._getDimension() - let offsetDimension = dimension === Dimension.WIDTH ? + const dimension = this._getDimension() + const offsetDimension = dimension === Dimension.WIDTH ? 'offsetWidth' : 'offsetHeight' this._element.style[dimension] = `${this._element[offsetDimension]}px` @@ -215,7 +221,7 @@ const Collapse = (($) => { $(this._element) .addClass(ClassName.COLLAPSING) .removeClass(ClassName.COLLAPSE) - .removeClass(ClassName.IN) + .removeClass(ClassName.SHOW) this._element.setAttribute('aria-expanded', false) @@ -227,7 +233,7 @@ const Collapse = (($) => { this.setTransitioning(true) - let complete = () => { + const complete = () => { this.setTransitioning(false) $(this._element) .removeClass(ClassName.COLLAPSING) @@ -235,7 +241,7 @@ const Collapse = (($) => { .trigger(Event.HIDDEN) } - this._element.style[dimension] = 0 + this._element.style[dimension] = '' if (!Util.supportsTransitionEnd()) { complete() @@ -272,13 +278,13 @@ const Collapse = (($) => { } _getDimension() { - let hasWidth = $(this._element).hasClass(Dimension.WIDTH) + const hasWidth = $(this._element).hasClass(Dimension.WIDTH) return hasWidth ? Dimension.WIDTH : Dimension.HEIGHT } _getParent() { - let parent = $(this._config.parent)[0] - let selector = + const parent = $(this._config.parent)[0] + const selector = `[data-toggle="collapse"][data-parent="${this._config.parent}"]` $(parent).find(selector).each((i, element) => { @@ -293,7 +299,7 @@ const Collapse = (($) => { _addAriaAndCollapsedClass(element, triggerArray) { if (element) { - let isOpen = $(element).hasClass(ClassName.IN) + const isOpen = $(element).hasClass(ClassName.SHOW) element.setAttribute('aria-expanded', isOpen) if (triggerArray.length) { @@ -308,15 +314,15 @@ const Collapse = (($) => { // static static _getTargetFromElement(element) { - let selector = Util.getSelectorFromElement(element) + const selector = Util.getSelectorFromElement(element) return selector ? $(selector)[0] : null } static _jQueryInterface(config) { return this.each(function () { - let $this = $(this) - let data = $this.data(DATA_KEY) - let _config = $.extend( + const $this = $(this) + let data = $this.data(DATA_KEY) + const _config = $.extend( {}, Default, $this.data(), @@ -353,9 +359,9 @@ const Collapse = (($) => { $(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { event.preventDefault() - let target = Collapse._getTargetFromElement(this) - let data = $(target).data(DATA_KEY) - let config = data ? 'toggle' : $(this).data() + const target = Collapse._getTargetFromElement(this) + const data = $(target).data(DATA_KEY) + const config = data ? 'toggle' : $(this).data() Collapse._jQueryInterface.call($(target), config) }) diff --git a/js/src/dropdown.js b/js/src/dropdown.js index a9786a534..bcf13caf1 100644 --- a/js/src/dropdown.js +++ b/js/src/dropdown.js @@ -3,7 +3,7 @@ import Util from './util' /** * -------------------------------------------------------------------------- - * Bootstrap (v4.0.0-alpha.2): dropdown.js + * Bootstrap (v4.0.0-alpha.5): dropdown.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * -------------------------------------------------------------------------- */ @@ -17,12 +17,16 @@ const Dropdown = (($) => { * ------------------------------------------------------------------------ */ - const NAME = 'dropdown' - const VERSION = '4.0.0-alpha.2' - const DATA_KEY = 'bs.dropdown' - const EVENT_KEY = `.${DATA_KEY}` - const DATA_API_KEY = '.data-api' - const JQUERY_NO_CONFLICT = $.fn[NAME] + const NAME = 'dropdown' + const VERSION = '4.0.0-alpha.5' + const DATA_KEY = 'bs.dropdown' + const EVENT_KEY = `.${DATA_KEY}` + const DATA_API_KEY = '.data-api' + const JQUERY_NO_CONFLICT = $.fn[NAME] + const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) 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 RIGHT_MOUSE_BUTTON_WHICH = 3 // MouseEvent.which value for the right button (assuming a right-handed mouse) const Event = { HIDE : `hide${EVENT_KEY}`, @@ -37,7 +41,7 @@ const Dropdown = (($) => { const ClassName = { BACKDROP : 'dropdown-backdrop', DISABLED : 'disabled', - OPEN : 'open' + SHOW : 'show' } const Selector = { @@ -79,8 +83,8 @@ const Dropdown = (($) => { return false } - let parent = Dropdown._getParentFromElement(this) - let isActive = $(parent).hasClass(ClassName.OPEN) + const parent = Dropdown._getParentFromElement(this) + const isActive = $(parent).hasClass(ClassName.SHOW) Dropdown._clearMenus() @@ -89,17 +93,19 @@ const Dropdown = (($) => { } if ('ontouchstart' in document.documentElement && - (!$(parent).closest(Selector.NAVBAR_NAV).length)) { + !$(parent).closest(Selector.NAVBAR_NAV).length) { // if mobile we use a backdrop because click events don't delegate - let dropdown = document.createElement('div') + const dropdown = document.createElement('div') dropdown.className = ClassName.BACKDROP $(dropdown).insertBefore(this) $(dropdown).on('click', Dropdown._clearMenus) } - let relatedTarget = { relatedTarget : this } - let showEvent = $.Event(Event.SHOW, relatedTarget) + const relatedTarget = { + relatedTarget : this + } + const showEvent = $.Event(Event.SHOW, relatedTarget) $(parent).trigger(showEvent) @@ -108,9 +114,9 @@ const Dropdown = (($) => { } this.focus() - this.setAttribute('aria-expanded', 'true') + this.setAttribute('aria-expanded', true) - $(parent).toggleClass(ClassName.OPEN) + $(parent).toggleClass(ClassName.SHOW) $(parent).trigger($.Event(Event.SHOWN, relatedTarget)) return false @@ -134,10 +140,11 @@ const Dropdown = (($) => { static _jQueryInterface(config) { return this.each(function () { - let data = $(this).data(DATA_KEY) + let data = $(this).data(DATA_KEY) if (!data) { - $(this).data(DATA_KEY, (data = new Dropdown(this))) + data = new Dropdown(this) + $(this).data(DATA_KEY, data) } if (typeof config === 'string') { @@ -150,32 +157,34 @@ const Dropdown = (($) => { } static _clearMenus(event) { - if (event && event.which === 3) { + if (event && event.which === RIGHT_MOUSE_BUTTON_WHICH) { return } - let backdrop = $(Selector.BACKDROP)[0] + const backdrop = $(Selector.BACKDROP)[0] if (backdrop) { backdrop.parentNode.removeChild(backdrop) } - let toggles = $.makeArray($(Selector.DATA_TOGGLE)) + const toggles = $.makeArray($(Selector.DATA_TOGGLE)) for (let i = 0; i < toggles.length; i++) { - let parent = Dropdown._getParentFromElement(toggles[i]) - let relatedTarget = { relatedTarget : toggles[i] } + const parent = Dropdown._getParentFromElement(toggles[i]) + const relatedTarget = { + relatedTarget : toggles[i] + } - if (!$(parent).hasClass(ClassName.OPEN)) { + if (!$(parent).hasClass(ClassName.SHOW)) { continue } if (event && event.type === 'click' && - (/input|textarea/i.test(event.target.tagName)) && - ($.contains(parent, event.target))) { + /input|textarea/i.test(event.target.tagName) && + $.contains(parent, event.target)) { continue } - let hideEvent = $.Event(Event.HIDE, relatedTarget) + const hideEvent = $.Event(Event.HIDE, relatedTarget) $(parent).trigger(hideEvent) if (hideEvent.isDefaultPrevented()) { continue @@ -184,14 +193,14 @@ const Dropdown = (($) => { toggles[i].setAttribute('aria-expanded', 'false') $(parent) - .removeClass(ClassName.OPEN) + .removeClass(ClassName.SHOW) .trigger($.Event(Event.HIDDEN, relatedTarget)) } } static _getParentFromElement(element) { let parent - let selector = Util.getSelectorFromElement(element) + const selector = Util.getSelectorFromElement(element) if (selector) { parent = $(selector)[0] @@ -213,14 +222,14 @@ const Dropdown = (($) => { return } - let parent = Dropdown._getParentFromElement(this) - let isActive = $(parent).hasClass(ClassName.OPEN) + const parent = Dropdown._getParentFromElement(this) + const isActive = $(parent).hasClass(ClassName.SHOW) - if ((!isActive && event.which !== 27) || - (isActive && event.which === 27)) { + if (!isActive && event.which !== ESCAPE_KEYCODE || + isActive && event.which === ESCAPE_KEYCODE) { - if (event.which === 27) { - let toggle = $(parent).find(Selector.DATA_TOGGLE)[0] + if (event.which === ESCAPE_KEYCODE) { + const toggle = $(parent).find(Selector.DATA_TOGGLE)[0] $(toggle).trigger('focus') } @@ -228,11 +237,7 @@ const Dropdown = (($) => { return } - let items = $.makeArray($(Selector.VISIBLE_ITEMS)) - - items = items.filter((item) => { - return item.offsetWidth || item.offsetHeight - }) + const items = $(parent).find(Selector.VISIBLE_ITEMS).get() if (!items.length) { return @@ -240,11 +245,11 @@ const Dropdown = (($) => { let index = items.indexOf(event.target) - if (event.which === 38 && index > 0) { // up + if (event.which === ARROW_UP_KEYCODE && index > 0) { // up index-- } - if (event.which === 40 && index < items.length - 1) { // down + if (event.which === ARROW_DOWN_KEYCODE && index < items.length - 1) { // down index++ } diff --git a/js/src/modal.js b/js/src/modal.js index f52af09a2..94abd19f4 100644 --- a/js/src/modal.js +++ b/js/src/modal.js @@ -3,7 +3,7 @@ import Util from './util' /** * -------------------------------------------------------------------------- - * Bootstrap (v4.0.0-alpha.2): modal.js + * Bootstrap (v4.0.0-alpha.5): modal.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * -------------------------------------------------------------------------- */ @@ -18,13 +18,14 @@ const Modal = (($) => { */ const NAME = 'modal' - const VERSION = '4.0.0-alpha.2' + const VERSION = '4.0.0-alpha.5' const DATA_KEY = 'bs.modal' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' const JQUERY_NO_CONFLICT = $.fn[NAME] const TRANSITION_DURATION = 300 const BACKDROP_TRANSITION_DURATION = 150 + const ESCAPE_KEYCODE = 27 // KeyboardEvent.which value for Escape (Esc) key const Default = { backdrop : true, @@ -59,7 +60,7 @@ const Modal = (($) => { BACKDROP : 'modal-backdrop', OPEN : 'modal-open', FADE : 'fade', - IN : 'in' + SHOW : 'show' } const Selector = { @@ -86,6 +87,7 @@ const Modal = (($) => { this._isShown = false this._isBodyOverflowing = false this._ignoreBackdropClick = false + this._isTransitioning = false this._originalBodyPadding = 0 this._scrollbarWidth = 0 } @@ -109,7 +111,15 @@ const Modal = (($) => { } show(relatedTarget) { - let showEvent = $.Event(Event.SHOW, { + if (this._isTransitioning) { + throw new Error('Modal is transitioning') + } + + if (Util.supportsTransitionEnd() && + $(this._element).hasClass(ClassName.FADE)) { + this._isTransitioning = true + } + const showEvent = $.Event(Event.SHOW, { relatedTarget }) @@ -132,7 +142,7 @@ const Modal = (($) => { $(this._element).on( Event.CLICK_DISMISS, Selector.DATA_DISMISS, - $.proxy(this.hide, this) + (event) => this.hide(event) ) $(this._dialog).on(Event.MOUSEDOWN_DISMISS, () => { @@ -143,9 +153,7 @@ const Modal = (($) => { }) }) - this._showBackdrop( - $.proxy(this._showElement, this, relatedTarget) - ) + this._showBackdrop(() => this._showElement(relatedTarget)) } hide(event) { @@ -153,8 +161,17 @@ const Modal = (($) => { event.preventDefault() } - let hideEvent = $.Event(Event.HIDE) + if (this._isTransitioning) { + throw new Error('Modal is transitioning') + } + const transition = Util.supportsTransitionEnd() && + $(this._element).hasClass(ClassName.FADE) + if (transition) { + this._isTransitioning = true + } + + const hideEvent = $.Event(Event.HIDE) $(this._element).trigger(hideEvent) if (!this._isShown || hideEvent.isDefaultPrevented()) { @@ -168,16 +185,14 @@ const Modal = (($) => { $(document).off(Event.FOCUSIN) - $(this._element).removeClass(ClassName.IN) + $(this._element).removeClass(ClassName.SHOW) $(this._element).off(Event.CLICK_DISMISS) $(this._dialog).off(Event.MOUSEDOWN_DISMISS) - if (Util.supportsTransitionEnd() && - ($(this._element).hasClass(ClassName.FADE))) { - + if (transition) { $(this._element) - .one(Util.TRANSITION_END, $.proxy(this._hideModal, this)) + .one(Util.TRANSITION_END, (event) => this._hideModal(event)) .emulateTransitionEnd(TRANSITION_DURATION) } else { this._hideModal() @@ -187,10 +202,7 @@ const Modal = (($) => { dispose() { $.removeData(this._element, DATA_KEY) - $(window).off(EVENT_KEY) - $(document).off(EVENT_KEY) - $(this._element).off(EVENT_KEY) - $(this._backdrop).off(EVENT_KEY) + $(window, document, this._element, this._backdrop).off(EVENT_KEY) this._config = null this._element = null @@ -213,11 +225,11 @@ const Modal = (($) => { } _showElement(relatedTarget) { - let transition = Util.supportsTransitionEnd() && + const transition = Util.supportsTransitionEnd() && $(this._element).hasClass(ClassName.FADE) if (!this._element.parentNode || - (this._element.parentNode.nodeType !== Node.ELEMENT_NODE)) { + this._element.parentNode.nodeType !== Node.ELEMENT_NODE) { // don't move modals dom position document.body.appendChild(this._element) } @@ -230,20 +242,21 @@ const Modal = (($) => { Util.reflow(this._element) } - $(this._element).addClass(ClassName.IN) + $(this._element).addClass(ClassName.SHOW) if (this._config.focus) { this._enforceFocus() } - let shownEvent = $.Event(Event.SHOWN, { + const shownEvent = $.Event(Event.SHOWN, { relatedTarget }) - let transitionComplete = () => { + const transitionComplete = () => { if (this._config.focus) { this._element.focus() } + this._isTransitioning = false $(this._element).trigger(shownEvent) } @@ -262,7 +275,7 @@ const Modal = (($) => { .on(Event.FOCUSIN, (event) => { if (document !== event.target && this._element !== event.target && - (!$(this._element).has(event.target).length)) { + !$(this._element).has(event.target).length) { this._element.focus() } }) @@ -271,7 +284,7 @@ const Modal = (($) => { _setEscapeEvent() { if (this._isShown && this._config.keyboard) { $(this._element).on(Event.KEYDOWN_DISMISS, (event) => { - if (event.which === 27) { + if (event.which === ESCAPE_KEYCODE) { this.hide() } }) @@ -283,7 +296,7 @@ const Modal = (($) => { _setResizeEvent() { if (this._isShown) { - $(window).on(Event.RESIZE, $.proxy(this._handleUpdate, this)) + $(window).on(Event.RESIZE, (event) => this._handleUpdate(event)) } else { $(window).off(Event.RESIZE) } @@ -292,6 +305,7 @@ const Modal = (($) => { _hideModal() { this._element.style.display = 'none' this._element.setAttribute('aria-hidden', 'true') + this._isTransitioning = false this._showBackdrop(() => { $(document.body).removeClass(ClassName.OPEN) this._resetAdjustments() @@ -308,11 +322,11 @@ const Modal = (($) => { } _showBackdrop(callback) { - let animate = $(this._element).hasClass(ClassName.FADE) ? + const animate = $(this._element).hasClass(ClassName.FADE) ? ClassName.FADE : '' if (this._isShown && this._config.backdrop) { - let doAnimate = Util.supportsTransitionEnd() && animate + const doAnimate = Util.supportsTransitionEnd() && animate this._backdrop = document.createElement('div') this._backdrop.className = ClassName.BACKDROP @@ -342,7 +356,7 @@ const Modal = (($) => { Util.reflow(this._backdrop) } - $(this._backdrop).addClass(ClassName.IN) + $(this._backdrop).addClass(ClassName.SHOW) if (!callback) { return @@ -358,9 +372,9 @@ const Modal = (($) => { .emulateTransitionEnd(BACKDROP_TRANSITION_DURATION) } else if (!this._isShown && this._backdrop) { - $(this._backdrop).removeClass(ClassName.IN) + $(this._backdrop).removeClass(ClassName.SHOW) - let callbackRemove = () => { + const callbackRemove = () => { this._removeBackdrop() if (callback) { callback() @@ -368,7 +382,7 @@ const Modal = (($) => { } if (Util.supportsTransitionEnd() && - ($(this._element).hasClass(ClassName.FADE))) { + $(this._element).hasClass(ClassName.FADE)) { $(this._backdrop) .one(Util.TRANSITION_END, callbackRemove) .emulateTransitionEnd(BACKDROP_TRANSITION_DURATION) @@ -392,7 +406,7 @@ const Modal = (($) => { } _adjustDialog() { - let isModalOverflowing = + const isModalOverflowing = this._element.scrollHeight > document.documentElement.clientHeight if (!this._isBodyOverflowing && isModalOverflowing) { @@ -415,7 +429,7 @@ const Modal = (($) => { } _setScrollbar() { - let bodyPadding = parseInt( + const bodyPadding = parseInt( $(Selector.FIXED_CONTENT).css('padding-right') || 0, 10 ) @@ -433,10 +447,10 @@ const Modal = (($) => { } _getScrollbarWidth() { // thx d.walsh - let scrollDiv = document.createElement('div') + const scrollDiv = document.createElement('div') scrollDiv.className = ClassName.SCROLLBAR_MEASURER document.body.appendChild(scrollDiv) - let scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth + const scrollbarWidth = scrollDiv.offsetWidth - scrollDiv.clientWidth document.body.removeChild(scrollDiv) return scrollbarWidth } @@ -446,8 +460,8 @@ const Modal = (($) => { static _jQueryInterface(config, relatedTarget) { return this.each(function () { - let data = $(this).data(DATA_KEY) - let _config = $.extend( + let data = $(this).data(DATA_KEY) + const _config = $.extend( {}, Modal.Default, $(this).data(), @@ -481,20 +495,20 @@ const Modal = (($) => { $(document).on(Event.CLICK_DATA_API, Selector.DATA_TOGGLE, function (event) { let target - let selector = Util.getSelectorFromElement(this) + const selector = Util.getSelectorFromElement(this) if (selector) { target = $(selector)[0] } - let config = $(target).data(DATA_KEY) ? + const config = $(target).data(DATA_KEY) ? 'toggle' : $.extend({}, $(target).data(), $(this).data()) - if (this.tagName === 'A') { + if (this.tagName === 'A' || this.tagName === 'AREA') { event.preventDefault() } - let $target = $(target).one(Event.SHOW, (showEvent) => { + const $target = $(target).one(Event.SHOW, (showEvent) => { if (showEvent.isDefaultPrevented()) { // only register focus restorer if modal will actually get shown return diff --git a/js/src/popover.js b/js/src/popover.js index 7e8ec4297..33bc9e48c 100644 --- a/js/src/popover.js +++ b/js/src/popover.js @@ -3,7 +3,7 @@ import Tooltip from './tooltip' /** * -------------------------------------------------------------------------- - * Bootstrap (v4.0.0-alpha.2): popover.js + * Bootstrap (v4.0.0-alpha.5): popover.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * -------------------------------------------------------------------------- */ @@ -18,7 +18,7 @@ const Popover = (($) => { */ const NAME = 'popover' - const VERSION = '4.0.0-alpha.2' + const VERSION = '4.0.0-alpha.5' const DATA_KEY = 'bs.popover' const EVENT_KEY = `.${DATA_KEY}` const JQUERY_NO_CONFLICT = $.fn[NAME] @@ -28,7 +28,6 @@ const Popover = (($) => { trigger : 'click', content : '', template : '<div class="popover" role="tooltip">' - + '<div class="popover-arrow"></div>' + '<h3 class="popover-title"></h3>' + '<div class="popover-content"></div></div>' }) @@ -39,13 +38,12 @@ const Popover = (($) => { const ClassName = { FADE : 'fade', - IN : 'in' + SHOW : 'show' } const Selector = { TITLE : '.popover-title', - CONTENT : '.popover-content', - ARROW : '.popover-arrow' + CONTENT : '.popover-content' } const Event = { @@ -109,19 +107,17 @@ const Popover = (($) => { } getTipElement() { - return (this.tip = this.tip || $(this.config.template)[0]) + return this.tip = this.tip || $(this.config.template)[0] } setContent() { - let $tip = $(this.getTipElement()) + const $tip = $(this.getTipElement()) // we use append for html objects to maintain js events this.setElementContent($tip.find(Selector.TITLE), this.getTitle()) this.setElementContent($tip.find(Selector.CONTENT), this._getContent()) - $tip - .removeClass(ClassName.FADE) - .removeClass(ClassName.IN) + $tip.removeClass(`${ClassName.FADE} ${ClassName.SHOW}`) this.cleanupTether() } @@ -140,8 +136,8 @@ const Popover = (($) => { static _jQueryInterface(config) { return this.each(function () { - let data = $(this).data(DATA_KEY) - let _config = typeof config === 'object' ? config : null + let data = $(this).data(DATA_KEY) + const _config = typeof config === 'object' ? config : null if (!data && /destroy|hide/.test(config)) { return diff --git a/js/src/scrollspy.js b/js/src/scrollspy.js index b9186db00..0a4708bf9 100644 --- a/js/src/scrollspy.js +++ b/js/src/scrollspy.js @@ -3,7 +3,7 @@ import Util from './util' /** * -------------------------------------------------------------------------- - * Bootstrap (v4.0.0-alpha.2): scrollspy.js + * Bootstrap (v4.0.0-alpha.5): scrollspy.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * -------------------------------------------------------------------------- */ @@ -18,7 +18,7 @@ const ScrollSpy = (($) => { */ const NAME = 'scrollspy' - const VERSION = '4.0.0-alpha.2' + const VERSION = '4.0.0-alpha.5' const DATA_KEY = 'bs.scrollspy' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' @@ -87,7 +87,7 @@ const ScrollSpy = (($) => { this._activeTarget = null this._scrollHeight = 0 - $(this._scrollElement).on(Event.SCROLL, $.proxy(this._process, this)) + $(this._scrollElement).on(Event.SCROLL, (event) => this._process(event)) this.refresh() this._process() @@ -108,13 +108,13 @@ const ScrollSpy = (($) => { // public refresh() { - let autoMethod = this._scrollElement !== this._scrollElement.window ? + const autoMethod = this._scrollElement !== this._scrollElement.window ? OffsetMethod.POSITION : OffsetMethod.OFFSET - let offsetMethod = this._config.method === 'auto' ? + const offsetMethod = this._config.method === 'auto' ? autoMethod : this._config.method - let offsetBase = offsetMethod === OffsetMethod.POSITION ? + const offsetBase = offsetMethod === OffsetMethod.POSITION ? this._getScrollTop() : 0 this._offsets = [] @@ -122,12 +122,12 @@ const ScrollSpy = (($) => { this._scrollHeight = this._getScrollHeight() - let targets = $.makeArray($(this._selector)) + const targets = $.makeArray($(this._selector)) targets .map((element) => { let target - let targetSelector = Util.getSelectorFromElement(element) + const targetSelector = Util.getSelectorFromElement(element) if (targetSelector) { target = $(targetSelector)[0] @@ -140,6 +140,7 @@ const ScrollSpy = (($) => { targetSelector ] } + return null }) .filter((item) => item) .sort((a, b) => a[0] - b[0]) @@ -195,33 +196,39 @@ const ScrollSpy = (($) => { ) } + _getOffsetHeight() { + return this._scrollElement === window ? + window.innerHeight : this._scrollElement.offsetHeight + } + _process() { - let scrollTop = this._getScrollTop() + this._config.offset - let scrollHeight = this._getScrollHeight() - let maxScroll = this._config.offset + const scrollTop = this._getScrollTop() + this._config.offset + const scrollHeight = this._getScrollHeight() + const maxScroll = this._config.offset + scrollHeight - - this._scrollElement.offsetHeight + - this._getOffsetHeight() if (this._scrollHeight !== scrollHeight) { this.refresh() } if (scrollTop >= maxScroll) { - let target = this._targets[this._targets.length - 1] + const target = this._targets[this._targets.length - 1] if (this._activeTarget !== target) { this._activate(target) } + return } - if (this._activeTarget && scrollTop < this._offsets[0]) { + if (this._activeTarget && scrollTop < this._offsets[0] && this._offsets[0] > 0) { this._activeTarget = null this._clear() return } for (let i = this._offsets.length; i--;) { - let isActiveTarget = this._activeTarget !== this._targets[i] + const isActiveTarget = this._activeTarget !== this._targets[i] && scrollTop >= this._offsets[i] && (this._offsets[i + 1] === undefined || scrollTop < this._offsets[i + 1]) @@ -243,7 +250,7 @@ const ScrollSpy = (($) => { `${selector}[href="${target}"]` }) - let $link = $(queries.join(',')) + const $link = $(queries.join(',')) if ($link.hasClass(ClassName.DROPDOWN_ITEM)) { $link.closest(Selector.DROPDOWN).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE) @@ -251,7 +258,7 @@ const ScrollSpy = (($) => { } else { // todo (fat) this is kinda sus... // recursively add actives to tested nav-links - $link.parents(Selector.LI).find(Selector.NAV_LINKS).addClass(ClassName.ACTIVE) + $link.parents(Selector.LI).find(`> ${Selector.NAV_LINKS}`).addClass(ClassName.ACTIVE) } $(this._scrollElement).trigger(Event.ACTIVATE, { @@ -268,8 +275,8 @@ const ScrollSpy = (($) => { static _jQueryInterface(config) { return this.each(function () { - let data = $(this).data(DATA_KEY) - let _config = typeof config === 'object' && config || null + let data = $(this).data(DATA_KEY) + const _config = typeof config === 'object' && config if (!data) { data = new ScrollSpy(this, _config) @@ -296,10 +303,10 @@ const ScrollSpy = (($) => { */ $(window).on(Event.LOAD_DATA_API, () => { - let scrollSpys = $.makeArray($(Selector.DATA_SPY)) + const scrollSpys = $.makeArray($(Selector.DATA_SPY)) for (let i = scrollSpys.length; i--;) { - let $spy = $(scrollSpys[i]) + const $spy = $(scrollSpys[i]) ScrollSpy._jQueryInterface.call($spy, $spy.data()) } }) diff --git a/js/src/tab.js b/js/src/tab.js index 33f56faae..2f4e453e0 100644 --- a/js/src/tab.js +++ b/js/src/tab.js @@ -3,7 +3,7 @@ import Util from './util' /** * -------------------------------------------------------------------------- - * Bootstrap (v4.0.0-alpha.2): tab.js + * Bootstrap (v4.0.0-alpha.5): tab.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * -------------------------------------------------------------------------- */ @@ -18,7 +18,7 @@ const Tab = (($) => { */ const NAME = 'tab' - const VERSION = '4.0.0-alpha.2' + const VERSION = '4.0.0-alpha.5' const DATA_KEY = 'bs.tab' const EVENT_KEY = `.${DATA_KEY}` const DATA_API_KEY = '.data-api' @@ -36,15 +36,16 @@ const Tab = (($) => { const ClassName = { DROPDOWN_MENU : 'dropdown-menu', ACTIVE : 'active', + DISABLED : 'disabled', FADE : 'fade', - IN : 'in' + SHOW : 'show' } const Selector = { A : 'a', LI : 'li', DROPDOWN : '.dropdown', - UL : 'ul:not(.dropdown-menu)', + LIST : 'ul:not(.dropdown-menu), ol:not(.dropdown-menu)', FADE_CHILD : '> .nav-item .fade, > .fade', ACTIVE : '.active', ACTIVE_CHILD : '> .nav-item > .active, > .active', @@ -78,26 +79,27 @@ const Tab = (($) => { show() { if (this._element.parentNode && - (this._element.parentNode.nodeType === Node.ELEMENT_NODE) && - ($(this._element).hasClass(ClassName.ACTIVE))) { + this._element.parentNode.nodeType === Node.ELEMENT_NODE && + $(this._element).hasClass(ClassName.ACTIVE) || + $(this._element).hasClass(ClassName.DISABLED)) { return } let target let previous - let ulElement = $(this._element).closest(Selector.UL)[0] - let selector = Util.getSelectorFromElement(this._element) + const listElement = $(this._element).closest(Selector.LIST)[0] + const selector = Util.getSelectorFromElement(this._element) - if (ulElement) { - previous = $.makeArray($(ulElement).find(Selector.ACTIVE)) + if (listElement) { + previous = $.makeArray($(listElement).find(Selector.ACTIVE)) previous = previous[previous.length - 1] } - let hideEvent = $.Event(Event.HIDE, { + const hideEvent = $.Event(Event.HIDE, { relatedTarget: this._element }) - let showEvent = $.Event(Event.SHOW, { + const showEvent = $.Event(Event.SHOW, { relatedTarget: previous }) @@ -108,7 +110,7 @@ const Tab = (($) => { $(this._element).trigger(showEvent) if (showEvent.isDefaultPrevented() || - (hideEvent.isDefaultPrevented())) { + hideEvent.isDefaultPrevented()) { return } @@ -118,15 +120,15 @@ const Tab = (($) => { this._activate( this._element, - ulElement + listElement ) - let complete = () => { - let hiddenEvent = $.Event(Event.HIDDEN, { + const complete = () => { + const hiddenEvent = $.Event(Event.HIDDEN, { relatedTarget: this._element }) - let shownEvent = $.Event(Event.SHOWN, { + const shownEvent = $.Event(Event.SHOWN, { relatedTarget: previous }) @@ -150,15 +152,13 @@ const Tab = (($) => { // private _activate(element, container, callback) { - let active = $(container).find(Selector.ACTIVE_CHILD)[0] - let isTransitioning = callback + const active = $(container).find(Selector.ACTIVE_CHILD)[0] + const isTransitioning = callback && Util.supportsTransitionEnd() - && ((active && $(active).hasClass(ClassName.FADE)) + && (active && $(active).hasClass(ClassName.FADE) || Boolean($(container).find(Selector.FADE_CHILD)[0])) - let complete = $.proxy( - this._transitionComplete, - this, + const complete = () => this._transitionComplete( element, active, isTransitioning, @@ -175,7 +175,7 @@ const Tab = (($) => { } if (active) { - $(active).removeClass(ClassName.IN) + $(active).removeClass(ClassName.SHOW) } } @@ -183,7 +183,7 @@ const Tab = (($) => { if (active) { $(active).removeClass(ClassName.ACTIVE) - let dropdownChild = $(active).find( + const dropdownChild = $(active.parentNode).find( Selector.DROPDOWN_ACTIVE_CHILD )[0] @@ -199,15 +199,15 @@ const Tab = (($) => { if (isTransitioning) { Util.reflow(element) - $(element).addClass(ClassName.IN) + $(element).addClass(ClassName.SHOW) } else { $(element).removeClass(ClassName.FADE) } if (element.parentNode && - ($(element.parentNode).hasClass(ClassName.DROPDOWN_MENU))) { + $(element.parentNode).hasClass(ClassName.DROPDOWN_MENU)) { - let dropdownElement = $(element).closest(Selector.DROPDOWN)[0] + const dropdownElement = $(element).closest(Selector.DROPDOWN)[0] if (dropdownElement) { $(dropdownElement).find(Selector.DROPDOWN_TOGGLE).addClass(ClassName.ACTIVE) } @@ -225,11 +225,11 @@ const Tab = (($) => { static _jQueryInterface(config) { return this.each(function () { - let $this = $(this) - let data = $this.data(DATA_KEY) + const $this = $(this) + let data = $this.data(DATA_KEY) if (!data) { - data = data = new Tab(this) + data = new Tab(this) $this.data(DATA_KEY, data) } diff --git a/js/src/tooltip.js b/js/src/tooltip.js index 77803dc40..0c1d381b9 100644 --- a/js/src/tooltip.js +++ b/js/src/tooltip.js @@ -5,7 +5,7 @@ import Util from './util' /** * -------------------------------------------------------------------------- - * Bootstrap (v4.0.0-alpha.2): tooltip.js + * Bootstrap (v4.0.0-alpha.5): tooltip.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * -------------------------------------------------------------------------- */ @@ -14,10 +14,10 @@ const Tooltip = (($) => { /** * Check for Tether dependency - * Tether - http://github.hubspot.com/tether/ + * Tether - http://tether.io/ */ - if (window.Tether === undefined) { - throw new Error('Bootstrap tooltips require Tether (http://github.hubspot.com/tether/)') + if (typeof Tether === 'undefined') { + throw new Error('Bootstrap tooltips require Tether (http://tether.io/)') } @@ -28,7 +28,7 @@ const Tooltip = (($) => { */ const NAME = 'tooltip' - const VERSION = '4.0.0-alpha.2' + const VERSION = '4.0.0-alpha.5' const DATA_KEY = 'bs.tooltip' const EVENT_KEY = `.${DATA_KEY}` const JQUERY_NO_CONFLICT = $.fn[NAME] @@ -38,7 +38,6 @@ const Tooltip = (($) => { const Default = { animation : true, template : '<div class="tooltip" role="tooltip">' - + '<div class="tooltip-arrow"></div>' + '<div class="tooltip-inner"></div></div>', trigger : 'hover focus', title : '', @@ -47,7 +46,8 @@ const Tooltip = (($) => { selector : false, placement : 'top', offset : '0 0', - constraints : [] + constraints : [], + container : false } const DefaultType = { @@ -60,7 +60,8 @@ const Tooltip = (($) => { selector : '(string|boolean)', placement : '(string|function)', offset : 'string', - constraints : 'array' + constraints : 'array', + container : '(string|element|boolean)' } const AttachmentMap = { @@ -71,8 +72,8 @@ const Tooltip = (($) => { } const HoverState = { - IN : 'in', - OUT : 'out' + SHOW : 'show', + OUT : 'out' } const Event = { @@ -90,7 +91,7 @@ const Tooltip = (($) => { const ClassName = { FADE : 'fade', - IN : 'in' + SHOW : 'show' } const Selector = { @@ -122,11 +123,12 @@ const Tooltip = (($) => { constructor(element, config) { // private - this._isEnabled = true - this._timeout = 0 - this._hoverState = '' - this._activeTrigger = {} - this._tether = null + this._isEnabled = true + this._timeout = 0 + this._hoverState = '' + this._activeTrigger = {} + this._isTransitioning = false + this._tether = null // protected this.element = element @@ -185,7 +187,7 @@ const Tooltip = (($) => { toggle(event) { if (event) { - let dataKey = this.constructor.DATA_KEY + const dataKey = this.constructor.DATA_KEY let context = $(event.currentTarget).data(dataKey) if (!context) { @@ -206,7 +208,7 @@ const Tooltip = (($) => { } else { - if ($(this.getTipElement()).hasClass(ClassName.IN)) { + if ($(this.getTipElement()).hasClass(ClassName.SHOW)) { this._leave(null, this) return } @@ -223,16 +225,17 @@ const Tooltip = (($) => { $.removeData(this.element, this.constructor.DATA_KEY) $(this.element).off(this.constructor.EVENT_KEY) + $(this.element).closest('.modal').off('hide.bs.modal') if (this.tip) { $(this.tip).remove() } - this._isEnabled = null - this._timeout = null - this._hoverState = null - this._activeTrigger = null - this._tether = null + this._isEnabled = null + this._timeout = null + this._hoverState = null + this._activeTrigger = null + this._tether = null this.element = null this.config = null @@ -240,12 +243,18 @@ const Tooltip = (($) => { } show() { - let showEvent = $.Event(this.constructor.Event.SHOW) + if ($(this.element).css('display') === 'none') { + throw new Error('Please use show on visible elements') + } + const showEvent = $.Event(this.constructor.Event.SHOW) if (this.isWithContent() && this._isEnabled) { + if (this._isTransitioning) { + throw new Error('Tooltip is transitioning') + } $(this.element).trigger(showEvent) - let isInTheDom = $.contains( + const isInTheDom = $.contains( this.element.ownerDocument.documentElement, this.element ) @@ -254,8 +263,8 @@ const Tooltip = (($) => { return } - let tip = this.getTipElement() - let tipId = Util.getUID(this.constructor.NAME) + const tip = this.getTipElement() + const tipId = Util.getUID(this.constructor.NAME) tip.setAttribute('id', tipId) this.element.setAttribute('aria-describedby', tipId) @@ -266,15 +275,17 @@ const Tooltip = (($) => { $(tip).addClass(ClassName.FADE) } - let placement = typeof this.config.placement === 'function' ? + const placement = typeof this.config.placement === 'function' ? this.config.placement.call(this, tip, this.element) : this.config.placement - let attachment = this._getAttachment(placement) + const attachment = this._getAttachment(placement) + + const container = this.config.container === false ? document.body : $(this.config.container) $(tip) .data(this.constructor.DATA_KEY, this) - .appendTo(document.body) + .appendTo(container) $(this.element).trigger(this.constructor.Event.INSERTED) @@ -292,11 +303,12 @@ const Tooltip = (($) => { Util.reflow(tip) this._tether.position() - $(tip).addClass(ClassName.IN) + $(tip).addClass(ClassName.SHOW) - let complete = () => { - let prevHoverState = this._hoverState + const complete = () => { + const prevHoverState = this._hoverState this._hoverState = null + this._isTransitioning = false $(this.element).trigger(this.constructor.Event.SHOWN) @@ -306,6 +318,7 @@ const Tooltip = (($) => { } if (Util.supportsTransitionEnd() && $(this.tip).hasClass(ClassName.FADE)) { + this._isTransitioning = true $(this.tip) .one(Util.TRANSITION_END, complete) .emulateTransitionEnd(Tooltip._TRANSITION_DURATION) @@ -317,15 +330,19 @@ const Tooltip = (($) => { } hide(callback) { - let tip = this.getTipElement() - let hideEvent = $.Event(this.constructor.Event.HIDE) - let complete = () => { - if (this._hoverState !== HoverState.IN && tip.parentNode) { + const tip = this.getTipElement() + const hideEvent = $.Event(this.constructor.Event.HIDE) + if (this._isTransitioning) { + throw new Error('Tooltip is transitioning') + } + const complete = () => { + if (this._hoverState !== HoverState.SHOW && tip.parentNode) { tip.parentNode.removeChild(tip) } this.element.removeAttribute('aria-describedby') $(this.element).trigger(this.constructor.Event.HIDDEN) + this._isTransitioning = false this.cleanupTether() if (callback) { @@ -339,11 +356,15 @@ const Tooltip = (($) => { return } - $(tip).removeClass(ClassName.IN) + $(tip).removeClass(ClassName.SHOW) - if (Util.supportsTransitionEnd() && - ($(this.tip).hasClass(ClassName.FADE))) { + this._activeTrigger[Trigger.CLICK] = false + this._activeTrigger[Trigger.FOCUS] = false + this._activeTrigger[Trigger.HOVER] = false + if (Util.supportsTransitionEnd() && + $(this.tip).hasClass(ClassName.FADE)) { + this._isTransitioning = true $(tip) .one(Util.TRANSITION_END, complete) .emulateTransitionEnd(TRANSITION_DURATION) @@ -363,23 +384,21 @@ const Tooltip = (($) => { } getTipElement() { - return (this.tip = this.tip || $(this.config.template)[0]) + return this.tip = this.tip || $(this.config.template)[0] } setContent() { - let $tip = $(this.getTipElement()) + const $tip = $(this.getTipElement()) this.setElementContent($tip.find(Selector.TOOLTIP_INNER), this.getTitle()) - $tip - .removeClass(ClassName.FADE) - .removeClass(ClassName.IN) + $tip.removeClass(`${ClassName.FADE} ${ClassName.SHOW}`) this.cleanupTether() } setElementContent($element, content) { - let html = this.config.html + const html = this.config.html if (typeof content === 'object' && (content.nodeType || content.jquery)) { // content is a DOM node or a jQuery if (html) { @@ -420,21 +439,21 @@ const Tooltip = (($) => { } _setListeners() { - let triggers = this.config.trigger.split(' ') + const triggers = this.config.trigger.split(' ') triggers.forEach((trigger) => { if (trigger === 'click') { $(this.element).on( this.constructor.Event.CLICK, this.config.selector, - $.proxy(this.toggle, this) + (event) => this.toggle(event) ) } else if (trigger !== Trigger.MANUAL) { - let eventIn = trigger === Trigger.HOVER ? + const eventIn = trigger === Trigger.HOVER ? this.constructor.Event.MOUSEENTER : this.constructor.Event.FOCUSIN - let eventOut = trigger === Trigger.HOVER ? + const eventOut = trigger === Trigger.HOVER ? this.constructor.Event.MOUSELEAVE : this.constructor.Event.FOCUSOUT @@ -442,14 +461,19 @@ const Tooltip = (($) => { .on( eventIn, this.config.selector, - $.proxy(this._enter, this) + (event) => this._enter(event) ) .on( eventOut, this.config.selector, - $.proxy(this._leave, this) + (event) => this._leave(event) ) } + + $(this.element).closest('.modal').on( + 'hide.bs.modal', + () => this.hide() + ) }) if (this.config.selector) { @@ -463,9 +487,9 @@ const Tooltip = (($) => { } _fixTitle() { - let titleType = typeof this.element.getAttribute('data-original-title') + const titleType = typeof this.element.getAttribute('data-original-title') if (this.element.getAttribute('title') || - (titleType !== 'string')) { + titleType !== 'string') { this.element.setAttribute( 'data-original-title', this.element.getAttribute('title') || '' @@ -475,7 +499,7 @@ const Tooltip = (($) => { } _enter(event, context) { - let dataKey = this.constructor.DATA_KEY + const dataKey = this.constructor.DATA_KEY context = context || $(event.currentTarget).data(dataKey) @@ -493,15 +517,15 @@ const Tooltip = (($) => { ] = true } - if ($(context.getTipElement()).hasClass(ClassName.IN) || - (context._hoverState === HoverState.IN)) { - context._hoverState = HoverState.IN + if ($(context.getTipElement()).hasClass(ClassName.SHOW) || + context._hoverState === HoverState.SHOW) { + context._hoverState = HoverState.SHOW return } clearTimeout(context._timeout) - context._hoverState = HoverState.IN + context._hoverState = HoverState.SHOW if (!context.config.delay || !context.config.delay.show) { context.show() @@ -509,14 +533,14 @@ const Tooltip = (($) => { } context._timeout = setTimeout(() => { - if (context._hoverState === HoverState.IN) { + if (context._hoverState === HoverState.SHOW) { context.show() } }, context.config.delay.show) } _leave(event, context) { - let dataKey = this.constructor.DATA_KEY + const dataKey = this.constructor.DATA_KEY context = context || $(event.currentTarget).data(dataKey) @@ -555,7 +579,7 @@ const Tooltip = (($) => { } _isWithActiveTrigger() { - for (let trigger in this._activeTrigger) { + for (const trigger in this._activeTrigger) { if (this._activeTrigger[trigger]) { return true } @@ -589,10 +613,10 @@ const Tooltip = (($) => { } _getDelegateConfig() { - let config = {} + const config = {} if (this.config) { - for (let key in this.config) { + for (const key in this.config) { if (this.constructor.Default[key] !== this.config[key]) { config[key] = this.config[key] } @@ -607,11 +631,10 @@ const Tooltip = (($) => { static _jQueryInterface(config) { return this.each(function () { - let data = $(this).data(DATA_KEY) - let _config = typeof config === 'object' ? - config : null + let data = $(this).data(DATA_KEY) + const _config = typeof config === 'object' && config - if (!data && /destroy|hide/.test(config)) { + if (!data && /dispose|hide/.test(config)) { return } diff --git a/js/src/util.js b/js/src/util.js index dba6e9bd6..06424fbfe 100644 --- a/js/src/util.js +++ b/js/src/util.js @@ -1,6 +1,6 @@ /** * -------------------------------------------------------------------------- - * Bootstrap (v4.0.0-alpha.2): util.js + * Bootstrap (v4.0.0-alpha.5): util.js * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) * -------------------------------------------------------------------------- */ @@ -16,6 +16,8 @@ const Util = (($) => { let transition = false + const MAX_UID = 1000000 + const TransitionEndEvent = { WebkitTransition : 'webkitTransitionEnd', MozTransition : 'transitionend', @@ -25,7 +27,7 @@ const Util = (($) => { // shoutout AngusCroll (https://goo.gl/pxwQGp) function toType(obj) { - return ({}).toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase() + return {}.toString.call(obj).match(/\s([a-zA-Z]+)/)[1].toLowerCase() } function isElement(obj) { @@ -38,8 +40,9 @@ const Util = (($) => { delegateType: transition.end, handle(event) { if ($(event.target).is(this)) { - return event.handleObj.handler.apply(this, arguments) + return event.handleObj.handler.apply(this, arguments) // eslint-disable-line prefer-rest-params } + return undefined } } } @@ -49,11 +52,13 @@ const Util = (($) => { return false } - let el = document.createElement('bootstrap') + const el = document.createElement('bootstrap') - for (let name in TransitionEndEvent) { + for (const name in TransitionEndEvent) { if (el.style[name] !== undefined) { - return { end: TransitionEndEvent[name] } + return { + end: TransitionEndEvent[name] + } } } @@ -93,15 +98,14 @@ const Util = (($) => { * -------------------------------------------------------------------------- */ - let Util = { + const Util = { TRANSITION_END: 'bsTransitionEnd', getUID(prefix) { do { - /* eslint-disable no-bitwise */ - prefix += ~~(Math.random() * 1000000) // "~~" acts like a faster Math.floor() here - /* eslint-enable no-bitwise */ + // eslint-disable-next-line no-bitwise + prefix += ~~(Math.random() * MAX_UID) // "~~" acts like a faster Math.floor() here } while (document.getElementById(prefix)) return prefix }, @@ -118,7 +122,7 @@ const Util = (($) => { }, reflow(element) { - new Function('bs', 'return bs')(element.offsetHeight) + return element.offsetHeight }, triggerTransitionEnd(element) { @@ -130,17 +134,12 @@ const Util = (($) => { }, typeCheckConfig(componentName, config, configTypes) { - for (let property in configTypes) { + for (const property in configTypes) { if (configTypes.hasOwnProperty(property)) { - let expectedTypes = configTypes[property] - let value = config[property] - let valueType - - if (value && isElement(value)) { - valueType = 'element' - } else { - valueType = toType(value) - } + const expectedTypes = configTypes[property] + const value = config[property] + const valueType = value && isElement(value) ? + 'element' : toType(value) if (!new RegExp(expectedTypes).test(valueType)) { throw new Error( |
