Advertisement
BackuPs-nl

fotorama-v464-jquery4.0fix.js

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