PageSourceSearch

https://outisnemo.com/wp-content/themes/uncode/library/js/plugins.js?ver=311255554

js outisnemo.com collected 2026-09-24 14:49:54 UTC 797,992 bytes, 24,417 lines download raw bytes

1/* ----------------------------------------------------------
2 * Plugins
3 * ---------------------------------------------------------- */
4
5(function(window, $) {
6  'use strict';
7  var counter = 0,
8    $headCache = $('head'),
9    oldBigText = window.BigText,
10    oldjQueryMethod = $.fn.bigtext,
11    BigText = {
12      DEBUG_MODE: false,
13      DEFAULT_MIN_FONT_SIZE_PX: null,
14      DEFAULT_MAX_FONT_SIZE_PX: 528,
15      GLOBAL_STYLE_ID: 'bigtext-style',
16      STYLE_ID: 'bigtext-id',
17      LINE_CLASS_PREFIX: 'bigtext-line',
18      EXEMPT_CLASS: 'bigtext-exempt',
19      noConflict: function(restore)
20      {
21        if(restore) {
22          $.fn.bigtext = oldjQueryMethod;
23          window.BigText = oldBigText;
24        }
25        return BigText;
26      },
27      supports: {
28        wholeNumberFontSizeOnly: (function() {
29          if( !( 'getComputedStyle' in window ) ) {
30            return true;
31          }
32          var test = $('<div/>').css({
33              position: 'absolute',
34              'font-size': '14.1px'
35            }).insertBefore( $('script').eq(0) ),
36            computedStyle = window.getComputedStyle( test[0], null );
37
38          var ret = computedStyle && computedStyle.getPropertyValue( 'font-size' ) === '14px';
39          test.remove();
40          return ret;
41        })()
42      },
43      init: function() {
44        if(!$('#'+BigText.GLOBAL_STYLE_ID).length) {
45          $headCache.append(BigText.generateStyleTag(BigText.GLOBAL_STYLE_ID, ['.bigtext * { white-space: nowrap; } .bigtext > * { display: block; }',
46                                          '.bigtext .' + BigText.EXEMPT_CLASS + ', .bigtext .' + BigText.EXEMPT_CLASS + ' * { white-space: normal; }']));
47        }
48      },
49      bindResize: function(eventName, resizeFunction) {
50        var timeoutId;
51        $(window).unbind(eventName).bind(eventName, function() {
52          if( timeoutId ) {
53            clearTimeout( timeoutId );
54          }
55          timeoutId = setTimeout( resizeFunction, 300 );
56        });
57      },
58      getStyleId: function(id)
59      {
60        return BigText.STYLE_ID + '-' + id;
61      },
62      generateStyleTag: function(id, css)
63      {
64        return $('<style>' + css.join('\n') + '</style>').attr('id', id);
65      },
66      clearCss: function(id)
67      {
68        var styleId = BigText.getStyleId(id);
69        $('#' + styleId).remove();
70      },
71      generateCss: function(id, linesFontSizes, lineWordSpacings, minFontSizes)
72      {
73        var css = [];
74
75        BigText.clearCss(id);
76
77        for(var j=0, k=linesFontSizes.length; j<k; j++) {
78          css.push('#' + id + ' .' + BigText.LINE_CLASS_PREFIX + j + ' {' +
79            (minFontSizes[j] ? ' white-space: normal;' : '') +
80            (linesFontSizes[j] ? ' font-size: ' + linesFontSizes[j] + 'px;' : '') +
81            (lineWordSpacings[j] ? ' word-spacing: ' + lineWordSpacings[j] + 'px;' : '') +
82            '}');
83        }
84
85        return BigText.generateStyleTag(BigText.getStyleId(id), css);
86      },
87      jQueryMethod: function(options)
88      {
89        BigText.init();
90
91        options = $.extend({
92          minfontsize: BigText.DEFAULT_MIN_FONT_SIZE_PX,
93          maxfontsize: BigText.DEFAULT_MAX_FONT_SIZE_PX,
94          childSelector: '',
95          resize: true
96        }, options || {});
97
98        this.each(function()
99        {
100          var $t = $(this).addClass('bigtext'),
101            maxWidth = $t.width(),
102            id = $t.attr('id'),
103            $children = options.childSelector ? $t.find( options.childSelector ) : $t.children();
104
105          if(!id) {
106            id = 'bigtext-id' + (counter++);
107            $t.attr('id', id);
108          }
109
110          if(options.resize) {
111            BigText.bindResize('resize.bigtext-event-' + id, function()
112            {
113              // TODO only call this if the width has changed.
114              BigText.jQueryMethod.call($('#' + id), options);
115            });
116          }
117
118          BigText.clearCss(id);
119
120          $children.addClass(function(lineNumber, className)
121          {
122            // remove existing line classes.
123            return [className.replace(new RegExp('\\b' + BigText.LINE_CLASS_PREFIX + '\\d+\\b'), ''),
124                BigText.LINE_CLASS_PREFIX + lineNumber].join(' ');
125          });
126
127          var sizes = calculateSizes($t, $children, maxWidth, options.maxfontsize, options.minfontsize);
128          $headCache.append(BigText.generateCss(id, sizes.fontSizes, sizes.wordSpacings, sizes.minFontSizes));
129        });
130
131        return this.trigger('bigtext:complete');
132      }
133    };
134
135  function testLineDimensions($line, maxWidth, property, size, interval, units, previousWidth)
136  {
137    var width;
138    previousWidth = typeof previousWidth === 'number' ? previousWidth : 0;
139    $line.css(property, size + units);
140
141    width = $line.width();
142
143    if(width >= maxWidth) {
144
145      $line.css(property, '');
146
147      if(width === maxWidth) {
148        return {
149          match: 'exact',
150          size: parseFloat((parseFloat(size) - 0.1).toFixed(3))
151        };
152      }
153
154      // Since this is an estimate, we calculate how far over the width we went with the new value.
155      // If this is word-spacing (our last resort guess) and the over is less than the under, we keep the higher value.
156      // Otherwise, we revert to the underestimate.
157      var under = maxWidth - previousWidth,
158        over = width - maxWidth;
159
160      return {
161        match: 'estimate',
162        size: parseFloat((parseFloat(size) - (property === 'word-spacing' && previousWidth && ( over < under ) ? 0 : interval)).toFixed(3))
163      };
164    }
165
166    return width;
167  }
168
169  function calculateSizes($t, $children, maxWidth, maxFontSize, minFontSize)
170  {
171    var $c = $t.clone(true)
172      .addClass('bigtext-cloned')
173      .css({
174        fontFamily: $t.css('font-family'),
175        textTransform: $t.css('text-transform'),
176        wordSpacing: $t.css('word-spacing'),
177        letterSpacing: $t.css('letter-spacing'),
178        position: 'absolute',
179        left: BigText.DEBUG_MODE ? 0 : -9999,
180        top: BigText.DEBUG_MODE ? 0 : -9999
181      })
182      .appendTo(document.body);
183
184    // font-size isn't the only thing we can modify, we can also mess with:
185    // word-spacing and letter-spacing. WebKit does not respect subpixel
186    // letter-spacing, word-spacing, or font-size.
187    // TODO try -webkit-transform: scale() as a workaround.
188    var fontSizes = [],
189      wordSpacings = [],
190      minFontSizes = [],
191      ratios = [];
192
193    $children.css('float', 'left').each(function() {
194      var $line = $(this),
195        // TODO replace 8, 4 with a proportional size to the calculated font-size.
196        intervals = BigText.supports.wholeNumberFontSizeOnly ? [8, 4, 1] : [8, 4, 1, 0.1],
197        lineMax,
198        newFontSize;
199
200      if($line.hasClass(BigText.EXEMPT_CLASS)) {
201        fontSizes.push(null);
202        ratios.push(null);
203        minFontSizes.push(false);
204        return;
205      }
206
207      // TODO we can cache this ratio?
208      var autoGuessSubtraction = 32, // font size in px
209        currentFontSize = parseFloat($line.css('font-size')),
210        ratio = ( $line.width() / currentFontSize ).toFixed(6);
211
212      newFontSize = parseInt( maxWidth / ratio, 10 ) - autoGuessSubtraction;
213
214      outer: for(var m=0, n=intervals.length; m<n; m++) {
215        inner: for(var j=1, k=10; j<=k; j++) {
216          if(newFontSize + j*intervals[m] > maxFontSize) {
217            newFontSize = maxFontSize;
218            break outer;
219          }
220
221          lineMax = testLineDimensions($line, maxWidth, 'font-size', newFontSize + j*intervals[m], intervals[m], 'px', lineMax);
222          if(typeof lineMax !== 'number') {
223            newFontSize = lineMax.size;
224
225            if(lineMax.match === 'exact') {
226              break outer;
227            }
228            break inner;
229          }
230        }
231      }
232
233      ratios.push(maxWidth / newFontSize);
234
235      if(newFontSize > maxFontSize) {
236        fontSizes.push(maxFontSize);
237        minFontSizes.push(false);
238      } else if(!!minFontSize && newFontSize < minFontSize) {
239        fontSizes.push(minFontSize);
240        minFontSizes.push(true);
241      } else {
242        fontSizes.push(newFontSize);
243        minFontSizes.push(false);
244      }
245    }).each(function(lineNumber) {
246      var $line = $(this),
247        wordSpacing = 0,
248        interval = 1,
249        maxWordSpacing;
250
251      if($line.hasClass(BigText.EXEMPT_CLASS)) {
252        wordSpacings.push(null);
253        return;
254      }
255
256      // must re-use font-size, even though it was removed above.
257      $line.css('font-size', fontSizes[lineNumber] + 'px');
258
259      for(var m=1, n=3; m<n; m+=interval) {
260        maxWordSpacing = testLineDimensions($line, maxWidth, 'word-spacing', m, interval, 'px', maxWordSpacing);
261        if(typeof maxWordSpacing !== 'number') {
262          wordSpacing = maxWordSpacing.size;
263          break;
264        }
265      }
266
267      $line.css('font-size', '');
268      wordSpacings.push(wordSpacing);
269    }).removeAttr('style');
270
271    if( !BigText.DEBUG_MODE ) {
272      $c.remove();
273    } else {
274      $c.css({
275        'background-color': 'rgba(255,255,255,.4)'
276      });
277    }
278
279    return {
280      fontSizes: fontSizes,
281      wordSpacings: wordSpacings,
282      ratios: ratios,
283      minFontSizes: minFontSizes
284    };
285  }
286
287  $.fn.bigtext = BigText.jQueryMethod;
288  window.BigText = BigText;
289
290})(this, jQuery);
vendor: 16,093 bytes, lines 291-948
291/*!
292 * Isotope PACKAGED v3.0.6
293 *
294 * Licensed GPLv3 for open source use
295 * or Isotope Commercial License for commercial use
296 *
297 * https://isotope.metafizzy.co
298 * Copyright 2010-2018 Metafizzy
299 */
300
301/**
302 * Bridget makes jQuery widgets
303 * v2.0.1
304 * MIT license
305 */
306
307/* jshint browser: true, strict: true, undef: true, unused: true */
308
309( function( window, factory ) {
310  // universal module definition
311  /*jshint strict: false */ /* globals define, module, require */
312  if ( typeof define == 'function' && define.amd ) {
313    // AMD
314    define( 'jquery-bridget/jquery-bridget',[ 'jquery' ], function( jQuery ) {
315      return factory( window, jQuery );
316    });
317  } else if ( typeof module == 'object' && module.exports ) {
318    // CommonJS
319    module.exports = factory(
320      window,
321      require('jquery')
322    );
323  } else {
324    // browser global
325    window.jQueryBridget = factory(
326      window,
327      window.jQuery
328    );
329  }
330
331}( window, function factory( window, jQuery ) {
332'use strict';
333
334// ----- utils ----- //
335
336var arraySlice = Array.prototype.slice;
337
338// helper function for logging errors
339// $.error breaks jQuery chaining
340var console = window.console;
341var logError = typeof console == 'undefined' ? function() {} :
342  function( message ) {
343    console.error( message );
344  };
345
346// ----- jQueryBridget ----- //
347
348function jQueryBridget( namespace, PluginClass, $ ) {
349  $ = $ || jQuery || window.jQuery;
350  if ( !$ ) {
351    return;
352  }
353
354  // add option method -> $().plugin('option', {...})
355  if ( !PluginClass.prototype.option ) {
356    // option setter
357    PluginClass.prototype.option = function( opts ) {
358      // bail out if not an object
359      if ( !$.isPlainObject( opts ) ){
360        return;
361      }
362      this.options = $.extend( true, this.options, opts );
363    };
364  }
365
366  // make jQuery plugin
367  $.fn[ namespace ] = function( arg0 /*, arg1 */ ) {
368    if ( typeof arg0 == 'string' ) {
369      // method call $().plugin( 'methodName', { options } )
370      // shift arguments by 1
371      var args = arraySlice.call( arguments, 1 );
372      return methodCall( this, arg0, args );
373    }
374    // just $().plugin({ options })
375    plainCall( this, arg0 );
376    return this;
377  };
378
379  // $().plugin('methodName')
380  function methodCall( $elems, methodName, args ) {
381    var returnValue;
382    var pluginMethodStr = '$().' + namespace + '("' + methodName + '")';
383
384    $elems.each( function( i, elem ) {
385      // get instance
386      var instance = $.data( elem, namespace );
387      if ( !instance ) {
388        logError( namespace + ' not initialized. Cannot call methods, i.e. ' +
389          pluginMethodStr );
390        return;
391      }
392
393      var method = instance[ methodName ];
394      if ( !method || methodName.charAt(0) == '_' ) {
395        logError( pluginMethodStr + ' is not a valid method' );
396        return;
397      }
398
399      // apply method, get return value
400      var value = method.apply( instance, args );
401      // set return value if value is returned, use only first value
402      returnValue = returnValue === undefined ? value : returnValue;
403    });
404
405    return returnValue !== undefined ? returnValue : $elems;
406  }
407
408  function plainCall( $elems, options ) {
409    $elems.each( function( i, elem ) {
410      var instance = $.data( elem, namespace );
411      if ( instance ) {
412        // set options & init
413        instance.option( options );
414        instance._init();
415      } else {
416        // initialize new instance
417        instance = new PluginClass( elem, options );
418        $.data( elem, namespace, instance );
419      }
420    });
421  }
422
423  updateJQuery( $ );
424
425}
426
427// ----- updateJQuery ----- //
428
429// set $.bridget for v1 backwards compatibility
430function updateJQuery( $ ) {
431  if ( !$ || ( $ && $.bridget ) ) {
432    return;
433  }
434  $.bridget = jQueryBridget;
435}
436
437updateJQuery( jQuery || window.jQuery );
438
439// -----  ----- //
440
441return jQueryBridget;
442
443}));
444
445/**
446 * EvEmitter v1.1.0
447 * Lil' event emitter
448 * MIT License
449 */
450
451/* jshint unused: true, undef: true, strict: true */
452
453( function( global, factory ) {
454  // universal module definition
455  /* jshint strict: false */ /* globals define, module, window */
456  if ( typeof define == 'function' && define.amd ) {
457    // AMD - RequireJS
458    define( 'ev-emitter/ev-emitter',factory );
459  } else if ( typeof module == 'object' && module.exports ) {
460    // CommonJS - Browserify, Webpack
461    module.exports = factory();
462  } else {
463    // Browser globals
464    global.EvEmitter = factory();
465  }
466
467}( typeof window != 'undefined' ? window : this, function() {
468
469
470
471function EvEmitter() {}
472
473var proto = EvEmitter.prototype;
474
475proto.on = function( eventName, listener ) {
476  if ( !eventName || !listener ) {
477    return;
478  }
479  // set events hash
480  var events = this._events = this._events || {};
481  // set listeners array
482  var listeners = events[ eventName ] = events[ eventName ] || [];
483  // only add once
484  if ( listeners.indexOf( listener ) == -1 ) {
485    listeners.push( listener );
486  }
487
488  return this;
489};
490
491proto.once = function( eventName, listener ) {
492  if ( !eventName || !listener ) {
493    return;
494  }
495  // add event
496  this.on( eventName, listener );
497  // set once flag
498  // set onceEvents hash
499  var onceEvents = this._onceEvents = this._onceEvents || {};
500  // set onceListeners object
501  var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {};
502  // set flag
503  onceListeners[ listener ] = true;
504
505  return this;
506};
507
508proto.off = function( eventName, listener ) {
509  var listeners = this._events && this._events[ eventName ];
510  if ( !listeners || !listeners.length ) {
511    return;
512  }
513  var index = listeners.indexOf( listener );
514  if ( index != -1 ) {
515    listeners.splice( index, 1 );
516  }
517
518  return this;
519};
520
521proto.emitEvent = function( eventName, args ) {
522  var listeners = this._events && this._events[ eventName ];
523  if ( !listeners || !listeners.length ) {
524    return;
525  }
526  // copy over to avoid interference if .off() in listener
527  listeners = listeners.slice(0);
528  args = args || [];
529  // once stuff
530  var onceListeners = this._onceEvents && this._onceEvents[ eventName ];
531
532  for ( var i=0; i < listeners.length; i++ ) {
533    var listener = listeners[i]
534    var isOnce = onceListeners && onceListeners[ listener ];
535    if ( isOnce ) {
536      // remove listener
537      // remove before trigger to prevent recursion
538      this.off( eventName, listener );
539      // unset once flag
540      delete onceListeners[ listener ];
541    }
542    // trigger listener
543    listener.apply( this, args );
544  }
545
546  return this;
547};
548
549proto.allOff = function() {
550  delete this._events;
551  delete this._onceEvents;
552};
553
554return EvEmitter;
555
556}));
557
558/*!
559 * getSize v2.0.3
560 * measure size of elements
561 * MIT license
562 */
563
564/* jshint browser: true, strict: true, undef: true, unused: true */
565/* globals console: false */
566
567( function( window, factory ) {
568  /* jshint strict: false */ /* globals define, module */
569  if ( typeof define == 'function' && define.amd ) {
570    // AMD
571    define( 'get-size/get-size',factory );
572  } else if ( typeof module == 'object' && module.exports ) {
573    // CommonJS
574    module.exports = factory();
575  } else {
576    // browser global
577    window.getSize = factory();
578  }
579
580})( window, function factory() {
581'use strict';
582
583// -------------------------- helpers -------------------------- //
584
585// get a number from a string, not a percentage
586function getStyleSize( value ) {
587  var num = parseFloat( value );
588  // not a percent like '100%', and a number
589  var isValid = value.indexOf('%') == -1 && !isNaN( num );
590  return isValid && num;
591}
592
593function noop() {}
594
595var logError = typeof console == 'undefined' ? noop :
596  function( message ) {
597    console.error( message );
598  };
599
600// -------------------------- measurements -------------------------- //
601
602var measurements = [
603  'paddingLeft',
604  'paddingRight',
605  'paddingTop',
606  'paddingBottom',
607  'marginLeft',
608  'marginRight',
609  'marginTop',
610  'marginBottom',
611  'borderLeftWidth',
612  'borderRightWidth',
613  'borderTopWidth',
614  'borderBottomWidth'
615];
616
617var measurementsLength = measurements.length;
618
619function getZeroSize() {
620  var size = {
621    width: 0,
622    height: 0,
623    innerWidth: 0,
624    innerHeight: 0,
625    outerWidth: 0,
626    outerHeight: 0
627  };
628  for ( var i=0; i < measurementsLength; i++ ) {
629    var measurement = measurements[i];
630    size[ measurement ] = 0;
631  }
632  return size;
633}
634
635// -------------------------- getStyle -------------------------- //
636
637/**
638 * getStyle, get style of element, check for Firefox bug
639 * https://bugzilla.mozilla.org/show_bug.cgi?id=548397
640 */
641function getStyle( elem ) {
642  var style = getComputedStyle( elem );
643  if ( !style ) {
644    logError( 'Style returned ' + style +
645      '. Are you running this code in a hidden iframe on Firefox? ' +
646      'See https://bit.ly/getsizebug1' );
647  }
648  return style;
649}
650
651// -------------------------- setup -------------------------- //
652
653var isSetup = false;
654
655var isBoxSizeOuter;
656
657/**
658 * setup
659 * check isBoxSizerOuter
660 * do on first getSize() rather than on page load for Firefox bug
661 */
662function setup() {
663  // setup once
664  if ( isSetup ) {
665    return;
666  }
667  isSetup = true;
668
669  // -------------------------- box sizing -------------------------- //
670
671  /**
672   * Chrome & Safari measure the outer-width on style.width on border-box elems
673   * IE11 & Firefox<29 measures the inner-width
674   */
675  var div = document.createElement('div');
676  div.style.width = '200px';
677  div.style.padding = '1px 2px 3px 4px';
678  div.style.borderStyle = 'solid';
679  div.style.borderWidth = '1px 2px 3px 4px';
680  div.style.boxSizing = 'border-box';
681
682  var body = document.body || document.documentElement;
683  body.appendChild( div );
684  var style = getStyle( div );
685  // round value for browser zoom. desandro/masonry#928
686  isBoxSizeOuter = Math.round( getStyleSize( style.width ) ) == 200;
687  getSize.isBoxSizeOuter = isBoxSizeOuter;
688
689  body.removeChild( div );
690}
691
692// -------------------------- getSize -------------------------- //
693
694function getSize( elem ) {
695  setup();
696
697  // use querySeletor if elem is string
698  if ( typeof elem == 'string' ) {
699    elem = document.querySelector( elem );
700  }
701
702  // do not proceed on non-objects
703  if ( !elem || typeof elem != 'object' || !elem.nodeType ) {
704    return;
705  }
706
707  var style = getStyle( elem );
708
709  // if hidden, everything is 0
710  if ( style.display == 'none' ) {
711    return getZeroSize();
712  }
713
714  var size = {};
715  size.width = elem.offsetWidth;
716  size.height = elem.offsetHeight;
717
718  var isBorderBox = size.isBorderBox = style.boxSizing == 'border-box';
719
720  // get all measurements
721  for ( var i=0; i < measurementsLength; i++ ) {
722    var measurement = measurements[i];
723    var value = style[ measurement ];
724    var num = parseFloat( value );
725    // any 'auto', 'medium' value will be 0
726    size[ measurement ] = !isNaN( num ) ? num : 0;
727  }
728
729  var paddingWidth = size.paddingLeft + size.paddingRight;
730  var paddingHeight = size.paddingTop + size.paddingBottom;
731  var marginWidth = size.marginLeft + size.marginRight;
732  var marginHeight = size.marginTop + size.marginBottom;
733  var borderWidth = size.borderLeftWidth + size.borderRightWidth;
734  var borderHeight = size.borderTopWidth + size.borderBottomWidth;
735
736  var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter;
737
738  // overwrite width and height if we can get it from style
739  var styleWidth = getStyleSize( style.width );
740  if ( styleWidth !== false ) {
741    size.width = styleWidth +
742      // add padding and border unless it's already including it
743      ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth );
744  }
745
746  var styleHeight = getStyleSize( style.height );
747  if ( styleHeight !== false ) {
748    size.height = styleHeight +
749      // add padding and border unless it's already including it
750      ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight );
751  }
752
753  size.innerWidth = size.width - ( paddingWidth + borderWidth );
754  size.innerHeight = size.height - ( paddingHeight + borderHeight );
755
756  size.outerWidth = size.width + marginWidth;
757  size.outerHeight = size.height + marginHeight;
758
759  return size;
760}
761
762return getSize;
763
764});
765
766/**
767 * matchesSelector v2.0.2
768 * matchesSelector( element, '.selector' )
769 * MIT license
770 */
771
772/*jshint browser: true, strict: true, undef: true, unused: true */
773
774( function( window, factory ) {
775  /*global define: false, module: false */
776  'use strict';
777  // universal module definition
778  if ( typeof define == 'function' && define.amd ) {
779    // AMD
780    define( 'desandro-matches-selector/matches-selector',factory );
781  } else if ( typeof module == 'object' && module.exports ) {
782    // CommonJS
783    module.exports = factory();
784  } else {
785    // browser global
786    window.matchesSelector = factory();
787  }
788
789}( window, function factory() {
790  'use strict';
791
792  var matchesMethod = ( function() {
793    var ElemProto = window.Element.prototype;
794    // check for the standard method name first
795    if ( ElemProto.matches ) {
796      return 'matches';
797    }
798    // check un-prefixed
799    if ( ElemProto.matchesSelector ) {
800      return 'matchesSelector';
801    }
802    // check vendor prefixes
803    var prefixes = [ 'webkit', 'moz', 'ms', 'o' ];
804
805    for ( var i=0; i < prefixes.length; i++ ) {
806      var prefix = prefixes[i];
807      var method = prefix + 'MatchesSelector';
808      if ( ElemProto[ method ] ) {
809        return method;
810      }
811    }
812  })();
813
814  return function matchesSelector( elem, selector ) {
815    return elem[ matchesMethod ]( selector );
816  };
817
818}));
819
820/**
821 * Fizzy UI utils v2.0.7
822 * MIT license
823 */
824
825/*jshint browser: true, undef: true, unused: true, strict: true */
826
827( function( window, factory ) {
828  // universal module definition
829  /*jshint strict: false */ /*globals define, module, require */
830
831  if ( typeof define == 'function' && define.amd ) {
832    // AMD
833    define( 'fizzy-ui-utils/utils',[
834      'desandro-matches-selector/matches-selector'
835    ], function( matchesSelector ) {
836      return factory( window, matchesSelector );
837    });
838  } else if ( typeof module == 'object' && module.exports ) {
839    // CommonJS
840    module.exports = factory(
841      window,
842      require('desandro-matches-selector')
843    );
844  } else {
845    // browser global
846    window.fizzyUIUtils = factory(
847      window,
848      window.matchesSelector
849    );
850  }
851
852}( window, function factory( window, matchesSelector ) {
853
854
855
856var utils = {};
857
858// ----- extend ----- //
859
860// extends objects
861utils.extend = function( a, b ) {
862  for ( var prop in b ) {
863    a[ prop ] = b[ prop ];
864  }
865  return a;
866};
867
868// ----- modulo ----- //
869
870utils.modulo = function( num, div ) {
871  return ( ( num % div ) + div ) % div;
872};
873
874// ----- makeArray ----- //
875
876var arraySlice = Array.prototype.slice;
877
878// turn element or nodeList into an array
879utils.makeArray = function( obj ) {
880  if ( Array.isArray( obj ) ) {
881    // use object if already an array
882    return obj;
883  }
884  // return empty array if undefined or null. #6
885  if ( obj === null || obj === undefined ) {
886    return [];
887  }
888
889  var isArrayLike = typeof obj == 'object' && typeof obj.length == 'number';
890  if ( isArrayLike ) {
891    // convert nodeList to array
892    return arraySlice.call( obj );
893  }
894
895  // array of single index
896  return [ obj ];
897};
898
899// ----- removeFrom ----- //
900
901utils.removeFrom = function( ary, obj ) {
902  var index = ary.indexOf( obj );
903  if ( index != -1 ) {
904    ary.splice( index, 1 );
905  }
906};
907
908// ----- getParent ----- //
909
910utils.getParent = function( elem, selector ) {
911  while ( elem.parentNode && elem != document.body ) {
912    elem = elem.parentNode;
913    if ( matchesSelector( elem, selector ) ) {
914      return elem;
915    }
916  }
917};
918
919// ----- getQueryElement ----- //
920
921// use element as selector string
922utils.getQueryElement = function( elem ) {
923  if ( typeof elem == 'string' ) {
924    return document.querySelector( elem );
925  }
926  return elem;
927};
928
929// ----- handleEvent ----- //
930
931// enable .ontype to trigger from .addEventListener( elem, 'type' )
932utils.handleEvent = function( event ) {
933  var method = 'on' + event.type;
934  if ( this[ method ] ) {
935    this[ method ]( event );
936  }
937};
938
939// ----- filterFindElements ----- //
940
941utils.filterFindElements = function( elems, selector ) {
942  // make array of elems
943  elems = utils.makeArray( elems );
944  var ffElems = [];
945
946  elems.forEach( function( elem ) {
947    // check that elem is an actual element
948    
948// START UNCODE EDIT
949    // if ( !( elem instanceof HTMLElement ) ) {
950    if ( !( elem instanceof HTMLElement ) && !SiteParameters.is_frontend_editor ) {
951    // END UNCODE EDIT
vendor: 75,264 bytes, lines 951-3856
951
952      return;
953    }
954    // add elem if no selector
955    if ( !selector ) {
956      ffElems.push( elem );
957      return;
958    }
959    // filter & find items if we have a selector
960    // filter
961    if ( matchesSelector( elem, selector ) ) {
962      ffElems.push( elem );
963    }
964    // find children
965    var childElems = elem.querySelectorAll( selector );
966    // concat childElems to filterFound array
967    for ( var i=0; i < childElems.length; i++ ) {
968      ffElems.push( childElems[i] );
969    }
970  });
971
972  return ffElems;
973};
974
975// ----- debounceMethod ----- //
976
977utils.debounceMethod = function( _class, methodName, threshold ) {
978  threshold = threshold || 100;
979  // original method
980  var method = _class.prototype[ methodName ];
981  var timeoutName = methodName + 'Timeout';
982
983  _class.prototype[ methodName ] = function() {
984    var timeout = this[ timeoutName ];
985    clearTimeout( timeout );
986
987    var args = arguments;
988    var _this = this;
989    this[ timeoutName ] = setTimeout( function() {
990      method.apply( _this, args );
991      delete _this[ timeoutName ];
992    }, threshold );
993  };
994};
995
996// ----- docReady ----- //
997
998utils.docReady = function( callback ) {
999  var readyState = document.readyState;
1000  if ( readyState == 'complete' || readyState == 'interactive' ) {
1001    // do async to allow for other scripts to run. metafizzy/flickity#441
1002    setTimeout( callback );
1003  } else {
1004    document.addEventListener( 'DOMContentLoaded', callback );
1005  }
1006};
1007
1008// ----- htmlInit ----- //
1009
1010// http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
1011utils.toDashed = function( str ) {
1012  return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
1013    return $1 + '-' + $2;
1014  }).toLowerCase();
1015};
1016
1017var console = window.console;
1018/**
1019 * allow user to initialize classes via [data-namespace] or .js-namespace class
1020 * htmlInit( Widget, 'widgetName' )
1021 * options are parsed from data-namespace-options
1022 */
1023utils.htmlInit = function( WidgetClass, namespace ) {
1024  utils.docReady( function() {
1025    var dashedNamespace = utils.toDashed( namespace );
1026    var dataAttr = 'data-' + dashedNamespace;
1027    var dataAttrElems = document.querySelectorAll( '[' + dataAttr + ']' );
1028    var jsDashElems = document.querySelectorAll( '.js-' + dashedNamespace );
1029    var elems = utils.makeArray( dataAttrElems )
1030      .concat( utils.makeArray( jsDashElems ) );
1031    var dataOptionsAttr = dataAttr + '-options';
1032    var jQuery = window.jQuery;
1033
1034    elems.forEach( function( elem ) {
1035      var attr = elem.getAttribute( dataAttr ) ||
1036        elem.getAttribute( dataOptionsAttr );
1037      var options;
1038      try {
1039        options = attr && JSON.parse( attr );
1040      } catch ( error ) {
1041        // log error, do not initialize
1042        if ( console ) {
1043          console.error( 'Error parsing ' + dataAttr + ' on ' + elem.className +
1044          ': ' + error );
1045        }
1046        return;
1047      }
1048      // initialize
1049      var instance = new WidgetClass( elem, options );
1050      // make available via $().data('namespace')
1051      if ( jQuery ) {
1052        jQuery.data( elem, namespace, instance );
1053      }
1054    });
1055
1056  });
1057};
1058
1059// -----  ----- //
1060
1061return utils;
1062
1063}));
1064
1065/**
1066 * Outlayer Item
1067 */
1068
1069( function( window, factory ) {
1070  // universal module definition
1071  /* jshint strict: false */ /* globals define, module, require */
1072  if ( typeof define == 'function' && define.amd ) {
1073    // AMD - RequireJS
1074    define( 'outlayer/item',[
1075        'ev-emitter/ev-emitter',
1076        'get-size/get-size'
1077      ],
1078      factory
1079    );
1080  } else if ( typeof module == 'object' && module.exports ) {
1081    // CommonJS - Browserify, Webpack
1082    module.exports = factory(
1083      require('ev-emitter'),
1084      require('get-size')
1085    );
1086  } else {
1087    // browser global
1088    window.Outlayer = {};
1089    window.Outlayer.Item = factory(
1090      window.EvEmitter,
1091      window.getSize
1092    );
1093  }
1094
1095}( window, function factory( EvEmitter, getSize ) {
1096'use strict';
1097
1098// ----- helpers ----- //
1099
1100function isEmptyObj( obj ) {
1101  for ( var prop in obj ) {
1102    return false;
1103  }
1104  prop = null;
1105  return true;
1106}
1107
1108// -------------------------- CSS3 support -------------------------- //
1109
1110
1111var docElemStyle = document.documentElement.style;
1112
1113var transitionProperty = typeof docElemStyle.transition == 'string' ?
1114  'transition' : 'WebkitTransition';
1115var transformProperty = typeof docElemStyle.transform == 'string' ?
1116  'transform' : 'WebkitTransform';
1117
1118var transitionEndEvent = {
1119  WebkitTransition: 'webkitTransitionEnd',
1120  transition: 'transitionend'
1121}[ transitionProperty ];
1122
1123// cache all vendor properties that could have vendor prefix
1124var vendorProperties = {
1125  transform: transformProperty,
1126  transition: transitionProperty,
1127  transitionDuration: transitionProperty + 'Duration',
1128  transitionProperty: transitionProperty + 'Property',
1129  transitionDelay: transitionProperty + 'Delay'
1130};
1131
1132// -------------------------- Item -------------------------- //
1133
1134function Item( element, layout ) {
1135  if ( !element ) {
1136    return;
1137  }
1138
1139  this.element = element;
1140  // parent layout class, i.e. Masonry, Isotope, or Packery
1141  this.layout = layout;
1142  this.position = {
1143    x: 0,
1144    y: 0
1145  };
1146
1147  this._create();
1148}
1149
1150// inherit EvEmitter
1151var proto = Item.prototype = Object.create( EvEmitter.prototype );
1152proto.constructor = Item;
1153
1154proto._create = function() {
1155  // transition objects
1156  this._transn = {
1157    ingProperties: {},
1158    clean: {},
1159    onEnd: {}
1160  };
1161
1162  this.css({
1163    position: 'absolute'
1164  });
1165};
1166
1167// trigger specified handler for event type
1168proto.handleEvent = function( event ) {
1169  var method = 'on' + event.type;
1170  if ( this[ method ] ) {
1171    this[ method ]( event );
1172  }
1173};
1174
1175proto.getSize = function() {
1176  this.size = getSize( this.element );
1177};
1178
1179/**
1180 * apply CSS styles to element
1181 * @param {Object} style
1182 */
1183proto.css = function( style ) {
1184  var elemStyle = this.element.style;
1185
1186  for ( var prop in style ) {
1187    // use vendor property if available
1188    var supportedProp = vendorProperties[ prop ] || prop;
1189    elemStyle[ supportedProp ] = style[ prop ];
1190  }
1191};
1192
1193 // measure position, and sets it
1194proto.getPosition = function() {
1195  var style = getComputedStyle( this.element );
1196  var isOriginLeft = this.layout._getOption('originLeft');
1197  var isOriginTop = this.layout._getOption('originTop');
1198  var xValue = style[ isOriginLeft ? 'left' : 'right' ];
1199  var yValue = style[ isOriginTop ? 'top' : 'bottom' ];
1200  var x = parseFloat( xValue );
1201  var y = parseFloat( yValue );
1202  // convert percent to pixels
1203  var layoutSize = this.layout.size;
1204  if ( xValue.indexOf('%') != -1 ) {
1205    x = ( x / 100 ) * layoutSize.width;
1206  }
1207  if ( yValue.indexOf('%') != -1 ) {
1208    y = ( y / 100 ) * layoutSize.height;
1209  }
1210  // clean up 'auto' or other non-integer values
1211  x = isNaN( x ) ? 0 : x;
1212  y = isNaN( y ) ? 0 : y;
1213  // remove padding from measurement
1214  x -= isOriginLeft ? layoutSize.paddingLeft : layoutSize.paddingRight;
1215  y -= isOriginTop ? layoutSize.paddingTop : layoutSize.paddingBottom;
1216
1217  this.position.x = x;
1218  this.position.y = y;
1219};
1220
1221// set settled position, apply padding
1222proto.layoutPosition = function() {
1223  var layoutSize = this.layout.size;
1224  var style = {};
1225  var isOriginLeft = this.layout._getOption('originLeft');
1226  var isOriginTop = this.layout._getOption('originTop');
1227
1228  // x
1229  var xPadding = isOriginLeft ? 'paddingLeft' : 'paddingRight';
1230  var xProperty = isOriginLeft ? 'left' : 'right';
1231  var xResetProperty = isOriginLeft ? 'right' : 'left';
1232
1233  var x = this.position.x + layoutSize[ xPadding ];
1234  // set in percentage or pixels
1235  style[ xProperty ] = this.getXValue( x );
1236  // reset other property
1237  style[ xResetProperty ] = '';
1238
1239  // y
1240  var yPadding = isOriginTop ? 'paddingTop' : 'paddingBottom';
1241  var yProperty = isOriginTop ? 'top' : 'bottom';
1242  var yResetProperty = isOriginTop ? 'bottom' : 'top';
1243
1244  var y = this.position.y + layoutSize[ yPadding ];
1245  // set in percentage or pixels
1246  style[ yProperty ] = this.getYValue( y );
1247  // reset other property
1248  style[ yResetProperty ] = '';
1249
1250  this.css( style );
1251  this.emitEvent( 'layout', [ this ] );
1252};
1253
1254proto.getXValue = function( x ) {
1255  var isHorizontal = this.layout._getOption('horizontal');
1256  return this.layout.options.percentPosition && !isHorizontal ?
1257    ( ( x / this.layout.size.width ) * 100 ) + '%' : x + 'px';
1258};
1259
1260proto.getYValue = function( y ) {
1261  var isHorizontal = this.layout._getOption('horizontal');
1262  return this.layout.options.percentPosition && isHorizontal ?
1263    ( ( y / this.layout.size.height ) * 100 ) + '%' : y + 'px';
1264};
1265
1266proto._transitionTo = function( x, y ) {
1267  this.getPosition();
1268  // get current x & y from top/left
1269  var curX = this.position.x;
1270  var curY = this.position.y;
1271
1272  var didNotMove = x == this.position.x && y == this.position.y;
1273
1274  // save end position
1275  this.setPosition( x, y );
1276
1277  // if did not move and not transitioning, just go to layout
1278  if ( didNotMove && !this.isTransitioning ) {
1279    this.layoutPosition();
1280    return;
1281  }
1282
1283  var transX = x - curX;
1284  var transY = y - curY;
1285  var transitionStyle = {};
1286  transitionStyle.transform = this.getTranslate( transX, transY );
1287
1288  this.transition({
1289    to: transitionStyle,
1290    onTransitionEnd: {
1291      transform: this.layoutPosition
1292    },
1293    isCleaning: true
1294  });
1295};
1296
1297proto.getTranslate = function( x, y ) {
1298  // flip cooridinates if origin on right or bottom
1299  var isOriginLeft = this.layout._getOption('originLeft');
1300  var isOriginTop = this.layout._getOption('originTop');
1301  x = isOriginLeft ? x : -x;
1302  y = isOriginTop ? y : -y;
1303  return 'translate3d(' + x + 'px, ' + y + 'px, 0)';
1304};
1305
1306// non transition + transform support
1307proto.goTo = function( x, y ) {
1308  this.setPosition( x, y );
1309  this.layoutPosition();
1310};
1311
1312proto.moveTo = proto._transitionTo;
1313
1314proto.setPosition = function( x, y ) {
1315  this.position.x = parseFloat( x );
1316  this.position.y = parseFloat( y );
1317};
1318
1319// ----- transition ----- //
1320
1321/**
1322 * @param {Object} style - CSS
1323 * @param {Function} onTransitionEnd
1324 */
1325
1326// non transition, just trigger callback
1327proto._nonTransition = function( args ) {
1328  this.css( args.to );
1329  if ( args.isCleaning ) {
1330    this._removeStyles( args.to );
1331  }
1332  for ( var prop in args.onTransitionEnd ) {
1333    args.onTransitionEnd[ prop ].call( this );
1334  }
1335};
1336
1337/**
1338 * proper transition
1339 * @param {Object} args - arguments
1340 *   @param {Object} to - style to transition to
1341 *   @param {Object} from - style to start transition from
1342 *   @param {Boolean} isCleaning - removes transition styles after transition
1343 *   @param {Function} onTransitionEnd - callback
1344 */
1345proto.transition = function( args ) {
1346  // redirect to nonTransition if no transition duration
1347  if ( !parseFloat( this.layout.options.transitionDuration ) ) {
1348    this._nonTransition( args );
1349    return;
1350  }
1351
1352  var _transition = this._transn;
1353  // keep track of onTransitionEnd callback by css property
1354  for ( var prop in args.onTransitionEnd ) {
1355    _transition.onEnd[ prop ] = args.onTransitionEnd[ prop ];
1356  }
1357  // keep track of properties that are transitioning
1358  for ( prop in args.to ) {
1359    _transition.ingProperties[ prop ] = true;
1360    // keep track of properties to clean up when transition is done
1361    if ( args.isCleaning ) {
1362      _transition.clean[ prop ] = true;
1363    }
1364  }
1365
1366  // set from styles
1367  if ( args.from ) {
1368    this.css( args.from );
1369    // force redraw. http://blog.alexmaccaw.com/css-transitions
1370    var h = this.element.offsetHeight;
1371    // hack for JSHint to hush about unused var
1372    h = null;
1373  }
1374  // enable transition
1375  this.enableTransition( args.to );
1376  // set styles that are transitioning
1377  this.css( args.to );
1378
1379  this.isTransitioning = true;
1380
1381};
1382
1383// dash before all cap letters, including first for
1384// WebkitTransform => -webkit-transform
1385function toDashedAll( str ) {
1386  return str.replace( /([A-Z])/g, function( $1 ) {
1387    return '-' + $1.toLowerCase();
1388  });
1389}
1390
1391var transitionProps = 'opacity,' + toDashedAll( transformProperty );
1392
1393proto.enableTransition = function(/* style */) {
1394  // HACK changing transitionProperty during a transition
1395  // will cause transition to jump
1396  if ( this.isTransitioning ) {
1397    return;
1398  }
1399
1400  // make `transition: foo, bar, baz` from style object
1401  // HACK un-comment this when enableTransition can work
1402  // while a transition is happening
1403  // var transitionValues = [];
1404  // for ( var prop in style ) {
1405  //   // dash-ify camelCased properties like WebkitTransition
1406  //   prop = vendorProperties[ prop ] || prop;
1407  //   transitionValues.push( toDashedAll( prop ) );
1408  // }
1409  // munge number to millisecond, to match stagger
1410  var duration = this.layout.options.transitionDuration;
1411  duration = typeof duration == 'number' ? duration + 'ms' : duration;
1412  // enable transition styles
1413  this.css({
1414    transitionProperty: transitionProps,
1415    transitionDuration: duration,
1416    transitionDelay: this.staggerDelay || 0
1417  });
1418  // listen for transition end event
1419  this.element.addEventListener( transitionEndEvent, this, false );
1420};
1421
1422// ----- events ----- //
1423
1424proto.onwebkitTransitionEnd = function( event ) {
1425  this.ontransitionend( event );
1426};
1427
1428proto.onotransitionend = function( event ) {
1429  this.ontransitionend( event );
1430};
1431
1432// properties that I munge to make my life easier
1433var dashedVendorProperties = {
1434  '-webkit-transform': 'transform'
1435};
1436
1437proto.ontransitionend = function( event ) {
1438  // disregard bubbled events from children
1439  if ( event.target !== this.element ) {
1440    return;
1441  }
1442  var _transition = this._transn;
1443  // get property name of transitioned property, convert to prefix-free
1444  var propertyName = dashedVendorProperties[ event.propertyName ] || event.propertyName;
1445
1446  // remove property that has completed transitioning
1447  delete _transition.ingProperties[ propertyName ];
1448  // check if any properties are still transitioning
1449  if ( isEmptyObj( _transition.ingProperties ) ) {
1450    // all properties have completed transitioning
1451    this.disableTransition();
1452  }
1453  // clean style
1454  if ( propertyName in _transition.clean ) {
1455    // clean up style
1456    this.element.style[ event.propertyName ] = '';
1457    delete _transition.clean[ propertyName ];
1458  }
1459  // trigger onTransitionEnd callback
1460  if ( propertyName in _transition.onEnd ) {
1461    var onTransitionEnd = _transition.onEnd[ propertyName ];
1462    onTransitionEnd.call( this );
1463    delete _transition.onEnd[ propertyName ];
1464  }
1465
1466  this.emitEvent( 'transitionEnd', [ this ] );
1467};
1468
1469proto.disableTransition = function() {
1470  this.removeTransitionStyles();
1471  this.element.removeEventListener( transitionEndEvent, this, false );
1472  this.isTransitioning = false;
1473};
1474
1475/**
1476 * removes style property from element
1477 * @param {Object} style
1478**/
1479proto._removeStyles = function( style ) {
1480  // clean up transition styles
1481  var cleanStyle = {};
1482  for ( var prop in style ) {
1483    cleanStyle[ prop ] = '';
1484  }
1485  this.css( cleanStyle );
1486};
1487
1488var cleanTransitionStyle = {
1489  transitionProperty: '',
1490  transitionDuration: '',
1491  transitionDelay: ''
1492};
1493
1494proto.removeTransitionStyles = function() {
1495  // remove transition
1496  this.css( cleanTransitionStyle );
1497};
1498
1499// ----- stagger ----- //
1500
1501proto.stagger = function( delay ) {
1502  delay = isNaN( delay ) ? 0 : delay;
1503  this.staggerDelay = delay + 'ms';
1504};
1505
1506// ----- show/hide/remove ----- //
1507
1508// remove element from DOM
1509proto.removeElem = function() {
1510  this.element.parentNode.removeChild( this.element );
1511  // remove display: none
1512  this.css({ display: '' });
1513  this.emitEvent( 'remove', [ this ] );
1514};
1515
1516proto.remove = function() {
1517  // just remove element if no transition support or no transition
1518  if ( !transitionProperty || !parseFloat( this.layout.options.transitionDuration ) ) {
1519    this.removeElem();
1520    return;
1521  }
1522
1523  // start transition
1524  this.once( 'transitionEnd', function() {
1525    this.removeElem();
1526  });
1527  this.hide();
1528};
1529
1530proto.reveal = function() {
1531  delete this.isHidden;
1532  // remove display: none
1533  this.css({ display: '' });
1534
1535  var options = this.layout.options;
1536
1537  var onTransitionEnd = {};
1538  var transitionEndProperty = this.getHideRevealTransitionEndProperty('visibleStyle');
1539  onTransitionEnd[ transitionEndProperty ] = this.onRevealTransitionEnd;
1540
1541  this.transition({
1542    from: options.hiddenStyle,
1543    to: options.visibleStyle,
1544    isCleaning: true,
1545    onTransitionEnd: onTransitionEnd
1546  });
1547};
1548
1549proto.onRevealTransitionEnd = function() {
1550  // check if still visible
1551  // during transition, item may have been hidden
1552  if ( !this.isHidden ) {
1553    this.emitEvent('reveal');
1554  }
1555};
1556
1557/**
1558 * get style property use for hide/reveal transition end
1559 * @param {String} styleProperty - hiddenStyle/visibleStyle
1560 * @returns {String}
1561 */
1562proto.getHideRevealTransitionEndProperty = function( styleProperty ) {
1563  var optionStyle = this.layout.options[ styleProperty ];
1564  // use opacity
1565  if ( optionStyle.opacity ) {
1566    return 'opacity';
1567  }
1568  // get first property
1569  for ( var prop in optionStyle ) {
1570    return prop;
1571  }
1572};
1573
1574proto.hide = function() {
1575  // set flag
1576  this.isHidden = true;
1577  // remove display: none
1578  this.css({ display: '' });
1579
1580  var options = this.layout.options;
1581
1582  var onTransitionEnd = {};
1583  var transitionEndProperty = this.getHideRevealTransitionEndProperty('hiddenStyle');
1584  onTransitionEnd[ transitionEndProperty ] = this.onHideTransitionEnd;
1585
1586  this.transition({
1587    from: options.visibleStyle,
1588    to: options.hiddenStyle,
1589    // keep hidden stuff hidden
1590    isCleaning: true,
1591    onTransitionEnd: onTransitionEnd
1592  });
1593};
1594
1595proto.onHideTransitionEnd = function() {
1596  // check if still hidden
1597  // during transition, item may have been un-hidden
1598  if ( this.isHidden ) {
1599    this.css({ display: 'none' });
1600    this.emitEvent('hide');
1601  }
1602};
1603
1604proto.destroy = function() {
1605  this.css({
1606    position: '',
1607    left: '',
1608    right: '',
1609    top: '',
1610    bottom: '',
1611    transition: '',
1612    transform: ''
1613  });
1614};
1615
1616return Item;
1617
1618}));
1619
1620/*!
1621 * Outlayer v2.1.1
1622 * the brains and guts of a layout library
1623 * MIT license
1624 */
1625
1626( function( window, factory ) {
1627  'use strict';
1628  // universal module definition
1629  /* jshint strict: false */ /* globals define, module, require */
1630  if ( typeof define == 'function' && define.amd ) {
1631    // AMD - RequireJS
1632    define( 'outlayer/outlayer',[
1633        'ev-emitter/ev-emitter',
1634        'get-size/get-size',
1635        'fizzy-ui-utils/utils',
1636        './item'
1637      ],
1638      function( EvEmitter, getSize, utils, Item ) {
1639        return factory( window, EvEmitter, getSize, utils, Item);
1640      }
1641    );
1642  } else if ( typeof module == 'object' && module.exports ) {
1643    // CommonJS - Browserify, Webpack
1644    module.exports = factory(
1645      window,
1646      require('ev-emitter'),
1647      require('get-size'),
1648      require('fizzy-ui-utils'),
1649      require('./item')
1650    );
1651  } else {
1652    // browser global
1653    window.Outlayer = factory(
1654      window,
1655      window.EvEmitter,
1656      window.getSize,
1657      window.fizzyUIUtils,
1658      window.Outlayer.Item
1659    );
1660  }
1661
1662}( window, function factory( window, EvEmitter, getSize, utils, Item ) {
1663'use strict';
1664
1665// ----- vars ----- //
1666
1667var console = window.console;
1668var jQuery = window.jQuery;
1669var noop = function() {};
1670
1671// -------------------------- Outlayer -------------------------- //
1672
1673// globally unique identifiers
1674var GUID = 0;
1675// internal store of all Outlayer intances
1676var instances = {};
1677
1678
1679/**
1680 * @param {Element, String} element
1681 * @param {Object} options
1682 * @constructor
1683 */
1684function Outlayer( element, options ) {
1685  var queryElement = utils.getQueryElement( element );
1686  if ( !queryElement ) {
1687    if ( console ) {
1688      console.error( 'Bad element for ' + this.constructor.namespace +
1689        ': ' + ( queryElement || element ) );
1690    }
1691    return;
1692  }
1693  this.element = queryElement;
1694  // add jQuery
1695  if ( jQuery ) {
1696    this.$element = jQuery( this.element );
1697  }
1698
1699  // options
1700  this.options = utils.extend( {}, this.constructor.defaults );
1701  this.option( options );
1702
1703  // add id for Outlayer.getFromElement
1704  var id = ++GUID;
1705  this.element.outlayerGUID = id; // expando
1706  instances[ id ] = this; // associate via id
1707
1708  // kick it off
1709  this._create();
1710
1711  var isInitLayout = this._getOption('initLayout');
1712  if ( isInitLayout ) {
1713    this.layout();
1714  }
1715}
1716
1717// settings are for internal use only
1718Outlayer.namespace = 'outlayer';
1719Outlayer.Item = Item;
1720
1721// default options
1722Outlayer.defaults = {
1723  containerStyle: {
1724    position: 'relative'
1725  },
1726  initLayout: true,
1727  originLeft: true,
1728  originTop: true,
1729  resize: true,
1730  resizeContainer: true,
1731  // item options
1732  transitionDuration: '0.4s',
1733  hiddenStyle: {
1734    opacity: 0,
1735    transform: 'scale(0.001)'
1736  },
1737  visibleStyle: {
1738    opacity: 1,
1739    transform: 'scale(1)'
1740  }
1741};
1742
1743var proto = Outlayer.prototype;
1744// inherit EvEmitter
1745utils.extend( proto, EvEmitter.prototype );
1746
1747/**
1748 * set options
1749 * @param {Object} opts
1750 */
1751proto.option = function( opts ) {
1752  utils.extend( this.options, opts );
1753};
1754
1755/**
1756 * get backwards compatible option value, check old name
1757 */
1758proto._getOption = function( option ) {
1759  var oldOption = this.constructor.compatOptions[ option ];
1760  return oldOption && this.options[ oldOption ] !== undefined ?
1761    this.options[ oldOption ] : this.options[ option ];
1762};
1763
1764Outlayer.compatOptions = {
1765  // currentName: oldName
1766  initLayout: 'isInitLayout',
1767  horizontal: 'isHorizontal',
1768  layoutInstant: 'isLayoutInstant',
1769  originLeft: 'isOriginLeft',
1770  originTop: 'isOriginTop',
1771  resize: 'isResizeBound',
1772  resizeContainer: 'isResizingContainer'
1773};
1774
1775proto._create = function() {
1776  // get items from children
1777  this.reloadItems();
1778  // elements that affect layout, but are not laid out
1779  this.stamps = [];
1780  this.stamp( this.options.stamp );
1781  // set container style
1782  utils.extend( this.element.style, this.options.containerStyle );
1783
1784  // bind resize method
1785  var canBindResize = this._getOption('resize');
1786  if ( canBindResize ) {
1787    this.bindResize();
1788  }
1789};
1790
1791// goes through all children again and gets bricks in proper order
1792proto.reloadItems = function() {
1793  // collection of item elements
1794  this.items = this._itemize( this.element.children );
1795};
1796
1797
1798/**
1799 * turn elements into Outlayer.Items to be used in layout
1800 * @param {Array or NodeList or HTMLElement} elems
1801 * @returns {Array} items - collection of new Outlayer Items
1802 */
1803proto._itemize = function( elems ) {
1804
1805  var itemElems = this._filterFindItemElements( elems );
1806  var Item = this.constructor.Item;
1807
1808  // create new Outlayer Items for collection
1809  var items = [];
1810  for ( var i=0; i < itemElems.length; i++ ) {
1811    var elem = itemElems[i];
1812    var item = new Item( elem, this );
1813    items.push( item );
1814  }
1815
1816  return items;
1817};
1818
1819/**
1820 * get item elements to be used in layout
1821 * @param {Array or NodeList or HTMLElement} elems
1822 * @returns {Array} items - item elements
1823 */
1824proto._filterFindItemElements = function( elems ) {
1825  return utils.filterFindElements( elems, this.options.itemSelector );
1826};
1827
1828/**
1829 * getter method for getting item elements
1830 * @returns {Array} elems - collection of item elements
1831 */
1832proto.getItemElements = function() {
1833  return this.items.map( function( item ) {
1834    return item.element;
1835  });
1836};
1837
1838// ----- init & layout ----- //
1839
1840/**
1841 * lays out all items
1842 */
1843proto.layout = function() {
1844  this._resetLayout();
1845  this._manageStamps();
1846
1847  // don't animate first layout
1848  var layoutInstant = this._getOption('layoutInstant');
1849  var isInstant = layoutInstant !== undefined ?
1850    layoutInstant : !this._isLayoutInited;
1851  this.layoutItems( this.items, isInstant );
1852
1853  // flag for initalized
1854  this._isLayoutInited = true;
1855};
1856
1857// _init is alias for layout
1858proto._init = proto.layout;
1859
1860/**
1861 * logic before any new layout
1862 */
1863proto._resetLayout = function() {
1864  this.getSize();
1865};
1866
1867
1868proto.getSize = function() {
1869  this.size = getSize( this.element );
1870};
1871
1872/**
1873 * get measurement from option, for columnWidth, rowHeight, gutter
1874 * if option is String -> get element from selector string, & get size of element
1875 * if option is Element -> get size of element
1876 * else use option as a number
1877 *
1878 * @param {String} measurement
1879 * @param {String} size - width or height
1880 * @private
1881 */
1882proto._getMeasurement = function( measurement, size ) {
1883  var option = this.options[ measurement ];
1884  var elem;
1885  if ( !option ) {
1886    // default to 0
1887    this[ measurement ] = 0;
1888  } else {
1889    // use option as an element
1890    if ( typeof option == 'string' ) {
1891      elem = this.element.querySelector( option );
1892    } else if ( option instanceof HTMLElement ) {
1893      elem = option;
1894    }
1895    // use size of element, if element
1896    this[ measurement ] = elem ? getSize( elem )[ size ] : option;
1897  }
1898};
1899
1900/**
1901 * layout a collection of item elements
1902 * @api public
1903 */
1904proto.layoutItems = function( items, isInstant ) {
1905  items = this._getItemsForLayout( items );
1906
1907  this._layoutItems( items, isInstant );
1908
1909  this._postLayout();
1910};
1911
1912/**
1913 * get the items to be laid out
1914 * you may want to skip over some items
1915 * @param {Array} items
1916 * @returns {Array} items
1917 */
1918proto._getItemsForLayout = function( items ) {
1919  return items.filter( function( item ) {
1920    return !item.isIgnored;
1921  });
1922};
1923
1924/**
1925 * layout items
1926 * @param {Array} items
1927 * @param {Boolean} isInstant
1928 */
1929proto._layoutItems = function( items, isInstant ) {
1930  this._emitCompleteOnItems( 'layout', items );
1931
1932  if ( !items || !items.length ) {
1933    // no items, emit event with empty array
1934    return;
1935  }
1936
1937  var queue = [];
1938
1939  items.forEach( function( item ) {
1940    // get x/y object from method
1941    var position = this._getItemLayoutPosition( item );
1942    // enqueue
1943    position.item = item;
1944    position.isInstant = isInstant || item.isLayoutInstant;
1945    queue.push( position );
1946  }, this );
1947
1948  this._processLayoutQueue( queue );
1949};
1950
1951/**
1952 * get item layout position
1953 * @param {Outlayer.Item} item
1954 * @returns {Object} x and y position
1955 */
1956proto._getItemLayoutPosition = function( /* item */ ) {
1957  return {
1958    x: 0,
1959    y: 0
1960  };
1961};
1962
1963/**
1964 * iterate over array and position each item
1965 * Reason being - separating this logic prevents 'layout invalidation'
1966 * thx @paul_irish
1967 * @param {Array} queue
1968 */
1969proto._processLayoutQueue = function( queue ) {
1970  this.updateStagger();
1971  queue.forEach( function( obj, i ) {
1972    this._positionItem( obj.item, obj.x, obj.y, obj.isInstant, i );
1973  }, this );
1974};
1975
1976// set stagger from option in milliseconds number
1977proto.updateStagger = function() {
1978  var stagger = this.options.stagger;
1979  if ( stagger === null || stagger === undefined ) {
1980    this.stagger = 0;
1981    return;
1982  }
1983  this.stagger = getMilliseconds( stagger );
1984  return this.stagger;
1985};
1986
1987/**
1988 * Sets position of item in DOM
1989 * @param {Outlayer.Item} item
1990 * @param {Number} x - horizontal position
1991 * @param {Number} y - vertical position
1992 * @param {Boolean} isInstant - disables transitions
1993 */
1994proto._positionItem = function( item, x, y, isInstant, i ) {
1995  if ( isInstant ) {
1996    // if not transition, just set CSS
1997    item.goTo( x, y );
1998  } else {
1999    item.stagger( i * this.stagger );
2000    item.moveTo( x, y );
2001  }
2002};
2003
2004/**
2005 * Any logic you want to do after each layout,
2006 * i.e. size the container
2007 */
2008proto._postLayout = function() {
2009  this.resizeContainer();
2010};
2011
2012proto.resizeContainer = function() {
2013  var isResizingContainer = this._getOption('resizeContainer');
2014  if ( !isResizingContainer ) {
2015    return;
2016  }
2017  var size = this._getContainerSize();
2018  if ( size ) {
2019    this._setContainerMeasure( size.width, true );
2020    this._setContainerMeasure( size.height, false );
2021  }
2022};
2023
2024/**
2025 * Sets width or height of container if returned
2026 * @returns {Object} size
2027 *   @param {Number} width
2028 *   @param {Number} height
2029 */
2030proto._getContainerSize = noop;
2031
2032/**
2033 * @param {Number} measure - size of width or height
2034 * @param {Boolean} isWidth
2035 */
2036proto._setContainerMeasure = function( measure, isWidth ) {
2037  if ( measure === undefined ) {
2038    return;
2039  }
2040
2041  var elemSize = this.size;
2042  // add padding and border width if border box
2043  if ( elemSize.isBorderBox ) {
2044    measure += isWidth ? elemSize.paddingLeft + elemSize.paddingRight +
2045      elemSize.borderLeftWidth + elemSize.borderRightWidth :
2046      elemSize.paddingBottom + elemSize.paddingTop +
2047      elemSize.borderTopWidth + elemSize.borderBottomWidth;
2048  }
2049
2050  measure = Math.max( measure, 0 );
2051  this.element.style[ isWidth ? 'width' : 'height' ] = measure + 'px';
2052};
2053
2054/**
2055 * emit eventComplete on a collection of items events
2056 * @param {String} eventName
2057 * @param {Array} items - Outlayer.Items
2058 */
2059proto._emitCompleteOnItems = function( eventName, items ) {
2060  var _this = this;
2061  function onComplete() {
2062    _this.dispatchEvent( eventName + 'Complete', null, [ items ] );
2063  }
2064
2065  var count = items.length;
2066  if ( !items || !count ) {
2067    onComplete();
2068    return;
2069  }
2070
2071  var doneCount = 0;
2072  function tick() {
2073    doneCount++;
2074    if ( doneCount == count ) {
2075      onComplete();
2076    }
2077  }
2078
2079  // bind callback
2080  items.forEach( function( item ) {
2081    item.once( eventName, tick );
2082  });
2083};
2084
2085/**
2086 * emits events via EvEmitter and jQuery events
2087 * @param {String} type - name of event
2088 * @param {Event} event - original event
2089 * @param {Array} args - extra arguments
2090 */
2091proto.dispatchEvent = function( type, event, args ) {
2092  // add original event to arguments
2093  var emitArgs = event ? [ event ].concat( args ) : args;
2094  this.emitEvent( type, emitArgs );
2095
2096  if ( jQuery ) {
2097    // set this.$element
2098    this.$element = this.$element || jQuery( this.element );
2099    if ( event ) {
2100      // create jQuery event
2101      var $event = jQuery.Event( event );
2102      $event.type = type;
2103      this.$element.trigger( $event, args );
2104    } else {
2105      // just trigger with type if no event available
2106      this.$element.trigger( type, args );
2107    }
2108  }
2109};
2110
2111// -------------------------- ignore & stamps -------------------------- //
2112
2113
2114/**
2115 * keep item in collection, but do not lay it out
2116 * ignored items do not get skipped in layout
2117 * @param {Element} elem
2118 */
2119proto.ignore = function( elem ) {
2120  var item = this.getItem( elem );
2121  if ( item ) {
2122    item.isIgnored = true;
2123  }
2124};
2125
2126/**
2127 * return item to layout collection
2128 * @param {Element} elem
2129 */
2130proto.unignore = function( elem ) {
2131  var item = this.getItem( elem );
2132  if ( item ) {
2133    delete item.isIgnored;
2134  }
2135};
2136
2137/**
2138 * adds elements to stamps
2139 * @param {NodeList, Array, Element, or String} elems
2140 */
2141proto.stamp = function( elems ) {
2142  elems = this._find( elems );
2143  if ( !elems ) {
2144    return;
2145  }
2146
2147  this.stamps = this.stamps.concat( elems );
2148  // ignore
2149  elems.forEach( this.ignore, this );
2150};
2151
2152/**
2153 * removes elements to stamps
2154 * @param {NodeList, Array, or Element} elems
2155 */
2156proto.unstamp = function( elems ) {
2157  elems = this._find( elems );
2158  if ( !elems ){
2159    return;
2160  }
2161
2162  elems.forEach( function( elem ) {
2163    // filter out removed stamp elements
2164    utils.removeFrom( this.stamps, elem );
2165    this.unignore( elem );
2166  }, this );
2167};
2168
2169/**
2170 * finds child elements
2171 * @param {NodeList, Array, Element, or String} elems
2172 * @returns {Array} elems
2173 */
2174proto._find = function( elems ) {
2175  if ( !elems ) {
2176    return;
2177  }
2178  // if string, use argument as selector string
2179  if ( typeof elems == 'string' ) {
2180    elems = this.element.querySelectorAll( elems );
2181  }
2182  elems = utils.makeArray( elems );
2183  return elems;
2184};
2185
2186proto._manageStamps = function() {
2187  if ( !this.stamps || !this.stamps.length ) {
2188    return;
2189  }
2190
2191  this._getBoundingRect();
2192
2193  this.stamps.forEach( this._manageStamp, this );
2194};
2195
2196// update boundingLeft / Top
2197proto._getBoundingRect = function() {
2198  // get bounding rect for container element
2199  var boundingRect = this.element.getBoundingClientRect();
2200  var size = this.size;
2201  this._boundingRect = {
2202    left: boundingRect.left + size.paddingLeft + size.borderLeftWidth,
2203    top: boundingRect.top + size.paddingTop + size.borderTopWidth,
2204    right: boundingRect.right - ( size.paddingRight + size.borderRightWidth ),
2205    bottom: boundingRect.bottom - ( size.paddingBottom + size.borderBottomWidth )
2206  };
2207};
2208
2209/**
2210 * @param {Element} stamp
2211**/
2212proto._manageStamp = noop;
2213
2214/**
2215 * get x/y position of element relative to container element
2216 * @param {Element} elem
2217 * @returns {Object} offset - has left, top, right, bottom
2218 */
2219proto._getElementOffset = function( elem ) {
2220  var boundingRect = elem.getBoundingClientRect();
2221  var thisRect = this._boundingRect;
2222  var size = getSize( elem );
2223  var offset = {
2224    left: boundingRect.left - thisRect.left - size.marginLeft,
2225    top: boundingRect.top - thisRect.top - size.marginTop,
2226    right: thisRect.right - boundingRect.right - size.marginRight,
2227    bottom: thisRect.bottom - boundingRect.bottom - size.marginBottom
2228  };
2229  return offset;
2230};
2231
2232// -------------------------- resize -------------------------- //
2233
2234// enable event handlers for listeners
2235// i.e. resize -> onresize
2236proto.handleEvent = utils.handleEvent;
2237
2238/**
2239 * Bind layout to window resizing
2240 */
2241proto.bindResize = function() {
2242  window.addEventListener( 'resize', this );
2243  this.isResizeBound = true;
2244};
2245
2246/**
2247 * Unbind layout to window resizing
2248 */
2249proto.unbindResize = function() {
2250  window.removeEventListener( 'resize', this );
2251  this.isResizeBound = false;
2252};
2253
2254proto.onresize = function() {
2255  this.resize();
2256};
2257
2258utils.debounceMethod( Outlayer, 'onresize', 100 );
2259
2260proto.resize = function() {
2261  // don't trigger if size did not change
2262  // or if resize was unbound. See #9
2263  if ( !this.isResizeBound || !this.needsResizeLayout() ) {
2264    return;
2265  }
2266
2267  this.layout();
2268};
2269
2270/**
2271 * check if layout is needed post layout
2272 * @returns Boolean
2273 */
2274proto.needsResizeLayout = function() {
2275  var size = getSize( this.element );
2276  // check that this.size and size are there
2277  // IE8 triggers resize on body size change, so they might not be
2278  var hasSizes = this.size && size;
2279  return hasSizes && size.innerWidth !== this.size.innerWidth;
2280};
2281
2282// -------------------------- methods -------------------------- //
2283
2284/**
2285 * add items to Outlayer instance
2286 * @param {Array or NodeList or Element} elems
2287 * @returns {Array} items - Outlayer.Items
2288**/
2289proto.addItems = function( elems ) {
2290  var items = this._itemize( elems );
2291  // add items to collection
2292  if ( items.length ) {
2293    this.items = this.items.concat( items );
2294  }
2295  return items;
2296};
2297
2298/**
2299 * Layout newly-appended item elements
2300 * @param {Array or NodeList or Element} elems
2301 */
2302proto.appended = function( elems ) {
2303  var items = this.addItems( elems );
2304  if ( !items.length ) {
2305    return;
2306  }
2307  // layout and reveal just the new items
2308  this.layoutItems( items, true );
2309  this.reveal( items );
2310};
2311
2312/**
2313 * Layout prepended elements
2314 * @param {Array or NodeList or Element} elems
2315 */
2316proto.prepended = function( elems ) {
2317  var items = this._itemize( elems );
2318  if ( !items.length ) {
2319    return;
2320  }
2321  // add items to beginning of collection
2322  var previousItems = this.items.slice(0);
2323  this.items = items.concat( previousItems );
2324  // start new layout
2325  this._resetLayout();
2326  this._manageStamps();
2327  // layout new stuff without transition
2328  this.layoutItems( items, true );
2329  this.reveal( items );
2330  // layout previous items
2331  this.layoutItems( previousItems );
2332};
2333
2334/**
2335 * reveal a collection of items
2336 * @param {Array of Outlayer.Items} items
2337 */
2338proto.reveal = function( items ) {
2339  this._emitCompleteOnItems( 'reveal', items );
2340  if ( !items || !items.length ) {
2341    return;
2342  }
2343  var stagger = this.updateStagger();
2344  items.forEach( function( item, i ) {
2345    item.stagger( i * stagger );
2346    item.reveal();
2347  });
2348};
2349
2350/**
2351 * hide a collection of items
2352 * @param {Array of Outlayer.Items} items
2353 */
2354proto.hide = function( items ) {
2355  this._emitCompleteOnItems( 'hide', items );
2356  if ( !items || !items.length ) {
2357    return;
2358  }
2359  var stagger = this.updateStagger();
2360  items.forEach( function( item, i ) {
2361    item.stagger( i * stagger );
2362    item.hide();
2363  });
2364};
2365
2366/**
2367 * reveal item elements
2368 * @param {Array}, {Element}, {NodeList} items
2369 */
2370proto.revealItemElements = function( elems ) {
2371  var items = this.getItems( elems );
2372  this.reveal( items );
2373};
2374
2375/**
2376 * hide item elements
2377 * @param {Array}, {Element}, {NodeList} items
2378 */
2379proto.hideItemElements = function( elems ) {
2380  var items = this.getItems( elems );
2381  this.hide( items );
2382};
2383
2384/**
2385 * get Outlayer.Item, given an Element
2386 * @param {Element} elem
2387 * @param {Function} callback
2388 * @returns {Outlayer.Item} item
2389 */
2390proto.getItem = function( elem ) {
2391  // loop through items to get the one that matches
2392  for ( var i=0; i < this.items.length; i++ ) {
2393    var item = this.items[i];
2394    if ( item.element == elem ) {
2395      // return item
2396      return item;
2397    }
2398  }
2399};
2400
2401/**
2402 * get collection of Outlayer.Items, given Elements
2403 * @param {Array} elems
2404 * @returns {Array} items - Outlayer.Items
2405 */
2406proto.getItems = function( elems ) {
2407  elems = utils.makeArray( elems );
2408  var items = [];
2409  elems.forEach( function( elem ) {
2410    var item = this.getItem( elem );
2411    if ( item ) {
2412      items.push( item );
2413    }
2414  }, this );
2415
2416  return items;
2417};
2418
2419/**
2420 * remove element(s) from instance and DOM
2421 * @param {Array or NodeList or Element} elems
2422 */
2423proto.remove = function( elems ) {
2424  var removeItems = this.getItems( elems );
2425
2426  this._emitCompleteOnItems( 'remove', removeItems );
2427
2428  // bail if no items to remove
2429  if ( !removeItems || !removeItems.length ) {
2430    return;
2431  }
2432
2433  removeItems.forEach( function( item ) {
2434    item.remove();
2435    // remove item from collection
2436    utils.removeFrom( this.items, item );
2437  }, this );
2438};
2439
2440// ----- destroy ----- //
2441
2442// remove and disable Outlayer instance
2443proto.destroy = function() {
2444  // clean up dynamic styles
2445  var style = this.element.style;
2446  style.height = '';
2447  style.position = '';
2448  style.width = '';
2449  // destroy items
2450  this.items.forEach( function( item ) {
2451    item.destroy();
2452  });
2453
2454  this.unbindResize();
2455
2456  var id = this.element.outlayerGUID;
2457  delete instances[ id ]; // remove reference to instance by id
2458  delete this.element.outlayerGUID;
2459  // remove data for jQuery
2460  if ( jQuery ) {
2461    jQuery.removeData( this.element, this.constructor.namespace );
2462  }
2463
2464};
2465
2466// -------------------------- data -------------------------- //
2467
2468/**
2469 * get Outlayer instance from element
2470 * @param {Element} elem
2471 * @returns {Outlayer}
2472 */
2473Outlayer.data = function( elem ) {
2474  elem = utils.getQueryElement( elem );
2475  var id = elem && elem.outlayerGUID;
2476  return id && instances[ id ];
2477};
2478
2479
2480// -------------------------- create Outlayer class -------------------------- //
2481
2482/**
2483 * create a layout class
2484 * @param {String} namespace
2485 */
2486Outlayer.create = function( namespace, options ) {
2487  // sub-class Outlayer
2488  var Layout = subclass( Outlayer );
2489  // apply new options and compatOptions
2490  Layout.defaults = utils.extend( {}, Outlayer.defaults );
2491  utils.extend( Layout.defaults, options );
2492  Layout.compatOptions = utils.extend( {}, Outlayer.compatOptions  );
2493
2494  Layout.namespace = namespace;
2495
2496  Layout.data = Outlayer.data;
2497
2498  // sub-class Item
2499  Layout.Item = subclass( Item );
2500
2501  // -------------------------- declarative -------------------------- //
2502
2503  utils.htmlInit( Layout, namespace );
2504
2505  // -------------------------- jQuery bridge -------------------------- //
2506
2507  // make into jQuery plugin
2508  if ( jQuery && jQuery.bridget ) {
2509    jQuery.bridget( namespace, Layout );
2510  }
2511
2512  return Layout;
2513};
2514
2515function subclass( Parent ) {
2516  function SubClass() {
2517    Parent.apply( this, arguments );
2518  }
2519
2520  SubClass.prototype = Object.create( Parent.prototype );
2521  SubClass.prototype.constructor = SubClass;
2522
2523  return SubClass;
2524}
2525
2526// ----- helpers ----- //
2527
2528// how many milliseconds are in each unit
2529var msUnits = {
2530  ms: 1,
2531  s: 1000
2532};
2533
2534// munge time-like parameter into millisecond number
2535// '0.4s' -> 40
2536function getMilliseconds( time ) {
2537  if ( typeof time == 'number' ) {
2538    return time;
2539  }
2540  var matches = time.match( /(^\d*\.?\d*)(\w*)/ );
2541  var num = matches && matches[1];
2542  var unit = matches && matches[2];
2543  if ( !num.length ) {
2544    return 0;
2545  }
2546  num = parseFloat( num );
2547  var mult = msUnits[ unit ] || 1;
2548  return num * mult;
2549}
2550
2551// ----- fin ----- //
2552
2553// back in global
2554Outlayer.Item = Item;
2555
2556return Outlayer;
2557
2558}));
2559
2560/**
2561 * Isotope Item
2562**/
2563
2564( function( window, factory ) {
2565  // universal module definition
2566  /* jshint strict: false */ /*globals define, module, require */
2567  if ( typeof define == 'function' && define.amd ) {
2568    // AMD
2569    define( 'isotope-layout/js/item',[
2570        'outlayer/outlayer'
2571      ],
2572      factory );
2573  } else if ( typeof module == 'object' && module.exports ) {
2574    // CommonJS
2575    module.exports = factory(
2576      require('outlayer')
2577    );
2578  } else {
2579    // browser global
2580    window.Isotope = window.Isotope || {};
2581    window.Isotope.Item = factory(
2582      window.Outlayer
2583    );
2584  }
2585
2586}( window, function factory( Outlayer ) {
2587'use strict';
2588
2589// -------------------------- Item -------------------------- //
2590
2591// sub-class Outlayer Item
2592function Item() {
2593  Outlayer.Item.apply( this, arguments );
2594}
2595
2596var proto = Item.prototype = Object.create( Outlayer.Item.prototype );
2597
2598var _create = proto._create;
2599proto._create = function() {
2600  // assign id, used for original-order sorting
2601  this.id = this.layout.itemGUID++;
2602  _create.call( this );
2603  this.sortData = {};
2604};
2605
2606proto.updateSortData = function() {
2607  if ( this.isIgnored ) {
2608    return;
2609  }
2610  // default sorters
2611  this.sortData.id = this.id;
2612  // for backward compatibility
2613  this.sortData['original-order'] = this.id;
2614  this.sortData.random = Math.random();
2615  // go thru getSortData obj and apply the sorters
2616  var getSortData = this.layout.options.getSortData;
2617  var sorters = this.layout._sorters;
2618  for ( var key in getSortData ) {
2619    var sorter = sorters[ key ];
2620    this.sortData[ key ] = sorter( this.element, this );
2621  }
2622};
2623
2624var _destroy = proto.destroy;
2625proto.destroy = function() {
2626  // call super
2627  _destroy.apply( this, arguments );
2628  // reset display, #741
2629  this.css({
2630    display: ''
2631  });
2632};
2633
2634return Item;
2635
2636}));
2637
2638/**
2639 * Isotope LayoutMode
2640 */
2641
2642( function( window, factory ) {
2643  // universal module definition
2644  /* jshint strict: false */ /*globals define, module, require */
2645  if ( typeof define == 'function' && define.amd ) {
2646    // AMD
2647    define( 'isotope-layout/js/layout-mode',[
2648        'get-size/get-size',
2649        'outlayer/outlayer'
2650      ],
2651      factory );
2652  } else if ( typeof module == 'object' && module.exports ) {
2653    // CommonJS
2654    module.exports = factory(
2655      require('get-size'),
2656      require('outlayer')
2657    );
2658  } else {
2659    // browser global
2660    window.Isotope = window.Isotope || {};
2661    window.Isotope.LayoutMode = factory(
2662      window.getSize,
2663      window.Outlayer
2664    );
2665  }
2666
2667}( window, function factory( getSize, Outlayer ) {
2668  'use strict';
2669
2670  // layout mode class
2671  function LayoutMode( isotope ) {
2672    this.isotope = isotope;
2673    // link properties
2674    if ( isotope ) {
2675      this.options = isotope.options[ this.namespace ];
2676      this.element = isotope.element;
2677      this.items = isotope.filteredItems;
2678      this.size = isotope.size;
2679    }
2680  }
2681
2682  var proto = LayoutMode.prototype;
2683
2684  /**
2685   * some methods should just defer to default Outlayer method
2686   * and reference the Isotope instance as `this`
2687  **/
2688  var facadeMethods = [
2689    '_resetLayout',
2690    '_getItemLayoutPosition',
2691    '_manageStamp',
2692    '_getContainerSize',
2693    '_getElementOffset',
2694    'needsResizeLayout',
2695    '_getOption'
2696  ];
2697
2698  facadeMethods.forEach( function( methodName ) {
2699    proto[ methodName ] = function() {
2700      return Outlayer.prototype[ methodName ].apply( this.isotope, arguments );
2701    };
2702  });
2703
2704  // -----  ----- //
2705
2706  // for horizontal layout modes, check vertical size
2707  proto.needsVerticalResizeLayout = function() {
2708    // don't trigger if size did not change
2709    var size = getSize( this.isotope.element );
2710    // check that this.size and size are there
2711    // IE8 triggers resize on body size change, so they might not be
2712    var hasSizes = this.isotope.size && size;
2713    return hasSizes && size.innerHeight != this.isotope.size.innerHeight;
2714  };
2715
2716  // ----- measurements ----- //
2717
2718  proto._getMeasurement = function() {
2719    this.isotope._getMeasurement.apply( this, arguments );
2720  };
2721
2722  proto.getColumnWidth = function() {
2723    this.getSegmentSize( 'column', 'Width' );
2724  };
2725
2726  proto.getRowHeight = function() {
2727    this.getSegmentSize( 'row', 'Height' );
2728  };
2729
2730  /**
2731   * get columnWidth or rowHeight
2732   * segment: 'column' or 'row'
2733   * size 'Width' or 'Height'
2734  **/
2735  proto.getSegmentSize = function( segment, size ) {
2736    var segmentName = segment + size;
2737    var outerSize = 'outer' + size;
2738    // columnWidth / outerWidth // rowHeight / outerHeight
2739    this._getMeasurement( segmentName, outerSize );
2740    // got rowHeight or columnWidth, we can chill
2741    if ( this[ segmentName ] ) {
2742      return;
2743    }
2744    // fall back to item of first element
2745    var firstItemSize = this.getFirstItemSize();
2746    this[ segmentName ] = firstItemSize && firstItemSize[ outerSize ] ||
2747      // or size of container
2748      this.isotope.size[ 'inner' + size ];
2749  };
2750
2751  proto.getFirstItemSize = function() {
2752    var firstItem = this.isotope.filteredItems[0];
2753    return firstItem && firstItem.element && getSize( firstItem.element );
2754  };
2755
2756  // ----- methods that should reference isotope ----- //
2757
2758  proto.layout = function() {
2759    this.isotope.layout.apply( this.isotope, arguments );
2760  };
2761
2762  proto.getSize = function() {
2763    this.isotope.getSize();
2764    this.size = this.isotope.size;
2765  };
2766
2767  // -------------------------- create -------------------------- //
2768
2769  LayoutMode.modes = {};
2770
2771  LayoutMode.create = function( namespace, options ) {
2772
2773    function Mode() {
2774      LayoutMode.apply( this, arguments );
2775    }
2776
2777    Mode.prototype = Object.create( proto );
2778    Mode.prototype.constructor = Mode;
2779
2780    // default options
2781    if ( options ) {
2782      Mode.options = options;
2783    }
2784
2785    Mode.prototype.namespace = namespace;
2786    // register in Isotope
2787    LayoutMode.modes[ namespace ] = Mode;
2788
2789    return Mode;
2790  };
2791
2792  return LayoutMode;
2793
2794}));
2795
2796/*!
2797 * Masonry v4.2.1
2798 * Cascading grid layout library
2799 * https://masonry.desandro.com
2800 * MIT License
2801 * by David DeSandro
2802 */
2803
2804( function( window, factory ) {
2805  // universal module definition
2806  /* jshint strict: false */ /*globals define, module, require */
2807  if ( typeof define == 'function' && define.amd ) {
2808    // AMD
2809    define( 'masonry-layout/masonry',[
2810        'outlayer/outlayer',
2811        'get-size/get-size'
2812      ],
2813      factory );
2814  } else if ( typeof module == 'object' && module.exports ) {
2815    // CommonJS
2816    module.exports = factory(
2817      require('outlayer'),
2818      require('get-size')
2819    );
2820  } else {
2821    // browser global
2822    window.Masonry = factory(
2823      window.Outlayer,
2824      window.getSize
2825    );
2826  }
2827
2828}( window, function factory( Outlayer, getSize ) {
2829
2830
2831
2832// -------------------------- masonryDefinition -------------------------- //
2833
2834  // create an Outlayer layout class
2835  var Masonry = Outlayer.create('masonry');
2836  // isFitWidth -> fitWidth
2837  Masonry.compatOptions.fitWidth = 'isFitWidth';
2838
2839  var proto = Masonry.prototype;
2840
2841  proto._resetLayout = function() {
2842    this.getSize();
2843    this._getMeasurement( 'columnWidth', 'outerWidth' );
2844    this._getMeasurement( 'gutter', 'outerWidth' );
2845    this.measureColumns();
2846
2847    // reset column Y
2848    this.colYs = [];
2849    for ( var i=0; i < this.cols; i++ ) {
2850      this.colYs.push( 0 );
2851    }
2852
2853    this.maxY = 0;
2854    this.horizontalColIndex = 0;
2855  };
2856
2857  proto.measureColumns = function() {
2858    this.getContainerWidth();
2859    // if columnWidth is 0, default to outerWidth of first item
2860    if ( !this.columnWidth ) {
2861      var firstItem = this.items[0];
2862      var firstItemElem = firstItem && firstItem.element;
2863      // columnWidth fall back to item of first element
2864      this.columnWidth = firstItemElem && getSize( firstItemElem ).outerWidth ||
2865        // if first elem has no width, default to size of container
2866        this.containerWidth;
2867    }
2868
2869    var columnWidth = this.columnWidth += this.gutter;
2870
2871    // calculate columns
2872    var containerWidth = this.containerWidth + this.gutter;
2873    var cols = containerWidth / columnWidth;
2874    // fix rounding errors, typically with gutters
2875    var excess = columnWidth - containerWidth % columnWidth;
2876    // if overshoot is less than a pixel, round up, otherwise floor it
2877    var mathMethod = excess && excess < 1 ? 'round' : 'floor';
2878    cols = Math[ mathMethod ]( cols );
2879    this.cols = Math.max( cols, 1 );
2880  };
2881
2882  proto.getContainerWidth = function() {
2883    // container is parent if fit width
2884    var isFitWidth = this._getOption('fitWidth');
2885    var container = isFitWidth ? this.element.parentNode : this.element;
2886    // check that this.size and size are there
2887    // IE8 triggers resize on body size change, so they might not be
2888    var size = getSize( container );
2889    this.containerWidth = size && size.innerWidth;
2890  };
2891
2892  proto._getItemLayoutPosition = function( item ) {
2893    item.getSize();
2894    // how many columns does this brick span
2895    var remainder = item.size.outerWidth % this.columnWidth;
2896    var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
2897    // round if off by 1 pixel, otherwise use ceil
2898    var colSpan = Math[ mathMethod ]( item.size.outerWidth / this.columnWidth );
2899    colSpan = Math.min( colSpan, this.cols );
2900    // use horizontal or top column position
2901    var colPosMethod = this.options.horizontalOrder ?
2902      '_getHorizontalColPosition' : '_getTopColPosition';
2903    var colPosition = this[ colPosMethod ]( colSpan, item );
2904    // position the brick
2905    var position = {
2906      x: this.columnWidth * colPosition.col,
2907      y: colPosition.y
2908    };
2909    // apply setHeight to necessary columns
2910    var setHeight = colPosition.y + item.size.outerHeight;
2911    var setMax = colSpan + colPosition.col;
2912    for ( var i = colPosition.col; i < setMax; i++ ) {
2913      this.colYs[i] = setHeight;
2914    }
2915
2916    return position;
2917  };
2918
2919  proto._getTopColPosition = function( colSpan ) {
2920    var colGroup = this._getTopColGroup( colSpan );
2921    // get the minimum Y value from the columns
2922    var minimumY = Math.min.apply( Math, colGroup );
2923
2924    return {
2925      col: colGroup.indexOf( minimumY ),
2926      y: minimumY,
2927    };
2928  };
2929
2930  /**
2931   * @param {Number} colSpan - number of columns the element spans
2932   * @returns {Array} colGroup
2933   */
2934  proto._getTopColGroup = function( colSpan ) {
2935    if ( colSpan < 2 ) {
2936      // if brick spans only one column, use all the column Ys
2937      return this.colYs;
2938    }
2939
2940    var colGroup = [];
2941    // how many different places could this brick fit horizontally
2942    var groupCount = this.cols + 1 - colSpan;
2943    // for each group potential horizontal position
2944    for ( var i = 0; i < groupCount; i++ ) {
2945      colGroup[i] = this._getColGroupY( i, colSpan );
2946    }
2947    return colGroup;
2948  };
2949
2950  proto._getColGroupY = function( col, colSpan ) {
2951    if ( colSpan < 2 ) {
2952      return this.colYs[ col ];
2953    }
2954    // make an array of colY values for that one group
2955    var groupColYs = this.colYs.slice( col, col + colSpan );
2956    // and get the max value of the array
2957    return Math.max.apply( Math, groupColYs );
2958  };
2959
2960  // get column position based on horizontal index. #873
2961  proto._getHorizontalColPosition = function( colSpan, item ) {
2962    var col = this.horizontalColIndex % this.cols;
2963    var isOver = colSpan > 1 && col + colSpan > this.cols;
2964    // shift to next row if item can't fit on current row
2965    col = isOver ? 0 : col;
2966    // don't let zero-size items take up space
2967    var hasSize = item.size.outerWidth && item.size.outerHeight;
2968    this.horizontalColIndex = hasSize ? col + colSpan : this.horizontalColIndex;
2969
2970    return {
2971      col: col,
2972      y: this._getColGroupY( col, colSpan ),
2973    };
2974  };
2975
2976  proto._manageStamp = function( stamp ) {
2977    var stampSize = getSize( stamp );
2978    var offset = this._getElementOffset( stamp );
2979    // get the columns that this stamp affects
2980    var isOriginLeft = this._getOption('originLeft');
2981    var firstX = isOriginLeft ? offset.left : offset.right;
2982    var lastX = firstX + stampSize.outerWidth;
2983    var firstCol = Math.floor( firstX / this.columnWidth );
2984    firstCol = Math.max( 0, firstCol );
2985    var lastCol = Math.floor( lastX / this.columnWidth );
2986    // lastCol should not go over if multiple of columnWidth #425
2987    lastCol -= lastX % this.columnWidth ? 0 : 1;
2988    lastCol = Math.min( this.cols - 1, lastCol );
2989    // set colYs to bottom of the stamp
2990
2991    var isOriginTop = this._getOption('originTop');
2992    var stampMaxY = ( isOriginTop ? offset.top : offset.bottom ) +
2993      stampSize.outerHeight;
2994    for ( var i = firstCol; i <= lastCol; i++ ) {
2995      this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
2996    }
2997  };
2998
2999  proto._getContainerSize = function() {
3000    this.maxY = Math.max.apply( Math, this.colYs );
3001    var size = {
3002      height: this.maxY
3003    };
3004
3005    if ( this._getOption('fitWidth') ) {
3006      size.width = this._getContainerFitWidth();
3007    }
3008
3009    return size;
3010  };
3011
3012  proto._getContainerFitWidth = function() {
3013    var unusedCols = 0;
3014    // count unused columns
3015    var i = this.cols;
3016    while ( --i ) {
3017      if ( this.colYs[i] !== 0 ) {
3018        break;
3019      }
3020      unusedCols++;
3021    }
3022    // fit container to columns that have been used
3023    return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
3024  };
3025
3026  proto.needsResizeLayout = function() {
3027    var previousWidth = this.containerWidth;
3028    this.getContainerWidth();
3029    return previousWidth != this.containerWidth;
3030  };
3031
3032  return Masonry;
3033
3034}));
3035
3036/*!
3037 * Masonry layout mode
3038 * sub-classes Masonry
3039 * https://masonry.desandro.com
3040 */
3041
3042( function( window, factory ) {
3043  // universal module definition
3044  /* jshint strict: false */ /*globals define, module, require */
3045  if ( typeof define == 'function' && define.amd ) {
3046    // AMD
3047    define( 'isotope-layout/js/layout-modes/masonry',[
3048        '../layout-mode',
3049        'masonry-layout/masonry'
3050      ],
3051      factory );
3052  } else if ( typeof module == 'object' && module.exports ) {
3053    // CommonJS
3054    module.exports = factory(
3055      require('../layout-mode'),
3056      require('masonry-layout')
3057    );
3058  } else {
3059    // browser global
3060    factory(
3061      window.Isotope.LayoutMode,
3062      window.Masonry
3063    );
3064  }
3065
3066}( window, function factory( LayoutMode, Masonry ) {
3067'use strict';
3068
3069// -------------------------- masonryDefinition -------------------------- //
3070
3071  // create an Outlayer layout class
3072  var MasonryMode = LayoutMode.create('masonry');
3073
3074  var proto = MasonryMode.prototype;
3075
3076  var keepModeMethods = {
3077    _getElementOffset: true,
3078    layout: true,
3079    _getMeasurement: true
3080  };
3081
3082  // inherit Masonry prototype
3083  for ( var method in Masonry.prototype ) {
3084    // do not inherit mode methods
3085    if ( !keepModeMethods[ method ] ) {
3086      proto[ method ] = Masonry.prototype[ method ];
3087    }
3088  }
3089
3090  var measureColumns = proto.measureColumns;
3091  proto.measureColumns = function() {
3092    // set items, used if measuring first item
3093    this.items = this.isotope.filteredItems;
3094    measureColumns.call( this );
3095  };
3096
3097  // point to mode options for fitWidth
3098  var _getOption = proto._getOption;
3099  proto._getOption = function( option ) {
3100    if ( option == 'fitWidth' ) {
3101      return this.options.isFitWidth !== undefined ?
3102        this.options.isFitWidth : this.options.fitWidth;
3103    }
3104    return _getOption.apply( this.isotope, arguments );
3105  };
3106
3107  return MasonryMode;
3108
3109}));
3110
3111/**
3112 * fitRows layout mode
3113 */
3114
3115( function( window, factory ) {
3116  // universal module definition
3117  /* jshint strict: false */ /*globals define, module, require */
3118  if ( typeof define == 'function' && define.amd ) {
3119    // AMD
3120    define( 'isotope-layout/js/layout-modes/fit-rows',[
3121        '../layout-mode'
3122      ],
3123      factory );
3124  } else if ( typeof exports == 'object' ) {
3125    // CommonJS
3126    module.exports = factory(
3127      require('../layout-mode')
3128    );
3129  } else {
3130    // browser global
3131    factory(
3132      window.Isotope.LayoutMode
3133    );
3134  }
3135
3136}( window, function factory( LayoutMode ) {
3137'use strict';
3138
3139var FitRows = LayoutMode.create('fitRows');
3140
3141var proto = FitRows.prototype;
3142
3143proto._resetLayout = function() {
3144  this.x = 0;
3145  this.y = 0;
3146  this.maxY = 0;
3147  this._getMeasurement( 'gutter', 'outerWidth' );
3148};
3149
3150proto._getItemLayoutPosition = function( item ) {
3151  item.getSize();
3152
3153  var itemWidth = item.size.outerWidth + this.gutter;
3154  // if this element cannot fit in the current row
3155  var containerWidth = this.isotope.size.innerWidth + this.gutter;
3156  if ( this.x !== 0 && itemWidth + this.x > containerWidth ) {
3157    this.x = 0;
3158    this.y = this.maxY;
3159  }
3160
3161  var position = {
3162    x: this.x,
3163    y: this.y
3164  };
3165
3166  this.maxY = Math.max( this.maxY, this.y + item.size.outerHeight );
3167  this.x += itemWidth;
3168
3169  return position;
3170};
3171
3172proto._getContainerSize = function() {
3173  return { height: this.maxY };
3174};
3175
3176return FitRows;
3177
3178}));
3179
3180/**
3181 * vertical layout mode
3182 */
3183
3184( function( window, factory ) {
3185  // universal module definition
3186  /* jshint strict: false */ /*globals define, module, require */
3187  if ( typeof define == 'function' && define.amd ) {
3188    // AMD
3189    define( 'isotope-layout/js/layout-modes/vertical',[
3190        '../layout-mode'
3191      ],
3192      factory );
3193  } else if ( typeof module == 'object' && module.exports ) {
3194    // CommonJS
3195    module.exports = factory(
3196      require('../layout-mode')
3197    );
3198  } else {
3199    // browser global
3200    factory(
3201      window.Isotope.LayoutMode
3202    );
3203  }
3204
3205}( window, function factory( LayoutMode ) {
3206'use strict';
3207
3208var Vertical = LayoutMode.create( 'vertical', {
3209  horizontalAlignment: 0
3210});
3211
3212var proto = Vertical.prototype;
3213
3214proto._resetLayout = function() {
3215  this.y = 0;
3216};
3217
3218proto._getItemLayoutPosition = function( item ) {
3219  item.getSize();
3220  var x = ( this.isotope.size.innerWidth - item.size.outerWidth ) *
3221    this.options.horizontalAlignment;
3222  var y = this.y;
3223  this.y += item.size.outerHeight;
3224  return { x: x, y: y };
3225};
3226
3227proto._getContainerSize = function() {
3228  return { height: this.y };
3229};
3230
3231return Vertical;
3232
3233}));
3234
3235/*!
3236 * Isotope v3.0.6
3237 *
3238 * Licensed GPLv3 for open source use
3239 * or Isotope Commercial License for commercial use
3240 *
3241 * https://isotope.metafizzy.co
3242 * Copyright 2010-2018 Metafizzy
3243 */
3244
3245( function( window, factory ) {
3246  // universal module definition
3247  /* jshint strict: false */ /*globals define, module, require */
3248  if ( typeof define == 'function' && define.amd ) {
3249    // AMD
3250    define( [
3251        'outlayer/outlayer',
3252        'get-size/get-size',
3253        'desandro-matches-selector/matches-selector',
3254        'fizzy-ui-utils/utils',
3255        'isotope-layout/js/item',
3256        'isotope-layout/js/layout-mode',
3257        // include default layout modes
3258        'isotope-layout/js/layout-modes/masonry',
3259        'isotope-layout/js/layout-modes/fit-rows',
3260        'isotope-layout/js/layout-modes/vertical'
3261      ],
3262      function( Outlayer, getSize, matchesSelector, utils, Item, LayoutMode ) {
3263        return factory( window, Outlayer, getSize, matchesSelector, utils, Item, LayoutMode );
3264      });
3265  } else if ( typeof module == 'object' && module.exports ) {
3266    // CommonJS
3267    module.exports = factory(
3268      window,
3269      require('outlayer'),
3270      require('get-size'),
3271      require('desandro-matches-selector'),
3272      require('fizzy-ui-utils'),
3273      require('isotope-layout/js/item'),
3274      require('isotope-layout/js/layout-mode'),
3275      // include default layout modes
3276      require('isotope-layout/js/layout-modes/masonry'),
3277      require('isotope-layout/js/layout-modes/fit-rows'),
3278      require('isotope-layout/js/layout-modes/vertical')
3279    );
3280  } else {
3281    // browser global
3282    window.Isotope = factory(
3283      window,
3284      window.Outlayer,
3285      window.getSize,
3286      window.matchesSelector,
3287      window.fizzyUIUtils,
3288      window.Isotope.Item,
3289      window.Isotope.LayoutMode
3290    );
3291  }
3292
3293}( window, function factory( window, Outlayer, getSize, matchesSelector, utils,
3294  Item, LayoutMode ) {
3295
3296
3297
3298// -------------------------- vars -------------------------- //
3299
3300var jQuery = window.jQuery;
3301
3302// -------------------------- helpers -------------------------- //
3303
3304var trim = String.prototype.trim ?
3305  function( str ) {
3306    return str.trim();
3307  } :
3308  function( str ) {
3309    return str.replace( /^\s+|\s+$/g, '' );
3310  };
3311
3312// -------------------------- isotopeDefinition -------------------------- //
3313
3314  // create an Outlayer layout class
3315  var Isotope = Outlayer.create( 'isotope', {
3316    layoutMode: 'masonry',
3317    isJQueryFiltering: true,
3318    sortAscending: true
3319  });
3320
3321  Isotope.Item = Item;
3322  Isotope.LayoutMode = LayoutMode;
3323
3324  var proto = Isotope.prototype;
3325
3326  proto._create = function() {
3327    this.itemGUID = 0;
3328    // functions that sort items
3329    this._sorters = {};
3330    this._getSorters();
3331    // call super
3332    Outlayer.prototype._create.call( this );
3333
3334    // create layout modes
3335    this.modes = {};
3336    // start filteredItems with all items
3337    this.filteredItems = this.items;
3338    // keep of track of sortBys
3339    this.sortHistory = [ 'original-order' ];
3340    // create from registered layout modes
3341    for ( var name in LayoutMode.modes ) {
3342      this._initLayoutMode( name );
3343    }
3344  };
3345
3346  proto.reloadItems = function() {
3347    // reset item ID counter
3348    this.itemGUID = 0;
3349    // call super
3350    Outlayer.prototype.reloadItems.call( this );
3351  };
3352
3353  proto._itemize = function() {
3354    var items = Outlayer.prototype._itemize.apply( this, arguments );
3355    // assign ID for original-order
3356    for ( var i=0; i < items.length; i++ ) {
3357      var item = items[i];
3358      item.id = this.itemGUID++;
3359    }
3360    this._updateItemsSortData( items );
3361    return items;
3362  };
3363
3364
3365  // -------------------------- layout -------------------------- //
3366
3367  proto._initLayoutMode = function( name ) {
3368    var Mode = LayoutMode.modes[ name ];
3369    // set mode options
3370    // HACK extend initial options, back-fill in default options
3371    var initialOpts = this.options[ name ] || {};
3372    this.options[ name ] = Mode.options ?
3373      utils.extend( Mode.options, initialOpts ) : initialOpts;
3374    // init layout mode instance
3375    this.modes[ name ] = new Mode( this );
3376  };
3377
3378
3379  proto.layout = function() {
3380    // if first time doing layout, do all magic
3381    if ( !this._isLayoutInited && this._getOption('initLayout') ) {
3382      this.arrange();
3383      return;
3384    }
3385    this._layout();
3386  };
3387
3388  // private method to be used in layout() & magic()
3389  proto._layout = function() {
3390    // don't animate first layout
3391    var isInstant = this._getIsInstant();
3392    // layout flow
3393    this._resetLayout();
3394    this._manageStamps();
3395    this.layoutItems( this.filteredItems, isInstant );
3396
3397    // flag for initalized
3398    this._isLayoutInited = true;
3399  };
3400
3401  // filter + sort + layout
3402  proto.arrange = function( opts ) {
3403    // set any options pass
3404    this.option( opts );
3405    this._getIsInstant();
3406    // filter, sort, and layout
3407
3408    // filter
3409    var filtered = this._filter( this.items );
3410    this.filteredItems = filtered.matches;
3411
3412    this._bindArrangeComplete();
3413
3414    if ( this._isInstant ) {
3415      this._noTransition( this._hideReveal, [ filtered ] );
3416    } else {
3417      this._hideReveal( filtered );
3418    }
3419
3420    this._sort();
3421    this._layout();
3422  };
3423  // alias to _init for main plugin method
3424  proto._init = proto.arrange;
3425
3426  proto._hideReveal = function( filtered ) {
3427    this.reveal( filtered.needReveal );
3428    this.hide( filtered.needHide );
3429  };
3430
3431  // HACK
3432  // Don't animate/transition first layout
3433  // Or don't animate/transition other layouts
3434  proto._getIsInstant = function() {
3435    var isLayoutInstant = this._getOption('layoutInstant');
3436    var isInstant = isLayoutInstant !== undefined ? isLayoutInstant :
3437      !this._isLayoutInited;
3438    this._isInstant = isInstant;
3439    return isInstant;
3440  };
3441
3442  // listen for layoutComplete, hideComplete and revealComplete
3443  // to trigger arrangeComplete
3444  proto._bindArrangeComplete = function() {
3445    // listen for 3 events to trigger arrangeComplete
3446    var isLayoutComplete, isHideComplete, isRevealComplete;
3447    var _this = this;
3448    function arrangeParallelCallback() {
3449      if ( isLayoutComplete && isHideComplete && isRevealComplete ) {
3450        _this.dispatchEvent( 'arrangeComplete', null, [ _this.filteredItems ] );
3451      }
3452    }
3453    this.once( 'layoutComplete', function() {
3454      isLayoutComplete = true;
3455      arrangeParallelCallback();
3456    });
3457    this.once( 'hideComplete', function() {
3458      isHideComplete = true;
3459      arrangeParallelCallback();
3460    });
3461    this.once( 'revealComplete', function() {
3462      isRevealComplete = true;
3463      arrangeParallelCallback();
3464    });
3465  };
3466
3467  // -------------------------- filter -------------------------- //
3468
3469  proto._filter = function( items ) {
3470    var filter = this.options.filter;
3471    filter = filter || '*';
3472    var matches = [];
3473    var hiddenMatched = [];
3474    var visibleUnmatched = [];
3475
3476    var test = this._getFilterTest( filter );
3477
3478    // test each item
3479    for ( var i=0; i < items.length; i++ ) {
3480      var item = items[i];
3481      if ( item.isIgnored ) {
3482        continue;
3483      }
3484      // add item to either matched or unmatched group
3485      var isMatched = test( item );
3486      // item.isFilterMatched = isMatched;
3487      // add to matches if its a match
3488      if ( isMatched ) {
3489        matches.push( item );
3490      }
3491      // add to additional group if item needs to be hidden or revealed
3492      if ( isMatched && item.isHidden ) {
3493        hiddenMatched.push( item );
3494      } else if ( !isMatched && !item.isHidden ) {
3495        visibleUnmatched.push( item );
3496      }
3497    }
3498
3499    // return collections of items to be manipulated
3500    return {
3501      matches: matches,
3502      needReveal: hiddenMatched,
3503      needHide: visibleUnmatched
3504    };
3505  };
3506
3507  // get a jQuery, function, or a matchesSelector test given the filter
3508  proto._getFilterTest = function( filter ) {
3509    if ( jQuery && this.options.isJQueryFiltering ) {
3510      // use jQuery
3511      return function( item ) {
3512        return jQuery( item.element ).is( filter );
3513      };
3514    }
3515    if ( typeof filter == 'function' ) {
3516      // use filter as function
3517      return function( item ) {
3518        return filter( item.element );
3519      };
3520    }
3521    // default, use filter as selector string
3522    return function( item ) {
3523      return matchesSelector( item.element, filter );
3524    };
3525  };
3526
3527  // -------------------------- sorting -------------------------- //
3528
3529  /**
3530   * @params {Array} elems
3531   * @public
3532   */
3533  proto.updateSortData = function( elems ) {
3534    // get items
3535    var items;
3536    if ( elems ) {
3537      elems = utils.makeArray( elems );
3538      items = this.getItems( elems );
3539    } else {
3540      // update all items if no elems provided
3541      items = this.items;
3542    }
3543
3544    this._getSorters();
3545    this._updateItemsSortData( items );
3546  };
3547
3548  proto._getSorters = function() {
3549    var getSortData = this.options.getSortData;
3550    for ( var key in getSortData ) {
3551      var sorter = getSortData[ key ];
3552      this._sorters[ key ] = mungeSorter( sorter );
3553    }
3554  };
3555
3556  /**
3557   * @params {Array} items - of Isotope.Items
3558   * @private
3559   */
3560  proto._updateItemsSortData = function( items ) {
3561    // do not update if no items
3562    var len = items && items.length;
3563
3564    for ( var i=0; len && i < len; i++ ) {
3565      var item = items[i];
3566      item.updateSortData();
3567    }
3568  };
3569
3570  // ----- munge sorter ----- //
3571
3572  // encapsulate this, as we just need mungeSorter
3573  // other functions in here are just for munging
3574  var mungeSorter = ( function() {
3575    // add a magic layer to sorters for convienent shorthands
3576    // `.foo-bar` will use the text of .foo-bar querySelector
3577    // `[foo-bar]` will use attribute
3578    // you can also add parser
3579    // `.foo-bar parseInt` will parse that as a number
3580    function mungeSorter( sorter ) {
3581      // if not a string, return function or whatever it is
3582      if ( typeof sorter != 'string' ) {
3583        return sorter;
3584      }
3585      // parse the sorter string
3586      var args = trim( sorter ).split(' ');
3587      var query = args[0];
3588      // check if query looks like [an-attribute]
3589      var attrMatch = query.match( /^\[(.+)\]$/ );
3590      var attr = attrMatch && attrMatch[1];
3591      var getValue = getValueGetter( attr, query );
3592      // use second argument as a parser
3593      var parser = Isotope.sortDataParsers[ args[1] ];
3594      // parse the value, if there was a parser
3595      sorter = parser ? function( elem ) {
3596        return elem && parser( getValue( elem ) );
3597      } :
3598      // otherwise just return value
3599      function( elem ) {
3600        return elem && getValue( elem );
3601      };
3602
3603      return sorter;
3604    }
3605
3606    // get an attribute getter, or get text of the querySelector
3607    function getValueGetter( attr, query ) {
3608      // if query looks like [foo-bar], get attribute
3609      if ( attr ) {
3610        return function getAttribute( elem ) {
3611          return elem.getAttribute( attr );
3612        };
3613      }
3614
3615      // otherwise, assume its a querySelector, and get its text
3616      return function getChildText( elem ) {
3617        var child = elem.querySelector( query );
3618        return child && child.textContent;
3619      };
3620    }
3621
3622    return mungeSorter;
3623  })();
3624
3625  // parsers used in getSortData shortcut strings
3626  Isotope.sortDataParsers = {
3627    'parseInt': function( val ) {
3628      return parseInt( val, 10 );
3629    },
3630    'parseFloat': function( val ) {
3631      return parseFloat( val );
3632    }
3633  };
3634
3635  // ----- sort method ----- //
3636
3637  // sort filteredItem order
3638  proto._sort = function() {
3639    if ( !this.options.sortBy ) {
3640      return;
3641    }
3642    // keep track of sortBy History
3643    var sortBys = utils.makeArray( this.options.sortBy );
3644    if ( !this._getIsSameSortBy( sortBys ) ) {
3645      // concat all sortBy and sortHistory, add to front, oldest goes in last
3646      this.sortHistory = sortBys.concat( this.sortHistory );
3647    }
3648    // sort magic
3649    var itemSorter = getItemSorter( this.sortHistory, this.options.sortAscending );
3650    this.filteredItems.sort( itemSorter );
3651  };
3652
3653  // check if sortBys is same as start of sortHistory
3654  proto._getIsSameSortBy = function( sortBys ) {
3655    for ( var i=0; i < sortBys.length; i++ ) {
3656      if ( sortBys[i] != this.sortHistory[i] ) {
3657        return false;
3658      }
3659    }
3660    return true;
3661  };
3662
3663  // returns a function used for sorting
3664  function getItemSorter( sortBys, sortAsc ) {
3665    return function sorter( itemA, itemB ) {
3666      // cycle through all sortKeys
3667      for ( var i = 0; i < sortBys.length; i++ ) {
3668        var sortBy = sortBys[i];
3669        var a = itemA.sortData[ sortBy ];
3670        var b = itemB.sortData[ sortBy ];
3671        if ( a > b || a < b ) {
3672          // if sortAsc is an object, use the value given the sortBy key
3673          var isAscending = sortAsc[ sortBy ] !== undefined ? sortAsc[ sortBy ] : sortAsc;
3674          var direction = isAscending ? 1 : -1;
3675          return ( a > b ? 1 : -1 ) * direction;
3676        }
3677      }
3678      return 0;
3679    };
3680  }
3681
3682  // -------------------------- methods -------------------------- //
3683
3684  // get layout mode
3685  proto._mode = function() {
3686    var layoutMode = this.options.layoutMode;
3687    var mode = this.modes[ layoutMode ];
3688    if ( !mode ) {
3689      // TODO console.error
3690      throw new Error( 'No layout mode: ' + layoutMode );
3691    }
3692    // HACK sync mode's options
3693    // any options set after init for layout mode need to be synced
3694    mode.options = this.options[ layoutMode ];
3695    return mode;
3696  };
3697
3698  proto._resetLayout = function() {
3699    // trigger original reset layout
3700    Outlayer.prototype._resetLayout.call( this );
3701    this._mode()._resetLayout();
3702  };
3703
3704  proto._getItemLayoutPosition = function( item  ) {
3705    return this._mode()._getItemLayoutPosition( item );
3706  };
3707
3708  proto._manageStamp = function( stamp ) {
3709    this._mode()._manageStamp( stamp );
3710  };
3711
3712  proto._getContainerSize = function() {
3713    return this._mode()._getContainerSize();
3714  };
3715
3716  proto.needsResizeLayout = function() {
3717    return this._mode().needsResizeLayout();
3718  };
3719
3720  // -------------------------- adding & removing -------------------------- //
3721
3722  // HEADS UP overwrites default Outlayer appended
3723  proto.appended = function( elems ) {
3724    var items = this.addItems( elems );
3725    if ( !items.length ) {
3726      return;
3727    }
3728    // filter, layout, reveal new items
3729    var filteredItems = this._filterRevealAdded( items );
3730    // add to filteredItems
3731    this.filteredItems = this.filteredItems.concat( filteredItems );
3732  };
3733
3734  // HEADS UP overwrites default Outlayer prepended
3735  proto.prepended = function( elems ) {
3736    var items = this._itemize( elems );
3737    if ( !items.length ) {
3738      return;
3739    }
3740    // start new layout
3741    this._resetLayout();
3742    this._manageStamps();
3743    // filter, layout, reveal new items
3744    var filteredItems = this._filterRevealAdded( items );
3745    // layout previous items
3746    this.layoutItems( this.filteredItems );
3747    // add to items and filteredItems
3748    this.filteredItems = filteredItems.concat( this.filteredItems );
3749    this.items = items.concat( this.items );
3750  };
3751
3752  proto._filterRevealAdded = function( items ) {
3753    var filtered = this._filter( items );
3754    this.hide( filtered.needHide );
3755    // reveal all new items
3756    this.reveal( filtered.matches );
3757    // layout new items, no transition
3758    this.layoutItems( filtered.matches, true );
3759    return filtered.matches;
3760  };
3761
3762  /**
3763   * Filter, sort, and layout newly-appended item elements
3764   * @param {Array or NodeList or Element} elems
3765   */
3766  proto.insert = function( elems ) {
3767    var items = this.addItems( elems );
3768    if ( !items.length ) {
3769      return;
3770    }
3771    // append item elements
3772    var i, item;
3773    var len = items.length;
3774    for ( i=0; i < len; i++ ) {
3775      item = items[i];
3776      this.element.appendChild( item.element );
3777    }
3778    // filter new stuff
3779    var filteredInsertItems = this._filter( items ).matches;
3780    // set flag
3781    for ( i=0; i < len; i++ ) {
3782      items[i].isLayoutInstant = true;
3783    }
3784    this.arrange();
3785    // reset flag
3786    for ( i=0; i < len; i++ ) {
3787      delete items[i].isLayoutInstant;
3788    }
3789    this.reveal( filteredInsertItems );
3790  };
3791
3792  var _remove = proto.remove;
3793  proto.remove = function( elems ) {
3794    elems = utils.makeArray( elems );
3795    var removeItems = this.getItems( elems );
3796    // do regular thing
3797    _remove.call( this, elems );
3798    // bail if no items to remove
3799    var len = removeItems && removeItems.length;
3800    // remove elems from filteredItems
3801    for ( var i=0; len && i < len; i++ ) {
3802      var item = removeItems[i];
3803      // remove item from collection
3804      utils.removeFrom( this.filteredItems, item );
3805    }
3806  };
3807
3808  proto.shuffle = function() {
3809    // update random sortData
3810    for ( var i=0; i < this.items.length; i++ ) {
3811      var item = this.items[i];
3812      item.sortData.random = Math.random();
3813    }
3814    this.options.sortBy = 'random';
3815    this._sort();
3816    this._layout();
3817  };
3818
3819  /**
3820   * trigger fn without transition
3821   * kind of hacky to have this in the first place
3822   * @param {Function} fn
3823   * @param {Array} args
3824   * @returns ret
3825   * @private
3826   */
3827  proto._noTransition = function( fn, args ) {
3828    // save transitionDuration before disabling
3829    var transitionDuration = this.options.transitionDuration;
3830    // disable transition
3831    this.options.transitionDuration = 0;
3832    // do it
3833    var returnValue = fn.apply( this, args );
3834    // re-enable transition for reveal
3835    this.options.transitionDuration = transitionDuration;
3836    return returnValue;
3837  };
3838
3839  // ----- helper methods ----- //
3840
3841  /**
3842   * getter method for getting filtered item elements
3843   * @returns {Array} elems - collection of item elements
3844   */
3845  proto.getFilteredItemElements = function() {
3846    return this.filteredItems.map( function( item ) {
3847      return item.element;
3848    });
3849  };
3850
3851  // -----  ----- //
3852
3853  return Isotope;
3854
3855}));
3856
3857/*!
3858 * Packery layout mode PACKAGED v2.0.1
3859 * sub-classes Packery
3860 */
3861
3862/**
3863 * Rect
3864 * low-level utility class for basic geometry
3865 */
3866
3867( function( window, factory ) {
3868  // universal module definition
3869  /* jshint strict: false */ /* globals define, module */
3870  if ( typeof define == 'function' && define.amd ) {
3871    // AMD
3872    define( 'packery/js/rect',factory );
3873  } else if ( typeof module == 'object' && module.exports ) {
3874    // CommonJS
3875    module.exports = factory();
3876  } else {
3877    // browser global
3878    window.Packery = window.Packery || {};
3879    window.Packery.Rect = factory();
3880  }
3881
3882}( window, function factory() {
3883
3884
3885// -------------------------- Rect -------------------------- //
3886
3887function Rect( props ) {
3888  // extend properties from defaults
3889  for ( var prop in Rect.defaults ) {
3890    this[ prop ] = Rect.defaults[ prop ];
3891  }
3892
3893  for ( prop in props ) {
3894    this[ prop ] = props[ prop ];
3895  }
3896
3897}
3898
3899Rect.defaults = {
3900  x: 0,
3901  y: 0,
3902  width: 0,
3903  height: 0
3904};
3905
3906var proto = Rect.prototype;
3907
3908/**
3909 * Determines whether or not this rectangle wholly encloses another rectangle or point.
3910 * @param {Rect} rect
3911 * @returns {Boolean}
3912**/
3913proto.contains = function( rect ) {
3914  // points don't have width or height
3915  var otherWidth = rect.width || 0;
3916  var otherHeight = rect.height || 0;
3917  return this.x <= rect.x &&
3918    this.y <= rect.y &&
3919    this.x + this.width >= rect.x + otherWidth &&
3920    this.y + this.height >= rect.y + otherHeight;
3921};
3922
3923/**
3924 * Determines whether or not the rectangle intersects with another.
3925 * @param {Rect} rect
3926 * @returns {Boolean}
3927**/
3928proto.overlaps = function( rect ) {
3929  var thisRight = this.x + this.width;
3930  var thisBottom = this.y + this.height;
3931  var rectRight = rect.x + rect.width;
3932  var rectBottom = rect.y + rect.height;
3933
3934  // http://stackoverflow.com/a/306332
3935  return this.x < rectRight &&
3936    thisRight > rect.x &&
3937    this.y < rectBottom &&
3938    thisBottom > rect.y;
3939};
3940
3941/**
3942 * @param {Rect} rect - the overlapping rect
3943 * @returns {Array} freeRects - rects representing the area around the rect
3944**/
3945proto.getMaximalFreeRects = function( rect ) {
3946
3947  // if no intersection, return false
3948  if ( !this.overlaps( rect ) ) {
3949    return false;
3950  }
3951
3952  var freeRects = [];
3953  var freeRect;
3954
3955  var thisRight = this.x + this.width;
3956  var thisBottom = this.y + this.height;
3957  var rectRight = rect.x + rect.width;
3958  var rectBottom = rect.y + rect.height;
3959
3960  // top
3961  if ( this.y < rect.y ) {
3962    freeRect = new Rect({
3963      x: this.x,
3964      y: this.y,
3965      width: this.width,
3966      height: rect.y - this.y
3967    });
3968    freeRects.push( freeRect );
3969  }
3970
3971  // right
3972  if ( thisRight > rectRight ) {
3973    freeRect = new Rect({
3974      x: rectRight,
3975      y: this.y,
3976      width: thisRight - rectRight,
3977      height: this.height
3978    });
3979    freeRects.push( freeRect );
3980  }
3981
3982  // bottom
3983  if ( thisBottom > rectBottom ) {
3984    freeRect = new Rect({
3985      x: this.x,
3986      y: rectBottom,
3987      width: this.width,
3988      height: thisBottom - rectBottom
3989    });
3990    freeRects.push( freeRect );
3991  }
3992
3993  // left
3994  if ( this.x < rect.x ) {
3995    freeRect = new Rect({
3996      x: this.x,
3997      y: this.y,
3998      width: rect.x - this.x,
3999      height: this.height
4000    });
4001    freeRects.push( freeRect );
4002  }
4003
4004  return freeRects;
4005};
4006
4007proto.canFit = function( rect ) {
4008  return this.width >= rect.width && this.height >= rect.height;
4009};
4010
4011return Rect;
4012
4013}));
4014
4015/**
4016 * Packer
4017 * bin-packing algorithm
4018 */
4019
4020( function( window, factory ) {
4021  // universal module definition
4022  /* jshint strict: false */ /* globals define, module, require */
4023  if ( typeof define == 'function' && define.amd ) {
4024    // AMD
4025    define( 'packery/js/packer',[ './rect' ], factory );
4026  } else if ( typeof module == 'object' && module.exports ) {
4027    // CommonJS
4028    module.exports = factory(
4029      require('./rect')
4030    );
4031  } else {
4032    // browser global
4033    var Packery = window.Packery = window.Packery || {};
4034    Packery.Packer = factory( Packery.Rect );
4035  }
4036
4037}( window, function factory( Rect ) {
4038
4039
4040// -------------------------- Packer -------------------------- //
4041
4042/**
4043 * @param {Number} width
4044 * @param {Number} height
4045 * @param {String} sortDirection
4046 *   topLeft for vertical, leftTop for horizontal
4047 */
4048function Packer( width, height, sortDirection ) {
4049  this.width = width || 0;
4050  this.height = height || 0;
4051  this.sortDirection = sortDirection || 'downwardLeftToRight';
4052
4053  this.reset();
4054}
4055
4056var proto = Packer.prototype;
4057
4058proto.reset = function() {
4059  this.spaces = [];
4060
4061  var initialSpace = new Rect({
4062    x: 0,
4063    y: 0,
4064    width: this.width,
4065    height: this.height
4066  });
4067
4068  this.spaces.push( initialSpace );
4069  // set sorter
4070  this.sorter = sorters[ this.sortDirection ] || sorters.downwardLeftToRight;
4071};
4072
4073// change x and y of rect to fit with in Packer's available space
4073s
4074proto.pack = function( rect ) {
4075  for ( var i=0; i < this.spaces.length; i++ ) {
4076    var space = this.spaces[i];
4077    if ( space.canFit( rect ) ) {
4078      this.placeInSpace( rect, space );
4079      break;
4080    }
4081  }
4082};
4083
4084proto.columnPack = function( rect ) {
4085  for ( var i=0; i < this.spaces.length; i++ ) {
4086    var space = this.spaces[i];
4087    var canFitInSpaceColumn = space.x <= rect.x &&
4088      space.x + space.width >= rect.x + rect.width &&
4089      space.height >= rect.height - 0.01; // fudge number for rounding error
4090    if ( canFitInSpaceColumn ) {
4091      rect.y = space.y;
4092      this.placed( rect );
4093      break;
4094    }
4095  }
4096};
4097
4098proto.rowPack = function( rect ) {
4099  for ( var i=0; i < this.spaces.length; i++ ) {
4100    var space = this.spaces[i];
4101    var canFitInSpaceRow = space.y <= rect.y &&
4102      space.y + space.height >= rect.y + rect.height &&
4103      space.width >= rect.width - 0.01; // fudge number for rounding error
4104    if ( canFitInSpaceRow ) {
4105      rect.x = space.x;
4106      this.placed( rect );
4107      break;
4108    }
4109  }
4110};
4111
4112proto.placeInSpace = function( rect, space ) {
4113  // place rect in space
4114  rect.x = space.x;
4115  rect.y = space.y;
4116
4117  this.placed( rect );
4118};
4119
4120// update spaces with placed rect
4121proto.placed = function( rect ) {
4122  // update spaces
4123  var revisedSpaces = [];
4124  for ( var i=0; i < this.spaces.length; i++ ) {
4125    var space = this.spaces[i];
4126    var newSpaces = space.getMaximalFreeRects( rect );
4127    // add either the original space or the new spaces to the revised spaces
4128    if ( newSpaces ) {
4129      revisedSpaces.push.apply( revisedSpaces, newSpaces );
4130    } else {
4131      revisedSpaces.push( space );
4132    }
4133  }
4134
4135  this.spaces = revisedSpaces;
4136
4137  this.mergeSortSpaces();
4138};
4139
4140proto.mergeSortSpaces = function() {
4141  // remove redundant spaces
4142  Packer.mergeRects( this.spaces );
4143  this.spaces.sort( this.sorter );
4144};
4145
4146// add a space back
4147proto.addSpace = function( rect ) {
4148  this.spaces.push( rect );
4149  this.mergeSortSpaces();
4150};
4151
4152// -------------------------- utility functions -------------------------- //
4153
4154/**
4155 * Remove redundant rectangle from array of rectangles
4156 * @param {Array} rects: an array of Rects
4157 * @returns {Array} rects: an array of Rects
4158**/
4159Packer.mergeRects = function( rects ) {
4160  var i = 0;
4161  var rect = rects[i];
4162
4163  rectLoop:
4164  while ( rect ) {
4165    var j = 0;
4166    var compareRect = rects[ i + j ];
4167
4168    while ( compareRect ) {
4169      if  ( compareRect == rect ) {
4170        j++; // next
4171      } else if ( compareRect.contains( rect ) ) {
4172        // remove rect
4173        rects.splice( i, 1 );
4174        rect = rects[i]; // set next rect
4175        continue rectLoop; // bail on compareLoop
4176      } else if ( rect.contains( compareRect ) ) {
4177        // remove compareRect
4178        rects.splice( i + j, 1 );
4179      } else {
4180        j++;
4181      }
4182      compareRect = rects[ i + j ]; // set next compareRect
4183    }
4184    i++;
4185    rect = rects[i];
4186  }
4187
4188  return rects;
4189};
4190
4191
4192// -------------------------- sorters -------------------------- //
4193
4194// functions for sorting rects in order
4195var sorters = {
4196  // top down, then left to right
4197  downwardLeftToRight: function( a, b ) {
4198    return a.y - b.y || a.x - b.x;
4199  },
4200  // left to right, then top down
4201  rightwardTopToBottom: function( a, b ) {
4202    return a.x - b.x || a.y - b.y;
4203  }
4204};
4205
4206
4207// --------------------------  -------------------------- //
4208
4209return Packer;
4210
4211}));
4212
4213/**
4214 * Packery Item Element
4215**/
4216
4217( function( window, factory ) {
4218  // universal module definition
4219  /* jshint strict: false */ /* globals define, module, require */
4220  if ( typeof define == 'function' && define.amd ) {
4221    // AMD
4222    define( 'packery/js/item',[
4223        'outlayer/outlayer',
4224        './rect'
4225      ],
4226      factory );
4227  } else if ( typeof module == 'object' && module.exports ) {
4228    // CommonJS
4229    module.exports = factory(
4230      require('outlayer'),
4231      require('./rect')
4232    );
4233  } else {
4234    // browser global
4235    window.Packery.Item = factory(
4236      window.Outlayer,
4237      window.Packery.Rect
4238    );
4239  }
4240
4241}( window, function factory( Outlayer, Rect ) {
4242
4243
4244// -------------------------- Item -------------------------- //
4245
4246var docElemStyle = document.documentElement.style;
4247
4248var transformProperty = typeof docElemStyle.transform == 'string' ?
4249  'transform' : 'WebkitTransform';
4250
4251// sub-class Item
4252var Item = function PackeryItem() {
4253  Outlayer.Item.apply( this, arguments );
4254};
4255
4256var proto = Item.prototype = Object.create( Outlayer.Item.prototype );
4257
4258var __create = proto._create;
4259proto._create = function() {
4260  // call default _create logic
4261  __create.call( this );
4262  this.rect = new Rect();
4263};
4264
4265var _moveTo = proto.moveTo;
4266proto.moveTo = function( x, y ) {
4267  // don't shift 1px while dragging
4268  var dx = Math.abs( this.position.x - x );
4269  var dy = Math.abs( this.position.y - y );
4270
4271  var canHackGoTo = this.layout.dragItemCount && !this.isPlacing &&
4272    !this.isTransitioning && dx < 1 && dy < 1;
4273  if ( canHackGoTo ) {
4274    this.goTo( x, y );
4275    return;
4276  }
4277  _moveTo.apply( this, arguments );
4278};
4279
4280// -------------------------- placing -------------------------- //
4281
4282proto.enablePlacing = function() {
4283  this.removeTransitionStyles();
4284  // remove transform property from transition
4285  if ( this.isTransitioning && transformProperty ) {
4286    this.element.style[ transformProperty ] = 'none';
4287  }
4288  this.isTransitioning = false;
4289  this.getSize();
4290  this.layout._setRectSize( this.element, this.rect );
4291  this.isPlacing = true;
4292};
4293
4294proto.disablePlacing = function() {
4295  this.isPlacing = false;
4296};
4297
4298// -----  ----- //
4299
4300// remove element from DOM
4301proto.removeElem = function() {
4302  this.element.parentNode.removeChild( this.element );
4303  // add space back to packer
4304  this.layout.packer.addSpace( this.rect );
4305  this.emitEvent( 'remove', [ this ] );
4306};
4307
4308// ----- dropPlaceholder ----- //
4309
4310proto.showDropPlaceholder = function() {
4311  var dropPlaceholder = this.dropPlaceholder;
4312  if ( !dropPlaceholder ) {
4313    // create dropPlaceholder
4314    dropPlaceholder = this.dropPlaceholder = document.createElement('div');
4315    dropPlaceholder.className = 'packery-drop-placeholder';
4316    dropPlaceholder.style.position = 'absolute';
4317  }
4318
4319  dropPlaceholder.style.width = this.size.width + 'px';
4320  dropPlaceholder.style.height = this.size.height + 'px';
4321  this.positionDropPlaceholder();
4322  this.layout.element.appendChild( dropPlaceholder );
4323};
4324
4325proto.positionDropPlaceholder = function() {
4326  this.dropPlaceholder.style[ transformProperty ] = 'translate(' +
4327    this.rect.x + 'px, ' + this.rect.y + 'px)';
4328};
4329
4330proto.hideDropPlaceholder = function() {
4331  this.layout.element.removeChild( this.dropPlaceholder );
4332};
4333
4334// -----  ----- //
4335
4336return Item;
4337
4338}));
4339
4340/*!
4341 * Packery v2.0.0
4342 * Gapless, draggable grid layouts
4343 *
4344 * Licensed GPLv3 for open source use
4345 * or Packery Commercial License for commercial use
4346 *
4347 * http://packery.metafizzy.co
4348 * Copyright 2016 Metafizzy
4349 */
4350
4351( function( window, factory ) {
4352  // universal module definition
4353  /* jshint strict: false */ /* globals define, module, require */
4354  if ( typeof define == 'function' && define.amd ) {
4355    // AMD
4356    define( 'packery/js/packery',[
4357        'get-size/get-size',
4358        'outlayer/outlayer',
4359        './rect',
4360        './packer',
4361        './item'
4362      ],
4363      factory );
4364  } else if ( typeof module == 'object' && module.exports ) {
4365    // CommonJS
4366    module.exports = factory(
4367      require('get-size'),
4368      require('outlayer'),
4369      require('./rect'),
4370      require('./packer'),
4371      require('./item')
4372    );
4373  } else {
4374    // browser global
4375    window.Packery = factory(
4376      window.getSize,
4377      window.Outlayer,
4378      window.Packery.Rect,
4379      window.Packery.Packer,
4380      window.Packery.Item
4381    );
4382  }
4383
4384}( window, function factory( getSize, Outlayer, Rect, Packer, Item ) {
4385
4386
4387// ----- Rect ----- //
4388
4389// allow for pixel rounding errors IE8-IE11 & Firefox; #227
4390Rect.prototype.canFit = function( rect ) {
4391  return this.width >= rect.width - 1 && this.height >= rect.height - 1;
4392};
4393
4394// -------------------------- Packery -------------------------- //
4395
4396// create an Outlayer layout class
4397var Packery = Outlayer.create('packery');
4398Packery.Item = Item;
4399
4400var proto = Packery.prototype;
4401
4402proto._create = function() {
4403  // call super
4404  Outlayer.prototype._create.call( this );
4405
4406  // initial properties
4407  this.packer = new Packer();
4408  // packer for drop targets
4409  this.shiftPacker = new Packer();
4410  this.isEnabled = true;
4411
4412  this.dragItemCount = 0;
4413
4414  // create drag handlers
4415  var _this = this;
4416  this.handleDraggabilly = {
4417    dragStart: function() {
4418      _this.itemDragStart( this.element );
4419    },
4420    dragMove: function() {
4421      _this.itemDragMove( this.element, this.position.x, this.position.y );
4422    },
4423    dragEnd: function() {
4424      _this.itemDragEnd( this.element );
4425    }
4426  };
4427
4428  this.handleUIDraggable = {
4429    start: function handleUIDraggableStart( event, ui ) {
4430      // HTML5 may trigger dragstart, dismiss HTML5 dragging
4431      if ( !ui ) {
4432        return;
4433      }
4434      _this.itemDragStart( event.currentTarget );
4435    },
4436    drag: function handleUIDraggableDrag( event, ui ) {
4437      if ( !ui ) {
4438        return;
4439      }
4440      _this.itemDragMove( event.currentTarget, ui.position.left, ui.position.top );
4441    },
4442    stop: function handleUIDraggableStop( event, ui ) {
4443      if ( !ui ) {
4444        return;
4445      }
4446      _this.itemDragEnd( event.currentTarget );
4447    }
4448  };
4449
4450};
4451
4452
4453// ----- init & layout ----- //
4454
4455/**
4456 * logic before any new layout
4457 */
4458proto._resetLayout = function() {
4459  this.getSize();
4460
4461  this._getMeasurements();
4462
4463  // reset packer
4464  var width, height, sortDirection;
4465  // packer settings, if horizontal or vertical
4466  if ( this._getOption('horizontal') ) {
4467    width = Infinity;
4468    height = this.size.innerHeight + this.gutter;
4469    sortDirection = 'rightwardTopToBottom';
4470  } else {
4471    width = this.size.innerWidth + this.gutter;
4472    height = Infinity;
4473    sortDirection = 'downwardLeftToRight';
4474  }
4475
4476  this.packer.width = this.shiftPacker.width = width;
4477  this.packer.height = this.shiftPacker.height = height;
4478  this.packer.sortDirection = this.shiftPacker.sortDirection = sortDirection;
4479
4480  this.packer.reset();
4481
4482  // layout
4483  this.maxY = 0;
4484  this.maxX = 0;
4485};
4486
4487/**
4488 * update columnWidth, rowHeight, & gutter
4489 * @private
4490 */
4491proto._getMeasurements = function() {
4492  this._getMeasurement( 'columnWidth', 'width' );
4493  this._getMeasurement( 'rowHeight', 'height' );
4494  this._getMeasurement( 'gutter', 'width' );
4495};
4496
4497proto._getItemLayoutPosition = function( item ) {
4498  this._setRectSize( item.element, item.rect );
4499  if ( this.isShifting || this.dragItemCount > 0 ) {
4500    var packMethod = this._getPackMethod();
4501    this.packer[ packMethod ]( item.rect );
4502  } else {
4503    this.packer.pack( item.rect );
4504  }
4505
4506  this._setMaxXY( item.rect );
4507  return item.rect;
4508};
4509
4510proto.shiftLayout = function() {
4511  this.isShifting = true;
4512  this.layout();
4513  delete this.isShifting;
4514};
4515
4516proto._getPackMethod = function() {
4517  return this._getOption('horizontal') ? 'rowPack' : 'columnPack';
4518};
4519
4520
4521/**
4522 * set max X and Y value, for size of container
4523 * @param {Packery.Rect} rect
4524 * @private
4525 */
4526proto._setMaxXY = function( rect ) {
4527  this.maxX = Math.max( rect.x + rect.width, this.maxX );
4528  this.maxY = Math.max( rect.y + rect.height, this.maxY );
4529};
4530
4531/**
4532 * set the width and height of a rect, applying columnWidth and rowHeight
4533 * @param {Element} elem
4534 * @param {Packery.Rect} rect
4535 */
4536proto._setRectSize = function( elem, rect ) {
4537  var size = getSize( elem );
4538  var w = size.outerWidth;
4539  var h = size.outerHeight;
4540  // size for columnWidth and rowHeight, if available
4541  // only check if size is non-zero, #177
4542  if ( w || h ) {
4543    w = this._applyGridGutter( w, this.columnWidth );
4544    h = this._applyGridGutter( h, this.rowHeight );
4545  }
4546  // rect must fit in packer
4547  rect.width = Math.min( w, this.packer.width );
4548  rect.height = Math.min( h, this.packer.height );
4549};
4550
4551/**
4552 * fits item to columnWidth/rowHeight and adds gutter
4553 * @param {Number} measurement - item width or height
4554 * @param {Number} gridSize - columnWidth or rowHeight
4555 * @returns measurement
4556 */
4557proto._applyGridGutter = function( measurement, gridSize ) {
4558  // just add gutter if no gridSize
4559  if ( !gridSize ) {
4560    return measurement + this.gutter;
4561  }
4562  gridSize += this.gutter;
4563  // fit item to columnWidth/rowHeight
4564  var remainder = measurement % gridSize;
4565  var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
4566  measurement = Math[ mathMethod ]( measurement / gridSize ) * gridSize;
4567  return measurement;
4568};
4569
4570proto._getContainerSize = function() {
4571  if ( this._getOption('horizontal') ) {
4572    return {
4573      width: this.maxX - this.gutter
4574    };
4575  } else {
4576    return {
4577      height: this.maxY - this.gutter
4578    };
4579  }
4580};
4581
4582
4583// -------------------------- stamp -------------------------- //
4584
4585/**
4586 * makes space for element
4587 * @param {Element} elem
4588 */
4589proto._manageStamp = function( elem ) {
4590
4591  var item = this.getItem( elem );
4592  var rect;
4593  if ( item && item.isPlacing ) {
4594    rect = item.rect;
4595  } else {
4596    var offset = this._getElementOffset( elem );
4597    rect = new Rect({
4598      x: this._getOption('originLeft') ? offset.left : offset.right,
4599      y: this._getOption('originTop') ? offset.top : offset.bottom
4600    });
4601  }
4602
4603  this._setRectSize( elem, rect );
4604  // save its space in the packer
4605  this.packer.placed( rect );
4606  this._setMaxXY( rect );
4607};
4608
4609// -------------------------- methods -------------------------- //
4610
4611function verticalSorter( a, b ) {
4612  return a.position.y - b.position.y || a.position.x - b.position.x;
4613}
4614
4615function horizontalSorter( a, b ) {
4616  return a.position.x - b.position.x || a.position.y - b.position.y;
4617}
4618
4619proto.sortItemsByPosition = function() {
4620  var sorter = this._getOption('horizontal') ? horizontalSorter : verticalSorter;
4621  this.items.sort( sorter );
4622};
4623
4624/**
4625 * Fit item element in its current position
4626 * Packery will position elements around it
4627 * useful for expanding elements
4628 *
4629 * @param {Element} elem
4630 * @param {Number} x - horizontal destination position, optional
4631 * @param {Number} y - vertical destination position, optional
4632 */
4633proto.fit = function( elem, x, y ) {
4634  var item = this.getItem( elem );
4635  if ( !item ) {
4636    return;
4637  }
4638
4639  // stamp item to get it out of layout
4640  this.stamp( item.element );
4641  // set placing flag
4642  item.enablePlacing();
4643  this.updateShiftTargets( item );
4644  // fall back to current position for fitting
4645  x = x === undefined ? item.rect.x: x;
4646  y = y === undefined ? item.rect.y: y;
4647  // position it best at its destination
4648  this.shift( item, x, y );
4649  this._bindFitEvents( item );
4650  item.moveTo( item.rect.x, item.rect.y );
4651  // layout everything else
4652  this.shiftLayout();
4653  // return back to regularly scheduled programming
4654  this.unstamp( item.element );
4655  this.sortItemsByPosition();
4656  item.disablePlacing();
4657};
4658
4659/**
4660 * emit event when item is fit and other items are laid out
4661 * @param {Packery.Item} item
4662 * @private
4663 */
4664proto._bindFitEvents = function( item ) {
4665  var _this = this;
4666  var ticks = 0;
4667  function onLayout() {
4668    ticks++;
4669    if ( ticks != 2 ) {
4670      return;
4671    }
4672    _this.dispatchEvent( 'fitComplete', null, [ item ] );
4673  }
4674  // when item is laid out
4675  item.once( 'layout', onLayout );
4676  // when all items are laid out
4677  this.once( 'layoutComplete', onLayout );
4678};
4679
4680// -------------------------- resize -------------------------- //
4681
4682// debounced, layout on resize
4683proto.resize = function() {
4684  // don't trigger if size did not change
4685  // or if resize was unbound. See #285, outlayer#9
4686  if ( !this.isResizeBound || !this.needsResizeLayout() ) {
4687    return;
4688  }
4689
4690  if ( this.options.shiftPercentResize ) {
4691    this.resizeShiftPercentLayout();
4692  } else {
4693    this.layout();
4694  }
4695};
4696
4697/**
4698 * check if layout is needed post layout
4699 * @returns Boolean
4700 */
4701proto.needsResizeLayout = function() {
4702  var size = getSize( this.element );
4703  var innerSize = this._getOption('horizontal') ? 'innerHeight' : 'innerWidth';
4704  return size[ innerSize ] != this.size[ innerSize ];
4705};
4706
4707proto.resizeShiftPercentLayout = function() {
4708  var items = this._getItemsForLayout( this.items );
4709
4710  var isHorizontal = this._getOption('horizontal');
4711  var coord = isHorizontal ? 'y' : 'x';
4712  var measure = isHorizontal ? 'height' : 'width';
4713  var segmentName = isHorizontal ? 'rowHeight' : 'columnWidth';
4714  var innerSize = isHorizontal ? 'innerHeight' : 'innerWidth';
4715
4716  // proportional re-align items
4717  var previousSegment = this[ segmentName ];
4718  previousSegment = previousSegment && previousSegment + this.gutter;
4719
4720  if ( previousSegment ) {
4721    this._getMeasurements();
4722    var currentSegment = this[ segmentName ] + this.gutter;
4723    items.forEach( function( item ) {
4724      var seg = Math.round( item.rect[ coord ] / previousSegment );
4725      item.rect[ coord ] = seg * currentSegment;
4726    });
4727  } else {
4728    var currentSize = getSize( this.element )[ innerSize ] + this.gutter;
4729    var previousSize = this.packer[ measure ];
4730    items.forEach( function( item ) {
4731      item.rect[ coord ] = ( item.rect[ coord ] / previousSize ) * currentSize;
4732    });
4733  }
4734
4735  this.shiftLayout();
4736};
4737
4738// -------------------------- drag -------------------------- //
4739
4740/**
4741 * handle an item drag start event
4742 * @param {Element} elem
4743 */
4744proto.itemDragStart = function( elem ) {
4745  if ( !this.isEnabled ) {
4746    return;
4747  }
4748  this.stamp( elem );
4749  // this.ignore( elem );
4750  var item = this.getItem( elem );
4751  if ( !item ) {
4752    return;
4753  }
4754
4755  item.enablePlacing();
4756  item.showDropPlaceholder();
4757  this.dragItemCount++;
4758  this.updateShiftTargets( item );
4759};
4760
4761proto.updateShiftTargets = function( dropItem ) {
4762  this.shiftPacker.reset();
4763
4764  // pack stamps
4765  this._getBoundingRect();
4766  var isOriginLeft = this._getOption('originLeft');
4767  var isOriginTop = this._getOption('originTop');
4768  this.stamps.forEach( function( stamp ) {
4769    // ignore dragged item
4770    var item = this.getItem( stamp );
4771    if ( item && item.isPlacing ) {
4772      return;
4773    }
4774    var offset = this._getElementOffset( stamp );
4775    var rect = new Rect({
4776      x: isOriginLeft ? offset.left : offset.right,
4777      y: isOriginTop ? offset.top : offset.bottom
4778    });
4779    this._setRectSize( stamp, rect );
4780    // save its space in the packer
4781    this.shiftPacker.placed( rect );
4782  }, this );
4783
4784  // reset shiftTargets
4785  var isHorizontal = this._getOption('horizontal');
4786  var segmentName = isHorizontal ? 'rowHeight' : 'columnWidth';
4787  var measure = isHorizontal ? 'height' : 'width';
4788
4789  this.shiftTargetKeys = [];
4790  this.shiftTargets = [];
4791  var boundsSize;
4792  var segment = this[ segmentName ];
4793  segment = segment && segment + this.gutter;
4794
4795  if ( segment ) {
4796    var segmentSpan = Math.ceil( dropItem.rect[ measure ] / segment );
4797    var segs = Math.floor( ( this.shiftPacker[ measure ] + this.gutter ) / segment );
4798    boundsSize = ( segs - segmentSpan ) * segment;
4799    // add targets on top
4800    for ( var i=0; i < segs; i++ ) {
4801      this._addShiftTarget( i * segment, 0, boundsSize );
4802    }
4803  } else {
4804    boundsSize = ( this.shiftPacker[ measure ] + this.gutter ) - dropItem.rect[ measure ];
4805    this._addShiftTarget( 0, 0, boundsSize );
4806  }
4807
4808  // pack each item to measure where shiftTargets are
4809  var items = this._getItemsForLayout( this.items );
4810  var packMethod = this._getPackMethod();
4811  items.forEach( function( item ) {
4812    var rect = item.rect;
4813    this._setRectSize( item.element, rect );
4814    this.shiftPacker[ packMethod ]( rect );
4815
4816    // add top left corner
4817    this._addShiftTarget( rect.x, rect.y, boundsSize );
4818    // add bottom left / top right corner
4819    var cornerX = isHorizontal ? rect.x + rect.width : rect.x;
4820    var cornerY = isHorizontal ? rect.y : rect.y + rect.height;
4821    this._addShiftTarget( cornerX, cornerY, boundsSize );
4822
4823    if ( segment ) {
4824      // add targets for each column on bottom / row on right
4825      var segSpan = Math.round( rect[ measure ] / segment );
4826      for ( var i=1; i < segSpan; i++ ) {
4827        var segX = isHorizontal ? cornerX : rect.x + segment * i;
4828        var segY = isHorizontal ? rect.y + segment * i : cornerY;
4829        this._addShiftTarget( segX, segY, boundsSize );
4830      }
4831    }
4832  }, this );
4833
4834};
4835
4836proto._addShiftTarget = function( x, y, boundsSize ) {
4837  var checkCoord = this._getOption('horizontal') ? y : x;
4838  if ( checkCoord !== 0 && checkCoord > boundsSize ) {
4839    return;
4840  }
4841  // create string for a key, easier to keep track of what targets
4842  var key = x + ',' + y;
4843  var hasKey = this.shiftTargetKeys.indexOf( key ) != -1;
4844  if ( hasKey ) {
4845    return;
4846  }
4847  this.shiftTargetKeys.push( key );
4848  this.shiftTargets.push({ x: x, y: y });
4849};
4850
4851// -------------------------- drop -------------------------- //
4852
4853proto.shift = function( item, x, y ) {
4854  var shiftPosition;
4855  var minDistance = Infinity;
4856  var position = { x: x, y: y };
4857  this.shiftTargets.forEach( function( target ) {
4858    var distance = getDistance( target, position );
4859    if ( distance < minDistance ) {
4860      shiftPosition = target;
4861      minDistance = distance;
4862    }
4863  });
4864  item.rect.x = shiftPosition.x;
4865  item.rect.y = shiftPosition.y;
4866};
4867
4868function getDistance( a, b ) {
4869  var dx = b.x - a.x;
4870  var dy = b.y - a.y;
4871  return Math.sqrt( dx * dx + dy * dy );
4872}
4873
4874// -------------------------- drag move -------------------------- //
4875
4876var DRAG_THROTTLE_TIME = 120;
4877
4878/**
4879 * handle an item drag move event
4880 * @param {Element} elem
4881 * @param {Number} x - horizontal change in position
4882 * @param {Number} y - vertical change in position
4883 */
4884proto.itemDragMove = function( elem, x, y ) {
4885  var item = this.isEnabled && this.getItem( elem );
4886  if ( !item ) {
4887    return;
4888  }
4889
4890  x -= this.size.paddingLeft;
4891  y -= this.size.paddingTop;
4892
4893  var _this = this;
4894  function onDrag() {
4895    _this.shift( item, x, y );
4896    item.positionDropPlaceholder();
4897    _this.layout();
4898  }
4899
4900  // throttle
4901  var now = new Date();
4902  if ( this._itemDragTime && now - this._itemDragTime < DRAG_THROTTLE_TIME ) {
4903    clearTimeout( this.dragTimeout );
4904    this.dragTimeout = setTimeout( onDrag, DRAG_THROTTLE_TIME );
4905  } else {
4906    onDrag();
4907    this._itemDragTime = now;
4908  }
4909};
4910
4911// -------------------------- drag end -------------------------- //
4912
4913/**
4914 * handle an item drag end event
4915 * @param {Element} elem
4916 */
4917proto.itemDragEnd = function( elem ) {
4918  var item = this.isEnabled && this.getItem( elem );
4919  if ( !item ) {
4920    return;
4921  }
4922
4923  clearTimeout( this.dragTimeout );
4924  item.element.classList.add('is-positioning-post-drag');
4925
4926  var completeCount = 0;
4927  var _this = this;
4928  function onDragEndLayoutComplete() {
4929    completeCount++;
4930    if ( completeCount != 2 ) {
4931      return;
4932    }
4933    // reset drag item
4934    item.element.classList.remove('is-positioning-post-drag');
4935    item.hideDropPlaceholder();
4936    _this.dispatchEvent( 'dragItemPositioned', null, [ item ] );
4937  }
4938
4939  item.once( 'layout', onDragEndLayoutComplete );
4940  this.once( 'layoutComplete', onDragEndLayoutComplete );
4941  item.moveTo( item.rect.x, item.rect.y );
4942  this.layout();
4943  this.dragItemCount = Math.max( 0, this.dragItemCount - 1 );
4944  this.sortItemsByPosition();
4945  item.disablePlacing();
4946  this.unstamp( item.element );
4947};
4948
4949/**
4950 * binds Draggabilly events
4951 * @param {Draggabilly} draggie
4952 */
4953proto.bindDraggabillyEvents = function( draggie ) {
4954  this._bindDraggabillyEvents( draggie, 'on' );
4955};
4956
4957proto.unbindDraggabillyEvents = function( draggie ) {
4958  this._bindDraggabillyEvents( draggie, 'off' );
4959};
4960
4961proto._bindDraggabillyEvents = function( draggie, method ) {
4962  var handlers = this.handleDraggabilly;
4963  draggie[ method ]( 'dragStart', handlers.dragStart );
4964  draggie[ method ]( 'dragMove', handlers.dragMove );
4965  draggie[ method ]( 'dragEnd', handlers.dragEnd );
4966};
4967
4968/**
4969 * binds jQuery UI Draggable events
4970 * @param {jQuery} $elems
4971 */
4972proto.bindUIDraggableEvents = function( $elems ) {
4973  this._bindUIDraggableEvents( $elems, 'on' );
4974};
4975
4976proto.unbindUIDraggableEvents = function( $elems ) {
4977  this._bindUIDraggableEvents( $elems, 'off' );
4978};
4979
4980proto._bindUIDraggableEvents = function( $elems, method ) {
4981  var handlers = this.handleUIDraggable;
4982  $elems
4983    [ method ]( 'dragstart', handlers.start )
4984    [ method ]( 'drag', handlers.drag )
4985    [ method ]( 'dragstop', handlers.stop );
4986};
4987
4988// ----- destroy ----- //
4989
4990var _destroy = proto.destroy;
4991proto.destroy = function() {
4992  _destroy.apply( this, arguments );
4993  // disable flag; prevent drag events from triggering. #72
4994  this.isEnabled = false;
4995};
4996
4997// -----  ----- //
4998
4999Packery.Rect = Rect;
5000Packery.Packer = Packer;
5001
5002return Packery;
5003
5004}));
5005
5006/*!
5007 * Packery layout mode v2.0.1
5008 * sub-classes Packery
5009 */
5010
5011/*jshint browser: true, strict: true, undef: true, unused: true */
5012
5013( function( window, factory ) {
5014
5015  // universal module definition
5016  if ( typeof define == 'function' && define.amd ) {
5017    // AMD
5018    define( [
5019        'isotope-layout/js/layout-mode',
5020        'packery/js/packery'
5021      ],
5022      factory );
5023  } else if ( typeof module == 'object' && module.exports ) {
5024    // CommonJS
5025    module.exports = factory(
5026      require('isotope-layout/js/layout-mode'),
5027      require('packery')
5028    );
5029  } else {
5030    // browser global
5031    factory(
5032      window.Isotope.LayoutMode,
5033      window.Packery
5034    );
5035  }
5036
5037}( window, function factor( LayoutMode, Packery ) {
5038
5039
5040  // create an Outlayer layout class
5041  var PackeryMode = LayoutMode.create('packery');
5042  var proto = PackeryMode.prototype;
5043
5044  var keepModeMethods = {
5045    _getElementOffset: true,
5046    _getMeasurement: true
5047  };
5048
5049  // inherit Packery prototype
5050  for ( var method in Packery.prototype ) {
5051    // do not inherit mode methods
5052    if ( !keepModeMethods[ method ] ) {
5053      proto[ method ] = Packery.prototype[ method ];
5054    }
5055  }
5056
5057  // set packer in _resetLayout
5058  var _resetLayout = proto._resetLayout;
5059  proto._resetLayout = function() {
5060    this.packer = this.packer || new Packery.Packer();
5061    this.shiftPacker = this.shiftPacker || new Packery.Packer();
5062    _resetLayout.apply( this, arguments );
5063  };
5064
5065  var _getItemLayoutPosition = proto._getItemLayoutPosition;
5066  proto._getItemLayoutPosition = function( item ) {
5067    // set packery rect
5068    item.rect = item.rect || new Packery.Rect();
5069    return _getItemLayoutPosition.call( this, item );
5070  };
5071
5072  // needsResizeLayout for vertical or horizontal
5073  var _needsResizeLayout = proto.needsResizeLayout;
5074  proto.needsResizeLayout = function() {
5075    if ( this._getOption('horizontal') ) {
5076      return this.needsVerticalResizeLayout();
5077    } else {
5078      return _needsResizeLayout.call( this );
5079    }
5080  };
5081
5082  // point to mode options for horizontal
5083  var _getOption = proto._getOption;
5084  proto._getOption = function( option ) {
5085    if ( option == 'horizontal' ) {
5086      return this.options.isHorizontal !== undefined ?
5087        this.options.isHorizontal : this.options.horizontal;
5088    }
5089    return _getOption.apply( this.isotope, arguments );
5090  };
5091
5092  return PackeryMode;
5093
5094}));
5095
5096/*!
5097 * cellsByRows layout mode for Isotope
5098 * v1.1.3
5099 * http://isotope.metafizzy.co/layout-modes/cellsbyrow.html
5100 */
5101
5102/*jshint browser: true, devel: false, strict: true, undef: true, unused: true */
5103
5104( function( window, factory ) {
5105  // universal module definition
5106  /* jshint strict: false */ /*globals define, module, require */
5107  if ( typeof define === 'function' && define.amd ) {
5108    // AMD
5109    define( [
5110        'isotope/js/layout-mode'
5111      ],
5112      factory );
5113  } else if ( typeof exports === 'object' ) {
5114    // CommonJS
5115    module.exports = factory(
5116      require('isotope-layout/js/layout-mode')
5117    );
5118  } else {
5119    // browser global
5120    factory(
5121      window.Isotope.LayoutMode
5122    );
5123  }
5124
5125}( window, function factory( LayoutMode ) {
5126  'use strict';
5127
5128  var CellsByRow = LayoutMode.create( 'cellsByRow' );
5129  var proto = CellsByRow.prototype;
5130
5131  proto._resetLayout = function() {
5132    // reset properties
5133    this.itemIndex = 0;
5134    // measurements
5135    this.getColumnWidth();
5136    this.getRowHeight();
5137    // set cols
5138    this.cols = Math.floor( this.isotope.size.innerWidth / this.columnWidth );
5139    this.cols = Math.max( this.cols, 1 );
5140  };
5141
5142  proto._getItemLayoutPosition = function( item ) {
5143    item.getSize();
5144    var col = this.itemIndex % this.cols;
5145    var row = Math.floor( this.itemIndex / this.cols );
5146    // center item within cell
5147    var x = ( col + 0.5 ) * this.columnWidth - item.size.outerWidth / 2;
5148    var y = ( row + 0.5 ) * this.rowHeight - item.size.outerHeight / 2;
5149    this.itemIndex++;
5150    return { x: x, y: y };
5151  };
5152
5153  proto._getContainerSize = function() {
5154    return {
5155      height: Math.ceil( this.itemIndex / this.cols ) * this.rowHeight
5156    };
5157  };
5158
5159  return CellsByRow;
5160
5161}));
5162/*global jQuery: true */
5163
5164/*!
5165   --------------------------------
5166   Infinite Scroll
5167   --------------------------------
5168   + https://github.com/paulirish/infinite-scroll
5169   + version 2.1.0
5170   + Copyright 2011/12 Paul Irish & Luke Shumard
5171   + Licensed under the MIT license
5172
5173   + Documentation: http://infinite-scroll.com/
5174*/
5175
5176// Uses AMD or browser globals to create a jQuery plugin.
5177(function (factory) {
5178    if (typeof define === 'function' && define.amd) {
5179        // AMD. Register as an anonymous module.
5180        define(['jquery'], factory);
5181    } else {
5182        // Browser globals
5183        factory(jQuery);
5184    }
5185}(function ($, undefined) {
5186    'use strict';
5187
5188    $.infinitescroll = function infscr(options, callback, element) {
5189        this.element = $(element);
5190
5191        // Flag the object in the event of a failed creation
5192        if (!this._create(options, callback)) {
5193            this.failed = true;
5194        }
5195    };
5196
5197    $.infinitescroll.defaults = {
5198        loading: {
5199            finished: undefined,
5200            finishedMsg: "<em>Congratulations, you've reached the end of the internet.</em>",
5201            img: 'data:image/gif;base64,R0lGODlh3AATAPQeAPDy+MnQ6LW/4N3h8MzT6rjC4sTM5r/I5NHX7N7j8c7U6tvg8OLl8uXo9Ojr9b3G5MfP6Ovu9tPZ7PT1+vX2+tbb7vf4+8/W69jd7rC73vn5/O/x+K243ai02////wAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQECgD/ACwAAAAA3AATAAAF/6AnjmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEj0BAScpHLJbDqf0Kh0Sq1ar9isdioItAKGw+MAKYMFhbF63CW438f0mg1R2O8EuXj/aOPtaHx7fn96goR4hmuId4qDdX95c4+RBIGCB4yAjpmQhZN0YGYGXitdZBIVGAsLoq4BBKQDswm1CQRkcG6ytrYKubq8vbfAcMK9v7q7EMO1ycrHvsW6zcTKsczNz8HZw9vG3cjTsMIYqQkCLBwHCgsMDQ4RDAYIqfYSFxDxEfz88/X38Onr16+Bp4ADCco7eC8hQYMAEe57yNCew4IVBU7EGNDiRn8Z831cGLHhSIgdFf9chIeBg7oA7gjaWUWTVQAGE3LqBDCTlc9WOHfm7PkTqNCh54rePDqB6M+lR536hCpUqs2gVZM+xbrTqtGoWqdy1emValeXKzggYBBB5y1acFNZmEvXAoN2cGfJrTv3bl69Ffj2xZt3L1+/fw3XRVw4sGDGcR0fJhxZsF3KtBTThZxZ8mLMgC3fRatCbYMNFCzwLEqLgE4NsDWs/tvqdezZf13Hvk2A9Szdu2X3pg18N+68xXn7rh1c+PLksI/Dhe6cuO3ow3NfV92bdArTqC2Ebd3A8vjf5QWfH6Bg7Nz17c2fj69+fnq+8N2Lty+fuP78/eV2X13neIcCeBRwxorbZrA1ANoCDGrgoG8RTshahQ9iSKEEzUmYIYfNWViUhheCGJyIP5E4oom7WWjgCeBFAJNv1DVV01MAdJhhjdkplWNzO/5oXI846njjVEIqR2OS2B1pE5PVscajkxhMycqLJghQSwT40PgfAl4GqNSXYdZXJn5gSkmmmmJu1aZYb14V51do+pTOCmA40AqVCIhG5IJ9PvYnhIFOxmdqhpaI6GeHCtpooisuutmg+Eg62KOMKuqoTaXgicQWoIYq6qiklmoqFV0UoeqqrLbq6quwxirrrLTWauutJ4QAACH5BAUKABwALAcABADOAAsAAAX/IPd0D2dyRCoUp/k8gpHOKtseR9yiSmGbuBykler9XLAhkbDavXTL5k2oqFqNOxzUZPU5YYZd1XsD72rZpBjbeh52mSNnMSC8lwblKZGwi+0QfIJ8CncnCoCDgoVnBHmKfByGJimPkIwtiAeBkH6ZHJaKmCeVnKKTHIihg5KNq4uoqmEtcRUtEREMBggtEr4QDrjCuRC8h7/BwxENeicSF8DKy82pyNLMOxzWygzFmdvD2L3P0dze4+Xh1Arkyepi7dfFvvTtLQkZBC0T/FX3CRgCMOBHsJ+EHYQY7OinAGECgQsB+Lu3AOK+CewcWjwxQeJBihtNGHSoQOE+iQ3//4XkwBBhRZMcUS6YSXOAwIL8PGqEaSJCiYt9SNoCmnJPAgUVLChdaoFBURN8MAzl2PQphwQLfDFd6lTowglHve6rKpbjhK7/pG5VinZP1qkiz1rl4+tr2LRwWU64cFEihwEtZgbgR1UiHaMVvxpOSwBA37kzGz9e8G+B5MIEKLutOGEsAH2ATQwYfTmuX8aETWdGPZmiZcccNSzeTCA1Sw0bdiitC7LBWgu8jQr8HRzqgpK6gX88QbrB14z/kF+ELpwB8eVQj/JkqdylAudji/+ts3039vEEfK8Vz2dlvxZKG0CmbkKDBvllRd6fCzDvBLKBDSCeffhRJEFebFk1k/Mv9jVIoIJZSeBggwUaNeB+Qk34IE0cXlihcfRxkOAJFFhwGmKlmWDiakZhUJtnLBpnWWcnKaAZcxI0piFGGLBm1mc90kajSCveeBVWKeYEoU2wqeaQi0PetoE+rr14EpVC7oAbAUHqhYExbn2XHHsVqbcVew9tx8+XJKk5AZsqqdlddGpqAKdbAYBn1pcczmSTdWvdmZ17c1b3FZ99vnTdCRFM8OEcAhLwm1NdXnWcBBSMRWmfkWZqVlsmLIiAp/o1gGV2vpS4lalGYsUOqXrddcKCmK61aZ8SjEpUpVFVoCpTj4r661Km7kBHjrDyc1RAIQAAIfkEBQoAGwAsBwAEAM4ACwAABf/gtmUCd4goQQgFKj6PYKi0yrrbc8i4ohQt12EHcal+MNSQiCP8gigdz7iCioaCIvUmZLp8QBzW0EN2vSlCuDtFKaq4RyHzQLEKZNdiQDhRDVooCwkbfm59EAmKi4SGIm+AjIsKjhsqB4mSjT2IOIOUnICeCaB/mZKFNTSRmqVpmJqklSqskq6PfYYCDwYHDC4REQwGCBLGxxIQDsHMwhAIX8bKzcENgSLGF9PU1j3Sy9zX2NrgzQziChLk1BHWxcjf7N046tvN82715czn9Pryz6Ilc4ACj4EBOCZM8KEnAYYADBRKnACAYUMFv1wotIhCEcaJCisqwJFgAUSQGyX/kCSVUUTIdKMwJlyo0oXHlhskwrTJciZHEXsgaqS4s6PJiCAr1uzYU8kBBSgnWFqpoMJMUjGtDmUwkmfVmVypakWhEKvXsS4nhLW5wNjVroJIoc05wSzTr0PtiigpYe4EC2vj4iWrFu5euWIMRBhacaVJhYQBEFjA9jHjyQ0xEABwGceGAZYjY0YBOrRLCxUp29QM+bRkx5s7ZyYgVbTqwwti2ybJ+vLtDYpycyZbYOlptxdx0kV+V7lC5iJAyyRrwYKxAdiz82ng0/jnAdMJFz0cPi104Ec1Vj9/M6F173vKL/feXv156dw11tlqeMMnv4V5Ap53GmjQQH97nFfg+IFiucfgRX5Z8KAgbUlQ4IULIlghhhdOSB6AgX0IVn8eReghen3NRIBsRgnH4l4LuEidZBjwRpt6NM5WGwoW0KSjCwX6yJSMab2GwwAPDXfaBCtWpluRTQqC5JM5oUZAjUNS+VeOLWpJEQ7VYQANW0INJSZVDFSnZphjSikfmzE5N4EEbQI1QJmnWXCmHulRp2edwDXF43txukenJwvI9xyg9Q26Z3MzGUcBYFEChZh6DVTq34AU8Iflh51Sd+CnKFYQ6mmZkhqfBKfSxZWqA9DZanWjxmhrWwi0qtCrt/43K6WqVjjpmhIqgEGvculaGKklKstAACEAACH5BAUKABwALAcABADOAAsAAAX/ICdyQmaMYyAUqPgIBiHPxNpy79kqRXH8wAPsRmDdXpAWgWdEIYm2llCHqjVHU+jjJkwqBTecwItShMXkEfNWSh8e1NGAcLgpDGlRgk7EJ/6Ae3VKfoF/fDuFhohVeDeCfXkcCQqDVQcQhn+VNDOYmpSWaoqBlUSfmowjEA+iEAEGDRGztAwGCDcXEA60tXEiCrq8vREMEBLIyRLCxMWSH
5201MzExnbRvQ2Sy7vN0zvVtNfU2tLY3rPgLdnDvca4VQS/Cpk3ABwSLQkYAQwT/P309vcI7OvXr94jBQMJ/nskkGA/BQBRLNDncAIAiDcG6LsxAWOLiQzmeURBKWSLCQbv/1F0eDGinJUKR47YY1IEgQASKk7Yc7ACRwZm7mHweRJoz59BJUogisKCUaFMR0x4SlJBVBFTk8pZivTR0K73rN5wqlXEAq5Fy3IYgHbEzQ0nLy4QSoCjXLoom96VOJEeCosK5n4kkFfqXjl94wa+l1gvAcGICbewAOAxY8l/Ky/QhAGz4cUkGxu2HNozhwMGBnCUqUdBg9UuW9eUynqSwLHIBujePef1ZGQZXcM+OFuEBeBhi3OYgLyqcuaxbT9vLkf4SeqyWxSQpKGB2gQpm1KdWbu72rPRzR9Ne2Nu9Kzr/1Jqj0yD/fvqP4aXOt5sW/5qsXXVcv1Nsp8IBUAmgswGF3llGgeU1YVXXKTN1FlhWFXW3gIE+DVChApysACHHo7Q4A35lLichh+ROBmLKAzgYmYEYDAhCgxKGOOMn4WR4kkDaoBBOxJtdNKQxFmg5JIWIBnQc07GaORfUY4AEkdV6jHlCEISSZ5yTXpp1pbGZbkWmcuZmQCaE6iJ0FhjMaDjTMsgZaNEHFRAQVp3bqXnZED1qYcECOz5V6BhSWCoVJQIKuKQi2KFKEkEFAqoAo7uYSmO3jk61wUUMKmknJ4SGimBmAa0qVQBhAAAIfkEBQoAGwAsBwAEAM4ACwAABf/gJm5FmRlEqhJC+bywgK5pO4rHI0D3pii22+Mg6/0Ej96weCMAk7cDkXf7lZTTnrMl7eaYoy10JN0ZFdco0XAuvKI6qkgVFJXYNwjkIBcNBgR8TQoGfRsJCRuCYYQQiI+ICosiCoGOkIiKfSl8mJkHZ4U9kZMbKaI3pKGXmJKrngmug4WwkhA0lrCBWgYFCCMQFwoQDRHGxwwGCBLMzRLEx8iGzMMO0cYNeCMKzBDW19lnF9DXDIY/48Xg093f0Q3s1dcR8OLe8+Y91OTv5wrj7o7B+7VNQqABIoRVCMBggsOHE36kSoCBIcSH3EbFangxogJYFi8CkJhqQciLJEf/LDDJEeJIBT0GsOwYUYJGBS0fjpQAMidGmyVP6sx4Y6VQhzs9VUwkwqaCCh0tmKoFtSMDmBOf9phg4SrVrROuasRQAaxXpVUhdsU6IsECZlvX3kwLUWzRt0BHOLTbNlbZG3vZinArge5Dvn7wbqtQkSYAAgtKmnSsYKVKo2AfW048uaPmG386i4Q8EQMBAIAnfB7xBxBqvapJ9zX9WgRS2YMpnvYMGdPK3aMjt/3dUcNI4blpj7iwkMFWDXDvSmgAlijrt9RTR78+PS6z1uAJZIe93Q8g5zcsWCi/4Y+C8bah5zUv3vv89uft30QP23punGCx5954oBBwnwYaNCDY/wYrsYeggnM9B2Fpf8GG2CEUVWhbWAtGouEGDy7Y4IEJVrbSiXghqGKIo7z1IVcXIkKWWR361QOLWWnIhwERpLaaCCee5iMBGJQmJGyPFTnbkfHVZGRtIGrg5HALEJAZbu39BuUEUmq1JJQIPtZilY5hGeSWsSk52G9XqsmgljdIcABytq13HyIM6RcUA+r1qZ4EBF3WHWB29tBgAzRhEGhig8KmqKFv8SeCeo+mgsF7YFXa1qWSbkDpom/mqR1PmHCqJ3fwNRVXjC7S6CZhFVCQ2lWvZiirhQq42SACt25IK2hv8TprriUV1usGgeka7LFcNmCldMLi6qZMgFLgpw16Cipb7bC1knXsBiEAACH5BAUKABsALAcABADOAAsAAAX/4FZsJPkUmUGsLCEUTywXglFuSg7fW1xAvNWLF6sFFcPb42C8EZCj24EJdCp2yoegWsolS0Uu6fmamg8n8YYcLU2bXSiRaXMGvqV6/KAeJAh8VgZqCX+BexCFioWAYgqNi4qAR4ORhRuHY408jAeUhAmYYiuVlpiflqGZa5CWkzc5fKmbbhIpsAoQDRG8vQwQCBLCwxK6vb5qwhfGxxENahvCEA7NzskSy7vNzzzK09W/PNHF1NvX2dXcN8K55cfh69Luveol3vO8zwi4Yhj+AQwmCBw4IYclDAAJDlQggVOChAoLKkgFkSCAHDwWLKhIEOONARsDKryogFPIiAUb/95gJNIiw4wnI778GFPhzBKFOAq8qLJEhQpiNArjMcHCmlTCUDIouTKBhApELSxFWiGiVKY4E2CAekPgUphDu0742nRrVLJZnyrFSqKQ2ohoSYAMW6IoDpNJ4bLdILTnAj8KUF7UeENjAKuDyxIgOuGiOI0EBBMgLNew5AUrDTMGsFixwBIaNCQuAXJB57qNJ2OWm2Aj4skwCQCIyNkhhtMkdsIuodE0AN4LJDRgfLPtn5YDLdBlraAByuUbBgxQwICxMOnYpVOPej074OFdlfc0TqC62OIbcppHjV4o+LrieWhfT8JC/I/T6W8oCl29vQ0XjLdBaA3s1RcPBO7lFvpX8BVoG4O5jTXRQRDuJ6FDTzEWF1/BCZhgbyAKE9qICYLloQYOFtahVRsWYlZ4KQJHlwHS/IYaZ6sZd9tmu5HQm2xi1UaTbzxYwJk/wBF5g5EEYOBZeEfGZmNdFyFZmZIR4jikbLThlh5kUUVJGmRT7sekkziRWUIACABk3T4qCsedgO4xhgGcY7q5pHJ4klBBTQRJ0CeHcoYHHUh6wgfdn9uJdSdMiebGJ0zUPTcoS286FCkrZxnYoYYKWLkBowhQoBeaOlZAgVhLidrXqg2G
5201iqpQpZ4apwSwRtjqrB3muoF9BboaXKmshlqWqsWiGt2wphJkQbAU5hoCACH5BAUKABsALAcABADOAAsAAAX/oGFw2WZuT5oZROsSQnGaKjRvilI893MItlNOJ5v5gDcFrHhKIWcEYu/xFEqNv6B1N62aclysF7fsZYe5aOx2yL5aAUGSaT1oTYMBwQ5VGCAJgYIJCnx1gIOBhXdwiIl7d0p2iYGQUAQBjoOFSQR/lIQHnZ+Ue6OagqYzSqSJi5eTpTxGcjcSChANEbu8DBAIEsHBChe5vL13G7fFuscRDcnKuM3H0La3EA7Oz8kKEsXazr7Cw9/Gztar5uHHvte47MjktznZ2w0G1+D3BgirAqJmJMAQgMGEgwgn5Ei0gKDBhBMALGRYEOJBb5QcWlQo4cbAihZz3GgIMqFEBSM1/4ZEOWPAgpIIJXYU+PIhRG8ja1qU6VHlzZknJNQ6UanCjQkWCIGSUGEjAwVLjc44+DTqUQtPPS5gejUrTa5TJ3g9sWCr1BNUWZI161StiQUDmLYdGfesibQ3XMq1OPYthrwuA2yU2LBs2cBHIypYQPPlYAKFD5cVvNPtW8eVGbdcQADATsiNO4cFAPkvHpedPzc8kUcPgNGgZ5RNDZG05reoE9s2vSEP79MEGiQGy1qP8LA4ZcdtsJE48ONoLTBtTV0B9LsTnPceoIDBDQvS7W7vfjVY3q3eZ4A339J4eaAmKqU/sV58HvJh2RcnIBsDUw0ABqhBA5aV5V9XUFGiHfVeAiWwoFgJJrIXRH1tEMiDFV4oHoAEGlaWhgIGSGBO2nFomYY3mKjVglidaNYJGJDkWW2xxTfbjCbVaOGNqoX2GloR8ZeTaECS9pthRGJH2g0b3Agbk6hNANtteHD2GJUucfajCQBy5OOTQ25ZgUPvaVVQmbKh9510/qQpwXx3SQdfk8tZJOd5b6JJFplT3ZnmmX3qd5l1eg5q00HrtUkUn0AKaiGjClSAgKLYZcgWXwocGRcCFGCKwSB6ceqphwmYRUFYT/1WKlOdUpipmxW0mlCqHjYkAaeoZlqrqZ4qd+upQKaapn/AmgAegZ8KUtYtFAQQAgAh+QQFCgAbACwHAAQAzgALAAAF/+C2PUcmiCiZGUTrEkKBis8jQEquKwU5HyXIbEPgyX7BYa5wTNmEMwWsSXsqFbEh8DYs9mrgGjdK6GkPY5GOeU6ryz7UFopSQEzygOGhJBjoIgMDBAcBM0V/CYqLCQqFOwobiYyKjn2TlI6GKC2YjJZknouaZAcQlJUHl6eooJwKooobqoewrJSEmyKdt59NhRKFMxLEEA4RyMkMEAjDEhfGycqAG8TQx9IRDRDE3d3R2ctD1RLg0ttKEnbY5wZD3+zJ6M7X2RHi9Oby7u/r9g38UFjTh2xZJBEBMDAboogAgwkQI07IMUORwocSJwCgWDFBAIwZOaJIsOBjRogKJP8wTODw5ESVHVtm3AhzpEeQElOuNDlTZ0ycEUWKWFASqEahGwYUPbnxoAgEdlYSqDBkgoUNClAlIHbSAoOsqCRQnQHxq1axVb06FWFxLIqyaze0Tft1JVqyE+pWXMD1pF6bYl3+HTqAWNW8cRUFzmih0ZAAB2oGKukSAAGGRHWJgLiR6AylBLpuHKKUMlMCngMpDSAa9QIUggZVVvDaJobLeC3XZpvgNgCmtPcuwP3WgmXSq4do0DC6o2/guzcseECtUoO0hmcsGKDgOt7ssBd07wqesAIGZC1YIBa7PQHvb1+SFo+++HrJSQfB33xfav3i5eX3Hnb4CTJgegEq8tH/YQEOcIJzbm2G2EoYRLgBXFpVmFYDcREV4HIcnmUhiGBRouEMJGJGzHIspqgdXxK0yCKHRNXoIX4uorCdTyjkyNtdPWrA4Up82EbAbzMRxxZRR54WXVLDIRmRcag5d2R6ugl3ZXzNhTecchpMhIGVAKAYpgJjjsSklBEd99maZoo535ZvdamjBEpusJyctg3h4X8XqodBMx0tiNeg/oGJaKGABpogS40KSqiaEgBqlQWLUtqoVQnytekEjzo0hHqhRorppOZt2p923M2AAV+oBtpAnnPNoB6HaU6mAAIU+IXmi3j2mtFXuUoHKwXpzVrsjcgGOauKEjQrwq157hitGq2NoWmjh7z6Wmxb0m5w66+2VRAuXN/yFUAIACH5BAUKABsALAcABADOAAsAAAX/4CZuRiaM45MZqBgIRbs9AqTcuFLE7VHLOh7KB5ERdjJaEaU4ClO/lgKWjKKcMiJQ8KgumcieVdQMD8cbBeuAkkC6LYLhOxoQ2PF5Ys9PKPBMen17f0CCg4VSh32JV4t8jSNqEIOEgJKPlkYBlJWRInKdiJdkmQlvKAsLBxdABA4RsbIMBggtEhcQsLKxDBC2TAS6vLENdJLDxMZAubu8vjIbzcQRtMzJz79S08oQEt/guNiyy7fcvMbh4OezdAvGrakLAQwyABsELQkY9BP+//ckyPDD4J9BfAMh1GsBoImMeQUN+lMgUJ9CiRMa5msxoB9Gh/o8GmxYMZXIgxtR/yQ46S/gQAURR0pDwYDfywoyLPip5AdnCwsMFPBU4BPFhKBDi444quCmDKZOfwZ9KEGpCKgcN1jdALSpPqIYsabS+nSqvqplvYqQYAeDPgwKwjaMtiDl0oaqUAyo+3TuWwUAMPpVCfee0cEjVBGQq2ABx7oTWmQk4FglZMGN9fGVDMCuiH2AOVOu/PmyxM630gwM0CCn6q8LjVJ8GXvpa5Uwn95OTC/nNxkda1/dLSK475IjCD6dHbK1ZOa4hXP9DXs5chJ00UpVm5xo2qRpoxptwF2E4/IbJpB/SDz9+q9b1aNfQH08+p4a8uvX8B53fLP+ycAfemjsRUBgp1H20K+BghHgVgt1GXZXZpZ5lt4ECjxYR4ScUWiShEtZqBiIInRGWnERNnjiBglw+JyGnxUmGowsyiiZg189lNtPGACjV2+S9UjbU0JWF6SPvEk3QZEqsZYTk3UAaRSUnznJI5LmESCdBVSyaOWUWLK4I5gDUYVeV1T9l+FZClCAUVA09uSmRHBCKAECFEhW51ht6rnmWBXkaR+NjuHpJ40D3DmnQXt2F+ihZxlqVKOfQRACACH5BAUKABwALAcABADOAAsAAAX/ICdyUCkUo/g8mUG8MCGkKgspeC6j6XEIEBpBUeCNfECaglBcOVfJFK7YQwZHQ6JRZBUqTrSuVEuD3nI45pYjFuWKvjjSkCoRaBUMWxkwBGgJCXspQ36Bh4EEB0oKhoiBgyNLjo8Ki4QElIiWfJqHnISNEI+Ql5J9o6SgkqKkgqYihamPkW6oNBgSfiMMDQkGCBLCwxIQDhHIyQwQCGMKxsnKVyPCF9DREQ3MxMPX0cu4wt7J2uHWx9jlKd3o39MiuefYEcvNkuLt5O8c1ePI2tyELXGQwoGDAQf+iEC2xByDCRAjTlAgIUWCBRgCPJQ4AQBFXAs0coT40WLIjRxL/47AcHLkxIomRXL0CHPERZkpa4q4iVKiyp0tR/7kwHMkTUBBJR5dOCEBAVcKKtCAyOHpowXCpk7goABqBZdcvWploACpBKkpIJI1q5OD2rIWE0R1uTZu1LFwbWL9OlKuWb4c6+o9i3dEgw0RCGDUG9KlRw56gDY2qmCByZBaASi+TACA0TucAaTteCcy0ZuOK3N2vJlx58+LRQyY3Xm0ZsgjZg+oPQLi7dUcNXi0LOJw1pgNtB7XG6CBy+U75SYfPTSQAgZTNUDnQHt67wnbZyvwLgKiMN3oCZB3C76tdewpLFgIP2C88rbi4Y+QT3+8S5USMICZXWj1pkEDeUU3lOYGB3alSoEiMIjgX4WlgNF2EibIwQIXauWXSRg2SAOHIU5IIIMoZkhhWiJaiFVbKo6AQEgQXrTAazO1JhkBrBG3Y2Y6EsUhaGn95hprSN0oWpFE7rhkeaQBchGOEWnwEmc0uKWZj0LeuNV3W4Y2lZHFlQCSRjTIl8uZ+kG5HU/3sRlnTG2ytyadytnD3HrmuRcSn+0h1dycexIK1KCjYaCnjCCVqOFFJTZ5GkUUjESWaUIKU2lgCmAKKQIUjHapXRKE+t2og1VgankNYnohqKJ2CmKplso6GKz7WYCgqxeuyoF8u9IQAgA7',
5202            msg: null,
5203            msgText: '<em>Loading the next set of posts...</em>',
5204            selector: null,
5205            speed: 'fast',
5206            start: undefined
5207        },
5208        state: {
5209            isDuringAjax: false,
5210            isInvalidPage: false,
5211            isDestroyed: false,
5212            isDone: false, // For when it goes all the way through the archive.
5213            isPaused: false,
5214            isBeyondMaxPage: false,
5215            currPage: 1
5216        },
5217        debug: false,
5218        behavior: undefined,
5219        binder: $(window), // used to cache the selector
5220        nextSelector: 'div.navigation a:first',
5221        navSelector: 'div.navigation',
5222        contentSelector: null, // rename to pageFragment
5223        extraScrollPx: 150,
5224        itemSelector: 'div.post',
5225        animate: false,
5226        pathParse: undefined,
5227        dataType: 'html',
5228        appendCallback: true,
5229        bufferPx: 40,
5230        errorCallback: function () { },
5231        infid: 0, //Instance ID
5232        pixelsFromNavToBottom: undefined,
5233        path: undefined, // Either parts of a URL as an array (e.g. ["/page/", "/"] or a function that takes in the page number and returns a URL
5234        prefill: false, // When the document is smaller than the window, load data until the document is larger or links are exhausted
5235        maxPage: undefined // to manually control maximum page (when maxPage is undefined, maximum page limitation is not work)
5236    };
5237
5238    $.infinitescroll.prototype = {
5239
5240        /*
5241            ----------------------------
5242            Private methods
5243            ----------------------------
5244            */
5245
5246        // Bind or unbind from scroll
5247        _binding: function infscr_binding(binding) {
5248
5249            var instance = this,
5250            opts = instance.options;
5251
5252            opts.v = '2.0b2.120520';
5253
5254            // if behavior is defined and this function is extended, call that instead of default
5255            if (!!opts.behavior && this['_binding_'+opts.behavior] !== undefined) {
5256                this['_binding_'+opts.behavior].call(this);
5257                return;
5258            }
5259
5260            if (binding !== 'bind' && binding !== 'unbind') {
5261                this._debug('Binding value  ' + binding + ' not valid');
5262                return false;
5263            }
5264
5265            if (binding === 'unbind') {
5266                (this.options.binder).unbind('smartscroll.infscr.' + instance.options.infid);
5267            } else {
5268                (this.options.binder)[binding]('smartscroll.infscr.' + instance.options.infid, function () {
5269                    instance.scroll();
5270                });
5271            }
5272
5273            this._debug('Binding', binding);
5274        },
5275
5276        // Fundamental aspects of the plugin are initialized
5277        _create: function infscr_create(options, callback) {
5278
5279            // Add custom options to defaults
5280            var opts = $.extend(true, {}, $.infinitescroll.defaults, options);
5281            this.options = opts;
5282            var $window = $(window);
5283            var instance = this;
5284
5285            // Validate selectors
5286            if (!instance._validate(options)) {
5287                return false;
5288            }
5289
5290            // Validate page fragment path
5291            var path = $(opts.nextSelector).attr('href');
5292            if (!path) {
5293                this._debug('Navigation selector not found');
5294                return false;
5295            }
5296
5297            // Set the path to be a relative URL from root.
5298            opts.path = opts.path || this._determinepath(path);
5299
5300            // contentSelector is 'page fragment' option for .load() / .ajax() calls
5301            opts.contentSelector = opts.contentSelector || this.element;
5302
5303            // loading.selector - if we want to place the load message in a specific selector, defaulted to the contentSelector
5304            opts.loading.selector = opts.loading.selector || opts.contentSelector;
5305
5306            // Define loading.msg
5307            opts.loading.msg = opts.loading.msg || $('<div id="infscr-loading"><img alt="Loading..." src="' + opts.loading.img + '" /><div>' + opts.loading.msgText + '</div></div>');
5308
5309            // Preload loading.img
5310            (new Image()).src = opts.loading.img;
5311
5312            // distance from nav links to bottom
5313            // computed as: height of the document + top offset of container - top offset of nav link
5314            if(opts.pixelsFromNavToBottom === undefined) {
5315                opts.pixelsFromNavToBottom = $(document).height() - $(opts.navSelector).offset().top;
5316                this._debug('pixelsFromNavToBottom: ' + opts.pixelsFromNavToBottom);
5317            }
5318
5319            var self = this;
5320
5321            // determine loading.start actions
5322            opts.loading.start = opts.loading.start || function() {
5323                $(opts.navSelector).hide();
5324                opts.loading.msg
5325                .appendTo(opts.loading.selector)
5326                .show(opts.loading.speed, $.proxy(function() {
5327                    this.beginAjax(opts);
5328                }, self));
5329            };
5330
5331            // determine loading.finished actions
5332            opts.loading.finished = opts.loading.finished || function() {
5333                if (!opts.state.isBeyondMaxPage)
5334                    opts.loading.msg.fadeOut(opts.loading.speed);
5335            };
5336
5337            // callback loading
5338            opts.callback = function(instance, data, url) {
5339                if (!!opts.behavior && instance['_callback_'+opts.behavior] !== undefined) {
5340                    instance['_callback_'+opts.behavior].call($(opts.contentSelector)[0], data, url);
5341                }
5342
5343                if (callback) {
5344                    callback.call($(opts.contentSelector)[0], data, opts, url);
5345                }
5346
5347                if (opts.prefill) {
5348                    $window.bind('resize.infinite-scroll', instance._prefill);
5349                }
5350            };
5351
5352            if (options.debug) {
5353                // Tell IE9 to use its built-in console
5354                if (Function.prototype.bind && (typeof console === 'object' || typeof console === 'function') && typeof console.log === 'object') {
5355                    ['log','info','warn','error','assert','dir','clear','profile','profileEnd']
5356                        .forEach(function (method) {
5357                            console[method] = this.call(console[method], console);
5358                        }, Function.prototype.bind);
5359                }
5360            }
5361
5362            this._setup();
5363
5364            // Setups the prefill method for use
5365            if (opts.prefill) {
5366                this._prefill();
5367            }
5368
5369            // Return true to indicate successful creation
5370            return true;
5371        },
5372
5373        _prefill: function infscr_prefill() {
5374            var instance = this;
5375            var $window = $(window);
5376
5377            function needsPrefill() {
5378                return ( $(instance.options.contentSelector).height() <= $window.height() );
5379            }
5380
5381            this._prefill = function() {
5382                if (needsPrefill()) {
5383                    instance.scroll();
5384                }
5385
5386                $window.bind('resize.infinite-scroll', function() {
5387                    if (needsPrefill()) {
5388                        $window.unbind('resize.infinite-scroll');
5389                        instance.scroll();
5390                    }
5391                });
5392            };
5393
5394            // Call self after setting up the new function
5395            this._prefill();
5396        },
5397
5398        // Console log wrapper
5399        _debug: function infscr_debug() {
5400            if (true !== this.options.debug) {
5401                return;
5402            }
5403
5404            if (typeof console !== 'undefined' && typeof console.log === 'function') {
5405                // Modern browsers
5406                // Single argument, which is a string
5407                if ((Array.prototype.slice.call(arguments)).length === 1 && typeof Array.prototype.slice.call(arguments)[0] === 'string') {
5408                    console.log( (Array.prototype.slice.call(arguments)).toString() );
5409                } else {
5410                    console.log( Array.prototype.slice.call(arguments) );
5411                }
5412            } else if (!Function.prototype.bind && typeof console !== 'undefined' && typeof console.log === 'object') {
5413                // IE8
5414                Function.prototype.call.call(console.log, console, Array.prototype.slice.call(arguments));
5415            }
5416        },
5417
5418        // find the number to increment in the path.
5419        _determinepath: function infscr_determinepath(path) {
5420
5421            var opts = this.options;
5422
5423            // if behavior is defined and this function is extended, call that instead of default
5424            if (!!opts.behavior && this['_determinepath_'+opts.behavior] !== undefined) {
5425                return this['_determinepath_'+opts.behavior].call(this,path);
5426            }
5427
5428            if (!!opts.pathParse) {
5429
5430                this._debug('pathParse manual');
5431                return opts.pathParse(path, this.options.state.currPage+1);
5432
5433            } else if (path.match(/^(.*?)\b2\b(.*?$)/)) {
5434                path = path.match(/^(.*?)\b2\b(.*?$)/).slice(1);
5435
5436                // if there is any 2 in the url at all.
5437            } else if (path.match(/^(.*?)2(.*?$)/)) {
5438
5439                // page= is used in django:
5440                // http://www.infinite-scroll.com/changelog/comment-page-1/#comment-127
5441                if (path.match(/^(.*?page=)2(\/.*|$)/)) {
5442                    path = path.match(/^(.*?page=)2(\/.*|$)/).slice(1);
5443                    return path;
5444                }
5445
5446                path = path.match(/^(.*?)2(.*?$)/).slice(1);
5447
5448            } else {
5449
5450                // page= is used in drupal too but second page is page=1 not page=2:
5451                // thx Jerod Fritz, vladikoff
5452                if (path.match(/^(.*?page=)1(\/.*|$)/)) {
5453                    path = path.match(/^(.*?page=)1(\/.*|$)/).slice(1);
5454                    return path;
5455                } else {
5456                    this._debug("Sorry, we couldn't parse your Next (Previous Posts) URL. Verify your the css selector points to the correct A tag. If you still get this error: yell, scream, and kindly ask for help at infinite-scroll.com.");
5457                    // Get rid of isInvalidPage to allow permalink to state
5458                    opts.state.isInvalidPage = true;  //prevent it from running on this page.
5459                }
5460            }
5461            this._debug('determinePath', path);
5462            return path;
5463
5464        },
5465
5466        // Custom error
5467        _error: function infscr_error(xhr) {
5468
5469            var opts = this.options;
5470
5471            // if behavior is defined and this function is extended, call that instead of default
5472            if (!!opts.behavior && this['_error_'+opts.behavior] !== undefined) {
5473                this['_error_'+opts.behavior].call(this,xhr);
5474                return;
5475            }
5476
5477            if (xhr !== 'destroy' && xhr !== 'end') {
5478                xhr = 'unknown';
5479            }
5480
5481            this._debug('Error', xhr);
5482
5483            if (xhr === 'end' || opts.state.isBeyondMaxPage) {
5484                this._showdonemsg();
5485            }
5486
5487            opts.state.isDone = true;
5488            opts.state.currPage = 1; // if you need to go back to this instance
5489            opts.state.isPaused = false;
5490            opts.state.isBeyondMaxPage = false;
5491            this._binding('unbind');
5492
5493        },
5494
5495        // Load Callback
5496        _loadcallback: function infscr_loadcallback(box, data, url) {
5497            var opts = this.options,
5498            callback = this.options.callback, // GLOBAL OBJECT FOR CALLBACK
5499            result = (opts.state.isDone) ? 'done' : (!opts.appendCallback) ? 'no-append' : 'append',
5500            frag;
5501
5502            // if behavior is defined and this function is extended, call that instead of default
5503            if (!!opts.behavior && this['_loadcallback_'+opts.behavior] !== undefined) {
5504                this['_loadcallback_'+opts.behavior].call(this,box,data);
5505                return;
5506            }
5507
5508            switch (result) {
5509                case 'done':
5510                    this._showdonemsg();
5511                    return false;
5512
5513                case 'no-append':
5514                    if (opts.dataType === 'html') {
5515                        data = '<div>' + data + '</div>';
5516                        data = $(data).find(opts.itemSelector);
5517                    }
5518
5519                    // if it didn't return anything
5520                    if (data.length === 0) {
5521                        return this._error('end');
5522                    }
5523
5524                    break;
5525
5526                case 'append':
5527                    var children = box.children();
5528                    // if it didn't return anything
5529                    if (children.length === 0) {
5530                        return this._error('end');
5531                    }
5532
5533                    // use a documentFragment because it works when content is going into a table or UL
5534                    frag = document.createDocumentFragment();
5535                    while (box[0].firstChild) {
5536                        frag.appendChild(box[0].firstChild);
5537                    }
5538
5539                    this._debug('contentSelector', $(opts.contentSelector)[0]);
5540                    $(opts.contentSelector)[0].appendChild(frag);
5541                    // previously, we would pass in the new DOM element as context for the callback
5542                    // however we're now using a documentfragment, which doesn't have parents or children,
5543                    // so the context is the contentContainer guy, and we pass in an array
5544                    // of the elements collected as the first argument.
5545
5546                    data = children.get();
5547                    break;
5548            }
5549
5550            // loadingEnd function
5551            opts.loading.finished.call($(opts.contentSelector)[0],opts);
5552
5553            // smooth scroll to ease in the new content
5554            if (opts.animate) {
5555                var scrollTo = $(window).scrollTop() + $(opts.loading.msg).height() + opts.extraScrollPx + 'px';
5556                $('html,body').animate({ scrollTop: scrollTo }, 800, function () { opts.state.isDuringAjax = false; });
5557            }
5558
5559            if (!opts.animate) {
5560                // once the call is done, we can allow it again.
5561                opts.state.isDuringAjax = false;
5562            }
5563
5564            callback(this, data, url);
5565
5566            if (opts.prefill) {
5567                this._prefill();
5568            }
5569        },
5570
5571        _nearbottom: function infscr_nearbottom() {
5572
5573            var opts = this.options,
5574            pixelsFromWindowBottomToBottom = 0 + $(document).height() - (opts.binder.scrollTop()) - $(window).height();
5575
5576            // if behavior is defined and this function is extended, call that instead of default
5577            if (!!opts.behavior && this['_nearbottom_'+opts.behavior] !== undefined) {
5578                return this['_nearbottom_'+opts.behavior].call(this);
5579            }
5580
5581            this._debug('math:', pixelsFromWindowBottomToBottom, opts.pixelsFromNavToBottom);
5582
5583            // if distance remaining in the scroll (including buffer) is less than the orignal nav to bottom....
5584            return (pixelsFromWindowBottomToBottom - opts.bufferPx < opts.pixelsFromNavToBottom);
5585
5586        },
5587
5588        // Pause / temporarily disable plugin from firing
5589        _pausing: function infscr_pausing(pause) {
5590
5591            var opts = this.options;
5592
5593            // if behavior is defined and this function is extended, call that instead of default
5594            if (!!opts.behavior && this['_pausing_'+opts.behavior] !== undefined) {
5595                this['_pausing_'+opts.behavior].call(this,pause);
5596                return;
5597            }
5598
5599            // If pause is not 'pause' or 'resume', toggle it's value
5600            if (pause !== 'pause' && pause !== 'resume' && pause !== null) {
5601                this._debug('Invalid argument. Toggling pause value instead');
5602            }
5603
5604            pause = (pause && (pause === 'pause' || pause === 'resume')) ? pause : 'toggle';
5605
5606            switch (pause) {
5607                case 'pause':
5608                    opts.state.isPaused = true;
5609                break;
5610
5611                case 'resume':
5612                    opts.state.isPaused = false;
5613                break;
5614
5615                case 'toggle':
5616                    opts.state.isPaused = !opts.state.isPaused;
5617                break;
5618            }
5619
5620            this._debug('Paused', opts.state.isPaused);
5621            return false;
5622
5623        },
5624
5625        // Behavior is determined
5626        // If the behavior option is undefined, it will set to default and bind to scroll
5627        _setup: function infscr_setup() {
5628
5629            var opts = this.options;
5630
5631            // if behavior is defined and this function is extended, call that instead of default
5632            if (!!opts.behavior && this['_setup_'+opts.behavior] !== undefined) {
5633                this['_setup_'+opts.behavior].call(this);
5634                return;
5635            }
5636
5637            this._binding('bind');
5638
5639            return false;
5640
5641        },
5642
5643        // Show done message
5644        _showdonemsg: function infscr_showdonemsg() {
5645
5646            var opts = this.options;
5647
5648            // if behavior is defined and this function is extended, call that instead of default
5649            if (!!opts.behavior && this['_showdonemsg_'+opts.behavior] !== undefined) {
5650                this['_showdonemsg_'+opts.behavior].call(this);
5651                return;
5652            }
5653
5654            opts.loading.msg
5655            .find('img')
5656            .hide()
5657            .parent()
5658            .find('div').html(opts.loading.finishedMsg).animate({ opacity: 1 }, 2000, function () {
5659                $(this).parent().fadeOut(opts.loading.speed);
5660            });
5661
5662            // user provided callback when done
5663            opts.errorCallback.call($(opts.contentSelector)[0],'done');
5664        },
5665
5666        // grab each selector option and see if any fail
5667        _validate: function infscr_validate(opts) {
5668            for (var key in opts) {
5669                if (key.indexOf && key.indexOf('Selector') > -1 && $(opts[key]).length === 0) {
5670                    this._debug('Your ' + key + ' found no elements.');
5671                    return false;
5672                }
5673            }
5674
5675            return true;
5676        },
5677
5678        /*
5679            ----------------------------
5680            Public methods
5681            ----------------------------
5682            */
5683
5684        // Bind to scroll
5685        bind: function infscr_bind() {
5686            this._binding('bind');
5687        },
5688
5689        // Destroy current instance of plugin
5690        destroy: function infscr_destroy() {
5691            this.options.state.isDestroyed = true;
5692            this.options.loading.finished();
5693            return this._error('destroy');
5694        },
5695
5696        // Set pause value to false
5697        pause: function infscr_pause() {
5698            this._pausing('pause');
5699        },
5700
5701        // Set pause value to false
5702        resume: function infscr_resume() {
5703            this._pausing('resume');
5704        },
5705
5706        beginAjax: function infscr_ajax(opts) {
5707            var instance = this,
5708                path = opts.path,
5709                box, desturl, method, condition;
5710
5711            // increment the URL bit. e.g. /page/3/
5712            opts.state.currPage++;
5713
5714            // Manually control maximum page
5715            if ( opts.maxPage !== undefined && opts.state.currPage > opts.maxPage ){
5716                opts.state.isBeyondMaxPage = true;
5717                this.destroy();
5718                return;
5719            }
5720
5721            // if we're dealing with a table we can't use DIVs
5722            box = $(opts.contentSelector).is('table, tbody') ? $('<tbody/>') : $('<div/>');
5723
5724            desturl = (typeof path === 'function') ? path(opts.state.currPage) : path.join(opts.state.currPage);
5725            instance._debug('heading into ajax', desturl);
5726
5727            method = (opts.dataType === 'html' || opts.dataType === 'json' ) ? opts.dataType : 'html+callback';
5728            if (opts.appendCallback && opts.dataType === 'html') {
5729                method += '+callback';
5730            }
5731
5732            switch (method) {
5733                case 'html+callback':
5734                    instance._debug('Using HTML via .load() method');
5735                    box.load(desturl + ' ' + opts.itemSelector, undefined, function infscr_ajax_callback(responseText) {
5736                        instance._loadcallback(box, responseText, desturl);
5737                    });
5738
5739                    break;
5740
5741                case 'html':
5742                    instance._debug('Using ' + (method.toUpperCase()) + ' via $.ajax() method');
5743                    $.ajax({
5744                        // params
5745                        url: desturl,
5746                        dataType: opts.dataType,
5747                        complete: function infscr_ajax_callback(jqXHR, textStatus) {
5748                            condition = (typeof (jqXHR.isResolved) !== 'undefined') ? (jqXHR.isResolved()) : (textStatus === 'success' || textStatus === 'notmodified');
5749                            if (condition) {
5750                                instance._loadcallback(box, jqXHR.responseText, desturl);
5751                            } else {
5752                                instance._error('end');
5753                            }
5754                        }
5755                    });
5756
5757                    break;
5758                case 'json':
5759                    instance._debug('Using ' + (method.toUpperCase()) + ' via $.ajax() method');
5760                    $.ajax({
5761                        dataType: 'json',
5762                        type: 'GET',
5763                        url: desturl,
5764                        success: function (data, textStatus, jqXHR) {
5765                            condition = (typeof (jqXHR.isResolved) !== 'undefined') ? (jqXHR.isResolved()) : (textStatus === 'success' || textStatus === 'notmodified');
5766                            if (opts.appendCallback) {
5767                                // if appendCallback is true, you must defined template in options.
5768                                // note that data passed into _loadcallback is already an html (after processed in opts.template(data)).
5769                                if (opts.template !== undefined) {
5770                                    var theData = opts.template(data);
5771                                    box.append(theData);
5772                                    if (condition) {
5773                                        instance._loadcallback(box, theData);
5774                                    } else {
5775                                        instance._error('end');
5776                                    }
5777                                } else {
5778                                    instance._debug('template must be defined.');
5779                                    instance._error('end');
5780                                }
5781                            } else {
5782                                // if appendCallback is false, we will pass in the JSON object. you should handle it yourself in your callback.
5783                                if (condition) {
5784                                    instance._loadcallback(box, data, desturl);
5785                                } else {
5786                                    instance._error('end');
5787                                }
5788                            }
5789                        },
5790                        error: function() {
5791                            instance._debug('JSON ajax request failed.');
5792                            instance._error('end');
5793                        }
5794                    });
5795
5796                    break;
5797            }
5798        },
5799
5800        // Retrieve next set of content items
5801        retrieve: function infscr_retrieve(pageNum) {
5802            pageNum = pageNum || null;
5803
5804            var instance = this,
5805            opts = instance.options;
5806
5807            // if behavior is defined and this function is extended, call that instead of default
5808            if (!!opts.behavior && this['retrieve_'+opts.behavior] !== undefined) {
5809                this['retrieve_'+opts.behavior].call(this,pageNum);
5810                return;
5811            }
5812
5813            // for manual triggers, if destroyed, get out of here
5814            if (opts.state.isDestroyed) {
5815                this._debug('Instance is destroyed');
5816                return false;
5817            }
5818
5819            // we dont want to fire the ajax multiple times
5820            opts.state.isDuringAjax = true;
5821
5822            opts.loading.start.call($(opts.contentSelector)[0],opts);
5823        },
5824
5825        // Check to see next page is needed
5826        scroll: function infscr_scroll() {
5827
5828            var opts = this.options,
5829            state = opts.state;
5830
5831            // if behavior is defined and this function is extended, call that instead of default
5832            if (!!opts.behavior && this['scroll_'+opts.behavior] !== undefined) {
5833                this['scroll_'+opts.behavior].call(this);
5834                return;
5835            }
5836
5837            if (state.isDuringAjax || state.isInvalidPage || state.isDone || state.isDestroyed || state.isPaused) {
5838                return;
5839            }
5840
5841            if (!this._nearbottom()) {
5842                return;
5843            }
5844
5845            this.retrieve();
5846
5847        },
5848
5849        // Toggle pause value
5850        toggle: function infscr_toggle() {
5851            this._pausing();
5852        },
5853
5854        // Unbind from scroll
5855        unbind: function infscr_unbind() {
5856            this._binding('unbind');
5857        },
5858
5859        // update options
5860        update: function infscr_options(key) {
5861            if ($.isPlainObject(key)) {
5862                this.options = $.extend(true,this.options,key);
5863            }
5864        }
5865    };
5866
5867
5868    /*
5869        ----------------------------
5870        Infinite Scroll function
5871        ----------------------------
5872
5873        Borrowed logic from the following...
5874
5875        jQuery UI
5876        - https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js
5877
5878        jCarousel
5879        - https://github.com/jsor/jcarousel/blob/master/lib/jquery.jcarousel.js
5880
5881        Masonry
5882        - https://github.com/desandro/masonry/blob/master/jquery.masonry.js
5883
5884*/
5885
5886    $.fn.infinitescroll = function infscr_init(options, callback) {
5887
5888
5889        var thisCall = typeof options;
5890
5891        switch (thisCall) {
5892
5893            // method
5894            case 'string':
5895                var args = Array.prototype.slice.call(arguments, 1);
5896
5897                this.each(function () {
5898                    var instance = $.data(this, 'infinitescroll');
5899
5900                    if (!instance) {
5901                        // not setup yet
5902                        // return $.error('Method ' + options + ' cannot be called until Infinite Scroll is setup');
5903                        return false;
5904                    }
5905
5906                    if (!$.isFunction(instance[options]) || options.charAt(0) === '_') {
5907                        // return $.error('No such method ' + options + ' for Infinite Scroll');
5908                        return false;
5909                    }
5910
5911                    // no errors!
5912                    instance[options].apply(instance, args);
5913                });
5914
5915            break;
5916
5917            // creation
5918            case 'object':
5919
5920                this.each(function () {
5921
5922                var instance = $.data(this, 'infinitescroll');
5923
5924                if (instance) {
5925
5926                    // update options of current instance
5927                    instance.update(options);
5928
5929                } else {
5930
5931                    // initialize new instance
5932                    instance = new $.infinitescroll(options, callback, this);
5933
5934                    // don't attach if instantiation failed
5935                    if (!instance.failed) {
5936                        $.data(this, 'infinitescroll', instance);
5937                    }
5938
5939                }
5940
5941            });
5942
5943            break;
5944
5945        }
5946
5947        return this;
5948    };
5949
5950
5951
5952    /*
5953     * smartscroll: debounced scroll event for jQuery *
5954     * https://github.com/lukeshumard/smartscroll
5955     * Based on smartresize by @louis_remi: https://github.com/lrbabe/jquery.smartresize.js *
5956     * Copyright 2011 Louis-Remi & Luke Shumard * Licensed under the MIT license. *
5957     */
5958
5959    var event = $.event,
5960    scrollTimeout;
5961
5962    event.special.smartscroll = {
5963        setup: function () {
5964            $(this).bind('scroll', event.special.smartscroll.handler);
5965        },
5966        teardown: function () {
5967            $(this).unbind('scroll', event.special.smartscroll.handler);
5968        },
5969        handler: function (event, execAsap) {
5970            // Save the context
5971            var context = this,
5972            args = arguments;
5973
5974            // set correct event type
5975            event.type = 'smartscroll';
5976
5977            if (scrollTimeout) { clearTimeout(scrollTimeout); }
5978            scrollTimeout = setTimeout(function () {
5979                $(context).trigger('smartscroll', args);
5980            }, execAsap === 'execAsap' ? 0 : 100);
5981        }
5982    };
5983
5984    $.fn.smartscroll = function (fn) {
5985        return fn ? this.bind('smartscroll', fn) : this.trigger('smartscroll', ['execAsap']);
5986    };
5987
5988}));
5989
5990/*!
5991Waypoints - 4.0.1
5992Copyright © 2011-2016 Caleb Troughton
5993Licensed under the MIT license.
5994https://github.com/imakewebthings/waypoints/blob/master/licenses.txt
5995*/
5996(function() {
5997  'use strict'
5998
5999  var keyCounter = 0
6000  var allWaypoints = {}
6001
6002  /* http://imakewebthings.com/waypoints/api/waypoint */
6003  function Waypoint(options) {
6004    if (!options) {
6005      throw new Error('No options passed to Waypoint constructor')
6006    }
6007    if (!options.element) {
6008      throw new Error('No element option passed to Waypoint constructor')
6009    }
6010    if (!options.handler) {
6011      throw new Error('No handler option passed to Waypoint constructor')
6012    }
6013
6014    this.key = 'waypoint-' + keyCounter
6015    this.options = Waypoint.Adapter.extend({}, Waypoint.defaults, options)
6016    this.element = this.options.element
6017    this.adapter = new Waypoint.Adapter(this.element)
6018    this.callback = options.handler
6019    this.axis = this.options.horizontal ? 'horizontal' : 'vertical'
6020    this.enabled = this.options.enabled
6021    this.triggerPoint = null
6022    this.group = Waypoint.Group.findOrCreate({
6023      name: this.options.group,
6024      axis: this.axis
6025    })
6026    this.context = Waypoint.Context.findOrCreateByElement(this.options.context)
6027
6028    if (Waypoint.offsetAliases[this.options.offset]) {
6029      this.options.offset = Waypoint.offsetAliases[this.options.offset]
6030    }
6031    this.group.add(this)
6032    this.context.add(this)
6033    allWaypoints[this.key] = this
6034    keyCounter += 1
6035  }
6036
6037  /* Private */
6038  Waypoint.prototype.queueTrigger = function(direction) {
6039    this.group.queueTrigger(this, direction)
6040  }
6041
6042  /* Private */
6043  Waypoint.prototype.trigger = function(args) {
6044    if (!this.enabled) {
6045      return
6046    }
6047    if (this.callback) {
6048      this.callback.apply(this, args)
6049    }
6050  }
6051
6052  /* Public */
6053  /* http://imakewebthings.com/waypoints/api/destroy */
6054  Waypoint.prototype.destroy = function() {
6055    this.context.remove(this)
6056    this.group.remove(this)
6057    delete allWaypoints[this.key]
6058  }
6059
6060  /* Public */
6061  /* http://imakewebthings.com/waypoints/api/disable */
6062  Waypoint.prototype.disable = function() {
6063    this.enabled = false
6064    return this
6065  }
6066
6067  /* Public */
6068  /* http://imakewebthings.com/waypoints/api/enable */
6069  Waypoint.prototype.enable = function() {
6070    this.context.refresh()
6071    this.enabled = true
6072    return this
6073  }
6074
6075  /* Public */
6076  /* http://imakewebthings.com/waypoints/api/next */
6077  Waypoint.prototype.next = function() {
6078    return this.group.next(this)
6079  }
6080
6081  /* Public */
6082  /* http://imakewebthings.com/waypoints/api/previous */
6083  Waypoint.prototype.previous = function() {
6084    return this.group.previous(this)
6085  }
6086
6087  /* Private */
6088  Waypoint.invokeAll = function(method) {
6089    var allWaypointsArray = []
6090    for (var waypointKey in allWaypoints) {
6091      allWaypointsArray.push(allWaypoints[waypointKey])
6092    }
6093    for (var i = 0, end = allWaypointsArray.length; i < end; i++) {
6094      allWaypointsArray[i][method]()
6095    }
6096  }
6097
6098  /* Public */
6099  /* http://imakewebthings.com/waypoints/api/destroy-all */
6100  Waypoint.destroyAll = function() {
6101    Waypoint.invokeAll('destroy')
6102  }
6103
6104  /* Public */
6105  /* http://imakewebthings.com/waypoints/api/disable-all */
6106  Waypoint.disableAll = function() {
6107    Waypoint.invokeAll('disable')
6108  }
6109
6110  /* Public */
6111  /* http://imakewebthings.com/waypoints/api/enable-all */
6112  Waypoint.enableAll = function() {
6113    Waypoint.Context.refreshAll()
6114    for (var waypointKey in allWaypoints) {
6115      allWaypoints[waypointKey].enabled = true
6116    }
6117    return this
6118  }
6119
6120  /* Public */
6121  /* http://imakewebthings.com/waypoints/api/refresh-all */
6122  Waypoint.refreshAll = function() {
6123    Waypoint.Context.refreshAll()
6124  }
6125
6126  /* Public */
6127  /* http://imakewebthings.com/waypoints/api/viewport-height */
6128  Waypoint.viewportHeight = function() {
6129    return window.innerHeight || document.documentElement.clientHeight
6130  }
6131
6132  /* Public */
6133  /* http://imakewebthings.com/waypoints/api/viewport-width */
6134  Waypoint.viewportWidth = function() {
6135    return document.documentElement.clientWidth
6136  }
6137
6138  Waypoint.adapters = []
6139
6140  Waypoint.defaults = {
6141    context: window,
6142    continuous: true,
6143    enabled: true,
6144    group: 'default',
6145    horizontal: false,
6146    offset: 0
6147  }
6148
6149  Waypoint.offsetAliases = {
6150    'bottom-in-view': function() {
6151      return this.context.innerHeight() - this.adapter.outerHeight()
6152    },
6153    'right-in-view': function() {
6154      return this.context.innerWidth() - this.adapter.outerWidth()
6155    }
6156  }
6157
6158  window.Waypoint = Waypoint
6159}())
6160;(function() {
6161  'use strict'
6162
6163  function requestAnimationFrameShim(callback) {
6164    window.setTimeout(callback, 1000 / 60)
6165  }
6166
6167  var keyCounter = 0
6168  var contexts = {}
6169  var Waypoint = window.Waypoint
6170  var oldWindowLoad = window.onload
6171
6172  /* http://imakewebthings.com/waypoints/api/context */
6173  function Context(element) {
6174    this.element = element
6175    this.Adapter = Waypoint.Adapter
6176    this.adapter = new this.Adapter(element)
6177    this.key = 'waypoint-context-' + keyCounter
6178    this.didScroll = false
6179    this.didResize = false
6180    this.oldScroll = {
6181      x: this.adapter.scrollLeft(),
6182      y: this.adapter.scrollTop()
6183    }
6184    this.waypoints = {
6185      vertical: {},
6186      horizontal: {}
6187    }
6188
6189    element.waypointContextKey = this.key
6190    contexts[element.waypointContextKey] = this
6191    keyCounter += 1
6192    if (!Waypoint.windowContext) {
6193      Waypoint.windowContext = true
6194      Waypoint.windowContext = new Context(window)
6195    }
6196
6197    this.createThrottledScrollHandler()
6198    this.createThrottledResizeHandler()
6199  }
6200
6201  /* Private */
6202  Context.prototype.add = function(waypoint) {
6203    var axis = waypoint.options.horizontal ? 'horizontal' : 'vertical'
6204    this.waypoints[axis][waypoint.key] = waypoint
6205    this.refresh()
6206  }
6207
6208  /* Private */
6209  Context.prototype.checkEmpty = function() {
6210    var horizontalEmpty = this.Adapter.isEmptyObject(this.waypoints.horizontal)
6211    var verticalEmpty = this.Adapter.isEmptyObject(this.waypoints.vertical)
6212    var isWindow = this.element == this.element.window
6213    if (horizontalEmpty && verticalEmpty && !isWindow) {
6214      this.adapter.off('.waypoints')
6215      delete contexts[this.key]
6216    }
6217  }
6218
6219  /* Private */
6220  Context.prototype.createThrottledResizeHandler = function() {
6221    var self = this
6222
6223    function resizeHandler() {
6224      self.handleResize()
6225      self.didResize = false
6226    }
6227
6228    this.adapter.on('resize.waypoints', function() {
6229      if (!self.didResize) {
6230        self.didResize = true
6231        Waypoint.requestAnimationFrame(resizeHandler)
6232      }
6233    })
6234  }
6235
6236  /* Private */
6237  Context.prototype.createThrottledScrollHandler = function() {
6238    var self = this
6239    function scrollHandler() {
6240      self.handleScroll()
6241      self.didScroll = false
6242    }
6243
6244    this.adapter.on('scroll.waypoints', function() {
6245      if (!self.didScroll || Waypoint.isTouch) {
6246        self.didScroll = true
6247        Waypoint.requestAnimationFrame(scrollHandler)
6248      }
6249    })
6250  }
6251
6252  /* Private */
6253  Context.prototype.handleResize = function() {
6254    Waypoint.Context.refreshAll()
6255  }
6256
6257  /* Private */
6258  Context.prototype.handleScroll = function() {
6259    var triggeredGroups = {}
6260    var axes = {
6261      horizontal: {
6262        newScroll: this.adapter.scrollLeft(),
6263        oldScroll: this.oldScroll.x,
6264        forward: 'right',
6265        backward: 'left'
6266      },
6267      vertical: {
6268        newScroll: this.adapter.scrollTop(),
6269        oldScroll: this.oldScroll.y,
6270        forward: 'down',
6271        backward: 'up'
6272      }
6273    }
6274
6275    for (var axisKey in axes) {
6276      var axis = axes[axisKey]
6277      var isForward = axis.newScroll > axis.oldScroll
6278      var direction = isForward ? axis.forward : axis.backward
6279
6280      for (var waypointKey in this.waypoints[axisKey]) {
6281        var waypoint = this.waypoints[axisKey][waypointKey]
6282        if (waypoint.triggerPoint === null) {
6283          continue
6284        }
6285        var wasBeforeTriggerPoint = axis.oldScroll < waypoint.triggerPoint
6286        var nowAfterTriggerPoint = axis.newScroll >= waypoint.triggerPoint
6287        var crossedForward = wasBeforeTriggerPoint && nowAfterTriggerPoint
6288        var crossedBackward = !wasBeforeTriggerPoint && !nowAfterTriggerPoint
6289        if (crossedForward || crossedBackward) {
6290          waypoint.queueTrigger(direction)
6291          triggeredGroups[waypoint.group.id] = waypoint.group
6292        }
6293      }
6294    }
6295
6296    for (var groupKey in triggeredGroups) {
6297      triggeredGroups[groupKey].flushTriggers()
6298    }
6299
6300    this.oldScroll = {
6301      x: axes.horizontal.newScroll,
6302      y: axes.vertical.newScroll
6303    }
6304  }
6305
6306  /* Private */
6307  Context.prototype.innerHeight = function() {
6308    /*eslint-disable eqeqeq */
6309    if (this.element == this.element.window) {
6310      return Waypoint.viewportHeight()
6311    }
6312    /*eslint-enable eqeqeq */
6313    return this.adapter.innerHeight()
6314  }
6315
6316  /* Private */
6317  Context.prototype.remove = function(waypoint) {
6318    delete this.waypoints[waypoint.axis][waypoint.key]
6319    this.checkEmpty()
6320  }
6321
6322  /* Private */
6323  Context.prototype.innerWidth = function() {
6324    /*eslint-disable eqeqeq */
6325    if (this.element == this.element.window) {
6326      return Waypoint.viewportWidth()
6327    }
6328    /*eslint-enable eqeqeq */
6329    return this.adapter.innerWidth()
6330  }
6331
6332  /* Public */
6333  /* http://imakewebthings.com/waypoints/api/context-destroy */
6334  Context.prototype.destroy = function() {
6335    var allWaypoints = []
6336    for (var axis in this.waypoints) {
6337      for (var waypointKey in this.waypoints[axis]) {
6338        allWaypoints.push(this.waypoints[axis][waypointKey])
6339      }
6340    }
6341    for (var i = 0, end = allWaypoints.length; i < end; i++) {
6342      allWaypoints[i].destroy()
6343    }
6344  }
6345
6346  /* Public */
6347  /* http://imakewebthings.com/waypoints/api/context-refresh */
6348  Context.prototype.refresh = function() {
6349    /*eslint-disable eqeqeq */
6350    var isWindow = this.element == this.element.window
6351    /*eslint-enable eqeqeq */
6352    var contextOffset = isWindow ? undefined : this.adapter.offset()
6353    var triggeredGroups = {}
6354    var axes
6355
6356    this.handleScroll()
6357    axes = {
6358      horizontal: {
6359        contextOffset: isWindow ? 0 : contextOffset.left,
6360        contextScroll: isWindow ? 0 : this.oldScroll.x,
6361        contextDimension: this.innerWidth(),
6362        oldScroll: this.oldScroll.x,
6363        forward: 'right',
6364        backward: 'left',
6365        offsetProp: 'left'
6366      },
6367      vertical: {
6368        contextOffset: isWindow ? 0 : contextOffset.top,
6369        contextScroll: isWindow ? 0 : this.oldScroll.y,
6370        contextDimension: this.innerHeight(),
6371        oldScroll: this.oldScroll.y,
6372        forward: 'down',
6373        backward: 'up',
6374        offsetProp: 'top'
6375      }
6376    }
6377
6378    for (var axisKey in axes) {
6379      var axis = axes[axisKey]
6380      for (var waypointKey in this.waypoints[axisKey]) {
6381        var waypoint = this.waypoints[axisKey][waypointKey]
6382        var adjustment = waypoint.options.offset
6383        var oldTriggerPoint = waypoint.triggerPoint
6384        var elementOffset = 0
6385        var freshWaypoint = oldTriggerPoint == null
6386        var contextModifier, wasBeforeScroll, nowAfterScroll
6387        var triggeredBackward, triggeredForward
6388
6389        if (waypoint.element !== waypoint.element.window) {
6390          elementOffset = waypoint.adapter.offset()[axis.offsetProp]
6391        }
6392
6393        if (typeof adjustment === 'function') {
6394          adjustment = adjustment.apply(waypoint)
6395        }
6396        else if (typeof adjustment === 'string') {
6397          adjustment = parseFloat(adjustment)
6398          if (waypoint.options.offset.indexOf('%') > - 1) {
6399            adjustment = Math.ceil(axis.contextDimension * adjustment / 100)
6400          }
6401        }
6402
6403        contextModifier = axis.contextScroll - axis.contextOffset
6404        waypoint.triggerPoint = Math.floor(elementOffset + contextModifier - adjustment)
6405        wasBeforeScroll = oldTriggerPoint < axis.oldScroll
6406        nowAfterScroll = waypoint.triggerPoint >= axis.oldScroll
6407        triggeredBackward = wasBeforeScroll && nowAfterScroll
6408        triggeredForward = !wasBeforeScroll && !nowAfterScroll
6409
6410        if (!freshWaypoint && triggeredBackward) {
6411          waypoint.queueTrigger(axis.backward)
6412          triggeredGroups[waypoint.group.id] = waypoint.group
6413        }
6414        else if (!freshWaypoint && triggeredForward) {
6415          waypoint.queueTrigger(axis.forward)
6416          triggeredGroups[waypoint.group.id] = waypoint.group
6417        }
6418        else if (freshWaypoint && axis.oldScroll >= waypoint.triggerPoint) {
6419          waypoint.queueTrigger(axis.forward)
6420          triggeredGroups[waypoint.group.id] = waypoint.group
6421        }
6422      }
6423    }
6424
6425    Waypoint.requestAnimationFrame(function() {
6426      for (var groupKey in triggeredGroups) {
6427        triggeredGroups[groupKey].flushTriggers()
6428      }
6429    })
6430
6431    return this
6432  }
6433
6434  /* Private */
6435  Context.findOrCreateByElement = function(element) {
6436    return Context.findByElement(element) || new Context(element)
6437  }
6438
6439  /* Private */
6440  Context.refreshAll = function() {
6441    for (var contextId in contexts) {
6442      contexts[contextId].refresh()
6443    }
6444  }
6445
6446  /* Public */
6447  /* http://imakewebthings.com/waypoints/api/context-find-by-element */
6448  Context.findByElement = function(element) {
6449    return contexts[element.waypointContextKey]
6450  }
6451
6452  window.onload = function() {
6453    if (oldWindowLoad) {
6454      oldWindowLoad()
6455    }
6456    Context.refreshAll()
6457  }
6458
6459
6460  Waypoint.requestAnimationFrame = function(callback) {
6461    var requestFn = window.requestAnimationFrame ||
6462      window.mozRequestAnimationFrame ||
6463      window.webkitRequestAnimationFrame ||
6464      requestAnimationFrameShim
6465    requestFn.call(window, callback)
6466  }
6467  Waypoint.Context = Context
6468}())
6469;(function() {
6470  'use strict'
6471
6472  function byTriggerPoint(a, b) {
6473    return a.triggerPoint - b.triggerPoint
6474  }
6475
6476  function byReverseTriggerPoint(a, b) {
6477    return b.triggerPoint - a.triggerPoint
6478  }
6479
6480  var groups = {
6481    vertical: {},
6482    horizontal: {}
6483  }
6484  var Waypoint = window.Waypoint
6485
6486  /* http://imakewebthings.com/waypoints/api/group */
6487  function Group(options) {
6488    this.name = options.name
6489    this.axis = options.axis
6490    this.id = this.name + '-' + this.axis
6491    this.waypoints = []
6492    this.clearTriggerQueues()
6493    groups[this.axis][this.name] = this
6494  }
6495
6496  /* Private */
6497  Group.prototype.add = function(waypoint) {
6498    this.waypoints.push(waypoint)
6499  }
6500
6501  /* Private */
6502  Group.prototype.clearTriggerQueues = function() {
6503    this.triggerQueues = {
6504      up: [],
6505      down: [],
6506      left: [],
6507      right: []
6508    }
6509  }
6510
6511  /* Private */
6512  Group.prototype.flushTriggers = function() {
6513    for (var direction in this.triggerQueues) {
6514      var waypoints = this.triggerQueues[direction]
6515      var reverse = direction === 'up' || direction === 'left'
6516      waypoints.sort(reverse ? byReverseTriggerPoint : byTriggerPoint)
6517      for (var i = 0, end = waypoints.length; i < end; i += 1) {
6518        var waypoint = waypoints[i]
6519        if (waypoint.options.continuous || i === waypoints.length - 1) {
6520          waypoint.trigger([direction])
6521        }
6522      }
6523    }
6524    this.clearTriggerQueues()
6525  }
6526
6527  /* Private */
6528  Group.prototype.next = function(waypoint) {
6529    this.waypoints.sort(byTriggerPoint)
6530    var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
6531    var isLast = index === this.waypoints.length - 1
6532    return isLast ? null : this.waypoints[index + 1]
6533  }
6534
6535  /* Private */
6536  Group.prototype.previous = function(waypoint) {
6537    this.waypoints.sort(byTriggerPoint)
6538    var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
6539    return index ? this.waypoints[index - 1] : null
6540  }
6541
6542  /* Private */
6543  Group.prototype.queueTrigger = function(waypoint, direction) {
6544    this.triggerQueues[direction].push(waypoint)
6545  }
6546
6547  /* Private */
6548  Group.prototype.remove = function(waypoint) {
6549    var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
6550    if (index > -1) {
6551      this.waypoints.splice(index, 1)
6552    }
6553  }
6554
6555  /* Public */
6556  /* http://imakewebthings.com/waypoints/api/first */
6557  Group.prototype.first = function() {
6558    return this.waypoints[0]
6559  }
6560
6561  /* Public */
6562  /* http://imakewebthings.com/waypoints/api/last */
6563  Group.prototype.last = function() {
6564    return this.waypoints[this.waypoints.length - 1]
6565  }
6566
6567  /* Private */
6568  Group.findOrCreate = function(options) {
6569    return groups[options.axis][options.name] || new Group(options)
6570  }
6571
6572  Waypoint.Group = Group
6573}())
6574;(function() {
6575  'use strict'
6576
6577  var $ = window.jQuery
6578  var Waypoint = window.Waypoint
6579
6580  function JQueryAdapter(element) {
6581    this.$element = $(element)
6582  }
6583
6584  $.each([
6585    'innerHeight',
6586    'innerWidth',
6587    'off',
6588    'offset',
6589    'on',
6590    'outerHeight',
6591    'outerWidth',
6592    'scrollLeft',
6593    'scrollTop'
6594  ], function(i, method) {
6595    JQueryAdapter.prototype[method] = function() {
6596      var args = Array.prototype.slice.call(arguments)
6597      return this.$element[method].apply(this.$element, args)
6598    }
6599  })
6600
6601  $.each([
6602    'extend',
6603    'inArray',
6604    'isEmptyObject'
6605  ], function(i, method) {
6606    JQueryAdapter[method] = $[method]
6607  })
6608
6609  Waypoint.adapters.push({
6610    name: 'jquery',
6611    Adapter: JQueryAdapter
6612  })
6613  Waypoint.Adapter = JQueryAdapter
6614}())
6615;(function() {
6616  'use strict'
6617
6618  var Waypoint = window.Waypoint
6619
6620  function createExtension(framework) {
6621    return function() {
6622      var waypoints = []
6623      var overrides = arguments[0]
6624
6625      if (framework.isFunction(arguments[0])) {
6626        overrides = framework.extend({}, arguments[1])
6627        overrides.handler = arguments[0]
6628      }
6629
6630      this.each(function() {
6631        var options = framework.extend({}, overrides, {
6632          element: this
6633        })
6634        if (typeof options.context === 'string') {
6635          options.context = framework(this).closest(options.context)[0]
6636        }
6637        waypoints.push(new Waypoint(options))
6638      })
6639
6640      return waypoints
6641    }
6642  }
6643
6644  if (window.jQuery) {
6645    window.jQuery.fn.waypoint = createExtension(window.jQuery)
6646  }
6647  if (window.Zepto) {
6648    window.Zepto.fn.waypoint = createExtension(window.Zepto)
6649  }
6650}())
6651;
6652/*!
6653Waypoints Inview Shortcut - 4.0.1
6654Copyright © 2011-2016 Caleb Troughton
6655Licensed under the MIT license.
6656https://github.com/imakewebthings/waypoints/blob/master/licenses.txt
6657*/
6658(function() {
6659  'use strict'
6660
6661  function noop() {}
6662
6663  var Waypoint = window.Waypoint
6664
6665  /* http://imakewebthings.com/waypoints/shortcuts/inview */
6666  function Inview(options) {
6667    this.options = Waypoint.Adapter.extend({}, Inview.defaults, options)
6668    this.axis = this.options.horizontal ? 'horizontal' : 'vertical'
6669    this.waypoints = []
6670    this.element = this.options.element
6671    this.createWaypoints()
6672  }
6673
6674  /* Private */
6675  Inview.prototype.createWaypoints = function() {
6676    var configs = {
6677      vertical: [{
6678        down: 'enter',
6679        up: 'exited',
6680        offset: '100%'
6681      }, {
6682        down: 'entered',
6683        up: 'exit',
6684        offset: 'bottom-in-view'
6685      }, {
6686        down: 'exit',
6687        up: 'entered',
6688        offset: 0
6689      }, {
6690        down: 'exited',
6691        up: 'enter',
6692        offset: function() {
6693          return -this.adapter.outerHeight()
6694        }
6695      }],
6696      horizontal: [{
6697        right: 'enter',
6698        left: 'exited',
6699        offset: '100%'
6700      }, {
6701        right: 'entered',
6702        left: 'exit',
6703        offset: 'right-in-view'
6704      }, {
6705        right: 'exit',
6706        left: 'entered',
6707        offset: 0
6708      }, {
6709        right: 'exited',
6710        left: 'enter',
6711        offset: function() {
6712          return -this.adapter.outerWidth()
6713        }
6714      }]
6715    }
6716
6717    for (var i = 0, end = configs[this.axis].length; i < end; i++) {
6718      var config = configs[this.axis][i]
6719      this.createWaypoint(config)
6720    }
6721  }
6722
6723  /* Private */
6724  Inview.prototype.createWaypoint = function(config) {
6725    var self = this
6726    this.waypoints.push(new Waypoint({
6727      context: this.options.context,
6728      element: this.options.element,
6729      enabled: this.options.enabled,
6730      handler: (function(config) {
6731        return function(direction) {
6732          self.options[config[direction]].call(self, direction)
6733        }
6734      }(config)),
6735      offset: config.offset,
6736      horizontal: this.options.horizontal
6737    }))
6738  }
6739
6740  /* Public */
6741  Inview.prototype.destroy = function() {
6742    for (var i = 0, end = this.waypoints.length; i < end; i++) {
6743      this.waypoints[i].destroy()
6744    }
6745    this.waypoints = []
6746  }
6747
6748  Inview.prototype.disable = function() {
6749    for (var i = 0, end = this.waypoints.length; i < end; i++) {
6750      this.waypoints[i].disable()
6751    }
6752  }
6753
6754  Inview.prototype.enable = function() {
6755    for (var i = 0, end = this.waypoints.length; i < end; i++) {
6756      this.waypoints[i].enable()
6757    }
6758  }
6759
6760  Inview.defaults = {
6761    context: window,
6762    enabled: true,
6763    enter: noop,
6764    entered: noop,
6765    exit: noop,
6766    exited: noop
6767  }
6768
6769  Waypoint.Inview = Inview
6770}())
6771;
6772
6773/*
6774 * SmartMenus jQuery v0.9.6
6775 * http://www.smartmenus.org/
6776 *
6777 * Copyright 2014 Vasil Dinkov, Vadikom Web Ltd.
6778 * http://vadikom.com/
6779 *
6780 * Released under the MIT license:
6781 * http://www.opensource.org/licenses/MIT
6782 */
6783
6784(function($) {
6785
6786	var menuTrees = [],
6787		IE = !!window.createPopup, // we need to detect it, unfortunately
6788		IElt9 = IE && !document.defaultView,
6789		IElt8 = IE && !document.querySelector,
6790		IE6 = IE && typeof document.documentElement.currentStyle.minWidth == 'undefined',
6791		mouse = false, // optimize for touch by default - we will detect for mouse input
6792		mouseDetectionEnabled = false;
6793
6794	// Handle detection for mouse input (i.e. desktop browsers, tablets with a mouse, etc.)
6795	function initMouseDetection(disable) {
6796		if (!mouseDetectionEnabled && !disable) {
6797			// if we get two consecutive mousemoves within 2 pixels from each other and within 300ms, we assume a real mouse/cursor is present
6798			// in practice, this seems like impossible to trick unintentianally with a real mouse and a pretty safe detection on touch devices (even with older browsers that do not support touch events)
6799			var firstTime = true,
6800				lastMove = null;
6801			$(document).bind({
6802				'mousemove.smartmenus_mouse': function(e) {
6803					var thisMove = { x: e.pageX, y: e.pageY, timeStamp: new Date().getTime() };
6804					if (lastMove) {
6805						var deltaX = Math.abs(lastMove.x - thisMove.x),
6806							deltaY = Math.abs(lastMove.y - thisMove.y);
6807	 					if ((deltaX > 0 || deltaY > 0) && deltaX <= 2 && deltaY <= 2 && thisMove.timeStamp - lastMove.timeStamp <= 300) {
6808							mouse = true;
6809							// if this is the first check after page load, check if we are not over some item by chance and call the mouseenter handler if yes
6810							if (firstTime) {
6811								var $a = $(e.target).closest('a');
6812								if ($a.is('a')) {
6813									$.each(menuTrees, function() {
6814										if ($.contains(this.$root[0], $a[0])) {
6815											this.itemEnter({ currentTarget: $a[0] });
6816											return false;
6817										}
6818									});
6819								}
6820								firstTime = false;
6821							}
6822						}
6823					}
6824					lastMove = thisMove;
6825				},
6826				'touchstart.smartmenus_mouse pointerover.smartmenus_mouse MSPointerOver.smartmenus_mouse': function(e) {
6827					if (!/^(4|mouse)$/.test(e.originalEvent.pointerType)) {
6828						mouse = false;
6829					}
6830				}
6831			});
6832			mouseDetectionEnabled = true;
6833		} else if (mouseDetectionEnabled && disable) {
6834			$(document).unbind('.smartmenus_mouse');
6835			mouseDetectionEnabled = false;
6836		}
6837	};
6838
6839	$.SmartMenus = function(elm, options) {
6840		this.$root = $(elm);
6841		this.opts = options;
6842		this.rootId = ''; // internal
6843		this.$subArrow = null;
6844		this.subMenus = []; // all sub menus in the tree (UL elms) in no particular order (only real - e.g. UL's in mega sub menus won't be counted)
6845		this.activatedItems = []; // stores last activated A's for each level
6846		this.visibleSubMenus = []; // stores visible sub menus UL's
6847		this.showTimeout = 0;
6848		this.hideTimeout = 0;
6849		this.scrollTimeout = 0;
6850		this.clickActivated = false;
6851		this.zIndexInc = 0;
6852		this.$firstLink = null; // we'll use these for some tests
6853		this.$firstSub = null; // at runtime so we'll cache them
6854		this.disabled = false;
6855		this.$disableOverlay = null;
6856		this.init();
6857	};
6858
6859	$.extend($.SmartMenus, {
6860		hideAll: function() {
6861			$.each(menuTrees, function() {
6862				this.menuHideAll();
6863			});
6864		},
6865		destroy: function() {
6866			while (menuTrees.length) {
6867				menuTrees[0].destroy();
6868			}
6869			initMouseDetection(true);
6870		},
6871		prototype: {
6872			init: function(refresh) {
6873				var self = this;
6874
6875				if (!refresh) {
6876					menuTrees.push(this);
6877
6878					this.rootId = (new Date().getTime() + Math.random() + '').replace(/\D/g, '');
6879
6880					if (this.$root.hasClass('sm-rtl')) {
6881						this.opts.rightToLeftSubMenus = true;
6882					}
6883
6884					// init root (main menu)
6885					this.$root
6886						.data('smartmenus', this)
6887						.attr('data-smartmenus-id', this.rootId)
6888						.dataSM('level', 1)
6889						.bind({
6890							'mouseover.smartmenus focusin.smartmenus': $.proxy(this.rootOver, this),
6891							'mouseout.smartmenus focusout.smartmenus': $.proxy(this.rootOut, this)
6892						})
6893						.delegate('a', {
6894							'mouseenter.smartmenus': $.proxy(this.itemEnter, this),
6895							'mouseleave.smartmenus': $.proxy(this.itemLeave, this),
6896							'mousedown.smartmenus': $.proxy(this.itemDown, this),
6897							'focus.smartmenus': $.proxy(this.itemFocus, this),
6898							'blur.smartmenus': $.proxy(this.itemBlur, this),
6899							'click.smartmenus': $.proxy(this.itemClick, this),
6900							'touchend.smartmenus': $.proxy(this.itemTouchEnd, this)
6901						});
6902
6903					var eNamespace = '.smartmenus' + this.rootId;
6904					// hide menus on tap or click outside the root UL
6905					if (this.opts.hideOnClick) {
6906						$(document).bind('touchstart' + eNamespace, $.proxy(this.docTouchStart, this))
6907							.bind('touchmove' + eNamespace, $.proxy(this.docTouchMove, this))
6908							.bind('touchend' + eNamespace, $.proxy(this.docTouchEnd, this))
6909							// for Opera Mobile < 11.5, webOS browser, etc. we'll check click too
6910							.bind('click' + eNamespace, $.proxy(this.docClick, this));
6911					}
6912					// hide sub menus on resize
6913					$(window).bind('resize' + eNamespace + ' orientationchange' + eNamespace, $.proxy(this.winResize, this));
6914					$(window).bind('scroll' + eNamespace + ' orientationchange' + eNamespace, $.proxy(this.winResize, this));
6915
6916					if (this.opts.subIndicators) {
6917						this.$subArrow = $('<span/>').addClass('sub-arrow');
6918						if (this.opts.subIndicatorsText) {
6919							this.$subArrow.html(this.opts.subIndicatorsText);
6920						}
6921					}
6922
6923					// make sure mouse detection is enabled
6924					initMouseDetection();
6925				}
6926
6927				// init sub menus
6928				this.$firstSub = this.$root.find('ul').each(function() { self.menuInit($(this)); }).eq(0);
6929
6930				this.$firstLink = this.$root.find('a').eq(0);
6931
6932				// find current item
6933				if (this.opts.markCurrentItem) {
6934					var reDefaultDoc = /(index|default)\.[^#\?\/]*/i,
6935						reHash = /#.*/,
6936						locHref = window.location.href.replace(reDefaultDoc, ''),
6937						locHrefNoHash = locHref.replace(reHash, '');
6938					this.$root.find('a').each(function() {
6939						var href = this.href.replace(reDefaultDoc, ''),
6940							$this = $(this);
6941						if (href == locHref || href == locHrefNoHash) {
6942							$this.addClass('current');
6943							if (self.opts.markCurrentTree) {
6944								$this.parents('li').each(function() {
6945									var $this = $(this);
6946									if ($this.dataSM('sub')) {
6947										$this.children('a').addClass('current');
6948									}
6949								});
6950							}
6951						}
6952					});
6953				}
6954			},
6955			destroy: function() {
6956				this.menuHideAll();
6957				this.$root
6958					.removeData('smartmenus')
6959					.removeAttr('data-smartmenus-id')
6960					.removeDataSM('level')
6961					.unbind('.smartmenus')
6962					.undelegate('.smartmenus');
6963				var eNamespace = '.smartmenus' + this.rootId;
6964				$(document).unbind(eNamespace);
6965				$(window).unbind(eNamespace);
6966				if (this.opts.subIndicators) {
6967					this.$subArrow = null;
6968				}
6969				var self = this;
6970				$.each(this.subMenus, function() {
6971					if (this.hasClass('mega-menu')) {
6972						this.find('ul').removeDataSM('in-mega');
6973					}
6974					if (this.dataSM('shown-before')) {
6975						if (IElt8) {
6976							this.children().css({ styleFloat: '', width: '' });
6977						}
6978						if (self.opts.subMenusMinWidth || self.opts.subMenusMaxWidth) {
6979							if (!IE6) {
6980								this.css({ width: '', minWidth: '', maxWidth: '' }).removeClass('sm-nowrap');
6981							} else {
6982								this.css({ width: '', overflowX: '', overflowY: '' }).children().children('a').css('white-space', '');
6983							}
6984						}
6985						if (this.dataSM('scroll-arrows')) {
6986							this.dataSM('scroll-arrows').remove();
6987						}
6988						this.css({ zIndex: '', top: '', left: '', marginLeft: '', marginTop: '', display: '' });
6989					}
6990					if (self.opts.subIndicators) {
6991						this.dataSM('parent-a').removeClass('has-submenu').children('span.sub-arrow').remove();
6992					}
6993					this.removeDataSM('shown-before')
6994						.removeDataSM('ie-shim')
6995						.removeDataSM('scroll-arrows')
6996						.removeDataSM('parent-a')
6997						.removeDataSM('level')
6998						.removeDataSM('beforefirstshowfired')
6999						.parent().removeDataSM('sub');
7000				});
7001				if (this.opts.markCurrentItem) {
7002					this.$root.find('a.current').removeClass('current');
7003				}
7004				this.$root = null;
7005				this.$firstLink = null;
7006				this.$firstSub = null;
7007				if (this.$disableOverlay) {
7008					this.$disableOverlay.remove();
7009					this.$disableOverlay = null;
7010				}
7011				menuTrees.splice($.inArray(this, menuTrees), 1);
7012			},
7013			disable: function(noOverlay) {
7014				if (!this.disabled) {
7015					this.menuHideAll();
7016					// display overlay over the menu to prevent interaction
7017					if (!noOverlay && !this.opts.isPopup && this.$root.is(':visible')) {
7018						var pos = this.$root.offset();
7019						this.$disableOverlay = $('<div class="sm-jquery-disable-overlay"/>').css({
7020							position: 'absolute',
7021							top: pos.top,
7022							left: pos.left,
7023							width: this.$root.outerWidth(),
7024							height: this.$root.outerHeight(),
7025							zIndex: this.getStartZIndex() + 1,
7026							opacity: 0
7027						}).appendTo(document.body);
7028					}
7029					this.disabled = true;
7030				}
7031			},
7032			docClick: function(e) {
7033				// hide on any click outside the menu or on a menu link
7034				if (this.visibleSubMenus.length && !$.contains(this.$root[0], e.target) || $(e.target).is('a')) {
7035					this.menuHideAll($(e.target));
7036				}
7037			},
7038			docTouchEnd: function(e) {
7039				if (!this.lastTouch) {
7040					return;
7041				}
7042				if (this.visibleSubMenus.length && (this.lastTouch.x2 === undefined || this.lastTouch.x1 == this.lastTouch.x2) && (this.lastTouch.y2 === undefined || this.lastTouch.y1 == this.lastTouch.y2) && (!this.lastTouch.target || !$.contains(this.$root[0], this.lastTouch.target))) {
7043					if (this.hideTimeout) {
7044						clearTimeout(this.hideTimeout);
7045						this.hideTimeout = 0;
7046					}
7047					// hide with a delay to prevent triggering accidental unwanted click on some page element
7048					var self = this;
7049					this.hideTimeout = setTimeout(function() { self.menuHideAll($(e.target)); }, 350);
7050				}
7051				this.lastTouch = null;
7052			},
7053			docTouchMove: function(e) {
7054				if (!this.lastTouch) {
7055					return;
7056				}
7057				var touchPoint = e.originalEvent.touches[0];
7058				this.lastTouch.x2 = touchPoint.pageX;
7059				this.lastTouch.y2 = touchPoint.pageY;
7060			},
7061			docTouchStart: function(e) {
7062				var touchPoint = e.originalEvent.touches[0];
7063				this.lastTouch = { x1: touchPoint.pageX, y1: touchPoint.pageY, target: touchPoint.target };
7064			},
7065			enable: function() {
7066				if (this.disabled) {
7067					if (this.$disableOverlay) {
7068						this.$disableOverlay.remove();
7069						this.$disableOverlay = null;
7070					}
7071					this.disabled = false;
7072				}
7073			},
7074			getHeight: function($elm) {
7075				return this.getOffset($elm, true);
7076			},
7077			// returns precise width/height float values in IE9+, FF4+, recent WebKit
7078			// http://vadikom.com/dailies/offsetwidth-offsetheight-useless-in-ie9-firefox4/
7079			getOffset: function($elm, height) {
7080				var old;
7081				if ($elm.css('display') == 'none') {
7082					old = { position: $elm[0].style.position, visibility: $elm[0].style.visibility };
7083					$elm.css({ position: 'absolute', visibility: 'hidden' }).show();
7084				}
7085				var defaultView = $elm[0].ownerDocument.defaultView,
7086					compStyle = defaultView && defaultView.getComputedStyle && defaultView.getComputedStyle($elm[0], null),
7087					val = compStyle && parseFloat(compStyle[height ? 'height' : 'width']);
7088				if (val) {
7089					val += parseFloat(compStyle[height ? 'paddingTop' : 'paddingLeft'])
7090	 					+ parseFloat(compStyle[height ? 'paddingBottom' : 'paddingRight'])
7091	 					+ parseInt(compStyle[height ? 'borderTopWidth' : 'borderLeftWidth'])
7092	 					+ parseInt(compStyle[height ? 'borderBottomWidth' : 'borderRightWidth']);
7093				} else {
7094					val = height ? $elm[0].offsetHeight : $elm[0].offsetWidth;
7095				}
7096				if (old) {
7097					$elm.hide().css(old);
7098				}
7099				return val;
7100			},
7101			getWidth: function($elm) {
7102				return this.getOffset($elm);
7103			},
7104			getStartZIndex: function() {
7105				var zIndex = parseInt(this.$root.css('z-index'));
7106				return !isNaN(zIndex) ? zIndex : 1;
7107			},
7108			handleEvents: function() {
7109				return !this.disabled && this.isCSSOn();
7110			},
7111			handleItemEvents: function($a) {
7112				return this.handleEvents() && !this.isLinkInMegaMenu($a);
7113			},
7114			isCollapsible: function() {
7115				return this.$firstSub.css('position') == 'static';
7116			},
7117			isCSSOn: function() {
7118				return this.$firstLink.css('display') == 'block' || this.$firstLink.css('display') == 'inline';
7119			},
7120			isFixed: function() {
7121				return this.$root.css('position') == 'fixed';
7122			},
7123			isLinkInMegaMenu: function($a) {
7124				return !$a.parent().parent().dataSM('level');
7125			},
7126			isTouchMode: function() {
7127				return !mouse || this.isCollapsible();
7128			},
7129			itemActivate: function($a) {
7130				var $li = $a.parent(),
7131					$ul = $li.parent(),
7132					level = $ul.dataSM('level');
7133				// if for some reason the parent item is not activated (e.g. this is an API call to activate the item), activate all parent items first
7134				if (level > 1 && (!this.activatedItems[level - 2] || this.activatedItems[level - 2][0] != $ul.dataSM('parent-a')[0])) {
7135					var self = this;
7136					$($ul.parentsUntil('[data-smartmenus-id]', 'ul').get().reverse()).add($ul).each(function() {
7137						self.itemActivate($(this).dataSM('parent-a'));
7138					});
7139				}
7140				// hide any visible deeper level sub menus
7141				if (this.visibleSubMenus.length > level) {
7142					for (var i = this.visibleSubMenus.length - 1, l = !this.activatedItems[level - 1] || this.activatedItems[level - 1][0] != $a[0] ? level - 1 : level; i > l; i--) {
7143						this.menuHide(this.visibleSubMenus[i]);
7144					}
7145				}
7146				// save new active item and sub menu for this level
7147				this.activatedItems[level - 1] = $a;
7148				this.visibleSubMenus[level - 1] = $ul;
7149				if (this.$root.triggerHandler('activate.smapi', $a[0]) === false) {
7150					return;
7151				}
7152				// show the sub menu if this item has one
7153				var $sub = $li.dataSM('sub');
7154				if ($sub && (this.isTouchMode() || (!this.opts.showOnClick || this.clickActivated))) {
7155					this.menuShow($sub);
7156				}
7157			},
7158			itemBlur: function(e) {
7159				var $a = $(e.currentTarget);
7160				if (!this.handleItemEvents($a)) {
7161					return;
7162				}
7163				this.$root.triggerHandler('blur.smapi', $a[0]);
7164			},
7165			itemClick: function(e) {
7166				var $a = $(e.currentTarget);
7167
7168				if (!this.handleItemEvents($a)) {
7169					return;
7170				}
7171				$a.removeDataSM('mousedown');
7172				if (this.$root.triggerHandler('click.smapi', $a[0]) === false) {
7173					return false;
7174				}
7175				var $sub = $a.parent().dataSM('sub');
7176				if (this.isTouchMode()) {
7177					// undo fix: prevent the address bar on iPhone from sliding down when expanding a sub menu
7178					if ($a.dataSM('href')) {
7179						$a.attr('href', $a.dataSM('href')).removeDataSM('href');
7180					}
7181					// if the sub is not visible
7182					if ($sub && (!$sub.dataSM('shown-before') || !$sub.is(':visible'))) {
7183						// try to activate the item and show the sub
7184						this.itemActivate($a);
7185						// if "itemActivate" showed the sub, prevent the click so that the link is not loaded
7186						// if it couldn't show it, then the sub menus are disabled with an !important declaration (e.g. via mobile styles) so let the link get loaded
7187						if ($sub.is(':visible')) {
7188							return false;
7189						}
7190					}
7191				} else if (this.opts.showOnClick && $a.parent().parent().dataSM('level') == 1 && $sub) {
7192					this.clickActivated = true;
7193					this.menuShow($sub);
7194					return false;
7195				}
7196				if ($a.hasClass('disabled')) {
7197					return false;
7198				}
7199				if (this.$root.triggerHandler('select.smapi', $a[0]) === false) {
7200					return false;
7201				}
7202			},
7203			itemDown: function(e) {
7204				var $a = $(e.currentTarget);
7205				if (!this.handleItemEvents($a)) {
7206					return;
7207				}
7208				$a.dataSM('mousedown', true);
7209			},
7210			itemEnter: function(e) {
7211				var $a = $(e.currentTarget);
7212				if (!this.handleItemEvents($a)) {
7213					return;
7214				}
7215				if (!this.isTouchMode()) {
7216					if (this.showTimeout) {
7217						clearTimeout(this.showTimeout);
7218						this.showTimeout = 0;
7219					}
7220					var self = this;
7221					this.showTimeout = setTimeout(function() { self.itemActivate($a); }, this.opts.showOnClick && $a.parent().parent().dataSM('level') == 1 ? 1 : this.opts.showTimeout);
7222				}
7223				this.$root.triggerHandler('mouseenter.smapi', $a[0]);
7224			},
7225			itemFocus: function(e) {
7226				var $a = $(e.currentTarget);
7227				if (!this.handleItemEvents($a)) {
7228					return;
7229				}
7230				// fix (the mousedown check): in some browsers a tap/click produces consecutive focus + click events so we don't need to activate the item on focus
7231				if ((!this.isTouchMode() || !$a.dataSM('mousedown')) && (!this.activatedItems.length || this.activatedItems[this.activatedItems.length - 1][0] != $a[0])) {
7232					this.itemActivate($a);
7233				}
7234				this.$root.triggerHandler('focus.smapi', $a[0]);
7235			},
7236			itemLeave: function(e) {
7237				var $a = $(e.currentTarget);
7238				if (!this.handleItemEvents($a)) {
7239					return;
7240				}
7241				if (!this.isTouchMode()) {
7242					if ($a[0].blur) {
7243						$a[0].blur();
7244					}
7245					if (this.showTimeout) {
7246						clearTimeout(this.showTimeout);
7247						this.showTimeout = 0;
7248					}
7249				}
7250				$a.removeDataSM('mousedown');
7251				this.$root.triggerHandler('mouseleave.smapi', $a[0]);
7252			},
7253			itemTouchEnd: function(e) {
7254				var $a = $(e.currentTarget);
7255				if (!this.handleItemEvents($a)) {
7256					return;
7257				}
7258				// prevent the address bar on iPhone from sliding down when expanding a sub menu
7259				var $sub = $a.parent().dataSM('sub');
7260				if ($a.attr('href').charAt(0) !== '#' && $sub && (!$sub.dataSM('shown-before') || !$sub.is(':visible'))) {
7261					$a.dataSM('href', $a.attr('href'));
7262					$a.attr('href', '#');
7263				}
7264			},
7265			menuFixLayout: function($ul) {
7266				// fixes a menu that is being shown for the first time
7267				if (!$ul.dataSM('shown-before')) {
7268					$ul.hide().dataSM('shown-before', true);
7269					// fix the layout of the items in IE<8
7270					if (IElt8) {
7271						$ul.children().css({ styleFloat: 'left', width: '100%' });
7272					}
7273				}
7274			},
7275			menuHide: function($sub) {
7276				if (this.$root.triggerHandler('beforehide.smapi', $sub[0]) === false) {
7277					return;
7278				}
7279				$sub.stop(true, true);
7280				if ($sub.is(':visible')) {
7281					var complete = function() {
7282						// unset z-index
7283						if (IElt9) {
7284							$sub.parent().css('z-index', '');
7285						} else {
7286							$sub.css('z-index', '');
7287						}
7288					};
7289					// if sub is collapsible (mobile view)
7290					if (this.isCollapsible()) {
7291						if (this.opts.collapsibleHideFunction) {
7292							this.opts.collapsibleHideFunction.call(this, $sub, complete);
7293						} else {
7294							$sub.hide(this.opts.collapsibleHideDuration, complete);
7295						}
7296					} else {
7297						if (this.opts.hideFunction) {
7298							this.opts.hideFunction.call(this, $sub, complete);
7299						} else {
7300							$sub.hide(this.opts.hideDuration, complete);
7301						}
7302					}
7303					// remove IE iframe shim
7304					if ($sub.dataSM('ie-shim')) {
7305						$sub.dataSM('ie-shim').remove();
7306					}
7307					// deactivate scrolling if it is activated for this sub
7308					if ($sub.dataSM('scroll')) {
7309						$sub.unbind('.smartmenus_scroll').removeDataSM('scroll').dataSM('scroll-arrows').hide();
7310					}
7311					// unhighlight parent item
7312					$sub.dataSM('parent-a').removeClass('highlighted');
7313					var level = $sub.dataSM('level');
7314					this.activatedItems.splice(level - 1, 1);
7315					this.visibleSubMenus.splice(level - 1, 1);
7316					this.$root.triggerHandler('hide.smapi', $sub[0]);
7317				}
7318			},
7319			menuHideAll: function($item) {
7320				if ($item != undefined && $item.parent().hasClass('menu-item') && !$item.parent().hasClass('menu-item-has-children')) return;
7321				if ( $('body').hasClass('vmenu') ) return;
7322				if (this.showTimeout) {
7323					clearTimeout(this.showTimeout);
7324					this.showTimeout = 0;
7325				}
7326				// hide all subs
7327				for (var i = this.visibleSubMenus.length - 1; i > 0; i--) {
7328					this.menuHide(this.visibleSubMenus[i]);
7329				}
7330				// hide root if it's popup
7331				if (this.opts.isPopup) {
7332					this.$root.stop(true, true);
7333					if (this.$root.is(':visible')) {
7334						if (this.opts.hideFunction) {
7335							this.opts.hideFunction.call(this, this.$root);
7336						} else {
7337							this.$root.hide(this.opts.hideDuration);
7338						}
7339						// remove IE iframe shim
7340						if (this.$root.dataSM('ie-shim')) {
7341							this.$root.dataSM('ie-shim').remove();
7342						}
7343					}
7344				}
7345				this.activatedItems = [];
7346				this.visibleSubMenus = [];
7347				this.clickActivated = false;
7348				// reset z-index increment
7349				this.zIndexInc = 0;
7350			},
7351			menuIframeShim: function($ul) {
7352				// create iframe shim for the menu
7353				if (IE && this.opts.overlapControlsInIE && !$ul.dataSM('ie-shim')) {
7354					$ul.dataSM('ie-shim', $('<iframe/>').attr({ src: 'javascript:0', tabindex: -9 })
7355						.css({ position: 'absolute', top: 'auto', left: '0', opacity: 0, border: '0' })
7356					);
7357				}
7358			},
7359			menuInit: function($ul) {
7360				if (!$ul.dataSM('in-mega')) {
7361					this.subMenus.push($ul);
7362					// mark UL's in mega drop downs (if any) so we can neglect them
7363					if ($ul.hasClass('mega-menu')) {
7364						$ul.find('ul').dataSM('in-mega', true);
7365					}
7366					// get level (much faster than, for example, using parentsUntil)
7367					var level = 2,
7368						par = $ul[0];
7369					while ((par = par.parentNode.parentNode) != this.$root[0]) {
7370						level++;
7371					}
7372					// cache stuff
7373					$ul.dataSM('parent-a', $ul.prevAll('a').eq(-1))
7374						.dataSM('level', level)
7375						.parent().dataSM('sub', $ul);
7376					// add sub indicator to parent item
7377					if (this.opts.subIndicators) {
7378						$ul.dataSM('parent-a').addClass('has-submenu')[this.opts.subIndicatorsPos](this.$subArrow.clone());
7379					}
7380				}
7381			},
7382			menuPosition: function($sub) {
7383				var fixIE = $('html.ie').length;
7384				// if (fixIE) {
7385				// 	var $rowParent = $($sub).closest('.main-menu-container');
7386				// 	$rowParent.height($rowParent.height());
7387				// }
7388				var $a = $sub.dataSM('parent-a'),
7389					$li = $sub.parent(),
7390					$ul = $sub.parent().parent(),
7391					$container = $ul.closest('.row-menu-inner').length ? $ul.closest('.row-menu-inner') : $ul.closest('.uncol'),
7392					level = $sub.dataSM('level'),
7393					subW = this.getWidth($sub),
7394					subH = this.getHeight($sub),
7395					itemOffset = $a.offset(),
7396					itemX = itemOffset.left,
7397					itemY = itemOffset.top,
7398					itemW = this.getWidth($a),
7399					itemH = this.getHeight($a),
7400					$win = $(window),
7401					winX = $win.scrollLeft(),
7402					winY = $win.scrollTop(),
7403					winW = $win.width(),
7404					winH = $win.height(),
7405					containerW = $container.width(),
7406					containerOffsetX = containerW + ((winW - containerW) / 2),
7407					horizontalParent = $ul.hasClass('sm') && !$ul.hasClass('sm-vertical'),
7408					subOffsetX = level == 2 ? this.opts.mainMenuSubOffsetX : this.opts.subMenusSubOffsetX,
7409					subOffsetY = level == 2 ? this.opts.mainMenuSubOffsetY : this.opts.subMenusSubOffsetY,
7410					x, y, leftPos;
7411				if (horizontalParent) {
7412					x = this.opts.rightToLeftSubMenus ? itemW - subW - subOffsetX : subOffsetX;
7413					y = this.opts.bottomToTopSubMenus ? -subH - subOffsetY : itemH + subOffsetY;
7414				} else {
7415					x = this.opts.rightToLeftSubMenus ? subOffsetX - subW : itemW - subOffsetX;
7416					y = this.opts.bottomToTopSubMenus ? itemH - subOffsetY - subH : subOffsetY;
7417				}
7418				if (this.opts.keepInViewport && !this.isCollapsible()) {
7419					if (this.isFixed()) {
7420						itemX -= winX;
7421						itemY -= winY;
7422						winX = winY = 0;
7423					}
7424					var absX = itemX + x,
7425						absY = itemY + y;
7426					if (this.opts.rightToLeftSubMenus && absX < winX) {
7427						x = horizontalParent ? winX - absX + x : itemW - subOffsetX;
7428					} else if (!this.opts.rightToLeftSubMenus && absX + subW > winX + containerOffsetX ) {
7429						x = horizontalParent ? winX + containerOffsetX - subW - absX + x : subOffsetX - subW;
7430					}
7431					if (!horizontalParent) {
7432						if (subH < winH && absY + subH > winY + winH) {
7433							y += winY + winH - subH - absY;
7434						} else if (subH >= winH || absY < winY) {
7435							y += winY - absY;
7436						}
7437					}
7438					// do we need scrolling?
7439					// 0.49 added for the sake of IE9/FF4+ where we might be dealing with float numbers for "subH"
7440					if (mouse && (horizontalParent && (absY + subH > winY + winH + 0.49 || absY < winY) || !horizontalParent && subH > winH + 0.49)) {
7441						var self = this;
7442						if (!$sub.dataSM('scroll-arrows')) {
7443							$sub.dataSM('scroll-arrows', $([$('<span class="scroll-up"><span class="scroll-up-arrow"></span></span>')[0], $('<span class="scroll-down"><span class="scroll-down-arrow"></span></span>')[0]])
7444								.bind({
7445									mouseenter: function() { self.menuScroll($sub, $(this).hasClass('scroll-up')); },
7446									mouseleave: function(e) {
7447										self.menuScrollStop($sub);
7448										self.menuScrollOut($sub, e);
7449									},
7450									'mousewheel DOMMouseScroll': function(e) { e.preventDefault(); }
7451								})
7452								.insertAfter($sub)
7453							);
7454						}
7455						// bind events to show/hide arrows on hover and save scrolling data for this sub
7456						var vportY = winY - (itemY + itemH);
7457						$sub.dataSM('scroll', {
7458								vportY: vportY,
7459								subH: subH,
7460								winH: winH,
7461								step: 1
7462							})
7463							.bind({
7464								'mouseover.smartmenus_scroll': function(e) { self.menuScrollOver($sub, e); },
7465								'mouseout.smartmenus_scroll': function(e) { self.menuScrollOut($sub, e); },
7466								'mousewheel.smartmenus_scroll DOMMouseScroll.smartmenus_scroll': function(e) { self.menuScrollMousewheel($sub, e); }
7467							})
7468							.dataSM('scroll-arrows').css({ top: 'auto', left: '0', marginLeft: x + (parseInt($sub.css('border-left-width')) || 0), width: this.getWidth($sub) - (parseInt($sub.css('border-left-width')) || 0) - (parseInt($sub.css('border-right-width')) || 0), zIndex: this.get
7468StartZIndex() + this.zIndexInc })
7469							.eq(0).css('margin-top', vportY).end()
7470							.eq(1).css('margin-top', vportY + winH - this.getHeight($sub.dataSM('scroll-arrows').eq(1))).end()
7471							.eq(horizontalParent && this.opts.bottomToTopSubMenus ? 0 : 1).show();
7472					}
7473				}
7474				// if ($sub.hasClass('mega-menu-inner')) {
7475				// 	if ($('.menu-wrapper .megamenu-diff').length > 0) {
7476				// 		leftPos = parseFloat($('.menu-wrapper .megamenu-diff').outerWidth());
7477				// 		leftPos = -leftPos;
7478				// 	} else leftPos = parseFloat($('.menu-wrapper .main-menu-container').css('paddingLeft'));
7479
7480				// 	if ($('body.header-full-width').length > 0) {
7481				// 		$sub.css({ width: $('.box-container').width()});
7482				// 		leftPos = (leftPos - parseFloat($('.menu-wrapper .row-menu-inner').css('paddingLeft')));
7483				// 	} else $sub.css({ width: containerW});
7484				// 	leftPos = leftPos + 'px';
7485
7486				// } else {
7487				// 	leftPos = $li.position().left + 'px';
7488				// }
7489				var rightPos = 'auto';
7490				if ( $sub.parents('.isotope-filters').length ) {
7491					if ( $sub.parents('.text-right').eq(0).length ) {
7492						leftPos = '0px';
7493						rightPos = 'auto';
7494					} else {
7495						leftPos = 'auto';
7496						rightPos = '0px';
7497					}
7498					x = 0;
7499				} else {
7500					if ($sub.hasClass('mega-menu-inner')) {
7501						$sub.css({ width: containerW});
7502						if ( $('body.hmenu-center-double').length ) {
7503							leftPos = '0px';
7504						} else {
7505							if ( $('body.hmenu-center-split').length > 0 && !$sub.closest('.navbar-cta').length && ! $('body.hormenu-position-right').length ) {
7506								leftPos = - ($sub.closest('.menu-primary-inner')[0].offsetLeft) + 'px';
7507							} else {
7508								if ( $sub.closest('.navbar-cta').length && !$('body.hmenu').length && !$('body.hmenu-center').length ) {
7509									leftPos = - ( parseFloat($sub.closest('.navbar-cta').offset().left) - parseFloat($sub.closest('.row-menu-inner').find('.logo-container').offset().left) ) + 'px';
7510								} else if ($sub.closest('.row-menu-inner').find('.logo-container').length > 0 && !$('body.hmenu-center-split').length) {
7511									leftPos = - (parseFloat($sub.closest('.row-menu-inner').find('.logo-container').css('paddingRight')) + ($sub.closest('.row-menu-inner').find('.logo-container').width())) + 'px';
7512								} else {
7513									leftPos = '0px';
7514								}
7515							}
7516						}
7517					} else {
7518						leftPos = (level > 2 ? $li.position().left - parseFloat($li.closest('ul').css('paddingLeft')) : $li.position().left ) + 'px';
7519						x = (level > 2 && x >= 0) ? x + (parseFloat($li.closest('ul').css('paddingLeft')) * 2) + 1 : x - 1;
7520					}
7521				}
7522
7523
7524				$sub.css({ top: (level > 2) ? $a[0].offsetTop : (fixIE ? itemH : '100%'), left: leftPos, right: rightPos, marginLeft: x, marginTop: (level > 2) ? 0 : y - itemH + ($sub.closest('.menu-mini').length ? 0 : 1) });
7525				// IE iframe shim
7526				this.menuIframeShim($sub);
7527				if ($sub.dataSM('ie-shim')) {
7528					$sub.dataSM('ie-shim').css({ zIndex: $sub.css('z-index'), width: subW, height: subH, marginLeft: x, marginTop: y - itemH + ($sub.closest('.menu-mini').length ? 0 : 1) });
7529				}
7530			},
7531			menuScroll: function($sub, up, wheel) {
7532				var y = parseFloat($sub.css('margin-top')),
7533					scroll = $sub.dataSM('scroll'),
7534					end = scroll.vportY + (up ? 0 : scroll.winH - scroll.subH),
7535					step = wheel || !this.opts.scrollAccelerate ? this.opts.scrollStep : Math.floor($sub.dataSM('scroll').step);
7536				$sub.add($sub.dataSM('ie-shim')).css('margin-top', Math.abs(end - y) > step ? y + (up ? step : -step) : end);
7537				y = parseFloat($sub.css('margin-top'));
7538				// show opposite arrow if appropriate
7539				if (up && y + scroll.subH > scroll.vportY + scroll.winH || !up && y < scroll.vportY) {
7540					$sub.dataSM('scroll-arrows').eq(up ? 1 : 0).show();
7541				}
7542				// accelerate when not using mousewheel to scroll
7543				if (!wheel && this.opts.scrollAccelerate && $sub.dataSM('scroll').step < this.opts.scrollStep) {
7544					$sub.dataSM('scroll').step += 0.5;
7545				}
7546				// "y" and "end" might be float numbers in IE9/FF4+ so this weird way to check is used
7547				if (Math.abs(y - end) < 1) {
7548					$sub.dataSM('scroll-arrows').eq(up ? 0 : 1).hide();
7549					$sub.dataSM('scroll').step = 1;
7550				} else if (!wheel) {
7551					var self = this;
7552					this.scrollTimeout = setTimeout(function() { self.menuScroll($sub, up); }, this.opts.scrollInterval);
7553				}
7554			},
7555			menuScrollMousewheel: function($sub, e) {
7556				var $closestSub = $(e.target).closest('ul');
7557				while ($closestSub.dataSM('in-mega')) {
7558					$closestSub = $closestSub.parent().closest('ul');
7559				}
7560				if ($closestSub[0] == $sub[0]) {
7561					var up = (e.originalEvent.wheelDelta || -e.originalEvent.detail) > 0;
7562					if ($sub.dataSM('scroll-arrows').eq(up ? 0 : 1).is(':visible')) {
7563						this.menuScroll($sub, up, true);
7564					}
7565				}
7566				e.preventDefault();
7567			},
7568			menuScrollOut: function($sub, e) {
7569				var reClass = /^scroll-(up|down)/,
7570					$closestSub = $(e.relatedTarget).closest('ul');
7571				while ($closestSub.dataSM('in-mega')) {
7572					$closestSub = $closestSub.parent().closest('ul');
7573				}
7574				if (!reClass.test((e.relatedTarget || '').className) && ($sub[0] != e.relatedTarget && !$.contains($sub[0], e.relatedTarget) || $closestSub[0] != $sub[0])) {
7575					$sub.dataSM('scroll-arrows').css('visibility', 'hidden');
7576				}
7577			},
7578			menuScrollOver: function($sub, e) {
7579				var reClass = /^scroll-(up|down)/,
7580					$closestSub = $(e.target).closest('ul');
7581				while ($closestSub.dataSM('in-mega')) {
7582					$closestSub = $closestSub.parent().closest('ul');
7583				}
7584				if (!reClass.test(e.target.className) && $closestSub[0] == $sub[0]) {
7585					$sub.dataSM('scroll-arrows').css('visibility', 'visible');
7586				}
7587			},
7588			menuScrollStop: function($sub) {
7589				if (this.scrollTimeout) {
7590					clearTimeout(this.scrollTimeout);
7591					this.scrollTimeout = 0;
7592					$sub.dataSM('scroll').step = 1;
7593				}
7594			},
7595			menuShow: function($sub) {
7596				if (!$sub.dataSM('beforefirstshowfired')) {
7597					$sub.dataSM('beforefirstshowfired', true);
7598					if (this.$root.triggerHandler('beforefirstshow.smapi', $sub[0]) === false) {
7599						return;
7600					}
7601				}
7602				if (this.$root.triggerHandler('beforeshow.smapi', $sub[0]) === false) {
7603					return;
7604				}
7605				this.menuFixLayout($sub);
7606				$sub.stop(true, true);
7607				if (!$sub.is(':visible')) {
7608					// set z-index - for IE < 9 set it to the parent LI
7609					var zIndex = this.getStartZIndex() + (++this.zIndexInc);
7610					if (IElt9) {
7611						$sub.parent().css('z-index', zIndex);
7612					} else {
7613						$sub.css('z-index', zIndex);
7614					}
7615					// highlight parent item
7616					if (this.opts.keepHighlighted || this.isCollapsible()) {
7617						if ($sub.dataSM('parent-a').attr('data-type') != 'title')
7618							$sub.dataSM('parent-a').addClass('highlighted');
7619					}
7620					// min/max-width fix - no way to rely purely on CSS as all UL's are nested
7621					if (this.opts.subMenusMinWidth || this.opts.subMenusMaxWidth) {
7622						if (!IElt8) {
7623							$sub.css({ width: ($sub.hasClass('mega-menu-inner')) ? $('.box-container').outerWidth() + 'px' : 'auto', minWidth: '', maxWidth: '' }).addClass('sm-nowrap');
7624							if (this.opts.subMenusMinWidth) {
7625							 	$sub.css('min-width', this.opts.subMenusMinWidth);
7626							}
7627							if (this.opts.subMenusMaxWidth) {
7628							 	var noMaxWidth = this.getWidth($sub);
7629							 	$sub.css('max-width', this.opts.subMenusMaxWidth);
7630								if (noMaxWidth > this.getWidth($sub)) {
7631									$sub.removeClass('sm-nowrap').css('width', this.opts.subMenusMaxWidth);
7632								}
7633							}
7634						// IE6,7
7635 						} else {
7636							$sub.children().css('styleFloat', 'none');
7637							if (IE6) {
7638								$sub.width(this.opts.subMenusMinWidth ? this.opts.subMenusMinWidth : 1)
7639									.children().children('a').css('white-space', 'nowrap');
7640							} else { // IE7
7641								$sub.css({ width: ($sub.hasClass('mega-menu-inner')) ? $('.box-container').outerWidth() + 'px' : 'auto', minWidth: '', maxWidth: '' }).addClass('sm-nowrap');
7642								if (this.opts.subMenusMinWidth) {
7643							    $sub.css('min-width', this.opts.subMenusMinWidth);
7644								}
7645							}
7646							if (this.opts.subMenusMaxWidth) {
7647								var noMaxWidth = $sub.width();
7648								if (IE6) {
7649							 		var maxWidth = $sub.css({ width: this.opts.subMenusMaxWidth, overflowX: 'hidden', overflowY: 'hidden' }).width();
7650									if (noMaxWidth > maxWidth) {
7651										$sub.css({ width: maxWidth, overflowX: 'visible', overflowY: 'visible' }).children().children('a').css('white-space', '');
7652									} else {
7653										$sub.css({ width: noMaxWidth, overflowX: 'visible', overflowY: 'visible' });
7654									}
7655								} else { // IE7
7656									$sub.css('max-width', this.opts.subMenusMaxWidth);
7657									if (noMaxWidth > $sub.width()) {
7658										$sub.removeClass('sm-nowrap').css('width', this.opts.subMenusMaxWidth);
7659									} else {
7660										$sub.width(noMaxWidth);
7661									}
7662								}
7663							} else {
7664							 	$sub.width($sub.width());
7665							}
7666							$sub.children().css('styleFloat', 'left');
7667						}
7668					}
7669					this.menuPosition($sub);
7670					// insert IE iframe shim
7671					if ($sub.dataSM('ie-shim')) {
7672						$sub.dataSM('ie-shim').insertBefore($sub);
7673					}
7674					var complete = function() {
7675						// fix: "overflow: hidden;" is not reset on animation complete in jQuery < 1.9.0 in Chrome when global "box-sizing: border-box;" is used
7676						$sub.css('overflow', '');
7677					};
7678					// if sub is collapsible (mobile view)
7679					if (this.isCollapsible()) {
7680						if (this.opts.collapsibleShowFunction) {
7681							this.opts.collapsibleShowFunction.call(this, $sub, complete);
7682						} else {
7683							$sub.show(this.opts.collapsibleShowDuration, complete);
7684						}
7685					} else {
7686						if (this.opts.showFunction) {
7687							this.opts.showFunction.call(this, $sub, complete);
7688						} else {
7689							$sub.show(this.opts.showDuration, complete);
7690						}
7691					}
7692					// save new sub menu for this level
7693					this.visibleSubMenus[$sub.dataSM('level') - 1] = $sub;
7694					this.$root.triggerHandler('show.smapi', $sub[0]);
7695				}
7696			},
7697			popupHide: function(noHideTimeout) {
7698				if (this.hideTimeout) {
7699					clearTimeout(this.hideTimeout);
7700					this.hideTimeout = 0;
7701				}
7702				var self = this;
7703				this.hideTimeout = setTimeout(function() {
7704					self.menuHideAll();
7705				}, noHideTimeout ? 1 : this.opts.hideTimeout);
7706			},
7707			popupShow: function(left, top) {
7708				if (!this.opts.isPopup) {
7709					alert('SmartMenus jQuery Error:\n\nIf you want to show this menu via the "popupShow" method, set the isPopup:true option.');
7710					return;
7711				}
7712				if (this.hideTimeout) {
7713					clearTimeout(this.hideTimeout);
7714					this.hideTimeout = 0;
7715				}
7716				this.menuFixLayout(this.$root);
7717				this.$root.stop(true, true);
7718				if (!this.$root.is(':visible')) {
7719					this.$root.css({ left: left, top: top });
7720					// IE iframe shim
7721					this.menuIframeShim(this.$root);
7722					if (this.$root.dataSM('ie-shim')) {
7723						this.$root.dataSM('ie-shim').css({ zIndex: this.$root.css('z-index'), width: this.getWidth(this.$root), height: this.getHeight(this.$root), left
7723: left, top: top }).insertBefore(this.$root);
7724					}
7725					// show menu
7726					if (this.opts.showFunction) {
7727						this.opts.showFunction.call(this, this.$root);
7728					} else {
7729						this.$root.show(this.opts.showDuration);
7730					}
7731					this.visibleSubMenus[0] = this.$root;
7732				}
7733			},
7734			refresh: function() {
7735				this.menuHideAll();
7736				this.$root.find('ul').each(function() {
7737						var $this = $(this);
7738						if ($this.dataSM('scroll-arrows')) {
7739							$this.dataSM('scroll-arrows').remove();
7740						}
7741					})
7742					.removeDataSM('in-mega')
7743					.removeDataSM('shown-before')
7744					.removeDataSM('ie-shim')
7745					.removeDataSM('scroll-arrows')
7746					.removeDataSM('parent-a')
7747					.removeDataSM('level')
7748					.removeDataSM('beforefirstshowfired');
7749				this.$root.find('a.has-submenu').removeClass('has-submenu')
7750					.parent().removeDataSM('sub');
7751				if (this.opts.subIndicators) {
7752					this.$root.find('span.sub-arrow').remove();
7753				}
7754				if (this.opts.markCurrentItem) {
7755					this.$root.find('a.current').removeClass('current');
7756				}
7757				this.subMenus = [];
7758				this.init(true);
7759			},
7760			rootOut: function(e) {
7761				if (!this.handleEvents() || this.isTouchMode() || e.target == this.$root[0]) {
7762					return;
7763				}
7764				if (this.hideTimeout) {
7765					clearTimeout(this.hideTimeout);
7766					this.hideTimeout = 0;
7767				}
7768				if (!this.opts.showOnClick || !this.opts.hideOnClick) {
7769					var self = this;
7770					this.hideTimeout = setTimeout(function() { self.menuHideAll(); }, this.opts.hideTimeout);
7771				}
7772			},
7773			rootOver: function(e) {
7774				if (!this.handleEvents() || this.isTouchMode() || e.target == this.$root[0]) {
7775					return;
7776				}
7777				if (this.hideTimeout) {
7778					clearTimeout(this.hideTimeout);
7779					this.hideTimeout = 0;
7780				}
7781			},
7782			winResize: function(e) {
7783				if (!this.handleEvents()) {
7784					// we still need to resize the disable overlay if it's visible
7785					if (this.$disableOverlay) {
7786						var pos = this.$root.offset();
7787	 					this.$disableOverlay.css({
7788							top: pos.top,
7789							left: pos.left,
7790							width: this.$root.outerWidth(),
7791							height: this.$root.outerHeight()
7792						});
7793					}
7794					return;
7795				}
7796				// hide sub menus on resize - on mobile do it only on orientation change
7797				if (!this.isCollapsible() && (!('onorientationchange' in window) || e.type == 'orientationchange')) {
7798					if (this.activatedItems.length) {
7799						this.activatedItems[this.activatedItems.length - 1][0].blur();
7800					}
7801					this.menuHideAll();
7802				}
7803			}
7804		}
7805	});
7806
7807	$.fn.dataSM = function(key, val) {
7808		if (val) {
7809			return this.data(key + '_smartmenus', val);
7810		}
7811		return this.data(key + '_smartmenus');
7812	}
7813
7814	$.fn.removeDataSM = function(key) {
7815		return this.removeData(key + '_smartmenus');
7816	}
7817
7818	$.fn.smartmenus = function(options) {
7819		if (typeof options == 'string') {
7820			var args = arguments,
7821				method = options;
7822			Array.prototype.shift.call(args);
7823			return this.each(function() {
7824				var smartmenus = $(this).data('smartmenus');
7825				if (smartmenus && smartmenus[method]) {
7826					smartmenus[method].apply(smartmenus, args);
7827				}
7828			});
7829		}
7830		var opts = $.extend({}, $.fn.smartmenus.defaults, options);
7831		return this.each(function() {
7832			new $.SmartMenus(this, opts);
7833		});
7834	}
7835
7836	// default settings
7837	$.fn.smartmenus.defaults = {
7838		isPopup:		false,		// is this a popup menu (can be shown via the popupShow/popupHide methods) or a permanent menu bar
7839		mainMenuSubOffsetX:	0,		// pixels offset from default position
7840		mainMenuSubOffsetY:	0,		// pixels offset from default position
7841		subMenusSubOffsetX:	0,		// pixels offset from default position
7842		subMenusSubOffsetY:	0,		// pixels offset from default position
7843		subMenusMinWidth:	'10em',		// min-width for the sub menus (any CSS unit) - if set, the fixed width set in CSS will be ignored
7844		subMenusMaxWidth:	'20em',		// max-width for the sub menus (any CSS unit) - if set, the fixed width set in CSS will be ignored
7845		subIndicators: 		true,		// create sub menu indicators - creates a SPAN and inserts it in the A
7846		subIndicatorsPos: 	'prepend',	// position of the SPAN relative to the menu item content ('prepend', 'append')
7847		subIndicatorsText:	'+',		// [optionally] add text in the SPAN (e.g. '+') (you may want to check the CSS for the sub indicators too)
7848		scrollStep: 		30,		// pixels step when scrolling long sub menus that do not fit in the viewport height
7849		scrollInterval:		30,		// interval between each scrolling step
7850		scrollAccelerate:	true,		// accelerate scrolling or use a fixed step
7851		showTimeout:		250,		// timeout before showing the sub menus
7852		hideTimeout:		500,		// timeout before hiding the sub menus
7853		showDuration:		0,		// duration for show animation - set to 0 for no animation - matters only if showFunction:null
7854		showFunction:		null,		// custom function to use when showing a sub menu (the default is the jQuery 'show')
7855							// don't forget to call complete() at the end of whatever you do
7856							// e.g.: function($ul, complete) { $ul.fadeIn(250, complete); }
7857		hideDuration:		0,		// duration for hide animation - set to 0 for no animation - matters only if hideFunction:null
7858		hideFunction:		function($ul, complete) { $ul.fadeOut(200, complete); },	// custom function to use when hiding a sub menu (the default is the jQuery 'hide')
7859							// don't forget to call complete() at the end of whatever you do
7860							// e.g.: function($ul, complete) { $ul.fadeOut(250, complete); }
7861		collapsibleShowDuration:0,		// duration for show animation for collapsible sub menus - matters only if collapsibleShowFunction:null
7862		collapsibleShowFunction:function($ul, complete) { $ul.slideDown(200, complete); },	// custom function to use when showing a collapsible sub menu
7863							// (i.e. when mobile styles are used to make the sub menus collapsible)
7864		collapsibleHideDuration:0,		// duration for hide animation for collapsible sub menus - matters only if collapsibleHideFunction:null
7865		collapsibleHideFunction:function($ul, complete) { $ul.slideUp(200, complete); },	// custom function to use when hiding a collapsible sub menu
7866							// (i.e. when mobile styles are used to make the sub menus collapsible)
7867		showOnClick:		false,		// show the first-level sub menus onclick instead of onmouseover (matters only for mouse input)
7868		hideOnClick:		true,		// hide the sub menus on click/tap anywhere on the page
7869		keepInViewport:		true,		// reposition the sub menus if needed to make sure they always appear inside the viewport
7870		keepHighlighted:	true,		// keep all ancestor items of the current sub menu highlighted (adds the 'highlighted' class to the A's)
7871		markCurrentItem:	false,		// automatically add the 'current' class to the A element of the item linking to the current URL
7872		markCurrentTree:	true,		// add the 'current' class also to the A elements of all ancestor items of the current item
7873		rightToLeftSubMenus:	false,		// right to left display of the sub menus (check the CSS for the sub indicators' position)
7874		bottomToTopSubMenus:	false,		// bottom to top display of the sub menus
7875		overlapControlsInIE:	true		// make sure sub menus appear on top of special OS controls in IE (i.e. SELECT, OBJECT, EMBED, etc.)
7876	};
7877
7878})(jQuery);
7879
7880/*
7881 * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
7882 *
7883 * Uses the built in easing capabilities added In jQuery 1.1
7884 * to offer multiple easing options
7885 *
7886 * TERMS OF USE - jQuery Easing
7887 * 
7888 * Open source under the BSD License.
7889 * 
7890 * Copyright © 2008 George McGinley Smith
7891 * All rights reserved.
7892 * 
7893 * Redistribution and use in source and binary forms, with or without modification, 
7894 * are permitted provided that the following conditions are met:
7895 * 
7896 * Redistributions of source code must retain the above copyright notice, this list of 
7897 * conditions and the following disclaimer.
7898 * Redistributions in binary form must reproduce the above copyright notice, this list 
7899 * of conditions and the following disclaimer in the documentation and/or other materials 
7900 * provided with the distribution.
7901 * 
7902 * Neither the name of the author nor the names of contributors may be used to endorse 
7903 * or promote products derived from this software without specific prior written permission.
7904 * 
7905 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
7906 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
7907 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
7908 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
vendor: 5,091 bytes, lines 7909-8054
7909 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
7910 *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
7911 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
7912 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
7913 * OF THE POSSIBILITY OF SUCH DAMAGE. 
7914 *
7915*/
7916
7917// t: current time, b: begInnIng value, c: change In value, d: duration
7918jQuery.easing['jswing'] = jQuery.easing['swing'];
7919
7920jQuery.extend( jQuery.easing,
7921{
7922	def: 'easeOutQuad',
7923	swing: function (x, t, b, c, d) {
7924		//alert(jQuery.easing.default);
7925		return jQuery.easing[jQuery.easing.def](x, t, b, c, d);
7926	},
7927	easeInQuad: function (x, t, b, c, d) {
7928		return c*(t/=d)*t + b;
7929	},
7930	easeOutQuad: function (x, t, b, c, d) {
7931		return -c *(t/=d)*(t-2) + b;
7932	},
7933	easeInOutQuad: function (x, t, b, c, d) {
7934		if ((t/=d/2) < 1) return c/2*t*t + b;
7935		return -c/2 * ((--t)*(t-2) - 1) + b;
7936	},
7937	easeInCubic: function (x, t, b, c, d) {
7938		return c*(t/=d)*t*t + b;
7939	},
7940	easeOutCubic: function (x, t, b, c, d) {
7941		return c*((t=t/d-1)*t*t + 1) + b;
7942	},
7943	easeInOutCubic: function (x, t, b, c, d) {
7944		if ((t/=d/2) < 1) return c/2*t*t*t + b;
7945		return c/2*((t-=2)*t*t + 2) + b;
7946	},
7947	easeInQuart: function (x, t, b, c, d) {
7948		return c*(t/=d)*t*t*t + b;
7949	},
7950	easeOutQuart: function (x, t, b, c, d) {
7951		return -c * ((t=t/d-1)*t*t*t - 1) + b;
7952	},
7953	easeInOutQuart: function (x, t, b, c, d) {
7954		if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
7955		return -c/2 * ((t-=2)*t*t*t - 2) + b;
7956	},
7957	easeInQuint: function (x, t, b, c, d) {
7958		return c*(t/=d)*t*t*t*t + b;
7959	},
7960	easeOutQuint: function (x, t, b, c, d) {
7961		return c*((t=t/d-1)*t*t*t*t + 1) + b;
7962	},
7963	easeInOutQuint: function (x, t, b, c, d) {
7964		if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
7965		return c/2*((t-=2)*t*t*t*t + 2) + b;
7966	},
7967	easeInSine: function (x, t, b, c, d) {
7968		return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
7969	},
7970	easeOutSine: function (x, t, b, c, d) {
7971		return c * Math.sin(t/d * (Math.PI/2)) + b;
7972	},
7973	easeInOutSine: function (x, t, b, c, d) {
7974		return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
7975	},
7976	easeInExpo: function (x, t, b, c, d) {
7977		return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
7978	},
7979	easeOutExpo: function (x, t, b, c, d) {
7980		return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
7981	},
7982	easeInOutExpo: function (x, t, b, c, d) {
7983		if (t==0) return b;
7984		if (t==d) return b+c;
7985		if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
7986		return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
7987	},
7988	easeInCirc: function (x, t, b, c, d) {
7989		return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
7990	},
7991	easeOutCirc: function (x, t, b, c, d) {
7992		return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
7993	},
7994	easeInOutCirc: function (x, t, b, c, d) {
7995		if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
7996		return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
7997	},
7998	easeInElastic: function (x, t, b, c, d) {
7999		var s=1.70158;var p=0;var a=c;
8000		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
8001		if (a < Math.abs(c)) { a=c; var s=p/4; }
8002		else var s = p/(2*Math.PI) * Math.asin (c/a);
8003		return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
8004	},
8005	easeOutElastic: function (x, t, b, c, d) {
8006		var s=1.70158;var p=0;var a=c;
8007		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
8008		if (a < Math.abs(c)) { a=c; var s=p/4; }
8009		else var s = p/(2*Math.PI) * Math.asin (c/a);
8010		return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
8011	},
8012	easeInOutElastic: function (x, t, b, c, d) {
8013		var s=1.70158;var p=0;var a=c;
8014		if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5);
8015		if (a < Math.abs(c)) { a=c; var s=p/4; }
8016		else var s = p/(2*Math.PI) * Math.asin (c/a);
8017		if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
8018		return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
8019	},
8020	easeInBack: function (x, t, b, c, d, s) {
8021		if (s == undefined) s = 1.70158;
8022		return c*(t/=d)*t*((s+1)*t - s) + b;
8023	},
8024	easeOutBack: function (x, t, b, c, d, s) {
8025		if (s == undefined) s = 1.70158;
8026		return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
8027	},
8028	easeInOutBack: function (x, t, b, c, d, s) {
8029		if (s == undefined) s = 1.70158; 
8030		if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
8031		return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
8032	},
8033	easeInBounce: function (x, t, b, c, d) {
8034		return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b;
8035	},
8036	easeOutBounce: function (x, t, b, c, d) {
8037		if ((t/=d) < (1/2.75)) {
8038			return c*(7.5625*t*t) + b;
8039		} else if (t < (2/2.75)) {
8040			return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
8041		} else if (t < (2.5/2.75)) {
8042			return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
8043		} else {
8044			return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
8045		}
8046	},
8047	easeInOutBounce: function (x, t, b, c, d) {
8048		if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
8049		return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
8050	}
8051});
8052
8053/*
8054 *
8055 * TERMS OF USE - EASING EQUATIONS
8056 * 
8057 * Open source under the BSD License. 
8058 * 
8059 * Copyright © 2001 Robert Penner
8060 * All rights reserved.
8061 * 
8062 * Redistribution and use in source and binary forms, with or without modification, 
8063 * are permitted provided that the following conditions are met:
8064 * 
8065 * Redistributions of source code must retain the above copyright notice, this list of 
8066 * conditions and the following disclaimer.
8067 * Redistributions in binary form must reproduce the above copyright notice, this list 
8068 * of conditions and the following disclaimer in the documentation and/or other materials 
8069 * provided with the distribution.
8070 * 
8071 * Neither the name of the author nor the names of contributors may be used to endorse 
8072 * or promote products derived from this software without specific prior written permission.
8073 * 
8074 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
8075 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
8076 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
8077 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
8078 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
8079 *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
8080 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
8081 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
8082 * OF THE POSSIBILITY OF SUCH DAMAGE. 
8083 *
8084 */
8085(function($, window, undefined) {
8086    // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
8087    // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
8088 
8089    // requestAnimationFrame polyfill by Erik Möller
8090    // fixes from Paul Irish and Tino Zijdel
8091 
8092    var lastTime = 0,
8093        running,
8094        animate = function (elem) {
8095            if (running) {
8096                window.requestAnimationFrame(animate, elem);
8097                jQuery.fx.tick();
8098            }
8099        },
8100        vendors = ['ms', 'moz', 'webkit', 'o'];
8101 
8102    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
8103        window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
8104        window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
8105            || window[vendors[x]+'CancelRequestAnimationFrame'];
8106    }
8107 
8108    if (!window.requestAnimationFrame)
8109        window.requestAnimationFrame = function(fn, element) {
8110            var currTime = new Date().getTime(),
8111                delta = currTime - lastTime,
8112                timeToCall = Math.max(0, 16 - delta);
8113 
8114            var id = window.setTimeout(function() {
8115                    fn(currTime + timeToCall);
8116                },
8117                timeToCall
8118            );
8119 
8120            lastTime = currTime + timeToCall;
8121 
8122            return id;
8123        };
8124 
8125    if (!window.cancelAnimationFrame) {
8126        window.cancelAnimationFrame = function(id) {
8127            clearTimeout(id);
8128        };
8129    }
8130 
8131    jQuery.fx.timer = function (timer) {
8132        if (timer() && jQuery.timers.push(timer) && !running) {
8133            running = true;
8134            animate(timer.elem);
8135        }
8136    };
8137 
8138    jQuery.fx.stop = function() {
8139        running = false;
8140    };
8141 
8142}(jQuery, this));
8143/*! Copyright (c) 2011 Brandon Aaron (http://brandonaaron.net)
8144 * Licensed under the MIT License (LICENSE.txt).
8145 *
8146 * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
8147 * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
8148 * Thanks to: Seamus Leahy for adding deltaX and deltaY
8149 *
8150 * Version: 3.0.6
8151 *
8152 * Requires: 1.2.2+
8153 */
8154(function($) {
8155    var types = ['DOMMouseScroll', 'mousewheel'];
8156    if ($.event.fixHooks) {
8157        for (var i = types.length; i;) {
8158            $.event.fixHooks[types[--i]] = $.event.mouseHooks;
8159        }
8160    }
8161    $.event.special.mousewheel = {
8162        setup: function() {
8163            if (this.addEventListener) {
8164                for (var i = types.length; i;) {
8165                    this.addEventListener(types[--i], handler, false);
8166                }
8167            } else {
8168                this.onmousewheel = handler;
8169            }
8170        },
8171        teardown: function() {
8172            if (this.removeEventListener) {
8173                for (var i = types.length; i;) {
8174                    this.removeEventListener(types[--i], handler, false);
8175                }
8176            } else {
8177                this.onmousewheel = null;
8178            }
8179        }
8180    };
8181    $.fn.extend({
8182        mousewheel: function(fn) {
8183            return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
8184        },
8185        unmousewheel: function(fn) {
8186            return this.unbind("mousewheel", fn);
8187        }
8188    });
8189
8190    function handler(event) {
8191        var orgEvent = event || window.event,
8192            args = [].slice.call(arguments, 1),
8193            delta = 0,
8194            returnValue = true,
8195            deltaX = 0,
8196            deltaY = 0;
8197        event = $.event.fix(orgEvent);
8198        event.type = "mousewheel";
8199        // Old school scrollwheel delta
8200        if (orgEvent.wheelDelta) {
8201            delta = orgEvent.wheelDelta / 120;
8202        }
8203        if (orgEvent.detail) {
8204            delta = -orgEvent.detail / 3;
8205        }
8206        // New school multidimensional scroll (touchpads) deltas
8207        deltaY = delta;
8208        // Gecko
8209        if (orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS) {
8210            deltaY = 0;
8211            deltaX = -1 * delta;
8212        }
8213        // Webkit
8214        if (orgEvent.wheelDeltaY !== undefined) {
8215            deltaY = orgEvent.wheelDeltaY / 120;
8216        }
8217        if (orgEvent.wheelDeltaX !== undefined) {
8218            deltaX = -1 * orgEvent.wheelDeltaX / 120;
8219        }
8220        // Add event and delta to the front of the arguments
8221        args.unshift(event, delta, deltaX, deltaY);
8222        return ($.event.dispatch || $.event.handle).apply(this, args);
8223    }
8224})(jQuery);
8225/**
8226 * jQuery iLightBox - Revolutionary Lightbox Plugin
8227 * http://www.ilightbox.net/
8228 *
8229 * @version: 2.2.4 - October 14, 2017
8230 *
8231 * @author: iProDev (Hemn Chawroka)
8232 *          http://www.iprodev.com/
8233 *
8234 */
8235(function($, window, undefined) {
8236
8237	var extensions = {
8238			flash: ['swf'],
8239			image: ['bmp', 'gif', 'jpeg', 'jpg', 'png', 'tiff', 'tif', 'jfif', 'jpe'],
8240			iframe: ['asp', 'aspx', 'cgi', 'cfm', 'htm', 'html', 'jsp', 'php', 'pl', 'php3', 'php4', 'php5', 'phtml', 'rb', 'rhtml', 'shtml', 'txt'],
8241			video: ['avi', 'mov', 'mpg', 'mpeg', 'movie', 'mp4', 'webm', 'ogv', 'ogg', '3gp', 'm4v']
8242		},
8243
8244		// Global DOM elements
8245		$win = $(window),
8246		$doc = $(document),
8247
8248		// Support indicators
8249		browser,
8250		transform,
8251		gpuAcceleration,
8252		fullScreenApi = '',
8253		userAgent = navigator.userAgent || navigator.vendor || window.opera,
8254        supportTouch = "ontouchstart" in window || navigator.msMaxTouchPoints || (navigator.maxTouchPoints && navigator.userAgent.indexOf('Windows') > -1),
8255        ishybrid = navigator.maxTouchPoints && navigator.userAgent.indexOf('Windows') > -1,
8256        isMobile = /(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(userAgent) || /1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(userAgent.substr(0, 4)),
8257
8258		// Events
8259		clickEvent = ishybrid ? "itap.iLightBox click.iLightBox" : supportTouch ? "itap.iLightBox" : "click.iLightBox",
8260		touchStartEvent = ishybrid ? "touchstart.iLightBox mousedown.iLightBox" : supportTouch ? "touchstart.iLightBox" : "mousedown.iLightBox",
8261		touchStopEvent = ishybrid ? "touchend.iLightBox mouseup.iLightBox" : supportTouch ? "touchend.iLightBox" : "mouseup.iLightBox",
8262		touchMoveEvent = ishybrid ? "touchmove.iLightBox mousemove.iLightBox" : supportTouch ? "touchmove.iLightBox" : "mousemove.iLightBox",
8263
8264		// Math shorthands
8265		abs = Math.abs,
8266		sqrt = Math.sqrt,
8267		round = Math.round,
8268		max = Math.max,
8269		min = Math.min,
8270		floor = Math.floor,
8271		random = Math.random,
8272
8273		pluginspages = {
8274			quicktime: 'http://www.apple.com/quicktime/download',
8275			flash: 'http://www.adobe.com/go/getflash'
8276		},
8277
8278		iLightBox = function(el, options, items, instant) {
8279			var iL = this;
8280
8281			iL.options = options,
8282				iL.selector = el.selector || el,
8283				iL.context = el.context,
8284				iL.instant = instant;
8285
8286			if (items.length < 1) iL.attachItems();
8287			else iL.items = items;
8288
8289			iL.vars = {
8290				total: iL.items.length,
8291				start: 0,
8292				current: null,
8293				next: null,
8294				prev: null,
8295				BODY: $('body'),
8296				loadRequests: 0,
8297				overlay: $('<div class="ilightbox-overlay"></div>'),
8298				loader: $('<div class="ilightbox-loader"><div></div></div>'),
8299				toolbar: $('<div class="ilightbox-toolbar"></div>'),
8300				innerToolbar: $('<div class="ilightbox-inner-toolbar"></div>'),
8301				title: $('<div class="ilightbox-title"></div>'),
8302				closeButton: $('<a class="ilightbox-close" title="' + iL.options.text.close + '"></a>'),
8303				fullScreenButton: $('<a class="ilightbox-fullscreen" title="' + iL.options.text.enterFullscreen + '"></a>'),
8304				innerPlayButton: $('<a class="ilightbox-play" title="' + iL.options.text.slideShow + '"></a>'),
8305				innerNextButton: $('<a class="ilightbox-next-button" title="' + iL.options.text.next + '"></a>'),
8306				innerPrevButton: $('<a class="ilightbox-prev-button" title="' + iL.options.text.previous + '"></a>'),
8307				holder: $('<div class="ilightbox-holder' + (supportTouch ? ' supportTouch' : '') + '" ondragstart="return false;"><div class="ilightbox-container"></div></div>'),
8308				nextPhoto: $('<div class="ilightbox-holder' + (supportTouch ? ' supportTouch' : '') + ' ilightbox-next" ondragstart="return false;"><div class="ilightbox-container"></div></div>'),
8309				prevPhoto: $('<div class="ilightbox-holder' + (supportTouch ? ' supportTouch' : '') + ' ilightbox-prev" ondragstart="return false;"><div class="ilightbox-container"></div></div>'),
8310				nextButton: $('<a class="ilightbox-button ilightbox-next-button" ondragstart="return false;" title="' + iL.options.text.next + '"><span></span></a>'),
8311				prevButton: $('<a class="ilightbox-button ilightbox-prev-button" ondragstart="return false;" title="' + iL.options.text.previous + '"><span></span></a>'),
8312				thumbnails: $('<div class="ilightbox-thumbnails" ondragstart="return false;"><div class="ilightbox-thumbnails-container"><a class="ilightbox-thumbnails-dragger"></a><div class="ilightbox-thumbnails-grid"></div></div></div>'),
8313				thumbs: false,
8314				nextLock: false,
8315				prevLock: false,
8316				hashLock: false,
8317				isMobile: false,
8318				mobileMaxWidth: 980,
8319				isInFullScreen: false,
8320				isSwipe: false,
8321				mouseID: 0,
8322				cycleID: 0,
8323				isPaused: 0,
8324				captionHeight: 39,
8325			};
8326
8327			// Hideable elements with mousemove event
8328			iL.vars.hideableElements = iL.vars.nextButton.add(iL.vars.prevButton);
8329
8330			iL.normalizeItems();
8331
8332			//Check necessary plugins
8333			iL.availPlugins();
8334
8335			//Set startFrom
8336			iL.options.startFrom = (iL.options.startFrom > 0 && iL.options.startFrom >= iL.vars.total) ? iL.vars.total - 1 : iL.options.startFrom;
8337
8338			//If randomStart
8339			iL.options.startFrom = (iL.options.randomStart) ? floor(random() * iL.vars.total) : iL.options.startFrom;
8340			iL.vars.start = iL.options.startFrom;
8341
8342			if (instant) iL.instantCall();
8343			else iL.patchItemsEvents();
8344
8345			if (iL.options.linkId) {
8346				iL.hashChangeHandler();
8347				$win.iLightBoxHashChange(function() {
8348					iL.hashChangeHandler();
8349				});
8350			}
8351
8352			if (supportTouch) {
8353				var RegExp = /(click|mouseenter|mouseleave|mouseover|mouseout)/ig,
8354					replace = "itap";
8355				iL.options.caption.show = iL.options.caption.show.replace(RegExp, replace),
8356					iL.options.caption.hide = iL.options.caption.hide.replace(RegExp, replace),
8357					iL.options.social.show = iL.options.social.show.replace(RegExp, replace),
8358					iL.options.social.hide = iL.options.social.hide.replace(RegExp, replace);
8359			}
8360
8361			if (iL.options.controls.arrows || $(window).width() < UNCODE.mediaQuery) {
8362				$.extend(iL.options.styles, {
8363					nextOffsetX: ($(window).width() > UNCODE.mediaQuery) ? 36 : 4,
8364					prevOffsetX: ($(window).width() > UNCODE.mediaQuery) ? 36 : 4,
8365					nextOpacity: 0,
8366					prevOpacity: 0
8367				});
8368			}
8369		};
8370
8371	//iLightBox helpers
8372	iLightBox.prototype = {
8373		showLoader: function() {
8374			var iL = this;
8375			iL.vars.loadRequests += 1;
8376			if (iL.options.path.toLowerCase() == "horizontal") iL.vars.loader.addClass('ilightbox-show').stop().animate({
8377				//top: '-30px'
8378				opacity: '1'
8379			}, iL.options.show.speed, 'easeOutCirc');
8380			else iL.vars.loader.addClass('ilightbox-show').stop().animate({
8381				//left: '-30px'
8382				opacity: '1'
8383			}, iL.options.show.speed, 'easeOutCirc');
8384		},
8385
8386		hideLoader: function() {
8387			var iL = this;
8388			iL.vars.loadRequests -= 1;
8389			iL.vars.loadRequests = (iL.vars.loadRequests < 0) ? 0 : iL.vars.loadRequests;
8390			if (iL.options.path.toLowerCase() == "horizontal") {
8391				if (iL.vars.loadRequests <= 0) iL.vars.loader.removeClass('ilightbox-show').stop().animate({
8392					//top: '-192px'
8393					opacity: '0'
8394				}, iL.options.show.speed, 'easeInCirc');
8395			} else {
8396				if (iL.vars.loadRequests <= 0) iL.vars.loader.removeClass('ilightbox-show').stop().animate({
8397					//left: '-192px'
8398					opacity: '0'
8399				}, iL.options.show.speed, 'easeInCirc');
8400			}
8401		},
8402
8403		createUI: function() {
8404			var iL = this;
8405
8406			iL.ui = {
8407				currentElement: iL.vars.holder,
8408				nextElement: iL.vars.nextPhoto,
8409				prevElement: iL.vars.prevPhoto,
8410				currentItem: iL.vars.current,
8411				nextItem: iL.vars.next,
8412				prevItem: iL.vars.prev,
8413				hide: function() {
8414					iL.closeAction();
8415				},
8416				refresh: function() {
8417					(arguments.length > 0) ? iL.repositionPhoto(true): iL.repositionPhoto();
8418				},
8419				fullscreen: function() {
8420					iL.fullScreenAction();
8421				}
8422			};
8423		},
8424
8425		attachItems: function() {
8426			var iL = this,
8427				itemsObject = new Array(),
8428				items = new Array();
8429
8430			$(iL.selector, iL.context).each(function() {
8431				var t = $(this),
8432					URL = t.attr(iL.options.attr) || null,
8433					options = t.data("options") && eval("({" + t.data("options") + "})") || {},
8434					caption = t.data('caption'),
8435					title = t.data('title'),
8436					type = t.data('type') || getTypeByExtension(URL),
8437					clone = t.data('lbox-clone') || false;
8438
8439				if (t.data('lbox-clone') != undefined) return;
8440
8441				//Uncode addition ##START##
8442				if ( typeof t.attr('data-album') != 'undefined' && t.attr('data-album') != '' ) {
8443					var ALBUM_URLS = JSON.parse(t.attr('data-album')),
8444						URL_i,
8445						URLS_LENGHT = ALBUM_URLS.length;
8446					for (URL_i = 0; URL_i < URLS_LENGHT; URL_i++) {
8447						if ( typeof ALBUM_URLS[URL_i].url !== 'undefined' && ALBUM_URLS[URL_i].url != '' ) {
8448							var item_opts = 'width:' + ALBUM_URLS[URL_i].width + ',height:' + ALBUM_URLS[URL_i].height + ',thumbnail: \'' + ALBUM_URLS[URL_i].thumbnail + '\'';
8449							item_opts = item_opts && eval("({" + item_opts + "})") || {},
8450							items.push({
8451								URL: ALBUM_URLS[URL_i].url,
8452								caption: ALBUM_URLS[URL_i].caption,
8453								title: ALBUM_URLS[URL_i].title,
8454								type: getTypeByExtension(ALBUM_URLS[URL_i].url),
8455								options: item_opts,
8456								clone: clone
8457							});
8458							var newT = t;
8459							newT.attr('href',ALBUM_URLS[URL_i].url);
8460							if (!iL.instant) itemsObject.push(newT);
8461						}
8462					}
8463				} else {
8464				//Uncode addition ##END##
8465					items.push({
8466						URL: URL,
8467						caption: caption,
8468						title: title,
8469						type: type,
8470						options: options,
8471						clone: clone
8472					});
8473				}
8474
8475
8476				if (iL.vars != undefined) iL.vars.total = items.length;
8477
8478				if (!iL.instant) itemsObject.push(t);
8479			});
8480
8481			iL.items = items,
8482				iL.itemsObject = itemsObject;
8483
8484		},
8485
8486		normalizeItems: function() {
8487			var iL = this,
8488				newItems = new Array();
8489
8490			$.each(iL.items, function(key, val) {
8491
8492				if (typeof val == "string") val = {
8493					url: val
8494				};
8495
8496				var URL = val.url || val.URL || null,
8497					options = val.options || {},
8498					caption = val.caption || null,
8499					title = val.title || null,
8500					type = (val.type) ? val.type.toLowerCase() : getTypeByExtension(URL),
8501					ext = (typeof URL != 'object') ? getExtension(URL) : '';
8502
8503				options.thumbnail = options.thumbnail || ((type == "image") ? URL : null),
8504				options.videoType = options.videoType || null,
8505				options.skin = options.skin || iL.options.skin,
8506				options.width = options.width || null,
8507				options.height = options.height || null,
8508				options.mousewheel = (typeof options.mousewheel != 'undefined') ? options.mousewheel : true,
8509				options.swipe = (typeof options.swipe != 'undefined') ? options.swipe : true,
8510				options.social = (typeof options.social != 'undefined') ? options.social : iL.options.social.buttons && $.extend({}, {}, iL.options.social.buttons);
8511
8512				if (type == "video") {
8513					options.html5video = (typeof options.html5video != 'undefined') ? options.html5video : {};
8514
8515					options.html5video.webm = options.html5video.webm || options.html5video.WEBM || null;
8516					options.html5video.controls = (typeof options.html5video.controls != 'undefined') ? options.html5video.controls : "controls";
8517					options.html5video.preload = options.html5video.preload || "metadata";
8518					options.html5video.autoplay = (typeof options.html5video.autoplay != 'undefined') ? options.html5video.autoplay : false;
8519					//UNCODE addition
8520					options.html5video.loop = (typeof options.html5video.loop != 'undefined') ? options.html5video.loop : false;
8521				}
8522
8523				if (!options.width || !options.height) {
8524					if (type == "video") options.width = 1280, options.height = 720;
8525					else if (type == "iframe") options.width = '100%', options.height = '90%';
8526					else if (type == "flash") options.width = 1280, options.height = 720;
8527				}
8528
8529				delete val.url;
8530				val.index = key;
8531				val.URL = URL;
8532				val.caption = caption;
8533				val.title = title;
8534				val.type = type;
8535				val.options = options;
8536				val.ext = ext;
8537
8538				newItems.push(val);
8539			});
8540
8541			iL.items = newItems;
8542		},
8543
8544		instantCall: function() {
8545			var iL = this,
8546				key = iL.vars.start;
8547
8548			iL.vars.current = key;
8549			iL.vars.next = (iL.items[key + 1]) ? key + 1 : null;
8550			iL.vars.prev = (iL.items[key - 1]) ? key - 1 : null;
8551
8552			iL.addContents();
8553			iL.patchEvents();
8554		},
8555
8556		addContents: function() {
8557			var iL = this,
8558				vars = iL.vars,
8559				opts = iL.options,
8560				viewport = getViewport(),
8561				path = opts.path.toLowerCase(),
8562				recognizingItems = vars.total > 0 && iL.items.filter(function(e, i, arr) {
8563					return ['image', 'flash', 'video'].indexOf(e.type) === -1 && typeof e.recognized === 'undefined' && (opts.smartRecognition || e.options.smartRecognition);
8564				}),
8565				needRecognition = recognizingItems.length > 0;
8566
8567			if (opts.mobileOptimizer && !opts.innerToolbar)
8568				vars.isMobile = viewport.width <= vars.mobileMaxWidth;
8569
8570			vars.overlay.addClass(opts.skin).hide().css('opacity', opts.overlay.opacity);
8571
8572			if (opts.linkId)
8573				vars.overlay[0].setAttribute('linkid', opts.linkId);
8574
8575			//Add Toolbar Buttons
8576			if (opts.controls.toolbar) {
8577				vars.toolbar.addClass(opts.skin).append(vars.closeButton);
8578				if (opts.controls.fullscreen)
8579					vars.toolbar.append(vars.fullScreenButton);
8580				if (opts.controls.slideshow)
8581					vars.toolbar.append(vars.innerPlayButton);
8582				if (vars.total > 1)
8583					vars.toolbar.append(vars.innerPrevButton).append(vars.innerNextButton);
8584			}
8585
8586			//Append elements to body
8587			vars.BODY.addClass('ilightbox-noscroll').append(vars.overlay).append(vars.loader).append(vars.holder).append(vars.nextPhoto).append(vars.prevPhoto);
8588
8589			if (!opts.innerToolbar)
8590				vars.BODY.append(vars.toolbar);
8591			if (opts.controls.arrows)
8592				vars.BODY.append(vars.nextButton).append(vars.prevButton);
8593
8594			if (opts.controls.thumbnail && vars.total > 1) {
8595				vars.BODY.append(vars.thumbnails);
8596				vars.thumbnails.addClass(opts.skin).addClass('ilightbox-' + path);
8597				$('div.ilightbox-thumbnails-grid', vars.thumbnails).empty();
8598				vars.thumbs = true;
8599			}
8600
8601			//Configure loader and arrows
8602			var loaderCss = (opts.path.toLowerCase() == "horizontal") ? {
8603				left: parseInt((viewport.width / 2) - (vars.loader.outerWidth() / 2))
8604			} : {
8605				top: parseInt((viewport.height / 2) - (vars.loader.outerHeight() / 2))
8606			};
8607			vars.loader.addClass(opts.skin).css(loaderCss);
8608			vars.nextButton.add(vars.prevButton).addClass(opts.skin);
8609			if (path == "horizontal")
8610				vars.loader.add(vars.nextButton).add(vars.prevButton).addClass('horizontal');
8611
8612			// Configure arrow buttons
8613			vars.BODY[vars.isMobile ? 'addClass' : 'removeClass']('isMobile');
8614
8615			if (!opts.infinite) {
8616				vars.prevButton.add(vars.prevButton).add(vars.innerPrevButton).add(vars.innerNextButton).removeClass('disabled');
8617
8618				if (vars.current == 0)
8619					vars.prevButton.add(vars.innerPrevButton).addClass('disabled');
8620				if (vars.current >= vars.total - 1)
8621					vars.nextButton.add(vars.innerNextButton).addClass('disabled');
8622			}
8623
8624			if (opts.show.effect) {
8625				vars.overlay.stop().fadeIn(opts.show.speed);
8626				vars.toolbar.stop().fadeIn(opts.show.speed);
8627			} else {
8628				vars.overlay.show();
8629				vars.toolbar.show();
8630			}
8631
8632			var length = recognizingItems.length;
8633			if (needRecognition) {
8634				iL.showLoader();
8635
8636				$.each(recognizingItems, function(key, val) {
8637					var resultFnc = function(result) {
8638							var key = -1,
8639								filter = iL.items.filter(function(e, i, arr) {
8640									if (e.URL == result.url)
8641										key = i;
8642
8643									return e.URL == result.url;
8644								}),
8645								self = iL.items[key];
8646
8647							if (result)
8648								$.extend(true, self, {
8649									URL: result.source,
8650									type: result.type,
8651									recognized: true,
8652									options: {
8653										html5video: result.html5video,
8654										width: (result.type == "image") ? 0 : (result.width || self.width),
8655										height: (result.type == "image") ? 0 : (result.height || self.height),
8656										thumbnail: self.options.thumbnail || result.thumbnail
8657									}
8658								});
8659
8660							length--;
8661
8662							if (length == 0) {
8663								iL.hideLoader();
8664
8665								vars.dontGenerateThumbs = false;
8666								iL.generateThumbnails();
8667
8668								if (opts.show.effect)
8669									setTimeout(function() {
8670										iL.generateBoxes();
8671									}, opts.show.speed);
8672								else
8673									iL.generateBoxes();
8674							}
8675						};
8676
8677					iL.ogpRecognition(this, resultFnc);
8678				});
8679			}
8680			else {
8681				if (opts.show.effect)
8682					setTimeout(function() {
8683						iL.generateBoxes();
8684					}, opts.show.speed);
8685				else
8686					iL.generateBoxes();
8687			}
8688
8689			iL.createUI();
8690
8691			window.iLightBox = {
8692				close: function() {
8693					iL.closeAction();
8694				},
8695				fullscreen: function() {
8696					iL.fullScreenAction();
8697				},
8698				moveNext: function() {
8699					iL.moveTo('next');
8700				},
8701				movePrev: function() {
8702					iL.moveTo('prev');
8703				},
8704				goTo: function(index) {
8705					iL.goTo(index);
8706				},
8707				refresh: function() {
8708					iL.refresh();
8709				},
8710				reposition: function() {
8711					(arguments.length > 0) ? iL.repositionPhoto(true): iL.repositionPhoto();
8712				},
8713				setOption: function(options) {
8714					iL.setOption(options);
8715				},
8716				destroy: function() {
8717					iL.closeAction();
8718					iL.dispatchItemsEvents();
8719				}
8720			};
8721
8722			if (opts.linkId) {
8723				vars.hashLock = true;
8724				window.location.hash = opts.linkId + '/' + vars.current;
8725				setTimeout(function() {
8726					vars.hashLock = false;
8727				}, 55);
8728			}
8729
8730			if (!opts.slideshow.startPaused) {
8731				iL.resume();
8732				vars.innerPlayButton.removeClass('ilightbox-play').addClass('ilightbox-pause');
8733			}
8734
8735			//Trigger the onOpen callback
8736			if (typeof iL.options.callback.onOpen == 'function') iL.options.callback.onOpen.call(iL);
8737		},
8738
8739		loadContent: function(obj, opt, speed) {
8740			var iL = this,
8741				holder, item;
8742
8743			iL.createUI();
8744
8745			obj.speed = speed || iL.options.effects.loadedFadeSpeed;
8746
8747			if (opt == 'current') {
8748				if (!obj.options.mousewheel) iL.vars.lockWheel = true;
8749				else iL.vars.lockWheel = false;
8750
8751				if (!obj.options.swipe) iL.vars.lockSwipe = true;
8752				else iL.vars.lockSwipe = false;
8753			}
8754
8755			switch (opt) {
8756				case 'current':
8757					holder = iL.vars.holder, item = iL.vars.current;
8758					break;
8759				case 'next':
8760					holder = iL.vars.nextPhoto, item = iL.vars.next;
8761					break;
8762				case 'prev':
8763					holder = iL.vars.prevPhoto, item = iL.vars.prev;
8764					break;
8765			}
8766
8767			holder.removeAttr('style class').addClass('ilightbox-holder' + (supportTouch ? ' supportTouch' : '')).addClass(obj.options.skin);
8768			$('div.ilightbox-inner-toolbar', holder).remove();
8769
8770			if (obj.title || iL.options.innerToolbar) {
8771				var innerToolbar = iL.vars.innerToolbar.clone();
8772				if (obj.title && iL.options.show.title) {
8773					var title = iL.vars.title.clone();
8774					title.empty().html(obj.title);
8775					innerToolbar.append(title);
8776				}
8777				if (iL.options.innerToolbar) {
8778					innerToolbar.append((iL.vars.total > 1) ? iL.vars.toolbar.clone() : iL.vars.toolbar);
8779				}
8780				holder.prepend(innerToolbar);
8781			}
8782
8783			//console.warn('loadContent', arguments);
8784
8785			iL.loadSwitcher(obj, holder, item, opt);
8786		},
8787
8788		loadSwitcher: function(obj, holder, item, opt) {
8789			var iL = this,
8790				opts = iL.options,
8791				api = {
8792					element: holder,
8793					position: item
8794				};
8795
8796			switch (obj.type) {
8797				case 'image':
8798					//Trigger the onBeforeLoad callback
8799					if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
8800					if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
8801
8802					iL.loadImage(obj.URL, function(img) {
8803						//Trigger the onAfterLoad callback
8804						if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
8805						if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
8806
8807						var width = (img) ? img.width : 400,
8808							height = (img) ? img.height : 200;
8809
8810						holder.data({
8811							naturalWidth: width,
8812							naturalHeight: height
8813						});
8814						$('div.ilightbox-container', holder).empty().append((img) ? '<img src="' + obj.URL + '" class="ilightbox-image" />' : '<span class="ilightbox-alert">' + opts.errors.loadImage + '</span>');
8815
8816						//Trigger the onRender callback
8817						if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
8818						if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
8819
8820						iL.configureHolder(obj, opt, holder);
8821					});
8822
8823					break;
8824
8825				case 'video':
8826					holder.data({
8827						naturalWidth: obj.options.width,
8828						naturalHeight: obj.options.height
8829					});
8830
8831					if (opt === 'current') {
8832						iL.addContent(holder, obj);
8833
8834						//Trigger the onRender callback
8835						if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
8836						if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
8837					} else {
8838						$('div.ilightbox-container', holder).empty();
8839					}
8840
8841					iL.configureHolder(obj, opt, holder);
8842
8843					break;
8844
8845				case 'iframe':
8846					//iL.showLoader();
8847					holder.data({
8848						naturalWidth: obj.options.width,
8849						naturalHeight: obj.options.height
8850					});
8851
8852					iL.configureHolder(obj, opt, holder);
8853
8854					if (opt === 'current') {
8855						var el = iL.addContent(holder, obj);
8856
8857						//Trigger the onRender callback
8858						if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
8859						if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
8860
8861						//Trigger the onBeforeLoad callback
8862						if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
8863						if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
8864
8865						el.bind('load', function() {
8866							//Trigger the onAfterLoad callback
8867							if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
8868							if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
8869
8870							//iL.hideLoader();
8871							el.unbind('load');
8872						});
8873					} else {
8874						$('div.ilightbox-container', holder).empty();
8875					}
8876
8877					break;
8878
8879				case 'inline':
8880					var el = $(obj.URL),
8881						content = iL.addContent(holder, obj),
8882						images = findImageInElement(holder);
8883
8884					holder.data({
8885						naturalWidth: (iL.items[item].options.width || el.outerWidth()),
8886						naturalHeight: (iL.items[item].options.height || el.outerHeight())
8887					});
8888					content.children().eq(0).show();
8889
8890					//Trigger the onRender callback
8891					if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
8892					if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
8893
8894					//Trigger the onBeforeLoad callback
8895					if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
8896					if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
8897
8898					iL.loadImage(images, function() {
8899						//Trigger the onAfterLoad callback
8900						if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
8901						if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
8902
8903						iL.configureHolder(obj, opt, holder);
8904					});
8905
8906					break;
8907
8908				case 'flash':
8909					var el = iL.addContent(holder, obj);
8910
8911					holder.data({
8912						naturalWidth: (iL.items[item].options.width || el.outerWidth()),
8913						naturalHeight: (iL.items[item].options.height || el.outerHeight())
8914					});
8915
8916					//Trigger the onRender callback
8917					if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
8918					if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
8919
8920					iL.configureHolder(obj, opt, holder);
8921
8922					break;
8923
8924				case 'ajax':
8925					var ajax = obj.options.ajax || {};
8926					//Trigger the onBeforeLoad callback
8927					if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
8928					if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
8929
8930					iL.showLoader();
8931					$.ajax({
8932						url: obj.URL || opts.ajaxSetup.url,
8933						data: ajax.data || null,
8934						dataType: ajax.dataType || "html",
8935						type: ajax.type || opts.ajaxSetup.type,
8936						cache: ajax.cache || opts.ajaxSetup.cache,
8937						crossDomain: ajax.crossDomain || opts.ajaxSetup.crossDomain,
8938						global: ajax.global || opts.ajaxSetup.global,
8939						ifModified: ajax.ifModified || opts.ajaxSetup.ifModified,
8940						username: ajax.username || opts.ajaxSetup.username,
8941						password: ajax.password || opts.ajaxSetup.password,
8942						beforeSend: ajax.beforeSend || opts.ajaxSetup.beforeSend,
8943						complete: ajax.complete || opts.ajaxSetup.complete,
8944						success: function(data, textStatus, jqXHR) {
8945							iL.hideLoader();
8946
8947							var el = $(data),
8948								container = $('div.ilightbox-container', holder),
8949								elWidth = iL.items[item].options.width || parseInt(el[0].getAttribute('width')),
8950								elHeight = iL.items[item].options.height || parseInt(el[0].getAttribute('height')),
8951								css = (el[0].getAttribute('width') && el[0].getAttribute('height')) ? {
8952									'overflow': 'hidden'
8953								} : {};
8954
8955							container.empty().append($('<div class="ilightbox-wrapper"></div>').css(css).html(el));
8956							holder.show().data({
8957								naturalWidth: (elWidth || container.outerWidth()),
8958								naturalHeight: (elHeight || container.outerHeight())
8959							}).hide();
8960
8961							//Trigger the onRender callback
8962							if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
8963							if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
8964
8965							var images = findImageInElement(holder);
8966							iL.loadImage(images, function() {
8967								//Trigger the onAfterLoad callback
8968								if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
8969								if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
8970
8971								iL.configureHolder(obj, opt, holder);
8972							});
8973
8974							opts.ajaxSetup.success(data, textStatus, jqXHR);
8975							if (typeof ajax.success == 'function') ajax.success(data, textStatus, jqXHR);
8976						},
8977						error: function(jqXHR, textStatus, errorThrown) {
8978							//Trigger the onAfterLoad callback
8979							if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
8980							if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
8981
8982							iL.hideLoader();
8983							$('div.ilightbox-container', holder).empty().append('<span class="ilightbox-alert">' + opts.errors.loadContents + '</span>');
8984							iL.configureHolder(obj, opt, holder);
8985
8986							opts.ajaxSetup.error(jqXHR, textStatus, errorThrown);
8987							if (typeof ajax.error == 'function') ajax.error(jqXHR, textStatus, errorThrown);
8988						}
8989					});
8990
8991					break;
8992
8993				case 'html':
8994					var object = obj.URL,
8995						el
8996					container = $('div.ilightbox-container', holder);
8997
8998					if (object[0].nodeName) el = object.clone();
8999					else {
9000						var dom = $(object);
9001						if (dom.selector) el = $('<div>' + dom + '</div>');
9002						else el = dom;
9003					}
9004
9005					var elWidth = iL.items[item].options.width || parseInt(el.attr('width')),
9006						elHeight = iL.items[item].options.height || parseInt(el.attr('height'));
9007
9008					iL.addContent(holder, obj);
9009
9010					el.appendTo(document.documentElement).hide();
9011
9012					//Trigger the onRender callback
9013					if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
9014					if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
9015
9016					var images = findImageInElement(holder);
9017
9018					//Trigger the onBeforeLoad callback
9019					if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
9020					if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
9021
9022					iL.loadImage(images, function() {
9023						//Trigger the onAfterLoad callback
9024						if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
9025						if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
9026
9027						holder.show().data({
9028							naturalWidth: (elWidth || container.outerWidth()),
9029							naturalHeight: (elHeight || container.outerHeight())
9030						}).hide();
9031						el.remove();
9032
9033						iL.configureHolder(obj, opt, holder);
9034					});
9035
9036					break;
9037			}
9038		},
9039
9040		configureHolder: function(obj, opt, holder) {
9041			var iL = this,
9042				vars = iL.vars,
9043				opts = iL.options;
9044
9045			if (opt != "current")(opt == "next") ? holder.addClass('ilightbox-next') : holder.addClass('ilightbox-prev');
9046
9047			if (opt == "current")
9048				var item = vars.current;
9049			else if (opt == "next")
9050				var opacity = opts.styles.nextOpacity,
9051					item = vars.next;
9052			else
9053				var opacity = opts.styles.prevOpacity,
9054					item = vars.prev;
9055
9056			var api = {
9057				element: holder,
9058				position: item
9059			};
9060
9061			iL.items[item].options.width = iL.items[item].options.width || 0,
9062			iL.items[item].options.height = iL.items[item].options.height || 0;
9063
9064			if (opt == "current") {
9065				if (opts.show.effect) holder.css(transform, gpuAcceleration).fadeIn(obj.speed, function() {
9066					holder.css(transform, '');
9067					if (obj.caption) {
9068						iL.setCaption(obj, holder);
9069						var caption = $('div.ilightbox-caption', holder),
9070							percent = parseInt((caption.outerHeight() / holder.outerHeight()) * 100);
9071						if (opts.caption.start & percent <= 50) caption.fadeIn(opts.effects.fade
9071Speed);
9072					}
9073
9074					var social = obj.options.social;
9075					if (social) {
9076						iL.setSocial(social, obj.URL, holder);
9077						if (opts.social.start) $('div.ilightbox-social', holder).fadeIn(opts.effects.fadeSpeed);
9078					}
9079
9080					//Generate thumbnails
9081					iL.generateThumbnails();
9082
9083					//Trigger the onShow callback
9084					if (typeof opts.callback.onShow == 'function') opts.callback.onShow.call(iL, iL.ui, item);
9085					if (typeof obj.options.onShow == 'function') obj.options.onShow.call(iL, api);
9086				});
9087				else {
9088					holder.show();
9089
9090					//Generate thumbnails
9091					iL.generateThumbnails();
9092
9093					//Trigger the onShow callback
9094					if (typeof opts.callback.onShow == 'function') opts.callback.onShow.call(iL, iL.ui, item);
9095					if (typeof obj.options.onShow == 'function') obj.options.onShow.call(iL, api);
9096				}
9097			} else {
9098				if (opts.show.effect) holder.fadeTo(obj.speed, opacity, function() {
9099					if (opt == "next") vars.nextLock = false;
9100					else vars.prevLock = false;
9101
9102					//Generate thumbnails
9103					iL.generateThumbnails();
9104
9105					//Trigger the onShow callback
9106					if (typeof opts.callback.onShow == 'function') opts.callback.onShow.call(iL, iL.ui, item);
9107					if (typeof obj.options.onShow == 'function') obj.options.onShow.call(iL, api);
9108				});
9109				else {
9110					holder.css({
9111						opacity: opacity
9112					}).show();
9113					if (opt == "next") vars.nextLock = false;
9114					else vars.prevLock = false;
9115
9116					//Generate thumbnails
9117					iL.generateThumbnails();
9118
9119					//Trigger the onShow callback
9120					if (typeof opts.callback.onShow == 'function') opts.callback.onShow.call(iL, iL.ui, item);
9121					if (typeof obj.options.onShow == 'function') obj.options.onShow.call(iL, api);
9122				}
9123			}
9124
9125			setTimeout(function() {
9126				iL.repositionPhoto();
9127			}, 0);
9128		},
9129
9130		generateBoxes: function() {
9131			var iL = this,
9132				vars = iL.vars,
9133				opts = iL.options;
9134
9135			if (opts.infinite && vars.total >= 3) {
9136				if (vars.current == vars.total - 1) vars.next = 0;
9137				if (vars.current == 0) vars.prev = vars.total - 1;
9138			} else opts.infinite = false;
9139
9140			//UNCODE addition
9141			if ( typeof iL.items[vars.current] == 'undefined' )
9142				vars.current = 0;
9143
9144			iL.loadContent(iL.items[vars.current], 'current', opts.show.speed);
9145
9146			if (iL.items[vars.next]) iL.loadContent(iL.items[vars.next], 'next', opts.show.speed);
9147			if (iL.items[vars.prev]) iL.loadContent(iL.items[vars.prev], 'prev', opts.show.speed);
9148		},
9149
9150		generateThumbnails: function() {
9151			var iL = this,
9152				vars = iL.vars,
9153				opts = iL.options,
9154				timeOut = null;
9155
9156			if (vars.thumbs && !iL.vars.dontGenerateThumbs) {
9157				var thumbnails = vars.thumbnails,
9158					container = $('div.ilightbox-thumbnails-container', thumbnails),
9159					grid = $('div.ilightbox-thumbnails-grid', container),
9160					i = 0;
9161
9162				grid.removeAttr('style').empty();
9163
9164				$.each(iL.items, function(key, val) {
9165					var isActive = (vars.current == key) ? 'ilightbox-active' : '',
9166						opacity = (vars.current == key) ? opts.thumbnails.activeOpacity : opts.thumbnails.normalOpacity,
9167						thumb = val.options.thumbnail,
9168						thumbnail = $('<div class="ilightbox-thumbnail"></div>'),
9169						icon = $('<div class="ilightbox-thumbnail-icon"></div>');
9170
9171					thumbnail.css({
9172						opacity: 0
9173					}).addClass(isActive);
9174
9175					if ((val.type == "video" || val.type == "flash") && typeof val.options.icon == 'undefined') {
9176						icon.addClass('ilightbox-thumbnail-video');
9177						thumbnail.append(icon);
9178					} else if (val.options.icon) {
9179						icon.addClass('ilightbox-thumbnail-' + val.options.icon);
9180						thumbnail.append(icon);
9181					}
9182
9183					if (thumb) iL.loadImage(thumb, function(img) {
9184						i++;
9185						if (img) thumbnail.data({
9186							naturalWidth: img.width,
9187							naturalHeight: img.height
9188						}).append('<img src="' + thumb + '" border="0" />');
9189						else thumbnail.data({
9190							naturalWidth: opts.thumbnails.maxWidth,
9191							naturalHeight: opts.thumbnails.maxHeight
9192						});
9193
9194						clearTimeout(timeOut);
9195						timeOut = setTimeout(function() {
9196							iL.positionThumbnails(thumbnails, container, grid);
9197						}, 20);
9198						setTimeout(function() {
9199							thumbnail.fadeTo(opts.effects.loadedFadeSpeed, opacity);
9200						}, i * 20);
9201					});
9202
9203					grid.append(thumbnail);
9204				});
9205				iL.vars.dontGenerateThumbs = true;
9206			}
9207		},
9208
9209		positionThumbnails: function(thumbnails, container, grid) {
9210			var iL = this,
9211				vars = iL.vars,
9212				opts = iL.options,
9213				viewport = getViewport(),
9214				path = opts.path.toLowerCase();
9215
9216			if (!thumbnails) thumbnails = vars.thumbnails;
9217			if (!container) container = $('div.ilightbox-thumbnails-container', thumbnails);
9218			if (!grid) grid = $('div.ilightbox-thumbnails-grid', container);
9219
9220			var thumbs = $('.ilightbox-thumbnail', grid),
9221				widthAvail = (path == 'horizontal') ? viewport.width - opts.styles.pageOffsetX : thumbs.eq(0).outerWidth() - opts.styles.pageOffsetX,
9222				heightAvail = (path == 'horizontal') ? thumbs.eq(0).outerHeight() - opts.styles.pageOffsetY : viewport.height - opts.styles.pageOffsetY,
9223				gridWidth = (path == 'horizontal') ? 0 : widthAvail,
9224				gridHeight = (path == 'horizontal') ? heightAvail : 0,
9225				active = $('.ilightbox-active', grid),
9226				gridCss = {},
9227				css = {};
9228
9229			if (arguments.length < 3) {
9230				thumbs.css({
9231					opacity: opts.thumbnails.normalOpacity
9232				});
9233				active.css({
9234					opacity: opts.thumbnails.activeOpacity
9235				});
9236			}
9237
9238			thumbs.each(function(i) {
9239				var t = $(this),
9240					data = t.data(),
9241					width = (path == 'horizontal') ? 0 : opts.thumbnails.maxWidth;
9242				height = (path == 'horizontal') ? opts.thumbnails.maxHeight : 0;
9243				dims = iL.getNewDimenstions(width, height, data.naturalWidth, data.naturalHeight, true);
9244
9245				t.css({
9246					width: dims.width,
9247					height: dims.height
9248				});
9249				if (path == 'horizontal') t.css({
9250					'float': 'left'
9251				});
9252
9253				(path == 'horizontal') ? (
9254					gridWidth += t.outerWidth()
9255				) : (
9256					gridHeight += t.outerHeight()
9257				);
9258			});
9259
9260			gridCss = {
9261				width: gridWidth,
9262				height: gridHeight
9263			};
9264
9265			grid.css(gridCss);
9266
9267			gridCss = {};
9268
9269			var gridOffset = grid.offset(),
9270				activeOffset = (active.length) ? active.offset() : {
9271					top: parseInt(heightAvail / 2),
9272					left: parseInt(widthAvail / 2)
9273				};
9274
9275			gridOffset.top = (gridOffset.top - $doc.scrollTop()),
9276				gridOffset.left = (gridOffset.left - $doc.scrollLeft()),
9277				activeOffset.top = (activeOffset.top - gridOffset.top - $doc.scrollTop()),
9278				activeOffset.left = (activeOffset.left - gridOffset.left - $doc.scrollLeft());
9279
9280			(path == 'horizontal') ? (
9281				gridCss.top = 0,
9282				gridCss.left = parseInt((widthAvail / 2) - activeOffset.left - (active.outerWidth() / 2))
9283			) : (
9284				gridCss.top = parseInt(((heightAvail / 2) - activeOffset.top - (active.outerHeight() / 2))),
9285				gridCss.left = 0
9286			);
9287
9288			if (arguments.length < 3) grid.stop().animate(gridCss, opts.effects.repositionSpeed, 'easeOutCirc');
9289			else grid.css(gridCss);
9290		},
9291
9292		loadImage: function(image, callback) {
9293			if (!$.isArray(image)) image = [image];
9294
9295			var iL = this,
9296				length = image.length;
9297
9298			if (length > 0) {
9299				iL.showLoader();
9300				$.each(image, function(index, value) {
9301					var img = new Image();
9302					img.onload = function() {
9303						length -= 1;
9304						if (length == 0) {
9305							iL.hideLoader();
9306							callback(img);
9307						}
9308					};
9309					img.onerror = img.onabort = function() {
9310						length -= 1;
9311						if (length == 0) {
9312							iL.hideLoader();
9313							callback(false);
9314						}
9315					};
9316					img.src = image[index];
9317				});
9318			} else callback(false);
9319		},
9320
9321		patchItemsEvents: function() {
9322			var iL = this,
9323				vars = iL.vars,
9324				clickEvent = ishybrid ? "itap.iL click.iL" : supportTouch ? "itap.iL" : "click.iL",
9325				vEvent = ishybrid ? "click.iL itap.iL" : supportTouch ? "click.iL" : "itap.iL";
9326
9327			if (iL.context && iL.selector) {
9328				var $items = $(iL.selector, iL.context);
9329
9330				$(iL.context).on(clickEvent, iL.selector, function() {
9331					var $this = $(this),
9332						//UNCODE addition
9333						key = typeof $(this).data('lb-index') !== '' && $(this).closest('.owl-item').length ? $(this).data('lb-index') : $items.index($this);
9334					if (UNCODE.isMobile) {
9335						var isCarousel = $this.closest('.owl-carousel');
9336						if (isCarousel.length) {
9337							if (isCarousel.hasClass('owl-translating')) return false;
9338						}
9339					}
9340
9341					vars.current = key;
9342					vars.next = iL.items[key + 1] ? key + 1 : null;
9343					vars.prev = iL.items[key - 1] ? key - 1 : null;
9344
9345					//UNCODE addition
9346					if ( vars.BODY.hasClass('fp-slide-scrolling') )
9347						return false;
9348
9349					iL.addContents();
9350					iL.patchEvents();
9351
9352					return false;
9353				}).on(vEvent, iL.selector, function() {
9354					return false;
9355				});
9356			} else
9357				$.each(iL.itemsObject, function(key, val) {
9358					val.on(clickEvent, function() {
9359						vars.current = key;
9360						vars.next = iL.items[key + 1] ? key + 1 : null;
9361						vars.prev = iL.items[key - 1] ? key - 1 : null;
9362
9363						iL.addContents();
9364						iL.patchEvents();
9365
9366						return false;
9367					}).on(vEvent, function() {
9368						return false;
9369					});
9370				});
9371		},
9372
9373		dispatchItemsEvents: function() {
9374			var iL = this,
9375				vars = iL.vars,
9376				opts = iL.options;
9377
9378			if (iL.context && iL.selector)
9379				$(iL.context).off('.iL', iL.selector);
9380			else
9381				$.each(iL.itemsObject, function(key, val) {
9382					val.off('.iL');
9383				});
9384		},
9385
9386		refresh: function() {
9387			var iL = this;
9388
9389			iL.dispatchItemsEvents();
9390			iL.attachItems();
9391			iL.normalizeItems();
9392			iL.patchItemsEvents();
9393		},
9394
9395		patchEvents: function() {
9396			var iL = this,
9397				vars = iL.vars,
9398				opts = iL.options,
9399				path = opts.path.toLowerCase(),
9400				holders = $('.ilightbox-holder'),
9401				fullscreenEvent = fullScreenApi.fullScreenEventName + '.iLightBox',
9402				durationThreshold = 1000,
9403				horizontalDistanceThreshold =
9404				verticalDistanceThreshold = 100,
9405				buttonsArray = [vars.nextButton[0], vars.prevButton[0], vars.nextButton[0].firstChild, vars.prevButton[0].firstChild];
9406
9407			$win.bind('resize.iLightBox', function() {
9408				var viewport = getViewport();
9409
9410				if (opts.mobileOptimizer && !opts.innerToolbar) vars.isMobile = viewport.width <= vars.mobileMaxWidth;
9411				vars.BODY[vars.isMobile ? 'addClass' : 'removeClass']('isMobile');
9412
9413				iL.repositionPhoto(null);
9414				if (supportTouch) {
9415					clearTimeout(vars.setTime);
9416					vars.setTime = setTimeout(function() {
9417						var scrollTop = getScrollXY().y;
9418						window.scrollTo(0, scrollTop - 30);
9419						window.scrollTo(0, scrollTop + 30);
9420						window.scrollTo(0, scrollTop);
9421					}, 2000);
9422				}
9423				if (vars.thumbs) iL.positionThumbnails();
9424			}).bind('keydown.iLightBox', function(event) {
9425				if (opts.controls.keyboard) {
9426					switch (event.keyCode) {
9427						case 13:
9428							if (event.shiftKey && opts.keyboard.shift_enter) iL.fullScreenAction();
9429							break;
9430						case 27:
9431							if (opts.keyboard.esc) iL.closeAction();
9432							break;
9433						case 37:
9434							if (opts.keyboard.left && !vars.lockKey) iL.moveTo('prev');
9435							break;
9436						case 38:
9437							if (opts.keyboard.up && !vars.lockKey) iL.moveTo('prev');
9438							break;
9439						case 39:
9440							if (opts.keyboard.right && !vars.lockKey) iL.moveTo('next');
9441							break;
9442						case 40:
9443							if (opts.keyboard.down && !vars.lockKey) iL.moveTo('next');
9444							break;
9445					}
9446				}
9447			});
9448
9449			if (fullScreenApi.supportsFullScreen) $win.bind(fullscreenEvent, function() {
9450				iL.doFullscreen();
9451			});
9452
9453			var holderEventsArr = [opts.caption.show + '.iLightBox', opts.caption.hide + '.iLightBox', opts.social.show + '.iLightBox', opts.social.hide + '.iLightBox'].filter(function(e, i, arr) {
9454					return arr.lastIndexOf(e) === i;
9455				}),
9456				holderEvents = "";
9457
9458			$.each(holderEventsArr, function(key, val) {
9459				if (key != 0) holderEvents += ' ';
9460				holderEvents += val;
9461			});
9462
9463			$doc.on(clickEvent, '.ilightbox-overlay', function() {
9464				if (opts.overlay.blur) iL.closeAction();
9465			}).on(clickEvent, '.ilightbox-next, .ilightbox-next-button', function() {
9466				iL.moveTo('next');
9467			}).on(clickEvent, '.ilightbox-prev, .ilightbox-prev-button', function() {
9468				iL.moveTo('prev');
9469			}).on(clickEvent, '.ilightbox-thumbnail', function() {
9470				var t = $(this),
9471					thumbs = $('.ilightbox-thumbnail', vars.thumbnails),
9472					index = thumbs.index(t);
9473
9474				if (index != vars.current) iL.goTo(index);
9475			}).on(holderEvents, '.ilightbox-holder:not(.ilightbox-next, .ilightbox-prev)', function(e) {
9476				var caption = $('div.ilightbox-caption', vars.holder),
9477					social = $('div.ilightbox-social', vars.holder),
9478					fadeSpeed = opts.effects.fadeSpeed;
9479
9480				if (vars.nextLock || vars.prevLock) {
9481					if (e.type == opts.caption.show && !caption.is(':visible')) caption.fadeIn(fadeSpeed);
9482					else if (e.type == opts.caption.hide && caption.is(':visible')) caption.fadeOut(fadeSpeed);
9483
9484					if (e.type == opts.social.show && !social.is(':visible')) social.fadeIn(fadeSpeed);
9485					else if (e.type == opts.social.hide && social.is(':visible')) social.fadeOut(fadeSpeed);
9486				} else {
9487					if (e.type == opts.caption.show && !caption.is(':visible')) caption.stop().fadeIn(fadeSpeed);
9488					else if (e.type == opts.caption.hide && caption.is(':visible')) caption.stop().fadeOut(fadeSpeed);
9489
9490					if (e.type == opts.social.show && !social.is(':visible')) social.stop().fadeIn(fadeSpeed);
9491					else if (e.type == opts.social.hide && social.is(':visible')) social.stop().fadeOut(fadeSpeed);
9492				}
9493			}).on('mouseenter.iLightBox mouseleave.iLightBox', '.ilightbox-wrapper', function(e) {
9494				if (e.type == 'mouseenter') vars.lockWheel = true;
9495				else vars.lockWheel = false;
9496			}).on(clickEvent, '.ilightbox-toolbar a.ilightbox-close, .ilightbox-toolbar a.ilightbox-fullscreen, .ilightbox-toolbar a.ilightbox-play, .ilightbox-toolbar a.ilightbox-pause', function() {
9497				var t = $(this);
9498
9499				if (t.hasClass('ilightbox-fullscreen')) iL.fullScreenAction();
9500				else if (t.hasClass('ilightbox-play')) {
9501					iL.resume();
9502					t.addClass('ilightbox-pause').removeClass('ilightbox-play');
9503				} else if (t.hasClass('ilightbox-pause')) {
9504					iL.pause();
9505					t.addClass('ilightbox-play').removeClass('ilightbox-pause');
9506				} else iL.closeAction();
9507			}).on(touchMoveEvent, '.ilightbox-overlay, .ilightbox-thumbnails-container', function(e) {
9508				// prevent scrolling
9509				e.preventDefault();
9510			});
9511
9512			function mouseMoveHandler(e) {
9513				if (!vars.isMobile) {
9514					if (!vars.mouseID) {
9515						vars.hideableElements.show();
9516					}
9517
9518					vars.mouseID = clearTimeout(vars.mouseID);
9519
9520					if (buttonsArray.indexOf(e.target) === -1)
9521						vars.mouseID = setTimeout(function() {
9522							vars.hideableElements.hide();
9523							vars.mouseID = clearTimeout(vars.mouseID);
9524						}, 3000);
9525				}
9526			}
9527
9528			if (opts.controls.arrows && !supportTouch) $doc.on('mousemove.iLightBox', mouseMoveHandler);
9529
9530			if (opts.controls.slideshow && opts.slideshow.pauseOnHover) $doc.on('mouseenter.iLightBox mouseleave.iLightBox', '.ilightbox-holder:not(.ilightbox-next, .ilightbox-prev)', function(e) {
9531				if (e.type == 'mouseenter' && vars.cycleID) iL.pause();
9532				else if (e.type == 'mouseleave' && vars.isPaused) iL.resume();
9533			});
9534
9535			var switchers = $('.ilightbox-overlay, .ilightbox-holder, .ilightbox-thumbnails');
9536
9537			if (opts.controls.mousewheel) switchers.on('mousewheel.iLightBox', function(event, delta) {
9538				if (!vars.lockWheel) {
9539					event.preventDefault();
9540					if (delta < 0) iL.moveTo('next');
9541					else if (delta > 0) iL.moveTo('prev');
9542				}
9543			});
9544
9545			if (opts.controls.swipe) holders.on(touchStartEvent, function(event) {
9546				if (vars.nextLock || vars.prevLock || vars.total == 1 || vars.lockSwipe) return;
9547
9548				vars.BODY.addClass('ilightbox-closedhand');
9549
9550				var data = event.originalEvent.touches ? event.originalEvent.touches[0] : event,
9551					scrollTop = $doc.scrollTop(),
9552					scrollLeft = $doc.scrollLeft(),
9553					offsets = [
9554						holders.eq(0).offset(),
9555						holders.eq(1).offset(),
9556						holders.eq(2).offset()
9557					],
9558					offSet = [{
9559						top: offsets[0].top - scrollTop,
9560						left: offsets[0].left - scrollLeft
9561					}, {
9562						top: offsets[1].top - scrollTop,
9563						left: offsets[1].left - scrollLeft
9564					}, {
9565						top: offsets[2].top - scrollTop,
9566						left: offsets[2].left - scrollLeft
9567					}],
9568					start = {
9569						time: (new Date()).getTime(),
9570						coords: [data.pageX - scrollLeft, data.pageY - scrollTop]
9571					},
9572					stop;
9573
9574				function moveEachHandler(i) {
9575					var t = $(this),
9576						offset = offSet[i],
9577						scroll = [(start.coords[0] - stop.coords[0]), (start.coords[1] - stop.coords[1])];
9578
9579					t[0].style[path == "horizontal" ? 'left' : 'top'] = (path == "horizontal" ? offset.left - scroll[0] : offset.top - scroll[1]) + 'px';
9580				}
9581
9582				function moveHandler(event) {
9583
9584					if (!start) return;
9585
9586					var data = event.originalEvent.touches ? event.originalEvent.touches[0] : event;
9587
9588					stop = {
9589						time: (new Date()).getTime(),
9590						coords: [data.pageX - scrollLeft, data.pageY - scrollTop]
9591					};
9592
9593					holders.each(moveEachHandler);
9594
9595					// prevent scrolling
9596					event.preventDefault();
9597				}
9598
9599				function repositionHolders() {
9600					holders.each(function() {
9601						var t = $(this),
9602							offset = t.data('offset') || {
9603								top: t.offset().top - scrollTop,
9604								left: t.offset().left - scrollLeft
9605							},
9606							top = offset.top,
9607							left = offset.left;
9608
9609						t.css(transform, gpuAcceleration).stop().animate({
9610							top: top,
9611							left: left
9612						}, 500, 'easeOutCirc', function() {
9613							t.css(transform, '');
9614						});
9615					});
9616				}
9617
9618				holders.bind(touchMoveEvent, moveHandler);
9619				$doc.one(touchStopEvent, function(event) {
9620					holders.unbind(touchMoveEvent, moveHandler);
9621
9622					vars.BODY.removeClass('ilightbox-closedhand');
9623
9624					if (start && stop) {
9625						if (path == "horizontal" && stop.time - start.time < durationThreshold && abs(start.coords[0] - stop.coords[0]) > horizontalDistanceThreshold && abs(start.coords[1] - stop.coords[1]) < verticalDistanceThreshold) {
9626							if (start.coords[0] > stop.coords[0]) {
9627								if (vars.current == vars.total - 1 && !opts.infinite) repositionHolders();
9628								else {
9629									vars.isSwipe = true;
9630									iL.moveTo('next');
9631								}
9632							} else {
9633								if (vars.current == 0 && !opts.infinite) repositionHolders();
9634								else {
9635									vars.isSwipe = true;
9636									iL.moveTo('prev');
9637								}
9638							}
9639						} else if (path == "vertical" && stop.time - start.time < durationThreshold && abs(start.coords[1] - stop.coords[1]) > horizontalDistanceThreshold && abs(start.coords[0] - stop.coords[0]) < verticalDistanceThreshold) {
9640							if (start.coords[1] > stop.coords[1]) {
9641								if (vars.current == vars.total - 1 && !opts.infinite) repositionHolders();
9642								else {
9643									vars.isSwipe = true;
9644									iL.moveTo('next');
9645								}
9646							} else {
9647								if (vars.current == 0 && !opts.infinite) repositionHolders();
9648								else {
9649									vars.isSwipe = true;
9650									iL.moveTo('prev');
9651								}
9652							}
9653						} else repositionHolders();
9654					}
9655					start = stop = undefined;
9656				});
9657			});
9658
9659		},
9660
9661		goTo: function(index) {
9662			var iL = this,
9663				vars = iL.vars,
9664				opts = iL.options,
9665				diff = (index - vars.current);
9666
9667			if (opts.infinite) {
9668				if (index == vars.total - 1 && vars.current == 0) diff = -1;
9669				if (vars.current == vars.total - 1 && index == 0) diff = 1;
9670			}
9671
9672			if (diff == 1) iL.moveTo('next');
9673			else if (diff == -1) iL.moveTo('prev');
9674			else {
9675
9676				if (vars.nextLock || vars.prevLock) return false;
9677
9678				//Trigger the onBeforeChange callback
9679				if (typeof opts.callback.onBeforeChange == 'function') opts.callback.onBeforeChange.call(iL, iL.ui);
9680
9681				if (opts.linkId) {
9682					vars.hashLock = true;
9683					window.location.hash = opts.linkId + '/' + index;
9684				}
9685
9686				if (iL.items[index]) {
9687					if (!iL.items[index].options.mousewheel) vars.lockWheel = true;
9688					else iL.vars.lockWheel = false;
9689
9690					if (!iL.items[index].options.swipe) vars.lockSwipe = true;
9691					else vars.lockSwipe = false;
9692				}
9693
9694				$.each([vars.holder, vars.nextPhoto, vars.prevPhoto], function(key, val) {
9695					val.css(transform, gpuAcceleration).fadeOut(opts.effects.loadedFadeSpeed);
9696				});
9697
9698				vars.current = index;
9699				vars.next = index + 1;
9700				vars.prev = index - 1;
9701
9702				iL.createUI();
9703
9704				setTimeout(function() {
9705					iL.generateBoxes();
9706				}, opts.effects.loadedFadeSpeed + 50);
9707
9708				$('.ilightbox-thumbnail', vars.thumbnails).removeClass('ilightbox-active').eq(index).addClass('ilightbox-active');
9709				iL.positionThumbnails();
9710
9711				if (opts.linkId) setTimeout(function() {
9712					vars.hashLock = false;
9713				}, 55);
9714
9715				// Configure arrow buttons
9716				if (!opts.infinite) {
9717					vars.nextButton.add(vars.prevButton).add(vars.innerPrevButton).add(vars.innerNextButton).removeClass('disabled');
9718
9719					if (vars.current == 0) {
9720						vars.prevButton.add(vars.innerPrevButton).addClass('disabled');
9721					}
9722					if (vars.current >= vars.total - 1) {
9723						vars.nextButton.add(vars.innerNextButton).addClass('disabled');
9724					}
9725				}
9726
9727				// Reset next cycle timeout
9728				iL.resetCycle();
9729
9730				//Trigger the onAfterChange callback
9731				if (typeof opts.callback.onAfterChange == 'function') opts.callback.onAfterChange.call(iL, iL.ui);
9732			}
9733		},
9734
9735		moveTo: function(side) {
9736			var iL = this,
9737				vars = iL.vars,
9738				opts = iL.options,
9739				path = opts.path.toLowerCase(),
9740				viewport = getViewport(),
9741				switchSpeed = opts.effects.switchSpeed;
9742
9743			if (vars.nextLock || vars.prevLock) return false;
9744			else {
9745
9746				var item = (side == "next") ? vars.next : vars.prev;
9747
9748				if (opts.linkId) {
9749					vars.hashLock = true;
9750					window.location.hash = opts.linkId + '/' + item;
9751				}
9752
9753				if (side == "next") {
9754					if (!iL.items[item]) return false;
9755					var firstHolder = vars.nextPhoto,
9756						secondHolder = vars.holder,
9757						lastHolder = vars.prevPhoto,
9758						firstClass = 'ilightbox-prev',
9759						secondClass = 'ilightbox-next';
9760				} else if (side == "prev") {
9761					if (!iL.items[item]) return false;
9762					var firstHolder = vars.prevPhoto,
9763						secondHolder = vars.holder,
9764						lastHolder = vars.nextPhoto,
9765						firstClass = 'ilightbox-next',
9766						secondClass = 'ilightbox-prev';
9767				}
9768
9769				//Trigger the onBeforeChange callback
9770				if (typeof opts.callback.onBeforeChange == 'function')
9771					opts.callback.onBeforeChange.call(iL, iL.ui);
9772
9773				(side == "next") ? vars.nextLock = true: vars.prevLock = true;
9774
9775				var captionFirst = $('div.ilightbox-caption', secondHolder),
9776					socialFirst = $('div.ilightbox-social', secondHolder);
9777
9778				if (captionFirst.length)
9779					captionFirst.stop().fadeOut(switchSpeed, function() {
9780						$(this).remove();
9781					});
9782				if (socialFirst.length)
9783					socialFirst.stop().fadeOut(switchSpeed, function() {
9784						$(this).remove();
9785					});
9786
9787				if (iL.items[item].caption) {
9788					iL.setCaption(iL.items[item], firstHolder);
9789					var caption = $('div.ilightbox-caption', firstHolder),
9790						percent = parseInt((caption.outerHeight() / firstHolder.outerHeight()) * 100);
9791					if (opts.caption.start && percent <= 50) caption.fadeIn(switchSpeed);
9792				}
9793
9794				var social = iL.items[item].options.social;
9795				if (social) {
9796					iL.setSocial(social, iL.items[item].URL, firstHolder);
9797					if (opts.social.start) $('div.ilightbox-social', firstHolder).fadeIn(opts.effects.fadeSpeed);
9798				}
9799
9800				$.each([firstHolder, secondHolder, lastHolder], function(key, val) {
9801					val.removeClass('ilightbox-next ilightbox-prev');
9802				});
9803
9804				var firstOffset = firstHolder.data('offset'),
9805					winW = (viewport.width - (opts.styles.pageOffsetX)),
9806					winH = (viewport.height - (opts.styles.pageOffsetY)),
9807					width = firstOffset.newDims.width,
9808					height = firstOffset.newDims.height,
9809					thumbsOffset = firstOffset.thumbsOffset,
9810					diff = firstOffset.diff,
9811					top = parseInt((winH / 2) - (height / 2) - diff.H - (thumbsOffset.H / 2)),
9812					left = parseInt((winW / 2) - (width / 2) - diff.W - (thumbsOffset.W / 2));
9813
9814				var secondOffset = secondHolder.data('offset'),
9815					object = secondOffset.object;
9816
9817				/*if (object.item.caption && !isNaN(width) && !isNaN(height) && UNCODE.wwidth > UNCODE.mediaQuery) {
9818					var objRatio = width / height;
9819					height = height - vars.captionHeight;
9820					width = height * objRatio;
9821					top = path == 'horizontal' ? parseInt((winH / 2) - (height / 2) - diff.H - (thumbsOffset.H / 2)) : top,
9822					left = parseInt((winW / 2) - (width / 2) - diff.W - (thumbsOffset.W / 2));
9823				}*/
9824
9825				firstHolder.show().css(transform, gpuAcceleration).animate({
9826					top: top,
9827					left: left,
9828					opacity: 1
9829				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc', function() {
9830					firstHolder.css(transform, '');
9831				});
9832
9833				$('div.ilightbox-container', firstHolder).animate({
9834					width: width,
9835					height: height
9836				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc');
9837
9838				diff = secondOffset.diff;
9839
9840				width = secondOffset.newDims.width,
9841					height = secondOffset.newDims.height;
9842
9843				width = parseInt(width * opts.styles[side == 'next' ? 'prevScale' : 'nextScale']),
9844					height = parseInt(height * opts.styles[side == 'next' ? 'prevScale' : 'nextScale']),
9845					top = (path == 'horizontal') ? parseInt((winH / 2) - object.offsetY - (height / 2) - diff.H - (thumbsOffset.H / 2)) : parseInt(winH - object.offsetX - diff.H - (thumbsOffset.H / 2));
9846
9847				if (side == 'prev')
9848					left = (path == 'horizontal') ? parseInt(winW - object.offsetX - diff.W - (thumbsOffset.W / 2)) : parseInt((winW / 2) - (width / 2) - diff.W - object.offsetY - (thumbsOffset.W / 2));
9849				else {
9850					top = (path == 'horizontal') ? top : parseInt(object.offsetX - diff.H - height - (thumbsOffset.H / 2)),
9851					left = (path == 'horizontal') ? parseInt(object.offsetX - diff.W - width - (thumbsOffset.W / 2)) : parseInt((winW / 2) - object.offsetY - (width / 2) - diff.W - (thumbsOffset.W / 2));
9852				}
9853
9854				/*if (object.item.caption && !isNaN(width) && !isNaN(height) && UNCODE.wwidth > UNCODE.mediaQuery) {
9855					var objRatio = width / height;
9856					height = height - vars.captionHeight;
9857					width = height * objRatio;
9858					top = path == 'horizontal' ? parseInt((winH / 2) - (height / 2) - diff.H - (thumbsOffset.H / 2)) : top;
9859				}*/
9860
9861				$('div.ilightbox-container', secondHolder).animate({
9862					width: width,
9863					height: height
9864				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc');
9865
9866				secondHolder.addClass(firstClass).css(transform, gpuAcceleration).animate({
9867					top: top,
9868					left: left,
9869					opacity: opts.styles.prevOpacity,
9870				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc', function() {
9871					secondHolder.css(transform, '');
9872
9873					$('.ilightbox-thumbnail', vars.thumbnails).removeClass('ilightbox-active').eq(item).addClass('ilightbox-active');
9874					iL.positionThumbnails();
9875
9876					if (iL.items[item]) {
9877						if (!iL.items[item].options.mousewheel) vars.lockWheel = true;
9878						else vars.lockWheel = false;
9879
9880						if (!iL.items[item].options.swipe) vars.lockSwipe = true;
9881						else vars.lockSwipe = false;
9882					}
9883
9884					vars.isSwipe = false;
9885
9886					// Remove iframe & video from previous slide
9887					if (['iframe', 'video'].indexOf(iL.items[vars.current].type) !== -1) {
9888						$('div.ilightbox-container', secondHolder).empty();
9889					}
9890
9891					if (side == "next") {
9892						vars.nextPhoto = lastHolder,
9893							vars.prevPhoto = secondHolder,
9894							vars.holder = firstHolder;
9895
9896						vars.nextPhoto.hide();
9897
9898						vars.next = vars.next + 1,
9899							vars.prev = vars.current,
9900							vars.current = vars.current + 1;
9901
9902						if (opts.infinite) {
9903							if (vars.current > vars.total - 1) vars.current = 0;
9904							if (vars.current == vars.total - 1) vars.next = 0;
9905							if (vars.current == 0) vars.prev = vars.total - 1;
9906						}
9907
9908						iL.createUI();
9909
9910						if (!iL.items[vars.next])
9911							vars.nextLock = false;
9912						else
9913							iL.loadContent(iL.items[vars.next], 'next');
9914					} else {
9915						vars.prevPhoto = lastHolder;
9916						vars.nextPhoto = secondHolder;
9917						vars.holder = firstHolder;
9918
9919						vars.prevPhoto.hide();
9920
9921						vars.next = vars.current;
9922						vars.current = vars.prev;
9923						vars.prev = vars.current - 1;
9924
9925						if (opts.infinite) {
9926							if (vars.current == vars.total - 1) vars.next = 0;
9927							if (vars.current == 0) vars.prev = vars.total - 1;
9928						}
9929
9930						iL.createUI();
9931
9932						if (!iL.items[vars.prev])
9933							vars.prevLock = false;
9934						else
9935							iL.loadContent(iL.items[vars.prev], 'prev');
9936					}
9937
9938					// Add iframe & video content for current slide
9939					if (['iframe', 'video'].indexOf(iL.items[vars.current].type) !== -1) {
9940						iL.loadContent(iL.items[vars.current], 'current');
9941					}
9942
9943					if (opts.linkId) setTimeout(function() {
9944						vars.hashLock = false;
9945					}, 55);
9946
9947					// Configure arrow buttons
9948					if (!opts.infinite) {
9949						vars.nextButton.add(vars.prevButton).add(vars.innerPrevButton).add(vars.innerNextButton).removeClass('disabled');
9950
9951						if (vars.current == 0)
9952							vars.prevButton.add(vars.innerPrevButton).addClass('disabled');
9953						if (vars.current >= vars.total - 1)
9954							vars.nextButton.add(vars.innerNextButton).addClass('disabled');
9955					}
9956
9957					iL.repositionPhoto();
9958
9959					// Reset next cycle timeout
9960					iL.resetCycle();
9961
9962					//Trigger the onAfterChange callback
9963					if (typeof opts.callback.onAfterChange == 'function')
9964						opts.callback.onAfterChange.call(iL, iL.ui);
9965				});
9966
9967				top = (path == 'horizontal') ? getPixel(lastHolder, 'top') : ((side == "next") ? parseInt(-(winH / 2) - lastHolder.outerHeight()) : parseInt(top * 2)),
9968					left = (path == 'horizontal') ? ((side == "next") ? parseInt(-(winW / 2) - lastHolder.outerWidth()) : parseInt(left * 2)) : getPixel(lastHolder, 'left');
9969
9970				lastHolder.css(transform, gpuAcceleration).animate({
9971					top: top,
9972					left: left,
9973					opacity: opts.styles.nextOpacity
9974				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc', function() {
9975					lastHolder.css(transform, '');
9976				}).addClass(secondClass);
9977			}
9978		},
9979
9980		setCaption: function(obj, target) {
9981			var iL = this,
9982				caption = $('<div class="ilightbox-caption"></div>');
9983
9984			if (obj.caption) {
9985				caption.html(obj.caption);
9986				$('div.ilightbox-container', target).append(caption);
9987			}
9988		},
9989
9990		normalizeSocial: function(obj, url) {
9991			var iL = this,
9992				vars = iL.vars,
9993				opts = iL.options,
9994				baseURL = window.location.href;
9995
9996			$.each(obj, function(key, value) {
9997				if (!value)
9998					return true;
9999
10000				var item = key.toLowerCase(),
10001					source, text;
10002
10003				switch (item) {
10004					case 'facebook':
10005						source = "http://www.facebook.com/share.php?v=4&src=bm&u={URL}",
10006							text = "Share on Facebook";
10007						break;
10008					case 'twitter':
10009						source = "http://twitter.com/home?status={URL}",
10010							text = "Share on Twitter";
10011						break;
10012					case 'googleplus':
10013						source = "https://plus.google.com/share?url={URL}",
10014							text = "Share on Google+";
10015						break;
10016					case 'delicious':
10017						source = "http://delicious.com/post?url={URL}",
10018							text = "Share on Delicious";
10019						break;
10020					case 'digg':
10021						source = "http://digg.com/submit?phase=2&url={URL}",
10022							text = "Share on Digg";
10023						break;
10024					case 'reddit':
10025						source = "http://reddit.com/submit?url={URL}",
10026							text = "Share on reddit";
10027						break;
10028				}
10029
10030				obj[key] = {
10031					URL: value.URL && absolutizeURI(baseURL, value.URL) || opts.linkId && window.location.href || typeof url !== 'string' && baseURL || url && absolutizeURI(baseURL, url) || baseURL,
10032					source: value.source || source || value.URL && absolutizeURI(baseURL, value.URL) || url && absolutizeURI(baseURL, url),
10033					text: value.text || text || "Share on " + key,
10034					width: (typeof(value.width) != 'undefined' && !isNaN(value.width)) ? parseInt(value.width) : 640,
10035					height: value.height || 360
10036				};
10037
10038			});
10039
10040			return obj;
10041		},
10042
10043		setSocial: function(obj, url, target) {
10044			var iL = this,
10045				socialBar = $('<div class="ilightbox-social"></div>'),
10046				buttons = '<ul>';
10047
10048			obj = iL.normalizeSocial(obj, url);
10049
10050			$.each(obj, function(key, value) {
10051				var item = key.toLowerCase(),
10052					source = value.source.replace(/\{URL\}/g, encodeURIComponent(value.URL).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A').replace(/%20/g, '+'));
10053				buttons += '<li class="' + key + '"><a href="' + source + '" onclick="javascript:window.open(this.href' + ((value.width <= 0 || value.height <= 0) ? '' : ', \'\', \'menubar=no,toolbar=no,resizable=yes,scrollbars=yes,height=' + value.height + ',width=' + value.width + ',left=40,top=40\'') + ');return false;" title="' + value.text + '" target="_blank"></a></li>';
10054			});
10055
10056			buttons += '</ul>';
10057
10058			socialBar.html(buttons);
10059			$('div.ilightbox-container', target).append(socialBar);
10060		},
10061
10062		fullScreenAction: function() {
10063			var iL = this,
10064				vars = iL.vars;
10065
10066			if (fullScreenApi.supportsFullScreen) {
10067				if (fullScreenApi.isFullScreen()) fullScreenApi.cancelFullScreen(document.documentElement);
10068				else fullScreenApi.requestFullScreen(document.documentElement);
10069			} else {
10070				iL.doFullscreen();
10071			}
10072		},
10073
10074		doFullscreen: function() {
10075			var iL = this,
10076				vars = iL.vars,
10077				viewport = getViewport(),
10078				opts = iL.options;
10079
10080			if (opts.fullAlone) {
10081				var currentHolder = vars.holder,
10082					current = iL.items[vars.current],
10083					windowWidth = viewport.width,
10084					windowHeight = viewport.height,
10085					elements = [currentHolder, vars.nextPhoto, vars.prevPhoto, vars.nextButton, vars.prevButton, vars.overlay, vars.toolbar, vars.thumbnails, vars.loader],
10086					hideElements = [vars.nextPhoto, vars.prevPhoto, vars.nextButton, vars.prevButton, vars.loader, vars.thumbnails];
10087
10088				if (!vars.isInFullScreen) {
10089					vars.isInFullScreen = vars.lockKey = vars.lockWheel = vars.lockSwipe = true;
10090					vars.overlay.css({
10091						opacity: 1
10092					});
10093
10094					$.each(hideElements, function(i, element) {
10095						element.hide();
10096					});
10097
10098					vars.fullScreenButton.attr('title', opts.text.exitFullscreen);
10099
10100					if (opts.fullStretchTypes.indexOf(current.type) != -1) currentHolder.data({
10101						naturalWidthOld: currentHolder.data('naturalWidth'),
10102						naturalHeightOld: currentHolder.data('naturalHeight'),
10103						naturalWidth: windowWidth,
10104						naturalHeight: windowHeight
10105					});
10106					else {
10107						var viewport = current.options.fullViewPort || opts.fullViewPort || '',
10108							newWidth = windowWidth,
10109							newHeight = windowHeight,
10110							width = currentHolder.data('naturalWidth'),
10111							height = currentHolder.data('naturalHeight');
10112
10113						if (viewport.toLowerCase() == 'fill') {
10114							newHeight = (newWidth / width) * height;
10115
10116							if (newHeight < windowHeight) {
10117								newWidth = (windowHeight / height) * width,
10118									newHeight = windowHeight;
10119							}
10120						} else if (viewport.toLowerCase() == 'fit') {
10121							var dims = iL.getNewDimenstions(newWidth, newHeight, width, height, true);
10122
10123							newWidth = dims.width,
10124								newHeight = dims.height;
10125						} else if (viewport.toLowerCase() == 'stretch') {
10126							newWidth = newWidth,
10127								newHeight = newHeight;
10128						} else {
10129							var scale = (width > newWidth || height > newHeight) ? true : false,
10130								dims = iL.getNewDimenstions(newWidth, newHeight, width, height, scale);
10131							newWidth = dims.width,
10132								newHeight = dims.height;
10133						}
10134
10135						currentHolder.data({
10136							naturalWidthOld: currentHolder.data('naturalWidth'),
10137							naturalHeightOld: currentHolder.data('naturalHeight'),
10138							naturalWidth: newWidth,
10139							naturalHeight: newHeight
10140						});
10141					}
10142
10143					$.each(elements, function(key, val) {
10144						val.addClass('ilightbox-fullscreen');
10145					});
10146
10147					//Trigger the onEnterFullScreen callback
10148					if (typeof opts.callback.onEnterFullScreen == 'function') opts.callback.onEnterFullScreen.call(iL, iL.ui);
10149				} else {
10150					vars.isInFullScreen = vars.lockKey = vars.lockWheel = vars.lockSwipe = false;
10151					vars.overlay.css({
10152						opacity: iL.options.overlay.opacity
10153					});
10154
10155					$.each(hideElements, function(i, element) {
10156						element.show();
10157					});
10158
10159					vars.fullScreenButton.attr('title', opts.text.enterFullscreen);
10160
10161					currentHolder.data({
10162						naturalWidth: currentHolder.data('naturalWidthOld'),
10163						naturalHeight: currentHolder.data('naturalHeightOld'),
10164						naturalWidthOld: null,
10165						naturalHeightOld: null
10166					});
10167
10168					$.each(elements, function(key, val) {
10169						val.removeClass('ilightbox-fullscreen');
10170					});
10171
10172					//Trigger the onExitFullScreen callback
10173					if (typeof opts.callback.onExitFullScreen == 'function') opts.callback.onExitFullScreen.call(iL, iL.ui);
10174				}
10175			} else {
10176				if (!vars.isInFullScreen) vars.isInFullScreen = true;
10177				else vars.isInFullScreen = false;
10178			}
10179			iL.repositionPhoto(true);
10180		},
10181
10182		closeAction: function() {
10183			var iL = this,
10184				vars = iL.vars,
10185				opts = iL.options;
10186
10187			$win.unbind('.iLightBox');
10188			$doc.off('.iLightBox');
10189
10190			if (vars.isInFullScreen) fullScreenApi.cancelFullScreen(document.documentElement);
10191
10192			$('.ilightbox-overlay, .ilightbox-holder, .ilightbox-thumbnails').off('.iLightBox');
10193
10194			if (opts.hide.effect) vars.overlay.stop().fadeOut(opts.hide.speed, function() {
10195				vars.overlay.remove();
10196				vars.BODY.removeClass('ilightbox-noscroll').off('.iLightBox');
10197			});
10198			else {
10199				vars.overlay.remove();
10200				vars.BODY.removeClass('ilightbox-noscroll').off('.iLightBox');
10201			}
10202
10203			var fadeOuts = [vars.toolbar, vars.holder, vars.nextPhoto, vars.prevPhoto, vars.nextButton, vars.prevButton, vars.loader, vars.thumbnails];
10204
10205			$.each(fadeOuts, function(i, element) {
10206				element.removeAttr('style').remove();
10207			});
10208
10209			vars.dontGenerateThumbs = vars.isInFullScreen = false;
10210
10211			window.iLightBox = null;
10212
10213			if (opts.linkId) {
10214				vars.hashLock = true;
10215				removeHash();
10216				setTimeout(function() {
10217					vars.hashLock = false;
10218				}, 55);
10219			}
10220
10221			vars.nextButton.add(vars.prevButton).add(vars.innerPrevButton).add(vars.innerNextButton).removeClass('disabled');
10222
10223			//Trigger the onHide callback
10224			if (typeof opts.callback.onHide == 'function') opts.callback.onHide.call(iL, iL.ui);
10225		},
10226
10227		repositionPhoto: function() {
10228			var iL = this,
10229				vars = iL.vars,
10230				opts = iL.options,
10231				path = opts.path.toLowerCase(),
10232				viewport = getViewport(),
10233				winWidth = viewport.width,
10234				winHeight = viewport.height;
10235
10236			if (viewport.width < UNCODE.mediaQuery) opts.styles.nextOffsetX = 0;
10237
10238			var thumbsOffsetW = (vars.isInFullScreen && opts.fullAlone || vars.isMobile) ? 0 : ((path == 'horizontal') ? 0 : vars.thumbnails.outerWidth()),
10239				thumbsOffsetH = vars.isMobile ? vars.toolbar.outerHeight() : ((vars.isInFullScreen && opts.fullAlone) ? 0 : ((path == 'horizontal') ? vars.thumbnails.outerHeight() : 0)),
10240				width = (vars.isInFullScreen && opts.fullAlone) ? winWidth : (winWidth - (opts.styles.pageOffsetX)),
10241				height = (vars.isInFullScreen && opts.fullAlone) ? winHeight : (winHeight - (opts.styles.pageOffsetY)),
10242				offsetW = (path == 'horizontal') ? parseInt((iL.items[vars.next] || iL.items[vars.prev]) ? ((opts.styles.nextOffsetX + opts.styles.prevOffsetX)) * 2 : (((width / 10) <= 30) ? 30 : (width / 10))) : parseInt(((width / 10) <= 30) ? 30 : (width / 10)) + thumbsOffsetW,
10243				offsetH = (path == 'horizontal') ? parseInt(((height / 10) <= 30) ? 30 : (height / 10)) + thumbsOffsetH : parseInt((iL.items[vars.next] || iL.items[vars.prev]) ? ((opts.styles.nextOffsetX + opts.styles.prevOffsetX)) * 2 : (((height / 10) <= 30) ? 30 : (height / 10)));
10244
10245			var elObject = {
10246				type: 'current',
10247				width: width,
10248				height: height,
10249				item: iL.items[vars.current],
10250				offsetW: offsetW,
10251				offsetH: offsetH,
10252				thumbsOffsetW: thumbsOffsetW,
10253				thumbsOffsetH: thumbsOffsetH,
10254				animate: arguments.length,
10255				holder: vars.holder
10256			};
10257
10258			iL.repositionEl(elObject);
10259
10260			if (iL.items[vars.next]) {
10261				elObject = $.extend(elObject, {
10262					type: 'next',
10263					item: iL.items[vars.next],
10264					offsetX: opts.styles.nextOffsetX,
10265					offsetY: opts.styles.nextOffsetY,
10266					holder: vars.nextPhoto
10267				});
10268
10269				iL.repositionEl(elObject);
10270			}
10271
10272			if (iL.items[vars.prev]) {
10273				elObject = $.extend(elObject, {
10274					type: 'prev',
10275					item: iL.items[vars.prev],
10276					offsetX: opts.styles.nextOffsetX,
10277					offsetY: opts.styles.prevOffsetY,
10278					holder: vars.prevPhoto
10279				});
10280
10281				iL.repositionEl(elObject);
10282			}
10283
10284			var loaderCss = (path == "horizontal") ? {
10285				left: parseInt((width / 2) - (vars.loader.outerWidth() / 2))
10286			} : {
10287				top: parseInt((height / 2) - (vars.loader.outerHeight() / 2))
10288			};
10289			vars.loader.css(loaderCss);
10290		},
10291
10292		repositionEl: function(obj) {
10293			var iL = this,
10294				vars = iL.vars,
10295				opts = iL.options,
10296				path = opts.path.toLowerCase(),
10297				widthAvail = (obj.type == 'current') ? ((vars.isInFullScreen && opts.fullAlone) ? obj.width : (obj.width - obj.offsetW)) : (obj.width - obj.offsetW),
10298				heightAvail = (obj.type == 'current') ? ((vars.isInFullScreen && opts.fullAlone) ? obj.height : (obj.height - obj.offsetH)) : (obj.height - obj.offsetH),
10299				itemParent = obj.item,
10300				item = obj.item.options,
10301				holder = obj.holder,
10302				offsetX = obj.offsetX || 0,
10303				offsetY = obj.offsetY || 0,
10304				thumbsOffsetW = obj.thumbsOffsetW,
10305				thumbsOffsetH = obj.thumbsOffsetH;
10306
10307			if (obj.type == 'current') {
10308				if (typeof item.width == 'number' && item.width) widthAvail = ((vars.isInFullScreen && opts.fullAlone) && (opts.fullStretchTypes.indexOf(itemParent.type) != -1 || item.fullViewPort || opts.fullViewPort)) ? widthAvail : ((item.width > widthAvail) ? widthAvail : item.width);
10309				if (typeof item.height == 'number' && item.height) heightAvail = ((vars.isInFullScreen && opts.fullAlone) && (opts.fullStretchTypes.indexOf(itemParent.type) != -1 || item.fullViewPort || opts.fullViewPort)) ? heightAvail : ((item.height > heightAvail) ? heightAvail : item.height);
10310			} else {
10311				if (typeof item.width == 'number' && item.width) widthAvail = (item.width > widthAvail) ? widthAvail : item.width;
10312				if (typeof item.height == 'number' && item.height) heightAvail = (item.height > heightAvail) ? heightAvail : item.height;
10313			}
10314
10315			heightAvail = parseInt(heightAvail - $('.ilightbox-inner-toolbar', holder).outerHeight());
10316
10317			var width = (typeof item.width == 'string' && item.width.indexOf('%') != -1) ? percentToValue(parseInt(item.width.replace('%', '')), obj.width) : holder.data('naturalWidth'),
10318				height = (typeof item.height == 'string' && item.height.indexOf('%') != -1) ? percentToValue(parseInt(item.height.replace('%', '')), obj.height) : holder.data('naturalHeight');
10319
10320			var dims = ((typeof item.width == 'string' && item.width.indexOf('%') != -1 || typeof item.height == 'string' && item.height.indexOf('%') != -1) ? {
10321					width: width,
10322					height: height
10323				} : iL.getNewDimenstions(widthAvail, heightAvail, width, height)),
10324				newDims = $.extend({}, dims, {});
10325
10326			if (obj.type == 'prev' || obj.type == 'next')
10327				width = parseInt(dims.width * ((obj.type == 'next') ? opts.styles.nextScale : opts.styles.prevScale)),
10328				height = parseInt(dims.height * ((obj.type == 'next') ? opts.styles.nextScale : opts.styles.prevScale));
10329			else
10330				width = dims.width,
10331				height = dims.height;
10332
10333			var widthDiff = parseInt((getPixel(holder, 'padding-left') + getPixel(holder, 'padding-right') + getPixel(holder, 'border-left-width') + getPixel(holder, 'border-right-width')) / 2),
10334				heightDiff = parseInt((getPixel(holder, 'padding-top') + getPixel(holder, 'padding-bottom') + getPixel(holder, 'border-top-width') + getPixel(holder, 'border-bottom-width') + ($('.ilightbox-inner-toolbar', holder).outerHeight() || 0)) / 2);
10335
10336			switch (obj.type) {
10337				case 'current':
10338					var top = parseInt((obj.height / 2) - (height / 2) - heightDiff - (thumbsOffsetH / 2)),
10339						left = parseInt((obj.width / 2) - (width / 2) - widthDiff - (thumbsOffsetW / 2));
10340					break;
10341
10342				case 'next':
10343					var top = (path == 'horizontal') ? parseInt((obj.height / 2) - offsetY - (height / 2) - heightDiff - (thumbsOffsetH / 2)) : parseInt(obj.height - offsetX - heightDiff - (thumbsOffsetH / 2)),
10344						left = (path == 'horizontal') ? parseInt(obj.width - offsetX - widthDiff - (thumbsOffsetW / 2)) : parseInt((obj.width / 2) - (width / 2) - widthDiff - offsetY - (thumbsOffsetW / 2));
10345					break;
10346
10347				case 'prev':
10348					var top = (path == 'horizontal') ? parseInt((obj.height / 2) - offsetY - (height / 2) - heightDiff - (thumbsOffsetH / 2)) : parseInt(offsetX - heightDiff - height - (thumbsOffsetH / 2)),
10349						left = (path == 'horizontal') ? parseInt(offsetX - widthDiff - width - (thumbsOffsetW / 2)) : parseInt((obj.width / 2) - offsetY - (width / 2) - widthDiff - (thumbsOffsetW / 2));
10350					break;
10351			}
10352
10353			holder.data('offset', {
10354				top: top,
10355				left: left,
10356				newDims: newDims,
10357				diff: {
10358					W: widthDiff,
10359					H: heightDiff
10360				},
10361				thumbsOffset: {
10362					W: thumbsOffsetW,
10363					H: thumbsOffsetH
10364				},
10365				object: obj
10366			});
10367
10368			var opacityMobile;
10369			if ( isMobile ) {
10370				opacityMobile = {
10371					opacity: 1,
10372					top: top,
10373					left: left
10374				};
10375			} else {
10376				opacityMobile = {
10377					top: top,
10378					left: left
10379				};
10380			}
10381
10382			if (obj.animate > 0 && opts.effects.reposition) {
10383				holder.css(transform, gpuAcceleration).stop().animate(
10384					opacityMobile,
10385					opts.effects.repositionSpeed, 'easeOutCirc', function() {
10386					holder.css(transform, '');
10387				});
10388				$('div.ilightbox-container', holder).stop().animate({
10389					width: width,
10390					height: height
10391				}, opts.effects.repositionSpeed, 'easeOutCirc');
10392				$('div.ilightbox-inner-toolbar', holder).stop().animate({
10393					width: width
10394				}, opts.effects.repositionSpeed, 'easeOutCirc', function() {
10395					$(this).css('overflow', 'visible');
10396				});
10397			} else {
10398				holder.css(opacityMobile);
10399				$('div.ilightbox-container', holder).css({
10400					width: width,
10401					height: height
10402				});
10403				$('div.ilightbox-inner-toolbar', holder).css({
10404					width: width
10405				});
10406			}
10407		},
10408
10409
10410		resume: function(priority) {
10411			var iL = this,
10412				vars = iL.vars,
10413				opts = iL.options;
10414
10415			if (!opts.slideshow.pauseTime || opts.controls.slideshow && vars.total <= 1 || priority < vars.isPaused) {
10416				return;
10417			}
10418
10419			vars.isPaused = 0;
10420
10421			if (vars.cycleID) {
10422				vars.cycleID = clearTimeout(vars.cycleID);
10423			}
10424
10425			vars.cycleID = setTimeout(function() {
10426				if (vars.current == vars.total - 1) iL.goTo(0);
10427				else iL.moveTo('next');
10428			}, opts.slideshow.pauseTime);
10429		},
10430
10431		pause: function(priority) {
10432			var iL = this,
10433				vars = iL.vars,
10434				opts = iL.options;
10435
10436			if (priority < vars.isPaused) {
10437				return;
10438			}
10439
10440			vars.isPaused = priority || 100;
10441
10442			if (vars.cycleID) {
10443				vars.cycleID = clearTimeout(vars.cycleID);
10444			}
10445		},
10446
10447		resetCycle: function() {
10448			var iL = this,
10449				vars = iL.vars,
10450				opts = iL.options;
10451
10452			if (opts.controls.slideshow && vars.cycleID && !vars.isPaused) {
10453				iL.resume();
10454			}
10455		},
10456
10457		getNewDimenstions: function(width, height, width_old, height_old, thumb) {
10458			var iL = this;
10459
10460			if (!width) factor = height / height_old;
10461			else if (!height) factor = width / width_old;
10462			else factor = min(width / width_old, height / height_old);
10463
10464			if (!thumb) {
10465				if (factor > iL.options.maxScale) factor = iL.options.maxScale;
10466				else if (factor < iL.options.minScale) factor = iL.options.minScale;
10467			}
10468
10469			var final_width = (iL.options.keepAspectRatio) ? round(width_old * factor) : width,
10470				final_height = (iL.options.keepAspectRatio) ? round(height_old * factor) : height;
10471
10472			return {
10473				width: final_width,
10474				height: final_height,
10475				ratio: factor
10476			};
10477		},
10478
10479		setOption: function(options) {
10480			var iL = this;
10481
10482			iL.options = $.extend(true, iL.options, options || {});
10483			iL.refresh();
10484		},
10485
10486		availPlugins: function() {
10487			var iL = this,
10488				testEl = document.createElement("video");
10489
10490			iL.plugins = {
10491				flash: !isMobile,
10492				quicktime: (parseInt(PluginDetect.getVersion("QuickTime")) >= 0) ? true : false,
10493				html5H264: !!(testEl.canPlayType && testEl.canPlayType('video/mp4').replace(/no/, '')),
10494				html5WebM: !!(testEl.canPlayType && testEl.canPlayType('video/webm').replace(/no/, '')),
10495				html5Vorbis: !!(testEl.canPlayType && testEl.canPlayType('video/ogg').replace(/no/, '')),
10496				html5QuickTime: !!(testEl.canPlayType && testEl.canPlayType('video/quicktime').replace(/no/, ''))
10497			};
10498		},
10499
10500		addContent: function(element, obj) {
10501			var iL = this,
10502				el;
10503
10504			switch (obj.type) {
10505				case 'video':
10506					var HTML5 = false,
10507						videoType = obj.videoType,
10508						html5video = obj.options.html5video;
10509
10510					if (((videoType == 'video/mp4' || obj.ext == 'mp4' || obj.ext == 'm4v') || html5video.h264) && iL.plugins.html5H264)
10511						obj.ext = 'mp4',
10512						obj.URL = html5video.h264 || obj.URL;
10513					else if (html5video.webm && iL.plugins.html5WebM)
10514						obj.ext = 'webm',
10515						obj.URL = html5video.webm || obj.URL;
10516					else if (html5video.ogg && iL.plugins.html5Vorbis)
10517						obj.ext = 'ogv',
10518						obj.URL = html5video.ogg || obj.URL;
10519
10520					if (iL.plugins.html5H264 && (videoType == 'video/mp4' || obj.ext == 'mp4' || obj.ext == 'm4v')) HTML5 = true, videoType = "video/mp4";
10521					else if (iL.plugins.html5WebM && (videoType == 'video/webm' || obj.ext == 'webm')) HTML5 = true, videoType = "video/webm";
10522					else if (iL.plugins.html5Vorbis && (videoType == 'video/ogg' || obj.ext == 'ogv')) HTML5 = true, videoType = "video/ogg";
10523					else if (iL.plugins.html5QuickTime && (videoType == 'video/quicktime' || obj.ext == 'mov' || obj.ext == 'qt')) HTML5 = true, videoType = "video/quicktime";
10524
10525					if (HTML5) {
10526						el = $('<video />
10526', {
10527							"width": "100%",
10528							"height": "100%",
10529							"preload": html5video.preload,
10530							"autoplay": html5video.autoplay,
10531							"loop": html5video.loop,
10532							"poster": html5video.poster,
10533							"controls": html5video.controls,
10534							"controlslist": "nodownload"
10535						}).append($('<source />', {
10536							"src": obj.URL,
10537							"type": videoType
10538						}));
10539					} else {
10540						if (!iL.plugins.quicktime) el = $('<span />', {
10541							"class": "ilightbox-alert",
10542							html: iL.options.errors.missingPlugin.replace('{pluginspage}', pluginspages.quicktime).replace('{type}', 'QuickTime')
10543						});
10544						else {
10545
10546							el = $('<object />', {
10547								"type": "video/quicktime",
10548								"pluginspage": pluginspages.quicktime
10549							}).attr({
10550								"data": obj.URL,
10551								"width": "100%",
10552								"height": "100%"
10553							}).append($('<param />', {
10554								"name": "src",
10555								"value": obj.URL
10556							})).append($('<param />', {
10557								"name": "autoplay",
10558								"value": "false"
10559							})).append($('<param />', {
10560								"name": "loop",
10561								"value": "false"
10562							})).append($('<param />', {
10563								"name": "scale",
10564								"value": "tofit"
10565							}));
10566
10567							if (browser.msie) el = QT_GenerateOBJECTText(obj.URL, '100%', '100%', '', 'SCALE', 'tofit', 'AUTOPLAY', 'false', 'LOOP', 'false');
10568						}
10569					}
10570
10571					break;
10572
10573				case 'flash':
10574					if (!iL.plugins.flash) el = $('<span />', {
10575						"class": "ilightbox-alert",
10576						html: iL.options.errors.missingPlugin.replace('{pluginspage}', pluginspages.flash).replace('{type}', 'Adobe Flash player')
10577					});
10578					else {
10579						var flashvars = "",
10580							i = 0;
10581
10582						if (obj.options.flashvars) $.each(obj.options.flashvars, function(k, v) {
10583							if (i != 0) flashvars += "&";
10584							flashvars += k + "=" + encodeURIComponent(v);
10585							i++;
10586						});
10587						else flashvars = null;
10588
10589						el = $('<embed />').attr({
10590							"type": "application/x-shockwave-flash",
10591							"src": obj.URL,
10592							"width": (typeof obj.options.width == 'number' && obj.options.width && iL.options.minScale == '1' && iL.options.maxScale == '1') ? obj.options.width : "100%",
10593							"height": (typeof obj.options.height == 'number' && obj.options.height && iL.options.minScale == '1' && iL.options.maxScale == '1') ? obj.options.height : "100%",
10594							"quality": "high",
10595							"bgcolor": "#000000",
10596							"play": "true",
10597							"loop": "true",
10598							"menu": "true",
10599							"wmode": "transparent",
10600							"scale": "showall",
10601							"allowScriptAccess": "always",
10602							"allowFullScreen": "true",
10603							"flashvars": flashvars,
10604							"fullscreen": "yes"
10605						});
10606					}
10607
10608					break;
10609
10610				case 'iframe':
10611					el = $('<iframe />').attr({
10612						"width": (typeof obj.options.width == 'number' && obj.options.width && iL.options.minScale == '1' && iL.options.maxScale == '1') ? obj.options.width : "100%",
10613						"height": (typeof obj.options.height == 'number' && obj.options.height && iL.options.minScale == '1' && iL.options.maxScale == '1') ? obj.options.height : "100%",
10614						src: obj.URL,
10615						frameborder: 0,
10616						'hspace': 0,
10617						'vspace': 0,
10618						'scrolling': supportTouch ? 'auto' : 'scroll',
10619						'webkitAllowFullScreen': '',
10620						'mozallowfullscreen': '',
10621						'allowFullScreen': ''
10622					});
10623
10624					break;
10625
10626				case 'inline':
10627					el = $('<div class="ilightbox-wrapper"></div>').html($(obj.URL).clone(true));
10628
10629					break;
10630
10631				case 'html':
10632					var object = obj.URL,
10633						el;
10634
10635					if (object[0].nodeName) {
10636						el = $('<div class="ilightbox-wrapper"></div>').html(object);
10637					} else {
10638						var dom = $(obj.URL),
10639							html = (dom.selector) ? $('<div>' + dom + '</div>') : dom;
10640						el = $('<div class="ilightbox-wrapper"></div>').html(html);
10641					}
10642
10643					break;
10644			}
10645
10646			$('div.ilightbox-container', element).empty().html(el);
10647
10648			// For fixing Chrome about just playing the video for first time
10649			if (el[0].tagName.toLowerCase() === 'video' && browser.webkit) setTimeout(function() {
10650				var src = el[0].currentSrc + '?' + floor(random() * 30000);
10651				el[0].currentSrc = src;
10652				el[0].src = src;
10653			});
10654
10655			return el;
10656		},
10657
10658		ogpRecognition: function(obj, callback) {
10659			var iL = this,
10660				url = obj.URL;
10661
10662			iL.showLoader();
10663			doAjax(url, function(data) {
10664				iL.hideLoader();
10665				if (data) {
10666					var object = new Object();
10667
10668					object.length = false,
10669					object.url = data.url;
10670
10671					if (data.status == 200) {
10672						var result = data.results,
10673							type = result.type,
10674							source = result.source;
10675
10676						object.source = source.src,
10677						object.width = source.width && parseInt(source.width) || 0,
10678						object.height = source.height && parseInt(source.height) || 0,
10679						object.type = type,
10680						object.thumbnail = source.thumbnail || result.images && result.images[0],
10681						object.html5video = result.html5video || {},
10682						object.length = true;
10683
10684						if (source.type == 'application/x-shockwave-flash') object.type = "flash";
10685						else if (source.type.indexOf("video/") != -1) object.type = "video";
10686						else if (source.type.indexOf("/html") != -1) object.type = "iframe";
10687						else if (source.type.indexOf("image/") != -1) object.type = "image";
10688
10689					} else if (typeof data.response != 'undefined')
10690						throw data.response;
10691
10692					callback.call(this, object.length ? object : false);
10693				}
10694			});
10695		},
10696
10697		hashChangeHandler: function(url) {
10698			var iL = this,
10699				vars = iL.vars,
10700				opts = iL.options,
10701				URL = url || window.location.href,
10702				hash = parseURI(URL).hash,
10703				split = hash.split('/'),
10704				index = split[1];
10705
10706			if (vars.hashLock || ('#' + opts.linkId != split[0] && hash.length > 1)) return;
10707
10708			if (index) {
10709				var target = split[1] || 0;
10710				if (iL.items[target]) {
10711					var overlay = $('.ilightbox-overlay');
10712					if (overlay.length && overlay.attr('linkid') == opts.linkId) {
10713						iL.goTo(target);
10714					} else {
10715						iL.itemsObject[target].trigger(ishybrid ? "itap click" : supportTouch ? 'itap' : 'click');
10716						//iL.goTo(target);
10717					}
10718				} else {
10719					var overlay = $('.ilightbox-overlay');
10720					if (overlay.length) iL.closeAction();
10721				}
10722			} else {
10723				var overlay = $('.ilightbox-overlay');
10724				if (overlay.length) iL.closeAction();
10725			}
10726		}
10727	};
10728
10729	/**
10730	 * Parse style to pixels.
10731	 *
10732	 * @param {Object}   $element   jQuery object with element.
10733	 * @param {Property} property   CSS property to get the pixels from.
10734	 *
10735	 * @return {Int}
10736	 */
10737	function getPixel($element, property) {
10738		return parseInt($element.css(property), 10) || 0;
10739	}
10740
10741	/**
10742	 * Make sure that number is within the limits.
10743	 *
10744	 * @param {Number} number
10745	 * @param {Number} min
10746	 * @param {Number} max
10747	 *
10748	 * @return {Number}
10749	 */
10750	function within(number, min, max) {
10751		return number < min ? min : number > max ? max : number;
10752	}
10753
10754	/**
10755	 * Get viewport/window size (width and height).
10756	 *
10757	 * @return {Object}
10758	 */
10759	function getViewport() {
10760		var e = window,
10761			a = 'inner';
10762		if (!('innerWidth' in window)) {
10763			a = 'client';
10764			e = document.documentElement || document.body;
10765		}
10766		return {
10767			width: e[a + 'Width'],
10768			height: e[a + 'Height']
10769		}
10770	}
10771
10772	/**
10773	 * Remove hash tag from the URL
10774	 *
10775	 * @return {Void}
10776	 */
10777	function removeHash() {
10778		var scroll = getScrollXY();
10779
10780		//UNCODE change
10781		//window.location.hash = "";
10782		history.pushState("", document.title, window.location.pathname + window.location.search);
10783
10784		// Restore the scroll offset, should be flicker free
10785		window.scrollTo(scroll.x, scroll.y);
10786	}
10787
10788	/**
10789	 * Do the ajax requests with callback.
10790	 *
10791	 * @param {String}   url
10792	 * @param {Function} callback
10793	 *
10794	 * @return {Void}
10795	 */
10796	function doAjax(url, callback) {
10797		var url = "//ilightbox.net/getSource/jsonp.php?url=" + encodeURIComponent(url).replace(/!/g, '%21').replace(/'/g, '%27').replace(/\(/g, '%28').replace(/\)/g, '%29').replace(/\*/g, '%2A');
10798		$.ajax({
10799			url: url,
10800			dataType: 'jsonp'
10801		});
10802
10803		iLCallback = function(data) {
10804			callback.call(this, data);
10805		};
10806	}
10807
10808	/**
10809	 * Find image from DOM elements
10810	 *
10811	 * @param {Element} element
10812	 *
10813	 * @return {Void}
10814	 */
10815	function findImageInElement(element) {
10816		var elements = $('*', element),
10817			imagesArr = new Array();
10818
10819		elements.each(function() {
10820			var url = "",
10821				element = this;
10822
10823			if ($(element).css("background-image") != "none") {
10824				url = $(element).css("background-image");
10825			} else if (typeof($(element).attr("src")) != "undefined" && element.nodeName.toLowerCase() == "img") {
10826				url = $(element).attr("src");
10827			}
10828
10829			if (url.indexOf("gradient") == -1) {
10830				url = url.replace(/url\(\"/g, "");
10831				url = url.replace(/url\(/g, "");
10832				url = url.replace(/\"\)/g, "");
10833				url = url.replace(/\)/g, "");
10834
10835				var urls = url.split(",");
10836
10837				for (var i = 0; i < urls.length; i++) {
10838					if (urls[i].length > 0 && $.inArray(urls[i], imagesArr) == -1) {
10839						var extra = "";
10840						if (browser.msie && browser.version < 9) {
10841							extra = "?" + floor(random() * 3000);
10842						}
10843						imagesArr.push(urls[i] + extra);
10844					}
10845				}
10846			}
10847		});
10848
10849		return imagesArr;
10850	}
10851
10852	/**
10853	 * Get file extension.
10854	 *
10855	 * @param {String} URL
10856	 *
10857	 * @return {String}
10858	 */
10859	function getExtension(URL) {
10860		if (URL !== null) {
10861			//UNCODE: fix issue to detect media type with query strings
10862			var ext = URL.indexOf('?') !== -1 ? URL.split('?')[0] : URL;
10863			ext = ext.split('.').pop().toLowerCase();
10864
10865			return ext;
10866		}
10867	}
10868
10869	/**
10870	 * Get type via extension.
10871	 *
10872	 * @param {String} URL
10873	 *
10874	 * @return {String}
10875	 */
10876	function getTypeByExtension(URL) {
10877		var type,
10878			ext = getExtension(URL);
10879
10880		if (extensions.image.indexOf(ext) !== -1) type = 'image';
10881		else if (extensions.flash.indexOf(ext) !== -1) type = 'flash';
10882		else if (extensions.video.indexOf(ext) !== -1) type = 'video';
10883		else if (ext.substring(0, 1) == "#") type = 'inline'; //Uncode addition
10884		else type = 'iframe';
10885
10886		return type;
10887	}
10888
10889	/**
10890	 * Return value from percent of a number.
10891	 *
10892	 * @param {Number} percent
10893	 * @param {Number} total
10894	 *
10895	 * @return {Number}
10896	 */
10897	function percentToValue(percent, total) {
10898		return parseInt((total / 100) * percent);
10899	}
10900
10901	/**
10902	 * A JavaScript equivalent of PHP’s parse_url.
10903	 *
10904	 * @param {String} url           The URL to parse.
10905	 *
10906	 * @return {Mixed}
10907	 */
10908	function parseURI(url) {
10909		var m = String(url).replace(/^\s+|\s+$/g, '').match(/^([^:\/?#]+:)?(\/\/(?:[^:@]*(?::[^:@]*)?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/);
10910		// authority = '//' + user + ':' + pass '@' + hostname + ':' port
10911		return (m ? {
10912			href: m[0] || '',
10913			protocol: m[1] || '',
10914			authority: m[2] || '',
10915			host: m[3] || '',
10916			hostname: m[4] || '',
10917			port: m[5] || '',
10918			pathname: m[6] || '',
10919			search: m[7] || '',
10920			hash: m[8] || ''
10921		} : null);
10922	}
10923
10924	/**
10925	 * Gets the absolute URI.
10926	 *
10927	 * @param {String} href     The relative URL.
10928	 * @param {String} base     The base URL.
10929	 *
10930	 * @return {String}         The absolute URL.
10931	 */
10932	function absolutizeURI(base, href) { // RFC 3986
10933		var iL = this;
10934
10935		function removeDotSegments(input) {
10936			var output = [];
10937			input.replace(/^(\.\.?(\/|$))+/, '')
10938				.replace(/\/(\.(\/|$))+/g, '/')
10939				.replace(/\/\.\.$/, '/../')
10940				.replace(/\/?[^\/]*/g, function(p) {
10941					if (p === '/..') {
10942						output.pop();
10943					} else {
10944						output.push(p);
10945					}
10946				});
10947			return output.join('').replace(/^\//, input.charAt(0) === '/' ? '/' : '');
10948		}
10949
10950		href = parseURI(href || '');
10951		base = parseURI(base || '');
10952
10953		return !href || !base ? null : (href.protocol || base.protocol) +
10954			(href.protocol || href.authority ? href.authority : base.authority) +
10955			removeDotSegments(href.protocol || href.authority || href.pathname.charAt(0) === '/' ? href.pathname : (href.pathname ? ((base.authority && !base.pathname ? '/' : '') + base.pathname.slice(0, base.pathname.lastIndexOf('/') + 1) + href.pathname) : base.pathname)) +
10956			(href.protocol || href.authority || href.pathname ? href.search : (href.search || base.search)) +
10957			href.hash;
10958	}
10959
10960	/**
10961	 * A JavaScript equivalent of PHP’s version_compare.
10962	 *
10963	 * @param {String} v1
10964	 * @param {String} v2
10965	 * @param {String} operator
10966	 *
10967	 * @return {Boolean}
10968	 */
10969	function version_compare(v1, v2, operator) {
10970		this.php_js = this.php_js || {};
10971		this.php_js.ENV = this.php_js.ENV || {};
10972		var i = 0,
10973			x = 0,
10974			compare = 0,
10975			vm = {
10976				'dev': -6,
10977				'alpha': -5,
10978				'a': -5,
10979				'beta': -4,
10980				'b': -4,
10981				'RC': -3,
10982				'rc': -3,
10983				'#': -2,
10984				'p': 1,
10985				'pl': 1
10986			},
10987			prepVersion = function(v) {
10988				v = ('' + v).replace(/[_\-+]/g, '.');
10989				v = v.replace(/([^.\d]+)/g, '.$1.').replace(/\.{2,}/g, '.');
10990				return (!v.length ? [-8] : v.split('.'));
10991			},
10992			numVersion = function(v) {
10993				return !v ? 0 : (isNaN(v) ? vm[v] || -7 : parseInt(v, 10));
10994			};
10995		v1 = prepVersion(v1);
10996		v2 = prepVersion(v2);
10997		x = max(v1.length, v2.length);
10998		for (i = 0; i < x; i++) {
10999			if (v1[i] == v2[i]) {
11000				continue;
11001			}
11002			v1[i] = numVersion(v1[i]);
11003			v2[i] = numVersion(v2[i]);
11004			if (v1[i] < v2[i]) {
11005				compare = -1;
11006				break;
11007			} else if (v1[i] > v2[i]) {
11008				compare = 1;
11009				break;
11010			}
11011		}
11012		if (!operator) {
11013			return compare;
11014		}
11015
11016		switch (operator) {
11017			case '>':
11018			case 'gt':
11019				return (compare > 0);
11020			case '>=':
11021			case 'ge':
11022				return (compare >= 0);
11023			case '<=':
11024			case 'le':
11025				return (compare <= 0);
11026			case '==':
11027			case '=':
11028			case 'eq':
11029				return (compare === 0);
11030			case '<>':
11031			case '!=':
11032			case 'ne':
11033				return (compare !== 0);
11034			case '':
11035			case '<':
11036			case 'lt':
11037				return (compare < 0);
11038			default:
11039				return null;
11040		}
11041	}
11042
11043
11044	// Begin the iLightBox plugin
11045	$.fn.iLightBox = function() {
11046
11047		var args = arguments,
11048			opt = ($.isPlainObject(args[0])) ? args[0] : args[1],
11049			items = ($.isArray(args[0]) || typeof args[0] == 'string') ? args[0] : args[1];
11050
11051		if (!opt) opt = {};
11052
11053		// Default options. Play carefully.
11054		var options = $.extend(true, {
11055			attr: 'href',
11056			path: 'vertical',
11057			skin: 'dark',
11058			linkId: false,
11059			infinite: false,
11060			startFrom: 0,
11061			randomStart: false,
11062			keepAspectRatio: true,
11063			maxScale: 1,
11064			minScale: .2,
11065			innerToolbar: false,
11066			smartRecognition: false,
11067			mobileOptimizer: true,
11068			fullAlone: true,
11069			fullViewPort: null,
11070			fullStretchTypes: 'flash, video',
11071			overlay: {
11072				blur: true,
11073				opacity: .85
11074			},
11075			controls: {
11076				arrows: false,
11077				slideshow: false,
11078				toolbar: true,
11079				fullscreen: true,
11080				thumbnail: true,
11081				keyboard: true,
11082				mousewheel: true,
11083				swipe: true
11084			},
11085			keyboard: {
11086				left: true, // previous
11087				right: true, // next
11088				up: true, // previous
11089				down: true, // next
11090				esc: true, // close
11091				shift_enter: true // fullscreen
11092			},
11093			show: {
11094				effect: true,
11095				speed: 300,
11096				title: true
11097			},
11098			hide: {
11099				effect: true,
11100				speed: 300
11101			},
11102			caption: {
11103				start: true,
11104				show: 'mouseenter',
11105				hide: 'mouseleave'
11106			},
11107			social: {
11108				start: true,
11109				show: 'mouseenter',
11110				hide: 'mouseleave',
11111				buttons: false
11112			},
11113			styles: {
11114				pageOffsetX: 0,
11115				pageOffsetY: 0,
11116				nextOffsetX: 45,
11117				nextOffsetY: 0,
11118				nextOpacity: 1,
11119				nextScale: 1,
11120				prevOffsetX: 45,
11121				prevOffsetY: 0,
11122				prevOpacity: 1,
11123				prevScale: 1
11124			},
11125			thumbnails: {
11126				maxWidth: 120,
11127				maxHeight: 80,
11128				normalOpacity: 1,
11129				activeOpacity: .6
11130			},
11131			effects: {
11132				reposition: true,
11133				repositionSpeed: 200,
11134				switchSpeed: 500,
11135				loadedFadeSpeed: 180,
11136				fadeSpeed: 200
11137			},
11138			slideshow: {
11139				pauseTime: 5000,
11140				pauseOnHover: false,
11141				startPaused: true
11142			},
11143			text: {
11144				close: "Press Esc to close",
11145				enterFullscreen: "Enter Fullscreen (Shift+Enter)",
11146				exitFullscreen: "Exit Fullscreen (Shift+Enter)",
11147				slideShow: "Slideshow",
11148				next: "Next",
11149				previous: "Previous"
11150			},
11151			errors: {
11152				loadImage: "An error occurred when trying to load photo.",
11153				loadContents: "An error occurred when trying to load contents.",
11154				missingPlugin: "The content your are attempting to view requires the <a href='{pluginspage}' target='_blank'>{type} plugin<\/a>."
11155			},
11156			ajaxSetup: {
11157				url: '',
11158				beforeSend: function(jqXHR, settings) {},
11159				cache: false,
11160				complete: function(jqXHR, textStatus) {},
11161				crossDomain: false,
11162				error: function(jqXHR, textStatus, errorThrown) {},
11163				success: function(data, textStatus, jqXHR) {},
11164				global: true,
11165				ifModified: false,
11166				username: null,
11167				password: null,
11168				type: 'GET'
11169			},
11170			callback: {}
11171		}, opt);
11172
11173		var instant = ($.isArray(items) || typeof items == 'string') ? true : false;
11174
11175		items = $.isArray(items) ? items : new Array();
11176
11177		if (typeof args[0] == 'string') items[0] = args[0];
11178
11179		if (version_compare($.fn.jquery, '1.8', '>=')) {
11180			var iLB = new iLightBox($(this), options, items, instant);
11181			return {
11182				close: function() {
11183					iLB.closeAction();
11184				},
11185				fullscreen: function() {
11186					iLB.fullScreenAction();
11187				},
11188				moveNext: function() {
11189					iLB.moveTo('next');
11190				},
11191				movePrev: function() {
11192					iLB.moveTo('prev');
11193				},
11194				goTo: function(index) {
11195					iLB.goTo(index);
11196				},
11197				refresh: function() {
11198					iLB.refresh();
11199				},
11200				reposition: function() {
11201					(arguments.length > 0) ? iLB.repositionPhoto(true): iLB.repositionPhoto();
11202				},
11203				setOption: function(options) {
11204					iLB.setOption(options);
11205				},
11206				destroy: function() {
11207					iLB.closeAction();
11208					iLB.dispatchItemsEvents();
11209				}
11210			};
11211		} else {
11212			throw "The jQuery version that was loaded is too old. iLightBox requires jQuery 1.8+";
11213		}
11214
11215	};
11216
11217
11218	$.iLightBox = function() {
11219		return $.fn.iLightBox(arguments[0], arguments[1]);
11220	};
11221
11222
11223	$.extend($.easing, {
11224		easeInCirc: function(x, t, b, c, d) {
11225			return -c * (sqrt(1 - (t /= d) * t) - 1) + b;
11226		},
11227		easeOutCirc: function(x, t, b, c, d) {
11228			return c * sqrt(1 - (t = t / d - 1) * t) + b;
11229		},
11230		easeInOutCirc: function(x, t, b, c, d) {
11231			if ((t /= d / 2) < 1) return -c / 2 * (sqrt(1 - t * t) - 1) + b;
11232			return c / 2 * (sqrt(1 - (t -= 2) * t) + 1) + b;
11233		}
11234	});
11235
11236	function getScrollXY() {
11237		var scrOfX = 0,
11238			scrOfY = 0;
11239		if (typeof(window.pageYOffset) == 'number') {
11240			//Netscape compliant
11241			scrOfY = window.pageYOffset;
11242			scrOfX = window.pageXOffset;
11243		} else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) {
11244			//DOM compliant
11245			scrOfY = document.body.scrollTop;
11246			scrOfX = document.body.scrollLeft;
11247		} else if (document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) {
11248			//IE6 standards compliant mode
11249			scrOfY = document.documentElement.scrollTop;
11250			scrOfX = document.documentElement.scrollLeft;
11251		}
11252		return {
11253			x: scrOfX,
11254			y: scrOfY
11255		};
11256	}
11257
11258	(function() {
11259		// add new event shortcuts
11260		$.each(("touchstart touchmove touchend " +
11261			"tap taphold " +
11262			"swipe swipeleft swiperight " +
11263			"scrollstart scrollstop").split(" "), function(i, name) {
11264
11265			$.fn[name] = function(fn) {
11266				return fn ? this.bind(name, fn) : this.trigger(name);
11267			};
11268
11269			// jQuery < 1.8
11270			// if ($.attrFn) {
11271			// 	$.attrFn[name] = true;
11272			// }
11273		});
11274
11275		var tapSettings = {
11276			startEvent: 'touchstart.iTap',
11277			endEvent: 'touchend.iTap'
11278		};
11279
11280		// tap Event:
11281		$.event.special.itap = {
11282			setup: function() {
11283
11284				var self = this,
11285					$self = $(this),
11286					start, stop;
11287
11288				$self.bind(tapSettings.startEvent, function(event) {
11289					start = getScrollXY();
11290
11291					$self.one(tapSettings.endEvent, function(event) {
11292						stop = getScrollXY();
11293
11294						var orgEvent = event || window.event;
11295						event = $.event.fix(orgEvent);
11296						event.type = "itap";
11297
11298						if ((start && stop) && (start.x == stop.x && start.y == stop.y))($.event.dispatch || $.event.handle).call(self, event);
11299
11300						start = stop = undefined;
11301					});
11302				});
11303			},
11304
11305			teardown: function() {
11306				$(this).unbind(tapSettings.startEvent);
11307			}
11308		};
11309	}());
11310
11311
11312	//Fullscreen API
11313	(function() {
11314		fullScreenApi = {
11315				supportsFullScreen: false,
11316				isFullScreen: function() {
11317					return false;
11318				},
11319				requestFullScreen: function() {},
11320				cancelFullScreen: function() {},
11321				fullScreenEventName: '',
11322				prefix: ''
11323			},
11324			browserPrefixes = 'webkit moz o ms khtml'.split(' ');
11325
11326		// check for native support
11327		if (typeof document.cancelFullScreen != 'undefined') {
11328			fullScreenApi.supportsFullScreen = true;
11329		} else {
11330			// check for fullscreen support by vendor prefix
11331			for (var i = 0, il = browserPrefixes.length; i < il; i++) {
11332				fullScreenApi.prefix = browserPrefixes[i];
11333
11334				if (typeof document[fullScreenApi.prefix + 'CancelFullScreen'] != 'undefined') {
11335					fullScreenApi.supportsFullScreen = true;
11336
11337					break;
11338				}
11339			}
11340		}
11341
11342		// update methods to do something useful
11343		if (fullScreenApi.supportsFullScreen) {
11344			fullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange';
11345
11346			fullScreenApi.isFullScreen = function() {
11347				switch (this.prefix) {
11348					case '':
11349						return document.fullScreen;
11350					case 'webkit':
11351						return document.webkitIsFullScreen;
11352					default:
11353						return document[this.prefix + 'FullScreen'];
11354				}
11355			}
11356			fullScreenApi.requestFullScreen = function(el) {
11357				return (this.prefix === '') ? el.requestFullScreen() : el[this.prefix + 'RequestFullScreen']();
11358			}
11359			fullScreenApi.cancelFullScreen = function(el) {
11360				return (this.prefix === '') ? document.cancelFullScreen() : document[this.prefix + 'CancelFullScreen']();
11361			}
11362		}
11363	}());
11364
11365	// Browser detect
11366	(function() {
11367		function uaMatch(ua) {
11368			ua = ua.toLowerCase();
11369
11370			var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
11371				/(webkit)[ \/]([\w.]+)/.exec(ua) ||
11372				/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
11373				/(msie) ([\w.]+)/.exec(ua) ||
11374				ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || [];
11375
11376			return {
11377				browser: match[1] || "",
11378				version: match[2] || "0"
11379			};
11380		}
11381
11382		var matched = uaMatch(navigator.userAgent);
11383		browser = {};
11384
11385		if (matched.browser) {
11386			browser[matched.browser] = true;
11387			browser.version = matched.version;
11388		}
11389
11390		// Chrome is Webkit, but Webkit is also Safari.
11391		if (browser.chrome) {
11392			browser.webkit = true;
11393		} else if (browser.webkit) {
11394			browser.safari = true;
11395		}
11396	}());
11397
11398	// Feature detects
11399	(function() {
11400		var prefixes = ['', 'webkit', 'moz', 'ms', 'o'];
11401		var el = document.createElement('div');
11402
11403		function testProp(prop) {
11404			for (var p = 0, pl = prefixes.length; p < pl; p++) {
11405				var prefixedProp = prefixes[p] ? prefixes[p] + prop.charAt(0).toUpperCase() + prop.slice(1) : prop;
11406				if (el.style[prefixedProp] !== undefined) {
11407					return prefixedProp;
11408				}
11409			}
11410		}
11411
11412		// Global support indicators
11413		transform = testProp('transform') || '';
11414		gpuAcceleration = testProp('perspective') ? 'translateZ(0) ' : '';
11415	}());
11416
11417
11418	/*
11419		PluginDetect v0.7.9
11420		www.pinlady.net/PluginDetect/license/
11421		[ getVersion onWindowLoaded BetterIE ]
11422		[ Flash QuickTime Shockwave ]
11423	*/
11424	var PluginDetect={version:"0.7.9",name:"PluginDetect",handler:function(c,b,a){return function(){c(b,a)}},openTag:"<",isDefined:function(b){return typeof b!="undefined"},isArray:function(b){return(/array/i).test(Object.prototype.toString.call(b))},isFunc:function(b){return typeof b=="function"},isString:function(b){return typeof b=="string"},isNum:function(b){return typeof b=="number"},isStrNum:function(b){return(typeof b=="string"&&(/\d/).test(b))},getNumRegx:/[\d][\d\.\_,-]*/,splitNumRegx:/[\.\_,-]/g,getNum:function(b,c){var d=this,a=d.isStrNum(b)?(d.isDefined(c)?new RegExp(c):d.getNumRegx).exec(b):null;return a?a[0]:null},compareNums:function(h,f,d){var e=this,c,b,a,g=parseInt;if(e.isStrNum(h)&&e.isStrNum(f)){if(e.isDefined(d)&&d.compareNums){return d.compareNums(h,f)}c=h.split(e.splitNumRegx);b=f.split(e.splitNumRegx);for(a=0;a<min(c.length,b.length);a++){if(g(c[a],10)>g(b[a],10)){return 1}if(g(c[a],10)<g(b[a],10)){return -1}}}return 0},formatNum:function(b,c){var d=this,a,e;if(!d.isStrNum(b)){return null}if(!d.isNum(c)){c=4}c--;e=b.replace(/\s/g,"").split(d.splitNumRegx).concat(["0","0","0","0"]);for(a=0;a<4;a++){if(/^(0+)(.+)$/.test(e[a])){e[a]=RegExp.$2}if(a>c||!(/\d/).test(e[a])){e[a]="0"}}return e.slice(0,4).join(",")},$$hasMimeType:function(a){return function(c){if(!a.isIE&&c){var f,e,b,d=a.isArray(c)?c:(a.isString(c)?[c]:[]);for(b=0;b<d.length;b++){if(a.isString(d[b])&&/[^\s]/.test(d[b])){f=navigator.mimeTypes[d[b]];e=f?f.enabledPlugin:0;if(e&&(e.name||e.description)){return f}}}}return null}},findNavPlugin:function(l,e,c){var j=this,h=new RegExp(l,"i"),d=(!j.isDefined(e)||e)?/\d/:0,k=c?new RegExp(c,"i"):0,a=navigator.plugins,g="",f,b,m;for(f=0;f<a.length;f++){m=a[f].description||g;b=a[f].name||g;if((h.test(m)&&(!d||d.test(RegExp.leftContext+RegExp.rightContext)))||(h.test(b)&&(!d||d.test(RegExp.leftContext+RegExp.rightContext)))){if(!k||!(k.test(m)||k.test(b))){return a[f]}}}return null},getMimeEnabledPlugin:function(k,m,c){var e=this,f,b=new RegExp(m,"i"),h="",g=c?new RegExp(c,"i"):0,a,l,d,j=e.isString(k)?[k]:k;for(d=0;d<j.length;
11424d++){if((f=e.hasMimeType(j[d]))&&(f=f.enabledPlugin)){l=f.description||h;a=f.name||h;if(b.test(l)||b.test(a)){if(!g||!(g.test(l)||g.test(a))){return f}}}}return 0},getPluginFileVersion:function(f,b){var h=this,e,d,g,a,c=-1;if(h.OS>2||!f||!f.version||!(e=h.getNum(f.version))){return b}if(!b){return e}e=h.formatNum(e);b=h.formatNum(b);d=b.split(h.splitNumRegx);g=e.split(h.splitNumRegx);for(a=0;a<d.length;a++){if(c>-1&&a>c&&d[a]!="0"){return b}if(g[a]!=d[a]){if(c==-1){c=a}if(d[a]!="0"){return b}}}return e},AXO:window.ActiveXObject,getAXO:function(a){var f=null,d,b=this,c={};try{f=new b.AXO(a)}catch(d){}return f},convertFuncs:function(f){var a,g,d,b=/^[\$][\$]/,c=this;for(a in f){if(b.test(a)){try{g=a.slice(2);if(g.length>0&&!f[g]){f[g]=f[a](f);delete f[a]}}catch(d){}}}},initObj:function(e,b,d){var a,c;if(e){if(e[b[0]]==1||d){for(a=0;a<b.length;a=a+2){e[b[a]]=b[a+1]}}for(a in e){c=e[a];if(c&&c[b[0]]==1){this.initObj(c,b)}}}},initScript:function(){var d=this,a=navigator,h,i=document,l=a.userAgent||"",j=a.vendor||"",b=a.platform||"",k=a.product||"";d.initObj(d,["$",d]);for(h in d.Plugins){if(d.Plugins[h]){d.initObj(d.Plugins[h],["$",d,"$$",d.Plugins[h]],1)}}d.convertFuncs(d);d.OS=100;if(b){var g=["Win",1,"Mac",2,"Linux",3,"FreeBSD",4,"iPhone",21.1,"iPod",21.2,"iPad",21.3,"Win.*CE",22.1,"Win.*Mobile",22.2,"Pocket\\s*PC",22.3,"",100];for(h=g.length-2;h>=0;h=h-2){if(g[h]&&new RegExp(g[h],"i").test(b)){d.OS=g[h+1];break}}};d.head=i.getElementsByTagName("head")[0]||i.getElementsByTagName("body")[0]||i.body||null;d.isIE=new Function("return/*@cc_on!@*/!1")();d.verIE=d.isIE&&(/MSIE\s*(\d+\.?\d*)/i).test(l)?parseFloat(RegExp.$1,10):null;d.verIEfull=null;d.docModeIE=null;if(d.isIE){var f,n,c=document.createElement("div");try{c.style.behavior="url(#default#clientcaps)";d.verIEfull=(c.getComponentVersion("{89820200-ECBD-11CF-8B85-00AA005B4383}","componentid")).replace(/,/g,".")}catch(f){}n=parseFloat(d.verIEfull||"0",10);d.docModeIE=i.documentMode||((/back/i).test(i.compatMode||"")?5:n)||d.verIE;d.verIE=n||d.docModeIE};d.ActiveXEnabled=false;if(d.isIE){var h,m=["Msxml2.XMLHTTP","Msxml2.DOMDocument","Microsoft.XMLDOM","ShockwaveFlash.ShockwaveFlash","TDCCtl.TDCCtl","Shell.UIHelper","Scripting.Dictionary","wmplayer.ocx"];for(h=0;h<m.length;h++){if(d.getAXO(m[h])){d.ActiveXEnabled=true;break}}};d.isGecko=(/Gecko/i).test(k)&&(/Gecko\s*\/\s*\d/i).test(l);
11424d.verGecko=d.isGecko?d.formatNum((/rv\s*\:\s*([\.\,\d]+)/i).test(l)?RegExp.$1:"0.9"):null;d.isChrome=(/Chrome\s*\/\s*(\d[\d\.]*)/i).test(l);d.verChrome=d.isChrome?d.formatNum(RegExp.$1):null;d.isSafari=((/Apple/i).test(j)||(!j&&!d.isChrome))&&(/Safari\s*\/\s*(\d[\d\.]*)/i).test(l);d.verSafari=d.isSafari&&(/Version\s*\/\s*(\d[\d\.]*)/i).test(l)?d.formatNum(RegExp.$1):null;d.isOpera=(/Opera\s*[\/]?\s*(\d+\.?\d*)/i).test(l);d.verOpera=d.isOpera&&((/Version\s*\/\s*(\d+\.?\d*)/i).test(l)||1)?parseFloat(RegExp.$1,10):null;d.addWinEvent("load",d.handler(d.runWLfuncs,d))},init:function(d){var c=this,b,d,a={status:-3,plugin:0};if(!c.isString(d)){return a}if(d.length==1){c.getVersionDelimiter=d;return a}d=d.toLowerCase().replace(/\s/g,"");b=c.Plugins[d];if(!b||!b.getVersion){return a}a.plugin=b;if(!c.isDefined(b.installed)){b.installed=null;b.version=null;b.version0=null;b.getVersionDone=null;b.pluginName=d}c.garbage=false;if(c.isIE&&!c.ActiveXEnabled&&d!=="java"){a.status=-2;return a}a.status=1;return a},fPush:function(b,a){var c=this;if(c.isArray(a)&&(c.isFunc(b)||(c.isArray(b)&&b.length>0&&c.isFunc(b[0])))){a.push(b)}},callArray:function(b){var c=this,a;if(c.isArray(b)){for(a=0;a<b.length;a++){if(b[a]===null){return}c.call(b[a]);b[a]=null}}},call:function(c){var b=this,a=b.isArray(c)?c.length:-1;if(a>0&&b.isFunc(c[0])){c[0](b,a>1?c[1]:0,a>2?c[2]:0,a>3?c[3]:0)}else{if(b.isFunc(c)){c(b)}}},getVersionDelimiter:",",$$getVersion:function(a){return function(g,d,c){var e=a.init(g),f,b,h={};if(e.status<0){return null};f=e.plugin;if(f.getVersionDone!=1){f.getVersion(null,d,c);if(f.getVersionDone===null){f.getVersionDone=1}}a.cleanup();b=(f.version||f.version0);b=b?b.replace(a.splitNumRegx,a.getVersionDelimiter):b;return b}},cleanup:function(){var a=this;if(a.garbage&&a.isDefined(window.CollectGarbage)){window.CollectGarbage()}},isActiveXObject:function(d,b){var f=this,a=false,g,c='<object width="1" height="1" style="display:none" '+d.getCodeBaseVersion(b)+">"+d.HTML+f.openTag+"/object>";if(!f.head){return a}f.head.insertBefore(document.createElement("object"),f.head.firstChild);f.head.firstChild.outerHTML=c;try{f.head.firstChild.classid=d.classID}catch(g){}try{if(f.head.firstChild.object){a=true}}catch(g){}try{if(a&&f.head.firstChild.readyState<4){f.garbage=true}}catch(g){}f.head.removeChild(f.head.firstChild);return a},codebaseSearch:function(f,b){var c=this;if(!c.ActiveXEnabled||!f){return null}if(f.BIfuncs&&f.BIfuncs.length&&f.BIfuncs[f.BIfuncs.length-1]!==null){c.callArray(f.BIfuncs)}var d,o=f.SEARCH,k={};if(c.isStrNum(b)){if(o.match&&o.min&&c.compareNums(b,o.min)<=0){return true}if(o.match&&o.max&&c.compareNums(b,o.max)>=0){return false}d=c.isActiveXObject(f,b);if(d&&(!o.min||c.compareNums(b,o.min)>0)){o.min=b}if(!d&&(!o.max||c.compareNums(b,o.max)<0)){o.max=b}return d};var e=[0,0,0,0],l=[].concat(o.digits),a=o.min?1:0,j,i,h,g,m,n=function(p,r){var q=[].concat(e);q[p]=r;return c.isActiveXObject(f,q.join(","))};if(o.max){g=o.max.split(c.splitNumRegx);for(j=0;j<g.length;j++){g[j]=parseInt(g[j],10)}if(g[0]<l[0]){l[0]=g[0]}}if(o.min){m=o.min.split(c.splitNumRegx);for(j=0;j<m.length;j++){m[j]=parseInt(m[j],10)}if(m[0]>e[0]){e[0]=m[0]}}if(m&&g){for(j=1;j<m.length;j++){if(m[j-1]!=g[j-1]){break}if(g[j]<l[j]){l[j]=g[j]}if(m[j]>e[j]){e[j]=m[j]}}}if(o.max){for(j=1;j<l.length;j++){if(g[j]>0&&l[j]==0&&l[j-1]<o.digits[j-1]){l[j-1]+=1;break}}};for(j=0;j<l.length;j++){h={};for(i=0;i<20;i++){if(l[j]-e[j]<1){break}d=round((l[j]+e[j])/2);if(h["a"+d]){break}h["a"+d]=1;if(n(j,d)){e[j]=d;a=1}else{l[j]=d}}l[j]=e[j];if(!a&&n(j,e[j])){a=1};if(!a){break}};return a?e.join(","):null},addWinEvent:function(d,c){var e=this,a=window,b;if(e.isFunc(c)){if(a.addEventListener){a.addEventListener(d,c,false)}else{if(a.attachEvent){a.attachEvent("on"+d,c)}else{b=a["on"+d];a["on"+d]=e.winHandler(c,b)}}}},winHandler:function(d,c){return function(){d();if(typeof c=="function"){c()}}},WLfuncs0:[],WLfuncs:[],runWLfuncs:function(a){var b={};a.winLoaded=true;a.callArray(a.WLfuncs0);a.callArray(a.WLfuncs);if(a.onDoneEmptyDiv){a.onDoneEmptyDiv()}},winLoaded:false,$$onWindowLoaded:function(a){return function(b){if(a.winLoaded){a.call(b)}else{a.fPush(b,a.WLfuncs)}}},div:null,divID:"plugindetect",divWidth:50,pluginSize:1,emptyDiv:function(){var d=this,b,h,c,a,f,g;if(d.div&&d.div.childNodes){for(b=d.div.childNodes.length-1;b>=0;b--){c=d.div.childNodes[b];if(c&&c.childNodes){for(h=c.childNodes.length-1;h>=0;h--){g=c.childNodes[h];try{c.removeChild(g)}catch(f){}}}if(c){try{d.div.removeChild(c)}catch(f){}}}}if(!d.div){a=document.getElementById(d.divID);if(a){d.div=a}}if(d.div&&d.div.parentNode){try{d.div.parentNode.removeChild(d.div)}catch(f){}d.div=null}},DONEfuncs:[],onDoneEmptyDiv:function(){var c=this,a,b;if(!c.winLoaded){return}if(c.WLfuncs&&c.WLfuncs.length&&c.WLfuncs[c.WLfuncs.length-1]!==null){return}for(a in c){b=c[a];if(b&&b.funcs){if(b.OTF==3){return}if(b.funcs.length&&b.funcs[b.funcs.length-1]!==null){return}}}for(a=0;a<c.DONEfuncs.length;a++){c.callArray(c.DONEfuncs)}
11424c.emptyDiv()},getWidth:function(c){if(c){var a=c.scrollWidth||c.offsetWidth,b=this;if(b.isNum(a)){return a}}return -1},getTagStatus:function(m,g,a,b){var c=this,f,k=m.span,l=c.getWidth(k),h=a.span,j=c.getWidth(h),d=g.span,i=c.getWidth(d);if(!k||!h||!d||!c.getDOMobj(m)){return -2}if(j<i||l<0||j<0||i<0||i<=c.pluginSize||c.pluginSize<1){return 0}if(l>=i){return -1}try{if(l==c.pluginSize&&(!c.isIE||c.getDOMobj(m).readyState==4)){if(!m.winLoaded&&c.winLoaded){return 1}if(m.winLoaded&&c.isNum(b)){if(!c.isNum(m.count)){m.count=b}if(b-m.count>=10){return 1}}}}catch(f){}return 0},getDOMobj:function(g,a){var f,d=this,c=g?g.span:0,b=c&&c.firstChild?1:0;try{if(b&&a){d.div.focus()}}catch(f){}return b?c.firstChild:null},setStyle:function(b,g){var f=b.style,a,d,c=this;if(f&&g){for(a=0;a<g.length;a=a+2){try{f[g[a]]=g[a+1]}catch(d){}}}},insertDivInBody:function(i,g){var f,c=this,h="pd33993399",b=null,d=g?window.top.document:window.document,a=d.getElementsByTagName("body")[0]||d.body;if(!a){try{d.write('<div id="'+h+'">.'+c.openTag+"/div>");b=d.getElementById(h)}catch(f){}}a=d.getElementsByTagName("body")[0]||d.body;if(a){a.insertBefore(i,a.firstChild);if(b){a.removeChild(b)}}},insertHTML:function(f,b,g,a,k){var l,m=document,j=this,p,o=m.createElement("span"),n,i;var c=["outlineStyle","none","borderStyle","none","padding","0px","margin","0px","visibility","visible"];var h="outline-style:none;border-style:none;padding:0px;margin:0px;visibility:visible;";if(!j.isDefined(a)){a=""}if(j.isString(f)&&(/[^\s]/).test(f)){f=f.toLowerCase().replace(/\s/g,"");p=j.openTag+f+' width="'+j.pluginSize+'" height="'+j.pluginSize+'" ';p+='style="'+h+'display:inline;" ';for(n=0;n<b.length;n=n+2){if(/[^\s]/.test(b[n+1])){p+=b[n]+'="'+b[n+1]+'" '}}p+=">";for(n=0;n<g.length;n=n+2){if(/[^\s]/.test(g[n+1])){p+=j.openTag+'param name="'+g[n]+'" value="'+g[n+1]+'" />'}}p+=a+j.openTag+"/"+f+">"}else{p=a}if(!j.div){i=m.getElementById(j.divID);if(i){j.div=i}else{j.div=m.createElement("div");j.div.id=j.divID}j.setStyle(j.div,c.concat(["width",j.divWidth+"px","height",(j.pluginSize+3)+"px","fontSize",(j.pluginSize+3)+"px","lineHeight",(j.pluginSize+3)+"px","verticalAlign","baseline","display","block"]));if(!i){j.setStyle(j.div,["position","absolute","right","0px","top","0px"]);j.insertDivInBody(j.div)}}if(j.div&&j.div.parentNode){j.setStyle(o,c.concat(["fontSize",(j.pluginSize+3)+"px","lineHeight",(j.pluginSize+3)+"px","verticalAlign","baseline","display","inline"]));try{o.innerHTML=p}catch(l){};try{j.div.appendChild(o)}catch(l){};return{span:o,winLoaded:j.winLoaded,tagName:f,outerHTML:p}}return{span:null,winLoaded:j.winLoaded,tagName:"",outerHTML:p}},Plugins:{quicktime:{mimeType:["video/quicktime","application/x-quicktimeplayer","image/x-macpaint","image/x-quicktime"],progID:"QuickTimeCheckObject.QuickTimeCheck.1",progID0:"QuickTime.QuickTime",classID:"clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B",minIEver:7,HTML:'<param name="src" value="" /><param name="controller" value="false" />',getCodeBaseVersion:function(a){return'codebase="#version='+a+'"'},SEARCH:{min:0,max:0,match:0,digits:[16,128,128,0]},getVersion:function(c){var f=this,d=f.$,a=null,e=null,b;if(!d.isIE){if(d.hasMimeType(f.mimeType)){e=d.OS!=3?d.findNavPlugin("QuickTime.*Plug-?in",0):null;if(e&&e.name){a=d.getNum(e.name)}}}else{if(d.isStrNum(c)){b=c.split(d.splitNumRegx);if(b.length>3&&parseInt(b[3],10)>0){b[3]="9999"}c=b.join(",")}if(d.isStrNum(c)&&d.verIE>=f.minIEver&&f.canUseIsMin()>0){f.installed=f.isMin(c);f.getVersionDone=0;return}f.getVersionDone=1;if(!a&&d.verIE>=f.minIEver){a=f.CDBASE2VER(d.codebaseSearch(f))}if(!a){e=d.getAXO(f.progID);if(e&&e.QuickTimeVersion){a=e.QuickTimeVersion.toString(16);a=parseInt(a.charAt(0),16)+"."+parseInt(a.charAt(1),16)+"."+parseInt(a.charAt(2),16)}}}f.installed=a?1:(e?0:-1);f.version=d.formatNum(a,3)},cdbaseUpper:["7,60,0,0","0,0,0,0"],cdbaseLower:["7,50,0,0",null],cdbase2ver:[function(c,b){var a=b.split(c.$.splitNumRegx);return[a[0],a[1].charAt(0),a[1].charAt(1),a[2]].join(",")},null],CDBASE2VER:function(f){var e=this,c=e.$,b,a=e.cdbaseUpper,d=e.cdbaseLower;
11424if(f){f=c.formatNum(f);for(b=0;b<a.length;b++){if(a[b]&&c.compareNums(f,a[b])<0&&d[b]&&c.compareNums(f,d[b])>=0&&e.cdbase2ver[b]){return e.cdbase2ver[b](e,f)}}}return f},canUseIsMin:function(){var f=this,d=f.$,b,c=f.canUseIsMin,a=f.cdbaseUpper,e=f.cdbaseLower;if(!c.value){c.value=-1;for(b=0;b<a.length;b++){if(a[b]&&d.codebaseSearch(f,a[b])){c.value=1;break}if(e[b]&&d.codebaseSearch(f,e[b])){c.value=-1;break}}}f.SEARCH.match=c.value==1?1:0;return c.value},isMin:function(c){var b=this,a=b.$;return a.codebaseSearch(b,c)?0.7:-1}},flash:{mimeType:"application/x-shockwave-flash",progID:"ShockwaveFlash.ShockwaveFlash",classID:"clsid:D27CDB6E-AE6D-11CF-96B8-444553540000",getVersion:function(){var b=function(i){if(!i){return null}var e=/[\d][\d\,\.\s]*[rRdD]{0,1}[\d\,]*/.exec(i);return e?e[0].replace(/[rRdD\.]/g,",").replace(/\s/g,""):null};var j=this,g=j.$,k,h,l=null,c=null,a=null,f,m,d;if(!g.isIE){m=g.hasMimeType(j.mimeType);if(m){f=g.getDOMobj(g.insertHTML("object",["type",j.mimeType],[],"",j));try{l=g.getNum(f.GetVariable("$version"))}catch(k){}}if(!l){d=m?m.enabledPlugin:null;if(d&&d.description){l=b(d.description)}if(l){l=g.getPluginFileVersion(d,l)}}}else{for(h=15;h>2;h--){c=g.getAXO(j.progID+"."+h);if(c){a=h.toString();break}}if(!c){c=g.getAXO(j.progID)}if(a=="6"){try{c.AllowScriptAccess="always"}catch(k){return"6,0,21,0"}}try{l=b(c.GetVariable("$version"))}catch(k){}if(!l&&a){l=a}}j.installed=l?1:-1;j.version=g.formatNum(l);return true}},shockwave:{mimeType:"application/x-director",progID:"SWCtl.SWCtl",classID:"clsid:166B1BCA-3F9C-11CF-8075-444553540000",getVersion:function(){var a=null,b=null,g,f,d=this,c=d.$;if(!c.isIE){f=c.findNavPlugin("Shockwave\\s*for\\s*Director");if(f&&f.description&&c.hasMimeType(d.mimeType)){a=c.getNum(f.description)}if(a){a=c.getPluginFileVersion(f,a)}}else{try{b=c.getAXO(d.progID).ShockwaveVersion("")}catch(g){}if(c.isString(b)&&b.length>0){a=c.getNum(b)}else{if(c.getAXO(d.progID+".8")){a="8"}else{if(c.getAXO(d.progID+".7")){a="7"}else{if(c.getAXO(d.progID+".1")){a="6"}}}}}d.installed=a?1:-1;d.version=c.formatNum(a)}},zz:0}};PluginDetect.initScript();
11425
11426	var gArgCountErr='The "%%" function requires an even number of arguments.\nArguments should be in the form "atttributeName", "attributeValue", ...',gTagAttrs=null,gQTGeneratorVersion=1;function AC_QuickTimeVersion(){return gQTGeneratorVersion}function _QTComplain(a,b){b=b.replace("%%",a);alert(b)}function _QTAddAttribute(a,b,c){var d;d=gTagAttrs[a+b];null==d&&(d=gTagAttrs[b]);return null!=d?(0==b.indexOf(a)&&null==c&&(c=b.substring(a.length)),null==c&&(c=b),c+'="'+d+'" '):""}function _QTAddObjectAttr(a,b){if(0==a.indexOf("emb#"))return"";0==a.indexOf("obj#")&&null==b&&(b=a.substring(4));return _QTAddAttribute("obj#",a,b)}function _QTAddEmbedAttr(a,b){if(0==a.indexOf("obj#"))return"";0==a.indexOf("emb#")&&null==b&&(b=a.substring(4));return _QTAddAttribute("emb#",a,b)}function _QTAddObjectParam(a,b){var c,d="",e=b?" />":">";-1==a.indexOf("emb#")&&(c=gTagAttrs["obj#"+a],null==c&&(c=gTagAttrs[a]),0==a.indexOf("obj#")&&(a=a.substring(4)),null!=c&&(d='  <param name="'+a+'" value="'+c+'"'+e+"\n"));return d}function _QTDeleteTagAttrs(){for(var a=0;a<arguments.length;a++){var b=arguments[a];delete gTagAttrs[b];delete gTagAttrs["emb#"+b];delete gTagAttrs["obj#"+b]}}function _QTGenerate(a,b,c){if(4>c.length||0!=c.length%2)return _QTComplain(a,gArgCountErr),"";gTagAttrs=[];gTagAttrs.src=c[0];gTagAttrs.width=c[1];gTagAttrs.height=c[2];gTagAttrs.classid="clsid:02BF25D5-8C17-4B23-BC80-D3488ABDDC6B";gTagAttrs.pluginspage="http://www.apple.com/quicktime/download/";a=c[3];if(null==a||""==a)a="6,0,2,0";gTagAttrs.codebase="http://www.apple.com/qtactivex/qtplugin.cab#version="+a;for(var d,e=4;e<c.length;e+=2)d=c[e].toLowerCase(),a=c[e+1],"name"==d||"id"==d?gTagAttrs.name=a:gTagAttrs[d]=a;c="<object "+_QTAddObjectAttr("classid")+_QTAddObjectAttr("width")+_QTAddObjectAttr("height")+_QTAddObjectAttr("codebase")+_QTAddObjectAttr("name","id")+_QTAddObjectAttr("tabindex")+_QTAddObjectAttr("hspace")+_QTAddObjectAttr("vspace")+_QTAddObjectAttr("border")+_QTAddObjectAttr("align")+_QTAddObjectAttr("class")+_QTAddObjectAttr("title")+_QTAddObjectAttr("accesskey")+_QTAddObjectAttr("noexternaldata")+">\n"+_QTAddObjectParam("src",b);e="  <embed "+_QTAddEmbedAttr("src")+_QTAddEmbedAttr("width")+_QTAddEmbedAttr("height")+_QTAddEmbedAttr("pluginspage")+_QTAddEmbedAttr("name")+_QTAddEmbedAttr("align")+_QTAddEmbedAttr("tabindex");_QTDeleteTagAttrs("src","width","height","pluginspage","classid","codebase","name","tabindex","hspace","vspace","border","align","noexternaldata","class","title","accesskey");for(d in gTagAttrs)a=gTagAttrs[d],null!=a&&(e+=_QTAddEmbedAttr(d),c+=_QTAddObjectParam(d,b));return c+e+"> </embed>\n</object>"}function QT_GenerateOBJECTText(){return _QTGenerate("QT_GenerateOBJECTText",!1,arguments)};
11427
11428
11429	/*
11430		jQuery hashchange event v1.3
11431		https://github.com/cowboy/jquery-hashchange
11432		Copyright (c) 2010 "Cowboy" Ben Alman
11433		Dual licensed under the MIT and GPL licenses.
11434	*/
11435	(function(){function e(a){a=a||location.href;return"#"+a.replace(/^[^#]*#?(.*)$/,"$1")}var k=document,b,f=$.event.special,p=k.documentMode,m="oniLightBoxHashChange"in window&&(void 0===p||7<p);$.fn.iLightBoxHashChange=function(a){return a?this.bind("iLightBoxHashChange",a):this.trigger("iLightBoxHashChange")};$.fn.iLightBoxHashChange.delay=50;f.iLightBoxHashChange=$.extend(f.iLightBoxHashChange,{setup:function(){if(m)return!1;$(b.start)},teardown:function(){if(m)return!1;$(b.stop)}});b=function(){function a(){var c=
11436	e(),d=f(l);c!==l?(n(l=c,d),$(window).trigger("iLightBoxHashChange")):d!==l&&(location.href=location.href.replace(/#.*/,"")+d);g=setTimeout(a,$.fn.iLightBoxHashChange.delay)}var h={},g,l=e(),b=function(c){return c},n=b,f=b;h.start=function(){g||a()};h.stop=function(){g&&clearTimeout(g);g=void 0};browser.msie&&!m&&function(){var c,d;h.start=function(){c||(d=(d=$.fn.iLightBoxHashChange.src)&&d+e(),c=$('<iframe tabindex="-1" title="empty"/>').hide().one("load",function(){d||n(e());a()}).attr("src",d||
11437	"javascript:0").insertAfter("body")[0].contentWindow,k.onpropertychange=function(){try{"title"===event.propertyName&&(c.document.title=k.title)}catch(a){}})};h.stop=b;f=function(){return e(c.location.href)};n=function(a,d){var b=c.document,e=$.fn.iLightBoxHashChange.domain;
11437a!==d&&(b.title=k.title,b.open(),e&&b.write('<script>document.domain="'+e+'"\x3c/script>'),b.close(),c.location.hash=a)}}();return h}()})();
11438
11439	if (!Array.prototype.filter) {
11440		Array.prototype.filter = function(fun /*, thisp */ ) {
11441			"use strict";
11442
11443			if (this == null)
11444				throw new TypeError();
11445
11446			var t = Object(this);
11447			var len = t.length >>> 0;
11448			if (typeof fun != "function")
11449				throw new TypeError();
11450
11451			var res = [];
11452			var thisp = arguments[1];
11453			for (var i = 0; i < len; i++) {
11454				if (i in t) {
11455					var val = t[i]; // in case fun mutates this
11456					if (fun.call(thisp, val, i, t))
11457						res.push(val);
11458				}
11459			}
11460
11461			return res;
11462		};
11463	}
11464
11465	if (!Array.prototype.indexOf) {
11466		Array.prototype.indexOf = function(searchElement, fromIndex) {
11467			var k;
11468
11469			if (this == null) {
11470				throw new TypeError('"this" is null or not defined');
11471			}
11472
11473			var O = Object(this);
11474
11475			var len = O.length >>> 0;
11476
11477			if (len === 0) {
11478				return -1;
11479			}
11480
11481			var n = +fromIndex || 0;
11482
11483			if (abs(n) === Infinity) {
11484				n = 0;
11485			}
11486
11487			if (n >= len) {
11488				return -1;
11489			}
11490
11491			k = max(n >= 0 ? n : len - abs(n), 0);
11492
11493			while (k < len) {
11494				var kValue;
11495				if (k in O && O[k] === searchElement) {
11496					return k;
11497				}
11498				k++;
11499			}
11500			return -1;
11501		};
11502	}
11503
11504	if (!Array.prototype.lastIndexOf) {
11505		Array.prototype.lastIndexOf = function(searchElement /*, fromIndex*/ ) {
11506			"use strict";
11507
11508			if (this == null)
11509				throw new TypeError();
11510
11511			var t = Object(this);
11512			var len = t.length >>> 0;
11513			if (len === 0)
11514				return -1;
11515
11516			var n = len;
11517			if (arguments.length > 1) {
11518				n = Number(arguments[1]);
11519				if (n != n)
11520					n = 0;
11521				else if (n != 0 && n != (1 / 0) && n != -(1 / 0))
11522					n = (n > 0 || -1) * floor(abs(n));
11523			}
11524
11525			var k = n >= 0 ? min(n, len - 1) : len - abs(n);
11526
11527			for (; k >= 0; k--) {
11528				if (k in t && t[k] === searchElement)
11529					return k;
11530			}
11531			return -1;
11532		};
11533	}
11534})(jQuery, this);
11535
11536/**
11537 * Owl carousel
vendor: 12,108 bytes, lines 11538-12044
11538 * @version 2.0.0-beta.3
11539 * @author Bartosz Wojciechowski
11540 * @license The MIT License (MIT)
11541 * @todo Lazy Load Icon
11542 * @todo prevent animationend bubling
11543 * @todo itemsScaleUp
11544 * @todo Test Zepto
11545 * @todo stagePadding calculate wrong active classes
11546 */
11547;(function($, window, document, undefined) {
11548
11549	/**
11550	 * Creates a carousel.
11551	 * @class The Owl Carousel.
11552	 * @public
11553	 * @param {HTMLElement|jQuery} element - The element to create the carousel for.
11554	 * @param {Object} [options] - The options
11555	 */
11556	function Owl(element, options) {
11557
11558		/**
11559		 * Current settings for the carousel.
11560		 * @public
11561		 */
11562		this.settings = null;
11563
11564		/**
11565		 * Current options set by the caller including defaults.
11566		 * @public
11567		 */
11568		this.options = $.extend({}, Owl.Defaults, options);
11569
11570		/**
11571		 * Plugin element.
11572		 * @public
11573		 */
11574		this.$element = $(element);
11575
11576		/**
11577		 * Proxied event handlers.
11578		 * @protected
11579		 */
11580		this._handlers = {};
11581
11582		/**
11583		 * References to the running plugins of this carousel.
11584		 * @protected
11585		 */
11586		this._plugins = {};
11587
11588		/**
11589		 * Currently suppressed events to prevent them from beeing retriggered.
11590		 * @protected
11591		 */
11592		this._supress = {};
11593
11594		/**
11595		 * Absolute current position.
11596		 * @protected
11597		 */
11598		this._current = null;
11599
11600		/**
11601		 * Animation speed in milliseconds.
11602		 * @protected
11603		 */
11604		this._speed = null;
11605
11606		/**
11607		 * Coordinates of all items in pixel.
11608		 * @todo The name of this member is missleading.
11609		 * @protected
11610		 */
11611		this._coordinates = [];
11612
11613		/**
11614		 * Current breakpoint.
11615		 * @todo Real media queries would be nice.
11616		 * @protected
11617		 */
11618		this._breakpoint = null;
11619
11620		/**
11621		 * Current width of the plugin element.
11622		 */
11623		this._width = null;
11624
11625		/**
11626		 * All real items.
11627		 * @protected
11628		 */
11629		this._items = [];
11630
11631		/**
11632		 * All cloned items.
11633		 * @protected
11634		 */
11635		this._clones = [];
11636
11637		/**
11638		 * Merge values of all items.
11639		 * @todo Maybe this could be part of a plugin.
11640		 * @protected
11641		 */
11642		this._mergers = [];
11643
11644		/**
11645		 * Widths of all items.
11646		 */
11647		this._widths = [];
11648
11649		/**
11650		 * Invalidated parts within the update process.
11651		 * @protected
11652		 */
11653		this._invalidated = {};
11654
11655		/**
11656		 * Ordered list of workers for the update process.
11657		 * @protected
11658		 */
11659		this._pipe = [];
11660
11661		/**
11662		 * Current state information for the drag operation.
11663		 * @todo #261
11664		 * @protected
11665		 */
11666		this._drag = {
11667			time: null,
11668			target: null,
11669			pointer: null,
11670			stage: {
11671				start: null,
11672				current: null
11673			},
11674			direction: null
11675		};
11676
11677		/**
11678		 * Current state information and their tags.
11679		 * @type {Object}
11680		 * @protected
11681		 */
11682		this._states = {
11683			current: {},
11684			tags: {
11685				'initializing': [ 'busy' ],
11686				'animating': [ 'busy' ],
11687				'dragging': [ 'interacting' ]
11688			}
11689		};
11690
11691		$.each([ 'onResize', 'onThrottledResize' ], $.proxy(function(i, handler) {
11692			this._handlers[handler] = $.proxy(this[handler], this);
11693		}, this));
11694
11695		$.each(Owl.Plugins, $.proxy(function(key, plugin) {
11696			this._plugins[key.charAt(0).toLowerCase() + key.slice(1)]
11697				= new plugin(this);
11698		}, this));
11699
11700		$.each(Owl.Workers, $.proxy(function(priority, worker) {
11701			this._pipe.push({
11702				'filter': worker.filter,
11703				'run': $.proxy(worker.run, this)
11704			});
11705		}, this));
11706
11707		this.setup();
11708		this.initialize();
11709	}
11710
11711	/**
11712	 * Default options for the carousel.
11713	 * @public
11714	 */
11715	Owl.Defaults = {
11716		items: 3,
11717		loop: false,
11718		center: false,
11719		rewind: false,
11720
11721		mouseDrag: true,
11722		touchDrag: true,
11723		pullDrag: true,
11724		freeDrag: false,
11725
11726		margin: 0,
11727		stagePadding: 0,
11728
11729		merge: false,
11730		mergeFit: true,
11731		autoWidth: false,
11732
11733		startPosition: 0,
11734		rtl: false,
11735
11736		smartSpeed: 250,
11737		fluidSpeed: false,
11738		dragEndSpeed: false,
11739
11740		responsive: {},
11741		responsiveRefreshRate: 200,
11742		responsiveBaseElement: window,
11743
11744		fallbackEasing: 'swing',
11745
11746		info: false,
11747
11748		nestedItemSelector: false,
11749		itemElement: 'div',
11750		stageElement: 'div',
11751
11752		refreshClass: 'owl-refresh',
11753		loadedClass: 'owl-loaded',
11754		loadingClass: 'owl-loading',
11755		rtlClass: 'owl-rtl',
11756		responsiveClass: 'owl-responsive',
11757		dragClass: 'owl-drag',
11758		itemClass: 'owl-item',
11759		stageClass: 'owl-stage',
11760		stageOuterClass: 'owl-stage-outer',
11761		grabClass: 'owl-grab'
11762	};
11763
11764	/**
11765	 * Enumeration for width.
11766	 * @public
11767	 * @readonly
11768	 * @enum {String}
11769	 */
11770	Owl.Width = {
11771		Default: 'default',
11772		Inner: 'inner',
11773		Outer: 'outer'
11774	};
11775
11776	/**
11777	 * Enumeration for types.
11778	 * @public
11779	 * @readonly
11780	 * @enum {String}
11781	 */
11782	Owl.Type = {
11783		Event: 'event',
11784		State: 'state'
11785	};
11786
11787	/**
11788	 * Contains all registered plugins.
11789	 * @public
11790	 */
11791	Owl.Plugins = {};
11792
11793	/**
11794	 * List of workers involved in the update process.
11795	 */
11796	Owl.Workers = [ {
11797		filter: [ 'width', 'settings' ],
11798		run: function() {
11799			this._width = (this.$element.closest('.px-gutter').length) ? 12 * Math.ceil(this.$element.width() / 12) : this.$element.width();
11800			//this._width = (UNCODE.isMobile) ? this.$element.width() : 12 * Math.ceil(this.$element.width() / 12);
11801		}
11802	}, {
11803		filter: [ 'width', 'items', 'settings' ],
11804		run: function(cache) {
11805			cache.current = this._items && this._items[this.relative(this._current)];
11806		}
11807	}, {
11808		filter: [ 'items', 'settings' ],
11809		run: function() {
11810			this.$stage.children('.cloned').remove();
11811		}
11812	}, {
11813		filter: [ 'width', 'items', 'settings' ],
11814		run: function(cache) {
11815			var margin = this.settings.margin || '',
11816				grid = !this.settings.autoWidth,
11817				rtl = this.settings.rtl,
11818				css = {
11819					'width': 'auto',
11820					'margin-left': rtl ? margin : '',
11821					'margin-right': rtl ? '' : margin
11822				};
11823
11824			!grid && this.$stage.children().css(css);
11825
11826			cache.css = css;
11827		}
11828	}, {
11829		filter: [ 'width', 'items', 'settings' ],
11830		run: function(cache) {
11831			//UNCODE edit to fix Chrome stage padding issue
11832			var width = Math.round((this.width() / this.settings.items).toFixed(3) - this.settings.margin),
11833				merge = null,
11834				iterator = this._items.length,
11835				grid = !this.settings.autoWidth,
11836				widths = [];
11837
11838			cache.items = {
11839				merge: false,
11840				width: width
11841			};
11842
11843			while (iterator--) {
11844				merge = this._mergers[iterator];
11845				merge = this.settings.mergeFit && Math.min(merge, this.settings.items) || merge;
11846
11847				cache.items.merge = merge > 1 || cache.items.merge;
11848
11849				widths[iterator] = !grid ? this._items[iterator].width() : width * merge;
11850			}
11851
11852			this._widths = widths;
11853		}
11854	}, {
11855		filter: [ 'items', 'settings' ],
11856		run: function() {
11857			var clones = [],
11858				items = this._items,
11859				settings = this.settings,
11860				view = Math.max(settings.items * 2, 4),
11861				size = Math.ceil(items.length / 2) * 2,
11862				repeat = settings.loop && items.length ? settings.rewind ? view : Math.max(view, size) : 0,
11863				append = '',
11864				prepend = '';
11865
11866			repeat /= 2;
11867
11868			while (repeat--) {
11869				clones.push(this.normalize(clones.length / 2, true));
11870				append = append + items[clones[clones.length - 1]][0].outerHTML;
11871				clones.push(this.normalize(items.length - 1 - (clones.length - 1) / 2, true));
11872				prepend = items[clones[clones.length - 1]][0].outerHTML + prepend;
11873			}
11874
11875			this._clones = clones;
11876
11877			// var appendVideo = $(append).find('.uncode-video-container');
11878			// if (appendVideo.length) {
11879			// 	appendVideo.attr('data-id', Math.round(Math.random() * 100000));
11880			// }
11881			// var prependVideo = $(prepend).find('.uncode-video-container');
11882			// if (prependVideo.length) {
11883			// 	prependVideo.attr('data-id', Math.round(Math.random() * 100000));
11884			// }
11885			$(append).addClass('cloned').appendTo(this.$stage);
11886			$(prepend).addClass('cloned').prependTo(this.$stage);
11887		}
11888	}, {
11889		filter: [ 'width', 'items', 'settings' ],
11890		run: function() {
11891			var rtl = this.settings.rtl ? 1 : -1,
11892				size = this._clones.length + this._items.length,
11893				iterator = -1,
11894				previous = 0,
11895				current = 0,
11896				coordinates = [];
11897
11898			while (++iterator < size) {
11899				previous = coordinates[iterator - 1] || 0;
11900				current = this._widths[this.relative(iterator)] + this.settings.margin;
11901				coordinates.push(previous + current * rtl);
11902			}
11903
11904			this._coordinates = coordinates;
11905		}
11906	}, {
11907		filter: [ 'width', 'items', 'settings' ],
11908		run: function() {
11909			var stagePadding = (this._width < 480 && this.settings.stagePadding > 0) ? 41 : (this._width * this.settings.stagePadding) / 200,
11910				padding = stagePadding,
11911				coordinates = this._coordinates,
11912				css = {
11913					'width': Math.ceil(Math.abs(coordinates[coordinates.length - 1])) + padding * 2,
11914					'padding-left': padding || '',
11915					'padding-right': padding || ''
11916				};
11917
11918			this.$stage.css(css);
11919		}
11920	}, {
11921		filter: [ 'width', 'items', 'settings' ],
11922		run: function(cache) {
11923			var iterator = this._coordinates.length,
11924				grid = !this.settings.autoWidth,
11925				items = this.$stage.children();
11926
11927			if (grid && cache.items.merge) {
11928				while (iterator--) {
11929					cache.css.width = this._widths[this.relative(iterator)];
11930					items.eq(iterator).css(cache.css);
11931				}
11932			} else if (grid) {
11933				cache.css.width = cache.items.width;
11934				items.css(cache.css);
11935			}
11936		}
11937	}, {
11938		filter: [ 'items' ],
11939		run: function() {
11940			this._coordinates.length < 1 && this.$stage.removeAttr('style');
11941		}
11942	}, {
11943		filter: [ 'width', 'items', 'settings' ],
11944		run: function(cache) {
11945			cache.current = cache.current ? this.$stage.children().index(cache.current) : 0;
11946			cache.current = Math.max(this.minimum(), Math.min(this.maximum(), cache.current));
11947			this.reset(cache.current);
11948		}
11949	}, {
11950		filter: [ 'position' ],
11951		run: function() {
11952			this.animate(this.coordinates(this._current));
11953		}
11954	}, {
11955		filter: [ 'width', 'position', 'items', 'settings' ],
11956		run: function() {
11957			var stagePadding = (this._width < 480 && this.settings.stagePadding > 0) ? 41 : (this._width * this.settings.stagePadding) / 200,
11958				rtl = this.settings.rtl ? 1 : -1,
11959				padding = this.settings.stagePadding * 2,
11960				begin = this.coordinates(this.current()) + padding,
11961				end = begin + this.width() * rtl,
11962				inner, outer, matches = [], i, n;
11963
11964			for (i = 0, n = this._coordinates.length; i < n; i++) {
11965				inner = this._coordinates[i - 1] || 0;
11966				outer = Math.abs(this._coordinates[i]) + padding * rtl;
11967
11968				if ((this.op(inner, '<=', begin) && (this.op(inner, '>', end)))
11969					|| (this.op(outer, '<', begin) && this.op(outer, '>', end))) {
11970					matches.push(i);
11971				}
11972			}
11973
11974			this.$stage.children('.active').removeClass('active');
11975			this.$stage.children(':eq(' + matches.join('), :eq(') + ')').addClass('active');
11976
11977			if (this.settings.center) {
11978				this.$stage.children('.center').removeClass('center');
11979				this.$stage.children().eq(this.current()).addClass('center');
11980			}
11981		}
11982	} ];
11983
11984	/**
11985	 * Initializes the carousel.
11986	 * @protected
11987	 */
11988	Owl.prototype.initialize = function() {
11989		this.enter('initializing');
11990		this.trigger('initialize');
11991
11992		this.$element.toggleClass(this.settings.rtlClass, this.settings.rtl);
11993
11994		if (this.settings.autoWidth && !this.is('pre-loading')) {
11995			var imgs, nestedSelector, width;
11996			imgs = this.$element.find('img');
11997			nestedSelector = this.settings.nestedItemSelector ? '.' + this.settings.nestedItemSelector : undefined;
11998			width = this.$element.children(nestedSelector).width();
11999
12000			if (imgs.length && width <= 0) {
12001				this.preloadAutoWidthImages(imgs);
12002			}
12003		}
12004
12005		this.$element.addClass(this.options.loadingClass);
12006
12007		// create stage
12008		this.$stage = $('<' + this.settings.stageElement + ' class="' + this.settings.stageClass + '"/>')
12009			.wrap('<div class="' + this.settings.stageOuterClass + '"/>');
12010
12011		// append stage
12012		this.$element.append(this.$stage.parent());
12013
12014		// append content
12015		this.replace(this.$element.children().not(this.$stage.parent()));
12016
12017		// check visibility
12018		if (this.$element.is(':visible')) {
12019			// update view
12020			this.refresh();
12021		} else {
12022			// invalidate width
12023			this.invalidate('width');
12024		}
12025
12026		this.$element
12027			.removeClass(this.options.loadingClass)
12028			.addClass(this.options.loadedClass);
12029
12030		// register event handlers
12031		this.registerEventHandlers();
12032
12033		this.leave('initializing');
12034		this.trigger('initialized');
12035	};
12036
12037	/**
12038	 * Setups the current settings.
12039	 * @todo Remove responsive classes. Why should adaptive designs be brought into IE8?
12040	 * @todo Support for media queries by using `matchMedia` would be nice.
12041	 * @public
12042	 */
12043	Owl.prototype.setup = function() {
12044		var viewport = this.viewport(),
12045			overwrites = this.options.responsive,
12046			match = -1,
12047			settings = null;
12048
12049		if (!overwrites) {
12050			settings = $.extend({}, this.options);
12051		} else {
12052			$.each(overwrites, function(breakpoint) {
12053				if (breakpoint <= viewport && breakpoint > match) {
12054					match = Number(breakpoint);
12055				}
12056			});
12057
12058			settings = $.extend({}, this.options, overwrites[match]);
12059			delete settings.responsive;
12060
12061			// responsive class
12062			if (settings.responsiveClass) {
12063				this.$element.attr('class',
12064					this.$element.attr('class').replace(new RegExp('(' + this.options.responsiveClass + '-)\\S+\\s', 'g'), '$1' + match)
12065				);
12066			}
12067		}
12068
12069		if (this.settings === null || this._breakpoint !== match) {
12070			this.trigger('change', { property: { name: 'settings', value: settings } });
12071			this._breakpoint = match;
12072			this.settings = settings;
12073			this.invalidate('settings');
12074			this.trigger('changed', { property: { name: 'settings', value: this.settings } });
12075		}
12076	};
12077
12078	/**
12079	 * Updates option logic if necessery.
12080	 * @protected
12081	 */
12082	Owl.prototype.optionsLogic = function() {
12083		if (this.settings.autoWidth) {
12084			this.settings.stagePadding = false;
12085			this.settings.merge = false;
12086		}
12087	};
12088
12089	/**
12090	 * Prepares an item before add.
12091	 * @todo Rename event parameter `content` to `item`.
12092	 * @protected
12093	 * @returns {jQuery|HTMLElement} - The item container.
12094	 */
12095	Owl.prototype.prepare = function(item, index) {
12096		var event = this.trigger('prepare', { content: item });
12097		if (!event.data) {
12098			event.data = $('<' + this.settings.itemElement + '/>')
12099				.addClass(this.options.itemClass).attr('data-index', index + 1).append(item)
12100		}
12101
12102		this.trigger('prepared', { content: event.data });
12103
12104		return event.data;
12105	};
12106
12107	/**
12108	 * Updates the view.
12109	 * @public
12110	 */
12111	Owl.prototype.update = function() {
12112		var i = 0,
12113			n = this._pipe.length,
12114			filter = $.proxy(function(p) { return this[p] }, this._invalidated),
12115			cache = {};
12116
12117		while (i < n) {
12118			if (this._invalidated.all || $.grep(this._pipe[i].filter, filter).length > 0) {
12119				this._pipe[i].run(cache);
12120			}
12121			i++;
12122		}
12123
12124		this._invalidated = {};
12125
12126		!this.is('valid') && this.enter('valid');
12127	};
12128
12129	/**
12130	 * Gets the width of the view.
12131	 * @public
12132	 * @param {Owl.Width} [dimension=Owl.Width.Default] - The dimension to return.
12133	 * @returns {Number} - The width of the view in pixel.
12134	 */
12135	Owl.prototype.width = function(dimension) {
12136		dimension = dimension || Owl.Width.Default;
12137		var stagePadding = (this._width < 480 && this.settings.stagePadding > 0) ? 41 : (this._width * this.settings.stagePadding) / 200;
12138		switch (dimension) {
12139			case Owl.Width.Inner:
12140			case Owl.Width.Outer:
12141				return this._width;
12142			default:
12143				return this._width - stagePadding * 2 + this.settings.margin;
12144		}
12145	};
12146
12147	/**
12148	 * Refreshes the carousel primarily for adaptive purposes.
12149	 * @public
12150	 */
12151	Owl.prototype.refresh = function() {
12152		this.enter('refreshing');
12153		this.trigger('refresh');
12154
12155		this.setup();
12156
12157		this.optionsLogic();
12158
12159		this.$element.addClass(this.options.refreshClass);
12160
12161		this.update();
12162
12163		this.$element.removeClass(this.options.refreshClass);
12164
12165		this.leave('refreshing');
12166		this.trigger('refreshed');
12167	};
12168
12169	/**
12170	 * Checks window `resize` event.
12171	 * @protected
12172	 */
12173	Owl.prototype.onThrottledResize = function() {
12174		window.clearTimeout(this.resizeTimer);
12175		this.resizeTimer = window.setTimeout(this._handlers.onResize, this.settings.responsiveRefreshRate);
12176	};
12177
12178	/**
12179	 * Checks window `resize` event.
12180	 * @protected
12181	 */
12182	Owl.prototype.onResize = function() {
12183		if (!this._items.length) {
12184			return false;
12185		}
12186
12187		if (this._width === this.$element.width()) {
12188			return false;
12189		}
12190
12191		if (!this.$element.is(':visible')) {
12192			return false;
12193		}
12194
12195		this.enter('resizing');
12196
12197		if (this.trigger('resize').isDefaultPrevented()) {
12198			this.leave('resizing');
12199			return false;
12200		}
12201
12202		this.invalidate('width');
12203
12204		this.refresh();
12205
12206		this.leave('resizing');
12207		this.trigger('resized');
12208	};
12209
12210	/**
12211	 * Registers event handlers.
12212	 * @todo Check `msPointerEnabled`
12213	 * @todo #261
12214	 * @protected
12215	 */
12216	Owl.prototype.registerEventHandlers = function() {
12217		if ($.support.transition) {
12218			this.$stage.on($.support.transition.end + '.owl.core', $.proxy(this.onTransitionEnd, this));
12219		}
12220
12221		if (this.settings.responsive !== false) {
12222			this.on(window, 'resize', this._handlers.onThrottledResize);
12223		}
12224
12225		if (this.settings.mouseDrag) {
12226			this.$element.addClass(this.options.dragClass);
12227			this.$stage.on('mousedown.owl.core', $.proxy(this.onDragStart, this));
12228			this.$stage.on('dragstart.owl.core selectstart.owl.core', function() { return false });
12229		}
12230
12231		if (this.settings.touchDrag){
12232			this.$stage.on('touchstart.owl.core', $.proxy(this.onDragStart, this));
12233			this.$stage.on('touchcancel.owl.core', $.proxy(this.onDragEnd, this));
12234		}
12235	};
12236
12237	/**
12238	 * Handles `touchstart` and `mousedown` events.
12239	 * @todo Horizontal swipe threshold as option
12240	 * @todo #261
12241	 * @protected
12242	 * @param {Event} event - The event arguments.
12243	 */
12244	Owl.prototype.onDragStart = function(event) {
12245		var stage = null;
12246
12247		if (event.which === 3) {
12248			return;
12249		}
12250
vendor: 9,106 bytes, lines 12251-12566
12251		if ($.support.transform) {
12252			stage = this.$stage.css('transform').replace(/.*\(|\)| /g, '').split(',');
12253			stage = {
12254				x: stage[stage.length === 16 ? 12 : 4],
12255				y: stage[stage.length === 16 ? 13 : 5]
12256			};
12257		} else {
12258			stage = this.$stage.position();
12259			stage = {
12260				x: this.settings.rtl ?
12261					stage.left + this.$stage.width() - this.width() + this.settings.margin :
12262					stage.left,
12263				y: stage.top
12264			};
12265		}
12266
12267		if (this.is('animating')) {
12268			$.support.transform ? this.animate(stage.x) : this.$stage.stop()
12269			this.invalidate('position');
12270		}
12271
12272		this.$element.toggleClass(this.options.grabClass, event.type === 'mousedown');
12273
12274		this.speed(0);
12275
12276		this._drag.time = new Date().getTime();
12277		this._drag.target = $(event.target);
12278		this._drag.stage.start = stage;
12279		this._drag.stage.current = stage;
12280		this._drag.pointer = this.pointer(event);
12281
12282		$(document).on('mouseup.owl.core touchend.owl.core', $.proxy(this.onDragEnd, this));
12283
12284		$(document).one('mousemove.owl.core touchmove.owl.core', $.proxy(function(event) {
12285			var delta = this.difference(this._drag.pointer, this.pointer(event));
12286
12287			$(document).on('mousemove.owl.core touchmove.owl.core', $.proxy(this.onDragMove, this));
12288
12289			if (Math.abs(delta.x) < Math.abs(delta.y) && this.is('valid')) {
12290				return;
12291			}
12292
12293			event.preventDefault();
12294
12295			this.enter('dragging');
12296			this.trigger('drag');
12297		}, this));
12298	};
12299
12300	/**
12301	 * Handles the `touchmove` and `mousemove` events.
12302	 * @todo #261
12303	 * @protected
12304	 * @param {Event} event - The event arguments.
12305	 */
12306	Owl.prototype.onDragMove = function(event) {
12307		var minimum = null,
12308			maximum = null,
12309			pull = null,
12310			delta = this.difference(this._drag.pointer, this.pointer(event)),
12311			stage = this.difference(this._drag.stage.start, delta);
12312
12313		if (!this.is('dragging')) {
12314			return;
12315		}
12316
12317		event.preventDefault();
12318
12319		if (this.settings.loop) {
12320			minimum = this.coordinates(this.minimum());
12321			maximum = this.coordinates(this.maximum() + 1) - minimum;
12322			stage.x = (((stage.x - minimum) % maximum + maximum) % maximum) + minimum;
12323		} else {
12324			minimum = this.settings.rtl ? this.coordinates(this.maximum()) : this.coordinates(this.minimum());
12325			maximum = this.settings.rtl ? this.coordinates(this.minimum()) : this.coordinates(this.maximum());
12326			pull = this.settings.pullDrag ? -1 * delta.x / 5 : 0;
12327			stage.x = Math.max(Math.min(stage.x, minimum + pull), maximum + pull);
12328		}
12329
12330		this._drag.stage.current = stage;
12331
12332		this.animate(stage.x);
12333	};
12334
12335	/**
12336	 * Handles the `touchend` and `mouseup` events.
12337	 * @todo #261
12338	 * @todo Threshold for click event
12339	 * @protected
12340	 * @param {Event} event - The event arguments.
12341	 */
12342	Owl.prototype.onDragEnd = function(event) {
12343		var delta = this.difference(this._drag.pointer, this.pointer(event)),
12344			stage = this._drag.stage.current,
12345			direction = delta.x > 0 ^ this.settings.rtl ? 'left' : 'right';
12346
12347		$(document).off('.owl.core');
12348
12349		this.$element.removeClass(this.options.grabClass);
12350
12351		if (delta.x !== 0 && this.is('dragging') || !this.is('valid')) {
12352			this.speed(this.settings.dragEndSpeed || this.settings.smartSpeed);
12353			this.current(this.closest(stage.x, delta.x !== 0 ? direction : this._drag.direction));
12354			this.invalidate('position');
12355			this.update();
12356
12357			this._drag.direction = direction;
12358
12359			if (Math.abs(delta.x) > 3 || new Date().getTime() - this._drag.time > 300) {
12360				this._drag.target.one('click.owl.core', function() { return false; });
12361			}
12362		}
12363
12364		if (!this.is('dragging')) {
12365			return;
12366		}
12367
12368		this.leave('dragging');
12369		this.trigger('dragged');
12370	};
12371
12372	/**
12373	 * Gets absolute position of the closest item for a coordinate.
12374	 * @todo Setting `freeDrag` makes `closest` not reusable. See #165.
12375	 * @protected
12376	 * @param {Number} coordinate - The coordinate in pixel.
12377	 * @param {String} direction - The direction to check for the closest item. Ether `left` or `right`.
12378	 * @return {Number} - The absolute position of the closest item.
12379	 */
12380	Owl.prototype.closest = function(coordinate, direction) {
12381		var position = -1,
12382			pull = 30,
12383			width = this.width(),
12384			coordinates = this.coordinates();
12385
12386		if (!this.settings.freeDrag) {
12387			// check closest item
12388			$.each(coordinates, $.proxy(function(index, value) {
12389				if (coordinate > value - pull && coordinate < value + pull) {
12390					position = index;
12391				} else if (this.op(coordinate, '<', value)
12392					&& this.op(coordinate, '>', coordinates[index + 1] || value - width)) {
12393					position = direction === 'left' ? index + 1 : index;
12394				}
12395				return position === -1;
12396			}, this));
12397		}
12398
12399		if (!this.settings.loop) {
12400			// non loop boundries
12401			if (this.op(coordinate, '>', coordinates[this.minimum()])) {
12402				position = coordinate = this.minimum();
12403			} else if (this.op(coordinate, '<', coordinates[this.maximum()])) {
12404				position = coordinate = this.maximum();
12405			}
12406		}
12407
12408		return position;
12409	};
12410
12411	/**
12412	 * Animates the stage.
12413	 * @todo #270
12414	 * @public
12415	 * @param {Number} coordinate - The coordinate in pixels.
12416	 */
12417	Owl.prototype.animate = function(coordinate) {
12418		var animate = this.speed() > 0;
12419
12420		this.is('animating') && this.onTransitionEnd();
12421
12422		if (animate) {
12423			this.enter('animating');
12424			this.trigger('translate');
12425		}
12426
12427		if ($.support.transform3d && $.support.transition) {
12428			this.$stage.css({
12429				transform: 'translate3d(' + coordinate + 'px,0px,0px)',
12430				transition: (this.speed() / 1000) + 's'
12431			});
12432		} else if (animate) {
12433			this.$stage.animate({
12434				left: coordinate + 'px'
12435			}, this.speed(), this.settings.fallbackEasing, $.proxy(this.onTransitionEnd, this));
12436		} else {
12437			this.$stage.css({
12438				left: coordinate + 'px'
12439			});
12440		}
12441	};
12442
12443	/**
12444	 * Checks whether the carousel is in a specific state or not.
12445	 * @param {String} state - The state to check.
12446	 * @returns {Boolean} - The flag which indicates if the carousel is busy.
12447	 */
12448	Owl.prototype.is = function(state) {
12449		return this._states.current[state] && this._states.current[state] > 0;
12450	};
12451
12452	/**
12453	 * Sets the absolute position of the current item.
12454	 * @public
12455	 * @param {Number} [position] - The new absolute position or nothing to leave it unchanged.
12456	 * @returns {Number} - The absolute position of the current item.
12457	 */
12458	Owl.prototype.current = function(position) {
12459		if (position === undefined) {
12460			return this._current;
12461		}
12462
12463		if (this._items.length === 0) {
12464			return undefined;
12465		}
12466
12467		position = this.normalize(position);
12468
12469		if (this._current !== position) {
12470			var event = this.trigger('change', { property: { name: 'position', value: position } });
12471
12472			if (event.data !== undefined) {
12473				position = this.normalize(event.data);
12474			}
12475
12476			this._current = position;
12477
12478			this.invalidate('position');
12479
12480			this.trigger('changed', { property: { name: 'position', value: this._current } });
12481		}
12482
12483		return this._current;
12484	};
12485
12486	/**
12487	 * Invalidates the given part of the update routine.
12488	 * @param {String} [part] - The part to invalidate.
12489	 * @returns {Array.<String>} - The invalidated parts.
12490	 */
12491	Owl.prototype.invalidate = function(part) {
12492		if ($.type(part) === 'string') {
12493			this._invalidated[part] = true;
12494			this.is('valid') && this.leave('valid');
12495		}
12496		return $.map(this._invalidated, function(v, i) { return i });
12497	};
12498
12499	/**
12500	 * Resets the absolute position of the current item.
12501	 * @public
12502	 * @param {Number} position - The absolute position of the new item.
12503	 */
12504	Owl.prototype.reset = function(position) {
12505		position = this.normalize(position);
12506
12507		if (position === undefined) {
12508			return;
12509		}
12510
12511		this._speed = 0;
12512		this._current = position;
12513
12514		this.suppress([ 'translate', 'translated' ]);
12515
12516		this.animate(this.coordinates(position));
12517
12518		this.release([ 'translate', 'translated' ]);
12519	};
12520
12521	/**
12522	 * Normalizes an absolute or a relative position of an item.
12523	 * @public
12524	 * @param {Number} position - The absolute or relative position to normalize.
12525	 * @param {Boolean} [relative=false] - Whether the given position is relative or not.
12526	 * @returns {Number} - The normalized position.
12527	 */
12528	Owl.prototype.normalize = function(position, relative) {
12529		var n = this._items.length,
12530			m = relative ? 0 : this._clones.length;
12531
12532		if (!$.isNumeric(position) || n < 1) {
12533			position = undefined;
12534		} else if (position < 0 || position >= n + m) {
12535			position = ((position - m / 2) % n + n) % n + m / 2;
12536		}
12537
12538		return position;
12539	};
12540
12541	/**
12542	 * Converts an absolute position of an item into a relative one.
12543	 * @public
12544	 * @param {Number} position - The absolute position to convert.
12545	 * @returns {Number} - The converted position.
12546	 */
12547	Owl.prototype.relative = function(position) {
12548		position -= this._clones.length / 2;
12549		return this.normalize(position, true);
12550	};
12551
12552	/**
12553	 * Gets the maximum position for the current item.
12554	 * @public
12555	 * @param {Boolean} [relative=false] - Whether to return an absolute position or a relative position.
12556	 * @returns {Number}
12557	 */
12558	Owl.prototype.maximum = function(relative) {
12559		var settings = this.settings,
12560			maximum = this._coordinates.length,
12561			boundary = Math.abs(this._coordinates[maximum - 1]) - this._width,
12562			i = -1, j;
12563
12564		if (settings.loop) {
12565			maximum = this._clones.length / 2 + this._items.length - 1;
12566		} else if (settings.autoWidth || settings.merge) {
12567			// binary search
12568			while (maximum - i > 1) {
12569				Math.abs(this._coordinates[j = maximum + i >> 1]) < boundary
12570					? i = j : maximum = j;
12571			}
12572		} else if (settings.center) {
12573			maximum = this._items.length - 1;
12574		} else {
12575			maximum = this._items.length - settings.items;
12576		}
12577
12578		if (relative) {
12579			maximum -= this._clones.length / 2;
12580		}
12581
12582		return Math.max(maximum, 0);
12583	};
12584
12585	/**
12586	 * Gets the minimum position for the current item.
12587	 * @public
12588	 * @param {Boolean} [relative=false] - Whether to return an absolute position or a relative position.
12589	 * @returns {Number}
12590	 */
12591	Owl.prototype.minimum = function(relative) {
12592		return relative ? 0 : this._clones.length / 2;
12593	};
12594
12595	/**
12596	 * Gets an item at the specified relative position.
12597	 * @public
12598	 * @param {Number} [position] - The relative position of the item.
12599	 * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given.
12600	 */
12601	Owl.prototype.items = function(position) {
12602		if (position === undefined) {
12603			return this._items.slice();
12604		}
12605
12606		position = this.normalize(position, true);
12607		return this._items[position];
12608	};
12609
12610	/**
12611	 * Gets an item at the specified relative position.
12612	 * @public
12613	 * @param {Number} [position] - The relative position of the item.
12614	 * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given.
12615	 */
12616	Owl.prototype.mergers = function(position) {
12617		if (position === undefined) {
12618			return this._mergers.slice();
12619		}
12620
12621		position = this.normalize(position, true);
12622		return this._mergers[position];
12623	};
12624
12625	/**
12626	 * Gets the absolute positions of clones for an item.
12627	 * @public
12628	 * @param {Number} [position] - The relative position of the item.
12629	 * @returns {Array.<Number>} - The absolute positions of clones for the item or all if no position was given.
12630	 */
12631	Owl.prototype.clones = function(position) {
12632		var odd = this._clones.length / 2,
12633			even = odd + this._items.length,
12634			map = function(index) { return index % 2 === 0 ? even + index / 2 : odd - (index + 1) / 2 };
12635
12636		if (position === undefined) {
12637			return $.map(this._clones, function(v, i) { return map(i) });
12638		}
12639
12640		return $.map(this._clones, function(v, i) { return v === position ? map(i) : null });
12641	};
12642
12643	/**
12644	 * Sets the current animation speed.
12645	 * @public
12646	 * @param {Number} [speed] - The animation speed in milliseconds or nothing to leave it unchanged.
12647	 * @returns {Number} - The current animation speed in milliseconds.
12648	 */
12649	Owl.prototype.speed = function(speed) {
12650		if (speed !== undefined) {
12651			this._speed = speed;
12652		}
12653
12654		return this._speed;
12655	};
12656
12657	/**
12658	 * Gets the coordinate of an item.
12659	 * @todo The name of this method is missleanding.
12660	 * @public
12661	 * @param {Number} position - The absolute position of the item within `minimum()` and `maximum()`.
12662	 * @returns {Number|Array.<Number>} - The coordinate of the item in pixel or all coordinates.
12663	 */
12664	Owl.prototype.coordinates = function(position) {
12665		var coordinate = null;
12666
12667		if (position === undefined) {
12668			return $.map(this._coordinates, $.proxy(function(coordinate, index) {
12669				return this.coordinates(index);
12670			}, this));
12671		}
12672
12673		if (this.settings.center) {
12674			coordinate = this._coordinates[position];
12675			coordinate += (this.width() - coordinate + (this._coordinates[position - 1] || 0)) / 2 * (this.settings.rtl ? -1 : 1);
12676		} else {
12677			coordinate = this._coordinates[position - 1] || 0;
12678		}
12679
12680		return coordinate;
12681	};
12682
12683	/**
12684	 * Calculates the speed for a translation.
12685	 * @protected
12686	 * @param {Number} from - The absolute position of the start item.
12687	 * @param {Number} to - The absolute position of the target item.
12688	 * @param {Number} [factor=undefined] - The time factor in milliseconds.
12689	 * @returns {Number} - The time in milliseconds for the translation.
12690	 */
12691	Owl.prototype.duration = function(from, to, factor) {
12692		return Math.min(Math.max(Math.abs(to - from), 1), 6) * Math.abs((factor || this.settings.smartSpeed));
12693	};
12694
12695	/**
12696	 * Slides to the specified item.
12697	 * @public
12698	 * @param {Number} position - The position of the item.
12699	 * @param {Number} [speed] - The time in milliseconds for the transition.
12700	 */
12701	Owl.prototype.to = function(position, speed) {
12702		var current = this.current(),
12703			revert = null,
12704			distance = position - this.relative(current),
12705			direction = (distance > 0) - (distance < 0),
12706			items = this._items.length,
12707			minimum = this.minimum(),
12708			maximum = this.maximum();
12709
12710		if (this.settings.loop) {
12711			if (!this.settings.rewind && Math.abs(distance) > items / 2) {
12712				distance += direction * -1 * items;
12713			}
12714
12715			position = current + distance;
12716			revert = ((position - minimum) % items + items) % items + minimum;
12717
12718			if (revert !== position && revert - distance <= maximum && revert - distance > 0) {
12719				current = revert - distance;
12720				position = revert;
12721				this.reset(current);
12722			}
12723		} else if (this.settings.rewind) {
12724			maximum += 1;
12725			position = (position % maximum + maximum) % maximum;
12726		} else {
12727			position = Math.max(minimum, Math.min(maximum, position));
12728		}
12729
12730		this.speed(this.duration(current, position, speed));
12731		this.current(position);
12732
12733		if (this.$element.is(':visible')) {
12734			this.update();
12735		}
12736	};
12737
12738	/**
12739	 * Slides to the next item.
12740	 * @public
12741	 * @param {Number}
vendor: 30,399 bytes, lines 12741-13889
12741 [speed] - The time in milliseconds for the transition.
12742	 */
12743	Owl.prototype.next = function(speed) {
12744		speed = speed || false;
12745		this.to(this.relative(this.current()) + 1, speed);
12746	};
12747
12748	/**
12749	 * Slides to the previous item.
12750	 * @public
12751	 * @param {Number} [speed] - The time in milliseconds for the transition.
12752	 */
12753	Owl.prototype.prev = function(speed) {
12754		speed = speed || false;
12755		this.to(this.relative(this.current()) - 1, speed);
12756	};
12757
12758	/**
12759	 * Handles the end of an animation.
12760	 * @protected
12761	 * @param {Event} event - The event arguments.
12762	 */
12763	Owl.prototype.onTransitionEnd = function(event) {
12764
12765		// if css2 animation then event object is undefined
12766		if (event !== undefined) {
12767			event.stopPropagation();
12768
12769			// Catch only owl-stage transitionEnd event
12770			if ((event.target || event.srcElement || event.originalTarget) !== this.$stage.get(0)) {
12771				return false;
12772			}
12773		}
12774
12775		this.leave('animating');
12776		this.trigger('translated');
12777	};
12778
12779	/**
12780	 * Gets viewport width.
12781	 * @protected
12782	 * @return {Number} - The width in pixel.
12783	 */
12784	Owl.prototype.viewport = function() {
12785		var width;
12786		if (this.options.responsiveBaseElement !== window) {
12787			width = $(this.options.responsiveBaseElement).width();
12788		} else if (window.innerWidth) {
12789			width = window.innerWidth;
12790		} else if (document.documentElement && document.documentElement.clientWidth) {
12791			width = document.documentElement.clientWidth;
12792		} else {
12793			throw 'Can not detect viewport width.';
12794		}
12795
12796		return width;
12797	};
12798
12799	/**
12800	 * Replaces the current content.
12801	 * @public
12802	 * @param {HTMLElement|jQuery|String} content - The new content.
12803	 */
12804	Owl.prototype.replace = function(content) {
12805		this.$stage.empty();
12806		this._items = [];
12807
12808		if (content) {
12809			content = (content instanceof jQuery) ? content : $(content);
12810		}
12811
12812		if (this.settings.nestedItemSelector) {
12813			content = content.find('.' + this.settings.nestedItemSelector);
12814		}
12815
12816		content.filter(function() {
12817			return this.nodeType === 1;
12818		}).each($.proxy(function(index, item) {
12819			item = this.prepare(item, index);
12820			this.$stage.append(item);
12821			this._items.push(item);
12822			this._mergers.push(item.find('[data-merge]').addBack('[data-merge]').attr('data-merge') * 1 || 1);
12823		}, this));
12824
12825		this.reset($.isNumeric(this.settings.startPosition) ? this.settings.startPosition : 0);
12826
12827		this.invalidate('items');
12828	};
12829
12830	/**
12831	 * Adds an item.
12832	 * @todo Use `item` instead of `content` for the event arguments.
12833	 * @public
12834	 * @param {HTMLElement|jQuery|String} content - The item content to add.
12835	 * @param {Number} [position] - The relative position at which to insert the item otherwise the item will be added to the end.
12836	 */
12837	Owl.prototype.add = function(content, position) {
12838		var current = this.relative(this._current);
12839
12840		position = position === undefined ? this._items.length : this.normalize(position, true);
12841		content = content instanceof jQuery ? content : $(content);
12842
12843		this.trigger('add', { content: content, position: position });
12844
12845		content = this.prepare(content, this._items[current].index());
12846
12847		if (this._items.length === 0 || position === this._items.length) {
12848			this._items.length === 0 && this.$stage.append(content);
12849			this._items.length !== 0 && this._items[position - 1].after(content);
12850			this._items.push(content);
12851			this._mergers.push(content.find('[data-merge]').andSelf('[data-merge]').attr('data-merge') * 1 || 1);
12852		} else {
12853			this._items[position].before(content);
12854			this._items.splice(position, 0, content);
12855			this._mergers.splice(position, 0, content.find('[data-merge]').andSelf('[data-merge]').attr('data-merge') * 1 || 1);
12856		}
12857
12858		this._items[current] && this.reset(this._items[current].index());
12859
12860		this.invalidate('items');
12861
12862		this.trigger('added', { content: content, position: position });
12863	};
12864
12865	/**
12866	 * Removes an item by its position.
12867	 * @todo Use `item` instead of `content` for the event arguments.
12868	 * @public
12869	 * @param {Number} position - The relative position of the item to remove.
12870	 */
12871	Owl.prototype.remove = function(position) {
12872		position = this.normalize(position, true);
12873
12874		if (position === undefined) {
12875			return;
12876		}
12877
12878		this.trigger('remove', { content: this._items[position], position: position });
12879
12880		this._items[position].remove();
12881		this._items.splice(position, 1);
12882		this._mergers.splice(position, 1);
12883
12884		this.invalidate('items');
12885
12886		this.trigger('removed', { content: null, position: position });
12887	};
12888
12889	/**
12890	 * Preloads images with auto width.
12891	 * @todo Replace by a more generic approach
12892	 * @protected
12893	 */
12894	Owl.prototype.preloadAutoWidthImages = function(images) {
12895		images.each($.proxy(function(i, element) {
12896			this.enter('pre-loading');
12897			element = $(element);
12898			$(new Image()).one('load', $.proxy(function(e) {
12899				element.attr('src', e.target.src);
12900				element.css('opacity', 1);
12901				this.leave('pre-loading');
12902				!this.is('pre-loading') && !this.is('initializing') && this.refresh();
12903			}, this)).attr('src', element.attr('src') || element.attr('data-src') || element.attr('data-src-retina'));
12904		}, this));
12905	};
12906
12907	/**
12908	 * Destroys the carousel.
12909	 * @public
12910	 */
12911	Owl.prototype.destroy = function() {
12912
12913		this.$element.off('.owl.core');
12914		this.$stage.off('.owl.core');
12915		$(document).off('.owl.core');
12916
12917		if (this.settings.responsive !== false) {
12918			window.clearTimeout(this.resizeTimer);
12919			this.off(window, 'resize', this._handlers.onThrottledResize);
12920		}
12921
12922		for (var i in this._plugins) {
12923			this._plugins[i].destroy();
12924		}
12925
12926		this.$stage.children('.cloned').remove();
12927
12928		this.$stage.unwrap();
12929		this.$stage.children().contents().unwrap();
12930		this.$stage.children().unwrap();
12931
12932		this.$element
12933			.removeClass(this.options.refreshClass)
12934			.removeClass(this.options.loadingClass)
12935			.removeClass(this.options.loadedClass)
12936			.removeClass(this.options.rtlClass)
12937			.removeClass(this.options.dragClass)
12938			.removeClass(this.options.grabClass)
12939			.attr('class', this.$element.attr('class').replace(new RegExp(this.options.responsiveClass + '-\\S+\\s', 'g'), ''))
12940			.removeData('owl.carousel');
12941	};
12942
12943	/**
12944	 * Operators to calculate right-to-left and left-to-right.
12945	 * @protected
12946	 * @param {Number} [a] - The left side operand.
12947	 * @param {String} [o] - The operator.
12948	 * @param {Number} [b] - The right side operand.
12949	 */
12950	Owl.prototype.op = function(a, o, b) {
12951		var rtl = this.settings.rtl;
12952		switch (o) {
12953			case '<':
12954				return rtl ? a > b : a < b;
12955			case '>':
12956				return rtl ? a < b : a > b;
12957			case '>=':
12958				return rtl ? a <= b : a >= b;
12959			case '<=':
12960				return rtl ? a >= b : a <= b;
12961			default:
12962				break;
12963		}
12964	};
12965
12966	/**
12967	 * Attaches to an internal event.
12968	 * @protected
12969	 * @param {HTMLElement} element - The event source.
12970	 * @param {String} event - The event name.
12971	 * @param {Function} listener - The event handler to attach.
12972	 * @param {Boolean} capture - Wether the event should be handled at the capturing phase or not.
12973	 */
12974	Owl.prototype.on = function(element, event, listener, capture) {
12975		if (element.addEventListener) {
12976			element.addEventListener(event, listener, capture);
12977		} else if (element.attachEvent) {
12978			element.attachEvent('on' + event, listener);
12979		}
12980	};
12981
12982	/**
12983	 * Detaches from an internal event.
12984	 * @protected
12985	 * @param {HTMLElement} element - The event source.
12986	 * @param {String} event - The event name.
12987	 * @param {Function} listener - The attached event handler to detach.
12988	 * @param {Boolean} capture - Wether the attached event handler was registered as a capturing listener or not.
12989	 */
12990	Owl.prototype.off = function(element, event, listener, capture) {
12991		if (element.removeEventListener) {
12992			element.removeEventListener(event, listener, capture);
12993		} else if (element.detachEvent) {
12994			element.detachEvent('on' + event, listener);
12995		}
12996	};
12997
12998	/**
12999	 * Triggers a public event.
13000	 * @todo Remove `status`, `relatedTarget` should be used instead.
13001	 * @protected
13002	 * @param {String} name - The event name.
13003	 * @param {*} [data=null] - The event data.
13004	 * @param {String} [namespace=carousel] - The event namespace.
13005	 * @param {String} [state] - The state which is associated with the event.
13006	 * @param {Boolean} [enter=false] - Indicates if the call enters the specified state or not.
13007	 * @returns {Event} - The event arguments.
13008	 */
13009	Owl.prototype.trigger = function(name, data, namespace, state, enter) {
13010		var status = {
13011			item: { count: this._items.length, index: this.current() }
13012		}, handler = $.camelCase(
13013			$.grep([ 'on', name, namespace ], function(v) { return v })
13014				.join('-').toLowerCase()
13015		), event = $.Event(
13016			[ name, 'owl', namespace || 'carousel' ].join('.').toLowerCase(),
13017			$.extend({ relatedTarget: this }, status, data)
13018		);
13019
13020		if (!this._supress[name]) {
13021			$.each(this._plugins, function(name, plugin) {
13022				if (plugin.onTrigger) {
13023					plugin.onTrigger(event);
13024				}
13025			});
13026
13027			this.register({ type: Owl.Type.Event, name: name });
13028			this.$element.trigger(event);
13029
13030			if (this.settings && typeof this.settings[handler] === 'function') {
13031				this.settings[handler].call(this, event);
13032			}
13033		}
13034
13035		return event;
13036	};
13037
13038	/**
13039	 * Enters a state.
13040	 * @param name - The state name.
13041	 */
13042	Owl.prototype.enter = function(name) {
13043		$.each([ name ].concat(this._states.tags[name] || []), $.proxy(function(i, name) {
13044			if (this._states.current[name] === undefined) {
13045				this._states.current[name] = 0;
13046			}
13047
13048			this._states.current[name]++;
13049		}, this));
13050	};
13051
13052	/**
13053	 * Leaves a state.
13054	 * @param name - The state name.
13055	 */
13056	Owl.prototype.leave = function(name) {
13057		$.each([ name ].concat(this._states.tags[name] || []), $.proxy(function(i, name) {
13058			this._states.current[name]--;
13059		}, this));
13060	};
13061
13062	/**
13063	 * Registers an event or state.
13064	 * @public
13065	 * @param {Object} object - The event or state to register.
13066	 */
13067	Owl.prototype.register = function(object) {
13068		if (object.type === Owl.Type.Event) {
13069			if (!$.event.special[object.name]) {
13070				$.event.special[object.name] = {};
13071			}
13072
13073			if (!$.event.special[object.name].owl) {
13074				var _default = $.event.special[object.name]._default;
13075				$.event.special[object.name]._default = function(e) {
13076					if (_default && _default.apply && (!e.namespace || e.namespace.indexOf('owl') === -1)) {
13077						return _default.apply(this, arguments);
13078					}
13079					return e.namespace && e.namespace.indexOf('owl') > -1;
13080				};
13081				$.event.special[object.name].owl = true;
13082			}
13083		} else if (object.type === Owl.Type.State) {
13084			if (!this._states.tags[object.name]) {
13085				this._states.tags[object.name] = object.tags;
13086			} else {
13087				this._states.tags[object.name] = this._states.tags[object.name].concat(object.tags);
13088			}
13089
13090			this._states.tags[object.name] = $.grep(this._states.tags[object.name], $.proxy(function(tag, i) {
13091				return $.inArray(tag, this._states.tags[object.name]) === i;
13092			}, this));
13093		}
13094	};
13095
13096	/**
13097	 * Suppresses events.
13098	 * @protected
13099	 * @param {Array.<String>} events - The events to suppress.
13100	 */
13101	Owl.prototype.suppress = function(events) {
13102		$.each(events, $.proxy(function(index, event) {
13103			this._supress[event] = true;
13104		}, this));
13105	};
13106
13107	/**
13108	 * Releases suppressed events.
13109	 * @protected
13110	 * @param {Array.<String>} events - The events to release.
13111	 */
13112	Owl.prototype.release = function(events) {
13113		$.each(events, $.proxy(function(index, event) {
13114			delete this._supress[event];
13115		}, this));
13116	};
13117
13118	/**
13119	 * Gets unified pointer coordinates from event.
13120	 * @todo #261
13121	 * @protected
13122	 * @param {Event} - The `mousedown` or `touchstart` event.
13123	 * @returns {Object} - Contains `x` and `y` coordinates of current pointer position.
13124	 */
13125	Owl.prototype.pointer = function(event) {
13126		var result = { x: null, y: null };
13127
13128		event = event.originalEvent || event || window.event;
13129
13130		event = event.touches && event.touches.length ?
13131			event.touches[0] : event.changedTouches && event.changedTouches.length ?
13132				event.changedTouches[0] : event;
13133
13134		if (event.pageX) {
13135			result.x = event.pageX;
13136			result.y = event.pageY;
13137		} else {
13138			result.x = event.clientX;
13139			result.y = event.clientY;
13140		}
13141
13142		return result;
13143	};
13144
13145	/**
13146	 * Gets the difference of two vectors.
13147	 * @todo #261
13148	 * @protected
13149	 * @param {Object} - The first vector.
13150	 * @param {Object} - The second vector.
13151	 * @returns {Object} - The difference.
13152	 */
13153	Owl.prototype.difference = function(first, second) {
13154		return {
13155			x: first.x - second.x,
13156			y: first.y - second.y
13157		};
13158	};
13159
13160	/**
13161	 * The jQuery Plugin for the Owl Carousel
13162	 * @todo Navigation plugin `next` and `prev`
13163	 * @public
13164	 */
13165	$.fn.owlCarousel = function(option) {
13166		var args = Array.prototype.slice.call(arguments, 1);
13167
13168		return this.each(function() {
13169			var $this = $(this),
13170				data = $this.data('owl.carousel');
13171
13172			if (!data) {
13173				data = new Owl(this, typeof option == 'object' && option);
13174				$this.data('owl.carousel', data);
13175
13176				$.each([
13177					'next', 'prev', 'to', 'destroy', 'refresh', 'replace', 'add', 'remove'
13178				], function(i, event) {
13179					data.register({ type: Owl.Type.Event, name: event });
13180					data.$element.on(event + '.owl.carousel.core', $.proxy(function(e) {
13181						if (e.namespace && e.relatedTarget !== this) {
13182							this.suppress([ event ]);
13183							data[event].apply(this, [].slice.call(arguments, 1));
13184							this.release([ event ]);
13185						}
13186					}, data));
13187				});
13188			}
13189
13190			if (typeof option == 'string' && option.charAt(0) !== '_') {
13191				data[option].apply(data, args);
13192			}
13193		});
13194	};
13195
13196	/**
13197	 * The constructor for the jQuery Plugin
13198	 * @public
13199	 */
13200	$.fn.owlCarousel.Constructor = Owl;
13201
13202})(window.Zepto || window.jQuery, window, document);
13203
13204/**
13205 * AutoRefresh Plugin
13206 * @version 2.0.0-beta.3
13207 * @author Artus Kolanowski
13208 * @license The MIT License (MIT)
13209 */
13210;(function($, window, document, undefined) {
13211
13212	/**
13213	 * Creates the auto refresh plugin.
13214	 * @class The Auto Refresh Plugin
13215	 * @param {Owl} carousel - The Owl Carousel
13216	 */
13217	var AutoRefresh = function(carousel) {
13218		/**
13219		 * Reference to the core.
13220		 * @protected
13221		 * @type {Owl}
13222		 */
13223		this._core = carousel;
13224
13225		/**
13226		 * Refresh interval.
13227		 * @protected
13228		 * @type {number}
13229		 */
13230		this._interval = null;
13231
13232		/**
13233		 * Whether the element is currently visible or not.
13234		 * @protected
13235		 * @type {Boolean}
13236		 */
13237		this._visible = null;
13238
13239		/**
13240		 * All event handlers.
13241		 * @protected
13242		 * @type {Object}
13243		 */
13244		this._handlers = {
13245			'initialized.owl.carousel': $.proxy(function(e) {
13246				if (e.namespace && this._core.settings.autoRefresh) {
13247					this.watch();
13248				}
13249			}, this)
13250		};
13251
13252		// set default options
13253		this._core.options = $.extend({}, AutoRefresh.Defaults, this._core.options);
13254
13255		// register event handlers
13256		this._core.$element.on(this._handlers);
13257	};
13258
13259	/**
13260	 * Default options.
13261	 * @public
13262	 */
13263	AutoRefresh.Defaults = {
13264		autoRefresh: true,
13265		autoRefreshInterval: 500
13266	};
13267
13268	/**
13269	 * Watches the element.
13270	 */
13271	AutoRefresh.prototype.watch = function() {
13272		if (this._interval) {
13273			return;
13274		}
13275
13276		this._visible = this._core.$element.is(':visible');
13277		this._interval = window.setInterval($.proxy(this.refresh, this), this._core.settings.autoRefreshInterval);
13278	};
13279
13280	/**
13281	 * Refreshes the element.
13282	 */
13283	AutoRefresh.prototype.refresh = function() {
13284		if (this._core.$element.is(':visible') === this._visible) {
13285			return;
13286		}
13287
13288		this._visible = !this._visible;
13289
13290		this._core.$element.toggleClass('owl-hidden', !this._visible);
13291
13292		this._visible && (this._core.invalidate('width') && this._core.refresh());
13293	};
13294
13295	/**
13296	 * Destroys the plugin.
13297	 */
13298	AutoRefresh.prototype.destroy = function() {
13299		var handler, property;
13300
13301		window.clearInterval(this._interval);
13302
13303		for (handler in this._handlers) {
13304			this._core.$element.off(handler, this._handlers[handler]);
13305		}
13306		for (property in Object.getOwnPropertyNames(this)) {
13307			typeof this[property] != 'function' && (this[property] = null);
13308		}
13309	};
13310
13311	$.fn.owlCarousel.Constructor.Plugins.AutoRefresh = AutoRefresh;
13312
13313})(window.Zepto || window.jQuery, window, document);
13314
13315/**
13316 * Lazy Plugin
13317 * @version 2.0.0-beta.3
13318 * @author Bartosz Wojciechowski
13319 * @license The MIT License (MIT)
13320 */
13321;(function($, window, document, undefined) {
13322
13323	/**
13324	 * Creates the lazy plugin.
13325	 * @class The Lazy Plugin
13326	 * @param {Owl} carousel - The Owl Carousel
13327	 */
13328	var Lazy = function(carousel) {
13329
13330		/**
13331		 * Reference to the core.
13332		 * @protected
13333		 * @type {Owl}
13334		 */
13335		this._core = carousel;
13336
13337		/**
13338		 * Already loaded items.
13339		 * @protected
13340		 * @type {Array.<jQuery>}
13341		 */
13342		this._loaded = [];
13343
13344		/**
13345		 * Event handlers.
13346		 * @protected
13347		 * @type {Object}
13348		 */
13349		this._handlers = {
13350			'initialized.owl.carousel change.owl.carousel': $.proxy(function(e) {
13351				if (!e.namespace) {
13352					return;
13353				}
13354
13355				if (!this._core.settings || !this._core.settings.lazyLoad) {
13356					return;
13357				}
13358
13359				if ((e.property && e.property.name == 'position') || e.type == 'initialized') {
13360					var settings = this._core.settings,
13361						n = (settings.center && Math.ceil(settings.items / 2) || settings.items),
13362						i = ((settings.center && n * -1) || 0),
13363						position = ((e.property && e.property.value) || this._core.current()) + i,
13364						clones = this._core.clones().length,
13365						load = $.proxy(function(i, v) { this.load(v) }, this);
13366
13367					while (i++ < n) {
13368						this.load(clones / 2 + this._core.relative(position));
13369						clones && $.each(this._core.clones(this._core.relative(position)), load);
13370						position++;
13371					}
13372				}
13373			}, this)
13374		};
13375
13376		// set the default options
13377		this._core.options = $.extend({}, Lazy.Defaults, this._core.options);
13378
13379		// register event handler
13380		this._core.$element.on(this._handlers);
13381	}
13382
13383	/**
13384	 * Default options.
13385	 * @public
13386	 */
13387	Lazy.Defaults = {
13388		lazyLoad: false
13389	}
13390
13391	/**
13392	 * Loads all resources of an item at the specified position.
13393	 * @param {Number} position - The absolute position of the item.
13394	 * @protected
13395	 */
13396	Lazy.prototype.load = function(position) {
13397		var $item = this._core.$stage.children().eq(position),
13398			$elements = $item && $item.find('.owl-lazy');
13399
13400		if (!$elements || $.inArray($item.get(0), this._loaded) > -1) {
13401			return;
13402		}
13403
13404		$elements.each($.proxy(function(index, element) {
13405			var $element = $(element), image,
13406				url = (window.devicePixelRatio > 1 && $element.attr('data-src-retina')) || $element.attr('data-src');
13407
13408			this._core.trigger('load', { element: $element, url: url }, 'lazy');
13409
13410			if ($element.is('img')) {
13411				$element.one('load.owl.lazy', $.proxy(function() {
13412					$element.css('opacity', 1);
13413					this._core.trigger('loaded', { element: $element, url: url }, 'lazy');
13414				}, this)).attr('src', url);
13415			} else {
13416				image = new Image();
13417				image.onload = $.proxy(function() {
13418					$element.css({
13419						'background-image': 'url(' + url + ')',
13420						'opacity': '1'
13421					});
13422					this._core.trigger('loaded', { element: $element, url: url }, 'lazy');
13423				}, this);
13424				image.src = url;
13425			}
13426		}, this));
13427
13428		this._loaded.push($item.get(0));
13429	}
13430
13431	/**
13432	 * Destroys the plugin.
13433	 * @public
13434	 */
13435	Lazy.prototype.destroy = function() {
13436		var handler, property;
13437
13438		for (handler in this.handlers) {
13439			this._core.$element.off(handler, this.handlers[handler]);
13440		}
13441		for (property in Object.getOwnPropertyNames(this)) {
13442			typeof this[property] != 'function' && (this[property] = null);
13443		}
13444	};
13445
13446	$.fn.owlCarousel.Constructor.Plugins.Lazy = Lazy;
13447
13448})(window.Zepto || window.jQuery, window, document);
13449
13450/**
13451 * AutoHeight Plugin
13452 * @version 2.0.0-beta.3
13453 * @author Bartosz Wojciechowski
13454 * @license The MIT License (MIT)
13455 */
13456;(function($, window, document, undefined) {
13457
13458	/**
13459	 * Creates the auto height plugin.
13460	 * @class The Auto Height Plugin
13461	 * @param {Owl} carousel - The Owl Carousel
13462	 */
13463	var AutoHeight = function(carousel) {
13464		/**
13465		 * Reference to the core.
13466		 * @protected
13467		 * @type {Owl}
13468		 */
13469		this._core = carousel;
13470
13471		/**
13472		 * All event handlers.
13473		 * @protected
13474		 * @type {Object}
13475		 */
13476		this._handlers = {
13477			'initialized.owl.carousel refreshed.owl.carousel': $.proxy(function(e) {
13478				if (e.namespace && this._core.settings.autoHeight) {
13479					this.update();
13480				}
13481			}, this),
13482			'changed.owl.carousel': $.proxy(function(e) {
13483				if (e.namespace && this._core.settings.autoHeight && e.property.name == 'position'){
13484					this.update();
13485				}
13486			}, this),
13487			'loaded.owl.lazy': $.proxy(function(e) {
13488				if (e.namespace && this._core.settings.autoHeight
13489					&& e.element.closest('.' + this._core.settings.itemClass).index() === this._core.current()) {
13490					this.update();
13491				}
13492			}, this)
13493		};
13494
13495		// set default options
13496		this._core.options = $.extend({}, AutoHeight.Defaults, this._core.options);
13497
13498		// register event handlers
13499		this._core.$element.on(this._handlers);
13500	};
13501
13502	/**
13503	 * Default options.
13504	 * @public
13505	 */
13506	AutoHeight.Defaults = {
13507		autoHeight: false,
13508		autoHeightClass: 'owl-height'
13509	};
13510
13511	/**
13512	 * Updates the view.
13513	 */
13514	AutoHeight.prototype.update = function() {
13515		var start = this._core._current,
13516			end = start + this._core.settings.items,
13517			visible = this._core.$stage.children().toArray().slice(start, end);
13518			heights = [],
13519			maxheight = 0;
13520
13521		$.each(visible, function(index, item) {
13522			heights.push($(item).height());
13523		});
13524
13525		maxheight = Math.max.apply(null, heights);
13526
13527		this._core.$stage.parent()
13528			.height(maxheight)
13529			.addClass(this._core.settings.autoHeightClass);
13530	};
13531
13532	AutoHeight.prototype.destroy = function() {
13533		var handler, property;
13534
13535		for (handler in this._handlers) {
13536			this._core.$element.off(handler, this._handlers[handler]);
13537		}
13538		for (property in Object.getOwnPropertyNames(this)) {
13539			typeof this[property] != 'function' && (this[property] = null);
13540		}
13541	};
13542
13543	$.fn.owlCarousel.Constructor.Plugins.AutoHeight = AutoHeight;
13544
13545})(window.Zepto || window.jQuery, window, document);
13546
13547/**
13548 * Video Plugin
13549 * @version 2.0.0-beta.3
13550 * @author Bartosz Wojciechowski
13551 * @license The MIT License (MIT)
13552 */
13553;(function($, window, document, undefined) {
13554
13555	/**
13556	 * Creates the video plugin.
13557	 * @class The Video Plugin
13558	 * @param {Owl} carousel - The Owl Carousel
13559	 */
13560	var Video = function(carousel) {
13561		/**
13562		 * Reference to the core.
13563		 * @protected
13564		 * @type {Owl}
13565		 */
13566		this._core = carousel;
13567
13568		/**
13569		 * Cache all video URLs.
13570		 * @protected
13571		 * @type {Object}
13572		 */
13573		this._videos = {};
13574
13575		/**
13576		 * Current playing item.
13577		 * @protected
13578		 * @type {jQuery}
13579		 */
13580		this._playing = null;
13581
13582		/**
13583		 * All event handlers.
13584		 * @todo The cloned content removale is too late
13585		 * @protected
13586		 * @type {Object}
13587		 */
13588		this._handlers = {
13589			'initialized.owl.carousel': $.proxy(function(e) {
13590				if (e.namespace) {
13591					this._core.register({ type: 'state', name: 'playing', tags: [ 'interacting' ] });
13592				}
13593			}, this),
13594			'resize.owl.carousel': $.proxy(function(e) {
13595				if (e.namespace && this._core.settings.video && this.isInFullScreen()) {
13596					e.preventDefault();
13597				}
13598			}, this),
13599			'refreshed.owl.carousel': $.proxy(function(e) {
13600				if (e.namespace && this._core.is('resizing')) {
13601					this._core.$stage.find('.cloned .owl-video-frame').remove();
13602				}
13603			}, this),
13604			'changed.owl.carousel': $.proxy(function(e) {
13605				if (e.namespace && e.property.name === 'position' && this._playing) {
13606					this.stop();
13607				}
13608			}, this),
13609			'prepared.owl.carousel': $.proxy(function(e) {
13610				if (!e.namespace) {
13611					return;
13612				}
13613
13614				var $element = $(e.content).find('.owl-video');
13615
13616				if ($element.length) {
13617					$element.css('display', 'none');
13618					this.fetch($element, $(e.content));
13619				}
13620			}, this)
13621		};
13622
13623		// set default options
13624		this._core.options = $.extend({}, Video.Defaults, this._core.options);
13625
13626		// register event handlers
13627		this._core.$element.on(this._handlers);
13628
13629		this._core.$element.on('click.owl.video', '.owl-video-play-icon', $.proxy(function(e) {
13630			this.play(e);
13631		}, this));
13632	};
13633
13634	/**
13635	 * Default options.
13636	 * @public
13637	 */
13638	Video.Defaults = {
13639		video: false,
13640		videoHeight: false,
13641		videoWidth: false
13642	};
13643
13644	/**
13645	 * Gets the video ID and the type (YouTube/Vimeo only).
13646	 * @protected
13647	 * @param {jQuery} target - The target containing the video data.
13648	 * @param {jQuery} item - The item containing the video.
13649	 */
13650	Video.prototype.fetch = function(target, item) {
13651		var type = target.attr('data-vimeo-id') ? 'vimeo' : 'youtube',
13652			id = target.attr('data-vimeo-id') || target.attr('data-youtube-id'),
13653			width = target.attr('data-width') || this._core.settings.videoWidth,
13654			height = target.attr('data-height') || this._core.settings.videoHeight,
13655			url = target.attr('href');
13656
13657		if (url) {
13658			id = url.match(/(http:|https:|)\/\/(player.|www.)?(vimeo\.com|youtu(be\.com|\.be|be\.googleapis\.com))\/(video\/|embed\/|watch\?v=|v\/)?([A-Za-z0-9._%-]*)(\&\S+)?/);
13659
13660			if (id[3].indexOf('youtu') > -1) {
13661				type = 'youtube';
13662			} else if (id[3].indexOf('vimeo') > -1) {
13663				type = 'vimeo';
13664			} else {
13665				throw new Error('Video URL not supported.');
13666			}
13667			id = id[6];
13668		} else {
13669			throw new Error('Missing video URL.');
13670		}
13671
13672		this._videos[url] = {
13673			type: type,
13674			id: id,
13675			width: width,
13676			height: height
13677		};
13678
13679		item.attr('data-video', url);
13680
13681		this.thumbnail(target, this._videos[url]);
13682	};
13683
13684	/**
13685	 * Creates video thumbnail.
13686	 * @protected
13687	 * @param {jQuery} target - The target containing the video data.
13688	 * @param {Object} info - The video info object.
13689	 * @see `fetch`
13690	 */
13691	Video.prototype.thumbnail = function(target, video) {
13692		var tnLink,
13693			icon,
13694			path,
13695			dimensions = video.width && video.height ? 'style="width:' + video.width + 'px;height:' + video.height + 'px;"' : '',
13696			customTn = target.find('img'),
13697			srcType = 'src',
13698			lazyClass = '',
13699			settings = this._core.settings,
13700			create = function(path) {
13701				icon = '<div class="owl-video-play-icon"></div>';
13702
13703				if (settings.lazyLoad) {
13704					tnLink = '<div class="owl-video-tn ' + lazyClass + '" ' + srcType + '="' + path + '"></div>';
13705				} else {
13706					tnLink = '<div class="owl-video-tn" style="opacity:1;background-image:url(' + path + ')"></div>';
13707				}
13708				target.after(tnLink);
13709				target.after(icon);
13710			};
13711
13712		// wrap video content into owl-video-wrapper div
13713		target.wrap('<div class="owl-video-wrapper"' + dimensions + '></div>');
13714
13715		if (this._core.settings.lazyLoad) {
13716			srcType = 'data-src';
13717			lazyClass = 'owl-lazy';
13718		}
13719
13720		// custom thumbnail
13721		if (customTn.length) {
13722			create(customTn.attr(srcType));
13723			customTn.remove();
13724			return false;
13725		}
13726
13727		if (video.type === 'youtube') {
13728			path = "//img.youtube.com/vi/" + video.id + "/hqdefault.jpg";
13729			create(path);
13730		} else if (video.type === 'vimeo') {
13731			$.ajax({
13732				type: 'GET',
13733				url: '//vimeo.com/api/v2/video/' + video.id + '.json',
13734				jsonp: 'callback',
13735				dataType: 'jsonp',
13736				success: function(data) {
13737					path = data[0].thumbnail_large;
13738					create(path);
13739				}
13740			});
13741		}
13742	};
13743
13744	/**
13745	 * Stops the current video.
13746	 * @public
13747	 */
13748	Video.prototype.stop = function() {
13749		this._core.trigger('stop', null, 'video');
13750		this._playing.find('.owl-video-frame').remove();
13751		this._playing.removeClass('owl-video-playing');
13752		this._playing = null;
13753		this._core.leave('playing');
13754		this._core.trigger('stopped', null, 'video');
13755	};
13756
13757	/**
13758	 * Starts the current video.
13759	 * @public
13760	 * @param {Event} event - The event arguments.
13761	 */
13762	Video.prototype.play = function(event) {
13763		var target = $(event.target),
13764			item = target.closest('.' + this._core.settings.itemClass),
13765			video = this._videos[item.attr('data-video')],
13766			width = video.width || '100%',
13767			height = video.height || this._core.$stage.height(),
13768			html;
13769
13770		if (this._playing) {
13771			return;
13772		}
13773
13774		this._core.enter('playing');
13775		this._core.trigger('play', null, 'video');
13776
13777		item = this._core.items(this._core.relative(item.index()));
13778
13779		this._core.reset(item.index());
13780
13781		if (video.type === 'youtube') {
13782			html = '<iframe width="' + width + '" height="' + height + '" src="//www.youtube.com/embed/' +
13783				video.id + '?autoplay=1&v=' + video.id + '" frameborder="0" allowfullscreen></iframe>';
13784		} else if (video.type === 'vimeo') {
13785			html = '<iframe src="//player.vimeo.com/video/' + video.id +
13786				'?autoplay=1" width="' + width + '" height="' + height +
13787				'" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>';
13788		}
13789
13790		$('<div class="owl-video-frame">' + html + '</div>').insertAfter(item.find('.owl-video'));
13791
13792		this._playing = item.addClass('owl-video-playing');
13793	};
13794
13795	/**
13796	 * Checks whether an video is currently in full screen mode or not.
13797	 * @todo Bad style because looks like a readonly method but changes members.
13798	 * @protected
13799	 * @returns {Boolean}
13800	 */
13801	Video.prototype.isInFullScreen = function() {
13802		var element = document.fullscreenElement || document.mozFullScreenElement ||
13803				document.webkitFullscreenElement;
13804
13805		return element && $(element).parent().hasClass('owl-video-frame');
13806	};
13807
13808	/**
13809	 * Destroys the plugin.
13810	 */
13811	Video.prototype.destroy = function() {
13812		var handler, property;
13813
13814		this._core.$element.off('click.owl.video');
13815
13816		for (handler in this._handlers) {
13817			this._core.$element.off(handler, this._handlers[handler]);
13818		}
13819		for (property in Object.getOwnPropertyNames(this)) {
13820			typeof this[property] != 'function' && (this[property] = null);
13821		}
13822	};
13823
13824	$.fn.owlCarousel.Constructor.Plugins.Video = Video;
13825
13826})(window.Zepto || window.jQuery, window, document);
13827
13828/**
13829 * Animate Plugin
13830 * @version 2.0.0-beta.3
13831 * @author Bartosz Wojciechowski
13832 * @license The MIT License (MIT)
13833 */
13834;(function($, window, document, undefined) {
13835
13836	/**
13837	 * Creates the animate plugin.
13838	 * @class The Navigation Plugin
13839	 * @param {Owl} scope - The Owl Carousel
13840	 */
13841	var Animate = function(scope) {
13842		this.core = scope;
13843		this.core.options = $.extend({}, Animate.Defaults, this.core.options);
13844		this.swapping = true;
13845		this.previous = undefined;
13846		this.next = undefined;
13847
13848		this.handlers = {
13849			'change.owl.carousel': $.proxy(function(e) {
13850				if (e.namespace && e.property.name == 'position') {
13851					this.previous = this.core.current();
13852					this.next = e.property.value;
13853				}
13854			}, this),
13855			'drag.owl.carousel dragged.owl.carousel translated.owl.carousel': $.proxy(function(e) {
13856				if (e.namespace) {
13857					this.swapping = e.type == 'translated';
13858				}
13859			}, this),
13860			'translate.owl.carousel': $.proxy(function(e) {
13861				if (e.namespace && this.swapping && (this.core.options.animateOut || this.core.options.animateIn)) {
13862					this.swap();
13863				}
13864			}, this)
13865		};
13866
13867		this.core.$element.on(this.handlers);
13868	};
13869
13870	/**
13871	 * Default options.
13872	 * @public
13873	 */
13874	Animate.Defaults = {
13875		animateOut: false,
13876		animateIn: false
13877	};
13878
13879	/**
13880	 * Toggles the animation classes whenever an translations starts.
13881	 * @protected
13882	 * @returns {Boolean|undefined}
13883	 */
13884	Animate.prototype.swap = function() {
13885
13886		if (this.core.settings.items !== 1) {
13887			return;
13888		}
13889
13890		if (!$.support.animation || !$.support.transition) {
13891			return;
13892		}
13893
13894		this.core.speed(0);
13895
13896		var left,
13897			clear = $.proxy(this.clear, this),
13898			previous = this.core.$stage.children().eq(this.previous),
13899			next = this.core.$stage.children().eq(this.next),
13900			incoming = this.core.settings.animateIn,
13901			outgoing = this.core.settings.animateOut;
13902
13903		if (this.core.current() === this.previous) {
13904			return;
13905		}
13906
13907		if (outgoing) {
13908			left = this.core.coordinates(this.previous) - this.core.coordinates(this.next);
13909			previous.css( { 'left': left + 'px' } )
13910				.addClass('animated owl-animated-out')
13911				.addClass(outgoing)
13912				.on($.support.animation.end, clear);
13913		}
13914
13915		if (incoming) {
13916			next.addClass('animated owl-animated-in')
13917				.addClass(incoming)
13918				.on($.support.animation.end, clear);
13919		}
13920	};
13921
13922	Animate.prototype.clear = function(e) {
13923		if ($(e.target).hasClass('animated')) {
13924			$(e.target).css( { 'left': '' } )
13925				.removeClass('animated owl-animated-out owl-animated-in')
13926				.removeClass(this.core.settings.animateIn)
13927				.removeClass(this.core.settings.animateOut);
13928			this.core.onTransitionEnd();
13929		}
13930	};
13931
13932	/**
13933	 * Destroys the plugin.
13934	 * @public
13935	 */
13936	Animate.prototype.destroy = function() {
13937		var handler, property;
13938
13939		for (handler in this.handlers) {
13940			this.core.$element.off(handler, this.handlers[handler]);
13941		}
13942		for (property in Object.getOwnPropertyNames(this)) {
13943			typeof this[property] != 'function' && (this[property] = null);
13944		}
13945	};
13946
13947	$.fn.owlCarousel.Constructor.Plugins.Animate = Animate;
13948
13949})(window.Zepto || window.jQuery, window, document);
13950
13951/**
13952 * Autoplay Plugin
13953 * @version 2.0.0-beta.3
13954 * @author Bartosz Wojciechowski
13955 * @author Artus Kolanowski
13956 * @license The MIT License (MIT)
13957 */
13958;(function($, window, document, undefined) {
13959
13960	/**
13961	 * Creates the autoplay plugin.
13962	 * @class The Autoplay Plugin
13963	 * @param {Owl} scope - The Owl Carousel
13964	 */
13965	var Autoplay = function(carousel) {
13966		/**
13967		 * Reference to the core.
13968		 * @protected
13969		 * @type {Owl}
13970		 */
13971		this._core = carousel;
13972
13973		/**
13974		 * The autoplay interval.
13975		 * @type {Number}
13976		 */
13977		this._interval = null;
13978
13979		/**
13980		 * Indicates whenever the autoplay is paused.
13981		 * @type {Boolean}
13982		 */
13983		this._paused = false;
13984
13985		/**
13986		 * All event handlers.
13987		 * @protected
13988		 * @type {Object}
13989		 */
13990		this._handlers = {
13991			'changed.owl.carousel': $.proxy(function(e) {
13992				if (e.namespace && e.property.name === 'settings') {
13993					if (this._core.settings.autoplay) {
13994						this.play();
13995					} else {
13996						this.stop();
13997					}
13998				}
13999			}, this),
14000			'initialized.owl.carousel': $.proxy(function(e) {
14001				if (e.namespace && this._core.settings.autoplay) {
14002					this.play();
14003				}
14004			}, this),
14005			'play.owl.autoplay': $.proxy(function(e, t, s) {
14006				if (e.namespace) {
14007					this.play(t, s);
14008				}
14009			}, this),
14010			'stop.owl.autoplay': $.proxy(function(e) {
14011				if (e.namespace) {
14012					this.stop();
14013				}
14014			}, this),
14015			'mouseover.owl.autoplay': $.proxy(function() {
14016				if (this._core.settings.autoplayHoverPause && this._core.is('rotating')) {
14017					this.pause();
14018				}
14019			}, this),
14020			'mouseleave.owl.autoplay': $.proxy(function() {
14021				if (this._core.settings.autoplayHoverPause && this._core.is('rotating')) {
14022					this.play();
14023				}
14024			}, this)
14025		};
14026
14027		// register event handlers
14028		this._core.$element.on(this._handlers);
14029
14030		// set default options
14031		this._core.options = $.extend({}, Autoplay.Defaults, this._core.options);
14032	};
14033
14034	/**
14035	 * Default options.
14036	 * @public
14037	 */
14038	Autoplay.Defaults = {
14039		autoplay: false,
14040		autoplayTimeout: 5000,
14041		autoplayHoverPause: false,
14042		autoplaySpeed: false
14043	};
14044
14045	/**
14046	 * Starts the autoplay.
14047	 * @public
14048	 * @param {Number} [timeout] - The interval before the next animation starts.
14049	 * @param {Number} [speed] - The animation speed for the animations.
14050	 */
14051	Autoplay.prototype.play = function(timeout, speed) {
14052		this._paused = false;
14053
14054		if (this._core.is('rotating')) {
14055			return;
14056		}
14057
14058		this._core.enter('rotating');
14059
14060		this._interval = window.setInterval($.proxy(function() {
14061			if (this._paused || this._core.is('busy') || this._core.is('interacting') || document.hidden) {
14062				return;
14063			}
14064			this._core.next(speed || this._core.settings.autoplaySpeed);
14065		}, this), timeout || this._core.settings.autoplayTimeout);
14066	};
14067
14068	/**
14069	 * Stops the autoplay.
14070	 * @public
14071	 */
14072	Autoplay.prototype.stop = function() {
14073		if (!this._core.is('rotating')) {
14074			return;
14075		}
14076
14077		window.clearInterval(this._interval);
14078		this._core.leave('rotating');
14079	};
14080
14081	/**
14082	 * Stops the autoplay.
14083	 * @public
14084	 */
14085	Autoplay.prototype.pause = function() {
14086		if (!this._core.is('rotating')) {
14087			return;
14088		}
14089
14090		this._paused = true;
14091	};
14092
14093	/**
14094	 * Destroys the plugin.
14095	 */
14096	Autoplay.prototype.destroy = function() {
14097		var handler, property;
14098
14099		this.stop();
14100
14101		for (handler in this._handlers) {
14102			this._core.$element.off(handler, this._handlers[handler]);
14103		}
14104		for (property in Object.getOwnPropertyNames(this)) {
14105			typeof this[property] != 'function' && (this[property] = null);
14106		}
14107	};
14108
14109	$.fn.owlCarousel.Constructor.Plugins.autoplay = Autoplay;
14110
14111})(window.Zepto || window.jQuery, window, document);
14112
14113/**
14114 * Navigation Plugin
14115 * @version 2.0.0-beta.3
14116 * @author Artus Kolanowski
14117 * @license The MIT License (MIT)
14118 */
14119;(function($, window, document, undefined) {
14120	'use strict';
14121
14122	/**
14123	 * Creates the navigation plugin.
14124	 * @class The Navigation Plugin
14125	 * @param {Owl} carousel - The Owl Carousel.
14126	 */
14127	var Navigation = function(carousel) {
14128		/**
14129		 * Reference to the core.
14130		 * @protected
14131		 * @type {Owl}
14132		 */
14133		this._core = carousel;
14134
14135		/**
14136		 * Indicates whether the plugin is initialized or not.
14137		 * @protected
14138		 * @type {Boolean}
14139		 */
14140		this._initialized = false;
vendor: 14,104 bytes, lines 14141-14669
14141
14142		/**
14143		 * The current paging indexes.
14144		 * @protected
14145		 * @type {Array}
14146		 */
14147		this._pages = [];
14148
14149		/**
14150		 * All DOM elements of the user interface.
14151		 * @protected
14152		 * @type {Object}
14153		 */
14154		this._controls = {};
14155
14156		/**
14157		 * Markup for an indicator.
14158		 * @protected
14159		 * @type {Array.<String>}
14160		 */
14161		this._templates = [];
14162
14163		/**
14164		 * The carousel element.
14165		 * @type {jQuery}
14166		 */
14167		this.$element = this._core.$element;
14168
14169		/**
14170		 * Overridden methods of the carousel.
14171		 * @protected
14172		 * @type {Object}
14173		 */
14174		this._overrides = {
14175			next: this._core.next,
14176			prev: this._core.prev,
14177			to: this._core.to
14178		};
14179
14180		/**
14181		 * All event handlers.
14182		 * @protected
14183		 * @type {Object}
14184		 */
14185		this._handlers = {
14186			'prepared.owl.carousel': $.proxy(function(e) {
14187				if (e.namespace && this._core.settings.dotsData) {
14188					this._templates.push('<div class="' + this._core.settings.dotClass + '">' +
14189						$(e.content).find('[data-dot]').andSelf('[data-dot]').attr('data-dot') + '</div>');
14190				}
14191			}, this),
14192			'added.owl.carousel': $.proxy(function(e) {
14193				if (e.namespace && this._core.settings.dotsData) {
14194					this._templates.splice(e.position, 0, this._templates.pop());
14195				}
14196			}, this),
14197			'remove.owl.carousel': $.proxy(function(e) {
14198				if (e.namespace && this._core.settings.dotsData) {
14199					this._templates.splice(e.position, 1);
14200				}
14201			}, this),
14202			'changed.owl.carousel': $.proxy(function(e) {
14203				if (e.namespace && e.property.name == 'position') {
14204					this.draw();
14205				}
14206			}, this),
14207			'initialized.owl.carousel': $.proxy(function(e) {
14208				if (e.namespace && !this._initialized) {
14209					this._core.trigger('initialize', null, 'navigation');
14210					this.initialize();
14211					this.update();
14212					this.draw();
14213					this._initialized = true;
14214					this._core.trigger('initialized', null, 'navigation');
14215				}
14216			}, this),
14217			'refreshed.owl.carousel': $.proxy(function(e) {
14218				if (e.namespace && this._initialized) {
14219					this._core.trigger('refresh', null, 'navigation');
14220					this.update();
14221					this.draw();
14222					this._core.trigger('refreshed', null, 'navigation');
14223				}
14224			}, this)
14225		};
14226
14227		// set default options
14228		this._core.options = $.extend({}, Navigation.Defaults, this._core.options);
14229
14230		// register event handlers
14231		this.$element.on(this._handlers);
14232	};
14233
14234	/**
14235	 * Default options.
14236	 * @public
14237	 * @todo Rename `slideBy` to `navBy`
14238	 */
14239	Navigation.Defaults = {
14240		nav: false,
14241		navText: [ 'prev', 'next' ],
14242		navSpeed: false,
14243		navElement: 'div',
14244		navContainer: false,
14245		navContainerClass: 'owl-nav',
14246		navClass: [ 'owl-prev', 'owl-next' ],
14247		slideBy: 1,
14248		dotClass: 'owl-dot',
14249		dotsClass: 'owl-dots',
14250		dots: true,
14251		dotsEach: false,
14252		dotsData: false,
14253		dotsSpeed: false,
14254		dotsContainer: false
14255	};
14256
14257	/**
14258	 * Initializes the layout of the plugin and extends the carousel.
14259	 * @protected
14260	 */
14261	Navigation.prototype.initialize = function() {
14262		var override,
14263			settings = this._core.settings;
14264
14265		// create DOM structure for relative navigation
14266		this._controls.$relative = (settings.navContainer ? $(settings.navContainer)
14267			: $('<div>').addClass(settings.navContainerClass).appendTo(this.$element)).addClass('disabled');
14268
14269		this._controls.$previous = $('<' + settings.navElement + '>')
14270			.addClass(settings.navClass[0])
14271			.html(settings.navText[0])
14272			.prependTo(this._controls.$relative)
14273			.on('click', $.proxy(function(e) {
14274				this.prev(settings.navSpeed);
14275			}, this));
14276		this._controls.$next = $('<' + settings.navElement + '>')
14277			.addClass(settings.navClass[1])
14278			.html(settings.navText[1])
14279			.appendTo(this._controls.$relative)
14280			.on('click', $.proxy(function(e) {
14281				this.next(settings.navSpeed);
14282			}, this));
14283
14284		// create DOM structure for absolute navigation
14285		if (!settings.dotsData) {
14286			this._templates = [ $('<div>')
14287				.addClass(settings.dotClass)
14288				.append($('<span>'))
14289				.prop('outerHTML') ];
14290		}
14291
14292		this._controls.$absolute = (settings.dotsContainer ? $(settings.dotsContainer)
14293			: $('<div>').addClass(settings.dotsClass).appendTo(this.$element)).addClass('disabled');
14294
14295		this._controls.$absolute.on('click', 'div', $.proxy(function(e) {
14296			var index = $(e.target).parent().is(this._controls.$absolute)
14297				? $(e.target).index() : $(e.target).parent().index();
14298
14299			e.preventDefault();
14300
14301			this.to(index, settings.dotsSpeed);
14302		}, this));
14303
14304		// override public methods of the carousel
14305		for (override in this._overrides) {
14306			this._core[override] = $.proxy(this[override], this);
14307		}
14308	};
14309
14310	/**
14311	 * Destroys the plugin.
14312	 * @protected
14313	 */
14314	Navigation.prototype.destroy = function() {
14315		var handler, control, property, override;
14316
14317		for (handler in this._handlers) {
14318			this.$element.off(handler, this._handlers[handler]);
14319		}
14320		for (control in this._controls) {
14321			this._controls[control].remove();
14322		}
14323		for (override in this.overides) {
14324			this._core[override] = this._overrides[override];
14325		}
14326		for (property in Object.getOwnPropertyNames(this)) {
14327			typeof this[property] != 'function' && (this[property] = null);
14328		}
14329	};
14330
14331	/**
14332	 * Updates the internal state.
14333	 * @protected
14334	 */
14335	Navigation.prototype.update = function() {
14336		var i, j, k,
14337			lower = this._core.clones().length / 2,
14338			upper = lower + this._core.items().length,
14339			maximum = this._core.maximum(true),
14340			settings = this._core.settings,
14341			size = settings.center || settings.autoWidth || settings.dotsData
14342				? 1 : settings.dotsEach || settings.items;
14343
14344		if (settings.slideBy !== 'page') {
14345			settings.slideBy = Math.min(settings.slideBy, settings.items);
14346		}
14347
14348		if (settings.dots || settings.slideBy == 'page') {
14349			this._pages = [];
14350
14351			for (i = lower, j = 0, k = 0; i < upper; i++) {
14352				if (j >= size || j === 0) {
14353					this._pages.push({
14354						start: Math.min(maximum, i - lower),
14355						end: i - lower + size - 1
14356					});
14357					if (Math.min(maximum, i - lower) === maximum) {
14358						break;
14359					}
14360					j = 0, ++k;
14361				}
14362				j += this._core.mergers(this._core.relative(i));
14363			}
14364		}
14365	};
14366
14367	/**
14368	 * Draws the user interface.
14369	 * @todo The option `dotsData` wont work.
14370	 * @protected
14371	 */
14372	Navigation.prototype.draw = function() {
14373		var difference,
14374			settings = this._core.settings,
14375			disabled = this._core.items().length <= settings.items,
14376			index = this._core.relative(this._core.current()),
14377			loop = settings.loop || settings.rewind;
14378
14379		this._controls.$relative.toggleClass('disabled', !settings.nav || disabled);
14380
14381		if (settings.nav) {
14382			this._controls.$previous.toggleClass('disabled', !loop && index <= this._core.minimum(true));
14383			this._controls.$next.toggleClass('disabled', !loop && index >= this._core.maximum(true));
14384		}
14385
14386		this._controls.$absolute.toggleClass('disabled', !settings.dots || disabled);
14387
14388		if (settings.dots) {
14389			difference = this._pages.length - this._controls.$absolute.children().length;
14390
14391			if (settings.dotsData && difference !== 0) {
14392				this._controls.$absolute.html(this._templates.join(''));
14393			} else if (difference > 0) {
14394				this._controls.$absolute.append(new Array(difference + 1).join(this._templates[0]));
14395			} else if (difference < 0) {
14396				this._controls.$absolute.children().slice(difference).remove();
14397			}
14398
14399			this._controls.$absolute.find('.active').removeClass('active');
14400			this._controls.$absolute.children().eq($.inArray(this.current(), this._pages)).addClass('active');
14401		}
14402	};
14403
14404	/**
14405	 * Extends event data.
14406	 * @protected
14407	 * @param {Event} event - The event object which gets thrown.
14408	 */
14409	Navigation.prototype.onTrigger = function(event) {
14410		var settings = this._core.settings;
14411
14412		event.page = {
14413			index: $.inArray(this.current(), this._pages),
14414			count: this._pages.length,
14415			size: settings && (settings.center || settings.autoWidth || settings.dotsData
14416				? 1 : settings.dotsEach || settings.items)
14417		};
14418	};
14419
14420	/**
14421	 * Gets the current page position of the carousel.
14422	 * @protected
14423	 * @returns {Number}
14424	 */
14425	Navigation.prototype.current = function() {
14426		var current = this._core.relative(this._core.current());
14427		return $.grep(this._pages, $.proxy(function(page, index) {
14428			return page.start <= current && page.end >= current;
14429		}, this)).pop();
14430	};
14431
14432	/**
14433	 * Gets the current succesor/predecessor position.
14434	 * @protected
14435	 * @returns {Number}
14436	 */
14437	Navigation.prototype.getPosition = function(successor) {
14438		var position, length,
14439			settings = this._core.settings;
14440
14441		if (settings.slideBy == 'page') {
14442			position = $.inArray(this.current(), this._pages);
14443			length = this._pages.length;
14444			successor ? ++position : --position;
14445			position = this._pages[((position % length) + length) % length].start;
14446		} else {
14447			position = this._core.relative(this._core.current());
14448			length = this._core.items().length;
14449			successor ? position += settings.slideBy : position -= settings.slideBy;
14450		}
14451
14452		return position;
14453	};
14454
14455	/**
14456	 * Slides to the next item or page.
14457	 * @public
14458	 * @param {Number} [speed=false] - The time in milliseconds for the transition.
14459	 */
14460	Navigation.prototype.next = function(speed) {
14461		$.proxy(this._overrides.to, this._core)(this.getPosition(true), speed);
14462	};
14463
14464	/**
14465	 * Slides to the previous item or page.
14466	 * @public
14467	 * @param {Number} [speed=false] - The time in milliseconds for the transition.
14468	 */
14469	Navigation.prototype.prev = function(speed) {
14470		$.proxy(this._overrides.to, this._core)(this.getPosition(false), speed);
14471	};
14472
14473	/**
14474	 * Slides to the specified item or page.
14475	 * @public
14476	 * @param {Number} position - The position of the item or page.
14477	 * @param {Number} [speed] - The time in milliseconds for the transition.
14478	 * @param {Boolean} [standard=false] - Whether to use the standard behaviour or not.
14479	 */
14480	Navigation.prototype.to = function(position, speed, standard) {
14481		var length;
14482
14483		if (!standard) {
14484			length = this._pages.length;
14485			$.proxy(this._overrides.to, this._core)(this._pages[((position % length) + length) % length].start, speed);
14486		} else {
14487			$.proxy(this._overrides.to, this._core)(position, speed);
14488		}
14489	};
14490
14491	$.fn.owlCarousel.Constructor.Plugins.Navigation = Navigation;
14492
14493})(window.Zepto || window.jQuery, window, document);
14494
14495/**
14496 * Hash Plugin
14497 * @version 2.0.0-beta.3
14498 * @author Artus Kolanowski
14499 * @license The MIT License (MIT)
14500 */
14501;(function($, window, document, undefined) {
14502	'use strict';
14503
14504	/**
14505	 * Creates the hash plugin.
14506	 * @class The Hash Plugin
14507	 * @param {Owl} carousel - The Owl Carousel
14508	 */
14509	var Hash = function(carousel) {
14510		/**
14511		 * Reference to the core.
14512		 * @protected
14513		 * @type {Owl}
14514		 */
14515		this._core = carousel;
14516
14517		/**
14518		 * Hash index for the items.
14519		 * @protected
14520		 * @type {Object}
14521		 */
14522		this._hashes = {};
14523
14524		/**
14525		 * The carousel element.
14526		 * @type {jQuery}
14527		 */
14528		this.$element = this._core.$element;
14529
14530		/**
14531		 * All event handlers.
14532		 * @protected
14533		 * @type {Object}
14534		 */
14535		this._handlers = {
14536			/*'initialized.owl.carousel': $.proxy(function(e) {
14537				if (e.namespace && this._core.settings.startPosition === 'URLHash') {
14538					$(window).trigger('hashchange.owl.navigation');
14539				}
14540			}, this),*/
14541			'prepared.owl.carousel': $.proxy(function(e) {
14542				if (e.namespace) {
14543					var hash = $(e.content).find('[data-hash]').addBack('[data-hash]').attr('data-hash');
14544
14545					if (!hash) {
14546						return;
14547					}
14548
14549					this._hashes[hash] = e.content;
14550				}
14551			}, this),
14552			'changed.owl.carousel': $.proxy(function(e) {
14553				if (e.namespace && e.property.name === 'position') {
14554					var current = this._core.items(this._core.relative(this._core.current())),
14555						hash = $.map(this._hashes, function(item, hash) {
14556							return item === current ? hash : null;
14557						}).join();
14558
14559					if (!hash || window.location.hash.slice(1) === hash) {
14560						return;
14561					}
14562
14563					window.location.hash = hash;
14564				}
14565			}, this)
14566		};
14567
14568		// set default options
14569		this._core.options = $.extend({}, Hash.Defaults, this._core.options);
14570
14571		// register the event handlers
14572		this.$element.on(this._handlers);
14573
14574		// register event listener for hash navigation
14575		/*$(window).on('hashchange.owl.navigation', $.proxy(function(e) {
14576			var hash = window.location.hash.substring(1),
14577				items = this._core.$stage.children(),
14578				position = this._hashes[hash] && items.index(this._hashes[hash]);
14579
14580			if (position === undefined || position === this._core.current()) {
14581				return;
14582			}
14583
14584			this._core.to(this._core.relative(position), false, true);
14585		}, this));*/
14586	};
14587
14588	/**
14589	 * Default options.
14590	 * @public
14591	 */
14592	Hash.Defaults = {
14593		URLhashListener: false
14594	};
14595
14596	/**
14597	 * Destroys the plugin.
14598	 * @public
14599	 */
14600	Hash.prototype.destroy = function() {
14601		var handler, property;
14602
14603		$(window).off('hashchange.owl.navigation');
14604
14605		for (handler in this._handlers) {
14606			this._core.$element.off(handler, this._handlers[handler]);
14607		}
14608		for (property in Object.getOwnPropertyNames(this)) {
14609			typeof this[property] != 'function' && (this[property] = null);
14610		}
14611	};
14612
14613	$.fn.owlCarousel.Constructor.Plugins.Hash = Hash;
14614
14615})(window.Zepto || window.jQuery, window, document);
14616
14617/**
14618 * Support Plugin
14619 *
14620 * @version 2.0.0-beta.3
14621 * @author Vivid Planet Software GmbH
14622 * @author Artus Kolanowski
14623 * @license The MIT License (MIT)
14624 */
14625;(function($, window, document, undefined) {
14626
14627	var style = $('<support>').get(0).style,
14628		prefixes = 'Webkit Moz O ms'.split(' '),
14629		events = {
14630			transition: {
14631				end: {
14632					WebkitTransition: 'webkitTransitionEnd',
14633					MozTransition: 'transitionend',
14634					OTransition: 'oTransitionEnd',
14635					transition: 'transitionend'
14636				}
14637			},
14638			animation: {
14639				end: {
14640					WebkitAnimation: 'webkitAnimationEnd',
14641					MozAnimation: 'animationend',
14642					OAnimation: 'oAnimationEnd',
14643					animation: 'animationend'
14644				}
14645			}
14646		},
14647		tests = {
14648			csstransforms: function() {
14649				return !!test('transform');
14650			},
14651			csstransforms3d: function() {
14652				return !!test('perspective');
14653			},
14654			csstransitions: function() {
14655				return !!test('transition');
14656			},
14657			cssanimations: function() {
14658				return !!test('animation');
14659			}
14660		};
14661
14662	function test(property, prefixed) {
14663		var result = false,
14664			upper = property.charAt(0).toUpperCase() + property.slice(1);
14665
14666		$.each((property + ' ' + prefixes.join(upper + ' ') + upper).split(' '), function(i, property) {
14667			if (style[property] !== undefined) {
14668				result = prefixed ? property : true;
14669				return false;
14670			}
14671		});
14672
14673		return result;
14674	}
14675
14676	function prefixed(property) {
14677		return test(property, true);
14678	}
14679
14680	if (tests.csstransitions()) {
14681		/* jshint -W053 */
14682		$.support.transition = new String(prefixed('transition'))
14683		$.support.transition.end = events.transition.end[ $.support.transition ];
14684	}
14685
14686	if (tests.cssanimations()) {
14687		/* jshint -W053 */
14688		$.support.animation = new String(prefixed('animation'))
14689		$.support.animation.end = events.animation.end[ $.support.animation ];
14690	}
14691
14692	if (tests.csstransforms()) {
14693		/* jshint -W053 */
14694		$.support.transform = new String(prefixed('transform'));
14695		$.support.transform3d = tests.csstransforms3d();
14696	}
14697
14698})(window.Zepto || window.jQuery, window, document);
14699
14700(function(window, document, undefined) {
14701
14702	/**
14703	 * Find the absolute position of an element
14704	 */
14705	var absPos = function(element) {
14706		var offsetLeft, offsetTop;
14707		offsetLeft = offsetTop = 0;
14708		if (element.offsetParent) {
14709			do {
14710				offsetLeft += element.offsetLeft;
14711				offsetTop += element.offsetTop;
14712			} while (element = element.offsetParent);
14713		}
14714		return [offsetLeft, offsetTop];
14715	};
14716
14717	/**
14718	 * @constructor Progress Circle class
14719	 * @param params.canvas Canvas on which the circles will be drawn.
14720	 * @param params.minRadius Inner radius of the innermost circle, in px.
14721	 * @param params.arcWidth Width of each circle(to be more accurate, ring).
14722	 * @param params.gapWidth Space between each circle.
14723	 * @param params.centerX X coordinate of the center of circles.
14724	 * @param params.centerY Y coordinate of the center of circles.
14725	 * @param params.infoLineBaseAngle Base angle of the info line.
14726	 * @param params.infoLineAngleInterval Angles between the info lines.
14727	 */
14728	var ProgressCircle = function(params) {
14729		this.canvas = params.canvas;
14730		this.minRadius = params.minRadius || 15;
14731		this.arcWidth = params.arcWidth || 5;
14732		this.gapWidth = params.gapWidth || 3;
14733		this.centerX = params.centerX || this.canvas.width / 2;
14734		this.centerY = params.centerY || this.canvas.height / 2;
14735		this.infoLineLength = params.infoLineLength || 60;
14736		this.horizLineLength = params.horizLineLength || 10;
14737		this.infoLineAngleInterval = params.infoLineAngleInterval || Math.PI / 8;
14738		this.infoLineBaseAngle = params.infoLineBaseAngle || Math.PI / 6;
14739
14740		this.context = this.canvas.getContext('2d');
14741
14742		this.width = this.canvas.width;
14743		this.height = this.canvas.height;
14744
14745		this.circles = [];
14746		this.runningCount = 0;
14747	};
14748
14749	ProgressCircle.prototype = {
14750		constructor: ProgressCircle,
14751
14752		/**
14753		 * @method Adds an progress monitor entry.
14754		 * @param params.fillColor Color to fill in the circle.
14755		 * @param params.outlineColor Color to outline the circle.
14756		 * @param params.progressListener Callback function to fetch the progress.
14757		 * @param params.infoListener Callback function to fetch the info.
14758		 * @returns this
14759		 */
14760		addEntry: function(params) {
14761			this.circles.push(new Circle({
14762				canvas: this.canvas,
14763				context: this.context,
14764				centerX: this.centerX,
14765				centerY: this.centerY,
14766				innerRadius: this.minRadius + this.circles.length *
14767					(this.gapWidth + this.arcWidth),
14768				arcWidth: this.arcWidth,
14769				infoLineLength: this.infoLineLength,
14770				horizLineLength: this.horizLineLength,
14771
14772				id: this.circles.length,
14773				fillColor: params.fillColor,
14774				outlineColor: params.outlineColor,
14775				progressListener: params.progressListener,
14776				infoListener: params.infoListener,
14777				infoLineAngle: this.infoLineBaseAngle +
14778					this.circles.length * this.infoLineAngleInterval,
14779			}));
14780
14781			return this;
14782		},
14783
14784		/**
14785		 * @method Starts the monitor and updates with the given interval.
14786		 * @param interval Interval between updates, in millisecond.
14787		 * @returns this
14788		 */
14789		start: function(interval) {
14790			var self = this;
14791			this.timer = setInterval(function() {
14792				self._update();
14793			}, interval || 33);
14794
14795			return this;
14796		},
14797
14798		/**
14799		 * @method Stop the animation.
14800		 */
14801		stop: function() {
14802			clearTimeout(this.timer);
14803		},
14804
14805		/**
14806		 * @private
14807		 * @method Call update on each circle and redraw them.
14808		 * @returns this
14809		 */
14810		_update: function() {
14811			this._clear();
14812			this.circles.forEach(function(circle, idx, array) {
14813				circle.update();
14814			});
14815
14816			return this;
14817		},
14818
14819		/**
14820		 * @private
14821		 * @method Clear the canvas.
14822		 * @returns this
14823		 */
14824		_clear: function() {
14825			this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
14826
14827			return this;
14828		},
14829
14830	};
14831
14832	/**
14833	 * @private
14834	 * @class Individual progress circle.
14835	 * @param params.canvas Canvas on which the circle will be drawn.
14836	 * @param params.context Context of the canvas.
14837	 * @param params.innerRadius Inner radius of the circle, in px.
14838	 * @param params.arcWidth Width of each arc(circle).
14839	 * @param params.gapWidth Distance between each arc.
14840	 * @param params.centerX X coordinate of the center of circles.
14841	 * @param params.centerY Y coordinate of the center of circles.
14842	 * @param params.fillColor Color to fill in the circle.
14843	 * @param params.outlineColor Color to outline the circle.
14844	 * @param params.progressListener Callback function to fetch the progress.
14845	 * @param params.infoListener Callback function to fetch the info.
14846	 * @param params.infoLineAngle Angle of info line.
14847	 */
14848	var Circle = function(params) {
14849		this.id = params.id;
14850		this.canvas = params.canvas;
14851		this.context = params.context;
14852		this.centerX = params.centerX;
14853		this.centerY = params.centerY;
14854		this.arcWidth = params.arcWidth;
14855		this.innerRadius = params.innerRadius || 0;
14856		this.fillColor = params.fillColor || '#fff';
14857		this.outlineColor = params.outlineColor || this.fillColor;
14858		this.progressListener = params.progressListener;
14859		this.infoLineLength = params.infoLineLength || 250;
14860		this.horizLineLength = params.horizLineLength || 50;
14861		this.infoListener = params.infoListener;
14862		this.infoLineAngle = params.infoLineAngle;
14863
14864		this.outerRadius = this.innerRadius + this.arcWidth;
14865
14866		// If the info listener is not registered, then don't calculate
14867		// the related coordinates
14868		if (!this.infoListener) return;
14869
14870		// calculate the info-line turning points
14871		var angle = this.infoLineAngle,
14872			arcDistance = (this.innerRadius + this.outerRadius) / 2,
14873
14874			sinA = Math.sin(angle),
14875			cosA = Math.cos(angle);
14876
14877		this.infoLineStartX = this.centerX + sinA * arcDistance;
14878		this.infoLineStartY = this.centerY - cosA * arcDistance;
14879
14880		this.infoLineMidX = this.centerX + sinA * this.infoLineLength;
14881		this.infoLineMidY = this.centerY - cosA * this.infoLineLength;
14882
14883		this.infoLineEndX = this.infoLineMidX +
14884			 (sinA < 0 ? -this.horizLineLength : this.horizLineLength);
14885		this.infoLineEndY = this.infoLineMidY;
14886
14887		var infoText = document.createElement('div'),
14888			style = infoText.style;
14889
14890		style.color = this.fillColor;
14891		style.position = 'absolute';
14892		style.left = this.infoLineEndX + absPos(this.canvas)[0] + 'px';
14893		// style.top will be calculated in the `drawInfo` method. Since
14894		// user may want to change the size of the font, so the top offset
14895		// must be updated in each loop.
14896		infoText.className = 'ProgressCircleInfo'; // For css styling
14897		infoText.id = 'progress_circle_info_' + this.id;
14898		document.body.appendChild(infoText);
14899		this.infoText = infoText;
14900	};
14901
14902	Circle.prototype = {
14903		constructor: Circle,
14904
14905		update: function() {
14906			this.progress = this.progressListener();
14907			this._draw();
14908
14909			if (this.infoListener) {
14910				this.info = this.infoListener();
14911				this._drawInfo();
14912			}
14913		},
14914
14915		/**
14916		 * @private
14917		 * @method Draw the circle on the canvas.
14918		 * @returns this
14919		 */
14920		_draw: function() {
14921			var ctx = this.context,
14922
14923				ANGLE_OFFSET = -Math.PI / 2,
14924
14925				startAngle = 0 + ANGLE_OFFSET,
14926				endAngle= startAngle + this.progress * Math.PI * 2,
14927
14928				x = this.centerX,
14929				y = this.centerY,
14930
14931				innerRadius = this.innerRadius - this.arcWidth - 1,
14932				outerRadius = this.outerRadius - this.arcWidth - 1;
14933
14934			if ( innerRadius < 0 )
14935				return;
14936
14937			ctx.fillStyle = this.fillColor;
14938			ctx.strokeStyle = this.outlineColor;
14939
14940			ctx.beginPath();
14941			ctx.arc(x, y, innerRadius, startAngle, endAngle, false);
14942			ctx.arc(x, y, outerRadius, endAngle, startAngle, true);
14943			ctx.closePath();
14944			ctx.stroke();
14945			ctx.fill();
14946
14947			return this;
14948		},
14949
14950		/**
14951		 * @private
14952		 * @method Draw the info lines and info text.
14953		 * @returns this
14954		 */
14955		_drawInfo: function() {
14956
14957			var pointList, lineHeight;
14958
14959			pointList = [
14960				[this.infoLineStartX, this.infoLineStartY],
14961				[this.infoLineMidX, this.infoLineMidY],
14962				[this.infoLineEndX, this.infoLineEndY],
14963			];
14964			this._drawSegments(pointList, false);
14965
14966			this.infoText.innerHTML = this.info;
14967
14968			lineHeight = this.infoText.offsetHeight;
14969			this.infoText.style.top = this.infoLineEndY +
14970				absPos(this.canvas)[1] - lineHeight / 2 + 'px';
14971
14972			return this;
14973		},
14974
14975		/**
14976		 * @private
14977		 * @method Helper function to draw the segments
14978		 * @param pointList An array of points in the form of [x, y].
14979		 * @param close Whether to connect the first and last point.
14980		 */
14981		_drawSegments: function(pointList, close) {
14982			var ctx = this.context;
14983
14984			ctx.beginPath();
14985			ctx.moveTo(pointList[0][0], pointList[0][1]);
14986			for (var i = 1; i < pointList.length; ++i) {
14987				ctx.lineTo(pointList[i][0], pointList[i][1]);
14988			}
14989
14990			if (close) {
14991				ctx.closePath();
14992			}
14993			ctx.stroke();
14994		},
14995	};
14996
14997	window.ProgressCircle = ProgressCircle;
14998
14999})(window, document);
15000
15001
15002
15003
15004/* =========================================================
15005 * jquery.vc_chart.js v1.0
15006 * =========================================================
15007 * Copyright 2013 Wpbakery
15008 *
15009 * Jquery chart plugin for the Visual Composer.
15010 * ========================================================= */
15011(function($){
15012	/**
15013	 * Pie chart animated.
15014	 * @param element - DOM element
15015	 * @param options - settings object.
15016	 * @constructor
15017	 */
15018	var VcChart = function(element, options) {
15019		this.el = element;
15020		this.$el = $(this.el);
15021		var $this = this;
15022		$this.options = $.extend({
15023			color: 'wpb_button',
15024			units: '',
15025			width: '',
15026			label_selector: '.vc_pie_chart_value',
15027			back_selector: '.vc_pie_chart_back',
15028			responsive: true
15029		}, options);
15030		$this.init();
15031	};
15032	VcChart.prototype = {
15033		constructor: VcChart,
15034		_progress_v: 0,
15035		animated: false,
15036		colors: {
15037			'wpb_button': 'rgba(247, 247, 247, 1)',
15038			'btn-primary': 'rgba(0, 136, 204, 1)',
15039			'btn-info': 'rgba(88, 185, 218, 1)',
15040			'btn-success': 'rgba(106, 177, 101, 1)',
15041			'btn-warning': 'rgba(255, 153, 0, 1)',
15042			'btn-danger': 'rgba(255, 103, 91, 1)',
15043			'btn-inverse': 'rgba(85, 85, 85, 1)'
15044		},
15045		init: function() {
15046			this.setupColor();
15047			this.value = this.$el.data('pie-value')/100;
15048			this.label_value = this.$el.data('pie-label-value') || this.$el.data('pie-value');
15049			this.$wrapper = $('.vc_pie_wrapper', this.$el);
15050			this.$label = $(this.options.label_selector, this.$el);
15051			this.$back = $(this.options.back_selector, this.$el);
15052			this.$canvas = this.$el.find('canvas');
15053			this.arcWidth = this.$el.data('pie-width') * 2;
15054			this.draw();
15055			this.setWayPoint();
15056			if(this.options.responsive === true) this.setResponsive();
15057			if (UNCODE.isMobile) this._progress_v = this.value;
15058		},
15059		setupColor: function() {
15060			// if(typeof this.colors[this.options.color] !== 'undefined') {
15061			//     this.color = this.colors[this.options.color];
15062			// } else if(typeof this.options.color === 'string' && this.options.color.match(/^rgba?\([^\)]+\)/)) {
15063			//     this.color = this.options.color;
15064			// } else {
15065			//     this.color = 'rgba(247, 247, 247, 0.2)';
15066			// }
15067			if(typeof this.options.color !== 'undefined') {
15068				this.color = this.options.color;
15069			} else this.color = 'rgba(247, 247, 247, 0.2)';
15070
15071		},
15072		setResponsive: function() {
15073			var that = this;
15074			if (!UNCODE.isMobile) {
15075				$(window).resize(function(){
15076					if(that.animated === true) that.circle.stop();
15077					that.draw(true);
15078				});
15079			}
15080		},
15081		draw: function(redraw) {
15082			var w = this.$el.addClass('vc_ready').width() * 2,
15083				border_w = this.arcWidth,
15084				radius;
15085			if(!w) w = this.$el.parents(':visible').first().width()-2;
15086			//w = Math.round(w/100*80);
15087			//if (w < 250) w = 250;
15088			radius = w/2;
15089			this.$wrapper.css({"width" : (w / 2) + "px"});
15090			this.$label.css({"width" : (w / 2), "height" : (w / 2), "line-height" : (w / 2)+"px"});
15091			this.$back.css({"width" : (w / 2), "height" : (w / 2)});
15092			this.$canvas.attr({"width" : w + "px", "height" : w + "px"});
15093			this.$el.addClass('vc_ready');
15094			this.circle = new ProgressCircle({
15095				canvas: this.$canvas.get(0),
15096				minRadius: radius,
15097				arcWidth: border_w
15098			});
15099			if(redraw === true && this.animated === true) {
15100				this._progress_v = this.value;
15101				this.circle.addEntry({
15102					fillColor: this.color,
15103					progressListener: $.proxy(this.setProgress, this)
15104				}).start();
15105			}
15106		},
15107		setProgress: function() {
15108			if (this._progress_v >= this.value) {
15109				this.circle.stop();
15110				this.animated = true;
15111				this.$label.html(this.label_value + this.options.units);
15112				return this._progress_v;
15113			}
15114			this._progress_v += 0.01;
15115			if (!isNaN(this.label_value)) {
15116				var label_value = this._progress_v/this.value*this.label_value;
15117				var val = Math.round(label_value) + this.options.units;
15118				this.$label.html(val);
15119			} else this.$label.html(this.label_value + this.options.units);
15120			return this._progress_v;
15121		},
15122		animate: function() {
15123			if(this.animated !== true) {
15124				this.circle.addEntry({
15125					fillColor: this.color,
15126					progressListener: $.proxy(this.setProgress, this)
15127				}).start(10);
15128			}
15129		},
15130		setWayPoint: function() {
15131			if (typeof $.fn.waypoint !== 'undefined' && !UNCODE.isMobile) {
15132				this.$el.waypoint($.proxy(this.animate, this), { offset: '85%' });
15133			} else {
15134				this.animate();
15135			}
15136		}
15137	};
15138	/**
15139	 * jQuery plugin
15140	 * @param option - object with settings
15141	 * @return {*}
15142	 */
15143	$.fn.vcChat = function(option, value) {
15144		return this.each(function () {
15145			var $this = $(this),
15146				data = $this.data('vc_chart'),
15147				options = typeof option === 'object' ? option : {
15148					color: $this.data('pie-color'),
15149					units: $this.data('pie-units')
15150				};
15151			if (typeof option == 'undefined') $this.data('vc_chart', (data = new VcChart(this, options)));
15152			if (typeof option == 'string') data[option](value);
15153		});
15154	};
15155	/**
15156	 * Allows users to rewrite function inside theme.
15157	 */
15158	if ( typeof window['vc_pieChart'] !== 'function' ) {
15159		window.vc_pieChart = function() {
15160			$('.vc_pie_chart:visible:not(.vc_ready)').vcChat();
15161		}
15162	}
15163	$(document).ready(function(){
15164		!window.vc_iframe && vc_pieChart();
15165
15166		/**
15167		* Tab and collapse integration.
15168		*/
15169		$('.nav-tabs a').on('shown.bs.tab', function(e){
15170			var $cont = $(e.target).closest('.tab-container'),
15171				$active = $('.tab-pane.active', $cont);
15172			$('.vc_pie_chart:not(.vc_ready)', $active).vcChat();
15173		});
15174
15175		$('.panel-collapse').on('shown.bs.collapse', function (e) {
15176			$('.vc_pie_chart:not(.vc_ready)', e.target).vcChat();
15177		})
15178	});
15179
15180})(window.jQuery);
15181
15182/* Progress bar
15183 ---------------------------------------------------------- */
15184function uncode_progress_bar() {    
15185	jQuery.each(jQuery('.vc_progress_bar'), function(index, val) {
15186		if (UNCODE.isUnmodalOpen && !val.closest('#unmodal-content')) {
15187			return;
15188		}
15189		if (!UNCODE.isMobile) {
15190			new Waypoint({
15191				context: UNCODE.isUnmodalOpen ? document.getElementById('unmodal-content') : window,
15192				element: val,
15193				handler: function() {
15194					var element = jQuery(this.element);
15195					element.find('.vc_single_bar').each(function(index) {          
15196						var $this = jQuery(this),
15197							            bar = $this.find('.vc_bar'),
15198							            val = bar.data('percentage-value');          
15199						setTimeout(function() {            
15200							bar.css({
15201								"width": val + '%'
15202							});          
15203						}, index * 200);        
15204					});
15205				},
15206				offset: '80%'
15207			});
15208		} else {
15209			var element = jQuery(val);
15210			element.find('.vc_single_bar').each(function(index) {          
15211				var $this = jQuery(this),
15212					            bar = $this.find('.vc_bar'),
15213					            val = bar.data('percentage-value');          
15214				setTimeout(function() {            
15215					bar.css({
15216						"width": val + '%'
15217					});          
15218				}, index * 200);        
15219			});
15220		}
15221	});
15222};
15223uncode_progress_bar();
15224
15225/*!
15226 * jquery.counterup.js 1.0
15227 *
15228 * Copyright 2013, Benjamin Intal http://gambit.ph @bfintal
15229 * Released under the GPL v2 License
15230 *
15231 * Date: Nov 26, 2013
15232 */
15233(function($) {
15234	"use strict";
15235	$.fn.counterUp = function(options) {
15236		// Defaults
15237		var settings = $.extend({
15238			'time': 400,
15239			'delay': 10
15240		}, options);
15241		return this.each(function() {
15242			// Store the object
15243			var $this = $(this);
15244			var $settings = settings;
15245			var counterUpper = function() {
15246				var nums = [];
15247				var divisions = $settings.time / $settings.delay;
15248				var numReal = $this.attr('data-val'),
15249				num = numReal;
15250				var isComma = /[0-9]+,[0-9]+/.test(num);
15251				num = num.replace(/,/g, '');
15252				var isInt = /^[0-9]+$/.test(num);
15253				var isFloat = /^[0-9]+\.[0-9]+$/.test(num);
15254				var decimalPlaces = isFloat ? (num.split('.')[1] || []).length : 0;
15255				// Generate list of incremental numbers to display
15256				for (var i = divisions; i >= 1; i--) {
15257					// Preserve as int if input was int
15258					var newNum = parseInt(num / divisions * i);
15259					// Preserve float if input was float
15260					if (isFloat) {
15261						newNum = parseFloat(num / divisions * i).toFixed(decimalPlaces);
15262					}
15263					// Preserve commas if input had commas
15264					if (isComma) {
15265						while (/(\d+)(\d{3})/.test(newNum.toString())) {
15266							newNum = newNum.toString().replace(/(\d+)(\d{3})/, '$1' + ',' + '$2');
15267						}
15268					}
15269					nums.unshift(newNum);
15270				}
15271				nums.push(numReal);
15272				$this.data('counterup-nums', nums);
15273				$this.text('0');
15274				// Updates the number until we're done
15275				var f = function() {
15276					if ($this.data('counterup-nums') != null) {
15277						$this.text($this.data('counterup-nums').shift());
15278						if ($this.data('counterup-nums').length) {
15279							setTimeout($this.data('counterup-func'), $settings.delay);
15280						} else {
15281							delete $this.data('counterup-nums');
15282							$this.data('counterup-nums', null);
15283							$this.data('counterup-func', null);
15284						}
15285					}
15286				};
15287				$this.data('counterup-func', f);
15288				// Start the count up
15289				setTimeout($this.data('counterup-func'), $settings.delay);
15290			};
15291			// Perform counts when the element gets into view
15292			new Waypoint({
15293				context: UNCODE.isUnmodalOpen ? document.getElementById('unmodal-content') : window,
15294				element: this,
15295				handler: function() {
15296					counterUpper();
15297					if (!UNCODE.isUnmodalOpen) {
15298						this.destroy();
15299					}
15300				},
15301				offset: '100%'
15302			});
15303		});
15304	};
15305})(jQuery);
15306
15307/*!
15308 * The Final Countdown for jQuery v2.0.4 (http://hilios.github.io/jQuery.countdown/)
15309 * Copyright (c) 2014 Edson Hilios
15310 *
15311 * Permission is hereby granted, free of charge, to any person obtaining a copy of
15312 * this software and associated documentation files (the "Software"), to deal in
15313 * the Software without restriction, including without limitation the rights to
15314 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
15315 * the Software, and to permit persons to whom the Software is furnished to do so,
15316 * subject to the following conditions:
15317 *
15318 * The above copyright notice and this permission notice shall be included in all
15319 * copies or substantial portions of the Software.
15320 *
15321 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15322 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15323 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15324 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
15325 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
15326 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15327 */
15328(function(factory) {
15329    "use strict";
15330    if (typeof define === "function" && define.amd) {
15331        define([ "jquery" ], factory);
15332    } else {
15333        factory(jQuery);
15334    }
15335})(function($) {
15336    "use strict";
15337    var PRECISION = 100;
15338    var instances = [], matchers = [];
15339    matchers.push(/^[0-9]*$/.source);
15340    matchers.push(/([0-9]{1,2}\/){2}[0-9]{4}( [0-9]{1,2}(:[0-9]{2}){2})?/.source);
15341    matchers.push(/[0-9]{4}([\/\-][0-9]{1,2}){2}( [0-9]{1,2}(:[0-9]{2}){2})?/.source);
15342    matchers = new RegExp(matchers.join("|"));
15343    function parseDateString(dateString) {
15344        if (dateString instanceof Date) {
15345            return dateString;
15346        }
15347        if (String(dateString).match(matchers)) {
15348            if (String(dateString).match(/^[0-9]*$/)) {
15349                dateString = Number(dateString);
15350            }
15351            if (String(dateString).match(/\-/)) {
15352                dateString = String(dateString).replace(/\-/g, "/");
15353            }
15354            return new Date(dateString);
15355        } else {
15356            throw new Error("Couldn't cast `" + dateString + "` to a date object.");
15357        }
15358    }
15359    var DIRECTIVE_KEY_MAP = {
15360        Y: "years",
15361        m: "months",
15362        w: "weeks",
15363        d: "days",
15364        D: "totalDays",
15365        H: "hours",
15366        M: "minutes",
15367        S: "seconds"
15368    };
15369    function strftime(offsetObject) {
15370        return function(format) {
15371            var directives = format.match(/%(-|!)?[A-Z]{1}(:[^;]+;)?/gi);
15372            if (directives) {
15373                for (var i = 0, len = directives.length; i < len; ++i) {
15374                    var directive = directives[i].match(/%(-|!)?([a-zA-Z]{1})(:[^;]+;)?/), regexp = new RegExp(directive[0]), modifier = directive[1] || "", plural = directive[3] || "", value = null;
15375                    directive = directive[2];
15376                    if (DIRECTIVE_KEY_MAP.hasOwnProperty(directive)) {
15377                        value = DIRECTIVE_KEY_MAP[directive];
15378                        value = Number(offsetObject[value]);
15379                    }
15380                    if (value !== null) {
15381                        if (modifier === "!") {
15382                  
15382          value = pluralize(plural, value);
15383                        }
15384                        if (modifier === "") {
15385                            if (value < 10) {
15386                                value = "0" + value.toString();
15387                            }
15388                        }
15389                        format = format.replace(regexp, value.toString());
15390                    }
15391                }
15392            }
15393            format = format.replace(/%%/, "%");
15394            return format;
15395        };
15396    }
15397    function pluralize(format, count) {
15398        var plural = "s", singular = "";
15399        if (format) {
15400            format = format.replace(/(:|;|\s)/gi, "").split(/\,/);
15401            if (format.length === 1) {
15402                plural = format[0];
15403            } else {
15404                singular = format[0];
15405                plural = format[1];
15406            }
15407        }
15408        if (Math.abs(count) === 1) {
15409            return singular;
15410        } else {
15411            return plural;
15412        }
15413    }
15414    var Countdown = function(el, finalDate, callback) {
15415        this.el = el;
15416        this.$el = $(el);
15417        this.interval = null;
15418        this.offset = {};
15419        this.instanceNumber = instances.length;
15420        instances.push(this);
15421        this.$el.data("countdown-instance", this.instanceNumber);
15422        if (callback) {
15423            this.$el.on("update.countdown", callback);
15424            this.$el.on("stoped.countdown", callback);
15425            this.$el.on("finish.countdown", callback);
15426        }
15427        this.setFinalDate(finalDate);
15428        this.start();
15429    };
15430    $.extend(Countdown.prototype, {
15431        start: function() {
15432            if (this.interval !== null) {
15433                clearInterval(this.interval);
15434            }
15435            var self = this;
15436            this.update();
15437            this.interval = setInterval(function() {
15438                self.update.call(self);
15439            }, PRECISION);
15440        },
15441        stop: function() {
15442            clearInterval(this.interval);
15443            this.interval = null;
15444            this.dispatchEvent("stoped");
15445        },
15446        pause: function() {
15447            this.stop.call(this);
15448        },
15449        resume: function() {
15450            this.start.call(this);
15451        },
15452        remove: function() {
15453            this.stop();
15454            instances[this.instanceNumber] = null;
15455            delete this.$el.data().countdownInstance;
15456        },
15457        setFinalDate: function(value) {
15458            this.finalDate = parseDateString(value);
15459        },
15460        update: function() {
15461            if (this.$el.closest("html").length === 0) {
15462                this.remove();
15463                return;
15464            }
15465            this.totalSecsLeft = this.finalDate.getTime() - new Date().getTime();
15466            this.totalSecsLeft = Math.ceil(this.totalSecsLeft / 1e3);
15467            this.totalSecsLeft = this.totalSecsLeft < 0 ? 0 : this.totalSecsLeft;
15468            this.offset = {
15469                seconds: this.totalSecsLeft % 60,
15470                minutes: Math.floor(this.totalSecsLeft / 60) % 60,
15471                hours: Math.floor(this.totalSecsLeft / 60 / 60) % 24,
15472                days: Math.floor(this.totalSecsLeft / 60 / 60 / 24) % 7,
15473                totalDays: Math.floor(this.totalSecsLeft / 60 / 60 / 24),
15474                weeks: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 7),
15475                months: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 30),
15476                years: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 365)
15477            };
15478            if (this.totalSecsLeft === 0) {
15479                this.stop();
15480                this.dispatchEvent("finish");
15481            } else {
15482                this.dispatchEvent("update");
15483            }
15484        },
15485        dispatchEvent: function(eventName) {
15486            var event = $.Event(eventName + ".countdown");
15487            event.finalDate = this.finalDate;
15488            event.offset = $.extend({}, this.offset);
15489            event.strftime = strftime(this.offset);
15490            this.$el.trigger(event);
15491        }
15492    });
15493    $.fn.countdown = function() {
15494        var argumentsArray = Array.prototype.slice.call(arguments, 0);
15495        return this.each(function() {
15496            var instanceNumber = $(this).data("countdown-instance");
15497            if (instanceNumber !== undefined) {
15498                var instance = instances[instanceNumber], method = argumentsArray[0];
15499                if (Countdown.prototype.hasOwnProperty(method)) {
15500                    instance[method].apply(instance, argumentsArray.slice(1));
15501                } else if (String(method).match(/^[$A-Z_][0-9A-Z_$]*$/i) === null) {
15502                    instance.setFinalDate.call(instance, method);
15503                    instance.start();
15504                } else {
15505                    $.error("Method %s does not exist on jQuery.countdown".replace(/\%s/gi, method));
15506                }
15507            } else {
15508                new Countdown(this, argumentsArray[0], argumentsArray[1]);
15509            }
15510        });
15511    };
15512});
15513
15514/*! waitForImages jQuery Plugin - v2.0.2 - 2015-05-05
15515* https://github.com/alexanderdickson/waitForImages
15516* Copyright (c) 2015 Alex Dickson; Licensed MIT */
15517;(function (factory) {
15518    if (typeof define === 'function' && define.amd) {
15519        // AMD. Register as an anonymous module.
15520        define(['jquery'], factory);
15521    } else if (typeof exports === 'object') {
15522        // CommonJS / nodejs module
15523        module.exports = factory(require('jquery'));
15524    } else {
15525        // Browser globals
15526        factory(jQuery);
15527    }
15528}(function ($) {
15529    // Namespace all events.
15530    var eventNamespace = 'waitForImages';
15531
15532    // CSS properties which contain references to images.
15533    $.waitForImages = {
15534        hasImageProperties: [
15535            'backgroundImage',
15536            'listStyleImage',
15537            'borderImage',
15538            'borderCornerImage',
15539            'cursor'
15540        ],
15541        hasImageAttributes: ['srcset']
15542    };
15543
15544    // Custom selector to find `img` elements that have a valid `src`
15545    // attribute and have not already loaded.
15546    $.expr[':'].uncached = function (obj) {
15547        // Ensure we are dealing with an `img` element with a valid
15548        // `src` attribute.
15549        if (!$(obj).is('img[src][src!=""]')) {
15550            return false;
15551        }
15552
15553        return !obj.complete;
15554    };
15555
15556    $.fn.waitForImages = function () {
15557
15558        var allImgsLength = 0;
15559        var allImgsLoaded = 0;
15560        var deferred = $.Deferred();
15561
15562        var finishedCallback;
15563        var eachCallback;
15564        var waitForAll;
15565
15566        // Handle options object (if passed).
15567        if ($.isPlainObject(arguments[0])) {
15568
15569            waitForAll = arguments[0].waitForAll;
15570            eachCallback = arguments[0].each;
15571            finishedCallback = arguments[0].finished;
15572
15573        } else {
15574
15575            // Handle if using deferred object and only one param was passed in.
15576            if (arguments.length === 1 && $.type(arguments[0]) === 'boolean') {
15577                waitForAll = arguments[0];
15578            } else {
15579                finishedCallback = arguments[0];
15580                eachCallback = arguments[1];
15581                waitForAll = arguments[2];
15582            }
15583
15584        }
15585
15586        // Handle missing callbacks.
15587        finishedCallback = finishedCallback || $.noop;
15588        eachCallback = eachCallback || $.noop;
15589
15590        // Convert waitForAll to Boolean
15591        waitForAll = !! waitForAll;
15592
15593        // Ensure callbacks are functions.
15594        if (!$.isFunction(finishedCallback) || !$.isFunction(eachCallback)) {
15595            throw new TypeError('An invalid callback was supplied.');
15596        }
15597
15598        this.each(function () {
15599            // Build a list of all imgs, dependent on what images will
15600            // be considered.
15601            var obj = $(this);
15602            var allImgs = [];
15603            // CSS properties which may contain an image.
15604            var hasImgProperties = $.waitForImages.hasImageProperties || [];
15605            // Element attributes which may contain an image.
15606            var hasImageAttributes = $.waitForImages.hasImageAttributes || [];
15607            // To match `url()` references.
15608            // Spec: http://www.w3.org/TR/CSS2/syndata.html#value-def-uri
15609            var matchUrl = /url\(\s*(['"]?)(.*?)\1\s*\)/g;
15610
15611            if (waitForAll) {
15612
15613                // Get all elements (including the original), as any one of
15614                // them could have a background image.
15615                obj.find('*').addBack().each(function () {
15616                    var element = $(this);
15617
15618                    // If an `img` element, add it. But keep iterating in
15619                    // case it has a background image too.
15620                    if (element.is('img:uncached')) {
15621                        allImgs.push({
15622                            src: element.attr('src'),
15623                            element: element[0]
15624                        });
15625                    }
15626
15627                    $.each(hasImgProperties, function (i, property) {
15628                        var propertyValue = element.css(property);
15629                        var match;
15630
15631                        // If it doesn't contain this property, skip.
15632                        if (!propertyValue) {
15633                            return true;
15634                        }
15635
15636                        // Get all url() of this element.
15637                        while (match = matchUrl.exec(propertyValue)) {
15638                            allImgs.push({
15639                                src: match[2],
15640                                element: element[0]
15641                            });
15642                        }
15643                    });
15644
15645                    $.each(hasImageAttributes, function (i, attribute) {
15646                        var attributeValue = element.attr(attribute);
15647                        var attributeValues;
15648
15649                        // If it doesn't contain this property, skip.
15650                        if (!attributeValue) {
15651                            return true;
15652                        }
15653
15654                        // Check for multiple comma separated images
15655                        attributeValues = attributeValue.split(',');
15656
15657                        $.each(attributeValues, function(i, value) {
15658                            // Trim value and get string before first
15659                            // whitespace (for use with srcset).
15660                            value = $.trim(value).split(' ')[0];
15661                            allImgs.push({
15662                                src: value,
15663                                element: element[0]
15664                            });
15665                        });
15666                    });
15667                });
15668            } else {
15669                // For images only, the task is simpler.
15670                obj.find('img:uncached')
15671                    .each(function () {
15672                    allImgs.push({
15673                        src: this.src,
15674                        element: this
15675                    });
15676                });
15677            }
15678
15679            allImgsLength = allImgs.length;
15680            allImgsLoaded = 0;
15681
15682            // If no images found, don't bother.
15683            if (allImgsLength === 0) {
15684                finishedCallback.call(obj[0]);
15685                deferred.resolveWith(obj[0]);
15686            }
15687
15688            $.each(allImgs, function (i, img) {
15689
15690                var image = new Image();
15691                var events =
15692                  'load.' + eventNamespace + ' error.' + eventNamespace;
15693
15694                // Handle the image loading and error with the same callback.
15695                $(image).one(events, function me (event) {
15696                    // If an error occurred with loading the image, set the
15697                    // third argument accordingly.
15698                    var eachArguments = [
15699                        allImgsLoaded,
15700                        allImgsLength,
15701                        event.type == 'load'
15702                    ];
15703                    allImgsLoaded++;
15704
15705                    eachCallback.apply(img.element, eachArguments);
15706                    deferred.notifyWith(img.element, eachArguments);
15707
15708                    // Unbind the event listeners. I use this in addition to
15709                    // `one` as one of those events won't be called (either
15710                    // 'load' or 'error' will be called).
15711                    $(this).off(events, me);
15712
15713                    if (allImgsLoaded == allImgsLength) {
15714                        finishedCallback.call(obj[0]);
15715                        deferred.resolveWith(obj[0]);
15716                        return false;
15717                    }
15718
15719                });
15720
15721                image.src = img.src;
15722            });
15723        });
15724
15725        return deferred.promise();
15726
15727    };
15728}));
15729
15730/**
15731 * @preserve  textfill
15732 * @name      jquery.textfill.js
15733 * @author    Russ Painter
15734 * @author    Yu-Jie Lin
15735 * @author    Alexandre Dantas
15736 * @version   0.6.0
15737 * @date      2014-08-19
15738 * @copyright (c) 2014 Alexandre Dantas
15739 * @copyright (c) 2012-2013 Yu-Jie Lin
15740 * @copyright (c) 2009 Russ Painter
15741 * @license   MIT License
15742 * @homepage  https://github.com/jquery-textfill/jquery-textfill
15743 * @example   http://jquery-textfill.github.io/jquery-textfill/index.html
15744 */
15745; (function($) {
15746
15747	/**
15748	 * Resizes an inner element's font so that the
15749	 * inner element completely fills the outer element.
15750	 *
15751	 * @param {Object} options User options that take
15752	 *                         higher precedence when
15753	 *                         merging with the default ones.
15754	 *
15755	 * @return All outer elements processed
15756	 */
15757	$.fn.textfill = function(options) {
15758
15759		// ______  _______ _______ _______ _     _        _______ _______
15760		// |     \ |______ |______ |_____| |     | |         |    |______
15761		// |_____/ |______ |       |     | |_____| |_____    |    ______|
15762        //
15763		// Merging user options with the default values
15764
15765		var defaults = {
15766			debug            : false,
15767			maxFontPixels    : 40,
15768			minFontPixels    : 4,
15769			innerTag         : 'span',
15770			widthOnly        : false,
15771			success          : null, // callback when a resizing is done
15772			callback         : null, // callback when a resizing is done (deprecated, use success)
15773			fail             : null, // callback when a resizing is failed
15774			complete         : null, // callback when all is done
15775			explicitWidth    : null,
15776			explicitHeight   : null,
15777			changeLineHeight : false
15778		};
15779
15780		var Opts = $.extend(defaults, options);
15781
15782		// _______ _     _ __   _ _______ _______ _____  _____  __   _ _______
15783		// |______ |     | | \  | |          |      |   |     | | \  | |______
15784		// |       |_____| |  \_| |_____     |    __|__ |_____| |  \_| ______|
15785		//
15786		// Predefining the awesomeness
15787
15788		// Output arguments to the Debug console
15789		// if "Debug Mode" is enabled
15790		function _debug() {
15791			if (!Opts.debug
15792				||  typeof console       == 'undefined'
15793				||  typeof console.debug == 'undefined') {
15794				return;
15795			}
15796			console.debug.apply(console, arguments);
15797		}
15798
15799		// Output arguments to the Warning console
15800		function _warn() {
15801			if (typeof console      == 'undefined' ||
15802				typeof console.warn == 'undefined') {
15803				return;
15804			}
15805			console.warn.apply(console, arguments);
15806		}
15807
15808		// Outputs all information on the current sizing
15809		// of the font.
15810		function _debug_sizing(prefix, ourText, maxHeight, maxWidth, minFontPixels, maxFontPixels) {
15811
15812			function _m(v1, v2) {
15813
15814				var marker = ' / ';
15815
15816				if (v1 > v2)
15817					marker = ' > ';
15818
15819				else if (v1 == v2)
15820					marker = ' = ';
15821
15822				return marker;
15823			}
15824
15825			_debug(
15826				'[TextFill] '  + prefix + ' { ' +
15827				'font-size: ' + ourText.css('font-size') + ',' +
15828				'Height: '    + ourText.height() + 'px ' + _m(ourText.height(), maxHeight) + maxHeight + 'px,' +
15829				'Width: '     + ourText.width()  + _m(ourText.width() , maxWidth)  + maxWidth + ',' +
15830				'minFontPixels: ' + minFontPixels + 'px, ' +
15831				'maxFontPixels: ' + maxFontPixels + 'px }'
15832			);
15833		}
15834
15835		/**
15836		 * Calculates which size the font can get resized,
15837		 * according to constrains.
15838		 *
15839		 * @param {String} prefix Gets shown on the console before
15840		 *                        all the arguments, if debug mode is on.
15841		 * @param {Object} ourText The DOM element to resize,
15842		 *                         that contains the text.
15843		 * @param {function} func Function called on `ourText` that's
15844		 *                        used to compare with `max`.
15845		 * @param {number} max Maximum value, that gets compared with
15846		 *                     `func` called on `ourText`.
15847		 * @param {number} minFontPixels Minimum value the font can
15848		 *                               get resized to (in pixels).
15849		 * @param {number} maxFontPixels Maximum value the font can
15850		 *                               get resized to (in pixels).
15851		 *
15852		 * @return Size (in pixels) that the font can be resized.
15853		 */
15854		function _sizing(prefix, ourText, func, max, maxHeight, maxWidth, minFontPixels, maxFontPixels) {
15855
15856			_debug_sizing(
15857				prefix, ourText,
15858				maxHeight, maxWidth,
15859				minFontPixels, maxFontPixels
15860			);
15861
15862			// The kernel of the whole plugin, take most attention
15863			// on this part.
15864			//
15865			// This is a loop that keeps increasing the `font-size`
15866			// until it fits the parent element.
15867			//
15868			// - Start from the minimal allowed value (`minFontPixels`)
15869			// - Guesses an average font size (in pixels) for the font,
15870			// - Resizes the text and sees if its size is within the
15871			//   boundaries (`minFontPixels` and `maxFontPixels`).
15872			//   - If so, keep guessing until we break.
15873			//   - If not, return the last calculated size.
15874			//
15875			// I understand this is not optimized and we should
15876			// consider implementing something akin to
15877			// Daniel Hoffmann's answer here:
15878			//
15879			//     http://stackoverflow.com/a/17433451/1094964
15880			//
15881
15882			while (minFontPixels < (maxFontPixels - 1)) {
15883
15884				var fontSize = Math.floor((minFontPixels + maxFontPixels) / 2);
15885				ourText.css('font-size', fontSize);
15886
15887				if (func.call(ourText) <= max) {
15888					minFontPixels = fontSize;
15889
15890					if (func.call(ourText) == max)
15891						break;
15892				}
15893				else
15894					maxFontPixels = fontSize;
15895
15896				_debug_sizing(
15897					prefix, ourText,
15898					maxHeight, maxWidth,
15899					minFontPixels, maxFontPixels
15900				);
15901			}
15902
15903			ourText.css('font-size', maxFontPixels);
15904
15905			if (func.call(ourText) <= max) {
15906				minFontPixels = maxFontPixels;
15907
15908				_debug_sizing(
15909					prefix + '* ', ourText,
15910					maxHeight, maxWidth,
15911					minFontPixels, maxFontPixels
15912				);
15913			}
15914			return minFontPixels;
15915		}
15916
15917		// _______ _______ _______  ______ _______
15918		// |______    |    |_____| |_____/    |
15919		// ______|    |    |     | |    \_    |
15920        //
15921		// Let's get it started (yeah)!
15922
15923		_debug('[TextFill] Start Debug');
15924
15925		this.each(function() {
15926
15927			// Contains the child element we will resize.
15928			// $(this) means the parent container
15929			var ourText = $(Opts.innerTag + ':visible:first', this);
15930
15931			// Will resize to this dimensions.
15932			// Use explicit dimensions when specified
15933			var maxHeight = Opts.explicitHeight || $(this).height();
15934			var maxWidth  = Opts.explicitWidth  || $(this).width();
15935
15936			var oldFontSize = ourText.css('font-size');
15937
15938			var lineHeight  = parseFloat(ourText.css('line-height')) / parseFloat(oldFontSize);
15939
15940			_debug('[TextFill] Inner text: ' + ourText.text());
15941			_debug('[TextFill] All options: ', Opts);
15942			_debug('[TextFill] Maximum sizes: { ' +
15943				   'Height: ' + maxHeight + 'px, ' +
15944				   'Width: '  + maxWidth  + 'px' + ' }'
15945				  );
15946
15947			var minFontPixels = Opts.minFontPixels;
15948
15949			// Remember, if this `maxFontPixels` is negative,
15950			// the text will resize to as long as the container
15951			// can accomodate
15952			var maxFontPixels = (Opts.maxFontPixels <= 0 ?
15953								 maxHeight :
15954								 Opts.maxFontPixels);
15955
15956
15957			// Let's start it all!
15958
15959			// 1. Calculate which `font-size` would
15960			//    be best for the Height
15961			var fontSizeHeight = undefined;
15962
15963			if (! Opts.widthOnly)
15964				fontSizeHeight = _sizing(
15965					'Height', ourText,
15966					$.fn.height, maxHeight,
15967					maxHeight, maxWidth,
15968					minFontPixels, maxFontPixels
15969				);
15970
15971			// 2. Calculate which `font-size` would
15972			//    be best for the Width
15973			var fontSizeWidth = undefined;
15974
15975			fontSizeWidth = _sizing(
15976				'Width', ourText,
15977				$.fn.width, maxWidth,
15978				maxHeight, maxWidth,
15979				minFontPixels, maxFontPixels
15980			);
15981
15982			// 3. Actually resize the text!
15983
15984			if (Opts.widthOnly) {
15985				ourText.css({
15986					'font-size'  : fontSizeWidth,
15987					'white-space': 'nowrap'
15988				});
15989
15990				if (Opts.changeLineHeight)
15991					ourText.parent().css(
15992						'line-height',
15993						(lineHeight * fontSizeWidth + 'px')
15994					);
15995			}
15996			else {
15997				var fontSizeFinal = Math.min(fontSizeHeight, fontSizeWidth);
15998
15999				ourText.css('font-size', fontSizeFinal);
16000
16001				if (Opts.changeLineHeight)
16002					ourText.parent().css(
16003						'line-height',
16004						(lineHeight * fontSizeFinal) + 'px'
16005					);
16006			}
16007
16008			_debug(
16009				'[TextFill] Finished { ' +
16010				'Old font-size: ' + oldFontSize              + ', ' +
16011				'New font-size: ' + ourText.css('font-size') + ' }'
16012			);
16013
16014			// Oops, something wrong happened!
16015			// We weren't supposed to exceed the original size
16016			if ((ourText.width()  > maxWidth) ||
16017				(ourText.height() > maxHeight && !Opts.widthOnly)) {
16018
16019				ourText.css('font-size', oldFontSize);
16020
16021				// Failure callback
16022				if (Opts.fail)
16023					Opts.fail(this);
16024
16025				_debug(
16026					'[TextFill] Failure { ' +
16027					'Current Width: '  + ourText.width()  + ', ' +
16028					'Maximum Width: '  + maxWidth         + ', ' +
16029					'Current Height: ' + ourText.height() + ', ' +
16030					'Maximum Height: ' + maxHeight        + ' }'
16031				);
16032			}
16033			else if (Opts.success) {
16034				Opts.success(this);
16035			}
16036			else if (Opts.callback) {
16037				_warn('callback is deprecated, use success, instead');
16038
16039				// Success callback
16040				Opts.callback(this);
16041			}
16042		});
16043
16044		// Complete callback
16045		if (Opts.complete)
16046			Opts.complete(this);
16047
16048		_debug('[TextFill] End Debug');
16049		return this;
16050	};
16051
16052})(window.jQuery);
16053
16054!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var f;"undefined"!=typeof window?f=window:"undefined"!=typeof global?f=global:"undefined"!=typeof self&&(f=self),f.Share=e()}}(function(){var define,module,exports;
16055function getStyles(config){
16056  // return ""+config.selector+"{width:92px;height:20px;-webkit-touch-callout:none;-khtml-user-select:none;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}"+config.selector+" [class*=social-]:before{font-family:'uncodeicon'}"+config.selector+" label{font-size:16px;cursor:pointer;margin:0;padding:5px 10px;border-radius:5px;background:#a29baa;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}"+config.selector+" label:hover{opacity:.8}"+config.selector+" label span{text-transform:uppercase;font-size:.9em;font-family:Lato,sans-serif;font-weight:700;-webkit-font-smoothing:antialiased;padding-left:6px}"+config.selector+" .social{opacity:0;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out;margin-left:-15px;visibility:hidden}"+config.selector+" .social.top{-webkit-transform-origin:0 0;-ms-transform-origin:0 0;transform-origin:0 0;margin-top:-80px}"+config.selector+" .social.bottom{-webkit-transform-origin:0 0;-ms-transform-origin:0 0;transform-origin:0 0;margin-top:5px}"+config.selector+" .social.middle{margin-top:-34px}"+config.selector+" .social.middle.right{-webkit-transform-origin:5% 50%;-ms-transform-origin:5% 50%;transform-origin:5% 50%;margin-left:105px}"+config.selector+" .social.middle.left{-webkit-transform-origin:5% 50%;-ms-transform-origin:5% 50%;transform-origin:5% 50%}"+config.selector+" .social.right{margin-left:14px}"+config.selector+" .social.load{-webkit-transition:none!important;transition:none!important}"+config.selector+" .social.networks-1{width:60px}"+config.selector+" .social.networks-1.center,"+config.selector+" .social.networks-1.left{margin-left:14px}"+config.selector+" .social.networks-1.middle.left{margin-left:-70px}"+config.selector+" .social.networks-1 ul{width:60px}"+config.selector+" .social.networks-2{width:120px}"+config.selector+" .social.networks-2.center{margin-left:-13px}"+config.selector+" .social.networks-2.left{margin-left:-44px}"+config.selector+" .social.networks-2.middle.left{margin-left:-130px}"+config.selector+" .social.networks-2 ul{width:120px}"+config.selector+" .social.networks-3{width:180px}"+config.selector+" .social.networks-3.center{margin-left:-45px}"+config.selector+" .social.networks-3.left{margin-left:-102px}"+config.selector+" .social.networks-3.middle.left{margin-left:-190px}"+config.selector+" .social.networks-3 ul{width:180px}"+config.selector+" .social.networks-4{width:240px}"+config.selector+" .social.networks-4.center{margin-left:-75px}"+config.selector+" .social.networks-4.left{margin-left:162px}"+config.selector+" .social.networks-4.middle.left{margin-left:-250px}"+config.selector+" .social.networks-4 ul{width:240px}"+config.selector+" .social.networks-5{width:300px}"+config.selector+" .social.networks-5.center{margin-left:-105px}"+config.selector+" .social.networks-5.left{margin-left:-225px}"+config.selector+" .social.networks-5.middle.left{margin-left:-320px}"+config.selector+" .social.networks-5 ul{width:300px}"+config.selector+" .social.active{opacity:1;-webkit-transition:all .2s ease-in-out;transition:all .2s ease-in-out;visibility:visible}"+config.selector+" .social.active.top{-webkit-transform:scale(1) translateY(-20px);-ms-transform:scale(1) translateY(-20px);transform:scale(1) translateY(-20px)}"+config.selector+" .social.active.bottom{-webkit-transform:scale(1) translateY(15px);-ms-transform:scale(1) translateY(15px);transform:scale(1) translateY(15px)}"+config.selector+" .social.active.middle.right{-webkit-transform:scale(1) translateX(10px);-ms-transform:scale(1) translateX(10px);transform:scale(1) translateX(10px)}"+config.selector+" .social.active.middle.left{-webkit-transform:scale(1) translateX(-20px);-ms-transform:scale(1) translateX(-20px);transform:scale(1) translateX(-20px)}"+config.selector+" .social ul{position:relative;
16056left:0;right:0;height:46px;color:#fff;margin:auto;padding:0;list-style:none}"+config.selector+" .social ul li{font-size:20px;cursor:pointer;width:60px;margin:0;padding:12px 0;text-align:center;float:left;display:none;height:22px;position:relative;z-index:2;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}"+config.selector+" .social ul li:hover{}"+config.selector+" .social li[class*=facebook]{display:"+config.networks.facebook.display+"}"+config.selector+" .social li[class*=twitter]{display:"+config.networks.twitter.display+"}"+config.selector+" .social li[class*=gplus]{display:"+config.networks.google_plus.display+"}"+config.selector+" .social li[class*=pinterest]{display:"+config.networks.pinterest.display+"}"+config.selector+" .social li[class*=paper-plane]{display:"+config.networks.email.display+"}"
16057};
16058  var ShareUtils;
16059
16060if ((!("classList" in document.documentElement)) && Object.defineProperty && typeof HTMLElement !== "undefined") {
16061  Object.defineProperty(HTMLElement.prototype, "classList", {
16062    get: function() {
16063      var ret, self, update;
16064      update = function(fn) {
16065        return function(value) {
16066          var classes, index;
16067          classes = self.className.split(/\s+/);
16068          index = classes.indexOf(value);
16069          fn(classes, index, value);
16070          self.className = classes.join(" ");
16071        };
16072      };
16073      self = this;
16074      ret = {
16075        add: update(function(classes, index, value) {
16076          ~index || classes.push(value);
16077        }),
16078        remove: update(function(classes, index) {
16079          ~index && classes.splice(index, 1);
16080        }),
16081        toggle: update(function(classes, index, value) {
16082          if (~index) {
16083            classes.splice(index, 1);
16084          } else {
16085            classes.push(value);
16086          }
16087        }),
16088        contains: function(value) {
16089          return !!~self.className.split(/\s+/).indexOf(value);
16090        },
16091        item: function(i) {
16092          return self.className.split(/\s+/)[i] || null;
16093        }
16094      };
16095      Object.defineProperty(ret, "length", {
16096        get: function() {
16097          return self.className.split(/\s+/).length;
16098        }
16099      });
16100      return ret;
16101    }
16102  });
16103}
16104
16105String.prototype.to_rfc3986 = function() {
16106  var tmp;
16107  tmp = encodeURIComponent(this);
16108  return tmp.replace(/[!'()*]/g, function(c) {
16109    return "%" + c.charCodeAt(0).toString(16);
16110  });
16111};
16112
16113ShareUtils = (function() {
16114  function ShareUtils() {}
16115
16116  ShareUtils.prototype.extend = function(to, from, overwrite) {
16117    var hasProp, prop;
16118    for (prop in from) {
16119      hasProp = to[prop] !== undefined;
16120      if (hasProp && typeof from[prop] === "object") {
16121        this.extend(to[prop], from[prop], overwrite);
16122      } else {
16123        if (overwrite || !hasProp) {
16124          to[prop] = from[prop];
16125        }
16126      }
16127    }
16128  };
16129
16130  ShareUtils.prototype.hide = function(el) {
16131    return el.style.display = "none";
16132  };
16133
16134  ShareUtils.prototype.show = function(el) {
16135    return el.style.display = "block";
16136  };
16137
16138  ShareUtils.prototype.has_class = function(el, class_name) {
16139    return el.classList.contains(class_name);
16140  };
16141
16142  ShareUtils.prototype.add_class = function(el, class_name) {
16143    return el.classList.add(class_name);
16144  };
16145
16146  ShareUtils.prototype.remove_class = function(el, class_name) {
16147    return el.classList.remove(class_name);
16148  };
16149
16150  ShareUtils.prototype.is_encoded = function(str) {
16151    str = str.to_rfc3986();
16152    return decodeURIComponent(str) !== str;
16153  };
16154
16155  ShareUtils.prototype.encode = function(str) {
16156    if (typeof str === "undefined" || this.is_encoded(str)) {
16157      return str;
16158    } else {
16159      return str.to_rfc3986();
16160    }
16161  };
16162
16163  ShareUtils.prototype.popup = function(url, params) {
16164    var k, popup, qs, v;
16165    if (params == null) {
16166      params = {};
16167    }
16168    popup = {
16169      width: 500,
16170      height: 350
16171    };
16172    popup.top = (screen.height / 2) - (popup.height / 2);
16173    popup.left = (screen.width / 2) - (popup.width / 2);
16174    qs = ((function() {
16175      var _results;
16176      _results = [];
16177      for (k in params) {
16178        v = params[k];
16179        _results.push("" + k + "=" + (this.encode(v)));
16180      }
16181      return _results;
16182    }).call(this)).join('&');
16183    if (qs) {
16184      qs = "?" + qs;
16185    }
16186    return window.open(url + qs, 'targetWindow', "toolbar=no,location=no,status=no,menubar=no,scrollbars=yes,resizable=yes,left=" + popup.left + ",top=" + popup.top + ",width=" 
16186+ popup.width + ",height=" + popup.height);
16187  };
16188
16189  return ShareUtils;
16190
16191})();
16192var Share,
16193  __hasProp = {}.hasOwnProperty,
16194  __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
16195
16196Share = (function(_super) {
16197  __extends(Share, _super);
16198
16199  function Share(element, options) {
16200    this.element = element;
16201    this.el = {
16202      head: document.getElementsByTagName('head')[0],
16203      body: document.getElementsByTagName('body')[0]
16204    };
16205    this.config = {
16206      enabled_networks: 0,
16207      protocol: ['http', 'https'].indexOf(window.location.href.split(':')[0]) === -1 ? 'https://' : '//',
16208      url: window.location.href.replace("&", "%26"),
16209      caption: null,
16210      title: this.default_title(),
16211      image: this.default_image(),
16212      description: this.default_description(),
16213      ui: {
16214        flyout: 'top center',
16215        button_text: 'Share',
16216        button_font: true,
16217        icon_font: true
16218      },
16219      networks: {
16220        google_plus: {
16221          enabled: true,
16222          url: null
16223        },
16224        twitter: {
16225          enabled: true,
16226          url: null,
16227          title: null,
16228          description: null
16229        },
16230        facebook: {
16231          enabled: true,
16232          load_sdk: true,
16233          url: null,
16234          app_id: null,
16235          title: null,
16236          caption: null,
16237          description: null,
16238          image: null
16239        },
16240        pinterest: {
16241          enabled: true,
16242          url: null,
16243          image: null,
16244          description: null
16245        },
16246        reddit: {
16247          enabled: true,
16248          url: null,
16249          title: null
16250        },
16251        linkedin: {
16252          enabled: true,
16253          url: null,
16254          title: null,
16255          description: null
16256        },
16257        xing: {
16258          enabled: true,
16259          url: null,
16260          title: null,
16261          image: null,
16262          description: null
16263        },
16264        whatsapp: {
16265          enabled: true,
16266          description: null,
16267          url: null
16268        },
16269        email: {
16270          enabled: true,
16271          title: null,
16272          description: null,
16273          url: null
16274        }
16275      }
16276    };
16277    this.setup(element, options);
16278    return this;
16279  }
16280
16281  Share.prototype.setup = function(element, opts) {
16282    var index, instance, instances, _i, _len;
16283    instances = document.querySelectorAll(element);
16284    this.extend(this.config, opts, true);
16285    this.set_global_configuration();
16286    this.normalize_network_configuration();
16287    if (this.config.ui.icon_font) {
16288      this.inject_icons();
16289    }
16290    if (this.config.ui.button_font) {
16291      this.inject_fonts();
16292    }
16293    if (this.config.networks.facebook.enabled && this.config.networks.facebook.load_sdk) {
16294      this.inject_facebook_sdk();
16295    }
16296    for (index = _i = 0, _len = instances.length; _i < _len; index = ++_i) {
16297      instance = instances[index];
16298      this.setup_instance(element, index);
16299    }
16300  };
16301
16302  Share.prototype.setup_instance = function(element, index) {
16303    var button, instance, label, network, networks, _i, _len, _results,
16304      _this = this;
16305    instance = document.querySelectorAll(element)[index];
16306    this.hide(instance);
16307    this.add_class(instance, "sharer-" + index);
16308    instance = document.querySelectorAll(element)[index];
16309    this.inject_css(instance);
16310    this.inject_html(instance);
16311    this.show(instance);
16312    label = instance.getElementsByTagName("label")[0];
16313    button = instance.getElementsByClassName("social")[0];
16314    networks = instance.getElementsByTagName('li');
16315    this.add_class(button, "networks-" + this.config.enabled_networks);
16316    label.addEventListener("click", function() {
16317      return _this.event_toggle(button);
16318    });
16319    _this = this;
16320    _results = [];
16321    for (index = _i = 0, _len = networks.length; _i < _len; index = ++_i) {
16322      network = networks[index];
16323      _results.push(network.addEventListener("click", function() {
16324        _this.event_network(instance, this);
16325        return _this.event_close(button);
16326      }));
16327    }
16328    return _results;
16329  };
16330
16331  Share.prototype.event_toggle = function(button) {
16332    if (this.has_class(button, "active")) {
16333      return this.event_close(button);
16334    } else {
16335      return this.event_open(button);
16336    }
16337  };
16338
16339  Share.prototype.event_open = function(button) {
16340    if (this.has_class(button, "load")) {
16341      this.remove_class(button, "load");
16342    }
16343    return this.add_class(button, "active");
16344  };
16345
16346  Share.prototype.event_close = function(button) {
16347    return this.remove_class(button, "active");
16348  };
16349
16350  Share.prototype.event_network = function(instance, network) {
16351    var name;
16352    name = network.getAttribute("data-network");
16353    this.hook("before", name, instance);
16354    this["network_" + name]();
16355    return this.hook("after", name, instance);
16356  };
16357
16358  Share.prototype.open = function() {
16359    return this["public"]("open");
16360  };
16361
16362  Share.prototype.close = function() {
16363    return this["public"]("close");
16364  };
16365
16366  Share.prototype.toggle = function() {
16367    return this["public"]("toggle");
16368  };
16369
16370  Share.prototype["public"] = function(action) {
16371    var button, index, instance, _i, _len, _ref, _results;
16372    _ref = document.querySelectorAll(this.element);
16373    _results = [];
16374    for (index = _i = 0, _len = _ref.length; _i < _len; index = ++_i) {
16375      instance = _ref[index];
16376      button = instance.getElementsByClassName("social")[0];
16377      _results.push(this["event_" + action](button));
16378    }
16379    return _results;
16380  };
16381
16382  Share.prototype.network_facebook = function() {
16383    if (this.config.networks.facebook.load_sdk) {
16384      if (!window.FB) {
16385        return console.error("The Facebook JS SDK hasn't loaded yet.");
16386      }
16387      return FB.ui({
16388        method: 'feed',
16389        name: this.config.networks.facebook.title,
16390        link: this.config.networks.facebook.url,
16391        picture: this.config.networks.facebook.image,
16392        caption: this.config.networks.facebook.caption,
16393        description: this.config.networks.facebook.description
16394      });
16395    } else {
16396      return this.popup('https://www.facebook.com/sharer/sharer.php', {
16397        u: this.config.networks.facebook.url
16398      });
16399    }
16400  };
16401
16402  Share.prototype.network_twitter = function() {
16403    return this.popup('https://twitter.com/intent/tweet', {
16404      text: this.config.networks.twitter.title,
16405      url: this.config.networks.twitter.url
16406    });
16407  };
16408
16409  Share.prototype.network_google_plus = function() {
16410    return this.popup('https://plus.google.com/share', {
16411      url: this.config.networks.google_plus.url
16412    });
16413  };
16414
16415  Share.prototype.network_pinterest = function() {
16416    return this.popup('https://www.pinterest.com/pin/create/button', {
16417      url: this.config.networks.pinterest.url,
16418      media: this.config.networks.pinterest.image,
16419      description: this.config.networks.pinterest.description
16420    });
16421  };
16422
16423  Share.prototype.network_linkedin = function() {
16424    return this.popup('https://www.linkedin.com/shareArticle', {
16425      url: this.config.networks.linkedin.url,
16426      title: this.config.networks.linkedin.title,
16427      summary: this.config.networks.linkedin.description
16428    });
16429  }
16430
16431  Share.prototype.network_xing = function() {
16432    return this.popup('https://www.xing.com/spi/shares/new', {
16433      url: this.config.networks.xing.url,
16434      image: this.config.networks.xing.image,
16435      title: this.config.networks.xing.title,
16436      summary: this.config.networks.xing.description
16437    });
16438  }
16439
16440  Share.prototype.network_email = function() {
16441    return this.popup('mailto:', {
16442      subject: this.config.networks.email.title,
16443      body: this.config.networks.email.url + '%0A%0A' + this.config.networks.email.description,
16444    });
16445  };
16446
16447  Share.prototype.inject_icons = function() {
16448  //  return this.inject_stylesheet("https://www.sharebutton.co/fonts/v2/entypo.min.css");
16449  };
16450
16451  Share.prototype.inject_fonts = function() {
16452   // return this.inject_stylesheet("http://fonts.googleapis.com/css?family=Lato:900&text=" + this.config.ui.button_text);
16453  };
16454
16455  Share.prototype.inject_stylesheet = function(url) {
16456    var link;
16457    if (!this.el.head.querySelector("link[href=\"" + url + "\"]")) {
16458      link = document.createElement("link");
16459      link.setAttribute("rel", "stylesheet");
16460      link.setAttribute("href", url);
16461      return this.el.head.appendChild(link);
16462    }
16463  };
16464
16465  Share.prototype.inject_css = function(instance) {
16466    var css, meta, selector, style;
16467    selector = "." + (instance.getAttribute('class').split(" ").join("."));
16468    if (!this.el.head.querySelector("meta[name='sharer" + selector + "']")) {
16469      this.config.selector = selector;
16470      css = getStyles(this.config);
16471      style = document.createElement("style");
16472      style.type = "text/css";
16473      if (style.styleSheet) {
16474        style.styleSheet.cssText = css;
16475      } else {
16476        style.appendChild(document.createTextNode(css));
16477      }
16478      this.el.head.appendChild(style);
16479      delete this.config.selector;
16480      meta = document.createElement("meta");
16481      meta.setAttribute("name", "sharer" + selector);
16482      return this.el.head.appendChild(meta);
16483    }
16484  };
16485
16486  Share.prototype.inject_html = function(instance) {
16487    //return instance.innerHTML = "<label class='social-export'><span>" + this.config.ui.button_text + "</span></label><div class='social load " + this.config.ui.flyout + "'><ul><li class='social-facebook' data-network='facebook' tabindex='0'></li><li class='social-twitter' data-network='twitter' tabindex='0'></li><li class='social-gplus' data-network='google_plus' tabindex='0'></li><li class='social-pinterest' data-network='pinterest' tabindex='0'></li><li class='social-linkedin' data-network='linkedin' tabindex='0'></li><li class='social-xing' data-network='xing' tabindex='0'></li><li class='social-paper-plane' data-network='email' tabindex='0'></li></ul></div>";
16488    return instance.innerHTML = "<label class='social-export'><span>" + this.config.ui.button_text + "</span></label><div class='social load " + this.config.ui.flyout + "'><ul><li class='social-facebook' data-network='facebook' tabindex='0'></li><li class='social-twitter' data-network='twitter' tabindex='0'></li><li class='social-pinterest' data-network='pinterest' tabindex='0'></li><li class='social-linkedin' data-network='linkedin' tabindex='0'></li><li class='social-xing' data-network='xing' tabindex='0'></li><li class='social-paper-plane' data-network='email' tabindex='0'></li></ul></div>";
16489  };
16490
16491  Share.prototype.inject_facebook_sdk = function() {
16492    var fb_root, script;
16493    if (!window.FB && this.config.networks.facebook.app_id && !this.el.body.querySelector('#fb-root')) {
16494      script = document.createElement("script");
16495      script.text = "window.fbAsyncInit=function(){FB.init({appId:'" + this.config.networks.facebook.app_id + "',status:true,xfbml:true})};(function(e,t,n){var r,i=e.getElementsByTagName(t)[0];if(e.getElementById(n)){return}r=e.createElement(t);r.id=n;r.src='" + this.config.protocol + "connect.facebook.net/en_US/all.js';i.parentNode.insertBefore(r,i)})(document,'script','facebook-jssdk')";
16496      fb_root = document.createElement("div");
16497      fb_root.id = "fb-root";
16498      this.el.body.appendChild(fb_root);
16499      return this.el.body.appendChild(script);
16500    }
16501  };
16502
16503  Share.prototype.hook = function(type, network, instance) {
16504    var fn, opts;
16505    fn = this.config.networks[network][type];
16506    if (typeof fn === "function") {
16507      opts = fn.call(this.config.networks[network], instance);
16508      if (opts !== void 0) {
16509        opts = this.normalize_filter_config_updates(opts);
16510        this.extend(this.config.networks[network], opts, true);
16511        this.normalize_network_configuration();
16512      }
16513    }
16514  };
16515
16516  Share.prototype.default_title = function() {
16517    var content;
16518    if (content = document.querySelector('meta[property="og:title"]') || document.querySelector('meta[name="twitter:title"]')) {
16519      return encodeURIComponent(content.getAttribute('content'));
16520    } else if (content = document.querySelector('title')) {
16521      return encodeURIComponent(content.innerText);
16522    }
16523  };
16524
16525  Share.prototype.default_image = function() {
16526    var content;
16527    if (content = document.querySelector('meta[property="og:image"]') || document.querySelector('meta[name="twitter:image"]')) {
16528      return content.getAttribute('content');
16529    }
16530  };
16531
16532  Share.prototype.default_description = function() {
16533    var content;
16534    if (content = document.querySelector('meta[property="og:description"]') || document.querySelector('meta[name="twitter:description"]') || document.querySelector('meta[name="description"]')) {
16535      return encodeURIComponent(content.getAttribute('content'));
16536    } else {
16537      return '';
16538    }
16539  };
16540
16541  Share.prototype.set_global_configuration = function() {
16542    var display, network, option, options, _ref, _results;
16543    _ref = this.config.networks;
16544    _results = [];
16545    for (network in _ref) {
16546      options = _ref[network];
16547      for (option in options) {
16548        if (this.config.networks[network][option] == null) {
16549          this.config.networks[network][option] = this.config[option];
16550        }
16551      }
16552      if (this.config.networks[network].enabled) {
16553        display = 'block';
16554        this.config.enabled_networks += 1;
16555      } else {
16556        display = 'none';
16557      }
16558      _results.push(this.config.networks[network].display = display);
16559    }
16560    return _results;
16561  };
16562
16563  Share.prototype.normalize_network_configuration = function() {
16564    if (!this.config.networks.facebook.app_id) {
16565      this.config.networks.facebook.load_sdk = false;
16566    }
16567    if (!this.is_encoded(this.config.networks.twitter.description)) {
16568      this.config.networks.twitter.description = encodeURIComponent(this.config.networks.twitter.description);
16569    }
16570    if (typeof this.config.networks.facebook.app_id === 'number') {
16571      return this.config.networks.facebook.app_id = this.config.networks.facebook.app_id.toString();
16572    }
16573  };
16574
16575  Share.prototype.normalize_filter_config_updates = function(opts) {
16576    if (this.config.networks.facebook.app_id !== opts.app_id) {
16577      console.warn("You are unable to change the Facebook app_id after the button has been initialized. Please-in-out update your Facebook filters accordingly.");
16578      delete opts.app_id;
16579    }
16580    if (this.config.networks.facebook.load_sdk !== opts.load_sdk) {
16581      console.warn("You are unable to change the Facebook load_sdk option after the button has been initialized. Please-in-out update your Facebook filters accordingly.");
16582      delete opts.app_id;
16583    }
16584    return opts;
16585  };
16586
16587  return Share;
16588
16589})(ShareUtils);
16590 return Share;
16591});
16592
16593// Generated by CoffeeScript 1.9.2
16594
16595// @license Sticky-kit v1.1.2 | WTFPL | Leaf Corcoran 2015 | http://leafo.net
16596
16597(function() {
16598  var $, win;
16599
16600  $ = this.jQuery || window.jQuery;
16601
16602  win = $(window);
16603
16604  $.fn.stick_in_parent = function(opts) {
16605    var doc, elm, enable_bottoming, fn, i, inner_scrolling, len, manual_spacer, offset_top, outer_width, parent_selector, recalc_every, sticky_class;
16606    if (opts == null) {
16607      opts = {};
16608    }
16609    sticky_class = opts.sticky_class, inner_scrolling = opts.inner_scrolling, recalc_every = opts.recalc_every, parent_selector = opts.parent, offset_top = opts.offset_top, manual_spacer = opts.spacer, enable_bottoming = opts.bottoming;
16610    if (offset_top == null) {
16611      offset_top = 0;
16612    }
16613    if (parent_selector == null) {
16614      parent_selector = void 0;
16615    }
16616    if (inner_scrolling == null) {
16617      inner_scrolling = true;
16618    }
16619    if (sticky_class == null) {
16620      sticky_class = "is_stuck";
16621    }
16622    doc = $(document);
16623    if (enable_bottoming == null) {
16624      enable_bottoming = true;
16625    }
16626    outer_width = function(el) {
16627      var _el, computed, w;
16628      if (window.getComputedStyle) {
16629        _el = el[0];
16630        computed = window.getComputedStyle(el[0]);
16631        w = parseFloat(computed.getPropertyValue("width")) + parseFloat(computed.getPropertyValue("margin-left")) + parseFloat(computed.getPropertyValue("margin-right"));
16632        if (computed.getPropertyValue("box-sizing") !== "border-box") {
16633          w += parseFloat(computed.getPropertyValue("border-left-width")) + parseFloat(computed.getPropertyValue("border-right-width")) + parseFloat(computed.getPropertyValue("padding-left")) + parseFloat(computed.getPropertyValue("padding-right"));
16634        }
16635        return w;
16636      } else {
16637        return el.outerWidth(true);
16638      }
16639    };
16640    fn = function(elm, padding_bottom, parent_top, parent_height, top, height, el_float, detached) {
16641      var bottomed, detach, fixed, last_pos, last_scroll_height, offset, parent, recalc, recalc_and_tick, recalc_counter, spacer, tick;
16642      if (elm.data("sticky_kit")) {
16643        return;
16644      }
16645      elm.data("sticky_kit", true);
16646      last_scroll_height = doc.height();
16647      parent = elm.parent();
16648      if (parent_selector != null) {
16649        parent = parent.closest(parent_selector);
16650      }
16651      if (!parent.length) {
16652        throw "failed to find stick parent";
16653      }
16654      fixed = false;
16655      bottomed = false;
16656      spacer = manual_spacer != null ? manual_spacer && elm.closest(manual_spacer) : $("<div />");
16657      if (spacer) {
16658        spacer.css('position', elm.css('position'));
16659      }
16660      recalc = function() {
16661        var border_top, padding_top, restore;
16662        if (detached) {
16663          return;
16664        }
16665        last_scroll_height = doc.height();
16666        border_top = parseInt(parent.css("border-top-width"), 10);
16667        padding_top = parseInt(parent.css("padding-top"), 10);
16668        padding_bottom = parseInt(parent.css("padding-bottom"), 10);
16669        parent_top = parent.offset().top + border_top + padding_top;
16670        parent_height = parent.height();
16671        if (fixed) {
16672          fixed = false;
vendor: 6,068 bytes, lines 16673-16867
16673          bottomed = false;
16674          if (manual_spacer == null) {
16675            elm.insertAfter(spacer);
16676            spacer.detach();
16677          }
16678          elm.css({
16679            position: "",
16680            top: "",
16681            width: "",
16682            bottom: ""
16683          }).removeClass(sticky_class);
16684          restore = true;
16685        }
16686        top = elm.offset().top - (parseInt(elm.css("margin-top"), 10) || 0) - offset_top;
16687        height = elm.outerHeight(true);
16688        el_float = elm.css("float");
16689        if (spacer) {
16690          spacer.css({
16691            width: outer_width(elm),
16692            height: height,
16693            display: elm.css("display"),
16694            "vertical-align": elm.css("vertical-align"),
16695            "float": el_float
16696          });
16697        }
16698        if (restore) {
16699          return tick();
16700        }
16701      };
16702      recalc();
16703      if (height === parent_height) {
16704        return;
16705      }
16706      last_pos = void 0;
16707      offset = offset_top;
16708      recalc_counter = recalc_every;
16709      tick = function() {
16710        var css, delta, recalced, scroll, will_bottom, win_height;
16711        if (detached) {
16712          return;
16713        }
16714        recalced = false;
16715        if (recalc_counter != null) {
16716          recalc_counter -= 1;
16717          if (recalc_counter <= 0) {
16718            recalc_counter = recalc_every;
16719            recalc();
16720            recalced = true;
16721          }
16722        }
16723        if (!recalced && doc.height() !== last_scroll_height) {
16724          recalc();
16725          recalced = true;
16726        }
16727        scroll = win.scrollTop();
16728        if (last_pos != null) {
16729          delta = scroll - last_pos;
16730        }
16731        last_pos = scroll;
16732        if (fixed) {
16733          if (enable_bottoming) {
16734            will_bottom = scroll + height + offset > parent_height + parent_top;
16735            if (bottomed && !will_bottom) {
16736              bottomed = false;
16737              elm.css({
16738                position: "fixed",
16739                bottom: "",
16740                top: offset
16741              }).trigger("sticky_kit:unbottom");
16742            }
16743          }
16744          if (scroll < top) {
16745            fixed = false;
16746            offset = offset_top;
16747            if (manual_spacer == null) {
16748              if (el_float === "left" || el_float === "right") {
16749                elm.insertAfter(spacer);
16750              }
16751              spacer.detach();
16752            }
16753            css = {
16754              position: "",
16755              width: "",
16756              top: ""
16757            };
16758            elm.css(css).removeClass(sticky_class).trigger("sticky_kit:unstick");
16759          }
16760          if (inner_scrolling) {
16761            win_height = win.height();
16762            if (height + offset_top > win_height) {
16763              if (!bottomed) {
16764                offset -= delta;
16765                offset = Math.max(win_height - height, offset);
16766                offset = Math.min(offset_top, offset);
16767                if (fixed) {
16768                  elm.css({
16769                    top: offset + "px"
16770                  });
16771                }
16772              }
16773            }
16774          }
16775        } else {
16776          if (scroll > top) {
16777            fixed = true;
16778            css = {
16779              position: "fixed",
16780              top: offset
16781            };
16782            css.width = elm.css("box-sizing") === "border-box" ? elm.outerWidth() + "px" : elm.width() + "px";
16783            elm.css(css).addClass(sticky_class);
16784            if (manual_spacer == null) {
16785              elm.after(spacer);
16786              if (el_float === "left" || el_float === "right") {
16787                spacer.append(elm);
16788              }
16789            }
16790            elm.trigger("sticky_kit:stick");
16791          }
16792        }
16793        if (fixed && enable_bottoming) {
16794          if (will_bottom == null) {
16795            will_bottom = scroll + height + offset > parent_height + parent_top;
16796          }
16797          if (!bottomed && will_bottom) {
16798            bottomed = true;
16799            if (parent.css("position") === "static") {
16800              parent.css({
16801                position: "relative"
16802              });
16803            }
16804            return elm.css({
16805              position: "absolute",
16806              bottom: padding_bottom,
16807              top: "auto"
16808            }).trigger("sticky_kit:bottom");
16809          }
16810        }
16811      };
16812      recalc_and_tick = function() {
16813        recalc();
16814        return tick();
16815      };
16816      detach = function() {
16817        detached = true;
16818        win.off("touchmove", tick);
16819        win.off("scroll", tick);
16820        win.off("resize", recalc_and_tick);
16821        $(document.body).off("sticky_kit:recalc", recalc_and_tick);
16822        elm.off("sticky_kit:detach", detach);
16823        elm.removeData("sticky_kit");
16824        elm.css({
16825          position: "",
16826          bottom: "",
16827          top: "",
16828          width: ""
16829        });
16830        parent.position("position", "");
16831        if (fixed) {
16832          if (manual_spacer == null) {
16833            if (el_float === "left" || el_float === "right") {
16834              elm.insertAfter(spacer);
16835            }
16836            spacer.remove();
16837          }
16838          return elm.removeClass(sticky_class);
16839        }
16840      };
16841      win.on("touchmove", tick);
16842      win.on("scroll", tick);
16843      win.on("resize", recalc_and_tick);
16844      $(document.body).on("sticky_kit:recalc", recalc_and_tick);
16845      elm.on("sticky_kit:detach", detach);
16846      return setTimeout(tick, 0);
16847    };
16848    for (i = 0, len = this.length; i < len; i++) {
16849      elm = this[i];
16850      fn($(elm));
16851    }
16852    return this;
16853  };
16854
16855}).call(this);
16856/* ==========================================================================
16857 * bootstrap-tab-history.js
16858 * Author: Michael Narayan <[email protected]>
16859 * Repository: https://github.com/mnarayan01/bootstrap-tab-history/
16860 * ==========================================================================
16861 * Licensed under the Apache License, Version 2.0 (the "License"); you may
16862 * not use this file except in compliance with the License. You may obtain a
16863 * copy of the License at
16864 *
16865 * http://www.apache.org/licenses/LICENSE-2.0
16866 *
16867 * Unless required by applicable law or agreed to in writing, softw
16867are
16868 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16869 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16870 * License for the specific language governing permissions and limitations
16871 * under the License.
16872 * ========================================================================== */
16873
16874/* ========================================================================== */
16875/* JSHint directives                                                          */
16876/*                                                                            */
16877/* global BootstrapTabHistory: true                                           */
16878/*                                                                            */
16879/* global document                                                            */
16880/* global jQuery                                                              */
16881/* global history                                                             */
16882/* global window                                                              */
16883/* ========================================================================== */
16884
16885/**
16886 * Integrate [HTML5 history state tracking](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history)
16887 * with [`bootstrap/tab.js`](http://getbootstrap.com/javascript/#tabs). To enable tracking on a tab element, simply set
16888 * the `data-tab-history` attribute to true (or a string denoting a tab grouping).
16889 *
16890 * See the README for additional information.
16891 *
16892 * Functionality based upon bootstrap/tab.js v3.1.0; reference it when making any changes.
16893 */
16894
16895BootstrapTabHistory = {
16896  options: {
16897    /**
16898     * When the anchor portion of the URI is used to activate a tab, scroll down to the given offset, rather than the
16899     * element with the given `id` attribute. Set to null to disable. Only relevant if showTabsBasedOnAnchor is true.
16900     *
16901     * May be overriden on a per-element basis by the attribute `data-tab-history-anchor-y-offset`.
16902     *
16903     * @public
16904     * @type {?number}
16905     */
16906    defaultAnchorYOffset: 0,
16907    /**
16908     * Either 'push' or 'replace', for whether to use `history.pushState` or `history.replaceState`, resp.
16909     *
16910     * May be overriden on a per-element basis by the attribute `data-tab-history-changer`.
16911     *
16912     * @public
16913     * @type {string}
16914     */
16915    defaultChanger: 'replace',
16916    /**
16917     * If true, update the URL in onShownTab in the calls to `history.pushState` and `history.replaceState`. Otherwise,
16918     * `null` is passed as the third parameter to these calls.
16919     *
16920     * May be overriden on a per-element basis by the attribute `data-tab-history-update-url`.
16921     *
16922     * @public
16923     * @type {boolean}
16924     */
16925    defaultUpdateURL: false,
16926    /**
16927     * Should the anchor portion of the loaded URI be used to activate a single tab if no history was present on page
16928     * load.
16929     *
16930     * @public
16931     * @type {boolean}
16932     */
16933    showTabsBasedOnAnchor: true
16934  }
16935};
16936
16937(function () {
16938  'use strict';
16939
16940  jQuery(function () {
16941    if(history && history.pushState && history.replaceState) {
16942      var bootstrapTabHistory = history.state && history.state.bootstrapTabHistory;
16943
16944      if(bootstrapTabHistory) {
16945        showTabsBasedOnState(bootstrapTabHistory);
16946      } else {
16947        showTabsBasedOnAnchor();
16948      }
16949
16950      backfillHistoryState();
16951
16952      jQuery(document).on('shown.bs.tab show.bs.collapse', onShownTab);
16953      jQuery(window).on('popstate', onPopState);
16954    } else {
16955      showTabsBasedOnAnchor();
16956    }
16957  });
16958
16959  /**
16960   * Used to prevent onShownTab from registering shown events that we triggered via showTabsBasedOnState.
16961   *
16962   * @type {boolean}
16963   */
16964  var showingTabsBasedOnState = false;
16965
16966  /**
16967   * Used to update `history.state` to reflect the default active tabs on initial page load. This supports proper
16968   * `history.back` handling when `data-tab-history-update-url` is true.
16969   */
16970  function backfillHistoryState() {
16971    var newState = null;
16972
16973    jQuery('li.active > [data-tab-history], .panel-title.active [data-tab-history]').each(function () {//edited by Uncode
16974      var $activeTabElement = jQuery(this);
16975      var selector = getTabSelector($activeTabElement);
16976
16977      if(selector) {
16978        var tabGroup = getTabGroup($activeTabElement);
16979
16980        if(tabGroup) {
16981          newState = createNewHistoryState(newState || history.state, tabGroup, selector);
16982        }
16983      }
16984    });
16985
16986    if(newState) {
16987      history.replaceState(newState, '', null);
16988    }
16989  }
16990
16991  /**
16992   * Clone the existing state, ensure its bootstrapTabHistory attribute is an Object, and add the provided tabGroup to
16993   * said bootstrapTabHistory attribute.
16994   *
16995   * @param {?object} existingState
16996   * @param {!string} tabGroup
16997   * @param {!string} selector
16998   * @returns {!object}
16999   */
17000  function createNewHistoryState(existingState, tabGroup, selector) {
17001    // Clone history.state and ensure it has a bootstrapTabHistory entry.
17002    var newState = jQuery.extend(true, {}, existingState, {
17003      bootstrapTabHistory: {}
17004    });
17005
17006    newState.bootstrapTabHistory[tabGroup] = selector;
17007
17008    return newState;
17009  }
17010
17011  /**
17012   * @param {jQuery} $tab
17013   * @returns {?string}
17014   */
17015  function getTabGroup($tab) {
17016    return parseTruthyAttributeValue($tab.data('tab-history'));
17017  }
17018
17019  /**
17020   * @param {jQuery} $tab
17021   * @returns {?string}
17022   */
17023  function getTabSelector($tab) {
17024    return $tab.data('target') || $tab.attr('href');
17025  }
17026
17027  /**
17028   * Receives the `shown.bs.tab` event. Updates `history.state` as appropriate.
17029   *
17030   * @param {jQuery.Event} shownEvt
17031   */
17032  function onShownTab(shownEvt) {
17033    if(!showingTabsBasedOnState) {
17034      var $activatedTab = jQuery(shownEvt.target);
17035      if ( $activatedTab.hasClass('panel-collapse') )
17036        $activatedTab = $activatedTab.closest('.panel').find('a');
17037      var selector = getTabSelector($activatedTab);
17038
17039      if(selector) {
17040        var tabGroup = getTabGroup($activatedTab);
17041
17042        if(tabGroup) {
17043          var historyChanger = $activatedTab.data('tab-history-changer') || BootstrapTabHistory.options.defaultChanger;
17044          var newState = createNewHistoryState(history.state, tabGroup, selector);
17045          var updateURL = (function ($activatedTab) {
17046            if(selector[0] === '#') {
17047              var elementUpdateURLOption = parseTruthyAttributeValue($activatedTab.data('tab-history-update-url'));
17048
17049              if(elementUpdateURLOption === undefined) {
17050                return BootstrapTabHistory.options.defaultUpdateURL;
17051              } else {
17052                return elementUpdateURLOption;
17053              }
17054            } else {
17055              return false;
17056            }
17057          })($activatedTab);
17058
17059          switch(historyChanger) {
17060            case 'push':
17061              history.pushState(newState, '', updateURL ? selector : null);
17062              break;
17063            case 'replace':
17064              history.replaceState(newState, '', updateURL ? selector : null);
17065              break;
17066            default:
17067              throw new Error('Unknown tab-history-changer: ' + historyChanger);
17068          }
17069        }
17070      }
17071    }
17072  }
17073
17074  /**
17075   * Receives the `popstate` event. Shows tabs based on the value of `history.state` as appropriate.
17076   */
17077  function onPopState() {
17078    var bootstrapTabHistory = history.state && history.state.bootstrapTabHistory;
17079
17080    if(bootstrapTabHistory) {
17081      showTabsBasedOnState(bootstrapTabHistory);
17082    }
17083  }
17084
17085  /**
17086   * Returns the given value, _unless_ that value is an empty string, in which case `true` is returned.
17087   *
17088   * Rationale: HAML data attributes which are set to `true` are rendered as a blank string.
17089   *
17090   * @param {*} value
17091   * @returns {*}
17092   */
17093  function parseTruthyAttributeValue(value) {
17094    if(value) {
17095      return value;
17096    } else if(value === '') {
17097      return true;
17098    } else {
17099      return value;
17100    }
17101  }
17102
17103  /**
17104   * Show tabs based upon the anchor component of `window.location`.
17105   */
17106  function showTabsBasedOnAnchor() {
17107    if(BootstrapTabHistory.options.showTabsBasedOnAnchor) {
17108      var anchor = window.location && window.location.hash;
17109
17110      if(anchor) {
17111        var $tabElement = showTabForSelector(anchor);
17112
17113        if($tabElement && window.addEventListener && window.removeEventListener) {
17114          var anchorYOffset = (function ($tabElement) {
17115            var elementSetting = $tabElement.data('tab-history-anchor-y-offset');
17116
17117            if(elementSetting === undefined) {
17118              return BootstrapTabHistory.options.defaultAnchorYOffset;
17119            } else {
17120              return elementSetting;
17121            }
17122          })($tabElement);
17123
17124          // HACK: This prevents scrolling to the tab on page load. This relies on the fact that we should never get
17125          //   here on `history.forward`, `history.back`, or `location.reload`, since in all those situations the
17126          //   `history.state` object should have been used (unless the browser did not support the modern History API).
17127          if(anchorYOffset || anchorYOffset === 0) {
17128            var scrollListener = function resetAnchorScroll () {
17129              window.removeEventListener('scroll', scrollListener);
17130              window.scrollTo(0, anchorYOffset);
17131            };
17132
17133            window.addEventListener('scroll', scrollListener);
17134          }
17135        }
17136      }
17137    }
17138  }
17139
17140  /**
17141   * Show a tab which corresponds to the provided selector.
17142   *
17143   * @param {string} selector - A CSS selector.
17144   * @returns {?jQuery} - The tab which was found to show (even if said tab was already active).
17145   */
17146  function showTabForSelector(selector) {
17147    var $tabElement = (function (selector) {
17148      var $ret = null;
17149
17150      jQuery('[data-toggle="tab"], [data-toggle="pill"], [data-toggle="collapse"]').each(function () {
17151        var $potentialTab = jQuery(this);
17152
17153        if(($potentialTab.attr('href') === selector || $potentialTab.data('target') === selector ) && getTabGroup($potentialTab)) {
17154          $ret = $potentialTab;
17155          return false;
vendor: 4,522 bytes, lines 17156-17305
17156        } else {
17157         return null;
17158        }
17159      });
17160
17161      return $ret;
17162    })(selector);
17163
17164    if($tabElement) {
17165      $tabElement.trigger('click');
17166      //$tabElement.tab('show');
17167    }
17168
17169    return $tabElement;
17170  }
17171
17172  /**
17173   * Iterate through the provided set of tab tab groups, showing the tabs based on the stored selectors.
17174   *
17175   * @param {object} bootstrapTabHistory - Each of the values is passed to showTabForSelector; the keys are actually irrelevant.
17176   */
17177  function showTabsBasedOnState(bootstrapTabHistory) {
17178    showingTabsBasedOnState = true;
17179
17180    try {
17181      for(var k in bootstrapTabHistory) {
17182        if(bootstrapTabHistory.hasOwnProperty(k)) {
17183          showTabForSelector(bootstrapTabHistory[k]);
17184        }
17185      }
17186    } finally {
17187      showingTabsBasedOnState = false;
17188    }
17189  }
17190})();
17191
17192/*!
17193 * Justified Gallery - v3.6.3
17194 * http://miromannino.github.io/Justified-Gallery/
17195 * Copyright (c) 2016 Miro Mannino
17196 * Licensed under the MIT license.
17197 */
17198(function($) {
17199
17200  function hasScrollBar() {
17201    return $("body").height() > $(window).height();
17202  }
17203  /**
17204   * Justified Gallery controller constructor
17205   *
17206   * @param $gallery the gallery to build
17207   * @param settings the settings (the defaults are in $.fn.justifiedGallery.defaults)
17208   * @constructor
17209   */
17210  var JustifiedGallery = function ($gallery, settings) {
17211    
17212    this.settings = settings;
17213    this.checkSettings();
17214
17215    this.imgAnalyzerTimeout = null;
17216    this.entries = null;
17217    this.buildingRow = {
17218      entriesBuff : [],
17219      width : 0,
17220      height : 0,
17221      aspectRatio : 0
17222    };
17223    this.lastFetchedEntry = null;
17224    this.lastAnalyzedIndex = -1;
17225    this.yield = {
17226      every : 2, // do a flush every n flushes (must be greater than 1)
17227      flushed : 0 // flushed rows without a yield
17228    };
17229    this.border = settings.border >= 0 ? settings.border : settings.margins;
17230    this.maxRowHeight = this.retrieveMaxRowHeight();
17231    this.suffixRanges = this.retrieveSuffixRanges();
17232    this.offY = this.border;
17233    this.rows = 0;
17234    this.spinner = {
17235      phase : 0,
17236      timeSlot : 150,
17237      $el : $('<div class="spinner"><span></span><span></span><span></span></div>'),
17238      intervalId : null
17239    };
17240    this.checkWidthIntervalId = null;
17241    this.galleryWidth = $gallery.width();
17242    this.$gallery = $gallery;
17243
17244  };
17245
17246  /** @returns {String} the best suffix given the width and the height */
17247  JustifiedGallery.prototype.getSuffix = function (width, height) {
17248    var longestSide, i;
17249    longestSide = (width > height) ? width : height;
17250    for (i = 0; i < this.suffixRanges.length; i++) {
17251      if (longestSide <= this.suffixRanges[i]) {
17252        return this.settings.sizeRangeSuffixes[this.suffixRanges[i]];
17253      }
17254    }
17255    return this.settings.sizeRangeSuffixes[this.suffixRanges[i - 1]];
17256  };
17257
17258  /**
17259   * Remove the suffix from the string
17260   *
17261   * @returns {string} a new string without the suffix
17262   */
17263  JustifiedGallery.prototype.removeSuffix = function (str, suffix) {
17264    return str.substring(0, str.length - suffix.length);
17265  };
17266
17267  /**
17268   * @returns {boolean} a boolean to say if the suffix is contained in the str or not
17269   */
17270  JustifiedGallery.prototype.endsWith = function (str, suffix) {
17271    return str.indexOf(suffix, str.length - suffix.length) !== -1;
17272  };
17273
17274  /**
17275   * Get the used suffix of a particular url
17276   *
17277   * @param str
17278   * @returns {String} return the used suffix
17279   */
17280  JustifiedGallery.prototype.getUsedSuffix = function (str) {
17281    for (var si in this.settings.sizeRangeSuffixes) {
17282      if (this.settings.sizeRangeSuffixes.hasOwnProperty(si)) {
17283        if (this.settings.sizeRangeSuffixes[si].length === 0) continue;
17284        if (this.endsWith(str, this.settings.sizeRangeSuffixes[si])) return this.settings.sizeRangeSuffixes[si];
17285      }
17286    }
17287    return '';
17288  };
17289
17290  /**
17291   * Given an image src, with the width and the height, returns the new image src with the
17292   * best suffix to show the best quality thumbnail.
17293   *
17294   * @returns {String} the suffix to use
17295   */
17296  JustifiedGallery.prototype.newSrc = function (imageSrc, imgWidth, imgHeight, image) {
17297    var newImageSrc;
17298
17299    if (this.settings.thumbnailPath) {
17300      newImageSrc = this.settings.thumbnailPath(imageSrc, imgWidth, imgHeight, image);
17301    } else {
17302      var matchRes = imageSrc.match(this.settings.extension);
17303      var ext = (matchRes !== null) ? matchRes[0] : '';
17304      newImageSrc = imageSrc.replace(this.settings.extension, '');
17305      newImageSrc = this.removeSuffix(newImageSrc, this.getUsedSuffix(newImageSrc));
17306      newImageSrc += this.getSuffix(imgWidth, imgHeight) + ext;
17307    }
17308
17309    return newImageSrc;
17310  };
17311
17312  /**
17313   * Shows the images that is in the given entry
17314   *
17315   * @param $entry the entry
17316   * @param callback the callback that is called when the show animation is finished
17317   */
17318  JustifiedGallery.prototype.showImg = function ($entry, callback) {
17319    if (this.settings.cssAnimation) {
17320      $entry.addClass('entry-visible');
17321      if (callback) callback();
17322    } else {
17323      $entry.stop().fadeTo(this.settings.imagesAnimationDuration, 1.0, callback);
17324      $entry.find('> img, > a > img').stop().fadeTo(this.settings.imagesAnimationDuration, 1.0, callback);
17325    }
17326  };
17327
17328  /**
17329   * Extract the image src form the image, looking from the 'safe-src', and if it can't be found, from the
17330   * 'src' attribute. It saves in the image data the 'jg.originalSrc' field, with the extracted src.
17331   *
17332   * @param $image the image to analyze
17333   * @returns {String} the extracted src
17334   */
17335  JustifiedGallery.prototype.extractImgSrcFromImage = function ($image) {
17336    var imageSrc = (typeof $image.data('safe-src') !== 'undefined') ? $image.data('safe-src') : $image.attr('src');
17337    $image.data('jg.originalSrc', imageSrc);
17338    return imageSrc;
17339  };
17340
17341  /** @returns {jQuery} the image in the given entry */
17342  JustifiedGallery.prototype.imgFromEntry = function ($entry) {
17343    var $img = $entry.find('> img');
17344    if ($img.length === 0) $img = $entry.find('> a > img');
17345    if ($img.length === 0) $img = $entry.find('.t-entry-visual-cont img');//hacked by Uncode
17346    return $img.length === 0 ? null : $img;
17347  };
17348
17349  /** @returns {jQuery} the caption in the given entry */
17350  JustifiedGallery.prototype.captionFromEntry = function ($entry) {
17351    var $caption = $entry.find('> .caption');
17352    return $caption.length === 0 ? null : $caption;
17353  };
17354
17355  /**
17356   * Display the entry
17357   *
17358   * @param {jQuery} $entry the entry to display
17359   * @param {int} x the x position where the entry must be positioned
17360   * @param y the y position where the entry must be positioned
17361   * @param imgWidth the image width
17362   * @param imgHeight the image height
17363   * @param rowHeight the row height of the row that owns the entry
17364   */
17365  JustifiedGallery.prototype.displayEntry = function ($entry, x, y, imgWidth, imgHeight, rowHeight) {
17366    $entry.width(imgWidth);
17367    $entry.height(Math.floor(rowHeight));//hacked by Uncode
17368    $entry.css('top', Math.floor(y));//hacked by Uncode
17369    $entry.css('left', x);
17370
17371    var $image = this.imgFromEntry($entry);
17372    if ($image !== null) {
17373      $image.css('width', imgWidth);
17374      $image.css('height', imgHeight);
17375      $image.css('margin-left', - imgWidth / 2);
17376      $image.css('margin-top', - imgHeight / 2);
17377
17378      // Image reloading for an high quality of thumbnails
17379      var imageSrc = $image.attr('src');
17380      var newImageSrc = this.newSrc(imageSrc, imgWidth, imgHeight, $image[0]);
17381
17382      $image.one('error', function () {
17383        $image.attr('src', $image.data('jg.originalSrc')); //revert to the original thumbnail, we got it.
17384      });
17385
17386      var loadNewImage = function () {
17387        if (imageSrc !== newImageSrc) { //load the new image after the fadeIn
17388          $image.attr('src', newImageSrc);
17389        }
17390      };
17391
17392      if ($entry.data('jg.loaded') === 'skipped') {
17393        this.onImageEvent(imageSrc, $.proxy(function() {
17394          this.showImg($entry, loadNewImage);
17395          $entry.data('jg.loaded', true);
17396        }, this));
17397      } else {
17398        this.showImg($entry, loadNewImage);
17399      }
17400
17401    } else {
17402      this.showImg($entry);
17403    }
17404
17405    this.displayEntryCaption($entry);
17406  };
17407
17408  /**
17409   * Display the entry caption. If the caption element doesn't exists, it creates the caption using the 'alt'
17410   * or the 'title' attributes.
17411   *
17412   * @param {jQuery} $entry the entry to process
17413   */
17414  JustifiedGallery.prototype.displayEntryCaption = function ($entry) {
17415    var $image = this.imgFromEntry($entry);
17416    if ($image !== null && this.settings.captions) {
17417      var $imgCaption = this.captionFromEntry($entry);
17418
17419      // Create it if it doesn't exists
17420      if ($imgCaption === null) {
17421        var caption = $image.attr('alt');
17422        if (!this.isValidCaption(caption)) caption = $entry.attr('title');
17423        if (this.isValidCaption(caption)) { // Create only we found something
17424          $imgCaption = $('<div class="caption">' + caption + '</div>');
17425          $entry.append($imgCaption);
17426          $entry.data('jg.createdCaption', true);
17427        }
17428      }
17429
17430      // Create events (we check again the $imgCaption because it can be still inexistent)
17431      if ($imgCaption !== null) {
17432        if (!this.settings.cssAnimation) $imgCaption.stop().fadeTo(0, this.settings.captionSettings.nonVisibleOpacity);
17433        this.addCaptionEventsHandlers($entry);
17434      }
17435    } else {
17436      this.removeCaptionEventsHandlers($entry);
17437    }
17438  };
17439
17440  /**
17441   * Validates the caption
17442   *
17443   * @param caption The caption that should be validated
17444   * @return {boolean} Validation result
17445   */
17446  JustifiedGallery.prototype.isValidCaption = function (caption) {
17447    return (typeof caption !== 'undefined' && caption.length > 0);
17448  };
17449
17450  /**
17451   * The callback for the event 'mouseenter'. It assumes that the event currentTarget is an entry.
17452   * It shows the caption using jQuery (or using CSS if it is configured so)
17453   *
17454   * @param {Event} eventObject the event object
17455   */
17456  JustifiedGallery.prototype.onEntryMouseEnterForCaption = function (eventObject) {
17457    var $caption = this.captionFromEntry($(eventObject.currentTarget));
17458    if (this.settings.cssAnimation) {
17459      $caption.addClass('caption-visible').removeClass('caption-hidden');
17460    } else {
17461      $caption.stop().fadeTo(this.settings.captionSettings.animationDuration,
17462          this.settings.captionSettings.visibleOpacity);
17463    }
17464  };
17465
17466  /**
17467   * The callback for the event 'mouseleave'. It assumes that the event currentTarget is an entry.
17468   * It hides the caption using jQuery (or using CSS if it is configured so)
17469   *
17470   * @param {Event} eventObject the event object
17471   */
17472  JustifiedGallery.prototype.onEntryMouseLeaveForCaption = function (eventObject) {
17473    var $caption = this.captionFromEntry($(eventObject.currentTarget));
17474    if (this.settings.cssAnimation) {
17475      $caption.removeClass('caption-visible').removeClass('caption-hidden');
17476    } else {
17477      $caption.stop().fadeTo(this.settings.captionSettings.animationDuration,
17478          this.settings.captionSettings.nonVisibleOpacity);
17479    }
17480  };
17481
17482  /**
17483   * Add the handlers of the entry for the caption
17484   *
17485   * @param $entry the entry to modify
17486   */
17487  JustifiedGallery.prototype.addCaptionEventsHandlers = function ($entry) {
17488    var captionMouseEvents = $entry.data('jg.captionMouseEvents');
17489    if (typeof captionMouseEvents === 'undefined') {
17490      captionMouseEvents = {
17491        mouseenter: $.proxy(this.onEntryMouseEnterForCaption, this),
17492        mouseleave: $.proxy(this.onEntryMouseLeaveForCaption, this)
17493      };
17494      $entry.on('mouseenter', undefined, undefined, captionMouseEvents.mouseenter);
17495      $entry.on('mouseleave', undefined, undefined, captionMouseEvents.mouseleave);
17496      $entry.data('jg.captionMouseEvents', captionMouseEvents);
17497    }
17498  };
17499
17500  /**
17501   * Remove the handlers of the entry for the caption
17502   *
17503   * @param $entry the entry to modify
17504   */
17505  JustifiedGallery.prototype.removeCaptionEventsHandlers = function ($entry) {
17506    var captionMouseEvents = $entry.data('jg.captionMouseEvents');
17507    if (typeof captionMouseEvents !== 'undefined') {
17508      $entry.off('mouseenter', undefined, captionMouseEvents.mouseenter);
17509      $entry.off('mouseleave', undefined, captionMouseEvents.mouseleave);
17510      $entry.removeData('jg.captionMouseEvents');
17511    }
17512  };
17513
17514  /**
17515   * Justify the building row, preparing it to
17516   *
17517   * @param isLastRow
17518   * @returns a boolean to know if the row has been justified or not
17519   */
17520  JustifiedGallery.prototype.prepareBuildingRow = function (isLastRow) {
17521    var i, $entry, imgAspectRatio, newImgW, newImgH, justify = true;
17522    var minHeight = 0;
17523    var availableWidth = this.galleryWidth - 2 * this.border - (
17524        (this.buildingRow.entriesBuff.length - 1) * this.settings.margins);
17525    var rowHeight = Math.floor( availableWidth / this.buildingRow.aspectRatio );//hacked by Uncode
17526    var defaultRowHeight = this.settings.rowHeight;
17527    var justifiable = this.buildingRow.width / availableWidth > this.settings.justifyThreshold;
17528
17529    //Skip the last row if we can't justify it and the lastRow == 'hide'
17530    if (isLastRow && this.settings.lastRow === 'hide' && !justifiable) {
17531      for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
17532        $entry = this.buildingRow.entriesBuff[i];
17533        if (this.settings.cssAnimation)
17534          $entry.removeClass('entry-visible');
17535        else {
17536          $entry.stop().fadeTo(0, 0.1);
17537          $entry.find('> img, > a > img').fadeTo(0, 0);
17538        }
17539      }
17540      return -1;
17541    }
17542
17543    // With lastRow = nojustify, justify if is justificable (the images will not become too big)
17544    if (isLastRow && !justifiable && this.settings.lastRow !== 'justify' && this.settings.lastRow !== 'hide') {
17545      justify = false;
17546
17547      if (this.rows > 0) {
17548        defaultRowHeight = (this.offY - this.border - this.settings.margins * this.rows) / this.rows;
17549        justify = defaultRowHeight * this.buildingRow.aspectRatio / availableWidth > this.settings.justifyThreshold;
17550      }
17551    }
17552
17553    for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
17554      $entry = this.buildingRow.entriesBuff[i];
17555      imgAspectRatio = $entry.data('jg.width') / $entry.data('jg.height');
17556
17557      if (justify) {
17558        newImgW = (i === this.buildingRow.entriesBuff.length - 1) ? availableWidth : rowHeight * imgAspectRatio;
17559        newImgH = rowHeight;
17560      } else {
17561        newImgW = defaultRowHeight * imgAspectRatio;
17562        newImgH = defaultRowHeight;
17563      }
17564
17565      availableWidth -= Math.round(newImgW);
17566      $entry.data('jg.jwidth', Math.round(newImgW));
17567      $entry.data('jg.jheight', Math.ceil(newImgH));
17568      if (i === 0 || minHeight > newImgH) minHeight = newImgH;
17569    }
17570
17571    this.buildingRow.height = minHeight;
17572    return justify;
17573  };
17574
17575  /**
17576   * Clear the building row data to be used for a new row
17577   */
17578  JustifiedGallery.prototype.clearBuildingRow = function () {
17579    this.buildingRow.entriesBuff = [];
17580    this.buildingRow.aspectRatio = 0;
17581    this.buildingRow.width = 0;
17582  };
17583
17584  /**
17585   * Flush a row: justify it, modify the gallery height accordingly to the row height
17586   *
17587   * @param isLastRow
17588   */
17589  JustifiedGallery.prototype.flushRow = function (isLastRow) {
17590    var settings = this.settings;
17591    var $entry, buildingRowRes, offX = this.border, i;
17592
17593    buildingRowRes = this.prepareBuildingRow(isLastRow);
17594    if (isLastRow && settings.lastRow === 'hide' && buildingRowRes === -1) {
17595      this.clearBuildingRow();
17596      return;
17597    }
17598
17599    if (this.maxRowHeight) {
17600      if (this.maxRowHeight.isPercentage && this.maxRowHeight.value * settings.rowHeight < this.buildingRow.height) {
17601        this.buildingRow.height = this.maxRowHeight.value * settings.rowHeight;
17602      } else if (this.maxRowHeight.value >= settings.rowHeight && this.maxRowHeight.value < this.buildingRow.height) {
17603        this.buildingRow.height = this.maxRowHeight.value;
17604      }
17605    }
17606
17607    //Align last (unjustified) row
17608    if (settings.lastRow === 'center' || settings.lastRow === 'right') {
17609      var availableWidth = this.galleryWidth - 2 * this.border - (this.buildingRow.entriesBuff.length - 1) * settings.margins;
17610
17611      for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
17612        $entry = this.buildingRow.entriesBuff[i];
17613        availableWidth -= $entry.data('jg.jwidth');
17614      }
17615
17616      if (settings.lastRow === 'center')
17617        offX += availableWidth / 2;
17618      else if (settings.lastRow === 'right')
17619        offX += availableWidth;
17620    }
17621
17622    for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
17623      $entry = this.buildingRow.entriesBuff[i];
17624      this.displayEntry($entry, offX, this.offY, $entry.data('jg.jwidth'), $entry.data('jg.jheight'), this.buildingRow.height);
17625      offX += $entry.data('jg.jwidth') + settings.margins;
17626    }
17627
17628    //Gallery Height
17629    this.galleryHeightToSet = this.offY + this.buildingRow.height + this.border;
17630    this.$gallery.height(this.galleryHeightToSet + this.getSpinnerHeight());
17631
17632    if (!isLastRow || (this.buildingRow.height <= settings.rowHeight && buildingRowRes)) {
17633      //Ready for a new row
17634      this.offY += this.buildingRow.height + settings.margins;
17635      this.rows += 1;
17636      this.clearBuildingRow();
17637      this.$gallery.trigger('jg.rowflush');
17638    }
17639  };
17640
17641  /**
17642   * Checks the width of the gallery container, to know if a new justification is needed
17643   */
17644  var scrollBarOn = false;
17645  JustifiedGallery.prototype.checkWidth = function () {
17646    this.checkWidthIntervalId = setInterval($.proxy(function () {
17647      var galleryWidth = parseFloat(this.$gallery.width());
17648      if (hasScrollBar() === scrollBarOn) {
17649        if (Math.abs(galleryWidth - this.galleryWidth) > this.settings.refreshSensitivity) {
17650          this.galleryWidth = galleryWidth;
17651          this.rewind();
17652
17653          // Restart to analyze
17654          this.startImgAnalyzer(true);
17655        }
17656      } else {
17657        scrollBarOn = hasScrollBar();
17658        this.galleryWidth = galleryWidth;
17659      }
17660    }, this), this.settings.refreshTime);
17661  };
17662
17663  /**
17664   * @returns {boolean} a boolean saying if the spinner is active or not
17665   */
17666  JustifiedGallery.prototype.isSpinnerActive = function () {
17667    return this.spinner.intervalId !== null;
17668  };
17669
17670  /**
17671   * @returns {int} the spinner height
17672   */
17673  JustifiedGallery.prototype.getSpinnerHeight = function () {
17674    return this.spinner.$el.innerHeight();
17675  };
17676
17677  /**
17678   * Stops the spinner animation and modify the gallery height to exclude the spinner
17679   */
17680  JustifiedGallery.prototype.stopLoadingSpinnerAnimation = function () {
17681    clearInterval(this.spinner.intervalId);
17682    this.spinner.intervalId = null;
17683    this.$gallery.height(this.$gallery.height() - this.getSpinnerHeight());
17684    this.spinner.$el.detach();
17685  };
17686
17687  /**
17688   * Starts the spinner animation
17689   */
17690  JustifiedGallery.prototype.startLoadingSpinnerAnimation = function () {
17691    var spinnerContext = this.spinner;
17692    var $spinnerPoints = spinnerContext.$el.find('span');
17693    clearInterval(spinnerContext.intervalId);
17694    this.$gallery.append(spinnerContext.$el);
17695    this.$gallery.height(this.offY + this.buildingRow.height + this.getSpinnerHeight());
17696    spinnerContext.intervalId = setInterval(function () {
17697      if (spinnerContext.phase < $spinnerPoints.length) {
17698        $spinnerPoints.eq(spinnerContext.phase).fadeTo(spinnerContext.timeSlot, 1);
17699      } else {
vendor: 4,333 bytes, lines 17700-17844
17700        $spinnerPoints.eq(spinnerContext.phase - $spinnerPoints.length).fadeTo(spinnerContext.timeSlot, 0);
17701      }
17702      spinnerContext.phase = (spinnerContext.phase + 1) % ($spinnerPoints.length * 2);
17703    }, spinnerContext.timeSlot);
17704  };
17705
17706  /**
17707   * Rewind the image analysis to start from the first entry.
17708   */
17709  JustifiedGallery.prototype.rewind = function () {
17710    this.lastFetchedEntry = null;
17711    this.lastAnalyzedIndex = -1;
17712    this.offY = this.border;
17713    this.rows = 0;
17714    this.clearBuildingRow();
17715  };
17716
17717  /**
17718   * Update the entries searching it from the justified gallery HTML element
17719   *
17720   * @param norewind if norewind only the new entries will be changed (i.e. randomized, sorted or filtered)
17721   * @returns {boolean} true if some entries has been founded
17722   */
17723  JustifiedGallery.prototype.updateEntries = function (norewind) {
17724    var newEntries;
17725
17726    if (norewind && this.lastFetchedEntry != null) {
17727      newEntries = $(this.lastFetchedEntry).nextAll(this.settings.selector).toArray();
17728    } else {
17729      this.entries = [];
17730      newEntries = this.$gallery.children(this.settings.selector).toArray();
17731    }
17732
17733    if (newEntries.length > 0) {
17734
17735      // Sort or randomize
17736      if ($.isFunction(this.settings.sort)) {
17737        newEntries = this.sortArray(newEntries);
17738      } else if (this.settings.randomize) {
17739        newEntries = this.shuffleArray(newEntries);
17740      }
17741      this.lastFetchedEntry = newEntries[newEntries.length - 1];
17742
17743      // Filter
17744      if (this.settings.filter) {
17745        newEntries = this.filterArray(newEntries);
17746      } else {
17747        this.resetFilters(newEntries);
17748      }
17749
17750    }
17751
17752    this.entries = this.entries.concat(newEntries);
17753    return true;
17754  };
17755
17756  /**
17757   * Apply the entries order to the DOM, iterating the entries and appending the images
17758   *
17759   * @param entries the entries that has been modified and that must be re-ordered in the DOM
17760   */
17761  JustifiedGallery.prototype.insertToGallery = function (entries) {
17762    var that = this;
17763    $.each(entries, function () {
17764      $(this).appendTo(that.$gallery);
17765    });
17766  };
17767
17768  /**
17769   * Shuffle the array using the Fisher-Yates shuffle algorithm
17770   *
17771   * @param a the array to shuffle
17772   * @return the shuffled array
17773   */
17774  JustifiedGallery.prototype.shuffleArray = function (a) {
17775    var i, j, temp;
17776    for (i = a.length - 1; i > 0; i--) {
17777      j = Math.floor(Math.random() * (i + 1));
17778      temp = a[i];
17779      a[i] = a[j];
17780      a[j] = temp;
17781    }
17782    this.insertToGallery(a);
17783    return a;
17784  };
17785
17786  /**
17787   * Sort the array using settings.comparator as comparator
17788   *
17789   * @param a the array to sort (it is sorted)
17790   * @return the sorted array
17791   */
17792  JustifiedGallery.prototype.sortArray = function (a) {
17793    a.sort(this.settings.sort);
17794    this.insertToGallery(a);
17795    return a;
17796  };
17797
17798  /**
17799   * Reset the filters removing the 'jg-filtered' class from all the entries
17800   *
17801   * @param a the array to reset
17802   */
17803  JustifiedGallery.prototype.resetFilters = function (a) {
17804    for (var i = 0; i < a.length; i++) $(a[i]).removeClass('jg-filtered');
17805  };
17806
17807  /**
17808   * Filter the entries considering theirs classes (if a string has been passed) or using a function for filtering.
17809   *
17810   * @param a the array to filter
17811   * @return the filtered array
17812   */
17813  JustifiedGallery.prototype.filterArray = function (a) {
17814    var settings = this.settings;
17815    if ($.type(settings.filter) === 'string') {
17816      // Filter only keeping the entries passed in the string
17817      return a.filter(function (el) {
17818        var $el = $(el);
17819        if ($el.is(settings.filter)) {
17820          $el.removeClass('jg-filtered');
17821          return true;
17822        } else {
17823          $el.addClass('jg-filtered').removeClass('jg-visible');
17824          return false;
17825        }
17826      });
17827    } else if ($.isFunction(settings.filter)) {
17828      // Filter using the passed function
17829      var filteredArr = a.filter(settings.filter);
17830      for (var i = 0; i < a.length; i++) {
17831        if (filteredArr.indexOf(a[i]) == -1) {
17832          $(a[i]).addClass('jg-filtered').removeClass('jg-visible');
17833        } else {
17834          $(a[i]).removeClass('jg-filtered');
17835        }
17836      }
17837      return filteredArr;
17838    }
17839  };
17840
17841  /**
17842   * Destroy the Justified Gallery instance.
17843   *
17844   * It clears all the css properties added in the style attributes. We doesn't backup the original
17845   * values for those css attributes, because it costs (performance) and because in general one
17846   * shouldn't use the style attribute for an uniform set of images (where we suppose the use of
17847   * classes). Creating a backup is also difficult because JG could be called multiple times and
17848   * with different style attributes.
17849   */
17850  JustifiedGallery.prototype.destroy = function () {
17851    clearInterval(this.checkWidthIntervalId);
17852
17853    $.each(this.entries, $.proxy(function(_, entry) {
17854      var $entry = $(entry);
17855
17856      // Reset entry style
17857      $entry.css('width', '');
17858      $entry.css('height', '');
17859      $entry.css('top', '');
17860      $entry.css('left', '');
17861      $entry.data('jg.loaded', undefined);
17862      $entry.removeClass('jg-entry');
17863
17864      // Reset image style
17865      var $img = this.imgFromEntry($entry);
17866      $img.css('width', '');
17867      $img.css('height', '');
17868      $img.css('margin-left', '');
17869      $img.css('margin-top', '');
17870      $img.attr('src', $img.data('jg.originalSrc'));
17871      $img.data('jg.originalSrc', undefined);
17872
17873      // Remove caption
17874      this.removeCaptionEventsHandlers($entry);
17875      var $caption = this.captionFromEntry($entry);
17876      if ($entry.data('jg.createdCaption')) {
17877        // remove also the caption element (if created by jg)
17878        $entry.data('jg.createdCaption', undefined);
17879        if ($caption !== null) $caption.remove();
17880      } else {
17881        if ($caption !== null) $caption.fadeTo(0, 1);
17882      }
17883
17884    }, this));
17885
17886    this.$gallery.css('height', '');
17887    this.$gallery.removeClass('justified-gallery');
17888    this.$gallery.data('jg.controller', undefined);
17889  };
17890
17891  /**
17892   * Analyze the images and builds the rows. It returns if it found an image that is not loaded.
17893   *
17894   * @param isForResize if the image analyzer is called for resizing or not, to call a different callback at the end
17895   */
17896  JustifiedGallery.prototype.analyzeImages = function (isForResize) {
17897    for (var i = this.lastAnalyzedIndex + 1; i < this.entries.length; i++) {
17898      var $entry = $(this.entries[i]);
17899      if ($entry.data('jg.loaded') === true || $entry.data('jg.loaded') === 'skipped') {
17900        var availableWidth = this.galleryWidth - 2 * this.border - (
17901            (this.buildingRow.entriesBuff.length - 1) * this.settings.margins);
17902        var imgAspectRatio = $entry.data('jg.width') / $entry.data('jg.height');
17903        if (availableWidth / (this.buildingRow.aspectRatio + imgAspectRatio) < this.settings.rowHeight) {
17904          this.flushRow(false);
17905          if(++this.yield.flushed >= this.yield.every) {
17906            this.startImgAnalyzer(isForResize);
17907            return;
17908          }
17909        }
17910
17911        this.buildingRow.entriesBuff.push($entry);
17912        this.buildingRow.aspectRatio += imgAspectRatio;
17913        this.buildingRow.width += imgAspectRatio * this.settings.rowHeight;
17914        this.lastAnalyzedIndex = i;
17915
17916      } else if ($entry.data('jg.loaded') !== 'error') {
17917        return;
17918      }
17919    }
17920
17921    // Last row flush (the row is not full)
17922    if (this.buildingRow.entriesBuff.length > 0) this.flushRow(true);
17923
17924    if (this.isSpinnerActive()) {
17925      this.stopLoadingSpinnerAnimation();
17926    }
17927
17928    /* Stop, if there is, the timeout to start the analyzeImages.
17929     This is because an image can be set loaded, and the timeout can be set,
17930     but this image can be analyzed yet.
17931     */
17932    this.stopImgAnalyzerStarter();
17933
17934    //On complete callback
17935    this.$gallery.trigger(isForResize ? 'jg.resize' : 'jg.complete');
17936    this.$gallery.height(this.galleryHeightToSet);
17937  };
17938
17939  /**
17940   * Stops any ImgAnalyzer starter (that has an assigned timeout)
17941   */
17942  JustifiedGallery.prototype.stopImgAnalyzerStarter = function () {
17943    this.yield.flushed = 0;
17944    if (this.imgAnalyzerTimeout !== null) clearTimeout(this.imgAnalyzerTimeout);
17945  };
17946
17947  /**
17948   * Starts the image analyzer. It is not immediately called to let the browser to update the view
17949   *
17950   * @param isForResize specifies if the image analyzer must be called for resizing or not
17951   */
17952  JustifiedGallery.prototype.startImgAnalyzer = function (isForResize) {
17953    var that = this;
17954    this.stopImgAnalyzerStarter();
17955    this.imgAnalyzerTimeout = setTimeout(function () {
17956      that.analyzeImages(isForResize);
17957    }, 0.001); // we can't start it immediately due to a IE different behaviour
17958  };
17959
17960  /**
17961   * Checks if the image is loaded or not using another image object. We cannot use the 'complete' image property,
17962   * because some browsers, with a 404 set complete = true.
17963   *
17964   * @param imageSrc the image src to load
17965   * @param onLoad callback that is called when the image has been loaded
vendor: 13,320 bytes, lines 17966-18316
17966   * @param onError callback that is called in case of an error
17967   */
17968  JustifiedGallery.prototype.onImageEvent = function (imageSrc, onLoad, onError) {
17969    if (!onLoad && !onError) return;
17970
17971    var memImage = new Image();
17972    var $memImage = $(memImage);
17973    if (onLoad) {
17974      $memImage.one('load', function () {
17975        $memImage.off('load error');
17976        onLoad(memImage);
17977      });
17978    }
17979    if (onError) {
17980      $memImage.one('error', function() {
17981        $memImage.off('load error');
17982        onError(memImage);
17983      });
17984    }
17985    memImage.src = imageSrc;
17986  };
17987
17988  /**
17989   * Init of Justified Gallery controlled
17990   * It analyzes all the entries starting theirs loading and calling the image analyzer (that works with loaded images)
17991   */
17992  JustifiedGallery.prototype.init = function () {
17993    var imagesToLoad = false, skippedImages = false, that = this;
17994    $.each(this.entries, function (index, entry) {
17995      var $entry = $(entry);
17996      var $image = that.imgFromEntry($entry);
17997
17998      $entry.addClass('jg-entry');
17999
18000      if ($entry.data('jg.loaded') !== true && $entry.data('jg.loaded') !== 'skipped') {
18001
18002        // Link Rel global overwrite
18003        if (that.settings.rel !== null) $entry.attr('rel', that.settings.rel);
18004
18005        // Link Target global overwrite
18006        if (that.settings.target !== null) $entry.attr('target', that.settings.target);
18007
18008        if ($image !== null) {
18009
18010          // Image src
18011          var imageSrc = that.extractImgSrcFromImage($image);
18012          $image.attr('src', imageSrc);
18013
18014          /* If we have the height and the width, we don't wait that the image is loaded, but we start directly
18015           * with the justification */
18016          if (that.settings.waitThumbnailsLoad === false) {
18017            var width = parseFloat($image.attr('width'));
18018            var height = parseFloat($image.attr('height'));
18019            if (!isNaN(width) && !isNaN(height)) {
18020              $entry.data('jg.width', width);
18021              $entry.data('jg.height', height);
18022              $entry.data('jg.loaded', 'skipped');
18023              skippedImages = true;
18024              that.startImgAnalyzer(false);
18025              return true; // continue
18026            }
18027          }
18028
18029          $entry.data('jg.loaded', false);
18030          imagesToLoad = true;
18031
18032          // Spinner start
18033          if (!that.isSpinnerActive()) that.startLoadingSpinnerAnimation();
18034
18035          that.onImageEvent(imageSrc, function (loadImg) { // image loaded
18036            $entry.data('jg.width', loadImg.width);
18037            $entry.data('jg.height', loadImg.height);
18038            $entry.data('jg.loaded', true);
18039            that.startImgAnalyzer(false);
18040          }, function () { // image load error
18041            $entry.data('jg.loaded', 'error');
18042            that.startImgAnalyzer(false);
18043          });
18044
18045        } else {
18046          $entry.data('jg.loaded', true);
18047          $entry.data('jg.width', $entry.width() | parseFloat($entry.css('width')) | 1);
18048          $entry.data('jg.height', $entry.height() | parseFloat($entry.css('height')) | 1);
18049        }
18050
18051      }
18052
18053    });
18054
18055    if (!imagesToLoad && !skippedImages) this.startImgAnalyzer(false);
18056    this.checkWidth();
18057  };
18058
18059  /**
18060   * Checks that it is a valid number. If a string is passed it is converted to a number
18061   *
18062   * @param settingContainer the object that contains the setting (to allow the conversion)
18063   * @param settingName the setting name
18064   */
18065  JustifiedGallery.prototype.checkOrConvertNumber = function (settingContainer, settingName) {
18066    if ($.type(settingContainer[settingName]) === 'string') {
18067      settingContainer[settingName] = parseFloat(settingContainer[settingName]);
18068    }
18069
18070    if ($.type(settingContainer[settingName]) === 'number') {
18071      if (isNaN(settingContainer[settingName])) throw 'invalid number for ' + settingName;
18072    } else {
18073      throw settingName + ' must be a number';
18074    }
18075  };
18076
18077  /**
18078   * Checks the sizeRangeSuffixes and, if necessary, converts
18079   * its keys from string (e.g. old settings with 'lt100') to int.
18080   */
18081  JustifiedGallery.prototype.checkSizeRangesSuffixes = function () {
18082    if ($.type(this.settings.sizeRangeSuffixes) !== 'object') {
18083      throw 'sizeRangeSuffixes must be defined and must be an object';
18084    }
18085
18086    var suffixRanges = [];
18087    for (var rangeIdx in this.settings.sizeRangeSuffixes) {
18088      if (this.settings.sizeRangeSuffixes.hasOwnProperty(rangeIdx)) suffixRanges.push(rangeIdx);
18089    }
18090
18091    var newSizeRngSuffixes = {0: ''};
18092    for (var i = 0; i < suffixRanges.length; i++) {
18093      if ($.type(suffixRanges[i]) === 'string') {
18094        try {
18095          var numIdx = parseInt(suffixRanges[i].replace(/^[a-z]+/, ''), 10);
18096          newSizeRngSuffixes[numIdx] = this.settings.sizeRangeSuffixes[suffixRanges[i]];
18097        } catch (e) {
18098          throw 'sizeRangeSuffixes keys must contains correct numbers (' + e + ')';
18099        }
18100      } else {
18101        newSizeRngSuffixes[suffixRanges[i]] = this.settings.sizeRangeSuffixes[suffixRanges[i]];
18102      }
18103    }
18104
18105    this.settings.sizeRangeSuffixes = newSizeRngSuffixes;
18106  };
18107
18108  /**
18109   * check and convert the maxRowHeight setting
18110   */
18111  JustifiedGallery.prototype.retrieveMaxRowHeight = function () {
18112    var newMaxRowHeight = { };
18113
18114    if ($.type(this.settings.maxRowHeight) === 'string') {
18115      if (this.settings.maxRowHeight.match(/^[0-9]+%$/)) {
18116        newMaxRowHeight.value = parseFloat(this.settings.maxRowHeight.match(/^([0-9]+)%$/)[1]) / 100;
18117        newMaxRowHeight.isPercentage = false;
18118      } else {
18119        newMaxRowHeight.value = parseFloat(this.settings.maxRowHeight);
18120        newMaxRowHeight.isPercentage = true;
18121      }
18122    } else if ($.type(this.settings.maxRowHeight) === 'number') {
18123      newMaxRowHeight.value = this.settings.maxRowHeight;
18124      newMaxRowHeight.isPercentage = false;
18125    } else if (this.settings.maxRowHeight === false ||
18126        this.settings.maxRowHeight === null ||
18127        typeof this.settings.maxRowHeight == 'undefined') {
18128      return null;
18129    } else {
18130      throw 'maxRowHeight must be a number or a percentage';
18131    }
18132
18133    // check if the converted value is not a number
18134    if (isNaN(newMaxRowHeight.value)) throw 'invalid number for maxRowHeight';
18135
18136    // check values
18137    if (newMaxRowHeight.isPercentage) {
18138      if (newMaxRowHeight.value < 100) newMaxRowHeight.value = 100;
18139    }
18140
18141    return newMaxRowHeight;
18142  };
18143
18144  /**
18145   * Checks the settings
18146   */
18147  JustifiedGallery.prototype.checkSettings = function () {
18148    this.checkSizeRangesSuffixes();
18149
18150    this.checkOrConvertNumber(this.settings, 'rowHeight');
18151    this.checkOrConvertNumber(this.settings, 'margins');
18152    this.checkOrConvertNumber(this.settings, 'border');
18153
18154    var lastRowModes = [
18155      'justify',
18156      'nojustify',
18157      'left',
18158      'center',
18159      'right',
18160      'hide'
18161    ];
18162    if (lastRowModes.indexOf(this.settings.lastRow) === -1) {
18163      throw 'lastRow must be one of: ' + lastRowModes.join(', ');
18164    }
18165
18166    this.checkOrConvertNumber(this.settings, 'justifyThreshold');
18167    if (this.settings.justifyThreshold < 0 || this.settings.justifyThreshold > 1) {
18168      throw 'justifyThreshold must be in the interval [0,1]';
18169    }
18170    if ($.type(this.settings.cssAnimation) !== 'boolean') {
18171      throw 'cssAnimation must be a boolean';
18172    }
18173
18174    if ($.type(this.settings.captions) !== 'boolean') throw 'captions must be a boolean';
18175    this.checkOrConvertNumber(this.settings.captionSettings, 'animationDuration');
18176
18177    this.checkOrConvertNumber(this.settings.captionSettings, 'visibleOpacity');
18178    if (this.settings.captionSettings.visibleOpacity < 0 ||
18179        this.settings.captionSettings.visibleOpacity > 1) {
18180      throw 'captionSettings.visibleOpacity must be in the interval [0, 1]';
18181    }
18182
18183    this.checkOrConvertNumber(this.settings.captionSettings, 'nonVisibleOpacity');
18184    if (this.settings.captionSettings.nonVisibleOpacity < 0 ||
18185        this.settings.captionSettings.nonVisibleOpacity > 1) {
18186      throw 'captionSettings.nonVisibleOpacity must be in the interval [0, 1]';
18187    }
18188
18189    this.checkOrConvertNumber(this.settings, 'imagesAnimationDuration');
18190    this.checkOrConvertNumber(this.settings, 'refreshTime');
18191    this.checkOrConvertNumber(this.settings, 'refreshSensitivity');
18192    if ($.type(this.settings.randomize) !== 'boolean') throw 'randomize must be a boolean';
18193    if ($.type(this.settings.selector) !== 'string') throw 'selector must be a string';
18194
18195    if (this.settings.sort !== false && !$.isFunction(this.settings.sort)) {
18196      throw 'sort must be false or a comparison function';
18197    }
18198
18199    if (this.settings.filter !== false && !$.isFunction(this.settings.filter) &&
18200        $.type(this.settings.filter) !== 'string') {
18201      throw 'filter must be false, a string or a filter function';
18202    }
18203  };
18204
18205  /**
18206   * It brings all the indexes from the sizeRangeSuffixes and it orders them. They are then sorted and returned.
18207   * @returns {Array} sorted suffix ranges
18208   */
18209  JustifiedGallery.prototype.retrieveSuffixRanges = function () {
18210    var suffixRanges = [];
18211    for (var rangeIdx in this.settings.sizeRangeSuffixes) {
18212      if (this.settings.sizeRangeSuffixes.hasOwnProperty(rangeIdx)) suffixRanges.push(parseInt(rangeIdx, 10));
18213    }
18214    suffixRanges.sort(function (a, b) { return a > b ? 1 : a < b ? -1 : 0; });
18215    return suffixRanges;
18216  };
18217
18218  /**
18219   * Update the existing settings only changing some of them
18220   *
18221   * @param newSettings the new settings (or a subgroup of them)
18222   */
18223  JustifiedGallery.prototype.updateSettings = function (newSettings) {
18224    // In this case Justified Gallery has been called again changing only some options
18225    this.settings = $.extend({}, this.settings, newSettings);
18226    this.checkSettings();
18227
18228    // As reported in the settings: negative value = same as margins, 0 = disabled
18229    this.border = this.settings.border >= 0 ? this.settings.border : this.settings.margins;
18230
18231    this.maxRowHeight = this.retrieveMaxRowHeight();
18232    this.suffixRanges = this.retrieveSuffixRanges();
18233  };
18234
18235  /**
18236   * Justified Gallery plugin for jQuery
18237   *
18238   * Events
18239   *  - jg.complete : called when all the gallery has been created
18240   *  - jg.resize : called when the gallery has been resized
18241   *  - jg.rowflush : when a new row appears
18242   *
18243   * @param arg the action (or the settings) passed when the plugin is called
18244   * @returns {*} the object itself
18245   */
18246  $.fn.justifiedGallery = function (arg) {
18247    return this.each(function (index, gallery) {
18248
18249      var $gallery = $(gallery);
18250      $gallery.addClass('justified-gallery');
18251
18252      var controller = $gallery.data('jg.controller');
18253      if (typeof controller === 'undefined') {
18254        // Create controller and assign it to the object data
18255        if (typeof arg !== 'undefined' && arg !== null && $.type(arg) !== 'object') {
18256          if (arg === 'destroy') return; // Just a call to an unexisting object
18257          throw 'The argument must be an object';
18258        }
18259        controller = new JustifiedGallery($gallery, $.extend({}, $.fn.justifiedGallery.defaults, arg));
18260        $gallery.data('jg.controller', controller);
18261      } else if (arg === 'norewind') {
18262        // In this case we don't rewind: we analyze only the latest images (e.g. to complete the last unfinished row
18263        // ... left to be more readable
18264      } else if (arg === 'destroy') {
18265        controller.destroy();
18266        return;
18267      } else {
18268        // In this case Justified Gallery has been called again changing only some options
18269        controller.updateSettings(arg);
18270        controller.rewind();
18271      }
18272
18273      // Update the entries list
18274      if (!controller.updateEntries(arg === 'norewind')) return;
18275
18276      // Init justified gallery
18277      controller.init();
18278
18279    });
18280  };
18281
18282  // Default options
18283  $.fn.justifiedGallery.defaults = {
18284    sizeRangeSuffixes: { }, /* e.g. Flickr configuration
18285        {
18286          100: '_t',  // used when longest is less than 100px
18287          240: '_m',  // used when longest is between 101px and 240px
18288          320: '_n',  // ...
18289          500: '',
18290          640: '_z',
18291          1024: '_b'  // used as else case because it is the last
18292        }
18293    */
18294    thumbnailPath: undefined, /* If defined, sizeRangeSuffixes is not used, and this function is used to determine the
18295    path relative to a specific thumbnail size. The function should accept respectively three arguments:
18296    current path, width and height */
18297    rowHeight: 120,
18298    maxRowHeight: false, // false or negative value to deactivate. Positive number to express the value in pixels,
18299                         // A string '[0-9]+%' to express in percentage (e.g. 300% means that the row height
18300                         // can't exceed 3 * rowHeight)
18301    margins: 1,
18302    border: -1, // negative value = same as margins, 0 = disabled, any other value to set the border
18303
18304    lastRow: 'nojustify', // … which is the same as 'left', or can be 'justify', 'center', 'right' or 'hide'
18305
18306    justifyThreshold: 0.90, /* if row width / available space > 0.90 it will be always justified
18307                             * (i.e. lastRow setting is not considered) */
18308    waitThumbnailsLoad: true,
18309    captions: true,
18310    cssAnimation: true,
18311    imagesAnimationDuration: 500, // ignored with css animations
18312    captionSettings: { // ignored with css animations
18313      animationDuration: 500,
18314      visibleOpacity: 0.7,
18315      nonVisibleOpacity: 0.0
18316    },
18317    rel: null, // rewrite the rel of each analyzed links
18318    target: null, // rewrite the target of all links
18319    extension: /\.[^.\\/]+$/, // regexp to capture the extension of an image
18320    refreshTime: 200, // time interval (in ms) to check if the page changes its width
18321    refreshSensitivity: 0, // change in width allowed (in px) without re-building the gallery
18322    randomize: false,
18323    sort: false, /*
18324      - false: to do not sort
18325      - function: to sort them using the function as comparator (see Array.prototype.sort())
18326    */
18327    filter: false, /*
18328      - false, null or undefined: for a disabled filter
18329      - a string: an entry is kept if entry.is(filter string) returns true
18330                  see jQuery's .is() function for further information
18331      - a function: invoked with arguments (entry, index, array). Return true to keep the entry, false otherwise.
18332                    It follows the specifications of the Array.prototype.filter() function of JavaScript.
18333    */
18334    selector: 'a, div:not(.spinner)' // The selector that is used to know what are the entries of the gallery
18335  };
18336
18337}(jQuery));
18338/*!
18339* Customized version of iScroll.js 0.0.1
18340* It fixes bugs affecting its integration with fullpage.js
18341*/
18342/*! iScroll v5.2.0 ~ (c) 2008-2016 Matteo Spinelli ~ http://cubiq.org/license */
18343(function (window, document, Math) {
18344var rAF = window.requestAnimationFrame  ||
18345    window.webkitRequestAnimationFrame  ||
18346    window.mozRequestAnimationFrame     ||
18347    window.oRequestAnimationFrame       ||
18348    window.msRequestAnimationFrame      ||
18349    function (callback) { window.setTimeout(callback, 1000 / 60); };
18350
18351var utils = (function () {
18352    var me = {};
18353
18354    var _elementStyle = document.createElement('div').style;
18355    var _vendor = (function () {
18356        var vendors = ['t', 'webkitT', 'MozT', 'msT', 'OT'],
18357            transform,
18358            i = 0,
18359            l = vendors.length;
18360
18361        for ( ; i < l; i++ ) {
18362            transform = vendors[i] + 'ransform';
18363            if ( transform in _elementStyle ) return vendors[i].substr(0, vendors[i].length-1);
18364        }
18365
18366        return false;
18367    })();
18368
18369    function _prefixStyle (style) {
18370        if ( _vendor === false ) return false;
18371        if ( _vendor === '' ) return style;
18372        return _vendor + style.charAt(0).toUpperCase() + style.substr(1);
18373    }
18374
18375    me.getTime = Date.now || function getTime () { return new Date().getTime(); };
18376
18377    me.extend = function (target, obj) {
18378        for ( var i in obj ) {
18379            target[i] = obj[i];
18380        }
18381    };
18382
18383    me.addEvent = function (el, type, fn, capture) {
18384        el.addEventListener(type, fn, !!capture);
18385    };
18386
18387    me.removeEvent = function (el, type, fn, capture) {
18388        el.removeEventListener(type, fn, !!capture);
18389    };
18390
18391    me.prefixPointerEvent = function (pointerEvent) {
18392        return window.MSPointerEvent ?
18393            'MSPointer' + pointerEvent.charAt(7).toUpperCase() + pointerEvent.substr(8):
18394            pointerEvent;
18395    };
18396
18397    me.momentum = function (current, start, time, lowerMargin, wrapperSize, deceleration) {
18398        var distance = current - start,
18399            speed = Math.abs(distance) / time,
18400            destination,
18401            duration;
18402
18403        deceleration = deceleration === undefined ? 0.0006 : deceleration;
18404
18405        destination = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 );
18406        duration = speed / deceleration;
18407
18408        if ( destination < lowerMargin ) {
18409            destination = wrapperSize ? lowerMargin - ( wrapperSize / 2.5 * ( speed / 8 ) ) : lowerMargin;
18410            distance = Math.abs(destination - current);
18411            duration = distance / speed;
18412        } else if ( destination > 0 ) {
18413            destination = wrapperSize ? wrapperSize / 2.5 * ( speed / 8 ) : 0;
18414            distance = Math.abs(current) + destination;
18415            duration = distance / speed;
18416        }
18417
18418        return {
18419            destination: Math.round(destination),
18420            duration: duration
18421        };
18422    };
18423
18424    var _transform = _prefixStyle('transform');
18425
18426    me.extend(me, {
18427        hasTransform: _transform !== false,
18428        hasPerspective: _prefixStyle('perspective') in _elementStyle,
18429        hasTouch: 'ontouchstart' in window,
18430        hasPointer: !!(window.PointerEvent || window.MSPointerEvent), // IE10 is prefixed
18431        hasTransition: _prefixStyle('transition') in _elementStyle
18432    });
18433
18434    /*
18435    This should find all Android browsers lower than build 535.19 (both stock browser and webview)
18436    - galaxy S2 is ok
18437    - 2.3.6 : `AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1`
18438    - 4.0.4 : `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
18439   - galaxy S3 is badAndroid (stock brower, webview)
18440     `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
18441   - galaxy S4 is badAndroid (stock brower, webview)
18442     `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
18443   - galaxy S5 is OK
18444     `AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36 (Chrome/)`
18445   - galaxy S6 is OK
18446     `AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36 (Chrome/)`
18447  */
18448    me.isBadAndroid = (function() {
18449        var appVersion = window.navigator.appVersion;
18450        // Android browser is not a chrome browser.
18451        if (/Android/.test(appVersion) && !(/Chrome\/\d/.test(appVersion))) {
18452            var safariVersion = appVersion.match(/Safari\/(\d+.\d)/);
18453            if(safariVersion && typeof safariVersion === "object" && safariVersion.length >= 2) {
18454                return parseFloat(safariVersion[1]) < 535.19;
18455            } else {
18456                return true;
18457            }
18458        } else {
18459            return false;
18460        }
18461    })();
18462
18463    me.extend(me.style = {}, {
18464        transform: _transform,
18465        transitionTimingFunction: _prefixStyle('transitionTimingFunction'),
18466        transitionDuration: _prefixStyle('transitionDuration'),
18467        transitionDelay: _prefixStyle('transitionDelay'),
18468        transformOrigin: _prefixStyle('transformOrigin')
18469    });
18470
18471    me.hasClass = function (e, c) {
18472        var re = new RegExp("(^|\\s)" + c + "(\\s|$)");
18473        return re.test(e.className);
18474    };
18475
18476    me.addClass = function (e, c) {
18477        if ( me.hasClass(e, c) ) {
18478            return;
18479        }
18480
18481        var newclass = e.className.split(' ');
18482        newclass.push(c);
18483        e.className = newclass.join(' ');
18484    };
18485
18486    me.removeClass = function (e, c) {
18487        if ( !me.hasClass(e, c) ) {
18488            return;
18489        }
18490
18491        var re = new RegExp("(^|\\s)" + c + "(\\s|$)", 'g');
18492        e.className = e.className.replace(re, ' ');
18493    };
18494
18495    me.offset = function (el) {
18496        var left = -el.offsetLeft,
18497            top = -el.offsetTop;
18498
18499        // jshint -W084
18500        while (el = el.offsetParent) {
18501            left -= el.offsetLeft;
18502            top -= el.offsetTop;
18503        }
18504        // jshint +W084
18505
18506        return {
18507            left: left,
18508            top: top
18509        };
18510    };
18511
18512    me.preventDefaultException = function (el, exceptions) {
18513        for ( var i in exceptions ) {
18514            if ( exceptions[i].test(el[i]) ) {
18515                return true;
18516            }
18517        }
18518
18519        return false;
18520    };
18521
18522    me.extend(me.eventType = {}, {
18523        touchstart: 1,
18524        touchmove: 1,
18525        touchend: 1,
18526
18527        mousedown: 2,
18528        mousemove: 2,
18529        mouseup: 2,
18530
18531        pointerdown: 3,
18532        pointermove: 3,
18533        pointerup: 3,
18534
18535        MSPointerDown: 3,
18536        MSPointerMove: 3,
18537        MSPointerUp: 3
18538    });
18539
18540    me.extend(me.ease = {}, {
18541        quadratic: {
18542            style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
18543            fn: function (k) {
18544                return k * ( 2 - k );
18545            }
18546        },
18547        circular: {
18548            style: 'cubic-bezier(0.1, 0.57, 0.1, 1)',   // Not properly "circular" but this looks better, it should be (0.075, 0.82, 0.165, 1)
18549            fn: function (k) {
18550                return Math.sqrt( 1 - ( --k * k ) );
18551            }
18552        },
18553        back: {
18554            style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',
18555            fn: function (k) {
18556                var b = 4;
18557                return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1;
18558            }
18559        },
18560        bounce: {
18561            style: '',
18562            fn: function (k) {
18563                if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {
18564                    return 7.5625 * k * k;
18565                } else if ( k < ( 2 / 2.75 ) ) {
18566                    return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;
18567                } else if ( k < ( 2.5 / 2.75 ) ) {
18568                    return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;
18569                } else {
18570                    return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;
18571                }
18572            }
18573        },
18574        elastic: {
18575            style: '',
18576            fn: function (k) {
18577                var f = 0.22,
18578                    e = 0.4;
18579
18580                if ( k === 0 ) { return 0; }
18581                if ( k == 1 ) { return 1; }
18582
18583                return ( e * Math.pow( 2, - 10 * k ) * Math.sin( ( k - f / 4 ) * ( 2 * Math.PI ) / f ) + 1 );
18584            }
18585        }
18586    });
18587
18588    me.tap = function (e, eventName) {
18589        var ev = document.createEvent('Event');
18590        ev.initEvent(eventName, true, true);
18591        ev.pageX = e.pageX;
18592        ev.pageY = e.pageY;
18593        e.target.dispatchEvent(ev);
18594    };
18595
18596    me.click = function (e) {
18597        var target = e.target,
18598            ev;
18599
18600        if ( !(/(SELECT|INPUT|TEXTAREA)/i).test(target.tagName) ) {
18601            // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/initMouseEvent
18602            // initMouseEvent is deprecated.
18603            ev = document.createEvent(window.MouseEvent ? 'MouseEvents' : 'Event');
18604            ev.initEvent('click', true, true);
18605            ev.view = e.view || window;
18606            ev.detail = 1;
18607            ev.screenX = target.screenX || 0;
18608            ev.screenY = target.screenY || 0;
18609            ev.clientX = target.clientX || 0;
18610            ev.clientY = target.clientY || 0;
18611            ev.ctrlKey = !!e.ctrlKey;
18612            ev.altKey = !!e.altKey;
18613            ev.shiftKey = !!e.shiftKey;
18614            ev.metaKey = !!e.metaKey;
18615            ev.button = 0;
18616            ev.relatedTarget = null;
18617            ev._constructed = true;
18618            target.dispatchEvent(ev);
18619        }
18620    };
18621
18622    return me;
18623})();
18624function IScroll (el, options) {
18625    this.wrapper = typeof el == 'string' ? document.querySelector(el) : el;
18626    this.scroller = this.wrapper.children[0];
18627    this.scrollerStyle = this.scroller.style;       // cache style for better performance
18628
18629    this.options = {
18630
18631        resizeScrollbars: true,
18632
18633        mouseWheelSpeed: 20,
18634
18635        snapThreshold: 0.334,
18636
18637// INSERT POINT: OPTIONS
18638        disablePointer : !utils.hasPointer,
18639        disableTouch : utils.hasPointer || !utils.hasTouch,
18640        disableMouse : utils.hasPointer || utils.hasTouch,
18641        startX: 0,
18642        startY: 0,
18643        scrollY: true,
18644        directionLockThreshold: 5,
18645        momentum: true,
18646
18647        bounce: true,
18648        bounceTime: 600,
18649        bounceEasing: '',
18650
18651        preventDefault: true,
18652        preventDefaultException: { tagName: /^(INPUT|TEXTAREA|BUTTON|
18652SELECT|LABEL)$/ },
18653
18654        HWCompositing: true,
18655        useTransition: true,
18656        useTransform: true,
18657        bindToWrapper: typeof window.onmousedown === "undefined"
18658    };
18659
18660    for ( var i in options ) {
18661        this.options[i] = options[i];
18662    }
18663
18664    // Normalize options
18665    this.translateZ = this.options.HWCompositing && utils.hasPerspective ? ' translateZ(0)' : '';
18666
18667    this.options.useTransition = utils.hasTransition && this.options.useTransition;
18668    this.options.useTransform = utils.hasTransform && this.options.useTransform;
18669
18670    this.options.eventPassthrough = this.options.eventPassthrough === true ? 'vertical' : this.options.eventPassthrough;
18671    this.options.preventDefault = !this.options.eventPassthrough && this.options.preventDefault;
18672
18673    // If you want eventPassthrough I have to lock one of the axes
18674    this.options.scrollY = this.options.eventPassthrough == 'vertical' ? false : this.options.scrollY;
18675    this.options.scrollX = this.options.eventPassthrough == 'horizontal' ? false : this.options.scrollX;
18676
18677    // With eventPassthrough we also need lockDirection mechanism
18678    this.options.freeScroll = this.options.freeScroll && !this.options.eventPassthrough;
18679    this.options.directionLockThreshold = this.options.eventPassthrough ? 0 : this.options.directionLockThreshold;
18680
18681    this.options.bounceEasing = typeof this.options.bounceEasing == 'string' ? utils.ease[this.options.bounceEasing] || utils.ease.circular : this.options.bounceEasing;
18682
18683    this.options.resizePolling = this.options.resizePolling === undefined ? 60 : this.options.resizePolling;
18684
18685    if ( this.options.tap === true ) {
18686        this.options.tap = 'tap';
18687    }
18688
18689    // https://github.com/cubiq/iscroll/issues/1029
18690    if (!this.options.useTransition && !this.options.useTransform) {
18691        if(!(/relative|absolute/i).test(this.scrollerStyle.position)) {
18692            this.scrollerStyle.position = "relative";
18693        }
18694    }
18695
18696    if ( this.options.shrinkScrollbars == 'scale' ) {
18697        this.options.useTransition = false;
18698    }
18699
18700    this.options.invertWheelDirection = this.options.invertWheelDirection ? -1 : 1;
18701
18702// INSERT POINT: NORMALIZATION
18703
18704    // Some defaults
18705    this.x = 0;
18706    this.y = 0;
18707    this.directionX = 0;
18708    this.directionY = 0;
18709    this._events = {};
18710
18711// INSERT POINT: DEFAULTS
18712
18713    this._init();
18714    this.refresh();
18715
18716    this.scrollTo(this.options.startX, this.options.startY);
18717    this.enable();
18718}
18719
18720IScroll.prototype = {
18721    version: '5.2.0',
18722
18723    _init: function () {
18724        this._initEvents();
18725
18726        if ( this.options.scrollbars || this.options.indicators ) {
18727            this._initIndicators();
18728        }
18729
18730        if ( this.options.mouseWheel ) {
18731            this._initWheel();
18732        }
18733
18734        if ( this.options.snap ) {
18735            this._initSnap();
18736        }
18737
18738        if ( this.options.keyBindings ) {
18739            this._initKeys();
18740        }
18741
18742// INSERT POINT: _init
18743
18744    },
18745
18746    destroy: function () {
18747        this._initEvents(true);
18748        clearTimeout(this.resizeTimeout);
18749        this.resizeTimeout = null;
18750        this._execEvent('destroy');
18751    },
18752
18753    _transitionEnd: function (e) {
18754        if ( e.target != this.scroller || !this.isInTransition ) {
18755            return;
18756        }
18757
18758        this._transitionTime();
18759        if ( !this.resetPosition(this.options.bounceTime) ) {
18760            this.isInTransition = false;
18761            this._execEvent('scrollEnd');
18762        }
18763    },
18764
18765    _start: function (e) {
18766        // React to left mouse button only
18767        if ( utils.eventType[e.type] != 1 ) {
18768          // for button property
18769          // http://unixpapa.com/js/mouse.html
18770          var button;
18771        if (!e.which) {
18772          /* IE case */
18773          button = (e.button < 2) ? 0 :
18774                   ((e.button == 4) ? 1 : 2);
18775        } else {
18776          /* All others */
18777          button = e.button;
18778        }
18779            if ( button !== 0 ) {
18780                return;
18781            }
18782        }
18783
18784        if ( !this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated) ) {
18785            return;
18786        }
18787
18788        if ( this.options.preventDefault && !utils.isBadAndroid && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) {
18789            e.preventDefault();
18790        }
18791
18792        var point = e.touches ? e.touches[0] : e,
18793            pos;
18794
18795        this.initiated  = utils.eventType[e.type];
18796        this.moved      = false;
18797        this.distX      = 0;
18798        this.distY      = 0;
18799        this.directionX = 0;
18800        this.directionY = 0;
18801        this.directionLocked = 0;
18802
18803        this.startTime = utils.getTime();
18804
18805        if ( this.options.useTransition && this.isInTransition ) {
18806            this._transitionTime();
18807            this.isInTransition = false;
18808            pos = this.getComputedPosition();
18809            this._translate(Math.round(pos.x), Math.round(pos.y));
18810            this._execEvent('scrollEnd');
18811        } else if ( !this.options.useTransition && this.isAnimating ) {
18812            this.isAnimating = false;
18813            this._execEvent('scrollEnd');
18814        }
18815
18816        this.startX    = this.x;
18817        this.startY    = this.y;
18818        this.absStartX = this.x;
18819        this.absStartY = this.y;
18820        this.pointX    = point.pageX;
18821        this.pointY    = point.pageY;
18822
18823        this._execEvent('beforeScrollStart');
18824    },
18825
18826    _move: function (e) {
18827        if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) {
18828            return;
18829        }
18830
18831        if ( this.options.preventDefault ) {    // increases performance on Android? TODO: check!
18832            e.preventDefault();
18833        }
18834
18835        var point       = e.touches ? e.touches[0] : e,
18836            deltaX      = point.pageX - this.pointX,
18837            deltaY      = point.pageY - this.pointY,
18838            timestamp   = utils.getTime(),
18839            newX, newY,
18840            absDistX, absDistY;
18841
18842        this.pointX     = point.pageX;
18843        this.pointY     = point.pageY;
18844
18845        this.distX      += deltaX;
18846        this.distY      += deltaY;
18847        absDistX        = Math.abs(this.distX);
18848        absDistY        = Math.abs(this.distY);
18849
18850        // We need to move at least 10 pixels for the scrolling to initiate
18851        if ( timestamp - this.endTime > 300 && (absDistX < 10 && absDistY < 10) ) {
18852            return;
18853        }
18854
18855        // If you are scrolling in one direction lock the other
18856        if ( !this.directionLocked && !this.options.freeScroll ) {
18857            if ( absDistX > absDistY + this.options.directionLockThreshold ) {
18858                this.directionLocked = 'h';     // lock horizontally
18859            } else if ( absDistY >= absDistX + this.options.directionLockThreshold ) {
18860                this.directionLocked = 'v';     // lock vertically
18861            } else {
18862                this.directionLocked = 'n';     // no lock
18863            }
18864        }
18865
18866        if ( this.directionLocked == 'h' ) {
18867            if ( this.options.eventPassthrough == 'vertical' ) {
18868                e.preventDefault();
18869            } else if ( this.options.eventPassthrough == 'horizontal' ) {
18870                this.initiated = false;
18871                return;
18872            }
18873
18874            deltaY = 0;
18875        } else if ( this.directionLocked == 'v' ) {
18876            if ( this.options.eventPassthrough == 'horizontal' ) {
18877                e.preventDefault();
18878            } else if ( this.options.eventPassthrough == 'vertical' ) {
18879                this.initiated = false;
18880                return;
18881            }
18882
18883            deltaX = 0;
18884        }
18885
18886        deltaX = this.hasHorizontalScroll ? deltaX : 0;
18887        deltaY = this.hasVerticalScroll ? deltaY : 0;
18888
18889        newX = this.x + deltaX;
18890        newY = this.y + deltaY;
18891
18892        // Slow down if outside of the boundaries
18893        if ( newX > 0 || newX < this.maxScrollX ) {
18894            newX = this.options.bounce ? this.x + deltaX / 3 : newX > 0 ? 0 : this.maxScrollX;
18895        }
18896        if ( newY > 0 || newY < this.maxScrollY ) {
18897            newY = this.options.bounce ? this.y + deltaY / 3 : newY > 0 ? 0 : this.maxScrollY;
18898        }
18899
18900        this.directionX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0;
18901        this.directionY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0;
18902
18903        if ( !this.moved ) {
18904            this._execEvent('scrollStart');
18905        }
18906
18907        this.moved = true;
18908
18909        this._translate(newX, newY);
18910
18911/* REPLACE START: _move */
18912
18913        if ( timestamp - this.startTime > 300 ) {
18914            this.startTime = timestamp;
18915            this.startX = this.x;
18916            this.startY = this.y;
18917        }
18918
18919/* REPLACE END: _move */
18920
18921    },
18922
18923    _end: function (e) {
18924        if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) {
18925            return;
18926        }
18927
18928        if ( this.options.preventDefault && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) {
18929            e.preventDefault();
18930        }
18931
18932        var point = e.changedTouches ? e.changedTouches[0] : e,
18933            momentumX,
18934            momentumY,
18935            duration = utils.getTime() - this.startTime,
18936            newX = Math.round(this.x),
18937            newY = Math.round(this.y),
18938            distanceX = Math.abs(newX - this.startX),
18939            distanceY = Math.abs(newY - this.startY),
18940            time = 0,
18941            easing = '';
18942
18943        this.isInTransition = 0;
18944        this.initiated = 0;
18945        this.endTime = utils.getTime();
18946
18947        // reset if we are outside of the boundaries
18948        if ( this.resetPosition(this.options.bounceTime) ) {
18949            return;
18950        }
18951
18952        this.scrollTo(newX, newY);  // ensures that the last position is rounded
18953
18954        // we scrolled less than 10 pixels
18955        if ( !this.moved ) {
18956            if ( this.options.tap ) {
18957                utils.tap(e, this.options.tap);
18958            }
18959
18960            if ( this.options.click ) {
18961                utils.click(e);
18962            }
18963
18964            this._execEvent('scrollCancel');
18965            return;
18966        }
18967
18968        if ( this._events.flick && duration < 200 && distanceX < 100 && distanceY < 100 ) {
18969            this._execEvent('flick');
18970            return;
18971        }
18972
18973        // start momentum animation if needed
18974        if ( this.options.momentum && duration < 300 ) {
18975            momentumX = this.hasHorizontalScroll ? utils.momentum(this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperW
18975idth : 0, this.options.deceleration) : { destination: newX, duration: 0 };
18976            momentumY = this.hasVerticalScroll ? utils.momentum(this.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0, this.options.deceleration) : { destination: newY, duration: 0 };
18977            newX = momentumX.destination;
18978            newY = momentumY.destination;
18979            time = Math.max(momentumX.duration, momentumY.duration);
18980            this.isInTransition = 1;
18981        }
18982
18983
18984        if ( this.options.snap ) {
18985            var snap = this._nearestSnap(newX, newY);
18986            this.currentPage = snap;
18987            time = this.options.snapSpeed || Math.max(
18988                    Math.max(
18989                        Math.min(Math.abs(newX - snap.x), 1000),
18990                        Math.min(Math.abs(newY - snap.y), 1000)
18991                    ), 300);
18992            newX = snap.x;
18993            newY = snap.y;
18994
18995            this.directionX = 0;
18996            this.directionY = 0;
18997            easing = this.options.bounceEasing;
18998        }
18999
19000// INSERT POINT: _end
19001
19002        if ( newX != this.x || newY != this.y ) {
19003            // change easing function when scroller goes out of the boundaries
19004            if ( newX > 0 || newX < this.maxScrollX || newY > 0 || newY < this.maxScrollY ) {
19005                easing = utils.ease.quadratic;
19006            }
19007
19008            this.scrollTo(newX, newY, time, easing);
19009            return;
19010        }
19011
19012        this._execEvent('scrollEnd');
19013    },
19014
19015    _resize: function () {
19016        var that = this;
19017
19018        clearTimeout(this.resizeTimeout);
19019
19020        this.resizeTimeout = setTimeout(function () {
19021            that.refresh();
19022        }, this.options.resizePolling);
19023    },
19024
19025    resetPosition: function (time) {
19026        var x = this.x,
19027            y = this.y;
19028
19029        time = time || 0;
19030
19031        if ( !this.hasHorizontalScroll || this.x > 0 ) {
19032            x = 0;
19033        } else if ( this.x < this.maxScrollX ) {
19034            x = this.maxScrollX;
19035        }
19036
19037        if ( !this.hasVerticalScroll || this.y > 0 ) {
19038            y = 0;
19039        } else if ( this.y < this.maxScrollY ) {
19040            y = this.maxScrollY;
19041        }
19042
19043        if ( x == this.x && y == this.y ) {
19044            return false;
19045        }
19046
19047        this.scrollTo(x, y, time, this.options.bounceEasing);
19048
19049        return true;
19050    },
19051
19052    disable: function () {
19053        this.enabled = false;
19054    },
19055
19056    enable: function () {
19057        this.enabled = true;
19058    },
19059
19060    refresh: function () {
19061        var rf = this.wrapper.offsetHeight;     // Force reflow
19062
19063        this.wrapperWidth   = this.wrapper.clientWidth;
19064        this.wrapperHeight  = this.wrapper.clientHeight;
19065
19066/* REPLACE START: refresh */
19067
19068        this.scrollerWidth  = this.scroller.offsetWidth;
19069        this.scrollerHeight = this.scroller.offsetHeight;
19070
19071        this.maxScrollX     = this.wrapperWidth - this.scrollerWidth;
19072        this.maxScrollY     = this.wrapperHeight - this.scrollerHeight;
19073
19074/* REPLACE END: refresh */
19075
19076        this.hasHorizontalScroll    = this.options.scrollX && this.maxScrollX < 0;
19077        this.hasVerticalScroll      = this.options.scrollY && this.maxScrollY < 0;
19078
19079        if ( !this.hasHorizontalScroll ) {
19080            this.maxScrollX = 0;
19081            this.scrollerWidth = this.wrapperWidth;
19082        }
19083
19084        if ( !this.hasVerticalScroll ) {
19085            this.maxScrollY = 0;
19086            this.scrollerHeight = this.wrapperHeight;
19087        }
19088
19089        this.endTime = 0;
19090        this.directionX = 0;
19091        this.directionY = 0;
19092
19093        this.wrapperOffset = utils.offset(this.wrapper);
19094
19095        this._execEvent('refresh');
19096
19097        this.resetPosition();
19098
19099// INSERT POINT: _refresh
19100
19101    },
19102
19103    on: function (type, fn) {
19104        if ( !this._events[type] ) {
19105            this._events[type] = [];
19106        }
19107
19108        this._events[type].push(fn);
19109    },
19110
19111    off: function (type, fn) {
19112        if ( !this._events[type] ) {
19113            return;
19114        }
19115
19116        var index = this._events[type].indexOf(fn);
19117
19118        if ( index > -1 ) {
19119            this._events[type].splice(index, 1);
19120        }
19121    },
19122
19123    _execEvent: function (type) {
19124        if ( !this._events[type] ) {
19125            return;
19126        }
19127
19128        var i = 0,
19129            l = this._events[type].length;
19130
19131        if ( !l ) {
19132            return;
19133        }
19134
19135        for ( ; i < l; i++ ) {
19136            this._events[type][i].apply(this, [].slice.call(arguments, 1));
19137        }
19138    },
19139
19140    scrollBy: function (x, y, time, easing) {
19141        x = this.x + x;
19142        y = this.y + y;
19143        time = time || 0;
19144
19145        this.scrollTo(x, y, time, easing);
19146    },
19147
19148    scrollTo: function (x, y, time, easing) {
19149        easing = easing || utils.ease.circular;
19150
19151        this.isInTransition = this.options.useTransition && time > 0;
19152        var transitionType = this.options.useTransition && easing.style;
19153        if ( !time || transitionType ) {
19154                if(transitionType) {
19155                    this._transitionTimingFunction(easing.style);
19156                    this._transitionTime(time);
19157                }
19158            this._translate(x, y);
19159        } else {
19160            this._animate(x, y, time, easing.fn);
19161        }
19162    },
19163
19164    scrollToElement: function (el, time, offsetX, offsetY, easing) {
19165        el = el.nodeType ? el : this.scroller.querySelector(el);
19166
19167        if ( !el ) {
19168            return;
19169        }
19170
19171        var pos = utils.offset(el);
19172
19173        pos.left -= this.wrapperOffset.left;
19174        pos.top  -= this.wrapperOffset.top;
19175
19176        // if offsetX/Y are true we center the element to the screen
19177        if ( offsetX === true ) {
19178            offsetX = Math.round(el.offsetWidth / 2 - this.wrapper.offsetWidth / 2);
19179        }
19180        if ( offsetY === true ) {
19181            offsetY = Math.round(el.offsetHeight / 2 - this.wrapper.offsetHeight / 2);
19182        }
19183
19184        pos.left -= offsetX || 0;
19185        pos.top  -= offsetY || 0;
19186
19187        pos.left = pos.left > 0 ? 0 : pos.left < this.maxScrollX ? this.maxScrollX : pos.left;
19188        pos.top  = pos.top  > 0 ? 0 : pos.top  < this.maxScrollY ? this.maxScrollY : pos.top;
19189
19190        time = time === undefined || time === null || time === 'auto' ? Math.max(Math.abs(this.x-pos.left), Math.abs(this.y-pos.top)) : time;
19191
19192        this.scrollTo(pos.left, pos.top, time, easing);
19193    },
19194
19195    _transitionTime: function (time) {
19196        if (!this.options.useTransition) {
19197            return;
19198        }
19199        time = time || 0;
19200        var durationProp = utils.style.transitionDuration;
19201        if(!durationProp) {
19202            return;
19203        }
19204
19205        this.scrollerStyle[durationProp] = time + 'ms';
19206
19207        if ( !time && utils.isBadAndroid ) {
19208            this.scrollerStyle[durationProp] = '0.0001ms';
19209            // remove 0.0001ms
19210            var self = this;
19211            rAF(function() {
19212                if(self.scrollerStyle[durationProp] === '0.0001ms') {
19213                    self.scrollerStyle[durationProp] = '0s';
19214                }
19215            });
19216        }
19217
19218
19219        if ( this.indicators ) {
19220            for ( var i = this.indicators.length; i--; ) {
19221                this.indicators[i].transitionTime(time);
19222            }
19223        }
19224
19225
19226// INSERT POINT: _transitionTime
19227
19228    },
19229
19230    _transitionTimingFunction: function (easing) {
19231        this.scrollerStyle[utils.style.transitionTimingFunction] = easing;
19232
19233
19234        if ( this.indicators ) {
19235            for ( var i = this.indicators.length; i--; ) {
19236                this.indicators[i].transitionTimingFunction(easing);
19237            }
19238        }
19239
19240
19241// INSERT POINT: _transitionTimingFunction
19242
19243    },
19244
19245    _translate: function (x, y) {
19246        //Uncode addition
19247        if ( (" " + this.wrapper.className + " ").replace(/[\n\t]/g, " ").indexOf(" no-scrolloverflow ") > -1 )
19248            return false;
19249
19250        if ( this.options.useTransform ) {
19251
19252/* REPLACE START: _translate */
19253
19254            this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ;
19255
19256/* REPLACE END: _translate */
19257
19258        } else {
19259            x = Math.round(x);
19260            y = Math.round(y);
19261            this.scrollerStyle.left = x + 'px';
19262            this.scrollerStyle.top = y + 'px';
19263        }
19264
19265        this.x = x;
19266        this.y = y;
19267
19268
19269        if ( this.indicators ) {
19270            for ( var i = this.indicators.length; i--; ) {
19271                this.indicators[i].updatePosition();
19272            }
19273        }
19274
19275        //Uncode addition
19276        var uncodevent = new CustomEvent('fp-slide-scroll');
19277        window.dispatchEvent(uncodevent);
19278
19279// INSERT POINT: _translate
19280
19281    },
19282
19283    _initEvents: function (remove) {
19284        var eventType = remove ? utils.removeEvent : utils.addEvent,
19285            target = this.options.bindToWrapper ? this.wrapper : window;
19286
19287        eventType(window, 'orientationchange', this);
19288        eventType(window, 'resize', this);
19289
19290        if ( this.options.click ) {
19291            eventType(this.wrapper, 'click', this, true);
19292        }
19293
19294        if ( !this.options.disableMouse ) {
19295            eventType(this.wrapper, 'mousedown', this);
19296            eventType(target, 'mousemove', this);
19297            eventType(target, 'mousecancel', this);
19298            eventType(target, 'mouseup', this);
19299        }
19300
19301        if ( utils.hasPointer && !this.options.disablePointer ) {
19302            eventType(this.wrapper, utils.prefixPointerEvent('pointerdown'), this);
19303            eventType(target, utils.prefixPointerEvent('pointermove'), this);
19304            eventType(target, utils.prefixPointerEvent('pointercancel'), this);
19305            eventType(target, utils.prefixPointerEvent('pointerup'), this);
19306        }
19307
19308        if ( utils.hasTouch && !this.options.disableTouch ) {
19309            eventType(this.wrapper, 'touchstart', this);
19310            eventType(target, 'touchmove', this);
19311            eventType(target, 'touchcancel', this);
19312            eventType(target, 'touchend', this);
19313        }
19314
19315        eventType(this.scroller, 'transitionend', this);
19316        eventType(this.scroller, 'webkitTransitionEnd', this);
19317        eventType(this.scroller, 'oTransitionEnd', this);
19318        eventType(this.scroller, 'MSTransitionEnd', this);
19319    },
19320
19321    getComputedPosition: function () {
19322        var matrix = window.getComputedStyle(this.scroller, null),
19323            x, y;
19324
19325        if ( this.options.useTransform ) {
19326            matrix = matrix[utils.style.transform].split(')')[0].split(', ');
19327            x = +(matrix[12] || matrix[4]);
19328            y = +(matrix[13] || matrix[5]);
19329        } else {
19330            x = +matrix.left.replace(/[^-\d.]/g, '');
19331            y = +matrix.top.replace(/[^-\d.]/g, '');
19332        }
19333
19334        return { x: x, y: y };
19335    },
19336    _initIndicators: function () {
19337        var interactive = this.options.interactiveScrollbars,
19338            customStyle = typeof this.options.scrollbars != 'string',
19339            indicators = [],
19340            indicator;
19341
19342        var that = this;
19343
19344        this.indicators = [];
19345
19346        if ( this.options.scrollbars ) {
19347            // Vertical scrollbar
19348            if ( this.options.scrollY ) {
19349                indicator = {
19350                    el: createDefaultScrollbar('v', interactive, this.options.scrollbars),
19351                    interactive: interactive,
19352                    defaultScrollbars: true,
19353                    customStyle: customStyle,
19354                    resize: this.options.resizeScrollbars,
19355                    shrink: this.options.shrinkScrollbars,
19356                    fade: this.options.fadeScrollbars,
19357                    listenX: false
19358                };
19359
19360                this.wrapper.appendChild(indicator.el);
19361                indicators.push(indicator);
19362            }
19363
19364            // Horizontal scrollbar
19365            if ( this.options.scrollX ) {
19366                indicator = {
19367                    el: createDefaultScrollbar('h', interactive, this.options.scrollbars),
19368                    interactive: interactive,
19369                    defaultScrollbars: true,
19370                    customStyle: customStyle,
19371                    resize: this.options.resizeScrollbars,
19372                    shrink: this.options.shrinkScrollbars,
19373                    fade: this.options.fadeScrollbars,
19374                    listenY: false
19375                };
19376
19377                this.wrapper.appendChild(indicator.el);
19378                indicators.push(indicator);
19379            }
19380        }
19381
19382        if ( this.options.indicators ) {
19383            // TODO: check concat compatibility
19384            indicators = indicators.concat(this.options.indicators);
19385        }
19386
19387        for ( var i = indicators.length; i--; ) {
19388            this.indicators.push( new Indicator(this, indicators[i]) );
19389        }
19390
19391        // TODO: check if we can use array.map (wide compatibility and performance issues)
19392        function _indicatorsMap (fn) {
19393            if (that.indicators) {
19394                for ( var i = that.indicators.length; i--; ) {
19395                    fn.call(that.indicators[i]);
19396                }
19397            }
19398        }
19399
19400        if ( this.options.fadeScrollbars ) {
19401            this.on('scrollEnd', function () {
19402                _indicatorsMap(function () {
19403                    this.fade();
19404                });
19405            });
19406
19407            this.on('scrollCancel', function () {
19408                _indicatorsMap(function () {
19409                    this.fade();
19410                });
19411            });
19412
19413            this.on('scrollStart', function () {
19414                _indicatorsMap(function () {
19415                    this.fade(1);
19416                });
19417            });
19418
19419            this.on('beforeScrollStart', function () {
19420                _indicatorsMap(function () {
19421                    this.fade(1, true);
19422                });
19423            });
19424        }
19425
19426
19427        this.on('refresh', function () {
19428            _indicatorsMap(function () {
19429                this.refresh();
19430            });
19431        });
19432
19433        this.on('destroy', function () {
19434            _indicatorsMap(function () {
19435                this.destroy();
19436            });
19437
19438            delete this.indicators;
19439        });
19440    },
19441
19442    _initWheel: function () {
19443        utils.addEvent(this.wrapper, 'wheel', this);
19444        utils.addEvent(this.wrapper, 'mousewheel', this);
19445        utils.addEvent(this.wrapper, 'DOMMouseScroll', this);
19446
19447        this.on('destroy', function () {
19448            clearTimeout(this.wheelTimeout);
19449            this.wheelTimeout = null;
19450            utils.removeEvent(this.wrapper, 'wheel', this);
19451            utils.removeEvent(this.wrapper, 'mousewheel', this);
19452            utils.removeEvent(this.wrapper, 'DOMMouseScroll', this);
19453        });
19454    },
19455
19456    _wheel: function (e) {
19457        if ( !this.enabled ) {
19458            return;
19459        }
19460
19461        var wheelDeltaX, wheelDeltaY,
19462            newX, newY,
19463            that = this;
19464
19465        if ( this.wheelTimeout === undefined ) {
19466            that._execEvent('scrollStart');
19467        }
19468
19469        // Execute the scrollEnd event after 400ms the wheel stopped scrolling
19470        clearTimeout(this.wheelTimeout);
19471        this.wheelTimeout = setTimeout(function () {
19472            if(!that.options.snap) {
19473                that._execEvent('scrollEnd');
19474            }
19475            that.wheelTimeout = undefined;
19476        }, 400);
19477
19478        if ( 'deltaX' in e ) {
19479            if (e.deltaMode === 1) {
19480                wheelDeltaX = -e.deltaX * this.options.mouseWheelSpeed;
19481                wheelDeltaY = -e.deltaY * this.options.mouseWheelSpeed;
19482            } else {
19483                wheelDeltaX = -e.deltaX;
19484                wheelDeltaY = -e.deltaY;
19485            }
19486        } else if ( 'wheelDeltaX' in e ) {
19487            wheelDeltaX = e.wheelDeltaX / 120 * this.options.mouseWheelSpeed;
19488            wheelDeltaY = e.wheelDeltaY / 120 * this.options.mouseWheelSpeed;
19489        } else if ( 'wheelDelta' in e ) {
19490            wheelDeltaX = wheelDeltaY = e.wheelDelta / 120 * this.options.mouseWheelSpeed;
19491        } else if ( 'detail' in e ) {
19492            wheelDeltaX = wheelDeltaY = -e.detail / 3 * this.options.mouseWheelSpeed;
19493        } else {
19494            return;
19495        }
19496
19497        wheelDeltaX *= this.options.invertWheelDirection;
19498        wheelDeltaY *= this.options.invertWheelDirection;
19499
19500        if ( !this.hasVerticalScroll ) {
19501            wheelDeltaX = wheelDeltaY;
19502            wheelDeltaY = 0;
19503        }
19504
19505        if ( this.options.snap ) {
19506            newX = this.currentPage.pageX;
19507            newY = this.currentPage.pageY;
19508
19509            if ( wheelDeltaX > 0 ) {
19510                newX--;
19511            } else if ( wheelDeltaX < 0 ) {
19512                newX++;
19513            }
19514
19515            if ( wheelDeltaY > 0 ) {
19516                newY--;
19517            } else if ( wheelDeltaY < 0 ) {
19518                newY++;
19519            }
19520
19521            this.goToPage(newX, newY);
19522
19523            return;
19524        }
19525
19526        newX = this.x + Math.round(this.hasHorizontalScroll ? wheelDeltaX : 0);
19527        newY = this.y + Math.round(this.hasVerticalScroll ? wheelDeltaY : 0);
19528
19529        this.directionX = wheelDeltaX > 0 ? -1 : wheelDeltaX < 0 ? 1 : 0;
19530        this.directionY = wheelDeltaY > 0 ? -1 : wheelDeltaY < 0 ? 1 : 0;
19531
19532        if ( newX > 0 ) {
19533            newX = 0;
19534        } else if ( newX < this.maxScrollX ) {
19535            newX = this.maxScrollX;
19536        }
19537
19538        if ( newY > 0 ) {
19539            newY = 0;
19540        } else if ( newY < this.maxScrollY ) {
19541            newY = this.maxScrollY;
19542        }
19543
19544        this.scrollTo(newX, newY, 0);
19545
19546// INSERT POINT: _wheel
19547    },
19548
19549    _initSnap: function () {
19550        this.currentPage = {};
19551
19552        if ( typeof this.options.snap == 'string' ) {
19553            this.options.snap = this.scroller.querySelectorAll(this.options.snap);
19554        }
19555
19556        this.on('refresh', function () {
19557            var i = 0, l,
19558                m = 0, n,
19559                cx, cy,
19560                x = 0, y,
19561                stepX = this.options.snapStepX || this.wrapperWidth,
19562                stepY = this.options.snapStepY || this.wrapperHeight,
19563                el;
19564
19565            this.pages = [];
19566
19567            if ( !this.wrapperWidth || !this.wrapperHeight || !this.scrollerWidth || !this.scrollerHeight ) {
19568                return;
19569            }
19570
19571            if ( this.options.snap === true ) {
19572                cx = Math.round( stepX / 2 );
19573                cy = Math.round( stepY / 2 );
19574
19575                while ( x > -this.scrollerWidth ) {
19576                    this.pages[i] = [];
19577                    l = 0;
19578                    y = 0;
19579
19580                    while ( y > -this.scrollerHeight ) {
19581                        this.pages[i][l] = {
19582                            x: Math.max(x, this.maxScrollX),
19583                            y: Math.max(y, this.maxScrollY),
19584                            width: stepX,
19585                            height: stepY,
19586                            cx: x - cx,
19587                            cy: y - cy
19588                        };
19589
19590                        y -= stepY;
19591                        l++;
19592                    }
19593
19594                    x -= stepX;
19595                    i++;
19596                }
19597            } else {
19598                el = this.options.snap;
19599                l = el.length;
19600                n = -1;
19601
19602                for ( ; i < l; i++ ) {
19603                    if ( i === 0 || el[i].offsetLeft <= el[i-1].offsetLeft ) {
19604                        m = 0;
19605                        n++;
19606                    }
19607
19608                    if ( !this.pages[m] ) {
19609                        this.pages[m] = [];
19610                    }
19611
19612                    x = Math.max(-el[i].offsetLeft, this.maxScrollX);
19613                    y = Math.max(-el[i].offsetTop, this.maxScrollY);
19614                    cx = x - Math.round(el[i].offsetWidth / 2);
19615                    cy = y - Math.round(el[i].offsetHeight / 2);
19616
19617                    this.pages[m][n] = {
19618                        x: x,
19619                        y: y,
19620                        width: el[i].offsetWidth,
19621                        height: el[i].offsetHeight,
19622                        cx: cx,
19623                        cy: cy
19624                    };
19625
19626                    if ( x > this.maxScrollX ) {
19627                        m++;
19628                    }
19629                }
19630            }
19631
19632            this.goToPage(this.currentPage.pageX || 0, this.currentPage.pageY || 0, 0);
19633
19634            // Update snap threshold if needed
19635            if ( this.options.snapThreshold % 1 === 0 ) {
19636                this.snapThresholdX = this.options.snapThreshold;
19637                this.snapThresholdY = this.options.snapThreshold;
19638            } else {
19639                this.snapThresholdX = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].width * this.options.snapThreshold);
19640                this.snapThresholdY = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].height * this.options.snapThreshold);
19641            }
19642        });
19643
19644        this.on('flick', function () {
19645            var time = this.options.snapSpeed || Math.max(
19646                    Math.max(
19647                        Math.min(Math.abs(this.x - this.startX), 1000),
19648                        Math.min(Math.abs(this.y - this.startY), 1000)
19649                    ), 300);
19650
19651            this.goToPage(
19652                this.currentPage.pageX + this.directionX,
19653                this.currentPage.pageY + this.directionY,
19654                time
19655            );
19656        });
19657    },
19658
19659    _nearestSnap: function (x, y) {
19660        if ( !this.pages.length ) {
19661            return { x: 0, y: 0, pageX: 0, pageY: 0 };
19662        }
19663
19664        var i = 0,
19665            l = this.pages.length,
19666            m = 0;
19667
19668        // Check if we exceeded the snap threshold
19669        if ( Math.abs(x - this.absStartX) < this.snapThresholdX &&
19670            Math.abs(y - this.absStartY) < this.snapThresholdY ) {
19671            return this.currentPage;
19672        }
19673
19674        if ( x > 0 ) {
19675            x = 0;
19676        } else if ( x < this.maxScrollX ) {
19677            x = this.maxScrollX;
19678        }
19679
19680        if ( y > 0 ) {
19681            y = 0;
19682        } else if ( y < this.maxScrollY ) {
19683            y = this.maxScrollY;
19684        }
19685
19686        for ( ; i < l; i++ ) {
19687            if ( x >= this.pages[i][0].cx ) {
19688                x = this.pages[i][0].x;
19689                break;
19690            }
19691        }
19692
19693        l = this.pages[i].length;
19694
19695        for ( ; m < l; m++ ) {
19696            if ( y >= this.pages[0][m].cy ) {
19697                y = this.pages[0][m].y;
19698                break;
19699            }
19700        }
19701
19702        if ( i == this.currentPage.pageX ) {
19703            i += this.directionX;
19704
19705            if ( i < 0 ) {
19706                i = 0;
19707            } else if ( i >= this.pages.length ) {
19708                i = this.pages.length - 1;
19709            }
19710
19711            x = this.pages[i][0].x;
19712        }
19713
19714        if ( m == this.currentPage.pageY ) {
19715            m += this.directionY;
19716
19717            if ( m < 0 ) {
19718                m = 0;
19719            } else if ( m >= this.pages[0].length ) {
19720                m = this.pages[0].length - 1;
19721            }
19722
19723            y = this.pages[0][m].y;
19724        }
19725
19726        return {
19727            x: x,
19728            y: y,
19729            pageX: i,
19730            pageY: m
19731        };
19732    },
19733
19734    goToPage: function (x, y, time, easing) {
19735        easing = easing || this.options.bounceEasing;
19736
19737        if ( x >= this.pages.length ) {
19738            x = this.pages.length - 1;
19739        } else if ( x < 0 ) {
19740            x = 0;
19741        }
19742
19743        if ( y >= this.pages[x].length ) {
19744            y = this.pages[x].length - 1;
19745        } else if ( y < 0 ) {
19746            y = 0;
19747        }
19748
19749        var posX = this.pages[x][y].x,
19750            posY = this.pages[x][y].y;
19751
19752        time = time === undefined ? this.options.snapSpeed || Math.max(
19753            Math.max(
19754                Math.min(Math.abs(posX - this.x), 1000),
19755                Math.min(Math.abs(posY - this.y), 1000)
19756            ), 300) : time;
19757
19758        this.currentPage = {
19759            x: posX,
19760            y: posY,
19761            pageX: x,
19762            pageY: y
19763        };
19764
19765        this.scrollTo(posX, posY, time, easing);
19766    },
19767
19768    next: function (time, easing) {
19769        var x = this.currentPage.pageX,
19770            y = this.currentPage.pageY;
19771
19772        x++;
19773
19774        if ( x >= this.pages.length && this.hasVerticalScroll ) {
19775            x = 0;
19776            y++;
19777        }
19778
19779        this.goToPage(x, y, time, easing);
19780    },
19781
19782    prev: function (time, easing) {
19783        var x = this.currentPage.pageX,
19784            y = this.currentPage.pageY;
19785
19786        x--;
19787
19788        if ( x < 0 && this.hasVerticalScroll ) {
19789            x = 0;
19790            y--;
19791        }
19792
19793        this.goToPage(x, y, time, easing);
19794    },
19795
19796    _initKeys: function (e) {
19797        // default key bindings
19798        var keys = {
19799            pageUp: 33,
19800            pageDown: 34,
19801            end: 35,
19802            home: 36,
19803            left: 37,
19804            up: 38,
19805            right: 39,
19806            down: 40
19807        };
19808        var i;
19809
19810        // if you give me characters I give you keycode
19811        if ( typeof this.options.keyBindings == 'object' ) {
19812            for ( i in this.options.keyBindings ) {
19813                if ( typeof this.options.keyBindings[i] == 'string' ) {
19814                    this.options.keyBindings[i] = this.options.keyBindings[i].toUpperCase().charCo
19814deAt(0);
19815                }
19816            }
19817        } else {
19818            this.options.keyBindings = {};
19819        }
19820
19821        for ( i in keys ) {
19822            this.options.keyBindings[i] = this.options.keyBindings[i] || keys[i];
19823        }
19824
19825        utils.addEvent(window, 'keydown', this);
19826
19827        this.on('destroy', function () {
19828            utils.removeEvent(window, 'keydown', this);
19829        });
19830    },
19831
19832    _key: function (e) {
19833        if ( !this.enabled ) {
19834            return;
19835        }
19836
19837        var snap = this.options.snap,   // we are using this alot, better to cache it
19838            newX = snap ? this.currentPage.pageX : this.x,
19839            newY = snap ? this.currentPage.pageY : this.y,
19840            now = utils.getTime(),
19841            prevTime = this.keyTime || 0,
19842            acceleration = 0.250,
19843            pos;
19844
19845        if ( this.options.useTransition && this.isInTransition ) {
19846            pos = this.getComputedPosition();
19847
19848            this._translate(Math.round(pos.x), Math.round(pos.y));
19849            this.isInTransition = false;
19850        }
19851
19852        this.keyAcceleration = now - prevTime < 200 ? Math.min(this.keyAcceleration + acceleration, 50) : 0;
19853
19854        switch ( e.keyCode ) {
19855            case this.options.keyBindings.pageUp:
19856                if ( this.hasHorizontalScroll && !this.hasVerticalScroll ) {
19857                    newX += snap ? 1 : this.wrapperWidth;
19858                } else {
19859                    newY += snap ? 1 : this.wrapperHeight;
19860                }
19861                break;
19862            case this.options.keyBindings.pageDown:
19863                if ( this.hasHorizontalScroll && !this.hasVerticalScroll ) {
19864                    newX -= snap ? 1 : this.wrapperWidth;
19865                } else {
19866                    newY -= snap ? 1 : this.wrapperHeight;
19867                }
19868                break;
19869            case this.options.keyBindings.end:
19870                newX = snap ? this.pages.length-1 : this.maxScrollX;
19871                newY = snap ? this.pages[0].length-1 : this.maxScrollY;
19872                break;
19873            case this.options.keyBindings.home:
19874                newX = 0;
19875                newY = 0;
19876                break;
19877            case this.options.keyBindings.left:
19878                newX += snap ? -1 : 5 + this.keyAcceleration>>0;
19879                break;
19880            case this.options.keyBindings.up:
19881                newY += snap ? 1 : 5 + this.keyAcceleration>>0;
19882                break;
19883            case this.options.keyBindings.right:
19884                newX -= snap ? -1 : 5 + this.keyAcceleration>>0;
19885                break;
19886            case this.options.keyBindings.down:
19887                newY -= snap ? 1 : 5 + this.keyAcceleration>>0;
19888                break;
19889            default:
19890                return;
19891        }
19892
19893        if ( snap ) {
19894            this.goToPage(newX, newY);
19895            return;
19896        }
19897
19898        if ( newX > 0 ) {
19899            newX = 0;
19900            this.keyAcceleration = 0;
19901        } else if ( newX < this.maxScrollX ) {
19902            newX = this.maxScrollX;
19903            this.keyAcceleration = 0;
19904        }
19905
19906        if ( newY > 0 ) {
19907            newY = 0;
19908            this.keyAcceleration = 0;
19909        } else if ( newY < this.maxScrollY ) {
19910            newY = this.maxScrollY;
19911            this.keyAcceleration = 0;
19912        }
19913
19914        this.scrollTo(newX, newY, 0);
19915
19916        this.keyTime = now;
19917    },
19918
19919    _animate: function (destX, destY, duration, easingFn) {
19920        var that = this,
19921            startX = this.x,
19922            startY = this.y,
19923            startTime = utils.getTime(),
19924            destTime = startTime + duration;
19925
19926        function step () {
19927            var now = utils.getTime(),
19928                newX, newY,
19929                easing;
19930
19931            if ( now >= destTime ) {
19932                that.isAnimating = false;
19933                that._translate(destX, destY);
19934
19935                if ( !that.resetPosition(that.options.bounceTime) ) {
19936                    that._execEvent('scrollEnd');
19937                }
19938
19939                return;
19940            }
19941
19942            now = ( now - startTime ) / duration;
19943            easing = easingFn(now);
19944            newX = ( destX - startX ) * easing + startX;
19945            newY = ( destY - startY ) * easing + startY;
19946            that._translate(newX, newY);
19947
19948            if ( that.isAnimating ) {
19949                rAF(step);
19950            }
19951        }
19952
19953        this.isAnimating = true;
19954        step();
19955    },
19956    handleEvent: function (e) {
19957        switch ( e.type ) {
19958            case 'touchstart':
19959            case 'pointerdown':
19960            case 'MSPointerDown':
19961            case 'mousedown':
19962                this._start(e);
19963                break;
19964            case 'touchmove':
19965            case 'pointermove':
19966            case 'MSPointerMove':
19967            case 'mousemove':
19968                this._move(e);
19969                break;
19970            case 'touchend':
19971            case 'pointerup':
19972            case 'MSPointerUp':
19973            case 'mouseup':
19974            case 'touchcancel':
19975            case 'pointercancel':
19976            case 'MSPointerCancel':
19977            case 'mousecancel':
19978                this._end(e);
19979                break;
19980            case 'orientationchange':
19981            case 'resize':
19982                this._resize();
19983                break;
19984            case 'transitionend':
19985            case 'webkitTransitionEnd':
19986            case 'oTransitionEnd':
19987            case 'MSTransitionEnd':
19988                this._transitionEnd(e);
19989                break;
19990            case 'wheel':
19991            case 'DOMMouseScroll':
19992            case 'mousewheel':
19993                this._wheel(e);
19994                break;
19995            case 'keydown':
19996                this._key(e);
19997                break;
19998            case 'click':
19999                if ( this.enabled && !e._constructed ) {
20000                    e.preventDefault();
20001                    e.stopPropagation();
20002                }
20003                break;
20004        }
20005    }
20006};
20007function createDefaultScrollbar (direction, interactive, type) {
20008    var scrollbar = document.createElement('div'),
20009        indicator = document.createElement('div');
20010
20011    if ( type === true ) {
20012        scrollbar.style.cssText = 'position:absolute;z-index:9999';
20013        indicator.style.cssText = '-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;position:absolute;background:rgba(0,0,0,0.5);border:1px solid rgba(255,255,255,0.9);border-radius:3px';
20014    }
20015
20016    indicator.className = 'iScrollIndicator';
20017
20018    if ( direction == 'h' ) {
20019        if ( type === true ) {
20020            scrollbar.style.cssText += ';height:7px;left:2px;right:2px;bottom:0';
20021            indicator.style.height = '100%';
20022        }
20023        scrollbar.className = 'iScrollHorizontalScrollbar';
20024    } else {
20025        if ( type === true ) {
20026            scrollbar.style.cssText += ';width:7px;bottom:2px;top:2px;right:1px';
20027            indicator.style.width = '100%';
20028        }
20029        scrollbar.className = 'iScrollVerticalScrollbar';
20030    }
20031
20032    scrollbar.style.cssText += ';overflow:hidden';
20033
20034    if ( !interactive ) {
20035        scrollbar.style.pointerEvents = 'none';
20036    }
20037
20038    scrollbar.appendChild(indicator);
20039
20040    return scrollbar;
20041}
20042
20043function Indicator (scroller, options) {
20044    this.wrapper = typeof options.el == 'string' ? document.querySelector(options.el) : options.el;
20045    this.wrapperStyle = this.wrapper.style;
20046    this.indicator = this.wrapper.children[0];
20047    this.indicatorStyle = this.indicator.style;
20048    this.scroller = scroller;
20049
20050    this.options = {
20051        listenX: true,
20052        listenY: true,
20053        interactive: false,
20054        resize: true,
20055        defaultScrollbars: false,
20056        shrink: false,
20057        fade: false,
20058        speedRatioX: 0,
20059        speedRatioY: 0
20060    };
20061
20062    for ( var i in options ) {
20063        this.options[i] = options[i];
20064    }
20065
20066    this.sizeRatioX = 1;
20067    this.sizeRatioY = 1;
20068    this.maxPosX = 0;
20069    this.maxPosY = 0;
20070
20071    if ( this.options.interactive ) {
20072        if ( !this.options.disableTouch ) {
20073            utils.addEvent(this.indicator, 'touchstart', this);
20074            utils.addEvent(window, 'touchend', this);
20075        }
20076        if ( !this.options.disablePointer ) {
20077            utils.addEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this);
20078            utils.addEvent(window, utils.prefixPointerEvent('pointerup'), this);
20079        }
20080        if ( !this.options.disableMouse ) {
20081            utils.addEvent(this.indicator, 'mousedown', this);
20082            utils.addEvent(window, 'mouseup', this);
20083        }
20084    }
20085
20086    if ( this.options.fade ) {
20087        this.wrapperStyle[utils.style.transform] = this.scroller.translateZ;
20088        var durationProp = utils.style.transitionDuration;
20089        if(!durationProp) {
20090            return;
20091        }
20092        this.wrapperStyle[durationProp] = utils.isBadAndroid ? '0.0001ms' : '0ms';
20093        // remove 0.0001ms
20094        var self = this;
20095        if(utils.isBadAndroid) {
20096            rAF(function() {
20097                if(self.wrapperStyle[durationProp] === '0.0001ms') {
20098                    self.wrapperStyle[durationProp] = '0s';
20099                }
20100            });
20101        }
20102        this.wrapperStyle.opacity = '0';
20103    }
20104}
20105
20106Indicator.prototype = {
20107    handleEvent: function (e) {
20108        switch ( e.type ) {
20109            case 'touchstart':
20110            case 'pointerdown':
20111            case 'MSPointerDown':
20112            case 'mousedown':
20113                this._start(e);
20114                break;
20115            case 'touchmove':
20116            case 'pointermove':
20117            case 'MSPointerMove':
20118            case 'mousemove':
20119                this._move(e);
20120                break;
20121            case 'touchend':
20122            case 'pointerup':
20123            case 'MSPointerUp':
20124            case 'mouseup':
20125            case 'touchcancel':
20126            case 'pointercancel':
20127            case 'MSPointerCancel':
20128            case 'mousecancel':
20129                this._end(e);
20130                break;
20131        }
20132    },
20133
20134    destroy: function () {
20135        if ( this.options.fadeScrollbars ) {
20136            clearTimeout(this.fadeTimeout);
20137            this.fadeTimeout = null;
20138        }
20139        if ( this.options.interactive ) {
20140            utils.removeEvent(this.indicator, 'touchstart', this);
20141            utils.removeEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this);
20142            utils.removeEvent(this.indicator, 'mousedown', this);
20143
20144            utils.removeEvent(window, 'touchmove', this);
20145            utils.removeEvent(window, utils.prefixPointerEvent('pointermove'), this);
20146            utils.removeEvent(window, 'mousemove', this);
20147
20148            utils.removeEvent(window, 'touchend', this);
20149            utils.removeEvent(window, utils.prefixPointerEvent('pointerup'), this);
20150            utils.removeEvent(window, 'mouseup', this);
20151        }
20152
20153        if ( this.options.defaultScrollbars ) {
20154            this.wrapper.parentNode.removeChild(this.wrapper);
20155        }
20156    },
20157
20158    _start: function (e) {
20159        var point = e.touches ? e.touches[0] : e;
20160
20161        e.preventDefault();
20162        e.stopPropagation();
20163
20164        this.transitionTime();
20165
20166        this.initiated = true;
20167        this.moved = false;
20168        this.lastPointX = point.pageX;
20169        this.lastPointY = point.pageY;
20170
20171        this.startTime  = utils.getTime();
20172
20173        if ( !this.options.disableTouch ) {
20174            utils.addEvent(window, 'touchmove', this);
20175        }
20176        if ( !this.options.disablePointer ) {
20177            utils.addEvent(window, utils.prefixPointerEvent('pointermove'), this);
20178        }
20179        if ( !this.options.disableMouse ) {
20180            utils.addEvent(window, 'mousemove', this);
20181        }
20182
20183        this.scroller._execEvent('beforeScrollStart');
20184    },
20185
20186    _move: function (e) {
20187        var point = e.touches ? e.touches[0] : e,
20188            deltaX, deltaY,
20189            newX, newY,
20190            timestamp = utils.getTime();
20191
20192        if ( !this.moved ) {
20193            this.scroller._execEvent('scrollStart');
20194        }
20195
20196        this.moved = true;
20197
20198        deltaX = point.pageX - this.lastPointX;
20199        this.lastPointX = point.pageX;
20200
20201        deltaY = point.pageY - this.lastPointY;
20202        this.lastPointY = point.pageY;
20203
20204        newX = this.x + deltaX;
20205        newY = this.y + deltaY;
20206
20207        this._pos(newX, newY);
20208
20209// INSERT POINT: indicator._move
20210
20211        e.preventDefault();
20212        e.stopPropagation();
20213    },
20214
20215    _end: function (e) {
20216        if ( !this.initiated ) {
20217            return;
20218        }
20219
20220        this.initiated = false;
20221
20222        e.preventDefault();
20223        e.stopPropagation();
20224
20225        utils.removeEvent(window, 'touchmove', this);
20226        utils.removeEvent(window, utils.prefixPointerEvent('pointermove'), this);
20227        utils.removeEvent(window, 'mousemove', this);
20228
20229        if ( this.scroller.options.snap ) {
20230            var snap = this.scroller._nearestSnap(this.scroller.x, this.scroller.y);
20231
20232            var time = this.options.snapSpeed || Math.max(
20233                    Math.max(
20234                        Math.min(Math.abs(this.scroller.x - snap.x), 1000),
20235                        Math.min(Math.abs(this.scroller.y - snap.y), 1000)
20236                    ), 300);
20237
20238            if ( this.scroller.x != snap.x || this.scroller.y != snap.y ) {
20239                this.scroller.directionX = 0;
20240                this.scroller.directionY = 0;
20241                this.scroller.currentPage = snap;
20242                this.scroller.scrollTo(snap.x, snap.y, time, this.scroller.options.bounceEasing);
20243            }
20244        }
20245
20246        if ( this.moved ) {
20247            this.scroller._execEvent('scrollEnd');
20248        }
20249    },
20250
20251    transitionTime: function (time) {
20252        time = time || 0;
20253        var durationProp = utils.style.transitionDuration;
20254        if(!durationProp) {
20255            return;
20256        }
20257
20258        this.indicatorStyle[durationProp] = time + 'ms';
20259
20260        if ( !time && utils.isBadAndroid ) {
20261            this.indicatorStyle[durationProp] = '0.0001ms';
20262            // remove 0.0001ms
20263            var self = this;
20264            rAF(function() {
20265                if(self.indicatorStyle[durationProp] === '0.0001ms') {
20266                    self.indicatorStyle[durationProp] = '0s';
20267                }
20268            });
20269        }
20270    },
20271
20272    transitionTimingFunction: function (easing) {
20273        this.indicatorStyle[utils.style.transitionTimingFunction] = easing;
20274    },
20275
20276    refresh: function () {
20277        this.transitionTime();
20278
20279        if ( this.options.listenX && !this.options.listenY ) {
20280            this.indicatorStyle.display = this.scroller.hasHorizontalScroll ? 'block' : 'none';
20281        } else if ( this.options.listenY && !this.options.listenX ) {
20282            this.indicatorStyle.display = this.scroller.hasVerticalScroll ? 'block' : 'none';
20283        } else {
20284            this.indicatorStyle.display = this.scroller.hasHorizontalScroll || this.scroller.hasVerticalScroll ? 'block' : 'none';
20285        }
20286
20287        if ( this.scroller.hasHorizontalScroll && this.scroller.hasVerticalScroll ) {
20288            utils.addClass(this.wrapper, 'iScrollBothScrollbars');
20289            utils.removeClass(this.wrapper, 'iScrollLoneScrollbar');
20290
20291            if ( this.options.defaultScrollbars && this.options.customStyle ) {
20292                if ( this.options.listenX ) {
20293                    this.wrapper.style.right = '8px';
20294                } else {
20295                    this.wrapper.style.bottom = '8px';
20296                }
20297            }
20298        } else {
20299            utils.removeClass(this.wrapper, 'iScrollBothScrollbars');
20300            utils.addClass(this.wrapper, 'iScrollLoneScrollbar');
20301
20302            if ( this.options.defaultScrollbars && this.options.customStyle ) {
20303                if ( this.options.listenX ) {
20304                    this.wrapper.style.right = '2px';
20305                } else {
20306                    this.wrapper.style.bottom = '2px';
20307                }
20308            }
20309        }
20310
20311        var r = this.wrapper.offsetHeight;  // force refresh
20312
20313        if ( this.options.listenX ) {
20314            this.wrapperWidth = this.wrapper.clientWidth;
20315            if ( this.options.resize ) {
20316                this.indicatorWidth = Math.max(Math.round(this.wrapperWidth * this.wrapperWidth / (this.scroller.scrollerWidth || this.wrapperWidth || 1)), 8);
20317                this.indicatorStyle.width = this.indicatorWidth + 'px';
20318            } else {
20319                this.indicatorWidth = this.indicator.clientWidth;
20320            }
20321
20322            this.maxPosX = this.wrapperWidth - this.indicatorWidth;
20323
20324            if ( this.options.shrink == 'clip' ) {
20325                this.minBoundaryX = -this.indicatorWidth + 8;
20326                this.maxBoundaryX = this.wrapperWidth - 8;
20327            } else {
20328                this.minBoundaryX = 0;
20329                this.maxBoundaryX = this.maxPosX;
20330            }
20331
20332            this.sizeRatioX = this.options.speedRatioX || (this.scroller.maxScrollX && (this.maxPosX / this.scroller.maxScrollX));
20333        }
20334
20335        if ( this.options.listenY ) {
20336            this.wrapperHeight = this.wrapper.clientHeight;
20337            if ( this.options.resize ) {
20338                this.indicatorHeight = Math.max(Math.round(this.wrapperHeight * this.wrapperHeight / (this.scroller.scrollerHeight || this.wrapperHeight || 1)), 8);
20339                this.indicatorStyle.height = this.indicatorHeight + 'px';
20340            } else {
20341                this.indicatorHeight = this.indicator.clientHeight;
20342            }
20343
20344            this.maxPosY = this.wrapperHeight - this.indicatorHeight;
20345
20346            if ( this.options.shrink == 'clip' ) {
20347                this.minBoundaryY = -this.indicatorHeight + 8;
20348                this.maxBoundaryY = this.wrapperHeight - 8;
20349            } else {
20350                this.minBoundaryY = 0;
20351                this.maxBoundaryY = this.maxPosY;
20352            }
20353
20354            this.maxPosY = this.wrapperHeight - this.indicatorHeight;
20355            this.sizeRatioY = this.options.speedRatioY || (this.scroller.maxScrollY && (this.maxPosY / this.scroller.maxScrollY));
20356        }
20357
20358        this.updatePosition();
20359    },
20360
20361    updatePosition: function () {
20362        var x = this.options.listenX && Math.round(this.sizeRatioX * this.scroller.x) || 0,
20363            y = this.options.listenY && Math.round(this.sizeRatioY * this.scroller.y) || 0;
20364
20365        if ( !this.options.ignoreBoundaries ) {
20366            if ( x < this.minBoundaryX ) {
20367                if ( this.options.shrink == 'scale' ) {
20368                    this.width = Math.max(this.indicatorWidth + x, 8);
20369                    this.indicatorStyle.width = this.width + 'px';
20370                }
20371                x = this.minBoundaryX;
20372            } else if ( x > this.maxBoundaryX ) {
20373                if ( this.options.shrink == 'scale' ) {
20374                    this.width = Math.max(this.indicatorWidth - (x - this.maxPosX), 8);
20375                    this.indicatorStyle.width = this.width + 'px';
20376                    x = this.maxPosX + this.indicatorWidth - this.width;
20377                } else {
20378                    x = this.maxBoundaryX;
20379                }
20380            } else if ( this.options.shrink == 'scale' && this.width != this.indicatorWidth ) {
20381                this.width = this.indicatorWidth;
20382                this.indicatorStyle.width = this.width + 'px';
20383            }
20384
20385            if ( y < this.minBoundaryY ) {
20386                if ( this.options.shrink == 'scale' ) {
20387                    this.height = Math.max(this.indicatorHeight + y * 3, 8);
20388                    this.indicatorStyle.height = this.height + 'px';
20389                }
20390                y = this.minBoundaryY;
20391            } else if ( y > this.maxBoundaryY ) {
20392                if ( this.options.shrink == 'scale' ) {
20393                    this.height = Math.max(this.indicatorHeight - (y - this.maxPosY) * 3, 8);
20394                    this.indicatorStyle.height = this.height + 'px';
20395                    y = this.maxPosY + this.indicatorHeight - this.height;
20396                } else {
20397                    y = this.maxBoundaryY;
20398                }
20399            } else if ( this.options.shrink == 'scale' && this.height != this.indicatorHeight ) {
20400                this.height = this.indicatorHeight;
20401                this.indicatorStyle.height = this.height + 'px';
20402            }
20403        }
20404
20405        this.x = x;
20406        this.y = y;
20407
20408        if ( this.scroller.options.useTransform ) {
20409            this.indicatorStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.scroller.translateZ;
20410        } else {
20411            this.indicatorStyle.left = x + 'px';
20412            this.indicatorStyle.top = y + 'px';
20413        }
20414    },
20415
20416    _pos: function (x, y) {
20417        if ( x < 0 ) {
20418            x = 0;
20419        } else if ( x > this.maxPosX ) {
20420            x = this.maxPosX;
20421        }
20422
20423        if ( y < 0 ) {
20424            y = 0;
20425        } else if ( y > this.maxPosY ) {
20426            y = this.maxPosY;
20427        }
20428
20429        x = this.options.listenX ? Math.round(x / this.sizeRatioX) : this.scroller.x;
20430        y = this.options.listenY ? Math.round(y / this.sizeRatioY) : this.scroller.y;
20431
20432        this.scroller.scrollTo(x, y);
20433    },
20434
20435    fade: function (val, hold) {
20436        if ( hold && !this.visible ) {
20437            return;
20438        }
20439
20440        clearTimeout(this.fadeTimeout);
20441        this.fadeTimeout = null;
20442
20443        var time = val ? 250 : 500,
20444            delay = val ? 0 : 300;
20445
20446        val = val ? '1' : '0';
20447
20448        this.wrapperStyle[utils.style.transitionDuration] = time + 'ms';
20449
20450        this.fadeTimeout = setTimeout((function (val) {
20451            this.wrapperStyle.opacity = val;
20452            this.visible = +val;
20453        }).bind(this, val), delay);
20454    }
20455};
20456
20457IScroll.utils = utils;
20458
20459if ( typeof module != 'undefined' && module.exports ) {
20460    module.exports = IScroll;
20461} else if ( typeof define == 'function' && define.amd ) {
20462        define( function () { return IScroll; } );
20463} else {
20464    window.IScroll = IScroll;
20465}
20466
20467})(window, document, Math);
20468/*!
20469 * fullPage 2.9.4
20470 * https://github.com/alvarotrigo/fullPage.js
20471 * @license MIT licensed
20472 *
20473 * Copyright (C) 2015 alvarotrigo.com - A project by Alvaro Trigo
20474 */
20475(function(global, factory) {
20476    'use strict';
20477    if (typeof define === 'function' && define.amd) {
20478        define(['jquery'], function($) {
20479          return factory($, global, global.document, global.Math);
20480        });
20481    } else if (typeof exports === "object" && exports) {
20482        module.exports = factory(require('jquery'), global, global.document, global.Math);
20483    } else {
20484        factory(jQuery, global, global.document, global.Math);
20485    }
20486})(typeof window !== 'undefined' ? window : this, function($, window, document, Math, undefined) {
20487    'use strict';
20488
20489    // keeping central set of classnames and selectors
20490    var WRAPPER =               'fullpage-wrapper';
20491    var WRAPPER_SEL =           '.' + WRAPPER;
20492
20493    // slimscroll
20494    var SCROLLABLE =            'fp-scrollable';
20495    var SCROLLABLE_SEL =        '.' + SCROLLABLE;
20496
20497    // util
20498    var RESPONSIVE =            'fp-responsive';
20499    var NO_TRANSITION =         'fp-notransition';
20500    var DESTROYED =             'fp-destroyed';
20501    var ENABLED =               'fp-enabled';
20502    var VIEWING_PREFIX =        'fp-viewing';
20503    var ACTIVE =                'active';
20504    var ACTIVE_SEL =            '.' + ACTIVE;
20505    var COMPLETELY =            'fp-completely';
20506    var COMPLETELY_SEL =        '.' + COMPLETELY;
20507
20508    // section
20509    var SECTION_DEFAULT_SEL =   '.section';
20510    var SECTION =               'fp-section';
20511    var SECTION_SEL =           '.' + SECTION;
20512    var SECTION_ACTIVE_SEL =    SECTION_SEL + ACTIVE_SEL;
20513    var SECTION_FIRST_SEL =     SECTION_SEL + ':first';
20514    var SECTION_LAST_SEL =      SECTION_SEL + ':last';
20515    var TABLE_CELL =            'fp-tableCell';
20516    var TABLE_CELL_SEL =        '.' + TABLE_CELL;
20517    var AUTO_HEIGHT =           'fp-auto-height';
20518    var AUTO_HEIGHT_SEL =       '.fp-auto-height';
20519    var NORMAL_SCROLL =         'fp-normal-scroll';
20520    var NORMAL_SCROLL_SEL =     '.fp-normal-scroll';
20521
20522    // section nav
20523    var SECTION_NAV =           'fp-nav';
20524    var SECTION_NAV_SEL =       '#' + SECTION_NAV;
20525    var SECTION_NAV_TOOLTIP =   'fp-tooltip';
20526    var SECTION_NAV_TOOLTIP_SEL='.'+SECTION_NAV_TOOLTIP;
20527    var SHOW_ACTIVE_TOOLTIP =   'fp-show-active';
20528
20529    // slide
20530    var SLIDE_DEFAULT_SEL =     '.slide';
20531    var SLIDE =                 'fp-slide';
20532    var SLIDE_SEL =             '.' + SLIDE;
20533    var SLIDE_ACTIVE_SEL =      SLIDE_SEL + ACTIVE_SEL;
20534    var SLIDES_WRAPPER =        'fp-slides';
20535    var SLIDES_WRAPPER_SEL =    '.' + SLIDES_WRAPPER;
20536    var SLIDES_CONTAINER =      'fp-slidesContainer';
20537    var SLIDES_CONTAINER_SEL =  '.' + SLIDES_CONTAINER;
20538    var TABLE =                 'fp-table';
20539
20540    // slide nav
20541    var SLIDES_NAV =            'fp-slidesNav';
20542    var SLIDES_NAV_SEL =        '.' + SLIDES_NAV;
20543    var SLIDES_NAV_LINK_SEL =   SLIDES_NAV_SEL + ' a';
20544    var SLIDES_ARROW =          'fp-controlArrow';
20545    var SLIDES_ARROW_SEL =      '.' + SLIDES_ARROW;
20546    var SLIDES_PREV =           'fp-prev';
20547    var SLIDES_PREV_SEL =       '.' + SLIDES_PREV;
20548    var SLIDES_ARROW_PREV =     SLIDES_ARROW + ' ' + SLIDES_PREV;
20549    var SLIDES_ARROW_PREV_SEL = SLIDES_ARROW_SEL + SLIDES_PREV_SEL;
20550    var SLIDES_NEXT =           'fp-next';
20551    var SLIDES_NEXT_SEL =       '.' + SLIDES_NEXT;
20552    var SLIDES_ARROW_NEXT =     SLIDES_ARROW + ' ' + SLIDES_NEXT;
20553    var SLIDES_ARROW_NEXT_SEL = SLIDES_ARROW_SEL + SLIDES_NEXT_SEL;
20554
20555    var $window = $(window);
20556    var $document = $(document);
20557
20558    // Default options for iScroll.js used when using scrollOverflow
20559    var iscrollOptions = {
20560        scrollbars: true,
20561        mouseWheel: true,
20562        hideScrollbars: false,
20563        fadeScrollbars: false,
20564        disableMouse: true,
20565        interactiveScrollbars: true,
20566        bounce: false,
20567    };
20568
20569    $.fn.fullpage = function(options) {
20570        //only once my friend!
20571        if($('html').hasClass(ENABLED)){ displayWarnings(); return; }
20572
20573        // common jQuery objects
20574        var $htmlBody = $('html, body');
20575        var $body = $('body');
20576
20577        var FP = $.fn.fullpage;
20578
20579        // Creating some defaults, extending them with any options that were provided
20580        options = $.extend({
20581            //navigation
20582            menu: false,
20583            anchors:[],
20584            lockAnchors: false,
20585            navigation: false,
20586            navigationPosition: 'right',
20587            navigationTooltips: [],
20588            showActiveTooltip: false,
20589            slidesNavigation: false,
20590            slidesNavPosition: 'bottom',
20591            scrollBar: false,
20592            hybrid: false,
20593
20594            //scrolling
20595            css3: true,
20596            scrollingSpeed: 700,
20597            autoScrolling: true,
20598            fitToSection: true,
20599            fitToSectionDelay: 1000,
20600            easing: 'easeInOutCubic',
20601            easingcss3: 'ease',
20602            loopBottom: false,
20603            loopTop: false,
20604            loopHorizontal: true,
20605            continuousVertical: false,
20606            continuousHorizontal: false,
20607            scrollHorizontally: false,
20608            interlockedSlides: false,
20609            dragAndMove: false,
20610            offsetSections: false,
20611            resetSliders: false,
20612            fadingEffect: false,
20613            normalScrollElements: null,
20614            scrollOverflow: false,
20615            scrollOverflowReset: false,
20616            scrollOverflowHandler: iscrollHandler,
20617            scrollOverflowOptions: null,
20618            touchSensitivity: 5,
20619            normalScrollElementTouchThreshold: 5,
20620            bigSectionsDestination: null,
20621
20622            //Accessibility
20623            keyboardScrolling: true,
20624            animateAnchor: true,
20625            recordHistory: true,
20626
20627            //design
20628            controlArrows: true,
20629            controlArrowColor: '#fff',
20630            verticalCentered: true,
20631            sectionsColor : [],
20632            paddingTop: 0,
20633            paddingBottom: 0,
20634            fixedElements: null,
20635            responsive: 0, //backwards compabitility with responsiveWiddth
20636            responsiveWidth: 0,
20637            responsiveHeight: 0,
20638            responsiveSlides: false,
20639            parallax: false,
20640            parallaxOptions: {
20641                type: 'reveal',
20642                percentage: 62,
20643                property: 'translate'
20644            },
20645
20646            //Custom selectors
20647            sectionSelector: SECTION_DEFAULT_SEL,
20648            slideSelector: SLIDE_DEFAULT_SEL,
20649
20650            //events
20651            afterLoad: null,
20652            onLeave: null,
20653            afterRender: null,
20654            afterResize: null,
20655            afterReBuild: null,
20656            afterSlideLoad: null,
20657            onSlideLeave: null,
20658            afterResponsive: null,
20659
20660            lazyLoading: true
20661        }, options);
20662
20663        //flag to avoid very fast sliding for landscape sliders
20664        var slideMoving = false;
20665
20666        var isTouchDevice = navigator.userAgent.match(/(iPhone|iPod|iPad|Android|playbook|silk|BlackBerry|BB10|Windows Phone|Tizen|Bada|webOS|IEMobile|Opera Mini)/);
20667        var isTouch = (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0) || (navigator.maxTouchPoints));
20668        var container = $(this);
20669        var windowsHeight = $window.height();
20670        var isResizing = false;
20671        var isWindowFocused = true;
20672        var lastScrolledDestiny;
20673        var lastScrolledSlide;
20674        var canScroll = true;
20675        var scrollings = [];
20676        var controlPressed;
20677        var startingSection;
20678        var isScrollAllowed = {};
20679        isScrollAllowed.m = {  'up':true, 'down':true, 'left':true, 'right':true };
20680        isScrollAllowed.k = $.extend(true,{}, isScrollAllowed.m);
20681        var MSPointer = getMSPointer();
20682        var events = {
20683            touchmove: 'ontouchmove' in window ? 'touchmove' :  MSPointer.move,
20684            touchstart: 'ontouchstart' in window ? 'touchstart' :  MSPointer.down
20685        };
20686
20687        //cheks for passive event support
20688        //added by Uncode from new fullPage updates
20689        var g_supportsPassive = false;
20690        try {
20691          var opts = Object.defineProperty({}, 'passive', {
20692            get: function() {
20693              g_supportsPassive = true;
20694            }
20695          });
20696          window.addEventListener("testPassive", null, opts);
20697          window.removeEventListener("testPassive", null, opts);
20698        } catch (e) {}
20699
20700        //timeouts
20701        var resizeId;
20702        var afterSectionLoadsId;
20703        var afterSlideLoadsId;
20704        var scrollId;
20705        var scrollId2;
20706        var keydownId;
20707        var originals = $.extend(true, {}, options); //deep copy
20708
20709        //Uncode addition
20710        var $masthead = $('#masthead');
20711        var hideMenu = !$('body').hasClass('vmenu') && $('body').hasClass('uncode-fp-menu-hide') ? true : false;
20712        var menuHeight = $masthead.hasClass('menu-transparent') || hideMenu ? 0 : UNCODE.menuHeight;
20713        var bodyBorder = UNCODE.bodyBorder;
20714        var adminBarHeight = UNCODE.adminBarHeight;
20715
20716        displayWarnings();
20717
20718        //fixing bug in iScroll with links: https://github.com/cubiq/iscroll/issues/783
20719        iscrollOptions.click = isTouch; // see #2035
20720
20721        //extending iScroll options with the user custom ones
20722        iscrollOptions = $.extend(iscrollOptions, options.scrollOverflowOptions);
20723
20724        //easeInOutCubic animation included in the plugin
20725        $.extend($.easing,{ easeInOutCubic: function (x, t, b, c, d) {if ((t/=d/2) < 1) return c/2*t*t*t + b;return c/2*((t-=2)*t*t + 2) + b;}});
20726
20727        /**
20728        * Sets the autoScroll option.
20729        * It changes the scroll bar visibility and the history of the site as a result.
20730        */
20731        function setAutoScrolling(value, type){
20732            //removing the transformation
20733            if(!value){
20734                silentScroll(0);
20735            }
20736
20737            setVariableState('autoScrolling', value, type);
20738
20739            var element = $(SECTION_ACTIVE_SEL);
20740
20741            if(options.autoScrolling && !options.scrollBar){
20742                $htmlBody.css({
20743                    'overflow' : 'hidden !important',
20744                    'height' : '100%'
20745                });
20746
20747                //Uncode addition
20748                //setRecordHistory(originals.recordHistory, 'internal');
20749
20750                //for IE touch devices
20751                container.css({
20752                    '-ms-touch-action': 'none',
20753                    'touch-action': 'none'
20754                });
20755
20756                if(element.length){
20757                    //moving the container up
20758                    silentScroll(element.position().top);
20759                }
20760
20761            }else{
20762                $htmlBody.css({
20763                    'overflow' : 'visible !important',
20764                    'height' : 'initial'
20765                });
20766
20767                //Uncode addition
20768                //setRecordHistory(false, 'internal');
20769
20770                //for IE touch devices
20771                container.css({
20772                    '-ms-touch-action': '',
20773                    'touch-action': ''
20774                });
20775
20776                //scrolling the page to the section with no animation
20777                if (element.length) {
20778                    $htmlBody.scrollTop(element.position().top);
20779                }
20780            }
20781        }
20782
20783        /**
20784        * Defines wheter to record the history for each hash change in the URL.
20785        */
20786        function setRecordHistory(value, type){
20787            setVariableState('recordHistory', value, type);
20788        }
20789
20790        /**
20791        * Defines the scrolling speed
20792        */
20793        function setScrollingSpeed(value, type){
20794            setVariableState('scrollingSpeed', value, type);
20795        }
20796
20797        /**
20798        * Sets fitToSection
20799        */
20800        function setFitToSection(value, type){
20801            setVariableState('fitToSection', value, type);
20802        }
20803
20804        /**
20805        * Sets lockAnchors
20806        */
20807        function setLockAnchors(value){
20808            options.lockAnchors = value;
20809        }
20810
20811        /**
20812        * Adds or remove the possiblity of scrolling through sections by using the mouse wheel or the trackpad.
20813        */
20814        function setMouseWheelScrolling(value){
20815            if(value){
20816                addMouseWheelHandler();
20817                addMiddleWheelHandler();
20818            }else{
20819                removeMouseWheelHandler();
20820                removeMiddleWheelHandler();
20821            }
20822        }
20823
20824        /**
20825        * Adds or remove the possibility of scrolling through sections by using the mouse wheel/trackpad or touch gestures.
20826        * Optionally a second parameter can be used to specify the direction for which the action will be applied.
20827        *
20828        * @param directions string containing the direction or directions separated by comma.
20829        */
20830        function setAllowScrolling(value, directions){
20831            if(typeof directions !== 'undefined'){
20832                directions = directions.replace(/ /g,'').split(',');
20833
20834                $.each(directions, function (index, direction){
20835                    setIsScrollAllowed(value, direction, 'm');
20836                });
20837            }
20838            else if(value){
20839                setMouseWheelScrolling(true);
20840                addTouchHandler();
20841            }else{
20842                setMouseWheelScrolling(false);
20843                removeTouchHandler();
20844            }
20845        }
20846
20847        /**
20848        * Adds or remove the possibility of scrolling through sections by using the keyboard arrow keys
20849        */
20850        function setKeyboardScrolling(value, directions){
20851            if(typeof directions !== 'undefined'){
20852                directions = directions.replace(/ /g,'').split(',');
20853
20854                $.each(directions, function (index, direction){
20855                    setIsScrollAllowed(value, direction, 'k');
20856                });
20857            }else{
20858                options.keyboardScrolling = value;
20859            }
20860        }
20861
20862        /**
20863        * Moves the page up one section.
20864        */
20865        function moveSectionUp(){
20866            var prev = $(SECTION_ACTIVE_SEL).prev(SECTION_SEL);
20867
20868            //looping to the bottom if there's no more sections above
20869            if (!prev.length && (options.loopTop || options.continuousVertical)) {
20870                prev = $(SECTION_SEL).last();
20871            }
20872
20873            if (prev.length) {
20874                scrollPage(prev, null, true);
20875            }
20876        }
20877
20878        /**
20879        * Moves the page down one section.
20880        */
20881        function moveSectionDown(){
20882            var next = $(SECTION_ACTIVE_SEL).next(SECTION_SEL);
20883
20884            //looping to the top if there's no more sections below
20885            if(!next.length &&
20886                (options.loopBottom || options.continuousVertical)){
20887                next = $(SECTION_SEL).first();
20888            }
20889
20890            if(next.length){
20891                scrollPage(next, null, false);
20892            }
20893        }
20894
20895        /**
20896        * Moves the page to the given section and slide with no animation.
20897        * Anchors or index positions can be used as params.
20898        */
20899        function silentMoveTo(sectionAnchor, slideAnchor){
20900            setScrollingSpeed (0, 'internal');
20901            moveTo(sectionAnchor, slideAnchor);
20902            setScrollingSpeed (originals.scrollingSpeed, 'internal');
20903        }
20904
20905        /**
20906        * Moves the page to the given section and slide.
20907        * Anchors or index positions can be used as params.
20908        */
20909        function moveTo(sectionAnchor, slideAnchor){
20910            var destiny = getSectionByAnchor(sectionAnchor);
20911
20912            if (typeof slideAnchor !== 'undefined'){
20913                scrollPageAndSlide(sectionAnchor, slideAnchor);
20914            }else if(destiny.length > 0){
20915                scrollPage(destiny);
20916            }
20917        }
20918
20919        /**
20920        * Slides right the slider of the active section.
20921        * Optional `section` param.
20922        */
20923        function moveSlideRight(section){
20924            moveSlide('right', section);
20925        }
20926
20927        /**
20928        * Slides left the slider of the active section.
20929        * Optional `section` param.
20930        */
20931        function moveSlideLeft(section){
20932            moveSlide('left', section);
20933        }
20934
20935        /**
20936         * When resizing is finished, we adjust the slides sizes and positions
20937         */
20938        function reBuild(resizing){
20939            if(container.hasClass(DESTROYED)){ return; }  //nothing to do if the plugin was destroyed
20940
20941            isResizing = true;
20942
20943            windowsHeight = $window.height();  //updating global var
20944
20945            $(SECTION_SEL).each(function(){
20946                var slidesWrap = $(this).find(SLIDES_WRAPPER_SEL);
20947                var slides = $(this).find(SLIDE_SEL);
20948
20949                //adjusting the height of the table-cell for IE and Firefox
20950                if(options.verticalCentered){
20951                    $(this).find(TABLE_CELL_SEL).css('height', getTableHeight($(this)) + 'px');
20952                }
20953
20954                $(this).css('height', windowsHeight + 'px');
20955
20956                //resizing the scrolling divs
20957                if(options.scrollOverflow){
20958                    if(slides.length){
20959                        slides.each(function(){
20960                            createScrollBar($(this));
20961                        });
20962                    }else{
20963                        createScrollBar($(this));
20964                    }
20965                }
20966
20967                //adjusting the position fo the FULL WIDTH slides...
20968                if (slides.length > 1) {
20969                    landscapeScroll(slidesWrap, slidesWrap.find(SLIDE_ACTIVE_SEL));
20970                }
20971            });
20972
20973            var activeSection = $(SECTION_ACTIVE_SEL);
20974            var sectionIndex = activeSection.index(SECTION_SEL);
20975
20976            //isn't it the first section?
20977            if(sectionIndex){
20978                //adjusting the position for the current section
20979                silentMoveTo(sectionIndex + 1);
20980            }
20981
20982            isResizing = false;
20983            $.isFunction( options.afterResize ) && resizing && options.afterResize.call(container);
20984            $.isFunction( options.afterReBuild ) && !resizing && options.afterReBuild.call(container);
20985        }
20986
20987        /**
20988        * Turns fullPage.js to normal scrolling mode when the viewport `width` or `height`
20989        * are smaller than the set limit values.
20990        */
20991        function setResponsive(active){
20992            var isResponsive = $body.hasClass(RESPONSIVE);
20993
20994            if(active){
20995                if(!isResponsive){
20996                    setAutoScrolling(false, 'internal');
20997                    setFitToSection(false, 'internal');
20998                    $(SECTION_NAV_SEL).hide();
20999                    $body.addClass(RESPONSIVE);
21000                    $.isFunction( options.afterResponsive ) && options.afterResponsive.call( container, active);
21001                }
21002            }
21003            else if(isResponsive){
21004                setAutoScrolling(originals.autoScrolling, 'internal');
21005                setFitToSection(originals.autoScrolling, 'internal');
21006                $(SECTION_NAV_SEL).show();
21007                $body.removeClass(RESPONSIVE);
21008                $.isFunction( options.afterResponsive ) && options.afterResponsive.call( container, active);
21009            }
21010        }
21011
21012        if($(this).length){
21013            //public functions
21014            FP.setAutoScrolling = setAutoScrolling;
21015            FP.setRecordHistory = setRecordHistory;
21016            FP.setScrollingSpeed = setScrollingSpeed;
21017            FP.setFitToSection = setFitToSection;
21018            FP.setLockAnchors = setLockAnchors;
21019            FP.setMouseWheelScrolling = setMouseWheelScrolling;
21020            FP.setAllowScrolling = setAllowScrolling;
21021            FP.setKeyboardScrolling = setKeyboardScrolling;
21022            FP.moveSectionUp = moveSectionUp;
21023            FP.moveSectionDown = moveSectionDown;
21024            FP.silentMoveTo = silentMoveTo;
21025            FP.moveTo = moveTo;
21026            FP.moveSlideRight = moveSlideRight;
21027            FP.moveSlideLeft = moveSlideLeft;
21028            FP.fitToSection = fitToSection;
21029            FP.reBuild = reBuild;
21030            FP.setResponsive = setResponsive;
21031            FP.destroy = destroy;
21032
21033            init();
21034
21035            bindEvents();
21036        }
21037
21038        function init(){
21039            //if css3 is not supported, it will use jQuery animations
21040            if(options.css3){
21041                options.css3 = support3d();
21042            }
21043
21044            options.scrollBar = options.scrollBar || options.hybrid;
21045
21046            setOptionsFromDOM();
21047            prepareDom();
21048            setAllowScrolling(true);
21049            setAutoScrolling(options.autoScrolling, 'internal');
21050            responsive();
21051
21052            //setting the class for the body element
21053            setBodyClass();
21054
21055            //Uncode addition
21056            // if(document.readyState === 'complete'){
21057            //     scrollToAnchor();
21058            // }
21059            // $window.on('load', scrollToAnchor);
21060        }
21061
21062        function bindEvents(){
21063            $window
21064                //when scrolling...
21065                //.on('scroll', scrollHandler)
21066
21067                //detecting any change on the URL to scroll to the given anchor link
21068                //(a way to detect back history button as we play with the hashes on the URL)
21069                .on('hashchange', hashChangeHandler)
21070
21071                //when opening a new tab (ctrl + t), `control` won't be pressed when coming back.
21072                .blur(blurHandler)
21073
21074                //when resizing the site, we adjust the heights of the sections, slimScroll...
21075                .resize(resizeHandler);
21076
21077            $document
21078                //Sliding with arrow keys, both, vertical and horizontal
21079                .keydown(keydownHandler)
21080
21081                //to prevent scrolling while zooming
21082                .keyup(keyUpHandler)
21083
21084                //Scrolls to the section when clicking the navigation bullet
21085                .on('click touchstart', SECTION_NAV_SEL + ' a', sectionBulletHandler)
21086
21087                //Scrolls the slider to the given slide destination for the given section
21088                .on('click touchstart', SLIDES_NAV_LINK_SEL, slideBulletHandler)
21089
21090                .on('click', SECTION_NAV_TOOLTIP_SEL, tooltipTextHandler);
21091
21092            //Scrolling horizontally when clicking on the slider controls.
21093            $(SECTION_SEL).on('click touchstart', SLIDES_ARROW_SEL, slideArrowHandler);
21094
21095            /**
21096            * Applying normalScroll elements.
21097            * Ignoring the scrolls over the specified selectors.
21098            */
21099            if(options.normalScrollElements){
21100                $document.on('mouseenter', options.normalScrollElements, function () {
21101                    setMouseWheelScrolling(false);
21102                });
21103
21104                $document.on('mouseleave', options.normalScrollElements, function(){
21105                    setMouseWheelScrolling(true);
21106                });
21107            }
21108        }
21109
21110        /**
21111        * Setting options from DOM elements if they are not provided.
21112        */
21113        function setOptionsFromDOM(){
21114            var sections = container.find(options.sectionSelector);
21115
21116            //no anchors option? Checking for them in the DOM attributes
21117            if(!options.anchors.length){
21118                options.anchors = sections.filter('[data-anchor]').map(function(){
21119                    return $(this).data('anchor').toString();
21120                }).get();
21121            }
21122
21123            //no tooltips option? Checking for them in the DOM attributes
21124            if(!options.navigationTooltips.length){
21125                options.navigationTooltips = sections.filter('[data-tooltip]').map(function(){
21126                    return $(this).data('tooltip').toString();
21127                }).get();
21128            }
21129        }
21130
21131        /**
21132        * Works over the DOM structure to set it up for the current fullpage options.
21133        */
21134        function prepareDom(){
21135            container.css({
21136                'height': '100%',
21137                'position': 'relative'
21138            });
21139
21140            //adding a class to recognize the container internally in the code
21141            container.addClass(WRAPPER);
21142            $('html').addClass(ENABLED);
21143
21144            windowsHeight = $window.height();
21145
21146            container.removeClass(DESTROYED); //in case it was destroyed before initializing it again
21147
21148            addInternalSelectors();
21149
21150             //styling the sections / slides / menu
21151            $(SECTION_SEL).each(function(index){
21152                var section = $(this);
21153                var slides = section.find(SLIDE_SEL);
21154                var numSlides = slides.length;
21155
21156                styleSection(section, index);
21157                styleMenu(section, index);
21158
21159                // if there's any slide
21160                if (numSlides > 0) {
21161                    styleSlides(section, slides, numSlides);
21162                }else{
21163                    if(options.verticalCentered){
21164                        addTableClass(section);
21165                    }
21166                }
21167            });
21168
21169            //fixed elements need to be moved out of the plugin container due to problems with CSS3.
21170            if(options.fixedElements && options.css3){
21171                $(options.fixedElements).appendTo($body);
21172            }
21173
21174            //vertical centered of the navigation + active bullet
21175            if(options.navigation){
21176                addVerticalNavigation();
21177            }
21178
21179            enableYoutubeAPI();
21180
21181            if(options.scrollOverflow){
21182                if(document.readyState === 'complete'){
21183                    createScrollBarHandler();
21184                }
21185                //after DOM and images are loaded
21186                $window.on('load', createScrollBarHandler);
21187            }else{
21188                afterRenderActions();
21189            }
21190        }
21191
21192        /**
21193        * Styles the horizontal slides for a section.
21194        */
21195        function styleSlides(section, slides, numSlides){
21196            var sliderWidth = numSlides * 100;
21197            var slideWidth = 100 / numSlides;
21198
21199            slides.wrapAll('<div class="' + SLIDES_CONTAINER + '" />');
21200            slides.parent().wrap('<div class="' + SLIDES_WRAPPER + '" />');
21201
21202            section.find(SLIDES_CONTAINER_SEL).css('width', sliderWidth + '%');
21203
21204            if(numSlides > 1){
21205                if(options.controlArrows){
21206                    createSlideArrows(section);
21207                }
21208
21209                if(options.slidesNavigation){
21210                    addSlidesNavigation(section, numSlides);
21211                }
21212            }
21213
21214            slides.each(function(index) {
21215                $(this).css('width', slideWidth + '%');
21216
21217                if(options.verticalCentered){
21218                    addTableClass($(this));
21219                }
21220            });
21221
21222            var startingSlide = section.find(SLIDE_ACTIVE_SEL);
21223
21224            //if the slide won't be an starting point, the default will be the first one
21225            //the active section isn't the first one? Is not the first slide of the first section? Then we load that section/sl
21225ide by default.
21226            if( startingSlide.length &&  ($(SECTION_ACTIVE_SEL).index(SECTION_SEL) !== 0 || ($(SECTION_ACTIVE_SEL).index(SECTION_SEL) === 0 && startingSlide.index() !== 0))){
21227                silentLandscapeScroll(startingSlide, 'internal');
21228            }else{
21229                slides.eq(0).addClass(ACTIVE);
21230            }
21231        }
21232
21233        /**
21234        * Styling vertical sections
21235        */
21236        function styleSection(section, index){
21237            //if no active section is defined, the 1st one will be the default one
21238            if(!index && $(SECTION_ACTIVE_SEL).length === 0) {
21239                section.addClass(ACTIVE);
21240            }
21241            startingSection = $(SECTION_ACTIVE_SEL);
21242
21243            section.css('height', windowsHeight + 'px');
21244
21245            if(options.paddingTop){
21246                section.css('padding-top', options.paddingTop);
21247            }
21248
21249            if(options.paddingBottom){
21250                section.css('padding-bottom', options.paddingBottom);
21251            }
21252
21253            if (typeof options.sectionsColor[index] !==  'undefined') {
21254                section.css('background-color', options.sectionsColor[index]);
21255            }
21256
21257            if (typeof options.anchors[index] !== 'undefined') {
21258                section.attr('data-anchor', options.anchors[index]);
21259            }
21260        }
21261
21262        /**
21263        * Sets the data-anchor attributes to the menu elements and activates the current one.
21264        */
21265        function styleMenu(section, index){
21266            if (typeof options.anchors[index] !== 'undefined') {
21267                //activating the menu / nav element on load
21268                if(section.hasClass(ACTIVE)){
21269                    activateMenuAndNav(options.anchors[index], index);
21270                }
21271            }
21272
21273            //moving the menu outside the main container if it is inside (avoid problems with fixed positions when using CSS3 tranforms)
21274            if(options.menu && options.css3 && $(options.menu).closest(WRAPPER_SEL).length){
21275                $(options.menu).appendTo($body);
21276            }
21277        }
21278
21279        /**
21280        * Adds internal classes to be able to provide customizable selectors
21281        * keeping the link with the style sheet.
21282        */
21283        function addInternalSelectors(){
21284            container.find(options.sectionSelector).addClass(SECTION);
21285            container.find(options.slideSelector).addClass(SLIDE);
21286        }
21287
21288        /**
21289        * Creates the control arrows for the given section
21290        */
21291        function createSlideArrows(section){
21292            section.find(SLIDES_WRAPPER_SEL).after('<div class="' + SLIDES_ARROW_PREV + '"></div><div class="' + SLIDES_ARROW_NEXT + '"></div>');
21293
21294            if(options.controlArrowColor!='#fff'){
21295                section.find(SLIDES_ARROW_NEXT_SEL).css('border-color', 'transparent transparent transparent '+options.controlArrowColor);
21296                section.find(SLIDES_ARROW_PREV_SEL).css('border-color', 'transparent '+ options.controlArrowColor + ' transparent transparent');
21297            }
21298
21299            if(!options.loopHorizontal){
21300                section.find(SLIDES_ARROW_PREV_SEL).hide();
21301            }
21302        }
21303
21304        /**
21305        * Creates a vertical navigation bar.
21306        */
21307        function addVerticalNavigation(){
21308            $body.append('<div id="' + SECTION_NAV + '"><ul></ul></div>');
21309            var nav = $(SECTION_NAV_SEL);
21310
21311            nav.addClass(function() {
21312                return options.showActiveTooltip ? SHOW_ACTIVE_TOOLTIP + ' ' + options.navigationPosition : options.navigationPosition;
21313            });
21314
21315            for (var i = 0; i < $(SECTION_SEL).length; i++) {
21316                var link = '';
21317                if (options.anchors.length) {
21318                    link = options.anchors[i];
21319                }
21320
21321                var li = '<li><a href="#' + link + '"><span></span></a>';
21322
21323                // Only add tooltip if needed (defined by user)
21324                var tooltip = options.navigationTooltips[i];
21325
21326                if (typeof tooltip !== 'undefined' && tooltip !== '') {
21327                    li += '<div class="' + SECTION_NAV_TOOLTIP + ' ' + options.navigationPosition + '">' + tooltip + '</div>';
21328                }
21329
21330                li += '</li>';
21331
21332                nav.find('ul').append(li);
21333            }
21334
21335            //centering it vertically
21336            $(SECTION_NAV_SEL).css('margin-top', '-' + ($(SECTION_NAV_SEL).height()/2) + 'px');
21337
21338            //activating the current active section
21339            $(SECTION_NAV_SEL).find('li').eq($(SECTION_ACTIVE_SEL).index(SECTION_SEL)).find('a').addClass(ACTIVE);
21340        }
21341
21342        /**
21343        * Creates the slim scroll scrollbar for the sections and slides inside them.
21344        */
21345        function createScrollBarHandler(){
21346            $(SECTION_SEL).each(function(){
21347                var slides = $(this).find(SLIDE_SEL);
21348
21349                if(slides.length){
21350                    slides.each(function(){
21351                        createScrollBar($(this));
21352                    });
21353                }else{
21354                    createScrollBar($(this));
21355                }
21356
21357            });
21358            afterRenderActions();
21359        }
21360
21361        /*
21362        * Enables the Youtube videos API so we can control their flow if necessary.
21363        */
21364        function enableYoutubeAPI(){
21365            container.find('iframe[src*="youtube.com/embed/"]').each(function(){
21366                addURLParam($(this), 'enablejsapi=1');
21367            });
21368        }
21369
21370        /**
21371        * Adds a new parameter and its value to the `src` of a given element
21372        */
21373        function addURLParam(element, newParam){
21374            var originalSrc = element.attr('src');
21375            element.attr('src', originalSrc + getUrlParamSign(originalSrc) + newParam);
21376        }
21377
21378        /*
21379        * Returns the prefix sign to use for a new parameter in an existen URL.
21380        *
21381        * @return {String}  ? | &
21382        */
21383        function getUrlParamSign(url){
21384            return ( !/\?/.test( url ) ) ? '?' : '&';
21385        }
21386
21387        /**
21388        * Actions and callbacks to fire afterRender
21389        */
21390        function afterRenderActions(){
21391            var section = $(SECTION_ACTIVE_SEL);
21392
21393            section.addClass(COMPLETELY);
21394
21395            if(options.scrollOverflowHandler.afterRender){
21396                options.scrollOverflowHandler.afterRender(section);
21397            }
21398            lazyLoad(section);
21399            playMedia(section);
21400            options.scrollOverflowHandler.afterLoad();
21401
21402            if(isDestinyTheStartingSection()){
21403                $.isFunction( options.afterLoad ) && options.afterLoad.call(section, section.data('anchor'), (section.index(SECTION_SEL) + 1));
21404            }
21405
21406            $.isFunction( options.afterRender ) && options.afterRender.call(container);
21407        }
21408
21409        /**
21410        * Determines if the URL anchor destiny is the starting section (the one using 'active' class before initialization)
21411        */
21412        function isDestinyTheStartingSection(){
21413            var anchors =  window.location.hash.replace('#', '').split('/');
21414            var destinationSection = getSectionByAnchor(decodeURIComponent(anchors[0]));
21415
21416            return !destinationSection.length || destinationSection.length && destinationSection.index() === startingSection.index();
21417        }
21418
21419
21420        var isScrolling = false;
21421        var lastScroll = 0;
21422
21423        //when scrolling...
21424        function scrollHandler(){
21425            var currentSection;
21426
21427            if(!options.autoScrolling || options.scrollBar){
21428                var currentScroll = $window.scrollTop();
21429                var scrollDirection = getScrollDirection(currentScroll);
21430                var visibleSectionIndex = 0;
21431                var screen_mid = currentScroll + ($window.height() / 2.0);
21432                var isAtBottom = $body.height() - $window.height() === currentScroll;
21433                var sections =  document.querySelectorAll(SECTION_SEL);
21434
21435                //when using `auto-height` for a small last section it won't be centered in the viewport
21436                if(isAtBottom){
21437                    visibleSectionIndex = sections.length - 1;
21438                }
21439                //is at top? when using `auto-height` for a small first section it won't be centered in the viewport
21440                else if(!currentScroll){
21441                    visibleSectionIndex = 0;
21442                }
21443
21444                //taking the section which is showing more content in the viewport
21445                else{
21446                    for (var i = 0; i < sections.length; ++i) {
21447                        var section = sections[i];
21448
21449                        // Pick the the last section which passes the middle line of the screen.
21450                        if (section.offsetTop <= screen_mid)
21451                        {
21452                            visibleSectionIndex = i;
21453                        }
21454                    }
21455                }
21456
21457                if(isCompletelyInViewPort(scrollDirection)){
21458                    if(!$(SECTION_ACTIVE_SEL).hasClass(COMPLETELY)){
21459                        $(SECTION_ACTIVE_SEL).addClass(COMPLETELY).siblings().removeClass(COMPLETELY);
21460                    }
21461                }
21462
21463                //geting the last one, the current one on the screen
21464                currentSection = $(sections).eq(visibleSectionIndex);
21465
21466                //setting the visible section as active when manually scrolling
21467                //executing only once the first time we reach the section
21468                if(!currentSection.hasClass(ACTIVE)){
21469                    isScrolling = true;
21470                    var leavingSection = $(SECTION_ACTIVE_SEL);
21471                    var leavingSectionIndex = leavingSection.index(SECTION_SEL) + 1;
21472                    var yMovement = getYmovement(currentSection);
21473                    var anchorLink  = currentSection.data('anchor');
21474                    var sectionIndex = currentSection.index(SECTION_SEL) + 1;
21475                    var activeSlide = currentSection.find(SLIDE_ACTIVE_SEL);
21476                    var slideIndex;
21477                    var slideAnchorLink;
21478
21479                    if(activeSlide.length){
21480                        slideAnchorLink = activeSlide.data('anchor');
21481                        slideIndex = activeSlide.index();
21482                    }
21483
21484                    if(canScroll){
21485                        currentSection.addClass(ACTIVE).siblings().removeClass(ACTIVE);
21486
21487                        $.isFunction( options.onLeave ) && options.onLeave.call( leavingSection, leavingSectionIndex, sectionIndex, yMovement);
21488                        $.isFunction( options.afterLoad ) && options.afterLoad.call( currentSection, anchorLink, sectionIndex);
21489
21490                        stopMedia(leavingSection);
21491                        lazyLoad(currentSection);
21492                        playMedia(currentSection);
21493
21494                        activateMenuAndNav(anchorLink, sectionIndex - 1);
21495
21496                        if(options.anchors.length){
21497                            //needed to enter in hashChange event when using the menu with anchor links
21498                            lastScrolledDestiny = anchorLink;
21499                        }
21500                        setState(slideIndex, slideAnchorLink, anchorLink, sectionIndex);
21501                    }
21502
21503                    //small timeout in order to avoid entering in hashChange event when scrolling is not finished yet
21504                    clearTimeout(scrollId);
21505                    scrollId = setTimeout(function(){
21506                        isScrolling = false;
21507                    }, 100);
21508                }
21509
21510                if(options.fitToSection){
21511                    //for the auto adjust of the viewport to fit a whole section
21512                    clearTimeout(scrollId2);
21513
21514                    scrollId2 = setTimeout(function(){
21515                        //checking it again in case it changed during the delay
21516                        if(options.fitToSection){
21517                            fitToSection();
21518                        }
21519                    }, options.fitToSectionDelay);
21520                }
21521            }
21522        }
21523
21524        /**
21525        * Fits the site to the nearest active section
21526        */
21527        function fitToSection(){
21528            //checking fitToSection again in case it was set to false before the timeout delay
21529            if(canScroll){
21530                //allows to scroll to an active section and
21531                //if the section is already active, we prevent firing callbacks
21532                isResizing = true;
21533
21534                scrollPage($(SECTION_ACTIVE_SEL));
21535                isResizing = false;
21536            }
21537        }
21538
21539        /**
21540        * Determines whether the active section has seen in its whole or not.
21541        */
21542        function isCompletelyInViewPort(movement){
21543            var top = $(SECTION_ACTIVE_SEL).position().top;
21544            var bottom = top + $window.height();
21545
21546            if(movement == 'up'){
21547                return bottom >= ($window.scrollTop() + $window.height());
21548            }
21549            return top <= $window.scrollTop();
21550        }
21551
21552        /**
21553        * Gets the directon of the the scrolling fired by the scroll event.
21554        */
21555        function getScrollDirection(currentScroll){
21556            var direction = currentScroll > lastScroll ? 'down' : 'up';
21557
21558            lastScroll = currentScroll;
21559
21560            //needed for auto-height sections to determine if we want to scroll to the top or bottom of the destination
21561            previousDestTop = currentScroll;
21562
21563            return direction;
21564        }
21565
21566        /**
21567        * Determines the way of scrolling up or down:
21568        * by 'automatically' scrolling a section or by using the default and normal scrolling.
21569        */
21570        function scrolling(type, scrollable){
21571            if (!isScrollAllowed.m[type]){
21572                return;
21573            }
21574            var check = (type === 'down') ? 'bottom' : 'top';
21575            var scrollSection = (type === 'down') ? moveSectionDown : moveSectionUp;
21576
21577            if(scrollable.length > 0 ){
21578                //is the scrollbar at the start/end of the scroll?
21579                if(options.scrollOverflowHandler.isScrolled(check, scrollable)){
21580                    scrollSection();
21581                }else{
21582                    return true;
21583                }
21584            }else{
21585                // moved up/down
21586                scrollSection();
21587            }
21588        }
21589
21590        /*
21591        * Preventing bouncing in iOS #2285
21592        */
21593        function preventBouncing(event){
21594            var e = event.originalEvent;
21595            if(!checkParentForNormalScrollElement(event.target) && options.autoScrolling && isReallyTouch(e) && isScrollAllowed.m.up){
21596                //preventing the easing on iOS devices
21597                event.preventDefault();
21598            }
21599        }
21600
21601        var touchStartY = 0;
21602        var touchStartX = 0;
21603        var touchEndY = 0;
21604        var touchEndX = 0;
21605
21606        /* Detecting touch events
21607
21608        * As we are changing the top property of the page on scrolling, we can not use the traditional way to detect it.
21609        * This way, the touchstart and the touch moves shows an small difference between them which is the
21610        * used one to determine the direction.
21611        */
21612        function touchMoveHandler(event){
21613            var e = event.originalEvent;
21614            var activeSection = $(e.target).closest(SECTION_SEL);
21615
21616            // additional: if one of the normalScrollElements isn't within options.normalScrollElementTouchThreshold hops up the DOM chain
21617            if (!checkParentForNormalScrollElement(event.target) && isReallyTouch(e) ) {
21618
21619                if(options.autoScrolling){
21620                    //preventing the easing on iOS devices
21621                    event.preventDefault();
21622                }
21623
21624                var scrollable = options.scrollOverflowHandler.scrollable(activeSection);
21625                var touchEvents = getEventsPage(e);
21626
21627                touchEndY = touchEvents.y;
21628                touchEndX = touchEvents.x;
21629
21630                //if movement in the X axys is greater than in the Y and the currect section has slides...
21631                if (activeSection.find(SLIDES_WRAPPER_SEL).length && Math.abs(touchStartX - touchEndX) > (Math.abs(touchStartY - touchEndY))) {
21632
21633                    //is the movement greater than the minimum resistance to scroll?
21634                    if (!slideMoving && Math.abs(touchStartX - touchEndX) > ($window.outerWidth() / 100 * options.touchSensitivity)) {
21635                        if (touchStartX > touchEndX) {
21636                            if(isScrollAllowed.m.right){
21637                                moveSlideRight(activeSection); //next
21638                            }
21639                        } else {
21640                            if(isScrollAllowed.m.left){
21641                                moveSlideLeft(activeSection); //prev
21642                            }
21643                        }
21644                    }
21645                }
21646
21647                //vertical scrolling (only when autoScrolling is enabled)
21648                else if(options.autoScrolling && canScroll){
21649
21650                    //is the movement greater than the minimum resistance to scroll?
21651                    if (Math.abs(touchStartY - touchEndY) > ($window.height() / 100 * options.touchSensitivity)) {
21652                        if (touchStartY > touchEndY) {
21653                            scrolling('down', scrollable);
21654                        } else if (touchEndY > touchStartY) {
21655                            scrolling('up', scrollable);
21656                        }
21657                    }
21658                }
21659            }
21660        }
21661
21662        /**
21663         * recursive function to loop up the parent nodes to check if one of them exists in options.normalScrollElements
21664         * Currently works well for iOS - Android might need some testing
21665         * @param  {Element} el  target element / jquery selector (in subsequent nodes)
21666         * @param  {int}     hop current hop compared to options.normalScrollElementTouchThreshold
21667         * @return {boolean} true if there is a match to options.normalScrollElements
21668         */
21669        function checkParentForNormalScrollElement (el, hop) {
21670            hop = hop || 0;
21671            var parent = $(el).parent();
21672
21673            if (hop < options.normalScrollElementTouchThreshold &&
21674                parent.is(options.normalScrollElements) ) {
21675                return true;
21676            } else if (hop == options.normalScrollElementTouchThreshold) {
21677                return false;
21678            } else {
21679                return checkParentForNormalScrollElement(parent, ++hop);
21680            }
21681        }
21682
21683        /**
21684        * As IE >= 10 fires both touch and mouse events when using a mouse in a touchscreen
21685        * this way we make sure that is really a touch event what IE is detecting.
21686        */
21687        function isReallyTouch(e){
21688            //if is not IE   ||  IE is detecting `touch` or `pen`
21689            return typeof e.pointerType === 'undefined' || e.pointerType != 'mouse';
21690        }
21691
21692        /**
21693        * Handler for the touch start event.
21694        */
21695        function touchStartHandler(event){
21696            var e = event.originalEvent;
21697
21698            //stopping the auto scroll to adjust to a section
21699            if(options.fitToSection){
21700                $htmlBody.stop();
21701            }
21702
21703            if(isReallyTouch(e)){
21704                var touchEvents = getEventsPage(e);
21705                touchStartY = touchEvents.y;
21706                touchStartX = touchEvents.x;
21707            }
21708        }
21709
21710        /**
21711        * Gets the average of the last `number` elements of the given array.
21712        */
21713        function getAverage(elements, number){
21714            var sum = 0;
21715
21716            //taking `number` elements from the end to make the average, if there are not enought, 1
21717            var lastElements = elements.slice(Math.max(elements.length - number, 1));
21718
21719            for(var i = 0; i < lastElements.length; i++){
21720                sum = sum + lastElements[i];
21721            }
21722
21723            return Math.ceil(sum/number);
21724        }
21725
21726        /**
21727         * Detecting mousewheel scrolling
21728         *
21729         * http://blogs.sitepointstatic.com/examples/tech/mouse-wheel/index.html
21730         * http://www.sitepoint.com/html5-javascript-mouse-wheel/
21731         */
21732        var prevTime = new Date().getTime();
21733
21734        function MouseWheelHandler(e) {
21735            var curTime = new Date().getTime();
21736            var isNormalScroll = $(COMPLETELY_SEL).hasClass(NORMAL_SCROLL);
21737
21738            //autoscrolling and not zooming?
21739            if(options.autoScrolling && !controlPressed && !isNormalScroll){
21740                // cross-browser wheel delta
21741                e = e || window.event;
21742                var value = e.wheelDelta || -e.deltaY || -e.detail;
21743                var delta = Math.max(-1, Math.min(1, value));
21744
21745                var horizontalDetection = typeof e.wheelDeltaX !== 'undefined' || typeof e.deltaX !== 'undefined';
21746                var isScrollingVertically = (Math.abs(e.wheelDeltaX) < Math.abs(e.wheelDelta)) || (Math.abs(e.deltaX ) < Math.abs(e.deltaY) || !horizontalDetection);
21747
21748                //Limiting the array to 150 (lets not waste memory!)
21749                if(scrollings.length > 149){
21750                    scrollings.shift();
21751                }
21752
21753                //keeping record of the previous scrollings
21754                scrollings.push(Math.abs(value));
21755
21756                //preventing to scroll the site on mouse wheel when scrollbar is present
21757                if(options.scrollBar){
21758                    e.preventDefault ? e.preventDefault() : e.returnValue = false;
21759                }
21760
21761                var activeSection = $(SECTION_ACTIVE_SEL);
21762                var scrollable = options.scrollOverflowHandler.scrollable(activeSection);
21763
21764                //time difference between the last scroll and the current one
21765                var timeDiff = curTime-prevTime;
21766                prevTime = curTime;
21767
21768                //haven't they scrolled in a while?
21769                //(enough to be consider a different scrolling action to scroll another section)
21770                if(timeDiff > 200){
21771                    //emptying the array, we dont care about old scrollings for our averages
21772                    scrollings = [];
21773                }
21774
21775                if(canScroll){
21776                    var averageEnd = getAverage(scrollings, 10);
21777                    var averageMiddle = getAverage(scrollings, 70);
21778                    var isAccelerating = averageEnd >= averageMiddle;
21779
21780                    //to avoid double swipes...
21781                    if(isAccelerating && isScrollingVertically){
21782                        //scrolling down?
21783                        if (delta < 0) {
21784                            scrolling('down', scrollable);
21785
21786                        //scrolling up?
21787                        }else {
21788                            scrolling('up', scrollable);
21789                        }
21790                    }
21791                }
21792
21793                return false;
21794            }
21795
21796            if(options.fitToSection){
21797                //stopping the auto scroll to adjust to a section
21798                $htmlBody.stop();
21799            }
21800        }
21801
21802        /**
21803        * Slides a slider to the given direction.
21804        * Optional `section` param.
21805        */
21806        function moveSlide(direction, section){
21807            var activeSection = typeof section === 'undefined' ? $(SECTION_ACTIVE_SEL) : section;
21808            var slides = activeSection.find(SLIDES_WRAPPER_SEL);
21809            var numSlides = slides.find(SLIDE_SEL).length;
21810
21811            // more than one slide needed and nothing should be sliding
21812            if (!slides.length || slideMoving || numSlides < 2) {
21813                return;
21814            }
21815
21816            var currentSlide = slides.find(SLIDE_ACTIVE_SEL);
21817            var destiny = null;
21818
21819            if(direction === 'left'){
21820                destiny = currentSlide.prev(SLIDE_SEL);
21821            }else{
21822                destiny = currentSlide.next(SLIDE_SEL);
21823            }
21824
21825            //isn't there a next slide in the secuence?
21826            if(!destiny.length){
21827                //respect loopHorizontal settin
21828                if (!options.loopHorizontal) return;
21829
21830                if(direction === 'left'){
21831                    destiny = currentSlide.siblings(':last');
21832                }else{
21833                    destiny = currentSlide.siblings(':first');
21834                }
21835            }
21836
21837            slideMoving = true;
21838
21839            landscapeScroll(slides, destiny, direction);
21840        }
21841
21842        /**
21843        * Maintains the active slides in the viewport
21844        * (Because the `scroll` animation might get lost with some actions, such as when using continuousVertical)
21845        */
21846        function keepSlidesPosition(){
21847            $(SLIDE_ACTIVE_SEL).each(function(){
21848                silentLandscapeScroll($(this), 'internal');
21849            });
21850        }
21851
21852        var previousDestTop = 0;
21853        /**
21854        * Returns the destination Y position based on the scrolling direction and
21855        * the height of the section.
21856        */
21857        function getDestinationPosition(element){
21858            var elemPosition = element.position();
21859
21860            //top of the desination will be at the top of the viewport
21861            var position = elemPosition.top;
21862            var isScrollingDown =  elemPosition.top > previousDestTop;
21863            var sectionBottom = position - windowsHeight + element.outerHeight();
21864            var bigSectionsDestination = options.bigSectionsDestination;
21865
21866            //Uncode addition
21867            var containerH = container.outerHeight();
21868            var containerPosition = container.offset();
21869
21870            //########### Commented by Uncode - START ###########
21871            //is the destination element bigger than the viewport?
21872            // if(element.outerHeight() > windowsHeight){
21873            //     //scrolling up?
21874            //     if(!isScrollingDown && !bigSectionsDestination || bigSectionsDestination === 'bottom' ){
21875            //         position = sectionBottom;
21876            //     }
21877            // }
21878
21879            // //sections equal or smaller than the viewport height && scrolling down? ||  is resizing and its in the last section
21880            // else if(isScrollingDown || (isResizing && element.is(':last-child')) ){
21881            //     //The bottom of the destination will be at the bottom of the viewport
21882            //     position = sectionBottom;
21883            // }
21884            //########### Commented by Uncode - END ###########
21885
21886            //Uncode addition
21887            if ( !$masthead.hasClass('menu-transparent') && $('body').hasClass('uncode-fp-menu-shrink') && !element.is(':first-child') )
21888                position += 18;
21889            if ( ( containerH + menuHeight + bodyBorder + adminBarHeight - windowsHeight ) < position || ( isResizing && element.is(':last-child') ) ) {
21890                position = sectionBottom + menuHeight + bodyBorder*2 + adminBarHeight;
21891            }
21892
21893            /*
21894            Keeping record of the last scrolled position to determine the scrolling direction.
21895            No conventional methods can be used as the scroll bar might not be present
21896            AND the section might not be active if it is auto-height and didnt reach the middle
21897            of the viewport.
21898            */
21899            previousDestTop = position;
21900            return position;
21901        }
21902
21903        /**
21904        * Scrolls the site to the given element and scrolls to the slide if a callback is given.
21905        */
21906        function scrollPage(element, callback, isMovementUp){
21907            if(typeof element === 'undefined'){ return; } //there's no element to scroll, leaving the function
21908
21909            var dtop = getDestinationPosition(element);
21910            var slideAnchorLink;
21911            var slideIndex;
21912
21913            //local variables
21914            var v = {
21915                element: element,
21916                callback: callback,
21917                isMovementUp: isMovementUp,
21918                dtop: dtop,
21919                yMovement: getYmovement(element),
21920                anchorLink: element.data('anchor'),
21921                sectionIndex: element.index(SECTION_SEL),
21922                activeSlide: element.find(SLIDE_ACTIVE_SEL),
21923                activeSection: $(SECTION_ACTIVE_SEL),
21924                leavingSection: $(SECTION_ACTIVE_SEL).index(SECTION_SEL) + 1,
21925
21926                //caching the value of isResizing at the momment the function is called
21927                //because it will be checked later inside a setTimeout and the value might change
21928                localIsResizing: isResizing
21929            };
21930
21931            //quiting when destination scroll is the same as the current one
21932            if((v.activeSection.is(element) && !isResizing) || (options.scrollBar && $window.scrollTop() === v.dtop && !element.hasClass(AUTO_HEIGHT) )){ return; }
21933
21934            if(v.activeSlide.length){
21935                slideAnchorLink = v.activeSlide.data('anchor');
21936                slideIndex = v.activeSlide.index();
21937            }
21938
21939            // If continuousVertical && we need to wrap around
21940            if (options.autoScrolling && options.continuousVertical && typeof (v.isMovementUp) !== "undefined" &&
21941                ((!v.isMovementUp && v.yMovement == 'up') || // Intending to scroll down but about to go up or
21942                (v.isMovementUp && v.yMovement == 'down'))) { // intending to scroll up but about to go down
21943
21944                v = createInfiniteSections(v);
21945            }
21946
21947            //callback (onLeave) if the site is not just resizing and readjusting the slides
21948            if($.isFunction(options.onLeave) && !v.localIsResizing){
21949                if(options.onLeave.call(v.activeSection, v.leavingSection, (v.sectionIndex + 1), v.yMovement) === false){
21950                    return;
21951                }
21952            }
21953
21954            //pausing media of the leaving section (if we are not just resizing, as destinatino will be the same one)
21955            if(!v.localIsResizing){
21956                stopMedia(v.activeSection);
21957            }
21958
21959            options.scrollOverflowHandler.beforeLeave();
21960            element.addClass(ACTIVE).siblings().removeClass(ACTIVE);
21961            lazyLoad(element);
21962            options.scrollOverflowHandler.onLeave();
21963
21964
21965            //preventing from activating the MouseWheelHandler event
21966            //more than once if the page is scrolling
21967            canScroll = false;
21968
21969            setState(slideIndex, slideAnchorLink, v.anchorLink, v.sectionIndex);
21970
21971            performMovement(v);
21972
21973            //flag to avoid callingn `scrollPage()` twice in case of using anchor links
21974            lastScrolledDestiny = v.anchorLink;
21975
21976            //avoid firing it twice (as it does also on scroll)
21977            activateMenuAndNav(v.anchorLink, v.sectionIndex);
21978        }
21979
21980        /**
21981        * Performs the vertical movement (by CSS3 or by jQuery)
21982        */
21983        function performMovement(v){
21984            // using CSS3 translate functionality
21985            if (options.css3 && options.autoScrolling && !options.scrollBar) {
21986
21987                // The first section can have a negative value in iOS 10. Not quite sure why: -0.0142822265625
21988                // that's why we round it to 0.
21989                var translate3d = 'translate3d(0px, -' + Math.round(v.dtop) + 'px, 0px)';
21990                transformContainer(translate3d, true);
21991
21992                //even when the scrollingSpeed is 0 there's a little delay, which might cause the
21993                //scrollingSpeed to change in case of using silentMoveTo();
21994                if(options.scrollingSpeed){
21995                    clearTimeout(afterSectionLoadsId);
21996                    afterSectionLoadsId = setTimeout(function () {
21997                        afterSectionLoads(v);
21998                    }, options.scrollingSpeed);
21999                }else{
22000                    afterSectionLoads(v);
22001                }
22002            }
22003
22004            // using jQuery animate
22005            else{
22006                var scrollSettings = getScrollSettings(v);
22007
22008                $(scrollSettings.element).animate(
22009                    scrollSettings.options,
22010                options.scrollingSpeed, options.easing).promise().done(function () { //only one single callback in case of animating  `html, body`
22011                    if(options.scrollBar){
22012
22013                        /* Hack!
22014                        The timeout prevents setting the most dominant section in the viewport as "active" when the user
22015                        scrolled to a smaller section by using the mousewheel (auto scrolling) rather than draging the scroll bar.
22016
22017                        When using scrollBar:true It seems like the scroll events still getting propagated even after the scrolling animation has finished.
22018                        */
22019                        setTimeout(function(){
22020                            afterSectionLoads(v);
22021                        },30);
22022                    }else{
22023                        afterSectionLoads(v);
22024                    }
22025                });
22026            }
22027        }
22028
22029        /**
22030        * Gets the scrolling settings depending on the plugin autoScrolling option
22031        */
22032        function getScrollSettings(v){
22033            var scroll = {};
22034
22035            if(options.autoScrolling && !options.scrollBar){
22036                scroll.options = { 'top': -v.dtop};
22037                scroll.element = WRAPPER_SEL;
22038            }else{
22039                scroll.options = { 'scrollTop': v.dtop};
22040                scroll.element = 'html, body';
22041            }
22042
22043            return scroll;
22044        }
22045
22046        /**
22047        * Adds sections before or after the current one to create the infinite effect.
22048        */
22049        function createInfiniteSections(v){
22050            // Scrolling down
22051            if (!v.isMovementUp) {
22052                // Move all previous sections to after the active section
22053                $(SECTION_ACTIVE_SEL).after(v.activeSection.prevAll(SECTION_SEL).get().reverse());
22054            }
22055            else { // Scrolling up
22056                // Move all next sections to before the active section
22057                $(SECTION_ACTIVE_SEL).before(v.activeSection.nextAll(SECTION_SEL));
22058            }
22059
22060            // Maintain the displayed position (now that we changed the element order)
22061            silentScroll($(SECTION_ACTIVE_SEL).position().top);
22062
22063            // Maintain the active slides visible in the viewport
22064            keepSlidesPosition();
22065
22066            // save for later the elements that still need to be reordered
22067            v.wrapAroundElements = v.activeSection;
22068
22069            // Recalculate animation variables
22070            v.dtop = v.element.position().top;
22071            v.yMovement = getYmovement(v.element);
22072
22073            return v;
22074        }
22075
22076        /**
22077        * Fix section order after continuousVertical changes have been animated
22078        */
22079        function continuousVerticalFixSectionOrder (v) {
22080            // If continuousVertical is in effect (and autoScrolling would also be in effect then),
22081            // finish moving the elements around so the direct navigation will function more simply
22082            if (!v.wrapAroundElements || !v.wrapAroundElements.length) {
22083                return;
22084            }
22085
22086            if (v.isMovementUp) {
22087                $(SECTION_FIRST_SEL).before(v.wrapAroundElements);
22088            }
22089            else {
22090                $(SECTION_LAST_SEL).after(v.wrapAroundElements);
22091            }
22092
22093            silentScroll($(SECTION_ACTIVE_SEL).position().top);
22094
22095            // Maintain the active slides visible in the viewport
22096            keepSlidesPosition();
22097        }
22098
22099
22100        /**
22101        * Actions to do once the section is loaded.
22102        */
22103        function afterSectionLoads (v){
22104            continuousVerticalFixSectionOrder(v);
22105
22106            //callback (afterLoad) if the site is not just resizing and readjusting the slides
22107            $.isFunction(options.afterLoad) && !v.localIsResizing && options.afterLoad.call(v.element, v.anchorLink, (v.sectionIndex + 1));
22108            options.scrollOverflowHandler.afterLoad();
22109
22110            if(!v.localIsResizing){
22111                playMedia(v.element);
22112            }
22113
22114            v.element.addClass(COMPLETELY).siblings().removeClass(COMPLETELY);
22115
22116            canScroll = true;
22117
22118            $.isFunction(v.callback) && v.callback.call(this);
22119        }
22120
22121        /**
22122        * Sets the value for the given attribute from the `data-` attribute with the same suffix
22123        * ie: data-srcset ==> srcset  |  data-src ==> src
22124        */
22125        function setSrc(element, attribute){
22126            element
22127                .attr(attribute, element.data(attribute))
22128                .removeAttr('data-' + attribute);
22129        }
22130
22131        /**
22132        * Lazy loads image, video and audio elements.
22133        */
22134        function lazyLoad(destiny){
22135            if (!options.lazyLoading){
22136                return;
22137            }
22138
22139            var panel = getSlideOrSection(destiny);
22140            var element;
22141
22142            panel.find('img[data-src], img[data-srcset], source[data-src], audio[data-src], iframe[data-src]').each(function(){
22143                element = $(this);
22144
22145                $.each(['src', 'srcset'], function(index, type){
22146                    var attribute = element.attr('data-' + type);
22147                    if(typeof attribute !== 'undefined' && attribute){
22148                        setSrc(element, type);
22149                    }
22150                });
22151
22152                if(element.is('source')){
22153                    element.closest('video').get(0).load();
22154                }
22155            });
22156        }
22157
22158        /**
22159        * Plays video and audio elements.
22160        */
22161        function playMedia(destiny){
22162            var panel = getSlideOrSection(destiny);
22163
22164            //playing HTML5 media elements
22165            panel.find('video, audio').each(function(){
22166                var element = $(this).get(0);
22167
22168                if( element.hasAttribute('data-autoplay') && typeof element.play === 'function' ) {
22169                    element.play();
22170                }
22171            });
22172
22173            //youtube videos
22174            panel.find('iframe[src*="youtube.com/embed/"]').each(function(){
22175                var element = $(this).get(0);
22176
22177                if ( element.hasAttribute('data-autoplay') ){
22178                    playYoutube(element);
22179                }
22180
22181                //in case the URL was not loaded yet. On page load we need time for the new URL (with the API string) to load.
22182                element.onload = function() {
22183                    if ( element.hasAttribute('data-autoplay') ){
22184                        playYoutube(element);
22185                    }
22186                };
22187            });
22188        }
22189
22190        /**
22191        * Plays a youtube video
22192        */
22193        function playYoutube(element){
22194            element.contentWindow.postMessage('{"event":"command","func":"playVideo","args":""}', '*');
22195        }
22196
22197        /**
22198        * Stops video and audio elements.
22199        */
22200        function stopMedia(destiny){
22201            var panel = getSlideOrSection(destiny);
22202
22203            //stopping HTML5 media elements
22204            panel.find('video, audio').each(function(){
22205                var element = $(this).get(0);
22206
22207                if( !element.hasAttribute('data-keepplaying') && typeof element.pause === 'function' ) {
22208                    element.pause();
22209                }
22210            });
22211
22212            //youtube videos
22213            panel.find('iframe[src*="youtube.com/embed/"]').each(function(){
22214                var element = $(this).get(0);
22215
22216                if( /youtube\.com\/embed\//.test($(this).attr('src')) && !element.hasAttribute('data-keepplaying')){
22217                    $(this).get(0).contentWindow.postMessage('{"event":"command","func":"pauseVideo","args":""}','*');
22218                }
22219            });
22220        }
22221
22222        /**
22223        * Gets the active slide (or section) for the given section
22224        */
22225        function getSlideOrSection(destiny){
22226            var slide = destiny.find(SLIDE_ACTIVE_SEL);
22227            if( slide.length ) {
22228                destiny = $(slide);
22229            }
22230
22231            return destiny;
22232        }
22233
22234        /**
22235        * Scrolls to the anchor in the URL when loading the site
22236        */
22237        function scrollToAnchor(){
22238            //getting the anchor link in the URL and deleting the `#`
22239            var value =  window.location.hash.replace('#', '').split('/');
22240            var sectionAnchor = decodeURIComponent(value[0]);
22241            var slideAnchor = decodeURIComponent(value[1]);
22242
22243            if(sectionAnchor){  //if theres any #
22244                if(options.animateAnchor){
22245                    scrollPageAndSlide(sectionAnchor, slideAnchor);
22246                }else{
22247                    silentMoveTo(sectionAnchor, slideAnchor);
22248                }
22249            }
22250        }
22251
22252        /**
22253        * Detecting any change on the URL to scroll to the given anchor link
22254        * (a way to detect back history button as we play with the hashes on the URL)
22255        */
22256        function hashChangeHandler(){
22257            if(!isScrolling && !options.lockAnchors){
22258                var value =  window.location.hash.replace('#', '').split('/');
22259                var sectionAnchor = decodeURIComponent(value[0]);
22260                var slideAnchor = decodeURIComponent(value[1]);
22261
22262                    //when moving to a slide in the first section for the first time (first time to add an anchor to the URL)
22263                    var isFirstSlideMove =  (typeof lastScrolledDestiny === 'undefined');
22264                    var isFirstScrollMove = (typeof lastScrolledDestiny === 'undefined' && typeof slideAnchor === 'undefined' && !slideMoving);
22265
22266
22267                if(sectionAnchor.length){
22268                    /*in order to call scrollpage() only once for each destination at a time
22269                    It is called twice for each scroll otherwise, as in case of using anchorlinks `hashChange`
22270                    event is fired on every scroll too.*/
22271                    if ((sectionAnchor && sectionAnchor !== lastScrolledDestiny) && !isFirstSlideMove || isFirstScrollMove || (!slideMoving && lastScrolledSlide != slideAnchor ))  {
22272                        scrollPageAndSlide(sectionAnchor, slideAnchor);
22273                    }
22274                }
22275            }
22276        }
22277
22278        //Sliding with arrow keys, both, vertical and horizontal
22279        function keydownHandler(e) {
22280
22281            clearTimeout(keydownId);
22282
22283            var activeElement = $(':focus');
22284
22285            if(!activeElement.is('textarea') && !activeElement.is('input') && !activeElement.is('select') &&
22286                activeElement.attr('contentEditable') !== "true" && activeElement.attr('contentEditable') !== '' &&
22287                options.keyboardScrolling && options.autoScrolling){
22288                var keyCode = e.which;
22289
22290                //preventing the scroll with arrow keys & spacebar & Page Up & Down keys
22291                var keyControls = [40, 38, 32, 33, 34];
22292                if($.inArray(keyCode, keyControls) > -1){
22293                    e.preventDefault();
22294                }
22295
22296                controlPressed = e.ctrlKey;
22297
22298                keydownId = setTimeout(function(){
22299                    onkeydown(e);
22300                },150);
22301            }
22302        }
22303
22304        function tooltipTextHandler(){
22305            $(this).prev().trigger('click');
22306        }
22307
22308        //to prevent scrolling while zooming
22309        function keyUpHandler(e){
22310            if(isWindowFocused){ //the keyup gets fired on new tab ctrl + t in Firefox
22311                controlPressed = e.ctrlKey;
22312            }
22313        }
22314
22315        //binding the mousemove when the mouse's middle button is released
22316        function mouseDownHandler(e){
22317            //middle button
22318            if (e.which == 2){
22319                oldPageY = e.pageY;
22320                container.on('mousemove', mouseMoveHandler);
22321            }
22322        }
22323
22324        //unbinding the mousemove when the mouse's middle button is released
22325        function mouseUpHandler(e){
22326            //middle button
22327            if (e.which == 2){
22328                container.off('mousemove');
22329            }
22330        }
22331
22332        //Scrolling horizontally when clicking on the slider controls.
22333        function slideArrowHandler(){
22334            var section = $(this).closest(SECTION_SEL);
22335
22336            if ($(this).hasClass(SLIDES_PREV)) {
22337                if(isScrollAllowed.m.left){
22338                    moveSlideLeft(section);
22339                }
22340            } else {
22341                if(isScrollAllowed.m.right){
22342                    moveSlideRight(section);
22343                }
22344            }
22345        }
22346
22347        //when opening a new tab (ctrl + t), `control` won't be pressed when coming back.
22348        function blurHandler(){
22349            isWindowFocused = false;
22350            controlPressed = false;
22351        }
22352
22353        //Scrolls to the section when clicking the navigation bullet
22354        function sectionBulletHandler(e){
22355            e.preventDefault();
22356            var index = $(this).parent().index();
22357            scrollPage($(SECTION_SEL).eq(index));
22358        }
22359
22360        //Scrolls the slider to the given slide destination for the given section
22361        function slideBulletHandler(e){
22362            e.preventDefault();
22363            var slides = $(this).closest(SECTION_SEL).find(SLIDES_WRAPPER_SEL);
22364            var destiny = slides.find(SLIDE_SEL).eq($(this).closest('li').index());
22365
22366            landscapeScroll(slides, destiny);
22367        }
22368
22369        /**
22370        * Keydown event
22371        */
22372        function onkeydown(e){
22373            var shiftPressed = e.shiftKey;
22374
22375            //do nothing if we can not scroll or we are not using horizotnal key arrows.
22376            if(!canScroll && [37,39].indexOf(e.which) < 0){
22377                return;
22378            }
22379
22380            switch (e.which) {
22381                //up
22382                case 38:
22383                case 33:
22384                    if(isScrollAllowed.k.up){
22385                        moveSectionUp();
22386                    }
22387                    break;
22388
22389                //down
22390                case 32: //spacebar
22391                    if(shiftPressed && isScrollAllowed.k.up){
22392                        moveSectionUp();
22393                        break;
22394                    }
22395                /* falls through */
22396                case 40:
22397                case 34:
22398                    if(isScrollAllowed.k.down){
22399                        moveSectionDown();
22400                    }
22401                    break;
22402
22403                //Home
22404                case 36:
22405                    if(isScrollAllowed.k.up){
22406                        moveTo(1);
22407                    }
22408                    break;
22409
22410                //End
22411                case 35:
22412                     if(isScrollAllowed.k.down){
22413                        moveTo( $(SECTION_SEL).length );
22414                    }
22415                    break;
22416
22417                //left
22418                case 37:
22419                    if(isScrollAllowed.k.left){
22420                        moveSlideLeft();
22421                    }
22422                    break;
22423
22424                //right
22425                case 39:
22426                    if(isScrollAllowed.k.right){
22427                        moveSlideRight();
22428                    }
22429                    break;
22430
22431                default:
22432                    return; // exit this handler for other keys
22433            }
22434        }
22435
22436        /**
22437        * Detecting the direction of the mouse movement.
22438        * Used only for the middle button of the mouse.
22439        */
22440        var oldPageY = 0;
22441        function mouseMoveHandler(e){
22442            if(canScroll){
22443                // moving up
22444                if (e.pageY < oldPageY && isScrollAllowed.m.up){
22445                    moveSectionUp();
22446                }
22447
22448                // moving down
22449                else if(e.pageY > oldPageY && isScrollAllowed.m.down){
22450                    moveSectionDown();
22451                }
22452            }
22453            oldPageY = e.pageY;
22454        }
22455
22456        /**
22457        * Scrolls horizontal sliders.
22458        */
22459        function landscapeScroll(slides, destiny, direction){
22460            var section = slides.closest(SECTION_SEL);
22461            var v = {
22462                slides: slides,
22463                destiny: destiny,
22464                direction: direction,
22465                destinyPos: destiny.position(),
22466                slideIndex: destiny.index(),
22467                section: section,
22468                sectionIndex: section.index(SECTION_SEL),
22469                anchorLink: section.data('anchor'),
22470                slidesNav: section.find(SLIDES_NAV_SEL),
22471                slideAnchor:  getAnchor(destiny),
22472                prevSlide: section.find(SLIDE_ACTIVE_SEL),
22473                prevSlideIndex: section.find(SLIDE_ACTIVE_SEL).index(),
22474
22475                //caching the value of isResizing at the momment the function is called
22476                //because it will be checked later inside a setTimeout and the value might change
22477                localIsResizing: isResizing
22478            };
22479            v.xMovement = getXmovement(v.prevSlideIndex, v.slideIndex);
22480            v.direction = v.direction ? v.direction : v.xMovement;
22481
22482            //important!! Only do it when not resizing
22483            if(!v.localIsResizing){
22484                //preventing from scrolling to the next/prev section when using scrollHorizontally
22485                canScroll = false;
22486            }
22487
22488            if(options.onSlideLeave){
22489
22490                //if the site is not just resizing and readjusting the slides
22491                if(!v.localIsResizing && v.xMovement!=='none'){
22492                    if($.isFunction( options.onSlideLeave )){
22493                        if(options.onSlideLeave.call( v.prevSlide, v.anchorLink, (v.sectionIndex + 1), v.prevSlideIndex, v.xMovement, v.slideIndex ) === false){
22494                            slideMoving = false;
22495                            return;
22496                        }
22497                    }
22498                }
22499            }
22500
22501            destiny.addClass(ACTIVE).siblings().removeClass(ACTIVE);
22502
22503            if(!v.localIsResizing){
22504                stopMedia(v.prevSlide);
22505                lazyLoad(destiny);
22506            }
22507
22508            if(!options.loopHorizontal && options.controlArrows){
22509                //hidding it for the fist slide, showing for the rest
22510                section.find(SLIDES_ARROW_PREV_SEL).toggle(v.slideIndex!==0);
22511
22512                //hidding it for the last slide, showing for the rest
22513                section.find(SLIDES_ARROW_NEXT_SEL).toggle(!destiny.is(':last-child'));
22514            }
22515
22516            //only changing the URL if the slides are in the current section (not for resize re-adjusting)
22517            if(section.hasClass(ACTIVE) && !v.localIsResizing){
22518                setState(v.slideIndex, v.slideAnchor, v.anchorLink, v.sectionIndex);
22519            }
22520
22521            performHorizontalMove(slides, v, true);
22522        }
22523
22524
22525        function afterSlideLoads(v){
22526            activeSlidesNavigation(v.slidesNav, v.slideIndex);
22527
22528            //if the site is not just resizing and readjusting the slides
22529            if(!v.localIsResizing){
22530                $.isFunction( options.afterSlideLoad ) && options.afterSlideLoad.call( v.destiny, v.anchorLink, (v.sectionIndex + 1), v.slideAnchor, v.slideIndex);
22531
22532                //needs to be inside the condition to prevent problems with continuousVertical and scrollHorizontally
22533                //and to prevent double scroll right after a windows resize
22534                canScroll = true;
22535
22536                playMedia(v.destiny);
22537            }
22538
22539            //letting them slide again
22540            slideMoving = false;
22541        }
22542
22543        /**
22544        * Performs the horizontal movement. (CSS3 or jQuery)
22545        *
22546        * @param fireCallback {Bool} - determines whether or not to fire the callback
22547        */
22548        function performHorizontalMove(slides, v, fireCallback){
22549            var destinyPos = v.destinyPos;
22550
22551            if(options.css3){
22552                var translate3d = 'translate3d(-' + Math.round(destinyPos.left) + 'px, 0px, 0px)';
22553
22554                addAnimation(slides.find(SLIDES_CONTAINER_SEL)).css(getTransforms(translate3d), v);
22555
22556                afterSlideLoadsId = setTimeout(function(){
22557                    fireCallback && afterSlideLoads(v);
22558                }, options.scrollingSpeed, options.easing);
22559            }else{
22560                slides.animate({
22561                    scrollLeft : Math.round(destinyPos.left)
22562                }, options.scrollingSpeed, options.easing, function() {
22563
22564                    fireCallback && afterSlideLoads(v);
22565                });
22566            }
22567        }
22568
22569        /**
22570        * Sets the state for the horizontal bullet navigations.
22571        */
22572        function activeSlidesNavigation(slidesNav, slideIndex){
22573            slidesNav.find(ACTIVE_SEL).removeClass(ACTIVE);
22574            slidesNav.find('li').eq(slideIndex).find('a').addClass(ACTIVE);
22575        }
22576
22577        var previousHeight = windowsHeight;
22578
22579        //when resizing the site, we adjust the heights of the sections, slimScroll...
22580        function resizeHandler(){
22581            //checking if it needs to get responsive
22582            responsive();
22583
22584            // rebuild immediately on touch devices
22585            if (isTouchDevice) {
22586                var activeElement = $(document.activeElement);
22587
22588                //if the keyboard is NOT visible
22589                if (!activeElement.is('textarea') && !activeElement.is('input') && !activeElement.is('select')) {
22590                    var currentHeight = $window.height();
22591
22592                    //making sure the change in the viewport size is enough to force a rebuild. (20 % of the window to avoid problems when hidding scroll bars)
22593                    if( Math.abs(currentHeight - previousHeight) > (20 * Math.max(previousHeight, currentHeight) / 100) ){
22594                        reBuild(true);
22595                        previousHeight = currentHeight;
22596                    }
22597                }
22598            }else{
22599                //in order to call the functions only when the resize is finished
22600                //http://stackoverflow.com/questions/4298612/jquery-how-to-call-resize-event-only-once-its-finished-resizing
22601                clearTimeout(resizeId);
22602
22603                resizeId = setTimeout(function(){
22604                    reBuild(true);
22605                }, 350);
22606            }
22607        }
22608
22609        /**
22610        * Checks if the site needs to get responsive and disables autoScrolling if so.
22611        * A class `fp-responsive` is added to the plugin's container in case the user wants to use it for his own responsive CSS.
22612        */
22613        function responsive(){
22614            var widthLimit = options.responsive || options.responsiveWidth; //backwards compatiblity
22615            var heightLimit = options.responsiveHeight;
22616
22617            //only calculating what we need. Remember its called on the resize event.
22618            var isBreakingPointWidth = widthLimit && $window.outerWidth() < widthLimit;
22619            var isBreakingPointHeight = heightLimit && $window.height() < heightLimit;
22620
22621            if(widthLimit && heightLimit){
22622                setResponsive(isBreakingPointWidth || isBreakingPointHeight);
22623            }
22624            else if(widthLimit){
22625                setResponsive(isBreakingPointWidth);
22626            }
22627            else if(heightLimit){
22628                setResponsive(isBreakingPointHeight);
22629            }
22630        }
22631
22632        /**
22633        * Adds transition animations for the given element
22634        */
22635        function addAnimation(container, element){
22636            var transition = 'all ' + options.scrollingSpeed + 'ms ' + options.easingcss3;
22637
22638            container.removeClass(NO_TRANSITION);
22639            return container.css({
22640                '-webkit-transition': transition,
22641                'transition': transition
22642            });
22643        }
22644
22645        /**
22646        * Remove transition animations for the given element
22647        */
22648        function removeAnimation(element){
22649            return element.addClass(NO_TRANSITION);
22650        }
22651
22652        /**
22653        * Activating the vertical navigation bullets according to the given slide name.
22654        */
22655        function activateNavDots(name, sectionIndex){
22656            if(options.navigation){
22657                $(SECTION_NAV_SEL).find(ACTIVE_SEL).removeClass(ACTIVE);
22658                if(name){
22659                    $(SECTION_NAV_SEL).find('a[href="#' + name + '"]').addClass(ACTIVE);
22660                }else{
22661                    $(SECTION_NAV_SEL).find('li').eq(sectionIndex).find('a').addClass(ACTIVE);
22662                }
22663            }
22664        }
22665
22666        /**
22667        * Activating the website main menu elements according to the given slide name.
22668        */
22669        function activateMenuElement(name){
22670            if(options.menu){
22671                $(options.menu).find(ACTIVE_SEL).removeClass(ACTIVE);
22672                $(options.menu).find('[data-menuanchor="'+name+'"]').addClass(ACTIVE);
22673            }
22674        }
22675
22676        /**
22677        * Sets to active the current menu and vertical nav items.
22678        */
22679        function activateMenuAndNav(anchor, index){
22680            activateMenuElement(anchor);
22681            activateNavDots(anchor, index);
22682        }
22683
22684        /**
22685        * Retuns `up` or `down` depending on the scrolling movement to reach its destination
22686        * from the current section.
22687        */
22688        function getYmovement(destiny){
22689            var fromIndex = $(SECTION_ACTIVE_SEL).index(SECTION_SEL);
22690            var toIndex = destiny.index(SECTION_SEL);
22691            if( fromIndex == toIndex){
22692                return 'none';
22693            }
22694            if(fromIndex > toIndex){
22695                return 'up';
22696            }
22697            return 'down';
22698        }
22699
22700        /**
22701        * Retuns `right` or `left` depending on the scrolling movement to reach its destination
22702        * from the current slide.
22703        */
22704        function getXmovement(fromIndex, toIndex){
22705            if( fromIndex == toIndex){
22706                return 'none';
22707            }
22708            if(fromIndex > toIndex){
22709                return 'left';
22710            }
22711            return 'right';
22712        }
22713
22714        /**
22715        * Checks if the element needs scrollbar and if the user wants to apply it.
22716        * If so it creates it.
22717        *
22718        * @param {Object} element   jQuery object of the section or slide
22719        */
22720        function createScrollBar(element){
22721            //User doesn't want scrollbar here? Sayonara baby!
22722            if(element.hasClass('fp-noscroll')) return;
22723
22724            //needed to make `scrollHeight` work under Opera 12
22725            element.css('overflow', 'hidden');
22726
22727            var scrollOverflowHandler = options.scrollOverflowHandler;
22728            var wrap = scrollOverflowHandler.wrapContent();
22729            //in case element is a slide
22730            var section = element.closest(SECTION_SEL);
22731            var scrollable = scrollOverflowHandler.scrollable(element);
22732            var contentHeight;
22733
22734            //if there was scroll, the contentHeight will be the one in the scrollable section
22735            if(scrollable.length){
22736                contentHeight = scrollOverflowHandler.scrollHeight(element);
22737            }else{
22738                contentHeight = element.get(0).scrollHeight;
22739                if(options.verticalCentered){
22740                    contentHeight = element.find(TABLE_CELL_SEL).get(0).scrollHeight;
22741                }
22742            }
22743
22744            var scrollHeight = windowsHeight - parseInt(section.css('padding-bottom')) - parseInt(section.css('padding-top'));
22745
22746            //needs scroll?
22747            if ( contentHeight > scrollHeight) {
22748                //did we already have an scrollbar ? Updating it
22749                if(scrollable.length){
22750                    scrollOverflowHandler.update(element, scrollHeight);
22751                }
22752                //creating the scrolling
22753                else{
22754                    if(options.verticalCentered){
22755                        element.find(TABLE_CELL_SEL).wrapInner(wrap);
22756                    }else{
22757                        element.wrapInner(wrap);
22758                    }
22759                    scrollOverflowHandler.create(element, scrollHeight);
22760                }
22761            }
22762            //removing the scrolling when it is not necessary anymore
22763            else{
22764                scrollOverflowHandler.remove(element);
22765            }
22766
22767            //undo
22768            element.css('overflow', '');
22769        }
22770
22771        function addTableClass(element){
22772            //In case we are styling for the 2nd time as in with reponsiveSlides
22773            if(!element.hasClass(TABLE)){
22774                element.addClass(TABLE).wrapInner('<div class="' + TABLE_CELL + '" style="height:' + getTableHeight(element) + 'px;" />');
22775            }
22776        }
22777
22778        function getTableHeight(element){
22779            var sectionHeight = windowsHeight;
22780
22781            if(options.paddingTop || options.paddingBottom){
22782                var section = element;
22783                if(!section.hasClass(SECTION)){
22784                    section = element.closest(SECTION_SEL);
22785                }
22786
22787                var paddings = parseInt(section.css('padding-top')) + parseInt(section.css('padding-bottom'));
22788                sectionHeight = (windowsHeight - paddings);
22789            }
22790
22791            return sectionHeight;
22792        }
22793
22794        /**
22795        * Adds a css3 transform property to the container class with or without animation depending on the animated param.
22796        */
22797        function transformContainer(translate3d, animated){
22798            if(animated){
22799                addAnimation(container);
22800            }else{
22801                removeAnimation(container);
22802            }
22803
22804            container.css(getTransforms(translate3d));
22805
22806            //syncronously removing the class after the animation has been applied.
22807            setTimeout(function(){
22808                container.removeClass(NO_TRANSITION);
22809            },10);
22810        }
22811
22812        /**
22813        * Gets a section by its anchor / index
22814        */
22815        function getSectionByAnchor(sectionAnchor){
22816            if(!sectionAnchor) return [];
22817
22818            var section = container.find(SECTION_SEL + '[data-anchor="'+sectionAnchor+'"]');
22819            if(!section.length){
22820                section = $(SECTION_SEL).eq( sectionAnchor -1);
22821            }
22822
22823            return section;
22824        }
22825
22826        /**
22827        * Gets a slide inside a given section by its anchor / index
22828        */
22829        function getSlideByAnchor(slideAnchor, section){
22830            var slides = section.find(SLIDES_WRAPPER_SEL);
22831            var slide =  slides.find(SLIDE_SEL + '[data-anchor="'+slideAnchor+'"]');
22832
22833            if(!slide.length){
22834                slide = slides.find(SLIDE_SEL).eq(slideAnchor);
22835            }
22836
22837            return slide;
22838        }
22839
22840        /**
22841        * Scrolls to the given section and slide anchors
22842        */
22843        function scrollPageAndSlide(destiny, slide){
22844            var section = getSectionByAnchor(destiny);
22845
22846            //do nothing if there's no section with the given anchor name
22847            if(!section.length) return;
22848
22849            //default slide
22850            if (typeof slide === 'undefined') {
22851                slide = 0;
22852            }
22853
22854            //we need to scroll to the section and then to the slide
22855            if (destiny !== lastScrolledDestiny && !section.hasClass(ACTIVE)){
22856                scrollPage(section, function(){
22857                    scrollSlider(section, slide);
22858                });
22859            }
22860            //if we were already in the section
22861            else{
22862                scrollSlider(section, slide);
22863            }
22864        }
22865
22866        /**
22867        * Scrolls the slider to the given slide destination for the given section
22868        */
22869        function scrollSlider(section, slideAnchor){
22870            if(typeof slideAnchor !== 'undefined'){
22871                var slides = section.find(SLIDES_WRAPPER_SEL);
22872                var destiny =  getSlideByAnchor(slideAnchor, section);
22873
22874                if(destiny.length){
22875                    landscapeScroll(slides, destiny);
22876                }
22877            }
22878        }
22879
22880        /**
22881        * Creates a landscape navigation bar with dots for horizontal sliders.
22882        */
22883        function addSlidesNavigation(section, numSlides){
22884            section.append('<div class="' + SLIDES_NAV + '"><ul></ul></div>');
22885            var nav = section.find(SLIDES_NAV_SEL);
22886
22887            //top or bottom
22888            nav.addClass(options.slidesNavPosition);
22889
22890            for(var i=0; i< numSlides; i++){
22891                nav.find('ul').append('<li><a href="#"><span></span></a></li>');
22892            }
22893
22894            //centering it
22895            nav.css('margin-left', '-' + (nav.width()/2) + 'px');
22896
22897            nav.find('li').first().find('a').addClass(ACTIVE);
22898        }
22899
22900
22901        /**
22902        * Sets the state of the website depending on the active section/slide.
22903        * It changes the URL hash when needed and updates the body class.
22904        */
22905        function setState(slideIndex, slideAnchor, anchorLink, sectionIndex){
22906            var sectionHash = '';
22907
22908            if(options.anchors.length && !options.lockAnchors){
22909
22910                //isn't it the first slide?
22911                if(slideIndex){
22912                    if(typeof anchorLink !== 'undefined'){
22913                        sectionHash = anchorLink;
22914                    }
22915
22916                    //slide without anchor link? We take the index instead.
22917                    if(typeof slideAnchor === 'undefined'){
22918                        slideAnchor = slideIndex;
22919                    }
22920
22921                    lastScrolledSlide = slideAnchor;
22922                    setUrlHash(sectionHash + '/' + slideAnchor);
22923
22924                //first slide won't have slide anchor, just the section one
22925                }else if(typeof slideIndex !== 'undefined'){
22926                    lastScrolledSlide = slideAnchor;
22927                    setUrlHash(anchorLink);
22928                }
22929
22930                //section without slides
22931                else{
22932                    setUrlHash(anchorLink);
22933                }
22934            }
22935
22936            setBodyClass();
22937        }
22938
22939        /**
22940        * Sets the URL hash.
22941        */
22942        function setUrlHash(url){
22943            //Uncode addition
22944            if ( typeof SiteParameters.slide_footer != 'undefined' && url == SiteParameters.slide_footer )
22945                return false;
22946
22947            if(options.recordHistory){
22948                location.hash = url;
22949            }else{
22950                //Uncode addition
22951                //Mobile Chrome doesn't work the normal way, so... lets use HTML5 for phones :)
22952                // if(isTouchDevice || isTouch){
22953                //     window.history.replaceState(undefined, undefined, '#' + url);
22954                // }else{
22955                //     var baseUrl = window.location.href.split('#')[0];
22956                //     window.location.replace( baseUrl + '#' + url );
22957                // }
22958                return false;
22959            }
22960        }
22961
22962        /**
22963        * Gets the anchor for the given slide / section. Its index will be used if there's none.
22964        */
22965        function getAnchor(element){
22966            var anchor = element.data('anchor');
22967            var index = element.index();
22968
22969            //Slide without anchor link? We take the index instead.
22970            if(typeof anchor === 'undefined'){
22971                anchor = index;
22972            }
22973
22974            return anchor;
22975        }
22976
22977        /**
22978        * Sets a class for the body of the page depending on the active section / slide
22979        */
22980        function setBodyClass(){
22981            var section = $(SECTION_ACTIVE_SEL);
22982            var slide = section.find(SLIDE_ACTIVE_SEL);
22983
22984            var sectionAnchor = getAnchor(section);
22985            var slideAnchor = getAnchor(slide);
22986
22987            var text = String(sectionAnchor);
22988
22989            if(slide.length){
22990                text = text + '-' + slideAnchor;
22991            }
22992
22993            //changing slash for dash to make it a valid CSS style
22994            text = text.replace('/', '-').replace('#','');
22995
22996            //removing previous anchor classes
22997            var classRe = new RegExp('\\b\\s?' + VIEWING_PREFIX + '-[^\\s]+\\b', "g");
22998            $body[0].className = $body[0].className.replace(classRe, '');
22999
23000            //adding the current anchor
23001            $body.addClass(VIEWING_PREFIX + '-' + text);
23002        }
23003
23004        /**
23005        * Checks for translate3d support
23006        * @return boolean
23007        * http://stackoverflow.com/questions/5661671/detecting-transform-translate3d-support
23008        */
23009        function support3d() {
23010            var el = document.createElement('p'),
23011                has3d,
23012                transforms = {
23013                    'webkitTransform':'-webkit-transform',
23014                    'OTransform':'-o-transform',
23015                    'msTransform':'-ms-transform',
23016                    'MozTransform':'-moz-transform',
23017                    'transform':'transform'
23018                };
23019
23020            // Add it to the body to get the computed style.
23021            document.body.insertBefore(el, null);
23022
23023            for (var t in transforms) {
23024                if (el.style[t] !== undefined) {
23025                    el.style[t] = 'translate3d(1px,1px,1px)';
23026                    has3d = window.getComputedStyle(el).getPropertyValue(transforms[t]);
23027                }
23028            }
23029
23030            document.body.removeChild(el);
23031
23032            return (has3d !== undefined && has3d.length > 0 && has3d !== 'none');
23033        }
23034
23035        /**
23036        * Removes the auto scrolling action fired by the mouse wheel and trackpad.
23037        * After this function is called, the mousewheel and trackpad movements won't scroll through sections.
23038        */
23039        function removeMouseWheelHandler(){
23040            if (document.addEventListener) {
23041                document.removeEventListener('mousewheel', MouseWheelHandler, false); //IE9, Chrome, Safari, Oper
23042                document.removeEventListener('wheel', MouseWheelHandler, false); //Firefox
23043                document.removeEventListener('MozMousePixelScroll', MouseWheelHandler, false); //old Firefox
23044            } else {
23045                document.detachEvent('onmousewheel', MouseWheelHandler); //IE 6/7/8
23046            }
23047        }
23048
23049        /**
23050        * Adds the auto scrolling action for the mouse wheel and trackpad.
23051        * After this function is called, the mousewheel and trackpad movements will scroll through sections
23052        * https://developer.mozilla.org/en-US/docs/Web/Events/wheel
23053        */
23054        function addMouseWheelHandler(){
23055            var prefix = '';
23056            var _addEventListener;
23057
23058            if (window.addEventListener){
23059                _addEventListener = "addEventListener";
23060            }else{
23061                _addEventListener = "attachEvent";
23062                prefix = 'on';
23063            }
23064
23065             // detect available wheel event
23066            var support = 'onwheel' in document.createElement('div') ? 'wheel' : // Modern browsers support "wheel"
23067                      document.onmousewheel !== undefined ? 'mousewheel' : // Webkit and IE support at least "mousewheel"
23068                      'DOMMouseScroll'; // let's assume that remaining browsers are older Firefox
23069            var passiveEvent = g_supportsPassive ? {passive: false }
23069: false;
23070
23071
23072            if(support == 'DOMMouseScroll'){
23073                document[ _addEventListener ](prefix + 'MozMousePixelScroll', MouseWheelHandler, passiveEvent);
23074            }
23075
23076            //handle MozMousePixelScroll in older Firefox
23077            else{
23078                document[ _addEventListener ](prefix + support, MouseWheelHandler, passiveEvent);
23079            }
23080        }
23081
23082        /**
23083        * Binding the mousemove when the mouse's middle button is pressed
23084        */
23085        function addMiddleWheelHandler(){
23086            container
23087                .on('mousedown', mouseDownHandler)
23088                .on('mouseup', mouseUpHandler);
23089        }
23090
23091        /**
23092        * Unbinding the mousemove when the mouse's middle button is released
23093        */
23094        function removeMiddleWheelHandler(){
23095            container
23096                .off('mousedown', mouseDownHandler)
23097                .off('mouseup', mouseUpHandler);
23098        }
23099
23100        /**
23101        * Adds the possibility to auto scroll through sections on touch devices.
23102        */
23103        function addTouchHandler(){
23104            if(isTouchDevice || isTouch){
23105                if(options.autoScrolling){
23106                    $body.off(events.touchmove).on(events.touchmove, preventBouncing);
23107                }
23108
23109                $(WRAPPER_SEL)
23110                    .off(events.touchstart).on(events.touchstart, touchStartHandler)
23111                    .off(events.touchmove).on(events.touchmove, touchMoveHandler);
23112            }
23113        }
23114
23115        /**
23116        * Removes the auto scrolling for touch devices.
23117        */
23118        function removeTouchHandler(){
23119            if(isTouchDevice || isTouch){
23120                $(WRAPPER_SEL)
23121                    .off(events.touchstart)
23122                    .off(events.touchmove);
23123            }
23124        }
23125
23126        /*
23127        * Returns and object with Microsoft pointers (for IE<11 and for IE >= 11)
23128        * http://msdn.microsoft.com/en-us/library/ie/dn304886(v=vs.85).aspx
23129        */
23130        function getMSPointer(){
23131            var pointer;
23132
23133            //IE >= 11 & rest of browsers
23134            if(window.PointerEvent){
23135                pointer = { down: 'pointerdown', move: 'pointermove'};
23136            }
23137
23138            //IE < 11
23139            else{
23140                pointer = { down: 'MSPointerDown', move: 'MSPointerMove'};
23141            }
23142
23143            return pointer;
23144        }
23145
23146        /**
23147        * Gets the pageX and pageY properties depending on the browser.
23148        * https://github.com/alvarotrigo/fullPage.js/issues/194#issuecomment-34069854
23149        */
23150        function getEventsPage(e){
23151            var events = [];
23152
23153            events.y = (typeof e.pageY !== 'undefined' && (e.pageY || e.pageX) ? e.pageY : e.touches[0].pageY);
23154            events.x = (typeof e.pageX !== 'undefined' && (e.pageY || e.pageX) ? e.pageX : e.touches[0].pageX);
23155
23156            //in touch devices with scrollBar:true, e.pageY is detected, but we have to deal with touch events. #1008
23157            if(isTouch && isReallyTouch(e) && options.scrollBar){
23158                events.y = e.touches[0].pageY;
23159                events.x = e.touches[0].pageX;
23160            }
23161
23162            return events;
23163        }
23164
23165        /**
23166        * Slides silently (with no animation) the active slider to the given slide.
23167        * @param noCallback {bool} true or defined -> no callbacks
23168        */
23169        function silentLandscapeScroll(activeSlide, noCallbacks){
23170            setScrollingSpeed (0, 'internal');
23171
23172            if(typeof noCallbacks !== 'undefined'){
23173                //preventing firing callbacks afterSlideLoad etc.
23174                isResizing = true;
23175            }
23176
23177            landscapeScroll(activeSlide.closest(SLIDES_WRAPPER_SEL), activeSlide);
23178
23179            if(typeof noCallbacks !== 'undefined'){
23180                isResizing = false;
23181            }
23182
23183            setScrollingSpeed(originals.scrollingSpeed, 'internal');
23184        }
23185
23186        /**
23187        * Scrolls silently (with no animation) the page to the given Y position.
23188        */
23189        function silentScroll(top){
23190            // The first section can have a negative value in iOS 10. Not quite sure why: -0.0142822265625
23191            // that's why we round it to 0.
23192            var roundedTop = Math.round(top);
23193
23194            if (options.css3 && options.autoScrolling && !options.scrollBar){
23195                var translate3d = 'translate3d(0px, -' + roundedTop + 'px, 0px)';
23196                transformContainer(translate3d, false);
23197            }
23198            else if(options.autoScrolling && !options.scrollBar){
23199                container.css('top', -roundedTop);
23200            }
23201            else{
23202                $htmlBody.scrollTop(roundedTop);
23203            }
23204        }
23205
23206        /**
23207        * Returns the cross-browser transform string.
23208        */
23209        function getTransforms(translate3d){
23210            return {
23211                '-webkit-transform': translate3d,
23212                '-moz-transform': translate3d,
23213                '-ms-transform':translate3d,
23214                'transform': translate3d
23215            };
23216        }
23217
23218        /**
23219        * Allowing or disallowing the mouse/swipe scroll in a given direction. (not for keyboard)
23220        * @type  m (mouse) or k (keyboard)
23221        */
23222        function setIsScrollAllowed(value, direction, type){
23223            switch (direction){
23224                case 'up': isScrollAllowed[type].up = value; break;
23225                case 'down': isScrollAllowed[type].down = value; break;
23226                case 'left': isScrollAllowed[type].left = value; break;
23227                case 'right': isScrollAllowed[type].right = value; break;
23228                case 'all':
23229                    if(type == 'm'){
23230                        setAllowScrolling(value);
23231                    }else{
23232                        setKeyboardScrolling(value);
23233                    }
23234            }
23235        }
23236
23237        /*
23238        * Destroys fullpage.js plugin events and optinally its html markup and styles
23239        */
23240        function destroy(all){
23241            setAutoScrolling(false, 'internal');
23242            setAllowScrolling(false);
23243            setKeyboardScrolling(false);
23244            container.addClass(DESTROYED);
23245
23246            clearTimeout(afterSlideLoadsId);
23247            clearTimeout(afterSectionLoadsId);
23248            clearTimeout(resizeId);
23249            clearTimeout(scrollId);
23250            clearTimeout(scrollId2);
23251
23252            $window
23253                .off('scroll', scrollHandler)
23254                .off('hashchange', hashChangeHandler)
23255                .off('resize', resizeHandler);
23256
23257            $document
23258                .off('click touchstart', SECTION_NAV_SEL + ' a')
23259                .off('mouseenter', SECTION_NAV_SEL + ' li')
23260                .off('mouseleave', SECTION_NAV_SEL + ' li')
23261                .off('click touchstart', SLIDES_NAV_LINK_SEL)
23262                .off('mouseover', options.normalScrollElements)
23263                .off('mouseout', options.normalScrollElements);
23264
23265            $(SECTION_SEL)
23266                .off('click touchstart', SLIDES_ARROW_SEL);
23267
23268            clearTimeout(afterSlideLoadsId);
23269            clearTimeout(afterSectionLoadsId);
23270
23271            //lets make a mess!
23272            if(all){
23273                destroyStructure();
23274            }
23275        }
23276
23277        /*
23278        * Removes inline styles added by fullpage.js
23279        */
23280        function destroyStructure(){
23281            //reseting the `top` or `translate` properties to 0
23282            silentScroll(0);
23283
23284            //loading all the lazy load content
23285            container.find('img[data-src], source[data-src], audio[data-src], iframe[data-src]').each(function(){
23286                setSrc($(this), 'src');
23287            });
23288
23289            container.find('img[data-srcset]').each(function(){
23290                setSrc($(this), 'srcset');
23291            });
23292
23293            $(SECTION_NAV_SEL + ', ' + SLIDES_NAV_SEL +  ', ' + SLIDES_ARROW_SEL).remove();
23294
23295            //removing inline styles
23296            $(SECTION_SEL).css( {
23297                'height': '',
23298                'background-color' : '',
23299                'padding': ''
23300            });
23301
23302            $(SLIDE_SEL).css( {
23303                'width': ''
23304            });
23305
23306            container.css({
23307                'height': '',
23308                'position': '',
23309                '-ms-touch-action': '',
23310                'touch-action': ''
23311            });
23312
23313            $htmlBody.css({
23314                'overflow': '',
23315                'height': ''
23316            });
23317
23318            // remove .fp-enabled class
23319            $('html').removeClass(ENABLED);
23320
23321            // remove .fp-responsive class
23322            $body.removeClass(RESPONSIVE);
23323
23324            // remove all of the .fp-viewing- classes
23325            $.each($body.get(0).className.split(/\s+/), function (index, className) {
23326                if (className.indexOf(VIEWING_PREFIX) === 0) {
23327                    $body.removeClass(className);
23328                }
23329            });
23330
23331            //removing added classes
23332            $(SECTION_SEL + ', ' + SLIDE_SEL).each(function(){
23333                options.scrollOverflowHandler.remove($(this));
23334                $(this).removeClass(TABLE + ' ' + ACTIVE);
23335            });
23336
23337            removeAnimation(container);
23338
23339            //Unwrapping content
23340            container.find(TABLE_CELL_SEL + ', ' + SLIDES_CONTAINER_SEL + ', ' + SLIDES_WRAPPER_SEL).each(function(){
23341                //unwrap not being use in case there's no child element inside and its just text
23342                $(this).replaceWith(this.childNodes);
23343            });
23344
23345            //removing the applied transition from the fullpage wrapper
23346            container.css({
23347                '-webkit-transition': 'none',
23348                'transition': 'none'
23349            });
23350
23351            //scrolling the page to the top with no animation
23352            $htmlBody.scrollTop(0);
23353
23354            //removing selectors
23355            var usedSelectors = [SECTION, SLIDE, SLIDES_CONTAINER];
23356            $.each(usedSelectors, function(index, value){
23357                $('.' + value).removeClass(value);
23358            });
23359        }
23360
23361        /*
23362        * Sets the state for a variable with multiple states (original, and temporal)
23363        * Some variables such as `autoScrolling` or `recordHistory` might change automatically its state when using `responsive` or `autoScrolling:false`.
23364        * This function is used to keep track of both states, the original and the temporal one.
23365        * If type is not 'internal', then we assume the user is globally changing the variable.
23366        */
23367        function setVariableState(variable, value, type){
23368            options[variable] = value;
23369            if(type !== 'internal'){
23370                originals[variable] = value;
23371            }
23372        }
23373
23374        /**
23375        * Displays warnings
23376        */
23377        function displayWarnings(){
23378            var extensions = ['fadingEffect', 'continuousHorizontal', 'scrollHorizontally', 'interlockedSlides', 'resetSliders', 'responsiveSlides', 'offsetSections', 'dragAndMove', 'scrollOverflowReset', 'parallax'];
23379            //UNCODE.addition
23380            // if($('html').hasClass(ENABLED)){
23381            //     showError('error', 'Fullpage.js can only be initialized once and you are doing it multiple times!');
23382            //     return;
23383            // }
23384
23385            // Disable mutually exclusive settings
23386            //UNCODE.addition
23387            // if (options.continuousVertical &&
23388            //     (options.loopTop || options.loopBottom)) {
23389            //     options.continuousVertical = false;
23390            //     showError('warn', 'Option `loopTop/loopBottom` is mutually exclusive with `continuousVertical`; `continuousVertical` disabled');
23391            // }
23392
23393            // if(options.scrollBar && options.scrollOverflow){
23394            //     showError('warn', 'Option `scrollBar` is mutually exclusive with `scrollOverflow`. Sections with scrollOverflow might not work well in Firefox');
23395            // }
23396
23397            // if(options.continuousVertical && (options.scrollBar || !options.autoScrolling)){
23398            //     options.continuousVertical = false;
23399            //     showError('warn', 'Scroll bars (`scrollBar:true` or `autoScrolling:false`) are mutually exclusive with `continuousVertical`; `continuousVertical` disabled');
23400            // }
23401
23402            //using extensions? Wrong file!
23403            $.each(extensions, function(index, extension){
23404                //is the option set to true?
23405                if(options[extension]){
23406                    showError('warn', 'fullpage.js extensions require jquery.fullpage.extensions.min.js file instead of the usual jquery.fullpage.js. Requested: '+ extension);
23407                }
23408            });
23409
23410            //anchors can not have the same value as any element ID or NAME
23411            $.each(options.anchors, function(index, name){
23412
23413                //case insensitive selectors (http://stackoverflow.com/a/19465187/1081396)
23414                var nameAttr = $document.find('[name]').filter(function() {
23415                    return $(this).attr('name') && $(this).attr('name').toLowerCase() == name.toLowerCase();
23416                });
23417
23418                var idAttr = $document.find('[id]').filter(function() {
23419                    return $(this).attr('id') && $(this).attr('id').toLowerCase() == name.toLowerCase();
23420                });
23421
23422                if(idAttr.length || nameAttr.length ){
23423                    showError('error', 'data-anchor tags can not have the same value as any `id` element on the site (or `name` element for IE).');
23424                    idAttr.length && showError('error', '"' + name + '" is is being used by another element `id` property');
23425                    nameAttr.length && showError('error', '"' + name + '" is is being used by another element `name` property');
23426                }
23427            });
23428        }
23429
23430        /**
23431        * Shows a message in the console of the given type.
23432        */
23433        function showError(type, text){
23434            console && console[type] && console[type]('fullPage: ' + text);
23435        }
23436
23437    }; //end of $.fn.fullpage
23438
23439    if(typeof IScroll !== 'undefined'){
23440        /*
23441        * Turns iScroll `mousewheel` option off dynamically
23442        * https://github.com/cubiq/iscroll/issues/1036
23443        */
23444        IScroll.prototype.wheelOn = function () {
23445            this.wrapper.addEventListener('wheel', this);
23446            this.wrapper.addEventListener('mousewheel', this);
23447            this.wrapper.addEventListener('DOMMouseScroll', this);
23448        };
23449
23450        /*
23451        * Turns iScroll `mousewheel` option on dynamically
23452        * https://github.com/cubiq/iscroll/issues/1036
23453        */
23454        IScroll.prototype.wheelOff = function () {
23455            this.wrapper.removeEventListener('wheel', this);
23456            this.wrapper.removeEventListener('mousewheel', this);
23457            this.wrapper.removeEventListener('DOMMouseScroll', this);
23458        };
23459    }
23460
23461    /**
23462     * An object to handle overflow scrolling.
23463     * This uses jquery.slimScroll to accomplish overflow scrolling.
23464     * It is possible to pass in an alternate scrollOverflowHandler
23465     * to the fullpage.js option that implements the same functions
23466     * as this handler.
23467     *
23468     * @type {Object}
23469     */
23470    var iscrollHandler = {
23471        refreshId: null,
23472        iScrollInstances: [],
23473
23474        // Enables or disables the mouse wheel for the active section or all slides in it
23475        toggleWheel: function(value){
23476            var scrollable = $(SECTION_ACTIVE_SEL).find(SCROLLABLE_SEL);
23477            scrollable.each(function(){
23478                var iScrollInstance = $(this).data('iscrollInstance');
23479                if(typeof iScrollInstance !== 'undefined' && iScrollInstance){
23480                    if(value){
23481                        iScrollInstance.wheelOn();
23482                    }
23483                    else{
23484                        iScrollInstance.wheelOff();
23485                    }
23486                }
23487            });
23488        },
23489
23490        /**
23491        * Turns off iScroll for the destination section.
23492        * When scrolling very fast on some trackpads (and Apple laptops) the inertial scrolling would
23493        * scroll the destination section/slide before the sections animations ends.
23494        */
23495        onLeave: function(){
23496            iscrollHandler.toggleWheel(false);
23497        },
23498
23499        // Turns off iScroll for the leaving section
23500        beforeLeave: function(){
23501            iscrollHandler.onLeave()
23502        },
23503
23504        // Turns on iScroll on section load
23505        afterLoad: function(){
23506            iscrollHandler.toggleWheel(true);
23507        },
23508
23509        /**
23510         * Called when overflow scrolling is needed for a section.
23511         *
23512         * @param  {Object} element      jQuery object containing current section
23513         * @param  {Number} scrollHeight Current window height in pixels
23514         */
23515        create: function(element, scrollHeight) {
23516            var scrollable = element.find(SCROLLABLE_SEL);
23517
23518            scrollable.height(scrollHeight);
23519            scrollable.each(function() {
23520                var $this = $(this);
23521                var iScrollInstance = $this.data('iscrollInstance');
23522                if (iScrollInstance) {
23523                    $.each(iscrollHandler.iScrollInstances, function(){
23524                        $(this).destroy();
23525                    });
23526                }
23527
23528                iScrollInstance = new IScroll($this.get(0), iscrollOptions);
23529                iscrollHandler.iScrollInstances.push(iScrollInstance);
23530
23531                //off by default until the section gets active
23532                iScrollInstance.wheelOff();
23533
23534                $this.data('iscrollInstance', iScrollInstance);
23535            });
23536        },
23537
23538        /**
23539         * Return a boolean depending on whether the scrollable element is a
23540         * the end or at the start of the scrolling depending on the given type.
23541         *
23542         * @param  {String}  type       Either 'top' or 'bottom'
23543         * @param  {Object}  scrollable jQuery object for the scrollable element
23544         * @return {Boolean}
23545         */
23546        isScrolled: function(type, scrollable) {
23547            var scroller = scrollable.data('iscrollInstance');
23548
23549            //no scroller?
23550            if (!scroller) {
23551                return true;
23552            }
23553
23554            if (type === 'top') {
23555                return scroller.y >= 0 && !scrollable.scrollTop();
23556            } else if (type === 'bottom') {
23557                return (0 - scroller.y) + scrollable.scrollTop() + 1 + scrollable.innerHeight() >= scrollable[0].scrollHeight;
23558            }
23559        },
23560
23561        /**
23562         * Returns the scrollable element for the given section.
23563         * If there are landscape slides, will only return a scrollable element
23564         * if it is in the active slide.
23565         *
23566         * @param  {Object}  activeSection jQuery object containing current section
23567         * @return {Boolean}
23568         */
23569        scrollable: function(activeSection){
23570            // if there are landscape slides, we check if the scrolling bar is in the current one or not
23571            if (activeSection.find(SLIDES_WRAPPER_SEL).length) {
23572                return activeSection.find(SLIDE_ACTIVE_SEL).find(SCROLLABLE_SEL);
23573            }
23574            return activeSection.find(SCROLLABLE_SEL);
23575        },
23576
23577        /**
23578         * Returns the scroll height of the wrapped content.
23579         * If this is larger than the window height minus section padding,
23580         * overflow scrolling is needed.
23581         *
23582         * @param  {Object} element jQuery object containing current section
23583         * @return {Number}
23584         */
23585        scrollHeight: function(element) {
23586            return element.find(SCROLLABLE_SEL).children().first().get(0).scrollHeight;
23587        },
23588
23589        /**
23590         * Called when overflow scrolling is no longer needed for a section.
23591         *
23592         * @param  {Object} element      jQuery object containing current section
23593         */
23594        remove: function(element) {
23595            var scrollable = element.find(SCROLLABLE_SEL);
23596            if (scrollable.length) {
23597                var iScrollInstance = scrollable.data('iscrollInstance');
23598                iScrollInstance.destroy();
23599
23600                scrollable.data('iscrollInstance', null);
23601            }
23602            element.find(SCROLLABLE_SEL).children().first().children().first().unwrap().unwrap();
23603        },
23604
23605        /**
23606         * Called when overflow scrolling has already been setup but the
23607         * window height has potentially changed.
23608         *
23609         * @param  {Object}
23609 element      jQuery object containing current section
23610         * @param  {Number} scrollHeight Current window height in pixels
23611         */
23612        update: function(element, scrollHeight) {
23613            //using a timeout in order to execute the refresh function only once when `update` is called multiple times in a
23614            //short period of time.
23615            //it also comes on handy because iScroll requires the use of timeout when using `refresh`.
23616            clearTimeout(iscrollHandler.refreshId);
23617            iscrollHandler.refreshId = setTimeout(function(){
23618                $.each(iscrollHandler.iScrollInstances, function(){
23619                    $(this).get(0).refresh();
23620                });
23621            }, 150);
23622
23623            //updating the wrappers height
23624            element.find(SCROLLABLE_SEL).css('height', scrollHeight + 'px').parent().css('height', scrollHeight + 'px');
23625        },
23626
23627        /**
23628         * Called to get any additional elements needed to wrap the section
23629         * content in order to facilitate overflow scrolling.
23630         *
23631         * @return {String|Object} Can be a string containing HTML,
23632         *                         a DOM element, or jQuery object.
23633         */
23634        wrapContent: function() {
23635            return '<div class="' + SCROLLABLE + '"><div class="fp-scroller"></div></div>';
23636        }
23637    };
23638});
23639
23640/* ========================================================================
23641 * Bootstrap: collapse.js v3.1.1
23642 * http://getbootstrap.com/javascript/#collapse
23643 * ========================================================================
23644 * Copyright 2011-2014 Twitter, Inc.
23645 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
23646 * ======================================================================== */
23647
23648
23649+function ($) {
23650  'use strict';
23651
23652  // COLLAPSE PUBLIC CLASS DEFINITION
23653  // ================================
23654
23655  var Collapse = function (element, options) {
23656    this.$element      = $(element)
23657    this.options       = $.extend({}, Collapse.DEFAULTS, options)
23658    this.transitioning = null
23659
23660    if (this.options.parent) this.$parent = $(this.options.parent)
23661    if (this.options.toggle) this.toggle()
23662  }
23663
23664  Collapse.DEFAULTS = {
23665    toggle: true
23666  }
23667
23668  Collapse.prototype.dimension = function () {
23669    var hasWidth = this.$element.hasClass('width')
23670    return hasWidth ? 'width' : 'height'
23671  }
23672
23673  Collapse.prototype.show = function () {
23674    if (this.transitioning || this.$element.hasClass('in')) return
23675
23676    var startEvent = $.Event('show.bs.collapse')
23677    this.$element.trigger(startEvent)
23678    if (startEvent.isDefaultPrevented()) return
23679
23680    var actives = this.$parent && this.$parent.find('> .panel > .in')
23681
23682    if (actives && actives.length) {
23683      var hasData = actives.data('bs.collapse')
23684      if (hasData && hasData.transitioning) return
23685      actives.collapse('hide')
23686      hasData || actives.data('bs.collapse', null)
23687    }
23688
23689    var dimension = this.dimension()
23690
23691    this.$element
23692      .removeClass('collapse')
23693      .addClass('collapsing')[dimension](0)
23694
23695    this.transitioning = 1
23696
23697    var complete = function (e) {
23698      if (e && e.target != this.$element[0]) return
23699      this.$element
23700        .removeClass('collapsing')
23701        .addClass('collapse in')[dimension]('auto')
23702      this.transitioning = 0
23703      this.$element.trigger('shown.bs.collapse')
23704    }
23705
23706    if (!$.support.transition) return complete.call(this)
23707
23708    var scrollSize = $.camelCase(['scroll', dimension].join('-'))
23709
23710    this.$element
23711      .one($.support.transition.end, $.proxy(complete, this))
23712      .emulateTransitionEnd(350)[dimension](this.$element[0][scrollSize])
23713  }
23714
23715  Collapse.prototype.hide = function () {
23716    if (this.transitioning || !this.$element.hasClass('in')) return
23717
23718    var startEvent = $.Event('hide.bs.collapse')
23719    this.$element.trigger(startEvent)
23720    if (startEvent.isDefaultPrevented()) return
23721
23722    var dimension = this.dimension()
23723
23724    this.$element[dimension](this.$element[dimension]())[0].offsetHeight
23725
23726    this.$element
23727      .addClass('collapsing')
23728      .removeClass('collapse')
23729      .removeClass('in')
23730
23731    this.transitioning = 1
23732
23733    var complete = function (e) {
23734      if (e && e.target != this.$element[0]) return
23735      this.transitioning = 0
23736      this.$element
23737        .trigger('hidden.bs.collapse')
23738        .removeClass('collapsing')
23739        .addClass('collapse')
23740    }
23741
23742    if (!$.support.transition) return complete.call(this)
23743
23744    this.$element
23745      [dimension](0)
23746      .one($.support.transition.end, $.proxy(complete, this))
23747      .emulateTransitionEnd(350)
23748  }
23749
23750  Collapse.prototype.toggle = function () {
23751    this[this.$element.hasClass('in') ? 'hide' : 'show']()
23752  }
23753
23754
23755  // COLLAPSE PLUGIN DEFINITION
23756  // ==========================
23757
23758  var old = $.fn.collapse
23759
23760  $.fn.collapse = function (option) {
23761    return this.each(function () {
23762      var $this   = $(this)
23763      var data    = $this.data('bs.collapse')
23764      var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
23765
23766      if (!data && options.toggle && option == 'show') option = !option
23767      if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
23768      if (typeof option == 'string') data[option]()
23769    })
23770  }
23771
23772  $.fn.collapse.Constructor = Collapse
23773
23774
23775  // COLLAPSE NO CONFLICT
23776  // ====================
23777
23778  $.fn.collapse.noConflict = function () {
23779    $.fn.collapse = old
23780    return this
23781  }
23782
23783
23784  // COLLAPSE DATA-API
23785  // =================
23786
23787  $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
23788    var $this   = $(this), href
23789    var target  = $this.attr('data-target')
23790        || e.preventDefault()
23791        || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
23792    //Edited by Uncode to replace ID in panel [START]
23793    var _target = href.replace(/^#/, "");
23794    if ( $('[data-id="' + _target + '"]').length ) {
23795      var $target = $('[data-id="' + _target + '"]')
23796    } else {
23797      var $target = $(target)
23798    }
23799    //Edited by Uncode to replace ID in panel [END]
23800    var data    = $target.data('bs.collapse')
23801    var option  = data ? 'toggle' : $this.data()
23802    var parent  = $this.attr('data-parent')
23803    var $parent = parent && $(parent)
23804
23805    if (!data || !data.transitioning) {
23806      if ($parent) $parent.find('[data-toggle="collapse"][data-parent="' + parent + '"]').not($this).addClass('collapsed')
23807      $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
23808    }
23809
23810    $target.collapse(option)
23811  })
23812
23813}(jQuery);
23814
23815/* ========================================================================
23816 * Bootstrap: tab.js v3.1.1
23817 * http://getbootstrap.com/javascript/#tabs
23818 * ========================================================================
23819 * Copyright 2011-2014 Twitter, Inc.
23820 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
23821 * ======================================================================== */
23822
23823
23824+function ($) {
23825  'use strict';
23826
23827  // TAB CLASS DEFINITION
23828  // ====================
23829
23830  var Tab = function (element) {
23831    this.element = $(element)
23832  }
23833
23834  Tab.prototype.show = function () {
23835    var $this    = this.element
23836    var $ul      = $this.closest('ul:not(.dropdown-menu)')
23837    var selector = $this.data('target')
23838
23839    if (!selector) {
23840      selector = $this.attr('href')
23841      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
23842    }
23843
23844    if ($this.parent('li').hasClass('active')) return
23845
23846    var previous = $ul.find('.active:last a')[0]
23847    var e        = $.Event('show.bs.tab', {
23848      relatedTarget: previous
23849    })
23850
23851    $this.trigger(e)
23852
23853    if (e.isDefaultPrevented()) return
23854
23855    //Edited by Uncode to replace ID in panel [START]
23856    var _target = selector.replace(/^#/, "");
23857    if ( $('[data-id="' + _target + '"]').length )
23858      var $target = $('[data-id="' + _target + '"]')
23859    else
23860      var $target = $(selector)
23861    //Edited by Uncode to replace ID in panel [END]
23862
23863    this.activate($this.parent('li'), $ul)
23864    this.activate($target, $target.parent(), function () {
23865      $this.trigger({
23866        type: 'shown.bs.tab',
23867        relatedTarget: previous
23868      })
23869    })
23870  }
23871
23872  Tab.prototype.activate = function (element, container, callback) {
23873    var $active    = container.find('> .active')
23874    var transition = callback
23875      && $.support.transition
23876      && $active.hasClass('fade')
23877
23878    function next() {
23879      $active
23880        .removeClass('active')
23881        .find('> .dropdown-menu > .active')
23882        .removeClass('active')
23883
23884      element.addClass('active')
23885
23886      if (transition) {
23887        element[0].offsetWidth // reflow for transition
23888        element.addClass('in')
23889      } else {
23890        element.removeClass('fade')
23891      }
23892
23893      if (element.parent('.dropdown-menu')) {
23894        element.closest('li.dropdown').addClass('active')
23895      }
23896
23897      callback && callback()
23898    }
23899
23900    transition ?
23901      $active
23902        .one($.support.transition.end, next)
23903        .emulateTransitionEnd(150) :
23904      next()
23905
23906    $active.removeClass('in')
23907  }
23908
23909
23910  // TAB PLUGIN DEFINITION
23911  // =====================
23912
23913  var old = $.fn.tab
23914
23915  $.fn.tab = function ( option ) {
23916    return this.each(function () {
23917      var $this = $(this)
23918      var data  = $this.data('bs.tab')
23919
23920      if (!data) $this.data('bs.tab', (data = new Tab(this)))
23921      if (typeof option == 'string') data[option]()
23922    })
23923  }
23924
23925  $.fn.tab.Constructor = Tab
23926
23927
23928  // TAB NO CONFLICT
23929  // ===============
23930
23931  $.fn.tab.noConflict = function () {
23932    $.fn.tab = old
23933    return this
23934  }
23935
23936
23937  // TAB DATA-API
23938  // ============
23939
23940  $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
23941    e.preventDefault()
23942    $(this).tab('show')
23943  })
23944
23945}(jQuery);
23946
23947/* ========================================================================
23948 * Bootstrap: tooltip.js v3.1.1
23949 * http://getbootstrap.com/javascript/#tooltip
23950 * Inspired by the original jQuery.tipsy by Jason Frame
23951 * ========================================================================
23952 * Copyright 2011-2014 Twitter, Inc.
23953 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
23954 * ======================================================================== */
23955
23956
23957+function ($) {
23958  'use strict';
23959
23960  // TOOLTIP PUBLIC CLASS DEFINITION
23961  // ===============================
23962
23963  var Tooltip = function (element, options) {
23964    this.type       =
23965    this.options    =
23966    this.enabled    =
23967    this.timeout    =
23968    this.hoverState =
23969    this.$element   = null
23970
23971    this.init('tooltip', element, options)
23972  }
23973
23974  Tooltip.DEFAULTS = {
23975    animation: true,
23976    placement: 'top',
23977    selector: false,
23978    template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
23979    trigger: 'hover focus',
23980    title: '',
23981    delay: 0,
23982    html: false,
23983    container: false,
23984    viewport: {
23985      selector: 'body',
23986      padding: 0
23987    }
23988  }
23989
23990  Tooltip.prototype.init = function (type, element, options) {
23991    this.enabled   = true
23992    this.type      = type
23993    this.$element  = $(element)
23994    this.options   = this.getOptions(options)
23995    this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)
23996
23997    var triggers = this.options.trigger.split(' ')
23998
23999    for (var i = triggers.length; i--;) {
24000      var trigger = triggers[i]
24001
24002      if (trigger == 'click') {
24003        this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
24004      } else if (trigger != 'manual') {
24005        var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
24006        var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
24007
24008        this.$element.on(eventIn  + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
24009        this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
24010      }
24011    }
24012
24013    this.options.selector ?
24014      (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
24015      this.fixTitle()
24016  }
24017
24018  Tooltip.prototype.getDefaults = function () {
24019    return Tooltip.DEFAULTS
24020  }
24021
24022  Tooltip.prototype.getOptions = function (options) {
24023    options = $.extend({}, this.getDefaults(), this.$element.data(), options)
24024
24025    if (options.delay && typeof options.delay == 'number') {
24026      options.delay = {
24027        show: options.delay,
24028        hide: options.delay
24029      }
24030    }
24031
24032    return options
24033  }
24034
24035  Tooltip.prototype.getDelegateOptions = function () {
24036    var options  = {}
24037    var defaults = this.getDefaults()
24038
24039    this._options && $.each(this._options, function (key, value) {
24040      if (defaults[key] != value) options[key] = value
24041    })
24042
24043    return options
24044  }
24045
24046  Tooltip.prototype.enter = function (obj) {
24047    var self = obj instanceof this.constructor ?
24048      obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
24049
24050    clearTimeout(self.timeout)
24051
24052    self.hoverState = 'in'
24053
24054    if (!self.options.delay || !self.options.delay.show) return self.show()
24055
24056    self.timeout = setTimeout(function () {
24057      if (self.hoverState == 'in') self.show()
24058    }, self.options.delay.show)
24059  }
24060
24061  Tooltip.prototype.leave = function (obj) {
24062    var self = obj instanceof this.constructor ?
24063      obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
24064
24065    clearTimeout(self.timeout)
24066
24067    self.hoverState = 'out'
24068
24069    if (!self.options.delay || !self.options.delay.hide) return self.hide()
24070
24071    self.timeout = setTimeout(function () {
24072      if (self.hoverState == 'out') self.hide()
24073    }, self.options.delay.hide)
24074  }
24075
24076  Tooltip.prototype.show = function () {
24077    var e = $.Event('show.bs.' + this.type)
24078
24079    if (this.hasContent() && this.enabled) {
24080      this.$element.trigger(e)
24081
24082      if (e.isDefaultPrevented()) return
24083      var that = this;
24084
24085      var $tip = this.tip()
24086
24087      this.setContent()
24088
24089      if (this.options.animation) $tip.addClass('fade')
24090
24091      var placement = typeof this.options.placement == 'function' ?
24092        this.options.placement.call(this, $tip[0], this.$element[0]) :
24093        this.options.placement
24094
24095      var autoToken = /\s?auto?\s?/i
24096      var autoPlace = autoToken.test(placement)
24097      if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
24098
24099      $tip
24100        .detach()
24101        .css({ top: 0, left: 0, display: 'block' })
24102        .addClass(placement)
24103
24104      this.options.container ? $tip.appendTo($(document).find(this.options.container)) : $tip.insertAfter(this.$element)
24105
24106      var pos          = this.getPosition()
24107      var actualWidth  = $tip[0].offsetWidth
24108      var actualHeight = $tip[0].offsetHeight
24109
24110      if (autoPlace) {
24111        var orgPlacement = placement
24112        var $parent      = this.$element.parent()
24113        var parentDim    = this.getPosition($parent)
24114
24115        placement = placement == 'bottom' && pos.top   + pos.height       + actualHeight - parentDim.scroll > parentDim.height ? 'top'    :
24116                    placement == 'top'    && pos.top   - parentDim.scroll - actualHeight < 0                                   ? 'bottom' :
24117                    placement == 'right'  && pos.right + actualWidth      > parentDim.width                                    ? 'left'   :
24118                    placement == 'left'   && pos.left  - actualWidth      < parentDim.left                                     ? 'right'  :
24119                    placement
24120
24121        $tip
24122          .removeClass(orgPlacement)
24123          .addClass(placement)
24124      }
24125
24126      var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
24127
24128      this.applyPlacement(calculatedOffset, placement)
24129      this.hoverState = null
24130
24131      var complete = function() {
24132        that.$element.trigger('shown.bs.' + that.type)
24133      }
24134
24135      $.support.transition && this.$tip.hasClass('fade') ?
24136        $tip
24137          .one($.support.transition.end, complete)
24138          .emulateTransitionEnd(150) :
24139        complete()
24140    }
24141  }
24142
24143  Tooltip.prototype.applyPlacement = function (offset, placement) {
24144    var $tip   = this.tip()
24145    var width  = $tip[0].offsetWidth
24146    var height = $tip[0].offsetHeight
24147
24148    // manually read margins because getBoundingClientRect includes difference
24149    var marginTop = parseInt($tip.css('margin-top'), 10)
24150    var marginLeft = parseInt($tip.css('margin-left'), 10)
24151
24152    // we must check
24152 for NaN for ie 8/9
24153    if (isNaN(marginTop))  marginTop  = 0
24154    if (isNaN(marginLeft)) marginLeft = 0
24155
24156    offset.top  = offset.top  + marginTop
24157    offset.left = offset.left + marginLeft
24158
24159    // $.fn.offset doesn't round pixel values
24160    // so we use setOffset directly with our own function B-0
24161    $.offset.setOffset($tip[0], $.extend({
24162      using: function (props) {
24163        $tip.css({
24164          top: Math.round(props.top),
24165          left: Math.round(props.left)
24166        })
24167      }
24168    }, offset), 0)
24169
24170    $tip.addClass('in')
24171
24172    // check to see if placing tip in new offset caused the tip to resize itself
24173    var actualWidth  = $tip[0].offsetWidth
24174    var actualHeight = $tip[0].offsetHeight
24175
24176    if (placement == 'top' && actualHeight != height) {
24177      offset.top = offset.top + height - actualHeight
24178    }
24179
24180    var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
24181
24182    if (delta.left) offset.left += delta.left
24183    else offset.top += delta.top
24184
24185    var arrowDelta          = delta.left ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
24186    var arrowPosition       = delta.left ? 'left'        : 'top'
24187    var arrowOffsetPosition = delta.left ? 'offsetWidth' : 'offsetHeight'
24188
24189    $tip.offset(offset)
24190    this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], arrowPosition)
24191  }
24192
24193  Tooltip.prototype.replaceArrow = function (delta, dimension, position) {
24194    this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '')
24195  }
24196
24197  Tooltip.prototype.setContent = function () {
24198    var $tip  = this.tip()
24199    var title = this.getTitle()
24200
24201    $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
24202    $tip.removeClass('fade in top bottom left right')
24203  }
24204
24205  Tooltip.prototype.hide = function () {
24206    var that = this
24207    var $tip = this.tip()
24208    var e    = $.Event('hide.bs.' + this.type)
24209
24210    function complete() {
24211      if (that.hoverState != 'in') $tip.detach()
24212      that.$element.trigger('hidden.bs.' + that.type)
24213    }
24214
24215    this.$element.trigger(e)
24216
24217    if (e.isDefaultPrevented()) return
24218
24219    $tip.removeClass('in')
24220
24221    $.support.transition && this.$tip.hasClass('fade') ?
24222      $tip
24223        .one($.support.transition.end, complete)
24224        .emulateTransitionEnd(150) :
24225      complete()
24226
24227    this.hoverState = null
24228
24229    return this
24230  }
24231
24232  Tooltip.prototype.fixTitle = function () {
24233    var $e = this.$element
24234    if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
24235      $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
24236    }
24237  }
24238
24239  Tooltip.prototype.hasContent = function () {
24240    return this.getTitle()
24241  }
24242
24243  Tooltip.prototype.getPosition = function ($element) {
24244    $element   = $element || this.$element
24245    var el     = $element[0]
24246    var isBody = el.tagName == 'BODY'
24247    return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : null, {
24248      scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop(),
24249      width:  isBody ? $(window).width()  : $element.outerWidth(),
24250      height: isBody ? $(window).height() : $element.outerHeight()
24251    }, isBody ? {top: 0, left: 0} : $element.offset())
24252  }
24253
24254  Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
24255    return placement == 'bottom' ? { top: pos.top + pos.height,   left: pos.left + pos.width / 2 - actualWidth / 2  } :
24256           placement == 'top'    ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2  } :
24257           placement == 'left'   ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
24258        /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width   }
24259
24260  }
24261
24262  Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
24263    var delta = { top: 0, left: 0 }
24264    if (!this.$viewport) return delta
24265
24266    var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
24267    var viewportDimensions = this.getPosition(this.$viewport)
24268
24269    if (/right|left/.test(placement)) {
24270      var topEdgeOffset    = pos.top - viewportPadding - viewportDimensions.scroll
24271      var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
24272      if (topEdgeOffset < viewportDimensions.top) { // top overflow
24273        delta.top = viewportDimensions.top - topEdgeOffset
24274      } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
24275        delta.top = viewportDimensions.top + viewportDimensions.height - bottom
24275EdgeOffset
24276      }
24277    } else {
24278      var leftEdgeOffset  = pos.left - viewportPadding
24279      var rightEdgeOffset = pos.left + viewportPadding + actualWidth
24280      if (leftEdgeOffset < viewportDimensions.left) { // left overflow
24281        delta.left = viewportDimensions.left - leftEdgeOffset
24282      } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
24283        delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
24284      }
24285    }
24286
24287    return delta
24288  }
24289
24290  Tooltip.prototype.getTitle = function () {
24291    var title
24292    var $e = this.$element
24293    var o  = this.options
24294
24295    title = $e.attr('data-original-title')
24296      || (typeof o.title == 'function' ? o.title.call($e[0]) :  o.title)
24297
24298    return title
24299  }
24300
24301  Tooltip.prototype.tip = function () {
24302    return this.$tip = this.$tip || $(this.options.template)
24303  }
24304
24305  Tooltip.prototype.arrow = function () {
24306    return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')
24307  }
24308
24309  Tooltip.prototype.validate = function () {
24310    if (!this.$element[0].parentNode) {
24311      this.hide()
24312      this.$element = null
24313      this.options  = null
24314    }
24315  }
24316
24317  Tooltip.prototype.enable = function () {
24318    this.enabled = true
24319  }
24320
24321  Tooltip.prototype.disable = function () {
24322    this.enabled = false
24323  }
24324
24325  Tooltip.prototype.toggleEnabled = function () {
24326    this.enabled = !this.enabled
24327  }
24328
24329  Tooltip.prototype.toggle = function (e) {
24330    var self = e ? $(e.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) : this
24331    self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
24332  }
24333
24334  Tooltip.prototype.destroy = function () {
24335    clearTimeout(this.timeout)
24336    this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
24337  }
24338
24339
24340  // TOOLTIP PLUGIN DEFINITION
24341  // =========================
24342
24343  var old = $.fn.tooltip
24344
24345  $.fn.tooltip = function (option) {
24346    return this.each(function () {
24347      var $this   = $(this)
24348      var data    = $this.data('bs.tooltip')
24349      var options = typeof option == 'object' && option
24350
24351      if (!data && option == 'destroy') return
24352      if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
24353      if (typeof option == 'string') data[option]()
24354    })
24355  }
24356
24357  $.fn.tooltip.Constructor = Tooltip
24358
24359
24360  // TOOLTIP NO CONFLICT
24361  // ===================
24362
24363  $.fn.tooltip.noConflict = function () {
24364    $.fn.tooltip = old
24365    return this
24366  }
24367
24368}(jQuery);
24369
24370/* ========================================================================
24371 * Bootstrap: transition.js v3.1.1
24372 * http://getbootstrap.com/javascript/#transitions
24373 * ========================================================================
24374 * Copyright 2011-2014 Twitter, Inc.
24375 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
24376 * ======================================================================== */
24377
24378
24379+function ($) {
24380  'use strict';
24381
24382  // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
24383  // ============================================================
24384
24385  function transitionEnd() {
24386    var el = document.createElement('bootstrap')
24387
24388    var transEndEventNames = {
24389      WebkitTransition : 'webkitTransitionEnd',
24390      MozTransition    : 'transitionend',
24391      OTransition      : 'oTransitionEnd otransitionend',
24392      transition       : 'transitionend'
24393    }
24394
24395    for (var name in transEndEventNames) {
24396      if (el.style[name] !== undefined) {
24397        return { end: transEndEventNames[name] }
24398      }
24399    }
24400
24401    return false // explicit for ie8 (  ._.)
24402  }
24403
24404  // http://blog.alexmaccaw.com/css-transitions
24405  $.fn.emulateTransitionEnd = function (duration) {
24406    var called = false, $el = this
24407    $(this).one($.support.transition.end, function () { called = true })
24408    var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
24409    setTimeout(callback, duration)
24410    return this
24411  }
24412
24413  $(function () {
24414    $.support.transition = transitionEnd()
24415  })
24416
24417}(jQuery);

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