PageSourceSearch

https://indianhillschools.org/jquery/js/fotorama/fotorama.js

js indianhillschools.org collected 2026-10-02 11:26:21 UTC 107,080 bytes, 3,728 lines download raw bytes

vendor: 7,302 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
745: 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|Windows 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        _imgAlt = $img.attr('alt'),
1190        _imgSrc = $img.attr('src'),
1191        _thumbSrc = $child.attr('src'),
1192        _video = imgData.video,
1193        video = checkVideo ? findVideoId(_imgHref, _video === true) : false;
1194
1195    if (video) {
1196      _imgHref = false;
1197    } else {
1198      video = _video;
1199    }
1200
1201    getDimensions($img, $child, $.extend(imgData, {
1202      video: video,
1203      img: imgData.img || _imgHref || _imgSrc || _thumbSrc || imgData.alt,
1204      thumb: imgData.thumb || _thumbSrc || _imgSrc || _imgHref || imgData.alt
1205    }));
1206  }
1207
1208  function getDimensions ($img, $child, imgData) {
1209    var separateThumbFLAG = imgData.thumb && imgData.img !== imgData.thumb,
1210        width = numberFromMeasure(imgData.width || $img.attr('width')),
1211        height = numberFromMeasure(imgData.height || $img.attr('height')),
1212        altText = $img.attr('alt'),
1213        alt = $img.attr('alt'),
1214        title = $img.attr('title');
1215
1216    if (alt)
1217    {
1218        $.extend(imgData, {
1219            alt: alt
1220        });
1221    }
1222
1223    if (title) {
1224        $.extend(imgData, {
1225            title: title
1226        });
1227    }
1228      
1229    $.extend(imgData, {
1230      width: width,
1231      height: height,
1232      thumbratio: getRatio(imgData.thumbratio || (numberFromMeasure(imgData.thumbwidth || ($child && $child.attr('width')) || separateThumbFLAG || width) / numberFromMeasure(imgData.thumbheight || ($child && $child.attr('height')) || separateThumbFLAG || height)))
1233    });
1234  }
1235
1236  $el.children().each(function () {
1237    var $this = $(this),
1238        dataFrame = optionsToLowerCase($.extend($this.data(), {id: $this.attr('id')}));
1239    if ($this.is('a, img')) {
1240      getDataFromImg($this, dataFrame, true);
1241    } else if (!$this.is(':empty')) {
1242      getDimensions($this, null, $.extend(dataFrame, {
1243        html: this,
1244        _html: $this.html() // Because of IE
1245      }));
1246    } else return;
1247    data.push(dataFrame);
1248  });
1249  debugger;
1250  return data;
1251}
1252
1253function isHidden (el) {
1254  return el.offsetWidth === 0 && el.offsetHeight === 0;
1255}
1256
1257function isDetached (el) {
1258  return !$.contains(document.documentElement, el);
1259}
1260
1261function waitFor (test, fn, timeout, i) {
1262  if (!waitFor.i) {
1263    waitFor.i = 1;
1264    waitFor.ii = [true];
1265  }
1266
1267  i = i || waitFor.i;
1268
1269  if (typeof waitFor.ii[i] === 'undefined') {
1270    waitFor.ii[i] = true;
1271  }
1272
1273  if (test()) {
1274    fn();
1275  } else {
1276    waitFor.ii[i] && setTimeout(function () {
1277      waitFor.ii[i] && waitFor(test, fn, timeout, i);
1278    }, timeout || 100);
1279  }
1280
1281  return waitFor.i++;
1282}
1283
1284waitFor.stop = function (i) {
1285  waitFor.ii[i] = false;
1286};
1287
1288function setHash (hash) {
1289  //////console.time('setHash ' + hash);
1290  location.replace(location.protocol
1291      + '//'
1292      + location.host
1293      + location.pathname.replace(/^\/?/, '/')
1294      + location.search
1295      + '#' + hash);
1296  //////console.timeEnd('setHash ' + hash);
1297}
1298
1299function fit ($el, measuresToFit, method, position) {
1300  var elData = $el.data(),
1301      measures = elData.measures;
1302
1303  if (measures && (!elData.l ||
1304      elData.l.W !== measures.width ||
1305      elData.l.H !== measures.height ||
1306      elData.l.r !== measures.ratio ||
1307      elData.l.w !== measuresToFit.w ||
1308      elData.l.h !== measuresToFit.h ||
1309      elData.l.m !== method ||
1310      elData.l.p !== position)) {
1311
1312    //console.log('fit');
1313
1314    var width = measures.width,
1315        height = measures.height,
1316        ratio = measuresToFit.w / measuresToFit.h,
1317        biggerRatioFLAG = measures.ratio >= ratio,
1318        fitFLAG = method === 'scaledown',
1319        containFLAG = method === 'contain',
1320        coverFLAG = method === 'cover',
1321        pos = parsePosition(position);
1322
1323    if (biggerRatioFLAG && (fitFLAG || containFLAG) || !biggerRatioFLAG && coverFLAG) {
1324      width = minMaxLimit(measuresToFit.w, 0, fitFLAG ? width : Infinity);
1325      height = width / measures.ratio;
1326    } else if (biggerRatioFLAG && coverFLAG || !biggerRatioFLAG && (fitFLAG || containFLAG)) {
1327      height = minMaxLimit(measuresToFit.h, 0, fitFLAG ? height : Infinity);
1328      width = height * measures.ratio;
1329    }
1330
1331    $el.css({
1332      width: width,
1333      height: height,
1334      left: numberFromWhatever(pos.x, measuresToFit.w - width),
1335      top: numberFromWhatever(pos.y, measuresToFit.h- height)
1336    });
1337
1338    elData.l = {
1339      W: measures.width,
1340      H: measures.height,
1341      r: measures.ratio,
1342      w: measuresToFit.w,
1343      h: measuresToFit.h,
1344      m: method,
1345      p: position
1346    };
1347  }
1348
1349  return true;
1350}
1351
1352function setStyle ($el, style) {
1353  var el = $el[0];
1354  if (el.styleSheet) {
1355    el.styleSheet.cssText = style;
1356  } else {
1357    $el.html(style);
1358  }
1359}
1360
1361function findShadowEdge (pos, min, max) {
1362  return min === max ? false : pos <= min ? 'left' : pos >= max ? 'right' : 'left right';
1363}
1364
1365function getIndexFromHash (hash, data, ok, startindex) {
1366  if (!ok) return false;
1367  if (!isNaN(hash)) return hash - (startindex ? 0 : 1);
1368
1369  var index;
1370
1371  for (var _i = 0, _l = data.length; _i < _l; _i++) {
1372    var dataFrame = data[_i];
1373
1374    if (dataFrame.id === hash) {
1375      index = _i;
1376      break;
1377    }
1378  }
1379
1380  return index;
1381}
1382
1383function smartClick ($el, fn, _options) {
1384  _options = _options || {};
1385
1386  $el.each(function () {
1387    var $this = $(this),
1388        thisData = $this.data(),
1389        startEvent;
1390
1391    if (thisData.clickOn) return;
1392
1393    thisData.clickOn = true;
1394
1395    $.extend(touch($this, {
1396      onStart: function (e) {
1397        startEvent = e;
1398        (_options.onStart || noop).call(this, e);
1399      },
1400      onMove: _options.onMove || noop,
1401      onTouchEnd: _options.onTouchEnd || noop,
1402      onEnd: function (result) {
1403        ////console.log('smartClick → result.moved', result.moved);
1404        if (result.moved) return;
1405        fn.call(this, startEvent);
1406      }
1407    }), {noMove: true});
1408  });
1409}
1410
1411function div (classes, child) {
1412  return '<div class="' + classes + '">' + (child || '') + '</div>';
1413}
1414
1415// Fisher–Yates Shuffle
1416// http://bost.ocks.org/mike/shuffle/
1417function shuffle (array) {
1418  // While there remain elements to shuffle
1419  var l = array.length;
1420  while (l) {
1421    // Pick a remaining element
1422    var i = Math.floor(Math.random() * l--);
1423
1424    // And swap it with the current element
1425    var t = array[l];
1426    array[l] = array[i];
1427    array[i] = t;
1428  }
1429
1430  return array;
1431}
1432
1433function clone (array) {
1434  return Object.prototype.toString.call(array) == '[object Array]'
1435      && $.map(array, function (frame) {
1436       return $.extend({}, frame);
1437      });
1438}
1439
1440function lockScroll ($el, left, top) {
1441  $el
1442    .scrollLeft(left || 0)
1443    .scrollTop(top || 0);
1444}
1445
1446function optionsToLowerCase (options) {
1447  if (options) {
1448    var opts = {};
1449    $.each(options, function (key, value) {
1450      opts[key.toLowerCase()] = value;
1451    });
1452
1453    return opts;
1454  }
1455}
1456
1457function getRatio (_ratio) {
1458  if (!_ratio) return;
1459  var ratio = +_ratio;
1460  if (!isNaN(ratio)) {
1461    return ratio;
1462  } else {
1463    ratio = _ratio.split('/');
1464    return +ratio[0] / +ratio[1] || undefined;
1465  }
1466}
1467
1468function addEvent (el, e, fn, bool) {
1469  if (!e) return;
1470  el.addEventListener ? el.addEventListener(e, fn, !!bool) : el.attachEvent('on'+e, fn);
1471}
1472
1473function elIsDisabled (el) {
1474  return !!el.getAttribute('disabled');
1475}
1476
1477function disableAttr (FLAG) {
1478  return {tabindex: FLAG * -1 + '', disabled: FLAG};
1479}
1480
1481function addEnterUp (el, fn) {
1482  addEvent(el, 'keyup', function (e) {
1483    elIsDisabled(el) || e.keyCode == 13 && fn.call(el, e);
1484  });
1485}
1486
1487function addFocus (el, fn) {
1488  addEvent(el, 'focus', el.onfocusin = function (e) {
1489    fn.call(el, e);
1490  }, true);
1491}
1492
1493function stopEvent (e, stopPropagation) {
1494  e.preventDefault ? e.preventDefault() : (e.returnValue = false);
1495  stopPropagation && e.stopPropagation && e.stopPropagation();
1496}
1497
1498function getDirectionSign (forward) {
1499  return forward ? '>' : '<';
1500}
1501
1502function parsePosition (rule) {
1503  rule = (rule + '').split(/\s+/);
1504  return {
1505    x: measureIsValid(rule[0]) || FIFTYFIFTY,
1506    y: measureIsValid(rule[1]) || FIFTYFIFTY
1507  }
1508}
1509function slide ($el, options) {
1510  var elData = $el.data(),
1511      elPos = Math.round(options.pos),
1512      onEndFn = function () {
1513        elData.sliding = false;
1514        (options.onEnd || noop)();
1515      };
1516
1517  if (typeof options.overPos !== 'undefined' && options.overPos !== options.pos) {
1518    elPos = options.overPos;
1519    onEndFn = function () {
1520      slide($el, $.extend({}, options, {overPos: options.pos, time: Math.max(TRANSITION_DURATION, options.time / 2)}))
1521    };
1522  }
1523
1524  ////////console.time('var translate = $.extend');
1525  var translate = $.extend(getTranslate(elPos/*, options._001*/), options.width && {width: options.width});
1526  ////////console.timeEnd('var translate = $.extend');
1527
1528  elData.sliding = true;
1529
1530  if (CSS3) {
1531    $el.css($.extend(getDuration(options.time), translate));
1532    if (options.time > 10) {
1533      ////////console.time('afterTransition');
1534      afterTransition($el, 'transform', onEndFn, options.time);
1535      ////////console.timeEnd('afterTransition');
1536    } else {
1537      onEndFn();
1538    }
1539  } else {
1540    $el.stop().animate(translate, options.time, BEZIER, onEndFn);
1541  }
1542}
1543
1544function fade ($el1, $el2, $frames, options, fadeStack, chain) {
1545  var chainedFLAG = typeof chain !== 'undefined';
1546  if (!chainedFLAG) {
1547    fadeStack.push(arguments);
1548    Array.prototype.push.call(arguments, fadeStack.length);
1549    if (fadeStack.length > 1) return;
1550  }
1551
1552  $el1 = $el1 || $($el1);
1553  $el2 = $el2 || $($el2);
1554
1555  var _$el1 = $el1[0],
1556      _$el2 = $el2[0],
1557      crossfadeFLAG = options.method === 'crossfade',
1558      onEndFn = function () {
1559        if (!onEndFn.done) {
1560          onEndFn.done = true;
1561          var args = (chainedFLAG || fadeStack.shift()) && fadeStack.shift();
1562          args && fade.apply(this, args);
1563          (options.onEnd || noop)(!!args);
1564        }
1565      },
1566      time = options.time / (chain || 1);
1567
1568  $frames.removeClass(fadeRearClass + ' ' + fadeFrontClass);
1569
1570  $el1
1571      .stop()
1572      .addClass(fadeRearClass);
1573  $el2
1574      .stop()
1575      .addClass(fadeFrontClass);
1576
1577  crossfadeFLAG && _$el2 && $el1.fadeTo(0, 0);
1578
1579  $el1.fadeTo(crossfadeFLAG ? time : 0, 1, crossfadeFLAG && onEndFn);
1580  $el2.fadeTo(time, 0, onEndFn);
1581
1582  (_$el1 && crossfadeFLAG) || _$el2 || onEndFn();
1583}
1584var lastEvent,
1585    moveEventType,
1586    preventEvent,
1587    preventEventTimeout;
1588
1589function extendEvent (e) {
1590  var touch = (e.touches || [])[0] || e;
1591  e._x = touch.pageX;
1592  e._y = touch.clientY;
1593  e._now = $.now();
1594}
1595
1596function touch ($el, options) {
1597  var el = $el[0],
1598      tail = {},
1599      touchEnabledFLAG,
1600      startEvent,
1601      $target,
1602      controlTouch,
1603      touchFLAG,
1604      targetIsSelectFLAG,
1605      targetIsLinkFlag,
1606      tolerance,
1607      moved;
1608
1609  function onStart (e) {
1610    $target = $(e.target);
1611    tail.checked = targetIsSelectFLAG = targetIsLinkFlag = moved = false;
1612
1613    if (touchEnabledFLAG
1614        || tail.flow
1615        || (e.touches && e.touches.length > 1)
1616        || e.which > 1
1617        || (lastEvent && lastEvent.type !== e.type && preventEvent)
1618        || (targetIsSelectFLAG = options.select && $target.is(options.select, el))) return targetIsSelectFLAG;
1619
1620    touchFLAG = e.type === 'touchstart';
1621    targetIsLinkFlag = $target.is('a, a *', el);
1622    controlTouch = tail.control;
1623
1624    tolerance = (tail.noMove || tail.noSwipe || controlTouch) ? 16 : !tail.snap ? 4 : 0;
1625
1626    extendEvent(e);
1627
1628    startEvent = lastEvent = e;
1629    moveEventType = e.type.replace(/down|start/, 'move').replace(/Down/, 'Move');
1630
1631    (options.onStart || noop).call(el, e, {control: controlTouch, $target: $target});
1632
1633    touchEnabledFLAG = tail.flow = true;
1634
1635    if (!touchFLAG || tail.go) stopEvent(e);
1636  }
1637
1638  function onMove (e) {
1639    if ((e.touches && e.touches.length > 1)
1640        || (MS_POINTER && !e.isPrimary)
1641        || moveEventType !== e.type
1642        || !touchEnabledFLAG) {
1643      touchEnabledFLAG && onEnd();
1644      (options.onTouchEnd || noop)();
1645      return;
1646    }
1647
1648    extendEvent(e);
1649
1650    var xDiff = Math.abs(e._x - startEvent._x), // opt _x → _pageX
1651        yDiff = Math.abs(e._y - startEvent._y),
1652        xyDiff = xDiff - yDiff,
1653        xWin = (tail.go || tail.x || xyDiff >= 0) && !tail.noSwipe,
1654        yWin = xyDiff < 0;
1655
1656    if (touchFLAG && !tail.checked) {
1657      if (touchEnabledFLAG = xWin) {
1658        stopEvent(e);
1659      }
1660    } else {
1661      ////console.log('onMove e.preventDefault');
1662      stopEvent(e);
1663      (options.onMove || noop).call(el, e, {touch: touchFLAG});
1664    }
1665
1666    if (!moved && Math.sqrt(Math.pow(xDiff, 2) + Math.pow(yDiff, 2)) > tolerance) {
1667      moved = true;
1668    }
1669
1670    tail.checked = tail.checked || xWin || yWin;
1671  }
1672
1673  function onEnd (e) {
1674    //////console.time('touch.js onEnd');
1675
1676    (options.onTouchEnd || noop)();
1677
1678    var _touchEnabledFLAG = touchEnabledFLAG;
1679    tail.control = touchEnabledFLAG = false;
1680
1681    if (_touchEnabledFLAG) {
1682      tail.flow = false;
1683    }
1684
1685    if (!_touchEnabledFLAG || (targetIsLinkFlag && !tail.checked)) return;
1686
1687    e && stopEvent(e);
1688
1689    preventEvent = true;
1690    clearTimeout(preventEventTimeout);
1691    preventEventTimeout = setTimeout(function () {
1692      preventEvent = false;
1693    }, 1000);
1694
1695    (options.onEnd || noop).call(el, {moved: moved, $target: $target, control: controlTouch, touch: touchFLAG, startEvent: startEvent, aborted: !e || e.type === 'MSPointerCancel'});
1696    //////console.timeEnd('touch.js onEnd');
1697  }
1698
1699  function onOtherStart () {
1700    if (tail.flow) return;
1701    setTimeout(function () {
1702      tail.flow = true;
1703    }, 10);
1704  }
1705
1706  function onOtherEnd () {
1707    if (!tail.flow) return;
1708    setTimeout(function () {
1709      tail.flow = false;
1710    }, TOUCH_TIMEOUT);
1711  }
1712
1713  if (MS_POINTER) {
1714    addEvent(el, 'MSPointerDown', onStart);
1715    addEvent(document, 'MSPointerMove', onMove);
1716    addEvent(document,'MSPointerCancel', onEnd);
1717    addEvent(document, 'MSPointerUp', onEnd);
1718  } else {
1719    addEvent(el, 'touchstart', onStart);
1720    addEvent(el, 'touchmove', onMove);
1721    addEvent(el, 'touchend', onEnd);
1722
1723    addEvent(document, 'touchstart', onOtherStart);
1724    addEvent(document, 'touchend', onOtherEnd);
1725    addEvent(document, 'touchcancel', onOtherEnd);
1726
1727    $WINDOW.on('scroll', onOtherEnd);
1728
1729    $el.on('mousedown', onStart);
1730    $DOCUMENT
1731        .on('mousemove', onMove)
1732        .on('mouseup', onEnd);
1733  }
1734
1735  $el.on('click', 'a', function (e) {
1736    tail.checked && stopEvent(e);
1737  });
1738
1739  return tail;
1740}
1741
1742function moveOnTouch ($el, options) {
1743  var el = $el[0],
1744      elData = $el.data(),
1745      tail = {},
1746      startCoo,
1747      coo,
1748      startElPos,
1749      moveElPos,
1750      edge,
1751      moveTrack,
1752      startTime,
1753      endTime,
1754      min,
1755      max,
1756      snap,
1757      slowFLAG,
1758      controlFLAG,
1759      moved,
1760      tracked;
1761
1762  function startTracking (e, noStop) {
1763    tracked = true;
1764    startCoo = coo = e._x;
1765    startTime = e._now;
1766
1767    moveTrack = [
1768      [startTime, startCoo]
1769    ];
1770
1771    startElPos = moveElPos = tail.noMove || noStop ? 0 : stop($el, (options.getPos || noop)()/*, options._001*/);
1772
1773    (options.onStart || noop).call(el, e);
1774  }
1775
1776  function onStart (e, result) {
1777    min = tail.min;
1778    max = tail.max;
1779    snap = tail.snap;
1780
1781    slowFLAG = e.altKey;
1782    tracked = moved = false;
1783
1784    controlFLAG = result.control;
1785
1786    if (!controlFLAG && !elData.sliding) {
1787      startTracking(e);
1788    }
1789  }
1790
1791  function onMove (e, result) {
1792    if (!tail.noSwipe) {
1793      if (!tracked) {
1794        startTracking(e);
1795      }
1796
1797      coo = e._x;
1798
1799      moveTrack.push([e._now, coo]);
1800
1801      moveElPos = startElPos - (startCoo - coo);
1802
1803      edge = findShadowEdge(moveElPos, min, max);
1804
1805      if (moveElPos <= min) {
1806        moveElPos = edgeResistance(moveElPos, min);
1807      } else if (moveElPos >= max) {
1808        moveElPos = edgeResistance(moveElPos, max);
1809      }
1810
1811      if (!tail.noMove) {
1812        $el.css(getTranslate(moveElPos/*, options._001*/));
1813        if (!moved) {
1814          moved = true;
1815          // only for mouse
1816          result.touch || MS_POINTER || $el.addClass(grabbingClass);
1817        }
1818
1819        (options.onMove || noop).call(el, e, {pos: moveElPos, edge: edge});
1820      }
1821    }
1822  }
1823
1824  function onEnd (result) {
1825    //////console.time('moveontouch.js onEnd');
1826    if (tail.noSwipe && result.moved) return;
1827
1828    if (!tracked) {
1829      startTracking(result.startEvent, true);
1830    }
1831
1832    ////console.log('onEnd');
1833
1834    result.touch || MS_POINTER || $el.removeClass(grabbingClass);
1835
1836    endTime = $.now();
1837
1838    var _backTimeIdeal = endTime - TOUCH_TIMEOUT,
1839        _backTime,
1840        _timeDiff,
1841        _timeDiffLast,
1842        backTime = null,
1843        backCoo,
1844        virtualPos,
1845        limitPos,
1846        newPos,
1847        overPos,
1848        time = TRANSITION_DURATION,
1849        speed,
1850        friction = options.friction;
1851
1852    for (var _i = moveTrack.length - 1; _i >= 0; _i--) {
1853      _backTime = moveTrack[_i][0];
1854      _timeDiff = Math.abs(_backTime - _backTimeIdeal);
1855      if (backTime === null || _timeDiff < _timeDiffLast) {
1856        backTime = _backTime;
1857        backCoo = moveTrack[_i][1];
1858      } else if (backTime === _backTimeIdeal || _timeDiff > _timeDiffLast) {
1859        break;
1860      }
1861      _timeDiffLast = _timeDiff;
1862    }
1863
1864    newPos = minMaxLimit(moveElPos, min, max);
1865
1866    var cooDiff = backCoo - coo,
1867        forwardFLAG = cooDiff >= 0,
1868        timeDiff = endTime - backTime,
1869        longTouchFLAG = timeDiff > TOUCH_TIMEOUT,
1870        swipeFLAG = !longTouchFLAG && moveElPos !== startElPos && newPos === moveElPos;
1871
1872    if (snap) {
1873      newPos = minMaxLimit(Math[swipeFLAG ? (forwardFLAG ? 'floor' : 'ceil') : 'round'](moveElPos / snap) * snap, min, max);
1874      min = max = newPos;
1875    }
1876
1877    if (swipeFLAG && (snap || newPos === moveElPos)) {
1878      speed = -(cooDiff / timeDiff);
1879      time *= minMaxLimit(Math.abs(speed), options.timeLow, options.timeHigh);
1880      virtualPos = Math.round(moveElPos + speed * time / friction);
1881
1882      if (!snap) {
1883        newPos = virtualPos;
1884      }
1885
1886      if (!forwardFLAG && virtualPos > max || forwardFLAG && virtualPos < min) {
1887        limitPos = forwardFLAG ? min : max;
1888        overPos = virtualPos - limitPos;
1889        if (!snap) {
1890          newPos = limitPos;
1891        }
1892        overPos = minMaxLimit(newPos + overPos * .03, limitPos - 50, limitPos + 50);
1893        time = Math.abs((moveElPos - overPos) / (speed / friction));
1894      }
1895    }
1896
1897    time *= slowFLAG ? 10 : 1;
1898
1899    (options.onEnd || noop).call(el, $.extend(result, {moved: result.moved || longTouchFLAG && snap, pos: moveElPos, newPos: newPos, overPos: overPos, time: time}));
1900  }
1901
1902  tail = $.extend(touch(options.$wrap, $.extend({}, options, {
1903    onStart: onStart,
1904    onMove: onMove,
1905    onEnd: onEnd
1906  })), tail);
1907
1908  return tail;
1909}
1910function wheel ($el, options) {
1911  var el = $el[0],
1912      lockFLAG,
1913      lastDirection,
1914      lastNow,
1915      tail = {
1916        prevent: {}
1917      };
1918
1919  addEvent(el, WHEEL, function (e) {
1920    var yDelta = e.wheelDeltaY || -1 * e.deltaY || 0,
1921        xDelta = e.wheelDeltaX || -1 * e.deltaX || 0,
1922        xWin = Math.abs(xDelta) && !Math.abs(yDelta),
1923        direction = getDirectionSign(xDelta < 0),
1924        sameDirection = lastDirection === direction,
1925        now = $.now(),
1926        tooFast = now - lastNow < TOUCH_TIMEOUT;
1927
1928    lastDirection = direction;
1929    lastNow = now;
1930
1931    if (!xWin || !tail.ok || tail.prevent[direction] && !lockFLAG) {
1932      return;
1933    } else {
1934      stopEvent(e, true);
1935      if (lockFLAG && sameDirection && tooFast) {
1936        return;
1937      }
1938    }
1939
1940    if (options.shift) {
1941      lockFLAG = true;
1942      clearTimeout(tail.t);
1943      tail.t = setTimeout(function () {
1944        lockFLAG = false;
1945      }, SCROLL_LOCK_TIMEOUT);
1946    }
1947
1948    (options.onEnd || noop)(e, options.shift ? direction : xDelta);
1949
1950  });
1951
1952  return tail;
1953}
1954jQuery.Fotorama = function ($fotorama, opts) {
1955  $HTML = $('html');
1956  $BODY = $('body');
1957
1958  var that = this,
1959      stamp = $.now(),
1960      stampClass = _fotoramaClass + stamp,
1961      fotorama = $fotorama[0],
1962      data,
1963      dataFrameCount = 1,
1964      fotoramaData = $fotorama.data(),
1965      size,
1966
1967      $style = $('<style></style>'),
1968
1969      $anchor = $(div(hiddenClass)),
1970      $wrap = $(div(wrapClass)),
1971      $stage = $(div(stageClass)).appendTo($wrap),
1972      stage = $stage[0],
1973
1974      $stageShaft = $(div(stageShaftClass)).appendTo($stage),
1975      $stageFrame = $(),
1976      $arrPrev = $(div(arrClass + ' ' + arrPrevClass + buttonAttributes)),
1977      $arrNext = $(div(arrClass + ' ' + arrNextClass + buttonAttributes)),
1978      $arrs = $arrPrev.add($arrNext).appendTo($stage),
1979      $navWrap = $(div(navWrapClass)),
1980      $nav = $(div(navClass)).appendTo($navWrap),
1981      $navShaft = $(div(navShaftClass)).appendTo($nav),
1982      $navFrame,
1983      $navDotFrame = $(),
1984      $navThumbFrame = $(),
1985
1986      stageShaftData = $stageShaft.data(),
1987      navShaftData = $navShaft.data(),
1988
1989      $thumbBorder = $(div(thumbBorderClass)).appendTo($navShaft),
1990
1991      $fullscreenIcon = $(div(fullscreenIconClass + buttonAttributes)),
1992      fullscreenIcon = $fullscreenIcon[0],
1993      $videoPlay = $(div(videoPlayClass)),
1994      $videoClose = $(div(videoCloseClass)).appendTo($stage),
1995      videoClose = $videoClose[0],
1996
1997      spinner,
1998      $spinner = $(div(spinnerClass)),
1999
2000      $videoPlaying,
2001
2002      activeIndex = false,
2003      activeFrame,
2004      activeIndexes,
2005      repositionIndex,
2006      dirtyIndex,
2007      lastActiveIndex,
2008      prevIndex,
2009      nextIndex,
2010      nextAutoplayIndex,
2011      startIndex,
2012
2013      o_loop,
2014      o_nav,
2015      o_navThumbs,
2016      o_navTop,
2017      o_allowFullScreen,
2018      o_nativeFullScreen,
2019      o_fade,
2020      o_thumbSide,
2021      o_thumbSide2,
2022      o_transitionDuration,
2023      o_transition,
2024      o_shadows,
2025      o_rtl,
2026      o_keyboard,
2027      lastOptions = {},
2028
2029      measures = {},
2030      measuresSetFLAG,
2031
2032      stageShaftTouchTail = {},
2033      stageWheelTail = {},
2034      navShaftTouchTail = {},
2035      navWheelTail = {},
2036
2037      scrollTop,
2038      scrollLeft,
2039
2040      showedFLAG,
2041      pausedAutoplayFLAG,
2042      stoppedAutoplayFLAG,
2043
2044      toDeactivate = {},
2045      toDetach = {},
2046
2047      measuresStash,
2048
2049      touchedFLAG,
2050
2051      hoverFLAG,
2052
2053      navFrameKey,
2054      stageLeft = 0,
2055
2056      fadeStack = [];
2057
2058  $wrap[STAGE_FRAME_KEY] = $(div(stageFrameClass));
2059  $wrap[NAV_THUMB_FRAME_KEY] = $(div(navFrameClass + ' ' + navFrameThumbClass + buttonAttributes, div(thumbClass)));
2060  $wrap[NAV_DOT_FRAME_KEY] = $(div(navFrameClass + ' ' + navFrameDotClass + buttonAttributes, div(dotClass)));
2061
2062  toDeactivate[STAGE_FRAME_KEY] = [];
2063  toDeactivate[NAV_THUMB_FRAME_KEY] = [];
2064  toDeactivate[NAV_DOT_FRAME_KEY] = [];
2065  toDetach[STAGE_FRAME_KEY] = {};
2066
2067  $wrap
2068      .addClass(CSS3 ? wrapCss3Class : wrapCss2Class)
2069      .toggleClass(wrapNoControlsClass, !opts.controlsonstart);
2070
2071  fotoramaData.fotorama = this;
2072
2073  function checkForVideo () {
2074    $.each(data, function (i, dataFrame) {
2075      if (!dataFrame.i) {
2076        dataFrame.i = dataFrameCount++;
2077        var video = findVideoId(dataFrame.video, true);
2078        if (video) {
2079          var thumbs = {};
2080          dataFrame.video = video;
2081          if (!dataFrame.img && !dataFrame.thumb) {
2082            thumbs = getVideoThumbs(dataFrame, data, that);
2083          } else {
2084            dataFrame.thumbsReady = true;
2085          }
2086          updateData(data, {img: thumbs.img, thumb: thumbs.thumb}, dataFrame.i, that);
2087        }
2088      }
2089    });
2090  }
2091
2092  function allowKey (key) {
2093    return o_keyboard[key] || that.fullScreen;
2094  }
2095
2096  function bindGlobalEvents (FLAG) {
2097    var keydownCommon = 'keydown.' + _fotoramaClass,
2098        localStamp = _fotoramaClass + stamp,
2099        keydownLocal = 'keydown.' + localStamp,
2100        resizeLocal = 'resize.' + localStamp + ' ' + 'orientationchange.' + localStamp;
2101
2102    if (FLAG) {
2103      $DOCUMENT
2104          .on(keydownLocal, function (e) {
2105            var catched,
2106                index;
2107
2108            if ($videoPlaying && e.keyCode === 27) {
2109              catched = true;
2110              unloadVideo($videoPlaying, true, true);
2111            } else if (that.fullScreen || (opts.keyboard && !that.index)) {
2112              if (e.keyCode === 27) {
2113                catched = true;
2114                that.cancelFullScreen();
2115              } else if ((e.shiftKey && e.keyCode === 32 && allowKey('space')) || (e.keyCode === 37 && allowKey('left')) || (e.keyCode === 38 && allowKey('up'))) {
2116                index = '<';
2117              } else if ((e.keyCode === 32 && allowKey('space')) || (e.keyCode === 39 && allowKey('right')) || (e.keyCode === 40 && allowKey('down'))) {
2118                index = '>';
2119              } else if (e.keyCode === 36 && allowKey('home')) {
2120                index = '<<';
2121              } else if (e.keyCode === 35 && allowKey('end')) {
2122                index = '>>';
2123              }
2124            }
2125
2126            (catched || index) && stopEvent(e);
2127            index && that.show({index: index, slow: e.altKey, user: true});
2128          });
2129
2130      if (!that.index) {
2131        $DOCUMENT
2132            .off(keydownCommon)
2133            .on(keydownCommon, 'textarea, input, select', function (e) {
2134              !$BODY.hasClass(_fullscreenClass) && e.stopPropagation();
2135            });
2136      }
2137
2138      $WINDOW.on(resizeLocal, that.resize);
2139    } else {
2140      $DOCUMENT.off(keydownLocal);
2141      $WINDOW.off(resizeLocal);
2142    }
2143  }
2144
2145  function appendElements (FLAG) {
2146    if (FLAG === appendElements.f) return;
2147
2148    if (FLAG) {
2149      $fotorama
2150          .html('')
2151          .addClass(_fotoramaClass + ' ' + stampClass)
2152          .append($wrap)
2153          .before($style)
2154          .before($anchor);
2155
2156      addInstance(that);
2157    } else {
2158      $wrap.detach();
2159      $style.detach();
2160      $anchor.detach();
2161      $fotorama
2162          .html(fotoramaData.urtext)
2163          .removeClass(stampClass);
2164
2165      hideInstance(that);
2166    }
2167
2168    bindGlobalEvents(FLAG);
2169    appendElements.f = FLAG;
2170  }
2171
2172  function setData () {
2173    data = that.data = data || clone(opts.data) || getDataFromHtml($fotorama);
2174    size = that.size = data.length;
2175
2176    !ready.ok && opts.shuffle && shuffle(data);
2177
2178    checkForVideo();
2179
2180    activeIndex = limitIndex(activeIndex);
2181
2182    size && appendElements(true);
2183  }
2184
2185  function stageNoMove () {
2186    var _noMove = (size < 2 && !opts.enableifsingleframe) || $videoPlaying;
2187    stageShaftTouchTail.noMove = _noMove || o_fade;
2188    stageShaftTouchTail.noSwipe = _noMove || !opts.swipe;
2189
2190    !o_transition && $stageShaft.toggleClass(grabClass, !opts.click && !stageShaftTouchTail.noMove && !stageShaftTouchTail.noSwipe);
2191    MS_POINTER && $wrap.toggleClass(wrapPanYClass, !stageShaftTouchTail.noSwipe);
2192  }
2193
2194  function setAutoplayInterval (interval) {
2195    if (interval === true) interval = '';
2196    opts.autoplay = Math.max(+interval || AUTOPLAY_INTERVAL, o_transitionDuration * 1.5);
2197  }
2198
2199  /**
2200   * Options on the fly
2201   * */
2202  function setOptions () {
2203    that.options = opts = optionsToLowerCase(opts);
2204
2205    o_fade = (opts.transition === 'crossfade' || opts.transition === 'dissolve');
2206
2207    o_loop = opts.loop && (size > 2 || (o_fade && (!o_transition || o_transition !== 'slide')));
2208
2209    o_transitionDuration = +opts.transitionduration || TRANSITION_DURATION;
2210
2211    o_rtl = opts.direction === 'rtl';
2212
2213    o_keyboard = $.extend({}, opts.keyboard && KEYBOARD_OPTIONS, opts.keyboard);
2214
2215    var classes = {add: [], remove: []};
2216
2217    function addOrRemoveClass (FLAG, value) {
2218      classes[FLAG ? 'add' : 'remove'].push(value);
2219    }
2220
2221    if (size > 1 || opts.enableifsingleframe) {
2222      o_nav = opts.nav;
2223      o_navTop = opts.navposition === 'top';
2224      classes.remove.push(selectClass);
2225
2226      $arrs.toggle(!!opts.arrows);
2227    } else {
2228      o_nav = false;
2229      $arrs.hide();
2230    }
2231
2232    spinnerStop();
2233    spinner = new Spinner($.extend(spinnerDefaults, opts.spinner, spinnerOverride, {direction: o_rtl ? -1 : 1}));
2234
2235    arrsUpdate();
2236    stageWheelUpdate();
2237
2238    if (opts.autoplay) setAutoplayInterval(opts.autoplay);
2239
2240    o_thumbSide = numberFromMeasure(opts.thumbwidth) || THUMB_SIZE;
2241    o_thumbSide2 = numberFromMeasure(opts.thumbheight) || THUMB_SIZE;
2242
2243    stageWheelTail.ok = navWheelTail.ok = opts.trackpad && !SLOW;
2244
2245    stageNoMove();
2246
2247    extendMeasures(opts, [measures]);
2248
2249    o_navThumbs = o_nav === 'thumbs';
2250
2251    if (o_navThumbs) {
2252      frameDraw(size, 'navThumb');
2253
2254      $navFrame = $navThumbFrame;
2255      navFrameKey = NAV_THUMB_FRAME_KEY;
2256
2257      setStyle($style, $.Fotorama.jst.style({w: o_thumbSide, h: o_thumbSide2, b: opts.thumbborderwidth, m: opts.thumbmargin, s: stamp, q: !COMPAT}));
2258
2259      $nav
2260          .addClass(navThumbsClass)
2261          .removeClass(navDotsClass);
2262    } else if (o_nav === 'dots') {
2263      frameDraw(size, 'navDot');
2264
2265      $navFrame = $navDotFrame;
2266      navFrameKey = NAV_DOT_FRAME_KEY;
2267
2268      $nav
2269          .addClass(navDotsClass)
2270          .removeClass(navThumbsClass);
2271    } else {
2272      o_nav = false;
2273      $nav.removeClass(navThumbsClass + ' ' + navDotsClass);
2274    }
2275
2276    if (o_nav) {
2277      if (o_navTop) {
2278        $navWrap.insertBefore($stage);
2279      } else {
2280        $navWrap.insertAfter($stage);
2281      }
2282      frameAppend.nav = false;
2283      frameAppend($navFrame, $navShaft, 'nav');
2284    }
2285
2286    o_allowFullScreen = opts.allowfullscreen;
2287
2288    if (o_allowFullScreen) {
2289      $fullscreenIcon.prependTo($stage);
2290      o_nativeFullScreen = FULLSCREEN && o_allowFullScreen === 'native';
2291    } else {
2292      $fullscreenIcon.detach();
2293      o_nativeFullScreen = false;
2294    }
2295
2296    addOrRemoveClass(o_fade, wrapFadeClass);
2297    addOrRemoveClass(!o_fade, wrapSlideClass);
2298    addOrRemoveClass(!opts.captions, wrapNoCaptionsClass);
2299    addOrRemoveClass(o_rtl, wrapRtlClass);
2300    addOrRemoveClass(opts.arrows !== 'always', wrapToggleArrowsClass);
2301
2302    o_shadows = opts.shadows && !SLOW;
2303    addOrRemoveClass(!o_shadows, wrapNoShadowsClass);
2304
2305    $wrap
2306        .addClass(classes.add.join(' '))
2307        .removeClass(classes.remove.join(' '));
2308
2309    lastOptions = $.extend({}, opts);
2310  }
2311
2312  function normalizeIndex (index) {
2313    return index < 0 ? (size + (index % size)) % size : index >= size ? index % size : index;
2314  }
2315
2316  function limitIndex (index) {
2317    return minMaxLimit(index, 0, size - 1);
2318  }
2319
2320  function edgeIndex (index) {
2321    return o_loop ? normalizeIndex(index) : limitIndex(index);
2322  }
2323
2324  function getPrevIndex (index) {
2325    return index > 0 || o_loop ? index - 1 : false;
2326  }
2327
2328  function getNextIndex (index) {
2329    return index < size - 1 || o_loop ? index + 1 : false;
2330  }
2331
2332  function setStageShaftMinmaxAndSnap () {
2333    stageShaftTouchTail.min = o_loop ? -Infinity : -getPosByIndex(size - 1, measures.w, opts.margin, repositionIndex);
2334    stageShaftTouchTail.max = o_loop ? Infinity : -getPosByIndex(0, measures.w, opts.margin, repositionIndex);
2335    stageShaftTouchTail.snap = measures.w + opts.margin;
2336  }
2337
2338  function setNavShaftMinMax () {
2339    ////////console.log('setNavShaftMinMax', measures.nw);
2340    navShaftTouchTail.min = Math.min(0, measures.nw - $navShaft.width());
2341    navShaftTouchTail.max = 0;
2342    $navShaft.toggleClass(grabClass, !(navShaftTouchTail.noMove = navShaftTouchTail.min === navShaftTouchTail.max));
2343  }
2344
2345  function eachIndex (indexes, type, fn) {
2346    if (typeof indexes === 'number') {
2347      indexes = new Array(indexes);
2348      var rangeFLAG = true;
2349    }
2350    return $.each(indexes, function (i, index) {
2351      if (rangeFLAG) index = i;
2352      if (typeof index === 'number') {
2353        var dataFrame = data[normalizeIndex(index)];
2354
2355        if (dataFrame) {
2356          var key = '$' + type + 'Frame',
2357              $frame = dataFrame[key];
2358
2359          fn.call(this, i, index, dataFrame, $frame, key, $frame && $frame.data());
2360        }
2361      }
2362    });
2363  }
2364
2365  function setMeasures (width, height, ratio, index) {
2366    if (!measuresSetFLAG || (measuresSetFLAG === '*' && index === startIndex)) {
2367
2368      //////console.log('setMeasures', index, opts.width, opts.height);
2369
2370      width = measureIsValid(opts.width) || measureIsValid(width) || WIDTH;
2371      height = measureIsValid(opts.height) || measureIsValid(height) || HEIGHT;
2372      that.resize({
2373        width: width,
2374        ratio: opts.ratio || ratio || width / height
2375      }, 0, index !== startIndex && '*');
2376    }
2377  }
2378
2379  function loadImg (indexes, type, specialMeasures, method, position, again) {
2380    eachIndex(indexes, type, function (i, index, dataFrame, $frame, key, frameData) {
2381
2382      if (!$frame) return;
2383
2384      var fullFLAG = that.fullScreen && dataFrame.full && dataFrame.full !== dataFrame.img && !frameData.$full && type === 'stage';
2385
2386      if (frameData.$img && !again && !fullFLAG) return;
2387
2388      var img = new Image(),
2389          $img = $(img),
2390          imgData = $img.data();
2391
2392      frameData[fullFLAG ? '$full' : '$img'] = $img;
2393
2394      var srcKey = type === 'stage' ? (fullFLAG ? 'full' : 'img') : 'thumb',
2395          src = dataFrame[srcKey],
2396          dummy = fullFLAG ? null : dataFrame[type === 'stage' ? 'thumb' : 'img'];
2397
2398      if (type === 'navThumb') $frame = frameData.$wrap;
2399
2400      function triggerTriggerEvent (event) {
2401        var _index = normalizeIndex(index);
2402        triggerEvent(event, {
2403          index: _index,
2404          src: src,
2405          frame: data[_index]
2406        });
2407      }
2408
2409      function error () {
2410        $img.remove();
2411
2412        $.Fotorama.cache[src] = 'error';
2413
2414        if ((!dataFrame.html || type !== 'stage') && dummy && dummy !== src) {
2415          dataFrame[srcKey] = src = dummy;
2416          loadImg([index], type, specialMeasures, method, position, true);
2417        } else {
2418          if (src && !dataFrame.html && !fullFLAG) {
2419            $frame
2420                .trigger('f:error')
2421                .removeClass(loadingClass)
2422                .addClass(errorClass);
2423
2424            triggerTriggerEvent('error');
2425          } else if (type === 'stage') {
2426            $frame
2427                .trigger('f:load')
2428                .removeClass(loadingClass + ' ' + errorClass)
2429                .addClass(loadedClass);
2430
2431            triggerTriggerEvent('load');
2432            setMeasures();
2433          }
2434
2435          frameData.state = 'error';
2436
2437          if (size > 1 && data[index] === dataFrame && !dataFrame.html && !dataFrame.deleted && !dataFrame.video && !fullFLAG) {
2438            dataFrame.deleted = true;
2439            that.splice(index, 1);
2440          }
2441        }
2442      }
2443
2444      function loaded () {
2445        //////console.log('loaded: ' + src);
2446
2447        ////console.log('$.Fotorama.measures[src]', $.Fotorama.measures[src]);
2448
2449        $.Fotorama.measures[src] = imgData.measures = $.Fotorama.measures[src] || {
2450          width: img.width,
2451          height: img.height,
2452          ratio: img.width / img.height
2453        };
2454
2455        setMeasures(imgData.measures.width, imgData.measures.height, imgData.measures.ratio, index);
2456
2457        $img
2458            .off('load error')
2459            .addClass(imgClass + (fullFLAG ? ' ' + imgFullClass : ''))
2460            .prependTo($frame);
2461
2462        fit($img, ($.isFunction(specialMeasures) ? specialMeasures() : specialMeasures) || measures, method || dataFrame.fit || opts.fit, position || dataFrame.position || opts.position);
2463
2464        $.Fotorama.cache[src] = frameData.state = 'loaded';
2465
2466        setTimeout(function () {
2467          $frame
2468              .trigger('f:load')
2469              .removeClass(loadingClass + ' ' + errorClass)
2470              .addClass(loadedClass + ' ' + (fullFLAG ? loadedFullClass : loadedImgClass));
2471
2472          if (type === 'stage') {
2473            triggerTriggerEvent('load');
2474          } else if (dataFrame.thumbratio === AUTO || !dataFrame.thumbratio && opts.thumbratio === AUTO) {
2475            // danger! reflow for all thumbnails
2476            dataFrame.thumbratio = imgData.measures.ratio;
2477            reset();
2478          }
2479        }, 0);
2480      }
2481
2482      if (!src) {
2483        error();
2484        return;
2485      }
2486
2487      function waitAndLoad () {
2488        var _i = 10;
2489        waitFor(function () {
2490          return !touchedFLAG || !_i-- && !SLOW;
2491        }, function () {
2492          loaded();
2493        });
2494      }
2495
2496      if (!$.Fotorama.cache[src]) {
2497        $.Fotorama.cache[src] = '*';
2498
2499        $img
2500            .on('load', waitAndLoad)
2501            .on('error', error);
2502      } else {
2503        (function justWait () {
2504          if ($.Fotorama.cache[src] === 'error') {
2505            error();
2506          } else if ($.Fotorama.cache[src] === 'loaded') {
2507            ////console.log('take from cache: ' + src);
2508            setTimeout(waitAndLoad, 0);
2509          } else {
2510            setTimeout(justWait, 100);
2511          }
2512        })();
2513      }
2514
2515      frameData.state = '';
2516
2517      if ( frameData.data.hasOwnProperty('alt') ) {
2518          img.alt= frameData.data.alt;
2519      }
2520      if ( frameData.data.hasOwnProperty('title') ) {
2521      img.title= frameData.data.title;
2522      }
2523      img.src = src;
2524    });
2525  }
2526
2527  function spinnerSpin ($el) {
2528    $spinner.append(spinner.spin().el).appendTo($el);
2529  }
2530
2531  function spinnerStop () {
2532    $spinner.detach();
2533    spinner && spinner.stop();
2534  }
2535
2536  function updateFotoramaState () {
2537    var $frame = activeFrame[STAGE_FRAME_KEY];
2538
2539    if ($frame && !$frame.data().state) {
2540      spinnerSpin($frame);
2541      $frame.on('f:load f:error', function () {
2542        $frame.off('f:load f:error');
2543        spinnerStop();
2544      });
2545    }
2546  }
2547
2548  function addNavFrameEvents (frame) {
2549    addEnterUp(frame, onNavFrameClick);
2550    addFocus(frame, function () {
2551
2552      setTimeout(function () {
2553        lockScroll($nav);
2554      }, 0);
2555      slideNavShaft({time: o_transitionDuration, guessIndex: $(this).data().eq, minMax: navShaftTouchTail});
2556    });
2557  }
2558
2559  function frameDraw (indexes, type) {
2560    eachIndex(indexes, type, function (i, index, dataFrame, $frame, key, frameData) {
2561      if ($frame) return;
2562
2563      $frame = dataFrame[key] = $wrap[key].clone();
2564      frameData = $frame.data();
2565      frameData.data = dataFrame;
2566      var frame = $frame[0];
2567
2568      if (type === 'stage') {
2569
2570        if (dataFrame.html) {
2571          $('<div class="' + htmlClass + '"></div>')
2572              .append(
2573                  dataFrame._html ? $(dataFrame.html)
2574                      .removeAttr('id')
2575                      .html(dataFrame._html) // Because of IE
2576                  : dataFrame.html
2577              )
2578              .appendTo($frame);
2579        }
2580
2581        dataFrame.caption && $(div(captionClass, div(captionWrapClass, dataFrame.caption))).appendTo($frame);
2582
2583        dataFrame.video && $frame
2584          .addClass(stageFrameVideoClass)
2585          .append($videoPlay.clone());
2586
2587        // This solves tabbing problems
2588        addFocus(frame, function () {
2589          setTimeout(function () {
2590            lockScroll($stage);
2591          }, 0);
2592          clickToShow({index: frameData.eq, user: true});
2593        });
2594
2595        $stageFrame = $stageFrame.add($frame);
2596      } else if (type === 'navDot') {
2597        addNavFrameEvents(frame);
2598        $navDotFrame = $navDotFrame.add($frame);
2599      } else if (type === 'navThumb') {
2600        addNavFrameEvents(frame);
2601        frameData.$wrap = $frame.children(':first');
2602        $navThumbFrame = $navThumbFrame.add($frame);
2603        if (dataFrame.video) {
2604          frameData.$wrap.append($videoPlay.clone());
2605        }
2606      }
2607    });
2608  }
2609
2610  function callFit ($img, measuresToFit, method, position) {
2611    return $img && $img.length && fit($img, measuresToFit, method, position);
2612  }
2613
2614  function stageFramePosition (indexes) {
2615    eachIndex(indexes, 'stage', function (i, index, dataFrame, $frame, key, frameData) {
2616      if (!$frame) return;
2617
2618      var normalizedIndex = normalizeIndex(index),
2619          method = dataFrame.fit || opts.fit,
2620          position = dataFrame.position || opts.position;
2621      frameData.eq = normalizedIndex;
2622
2623      toDetach[STAGE_FRAME_KEY][normalizedIndex] = $frame.css($.extend({left: o_fade ? 0 : getPosByIndex(index, measures.w, opts.margin, repositionIndex)}, o_fade && getDuration(0)));
2624
2625      if (isDetached($frame[0])) {
2626        $frame.appendTo($stageShaft);
2627        unloadVideo(dataFrame.$video);
2628      }
2629
2630      callFit(frameData.$img, measures, method, position);
2631      callFit(frameData.$full, measures, method, position);
2632    });
2633  }
2634
2635  function thumbsDraw (pos, loadFLAG) {
2636    if (o_nav !== 'thumbs' || isNaN(pos)) return;
2637
2638    var leftLimit = -pos,
2639        rightLimit = -pos + measures.nw;
2640
2641    $navThumbFrame.each(function () {
2642      var $this = $(this),
2643          thisData = $this.data(),
2644          eq = thisData.eq,
2645          getSpecialMeasures = function () {
2646            return {
2647              h: o_thumbSide2,
2648              w: thisData.w
2649            }
2650          },
2651          specialMeasures = getSpecialMeasures(),
2652          dataFrame = data[eq] || {},
2653          method = dataFrame.thumbfit || opts.thumbfit,
2654          position = dataFrame.thumbposition || opts.thumbposition;
2655
2656      specialMeasures.w = thisData.w;
2657
2658      if (thisData.l + thisData.w < leftLimit
2659          || thisData.l > rightLimit
2660          || callFit(thisData.$img, specialMeasures, method, position)) return;
2661
2662      loadFLAG && loadImg([eq], 'navThumb', getSpecialMeasures, method, position);
2663    });
2664  }
2665
2666  function frameAppend ($frames, $shaft, type) {
2667    if (!frameAppend[type]) {
2668
2669      var thumbsFLAG = type === 'nav' && o_navThumbs,
2670          left = 0;
2671
2672      $shaft.append(
2673        $frames
2674            .filter(function () {
2675              var actual,
2676                  $this = $(this),
2677                  frameData = $this.data();
2678              for (var _i = 0, _l = data.length; _i < _l; _i++) {
2679                if (frameData.data === data[_i]) {
2680                  actual = true;
2681                  frameData.eq = _i;
2682                  break;
2683                }
2684              }
2685              return actual || $this.remove() && false;
2686            })
2687            .sort(function (a, b) {
2688              return $(a).data().eq - $(b).data().eq;
2689            })
2690            .each(function () {
2691
2692              if (!thumbsFLAG) return;
2693
2694              var $this = $(this),
2695                  frameData = $this.data(),
2696                  thumbwidth = Math.round(o_thumbSide2 * frameData.data.thumbratio) || o_thumbSide;
2697
2698              frameData.l = left;
2699              frameData.w = thumbwidth;
2700
2701              $this.css({width: thumbwidth});
2702
2703              left += thumbwidth + opts.thumbmargin;
2704            })
2705      );
2706
2707      frameAppend[type] = true;
2708    }
2709  }
2710
2711  function getDirection (x) {
2712    return x - stageLeft > measures.w / 3;
2713  }
2714
2715  function disableDirrection (i) {
2716    return !o_loop && (!(activeIndex + i) || !(activeIndex - size + i)) && !$videoPlaying;
2717  }
2718
2719  function arrsUpdate () {
2720    var disablePrev = disableDirrection(0),
2721        disableNext = disableDirrection(1);
2722    $arrPrev
2723        .toggleClass(arrDisabledClass, disablePrev)
2724        .attr(disableAttr(disablePrev));
2725    $arrNext
2726        .toggleClass(arrDisabledClass, disableNext)
2727        .attr(disableAttr(disableNext));
2728  }
2729
2730  function stageWheelUpdate () {
2731    if (stageWheelTail.ok) {
2732      stageWheelTail.prevent = {'<': disableDirrection(0), '>': disableDirrection(1)};
2733    }
2734  }
2735
2736  function getNavFrameBounds ($navFrame) {
2737    var navFrameData = $navFrame.data(),
2738        left,
2739        width;
2740
2741    if (o_navThumbs) {
2742      left = navFrameData.l;
2743      width = navFrameData.w;
2744    } else {
2745      left = $navFrame.position().left;
2746      width = $navFrame.width();
2747    }
2748
2749    return {
2750      c: left + width / 2,
2751      min: -left + opts.thumbmargin * 10,
2752      max: -left + measures.w - width - opts.thumbmargin * 10
2753    };
2754  }
2755
2756  function slideThumbBorder (time) {
2757    var navFrameData = activeFrame[navFrameKey].data();
2758    slide($thumbBorder, {
2759      time: time * 1.2,
2760      pos: navFrameData.l,
2761      width: navFrameData.w - opts.thumbborderwidth * 2
2762    });
2763  }
2764
2765  function slideNavShaft (options) {
2766    ////console.log('slideNavShaft', options.guessIndex, options.keep, slideNavShaft.l);
2767    var $guessNavFrame = data[options.guessIndex][navFrameKey];
2768    if ($guessNavFrame) {
2769      var overflowFLAG = navShaftTouchTail.min !== navShaftTouchTail.max,
2770          minMax = options.minMax || overflowFLAG && getNavFrameBounds(activeFrame[navFrameKey]),
2771          l = overflowFLAG && (options.keep && slideNavShaft.l ? slideNavShaft.l : minMaxLimit((options.coo || measures.nw / 2) - getNavFrameBounds($guessNavFrame).c, minMax.min, minMax.max)),
2772          pos = overflowFLAG && minMaxLimit(l, navShaftTouchTail.min, navShaftTouchTail.max),
2773          time = options.time * 1.1;
2774
2775      slide($navShaft, {
2776        time: time,
2777        pos: pos || 0,
2778        onEnd: function () {
2779          thumbsDraw(pos, true);
2780        }
2781      });
2782
2783      //if (time) thumbsDraw(pos);
2784
2785      setShadow($nav, findShadowEdge(pos, navShaftTouchTail.min, navShaftTouchTail.max));
2786      slideNavShaft.l = l;
2787    }
2788  }
2789
2790  function navUpdate () {
2791    deactivateFrames(navFrameKey);
2792    toDeactivate[navFrameKey].push(activeFrame[navFrameKey].addClass(activeClass));
2793  }
2794
2795  function deactivateFrames (key) {
2796    var _toDeactivate = toDeactivate[key];
2797
2798    while (_toDeactivate.length) {
2799      _toDeactivate.shift().removeClass(activeClass);
2800    }
2801  }
2802
2803  function detachFrames (key) {
2804    var _toDetach = toDetach[key];
2805
2806    //////console.log('_toDetach', _toDetach);
2807    //////console.log('activeIndexes', activeIndexes);
2808
2809    $.each(activeIndexes, function (i, index) {
2810      delete _toDetach[normalizeIndex(index)];
2811    });
2812
2813    $.each(_toDetach, function (index, $frame) {
2814      delete _toDetach[index];
2815      //////console.log('Detach', index);
2816      $frame.detach();
2817    });
2818  }
2819
2820  function stageShaftReposition (skipOnEnd) {
2821
2822    repositionIndex = dirtyIndex = activeIndex;
2823
2824    var $frame = activeFrame[STAGE_FRAME_KEY];
2825
2826    if ($frame) {
2827      deactivateFrames(STAGE_FRAME_KEY);
2828      toDeactivate[STAGE_FRAME_KEY].push($frame.addClass(activeClass));
2829
2830      skipOnEnd || that.show.onEnd(true);
2831      stop($stageShaft, 0, true);
2832
2833      detachFrames(STAGE_FRAME_KEY);
2834      stageFramePosition(activeIndexes);
2835      setStageShaftMinmaxAndSnap();
2836      setNavShaftMinMax();
2837    }
2838  }
2839
2840  function extendMeasures (options, measuresArray) {
2841    if (!options) return;
2842
2843    $.each(measuresArray, function (i, measures) {
2844      if (!measures) return;
2845
2846      $.extend(measures, {
2847        width: options.width || measures.width,
2848        height: options.height,
2849        minwidth: options.minwidth,
2850        maxwidth: options.maxwidth,
2851        minheight: options.minheight,
2852        maxheight: options.maxheight,
2853        ratio: getRatio(options.ratio)
2854      })
2855    });
2856  }
2857
2858  function triggerEvent (event, extra) {
2859    $fotorama.trigger(_fotoramaClass + ':' + event, [that, extra]);
2860  }
2861
2862  function onTouchStart () {
2863    clearTimeout(onTouchEnd.t);
2864    touchedFLAG = 1;
2865
2866    if (opts.stopautoplayontouch) {
2867      that.stopAutoplay();
2868    } else {
2869      pausedAutoplayFLAG = true;
2870    }
2871  }
2872
2873  function onTouchEnd () {
2874    if (!touchedFLAG) return;
2875    if (!opts.stopautoplayontouch) {
2876      releaseAutoplay();
2877      changeAutoplay();
2878    }
2879
2880    onTouchEnd.t = setTimeout(function () {
2881      touchedFLAG = 0;
2882    }, TRANSITION_DURATION + TOUCH_TIMEOUT);
2883    //////console.timeEnd('onTouchEnd');
2884  }
2885
2886  function releaseAutoplay () {
2887    ////console.log('releaseAutoplay');
2888    pausedAutoplayFLAG = !!($videoPlaying || stoppedAutoplayFLAG);
2889  }
2890
2891  function changeAutoplay () {
2892    ////console.log('changeAutoplay');
2893
2894    clearTimeout(changeAutoplay.t);
2895    waitFor.stop(changeAutoplay.w);
2896
2897    if (!opts.autoplay || pausedAutoplayFLAG) {
2898      if (that.autoplay) {
2899        that.autoplay = false;
2900        triggerEvent('stopautoplay');
2901      }
2902
2903      return;
2904    }
2905
2906    ////console.log('changeAutoplay continue');
2907
2908    if (!that.autoplay) {
2909      that.autoplay = true;
2910      triggerEvent('startautoplay');
2911    }
2912
2913    var _activeIndex = activeIndex;
2914
2915
2916    var frameData = activeFrame[STAGE_FRAME_KEY].data();
2917    changeAutoplay.w = waitFor(function () {
2918      ////console.log('wait for the state of the current frame');
2919      return frameData.state || _activeIndex !== activeIndex;
2920    }, function () {
2921      ////console.log('the current frame is ready');
2922      changeAutoplay.t = setTimeout(function () {
2923        ////console.log('changeAutoplay.t setTimeout', pausedAutoplayFLAG, _activeIndex !== activeIndex);
2924        if (pausedAutoplayFLAG || _activeIndex !== activeIndex) return;
2925
2926        var _nextAutoplayIndex = nextAutoplayIndex,
2927            nextFrameData = data[_nextAutoplayIndex][STAGE_FRAME_KEY].data();
2928
2929        changeAutoplay.w = waitFor(function () {
2930          ////console.log('wait for the state of the next frame');
2931          return nextFrameData.state || _nextAutoplayIndex !== nextAutoplayIndex;
2932        }, function () {
2933          if (pausedAutoplayFLAG || _nextAutoplayIndex !== nextAutoplayIndex) return;
2934          that.show(o_loop ? getDirectionSign(!o_rtl) : nextAutoplayIndex);
2935        });
2936      }, opts.autoplay);
2937    });
2938
2939  }
2940
2941  that.startAutoplay = function (interval) {
2942    if (that.autoplay) return this;
2943    pausedAutoplayFLAG = stoppedAutoplayFLAG = false;
2944    setAutoplayInterval(interval || opts.autoplay);
2945    changeAutoplay();
2946
2947    return this;
2948  };
2949
2950  that.stopAutoplay = function () {
2951    if (that.autoplay) {
2952      pausedAutoplayFLAG = stoppedAutoplayFLAG = true;
2953      changeAutoplay();
2954    }
2955    return this;
2956  };
2957
2958  that.show = function (options) {
2959    ////console.log('that.show');
2960    //////console.time('that.show prepare');
2961    var index;
2962
2963    if (typeof options !== 'object') {
2964      index = options;
2965      options = {};
2966    } else {
2967      index = options.index;
2968    }
2969
2970    index = index === '>' ? dirtyIndex + 1 : index === '<' ? dirtyIndex - 1 : index === '<<' ? 0 : index === '>>' ? size - 1 : index;
2971    index = isNaN(index) ? getIndexFromHash(index, data, true) : index;
2972    index = typeof index === 'undefined' ? activeIndex || 0 : index;
2973
2974    that.activeIndex = activeIndex = edgeIndex(index);
2975    prevIndex = getPrevIndex(activeIndex);
2976    nextIndex = getNextIndex(activeIndex);
2977    nextAutoplayIndex = normalizeIndex(activeIndex + (o_rtl ? -1 : 1));
2978    activeIndexes = [activeIndex, prevIndex, nextIndex];
2979
2980    dirtyIndex = o_loop ? index : activeIndex;
2981
2982    var diffIndex = Math.abs(lastActiveIndex - dirtyIndex),
2983        time = getNumber(options.time, function () {
2984          return Math.min(o_transitionDuration * (1 + (diffIndex - 1) / 12), o_transitionDuration * 2);
2985        }),
2986        overPos = options.overPos;
2987
2988    if (options.slow) time *= 10;
2989
2990    var _activeFrame = activeFrame;
2991    that.activeFrame = activeFrame = data[activeIndex];
2992    //////console.timeEnd('that.show prepare');
2993
2994    var silent = _activeFrame === activeFrame && !options.user;
2995
2996    //setTimeout(function () {
2997    //////console.time('unloadVideo');
2998    unloadVideo($videoPlaying, activeFrame.i !== data[normalizeIndex(repositionIndex)].i);
2999    //////console.timeEnd('unloadVideo');
3000    //////console.time('frameDraw');
3001    frameDraw(activeIndexes, 'stage');
3002    //////console.timeEnd('frameDraw');
3003    //////console.time('stageFramePosition');
3004    stageFramePosition(SLOW ? [dirtyIndex] : [dirtyIndex, getPrevIndex(dirtyIndex), getNextIndex(dirtyIndex)]);
3005    //////console.timeEnd('stageFramePosition');
3006    //////console.time('updateTouchTails');
3007    updateTouchTails('go', true);
3008    //////console.timeEnd('updateTouchTails');
3009    //////console.time('triggerEvent');
3010
3011    silent || triggerEvent('show', {
3012      user: options.user,
3013      time: time
3014    });
3015    //////console.timeEnd('triggerEvent');
3016    //}, 0);
3017
3018    //////console.time('bind onEnd');
3019
3020    pausedAutoplayFLAG = true;
3021
3022    var onEnd = that.show.onEnd = function (skipReposition) {
3023      if (onEnd.ok) return;
3024      onEnd.ok = true;
3025
3026      skipReposition || stageShaftReposition(true);
3027
3028      if (!silent) {
3029        triggerEvent('showend', {
3030          user: options.user
3031        });
3032      }
3033
3034      ////console.log('o_transition', o_transition);
3035
3036      if (!skipReposition && o_transition && o_transition !== opts.transition) {
3037        ////console.log('set transition back to: ' + o_transition);
3038        that.setOptions({transition: o_transition});
3039        o_transition = false;
3040        return;
3041      }
3042
3043      updateFotoramaState();
3044      loadImg(activeIndexes, 'stage');
3045
3046      updateTouchTails('go', false);
3047      stageWheelUpdate();
3048
3049      stageCursor();
3050      releaseAutoplay();
3051      changeAutoplay();
3052    };
3053    //////console.timeEnd('bind onEnd');
3054
3055    if (!o_fade) {
3056      //////console.time('slide');
3057      slide($stageShaft, {
3058        pos: -getPosByIndex(dirtyIndex, measures.w, opts.margin, repositionIndex),
3059        overPos: overPos,
3060        time: time,
3061        onEnd: onEnd/*,
3062        _001: true*/
3063      });
3064      //////console.timeEnd('slide');
3065    } else {
3066      var $activeFrame = activeFrame[STAGE_FRAME_KEY],
3067          $prevActiveFrame = activeIndex !== lastActiveIndex ? data[lastActiveIndex][STAGE_FRAME_KEY] : null;
3068
3069      fade($activeFrame, $prevActiveFrame, $stageFrame, {
3070        time: time,
3071        method: opts.transition,
3072        onEnd: onEnd
3073      }, fadeStack);
3074    }
3075
3076    //////console.time('arrsUpdate');
3077    arrsUpdate();
3078    //////console.timeEnd('arrsUpdate');
3079
3080    if (o_nav) {
3081      //////console.time('navUpdate');
3082      navUpdate();
3083      //////console.timeEnd('navUpdate');
3084
3085      //////console.time('slideNavShaft');
3086      var guessIndex = limitIndex(activeIndex + minMaxLimit(dirtyIndex - lastActiveIndex, -1, 1));
3087      slideNavShaft({time: time, coo: guessIndex !== activeIndex && options.coo, guessIndex: typeof options.coo !== 'undefined' ? guessIndex : activeIndex, keep: silent});
3088      //////console.timeEnd('slideNavShaft');
3089
3090      //////console.time('slideThumbBorder');
3091      if (o_navThumbs) slideThumbBorder(time);
3092      //////console.timeEnd('slideThumbBorder');
3093    }
3094
3095    //////console.time('that.show end');
3096    showedFLAG = typeof lastActiveIndex !== 'undefined' && lastActiveIndex !== activeIndex;
3097    lastActiveIndex = activeIndex;
3098    opts.hash && showedFLAG && !that.eq && setHash(activeFrame.id || activeIndex + 1);
3099    //////console.timeEnd('that.show end');
3100
3101    //////console.timeEnd('that.show');
3102
3103    return this;
3104  };
3105
3106  that.requestFullScreen = function () {
3107    if (o_allowFullScreen && !that.fullScreen) {
3108      scrollTop = $WINDOW.scrollTop();
3109      scrollLeft = $WINDOW.scrollLeft();
3110
3111      lockScroll($WINDOW);
3112
3113      updateTouchTails('x', true);
3114
3115      measuresStash = $.extend({}, measures);
3116
3117      $fotorama
3118          .addClass(fullscreenClass)
3119          .appendTo($BODY.addClass(_fullscreenClass));
3120
3121      $HTML.addClass(_fullscreenClass);
3122
3123      unloadVideo($videoPlaying, true, true);
3124
3125      that.fullScreen = true;
3126
3127      if (o_nativeFullScreen) {
3128        fullScreenApi.request(fotorama);
3129      }
3130
3131      that.resize();
3132      loadImg(activeIndexes, 'stage');
3133      updateFotoramaState();
3134
3135      triggerEvent('fullscreenenter');
3136    }
3137
3138    return this;
3139  };
3140
3141  function cancelFullScreen () {
3142    if (that.fullScreen) {
3143      that.fullScreen = false;
3144
3145      if (FULLSCREEN) {
3146        fullScreenApi.cancel(fotorama);
3147      }
3148
3149      $BODY.removeClass(_fullscreenClass);
3150      $HTML.removeClass(_fullscreenClass);
3151
3152      $fotorama
3153          .removeClass(fullscreenClass)
3154          .insertAfter($anchor);
3155
3156      measures = $.extend({}, measuresStash);
3157
3158      unloadVideo($videoPlaying, true, true);
3159
3160      updateTouchTails('x', false);
3161
3162      that.resize();
3163      loadImg(activeIndexes, 'stage');
3164
3165      lockScroll($WINDOW, scrollLeft, scrollTop);
3166
3167      triggerEvent('fullscreenexit');
3168    }
3169  }
3170
3171  that.cancelFullScreen = function () {
3172    if (o_nativeFullScreen && fullScreenApi.is()) {
3173      fullScreenApi.cancel(document);
3174    } else {
3175      cancelFullScreen();
3176    }
3177
3178    return this;
3179  };
3180
3181  that.toggleFullScreen = function () {
3182    return that[(that.fullScreen ? 'cancel' : 'request') + 'FullScreen']();
3183  };
3184
3185  addEvent(document, fullScreenApi.event, function () {
3186    if (data && !fullScreenApi.is() && !$videoPlaying) {
3187      cancelFullScreen();
3188    }
3189  });
3190
3191  that.resize = function (options) {
3192    if (!data) return this;
3193
3194    var time = arguments[1] || 0,
3195        setFLAG = arguments[2];
3196
3197    extendMeasures(!that.fullScreen ? optionsToLowerCase(options) : {width: '100%', maxwidth: null, minwidth: null, height: '100%', maxheight: null, minheight: null}, [measures, setFLAG || that.fullScreen || opts]);
3198
3199    var width = measures.width,
3200        height = measures.height,
3201        ratio = measures.ratio,
3202        windowHeight = $WINDOW.height() - (o_nav ? $nav.height() : 0);
3203
3204    if (measureIsValid(width)) {
3205      $wrap
3206          .addClass(wrapOnlyActiveClass)
3207          .css({width: width, minWidth: measures.minwidth || 0, maxWidth: measures.maxwidth || MAX_WIDTH});
3208
3209      width = measures.W = measures.w = $wrap.width();
3210      measures.nw = o_nav && numberFromWhatever(opts.navwidth, width) || width;
3211
3212      if (opts.glimpse) {
3213        // Glimpse
3214        measures.w -= Math.round((numberFromWhatever(opts.glimpse, width) || 0) * 2);
3215      }
3216
3217      $stageShaft.css({width: measures.w, marginLeft: (measures.W - measures.w) / 2});
3218
3219      //////console.log('measures.W', measures.W);
3220      //////console.log('measures.w', measures.w);
3221
3222      height = numberFromWhatever(height, windowHeight);
3223
3224      height = height || (ratio && width / ratio);
3225
3226      if (height) {
3227        width = Math.round(width);
3228        height = measures.h = Math.round(minMaxLimit(height, numberFromWhatever(measures.minheight, windowHeight), numberFromWhatever(measures.maxheight, windowHeight)));
3229
3230        $stage
3231            .stop()
3232            .animate({width: width, height: height}, time, function () {
3233              $wrap.removeClass(wrapOnlyActiveClass);
3234            });
3235
3236        stageShaftReposition();
3237
3238        if (o_nav) {
3239          $nav
3240              .stop()
3241              .animate({width: measures.nw}, time);
3242
3243          slideNavShaft({guessIndex: activeIndex, time: time, keep: true});
3244          if (o_navThumbs && frameAppend.nav) slideThumbBorder(time);
3245        }
3246
3247        measuresSetFLAG = setFLAG || true;
3248
3249        ready();
3250      }
3251    }
3252
3253    stageLeft = $stage.offset().left;
3254
3255    return this;
3256  };
3257
3258  that.setOptions = function (options) {
3259    $.extend(opts, options);
3260    reset();
3261    return this;
3262  };
3263
3264  that.shuffle = function () {
3265    data && shuffle(data) && reset();
3266    return this;
3267  };
3268
3269  function setShadow ($el, edge) {
3270    //////console.time('setShadow');
3271    if (o_shadows) {
3272      $el.removeClass(shadowsLeftClass + ' ' + shadowsRightClass);
3273      edge && !$videoPlaying && $el.addClass(edge.replace(/^|\s/g, ' ' + shadowsClass + '--'));
3274    }
3275    //////console.timeEnd('setShadow');
3276  }
3277
3278  that.destroy = function () {
3279    that.cancelFullScreen();
3280    that.stopAutoplay();
3281
3282    data = that.data = null;
3283
3284    appendElements();
3285
3286    activeIndexes = [];
3287    detachFrames(STAGE_FRAME_KEY);
3288
3289    reset.ok = false;
3290
3291    return this;
3292  };
3293
3294  that.playVideo = function () {
3295    var dataFrame = activeFrame,
3296        video = dataFrame.video,
3297        _activeIndex = activeIndex;
3298
3299    if (typeof video === 'object' && dataFrame.videoReady) {
3300      o_nativeFullScreen && that.fullScreen && that.cancelFullScreen();
3301
3302      waitFor(function () {
3303        return !fullScreenApi.is() || _activeIndex !== activeIndex;
3304      }, function () {
3305        if (_activeIndex === activeIndex) {
3306          dataFrame.$video = dataFrame.$video || $($.Fotorama.jst.video(video));
3307          dataFrame.$video.appendTo(dataFrame[STAGE_FRAME_KEY]);
3308
3309          $wrap.addClass(wrapVideoClass);
3310          $videoPlaying = dataFrame.$video;
3311
3312          stageNoMove();
3313
3314          $arrs.blur();
3315          $fullscreenIcon.blur();
3316
3317          triggerEvent('loadvideo');
3318        }
3319      });
3320    }
3321
3322    return this;
3323  };
3324
3325  that.stopVideo = function () {
3326    unloadVideo($videoPlaying, true, true);
3327    return this;
3328  };
3329
3330  function unloadVideo ($video, unloadActiveFLAG, releaseAutoplayFLAG) {
3331    if (unloadActiveFLAG) {
3332      $wrap.removeClass(wrapVideoClass);
3333      $videoPlaying = false;
3334
3335      stageNoMove();
3336    }
3337
3338    if ($video && $video !== $videoPlaying) {
3339      $video.remove();
3340      triggerEvent('unloadvideo');
3341    }
3342
3343    if (releaseAutoplayFLAG) {
3344      releaseAutoplay();
3345      changeAutoplay();
3346    }
3347  }
3348
3349  function toggleControlsClass (FLAG) {
3350    $wrap.toggleClass(wrapNoControlsClass, FLAG);
3351  }
3352
3353  function stageCursor (e) {
3354    if (stageShaftTouchTail.flow) return;
3355
3356    var x = e ? e.pageX : stageCursor.x,
3357        pointerFLAG = x && !disableDirrection(getDirection(x)) && opts.click;
3358
3359    if (stageCursor.p !== pointerFLAG
3360        && $stage.toggleClass(pointerClass, pointerFLAG)) {
3361      stageCursor.p = pointerFLAG;
3362      stageCursor.x = x;
3363    }
3364  }
3365
3366  $stage.on('mousemove', stageCursor);
3367
3368  function clickToShow (showOptions) {
3369    clearTimeout(clickToShow.t);
3370
3371    if (opts.clicktransition && opts.clicktransition !== opts.transition) {
3372      ////console.log('change transition to: ' + opts.clicktransition);
3373
3374      // this timeout is for yield events flow
3375      setTimeout(function () {
3376        // save original transition for later
3377        var _o_transition = opts.transition;
3378
3379        that.setOptions({transition: opts.clicktransition});
3380
3381        // now safe to pass base transition to o_transition, so that.show will restor it
3382        o_transition = _o_transition;
3383        // this timeout is here to prevent jerking in some browsers
3384        clickToShow.t = setTimeout(function () {
3385          that.show(showOptions);
3386        }, 10);
3387      }, 0);
3388    } else {
3389      that.show(showOptions);
3390    }
3391  }
3392
3393  function onStageTap (e, toggleControlsFLAG) {
3394    //////console.time('onStageTap');
3395    var target = e.target,
3396        $target = $(target);
3397
3398    if ($target.hasClass(videoPlayClass)) {
3399      that.playVideo();
3400    } else if (target === fullscreenIcon) {
3401      that.toggleFullScreen();
3402    } else if ($videoPlaying) {
3403      target === videoClose && unloadVideo($videoPlaying, true, true);
3404    } else {
3405      if (toggleControlsFLAG) {
3406        toggleControlsClass();
3407      } else if (opts.click) {
3408
3409        clickToShow({index: e.shiftKey || getDirectionSign(getDirection(e._x)), slow: e.altKey, user: true});
3410      }
3411    }
3412    //////console.timeEnd('onStageTap');
3413  }
3414
3415  function updateTouchTails (key, value) {
3416    stageShaftTouchTail[key] = navShaftTouchTail[key] = value;
3417  }
3418
3419  stageShaftTouchTail = moveOnTouch($stageShaft, {
3420    onStart: onTouchStart,
3421    onMove: function (e, result) {
3422      setShadow($stage, result.edge);
3423    },
3424    onTouchEnd: onTouchEnd,
3425    onEnd: function (result) {
3426      //////console.time('stageShaftTouchTail.onEnd');
3427      setShadow($stage);
3428
3429      //////console.log('result', result);
3430
3431      var toggleControlsFLAG = (MS_POINTER && !hoverFLAG || result.touch) && opts.arrows && opts.arrows !== 'always';
3432
3433      if (result.moved || (toggleControlsFLAG && result.pos !== result.newPos && !result.control)) {
3434        var index = getIndexByPos(result.newPos, measures.w, opts.margin, repositionIndex);
3435        that.show({
3436          index: index,
3437          time: o_fade ? o_transitionDuration : result.time,
3438          overPos: result.overPos,
3439          user: true
3440        });
3441      } else if (!result.aborted && !result.control) {
3442        onStageTap(result.startEvent, toggleControlsFLAG);
3443      }
3444      //////console.timeEnd('stageShaftTouchTail.onEnd');
3445    },
3446//    getPos: function () {
3447//      return -getPosByIndex(dirtyIndex, measures.w, opts.margin, repositionIndex);
3448//    },
3449    //_001: true,
3450    timeLow: 1,
3451    timeHigh: 1,
3452    friction: 2,
3453    select: '.' + selectClass + ', .' + selectClass + ' *',
3454    $wrap: $stage
3455  });
3456
3457  navShaftTouchTail = moveOnTouch($navShaft, {
3458    onStart: onTouchStart,
3459    onMove: function (e, result) {
3460      setShadow($nav, result.edge);
3461    },
3462    onTouchEnd: onTouchEnd,
3463    onEnd: function (result) {
3464
3465      function onEnd () {
3466        slideNavShaft.l = result.newPos;
3467        releaseAutoplay();
3468        changeAutoplay();
3469        thumbsDraw(result.newPos, true);
3470      }
3471
3472      if (!result.moved) {
3473        var target = result.$target.closest('.' + navFrameClass, $navShaft)[0];
3474        target && onNavFrameClick.call(target, result.startEvent);
3475      } else if (result.pos !== result.newPos) {
3476        pausedAutoplayFLAG = true;
3477        slide($navShaft, {
3478          time: result.time,
3479          pos: result.newPos,
3480          overPos: result.overPos,
3481          onEnd: onEnd
3482        });
3483        thumbsDraw(result.newPos);
3484        o_shadows && setShadow($nav, findShadowEdge(result.newPos, navShaftTouchTail.min, navShaftTouchTail.max));
3485      } else {
3486        onEnd();
3487      }
3488    },
3489    timeLow: .5,
3490    timeHigh: 2,
3491    friction: 5,
3492    $wrap: $nav
3493  });
3494
3495  stageWheelTail = wheel($stage, {
3496    shift: true,
3497    onEnd: function (e, direction) {
3498      //////console.log('wheel $stage onEnd', direction);
3499      onTouchStart();
3500      onTouchEnd();
3501      that.show({index: direction, slow: e.altKey})
3502    }
3503  });
3504
3505  navWheelTail = wheel($nav, {
3506    onEnd: function (e, direction) {
3507      //////console.log('wheel $nav onEnd', direction);
3508      onTouchStart();
3509      onTouchEnd();
3510      var newPos = stop($navShaft) + direction * .25;
3511      $navShaft.css(getTranslate(minMaxLimit(newPos, navShaftTouchTail.min, navShaftTouchTail.max)));
3512      o_shadows && setShadow($nav, findShadowEdge(newPos, navShaftTouchTail.min, navShaftTouchTail.max));
3513      navWheelTail.prevent = {'<': newPos >= navShaftTouchTail.max, '>': newPos <= navShaftTouchTail.min};
3514      clearTimeout(navWheelTail.t);
3515      navWheelTail.t = setTimeout(function () {
3516        slideNavShaft.l = newPos;
3517        thumbsDraw(newPos, true)
3518      }, TOUCH_TIMEOUT);
3519      thumbsDraw(newPos);
3520    }
3521  });
3522
3523  $wrap.hover(
3524      function () {
3525        setTimeout(function () {
3526          if (touchedFLAG) return;
3527          toggleControlsClass(!(hoverFLAG = true));
3528        }, 0);
3529      }, function () {
3530        if (!hoverFLAG) return;
3531        toggleControlsClass(!(hoverFLAG = false));
3532      }
3533  );
3534
3535  function onNavFrameClick (e) {
3536    var index = $(this).data().eq;
3537    clickToShow({index: index, slow: e.altKey, user: true, coo: e._x - $nav.offset().left});
3538  }
3539
3540  function onArrClick (e) {
3541    clickToShow({index: $arrs.index(this) ? '>' : '<', slow: e.altKey, user: true});
3542  }
3543
3544  smartClick($arrs, function (e) {
3545    stopEvent(e);
3546    onArrClick.call(this, e);
3547  }, {
3548    onStart: function () {
3549      onTouchStart();
3550      stageShaftTouchTail.control = true;
3551    },
3552    onTouchEnd: onTouchEnd
3553  });
3554
3555  function addFocusOnControls (el) {
3556    addFocus(el, function () {
3557      setTimeout(function () {
3558        lockScroll($stage);
3559      }, 0);
3560      toggleControlsClass(false);
3561    });
3562  }
3563
3564  $arrs.each(function () {
3565    addEnterUp(this, function (e) {
3566      onArrClick.call(this, e);
3567    });
3568    addFocusOnControls(this);
3569  });
3570
3571  addEnterUp(fullscreenIcon, that.toggleFullScreen);
3572  addFocusOnControls(fullscreenIcon);
3573
3574  function reset () {
3575    setData();
3576    setOptions();
3577
3578    if (!reset.i) {
3579      reset.i = true;
3580      // Only once
3581      var _startindex = opts.startindex;
3582      if (_startindex || opts.hash && location.hash) {
3583        startIndex = getIndexFromHash(_startindex || location.hash.replace(/^#/, ''), data, that.index === 0 || _startindex, _startindex);
3584      }
3585      activeIndex = repositionIndex = dirtyIndex = lastActiveIndex = startIndex = edgeIndex(startIndex) || 0;/*(o_rtl ? size - 1 : 0)*///;
3586    }
3587
3588    if (size) {
3589      if (changeToRtl()) return;
3590
3591      if ($videoPlaying) {
3592        unloadVideo($videoPlaying, true);
3593      }
3594
3595      activeIndexes = [];
3596      detachFrames(STAGE_FRAME_KEY);
3597
3598      reset.ok = true;
3599
3600      that.show({index: activeIndex, time: 0});
3601      that.resize();
3602    } else {
3603      that.destroy();
3604    }
3605  }
3606
3607  function changeToRtl () {
3608    //////console.log('changeToRtl');
3609    if (!changeToRtl.f === o_rtl) {
3610      changeToRtl.f = o_rtl;
3611      activeIndex = size - 1 - activeIndex;
3612      //////console.log('changeToRtl execute, activeIndex is', activeIndex);
3613      that.reverse();
3614
3615      return true;
3616    }
3617  }
3618
3619  $.each('load push pop shift unshift reverse sort splice'.split(' '), function (i, method) {
3620    that[method] = function () {
3621      data = data || [];
3622      if (method !== 'load') {
3623        Array.prototype[method].apply(data, arguments);
3624      } else if (arguments[0] && typeof arguments[0] === 'object' && arguments[0].length) {
3625        data = clone(arguments[0]);
3626      }
3627      reset();
3628      return that;
3629    }
3630  });
3631
3632  function ready () {
3633    if (!ready.ok) {
3634      ready.ok = true;
3635      triggerEvent('ready');
3636    }
3637  }
3638
3639  reset();
3640};
3641
3642
3643$.fn.fotorama = function (opts) {
3644  return this.each(function () {
3645    var that = this,
3646        $fotorama = $(this),
3647        fotoramaData = $fotorama.data(),
3648        fotorama = fotoramaData.fotorama;
3649
3650    if (!fotorama) {
3651      waitFor(function () {
3652        return !isHidden(that);
3653      }, function () {
3654        fotoramaData.urtext = $fotorama.html();
3655        new $.Fotorama($fotorama,
3656            /* Priority for options:
3657             * 1. <div data-loop="true"></div>
3658             * 2. $('div').fotorama({loop: false})
3659             * 3. Defaults */
3660            $.extend(
3661                {},
3662                OPTIONS,
3663                window.fotoramaDefaults,
3664                opts,
3665                fotoramaData
3666            )
3667        );
3668      });
3669    } else {
3670      fotorama.setOptions(opts, true);
3671    }
3672  });
3673};
3674$.Fotorama.instances = [];
3675
3676function calculateIndexes () {
3677  $.each($.Fotorama.instances, function (index, instance) {
3678    instance.index = index;
3679  });
3680}
3681
3682function addInstance (instance) {
3683  $.Fotorama.instances.push(instance);
3684  calculateIndexes();
3685}
3686
3687function hideInstance (instance) {
3688  $.Fotorama.instances.splice(instance.index, 1);
3689  calculateIndexes();
3690}
3691$.Fotorama.cache = {};
3692$.Fotorama.measures = {};
3693$ = $ || {};
3694$.Fotorama = $.Fotorama || {};
3695$.Fotorama.jst = $.Fotorama.jst || {};
3696
3697$.Fotorama.jst.style = function(v) {
3698var __t, __p = '', __e = _.escape;
3699__p += '.fotorama' +
3700((__t = ( v.s )) == null ? '' : __t) +
3701' .fotorama__nav--thumbs .fotorama__nav__frame{\npadding:' +
3702((__t = ( v.m )) == null ? '' : __t) +
3703'px;\nheight:' +
3704((__t = ( v.h )) == null ? '' : __t) +
3705'px}\n.fotorama' +
3706((__t = ( v.s )) == null ? '' : __t) +
3707' .fotorama__thumb-border{\nheight:' +
3708((__t = ( v.h - v.b * (v.q ? 0 : 2) )) == null ? '' : __t) +
3709'px;\nborder-width:' +
3710((__t = ( v.b )) == null ? '' : __t) +
3711'px;\nmargin-top:' +
3712((__t = ( v.m )) == null ? '' : __t) +
3713'px}';
3714return __p
3715};
3716
3717$.Fotorama.jst.video = function(v) {
3718var __t, __p = '', __e = _.escape, __j = Array.prototype.join;
3719function print() { __p += __j.call(arguments, '') }
3720__p += '<div class="fotorama__video"><iframe src="';
3721 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 : '')) ;
3722__p += '" frameborder="0" allowfullscreen></iframe></div>\n';
3723return __p
3724};
3725$(function () {
3726  $('.' + _fotoramaClass + ':not([data-auto="false"])').fotorama();
3727});
3728})(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.