1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477 |
- /*
- * blueimp Gallery JS
- * https://github.com/blueimp/Gallery
- *
- * Copyright 2013, Sebastian Tschan
- * https://blueimp.net
- *
- * Swipe implementation based on
- * https://github.com/bradbirdsall/Swipe
- *
- * Licensed under the MIT license:
- * https://opensource.org/licenses/MIT
- */
- /* global define, DocumentTouch */
- /* eslint-disable no-param-reassign */
- ;(function (factory) {
- 'use strict'
- if (typeof define === 'function' && define.amd) {
- // Register as an anonymous AMD module:
- define(['./blueimp-helper'], factory)
- } else {
- // Browser globals:
- window.blueimp = window.blueimp || {}
- window.blueimp.Gallery = factory(window.blueimp.helper || window.jQuery)
- }
- })(function ($) {
- 'use strict'
- /**
- * Gallery constructor
- *
- * @class
- * @param {Array|NodeList} list Gallery content
- * @param {object} [options] Gallery options
- * @returns {object} Gallery object
- */
- function Gallery(list, options) {
- if (document.body.style.maxHeight === undefined) {
- // document.body.style.maxHeight is undefined on IE6 and lower
- return null
- }
- if (!this || this.options !== Gallery.prototype.options) {
- // Called as function instead of as constructor,
- // so we simply return a new instance:
- return new Gallery(list, options)
- }
- if (!list || !list.length) {
- this.console.log(
- 'blueimp Gallery: No or empty list provided as first argument.',
- list
- )
- return
- }
- this.list = list
- this.num = list.length
- this.initOptions(options)
- this.initialize()
- }
- $.extend(Gallery.prototype, {
- options: {
- // The Id, element or querySelector of the gallery widget:
- container: '#blueimp-gallery',
- // The tag name, Id, element or querySelector of the slides container:
- slidesContainer: 'div',
- // The tag name, Id, element or querySelector of the title element:
- titleElement: 'h3',
- // The class to add when the gallery is visible:
- displayClass: 'blueimp-gallery-display',
- // The class to add when the gallery controls are visible:
- controlsClass: 'blueimp-gallery-controls',
- // The class to add when the gallery only displays one element:
- singleClass: 'blueimp-gallery-single',
- // The class to add when the left edge has been reached:
- leftEdgeClass: 'blueimp-gallery-left',
- // The class to add when the right edge has been reached:
- rightEdgeClass: 'blueimp-gallery-right',
- // The class to add when the automatic slideshow is active:
- playingClass: 'blueimp-gallery-playing',
- // The class for all slides:
- slideClass: 'slide',
- // The slide class for loading elements:
- slideLoadingClass: 'slide-loading',
- // The slide class for elements that failed to load:
- slideErrorClass: 'slide-error',
- // The class for the content element loaded into each slide:
- slideContentClass: 'slide-content',
- // The class for the "toggle" control:
- toggleClass: 'toggle',
- // The class for the "prev" control:
- prevClass: 'prev',
- // The class for the "next" control:
- nextClass: 'next',
- // The class for the "close" control:
- closeClass: 'close',
- // The class for the "play-pause" toggle control:
- playPauseClass: 'play-pause',
- // The list object property (or data attribute) with the object type:
- typeProperty: 'type',
- // The list object property (or data attribute) with the object title:
- titleProperty: 'title',
- // The list object property (or data attribute) with the object alt text:
- altTextProperty: 'alt',
- // The list object property (or data attribute) with the object URL:
- urlProperty: 'href',
- // The list object property (or data attribute) with the object srcset URL(s):
- srcsetProperty: 'urlset',
- // The gallery listens for transitionend events before triggering the
- // opened and closed events, unless the following option is set to false:
- displayTransition: true,
- // Defines if the gallery slides are cleared from the gallery modal,
- // or reused for the next gallery initialization:
- clearSlides: true,
- // Defines if images should be stretched to fill the available space,
- // while maintaining their aspect ratio (will only be enabled for browsers
- // supporting background-size="contain", which excludes IE < 9).
- // Set to "cover", to make images cover all available space (requires
- // support for background-size="cover", which excludes IE < 9):
- stretchImages: false,
- // Toggle the controls on pressing the Return key:
- toggleControlsOnReturn: true,
- // Toggle the controls on slide click:
- toggleControlsOnSlideClick: true,
- // Toggle the automatic slideshow interval on pressing the Space key:
- toggleSlideshowOnSpace: true,
- // Navigate the gallery by pressing left and right on the keyboard:
- enableKeyboardNavigation: true,
- // Close the gallery on pressing the Esc key:
- closeOnEscape: true,
- // Close the gallery when clicking on an empty slide area:
- closeOnSlideClick: true,
- // Close the gallery by swiping up or down:
- closeOnSwipeUpOrDown: true,
- // Close the gallery when URL changes:
- closeOnHashChange: true,
- // Emulate touch events on mouse-pointer devices such as desktop browsers:
- emulateTouchEvents: true,
- // Stop touch events from bubbling up to ancestor elements of the Gallery:
- stopTouchEventsPropagation: false,
- // Hide the page scrollbars:
- hidePageScrollbars: true,
- // Stops any touches on the container from scrolling the page:
- disableScroll: true,
- // Carousel mode (shortcut for carousel specific options):
- carousel: false,
- // Allow continuous navigation, moving from last to first
- // and from first to last slide:
- continuous: true,
- // Remove elements outside of the preload range from the DOM:
- unloadElements: true,
- // Start with the automatic slideshow:
- startSlideshow: false,
- // Delay in milliseconds between slides for the automatic slideshow:
- slideshowInterval: 5000,
- // The direction the slides are moving: ltr=LeftToRight or rtl=RightToLeft
- slideshowDirection: 'ltr',
- // The starting index as integer.
- // Can also be an object of the given list,
- // or an equal object with the same url property:
- index: 0,
- // The number of elements to load around the current index:
- preloadRange: 2,
- // The transition speed between slide changes in milliseconds:
- transitionSpeed: 400,
- // The transition speed for automatic slide changes, set to an integer
- // greater 0 to override the default transition speed:
- slideshowTransitionSpeed: undefined,
- // The event object for which the default action will be canceled
- // on Gallery initialization (e.g. the click event to open the Gallery):
- event: undefined,
- // Callback function executed when the Gallery is initialized.
- // Is called with the gallery instance as "this" object:
- onopen: undefined,
- // Callback function executed when the Gallery has been initialized
- // and the initialization transition has been completed.
- // Is called with the gallery instance as "this" object:
- onopened: undefined,
- // Callback function executed on slide change.
- // Is called with the gallery instance as "this" object and the
- // current index and slide as arguments:
- onslide: undefined,
- // Callback function executed after the slide change transition.
- // Is called with the gallery instance as "this" object and the
- // current index and slide as arguments:
- onslideend: undefined,
- // Callback function executed on slide content load.
- // Is called with the gallery instance as "this" object and the
- // slide index and slide element as arguments:
- onslidecomplete: undefined,
- // Callback function executed when the Gallery is about to be closed.
- // Is called with the gallery instance as "this" object:
- onclose: undefined,
- // Callback function executed when the Gallery has been closed
- // and the closing transition has been completed.
- // Is called with the gallery instance as "this" object:
- onclosed: undefined
- },
- carouselOptions: {
- hidePageScrollbars: false,
- toggleControlsOnReturn: false,
- toggleSlideshowOnSpace: false,
- enableKeyboardNavigation: false,
- closeOnEscape: false,
- closeOnSlideClick: false,
- closeOnSwipeUpOrDown: false,
- disableScroll: false,
- startSlideshow: true
- },
- console:
- window.console && typeof window.console.log === 'function'
- ? window.console
- : { log: function () {} },
- // Detect touch, transition, transform and background-size support:
- support: (function (element) {
- var support = {
- touch:
- window.ontouchstart !== undefined ||
- (window.DocumentTouch && document instanceof DocumentTouch)
- }
- var transitions = {
- webkitTransition: {
- end: 'webkitTransitionEnd',
- prefix: '-webkit-'
- },
- MozTransition: {
- end: 'transitionend',
- prefix: '-moz-'
- },
- OTransition: {
- end: 'otransitionend',
- prefix: '-o-'
- },
- transition: {
- end: 'transitionend',
- prefix: ''
- }
- }
- var prop
- for (prop in transitions) {
- if (
- Object.prototype.hasOwnProperty.call(transitions, prop) &&
- element.style[prop] !== undefined
- ) {
- support.transition = transitions[prop]
- support.transition.name = prop
- break
- }
- }
- /**
- * Tests browser support
- */
- function elementTests() {
- var transition = support.transition
- var prop
- var translateZ
- document.body.appendChild(element)
- if (transition) {
- prop = transition.name.slice(0, -9) + 'ransform'
- if (element.style[prop] !== undefined) {
- element.style[prop] = 'translateZ(0)'
- translateZ = window
- .getComputedStyle(element)
- .getPropertyValue(transition.prefix + 'transform')
- support.transform = {
- prefix: transition.prefix,
- name: prop,
- translate: true,
- translateZ: !!translateZ && translateZ !== 'none'
- }
- }
- }
- if (element.style.backgroundSize !== undefined) {
- support.backgroundSize = {}
- element.style.backgroundSize = 'contain'
- support.backgroundSize.contain =
- window
- .getComputedStyle(element)
- .getPropertyValue('background-size') === 'contain'
- element.style.backgroundSize = 'cover'
- support.backgroundSize.cover =
- window
- .getComputedStyle(element)
- .getPropertyValue('background-size') === 'cover'
- }
- document.body.removeChild(element)
- }
- if (document.body) {
- elementTests()
- } else {
- $(document).on('DOMContentLoaded', elementTests)
- }
- return support
- // Test element, has to be standard HTML and must not be hidden
- // for the CSS3 tests using window.getComputedStyle to be applicable:
- })(document.createElement('div')),
- requestAnimationFrame:
- window.requestAnimationFrame ||
- window.webkitRequestAnimationFrame ||
- window.mozRequestAnimationFrame,
- cancelAnimationFrame:
- window.cancelAnimationFrame ||
- window.webkitCancelRequestAnimationFrame ||
- window.webkitCancelAnimationFrame ||
- window.mozCancelAnimationFrame,
- initialize: function () {
- this.initStartIndex()
- if (this.initWidget() === false) {
- return false
- }
- this.initEventListeners()
- // Load the slide at the given index:
- this.onslide(this.index)
- // Manually trigger the slideend event for the initial slide:
- this.ontransitionend()
- // Start the automatic slideshow if applicable:
- if (this.options.startSlideshow) {
- this.play()
- }
- },
- slide: function (to, speed) {
- window.clearTimeout(this.timeout)
- var index = this.index
- var direction
- var naturalDirection
- var diff
- if (index === to || this.num === 1) {
- return
- }
- if (!speed) {
- speed = this.options.transitionSpeed
- }
- if (this.support.transform) {
- if (!this.options.continuous) {
- to = this.circle(to)
- }
- // 1: backward, -1: forward:
- direction = Math.abs(index - to) / (index - to)
- // Get the actual position of the slide:
- if (this.options.continuous) {
- naturalDirection = direction
- direction = -this.positions[this.circle(to)] / this.slideWidth
- // If going forward but to < index, use to = slides.length + to
- // If going backward but to > index, use to = -slides.length + to
- if (direction !== naturalDirection) {
- to = -direction * this.num + to
- }
- }
- diff = Math.abs(index - to) - 1
- // Move all the slides between index and to in the right direction:
- while (diff) {
- diff -= 1
- this.move(
- this.circle((to > index ? to : index) - diff - 1),
- this.slideWidth * direction,
- 0
- )
- }
- to = this.circle(to)
- this.move(index, this.slideWidth * direction, speed)
- this.move(to, 0, speed)
- if (this.options.continuous) {
- this.move(
- this.circle(to - direction),
- -(this.slideWidth * direction),
- 0
- )
- }
- } else {
- to = this.circle(to)
- this.animate(index * -this.slideWidth, to * -this.slideWidth, speed)
- }
- this.onslide(to)
- },
- getIndex: function () {
- return this.index
- },
- getNumber: function () {
- return this.num
- },
- prev: function () {
- if (this.options.continuous || this.index) {
- this.slide(this.index - 1)
- }
- },
- next: function () {
- if (this.options.continuous || this.index < this.num - 1) {
- this.slide(this.index + 1)
- }
- },
- play: function (time) {
- var that = this
- var nextIndex =
- this.index + (this.options.slideshowDirection === 'rtl' ? -1 : 1)
- window.clearTimeout(this.timeout)
- this.interval = time || this.options.slideshowInterval
- if (this.elements[this.index] > 1) {
- this.timeout = this.setTimeout(
- (!this.requestAnimationFrame && this.slide) ||
- function (to, speed) {
- that.animationFrameId = that.requestAnimationFrame.call(
- window,
- function () {
- that.slide(to, speed)
- }
- )
- },
- [nextIndex, this.options.slideshowTransitionSpeed],
- this.interval
- )
- }
- this.container.addClass(this.options.playingClass)
- },
- pause: function () {
- window.clearTimeout(this.timeout)
- this.interval = null
- if (this.cancelAnimationFrame) {
- this.cancelAnimationFrame.call(window, this.animationFrameId)
- this.animationFrameId = null
- }
- this.container.removeClass(this.options.playingClass)
- },
- add: function (list) {
- var i
- if (!list.concat) {
- // Make a real array out of the list to add:
- list = Array.prototype.slice.call(list)
- }
- if (!this.list.concat) {
- // Make a real array out of the Gallery list:
- this.list = Array.prototype.slice.call(this.list)
- }
- this.list = this.list.concat(list)
- this.num = this.list.length
- if (this.num > 2 && this.options.continuous === null) {
- this.options.continuous = true
- this.container.removeClass(this.options.leftEdgeClass)
- }
- this.container
- .removeClass(this.options.rightEdgeClass)
- .removeClass(this.options.singleClass)
- for (i = this.num - list.length; i < this.num; i += 1) {
- this.addSlide(i)
- this.positionSlide(i)
- }
- this.positions.length = this.num
- this.initSlides(true)
- },
- resetSlides: function () {
- this.slidesContainer.empty()
- this.unloadAllSlides()
- this.slides = []
- },
- handleClose: function () {
- var options = this.options
- this.destroyEventListeners()
- // Cancel the slideshow:
- this.pause()
- this.container[0].style.display = 'none'
- this.container
- .removeClass(options.displayClass)
- .removeClass(options.singleClass)
- .removeClass(options.leftEdgeClass)
- .removeClass(options.rightEdgeClass)
- if (options.hidePageScrollbars) {
- document.body.style.overflow = this.bodyOverflowStyle
- }
- if (this.options.clearSlides) {
- this.resetSlides()
- }
- if (this.options.onclosed) {
- this.options.onclosed.call(this)
- }
- },
- close: function () {
- var that = this
- /**
- * Close handler
- *
- * @param {event} event Close event
- */
- function closeHandler(event) {
- if (event.target === that.container[0]) {
- that.container.off(that.support.transition.end, closeHandler)
- that.handleClose()
- }
- }
- if (this.options.onclose) {
- this.options.onclose.call(this)
- }
- if (this.support.transition && this.options.displayTransition) {
- this.container.on(this.support.transition.end, closeHandler)
- this.container.removeClass(this.options.displayClass)
- } else {
- this.handleClose()
- }
- },
- circle: function (index) {
- // Always return a number inside of the slides index range:
- return (this.num + (index % this.num)) % this.num
- },
- move: function (index, dist, speed) {
- this.translateX(index, dist, speed)
- this.positions[index] = dist
- },
- translate: function (index, x, y, speed) {
- if (!this.slides[index]) return
- var style = this.slides[index].style
- var transition = this.support.transition
- var transform = this.support.transform
- style[transition.name + 'Duration'] = speed + 'ms'
- style[transform.name] =
- 'translate(' +
- x +
- 'px, ' +
- y +
- 'px)' +
- (transform.translateZ ? ' translateZ(0)' : '')
- },
- translateX: function (index, x, speed) {
- this.translate(index, x, 0, speed)
- },
- translateY: function (index, y, speed) {
- this.translate(index, 0, y, speed)
- },
- animate: function (from, to, speed) {
- if (!speed) {
- this.slidesContainer[0].style.left = to + 'px'
- return
- }
- var that = this
- var start = new Date().getTime()
- var timer = window.setInterval(function () {
- var timeElap = new Date().getTime() - start
- if (timeElap > speed) {
- that.slidesContainer[0].style.left = to + 'px'
- that.ontransitionend()
- window.clearInterval(timer)
- return
- }
- that.slidesContainer[0].style.left =
- (to - from) * (Math.floor((timeElap / speed) * 100) / 100) +
- from +
- 'px'
- }, 4)
- },
- preventDefault: function (event) {
- if (event.preventDefault) {
- event.preventDefault()
- } else {
- event.returnValue = false
- }
- },
- stopPropagation: function (event) {
- if (event.stopPropagation) {
- event.stopPropagation()
- } else {
- event.cancelBubble = true
- }
- },
- onresize: function () {
- this.initSlides(true)
- },
- onhashchange: function () {
- if (this.options.closeOnHashChange) {
- this.close()
- }
- },
- onmousedown: function (event) {
- // Trigger on clicks of the left mouse button only
- // and exclude video & audio elements:
- if (
- event.which &&
- event.which === 1 &&
- event.target.nodeName !== 'VIDEO' &&
- event.target.nodeName !== 'AUDIO'
- ) {
- // Preventing the default mousedown action is required
- // to make touch emulation work with Firefox:
- event.preventDefault()
- ;(event.originalEvent || event).touches = [
- {
- pageX: event.pageX,
- pageY: event.pageY
- }
- ]
- this.ontouchstart(event)
- }
- },
- onmousemove: function (event) {
- if (this.touchStart) {
- ;(event.originalEvent || event).touches = [
- {
- pageX: event.pageX,
- pageY: event.pageY
- }
- ]
- this.ontouchmove(event)
- }
- },
- onmouseup: function (event) {
- if (this.touchStart) {
- this.ontouchend(event)
- delete this.touchStart
- }
- },
- onmouseout: function (event) {
- if (this.touchStart) {
- var target = event.target
- var related = event.relatedTarget
- if (!related || (related !== target && !$.contains(target, related))) {
- this.onmouseup(event)
- }
- }
- },
- ontouchstart: function (event) {
- if (this.options.stopTouchEventsPropagation) {
- this.stopPropagation(event)
- }
- // jQuery doesn't copy touch event properties by default,
- // so we have to access the originalEvent object:
- var touches = (event.originalEvent || event).touches[0]
- this.touchStart = {
- // Remember the initial touch coordinates:
- x: touches.pageX,
- y: touches.pageY,
- // Store the time to determine touch duration:
- time: Date.now()
- }
- // Helper variable to detect scroll movement:
- this.isScrolling = undefined
- // Reset delta values:
- this.touchDelta = {}
- },
- ontouchmove: function (event) {
- if (this.options.stopTouchEventsPropagation) {
- this.stopPropagation(event)
- }
- // jQuery doesn't copy touch event properties by default,
- // so we have to access the originalEvent object:
- var touches = (event.originalEvent || event).touches[0]
- var scale = (event.originalEvent || event).scale
- var index = this.index
- var touchDeltaX
- var indices
- // Ensure this is a one touch swipe and not, e.g. a pinch:
- if (touches.length > 1 || (scale && scale !== 1)) {
- return
- }
- if (this.options.disableScroll) {
- event.preventDefault()
- }
- // Measure change in x and y coordinates:
- this.touchDelta = {
- x: touches.pageX - this.touchStart.x,
- y: touches.pageY - this.touchStart.y
- }
- touchDeltaX = this.touchDelta.x
- // Detect if this is a vertical scroll movement (run only once per touch):
- if (this.isScrolling === undefined) {
- this.isScrolling =
- this.isScrolling ||
- Math.abs(touchDeltaX) < Math.abs(this.touchDelta.y)
- }
- if (!this.isScrolling) {
- // Always prevent horizontal scroll:
- event.preventDefault()
- // Stop the slideshow:
- window.clearTimeout(this.timeout)
- if (this.options.continuous) {
- indices = [this.circle(index + 1), index, this.circle(index - 1)]
- } else {
- // Increase resistance if first slide and sliding left
- // or last slide and sliding right:
- this.touchDelta.x = touchDeltaX =
- touchDeltaX /
- ((!index && touchDeltaX > 0) ||
- (index === this.num - 1 && touchDeltaX < 0)
- ? Math.abs(touchDeltaX) / this.slideWidth + 1
- : 1)
- indices = [index]
- if (index) {
- indices.push(index - 1)
- }
- if (index < this.num - 1) {
- indices.unshift(index + 1)
- }
- }
- while (indices.length) {
- index = indices.pop()
- this.translateX(index, touchDeltaX + this.positions[index], 0)
- }
- } else {
- this.translateY(index, this.touchDelta.y + this.positions[index], 0)
- }
- },
- ontouchend: function (event) {
- if (this.options.stopTouchEventsPropagation) {
- this.stopPropagation(event)
- }
- var index = this.index
- var speed = this.options.transitionSpeed
- var slideWidth = this.slideWidth
- var isShortDuration = Number(Date.now() - this.touchStart.time) < 250
- // Determine if slide attempt triggers next/prev slide:
- var isValidSlide =
- (isShortDuration && Math.abs(this.touchDelta.x) > 20) ||
- Math.abs(this.touchDelta.x) > slideWidth / 2
- // Determine if slide attempt is past start or end:
- var isPastBounds =
- (!index && this.touchDelta.x > 0) ||
- (index === this.num - 1 && this.touchDelta.x < 0)
- var isValidClose =
- !isValidSlide &&
- this.options.closeOnSwipeUpOrDown &&
- ((isShortDuration && Math.abs(this.touchDelta.y) > 20) ||
- Math.abs(this.touchDelta.y) > this.slideHeight / 2)
- var direction
- var indexForward
- var indexBackward
- var distanceForward
- var distanceBackward
- if (this.options.continuous) {
- isPastBounds = false
- }
- // Determine direction of swipe (true: right, false: left):
- direction = this.touchDelta.x < 0 ? -1 : 1
- if (!this.isScrolling) {
- if (isValidSlide && !isPastBounds) {
- indexForward = index + direction
- indexBackward = index - direction
- distanceForward = slideWidth * direction
- distanceBackward = -slideWidth * direction
- if (this.options.continuous) {
- this.move(this.circle(indexForward), distanceForward, 0)
- this.move(this.circle(index - 2 * direction), distanceBackward, 0)
- } else if (indexForward >= 0 && indexForward < this.num) {
- this.move(indexForward, distanceForward, 0)
- }
- this.move(index, this.positions[index] + distanceForward, speed)
- this.move(
- this.circle(indexBackward),
- this.positions[this.circle(indexBackward)] + distanceForward,
- speed
- )
- index = this.circle(indexBackward)
- this.onslide(index)
- } else {
- // Move back into position
- if (this.options.continuous) {
- this.move(this.circle(index - 1), -slideWidth, speed)
- this.move(index, 0, speed)
- this.move(this.circle(index + 1), slideWidth, speed)
- } else {
- if (index) {
- this.move(index - 1, -slideWidth, speed)
- }
- this.move(index, 0, speed)
- if (index < this.num - 1) {
- this.move(index + 1, slideWidth, speed)
- }
- }
- }
- } else {
- if (isValidClose) {
- this.close()
- } else {
- // Move back into position
- this.translateY(index, 0, speed)
- }
- }
- },
- ontouchcancel: function (event) {
- if (this.touchStart) {
- this.ontouchend(event)
- delete this.touchStart
- }
- },
- ontransitionend: function (event) {
- var slide = this.slides[this.index]
- if (!event || slide === event.target) {
- if (this.interval) {
- this.play()
- }
- this.setTimeout(this.options.onslideend, [this.index, slide])
- }
- },
- oncomplete: function (event) {
- var target = event.target || event.srcElement
- var parent = target && target.parentNode
- var index
- if (!target || !parent) {
- return
- }
- index = this.getNodeIndex(parent)
- $(parent).removeClass(this.options.slideLoadingClass)
- if (event.type === 'error') {
- $(parent).addClass(this.options.slideErrorClass)
- this.elements[index] = 3 // Fail
- } else {
- this.elements[index] = 2 // Done
- }
- // Fix for IE7's lack of support for percentage max-height:
- if (target.clientHeight > this.container[0].clientHeight) {
- target.style.maxHeight = this.container[0].clientHeight
- }
- if (this.interval && this.slides[this.index] === parent) {
- this.play()
- }
- this.setTimeout(this.options.onslidecomplete, [index, parent])
- },
- onload: function (event) {
- this.oncomplete(event)
- },
- onerror: function (event) {
- this.oncomplete(event)
- },
- onkeydown: function (event) {
- switch (event.which || event.keyCode) {
- case 13: // Return
- if (this.options.toggleControlsOnReturn) {
- this.preventDefault(event)
- this.toggleControls()
- }
- break
- case 27: // Esc
- if (this.options.closeOnEscape) {
- this.close()
- // prevent Esc from closing other things
- event.stopImmediatePropagation()
- }
- break
- case 32: // Space
- if (this.options.toggleSlideshowOnSpace) {
- this.preventDefault(event)
- this.toggleSlideshow()
- }
- break
- case 37: // Left
- if (this.options.enableKeyboardNavigation) {
- this.preventDefault(event)
- this.prev()
- }
- break
- case 39: // Right
- if (this.options.enableKeyboardNavigation) {
- this.preventDefault(event)
- this.next()
- }
- break
- }
- },
- handleClick: function (event) {
- var options = this.options
- var target = event.target || event.srcElement
- var parent = target.parentNode
- /**
- * Checks if the target from the close has the given class
- *
- * @param {string} className Class name
- * @returns {boolean} Returns true if the target has the class name
- */
- function isTarget(className) {
- return $(target).hasClass(className) || $(parent).hasClass(className)
- }
- if (isTarget(options.toggleClass)) {
- // Click on "toggle" control
- this.preventDefault(event)
- this.toggleControls()
- } else if (isTarget(options.prevClass)) {
- // Click on "prev" control
- this.preventDefault(event)
- this.prev()
- } else if (isTarget(options.nextClass)) {
- // Click on "next" control
- this.preventDefault(event)
- this.next()
- } else if (isTarget(options.closeClass)) {
- // Click on "close" control
- this.preventDefault(event)
- this.close()
- } else if (isTarget(options.playPauseClass)) {
- // Click on "play-pause" control
- this.preventDefault(event)
- this.toggleSlideshow()
- } else if (parent === this.slidesContainer[0]) {
- // Click on slide background
- if (options.closeOnSlideClick) {
- this.preventDefault(event)
- this.close()
- } else if (options.toggleControlsOnSlideClick) {
- this.preventDefault(event)
- this.toggleControls()
- }
- } else if (
- parent.parentNode &&
- parent.parentNode === this.slidesContainer[0]
- ) {
- // Click on displayed element
- if (options.toggleControlsOnSlideClick) {
- this.preventDefault(event)
- this.toggleControls()
- }
- }
- },
- onclick: function (event) {
- if (
- this.options.emulateTouchEvents &&
- this.touchDelta &&
- (Math.abs(this.touchDelta.x) > 20 || Math.abs(this.touchDelta.y) > 20)
- ) {
- delete this.touchDelta
- return
- }
- return this.handleClick(event)
- },
- updateEdgeClasses: function (index) {
- if (!index) {
- this.container.addClass(this.options.leftEdgeClass)
- } else {
- this.container.removeClass(this.options.leftEdgeClass)
- }
- if (index === this.num - 1) {
- this.container.addClass(this.options.rightEdgeClass)
- } else {
- this.container.removeClass(this.options.rightEdgeClass)
- }
- },
- handleSlide: function (index) {
- if (!this.options.continuous) {
- this.updateEdgeClasses(index)
- }
- this.loadElements(index)
- if (this.options.unloadElements) {
- this.unloadElements(index)
- }
- this.setTitle(index)
- },
- onslide: function (index) {
- this.index = index
- this.handleSlide(index)
- this.setTimeout(this.options.onslide, [index, this.slides[index]])
- },
- setTitle: function (index) {
- var firstChild = this.slides[index].firstChild
- var text = firstChild.title || firstChild.alt
- var titleElement = this.titleElement
- if (titleElement.length) {
- this.titleElement.empty()
- if (text) {
- titleElement[0].appendChild(document.createTextNode(text))
- }
- }
- },
- setTimeout: function (func, args, wait) {
- var that = this
- return (
- func &&
- window.setTimeout(function () {
- func.apply(that, args || [])
- }, wait || 0)
- )
- },
- imageFactory: function (obj, callback) {
- var that = this
- var img = this.imagePrototype.cloneNode(false)
- var url = obj
- var backgroundSize = this.options.stretchImages
- var called
- var element
- var title
- var altText
- /**
- * Wraps the callback function for the load/error event
- *
- * @param {event} event load/error event
- * @returns {number} timeout ID
- */
- function callbackWrapper(event) {
- if (!called) {
- event = {
- type: event.type,
- target: element
- }
- if (!element.parentNode) {
- // Fix for IE7 firing the load event for
- // cached images before the element could
- // be added to the DOM:
- return that.setTimeout(callbackWrapper, [event])
- }
- called = true
- $(img).off('load error', callbackWrapper)
- if (backgroundSize) {
- if (event.type === 'load') {
- element.style.background = 'url("' + url + '") center no-repeat'
- element.style.backgroundSize = backgroundSize
- }
- }
- callback(event)
- }
- }
- if (typeof url !== 'string') {
- url = this.getItemProperty(obj, this.options.urlProperty)
- title = this.getItemProperty(obj, this.options.titleProperty)
- altText =
- this.getItemProperty(obj, this.options.altTextProperty) || title
- }
- if (backgroundSize === true) {
- backgroundSize = 'contain'
- }
- backgroundSize =
- this.support.backgroundSize &&
- this.support.backgroundSize[backgroundSize] &&
- backgroundSize
- if (backgroundSize) {
- element = this.elementPrototype.cloneNode(false)
- } else {
- element = img
- img.draggable = false
- }
- if (title) {
- element.title = title
- }
- if (altText) {
- element.alt = altText
- }
- $(img).on('load error', callbackWrapper)
- img.src = url
- return element
- },
- createElement: function (obj, callback) {
- var type = obj && this.getItemProperty(obj, this.options.typeProperty)
- var factory =
- (type && this[type.split('/')[0] + 'Factory']) || this.imageFactory
- var element = obj && factory.call(this, obj, callback)
- var srcset = this.getItemProperty(obj, this.options.srcsetProperty)
- if (!element) {
- element = this.elementPrototype.cloneNode(false)
- this.setTimeout(callback, [
- {
- type: 'error',
- target: element
- }
- ])
- }
- if (srcset) {
- element.setAttribute('srcset', srcset)
- }
- $(element).addClass(this.options.slideContentClass)
- return element
- },
- loadElement: function (index) {
- if (!this.elements[index]) {
- if (this.slides[index].firstChild) {
- this.elements[index] = $(this.slides[index]).hasClass(
- this.options.slideErrorClass
- )
- ? 3
- : 2
- } else {
- this.elements[index] = 1 // Loading
- $(this.slides[index]).addClass(this.options.slideLoadingClass)
- this.slides[index].appendChild(
- this.createElement(this.list[index], this.proxyListener)
- )
- }
- }
- },
- loadElements: function (index) {
- var limit = Math.min(this.num, this.options.preloadRange * 2 + 1)
- var j = index
- var i
- for (i = 0; i < limit; i += 1) {
- // First load the current slide element (0),
- // then the next one (+1),
- // then the previous one (-2),
- // then the next after next (+2), etc.:
- j += i * (i % 2 === 0 ? -1 : 1)
- // Connect the ends of the list to load slide elements for
- // continuous navigation:
- j = this.circle(j)
- this.loadElement(j)
- }
- },
- unloadElements: function (index) {
- var i, diff
- for (i in this.elements) {
- if (Object.prototype.hasOwnProperty.call(this.elements, i)) {
- diff = Math.abs(index - i)
- if (
- diff > this.options.preloadRange &&
- diff + this.options.preloadRange < this.num
- ) {
- this.unloadSlide(i)
- delete this.elements[i]
- }
- }
- }
- },
- addSlide: function (index) {
- var slide = this.slidePrototype.cloneNode(false)
- slide.setAttribute('data-index', index)
- this.slidesContainer[0].appendChild(slide)
- this.slides.push(slide)
- },
- positionSlide: function (index) {
- var slide = this.slides[index]
- slide.style.width = this.slideWidth + 'px'
- if (this.support.transform) {
- slide.style.left = index * -this.slideWidth + 'px'
- this.move(
- index,
- this.index > index
- ? -this.slideWidth
- : this.index < index
- ? this.slideWidth
- : 0,
- 0
- )
- }
- },
- initSlides: function (reload) {
- var clearSlides, i
- if (!reload) {
- this.positions = []
- this.positions.length = this.num
- this.elements = {}
- this.imagePrototype = document.createElement('img')
- this.elementPrototype = document.createElement('div')
- this.slidePrototype = document.createElement('div')
- $(this.slidePrototype).addClass(this.options.slideClass)
- this.slides = this.slidesContainer[0].children
- clearSlides =
- this.options.clearSlides || this.slides.length !== this.num
- }
- this.slideWidth = this.container[0].clientWidth
- this.slideHeight = this.container[0].clientHeight
- this.slidesContainer[0].style.width = this.num * this.slideWidth + 'px'
- if (clearSlides) {
- this.resetSlides()
- }
- for (i = 0; i < this.num; i += 1) {
- if (clearSlides) {
- this.addSlide(i)
- }
- this.positionSlide(i)
- }
- // Reposition the slides before and after the given index:
- if (this.options.continuous && this.support.transform) {
- this.move(this.circle(this.index - 1), -this.slideWidth, 0)
- this.move(this.circle(this.index + 1), this.slideWidth, 0)
- }
- if (!this.support.transform) {
- this.slidesContainer[0].style.left =
- this.index * -this.slideWidth + 'px'
- }
- },
- unloadSlide: function (index) {
- var slide, firstChild
- slide = this.slides[index]
- firstChild = slide.firstChild
- if (firstChild !== null) {
- slide.removeChild(firstChild)
- }
- },
- unloadAllSlides: function () {
- var i, len
- for (i = 0, len = this.slides.length; i < len; i++) {
- this.unloadSlide(i)
- }
- },
- toggleControls: function () {
- var controlsClass = this.options.controlsClass
- if (this.container.hasClass(controlsClass)) {
- this.container.removeClass(controlsClass)
- } else {
- this.container.addClass(controlsClass)
- }
- },
- toggleSlideshow: function () {
- if (!this.interval) {
- this.play()
- } else {
- this.pause()
- }
- },
- getNodeIndex: function (element) {
- return parseInt(element.getAttribute('data-index'), 10)
- },
- getNestedProperty: function (obj, property) {
- property.replace(
- // Matches native JavaScript notation in a String,
- // e.g. '["doubleQuoteProp"].dotProp[2]'
- // eslint-disable-next-line no-useless-escape
- /\[(?:'([^']+)'|"([^"]+)"|(\d+))\]|(?:(?:^|\.)([^\.\[]+))/g,
- function (str, singleQuoteProp, doubleQuoteProp, arrayIndex, dotProp) {
- var prop =
- dotProp ||
- singleQuoteProp ||
- doubleQuoteProp ||
- (arrayIndex && parseInt(arrayIndex, 10))
- if (str && obj) {
- obj = obj[prop]
- }
- }
- )
- return obj
- },
- getDataProperty: function (obj, property) {
- var key
- var prop
- if (obj.dataset) {
- key = property.replace(/-([a-z])/g, function (_, b) {
- return b.toUpperCase()
- })
- prop = obj.dataset[key]
- } else if (obj.getAttribute) {
- prop = obj.getAttribute(
- 'data-' + property.replace(/([A-Z])/g, '-$1').toLowerCase()
- )
- }
- if (typeof prop === 'string') {
- // eslint-disable-next-line no-useless-escape
- if (
- /^(true|false|null|-?\d+(\.\d+)?|\{[\s\S]*\}|\[[\s\S]*\])$/.test(prop)
- ) {
- try {
- return $.parseJSON(prop)
- } catch (ignore) {
- // ignore JSON parsing errors
- }
- }
- return prop
- }
- },
- getItemProperty: function (obj, property) {
- var prop = this.getDataProperty(obj, property)
- if (prop === undefined) {
- prop = obj[property]
- }
- if (prop === undefined) {
- prop = this.getNestedProperty(obj, property)
- }
- return prop
- },
- initStartIndex: function () {
- var index = this.options.index
- var urlProperty = this.options.urlProperty
- var i
- // Check if the index is given as a list object:
- if (index && typeof index !== 'number') {
- for (i = 0; i < this.num; i += 1) {
- if (
- this.list[i] === index ||
- this.getItemProperty(this.list[i], urlProperty) ===
- this.getItemProperty(index, urlProperty)
- ) {
- index = i
- break
- }
- }
- }
- // Make sure the index is in the list range:
- this.index = this.circle(parseInt(index, 10) || 0)
- },
- initEventListeners: function () {
- var that = this
- var slidesContainer = this.slidesContainer
- /**
- * Proxy listener
- *
- * @param {event} event original event
- */
- function proxyListener(event) {
- var type =
- that.support.transition && that.support.transition.end === event.type
- ? 'transitionend'
- : event.type
- that['on' + type](event)
- }
- $(window).on('resize', proxyListener)
- $(window).on('hashchange', proxyListener)
- $(document.body).on('keydown', proxyListener)
- this.container.on('click', proxyListener)
- if (this.support.touch) {
- slidesContainer.on(
- 'touchstart touchmove touchend touchcancel',
- proxyListener
- )
- } else if (this.options.emulateTouchEvents && this.support.transition) {
- slidesContainer.on(
- 'mousedown mousemove mouseup mouseout',
- proxyListener
- )
- }
- if (this.support.transition) {
- slidesContainer.on(this.support.transition.end, proxyListener)
- }
- this.proxyListener = proxyListener
- },
- destroyEventListeners: function () {
- var slidesContainer = this.slidesContainer
- var proxyListener = this.proxyListener
- $(window).off('resize', proxyListener)
- $(document.body).off('keydown', proxyListener)
- this.container.off('click', proxyListener)
- if (this.support.touch) {
- slidesContainer.off(
- 'touchstart touchmove touchend touchcancel',
- proxyListener
- )
- } else if (this.options.emulateTouchEvents && this.support.transition) {
- slidesContainer.off(
- 'mousedown mousemove mouseup mouseout',
- proxyListener
- )
- }
- if (this.support.transition) {
- slidesContainer.off(this.support.transition.end, proxyListener)
- }
- },
- handleOpen: function () {
- if (this.options.onopened) {
- this.options.onopened.call(this)
- }
- },
- initWidget: function () {
- var that = this
- /**
- * Open handler
- *
- * @param {event} event Gallery open event
- */
- function openHandler(event) {
- if (event.target === that.container[0]) {
- that.container.off(that.support.transition.end, openHandler)
- that.handleOpen()
- }
- }
- this.container = $(this.options.container)
- if (!this.container.length) {
- this.console.log(
- 'blueimp Gallery: Widget container not found.',
- this.options.container
- )
- return false
- }
- this.slidesContainer = this.container
- .find(this.options.slidesContainer)
- .first()
- if (!this.slidesContainer.length) {
- this.console.log(
- 'blueimp Gallery: Slides container not found.',
- this.options.slidesContainer
- )
- return false
- }
- this.titleElement = this.container.find(this.options.titleElement).first()
- if (this.num === 1) {
- this.container.addClass(this.options.singleClass)
- }
- if (this.options.onopen) {
- this.options.onopen.call(this)
- }
- if (this.support.transition && this.options.displayTransition) {
- this.container.on(this.support.transition.end, openHandler)
- } else {
- this.handleOpen()
- }
- if (this.options.hidePageScrollbars) {
- // Hide the page scrollbars:
- this.bodyOverflowStyle = document.body.style.overflow
- document.body.style.overflow = 'hidden'
- }
- this.container[0].style.display = 'block'
- this.initSlides()
- this.container.addClass(this.options.displayClass)
- },
- initOptions: function (options) {
- // Create a copy of the prototype options:
- this.options = $.extend({}, this.options)
- // Check if carousel mode is enabled:
- if (
- (options && options.carousel) ||
- (this.options.carousel && (!options || options.carousel !== false))
- ) {
- $.extend(this.options, this.carouselOptions)
- }
- // Override any given options:
- $.extend(this.options, options)
- if (this.num < 3) {
- // 1 or 2 slides cannot be displayed continuous,
- // remember the original option by setting to null instead of false:
- this.options.continuous = this.options.continuous ? null : false
- }
- if (!this.support.transition) {
- this.options.emulateTouchEvents = false
- }
- if (this.options.event) {
- this.preventDefault(this.options.event)
- }
- }
- })
- return Gallery
- })
|