OSDN Git Service

62af5cdb16d14f8e2859c133cf4a79ddb98b9a2e
[bytom/vapor.git] / tools / side_chain_tool / web / node_modules / bootstrap / js / src / carousel.js
1 import $ from 'jquery'
2 import Util from './util'
3
4 /**
5  * --------------------------------------------------------------------------
6  * Bootstrap (v4.1.3): carousel.js
7  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
8  * --------------------------------------------------------------------------
9  */
10
11 const Carousel = (($) => {
12   /**
13    * ------------------------------------------------------------------------
14    * Constants
15    * ------------------------------------------------------------------------
16    */
17
18   const NAME                   = 'carousel'
19   const VERSION                = '4.1.3'
20   const DATA_KEY               = 'bs.carousel'
21   const EVENT_KEY              = `.${DATA_KEY}`
22   const DATA_API_KEY           = '.data-api'
23   const JQUERY_NO_CONFLICT     = $.fn[NAME]
24   const ARROW_LEFT_KEYCODE     = 37 // KeyboardEvent.which value for left arrow key
25   const ARROW_RIGHT_KEYCODE    = 39 // KeyboardEvent.which value for right arrow key
26   const TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch
27
28   const Default = {
29     interval : 5000,
30     keyboard : true,
31     slide    : false,
32     pause    : 'hover',
33     wrap     : true
34   }
35
36   const DefaultType = {
37     interval : '(number|boolean)',
38     keyboard : 'boolean',
39     slide    : '(boolean|string)',
40     pause    : '(string|boolean)',
41     wrap     : 'boolean'
42   }
43
44   const Direction = {
45     NEXT     : 'next',
46     PREV     : 'prev',
47     LEFT     : 'left',
48     RIGHT    : 'right'
49   }
50
51   const Event = {
52     SLIDE          : `slide${EVENT_KEY}`,
53     SLID           : `slid${EVENT_KEY}`,
54     KEYDOWN        : `keydown${EVENT_KEY}`,
55     MOUSEENTER     : `mouseenter${EVENT_KEY}`,
56     MOUSELEAVE     : `mouseleave${EVENT_KEY}`,
57     TOUCHEND       : `touchend${EVENT_KEY}`,
58     LOAD_DATA_API  : `load${EVENT_KEY}${DATA_API_KEY}`,
59     CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
60   }
61
62   const ClassName = {
63     CAROUSEL : 'carousel',
64     ACTIVE   : 'active',
65     SLIDE    : 'slide',
66     RIGHT    : 'carousel-item-right',
67     LEFT     : 'carousel-item-left',
68     NEXT     : 'carousel-item-next',
69     PREV     : 'carousel-item-prev',
70     ITEM     : 'carousel-item'
71   }
72
73   const Selector = {
74     ACTIVE      : '.active',
75     ACTIVE_ITEM : '.active.carousel-item',
76     ITEM        : '.carousel-item',
77     NEXT_PREV   : '.carousel-item-next, .carousel-item-prev',
78     INDICATORS  : '.carousel-indicators',
79     DATA_SLIDE  : '[data-slide], [data-slide-to]',
80     DATA_RIDE   : '[data-ride="carousel"]'
81   }
82
83   /**
84    * ------------------------------------------------------------------------
85    * Class Definition
86    * ------------------------------------------------------------------------
87    */
88
89   class Carousel {
90     constructor(element, config) {
91       this._items              = null
92       this._interval           = null
93       this._activeElement      = null
94
95       this._isPaused           = false
96       this._isSliding          = false
97
98       this.touchTimeout        = null
99
100       this._config             = this._getConfig(config)
101       this._element            = $(element)[0]
102       this._indicatorsElement  = this._element.querySelector(Selector.INDICATORS)
103
104       this._addEventListeners()
105     }
106
107     // Getters
108
109     static get VERSION() {
110       return VERSION
111     }
112
113     static get Default() {
114       return Default
115     }
116
117     // Public
118
119     next() {
120       if (!this._isSliding) {
121         this._slide(Direction.NEXT)
122       }
123     }
124
125     nextWhenVisible() {
126       // Don't call next when the page isn't visible
127       // or the carousel or its parent isn't visible
128       if (!document.hidden &&
129         ($(this._element).is(':visible') && $(this._element).css('visibility') !== 'hidden')) {
130         this.next()
131       }
132     }
133
134     prev() {
135       if (!this._isSliding) {
136         this._slide(Direction.PREV)
137       }
138     }
139
140     pause(event) {
141       if (!event) {
142         this._isPaused = true
143       }
144
145       if (this._element.querySelector(Selector.NEXT_PREV)) {
146         Util.triggerTransitionEnd(this._element)
147         this.cycle(true)
148       }
149
150       clearInterval(this._interval)
151       this._interval = null
152     }
153
154     cycle(event) {
155       if (!event) {
156         this._isPaused = false
157       }
158
159       if (this._interval) {
160         clearInterval(this._interval)
161         this._interval = null
162       }
163
164       if (this._config.interval && !this._isPaused) {
165         this._interval = setInterval(
166           (document.visibilityState ? this.nextWhenVisible : this.next).bind(this),
167           this._config.interval
168         )
169       }
170     }
171
172     to(index) {
173       this._activeElement = this._element.querySelector(Selector.ACTIVE_ITEM)
174
175       const activeIndex = this._getItemIndex(this._activeElement)
176
177       if (index > this._items.length - 1 || index < 0) {
178         return
179       }
180
181       if (this._isSliding) {
182         $(this._element).one(Event.SLID, () => this.to(index))
183         return
184       }
185
186       if (activeIndex === index) {
187         this.pause()
188         this.cycle()
189         return
190       }
191
192       const direction = index > activeIndex
193         ? Direction.NEXT
194         : Direction.PREV
195
196       this._slide(direction, this._items[index])
197     }
198
199     dispose() {
200       $(this._element).off(EVENT_KEY)
201       $.removeData(this._element, DATA_KEY)
202
203       this._items             = null
204       this._config            = null
205       this._element           = null
206       this._interval          = null
207       this._isPaused          = null
208       this._isSliding         = null
209       this._activeElement     = null
210       this._indicatorsElement = null
211     }
212
213     // Private
214
215     _getConfig(config) {
216       config = {
217         ...Default,
218         ...config
219       }
220       Util.typeCheckConfig(NAME, config, DefaultType)
221       return config
222     }
223
224     _addEventListeners() {
225       if (this._config.keyboard) {
226         $(this._element)
227           .on(Event.KEYDOWN, (event) => this._keydown(event))
228       }
229
230       if (this._config.pause === 'hover') {
231         $(this._element)
232           .on(Event.MOUSEENTER, (event) => this.pause(event))
233           .on(Event.MOUSELEAVE, (event) => this.cycle(event))
234         if ('ontouchstart' in document.documentElement) {
235           // If it's a touch-enabled device, mouseenter/leave are fired as
236           // part of the mouse compatibility events on first tap - the carousel
237           // would stop cycling until user tapped out of it;
238           // here, we listen for touchend, explicitly pause the carousel
239           // (as if it's the second time we tap on it, mouseenter compat event
240           // is NOT fired) and after a timeout (to allow for mouse compatibility
241           // events to fire) we explicitly restart cycling
242           $(this._element).on(Event.TOUCHEND, () => {
243             this.pause()
244             if (this.touchTimeout) {
245               clearTimeout(this.touchTimeout)
246             }
247             this.touchTimeout = setTimeout((event) => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)
248           })
249         }
250       }
251     }
252
253     _keydown(event) {
254       if (/input|textarea/i.test(event.target.tagName)) {
255         return
256       }
257
258       switch (event.which) {
259         case ARROW_LEFT_KEYCODE:
260           event.preventDefault()
261           this.prev()
262           break
263         case ARROW_RIGHT_KEYCODE:
264           event.preventDefault()
265           this.next()
266           break
267         default:
268       }
269     }
270
271     _getItemIndex(element) {
272       this._items = element && element.parentNode
273         ? [].slice.call(element.parentNode.querySelectorAll(Selector.ITEM))
274         : []
275       return this._items.indexOf(element)
276     }
277
278     _getItemByDirection(direction, activeElement) {
279       const isNextDirection = direction === Direction.NEXT
280       const isPrevDirection = direction === Direction.PREV
281       const activeIndex     = this._getItemIndex(activeElement)
282       const lastItemIndex   = this._items.length - 1
283       const isGoingToWrap   = isPrevDirection && activeIndex === 0 ||
284                               isNextDirection && activeIndex === lastItemIndex
285
286       if (isGoingToWrap && !this._config.wrap) {
287         return activeElement
288       }
289
290       const delta     = direction === Direction.PREV ? -1 : 1
291       const itemIndex = (activeIndex + delta) % this._items.length
292
293       return itemIndex === -1
294         ? this._items[this._items.length - 1] : this._items[itemIndex]
295     }
296
297     _triggerSlideEvent(relatedTarget, eventDirectionName) {
298       const targetIndex = this._getItemIndex(relatedTarget)
299       const fromIndex = this._getItemIndex(this._element.querySelector(Selector.ACTIVE_ITEM))
300       const slideEvent = $.Event(Event.SLIDE, {
301         relatedTarget,
302         direction: eventDirectionName,
303         from: fromIndex,
304         to: targetIndex
305       })
306
307       $(this._element).trigger(slideEvent)
308
309       return slideEvent
310     }
311
312     _setActiveIndicatorElement(element) {
313       if (this._indicatorsElement) {
314         const indicators = [].slice.call(this._indicatorsElement.querySelectorAll(Selector.ACTIVE))
315         $(indicators)
316           .removeClass(ClassName.ACTIVE)
317
318         const nextIndicator = this._indicatorsElement.children[
319           this._getItemIndex(element)
320         ]
321
322         if (nextIndicator) {
323           $(nextIndicator).addClass(ClassName.ACTIVE)
324         }
325       }
326     }
327
328     _slide(direction, element) {
329       const activeElement = this._element.querySelector(Selector.ACTIVE_ITEM)
330       const activeElementIndex = this._getItemIndex(activeElement)
331       const nextElement   = element || activeElement &&
332         this._getItemByDirection(direction, activeElement)
333       const nextElementIndex = this._getItemIndex(nextElement)
334       const isCycling = Boolean(this._interval)
335
336       let directionalClassName
337       let orderClassName
338       let eventDirectionName
339
340       if (direction === Direction.NEXT) {
341         directionalClassName = ClassName.LEFT
342         orderClassName = ClassName.NEXT
343         eventDirectionName = Direction.LEFT
344       } else {
345         directionalClassName = ClassName.RIGHT
346         orderClassName = ClassName.PREV
347         eventDirectionName = Direction.RIGHT
348       }
349
350       if (nextElement && $(nextElement).hasClass(ClassName.ACTIVE)) {
351         this._isSliding = false
352         return
353       }
354
355       const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName)
356       if (slideEvent.isDefaultPrevented()) {
357         return
358       }
359
360       if (!activeElement || !nextElement) {
361         // Some weirdness is happening, so we bail
362         return
363       }
364
365       this._isSliding = true
366
367       if (isCycling) {
368         this.pause()
369       }
370
371       this._setActiveIndicatorElement(nextElement)
372
373       const slidEvent = $.Event(Event.SLID, {
374         relatedTarget: nextElement,
375         direction: eventDirectionName,
376         from: activeElementIndex,
377         to: nextElementIndex
378       })
379
380       if ($(this._element).hasClass(ClassName.SLIDE)) {
381         $(nextElement).addClass(orderClassName)
382
383         Util.reflow(nextElement)
384
385         $(activeElement).addClass(directionalClassName)
386         $(nextElement).addClass(directionalClassName)
387
388         const transitionDuration = Util.getTransitionDurationFromElement(activeElement)
389
390         $(activeElement)
391           .one(Util.TRANSITION_END, () => {
392             $(nextElement)
393               .removeClass(`${directionalClassName} ${orderClassName}`)
394               .addClass(ClassName.ACTIVE)
395
396             $(activeElement).removeClass(`${ClassName.ACTIVE} ${orderClassName} ${directionalClassName}`)
397
398             this._isSliding = false
399
400             setTimeout(() => $(this._element).trigger(slidEvent), 0)
401           })
402           .emulateTransitionEnd(transitionDuration)
403       } else {
404         $(activeElement).removeClass(ClassName.ACTIVE)
405         $(nextElement).addClass(ClassName.ACTIVE)
406
407         this._isSliding = false
408         $(this._element).trigger(slidEvent)
409       }
410
411       if (isCycling) {
412         this.cycle()
413       }
414     }
415
416     // Static
417
418     static _jQueryInterface(config) {
419       return this.each(function () {
420         let data = $(this).data(DATA_KEY)
421         let _config = {
422           ...Default,
423           ...$(this).data()
424         }
425
426         if (typeof config === 'object') {
427           _config = {
428             ..._config,
429             ...config
430           }
431         }
432
433         const action = typeof config === 'string' ? config : _config.slide
434
435         if (!data) {
436           data = new Carousel(this, _config)
437           $(this).data(DATA_KEY, data)
438         }
439
440         if (typeof config === 'number') {
441           data.to(config)
442         } else if (typeof action === 'string') {
443           if (typeof data[action] === 'undefined') {
444             throw new TypeError(`No method named "${action}"`)
445           }
446           data[action]()
447         } else if (_config.interval) {
448           data.pause()
449           data.cycle()
450         }
451       })
452     }
453
454     static _dataApiClickHandler(event) {
455       const selector = Util.getSelectorFromElement(this)
456
457       if (!selector) {
458         return
459       }
460
461       const target = $(selector)[0]
462
463       if (!target || !$(target).hasClass(ClassName.CAROUSEL)) {
464         return
465       }
466
467       const config = {
468         ...$(target).data(),
469         ...$(this).data()
470       }
471       const slideIndex = this.getAttribute('data-slide-to')
472
473       if (slideIndex) {
474         config.interval = false
475       }
476
477       Carousel._jQueryInterface.call($(target), config)
478
479       if (slideIndex) {
480         $(target).data(DATA_KEY).to(slideIndex)
481       }
482
483       event.preventDefault()
484     }
485   }
486
487   /**
488    * ------------------------------------------------------------------------
489    * Data Api implementation
490    * ------------------------------------------------------------------------
491    */
492
493   $(document)
494     .on(Event.CLICK_DATA_API, Selector.DATA_SLIDE, Carousel._dataApiClickHandler)
495
496   $(window).on(Event.LOAD_DATA_API, () => {
497     const carousels = [].slice.call(document.querySelectorAll(Selector.DATA_RIDE))
498     for (let i = 0, len = carousels.length; i < len; i++) {
499       const $carousel = $(carousels[i])
500       Carousel._jQueryInterface.call($carousel, $carousel.data())
501     }
502   })
503
504   /**
505    * ------------------------------------------------------------------------
506    * jQuery
507    * ------------------------------------------------------------------------
508    */
509
510   $.fn[NAME] = Carousel._jQueryInterface
511   $.fn[NAME].Constructor = Carousel
512   $.fn[NAME].noConflict = function () {
513     $.fn[NAME] = JQUERY_NO_CONFLICT
514     return Carousel._jQueryInterface
515   }
516
517   return Carousel
518 })($)
519
520 export default Carousel