276091acc6d558fc1c4c521569a269600f9d98ac.svn-base 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470
  1. import Util from './util'
  2. /**
  3. * --------------------------------------------------------------------------
  4. * Bootstrap (v4.0.0): carousel.js
  5. * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
  6. * --------------------------------------------------------------------------
  7. */
  8. const Carousel = (($) => {
  9. /**
  10. * ------------------------------------------------------------------------
  11. * Constants
  12. * ------------------------------------------------------------------------
  13. */
  14. const NAME = 'carousel'
  15. const VERSION = '4.0.0'
  16. const DATA_KEY = 'bs.carousel'
  17. const EVENT_KEY = `.${DATA_KEY}`
  18. const DATA_API_KEY = '.data-api'
  19. const JQUERY_NO_CONFLICT = $.fn[NAME]
  20. const TRANSITION_DURATION = 600
  21. const Default = {
  22. interval : 5000,
  23. keyboard : true,
  24. slide : false,
  25. pause : 'hover',
  26. wrap : true
  27. }
  28. const DefaultType = {
  29. interval : '(number|boolean)',
  30. keyboard : 'boolean',
  31. slide : '(boolean|string)',
  32. pause : '(string|boolean)',
  33. wrap : 'boolean'
  34. }
  35. const Direction = {
  36. NEXT : 'next',
  37. PREVIOUS : 'prev'
  38. }
  39. const Event = {
  40. SLIDE : `slide${EVENT_KEY}`,
  41. SLID : `slid${EVENT_KEY}`,
  42. KEYDOWN : `keydown${EVENT_KEY}`,
  43. MOUSEENTER : `mouseenter${EVENT_KEY}`,
  44. MOUSELEAVE : `mouseleave${EVENT_KEY}`,
  45. LOAD_DATA_API : `load${EVENT_KEY}${DATA_API_KEY}`,
  46. CLICK_DATA_API : `click${EVENT_KEY}${DATA_API_KEY}`
  47. }
  48. const ClassName = {
  49. CAROUSEL : 'carousel',
  50. ACTIVE : 'active',
  51. SLIDE : 'slide',
  52. RIGHT : 'right',
  53. LEFT : 'left',
  54. ITEM : 'carousel-item'
  55. }
  56. const Selector = {
  57. ACTIVE : '.active',
  58. ACTIVE_ITEM : '.active.carousel-item',
  59. ITEM : '.carousel-item',
  60. NEXT_PREV : '.next, .prev',
  61. INDICATORS : '.carousel-indicators',
  62. DATA_SLIDE : '[data-slide], [data-slide-to]',
  63. DATA_RIDE : '[data-ride="carousel"]'
  64. }
  65. /**
  66. * ------------------------------------------------------------------------
  67. * Class Definition
  68. * ------------------------------------------------------------------------
  69. */
  70. class Carousel {
  71. constructor(element, config) {
  72. this._items = null
  73. this._interval = null
  74. this._activeElement = null
  75. this._isPaused = false
  76. this._isSliding = false
  77. this._config = this._getConfig(config)
  78. this._element = $(element)[0]
  79. this._indicatorsElement = $(this._element).find(Selector.INDICATORS)[0]
  80. this._addEventListeners()
  81. }
  82. // getters
  83. static get VERSION() {
  84. return VERSION
  85. }
  86. static get Default() {
  87. return Default
  88. }
  89. // public
  90. next() {
  91. if (!this._isSliding) {
  92. this._slide(Direction.NEXT)
  93. }
  94. }
  95. prev() {
  96. if (!this._isSliding) {
  97. this._slide(Direction.PREVIOUS)
  98. }
  99. }
  100. pause(event) {
  101. if (!event) {
  102. this._isPaused = true
  103. }
  104. if ($(this._element).find(Selector.NEXT_PREV)[0] &&
  105. Util.supportsTransitionEnd()) {
  106. Util.triggerTransitionEnd(this._element)
  107. this.cycle(true)
  108. }
  109. clearInterval(this._interval)
  110. this._interval = null
  111. }
  112. cycle(event) {
  113. if (!event) {
  114. this._isPaused = false
  115. }
  116. if (this._interval) {
  117. clearInterval(this._interval)
  118. this._interval = null
  119. }
  120. if (this._config.interval && !this._isPaused) {
  121. this._interval = setInterval(
  122. $.proxy(this.next, this), this._config.interval
  123. )
  124. }
  125. }
  126. to(index) {
  127. this._activeElement = $(this._element).find(Selector.ACTIVE_ITEM)[0]
  128. let activeIndex = this._getItemIndex(this._activeElement)
  129. if (index > (this._items.length - 1) || index < 0) {
  130. return
  131. }
  132. if (this._isSliding) {
  133. $(this._element).one(Event.SLID, () => this.to(index))
  134. return
  135. }
  136. if (activeIndex === index) {
  137. this.pause()
  138. this.cycle()
  139. return
  140. }
  141. let direction = index > activeIndex ?
  142. Direction.NEXT :
  143. Direction.PREVIOUS
  144. this._slide(direction, this._items[index])
  145. }
  146. dispose() {
  147. $(this._element).off(EVENT_KEY)
  148. $.removeData(this._element, DATA_KEY)
  149. this._items = null
  150. this._config = null
  151. this._element = null
  152. this._interval = null
  153. this._isPaused = null
  154. this._isSliding = null
  155. this._activeElement = null
  156. this._indicatorsElement = null
  157. }
  158. // private
  159. _getConfig(config) {
  160. config = $.extend({}, Default, config)
  161. Util.typeCheckConfig(NAME, config, DefaultType)
  162. return config
  163. }
  164. _addEventListeners() {
  165. if (this._config.keyboard) {
  166. $(this._element)
  167. .on(Event.KEYDOWN, $.proxy(this._keydown, this))
  168. }
  169. if (this._config.pause === 'hover' &&
  170. !('ontouchstart' in document.documentElement)) {
  171. $(this._element)
  172. .on(Event.MOUSEENTER, $.proxy(this.pause, this))
  173. .on(Event.MOUSELEAVE, $.proxy(this.cycle, this))
  174. }
  175. }
  176. _keydown(event) {
  177. event.preventDefault()
  178. if (/input|textarea/i.test(event.target.tagName)) {
  179. return
  180. }
  181. switch (event.which) {
  182. case 37: this.prev(); break
  183. case 39: this.next(); break
  184. default: return
  185. }
  186. }
  187. _getItemIndex(element) {
  188. this._items = $.makeArray($(element).parent().find(Selector.ITEM))
  189. return this._items.indexOf(element)
  190. }
  191. _getItemByDirection(direction, activeElement) {
  192. let isNextDirection = direction === Direction.NEXT
  193. let isPrevDirection = direction === Direction.PREVIOUS
  194. let activeIndex = this._getItemIndex(activeElement)
  195. let lastItemIndex = (this._items.length - 1)
  196. let isGoingToWrap = (isPrevDirection && activeIndex === 0) ||
  197. (isNextDirection && activeIndex === lastItemIndex)
  198. if (isGoingToWrap && !this._config.wrap) {
  199. return activeElement
  200. }
  201. let delta = direction === Direction.PREVIOUS ? -1 : 1
  202. let itemIndex = (activeIndex + delta) % this._items.length
  203. return itemIndex === -1 ?
  204. this._items[this._items.length - 1] : this._items[itemIndex]
  205. }
  206. _triggerSlideEvent(relatedTarget, directionalClassname) {
  207. let slideEvent = $.Event(Event.SLIDE, {
  208. relatedTarget,
  209. direction: directionalClassname
  210. })
  211. $(this._element).trigger(slideEvent)
  212. return slideEvent
  213. }
  214. _setActiveIndicatorElement(element) {
  215. if (this._indicatorsElement) {
  216. $(this._indicatorsElement)
  217. .find(Selector.ACTIVE)
  218. .removeClass(ClassName.ACTIVE)
  219. let nextIndicator = this._indicatorsElement.children[
  220. this._getItemIndex(element)
  221. ]
  222. if (nextIndicator) {
  223. $(nextIndicator).addClass(ClassName.ACTIVE)
  224. }
  225. }
  226. }
  227. _slide(direction, element) {
  228. let activeElement = $(this._element).find(Selector.ACTIVE_ITEM)[0]
  229. let nextElement = element || activeElement &&
  230. this._getItemByDirection(direction, activeElement)
  231. let isCycling = Boolean(this._interval)
  232. let directionalClassName = direction === Direction.NEXT ?
  233. ClassName.LEFT :
  234. ClassName.RIGHT
  235. if (nextElement && $(nextElement).hasClass(ClassName.ACTIVE)) {
  236. this._isSliding = false
  237. return
  238. }
  239. let slideEvent = this._triggerSlideEvent(nextElement, directionalClassName)
  240. if (slideEvent.isDefaultPrevented()) {
  241. return
  242. }
  243. if (!activeElement || !nextElement) {
  244. // some weirdness is happening, so we bail
  245. return
  246. }
  247. this._isSliding = true
  248. if (isCycling) {
  249. this.pause()
  250. }
  251. this._setActiveIndicatorElement(nextElement)
  252. let slidEvent = $.Event(Event.SLID, {
  253. relatedTarget: nextElement,
  254. direction: directionalClassName
  255. })
  256. if (Util.supportsTransitionEnd() &&
  257. $(this._element).hasClass(ClassName.SLIDE)) {
  258. $(nextElement).addClass(direction)
  259. Util.reflow(nextElement)
  260. $(activeElement).addClass(directionalClassName)
  261. $(nextElement).addClass(directionalClassName)
  262. $(activeElement)
  263. .one(Util.TRANSITION_END, () => {
  264. $(nextElement)
  265. .removeClass(directionalClassName)
  266. .removeClass(direction)
  267. $(nextElement).addClass(ClassName.ACTIVE)
  268. $(activeElement)
  269. .removeClass(ClassName.ACTIVE)
  270. .removeClass(direction)
  271. .removeClass(directionalClassName)
  272. this._isSliding = false
  273. setTimeout(() => $(this._element).trigger(slidEvent), 0)
  274. })
  275. .emulateTransitionEnd(TRANSITION_DURATION)
  276. } else {
  277. $(activeElement).removeClass(ClassName.ACTIVE)
  278. $(nextElement).addClass(ClassName.ACTIVE)
  279. this._isSliding = false
  280. $(this._element).trigger(slidEvent)
  281. }
  282. if (isCycling) {
  283. this.cycle()
  284. }
  285. }
  286. // static
  287. static _jQueryInterface(config) {
  288. return this.each(function () {
  289. let data = $(this).data(DATA_KEY)
  290. let _config = $.extend({}, Default, $(this).data())
  291. if (typeof config === 'object') {
  292. $.extend(_config, config)
  293. }
  294. let action = typeof config === 'string' ? config : _config.slide
  295. if (!data) {
  296. data = new Carousel(this, _config)
  297. $(this).data(DATA_KEY, data)
  298. }
  299. if (typeof config === 'number') {
  300. data.to(config)
  301. } else if (action) {
  302. data[action]()
  303. } else if (_config.interval) {
  304. data.pause()
  305. data.cycle()
  306. }
  307. })
  308. }
  309. static _dataApiClickHandler(event) {
  310. let selector = Util.getSelectorFromElement(this)
  311. if (!selector) {
  312. return
  313. }
  314. let target = $(selector)[0]
  315. if (!target || !$(target).hasClass(ClassName.CAROUSEL)) {
  316. return
  317. }
  318. let config = $.extend({}, $(target).data(), $(this).data())
  319. let slideIndex = this.getAttribute('data-slide-to')
  320. if (slideIndex) {
  321. config.interval = false
  322. }
  323. Carousel._jQueryInterface.call($(target), config)
  324. if (slideIndex) {
  325. $(target).data(DATA_KEY).to(slideIndex)
  326. }
  327. event.preventDefault()
  328. }
  329. }
  330. /**
  331. * ------------------------------------------------------------------------
  332. * Data Api implementation
  333. * ------------------------------------------------------------------------
  334. */
  335. $(document)
  336. .on(Event.CLICK_DATA_API, Selector.DATA_SLIDE, Carousel._dataApiClickHandler)
  337. $(window).on(Event.LOAD_DATA_API, () => {
  338. $(Selector.DATA_RIDE).each(function () {
  339. let $carousel = $(this)
  340. Carousel._jQueryInterface.call($carousel, $carousel.data())
  341. })
  342. })
  343. /**
  344. * ------------------------------------------------------------------------
  345. * jQuery
  346. * ------------------------------------------------------------------------
  347. */
  348. $.fn[NAME] = Carousel._jQueryInterface
  349. $.fn[NAME].Constructor = Carousel
  350. $.fn[NAME].noConflict = function () {
  351. $.fn[NAME] = JQUERY_NO_CONFLICT
  352. return Carousel._jQueryInterface
  353. }
  354. return Carousel
  355. })(jQuery)
  356. export default Carousel