Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes15kdownloads
carousel.js601 linesDownload Raw Back to src
1/**2 * --------------------------------------------------------------------------3 * Bootstrap (v4.6.2): carousel.js4 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)5 * --------------------------------------------------------------------------6 */7 8import $ from 'jquery'9import Util from './util'10 11/**12 * Constants13 */14 15const NAME = 'carousel'16const VERSION = '4.6.2'17const DATA_KEY = 'bs.carousel'18const EVENT_KEY = `.${DATA_KEY}`19const DATA_API_KEY = '.data-api'20const JQUERY_NO_CONFLICT = $.fn[NAME]21const ARROW_LEFT_KEYCODE = 37 // KeyboardEvent.which value for left arrow key22const ARROW_RIGHT_KEYCODE = 39 // KeyboardEvent.which value for right arrow key23const TOUCHEVENT_COMPAT_WAIT = 500 // Time for mouse compat events to fire after touch24const SWIPE_THRESHOLD = 4025 26const CLASS_NAME_CAROUSEL = 'carousel'27const CLASS_NAME_ACTIVE = 'active'28const CLASS_NAME_SLIDE = 'slide'29const CLASS_NAME_RIGHT = 'carousel-item-right'30const CLASS_NAME_LEFT = 'carousel-item-left'31const CLASS_NAME_NEXT = 'carousel-item-next'32const CLASS_NAME_PREV = 'carousel-item-prev'33const CLASS_NAME_POINTER_EVENT = 'pointer-event'34 35const DIRECTION_NEXT = 'next'36const DIRECTION_PREV = 'prev'37const DIRECTION_LEFT = 'left'38const DIRECTION_RIGHT = 'right'39 40const EVENT_SLIDE = `slide${EVENT_KEY}`41const EVENT_SLID = `slid${EVENT_KEY}`42const EVENT_KEYDOWN = `keydown${EVENT_KEY}`43const EVENT_MOUSEENTER = `mouseenter${EVENT_KEY}`44const EVENT_MOUSELEAVE = `mouseleave${EVENT_KEY}`45const EVENT_TOUCHSTART = `touchstart${EVENT_KEY}`46const EVENT_TOUCHMOVE = `touchmove${EVENT_KEY}`47const EVENT_TOUCHEND = `touchend${EVENT_KEY}`48const EVENT_POINTERDOWN = `pointerdown${EVENT_KEY}`49const EVENT_POINTERUP = `pointerup${EVENT_KEY}`50const EVENT_DRAG_START = `dragstart${EVENT_KEY}`51const EVENT_LOAD_DATA_API = `load${EVENT_KEY}${DATA_API_KEY}`52const EVENT_CLICK_DATA_API = `click${EVENT_KEY}${DATA_API_KEY}`53 54const SELECTOR_ACTIVE = '.active'55const SELECTOR_ACTIVE_ITEM = '.active.carousel-item'56const SELECTOR_ITEM = '.carousel-item'57const SELECTOR_ITEM_IMG = '.carousel-item img'58const SELECTOR_NEXT_PREV = '.carousel-item-next, .carousel-item-prev'59const SELECTOR_INDICATORS = '.carousel-indicators'60const SELECTOR_DATA_SLIDE = '[data-slide], [data-slide-to]'61const SELECTOR_DATA_RIDE = '[data-ride="carousel"]'62 63const Default = {64  interval: 5000,65  keyboard: true,66  slide: false,67  pause: 'hover',68  wrap: true,69  touch: true70}71 72const DefaultType = {73  interval: '(number|boolean)',74  keyboard: 'boolean',75  slide: '(boolean|string)',76  pause: '(string|boolean)',77  wrap: 'boolean',78  touch: 'boolean'79}80 81const PointerType = {82  TOUCH: 'touch',83  PEN: 'pen'84}85 86/**87 * Class definition88 */89 90class Carousel {91  constructor(element, config) {92    this._items = null93    this._interval = null94    this._activeElement = null95    this._isPaused = false96    this._isSliding = false97    this.touchTimeout = null98    this.touchStartX = 099    this.touchDeltaX = 0100 101    this._config = this._getConfig(config)102    this._element = element103    this._indicatorsElement = this._element.querySelector(SELECTOR_INDICATORS)104    this._touchSupported = 'ontouchstart' in document.documentElement || navigator.maxTouchPoints > 0105    this._pointerEvent = Boolean(window.PointerEvent || window.MSPointerEvent)106 107    this._addEventListeners()108  }109 110  // Getters111  static get VERSION() {112    return VERSION113  }114 115  static get Default() {116    return Default117  }118 119  // Public120  next() {121    if (!this._isSliding) {122      this._slide(DIRECTION_NEXT)123    }124  }125 126  nextWhenVisible() {127    const $element = $(this._element)128    // Don't call next when the page isn't visible129    // or the carousel or its parent isn't visible130    if (!document.hidden &&131      ($element.is(':visible') && $element.css('visibility') !== 'hidden')) {132      this.next()133    }134  }135 136  prev() {137    if (!this._isSliding) {138      this._slide(DIRECTION_PREV)139    }140  }141 142  pause(event) {143    if (!event) {144      this._isPaused = true145    }146 147    if (this._element.querySelector(SELECTOR_NEXT_PREV)) {148      Util.triggerTransitionEnd(this._element)149      this.cycle(true)150    }151 152    clearInterval(this._interval)153    this._interval = null154  }155 156  cycle(event) {157    if (!event) {158      this._isPaused = false159    }160 161    if (this._interval) {162      clearInterval(this._interval)163      this._interval = null164    }165 166    if (this._config.interval && !this._isPaused) {167      this._updateInterval()168 169      this._interval = setInterval(170        (document.visibilityState ? this.nextWhenVisible : this.next).bind(this),171        this._config.interval172      )173    }174  }175 176  to(index) {177    this._activeElement = this._element.querySelector(SELECTOR_ACTIVE_ITEM)178 179    const activeIndex = this._getItemIndex(this._activeElement)180 181    if (index > this._items.length - 1 || index < 0) {182      return183    }184 185    if (this._isSliding) {186      $(this._element).one(EVENT_SLID, () => this.to(index))187      return188    }189 190    if (activeIndex === index) {191      this.pause()192      this.cycle()193      return194    }195 196    const direction = index > activeIndex ?197      DIRECTION_NEXT :198      DIRECTION_PREV199 200    this._slide(direction, this._items[index])201  }202 203  dispose() {204    $(this._element).off(EVENT_KEY)205    $.removeData(this._element, DATA_KEY)206 207    this._items = null208    this._config = null209    this._element = null210    this._interval = null211    this._isPaused = null212    this._isSliding = null213    this._activeElement = null214    this._indicatorsElement = null215  }216 217  // Private218  _getConfig(config) {219    config = {220      ...Default,221      ...config222    }223    Util.typeCheckConfig(NAME, config, DefaultType)224    return config225  }226 227  _handleSwipe() {228    const absDeltax = Math.abs(this.touchDeltaX)229 230    if (absDeltax <= SWIPE_THRESHOLD) {231      return232    }233 234    const direction = absDeltax / this.touchDeltaX235 236    this.touchDeltaX = 0237 238    // swipe left239    if (direction > 0) {240      this.prev()241    }242 243    // swipe right244    if (direction < 0) {245      this.next()246    }247  }248 249  _addEventListeners() {250    if (this._config.keyboard) {251      $(this._element).on(EVENT_KEYDOWN, event => this._keydown(event))252    }253 254    if (this._config.pause === 'hover') {255      $(this._element)256        .on(EVENT_MOUSEENTER, event => this.pause(event))257        .on(EVENT_MOUSELEAVE, event => this.cycle(event))258    }259 260    if (this._config.touch) {261      this._addTouchEventListeners()262    }263  }264 265  _addTouchEventListeners() {266    if (!this._touchSupported) {267      return268    }269 270    const start = event => {271      if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) {272        this.touchStartX = event.originalEvent.clientX273      } else if (!this._pointerEvent) {274        this.touchStartX = event.originalEvent.touches[0].clientX275      }276    }277 278    const move = event => {279      // ensure swiping with one touch and not pinching280      this.touchDeltaX = event.originalEvent.touches && event.originalEvent.touches.length > 1 ?281        0 :282        event.originalEvent.touches[0].clientX - this.touchStartX283    }284 285    const end = event => {286      if (this._pointerEvent && PointerType[event.originalEvent.pointerType.toUpperCase()]) {287        this.touchDeltaX = event.originalEvent.clientX - this.touchStartX288      }289 290      this._handleSwipe()291      if (this._config.pause === 'hover') {292        // If it's a touch-enabled device, mouseenter/leave are fired as293        // part of the mouse compatibility events on first tap - the carousel294        // would stop cycling until user tapped out of it;295        // here, we listen for touchend, explicitly pause the carousel296        // (as if it's the second time we tap on it, mouseenter compat event297        // is NOT fired) and after a timeout (to allow for mouse compatibility298        // events to fire) we explicitly restart cycling299 300        this.pause()301        if (this.touchTimeout) {302          clearTimeout(this.touchTimeout)303        }304 305        this.touchTimeout = setTimeout(event => this.cycle(event), TOUCHEVENT_COMPAT_WAIT + this._config.interval)306      }307    }308 309    $(this._element.querySelectorAll(SELECTOR_ITEM_IMG))310      .on(EVENT_DRAG_START, e => e.preventDefault())311 312    if (this._pointerEvent) {313      $(this._element).on(EVENT_POINTERDOWN, event => start(event))314      $(this._element).on(EVENT_POINTERUP, event => end(event))315 316      this._element.classList.add(CLASS_NAME_POINTER_EVENT)317    } else {318      $(this._element).on(EVENT_TOUCHSTART, event => start(event))319      $(this._element).on(EVENT_TOUCHMOVE, event => move(event))320      $(this._element).on(EVENT_TOUCHEND, event => end(event))321    }322  }323 324  _keydown(event) {325    if (/input|textarea/i.test(event.target.tagName)) {326      return327    }328 329    switch (event.which) {330      case ARROW_LEFT_KEYCODE:331        event.preventDefault()332        this.prev()333        break334      case ARROW_RIGHT_KEYCODE:335        event.preventDefault()336        this.next()337        break338      default:339    }340  }341 342  _getItemIndex(element) {343    this._items = element && element.parentNode ?344      [].slice.call(element.parentNode.querySelectorAll(SELECTOR_ITEM)) :345      []346    return this._items.indexOf(element)347  }348 349  _getItemByDirection(direction, activeElement) {350    const isNextDirection = direction === DIRECTION_NEXT351    const isPrevDirection = direction === DIRECTION_PREV352    const activeIndex = this._getItemIndex(activeElement)353    const lastItemIndex = this._items.length - 1354    const isGoingToWrap = isPrevDirection && activeIndex === 0 ||355                            isNextDirection && activeIndex === lastItemIndex356 357    if (isGoingToWrap && !this._config.wrap) {358      return activeElement359    }360 361    const delta = direction === DIRECTION_PREV ? -1 : 1362    const itemIndex = (activeIndex + delta) % this._items.length363 364    return itemIndex === -1 ?365      this._items[this._items.length - 1] : this._items[itemIndex]366  }367 368  _triggerSlideEvent(relatedTarget, eventDirectionName) {369    const targetIndex = this._getItemIndex(relatedTarget)370    const fromIndex = this._getItemIndex(this._element.querySelector(SELECTOR_ACTIVE_ITEM))371    const slideEvent = $.Event(EVENT_SLIDE, {372      relatedTarget,373      direction: eventDirectionName,374      from: fromIndex,375      to: targetIndex376    })377 378    $(this._element).trigger(slideEvent)379 380    return slideEvent381  }382 383  _setActiveIndicatorElement(element) {384    if (this._indicatorsElement) {385      const indicators = [].slice.call(this._indicatorsElement.querySelectorAll(SELECTOR_ACTIVE))386      $(indicators).removeClass(CLASS_NAME_ACTIVE)387 388      const nextIndicator = this._indicatorsElement.children[389        this._getItemIndex(element)390      ]391 392      if (nextIndicator) {393        $(nextIndicator).addClass(CLASS_NAME_ACTIVE)394      }395    }396  }397 398  _updateInterval() {399    const element = this._activeElement || this._element.querySelector(SELECTOR_ACTIVE_ITEM)400 401    if (!element) {402      return403    }404 405    const elementInterval = parseInt(element.getAttribute('data-interval'), 10)406 407    if (elementInterval) {408      this._config.defaultInterval = this._config.defaultInterval || this._config.interval409      this._config.interval = elementInterval410    } else {411      this._config.interval = this._config.defaultInterval || this._config.interval412    }413  }414 415  _slide(direction, element) {416    const activeElement = this._element.querySelector(SELECTOR_ACTIVE_ITEM)417    const activeElementIndex = this._getItemIndex(activeElement)418    const nextElement = element || activeElement &&419      this._getItemByDirection(direction, activeElement)420    const nextElementIndex = this._getItemIndex(nextElement)421    const isCycling = Boolean(this._interval)422 423    let directionalClassName424    let orderClassName425    let eventDirectionName426 427    if (direction === DIRECTION_NEXT) {428      directionalClassName = CLASS_NAME_LEFT429      orderClassName = CLASS_NAME_NEXT430      eventDirectionName = DIRECTION_LEFT431    } else {432      directionalClassName = CLASS_NAME_RIGHT433      orderClassName = CLASS_NAME_PREV434      eventDirectionName = DIRECTION_RIGHT435    }436 437    if (nextElement && $(nextElement).hasClass(CLASS_NAME_ACTIVE)) {438      this._isSliding = false439      return440    }441 442    const slideEvent = this._triggerSlideEvent(nextElement, eventDirectionName)443    if (slideEvent.isDefaultPrevented()) {444      return445    }446 447    if (!activeElement || !nextElement) {448      // Some weirdness is happening, so we bail449      return450    }451 452    this._isSliding = true453 454    if (isCycling) {455      this.pause()456    }457 458    this._setActiveIndicatorElement(nextElement)459    this._activeElement = nextElement460 461    const slidEvent = $.Event(EVENT_SLID, {462      relatedTarget: nextElement,463      direction: eventDirectionName,464      from: activeElementIndex,465      to: nextElementIndex466    })467 468    if ($(this._element).hasClass(CLASS_NAME_SLIDE)) {469      $(nextElement).addClass(orderClassName)470 471      Util.reflow(nextElement)472 473      $(activeElement).addClass(directionalClassName)474      $(nextElement).addClass(directionalClassName)475 476      const transitionDuration = Util.getTransitionDurationFromElement(activeElement)477 478      $(activeElement)479        .one(Util.TRANSITION_END, () => {480          $(nextElement)481            .removeClass(`${directionalClassName} ${orderClassName}`)482            .addClass(CLASS_NAME_ACTIVE)483 484          $(activeElement).removeClass(`${CLASS_NAME_ACTIVE} ${orderClassName} ${directionalClassName}`)485 486          this._isSliding = false487 488          setTimeout(() => $(this._element).trigger(slidEvent), 0)489        })490        .emulateTransitionEnd(transitionDuration)491    } else {492      $(activeElement).removeClass(CLASS_NAME_ACTIVE)493      $(nextElement).addClass(CLASS_NAME_ACTIVE)494 495      this._isSliding = false496      $(this._element).trigger(slidEvent)497    }498 499    if (isCycling) {500      this.cycle()501    }502  }503 504  // Static505  static _jQueryInterface(config) {506    return this.each(function () {507      let data = $(this).data(DATA_KEY)508      let _config = {509        ...Default,510        ...$(this).data()511      }512 513      if (typeof config === 'object') {514        _config = {515          ..._config,516          ...config517        }518      }519 520      const action = typeof config === 'string' ? config : _config.slide521 522      if (!data) {523        data = new Carousel(this, _config)524        $(this).data(DATA_KEY, data)525      }526 527      if (typeof config === 'number') {528        data.to(config)529      } else if (typeof action === 'string') {530        if (typeof data[action] === 'undefined') {531          throw new TypeError(`No method named "${action}"`)532        }533 534        data[action]()535      } else if (_config.interval && _config.ride) {536        data.pause()537        data.cycle()538      }539    })540  }541 542  static _dataApiClickHandler(event) {543    const selector = Util.getSelectorFromElement(this)544 545    if (!selector) {546      return547    }548 549    const target = $(selector)[0]550 551    if (!target || !$(target).hasClass(CLASS_NAME_CAROUSEL)) {552      return553    }554 555    const config = {556      ...$(target).data(),557      ...$(this).data()558    }559    const slideIndex = this.getAttribute('data-slide-to')560 561    if (slideIndex) {562      config.interval = false563    }564 565    Carousel._jQueryInterface.call($(target), config)566 567    if (slideIndex) {568      $(target).data(DATA_KEY).to(slideIndex)569    }570 571    event.preventDefault()572  }573}574 575/**576 * Data API implementation577 */578 579$(document).on(EVENT_CLICK_DATA_API, SELECTOR_DATA_SLIDE, Carousel._dataApiClickHandler)580 581$(window).on(EVENT_LOAD_DATA_API, () => {582  const carousels = [].slice.call(document.querySelectorAll(SELECTOR_DATA_RIDE))583  for (let i = 0, len = carousels.length; i < len; i++) {584    const $carousel = $(carousels[i])585    Carousel._jQueryInterface.call($carousel, $carousel.data())586  }587})588 589/**590 * jQuery591 */592 593$.fn[NAME] = Carousel._jQueryInterface594$.fn[NAME].Constructor = Carousel595$.fn[NAME].noConflict = () => {596  $.fn[NAME] = JQUERY_NO_CONFLICT597  return Carousel._jQueryInterface598}599 600export default Carousel601 
codekingpro/portable-devtools · Team Ai