]>
git.ipfire.org Git - ipfire.org.git/blob - src/scss/bootstrap-4.0.0-alpha.6/js/src/carousel.js
1 import Util
from './util'
5 * --------------------------------------------------------------------------
6 * Bootstrap (v4.0.0-alpha.6): carousel.js
7 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
8 * --------------------------------------------------------------------------
11 const Carousel
= (($) => {
15 * ------------------------------------------------------------------------
17 * ------------------------------------------------------------------------
20 const NAME
= 'carousel'
21 const VERSION
= '4.0.0-alpha.6'
22 const DATA_KEY
= 'bs.carousel'
23 const EVENT_KEY
= `.${DATA_KEY}`
24 const DATA_API_KEY
= '.data-api'
25 const JQUERY_NO_CONFLICT
= $.fn
[NAME
]
26 const TRANSITION_DURATION
= 600
27 const ARROW_LEFT_KEYCODE
= 37 // KeyboardEvent.which value for left arrow key
28 const ARROW_RIGHT_KEYCODE
= 39 // KeyboardEvent.which value for right arrow key
39 interval
: '(number|boolean)',
41 slide
: '(boolean|string)',
42 pause
: '(string|boolean)',
54 SLIDE
: `slide${EVENT_KEY}`,
55 SLID
: `slid${EVENT_KEY}`,
56 KEYDOWN
: `keydown${EVENT_KEY}`,
57 MOUSEENTER
: `mouseenter${EVENT_KEY}`,
58 MOUSELEAVE
: `mouseleave${EVENT_KEY}`,
59 LOAD_DATA_API
: `load${EVENT_KEY}${DATA_API_KEY}`,
60 CLICK_DATA_API
: `click${EVENT_KEY}${DATA_API_KEY}`
64 CAROUSEL
: 'carousel',
67 RIGHT
: 'carousel-item-right',
68 LEFT
: 'carousel-item-left',
69 NEXT
: 'carousel-item-next',
70 PREV
: 'carousel-item-prev',
71 ITEM
: 'carousel-item'
76 ACTIVE_ITEM
: '.active.carousel-item',
77 ITEM
: '.carousel-item',
78 NEXT_PREV
: '.carousel-item-next, .carousel-item-prev',
79 INDICATORS
: '.carousel-indicators',
80 DATA_SLIDE
: '[data-slide], [data-slide-to]',
81 DATA_RIDE
: '[data-ride="carousel"]'
86 * ------------------------------------------------------------------------
88 * ------------------------------------------------------------------------
93 constructor(element
, config
) {
96 this._activeElement
= null
98 this._isPaused
= false
99 this._isSliding
= false
101 this._config
= this._getConfig(config
)
102 this._element
= $(element
)[0]
103 this._indicatorsElement
= $(this._element
).find(Selector
.INDICATORS
)[0]
105 this._addEventListeners()
111 static get VERSION() {
115 static get Default() {
123 if (this._isSliding
) {
124 throw new Error('Carousel is sliding')
126 this._slide(Direction
.NEXT
)
130 // Don't call next when the page isn't visible
131 if (!document
.hidden
) {
137 if (this._isSliding
) {
138 throw new Error('Carousel is sliding')
140 this._slide(Direction
.PREVIOUS
)
145 this._isPaused
= true
148 if ($(this._element
).find(Selector
.NEXT_PREV
)[0] &&
149 Util
.supportsTransitionEnd()) {
150 Util
.triggerTransitionEnd(this._element
)
154 clearInterval(this._interval
)
155 this._interval
= null
160 this._isPaused
= false
163 if (this._interval
) {
164 clearInterval(this._interval
)
165 this._interval
= null
168 if (this._config
.interval
&& !this._isPaused
) {
169 this._interval
= setInterval(
170 (document
.visibilityState
? this.nextWhenVisible
: this.next
).bind(this),
171 this._config
.interval
177 this._activeElement
= $(this._element
).find(Selector
.ACTIVE_ITEM
)[0]
179 const activeIndex
= this._getItemIndex(this._activeElement
)
181 if (index
> this._items
.length
- 1 || index
< 0) {
185 if (this._isSliding
) {
186 $(this._element
).one(Event
.SLID
, () => this.to(index
))
190 if (activeIndex
=== index
) {
196 const direction
= index
> activeIndex
?
200 this._slide(direction
, this._items
[index
])
204 $(this._element
).off(EVENT_KEY
)
205 $.removeData(this._element
, DATA_KEY
)
210 this._interval
= null
211 this._isPaused
= null
212 this._isSliding
= null
213 this._activeElement
= null
214 this._indicatorsElement
= null
221 config
= $.extend({}, Default
, config
)
222 Util
.typeCheckConfig(NAME
, config
, DefaultType
)
226 _addEventListeners() {
227 if (this._config
.keyboard
) {
229 .on(Event
.KEYDOWN
, (event
) => this._keydown(event
))
232 if (this._config
.pause
=== 'hover' &&
233 !('ontouchstart' in document
.documentElement
)) {
235 .on(Event
.MOUSEENTER
, (event
) => this.pause(event
))
236 .on(Event
.MOUSELEAVE
, (event
) => this.cycle(event
))
241 if (/input|textarea/i.test(event
.target
.tagName
)) {
245 switch (event
.which
) {
246 case ARROW_LEFT_KEYCODE
:
247 event
.preventDefault()
250 case ARROW_RIGHT_KEYCODE
:
251 event
.preventDefault()
259 _getItemIndex(element
) {
260 this._items
= $.makeArray($(element
).parent().find(Selector
.ITEM
))
261 return this._items
.indexOf(element
)
264 _getItemByDirection(direction
, activeElement
) {
265 const isNextDirection
= direction
=== Direction
.NEXT
266 const isPrevDirection
= direction
=== Direction
.PREVIOUS
267 const activeIndex
= this._getItemIndex(activeElement
)
268 const lastItemIndex
= this._items
.length
- 1
269 const isGoingToWrap
= isPrevDirection
&& activeIndex
=== 0 ||
270 isNextDirection
&& activeIndex
=== lastItemIndex
272 if (isGoingToWrap
&& !this._config
.wrap
) {
276 const delta
= direction
=== Direction
.PREVIOUS
? -1 : 1
277 const itemIndex
= (activeIndex
+ delta
) % this._items
.length
279 return itemIndex
=== -1 ?
280 this._items
[this._items
.length
- 1] : this._items
[itemIndex
]
284 _triggerSlideEvent(relatedTarget
, eventDirectionName
) {
285 const slideEvent
= $.Event(Event
.SLIDE
, {
287 direction
: eventDirectionName
290 $(this._element
).trigger(slideEvent
)
295 _setActiveIndicatorElement(element
) {
296 if (this._indicatorsElement
) {
297 $(this._indicatorsElement
)
298 .find(Selector
.ACTIVE
)
299 .removeClass(ClassName
.ACTIVE
)
301 const nextIndicator
= this._indicatorsElement
.children
[
302 this._getItemIndex(element
)
306 $(nextIndicator
).addClass(ClassName
.ACTIVE
)
311 _slide(direction
, element
) {
312 const activeElement
= $(this._element
).find(Selector
.ACTIVE_ITEM
)[0]
313 const nextElement
= element
|| activeElement
&&
314 this._getItemByDirection(direction
, activeElement
)
316 const isCycling
= Boolean(this._interval
)
318 let directionalClassName
320 let eventDirectionName
322 if (direction
=== Direction
.NEXT
) {
323 directionalClassName
= ClassName
.LEFT
324 orderClassName
= ClassName
.NEXT
325 eventDirectionName
= Direction
.LEFT
327 directionalClassName
= ClassName
.RIGHT
328 orderClassName
= ClassName
.PREV
329 eventDirectionName
= Direction
.RIGHT
332 if (nextElement
&& $(nextElement
).hasClass(ClassName
.ACTIVE
)) {
333 this._isSliding
= false
337 const slideEvent
= this._triggerSlideEvent(nextElement
, eventDirectionName
)
338 if (slideEvent
.isDefaultPrevented()) {
342 if (!activeElement
|| !nextElement
) {
343 // some weirdness is happening, so we bail
347 this._isSliding
= true
353 this._setActiveIndicatorElement(nextElement
)
355 const slidEvent
= $.Event(Event
.SLID
, {
356 relatedTarget
: nextElement
,
357 direction
: eventDirectionName
360 if (Util
.supportsTransitionEnd() &&
361 $(this._element
).hasClass(ClassName
.SLIDE
)) {
363 $(nextElement
).addClass(orderClassName
)
365 Util
.reflow(nextElement
)
367 $(activeElement
).addClass(directionalClassName
)
368 $(nextElement
).addClass(directionalClassName
)
371 .one(Util
.TRANSITION_END
, () => {
373 .removeClass(`${directionalClassName} ${orderClassName}`)
374 .addClass(ClassName
.ACTIVE
)
376 $(activeElement
).removeClass(`${ClassName.ACTIVE} ${orderClassName} ${directionalClassName}`)
378 this._isSliding
= false
380 setTimeout(() => $(this._element
).trigger(slidEvent
), 0)
383 .emulateTransitionEnd(TRANSITION_DURATION
)
386 $(activeElement
).removeClass(ClassName
.ACTIVE
)
387 $(nextElement
).addClass(ClassName
.ACTIVE
)
389 this._isSliding
= false
390 $(this._element
).trigger(slidEvent
)
401 static _jQueryInterface(config
) {
402 return this.each(function () {
403 let data
= $(this).data(DATA_KEY
)
404 const _config
= $.extend({}, Default
, $(this).data())
406 if (typeof config
=== 'object') {
407 $.extend(_config
, config
)
410 const action
= typeof config
=== 'string' ? config
: _config
.slide
413 data
= new Carousel(this, _config
)
414 $(this).data(DATA_KEY
, data
)
417 if (typeof config
=== 'number') {
419 } else if (typeof action
=== 'string') {
420 if (data
[action
] === undefined) {
421 throw new Error(`No method named "${action}"`)
424 } else if (_config
.interval
) {
431 static _dataApiClickHandler(event
) {
432 const selector
= Util
.getSelectorFromElement(this)
438 const target
= $(selector
)[0]
440 if (!target
|| !$(target
).hasClass(ClassName
.CAROUSEL
)) {
444 const config
= $.extend({}, $(target
).data(), $(this).data())
445 const slideIndex
= this.getAttribute('data-slide-to')
448 config
.interval
= false
451 Carousel
._jQueryInterface
.call($(target
), config
)
454 $(target
).data(DATA_KEY
).to(slideIndex
)
457 event
.preventDefault()
464 * ------------------------------------------------------------------------
465 * Data Api implementation
466 * ------------------------------------------------------------------------
470 .on(Event
.CLICK_DATA_API
, Selector
.DATA_SLIDE
, Carousel
._dataApiClickHandler
)
472 $(window
).on(Event
.LOAD_DATA_API
, () => {
473 $(Selector
.DATA_RIDE
).each(function () {
474 const $carousel
= $(this)
475 Carousel
._jQueryInterface
.call($carousel
, $carousel
.data())
481 * ------------------------------------------------------------------------
483 * ------------------------------------------------------------------------
486 $.fn
[NAME
] = Carousel
._jQueryInterface
487 $.fn
[NAME
].Constructor
= Carousel
488 $.fn
[NAME
].noConflict = function () {
489 $.fn
[NAME
] = JQUERY_NO_CONFLICT
490 return Carousel
._jQueryInterface
497 export default Carousel