1
0
mirror of https://github.com/twbs/bootstrap.git synced 2025-02-24 03:40:10 +00:00

Rewrite carousel without jquery

This commit is contained in:
Johann-S 2017-08-24 22:22:02 +02:00 committed by XhmikosR
parent c5595e5b67
commit 44f38e4128
7 changed files with 353 additions and 207 deletions

View File

@ -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' import Util from './util'
/** /**
@ -112,7 +114,7 @@ class Carousel {
this._config = this._getConfig(config) this._config = this._getConfig(config)
this._element = element 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._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0
this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent) this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent)
@ -140,8 +142,7 @@ class Carousel {
nextWhenVisible() { nextWhenVisible() {
// Don't call next when the page isn't visible // Don't call next when the page isn't visible
// or the carousel or its parent isn't visible // or the carousel or its parent isn't visible
if (!document.hidden && if (!document.hidden && Util.isVisible(this._element)) {
($(this._element).is(':visible') && $(this._element).css('visibility') !== 'hidden')) {
this.next() this.next()
} }
} }
@ -157,7 +158,7 @@ class Carousel {
this._isPaused = true this._isPaused = true
} }
if (this._element.querySelector(Selector.NEXT_PREV)) { if (SelectorEngine.findOne(Selector.NEXT_PREV, this._element)) {
Util.triggerTransitionEnd(this._element) Util.triggerTransitionEnd(this._element)
this.cycle(true) this.cycle(true)
} }
@ -185,8 +186,7 @@ class Carousel {
} }
to(index) { 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) const activeIndex = this._getItemIndex(this._activeElement)
if (index > this._items.length - 1 || index < 0) { if (index > this._items.length - 1 || index < 0) {
@ -194,7 +194,7 @@ class Carousel {
} }
if (this._isSliding) { if (this._isSliding) {
$(this._element).one(Event.SLID, () => this.to(index)) EventHandler.one(this._element, Event.SLID, () => this.to(index))
return return
} }
@ -212,8 +212,8 @@ class Carousel {
} }
dispose() { dispose() {
$(this._element).off(EVENT_KEY) EventHandler.off(this._element, DATA_KEY)
$.removeData(this._element, DATA_KEY) Data.removeData(this._element, DATA_KEY)
this._items = null this._items = null
this._config = null this._config = null
@ -258,14 +258,15 @@ class Carousel {
_addEventListeners() { _addEventListeners() {
if (this._config.keyboard) { if (this._config.keyboard) {
$(this._element) EventHandler
.on(Event.KEYDOWN, (event) => this._keydown(event)) .on(this._element, Event.KEYDOWN, (event) => this._keydown(event))
} }
if (this._config.pause === 'hover') { if (this._config.pause === 'hover') {
$(this._element) EventHandler
.on(Event.MOUSEENTER, (event) => this.pause(event)) .on(this._element, Event.MOUSEENTER, (event) => this.pause(event))
.on(Event.MOUSELEAVE, (event) => this.cycle(event)) EventHandler
.on(this._element, Event.MOUSELEAVE, (event) => this.cycle(event))
} }
if (this._config.touch) { if (this._config.touch) {
@ -279,25 +280,25 @@ class Carousel {
} }
const start = (event) => { const start = (event) => {
if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) { if (this._pointerEvent && PointerType[event.pointerType.toUpperCase()]) {
this.touchStartX = event.originalEvent.clientX this.touchStartX = event.clientX
} else if (!this._pointerEvent) { } else if (!this._pointerEvent) {
this.touchStartX = event.originalEvent.touches[0].clientX this.touchStartX = event.touches[0].clientX
} }
} }
const move = (event) => { const move = (event) => {
// ensure swiping with one touch and not pinching // 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 this.touchDeltaX = 0
} else { } else {
this.touchDeltaX = event.originalEvent.touches[0].clientX - this.touchStartX this.touchDeltaX = event.touches[0].clientX - this.touchStartX
} }
} }
const end = (event) => { const end = (event) => {
if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) { if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) {
this.touchDeltaX = event.originalEvent.clientX - this.touchStartX this.touchDeltaX = event.clientX - this.touchStartX
} }
this._handleSwipe() 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) { if (this._pointerEvent) {
$(this._element).on(Event.POINTERDOWN, (event) => start(event)) EventHandler.on(this._element, Event.POINTERDOWN, (event) => start(event))
$(this._element).on(Event.POINTERUP, (event) => end(event)) EventHandler.on(this._element, Event.POINTERUP, (event) => end(event))
this._element.classList.add(ClassName.POINTER_EVENT) this._element.classList.add(ClassName.POINTER_EVENT)
} else { } else {
$(this._element).on(Event.TOUCHSTART, (event) => start(event)) EventHandler.on(this._element, Event.TOUCHSTART, (event) => start(event))
$(this._element).on(Event.TOUCHMOVE, (event) => move(event)) EventHandler.on(this._element, Event.TOUCHMOVE, (event) => move(event))
$(this._element).on(Event.TOUCHEND, (event) => end(event)) EventHandler.on(this._element, Event.TOUCHEND, (event) => end(event))
} }
} }
@ -351,8 +354,9 @@ class Carousel {
_getItemIndex(element) { _getItemIndex(element) {
this._items = element && element.parentNode 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) return this._items.indexOf(element)
} }
@ -377,40 +381,39 @@ class Carousel {
_triggerSlideEvent(relatedTarget, eventDirectionName) { _triggerSlideEvent(relatedTarget, eventDirectionName) {
const targetIndex = this._getItemIndex(relatedTarget) const targetIndex = this._getItemIndex(relatedTarget)
const fromIndex = this._getItemIndex(this._element.querySelector(Selector.ACTIVE_ITEM)) const fromIndex = this._getItemIndex(SelectorEngine.findOne(Selector.ACTIVE_ITEM, this._element))
const slideEvent = $.Event(Event.SLIDE, {
return EventHandler.trigger(this._element, Event.SLIDE, {
relatedTarget, relatedTarget,
direction: eventDirectionName, direction: eventDirectionName,
from: fromIndex, from: fromIndex,
to: targetIndex to: targetIndex
}) })
$(this._element).trigger(slideEvent)
return slideEvent
} }
_setActiveIndicatorElement(element) { _setActiveIndicatorElement(element) {
if (this._indicatorsElement) { if (this._indicatorsElement) {
const indicators = [].slice.call(this._indicatorsElement.querySelectorAll(Selector.ACTIVE)) const indicators = SelectorEngine.find(Selector.ACTIVE, this._indicatorsElement)
$(indicators) for (let i = 0; i < indicators.length; i++) {
.removeClass(ClassName.ACTIVE) indicators[i].classList.remove(ClassName.ACTIVE)
}
const nextIndicator = this._indicatorsElement.children[ const nextIndicator = this._indicatorsElement.children[
this._getItemIndex(element) this._getItemIndex(element)
] ]
if (nextIndicator) { if (nextIndicator) {
$(nextIndicator).addClass(ClassName.ACTIVE) nextIndicator.classList.add(ClassName.ACTIVE)
} }
} }
} }
_slide(direction, element) { _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 activeElementIndex = this._getItemIndex(activeElement)
const nextElement = element || activeElement && const nextElement = element || activeElement &&
this._getItemByDirection(direction, activeElement) this._getItemByDirection(direction, activeElement)
const nextElementIndex = this._getItemIndex(nextElement) const nextElementIndex = this._getItemIndex(nextElement)
const isCycling = Boolean(this._interval) const isCycling = Boolean(this._interval)
@ -428,13 +431,13 @@ class Carousel {
eventDirectionName = Direction.RIGHT eventDirectionName = Direction.RIGHT
} }
if (nextElement && $(nextElement).hasClass(ClassName.ACTIVE)) { if (nextElement && nextElement.classList.contains(ClassName.ACTIVE)) {
this._isSliding = false this._isSliding = false
return return
} }
const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName) const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName)
if (slideEvent.isDefaultPrevented()) { if (slideEvent.defaultPrevented) {
return return
} }
@ -451,51 +454,50 @@ class Carousel {
this._setActiveIndicatorElement(nextElement) this._setActiveIndicatorElement(nextElement)
const slidEvent = $.Event(Event.SLID, { if (this._element.classList.contains(ClassName.SLIDE)) {
nextElement.classList.add(orderClassName)
Util.reflow(nextElement)
activeElement.classList.add(directionalClassName)
nextElement.classList.add(directionalClassName)
const transitionDuration = Util.getTransitionDurationFromElement(activeElement)
EventHandler
.one(activeElement, Util.TRANSITION_END, () => {
nextElement.classList.remove(directionalClassName)
nextElement.classList.remove(orderClassName)
nextElement.classList.add(ClassName.ACTIVE)
activeElement.classList.remove(ClassName.ACTIVE)
activeElement.classList.remove(orderClassName)
activeElement.classList.remove(directionalClassName)
this._isSliding = false
setTimeout(() => {
EventHandler.trigger(this._element, Event.SLID, {
relatedTarget: nextElement, relatedTarget: nextElement,
direction: eventDirectionName, direction: eventDirectionName,
from: activeElementIndex, from: activeElementIndex,
to: nextElementIndex to: nextElementIndex
}) })
}, 0)
if ($(this._element).hasClass(ClassName.SLIDE)) {
$(nextElement).addClass(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
}
const transitionDuration = Util.getTransitionDurationFromElement(activeElement)
$(activeElement)
.one(Util.TRANSITION_END, () => {
$(nextElement)
.removeClass(`${directionalClassName} ${orderClassName}`)
.addClass(ClassName.ACTIVE)
$(activeElement).removeClass(`${ClassName.ACTIVE} ${orderClassName} ${directionalClassName}`)
this._isSliding = false
setTimeout(() => $(this._element).trigger(slidEvent), 0)
}) })
Util.emulateTransitionEnd(transitionDuration) Util.emulateTransitionEnd(activeElement, transitionDuration)
} else { } else {
$(activeElement).removeClass(ClassName.ACTIVE) activeElement.classList.remove(ClassName.ACTIVE)
$(nextElement).addClass(ClassName.ACTIVE) nextElement.classList.add(ClassName.ACTIVE)
this._isSliding = false this._isSliding = false
$(this._element).trigger(slidEvent) EventHandler.trigger(this._element, Event.SLID, {
relatedTarget: nextElement,
direction: eventDirectionName,
from: activeElementIndex,
to: nextElementIndex
})
} }
if (isCycling) { if (isCycling) {
@ -507,10 +509,10 @@ class Carousel {
static _jQueryInterface(config) { static _jQueryInterface(config) {
return this.each(function () { return this.each(function () {
let data = $(this).data(DATA_KEY) let data = Data.getData(this, DATA_KEY)
let _config = { let _config = {
...Default, ...Default,
...$(this).data() ...Data.getData(this, DATA_KEY)
} }
if (typeof config === 'object') { if (typeof config === 'object') {
@ -524,7 +526,7 @@ class Carousel {
if (!data) { if (!data) {
data = new Carousel(this, _config) data = new Carousel(this, _config)
$(this).data(DATA_KEY, data) Data.setData(this, DATA_KEY, data)
} }
if (typeof config === 'number') { if (typeof config === 'number') {
@ -548,15 +550,15 @@ class Carousel {
return return
} }
const target = $(selector)[0] const target = SelectorEngine.findOne(selector)
if (!target || !$(target).hasClass(ClassName.CAROUSEL)) { if (!target || !target.classList.contains(ClassName.CAROUSEL)) {
return return
} }
const config = { const config = {
...$(target).data(), ...Util.getDataAttributes(target),
...$(this).data() ...Util.getDataAttributes(this)
} }
const slideIndex = this.getAttribute('data-slide-to') const slideIndex = this.getAttribute('data-slide-to')
@ -567,7 +569,7 @@ class Carousel {
Carousel._jQueryInterface.call($(target), config) Carousel._jQueryInterface.call($(target), config)
if (slideIndex) { if (slideIndex) {
$(target).data(DATA_KEY).to(slideIndex) Data.getData(target, DATA_KEY).to(slideIndex)
} }
event.preventDefault() event.preventDefault()
@ -580,17 +582,17 @@ class Carousel {
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
*/ */
$(document) EventHandler
.on(Event.CLICK_DATA_API, Selector.DATA_SLIDE, Carousel._dataApiClickHandler) .on(document, Event.CLICK_DATA_API, Selector.DATA_SLIDE, Carousel._dataApiClickHandler)
$(window).on(Event.LOAD_DATA_API, () => { EventHandler.on(window, Event.LOAD_DATA_API, () => {
const carousels = [].slice.call(document.querySelectorAll(Selector.DATA_RIDE)) const carousels = Util.makeArray(SelectorEngine.find(Selector.DATA_RIDE))
for (let i = 0, len = carousels.length; i < len; i++) { for (let i = 0, len = carousels.length; i < len; i++) {
const $carousel = $(carousels[i]) Carousel._jQueryInterface.call($(carousels[i]), Data.getData(carousels[i], DATA_KEY))
Carousel._jQueryInterface.call($carousel, $carousel.data())
} }
}) })
/** /**
* ------------------------------------------------------------------------ * ------------------------------------------------------------------------
* jQuery * jQuery

View File

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

View File

@ -1,3 +1,5 @@
import Util from '../util'
/** /**
* -------------------------------------------------------------------------- * --------------------------------------------------------------------------
* Bootstrap (v4.0.0-beta): dom/eventHandler.js * Bootstrap (v4.0.0-beta): dom/eventHandler.js
@ -60,6 +62,7 @@ if (!window.Event || typeof window.Event !== 'function') {
const namespaceRegex = /[^.]*(?=\..*)\.|.*/ const namespaceRegex = /[^.]*(?=\..*)\.|.*/
const stripNameRegex = /\..*/ const stripNameRegex = /\..*/
const keyEventRegex = /^key/
// Events storage // Events storage
const eventRegistry = {} const eventRegistry = {}
@ -87,14 +90,29 @@ const nativeEvents = [
'error', 'abort', 'scroll' '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) { function bootstrapHandler(element, fn) {
return function (event) { return function (event) {
event = fixEvent(event)
return fn.apply(element, [event]) return fn.apply(element, [event])
} }
} }
function bootstrapDelegationHandler(selector, fn) { function bootstrapDelegationHandler(selector, fn) {
return function (event) { return function (event) {
event = fixEvent(event)
const domElements = document.querySelectorAll(selector) const domElements = document.querySelectorAll(selector)
for (let target = event.target; target && target !== this; target = target.parentNode) { for (let target = event.target; target && target !== this; target = target.parentNode) {
for (let i = domElements.length; i--;) { for (let i = domElements.length; i--;) {
@ -117,8 +135,15 @@ const EventHandler = {
const delegation = typeof handler === 'string' const delegation = typeof handler === 'string'
const originalHandler = delegation ? delegationFn : handler const originalHandler = delegation ? delegationFn : handler
// allow to get the native events from namespaced events ('click.bs.button' --> 'click') // allow to get the native events from namespaced events ('click.bs.button' --> 'click')
let typeEvent = originalTypeEvent.replace(stripNameRegex, '') let typeEvent = originalTypeEvent.replace(stripNameRegex, '')
const custom = customEvents[typeEvent]
if (custom) {
typeEvent = custom
}
const isNative = nativeEvents.indexOf(typeEvent) > -1 const isNative = nativeEvents.indexOf(typeEvent) > -1
if (!isNative) { if (!isNative) {
typeEvent = originalTypeEvent typeEvent = originalTypeEvent
@ -131,20 +156,17 @@ const EventHandler = {
} }
const fn = !delegation ? bootstrapHandler(element, handler) : bootstrapDelegationHandler(handler, delegationFn) const fn = !delegation ? bootstrapHandler(element, handler) : bootstrapDelegationHandler(handler, delegationFn)
fn.isDelegation = delegation
handlers[uid] = fn handlers[uid] = fn
originalHandler.uidEvent = uid originalHandler.uidEvent = uid
fn.originalHandler = originalHandler
element.addEventListener(typeEvent, fn, delegation) element.addEventListener(typeEvent, fn, delegation)
}, },
one(element, event, handler) { one(element, event, handler) {
function complete(e) { 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) EventHandler.off(element, event, complete)
handler.apply(element, [e])
} }
EventHandler.on(element, event, complete) EventHandler.on(element, event, complete)
}, },
@ -155,16 +177,46 @@ const EventHandler = {
return return
} }
const typeEvent = originalTypeEvent.replace(stripNameRegex, '')
const events = getEvent(element) 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
}
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]) { if (!events || !events[typeEvent]) {
return return
} }
const uidEvent = handler.uidEvent const uidEvent = handler.uidEvent
const fn = events[typeEvent][uidEvent] const fn = events[typeEvent][uidEvent]
element.removeEventListener(typeEvent, fn, false) element.removeEventListener(typeEvent, fn, fn.delegation)
delete events[typeEvent][uidEvent] delete events[typeEvent][uidEvent]
}
}, },
trigger(element, event, args) { trigger(element, event, args) {
@ -172,24 +224,27 @@ const EventHandler = {
(typeof element === 'undefined' || element === null)) { (typeof element === 'undefined' || element === null)) {
return null return null
} }
const typeEvent = event.replace(stripNameRegex, '') const typeEvent = event.replace(stripNameRegex, '')
const isNative = nativeEvents.indexOf(typeEvent) > -1 const isNative = nativeEvents.indexOf(typeEvent) > -1
let returnedEvent = null let evt = null
if (isNative) { if (isNative) {
const evt = document.createEvent('HTMLEvents') evt = document.createEvent('HTMLEvents')
evt.initEvent(typeEvent, true, true, typeof args !== 'undefined' ? args : {}) evt.initEvent(typeEvent, true, true)
element.dispatchEvent(evt)
returnedEvent = evt
} else { } else {
const eventToDispatch = new CustomEvent(event, { evt = new CustomEvent(event, {
bubbles: true, bubbles: true,
cancelable: true, cancelable: true
detail: typeof args !== 'undefined' ? args : {}
}) })
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
} }
} }

View File

@ -111,6 +111,59 @@ const Util = {
`but expected type "${expectedTypes}".`) `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
} }
}, },

View File

@ -13,7 +13,8 @@
"Carousel": false, "Carousel": false,
"Simulator": false, "Simulator": false,
"Toast": false, "Toast": false,
"EventHandler": false "EventHandler": false,
"Data": false
}, },
"parserOptions": { "parserOptions": {
"ecmaVersion": 5, "ecmaVersion": 5,

View File

@ -34,6 +34,7 @@ $(function () {
$.fn.bootstrapCarousel = $.fn.carousel.noConflict() $.fn.bootstrapCarousel = $.fn.carousel.noConflict()
}, },
afterEach: function () { afterEach: function () {
$('.carousel').bootstrapCarousel('dispose')
$.fn.carousel = $.fn.bootstrapCarousel $.fn.carousel = $.fn.bootstrapCarousel
delete $.fn.bootstrapCarousel delete $.fn.bootstrapCarousel
$('#qunit-fixture').html('') $('#qunit-fixture').html('')
@ -112,16 +113,18 @@ $(function () {
QUnit.test('should not fire slid when slide is prevented', function (assert) { QUnit.test('should not fire slid when slide is prevented', function (assert) {
assert.expect(1) assert.expect(1)
var done = assert.async() var done = assert.async()
$('<div class="carousel"/>') var $carousel = $('<div class="carousel"/>')
.on('slide.bs.carousel', function (e) { $carousel.appendTo('#qunit-fixture')
EventHandler.on($carousel[0], 'slide.bs.carousel', function (e) {
e.preventDefault() e.preventDefault()
assert.ok(true, 'slide event fired') assert.ok(true, 'slide event fired')
done() done()
}) })
.on('slid.bs.carousel', function () { EventHandler.on($carousel[0], 'slid.bs.carousel', function () {
assert.ok(false, 'slid event fired') assert.ok(false, 'slid event fired')
}) })
.bootstrapCarousel('next') $carousel.bootstrapCarousel('next')
}) })
QUnit.test('should reset when slide is prevented', function (assert) { QUnit.test('should reset when slide is prevented', function (assert) {
@ -147,10 +150,11 @@ $(function () {
'<a class="right carousel-control" href="#carousel-example-generic" data-slide="next"/>' + '<a class="right carousel-control" href="#carousel-example-generic" data-slide="next"/>' +
'</div>' '</div>'
var $carousel = $(carouselHTML) var $carousel = $(carouselHTML)
$carousel.appendTo('#qunit-fixture')
var done = assert.async() var done = assert.async()
$carousel EventHandler
.one('slide.bs.carousel', function (e) { .one($carousel[0], 'slide.bs.carousel', function (e) {
e.preventDefault() e.preventDefault()
setTimeout(function () { setTimeout(function () {
assert.ok($carousel.find('.carousel-item:nth-child(1)').is('.active'), 'first item still active') assert.ok($carousel.find('.carousel-item:nth-child(1)').is('.active'), 'first item still active')
@ -158,7 +162,9 @@ $(function () {
$carousel.bootstrapCarousel('next') $carousel.bootstrapCarousel('next')
}, 0) }, 0)
}) })
.one('slid.bs.carousel', function () {
EventHandler
.one($carousel[0], 'slid.bs.carousel', function () {
setTimeout(function () { setTimeout(function () {
assert.ok(!$carousel.find('.carousel-item:nth-child(1)').is('.active'), 'first item still active') 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') assert.ok(!$carousel.find('.carousel-indicators li:nth-child(1)').is('.active'), 'first indicator still active')
@ -167,7 +173,8 @@ $(function () {
done() done()
}, 0) }, 0)
}) })
.bootstrapCarousel('next')
$carousel.bootstrapCarousel('next')
}) })
QUnit.test('should fire slide event with direction', function (assert) { QUnit.test('should fire slide event with direction', function (assert) {
@ -206,23 +213,24 @@ $(function () {
'<a class="right carousel-control" href="#myCarousel" data-slide="next">&rsaquo;</a>' + '<a class="right carousel-control" href="#myCarousel" data-slide="next">&rsaquo;</a>' +
'</div>' '</div>'
var $carousel = $(carouselHTML) var $carousel = $(carouselHTML)
$carousel.appendTo('#qunit-fixture')
var done = assert.async() var done = assert.async()
$carousel EventHandler
.one('slide.bs.carousel', function (e) { .one($carousel[0], 'slide.bs.carousel', function (e) {
assert.ok(e.direction, 'direction present on next') assert.ok(e.direction, 'direction present on next')
assert.strictEqual(e.direction, 'left', 'direction is left on next') assert.strictEqual(e.direction, 'left', 'direction is left on next')
$carousel EventHandler
.one('slide.bs.carousel', function (e) { .one($carousel[0], 'slide.bs.carousel', function (e) {
assert.ok(e.direction, 'direction present on prev') assert.ok(e.direction, 'direction present on prev')
assert.strictEqual(e.direction, 'right', 'direction is right on prev') assert.strictEqual(e.direction, 'right', 'direction is right on prev')
done() done()
}) })
.bootstrapCarousel('prev') $carousel.bootstrapCarousel('prev')
}) })
.bootstrapCarousel('next') $carousel.bootstrapCarousel('next')
}) })
QUnit.test('should fire slid event with direction', function (assert) { QUnit.test('should fire slid event with direction', function (assert) {
@ -261,23 +269,24 @@ $(function () {
'<a class="right carousel-control" href="#myCarousel" data-slide="next">&rsaquo;</a>' + '<a class="right carousel-control" href="#myCarousel" data-slide="next">&rsaquo;</a>' +
'</div>' '</div>'
var $carousel = $(carouselHTML) var $carousel = $(carouselHTML)
$carousel.appendTo('#qunit-fixture')
var done = assert.async() var done = assert.async()
$carousel EventHandler
.one('slid.bs.carousel', function (e) { .one($carousel[0], 'slid.bs.carousel', function (e) {
assert.ok(e.direction, 'direction present on next') assert.ok(e.direction, 'direction present on next')
assert.strictEqual(e.direction, 'left', 'direction is left on next') assert.strictEqual(e.direction, 'left', 'direction is left on next')
$carousel EventHandler
.one('slid.bs.carousel', function (e) { .one($carousel[0], 'slid.bs.carousel', function (e) {
assert.ok(e.direction, 'direction present on prev') assert.ok(e.direction, 'direction present on prev')
assert.strictEqual(e.direction, 'right', 'direction is right on prev') assert.strictEqual(e.direction, 'right', 'direction is right on prev')
done() done()
}) })
.bootstrapCarousel('prev') $carousel.bootstrapCarousel('prev')
}) })
.bootstrapCarousel('next') $carousel.bootstrapCarousel('next')
}) })
QUnit.test('should fire slide event with relatedTarget', function (assert) { QUnit.test('should fire slide event with relatedTarget', function (assert) {
@ -317,14 +326,17 @@ $(function () {
'</div>' '</div>'
var done = assert.async() var done = assert.async()
var $carousel = $(template)
$carousel.appendTo('#qunit-fixture')
$(template) EventHandler
.on('slide.bs.carousel', function (e) { .one($carousel[0], 'slide.bs.carousel', function (e) {
assert.ok(e.relatedTarget, 'relatedTarget present') assert.ok(e.relatedTarget, 'relatedTarget present')
assert.ok($(e.relatedTarget).hasClass('carousel-item'), 'relatedTarget has class "item"') assert.ok($(e.relatedTarget).hasClass('carousel-item'), 'relatedTarget has class "item"')
done() done()
}) })
.bootstrapCarousel('next')
$carousel.bootstrapCarousel('next')
}) })
QUnit.test('should fire slid event with relatedTarget', function (assert) { QUnit.test('should fire slid event with relatedTarget', function (assert) {
@ -364,14 +376,17 @@ $(function () {
'</div>' '</div>'
var done = assert.async() var done = assert.async()
var $carousel = $(template)
$carousel.appendTo('#qunit-fixture')
$(template) EventHandler
.on('slid.bs.carousel', function (e) { .one($carousel[0], 'slid.bs.carousel', function (e) {
assert.ok(e.relatedTarget, 'relatedTarget present') assert.ok(e.relatedTarget, 'relatedTarget present')
assert.ok($(e.relatedTarget).hasClass('carousel-item'), 'relatedTarget has class "item"') assert.ok($(e.relatedTarget).hasClass('carousel-item'), 'relatedTarget has class "item"')
done() done()
}) })
.bootstrapCarousel('next')
$carousel.bootstrapCarousel('next')
}) })
QUnit.test('should fire slid and slide events with from and to', function (assert) { QUnit.test('should fire slid and slide events with from and to', function (assert) {
@ -402,19 +417,22 @@ $(function () {
'</div>' '</div>'
var done = assert.async() var done = assert.async()
$(template) var $carousel = $(template)
.on('slid.bs.carousel', function (e) {
EventHandler
.one($carousel[0], 'slid.bs.carousel', function (e) {
assert.ok(typeof e.from !== 'undefined', 'from present') assert.ok(typeof e.from !== 'undefined', 'from present')
assert.ok(typeof e.to !== 'undefined', 'to present') assert.ok(typeof e.to !== 'undefined', 'to present')
$(this).off()
done() 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.from !== 'undefined', 'from present')
assert.ok(typeof e.to !== 'undefined', 'to 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) { QUnit.test('should set interval from data attribute', function (assert) {
@ -456,26 +474,27 @@ $(function () {
$carousel.attr('data-interval', 1814) $carousel.attr('data-interval', 1814)
$carousel.appendTo('body') $carousel.appendTo('body')
$('[data-slide]').first().trigger('click') EventHandler.trigger($('[data-slide]').first()[0], 'click')
assert.strictEqual($carousel.data('bs.carousel')._config.interval, 1814) assert.strictEqual(Data.getData($carousel[0], 'bs.carousel')._config.interval, 1814)
$carousel.remove() $carousel.remove()
$carousel.appendTo('body').attr('data-modal', 'foobar') $carousel.appendTo('body').attr('data-modal', 'foobar')
$('[data-slide]').first().trigger('click') EventHandler.trigger($('[data-slide]').first()[0], 'click')
assert.strictEqual($carousel.data('bs.carousel')._config.interval, 1814, 'even if there is an data-modal attribute set') assert.strictEqual(Data.getData($carousel[0], 'bs.carousel')._config.interval, 1814, 'even if there is an data-modal attribute set')
$carousel.remove() $carousel.remove()
$carousel.appendTo('body') $carousel.appendTo('body')
$('[data-slide]').first().trigger('click') EventHandler.trigger($('[data-slide]').first()[0], 'click')
$carousel.attr('data-interval', 1860) $carousel.attr('data-interval', 1860)
$('[data-slide]').first().trigger('click') EventHandler.trigger($('[data-slide]').first()[0], 'click')
assert.strictEqual($carousel.data('bs.carousel')._config.interval, 1814, 'attributes should be read only on initialization') assert.strictEqual(Data.getData($carousel[0], 'bs.carousel')._config.interval, 1814, 'attributes should be read only on initialization')
$carousel.bootstrapCarousel('dispose')
$carousel.remove() $carousel.remove()
$carousel.attr('data-interval', false) $carousel.attr('data-interval', false)
$carousel.appendTo('body') $carousel.appendTo('body')
$carousel.bootstrapCarousel(1) $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() $carousel.remove()
}) })
@ -600,9 +619,13 @@ $(function () {
assert.strictEqual($template.find('.carousel-item')[1], $template.find('.active')[0], 'second item active') assert.strictEqual($template.find('.carousel-item')[1], $template.find('.active')[0], 'second item active')
<<<<<<< HEAD
$template.trigger($.Event('keydown', { $template.trigger($.Event('keydown', {
which: 37 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') 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') assert.strictEqual($template.find('.carousel-item')[0], $template.find('.active')[0], 'first item active')
<<<<<<< HEAD
$template.trigger($.Event('keydown', { $template.trigger($.Event('keydown', {
which: 39 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') assert.strictEqual($template.find('.carousel-item')[1], $template.find('.active')[0], 'second item active')
}) })
@ -649,6 +676,7 @@ $(function () {
$template.bootstrapCarousel() $template.bootstrapCarousel()
var done = assert.async() var done = assert.async()
<<<<<<< HEAD
var eventArrowDown = $.Event('keydown', { var eventArrowDown = $.Event('keydown', {
which: 40 which: 40
}) })
@ -658,16 +686,22 @@ $(function () {
$template.one('keydown', function (event) { $template.one('keydown', function (event) {
assert.strictEqual(event.isDefaultPrevented(), false) 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) { EventHandler.one($template[0], 'keydown', function (event) {
assert.strictEqual(event.isDefaultPrevented(), false) assert.strictEqual(event.defaultPrevented, false)
done() done()
}) })
$template.trigger(eventArrowUp) // arrow up
EventHandler.trigger($template[0], 'keydown', { which: 38 })
}) })
QUnit.test('should support disabling the keyboard navigation', function (assert) { QUnit.test('should support disabling the keyboard navigation', function (assert) {
@ -782,22 +816,21 @@ $(function () {
var done = assert.async() var done = assert.async()
$carousel EventHandler.one($carousel[0], 'slid.bs.carousel', function () {
.one('slid.bs.carousel', function () {
assert.strictEqual(getActiveId(), 'two', 'carousel slid from 1st to 2nd slide') assert.strictEqual(getActiveId(), 'two', 'carousel slid from 1st to 2nd slide')
$carousel
.one('slid.bs.carousel', function () { EventHandler.one($carousel[0], 'slid.bs.carousel', function () {
assert.strictEqual(getActiveId(), 'three', 'carousel slid from 2nd to 3rd slide') assert.strictEqual(getActiveId(), 'three', 'carousel slid from 2nd to 3rd slide')
$carousel
.one('slid.bs.carousel', function () { EventHandler.one($carousel[0], 'slid.bs.carousel', function () {
assert.strictEqual(getActiveId(), 'one', 'carousel wrapped around and slid from 3rd to 1st slide') assert.strictEqual(getActiveId(), 'one', 'carousel wrapped around and slid from 3rd to 1st slide')
done() done()
}) })
.bootstrapCarousel('next') $carousel.bootstrapCarousel('next')
}) })
.bootstrapCarousel('next') $carousel.bootstrapCarousel('next')
}) })
.bootstrapCarousel('next') $carousel.bootstrapCarousel('next')
}) })
QUnit.test('should wrap around from start to end when wrap option is true', function (assert) { 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() var done = assert.async()
$carousel EventHandler.one($carousel[0], 'slid.bs.carousel', function () {
.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') assert.strictEqual($carousel.find('.carousel-item.active').attr('id'), 'three', 'carousel wrapped around and slid from 1st to 3rd slide')
done() done()
}) })
.bootstrapCarousel('prev') $carousel.bootstrapCarousel('prev')
}) })
QUnit.test('should stay at the end when the next method is called and wrap is false', function (assert) { 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() var done = assert.async()
$carousel EventHandler.one($carousel[0], 'slid.bs.carousel', function () {
.one('slid.bs.carousel', function () {
assert.strictEqual(getActiveId(), 'two', 'carousel slid from 1st to 2nd slide') assert.strictEqual(getActiveId(), 'two', 'carousel slid from 1st to 2nd slide')
$carousel
.one('slid.bs.carousel', function () { EventHandler.one($carousel[0], 'slid.bs.carousel', function () {
assert.strictEqual(getActiveId(), 'three', 'carousel slid from 2nd to 3rd slide') assert.strictEqual(getActiveId(), 'three', 'carousel slid from 2nd to 3rd slide')
$carousel
.one('slid.bs.carousel', function () { EventHandler.one($carousel[0], 'slid.bs.carousel', function () {
assert.ok(false, 'carousel slid when it should not have slid') assert.ok(false, 'carousel slid when it should not have slid')
}) })
.bootstrapCarousel('next') $carousel.bootstrapCarousel('next')
assert.strictEqual(getActiveId(), 'three', 'carousel did not wrap around and stayed on 3rd slide') assert.strictEqual(getActiveId(), 'three', 'carousel did not wrap around and stayed on 3rd slide')
done() done()
}) })
.bootstrapCarousel('next') $carousel.bootstrapCarousel('next')
}) })
.bootstrapCarousel('next') $carousel.bootstrapCarousel('next')
}) })
QUnit.test('should stay at the start when the prev method is called and wrap is false', function (assert) { QUnit.test('should stay at the start when the prev method is called and wrap is false', function (assert) {
@ -906,11 +937,10 @@ $(function () {
'</div>' '</div>'
var $carousel = $(carouselHTML) var $carousel = $(carouselHTML)
$carousel EventHandler.one($carousel[0], 'slid.bs.carousel', function () {
.on('slid.bs.carousel', function () {
assert.ok(false, 'carousel slid when it should not have slid') assert.ok(false, 'carousel slid when it should not have slid')
}) })
.bootstrapCarousel('prev') $carousel.bootstrapCarousel('prev')
assert.strictEqual($carousel.find('.carousel-item.active').attr('id'), 'one', 'carousel did not wrap around and stayed on 1st slide') assert.strictEqual($carousel.find('.carousel-item.active').attr('id'), 'one', 'carousel did not wrap around and stayed on 1st slide')
}) })

View File

@ -47,18 +47,22 @@
<script src="../../../node_modules/jquery/dist/jquery.slim.min.js"></script> <script src="../../../node_modules/jquery/dist/jquery.slim.min.js"></script>
<script src="../../dist/dom/eventHandler.js"></script> <script src="../../dist/dom/eventHandler.js"></script>
<script src="../../dist/dom/selectorEngine.js"></script>
<script src="../../dist/dom/data.js"></script>
<script src="../../dist/util.js"></script> <script src="../../dist/util.js"></script>
<script src="../../dist/carousel.js"></script> <script src="../../dist/carousel.js"></script>
<script> <script>
$(function() { $(function() {
var t0, t1; var t0, t1;
var carousel = SelectorEngine.find('#carousel-example-generic')
// Test to show that the carousel doesn't slide when the current tab isn't visible // Test to show that the carousel doesn't slide when the current tab isn't visible
// Test to show that transition-duration can be changed with css // Test to show that transition-duration can be changed with css
$('#carousel-example-generic').on('slid.bs.carousel', function(event) { EventHandler.on(carousel, 'slid.bs.carousel', function (event) {
t1 = performance.now() t1 = performance.now()
console.log('transition-duration took' + (t1 - t0) + 'ms, slid at ', event.timeStamp) console.log('transition-duration took' + (t1 - t0) + 'ms, slid at ', event.timeStamp)
}).on('slide.bs.carousel', function() { })
EventHandler.on(carousel, 'slide.bs.carousel', function () {
t0 = performance.now() t0 = performance.now()
}) })
}) })