PageSourceSearch

https://www.iodibetto.edu.it/pvw2/lib/js/fotorama.js?v=20261001

js iodibetto.edu.it collected 2026-10-01 15:01:53 UTC 103,139 bytes, 3,714 lines download raw bytes

vendor: 7,057 bytes, lines 1-245
1/*!
2 * Fotorama 4.6.4 | http://fotorama.io/license/
3 */
4fotoramaVersion = '4.6.4';
5(function (window, document, location, $, undefined) {
6  "use strict";
7var _fotoramaClass = 'fotorama',
8    _fullscreenClass = 'fullscreen',
9
10    wrapClass = _fotoramaClass + '__wrap',
11    wrapCss2Class = wrapClass + '--css2',
12    wrapCss3Class = wrapClass + '--css3',
13    wrapVideoClass = wrapClass + '--video',
14    wrapFadeClass = wrapClass + '--fade',
15    wrapSlideClass = wrapClass + '--slide',
16    wrapNoControlsClass = wrapClass + '--no-controls',
17    wrapNoShadowsClass = wrapClass + '--no-shadows',
18    wrapPanYClass = wrapClass + '--pan-y',
19    wrapRtlClass = wrapClass + '--rtl',
20    wrapOnlyActiveClass = wrapClass + '--only-active',
21    wrapNoCaptionsClass = wrapClass + '--no-captions',
22    wrapToggleArrowsClass = wrapClass + '--toggle-arrows',
23
24    stageClass = _fotoramaClass + '__stage',
25    stageFrameClass = stageClass + '__frame',
26    stageFrameVideoClass = stageFrameClass + '--video',
27    stageShaftClass = stageClass + '__shaft',
28
29    grabClass = _fotoramaClass + '__grab',
30    pointerClass = _fotoramaClass + '__pointer',
31
32    arrClass = _fotoramaClass + '__arr',
33    arrDisabledClass = arrClass + '--disabled',
34    arrPrevClass = arrClass + '--prev',
35    arrNextClass = arrClass + '--next',
36    arrArrClass = arrClass + '__arr',
37
38    navClass = _fotoramaClass + '__nav',
39    navWrapClass = navClass + '-wrap',
40    navShaftClass = navClass + '__shaft',
41    navDotsClass = navClass + '--dots',
42    navThumbsClass = navClass + '--thumbs',
43    navFrameClass = navClass + '__frame',
44    navFrameDotClass = navFrameClass + '--dot',
45    navFrameThumbClass = navFrameClass + '--thumb',
46
47    fadeClass = _fotoramaClass + '__fade',
48    fadeFrontClass = fadeClass + '-front',
49    fadeRearClass = fadeClass + '-rear',
50
51    shadowClass = _fotoramaClass + '__shadow',
52    shadowsClass = shadowClass + 's',
53    shadowsLeftClass = shadowsClass + '--left',
54    shadowsRightClass = shadowsClass + '--right',
55
56    activeClass = _fotoramaClass + '__active',
57    selectClass = _fotoramaClass + '__select',
58
59    hiddenClass = _fotoramaClass + '--hidden',
60
61    fullscreenClass = _fotoramaClass + '--fullscreen',
62    fullscreenIconClass = _fotoramaClass + '__fullscreen-icon',
63
64    errorClass = _fotoramaClass + '__error',
65    loadingClass = _fotoramaClass + '__loading',
66    loadedClass = _fotoramaClass + '__loaded',
67    loadedFullClass = loadedClass + '--full',
68    loadedImgClass = loadedClass + '--img',
69
70    grabbingClass = _fotoramaClass + '__grabbing',
71
72    imgClass = _fotoramaClass + '__img',
73    imgFullClass = imgClass + '--full',
74
75    dotClass = _fotoramaClass + '__dot',
76    thumbClass = _fotoramaClass + '__thumb',
77    thumbBorderClass = thumbClass + '-border',
78
79    htmlClass = _fotoramaClass + '__html',
80
81    videoClass = _fotoramaClass + '__video',
82    videoPlayClass = videoClass + '-play',
83    videoCloseClass = videoClass + '-close',
84
85    captionClass = _fotoramaClass + '__caption',
86    captionWrapClass = _fotoramaClass + '__caption__wrap',
87
88    spinnerClass = _fotoramaClass + '__spinner',
89
90    buttonAttributes = '" tabindex="0" role="button';
91var JQUERY_VERSION = $ && $.fn.jquery.split('.');
92
93if (!JQUERY_VERSION
94    || JQUERY_VERSION[0] < 1
95    || (JQUERY_VERSION[0] == 1 && JQUERY_VERSION[1] < 8)) {
96  throw 'Fotorama requires jQuery 1.8 or later and will not run without it.';
97}
98// My Underscore :-)
99var _ = {};
100/* Modernizr 2.6.2 (Custom Build) | MIT & BSD
101 * Build: http://modernizr.com/download/#-csstransforms3d-prefixed-teststyles-testprop-testallprops-prefixes-domprefixes
102 */
103
104var Modernizr = (function (window, document, undefined) {
105
106  var version = '2.6.2',
107
108      Modernizr = {},
109
110      docElement = document.documentElement,
111
112      mod = 'modernizr',
113      modElem = document.createElement(mod),
114      mStyle = modElem.style,
115
116      inputElem,
117
118      toString = {}.toString,
119
120      prefixes = ' -webkit- -moz- -o- -ms- '.split(' '),
121
122      omPrefixes = 'Webkit Moz O ms',
123
124      cssomPrefixes = omPrefixes.split(' '),
125
126      domPrefixes = omPrefixes.toLowerCase().split(' '),
127
128      tests = {},
129      inputs = {},
130      attrs = {},
131
132      classes = [],
133
134      slice = classes.slice,
135
136      featureName,
137
138      injectElementWithStyles = function (rule, callback, nodes, testnames) {
139
140        var style, ret, node, docOverflow,
141            div = document.createElement('div'),
142            body = document.body,
143            fakeBody = body || document.createElement('body');
144
145        if (parseInt(nodes, 10)) {
146          while (nodes--) {
147            node = document.createElement('div');
148            node.id = testnames ? testnames[nodes] : mod + (nodes + 1);
149            div.appendChild(node);
150          }
151        }
152
153        style = ['&#173;', '<style id="s', mod, '">', rule, '</style>'].join('');
154        div.id = mod;
155        (body ? div : fakeBody).innerHTML += style;
156        fakeBody.appendChild(div);
157        if (!body) {
158          fakeBody.style.background = '';
159          fakeBody.style.overflow = 'hidden';
160          docOverflow = docElement.style.overflow;
161          docElement.style.overflow = 'hidden';
162          docElement.appendChild(fakeBody);
163        }
164
165        ret = callback(div, rule);
166        if (!body) {
167          fakeBody.parentNode.removeChild(fakeBody);
168          docElement.style.overflow = docOverflow;
169        } else {
170          div.parentNode.removeChild(div);
171        }
172
173        return !!ret;
174
175      },
176      _hasOwnProperty = ({}).hasOwnProperty, hasOwnProp;
177
178  if (!is(_hasOwnProperty, 'undefined') && !is(_hasOwnProperty.call, 'undefined')) {
179    hasOwnProp = function (object, property) {
180      return _hasOwnProperty.call(object, property);
181    };
182  }
183  else {
184    hasOwnProp = function (object, property) {
185      return ((property in object) && is(object.constructor.prototype[property], 'undefined'));
186    };
187  }
188
189
190  if (!Function.prototype.bind) {
191    Function.prototype.bind = function bind (that) {
192
193      var target = this;
194
195      if (typeof target != "function") {
196        throw new TypeError();
197      }
198
199      var args = slice.call(arguments, 1),
200          bound = function () {
201
202            if (this instanceof bound) {
203
204              var F = function () {
205              };
206              F.prototype = target.prototype;
207              var self = new F();
208
209              var result = target.apply(
210                  self,
211                  args.concat(slice.call(arguments))
212              );
213              if (Object(result) === result) {
214                return result;
215              }
216              return self;
217
218            } else {
219
220              return target.apply(
221                  that,
222                  args.concat(slice.call(arguments))
223              );
224
225            }
226
227          };
228
229      return bound;
230    };
231  }
232
233  function setCss (str) {
234    mStyle.cssText = str;
235  }
236
237  function setCssAll (str1, str2) {
238    return setCss(prefixes.join(str1 + ';') + ( str2 || '' ));
239  }
240
241  function is (obj, type) {
242    return typeof obj === type;
243  }
244
245  function contains (str, substr) {
246    return !!~('' + str).indexOf(substr);
247  }
248
249  function testProps (props, prefixed) {
250    for (var i in props) {
251      var prop = props[i];
252      if (!contains(prop, "-") && mStyle[prop] !== undefined) {
253        return prefixed == 'pfx' ? prop : true;
254      }
255    }
256    return false;
257  }
258
259  function testDOMProps (props, obj, elem) {
260    for (var i in props) {
261      var item = obj[props[i]];
262      if (item !== undefined) {
263
264        if (elem === false) return props[i];
265
266        if (is(item, 'function')) {
267          return item.bind(elem || obj);
268        }
269
270        return item;
271      }
272    }
273    return false;
274  }
275
276  function testPropsAll (prop, prefixed, elem) {
277
278    var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1),
279        props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');
280
281    if (is(prefixed, "string") || is(prefixed, "undefined")) {
282      return testProps(props, prefixed);
283
284    } else {
285      props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');
286      return testDOMProps(props, prefixed, elem);
287    }
288  }
289
290  tests['csstransforms3d'] = function () {
291
292    var ret = !!testPropsAll('perspective');
293
294// Chrome fails that test, ignore
295//		if (ret && 'webkitPerspective' in docElement.style) {
296//
297//			injectElementWithStyles('@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}', function (node, rule) {
298//				ret = node.offsetLeft === 9 && node.offsetHeight === 3;
299//			});
300//		}
301    return ret;
302  };
303
304  for (var feature in tests) {
305    if (hasOwnProp(tests, feature)) {
306      featureName = feature.toLowerCase();
307      Modernizr[featureName] = tests[feature]();
308
309      classes.push((Modernizr[featureName] ? '' : 'no-') + featureName);
310    }
311  }
312
313  Modernizr.addTest = function (feature, test) {
314    if (typeof feature == 'object') {
315      for (var key in feature) {
316        if (hasOwnProp(feature, key)) {
317          Modernizr.addTest(key, feature[ key ]);
318        }
319      }
320    } else {
321
322      feature = feature.toLowerCase();
323
324      if (Modernizr[feature] !== undefined) {
325        return Modernizr;
326      }
327
328      test = typeof test == 'function' ? test() : test;
329
330      if (typeof enableClasses !== "undefined" && enableClasses) {
331        docElement.className += ' ' + (test ? '' : 'no-') + feature;
332      }
333      Modernizr[feature] = test;
334
335    }
336
337    return Modernizr;
338  };
339
340
341  setCss('');
342  modElem = inputElem = null;
343
344
345  Modernizr._version = version;
346
347  Modernizr._prefixes = prefixes;
348  Modernizr._domPrefixes = domPrefixes;
349  Modernizr._cssomPrefixes = cssomPrefixes;
350
351  Modernizr.testProp = function (prop) {
352    return testProps([prop]);
353  };
354
355  Modernizr.testAllProps = testPropsAll;
356
357  Modernizr.testStyles = injectElementWithStyles;
358  Modernizr.prefixed = function (prop, obj, elem) {
359    if (!obj) {
360      return testPropsAll(prop, 'pfx');
361    } else {
362      return testPropsAll(prop, obj, elem);
363    }
364  };
365
366  return Modernizr;
367})(window, document);
368var fullScreenApi = {
369      ok: false,
370      is: function () {
371        return false;
372      },
373      request: function () {
374      },
375      cancel: function () {
376      },
377      event: '',
378      prefix: ''
379    },
380    browserPrefixes = 'webkit moz o ms khtml'.split(' ');
381
382// check for native support
383if (typeof document.cancelFullScreen != 'undefined') {
384  fullScreenApi.ok = true;
385} else {
386  // check for fullscreen support by vendor prefix
387  for (var i = 0, il = browserPrefixes.length; i < il; i++) {
388    fullScreenApi.prefix = browserPrefixes[i];
389    if (typeof document[fullScreenApi.prefix + 'CancelFullScreen' ] != 'undefined') {
390      fullScreenApi.ok = true;
391      break;
392    }
393  }
394}
395
396// update methods to do something useful
397if (fullScreenApi.ok) {
398  fullScreenApi.event = fullScreenApi.prefix + 'fullscreenchange';
399  fullScreenApi.is = function () {
400    switch (this.prefix) {
401      case '':
402        return document.fullScreen;
403      case 'webkit':
404        return document.webkitIsFullScreen;
405      default:
406        return document[this.prefix + 'FullScreen'];
407    }
408  };
409  fullScreenApi.request = function (el) {
410    return (this.prefix === '') ? el.requestFullScreen() : el[this.prefix + 'RequestFullScreen']();
411  };
412  fullScreenApi.cancel = function (el) {
413    return (this.prefix === '') ? document.cancelFullScreen() : document[this.prefix + 'CancelFullScreen']();
414  };
415}
416//fgnass.github.com/spin.js#v1.3.2
417
418/**
419 * Copyright (c) 2011-2013 Felix Gnass
420 * Licensed under the MIT license
421 */
422
423var Spinner,
424    spinnerDefaults = {
425      lines: 12, // The number of lines to draw
426      length: 5, // The length of each line
427      width: 2, // The line thickness
428      radius: 7, // The radius of the inner circle
429      corners: 1, // Corner roundness (0..1)
430      rotate: 15, // The rotation offset
431      color: 'rgba(128, 128, 128, .75)',
432      hwaccel: true
433    },
434    spinnerOverride = {
435      top: 'auto',
436      left: 'auto',
437      className: ''
438    };
439
440(function(root, factory) {
441
442  /* CommonJS */
443  //if (typeof exports == 'object')  module.exports = factory()
444
445  /* AMD module */
446  //else if (typeof define == 'function' && define.amd) define(factory)
447
448  /* Browser global */
449  //else root.Spinner = factory()
450
451  Spinner = factory();
452}
453(this, function() {
454  "use strict";
455
456  var prefixes = ['webkit', 'Moz', 'ms', 'O'] /* Vendor prefixes */
457    , animations = {} /* Animation rules keyed by their name */
458    , useCssAnimations /* Whether to use CSS animations or setTimeout */
459
460  /**
461   * Utility function to create elements. If no tag name is given,
462   * a DIV is created. Optionally properties can be passed.
463   */
464  function createEl(tag, prop) {
465    var el = document.createElement(tag || 'div')
466      , n
467
468    for(n in prop) el[n] = prop[n]
469    return el
470  }
471
472  /**
473   * Appends children and returns the parent.
474   */
475  function ins(parent /* child1, child2, ...*/) {
476    for (var i=1, n=arguments.length; i<n; i++)
477      parent.appendChild(arguments[i])
478
479    return parent
480  }
481
482  /**
483   * Insert a new stylesheet to hold the @keyframe or VML rules.
484   */
485  var sheet = (function() {
486    var el = createEl('style', {type : 'text/css'})
487    ins(document.getElementsByTagName('head')[0], el)
488    return el.sheet || el.styleSheet
489  }())
490
491  /**
492   * Creates an opacity keyframe animation rule and returns its name.
493   * Since most mobile Webkits have timing issues with animation-delay,
494   * we create separate rules for each line/segment.
495   */
496  function addAnimation(alpha, trail, i, lines) {
497    var name = ['opacity', trail, ~~(alpha*100), i, lines].join('-')
498      , start = 0.01 + i/lines * 100
499      , z = Math.max(1 - (1-alpha) / trail * (100-start), alpha)
500      , prefix = useCssAnimations.substring(0, useCssAnimations.indexOf('Animation')).toLowerCase()
501      , pre = prefix && '-' + prefix + '-' || ''
502
503    if (!animations[name]) {
504      sheet.insertRule(
505        '@' + pre + 'keyframes ' + name + '{' +
506        '0%{opacity:' + z + '}' +
507        start + '%{opacity:' + alpha + '}' +
508        (start+0.01) + '%{opacity:1}' +
509        (start+trail) % 100 + '%{opacity:' + alpha + '}' +
510        '100%{opacity:' + z + '}' +
511        '}', sheet.cssRules.length)
512
513      animations[name] = 1
514    }
515
516    return name
517  }
518
519  /**
520   * Tries various vendor prefixes and returns the first supported property.
521   */
522  function vendor(el, prop) {
523    var s = el.style
524      , pp
525      , i
526
527    prop = prop.charAt(0).toUpperCase() + prop.slice(1)
528    for(i=0; i<prefixes.length; i++) {
529      pp = prefixes[i]+prop
530      if(s[pp] !== undefined) return pp
531    }
532    if(s[prop] !== undefined) return prop
533  }
534
535  /**
536   * Sets multiple style properties at once.
537   */
538  function css(el, prop) {
539    for (var n in prop)
540      el.style[vendor(el, n)||n] = prop[n]
541
542    return el
543  }
544
545  /**
546   * Fills in default values.
547   */
548  function merge(obj) {
549    for (var i=1; i < arguments.length; i++) {
550      var def = arguments[i]
551      for (var n in def)
552        if (obj[n] === undefined) obj[n] = def[n]
553    }
554    return obj
555  }
556
557  /**
558   * Returns the absolute page-offset of the given element.
559   */
560  function pos(el) {
561    var o = { x:el.offsetLeft, y:el.offsetTop }
562    while((el = el.offsetParent))
563      o.x+=el.offsetLeft, o.y+=el.offsetTop
564
565    return o
566  }
567
568  /**
569   * Returns the line color from the given string or array.
570   */
571  function getColor(color, idx) {
572    return typeof color == 'string' ? color : color[idx % color.length]
573  }
574
575  // Built-in defaults
576
577  var defaults = {
578    lines: 12,            // The number of lines to draw
579    length: 7,            // The length of each line
580    width: 5,             // The line thickness
581    radius: 10,           // The radius of the inner circle
582    rotate: 0,            // Rotation offset
583    corners: 1,           // Roundness (0..1)
584    color: '#000',        // #rgb or #rrggbb
585    direction: 1,         // 1: clockwise, -1: counterclockwise
586    speed: 1,             // Rounds per second
587    trail: 100,           // Afterglow percentage
588    opacity: 1/4,         // Opacity of the lines
589    fps: 20,              // Frames per second when using setTimeout()
590    zIndex: 2e9,          // Use a high z-index by default
591    className: 'spinner', // CSS class to assign to the element
592    top: 'auto',          // center vertically
593    left: 'auto',         // center horizontally
594    position: 'relative'  // element position
595  }
596
597  /** The constructor */
598  function Spinner(o) {
599    if (typeof this == 'undefined') return new Spinner(o)
600    this.opts = merge(o || {}, Spinner.defaults, defaults)
601  }
602
603  // Global defaults that override the built-ins:
604  Spinner.defaults = {}
605
606  merge(Spinner.prototype, {
607
608    /**
609     * Adds the spinner to the given target element. If this instance is already
610     * spinning, it is automatically removed from its previous target b calling
611     * stop() internally.
612     */
613    spin: function(target) {
614      this.stop()
615
616      var self = this
617        , o = self.opts
618        , el = self.el = css(createEl(0, {className: o.className}), {position: o.position, width: 0, zIndex: o.zIndex})
619        , mid = o.radius+o.length+o.width
620        , ep // element position
621        , tp // target position
622
623      if (target) {
624        target.insertBefore(el, target.firstChild||null)
625        tp = pos(target)
626        ep = pos(el)
627        css(el, {
628          left: (o.left == 'auto' ? tp.x-ep.x + (target.offsetWidth >> 1) : parseInt(o.left, 10) + mid) + 'px',
629          top: (o.top == 'auto' ? tp.y-ep.y + (target.offsetHeight >> 1) : parseInt(o.top, 10) + mid)  + 'px'
630        })
631      }
632
633      el.setAttribute('role', 'progressbar')
634      self.lines(el, self.opts)
635
636      if (!useCssAnimations) {
637        // No CSS animation support, use setTimeout() instead
638        var i = 0
639          , start = (o.lines - 1) * (1 - o.direction) / 2
640          , alpha
641          , fps = o.fps
642          , f = fps/o.speed
643          , ostep = (1-o.opacity) / (f*o.trail / 100)
644          , astep = f/o.lines
645
646        ;(function anim() {
647          i++;
648          for (var j = 0; j < o.lines; j++) {
649            alpha = Math.max(1 - (i + (o.lines - j) * astep) % f * ostep, o.opacity)
650
651            self.opacity(el, j * o.direction + start, alpha, o)
652          }
653          self.timeout = self.el && setTimeout(anim, ~~(1000/fps))
654        })()
655      }
656      return self
657    },
658
659    /**
660     * Stops and removes the Spinner.
661     */
662    stop: function() {
663      var el = this.el
664      if (el) {
665        clearTimeout(this.timeout)
666        if (el.parentNode) el.parentNode.removeChild(el)
667        this.el = undefined
668      }
669      return this
670    },
671
672    /**
673     * Internal method that draws the individual lines. Will be overwritten
674     * in VML fallback mode below.
675     */
676    lines: function(el, o) {
677      var i = 0
678        , start = (o.lines - 1) * (1 - o.direction) / 2
679        , seg
680
681      function fill(color, shadow) {
682        return css(createEl(), {
683          position: 'absolute',
684          width: (o.length+o.width) + 'px',
685          height: o.width + 'px',
686          background: color,
687          boxShadow: shadow,
688          transformOrigin: 'left',
689          transform: 'rotate(' + ~~(360/o.lines*i+o.rotate) + 'deg) translate(' + o.radius+'px' +',0)',
690          borderRadius: (o.corners * o.width>>1) + 'px'
691        })
692      }
693
694      for (; i < o.lines; i++) {
695        seg = css(createEl(), {
696          position: 'absolute',
697          top: 1+~(o.width/2) + 'px',
698          transform: o.hwaccel ? 'translate3d(0,0,0)' : '',
699          opacity: o.opacity,
700          animation: useCssAnimations && addAnimation(o.opacity, o.trail, start + i * o.direction, o.lines) + ' ' + 1/o.speed + 's linear infinite'
701        })
702
703        if (o.shadow) ins(seg, css(fill('#000', '0 0 4px ' + '#000'), {top: 2+'px'}))
704        ins(el, ins(seg, fill(getColor(o.color, i), '0 0 1px rgba(0,0,0,.1)')))
705      }
706      return el
707    },
708
709    /**
710     * Internal method that adjusts the opacity of a single line.
711     * Will be overwritten in VML fallback mode below.
712     */
713    opacity: function(el, i, val) {
714      if (i < el.childNodes.length) el.childNodes[i].style.opacity = val
715    }
716
717  })
718
719
720  function initVML() {
721
722    /* Utility function to create a VML tag */
723    function vml(tag, attr) {
724      return createEl('<' + tag + ' xmlns="urn:schemas-microsoft.com:vml" class="spin-vml">', attr)
725    }
726
727    // No CSS transforms but VML support, add a CSS rule for VML elements:
728    sheet.addRule('.spin-vml', 'behavior:url(#default#VML)')
729
730    Spinner.prototype.lines = function(el, o) {
731      var r = o.length+o.width
732        , s = 2*r
733
734      function grp() {
735        return css(
736          vml('group', {
737            coordsize: s + ' ' + s,
738            coordorigin: -r + ' ' + -r
739          }),
740          { width: s, height: s }
741        )
742      }
743
744      var margin = -(o.width+o.length)*2 + 'px'
745        , g = css(grp(), {position: 'absolute', top: margin, left: margin})
746        , i
747
748      function seg(i, dx, filter) {
749        ins(g,
750          ins(css(grp(), {rotation: 360 / o.lines * i + 'deg', left: ~~dx}),
751            ins(css(vml('roundrect', {arcsize: o.corners}), {
752                width: r,
753                height: o.width,
754                left: o.radius,
755                top: -o.width>>1,
756                filter: filter
757              }),
758              vml('fill', {color: getColor(o.color, i), opacity: o.opacity}),
759              vml('stroke', {opacity: 0}) // transparent stroke to fix color bleeding upon opacity change
760            )
761          )
762        )
763      }
764
765      if (o.shadow)
766        for (i = 1; i <= o.lines; i++)
767          seg(i, -2, 'progid:DXImageTransform.Microsoft.Blur(pixelradius=2,makeshadow=1,shadowopacity=.3)')
768
769      for (i = 1; i <= o.lines; i++) seg(i)
770      return ins(el, g)
771    }
772
773    Spinner.prototype.opacity = function(el, i, val, o) {
774      var c = el.firstChild
775      o = o.shadow && o.lines || 0
776      if (c && i+o < c.childNodes.length) {
777        c = c.childNodes[i+o]; c = c && c.firstChild; c = c && c.firstChild
778        if (c) c.opacity = val
779      }
780    }
781  }
782
783  var probe = css(createEl('group'), {behavior: 'url(#default#VML)'})
784
785  if (!vendor(probe, 'transform') && probe.adj) initVML()
786  else useCssAnimations = vendor(probe, 'animation')
787
788  return Spinner
789
790}));
791
792/* Bez v1.0.10-g5ae0136
793 * http://github.com/rdallasgray/bez
794 *
795 * A plugin to convert CSS3 cubic-bezier co-ordinates to jQuery-compatible easing functions
796 *
797 * With thanks to Nikolay Nemshilov for clarification on the cubic-bezier maths
798 * See http://st-on-it.blogspot.com/2011/05/calculating-cubic-bezier-function.html
799 *
800 * Copyright 2011 Robert Dallas Gray. All rights reserved.
801 * Provided under the FreeBSD license: https://github.com/rdallasgray/bez/blob/master/LICENSE.txt
802 */
803function bez (coOrdArray) {
804  var encodedFuncName = "bez_" + $.makeArray(arguments).join("_").replace(".", "p");
805  if (typeof $['easing'][encodedFuncName] !== "function") {
806    var polyBez = function (p1, p2) {
807      var A = [null, null],
808          B = [null, null],
809          C = [null, null],
810          bezCoOrd = function (t, ax) {
811            C[ax] = 3 * p1[ax];
812            B[ax] = 3 * (p2[ax] - p1[ax]) - C[ax];
813            A[ax] = 1 - C[ax] - B[ax];
814            return t * (C[ax] + t * (B[ax] + t * A[ax]));
815          },
816          xDeriv = function (t) {
817            return C[0] + t * (2 * B[0] + 3 * A[0] * t);
818          },
819          xForT = function (t) {
820            var x = t, i = 0, z;
821            while (++i < 14) {
822              z = bezCoOrd(x, 0) - t;
823              if (Math.abs(z) < 1e-3) break;
824              x -= z / xDeriv(x);
825            }
826            return x;
827          };
828      return function (t) {
829        return bezCoOrd(xForT(t), 1);
830      }
831    };
832    $['easing'][encodedFuncName] = function (x, t, b, c, d) {
833      return c * polyBez([coOrdArray[0], coOrdArray[1]], [coOrdArray[2], coOrdArray[3]])(t / d) + b;
834    }
835  }
836  return encodedFuncName;
837}
838var $WINDOW = $(window),
839    $DOCUMENT = $(document),
840    $HTML,
841    $BODY,
842
843    QUIRKS_FORCE = location.hash.replace('#', '') === 'quirks',
844    TRANSFORMS3D = Modernizr.csstransforms3d,
845    CSS3 = TRANSFORMS3D && !QUIRKS_FORCE,
846    COMPAT = TRANSFORMS3D || document.compatMode === 'CSS1Compat',
847    FULLSCREEN = fullScreenApi.ok,
848
849    MOBILE = navigator.userAgent.match(/Android|webOS|iPhone|iPad|iPod|BlackBerry|W
849indows Phone/i),
850    SLOW = !CSS3 || MOBILE,
851
852    MS_POINTER = navigator.msPointerEnabled,
853
854    WHEEL = "onwheel" in document.createElement("div") ? "wheel" : document.onmousewheel !== undefined ? "mousewheel" : "DOMMouseScroll",
855
856    TOUCH_TIMEOUT = 250,
857    TRANSITION_DURATION = 300,
858
859    SCROLL_LOCK_TIMEOUT = 1400,
860
861    AUTOPLAY_INTERVAL = 5000,
862    MARGIN = 2,
863    THUMB_SIZE = 64,
864
865    WIDTH = 500,
866    HEIGHT = 333,
867
868    STAGE_FRAME_KEY = '$stageFrame',
869    NAV_DOT_FRAME_KEY = '$navDotFrame',
870    NAV_THUMB_FRAME_KEY = '$navThumbFrame',
871
872    AUTO = 'auto',
873
874    BEZIER = bez([.1, 0, .25, 1]),
875
876    MAX_WIDTH = 99999,
877
878    FIFTYFIFTY = '50%',
879
880    OPTIONS = {
881      // dimensions
882      width: null, // 500 || '100%'
883      minwidth: null,
884      maxwidth: '100%', // '100%'
885      height: null,
886      minheight: null,
887      maxheight: null,
888
889      ratio: null, // '16/9' || 500/333 || 1.5
890
891      margin: MARGIN,
892      glimpse: 0,
893
894      fit: 'contain', // 'cover' || 'scaledown' || 'none'
895
896      position: FIFTYFIFTY,
897      thumbposition: FIFTYFIFTY,
898
899      // navigation, thumbs
900      nav: 'dots', // 'thumbs' || false
901      navposition: 'bottom', // 'top'
902      navwidth: null,
903      thumbwidth: THUMB_SIZE,
904      thumbheight: THUMB_SIZE,
905      thumbmargin: MARGIN,
906      thumbborderwidth: MARGIN,
907      thumbfit: 'cover', // 'contain' || 'scaledown' || 'none'
908
909      allowfullscreen: false, // true || 'native'
910
911      transition: 'slide', // 'crossfade' || 'dissolve'
912      clicktransition: null,
913      transitionduration: TRANSITION_DURATION,
914
915      captions: true,
916
917      hash: false,
918      startindex: 0,
919
920      loop: false,
921
922      autoplay: false,
923      stopautoplayontouch: true,
924
925      keyboard: false,
926
927      arrows: true,
928      click: true,
929      swipe: true,
930      trackpad: false,
931
932      enableifsingleframe: false,
933
934      controlsonstart: true,
935
936      shuffle: false,
937
938      direction: 'ltr', // 'rtl'
939
940      shadows: true,
941      spinner: null
942    },
943
944    KEYBOARD_OPTIONS = {
945      left: true,
946      right: true,
947      down: false,
948      up: false,
949      space: false,
950      home: false,
951      end: false
952    };
953function noop () {}
954
955function minMaxLimit (value, min, max) {
956  return Math.max(isNaN(min) ? -Infinity : min, Math.min(isNaN(max) ? Infinity : max, value));
957}
958
959function readTransform (css) {
960  return css.match(/ma/) && css.match(/-?\d+(?!d)/g)[css.match(/3d/) ? 12 : 4];
961}
962
963function readPosition ($el) {
964  if (CSS3) {
965    return +readTransform($el.css('transform'));
966  } else {
967    return +$el.css('left').replace('px', '');
968  }
969}
970
971function getTranslate (pos/*, _001*/) {
972  var obj = {};
973  if (CSS3) {
974    obj.transform = 'translate3d(' + (pos/* + (_001 ? 0.001 : 0)*/) + 'px,0,0)'; // 0.001 to remove Retina artifacts
975  } else {
976    obj.left = pos;
977  }
978  return obj;
979}
980
981function getDuration (time) {
982  return {'transition-duration': time + 'ms'};
983}
984
985function unlessNaN (value, alternative) {
986  return isNaN(value) ? alternative : value;
987}
988
989function numberFromMeasure (value, measure) {
990  return unlessNaN(+String(value).replace(measure || 'px', ''));
991}
992
993function numberFromPercent (value) {
994  return /%$/.test(value) ? numberFromMeasure(value, '%') : undefined;
995}
996
997function numberFromWhatever (value, whole) {
998  return unlessNaN(numberFromPercent(value) / 100 * whole, numberFromMeasure(value));
999}
1000
1001function measureIsValid (value) {
1002  return (!isNaN(numberFromMeasure(value)) || !isNaN(numberFromMeasure(value, '%'))) && value;
1003}
1004
1005function getPosByIndex (index, side, margin, baseIndex) {
1006  ////console.log('getPosByIndex', index, side, margin, baseIndex);
1007  ////console.log((index - (baseIndex || 0)) * (side + (margin || 0)));
1008
1009  return (index - (baseIndex || 0)) * (side + (margin || 0));
1010}
1011
1012function getIndexByPos (pos, side, margin, baseIndex) {
1013  return -Math.round(pos / (side + (margin || 0)) - (baseIndex || 0));
1014}
1015
1016function bindTransitionEnd ($el) {
1017  var elData = $el.data();
1018
1019  if (elData.tEnd) return;
1020
1021  var el = $el[0],
1022      transitionEndEvent = {
1023        WebkitTransition: 'webkitTransitionEnd',
1024        MozTransition: 'transitionend',
1025        OTransition: 'oTransitionEnd otransitionend',
1026        msTransition: 'MSTransitionEnd',
1027        transition: 'transitionend'
1028      };
1029  addEvent(el, transitionEndEvent[Modernizr.prefixed('transition')], function (e) {
1030    elData.tProp && e.propertyName.match(elData.tProp) && elData.onEndFn();
1031  });
1032  elData.tEnd = true;
1033}
1034
1035function afterTransition ($el, property, fn, time) {
1036  var ok,
1037      elData = $el.data();
1038
1039  if (elData) {
1040    elData.onEndFn = function () {
1041      if (ok) return;
1042      ok = true;
1043      clearTimeout(elData.tT);
1044      fn();
1045    };
1046    elData.tProp = property;
1047
1048    // Passive call, just in case of fail of native transition-end event
1049    clearTimeout(elData.tT);
1050    elData.tT = setTimeout(function () {
1051      elData.onEndFn();
1052    }, time * 1.5);
1053
1054    bindTransitionEnd($el);
1055  }
1056}
1057
1058
1059function stop ($el, left/*, _001*/) {
1060  if ($el.length) {
1061    var elData = $el.data();
1062    if (CSS3) {
1063      $el.css(getDuration(0));
1064      elData.onEndFn = noop;
1065      clearTimeout(elData.tT);
1066    } else {
1067      $el.stop();
1068    }
1069    var lockedLeft = getNumber(left, function () {
1070      return readPosition($el);
1071    });
1072
1073    $el.css(getTranslate(lockedLeft/*, _001*/));//.width(); // `.width()` for reflow
1074    return lockedLeft;
1075  }
1076}
1077
1078function getNumber () {
1079  var number;
1080  for (var _i = 0, _l = arguments.length; _i < _l; _i++) {
1081    number = _i ? arguments[_i]() : arguments[_i];
1082    if (typeof number === 'number') {
1083      break;
1084    }
1085  }
1086
1087  return number;
1088}
1089
1090function edgeResistance (pos, edge) {
1091  return Math.round(pos + ((edge - pos) / 1.5));
1092}
1093
1094function getProtocol () {
1095  getProtocol.p = getProtocol.p || (location.protocol === 'https:' ? 'https://' : 'http://');
1096  return getProtocol.p;
1097}
1098
1099function parseHref (href) {
1100  var a = document.createElement('a');
1101  a.href = href;
1102  return a;
1103}
1104
1105function findVideoId (href, forceVideo) {
1106  if (typeof href !== 'string') return href;
1107  href = parseHref(href);
1108
1109  var id,
1110      type;
1111
1112  if (href.host.match(/youtube\.com/) && href.search) {
1113    //.log();
1114    id = href.search.split('v=')[1];
1115    if (id) {
1116      var ampersandPosition = id.indexOf('&');
1117      if (ampersandPosition !== -1) {
1118        id = id.substring(0, ampersandPosition);
1119      }
1120      type = 'youtube';
1121    }
1122  } else if (href.host.match(/youtube\.com|youtu\.be/)) {
1123    id = href.pathname.replace(/^\/(embed\/|v\/)?/, '').replace(/\/.*/, '');
1124    type = 'youtube';
1125  } else if (href.host.match(/vimeo\.com/)) {
1126    type = 'vimeo';
1127    id = href.pathname.replace(/^\/(video\/)?/, '').replace(/\/.*/, '');
1128  }
1129
1130  if ((!id || !type) && forceVideo) {
1131    id = href.href;
1132    type = 'custom';
1133  }
1134
1135  return id ? {id: id, type: type, s: href.search.replace(/^\?/, ''), p: getProtocol()} : false;
1136}
1137
1138function getVideoThumbs (dataFrame, data, fotorama) {
1139  var img, thumb, video = dataFrame.video;
1140  if (video.type === 'youtube') {
1141    thumb = getProtocol() + 'img.youtube.com/vi/' + video.id + '/default.jpg';
1142    img = thumb.replace(/\/default.jpg$/, '/hqdefault.jpg');
1143    dataFrame.thumbsReady = true;
1144  } else if (video.type === 'vimeo') {
1145    $.ajax({
1146      url: getProtocol() + 'vimeo.com/api/v2/video/' + video.id + '.json',
1147      dataType: 'jsonp',
1148      success: function (json) {
1149        dataFrame.thumbsReady = true;
1150        updateData(data, {img: json[0].thumbnail_large, thumb: json[0].thumbnail_small}, dataFrame.i, fotorama);
1151      }
1152    });
1153  } else {
1154    dataFrame.thumbsReady = true;
1155  }
1156
1157  return {
1158    img: img,
1159    thumb: thumb
1160  }
1161}
1162
1163function updateData (data, _dataFrame, i, fotorama) {
1164  for (var _i = 0, _l = data.length; _i < _l; _i++) {
1165    var dataFrame = data[_i];
1166
1167    if (dataFrame.i === i && dataFrame.thumbsReady) {
1168      var clear = {videoReady: true};
1169      clear[STAGE_FRAME_KEY] = clear[NAV_THUMB_FRAME_KEY] = clear[NAV_DOT_FRAME_KEY] = false;
1170
1171      fotorama.splice(_i, 1, $.extend(
1172          {},
1173          dataFrame,
1174          clear,
1175          _dataFrame
1176      ));
1177
1178      break;
1179    }
1180  }
1181}
1182
1183function getDataFromHtml ($el) {
1184  var data = [];
1185
1186  function getDataFromImg ($img, imgData, checkVideo) {
1187    var $child = $img.children('img').eq(0),
1188        _imgHref = $img.attr('href'),
1189        _imgSrc = $img.attr('src'),
1190        _thumbSrc = $child.attr('src'),
1191        _video = imgData.video,
1192        video = checkVideo ? findVideoId(_imgHref, _video === true) : false;
1193
1194    if (video) {
1195      _imgHref = false;
1196    } else {
1197      video = _video;
1198    }
1199
1200    getDimensions($img, $child, $.extend(imgData, {
1201      video: video,
1202      img: imgData.img || _imgHref || _imgSrc || _thumbSrc,
1203      thumb: imgData.thumb || _thumbSrc || _imgSrc || _imgHref
1204    }));
1205  }
1206
1207  function getDimensions ($img, $child, imgData) {
1208    var separateThumbFLAG = imgData.thumb && imgData.img !== imgData.thumb,
1209    width  = numberFromMeasure(imgData.width || $img.attr('width')),
1210    height = numberFromMeasure(imgData.height || $img.attr('height')),
1211    alt    = $child.attr('alt'),
1212    title  = $child.attr('title');
1213
1214    if (alt)   { $.extend(imgData, { alt: alt }); }
1215    if (title) { $.extend(imgData, { title: title }); }
1216
1217    $.extend(imgData, {
1218      width: width,
1219      height: height,
1220      thumbratio: getRatio(imgData.thumbratio || (numberFromMeasure(imgData.thumbwidth || ($child && $child.attr('width')) || separateThumbFLAG || width) / numberFromMeasure(imgData.thumbheight || ($child && $child.attr('height')) || separateThumbFLAG || height)))
1221    });
1222  }
1223
1224  $el.children().each(function () {
1225    var $this = $(this),
1226        dataFrame = optionsToLowerCase($.extend($this.data(), {id: $this.attr('id')}));
1227    if ($this.is('a, img')) {
1228      getDataFromImg($this, dataFrame, true);
1229    } else if (!$this.is(':empty')) {
1230      getDimensions($this, null, $.extend(dataFrame, {
1231        html: this,
1232        _html: $this.html() // Because of IE
1233      }));
1234    } else return;
1235
1236    data.push(dataFrame);
1237  });
1238
1239  return data;
1240}
1241
1242function isHidden (el) {
1243  return el.offsetWidth === 0 && el.offsetHeight === 0;
1244}
1245
1246function isDetached (el) {
1247  return !$.contains(document.documentElement, el);
1248}
1249
1250function waitFor (test, fn, timeout, i) {
1251  if (!waitFor.i) {
1252    waitFor.i = 1;
1253    waitFor.ii = [true];
1254  }
1255
1256  i = i || waitFor.i;
1257
1258  if (typeof waitFor.ii[i] === 'undefined') {
1259    waitFor.ii[i] = true;
1260  }
1261
1262  if (test()) {
1263    fn();
1264  } else {
1265    waitFor.ii[i] && setTimeout(function () {
1266      waitFor.ii[i] && waitFor(test, fn, timeout, i);
1267    }, timeout || 100);
1268  }
1269
1270  return waitFor.i++;
1271}
1272
1273waitFor.stop = function (i) {
1274  waitFor.ii[i] = false;
1275};
1276
1277function setHash (hash) {
1278  //////console.time('setHash ' + hash);
1279  location.replace(location.protocol
1280      + '//'
1281      + location.host
1282      + location.pathname.replace(/^\/?/, '/')
1283      + location.search
1284      + '#' + hash);
1285  //////console.timeEnd('setHash ' + hash);
1286}
1287
1288function fit ($el, measuresToFit, method, position) {
1289  var elData = $el.data(),
1290      measures = elData.measures;
1291
1292  if (measures && (!elData.l ||
1293      elData.l.W !== measures.width ||
1294      elData.l.H !== measures.height ||
1295      elData.l.r !== measures.ratio ||
1296      elData.l.w !== measuresToFit.w ||
1297      elData.l.h !== measuresToFit.h ||
1298      elData.l.m !== method ||
1299      elData.l.p !== position)) {
1300
1301    //console.log('fit');
1302
1303    var width = measures.width,
1304        height = measures.height,
1305        ratio = measuresToFit.w / measuresToFit.h,
1306        biggerRatioFLAG = measures.ratio >= ratio,
1307        fitFLAG = method === 'scaledown',
1308        containFLAG = method === 'contain',
1309        coverFLAG = method === 'cover',
1310        pos = parsePosition(position);
1311
1312    if (biggerRatioFLAG && (fitFLAG || containFLAG) || !biggerRatioFLAG && coverFLAG) {
1313      width = minMaxLimit(measuresToFit.w, 0, fitFLAG ? width : Infinity);
1314      height = width / measures.ratio;
1315    } else if (biggerRatioFLAG && coverFLAG || !biggerRatioFLAG && (fitFLAG || containFLAG)) {
1316      height = minMaxLimit(measuresToFit.h, 0, fitFLAG ? height : Infinity);
1317      width = height * measures.ratio;
1318    }
1319
1320    $el.css({
1321      width: width,
1322      height: height,
1323      left: numberFromWhatever(pos.x, measuresToFit.w - width),
1324      top: numberFromWhatever(pos.y, measuresToFit.h- height)
1325    });
1326
1327    elData.l = {
1328      W: measures.width,
1329      H: measures.height,
1330      r: measures.ratio,
1331      w: measuresToFit.w,
1332      h: measuresToFit.h,
1333      m: method,
1334      p: position
1335    };
1336  }
1337
1338  return true;
1339}
1340
1341function setStyle ($el, style) {
1342  var el = $el[0];
1343  if (el.styleSheet) {
1344    el.styleSheet.cssText = style;
1345  } else {
1346    $el.html(style);
1347  }
1348}
1349
1350function findShadowEdge (pos, min, max) {
1351  return min === max ? false : pos <= min ? 'left' : pos >= max ? 'right' : 'left right';
1352}
1353
1354function getIndexFromHash (hash, data, ok, startindex) {
1355  if (!ok) return false;
1356  if (!isNaN(hash)) return hash - (startindex ? 0 : 1);
1357
1358  var index;
1359
1360  for (var _i = 0, _l = data.length; _i < _l; _i++) {
1361    var dataFrame = data[_i];
1362
1363    if (dataFrame.id === hash) {
1364      index = _i;
1365      break;
1366    }
1367  }
1368
1369  return index;
1370}
1371
1372function smartClick ($el, fn, _options) {
1373  _options = _options || {};
1374
1375  $el.each(function () {
1376    var $this = $(this),
1377        thisData = $this.data(),
1378        startEvent;
1379
1380    if (thisData.clickOn) return;
1381
1382    thisData.clickOn = true;
1383
1384    $.extend(touch($this, {
1385      onStart: function (e) {
1386        startEvent = e;
1387        (_options.onStart || noop).call(this, e);
1388      },
1389      onMove: _options.onMove || noop,
1390      onTouchEnd: _options.onTouchEnd || noop,
1391      onEnd: function (result) {
1392        ////console.log('smartClick → result.moved', result.moved);
1393        if (result.moved) return;
1394        fn.call(this, startEvent);
1395      }
1396    }), {noMove: true});
1397  });
1398}
1399
1400function div (classes, child) {
1401  return '<div class="' + classes + '">' + (child || '') + '</div>';
1402}
1403
1404// Fisher–Yates Shuffle
1405// http://bost.ocks.org/mike/shuffle/
1406function shuffle (array) {
1407  // While there remain elements to shuffle
1408  var l = array.length;
1409  while (l) {
1410    // Pick a remaining element
1411    var i = Math.floor(Math.random() * l--);
1412
1413    // And swap it with the current element
1414    var t = array[l];
1415    array[l] = array[i];
1416    array[i] = t;
1417  }
1418
1419  return array;
1420}
1421
1422function clone (array) {
1423  return Object.prototype.toString.call(array) == '[object Array]'
1424      && $.map(array, function (frame) {
1425       return $.extend({}, frame);
1426      });
1427}
1428
1429function lockScroll ($el, left, top) {
1430  $el
1431    .scrollLeft(left || 0)
1432    .scrollTop(top || 0);
1433}
1434
1435function optionsToLowerCase (options) {
1436  if (options) {
1437    var opts = {};
1438    $.each(options, function (key, value) {
1439      opts[key.toLowerCase()] = value;
1440    });
1441
1442    return opts;
1443  }
1444}
1445
1446function getRatio (_ratio) {
1447  if (!_ratio) return;
1448  var ratio = +_ratio;
1449  if (!isNaN(ratio)) {
1450    return ratio;
1451  } else {
1452    ratio = _ratio.split('/');
1453    return +ratio[0] / +ratio[1] || undefined;
1454  }
1455}
1456
1457function addEvent (el, e, fn, bool) {
1458  if (!e) return;
1459  el.addEventListener ? el.addEventListener(e, fn, !!bool) : el.attachEvent('on'+e, fn);
1460}
1461
1462function elIsDisabled (el) {
1463  return !!el.getAttribute('disabled');
1464}
1465
1466function disableAttr (FLAG) {
1467  return {tabindex: FLAG * -1 + '', disabled: FLAG};
1468}
1469
1470function addEnterUp (el, fn) {
1471  addEvent(el, 'keyup', function (e) {
1472    elIsDisabled(el) || e.keyCode == 13 && fn.call(el, e);
1473  });
1474}
1475
1476function addFocus (el, fn) {
1477  addEvent(el, 'focus', el.onfocusin = function (e) {
1478    fn.call(el, e);
1479  }, true);
1480}
1481
1482function stopEvent (e, stopPropagation) {
1483  e.preventDefault ? e.preventDefault() : (e.returnValue = false);
1484  stopPropagation && e.stopPropagation && e.stopPropagation();
1485}
1486
1487function getDirectionSign (forward) {
1488  return forward ? '>' : '<';
1489}
1490
1491function parsePosition (rule) {
1492  rule = (rule + '').split(/\s+/);
1493  return {
1494    x: measureIsValid(rule[0]) || FIFTYFIFTY,
1495    y: measureIsValid(rule[1]) || FIFTYFIFTY
1496  }
1497}
1498function slide ($el, options) {
1499  var elData = $el.data(),
1500      elPos = Math.round(options.pos),
1501      onEndFn = function () {
1502        elData.sliding = false;
1503        (options.onEnd || noop)();
1504      };
1505
1506  if (typeof options.overPos !== 'undefined' && options.overPos !== options.pos) {
1507    elPos = options.overPos;
1508    onEndFn = function () {
1509      slide($el, $.extend({}, options, {overPos: options.pos, time: Math.max(TRANSITION_DURATION, options.time / 2)}))
1510    };
1511  }
1512
1513  ////////console.time('var translate = $.extend');
1514  var translate = $.extend(getTranslate(elPos/*, options._001*/), options.width && {width: options.width});
1515  ////////console.timeEnd('var translate = $.extend');
1516
1517  elData.sliding = true;
1518
1519  if (CSS3) {
1520    $el.css($.extend(getDuration(options.time), translate));
1521    if (options.time > 10) {
1522      ////////console.time('afterTransition');
1523      afterTransition($el, 'transform', onEndFn, options.time);
1524      ////////console.timeEnd('afterTransition');
1525    } else {
1526      onEndFn();
1527    }
1528  } else {
1529    $el.stop().animate(translate, options.time, BEZIER, onEndFn);
1530  }
1531}
1532
1533function fade ($el1, $el2, $frames, options, fadeStack, chain) {
1534  var chainedFLAG = typeof chain !== 'undefined';
1535  if (!chainedFLAG) {
1536    fadeStack.push(arguments);
1537    Array.prototype.push.call(arguments, fadeStack.length);
1538    if (fadeStack.length > 1) return;
1539  }
1540
1541  $el1 = $el1 || $($el1);
1542  $el2 = $el2 || $($el2);
1543
1544  var _$el1 = $el1[0],
1545      _$el2 = $el2[0],
1546      crossfadeFLAG = options.method === 'crossfade',
1547      onEndFn = function () {
1548        if (!onEndFn.done) {
1549          onEndFn.done = true;
1550          var args = (chainedFLAG || fadeStack.shift()) && fadeStack.shift();
1551          args && fade.apply(this, args);
1552          (options.onEnd || noop)(!!args);
1553        }
1554      },
1555      time = options.time / (chain || 1);
1556
1557  $frames.removeClass(fadeRearClass + ' ' + fadeFrontClass);
1558
1559  $el1
1560      .stop()
1561      .addClass(fadeRearClass);
1562  $el2
1563      .stop()
1564      .addClass(fadeFrontClass);
1565
1566  crossfadeFLAG && _$el2 && $el1.fadeTo(0, 0);
1567
1568  $el1.fadeTo(crossfadeFLAG ? time : 0, 1, crossfadeFLAG && onEndFn);
1569  $el2.fadeTo(time, 0, onEndFn);
1570
1571  (_$el1 && crossfadeFLAG) || _$el2 || onEndFn();
1572}
1573var lastEvent,
1574    moveEventType,
1575    preventEvent,
1576    preventEventTimeout;
1577
1578function extendEvent (e) {
1579  var touch = (e.touches || [])[0] || e;
1580  e._x = touch.pageX;
1581  e._y = touch.clientY;
1582  e._now = $.now();
1583}
1584
1585function touch ($el, options) {
1586  var el = $el[0],
1587      tail = {},
1588      touchEnabledFLAG,
1589      startEvent,
1590      $target,
1591      controlTouch,
1592      touchFLAG,
1593      targetIsSelectFLAG,
1594      targetIsLinkFlag,
1595      tolerance,
1596      moved;
1597
1598  function onStart (e) {
1599    $target = $(e.target);
1600    tail.checked = targetIsSelectFLAG = targetIsLinkFlag = moved = false;
1601
1602    if (touchEnabledFLAG
1603        || tail.flow
1604        || (e.touches && e.touches.length > 1)
1605        || e.which > 1
1606        || (lastEvent && lastEvent.type !== e.type && preventEvent)
1607        || (targetIsSelectFLAG = options.select && $target.is(options.select, el))) return targetIsSelectFLAG;
1608
1609    touchFLAG = e.type === 'touchstart';
1610    targetIsLinkFlag = $target.is('a, a *', el);
1611    controlTouch = tail.control;
1612
1613    tolerance = (tail.noMove || tail.noSwipe || controlTouch) ? 16 : !tail.snap ? 4 : 0;
1614
1615    extendEvent(e);
1616
1617    startEvent = lastEvent = e;
1618    moveEventType = e.type.replace(/down|start/, 'move').replace(/Down/, 'Move');
1619
1620    (options.onStart || noop).call(el, e, {control: controlTouch, $target: $target});
1621
1622    touchEnabledFLAG = tail.flow = true;
1623
1624    if (!touchFLAG || tail.go) stopEvent(e);
1625  }
1626
1627  function onMove (e) {
1628    if ((e.touches && e.touches.length > 1)
1629        || (MS_POINTER && !e.isPrimary)
1630        || moveEventType !== e.type
1631        || !touchEnabledFLAG) {
1632      touchEnabledFLAG && onEnd();
1633      (options.onTouchEnd || noop)();
1634      return;
1635    }
1636
1637    extendEvent(e);
1638
1639    var xDiff = Math.abs(e._x - startEvent._x), // opt _x → _pageX
1640        yDiff = Math.abs(e._y - startEvent._y),
1641        xyDiff = xDiff - yDiff,
1642        xWin = (tail.go || tail.x || xyDiff >= 0) && !tail.noSwipe,
1643        yWin = xyDiff < 0;
1644
1645    if (touchFLAG && !tail.checked) {
1646      if (touchEnabledFLAG = xWin) {
1647        stopEvent(e);
1648      }
1649    } else {
1650      ////console.log('onMove e.preventDefault');
1651      stopEvent(e);
1652      (options.onMove || noop).call(el, e, {touch: touchFLAG});
1653    }
1654
1655    if (!moved && Math.sqrt(Math.pow(xDiff, 2) + Math.pow(yDiff, 2)) > tolerance) {
1656      moved = true;
1657    }
1658
1659    tail.checked = tail.checked || xWin || yWin;
1660  }
1661
1662  function onEnd (e) {
1663    //////console.time('touch.js onEnd');
1664
1665    (options.onTouchEnd || noop)();
1666
1667    var _touchEnabledFLAG = touchEnabledFLAG;
1668    tail.control = touchEnabledFLAG = false;
1669
1670    if (_touchEnabledFLAG) {
1671      tail.flow = false;
1672    }
1673
1674    if (!_touchEnabledFLAG || (targetIsLinkFlag && !tail.checked)) return;
1675
1676    e && stopEvent(e);
1677
1678    preventEvent = true;
1679    clearTimeout(preventEventTimeout);
1680    preventEventTimeout = setTimeout(function () {
1681      preventEvent = false;
1682    }, 1000);
1683
1684    (options.onEnd || noop).call(el, {moved: moved, $target: $target, control: controlTouch, touch: touchFLAG, startEvent: startEvent, aborted: !e || e.type === 'MSPointerCancel'});
1685    //////console.timeEnd('touch.js onEnd');
1686  }
1687
1688  function onOtherStart () {
1689    if (tail.flow) return;
1690    setTimeout(function () {
1691      tail.flow = true;
1692    }, 10);
1693  }
1694
1695  function onOtherEnd () {
1696    if (!tail.flow) return;
1697    setTimeout(function () {
1698      tail.flow = false;
1699    }, TOUCH_TIMEOUT);
1700  }
1701
1702  if (MS_POINTER) {
1703    addEvent(el, 'MSPointerDown', onStart);
1704    addEvent(document, 'MSPointerMove', onMove);
1705    addEvent(document,'MSPointerCancel', onEnd);
1706    addEvent(document, 'MSPointerUp', onEnd);
1707  } else {
1708    addEvent(el, 'touchstart', onStart);
1709    addEvent(el, 'touchmove', onMove);
1710    addEvent(el, 'touchend', onEnd);
1711
1712    addEvent(document, 'touchstart', onOtherStart);
1713    addEvent(document, 'touchend', onOtherEnd);
1714    addEvent(document, 'touchcancel', onOtherEnd);
1715
1716    $WINDOW.on('scroll', onOtherEnd);
1717
1718    $el.on('mousedown', onStart);
1719    $DOCUMENT
1720        .on('mousemove', onMove)
1721        .on('mouseup', onEnd);
1722  }
1723
1724  $el.on('click', 'a', function (e) {
1725    tail.checked && stopEvent(e);
1726  });
1727
1728  return tail;
1729}
1730
1731function moveOnTouch ($el, options) {
1732  var el = $el[0],
1733      elData = $el.data(),
1734      tail = {},
1735      startCoo,
1736      coo,
1737      startElPos,
1738      moveElPos,
1739      edge,
1740      moveTrack,
1741      startTime,
1742      endTime,
1743      min,
1744      max,
1745      snap,
1746      slowFLAG,
1747      controlFLAG,
1748      moved,
1749      tracked;
1750
1751  function startTracking (e, noStop) {
1752    tracked = true;
1753    startCoo = coo = e._x;
1754    startTime = e._now;
1755
1756    moveTrack = [
1757      [startTime, startCoo]
1758    ];
1759
1760    startElPos = moveElPos = tail.noMove || noStop ? 0 : stop($el, (options.getPos || noop)()/*, options._001*/);
1761
1762    (options.onStart || noop).call(el, e);
1763  }
1764
1765  function onStart (e, result) {
1766    min = tail.min;
1767    max = tail.max;
1768    snap = tail.snap;
1769
1770    slowFLAG = e.altKey;
1771    tracked = moved = false;
1772
1773    controlFLAG = result.control;
1774
1775    if (!controlFLAG && !elData.sliding) {
1776      startTracking(e);
1777    }
1778  }
1779
1780  function onMove (e, result) {
1781    if (!tail.noSwipe) {
1782      if (!tracked) {
1783        startTracking(e);
1784      }
1785
1786      coo = e._x;
1787
1788      moveTrack.push([e._now, coo]);
1789
1790      moveElPos = startElPos - (startCoo - coo);
1791
1792      edge = findShadowEdge(moveElPos, min, max);
1793
1794      if (moveElPos <= min) {
1795        moveElPos = edgeResistance(moveElPos, min);
1796      } else if (moveElPos >= max) {
1797        moveElPos = edgeResistance(moveElPos, max);
1798      }
1799
1800      if (!tail.noMove) {
1801        $el.css(getTranslate(moveElPos/*, options._001*/));
1802        if (!moved) {
1803          moved = true;
1804          // only for mouse
1805          result.touch || MS_POINTER || $el.addClass(grabbingClass);
1806        }
1807
1808        (options.onMove || noop).call(el, e, {pos: moveElPos, edge: edge});
1809      }
1810    }
1811  }
1812
1813  function onEnd (result) {
1814    //////console.time('moveontouch.js onEnd');
1815    if (tail.noSwipe && result.moved) return;
1816
1817    if (!tracked) {
1818      startTracking(result.startEvent, true);
1819    }
1820
1821    ////console.log('onEnd');
1822
1823    result.touch || MS_POINTER || $el.removeClass(grabbingClass);
1824
1825    endTime = $.now();
1826
1827    var _backTimeIdeal = endTime - TOUCH_TIMEOUT,
1828        _backTime,
1829        _timeDiff,
1830        _timeDiffLast,
1831        backTime = null,
1832        backCoo,
1833        virtualPos,
1834        limitPos,
1835        newPos,
1836        overPos,
1837        time = TRANSITION_DURATION,
1838        speed,
1839        friction = options.friction;
1840
1841    for (var _i = moveTrack.length - 1; _i >= 0; _i--) {
1842      _backTime = moveTrack[_i][0];
1843      _timeDiff = Math.abs(_backTime - _backTimeIdeal);
1844      if (backTime === null || _timeDiff < _timeDiffLast) {
1845        backTime = _backTime;
1846        backCoo = moveTrack[_i][1];
1847      } else if (backTime === _backTimeIdeal || _timeDiff > _timeDiffLast) {
1848        break;
1849      }
1850      _timeDiffLast = _timeDiff;
1851    }
1852
1853    newPos = minMaxLimit(moveElPos, min, max);
1854
1855    var cooDiff = backCoo - coo,
1856        forwardFLAG = cooDiff >= 0,
1857        timeDiff = endTime - backTime,
1858        longTouchFLAG = timeDiff > TOUCH_TIMEOUT,
1859        swipeFLAG = !longTouchFLAG && moveElPos !== startElPos && newPos === moveElPos;
1860
1861    if (snap) {
1862      newPos = minMaxLimit(Math[swipeFLAG ? (forwardFLAG ? 'floor' : 'ceil') : 'round'](moveElPos / snap) * snap, min, max);
1863      min = max = newPos;
1864    }
1865
1866    if (swipeFLAG && (snap || newPos === moveElPos)) {
1867      speed = -(cooDiff / timeDiff);
1868      time *= minMaxLimit(Math.abs(speed), options.timeLow, options.timeHigh);
1869      virtualPos = Math.round(moveElPos + speed * time / friction);
1870
1871      if (!snap) {
1872        newPos = virtualPos;
1873      }
1874
1875      if (!forwardFLAG && virtualPos > max || forwardFLAG && virtualPos < min) {
1876        limitPos = forwardFLAG ? min : max;
1877        overPos = virtualPos - limitPos;
1878        if (!snap) {
1879          newPos = limitPos;
1880        }
1881        overPos = minMaxLimit(newPos + overPos * .03, limitPos - 50, limitPos + 50);
1882        time = Math.abs((moveElPos - overPos) / (speed / friction));
1883      }
1884    }
1885
1886    time *= slowFLAG ? 10 : 1;
1887
1888    (options.onEnd || noop).call(el, $.extend(result, {moved: result.moved || longTouchFLAG && snap, pos: moveElPos, newPos: newPos, overPos: overPos, time: time}));
1889  }
1890
1891  tail = $.extend(touch(options.$wrap, $.extend({}, options, {
1892    onStart: onStart,
1893    onMove: onMove,
1894    onEnd: onEnd
1895  })), tail);
1896
1897  return tail;
1898}
1899function wheel ($el, options) {
1900  var el = $el[0],
1901      lockFLAG,
1902      lastDirection,
1903      lastNow,
1904      tail = {
1905        prevent: {}
1906      };
1907
1908  addEvent(el, WHEEL, function (e) {
1909    var yDelta = e.wheelDeltaY || -1 * e.deltaY || 0,
1910        xDelta = e.wheelDeltaX || -1 * e.deltaX || 0,
1911        xWin = Math.abs(xDelta) && !Math.abs(yDelta),
1912        direction = getDirectionSign(xDelta < 0),
1913        sameDirection = lastDirection === direction,
1914        now = $.now(),
1915        tooFast = now - lastNow < TOUCH_TIMEOUT;
1916
1917    lastDirection = direction;
1918    lastNow = now;
1919
1920    if (!xWin || !tail.ok || tail.prevent[direction] && !lockFLAG) {
1921      return;
1922    } else {
1923      stopEvent(e, true);
1924      if (lockFLAG && sameDirection && tooFast) {
1925        return;
1926      }
1927    }
1928
1929    if (options.shift) {
1930      lockFLAG = true;
1931      clearTimeout(tail.t);
1932      tail.t = setTimeout(function () {
1933        lockFLAG = false;
1934      }, SCROLL_LOCK_TIMEOUT);
1935    }
1936
1937    (options.onEnd || noop)(e, options.shift ? direction : xDelta);
1938
1939  });
1940
1941  return tail;
1942}
1943jQuery.Fotorama = function ($fotorama, opts) {
1944  $HTML = $('html');
1945  $BODY = $('body');
1946
1947  var that = this,
1948      stamp = $.now(),
1949      stampClass = _fotoramaClass + stamp,
1950      fotorama = $fotorama[0],
1951      data,
1952      dataFrameCount = 1,
1953      fotoramaData = $fotorama.data(),
1954      size,
1955
1956      $style = $('<style></style>'),
1957
1958      $anchor = $(div(hiddenClass)),
1959      $wrap = $(div(wrapClass)),
1960      $stage = $(div(stageClass)).appendTo($wrap),
1961      stage = $stage[0],
1962
1963      $stageShaft = $(div(stageShaftClass)).appendTo($stage),
1964      $stageFrame = $(),
1965      $arrPrev = $(div(arrClass + ' ' + arrPrevClass + buttonAttributes)),
1966      $arrNext = $(div(arrClass + ' ' + arrNextClass + buttonAttributes)),
1967      $arrs = $arrPrev.add($arrNext).appendTo($stage),
1968      $navWrap = $(div(navWrapClass)),
1969      $nav = $(div(navClass)).appendTo($navWrap),
1970      $navShaft = $(div(navShaftClass)).appendTo($nav),
1971      $navFrame,
1972      $navDotFrame = $(),
1973      $navThumbFrame = $(),
1974
1975      stageShaftData = $stageShaft.data(),
1976      navShaftData = $navShaft.data(),
1977
1978      $thumbBorder = $(div(thumbBorderClass)).appendTo($navShaft),
1979
1980      $fullscreenIcon = $(div(fullscreenIconClass + buttonAttributes)),
1981      fullscreenIcon = $fullscreenIcon[0],
1982      $videoPlay = $(div(videoPlayClass)),
1983      $videoClose = $(div(videoCloseClass)).appendTo($stage),
1984      videoClose = $videoClose[0],
1985
1986      spinner,
1987      $spinner = $(div(spinnerClass)),
1988
1989      $videoPlaying,
1990
1991      activeIndex = false,
1992      activeFrame,
1993      activeIndexes,
1994      repositionIndex,
1995      dirtyIndex,
1996      lastActiveIndex,
1997      prevIndex,
1998      nextIndex,
1999      nextAutoplayIndex,
2000      startIndex,
2001
2002      o_loop,
2003      o_nav,
2004      o_navThumbs,
2005      o_navTop,
2006      o_allowFullScreen,
2007      o_nativeFullScreen,
2008      o_fade,
2009      o_thumbSide,
2010      o_thumbSide2,
2011      o_transitionDuration,
2012      o_transition,
2013      o_shadows,
2014      o_rtl,
2015      o_keyboard,
2016      lastOptions = {},
2017
2018      measures = {},
2019      measuresSetFLAG,
2020
2021      stageShaftTouchTail = {},
2022      stageWheelTail = {},
2023      navShaftTouchTail = {},
2024      navWheelTail = {},
2025
2026      scrollTop,
2027      scrollLeft,
2028
2029      showedFLAG,
2030      pausedAutoplayFLAG,
2031      stoppedAutoplayFLAG,
2032
2033      toDeactivate = {},
2034      toDetach = {},
2035
2036      measuresStash,
2037
2038      touchedFLAG,
2039
2040      hoverFLAG,
2041
2042      navFrameKey,
2043      stageLeft = 0,
2044
2045      fadeStack = [];
2046
2047  $wrap[STAGE_FRAME_KEY] = $(div(stageFrameClass));
2048  $wrap[NAV_THUMB_FRAME_KEY] = $(div(navFrameClass + ' ' + navFrameThumbClass + buttonAttributes, div(thumbClass)));
2049  $wrap[NAV_DOT_FRAME_KEY] = $(div(navFrameClass + ' ' + navFrameDotClass + buttonAttributes, div(dotClass)));
2050
2051  toDeactivate[STAGE_FRAME_KEY] = [];
2052  toDeactivate[NAV_THUMB_FRAME_KEY] = [];
2053  toDeactivate[NAV_DOT_FRAME_KEY] = [];
2054  toDetach[STAGE_FRAME_KEY] = {};
2055
2056  $wrap
2057      .addClass(CSS3 ? wrapCss3Class : wrapCss2Class)
2058      .toggleClass(wrapNoControlsClass, !opts.controlsonstart);
2059
2060  fotoramaData.fotorama = this;
2061
2062  function checkForVideo () {
2063    $.each(data, function (i, dataFrame) {
2064      if (!dataFrame.i) {
2065        dataFrame.i = dataFrameCount++;
2066        var video = findVideoId(dataFrame.video, true);
2067        if (video) {
2068          var thumbs = {};
2069          dataFrame.video = video;
2070          if (!dataFrame.img && !dataFrame.thumb) {
2071            thumbs = getVideoThumbs(dataFrame, data, that);
2072          } else {
2073            dataFrame.thumbsReady = true;
2074          }
2075          updateData(data, {img: thumbs.img, thumb: thumbs.thumb}, dataFrame.i, that);
2076        }
2077      }
2078    });
2079  }
2080
2081  function allowKey (key) {
2082    return o_keyboard[key] || that.fullScreen;
2083  }
2084
2085  function bindGlobalEvents (FLAG) {
2086    var keydownCommon = 'keydown.' + _fotoramaClass,
2087        localStamp = _fotoramaClass + stamp,
2088        keydownLocal = 'keydown.' + localStamp,
2089        resizeLocal = 'resize.' + localStamp + ' ' + 'orientationchange.' + localStamp;
2090
2091    if (FLAG) {
2092      $DOCUMENT
2093          .on(keydownLocal, function (e) {
2094            var catched,
2095                index;
2096
2097            if ($videoPlaying && e.keyCode === 27) {
2098              catched = true;
2099              unloadVideo($videoPlaying, true, true);
2100            } else if (that.fullScreen || (opts.keyboard && !that.index)) {
2101              if (e.keyCode === 27) {
2102                catched = true;
2103                that.cancelFullScreen();
2104              } else if ((e.shiftKey && e.keyCode === 32 && allowKey('space')) || (e.keyCode === 37 && allowKey('left')) || (e.keyCode === 38 && allowKey('up'))) {
2105                index = '<';
2106              } else if ((e.keyCode === 32 && allowKey('space')) || (e.keyCode === 39 && allowKey('right')) || (e.keyCode === 40 && allowKey('down'))) {
2107                index = '>';
2108              } else if (e.keyCode === 36 && allowKey('home')) {
2109                index = '<<';
2110              } else if (e.keyCode === 35 && allowKey('end')) {
2111                index = '>>';
2112              }
2113            }
2114
2115            (catched || index) && stopEvent(e);
2116            index && that.show({index: index, slow: e.altKey, user: true});
2117          });
2118
2119      if (!that.index) {
2120        $DOCUMENT
2121            .off(keydownCommon)
2122            .on(keydownCommon, 'textarea, input, select', function (e) {
2123              !$BODY.hasClass(_fullscreenClass) && e.stopPropagation();
2124            });
2125      }
2126
2127      $WINDOW.on(resizeLocal, that.resize);
2128    } else {
2129      $DOCUMENT.off(keydownLocal);
2130      $WINDOW.off(resizeLocal);
2131    }
2132  }
2133
2134  function appendElements (FLAG) {
2135    if (FLAG === appendElements.f) return;
2136
2137    if (FLAG) {
2138      $fotorama
2139          .html('')
2140          .addClass(_fotoramaClass + ' ' + stampClass)
2141          .append($wrap)
2142          .before($style)
2143          .before($anchor);
2144
2145      addInstance(that);
2146    } else {
2147      $wrap.detach();
2148      $style.detach();
2149      $anchor.detach();
2150      $fotorama
2151          .html(fotoramaData.urtext)
2152          .removeClass(stampClass);
2153
2154      hideInstance(that);
2155    }
2156
2157    bindGlobalEvents(FLAG);
2158    appendElements.f = FLAG;
2159  }
2160
2161  function setData () {
2162    data = that.data = data || clone(opts.data) || getDataFromHtml($fotorama);
2163    size = that.size = data.length;
2164
2165    !ready.ok && opts.shuffle && shuffle(data);
2166
2167    checkForVideo();
2168
2169    activeIndex = limitIndex(activeIndex);
2170
2171    size && appendElements(true);
2172  }
2173
2174  function stageNoMove () {
2175    var _noMove = (size < 2 && !opts.enableifsingleframe) || $videoPlaying;
2176    stageShaftTouchTail.noMove = _noMove || o_fade;
2177    stageShaftTouchTail.noSwipe = _noMove || !opts.swipe;
2178
2179    !o_transition && $stageShaft.toggleClass(grabClass, !opts.click && !stageShaftTouchTail.noMove && !stageShaftTouchTail.noSwipe);
2180    MS_POINTER && $wrap.toggleClass(wrapPanYClass, !stageShaftTouchTail.noSwipe);
2181  }
2182
2183  function setAutoplayInterval (interval) {
2184    if (interval === true) interval = '';
2185    opts.autoplay = Math.max(+interval || AUTOPLAY_INTERVAL, o_transitionDuration * 1.5);
2186  }
2187
2188  /**
2189   * Options on the fly
2190   * */
2191  function setOptions () {
2192    that.options = opts = optionsToLowerCase(opts);
2193
2194    o_fade = (opts.transition === 'crossfade' || opts.transition === 'dissolve');
2195
2196    o_loop = opts.loop && (size > 2 || (o_fade && (!o_transition || o_transition !== 'slide')));
2197
2198    o_transitionDuration = +opts.transitionduration || TRANSITION_DURATION;
2199
2200    o_rtl = opts.direction === 'rtl';
2201
2202    o_keyboard = $.extend({}, opts.keyboard && KEYBOARD_OPTIONS, opts.keyboard);
2203
2204    var classes = {add: [], remove: []};
2205
2206    function addOrRemoveClass (FLAG, value) {
2207      classes[FLAG ? 'add' : 'remove'].push(value);
2208    }
2209
2210    if (size > 1 || opts.enableifsingleframe) {
2211      o_nav = opts.nav;
2212      o_navTop = opts.navposition === 'top';
2213      classes.remove.push(selectClass);
2214
2215      $arrs.toggle(!!opts.arrows);
2216    } else {
2217      o_nav = false;
2218      $arrs.hide();
2219    }
2220
2221    spinnerStop();
2222    spinner = new Spinner($.extend(spinnerDefaults, opts.spinner, spinnerOverride, {direction: o_rtl ? -1 : 1}));
2223
2224    arrsUpdate();
2225    stageWheelUpdate();
2226
2227    if (opts.autoplay) setAutoplayInterval(opts.autoplay);
2228
2229    o_thumbSide = numberFromMeasure(opts.thumbwidth) || THUMB_SIZE;
2230    o_thumbSide2 = numberFromMeasure(opts.thumbheight) || THUMB_SIZE;
2231
2232    stageWheelTail.ok = navWheelTail.ok = opts.trackpad && !SLOW;
2233
2234    stageNoMove();
2235
2236    extendMeasures(opts, [measures]);
2237
2238    o_navThumbs = o_nav === 'thumbs';
2239
2240    if (o_navThumbs) {
2241      frameDraw(size, 'navThumb');
2242
2243      $navFrame = $navThumbFrame;
2244      navFrameKey = NAV_THUMB_FRAME_KEY;
2245
2246      setStyle($style, $.Fotorama.jst.style({w: o_thumbSide, h: o_thumbSide2, b: opts.thumbborderwidth, m: opts.thumbmargin, s: stamp, q: !COMPAT}));
2247
2248      $nav
2249          .addClass(navThumbsClass)
2250          .removeClass(navDotsClass);
2251    } else if (o_nav === 'dots') {
2252      frameDraw(size, 'navDot');
2253
2254      $navFrame = $navDotFrame;
2255      navFrameKey = NAV_DOT_FRAME_KEY;
2256
2257      $nav
2258          .addClass(navDotsClass)
2259          .removeClass(navThumbsClass);
2260    } else {
2261      o_nav = false;
2262      $nav.removeClass(navThumbsClass + ' ' + navDotsClass);
2263    }
2264
2265    if (o_nav) {
2266      if (o_navTop) {
2267        $navWrap.insertBefore($stage);
2268      } else {
2269        $navWrap.insertAfter($stage);
2270      }
2271      frameAppend.nav = false;
2272      frameAppend($navFrame, $navShaft, 'nav');
2273    }
2274
2275    o_allowFullScreen = opts.allowfullscreen;
2276
2277    if (o_allowFullScreen) {
2278      $fullscreenIcon.prependTo($stage);
2279      o_nativeFullScreen = FULLSCREEN && o_allowFullScreen === 'native';
2280    } else {
2281      $fullscreenIcon.detach();
2282      o_nativeFullScreen = false;
2283    }
2284
2285    addOrRemoveClass(o_fade, wrapFadeClass);
2286    addOrRemoveClass(!o_fade, wrapSlideClass);
2287    addOrRemoveClass(!opts.captions, wrapNoCaptionsClass);
2288    addOrRemoveClass(o_rtl, wrapRtlClass);
2289    addOrRemoveClass(opts.arrows !== 'always', wrapToggleArrowsClass);
2290
2291    o_shadows = opts.shadows && !SLOW;
2292    addOrRemoveClass(!o_shadows, wrapNoShadowsClass);
2293
2294    $wrap
2295        .addClass(classes.add.join(' '))
2296        .removeClass(classes.remove.join(' '));
2297
2298    lastOptions = $.extend({}, opts);
2299  }
2300
2301  function normalizeIndex (index) {
2302    return index < 0 ? (size + (index % size)) % size : index >= size ? index % size : index;
2303  }
2304
2305  function limitIndex (index) {
2306    return minMaxLimit(index, 0, size - 1);
2307  }
2308
2309  function edgeIndex (index) {
2310    return o_loop ? normalizeIndex(index) : limitIndex(index);
2311  }
2312
2313  function getPrevIndex (index) {
2314    return index > 0 || o_loop ? index - 1 : false;
2315  }
2316
2317  function getNextIndex (index) {
2318    return index < size - 1 || o_loop ? index + 1 : false;
2319  }
2320
2321  function setStageShaftMinmaxAndSnap () {
2322    stageShaftTouchTail.min = o_loop ? -Infinity : -getPosByIndex(size - 1, measures.w, opts.margin, repositionIndex);
2323    stageShaftTouchTail.max = o_loop ? Infinity : -getPosByIndex(0, measures.w, opts.margin, repositionIndex);
2324    stageShaftTouchTail.snap = measures.w + opts.margin;
2325  }
2326
2327  function setNavShaftMinMax () {
2328    ////////console.log('setNavShaftMinMax', measures.nw);
2329    navShaftTouchTail.min = Math.min(0, measures.nw - $navShaft.width());
2330    navShaftTouchTail.max = 0;
2331    $navShaft.toggleClass(grabClass, !(navShaftTouchTail.noMove = navShaftTouchTail.min === navShaftTouchTail.max));
2332  }
2333
2334  function eachIndex (indexes, type, fn) {
2335    if (typeof indexes === 'number') {
2336      indexes = new Array(indexes);
2337      var rangeFLAG = true;
2338    }
2339    return $.each(indexes, function (i, index) {
2340      if (rangeFLAG) index = i;
2341      if (typeof index === 'number') {
2342        var dataFrame = data[normalizeIndex(index)];
2343
2344        if (dataFrame) {
2345          var key = '$' + type + 'Frame',
2346              $frame = dataFrame[key];
2347
2348          fn.call(this, i, index, dataFrame, $frame, key, $frame && $frame.data());
2349        }
2350      }
2351    });
2352  }
2353
2354  function setMeasures (width, height, ratio, index) {
2355    if (!measuresSetFLAG || (measuresSetFLAG === '*' && index === startIndex)) {
2356
2357      //////console.log('setMeasures', index, opts.width, opts.height);
2358
2359      width = measureIsValid(opts.width) || measureIsValid(width) || WIDTH;
2360      height = measureIsValid(opts.height) || measureIsValid(height) || HEIGHT;
2361      that.resize({
2362        width: width,
2363        ratio: opts.ratio || ratio || width / height
2364      }, 0, index !== startIndex && '*');
2365    }
2366  }
2367
2368  function loadImg (indexes, type, specialMeasures, method, position, again) {
2369    eachIndex(indexes, type, function (i, index, dataFrame, $frame, key, frameData) {
2370
2371      if (!$frame) return;
2372
2373      var fullFLAG = that.fullScreen && dataFrame.full && dataFrame.full !== dataFrame.img && !frameData.$full && type === 'stage';
2374
2375      if (frameData.$img && !again && !fullFLAG) return;
2376
2377      var img = new Image(),
2378          $img = $(img),
2379          imgData = $img.data();
2380
2381      frameData[fullFLAG ? '$full' : '$img'] = $img;
2382
2383      var srcKey = type === 'stage' ? (fullFLAG ? 'full' : 'img') : 'thumb',
2384          src = dataFrame[srcKey],
2385          dummy = fullFLAG ? null : dataFrame[type === 'stage' ? 'thumb' : 'img'];
2386
2387      if (type === 'navThumb') $frame = frameData.$wrap;
2388
2389      function triggerTriggerEvent (event) {
2390        var _index = normalizeIndex(index);
2391        triggerEvent(event, {
2392          index: _index,
2393          src: src,
2394          frame: data[_index]
2395        });
2396      }
2397
2398      function error () {
2399        $img.remove();
2400
2401        $.Fotorama.cache[src] = 'error';
2402
2403        if ((!dataFrame.html || type !== 'stage') && dummy && dummy !== src) {
2404          dataFrame[srcKey] = src = dummy;
2405          loadImg([index], type, specialMeasures, method, position, true);
2406        } else {
2407          if (src && !dataFrame.html && !fullFLAG) {
2408            $frame
2409                .trigger('f:error')
2410                .removeClass(loadingClass)
2411                .addClass(errorClass);
2412
2413            triggerTriggerEvent('error');
2414          } else if (type === 'stage') {
2415            $frame
2416                .trigger('f:load')
2417                .removeClass(loadingClass + ' ' + errorClass)
2418                .addClass(loadedClass);
2419
2420            triggerTriggerEvent('load');
2421            setMeasures();
2422          }
2423
2424          frameData.state = 'error';
2425
2426          if (size > 1 && data[index] === dataFrame && !dataFrame.html && !dataFrame.deleted && !dataFrame.video && !fullFLAG) {
2427            dataFrame.deleted = true;
2428            that.splice(index, 1);
2429          }
2430        }
2431      }
2432
2433      function loaded () {
2434        //////console.log('loaded: ' + src);
2435
2436        ////console.log('$.Fotorama.measures[src]', $.Fotorama.measures[src]);
2437
2438        $.Fotorama.measures[src] = imgData.measures = $.Fotorama.measures[src] || {
2439          width: img.width,
2440          height: img.height,
2441          ratio: img.width / img.height
2442        };
2443
2444        setMeasures(imgData.measures.width, imgData.measures.height, imgData.measures.ratio, index);
2445
2446        $img
2447            .off('load error')
2448            .addClass(imgClass + (fullFLAG ? ' ' + imgFullClass : ''))
2449            .prependTo($frame);
2450
2451        fit($img, ($.isFunction(specialMeasures) ? specialMeasures() : specialMeasures) || measures, method || dataFrame.fit || opts.fit, position || dataFrame.position || opts.position);
2452
2453        $.Fotorama.cache[src] = frameData.state = 'loaded';
2454
2455        setTimeout(function () {
2456          $frame
2457              .trigger('f:load')
2458              .removeClass(loadingClass + ' ' + errorClass)
2459              .addClass(loadedClass + ' ' + (fullFLAG ? loadedFullClass : loadedImgClass));
2460
2461          if (type === 'stage') {
2462            triggerTriggerEvent('load');
2463          } else if (dataFrame.thumbratio === AUTO || !dataFrame.thumbratio && opts.thumbratio === AUTO) {
2464            // danger! reflow for all thumbnails
2465            dataFrame.thumbratio = imgData.measures.ratio;
2466            reset();
2467          }
2468        }, 0);
2469      }
2470
2471      if (!src) {
2472        error();
2473        return;
2474      }
2475
2476      function waitAndLoad () {
2477        var _i = 10;
2478        waitFor(function () {
2479          return !touchedFLAG || !_i-- && !SLOW;
2480        }, function () {
2481          loaded();
2482        });
2483      }
2484
2485      if (!$.Fotorama.cache[src]) {
2486        $.Fotorama.cache[src] = '*';
2487
2488        $img
2489            .on('load', waitAndLoad)
2490            .on('error', error);
2491      } else {
2492        (function justWait () {
2493          if ($.Fotorama.cache[src] === 'error') {
2494            error();
2495          } else if ($.Fotorama.cache[src] === 'loaded') {
2496            ////console.log('take from cache: ' + src);
2497            setTimeout(waitAndLoad, 0);
2498          } else {
2499            setTimeout(justWait, 100);
2500          }
2501        })();
2502      }
2503
2504      frameData.state = '';
2505
2506      if ( frameData.data.hasOwnProperty('alt')   ) { img.alt   = frameData.data.alt; }
2507      if ( frameData.data.hasOwnProperty('title') ) { img.title = frameData.data.title; }
2508
2509      img.src = src;
2510    });
2511  }
2512
2513  function spinnerSpin ($el) {
2514    $spinner.append(spinner.spin().el).appendTo($el);
2515  }
2516
2517  function spinnerStop () {
2518    $spinner.detach();
2519    spinner && spinner.stop();
2520  }
2521
2522  function updateFotoramaState () {
2523    var $frame = activeFrame[STAGE_FRAME_KEY];
2524
2525    if ($frame && !$frame.data().state) {
2526      spinnerSpin($frame);
2527      $frame.on('f:load f:error', function () {
2528        $frame.off('f:load f:error');
2529        spinnerStop();
2530      });
2531    }
2532  }
2533
2534  function addNavFrameEvents (frame) {
2535    addEnterUp(frame, onNavFrameClick);
2536    addFocus(frame, function () {
2537
2538      setTimeout(function () {
2539        lockScroll($nav);
2540      }, 0);
2541      slideNavShaft({time: o_transitionDuration, guessIndex: $(this).data().eq, minMax: navShaftTouchTail});
2542    });
2543  }
2544
2545  function frameDraw (indexes, type) {
2546    eachIndex(indexes, type, function (i, index, dataFrame, $frame, key, frameData) {
2547      if ($frame) return;
2548
2549      $frame = dataFrame[key] = $wrap[key].clone();
2550      frameData = $frame.data();
2551      frameData.data = dataFrame;
2552      var frame = $frame[0];
2553
2554      if (type === 'stage') {
2555
2556        if (dataFrame.html) {
2557          $('<div class="' + htmlClass + '"></div>')
2558              .append(
2559                  dataFrame._html ? $(dataFrame.html)
2560                      .removeAttr('id')
2561                      .html(dataFrame._html) // Because of IE
2562                  : dataFrame.html
2563              )
2564              .appendTo($frame);
2565        }
2566
2567        dataFrame.caption && $(div(captionClass, div(captionWrapClass, dataFrame.caption))).appendTo($frame);
2568
2569        dataFrame.video && $frame
2570          .addClass(stageFrameVideoClass)
2571          .append($videoPlay.clone());
2572
2573        // This solves tabbing problems
2574        addFocus(frame, function () {
2575          setTimeout(function () {
2576            lockScroll($stage);
2577          }, 0);
2578          clickToShow({index: frameData.eq, user: true});
2579        });
2580
2581        $stageFrame = $stageFrame.add($frame);
2582      } else if (type === 'navDot') {
2583        addNavFrameEvents(frame);
2584        $navDotFrame = $navDotFrame.add($frame);
2585      } else if (type === 'navThumb') {
2586        addNavFrameEvents(frame);
2587        frameData.$wrap = $frame.children(':first');
2588        $navThumbFrame = $navThumbFrame.add($frame);
2589        if (dataFrame.video) {
2590          frameData.$wrap.append($videoPlay.clone());
2591        }
2592      }
2593    });
2594  }
2595
2596  function callFit ($img, measuresToFit, method, position) {
2597    return $img && $img.length && fit($img, measuresToFit, method, position);
2598  }
2599
2600  function stageFramePosition (indexes) {
2601    eachIndex(indexes, 'stage', function (i, index, dataFrame, $frame, key, frameData) {
2602      if (!$frame) return;
2603
2604      var normalizedIndex = normalizeIndex(index),
2605          method = dataFrame.fit || opts.fit,
2606          position = dataFrame.position || opts.position;
2607      frameData.eq = normalizedIndex;
2608
2609      toDetach[STAGE_FRAME_KEY][normalizedIndex] = $frame.css($.extend({left: o_fade ? 0 : getPosByIndex(index, measures.w, opts.margin, repositionIndex)}, o_fade && getDuration(0)));
2610
2611      if (isDetached($frame[0])) {
2612        $frame.appendTo($stageShaft);
2613        unloadVideo(dataFrame.$video);
2614      }
2615
2616      callFit(frameData.$img, measures, method, position);
2617      callFit(frameData.$full, measures, method, position);
2618    });
2619  }
2620
2621  function thumbsDraw (pos, loadFLAG) {
2622    if (o_nav !== 'thumbs' || isNaN(pos)) return;
2623
2624    var leftLimit = -pos,
2625        rightLimit = -pos + measures.nw;
2626
2627    $navThumbFrame.each(function () {
2628      var $this = $(this),
2629          thisData = $this.data(),
2630          eq = thisData.eq,
2631          getSpecialMeasures = function () {
2632            return {
2633              h: o_thumbSide2,
2634              w: thisData.w
2635            }
2636          },
2637          specialMeasures = getSpecialMeasures(),
2638          dataFrame = data[eq] || {},
2639          method = dataFrame.thumbfit || opts.thumbfit,
2640          position = dataFrame.thumbposition || opts.thumbposition;
2641
2642      specialMeasures.w = thisData.w;
2643
2644      if (thisData.l + thisData.w < leftLimit
2645          || thisData.l > rightLimit
2646          || callFit(thisData.$img, specialMeasures, method, position)) return;
2647
2648      loadFLAG && loadImg([eq], 'navThumb', getSpecialMeasures, method, position);
2649    });
2650  }
2651
2652  function frameAppend ($frames, $shaft, type) {
2653    if (!frameAppend[type]) {
2654
2655      var thumbsFLAG = type === 'nav' && o_navThumbs,
2656          left = 0;
2657
2658      $shaft.append(
2659        $frames
2660            .filter(function () {
2661              var actual,
2662                  $this = $(this),
2663                  frameData = $this.data();
2664              for (var _i = 0, _l = data.length; _i < _l; _i++) {
2665                if (frameData.data === data[_i]) {
2666                  actual = true;
2667                  frameData.eq = _i;
2668                  break;
2669                }
2670              }
2671              return actual || $this.remove() && false;
2672            })
2673            .sort(function (a, b) {
2674              return $(a).data().eq - $(b).data().eq;
2675            })
2676            .each(function () {
2677
2678              if (!thumbsFLAG) return;
2679
2680              var $this = $(this),
2681                  frameData = $this.data(),
2682                  thumbwidth = Math.round(o_thumbSide2 * frameData.data.thumbratio) || o_thumbSide;
2683
2684              frameData.l = left;
2685              frameData.w = thumbwidth;
2686
2687              $this.css({width: thumbwidth});
2688
2689              left += thumbwidth + opts.thumbmargin;
2690            })
2691      );
2692
2693      frameAppend[type] = true;
2694    }
2695  }
2696
2697  function getDirection (x) {
2698    return x - stageLeft > measures.w / 3;
2699  }
2700
2701  function disableDirrection (i) {
2702    return !o_loop && (!(activeIndex + i) || !(activeIndex - size + i)) && !$videoPlaying;
2703  }
2704
2705  function arrsUpdate () {
2706    var disablePrev = disableDirrection(0),
2707        disableNext = disableDirrection(1);
2708    $arrPrev
2709        .toggleClass(arrDisabledClass, disablePrev)
2710        .attr(disableAttr(disablePrev));
2711    $arrNext
2712        .toggleClass(arrDisabledClass, disableNext)
2713        .attr(disableAttr(disableNext));
2714  }
2715
2716  function stageWheelUpdate () {
2717    if (stageWheelTail.ok) {
2718      stageWheelTail.prevent = {'<': disableDirrection(0), '>': disableDirrection(1)};
2719    }
2720  }
2721
2722  function getNavFrameBounds ($navFrame) {
2723    var navFrameData = $navFrame.data(),
2724        left,
2725        width;
2726
2727    if (o_navThumbs) {
2728      left = navFrameData.l;
2729      width = navFrameData.w;
2730    } else {
2731      left = $navFrame.position().left;
2732      width = $navFrame.width();
2733    }
2734
2735    return {
2736      c: left + width / 2,
2737      min: -left + opts.thumbmargin * 10,
2738      max: -left + measures.w - width - opts.thumbmargin * 10
2739    };
2740  }
2741
2742  function slideThumbBorder (time) {
2743    var navFrameData = activeFrame[navFrameKey].data();
2744    slide($thumbBorder, {
2745      time: time * 1.2,
2746      pos: navFrameData.l,
2747      width: navFrameData.w - opts.thumbborderwidth * 2
2748    });
2749  }
2750
2751  function slideNavShaft (options) {
2752    ////console.log('slideNavShaft', options.guessIndex, options.keep, slideNavShaft.l);
2753    var $guessNavFrame = data[options.guessIndex][navFrameKey];
2754    if ($guessNavFrame) {
2755      var overflowFLAG = navShaftTouchTail.min !== navShaftTouchTail.max,
2756          minMax = options.minMax || overflowFLAG && getNavFrameBounds(activeFrame[navFrameKey]),
2757          l = overflowFLAG && (options.keep && slideNavShaft.l ? slideNavShaft.l : minMaxLimit((options.coo || measures.nw / 2) - getNavFrameBounds($guessNavFrame).c, minMax.min, minMax.max)),
2758          pos = overflowFLAG && minMaxLimit(l, navShaftTouchTail.min, navShaftTouchTail.max),
2759          time = options.time * 1.1;
2760
2761      slide($navShaft, {
2762        time: time,
2763        pos: pos || 0,
2764        onEnd: function () {
2765          thumbsDraw(pos, true);
2766        }
2767      });
2768
2769      //if (time) thumbsDraw(pos);
2770
2771      setShadow($nav, findShadowEdge(pos, navShaftTouchTail.min, navShaftTouchTail.max));
2772      slideNavShaft.l = l;
2773    }
2774  }
2775
2776  function navUpdate () {
2777    deactivateFrames(navFrameKey);
2778    toDeactivate[navFrameKey].push(activeFrame[navFrameKey].addClass(activeClass));
2779  }
2780
2781  function deactivateFrames (key) {
2782    var _toDeactivate = toDeactivate[key];
2783
2784    while (_toDeactivate.length) {
2785      _toDeactivate.shift().removeClass(activeClass);
2786    }
2787  }
2788
2789  function detachFrames (key) {
2790    var _toDetach = toDetach[key];
2791
2792    //////console.log('_toDetach', _toDetach);
2793    //////console.log('activeIndexes', activeIndexes);
2794
2795    $.each(activeIndexes, function (i, index) {
2796      delete _toDetach[normalizeIndex(index)];
2797    });
2798
2799    $.each(_toDetach, function (index, $frame) {
2800      delete _toDetach[index];
2801      //////console.log('Detach', index);
2802      $frame.detach();
2803    });
2804  }
2805
2806  function stageShaftReposition (skipOnEnd) {
2807
2808    repositionIndex = dirtyIndex = activeIndex;
2809
2810    var $frame = activeFrame[STAGE_FRAME_KEY];
2811
2812    if ($frame) {
2813      deactivateFrames(STAGE_FRAME_KEY);
2814      toDeactivate[STAGE_FRAME_KEY].push($frame.addClass(activeClass));
2815
2816      skipOnEnd || that.show.onEnd(true);
2817      stop($stageShaft, 0, true);
2818
2819      detachFrames(STAGE_FRAME_KEY);
2820      stageFramePosition(activeIndexes);
2821      setStageShaftMinmaxAndSnap();
2822      setNavShaftMinMax();
2823    }
2824  }
2825
2826  function extendMeasures (options, measuresArray) {
2827    if (!options) return;
2828
2829    $.each(measuresArray, function (i, measures) {
2830      if (!measures) return;
2831
2832      $.extend(measures, {
2833        width: options.width || measures.width,
2834        height: options.height,
2835        minwidth: options.minwidth,
2836        maxwidth: options.maxwidth,
2837        minheight: options.minheight,
2838        maxheight: options.maxheight,
2839        ratio: getRatio(options.ratio)
2840      })
2841    });
2842  }
2843
2844  function triggerEvent (event, extra) {
2845    $fotorama.trigger(_fotoramaClass + ':' + event, [that, extra]);
2846  }
2847
2848  function onTouchStart () {
2849    clearTimeout(onTouchEnd.t);
2850    touchedFLAG = 1;
2851
2852    if (opts.stopautoplayontouch) {
2853      that.stopAutoplay();
2854    } else {
2855      pausedAutoplayFLAG = true;
2856    }
2857  }
2858
2859  function onTouchEnd () {
2860    if (!touchedFLAG) return;
2861    if (!opts.stopautoplayontouch) {
2862      releaseAutoplay();
2863      changeAutoplay();
2864    }
2865
2866    onTouchEnd.t = setTimeout(function () {
2867      touchedFLAG = 0;
2868    }, TRANSITION_DURATION + TOUCH_TIMEOUT);
2869    //////console.timeEnd('onTouchEnd');
2870  }
2871
2872  function releaseAutoplay () {
2873    ////console.log('releaseAutoplay');
2874    pausedAutoplayFLAG = !!($videoPlaying || stoppedAutoplayFLAG);
2875  }
2876
2877  function changeAutoplay () {
2878    ////console.log('changeAutoplay');
2879
2880    clearTimeout(changeAutoplay.t);
2881    waitFor.stop(changeAutoplay.w);
2882
2883    if (!opts.autoplay || pausedAutoplayFLAG) {
2884      if (that.autoplay) {
2885        that.autoplay = false;
2886        triggerEvent('stopautoplay');
2887      }
2888
2889      return;
2890    }
2891
2892    ////console.log('changeAutoplay continue');
2893
2894    if (!that.autoplay) {
2895      that.autoplay = true;
2896      triggerEvent('startautoplay');
2897    }
2898
2899    var _activeIndex = activeIndex;
2900
2901
2902    var frameData = activeFrame[STAGE_FRAME_KEY].data();
2903    changeAutoplay.w = waitFor(function () {
2904      ////console.log('wait for the state of the current frame');
2905      return frameData.state || _activeIndex !== activeIndex;
2906    }, function () {
2907      ////console.log('the current frame is ready');
2908      changeAutoplay.t = setTimeout(function () {
2909        ////console.log('changeAutoplay.t setTimeout', pausedAutoplayFLAG, _activeIndex !== activeIndex);
2910        if (pausedAutoplayFLAG || _activeIndex !== activeIndex) return;
2911
2912        var _nextAutoplayIndex = nextAutoplayIndex,
2913            nextFrameData = data[_nextAutoplayIndex][STAGE_FRAME_KEY].data();
2914
2915        changeAutoplay.w = waitFor(function () {
2916          ////console.log('wait for the state of the next frame');
2917          return nextFrameData.state || _nextAutoplayIndex !== nextAutoplayIndex;
2918        }, function () {
2919          if (pausedAutoplayFLAG || _nextAutoplayIndex !== nextAutoplayIndex) return;
2920          that.show(o_loop ? getDirectionSign(!o_rtl) : nextAutoplayIndex);
2921        });
2922      }, opts.autoplay);
2923    });
2924
2925  }
2926
2927  that.startAutoplay = function (interval) {
2928    if (that.autoplay) return this;
2929    pausedAutoplayFLAG = stoppedAutoplayFLAG = false;
2930    setAutoplayInterval(interval || opts.autoplay);
2931    changeAutoplay();
2932
2933    return this;
2934  };
2935
2936  that.stopAutoplay = function () {
2937    if (that.autoplay) {
2938      pausedAutoplayFLAG = stoppedAutoplayFLAG = true;
2939      changeAutoplay();
2940    }
2941    return this;
2942  };
2943
2944  that.show = function (options) {
2945    ////console.log('that.show');
2946    //////console.time('that.show prepare');
2947    var index;
2948
2949    if (typeof options !== 'object') {
2950      index = options;
2951      options = {};
2952    } else {
2953      index = options.index;
2954    }
2955
2956    index = index === '>' ? dirtyIndex + 1 : index === '<' ? dirtyIndex - 1 : index === '<<' ? 0 : index === '>>' ? size - 1 : index;
2957    index = isNaN(index) ? getIndexFromHash(index, data, true) : index;
2958    index = typeof index === 'undefined' ? activeIndex || 0 : index;
2959
2960    that.activeIndex = activeIndex = edgeIndex(index);
2961    prevIndex = getPrevIndex(activeIndex);
2962    nextIndex = getNextIndex(activeIndex);
2963    nextAutoplayIndex = normalizeIndex(activeIndex + (o_rtl ? -1 : 1));
2964    activeIndexes = [activeIndex, prevIndex, nextIndex];
2965
2966    dirtyIndex = o_loop ? index : activeIndex;
2967
2968    var diffIndex = Math.abs(lastActiveIndex - dirtyIndex),
2969        time = getNumber(options.time, function () {
2970          return Math.min(o_transitionDuration * (1 + (diffIndex - 1) / 12), o_transitionDuration * 2);
2971        }),
2972        overPos = options.overPos;
2973
2974    if (options.slow) time *= 10;
2975
2976    var _activeFrame = activeFrame;
2977    that.activeFrame = activeFrame = data[activeIndex];
2978    //////console.timeEnd('that.show prepare');
2979
2980    var silent = _activeFrame === activeFrame && !options.user;
2981
2982    //setTimeout(function () {
2983    //////console.time('unloadVideo');
2984    unloadVideo($videoPlaying, activeFrame.i !== data[normalizeIndex(repositionIndex)].i);
2985    //////console.timeEnd('unloadVideo');
2986    //////console.time('frameDraw');
2987    frameDraw(activeIndexes, 'stage');
2988    //////console.timeEnd('frameDraw');
2989    //////console.time('stageFramePosition');
2990    stageFramePosition(SLOW ? [dirtyIndex] : [dirtyIndex, getPrevIndex(dirtyIndex), getNextIndex(dirtyIndex)]);
2991    //////console.timeEnd('stageFramePosition');
2992    //////console.time('updateTouchTails');
2993    updateTouchTails('go', true);
2994    //////console.timeEnd('updateTouchTails');
2995    //////console.time('triggerEvent');
2996
2997    silent || triggerEvent('show', {
2998      user: options.user,
2999      time: time
3000    });
3001    //////console.timeEnd('triggerEvent');
3002    //}, 0);
3003
3004    //////console.time('bind onEnd');
3005
3006    pausedAutoplayFLAG = true;
3007
3008    var onEnd = that.show.onEnd = function (skipReposition) {
3009      if (onEnd.ok) return;
3010      onEnd.ok = true;
3011
3012      skipReposition || stageShaftReposition(true);
3013
3014      if (!silent) {
3015        triggerEvent('showend', {
3016          user: options.user
3017        });
3018      }
3019
3020      ////console.log('o_transition', o_transition);
3021
3022      if (!skipReposition && o_transition && o_transition !== opts.transition) {
3023        ////console.log('set transition back to: ' + o_transition);
3024        that.setOptions({transition: o_transition});
3025        o_transition = false;
3026        return;
3027      }
3028
3029      updateFotoramaState();
3030      loadImg(activeIndexes, 'stage');
3031
3032      updateTouchTails('go', false);
3033      stageWheelUpdate();
3034
3035      stageCursor();
3036      releaseAutoplay();
3037      changeAutoplay();
3038    };
3039    //////console.timeEnd('bind onEnd');
3040
3041    if (!o_fade) {
3042      //////console.time('slide');
3043      slide($stageShaft, {
3044        pos: -getPosByIndex(dirtyIndex, measures.w, opts.margin, repositionIndex),
3045        overPos: overPos,
3046        time: time,
3047        onEnd: onEnd/*,
3048        _001: true*/
3049      });
3050      //////console.timeEnd('slide');
3051    } else {
3052      var $activeFrame = activeFrame[STAGE_FRAME_KEY],
3053          $prevActiveFrame = activeIndex !== lastActiveIndex ? data[lastActiveIndex][STAGE_FRAME_KEY] : null;
3054
3055      fade($activeFrame, $prevActiveFrame, $stageFrame, {
3056        time: time,
3057        method: opts.transition,
3058        onEnd: onEnd
3059      }, fadeStack);
3060    }
3061
3062    //////console.time('arrsUpdate');
3063    arrsUpdate();
3064    //////console.timeEnd('arrsUpdate');
3065
3066    if (o_nav) {
3067      //////console.time('navUpdate');
3068      navUpdate();
3069      //////console.timeEnd('navUpdate');
3070
3071      //////console.time('slideNavShaft');
3072      var guessIndex = limitIndex(activeIndex + minMaxLimit(dirtyIndex - lastActiveIndex, -1, 1));
3073      slideNavShaft({time: time, coo: guessIndex !== activeIndex && options.coo, guessIndex: typeof options.coo !== 'undefined' ? guessIndex : activeIndex, keep: silent});
3074      //////console.timeEnd('slideNavShaft');
3075
3076      //////console.time('slideThumbBorder');
3077      if (o_navThumbs) slideThumbBorder(time);
3078      //////console.timeEnd('slideThumbBorder');
3079    }
3080
3081    //////console.time('that.show end');
3082    showedFLAG = typeof lastActiveIndex !== 'undefined' && lastActiveIndex !== activeIndex;
3083    lastActiveIndex = activeIndex;
3084    opts.hash && showedFLAG && !that.eq && setHash(activeFrame.id || activeIndex + 1);
3085    //////console.timeEnd('that.show end');
3086
3087    //////console.timeEnd('that.show');
3088
3089    return this;
3090  };
3091
3092  that.requestFullScreen = function () {
3093    if (o_allowFullScreen && !that.fullScreen) {
3094      scrollTop = $WINDOW.scrollTop();
3095      scrollLeft = $WINDOW.scrollLeft();
3096
3097      lockScroll($WINDOW);
3098
3099      updateTouchTails('x', true);
3100
3101      measuresStash = $.extend({}, measures);
3102
3103      $fotorama
3104          .addClass(fullscreenClass)
3105          .appendTo($BODY.addClass(_fullscreenClass));
3106
3107      $HTML.addClass(_fullscreenClass);
3108
3109      unloadVideo($videoPlaying, true, true);
3110
3111      that.fullScreen = true;
3112
3113      if (o_nativeFullScreen) {
3114        fullScreenApi.request(fotorama);
3115      }
3116
3117      that.resize();
3118      loadImg(activeIndexes, 'stage');
3119      updateFotoramaState();
3120
3121      triggerEvent('fullscreenenter');
3122    }
3123
3124    return this;
3125  };
3126
3127  function cancelFullScreen () {
3128    if (that.fullScreen) {
3129      that.fullScreen = false;
3130
3131      if (FULLSCREEN) {
3132        fullScreenApi.cancel(fotorama);
3133      }
3134
3135      $BODY.removeClass(_fullscreenClass);
3136      $HTML.removeClass(_fullscreenClass);
3137
3138      $fotorama
3139          .removeClass(fullscreenClass)
3140          .insertAfter($anchor);
3141
3142      measures = $.extend({}, measuresStash);
3143
3144      unloadVideo($videoPlaying, true, true);
3145
3146      updateTouchTails('x', false);
3147
3148      that.resize();
3149      loadImg(activeIndexes, 'stage');
3150
3151      lockScroll($WINDOW, scrollLeft, scrollTop);
3152
3153      triggerEvent('fullscreenexit');
3154    }
3155  }
3156
3157  that.cancelFullScreen = function () {
3158    if (o_nativeFullScreen && fullScreenApi.is()) {
3159      fullScreenApi.cancel(document);
3160    } else {
3161      cancelFullScreen();
3162    }
3163
3164    return this;
3165  };
3166
3167  that.toggleFullScreen = function () {
3168    return that[(that.fullScreen ? 'cancel' : 'request') + 'FullScreen']();
3169  };
3170
3171  addEvent(document, fullScreenApi.event, function () {
3172    if (data && !fullScreenApi.is() && !$videoPlaying) {
3173      cancelFullScreen();
3174    }
3175  });
3176
3177  that.resize = function (options) {
3178    if (!data) return this;
3179
3180    var time = arguments[1] || 0,
3181        setFLAG = arguments[2];
3182
3183    extendMeasures(!that.fullScreen ? optionsToLowerCase(options) : {width: '100%', maxwidth: null, minwidth: null, height: '100%', maxheight: null, minheight: null}, [measures, setFLAG || that.fullScreen || opts]);
3184
3185    var width = measures.width,
3186        height = measures.height,
3187        ratio = measures.ratio,
3188        windowHeight = $WINDOW.height() - (o_nav ? $nav.height() : 0);
3189
3190    if (measureIsValid(width)) {
3191      $wrap
3192          .addClass(wrapOnlyActiveClass)
3193          .css({width: width, minWidth: measures.minwidth || 0, maxWidth: measures.maxwidth || MAX_WIDTH});
3194
3195      width = measures.W = measures.w = $wrap.width();
3196      measures.nw = o_nav && numberFromWhatever(opts.navwidth, width) || width;
3197
3198      if (opts.glimpse) {
3199        // Glimpse
3200        measures.w -= Math.round((numberFromWhatever(opts.glimpse, width) || 0) * 2);
3201      }
3202
3203      $stageShaft.css({width: measures.w, marginLeft: (measures.W - measures.w) / 2});
3204
3205      //////console.log('measures.W', measures.W);
3206      //////console.log('measures.w', measures.w);
3207
3208      height = numberFromWhatever(height, windowHeight);
3209
3210      height = height || (ratio && width / ratio);
3211
3212      if (height) {
3213        width = Math.round(width);
3214        height = measures.h = Math.round(minMaxLimit(height, numberFromWhatever(measures.minheight, windowHeight), numberFromWhatever(measures.maxheight, windowHeight)));
3215
3216        $stage
3217            .stop()
3218            .animate({width: width, height: height}, time, function () {
3219              $wrap.removeClass(wrapOnlyActiveClass);
3220            });
3221
3222        stageShaftReposition();
3223
3224        if (o_nav) {
3225          $nav
3226              .stop()
3227              .animate({width: measures.nw}, time);
3228
3229          slideNavShaft({guessIndex: activeIndex, time: time, keep: true});
3230          if (o_navThumbs && frameAppend.nav) slideThumbBorder(time);
3231        }
3232
3233        measuresSetFLAG = setFLAG || true;
3234
3235        ready();
3236      }
3237    }
3238
3239    stageLeft = $stage.offset().left;
3240
3241    return this;
3242  };
3243
3244  that.setOptions = function (options) {
3245    $.extend(opts, options);
3246    reset();
3247    return this;
3248  };
3249
3250  that.shuffle = function () {
3251    data && shuffle(data) && reset();
3252    return this;
3253  };
3254
3255  function setShadow ($el, edge) {
3256    //////console.time('setShadow');
3257    if (o_shadows) {
3258      $el.removeClass(shadowsLeftClass + ' ' + shadowsRightClass);
3259      edge && !$videoPlaying && $el.addClass(edge.replace(/^|\s/g, ' ' + shadowsClass + '--'));
3260    }
3261    //////console.timeEnd('setShadow');
3262  }
3263
3264  that.destroy = function () {
3265    that.cancelFullScreen();
3266    that.stopAutoplay();
3267
3268    data = that.data = null;
3269
3270    appendElements();
3271
3272    activeIndexes = [];
3273    detachFrames(STAGE_FRAME_KEY);
3274
3275    reset.ok = false;
3276
3277    return this;
3278  };
3279
3280  that.playVideo = function () {
3281    var dataFrame = activeFrame,
3282        video = dataFrame.video,
3283        _activeIndex = activeIndex;
3284
3285    if (typeof video === 'object' && dataFrame.videoReady) {
3286      o_nativeFullScreen && that.fullScreen && that.cancelFullScreen();
3287
3288      waitFor(function () {
3289        return !fullScreenApi.is() || _activeIndex !== activeIndex;
3290      }, function () {
3291        if (_activeIndex === activeIndex) {
3292          dataFrame.$video = dataFrame.$video || $($.Fotorama.jst.video(video));
3293          dataFrame.$video.appendTo(dataFrame[STAGE_FRAME_KEY]);
3294
3295          $wrap.addClass(wrapVideoClass);
3296          $videoPlaying = dataFrame.$video;
3297
3298          stageNoMove();
3299
3300          $arrs.blur();
3301          $fullscreenIcon.blur();
3302
3303          triggerEvent('loadvideo');
3304        }
3305      });
3306    }
3307
3308    return this;
3309  };
3310
3311  that.stopVideo = function () {
3312    unloadVideo($videoPlaying, true, true);
3313    return this;
3314  };
3315
3316  function unloadVideo ($video, unloadActiveFLAG, releaseAutoplayFLAG) {
3317    if (unloadActiveFLAG) {
3318      $wrap.removeClass(wrapVideoClass);
3319      $videoPlaying = false;
3320
3321      stageNoMove();
3322    }
3323
3324    if ($video && $video !== $videoPlaying) {
3325      $video.remove();
3326      triggerEvent('unloadvideo');
3327    }
3328
3329    if (releaseAutoplayFLAG) {
3330      releaseAutoplay();
3331      changeAutoplay();
3332    }
3333  }
3334
3335  function toggleControlsClass (FLAG) {
3336    $wrap.toggleClass(wrapNoControlsClass, FLAG);
3337  }
3338
3339  function stageCursor (e) {
3340    if (stageShaftTouchTail.flow) return;
3341
3342    var x = e ? e.pageX : stageCursor.x,
3343        pointerFLAG = x && !disableDirrection(getDirection(x)) && opts.click;
3344
3345    if (stageCursor.p !== pointerFLAG
3346        && $stage.toggleClass(pointerClass, pointerFLAG)) {
3347      stageCursor.p = pointerFLAG;
3348      stageCursor.x = x;
3349    }
3350  }
3351
3352  $stage.on('mousemove', stageCursor);
3353
3354  function clickToShow (showOptions) {
3355    clearTimeout(clickToShow.t);
3356
3357    if (opts.clicktransition && opts.clicktransition !== opts.transition) {
3358      ////console.log('change transition to: ' + opts.clicktransition);
3359
3360      // this timeout is for yield events flow
3361      setTimeout(function () {
3362        // save original transition for later
3363        var _o_transition = opts.transition;
3364
3365        that.setOptions({transition: opts.clicktransition});
3366
3367        // now safe to pass base transition to o_transition, so that.show will restor it
3368        o_transition = _o_transition;
3369        // this timeout is here to prevent jerking in some browsers
3370        clickToShow.t = setTimeout(function () {
3371          that.show(showOptions);
3372        }, 10);
3373      }, 0);
3374    } else {
3375      that.show(showOptions);
3376    }
3377  }
3378
3379  function onStageTap (e, toggleControlsFLAG) {
3380    //////console.time('onStageTap');
3381    var target = e.target,
3382        $target = $(target);
3383
3384    if ($target.hasClass(videoPlayClass)) {
3385      that.playVideo();
3386    } else if (target === fullscreenIcon) {
3387      that.toggleFullScreen();
3388    } else if ($videoPlaying) {
3389      target === videoClose && unloadVideo($videoPlaying, true, true);
3390    } else {
3391      if (toggleControlsFLAG) {
3392        toggleControlsClass();
3393      } else if (opts.click) {
3394
3395        clickToShow({index: e.shiftKey || getDirectionSign(getDirection(e._x)), slow: e.altKey, user: true});
3396      }
3397    }
3398    //////console.timeEnd('onStageTap');
3399  }
3400
3401  function updateTouchTails (key, value) {
3402    stageShaftTouchTail[key] = navShaftTouchTail[key] = value;
3403  }
3404
3405  stageShaftTouchTail = moveOnTouch($stageShaft, {
3406    onStart: onTouchStart,
3407    onMove: function (e, result) {
3408      setShadow($stage, result.edge);
3409    },
3410    onTouchEnd: onTouchEnd,
3411    onEnd: function (result) {
3412      //////console.time('stageShaftTouchTail.onEnd');
3413      setShadow($stage);
3414
3415      //////console.log('result', result);
3416
3417      var toggleControlsFLAG = (MS_POINTER && !hoverFLAG || result.touch) && opts.arrows && opts.arrows !== 'always';
3418
3419      if (result.moved || (toggleControlsFLAG && result.pos !== result.newPos && !result.control)) {
3420        var index = getIndexByPos(result.newPos, measures.w, opts.margin, repositionIndex);
3421        that.show({
3422          index: index,
3423          time: o_fade ? o_transitionDuration : result.time,
3424          overPos: result.overPos,
3425          user: true
3426        });
3427      } else if (!result.aborted && !result.control) {
3428        onStageTap(result.startEvent, toggleControlsFLAG);
3429      }
3430      //////console.timeEnd('stageShaftTouchTail.onEnd');
3431    },
3432//    getPos: function () {
3433//      return -getPosByIndex(dirtyIndex, measures.w, opts.margin, repositionIndex);
3434//    },
3435    //_001: true,
3436    timeLow: 1,
3437    timeHigh: 1,
3438    friction: 2,
3439    select: '.' + selectClass + ', .' + selectClass + ' *',
3440    $wrap: $stage
3441  });
3442
3443  navShaftTouchTail = moveOnTouch($navShaft, {
3444    onStart: onTouchStart,
3445    onMove: function (e, result) {
3446      setShadow($nav, result.edge);
3447    },
3448    onTouchEnd: onTouchEnd,
3449    onEnd: function (result) {
3450
3451      function onEnd () {
3452        slideNavShaft.l = result.newPos;
3453        releaseAutoplay();
3454        changeAutoplay();
3455        thumbsDraw(result.newPos, true);
3456      }
3457
3458      if (!result.moved) {
3459        var target = result.$target.closest('.' + navFrameClass, $navShaft)[0];
3460        target && onNavFrameClick.call(target, result.startEvent);
3461      } else if (result.pos !== result.newPos) {
3462        pausedAutoplayFLAG = true;
3463        slide($navShaft, {
3464          time: result.time,
3465          pos: result.newPos,
3466          overPos: result.overPos,
3467          onEnd: onEnd
3468        });
3469        thumbsDraw(result.newPos);
3470        o_shadows && setShadow($nav, findShadowEdge(result.newPos, navShaftTouchTail.min, navShaftTouchTail.max));
3471      } else {
3472        onEnd();
3473      }
3474    },
3475    timeLow: .5,
3476    timeHigh: 2,
3477    friction: 5,
3478    $wrap: $nav
3479  });
3480
3481  stageWheelTail = wheel($stage, {
3482    shift: true,
3483    onEnd: function (e, direction) {
3484      //////console.log('wheel $stage onEnd', direction);
3485      onTouchStart();
3486      onTouchEnd();
3487      that.show({index: direction, slow: e.altKey})
3488    }
3489  });
3490
3491  navWheelTail = wheel($nav, {
3492    onEnd: function (e, direction) {
3493      //////console.log('wheel $nav onEnd', direction);
3494      onTouchStart();
3495      onTouchEnd();
3496      var newPos = stop($navShaft) + direction * .25;
3497      $navShaft.css(getTranslate(minMaxLimit(newPos, navShaftTouchTail.min, navShaftTouchTail.max)));
3498      o_shadows && setShadow($nav, findShadowEdge(newPos, navShaftTouchTail.min, navShaftTouchTail.max));
3499      navWheelTail.prevent = {'<': newPos >= navShaftTouchTail.max, '>': newPos <= navShaftTouchTail.min};
3500      clearTimeout(navWheelTail.t);
3501      navWheelTail.t = setTimeout(function () {
3502        slideNavShaft.l = newPos;
3503        thumbsDraw(newPos, true)
3504      }, TOUCH_TIMEOUT);
3505      thumbsDraw(newPos);
3506    }
3507  });
3508
3509  $wrap.hover(
3510      function () {
3511        setTimeout(function () {
3512          if (touchedFLAG) return;
3513          toggleControlsClass(!(hoverFLAG = true));
3514        }, 0);
3515      }, function () {
3516        if (!hoverFLAG) return;
3517        toggleControlsClass(!(hoverFLAG = false));
3518      }
3519  );
3520
3521  function onNavFrameClick (e) {
3522    var index = $(this).data().eq;
3523    clickToShow({index: index, slow: e.altKey, user: true, coo: e._x - $nav.offset().left});
3524  }
3525
3526  function onArrClick (e) {
3527    clickToShow({index: $arrs.index(this) ? '>' : '<', slow: e.altKey, user: true});
3528  }
3529
3530  smartClick($arrs, function (e) {
3531    stopEvent(e);
3532    onArrClick.call(this, e);
3533  }, {
3534    onStart: function () {
3535      onTouchStart();
3536      stageShaftTouchTail.control = true;
3537    },
3538    onTouchEnd: onTouchEnd
3539  });
3540
3541  function addFocusOnControls (el) {
3542    addFocus(el, function () {
3543      setTimeout(function () {
3544        lockScroll($stage);
3545      }, 0);
3546      toggleControlsClass(false);
3547    });
3548  }
3549
3550  $arrs.each(function () {
3551    addEnterUp(this, function (e) {
3552      onArrClick.call(this, e);
3553    });
3554    addFocusOnControls(this);
3555  });
3556
3557  addEnterUp(fullscreenIcon, that.toggleFullScreen);
3558  addFocusOnControls(fullscreenIcon);
3559
3560  function reset () {
3561    setData();
3562    setOptions();
3563
3564    if (!reset.i) {
3565      reset.i = true;
3566      // Only once
3567      var _startindex = opts.startindex;
3568      if (_startindex || opts.hash && location.hash) {
3569        startIndex = getIndexFromHash(_startindex || location.hash.replace(/^#/, ''), data, that.index === 0 || _startindex, _startindex);
3570      }
3571      activeIndex = repositionIndex = dirtyIndex = lastActiveIndex = startIndex = edgeIndex(startIndex) || 0;/*(o_rtl ? size - 1 : 0)*///;
3572    }
3573
3574    if (size) {
3575      if (changeToRtl()) return;
3576
3577      if ($videoPlaying) {
3578        unloadVideo($videoPlaying, true);
3579      }
3580
3581      activeIndexes = [];
3582      detachFrames(STAGE_FRAME_KEY);
3583
3584      reset.ok = true;
3585
3586      that.show({index: activeIndex, time: 0});
3587      that.resize();
3588    } else {
3589      that.destroy();
3590    }
3591  }
3592
3593  function changeToRtl () {
3594    //////console.log('changeToRtl');
3595    if (!changeToRtl.f === o_rtl) {
3596      changeToRtl.f = o_rtl;
3597      activeIndex = size - 1 - activeIndex;
3598      //////console.log('changeToRtl execute, activeIndex is', activeIndex);
3599      that.reverse();
3600
3601      return true;
3602    }
3603  }
3604
3605  $.each('load push pop shift unshift reverse sort splice'.split(' '), function (i, method) {
3606    that[method] = function () {
3607      data = data || [];
3608      if (method !== 'load') {
3609        Array.prototype[method].apply(data, arguments);
3610      } else if (arguments[0] && typeof arguments[0] === 'object' && arguments[0].length) {
3611        data = clone(arguments[0]);
3612      }
3613      reset();
3614      return that;
3615    }
3616  });
3617
3618  function ready () {
3619    if (!ready.ok) {
3620      ready.ok = true;
3621      triggerEvent('ready');
3622    }
3623  }
3624
3625  reset();
3626};
3627
3628
3629$.fn.fotorama = function (opts) {
3630  return this.each(function () {
3631    var that = this,
3632        $fotorama = $(this),
3633        fotoramaData = $fotorama.data(),
3634        fotorama = fotoramaData.fotorama;
3635
3636    if (!fotorama) {
3637      waitFor(function () {
3638        return !isHidden(that);
3639      }, function () {
3640        fotoramaData.urtext = $fotorama.html();
3641        new $.Fotorama($fotorama,
3642            /* Priority for options:
3643             * 1. <div data-loop="true"></div>
3644             * 2. $('div').fotorama({loop: false})
3645             * 3. Defaults */
3646            $.extend(
3647                {},
3648                OPTIONS,
3649                window.fotoramaDefaults,
3650                opts,
3651                fotoramaData
3652            )
3653        );
3654      });
3655    } else {
3656      fotorama.setOptions(opts, true);
3657    }
3658  });
3659};
3660$.Fotorama.instances = [];
3661
3662function calculateIndexes () {
3663  $.each($.Fotorama.instances, function (index, instance) {
3664    instance.index = index;
3665  });
3666}
3667
3668function addInstance (instance) {
3669  $.Fotorama.instances.push(instance);
3670  calculateIndexes();
3671}
3672
3673function hideInstance (instance) {
3674  $.Fotorama.instances.splice(instance.index, 1);
3675  calculateIndexes();
3676}
3677$.Fotorama.cache = {};
3678$.Fotorama.measures = {};
3679$ = $ || {};
3680$.Fotorama = $.Fotorama || {};
3681$.Fotorama.jst = $.Fotorama.jst || {};
3682
3683$.Fotorama.jst.style = function(v) {
3684var __t, __p = '', __e = _.escape;
3685__p += '.fotorama' +
3686((__t = ( v.s )) == null ? '' : __t) +
3687' .fotorama__nav--thumbs .fotorama__nav__frame{\npadding:' +
3688((__t = ( v.m )) == null ? '' : __t) +
3689'px;\nheight:' +
3690((__t = ( v.h )) == null ? '' : __t) +
3691'px}\n.fotorama' +
3692((__t = ( v.s )) == null ? '' : __t) +
3693' .fotorama__thumb-border{\nheight:' +
3694((__t = ( v.h - v.b * (v.q ? 0 : 2) )) == null ? '' : __t) +
3695'px;\nborder-width:' +
3696((__t = ( v.b )) == null ? '' : __t) +
3697'px;\nmargin-top:' +
3698((__t = ( v.m )) == null ? '' : __t) +
3699'px}';
3700return __p
3701};
3702
3703$.Fotorama.jst.video = function(v) {
3704var __t, __p = '', __e = _.escape, __j = Array.prototype.join;
3705function print() { __p += __j.call(arguments, '') }
3706__p += '<div class="fotorama__video"><iframe src="';
3707 print((v.type == 'youtube' ? v.p + 'youtube.com/embed/' + v.id +'?autoplay=1' : v.type == 'vimeo' ? v.p + 'player.vimeo.com/video/' + v.id + '?autoplay=1&badge=0' : v.id)  + (v.s && v.type != 'custom' ? '&' + v.s : '')) ;
3708__p += '" frameborder="0" allowfullscreen></iframe></div>\n';
3709return __p
3710};
3711$(function () {
3712  $('.' + _fotoramaClass + ':not([data-auto="false"])').fotorama();
3713});
3714})(window, document, location, typeof jQuery !== 'undefined' && jQuery);

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.