blueimp-gallery.js 46 KB

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