PageSourceSearch

https://glif.fr/wa_gallery/js/blueimp-gallery.min.js?v=125&

js glif.fr collected 2026-10-02 22:40:41 UTC 46,245 bytes, 1,436 lines download raw bytes

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

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.