diff --git a/js/src/carousel.js b/js/src/carousel.js index fa0ce331a1b6..5bc83243b7b0 100644 --- a/js/src/carousel.js +++ b/js/src/carousel.js @@ -5,7 +5,9 @@ * -------------------------------------------------------------------------- */ -import $ from 'jquery' +import Data from './dom/data' +import EventHandler from './dom/eventHandler' +import SelectorEngine from './dom/selectorEngine' import Util from './util' /** @@ -112,7 +114,7 @@ class Carousel { this._config = this._getConfig(config) this._element = element - this._indicatorsElement = this._element.querySelector(Selector.INDICATORS) + this._indicatorsElement = SelectorEngine.findOne(Selector.INDICATORS, this._element) this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0 this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent) @@ -140,8 +142,7 @@ class Carousel { nextWhenVisible() { // Don't call next when the page isn't visible // or the carousel or its parent isn't visible - if (!document.hidden && - ($(this._element).is(':visible') && $(this._element).css('visibility') !== 'hidden')) { + if (!document.hidden && Util.isVisible(this._element)) { this.next() } } @@ -157,7 +158,7 @@ class Carousel { this._isPaused = true } - if (this._element.querySelector(Selector.NEXT_PREV)) { + if (SelectorEngine.findOne(Selector.NEXT_PREV, this._element)) { Util.triggerTransitionEnd(this._element) this.cycle(true) } @@ -185,8 +186,7 @@ class Carousel { } to(index) { - this._activeElement = this._element.querySelector(Selector.ACTIVE_ITEM) - + this._activeElement = SelectorEngine.findOne(Selector.ACTIVE_ITEM, this._element) const activeIndex = this._getItemIndex(this._activeElement) if (index > this._items.length - 1 || index < 0) { @@ -194,7 +194,7 @@ class Carousel { } if (this._isSliding) { - $(this._element).one(Event.SLID, () => this.to(index)) + EventHandler.one(this._element, Event.SLID, () => this.to(index)) return } @@ -212,8 +212,8 @@ class Carousel { } dispose() { - $(this._element).off(EVENT_KEY) - $.removeData(this._element, DATA_KEY) + EventHandler.off(this._element, DATA_KEY) + Data.removeData(this._element, DATA_KEY) this._items = null this._config = null @@ -258,14 +258,15 @@ class Carousel { _addEventListeners() { if (this._config.keyboard) { - $(this._element) - .on(Event.KEYDOWN, (event) => this._keydown(event)) + EventHandler + .on(this._element, Event.KEYDOWN, (event) => this._keydown(event)) } if (this._config.pause === 'hover') { - $(this._element) - .on(Event.MOUSEENTER, (event) => this.pause(event)) - .on(Event.MOUSELEAVE, (event) => this.cycle(event)) + EventHandler + .on(this._element, Event.MOUSEENTER, (event) => this.pause(event)) + EventHandler + .on(this._element, Event.MOUSELEAVE, (event) => this.cycle(event)) } if (this._config.touch) { @@ -279,25 +280,25 @@ class Carousel { } const start = (event) => { - if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) { - this.touchStartX = event.originalEvent.clientX + if (this._pointerEvent && PointerType[event.pointerType.toUpperCase()]) { + this.touchStartX = event.clientX } else if (!this._pointerEvent) { - this.touchStartX = event.originalEvent.touches[0].clientX + this.touchStartX = event.touches[0].clientX } } const move = (event) => { // ensure swiping with one touch and not pinching - if (event.originalEvent.touches && event.originalEvent.touches.length > 1) { + if (event.touches && event.touches.length > 1) { this.touchDeltaX = 0 } else { - this.touchDeltaX = event.originalEvent.touches[0].clientX - this.touchStartX + this.touchDeltaX = event.touches[0].clientX - this.touchStartX } } const end = (event) => { if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) { - this.touchDeltaX = event.originalEvent.clientX - this.touchStartX + this.touchDeltaX = event.clientX - this.touchStartX } this._handleSwipe() @@ -318,16 +319,18 @@ class Carousel { } } - $(this._element.querySelectorAll(Selector.ITEM_IMG)).on(Event.DRAG_START, (e) => e.preventDefault()) + [].slice.call(SelectorEngine.find(Selector.ITEM_IMG, this._element)).forEach((itemImg) => { + EventHandler.on(itemImg, Event.DRAG_START, (e) => e.preventDefault()) + }) if (this._pointerEvent) { - $(this._element).on(Event.POINTERDOWN, (event) => start(event)) - $(this._element).on(Event.POINTERUP, (event) => end(event)) + EventHandler.on(this._element, Event.POINTERDOWN, (event) => start(event)) + EventHandler.on(this._element, Event.POINTERUP, (event) => end(event)) this._element.classList.add(ClassName.POINTER_EVENT) } else { - $(this._element).on(Event.TOUCHSTART, (event) => start(event)) - $(this._element).on(Event.TOUCHMOVE, (event) => move(event)) - $(this._element).on(Event.TOUCHEND, (event) => end(event)) + EventHandler.on(this._element, Event.TOUCHSTART, (event) => start(event)) + EventHandler.on(this._element, Event.TOUCHMOVE, (event) => move(event)) + EventHandler.on(this._element, Event.TOUCHEND, (event) => end(event)) } } @@ -351,8 +354,9 @@ class Carousel { _getItemIndex(element) { this._items = element && element.parentNode - ? [].slice.call(element.parentNode.querySelectorAll(Selector.ITEM)) + ? [].slice.call(SelectorEngine.find(Selector.ITEM, element.parentNode)) : [] + return this._items.indexOf(element) } @@ -377,40 +381,39 @@ class Carousel { _triggerSlideEvent(relatedTarget, eventDirectionName) { const targetIndex = this._getItemIndex(relatedTarget) - const fromIndex = this._getItemIndex(this._element.querySelector(Selector.ACTIVE_ITEM)) - const slideEvent = $.Event(Event.SLIDE, { + const fromIndex = this._getItemIndex(SelectorEngine.findOne(Selector.ACTIVE_ITEM, this._element)) + + return EventHandler.trigger(this._element, Event.SLIDE, { relatedTarget, direction: eventDirectionName, from: fromIndex, to: targetIndex }) - - $(this._element).trigger(slideEvent) - - return slideEvent } _setActiveIndicatorElement(element) { if (this._indicatorsElement) { - const indicators = [].slice.call(this._indicatorsElement.querySelectorAll(Selector.ACTIVE)) - $(indicators) - .removeClass(ClassName.ACTIVE) + const indicators = SelectorEngine.find(Selector.ACTIVE, this._indicatorsElement) + for (let i = 0; i < indicators.length; i++) { + indicators[i].classList.remove(ClassName.ACTIVE) + } const nextIndicator = this._indicatorsElement.children[ this._getItemIndex(element) ] if (nextIndicator) { - $(nextIndicator).addClass(ClassName.ACTIVE) + nextIndicator.classList.add(ClassName.ACTIVE) } } } _slide(direction, element) { - const activeElement = this._element.querySelector(Selector.ACTIVE_ITEM) + const activeElement = SelectorEngine.findOne(Selector.ACTIVE_ITEM, this._element) const activeElementIndex = this._getItemIndex(activeElement) const nextElement = element || activeElement && this._getItemByDirection(direction, activeElement) + const nextElementIndex = this._getItemIndex(nextElement) const isCycling = Boolean(this._interval) @@ -428,13 +431,13 @@ class Carousel { eventDirectionName = Direction.RIGHT } - if (nextElement && $(nextElement).hasClass(ClassName.ACTIVE)) { + if (nextElement && nextElement.classList.contains(ClassName.ACTIVE)) { this._isSliding = false return } const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName) - if (slideEvent.isDefaultPrevented()) { + if (slideEvent.defaultPrevented) { return } @@ -451,51 +454,50 @@ class Carousel { this._setActiveIndicatorElement(nextElement) - const slidEvent = $.Event(Event.SLID, { - relatedTarget: nextElement, - direction: eventDirectionName, - from: activeElementIndex, - to: nextElementIndex - }) - - if ($(this._element).hasClass(ClassName.SLIDE)) { - $(nextElement).addClass(orderClassName) + if (this._element.classList.contains(ClassName.SLIDE)) { + nextElement.classList.add(orderClassName) Util.reflow(nextElement) - $(activeElement).addClass(directionalClassName) - $(nextElement).addClass(directionalClassName) - - const nextElementInterval = parseInt(nextElement.getAttribute('data-interval'), 10) - if (nextElementInterval) { - this._config.defaultInterval = this._config.defaultInterval || this._config.interval - this._config.interval = nextElementInterval - } else { - this._config.interval = this._config.defaultInterval || this._config.interval - } + activeElement.classList.add(directionalClassName) + nextElement.classList.add(directionalClassName) const transitionDuration = Util.getTransitionDurationFromElement(activeElement) - $(activeElement) - .one(Util.TRANSITION_END, () => { - $(nextElement) - .removeClass(`${directionalClassName} ${orderClassName}`) - .addClass(ClassName.ACTIVE) + EventHandler + .one(activeElement, Util.TRANSITION_END, () => { + nextElement.classList.remove(directionalClassName) + nextElement.classList.remove(orderClassName) + nextElement.classList.add(ClassName.ACTIVE) - $(activeElement).removeClass(`${ClassName.ACTIVE} ${orderClassName} ${directionalClassName}`) + activeElement.classList.remove(ClassName.ACTIVE) + activeElement.classList.remove(orderClassName) + activeElement.classList.remove(directionalClassName) this._isSliding = false - setTimeout(() => $(this._element).trigger(slidEvent), 0) + setTimeout(() => { + EventHandler.trigger(this._element, Event.SLID, { + relatedTarget: nextElement, + direction: eventDirectionName, + from: activeElementIndex, + to: nextElementIndex + }) + }, 0) }) - Util.emulateTransitionEnd(transitionDuration) + Util.emulateTransitionEnd(activeElement, transitionDuration) } else { - $(activeElement).removeClass(ClassName.ACTIVE) - $(nextElement).addClass(ClassName.ACTIVE) + activeElement.classList.remove(ClassName.ACTIVE) + nextElement.classList.add(ClassName.ACTIVE) this._isSliding = false - $(this._element).trigger(slidEvent) + EventHandler.trigger(this._element, Event.SLID, { + relatedTarget: nextElement, + direction: eventDirectionName, + from: activeElementIndex, + to: nextElementIndex + }) } if (isCycling) { @@ -507,10 +509,10 @@ class Carousel { static _jQueryInterface(config) { return this.each(function () { - let data = $(this).data(DATA_KEY) + let data = Data.getData(this, DATA_KEY) let _config = { ...Default, - ...$(this).data() + ...Data.getData(this, DATA_KEY) } if (typeof config === 'object') { @@ -524,7 +526,7 @@ class Carousel { if (!data) { data = new Carousel(this, _config) - $(this).data(DATA_KEY, data) + Data.setData(this, DATA_KEY, data) } if (typeof config === 'number') { @@ -548,15 +550,15 @@ class Carousel { return } - const target = $(selector)[0] + const target = SelectorEngine.findOne(selector) - if (!target || !$(target).hasClass(ClassName.CAROUSEL)) { + if (!target || !target.classList.contains(ClassName.CAROUSEL)) { return } const config = { - ...$(target).data(), - ...$(this).data() + ...Util.getDataAttributes(target), + ...Util.getDataAttributes(this) } const slideIndex = this.getAttribute('data-slide-to') @@ -567,7 +569,7 @@ class Carousel { Carousel._jQueryInterface.call($(target), config) if (slideIndex) { - $(target).data(DATA_KEY).to(slideIndex) + Data.getData(target, DATA_KEY).to(slideIndex) } event.preventDefault() @@ -580,17 +582,17 @@ class Carousel { * ------------------------------------------------------------------------ */ -$(document) - .on(Event.CLICK_DATA_API, Selector.DATA_SLIDE, Carousel._dataApiClickHandler) +EventHandler + .on(document, Event.CLICK_DATA_API, Selector.DATA_SLIDE, Carousel._dataApiClickHandler) -$(window).on(Event.LOAD_DATA_API, () => { - const carousels = [].slice.call(document.querySelectorAll(Selector.DATA_RIDE)) +EventHandler.on(window, Event.LOAD_DATA_API, () => { + const carousels = Util.makeArray(SelectorEngine.find(Selector.DATA_RIDE)) for (let i = 0, len = carousels.length; i < len; i++) { - const $carousel = $(carousels[i]) - Carousel._jQueryInterface.call($carousel, $carousel.data()) + Carousel._jQueryInterface.call($(carousels[i]), Data.getData(carousels[i], DATA_KEY)) } }) + /** * ------------------------------------------------------------------------ * jQuery diff --git a/js/src/dom/data.js b/js/src/dom/data.js index 3e8fdc009df1..655706fbc3a2 100644 --- a/js/src/dom/data.js +++ b/js/src/dom/data.js @@ -39,6 +39,7 @@ const mapData = (() => { const keyProperties = element.key if (keyProperties.key === key) { delete storeData[keyProperties.id] + delete element.key } } } diff --git a/js/src/dom/eventHandler.js b/js/src/dom/eventHandler.js index 42c91c0906e4..476f7af26f5c 100644 --- a/js/src/dom/eventHandler.js +++ b/js/src/dom/eventHandler.js @@ -1,3 +1,5 @@ +import Util from '../util' + /** * -------------------------------------------------------------------------- * Bootstrap (v4.0.0-beta): dom/eventHandler.js @@ -60,6 +62,7 @@ if (!window.Event || typeof window.Event !== 'function') { const namespaceRegex = /[^.]*(?=\..*)\.|.*/ const stripNameRegex = /\..*/ +const keyEventRegex = /^key/ // Events storage const eventRegistry = {} @@ -87,14 +90,29 @@ const nativeEvents = [ 'error', 'abort', 'scroll' ] +const customEvents = { + mouseenter: 'mouseover', + mouseleave: 'mouseout' +} + +function fixEvent(event) { + // Add which for key events + if (event.which === null && keyEventRegex.test(event.type)) { + event.which = event.charCode !== null ? event.charCode : event.keyCode + } + return event +} + function bootstrapHandler(element, fn) { return function (event) { + event = fixEvent(event) return fn.apply(element, [event]) } } function bootstrapDelegationHandler(selector, fn) { return function (event) { + event = fixEvent(event) const domElements = document.querySelectorAll(selector) for (let target = event.target; target && target !== this; target = target.parentNode) { for (let i = domElements.length; i--;) { @@ -117,8 +135,15 @@ const EventHandler = { const delegation = typeof handler === 'string' const originalHandler = delegation ? delegationFn : handler + // allow to get the native events from namespaced events ('click.bs.button' --> 'click') let typeEvent = originalTypeEvent.replace(stripNameRegex, '') + + const custom = customEvents[typeEvent] + if (custom) { + typeEvent = custom + } + const isNative = nativeEvents.indexOf(typeEvent) > -1 if (!isNative) { typeEvent = originalTypeEvent @@ -131,20 +156,17 @@ const EventHandler = { } const fn = !delegation ? bootstrapHandler(element, handler) : bootstrapDelegationHandler(handler, delegationFn) + fn.isDelegation = delegation handlers[uid] = fn originalHandler.uidEvent = uid + fn.originalHandler = originalHandler element.addEventListener(typeEvent, fn, delegation) }, one(element, event, handler) { function complete(e) { - const typeEvent = event.replace(stripNameRegex, '') - const events = getEvent(element) - if (!events || !events[typeEvent]) { - return - } - handler.apply(element, [e]) EventHandler.off(element, event, complete) + handler.apply(element, [e]) } EventHandler.on(element, event, complete) }, @@ -155,16 +177,46 @@ const EventHandler = { return } - const typeEvent = originalTypeEvent.replace(stripNameRegex, '') - const events = getEvent(element) - if (!events || !events[typeEvent]) { - return + const events = getEvent(element) + let typeEvent = originalTypeEvent.replace(stripNameRegex, '') + const inNamespace = typeEvent !== originalTypeEvent + const custom = customEvents[typeEvent] + if (custom) { + typeEvent = custom + } + const isNative = nativeEvents.indexOf(typeEvent) > -1 + if (!isNative) { + typeEvent = originalTypeEvent } - const uidEvent = handler.uidEvent - const fn = events[typeEvent][uidEvent] - element.removeEventListener(typeEvent, fn, false) - delete events[typeEvent][uidEvent] + if (typeof handler === 'undefined') { + for (const elementEvent in events) { + if (!Object.prototype.hasOwnProperty.call(events, elementEvent)) { + continue + } + + const storeElementEvent = events[elementEvent] + for (const keyHandlers in storeElementEvent) { + if (!Object.prototype.hasOwnProperty.call(storeElementEvent, keyHandlers)) { + continue + } + // delete all the namespaced listeners + if (inNamespace && keyHandlers.indexOf(originalTypeEvent) > -1) { + const handlerFn = events[elementEvent][keyHandlers] + EventHandler.off(element, elementEvent, handlerFn.originalHandler) + } + } + } + } else { + if (!events || !events[typeEvent]) { + return + } + + const uidEvent = handler.uidEvent + const fn = events[typeEvent][uidEvent] + element.removeEventListener(typeEvent, fn, fn.delegation) + delete events[typeEvent][uidEvent] + } }, trigger(element, event, args) { @@ -172,24 +224,27 @@ const EventHandler = { (typeof element === 'undefined' || element === null)) { return null } - const typeEvent = event.replace(stripNameRegex, '') - const isNative = nativeEvents.indexOf(typeEvent) > -1 - let returnedEvent = null + + const typeEvent = event.replace(stripNameRegex, '') + const isNative = nativeEvents.indexOf(typeEvent) > -1 + let evt = null + if (isNative) { - const evt = document.createEvent('HTMLEvents') - evt.initEvent(typeEvent, true, true, typeof args !== 'undefined' ? args : {}) - element.dispatchEvent(evt) - returnedEvent = evt + evt = document.createEvent('HTMLEvents') + evt.initEvent(typeEvent, true, true) } else { - const eventToDispatch = new CustomEvent(event, { + evt = new CustomEvent(event, { bubbles: true, - cancelable: true, - detail: typeof args !== 'undefined' ? args : {} + cancelable: true }) - element.dispatchEvent(eventToDispatch) - returnedEvent = eventToDispatch } - return returnedEvent + + // merge custom informations in our event + if (typeof args !== 'undefined') { + evt = Util.extend(evt, args) + } + element.dispatchEvent(evt) + return evt } } diff --git a/js/src/util.js b/js/src/util.js index fb777e4f73ff..ad147a1be7ca 100644 --- a/js/src/util.js +++ b/js/src/util.js @@ -111,6 +111,59 @@ const Util = { `but expected type "${expectedTypes}".`) } } + }, + + extend(obj1, obj2) { + for (const secondProp in obj2) { + if (Object.prototype.hasOwnProperty.call(obj2, secondProp)) { + const secondVal = obj2[secondProp] + // Is this value an object? If so, iterate over its properties, copying them over + if (secondVal && Object.prototype.toString.call(secondVal) === '[object Object]') { + obj1[secondProp] = obj1[secondProp] || {} + Util.extend(obj1[secondProp], secondVal) + } else { + obj1[secondProp] = secondVal + } + } + } + return obj1 + }, + + makeArray(nodeList) { + if (typeof nodeList === 'undefined' || nodeList === null) { + return [] + } + return Array.prototype.slice.call(nodeList) + }, + + getDataAttributes(element) { + if (typeof element === 'undefined' || element === null) { + return {} + } + + const attributes = {} + for (let i = 0; i < element.attributes.length; i++) { + const attribute = element.attributes[i] + if (attribute.nodeName.indexOf('data-') !== -1) { + // remove 'data-' part of the attribute name + const attributeName = attribute.nodeName.substring('data-'.length) + attributes[attributeName] = attribute.nodeValue + } + } + return attributes + }, + + isVisible(element) { + if (typeof element === 'undefined' || element === null) { + return false + } + + if (element.style !== null && element.parentNode !== null && typeof element.parentNode.style !== 'undefined') { + return element.style.display !== 'none' + && element.parentNode.style.display !== 'none' + && element.style.visibility !== 'hidden' + } + return false } }, diff --git a/js/tests/unit/.eslintrc.json b/js/tests/unit/.eslintrc.json index b07efd269d17..487ba6c168a4 100644 --- a/js/tests/unit/.eslintrc.json +++ b/js/tests/unit/.eslintrc.json @@ -13,7 +13,8 @@ "Carousel": false, "Simulator": false, "Toast": false, - "EventHandler": false + "EventHandler": false, + "Data": false }, "parserOptions": { "ecmaVersion": 5, diff --git a/js/tests/unit/carousel.js b/js/tests/unit/carousel.js index 0d2a0f327104..615318ad3238 100644 --- a/js/tests/unit/carousel.js +++ b/js/tests/unit/carousel.js @@ -34,6 +34,7 @@ $(function () { $.fn.bootstrapCarousel = $.fn.carousel.noConflict() }, afterEach: function () { + $('.carousel').bootstrapCarousel('dispose') $.fn.carousel = $.fn.bootstrapCarousel delete $.fn.bootstrapCarousel $('#qunit-fixture').html('') @@ -112,16 +113,18 @@ $(function () { QUnit.test('should not fire slid when slide is prevented', function (assert) { assert.expect(1) var done = assert.async() - $('
') - .on('slide.bs.carousel', function (e) { - e.preventDefault() - assert.ok(true, 'slide event fired') - done() - }) - .on('slid.bs.carousel', function () { - assert.ok(false, 'slid event fired') - }) - .bootstrapCarousel('next') + var $carousel = $('') + $carousel.appendTo('#qunit-fixture') + + EventHandler.on($carousel[0], 'slide.bs.carousel', function (e) { + e.preventDefault() + assert.ok(true, 'slide event fired') + done() + }) + EventHandler.on($carousel[0], 'slid.bs.carousel', function () { + assert.ok(false, 'slid event fired') + }) + $carousel.bootstrapCarousel('next') }) QUnit.test('should reset when slide is prevented', function (assert) { @@ -147,10 +150,11 @@ $(function () { '' + '' var $carousel = $(carouselHTML) + $carousel.appendTo('#qunit-fixture') var done = assert.async() - $carousel - .one('slide.bs.carousel', function (e) { + EventHandler + .one($carousel[0], 'slide.bs.carousel', function (e) { e.preventDefault() setTimeout(function () { assert.ok($carousel.find('.carousel-item:nth-child(1)').is('.active'), 'first item still active') @@ -158,7 +162,9 @@ $(function () { $carousel.bootstrapCarousel('next') }, 0) }) - .one('slid.bs.carousel', function () { + + EventHandler + .one($carousel[0], 'slid.bs.carousel', function () { setTimeout(function () { assert.ok(!$carousel.find('.carousel-item:nth-child(1)').is('.active'), 'first item still active') assert.ok(!$carousel.find('.carousel-indicators li:nth-child(1)').is('.active'), 'first indicator still active') @@ -167,7 +173,8 @@ $(function () { done() }, 0) }) - .bootstrapCarousel('next') + + $carousel.bootstrapCarousel('next') }) QUnit.test('should fire slide event with direction', function (assert) { @@ -206,23 +213,24 @@ $(function () { '›' + '' var $carousel = $(carouselHTML) + $carousel.appendTo('#qunit-fixture') var done = assert.async() - $carousel - .one('slide.bs.carousel', function (e) { + EventHandler + .one($carousel[0], 'slide.bs.carousel', function (e) { assert.ok(e.direction, 'direction present on next') assert.strictEqual(e.direction, 'left', 'direction is left on next') - $carousel - .one('slide.bs.carousel', function (e) { + EventHandler + .one($carousel[0], 'slide.bs.carousel', function (e) { assert.ok(e.direction, 'direction present on prev') assert.strictEqual(e.direction, 'right', 'direction is right on prev') done() }) - .bootstrapCarousel('prev') + $carousel.bootstrapCarousel('prev') }) - .bootstrapCarousel('next') + $carousel.bootstrapCarousel('next') }) QUnit.test('should fire slid event with direction', function (assert) { @@ -261,23 +269,24 @@ $(function () { '›' + '' var $carousel = $(carouselHTML) + $carousel.appendTo('#qunit-fixture') var done = assert.async() - $carousel - .one('slid.bs.carousel', function (e) { + EventHandler + .one($carousel[0], 'slid.bs.carousel', function (e) { assert.ok(e.direction, 'direction present on next') assert.strictEqual(e.direction, 'left', 'direction is left on next') - $carousel - .one('slid.bs.carousel', function (e) { + EventHandler + .one($carousel[0], 'slid.bs.carousel', function (e) { assert.ok(e.direction, 'direction present on prev') assert.strictEqual(e.direction, 'right', 'direction is right on prev') done() }) - .bootstrapCarousel('prev') + $carousel.bootstrapCarousel('prev') }) - .bootstrapCarousel('next') + $carousel.bootstrapCarousel('next') }) QUnit.test('should fire slide event with relatedTarget', function (assert) { @@ -317,14 +326,17 @@ $(function () { '' var done = assert.async() + var $carousel = $(template) + $carousel.appendTo('#qunit-fixture') - $(template) - .on('slide.bs.carousel', function (e) { + EventHandler + .one($carousel[0], 'slide.bs.carousel', function (e) { assert.ok(e.relatedTarget, 'relatedTarget present') assert.ok($(e.relatedTarget).hasClass('carousel-item'), 'relatedTarget has class "item"') done() }) - .bootstrapCarousel('next') + + $carousel.bootstrapCarousel('next') }) QUnit.test('should fire slid event with relatedTarget', function (assert) { @@ -364,14 +376,17 @@ $(function () { '' var done = assert.async() + var $carousel = $(template) + $carousel.appendTo('#qunit-fixture') - $(template) - .on('slid.bs.carousel', function (e) { + EventHandler + .one($carousel[0], 'slid.bs.carousel', function (e) { assert.ok(e.relatedTarget, 'relatedTarget present') assert.ok($(e.relatedTarget).hasClass('carousel-item'), 'relatedTarget has class "item"') done() }) - .bootstrapCarousel('next') + + $carousel.bootstrapCarousel('next') }) QUnit.test('should fire slid and slide events with from and to', function (assert) { @@ -402,19 +417,22 @@ $(function () { '' var done = assert.async() - $(template) - .on('slid.bs.carousel', function (e) { + var $carousel = $(template) + + EventHandler + .one($carousel[0], 'slid.bs.carousel', function (e) { assert.ok(typeof e.from !== 'undefined', 'from present') assert.ok(typeof e.to !== 'undefined', 'to present') - $(this).off() done() }) - .on('slide.bs.carousel', function (e) { + + EventHandler + .one($carousel[0], 'slide.bs.carousel', function (e) { assert.ok(typeof e.from !== 'undefined', 'from present') assert.ok(typeof e.to !== 'undefined', 'to present') - $(this).off('slide.bs.carousel') }) - .bootstrapCarousel('next') + + $carousel.bootstrapCarousel('next') }) QUnit.test('should set interval from data attribute', function (assert) { @@ -456,26 +474,27 @@ $(function () { $carousel.attr('data-interval', 1814) $carousel.appendTo('body') - $('[data-slide]').first().trigger('click') - assert.strictEqual($carousel.data('bs.carousel')._config.interval, 1814) + EventHandler.trigger($('[data-slide]').first()[0], 'click') + assert.strictEqual(Data.getData($carousel[0], 'bs.carousel')._config.interval, 1814) $carousel.remove() $carousel.appendTo('body').attr('data-modal', 'foobar') - $('[data-slide]').first().trigger('click') - assert.strictEqual($carousel.data('bs.carousel')._config.interval, 1814, 'even if there is an data-modal attribute set') + EventHandler.trigger($('[data-slide]').first()[0], 'click') + assert.strictEqual(Data.getData($carousel[0], 'bs.carousel')._config.interval, 1814, 'even if there is an data-modal attribute set') $carousel.remove() $carousel.appendTo('body') - $('[data-slide]').first().trigger('click') + EventHandler.trigger($('[data-slide]').first()[0], 'click') $carousel.attr('data-interval', 1860) - $('[data-slide]').first().trigger('click') - assert.strictEqual($carousel.data('bs.carousel')._config.interval, 1814, 'attributes should be read only on initialization') + EventHandler.trigger($('[data-slide]').first()[0], 'click') + assert.strictEqual(Data.getData($carousel[0], 'bs.carousel')._config.interval, 1814, 'attributes should be read only on initialization') + $carousel.bootstrapCarousel('dispose') $carousel.remove() $carousel.attr('data-interval', false) $carousel.appendTo('body') $carousel.bootstrapCarousel(1) - assert.strictEqual($carousel.data('bs.carousel')._config.interval, false, 'data attribute has higher priority than default options') + assert.strictEqual(Data.getData($carousel[0], 'bs.carousel')._config.interval, false, 'data attribute has higher priority than default options') $carousel.remove() }) @@ -600,9 +619,13 @@ $(function () { assert.strictEqual($template.find('.carousel-item')[1], $template.find('.active')[0], 'second item active') +<<<<<<< HEAD $template.trigger($.Event('keydown', { which: 37 })) +======= + EventHandler.trigger($template[0], 'keydown', { which: 37 }) +>>>>>>> fix unit test for carousel assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active') }) @@ -628,9 +651,13 @@ $(function () { assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active') +<<<<<<< HEAD $template.trigger($.Event('keydown', { which: 39 })) +======= + EventHandler.trigger($template[0], 'keydown', { which: 39 }) +>>>>>>> fix unit test for carousel assert.strictEqual($template.find('.carousel-item')[1], $template.find('.active')[0], 'second item active') }) @@ -649,6 +676,7 @@ $(function () { $template.bootstrapCarousel() var done = assert.async() +<<<<<<< HEAD var eventArrowDown = $.Event('keydown', { which: 40 }) @@ -658,16 +686,22 @@ $(function () { $template.one('keydown', function (event) { assert.strictEqual(event.isDefaultPrevented(), false) +======= + EventHandler.one($template[0], 'keydown', function (event) { + assert.strictEqual(event.defaultPrevented, false) +>>>>>>> fix unit test for carousel }) - $template.trigger(eventArrowDown) + // arrow down + EventHandler.trigger($template[0], 'keydown', { which: 40 }) - $template.one('keydown', function (event) { - assert.strictEqual(event.isDefaultPrevented(), false) + EventHandler.one($template[0], 'keydown', function (event) { + assert.strictEqual(event.defaultPrevented, false) done() }) - $template.trigger(eventArrowUp) + // arrow up + EventHandler.trigger($template[0], 'keydown', { which: 38 }) }) QUnit.test('should support disabling the keyboard navigation', function (assert) { @@ -782,22 +816,21 @@ $(function () { var done = assert.async() - $carousel - .one('slid.bs.carousel', function () { - assert.strictEqual(getActiveId(), 'two', 'carousel slid from 1st to 2nd slide') - $carousel - .one('slid.bs.carousel', function () { - assert.strictEqual(getActiveId(), 'three', 'carousel slid from 2nd to 3rd slide') - $carousel - .one('slid.bs.carousel', function () { - assert.strictEqual(getActiveId(), 'one', 'carousel wrapped around and slid from 3rd to 1st slide') - done() - }) - .bootstrapCarousel('next') - }) - .bootstrapCarousel('next') + EventHandler.one($carousel[0], 'slid.bs.carousel', function () { + assert.strictEqual(getActiveId(), 'two', 'carousel slid from 1st to 2nd slide') + + EventHandler.one($carousel[0], 'slid.bs.carousel', function () { + assert.strictEqual(getActiveId(), 'three', 'carousel slid from 2nd to 3rd slide') + + EventHandler.one($carousel[0], 'slid.bs.carousel', function () { + assert.strictEqual(getActiveId(), 'one', 'carousel wrapped around and slid from 3rd to 1st slide') + done() + }) + $carousel.bootstrapCarousel('next') }) - .bootstrapCarousel('next') + $carousel.bootstrapCarousel('next') + }) + $carousel.bootstrapCarousel('next') }) QUnit.test('should wrap around from start to end when wrap option is true', function (assert) { @@ -826,12 +859,11 @@ $(function () { var done = assert.async() - $carousel - .on('slid.bs.carousel', function () { - assert.strictEqual($carousel.find('.carousel-item.active').attr('id'), 'three', 'carousel wrapped around and slid from 1st to 3rd slide') - done() - }) - .bootstrapCarousel('prev') + EventHandler.one($carousel[0], 'slid.bs.carousel', function () { + assert.strictEqual($carousel.find('.carousel-item.active').attr('id'), 'three', 'carousel wrapped around and slid from 1st to 3rd slide') + done() + }) + $carousel.bootstrapCarousel('prev') }) QUnit.test('should stay at the end when the next method is called and wrap is false', function (assert) { @@ -863,23 +895,22 @@ $(function () { var done = assert.async() - $carousel - .one('slid.bs.carousel', function () { - assert.strictEqual(getActiveId(), 'two', 'carousel slid from 1st to 2nd slide') - $carousel - .one('slid.bs.carousel', function () { - assert.strictEqual(getActiveId(), 'three', 'carousel slid from 2nd to 3rd slide') - $carousel - .one('slid.bs.carousel', function () { - assert.ok(false, 'carousel slid when it should not have slid') - }) - .bootstrapCarousel('next') - assert.strictEqual(getActiveId(), 'three', 'carousel did not wrap around and stayed on 3rd slide') - done() - }) - .bootstrapCarousel('next') + EventHandler.one($carousel[0], 'slid.bs.carousel', function () { + assert.strictEqual(getActiveId(), 'two', 'carousel slid from 1st to 2nd slide') + + EventHandler.one($carousel[0], 'slid.bs.carousel', function () { + assert.strictEqual(getActiveId(), 'three', 'carousel slid from 2nd to 3rd slide') + + EventHandler.one($carousel[0], 'slid.bs.carousel', function () { + assert.ok(false, 'carousel slid when it should not have slid') + }) + $carousel.bootstrapCarousel('next') + assert.strictEqual(getActiveId(), 'three', 'carousel did not wrap around and stayed on 3rd slide') + done() }) - .bootstrapCarousel('next') + $carousel.bootstrapCarousel('next') + }) + $carousel.bootstrapCarousel('next') }) QUnit.test('should stay at the start when the prev method is called and wrap is false', function (assert) { @@ -906,11 +937,10 @@ $(function () { '' var $carousel = $(carouselHTML) - $carousel - .on('slid.bs.carousel', function () { - assert.ok(false, 'carousel slid when it should not have slid') - }) - .bootstrapCarousel('prev') + EventHandler.one($carousel[0], 'slid.bs.carousel', function () { + assert.ok(false, 'carousel slid when it should not have slid') + }) + $carousel.bootstrapCarousel('prev') assert.strictEqual($carousel.find('.carousel-item.active').attr('id'), 'one', 'carousel did not wrap around and stayed on 1st slide') }) diff --git a/js/tests/visual/carousel.html b/js/tests/visual/carousel.html index d1376b39b9f5..aa6c5e4c71e1 100644 --- a/js/tests/visual/carousel.html +++ b/js/tests/visual/carousel.html @@ -47,18 +47,22 @@