carousel.js 16.9 KB
Newer Older
fat's avatar
fat committed
1
2
/**
 * --------------------------------------------------------------------------
XhmikosR's avatar
XhmikosR committed
3
 * Bootstrap (v4.3.1): carousel.js
fat's avatar
fat committed
4
5
6
7
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 * --------------------------------------------------------------------------
 */

8
import {
9
  getjQuery,
10
11
  TRANSITION_END,
  emulateTransitionEnd,
12
  getElementFromSelector,
13
14
15
16
17
  getTransitionDurationFromElement,
  isVisible,
  reflow,
  triggerTransitionEnd,
  typeCheckConfig
18
19
20
21
22
} from './util/index'
import Data from './dom/data'
import EventHandler from './dom/event-handler'
import Manipulator from './dom/manipulator'
import SelectorEngine from './dom/selector-engine'
Johann-S's avatar
Johann-S committed
23

Johann-S's avatar
Johann-S committed
24
25
26
27
28
/**
 * ------------------------------------------------------------------------
 * Constants
 * ------------------------------------------------------------------------
 */
fat's avatar
fat committed
29

XhmikosR's avatar
XhmikosR committed
30
31
32
33
34
const NAME = 'carousel'
const VERSION = '4.3.1'
const DATA_KEY = 'bs.carousel'
const EVENT_KEY = `.${DATA_KEY}`
const DATA_API_KEY = '.data-api'
XhmikosR's avatar
XhmikosR committed
35

XhmikosR's avatar
XhmikosR committed
36
37
const ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key
const ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key
Johann-S's avatar
Johann-S committed
38
const TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch
XhmikosR's avatar
XhmikosR committed
39
const SWIPE_THRESHOLD = 40
Johann-S's avatar
Johann-S committed
40
41

const Default = {
XhmikosR's avatar
XhmikosR committed
42
43
44
45
46
47
  interval: 5000,
  keyboard: true,
  slide: false,
  pause: 'hover',
  wrap: true,
  touch: true
Johann-S's avatar
Johann-S committed
48
49
50
}

const DefaultType = {
XhmikosR's avatar
XhmikosR committed
51
52
53
54
55
56
  interval: '(number|boolean)',
  keyboard: 'boolean',
  slide: '(boolean|string)',
  pause: '(string|boolean)',
  wrap: 'boolean',
  touch: 'boolean'
Johann-S's avatar
Johann-S committed
57
58
}

XhmikosR's avatar
XhmikosR committed
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
const DIRECTION_NEXT = 'next'
const DIRECTION_PREV = 'prev'
const DIRECTION_LEFT = 'left'
const DIRECTION_RIGHT = 'right'

const EVENT_SLIDE = `slide${EVENT_KEY}`
const EVENT_SLID = `slid${EVENT_KEY}`
const EVENT_KEYDOWN = `keydown${EVENT_KEY}`
const EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}`
const EVENT_MOUSELEAVE = `mouseleave${EVENT_KEY}`
const EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`
const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}`
const EVENT_TOUCHEND = `touchend${EVENT_KEY}`
const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`
const EVENT_POINTERUP = `pointerup${EVENT_KEY}`
const EVENT_DRAG_START = `dragstart${EVENT_KEY}`
const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`
const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`

const CLASS_NAME_CAROUSEL = 'carousel'
const CLASS_NAME_ACTIVE = 'active'
const CLASS_NAME_SLIDE = 'slide'
const CLASS_NAME_RIGHT = 'carousel-item-right'
const CLASS_NAME_LEFT = 'carousel-item-left'
const CLASS_NAME_NEXT = 'carousel-item-next'
const CLASS_NAME_PREV = 'carousel-item-prev'
const CLASS_NAME_POINTER_EVENT = 'pointer-event'

const SELECTOR_ACTIVE = '.active'
const SELECTOR_ACTIVE_ITEM = '.active.carousel-item'
const SELECTOR_ITEM = '.carousel-item'
const SELECTOR_ITEM_IMG = '.carousel-item img'
const SELECTOR_NEXT_PREV = '.carousel-item-next, .carousel-item-prev'
const SELECTOR_INDICATORS = '.carousel-indicators'
const SELECTOR_DATA_SLIDE = '[data-slide], [data-slide-to]'
const SELECTOR_DATA_RIDE = '[data-ride="carousel"]'
fat's avatar
fat committed
95

Johann-S's avatar
Johann-S committed
96
const PointerType = {
XhmikosR's avatar
XhmikosR committed
97
98
  TOUCH: 'touch',
  PEN: 'pen'
Johann-S's avatar
Johann-S committed
99
100
}

Johann-S's avatar
Johann-S committed
101
102
103
104
105
106
107
/**
 * ------------------------------------------------------------------------
 * Class Definition
 * ------------------------------------------------------------------------
 */
class Carousel {
  constructor(element, config) {
XhmikosR's avatar
XhmikosR committed
108
109
    this._items = null
    this._interval = null
110
    this._activeElement = null
XhmikosR's avatar
XhmikosR committed
111
112
113
114
115
116
117
118
    this._isPaused = false
    this._isSliding = false
    this.touchTimeout = null
    this.touchStartX = 0
    this.touchDeltaX = 0

    this._config = this._getConfig(config)
    this._element = element
XhmikosR's avatar
XhmikosR committed
119
    this._indicatorsElement = SelectorEngine.findOne(SELECTOR_INDICATORS, this._element)
XhmikosR's avatar
XhmikosR committed
120
121
    this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0
    this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent)
fat's avatar
fat committed
122

Johann-S's avatar
Johann-S committed
123
    this._addEventListeners()
124
    Data.setData(element, DATA_KEY, this)
Johann-S's avatar
Johann-S committed
125
  }
fat's avatar
fat committed
126

Johann-S's avatar
Johann-S committed
127
  // Getters
128

Johann-S's avatar
Johann-S committed
129
130
131
  static get VERSION() {
    return VERSION
  }
132

Johann-S's avatar
Johann-S committed
133
134
135
  static get Default() {
    return Default
  }
136

Johann-S's avatar
Johann-S committed
137
  // Public
138

Johann-S's avatar
Johann-S committed
139
140
  next() {
    if (!this._isSliding) {
XhmikosR's avatar
XhmikosR committed
141
      this._slide(DIRECTION_NEXT)
142
    }
Johann-S's avatar
Johann-S committed
143
  }
fat's avatar
fat committed
144

Johann-S's avatar
Johann-S committed
145
146
147
  nextWhenVisible() {
    // Don't call next when the page isn't visible
    // or the carousel or its parent isn't visible
148
    if (!document.hidden && isVisible(this._element)) {
Johann-S's avatar
Johann-S committed
149
      this.next()
fat's avatar
fat committed
150
    }
Johann-S's avatar
Johann-S committed
151
  }
fat's avatar
fat committed
152

Johann-S's avatar
Johann-S committed
153
154
  prev() {
    if (!this._isSliding) {
XhmikosR's avatar
XhmikosR committed
155
      this._slide(DIRECTION_PREV)
fat's avatar
fat committed
156
    }
Johann-S's avatar
Johann-S committed
157
  }
fat's avatar
fat committed
158

Johann-S's avatar
Johann-S committed
159
160
161
  pause(event) {
    if (!event) {
      this._isPaused = true
162
163
    }

XhmikosR's avatar
XhmikosR committed
164
    if (SelectorEngine.findOne(SELECTOR_NEXT_PREV, this._element)) {
165
      triggerTransitionEnd(this._element)
Johann-S's avatar
Johann-S committed
166
      this.cycle(true)
fat's avatar
fat committed
167
168
    }

Johann-S's avatar
Johann-S committed
169
170
171
    clearInterval(this._interval)
    this._interval = null
  }
fat's avatar
fat committed
172

Johann-S's avatar
Johann-S committed
173
174
175
176
  cycle(event) {
    if (!event) {
      this._isPaused = false
    }
fat's avatar
fat committed
177

Johann-S's avatar
Johann-S committed
178
    if (this._interval) {
fat's avatar
fat committed
179
180
181
182
      clearInterval(this._interval)
      this._interval = null
    }

Johann-S's avatar
Johann-S committed
183
    if (this._config && this._config.interval && !this._isPaused) {
Johann-S's avatar
Johann-S committed
184
185
186
187
188
189
      this._interval = setInterval(
        (document.visibilityState ? this.nextWhenVisible : this.next).bind(this),
        this._config.interval
      )
    }
  }
fat's avatar
fat committed
190

Johann-S's avatar
Johann-S committed
191
  to(index) {
XhmikosR's avatar
XhmikosR committed
192
    this._activeElement = SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)
Johann-S's avatar
Johann-S committed
193
194
195
196
    const activeIndex = this._getItemIndex(this._activeElement)

    if (index > this._items.length - 1 || index < 0) {
      return
fat's avatar
fat committed
197
198
    }

Johann-S's avatar
Johann-S committed
199
    if (this._isSliding) {
XhmikosR's avatar
XhmikosR committed
200
      EventHandler.one(this._element, EVENT_SLID, () => this.to(index))
Johann-S's avatar
Johann-S committed
201
202
      return
    }
fat's avatar
fat committed
203

Johann-S's avatar
Johann-S committed
204
205
206
207
208
    if (activeIndex === index) {
      this.pause()
      this.cycle()
      return
    }
fat's avatar
fat committed
209

XhmikosR's avatar
XhmikosR committed
210
    const direction = index > activeIndex ?
XhmikosR's avatar
XhmikosR committed
211
212
      DIRECTION_NEXT :
      DIRECTION_PREV
fat's avatar
fat committed
213

Johann-S's avatar
Johann-S committed
214
215
    this._slide(direction, this._items[index])
  }
fat's avatar
fat committed
216

Johann-S's avatar
Johann-S committed
217
  dispose() {
218
    EventHandler.off(this._element, EVENT_KEY)
Johann-S's avatar
Johann-S committed
219
    Data.removeData(this._element, DATA_KEY)
Johann-S's avatar
Johann-S committed
220

XhmikosR's avatar
XhmikosR committed
221
222
223
224
225
226
227
    this._items = null
    this._config = null
    this._element = null
    this._interval = null
    this._isPaused = null
    this._isSliding = null
    this._activeElement = null
Johann-S's avatar
Johann-S committed
228
229
    this._indicatorsElement = null
  }
fat's avatar
fat committed
230

Johann-S's avatar
Johann-S committed
231
  // Private
fat's avatar
fat committed
232

Johann-S's avatar
Johann-S committed
233
234
235
236
  _getConfig(config) {
    config = {
      ...Default,
      ...config
fat's avatar
fat committed
237
    }
238
    typeCheckConfig(NAME, config, DefaultType)
Johann-S's avatar
Johann-S committed
239
240
    return config
  }
fat's avatar
fat committed
241

Johann-S's avatar
Johann-S committed
242
243
244
245
246
247
248
249
250
  _handleSwipe() {
    const absDeltax = Math.abs(this.touchDeltaX)

    if (absDeltax <= SWIPE_THRESHOLD) {
      return
    }

    const direction = absDeltax / this.touchDeltaX

251
252
    this.touchDeltaX = 0

Johann-S's avatar
Johann-S committed
253
254
255
256
257
258
259
260
261
262
263
    // swipe left
    if (direction > 0) {
      this.prev()
    }

    // swipe right
    if (direction < 0) {
      this.next()
    }
  }

Johann-S's avatar
Johann-S committed
264
265
  _addEventListeners() {
    if (this._config.keyboard) {
Johann-S's avatar
Johann-S committed
266
      EventHandler
XhmikosR's avatar
XhmikosR committed
267
        .on(this._element, EVENT_KEYDOWN, event => this._keydown(event))
fat's avatar
fat committed
268
269
    }

Johann-S's avatar
Johann-S committed
270
    if (this._config.pause === 'hover') {
Johann-S's avatar
Johann-S committed
271
      EventHandler
XhmikosR's avatar
XhmikosR committed
272
        .on(this._element, EVENT_MOUSEENTER, event => this.pause(event))
Johann-S's avatar
Johann-S committed
273
      EventHandler
XhmikosR's avatar
XhmikosR committed
274
        .on(this._element, EVENT_MOUSELEAVE, event => this.cycle(event))
Johann-S's avatar
Johann-S committed
275
276
    }

Johann-S's avatar
Johann-S committed
277
    if (this._config.touch && this._touchSupported) {
278
279
      this._addTouchEventListeners()
    }
Johann-S's avatar
Johann-S committed
280
281
282
  }

  _addTouchEventListeners() {
XhmikosR's avatar
XhmikosR committed
283
    const start = event => {
Johann-S's avatar
Johann-S committed
284
285
      if (this._pointerEvent && PointerType[event.pointerType.toUpperCase()]) {
        this.touchStartX = event.clientX
286
      } else if (!this._pointerEvent) {
Johann-S's avatar
Johann-S committed
287
        this.touchStartX = event.touches[0].clientX
Johann-S's avatar
Johann-S committed
288
289
      }
    }
Johann-S's avatar
Johann-S committed
290

XhmikosR's avatar
XhmikosR committed
291
    const move = event => {
Johann-S's avatar
Johann-S committed
292
      // ensure swiping with one touch and not pinching
Johann-S's avatar
Johann-S committed
293
      if (event.touches && event.touches.length > 1) {
Johann-S's avatar
Johann-S committed
294
295
        this.touchDeltaX = 0
      } else {
Johann-S's avatar
Johann-S committed
296
        this.touchDeltaX = event.touches[0].clientX - this.touchStartX
Johann-S's avatar
Johann-S committed
297
298
299
      }
    }

XhmikosR's avatar
XhmikosR committed
300
    const end = event => {
Johann-S's avatar
Johann-S committed
301
      if (this._pointerEvent && PointerType[event.pointerType.toUpperCase()]) {
Johann-S's avatar
Johann-S committed
302
        this.touchDeltaX = event.clientX - this.touchStartX
Johann-S's avatar
Johann-S committed
303
      }
Johann-S's avatar
Johann-S committed
304
305
306

      this._handleSwipe()
      if (this._config.pause === 'hover') {
Johann-S's avatar
Johann-S committed
307
308
309
310
311
312
313
        // If it's a touch-enabled device, mouseenter/leave are fired as
        // part of the mouse compatibility events on first tap - the carousel
        // would stop cycling until user tapped out of it;
        // here, we listen for touchend, explicitly pause the carousel
        // (as if it's the second time we tap on it, mouseenter compat event
        // is NOT fired) and after a timeout (to allow for mouse compatibility
        // events to fire) we explicitly restart cycling
Johann-S's avatar
Johann-S committed
314
315
316
317
318

        this.pause()
        if (this.touchTimeout) {
          clearTimeout(this.touchTimeout)
        }
XhmikosR's avatar
XhmikosR committed
319
320

        this.touchTimeout = setTimeout(event => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)
321
      }
Johann-S's avatar
Johann-S committed
322
323
    }

324
    SelectorEngine.find(SELECTOR_ITEM_IMG, this._element).forEach(itemImg => {
XhmikosR's avatar
XhmikosR committed
325
      EventHandler.on(itemImg, EVENT_DRAG_START, e => e.preventDefault())
Johann-S's avatar
Johann-S committed
326
    })
327

Johann-S's avatar
Johann-S committed
328
    if (this._pointerEvent) {
XhmikosR's avatar
XhmikosR committed
329
330
      EventHandler.on(this._element, EVENT_POINTERDOWN, event => start(event))
      EventHandler.on(this._element, EVENT_POINTERUP, event => end(event))
Johann-S's avatar
Johann-S committed
331

XhmikosR's avatar
XhmikosR committed
332
      this._element.classList.add(CLASS_NAME_POINTER_EVENT)
Johann-S's avatar
Johann-S committed
333
    } else {
XhmikosR's avatar
XhmikosR committed
334
335
336
      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))
Johann-S's avatar
Johann-S committed
337
    }
Johann-S's avatar
Johann-S committed
338
  }
fat's avatar
fat committed
339

Johann-S's avatar
Johann-S committed
340
341
342
  _keydown(event) {
    if (/input|textarea/i.test(event.target.tagName)) {
      return
fat's avatar
fat committed
343
344
    }

Johann-S's avatar
Johann-S committed
345
346
347
348
349
350
351
352
353
354
    switch (event.which) {
      case ARROW_LEFT_KEYCODE:
        event.preventDefault()
        this.prev()
        break
      case ARROW_RIGHT_KEYCODE:
        event.preventDefault()
        this.next()
        break
      default:
fat's avatar
fat committed
355
    }
Johann-S's avatar
Johann-S committed
356
  }
fat's avatar
fat committed
357

Johann-S's avatar
Johann-S committed
358
  _getItemIndex(element) {
XhmikosR's avatar
XhmikosR committed
359
    this._items = element && element.parentNode ?
360
      SelectorEngine.find(SELECTOR_ITEM, element.parentNode) :
XhmikosR's avatar
XhmikosR committed
361
      []
Johann-S's avatar
Johann-S committed
362

Johann-S's avatar
Johann-S committed
363
364
    return this._items.indexOf(element)
  }
fat's avatar
fat committed
365

Johann-S's avatar
Johann-S committed
366
  _getItemByDirection(direction, activeElement) {
XhmikosR's avatar
XhmikosR committed
367
368
    const isNextDirection = direction === DIRECTION_NEXT
    const isPrevDirection = direction === DIRECTION_PREV
XhmikosR's avatar
XhmikosR committed
369
370
    const activeIndex = this._getItemIndex(activeElement)
    const lastItemIndex = this._items.length - 1
371
372
    const isGoingToWrap = (isPrevDirection && activeIndex === 0) ||
                            (isNextDirection && activeIndex === lastItemIndex)
fat's avatar
fat committed
373

Johann-S's avatar
Johann-S committed
374
375
376
    if (isGoingToWrap && !this._config.wrap) {
      return activeElement
    }
fat's avatar
fat committed
377

XhmikosR's avatar
XhmikosR committed
378
    const delta = direction === DIRECTION_PREV ? -1 : 1
Johann-S's avatar
Johann-S committed
379
    const itemIndex = (activeIndex + delta) % this._items.length
fat's avatar
fat committed
380

XhmikosR's avatar
XhmikosR committed
381
382
383
    return itemIndex === -1 ?
      this._items[this._items.length - 1] :
      this._items[itemIndex]
Johann-S's avatar
Johann-S committed
384
  }
fat's avatar
fat committed
385

Johann-S's avatar
Johann-S committed
386
387
  _triggerSlideEvent(relatedTarget, eventDirectionName) {
    const targetIndex = this._getItemIndex(relatedTarget)
XhmikosR's avatar
XhmikosR committed
388
    const fromIndex = this._getItemIndex(SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element))
Johann-S's avatar
Johann-S committed
389

XhmikosR's avatar
XhmikosR committed
390
    return EventHandler.trigger(this._element, EVENT_SLIDE, {
Johann-S's avatar
Johann-S committed
391
392
393
394
395
396
      relatedTarget,
      direction: eventDirectionName,
      from: fromIndex,
      to: targetIndex
    })
  }
fat's avatar
fat committed
397

Johann-S's avatar
Johann-S committed
398
399
  _setActiveIndicatorElement(element) {
    if (this._indicatorsElement) {
XhmikosR's avatar
XhmikosR committed
400
      const indicators = SelectorEngine.find(SELECTOR_ACTIVE, this._indicatorsElement)
Johann-S's avatar
Johann-S committed
401
      for (let i = 0; i < indicators.length; i++) {
XhmikosR's avatar
XhmikosR committed
402
        indicators[i].classList.remove(CLASS_NAME_ACTIVE)
Johann-S's avatar
Johann-S committed
403
      }
fat's avatar
fat committed
404

Johann-S's avatar
Johann-S committed
405
406
407
      const nextIndicator = this._indicatorsElement.children[
        this._getItemIndex(element)
      ]
fat's avatar
fat committed
408

Johann-S's avatar
Johann-S committed
409
      if (nextIndicator) {
XhmikosR's avatar
XhmikosR committed
410
        nextIndicator.classList.add(CLASS_NAME_ACTIVE)
fat's avatar
fat committed
411
412
      }
    }
Johann-S's avatar
Johann-S committed
413
  }
fat's avatar
fat committed
414

Johann-S's avatar
Johann-S committed
415
  _slide(direction, element) {
XhmikosR's avatar
XhmikosR committed
416
    const activeElement = SelectorEngine.findOne(SELECTOR_ACTIVE_ITEM, this._element)
Johann-S's avatar
Johann-S committed
417
    const activeElementIndex = this._getItemIndex(activeElement)
418
419
    const nextElement = element || (activeElement &&
      this._getItemByDirection(direction, activeElement))
Johann-S's avatar
Johann-S committed
420

Johann-S's avatar
Johann-S committed
421
422
423
424
425
426
427
    const nextElementIndex = this._getItemIndex(nextElement)
    const isCycling = Boolean(this._interval)

    let directionalClassName
    let orderClassName
    let eventDirectionName

XhmikosR's avatar
XhmikosR committed
428
429
430
431
    if (direction === DIRECTION_NEXT) {
      directionalClassName = CLASS_NAME_LEFT
      orderClassName = CLASS_NAME_NEXT
      eventDirectionName = DIRECTION_LEFT
Johann-S's avatar
Johann-S committed
432
    } else {
XhmikosR's avatar
XhmikosR committed
433
434
435
      directionalClassName = CLASS_NAME_RIGHT
      orderClassName = CLASS_NAME_PREV
      eventDirectionName = DIRECTION_RIGHT
Johann-S's avatar
Johann-S committed
436
    }
Mark Otto's avatar
Mark Otto committed
437

XhmikosR's avatar
XhmikosR committed
438
    if (nextElement && nextElement.classList.contains(CLASS_NAME_ACTIVE)) {
Johann-S's avatar
Johann-S committed
439
440
441
      this._isSliding = false
      return
    }
fat's avatar
fat committed
442

Johann-S's avatar
Johann-S committed
443
    const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName)
Johann-S's avatar
Johann-S committed
444
    if (slideEvent.defaultPrevented) {
Johann-S's avatar
Johann-S committed
445
446
      return
    }
fat's avatar
fat committed
447

Johann-S's avatar
Johann-S committed
448
449
450
451
    if (!activeElement || !nextElement) {
      // Some weirdness is happening, so we bail
      return
    }
fat's avatar
fat committed
452

Johann-S's avatar
Johann-S committed
453
    this._isSliding = true
fat's avatar
fat committed
454

Johann-S's avatar
Johann-S committed
455
456
457
    if (isCycling) {
      this.pause()
    }
fat's avatar
fat committed
458

Johann-S's avatar
Johann-S committed
459
    this._setActiveIndicatorElement(nextElement)
fat's avatar
fat committed
460

XhmikosR's avatar
XhmikosR committed
461
    if (this._element.classList.contains(CLASS_NAME_SLIDE)) {
Johann-S's avatar
Johann-S committed
462
      nextElement.classList.add(orderClassName)
fat's avatar
fat committed
463

464
      reflow(nextElement)
fat's avatar
fat committed
465

Johann-S's avatar
Johann-S committed
466
467
      activeElement.classList.add(directionalClassName)
      nextElement.classList.add(directionalClassName)
468

Johann-S's avatar
Johann-S committed
469
470
471
472
473
474
475
476
      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
      }

477
      const transitionDuration = getTransitionDurationFromElement(activeElement)
478

Johann-S's avatar
Johann-S committed
479
      EventHandler
480
        .one(activeElement, TRANSITION_END, () => {
Johann-S's avatar
Johann-S committed
481
482
          nextElement.classList.remove(directionalClassName)
          nextElement.classList.remove(orderClassName)
XhmikosR's avatar
XhmikosR committed
483
          nextElement.classList.add(CLASS_NAME_ACTIVE)
fat's avatar
fat committed
484

XhmikosR's avatar
XhmikosR committed
485
          activeElement.classList.remove(CLASS_NAME_ACTIVE)
Johann-S's avatar
Johann-S committed
486
487
          activeElement.classList.remove(orderClassName)
          activeElement.classList.remove(directionalClassName)
fat's avatar
fat committed
488

Johann-S's avatar
Johann-S committed
489
          this._isSliding = false
fat's avatar
fat committed
490

Johann-S's avatar
Johann-S committed
491
          setTimeout(() => {
XhmikosR's avatar
XhmikosR committed
492
            EventHandler.trigger(this._element, EVENT_SLID, {
Johann-S's avatar
Johann-S committed
493
494
495
496
497
498
              relatedTarget: nextElement,
              direction: eventDirectionName,
              from: activeElementIndex,
              to: nextElementIndex
            })
          }, 0)
Johann-S's avatar
Johann-S committed
499
        })
Johann-S's avatar
Johann-S committed
500

501
      emulateTransitionEnd(activeElement, transitionDuration)
Johann-S's avatar
Johann-S committed
502
    } else {
XhmikosR's avatar
XhmikosR committed
503
504
      activeElement.classList.remove(CLASS_NAME_ACTIVE)
      nextElement.classList.add(CLASS_NAME_ACTIVE)
fat's avatar
fat committed
505

Johann-S's avatar
Johann-S committed
506
      this._isSliding = false
XhmikosR's avatar
XhmikosR committed
507
      EventHandler.trigger(this._element, EVENT_SLID, {
Johann-S's avatar
Johann-S committed
508
509
510
511
512
        relatedTarget: nextElement,
        direction: eventDirectionName,
        from: activeElementIndex,
        to: nextElementIndex
      })
Johann-S's avatar
Johann-S committed
513
    }
fat's avatar
fat committed
514

Johann-S's avatar
Johann-S committed
515
516
    if (isCycling) {
      this.cycle()
fat's avatar
fat committed
517
    }
Johann-S's avatar
Johann-S committed
518
  }
fat's avatar
fat committed
519

Johann-S's avatar
Johann-S committed
520
  // Static
fat's avatar
fat committed
521

522
  static carouselInterface(element, config) {
XhmikosR's avatar
XhmikosR committed
523
    let data = Data.getData(element, DATA_KEY)
524
525
526
527
    let _config = {
      ...Default,
      ...Manipulator.getDataAttributes(element)
    }
fat's avatar
fat committed
528

529
530
531
532
    if (typeof config === 'object') {
      _config = {
        ..._config,
        ...config
Johann-S's avatar
Johann-S committed
533
      }
534
    }
fat's avatar
fat committed
535

536
    const action = typeof config === 'string' ? config : _config.slide
fat's avatar
fat committed
537

538
539
540
    if (!data) {
      data = new Carousel(element, _config)
    }
fat's avatar
fat committed
541

542
543
544
545
    if (typeof config === 'number') {
      data.to(config)
    } else if (typeof action === 'string') {
      if (typeof data[action] === 'undefined') {
XhmikosR's avatar
XhmikosR committed
546
        throw new TypeError(`No method named "${action}"`)
fat's avatar
fat committed
547
      }
XhmikosR's avatar
XhmikosR committed
548

549
550
551
552
553
554
555
      data[action]()
    } else if (_config.interval && _config.ride) {
      data.pause()
      data.cycle()
    }
  }

556
  static jQueryInterface(config) {
557
    return this.each(function () {
558
      Carousel.carouselInterface(this, config)
Johann-S's avatar
Johann-S committed
559
560
    })
  }
fat's avatar
fat committed
561

562
  static dataApiClickHandler(event) {
563
    const target = getElementFromSelector(this)
Jacob Thornton's avatar
Jacob Thornton committed
564

XhmikosR's avatar
XhmikosR committed
565
    if (!target || !target.classList.contains(CLASS_NAME_CAROUSEL)) {
Johann-S's avatar
Johann-S committed
566
567
      return
    }
fat's avatar
fat committed
568

Johann-S's avatar
Johann-S committed
569
    const config = {
570
571
      ...Manipulator.getDataAttributes(target),
      ...Manipulator.getDataAttributes(this)
Johann-S's avatar
Johann-S committed
572
573
    }
    const slideIndex = this.getAttribute('data-slide-to')
fat's avatar
fat committed
574

Johann-S's avatar
Johann-S committed
575
576
577
578
    if (slideIndex) {
      config.interval = false
    }

579
    Carousel.carouselInterface(target, config)
fat's avatar
fat committed
580

Johann-S's avatar
Johann-S committed
581
    if (slideIndex) {
Johann-S's avatar
Johann-S committed
582
      Data.getData(target, DATA_KEY).to(slideIndex)
fat's avatar
fat committed
583
    }
Johann-S's avatar
Johann-S committed
584
585

    event.preventDefault()
fat's avatar
fat committed
586
  }
587

588
  static getInstance(element) {
589
590
    return Data.getData(element, DATA_KEY)
  }
Johann-S's avatar
Johann-S committed
591
}
fat's avatar
fat committed
592

Johann-S's avatar
Johann-S committed
593
594
595
596
597
/**
 * ------------------------------------------------------------------------
 * Data Api implementation
 * ------------------------------------------------------------------------
 */
fat's avatar
fat committed
598

Johann-S's avatar
Johann-S committed
599
EventHandler
XhmikosR's avatar
XhmikosR committed
600
  .on(document, EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, Carousel.dataApiClickHandler)
fat's avatar
fat committed
601

XhmikosR's avatar
XhmikosR committed
602
EventHandler.on(window, EVENT_LOAD_DATA_API, () => {
603
604
  const carousels = SelectorEngine.find(SELECTOR_DATA_RIDE)

Johann-S's avatar
Johann-S committed
605
  for (let i = 0, len = carousels.length; i < len; i++) {
606
    Carousel.carouselInterface(carousels[i], Data.getData(carousels[i], DATA_KEY))
fat's avatar
fat committed
607
  }
Johann-S's avatar
Johann-S committed
608
609
})

610
611
const $ = getjQuery()

Johann-S's avatar
Johann-S committed
612
613
614
615
/**
 * ------------------------------------------------------------------------
 * jQuery
 * ------------------------------------------------------------------------
Johann-S's avatar
Johann-S committed
616
 * add .carousel to jQuery only if jQuery is present
Johann-S's avatar
Johann-S committed
617
 */
Johann-S's avatar
Johann-S committed
618
/* istanbul ignore if */
619
if ($) {
Johann-S's avatar
Johann-S committed
620
  const JQUERY_NO_CONFLICT = $.fn[NAME]
621
  $.fn[NAME] = Carousel.jQueryInterface
XhmikosR's avatar
XhmikosR committed
622
623
  $.fn[NAME].Constructor = Carousel
  $.fn[NAME].noConflict = () => {
Johann-S's avatar
Johann-S committed
624
    $.fn[NAME] = JQUERY_NO_CONFLICT
625
    return Carousel.jQueryInterface
Johann-S's avatar
Johann-S committed
626
  }
Johann-S's avatar
Johann-S committed
627
}
fat's avatar
fat committed
628
629

export default Carousel