blueimp-gallery.js 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417
  1. /*
  2. * blueimp Gallery JS
  3. * https://github.com/blueimp/Gallery
  4. *
  5. * Copyright 2013, Sebastian Tschan
  6. * https://blueimp.net
  7. *
  8. * Swipe implementation based on
  9. * https://github.com/bradbirdsall/Swipe
  10. *
  11. * Licensed under the MIT license:
  12. * https://opensource.org/licenses/MIT
  13. */
  14. /* global define, window, document, DocumentTouch */
  15. ;(function (factory) {
  16. 'use strict'
  17. if (typeof define === 'function' && define.amd) {
  18. // Register as an anonymous AMD module:
  19. define(['./blueimp-helper'], factory)
  20. } else {
  21. // Browser globals:
  22. window.blueimp = window.blueimp || {}
  23. window.blueimp.Gallery = factory(window.blueimp.helper || window.jQuery)
  24. }
  25. })(function ($) {
  26. 'use strict'
  27. function Gallery (list, options) {
  28. if (document.body.style.maxHeight === undefined) {
  29. // document.body.style.maxHeight is undefined on IE6 and lower
  30. return null
  31. }
  32. if (!this || this.options !== Gallery.prototype.options) {
  33. // Called as function instead of as constructor,
  34. // so we simply return a new instance:
  35. return new Gallery(list, options)
  36. }
  37. if (!list || !list.length) {
  38. this.console.log(
  39. 'blueimp Gallery: No or empty list provided as first argument.',
  40. list
  41. )
  42. return
  43. }
  44. this.list = list
  45. this.num = list.length
  46. this.initOptions(options)
  47. this.initialize()
  48. }
  49. $.extend(Gallery.prototype, {
  50. options: {
  51. // The Id, element or querySelector of the gallery widget:
  52. container: '#blueimp-gallery',
  53. // The tag name, Id, element or querySelector of the slides container:
  54. slidesContainer: 'div',
  55. // The tag name, Id, element or querySelector of the title element:
  56. titleElement: 'h3',
  57. // The class to add when the gallery is visible:
  58. displayClass: 'blueimp-gallery-display',
  59. // The class to add when the gallery controls are visible:
  60. controlsClass: 'blueimp-gallery-controls',
  61. // The class to add when the gallery only displays one element:
  62. singleClass: 'blueimp-gallery-single',
  63. // The class to add when the left edge has been reached:
  64. leftEdgeClass: 'blueimp-gallery-left',
  65. // The class to add when the right edge has been reached:
  66. rightEdgeClass: 'blueimp-gallery-right',
  67. // The class to add when the automatic slideshow is active:
  68. playingClass: 'blueimp-gallery-playing',
  69. // The class for all slides:
  70. slideClass: 'slide',
  71. // The slide class for loading elements:
  72. slideLoadingClass: 'slide-loading',
  73. // The slide class for elements that failed to load:
  74. slideErrorClass: 'slide-error',
  75. // The class for the content element loaded into each slide:
  76. slideContentClass: 'slide-content',
  77. // The class for the "toggle" control:
  78. toggleClass: 'toggle',
  79. // The class for the "prev" control:
  80. prevClass: 'prev',
  81. // The class for the "next" control:
  82. nextClass: 'next',
  83. // The class for the "close" control:
  84. closeClass: 'close',
  85. // The class for the "play-pause" toggle control:
  86. playPauseClass: 'play-pause',
  87. // The list object property (or data attribute) with the object type:
  88. typeProperty: 'type',
  89. // The list object property (or data attribute) with the object title:
  90. titleProperty: 'title',
  91. // The list object property (or data attribute) with the object alt text:
  92. altTextProperty: 'alt',
  93. // The list object property (or data attribute) with the object URL:
  94. urlProperty: 'href',
  95. // The list object property (or data attribute) with the object srcset URL(s):
  96. srcsetProperty: 'urlset',
  97. // The gallery listens for transitionend events before triggering the
  98. // opened and closed events, unless the following option is set to false:
  99. displayTransition: true,
  100. // Defines if the gallery slides are cleared from the gallery modal,
  101. // or reused for the next gallery initialization:
  102. clearSlides: true,
  103. // Defines if images should be stretched to fill the available space,
  104. // while maintaining their aspect ratio (will only be enabled for browsers
  105. // supporting background-size="contain", which excludes IE < 9).
  106. // Set to "cover", to make images cover all available space (requires
  107. // support for background-size="cover", which excludes IE < 9):
  108. stretchImages: false,
  109. // Toggle the controls on pressing the Return key:
  110. toggleControlsOnReturn: true,
  111. // Toggle the controls on slide click:
  112. toggleControlsOnSlideClick: true,
  113. // Toggle the automatic slideshow interval on pressing the Space key:
  114. toggleSlideshowOnSpace: true,
  115. // Navigate the gallery by pressing left and right on the keyboard:
  116. enableKeyboardNavigation: true,
  117. // Close the gallery on pressing the Esc key:
  118. closeOnEscape: true,
  119. // Close the gallery when clicking on an empty slide area:
  120. closeOnSlideClick: true,
  121. // Close the gallery by swiping up or down:
  122. closeOnSwipeUpOrDown: true,
  123. // Emulate touch events on mouse-pointer devices such as desktop browsers:
  124. emulateTouchEvents: true,
  125. // Stop touch events from bubbling up to ancestor elements of the Gallery:
  126. stopTouchEventsPropagation: false,
  127. // Hide the page scrollbars:
  128. hidePageScrollbars: true,
  129. // Stops any touches on the container from scrolling the page:
  130. disableScroll: true,
  131. // Carousel mode (shortcut for carousel specific options):
  132. carousel: false,
  133. // Allow continuous navigation, moving from last to first
  134. // and from first to last slide:
  135. continuous: true,
  136. // Remove elements outside of the preload range from the DOM:
  137. unloadElements: true,
  138. // Start with the automatic slideshow:
  139. startSlideshow: false,
  140. // Delay in milliseconds between slides for the automatic slideshow:
  141. slideshowInterval: 5000,
  142. // The starting index as integer.
  143. // Can also be an object of the given list,
  144. // or an equal object with the same url property:
  145. index: 0,
  146. // The number of elements to load around the current index:
  147. preloadRange: 2,
  148. // The transition speed between slide changes in milliseconds:
  149. transitionSpeed: 400,
  150. // The transition speed for automatic slide changes, set to an integer
  151. // greater 0 to override the default transition speed:
  152. slideshowTransitionSpeed: undefined,
  153. // The event object for which the default action will be canceled
  154. // on Gallery initialization (e.g. the click event to open the Gallery):
  155. event: undefined,
  156. // Callback function executed when the Gallery is initialized.
  157. // Is called with the gallery instance as "this" object:
  158. onopen: undefined,
  159. // Callback function executed when the Gallery has been initialized
  160. // and the initialization transition has been completed.
  161. // Is called with the gallery instance as "this" object:
  162. onopened: undefined,
  163. // Callback function executed on slide change.
  164. // Is called with the gallery instance as "this" object and the
  165. // current index and slide as arguments:
  166. onslide: undefined,
  167. // Callback function executed after the slide change transition.
  168. // Is called with the gallery instance as "this" object and the
  169. // current index and slide as arguments:
  170. onslideend: undefined,
  171. // Callback function executed on slide content load.
  172. // Is called with the gallery instance as "this" object and the
  173. // slide index and slide element as arguments:
  174. onslidecomplete: undefined,
  175. // Callback function executed when the Gallery is about to be closed.
  176. // Is called with the gallery instance as "this" object:
  177. onclose: undefined,
  178. // Callback function executed when the Gallery has been closed
  179. // and the closing transition has been completed.
  180. // Is called with the gallery instance as "this" object:
  181. onclosed: undefined
  182. },
  183. carouselOptions: {
  184. hidePageScrollbars: false,
  185. toggleControlsOnReturn: false,
  186. toggleSlideshowOnSpace: false,
  187. enableKeyboardNavigation: false,
  188. closeOnEscape: false,
  189. closeOnSlideClick: false,
  190. closeOnSwipeUpOrDown: false,
  191. disableScroll: false,
  192. startSlideshow: true
  193. },
  194. console:
  195. window.console && typeof window.console.log === 'function'
  196. ? window.console
  197. : { log: function () {} },
  198. // Detect touch, transition, transform and background-size support:
  199. support: (function (element) {
  200. var support = {
  201. touch:
  202. window.ontouchstart !== undefined ||
  203. (window.DocumentTouch && document instanceof DocumentTouch)
  204. }
  205. var transitions = {
  206. webkitTransition: {
  207. end: 'webkitTransitionEnd',
  208. prefix: '-webkit-'
  209. },
  210. MozTransition: {
  211. end: 'transitionend',
  212. prefix: '-moz-'
  213. },
  214. OTransition: {
  215. end: 'otransitionend',
  216. prefix: '-o-'
  217. },
  218. transition: {
  219. end: 'transitionend',
  220. prefix: ''
  221. }
  222. }
  223. var prop
  224. for (prop in transitions) {
  225. if (
  226. transitions.hasOwnProperty(prop) &&
  227. element.style[prop] !== undefined
  228. ) {
  229. support.transition = transitions[prop]
  230. support.transition.name = prop
  231. break
  232. }
  233. }
  234. function elementTests () {
  235. var transition = support.transition
  236. var prop
  237. var translateZ
  238. document.body.appendChild(element)
  239. if (transition) {
  240. prop = transition.name.slice(0, -9) + 'ransform'
  241. if (element.style[prop] !== undefined) {
  242. element.style[prop] = 'translateZ(0)'
  243. translateZ = window
  244. .getComputedStyle(element)
  245. .getPropertyValue(transition.prefix + 'transform')
  246. support.transform = {
  247. prefix: transition.prefix,
  248. name: prop,
  249. translate: true,
  250. translateZ: !!translateZ && translateZ !== 'none'
  251. }
  252. }
  253. }
  254. if (element.style.backgroundSize !== undefined) {
  255. support.backgroundSize = {}
  256. element.style.backgroundSize = 'contain'
  257. support.backgroundSize.contain =
  258. window
  259. .getComputedStyle(element)
  260. .getPropertyValue('background-size') === 'contain'
  261. element.style.backgroundSize = 'cover'
  262. support.backgroundSize.cover =
  263. window
  264. .getComputedStyle(element)
  265. .getPropertyValue('background-size') === 'cover'
  266. }
  267. document.body.removeChild(element)
  268. }
  269. if (document.body) {
  270. elementTests()
  271. } else {
  272. $(document).on('DOMContentLoaded', elementTests)
  273. }
  274. return support
  275. // Test element, has to be standard HTML and must not be hidden
  276. // for the CSS3 tests using window.getComputedStyle to be applicable:
  277. })(document.createElement('div')),
  278. requestAnimationFrame:
  279. window.requestAnimationFrame ||
  280. window.webkitRequestAnimationFrame ||
  281. window.mozRequestAnimationFrame,
  282. cancelAnimationFrame:
  283. window.cancelAnimationFrame ||
  284. window.webkitCancelRequestAnimationFrame ||
  285. window.webkitCancelAnimationFrame ||
  286. window.mozCancelAnimationFrame,
  287. initialize: function () {
  288. this.initStartIndex()
  289. if (this.initWidget() === false) {
  290. return false
  291. }
  292. this.initEventListeners()
  293. // Load the slide at the given index:
  294. this.onslide(this.index)
  295. // Manually trigger the slideend event for the initial slide:
  296. this.ontransitionend()
  297. // Start the automatic slideshow if applicable:
  298. if (this.options.startSlideshow) {
  299. this.play()
  300. }
  301. },
  302. slide: function (to, speed) {
  303. window.clearTimeout(this.timeout)
  304. var index = this.index
  305. var direction
  306. var naturalDirection
  307. var diff
  308. if (index === to || this.num === 1) {
  309. return
  310. }
  311. if (!speed) {
  312. speed = this.options.transitionSpeed
  313. }
  314. if (this.support.transform) {
  315. if (!this.options.continuous) {
  316. to = this.circle(to)
  317. }
  318. // 1: backward, -1: forward:
  319. direction = Math.abs(index - to) / (index - to)
  320. // Get the actual position of the slide:
  321. if (this.options.continuous) {
  322. naturalDirection = direction
  323. direction = -this.positions[this.circle(to)] / this.slideWidth
  324. // If going forward but to < index, use to = slides.length + to
  325. // If going backward but to > index, use to = -slides.length + to
  326. if (direction !== naturalDirection) {
  327. to = -direction * this.num + to
  328. }
  329. }
  330. diff = Math.abs(index - to) - 1
  331. // Move all the slides between index and to in the right direction:
  332. while (diff) {
  333. diff -= 1
  334. this.move(
  335. this.circle((to > index ? to : index) - diff - 1),
  336. this.slideWidth * direction,
  337. 0
  338. )
  339. }
  340. to = this.circle(to)
  341. this.move(index, this.slideWidth * direction, speed)
  342. this.move(to, 0, speed)
  343. if (this.options.continuous) {
  344. this.move(
  345. this.circle(to - direction),
  346. -(this.slideWidth * direction),
  347. 0
  348. )
  349. }
  350. } else {
  351. to = this.circle(to)
  352. this.animate(index * -this.slideWidth, to * -this.slideWidth, speed)
  353. }
  354. this.onslide(to)
  355. },
  356. getIndex: function () {
  357. return this.index
  358. },
  359. getNumber: function () {
  360. return this.num
  361. },
  362. prev: function () {
  363. if (this.options.continuous || this.index) {
  364. this.slide(this.index - 1)
  365. }
  366. },
  367. next: function () {
  368. if (this.options.continuous || this.index < this.num - 1) {
  369. this.slide(this.index + 1)
  370. }
  371. },
  372. play: function (time) {
  373. var that = this
  374. window.clearTimeout(this.timeout)
  375. this.interval = time || this.options.slideshowInterval
  376. if (this.elements[this.index] > 1) {
  377. this.timeout = this.setTimeout(
  378. (!this.requestAnimationFrame && this.slide) ||
  379. function (to, speed) {
  380. that.animationFrameId = that.requestAnimationFrame.call(
  381. window,
  382. function () {
  383. that.slide(to, speed)
  384. }
  385. )
  386. },
  387. [this.index + 1, this.options.slideshowTransitionSpeed],
  388. this.interval
  389. )
  390. }
  391. this.container.addClass(this.options.playingClass)
  392. },
  393. pause: function () {
  394. window.clearTimeout(this.timeout)
  395. this.interval = null
  396. if (this.cancelAnimationFrame) {
  397. this.cancelAnimationFrame.call(window, this.animationFrameId)
  398. this.animationFrameId = null
  399. }
  400. this.container.removeClass(this.options.playingClass)
  401. },
  402. add: function (list) {
  403. var i
  404. if (!list.concat) {
  405. // Make a real array out of the list to add:
  406. list = Array.prototype.slice.call(list)
  407. }
  408. if (!this.list.concat) {
  409. // Make a real array out of the Gallery list:
  410. this.list = Array.prototype.slice.call(this.list)
  411. }
  412. this.list = this.list.concat(list)
  413. this.num = this.list.length
  414. if (this.num > 2 && this.options.continuous === null) {
  415. this.options.continuous = true
  416. this.container.removeClass(this.options.leftEdgeClass)
  417. }
  418. this.container
  419. .removeClass(this.options.rightEdgeClass)
  420. .removeClass(this.options.singleClass)
  421. for (i = this.num - list.length; i < this.num; i += 1) {
  422. this.addSlide(i)
  423. this.positionSlide(i)
  424. }
  425. this.positions.length = this.num
  426. this.initSlides(true)
  427. },
  428. resetSlides: function () {
  429. this.slidesContainer.empty()
  430. this.unloadAllSlides()
  431. this.slides = []
  432. },
  433. handleClose: function () {
  434. var options = this.options
  435. this.destroyEventListeners()
  436. // Cancel the slideshow:
  437. this.pause()
  438. this.container[0].style.display = 'none'
  439. this.container
  440. .removeClass(options.displayClass)
  441. .removeClass(options.singleClass)
  442. .removeClass(options.leftEdgeClass)
  443. .removeClass(options.rightEdgeClass)
  444. if (options.hidePageScrollbars) {
  445. document.body.style.overflow = this.bodyOverflowStyle
  446. }
  447. if (this.options.clearSlides) {
  448. this.resetSlides()
  449. }
  450. if (this.options.onclosed) {
  451. this.options.onclosed.call(this)
  452. }
  453. },
  454. close: function () {
  455. var that = this
  456. function closeHandler (event) {
  457. if (event.target === that.container[0]) {
  458. that.container.off(that.support.transition.end, closeHandler)
  459. that.handleClose()
  460. }
  461. }
  462. if (this.options.onclose) {
  463. this.options.onclose.call(this)
  464. }
  465. if (this.support.transition && this.options.displayTransition) {
  466. this.container.on(this.support.transition.end, closeHandler)
  467. this.container.removeClass(this.options.displayClass)
  468. } else {
  469. this.handleClose()
  470. }
  471. },
  472. circle: function (index) {
  473. // Always return a number inside of the slides index range:
  474. return (this.num + index % this.num) % this.num
  475. },
  476. move: function (index, dist, speed) {
  477. this.translateX(index, dist, speed)
  478. this.positions[index] = dist
  479. },
  480. translate: function (index, x, y, speed) {
  481. var style = this.slides[index].style
  482. var transition = this.support.transition
  483. var transform = this.support.transform
  484. style[transition.name + 'Duration'] = speed + 'ms'
  485. style[transform.name] =
  486. 'translate(' +
  487. x +
  488. 'px, ' +
  489. y +
  490. 'px)' +
  491. (transform.translateZ ? ' translateZ(0)' : '')
  492. },
  493. translateX: function (index, x, speed) {
  494. this.translate(index, x, 0, speed)
  495. },
  496. translateY: function (index, y, speed) {
  497. this.translate(index, 0, y, speed)
  498. },
  499. animate: function (from, to, speed) {
  500. if (!speed) {
  501. this.slidesContainer[0].style.left = to + 'px'
  502. return
  503. }
  504. var that = this
  505. var start = new Date().getTime()
  506. var timer = window.setInterval(function () {
  507. var timeElap = new Date().getTime() - start
  508. if (timeElap > speed) {
  509. that.slidesContainer[0].style.left = to + 'px'
  510. that.ontransitionend()
  511. window.clearInterval(timer)
  512. return
  513. }
  514. that.slidesContainer[0].style.left =
  515. (to - from) * (Math.floor(timeElap / speed * 100) / 100) + from + 'px'
  516. }, 4)
  517. },
  518. preventDefault: function (event) {
  519. if (event.preventDefault) {
  520. event.preventDefault()
  521. } else {
  522. event.returnValue = false
  523. }
  524. },
  525. stopPropagation: function (event) {
  526. if (event.stopPropagation) {
  527. event.stopPropagation()
  528. } else {
  529. event.cancelBubble = true
  530. }
  531. },
  532. onresize: function () {
  533. this.initSlides(true)
  534. },
  535. onmousedown: function (event) {
  536. // Trigger on clicks of the left mouse button only
  537. // and exclude video & audio elements:
  538. if (
  539. event.which &&
  540. event.which === 1 &&
  541. event.target.nodeName !== 'VIDEO' &&
  542. event.target.nodeName !== 'AUDIO'
  543. ) {
  544. // Preventing the default mousedown action is required
  545. // to make touch emulation work with Firefox:
  546. event.preventDefault()
  547. ;(event.originalEvent || event).touches = [
  548. {
  549. pageX: event.pageX,
  550. pageY: event.pageY
  551. }
  552. ]
  553. this.ontouchstart(event)
  554. }
  555. },
  556. onmousemove: function (event) {
  557. if (this.touchStart) {
  558. ;(event.originalEvent || event).touches = [
  559. {
  560. pageX: event.pageX,
  561. pageY: event.pageY
  562. }
  563. ]
  564. this.ontouchmove(event)
  565. }
  566. },
  567. onmouseup: function (event) {
  568. if (this.touchStart) {
  569. this.ontouchend(event)
  570. delete this.touchStart
  571. }
  572. },
  573. onmouseout: function (event) {
  574. if (this.touchStart) {
  575. var target = event.target
  576. var related = event.relatedTarget
  577. if (!related || (related !== target && !$.contains(target, related))) {
  578. this.onmouseup(event)
  579. }
  580. }
  581. },
  582. ontouchstart: function (event) {
  583. if (this.options.stopTouchEventsPropagation) {
  584. this.stopPropagation(event)
  585. }
  586. // jQuery doesn't copy touch event properties by default,
  587. // so we have to access the originalEvent object:
  588. var touches = (event.originalEvent || event).touches[0]
  589. this.touchStart = {
  590. // Remember the initial touch coordinates:
  591. x: touches.pageX,
  592. y: touches.pageY,
  593. // Store the time to determine touch duration:
  594. time: Date.now()
  595. }
  596. // Helper variable to detect scroll movement:
  597. this.isScrolling = undefined
  598. // Reset delta values:
  599. this.touchDelta = {}
  600. },
  601. ontouchmove: function (event) {
  602. if (this.options.stopTouchEventsPropagation) {
  603. this.stopPropagation(event)
  604. }
  605. // jQuery doesn't copy touch event properties by default,
  606. // so we have to access the originalEvent object:
  607. var touches = (event.originalEvent || event).touches[0]
  608. var scale = (event.originalEvent || event).scale
  609. var index = this.index
  610. var touchDeltaX
  611. var indices
  612. // Ensure this is a one touch swipe and not, e.g. a pinch:
  613. if (touches.length > 1 || (scale && scale !== 1)) {
  614. return
  615. }
  616. if (this.options.disableScroll) {
  617. event.preventDefault()
  618. }
  619. // Measure change in x and y coordinates:
  620. this.touchDelta = {
  621. x: touches.pageX - this.touchStart.x,
  622. y: touches.pageY - this.touchStart.y
  623. }
  624. touchDeltaX = this.touchDelta.x
  625. // Detect if this is a vertical scroll movement (run only once per touch):
  626. if (this.isScrolling === undefined) {
  627. this.isScrolling =
  628. this.isScrolling ||
  629. Math.abs(touchDeltaX) < Math.abs(this.touchDelta.y)
  630. }
  631. if (!this.isScrolling) {
  632. // Always prevent horizontal scroll:
  633. event.preventDefault()
  634. // Stop the slideshow:
  635. window.clearTimeout(this.timeout)
  636. if (this.options.continuous) {
  637. indices = [this.circle(index + 1), index, this.circle(index - 1)]
  638. } else {
  639. // Increase resistance if first slide and sliding left
  640. // or last slide and sliding right:
  641. this.touchDelta.x = touchDeltaX =
  642. touchDeltaX /
  643. ((!index && touchDeltaX > 0) ||
  644. (index === this.num - 1 && touchDeltaX < 0)
  645. ? Math.abs(touchDeltaX) / this.slideWidth + 1
  646. : 1)
  647. indices = [index]
  648. if (index) {
  649. indices.push(index - 1)
  650. }
  651. if (index < this.num - 1) {
  652. indices.unshift(index + 1)
  653. }
  654. }
  655. while (indices.length) {
  656. index = indices.pop()
  657. this.translateX(index, touchDeltaX + this.positions[index], 0)
  658. }
  659. } else {
  660. this.translateY(index, this.touchDelta.y + this.positions[index], 0)
  661. }
  662. },
  663. ontouchend: function (event) {
  664. if (this.options.stopTouchEventsPropagation) {
  665. this.stopPropagation(event)
  666. }
  667. var index = this.index
  668. var speed = this.options.transitionSpeed
  669. var slideWidth = this.slideWidth
  670. var isShortDuration = Number(Date.now() - this.touchStart.time) < 250
  671. // Determine if slide attempt triggers next/prev slide:
  672. var isValidSlide =
  673. (isShortDuration && Math.abs(this.touchDelta.x) > 20) ||
  674. Math.abs(this.touchDelta.x) > slideWidth / 2
  675. // Determine if slide attempt is past start or end:
  676. var isPastBounds =
  677. (!index && this.touchDelta.x > 0) ||
  678. (index === this.num - 1 && this.touchDelta.x < 0)
  679. var isValidClose =
  680. !isValidSlide &&
  681. this.options.closeOnSwipeUpOrDown &&
  682. ((isShortDuration && Math.abs(this.touchDelta.y) > 20) ||
  683. Math.abs(this.touchDelta.y) > this.slideHeight / 2)
  684. var direction
  685. var indexForward
  686. var indexBackward
  687. var distanceForward
  688. var distanceBackward
  689. if (this.options.continuous) {
  690. isPastBounds = false
  691. }
  692. // Determine direction of swipe (true: right, false: left):
  693. direction = this.touchDelta.x < 0 ? -1 : 1
  694. if (!this.isScrolling) {
  695. if (isValidSlide && !isPastBounds) {
  696. indexForward = index + direction
  697. indexBackward = index - direction
  698. distanceForward = slideWidth * direction
  699. distanceBackward = -slideWidth * direction
  700. if (this.options.continuous) {
  701. this.move(this.circle(indexForward), distanceForward, 0)
  702. this.move(this.circle(index - 2 * direction), distanceBackward, 0)
  703. } else if (indexForward >= 0 && indexForward < this.num) {
  704. this.move(indexForward, distanceForward, 0)
  705. }
  706. this.move(index, this.positions[index] + distanceForward, speed)
  707. this.move(
  708. this.circle(indexBackward),
  709. this.positions[this.circle(indexBackward)] + distanceForward,
  710. speed
  711. )
  712. index = this.circle(indexBackward)
  713. this.onslide(index)
  714. } else {
  715. // Move back into position
  716. if (this.options.continuous) {
  717. this.move(this.circle(index - 1), -slideWidth, speed)
  718. this.move(index, 0, speed)
  719. this.move(this.circle(index + 1), slideWidth, speed)
  720. } else {
  721. if (index) {
  722. this.move(index - 1, -slideWidth, speed)
  723. }
  724. this.move(index, 0, speed)
  725. if (index < this.num - 1) {
  726. this.move(index + 1, slideWidth, speed)
  727. }
  728. }
  729. }
  730. } else {
  731. if (isValidClose) {
  732. this.close()
  733. } else {
  734. // Move back into position
  735. this.translateY(index, 0, speed)
  736. }
  737. }
  738. },
  739. ontouchcancel: function (event) {
  740. if (this.touchStart) {
  741. this.ontouchend(event)
  742. delete this.touchStart
  743. }
  744. },
  745. ontransitionend: function (event) {
  746. var slide = this.slides[this.index]
  747. if (!event || slide === event.target) {
  748. if (this.interval) {
  749. this.play()
  750. }
  751. this.setTimeout(this.options.onslideend, [this.index, slide])
  752. }
  753. },
  754. oncomplete: function (event) {
  755. var target = event.target || event.srcElement
  756. var parent = target && target.parentNode
  757. var index
  758. if (!target || !parent) {
  759. return
  760. }
  761. index = this.getNodeIndex(parent)
  762. $(parent).removeClass(this.options.slideLoadingClass)
  763. if (event.type === 'error') {
  764. $(parent).addClass(this.options.slideErrorClass)
  765. this.elements[index] = 3 // Fail
  766. } else {
  767. this.elements[index] = 2 // Done
  768. }
  769. // Fix for IE7's lack of support for percentage max-height:
  770. if (target.clientHeight > this.container[0].clientHeight) {
  771. target.style.maxHeight = this.container[0].clientHeight
  772. }
  773. if (this.interval && this.slides[this.index] === parent) {
  774. this.play()
  775. }
  776. this.setTimeout(this.options.onslidecomplete, [index, parent])
  777. },
  778. onload: function (event) {
  779. this.oncomplete(event)
  780. },
  781. onerror: function (event) {
  782. this.oncomplete(event)
  783. },
  784. onkeydown: function (event) {
  785. switch (event.which || event.keyCode) {
  786. case 13: // Return
  787. if (this.options.toggleControlsOnReturn) {
  788. this.preventDefault(event)
  789. this.toggleControls()
  790. }
  791. break
  792. case 27: // Esc
  793. if (this.options.closeOnEscape) {
  794. this.close()
  795. // prevent Esc from closing other things
  796. event.stopImmediatePropagation()
  797. }
  798. break
  799. case 32: // Space
  800. if (this.options.toggleSlideshowOnSpace) {
  801. this.preventDefault(event)
  802. this.toggleSlideshow()
  803. }
  804. break
  805. case 37: // Left
  806. if (this.options.enableKeyboardNavigation) {
  807. this.preventDefault(event)
  808. this.prev()
  809. }
  810. break
  811. case 39: // Right
  812. if (this.options.enableKeyboardNavigation) {
  813. this.preventDefault(event)
  814. this.next()
  815. }
  816. break
  817. }
  818. },
  819. handleClick: function (event) {
  820. var options = this.options
  821. var target = event.target || event.srcElement
  822. var parent = target.parentNode
  823. function isTarget (className) {
  824. return $(target).hasClass(className) || $(parent).hasClass(className)
  825. }
  826. if (isTarget(options.toggleClass)) {
  827. // Click on "toggle" control
  828. this.preventDefault(event)
  829. this.toggleControls()
  830. } else if (isTarget(options.prevClass)) {
  831. // Click on "prev" control
  832. this.preventDefault(event)
  833. this.prev()
  834. } else if (isTarget(options.nextClass)) {
  835. // Click on "next" control
  836. this.preventDefault(event)
  837. this.next()
  838. } else if (isTarget(options.closeClass)) {
  839. // Click on "close" control
  840. this.preventDefault(event)
  841. this.close()
  842. } else if (isTarget(options.playPauseClass)) {
  843. // Click on "play-pause" control
  844. this.preventDefault(event)
  845. this.toggleSlideshow()
  846. } else if (parent === this.slidesContainer[0]) {
  847. // Click on slide background
  848. if (options.closeOnSlideClick) {
  849. this.preventDefault(event)
  850. this.close()
  851. } else if (options.toggleControlsOnSlideClick) {
  852. this.preventDefault(event)
  853. this.toggleControls()
  854. }
  855. } else if (
  856. parent.parentNode &&
  857. parent.parentNode === this.slidesContainer[0]
  858. ) {
  859. // Click on displayed element
  860. if (options.toggleControlsOnSlideClick) {
  861. this.preventDefault(event)
  862. this.toggleControls()
  863. }
  864. }
  865. },
  866. onclick: function (event) {
  867. if (
  868. this.options.emulateTouchEvents &&
  869. this.touchDelta &&
  870. (Math.abs(this.touchDelta.x) > 20 || Math.abs(this.touchDelta.y) > 20)
  871. ) {
  872. delete this.touchDelta
  873. return
  874. }
  875. return this.handleClick(event)
  876. },
  877. updateEdgeClasses: function (index) {
  878. if (!index) {
  879. this.container.addClass(this.options.leftEdgeClass)
  880. } else {
  881. this.container.removeClass(this.options.leftEdgeClass)
  882. }
  883. if (index === this.num - 1) {
  884. this.container.addClass(this.options.rightEdgeClass)
  885. } else {
  886. this.container.removeClass(this.options.rightEdgeClass)
  887. }
  888. },
  889. handleSlide: function (index) {
  890. if (!this.options.continuous) {
  891. this.updateEdgeClasses(index)
  892. }
  893. this.loadElements(index)
  894. if (this.options.unloadElements) {
  895. this.unloadElements(index)
  896. }
  897. this.setTitle(index)
  898. },
  899. onslide: function (index) {
  900. this.index = index
  901. this.handleSlide(index)
  902. this.setTimeout(this.options.onslide, [index, this.slides[index]])
  903. },
  904. setTitle: function (index) {
  905. var firstChild = this.slides[index].firstChild
  906. var text = firstChild.title || firstChild.alt
  907. var titleElement = this.titleElement
  908. if (titleElement.length) {
  909. this.titleElement.empty()
  910. if (text) {
  911. titleElement[0].appendChild(document.createTextNode(text))
  912. }
  913. }
  914. },
  915. setTimeout: function (func, args, wait) {
  916. var that = this
  917. return (
  918. func &&
  919. window.setTimeout(function () {
  920. func.apply(that, args || [])
  921. }, wait || 0)
  922. )
  923. },
  924. imageFactory: function (obj, callback) {
  925. var that = this
  926. var img = this.imagePrototype.cloneNode(false)
  927. var url = obj
  928. var backgroundSize = this.options.stretchImages
  929. var called
  930. var element
  931. var title
  932. var altText
  933. function callbackWrapper (event) {
  934. if (!called) {
  935. event = {
  936. type: event.type,
  937. target: element
  938. }
  939. if (!element.parentNode) {
  940. // Fix for IE7 firing the load event for
  941. // cached images before the element could
  942. // be added to the DOM:
  943. return that.setTimeout(callbackWrapper, [event])
  944. }
  945. called = true
  946. $(img).off('load error', callbackWrapper)
  947. if (backgroundSize) {
  948. if (event.type === 'load') {
  949. element.style.background = 'url("' + url + '") center no-repeat'
  950. element.style.backgroundSize = backgroundSize
  951. }
  952. }
  953. callback(event)
  954. }
  955. }
  956. if (typeof url !== 'string') {
  957. url = this.getItemProperty(obj, this.options.urlProperty)
  958. title = this.getItemProperty(obj, this.options.titleProperty)
  959. altText =
  960. this.getItemProperty(obj, this.options.altTextProperty) || title
  961. }
  962. if (backgroundSize === true) {
  963. backgroundSize = 'contain'
  964. }
  965. backgroundSize =
  966. this.support.backgroundSize &&
  967. this.support.backgroundSize[backgroundSize] &&
  968. backgroundSize
  969. if (backgroundSize) {
  970. element = this.elementPrototype.cloneNode(false)
  971. } else {
  972. element = img
  973. img.draggable = false
  974. }
  975. if (title) {
  976. element.title = title
  977. }
  978. if (altText) {
  979. element.alt = altText
  980. }
  981. $(img).on('load error', callbackWrapper)
  982. img.src = url
  983. return element
  984. },
  985. createElement: function (obj, callback) {
  986. var type = obj && this.getItemProperty(obj, this.options.typeProperty)
  987. var factory =
  988. (type && this[type.split('/')[0] + 'Factory']) || this.imageFactory
  989. var element = obj && factory.call(this, obj, callback)
  990. var srcset = this.getItemProperty(obj, this.options.srcsetProperty)
  991. if (!element) {
  992. element = this.elementPrototype.cloneNode(false)
  993. this.setTimeout(callback, [
  994. {
  995. type: 'error',
  996. target: element
  997. }
  998. ])
  999. }
  1000. if (srcset) {
  1001. element.setAttribute('srcset', srcset)
  1002. }
  1003. $(element).addClass(this.options.slideContentClass)
  1004. return element
  1005. },
  1006. loadElement: function (index) {
  1007. if (!this.elements[index]) {
  1008. if (this.slides[index].firstChild) {
  1009. this.elements[index] = $(this.slides[index]).hasClass(
  1010. this.options.slideErrorClass
  1011. )
  1012. ? 3
  1013. : 2
  1014. } else {
  1015. this.elements[index] = 1 // Loading
  1016. $(this.slides[index]).addClass(this.options.slideLoadingClass)
  1017. this.slides[index].appendChild(
  1018. this.createElement(this.list[index], this.proxyListener)
  1019. )
  1020. }
  1021. }
  1022. },
  1023. loadElements: function (index) {
  1024. var limit = Math.min(this.num, this.options.preloadRange * 2 + 1)
  1025. var j = index
  1026. var i
  1027. for (i = 0; i < limit; i += 1) {
  1028. // First load the current slide element (0),
  1029. // then the next one (+1),
  1030. // then the previous one (-2),
  1031. // then the next after next (+2), etc.:
  1032. j += i * (i % 2 === 0 ? -1 : 1)
  1033. // Connect the ends of the list to load slide elements for
  1034. // continuous navigation:
  1035. j = this.circle(j)
  1036. this.loadElement(j)
  1037. }
  1038. },
  1039. unloadElements: function (index) {
  1040. var i, diff
  1041. for (i in this.elements) {
  1042. if (this.elements.hasOwnProperty(i)) {
  1043. diff = Math.abs(index - i)
  1044. if (
  1045. diff > this.options.preloadRange &&
  1046. diff + this.options.preloadRange < this.num
  1047. ) {
  1048. this.unloadSlide(i)
  1049. delete this.elements[i]
  1050. }
  1051. }
  1052. }
  1053. },
  1054. addSlide: function (index) {
  1055. var slide = this.slidePrototype.cloneNode(false)
  1056. slide.setAttribute('data-index', index)
  1057. this.slidesContainer[0].appendChild(slide)
  1058. this.slides.push(slide)
  1059. },
  1060. positionSlide: function (index) {
  1061. var slide = this.slides[index]
  1062. slide.style.width = this.slideWidth + 'px'
  1063. if (this.support.transform) {
  1064. slide.style.left = index * -this.slideWidth + 'px'
  1065. this.move(
  1066. index,
  1067. this.index > index
  1068. ? -this.slideWidth
  1069. : this.index < index ? this.slideWidth : 0,
  1070. 0
  1071. )
  1072. }
  1073. },
  1074. initSlides: function (reload) {
  1075. var clearSlides, i
  1076. if (!reload) {
  1077. this.positions = []
  1078. this.positions.length = this.num
  1079. this.elements = {}
  1080. this.imagePrototype = document.createElement('img')
  1081. this.elementPrototype = document.createElement('div')
  1082. this.slidePrototype = document.createElement('div')
  1083. $(this.slidePrototype).addClass(this.options.slideClass)
  1084. this.slides = this.slidesContainer[0].children
  1085. clearSlides =
  1086. this.options.clearSlides || this.slides.length !== this.num
  1087. }
  1088. this.slideWidth = this.container[0].clientWidth
  1089. this.slideHeight = this.container[0].clientHeight
  1090. this.slidesContainer[0].style.width = this.num * this.slideWidth + 'px'
  1091. if (clearSlides) {
  1092. this.resetSlides()
  1093. }
  1094. for (i = 0; i < this.num; i += 1) {
  1095. if (clearSlides) {
  1096. this.addSlide(i)
  1097. }
  1098. this.positionSlide(i)
  1099. }
  1100. // Reposition the slides before and after the given index:
  1101. if (this.options.continuous && this.support.transform) {
  1102. this.move(this.circle(this.index - 1), -this.slideWidth, 0)
  1103. this.move(this.circle(this.index + 1), this.slideWidth, 0)
  1104. }
  1105. if (!this.support.transform) {
  1106. this.slidesContainer[0].style.left =
  1107. this.index * -this.slideWidth + 'px'
  1108. }
  1109. },
  1110. unloadSlide: function (index) {
  1111. var slide, firstChild
  1112. slide = this.slides[index]
  1113. firstChild = slide.firstChild
  1114. if (firstChild !== null) {
  1115. slide.removeChild(firstChild)
  1116. }
  1117. },
  1118. unloadAllSlides: function () {
  1119. var i, len
  1120. for (i = 0, len = this.slides.length; i < len; i++) {
  1121. this.unloadSlide(i)
  1122. }
  1123. },
  1124. toggleControls: function () {
  1125. var controlsClass = this.options.controlsClass
  1126. if (this.container.hasClass(controlsClass)) {
  1127. this.container.removeClass(controlsClass)
  1128. } else {
  1129. this.container.addClass(controlsClass)
  1130. }
  1131. },
  1132. toggleSlideshow: function () {
  1133. if (!this.interval) {
  1134. this.play()
  1135. } else {
  1136. this.pause()
  1137. }
  1138. },
  1139. getNodeIndex: function (element) {
  1140. return parseInt(element.getAttribute('data-index'), 10)
  1141. },
  1142. getNestedProperty: function (obj, property) {
  1143. property.replace(
  1144. // Matches native JavaScript notation in a String,
  1145. // e.g. '["doubleQuoteProp"].dotProp[2]'
  1146. // eslint-disable-next-line no-useless-escape
  1147. /\[(?:'([^']+)'|"([^"]+)"|(\d+))\]|(?:(?:^|\.)([^\.\[]+))/g,
  1148. function (str, singleQuoteProp, doubleQuoteProp, arrayIndex, dotProp) {
  1149. var prop =
  1150. dotProp ||
  1151. singleQuoteProp ||
  1152. doubleQuoteProp ||
  1153. (arrayIndex && parseInt(arrayIndex, 10))
  1154. if (str && obj) {
  1155. obj = obj[prop]
  1156. }
  1157. }
  1158. )
  1159. return obj
  1160. },
  1161. getDataProperty: function (obj, property) {
  1162. var key
  1163. var prop
  1164. if (obj.dataset) {
  1165. key = property.replace(/-([a-z])/g, function (_, b) {
  1166. return b.toUpperCase()
  1167. })
  1168. prop = obj.dataset[key]
  1169. } else if (obj.getAttribute) {
  1170. prop = obj.getAttribute(
  1171. 'data-' + property.replace(/([A-Z])/g, '-$1').toLowerCase()
  1172. )
  1173. }
  1174. if (typeof prop === 'string') {
  1175. // eslint-disable-next-line no-useless-escape
  1176. if (
  1177. /^(true|false|null|-?\d+(\.\d+)?|\{[\s\S]*\}|\[[\s\S]*\])$/.test(prop)
  1178. ) {
  1179. try {
  1180. return $.parseJSON(prop)
  1181. } catch (ignore) {}
  1182. }
  1183. return prop
  1184. }
  1185. },
  1186. getItemProperty: function (obj, property) {
  1187. var prop = this.getDataProperty(obj, property)
  1188. if (prop === undefined) {
  1189. prop = obj[property]
  1190. }
  1191. if (prop === undefined) {
  1192. prop = this.getNestedProperty(obj, property)
  1193. }
  1194. return prop
  1195. },
  1196. initStartIndex: function () {
  1197. var index = this.options.index
  1198. var urlProperty = this.options.urlProperty
  1199. var i
  1200. // Check if the index is given as a list object:
  1201. if (index && typeof index !== 'number') {
  1202. for (i = 0; i < this.num; i += 1) {
  1203. if (
  1204. this.list[i] === index ||
  1205. this.getItemProperty(this.list[i], urlProperty) ===
  1206. this.getItemProperty(index, urlProperty)
  1207. ) {
  1208. index = i
  1209. break
  1210. }
  1211. }
  1212. }
  1213. // Make sure the index is in the list range:
  1214. this.index = this.circle(parseInt(index, 10) || 0)
  1215. },
  1216. initEventListeners: function () {
  1217. var that = this
  1218. var slidesContainer = this.slidesContainer
  1219. function proxyListener (event) {
  1220. var type =
  1221. that.support.transition && that.support.transition.end === event.type
  1222. ? 'transitionend'
  1223. : event.type
  1224. that['on' + type](event)
  1225. }
  1226. $(window).on('resize', proxyListener)
  1227. $(document.body).on('keydown', proxyListener)
  1228. this.container.on('click', proxyListener)
  1229. if (this.support.touch) {
  1230. slidesContainer.on(
  1231. 'touchstart touchmove touchend touchcancel',
  1232. proxyListener
  1233. )
  1234. } else if (this.options.emulateTouchEvents && this.support.transition) {
  1235. slidesContainer.on(
  1236. 'mousedown mousemove mouseup mouseout',
  1237. proxyListener
  1238. )
  1239. }
  1240. if (this.support.transition) {
  1241. slidesContainer.on(this.support.transition.end, proxyListener)
  1242. }
  1243. this.proxyListener = proxyListener
  1244. },
  1245. destroyEventListeners: function () {
  1246. var slidesContainer = this.slidesContainer
  1247. var proxyListener = this.proxyListener
  1248. $(window).off('resize', proxyListener)
  1249. $(document.body).off('keydown', proxyListener)
  1250. this.container.off('click', proxyListener)
  1251. if (this.support.touch) {
  1252. slidesContainer.off(
  1253. 'touchstart touchmove touchend touchcancel',
  1254. proxyListener
  1255. )
  1256. } else if (this.options.emulateTouchEvents && this.support.transition) {
  1257. slidesContainer.off(
  1258. 'mousedown mousemove mouseup mouseout',
  1259. proxyListener
  1260. )
  1261. }
  1262. if (this.support.transition) {
  1263. slidesContainer.off(this.support.transition.end, proxyListener)
  1264. }
  1265. },
  1266. handleOpen: function () {
  1267. if (this.options.onopened) {
  1268. this.options.onopened.call(this)
  1269. }
  1270. },
  1271. initWidget: function () {
  1272. var that = this
  1273. function openHandler (event) {
  1274. if (event.target === that.container[0]) {
  1275. that.container.off(that.support.transition.end, openHandler)
  1276. that.handleOpen()
  1277. }
  1278. }
  1279. this.container = $(this.options.container)
  1280. if (!this.container.length) {
  1281. this.console.log(
  1282. 'blueimp Gallery: Widget container not found.',
  1283. this.options.container
  1284. )
  1285. return false
  1286. }
  1287. this.slidesContainer = this.container
  1288. .find(this.options.slidesContainer)
  1289. .first()
  1290. if (!this.slidesContainer.length) {
  1291. this.console.log(
  1292. 'blueimp Gallery: Slides container not found.',
  1293. this.options.slidesContainer
  1294. )
  1295. return false
  1296. }
  1297. this.titleElement = this.container.find(this.options.titleElement).first()
  1298. if (this.num === 1) {
  1299. this.container.addClass(this.options.singleClass)
  1300. }
  1301. if (this.options.onopen) {
  1302. this.options.onopen.call(this)
  1303. }
  1304. if (this.support.transition && this.options.displayTransition) {
  1305. this.container.on(this.support.transition.end, openHandler)
  1306. } else {
  1307. this.handleOpen()
  1308. }
  1309. if (this.options.hidePageScrollbars) {
  1310. // Hide the page scrollbars:
  1311. this.bodyOverflowStyle = document.body.style.overflow
  1312. document.body.style.overflow = 'hidden'
  1313. }
  1314. this.container[0].style.display = 'block'
  1315. this.initSlides()
  1316. this.container.addClass(this.options.displayClass)
  1317. },
  1318. initOptions: function (options) {
  1319. // Create a copy of the prototype options:
  1320. this.options = $.extend({}, this.options)
  1321. // Check if carousel mode is enabled:
  1322. if (
  1323. (options && options.carousel) ||
  1324. (this.options.carousel && (!options || options.carousel !== false))
  1325. ) {
  1326. $.extend(this.options, this.carouselOptions)
  1327. }
  1328. // Override any given options:
  1329. $.extend(this.options, options)
  1330. if (this.num < 3) {
  1331. // 1 or 2 slides cannot be displayed continuous,
  1332. // remember the original option by setting to null instead of false:
  1333. this.options.continuous = this.options.continuous ? null : false
  1334. }
  1335. if (!this.support.transition) {
  1336. this.options.emulateTouchEvents = false
  1337. }
  1338. if (this.options.event) {
  1339. this.preventDefault(this.options.event)
  1340. }
  1341. }
  1342. })
  1343. return Gallery
  1344. })