PageSourceSearch

https://www.carryingmatters.co.uk/wp-content/themes/uncode/library/js/plugins.js?ver=946054784

js carryingmatters.co.uk collected 2026-09-24 09:54:59 UTC 797,601 bytes, 24,416 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        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)),
8256
8257		// Events
8258		clickEvent = supportTouch ? "itap.iLightBox" : "click.iLightBox",
8259		touchStartEvent = supportTouch ? "touchstart.iLightBox" : "mousedown.iLightBox",
8260		touchStopEvent = supportTouch ? "touchend.iLightBox" : "mouseup.iLightBox",
8261		touchMoveEvent = supportTouch ? "touchmove.iLightBox" : "mousemove.iLightBox",
8262
8263		// Math shorthands
8264		abs = Math.abs,
8265		sqrt = Math.sqrt,
8266		round = Math.round,
8267		max = Math.max,
8268		min = Math.min,
8269		floor = Math.floor,
8270		random = Math.random,
8271
8272		pluginspages = {
8273			quicktime: 'http://www.apple.com/quicktime/download',
8274			flash: 'http://www.adobe.com/go/getflash'
8275		},
8276
8277		iLightBox = function(el, options, items, instant) {
8278			var iL = this;
8279
8280			iL.options = options,
8281				iL.selector = el.selector || el,
8282				iL.context = el.context,
8283				iL.instant = instant;
8284
8285			if (items.length < 1) iL.attachItems();
8286			else iL.items = items;
8287
8288			iL.vars = {
8289				total: iL.items.length,
8290				start: 0,
8291				current: null,
8292				next: null,
8293				prev: null,
8294				BODY: $('body'),
8295				loadRequests: 0,
8296				overlay: $('<div class="ilightbox-overlay"></div>'),
8297				loader: $('<div class="ilightbox-loader"><div></div></div>'),
8298				toolbar: $('<div class="ilightbox-toolbar"></div>'),
8299				innerToolbar: $('<div class="ilightbox-inner-toolbar"></div>'),
8300				title: $('<div class="ilightbox-title"></div>'),
8301				closeButton: $('<a class="ilightbox-close" title="' + iL.options.text.close + '"></a>'),
8302				fullScreenButton: $('<a class="ilightbox-fullscreen" title="' + iL.options.text.enterFullscreen + '"></a>'),
8303				innerPlayButton: $('<a class="ilightbox-play" title="' + iL.options.text.slideShow + '"></a>'),
8304				innerNextButton: $('<a class="ilightbox-next-button" title="' + iL.options.text.next + '"></a>'),
8305				innerPrevButton: $('<a class="ilightbox-prev-button" title="' + iL.options.text.previous + '"></a>'),
8306				holder: $('<div class="ilightbox-holder' + (supportTouch ? ' supportTouch' : '') + '" ondragstart="return false;"><div class="ilightbox-container"></div></div>'),
8307				nextPhoto: $('<div class="ilightbox-holder' + (supportTouch ? ' supportTouch' : '') + ' ilightbox-next" ondragstart="return false;"><div class="ilightbox-container"></div></div>'),
8308				prevPhoto: $('<div class="ilightbox-holder' + (supportTouch ? ' supportTouch' : '') + ' ilightbox-prev" ondragstart="return false;"><div class="ilightbox-container"></div></div>'),
8309				nextButton: $('<a class="ilightbox-button ilightbox-next-button" ondragstart="return false;" title="' + iL.options.text.next + '"><span></span></a>'),
8310				prevButton: $('<a class="ilightbox-button ilightbox-prev-button" ondragstart="return false;" title="' + iL.options.text.previous + '"><span></span></a>'),
8311				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>'),
8312				thumbs: false,
8313				nextLock: false,
8314				prevLock: false,
8315				hashLock: false,
8316				isMobile: false,
8317				mobileMaxWidth: 980,
8318				isInFullScreen: false,
8319				isSwipe: false,
8320				mouseID: 0,
8321				cycleID: 0,
8322				isPaused: 0,
8323				captionHeight: 39,
8324			};
8325
8326			// Hideable elements with mousemove event
8327			iL.vars.hideableElements = iL.vars.nextButton.add(iL.vars.prevButton);
8328
8329			iL.normalizeItems();
8330
8331			//Check necessary plugins
8332			iL.availPlugins();
8333
8334			//Set startFrom
8335			iL.options.startFrom = (iL.options.startFrom > 0 && iL.options.startFrom >= iL.vars.total) ? iL.vars.total - 1 : iL.options.startFrom;
8336
8337			//If randomStart
8338			iL.options.startFrom = (iL.options.randomStart) ? floor(random() * iL.vars.total) : iL.options.startFrom;
8339			iL.vars.start = iL.options.startFrom;
8340
8341			if (instant) iL.instantCall();
8342			else iL.patchItemsEvents();
8343
8344			if (iL.options.linkId) {
8345				iL.hashChangeHandler();
8346				$win.iLightBoxHashChange(function() {
8347					iL.hashChangeHandler();
8348				});
8349			}
8350
8351			if (supportTouch) {
8352				var RegExp = /(click|mouseenter|mouseleave|mouseover|mouseout)/ig,
8353					replace = "itap";
8354				iL.options.caption.show = iL.options.caption.show.replace(RegExp, replace),
8355					iL.options.caption.hide = iL.options.caption.hide.replace(RegExp, replace),
8356					iL.options.social.show = iL.options.social.show.replace(RegExp, replace),
8357					iL.options.social.hide = iL.options.social.hide.replace(RegExp, replace);
8358			}
8359
8360			if (iL.options.controls.arrows || $(window).width() < UNCODE.mediaQuery) {
8361				$.extend(iL.options.styles, {
8362					nextOffsetX: ($(window).width() > UNCODE.mediaQuery) ? 36 : 4,
8363					prevOffsetX: ($(window).width() > UNCODE.mediaQuery) ? 36 : 4,
8364					nextOpacity: 0,
8365					prevOpacity: 0
8366				});
8367			}
8368		};
8369
8370	//iLightBox helpers
8371	iLightBox.prototype = {
8372		showLoader: function() {
8373			var iL = this;
8374			iL.vars.loadRequests += 1;
8375			if (iL.options.path.toLowerCase() == "horizontal") iL.vars.loader.addClass('ilightbox-show').stop().animate({
8376				//top: '-30px'
8377				opacity: '1'
8378			}, iL.options.show.speed, 'easeOutCirc');
8379			else iL.vars.loader.addClass('ilightbox-show').stop().animate({
8380				//left: '-30px'
8381				opacity: '1'
8382			}, iL.options.show.speed, 'easeOutCirc');
8383		},
8384
8385		hideLoader: function() {
8386			var iL = this;
8387			iL.vars.loadRequests -= 1;
8388			iL.vars.loadRequests = (iL.vars.loadRequests < 0) ? 0 : iL.vars.loadRequests;
8389			if (iL.options.path.toLowerCase() == "horizontal") {
8390				if (iL.vars.loadRequests <= 0) iL.vars.loader.removeClass('ilightbox-show').stop().animate({
8391					//top: '-192px'
8392					opacity: '0'
8393				}, iL.options.show.speed, 'easeInCirc');
8394			} else {
8395				if (iL.vars.loadRequests <= 0) iL.vars.loader.removeClass('ilightbox-show').stop().animate({
8396					//left: '-192px'
8397					opacity: '0'
8398				}, iL.options.show.speed, 'easeInCirc');
8399			}
8400		},
8401
8402		createUI: function() {
8403			var iL = this;
8404
8405			iL.ui = {
8406				currentElement: iL.vars.holder,
8407				nextElement: iL.vars.nextPhoto,
8408				prevElement: iL.vars.prevPhoto,
8409				currentItem: iL.vars.current,
8410				nextItem: iL.vars.next,
8411				prevItem: iL.vars.prev,
8412				hide: function() {
8413					iL.closeAction();
8414				},
8415				refresh: function() {
8416					(arguments.length > 0) ? iL.repositionPhoto(true): iL.repositionPhoto();
8417				},
8418				fullscreen: function() {
8419					iL.fullScreenAction();
8420				}
8421			};
8422		},
8423
8424		attachItems: function() {
8425			var iL = this,
8426				itemsObject = new Array(),
8427				items = new Array();
8428
8429			$(iL.selector, iL.context).each(function() {
8430				var t = $(this),
8431					URL = t.attr(iL.options.attr) || null,
8432					options = t.data("options") && eval("({" + t.data("options") + "})") || {},
8433					caption = t.data('caption'),
8434					title = t.data('title'),
8435					type = t.data('type') || getTypeByExtension(URL),
8436					clone = t.data('lbox-clone') || false;
8437
8438				if (t.data('lbox-clone') != undefined) return;
8439
8440				//Uncode addition ##START##
8441				if ( typeof t.attr('data-album') != 'undefined' && t.attr('data-album') != '' ) {
8442					var ALBUM_URLS = JSON.parse(t.attr('data-album')),
8443						URL_i,
8444						URLS_LENGHT = ALBUM_URLS.length;
8445					for (URL_i = 0; URL_i < URLS_LENGHT; URL_i++) {
8446						if ( typeof ALBUM_URLS[URL_i].url !== 'undefined' && ALBUM_URLS[URL_i].url != '' ) {
8447							var item_opts = 'width:' + ALBUM_URLS[URL_i].width + ',height:' + ALBUM_URLS[URL_i].height + ',thumbnail: \'' + ALBUM_URLS[URL_i].thumbnail + '\'';
8448							item_opts = item_opts && eval("({" + item_opts + "})") || {},
8449							items.push({
8450								URL: ALBUM_URLS[URL_i].url,
8451								caption: ALBUM_URLS[URL_i].caption,
8452								title: ALBUM_URLS[URL_i].title,
8453								type: getTypeByExtension(ALBUM_URLS[URL_i].url),
8454								options: item_opts,
8455								clone: clone
8456							});
8457							var newT = t;
8458							newT.attr('href',ALBUM_URLS[URL_i].url);
8459							if (!iL.instant) itemsObject.push(newT);
8460						}
8461					}
8462				} else {
8463				//Uncode addition ##END##
8464					items.push({
8465						URL: URL,
8466						caption: caption,
8467						title: title,
8468						type: type,
8469						options: options,
8470						clone: clone
8471					});
8472				}
8473
8474
8475				if (iL.vars != undefined) iL.vars.total = items.length;
8476
8477				if (!iL.instant) itemsObject.push(t);
8478			});
8479
8480			iL.items = items,
8481				iL.itemsObject = itemsObject;
8482
8483		},
8484
8485		normalizeItems: function() {
8486			var iL = this,
8487				newItems = new Array();
8488
8489			$.each(iL.items, function(key, val) {
8490
8491				if (typeof val == "string") val = {
8492					url: val
8493				};
8494
8495				var URL = val.url || val.URL || null,
8496					options = val.options || {},
8497					caption = val.caption || null,
8498					title = val.title || null,
8499					type = (val.type) ? val.type.toLowerCase() : getTypeByExtension(URL),
8500					ext = (typeof URL != 'object') ? getExtension(URL) : '';
8501
8502				options.thumbnail = options.thumbnail || ((type == "image") ? URL : null),
8503				options.videoType = options.videoType || null,
8504				options.skin = options.skin || iL.options.skin,
8505				options.width = options.width || null,
8506				options.height = options.height || null,
8507				options.mousewheel = (typeof options.mousewheel != 'undefined') ? options.mousewheel : true,
8508				options.swipe = (typeof options.swipe != 'undefined') ? options.swipe : true,
8509				options.social = (typeof options.social != 'undefined') ? options.social : iL.options.social.buttons && $.extend({}, {}, iL.options.social.buttons);
8510
8511				if (type == "video") {
8512					options.html5video = (typeof options.html5video != 'undefined') ? options.html5video : {};
8513
8514					options.html5video.webm = options.html5video.webm || options.html5video.WEBM || null;
8515					options.html5video.controls = (typeof options.html5video.controls != 'undefined') ? options.html5video.controls : "controls";
8516					options.html5video.preload = options.html5video.preload || "metadata";
8517					options.html5video.autoplay = (typeof options.html5video.autoplay != 'undefined') ? options.html5video.autoplay : false;
8518					//UNCODE addition
8519					options.html5video.loop = (typeof options.html5video.loop != 'undefined') ? options.html5video.loop : false;
8520				}
8521
8522				if (!options.width || !options.height) {
8523					if (type == "video") options.width = 1280, options.height = 720;
8524					else if (type == "iframe") options.width = '100%', options.height = '90%';
8525					else if (type == "flash") options.width = 1280, options.height = 720;
8526				}
8527
8528				delete val.url;
8529				val.index = key;
8530				val.URL = URL;
8531				val.caption = caption;
8532				val.title = title;
8533				val.type = type;
8534				val.options = options;
8535				val.ext = ext;
8536
8537				newItems.push(val);
8538			});
8539
8540			iL.items = newItems;
8541		},
8542
8543		instantCall: function() {
8544			var iL = this,
8545				key = iL.vars.start;
8546
8547			iL.vars.current = key;
8548			iL.vars.next = (iL.items[key + 1]) ? key + 1 : null;
8549			iL.vars.prev = (iL.items[key - 1]) ? key - 1 : null;
8550
8551			iL.addContents();
8552			iL.patchEvents();
8553		},
8554
8555		addContents: function() {
8556			var iL = this,
8557				vars = iL.vars,
8558				opts = iL.options,
8559				viewport = getViewport(),
8560				path = opts.path.toLowerCase(),
8561				recognizingItems = vars.total > 0 && iL.items.filter(function(e, i, arr) {
8562					return ['image', 'flash', 'video'].indexOf(e.type) === -1 && typeof e.recognized === 'undefined' && (opts.smartRecognition || e.options.smartRecognition);
8563				}),
8564				needRecognition = recognizingItems.length > 0;
8565
8566			if (opts.mobileOptimizer && !opts.innerToolbar)
8567				vars.isMobile = viewport.width <= vars.mobileMaxWidth;
8568
8569			vars.overlay.addClass(opts.skin).hide().css('opacity', opts.overlay.opacity);
8570
8571			if (opts.linkId)
8572				vars.overlay[0].setAttribute('linkid', opts.linkId);
8573
8574			//Add Toolbar Buttons
8575			if (opts.controls.toolbar) {
8576				vars.toolbar.addClass(opts.skin).append(vars.closeButton);
8577				if (opts.controls.fullscreen)
8578					vars.toolbar.append(vars.fullScreenButton);
8579				if (opts.controls.slideshow)
8580					vars.toolbar.append(vars.innerPlayButton);
8581				if (vars.total > 1)
8582					vars.toolbar.append(vars.innerPrevButton).append(vars.innerNextButton);
8583			}
8584
8585			//Append elements to body
8586			vars.BODY.addClass('ilightbox-noscroll').append(vars.overlay).append(vars.loader).append(vars.holder).append(vars.nextPhoto).append(vars.prevPhoto);
8587
8588			if (!opts.innerToolbar)
8589				vars.BODY.append(vars.toolbar);
8590			if (opts.controls.arrows)
8591				vars.BODY.append(vars.nextButton).append(vars.prevButton);
8592
8593			if (opts.controls.thumbnail && vars.total > 1) {
8594				vars.BODY.append(vars.thumbnails);
8595				vars.thumbnails.addClass(opts.skin).addClass('ilightbox-' + path);
8596				$('div.ilightbox-thumbnails-grid', vars.thumbnails).empty();
8597				vars.thumbs = true;
8598			}
8599
8600			//Configure loader and arrows
8601			var loaderCss = (opts.path.toLowerCase() == "horizontal") ? {
8602				left: parseInt((viewport.width / 2) - (vars.loader.outerWidth() / 2))
8603			} : {
8604				top: parseInt((viewport.height / 2) - (vars.loader.outerHeight() / 2))
8605			};
8606			vars.loader.addClass(opts.skin).css(loaderCss);
8607			vars.nextButton.add(vars.prevButton).addClass(opts.skin);
8608			if (path == "horizontal")
8609				vars.loader.add(vars.nextButton).add(vars.prevButton).addClass('horizontal');
8610
8611			// Configure arrow buttons
8612			vars.BODY[vars.isMobile ? 'addClass' : 'removeClass']('isMobile');
8613
8614			if (!opts.infinite) {
8615				vars.prevButton.add(vars.prevButton).add(vars.innerPrevButton).add(vars.innerNextButton).removeClass('disabled');
8616
8617				if (vars.current == 0)
8618					vars.prevButton.add(vars.innerPrevButton).addClass('disabled');
8619				if (vars.current >= vars.total - 1)
8620					vars.nextButton.add(vars.innerNextButton).addClass('disabled');
8621			}
8622
8623			if (opts.show.effect) {
8624				vars.overlay.stop().fadeIn(opts.show.speed);
8625				vars.toolbar.stop().fadeIn(opts.show.speed);
8626			} else {
8627				vars.overlay.show();
8628				vars.toolbar.show();
8629			}
8630
8631			var length = recognizingItems.length;
8632			if (needRecognition) {
8633				iL.showLoader();
8634
8635				$.each(recognizingItems, function(key, val) {
8636					var resultFnc = function(result) {
8637							var key = -1,
8638								filter = iL.items.filter(function(e, i, arr) {
8639									if (e.URL == result.url)
8640										key = i;
8641
8642									return e.URL == result.url;
8643								}),
8644								self = iL.items[key];
8645
8646							if (result)
8647								$.extend(true, self, {
8648									URL: result.source,
8649									type: result.type,
8650									recognized: true,
8651									options: {
8652										html5video: result.html5video,
8653										width: (result.type == "image") ? 0 : (result.width || self.width),
8654										height: (result.type == "image") ? 0 : (result.height || self.height),
8655										thumbnail: self.options.thumbnail || result.thumbnail
8656									}
8657								});
8658
8659							length--;
8660
8661							if (length == 0) {
8662								iL.hideLoader();
8663
8664								vars.dontGenerateThumbs = false;
8665								iL.generateThumbnails();
8666
8667								if (opts.show.effect)
8668									setTimeout(function() {
8669										iL.generateBoxes();
8670									}, opts.show.speed);
8671								else
8672									iL.generateBoxes();
8673							}
8674						};
8675
8676					iL.ogpRecognition(this, resultFnc);
8677				});
8678			}
8679			else {
8680				if (opts.show.effect)
8681					setTimeout(function() {
8682						iL.generateBoxes();
8683					}, opts.show.speed);
8684				else
8685					iL.generateBoxes();
8686			}
8687
8688			iL.createUI();
8689
8690			window.iLightBox = {
8691				close: function() {
8692					iL.closeAction();
8693				},
8694				fullscreen: function() {
8695					iL.fullScreenAction();
8696				},
8697				moveNext: function() {
8698					iL.moveTo('next');
8699				},
8700				movePrev: function() {
8701					iL.moveTo('prev');
8702				},
8703				goTo: function(index) {
8704					iL.goTo(index);
8705				},
8706				refresh: function() {
8707					iL.refresh();
8708				},
8709				reposition: function() {
8710					(arguments.length > 0) ? iL.repositionPhoto(true): iL.repositionPhoto();
8711				},
8712				setOption: function(options) {
8713					iL.setOption(options);
8714				},
8715				destroy: function() {
8716					iL.closeAction();
8717					iL.dispatchItemsEvents();
8718				}
8719			};
8720
8721			if (opts.linkId) {
8722				vars.hashLock = true;
8723				window.location.hash = opts.linkId + '/' + vars.current;
8724				setTimeout(function() {
8725					vars.hashLock = false;
8726				}, 55);
8727			}
8728
8729			if (!opts.slideshow.startPaused) {
8730				iL.resume();
8731				vars.innerPlayButton.removeClass('ilightbox-play').addClass('ilightbox-pause');
8732			}
8733
8734			//Trigger the onOpen callback
8735			if (typeof iL.options.callback.onOpen == 'function') iL.options.callback.onOpen.call(iL);
8736		},
8737
8738		loadContent: function(obj, opt, speed) {
8739			var iL = this,
8740				holder, item;
8741
8742			iL.createUI();
8743
8744			obj.speed = speed || iL.options.effects.loadedFadeSpeed;
8745
8746			if (opt == 'current') {
8747				if (!obj.options.mousewheel) iL.vars.lockWheel = true;
8748				else iL.vars.lockWheel = false;
8749
8750				if (!obj.options.swipe) iL.vars.lockSwipe = true;
8751				else iL.vars.lockSwipe = false;
8752			}
8753
8754			switch (opt) {
8755				case 'current':
8756					holder = iL.vars.holder, item = iL.vars.current;
8757					break;
8758				case 'next':
8759					holder = iL.vars.nextPhoto, item = iL.vars.next;
8760					break;
8761				case 'prev':
8762					holder = iL.vars.prevPhoto, item = iL.vars.prev;
8763					break;
8764			}
8765
8766			holder.removeAttr('style class').addClass('ilightbox-holder' + (supportTouch ? ' supportTouch' : '')).addClass(obj.options.skin);
8767			$('div.ilightbox-inner-toolbar', holder).remove();
8768
8769			if (obj.title || iL.options.innerToolbar) {
8770				var innerToolbar = iL.vars.innerToolbar.clone();
8771				if (obj.title && iL.options.show.title) {
8772					var title = iL.vars.title.clone();
8773					title.empty().html(obj.title);
8774					innerToolbar.append(title);
8775				}
8776				if (iL.options.innerToolbar) {
8777					innerToolbar.append((iL.vars.total > 1) ? iL.vars.toolbar.clone() : iL.vars.toolbar);
8778				}
8779				holder.prepend(innerToolbar);
8780			}
8781
8782			//console.warn('loadContent', arguments);
8783
8784			iL.loadSwitcher(obj, holder, item, opt);
8785		},
8786
8787		loadSwitcher: function(obj, holder, item, opt) {
8788			var iL = this,
8789				opts = iL.options,
8790				api = {
8791					element: holder,
8792					position: item
8793				};
8794
8795			switch (obj.type) {
8796				case 'image':
8797					//Trigger the onBeforeLoad callback
8798					if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
8799					if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
8800
8801					iL.loadImage(obj.URL, function(img) {
8802						//Trigger the onAfterLoad callback
8803						if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
8804						if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
8805
8806						var width = (img) ? img.width : 400,
8807							height = (img) ? img.height : 200;
8808
8809						holder.data({
8810							naturalWidth: width,
8811							naturalHeight: height
8812						});
8813						$('div.ilightbox-container', holder).empty().append((img) ? '<img src="' + obj.URL + '" class="ilightbox-image" />' : '<span class="ilightbox-alert">' + opts.errors.loadImage + '</span>');
8814
8815						//Trigger the onRender callback
8816						if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
8817						if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
8818
8819						iL.configureHolder(obj, opt, holder);
8820					});
8821
8822					break;
8823
8824				case 'video':
8825					holder.data({
8826						naturalWidth: obj.options.width,
8827						naturalHeight: obj.options.height
8828					});
8829
8830					if (opt === 'current') {
8831						iL.addContent(holder, obj);
8832
8833						//Trigger the onRender callback
8834						if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
8835						if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
8836					} else {
8837						$('div.ilightbox-container', holder).empty();
8838					}
8839
8840					iL.configureHolder(obj, opt, holder);
8841
8842					break;
8843
8844				case 'iframe':
8845					//iL.showLoader();
8846					holder.data({
8847						naturalWidth: obj.options.width,
8848						naturalHeight: obj.options.height
8849					});
8850
8851					iL.configureHolder(obj, opt, holder);
8852
8853					if (opt === 'current') {
8854						var el = iL.addContent(holder, obj);
8855
8856						//Trigger the onRender callback
8857						if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
8858						if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
8859
8860						//Trigger the onBeforeLoad callback
8861						if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
8862						if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
8863
8864						el.bind('load', function() {
8865							//Trigger the onAfterLoad callback
8866							if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
8867							if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
8868
8869							//iL.hideLoader();
8870							el.unbind('load');
8871						});
8872					} else {
8873						$('div.ilightbox-container', holder).empty();
8874					}
8875
8876					break;
8877
8878				case 'inline':
8879					var el = $(obj.URL),
8880						content = iL.addContent(holder, obj),
8881						images = findImageInElement(holder);
8882
8883					holder.data({
8884						naturalWidth: (iL.items[item].options.width || el.outerWidth()),
8885						naturalHeight: (iL.items[item].options.height || el.outerHeight())
8886					});
8887					content.children().eq(0).show();
8888
8889					//Trigger the onRender callback
8890					if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
8891					if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
8892
8893					//Trigger the onBeforeLoad callback
8894					if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
8895					if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
8896
8897					iL.loadImage(images, function() {
8898						//Trigger the onAfterLoad callback
8899						if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
8900						if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
8901
8902						iL.configureHolder(obj, opt, holder);
8903					});
8904
8905					break;
8906
8907				case 'flash':
8908					var el = iL.addContent(holder, obj);
8909
8910					holder.data({
8911						naturalWidth: (iL.items[item].options.width || el.outerWidth()),
8912						naturalHeight: (iL.items[item].options.height || el.outerHeight())
8913					});
8914
8915					//Trigger the onRender callback
8916					if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
8917					if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
8918
8919					iL.configureHolder(obj, opt, holder);
8920
8921					break;
8922
8923				case 'ajax':
8924					var ajax = obj.options.ajax || {};
8925					//Trigger the onBeforeLoad callback
8926					if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
8927					if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
8928
8929					iL.showLoader();
8930					$.ajax({
8931						url: obj.URL || opts.ajaxSetup.url,
8932						data: ajax.data || null,
8933						dataType: ajax.dataType || "html",
8934						type: ajax.type || opts.ajaxSetup.type,
8935						cache: ajax.cache || opts.ajaxSetup.cache,
8936						crossDomain: ajax.crossDomain || opts.ajaxSetup.crossDomain,
8937						global: ajax.global || opts.ajaxSetup.global,
8938						ifModified: ajax.ifModified || opts.ajaxSetup.ifModified,
8939						username: ajax.username || opts.ajaxSetup.username,
8940						password: ajax.password || opts.ajaxSetup.password,
8941						beforeSend: ajax.beforeSend || opts.ajaxSetup.beforeSend,
8942						complete: ajax.complete || opts.ajaxSetup.complete,
8943						success: function(data, textStatus, jqXHR) {
8944							iL.hideLoader();
8945
8946							var el = $(data),
8947								container = $('div.ilightbox-container', holder),
8948								elWidth = iL.items[item].options.width || parseInt(el[0].getAttribute('width')),
8949								elHeight = iL.items[item].options.height || parseInt(el[0].getAttribute('height')),
8950								css = (el[0].getAttribute('width') && el[0].getAttribute('height')) ? {
8951									'overflow': 'hidden'
8952								} : {};
8953
8954							container.empty().append($('<div class="ilightbox-wrapper"></div>').css(css).html(el));
8955							holder.show().data({
8956								naturalWidth: (elWidth || container.outerWidth()),
8957								naturalHeight: (elHeight || container.outerHeight())
8958							}).hide();
8959
8960							//Trigger the onRender callback
8961							if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
8962							if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
8963
8964							var images = findImageInElement(holder);
8965							iL.loadImage(images, function() {
8966								//Trigger the onAfterLoad callback
8967								if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
8968								if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
8969
8970								iL.configureHolder(obj, opt, holder);
8971							});
8972
8973							opts.ajaxSetup.success(data, textStatus, jqXHR);
8974							if (typeof ajax.success == 'function') ajax.success(data, textStatus, jqXHR);
8975						},
8976						error: function(jqXHR, textStatus, errorThrown) {
8977							//Trigger the onAfterLoad callback
8978							if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
8979							if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
8980
8981							iL.hideLoader();
8982							$('div.ilightbox-container', holder).empty().append('<span class="ilightbox-alert">' + opts.errors.loadContents + '</span>');
8983							iL.configureHolder(obj, opt, holder);
8984
8985							opts.ajaxSetup.error(jqXHR, textStatus, errorThrown);
8986							if (typeof ajax.error == 'function') ajax.error(jqXHR, textStatus, errorThrown);
8987						}
8988					});
8989
8990					break;
8991
8992				case 'html':
8993					var object = obj.URL,
8994						el
8995					container = $('div.ilightbox-container', holder);
8996
8997					if (object[0].nodeName) el = object.clone();
8998					else {
8999						var dom = $(object);
9000						if (dom.selector) el = $('<div>' + dom + '</div>');
9001						else el = dom;
9002					}
9003
9004					var elWidth = iL.items[item].options.width || parseInt(el.attr('width')),
9005						elHeight = iL.items[item].options.height || parseInt(el.attr('height'));
9006
9007					iL.addContent(holder, obj);
9008
9009					el.appendTo(document.documentElement).hide();
9010
9011					//Trigger the onRender callback
9012					if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
9013					if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
9014
9015					var images = findImageInElement(holder);
9016
9017					//Trigger the onBeforeLoad callback
9018					if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
9019					if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
9020
9021					iL.loadImage(images, function() {
9022						//Trigger the onAfterLoad callback
9023						if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
9024						if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
9025
9026						holder.show().data({
9027							naturalWidth: (elWidth || container.outerWidth()),
9028							naturalHeight: (elHeight || container.outerHeight())
9029						}).hide();
9030						el.remove();
9031
9032						iL.configureHolder(obj, opt, holder);
9033					});
9034
9035					break;
9036			}
9037		},
9038
9039		configureHolder: function(obj, opt, holder) {
9040			var iL = this,
9041				vars = iL.vars,
9042				opts = iL.options;
9043
9044			if (opt != "current")(opt == "next") ? holder.addClass('ilightbox-next') : holder.addClass('ilightbox-prev');
9045
9046			if (opt == "current")
9047				var item = vars.current;
9048			else if (opt == "next")
9049				var opacity = opts.styles.nextOpacity,
9050					item = vars.next;
9051			else
9052				var opacity = opts.styles.prevOpacity,
9053					item = vars.prev;
9054
9055			var api = {
9056				element: holder,
9057				position: item
9058			};
9059
9060			iL.items[item].options.width = iL.items[item].options.width || 0,
9061			iL.items[item].options.height = iL.items[item].options.height || 0;
9062
9063			if (opt == "current") {
9064				if (opts.show.effect) holder.css(transform, gpuAcceleration).fadeIn(obj.speed, function() {
9065					holder.css(transform, '');
9066					if (obj.caption) {
9067						iL.setCaption(obj, holder);
9068						var caption = $('div.ilightbox-caption', holder),
9069							percent = parseInt((caption.outerHeight() / holder.outerHeight()) * 100);
9070						if (opts.caption.start & percent <= 50) caption.fadeIn(opts.effects.fade
9070Speed);
9071					}
9072
9073					var social = obj.options.social;
9074					if (social) {
9075						iL.setSocial(social, obj.URL, holder);
9076						if (opts.social.start) $('div.ilightbox-social', holder).fadeIn(opts.effects.fadeSpeed);
9077					}
9078
9079					//Generate thumbnails
9080					iL.generateThumbnails();
9081
9082					//Trigger the onShow callback
9083					if (typeof opts.callback.onShow == 'function') opts.callback.onShow.call(iL, iL.ui, item);
9084					if (typeof obj.options.onShow == 'function') obj.options.onShow.call(iL, api);
9085				});
9086				else {
9087					holder.show();
9088
9089					//Generate thumbnails
9090					iL.generateThumbnails();
9091
9092					//Trigger the onShow callback
9093					if (typeof opts.callback.onShow == 'function') opts.callback.onShow.call(iL, iL.ui, item);
9094					if (typeof obj.options.onShow == 'function') obj.options.onShow.call(iL, api);
9095				}
9096			} else {
9097				if (opts.show.effect) holder.fadeTo(obj.speed, opacity, function() {
9098					if (opt == "next") vars.nextLock = false;
9099					else vars.prevLock = false;
9100
9101					//Generate thumbnails
9102					iL.generateThumbnails();
9103
9104					//Trigger the onShow callback
9105					if (typeof opts.callback.onShow == 'function') opts.callback.onShow.call(iL, iL.ui, item);
9106					if (typeof obj.options.onShow == 'function') obj.options.onShow.call(iL, api);
9107				});
9108				else {
9109					holder.css({
9110						opacity: opacity
9111					}).show();
9112					if (opt == "next") vars.nextLock = false;
9113					else vars.prevLock = false;
9114
9115					//Generate thumbnails
9116					iL.generateThumbnails();
9117
9118					//Trigger the onShow callback
9119					if (typeof opts.callback.onShow == 'function') opts.callback.onShow.call(iL, iL.ui, item);
9120					if (typeof obj.options.onShow == 'function') obj.options.onShow.call(iL, api);
9121				}
9122			}
9123
9124			setTimeout(function() {
9125				iL.repositionPhoto();
9126			}, 0);
9127		},
9128
9129		generateBoxes: function() {
9130			var iL = this,
9131				vars = iL.vars,
9132				opts = iL.options;
9133
9134			if (opts.infinite && vars.total >= 3) {
9135				if (vars.current == vars.total - 1) vars.next = 0;
9136				if (vars.current == 0) vars.prev = vars.total - 1;
9137			} else opts.infinite = false;
9138
9139			//UNCODE addition
9140			if ( typeof iL.items[vars.current] == 'undefined' )
9141				vars.current = 0;
9142
9143			iL.loadContent(iL.items[vars.current], 'current', opts.show.speed);
9144
9145			if (iL.items[vars.next]) iL.loadContent(iL.items[vars.next], 'next', opts.show.speed);
9146			if (iL.items[vars.prev]) iL.loadContent(iL.items[vars.prev], 'prev', opts.show.speed);
9147		},
9148
9149		generateThumbnails: function() {
9150			var iL = this,
9151				vars = iL.vars,
9152				opts = iL.options,
9153				timeOut = null;
9154
9155			if (vars.thumbs && !iL.vars.dontGenerateThumbs) {
9156				var thumbnails = vars.thumbnails,
9157					container = $('div.ilightbox-thumbnails-container', thumbnails),
9158					grid = $('div.ilightbox-thumbnails-grid', container),
9159					i = 0;
9160
9161				grid.removeAttr('style').empty();
9162
9163				$.each(iL.items, function(key, val) {
9164					var isActive = (vars.current == key) ? 'ilightbox-active' : '',
9165						opacity = (vars.current == key) ? opts.thumbnails.activeOpacity : opts.thumbnails.normalOpacity,
9166						thumb = val.options.thumbnail,
9167						thumbnail = $('<div class="ilightbox-thumbnail"></div>'),
9168						icon = $('<div class="ilightbox-thumbnail-icon"></div>');
9169
9170					thumbnail.css({
9171						opacity: 0
9172					}).addClass(isActive);
9173
9174					if ((val.type == "video" || val.type == "flash") && typeof val.options.icon == 'undefined') {
9175						icon.addClass('ilightbox-thumbnail-video');
9176						thumbnail.append(icon);
9177					} else if (val.options.icon) {
9178						icon.addClass('ilightbox-thumbnail-' + val.options.icon);
9179						thumbnail.append(icon);
9180					}
9181
9182					if (thumb) iL.loadImage(thumb, function(img) {
9183						i++;
9184						if (img) thumbnail.data({
9185							naturalWidth: img.width,
9186							naturalHeight: img.height
9187						}).append('<img src="' + thumb + '" border="0" />');
9188						else thumbnail.data({
9189							naturalWidth: opts.thumbnails.maxWidth,
9190							naturalHeight: opts.thumbnails.maxHeight
9191						});
9192
9193						clearTimeout(timeOut);
9194						timeOut = setTimeout(function() {
9195							iL.positionThumbnails(thumbnails, container, grid);
9196						}, 20);
9197						setTimeout(function() {
9198							thumbnail.fadeTo(opts.effects.loadedFadeSpeed, opacity);
9199						}, i * 20);
9200					});
9201
9202					grid.append(thumbnail);
9203				});
9204				iL.vars.dontGenerateThumbs = true;
9205			}
9206		},
9207
9208		positionThumbnails: function(thumbnails, container, grid) {
9209			var iL = this,
9210				vars = iL.vars,
9211				opts = iL.options,
9212				viewport = getViewport(),
9213				path = opts.path.toLowerCase();
9214
9215			if (!thumbnails) thumbnails = vars.thumbnails;
9216			if (!container) container = $('div.ilightbox-thumbnails-container', thumbnails);
9217			if (!grid) grid = $('div.ilightbox-thumbnails-grid', container);
9218
9219			var thumbs = $('.ilightbox-thumbnail', grid),
9220				widthAvail = (path == 'horizontal') ? viewport.width - opts.styles.pageOffsetX : thumbs.eq(0).outerWidth() - opts.styles.pageOffsetX,
9221				heightAvail = (path == 'horizontal') ? thumbs.eq(0).outerHeight() - opts.styles.pageOffsetY : viewport.height - opts.styles.pageOffsetY,
9222				gridWidth = (path == 'horizontal') ? 0 : widthAvail,
9223				gridHeight = (path == 'horizontal') ? heightAvail : 0,
9224				active = $('.ilightbox-active', grid),
9225				gridCss = {},
9226				css = {};
9227
9228			if (arguments.length < 3) {
9229				thumbs.css({
9230					opacity: opts.thumbnails.normalOpacity
9231				});
9232				active.css({
9233					opacity: opts.thumbnails.activeOpacity
9234				});
9235			}
9236
9237			thumbs.each(function(i) {
9238				var t = $(this),
9239					data = t.data(),
9240					width = (path == 'horizontal') ? 0 : opts.thumbnails.maxWidth;
9241				height = (path == 'horizontal') ? opts.thumbnails.maxHeight : 0;
9242				dims = iL.getNewDimenstions(width, height, data.naturalWidth, data.naturalHeight, true);
9243
9244				t.css({
9245					width: dims.width,
9246					height: dims.height
9247				});
9248				if (path == 'horizontal') t.css({
9249					'float': 'left'
9250				});
9251
9252				(path == 'horizontal') ? (
9253					gridWidth += t.outerWidth()
9254				) : (
9255					gridHeight += t.outerHeight()
9256				);
9257			});
9258
9259			gridCss = {
9260				width: gridWidth,
9261				height: gridHeight
9262			};
9263
9264			grid.css(gridCss);
9265
9266			gridCss = {};
9267
9268			var gridOffset = grid.offset(),
9269				activeOffset = (active.length) ? active.offset() : {
9270					top: parseInt(heightAvail / 2),
9271					left: parseInt(widthAvail / 2)
9272				};
9273
9274			gridOffset.top = (gridOffset.top - $doc.scrollTop()),
9275				gridOffset.left = (gridOffset.left - $doc.scrollLeft()),
9276				activeOffset.top = (activeOffset.top - gridOffset.top - $doc.scrollTop()),
9277				activeOffset.left = (activeOffset.left - gridOffset.left - $doc.scrollLeft());
9278
9279			(path == 'horizontal') ? (
9280				gridCss.top = 0,
9281				gridCss.left = parseInt((widthAvail / 2) - activeOffset.left - (active.outerWidth() / 2))
9282			) : (
9283				gridCss.top = parseInt(((heightAvail / 2) - activeOffset.top - (active.outerHeight() / 2))),
9284				gridCss.left = 0
9285			);
9286
9287			if (arguments.length < 3) grid.stop().animate(gridCss, opts.effects.repositionSpeed, 'easeOutCirc');
9288			else grid.css(gridCss);
9289		},
9290
9291		loadImage: function(image, callback) {
9292			if (!$.isArray(image)) image = [image];
9293
9294			var iL = this,
9295				length = image.length;
9296
9297			if (length > 0) {
9298				iL.showLoader();
9299				$.each(image, function(index, value) {
9300					var img = new Image();
9301					img.onload = function() {
9302						length -= 1;
9303						if (length == 0) {
9304							iL.hideLoader();
9305							callback(img);
9306						}
9307					};
9308					img.onerror = img.onabort = function() {
9309						length -= 1;
9310						if (length == 0) {
9311							iL.hideLoader();
9312							callback(false);
9313						}
9314					};
9315					img.src = image[index];
9316				});
9317			} else callback(false);
9318		},
9319
9320		patchItemsEvents: function() {
9321			var iL = this,
9322				vars = iL.vars,
9323				clickEvent = supportTouch ? "itap.iL" : "click.iL",
9324				vEvent = supportTouch ? "click.iL" : "itap.iL";
9325
9326			if (iL.context && iL.selector) {
9327				var $items = $(iL.selector, iL.context);
9328
9329				$(iL.context).on(clickEvent, iL.selector, function() {
9330					var $this = $(this),
9331						//UNCODE addition
9332						key = typeof $(this).data('lb-index') !== '' && $(this).closest('.owl-item').length ? $(this).data('lb-index') : $items.index($this);
9333					if (UNCODE.isMobile) {
9334						var isCarousel = $this.closest('.owl-carousel');
9335						if (isCarousel.length) {
9336							if (isCarousel.hasClass('owl-translating')) return false;
9337						}
9338					}
9339
9340					vars.current = key;
9341					vars.next = iL.items[key + 1] ? key + 1 : null;
9342					vars.prev = iL.items[key - 1] ? key - 1 : null;
9343
9344					//UNCODE addition
9345					if ( vars.BODY.hasClass('fp-slide-scrolling') )
9346						return false;
9347
9348					iL.addContents();
9349					iL.patchEvents();
9350
9351					return false;
9352				}).on(vEvent, iL.selector, function() {
9353					return false;
9354				});
9355			} else
9356				$.each(iL.itemsObject, function(key, val) {
9357					val.on(clickEvent, function() {
9358						vars.current = key;
9359						vars.next = iL.items[key + 1] ? key + 1 : null;
9360						vars.prev = iL.items[key - 1] ? key - 1 : null;
9361
9362						iL.addContents();
9363						iL.patchEvents();
9364
9365						return false;
9366					}).on(vEvent, function() {
9367						return false;
9368					});
9369				});
9370		},
9371
9372		dispatchItemsEvents: function() {
9373			var iL = this,
9374				vars = iL.vars,
9375				opts = iL.options;
9376
9377			if (iL.context && iL.selector)
9378				$(iL.context).off('.iL', iL.selector);
9379			else
9380				$.each(iL.itemsObject, function(key, val) {
9381					val.off('.iL');
9382				});
9383		},
9384
9385		refresh: function() {
9386			var iL = this;
9387
9388			iL.dispatchItemsEvents();
9389			iL.attachItems();
9390			iL.normalizeItems();
9391			iL.patchItemsEvents();
9392		},
9393
9394		patchEvents: function() {
9395			var iL = this,
9396				vars = iL.vars,
9397				opts = iL.options,
9398				path = opts.path.toLowerCase(),
9399				holders = $('.ilightbox-holder'),
9400				fullscreenEvent = fullScreenApi.fullScreenEventName + '.iLightBox',
9401				durationThreshold = 1000,
9402				horizontalDistanceThreshold =
9403				verticalDistanceThreshold = 100,
9404				buttonsArray = [vars.nextButton[0], vars.prevButton[0], vars.nextButton[0].firstChild, vars.prevButton[0].firstChild];
9405
9406			$win.bind('resize.iLightBox', function() {
9407				var viewport = getViewport();
9408
9409				if (opts.mobileOptimizer && !opts.innerToolbar) vars.isMobile = viewport.width <= vars.mobileMaxWidth;
9410				vars.BODY[vars.isMobile ? 'addClass' : 'removeClass']('isMobile');
9411
9412				iL.repositionPhoto(null);
9413				if (supportTouch) {
9414					clearTimeout(vars.setTime);
9415					vars.setTime = setTimeout(function() {
9416						var scrollTop = getScrollXY().y;
9417						window.scrollTo(0, scrollTop - 30);
9418						window.scrollTo(0, scrollTop + 30);
9419						window.scrollTo(0, scrollTop);
9420					}, 2000);
9421				}
9422				if (vars.thumbs) iL.positionThumbnails();
9423			}).bind('keydown.iLightBox', function(event) {
9424				if (opts.controls.keyboard) {
9425					switch (event.keyCode) {
9426						case 13:
9427							if (event.shiftKey && opts.keyboard.shift_enter) iL.fullScreenAction();
9428							break;
9429						case 27:
9430							if (opts.keyboard.esc) iL.closeAction();
9431							break;
9432						case 37:
9433							if (opts.keyboard.left && !vars.lockKey) iL.moveTo('prev');
9434							break;
9435						case 38:
9436							if (opts.keyboard.up && !vars.lockKey) iL.moveTo('prev');
9437							break;
9438						case 39:
9439							if (opts.keyboard.right && !vars.lockKey) iL.moveTo('next');
9440							break;
9441						case 40:
9442							if (opts.keyboard.down && !vars.lockKey) iL.moveTo('next');
9443							break;
9444					}
9445				}
9446			});
9447
9448			if (fullScreenApi.supportsFullScreen) $win.bind(fullscreenEvent, function() {
9449				iL.doFullscreen();
9450			});
9451
9452			var holderEventsArr = [opts.caption.show + '.iLightBox', opts.caption.hide + '.iLightBox', opts.social.show + '.iLightBox', opts.social.hide + '.iLightBox'].filter(function(e, i, arr) {
9453					return arr.lastIndexOf(e) === i;
9454				}),
9455				holderEvents = "";
9456
9457			$.each(holderEventsArr, function(key, val) {
9458				if (key != 0) holderEvents += ' ';
9459				holderEvents += val;
9460			});
9461
9462			$doc.on(clickEvent, '.ilightbox-overlay', function() {
9463				if (opts.overlay.blur) iL.closeAction();
9464			}).on(clickEvent, '.ilightbox-next, .ilightbox-next-button', function() {
9465				iL.moveTo('next');
9466			}).on(clickEvent, '.ilightbox-prev, .ilightbox-prev-button', function() {
9467				iL.moveTo('prev');
9468			}).on(clickEvent, '.ilightbox-thumbnail', function() {
9469				var t = $(this),
9470					thumbs = $('.ilightbox-thumbnail', vars.thumbnails),
9471					index = thumbs.index(t);
9472
9473				if (index != vars.current) iL.goTo(index);
9474			}).on(holderEvents, '.ilightbox-holder:not(.ilightbox-next, .ilightbox-prev)', function(e) {
9475				var caption = $('div.ilightbox-caption', vars.holder),
9476					social = $('div.ilightbox-social', vars.holder),
9477					fadeSpeed = opts.effects.fadeSpeed;
9478
9479				if (vars.nextLock || vars.prevLock) {
9480					if (e.type == opts.caption.show && !caption.is(':visible')) caption.fadeIn(fadeSpeed);
9481					else if (e.type == opts.caption.hide && caption.is(':visible')) caption.fadeOut(fadeSpeed);
9482
9483					if (e.type == opts.social.show && !social.is(':visible')) social.fadeIn(fadeSpeed);
9484					else if (e.type == opts.social.hide && social.is(':visible')) social.fadeOut(fadeSpeed);
9485				} else {
9486					if (e.type == opts.caption.show && !caption.is(':visible')) caption.stop().fadeIn(fadeSpeed);
9487					else if (e.type == opts.caption.hide && caption.is(':visible')) caption.stop().fadeOut(fadeSpeed);
9488
9489					if (e.type == opts.social.show && !social.is(':visible')) social.stop().fadeIn(fadeSpeed);
9490					else if (e.type == opts.social.hide && social.is(':visible')) social.stop().fadeOut(fadeSpeed);
9491				}
9492			}).on('mouseenter.iLightBox mouseleave.iLightBox', '.ilightbox-wrapper', function(e) {
9493				if (e.type == 'mouseenter') vars.lockWheel = true;
9494				else vars.lockWheel = false;
9495			}).on(clickEvent, '.ilightbox-toolbar a.ilightbox-close, .ilightbox-toolbar a.ilightbox-fullscreen, .ilightbox-toolbar a.ilightbox-play, .ilightbox-toolbar a.ilightbox-pause', function() {
9496				var t = $(this);
9497
9498				if (t.hasClass('ilightbox-fullscreen')) iL.fullScreenAction();
9499				else if (t.hasClass('ilightbox-play')) {
9500					iL.resume();
9501					t.addClass('ilightbox-pause').removeClass('ilightbox-play');
9502				} else if (t.hasClass('ilightbox-pause')) {
9503					iL.pause();
9504					t.addClass('ilightbox-play').removeClass('ilightbox-pause');
9505				} else iL.closeAction();
9506			}).on(touchMoveEvent, '.ilightbox-overlay, .ilightbox-thumbnails-container', function(e) {
9507				// prevent scrolling
9508				e.preventDefault();
9509			});
9510
9511			function mouseMoveHandler(e) {
9512				if (!vars.isMobile) {
9513					if (!vars.mouseID) {
9514						vars.hideableElements.show();
9515					}
9516
9517					vars.mouseID = clearTimeout(vars.mouseID);
9518
9519					if (buttonsArray.indexOf(e.target) === -1)
9520						vars.mouseID = setTimeout(function() {
9521							vars.hideableElements.hide();
9522							vars.mouseID = clearTimeout(vars.mouseID);
9523						}, 3000);
9524				}
9525			}
9526
9527			if (opts.controls.arrows && !supportTouch) $doc.on('mousemove.iLightBox', mouseMoveHandler);
9528
9529			if (opts.controls.slideshow && opts.slideshow.pauseOnHover) $doc.on('mouseenter.iLightBox mouseleave.iLightBox', '.ilightbox-holder:not(.ilightbox-next, .ilightbox-prev)', function(e) {
9530				if (e.type == 'mouseenter' && vars.cycleID) iL.pause();
9531				else if (e.type == 'mouseleave' && vars.isPaused) iL.resume();
9532			});
9533
9534			var switchers = $('.ilightbox-overlay, .ilightbox-holder, .ilightbox-thumbnails');
9535
9536			if (opts.controls.mousewheel) switchers.on('mousewheel.iLightBox', function(event, delta) {
9537				if (!vars.lockWheel) {
9538					event.preventDefault();
9539					if (delta < 0) iL.moveTo('next');
9540					else if (delta > 0) iL.moveTo('prev');
9541				}
9542			});
9543
9544			if (opts.controls.swipe) holders.on(touchStartEvent, function(event) {
9545				if (vars.nextLock || vars.prevLock || vars.total == 1 || vars.lockSwipe) return;
9546
9547				vars.BODY.addClass('ilightbox-closedhand');
9548
9549				var data = event.originalEvent.touches ? event.originalEvent.touches[0] : event,
9550					scrollTop = $doc.scrollTop(),
9551					scrollLeft = $doc.scrollLeft(),
9552					offsets = [
9553						holders.eq(0).offset(),
9554						holders.eq(1).offset(),
9555						holders.eq(2).offset()
9556					],
9557					offSet = [{
9558						top: offsets[0].top - scrollTop,
9559						left: offsets[0].left - scrollLeft
9560					}, {
9561						top: offsets[1].top - scrollTop,
9562						left: offsets[1].left - scrollLeft
9563					}, {
9564						top: offsets[2].top - scrollTop,
9565						left: offsets[2].left - scrollLeft
9566					}],
9567					start = {
9568						time: (new Date()).getTime(),
9569						coords: [data.pageX - scrollLeft, data.pageY - scrollTop]
9570					},
9571					stop;
9572
9573				function moveEachHandler(i) {
9574					var t = $(this),
9575						offset = offSet[i],
9576						scroll = [(start.coords[0] - stop.coords[0]), (start.coords[1] - stop.coords[1])];
9577
9578					t[0].style[path == "horizontal" ? 'left' : 'top'] = (path == "horizontal" ? offset.left - scroll[0] : offset.top - scroll[1]) + 'px';
9579				}
9580
9581				function moveHandler(event) {
9582
9583					if (!start) return;
9584
9585					var data = event.originalEvent.touches ? event.originalEvent.touches[0] : event;
9586
9587					stop = {
9588						time: (new Date()).getTime(),
9589						coords: [data.pageX - scrollLeft, data.pageY - scrollTop]
9590					};
9591
9592					holders.each(moveEachHandler);
9593
9594					// prevent scrolling
9595					event.preventDefault();
9596				}
9597
9598				function repositionHolders() {
9599					holders.each(function() {
9600						var t = $(this),
9601							offset = t.data('offset') || {
9602								top: t.offset().top - scrollTop,
9603								left: t.offset().left - scrollLeft
9604							},
9605							top = offset.top,
9606							left = offset.left;
9607
9608						t.css(transform, gpuAcceleration).stop().animate({
9609							top: top,
9610							left: left
9611						}, 500, 'easeOutCirc', function() {
9612							t.css(transform, '');
9613						});
9614					});
9615				}
9616
9617				holders.bind(touchMoveEvent, moveHandler);
9618				$doc.one(touchStopEvent, function(event) {
9619					holders.unbind(touchMoveEvent, moveHandler);
9620
9621					vars.BODY.removeClass('ilightbox-closedhand');
9622
9623					if (start && stop) {
9624						if (path == "horizontal" && stop.time - start.time < durationThreshold && abs(start.coords[0] - stop.coords[0]) > horizontalDistanceThreshold && abs(start.coords[1] - stop.coords[1]) < verticalDistanceThreshold) {
9625							if (start.coords[0] > stop.coords[0]) {
9626								if (vars.current == vars.total - 1 && !opts.infinite) repositionHolders();
9627								else {
9628									vars.isSwipe = true;
9629									iL.moveTo('next');
9630								}
9631							} else {
9632								if (vars.current == 0 && !opts.infinite) repositionHolders();
9633								else {
9634									vars.isSwipe = true;
9635									iL.moveTo('prev');
9636								}
9637							}
9638						} 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) {
9639							if (start.coords[1] > stop.coords[1]) {
9640								if (vars.current == vars.total - 1 && !opts.infinite) repositionHolders();
9641								else {
9642									vars.isSwipe = true;
9643									iL.moveTo('next');
9644								}
9645							} else {
9646								if (vars.current == 0 && !opts.infinite) repositionHolders();
9647								else {
9648									vars.isSwipe = true;
9649									iL.moveTo('prev');
9650								}
9651							}
9652						} else repositionHolders();
9653					}
9654					start = stop = undefined;
9655				});
9656			});
9657
9658		},
9659
9660		goTo: function(index) {
9661			var iL = this,
9662				vars = iL.vars,
9663				opts = iL.options,
9664				diff = (index - vars.current);
9665
9666			if (opts.infinite) {
9667				if (index == vars.total - 1 && vars.current == 0) diff = -1;
9668				if (vars.current == vars.total - 1 && index == 0) diff = 1;
9669			}
9670
9671			if (diff == 1) iL.moveTo('next');
9672			else if (diff == -1) iL.moveTo('prev');
9673			else {
9674
9675				if (vars.nextLock || vars.prevLock) return false;
9676
9677				//Trigger the onBeforeChange callback
9678				if (typeof opts.callback.onBeforeChange == 'function') opts.callback.onBeforeChange.call(iL, iL.ui);
9679
9680				if (opts.linkId) {
9681					vars.hashLock = true;
9682					window.location.hash = opts.linkId + '/' + index;
9683				}
9684
9685				if (iL.items[index]) {
9686					if (!iL.items[index].options.mousewheel) vars.lockWheel = true;
9687					else iL.vars.lockWheel = false;
9688
9689					if (!iL.items[index].options.swipe) vars.lockSwipe = true;
9690					else vars.lockSwipe = false;
9691				}
9692
9693				$.each([vars.holder, vars.nextPhoto, vars.prevPhoto], function(key, val) {
9694					val.css(transform, gpuAcceleration).fadeOut(opts.effects.loadedFadeSpeed);
9695				});
9696
9697				vars.current = index;
9698				vars.next = index + 1;
9699				vars.prev = index - 1;
9700
9701				iL.createUI();
9702
9703				setTimeout(function() {
9704					iL.generateBoxes();
9705				}, opts.effects.loadedFadeSpeed + 50);
9706
9707				$('.ilightbox-thumbnail', vars.thumbnails).removeClass('ilightbox-active').eq(index).addClass('ilightbox-active');
9708				iL.positionThumbnails();
9709
9710				if (opts.linkId) setTimeout(function() {
9711					vars.hashLock = false;
9712				}, 55);
9713
9714				// Configure arrow buttons
9715				if (!opts.infinite) {
9716					vars.nextButton.add(vars.prevButton).add(vars.innerPrevButton).add(vars.innerNextButton).removeClass('disabled');
9717
9718					if (vars.current == 0) {
9719						vars.prevButton.add(vars.innerPrevButton).addClass('disabled');
9720					}
9721					if (vars.current >= vars.total - 1) {
9722						vars.nextButton.add(vars.innerNextButton).addClass('disabled');
9723					}
9724				}
9725
9726				// Reset next cycle timeout
9727				iL.resetCycle();
9728
9729				//Trigger the onAfterChange callback
9730				if (typeof opts.callback.onAfterChange == 'function') opts.callback.onAfterChange.call(iL, iL.ui);
9731			}
9732		},
9733
9734		moveTo: function(side) {
9735			var iL = this,
9736				vars = iL.vars,
9737				opts = iL.options,
9738				path = opts.path.toLowerCase(),
9739				viewport = getViewport(),
9740				switchSpeed = opts.effects.switchSpeed;
9741
9742			if (vars.nextLock || vars.prevLock) return false;
9743			else {
9744
9745				var item = (side == "next") ? vars.next : vars.prev;
9746
9747				if (opts.linkId) {
9748					vars.hashLock = true;
9749					window.location.hash = opts.linkId + '/' + item;
9750				}
9751
9752				if (side == "next") {
9753					if (!iL.items[item]) return false;
9754					var firstHolder = vars.nextPhoto,
9755						secondHolder = vars.holder,
9756						lastHolder = vars.prevPhoto,
9757						firstClass = 'ilightbox-prev',
9758						secondClass = 'ilightbox-next';
9759				} else if (side == "prev") {
9760					if (!iL.items[item]) return false;
9761					var firstHolder = vars.prevPhoto,
9762						secondHolder = vars.holder,
9763						lastHolder = vars.nextPhoto,
9764						firstClass = 'ilightbox-next',
9765						secondClass = 'ilightbox-prev';
9766				}
9767
9768				//Trigger the onBeforeChange callback
9769				if (typeof opts.callback.onBeforeChange == 'function')
9770					opts.callback.onBeforeChange.call(iL, iL.ui);
9771
9772				(side == "next") ? vars.nextLock = true: vars.prevLock = true;
9773
9774				var captionFirst = $('div.ilightbox-caption', secondHolder),
9775					socialFirst = $('div.ilightbox-social', secondHolder);
9776
9777				if (captionFirst.length)
9778					captionFirst.stop().fadeOut(switchSpeed, function() {
9779						$(this).remove();
9780					});
9781				if (socialFirst.length)
9782					socialFirst.stop().fadeOut(switchSpeed, function() {
9783						$(this).remove();
9784					});
9785
9786				if (iL.items[item].caption) {
9787					iL.setCaption(iL.items[item], firstHolder);
9788					var caption = $('div.ilightbox-caption', firstHolder),
9789						percent = parseInt((caption.outerHeight() / firstHolder.outerHeight()) * 100);
9790					if (opts.caption.start && percent <= 50) caption.fadeIn(switchSpeed);
9791				}
9792
9793				var social = iL.items[item].options.social;
9794				if (social) {
9795					iL.setSocial(social, iL.items[item].URL, firstHolder);
9796					if (opts.social.start) $('div.ilightbox-social', firstHolder).fadeIn(opts.effects.fadeSpeed);
9797				}
9798
9799				$.each([firstHolder, secondHolder, lastHolder], function(key, val) {
9800					val.removeClass('ilightbox-next ilightbox-prev');
9801				});
9802
9803				var firstOffset = firstHolder.data('offset'),
9804					winW = (viewport.width - (opts.styles.pageOffsetX)),
9805					winH = (viewport.height - (opts.styles.pageOffsetY)),
9806					width = firstOffset.newDims.width,
9807					height = firstOffset.newDims.height,
9808					thumbsOffset = firstOffset.thumbsOffset,
9809					diff = firstOffset.diff,
9810					top = parseInt((winH / 2) - (height / 2) - diff.H - (thumbsOffset.H / 2)),
9811					left = parseInt((winW / 2) - (width / 2) - diff.W - (thumbsOffset.W / 2));
9812
9813				var secondOffset = secondHolder.data('offset'),
9814					object = secondOffset.object;
9815
9816				/*if (object.item.caption && !isNaN(width) && !isNaN(height) && UNCODE.wwidth > UNCODE.mediaQuery) {
9817					var objRatio = width / height;
9818					height = height - vars.captionHeight;
9819					width = height * objRatio;
9820					top = path == 'horizontal' ? parseInt((winH / 2) - (height / 2) - diff.H - (thumbsOffset.H / 2)) : top,
9821					left = parseInt((winW / 2) - (width / 2) - diff.W - (thumbsOffset.W / 2));
9822				}*/
9823
9824				firstHolder.show().css(transform, gpuAcceleration).animate({
9825					top: top,
9826					left: left,
9827					opacity: 1
9828				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc', function() {
9829					firstHolder.css(transform, '');
9830				});
9831
9832				$('div.ilightbox-container', firstHolder).animate({
9833					width: width,
9834					height: height
9835				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc');
9836
9837				diff = secondOffset.diff;
9838
9839				width = secondOffset.newDims.width,
9840					height = secondOffset.newDims.height;
9841
9842				width = parseInt(width * opts.styles[side == 'next' ? 'prevScale' : 'nextScale']),
9843					height = parseInt(height * opts.styles[side == 'next' ? 'prevScale' : 'nextScale']),
9844					top = (path == 'horizontal') ? parseInt((winH / 2) - object.offsetY - (height / 2) - diff.H - (thumbsOffset.H / 2)) : parseInt(winH - object.offsetX - diff.H - (thumbsOffset.H / 2));
9845
9846				if (side == 'prev')
9847					left = (path == 'horizontal') ? parseInt(winW - object.offsetX - diff.W - (thumbsOffset.W / 2)) : parseInt((winW / 2) - (width / 2) - diff.W - object.offsetY - (thumbsOffset.W / 2));
9848				else {
9849					top = (path == 'horizontal') ? top : parseInt(object.offsetX - diff.H - height - (thumbsOffset.H / 2)),
9850					left = (path == 'horizontal') ? parseInt(object.offsetX - diff.W - width - (thumbsOffset.W / 2)) : parseInt((winW / 2) - object.offsetY - (width / 2) - diff.W - (thumbsOffset.W / 2));
9851				}
9852
9853				/*if (object.item.caption && !isNaN(width) && !isNaN(height) && UNCODE.wwidth > UNCODE.mediaQuery) {
9854					var objRatio = width / height;
9855					height = height - vars.captionHeight;
9856					width = height * objRatio;
9857					top = path == 'horizontal' ? parseInt((winH / 2) - (height / 2) - diff.H - (thumbsOffset.H / 2)) : top;
9858				}*/
9859
9860				$('div.ilightbox-container', secondHolder).animate({
9861					width: width,
9862					height: height
9863				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc');
9864
9865				secondHolder.addClass(firstClass).css(transform, gpuAcceleration).animate({
9866					top: top,
9867					left: left,
9868					opacity: opts.styles.prevOpacity,
9869				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc', function() {
9870					secondHolder.css(transform, '');
9871
9872					$('.ilightbox-thumbnail', vars.thumbnails).removeClass('ilightbox-active').eq(item).addClass('ilightbox-active');
9873					iL.positionThumbnails();
9874
9875					if (iL.items[item]) {
9876						if (!iL.items[item].options.mousewheel) vars.lockWheel = true;
9877						else vars.lockWheel = false;
9878
9879						if (!iL.items[item].options.swipe) vars.lockSwipe = true;
9880						else vars.lockSwipe = false;
9881					}
9882
9883					vars.isSwipe = false;
9884
9885					// Remove iframe & video from previous slide
9886					if (['iframe', 'video'].indexOf(iL.items[vars.current].type) !== -1) {
9887						$('div.ilightbox-container', secondHolder).empty();
9888					}
9889
9890					if (side == "next") {
9891						vars.nextPhoto = lastHolder,
9892							vars.prevPhoto = secondHolder,
9893							vars.holder = firstHolder;
9894
9895						vars.nextPhoto.hide();
9896
9897						vars.next = vars.next + 1,
9898							vars.prev = vars.current,
9899							vars.current = vars.current + 1;
9900
9901						if (opts.infinite) {
9902							if (vars.current > vars.total - 1) vars.current = 0;
9903							if (vars.current == vars.total - 1) vars.next = 0;
9904							if (vars.current == 0) vars.prev = vars.total - 1;
9905						}
9906
9907						iL.createUI();
9908
9909						if (!iL.items[vars.next])
9910							vars.nextLock = false;
9911						else
9912							iL.loadContent(iL.items[vars.next], 'next');
9913					} else {
9914						vars.prevPhoto = lastHolder;
9915						vars.nextPhoto = secondHolder;
9916						vars.holder = firstHolder;
9917
9918						vars.prevPhoto.hide();
9919
9920						vars.next = vars.current;
9921						vars.current = vars.prev;
9922						vars.prev = vars.current - 1;
9923
9924						if (opts.infinite) {
9925							if (vars.current == vars.total - 1) vars.next = 0;
9926							if (vars.current == 0) vars.prev = vars.total - 1;
9927						}
9928
9929						iL.createUI();
9930
9931						if (!iL.items[vars.prev])
9932							vars.prevLock = false;
9933						else
9934							iL.loadContent(iL.items[vars.prev], 'prev');
9935					}
9936
9937					// Add iframe & video content for current slide
9938					if (['iframe', 'video'].indexOf(iL.items[vars.current].type) !== -1) {
9939						iL.loadContent(iL.items[vars.current], 'current');
9940					}
9941
9942					if (opts.linkId) setTimeout(function() {
9943						vars.hashLock = false;
9944					}, 55);
9945
9946					// Configure arrow buttons
9947					if (!opts.infinite) {
9948						vars.nextButton.add(vars.prevButton).add(vars.innerPrevButton).add(vars.innerNextButton).removeClass('disabled');
9949
9950						if (vars.current == 0)
9951							vars.prevButton.add(vars.innerPrevButton).addClass('disabled');
9952						if (vars.current >= vars.total - 1)
9953							vars.nextButton.add(vars.innerNextButton).addClass('disabled');
9954					}
9955
9956					iL.repositionPhoto();
9957
9958					// Reset next cycle timeout
9959					iL.resetCycle();
9960
9961					//Trigger the onAfterChange callback
9962					if (typeof opts.callback.onAfterChange == 'function')
9963						opts.callback.onAfterChange.call(iL, iL.ui);
9964				});
9965
9966				top = (path == 'horizontal') ? getPixel(lastHolder, 'top') : ((side == "next") ? parseInt(-(winH / 2) - lastHolder.outerHeight()) : parseInt(top * 2)),
9967					left = (path == 'horizontal') ? ((side == "next") ? parseInt(-(winW / 2) - lastHolder.outerWidth()) : parseInt(left * 2)) : getPixel(lastHolder, 'left');
9968
9969				lastHolder.css(transform, gpuAcceleration).animate({
9970					top: top,
9971					left: left,
9972					opacity: opts.styles.nextOpacity
9973				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc', function() {
9974					lastHolder.css(transform, '');
9975				}).addClass(secondClass);
9976			}
9977		},
9978
9979		setCaption: function(obj, target) {
9980			var iL = this,
9981				caption = $('<div class="ilightbox-caption"></div>');
9982
9983			if (obj.caption) {
9984				caption.html(obj.caption);
9985				$('div.ilightbox-container', target).append(caption);
9986			}
9987		},
9988
9989		normalizeSocial: function(obj, url) {
9990			var iL = this,
9991				vars = iL.vars,
9992				opts = iL.options,
9993				baseURL = window.location.href;
9994
9995			$.each(obj, function(key, value) {
9996				if (!value)
9997					return true;
9998
9999				var item = key.toLowerCase(),
10000					source, text;
10001
10002				switch (item) {
10003					case 'facebook':
10004						source = "http://www.facebook.com/share.php?v=4&src=bm&u={URL}",
10005							text = "Share on Facebook";
10006						break;
10007					case 'twitter':
10008						source = "http://twitter.com/home?status={URL}",
10009							text = "Share on Twitter";
10010						break;
10011					case 'googleplus':
10012						source = "https://plus.google.com/share?url={URL}",
10013							text = "Share on Google+";
10014						break;
10015					case 'delicious':
10016						source = "http://delicious.com/post?url={URL}",
10017							text = "Share on Delicious";
10018						break;
10019					case 'digg':
10020						source = "http://digg.com/submit?phase=2&url={URL}",
10021							text = "Share on Digg";
10022						break;
10023					case 'reddit':
10024						source = "http://reddit.com/submit?url={URL}",
10025							text = "Share on reddit";
10026						break;
10027				}
10028
10029				obj[key] = {
10030					URL: value.URL && absolutizeURI(baseURL, value.URL) || opts.linkId && window.location.href || typeof url !== 'string' && baseURL || url && absolutizeURI(baseURL, url) || baseURL,
10031					source: value.source || source || value.URL && absolutizeURI(baseURL, value.URL) || url && absolutizeURI(baseURL, url),
10032					text: value.text || text || "Share on " + key,
10033					width: (typeof(value.width) != 'undefined' && !isNaN(value.width)) ? parseInt(value.width) : 640,
10034					height: value.height || 360
10035				};
10036
10037			});
10038
10039			return obj;
10040		},
10041
10042		setSocial: function(obj, url, target) {
10043			var iL = this,
10044				socialBar = $('<div class="ilightbox-social"></div>'),
10045				buttons = '<ul>';
10046
10047			obj = iL.normalizeSocial(obj, url);
10048
10049			$.each(obj, function(key, value) {
10050				var item = key.toLowerCase(),
10051					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, '+'));
10052				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>';
10053			});
10054
10055			buttons += '</ul>';
10056
10057			socialBar.html(buttons);
10058			$('div.ilightbox-container', target).append(socialBar);
10059		},
10060
10061		fullScreenAction: function() {
10062			var iL = this,
10063				vars = iL.vars;
10064
10065			if (fullScreenApi.supportsFullScreen) {
10066				if (fullScreenApi.isFullScreen()) fullScreenApi.cancelFullScreen(document.documentElement);
10067				else fullScreenApi.requestFullScreen(document.documentElement);
10068			} else {
10069				iL.doFullscreen();
10070			}
10071		},
10072
10073		doFullscreen: function() {
10074			var iL = this,
10075				vars = iL.vars,
10076				viewport = getViewport(),
10077				opts = iL.options;
10078
10079			if (opts.fullAlone) {
10080				var currentHolder = vars.holder,
10081					current = iL.items[vars.current],
10082					windowWidth = viewport.width,
10083					windowHeight = viewport.height,
10084					elements = [currentHolder, vars.nextPhoto, vars.prevPhoto, vars.nextButton, vars.prevButton, vars.overlay, vars.toolbar, vars.thumbnails, vars.loader],
10085					hideElements = [vars.nextPhoto, vars.prevPhoto, vars.nextButton, vars.prevButton, vars.loader, vars.thumbnails];
10086
10087				if (!vars.isInFullScreen) {
10088					vars.isInFullScreen = vars.lockKey = vars.lockWheel = vars.lockSwipe = true;
10089					vars.overlay.css({
10090						opacity: 1
10091					});
10092
10093					$.each(hideElements, function(i, element) {
10094						element.hide();
10095					});
10096
10097					vars.fullScreenButton.attr('title', opts.text.exitFullscreen);
10098
10099					if (opts.fullStretchTypes.indexOf(current.type) != -1) currentHolder.data({
10100						naturalWidthOld: currentHolder.data('naturalWidth'),
10101						naturalHeightOld: currentHolder.data('naturalHeight'),
10102						naturalWidth: windowWidth,
10103						naturalHeight: windowHeight
10104					});
10105					else {
10106						var viewport = current.options.fullViewPort || opts.fullViewPort || '',
10107							newWidth = windowWidth,
10108							newHeight = windowHeight,
10109							width = currentHolder.data('naturalWidth'),
10110							height = currentHolder.data('naturalHeight');
10111
10112						if (viewport.toLowerCase() == 'fill') {
10113							newHeight = (newWidth / width) * height;
10114
10115							if (newHeight < windowHeight) {
10116								newWidth = (windowHeight / height) * width,
10117									newHeight = windowHeight;
10118							}
10119						} else if (viewport.toLowerCase() == 'fit') {
10120							var dims = iL.getNewDimenstions(newWidth, newHeight, width, height, true);
10121
10122							newWidth = dims.width,
10123								newHeight = dims.height;
10124						} else if (viewport.toLowerCase() == 'stretch') {
10125							newWidth = newWidth,
10126								newHeight = newHeight;
10127						} else {
10128							var scale = (width > newWidth || height > newHeight) ? true : false,
10129								dims = iL.getNewDimenstions(newWidth, newHeight, width, height, scale);
10130							newWidth = dims.width,
10131								newHeight = dims.height;
10132						}
10133
10134						currentHolder.data({
10135							naturalWidthOld: currentHolder.data('naturalWidth'),
10136							naturalHeightOld: currentHolder.data('naturalHeight'),
10137							naturalWidth: newWidth,
10138							naturalHeight: newHeight
10139						});
10140					}
10141
10142					$.each(elements, function(key, val) {
10143						val.addClass('ilightbox-fullscreen');
10144					});
10145
10146					//Trigger the onEnterFullScreen callback
10147					if (typeof opts.callback.onEnterFullScreen == 'function') opts.callback.onEnterFullScreen.call(iL, iL.ui);
10148				} else {
10149					vars.isInFullScreen = vars.lockKey = vars.lockWheel = vars.lockSwipe = false;
10150					vars.overlay.css({
10151						opacity: iL.options.overlay.opacity
10152					});
10153
10154					$.each(hideElements, function(i, element) {
10155						element.show();
10156					});
10157
10158					vars.fullScreenButton.attr('title', opts.text.enterFullscreen);
10159
10160					currentHolder.data({
10161						naturalWidth: currentHolder.data('naturalWidthOld'),
10162						naturalHeight: currentHolder.data('naturalHeightOld'),
10163						naturalWidthOld: null,
10164						naturalHeightOld: null
10165					});
10166
10167					$.each(elements, function(key, val) {
10168						val.removeClass('ilightbox-fullscreen');
10169					});
10170
10171					//Trigger the onExitFullScreen callback
10172					if (typeof opts.callback.onExitFullScreen == 'function') opts.callback.onExitFullScreen.call(iL, iL.ui);
10173				}
10174			} else {
10175				if (!vars.isInFullScreen) vars.isInFullScreen = true;
10176				else vars.isInFullScreen = false;
10177			}
10178			iL.repositionPhoto(true);
10179		},
10180
10181		closeAction: function() {
10182			var iL = this,
10183				vars = iL.vars,
10184				opts = iL.options;
10185
10186			$win.unbind('.iLightBox');
10187			$doc.off('.iLightBox');
10188
10189			if (vars.isInFullScreen) fullScreenApi.cancelFullScreen(document.documentElement);
10190
10191			$('.ilightbox-overlay, .ilightbox-holder, .ilightbox-thumbnails').off('.iLightBox');
10192
10193			if (opts.hide.effect) vars.overlay.stop().fadeOut(opts.hide.speed, function() {
10194				vars.overlay.remove();
10195				vars.BODY.removeClass('ilightbox-noscroll').off('.iLightBox');
10196			});
10197			else {
10198				vars.overlay.remove();
10199				vars.BODY.removeClass('ilightbox-noscroll').off('.iLightBox');
10200			}
10201
10202			var fadeOuts = [vars.toolbar, vars.holder, vars.nextPhoto, vars.prevPhoto, vars.nextButton, vars.prevButton, vars.loader, vars.thumbnails];
10203
10204			$.each(fadeOuts, function(i, element) {
10205				element.removeAttr('style').remove();
10206			});
10207
10208			vars.dontGenerateThumbs = vars.isInFullScreen = false;
10209
10210			window.iLightBox = null;
10211
10212			if (opts.linkId) {
10213				vars.hashLock = true;
10214				removeHash();
10215				setTimeout(function() {
10216					vars.hashLock = false;
10217				}, 55);
10218			}
10219
10220			vars.nextButton.add(vars.prevButton).add(vars.innerPrevButton).add(vars.innerNextButton).removeClass('disabled');
10221
10222			//Trigger the onHide callback
10223			if (typeof opts.callback.onHide == 'function') opts.callback.onHide.call(iL, iL.ui);
10224		},
10225
10226		repositionPhoto: function() {
10227			var iL = this,
10228				vars = iL.vars,
10229				opts = iL.options,
10230				path = opts.path.toLowerCase(),
10231				viewport = getViewport(),
10232				winWidth = viewport.width,
10233				winHeight = viewport.height;
10234
10235			if (viewport.width < UNCODE.mediaQuery) opts.styles.nextOffsetX = 0;
10236
10237			var thumbsOffsetW = (vars.isInFullScreen && opts.fullAlone || vars.isMobile) ? 0 : ((path == 'horizontal') ? 0 : vars.thumbnails.outerWidth()),
10238				thumbsOffsetH = vars.isMobile ? vars.toolbar.outerHeight() : ((vars.isInFullScreen && opts.fullAlone) ? 0 : ((path == 'horizontal') ? vars.thumbnails.outerHeight() : 0)),
10239				width = (vars.isInFullScreen && opts.fullAlone) ? winWidth : (winWidth - (opts.styles.pageOffsetX)),
10240				height = (vars.isInFullScreen && opts.fullAlone) ? winHeight : (winHeight - (opts.styles.pageOffsetY)),
10241				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,
10242				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)));
10243
10244			var elObject = {
10245				type: 'current',
10246				width: width,
10247				height: height,
10248				item: iL.items[vars.current],
10249				offsetW: offsetW,
10250				offsetH: offsetH,
10251				thumbsOffsetW: thumbsOffsetW,
10252				thumbsOffsetH: thumbsOffsetH,
10253				animate: arguments.length,
10254				holder: vars.holder
10255			};
10256
10257			iL.repositionEl(elObject);
10258
10259			if (iL.items[vars.next]) {
10260				elObject = $.extend(elObject, {
10261					type: 'next',
10262					item: iL.items[vars.next],
10263					offsetX: opts.styles.nextOffsetX,
10264					offsetY: opts.styles.nextOffsetY,
10265					holder: vars.nextPhoto
10266				});
10267
10268				iL.repositionEl(elObject);
10269			}
10270
10271			if (iL.items[vars.prev]) {
10272				elObject = $.extend(elObject, {
10273					type: 'prev',
10274					item: iL.items[vars.prev],
10275					offsetX: opts.styles.nextOffsetX,
10276					offsetY: opts.styles.prevOffsetY,
10277					holder: vars.prevPhoto
10278				});
10279
10280				iL.repositionEl(elObject);
10281			}
10282
10283			var loaderCss = (path == "horizontal") ? {
10284				left: parseInt((width / 2) - (vars.loader.outerWidth() / 2))
10285			} : {
10286				top: parseInt((height / 2) - (vars.loader.outerHeight() / 2))
10287			};
10288			vars.loader.css(loaderCss);
10289		},
10290
10291		repositionEl: function(obj) {
10292			var iL = this,
10293				vars = iL.vars,
10294				opts = iL.options,
10295				path = opts.path.toLowerCase(),
10296				widthAvail = (obj.type == 'current') ? ((vars.isInFullScreen && opts.fullAlone) ? obj.width : (obj.width - obj.offsetW)) : (obj.width - obj.offsetW),
10297				heightAvail = (obj.type == 'current') ? ((vars.isInFullScreen && opts.fullAlone) ? obj.height : (obj.height - obj.offsetH)) : (obj.height - obj.offsetH),
10298				itemParent = obj.item,
10299				item = obj.item.options,
10300				holder = obj.holder,
10301				offsetX = obj.offsetX || 0,
10302				offsetY = obj.offsetY || 0,
10303				thumbsOffsetW = obj.thumbsOffsetW,
10304				thumbsOffsetH = obj.thumbsOffsetH;
10305
10306			if (obj.type == 'current') {
10307				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);
10308				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);
10309			} else {
10310				if (typeof item.width == 'number' && item.width) widthAvail = (item.width > widthAvail) ? widthAvail : item.width;
10311				if (typeof item.height == 'number' && item.height) heightAvail = (item.height > heightAvail) ? heightAvail : item.height;
10312			}
10313
10314			heightAvail = parseInt(heightAvail - $('.ilightbox-inner-toolbar', holder).outerHeight());
10315
10316			var width = (typeof item.width == 'string' && item.width.indexOf('%') != -1) ? percentToValue(parseInt(item.width.replace('%', '')), obj.width) : holder.data('naturalWidth'),
10317				height = (typeof item.height == 'string' && item.height.indexOf('%') != -1) ? percentToValue(parseInt(item.height.replace('%', '')), obj.height) : holder.data('naturalHeight');
10318
10319			var dims = ((typeof item.width == 'string' && item.width.indexOf('%') != -1 || typeof item.height == 'string' && item.height.indexOf('%') != -1) ? {
10320					width: width,
10321					height: height
10322				} : iL.getNewDimenstions(widthAvail, heightAvail, width, height)),
10323				newDims = $.extend({}, dims, {});
10324
10325			if (obj.type == 'prev' || obj.type == 'next')
10326				width = parseInt(dims.width * ((obj.type == 'next') ? opts.styles.nextScale : opts.styles.prevScale)),
10327				height = parseInt(dims.height * ((obj.type == 'next') ? opts.styles.nextScale : opts.styles.prevScale));
10328			else
10329				width = dims.width,
10330				height = dims.height;
10331
10332			var widthDiff = parseInt((getPixel(holder, 'padding-left') + getPixel(holder, 'padding-right') + getPixel(holder, 'border-left-width') + getPixel(holder, 'border-right-width')) / 2),
10333				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);
10334
10335			switch (obj.type) {
10336				case 'current':
10337					var top = parseInt((obj.height / 2) - (height / 2) - heightDiff - (thumbsOffsetH / 2)),
10338						left = parseInt((obj.width / 2) - (width / 2) - widthDiff - (thumbsOffsetW / 2));
10339					break;
10340
10341				case 'next':
10342					var top = (path == 'horizontal') ? parseInt((obj.height / 2) - offsetY - (height / 2) - heightDiff - (thumbsOffsetH / 2)) : parseInt(obj.height - offsetX - heightDiff - (thumbsOffsetH / 2)),
10343						left = (path == 'horizontal') ? parseInt(obj.width - offsetX - widthDiff - (thumbsOffsetW / 2)) : parseInt((obj.width / 2) - (width / 2) - widthDiff - offsetY - (thumbsOffsetW / 2));
10344					break;
10345
10346				case 'prev':
10347					var top = (path == 'horizontal') ? parseInt((obj.height / 2) - offsetY - (height / 2) - heightDiff - (thumbsOffsetH / 2)) : parseInt(offsetX - heightDiff - height - (thumbsOffsetH / 2)),
10348						left = (path == 'horizontal') ? parseInt(offsetX - widthDiff - width - (thumbsOffsetW / 2)) : parseInt((obj.width / 2) - offsetY - (width / 2) - widthDiff - (thumbsOffsetW / 2));
10349					break;
10350			}
10351
10352			holder.data('offset', {
10353				top: top,
10354				left: left,
10355				newDims: newDims,
10356				diff: {
10357					W: widthDiff,
10358					H: heightDiff
10359				},
10360				thumbsOffset: {
10361					W: thumbsOffsetW,
10362					H: thumbsOffsetH
10363				},
10364				object: obj
10365			});
10366
10367			var opacityMobile;
10368			if ( isMobile ) {
10369				opacityMobile = {
10370					opacity: 1,
10371					top: top,
10372					left: left
10373				};
10374			} else {
10375				opacityMobile = {
10376					top: top,
10377					left: left
10378				};
10379			}
10380
10381			if (obj.animate > 0 && opts.effects.reposition) {
10382				holder.css(transform, gpuAcceleration).stop().animate(
10383					opacityMobile,
10384					opts.effects.repositionSpeed, 'easeOutCirc', function() {
10385					holder.css(transform, '');
10386				});
10387				$('div.ilightbox-container', holder).stop().animate({
10388					width: width,
10389					height: height
10390				}, opts.effects.repositionSpeed, 'easeOutCirc');
10391				$('div.ilightbox-inner-toolbar', holder).stop().animate({
10392					width: width
10393				}, opts.effects.repositionSpeed, 'easeOutCirc', function() {
10394					$(this).css('overflow', 'visible');
10395				});
10396			} else {
10397				holder.css(opacityMobile);
10398				$('div.ilightbox-container', holder).css({
10399					width: width,
10400					height: height
10401				});
10402				$('div.ilightbox-inner-toolbar', holder).css({
10403					width: width
10404				});
10405			}
10406		},
10407
10408
10409		resume: function(priority) {
10410			var iL = this,
10411				vars = iL.vars,
10412				opts = iL.options;
10413
10414			if (!opts.slideshow.pauseTime || opts.controls.slideshow && vars.total <= 1 || priority < vars.isPaused) {
10415				return;
10416			}
10417
10418			vars.isPaused = 0;
10419
10420			if (vars.cycleID) {
10421				vars.cycleID = clearTimeout(vars.cycleID);
10422			}
10423
10424			vars.cycleID = setTimeout(function() {
10425				if (vars.current == vars.total - 1) iL.goTo(0);
10426				else iL.moveTo('next');
10427			}, opts.slideshow.pauseTime);
10428		},
10429
10430		pause: function(priority) {
10431			var iL = this,
10432				vars = iL.vars,
10433				opts = iL.options;
10434
10435			if (priority < vars.isPaused) {
10436				return;
10437			}
10438
10439			vars.isPaused = priority || 100;
10440
10441			if (vars.cycleID) {
10442				vars.cycleID = clearTimeout(vars.cycleID);
10443			}
10444		},
10445
10446		resetCycle: function() {
10447			var iL = this,
10448				vars = iL.vars,
10449				opts = iL.options;
10450
10451			if (opts.controls.slideshow && vars.cycleID && !vars.isPaused) {
10452				iL.resume();
10453			}
10454		},
10455
10456		getNewDimenstions: function(width, height, width_old, height_old, thumb) {
10457			var iL = this;
10458
10459			if (!width) factor = height / height_old;
10460			else if (!height) factor = width / width_old;
10461			else factor = min(width / width_old, height / height_old);
10462
10463			if (!thumb) {
10464				if (factor > iL.options.maxScale) factor = iL.options.maxScale;
10465				else if (factor < iL.options.minScale) factor = iL.options.minScale;
10466			}
10467
10468			var final_width = (iL.options.keepAspectRatio) ? round(width_old * factor) : width,
10469				final_height = (iL.options.keepAspectRatio) ? round(height_old * factor) : height;
10470
10471			return {
10472				width: final_width,
10473				height: final_height,
10474				ratio: factor
10475			};
10476		},
10477
10478		setOption: function(options) {
10479			var iL = this;
10480
10481			iL.options = $.extend(true, iL.options, options || {});
10482			iL.refresh();
10483		},
10484
10485		availPlugins: function() {
10486			var iL = this,
10487				testEl = document.createElement("video");
10488
10489			iL.plugins = {
10490				flash: !isMobile,
10491				quicktime: (parseInt(PluginDetect.getVersion("QuickTime")) >= 0) ? true : false,
10492				html5H264: !!(testEl.canPlayType && testEl.canPlayType('video/mp4').replace(/no/, '')),
10493				html5WebM: !!(testEl.canPlayType && testEl.canPlayType('video/webm').replace(/no/, '')),
10494				html5Vorbis: !!(testEl.canPlayType && testEl.canPlayType('video/ogg').replace(/no/, '')),
10495				html5QuickTime: !!(testEl.canPlayType && testEl.canPlayType('video/quicktime').replace(/no/, ''))
10496			};
10497		},
10498
10499		addContent: function(element, obj) {
10500			var iL = this,
10501				el;
10502
10503			switch (obj.type) {
10504				case 'video':
10505					var HTML5 = false,
10506						videoType = obj.videoType,
10507						html5video = obj.options.html5video;
10508
10509					if (((videoType == 'video/mp4' || obj.ext == 'mp4' || obj.ext == 'm4v') || html5video.h264) && iL.plugins.html5H264)
10510						obj.ext = 'mp4',
10511						obj.URL = html5video.h264 || obj.URL;
10512					else if (html5video.webm && iL.plugins.html5WebM)
10513						obj.ext = 'webm',
10514						obj.URL = html5video.webm || obj.URL;
10515					else if (html5video.ogg && iL.plugins.html5Vorbis)
10516						obj.ext = 'ogv',
10517						obj.URL = html5video.ogg || obj.URL;
10518
10519					if (iL.plugins.html5H264 && (videoType == 'video/mp4' || obj.ext == 'mp4' || obj.ext == 'm4v')) HTML5 = true, videoType = "video/mp4";
10520					else if (iL.plugins.html5WebM && (videoType == 'video/webm' || obj.ext == 'webm')) HTML5 = true, videoType = "video/webm";
10521					else if (iL.plugins.html5Vorbis && (videoType == 'video/ogg' || obj.ext == 'ogv')) HTML5 = true, videoType = "video/ogg";
10522					else if (iL.plugins.html5QuickTime && (videoType == 'video/quicktime' || obj.ext == 'mov' || obj.ext == 'qt')) HTML5 = true, videoType = "video/quicktime";
10523
10524					if (HTML5) {
10525						el = $('<video />
10525', {
10526							"width": "100%",
10527							"height": "100%",
10528							"preload": html5video.preload,
10529							"autoplay": html5video.autoplay,
10530							"loop": html5video.loop,
10531							"poster": html5video.poster,
10532							"controls": html5video.controls,
10533							"controlslist": "nodownload"
10534						}).append($('<source />', {
10535							"src": obj.URL,
10536							"type": videoType
10537						}));
10538					} else {
10539						if (!iL.plugins.quicktime) el = $('<span />', {
10540							"class": "ilightbox-alert",
10541							html: iL.options.errors.missingPlugin.replace('{pluginspage}', pluginspages.quicktime).replace('{type}', 'QuickTime')
10542						});
10543						else {
10544
10545							el = $('<object />', {
10546								"type": "video/quicktime",
10547								"pluginspage": pluginspages.quicktime
10548							}).attr({
10549								"data": obj.URL,
10550								"width": "100%",
10551								"height": "100%"
10552							}).append($('<param />', {
10553								"name": "src",
10554								"value": obj.URL
10555							})).append($('<param />', {
10556								"name": "autoplay",
10557								"value": "false"
10558							})).append($('<param />', {
10559								"name": "loop",
10560								"value": "false"
10561							})).append($('<param />', {
10562								"name": "scale",
10563								"value": "tofit"
10564							}));
10565
10566							if (browser.msie) el = QT_GenerateOBJECTText(obj.URL, '100%', '100%', '', 'SCALE', 'tofit', 'AUTOPLAY', 'false', 'LOOP', 'false');
10567						}
10568					}
10569
10570					break;
10571
10572				case 'flash':
10573					if (!iL.plugins.flash) el = $('<span />', {
10574						"class": "ilightbox-alert",
10575						html: iL.options.errors.missingPlugin.replace('{pluginspage}', pluginspages.flash).replace('{type}', 'Adobe Flash player')
10576					});
10577					else {
10578						var flashvars = "",
10579							i = 0;
10580
10581						if (obj.options.flashvars) $.each(obj.options.flashvars, function(k, v) {
10582							if (i != 0) flashvars += "&";
10583							flashvars += k + "=" + encodeURIComponent(v);
10584							i++;
10585						});
10586						else flashvars = null;
10587
10588						el = $('<embed />').attr({
10589							"type": "application/x-shockwave-flash",
10590							"src": obj.URL,
10591							"width": (typeof obj.options.width == 'number' && obj.options.width && iL.options.minScale == '1' && iL.options.maxScale == '1') ? obj.options.width : "100%",
10592							"height": (typeof obj.options.height == 'number' && obj.options.height && iL.options.minScale == '1' && iL.options.maxScale == '1') ? obj.options.height : "100%",
10593							"quality": "high",
10594							"bgcolor": "#000000",
10595							"play": "true",
10596							"loop": "true",
10597							"menu": "true",
10598							"wmode": "transparent",
10599							"scale": "showall",
10600							"allowScriptAccess": "always",
10601							"allowFullScreen": "true",
10602							"flashvars": flashvars,
10603							"fullscreen": "yes"
10604						});
10605					}
10606
10607					break;
10608
10609				case 'iframe':
10610					el = $('<iframe />').attr({
10611						"width": (typeof obj.options.width == 'number' && obj.options.width && iL.options.minScale == '1' && iL.options.maxScale == '1') ? obj.options.width : "100%",
10612						"height": (typeof obj.options.height == 'number' && obj.options.height && iL.options.minScale == '1' && iL.options.maxScale == '1') ? obj.options.height : "100%",
10613						src: obj.URL,
10614						frameborder: 0,
10615						'hspace': 0,
10616						'vspace': 0,
10617						'scrolling': supportTouch ? 'auto' : 'scroll',
10618						'webkitAllowFullScreen': '',
10619						'mozallowfullscreen': '',
10620						'allowFullScreen': ''
10621					});
10622
10623					break;
10624
10625				case 'inline':
10626					el = $('<div class="ilightbox-wrapper"></div>').html($(obj.URL).clone(true));
10627
10628					break;
10629
10630				case 'html':
10631					var object = obj.URL,
10632						el;
10633
10634					if (object[0].nodeName) {
10635						el = $('<div class="ilightbox-wrapper"></div>').html(object);
10636					} else {
10637						var dom = $(obj.URL),
10638							html = (dom.selector) ? $('<div>' + dom + '</div>') : dom;
10639						el = $('<div class="ilightbox-wrapper"></div>').html(html);
10640					}
10641
10642					break;
10643			}
10644
10645			$('div.ilightbox-container', element).empty().html(el);
10646
10647			// For fixing Chrome about just playing the video for first time
10648			if (el[0].tagName.toLowerCase() === 'video' && browser.webkit) setTimeout(function() {
10649				var src = el[0].currentSrc + '?' + floor(random() * 30000);
10650				el[0].currentSrc = src;
10651				el[0].src = src;
10652			});
10653
10654			return el;
10655		},
10656
10657		ogpRecognition: function(obj, callback) {
10658			var iL = this,
10659				url = obj.URL;
10660
10661			iL.showLoader();
10662			doAjax(url, function(data) {
10663				iL.hideLoader();
10664				if (data) {
10665					var object = new Object();
10666
10667					object.length = false,
10668					object.url = data.url;
10669
10670					if (data.status == 200) {
10671						var result = data.results,
10672							type = result.type,
10673							source = result.source;
10674
10675						object.source = source.src,
10676						object.width = source.width && parseInt(source.width) || 0,
10677						object.height = source.height && parseInt(source.height) || 0,
10678						object.type = type,
10679						object.thumbnail = source.thumbnail || result.images && result.images[0],
10680						object.html5video = result.html5video || {},
10681						object.length = true;
10682
10683						if (source.type == 'application/x-shockwave-flash') object.type = "flash";
10684						else if (source.type.indexOf("video/") != -1) object.type = "video";
10685						else if (source.type.indexOf("/html") != -1) object.type = "iframe";
10686						else if (source.type.indexOf("image/") != -1) object.type = "image";
10687
10688					} else if (typeof data.response != 'undefined')
10689						throw data.response;
10690
10691					callback.call(this, object.length ? object : false);
10692				}
10693			});
10694		},
10695
10696		hashChangeHandler: function(url) {
10697			var iL = this,
10698				vars = iL.vars,
10699				opts = iL.options,
10700				URL = url || window.location.href,
10701				hash = parseURI(URL).hash,
10702				split = hash.split('/'),
10703				index = split[1];
10704
10705			if (vars.hashLock || ('#' + opts.linkId != split[0] && hash.length > 1)) return;
10706
10707			if (index) {
10708				var target = split[1] || 0;
10709				if (iL.items[target]) {
10710					var overlay = $('.ilightbox-overlay');
10711					if (overlay.length && overlay.attr('linkid') == opts.linkId) {
10712						iL.goTo(target);
10713					} else {
10714						iL.itemsObject[target].trigger(supportTouch ? 'itap' : 'click');
10715						//iL.goTo(target);
10716					}
10717				} else {
10718					var overlay = $('.ilightbox-overlay');
10719					if (overlay.length) iL.closeAction();
10720				}
10721			} else {
10722				var overlay = $('.ilightbox-overlay');
10723				if (overlay.length) iL.closeAction();
10724			}
10725		}
10726	};
10727
10728	/**
10729	 * Parse style to pixels.
10730	 *
10731	 * @param {Object}   $element   jQuery object with element.
10732	 * @param {Property} property   CSS property to get the pixels from.
10733	 *
10734	 * @return {Int}
10735	 */
10736	function getPixel($element, property) {
10737		return parseInt($element.css(property), 10) || 0;
10738	}
10739
10740	/**
10741	 * Make sure that number is within the limits.
10742	 *
10743	 * @param {Number} number
10744	 * @param {Number} min
10745	 * @param {Number} max
10746	 *
10747	 * @return {Number}
10748	 */
10749	function within(number, min, max) {
10750		return number < min ? min : number > max ? max : number;
10751	}
10752
10753	/**
10754	 * Get viewport/window size (width and height).
10755	 *
10756	 * @return {Object}
10757	 */
10758	function getViewport() {
10759		var e = window,
10760			a = 'inner';
10761		if (!('innerWidth' in window)) {
10762			a = 'client';
10763			e = document.documentElement || document.body;
10764		}
10765		return {
10766			width: e[a + 'Width'],
10767			height: e[a + 'Height']
10768		}
10769	}
10770
10771	/**
10772	 * Remove hash tag from the URL
10773	 *
10774	 * @return {Void}
10775	 */
10776	function removeHash() {
10777		var scroll = getScrollXY();
10778
10779		//UNCODE change
10780		//window.location.hash = "";
10781		history.pushState("", document.title, window.location.pathname + window.location.search);
10782
10783		// Restore the scroll offset, should be flicker free
10784		window.scrollTo(scroll.x, scroll.y);
10785	}
10786
10787	/**
10788	 * Do the ajax requests with callback.
10789	 *
10790	 * @param {String}   url
10791	 * @param {Function} callback
10792	 *
10793	 * @return {Void}
10794	 */
10795	function doAjax(url, callback) {
10796		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');
10797		$.ajax({
10798			url: url,
10799			dataType: 'jsonp'
10800		});
10801
10802		iLCallback = function(data) {
10803			callback.call(this, data);
10804		};
10805	}
10806
10807	/**
10808	 * Find image from DOM elements
10809	 *
10810	 * @param {Element} element
10811	 *
10812	 * @return {Void}
10813	 */
10814	function findImageInElement(element) {
10815		var elements = $('*', element),
10816			imagesArr = new Array();
10817
10818		elements.each(function() {
10819			var url = "",
10820				element = this;
10821
10822			if ($(element).css("background-image") != "none") {
10823				url = $(element).css("background-image");
10824			} else if (typeof($(element).attr("src")) != "undefined" && element.nodeName.toLowerCase() == "img") {
10825				url = $(element).attr("src");
10826			}
10827
10828			if (url.indexOf("gradient") == -1) {
10829				url = url.replace(/url\(\"/g, "");
10830				url = url.replace(/url\(/g, "");
10831				url = url.replace(/\"\)/g, "");
10832				url = url.replace(/\)/g, "");
10833
10834				var urls = url.split(",");
10835
10836				for (var i = 0; i < urls.length; i++) {
10837					if (urls[i].length > 0 && $.inArray(urls[i], imagesArr) == -1) {
10838						var extra = "";
10839						if (browser.msie && browser.version < 9) {
10840							extra = "?" + floor(random() * 3000);
10841						}
10842						imagesArr.push(urls[i] + extra);
10843					}
10844				}
10845			}
10846		});
10847
10848		return imagesArr;
10849	}
10850
10851	/**
10852	 * Get file extension.
10853	 *
10854	 * @param {String} URL
10855	 *
10856	 * @return {String}
10857	 */
10858	function getExtension(URL) {
10859		if (URL !== null) {
10860			//UNCODE: fix issue to detect media type with query strings
10861			var ext = URL.indexOf('?') !== -1 ? URL.split('?')[0] : URL;
10862			ext = ext.split('.').pop().toLowerCase();
10863
10864			return ext;
10865		}
10866	}
10867
10868	/**
10869	 * Get type via extension.
10870	 *
10871	 * @param {String} URL
10872	 *
10873	 * @return {String}
10874	 */
10875	function getTypeByExtension(URL) {
10876		var type,
10877			ext = getExtension(URL);
10878
10879		if (extensions.image.indexOf(ext) !== -1) type = 'image';
10880		else if (extensions.flash.indexOf(ext) !== -1) type = 'flash';
10881		else if (extensions.video.indexOf(ext) !== -1) type = 'video';
10882		else if (ext.substring(0, 1) == "#") type = 'inline'; //Uncode addition
10883		else type = 'iframe';
10884
10885		return type;
10886	}
10887
10888	/**
10889	 * Return value from percent of a number.
10890	 *
10891	 * @param {Number} percent
10892	 * @param {Number} total
10893	 *
10894	 * @return {Number}
10895	 */
10896	function percentToValue(percent, total) {
10897		return parseInt((total / 100) * percent);
10898	}
10899
10900	/**
10901	 * A JavaScript equivalent of PHP’s parse_url.
10902	 *
10903	 * @param {String} url           The URL to parse.
10904	 *
10905	 * @return {Mixed}
10906	 */
10907	function parseURI(url) {
10908		var m = String(url).replace(/^\s+|\s+$/g, '').match(/^([^:\/?#]+:)?(\/\/(?:[^:@]*(?::[^:@]*)?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/);
10909		// authority = '//' + user + ':' + pass '@' + hostname + ':' port
10910		return (m ? {
10911			href: m[0] || '',
10912			protocol: m[1] || '',
10913			authority: m[2] || '',
10914			host: m[3] || '',
10915			hostname: m[4] || '',
10916			port: m[5] || '',
10917			pathname: m[6] || '',
10918			search: m[7] || '',
10919			hash: m[8] || ''
10920		} : null);
10921	}
10922
10923	/**
10924	 * Gets the absolute URI.
10925	 *
10926	 * @param {String} href     The relative URL.
10927	 * @param {String} base     The base URL.
10928	 *
10929	 * @return {String}         The absolute URL.
10930	 */
10931	function absolutizeURI(base, href) { // RFC 3986
10932		var iL = this;
10933
10934		function removeDotSegments(input) {
10935			var output = [];
10936			input.replace(/^(\.\.?(\/|$))+/, '')
10937				.replace(/\/(\.(\/|$))+/g, '/')
10938				.replace(/\/\.\.$/, '/../')
10939				.replace(/\/?[^\/]*/g, function(p) {
10940					if (p === '/..') {
10941						output.pop();
10942					} else {
10943						output.push(p);
10944					}
10945				});
10946			return output.join('').replace(/^\//, input.charAt(0) === '/' ? '/' : '');
10947		}
10948
10949		href = parseURI(href || '');
10950		base = parseURI(base || '');
10951
10952		return !href || !base ? null : (href.protocol || base.protocol) +
10953			(href.protocol || href.authority ? href.authority : base.authority) +
10954			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)) +
10955			(href.protocol || href.authority || href.pathname ? href.search : (href.search || base.search)) +
10956			href.hash;
10957	}
10958
10959	/**
10960	 * A JavaScript equivalent of PHP’s version_compare.
10961	 *
10962	 * @param {String} v1
10963	 * @param {String} v2
10964	 * @param {String} operator
10965	 *
10966	 * @return {Boolean}
10967	 */
10968	function version_compare(v1, v2, operator) {
10969		this.php_js = this.php_js || {};
10970		this.php_js.ENV = this.php_js.ENV || {};
10971		var i = 0,
10972			x = 0,
10973			compare = 0,
10974			vm = {
10975				'dev': -6,
10976				'alpha': -5,
10977				'a': -5,
10978				'beta': -4,
10979				'b': -4,
10980				'RC': -3,
10981				'rc': -3,
10982				'#': -2,
10983				'p': 1,
10984				'pl': 1
10985			},
10986			prepVersion = function(v) {
10987				v = ('' + v).replace(/[_\-+]/g, '.');
10988				v = v.replace(/([^.\d]+)/g, '.$1.').replace(/\.{2,}/g, '.');
10989				return (!v.length ? [-8] : v.split('.'));
10990			},
10991			numVersion = function(v) {
10992				return !v ? 0 : (isNaN(v) ? vm[v] || -7 : parseInt(v, 10));
10993			};
10994		v1 = prepVersion(v1);
10995		v2 = prepVersion(v2);
10996		x = max(v1.length, v2.length);
10997		for (i = 0; i < x; i++) {
10998			if (v1[i] == v2[i]) {
10999				continue;
11000			}
11001			v1[i] = numVersion(v1[i]);
11002			v2[i] = numVersion(v2[i]);
11003			if (v1[i] < v2[i]) {
11004				compare = -1;
11005				break;
11006			} else if (v1[i] > v2[i]) {
11007				compare = 1;
11008				break;
11009			}
11010		}
11011		if (!operator) {
11012			return compare;
11013		}
11014
11015		switch (operator) {
11016			case '>':
11017			case 'gt':
11018				return (compare > 0);
11019			case '>=':
11020			case 'ge':
11021				return (compare >= 0);
11022			case '<=':
11023			case 'le':
11024				return (compare <= 0);
11025			case '==':
11026			case '=':
11027			case 'eq':
11028				return (compare === 0);
11029			case '<>':
11030			case '!=':
11031			case 'ne':
11032				return (compare !== 0);
11033			case '':
11034			case '<':
11035			case 'lt':
11036				return (compare < 0);
11037			default:
11038				return null;
11039		}
11040	}
11041
11042
11043	// Begin the iLightBox plugin
11044	$.fn.iLightBox = function() {
11045
11046		var args = arguments,
11047			opt = ($.isPlainObject(args[0])) ? args[0] : args[1],
11048			items = ($.isArray(args[0]) || typeof args[0] == 'string') ? args[0] : args[1];
11049
11050		if (!opt) opt = {};
11051
11052		// Default options. Play carefully.
11053		var options = $.extend(true, {
11054			attr: 'href',
11055			path: 'vertical',
11056			skin: 'dark',
11057			linkId: false,
11058			infinite: false,
11059			startFrom: 0,
11060			randomStart: false,
11061			keepAspectRatio: true,
11062			maxScale: 1,
11063			minScale: .2,
11064			innerToolbar: false,
11065			smartRecognition: false,
11066			mobileOptimizer: true,
11067			fullAlone: true,
11068			fullViewPort: null,
11069			fullStretchTypes: 'flash, video',
11070			overlay: {
11071				blur: true,
11072				opacity: .85
11073			},
11074			controls: {
11075				arrows: false,
11076				slideshow: false,
11077				toolbar: true,
11078				fullscreen: true,
11079				thumbnail: true,
11080				keyboard: true,
11081				mousewheel: true,
11082				swipe: true
11083			},
11084			keyboard: {
11085				left: true, // previous
11086				right: true, // next
11087				up: true, // previous
11088				down: true, // next
11089				esc: true, // close
11090				shift_enter: true // fullscreen
11091			},
11092			show: {
11093				effect: true,
11094				speed: 300,
11095				title: true
11096			},
11097			hide: {
11098				effect: true,
11099				speed: 300
11100			},
11101			caption: {
11102				start: true,
11103				show: 'mouseenter',
11104				hide: 'mouseleave'
11105			},
11106			social: {
11107				start: true,
11108				show: 'mouseenter',
11109				hide: 'mouseleave',
11110				buttons: false
11111			},
11112			styles: {
11113				pageOffsetX: 0,
11114				pageOffsetY: 0,
11115				nextOffsetX: 45,
11116				nextOffsetY: 0,
11117				nextOpacity: 1,
11118				nextScale: 1,
11119				prevOffsetX: 45,
11120				prevOffsetY: 0,
11121				prevOpacity: 1,
11122				prevScale: 1
11123			},
11124			thumbnails: {
11125				maxWidth: 120,
11126				maxHeight: 80,
11127				normalOpacity: 1,
11128				activeOpacity: .6
11129			},
11130			effects: {
11131				reposition: true,
11132				repositionSpeed: 200,
11133				switchSpeed: 500,
11134				loadedFadeSpeed: 180,
11135				fadeSpeed: 200
11136			},
11137			slideshow: {
11138				pauseTime: 5000,
11139				pauseOnHover: false,
11140				startPaused: true
11141			},
11142			text: {
11143				close: "Press Esc to close",
11144				enterFullscreen: "Enter Fullscreen (Shift+Enter)",
11145				exitFullscreen: "Exit Fullscreen (Shift+Enter)",
11146				slideShow: "Slideshow",
11147				next: "Next",
11148				previous: "Previous"
11149			},
11150			errors: {
11151				loadImage: "An error occurred when trying to load photo.",
11152				loadContents: "An error occurred when trying to load contents.",
11153				missingPlugin: "The content your are attempting to view requires the <a href='{pluginspage}' target='_blank'>{type} plugin<\/a>."
11154			},
11155			ajaxSetup: {
11156				url: '',
11157				beforeSend: function(jqXHR, settings) {},
11158				cache: false,
11159				complete: function(jqXHR, textStatus) {},
11160				crossDomain: false,
11161				error: function(jqXHR, textStatus, errorThrown) {},
11162				success: function(data, textStatus, jqXHR) {},
11163				global: true,
11164				ifModified: false,
11165				username: null,
11166				password: null,
11167				type: 'GET'
11168			},
11169			callback: {}
11170		}, opt);
11171
11172		var instant = ($.isArray(items) || typeof items == 'string') ? true : false;
11173
11174		items = $.isArray(items) ? items : new Array();
11175
11176		if (typeof args[0] == 'string') items[0] = args[0];
11177
11178		if (version_compare($.fn.jquery, '1.8', '>=')) {
11179			var iLB = new iLightBox($(this), options, items, instant);
11180			return {
11181				close: function() {
11182					iLB.closeAction();
11183				},
11184				fullscreen: function() {
11185					iLB.fullScreenAction();
11186				},
11187				moveNext: function() {
11188					iLB.moveTo('next');
11189				},
11190				movePrev: function() {
11191					iLB.moveTo('prev');
11192				},
11193				goTo: function(index) {
11194					iLB.goTo(index);
11195				},
11196				refresh: function() {
11197					iLB.refresh();
11198				},
11199				reposition: function() {
11200					(arguments.length > 0) ? iLB.repositionPhoto(true): iLB.repositionPhoto();
11201				},
11202				setOption: function(options) {
11203					iLB.setOption(options);
11204				},
11205				destroy: function() {
11206					iLB.closeAction();
11207					iLB.dispatchItemsEvents();
11208				}
11209			};
11210		} else {
11211			throw "The jQuery version that was loaded is too old. iLightBox requires jQuery 1.8+";
11212		}
11213
11214	};
11215
11216
11217	$.iLightBox = function() {
11218		return $.fn.iLightBox(arguments[0], arguments[1]);
11219	};
11220
11221
11222	$.extend($.easing, {
11223		easeInCirc: function(x, t, b, c, d) {
11224			return -c * (sqrt(1 - (t /= d) * t) - 1) + b;
11225		},
11226		easeOutCirc: function(x, t, b, c, d) {
11227			return c * sqrt(1 - (t = t / d - 1) * t) + b;
11228		},
11229		easeInOutCirc: function(x, t, b, c, d) {
11230			if ((t /= d / 2) < 1) return -c / 2 * (sqrt(1 - t * t) - 1) + b;
11231			return c / 2 * (sqrt(1 - (t -= 2) * t) + 1) + b;
11232		}
11233	});
11234
11235	function getScrollXY() {
11236		var scrOfX = 0,
11237			scrOfY = 0;
11238		if (typeof(window.pageYOffset) == 'number') {
11239			//Netscape compliant
11240			scrOfY = window.pageYOffset;
11241			scrOfX = window.pageXOffset;
11242		} else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) {
11243			//DOM compliant
11244			scrOfY = document.body.scrollTop;
11245			scrOfX = document.body.scrollLeft;
11246		} else if (document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) {
11247			//IE6 standards compliant mode
11248			scrOfY = document.documentElement.scrollTop;
11249			scrOfX = document.documentElement.scrollLeft;
11250		}
11251		return {
11252			x: scrOfX,
11253			y: scrOfY
11254		};
11255	}
11256
11257	(function() {
11258		// add new event shortcuts
11259		$.each(("touchstart touchmove touchend " +
11260			"tap taphold " +
11261			"swipe swipeleft swiperight " +
11262			"scrollstart scrollstop").split(" "), function(i, name) {
11263
11264			$.fn[name] = function(fn) {
11265				return fn ? this.bind(name, fn) : this.trigger(name);
11266			};
11267
11268			// jQuery < 1.8
11269			// if ($.attrFn) {
11270			// 	$.attrFn[name] = true;
11271			// }
11272		});
11273
11274		var tapSettings = {
11275			startEvent: 'touchstart.iTap',
11276			endEvent: 'touchend.iTap'
11277		};
11278
11279		// tap Event:
11280		$.event.special.itap = {
11281			setup: function() {
11282
11283				var self = this,
11284					$self = $(this),
11285					start, stop;
11286
11287				$self.bind(tapSettings.startEvent, function(event) {
11288					start = getScrollXY();
11289
11290					$self.one(tapSettings.endEvent, function(event) {
11291						stop = getScrollXY();
11292
11293						var orgEvent = event || window.event;
11294						event = $.event.fix(orgEvent);
11295						event.type = "itap";
11296
11297						if ((start && stop) && (start.x == stop.x && start.y == stop.y))($.event.dispatch || $.event.handle).call(self, event);
11298
11299						start = stop = undefined;
11300					});
11301				});
11302			},
11303
11304			teardown: function() {
11305				$(this).unbind(tapSettings.startEvent);
11306			}
11307		};
11308	}());
11309
11310
11311	//Fullscreen API
11312	(function() {
11313		fullScreenApi = {
11314				supportsFullScreen: false,
11315				isFullScreen: function() {
11316					return false;
11317				},
11318				requestFullScreen: function() {},
11319				cancelFullScreen: function() {},
11320				fullScreenEventName: '',
11321				prefix: ''
11322			},
11323			browserPrefixes = 'webkit moz o ms khtml'.split(' ');
11324
11325		// check for native support
11326		if (typeof document.cancelFullScreen != 'undefined') {
11327			fullScreenApi.supportsFullScreen = true;
11328		} else {
11329			// check for fullscreen support by vendor prefix
11330			for (var i = 0, il = browserPrefixes.length; i < il; i++) {
11331				fullScreenApi.prefix = browserPrefixes[i];
11332
11333				if (typeof document[fullScreenApi.prefix + 'CancelFullScreen'] != 'undefined') {
11334					fullScreenApi.supportsFullScreen = true;
11335
11336					break;
11337				}
11338			}
11339		}
11340
11341		// update methods to do something useful
11342		if (fullScreenApi.supportsFullScreen) {
11343			fullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange';
11344
11345			fullScreenApi.isFullScreen = function() {
11346				switch (this.prefix) {
11347					case '':
11348						return document.fullScreen;
11349					case 'webkit':
11350						return document.webkitIsFullScreen;
11351					default:
11352						return document[this.prefix + 'FullScreen'];
11353				}
11354			}
11355			fullScreenApi.requestFullScreen = function(el) {
11356				return (this.prefix === '') ? el.requestFullScreen() : el[this.prefix + 'RequestFullScreen']();
11357			}
11358			fullScreenApi.cancelFullScreen = function(el) {
11359				return (this.prefix === '') ? document.cancelFullScreen() : document[this.prefix + 'CancelFullScreen']();
11360			}
11361		}
11362	}());
11363
11364	// Browser detect
11365	(function() {
11366		function uaMatch(ua) {
11367			ua = ua.toLowerCase();
11368
11369			var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
11370				/(webkit)[ \/]([\w.]+)/.exec(ua) ||
11371				/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
11372				/(msie) ([\w.]+)/.exec(ua) ||
11373				ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || [];
11374
11375			return {
11376				browser: match[1] || "",
11377				version: match[2] || "0"
11378			};
11379		}
11380
11381		var matched = uaMatch(navigator.userAgent);
11382		browser = {};
11383
11384		if (matched.browser) {
11385			browser[matched.browser] = true;
11386			browser.version = matched.version;
11387		}
11388
11389		// Chrome is Webkit, but Webkit is also Safari.
11390		if (browser.chrome) {
11391			browser.webkit = true;
11392		} else if (browser.webkit) {
11393			browser.safari = true;
11394		}
11395	}());
11396
11397	// Feature detects
11398	(function() {
11399		var prefixes = ['', 'webkit', 'moz', 'ms', 'o'];
11400		var el = document.createElement('div');
11401
11402		function testProp(prop) {
11403			for (var p = 0, pl = prefixes.length; p < pl; p++) {
11404				var prefixedProp = prefixes[p] ? prefixes[p] + prop.charAt(0).toUpperCase() + prop.slice(1) : prop;
11405				if (el.style[prefixedProp] !== undefined) {
11406					return prefixedProp;
11407				}
11408			}
11409		}
11410
11411		// Global support indicators
11412		transform = testProp('transform') || '';
11413		gpuAcceleration = testProp('perspective') ? 'translateZ(0) ' : '';
11414	}());
11415
11416
11417	/*
11418		PluginDetect v0.7.9
11419		www.pinlady.net/PluginDetect/license/
11420		[ getVersion onWindowLoaded BetterIE ]
11421		[ Flash QuickTime Shockwave ]
11422	*/
11423	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;
11423d++){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);
11423d.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)}
11423c.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;
11423if(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();
11424
11425	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)};
11426
11427
11428	/*
11429		jQuery hashchange event v1.3
11430		https://github.com/cowboy/jquery-hashchange
11431		Copyright (c) 2010 "Cowboy" Ben Alman
11432		Dual licensed under the MIT and GPL licenses.
11433	*/
11434	(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=
11435	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||
11436	"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;
11436a!==d&&(b.title=k.title,b.open(),e&&b.write('<script>document.domain="'+e+'"\x3c/script>'),b.close(),c.location.hash=a)}}();return h}()})();
11437
11438	if (!Array.prototype.filter) {
11439		Array.prototype.filter = function(fun /*, thisp */ ) {
11440			"use strict";
11441
11442			if (this == null)
11443				throw new TypeError();
11444
11445			var t = Object(this);
11446			var len = t.length >>> 0;
11447			if (typeof fun != "function")
11448				throw new TypeError();
11449
11450			var res = [];
11451			var thisp = arguments[1];
11452			for (var i = 0; i < len; i++) {
11453				if (i in t) {
11454					var val = t[i]; // in case fun mutates this
11455					if (fun.call(thisp, val, i, t))
11456						res.push(val);
11457				}
11458			}
11459
11460			return res;
11461		};
11462	}
11463
11464	if (!Array.prototype.indexOf) {
11465		Array.prototype.indexOf = function(searchElement, fromIndex) {
11466			var k;
11467
11468			if (this == null) {
11469				throw new TypeError('"this" is null or not defined');
11470			}
11471
11472			var O = Object(this);
11473
11474			var len = O.length >>> 0;
11475
11476			if (len === 0) {
11477				return -1;
11478			}
11479
11480			var n = +fromIndex || 0;
11481
11482			if (abs(n) === Infinity) {
11483				n = 0;
11484			}
11485
11486			if (n >= len) {
11487				return -1;
11488			}
11489
11490			k = max(n >= 0 ? n : len - abs(n), 0);
11491
11492			while (k < len) {
11493				var kValue;
11494				if (k in O && O[k] === searchElement) {
11495					return k;
11496				}
11497				k++;
11498			}
11499			return -1;
11500		};
11501	}
11502
11503	if (!Array.prototype.lastIndexOf) {
11504		Array.prototype.lastIndexOf = function(searchElement /*, fromIndex*/ ) {
11505			"use strict";
11506
11507			if (this == null)
11508				throw new TypeError();
11509
11510			var t = Object(this);
11511			var len = t.length >>> 0;
11512			if (len === 0)
11513				return -1;
11514
11515			var n = len;
11516			if (arguments.length > 1) {
11517				n = Number(arguments[1]);
11518				if (n != n)
11519					n = 0;
11520				else if (n != 0 && n != (1 / 0) && n != -(1 / 0))
11521					n = (n > 0 || -1) * floor(abs(n));
11522			}
11523
11524			var k = n >= 0 ? min(n, len - 1) : len - abs(n);
11525
11526			for (; k >= 0; k--) {
11527				if (k in t && t[k] === searchElement)
11528					return k;
11529			}
11530			return -1;
11531		};
11532	}
11533})(jQuery, this);
11534
11535/**
11536 * Owl carousel
vendor: 12,108 bytes, lines 11537-12043
11537 * @version 2.0.0-beta.3
11538 * @author Bartosz Wojciechowski
11539 * @license The MIT License (MIT)
11540 * @todo Lazy Load Icon
11541 * @todo prevent animationend bubling
11542 * @todo itemsScaleUp
11543 * @todo Test Zepto
11544 * @todo stagePadding calculate wrong active classes
11545 */
11546;(function($, window, document, undefined) {
11547
11548	/**
11549	 * Creates a carousel.
11550	 * @class The Owl Carousel.
11551	 * @public
11552	 * @param {HTMLElement|jQuery} element - The element to create the carousel for.
11553	 * @param {Object} [options] - The options
11554	 */
11555	function Owl(element, options) {
11556
11557		/**
11558		 * Current settings for the carousel.
11559		 * @public
11560		 */
11561		this.settings = null;
11562
11563		/**
11564		 * Current options set by the caller including defaults.
11565		 * @public
11566		 */
11567		this.options = $.extend({}, Owl.Defaults, options);
11568
11569		/**
11570		 * Plugin element.
11571		 * @public
11572		 */
11573		this.$element = $(element);
11574
11575		/**
11576		 * Proxied event handlers.
11577		 * @protected
11578		 */
11579		this._handlers = {};
11580
11581		/**
11582		 * References to the running plugins of this carousel.
11583		 * @protected
11584		 */
11585		this._plugins = {};
11586
11587		/**
11588		 * Currently suppressed events to prevent them from beeing retriggered.
11589		 * @protected
11590		 */
11591		this._supress = {};
11592
11593		/**
11594		 * Absolute current position.
11595		 * @protected
11596		 */
11597		this._current = null;
11598
11599		/**
11600		 * Animation speed in milliseconds.
11601		 * @protected
11602		 */
11603		this._speed = null;
11604
11605		/**
11606		 * Coordinates of all items in pixel.
11607		 * @todo The name of this member is missleading.
11608		 * @protected
11609		 */
11610		this._coordinates = [];
11611
11612		/**
11613		 * Current breakpoint.
11614		 * @todo Real media queries would be nice.
11615		 * @protected
11616		 */
11617		this._breakpoint = null;
11618
11619		/**
11620		 * Current width of the plugin element.
11621		 */
11622		this._width = null;
11623
11624		/**
11625		 * All real items.
11626		 * @protected
11627		 */
11628		this._items = [];
11629
11630		/**
11631		 * All cloned items.
11632		 * @protected
11633		 */
11634		this._clones = [];
11635
11636		/**
11637		 * Merge values of all items.
11638		 * @todo Maybe this could be part of a plugin.
11639		 * @protected
11640		 */
11641		this._mergers = [];
11642
11643		/**
11644		 * Widths of all items.
11645		 */
11646		this._widths = [];
11647
11648		/**
11649		 * Invalidated parts within the update process.
11650		 * @protected
11651		 */
11652		this._invalidated = {};
11653
11654		/**
11655		 * Ordered list of workers for the update process.
11656		 * @protected
11657		 */
11658		this._pipe = [];
11659
11660		/**
11661		 * Current state information for the drag operation.
11662		 * @todo #261
11663		 * @protected
11664		 */
11665		this._drag = {
11666			time: null,
11667			target: null,
11668			pointer: null,
11669			stage: {
11670				start: null,
11671				current: null
11672			},
11673			direction: null
11674		};
11675
11676		/**
11677		 * Current state information and their tags.
11678		 * @type {Object}
11679		 * @protected
11680		 */
11681		this._states = {
11682			current: {},
11683			tags: {
11684				'initializing': [ 'busy' ],
11685				'animating': [ 'busy' ],
11686				'dragging': [ 'interacting' ]
11687			}
11688		};
11689
11690		$.each([ 'onResize', 'onThrottledResize' ], $.proxy(function(i, handler) {
11691			this._handlers[handler] = $.proxy(this[handler], this);
11692		}, this));
11693
11694		$.each(Owl.Plugins, $.proxy(function(key, plugin) {
11695			this._plugins[key.charAt(0).toLowerCase() + key.slice(1)]
11696				= new plugin(this);
11697		}, this));
11698
11699		$.each(Owl.Workers, $.proxy(function(priority, worker) {
11700			this._pipe.push({
11701				'filter': worker.filter,
11702				'run': $.proxy(worker.run, this)
11703			});
11704		}, this));
11705
11706		this.setup();
11707		this.initialize();
11708	}
11709
11710	/**
11711	 * Default options for the carousel.
11712	 * @public
11713	 */
11714	Owl.Defaults = {
11715		items: 3,
11716		loop: false,
11717		center: false,
11718		rewind: false,
11719
11720		mouseDrag: true,
11721		touchDrag: true,
11722		pullDrag: true,
11723		freeDrag: false,
11724
11725		margin: 0,
11726		stagePadding: 0,
11727
11728		merge: false,
11729		mergeFit: true,
11730		autoWidth: false,
11731
11732		startPosition: 0,
11733		rtl: false,
11734
11735		smartSpeed: 250,
11736		fluidSpeed: false,
11737		dragEndSpeed: false,
11738
11739		responsive: {},
11740		responsiveRefreshRate: 200,
11741		responsiveBaseElement: window,
11742
11743		fallbackEasing: 'swing',
11744
11745		info: false,
11746
11747		nestedItemSelector: false,
11748		itemElement: 'div',
11749		stageElement: 'div',
11750
11751		refreshClass: 'owl-refresh',
11752		loadedClass: 'owl-loaded',
11753		loadingClass: 'owl-loading',
11754		rtlClass: 'owl-rtl',
11755		responsiveClass: 'owl-responsive',
11756		dragClass: 'owl-drag',
11757		itemClass: 'owl-item',
11758		stageClass: 'owl-stage',
11759		stageOuterClass: 'owl-stage-outer',
11760		grabClass: 'owl-grab'
11761	};
11762
11763	/**
11764	 * Enumeration for width.
11765	 * @public
11766	 * @readonly
11767	 * @enum {String}
11768	 */
11769	Owl.Width = {
11770		Default: 'default',
11771		Inner: 'inner',
11772		Outer: 'outer'
11773	};
11774
11775	/**
11776	 * Enumeration for types.
11777	 * @public
11778	 * @readonly
11779	 * @enum {String}
11780	 */
11781	Owl.Type = {
11782		Event: 'event',
11783		State: 'state'
11784	};
11785
11786	/**
11787	 * Contains all registered plugins.
11788	 * @public
11789	 */
11790	Owl.Plugins = {};
11791
11792	/**
11793	 * List of workers involved in the update process.
11794	 */
11795	Owl.Workers = [ {
11796		filter: [ 'width', 'settings' ],
11797		run: function() {
11798			this._width = (this.$element.closest('.px-gutter').length) ? 12 * Math.ceil(this.$element.width() / 12) : this.$element.width();
11799			//this._width = (UNCODE.isMobile) ? this.$element.width() : 12 * Math.ceil(this.$element.width() / 12);
11800		}
11801	}, {
11802		filter: [ 'width', 'items', 'settings' ],
11803		run: function(cache) {
11804			cache.current = this._items && this._items[this.relative(this._current)];
11805		}
11806	}, {
11807		filter: [ 'items', 'settings' ],
11808		run: function() {
11809			this.$stage.children('.cloned').remove();
11810		}
11811	}, {
11812		filter: [ 'width', 'items', 'settings' ],
11813		run: function(cache) {
11814			var margin = this.settings.margin || '',
11815				grid = !this.settings.autoWidth,
11816				rtl = this.settings.rtl,
11817				css = {
11818					'width': 'auto',
11819					'margin-left': rtl ? margin : '',
11820					'margin-right': rtl ? '' : margin
11821				};
11822
11823			!grid && this.$stage.children().css(css);
11824
11825			cache.css = css;
11826		}
11827	}, {
11828		filter: [ 'width', 'items', 'settings' ],
11829		run: function(cache) {
11830			//UNCODE edit to fix Chrome stage padding issue
11831			var width = Math.round((this.width() / this.settings.items).toFixed(3) - this.settings.margin),
11832				merge = null,
11833				iterator = this._items.length,
11834				grid = !this.settings.autoWidth,
11835				widths = [];
11836
11837			cache.items = {
11838				merge: false,
11839				width: width
11840			};
11841
11842			while (iterator--) {
11843				merge = this._mergers[iterator];
11844				merge = this.settings.mergeFit && Math.min(merge, this.settings.items) || merge;
11845
11846				cache.items.merge = merge > 1 || cache.items.merge;
11847
11848				widths[iterator] = !grid ? this._items[iterator].width() : width * merge;
11849			}
11850
11851			this._widths = widths;
11852		}
11853	}, {
11854		filter: [ 'items', 'settings' ],
11855		run: function() {
11856			var clones = [],
11857				items = this._items,
11858				settings = this.settings,
11859				view = Math.max(settings.items * 2, 4),
11860				size = Math.ceil(items.length / 2) * 2,
11861				repeat = settings.loop && items.length ? settings.rewind ? view : Math.max(view, size) : 0,
11862				append = '',
11863				prepend = '';
11864
11865			repeat /= 2;
11866
11867			while (repeat--) {
11868				clones.push(this.normalize(clones.length / 2, true));
11869				append = append + items[clones[clones.length - 1]][0].outerHTML;
11870				clones.push(this.normalize(items.length - 1 - (clones.length - 1) / 2, true));
11871				prepend = items[clones[clones.length - 1]][0].outerHTML + prepend;
11872			}
11873
11874			this._clones = clones;
11875
11876			// var appendVideo = $(append).find('.uncode-video-container');
11877			// if (appendVideo.length) {
11878			// 	appendVideo.attr('data-id', Math.round(Math.random() * 100000));
11879			// }
11880			// var prependVideo = $(prepend).find('.uncode-video-container');
11881			// if (prependVideo.length) {
11882			// 	prependVideo.attr('data-id', Math.round(Math.random() * 100000));
11883			// }
11884			$(append).addClass('cloned').appendTo(this.$stage);
11885			$(prepend).addClass('cloned').prependTo(this.$stage);
11886		}
11887	}, {
11888		filter: [ 'width', 'items', 'settings' ],
11889		run: function() {
11890			var rtl = this.settings.rtl ? 1 : -1,
11891				size = this._clones.length + this._items.length,
11892				iterator = -1,
11893				previous = 0,
11894				current = 0,
11895				coordinates = [];
11896
11897			while (++iterator < size) {
11898				previous = coordinates[iterator - 1] || 0;
11899				current = this._widths[this.relative(iterator)] + this.settings.margin;
11900				coordinates.push(previous + current * rtl);
11901			}
11902
11903			this._coordinates = coordinates;
11904		}
11905	}, {
11906		filter: [ 'width', 'items', 'settings' ],
11907		run: function() {
11908			var stagePadding = (this._width < 480 && this.settings.stagePadding > 0) ? 41 : (this._width * this.settings.stagePadding) / 200,
11909				padding = stagePadding,
11910				coordinates = this._coordinates,
11911				css = {
11912					'width': Math.ceil(Math.abs(coordinates[coordinates.length - 1])) + padding * 2,
11913					'padding-left': padding || '',
11914					'padding-right': padding || ''
11915				};
11916
11917			this.$stage.css(css);
11918		}
11919	}, {
11920		filter: [ 'width', 'items', 'settings' ],
11921		run: function(cache) {
11922			var iterator = this._coordinates.length,
11923				grid = !this.settings.autoWidth,
11924				items = this.$stage.children();
11925
11926			if (grid && cache.items.merge) {
11927				while (iterator--) {
11928					cache.css.width = this._widths[this.relative(iterator)];
11929					items.eq(iterator).css(cache.css);
11930				}
11931			} else if (grid) {
11932				cache.css.width = cache.items.width;
11933				items.css(cache.css);
11934			}
11935		}
11936	}, {
11937		filter: [ 'items' ],
11938		run: function() {
11939			this._coordinates.length < 1 && this.$stage.removeAttr('style');
11940		}
11941	}, {
11942		filter: [ 'width', 'items', 'settings' ],
11943		run: function(cache) {
11944			cache.current = cache.current ? this.$stage.children().index(cache.current) : 0;
11945			cache.current = Math.max(this.minimum(), Math.min(this.maximum(), cache.current));
11946			this.reset(cache.current);
11947		}
11948	}, {
11949		filter: [ 'position' ],
11950		run: function() {
11951			this.animate(this.coordinates(this._current));
11952		}
11953	}, {
11954		filter: [ 'width', 'position', 'items', 'settings' ],
11955		run: function() {
11956			var stagePadding = (this._width < 480 && this.settings.stagePadding > 0) ? 41 : (this._width * this.settings.stagePadding) / 200,
11957				rtl = this.settings.rtl ? 1 : -1,
11958				padding = this.settings.stagePadding * 2,
11959				begin = this.coordinates(this.current()) + padding,
11960				end = begin + this.width() * rtl,
11961				inner, outer, matches = [], i, n;
11962
11963			for (i = 0, n = this._coordinates.length; i < n; i++) {
11964				inner = this._coordinates[i - 1] || 0;
11965				outer = Math.abs(this._coordinates[i]) + padding * rtl;
11966
11967				if ((this.op(inner, '<=', begin) && (this.op(inner, '>', end)))
11968					|| (this.op(outer, '<', begin) && this.op(outer, '>', end))) {
11969					matches.push(i);
11970				}
11971			}
11972
11973			this.$stage.children('.active').removeClass('active');
11974			this.$stage.children(':eq(' + matches.join('), :eq(') + ')').addClass('active');
11975
11976			if (this.settings.center) {
11977				this.$stage.children('.center').removeClass('center');
11978				this.$stage.children().eq(this.current()).addClass('center');
11979			}
11980		}
11981	} ];
11982
11983	/**
11984	 * Initializes the carousel.
11985	 * @protected
11986	 */
11987	Owl.prototype.initialize = function() {
11988		this.enter('initializing');
11989		this.trigger('initialize');
11990
11991		this.$element.toggleClass(this.settings.rtlClass, this.settings.rtl);
11992
11993		if (this.settings.autoWidth && !this.is('pre-loading')) {
11994			var imgs, nestedSelector, width;
11995			imgs = this.$element.find('img');
11996			nestedSelector = this.settings.nestedItemSelector ? '.' + this.settings.nestedItemSelector : undefined;
11997			width = this.$element.children(nestedSelector).width();
11998
11999			if (imgs.length && width <= 0) {
12000				this.preloadAutoWidthImages(imgs);
12001			}
12002		}
12003
12004		this.$element.addClass(this.options.loadingClass);
12005
12006		// create stage
12007		this.$stage = $('<' + this.settings.stageElement + ' class="' + this.settings.stageClass + '"/>')
12008			.wrap('<div class="' + this.settings.stageOuterClass + '"/>');
12009
12010		// append stage
12011		this.$element.append(this.$stage.parent());
12012
12013		// append content
12014		this.replace(this.$element.children().not(this.$stage.parent()));
12015
12016		// check visibility
12017		if (this.$element.is(':visible')) {
12018			// update view
12019			this.refresh();
12020		} else {
12021			// invalidate width
12022			this.invalidate('width');
12023		}
12024
12025		this.$element
12026			.removeClass(this.options.loadingClass)
12027			.addClass(this.options.loadedClass);
12028
12029		// register event handlers
12030		this.registerEventHandlers();
12031
12032		this.leave('initializing');
12033		this.trigger('initialized');
12034	};
12035
12036	/**
12037	 * Setups the current settings.
12038	 * @todo Remove responsive classes. Why should adaptive designs be brought into IE8?
12039	 * @todo Support for media queries by using `matchMedia` would be nice.
12040	 * @public
12041	 */
12042	Owl.prototype.setup = function() {
12043		var viewport = this.viewport(),
12044			overwrites = this.options.responsive,
12045			match = -1,
12046			settings = null;
12047
12048		if (!overwrites) {
12049			settings = $.extend({}, this.options);
12050		} else {
12051			$.each(overwrites, function(breakpoint) {
12052				if (breakpoint <= viewport && breakpoint > match) {
12053					match = Number(breakpoint);
12054				}
12055			});
12056
12057			settings = $.extend({}, this.options, overwrites[match]);
12058			delete settings.responsive;
12059
12060			// responsive class
12061			if (settings.responsiveClass) {
12062				this.$element.attr('class',
12063					this.$element.attr('class').replace(new RegExp('(' + this.options.responsiveClass + '-)\\S+\\s', 'g'), '$1' + match)
12064				);
12065			}
12066		}
12067
12068		if (this.settings === null || this._breakpoint !== match) {
12069			this.trigger('change', { property: { name: 'settings', value: settings } });
12070			this._breakpoint = match;
12071			this.settings = settings;
12072			this.invalidate('settings');
12073			this.trigger('changed', { property: { name: 'settings', value: this.settings } });
12074		}
12075	};
12076
12077	/**
12078	 * Updates option logic if necessery.
12079	 * @protected
12080	 */
12081	Owl.prototype.optionsLogic = function() {
12082		if (this.settings.autoWidth) {
12083			this.settings.stagePadding = false;
12084			this.settings.merge = false;
12085		}
12086	};
12087
12088	/**
12089	 * Prepares an item before add.
12090	 * @todo Rename event parameter `content` to `item`.
12091	 * @protected
12092	 * @returns {jQuery|HTMLElement} - The item container.
12093	 */
12094	Owl.prototype.prepare = function(item, index) {
12095		var event = this.trigger('prepare', { content: item });
12096		if (!event.data) {
12097			event.data = $('<' + this.settings.itemElement + '/>')
12098				.addClass(this.options.itemClass).attr('data-index', index + 1).append(item)
12099		}
12100
12101		this.trigger('prepared', { content: event.data });
12102
12103		return event.data;
12104	};
12105
12106	/**
12107	 * Updates the view.
12108	 * @public
12109	 */
12110	Owl.prototype.update = function() {
12111		var i = 0,
12112			n = this._pipe.length,
12113			filter = $.proxy(function(p) { return this[p] }, this._invalidated),
12114			cache = {};
12115
12116		while (i < n) {
12117			if (this._invalidated.all || $.grep(this._pipe[i].filter, filter).length > 0) {
12118				this._pipe[i].run(cache);
12119			}
12120			i++;
12121		}
12122
12123		this._invalidated = {};
12124
12125		!this.is('valid') && this.enter('valid');
12126	};
12127
12128	/**
12129	 * Gets the width of the view.
12130	 * @public
12131	 * @param {Owl.Width} [dimension=Owl.Width.Default] - The dimension to return.
12132	 * @returns {Number} - The width of the view in pixel.
12133	 */
12134	Owl.prototype.width = function(dimension) {
12135		dimension = dimension || Owl.Width.Default;
12136		var stagePadding = (this._width < 480 && this.settings.stagePadding > 0) ? 41 : (this._width * this.settings.stagePadding) / 200;
12137		switch (dimension) {
12138			case Owl.Width.Inner:
12139			case Owl.Width.Outer:
12140				return this._width;
12141			default:
12142				return this._width - stagePadding * 2 + this.settings.margin;
12143		}
12144	};
12145
12146	/**
12147	 * Refreshes the carousel primarily for adaptive purposes.
12148	 * @public
12149	 */
12150	Owl.prototype.refresh = function() {
12151		this.enter('refreshing');
12152		this.trigger('refresh');
12153
12154		this.setup();
12155
12156		this.optionsLogic();
12157
12158		this.$element.addClass(this.options.refreshClass);
12159
12160		this.update();
12161
12162		this.$element.removeClass(this.options.refreshClass);
12163
12164		this.leave('refreshing');
12165		this.trigger('refreshed');
12166	};
12167
12168	/**
12169	 * Checks window `resize` event.
12170	 * @protected
12171	 */
12172	Owl.prototype.onThrottledResize = function() {
12173		window.clearTimeout(this.resizeTimer);
12174		this.resizeTimer = window.setTimeout(this._handlers.onResize, this.settings.responsiveRefreshRate);
12175	};
12176
12177	/**
12178	 * Checks window `resize` event.
12179	 * @protected
12180	 */
12181	Owl.prototype.onResize = function() {
12182		if (!this._items.length) {
12183			return false;
12184		}
12185
12186		if (this._width === this.$element.width()) {
12187			return false;
12188		}
12189
12190		if (!this.$element.is(':visible')) {
12191			return false;
12192		}
12193
12194		this.enter('resizing');
12195
12196		if (this.trigger('resize').isDefaultPrevented()) {
12197			this.leave('resizing');
12198			return false;
12199		}
12200
12201		this.invalidate('width');
12202
12203		this.refresh();
12204
12205		this.leave('resizing');
12206		this.trigger('resized');
12207	};
12208
12209	/**
12210	 * Registers event handlers.
12211	 * @todo Check `msPointerEnabled`
12212	 * @todo #261
12213	 * @protected
12214	 */
12215	Owl.prototype.registerEventHandlers = function() {
12216		if ($.support.transition) {
12217			this.$stage.on($.support.transition.end + '.owl.core', $.proxy(this.onTransitionEnd, this));
12218		}
12219
12220		if (this.settings.responsive !== false) {
12221			this.on(window, 'resize', this._handlers.onThrottledResize);
12222		}
12223
12224		if (this.settings.mouseDrag) {
12225			this.$element.addClass(this.options.dragClass);
12226			this.$stage.on('mousedown.owl.core', $.proxy(this.onDragStart, this));
12227			this.$stage.on('dragstart.owl.core selectstart.owl.core', function() { return false });
12228		}
12229
12230		if (this.settings.touchDrag){
12231			this.$stage.on('touchstart.owl.core', $.proxy(this.onDragStart, this));
12232			this.$stage.on('touchcancel.owl.core', $.proxy(this.onDragEnd, this));
12233		}
12234	};
12235
12236	/**
12237	 * Handles `touchstart` and `mousedown` events.
12238	 * @todo Horizontal swipe threshold as option
12239	 * @todo #261
12240	 * @protected
12241	 * @param {Event} event - The event arguments.
12242	 */
12243	Owl.prototype.onDragStart = function(event) {
12244		var stage = null;
12245
12246		if (event.which === 3) {
12247			return;
12248		}
12249
vendor: 9,106 bytes, lines 12250-12565
12250		if ($.support.transform) {
12251			stage = this.$stage.css('transform').replace(/.*\(|\)| /g, '').split(',');
12252			stage = {
12253				x: stage[stage.length === 16 ? 12 : 4],
12254				y: stage[stage.length === 16 ? 13 : 5]
12255			};
12256		} else {
12257			stage = this.$stage.position();
12258			stage = {
12259				x: this.settings.rtl ?
12260					stage.left + this.$stage.width() - this.width() + this.settings.margin :
12261					stage.left,
12262				y: stage.top
12263			};
12264		}
12265
12266		if (this.is('animating')) {
12267			$.support.transform ? this.animate(stage.x) : this.$stage.stop()
12268			this.invalidate('position');
12269		}
12270
12271		this.$element.toggleClass(this.options.grabClass, event.type === 'mousedown');
12272
12273		this.speed(0);
12274
12275		this._drag.time = new Date().getTime();
12276		this._drag.target = $(event.target);
12277		this._drag.stage.start = stage;
12278		this._drag.stage.current = stage;
12279		this._drag.pointer = this.pointer(event);
12280
12281		$(document).on('mouseup.owl.core touchend.owl.core', $.proxy(this.onDragEnd, this));
12282
12283		$(document).one('mousemove.owl.core touchmove.owl.core', $.proxy(function(event) {
12284			var delta = this.difference(this._drag.pointer, this.pointer(event));
12285
12286			$(document).on('mousemove.owl.core touchmove.owl.core', $.proxy(this.onDragMove, this));
12287
12288			if (Math.abs(delta.x) < Math.abs(delta.y) && this.is('valid')) {
12289				return;
12290			}
12291
12292			event.preventDefault();
12293
12294			this.enter('dragging');
12295			this.trigger('drag');
12296		}, this));
12297	};
12298
12299	/**
12300	 * Handles the `touchmove` and `mousemove` events.
12301	 * @todo #261
12302	 * @protected
12303	 * @param {Event} event - The event arguments.
12304	 */
12305	Owl.prototype.onDragMove = function(event) {
12306		var minimum = null,
12307			maximum = null,
12308			pull = null,
12309			delta = this.difference(this._drag.pointer, this.pointer(event)),
12310			stage = this.difference(this._drag.stage.start, delta);
12311
12312		if (!this.is('dragging')) {
12313			return;
12314		}
12315
12316		event.preventDefault();
12317
12318		if (this.settings.loop) {
12319			minimum = this.coordinates(this.minimum());
12320			maximum = this.coordinates(this.maximum() + 1) - minimum;
12321			stage.x = (((stage.x - minimum) % maximum + maximum) % maximum) + minimum;
12322		} else {
12323			minimum = this.settings.rtl ? this.coordinates(this.maximum()) : this.coordinates(this.minimum());
12324			maximum = this.settings.rtl ? this.coordinates(this.minimum()) : this.coordinates(this.maximum());
12325			pull = this.settings.pullDrag ? -1 * delta.x / 5 : 0;
12326			stage.x = Math.max(Math.min(stage.x, minimum + pull), maximum + pull);
12327		}
12328
12329		this._drag.stage.current = stage;
12330
12331		this.animate(stage.x);
12332	};
12333
12334	/**
12335	 * Handles the `touchend` and `mouseup` events.
12336	 * @todo #261
12337	 * @todo Threshold for click event
12338	 * @protected
12339	 * @param {Event} event - The event arguments.
12340	 */
12341	Owl.prototype.onDragEnd = function(event) {
12342		var delta = this.difference(this._drag.pointer, this.pointer(event)),
12343			stage = this._drag.stage.current,
12344			direction = delta.x > 0 ^ this.settings.rtl ? 'left' : 'right';
12345
12346		$(document).off('.owl.core');
12347
12348		this.$element.removeClass(this.options.grabClass);
12349
12350		if (delta.x !== 0 && this.is('dragging') || !this.is('valid')) {
12351			this.speed(this.settings.dragEndSpeed || this.settings.smartSpeed);
12352			this.current(this.closest(stage.x, delta.x !== 0 ? direction : this._drag.direction));
12353			this.invalidate('position');
12354			this.update();
12355
12356			this._drag.direction = direction;
12357
12358			if (Math.abs(delta.x) > 3 || new Date().getTime() - this._drag.time > 300) {
12359				this._drag.target.one('click.owl.core', function() { return false; });
12360			}
12361		}
12362
12363		if (!this.is('dragging')) {
12364			return;
12365		}
12366
12367		this.leave('dragging');
12368		this.trigger('dragged');
12369	};
12370
12371	/**
12372	 * Gets absolute position of the closest item for a coordinate.
12373	 * @todo Setting `freeDrag` makes `closest` not reusable. See #165.
12374	 * @protected
12375	 * @param {Number} coordinate - The coordinate in pixel.
12376	 * @param {String} direction - The direction to check for the closest item. Ether `left` or `right`.
12377	 * @return {Number} - The absolute position of the closest item.
12378	 */
12379	Owl.prototype.closest = function(coordinate, direction) {
12380		var position = -1,
12381			pull = 30,
12382			width = this.width(),
12383			coordinates = this.coordinates();
12384
12385		if (!this.settings.freeDrag) {
12386			// check closest item
12387			$.each(coordinates, $.proxy(function(index, value) {
12388				if (coordinate > value - pull && coordinate < value + pull) {
12389					position = index;
12390				} else if (this.op(coordinate, '<', value)
12391					&& this.op(coordinate, '>', coordinates[index + 1] || value - width)) {
12392					position = direction === 'left' ? index + 1 : index;
12393				}
12394				return position === -1;
12395			}, this));
12396		}
12397
12398		if (!this.settings.loop) {
12399			// non loop boundries
12400			if (this.op(coordinate, '>', coordinates[this.minimum()])) {
12401				position = coordinate = this.minimum();
12402			} else if (this.op(coordinate, '<', coordinates[this.maximum()])) {
12403				position = coordinate = this.maximum();
12404			}
12405		}
12406
12407		return position;
12408	};
12409
12410	/**
12411	 * Animates the stage.
12412	 * @todo #270
12413	 * @public
12414	 * @param {Number} coordinate - The coordinate in pixels.
12415	 */
12416	Owl.prototype.animate = function(coordinate) {
12417		var animate = this.speed() > 0;
12418
12419		this.is('animating') && this.onTransitionEnd();
12420
12421		if (animate) {
12422			this.enter('animating');
12423			this.trigger('translate');
12424		}
12425
12426		if ($.support.transform3d && $.support.transition) {
12427			this.$stage.css({
12428				transform: 'translate3d(' + coordinate + 'px,0px,0px)',
12429				transition: (this.speed() / 1000) + 's'
12430			});
12431		} else if (animate) {
12432			this.$stage.animate({
12433				left: coordinate + 'px'
12434			}, this.speed(), this.settings.fallbackEasing, $.proxy(this.onTransitionEnd, this));
12435		} else {
12436			this.$stage.css({
12437				left: coordinate + 'px'
12438			});
12439		}
12440	};
12441
12442	/**
12443	 * Checks whether the carousel is in a specific state or not.
12444	 * @param {String} state - The state to check.
12445	 * @returns {Boolean} - The flag which indicates if the carousel is busy.
12446	 */
12447	Owl.prototype.is = function(state) {
12448		return this._states.current[state] && this._states.current[state] > 0;
12449	};
12450
12451	/**
12452	 * Sets the absolute position of the current item.
12453	 * @public
12454	 * @param {Number} [position] - The new absolute position or nothing to leave it unchanged.
12455	 * @returns {Number} - The absolute position of the current item.
12456	 */
12457	Owl.prototype.current = function(position) {
12458		if (position === undefined) {
12459			return this._current;
12460		}
12461
12462		if (this._items.length === 0) {
12463			return undefined;
12464		}
12465
12466		position = this.normalize(position);
12467
12468		if (this._current !== position) {
12469			var event = this.trigger('change', { property: { name: 'position', value: position } });
12470
12471			if (event.data !== undefined) {
12472				position = this.normalize(event.data);
12473			}
12474
12475			this._current = position;
12476
12477			this.invalidate('position');
12478
12479			this.trigger('changed', { property: { name: 'position', value: this._current } });
12480		}
12481
12482		return this._current;
12483	};
12484
12485	/**
12486	 * Invalidates the given part of the update routine.
12487	 * @param {String} [part] - The part to invalidate.
12488	 * @returns {Array.<String>} - The invalidated parts.
12489	 */
12490	Owl.prototype.invalidate = function(part) {
12491		if ($.type(part) === 'string') {
12492			this._invalidated[part] = true;
12493			this.is('valid') && this.leave('valid');
12494		}
12495		return $.map(this._invalidated, function(v, i) { return i });
12496	};
12497
12498	/**
12499	 * Resets the absolute position of the current item.
12500	 * @public
12501	 * @param {Number} position - The absolute position of the new item.
12502	 */
12503	Owl.prototype.reset = function(position) {
12504		position = this.normalize(position);
12505
12506		if (position === undefined) {
12507			return;
12508		}
12509
12510		this._speed = 0;
12511		this._current = position;
12512
12513		this.suppress([ 'translate', 'translated' ]);
12514
12515		this.animate(this.coordinates(position));
12516
12517		this.release([ 'translate', 'translated' ]);
12518	};
12519
12520	/**
12521	 * Normalizes an absolute or a relative position of an item.
12522	 * @public
12523	 * @param {Number} position - The absolute or relative position to normalize.
12524	 * @param {Boolean} [relative=false] - Whether the given position is relative or not.
12525	 * @returns {Number} - The normalized position.
12526	 */
12527	Owl.prototype.normalize = function(position, relative) {
12528		var n = this._items.length,
12529			m = relative ? 0 : this._clones.length;
12530
12531		if (!$.isNumeric(position) || n < 1) {
12532			position = undefined;
12533		} else if (position < 0 || position >= n + m) {
12534			position = ((position - m / 2) % n + n) % n + m / 2;
12535		}
12536
12537		return position;
12538	};
12539
12540	/**
12541	 * Converts an absolute position of an item into a relative one.
12542	 * @public
12543	 * @param {Number} position - The absolute position to convert.
12544	 * @returns {Number} - The converted position.
12545	 */
12546	Owl.prototype.relative = function(position) {
12547		position -= this._clones.length / 2;
12548		return this.normalize(position, true);
12549	};
12550
12551	/**
12552	 * Gets the maximum position for the current item.
12553	 * @public
12554	 * @param {Boolean} [relative=false] - Whether to return an absolute position or a relative position.
12555	 * @returns {Number}
12556	 */
12557	Owl.prototype.maximum = function(relative) {
12558		var settings = this.settings,
12559			maximum = this._coordinates.length,
12560			boundary = Math.abs(this._coordinates[maximum - 1]) - this._width,
12561			i = -1, j;
12562
12563		if (settings.loop) {
12564			maximum = this._clones.length / 2 + this._items.length - 1;
12565		} else if (settings.autoWidth || settings.merge) {
12566			// binary search
12567			while (maximum - i > 1) {
12568				Math.abs(this._coordinates[j = maximum + i >> 1]) < boundary
12569					? i = j : maximum = j;
12570			}
12571		} else if (settings.center) {
12572			maximum = this._items.length - 1;
12573		} else {
12574			maximum = this._items.length - settings.items;
12575		}
12576
12577		if (relative) {
12578			maximum -= this._clones.length / 2;
12579		}
12580
12581		return Math.max(maximum, 0);
12582	};
12583
12584	/**
12585	 * Gets the minimum position for the current item.
12586	 * @public
12587	 * @param {Boolean} [relative=false] - Whether to return an absolute position or a relative position.
12588	 * @returns {Number}
12589	 */
12590	Owl.prototype.minimum = function(relative) {
12591		return relative ? 0 : this._clones.length / 2;
12592	};
12593
12594	/**
12595	 * Gets an item at the specified relative position.
12596	 * @public
12597	 * @param {Number} [position] - The relative position of the item.
12598	 * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given.
12599	 */
12600	Owl.prototype.items = function(position) {
12601		if (position === undefined) {
12602			return this._items.slice();
12603		}
12604
12605		position = this.normalize(position, true);
12606		return this._items[position];
12607	};
12608
12609	/**
12610	 * Gets an item at the specified relative position.
12611	 * @public
12612	 * @param {Number} [position] - The relative position of the item.
12613	 * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given.
12614	 */
12615	Owl.prototype.mergers = function(position) {
12616		if (position === undefined) {
12617			return this._mergers.slice();
12618		}
12619
12620		position = this.normalize(position, true);
12621		return this._mergers[position];
12622	};
12623
12624	/**
12625	 * Gets the absolute positions of clones for an item.
12626	 * @public
12627	 * @param {Number} [position] - The relative position of the item.
12628	 * @returns {Array.<Number>} - The absolute positions of clones for the item or all if no position was given.
12629	 */
12630	Owl.prototype.clones = function(position) {
12631		var odd = this._clones.length / 2,
12632			even = odd + this._items.length,
12633			map = function(index) { return index % 2 === 0 ? even + index / 2 : odd - (index + 1) / 2 };
12634
12635		if (position === undefined) {
12636			return $.map(this._clones, function(v, i) { return map(i) });
12637		}
12638
12639		return $.map(this._clones, function(v, i) { return v === position ? map(i) : null });
12640	};
12641
12642	/**
12643	 * Sets the current animation speed.
12644	 * @public
12645	 * @param {Number} [speed] - The animation speed in milliseconds or nothing to leave it unchanged.
12646	 * @returns {Number} - The current animation speed in milliseconds.
12647	 */
12648	Owl.prototype.speed = function(speed) {
12649		if (speed !== undefined) {
12650			this._speed = speed;
12651		}
12652
12653		return this._speed;
12654	};
12655
12656	/**
12657	 * Gets the coordinate of an item.
12658	 * @todo The name of this method is missleanding.
12659	 * @public
12660	 * @param {Number} position - The absolute position of the item within `minimum()` and `maximum()`.
12661	 * @returns {Number|Array.<Number>} - The coordinate of the item in pixel or all coordinates.
12662	 */
12663	Owl.prototype.coordinates = function(position) {
12664		var coordinate = null;
12665
12666		if (position === undefined) {
12667			return $.map(this._coordinates, $.proxy(function(coordinate, index) {
12668				return this.coordinates(index);
12669			}, this));
12670		}
12671
12672		if (this.settings.center) {
12673			coordinate = this._coordinates[position];
12674			coordinate += (this.width() - coordinate + (this._coordinates[position - 1] || 0)) / 2 * (this.settings.rtl ? -1 : 1);
12675		} else {
12676			coordinate = this._coordinates[position - 1] || 0;
12677		}
12678
12679		return coordinate;
12680	};
12681
12682	/**
12683	 * Calculates the speed for a translation.
12684	 * @protected
12685	 * @param {Number} from - The absolute position of the start item.
12686	 * @param {Number} to - The absolute position of the target item.
12687	 * @param {Number} [factor=undefined] - The time factor in milliseconds.
12688	 * @returns {Number} - The time in milliseconds for the translation.
12689	 */
12690	Owl.prototype.duration = function(from, to, factor) {
12691		return Math.min(Math.max(Math.abs(to - from), 1), 6) * Math.abs((factor || this.settings.smartSpeed));
12692	};
12693
12694	/**
12695	 * Slides to the specified item.
12696	 * @public
12697	 * @param {Number} position - The position of the item.
12698	 * @param {Number} [speed] - The time in milliseconds for the transition.
12699	 */
12700	Owl.prototype.to = function(position, speed) {
12701		var current = this.current(),
12702			revert = null,
12703			distance = position - this.relative(current),
12704			direction = (distance > 0) - (distance < 0),
12705			items = this._items.length,
12706			minimum = this.minimum(),
12707			maximum = this.maximum();
12708
12709		if (this.settings.loop) {
12710			if (!this.settings.rewind && Math.abs(distance) > items / 2) {
12711				distance += direction * -1 * items;
12712			}
12713
12714			position = current + distance;
12715			revert = ((position - minimum) % items + items) % items + minimum;
12716
12717			if (revert !== position && revert - distance <= maximum && revert - distance > 0) {
12718				current = revert - distance;
12719				position = revert;
12720				this.reset(current);
12721			}
12722		} else if (this.settings.rewind) {
12723			maximum += 1;
12724			position = (position % maximum + maximum) % maximum;
12725		} else {
12726			position = Math.max(minimum, Math.min(maximum, position));
12727		}
12728
12729		this.speed(this.duration(current, position, speed));
12730		this.current(position);
12731
12732		if (this.$element.is(':visible')) {
12733			this.update();
12734		}
12735	};
12736
12737	/**
12738	 * Slides to the next item.
12739	 * @public
12740	 * @param {Number}
vendor: 30,399 bytes, lines 12740-13888
12740 [speed] - The time in milliseconds for the transition.
12741	 */
12742	Owl.prototype.next = function(speed) {
12743		speed = speed || false;
12744		this.to(this.relative(this.current()) + 1, speed);
12745	};
12746
12747	/**
12748	 * Slides to the previous item.
12749	 * @public
12750	 * @param {Number} [speed] - The time in milliseconds for the transition.
12751	 */
12752	Owl.prototype.prev = function(speed) {
12753		speed = speed || false;
12754		this.to(this.relative(this.current()) - 1, speed);
12755	};
12756
12757	/**
12758	 * Handles the end of an animation.
12759	 * @protected
12760	 * @param {Event} event - The event arguments.
12761	 */
12762	Owl.prototype.onTransitionEnd = function(event) {
12763
12764		// if css2 animation then event object is undefined
12765		if (event !== undefined) {
12766			event.stopPropagation();
12767
12768			// Catch only owl-stage transitionEnd event
12769			if ((event.target || event.srcElement || event.originalTarget) !== this.$stage.get(0)) {
12770				return false;
12771			}
12772		}
12773
12774		this.leave('animating');
12775		this.trigger('translated');
12776	};
12777
12778	/**
12779	 * Gets viewport width.
12780	 * @protected
12781	 * @return {Number} - The width in pixel.
12782	 */
12783	Owl.prototype.viewport = function() {
12784		var width;
12785		if (this.options.responsiveBaseElement !== window) {
12786			width = $(this.options.responsiveBaseElement).width();
12787		} else if (window.innerWidth) {
12788			width = window.innerWidth;
12789		} else if (document.documentElement && document.documentElement.clientWidth) {
12790			width = document.documentElement.clientWidth;
12791		} else {
12792			throw 'Can not detect viewport width.';
12793		}
12794
12795		return width;
12796	};
12797
12798	/**
12799	 * Replaces the current content.
12800	 * @public
12801	 * @param {HTMLElement|jQuery|String} content - The new content.
12802	 */
12803	Owl.prototype.replace = function(content) {
12804		this.$stage.empty();
12805		this._items = [];
12806
12807		if (content) {
12808			content = (content instanceof jQuery) ? content : $(content);
12809		}
12810
12811		if (this.settings.nestedItemSelector) {
12812			content = content.find('.' + this.settings.nestedItemSelector);
12813		}
12814
12815		content.filter(function() {
12816			return this.nodeType === 1;
12817		}).each($.proxy(function(index, item) {
12818			item = this.prepare(item, index);
12819			this.$stage.append(item);
12820			this._items.push(item);
12821			this._mergers.push(item.find('[data-merge]').addBack('[data-merge]').attr('data-merge') * 1 || 1);
12822		}, this));
12823
12824		this.reset($.isNumeric(this.settings.startPosition) ? this.settings.startPosition : 0);
12825
12826		this.invalidate('items');
12827	};
12828
12829	/**
12830	 * Adds an item.
12831	 * @todo Use `item` instead of `content` for the event arguments.
12832	 * @public
12833	 * @param {HTMLElement|jQuery|String} content - The item content to add.
12834	 * @param {Number} [position] - The relative position at which to insert the item otherwise the item will be added to the end.
12835	 */
12836	Owl.prototype.add = function(content, position) {
12837		var current = this.relative(this._current);
12838
12839		position = position === undefined ? this._items.length : this.normalize(position, true);
12840		content = content instanceof jQuery ? content : $(content);
12841
12842		this.trigger('add', { content: content, position: position });
12843
12844		content = this.prepare(content, this._items[current].index());
12845
12846		if (this._items.length === 0 || position === this._items.length) {
12847			this._items.length === 0 && this.$stage.append(content);
12848			this._items.length !== 0 && this._items[position - 1].after(content);
12849			this._items.push(content);
12850			this._mergers.push(content.find('[data-merge]').andSelf('[data-merge]').attr('data-merge') * 1 || 1);
12851		} else {
12852			this._items[position].before(content);
12853			this._items.splice(position, 0, content);
12854			this._mergers.splice(position, 0, content.find('[data-merge]').andSelf('[data-merge]').attr('data-merge') * 1 || 1);
12855		}
12856
12857		this._items[current] && this.reset(this._items[current].index());
12858
12859		this.invalidate('items');
12860
12861		this.trigger('added', { content: content, position: position });
12862	};
12863
12864	/**
12865	 * Removes an item by its position.
12866	 * @todo Use `item` instead of `content` for the event arguments.
12867	 * @public
12868	 * @param {Number} position - The relative position of the item to remove.
12869	 */
12870	Owl.prototype.remove = function(position) {
12871		position = this.normalize(position, true);
12872
12873		if (position === undefined) {
12874			return;
12875		}
12876
12877		this.trigger('remove', { content: this._items[position], position: position });
12878
12879		this._items[position].remove();
12880		this._items.splice(position, 1);
12881		this._mergers.splice(position, 1);
12882
12883		this.invalidate('items');
12884
12885		this.trigger('removed', { content: null, position: position });
12886	};
12887
12888	/**
12889	 * Preloads images with auto width.
12890	 * @todo Replace by a more generic approach
12891	 * @protected
12892	 */
12893	Owl.prototype.preloadAutoWidthImages = function(images) {
12894		images.each($.proxy(function(i, element) {
12895			this.enter('pre-loading');
12896			element = $(element);
12897			$(new Image()).one('load', $.proxy(function(e) {
12898				element.attr('src', e.target.src);
12899				element.css('opacity', 1);
12900				this.leave('pre-loading');
12901				!this.is('pre-loading') && !this.is('initializing') && this.refresh();
12902			}, this)).attr('src', element.attr('src') || element.attr('data-src') || element.attr('data-src-retina'));
12903		}, this));
12904	};
12905
12906	/**
12907	 * Destroys the carousel.
12908	 * @public
12909	 */
12910	Owl.prototype.destroy = function() {
12911
12912		this.$element.off('.owl.core');
12913		this.$stage.off('.owl.core');
12914		$(document).off('.owl.core');
12915
12916		if (this.settings.responsive !== false) {
12917			window.clearTimeout(this.resizeTimer);
12918			this.off(window, 'resize', this._handlers.onThrottledResize);
12919		}
12920
12921		for (var i in this._plugins) {
12922			this._plugins[i].destroy();
12923		}
12924
12925		this.$stage.children('.cloned').remove();
12926
12927		this.$stage.unwrap();
12928		this.$stage.children().contents().unwrap();
12929		this.$stage.children().unwrap();
12930
12931		this.$element
12932			.removeClass(this.options.refreshClass)
12933			.removeClass(this.options.loadingClass)
12934			.removeClass(this.options.loadedClass)
12935			.removeClass(this.options.rtlClass)
12936			.removeClass(this.options.dragClass)
12937			.removeClass(this.options.grabClass)
12938			.attr('class', this.$element.attr('class').replace(new RegExp(this.options.responsiveClass + '-\\S+\\s', 'g'), ''))
12939			.removeData('owl.carousel');
12940	};
12941
12942	/**
12943	 * Operators to calculate right-to-left and left-to-right.
12944	 * @protected
12945	 * @param {Number} [a] - The left side operand.
12946	 * @param {String} [o] - The operator.
12947	 * @param {Number} [b] - The right side operand.
12948	 */
12949	Owl.prototype.op = function(a, o, b) {
12950		var rtl = this.settings.rtl;
12951		switch (o) {
12952			case '<':
12953				return rtl ? a > b : a < b;
12954			case '>':
12955				return rtl ? a < b : a > b;
12956			case '>=':
12957				return rtl ? a <= b : a >= b;
12958			case '<=':
12959				return rtl ? a >= b : a <= b;
12960			default:
12961				break;
12962		}
12963	};
12964
12965	/**
12966	 * Attaches to an internal event.
12967	 * @protected
12968	 * @param {HTMLElement} element - The event source.
12969	 * @param {String} event - The event name.
12970	 * @param {Function} listener - The event handler to attach.
12971	 * @param {Boolean} capture - Wether the event should be handled at the capturing phase or not.
12972	 */
12973	Owl.prototype.on = function(element, event, listener, capture) {
12974		if (element.addEventListener) {
12975			element.addEventListener(event, listener, capture);
12976		} else if (element.attachEvent) {
12977			element.attachEvent('on' + event, listener);
12978		}
12979	};
12980
12981	/**
12982	 * Detaches from an internal event.
12983	 * @protected
12984	 * @param {HTMLElement} element - The event source.
12985	 * @param {String} event - The event name.
12986	 * @param {Function} listener - The attached event handler to detach.
12987	 * @param {Boolean} capture - Wether the attached event handler was registered as a capturing listener or not.
12988	 */
12989	Owl.prototype.off = function(element, event, listener, capture) {
12990		if (element.removeEventListener) {
12991			element.removeEventListener(event, listener, capture);
12992		} else if (element.detachEvent) {
12993			element.detachEvent('on' + event, listener);
12994		}
12995	};
12996
12997	/**
12998	 * Triggers a public event.
12999	 * @todo Remove `status`, `relatedTarget` should be used instead.
13000	 * @protected
13001	 * @param {String} name - The event name.
13002	 * @param {*} [data=null] - The event data.
13003	 * @param {String} [namespace=carousel] - The event namespace.
13004	 * @param {String} [state] - The state which is associated with the event.
13005	 * @param {Boolean} [enter=false] - Indicates if the call enters the specified state or not.
13006	 * @returns {Event} - The event arguments.
13007	 */
13008	Owl.prototype.trigger = function(name, data, namespace, state, enter) {
13009		var status = {
13010			item: { count: this._items.length, index: this.current() }
13011		}, handler = $.camelCase(
13012			$.grep([ 'on', name, namespace ], function(v) { return v })
13013				.join('-').toLowerCase()
13014		), event = $.Event(
13015			[ name, 'owl', namespace || 'carousel' ].join('.').toLowerCase(),
13016			$.extend({ relatedTarget: this }, status, data)
13017		);
13018
13019		if (!this._supress[name]) {
13020			$.each(this._plugins, function(name, plugin) {
13021				if (plugin.onTrigger) {
13022					plugin.onTrigger(event);
13023				}
13024			});
13025
13026			this.register({ type: Owl.Type.Event, name: name });
13027			this.$element.trigger(event);
13028
13029			if (this.settings && typeof this.settings[handler] === 'function') {
13030				this.settings[handler].call(this, event);
13031			}
13032		}
13033
13034		return event;
13035	};
13036
13037	/**
13038	 * Enters a state.
13039	 * @param name - The state name.
13040	 */
13041	Owl.prototype.enter = function(name) {
13042		$.each([ name ].concat(this._states.tags[name] || []), $.proxy(function(i, name) {
13043			if (this._states.current[name] === undefined) {
13044				this._states.current[name] = 0;
13045			}
13046
13047			this._states.current[name]++;
13048		}, this));
13049	};
13050
13051	/**
13052	 * Leaves a state.
13053	 * @param name - The state name.
13054	 */
13055	Owl.prototype.leave = function(name) {
13056		$.each([ name ].concat(this._states.tags[name] || []), $.proxy(function(i, name) {
13057			this._states.current[name]--;
13058		}, this));
13059	};
13060
13061	/**
13062	 * Registers an event or state.
13063	 * @public
13064	 * @param {Object} object - The event or state to register.
13065	 */
13066	Owl.prototype.register = function(object) {
13067		if (object.type === Owl.Type.Event) {
13068			if (!$.event.special[object.name]) {
13069				$.event.special[object.name] = {};
13070			}
13071
13072			if (!$.event.special[object.name].owl) {
13073				var _default = $.event.special[object.name]._default;
13074				$.event.special[object.name]._default = function(e) {
13075					if (_default && _default.apply && (!e.namespace || e.namespace.indexOf('owl') === -1)) {
13076						return _default.apply(this, arguments);
13077					}
13078					return e.namespace && e.namespace.indexOf('owl') > -1;
13079				};
13080				$.event.special[object.name].owl = true;
13081			}
13082		} else if (object.type === Owl.Type.State) {
13083			if (!this._states.tags[object.name]) {
13084				this._states.tags[object.name] = object.tags;
13085			} else {
13086				this._states.tags[object.name] = this._states.tags[object.name].concat(object.tags);
13087			}
13088
13089			this._states.tags[object.name] = $.grep(this._states.tags[object.name], $.proxy(function(tag, i) {
13090				return $.inArray(tag, this._states.tags[object.name]) === i;
13091			}, this));
13092		}
13093	};
13094
13095	/**
13096	 * Suppresses events.
13097	 * @protected
13098	 * @param {Array.<String>} events - The events to suppress.
13099	 */
13100	Owl.prototype.suppress = function(events) {
13101		$.each(events, $.proxy(function(index, event) {
13102			this._supress[event] = true;
13103		}, this));
13104	};
13105
13106	/**
13107	 * Releases suppressed events.
13108	 * @protected
13109	 * @param {Array.<String>} events - The events to release.
13110	 */
13111	Owl.prototype.release = function(events) {
13112		$.each(events, $.proxy(function(index, event) {
13113			delete this._supress[event];
13114		}, this));
13115	};
13116
13117	/**
13118	 * Gets unified pointer coordinates from event.
13119	 * @todo #261
13120	 * @protected
13121	 * @param {Event} - The `mousedown` or `touchstart` event.
13122	 * @returns {Object} - Contains `x` and `y` coordinates of current pointer position.
13123	 */
13124	Owl.prototype.pointer = function(event) {
13125		var result = { x: null, y: null };
13126
13127		event = event.originalEvent || event || window.event;
13128
13129		event = event.touches && event.touches.length ?
13130			event.touches[0] : event.changedTouches && event.changedTouches.length ?
13131				event.changedTouches[0] : event;
13132
13133		if (event.pageX) {
13134			result.x = event.pageX;
13135			result.y = event.pageY;
13136		} else {
13137			result.x = event.clientX;
13138			result.y = event.clientY;
13139		}
13140
13141		return result;
13142	};
13143
13144	/**
13145	 * Gets the difference of two vectors.
13146	 * @todo #261
13147	 * @protected
13148	 * @param {Object} - The first vector.
13149	 * @param {Object} - The second vector.
13150	 * @returns {Object} - The difference.
13151	 */
13152	Owl.prototype.difference = function(first, second) {
13153		return {
13154			x: first.x - second.x,
13155			y: first.y - second.y
13156		};
13157	};
13158
13159	/**
13160	 * The jQuery Plugin for the Owl Carousel
13161	 * @todo Navigation plugin `next` and `prev`
13162	 * @public
13163	 */
13164	$.fn.owlCarousel = function(option) {
13165		var args = Array.prototype.slice.call(arguments, 1);
13166
13167		return this.each(function() {
13168			var $this = $(this),
13169				data = $this.data('owl.carousel');
13170
13171			if (!data) {
13172				data = new Owl(this, typeof option == 'object' && option);
13173				$this.data('owl.carousel', data);
13174
13175				$.each([
13176					'next', 'prev', 'to', 'destroy', 'refresh', 'replace', 'add', 'remove'
13177				], function(i, event) {
13178					data.register({ type: Owl.Type.Event, name: event });
13179					data.$element.on(event + '.owl.carousel.core', $.proxy(function(e) {
13180						if (e.namespace && e.relatedTarget !== this) {
13181							this.suppress([ event ]);
13182							data[event].apply(this, [].slice.call(arguments, 1));
13183							this.release([ event ]);
13184						}
13185					}, data));
13186				});
13187			}
13188
13189			if (typeof option == 'string' && option.charAt(0) !== '_') {
13190				data[option].apply(data, args);
13191			}
13192		});
13193	};
13194
13195	/**
13196	 * The constructor for the jQuery Plugin
13197	 * @public
13198	 */
13199	$.fn.owlCarousel.Constructor = Owl;
13200
13201})(window.Zepto || window.jQuery, window, document);
13202
13203/**
13204 * AutoRefresh Plugin
13205 * @version 2.0.0-beta.3
13206 * @author Artus Kolanowski
13207 * @license The MIT License (MIT)
13208 */
13209;(function($, window, document, undefined) {
13210
13211	/**
13212	 * Creates the auto refresh plugin.
13213	 * @class The Auto Refresh Plugin
13214	 * @param {Owl} carousel - The Owl Carousel
13215	 */
13216	var AutoRefresh = function(carousel) {
13217		/**
13218		 * Reference to the core.
13219		 * @protected
13220		 * @type {Owl}
13221		 */
13222		this._core = carousel;
13223
13224		/**
13225		 * Refresh interval.
13226		 * @protected
13227		 * @type {number}
13228		 */
13229		this._interval = null;
13230
13231		/**
13232		 * Whether the element is currently visible or not.
13233		 * @protected
13234		 * @type {Boolean}
13235		 */
13236		this._visible = null;
13237
13238		/**
13239		 * All event handlers.
13240		 * @protected
13241		 * @type {Object}
13242		 */
13243		this._handlers = {
13244			'initialized.owl.carousel': $.proxy(function(e) {
13245				if (e.namespace && this._core.settings.autoRefresh) {
13246					this.watch();
13247				}
13248			}, this)
13249		};
13250
13251		// set default options
13252		this._core.options = $.extend({}, AutoRefresh.Defaults, this._core.options);
13253
13254		// register event handlers
13255		this._core.$element.on(this._handlers);
13256	};
13257
13258	/**
13259	 * Default options.
13260	 * @public
13261	 */
13262	AutoRefresh.Defaults = {
13263		autoRefresh: true,
13264		autoRefreshInterval: 500
13265	};
13266
13267	/**
13268	 * Watches the element.
13269	 */
13270	AutoRefresh.prototype.watch = function() {
13271		if (this._interval) {
13272			return;
13273		}
13274
13275		this._visible = this._core.$element.is(':visible');
13276		this._interval = window.setInterval($.proxy(this.refresh, this), this._core.settings.autoRefreshInterval);
13277	};
13278
13279	/**
13280	 * Refreshes the element.
13281	 */
13282	AutoRefresh.prototype.refresh = function() {
13283		if (this._core.$element.is(':visible') === this._visible) {
13284			return;
13285		}
13286
13287		this._visible = !this._visible;
13288
13289		this._core.$element.toggleClass('owl-hidden', !this._visible);
13290
13291		this._visible && (this._core.invalidate('width') && this._core.refresh());
13292	};
13293
13294	/**
13295	 * Destroys the plugin.
13296	 */
13297	AutoRefresh.prototype.destroy = function() {
13298		var handler, property;
13299
13300		window.clearInterval(this._interval);
13301
13302		for (handler in this._handlers) {
13303			this._core.$element.off(handler, this._handlers[handler]);
13304		}
13305		for (property in Object.getOwnPropertyNames(this)) {
13306			typeof this[property] != 'function' && (this[property] = null);
13307		}
13308	};
13309
13310	$.fn.owlCarousel.Constructor.Plugins.AutoRefresh = AutoRefresh;
13311
13312})(window.Zepto || window.jQuery, window, document);
13313
13314/**
13315 * Lazy Plugin
13316 * @version 2.0.0-beta.3
13317 * @author Bartosz Wojciechowski
13318 * @license The MIT License (MIT)
13319 */
13320;(function($, window, document, undefined) {
13321
13322	/**
13323	 * Creates the lazy plugin.
13324	 * @class The Lazy Plugin
13325	 * @param {Owl} carousel - The Owl Carousel
13326	 */
13327	var Lazy = function(carousel) {
13328
13329		/**
13330		 * Reference to the core.
13331		 * @protected
13332		 * @type {Owl}
13333		 */
13334		this._core = carousel;
13335
13336		/**
13337		 * Already loaded items.
13338		 * @protected
13339		 * @type {Array.<jQuery>}
13340		 */
13341		this._loaded = [];
13342
13343		/**
13344		 * Event handlers.
13345		 * @protected
13346		 * @type {Object}
13347		 */
13348		this._handlers = {
13349			'initialized.owl.carousel change.owl.carousel': $.proxy(function(e) {
13350				if (!e.namespace) {
13351					return;
13352				}
13353
13354				if (!this._core.settings || !this._core.settings.lazyLoad) {
13355					return;
13356				}
13357
13358				if ((e.property && e.property.name == 'position') || e.type == 'initialized') {
13359					var settings = this._core.settings,
13360						n = (settings.center && Math.ceil(settings.items / 2) || settings.items),
13361						i = ((settings.center && n * -1) || 0),
13362						position = ((e.property && e.property.value) || this._core.current()) + i,
13363						clones = this._core.clones().length,
13364						load = $.proxy(function(i, v) { this.load(v) }, this);
13365
13366					while (i++ < n) {
13367						this.load(clones / 2 + this._core.relative(position));
13368						clones && $.each(this._core.clones(this._core.relative(position)), load);
13369						position++;
13370					}
13371				}
13372			}, this)
13373		};
13374
13375		// set the default options
13376		this._core.options = $.extend({}, Lazy.Defaults, this._core.options);
13377
13378		// register event handler
13379		this._core.$element.on(this._handlers);
13380	}
13381
13382	/**
13383	 * Default options.
13384	 * @public
13385	 */
13386	Lazy.Defaults = {
13387		lazyLoad: false
13388	}
13389
13390	/**
13391	 * Loads all resources of an item at the specified position.
13392	 * @param {Number} position - The absolute position of the item.
13393	 * @protected
13394	 */
13395	Lazy.prototype.load = function(position) {
13396		var $item = this._core.$stage.children().eq(position),
13397			$elements = $item && $item.find('.owl-lazy');
13398
13399		if (!$elements || $.inArray($item.get(0), this._loaded) > -1) {
13400			return;
13401		}
13402
13403		$elements.each($.proxy(function(index, element) {
13404			var $element = $(element), image,
13405				url = (window.devicePixelRatio > 1 && $element.attr('data-src-retina')) || $element.attr('data-src');
13406
13407			this._core.trigger('load', { element: $element, url: url }, 'lazy');
13408
13409			if ($element.is('img')) {
13410				$element.one('load.owl.lazy', $.proxy(function() {
13411					$element.css('opacity', 1);
13412					this._core.trigger('loaded', { element: $element, url: url }, 'lazy');
13413				}, this)).attr('src', url);
13414			} else {
13415				image = new Image();
13416				image.onload = $.proxy(function() {
13417					$element.css({
13418						'background-image': 'url(' + url + ')',
13419						'opacity': '1'
13420					});
13421					this._core.trigger('loaded', { element: $element, url: url }, 'lazy');
13422				}, this);
13423				image.src = url;
13424			}
13425		}, this));
13426
13427		this._loaded.push($item.get(0));
13428	}
13429
13430	/**
13431	 * Destroys the plugin.
13432	 * @public
13433	 */
13434	Lazy.prototype.destroy = function() {
13435		var handler, property;
13436
13437		for (handler in this.handlers) {
13438			this._core.$element.off(handler, this.handlers[handler]);
13439		}
13440		for (property in Object.getOwnPropertyNames(this)) {
13441			typeof this[property] != 'function' && (this[property] = null);
13442		}
13443	};
13444
13445	$.fn.owlCarousel.Constructor.Plugins.Lazy = Lazy;
13446
13447})(window.Zepto || window.jQuery, window, document);
13448
13449/**
13450 * AutoHeight Plugin
13451 * @version 2.0.0-beta.3
13452 * @author Bartosz Wojciechowski
13453 * @license The MIT License (MIT)
13454 */
13455;(function($, window, document, undefined) {
13456
13457	/**
13458	 * Creates the auto height plugin.
13459	 * @class The Auto Height Plugin
13460	 * @param {Owl} carousel - The Owl Carousel
13461	 */
13462	var AutoHeight = function(carousel) {
13463		/**
13464		 * Reference to the core.
13465		 * @protected
13466		 * @type {Owl}
13467		 */
13468		this._core = carousel;
13469
13470		/**
13471		 * All event handlers.
13472		 * @protected
13473		 * @type {Object}
13474		 */
13475		this._handlers = {
13476			'initialized.owl.carousel refreshed.owl.carousel': $.proxy(function(e) {
13477				if (e.namespace && this._core.settings.autoHeight) {
13478					this.update();
13479				}
13480			}, this),
13481			'changed.owl.carousel': $.proxy(function(e) {
13482				if (e.namespace && this._core.settings.autoHeight && e.property.name == 'position'){
13483					this.update();
13484				}
13485			}, this),
13486			'loaded.owl.lazy': $.proxy(function(e) {
13487				if (e.namespace && this._core.settings.autoHeight
13488					&& e.element.closest('.' + this._core.settings.itemClass).index() === this._core.current()) {
13489					this.update();
13490				}
13491			}, this)
13492		};
13493
13494		// set default options
13495		this._core.options = $.extend({}, AutoHeight.Defaults, this._core.options);
13496
13497		// register event handlers
13498		this._core.$element.on(this._handlers);
13499	};
13500
13501	/**
13502	 * Default options.
13503	 * @public
13504	 */
13505	AutoHeight.Defaults = {
13506		autoHeight: false,
13507		autoHeightClass: 'owl-height'
13508	};
13509
13510	/**
13511	 * Updates the view.
13512	 */
13513	AutoHeight.prototype.update = function() {
13514		var start = this._core._current,
13515			end = start + this._core.settings.items,
13516			visible = this._core.$stage.children().toArray().slice(start, end);
13517			heights = [],
13518			maxheight = 0;
13519
13520		$.each(visible, function(index, item) {
13521			heights.push($(item).height());
13522		});
13523
13524		maxheight = Math.max.apply(null, heights);
13525
13526		this._core.$stage.parent()
13527			.height(maxheight)
13528			.addClass(this._core.settings.autoHeightClass);
13529	};
13530
13531	AutoHeight.prototype.destroy = function() {
13532		var handler, property;
13533
13534		for (handler in this._handlers) {
13535			this._core.$element.off(handler, this._handlers[handler]);
13536		}
13537		for (property in Object.getOwnPropertyNames(this)) {
13538			typeof this[property] != 'function' && (this[property] = null);
13539		}
13540	};
13541
13542	$.fn.owlCarousel.Constructor.Plugins.AutoHeight = AutoHeight;
13543
13544})(window.Zepto || window.jQuery, window, document);
13545
13546/**
13547 * Video Plugin
13548 * @version 2.0.0-beta.3
13549 * @author Bartosz Wojciechowski
13550 * @license The MIT License (MIT)
13551 */
13552;(function($, window, document, undefined) {
13553
13554	/**
13555	 * Creates the video plugin.
13556	 * @class The Video Plugin
13557	 * @param {Owl} carousel - The Owl Carousel
13558	 */
13559	var Video = function(carousel) {
13560		/**
13561		 * Reference to the core.
13562		 * @protected
13563		 * @type {Owl}
13564		 */
13565		this._core = carousel;
13566
13567		/**
13568		 * Cache all video URLs.
13569		 * @protected
13570		 * @type {Object}
13571		 */
13572		this._videos = {};
13573
13574		/**
13575		 * Current playing item.
13576		 * @protected
13577		 * @type {jQuery}
13578		 */
13579		this._playing = null;
13580
13581		/**
13582		 * All event handlers.
13583		 * @todo The cloned content removale is too late
13584		 * @protected
13585		 * @type {Object}
13586		 */
13587		this._handlers = {
13588			'initialized.owl.carousel': $.proxy(function(e) {
13589				if (e.namespace) {
13590					this._core.register({ type: 'state', name: 'playing', tags: [ 'interacting' ] });
13591				}
13592			}, this),
13593			'resize.owl.carousel': $.proxy(function(e) {
13594				if (e.namespace && this._core.settings.video && this.isInFullScreen()) {
13595					e.preventDefault();
13596				}
13597			}, this),
13598			'refreshed.owl.carousel': $.proxy(function(e) {
13599				if (e.namespace && this._core.is('resizing')) {
13600					this._core.$stage.find('.cloned .owl-video-frame').remove();
13601				}
13602			}, this),
13603			'changed.owl.carousel': $.proxy(function(e) {
13604				if (e.namespace && e.property.name === 'position' && this._playing) {
13605					this.stop();
13606				}
13607			}, this),
13608			'prepared.owl.carousel': $.proxy(function(e) {
13609				if (!e.namespace) {
13610					return;
13611				}
13612
13613				var $element = $(e.content).find('.owl-video');
13614
13615				if ($element.length) {
13616					$element.css('display', 'none');
13617					this.fetch($element, $(e.content));
13618				}
13619			}, this)
13620		};
13621
13622		// set default options
13623		this._core.options = $.extend({}, Video.Defaults, this._core.options);
13624
13625		// register event handlers
13626		this._core.$element.on(this._handlers);
13627
13628		this._core.$element.on('click.owl.video', '.owl-video-play-icon', $.proxy(function(e) {
13629			this.play(e);
13630		}, this));
13631	};
13632
13633	/**
13634	 * Default options.
13635	 * @public
13636	 */
13637	Video.Defaults = {
13638		video: false,
13639		videoHeight: false,
13640		videoWidth: false
13641	};
13642
13643	/**
13644	 * Gets the video ID and the type (YouTube/Vimeo only).
13645	 * @protected
13646	 * @param {jQuery} target - The target containing the video data.
13647	 * @param {jQuery} item - The item containing the video.
13648	 */
13649	Video.prototype.fetch = function(target, item) {
13650		var type = target.attr('data-vimeo-id') ? 'vimeo' : 'youtube',
13651			id = target.attr('data-vimeo-id') || target.attr('data-youtube-id'),
13652			width = target.attr('data-width') || this._core.settings.videoWidth,
13653			height = target.attr('data-height') || this._core.settings.videoHeight,
13654			url = target.attr('href');
13655
13656		if (url) {
13657			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+)?/);
13658
13659			if (id[3].indexOf('youtu') > -1) {
13660				type = 'youtube';
13661			} else if (id[3].indexOf('vimeo') > -1) {
13662				type = 'vimeo';
13663			} else {
13664				throw new Error('Video URL not supported.');
13665			}
13666			id = id[6];
13667		} else {
13668			throw new Error('Missing video URL.');
13669		}
13670
13671		this._videos[url] = {
13672			type: type,
13673			id: id,
13674			width: width,
13675			height: height
13676		};
13677
13678		item.attr('data-video', url);
13679
13680		this.thumbnail(target, this._videos[url]);
13681	};
13682
13683	/**
13684	 * Creates video thumbnail.
13685	 * @protected
13686	 * @param {jQuery} target - The target containing the video data.
13687	 * @param {Object} info - The video info object.
13688	 * @see `fetch`
13689	 */
13690	Video.prototype.thumbnail = function(target, video) {
13691		var tnLink,
13692			icon,
13693			path,
13694			dimensions = video.width && video.height ? 'style="width:' + video.width + 'px;height:' + video.height + 'px;"' : '',
13695			customTn = target.find('img'),
13696			srcType = 'src',
13697			lazyClass = '',
13698			settings = this._core.settings,
13699			create = function(path) {
13700				icon = '<div class="owl-video-play-icon"></div>';
13701
13702				if (settings.lazyLoad) {
13703					tnLink = '<div class="owl-video-tn ' + lazyClass + '" ' + srcType + '="' + path + '"></div>';
13704				} else {
13705					tnLink = '<div class="owl-video-tn" style="opacity:1;background-image:url(' + path + ')"></div>';
13706				}
13707				target.after(tnLink);
13708				target.after(icon);
13709			};
13710
13711		// wrap video content into owl-video-wrapper div
13712		target.wrap('<div class="owl-video-wrapper"' + dimensions + '></div>');
13713
13714		if (this._core.settings.lazyLoad) {
13715			srcType = 'data-src';
13716			lazyClass = 'owl-lazy';
13717		}
13718
13719		// custom thumbnail
13720		if (customTn.length) {
13721			create(customTn.attr(srcType));
13722			customTn.remove();
13723			return false;
13724		}
13725
13726		if (video.type === 'youtube') {
13727			path = "//img.youtube.com/vi/" + video.id + "/hqdefault.jpg";
13728			create(path);
13729		} else if (video.type === 'vimeo') {
13730			$.ajax({
13731				type: 'GET',
13732				url: '//vimeo.com/api/v2/video/' + video.id + '.json',
13733				jsonp: 'callback',
13734				dataType: 'jsonp',
13735				success: function(data) {
13736					path = data[0].thumbnail_large;
13737					create(path);
13738				}
13739			});
13740		}
13741	};
13742
13743	/**
13744	 * Stops the current video.
13745	 * @public
13746	 */
13747	Video.prototype.stop = function() {
13748		this._core.trigger('stop', null, 'video');
13749		this._playing.find('.owl-video-frame').remove();
13750		this._playing.removeClass('owl-video-playing');
13751		this._playing = null;
13752		this._core.leave('playing');
13753		this._core.trigger('stopped', null, 'video');
13754	};
13755
13756	/**
13757	 * Starts the current video.
13758	 * @public
13759	 * @param {Event} event - The event arguments.
13760	 */
13761	Video.prototype.play = function(event) {
13762		var target = $(event.target),
13763			item = target.closest('.' + this._core.settings.itemClass),
13764			video = this._videos[item.attr('data-video')],
13765			width = video.width || '100%',
13766			height = video.height || this._core.$stage.height(),
13767			html;
13768
13769		if (this._playing) {
13770			return;
13771		}
13772
13773		this._core.enter('playing');
13774		this._core.trigger('play', null, 'video');
13775
13776		item = this._core.items(this._core.relative(item.index()));
13777
13778		this._core.reset(item.index());
13779
13780		if (video.type === 'youtube') {
13781			html = '<iframe width="' + width + '" height="' + height + '" src="//www.youtube.com/embed/' +
13782				video.id + '?autoplay=1&v=' + video.id + '" frameborder="0" allowfullscreen></iframe>';
13783		} else if (video.type === 'vimeo') {
13784			html = '<iframe src="//player.vimeo.com/video/' + video.id +
13785				'?autoplay=1" width="' + width + '" height="' + height +
13786				'" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>';
13787		}
13788
13789		$('<div class="owl-video-frame">' + html + '</div>').insertAfter(item.find('.owl-video'));
13790
13791		this._playing = item.addClass('owl-video-playing');
13792	};
13793
13794	/**
13795	 * Checks whether an video is currently in full screen mode or not.
13796	 * @todo Bad style because looks like a readonly method but changes members.
13797	 * @protected
13798	 * @returns {Boolean}
13799	 */
13800	Video.prototype.isInFullScreen = function() {
13801		var element = document.fullscreenElement || document.mozFullScreenElement ||
13802				document.webkitFullscreenElement;
13803
13804		return element && $(element).parent().hasClass('owl-video-frame');
13805	};
13806
13807	/**
13808	 * Destroys the plugin.
13809	 */
13810	Video.prototype.destroy = function() {
13811		var handler, property;
13812
13813		this._core.$element.off('click.owl.video');
13814
13815		for (handler in this._handlers) {
13816			this._core.$element.off(handler, this._handlers[handler]);
13817		}
13818		for (property in Object.getOwnPropertyNames(this)) {
13819			typeof this[property] != 'function' && (this[property] = null);
13820		}
13821	};
13822
13823	$.fn.owlCarousel.Constructor.Plugins.Video = Video;
13824
13825})(window.Zepto || window.jQuery, window, document);
13826
13827/**
13828 * Animate Plugin
13829 * @version 2.0.0-beta.3
13830 * @author Bartosz Wojciechowski
13831 * @license The MIT License (MIT)
13832 */
13833;(function($, window, document, undefined) {
13834
13835	/**
13836	 * Creates the animate plugin.
13837	 * @class The Navigation Plugin
13838	 * @param {Owl} scope - The Owl Carousel
13839	 */
13840	var Animate = function(scope) {
13841		this.core = scope;
13842		this.core.options = $.extend({}, Animate.Defaults, this.core.options);
13843		this.swapping = true;
13844		this.previous = undefined;
13845		this.next = undefined;
13846
13847		this.handlers = {
13848			'change.owl.carousel': $.proxy(function(e) {
13849				if (e.namespace && e.property.name == 'position') {
13850					this.previous = this.core.current();
13851					this.next = e.property.value;
13852				}
13853			}, this),
13854			'drag.owl.carousel dragged.owl.carousel translated.owl.carousel': $.proxy(function(e) {
13855				if (e.namespace) {
13856					this.swapping = e.type == 'translated';
13857				}
13858			}, this),
13859			'translate.owl.carousel': $.proxy(function(e) {
13860				if (e.namespace && this.swapping && (this.core.options.animateOut || this.core.options.animateIn)) {
13861					this.swap();
13862				}
13863			}, this)
13864		};
13865
13866		this.core.$element.on(this.handlers);
13867	};
13868
13869	/**
13870	 * Default options.
13871	 * @public
13872	 */
13873	Animate.Defaults = {
13874		animateOut: false,
13875		animateIn: false
13876	};
13877
13878	/**
13879	 * Toggles the animation classes whenever an translations starts.
13880	 * @protected
13881	 * @returns {Boolean|undefined}
13882	 */
13883	Animate.prototype.swap = function() {
13884
13885		if (this.core.settings.items !== 1) {
13886			return;
13887		}
13888
13889		if (!$.support.animation || !$.support.transition) {
13890			return;
13891		}
13892
13893		this.core.speed(0);
13894
13895		var left,
13896			clear = $.proxy(this.clear, this),
13897			previous = this.core.$stage.children().eq(this.previous),
13898			next = this.core.$stage.children().eq(this.next),
13899			incoming = this.core.settings.animateIn,
13900			outgoing = this.core.settings.animateOut;
13901
13902		if (this.core.current() === this.previous) {
13903			return;
13904		}
13905
13906		if (outgoing) {
13907			left = this.core.coordinates(this.previous) - this.core.coordinates(this.next);
13908			previous.css( { 'left': left + 'px' } )
13909				.addClass('animated owl-animated-out')
13910				.addClass(outgoing)
13911				.on($.support.animation.end, clear);
13912		}
13913
13914		if (incoming) {
13915			next.addClass('animated owl-animated-in')
13916				.addClass(incoming)
13917				.on($.support.animation.end, clear);
13918		}
13919	};
13920
13921	Animate.prototype.clear = function(e) {
13922		if ($(e.target).hasClass('animated')) {
13923			$(e.target).css( { 'left': '' } )
13924				.removeClass('animated owl-animated-out owl-animated-in')
13925				.removeClass(this.core.settings.animateIn)
13926				.removeClass(this.core.settings.animateOut);
13927			this.core.onTransitionEnd();
13928		}
13929	};
13930
13931	/**
13932	 * Destroys the plugin.
13933	 * @public
13934	 */
13935	Animate.prototype.destroy = function() {
13936		var handler, property;
13937
13938		for (handler in this.handlers) {
13939			this.core.$element.off(handler, this.handlers[handler]);
13940		}
13941		for (property in Object.getOwnPropertyNames(this)) {
13942			typeof this[property] != 'function' && (this[property] = null);
13943		}
13944	};
13945
13946	$.fn.owlCarousel.Constructor.Plugins.Animate = Animate;
13947
13948})(window.Zepto || window.jQuery, window, document);
13949
13950/**
13951 * Autoplay Plugin
13952 * @version 2.0.0-beta.3
13953 * @author Bartosz Wojciechowski
13954 * @author Artus Kolanowski
13955 * @license The MIT License (MIT)
13956 */
13957;(function($, window, document, undefined) {
13958
13959	/**
13960	 * Creates the autoplay plugin.
13961	 * @class The Autoplay Plugin
13962	 * @param {Owl} scope - The Owl Carousel
13963	 */
13964	var Autoplay = function(carousel) {
13965		/**
13966		 * Reference to the core.
13967		 * @protected
13968		 * @type {Owl}
13969		 */
13970		this._core = carousel;
13971
13972		/**
13973		 * The autoplay interval.
13974		 * @type {Number}
13975		 */
13976		this._interval = null;
13977
13978		/**
13979		 * Indicates whenever the autoplay is paused.
13980		 * @type {Boolean}
13981		 */
13982		this._paused = false;
13983
13984		/**
13985		 * All event handlers.
13986		 * @protected
13987		 * @type {Object}
13988		 */
13989		this._handlers = {
13990			'changed.owl.carousel': $.proxy(function(e) {
13991				if (e.namespace && e.property.name === 'settings') {
13992					if (this._core.settings.autoplay) {
13993						this.play();
13994					} else {
13995						this.stop();
13996					}
13997				}
13998			}, this),
13999			'initialized.owl.carousel': $.proxy(function(e) {
14000				if (e.namespace && this._core.settings.autoplay) {
14001					this.play();
14002				}
14003			}, this),
14004			'play.owl.autoplay': $.proxy(function(e, t, s) {
14005				if (e.namespace) {
14006					this.play(t, s);
14007				}
14008			}, this),
14009			'stop.owl.autoplay': $.proxy(function(e) {
14010				if (e.namespace) {
14011					this.stop();
14012				}
14013			}, this),
14014			'mouseover.owl.autoplay': $.proxy(function() {
14015				if (this._core.settings.autoplayHoverPause && this._core.is('rotating')) {
14016					this.pause();
14017				}
14018			}, this),
14019			'mouseleave.owl.autoplay': $.proxy(function() {
14020				if (this._core.settings.autoplayHoverPause && this._core.is('rotating')) {
14021					this.play();
14022				}
14023			}, this)
14024		};
14025
14026		// register event handlers
14027		this._core.$element.on(this._handlers);
14028
14029		// set default options
14030		this._core.options = $.extend({}, Autoplay.Defaults, this._core.options);
14031	};
14032
14033	/**
14034	 * Default options.
14035	 * @public
14036	 */
14037	Autoplay.Defaults = {
14038		autoplay: false,
14039		autoplayTimeout: 5000,
14040		autoplayHoverPause: false,
14041		autoplaySpeed: false
14042	};
14043
14044	/**
14045	 * Starts the autoplay.
14046	 * @public
14047	 * @param {Number} [timeout] - The interval before the next animation starts.
14048	 * @param {Number} [speed] - The animation speed for the animations.
14049	 */
14050	Autoplay.prototype.play = function(timeout, speed) {
14051		this._paused = false;
14052
14053		if (this._core.is('rotating')) {
14054			return;
14055		}
14056
14057		this._core.enter('rotating');
14058
14059		this._interval = window.setInterval($.proxy(function() {
14060			if (this._paused || this._core.is('busy') || this._core.is('interacting') || document.hidden) {
14061				return;
14062			}
14063			this._core.next(speed || this._core.settings.autoplaySpeed);
14064		}, this), timeout || this._core.settings.autoplayTimeout);
14065	};
14066
14067	/**
14068	 * Stops the autoplay.
14069	 * @public
14070	 */
14071	Autoplay.prototype.stop = function() {
14072		if (!this._core.is('rotating')) {
14073			return;
14074		}
14075
14076		window.clearInterval(this._interval);
14077		this._core.leave('rotating');
14078	};
14079
14080	/**
14081	 * Stops the autoplay.
14082	 * @public
14083	 */
14084	Autoplay.prototype.pause = function() {
14085		if (!this._core.is('rotating')) {
14086			return;
14087		}
14088
14089		this._paused = true;
14090	};
14091
14092	/**
14093	 * Destroys the plugin.
14094	 */
14095	Autoplay.prototype.destroy = function() {
14096		var handler, property;
14097
14098		this.stop();
14099
14100		for (handler in this._handlers) {
14101			this._core.$element.off(handler, this._handlers[handler]);
14102		}
14103		for (property in Object.getOwnPropertyNames(this)) {
14104			typeof this[property] != 'function' && (this[property] = null);
14105		}
14106	};
14107
14108	$.fn.owlCarousel.Constructor.Plugins.autoplay = Autoplay;
14109
14110})(window.Zepto || window.jQuery, window, document);
14111
14112/**
14113 * Navigation Plugin
14114 * @version 2.0.0-beta.3
14115 * @author Artus Kolanowski
14116 * @license The MIT License (MIT)
14117 */
14118;(function($, window, document, undefined) {
14119	'use strict';
14120
14121	/**
14122	 * Creates the navigation plugin.
14123	 * @class The Navigation Plugin
14124	 * @param {Owl} carousel - The Owl Carousel.
14125	 */
14126	var Navigation = function(carousel) {
14127		/**
14128		 * Reference to the core.
14129		 * @protected
14130		 * @type {Owl}
14131		 */
14132		this._core = carousel;
14133
14134		/**
14135		 * Indicates whether the plugin is initialized or not.
14136		 * @protected
14137		 * @type {Boolean}
14138		 */
14139		this._initialized = false;
vendor: 14,104 bytes, lines 14140-14668
14140
14141		/**
14142		 * The current paging indexes.
14143		 * @protected
14144		 * @type {Array}
14145		 */
14146		this._pages = [];
14147
14148		/**
14149		 * All DOM elements of the user interface.
14150		 * @protected
14151		 * @type {Object}
14152		 */
14153		this._controls = {};
14154
14155		/**
14156		 * Markup for an indicator.
14157		 * @protected
14158		 * @type {Array.<String>}
14159		 */
14160		this._templates = [];
14161
14162		/**
14163		 * The carousel element.
14164		 * @type {jQuery}
14165		 */
14166		this.$element = this._core.$element;
14167
14168		/**
14169		 * Overridden methods of the carousel.
14170		 * @protected
14171		 * @type {Object}
14172		 */
14173		this._overrides = {
14174			next: this._core.next,
14175			prev: this._core.prev,
14176			to: this._core.to
14177		};
14178
14179		/**
14180		 * All event handlers.
14181		 * @protected
14182		 * @type {Object}
14183		 */
14184		this._handlers = {
14185			'prepared.owl.carousel': $.proxy(function(e) {
14186				if (e.namespace && this._core.settings.dotsData) {
14187					this._templates.push('<div class="' + this._core.settings.dotClass + '">' +
14188						$(e.content).find('[data-dot]').andSelf('[data-dot]').attr('data-dot') + '</div>');
14189				}
14190			}, this),
14191			'added.owl.carousel': $.proxy(function(e) {
14192				if (e.namespace && this._core.settings.dotsData) {
14193					this._templates.splice(e.position, 0, this._templates.pop());
14194				}
14195			}, this),
14196			'remove.owl.carousel': $.proxy(function(e) {
14197				if (e.namespace && this._core.settings.dotsData) {
14198					this._templates.splice(e.position, 1);
14199				}
14200			}, this),
14201			'changed.owl.carousel': $.proxy(function(e) {
14202				if (e.namespace && e.property.name == 'position') {
14203					this.draw();
14204				}
14205			}, this),
14206			'initialized.owl.carousel': $.proxy(function(e) {
14207				if (e.namespace && !this._initialized) {
14208					this._core.trigger('initialize', null, 'navigation');
14209					this.initialize();
14210					this.update();
14211					this.draw();
14212					this._initialized = true;
14213					this._core.trigger('initialized', null, 'navigation');
14214				}
14215			}, this),
14216			'refreshed.owl.carousel': $.proxy(function(e) {
14217				if (e.namespace && this._initialized) {
14218					this._core.trigger('refresh', null, 'navigation');
14219					this.update();
14220					this.draw();
14221					this._core.trigger('refreshed', null, 'navigation');
14222				}
14223			}, this)
14224		};
14225
14226		// set default options
14227		this._core.options = $.extend({}, Navigation.Defaults, this._core.options);
14228
14229		// register event handlers
14230		this.$element.on(this._handlers);
14231	};
14232
14233	/**
14234	 * Default options.
14235	 * @public
14236	 * @todo Rename `slideBy` to `navBy`
14237	 */
14238	Navigation.Defaults = {
14239		nav: false,
14240		navText: [ 'prev', 'next' ],
14241		navSpeed: false,
14242		navElement: 'div',
14243		navContainer: false,
14244		navContainerClass: 'owl-nav',
14245		navClass: [ 'owl-prev', 'owl-next' ],
14246		slideBy: 1,
14247		dotClass: 'owl-dot',
14248		dotsClass: 'owl-dots',
14249		dots: true,
14250		dotsEach: false,
14251		dotsData: false,
14252		dotsSpeed: false,
14253		dotsContainer: false
14254	};
14255
14256	/**
14257	 * Initializes the layout of the plugin and extends the carousel.
14258	 * @protected
14259	 */
14260	Navigation.prototype.initialize = function() {
14261		var override,
14262			settings = this._core.settings;
14263
14264		// create DOM structure for relative navigation
14265		this._controls.$relative = (settings.navContainer ? $(settings.navContainer)
14266			: $('<div>').addClass(settings.navContainerClass).appendTo(this.$element)).addClass('disabled');
14267
14268		this._controls.$previous = $('<' + settings.navElement + '>')
14269			.addClass(settings.navClass[0])
14270			.html(settings.navText[0])
14271			.prependTo(this._controls.$relative)
14272			.on('click', $.proxy(function(e) {
14273				this.prev(settings.navSpeed);
14274			}, this));
14275		this._controls.$next = $('<' + settings.navElement + '>')
14276			.addClass(settings.navClass[1])
14277			.html(settings.navText[1])
14278			.appendTo(this._controls.$relative)
14279			.on('click', $.proxy(function(e) {
14280				this.next(settings.navSpeed);
14281			}, this));
14282
14283		// create DOM structure for absolute navigation
14284		if (!settings.dotsData) {
14285			this._templates = [ $('<div>')
14286				.addClass(settings.dotClass)
14287				.append($('<span>'))
14288				.prop('outerHTML') ];
14289		}
14290
14291		this._controls.$absolute = (settings.dotsContainer ? $(settings.dotsContainer)
14292			: $('<div>').addClass(settings.dotsClass).appendTo(this.$element)).addClass('disabled');
14293
14294		this._controls.$absolute.on('click', 'div', $.proxy(function(e) {
14295			var index = $(e.target).parent().is(this._controls.$absolute)
14296				? $(e.target).index() : $(e.target).parent().index();
14297
14298			e.preventDefault();
14299
14300			this.to(index, settings.dotsSpeed);
14301		}, this));
14302
14303		// override public methods of the carousel
14304		for (override in this._overrides) {
14305			this._core[override] = $.proxy(this[override], this);
14306		}
14307	};
14308
14309	/**
14310	 * Destroys the plugin.
14311	 * @protected
14312	 */
14313	Navigation.prototype.destroy = function() {
14314		var handler, control, property, override;
14315
14316		for (handler in this._handlers) {
14317			this.$element.off(handler, this._handlers[handler]);
14318		}
14319		for (control in this._controls) {
14320			this._controls[control].remove();
14321		}
14322		for (override in this.overides) {
14323			this._core[override] = this._overrides[override];
14324		}
14325		for (property in Object.getOwnPropertyNames(this)) {
14326			typeof this[property] != 'function' && (this[property] = null);
14327		}
14328	};
14329
14330	/**
14331	 * Updates the internal state.
14332	 * @protected
14333	 */
14334	Navigation.prototype.update = function() {
14335		var i, j, k,
14336			lower = this._core.clones().length / 2,
14337			upper = lower + this._core.items().length,
14338			maximum = this._core.maximum(true),
14339			settings = this._core.settings,
14340			size = settings.center || settings.autoWidth || settings.dotsData
14341				? 1 : settings.dotsEach || settings.items;
14342
14343		if (settings.slideBy !== 'page') {
14344			settings.slideBy = Math.min(settings.slideBy, settings.items);
14345		}
14346
14347		if (settings.dots || settings.slideBy == 'page') {
14348			this._pages = [];
14349
14350			for (i = lower, j = 0, k = 0; i < upper; i++) {
14351				if (j >= size || j === 0) {
14352					this._pages.push({
14353						start: Math.min(maximum, i - lower),
14354						end: i - lower + size - 1
14355					});
14356					if (Math.min(maximum, i - lower) === maximum) {
14357						break;
14358					}
14359					j = 0, ++k;
14360				}
14361				j += this._core.mergers(this._core.relative(i));
14362			}
14363		}
14364	};
14365
14366	/**
14367	 * Draws the user interface.
14368	 * @todo The option `dotsData` wont work.
14369	 * @protected
14370	 */
14371	Navigation.prototype.draw = function() {
14372		var difference,
14373			settings = this._core.settings,
14374			disabled = this._core.items().length <= settings.items,
14375			index = this._core.relative(this._core.current()),
14376			loop = settings.loop || settings.rewind;
14377
14378		this._controls.$relative.toggleClass('disabled', !settings.nav || disabled);
14379
14380		if (settings.nav) {
14381			this._controls.$previous.toggleClass('disabled', !loop && index <= this._core.minimum(true));
14382			this._controls.$next.toggleClass('disabled', !loop && index >= this._core.maximum(true));
14383		}
14384
14385		this._controls.$absolute.toggleClass('disabled', !settings.dots || disabled);
14386
14387		if (settings.dots) {
14388			difference = this._pages.length - this._controls.$absolute.children().length;
14389
14390			if (settings.dotsData && difference !== 0) {
14391				this._controls.$absolute.html(this._templates.join(''));
14392			} else if (difference > 0) {
14393				this._controls.$absolute.append(new Array(difference + 1).join(this._templates[0]));
14394			} else if (difference < 0) {
14395				this._controls.$absolute.children().slice(difference).remove();
14396			}
14397
14398			this._controls.$absolute.find('.active').removeClass('active');
14399			this._controls.$absolute.children().eq($.inArray(this.current(), this._pages)).addClass('active');
14400		}
14401	};
14402
14403	/**
14404	 * Extends event data.
14405	 * @protected
14406	 * @param {Event} event - The event object which gets thrown.
14407	 */
14408	Navigation.prototype.onTrigger = function(event) {
14409		var settings = this._core.settings;
14410
14411		event.page = {
14412			index: $.inArray(this.current(), this._pages),
14413			count: this._pages.length,
14414			size: settings && (settings.center || settings.autoWidth || settings.dotsData
14415				? 1 : settings.dotsEach || settings.items)
14416		};
14417	};
14418
14419	/**
14420	 * Gets the current page position of the carousel.
14421	 * @protected
14422	 * @returns {Number}
14423	 */
14424	Navigation.prototype.current = function() {
14425		var current = this._core.relative(this._core.current());
14426		return $.grep(this._pages, $.proxy(function(page, index) {
14427			return page.start <= current && page.end >= current;
14428		}, this)).pop();
14429	};
14430
14431	/**
14432	 * Gets the current succesor/predecessor position.
14433	 * @protected
14434	 * @returns {Number}
14435	 */
14436	Navigation.prototype.getPosition = function(successor) {
14437		var position, length,
14438			settings = this._core.settings;
14439
14440		if (settings.slideBy == 'page') {
14441			position = $.inArray(this.current(), this._pages);
14442			length = this._pages.length;
14443			successor ? ++position : --position;
14444			position = this._pages[((position % length) + length) % length].start;
14445		} else {
14446			position = this._core.relative(this._core.current());
14447			length = this._core.items().length;
14448			successor ? position += settings.slideBy : position -= settings.slideBy;
14449		}
14450
14451		return position;
14452	};
14453
14454	/**
14455	 * Slides to the next item or page.
14456	 * @public
14457	 * @param {Number} [speed=false] - The time in milliseconds for the transition.
14458	 */
14459	Navigation.prototype.next = function(speed) {
14460		$.proxy(this._overrides.to, this._core)(this.getPosition(true), speed);
14461	};
14462
14463	/**
14464	 * Slides to the previous item or page.
14465	 * @public
14466	 * @param {Number} [speed=false] - The time in milliseconds for the transition.
14467	 */
14468	Navigation.prototype.prev = function(speed) {
14469		$.proxy(this._overrides.to, this._core)(this.getPosition(false), speed);
14470	};
14471
14472	/**
14473	 * Slides to the specified item or page.
14474	 * @public
14475	 * @param {Number} position - The position of the item or page.
14476	 * @param {Number} [speed] - The time in milliseconds for the transition.
14477	 * @param {Boolean} [standard=false] - Whether to use the standard behaviour or not.
14478	 */
14479	Navigation.prototype.to = function(position, speed, standard) {
14480		var length;
14481
14482		if (!standard) {
14483			length = this._pages.length;
14484			$.proxy(this._overrides.to, this._core)(this._pages[((position % length) + length) % length].start, speed);
14485		} else {
14486			$.proxy(this._overrides.to, this._core)(position, speed);
14487		}
14488	};
14489
14490	$.fn.owlCarousel.Constructor.Plugins.Navigation = Navigation;
14491
14492})(window.Zepto || window.jQuery, window, document);
14493
14494/**
14495 * Hash Plugin
14496 * @version 2.0.0-beta.3
14497 * @author Artus Kolanowski
14498 * @license The MIT License (MIT)
14499 */
14500;(function($, window, document, undefined) {
14501	'use strict';
14502
14503	/**
14504	 * Creates the hash plugin.
14505	 * @class The Hash Plugin
14506	 * @param {Owl} carousel - The Owl Carousel
14507	 */
14508	var Hash = function(carousel) {
14509		/**
14510		 * Reference to the core.
14511		 * @protected
14512		 * @type {Owl}
14513		 */
14514		this._core = carousel;
14515
14516		/**
14517		 * Hash index for the items.
14518		 * @protected
14519		 * @type {Object}
14520		 */
14521		this._hashes = {};
14522
14523		/**
14524		 * The carousel element.
14525		 * @type {jQuery}
14526		 */
14527		this.$element = this._core.$element;
14528
14529		/**
14530		 * All event handlers.
14531		 * @protected
14532		 * @type {Object}
14533		 */
14534		this._handlers = {
14535			/*'initialized.owl.carousel': $.proxy(function(e) {
14536				if (e.namespace && this._core.settings.startPosition === 'URLHash') {
14537					$(window).trigger('hashchange.owl.navigation');
14538				}
14539			}, this),*/
14540			'prepared.owl.carousel': $.proxy(function(e) {
14541				if (e.namespace) {
14542					var hash = $(e.content).find('[data-hash]').addBack('[data-hash]').attr('data-hash');
14543
14544					if (!hash) {
14545						return;
14546					}
14547
14548					this._hashes[hash] = e.content;
14549				}
14550			}, this),
14551			'changed.owl.carousel': $.proxy(function(e) {
14552				if (e.namespace && e.property.name === 'position') {
14553					var current = this._core.items(this._core.relative(this._core.current())),
14554						hash = $.map(this._hashes, function(item, hash) {
14555							return item === current ? hash : null;
14556						}).join();
14557
14558					if (!hash || window.location.hash.slice(1) === hash) {
14559						return;
14560					}
14561
14562					window.location.hash = hash;
14563				}
14564			}, this)
14565		};
14566
14567		// set default options
14568		this._core.options = $.extend({}, Hash.Defaults, this._core.options);
14569
14570		// register the event handlers
14571		this.$element.on(this._handlers);
14572
14573		// register event listener for hash navigation
14574		/*$(window).on('hashchange.owl.navigation', $.proxy(function(e) {
14575			var hash = window.location.hash.substring(1),
14576				items = this._core.$stage.children(),
14577				position = this._hashes[hash] && items.index(this._hashes[hash]);
14578
14579			if (position === undefined || position === this._core.current()) {
14580				return;
14581			}
14582
14583			this._core.to(this._core.relative(position), false, true);
14584		}, this));*/
14585	};
14586
14587	/**
14588	 * Default options.
14589	 * @public
14590	 */
14591	Hash.Defaults = {
14592		URLhashListener: false
14593	};
14594
14595	/**
14596	 * Destroys the plugin.
14597	 * @public
14598	 */
14599	Hash.prototype.destroy = function() {
14600		var handler, property;
14601
14602		$(window).off('hashchange.owl.navigation');
14603
14604		for (handler in this._handlers) {
14605			this._core.$element.off(handler, this._handlers[handler]);
14606		}
14607		for (property in Object.getOwnPropertyNames(this)) {
14608			typeof this[property] != 'function' && (this[property] = null);
14609		}
14610	};
14611
14612	$.fn.owlCarousel.Constructor.Plugins.Hash = Hash;
14613
14614})(window.Zepto || window.jQuery, window, document);
14615
14616/**
14617 * Support Plugin
14618 *
14619 * @version 2.0.0-beta.3
14620 * @author Vivid Planet Software GmbH
14621 * @author Artus Kolanowski
14622 * @license The MIT License (MIT)
14623 */
14624;(function($, window, document, undefined) {
14625
14626	var style = $('<support>').get(0).style,
14627		prefixes = 'Webkit Moz O ms'.split(' '),
14628		events = {
14629			transition: {
14630				end: {
14631					WebkitTransition: 'webkitTransitionEnd',
14632					MozTransition: 'transitionend',
14633					OTransition: 'oTransitionEnd',
14634					transition: 'transitionend'
14635				}
14636			},
14637			animation: {
14638				end: {
14639					WebkitAnimation: 'webkitAnimationEnd',
14640					MozAnimation: 'animationend',
14641					OAnimation: 'oAnimationEnd',
14642					animation: 'animationend'
14643				}
14644			}
14645		},
14646		tests = {
14647			csstransforms: function() {
14648				return !!test('transform');
14649			},
14650			csstransforms3d: function() {
14651				return !!test('perspective');
14652			},
14653			csstransitions: function() {
14654				return !!test('transition');
14655			},
14656			cssanimations: function() {
14657				return !!test('animation');
14658			}
14659		};
14660
14661	function test(property, prefixed) {
14662		var result = false,
14663			upper = property.charAt(0).toUpperCase() + property.slice(1);
14664
14665		$.each((property + ' ' + prefixes.join(upper + ' ') + upper).split(' '), function(i, property) {
14666			if (style[property] !== undefined) {
14667				result = prefixed ? property : true;
14668				return false;
14669			}
14670		});
14671
14672		return result;
14673	}
14674
14675	function prefixed(property) {
14676		return test(property, true);
14677	}
14678
14679	if (tests.csstransitions()) {
14680		/* jshint -W053 */
14681		$.support.transition = new String(prefixed('transition'))
14682		$.support.transition.end = events.transition.end[ $.support.transition ];
14683	}
14684
14685	if (tests.cssanimations()) {
14686		/* jshint -W053 */
14687		$.support.animation = new String(prefixed('animation'))
14688		$.support.animation.end = events.animation.end[ $.support.animation ];
14689	}
14690
14691	if (tests.csstransforms()) {
14692		/* jshint -W053 */
14693		$.support.transform = new String(prefixed('transform'));
14694		$.support.transform3d = tests.csstransforms3d();
14695	}
14696
14697})(window.Zepto || window.jQuery, window, document);
14698
14699(function(window, document, undefined) {
14700
14701	/**
14702	 * Find the absolute position of an element
14703	 */
14704	var absPos = function(element) {
14705		var offsetLeft, offsetTop;
14706		offsetLeft = offsetTop = 0;
14707		if (element.offsetParent) {
14708			do {
14709				offsetLeft += element.offsetLeft;
14710				offsetTop += element.offsetTop;
14711			} while (element = element.offsetParent);
14712		}
14713		return [offsetLeft, offsetTop];
14714	};
14715
14716	/**
14717	 * @constructor Progress Circle class
14718	 * @param params.canvas Canvas on which the circles will be drawn.
14719	 * @param params.minRadius Inner radius of the innermost circle, in px.
14720	 * @param params.arcWidth Width of each circle(to be more accurate, ring).
14721	 * @param params.gapWidth Space between each circle.
14722	 * @param params.centerX X coordinate of the center of circles.
14723	 * @param params.centerY Y coordinate of the center of circles.
14724	 * @param params.infoLineBaseAngle Base angle of the info line.
14725	 * @param params.infoLineAngleInterval Angles between the info lines.
14726	 */
14727	var ProgressCircle = function(params) {
14728		this.canvas = params.canvas;
14729		this.minRadius = params.minRadius || 15;
14730		this.arcWidth = params.arcWidth || 5;
14731		this.gapWidth = params.gapWidth || 3;
14732		this.centerX = params.centerX || this.canvas.width / 2;
14733		this.centerY = params.centerY || this.canvas.height / 2;
14734		this.infoLineLength = params.infoLineLength || 60;
14735		this.horizLineLength = params.horizLineLength || 10;
14736		this.infoLineAngleInterval = params.infoLineAngleInterval || Math.PI / 8;
14737		this.infoLineBaseAngle = params.infoLineBaseAngle || Math.PI / 6;
14738
14739		this.context = this.canvas.getContext('2d');
14740
14741		this.width = this.canvas.width;
14742		this.height = this.canvas.height;
14743
14744		this.circles = [];
14745		this.runningCount = 0;
14746	};
14747
14748	ProgressCircle.prototype = {
14749		constructor: ProgressCircle,
14750
14751		/**
14752		 * @method Adds an progress monitor entry.
14753		 * @param params.fillColor Color to fill in the circle.
14754		 * @param params.outlineColor Color to outline the circle.
14755		 * @param params.progressListener Callback function to fetch the progress.
14756		 * @param params.infoListener Callback function to fetch the info.
14757		 * @returns this
14758		 */
14759		addEntry: function(params) {
14760			this.circles.push(new Circle({
14761				canvas: this.canvas,
14762				context: this.context,
14763				centerX: this.centerX,
14764				centerY: this.centerY,
14765				innerRadius: this.minRadius + this.circles.length *
14766					(this.gapWidth + this.arcWidth),
14767				arcWidth: this.arcWidth,
14768				infoLineLength: this.infoLineLength,
14769				horizLineLength: this.horizLineLength,
14770
14771				id: this.circles.length,
14772				fillColor: params.fillColor,
14773				outlineColor: params.outlineColor,
14774				progressListener: params.progressListener,
14775				infoListener: params.infoListener,
14776				infoLineAngle: this.infoLineBaseAngle +
14777					this.circles.length * this.infoLineAngleInterval,
14778			}));
14779
14780			return this;
14781		},
14782
14783		/**
14784		 * @method Starts the monitor and updates with the given interval.
14785		 * @param interval Interval between updates, in millisecond.
14786		 * @returns this
14787		 */
14788		start: function(interval) {
14789			var self = this;
14790			this.timer = setInterval(function() {
14791				self._update();
14792			}, interval || 33);
14793
14794			return this;
14795		},
14796
14797		/**
14798		 * @method Stop the animation.
14799		 */
14800		stop: function() {
14801			clearTimeout(this.timer);
14802		},
14803
14804		/**
14805		 * @private
14806		 * @method Call update on each circle and redraw them.
14807		 * @returns this
14808		 */
14809		_update: function() {
14810			this._clear();
14811			this.circles.forEach(function(circle, idx, array) {
14812				circle.update();
14813			});
14814
14815			return this;
14816		},
14817
14818		/**
14819		 * @private
14820		 * @method Clear the canvas.
14821		 * @returns this
14822		 */
14823		_clear: function() {
14824			this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
14825
14826			return this;
14827		},
14828
14829	};
14830
14831	/**
14832	 * @private
14833	 * @class Individual progress circle.
14834	 * @param params.canvas Canvas on which the circle will be drawn.
14835	 * @param params.context Context of the canvas.
14836	 * @param params.innerRadius Inner radius of the circle, in px.
14837	 * @param params.arcWidth Width of each arc(circle).
14838	 * @param params.gapWidth Distance between each arc.
14839	 * @param params.centerX X coordinate of the center of circles.
14840	 * @param params.centerY Y coordinate of the center of circles.
14841	 * @param params.fillColor Color to fill in the circle.
14842	 * @param params.outlineColor Color to outline the circle.
14843	 * @param params.progressListener Callback function to fetch the progress.
14844	 * @param params.infoListener Callback function to fetch the info.
14845	 * @param params.infoLineAngle Angle of info line.
14846	 */
14847	var Circle = function(params) {
14848		this.id = params.id;
14849		this.canvas = params.canvas;
14850		this.context = params.context;
14851		this.centerX = params.centerX;
14852		this.centerY = params.centerY;
14853		this.arcWidth = params.arcWidth;
14854		this.innerRadius = params.innerRadius || 0;
14855		this.fillColor = params.fillColor || '#fff';
14856		this.outlineColor = params.outlineColor || this.fillColor;
14857		this.progressListener = params.progressListener;
14858		this.infoLineLength = params.infoLineLength || 250;
14859		this.horizLineLength = params.horizLineLength || 50;
14860		this.infoListener = params.infoListener;
14861		this.infoLineAngle = params.infoLineAngle;
14862
14863		this.outerRadius = this.innerRadius + this.arcWidth;
14864
14865		// If the info listener is not registered, then don't calculate
14866		// the related coordinates
14867		if (!this.infoListener) return;
14868
14869		// calculate the info-line turning points
14870		var angle = this.infoLineAngle,
14871			arcDistance = (this.innerRadius + this.outerRadius) / 2,
14872
14873			sinA = Math.sin(angle),
14874			cosA = Math.cos(angle);
14875
14876		this.infoLineStartX = this.centerX + sinA * arcDistance;
14877		this.infoLineStartY = this.centerY - cosA * arcDistance;
14878
14879		this.infoLineMidX = this.centerX + sinA * this.infoLineLength;
14880		this.infoLineMidY = this.centerY - cosA * this.infoLineLength;
14881
14882		this.infoLineEndX = this.infoLineMidX +
14883			 (sinA < 0 ? -this.horizLineLength : this.horizLineLength);
14884		this.infoLineEndY = this.infoLineMidY;
14885
14886		var infoText = document.createElement('div'),
14887			style = infoText.style;
14888
14889		style.color = this.fillColor;
14890		style.position = 'absolute';
14891		style.left = this.infoLineEndX + absPos(this.canvas)[0] + 'px';
14892		// style.top will be calculated in the `drawInfo` method. Since
14893		// user may want to change the size of the font, so the top offset
14894		// must be updated in each loop.
14895		infoText.className = 'ProgressCircleInfo'; // For css styling
14896		infoText.id = 'progress_circle_info_' + this.id;
14897		document.body.appendChild(infoText);
14898		this.infoText = infoText;
14899	};
14900
14901	Circle.prototype = {
14902		constructor: Circle,
14903
14904		update: function() {
14905			this.progress = this.progressListener();
14906			this._draw();
14907
14908			if (this.infoListener) {
14909				this.info = this.infoListener();
14910				this._drawInfo();
14911			}
14912		},
14913
14914		/**
14915		 * @private
14916		 * @method Draw the circle on the canvas.
14917		 * @returns this
14918		 */
14919		_draw: function() {
14920			var ctx = this.context,
14921
14922				ANGLE_OFFSET = -Math.PI / 2,
14923
14924				startAngle = 0 + ANGLE_OFFSET,
14925				endAngle= startAngle + this.progress * Math.PI * 2,
14926
14927				x = this.centerX,
14928				y = this.centerY,
14929
14930				innerRadius = this.innerRadius - this.arcWidth - 1,
14931				outerRadius = this.outerRadius - this.arcWidth - 1;
14932
14933			if ( innerRadius < 0 )
14934				return;
14935
14936			ctx.fillStyle = this.fillColor;
14937			ctx.strokeStyle = this.outlineColor;
14938
14939			ctx.beginPath();
14940			ctx.arc(x, y, innerRadius, startAngle, endAngle, false);
14941			ctx.arc(x, y, outerRadius, endAngle, startAngle, true);
14942			ctx.closePath();
14943			ctx.stroke();
14944			ctx.fill();
14945
14946			return this;
14947		},
14948
14949		/**
14950		 * @private
14951		 * @method Draw the info lines and info text.
14952		 * @returns this
14953		 */
14954		_drawInfo: function() {
14955
14956			var pointList, lineHeight;
14957
14958			pointList = [
14959				[this.infoLineStartX, this.infoLineStartY],
14960				[this.infoLineMidX, this.infoLineMidY],
14961				[this.infoLineEndX, this.infoLineEndY],
14962			];
14963			this._drawSegments(pointList, false);
14964
14965			this.infoText.innerHTML = this.info;
14966
14967			lineHeight = this.infoText.offsetHeight;
14968			this.infoText.style.top = this.infoLineEndY +
14969				absPos(this.canvas)[1] - lineHeight / 2 + 'px';
14970
14971			return this;
14972		},
14973
14974		/**
14975		 * @private
14976		 * @method Helper function to draw the segments
14977		 * @param pointList An array of points in the form of [x, y].
14978		 * @param close Whether to connect the first and last point.
14979		 */
14980		_drawSegments: function(pointList, close) {
14981			var ctx = this.context;
14982
14983			ctx.beginPath();
14984			ctx.moveTo(pointList[0][0], pointList[0][1]);
14985			for (var i = 1; i < pointList.length; ++i) {
14986				ctx.lineTo(pointList[i][0], pointList[i][1]);
14987			}
14988
14989			if (close) {
14990				ctx.closePath();
14991			}
14992			ctx.stroke();
14993		},
14994	};
14995
14996	window.ProgressCircle = ProgressCircle;
14997
14998})(window, document);
14999
15000
15001
15002
15003/* =========================================================
15004 * jquery.vc_chart.js v1.0
15005 * =========================================================
15006 * Copyright 2013 Wpbakery
15007 *
15008 * Jquery chart plugin for the Visual Composer.
15009 * ========================================================= */
15010(function($){
15011	/**
15012	 * Pie chart animated.
15013	 * @param element - DOM element
15014	 * @param options - settings object.
15015	 * @constructor
15016	 */
15017	var VcChart = function(element, options) {
15018		this.el = element;
15019		this.$el = $(this.el);
15020		var $this = this;
15021		$this.options = $.extend({
15022			color: 'wpb_button',
15023			units: '',
15024			width: '',
15025			label_selector: '.vc_pie_chart_value',
15026			back_selector: '.vc_pie_chart_back',
15027			responsive: true
15028		}, options);
15029		$this.init();
15030	};
15031	VcChart.prototype = {
15032		constructor: VcChart,
15033		_progress_v: 0,
15034		animated: false,
15035		colors: {
15036			'wpb_button': 'rgba(247, 247, 247, 1)',
15037			'btn-primary': 'rgba(0, 136, 204, 1)',
15038			'btn-info': 'rgba(88, 185, 218, 1)',
15039			'btn-success': 'rgba(106, 177, 101, 1)',
15040			'btn-warning': 'rgba(255, 153, 0, 1)',
15041			'btn-danger': 'rgba(255, 103, 91, 1)',
15042			'btn-inverse': 'rgba(85, 85, 85, 1)'
15043		},
15044		init: function() {
15045			this.setupColor();
15046			this.value = this.$el.data('pie-value')/100;
15047			this.label_value = this.$el.data('pie-label-value') || this.$el.data('pie-value');
15048			this.$wrapper = $('.vc_pie_wrapper', this.$el);
15049			this.$label = $(this.options.label_selector, this.$el);
15050			this.$back = $(this.options.back_selector, this.$el);
15051			this.$canvas = this.$el.find('canvas');
15052			this.arcWidth = this.$el.data('pie-width') * 2;
15053			this.draw();
15054			this.setWayPoint();
15055			if(this.options.responsive === true) this.setResponsive();
15056			if (UNCODE.isMobile) this._progress_v = this.value;
15057		},
15058		setupColor: function() {
15059			// if(typeof this.colors[this.options.color] !== 'undefined') {
15060			//     this.color = this.colors[this.options.color];
15061			// } else if(typeof this.options.color === 'string' && this.options.color.match(/^rgba?\([^\)]+\)/)) {
15062			//     this.color = this.options.color;
15063			// } else {
15064			//     this.color = 'rgba(247, 247, 247, 0.2)';
15065			// }
15066			if(typeof this.options.color !== 'undefined') {
15067				this.color = this.options.color;
15068			} else this.color = 'rgba(247, 247, 247, 0.2)';
15069
15070		},
15071		setResponsive: function() {
15072			var that = this;
15073			if (!UNCODE.isMobile) {
15074				$(window).resize(function(){
15075					if(that.animated === true) that.circle.stop();
15076					that.draw(true);
15077				});
15078			}
15079		},
15080		draw: function(redraw) {
15081			var w = this.$el.addClass('vc_ready').width() * 2,
15082				border_w = this.arcWidth,
15083				radius;
15084			if(!w) w = this.$el.parents(':visible').first().width()-2;
15085			//w = Math.round(w/100*80);
15086			//if (w < 250) w = 250;
15087			radius = w/2;
15088			this.$wrapper.css({"width" : (w / 2) + "px"});
15089			this.$label.css({"width" : (w / 2), "height" : (w / 2), "line-height" : (w / 2)+"px"});
15090			this.$back.css({"width" : (w / 2), "height" : (w / 2)});
15091			this.$canvas.attr({"width" : w + "px", "height" : w + "px"});
15092			this.$el.addClass('vc_ready');
15093			this.circle = new ProgressCircle({
15094				canvas: this.$canvas.get(0),
15095				minRadius: radius,
15096				arcWidth: border_w
15097			});
15098			if(redraw === true && this.animated === true) {
15099				this._progress_v = this.value;
15100				this.circle.addEntry({
15101					fillColor: this.color,
15102					progressListener: $.proxy(this.setProgress, this)
15103				}).start();
15104			}
15105		},
15106		setProgress: function() {
15107			if (this._progress_v >= this.value) {
15108				this.circle.stop();
15109				this.animated = true;
15110				this.$label.html(this.label_value + this.options.units);
15111				return this._progress_v;
15112			}
15113			this._progress_v += 0.01;
15114			if (!isNaN(this.label_value)) {
15115				var label_value = this._progress_v/this.value*this.label_value;
15116				var val = Math.round(label_value) + this.options.units;
15117				this.$label.html(val);
15118			} else this.$label.html(this.label_value + this.options.units);
15119			return this._progress_v;
15120		},
15121		animate: function() {
15122			if(this.animated !== true) {
15123				this.circle.addEntry({
15124					fillColor: this.color,
15125					progressListener: $.proxy(this.setProgress, this)
15126				}).start(10);
15127			}
15128		},
15129		setWayPoint: function() {
15130			if (typeof $.fn.waypoint !== 'undefined' && !UNCODE.isMobile) {
15131				this.$el.waypoint($.proxy(this.animate, this), { offset: '85%' });
15132			} else {
15133				this.animate();
15134			}
15135		}
15136	};
15137	/**
15138	 * jQuery plugin
15139	 * @param option - object with settings
15140	 * @return {*}
15141	 */
15142	$.fn.vcChat = function(option, value) {
15143		return this.each(function () {
15144			var $this = $(this),
15145				data = $this.data('vc_chart'),
15146				options = typeof option === 'object' ? option : {
15147					color: $this.data('pie-color'),
15148					units: $this.data('pie-units')
15149				};
15150			if (typeof option == 'undefined') $this.data('vc_chart', (data = new VcChart(this, options)));
15151			if (typeof option == 'string') data[option](value);
15152		});
15153	};
15154	/**
15155	 * Allows users to rewrite function inside theme.
15156	 */
15157	if ( typeof window['vc_pieChart'] !== 'function' ) {
15158		window.vc_pieChart = function() {
15159			$('.vc_pie_chart:visible:not(.vc_ready)').vcChat();
15160		}
15161	}
15162	$(document).ready(function(){
15163		!window.vc_iframe && vc_pieChart();
15164
15165		/**
15166		* Tab and collapse integration.
15167		*/
15168		$('.nav-tabs a').on('shown.bs.tab', function(e){
15169			var $cont = $(e.target).closest('.tab-container'),
15170				$active = $('.tab-pane.active', $cont);
15171			$('.vc_pie_chart:not(.vc_ready)', $active).vcChat();
15172		});
15173
15174		$('.panel-collapse').on('shown.bs.collapse', function (e) {
15175			$('.vc_pie_chart:not(.vc_ready)', e.target).vcChat();
15176		})
15177	});
15178
15179})(window.jQuery);
15180
15181/* Progress bar
15182 ---------------------------------------------------------- */
15183function uncode_progress_bar() {    
15184	jQuery.each(jQuery('.vc_progress_bar'), function(index, val) {
15185		if (UNCODE.isUnmodalOpen && !val.closest('#unmodal-content')) {
15186			return;
15187		}
15188		if (!UNCODE.isMobile) {
15189			new Waypoint({
15190				context: UNCODE.isUnmodalOpen ? document.getElementById('unmodal-content') : window,
15191				element: val,
15192				handler: function() {
15193					var element = jQuery(this.element);
15194					element.find('.vc_single_bar').each(function(index) {          
15195						var $this = jQuery(this),
15196							            bar = $this.find('.vc_bar'),
15197							            val = bar.data('percentage-value');          
15198						setTimeout(function() {            
15199							bar.css({
15200								"width": val + '%'
15201							});          
15202						}, index * 200);        
15203					});
15204				},
15205				offset: '80%'
15206			});
15207		} else {
15208			var element = jQuery(val);
15209			element.find('.vc_single_bar').each(function(index) {          
15210				var $this = jQuery(this),
15211					            bar = $this.find('.vc_bar'),
15212					            val = bar.data('percentage-value');          
15213				setTimeout(function() {            
15214					bar.css({
15215						"width": val + '%'
15216					});          
15217				}, index * 200);        
15218			});
15219		}
15220	});
15221};
15222uncode_progress_bar();
15223
15224/*!
15225 * jquery.counterup.js 1.0
15226 *
15227 * Copyright 2013, Benjamin Intal http://gambit.ph @bfintal
15228 * Released under the GPL v2 License
15229 *
15230 * Date: Nov 26, 2013
15231 */
15232(function($) {
15233	"use strict";
15234	$.fn.counterUp = function(options) {
15235		// Defaults
15236		var settings = $.extend({
15237			'time': 400,
15238			'delay': 10
15239		}, options);
15240		return this.each(function() {
15241			// Store the object
15242			var $this = $(this);
15243			var $settings = settings;
15244			var counterUpper = function() {
15245				var nums = [];
15246				var divisions = $settings.time / $settings.delay;
15247				var numReal = $this.attr('data-val'),
15248				num = numReal;
15249				var isComma = /[0-9]+,[0-9]+/.test(num);
15250				num = num.replace(/,/g, '');
15251				var isInt = /^[0-9]+$/.test(num);
15252				var isFloat = /^[0-9]+\.[0-9]+$/.test(num);
15253				var decimalPlaces = isFloat ? (num.split('.')[1] || []).length : 0;
15254				// Generate list of incremental numbers to display
15255				for (var i = divisions; i >= 1; i--) {
15256					// Preserve as int if input was int
15257					var newNum = parseInt(num / divisions * i);
15258					// Preserve float if input was float
15259					if (isFloat) {
15260						newNum = parseFloat(num / divisions * i).toFixed(decimalPlaces);
15261					}
15262					// Preserve commas if input had commas
15263					if (isComma) {
15264						while (/(\d+)(\d{3})/.test(newNum.toString())) {
15265							newNum = newNum.toString().replace(/(\d+)(\d{3})/, '$1' + ',' + '$2');
15266						}
15267					}
15268					nums.unshift(newNum);
15269				}
15270				nums.push(numReal);
15271				$this.data('counterup-nums', nums);
15272				$this.text('0');
15273				// Updates the number until we're done
15274				var f = function() {
15275					if ($this.data('counterup-nums') != null) {
15276						$this.text($this.data('counterup-nums').shift());
15277						if ($this.data('counterup-nums').length) {
15278							setTimeout($this.data('counterup-func'), $settings.delay);
15279						} else {
15280							delete $this.data('counterup-nums');
15281							$this.data('counterup-nums', null);
15282							$this.data('counterup-func', null);
15283						}
15284					}
15285				};
15286				$this.data('counterup-func', f);
15287				// Start the count up
15288				setTimeout($this.data('counterup-func'), $settings.delay);
15289			};
15290			// Perform counts when the element gets into view
15291			new Waypoint({
15292				context: UNCODE.isUnmodalOpen ? document.getElementById('unmodal-content') : window,
15293				element: this,
15294				handler: function() {
15295					counterUpper();
15296					if (!UNCODE.isUnmodalOpen) {
15297						this.destroy();
15298					}
15299				},
15300				offset: '100%'
15301			});
15302		});
15303	};
15304})(jQuery);
15305
15306/*!
15307 * The Final Countdown for jQuery v2.0.4 (http://hilios.github.io/jQuery.countdown/)
15308 * Copyright (c) 2014 Edson Hilios
15309 *
15310 * Permission is hereby granted, free of charge, to any person obtaining a copy of
15311 * this software and associated documentation files (the "Software"), to deal in
15312 * the Software without restriction, including without limitation the rights to
15313 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
15314 * the Software, and to permit persons to whom the Software is furnished to do so,
15315 * subject to the following conditions:
15316 *
15317 * The above copyright notice and this permission notice shall be included in all
15318 * copies or substantial portions of the Software.
15319 *
15320 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15321 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15322 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15323 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
15324 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
15325 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15326 */
15327(function(factory) {
15328    "use strict";
15329    if (typeof define === "function" && define.amd) {
15330        define([ "jquery" ], factory);
15331    } else {
15332        factory(jQuery);
15333    }
15334})(function($) {
15335    "use strict";
15336    var PRECISION = 100;
15337    var instances = [], matchers = [];
15338    matchers.push(/^[0-9]*$/.source);
15339    matchers.push(/([0-9]{1,2}\/){2}[0-9]{4}( [0-9]{1,2}(:[0-9]{2}){2})?/.source);
15340    matchers.push(/[0-9]{4}([\/\-][0-9]{1,2}){2}( [0-9]{1,2}(:[0-9]{2}){2})?/.source);
15341    matchers = new RegExp(matchers.join("|"));
15342    function parseDateString(dateString) {
15343        if (dateString instanceof Date) {
15344            return dateString;
15345        }
15346        if (String(dateString).match(matchers)) {
15347            if (String(dateString).match(/^[0-9]*$/)) {
15348                dateString = Number(dateString);
15349            }
15350            if (String(dateString).match(/\-/)) {
15351                dateString = String(dateString).replace(/\-/g, "/");
15352            }
15353            return new Date(dateString);
15354        } else {
15355            throw new Error("Couldn't cast `" + dateString + "` to a date object.");
15356        }
15357    }
15358    var DIRECTIVE_KEY_MAP = {
15359        Y: "years",
15360        m: "months",
15361        w: "weeks",
15362        d: "days",
15363        D: "totalDays",
15364        H: "hours",
15365        M: "minutes",
15366        S: "seconds"
15367    };
15368    function strftime(offsetObject) {
15369        return function(format) {
15370            var directives = format.match(/%(-|!)?[A-Z]{1}(:[^;]+;)?/gi);
15371            if (directives) {
15372                for (var i = 0, len = directives.length; i < len; ++i) {
15373                    var directive = directives[i].match(/%(-|!)?([a-zA-Z]{1})(:[^;]+;)?/), regexp = new RegExp(directive[0]), modifier = directive[1] || "", plural = directive[3] || "", value = null;
15374                    directive = directive[2];
15375                    if (DIRECTIVE_KEY_MAP.hasOwnProperty(directive)) {
15376                        value = DIRECTIVE_KEY_MAP[directive];
15377                        value = Number(offsetObject[value]);
15378                    }
15379                    if (value !== null) {
15380                        if (modifier === "!") {
15381                  
15381          value = pluralize(plural, value);
15382                        }
15383                        if (modifier === "") {
15384                            if (value < 10) {
15385                                value = "0" + value.toString();
15386                            }
15387                        }
15388                        format = format.replace(regexp, value.toString());
15389                    }
15390                }
15391            }
15392            format = format.replace(/%%/, "%");
15393            return format;
15394        };
15395    }
15396    function pluralize(format, count) {
15397        var plural = "s", singular = "";
15398        if (format) {
15399            format = format.replace(/(:|;|\s)/gi, "").split(/\,/);
15400            if (format.length === 1) {
15401                plural = format[0];
15402            } else {
15403                singular = format[0];
15404                plural = format[1];
15405            }
15406        }
15407        if (Math.abs(count) === 1) {
15408            return singular;
15409        } else {
15410            return plural;
15411        }
15412    }
15413    var Countdown = function(el, finalDate, callback) {
15414        this.el = el;
15415        this.$el = $(el);
15416        this.interval = null;
15417        this.offset = {};
15418        this.instanceNumber = instances.length;
15419        instances.push(this);
15420        this.$el.data("countdown-instance", this.instanceNumber);
15421        if (callback) {
15422            this.$el.on("update.countdown", callback);
15423            this.$el.on("stoped.countdown", callback);
15424            this.$el.on("finish.countdown", callback);
15425        }
15426        this.setFinalDate(finalDate);
15427        this.start();
15428    };
15429    $.extend(Countdown.prototype, {
15430        start: function() {
15431            if (this.interval !== null) {
15432                clearInterval(this.interval);
15433            }
15434            var self = this;
15435            this.update();
15436            this.interval = setInterval(function() {
15437                self.update.call(self);
15438            }, PRECISION);
15439        },
15440        stop: function() {
15441            clearInterval(this.interval);
15442            this.interval = null;
15443            this.dispatchEvent("stoped");
15444        },
15445        pause: function() {
15446            this.stop.call(this);
15447        },
15448        resume: function() {
15449            this.start.call(this);
15450        },
15451        remove: function() {
15452            this.stop();
15453            instances[this.instanceNumber] = null;
15454            delete this.$el.data().countdownInstance;
15455        },
15456        setFinalDate: function(value) {
15457            this.finalDate = parseDateString(value);
15458        },
15459        update: function() {
15460            if (this.$el.closest("html").length === 0) {
15461                this.remove();
15462                return;
15463            }
15464            this.totalSecsLeft = this.finalDate.getTime() - new Date().getTime();
15465            this.totalSecsLeft = Math.ceil(this.totalSecsLeft / 1e3);
15466            this.totalSecsLeft = this.totalSecsLeft < 0 ? 0 : this.totalSecsLeft;
15467            this.offset = {
15468                seconds: this.totalSecsLeft % 60,
15469                minutes: Math.floor(this.totalSecsLeft / 60) % 60,
15470                hours: Math.floor(this.totalSecsLeft / 60 / 60) % 24,
15471                days: Math.floor(this.totalSecsLeft / 60 / 60 / 24) % 7,
15472                totalDays: Math.floor(this.totalSecsLeft / 60 / 60 / 24),
15473                weeks: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 7),
15474                months: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 30),
15475                years: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 365)
15476            };
15477            if (this.totalSecsLeft === 0) {
15478                this.stop();
15479                this.dispatchEvent("finish");
15480            } else {
15481                this.dispatchEvent("update");
15482            }
15483        },
15484        dispatchEvent: function(eventName) {
15485            var event = $.Event(eventName + ".countdown");
15486            event.finalDate = this.finalDate;
15487            event.offset = $.extend({}, this.offset);
15488            event.strftime = strftime(this.offset);
15489            this.$el.trigger(event);
15490        }
15491    });
15492    $.fn.countdown = function() {
15493        var argumentsArray = Array.prototype.slice.call(arguments, 0);
15494        return this.each(function() {
15495            var instanceNumber = $(this).data("countdown-instance");
15496            if (instanceNumber !== undefined) {
15497                var instance = instances[instanceNumber], method = argumentsArray[0];
15498                if (Countdown.prototype.hasOwnProperty(method)) {
15499                    instance[method].apply(instance, argumentsArray.slice(1));
15500                } else if (String(method).match(/^[$A-Z_][0-9A-Z_$]*$/i) === null) {
15501                    instance.setFinalDate.call(instance, method);
15502                    instance.start();
15503                } else {
15504                    $.error("Method %s does not exist on jQuery.countdown".replace(/\%s/gi, method));
15505                }
15506            } else {
15507                new Countdown(this, argumentsArray[0], argumentsArray[1]);
15508            }
15509        });
15510    };
15511});
15512
15513/*! waitForImages jQuery Plugin - v2.0.2 - 2015-05-05
15514* https://github.com/alexanderdickson/waitForImages
15515* Copyright (c) 2015 Alex Dickson; Licensed MIT */
15516;(function (factory) {
15517    if (typeof define === 'function' && define.amd) {
15518        // AMD. Register as an anonymous module.
15519        define(['jquery'], factory);
15520    } else if (typeof exports === 'object') {
15521        // CommonJS / nodejs module
15522        module.exports = factory(require('jquery'));
15523    } else {
15524        // Browser globals
15525        factory(jQuery);
15526    }
15527}(function ($) {
15528    // Namespace all events.
15529    var eventNamespace = 'waitForImages';
15530
15531    // CSS properties which contain references to images.
15532    $.waitForImages = {
15533        hasImageProperties: [
15534            'backgroundImage',
15535            'listStyleImage',
15536            'borderImage',
15537            'borderCornerImage',
15538            'cursor'
15539        ],
15540        hasImageAttributes: ['srcset']
15541    };
15542
15543    // Custom selector to find `img` elements that have a valid `src`
15544    // attribute and have not already loaded.
15545    $.expr[':'].uncached = function (obj) {
15546        // Ensure we are dealing with an `img` element with a valid
15547        // `src` attribute.
15548        if (!$(obj).is('img[src][src!=""]')) {
15549            return false;
15550        }
15551
15552        return !obj.complete;
15553    };
15554
15555    $.fn.waitForImages = function () {
15556
15557        var allImgsLength = 0;
15558        var allImgsLoaded = 0;
15559        var deferred = $.Deferred();
15560
15561        var finishedCallback;
15562        var eachCallback;
15563        var waitForAll;
15564
15565        // Handle options object (if passed).
15566        if ($.isPlainObject(arguments[0])) {
15567
15568            waitForAll = arguments[0].waitForAll;
15569            eachCallback = arguments[0].each;
15570            finishedCallback = arguments[0].finished;
15571
15572        } else {
15573
15574            // Handle if using deferred object and only one param was passed in.
15575            if (arguments.length === 1 && $.type(arguments[0]) === 'boolean') {
15576                waitForAll = arguments[0];
15577            } else {
15578                finishedCallback = arguments[0];
15579                eachCallback = arguments[1];
15580                waitForAll = arguments[2];
15581            }
15582
15583        }
15584
15585        // Handle missing callbacks.
15586        finishedCallback = finishedCallback || $.noop;
15587        eachCallback = eachCallback || $.noop;
15588
15589        // Convert waitForAll to Boolean
15590        waitForAll = !! waitForAll;
15591
15592        // Ensure callbacks are functions.
15593        if (!$.isFunction(finishedCallback) || !$.isFunction(eachCallback)) {
15594            throw new TypeError('An invalid callback was supplied.');
15595        }
15596
15597        this.each(function () {
15598            // Build a list of all imgs, dependent on what images will
15599            // be considered.
15600            var obj = $(this);
15601            var allImgs = [];
15602            // CSS properties which may contain an image.
15603            var hasImgProperties = $.waitForImages.hasImageProperties || [];
15604            // Element attributes which may contain an image.
15605            var hasImageAttributes = $.waitForImages.hasImageAttributes || [];
15606            // To match `url()` references.
15607            // Spec: http://www.w3.org/TR/CSS2/syndata.html#value-def-uri
15608            var matchUrl = /url\(\s*(['"]?)(.*?)\1\s*\)/g;
15609
15610            if (waitForAll) {
15611
15612                // Get all elements (including the original), as any one of
15613                // them could have a background image.
15614                obj.find('*').addBack().each(function () {
15615                    var element = $(this);
15616
15617                    // If an `img` element, add it. But keep iterating in
15618                    // case it has a background image too.
15619                    if (element.is('img:uncached')) {
15620                        allImgs.push({
15621                            src: element.attr('src'),
15622                            element: element[0]
15623                        });
15624                    }
15625
15626                    $.each(hasImgProperties, function (i, property) {
15627                        var propertyValue = element.css(property);
15628                        var match;
15629
15630                        // If it doesn't contain this property, skip.
15631                        if (!propertyValue) {
15632                            return true;
15633                        }
15634
15635                        // Get all url() of this element.
15636                        while (match = matchUrl.exec(propertyValue)) {
15637                            allImgs.push({
15638                                src: match[2],
15639                                element: element[0]
15640                            });
15641                        }
15642                    });
15643
15644                    $.each(hasImageAttributes, function (i, attribute) {
15645                        var attributeValue = element.attr(attribute);
15646                        var attributeValues;
15647
15648                        // If it doesn't contain this property, skip.
15649                        if (!attributeValue) {
15650                            return true;
15651                        }
15652
15653                        // Check for multiple comma separated images
15654                        attributeValues = attributeValue.split(',');
15655
15656                        $.each(attributeValues, function(i, value) {
15657                            // Trim value and get string before first
15658                            // whitespace (for use with srcset).
15659                            value = $.trim(value).split(' ')[0];
15660                            allImgs.push({
15661                                src: value,
15662                                element: element[0]
15663                            });
15664                        });
15665                    });
15666                });
15667            } else {
15668                // For images only, the task is simpler.
15669                obj.find('img:uncached')
15670                    .each(function () {
15671                    allImgs.push({
15672                        src: this.src,
15673                        element: this
15674                    });
15675                });
15676            }
15677
15678            allImgsLength = allImgs.length;
15679            allImgsLoaded = 0;
15680
15681            // If no images found, don't bother.
15682            if (allImgsLength === 0) {
15683                finishedCallback.call(obj[0]);
15684                deferred.resolveWith(obj[0]);
15685            }
15686
15687            $.each(allImgs, function (i, img) {
15688
15689                var image = new Image();
15690                var events =
15691                  'load.' + eventNamespace + ' error.' + eventNamespace;
15692
15693                // Handle the image loading and error with the same callback.
15694                $(image).one(events, function me (event) {
15695                    // If an error occurred with loading the image, set the
15696                    // third argument accordingly.
15697                    var eachArguments = [
15698                        allImgsLoaded,
15699                        allImgsLength,
15700                        event.type == 'load'
15701                    ];
15702                    allImgsLoaded++;
15703
15704                    eachCallback.apply(img.element, eachArguments);
15705                    deferred.notifyWith(img.element, eachArguments);
15706
15707                    // Unbind the event listeners. I use this in addition to
15708                    // `one` as one of those events won't be called (either
15709                    // 'load' or 'error' will be called).
15710                    $(this).off(events, me);
15711
15712                    if (allImgsLoaded == allImgsLength) {
15713                        finishedCallback.call(obj[0]);
15714                        deferred.resolveWith(obj[0]);
15715                        return false;
15716                    }
15717
15718                });
15719
15720                image.src = img.src;
15721            });
15722        });
15723
15724        return deferred.promise();
15725
15726    };
15727}));
15728
15729/**
15730 * @preserve  textfill
15731 * @name      jquery.textfill.js
15732 * @author    Russ Painter
15733 * @author    Yu-Jie Lin
15734 * @author    Alexandre Dantas
15735 * @version   0.6.0
15736 * @date      2014-08-19
15737 * @copyright (c) 2014 Alexandre Dantas
15738 * @copyright (c) 2012-2013 Yu-Jie Lin
15739 * @copyright (c) 2009 Russ Painter
15740 * @license   MIT License
15741 * @homepage  https://github.com/jquery-textfill/jquery-textfill
15742 * @example   http://jquery-textfill.github.io/jquery-textfill/index.html
15743 */
15744; (function($) {
15745
15746	/**
15747	 * Resizes an inner element's font so that the
15748	 * inner element completely fills the outer element.
15749	 *
15750	 * @param {Object} options User options that take
15751	 *                         higher precedence when
15752	 *                         merging with the default ones.
15753	 *
15754	 * @return All outer elements processed
15755	 */
15756	$.fn.textfill = function(options) {
15757
15758		// ______  _______ _______ _______ _     _        _______ _______
15759		// |     \ |______ |______ |_____| |     | |         |    |______
15760		// |_____/ |______ |       |     | |_____| |_____    |    ______|
15761        //
15762		// Merging user options with the default values
15763
15764		var defaults = {
15765			debug            : false,
15766			maxFontPixels    : 40,
15767			minFontPixels    : 4,
15768			innerTag         : 'span',
15769			widthOnly        : false,
15770			success          : null, // callback when a resizing is done
15771			callback         : null, // callback when a resizing is done (deprecated, use success)
15772			fail             : null, // callback when a resizing is failed
15773			complete         : null, // callback when all is done
15774			explicitWidth    : null,
15775			explicitHeight   : null,
15776			changeLineHeight : false
15777		};
15778
15779		var Opts = $.extend(defaults, options);
15780
15781		// _______ _     _ __   _ _______ _______ _____  _____  __   _ _______
15782		// |______ |     | | \  | |          |      |   |     | | \  | |______
15783		// |       |_____| |  \_| |_____     |    __|__ |_____| |  \_| ______|
15784		//
15785		// Predefining the awesomeness
15786
15787		// Output arguments to the Debug console
15788		// if "Debug Mode" is enabled
15789		function _debug() {
15790			if (!Opts.debug
15791				||  typeof console       == 'undefined'
15792				||  typeof console.debug == 'undefined') {
15793				return;
15794			}
15795			console.debug.apply(console, arguments);
15796		}
15797
15798		// Output arguments to the Warning console
15799		function _warn() {
15800			if (typeof console      == 'undefined' ||
15801				typeof console.warn == 'undefined') {
15802				return;
15803			}
15804			console.warn.apply(console, arguments);
15805		}
15806
15807		// Outputs all information on the current sizing
15808		// of the font.
15809		function _debug_sizing(prefix, ourText, maxHeight, maxWidth, minFontPixels, maxFontPixels) {
15810
15811			function _m(v1, v2) {
15812
15813				var marker = ' / ';
15814
15815				if (v1 > v2)
15816					marker = ' > ';
15817
15818				else if (v1 == v2)
15819					marker = ' = ';
15820
15821				return marker;
15822			}
15823
15824			_debug(
15825				'[TextFill] '  + prefix + ' { ' +
15826				'font-size: ' + ourText.css('font-size') + ',' +
15827				'Height: '    + ourText.height() + 'px ' + _m(ourText.height(), maxHeight) + maxHeight + 'px,' +
15828				'Width: '     + ourText.width()  + _m(ourText.width() , maxWidth)  + maxWidth + ',' +
15829				'minFontPixels: ' + minFontPixels + 'px, ' +
15830				'maxFontPixels: ' + maxFontPixels + 'px }'
15831			);
15832		}
15833
15834		/**
15835		 * Calculates which size the font can get resized,
15836		 * according to constrains.
15837		 *
15838		 * @param {String} prefix Gets shown on the console before
15839		 *                        all the arguments, if debug mode is on.
15840		 * @param {Object} ourText The DOM element to resize,
15841		 *                         that contains the text.
15842		 * @param {function} func Function called on `ourText` that's
15843		 *                        used to compare with `max`.
15844		 * @param {number} max Maximum value, that gets compared with
15845		 *                     `func` called on `ourText`.
15846		 * @param {number} minFontPixels Minimum value the font can
15847		 *                               get resized to (in pixels).
15848		 * @param {number} maxFontPixels Maximum value the font can
15849		 *                               get resized to (in pixels).
15850		 *
15851		 * @return Size (in pixels) that the font can be resized.
15852		 */
15853		function _sizing(prefix, ourText, func, max, maxHeight, maxWidth, minFontPixels, maxFontPixels) {
15854
15855			_debug_sizing(
15856				prefix, ourText,
15857				maxHeight, maxWidth,
15858				minFontPixels, maxFontPixels
15859			);
15860
15861			// The kernel of the whole plugin, take most attention
15862			// on this part.
15863			//
15864			// This is a loop that keeps increasing the `font-size`
15865			// until it fits the parent element.
15866			//
15867			// - Start from the minimal allowed value (`minFontPixels`)
15868			// - Guesses an average font size (in pixels) for the font,
15869			// - Resizes the text and sees if its size is within the
15870			//   boundaries (`minFontPixels` and `maxFontPixels`).
15871			//   - If so, keep guessing until we break.
15872			//   - If not, return the last calculated size.
15873			//
15874			// I understand this is not optimized and we should
15875			// consider implementing something akin to
15876			// Daniel Hoffmann's answer here:
15877			//
15878			//     http://stackoverflow.com/a/17433451/1094964
15879			//
15880
15881			while (minFontPixels < (maxFontPixels - 1)) {
15882
15883				var fontSize = Math.floor((minFontPixels + maxFontPixels) / 2);
15884				ourText.css('font-size', fontSize);
15885
15886				if (func.call(ourText) <= max) {
15887					minFontPixels = fontSize;
15888
15889					if (func.call(ourText) == max)
15890						break;
15891				}
15892				else
15893					maxFontPixels = fontSize;
15894
15895				_debug_sizing(
15896					prefix, ourText,
15897					maxHeight, maxWidth,
15898					minFontPixels, maxFontPixels
15899				);
15900			}
15901
15902			ourText.css('font-size', maxFontPixels);
15903
15904			if (func.call(ourText) <= max) {
15905				minFontPixels = maxFontPixels;
15906
15907				_debug_sizing(
15908					prefix + '* ', ourText,
15909					maxHeight, maxWidth,
15910					minFontPixels, maxFontPixels
15911				);
15912			}
15913			return minFontPixels;
15914		}
15915
15916		// _______ _______ _______  ______ _______
15917		// |______    |    |_____| |_____/    |
15918		// ______|    |    |     | |    \_    |
15919        //
15920		// Let's get it started (yeah)!
15921
15922		_debug('[TextFill] Start Debug');
15923
15924		this.each(function() {
15925
15926			// Contains the child element we will resize.
15927			// $(this) means the parent container
15928			var ourText = $(Opts.innerTag + ':visible:first', this);
15929
15930			// Will resize to this dimensions.
15931			// Use explicit dimensions when specified
15932			var maxHeight = Opts.explicitHeight || $(this).height();
15933			var maxWidth  = Opts.explicitWidth  || $(this).width();
15934
15935			var oldFontSize = ourText.css('font-size');
15936
15937			var lineHeight  = parseFloat(ourText.css('line-height')) / parseFloat(oldFontSize);
15938
15939			_debug('[TextFill] Inner text: ' + ourText.text());
15940			_debug('[TextFill] All options: ', Opts);
15941			_debug('[TextFill] Maximum sizes: { ' +
15942				   'Height: ' + maxHeight + 'px, ' +
15943				   'Width: '  + maxWidth  + 'px' + ' }'
15944				  );
15945
15946			var minFontPixels = Opts.minFontPixels;
15947
15948			// Remember, if this `maxFontPixels` is negative,
15949			// the text will resize to as long as the container
15950			// can accomodate
15951			var maxFontPixels = (Opts.maxFontPixels <= 0 ?
15952								 maxHeight :
15953								 Opts.maxFontPixels);
15954
15955
15956			// Let's start it all!
15957
15958			// 1. Calculate which `font-size` would
15959			//    be best for the Height
15960			var fontSizeHeight = undefined;
15961
15962			if (! Opts.widthOnly)
15963				fontSizeHeight = _sizing(
15964					'Height', ourText,
15965					$.fn.height, maxHeight,
15966					maxHeight, maxWidth,
15967					minFontPixels, maxFontPixels
15968				);
15969
15970			// 2. Calculate which `font-size` would
15971			//    be best for the Width
15972			var fontSizeWidth = undefined;
15973
15974			fontSizeWidth = _sizing(
15975				'Width', ourText,
15976				$.fn.width, maxWidth,
15977				maxHeight, maxWidth,
15978				minFontPixels, maxFontPixels
15979			);
15980
15981			// 3. Actually resize the text!
15982
15983			if (Opts.widthOnly) {
15984				ourText.css({
15985					'font-size'  : fontSizeWidth,
15986					'white-space': 'nowrap'
15987				});
15988
15989				if (Opts.changeLineHeight)
15990					ourText.parent().css(
15991						'line-height',
15992						(lineHeight * fontSizeWidth + 'px')
15993					);
15994			}
15995			else {
15996				var fontSizeFinal = Math.min(fontSizeHeight, fontSizeWidth);
15997
15998				ourText.css('font-size', fontSizeFinal);
15999
16000				if (Opts.changeLineHeight)
16001					ourText.parent().css(
16002						'line-height',
16003						(lineHeight * fontSizeFinal) + 'px'
16004					);
16005			}
16006
16007			_debug(
16008				'[TextFill] Finished { ' +
16009				'Old font-size: ' + oldFontSize              + ', ' +
16010				'New font-size: ' + ourText.css('font-size') + ' }'
16011			);
16012
16013			// Oops, something wrong happened!
16014			// We weren't supposed to exceed the original size
16015			if ((ourText.width()  > maxWidth) ||
16016				(ourText.height() > maxHeight && !Opts.widthOnly)) {
16017
16018				ourText.css('font-size', oldFontSize);
16019
16020				// Failure callback
16021				if (Opts.fail)
16022					Opts.fail(this);
16023
16024				_debug(
16025					'[TextFill] Failure { ' +
16026					'Current Width: '  + ourText.width()  + ', ' +
16027					'Maximum Width: '  + maxWidth         + ', ' +
16028					'Current Height: ' + ourText.height() + ', ' +
16029					'Maximum Height: ' + maxHeight        + ' }'
16030				);
16031			}
16032			else if (Opts.success) {
16033				Opts.success(this);
16034			}
16035			else if (Opts.callback) {
16036				_warn('callback is deprecated, use success, instead');
16037
16038				// Success callback
16039				Opts.callback(this);
16040			}
16041		});
16042
16043		// Complete callback
16044		if (Opts.complete)
16045			Opts.complete(this);
16046
16047		_debug('[TextFill] End Debug');
16048		return this;
16049	};
16050
16051})(window.jQuery);
16052
16053!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;
16054function getStyles(config){
16055  // 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;
16055left: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+"}"
16056};
16057  var ShareUtils;
16058
16059if ((!("classList" in document.documentElement)) && Object.defineProperty && typeof HTMLElement !== "undefined") {
16060  Object.defineProperty(HTMLElement.prototype, "classList", {
16061    get: function() {
16062      var ret, self, update;
16063      update = function(fn) {
16064        return function(value) {
16065          var classes, index;
16066          classes = self.className.split(/\s+/);
16067          index = classes.indexOf(value);
16068          fn(classes, index, value);
16069          self.className = classes.join(" ");
16070        };
16071      };
16072      self = this;
16073      ret = {
16074        add: update(function(classes, index, value) {
16075          ~index || classes.push(value);
16076        }),
16077        remove: update(function(classes, index) {
16078          ~index && classes.splice(index, 1);
16079        }),
16080        toggle: update(function(classes, index, value) {
16081          if (~index) {
16082            classes.splice(index, 1);
16083          } else {
16084            classes.push(value);
16085          }
16086        }),
16087        contains: function(value) {
16088          return !!~self.className.split(/\s+/).indexOf(value);
16089        },
16090        item: function(i) {
16091          return self.className.split(/\s+/)[i] || null;
16092        }
16093      };
16094      Object.defineProperty(ret, "length", {
16095        get: function() {
16096          return self.className.split(/\s+/).length;
16097        }
16098      });
16099      return ret;
16100    }
16101  });
16102}
16103
16104String.prototype.to_rfc3986 = function() {
16105  var tmp;
16106  tmp = encodeURIComponent(this);
16107  return tmp.replace(/[!'()*]/g, function(c) {
16108    return "%" + c.charCodeAt(0).toString(16);
16109  });
16110};
16111
16112ShareUtils = (function() {
16113  function ShareUtils() {}
16114
16115  ShareUtils.prototype.extend = function(to, from, overwrite) {
16116    var hasProp, prop;
16117    for (prop in from) {
16118      hasProp = to[prop] !== undefined;
16119      if (hasProp && typeof from[prop] === "object") {
16120        this.extend(to[prop], from[prop], overwrite);
16121      } else {
16122        if (overwrite || !hasProp) {
16123          to[prop] = from[prop];
16124        }
16125      }
16126    }
16127  };
16128
16129  ShareUtils.prototype.hide = function(el) {
16130    return el.style.display = "none";
16131  };
16132
16133  ShareUtils.prototype.show = function(el) {
16134    return el.style.display = "block";
16135  };
16136
16137  ShareUtils.prototype.has_class = function(el, class_name) {
16138    return el.classList.contains(class_name);
16139  };
16140
16141  ShareUtils.prototype.add_class = function(el, class_name) {
16142    return el.classList.add(class_name);
16143  };
16144
16145  ShareUtils.prototype.remove_class = function(el, class_name) {
16146    return el.classList.remove(class_name);
16147  };
16148
16149  ShareUtils.prototype.is_encoded = function(str) {
16150    str = str.to_rfc3986();
16151    return decodeURIComponent(str) !== str;
16152  };
16153
16154  ShareUtils.prototype.encode = function(str) {
16155    if (typeof str === "undefined" || this.is_encoded(str)) {
16156      return str;
16157    } else {
16158      return str.to_rfc3986();
16159    }
16160  };
16161
16162  ShareUtils.prototype.popup = function(url, params) {
16163    var k, popup, qs, v;
16164    if (params == null) {
16165      params = {};
16166    }
16167    popup = {
16168      width: 500,
16169      height: 350
16170    };
16171    popup.top = (screen.height / 2) - (popup.height / 2);
16172    popup.left = (screen.width / 2) - (popup.width / 2);
16173    qs = ((function() {
16174      var _results;
16175      _results = [];
16176      for (k in params) {
16177        v = params[k];
16178        _results.push("" + k + "=" + (this.encode(v)));
16179      }
16180      return _results;
16181    }).call(this)).join('&');
16182    if (qs) {
16183      qs = "?" + qs;
16184    }
16185    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=" 
16185+ popup.width + ",height=" + popup.height);
16186  };
16187
16188  return ShareUtils;
16189
16190})();
16191var Share,
16192  __hasProp = {}.hasOwnProperty,
16193  __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; };
16194
16195Share = (function(_super) {
16196  __extends(Share, _super);
16197
16198  function Share(element, options) {
16199    this.element = element;
16200    this.el = {
16201      head: document.getElementsByTagName('head')[0],
16202      body: document.getElementsByTagName('body')[0]
16203    };
16204    this.config = {
16205      enabled_networks: 0,
16206      protocol: ['http', 'https'].indexOf(window.location.href.split(':')[0]) === -1 ? 'https://' : '//',
16207      url: window.location.href.replace("&", "%26"),
16208      caption: null,
16209      title: this.default_title(),
16210      image: this.default_image(),
16211      description: this.default_description(),
16212      ui: {
16213        flyout: 'top center',
16214        button_text: 'Share',
16215        button_font: true,
16216        icon_font: true
16217      },
16218      networks: {
16219        google_plus: {
16220          enabled: true,
16221          url: null
16222        },
16223        twitter: {
16224          enabled: true,
16225          url: null,
16226          title: null,
16227          description: null
16228        },
16229        facebook: {
16230          enabled: true,
16231          load_sdk: true,
16232          url: null,
16233          app_id: null,
16234          title: null,
16235          caption: null,
16236          description: null,
16237          image: null
16238        },
16239        pinterest: {
16240          enabled: true,
16241          url: null,
16242          image: null,
16243          description: null
16244        },
16245        reddit: {
16246          enabled: true,
16247          url: null,
16248          title: null
16249        },
16250        linkedin: {
16251          enabled: true,
16252          url: null,
16253          title: null,
16254          description: null
16255        },
16256        xing: {
16257          enabled: true,
16258          url: null,
16259          title: null,
16260          image: null,
16261          description: null
16262        },
16263        whatsapp: {
16264          enabled: true,
16265          description: null,
16266          url: null
16267        },
16268        email: {
16269          enabled: true,
16270          title: null,
16271          description: null,
16272          url: null
16273        }
16274      }
16275    };
16276    this.setup(element, options);
16277    return this;
16278  }
16279
16280  Share.prototype.setup = function(element, opts) {
16281    var index, instance, instances, _i, _len;
16282    instances = document.querySelectorAll(element);
16283    this.extend(this.config, opts, true);
16284    this.set_global_configuration();
16285    this.normalize_network_configuration();
16286    if (this.config.ui.icon_font) {
16287      this.inject_icons();
16288    }
16289    if (this.config.ui.button_font) {
16290      this.inject_fonts();
16291    }
16292    if (this.config.networks.facebook.enabled && this.config.networks.facebook.load_sdk) {
16293      this.inject_facebook_sdk();
16294    }
16295    for (index = _i = 0, _len = instances.length; _i < _len; index = ++_i) {
16296      instance = instances[index];
16297      this.setup_instance(element, index);
16298    }
16299  };
16300
16301  Share.prototype.setup_instance = function(element, index) {
16302    var button, instance, label, network, networks, _i, _len, _results,
16303      _this = this;
16304    instance = document.querySelectorAll(element)[index];
16305    this.hide(instance);
16306    this.add_class(instance, "sharer-" + index);
16307    instance = document.querySelectorAll(element)[index];
16308    this.inject_css(instance);
16309    this.inject_html(instance);
16310    this.show(instance);
16311    label = instance.getElementsByTagName("label")[0];
16312    button = instance.getElementsByClassName("social")[0];
16313    networks = instance.getElementsByTagName('li');
16314    this.add_class(button, "networks-" + this.config.enabled_networks);
16315    label.addEventListener("click", function() {
16316      return _this.event_toggle(button);
16317    });
16318    _this = this;
16319    _results = [];
16320    for (index = _i = 0, _len = networks.length; _i < _len; index = ++_i) {
16321      network = networks[index];
16322      _results.push(network.addEventListener("click", function() {
16323        _this.event_network(instance, this);
16324        return _this.event_close(button);
16325      }));
16326    }
16327    return _results;
16328  };
16329
16330  Share.prototype.event_toggle = function(button) {
16331    if (this.has_class(button, "active")) {
16332      return this.event_close(button);
16333    } else {
16334      return this.event_open(button);
16335    }
16336  };
16337
16338  Share.prototype.event_open = function(button) {
16339    if (this.has_class(button, "load")) {
16340      this.remove_class(button, "load");
16341    }
16342    return this.add_class(button, "active");
16343  };
16344
16345  Share.prototype.event_close = function(button) {
16346    return this.remove_class(button, "active");
16347  };
16348
16349  Share.prototype.event_network = function(instance, network) {
16350    var name;
16351    name = network.getAttribute("data-network");
16352    this.hook("before", name, instance);
16353    this["network_" + name]();
16354    return this.hook("after", name, instance);
16355  };
16356
16357  Share.prototype.open = function() {
16358    return this["public"]("open");
16359  };
16360
16361  Share.prototype.close = function() {
16362    return this["public"]("close");
16363  };
16364
16365  Share.prototype.toggle = function() {
16366    return this["public"]("toggle");
16367  };
16368
16369  Share.prototype["public"] = function(action) {
16370    var button, index, instance, _i, _len, _ref, _results;
16371    _ref = document.querySelectorAll(this.element);
16372    _results = [];
16373    for (index = _i = 0, _len = _ref.length; _i < _len; index = ++_i) {
16374      instance = _ref[index];
16375      button = instance.getElementsByClassName("social")[0];
16376      _results.push(this["event_" + action](button));
16377    }
16378    return _results;
16379  };
16380
16381  Share.prototype.network_facebook = function() {
16382    if (this.config.networks.facebook.load_sdk) {
16383      if (!window.FB) {
16384        return console.error("The Facebook JS SDK hasn't loaded yet.");
16385      }
16386      return FB.ui({
16387        method: 'feed',
16388        name: this.config.networks.facebook.title,
16389        link: this.config.networks.facebook.url,
16390        picture: this.config.networks.facebook.image,
16391        caption: this.config.networks.facebook.caption,
16392        description: this.config.networks.facebook.description
16393      });
16394    } else {
16395      return this.popup('https://www.facebook.com/sharer/sharer.php', {
16396        u: this.config.networks.facebook.url
16397      });
16398    }
16399  };
16400
16401  Share.prototype.network_twitter = function() {
16402    return this.popup('https://twitter.com/intent/tweet', {
16403      text: this.config.networks.twitter.title,
16404      url: this.config.networks.twitter.url
16405    });
16406  };
16407
16408  Share.prototype.network_google_plus = function() {
16409    return this.popup('https://plus.google.com/share', {
16410      url: this.config.networks.google_plus.url
16411    });
16412  };
16413
16414  Share.prototype.network_pinterest = function() {
16415    return this.popup('https://www.pinterest.com/pin/create/button', {
16416      url: this.config.networks.pinterest.url,
16417      media: this.config.networks.pinterest.image,
16418      description: this.config.networks.pinterest.description
16419    });
16420  };
16421
16422  Share.prototype.network_linkedin = function() {
16423    return this.popup('https://www.linkedin.com/shareArticle', {
16424      url: this.config.networks.linkedin.url,
16425      title: this.config.networks.linkedin.title,
16426      summary: this.config.networks.linkedin.description
16427    });
16428  }
16429
16430  Share.prototype.network_xing = function() {
16431    return this.popup('https://www.xing.com/spi/shares/new', {
16432      url: this.config.networks.xing.url,
16433      image: this.config.networks.xing.image,
16434      title: this.config.networks.xing.title,
16435      summary: this.config.networks.xing.description
16436    });
16437  }
16438
16439  Share.prototype.network_email = function() {
16440    return this.popup('mailto:', {
16441      subject: this.config.networks.email.title,
16442      body: this.config.networks.email.url + '%0A%0A' + this.config.networks.email.description,
16443    });
16444  };
16445
16446  Share.prototype.inject_icons = function() {
16447  //  return this.inject_stylesheet("https://www.sharebutton.co/fonts/v2/entypo.min.css");
16448  };
16449
16450  Share.prototype.inject_fonts = function() {
16451   // return this.inject_stylesheet("http://fonts.googleapis.com/css?family=Lato:900&text=" + this.config.ui.button_text);
16452  };
16453
16454  Share.prototype.inject_stylesheet = function(url) {
16455    var link;
16456    if (!this.el.head.querySelector("link[href=\"" + url + "\"]")) {
16457      link = document.createElement("link");
16458      link.setAttribute("rel", "stylesheet");
16459      link.setAttribute("href", url);
16460      return this.el.head.appendChild(link);
16461    }
16462  };
16463
16464  Share.prototype.inject_css = function(instance) {
16465    var css, meta, selector, style;
16466    selector = "." + (instance.getAttribute('class').split(" ").join("."));
16467    if (!this.el.head.querySelector("meta[name='sharer" + selector + "']")) {
16468      this.config.selector = selector;
16469      css = getStyles(this.config);
16470      style = document.createElement("style");
16471      style.type = "text/css";
16472      if (style.styleSheet) {
16473        style.styleSheet.cssText = css;
16474      } else {
16475        style.appendChild(document.createTextNode(css));
16476      }
16477      this.el.head.appendChild(style);
16478      delete this.config.selector;
16479      meta = document.createElement("meta");
16480      meta.setAttribute("name", "sharer" + selector);
16481      return this.el.head.appendChild(meta);
16482    }
16483  };
16484
16485  Share.prototype.inject_html = function(instance) {
16486    //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>";
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-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  };
16489
16490  Share.prototype.inject_facebook_sdk = function() {
16491    var fb_root, script;
16492    if (!window.FB && this.config.networks.facebook.app_id && !this.el.body.querySelector('#fb-root')) {
16493      script = document.createElement("script");
16494      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')";
16495      fb_root = document.createElement("div");
16496      fb_root.id = "fb-root";
16497      this.el.body.appendChild(fb_root);
16498      return this.el.body.appendChild(script);
16499    }
16500  };
16501
16502  Share.prototype.hook = function(type, network, instance) {
16503    var fn, opts;
16504    fn = this.config.networks[network][type];
16505    if (typeof fn === "function") {
16506      opts = fn.call(this.config.networks[network], instance);
16507      if (opts !== void 0) {
16508        opts = this.normalize_filter_config_updates(opts);
16509        this.extend(this.config.networks[network], opts, true);
16510        this.normalize_network_configuration();
16511      }
16512    }
16513  };
16514
16515  Share.prototype.default_title = function() {
16516    var content;
16517    if (content = document.querySelector('meta[property="og:title"]') || document.querySelector('meta[name="twitter:title"]')) {
16518      return encodeURIComponent(content.getAttribute('content'));
16519    } else if (content = document.querySelector('title')) {
16520      return encodeURIComponent(content.innerText);
16521    }
16522  };
16523
16524  Share.prototype.default_image = function() {
16525    var content;
16526    if (content = document.querySelector('meta[property="og:image"]') || document.querySelector('meta[name="twitter:image"]')) {
16527      return content.getAttribute('content');
16528    }
16529  };
16530
16531  Share.prototype.default_description = function() {
16532    var content;
16533    if (content = document.querySelector('meta[property="og:description"]') || document.querySelector('meta[name="twitter:description"]') || document.querySelector('meta[name="description"]')) {
16534      return encodeURIComponent(content.getAttribute('content'));
16535    } else {
16536      return '';
16537    }
16538  };
16539
16540  Share.prototype.set_global_configuration = function() {
16541    var display, network, option, options, _ref, _results;
16542    _ref = this.config.networks;
16543    _results = [];
16544    for (network in _ref) {
16545      options = _ref[network];
16546      for (option in options) {
16547        if (this.config.networks[network][option] == null) {
16548          this.config.networks[network][option] = this.config[option];
16549        }
16550      }
16551      if (this.config.networks[network].enabled) {
16552        display = 'block';
16553        this.config.enabled_networks += 1;
16554      } else {
16555        display = 'none';
16556      }
16557      _results.push(this.config.networks[network].display = display);
16558    }
16559    return _results;
16560  };
16561
16562  Share.prototype.normalize_network_configuration = function() {
16563    if (!this.config.networks.facebook.app_id) {
16564      this.config.networks.facebook.load_sdk = false;
16565    }
16566    if (!this.is_encoded(this.config.networks.twitter.description)) {
16567      this.config.networks.twitter.description = encodeURIComponent(this.config.networks.twitter.description);
16568    }
16569    if (typeof this.config.networks.facebook.app_id === 'number') {
16570      return this.config.networks.facebook.app_id = this.config.networks.facebook.app_id.toString();
16571    }
16572  };
16573
16574  Share.prototype.normalize_filter_config_updates = function(opts) {
16575    if (this.config.networks.facebook.app_id !== opts.app_id) {
16576      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.");
16577      delete opts.app_id;
16578    }
16579    if (this.config.networks.facebook.load_sdk !== opts.load_sdk) {
16580      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.");
16581      delete opts.app_id;
16582    }
16583    return opts;
16584  };
16585
16586  return Share;
16587
16588})(ShareUtils);
16589 return Share;
16590});
16591
16592// Generated by CoffeeScript 1.9.2
16593
16594// @license Sticky-kit v1.1.2 | WTFPL | Leaf Corcoran 2015 | http://leafo.net
16595
16596(function() {
16597  var $, win;
16598
16599  $ = this.jQuery || window.jQuery;
16600
16601  win = $(window);
16602
16603  $.fn.stick_in_parent = function(opts) {
16604    var doc, elm, enable_bottoming, fn, i, inner_scrolling, len, manual_spacer, offset_top, outer_width, parent_selector, recalc_every, sticky_class;
16605    if (opts == null) {
16606      opts = {};
16607    }
16608    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;
16609    if (offset_top == null) {
16610      offset_top = 0;
16611    }
16612    if (parent_selector == null) {
16613      parent_selector = void 0;
16614    }
16615    if (inner_scrolling == null) {
16616      inner_scrolling = true;
16617    }
16618    if (sticky_class == null) {
16619      sticky_class = "is_stuck";
16620    }
16621    doc = $(document);
16622    if (enable_bottoming == null) {
16623      enable_bottoming = true;
16624    }
16625    outer_width = function(el) {
16626      var _el, computed, w;
16627      if (window.getComputedStyle) {
16628        _el = el[0];
16629        computed = window.getComputedStyle(el[0]);
16630        w = parseFloat(computed.getPropertyValue("width")) + parseFloat(computed.getPropertyValue("margin-left")) + parseFloat(computed.getPropertyValue("margin-right"));
16631        if (computed.getPropertyValue("box-sizing") !== "border-box") {
16632          w += parseFloat(computed.getPropertyValue("border-left-width")) + parseFloat(computed.getPropertyValue("border-right-width")) + parseFloat(computed.getPropertyValue("padding-left")) + parseFloat(computed.getPropertyValue("padding-right"));
16633        }
16634        return w;
16635      } else {
16636        return el.outerWidth(true);
16637      }
16638    };
16639    fn = function(elm, padding_bottom, parent_top, parent_height, top, height, el_float, detached) {
16640      var bottomed, detach, fixed, last_pos, last_scroll_height, offset, parent, recalc, recalc_and_tick, recalc_counter, spacer, tick;
16641      if (elm.data("sticky_kit")) {
16642        return;
16643      }
16644      elm.data("sticky_kit", true);
16645      last_scroll_height = doc.height();
16646      parent = elm.parent();
16647      if (parent_selector != null) {
16648        parent = parent.closest(parent_selector);
16649      }
16650      if (!parent.length) {
16651        throw "failed to find stick parent";
16652      }
16653      fixed = false;
16654      bottomed = false;
16655      spacer = manual_spacer != null ? manual_spacer && elm.closest(manual_spacer) : $("<div />");
16656      if (spacer) {
16657        spacer.css('position', elm.css('position'));
16658      }
16659      recalc = function() {
16660        var border_top, padding_top, restore;
16661        if (detached) {
16662          return;
16663        }
16664        last_scroll_height = doc.height();
16665        border_top = parseInt(parent.css("border-top-width"), 10);
16666        padding_top = parseInt(parent.css("padding-top"), 10);
16667        padding_bottom = parseInt(parent.css("padding-bottom"), 10);
16668        parent_top = parent.offset().top + border_top + padding_top;
16669        parent_height = parent.height();
16670        if (fixed) {
16671          fixed = false;
vendor: 6,068 bytes, lines 16672-16866
16672          bottomed = false;
16673          if (manual_spacer == null) {
16674            elm.insertAfter(spacer);
16675            spacer.detach();
16676          }
16677          elm.css({
16678            position: "",
16679            top: "",
16680            width: "",
16681            bottom: ""
16682          }).removeClass(sticky_class);
16683          restore = true;
16684        }
16685        top = elm.offset().top - (parseInt(elm.css("margin-top"), 10) || 0) - offset_top;
16686        height = elm.outerHeight(true);
16687        el_float = elm.css("float");
16688        if (spacer) {
16689          spacer.css({
16690            width: outer_width(elm),
16691            height: height,
16692            display: elm.css("display"),
16693            "vertical-align": elm.css("vertical-align"),
16694            "float": el_float
16695          });
16696        }
16697        if (restore) {
16698          return tick();
16699        }
16700      };
16701      recalc();
16702      if (height === parent_height) {
16703        return;
16704      }
16705      last_pos = void 0;
16706      offset = offset_top;
16707      recalc_counter = recalc_every;
16708      tick = function() {
16709        var css, delta, recalced, scroll, will_bottom, win_height;
16710        if (detached) {
16711          return;
16712        }
16713        recalced = false;
16714        if (recalc_counter != null) {
16715          recalc_counter -= 1;
16716          if (recalc_counter <= 0) {
16717            recalc_counter = recalc_every;
16718            recalc();
16719            recalced = true;
16720          }
16721        }
16722        if (!recalced && doc.height() !== last_scroll_height) {
16723          recalc();
16724          recalced = true;
16725        }
16726        scroll = win.scrollTop();
16727        if (last_pos != null) {
16728          delta = scroll - last_pos;
16729        }
16730        last_pos = scroll;
16731        if (fixed) {
16732          if (enable_bottoming) {
16733            will_bottom = scroll + height + offset > parent_height + parent_top;
16734            if (bottomed && !will_bottom) {
16735              bottomed = false;
16736              elm.css({
16737                position: "fixed",
16738                bottom: "",
16739                top: offset
16740              }).trigger("sticky_kit:unbottom");
16741            }
16742          }
16743          if (scroll < top) {
16744            fixed = false;
16745            offset = offset_top;
16746            if (manual_spacer == null) {
16747              if (el_float === "left" || el_float === "right") {
16748                elm.insertAfter(spacer);
16749              }
16750              spacer.detach();
16751            }
16752            css = {
16753              position: "",
16754              width: "",
16755              top: ""
16756            };
16757            elm.css(css).removeClass(sticky_class).trigger("sticky_kit:unstick");
16758          }
16759          if (inner_scrolling) {
16760            win_height = win.height();
16761            if (height + offset_top > win_height) {
16762              if (!bottomed) {
16763                offset -= delta;
16764                offset = Math.max(win_height - height, offset);
16765                offset = Math.min(offset_top, offset);
16766                if (fixed) {
16767                  elm.css({
16768                    top: offset + "px"
16769                  });
16770                }
16771              }
16772            }
16773          }
16774        } else {
16775          if (scroll > top) {
16776            fixed = true;
16777            css = {
16778              position: "fixed",
16779              top: offset
16780            };
16781            css.width = elm.css("box-sizing") === "border-box" ? elm.outerWidth() + "px" : elm.width() + "px";
16782            elm.css(css).addClass(sticky_class);
16783            if (manual_spacer == null) {
16784              elm.after(spacer);
16785              if (el_float === "left" || el_float === "right") {
16786                spacer.append(elm);
16787              }
16788            }
16789            elm.trigger("sticky_kit:stick");
16790          }
16791        }
16792        if (fixed && enable_bottoming) {
16793          if (will_bottom == null) {
16794            will_bottom = scroll + height + offset > parent_height + parent_top;
16795          }
16796          if (!bottomed && will_bottom) {
16797            bottomed = true;
16798            if (parent.css("position") === "static") {
16799              parent.css({
16800                position: "relative"
16801              });
16802            }
16803            return elm.css({
16804              position: "absolute",
16805              bottom: padding_bottom,
16806              top: "auto"
16807            }).trigger("sticky_kit:bottom");
16808          }
16809        }
16810      };
16811      recalc_and_tick = function() {
16812        recalc();
16813        return tick();
16814      };
16815      detach = function() {
16816        detached = true;
16817        win.off("touchmove", tick);
16818        win.off("scroll", tick);
16819        win.off("resize", recalc_and_tick);
16820        $(document.body).off("sticky_kit:recalc", recalc_and_tick);
16821        elm.off("sticky_kit:detach", detach);
16822        elm.removeData("sticky_kit");
16823        elm.css({
16824          position: "",
16825          bottom: "",
16826          top: "",
16827          width: ""
16828        });
16829        parent.position("position", "");
16830        if (fixed) {
16831          if (manual_spacer == null) {
16832            if (el_float === "left" || el_float === "right") {
16833              elm.insertAfter(spacer);
16834            }
16835            spacer.remove();
16836          }
16837          return elm.removeClass(sticky_class);
16838        }
16839      };
16840      win.on("touchmove", tick);
16841      win.on("scroll", tick);
16842      win.on("resize", recalc_and_tick);
16843      $(document.body).on("sticky_kit:recalc", recalc_and_tick);
16844      elm.on("sticky_kit:detach", detach);
16845      return setTimeout(tick, 0);
16846    };
16847    for (i = 0, len = this.length; i < len; i++) {
16848      elm = this[i];
16849      fn($(elm));
16850    }
16851    return this;
16852  };
16853
16854}).call(this);
16855/* ==========================================================================
16856 * bootstrap-tab-history.js
16857 * Author: Michael Narayan <[email protected]>
16858 * Repository: https://github.com/mnarayan01/bootstrap-tab-history/
16859 * ==========================================================================
16860 * Licensed under the Apache License, Version 2.0 (the "License"); you may
16861 * not use this file except in compliance with the License. You may obtain a
16862 * copy of the License at
16863 *
16864 * http://www.apache.org/licenses/LICENSE-2.0
16865 *
16866 * Unless required by applicable law or agreed to in writing, softw
16866are
16867 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
16868 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16869 * License for the specific language governing permissions and limitations
16870 * under the License.
16871 * ========================================================================== */
16872
16873/* ========================================================================== */
16874/* JSHint directives                                                          */
16875/*                                                                            */
16876/* global BootstrapTabHistory: true                                           */
16877/*                                                                            */
16878/* global document                                                            */
16879/* global jQuery                                                              */
16880/* global history                                                             */
16881/* global window                                                              */
16882/* ========================================================================== */
16883
16884/**
16885 * Integrate [HTML5 history state tracking](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history)
16886 * with [`bootstrap/tab.js`](http://getbootstrap.com/javascript/#tabs). To enable tracking on a tab element, simply set
16887 * the `data-tab-history` attribute to true (or a string denoting a tab grouping).
16888 *
16889 * See the README for additional information.
16890 *
16891 * Functionality based upon bootstrap/tab.js v3.1.0; reference it when making any changes.
16892 */
16893
16894BootstrapTabHistory = {
16895  options: {
16896    /**
16897     * When the anchor portion of the URI is used to activate a tab, scroll down to the given offset, rather than the
16898     * element with the given `id` attribute. Set to null to disable. Only relevant if showTabsBasedOnAnchor is true.
16899     *
16900     * May be overriden on a per-element basis by the attribute `data-tab-history-anchor-y-offset`.
16901     *
16902     * @public
16903     * @type {?number}
16904     */
16905    defaultAnchorYOffset: 0,
16906    /**
16907     * Either 'push' or 'replace', for whether to use `history.pushState` or `history.replaceState`, resp.
16908     *
16909     * May be overriden on a per-element basis by the attribute `data-tab-history-changer`.
16910     *
16911     * @public
16912     * @type {string}
16913     */
16914    defaultChanger: 'replace',
16915    /**
16916     * If true, update the URL in onShownTab in the calls to `history.pushState` and `history.replaceState`. Otherwise,
16917     * `null` is passed as the third parameter to these calls.
16918     *
16919     * May be overriden on a per-element basis by the attribute `data-tab-history-update-url`.
16920     *
16921     * @public
16922     * @type {boolean}
16923     */
16924    defaultUpdateURL: false,
16925    /**
16926     * Should the anchor portion of the loaded URI be used to activate a single tab if no history was present on page
16927     * load.
16928     *
16929     * @public
16930     * @type {boolean}
16931     */
16932    showTabsBasedOnAnchor: true
16933  }
16934};
16935
16936(function () {
16937  'use strict';
16938
16939  jQuery(function () {
16940    if(history && history.pushState && history.replaceState) {
16941      var bootstrapTabHistory = history.state && history.state.bootstrapTabHistory;
16942
16943      if(bootstrapTabHistory) {
16944        showTabsBasedOnState(bootstrapTabHistory);
16945      } else {
16946        showTabsBasedOnAnchor();
16947      }
16948
16949      backfillHistoryState();
16950
16951      jQuery(document).on('shown.bs.tab show.bs.collapse', onShownTab);
16952      jQuery(window).on('popstate', onPopState);
16953    } else {
16954      showTabsBasedOnAnchor();
16955    }
16956  });
16957
16958  /**
16959   * Used to prevent onShownTab from registering shown events that we triggered via showTabsBasedOnState.
16960   *
16961   * @type {boolean}
16962   */
16963  var showingTabsBasedOnState = false;
16964
16965  /**
16966   * Used to update `history.state` to reflect the default active tabs on initial page load. This supports proper
16967   * `history.back` handling when `data-tab-history-update-url` is true.
16968   */
16969  function backfillHistoryState() {
16970    var newState = null;
16971
16972    jQuery('li.active > [data-tab-history], .panel-title.active [data-tab-history]').each(function () {//edited by Uncode
16973      var $activeTabElement = jQuery(this);
16974      var selector = getTabSelector($activeTabElement);
16975
16976      if(selector) {
16977        var tabGroup = getTabGroup($activeTabElement);
16978
16979        if(tabGroup) {
16980          newState = createNewHistoryState(newState || history.state, tabGroup, selector);
16981        }
16982      }
16983    });
16984
16985    if(newState) {
16986      history.replaceState(newState, '', null);
16987    }
16988  }
16989
16990  /**
16991   * Clone the existing state, ensure its bootstrapTabHistory attribute is an Object, and add the provided tabGroup to
16992   * said bootstrapTabHistory attribute.
16993   *
16994   * @param {?object} existingState
16995   * @param {!string} tabGroup
16996   * @param {!string} selector
16997   * @returns {!object}
16998   */
16999  function createNewHistoryState(existingState, tabGroup, selector) {
17000    // Clone history.state and ensure it has a bootstrapTabHistory entry.
17001    var newState = jQuery.extend(true, {}, existingState, {
17002      bootstrapTabHistory: {}
17003    });
17004
17005    newState.bootstrapTabHistory[tabGroup] = selector;
17006
17007    return newState;
17008  }
17009
17010  /**
17011   * @param {jQuery} $tab
17012   * @returns {?string}
17013   */
17014  function getTabGroup($tab) {
17015    return parseTruthyAttributeValue($tab.data('tab-history'));
17016  }
17017
17018  /**
17019   * @param {jQuery} $tab
17020   * @returns {?string}
17021   */
17022  function getTabSelector($tab) {
17023    return $tab.data('target') || $tab.attr('href');
17024  }
17025
17026  /**
17027   * Receives the `shown.bs.tab` event. Updates `history.state` as appropriate.
17028   *
17029   * @param {jQuery.Event} shownEvt
17030   */
17031  function onShownTab(shownEvt) {
17032    if(!showingTabsBasedOnState) {
17033      var $activatedTab = jQuery(shownEvt.target);
17034      if ( $activatedTab.hasClass('panel-collapse') )
17035        $activatedTab = $activatedTab.closest('.panel').find('a');
17036      var selector = getTabSelector($activatedTab);
17037
17038      if(selector) {
17039        var tabGroup = getTabGroup($activatedTab);
17040
17041        if(tabGroup) {
17042          var historyChanger = $activatedTab.data('tab-history-changer') || BootstrapTabHistory.options.defaultChanger;
17043          var newState = createNewHistoryState(history.state, tabGroup, selector);
17044          var updateURL = (function ($activatedTab) {
17045            if(selector[0] === '#') {
17046              var elementUpdateURLOption = parseTruthyAttributeValue($activatedTab.data('tab-history-update-url'));
17047
17048              if(elementUpdateURLOption === undefined) {
17049                return BootstrapTabHistory.options.defaultUpdateURL;
17050              } else {
17051                return elementUpdateURLOption;
17052              }
17053            } else {
17054              return false;
17055            }
17056          })($activatedTab);
17057
17058          switch(historyChanger) {
17059            case 'push':
17060              history.pushState(newState, '', updateURL ? selector : null);
17061              break;
17062            case 'replace':
17063              history.replaceState(newState, '', updateURL ? selector : null);
17064              break;
17065            default:
17066              throw new Error('Unknown tab-history-changer: ' + historyChanger);
17067          }
17068        }
17069      }
17070    }
17071  }
17072
17073  /**
17074   * Receives the `popstate` event. Shows tabs based on the value of `history.state` as appropriate.
17075   */
17076  function onPopState() {
17077    var bootstrapTabHistory = history.state && history.state.bootstrapTabHistory;
17078
17079    if(bootstrapTabHistory) {
17080      showTabsBasedOnState(bootstrapTabHistory);
17081    }
17082  }
17083
17084  /**
17085   * Returns the given value, _unless_ that value is an empty string, in which case `true` is returned.
17086   *
17087   * Rationale: HAML data attributes which are set to `true` are rendered as a blank string.
17088   *
17089   * @param {*} value
17090   * @returns {*}
17091   */
17092  function parseTruthyAttributeValue(value) {
17093    if(value) {
17094      return value;
17095    } else if(value === '') {
17096      return true;
17097    } else {
17098      return value;
17099    }
17100  }
17101
17102  /**
17103   * Show tabs based upon the anchor component of `window.location`.
17104   */
17105  function showTabsBasedOnAnchor() {
17106    if(BootstrapTabHistory.options.showTabsBasedOnAnchor) {
17107      var anchor = window.location && window.location.hash;
17108
17109      if(anchor) {
17110        var $tabElement = showTabForSelector(anchor);
17111
17112        if($tabElement && window.addEventListener && window.removeEventListener) {
17113          var anchorYOffset = (function ($tabElement) {
17114            var elementSetting = $tabElement.data('tab-history-anchor-y-offset');
17115
17116            if(elementSetting === undefined) {
17117              return BootstrapTabHistory.options.defaultAnchorYOffset;
17118            } else {
17119              return elementSetting;
17120            }
17121          })($tabElement);
17122
17123          // HACK: This prevents scrolling to the tab on page load. This relies on the fact that we should never get
17124          //   here on `history.forward`, `history.back`, or `location.reload`, since in all those situations the
17125          //   `history.state` object should have been used (unless the browser did not support the modern History API).
17126          if(anchorYOffset || anchorYOffset === 0) {
17127            var scrollListener = function resetAnchorScroll () {
17128              window.removeEventListener('scroll', scrollListener);
17129              window.scrollTo(0, anchorYOffset);
17130            };
17131
17132            window.addEventListener('scroll', scrollListener);
17133          }
17134        }
17135      }
17136    }
17137  }
17138
17139  /**
17140   * Show a tab which corresponds to the provided selector.
17141   *
17142   * @param {string} selector - A CSS selector.
17143   * @returns {?jQuery} - The tab which was found to show (even if said tab was already active).
17144   */
17145  function showTabForSelector(selector) {
17146    var $tabElement = (function (selector) {
17147      var $ret = null;
17148
17149      jQuery('[data-toggle="tab"], [data-toggle="pill"], [data-toggle="collapse"]').each(function () {
17150        var $potentialTab = jQuery(this);
17151
17152        if(($potentialTab.attr('href') === selector || $potentialTab.data('target') === selector ) && getTabGroup($potentialTab)) {
17153          $ret = $potentialTab;
17154          return false;
vendor: 4,522 bytes, lines 17155-17304
17155        } else {
17156         return null;
17157        }
17158      });
17159
17160      return $ret;
17161    })(selector);
17162
17163    if($tabElement) {
17164      $tabElement.trigger('click');
17165      //$tabElement.tab('show');
17166    }
17167
17168    return $tabElement;
17169  }
17170
17171  /**
17172   * Iterate through the provided set of tab tab groups, showing the tabs based on the stored selectors.
17173   *
17174   * @param {object} bootstrapTabHistory - Each of the values is passed to showTabForSelector; the keys are actually irrelevant.
17175   */
17176  function showTabsBasedOnState(bootstrapTabHistory) {
17177    showingTabsBasedOnState = true;
17178
17179    try {
17180      for(var k in bootstrapTabHistory) {
17181        if(bootstrapTabHistory.hasOwnProperty(k)) {
17182          showTabForSelector(bootstrapTabHistory[k]);
17183        }
17184      }
17185    } finally {
17186      showingTabsBasedOnState = false;
17187    }
17188  }
17189})();
17190
17191/*!
17192 * Justified Gallery - v3.6.3
17193 * http://miromannino.github.io/Justified-Gallery/
17194 * Copyright (c) 2016 Miro Mannino
17195 * Licensed under the MIT license.
17196 */
17197(function($) {
17198
17199  function hasScrollBar() {
17200    return $("body").height() > $(window).height();
17201  }
17202  /**
17203   * Justified Gallery controller constructor
17204   *
17205   * @param $gallery the gallery to build
17206   * @param settings the settings (the defaults are in $.fn.justifiedGallery.defaults)
17207   * @constructor
17208   */
17209  var JustifiedGallery = function ($gallery, settings) {
17210    
17211    this.settings = settings;
17212    this.checkSettings();
17213
17214    this.imgAnalyzerTimeout = null;
17215    this.entries = null;
17216    this.buildingRow = {
17217      entriesBuff : [],
17218      width : 0,
17219      height : 0,
17220      aspectRatio : 0
17221    };
17222    this.lastFetchedEntry = null;
17223    this.lastAnalyzedIndex = -1;
17224    this.yield = {
17225      every : 2, // do a flush every n flushes (must be greater than 1)
17226      flushed : 0 // flushed rows without a yield
17227    };
17228    this.border = settings.border >= 0 ? settings.border : settings.margins;
17229    this.maxRowHeight = this.retrieveMaxRowHeight();
17230    this.suffixRanges = this.retrieveSuffixRanges();
17231    this.offY = this.border;
17232    this.rows = 0;
17233    this.spinner = {
17234      phase : 0,
17235      timeSlot : 150,
17236      $el : $('<div class="spinner"><span></span><span></span><span></span></div>'),
17237      intervalId : null
17238    };
17239    this.checkWidthIntervalId = null;
17240    this.galleryWidth = $gallery.width();
17241    this.$gallery = $gallery;
17242
17243  };
17244
17245  /** @returns {String} the best suffix given the width and the height */
17246  JustifiedGallery.prototype.getSuffix = function (width, height) {
17247    var longestSide, i;
17248    longestSide = (width > height) ? width : height;
17249    for (i = 0; i < this.suffixRanges.length; i++) {
17250      if (longestSide <= this.suffixRanges[i]) {
17251        return this.settings.sizeRangeSuffixes[this.suffixRanges[i]];
17252      }
17253    }
17254    return this.settings.sizeRangeSuffixes[this.suffixRanges[i - 1]];
17255  };
17256
17257  /**
17258   * Remove the suffix from the string
17259   *
17260   * @returns {string} a new string without the suffix
17261   */
17262  JustifiedGallery.prototype.removeSuffix = function (str, suffix) {
17263    return str.substring(0, str.length - suffix.length);
17264  };
17265
17266  /**
17267   * @returns {boolean} a boolean to say if the suffix is contained in the str or not
17268   */
17269  JustifiedGallery.prototype.endsWith = function (str, suffix) {
17270    return str.indexOf(suffix, str.length - suffix.length) !== -1;
17271  };
17272
17273  /**
17274   * Get the used suffix of a particular url
17275   *
17276   * @param str
17277   * @returns {String} return the used suffix
17278   */
17279  JustifiedGallery.prototype.getUsedSuffix = function (str) {
17280    for (var si in this.settings.sizeRangeSuffixes) {
17281      if (this.settings.sizeRangeSuffixes.hasOwnProperty(si)) {
17282        if (this.settings.sizeRangeSuffixes[si].length === 0) continue;
17283        if (this.endsWith(str, this.settings.sizeRangeSuffixes[si])) return this.settings.sizeRangeSuffixes[si];
17284      }
17285    }
17286    return '';
17287  };
17288
17289  /**
17290   * Given an image src, with the width and the height, returns the new image src with the
17291   * best suffix to show the best quality thumbnail.
17292   *
17293   * @returns {String} the suffix to use
17294   */
17295  JustifiedGallery.prototype.newSrc = function (imageSrc, imgWidth, imgHeight, image) {
17296    var newImageSrc;
17297
17298    if (this.settings.thumbnailPath) {
17299      newImageSrc = this.settings.thumbnailPath(imageSrc, imgWidth, imgHeight, image);
17300    } else {
17301      var matchRes = imageSrc.match(this.settings.extension);
17302      var ext = (matchRes !== null) ? matchRes[0] : '';
17303      newImageSrc = imageSrc.replace(this.settings.extension, '');
17304      newImageSrc = this.removeSuffix(newImageSrc, this.getUsedSuffix(newImageSrc));
17305      newImageSrc += this.getSuffix(imgWidth, imgHeight) + ext;
17306    }
17307
17308    return newImageSrc;
17309  };
17310
17311  /**
17312   * Shows the images that is in the given entry
17313   *
17314   * @param $entry the entry
17315   * @param callback the callback that is called when the show animation is finished
17316   */
17317  JustifiedGallery.prototype.showImg = function ($entry, callback) {
17318    if (this.settings.cssAnimation) {
17319      $entry.addClass('entry-visible');
17320      if (callback) callback();
17321    } else {
17322      $entry.stop().fadeTo(this.settings.imagesAnimationDuration, 1.0, callback);
17323      $entry.find('> img, > a > img').stop().fadeTo(this.settings.imagesAnimationDuration, 1.0, callback);
17324    }
17325  };
17326
17327  /**
17328   * Extract the image src form the image, looking from the 'safe-src', and if it can't be found, from the
17329   * 'src' attribute. It saves in the image data the 'jg.originalSrc' field, with the extracted src.
17330   *
17331   * @param $image the image to analyze
17332   * @returns {String} the extracted src
17333   */
17334  JustifiedGallery.prototype.extractImgSrcFromImage = function ($image) {
17335    var imageSrc = (typeof $image.data('safe-src') !== 'undefined') ? $image.data('safe-src') : $image.attr('src');
17336    $image.data('jg.originalSrc', imageSrc);
17337    return imageSrc;
17338  };
17339
17340  /** @returns {jQuery} the image in the given entry */
17341  JustifiedGallery.prototype.imgFromEntry = function ($entry) {
17342    var $img = $entry.find('> img');
17343    if ($img.length === 0) $img = $entry.find('> a > img');
17344    if ($img.length === 0) $img = $entry.find('.t-entry-visual-cont img');//hacked by Uncode
17345    return $img.length === 0 ? null : $img;
17346  };
17347
17348  /** @returns {jQuery} the caption in the given entry */
17349  JustifiedGallery.prototype.captionFromEntry = function ($entry) {
17350    var $caption = $entry.find('> .caption');
17351    return $caption.length === 0 ? null : $caption;
17352  };
17353
17354  /**
17355   * Display the entry
17356   *
17357   * @param {jQuery} $entry the entry to display
17358   * @param {int} x the x position where the entry must be positioned
17359   * @param y the y position where the entry must be positioned
17360   * @param imgWidth the image width
17361   * @param imgHeight the image height
17362   * @param rowHeight the row height of the row that owns the entry
17363   */
17364  JustifiedGallery.prototype.displayEntry = function ($entry, x, y, imgWidth, imgHeight, rowHeight) {
17365    $entry.width(imgWidth);
17366    $entry.height(Math.floor(rowHeight));//hacked by Uncode
17367    $entry.css('top', Math.floor(y));//hacked by Uncode
17368    $entry.css('left', x);
17369
17370    var $image = this.imgFromEntry($entry);
17371    if ($image !== null) {
17372      $image.css('width', imgWidth);
17373      $image.css('height', imgHeight);
17374      $image.css('margin-left', - imgWidth / 2);
17375      $image.css('margin-top', - imgHeight / 2);
17376
17377      // Image reloading for an high quality of thumbnails
17378      var imageSrc = $image.attr('src');
17379      var newImageSrc = this.newSrc(imageSrc, imgWidth, imgHeight, $image[0]);
17380
17381      $image.one('error', function () {
17382        $image.attr('src', $image.data('jg.originalSrc')); //revert to the original thumbnail, we got it.
17383      });
17384
17385      var loadNewImage = function () {
17386        if (imageSrc !== newImageSrc) { //load the new image after the fadeIn
17387          $image.attr('src', newImageSrc);
17388        }
17389      };
17390
17391      if ($entry.data('jg.loaded') === 'skipped') {
17392        this.onImageEvent(imageSrc, $.proxy(function() {
17393          this.showImg($entry, loadNewImage);
17394          $entry.data('jg.loaded', true);
17395        }, this));
17396      } else {
17397        this.showImg($entry, loadNewImage);
17398      }
17399
17400    } else {
17401      this.showImg($entry);
17402    }
17403
17404    this.displayEntryCaption($entry);
17405  };
17406
17407  /**
17408   * Display the entry caption. If the caption element doesn't exists, it creates the caption using the 'alt'
17409   * or the 'title' attributes.
17410   *
17411   * @param {jQuery} $entry the entry to process
17412   */
17413  JustifiedGallery.prototype.displayEntryCaption = function ($entry) {
17414    var $image = this.imgFromEntry($entry);
17415    if ($image !== null && this.settings.captions) {
17416      var $imgCaption = this.captionFromEntry($entry);
17417
17418      // Create it if it doesn't exists
17419      if ($imgCaption === null) {
17420        var caption = $image.attr('alt');
17421        if (!this.isValidCaption(caption)) caption = $entry.attr('title');
17422        if (this.isValidCaption(caption)) { // Create only we found something
17423          $imgCaption = $('<div class="caption">' + caption + '</div>');
17424          $entry.append($imgCaption);
17425          $entry.data('jg.createdCaption', true);
17426        }
17427      }
17428
17429      // Create events (we check again the $imgCaption because it can be still inexistent)
17430      if ($imgCaption !== null) {
17431        if (!this.settings.cssAnimation) $imgCaption.stop().fadeTo(0, this.settings.captionSettings.nonVisibleOpacity);
17432        this.addCaptionEventsHandlers($entry);
17433      }
17434    } else {
17435      this.removeCaptionEventsHandlers($entry);
17436    }
17437  };
17438
17439  /**
17440   * Validates the caption
17441   *
17442   * @param caption The caption that should be validated
17443   * @return {boolean} Validation result
17444   */
17445  JustifiedGallery.prototype.isValidCaption = function (caption) {
17446    return (typeof caption !== 'undefined' && caption.length > 0);
17447  };
17448
17449  /**
17450   * The callback for the event 'mouseenter'. It assumes that the event currentTarget is an entry.
17451   * It shows the caption using jQuery (or using CSS if it is configured so)
17452   *
17453   * @param {Event} eventObject the event object
17454   */
17455  JustifiedGallery.prototype.onEntryMouseEnterForCaption = function (eventObject) {
17456    var $caption = this.captionFromEntry($(eventObject.currentTarget));
17457    if (this.settings.cssAnimation) {
17458      $caption.addClass('caption-visible').removeClass('caption-hidden');
17459    } else {
17460      $caption.stop().fadeTo(this.settings.captionSettings.animationDuration,
17461          this.settings.captionSettings.visibleOpacity);
17462    }
17463  };
17464
17465  /**
17466   * The callback for the event 'mouseleave'. It assumes that the event currentTarget is an entry.
17467   * It hides the caption using jQuery (or using CSS if it is configured so)
17468   *
17469   * @param {Event} eventObject the event object
17470   */
17471  JustifiedGallery.prototype.onEntryMouseLeaveForCaption = function (eventObject) {
17472    var $caption = this.captionFromEntry($(eventObject.currentTarget));
17473    if (this.settings.cssAnimation) {
17474      $caption.removeClass('caption-visible').removeClass('caption-hidden');
17475    } else {
17476      $caption.stop().fadeTo(this.settings.captionSettings.animationDuration,
17477          this.settings.captionSettings.nonVisibleOpacity);
17478    }
17479  };
17480
17481  /**
17482   * Add the handlers of the entry for the caption
17483   *
17484   * @param $entry the entry to modify
17485   */
17486  JustifiedGallery.prototype.addCaptionEventsHandlers = function ($entry) {
17487    var captionMouseEvents = $entry.data('jg.captionMouseEvents');
17488    if (typeof captionMouseEvents === 'undefined') {
17489      captionMouseEvents = {
17490        mouseenter: $.proxy(this.onEntryMouseEnterForCaption, this),
17491        mouseleave: $.proxy(this.onEntryMouseLeaveForCaption, this)
17492      };
17493      $entry.on('mouseenter', undefined, undefined, captionMouseEvents.mouseenter);
17494      $entry.on('mouseleave', undefined, undefined, captionMouseEvents.mouseleave);
17495      $entry.data('jg.captionMouseEvents', captionMouseEvents);
17496    }
17497  };
17498
17499  /**
17500   * Remove the handlers of the entry for the caption
17501   *
17502   * @param $entry the entry to modify
17503   */
17504  JustifiedGallery.prototype.removeCaptionEventsHandlers = function ($entry) {
17505    var captionMouseEvents = $entry.data('jg.captionMouseEvents');
17506    if (typeof captionMouseEvents !== 'undefined') {
17507      $entry.off('mouseenter', undefined, captionMouseEvents.mouseenter);
17508      $entry.off('mouseleave', undefined, captionMouseEvents.mouseleave);
17509      $entry.removeData('jg.captionMouseEvents');
17510    }
17511  };
17512
17513  /**
17514   * Justify the building row, preparing it to
17515   *
17516   * @param isLastRow
17517   * @returns a boolean to know if the row has been justified or not
17518   */
17519  JustifiedGallery.prototype.prepareBuildingRow = function (isLastRow) {
17520    var i, $entry, imgAspectRatio, newImgW, newImgH, justify = true;
17521    var minHeight = 0;
17522    var availableWidth = this.galleryWidth - 2 * this.border - (
17523        (this.buildingRow.entriesBuff.length - 1) * this.settings.margins);
17524    var rowHeight = Math.floor( availableWidth / this.buildingRow.aspectRatio );//hacked by Uncode
17525    var defaultRowHeight = this.settings.rowHeight;
17526    var justifiable = this.buildingRow.width / availableWidth > this.settings.justifyThreshold;
17527
17528    //Skip the last row if we can't justify it and the lastRow == 'hide'
17529    if (isLastRow && this.settings.lastRow === 'hide' && !justifiable) {
17530      for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
17531        $entry = this.buildingRow.entriesBuff[i];
17532        if (this.settings.cssAnimation)
17533          $entry.removeClass('entry-visible');
17534        else {
17535          $entry.stop().fadeTo(0, 0.1);
17536          $entry.find('> img, > a > img').fadeTo(0, 0);
17537        }
17538      }
17539      return -1;
17540    }
17541
17542    // With lastRow = nojustify, justify if is justificable (the images will not become too big)
17543    if (isLastRow && !justifiable && this.settings.lastRow !== 'justify' && this.settings.lastRow !== 'hide') {
17544      justify = false;
17545
17546      if (this.rows > 0) {
17547        defaultRowHeight = (this.offY - this.border - this.settings.margins * this.rows) / this.rows;
17548        justify = defaultRowHeight * this.buildingRow.aspectRatio / availableWidth > this.settings.justifyThreshold;
17549      }
17550    }
17551
17552    for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
17553      $entry = this.buildingRow.entriesBuff[i];
17554      imgAspectRatio = $entry.data('jg.width') / $entry.data('jg.height');
17555
17556      if (justify) {
17557        newImgW = (i === this.buildingRow.entriesBuff.length - 1) ? availableWidth : rowHeight * imgAspectRatio;
17558        newImgH = rowHeight;
17559      } else {
17560        newImgW = defaultRowHeight * imgAspectRatio;
17561        newImgH = defaultRowHeight;
17562      }
17563
17564      availableWidth -= Math.round(newImgW);
17565      $entry.data('jg.jwidth', Math.round(newImgW));
17566      $entry.data('jg.jheight', Math.ceil(newImgH));
17567      if (i === 0 || minHeight > newImgH) minHeight = newImgH;
17568    }
17569
17570    this.buildingRow.height = minHeight;
17571    return justify;
17572  };
17573
17574  /**
17575   * Clear the building row data to be used for a new row
17576   */
17577  JustifiedGallery.prototype.clearBuildingRow = function () {
17578    this.buildingRow.entriesBuff = [];
17579    this.buildingRow.aspectRatio = 0;
17580    this.buildingRow.width = 0;
17581  };
17582
17583  /**
17584   * Flush a row: justify it, modify the gallery height accordingly to the row height
17585   *
17586   * @param isLastRow
17587   */
17588  JustifiedGallery.prototype.flushRow = function (isLastRow) {
17589    var settings = this.settings;
17590    var $entry, buildingRowRes, offX = this.border, i;
17591
17592    buildingRowRes = this.prepareBuildingRow(isLastRow);
17593    if (isLastRow && settings.lastRow === 'hide' && buildingRowRes === -1) {
17594      this.clearBuildingRow();
17595      return;
17596    }
17597
17598    if (this.maxRowHeight) {
17599      if (this.maxRowHeight.isPercentage && this.maxRowHeight.value * settings.rowHeight < this.buildingRow.height) {
17600        this.buildingRow.height = this.maxRowHeight.value * settings.rowHeight;
17601      } else if (this.maxRowHeight.value >= settings.rowHeight && this.maxRowHeight.value < this.buildingRow.height) {
17602        this.buildingRow.height = this.maxRowHeight.value;
17603      }
17604    }
17605
17606    //Align last (unjustified) row
17607    if (settings.lastRow === 'center' || settings.lastRow === 'right') {
17608      var availableWidth = this.galleryWidth - 2 * this.border - (this.buildingRow.entriesBuff.length - 1) * settings.margins;
17609
17610      for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
17611        $entry = this.buildingRow.entriesBuff[i];
17612        availableWidth -= $entry.data('jg.jwidth');
17613      }
17614
17615      if (settings.lastRow === 'center')
17616        offX += availableWidth / 2;
17617      else if (settings.lastRow === 'right')
17618        offX += availableWidth;
17619    }
17620
17621    for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
17622      $entry = this.buildingRow.entriesBuff[i];
17623      this.displayEntry($entry, offX, this.offY, $entry.data('jg.jwidth'), $entry.data('jg.jheight'), this.buildingRow.height);
17624      offX += $entry.data('jg.jwidth') + settings.margins;
17625    }
17626
17627    //Gallery Height
17628    this.galleryHeightToSet = this.offY + this.buildingRow.height + this.border;
17629    this.$gallery.height(this.galleryHeightToSet + this.getSpinnerHeight());
17630
17631    if (!isLastRow || (this.buildingRow.height <= settings.rowHeight && buildingRowRes)) {
17632      //Ready for a new row
17633      this.offY += this.buildingRow.height + settings.margins;
17634      this.rows += 1;
17635      this.clearBuildingRow();
17636      this.$gallery.trigger('jg.rowflush');
17637    }
17638  };
17639
17640  /**
17641   * Checks the width of the gallery container, to know if a new justification is needed
17642   */
17643  var scrollBarOn = false;
17644  JustifiedGallery.prototype.checkWidth = function () {
17645    this.checkWidthIntervalId = setInterval($.proxy(function () {
17646      var galleryWidth = parseFloat(this.$gallery.width());
17647      if (hasScrollBar() === scrollBarOn) {
17648        if (Math.abs(galleryWidth - this.galleryWidth) > this.settings.refreshSensitivity) {
17649          this.galleryWidth = galleryWidth;
17650          this.rewind();
17651
17652          // Restart to analyze
17653          this.startImgAnalyzer(true);
17654        }
17655      } else {
17656        scrollBarOn = hasScrollBar();
17657        this.galleryWidth = galleryWidth;
17658      }
17659    }, this), this.settings.refreshTime);
17660  };
17661
17662  /**
17663   * @returns {boolean} a boolean saying if the spinner is active or not
17664   */
17665  JustifiedGallery.prototype.isSpinnerActive = function () {
17666    return this.spinner.intervalId !== null;
17667  };
17668
17669  /**
17670   * @returns {int} the spinner height
17671   */
17672  JustifiedGallery.prototype.getSpinnerHeight = function () {
17673    return this.spinner.$el.innerHeight();
17674  };
17675
17676  /**
17677   * Stops the spinner animation and modify the gallery height to exclude the spinner
17678   */
17679  JustifiedGallery.prototype.stopLoadingSpinnerAnimation = function () {
17680    clearInterval(this.spinner.intervalId);
17681    this.spinner.intervalId = null;
17682    this.$gallery.height(this.$gallery.height() - this.getSpinnerHeight());
17683    this.spinner.$el.detach();
17684  };
17685
17686  /**
17687   * Starts the spinner animation
17688   */
17689  JustifiedGallery.prototype.startLoadingSpinnerAnimation = function () {
17690    var spinnerContext = this.spinner;
17691    var $spinnerPoints = spinnerContext.$el.find('span');
17692    clearInterval(spinnerContext.intervalId);
17693    this.$gallery.append(spinnerContext.$el);
17694    this.$gallery.height(this.offY + this.buildingRow.height + this.getSpinnerHeight());
17695    spinnerContext.intervalId = setInterval(function () {
17696      if (spinnerContext.phase < $spinnerPoints.length) {
17697        $spinnerPoints.eq(spinnerContext.phase).fadeTo(spinnerContext.timeSlot, 1);
17698      } else {
vendor: 4,333 bytes, lines 17699-17843
17699        $spinnerPoints.eq(spinnerContext.phase - $spinnerPoints.length).fadeTo(spinnerContext.timeSlot, 0);
17700      }
17701      spinnerContext.phase = (spinnerContext.phase + 1) % ($spinnerPoints.length * 2);
17702    }, spinnerContext.timeSlot);
17703  };
17704
17705  /**
17706   * Rewind the image analysis to start from the first entry.
17707   */
17708  JustifiedGallery.prototype.rewind = function () {
17709    this.lastFetchedEntry = null;
17710    this.lastAnalyzedIndex = -1;
17711    this.offY = this.border;
17712    this.rows = 0;
17713    this.clearBuildingRow();
17714  };
17715
17716  /**
17717   * Update the entries searching it from the justified gallery HTML element
17718   *
17719   * @param norewind if norewind only the new entries will be changed (i.e. randomized, sorted or filtered)
17720   * @returns {boolean} true if some entries has been founded
17721   */
17722  JustifiedGallery.prototype.updateEntries = function (norewind) {
17723    var newEntries;
17724
17725    if (norewind && this.lastFetchedEntry != null) {
17726      newEntries = $(this.lastFetchedEntry).nextAll(this.settings.selector).toArray();
17727    } else {
17728      this.entries = [];
17729      newEntries = this.$gallery.children(this.settings.selector).toArray();
17730    }
17731
17732    if (newEntries.length > 0) {
17733
17734      // Sort or randomize
17735      if ($.isFunction(this.settings.sort)) {
17736        newEntries = this.sortArray(newEntries);
17737      } else if (this.settings.randomize) {
17738        newEntries = this.shuffleArray(newEntries);
17739      }
17740      this.lastFetchedEntry = newEntries[newEntries.length - 1];
17741
17742      // Filter
17743      if (this.settings.filter) {
17744        newEntries = this.filterArray(newEntries);
17745      } else {
17746        this.resetFilters(newEntries);
17747      }
17748
17749    }
17750
17751    this.entries = this.entries.concat(newEntries);
17752    return true;
17753  };
17754
17755  /**
17756   * Apply the entries order to the DOM, iterating the entries and appending the images
17757   *
17758   * @param entries the entries that has been modified and that must be re-ordered in the DOM
17759   */
17760  JustifiedGallery.prototype.insertToGallery = function (entries) {
17761    var that = this;
17762    $.each(entries, function () {
17763      $(this).appendTo(that.$gallery);
17764    });
17765  };
17766
17767  /**
17768   * Shuffle the array using the Fisher-Yates shuffle algorithm
17769   *
17770   * @param a the array to shuffle
17771   * @return the shuffled array
17772   */
17773  JustifiedGallery.prototype.shuffleArray = function (a) {
17774    var i, j, temp;
17775    for (i = a.length - 1; i > 0; i--) {
17776      j = Math.floor(Math.random() * (i + 1));
17777      temp = a[i];
17778      a[i] = a[j];
17779      a[j] = temp;
17780    }
17781    this.insertToGallery(a);
17782    return a;
17783  };
17784
17785  /**
17786   * Sort the array using settings.comparator as comparator
17787   *
17788   * @param a the array to sort (it is sorted)
17789   * @return the sorted array
17790   */
17791  JustifiedGallery.prototype.sortArray = function (a) {
17792    a.sort(this.settings.sort);
17793    this.insertToGallery(a);
17794    return a;
17795  };
17796
17797  /**
17798   * Reset the filters removing the 'jg-filtered' class from all the entries
17799   *
17800   * @param a the array to reset
17801   */
17802  JustifiedGallery.prototype.resetFilters = function (a) {
17803    for (var i = 0; i < a.length; i++) $(a[i]).removeClass('jg-filtered');
17804  };
17805
17806  /**
17807   * Filter the entries considering theirs classes (if a string has been passed) or using a function for filtering.
17808   *
17809   * @param a the array to filter
17810   * @return the filtered array
17811   */
17812  JustifiedGallery.prototype.filterArray = function (a) {
17813    var settings = this.settings;
17814    if ($.type(settings.filter) === 'string') {
17815      // Filter only keeping the entries passed in the string
17816      return a.filter(function (el) {
17817        var $el = $(el);
17818        if ($el.is(settings.filter)) {
17819          $el.removeClass('jg-filtered');
17820          return true;
17821        } else {
17822          $el.addClass('jg-filtered').removeClass('jg-visible');
17823          return false;
17824        }
17825      });
17826    } else if ($.isFunction(settings.filter)) {
17827      // Filter using the passed function
17828      var filteredArr = a.filter(settings.filter);
17829      for (var i = 0; i < a.length; i++) {
17830        if (filteredArr.indexOf(a[i]) == -1) {
17831          $(a[i]).addClass('jg-filtered').removeClass('jg-visible');
17832        } else {
17833          $(a[i]).removeClass('jg-filtered');
17834        }
17835      }
17836      return filteredArr;
17837    }
17838  };
17839
17840  /**
17841   * Destroy the Justified Gallery instance.
17842   *
17843   * It clears all the css properties added in the style attributes. We doesn't backup the original
17844   * values for those css attributes, because it costs (performance) and because in general one
17845   * shouldn't use the style attribute for an uniform set of images (where we suppose the use of
17846   * classes). Creating a backup is also difficult because JG could be called multiple times and
17847   * with different style attributes.
17848   */
17849  JustifiedGallery.prototype.destroy = function () {
17850    clearInterval(this.checkWidthIntervalId);
17851
17852    $.each(this.entries, $.proxy(function(_, entry) {
17853      var $entry = $(entry);
17854
17855      // Reset entry style
17856      $entry.css('width', '');
17857      $entry.css('height', '');
17858      $entry.css('top', '');
17859      $entry.css('left', '');
17860      $entry.data('jg.loaded', undefined);
17861      $entry.removeClass('jg-entry');
17862
17863      // Reset image style
17864      var $img = this.imgFromEntry($entry);
17865      $img.css('width', '');
17866      $img.css('height', '');
17867      $img.css('margin-left', '');
17868      $img.css('margin-top', '');
17869      $img.attr('src', $img.data('jg.originalSrc'));
17870      $img.data('jg.originalSrc', undefined);
17871
17872      // Remove caption
17873      this.removeCaptionEventsHandlers($entry);
17874      var $caption = this.captionFromEntry($entry);
17875      if ($entry.data('jg.createdCaption')) {
17876        // remove also the caption element (if created by jg)
17877        $entry.data('jg.createdCaption', undefined);
17878        if ($caption !== null) $caption.remove();
17879      } else {
17880        if ($caption !== null) $caption.fadeTo(0, 1);
17881      }
17882
17883    }, this));
17884
17885    this.$gallery.css('height', '');
17886    this.$gallery.removeClass('justified-gallery');
17887    this.$gallery.data('jg.controller', undefined);
17888  };
17889
17890  /**
17891   * Analyze the images and builds the rows. It returns if it found an image that is not loaded.
17892   *
17893   * @param isForResize if the image analyzer is called for resizing or not, to call a different callback at the end
17894   */
17895  JustifiedGallery.prototype.analyzeImages = function (isForResize) {
17896    for (var i = this.lastAnalyzedIndex + 1; i < this.entries.length; i++) {
17897      var $entry = $(this.entries[i]);
17898      if ($entry.data('jg.loaded') === true || $entry.data('jg.loaded') === 'skipped') {
17899        var availableWidth = this.galleryWidth - 2 * this.border - (
17900            (this.buildingRow.entriesBuff.length - 1) * this.settings.margins);
17901        var imgAspectRatio = $entry.data('jg.width') / $entry.data('jg.height');
17902        if (availableWidth / (this.buildingRow.aspectRatio + imgAspectRatio) < this.settings.rowHeight) {
17903          this.flushRow(false);
17904          if(++this.yield.flushed >= this.yield.every) {
17905            this.startImgAnalyzer(isForResize);
17906            return;
17907          }
17908        }
17909
17910        this.buildingRow.entriesBuff.push($entry);
17911        this.buildingRow.aspectRatio += imgAspectRatio;
17912        this.buildingRow.width += imgAspectRatio * this.settings.rowHeight;
17913        this.lastAnalyzedIndex = i;
17914
17915      } else if ($entry.data('jg.loaded') !== 'error') {
17916        return;
17917      }
17918    }
17919
17920    // Last row flush (the row is not full)
17921    if (this.buildingRow.entriesBuff.length > 0) this.flushRow(true);
17922
17923    if (this.isSpinnerActive()) {
17924      this.stopLoadingSpinnerAnimation();
17925    }
17926
17927    /* Stop, if there is, the timeout to start the analyzeImages.
17928     This is because an image can be set loaded, and the timeout can be set,
17929     but this image can be analyzed yet.
17930     */
17931    this.stopImgAnalyzerStarter();
17932
17933    //On complete callback
17934    this.$gallery.trigger(isForResize ? 'jg.resize' : 'jg.complete');
17935    this.$gallery.height(this.galleryHeightToSet);
17936  };
17937
17938  /**
17939   * Stops any ImgAnalyzer starter (that has an assigned timeout)
17940   */
17941  JustifiedGallery.prototype.stopImgAnalyzerStarter = function () {
17942    this.yield.flushed = 0;
17943    if (this.imgAnalyzerTimeout !== null) clearTimeout(this.imgAnalyzerTimeout);
17944  };
17945
17946  /**
17947   * Starts the image analyzer. It is not immediately called to let the browser to update the view
17948   *
17949   * @param isForResize specifies if the image analyzer must be called for resizing or not
17950   */
17951  JustifiedGallery.prototype.startImgAnalyzer = function (isForResize) {
17952    var that = this;
17953    this.stopImgAnalyzerStarter();
17954    this.imgAnalyzerTimeout = setTimeout(function () {
17955      that.analyzeImages(isForResize);
17956    }, 0.001); // we can't start it immediately due to a IE different behaviour
17957  };
17958
17959  /**
17960   * Checks if the image is loaded or not using another image object. We cannot use the 'complete' image property,
17961   * because some browsers, with a 404 set complete = true.
17962   *
17963   * @param imageSrc the image src to load
17964   * @param onLoad callback that is called when the image has been loaded
vendor: 13,320 bytes, lines 17965-18315
17965   * @param onError callback that is called in case of an error
17966   */
17967  JustifiedGallery.prototype.onImageEvent = function (imageSrc, onLoad, onError) {
17968    if (!onLoad && !onError) return;
17969
17970    var memImage = new Image();
17971    var $memImage = $(memImage);
17972    if (onLoad) {
17973      $memImage.one('load', function () {
17974        $memImage.off('load error');
17975        onLoad(memImage);
17976      });
17977    }
17978    if (onError) {
17979      $memImage.one('error', function() {
17980        $memImage.off('load error');
17981        onError(memImage);
17982      });
17983    }
17984    memImage.src = imageSrc;
17985  };
17986
17987  /**
17988   * Init of Justified Gallery controlled
17989   * It analyzes all the entries starting theirs loading and calling the image analyzer (that works with loaded images)
17990   */
17991  JustifiedGallery.prototype.init = function () {
17992    var imagesToLoad = false, skippedImages = false, that = this;
17993    $.each(this.entries, function (index, entry) {
17994      var $entry = $(entry);
17995      var $image = that.imgFromEntry($entry);
17996
17997      $entry.addClass('jg-entry');
17998
17999      if ($entry.data('jg.loaded') !== true && $entry.data('jg.loaded') !== 'skipped') {
18000
18001        // Link Rel global overwrite
18002        if (that.settings.rel !== null) $entry.attr('rel', that.settings.rel);
18003
18004        // Link Target global overwrite
18005        if (that.settings.target !== null) $entry.attr('target', that.settings.target);
18006
18007        if ($image !== null) {
18008
18009          // Image src
18010          var imageSrc = that.extractImgSrcFromImage($image);
18011          $image.attr('src', imageSrc);
18012
18013          /* If we have the height and the width, we don't wait that the image is loaded, but we start directly
18014           * with the justification */
18015          if (that.settings.waitThumbnailsLoad === false) {
18016            var width = parseFloat($image.attr('width'));
18017            var height = parseFloat($image.attr('height'));
18018            if (!isNaN(width) && !isNaN(height)) {
18019              $entry.data('jg.width', width);
18020              $entry.data('jg.height', height);
18021              $entry.data('jg.loaded', 'skipped');
18022              skippedImages = true;
18023              that.startImgAnalyzer(false);
18024              return true; // continue
18025            }
18026          }
18027
18028          $entry.data('jg.loaded', false);
18029          imagesToLoad = true;
18030
18031          // Spinner start
18032          if (!that.isSpinnerActive()) that.startLoadingSpinnerAnimation();
18033
18034          that.onImageEvent(imageSrc, function (loadImg) { // image loaded
18035            $entry.data('jg.width', loadImg.width);
18036            $entry.data('jg.height', loadImg.height);
18037            $entry.data('jg.loaded', true);
18038            that.startImgAnalyzer(false);
18039          }, function () { // image load error
18040            $entry.data('jg.loaded', 'error');
18041            that.startImgAnalyzer(false);
18042          });
18043
18044        } else {
18045          $entry.data('jg.loaded', true);
18046          $entry.data('jg.width', $entry.width() | parseFloat($entry.css('width')) | 1);
18047          $entry.data('jg.height', $entry.height() | parseFloat($entry.css('height')) | 1);
18048        }
18049
18050      }
18051
18052    });
18053
18054    if (!imagesToLoad && !skippedImages) this.startImgAnalyzer(false);
18055    this.checkWidth();
18056  };
18057
18058  /**
18059   * Checks that it is a valid number. If a string is passed it is converted to a number
18060   *
18061   * @param settingContainer the object that contains the setting (to allow the conversion)
18062   * @param settingName the setting name
18063   */
18064  JustifiedGallery.prototype.checkOrConvertNumber = function (settingContainer, settingName) {
18065    if ($.type(settingContainer[settingName]) === 'string') {
18066      settingContainer[settingName] = parseFloat(settingContainer[settingName]);
18067    }
18068
18069    if ($.type(settingContainer[settingName]) === 'number') {
18070      if (isNaN(settingContainer[settingName])) throw 'invalid number for ' + settingName;
18071    } else {
18072      throw settingName + ' must be a number';
18073    }
18074  };
18075
18076  /**
18077   * Checks the sizeRangeSuffixes and, if necessary, converts
18078   * its keys from string (e.g. old settings with 'lt100') to int.
18079   */
18080  JustifiedGallery.prototype.checkSizeRangesSuffixes = function () {
18081    if ($.type(this.settings.sizeRangeSuffixes) !== 'object') {
18082      throw 'sizeRangeSuffixes must be defined and must be an object';
18083    }
18084
18085    var suffixRanges = [];
18086    for (var rangeIdx in this.settings.sizeRangeSuffixes) {
18087      if (this.settings.sizeRangeSuffixes.hasOwnProperty(rangeIdx)) suffixRanges.push(rangeIdx);
18088    }
18089
18090    var newSizeRngSuffixes = {0: ''};
18091    for (var i = 0; i < suffixRanges.length; i++) {
18092      if ($.type(suffixRanges[i]) === 'string') {
18093        try {
18094          var numIdx = parseInt(suffixRanges[i].replace(/^[a-z]+/, ''), 10);
18095          newSizeRngSuffixes[numIdx] = this.settings.sizeRangeSuffixes[suffixRanges[i]];
18096        } catch (e) {
18097          throw 'sizeRangeSuffixes keys must contains correct numbers (' + e + ')';
18098        }
18099      } else {
18100        newSizeRngSuffixes[suffixRanges[i]] = this.settings.sizeRangeSuffixes[suffixRanges[i]];
18101      }
18102    }
18103
18104    this.settings.sizeRangeSuffixes = newSizeRngSuffixes;
18105  };
18106
18107  /**
18108   * check and convert the maxRowHeight setting
18109   */
18110  JustifiedGallery.prototype.retrieveMaxRowHeight = function () {
18111    var newMaxRowHeight = { };
18112
18113    if ($.type(this.settings.maxRowHeight) === 'string') {
18114      if (this.settings.maxRowHeight.match(/^[0-9]+%$/)) {
18115        newMaxRowHeight.value = parseFloat(this.settings.maxRowHeight.match(/^([0-9]+)%$/)[1]) / 100;
18116        newMaxRowHeight.isPercentage = false;
18117      } else {
18118        newMaxRowHeight.value = parseFloat(this.settings.maxRowHeight);
18119        newMaxRowHeight.isPercentage = true;
18120      }
18121    } else if ($.type(this.settings.maxRowHeight) === 'number') {
18122      newMaxRowHeight.value = this.settings.maxRowHeight;
18123      newMaxRowHeight.isPercentage = false;
18124    } else if (this.settings.maxRowHeight === false ||
18125        this.settings.maxRowHeight === null ||
18126        typeof this.settings.maxRowHeight == 'undefined') {
18127      return null;
18128    } else {
18129      throw 'maxRowHeight must be a number or a percentage';
18130    }
18131
18132    // check if the converted value is not a number
18133    if (isNaN(newMaxRowHeight.value)) throw 'invalid number for maxRowHeight';
18134
18135    // check values
18136    if (newMaxRowHeight.isPercentage) {
18137      if (newMaxRowHeight.value < 100) newMaxRowHeight.value = 100;
18138    }
18139
18140    return newMaxRowHeight;
18141  };
18142
18143  /**
18144   * Checks the settings
18145   */
18146  JustifiedGallery.prototype.checkSettings = function () {
18147    this.checkSizeRangesSuffixes();
18148
18149    this.checkOrConvertNumber(this.settings, 'rowHeight');
18150    this.checkOrConvertNumber(this.settings, 'margins');
18151    this.checkOrConvertNumber(this.settings, 'border');
18152
18153    var lastRowModes = [
18154      'justify',
18155      'nojustify',
18156      'left',
18157      'center',
18158      'right',
18159      'hide'
18160    ];
18161    if (lastRowModes.indexOf(this.settings.lastRow) === -1) {
18162      throw 'lastRow must be one of: ' + lastRowModes.join(', ');
18163    }
18164
18165    this.checkOrConvertNumber(this.settings, 'justifyThreshold');
18166    if (this.settings.justifyThreshold < 0 || this.settings.justifyThreshold > 1) {
18167      throw 'justifyThreshold must be in the interval [0,1]';
18168    }
18169    if ($.type(this.settings.cssAnimation) !== 'boolean') {
18170      throw 'cssAnimation must be a boolean';
18171    }
18172
18173    if ($.type(this.settings.captions) !== 'boolean') throw 'captions must be a boolean';
18174    this.checkOrConvertNumber(this.settings.captionSettings, 'animationDuration');
18175
18176    this.checkOrConvertNumber(this.settings.captionSettings, 'visibleOpacity');
18177    if (this.settings.captionSettings.visibleOpacity < 0 ||
18178        this.settings.captionSettings.visibleOpacity > 1) {
18179      throw 'captionSettings.visibleOpacity must be in the interval [0, 1]';
18180    }
18181
18182    this.checkOrConvertNumber(this.settings.captionSettings, 'nonVisibleOpacity');
18183    if (this.settings.captionSettings.nonVisibleOpacity < 0 ||
18184        this.settings.captionSettings.nonVisibleOpacity > 1) {
18185      throw 'captionSettings.nonVisibleOpacity must be in the interval [0, 1]';
18186    }
18187
18188    this.checkOrConvertNumber(this.settings, 'imagesAnimationDuration');
18189    this.checkOrConvertNumber(this.settings, 'refreshTime');
18190    this.checkOrConvertNumber(this.settings, 'refreshSensitivity');
18191    if ($.type(this.settings.randomize) !== 'boolean') throw 'randomize must be a boolean';
18192    if ($.type(this.settings.selector) !== 'string') throw 'selector must be a string';
18193
18194    if (this.settings.sort !== false && !$.isFunction(this.settings.sort)) {
18195      throw 'sort must be false or a comparison function';
18196    }
18197
18198    if (this.settings.filter !== false && !$.isFunction(this.settings.filter) &&
18199        $.type(this.settings.filter) !== 'string') {
18200      throw 'filter must be false, a string or a filter function';
18201    }
18202  };
18203
18204  /**
18205   * It brings all the indexes from the sizeRangeSuffixes and it orders them. They are then sorted and returned.
18206   * @returns {Array} sorted suffix ranges
18207   */
18208  JustifiedGallery.prototype.retrieveSuffixRanges = function () {
18209    var suffixRanges = [];
18210    for (var rangeIdx in this.settings.sizeRangeSuffixes) {
18211      if (this.settings.sizeRangeSuffixes.hasOwnProperty(rangeIdx)) suffixRanges.push(parseInt(rangeIdx, 10));
18212    }
18213    suffixRanges.sort(function (a, b) { return a > b ? 1 : a < b ? -1 : 0; });
18214    return suffixRanges;
18215  };
18216
18217  /**
18218   * Update the existing settings only changing some of them
18219   *
18220   * @param newSettings the new settings (or a subgroup of them)
18221   */
18222  JustifiedGallery.prototype.updateSettings = function (newSettings) {
18223    // In this case Justified Gallery has been called again changing only some options
18224    this.settings = $.extend({}, this.settings, newSettings);
18225    this.checkSettings();
18226
18227    // As reported in the settings: negative value = same as margins, 0 = disabled
18228    this.border = this.settings.border >= 0 ? this.settings.border : this.settings.margins;
18229
18230    this.maxRowHeight = this.retrieveMaxRowHeight();
18231    this.suffixRanges = this.retrieveSuffixRanges();
18232  };
18233
18234  /**
18235   * Justified Gallery plugin for jQuery
18236   *
18237   * Events
18238   *  - jg.complete : called when all the gallery has been created
18239   *  - jg.resize : called when the gallery has been resized
18240   *  - jg.rowflush : when a new row appears
18241   *
18242   * @param arg the action (or the settings) passed when the plugin is called
18243   * @returns {*} the object itself
18244   */
18245  $.fn.justifiedGallery = function (arg) {
18246    return this.each(function (index, gallery) {
18247
18248      var $gallery = $(gallery);
18249      $gallery.addClass('justified-gallery');
18250
18251      var controller = $gallery.data('jg.controller');
18252      if (typeof controller === 'undefined') {
18253        // Create controller and assign it to the object data
18254        if (typeof arg !== 'undefined' && arg !== null && $.type(arg) !== 'object') {
18255          if (arg === 'destroy') return; // Just a call to an unexisting object
18256          throw 'The argument must be an object';
18257        }
18258        controller = new JustifiedGallery($gallery, $.extend({}, $.fn.justifiedGallery.defaults, arg));
18259        $gallery.data('jg.controller', controller);
18260      } else if (arg === 'norewind') {
18261        // In this case we don't rewind: we analyze only the latest images (e.g. to complete the last unfinished row
18262        // ... left to be more readable
18263      } else if (arg === 'destroy') {
18264        controller.destroy();
18265        return;
18266      } else {
18267        // In this case Justified Gallery has been called again changing only some options
18268        controller.updateSettings(arg);
18269        controller.rewind();
18270      }
18271
18272      // Update the entries list
18273      if (!controller.updateEntries(arg === 'norewind')) return;
18274
18275      // Init justified gallery
18276      controller.init();
18277
18278    });
18279  };
18280
18281  // Default options
18282  $.fn.justifiedGallery.defaults = {
18283    sizeRangeSuffixes: { }, /* e.g. Flickr configuration
18284        {
18285          100: '_t',  // used when longest is less than 100px
18286          240: '_m',  // used when longest is between 101px and 240px
18287          320: '_n',  // ...
18288          500: '',
18289          640: '_z',
18290          1024: '_b'  // used as else case because it is the last
18291        }
18292    */
18293    thumbnailPath: undefined, /* If defined, sizeRangeSuffixes is not used, and this function is used to determine the
18294    path relative to a specific thumbnail size. The function should accept respectively three arguments:
18295    current path, width and height */
18296    rowHeight: 120,
18297    maxRowHeight: false, // false or negative value to deactivate. Positive number to express the value in pixels,
18298                         // A string '[0-9]+%' to express in percentage (e.g. 300% means that the row height
18299                         // can't exceed 3 * rowHeight)
18300    margins: 1,
18301    border: -1, // negative value = same as margins, 0 = disabled, any other value to set the border
18302
18303    lastRow: 'nojustify', // … which is the same as 'left', or can be 'justify', 'center', 'right' or 'hide'
18304
18305    justifyThreshold: 0.90, /* if row width / available space > 0.90 it will be always justified
18306                             * (i.e. lastRow setting is not considered) */
18307    waitThumbnailsLoad: true,
18308    captions: true,
18309    cssAnimation: true,
18310    imagesAnimationDuration: 500, // ignored with css animations
18311    captionSettings: { // ignored with css animations
18312      animationDuration: 500,
18313      visibleOpacity: 0.7,
18314      nonVisibleOpacity: 0.0
18315    },
18316    rel: null, // rewrite the rel of each analyzed links
18317    target: null, // rewrite the target of all links
18318    extension: /\.[^.\\/]+$/, // regexp to capture the extension of an image
18319    refreshTime: 200, // time interval (in ms) to check if the page changes its width
18320    refreshSensitivity: 0, // change in width allowed (in px) without re-building the gallery
18321    randomize: false,
18322    sort: false, /*
18323      - false: to do not sort
18324      - function: to sort them using the function as comparator (see Array.prototype.sort())
18325    */
18326    filter: false, /*
18327      - false, null or undefined: for a disabled filter
18328      - a string: an entry is kept if entry.is(filter string) returns true
18329                  see jQuery's .is() function for further information
18330      - a function: invoked with arguments (entry, index, array). Return true to keep the entry, false otherwise.
18331                    It follows the specifications of the Array.prototype.filter() function of JavaScript.
18332    */
18333    selector: 'a, div:not(.spinner)' // The selector that is used to know what are the entries of the gallery
18334  };
18335
18336}(jQuery));
18337/*!
18338* Customized version of iScroll.js 0.0.1
18339* It fixes bugs affecting its integration with fullpage.js
18340*/
18341/*! iScroll v5.2.0 ~ (c) 2008-2016 Matteo Spinelli ~ http://cubiq.org/license */
18342(function (window, document, Math) {
18343var rAF = window.requestAnimationFrame  ||
18344    window.webkitRequestAnimationFrame  ||
18345    window.mozRequestAnimationFrame     ||
18346    window.oRequestAnimationFrame       ||
18347    window.msRequestAnimationFrame      ||
18348    function (callback) { window.setTimeout(callback, 1000 / 60); };
18349
18350var utils = (function () {
18351    var me = {};
18352
18353    var _elementStyle = document.createElement('div').style;
18354    var _vendor = (function () {
18355        var vendors = ['t', 'webkitT', 'MozT', 'msT', 'OT'],
18356            transform,
18357            i = 0,
18358            l = vendors.length;
18359
18360        for ( ; i < l; i++ ) {
18361            transform = vendors[i] + 'ransform';
18362            if ( transform in _elementStyle ) return vendors[i].substr(0, vendors[i].length-1);
18363        }
18364
18365        return false;
18366    })();
18367
18368    function _prefixStyle (style) {
18369        if ( _vendor === false ) return false;
18370        if ( _vendor === '' ) return style;
18371        return _vendor + style.charAt(0).toUpperCase() + style.substr(1);
18372    }
18373
18374    me.getTime = Date.now || function getTime () { return new Date().getTime(); };
18375
18376    me.extend = function (target, obj) {
18377        for ( var i in obj ) {
18378            target[i] = obj[i];
18379        }
18380    };
18381
18382    me.addEvent = function (el, type, fn, capture) {
18383        el.addEventListener(type, fn, !!capture);
18384    };
18385
18386    me.removeEvent = function (el, type, fn, capture) {
18387        el.removeEventListener(type, fn, !!capture);
18388    };
18389
18390    me.prefixPointerEvent = function (pointerEvent) {
18391        return window.MSPointerEvent ?
18392            'MSPointer' + pointerEvent.charAt(7).toUpperCase() + pointerEvent.substr(8):
18393            pointerEvent;
18394    };
18395
18396    me.momentum = function (current, start, time, lowerMargin, wrapperSize, deceleration) {
18397        var distance = current - start,
18398            speed = Math.abs(distance) / time,
18399            destination,
18400            duration;
18401
18402        deceleration = deceleration === undefined ? 0.0006 : deceleration;
18403
18404        destination = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 );
18405        duration = speed / deceleration;
18406
18407        if ( destination < lowerMargin ) {
18408            destination = wrapperSize ? lowerMargin - ( wrapperSize / 2.5 * ( speed / 8 ) ) : lowerMargin;
18409            distance = Math.abs(destination - current);
18410            duration = distance / speed;
18411        } else if ( destination > 0 ) {
18412            destination = wrapperSize ? wrapperSize / 2.5 * ( speed / 8 ) : 0;
18413            distance = Math.abs(current) + destination;
18414            duration = distance / speed;
18415        }
18416
18417        return {
18418            destination: Math.round(destination),
18419            duration: duration
18420        };
18421    };
18422
18423    var _transform = _prefixStyle('transform');
18424
18425    me.extend(me, {
18426        hasTransform: _transform !== false,
18427        hasPerspective: _prefixStyle('perspective') in _elementStyle,
18428        hasTouch: 'ontouchstart' in window,
18429        hasPointer: !!(window.PointerEvent || window.MSPointerEvent), // IE10 is prefixed
18430        hasTransition: _prefixStyle('transition') in _elementStyle
18431    });
18432
18433    /*
18434    This should find all Android browsers lower than build 535.19 (both stock browser and webview)
18435    - galaxy S2 is ok
18436    - 2.3.6 : `AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1`
18437    - 4.0.4 : `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
18438   - galaxy S3 is badAndroid (stock brower, webview)
18439     `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
18440   - galaxy S4 is badAndroid (stock brower, webview)
18441     `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
18442   - galaxy S5 is OK
18443     `AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36 (Chrome/)`
18444   - galaxy S6 is OK
18445     `AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36 (Chrome/)`
18446  */
18447    me.isBadAndroid = (function() {
18448        var appVersion = window.navigator.appVersion;
18449        // Android browser is not a chrome browser.
18450        if (/Android/.test(appVersion) && !(/Chrome\/\d/.test(appVersion))) {
18451            var safariVersion = appVersion.match(/Safari\/(\d+.\d)/);
18452            if(safariVersion && typeof safariVersion === "object" && safariVersion.length >= 2) {
18453                return parseFloat(safariVersion[1]) < 535.19;
18454            } else {
18455                return true;
18456            }
18457        } else {
18458            return false;
18459        }
18460    })();
18461
18462    me.extend(me.style = {}, {
18463        transform: _transform,
18464        transitionTimingFunction: _prefixStyle('transitionTimingFunction'),
18465        transitionDuration: _prefixStyle('transitionDuration'),
18466        transitionDelay: _prefixStyle('transitionDelay'),
18467        transformOrigin: _prefixStyle('transformOrigin')
18468    });
18469
18470    me.hasClass = function (e, c) {
18471        var re = new RegExp("(^|\\s)" + c + "(\\s|$)");
18472        return re.test(e.className);
18473    };
18474
18475    me.addClass = function (e, c) {
18476        if ( me.hasClass(e, c) ) {
18477            return;
18478        }
18479
18480        var newclass = e.className.split(' ');
18481        newclass.push(c);
18482        e.className = newclass.join(' ');
18483    };
18484
18485    me.removeClass = function (e, c) {
18486        if ( !me.hasClass(e, c) ) {
18487            return;
18488        }
18489
18490        var re = new RegExp("(^|\\s)" + c + "(\\s|$)", 'g');
18491        e.className = e.className.replace(re, ' ');
18492    };
18493
18494    me.offset = function (el) {
18495        var left = -el.offsetLeft,
18496            top = -el.offsetTop;
18497
18498        // jshint -W084
18499        while (el = el.offsetParent) {
18500            left -= el.offsetLeft;
18501            top -= el.offsetTop;
18502        }
18503        // jshint +W084
18504
18505        return {
18506            left: left,
18507            top: top
18508        };
18509    };
18510
18511    me.preventDefaultException = function (el, exceptions) {
18512        for ( var i in exceptions ) {
18513            if ( exceptions[i].test(el[i]) ) {
18514                return true;
18515            }
18516        }
18517
18518        return false;
18519    };
18520
18521    me.extend(me.eventType = {}, {
18522        touchstart: 1,
18523        touchmove: 1,
18524        touchend: 1,
18525
18526        mousedown: 2,
18527        mousemove: 2,
18528        mouseup: 2,
18529
18530        pointerdown: 3,
18531        pointermove: 3,
18532        pointerup: 3,
18533
18534        MSPointerDown: 3,
18535        MSPointerMove: 3,
18536        MSPointerUp: 3
18537    });
18538
18539    me.extend(me.ease = {}, {
18540        quadratic: {
18541            style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
18542            fn: function (k) {
18543                return k * ( 2 - k );
18544            }
18545        },
18546        circular: {
18547            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)
18548            fn: function (k) {
18549                return Math.sqrt( 1 - ( --k * k ) );
18550            }
18551        },
18552        back: {
18553            style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',
18554            fn: function (k) {
18555                var b = 4;
18556                return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1;
18557            }
18558        },
18559        bounce: {
18560            style: '',
18561            fn: function (k) {
18562                if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {
18563                    return 7.5625 * k * k;
18564                } else if ( k < ( 2 / 2.75 ) ) {
18565                    return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;
18566                } else if ( k < ( 2.5 / 2.75 ) ) {
18567                    return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;
18568                } else {
18569                    return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;
18570                }
18571            }
18572        },
18573        elastic: {
18574            style: '',
18575            fn: function (k) {
18576                var f = 0.22,
18577                    e = 0.4;
18578
18579                if ( k === 0 ) { return 0; }
18580                if ( k == 1 ) { return 1; }
18581
18582                return ( e * Math.pow( 2, - 10 * k ) * Math.sin( ( k - f / 4 ) * ( 2 * Math.PI ) / f ) + 1 );
18583            }
18584        }
18585    });
18586
18587    me.tap = function (e, eventName) {
18588        var ev = document.createEvent('Event');
18589        ev.initEvent(eventName, true, true);
18590        ev.pageX = e.pageX;
18591        ev.pageY = e.pageY;
18592        e.target.dispatchEvent(ev);
18593    };
18594
18595    me.click = function (e) {
18596        var target = e.target,
18597            ev;
18598
18599        if ( !(/(SELECT|INPUT|TEXTAREA)/i).test(target.tagName) ) {
18600            // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/initMouseEvent
18601            // initMouseEvent is deprecated.
18602            ev = document.createEvent(window.MouseEvent ? 'MouseEvents' : 'Event');
18603            ev.initEvent('click', true, true);
18604            ev.view = e.view || window;
18605            ev.detail = 1;
18606            ev.screenX = target.screenX || 0;
18607            ev.screenY = target.screenY || 0;
18608            ev.clientX = target.clientX || 0;
18609            ev.clientY = target.clientY || 0;
18610            ev.ctrlKey = !!e.ctrlKey;
18611            ev.altKey = !!e.altKey;
18612            ev.shiftKey = !!e.shiftKey;
18613            ev.metaKey = !!e.metaKey;
18614            ev.button = 0;
18615            ev.relatedTarget = null;
18616            ev._constructed = true;
18617            target.dispatchEvent(ev);
18618        }
18619    };
18620
18621    return me;
18622})();
18623function IScroll (el, options) {
18624    this.wrapper = typeof el == 'string' ? document.querySelector(el) : el;
18625    this.scroller = this.wrapper.children[0];
18626    this.scrollerStyle = this.scroller.style;       // cache style for better performance
18627
18628    this.options = {
18629
18630        resizeScrollbars: true,
18631
18632        mouseWheelSpeed: 20,
18633
18634        snapThreshold: 0.334,
18635
18636// INSERT POINT: OPTIONS
18637        disablePointer : !utils.hasPointer,
18638        disableTouch : utils.hasPointer || !utils.hasTouch,
18639        disableMouse : utils.hasPointer || utils.hasTouch,
18640        startX: 0,
18641        startY: 0,
18642        scrollY: true,
18643        directionLockThreshold: 5,
18644        momentum: true,
18645
18646        bounce: true,
18647        bounceTime: 600,
18648        bounceEasing: '',
18649
18650        preventDefault: true,
18651        preventDefaultException: { tagName: /^(INPUT|TEXTAREA|BUTTON|
18651SELECT|LABEL)$/ },
18652
18653        HWCompositing: true,
18654        useTransition: true,
18655        useTransform: true,
18656        bindToWrapper: typeof window.onmousedown === "undefined"
18657    };
18658
18659    for ( var i in options ) {
18660        this.options[i] = options[i];
18661    }
18662
18663    // Normalize options
18664    this.translateZ = this.options.HWCompositing && utils.hasPerspective ? ' translateZ(0)' : '';
18665
18666    this.options.useTransition = utils.hasTransition && this.options.useTransition;
18667    this.options.useTransform = utils.hasTransform && this.options.useTransform;
18668
18669    this.options.eventPassthrough = this.options.eventPassthrough === true ? 'vertical' : this.options.eventPassthrough;
18670    this.options.preventDefault = !this.options.eventPassthrough && this.options.preventDefault;
18671
18672    // If you want eventPassthrough I have to lock one of the axes
18673    this.options.scrollY = this.options.eventPassthrough == 'vertical' ? false : this.options.scrollY;
18674    this.options.scrollX = this.options.eventPassthrough == 'horizontal' ? false : this.options.scrollX;
18675
18676    // With eventPassthrough we also need lockDirection mechanism
18677    this.options.freeScroll = this.options.freeScroll && !this.options.eventPassthrough;
18678    this.options.directionLockThreshold = this.options.eventPassthrough ? 0 : this.options.directionLockThreshold;
18679
18680    this.options.bounceEasing = typeof this.options.bounceEasing == 'string' ? utils.ease[this.options.bounceEasing] || utils.ease.circular : this.options.bounceEasing;
18681
18682    this.options.resizePolling = this.options.resizePolling === undefined ? 60 : this.options.resizePolling;
18683
18684    if ( this.options.tap === true ) {
18685        this.options.tap = 'tap';
18686    }
18687
18688    // https://github.com/cubiq/iscroll/issues/1029
18689    if (!this.options.useTransition && !this.options.useTransform) {
18690        if(!(/relative|absolute/i).test(this.scrollerStyle.position)) {
18691            this.scrollerStyle.position = "relative";
18692        }
18693    }
18694
18695    if ( this.options.shrinkScrollbars == 'scale' ) {
18696        this.options.useTransition = false;
18697    }
18698
18699    this.options.invertWheelDirection = this.options.invertWheelDirection ? -1 : 1;
18700
18701// INSERT POINT: NORMALIZATION
18702
18703    // Some defaults
18704    this.x = 0;
18705    this.y = 0;
18706    this.directionX = 0;
18707    this.directionY = 0;
18708    this._events = {};
18709
18710// INSERT POINT: DEFAULTS
18711
18712    this._init();
18713    this.refresh();
18714
18715    this.scrollTo(this.options.startX, this.options.startY);
18716    this.enable();
18717}
18718
18719IScroll.prototype = {
18720    version: '5.2.0',
18721
18722    _init: function () {
18723        this._initEvents();
18724
18725        if ( this.options.scrollbars || this.options.indicators ) {
18726            this._initIndicators();
18727        }
18728
18729        if ( this.options.mouseWheel ) {
18730            this._initWheel();
18731        }
18732
18733        if ( this.options.snap ) {
18734            this._initSnap();
18735        }
18736
18737        if ( this.options.keyBindings ) {
18738            this._initKeys();
18739        }
18740
18741// INSERT POINT: _init
18742
18743    },
18744
18745    destroy: function () {
18746        this._initEvents(true);
18747        clearTimeout(this.resizeTimeout);
18748        this.resizeTimeout = null;
18749        this._execEvent('destroy');
18750    },
18751
18752    _transitionEnd: function (e) {
18753        if ( e.target != this.scroller || !this.isInTransition ) {
18754            return;
18755        }
18756
18757        this._transitionTime();
18758        if ( !this.resetPosition(this.options.bounceTime) ) {
18759            this.isInTransition = false;
18760            this._execEvent('scrollEnd');
18761        }
18762    },
18763
18764    _start: function (e) {
18765        // React to left mouse button only
18766        if ( utils.eventType[e.type] != 1 ) {
18767          // for button property
18768          // http://unixpapa.com/js/mouse.html
18769          var button;
18770        if (!e.which) {
18771          /* IE case */
18772          button = (e.button < 2) ? 0 :
18773                   ((e.button == 4) ? 1 : 2);
18774        } else {
18775          /* All others */
18776          button = e.button;
18777        }
18778            if ( button !== 0 ) {
18779                return;
18780            }
18781        }
18782
18783        if ( !this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated) ) {
18784            return;
18785        }
18786
18787        if ( this.options.preventDefault && !utils.isBadAndroid && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) {
18788            e.preventDefault();
18789        }
18790
18791        var point = e.touches ? e.touches[0] : e,
18792            pos;
18793
18794        this.initiated  = utils.eventType[e.type];
18795        this.moved      = false;
18796        this.distX      = 0;
18797        this.distY      = 0;
18798        this.directionX = 0;
18799        this.directionY = 0;
18800        this.directionLocked = 0;
18801
18802        this.startTime = utils.getTime();
18803
18804        if ( this.options.useTransition && this.isInTransition ) {
18805            this._transitionTime();
18806            this.isInTransition = false;
18807            pos = this.getComputedPosition();
18808            this._translate(Math.round(pos.x), Math.round(pos.y));
18809            this._execEvent('scrollEnd');
18810        } else if ( !this.options.useTransition && this.isAnimating ) {
18811            this.isAnimating = false;
18812            this._execEvent('scrollEnd');
18813        }
18814
18815        this.startX    = this.x;
18816        this.startY    = this.y;
18817        this.absStartX = this.x;
18818        this.absStartY = this.y;
18819        this.pointX    = point.pageX;
18820        this.pointY    = point.pageY;
18821
18822        this._execEvent('beforeScrollStart');
18823    },
18824
18825    _move: function (e) {
18826        if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) {
18827            return;
18828        }
18829
18830        if ( this.options.preventDefault ) {    // increases performance on Android? TODO: check!
18831            e.preventDefault();
18832        }
18833
18834        var point       = e.touches ? e.touches[0] : e,
18835            deltaX      = point.pageX - this.pointX,
18836            deltaY      = point.pageY - this.pointY,
18837            timestamp   = utils.getTime(),
18838            newX, newY,
18839            absDistX, absDistY;
18840
18841        this.pointX     = point.pageX;
18842        this.pointY     = point.pageY;
18843
18844        this.distX      += deltaX;
18845        this.distY      += deltaY;
18846        absDistX        = Math.abs(this.distX);
18847        absDistY        = Math.abs(this.distY);
18848
18849        // We need to move at least 10 pixels for the scrolling to initiate
18850        if ( timestamp - this.endTime > 300 && (absDistX < 10 && absDistY < 10) ) {
18851            return;
18852        }
18853
18854        // If you are scrolling in one direction lock the other
18855        if ( !this.directionLocked && !this.options.freeScroll ) {
18856            if ( absDistX > absDistY + this.options.directionLockThreshold ) {
18857                this.directionLocked = 'h';     // lock horizontally
18858            } else if ( absDistY >= absDistX + this.options.directionLockThreshold ) {
18859                this.directionLocked = 'v';     // lock vertically
18860            } else {
18861                this.directionLocked = 'n';     // no lock
18862            }
18863        }
18864
18865        if ( this.directionLocked == 'h' ) {
18866            if ( this.options.eventPassthrough == 'vertical' ) {
18867                e.preventDefault();
18868            } else if ( this.options.eventPassthrough == 'horizontal' ) {
18869                this.initiated = false;
18870                return;
18871            }
18872
18873            deltaY = 0;
18874        } else if ( this.directionLocked == 'v' ) {
18875            if ( this.options.eventPassthrough == 'horizontal' ) {
18876                e.preventDefault();
18877            } else if ( this.options.eventPassthrough == 'vertical' ) {
18878                this.initiated = false;
18879                return;
18880            }
18881
18882            deltaX = 0;
18883        }
18884
18885        deltaX = this.hasHorizontalScroll ? deltaX : 0;
18886        deltaY = this.hasVerticalScroll ? deltaY : 0;
18887
18888        newX = this.x + deltaX;
18889        newY = this.y + deltaY;
18890
18891        // Slow down if outside of the boundaries
18892        if ( newX > 0 || newX < this.maxScrollX ) {
18893            newX = this.options.bounce ? this.x + deltaX / 3 : newX > 0 ? 0 : this.maxScrollX;
18894        }
18895        if ( newY > 0 || newY < this.maxScrollY ) {
18896            newY = this.options.bounce ? this.y + deltaY / 3 : newY > 0 ? 0 : this.maxScrollY;
18897        }
18898
18899        this.directionX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0;
18900        this.directionY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0;
18901
18902        if ( !this.moved ) {
18903            this._execEvent('scrollStart');
18904        }
18905
18906        this.moved = true;
18907
18908        this._translate(newX, newY);
18909
18910/* REPLACE START: _move */
18911
18912        if ( timestamp - this.startTime > 300 ) {
18913            this.startTime = timestamp;
18914            this.startX = this.x;
18915            this.startY = this.y;
18916        }
18917
18918/* REPLACE END: _move */
18919
18920    },
18921
18922    _end: function (e) {
18923        if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) {
18924            return;
18925        }
18926
18927        if ( this.options.preventDefault && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) {
18928            e.preventDefault();
18929        }
18930
18931        var point = e.changedTouches ? e.changedTouches[0] : e,
18932            momentumX,
18933            momentumY,
18934            duration = utils.getTime() - this.startTime,
18935            newX = Math.round(this.x),
18936            newY = Math.round(this.y),
18937            distanceX = Math.abs(newX - this.startX),
18938            distanceY = Math.abs(newY - this.startY),
18939            time = 0,
18940            easing = '';
18941
18942        this.isInTransition = 0;
18943        this.initiated = 0;
18944        this.endTime = utils.getTime();
18945
18946        // reset if we are outside of the boundaries
18947        if ( this.resetPosition(this.options.bounceTime) ) {
18948            return;
18949        }
18950
18951        this.scrollTo(newX, newY);  // ensures that the last position is rounded
18952
18953        // we scrolled less than 10 pixels
18954        if ( !this.moved ) {
18955            if ( this.options.tap ) {
18956                utils.tap(e, this.options.tap);
18957            }
18958
18959            if ( this.options.click ) {
18960                utils.click(e);
18961            }
18962
18963            this._execEvent('scrollCancel');
18964            return;
18965        }
18966
18967        if ( this._events.flick && duration < 200 && distanceX < 100 && distanceY < 100 ) {
18968            this._execEvent('flick');
18969            return;
18970        }
18971
18972        // start momentum animation if needed
18973        if ( this.options.momentum && duration < 300 ) {
18974            momentumX = this.hasHorizontalScroll ? utils.momentum(this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperW
18974idth : 0, this.options.deceleration) : { destination: newX, duration: 0 };
18975            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 };
18976            newX = momentumX.destination;
18977            newY = momentumY.destination;
18978            time = Math.max(momentumX.duration, momentumY.duration);
18979            this.isInTransition = 1;
18980        }
18981
18982
18983        if ( this.options.snap ) {
18984            var snap = this._nearestSnap(newX, newY);
18985            this.currentPage = snap;
18986            time = this.options.snapSpeed || Math.max(
18987                    Math.max(
18988                        Math.min(Math.abs(newX - snap.x), 1000),
18989                        Math.min(Math.abs(newY - snap.y), 1000)
18990                    ), 300);
18991            newX = snap.x;
18992            newY = snap.y;
18993
18994            this.directionX = 0;
18995            this.directionY = 0;
18996            easing = this.options.bounceEasing;
18997        }
18998
18999// INSERT POINT: _end
19000
19001        if ( newX != this.x || newY != this.y ) {
19002            // change easing function when scroller goes out of the boundaries
19003            if ( newX > 0 || newX < this.maxScrollX || newY > 0 || newY < this.maxScrollY ) {
19004                easing = utils.ease.quadratic;
19005            }
19006
19007            this.scrollTo(newX, newY, time, easing);
19008            return;
19009        }
19010
19011        this._execEvent('scrollEnd');
19012    },
19013
19014    _resize: function () {
19015        var that = this;
19016
19017        clearTimeout(this.resizeTimeout);
19018
19019        this.resizeTimeout = setTimeout(function () {
19020            that.refresh();
19021        }, this.options.resizePolling);
19022    },
19023
19024    resetPosition: function (time) {
19025        var x = this.x,
19026            y = this.y;
19027
19028        time = time || 0;
19029
19030        if ( !this.hasHorizontalScroll || this.x > 0 ) {
19031            x = 0;
19032        } else if ( this.x < this.maxScrollX ) {
19033            x = this.maxScrollX;
19034        }
19035
19036        if ( !this.hasVerticalScroll || this.y > 0 ) {
19037            y = 0;
19038        } else if ( this.y < this.maxScrollY ) {
19039            y = this.maxScrollY;
19040        }
19041
19042        if ( x == this.x && y == this.y ) {
19043            return false;
19044        }
19045
19046        this.scrollTo(x, y, time, this.options.bounceEasing);
19047
19048        return true;
19049    },
19050
19051    disable: function () {
19052        this.enabled = false;
19053    },
19054
19055    enable: function () {
19056        this.enabled = true;
19057    },
19058
19059    refresh: function () {
19060        var rf = this.wrapper.offsetHeight;     // Force reflow
19061
19062        this.wrapperWidth   = this.wrapper.clientWidth;
19063        this.wrapperHeight  = this.wrapper.clientHeight;
19064
19065/* REPLACE START: refresh */
19066
19067        this.scrollerWidth  = this.scroller.offsetWidth;
19068        this.scrollerHeight = this.scroller.offsetHeight;
19069
19070        this.maxScrollX     = this.wrapperWidth - this.scrollerWidth;
19071        this.maxScrollY     = this.wrapperHeight - this.scrollerHeight;
19072
19073/* REPLACE END: refresh */
19074
19075        this.hasHorizontalScroll    = this.options.scrollX && this.maxScrollX < 0;
19076        this.hasVerticalScroll      = this.options.scrollY && this.maxScrollY < 0;
19077
19078        if ( !this.hasHorizontalScroll ) {
19079            this.maxScrollX = 0;
19080            this.scrollerWidth = this.wrapperWidth;
19081        }
19082
19083        if ( !this.hasVerticalScroll ) {
19084            this.maxScrollY = 0;
19085            this.scrollerHeight = this.wrapperHeight;
19086        }
19087
19088        this.endTime = 0;
19089        this.directionX = 0;
19090        this.directionY = 0;
19091
19092        this.wrapperOffset = utils.offset(this.wrapper);
19093
19094        this._execEvent('refresh');
19095
19096        this.resetPosition();
19097
19098// INSERT POINT: _refresh
19099
19100    },
19101
19102    on: function (type, fn) {
19103        if ( !this._events[type] ) {
19104            this._events[type] = [];
19105        }
19106
19107        this._events[type].push(fn);
19108    },
19109
19110    off: function (type, fn) {
19111        if ( !this._events[type] ) {
19112            return;
19113        }
19114
19115        var index = this._events[type].indexOf(fn);
19116
19117        if ( index > -1 ) {
19118            this._events[type].splice(index, 1);
19119        }
19120    },
19121
19122    _execEvent: function (type) {
19123        if ( !this._events[type] ) {
19124            return;
19125        }
19126
19127        var i = 0,
19128            l = this._events[type].length;
19129
19130        if ( !l ) {
19131            return;
19132        }
19133
19134        for ( ; i < l; i++ ) {
19135            this._events[type][i].apply(this, [].slice.call(arguments, 1));
19136        }
19137    },
19138
19139    scrollBy: function (x, y, time, easing) {
19140        x = this.x + x;
19141        y = this.y + y;
19142        time = time || 0;
19143
19144        this.scrollTo(x, y, time, easing);
19145    },
19146
19147    scrollTo: function (x, y, time, easing) {
19148        easing = easing || utils.ease.circular;
19149
19150        this.isInTransition = this.options.useTransition && time > 0;
19151        var transitionType = this.options.useTransition && easing.style;
19152        if ( !time || transitionType ) {
19153                if(transitionType) {
19154                    this._transitionTimingFunction(easing.style);
19155                    this._transitionTime(time);
19156                }
19157            this._translate(x, y);
19158        } else {
19159            this._animate(x, y, time, easing.fn);
19160        }
19161    },
19162
19163    scrollToElement: function (el, time, offsetX, offsetY, easing) {
19164        el = el.nodeType ? el : this.scroller.querySelector(el);
19165
19166        if ( !el ) {
19167            return;
19168        }
19169
19170        var pos = utils.offset(el);
19171
19172        pos.left -= this.wrapperOffset.left;
19173        pos.top  -= this.wrapperOffset.top;
19174
19175        // if offsetX/Y are true we center the element to the screen
19176        if ( offsetX === true ) {
19177            offsetX = Math.round(el.offsetWidth / 2 - this.wrapper.offsetWidth / 2);
19178        }
19179        if ( offsetY === true ) {
19180            offsetY = Math.round(el.offsetHeight / 2 - this.wrapper.offsetHeight / 2);
19181        }
19182
19183        pos.left -= offsetX || 0;
19184        pos.top  -= offsetY || 0;
19185
19186        pos.left = pos.left > 0 ? 0 : pos.left < this.maxScrollX ? this.maxScrollX : pos.left;
19187        pos.top  = pos.top  > 0 ? 0 : pos.top  < this.maxScrollY ? this.maxScrollY : pos.top;
19188
19189        time = time === undefined || time === null || time === 'auto' ? Math.max(Math.abs(this.x-pos.left), Math.abs(this.y-pos.top)) : time;
19190
19191        this.scrollTo(pos.left, pos.top, time, easing);
19192    },
19193
19194    _transitionTime: function (time) {
19195        if (!this.options.useTransition) {
19196            return;
19197        }
19198        time = time || 0;
19199        var durationProp = utils.style.transitionDuration;
19200        if(!durationProp) {
19201            return;
19202        }
19203
19204        this.scrollerStyle[durationProp] = time + 'ms';
19205
19206        if ( !time && utils.isBadAndroid ) {
19207            this.scrollerStyle[durationProp] = '0.0001ms';
19208            // remove 0.0001ms
19209            var self = this;
19210            rAF(function() {
19211                if(self.scrollerStyle[durationProp] === '0.0001ms') {
19212                    self.scrollerStyle[durationProp] = '0s';
19213                }
19214            });
19215        }
19216
19217
19218        if ( this.indicators ) {
19219            for ( var i = this.indicators.length; i--; ) {
19220                this.indicators[i].transitionTime(time);
19221            }
19222        }
19223
19224
19225// INSERT POINT: _transitionTime
19226
19227    },
19228
19229    _transitionTimingFunction: function (easing) {
19230        this.scrollerStyle[utils.style.transitionTimingFunction] = easing;
19231
19232
19233        if ( this.indicators ) {
19234            for ( var i = this.indicators.length; i--; ) {
19235                this.indicators[i].transitionTimingFunction(easing);
19236            }
19237        }
19238
19239
19240// INSERT POINT: _transitionTimingFunction
19241
19242    },
19243
19244    _translate: function (x, y) {
19245        //Uncode addition
19246        if ( (" " + this.wrapper.className + " ").replace(/[\n\t]/g, " ").indexOf(" no-scrolloverflow ") > -1 )
19247            return false;
19248
19249        if ( this.options.useTransform ) {
19250
19251/* REPLACE START: _translate */
19252
19253            this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ;
19254
19255/* REPLACE END: _translate */
19256
19257        } else {
19258            x = Math.round(x);
19259            y = Math.round(y);
19260            this.scrollerStyle.left = x + 'px';
19261            this.scrollerStyle.top = y + 'px';
19262        }
19263
19264        this.x = x;
19265        this.y = y;
19266
19267
19268        if ( this.indicators ) {
19269            for ( var i = this.indicators.length; i--; ) {
19270                this.indicators[i].updatePosition();
19271            }
19272        }
19273
19274        //Uncode addition
19275        var uncodevent = new CustomEvent('fp-slide-scroll');
19276        window.dispatchEvent(uncodevent);
19277
19278// INSERT POINT: _translate
19279
19280    },
19281
19282    _initEvents: function (remove) {
19283        var eventType = remove ? utils.removeEvent : utils.addEvent,
19284            target = this.options.bindToWrapper ? this.wrapper : window;
19285
19286        eventType(window, 'orientationchange', this);
19287        eventType(window, 'resize', this);
19288
19289        if ( this.options.click ) {
19290            eventType(this.wrapper, 'click', this, true);
19291        }
19292
19293        if ( !this.options.disableMouse ) {
19294            eventType(this.wrapper, 'mousedown', this);
19295            eventType(target, 'mousemove', this);
19296            eventType(target, 'mousecancel', this);
19297            eventType(target, 'mouseup', this);
19298        }
19299
19300        if ( utils.hasPointer && !this.options.disablePointer ) {
19301            eventType(this.wrapper, utils.prefixPointerEvent('pointerdown'), this);
19302            eventType(target, utils.prefixPointerEvent('pointermove'), this);
19303            eventType(target, utils.prefixPointerEvent('pointercancel'), this);
19304            eventType(target, utils.prefixPointerEvent('pointerup'), this);
19305        }
19306
19307        if ( utils.hasTouch && !this.options.disableTouch ) {
19308            eventType(this.wrapper, 'touchstart', this);
19309            eventType(target, 'touchmove', this);
19310            eventType(target, 'touchcancel', this);
19311            eventType(target, 'touchend', this);
19312        }
19313
19314        eventType(this.scroller, 'transitionend', this);
19315        eventType(this.scroller, 'webkitTransitionEnd', this);
19316        eventType(this.scroller, 'oTransitionEnd', this);
19317        eventType(this.scroller, 'MSTransitionEnd', this);
19318    },
19319
19320    getComputedPosition: function () {
19321        var matrix = window.getComputedStyle(this.scroller, null),
19322            x, y;
19323
19324        if ( this.options.useTransform ) {
19325            matrix = matrix[utils.style.transform].split(')')[0].split(', ');
19326            x = +(matrix[12] || matrix[4]);
19327            y = +(matrix[13] || matrix[5]);
19328        } else {
19329            x = +matrix.left.replace(/[^-\d.]/g, '');
19330            y = +matrix.top.replace(/[^-\d.]/g, '');
19331        }
19332
19333        return { x: x, y: y };
19334    },
19335    _initIndicators: function () {
19336        var interactive = this.options.interactiveScrollbars,
19337            customStyle = typeof this.options.scrollbars != 'string',
19338            indicators = [],
19339            indicator;
19340
19341        var that = this;
19342
19343        this.indicators = [];
19344
19345        if ( this.options.scrollbars ) {
19346            // Vertical scrollbar
19347            if ( this.options.scrollY ) {
19348                indicator = {
19349                    el: createDefaultScrollbar('v', interactive, this.options.scrollbars),
19350                    interactive: interactive,
19351                    defaultScrollbars: true,
19352                    customStyle: customStyle,
19353                    resize: this.options.resizeScrollbars,
19354                    shrink: this.options.shrinkScrollbars,
19355                    fade: this.options.fadeScrollbars,
19356                    listenX: false
19357                };
19358
19359                this.wrapper.appendChild(indicator.el);
19360                indicators.push(indicator);
19361            }
19362
19363            // Horizontal scrollbar
19364            if ( this.options.scrollX ) {
19365                indicator = {
19366                    el: createDefaultScrollbar('h', interactive, this.options.scrollbars),
19367                    interactive: interactive,
19368                    defaultScrollbars: true,
19369                    customStyle: customStyle,
19370                    resize: this.options.resizeScrollbars,
19371                    shrink: this.options.shrinkScrollbars,
19372                    fade: this.options.fadeScrollbars,
19373                    listenY: false
19374                };
19375
19376                this.wrapper.appendChild(indicator.el);
19377                indicators.push(indicator);
19378            }
19379        }
19380
19381        if ( this.options.indicators ) {
19382            // TODO: check concat compatibility
19383            indicators = indicators.concat(this.options.indicators);
19384        }
19385
19386        for ( var i = indicators.length; i--; ) {
19387            this.indicators.push( new Indicator(this, indicators[i]) );
19388        }
19389
19390        // TODO: check if we can use array.map (wide compatibility and performance issues)
19391        function _indicatorsMap (fn) {
19392            if (that.indicators) {
19393                for ( var i = that.indicators.length; i--; ) {
19394                    fn.call(that.indicators[i]);
19395                }
19396            }
19397        }
19398
19399        if ( this.options.fadeScrollbars ) {
19400            this.on('scrollEnd', function () {
19401                _indicatorsMap(function () {
19402                    this.fade();
19403                });
19404            });
19405
19406            this.on('scrollCancel', function () {
19407                _indicatorsMap(function () {
19408                    this.fade();
19409                });
19410            });
19411
19412            this.on('scrollStart', function () {
19413                _indicatorsMap(function () {
19414                    this.fade(1);
19415                });
19416            });
19417
19418            this.on('beforeScrollStart', function () {
19419                _indicatorsMap(function () {
19420                    this.fade(1, true);
19421                });
19422            });
19423        }
19424
19425
19426        this.on('refresh', function () {
19427            _indicatorsMap(function () {
19428                this.refresh();
19429            });
19430        });
19431
19432        this.on('destroy', function () {
19433            _indicatorsMap(function () {
19434                this.destroy();
19435            });
19436
19437            delete this.indicators;
19438        });
19439    },
19440
19441    _initWheel: function () {
19442        utils.addEvent(this.wrapper, 'wheel', this);
19443        utils.addEvent(this.wrapper, 'mousewheel', this);
19444        utils.addEvent(this.wrapper, 'DOMMouseScroll', this);
19445
19446        this.on('destroy', function () {
19447            clearTimeout(this.wheelTimeout);
19448            this.wheelTimeout = null;
19449            utils.removeEvent(this.wrapper, 'wheel', this);
19450            utils.removeEvent(this.wrapper, 'mousewheel', this);
19451            utils.removeEvent(this.wrapper, 'DOMMouseScroll', this);
19452        });
19453    },
19454
19455    _wheel: function (e) {
19456        if ( !this.enabled ) {
19457            return;
19458        }
19459
19460        var wheelDeltaX, wheelDeltaY,
19461            newX, newY,
19462            that = this;
19463
19464        if ( this.wheelTimeout === undefined ) {
19465            that._execEvent('scrollStart');
19466        }
19467
19468        // Execute the scrollEnd event after 400ms the wheel stopped scrolling
19469        clearTimeout(this.wheelTimeout);
19470        this.wheelTimeout = setTimeout(function () {
19471            if(!that.options.snap) {
19472                that._execEvent('scrollEnd');
19473            }
19474            that.wheelTimeout = undefined;
19475        }, 400);
19476
19477        if ( 'deltaX' in e ) {
19478            if (e.deltaMode === 1) {
19479                wheelDeltaX = -e.deltaX * this.options.mouseWheelSpeed;
19480                wheelDeltaY = -e.deltaY * this.options.mouseWheelSpeed;
19481            } else {
19482                wheelDeltaX = -e.deltaX;
19483                wheelDeltaY = -e.deltaY;
19484            }
19485        } else if ( 'wheelDeltaX' in e ) {
19486            wheelDeltaX = e.wheelDeltaX / 120 * this.options.mouseWheelSpeed;
19487            wheelDeltaY = e.wheelDeltaY / 120 * this.options.mouseWheelSpeed;
19488        } else if ( 'wheelDelta' in e ) {
19489            wheelDeltaX = wheelDeltaY = e.wheelDelta / 120 * this.options.mouseWheelSpeed;
19490        } else if ( 'detail' in e ) {
19491            wheelDeltaX = wheelDeltaY = -e.detail / 3 * this.options.mouseWheelSpeed;
19492        } else {
19493            return;
19494        }
19495
19496        wheelDeltaX *= this.options.invertWheelDirection;
19497        wheelDeltaY *= this.options.invertWheelDirection;
19498
19499        if ( !this.hasVerticalScroll ) {
19500            wheelDeltaX = wheelDeltaY;
19501            wheelDeltaY = 0;
19502        }
19503
19504        if ( this.options.snap ) {
19505            newX = this.currentPage.pageX;
19506            newY = this.currentPage.pageY;
19507
19508            if ( wheelDeltaX > 0 ) {
19509                newX--;
19510            } else if ( wheelDeltaX < 0 ) {
19511                newX++;
19512            }
19513
19514            if ( wheelDeltaY > 0 ) {
19515                newY--;
19516            } else if ( wheelDeltaY < 0 ) {
19517                newY++;
19518            }
19519
19520            this.goToPage(newX, newY);
19521
19522            return;
19523        }
19524
19525        newX = this.x + Math.round(this.hasHorizontalScroll ? wheelDeltaX : 0);
19526        newY = this.y + Math.round(this.hasVerticalScroll ? wheelDeltaY : 0);
19527
19528        this.directionX = wheelDeltaX > 0 ? -1 : wheelDeltaX < 0 ? 1 : 0;
19529        this.directionY = wheelDeltaY > 0 ? -1 : wheelDeltaY < 0 ? 1 : 0;
19530
19531        if ( newX > 0 ) {
19532            newX = 0;
19533        } else if ( newX < this.maxScrollX ) {
19534            newX = this.maxScrollX;
19535        }
19536
19537        if ( newY > 0 ) {
19538            newY = 0;
19539        } else if ( newY < this.maxScrollY ) {
19540            newY = this.maxScrollY;
19541        }
19542
19543        this.scrollTo(newX, newY, 0);
19544
19545// INSERT POINT: _wheel
19546    },
19547
19548    _initSnap: function () {
19549        this.currentPage = {};
19550
19551        if ( typeof this.options.snap == 'string' ) {
19552            this.options.snap = this.scroller.querySelectorAll(this.options.snap);
19553        }
19554
19555        this.on('refresh', function () {
19556            var i = 0, l,
19557                m = 0, n,
19558                cx, cy,
19559                x = 0, y,
19560                stepX = this.options.snapStepX || this.wrapperWidth,
19561                stepY = this.options.snapStepY || this.wrapperHeight,
19562                el;
19563
19564            this.pages = [];
19565
19566            if ( !this.wrapperWidth || !this.wrapperHeight || !this.scrollerWidth || !this.scrollerHeight ) {
19567                return;
19568            }
19569
19570            if ( this.options.snap === true ) {
19571                cx = Math.round( stepX / 2 );
19572                cy = Math.round( stepY / 2 );
19573
19574                while ( x > -this.scrollerWidth ) {
19575                    this.pages[i] = [];
19576                    l = 0;
19577                    y = 0;
19578
19579                    while ( y > -this.scrollerHeight ) {
19580                        this.pages[i][l] = {
19581                            x: Math.max(x, this.maxScrollX),
19582                            y: Math.max(y, this.maxScrollY),
19583                            width: stepX,
19584                            height: stepY,
19585                            cx: x - cx,
19586                            cy: y - cy
19587                        };
19588
19589                        y -= stepY;
19590                        l++;
19591                    }
19592
19593                    x -= stepX;
19594                    i++;
19595                }
19596            } else {
19597                el = this.options.snap;
19598                l = el.length;
19599                n = -1;
19600
19601                for ( ; i < l; i++ ) {
19602                    if ( i === 0 || el[i].offsetLeft <= el[i-1].offsetLeft ) {
19603                        m = 0;
19604                        n++;
19605                    }
19606
19607                    if ( !this.pages[m] ) {
19608                        this.pages[m] = [];
19609                    }
19610
19611                    x = Math.max(-el[i].offsetLeft, this.maxScrollX);
19612                    y = Math.max(-el[i].offsetTop, this.maxScrollY);
19613                    cx = x - Math.round(el[i].offsetWidth / 2);
19614                    cy = y - Math.round(el[i].offsetHeight / 2);
19615
19616                    this.pages[m][n] = {
19617                        x: x,
19618                        y: y,
19619                        width: el[i].offsetWidth,
19620                        height: el[i].offsetHeight,
19621                        cx: cx,
19622                        cy: cy
19623                    };
19624
19625                    if ( x > this.maxScrollX ) {
19626                        m++;
19627                    }
19628                }
19629            }
19630
19631            this.goToPage(this.currentPage.pageX || 0, this.currentPage.pageY || 0, 0);
19632
19633            // Update snap threshold if needed
19634            if ( this.options.snapThreshold % 1 === 0 ) {
19635                this.snapThresholdX = this.options.snapThreshold;
19636                this.snapThresholdY = this.options.snapThreshold;
19637            } else {
19638                this.snapThresholdX = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].width * this.options.snapThreshold);
19639                this.snapThresholdY = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].height * this.options.snapThreshold);
19640            }
19641        });
19642
19643        this.on('flick', function () {
19644            var time = this.options.snapSpeed || Math.max(
19645                    Math.max(
19646                        Math.min(Math.abs(this.x - this.startX), 1000),
19647                        Math.min(Math.abs(this.y - this.startY), 1000)
19648                    ), 300);
19649
19650            this.goToPage(
19651                this.currentPage.pageX + this.directionX,
19652                this.currentPage.pageY + this.directionY,
19653                time
19654            );
19655        });
19656    },
19657
19658    _nearestSnap: function (x, y) {
19659        if ( !this.pages.length ) {
19660            return { x: 0, y: 0, pageX: 0, pageY: 0 };
19661        }
19662
19663        var i = 0,
19664            l = this.pages.length,
19665            m = 0;
19666
19667        // Check if we exceeded the snap threshold
19668        if ( Math.abs(x - this.absStartX) < this.snapThresholdX &&
19669            Math.abs(y - this.absStartY) < this.snapThresholdY ) {
19670            return this.currentPage;
19671        }
19672
19673        if ( x > 0 ) {
19674            x = 0;
19675        } else if ( x < this.maxScrollX ) {
19676            x = this.maxScrollX;
19677        }
19678
19679        if ( y > 0 ) {
19680            y = 0;
19681        } else if ( y < this.maxScrollY ) {
19682            y = this.maxScrollY;
19683        }
19684
19685        for ( ; i < l; i++ ) {
19686            if ( x >= this.pages[i][0].cx ) {
19687                x = this.pages[i][0].x;
19688                break;
19689            }
19690        }
19691
19692        l = this.pages[i].length;
19693
19694        for ( ; m < l; m++ ) {
19695            if ( y >= this.pages[0][m].cy ) {
19696                y = this.pages[0][m].y;
19697                break;
19698            }
19699        }
19700
19701        if ( i == this.currentPage.pageX ) {
19702            i += this.directionX;
19703
19704            if ( i < 0 ) {
19705                i = 0;
19706            } else if ( i >= this.pages.length ) {
19707                i = this.pages.length - 1;
19708            }
19709
19710            x = this.pages[i][0].x;
19711        }
19712
19713        if ( m == this.currentPage.pageY ) {
19714            m += this.directionY;
19715
19716            if ( m < 0 ) {
19717                m = 0;
19718            } else if ( m >= this.pages[0].length ) {
19719                m = this.pages[0].length - 1;
19720            }
19721
19722            y = this.pages[0][m].y;
19723        }
19724
19725        return {
19726            x: x,
19727            y: y,
19728            pageX: i,
19729            pageY: m
19730        };
19731    },
19732
19733    goToPage: function (x, y, time, easing) {
19734        easing = easing || this.options.bounceEasing;
19735
19736        if ( x >= this.pages.length ) {
19737            x = this.pages.length - 1;
19738        } else if ( x < 0 ) {
19739            x = 0;
19740        }
19741
19742        if ( y >= this.pages[x].length ) {
19743            y = this.pages[x].length - 1;
19744        } else if ( y < 0 ) {
19745            y = 0;
19746        }
19747
19748        var posX = this.pages[x][y].x,
19749            posY = this.pages[x][y].y;
19750
19751        time = time === undefined ? this.options.snapSpeed || Math.max(
19752            Math.max(
19753                Math.min(Math.abs(posX - this.x), 1000),
19754                Math.min(Math.abs(posY - this.y), 1000)
19755            ), 300) : time;
19756
19757        this.currentPage = {
19758            x: posX,
19759            y: posY,
19760            pageX: x,
19761            pageY: y
19762        };
19763
19764        this.scrollTo(posX, posY, time, easing);
19765    },
19766
19767    next: function (time, easing) {
19768        var x = this.currentPage.pageX,
19769            y = this.currentPage.pageY;
19770
19771        x++;
19772
19773        if ( x >= this.pages.length && this.hasVerticalScroll ) {
19774            x = 0;
19775            y++;
19776        }
19777
19778        this.goToPage(x, y, time, easing);
19779    },
19780
19781    prev: function (time, easing) {
19782        var x = this.currentPage.pageX,
19783            y = this.currentPage.pageY;
19784
19785        x--;
19786
19787        if ( x < 0 && this.hasVerticalScroll ) {
19788            x = 0;
19789            y--;
19790        }
19791
19792        this.goToPage(x, y, time, easing);
19793    },
19794
19795    _initKeys: function (e) {
19796        // default key bindings
19797        var keys = {
19798            pageUp: 33,
19799            pageDown: 34,
19800            end: 35,
19801            home: 36,
19802            left: 37,
19803            up: 38,
19804            right: 39,
19805            down: 40
19806        };
19807        var i;
19808
19809        // if you give me characters I give you keycode
19810        if ( typeof this.options.keyBindings == 'object' ) {
19811            for ( i in this.options.keyBindings ) {
19812                if ( typeof this.options.keyBindings[i] == 'string' ) {
19813                    this.options.keyBindings[i] = this.options.keyBindings[i].toUpperCase().charCo
19813deAt(0);
19814                }
19815            }
19816        } else {
19817            this.options.keyBindings = {};
19818        }
19819
19820        for ( i in keys ) {
19821            this.options.keyBindings[i] = this.options.keyBindings[i] || keys[i];
19822        }
19823
19824        utils.addEvent(window, 'keydown', this);
19825
19826        this.on('destroy', function () {
19827            utils.removeEvent(window, 'keydown', this);
19828        });
19829    },
19830
19831    _key: function (e) {
19832        if ( !this.enabled ) {
19833            return;
19834        }
19835
19836        var snap = this.options.snap,   // we are using this alot, better to cache it
19837            newX = snap ? this.currentPage.pageX : this.x,
19838            newY = snap ? this.currentPage.pageY : this.y,
19839            now = utils.getTime(),
19840            prevTime = this.keyTime || 0,
19841            acceleration = 0.250,
19842            pos;
19843
19844        if ( this.options.useTransition && this.isInTransition ) {
19845            pos = this.getComputedPosition();
19846
19847            this._translate(Math.round(pos.x), Math.round(pos.y));
19848            this.isInTransition = false;
19849        }
19850
19851        this.keyAcceleration = now - prevTime < 200 ? Math.min(this.keyAcceleration + acceleration, 50) : 0;
19852
19853        switch ( e.keyCode ) {
19854            case this.options.keyBindings.pageUp:
19855                if ( this.hasHorizontalScroll && !this.hasVerticalScroll ) {
19856                    newX += snap ? 1 : this.wrapperWidth;
19857                } else {
19858                    newY += snap ? 1 : this.wrapperHeight;
19859                }
19860                break;
19861            case this.options.keyBindings.pageDown:
19862                if ( this.hasHorizontalScroll && !this.hasVerticalScroll ) {
19863                    newX -= snap ? 1 : this.wrapperWidth;
19864                } else {
19865                    newY -= snap ? 1 : this.wrapperHeight;
19866                }
19867                break;
19868            case this.options.keyBindings.end:
19869                newX = snap ? this.pages.length-1 : this.maxScrollX;
19870                newY = snap ? this.pages[0].length-1 : this.maxScrollY;
19871                break;
19872            case this.options.keyBindings.home:
19873                newX = 0;
19874                newY = 0;
19875                break;
19876            case this.options.keyBindings.left:
19877                newX += snap ? -1 : 5 + this.keyAcceleration>>0;
19878                break;
19879            case this.options.keyBindings.up:
19880                newY += snap ? 1 : 5 + this.keyAcceleration>>0;
19881                break;
19882            case this.options.keyBindings.right:
19883                newX -= snap ? -1 : 5 + this.keyAcceleration>>0;
19884                break;
19885            case this.options.keyBindings.down:
19886                newY -= snap ? 1 : 5 + this.keyAcceleration>>0;
19887                break;
19888            default:
19889                return;
19890        }
19891
19892        if ( snap ) {
19893            this.goToPage(newX, newY);
19894            return;
19895        }
19896
19897        if ( newX > 0 ) {
19898            newX = 0;
19899            this.keyAcceleration = 0;
19900        } else if ( newX < this.maxScrollX ) {
19901            newX = this.maxScrollX;
19902            this.keyAcceleration = 0;
19903        }
19904
19905        if ( newY > 0 ) {
19906            newY = 0;
19907            this.keyAcceleration = 0;
19908        } else if ( newY < this.maxScrollY ) {
19909            newY = this.maxScrollY;
19910            this.keyAcceleration = 0;
19911        }
19912
19913        this.scrollTo(newX, newY, 0);
19914
19915        this.keyTime = now;
19916    },
19917
19918    _animate: function (destX, destY, duration, easingFn) {
19919        var that = this,
19920            startX = this.x,
19921            startY = this.y,
19922            startTime = utils.getTime(),
19923            destTime = startTime + duration;
19924
19925        function step () {
19926            var now = utils.getTime(),
19927                newX, newY,
19928                easing;
19929
19930            if ( now >= destTime ) {
19931                that.isAnimating = false;
19932                that._translate(destX, destY);
19933
19934                if ( !that.resetPosition(that.options.bounceTime) ) {
19935                    that._execEvent('scrollEnd');
19936                }
19937
19938                return;
19939            }
19940
19941            now = ( now - startTime ) / duration;
19942            easing = easingFn(now);
19943            newX = ( destX - startX ) * easing + startX;
19944            newY = ( destY - startY ) * easing + startY;
19945            that._translate(newX, newY);
19946
19947            if ( that.isAnimating ) {
19948                rAF(step);
19949            }
19950        }
19951
19952        this.isAnimating = true;
19953        step();
19954    },
19955    handleEvent: function (e) {
19956        switch ( e.type ) {
19957            case 'touchstart':
19958            case 'pointerdown':
19959            case 'MSPointerDown':
19960            case 'mousedown':
19961                this._start(e);
19962                break;
19963            case 'touchmove':
19964            case 'pointermove':
19965            case 'MSPointerMove':
19966            case 'mousemove':
19967                this._move(e);
19968                break;
19969            case 'touchend':
19970            case 'pointerup':
19971            case 'MSPointerUp':
19972            case 'mouseup':
19973            case 'touchcancel':
19974            case 'pointercancel':
19975            case 'MSPointerCancel':
19976            case 'mousecancel':
19977                this._end(e);
19978                break;
19979            case 'orientationchange':
19980            case 'resize':
19981                this._resize();
19982                break;
19983            case 'transitionend':
19984            case 'webkitTransitionEnd':
19985            case 'oTransitionEnd':
19986            case 'MSTransitionEnd':
19987                this._transitionEnd(e);
19988                break;
19989            case 'wheel':
19990            case 'DOMMouseScroll':
19991            case 'mousewheel':
19992                this._wheel(e);
19993                break;
19994            case 'keydown':
19995                this._key(e);
19996                break;
19997            case 'click':
19998                if ( this.enabled && !e._constructed ) {
19999                    e.preventDefault();
20000                    e.stopPropagation();
20001                }
20002                break;
20003        }
20004    }
20005};
20006function createDefaultScrollbar (direction, interactive, type) {
20007    var scrollbar = document.createElement('div'),
20008        indicator = document.createElement('div');
20009
20010    if ( type === true ) {
20011        scrollbar.style.cssText = 'position:absolute;z-index:9999';
20012        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';
20013    }
20014
20015    indicator.className = 'iScrollIndicator';
20016
20017    if ( direction == 'h' ) {
20018        if ( type === true ) {
20019            scrollbar.style.cssText += ';height:7px;left:2px;right:2px;bottom:0';
20020            indicator.style.height = '100%';
20021        }
20022        scrollbar.className = 'iScrollHorizontalScrollbar';
20023    } else {
20024        if ( type === true ) {
20025            scrollbar.style.cssText += ';width:7px;bottom:2px;top:2px;right:1px';
20026            indicator.style.width = '100%';
20027        }
20028        scrollbar.className = 'iScrollVerticalScrollbar';
20029    }
20030
20031    scrollbar.style.cssText += ';overflow:hidden';
20032
20033    if ( !interactive ) {
20034        scrollbar.style.pointerEvents = 'none';
20035    }
20036
20037    scrollbar.appendChild(indicator);
20038
20039    return scrollbar;
20040}
20041
20042function Indicator (scroller, options) {
20043    this.wrapper = typeof options.el == 'string' ? document.querySelector(options.el) : options.el;
20044    this.wrapperStyle = this.wrapper.style;
20045    this.indicator = this.wrapper.children[0];
20046    this.indicatorStyle = this.indicator.style;
20047    this.scroller = scroller;
20048
20049    this.options = {
20050        listenX: true,
20051        listenY: true,
20052        interactive: false,
20053        resize: true,
20054        defaultScrollbars: false,
20055        shrink: false,
20056        fade: false,
20057        speedRatioX: 0,
20058        speedRatioY: 0
20059    };
20060
20061    for ( var i in options ) {
20062        this.options[i] = options[i];
20063    }
20064
20065    this.sizeRatioX = 1;
20066    this.sizeRatioY = 1;
20067    this.maxPosX = 0;
20068    this.maxPosY = 0;
20069
20070    if ( this.options.interactive ) {
20071        if ( !this.options.disableTouch ) {
20072            utils.addEvent(this.indicator, 'touchstart', this);
20073            utils.addEvent(window, 'touchend', this);
20074        }
20075        if ( !this.options.disablePointer ) {
20076            utils.addEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this);
20077            utils.addEvent(window, utils.prefixPointerEvent('pointerup'), this);
20078        }
20079        if ( !this.options.disableMouse ) {
20080            utils.addEvent(this.indicator, 'mousedown', this);
20081            utils.addEvent(window, 'mouseup', this);
20082        }
20083    }
20084
20085    if ( this.options.fade ) {
20086        this.wrapperStyle[utils.style.transform] = this.scroller.translateZ;
20087        var durationProp = utils.style.transitionDuration;
20088        if(!durationProp) {
20089            return;
20090        }
20091        this.wrapperStyle[durationProp] = utils.isBadAndroid ? '0.0001ms' : '0ms';
20092        // remove 0.0001ms
20093        var self = this;
20094        if(utils.isBadAndroid) {
20095            rAF(function() {
20096                if(self.wrapperStyle[durationProp] === '0.0001ms') {
20097                    self.wrapperStyle[durationProp] = '0s';
20098                }
20099            });
20100        }
20101        this.wrapperStyle.opacity = '0';
20102    }
20103}
20104
20105Indicator.prototype = {
20106    handleEvent: function (e) {
20107        switch ( e.type ) {
20108            case 'touchstart':
20109            case 'pointerdown':
20110            case 'MSPointerDown':
20111            case 'mousedown':
20112                this._start(e);
20113                break;
20114            case 'touchmove':
20115            case 'pointermove':
20116            case 'MSPointerMove':
20117            case 'mousemove':
20118                this._move(e);
20119                break;
20120            case 'touchend':
20121            case 'pointerup':
20122            case 'MSPointerUp':
20123            case 'mouseup':
20124            case 'touchcancel':
20125            case 'pointercancel':
20126            case 'MSPointerCancel':
20127            case 'mousecancel':
20128                this._end(e);
20129                break;
20130        }
20131    },
20132
20133    destroy: function () {
20134        if ( this.options.fadeScrollbars ) {
20135            clearTimeout(this.fadeTimeout);
20136            this.fadeTimeout = null;
20137        }
20138        if ( this.options.interactive ) {
20139            utils.removeEvent(this.indicator, 'touchstart', this);
20140            utils.removeEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this);
20141            utils.removeEvent(this.indicator, 'mousedown', this);
20142
20143            utils.removeEvent(window, 'touchmove', this);
20144            utils.removeEvent(window, utils.prefixPointerEvent('pointermove'), this);
20145            utils.removeEvent(window, 'mousemove', this);
20146
20147            utils.removeEvent(window, 'touchend', this);
20148            utils.removeEvent(window, utils.prefixPointerEvent('pointerup'), this);
20149            utils.removeEvent(window, 'mouseup', this);
20150        }
20151
20152        if ( this.options.defaultScrollbars ) {
20153            this.wrapper.parentNode.removeChild(this.wrapper);
20154        }
20155    },
20156
20157    _start: function (e) {
20158        var point = e.touches ? e.touches[0] : e;
20159
20160        e.preventDefault();
20161        e.stopPropagation();
20162
20163        this.transitionTime();
20164
20165        this.initiated = true;
20166        this.moved = false;
20167        this.lastPointX = point.pageX;
20168        this.lastPointY = point.pageY;
20169
20170        this.startTime  = utils.getTime();
20171
20172        if ( !this.options.disableTouch ) {
20173            utils.addEvent(window, 'touchmove', this);
20174        }
20175        if ( !this.options.disablePointer ) {
20176            utils.addEvent(window, utils.prefixPointerEvent('pointermove'), this);
20177        }
20178        if ( !this.options.disableMouse ) {
20179            utils.addEvent(window, 'mousemove', this);
20180        }
20181
20182        this.scroller._execEvent('beforeScrollStart');
20183    },
20184
20185    _move: function (e) {
20186        var point = e.touches ? e.touches[0] : e,
20187            deltaX, deltaY,
20188            newX, newY,
20189            timestamp = utils.getTime();
20190
20191        if ( !this.moved ) {
20192            this.scroller._execEvent('scrollStart');
20193        }
20194
20195        this.moved = true;
20196
20197        deltaX = point.pageX - this.lastPointX;
20198        this.lastPointX = point.pageX;
20199
20200        deltaY = point.pageY - this.lastPointY;
20201        this.lastPointY = point.pageY;
20202
20203        newX = this.x + deltaX;
20204        newY = this.y + deltaY;
20205
20206        this._pos(newX, newY);
20207
20208// INSERT POINT: indicator._move
20209
20210        e.preventDefault();
20211        e.stopPropagation();
20212    },
20213
20214    _end: function (e) {
20215        if ( !this.initiated ) {
20216            return;
20217        }
20218
20219        this.initiated = false;
20220
20221        e.preventDefault();
20222        e.stopPropagation();
20223
20224        utils.removeEvent(window, 'touchmove', this);
20225        utils.removeEvent(window, utils.prefixPointerEvent('pointermove'), this);
20226        utils.removeEvent(window, 'mousemove', this);
20227
20228        if ( this.scroller.options.snap ) {
20229            var snap = this.scroller._nearestSnap(this.scroller.x, this.scroller.y);
20230
20231            var time = this.options.snapSpeed || Math.max(
20232                    Math.max(
20233                        Math.min(Math.abs(this.scroller.x - snap.x), 1000),
20234                        Math.min(Math.abs(this.scroller.y - snap.y), 1000)
20235                    ), 300);
20236
20237            if ( this.scroller.x != snap.x || this.scroller.y != snap.y ) {
20238                this.scroller.directionX = 0;
20239                this.scroller.directionY = 0;
20240                this.scroller.currentPage = snap;
20241                this.scroller.scrollTo(snap.x, snap.y, time, this.scroller.options.bounceEasing);
20242            }
20243        }
20244
20245        if ( this.moved ) {
20246            this.scroller._execEvent('scrollEnd');
20247        }
20248    },
20249
20250    transitionTime: function (time) {
20251        time = time || 0;
20252        var durationProp = utils.style.transitionDuration;
20253        if(!durationProp) {
20254            return;
20255        }
20256
20257        this.indicatorStyle[durationProp] = time + 'ms';
20258
20259        if ( !time && utils.isBadAndroid ) {
20260            this.indicatorStyle[durationProp] = '0.0001ms';
20261            // remove 0.0001ms
20262            var self = this;
20263            rAF(function() {
20264                if(self.indicatorStyle[durationProp] === '0.0001ms') {
20265                    self.indicatorStyle[durationProp] = '0s';
20266                }
20267            });
20268        }
20269    },
20270
20271    transitionTimingFunction: function (easing) {
20272        this.indicatorStyle[utils.style.transitionTimingFunction] = easing;
20273    },
20274
20275    refresh: function () {
20276        this.transitionTime();
20277
20278        if ( this.options.listenX && !this.options.listenY ) {
20279            this.indicatorStyle.display = this.scroller.hasHorizontalScroll ? 'block' : 'none';
20280        } else if ( this.options.listenY && !this.options.listenX ) {
20281            this.indicatorStyle.display = this.scroller.hasVerticalScroll ? 'block' : 'none';
20282        } else {
20283            this.indicatorStyle.display = this.scroller.hasHorizontalScroll || this.scroller.hasVerticalScroll ? 'block' : 'none';
20284        }
20285
20286        if ( this.scroller.hasHorizontalScroll && this.scroller.hasVerticalScroll ) {
20287            utils.addClass(this.wrapper, 'iScrollBothScrollbars');
20288            utils.removeClass(this.wrapper, 'iScrollLoneScrollbar');
20289
20290            if ( this.options.defaultScrollbars && this.options.customStyle ) {
20291                if ( this.options.listenX ) {
20292                    this.wrapper.style.right = '8px';
20293                } else {
20294                    this.wrapper.style.bottom = '8px';
20295                }
20296            }
20297        } else {
20298            utils.removeClass(this.wrapper, 'iScrollBothScrollbars');
20299            utils.addClass(this.wrapper, 'iScrollLoneScrollbar');
20300
20301            if ( this.options.defaultScrollbars && this.options.customStyle ) {
20302                if ( this.options.listenX ) {
20303                    this.wrapper.style.right = '2px';
20304                } else {
20305                    this.wrapper.style.bottom = '2px';
20306                }
20307            }
20308        }
20309
20310        var r = this.wrapper.offsetHeight;  // force refresh
20311
20312        if ( this.options.listenX ) {
20313            this.wrapperWidth = this.wrapper.clientWidth;
20314            if ( this.options.resize ) {
20315                this.indicatorWidth = Math.max(Math.round(this.wrapperWidth * this.wrapperWidth / (this.scroller.scrollerWidth || this.wrapperWidth || 1)), 8);
20316                this.indicatorStyle.width = this.indicatorWidth + 'px';
20317            } else {
20318                this.indicatorWidth = this.indicator.clientWidth;
20319            }
20320
20321            this.maxPosX = this.wrapperWidth - this.indicatorWidth;
20322
20323            if ( this.options.shrink == 'clip' ) {
20324                this.minBoundaryX = -this.indicatorWidth + 8;
20325                this.maxBoundaryX = this.wrapperWidth - 8;
20326            } else {
20327                this.minBoundaryX = 0;
20328                this.maxBoundaryX = this.maxPosX;
20329            }
20330
20331            this.sizeRatioX = this.options.speedRatioX || (this.scroller.maxScrollX && (this.maxPosX / this.scroller.maxScrollX));
20332        }
20333
20334        if ( this.options.listenY ) {
20335            this.wrapperHeight = this.wrapper.clientHeight;
20336            if ( this.options.resize ) {
20337                this.indicatorHeight = Math.max(Math.round(this.wrapperHeight * this.wrapperHeight / (this.scroller.scrollerHeight || this.wrapperHeight || 1)), 8);
20338                this.indicatorStyle.height = this.indicatorHeight + 'px';
20339            } else {
20340                this.indicatorHeight = this.indicator.clientHeight;
20341            }
20342
20343            this.maxPosY = this.wrapperHeight - this.indicatorHeight;
20344
20345            if ( this.options.shrink == 'clip' ) {
20346                this.minBoundaryY = -this.indicatorHeight + 8;
20347                this.maxBoundaryY = this.wrapperHeight - 8;
20348            } else {
20349                this.minBoundaryY = 0;
20350                this.maxBoundaryY = this.maxPosY;
20351            }
20352
20353            this.maxPosY = this.wrapperHeight - this.indicatorHeight;
20354            this.sizeRatioY = this.options.speedRatioY || (this.scroller.maxScrollY && (this.maxPosY / this.scroller.maxScrollY));
20355        }
20356
20357        this.updatePosition();
20358    },
20359
20360    updatePosition: function () {
20361        var x = this.options.listenX && Math.round(this.sizeRatioX * this.scroller.x) || 0,
20362            y = this.options.listenY && Math.round(this.sizeRatioY * this.scroller.y) || 0;
20363
20364        if ( !this.options.ignoreBoundaries ) {
20365            if ( x < this.minBoundaryX ) {
20366                if ( this.options.shrink == 'scale' ) {
20367                    this.width = Math.max(this.indicatorWidth + x, 8);
20368                    this.indicatorStyle.width = this.width + 'px';
20369                }
20370                x = this.minBoundaryX;
20371            } else if ( x > this.maxBoundaryX ) {
20372                if ( this.options.shrink == 'scale' ) {
20373                    this.width = Math.max(this.indicatorWidth - (x - this.maxPosX), 8);
20374                    this.indicatorStyle.width = this.width + 'px';
20375                    x = this.maxPosX + this.indicatorWidth - this.width;
20376                } else {
20377                    x = this.maxBoundaryX;
20378                }
20379            } else if ( this.options.shrink == 'scale' && this.width != this.indicatorWidth ) {
20380                this.width = this.indicatorWidth;
20381                this.indicatorStyle.width = this.width + 'px';
20382            }
20383
20384            if ( y < this.minBoundaryY ) {
20385                if ( this.options.shrink == 'scale' ) {
20386                    this.height = Math.max(this.indicatorHeight + y * 3, 8);
20387                    this.indicatorStyle.height = this.height + 'px';
20388                }
20389                y = this.minBoundaryY;
20390            } else if ( y > this.maxBoundaryY ) {
20391                if ( this.options.shrink == 'scale' ) {
20392                    this.height = Math.max(this.indicatorHeight - (y - this.maxPosY) * 3, 8);
20393                    this.indicatorStyle.height = this.height + 'px';
20394                    y = this.maxPosY + this.indicatorHeight - this.height;
20395                } else {
20396                    y = this.maxBoundaryY;
20397                }
20398            } else if ( this.options.shrink == 'scale' && this.height != this.indicatorHeight ) {
20399                this.height = this.indicatorHeight;
20400                this.indicatorStyle.height = this.height + 'px';
20401            }
20402        }
20403
20404        this.x = x;
20405        this.y = y;
20406
20407        if ( this.scroller.options.useTransform ) {
20408            this.indicatorStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.scroller.translateZ;
20409        } else {
20410            this.indicatorStyle.left = x + 'px';
20411            this.indicatorStyle.top = y + 'px';
20412        }
20413    },
20414
20415    _pos: function (x, y) {
20416        if ( x < 0 ) {
20417            x = 0;
20418        } else if ( x > this.maxPosX ) {
20419            x = this.maxPosX;
20420        }
20421
20422        if ( y < 0 ) {
20423            y = 0;
20424        } else if ( y > this.maxPosY ) {
20425            y = this.maxPosY;
20426        }
20427
20428        x = this.options.listenX ? Math.round(x / this.sizeRatioX) : this.scroller.x;
20429        y = this.options.listenY ? Math.round(y / this.sizeRatioY) : this.scroller.y;
20430
20431        this.scroller.scrollTo(x, y);
20432    },
20433
20434    fade: function (val, hold) {
20435        if ( hold && !this.visible ) {
20436            return;
20437        }
20438
20439        clearTimeout(this.fadeTimeout);
20440        this.fadeTimeout = null;
20441
20442        var time = val ? 250 : 500,
20443            delay = val ? 0 : 300;
20444
20445        val = val ? '1' : '0';
20446
20447        this.wrapperStyle[utils.style.transitionDuration] = time + 'ms';
20448
20449        this.fadeTimeout = setTimeout((function (val) {
20450            this.wrapperStyle.opacity = val;
20451            this.visible = +val;
20452        }).bind(this, val), delay);
20453    }
20454};
20455
20456IScroll.utils = utils;
20457
20458if ( typeof module != 'undefined' && module.exports ) {
20459    module.exports = IScroll;
20460} else if ( typeof define == 'function' && define.amd ) {
20461        define( function () { return IScroll; } );
20462} else {
20463    window.IScroll = IScroll;
20464}
20465
20466})(window, document, Math);
20467/*!
20468 * fullPage 2.9.4
20469 * https://github.com/alvarotrigo/fullPage.js
20470 * @license MIT licensed
20471 *
20472 * Copyright (C) 2015 alvarotrigo.com - A project by Alvaro Trigo
20473 */
20474(function(global, factory) {
20475    'use strict';
20476    if (typeof define === 'function' && define.amd) {
20477        define(['jquery'], function($) {
20478          return factory($, global, global.document, global.Math);
20479        });
20480    } else if (typeof exports === "object" && exports) {
20481        module.exports = factory(require('jquery'), global, global.document, global.Math);
20482    } else {
20483        factory(jQuery, global, global.document, global.Math);
20484    }
20485})(typeof window !== 'undefined' ? window : this, function($, window, document, Math, undefined) {
20486    'use strict';
20487
20488    // keeping central set of classnames and selectors
20489    var WRAPPER =               'fullpage-wrapper';
20490    var WRAPPER_SEL =           '.' + WRAPPER;
20491
20492    // slimscroll
20493    var SCROLLABLE =            'fp-scrollable';
20494    var SCROLLABLE_SEL =        '.' + SCROLLABLE;
20495
20496    // util
20497    var RESPONSIVE =            'fp-responsive';
20498    var NO_TRANSITION =         'fp-notransition';
20499    var DESTROYED =             'fp-destroyed';
20500    var ENABLED =               'fp-enabled';
20501    var VIEWING_PREFIX =        'fp-viewing';
20502    var ACTIVE =                'active';
20503    var ACTIVE_SEL =            '.' + ACTIVE;
20504    var COMPLETELY =            'fp-completely';
20505    var COMPLETELY_SEL =        '.' + COMPLETELY;
20506
20507    // section
20508    var SECTION_DEFAULT_SEL =   '.section';
20509    var SECTION =               'fp-section';
20510    var SECTION_SEL =           '.' + SECTION;
20511    var SECTION_ACTIVE_SEL =    SECTION_SEL + ACTIVE_SEL;
20512    var SECTION_FIRST_SEL =     SECTION_SEL + ':first';
20513    var SECTION_LAST_SEL =      SECTION_SEL + ':last';
20514    var TABLE_CELL =            'fp-tableCell';
20515    var TABLE_CELL_SEL =        '.' + TABLE_CELL;
20516    var AUTO_HEIGHT =           'fp-auto-height';
20517    var AUTO_HEIGHT_SEL =       '.fp-auto-height';
20518    var NORMAL_SCROLL =         'fp-normal-scroll';
20519    var NORMAL_SCROLL_SEL =     '.fp-normal-scroll';
20520
20521    // section nav
20522    var SECTION_NAV =           'fp-nav';
20523    var SECTION_NAV_SEL =       '#' + SECTION_NAV;
20524    var SECTION_NAV_TOOLTIP =   'fp-tooltip';
20525    var SECTION_NAV_TOOLTIP_SEL='.'+SECTION_NAV_TOOLTIP;
20526    var SHOW_ACTIVE_TOOLTIP =   'fp-show-active';
20527
20528    // slide
20529    var SLIDE_DEFAULT_SEL =     '.slide';
20530    var SLIDE =                 'fp-slide';
20531    var SLIDE_SEL =             '.' + SLIDE;
20532    var SLIDE_ACTIVE_SEL =      SLIDE_SEL + ACTIVE_SEL;
20533    var SLIDES_WRAPPER =        'fp-slides';
20534    var SLIDES_WRAPPER_SEL =    '.' + SLIDES_WRAPPER;
20535    var SLIDES_CONTAINER =      'fp-slidesContainer';
20536    var SLIDES_CONTAINER_SEL =  '.' + SLIDES_CONTAINER;
20537    var TABLE =                 'fp-table';
20538
20539    // slide nav
20540    var SLIDES_NAV =            'fp-slidesNav';
20541    var SLIDES_NAV_SEL =        '.' + SLIDES_NAV;
20542    var SLIDES_NAV_LINK_SEL =   SLIDES_NAV_SEL + ' a';
20543    var SLIDES_ARROW =          'fp-controlArrow';
20544    var SLIDES_ARROW_SEL =      '.' + SLIDES_ARROW;
20545    var SLIDES_PREV =           'fp-prev';
20546    var SLIDES_PREV_SEL =       '.' + SLIDES_PREV;
20547    var SLIDES_ARROW_PREV =     SLIDES_ARROW + ' ' + SLIDES_PREV;
20548    var SLIDES_ARROW_PREV_SEL = SLIDES_ARROW_SEL + SLIDES_PREV_SEL;
20549    var SLIDES_NEXT =           'fp-next';
20550    var SLIDES_NEXT_SEL =       '.' + SLIDES_NEXT;
20551    var SLIDES_ARROW_NEXT =     SLIDES_ARROW + ' ' + SLIDES_NEXT;
20552    var SLIDES_ARROW_NEXT_SEL = SLIDES_ARROW_SEL + SLIDES_NEXT_SEL;
20553
20554    var $window = $(window);
20555    var $document = $(document);
20556
20557    // Default options for iScroll.js used when using scrollOverflow
20558    var iscrollOptions = {
20559        scrollbars: true,
20560        mouseWheel: true,
20561        hideScrollbars: false,
20562        fadeScrollbars: false,
20563        disableMouse: true,
20564        interactiveScrollbars: true,
20565        bounce: false,
20566    };
20567
20568    $.fn.fullpage = function(options) {
20569        //only once my friend!
20570        if($('html').hasClass(ENABLED)){ displayWarnings(); return; }
20571
20572        // common jQuery objects
20573        var $htmlBody = $('html, body');
20574        var $body = $('body');
20575
20576        var FP = $.fn.fullpage;
20577
20578        // Creating some defaults, extending them with any options that were provided
20579        options = $.extend({
20580            //navigation
20581            menu: false,
20582            anchors:[],
20583            lockAnchors: false,
20584            navigation: false,
20585            navigationPosition: 'right',
20586            navigationTooltips: [],
20587            showActiveTooltip: false,
20588            slidesNavigation: false,
20589            slidesNavPosition: 'bottom',
20590            scrollBar: false,
20591            hybrid: false,
20592
20593            //scrolling
20594            css3: true,
20595            scrollingSpeed: 700,
20596            autoScrolling: true,
20597            fitToSection: true,
20598            fitToSectionDelay: 1000,
20599            easing: 'easeInOutCubic',
20600            easingcss3: 'ease',
20601            loopBottom: false,
20602            loopTop: false,
20603            loopHorizontal: true,
20604            continuousVertical: false,
20605            continuousHorizontal: false,
20606            scrollHorizontally: false,
20607            interlockedSlides: false,
20608            dragAndMove: false,
20609            offsetSections: false,
20610            resetSliders: false,
20611            fadingEffect: false,
20612            normalScrollElements: null,
20613            scrollOverflow: false,
20614            scrollOverflowReset: false,
20615            scrollOverflowHandler: iscrollHandler,
20616            scrollOverflowOptions: null,
20617            touchSensitivity: 5,
20618            normalScrollElementTouchThreshold: 5,
20619            bigSectionsDestination: null,
20620
20621            //Accessibility
20622            keyboardScrolling: true,
20623            animateAnchor: true,
20624            recordHistory: true,
20625
20626            //design
20627            controlArrows: true,
20628            controlArrowColor: '#fff',
20629            verticalCentered: true,
20630            sectionsColor : [],
20631            paddingTop: 0,
20632            paddingBottom: 0,
20633            fixedElements: null,
20634            responsive: 0, //backwards compabitility with responsiveWiddth
20635            responsiveWidth: 0,
20636            responsiveHeight: 0,
20637            responsiveSlides: false,
20638            parallax: false,
20639            parallaxOptions: {
20640                type: 'reveal',
20641                percentage: 62,
20642                property: 'translate'
20643            },
20644
20645            //Custom selectors
20646            sectionSelector: SECTION_DEFAULT_SEL,
20647            slideSelector: SLIDE_DEFAULT_SEL,
20648
20649            //events
20650            afterLoad: null,
20651            onLeave: null,
20652            afterRender: null,
20653            afterResize: null,
20654            afterReBuild: null,
20655            afterSlideLoad: null,
20656            onSlideLeave: null,
20657            afterResponsive: null,
20658
20659            lazyLoading: true
20660        }, options);
20661
20662        //flag to avoid very fast sliding for landscape sliders
20663        var slideMoving = false;
20664
20665        var isTouchDevice = navigator.userAgent.match(/(iPhone|iPod|iPad|Android|playbook|silk|BlackBerry|BB10|Windows Phone|Tizen|Bada|webOS|IEMobile|Opera Mini)/);
20666        var isTouch = (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0) || (navigator.maxTouchPoints));
20667        var container = $(this);
20668        var windowsHeight = $window.height();
20669        var isResizing = false;
20670        var isWindowFocused = true;
20671        var lastScrolledDestiny;
20672        var lastScrolledSlide;
20673        var canScroll = true;
20674        var scrollings = [];
20675        var controlPressed;
20676        var startingSection;
20677        var isScrollAllowed = {};
20678        isScrollAllowed.m = {  'up':true, 'down':true, 'left':true, 'right':true };
20679        isScrollAllowed.k = $.extend(true,{}, isScrollAllowed.m);
20680        var MSPointer = getMSPointer();
20681        var events = {
20682            touchmove: 'ontouchmove' in window ? 'touchmove' :  MSPointer.move,
20683            touchstart: 'ontouchstart' in window ? 'touchstart' :  MSPointer.down
20684        };
20685
20686        //cheks for passive event support
20687        //added by Uncode from new fullPage updates
20688        var g_supportsPassive = false;
20689        try {
20690          var opts = Object.defineProperty({}, 'passive', {
20691            get: function() {
20692              g_supportsPassive = true;
20693            }
20694          });
20695          window.addEventListener("testPassive", null, opts);
20696          window.removeEventListener("testPassive", null, opts);
20697        } catch (e) {}
20698
20699        //timeouts
20700        var resizeId;
20701        var afterSectionLoadsId;
20702        var afterSlideLoadsId;
20703        var scrollId;
20704        var scrollId2;
20705        var keydownId;
20706        var originals = $.extend(true, {}, options); //deep copy
20707
20708        //Uncode addition
20709        var $masthead = $('#masthead');
20710        var hideMenu = !$('body').hasClass('vmenu') && $('body').hasClass('uncode-fp-menu-hide') ? true : false;
20711        var menuHeight = $masthead.hasClass('menu-transparent') || hideMenu ? 0 : UNCODE.menuHeight;
20712        var bodyBorder = UNCODE.bodyBorder;
20713        var adminBarHeight = UNCODE.adminBarHeight;
20714
20715        displayWarnings();
20716
20717        //fixing bug in iScroll with links: https://github.com/cubiq/iscroll/issues/783
20718        iscrollOptions.click = isTouch; // see #2035
20719
20720        //extending iScroll options with the user custom ones
20721        iscrollOptions = $.extend(iscrollOptions, options.scrollOverflowOptions);
20722
20723        //easeInOutCubic animation included in the plugin
20724        $.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;}});
20725
20726        /**
20727        * Sets the autoScroll option.
20728        * It changes the scroll bar visibility and the history of the site as a result.
20729        */
20730        function setAutoScrolling(value, type){
20731            //removing the transformation
20732            if(!value){
20733                silentScroll(0);
20734            }
20735
20736            setVariableState('autoScrolling', value, type);
20737
20738            var element = $(SECTION_ACTIVE_SEL);
20739
20740            if(options.autoScrolling && !options.scrollBar){
20741                $htmlBody.css({
20742                    'overflow' : 'hidden !important',
20743                    'height' : '100%'
20744                });
20745
20746                //Uncode addition
20747                //setRecordHistory(originals.recordHistory, 'internal');
20748
20749                //for IE touch devices
20750                container.css({
20751                    '-ms-touch-action': 'none',
20752                    'touch-action': 'none'
20753                });
20754
20755                if(element.length){
20756                    //moving the container up
20757                    silentScroll(element.position().top);
20758                }
20759
20760            }else{
20761                $htmlBody.css({
20762                    'overflow' : 'visible !important',
20763                    'height' : 'initial'
20764                });
20765
20766                //Uncode addition
20767                //setRecordHistory(false, 'internal');
20768
20769                //for IE touch devices
20770                container.css({
20771                    '-ms-touch-action': '',
20772                    'touch-action': ''
20773                });
20774
20775                //scrolling the page to the section with no animation
20776                if (element.length) {
20777                    $htmlBody.scrollTop(element.position().top);
20778                }
20779            }
20780        }
20781
20782        /**
20783        * Defines wheter to record the history for each hash change in the URL.
20784        */
20785        function setRecordHistory(value, type){
20786            setVariableState('recordHistory', value, type);
20787        }
20788
20789        /**
20790        * Defines the scrolling speed
20791        */
20792        function setScrollingSpeed(value, type){
20793            setVariableState('scrollingSpeed', value, type);
20794        }
20795
20796        /**
20797        * Sets fitToSection
20798        */
20799        function setFitToSection(value, type){
20800            setVariableState('fitToSection', value, type);
20801        }
20802
20803        /**
20804        * Sets lockAnchors
20805        */
20806        function setLockAnchors(value){
20807            options.lockAnchors = value;
20808        }
20809
20810        /**
20811        * Adds or remove the possiblity of scrolling through sections by using the mouse wheel or the trackpad.
20812        */
20813        function setMouseWheelScrolling(value){
20814            if(value){
20815                addMouseWheelHandler();
20816                addMiddleWheelHandler();
20817            }else{
20818                removeMouseWheelHandler();
20819                removeMiddleWheelHandler();
20820            }
20821        }
20822
20823        /**
20824        * Adds or remove the possibility of scrolling through sections by using the mouse wheel/trackpad or touch gestures.
20825        * Optionally a second parameter can be used to specify the direction for which the action will be applied.
20826        *
20827        * @param directions string containing the direction or directions separated by comma.
20828        */
20829        function setAllowScrolling(value, directions){
20830            if(typeof directions !== 'undefined'){
20831                directions = directions.replace(/ /g,'').split(',');
20832
20833                $.each(directions, function (index, direction){
20834                    setIsScrollAllowed(value, direction, 'm');
20835                });
20836            }
20837            else if(value){
20838                setMouseWheelScrolling(true);
20839                addTouchHandler();
20840            }else{
20841                setMouseWheelScrolling(false);
20842                removeTouchHandler();
20843            }
20844        }
20845
20846        /**
20847        * Adds or remove the possibility of scrolling through sections by using the keyboard arrow keys
20848        */
20849        function setKeyboardScrolling(value, directions){
20850            if(typeof directions !== 'undefined'){
20851                directions = directions.replace(/ /g,'').split(',');
20852
20853                $.each(directions, function (index, direction){
20854                    setIsScrollAllowed(value, direction, 'k');
20855                });
20856            }else{
20857                options.keyboardScrolling = value;
20858            }
20859        }
20860
20861        /**
20862        * Moves the page up one section.
20863        */
20864        function moveSectionUp(){
20865            var prev = $(SECTION_ACTIVE_SEL).prev(SECTION_SEL);
20866
20867            //looping to the bottom if there's no more sections above
20868            if (!prev.length && (options.loopTop || options.continuousVertical)) {
20869                prev = $(SECTION_SEL).last();
20870            }
20871
20872            if (prev.length) {
20873                scrollPage(prev, null, true);
20874            }
20875        }
20876
20877        /**
20878        * Moves the page down one section.
20879        */
20880        function moveSectionDown(){
20881            var next = $(SECTION_ACTIVE_SEL).next(SECTION_SEL);
20882
20883            //looping to the top if there's no more sections below
20884            if(!next.length &&
20885                (options.loopBottom || options.continuousVertical)){
20886                next = $(SECTION_SEL).first();
20887            }
20888
20889            if(next.length){
20890                scrollPage(next, null, false);
20891            }
20892        }
20893
20894        /**
20895        * Moves the page to the given section and slide with no animation.
20896        * Anchors or index positions can be used as params.
20897        */
20898        function silentMoveTo(sectionAnchor, slideAnchor){
20899            setScrollingSpeed (0, 'internal');
20900            moveTo(sectionAnchor, slideAnchor);
20901            setScrollingSpeed (originals.scrollingSpeed, 'internal');
20902        }
20903
20904        /**
20905        * Moves the page to the given section and slide.
20906        * Anchors or index positions can be used as params.
20907        */
20908        function moveTo(sectionAnchor, slideAnchor){
20909            var destiny = getSectionByAnchor(sectionAnchor);
20910
20911            if (typeof slideAnchor !== 'undefined'){
20912                scrollPageAndSlide(sectionAnchor, slideAnchor);
20913            }else if(destiny.length > 0){
20914                scrollPage(destiny);
20915            }
20916        }
20917
20918        /**
20919        * Slides right the slider of the active section.
20920        * Optional `section` param.
20921        */
20922        function moveSlideRight(section){
20923            moveSlide('right', section);
20924        }
20925
20926        /**
20927        * Slides left the slider of the active section.
20928        * Optional `section` param.
20929        */
20930        function moveSlideLeft(section){
20931            moveSlide('left', section);
20932        }
20933
20934        /**
20935         * When resizing is finished, we adjust the slides sizes and positions
20936         */
20937        function reBuild(resizing){
20938            if(container.hasClass(DESTROYED)){ return; }  //nothing to do if the plugin was destroyed
20939
20940            isResizing = true;
20941
20942            windowsHeight = $window.height();  //updating global var
20943
20944            $(SECTION_SEL).each(function(){
20945                var slidesWrap = $(this).find(SLIDES_WRAPPER_SEL);
20946                var slides = $(this).find(SLIDE_SEL);
20947
20948                //adjusting the height of the table-cell for IE and Firefox
20949                if(options.verticalCentered){
20950                    $(this).find(TABLE_CELL_SEL).css('height', getTableHeight($(this)) + 'px');
20951                }
20952
20953                $(this).css('height', windowsHeight + 'px');
20954
20955                //resizing the scrolling divs
20956                if(options.scrollOverflow){
20957                    if(slides.length){
20958                        slides.each(function(){
20959                            createScrollBar($(this));
20960                        });
20961                    }else{
20962                        createScrollBar($(this));
20963                    }
20964                }
20965
20966                //adjusting the position fo the FULL WIDTH slides...
20967                if (slides.length > 1) {
20968                    landscapeScroll(slidesWrap, slidesWrap.find(SLIDE_ACTIVE_SEL));
20969                }
20970            });
20971
20972            var activeSection = $(SECTION_ACTIVE_SEL);
20973            var sectionIndex = activeSection.index(SECTION_SEL);
20974
20975            //isn't it the first section?
20976            if(sectionIndex){
20977                //adjusting the position for the current section
20978                silentMoveTo(sectionIndex + 1);
20979            }
20980
20981            isResizing = false;
20982            $.isFunction( options.afterResize ) && resizing && options.afterResize.call(container);
20983            $.isFunction( options.afterReBuild ) && !resizing && options.afterReBuild.call(container);
20984        }
20985
20986        /**
20987        * Turns fullPage.js to normal scrolling mode when the viewport `width` or `height`
20988        * are smaller than the set limit values.
20989        */
20990        function setResponsive(active){
20991            var isResponsive = $body.hasClass(RESPONSIVE);
20992
20993            if(active){
20994                if(!isResponsive){
20995                    setAutoScrolling(false, 'internal');
20996                    setFitToSection(false, 'internal');
20997                    $(SECTION_NAV_SEL).hide();
20998                    $body.addClass(RESPONSIVE);
20999                    $.isFunction( options.afterResponsive ) && options.afterResponsive.call( container, active);
21000                }
21001            }
21002            else if(isResponsive){
21003                setAutoScrolling(originals.autoScrolling, 'internal');
21004                setFitToSection(originals.autoScrolling, 'internal');
21005                $(SECTION_NAV_SEL).show();
21006                $body.removeClass(RESPONSIVE);
21007                $.isFunction( options.afterResponsive ) && options.afterResponsive.call( container, active);
21008            }
21009        }
21010
21011        if($(this).length){
21012            //public functions
21013            FP.setAutoScrolling = setAutoScrolling;
21014            FP.setRecordHistory = setRecordHistory;
21015            FP.setScrollingSpeed = setScrollingSpeed;
21016            FP.setFitToSection = setFitToSection;
21017            FP.setLockAnchors = setLockAnchors;
21018            FP.setMouseWheelScrolling = setMouseWheelScrolling;
21019            FP.setAllowScrolling = setAllowScrolling;
21020            FP.setKeyboardScrolling = setKeyboardScrolling;
21021            FP.moveSectionUp = moveSectionUp;
21022            FP.moveSectionDown = moveSectionDown;
21023            FP.silentMoveTo = silentMoveTo;
21024            FP.moveTo = moveTo;
21025            FP.moveSlideRight = moveSlideRight;
21026            FP.moveSlideLeft = moveSlideLeft;
21027            FP.fitToSection = fitToSection;
21028            FP.reBuild = reBuild;
21029            FP.setResponsive = setResponsive;
21030            FP.destroy = destroy;
21031
21032            init();
21033
21034            bindEvents();
21035        }
21036
21037        function init(){
21038            //if css3 is not supported, it will use jQuery animations
21039            if(options.css3){
21040                options.css3 = support3d();
21041            }
21042
21043            options.scrollBar = options.scrollBar || options.hybrid;
21044
21045            setOptionsFromDOM();
21046            prepareDom();
21047            setAllowScrolling(true);
21048            setAutoScrolling(options.autoScrolling, 'internal');
21049            responsive();
21050
21051            //setting the class for the body element
21052            setBodyClass();
21053
21054            //Uncode addition
21055            // if(document.readyState === 'complete'){
21056            //     scrollToAnchor();
21057            // }
21058            // $window.on('load', scrollToAnchor);
21059        }
21060
21061        function bindEvents(){
21062            $window
21063                //when scrolling...
21064                //.on('scroll', scrollHandler)
21065
21066                //detecting any change on the URL to scroll to the given anchor link
21067                //(a way to detect back history button as we play with the hashes on the URL)
21068                .on('hashchange', hashChangeHandler)
21069
21070                //when opening a new tab (ctrl + t), `control` won't be pressed when coming back.
21071                .blur(blurHandler)
21072
21073                //when resizing the site, we adjust the heights of the sections, slimScroll...
21074                .resize(resizeHandler);
21075
21076            $document
21077                //Sliding with arrow keys, both, vertical and horizontal
21078                .keydown(keydownHandler)
21079
21080                //to prevent scrolling while zooming
21081                .keyup(keyUpHandler)
21082
21083                //Scrolls to the section when clicking the navigation bullet
21084                .on('click touchstart', SECTION_NAV_SEL + ' a', sectionBulletHandler)
21085
21086                //Scrolls the slider to the given slide destination for the given section
21087                .on('click touchstart', SLIDES_NAV_LINK_SEL, slideBulletHandler)
21088
21089                .on('click', SECTION_NAV_TOOLTIP_SEL, tooltipTextHandler);
21090
21091            //Scrolling horizontally when clicking on the slider controls.
21092            $(SECTION_SEL).on('click touchstart', SLIDES_ARROW_SEL, slideArrowHandler);
21093
21094            /**
21095            * Applying normalScroll elements.
21096            * Ignoring the scrolls over the specified selectors.
21097            */
21098            if(options.normalScrollElements){
21099                $document.on('mouseenter', options.normalScrollElements, function () {
21100                    setMouseWheelScrolling(false);
21101                });
21102
21103                $document.on('mouseleave', options.normalScrollElements, function(){
21104                    setMouseWheelScrolling(true);
21105                });
21106            }
21107        }
21108
21109        /**
21110        * Setting options from DOM elements if they are not provided.
21111        */
21112        function setOptionsFromDOM(){
21113            var sections = container.find(options.sectionSelector);
21114
21115            //no anchors option? Checking for them in the DOM attributes
21116            if(!options.anchors.length){
21117                options.anchors = sections.filter('[data-anchor]').map(function(){
21118                    return $(this).data('anchor').toString();
21119                }).get();
21120            }
21121
21122            //no tooltips option? Checking for them in the DOM attributes
21123            if(!options.navigationTooltips.length){
21124                options.navigationTooltips = sections.filter('[data-tooltip]').map(function(){
21125                    return $(this).data('tooltip').toString();
21126                }).get();
21127            }
21128        }
21129
21130        /**
21131        * Works over the DOM structure to set it up for the current fullpage options.
21132        */
21133        function prepareDom(){
21134            container.css({
21135                'height': '100%',
21136                'position': 'relative'
21137            });
21138
21139            //adding a class to recognize the container internally in the code
21140            container.addClass(WRAPPER);
21141            $('html').addClass(ENABLED);
21142
21143            windowsHeight = $window.height();
21144
21145            container.removeClass(DESTROYED); //in case it was destroyed before initializing it again
21146
21147            addInternalSelectors();
21148
21149             //styling the sections / slides / menu
21150            $(SECTION_SEL).each(function(index){
21151                var section = $(this);
21152                var slides = section.find(SLIDE_SEL);
21153                var numSlides = slides.length;
21154
21155                styleSection(section, index);
21156                styleMenu(section, index);
21157
21158                // if there's any slide
21159                if (numSlides > 0) {
21160                    styleSlides(section, slides, numSlides);
21161                }else{
21162                    if(options.verticalCentered){
21163                        addTableClass(section);
21164                    }
21165                }
21166            });
21167
21168            //fixed elements need to be moved out of the plugin container due to problems with CSS3.
21169            if(options.fixedElements && options.css3){
21170                $(options.fixedElements).appendTo($body);
21171            }
21172
21173            //vertical centered of the navigation + active bullet
21174            if(options.navigation){
21175                addVerticalNavigation();
21176            }
21177
21178            enableYoutubeAPI();
21179
21180            if(options.scrollOverflow){
21181                if(document.readyState === 'complete'){
21182                    createScrollBarHandler();
21183                }
21184                //after DOM and images are loaded
21185                $window.on('load', createScrollBarHandler);
21186            }else{
21187                afterRenderActions();
21188            }
21189        }
21190
21191        /**
21192        * Styles the horizontal slides for a section.
21193        */
21194        function styleSlides(section, slides, numSlides){
21195            var sliderWidth = numSlides * 100;
21196            var slideWidth = 100 / numSlides;
21197
21198            slides.wrapAll('<div class="' + SLIDES_CONTAINER + '" />');
21199            slides.parent().wrap('<div class="' + SLIDES_WRAPPER + '" />');
21200
21201            section.find(SLIDES_CONTAINER_SEL).css('width', sliderWidth + '%');
21202
21203            if(numSlides > 1){
21204                if(options.controlArrows){
21205                    createSlideArrows(section);
21206                }
21207
21208                if(options.slidesNavigation){
21209                    addSlidesNavigation(section, numSlides);
21210                }
21211            }
21212
21213            slides.each(function(index) {
21214                $(this).css('width', slideWidth + '%');
21215
21216                if(options.verticalCentered){
21217                    addTableClass($(this));
21218                }
21219            });
21220
21221            var startingSlide = section.find(SLIDE_ACTIVE_SEL);
21222
21223            //if the slide won't be an starting point, the default will be the first one
21224            //the active section isn't the first one? Is not the first slide of the first section? Then we load that section/sl
21224ide by default.
21225            if( startingSlide.length &&  ($(SECTION_ACTIVE_SEL).index(SECTION_SEL) !== 0 || ($(SECTION_ACTIVE_SEL).index(SECTION_SEL) === 0 && startingSlide.index() !== 0))){
21226                silentLandscapeScroll(startingSlide, 'internal');
21227            }else{
21228                slides.eq(0).addClass(ACTIVE);
21229            }
21230        }
21231
21232        /**
21233        * Styling vertical sections
21234        */
21235        function styleSection(section, index){
21236            //if no active section is defined, the 1st one will be the default one
21237            if(!index && $(SECTION_ACTIVE_SEL).length === 0) {
21238                section.addClass(ACTIVE);
21239            }
21240            startingSection = $(SECTION_ACTIVE_SEL);
21241
21242            section.css('height', windowsHeight + 'px');
21243
21244            if(options.paddingTop){
21245                section.css('padding-top', options.paddingTop);
21246            }
21247
21248            if(options.paddingBottom){
21249                section.css('padding-bottom', options.paddingBottom);
21250            }
21251
21252            if (typeof options.sectionsColor[index] !==  'undefined') {
21253                section.css('background-color', options.sectionsColor[index]);
21254            }
21255
21256            if (typeof options.anchors[index] !== 'undefined') {
21257                section.attr('data-anchor', options.anchors[index]);
21258            }
21259        }
21260
21261        /**
21262        * Sets the data-anchor attributes to the menu elements and activates the current one.
21263        */
21264        function styleMenu(section, index){
21265            if (typeof options.anchors[index] !== 'undefined') {
21266                //activating the menu / nav element on load
21267                if(section.hasClass(ACTIVE)){
21268                    activateMenuAndNav(options.anchors[index], index);
21269                }
21270            }
21271
21272            //moving the menu outside the main container if it is inside (avoid problems with fixed positions when using CSS3 tranforms)
21273            if(options.menu && options.css3 && $(options.menu).closest(WRAPPER_SEL).length){
21274                $(options.menu).appendTo($body);
21275            }
21276        }
21277
21278        /**
21279        * Adds internal classes to be able to provide customizable selectors
21280        * keeping the link with the style sheet.
21281        */
21282        function addInternalSelectors(){
21283            container.find(options.sectionSelector).addClass(SECTION);
21284            container.find(options.slideSelector).addClass(SLIDE);
21285        }
21286
21287        /**
21288        * Creates the control arrows for the given section
21289        */
21290        function createSlideArrows(section){
21291            section.find(SLIDES_WRAPPER_SEL).after('<div class="' + SLIDES_ARROW_PREV + '"></div><div class="' + SLIDES_ARROW_NEXT + '"></div>');
21292
21293            if(options.controlArrowColor!='#fff'){
21294                section.find(SLIDES_ARROW_NEXT_SEL).css('border-color', 'transparent transparent transparent '+options.controlArrowColor);
21295                section.find(SLIDES_ARROW_PREV_SEL).css('border-color', 'transparent '+ options.controlArrowColor + ' transparent transparent');
21296            }
21297
21298            if(!options.loopHorizontal){
21299                section.find(SLIDES_ARROW_PREV_SEL).hide();
21300            }
21301        }
21302
21303        /**
21304        * Creates a vertical navigation bar.
21305        */
21306        function addVerticalNavigation(){
21307            $body.append('<div id="' + SECTION_NAV + '"><ul></ul></div>');
21308            var nav = $(SECTION_NAV_SEL);
21309
21310            nav.addClass(function() {
21311                return options.showActiveTooltip ? SHOW_ACTIVE_TOOLTIP + ' ' + options.navigationPosition : options.navigationPosition;
21312            });
21313
21314            for (var i = 0; i < $(SECTION_SEL).length; i++) {
21315                var link = '';
21316                if (options.anchors.length) {
21317                    link = options.anchors[i];
21318                }
21319
21320                var li = '<li><a href="#' + link + '"><span></span></a>';
21321
21322                // Only add tooltip if needed (defined by user)
21323                var tooltip = options.navigationTooltips[i];
21324
21325                if (typeof tooltip !== 'undefined' && tooltip !== '') {
21326                    li += '<div class="' + SECTION_NAV_TOOLTIP + ' ' + options.navigationPosition + '">' + tooltip + '</div>';
21327                }
21328
21329                li += '</li>';
21330
21331                nav.find('ul').append(li);
21332            }
21333
21334            //centering it vertically
21335            $(SECTION_NAV_SEL).css('margin-top', '-' + ($(SECTION_NAV_SEL).height()/2) + 'px');
21336
21337            //activating the current active section
21338            $(SECTION_NAV_SEL).find('li').eq($(SECTION_ACTIVE_SEL).index(SECTION_SEL)).find('a').addClass(ACTIVE);
21339        }
21340
21341        /**
21342        * Creates the slim scroll scrollbar for the sections and slides inside them.
21343        */
21344        function createScrollBarHandler(){
21345            $(SECTION_SEL).each(function(){
21346                var slides = $(this).find(SLIDE_SEL);
21347
21348                if(slides.length){
21349                    slides.each(function(){
21350                        createScrollBar($(this));
21351                    });
21352                }else{
21353                    createScrollBar($(this));
21354                }
21355
21356            });
21357            afterRenderActions();
21358        }
21359
21360        /*
21361        * Enables the Youtube videos API so we can control their flow if necessary.
21362        */
21363        function enableYoutubeAPI(){
21364            container.find('iframe[src*="youtube.com/embed/"]').each(function(){
21365                addURLParam($(this), 'enablejsapi=1');
21366            });
21367        }
21368
21369        /**
21370        * Adds a new parameter and its value to the `src` of a given element
21371        */
21372        function addURLParam(element, newParam){
21373            var originalSrc = element.attr('src');
21374            element.attr('src', originalSrc + getUrlParamSign(originalSrc) + newParam);
21375        }
21376
21377        /*
21378        * Returns the prefix sign to use for a new parameter in an existen URL.
21379        *
21380        * @return {String}  ? | &
21381        */
21382        function getUrlParamSign(url){
21383            return ( !/\?/.test( url ) ) ? '?' : '&';
21384        }
21385
21386        /**
21387        * Actions and callbacks to fire afterRender
21388        */
21389        function afterRenderActions(){
21390            var section = $(SECTION_ACTIVE_SEL);
21391
21392            section.addClass(COMPLETELY);
21393
21394            if(options.scrollOverflowHandler.afterRender){
21395                options.scrollOverflowHandler.afterRender(section);
21396            }
21397            lazyLoad(section);
21398            playMedia(section);
21399            options.scrollOverflowHandler.afterLoad();
21400
21401            if(isDestinyTheStartingSection()){
21402                $.isFunction( options.afterLoad ) && options.afterLoad.call(section, section.data('anchor'), (section.index(SECTION_SEL) + 1));
21403            }
21404
21405            $.isFunction( options.afterRender ) && options.afterRender.call(container);
21406        }
21407
21408        /**
21409        * Determines if the URL anchor destiny is the starting section (the one using 'active' class before initialization)
21410        */
21411        function isDestinyTheStartingSection(){
21412            var anchors =  window.location.hash.replace('#', '').split('/');
21413            var destinationSection = getSectionByAnchor(decodeURIComponent(anchors[0]));
21414
21415            return !destinationSection.length || destinationSection.length && destinationSection.index() === startingSection.index();
21416        }
21417
21418
21419        var isScrolling = false;
21420        var lastScroll = 0;
21421
21422        //when scrolling...
21423        function scrollHandler(){
21424            var currentSection;
21425
21426            if(!options.autoScrolling || options.scrollBar){
21427                var currentScroll = $window.scrollTop();
21428                var scrollDirection = getScrollDirection(currentScroll);
21429                var visibleSectionIndex = 0;
21430                var screen_mid = currentScroll + ($window.height() / 2.0);
21431                var isAtBottom = $body.height() - $window.height() === currentScroll;
21432                var sections =  document.querySelectorAll(SECTION_SEL);
21433
21434                //when using `auto-height` for a small last section it won't be centered in the viewport
21435                if(isAtBottom){
21436                    visibleSectionIndex = sections.length - 1;
21437                }
21438                //is at top? when using `auto-height` for a small first section it won't be centered in the viewport
21439                else if(!currentScroll){
21440                    visibleSectionIndex = 0;
21441                }
21442
21443                //taking the section which is showing more content in the viewport
21444                else{
21445                    for (var i = 0; i < sections.length; ++i) {
21446                        var section = sections[i];
21447
21448                        // Pick the the last section which passes the middle line of the screen.
21449                        if (section.offsetTop <= screen_mid)
21450                        {
21451                            visibleSectionIndex = i;
21452                        }
21453                    }
21454                }
21455
21456                if(isCompletelyInViewPort(scrollDirection)){
21457                    if(!$(SECTION_ACTIVE_SEL).hasClass(COMPLETELY)){
21458                        $(SECTION_ACTIVE_SEL).addClass(COMPLETELY).siblings().removeClass(COMPLETELY);
21459                    }
21460                }
21461
21462                //geting the last one, the current one on the screen
21463                currentSection = $(sections).eq(visibleSectionIndex);
21464
21465                //setting the visible section as active when manually scrolling
21466                //executing only once the first time we reach the section
21467                if(!currentSection.hasClass(ACTIVE)){
21468                    isScrolling = true;
21469                    var leavingSection = $(SECTION_ACTIVE_SEL);
21470                    var leavingSectionIndex = leavingSection.index(SECTION_SEL) + 1;
21471                    var yMovement = getYmovement(currentSection);
21472                    var anchorLink  = currentSection.data('anchor');
21473                    var sectionIndex = currentSection.index(SECTION_SEL) + 1;
21474                    var activeSlide = currentSection.find(SLIDE_ACTIVE_SEL);
21475                    var slideIndex;
21476                    var slideAnchorLink;
21477
21478                    if(activeSlide.length){
21479                        slideAnchorLink = activeSlide.data('anchor');
21480                        slideIndex = activeSlide.index();
21481                    }
21482
21483                    if(canScroll){
21484                        currentSection.addClass(ACTIVE).siblings().removeClass(ACTIVE);
21485
21486                        $.isFunction( options.onLeave ) && options.onLeave.call( leavingSection, leavingSectionIndex, sectionIndex, yMovement);
21487                        $.isFunction( options.afterLoad ) && options.afterLoad.call( currentSection, anchorLink, sectionIndex);
21488
21489                        stopMedia(leavingSection);
21490                        lazyLoad(currentSection);
21491                        playMedia(currentSection);
21492
21493                        activateMenuAndNav(anchorLink, sectionIndex - 1);
21494
21495                        if(options.anchors.length){
21496                            //needed to enter in hashChange event when using the menu with anchor links
21497                            lastScrolledDestiny = anchorLink;
21498                        }
21499                        setState(slideIndex, slideAnchorLink, anchorLink, sectionIndex);
21500                    }
21501
21502                    //small timeout in order to avoid entering in hashChange event when scrolling is not finished yet
21503                    clearTimeout(scrollId);
21504                    scrollId = setTimeout(function(){
21505                        isScrolling = false;
21506                    }, 100);
21507                }
21508
21509                if(options.fitToSection){
21510                    //for the auto adjust of the viewport to fit a whole section
21511                    clearTimeout(scrollId2);
21512
21513                    scrollId2 = setTimeout(function(){
21514                        //checking it again in case it changed during the delay
21515                        if(options.fitToSection){
21516                            fitToSection();
21517                        }
21518                    }, options.fitToSectionDelay);
21519                }
21520            }
21521        }
21522
21523        /**
21524        * Fits the site to the nearest active section
21525        */
21526        function fitToSection(){
21527            //checking fitToSection again in case it was set to false before the timeout delay
21528            if(canScroll){
21529                //allows to scroll to an active section and
21530                //if the section is already active, we prevent firing callbacks
21531                isResizing = true;
21532
21533                scrollPage($(SECTION_ACTIVE_SEL));
21534                isResizing = false;
21535            }
21536        }
21537
21538        /**
21539        * Determines whether the active section has seen in its whole or not.
21540        */
21541        function isCompletelyInViewPort(movement){
21542            var top = $(SECTION_ACTIVE_SEL).position().top;
21543            var bottom = top + $window.height();
21544
21545            if(movement == 'up'){
21546                return bottom >= ($window.scrollTop() + $window.height());
21547            }
21548            return top <= $window.scrollTop();
21549        }
21550
21551        /**
21552        * Gets the directon of the the scrolling fired by the scroll event.
21553        */
21554        function getScrollDirection(currentScroll){
21555            var direction = currentScroll > lastScroll ? 'down' : 'up';
21556
21557            lastScroll = currentScroll;
21558
21559            //needed for auto-height sections to determine if we want to scroll to the top or bottom of the destination
21560            previousDestTop = currentScroll;
21561
21562            return direction;
21563        }
21564
21565        /**
21566        * Determines the way of scrolling up or down:
21567        * by 'automatically' scrolling a section or by using the default and normal scrolling.
21568        */
21569        function scrolling(type, scrollable){
21570            if (!isScrollAllowed.m[type]){
21571                return;
21572            }
21573            var check = (type === 'down') ? 'bottom' : 'top';
21574            var scrollSection = (type === 'down') ? moveSectionDown : moveSectionUp;
21575
21576            if(scrollable.length > 0 ){
21577                //is the scrollbar at the start/end of the scroll?
21578                if(options.scrollOverflowHandler.isScrolled(check, scrollable)){
21579                    scrollSection();
21580                }else{
21581                    return true;
21582                }
21583            }else{
21584                // moved up/down
21585                scrollSection();
21586            }
21587        }
21588
21589        /*
21590        * Preventing bouncing in iOS #2285
21591        */
21592        function preventBouncing(event){
21593            var e = event.originalEvent;
21594            if(!checkParentForNormalScrollElement(event.target) && options.autoScrolling && isReallyTouch(e) && isScrollAllowed.m.up){
21595                //preventing the easing on iOS devices
21596                event.preventDefault();
21597            }
21598        }
21599
21600        var touchStartY = 0;
21601        var touchStartX = 0;
21602        var touchEndY = 0;
21603        var touchEndX = 0;
21604
21605        /* Detecting touch events
21606
21607        * As we are changing the top property of the page on scrolling, we can not use the traditional way to detect it.
21608        * This way, the touchstart and the touch moves shows an small difference between them which is the
21609        * used one to determine the direction.
21610        */
21611        function touchMoveHandler(event){
21612            var e = event.originalEvent;
21613            var activeSection = $(e.target).closest(SECTION_SEL);
21614
21615            // additional: if one of the normalScrollElements isn't within options.normalScrollElementTouchThreshold hops up the DOM chain
21616            if (!checkParentForNormalScrollElement(event.target) && isReallyTouch(e) ) {
21617
21618                if(options.autoScrolling){
21619                    //preventing the easing on iOS devices
21620                    event.preventDefault();
21621                }
21622
21623                var scrollable = options.scrollOverflowHandler.scrollable(activeSection);
21624                var touchEvents = getEventsPage(e);
21625
21626                touchEndY = touchEvents.y;
21627                touchEndX = touchEvents.x;
21628
21629                //if movement in the X axys is greater than in the Y and the currect section has slides...
21630                if (activeSection.find(SLIDES_WRAPPER_SEL).length && Math.abs(touchStartX - touchEndX) > (Math.abs(touchStartY - touchEndY))) {
21631
21632                    //is the movement greater than the minimum resistance to scroll?
21633                    if (!slideMoving && Math.abs(touchStartX - touchEndX) > ($window.outerWidth() / 100 * options.touchSensitivity)) {
21634                        if (touchStartX > touchEndX) {
21635                            if(isScrollAllowed.m.right){
21636                                moveSlideRight(activeSection); //next
21637                            }
21638                        } else {
21639                            if(isScrollAllowed.m.left){
21640                                moveSlideLeft(activeSection); //prev
21641                            }
21642                        }
21643                    }
21644                }
21645
21646                //vertical scrolling (only when autoScrolling is enabled)
21647                else if(options.autoScrolling && canScroll){
21648
21649                    //is the movement greater than the minimum resistance to scroll?
21650                    if (Math.abs(touchStartY - touchEndY) > ($window.height() / 100 * options.touchSensitivity)) {
21651                        if (touchStartY > touchEndY) {
21652                            scrolling('down', scrollable);
21653                        } else if (touchEndY > touchStartY) {
21654                            scrolling('up', scrollable);
21655                        }
21656                    }
21657                }
21658            }
21659        }
21660
21661        /**
21662         * recursive function to loop up the parent nodes to check if one of them exists in options.normalScrollElements
21663         * Currently works well for iOS - Android might need some testing
21664         * @param  {Element} el  target element / jquery selector (in subsequent nodes)
21665         * @param  {int}     hop current hop compared to options.normalScrollElementTouchThreshold
21666         * @return {boolean} true if there is a match to options.normalScrollElements
21667         */
21668        function checkParentForNormalScrollElement (el, hop) {
21669            hop = hop || 0;
21670            var parent = $(el).parent();
21671
21672            if (hop < options.normalScrollElementTouchThreshold &&
21673                parent.is(options.normalScrollElements) ) {
21674                return true;
21675            } else if (hop == options.normalScrollElementTouchThreshold) {
21676                return false;
21677            } else {
21678                return checkParentForNormalScrollElement(parent, ++hop);
21679            }
21680        }
21681
21682        /**
21683        * As IE >= 10 fires both touch and mouse events when using a mouse in a touchscreen
21684        * this way we make sure that is really a touch event what IE is detecting.
21685        */
21686        function isReallyTouch(e){
21687            //if is not IE   ||  IE is detecting `touch` or `pen`
21688            return typeof e.pointerType === 'undefined' || e.pointerType != 'mouse';
21689        }
21690
21691        /**
21692        * Handler for the touch start event.
21693        */
21694        function touchStartHandler(event){
21695            var e = event.originalEvent;
21696
21697            //stopping the auto scroll to adjust to a section
21698            if(options.fitToSection){
21699                $htmlBody.stop();
21700            }
21701
21702            if(isReallyTouch(e)){
21703                var touchEvents = getEventsPage(e);
21704                touchStartY = touchEvents.y;
21705                touchStartX = touchEvents.x;
21706            }
21707        }
21708
21709        /**
21710        * Gets the average of the last `number` elements of the given array.
21711        */
21712        function getAverage(elements, number){
21713            var sum = 0;
21714
21715            //taking `number` elements from the end to make the average, if there are not enought, 1
21716            var lastElements = elements.slice(Math.max(elements.length - number, 1));
21717
21718            for(var i = 0; i < lastElements.length; i++){
21719                sum = sum + lastElements[i];
21720            }
21721
21722            return Math.ceil(sum/number);
21723        }
21724
21725        /**
21726         * Detecting mousewheel scrolling
21727         *
21728         * http://blogs.sitepointstatic.com/examples/tech/mouse-wheel/index.html
21729         * http://www.sitepoint.com/html5-javascript-mouse-wheel/
21730         */
21731        var prevTime = new Date().getTime();
21732
21733        function MouseWheelHandler(e) {
21734            var curTime = new Date().getTime();
21735            var isNormalScroll = $(COMPLETELY_SEL).hasClass(NORMAL_SCROLL);
21736
21737            //autoscrolling and not zooming?
21738            if(options.autoScrolling && !controlPressed && !isNormalScroll){
21739                // cross-browser wheel delta
21740                e = e || window.event;
21741                var value = e.wheelDelta || -e.deltaY || -e.detail;
21742                var delta = Math.max(-1, Math.min(1, value));
21743
21744                var horizontalDetection = typeof e.wheelDeltaX !== 'undefined' || typeof e.deltaX !== 'undefined';
21745                var isScrollingVertically = (Math.abs(e.wheelDeltaX) < Math.abs(e.wheelDelta)) || (Math.abs(e.deltaX ) < Math.abs(e.deltaY) || !horizontalDetection);
21746
21747                //Limiting the array to 150 (lets not waste memory!)
21748                if(scrollings.length > 149){
21749                    scrollings.shift();
21750                }
21751
21752                //keeping record of the previous scrollings
21753                scrollings.push(Math.abs(value));
21754
21755                //preventing to scroll the site on mouse wheel when scrollbar is present
21756                if(options.scrollBar){
21757                    e.preventDefault ? e.preventDefault() : e.returnValue = false;
21758                }
21759
21760                var activeSection = $(SECTION_ACTIVE_SEL);
21761                var scrollable = options.scrollOverflowHandler.scrollable(activeSection);
21762
21763                //time difference between the last scroll and the current one
21764                var timeDiff = curTime-prevTime;
21765                prevTime = curTime;
21766
21767                //haven't they scrolled in a while?
21768                //(enough to be consider a different scrolling action to scroll another section)
21769                if(timeDiff > 200){
21770                    //emptying the array, we dont care about old scrollings for our averages
21771                    scrollings = [];
21772                }
21773
21774                if(canScroll){
21775                    var averageEnd = getAverage(scrollings, 10);
21776                    var averageMiddle = getAverage(scrollings, 70);
21777                    var isAccelerating = averageEnd >= averageMiddle;
21778
21779                    //to avoid double swipes...
21780                    if(isAccelerating && isScrollingVertically){
21781                        //scrolling down?
21782                        if (delta < 0) {
21783                            scrolling('down', scrollable);
21784
21785                        //scrolling up?
21786                        }else {
21787                            scrolling('up', scrollable);
21788                        }
21789                    }
21790                }
21791
21792                return false;
21793            }
21794
21795            if(options.fitToSection){
21796                //stopping the auto scroll to adjust to a section
21797                $htmlBody.stop();
21798            }
21799        }
21800
21801        /**
21802        * Slides a slider to the given direction.
21803        * Optional `section` param.
21804        */
21805        function moveSlide(direction, section){
21806            var activeSection = typeof section === 'undefined' ? $(SECTION_ACTIVE_SEL) : section;
21807            var slides = activeSection.find(SLIDES_WRAPPER_SEL);
21808            var numSlides = slides.find(SLIDE_SEL).length;
21809
21810            // more than one slide needed and nothing should be sliding
21811            if (!slides.length || slideMoving || numSlides < 2) {
21812                return;
21813            }
21814
21815            var currentSlide = slides.find(SLIDE_ACTIVE_SEL);
21816            var destiny = null;
21817
21818            if(direction === 'left'){
21819                destiny = currentSlide.prev(SLIDE_SEL);
21820            }else{
21821                destiny = currentSlide.next(SLIDE_SEL);
21822            }
21823
21824            //isn't there a next slide in the secuence?
21825            if(!destiny.length){
21826                //respect loopHorizontal settin
21827                if (!options.loopHorizontal) return;
21828
21829                if(direction === 'left'){
21830                    destiny = currentSlide.siblings(':last');
21831                }else{
21832                    destiny = currentSlide.siblings(':first');
21833                }
21834            }
21835
21836            slideMoving = true;
21837
21838            landscapeScroll(slides, destiny, direction);
21839        }
21840
21841        /**
21842        * Maintains the active slides in the viewport
21843        * (Because the `scroll` animation might get lost with some actions, such as when using continuousVertical)
21844        */
21845        function keepSlidesPosition(){
21846            $(SLIDE_ACTIVE_SEL).each(function(){
21847                silentLandscapeScroll($(this), 'internal');
21848            });
21849        }
21850
21851        var previousDestTop = 0;
21852        /**
21853        * Returns the destination Y position based on the scrolling direction and
21854        * the height of the section.
21855        */
21856        function getDestinationPosition(element){
21857            var elemPosition = element.position();
21858
21859            //top of the desination will be at the top of the viewport
21860            var position = elemPosition.top;
21861            var isScrollingDown =  elemPosition.top > previousDestTop;
21862            var sectionBottom = position - windowsHeight + element.outerHeight();
21863            var bigSectionsDestination = options.bigSectionsDestination;
21864
21865            //Uncode addition
21866            var containerH = container.outerHeight();
21867            var containerPosition = container.offset();
21868
21869            //########### Commented by Uncode - START ###########
21870            //is the destination element bigger than the viewport?
21871            // if(element.outerHeight() > windowsHeight){
21872            //     //scrolling up?
21873            //     if(!isScrollingDown && !bigSectionsDestination || bigSectionsDestination === 'bottom' ){
21874            //         position = sectionBottom;
21875            //     }
21876            // }
21877
21878            // //sections equal or smaller than the viewport height && scrolling down? ||  is resizing and its in the last section
21879            // else if(isScrollingDown || (isResizing && element.is(':last-child')) ){
21880            //     //The bottom of the destination will be at the bottom of the viewport
21881            //     position = sectionBottom;
21882            // }
21883            //########### Commented by Uncode - END ###########
21884
21885            //Uncode addition
21886            if ( !$masthead.hasClass('menu-transparent') && $('body').hasClass('uncode-fp-menu-shrink') && !element.is(':first-child') )
21887                position += 18;
21888            if ( ( containerH + menuHeight + bodyBorder + adminBarHeight - windowsHeight ) < position || ( isResizing && element.is(':last-child') ) ) {
21889                position = sectionBottom + menuHeight + bodyBorder*2 + adminBarHeight;
21890            }
21891
21892            /*
21893            Keeping record of the last scrolled position to determine the scrolling direction.
21894            No conventional methods can be used as the scroll bar might not be present
21895            AND the section might not be active if it is auto-height and didnt reach the middle
21896            of the viewport.
21897            */
21898            previousDestTop = position;
21899            return position;
21900        }
21901
21902        /**
21903        * Scrolls the site to the given element and scrolls to the slide if a callback is given.
21904        */
21905        function scrollPage(element, callback, isMovementUp){
21906            if(typeof element === 'undefined'){ return; } //there's no element to scroll, leaving the function
21907
21908            var dtop = getDestinationPosition(element);
21909            var slideAnchorLink;
21910            var slideIndex;
21911
21912            //local variables
21913            var v = {
21914                element: element,
21915                callback: callback,
21916                isMovementUp: isMovementUp,
21917                dtop: dtop,
21918                yMovement: getYmovement(element),
21919                anchorLink: element.data('anchor'),
21920                sectionIndex: element.index(SECTION_SEL),
21921                activeSlide: element.find(SLIDE_ACTIVE_SEL),
21922                activeSection: $(SECTION_ACTIVE_SEL),
21923                leavingSection: $(SECTION_ACTIVE_SEL).index(SECTION_SEL) + 1,
21924
21925                //caching the value of isResizing at the momment the function is called
21926                //because it will be checked later inside a setTimeout and the value might change
21927                localIsResizing: isResizing
21928            };
21929
21930            //quiting when destination scroll is the same as the current one
21931            if((v.activeSection.is(element) && !isResizing) || (options.scrollBar && $window.scrollTop() === v.dtop && !element.hasClass(AUTO_HEIGHT) )){ return; }
21932
21933            if(v.activeSlide.length){
21934                slideAnchorLink = v.activeSlide.data('anchor');
21935                slideIndex = v.activeSlide.index();
21936            }
21937
21938            // If continuousVertical && we need to wrap around
21939            if (options.autoScrolling && options.continuousVertical && typeof (v.isMovementUp) !== "undefined" &&
21940                ((!v.isMovementUp && v.yMovement == 'up') || // Intending to scroll down but about to go up or
21941                (v.isMovementUp && v.yMovement == 'down'))) { // intending to scroll up but about to go down
21942
21943                v = createInfiniteSections(v);
21944            }
21945
21946            //callback (onLeave) if the site is not just resizing and readjusting the slides
21947            if($.isFunction(options.onLeave) && !v.localIsResizing){
21948                if(options.onLeave.call(v.activeSection, v.leavingSection, (v.sectionIndex + 1), v.yMovement) === false){
21949                    return;
21950                }
21951            }
21952
21953            //pausing media of the leaving section (if we are not just resizing, as destinatino will be the same one)
21954            if(!v.localIsResizing){
21955                stopMedia(v.activeSection);
21956            }
21957
21958            options.scrollOverflowHandler.beforeLeave();
21959            element.addClass(ACTIVE).siblings().removeClass(ACTIVE);
21960            lazyLoad(element);
21961            options.scrollOverflowHandler.onLeave();
21962
21963
21964            //preventing from activating the MouseWheelHandler event
21965            //more than once if the page is scrolling
21966            canScroll = false;
21967
21968            setState(slideIndex, slideAnchorLink, v.anchorLink, v.sectionIndex);
21969
21970            performMovement(v);
21971
21972            //flag to avoid callingn `scrollPage()` twice in case of using anchor links
21973            lastScrolledDestiny = v.anchorLink;
21974
21975            //avoid firing it twice (as it does also on scroll)
21976            activateMenuAndNav(v.anchorLink, v.sectionIndex);
21977        }
21978
21979        /**
21980        * Performs the vertical movement (by CSS3 or by jQuery)
21981        */
21982        function performMovement(v){
21983            // using CSS3 translate functionality
21984            if (options.css3 && options.autoScrolling && !options.scrollBar) {
21985
21986                // The first section can have a negative value in iOS 10. Not quite sure why: -0.0142822265625
21987                // that's why we round it to 0.
21988                var translate3d = 'translate3d(0px, -' + Math.round(v.dtop) + 'px, 0px)';
21989                transformContainer(translate3d, true);
21990
21991                //even when the scrollingSpeed is 0 there's a little delay, which might cause the
21992                //scrollingSpeed to change in case of using silentMoveTo();
21993                if(options.scrollingSpeed){
21994                    clearTimeout(afterSectionLoadsId);
21995                    afterSectionLoadsId = setTimeout(function () {
21996                        afterSectionLoads(v);
21997                    }, options.scrollingSpeed);
21998                }else{
21999                    afterSectionLoads(v);
22000                }
22001            }
22002
22003            // using jQuery animate
22004            else{
22005                var scrollSettings = getScrollSettings(v);
22006
22007                $(scrollSettings.element).animate(
22008                    scrollSettings.options,
22009                options.scrollingSpeed, options.easing).promise().done(function () { //only one single callback in case of animating  `html, body`
22010                    if(options.scrollBar){
22011
22012                        /* Hack!
22013                        The timeout prevents setting the most dominant section in the viewport as "active" when the user
22014                        scrolled to a smaller section by using the mousewheel (auto scrolling) rather than draging the scroll bar.
22015
22016                        When using scrollBar:true It seems like the scroll events still getting propagated even after the scrolling animation has finished.
22017                        */
22018                        setTimeout(function(){
22019                            afterSectionLoads(v);
22020                        },30);
22021                    }else{
22022                        afterSectionLoads(v);
22023                    }
22024                });
22025            }
22026        }
22027
22028        /**
22029        * Gets the scrolling settings depending on the plugin autoScrolling option
22030        */
22031        function getScrollSettings(v){
22032            var scroll = {};
22033
22034            if(options.autoScrolling && !options.scrollBar){
22035                scroll.options = { 'top': -v.dtop};
22036                scroll.element = WRAPPER_SEL;
22037            }else{
22038                scroll.options = { 'scrollTop': v.dtop};
22039                scroll.element = 'html, body';
22040            }
22041
22042            return scroll;
22043        }
22044
22045        /**
22046        * Adds sections before or after the current one to create the infinite effect.
22047        */
22048        function createInfiniteSections(v){
22049            // Scrolling down
22050            if (!v.isMovementUp) {
22051                // Move all previous sections to after the active section
22052                $(SECTION_ACTIVE_SEL).after(v.activeSection.prevAll(SECTION_SEL).get().reverse());
22053            }
22054            else { // Scrolling up
22055                // Move all next sections to before the active section
22056                $(SECTION_ACTIVE_SEL).before(v.activeSection.nextAll(SECTION_SEL));
22057            }
22058
22059            // Maintain the displayed position (now that we changed the element order)
22060            silentScroll($(SECTION_ACTIVE_SEL).position().top);
22061
22062            // Maintain the active slides visible in the viewport
22063            keepSlidesPosition();
22064
22065            // save for later the elements that still need to be reordered
22066            v.wrapAroundElements = v.activeSection;
22067
22068            // Recalculate animation variables
22069            v.dtop = v.element.position().top;
22070            v.yMovement = getYmovement(v.element);
22071
22072            return v;
22073        }
22074
22075        /**
22076        * Fix section order after continuousVertical changes have been animated
22077        */
22078        function continuousVerticalFixSectionOrder (v) {
22079            // If continuousVertical is in effect (and autoScrolling would also be in effect then),
22080            // finish moving the elements around so the direct navigation will function more simply
22081            if (!v.wrapAroundElements || !v.wrapAroundElements.length) {
22082                return;
22083            }
22084
22085            if (v.isMovementUp) {
22086                $(SECTION_FIRST_SEL).before(v.wrapAroundElements);
22087            }
22088            else {
22089                $(SECTION_LAST_SEL).after(v.wrapAroundElements);
22090            }
22091
22092            silentScroll($(SECTION_ACTIVE_SEL).position().top);
22093
22094            // Maintain the active slides visible in the viewport
22095            keepSlidesPosition();
22096        }
22097
22098
22099        /**
22100        * Actions to do once the section is loaded.
22101        */
22102        function afterSectionLoads (v){
22103            continuousVerticalFixSectionOrder(v);
22104
22105            //callback (afterLoad) if the site is not just resizing and readjusting the slides
22106            $.isFunction(options.afterLoad) && !v.localIsResizing && options.afterLoad.call(v.element, v.anchorLink, (v.sectionIndex + 1));
22107            options.scrollOverflowHandler.afterLoad();
22108
22109            if(!v.localIsResizing){
22110                playMedia(v.element);
22111            }
22112
22113            v.element.addClass(COMPLETELY).siblings().removeClass(COMPLETELY);
22114
22115            canScroll = true;
22116
22117            $.isFunction(v.callback) && v.callback.call(this);
22118        }
22119
22120        /**
22121        * Sets the value for the given attribute from the `data-` attribute with the same suffix
22122        * ie: data-srcset ==> srcset  |  data-src ==> src
22123        */
22124        function setSrc(element, attribute){
22125            element
22126                .attr(attribute, element.data(attribute))
22127                .removeAttr('data-' + attribute);
22128        }
22129
22130        /**
22131        * Lazy loads image, video and audio elements.
22132        */
22133        function lazyLoad(destiny){
22134            if (!options.lazyLoading){
22135                return;
22136            }
22137
22138            var panel = getSlideOrSection(destiny);
22139            var element;
22140
22141            panel.find('img[data-src], img[data-srcset], source[data-src], audio[data-src], iframe[data-src]').each(function(){
22142                element = $(this);
22143
22144                $.each(['src', 'srcset'], function(index, type){
22145                    var attribute = element.attr('data-' + type);
22146                    if(typeof attribute !== 'undefined' && attribute){
22147                        setSrc(element, type);
22148                    }
22149                });
22150
22151                if(element.is('source')){
22152                    element.closest('video').get(0).load();
22153                }
22154            });
22155        }
22156
22157        /**
22158        * Plays video and audio elements.
22159        */
22160        function playMedia(destiny){
22161            var panel = getSlideOrSection(destiny);
22162
22163            //playing HTML5 media elements
22164            panel.find('video, audio').each(function(){
22165                var element = $(this).get(0);
22166
22167                if( element.hasAttribute('data-autoplay') && typeof element.play === 'function' ) {
22168                    element.play();
22169                }
22170            });
22171
22172            //youtube videos
22173            panel.find('iframe[src*="youtube.com/embed/"]').each(function(){
22174                var element = $(this).get(0);
22175
22176                if ( element.hasAttribute('data-autoplay') ){
22177                    playYoutube(element);
22178                }
22179
22180                //in case the URL was not loaded yet. On page load we need time for the new URL (with the API string) to load.
22181                element.onload = function() {
22182                    if ( element.hasAttribute('data-autoplay') ){
22183                        playYoutube(element);
22184                    }
22185                };
22186            });
22187        }
22188
22189        /**
22190        * Plays a youtube video
22191        */
22192        function playYoutube(element){
22193            element.contentWindow.postMessage('{"event":"command","func":"playVideo","args":""}', '*');
22194        }
22195
22196        /**
22197        * Stops video and audio elements.
22198        */
22199        function stopMedia(destiny){
22200            var panel = getSlideOrSection(destiny);
22201
22202            //stopping HTML5 media elements
22203            panel.find('video, audio').each(function(){
22204                var element = $(this).get(0);
22205
22206                if( !element.hasAttribute('data-keepplaying') && typeof element.pause === 'function' ) {
22207                    element.pause();
22208                }
22209            });
22210
22211            //youtube videos
22212            panel.find('iframe[src*="youtube.com/embed/"]').each(function(){
22213                var element = $(this).get(0);
22214
22215                if( /youtube\.com\/embed\//.test($(this).attr('src')) && !element.hasAttribute('data-keepplaying')){
22216                    $(this).get(0).contentWindow.postMessage('{"event":"command","func":"pauseVideo","args":""}','*');
22217                }
22218            });
22219        }
22220
22221        /**
22222        * Gets the active slide (or section) for the given section
22223        */
22224        function getSlideOrSection(destiny){
22225            var slide = destiny.find(SLIDE_ACTIVE_SEL);
22226            if( slide.length ) {
22227                destiny = $(slide);
22228            }
22229
22230            return destiny;
22231        }
22232
22233        /**
22234        * Scrolls to the anchor in the URL when loading the site
22235        */
22236        function scrollToAnchor(){
22237            //getting the anchor link in the URL and deleting the `#`
22238            var value =  window.location.hash.replace('#', '').split('/');
22239            var sectionAnchor = decodeURIComponent(value[0]);
22240            var slideAnchor = decodeURIComponent(value[1]);
22241
22242            if(sectionAnchor){  //if theres any #
22243                if(options.animateAnchor){
22244                    scrollPageAndSlide(sectionAnchor, slideAnchor);
22245                }else{
22246                    silentMoveTo(sectionAnchor, slideAnchor);
22247                }
22248            }
22249        }
22250
22251        /**
22252        * Detecting any change on the URL to scroll to the given anchor link
22253        * (a way to detect back history button as we play with the hashes on the URL)
22254        */
22255        function hashChangeHandler(){
22256            if(!isScrolling && !options.lockAnchors){
22257                var value =  window.location.hash.replace('#', '').split('/');
22258                var sectionAnchor = decodeURIComponent(value[0]);
22259                var slideAnchor = decodeURIComponent(value[1]);
22260
22261                    //when moving to a slide in the first section for the first time (first time to add an anchor to the URL)
22262                    var isFirstSlideMove =  (typeof lastScrolledDestiny === 'undefined');
22263                    var isFirstScrollMove = (typeof lastScrolledDestiny === 'undefined' && typeof slideAnchor === 'undefined' && !slideMoving);
22264
22265
22266                if(sectionAnchor.length){
22267                    /*in order to call scrollpage() only once for each destination at a time
22268                    It is called twice for each scroll otherwise, as in case of using anchorlinks `hashChange`
22269                    event is fired on every scroll too.*/
22270                    if ((sectionAnchor && sectionAnchor !== lastScrolledDestiny) && !isFirstSlideMove || isFirstScrollMove || (!slideMoving && lastScrolledSlide != slideAnchor ))  {
22271                        scrollPageAndSlide(sectionAnchor, slideAnchor);
22272                    }
22273                }
22274            }
22275        }
22276
22277        //Sliding with arrow keys, both, vertical and horizontal
22278        function keydownHandler(e) {
22279
22280            clearTimeout(keydownId);
22281
22282            var activeElement = $(':focus');
22283
22284            if(!activeElement.is('textarea') && !activeElement.is('input') && !activeElement.is('select') &&
22285                activeElement.attr('contentEditable') !== "true" && activeElement.attr('contentEditable') !== '' &&
22286                options.keyboardScrolling && options.autoScrolling){
22287                var keyCode = e.which;
22288
22289                //preventing the scroll with arrow keys & spacebar & Page Up & Down keys
22290                var keyControls = [40, 38, 32, 33, 34];
22291                if($.inArray(keyCode, keyControls) > -1){
22292                    e.preventDefault();
22293                }
22294
22295                controlPressed = e.ctrlKey;
22296
22297                keydownId = setTimeout(function(){
22298                    onkeydown(e);
22299                },150);
22300            }
22301        }
22302
22303        function tooltipTextHandler(){
22304            $(this).prev().trigger('click');
22305        }
22306
22307        //to prevent scrolling while zooming
22308        function keyUpHandler(e){
22309            if(isWindowFocused){ //the keyup gets fired on new tab ctrl + t in Firefox
22310                controlPressed = e.ctrlKey;
22311            }
22312        }
22313
22314        //binding the mousemove when the mouse's middle button is released
22315        function mouseDownHandler(e){
22316            //middle button
22317            if (e.which == 2){
22318                oldPageY = e.pageY;
22319                container.on('mousemove', mouseMoveHandler);
22320            }
22321        }
22322
22323        //unbinding the mousemove when the mouse's middle button is released
22324        function mouseUpHandler(e){
22325            //middle button
22326            if (e.which == 2){
22327                container.off('mousemove');
22328            }
22329        }
22330
22331        //Scrolling horizontally when clicking on the slider controls.
22332        function slideArrowHandler(){
22333            var section = $(this).closest(SECTION_SEL);
22334
22335            if ($(this).hasClass(SLIDES_PREV)) {
22336                if(isScrollAllowed.m.left){
22337                    moveSlideLeft(section);
22338                }
22339            } else {
22340                if(isScrollAllowed.m.right){
22341                    moveSlideRight(section);
22342                }
22343            }
22344        }
22345
22346        //when opening a new tab (ctrl + t), `control` won't be pressed when coming back.
22347        function blurHandler(){
22348            isWindowFocused = false;
22349            controlPressed = false;
22350        }
22351
22352        //Scrolls to the section when clicking the navigation bullet
22353        function sectionBulletHandler(e){
22354            e.preventDefault();
22355            var index = $(this).parent().index();
22356            scrollPage($(SECTION_SEL).eq(index));
22357        }
22358
22359        //Scrolls the slider to the given slide destination for the given section
22360        function slideBulletHandler(e){
22361            e.preventDefault();
22362            var slides = $(this).closest(SECTION_SEL).find(SLIDES_WRAPPER_SEL);
22363            var destiny = slides.find(SLIDE_SEL).eq($(this).closest('li').index());
22364
22365            landscapeScroll(slides, destiny);
22366        }
22367
22368        /**
22369        * Keydown event
22370        */
22371        function onkeydown(e){
22372            var shiftPressed = e.shiftKey;
22373
22374            //do nothing if we can not scroll or we are not using horizotnal key arrows.
22375            if(!canScroll && [37,39].indexOf(e.which) < 0){
22376                return;
22377            }
22378
22379            switch (e.which) {
22380                //up
22381                case 38:
22382                case 33:
22383                    if(isScrollAllowed.k.up){
22384                        moveSectionUp();
22385                    }
22386                    break;
22387
22388                //down
22389                case 32: //spacebar
22390                    if(shiftPressed && isScrollAllowed.k.up){
22391                        moveSectionUp();
22392                        break;
22393                    }
22394                /* falls through */
22395                case 40:
22396                case 34:
22397                    if(isScrollAllowed.k.down){
22398                        moveSectionDown();
22399                    }
22400                    break;
22401
22402                //Home
22403                case 36:
22404                    if(isScrollAllowed.k.up){
22405                        moveTo(1);
22406                    }
22407                    break;
22408
22409                //End
22410                case 35:
22411                     if(isScrollAllowed.k.down){
22412                        moveTo( $(SECTION_SEL).length );
22413                    }
22414                    break;
22415
22416                //left
22417                case 37:
22418                    if(isScrollAllowed.k.left){
22419                        moveSlideLeft();
22420                    }
22421                    break;
22422
22423                //right
22424                case 39:
22425                    if(isScrollAllowed.k.right){
22426                        moveSlideRight();
22427                    }
22428                    break;
22429
22430                default:
22431                    return; // exit this handler for other keys
22432            }
22433        }
22434
22435        /**
22436        * Detecting the direction of the mouse movement.
22437        * Used only for the middle button of the mouse.
22438        */
22439        var oldPageY = 0;
22440        function mouseMoveHandler(e){
22441            if(canScroll){
22442                // moving up
22443                if (e.pageY < oldPageY && isScrollAllowed.m.up){
22444                    moveSectionUp();
22445                }
22446
22447                // moving down
22448                else if(e.pageY > oldPageY && isScrollAllowed.m.down){
22449                    moveSectionDown();
22450                }
22451            }
22452            oldPageY = e.pageY;
22453        }
22454
22455        /**
22456        * Scrolls horizontal sliders.
22457        */
22458        function landscapeScroll(slides, destiny, direction){
22459            var section = slides.closest(SECTION_SEL);
22460            var v = {
22461                slides: slides,
22462                destiny: destiny,
22463                direction: direction,
22464                destinyPos: destiny.position(),
22465                slideIndex: destiny.index(),
22466                section: section,
22467                sectionIndex: section.index(SECTION_SEL),
22468                anchorLink: section.data('anchor'),
22469                slidesNav: section.find(SLIDES_NAV_SEL),
22470                slideAnchor:  getAnchor(destiny),
22471                prevSlide: section.find(SLIDE_ACTIVE_SEL),
22472                prevSlideIndex: section.find(SLIDE_ACTIVE_SEL).index(),
22473
22474                //caching the value of isResizing at the momment the function is called
22475                //because it will be checked later inside a setTimeout and the value might change
22476                localIsResizing: isResizing
22477            };
22478            v.xMovement = getXmovement(v.prevSlideIndex, v.slideIndex);
22479            v.direction = v.direction ? v.direction : v.xMovement;
22480
22481            //important!! Only do it when not resizing
22482            if(!v.localIsResizing){
22483                //preventing from scrolling to the next/prev section when using scrollHorizontally
22484                canScroll = false;
22485            }
22486
22487            if(options.onSlideLeave){
22488
22489                //if the site is not just resizing and readjusting the slides
22490                if(!v.localIsResizing && v.xMovement!=='none'){
22491                    if($.isFunction( options.onSlideLeave )){
22492                        if(options.onSlideLeave.call( v.prevSlide, v.anchorLink, (v.sectionIndex + 1), v.prevSlideIndex, v.xMovement, v.slideIndex ) === false){
22493                            slideMoving = false;
22494                            return;
22495                        }
22496                    }
22497                }
22498            }
22499
22500            destiny.addClass(ACTIVE).siblings().removeClass(ACTIVE);
22501
22502            if(!v.localIsResizing){
22503                stopMedia(v.prevSlide);
22504                lazyLoad(destiny);
22505            }
22506
22507            if(!options.loopHorizontal && options.controlArrows){
22508                //hidding it for the fist slide, showing for the rest
22509                section.find(SLIDES_ARROW_PREV_SEL).toggle(v.slideIndex!==0);
22510
22511                //hidding it for the last slide, showing for the rest
22512                section.find(SLIDES_ARROW_NEXT_SEL).toggle(!destiny.is(':last-child'));
22513            }
22514
22515            //only changing the URL if the slides are in the current section (not for resize re-adjusting)
22516            if(section.hasClass(ACTIVE) && !v.localIsResizing){
22517                setState(v.slideIndex, v.slideAnchor, v.anchorLink, v.sectionIndex);
22518            }
22519
22520            performHorizontalMove(slides, v, true);
22521        }
22522
22523
22524        function afterSlideLoads(v){
22525            activeSlidesNavigation(v.slidesNav, v.slideIndex);
22526
22527            //if the site is not just resizing and readjusting the slides
22528            if(!v.localIsResizing){
22529                $.isFunction( options.afterSlideLoad ) && options.afterSlideLoad.call( v.destiny, v.anchorLink, (v.sectionIndex + 1), v.slideAnchor, v.slideIndex);
22530
22531                //needs to be inside the condition to prevent problems with continuousVertical and scrollHorizontally
22532                //and to prevent double scroll right after a windows resize
22533                canScroll = true;
22534
22535                playMedia(v.destiny);
22536            }
22537
22538            //letting them slide again
22539            slideMoving = false;
22540        }
22541
22542        /**
22543        * Performs the horizontal movement. (CSS3 or jQuery)
22544        *
22545        * @param fireCallback {Bool} - determines whether or not to fire the callback
22546        */
22547        function performHorizontalMove(slides, v, fireCallback){
22548            var destinyPos = v.destinyPos;
22549
22550            if(options.css3){
22551                var translate3d = 'translate3d(-' + Math.round(destinyPos.left) + 'px, 0px, 0px)';
22552
22553                addAnimation(slides.find(SLIDES_CONTAINER_SEL)).css(getTransforms(translate3d), v);
22554
22555                afterSlideLoadsId = setTimeout(function(){
22556                    fireCallback && afterSlideLoads(v);
22557                }, options.scrollingSpeed, options.easing);
22558            }else{
22559                slides.animate({
22560                    scrollLeft : Math.round(destinyPos.left)
22561                }, options.scrollingSpeed, options.easing, function() {
22562
22563                    fireCallback && afterSlideLoads(v);
22564                });
22565            }
22566        }
22567
22568        /**
22569        * Sets the state for the horizontal bullet navigations.
22570        */
22571        function activeSlidesNavigation(slidesNav, slideIndex){
22572            slidesNav.find(ACTIVE_SEL).removeClass(ACTIVE);
22573            slidesNav.find('li').eq(slideIndex).find('a').addClass(ACTIVE);
22574        }
22575
22576        var previousHeight = windowsHeight;
22577
22578        //when resizing the site, we adjust the heights of the sections, slimScroll...
22579        function resizeHandler(){
22580            //checking if it needs to get responsive
22581            responsive();
22582
22583            // rebuild immediately on touch devices
22584            if (isTouchDevice) {
22585                var activeElement = $(document.activeElement);
22586
22587                //if the keyboard is NOT visible
22588                if (!activeElement.is('textarea') && !activeElement.is('input') && !activeElement.is('select')) {
22589                    var currentHeight = $window.height();
22590
22591                    //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)
22592                    if( Math.abs(currentHeight - previousHeight) > (20 * Math.max(previousHeight, currentHeight) / 100) ){
22593                        reBuild(true);
22594                        previousHeight = currentHeight;
22595                    }
22596                }
22597            }else{
22598                //in order to call the functions only when the resize is finished
22599                //http://stackoverflow.com/questions/4298612/jquery-how-to-call-resize-event-only-once-its-finished-resizing
22600                clearTimeout(resizeId);
22601
22602                resizeId = setTimeout(function(){
22603                    reBuild(true);
22604                }, 350);
22605            }
22606        }
22607
22608        /**
22609        * Checks if the site needs to get responsive and disables autoScrolling if so.
22610        * A class `fp-responsive` is added to the plugin's container in case the user wants to use it for his own responsive CSS.
22611        */
22612        function responsive(){
22613            var widthLimit = options.responsive || options.responsiveWidth; //backwards compatiblity
22614            var heightLimit = options.responsiveHeight;
22615
22616            //only calculating what we need. Remember its called on the resize event.
22617            var isBreakingPointWidth = widthLimit && $window.outerWidth() < widthLimit;
22618            var isBreakingPointHeight = heightLimit && $window.height() < heightLimit;
22619
22620            if(widthLimit && heightLimit){
22621                setResponsive(isBreakingPointWidth || isBreakingPointHeight);
22622            }
22623            else if(widthLimit){
22624                setResponsive(isBreakingPointWidth);
22625            }
22626            else if(heightLimit){
22627                setResponsive(isBreakingPointHeight);
22628            }
22629        }
22630
22631        /**
22632        * Adds transition animations for the given element
22633        */
22634        function addAnimation(container, element){
22635            var transition = 'all ' + options.scrollingSpeed + 'ms ' + options.easingcss3;
22636
22637            container.removeClass(NO_TRANSITION);
22638            return container.css({
22639                '-webkit-transition': transition,
22640                'transition': transition
22641            });
22642        }
22643
22644        /**
22645        * Remove transition animations for the given element
22646        */
22647        function removeAnimation(element){
22648            return element.addClass(NO_TRANSITION);
22649        }
22650
22651        /**
22652        * Activating the vertical navigation bullets according to the given slide name.
22653        */
22654        function activateNavDots(name, sectionIndex){
22655            if(options.navigation){
22656                $(SECTION_NAV_SEL).find(ACTIVE_SEL).removeClass(ACTIVE);
22657                if(name){
22658                    $(SECTION_NAV_SEL).find('a[href="#' + name + '"]').addClass(ACTIVE);
22659                }else{
22660                    $(SECTION_NAV_SEL).find('li').eq(sectionIndex).find('a').addClass(ACTIVE);
22661                }
22662            }
22663        }
22664
22665        /**
22666        * Activating the website main menu elements according to the given slide name.
22667        */
22668        function activateMenuElement(name){
22669            if(options.menu){
22670                $(options.menu).find(ACTIVE_SEL).removeClass(ACTIVE);
22671                $(options.menu).find('[data-menuanchor="'+name+'"]').addClass(ACTIVE);
22672            }
22673        }
22674
22675        /**
22676        * Sets to active the current menu and vertical nav items.
22677        */
22678        function activateMenuAndNav(anchor, index){
22679            activateMenuElement(anchor);
22680            activateNavDots(anchor, index);
22681        }
22682
22683        /**
22684        * Retuns `up` or `down` depending on the scrolling movement to reach its destination
22685        * from the current section.
22686        */
22687        function getYmovement(destiny){
22688            var fromIndex = $(SECTION_ACTIVE_SEL).index(SECTION_SEL);
22689            var toIndex = destiny.index(SECTION_SEL);
22690            if( fromIndex == toIndex){
22691                return 'none';
22692            }
22693            if(fromIndex > toIndex){
22694                return 'up';
22695            }
22696            return 'down';
22697        }
22698
22699        /**
22700        * Retuns `right` or `left` depending on the scrolling movement to reach its destination
22701        * from the current slide.
22702        */
22703        function getXmovement(fromIndex, toIndex){
22704            if( fromIndex == toIndex){
22705                return 'none';
22706            }
22707            if(fromIndex > toIndex){
22708                return 'left';
22709            }
22710            return 'right';
22711        }
22712
22713        /**
22714        * Checks if the element needs scrollbar and if the user wants to apply it.
22715        * If so it creates it.
22716        *
22717        * @param {Object} element   jQuery object of the section or slide
22718        */
22719        function createScrollBar(element){
22720            //User doesn't want scrollbar here? Sayonara baby!
22721            if(element.hasClass('fp-noscroll')) return;
22722
22723            //needed to make `scrollHeight` work under Opera 12
22724            element.css('overflow', 'hidden');
22725
22726            var scrollOverflowHandler = options.scrollOverflowHandler;
22727            var wrap = scrollOverflowHandler.wrapContent();
22728            //in case element is a slide
22729            var section = element.closest(SECTION_SEL);
22730            var scrollable = scrollOverflowHandler.scrollable(element);
22731            var contentHeight;
22732
22733            //if there was scroll, the contentHeight will be the one in the scrollable section
22734            if(scrollable.length){
22735                contentHeight = scrollOverflowHandler.scrollHeight(element);
22736            }else{
22737                contentHeight = element.get(0).scrollHeight;
22738                if(options.verticalCentered){
22739                    contentHeight = element.find(TABLE_CELL_SEL).get(0).scrollHeight;
22740                }
22741            }
22742
22743            var scrollHeight = windowsHeight - parseInt(section.css('padding-bottom')) - parseInt(section.css('padding-top'));
22744
22745            //needs scroll?
22746            if ( contentHeight > scrollHeight) {
22747                //did we already have an scrollbar ? Updating it
22748                if(scrollable.length){
22749                    scrollOverflowHandler.update(element, scrollHeight);
22750                }
22751                //creating the scrolling
22752                else{
22753                    if(options.verticalCentered){
22754                        element.find(TABLE_CELL_SEL).wrapInner(wrap);
22755                    }else{
22756                        element.wrapInner(wrap);
22757                    }
22758                    scrollOverflowHandler.create(element, scrollHeight);
22759                }
22760            }
22761            //removing the scrolling when it is not necessary anymore
22762            else{
22763                scrollOverflowHandler.remove(element);
22764            }
22765
22766            //undo
22767            element.css('overflow', '');
22768        }
22769
22770        function addTableClass(element){
22771            //In case we are styling for the 2nd time as in with reponsiveSlides
22772            if(!element.hasClass(TABLE)){
22773                element.addClass(TABLE).wrapInner('<div class="' + TABLE_CELL + '" style="height:' + getTableHeight(element) + 'px;" />');
22774            }
22775        }
22776
22777        function getTableHeight(element){
22778            var sectionHeight = windowsHeight;
22779
22780            if(options.paddingTop || options.paddingBottom){
22781                var section = element;
22782                if(!section.hasClass(SECTION)){
22783                    section = element.closest(SECTION_SEL);
22784                }
22785
22786                var paddings = parseInt(section.css('padding-top')) + parseInt(section.css('padding-bottom'));
22787                sectionHeight = (windowsHeight - paddings);
22788            }
22789
22790            return sectionHeight;
22791        }
22792
22793        /**
22794        * Adds a css3 transform property to the container class with or without animation depending on the animated param.
22795        */
22796        function transformContainer(translate3d, animated){
22797            if(animated){
22798                addAnimation(container);
22799            }else{
22800                removeAnimation(container);
22801            }
22802
22803            container.css(getTransforms(translate3d));
22804
22805            //syncronously removing the class after the animation has been applied.
22806            setTimeout(function(){
22807                container.removeClass(NO_TRANSITION);
22808            },10);
22809        }
22810
22811        /**
22812        * Gets a section by its anchor / index
22813        */
22814        function getSectionByAnchor(sectionAnchor){
22815            if(!sectionAnchor) return [];
22816
22817            var section = container.find(SECTION_SEL + '[data-anchor="'+sectionAnchor+'"]');
22818            if(!section.length){
22819                section = $(SECTION_SEL).eq( sectionAnchor -1);
22820            }
22821
22822            return section;
22823        }
22824
22825        /**
22826        * Gets a slide inside a given section by its anchor / index
22827        */
22828        function getSlideByAnchor(slideAnchor, section){
22829            var slides = section.find(SLIDES_WRAPPER_SEL);
22830            var slide =  slides.find(SLIDE_SEL + '[data-anchor="'+slideAnchor+'"]');
22831
22832            if(!slide.length){
22833                slide = slides.find(SLIDE_SEL).eq(slideAnchor);
22834            }
22835
22836            return slide;
22837        }
22838
22839        /**
22840        * Scrolls to the given section and slide anchors
22841        */
22842        function scrollPageAndSlide(destiny, slide){
22843            var section = getSectionByAnchor(destiny);
22844
22845            //do nothing if there's no section with the given anchor name
22846            if(!section.length) return;
22847
22848            //default slide
22849            if (typeof slide === 'undefined') {
22850                slide = 0;
22851            }
22852
22853            //we need to scroll to the section and then to the slide
22854            if (destiny !== lastScrolledDestiny && !section.hasClass(ACTIVE)){
22855                scrollPage(section, function(){
22856                    scrollSlider(section, slide);
22857                });
22858            }
22859            //if we were already in the section
22860            else{
22861                scrollSlider(section, slide);
22862            }
22863        }
22864
22865        /**
22866        * Scrolls the slider to the given slide destination for the given section
22867        */
22868        function scrollSlider(section, slideAnchor){
22869            if(typeof slideAnchor !== 'undefined'){
22870                var slides = section.find(SLIDES_WRAPPER_SEL);
22871                var destiny =  getSlideByAnchor(slideAnchor, section);
22872
22873                if(destiny.length){
22874                    landscapeScroll(slides, destiny);
22875                }
22876            }
22877        }
22878
22879        /**
22880        * Creates a landscape navigation bar with dots for horizontal sliders.
22881        */
22882        function addSlidesNavigation(section, numSlides){
22883            section.append('<div class="' + SLIDES_NAV + '"><ul></ul></div>');
22884            var nav = section.find(SLIDES_NAV_SEL);
22885
22886            //top or bottom
22887            nav.addClass(options.slidesNavPosition);
22888
22889            for(var i=0; i< numSlides; i++){
22890                nav.find('ul').append('<li><a href="#"><span></span></a></li>');
22891            }
22892
22893            //centering it
22894            nav.css('margin-left', '-' + (nav.width()/2) + 'px');
22895
22896            nav.find('li').first().find('a').addClass(ACTIVE);
22897        }
22898
22899
22900        /**
22901        * Sets the state of the website depending on the active section/slide.
22902        * It changes the URL hash when needed and updates the body class.
22903        */
22904        function setState(slideIndex, slideAnchor, anchorLink, sectionIndex){
22905            var sectionHash = '';
22906
22907            if(options.anchors.length && !options.lockAnchors){
22908
22909                //isn't it the first slide?
22910                if(slideIndex){
22911                    if(typeof anchorLink !== 'undefined'){
22912                        sectionHash = anchorLink;
22913                    }
22914
22915                    //slide without anchor link? We take the index instead.
22916                    if(typeof slideAnchor === 'undefined'){
22917                        slideAnchor = slideIndex;
22918                    }
22919
22920                    lastScrolledSlide = slideAnchor;
22921                    setUrlHash(sectionHash + '/' + slideAnchor);
22922
22923                //first slide won't have slide anchor, just the section one
22924                }else if(typeof slideIndex !== 'undefined'){
22925                    lastScrolledSlide = slideAnchor;
22926                    setUrlHash(anchorLink);
22927                }
22928
22929                //section without slides
22930                else{
22931                    setUrlHash(anchorLink);
22932                }
22933            }
22934
22935            setBodyClass();
22936        }
22937
22938        /**
22939        * Sets the URL hash.
22940        */
22941        function setUrlHash(url){
22942            //Uncode addition
22943            if ( typeof SiteParameters.slide_footer != 'undefined' && url == SiteParameters.slide_footer )
22944                return false;
22945
22946            if(options.recordHistory){
22947                location.hash = url;
22948            }else{
22949                //Uncode addition
22950                //Mobile Chrome doesn't work the normal way, so... lets use HTML5 for phones :)
22951                // if(isTouchDevice || isTouch){
22952                //     window.history.replaceState(undefined, undefined, '#' + url);
22953                // }else{
22954                //     var baseUrl = window.location.href.split('#')[0];
22955                //     window.location.replace( baseUrl + '#' + url );
22956                // }
22957                return false;
22958            }
22959        }
22960
22961        /**
22962        * Gets the anchor for the given slide / section. Its index will be used if there's none.
22963        */
22964        function getAnchor(element){
22965            var anchor = element.data('anchor');
22966            var index = element.index();
22967
22968            //Slide without anchor link? We take the index instead.
22969            if(typeof anchor === 'undefined'){
22970                anchor = index;
22971            }
22972
22973            return anchor;
22974        }
22975
22976        /**
22977        * Sets a class for the body of the page depending on the active section / slide
22978        */
22979        function setBodyClass(){
22980            var section = $(SECTION_ACTIVE_SEL);
22981            var slide = section.find(SLIDE_ACTIVE_SEL);
22982
22983            var sectionAnchor = getAnchor(section);
22984            var slideAnchor = getAnchor(slide);
22985
22986            var text = String(sectionAnchor);
22987
22988            if(slide.length){
22989                text = text + '-' + slideAnchor;
22990            }
22991
22992            //changing slash for dash to make it a valid CSS style
22993            text = text.replace('/', '-').replace('#','');
22994
22995            //removing previous anchor classes
22996            var classRe = new RegExp('\\b\\s?' + VIEWING_PREFIX + '-[^\\s]+\\b', "g");
22997            $body[0].className = $body[0].className.replace(classRe, '');
22998
22999            //adding the current anchor
23000            $body.addClass(VIEWING_PREFIX + '-' + text);
23001        }
23002
23003        /**
23004        * Checks for translate3d support
23005        * @return boolean
23006        * http://stackoverflow.com/questions/5661671/detecting-transform-translate3d-support
23007        */
23008        function support3d() {
23009            var el = document.createElement('p'),
23010                has3d,
23011                transforms = {
23012                    'webkitTransform':'-webkit-transform',
23013                    'OTransform':'-o-transform',
23014                    'msTransform':'-ms-transform',
23015                    'MozTransform':'-moz-transform',
23016                    'transform':'transform'
23017                };
23018
23019            // Add it to the body to get the computed style.
23020            document.body.insertBefore(el, null);
23021
23022            for (var t in transforms) {
23023                if (el.style[t] !== undefined) {
23024                    el.style[t] = 'translate3d(1px,1px,1px)';
23025                    has3d = window.getComputedStyle(el).getPropertyValue(transforms[t]);
23026                }
23027            }
23028
23029            document.body.removeChild(el);
23030
23031            return (has3d !== undefined && has3d.length > 0 && has3d !== 'none');
23032        }
23033
23034        /**
23035        * Removes the auto scrolling action fired by the mouse wheel and trackpad.
23036        * After this function is called, the mousewheel and trackpad movements won't scroll through sections.
23037        */
23038        function removeMouseWheelHandler(){
23039            if (document.addEventListener) {
23040                document.removeEventListener('mousewheel', MouseWheelHandler, false); //IE9, Chrome, Safari, Oper
23041                document.removeEventListener('wheel', MouseWheelHandler, false); //Firefox
23042                document.removeEventListener('MozMousePixelScroll', MouseWheelHandler, false); //old Firefox
23043            } else {
23044                document.detachEvent('onmousewheel', MouseWheelHandler); //IE 6/7/8
23045            }
23046        }
23047
23048        /**
23049        * Adds the auto scrolling action for the mouse wheel and trackpad.
23050        * After this function is called, the mousewheel and trackpad movements will scroll through sections
23051        * https://developer.mozilla.org/en-US/docs/Web/Events/wheel
23052        */
23053        function addMouseWheelHandler(){
23054            var prefix = '';
23055            var _addEventListener;
23056
23057            if (window.addEventListener){
23058                _addEventListener = "addEventListener";
23059            }else{
23060                _addEventListener = "attachEvent";
23061                prefix = 'on';
23062            }
23063
23064             // detect available wheel event
23065            var support = 'onwheel' in document.createElement('div') ? 'wheel' : // Modern browsers support "wheel"
23066                      document.onmousewheel !== undefined ? 'mousewheel' : // Webkit and IE support at least "mousewheel"
23067                      'DOMMouseScroll'; // let's assume that remaining browsers are older Firefox
23068            var passiveEvent = g_supportsPassive ? {passive: false }
23068: false;
23069
23070
23071            if(support == 'DOMMouseScroll'){
23072                document[ _addEventListener ](prefix + 'MozMousePixelScroll', MouseWheelHandler, passiveEvent);
23073            }
23074
23075            //handle MozMousePixelScroll in older Firefox
23076            else{
23077                document[ _addEventListener ](prefix + support, MouseWheelHandler, passiveEvent);
23078            }
23079        }
23080
23081        /**
23082        * Binding the mousemove when the mouse's middle button is pressed
23083        */
23084        function addMiddleWheelHandler(){
23085            container
23086                .on('mousedown', mouseDownHandler)
23087                .on('mouseup', mouseUpHandler);
23088        }
23089
23090        /**
23091        * Unbinding the mousemove when the mouse's middle button is released
23092        */
23093        function removeMiddleWheelHandler(){
23094            container
23095                .off('mousedown', mouseDownHandler)
23096                .off('mouseup', mouseUpHandler);
23097        }
23098
23099        /**
23100        * Adds the possibility to auto scroll through sections on touch devices.
23101        */
23102        function addTouchHandler(){
23103            if(isTouchDevice || isTouch){
23104                if(options.autoScrolling){
23105                    $body.off(events.touchmove).on(events.touchmove, preventBouncing);
23106                }
23107
23108                $(WRAPPER_SEL)
23109                    .off(events.touchstart).on(events.touchstart, touchStartHandler)
23110                    .off(events.touchmove).on(events.touchmove, touchMoveHandler);
23111            }
23112        }
23113
23114        /**
23115        * Removes the auto scrolling for touch devices.
23116        */
23117        function removeTouchHandler(){
23118            if(isTouchDevice || isTouch){
23119                $(WRAPPER_SEL)
23120                    .off(events.touchstart)
23121                    .off(events.touchmove);
23122            }
23123        }
23124
23125        /*
23126        * Returns and object with Microsoft pointers (for IE<11 and for IE >= 11)
23127        * http://msdn.microsoft.com/en-us/library/ie/dn304886(v=vs.85).aspx
23128        */
23129        function getMSPointer(){
23130            var pointer;
23131
23132            //IE >= 11 & rest of browsers
23133            if(window.PointerEvent){
23134                pointer = { down: 'pointerdown', move: 'pointermove'};
23135            }
23136
23137            //IE < 11
23138            else{
23139                pointer = { down: 'MSPointerDown', move: 'MSPointerMove'};
23140            }
23141
23142            return pointer;
23143        }
23144
23145        /**
23146        * Gets the pageX and pageY properties depending on the browser.
23147        * https://github.com/alvarotrigo/fullPage.js/issues/194#issuecomment-34069854
23148        */
23149        function getEventsPage(e){
23150            var events = [];
23151
23152            events.y = (typeof e.pageY !== 'undefined' && (e.pageY || e.pageX) ? e.pageY : e.touches[0].pageY);
23153            events.x = (typeof e.pageX !== 'undefined' && (e.pageY || e.pageX) ? e.pageX : e.touches[0].pageX);
23154
23155            //in touch devices with scrollBar:true, e.pageY is detected, but we have to deal with touch events. #1008
23156            if(isTouch && isReallyTouch(e) && options.scrollBar){
23157                events.y = e.touches[0].pageY;
23158                events.x = e.touches[0].pageX;
23159            }
23160
23161            return events;
23162        }
23163
23164        /**
23165        * Slides silently (with no animation) the active slider to the given slide.
23166        * @param noCallback {bool} true or defined -> no callbacks
23167        */
23168        function silentLandscapeScroll(activeSlide, noCallbacks){
23169            setScrollingSpeed (0, 'internal');
23170
23171            if(typeof noCallbacks !== 'undefined'){
23172                //preventing firing callbacks afterSlideLoad etc.
23173                isResizing = true;
23174            }
23175
23176            landscapeScroll(activeSlide.closest(SLIDES_WRAPPER_SEL), activeSlide);
23177
23178            if(typeof noCallbacks !== 'undefined'){
23179                isResizing = false;
23180            }
23181
23182            setScrollingSpeed(originals.scrollingSpeed, 'internal');
23183        }
23184
23185        /**
23186        * Scrolls silently (with no animation) the page to the given Y position.
23187        */
23188        function silentScroll(top){
23189            // The first section can have a negative value in iOS 10. Not quite sure why: -0.0142822265625
23190            // that's why we round it to 0.
23191            var roundedTop = Math.round(top);
23192
23193            if (options.css3 && options.autoScrolling && !options.scrollBar){
23194                var translate3d = 'translate3d(0px, -' + roundedTop + 'px, 0px)';
23195                transformContainer(translate3d, false);
23196            }
23197            else if(options.autoScrolling && !options.scrollBar){
23198                container.css('top', -roundedTop);
23199            }
23200            else{
23201                $htmlBody.scrollTop(roundedTop);
23202            }
23203        }
23204
23205        /**
23206        * Returns the cross-browser transform string.
23207        */
23208        function getTransforms(translate3d){
23209            return {
23210                '-webkit-transform': translate3d,
23211                '-moz-transform': translate3d,
23212                '-ms-transform':translate3d,
23213                'transform': translate3d
23214            };
23215        }
23216
23217        /**
23218        * Allowing or disallowing the mouse/swipe scroll in a given direction. (not for keyboard)
23219        * @type  m (mouse) or k (keyboard)
23220        */
23221        function setIsScrollAllowed(value, direction, type){
23222            switch (direction){
23223                case 'up': isScrollAllowed[type].up = value; break;
23224                case 'down': isScrollAllowed[type].down = value; break;
23225                case 'left': isScrollAllowed[type].left = value; break;
23226                case 'right': isScrollAllowed[type].right = value; break;
23227                case 'all':
23228                    if(type == 'm'){
23229                        setAllowScrolling(value);
23230                    }else{
23231                        setKeyboardScrolling(value);
23232                    }
23233            }
23234        }
23235
23236        /*
23237        * Destroys fullpage.js plugin events and optinally its html markup and styles
23238        */
23239        function destroy(all){
23240            setAutoScrolling(false, 'internal');
23241            setAllowScrolling(false);
23242            setKeyboardScrolling(false);
23243            container.addClass(DESTROYED);
23244
23245            clearTimeout(afterSlideLoadsId);
23246            clearTimeout(afterSectionLoadsId);
23247            clearTimeout(resizeId);
23248            clearTimeout(scrollId);
23249            clearTimeout(scrollId2);
23250
23251            $window
23252                .off('scroll', scrollHandler)
23253                .off('hashchange', hashChangeHandler)
23254                .off('resize', resizeHandler);
23255
23256            $document
23257                .off('click touchstart', SECTION_NAV_SEL + ' a')
23258                .off('mouseenter', SECTION_NAV_SEL + ' li')
23259                .off('mouseleave', SECTION_NAV_SEL + ' li')
23260                .off('click touchstart', SLIDES_NAV_LINK_SEL)
23261                .off('mouseover', options.normalScrollElements)
23262                .off('mouseout', options.normalScrollElements);
23263
23264            $(SECTION_SEL)
23265                .off('click touchstart', SLIDES_ARROW_SEL);
23266
23267            clearTimeout(afterSlideLoadsId);
23268            clearTimeout(afterSectionLoadsId);
23269
23270            //lets make a mess!
23271            if(all){
23272                destroyStructure();
23273            }
23274        }
23275
23276        /*
23277        * Removes inline styles added by fullpage.js
23278        */
23279        function destroyStructure(){
23280            //reseting the `top` or `translate` properties to 0
23281            silentScroll(0);
23282
23283            //loading all the lazy load content
23284            container.find('img[data-src], source[data-src], audio[data-src], iframe[data-src]').each(function(){
23285                setSrc($(this), 'src');
23286            });
23287
23288            container.find('img[data-srcset]').each(function(){
23289                setSrc($(this), 'srcset');
23290            });
23291
23292            $(SECTION_NAV_SEL + ', ' + SLIDES_NAV_SEL +  ', ' + SLIDES_ARROW_SEL).remove();
23293
23294            //removing inline styles
23295            $(SECTION_SEL).css( {
23296                'height': '',
23297                'background-color' : '',
23298                'padding': ''
23299            });
23300
23301            $(SLIDE_SEL).css( {
23302                'width': ''
23303            });
23304
23305            container.css({
23306                'height': '',
23307                'position': '',
23308                '-ms-touch-action': '',
23309                'touch-action': ''
23310            });
23311
23312            $htmlBody.css({
23313                'overflow': '',
23314                'height': ''
23315            });
23316
23317            // remove .fp-enabled class
23318            $('html').removeClass(ENABLED);
23319
23320            // remove .fp-responsive class
23321            $body.removeClass(RESPONSIVE);
23322
23323            // remove all of the .fp-viewing- classes
23324            $.each($body.get(0).className.split(/\s+/), function (index, className) {
23325                if (className.indexOf(VIEWING_PREFIX) === 0) {
23326                    $body.removeClass(className);
23327                }
23328            });
23329
23330            //removing added classes
23331            $(SECTION_SEL + ', ' + SLIDE_SEL).each(function(){
23332                options.scrollOverflowHandler.remove($(this));
23333                $(this).removeClass(TABLE + ' ' + ACTIVE);
23334            });
23335
23336            removeAnimation(container);
23337
23338            //Unwrapping content
23339            container.find(TABLE_CELL_SEL + ', ' + SLIDES_CONTAINER_SEL + ', ' + SLIDES_WRAPPER_SEL).each(function(){
23340                //unwrap not being use in case there's no child element inside and its just text
23341                $(this).replaceWith(this.childNodes);
23342            });
23343
23344            //removing the applied transition from the fullpage wrapper
23345            container.css({
23346                '-webkit-transition': 'none',
23347                'transition': 'none'
23348            });
23349
23350            //scrolling the page to the top with no animation
23351            $htmlBody.scrollTop(0);
23352
23353            //removing selectors
23354            var usedSelectors = [SECTION, SLIDE, SLIDES_CONTAINER];
23355            $.each(usedSelectors, function(index, value){
23356                $('.' + value).removeClass(value);
23357            });
23358        }
23359
23360        /*
23361        * Sets the state for a variable with multiple states (original, and temporal)
23362        * Some variables such as `autoScrolling` or `recordHistory` might change automatically its state when using `responsive` or `autoScrolling:false`.
23363        * This function is used to keep track of both states, the original and the temporal one.
23364        * If type is not 'internal', then we assume the user is globally changing the variable.
23365        */
23366        function setVariableState(variable, value, type){
23367            options[variable] = value;
23368            if(type !== 'internal'){
23369                originals[variable] = value;
23370            }
23371        }
23372
23373        /**
23374        * Displays warnings
23375        */
23376        function displayWarnings(){
23377            var extensions = ['fadingEffect', 'continuousHorizontal', 'scrollHorizontally', 'interlockedSlides', 'resetSliders', 'responsiveSlides', 'offsetSections', 'dragAndMove', 'scrollOverflowReset', 'parallax'];
23378            //UNCODE.addition
23379            // if($('html').hasClass(ENABLED)){
23380            //     showError('error', 'Fullpage.js can only be initialized once and you are doing it multiple times!');
23381            //     return;
23382            // }
23383
23384            // Disable mutually exclusive settings
23385            //UNCODE.addition
23386            // if (options.continuousVertical &&
23387            //     (options.loopTop || options.loopBottom)) {
23388            //     options.continuousVertical = false;
23389            //     showError('warn', 'Option `loopTop/loopBottom` is mutually exclusive with `continuousVertical`; `continuousVertical` disabled');
23390            // }
23391
23392            // if(options.scrollBar && options.scrollOverflow){
23393            //     showError('warn', 'Option `scrollBar` is mutually exclusive with `scrollOverflow`. Sections with scrollOverflow might not work well in Firefox');
23394            // }
23395
23396            // if(options.continuousVertical && (options.scrollBar || !options.autoScrolling)){
23397            //     options.continuousVertical = false;
23398            //     showError('warn', 'Scroll bars (`scrollBar:true` or `autoScrolling:false`) are mutually exclusive with `continuousVertical`; `continuousVertical` disabled');
23399            // }
23400
23401            //using extensions? Wrong file!
23402            $.each(extensions, function(index, extension){
23403                //is the option set to true?
23404                if(options[extension]){
23405                    showError('warn', 'fullpage.js extensions require jquery.fullpage.extensions.min.js file instead of the usual jquery.fullpage.js. Requested: '+ extension);
23406                }
23407            });
23408
23409            //anchors can not have the same value as any element ID or NAME
23410            $.each(options.anchors, function(index, name){
23411
23412                //case insensitive selectors (http://stackoverflow.com/a/19465187/1081396)
23413                var nameAttr = $document.find('[name]').filter(function() {
23414                    return $(this).attr('name') && $(this).attr('name').toLowerCase() == name.toLowerCase();
23415                });
23416
23417                var idAttr = $document.find('[id]').filter(function() {
23418                    return $(this).attr('id') && $(this).attr('id').toLowerCase() == name.toLowerCase();
23419                });
23420
23421                if(idAttr.length || nameAttr.length ){
23422                    showError('error', 'data-anchor tags can not have the same value as any `id` element on the site (or `name` element for IE).');
23423                    idAttr.length && showError('error', '"' + name + '" is is being used by another element `id` property');
23424                    nameAttr.length && showError('error', '"' + name + '" is is being used by another element `name` property');
23425                }
23426            });
23427        }
23428
23429        /**
23430        * Shows a message in the console of the given type.
23431        */
23432        function showError(type, text){
23433            console && console[type] && console[type]('fullPage: ' + text);
23434        }
23435
23436    }; //end of $.fn.fullpage
23437
23438    if(typeof IScroll !== 'undefined'){
23439        /*
23440        * Turns iScroll `mousewheel` option off dynamically
23441        * https://github.com/cubiq/iscroll/issues/1036
23442        */
23443        IScroll.prototype.wheelOn = function () {
23444            this.wrapper.addEventListener('wheel', this);
23445            this.wrapper.addEventListener('mousewheel', this);
23446            this.wrapper.addEventListener('DOMMouseScroll', this);
23447        };
23448
23449        /*
23450        * Turns iScroll `mousewheel` option on dynamically
23451        * https://github.com/cubiq/iscroll/issues/1036
23452        */
23453        IScroll.prototype.wheelOff = function () {
23454            this.wrapper.removeEventListener('wheel', this);
23455            this.wrapper.removeEventListener('mousewheel', this);
23456            this.wrapper.removeEventListener('DOMMouseScroll', this);
23457        };
23458    }
23459
23460    /**
23461     * An object to handle overflow scrolling.
23462     * This uses jquery.slimScroll to accomplish overflow scrolling.
23463     * It is possible to pass in an alternate scrollOverflowHandler
23464     * to the fullpage.js option that implements the same functions
23465     * as this handler.
23466     *
23467     * @type {Object}
23468     */
23469    var iscrollHandler = {
23470        refreshId: null,
23471        iScrollInstances: [],
23472
23473        // Enables or disables the mouse wheel for the active section or all slides in it
23474        toggleWheel: function(value){
23475            var scrollable = $(SECTION_ACTIVE_SEL).find(SCROLLABLE_SEL);
23476            scrollable.each(function(){
23477                var iScrollInstance = $(this).data('iscrollInstance');
23478                if(typeof iScrollInstance !== 'undefined' && iScrollInstance){
23479                    if(value){
23480                        iScrollInstance.wheelOn();
23481                    }
23482                    else{
23483                        iScrollInstance.wheelOff();
23484                    }
23485                }
23486            });
23487        },
23488
23489        /**
23490        * Turns off iScroll for the destination section.
23491        * When scrolling very fast on some trackpads (and Apple laptops) the inertial scrolling would
23492        * scroll the destination section/slide before the sections animations ends.
23493        */
23494        onLeave: function(){
23495            iscrollHandler.toggleWheel(false);
23496        },
23497
23498        // Turns off iScroll for the leaving section
23499        beforeLeave: function(){
23500            iscrollHandler.onLeave()
23501        },
23502
23503        // Turns on iScroll on section load
23504        afterLoad: function(){
23505            iscrollHandler.toggleWheel(true);
23506        },
23507
23508        /**
23509         * Called when overflow scrolling is needed for a section.
23510         *
23511         * @param  {Object} element      jQuery object containing current section
23512         * @param  {Number} scrollHeight Current window height in pixels
23513         */
23514        create: function(element, scrollHeight) {
23515            var scrollable = element.find(SCROLLABLE_SEL);
23516
23517            scrollable.height(scrollHeight);
23518            scrollable.each(function() {
23519                var $this = $(this);
23520                var iScrollInstance = $this.data('iscrollInstance');
23521                if (iScrollInstance) {
23522                    $.each(iscrollHandler.iScrollInstances, function(){
23523                        $(this).destroy();
23524                    });
23525                }
23526
23527                iScrollInstance = new IScroll($this.get(0), iscrollOptions);
23528                iscrollHandler.iScrollInstances.push(iScrollInstance);
23529
23530                //off by default until the section gets active
23531                iScrollInstance.wheelOff();
23532
23533                $this.data('iscrollInstance', iScrollInstance);
23534            });
23535        },
23536
23537        /**
23538         * Return a boolean depending on whether the scrollable element is a
23539         * the end or at the start of the scrolling depending on the given type.
23540         *
23541         * @param  {String}  type       Either 'top' or 'bottom'
23542         * @param  {Object}  scrollable jQuery object for the scrollable element
23543         * @return {Boolean}
23544         */
23545        isScrolled: function(type, scrollable) {
23546            var scroller = scrollable.data('iscrollInstance');
23547
23548            //no scroller?
23549            if (!scroller) {
23550                return true;
23551            }
23552
23553            if (type === 'top') {
23554                return scroller.y >= 0 && !scrollable.scrollTop();
23555            } else if (type === 'bottom') {
23556                return (0 - scroller.y) + scrollable.scrollTop() + 1 + scrollable.innerHeight() >= scrollable[0].scrollHeight;
23557            }
23558        },
23559
23560        /**
23561         * Returns the scrollable element for the given section.
23562         * If there are landscape slides, will only return a scrollable element
23563         * if it is in the active slide.
23564         *
23565         * @param  {Object}  activeSection jQuery object containing current section
23566         * @return {Boolean}
23567         */
23568        scrollable: function(activeSection){
23569            // if there are landscape slides, we check if the scrolling bar is in the current one or not
23570            if (activeSection.find(SLIDES_WRAPPER_SEL).length) {
23571                return activeSection.find(SLIDE_ACTIVE_SEL).find(SCROLLABLE_SEL);
23572            }
23573            return activeSection.find(SCROLLABLE_SEL);
23574        },
23575
23576        /**
23577         * Returns the scroll height of the wrapped content.
23578         * If this is larger than the window height minus section padding,
23579         * overflow scrolling is needed.
23580         *
23581         * @param  {Object} element jQuery object containing current section
23582         * @return {Number}
23583         */
23584        scrollHeight: function(element) {
23585            return element.find(SCROLLABLE_SEL).children().first().get(0).scrollHeight;
23586        },
23587
23588        /**
23589         * Called when overflow scrolling is no longer needed for a section.
23590         *
23591         * @param  {Object} element      jQuery object containing current section
23592         */
23593        remove: function(element) {
23594            var scrollable = element.find(SCROLLABLE_SEL);
23595            if (scrollable.length) {
23596                var iScrollInstance = scrollable.data('iscrollInstance');
23597                iScrollInstance.destroy();
23598
23599                scrollable.data('iscrollInstance', null);
23600            }
23601            element.find(SCROLLABLE_SEL).children().first().children().first().unwrap().unwrap();
23602        },
23603
23604        /**
23605         * Called when overflow scrolling has already been setup but the
23606         * window height has potentially changed.
23607         *
23608         * @param  {Object}
23608 element      jQuery object containing current section
23609         * @param  {Number} scrollHeight Current window height in pixels
23610         */
23611        update: function(element, scrollHeight) {
23612            //using a timeout in order to execute the refresh function only once when `update` is called multiple times in a
23613            //short period of time.
23614            //it also comes on handy because iScroll requires the use of timeout when using `refresh`.
23615            clearTimeout(iscrollHandler.refreshId);
23616            iscrollHandler.refreshId = setTimeout(function(){
23617                $.each(iscrollHandler.iScrollInstances, function(){
23618                    $(this).get(0).refresh();
23619                });
23620            }, 150);
23621
23622            //updating the wrappers height
23623            element.find(SCROLLABLE_SEL).css('height', scrollHeight + 'px').parent().css('height', scrollHeight + 'px');
23624        },
23625
23626        /**
23627         * Called to get any additional elements needed to wrap the section
23628         * content in order to facilitate overflow scrolling.
23629         *
23630         * @return {String|Object} Can be a string containing HTML,
23631         *                         a DOM element, or jQuery object.
23632         */
23633        wrapContent: function() {
23634            return '<div class="' + SCROLLABLE + '"><div class="fp-scroller"></div></div>';
23635        }
23636    };
23637});
23638
23639/* ========================================================================
23640 * Bootstrap: collapse.js v3.1.1
23641 * http://getbootstrap.com/javascript/#collapse
23642 * ========================================================================
23643 * Copyright 2011-2014 Twitter, Inc.
23644 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
23645 * ======================================================================== */
23646
23647
23648+function ($) {
23649  'use strict';
23650
23651  // COLLAPSE PUBLIC CLASS DEFINITION
23652  // ================================
23653
23654  var Collapse = function (element, options) {
23655    this.$element      = $(element)
23656    this.options       = $.extend({}, Collapse.DEFAULTS, options)
23657    this.transitioning = null
23658
23659    if (this.options.parent) this.$parent = $(this.options.parent)
23660    if (this.options.toggle) this.toggle()
23661  }
23662
23663  Collapse.DEFAULTS = {
23664    toggle: true
23665  }
23666
23667  Collapse.prototype.dimension = function () {
23668    var hasWidth = this.$element.hasClass('width')
23669    return hasWidth ? 'width' : 'height'
23670  }
23671
23672  Collapse.prototype.show = function () {
23673    if (this.transitioning || this.$element.hasClass('in')) return
23674
23675    var startEvent = $.Event('show.bs.collapse')
23676    this.$element.trigger(startEvent)
23677    if (startEvent.isDefaultPrevented()) return
23678
23679    var actives = this.$parent && this.$parent.find('> .panel > .in')
23680
23681    if (actives && actives.length) {
23682      var hasData = actives.data('bs.collapse')
23683      if (hasData && hasData.transitioning) return
23684      actives.collapse('hide')
23685      hasData || actives.data('bs.collapse', null)
23686    }
23687
23688    var dimension = this.dimension()
23689
23690    this.$element
23691      .removeClass('collapse')
23692      .addClass('collapsing')[dimension](0)
23693
23694    this.transitioning = 1
23695
23696    var complete = function (e) {
23697      if (e && e.target != this.$element[0]) return
23698      this.$element
23699        .removeClass('collapsing')
23700        .addClass('collapse in')[dimension]('auto')
23701      this.transitioning = 0
23702      this.$element.trigger('shown.bs.collapse')
23703    }
23704
23705    if (!$.support.transition) return complete.call(this)
23706
23707    var scrollSize = $.camelCase(['scroll', dimension].join('-'))
23708
23709    this.$element
23710      .one($.support.transition.end, $.proxy(complete, this))
23711      .emulateTransitionEnd(350)[dimension](this.$element[0][scrollSize])
23712  }
23713
23714  Collapse.prototype.hide = function () {
23715    if (this.transitioning || !this.$element.hasClass('in')) return
23716
23717    var startEvent = $.Event('hide.bs.collapse')
23718    this.$element.trigger(startEvent)
23719    if (startEvent.isDefaultPrevented()) return
23720
23721    var dimension = this.dimension()
23722
23723    this.$element[dimension](this.$element[dimension]())[0].offsetHeight
23724
23725    this.$element
23726      .addClass('collapsing')
23727      .removeClass('collapse')
23728      .removeClass('in')
23729
23730    this.transitioning = 1
23731
23732    var complete = function (e) {
23733      if (e && e.target != this.$element[0]) return
23734      this.transitioning = 0
23735      this.$element
23736        .trigger('hidden.bs.collapse')
23737        .removeClass('collapsing')
23738        .addClass('collapse')
23739    }
23740
23741    if (!$.support.transition) return complete.call(this)
23742
23743    this.$element
23744      [dimension](0)
23745      .one($.support.transition.end, $.proxy(complete, this))
23746      .emulateTransitionEnd(350)
23747  }
23748
23749  Collapse.prototype.toggle = function () {
23750    this[this.$element.hasClass('in') ? 'hide' : 'show']()
23751  }
23752
23753
23754  // COLLAPSE PLUGIN DEFINITION
23755  // ==========================
23756
23757  var old = $.fn.collapse
23758
23759  $.fn.collapse = function (option) {
23760    return this.each(function () {
23761      var $this   = $(this)
23762      var data    = $this.data('bs.collapse')
23763      var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
23764
23765      if (!data && options.toggle && option == 'show') option = !option
23766      if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
23767      if (typeof option == 'string') data[option]()
23768    })
23769  }
23770
23771  $.fn.collapse.Constructor = Collapse
23772
23773
23774  // COLLAPSE NO CONFLICT
23775  // ====================
23776
23777  $.fn.collapse.noConflict = function () {
23778    $.fn.collapse = old
23779    return this
23780  }
23781
23782
23783  // COLLAPSE DATA-API
23784  // =================
23785
23786  $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
23787    var $this   = $(this), href
23788    var target  = $this.attr('data-target')
23789        || e.preventDefault()
23790        || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
23791    //Edited by Uncode to replace ID in panel [START]
23792    var _target = href.replace(/^#/, "");
23793    if ( $('[data-id="' + _target + '"]').length ) {
23794      var $target = $('[data-id="' + _target + '"]')
23795    } else {
23796      var $target = $(target)
23797    }
23798    //Edited by Uncode to replace ID in panel [END]
23799    var data    = $target.data('bs.collapse')
23800    var option  = data ? 'toggle' : $this.data()
23801    var parent  = $this.attr('data-parent')
23802    var $parent = parent && $(parent)
23803
23804    if (!data || !data.transitioning) {
23805      if ($parent) $parent.find('[data-toggle="collapse"][data-parent="' + parent + '"]').not($this).addClass('collapsed')
23806      $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
23807    }
23808
23809    $target.collapse(option)
23810  })
23811
23812}(jQuery);
23813
23814/* ========================================================================
23815 * Bootstrap: tab.js v3.1.1
23816 * http://getbootstrap.com/javascript/#tabs
23817 * ========================================================================
23818 * Copyright 2011-2014 Twitter, Inc.
23819 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
23820 * ======================================================================== */
23821
23822
23823+function ($) {
23824  'use strict';
23825
23826  // TAB CLASS DEFINITION
23827  // ====================
23828
23829  var Tab = function (element) {
23830    this.element = $(element)
23831  }
23832
23833  Tab.prototype.show = function () {
23834    var $this    = this.element
23835    var $ul      = $this.closest('ul:not(.dropdown-menu)')
23836    var selector = $this.data('target')
23837
23838    if (!selector) {
23839      selector = $this.attr('href')
23840      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
23841    }
23842
23843    if ($this.parent('li').hasClass('active')) return
23844
23845    var previous = $ul.find('.active:last a')[0]
23846    var e        = $.Event('show.bs.tab', {
23847      relatedTarget: previous
23848    })
23849
23850    $this.trigger(e)
23851
23852    if (e.isDefaultPrevented()) return
23853
23854    //Edited by Uncode to replace ID in panel [START]
23855    var _target = selector.replace(/^#/, "");
23856    if ( $('[data-id="' + _target + '"]').length )
23857      var $target = $('[data-id="' + _target + '"]')
23858    else
23859      var $target = $(selector)
23860    //Edited by Uncode to replace ID in panel [END]
23861
23862    this.activate($this.parent('li'), $ul)
23863    this.activate($target, $target.parent(), function () {
23864      $this.trigger({
23865        type: 'shown.bs.tab',
23866        relatedTarget: previous
23867      })
23868    })
23869  }
23870
23871  Tab.prototype.activate = function (element, container, callback) {
23872    var $active    = container.find('> .active')
23873    var transition = callback
23874      && $.support.transition
23875      && $active.hasClass('fade')
23876
23877    function next() {
23878      $active
23879        .removeClass('active')
23880        .find('> .dropdown-menu > .active')
23881        .removeClass('active')
23882
23883      element.addClass('active')
23884
23885      if (transition) {
23886        element[0].offsetWidth // reflow for transition
23887        element.addClass('in')
23888      } else {
23889        element.removeClass('fade')
23890      }
23891
23892      if (element.parent('.dropdown-menu')) {
23893        element.closest('li.dropdown').addClass('active')
23894      }
23895
23896      callback && callback()
23897    }
23898
23899    transition ?
23900      $active
23901        .one($.support.transition.end, next)
23902        .emulateTransitionEnd(150) :
23903      next()
23904
23905    $active.removeClass('in')
23906  }
23907
23908
23909  // TAB PLUGIN DEFINITION
23910  // =====================
23911
23912  var old = $.fn.tab
23913
23914  $.fn.tab = function ( option ) {
23915    return this.each(function () {
23916      var $this = $(this)
23917      var data  = $this.data('bs.tab')
23918
23919      if (!data) $this.data('bs.tab', (data = new Tab(this)))
23920      if (typeof option == 'string') data[option]()
23921    })
23922  }
23923
23924  $.fn.tab.Constructor = Tab
23925
23926
23927  // TAB NO CONFLICT
23928  // ===============
23929
23930  $.fn.tab.noConflict = function () {
23931    $.fn.tab = old
23932    return this
23933  }
23934
23935
23936  // TAB DATA-API
23937  // ============
23938
23939  $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
23940    e.preventDefault()
23941    $(this).tab('show')
23942  })
23943
23944}(jQuery);
23945
23946/* ========================================================================
23947 * Bootstrap: tooltip.js v3.1.1
23948 * http://getbootstrap.com/javascript/#tooltip
23949 * Inspired by the original jQuery.tipsy by Jason Frame
23950 * ========================================================================
23951 * Copyright 2011-2014 Twitter, Inc.
23952 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
23953 * ======================================================================== */
23954
23955
23956+function ($) {
23957  'use strict';
23958
23959  // TOOLTIP PUBLIC CLASS DEFINITION
23960  // ===============================
23961
23962  var Tooltip = function (element, options) {
23963    this.type       =
23964    this.options    =
23965    this.enabled    =
23966    this.timeout    =
23967    this.hoverState =
23968    this.$element   = null
23969
23970    this.init('tooltip', element, options)
23971  }
23972
23973  Tooltip.DEFAULTS = {
23974    animation: true,
23975    placement: 'top',
23976    selector: false,
23977    template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
23978    trigger: 'hover focus',
23979    title: '',
23980    delay: 0,
23981    html: false,
23982    container: false,
23983    viewport: {
23984      selector: 'body',
23985      padding: 0
23986    }
23987  }
23988
23989  Tooltip.prototype.init = function (type, element, options) {
23990    this.enabled   = true
23991    this.type      = type
23992    this.$element  = $(element)
23993    this.options   = this.getOptions(options)
23994    this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)
23995
23996    var triggers = this.options.trigger.split(' ')
23997
23998    for (var i = triggers.length; i--;) {
23999      var trigger = triggers[i]
24000
24001      if (trigger == 'click') {
24002        this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
24003      } else if (trigger != 'manual') {
24004        var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
24005        var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
24006
24007        this.$element.on(eventIn  + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
24008        this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
24009      }
24010    }
24011
24012    this.options.selector ?
24013      (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
24014      this.fixTitle()
24015  }
24016
24017  Tooltip.prototype.getDefaults = function () {
24018    return Tooltip.DEFAULTS
24019  }
24020
24021  Tooltip.prototype.getOptions = function (options) {
24022    options = $.extend({}, this.getDefaults(), this.$element.data(), options)
24023
24024    if (options.delay && typeof options.delay == 'number') {
24025      options.delay = {
24026        show: options.delay,
24027        hide: options.delay
24028      }
24029    }
24030
24031    return options
24032  }
24033
24034  Tooltip.prototype.getDelegateOptions = function () {
24035    var options  = {}
24036    var defaults = this.getDefaults()
24037
24038    this._options && $.each(this._options, function (key, value) {
24039      if (defaults[key] != value) options[key] = value
24040    })
24041
24042    return options
24043  }
24044
24045  Tooltip.prototype.enter = function (obj) {
24046    var self = obj instanceof this.constructor ?
24047      obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
24048
24049    clearTimeout(self.timeout)
24050
24051    self.hoverState = 'in'
24052
24053    if (!self.options.delay || !self.options.delay.show) return self.show()
24054
24055    self.timeout = setTimeout(function () {
24056      if (self.hoverState == 'in') self.show()
24057    }, self.options.delay.show)
24058  }
24059
24060  Tooltip.prototype.leave = function (obj) {
24061    var self = obj instanceof this.constructor ?
24062      obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
24063
24064    clearTimeout(self.timeout)
24065
24066    self.hoverState = 'out'
24067
24068    if (!self.options.delay || !self.options.delay.hide) return self.hide()
24069
24070    self.timeout = setTimeout(function () {
24071      if (self.hoverState == 'out') self.hide()
24072    }, self.options.delay.hide)
24073  }
24074
24075  Tooltip.prototype.show = function () {
24076    var e = $.Event('show.bs.' + this.type)
24077
24078    if (this.hasContent() && this.enabled) {
24079      this.$element.trigger(e)
24080
24081      if (e.isDefaultPrevented()) return
24082      var that = this;
24083
24084      var $tip = this.tip()
24085
24086      this.setContent()
24087
24088      if (this.options.animation) $tip.addClass('fade')
24089
24090      var placement = typeof this.options.placement == 'function' ?
24091        this.options.placement.call(this, $tip[0], this.$element[0]) :
24092        this.options.placement
24093
24094      var autoToken = /\s?auto?\s?/i
24095      var autoPlace = autoToken.test(placement)
24096      if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
24097
24098      $tip
24099        .detach()
24100        .css({ top: 0, left: 0, display: 'block' })
24101        .addClass(placement)
24102
24103      this.options.container ? $tip.appendTo($(document).find(this.options.container)) : $tip.insertAfter(this.$element)
24104
24105      var pos          = this.getPosition()
24106      var actualWidth  = $tip[0].offsetWidth
24107      var actualHeight = $tip[0].offsetHeight
24108
24109      if (autoPlace) {
24110        var orgPlacement = placement
24111        var $parent      = this.$element.parent()
24112        var parentDim    = this.getPosition($parent)
24113
24114        placement = placement == 'bottom' && pos.top   + pos.height       + actualHeight - parentDim.scroll > parentDim.height ? 'top'    :
24115                    placement == 'top'    && pos.top   - parentDim.scroll - actualHeight < 0                                   ? 'bottom' :
24116                    placement == 'right'  && pos.right + actualWidth      > parentDim.width                                    ? 'left'   :
24117                    placement == 'left'   && pos.left  - actualWidth      < parentDim.left                                     ? 'right'  :
24118                    placement
24119
24120        $tip
24121          .removeClass(orgPlacement)
24122          .addClass(placement)
24123      }
24124
24125      var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
24126
24127      this.applyPlacement(calculatedOffset, placement)
24128      this.hoverState = null
24129
24130      var complete = function() {
24131        that.$element.trigger('shown.bs.' + that.type)
24132      }
24133
24134      $.support.transition && this.$tip.hasClass('fade') ?
24135        $tip
24136          .one($.support.transition.end, complete)
24137          .emulateTransitionEnd(150) :
24138        complete()
24139    }
24140  }
24141
24142  Tooltip.prototype.applyPlacement = function (offset, placement) {
24143    var $tip   = this.tip()
24144    var width  = $tip[0].offsetWidth
24145    var height = $tip[0].offsetHeight
24146
24147    // manually read margins because getBoundingClientRect includes difference
24148    var marginTop = parseInt($tip.css('margin-top'), 10)
24149    var marginLeft = parseInt($tip.css('margin-left'), 10)
24150
24151    // we must check
24151 for NaN for ie 8/9
24152    if (isNaN(marginTop))  marginTop  = 0
24153    if (isNaN(marginLeft)) marginLeft = 0
24154
24155    offset.top  = offset.top  + marginTop
24156    offset.left = offset.left + marginLeft
24157
24158    // $.fn.offset doesn't round pixel values
24159    // so we use setOffset directly with our own function B-0
24160    $.offset.setOffset($tip[0], $.extend({
24161      using: function (props) {
24162        $tip.css({
24163          top: Math.round(props.top),
24164          left: Math.round(props.left)
24165        })
24166      }
24167    }, offset), 0)
24168
24169    $tip.addClass('in')
24170
24171    // check to see if placing tip in new offset caused the tip to resize itself
24172    var actualWidth  = $tip[0].offsetWidth
24173    var actualHeight = $tip[0].offsetHeight
24174
24175    if (placement == 'top' && actualHeight != height) {
24176      offset.top = offset.top + height - actualHeight
24177    }
24178
24179    var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
24180
24181    if (delta.left) offset.left += delta.left
24182    else offset.top += delta.top
24183
24184    var arrowDelta          = delta.left ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
24185    var arrowPosition       = delta.left ? 'left'        : 'top'
24186    var arrowOffsetPosition = delta.left ? 'offsetWidth' : 'offsetHeight'
24187
24188    $tip.offset(offset)
24189    this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], arrowPosition)
24190  }
24191
24192  Tooltip.prototype.replaceArrow = function (delta, dimension, position) {
24193    this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '')
24194  }
24195
24196  Tooltip.prototype.setContent = function () {
24197    var $tip  = this.tip()
24198    var title = this.getTitle()
24199
24200    $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
24201    $tip.removeClass('fade in top bottom left right')
24202  }
24203
24204  Tooltip.prototype.hide = function () {
24205    var that = this
24206    var $tip = this.tip()
24207    var e    = $.Event('hide.bs.' + this.type)
24208
24209    function complete() {
24210      if (that.hoverState != 'in') $tip.detach()
24211      that.$element.trigger('hidden.bs.' + that.type)
24212    }
24213
24214    this.$element.trigger(e)
24215
24216    if (e.isDefaultPrevented()) return
24217
24218    $tip.removeClass('in')
24219
24220    $.support.transition && this.$tip.hasClass('fade') ?
24221      $tip
24222        .one($.support.transition.end, complete)
24223        .emulateTransitionEnd(150) :
24224      complete()
24225
24226    this.hoverState = null
24227
24228    return this
24229  }
24230
24231  Tooltip.prototype.fixTitle = function () {
24232    var $e = this.$element
24233    if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
24234      $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
24235    }
24236  }
24237
24238  Tooltip.prototype.hasContent = function () {
24239    return this.getTitle()
24240  }
24241
24242  Tooltip.prototype.getPosition = function ($element) {
24243    $element   = $element || this.$element
24244    var el     = $element[0]
24245    var isBody = el.tagName == 'BODY'
24246    return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : null, {
24247      scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop(),
24248      width:  isBody ? $(window).width()  : $element.outerWidth(),
24249      height: isBody ? $(window).height() : $element.outerHeight()
24250    }, isBody ? {top: 0, left: 0} : $element.offset())
24251  }
24252
24253  Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
24254    return placement == 'bottom' ? { top: pos.top + pos.height,   left: pos.left + pos.width / 2 - actualWidth / 2  } :
24255           placement == 'top'    ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2  } :
24256           placement == 'left'   ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
24257        /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width   }
24258
24259  }
24260
24261  Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
24262    var delta = { top: 0, left: 0 }
24263    if (!this.$viewport) return delta
24264
24265    var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
24266    var viewportDimensions = this.getPosition(this.$viewport)
24267
24268    if (/right|left/.test(placement)) {
24269      var topEdgeOffset    = pos.top - viewportPadding - viewportDimensions.scroll
24270      var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
24271      if (topEdgeOffset < viewportDimensions.top) { // top overflow
24272        delta.top = viewportDimensions.top - topEdgeOffset
24273      } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
24274        delta.top = viewportDimensions.top + viewportDimensions.height - bottom
24274EdgeOffset
24275      }
24276    } else {
24277      var leftEdgeOffset  = pos.left - viewportPadding
24278      var rightEdgeOffset = pos.left + viewportPadding + actualWidth
24279      if (leftEdgeOffset < viewportDimensions.left) { // left overflow
24280        delta.left = viewportDimensions.left - leftEdgeOffset
24281      } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
24282        delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
24283      }
24284    }
24285
24286    return delta
24287  }
24288
24289  Tooltip.prototype.getTitle = function () {
24290    var title
24291    var $e = this.$element
24292    var o  = this.options
24293
24294    title = $e.attr('data-original-title')
24295      || (typeof o.title == 'function' ? o.title.call($e[0]) :  o.title)
24296
24297    return title
24298  }
24299
24300  Tooltip.prototype.tip = function () {
24301    return this.$tip = this.$tip || $(this.options.template)
24302  }
24303
24304  Tooltip.prototype.arrow = function () {
24305    return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')
24306  }
24307
24308  Tooltip.prototype.validate = function () {
24309    if (!this.$element[0].parentNode) {
24310      this.hide()
24311      this.$element = null
24312      this.options  = null
24313    }
24314  }
24315
24316  Tooltip.prototype.enable = function () {
24317    this.enabled = true
24318  }
24319
24320  Tooltip.prototype.disable = function () {
24321    this.enabled = false
24322  }
24323
24324  Tooltip.prototype.toggleEnabled = function () {
24325    this.enabled = !this.enabled
24326  }
24327
24328  Tooltip.prototype.toggle = function (e) {
24329    var self = e ? $(e.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) : this
24330    self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
24331  }
24332
24333  Tooltip.prototype.destroy = function () {
24334    clearTimeout(this.timeout)
24335    this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
24336  }
24337
24338
24339  // TOOLTIP PLUGIN DEFINITION
24340  // =========================
24341
24342  var old = $.fn.tooltip
24343
24344  $.fn.tooltip = function (option) {
24345    return this.each(function () {
24346      var $this   = $(this)
24347      var data    = $this.data('bs.tooltip')
24348      var options = typeof option == 'object' && option
24349
24350      if (!data && option == 'destroy') return
24351      if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
24352      if (typeof option == 'string') data[option]()
24353    })
24354  }
24355
24356  $.fn.tooltip.Constructor = Tooltip
24357
24358
24359  // TOOLTIP NO CONFLICT
24360  // ===================
24361
24362  $.fn.tooltip.noConflict = function () {
24363    $.fn.tooltip = old
24364    return this
24365  }
24366
24367}(jQuery);
24368
24369/* ========================================================================
24370 * Bootstrap: transition.js v3.1.1
24371 * http://getbootstrap.com/javascript/#transitions
24372 * ========================================================================
24373 * Copyright 2011-2014 Twitter, Inc.
24374 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
24375 * ======================================================================== */
24376
24377
24378+function ($) {
24379  'use strict';
24380
24381  // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
24382  // ============================================================
24383
24384  function transitionEnd() {
24385    var el = document.createElement('bootstrap')
24386
24387    var transEndEventNames = {
24388      WebkitTransition : 'webkitTransitionEnd',
24389      MozTransition    : 'transitionend',
24390      OTransition      : 'oTransitionEnd otransitionend',
24391      transition       : 'transitionend'
24392    }
24393
24394    for (var name in transEndEventNames) {
24395      if (el.style[name] !== undefined) {
24396        return { end: transEndEventNames[name] }
24397      }
24398    }
24399
24400    return false // explicit for ie8 (  ._.)
24401  }
24402
24403  // http://blog.alexmaccaw.com/css-transitions
24404  $.fn.emulateTransitionEnd = function (duration) {
24405    var called = false, $el = this
24406    $(this).one($.support.transition.end, function () { called = true })
24407    var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
24408    setTimeout(callback, duration)
24409    return this
24410  }
24411
24412  $(function () {
24413    $.support.transition = transitionEnd()
24414  })
24415
24416}(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.