PageSourceSearch

https://www.youwillloveeachother.com/wp-content/themes/Uncode/uncode/library/js/plugins.js?ver=1701814458

js youwillloveeachother.com collected 2026-09-26 04:41:18 UTC 786,745 bytes, 22,832 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);
291
292/*!
293 * Isotope PACKAGED v2.2.2
294 *
295 * Licensed GPLv3 for open source use
296 * or Isotope Commercial License for commercial use
297 *
298 * http://isotope.metafizzy.co
299 * Copyright 2015 Metafizzy
300 */
301
302/**
303 * Bridget makes jQuery widgets
304 * v1.1.0
305 * MIT license
306 */
307
308( function( window ) {
309
310
311
312// -------------------------- utils -------------------------- //
313
314var slice = Array.prototype.slice;
315
316function noop() {}
317
318// -------------------------- definition -------------------------- //
319
320function defineBridget( $ ) {
321
322// bail if no jQuery
323if ( !$ ) {
324  return;
325}
326
327// -------------------------- addOptionMethod -------------------------- //
328
329/**
330 * adds option method -> $().plugin('option', {...})
331 * @param {Function} PluginClass - constructor class
332 */
333function addOptionMethod( PluginClass ) {
334  // don't overwrite original option method
335  if ( PluginClass.prototype.option ) {
336    return;
337  }
338
339  // option setter
340  PluginClass.prototype.option = function( opts ) {
341    // bail out if not an object
342    if ( !$.isPlainObject( opts ) ){
343      return;
344    }
345    this.options = $.extend( true, this.options, opts );
346  };
347}
348
349// -------------------------- plugin bridge -------------------------- //
350
351// helper function for logging errors
352// $.error breaks jQuery chaining
353var logError = typeof console === 'undefined' ? noop :
354  function( message ) {
355    console.error( message );
356  };
357
358/**
359 * jQuery plugin bridge, access methods like $elem.plugin('method')
360 * @param {String} namespace - plugin name
361 * @param {Function} PluginClass - constructor class
362 */
363function bridge( namespace, PluginClass ) {
364  // add to jQuery fn namespace
365  $.fn[ namespace ] = function( options ) {
366    if ( typeof options === 'string' ) {
367      // call plugin method when first argument is a string
368      // get arguments for method
369      var args = slice.call( arguments, 1 );
370
371      for ( var i=0, len = this.length; i < len; i++ ) {
372        var elem = this[i];
373        var instance = $.data( elem, namespace );
374        if ( !instance ) {
375          logError( "cannot call methods on " + namespace + " prior to initialization; " +
376            "attempted to call '" + options + "'" );
377          continue;
378        }
379        if ( !$.isFunction( instance[options] ) || options.charAt(0) === '_' ) {
380          logError( "no such method '" + options + "' for " + namespace + " instance" );
381          continue;
382        }
383
384        // trigger method with arguments
385        var returnValue = instance[ options ].apply( instance, args );
386
387        // break look and return first value if provided
388        if ( returnValue !== undefined ) {
389          return returnValue;
390        }
391      }
392      // return this if no return value
393      return this;
394    } else {
395      return this.each( function() {
396        var instance = $.data( this, namespace );
397        if ( instance ) {
398          // apply options & init
399          instance.option( options );
400          instance._init();
401        } else {
402          // initialize new instance
403          instance = new PluginClass( this, options );
404          $.data( this, namespace, instance );
405        }
406      });
407    }
408  };
409
410}
411
412// -------------------------- bridget -------------------------- //
413
414/**
415 * converts a Prototypical class into a proper jQuery plugin
416 *   the class must have a ._init method
417 * @param {String} namespace - plugin name, used in $().pluginName
418 * @param {Function} PluginClass - constructor class
419 */
420$.bridget = function( namespace, PluginClass ) {
421  addOptionMethod( PluginClass );
422  bridge( namespace, PluginClass );
423};
424
425return $.bridget;
426
427}
428
429// transport
430if ( typeof define === 'function' && define.amd ) {
431  // AMD
432  define( 'jquery-bridget/jquery.bridget',[ 'jquery' ], defineBridget );
433} else if ( typeof exports === 'object' ) {
434  defineBridget( require('jquery') );
435} else {
436  // get jquery from browser global
437  defineBridget( window.jQuery );
438}
439
440})( window );
441
442/*!
443 * eventie v1.0.6
444 * event binding helper
445 *   eventie.bind( elem, 'click', myFn )
446 *   eventie.unbind( elem, 'click', myFn )
447 * MIT license
448 */
449
450/*jshint browser: true, undef: true, unused: true */
451/*global define: false, module: false */
452
453( function( window ) {
454
455
456
457var docElem = document.documentElement;
458
459var bind = function() {};
460
461function getIEEvent( obj ) {
462  var event = window.event;
463  // add event.target
464  event.target = event.target || event.srcElement || obj;
465  return event;
466}
467
468if ( docElem.addEventListener ) {
469  bind = function( obj, type, fn ) {
470    obj.addEventListener( type, fn, false );
471  };
472} else if ( docElem.attachEvent ) {
473  bind = function( obj, type, fn ) {
474    obj[ type + fn ] = fn.handleEvent ?
475      function() {
476        var event = getIEEvent( obj );
477        fn.handleEvent.call( fn, event );
478      } :
479      function() {
480        var event = getIEEvent( obj );
481        fn.call( obj, event );
482      };
483    obj.attachEvent( "on" + type, obj[ type + fn ] );
484  };
485}
486
487var unbind = function() {};
488
489if ( docElem.removeEventListener ) {
490  unbind = function( obj, type, fn ) {
491    obj.removeEventListener( type, fn, false );
492  };
493} else if ( docElem.detachEvent ) {
494  unbind = function( obj, type, fn ) {
495    obj.detachEvent( "on" + type, obj[ type + fn ] );
496    try {
497      delete obj[ type + fn ];
498    } catch ( err ) {
499      // can't delete window object properties
500      obj[ type + fn ] = undefined;
501    }
502  };
503}
504
505var eventie = {
506  bind: bind,
507  unbind: unbind
508};
509
510// ----- module definition ----- //
511
512if ( typeof define === 'function' && define.amd ) {
513  // AMD
514  define( 'eventie/eventie',eventie );
515} else if ( typeof exports === 'object' ) {
516  // CommonJS
517  module.exports = eventie;
518} else {
519  // browser global
520  window.eventie = eventie;
521}
522
523})( window );
524
525/*!
526 * EventEmitter v4.2.11 - git.io/ee
527 * Unlicense - http://unlicense.org/
528 * Oliver Caldwell - http://oli.me.uk/
529 * @preserve
530 */
531
532;(function () {
533    'use strict';
534
535    /**
536     * Class for managing events.
537     * Can be extended to provide event functionality in other classes.
538     *
539     * @class EventEmitter Manages event registering and emitting.
540     */
541    function EventEmitter() {}
542
543    // Shortcuts to improve speed and size
544    var proto = EventEmitter.prototype;
545    var exports = this;
546    var originalGlobalValue = exports.EventEmitter;
547
548    /**
549     * Finds the index of the listener for the event in its storage array.
550     *
551     * @param {Function[]} listeners Array of listeners to search through.
552     * @param {Function} listener Method to look for.
553     * @return {Number} Index of the specified listener, -1 if not found
554     * @api private
555     */
556    function indexOfListener(listeners, listener) {
557        var i = listeners.length;
558        while (i--) {
559            if (listeners[i].listener === listener) {
560                return i;
561            }
562        }
563
564        return -1;
565    }
566
567    /**
568     * Alias a method while keeping the context correct, to allow for over
vendor: 16,384 bytes, lines 568-971
568writing of target method.
569     *
570     * @param {String} name The name of the target method.
571     * @return {Function} The aliased method
572     * @api private
573     */
574    function alias(name) {
575        return function aliasClosure() {
576            return this[name].apply(this, arguments);
577        };
578    }
579
580    /**
581     * Returns the listener array for the specified event.
582     * Will initialise the event object and listener arrays if required.
583     * Will return an object if you use a regex search. The object contains keys for each matched event. So /ba[rz]/ might return an object containing bar and baz. But only if you have either defined them with defineEvent or added some listeners to them.
584     * Each property in the object response is an array of listener functions.
585     *
586     * @param {String|RegExp} evt Name of the event to return the listeners from.
587     * @return {Function[]|Object} All listener functions for the event.
588     */
589    proto.getListeners = function getListeners(evt) {
590        var events = this._getEvents();
591        var response;
592        var key;
593
594        // Return a concatenated array of all matching events if
595        // the selector is a regular expression.
596        if (evt instanceof RegExp) {
597            response = {};
598            for (key in events) {
599                if (events.hasOwnProperty(key) && evt.test(key)) {
600                    response[key] = events[key];
601                }
602            }
603        }
604        else {
605            response = events[evt] || (events[evt] = []);
606        }
607
608        return response;
609    };
610
611    /**
612     * Takes a list of listener objects and flattens it into a list of listener functions.
613     *
614     * @param {Object[]} listeners Raw listener objects.
615     * @return {Function[]} Just the listener functions.
616     */
617    proto.flattenListeners = function flattenListeners(listeners) {
618        var flatListeners = [];
619        var i;
620
621        for (i = 0; i < listeners.length; i += 1) {
622            flatListeners.push(listeners[i].listener);
623        }
624
625        return flatListeners;
626    };
627
628    /**
629     * Fetches the requested listeners via getListeners but will always return the results inside an object. This is mainly for internal use but others may find it useful.
630     *
631     * @param {String|RegExp} evt Name of the event to return the listeners from.
632     * @return {Object} All listener functions for an event in an object.
633     */
634    proto.getListenersAsObject = function getListenersAsObject(evt) {
635        var listeners = this.getListeners(evt);
636        var response;
637
638        if (listeners instanceof Array) {
639            response = {};
640            response[evt] = listeners;
641        }
642
643        return response || listeners;
644    };
645
646    /**
647     * Adds a listener function to the specified event.
648     * The listener will not be added if it is a duplicate.
649     * If the listener returns true then it will be removed after it is called.
650     * If you pass a regular expression as the event name then the listener will be added to all events that match it.
651     *
652     * @param {String|RegExp} evt Name of the event to attach the listener to.
653     * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
654     * @return {Object} Current instance of EventEmitter for chaining.
655     */
656    proto.addListener = function addListener(evt, listener) {
657        var listeners = this.getListenersAsObject(evt);
658        var listenerIsWrapped = typeof listener === 'object';
659        var key;
660
661        for (key in listeners) {
662            if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) {
663                listeners[key].push(listenerIsWrapped ? listener : {
664                    listener: listener,
665                    once: false
666                });
667            }
668        }
669
670        return this;
671    };
672
673    /**
674     * Alias of addListener
675     */
676    proto.on = alias('addListener');
677
678    /**
679     * Semi-alias of addListener. It will add a listener that will be
680     * automatically removed after its first execution.
681     *
682     * @param {String|RegExp} evt Name of the event to attach the listener to.
683     * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
684     * @return {Object} Current instance of EventEmitter for chaining.
685     */
686    proto.addOnceListener = function addOnceListener(evt, listener) {
687        return this.addListener(evt, {
688            listener: listener,
689            once: true
690        });
691    };
692
693    /**
694     * Alias of addOnceListener.
695     */
696    proto.once = alias('addOnceListener');
697
698    /**
699     * Defines an event name. This is required if you want to use a regex to add a listener to multiple events at once. If you don't do this then how do you expect it to know what event to add to? Should it just add to every possible match for a regex? No. That is scary and bad.
700     * You need to tell it what event names should be matched by a regex.
701     *
702     * @param {String} evt Name of the event to create.
703     * @return {Object} Current instance of EventEmitter for chaining.
704     */
705    proto.defineEvent = function defineEvent(evt) {
706        this.getListeners(evt);
707        return this;
708    };
709
710    /**
711     * Uses defineEvent to define multiple events.
712     *
713     * @param {String[]} evts An array of event names to define.
714     * @return {Object} Current instance of EventEmitter for chaining.
715     */
716    proto.defineEvents = function defineEvents(evts) {
717        for (var i = 0; i < evts.length; i += 1) {
718            this.defineEvent(evts[i]);
719        }
720        return this;
721    };
722
723    /**
724     * Removes a listener function from the specified event.
725     * When passed a regular expression as the event name, it will remove the listener from all events that match it.
726     *
727     * @param {String|RegExp} evt Name of the event to remove the listener from.
728     * @param {Function} listener Method to remove from the event.
729     * @return {Object} Current instance of EventEmitter for chaining.
730     */
731    proto.removeListener = function removeListener(evt, listener) {
732        var listeners = this.getListenersAsObject(evt);
733        var index;
734        var key;
735
736        for (key in listeners) {
737            if (listeners.hasOwnProperty(key)) {
738                index = indexOfListener(listeners[key], listener);
739
740                if (index !== -1) {
741                    listeners[key].splice(index, 1);
742                }
743            }
744        }
745
746        return this;
747    };
748
749    /**
750     * Alias of removeListener
751     */
752    proto.off = alias('removeListener');
753
754    /**
755     * Adds listeners in bulk using the manipulateListeners method.
756     * If you pass an object as the second argument you can add to multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. You can also pass it an event name and an array of listeners to be added.
757     * You can also pass it a regular expression to add the array of listeners to all events that match it.
758     * Yeah, this function does quite a bit. That's probably a bad thing.
759     *
760     * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add to multiple events at once.
761     * @param {Function[]} [listeners] An optional array of listener functions to add.
762     * @return {Object} Current instance of EventEmitter for chaining.
763     */
764    proto.addListeners = function addListeners(evt, listeners) {
765        // Pass through to manipulateListeners
766        return this.manipulateListeners(false, evt, listeners);
767    };
768
769    /**
770     * Removes listeners in bulk using the manipulateListeners method.
771     * If you pass an object as the second argument you can remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
772     * You can also pass it an event name and an array of listeners to be removed.
773     * You can also pass it a regular expression to remove the listeners from all events that match it.
774     *
775     * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to remove from multiple events at once.
776     * @param {Function[]} [listeners] An optional array of listener functions to remove.
777     * @return {Object} Current instance of EventEmitter for chaining.
778     */
779    proto.removeListeners = function removeListeners(evt, listeners) {
780        // Pass through to manipulateListeners
781        return this.manipulateListeners(true, evt, listeners);
782    };
783
784    /**
785     * Edits listeners in bulk. The addListeners and removeListeners methods both use this to do their job. You should really use those instead, this is a little lower level.
786     * The first argument will determine if the listeners are removed (true) or added (false).
787     * If you pass an object as the second argument you can add/remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays.
788     * You can also pass it an event name and an array of listeners to be added/removed.
789     * You can also pass it a regular expression to manipulate the listeners of all events that match it.
790     *
791     * @param {Boolean} remove True if you want to remove listeners, false if you want to add.
792     * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add/remove from multiple events at once.
793     * @param {Function[]} [listeners] An optional array of listener functions to add/remove.
794     * @return {Object} Current instance of EventEmitter for chaining.
795     */
796    proto.manipulateListeners = function manipulateListeners(remove, evt, listeners) {
797        var i;
798        var value;
799        var single = remove ? this.removeListener : this.addListener;
800        var multiple = remove ? this.removeListeners : this.addListeners;
801
802        // If evt is an object then pass each of its properties to this method
803        if (typeof evt === 'object' && !(evt instanceof RegExp)) {
804            for (i in evt) {
805                if (evt.hasOwnProperty(i) && (value = evt[i])) {
806                    // Pass the single listener straight through to the singular method
807                    if (typeof value === 'function') {
808                        single.call(this, i, value);
809                    }
810                    else {
811                        // Otherwise pass back to the multiple function
812                        multiple.call(this, i, value);
813                    }
814                }
815            }
816        }
817        else {
818            // So evt must be a string
819            // And listeners must be an array of listeners
820            // Loop over it and pass each one to the multiple method
821            i = listeners.length;
822            while (i--) {
823                single.call(this, evt, listeners[i]);
824            }
825        }
826
827        return this;
828    };
829
830    /**
831     * Removes all listeners from a specified event.
832     * If you do not specify an event then all listeners will be removed.
833     * That means every event will be emptied.
834     * You can also pass a regex to remove all events that match it.
835     *
836     * @param {String|RegExp} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed.
837     * @return {Object} Current instance of EventEmitter for chaining.
838     */
839    proto.removeEvent = function removeEvent(evt) {
840        var type = typeof evt;
841        var events = this._getEvents();
842        var key;
843
844        // Remove different things depending on the state of evt
845        if (type === 'string') {
846            // Remove all listeners for the specified event
847            delete events[evt];
848        }
849        else if (evt instanceof RegExp) {
850            // Remove all events matching the regex.
851            for (key in events) {
852                if (events.hasOwnProperty(key) && evt.test(key)) {
853                    delete events[key];
854                }
855            }
856        }
857        else {
858            // Remove all listeners in all events
859            delete this._events;
860        }
861
862        return this;
863    };
864
865    /**
866     * Alias of removeEvent.
867     *
868     * Added to mirror the node API.
869     */
870    proto.removeAllListeners = alias('removeEvent');
871
872    /**
873     * Emits an event of your choice.
874     * When emitted, every listener attached to that event will be executed.
875     * If you pass the optional argument array then those arguments will be passed to every listener upon execution.
876     * Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately.
877     * So they will not arrive within the array on the other side, they will be separate.
878     * You can also pass a regular expression to emit to all events that match it.
879     *
880     * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
881     * @param {Array} [args] Optional array of arguments to be passed to each listener.
882     * @return {Object} Current instance of EventEmitter for chaining.
883     */
884    proto.emitEvent = function emitEvent(evt, args) {
885        var listeners = this.getListenersAsObject(evt);
886        var listener;
887        var i;
888        var key;
889        var response;
890
891        for (key in listeners) {
892            if (listeners.hasOwnProperty(key)) {
893                i = listeners[key].length;
894
895                while (i--) {
896                    // If the listener returns true then it shall be removed from the event
897                    // The function is executed either with a basic call or an apply if there is an args array
898                    listener = listeners[key][i];
899
900                    if (listener.once === true) {
901                        this.removeListener(evt, listener.listener);
902                    }
903
904                    response = listener.listener.apply(this, args || []);
905
906                    if (response === this._getOnceReturnValue()) {
907                        this.removeListener(evt, listener.listener);
908                    }
909                }
910            }
911        }
912
913        return this;
914    };
915
916    /**
917     * Alias of emitEvent
918     */
919    proto.trigger = alias('emitEvent');
920
921    /**
922     * Subtly different from emitEvent in that it will pass its arguments on to the listeners, as opposed to taking a single array of arguments to pass on.
923     * As with emitEvent, you can pass a regex in place of the event name to emit to all events that match it.
924     *
925     * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
926     * @param {...*} Optional additional arguments to be passed to each listener.
927     * @return {Object} Current instance of EventEmitter for chaining.
928     */
929    proto.emit = function emit(evt) {
930        var args = Array.prototype.slice.call(arguments, 1);
931        return this.emitEvent(evt, args);
932    };
933
934    /**
935     * Sets the current value to check against when executing listeners. If a
936     * listeners return value matches the one set here then it will be removed
937     * after execution. This value defaults to true.
938     *
939     * @param {*} value The new value to check for when executing listeners.
940     * @return {Object} Current instance of EventEmitter for chaining.
941     */
942    proto.setOnceReturnValue = function setOnceReturnValue(value) {
943        this._onceReturnValue = value;
944        return this;
945    };
946
947    /**
948     * Fetches the current value to check against when executing listeners. If
949     * the listeners return value matches this one then it should be removed
950     * automatically. It will return true by default.
951     *
952     * @return {*|Boolean} The current value to check for or the default, true.
953     * @api private
954     */
955    proto._getOnceReturnValue = function _getOnceReturnValue() {
956        if (this.hasOwnProperty('_onceReturnValue')) {
957            return this._onceReturnValue;
958        }
959        else {
960            return true;
961        }
962    };
963
964    /**
965     * Fetches the events object and creates one if required.
966     *
967     * @return {Object} The events storage object.
968     * @api private
969     */
970    proto._getEvents = function _getEvents() {
971        return this._events || (this
971._events = {});
972    };
973
974    /**
975     * Reverts the global {@link EventEmitter} to its previous value and returns a reference to this version.
976     *
977     * @return {Function} Non conflicting EventEmitter class.
978     */
979    EventEmitter.noConflict = function noConflict() {
980        exports.EventEmitter = originalGlobalValue;
981        return EventEmitter;
982    };
983
984    // Expose the class either via AMD, CommonJS or the global object
985    if (typeof define === 'function' && define.amd) {
986        define('eventEmitter/EventEmitter',[],function () {
987            return EventEmitter;
988        });
989    }
990    else if (typeof module === 'object' && module.exports){
991        module.exports = EventEmitter;
992    }
993    else {
994        exports.EventEmitter = EventEmitter;
995    }
996}.call(this));
997
998/*!
999 * getStyleProperty v1.0.4
1000 * original by kangax
1001 * http://perfectionkills.com/feature-testing-css-properties/
1002 * MIT license
1003 */
1004
1005/*jshint browser: true, strict: true, undef: true */
1006/*global define: false, exports: false, module: false */
1007
1008( function( window ) {
1009
1010
1011
1012var prefixes = 'Webkit Moz ms Ms O'.split(' ');
1013var docElemStyle = document.documentElement.style;
1014
1015function getStyleProperty( propName ) {
1016  if ( !propName ) {
1017    return;
1018  }
1019
1020  // test standard property first
1021  if ( typeof docElemStyle[ propName ] === 'string' ) {
1022    return propName;
1023  }
1024
1025  // capitalize
1026  propName = propName.charAt(0).toUpperCase() + propName.slice(1);
1027
1028  // test vendor specific properties
1029  var prefixed;
1030  for ( var i=0, len = prefixes.length; i < len; i++ ) {
1031    prefixed = prefixes[i] + propName;
1032    if ( typeof docElemStyle[ prefixed ] === 'string' ) {
1033      return prefixed;
1034    }
1035  }
1036}
1037
1038// transport
1039if ( typeof define === 'function' && define.amd ) {
1040  // AMD
1041  define( 'get-style-property/get-style-property',[],function() {
1042    return getStyleProperty;
1043  });
1044} else if ( typeof exports === 'object' ) {
1045  // CommonJS for Component
1046  module.exports = getStyleProperty;
1047} else {
1048  // browser global
1049  window.getStyleProperty = getStyleProperty;
1050}
1051
1052})( window );
1053
1054/*!
1055 * getSize v1.2.2
1056 * measure size of elements
1057 * MIT license
1058 */
1059
1060/*jshint browser: true, strict: true, undef: true, unused: true */
1061/*global define: false, exports: false, require: false, module: false, console: false */
1062
1063( function( window, undefined ) {
1064
1065
1066
1067// -------------------------- helpers -------------------------- //
1068
1069// get a number from a string, not a percentage
1070function getStyleSize( value ) {
1071  var num = parseFloat( value );
1072  // not a percent like '100%', and a number
1073  var isValid = value.indexOf('%') === -1 && !isNaN( num );
1074  return isValid && num;
1075}
1076
1077function noop() {}
1078
1079var logError = typeof console === 'undefined' ? noop :
1080  function( message ) {
1081    console.error( message );
1082  };
1083
1084// -------------------------- measurements -------------------------- //
1085
1086var measurements = [
1087  'paddingLeft',
1088  'paddingRight',
1089  'paddingTop',
1090  'paddingBottom',
1091  'marginLeft',
1092  'marginRight',
1093  'marginTop',
1094  'marginBottom',
1095  'borderLeftWidth',
1096  'borderRightWidth',
1097  'borderTopWidth',
1098  'borderBottomWidth'
1099];
1100
1101function getZeroSize() {
1102  var size = {
1103    width: 0,
1104    height: 0,
1105    innerWidth: 0,
1106    innerHeight: 0,
1107    outerWidth: 0,
1108    outerHeight: 0
1109  };
1110  for ( var i=0, len = measurements.length; i < len; i++ ) {
1111    var measurement = measurements[i];
1112    size[ measurement ] = 0;
1113  }
1114  return size;
1115}
1116
1117
1118
1119function defineGetSize( getStyleProperty ) {
1120
1121// -------------------------- setup -------------------------- //
1122
1123var isSetup = false;
1124
1125var getStyle, boxSizingProp, isBoxSizeOuter;
1126
1127/**
1128 * setup vars and functions
1129 * do it on initial getSize(), rather than on script load
1130 * For Firefox bug https://bugzilla.mozilla.org/show_bug.cgi?id=548397
1131 */
1132function setup() {
1133  // setup once
1134  if ( isSetup ) {
1135    return;
1136  }
1137  isSetup = true;
1138
1139  var getComputedStyle = window.getComputedStyle;
1140  getStyle = ( function() {
1141    var getStyleFn = getComputedStyle ?
1142      function( elem ) {
1143        return getComputedStyle( elem, null );
1144      } :
1145      function( elem ) {
1146        return elem.currentStyle;
1147      };
1148
1149      return function getStyle( elem ) {
1150        var style = getStyleFn( elem );
1151        if ( !style ) {
1152          logError( 'Style returned ' + style +
1153            '. Are you running this code in a hidden iframe on Firefox? ' +
1154            'See http://bit.ly/getsizebug1' );
1155        }
1156        return style;
1157      };
1158  })();
1159
1160  // -------------------------- box sizing -------------------------- //
1161
1162  boxSizingProp = getStyleProperty('boxSizing');
1163
1164  /**
1165   * WebKit measures the outer-width on style.width on border-box elems
1166   * IE & Firefox measures the inner-width
1167   */
1168  if ( boxSizingProp ) {
1169    var div = document.createElement('div');
1170    div.style.width = '200px';
1171    div.style.padding = '1px 2px 3px 4px';
1172    div.style.borderStyle = 'solid';
1173    div.style.borderWidth = '1px 2px 3px 4px';
1174    div.style[ boxSizingProp ] = 'border-box';
1175
1176    var body = document.body || document.documentElement;
1177    body.appendChild( div );
1178    var style = getStyle( div );
1179
1180    isBoxSizeOuter = getStyleSize( style.width ) === 200;
1181    body.removeChild( div );
1182  }
1183
1184}
1185
1186// -------------------------- getSize -------------------------- //
1187
1188function getSize( elem ) {
1189  setup();
1190
1191  // use querySeletor if elem is string
1192  if ( typeof elem === 'string' ) {
1193    elem = document.querySelector( elem );
1194  }
1195
1196  // do not proceed on non-objects
1197  if ( !elem || typeof elem !== 'object' || !elem.nodeType ) {
1198    return;
1199  }
1200
1201  var style = getStyle( elem );
1202
1203  // if hidden, everything is 0
1204  if ( style.display === 'none' ) {
1205    return getZeroSize();
1206  }
1207
1208  var size = {};
1209  size.width = elem.offsetWidth;
1210  size.height = elem.offsetHeight;
1211
1212  var isBorderBox = size.isBorderBox = !!( boxSizingProp &&
1213    style[ boxSizingProp ] && style[ boxSizingProp ] === 'border-box' );
1214
1215  // get all measurements
1216  for ( var i=0, len = measurements.length; i < len; i++ ) {
1217    var measurement = measurements[i];
1218    var value = style[ measurement ];
1219    value = mungeNonPixel( elem, value );
1220    var num = parseFloat( value );
1221    // any 'auto', 'medium' value will be 0
1222    size[ measurement ] = !isNaN( num ) ? num : 0;
1223  }
1224
1225  var paddingWidth = size.paddingLeft + size.paddingRight;
1226  var paddingHeight = size.paddingTop + size.paddingBottom;
1227  var marginWidth = size.marginLeft + size.marginRight;
1228  var marginHeight = size.marginTop + size.marginBottom;
1229  var borderWidth = size.borderLeftWidth + size.borderRightWidth;
1230  var borderHeight = size.borderTopWidth + size.borderBottomWidth;
1231
1232  var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter;
1233
1234  // overwrite width and height if we can get it from style
1235  var styleWidth = getStyleSize( style.width );
1236  if ( styleWidth !== false ) {
1237    size.width = styleWidth +
1238      // add padding and border unless it's already including it
1239      ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth );
1240  }
1241
1242  var styleHeight = getStyleSize( style.height );
1243  if ( styleHeight !== false ) {
1244    size.height = styleHeight +
1245      // add padding and border unless it's already including it
1246      ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight );
1247  }
1248
1249  size.innerWidth = size.width - ( paddingWidth + borderWidth );
1250  size.innerHeight = size.height - ( paddingHeight + borderHeight );
1251
1252  size.outerWidth = size.width + marginWidth;
1253  size.outerHeight = size.height + marginHeight;
1254
1255  return size;
1256}
1257
1258// IE8 returns percent values, not pixels
1259// taken from jQuery's curCSS
1260function mungeNonPixel( elem, value ) {
1261  // IE8 and has percent value
1262  if ( window.getComputedStyle || value.indexOf('%') === -1 ) {
1263    return value;
1264  }
1265  var style = elem.style;
1266  // Remember the original values
1267  var left = style.left;
1268  var rs = elem.runtimeStyle;
1269  var rsLeft = rs && rs.left;
1270
1271  // Put in the new values to get a computed value out
1272  if ( rsLeft ) {
1273    rs.left = elem.currentStyle.left;
1274  }
1275  style.left = value;
1276  value = style.pixelLeft;
1277
1278  // Revert the changed values
1279  style.left = left;
1280  if ( rsLeft ) {
1281    rs.left = rsLeft;
1282  }
1283
1284  return value;
1285}
1286
1287return getSize;
1288
1289}
1290
1291// transport
1292if ( typeof define === 'function' && define.amd ) {
1293  // AMD for RequireJS
1294  define( 'get-size/get-size',[ 'get-style-property/get-style-property' ], defineGetSize );
1295} else if ( typeof exports === 'object' ) {
1296  // CommonJS for Component
1297  module.exports = defineGetSize( require('desandro-get-style-property') );
1298} else {
1299  // browser global
1300  window.getSize = defineGetSize( window.getStyleProperty );
1301}
1302
1303})( window );
1304
1305/*!
1306 * docReady v1.0.4
1307 * Cross browser DOMContentLoaded event emitter
1308 * MIT license
1309 */
1310
1311/*jshint browser: true, strict: true, undef: true, unused: true*/
1312/*global define: false, require: false, module: false */
1313
1314( function( window ) {
1315
1316
1317
1318var document = window.document;
1319// collection of functions to be triggered on ready
1320var queue = [];
1321
1322function docReady( fn ) {
1323  // throw out non-functions
1324  if ( typeof fn !== 'function' ) {
1325    return;
1326  }
1327
1328  if ( docReady.isReady ) {
1329    // ready now, hit it
1330    fn();
1331  } else {
1332    // queue function when ready
1333    queue.push( fn );
1334  }
1335}
1336
1337docReady.isReady = false;
1338
1339// triggered on various doc ready events
1340function onReady( event ) {
1341  // bail if already triggered or IE8 document is not ready just yet
1342  var isIE8NotReady = event.type === 'readystatechange' && document.readyState !== 'complete';
1343  if ( docReady.isReady || isIE8NotReady ) {
1344    return;
1345  }
1346
1347  trigger();
1348}
1349
1350function trigger() {
1351  docReady.isReady = true;
1352  // process queue
1353  for ( var i=0, len = queue.length; i < len; i++ ) {
1354    var fn = queue[i];
1355    fn();
1356  }
1357}
1358
1359function defineDocReady( eventie ) {
1360  // trigger ready if page is ready
1361  if ( document.readyState === 'complete' ) {
1362    trigger();
1363  } else {
1364    // listen for events
1365    eventie.bind( document, 'DOMContentLoaded', onReady );
1366    eventie.bind( document, 'readystatechange', onReady );
1367    eventie.bind( window, 'load', onReady );
1368  }
1369
1370  return docReady;
1371}
1372
1373// transport
1374if ( typeof define === 'function' && define.amd ) {
1375  // AMD
1376  define( 'doc-ready/doc-ready',[ 'eventie/eventie' ], defineDocReady );
1377} else if ( typeof exports === 'object' ) {
1378  module.exports = defineDocReady( require('eventie') );
1379} else {
1380  // browser global
1381  window.docReady = defineDocReady( window.eventie );
1382}
1383
1384})( window );
1385
1386/**
1387 * matchesSelector v1.0.3
1388 * matchesSelector( element, '.selector' )
1389 * MIT license
1390 */
1391
1392/*jshint browser: true, strict: true, undef: true, unused: true */
1393/*global define: false, module: false */
1394
1395( function( ElemProto ) {
1396
1397  'use strict';
1398
1399  var matchesMethod = ( function() {
1400    // check for the standard method name first
1401    if ( ElemProto.matches ) {
1402      return 'matches';
1403    }
1404    // check un-prefixed
1405    if ( ElemProto.matchesSelector ) {
1406      return 'matchesSelector';
1407    }
1408    // check vendor prefixes
1409    var prefixes = [ 'webkit', 'moz', 'ms', 'o' ];
1410
1411    for ( var i=0, len = prefixes.length; i < len; i++ ) {
1412      var prefix = prefixes[i];
1413      var method = prefix + 'MatchesSelector';
1414      if ( ElemProto[ method ] ) {
1415        return method;
1416      }
1417    }
1418  })();
1419
1420  // ----- match ----- //
1421
1422  function match( elem, selector ) {
1423    return elem[ matchesMethod ]( selector );
1424  }
1425
1426  // ----- appendToFragment ----- //
1427
1428  function checkParent( elem ) {
1429    // not needed if already has parent
1430    if ( elem.parentNode ) {
1431      return;
1432    }
1433    var fragment = document.createDocumentFragment();
1434    fragment.appendChild( elem );
1435  }
1436
1437  // ----- query ----- //
1438
1439  // fall back to using QSA
1440  // thx @jonathantneal https://gist.github.com/3062955
1441  function query( elem, selector ) {
1442    // append to fragment if no parent
1443    checkParent( elem );
1444
1445    // match elem with all selected elems of parent
1446    var elems = elem.parentNode.querySelectorAll( selector );
1447    for ( var i=0, len = elems.length; i < len; i++ ) {
1448      // return true if match
1449      if ( elems[i] === elem ) {
1450        return true;
1451      }
1452    }
1453    // otherwise return false
1454    return false;
1455  }
1456
1457  // ----- matchChild ----- //
1458
1459  function matchChild( elem, selector ) {
1460    checkParent( elem );
1461    return match( elem, selector );
1462  }
1463
1464  // ----- matchesSelector ----- //
1465
1466  var matchesSelector;
1467
1468  if ( matchesMethod ) {
1469    // IE9 supports matchesSelector, but doesn't work on orphaned elems
1470    // check for that
1471    var div = document.createElement('div');
1472    var supportsOrphans = match( div, 'div' );
1473    matchesSelector = supportsOrphans ? match : matchChild;
1474  } else {
1475    matchesSelector = query;
1476  }
1477
1478  // transport
1479  if ( typeof define === 'function' && define.amd ) {
1480    // AMD
1481    define( 'matches-selector/matches-selector',[],function() {
1482      return matchesSelector;
1483    });
1484  } else if ( typeof exports === 'object' ) {
1485    module.exports = matchesSelector;
1486  }
1487  else {
1488    // browser global
1489    window.matchesSelector = matchesSelector;
1490  }
1491
1492})( Element.prototype );
1493
1494/**
1495 * Fizzy UI utils v1.0.1
1496 * MIT license
1497 */
1498
1499/*jshint browser: true, undef: true, unused: true, strict: true */
1500
1501( function( window, factory ) {
1502  /*global define: false, module: false, require: false */
1503  'use strict';
1504  // universal module definition
1505
1506  if ( typeof define == 'function' && define.amd ) {
1507    // AMD
1508    define( 'fizzy-ui-utils/utils',[
1509      'doc-ready/doc-ready',
1510      'matches-selector/matches-selector'
1511    ], function( docReady, matchesSelector ) {
1512      return factory( window, docReady, matchesSelector );
1513    });
1514  } else if ( typeof exports == 'object' ) {
1515    // CommonJS
1516    module.exports = factory(
1517      window,
1518      require('doc-ready'),
1519      require('desandro-matches-selector')
1520    );
1521  } else {
1522    // browser global
1523    window.fizzyUIUtils = factory(
1524      window,
1525      window.docReady,
1526      window.matchesSelector
1527    );
1528  }
1529
1530}( window, function factory( window, docReady, matchesSelector ) {
1531
1532
1533
1534var utils = {};
1535
1536// ----- extend ----- //
1537
1538// extends objects
1539utils.extend = function( a, b ) {
1540  for ( var prop in b ) {
1541    a[ prop ] = b[ prop ];
1542  }
1543  return a;
1544};
1545
1546// ----- modulo ----- //
1547
1548utils.modulo = function( num, div ) {
1549  return ( ( num % div ) + div ) % div;
1550};
1551
1552// ----- isArray ----- //
1553  
1554var objToString = Object.prototype.toString;
1555utils.isArray = function( obj ) {
1556  return objToString.call( obj ) == '[object Array]';
1557};
1558
1559// ----- makeArray ----- //
1560
1561// turn element or nodeList into an array
1562utils.makeArray = function( obj ) {
1563  var ary = [];
1564  if ( utils.isArray( obj ) ) {
1565    // use object if already an array
1566    ary = obj;
1567  } else if ( obj && typeof obj.length == 'number' ) {
1568    // convert nodeList to array
1569    for ( var i=0, len = obj.length; i < len; i++ ) {
1570      ary.push( obj[i] );
1571    }
1572  } else {
1573    // array of single index
1574    ary.push( obj );
1575  }
1576  return ary;
1577};
1578
1579// ----- indexOf ----- //
1580
1581// index of helper cause IE8
1582utils.indexOf = Array.prototype.indexOf ? function( ary, obj ) {
1583    return ary.indexOf( obj );
1584  } : function( ary, obj ) {
1585    for ( var i=0, len = ary.length; i < len; i++ ) {
1586      if ( ary[i] === obj ) {
1587        return i;
1588      }
1589    }
1590    return -1;
1591  };
1592
1593// ----- removeFrom ----- //
1594
1595utils.removeFrom = function( ary, obj ) {
1596  var index = utils.indexOf( ary, obj );
1597  if ( index != -1 ) {
1598    ary.splice( index, 1 );
1599  }
1600};
1601
1602// ----- isElement ----- //
1603
1604// http://stackoverflow.com/a/384380/182183
1605utils.isElement = ( typeof HTMLElement == 'function' || typeof HTMLElement == 'object' ) ?
1606  function isElementDOM2( obj ) {
1607    return obj instanceof HTMLElement;
1608  } :
1609  function isElementQuirky( obj ) {
1610    return obj && typeof obj == 'object' &&
1611      obj.nodeType == 1 && typeof obj.nodeName == 'string';
1612  };
1613
1614// ----- setText ----- //
1615
1616utils.setText = ( function() {
1617  var setTextProperty;
1618  function setText( elem, text ) {
1619    // only check setTextProperty once
1620    setTextProperty = setTextProperty || ( document.documentElement.textContent !== undefined ? 'textContent' : 'innerText' );
1621    elem[ setTextProperty ] = text;
1622  }
1623  return setText;
1624})();
1625
1626// ----- getParent ----- //
1627
1628utils.getParent = function( elem, selector ) {
1629  while ( elem != document.body ) {
1630    elem = elem.parentNode;
1631    if ( matchesSelector( elem, selector ) ) {
1632      return elem;
1633    }
1634  }
1635};
1636
1637// ----- getQueryElement ----- //
1638
1639// use element as selector string
1640utils.getQueryElement = function( elem ) {
1641  if ( typeof elem == 'string' ) {
1642    return document.querySelector( elem );
1643  }
1644  return elem;
1645};
1646
1647// ----- handleEvent ----- //
1648
1649// enable .ontype to trigger from .addEventListener( elem, 'type' )
1650utils.handleEvent = function( event ) {
1651  var method = 'on' + event.type;
1652  if ( this[ method ] ) {
1653    this[ method ]( event );
1654  }
1655};
1656
1657// ----- filterFindElements ----- //
1658
1659utils.filterFindElements = function( elems, selector ) {
1660  // make array of elems
1661  elems = utils.makeArray( elems );
1662  var ffElems = [];
1663
1664  for ( var i=0, len = elems.length; i < len; i++ ) {
1665    var elem = elems[i];
1666    // check that elem is an actual element
1667    if ( !utils.isElement( elem ) ) {
1668      continue;
1669    }
1670    // filter & find items if we have a selector
1671    if ( selector ) {
1672      // filter siblings
1673      if ( matchesSelector( elem, selector ) ) {
1674        ffElems.push( elem );
1675      }
1676      // find children
1677      var childElems = elem.querySelectorAll( selector );
1678      // concat childElems to filterFound array
1679      for ( var j=0, jLen = childElems.length; j < jLen; j++ ) {
1680        ffElems.push( childElems[j] );
1681      }
1682    } else {
1683      ffElems.push( elem );
1684    }
1685  }
1686
1687  return ffElems;
1688};
1689
1690// ----- debounceMethod ----- //
1691
1692utils.debounceMethod = function( _class, methodName, threshold ) {
1693  // original method
1694  var method = _class.prototype[ methodName ];
1695  var timeoutName = methodName + 'Timeout';
1696
1697  _class.prototype[ methodName ] = function() {
1698    var timeout = this[ timeoutName ];
1699    if ( timeout ) {
1700      clearTimeout( timeout );
1701    }
1702    var args = arguments;
1703
1704    var _this = this;
1705    this[ timeoutName ] = setTimeout( function() {
1706      method.apply( _this, args );
1707      delete _this[ timeoutName ];
1708    }, threshold || 100 );
1709  };
1710};
1711
1712// ----- htmlInit ----- //
1713
1714// http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
1715utils.toDashed = function( str ) {
1716  return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
1717    return $1 + '-' + $2;
1718  }).toLowerCase();
1719};
1720
1721var console = window.console;
1722/**
1723 * allow user to initialize classes via .js-namespace class
1724 * htmlInit( Widget, 'widgetName' )
1725 * options are parsed from data-namespace-option attribute
1726 */
1727utils.htmlInit = function( WidgetClass, namespace ) {
1728  docReady( function() {
1729    var dashedNamespace = utils.toDashed( namespace );
1730    var elems = document.querySelectorAll( '.js-' + dashedNamespace );
1731    var dataAttr = 'data-' + dashedNamespace + '-options';
1732
1733    for ( var i=0, len = elems.length; i < len; i++ ) {
1734      var elem = elems[i];
1735      var attr = elem.getAttribute( dataAttr );
1736      var options;
1737      try {
1738        options = attr && JSON.parse( attr );
1739      } catch ( error ) {
1740        // log error, do not initialize
1741        if ( console ) {
1742          console.error( 'Error parsing ' + dataAttr + ' on ' +
1743            elem.nodeName.toLowerCase() + ( elem.id ? '#' + elem.id : '' ) + ': ' +
1744            error );
1745        }
1746        continue;
1747      }
1748      // initialize
1749      var instance = new WidgetClass( elem, options );
1750      // make available via $().data('layoutname')
1751      var jQuery = window.jQuery;
1752      if ( jQuery ) {
1753        jQuery.data( elem, namespace, instance );
1754      }
1755    }
1756  });
1757};
1758
1759// -----  ----- //
1760
1761return utils;
1762
1763}));
1764
1765/**
1766 * Outlayer Item
1767 */
1768
1769( function( window, factory ) {
1770  'use strict';
1771  // universal module definition
1772  if ( typeof define === 'function' && define.amd ) {
1773    // AMD
1774    define( 'outlayer/item',[
1775        'eventEmitter/EventEmitter',
1776        'get-size/get-size',
1777        'get-style-property/get-style-property',
1778        'fizzy-ui-utils/utils'
1779      ],
1780      function( EventEmitter, getSize, getStyleProperty, utils ) {
1781        return factory( window, EventEmitter, getSize, getStyleProperty, utils );
1782      }
1783    );
1784  } else if (typeof exports === 'object') {
1785    // CommonJS
1786    module.exports = factory(
1787      window,
1788      require('wolfy87-eventemitter'),
1789      require('get-size'),
1790      require('desandro-get-style-property'),
1791      require('fizzy-ui-utils')
1792    );
1793  } else {
1794    // browser global
1795    window.Outlayer = {};
1796    window.Outlayer.Item = factory(
1797      window,
1798      window.EventEmitter,
1799      window.getSize,
1800      window.getStyleProperty,
1801      window.fizzyUIUtils
1802    );
1803  }
1804
1805}( window, function factory( window, EventEmitter, getSize, getStyleProperty, utils ) {
1806'use strict';
1807
1808// ----- helpers ----- //
1809
1810var getComputedStyle = window.getComputedStyle;
1811var getStyle = getComputedStyle ?
1812  function( elem ) {
1813    return getComputedStyle( elem, null );
1814  } :
1815  function( elem ) {
1816    return elem.currentStyle;
1817  };
1818
1819
1820function isEmptyObj( obj ) {
1821  for ( var prop in obj ) {
1822    return false;
1823  }
1824  prop = null;
1825  return true;
1826}
1827
1828// -------------------------- CSS3 support -------------------------- //
1829
1830var transitionProperty = getStyleProperty('transition');
1831var transformProperty = getStyleProperty('transform');
1832var supportsCSS3 = transitionProperty && transformProperty;
1833var is3d = !!getStyleProperty('perspective');
1834
1835var transitionEndEvent = {
1836  WebkitTransition: 'webkitTransitionEnd',
1837  MozTransition: 'transitionend',
1838  OTransition: 'otransitionend',
1839  transition: 'transitionend'
1840}[ transitionProperty ];
1841
1842// properties that could have vendor prefix
1843var prefixableProperties = [
1844  'transform',
1845  'transition',
1846  'transitionDuration',
1847  'transitionProperty'
1848];
1849
1850// cache all vendor properties
1851var vendorProperties = ( function() {
1852  var cache = {};
1853  for ( var i=0, len = prefixableProperties.length; i < len; i++ ) {
1854    var prop = prefixableProperties[i];
1855    var supportedProp = getStyleProperty( prop );
1856    if ( supportedProp && supportedProp !== prop ) {
1857      cache[ prop ] = supportedProp;
1858    }
1859  }
1860  return cache;
1861})();
1862
1863// -------------------------- Item -------------------------- //
1864
1865function Item( element, layout ) {
1866  if ( !element ) {
1867    return;
1868  }
1869
1870  this.element = element;
1871  // parent layout class, i.e. Masonry, Isotope, or Packery
1872  this.layout = layout;
1873  this.position = {
1874    x: 0,
1875    y: 0
1876  };
1877
1878  this._create();
1879}
1880
1881// inherit EventEmitter
1882utils.extend( Item.prototype, EventEmitter.prototype );
1883
1884Item.prototype._create = function() {
1885  // transition objects
1886  this._transn = {
1887    ingProperties: {},
1888    clean: {},
1889    onEnd: {}
1890  };
1891
1892  this.css({
1893    position: 'absolute'
1894  });
1895};
1896
1897// trigger specified handler for event type
1898Item.prototype.handleEvent = function( event ) {
1899  var method = 'on' + event.type;
1900  if ( this[ method ] ) {
1901    this[ method ]( event );
1902  }
1903};
1904
1905Item.prototype.getSize = function() {
1906  this.size = getSize( this.element );
1907};
1908
1909/**
1910 * apply CSS styles to element
1911 * @param {Object} style
1912 */
1913Item.prototype.css = function( style ) {
1914  var elemStyle = this.element.style;
1915
1916  for ( var prop in style ) {
1917    // use vendor property if available
1918    var supportedProp = vendorProperties[ prop ] || prop;
1919    elemStyle[ supportedProp ] = style[ prop ];
1920  }
1921};
1922
1923 // measure position, and sets it
1924Item.prototype.getPosition = function() {
1925  var style = getStyle( this.element );
1926  var layoutOptions = this.layout.options;
1927  var isOriginLeft = layoutOptions.isOriginLeft;
1928  var isOriginTop = layoutOptions.isOriginTop;
1929  var xValue = style[ isOriginLeft ? 'left' : 'right' ];
1930  var yValue = style[ isOriginTop ? 'top' : 'bottom' ];
1931  // convert percent to pixels
1932  var layoutSize = this.layout.size;
1933  var x = xValue.indexOf('%') != -1 ?
1934    ( parseFloat( xValue ) / 100 ) * layoutSize.width : parseInt( xValue, 10 );
1935  var y = yValue.indexOf('%') != -1 ?
1936    ( parseFloat( yValue ) / 100 ) * layoutSize.height : parseInt( yValue, 10 );
1937
1938  // clean up 'auto' or other non-integer values
1939  x = isNaN( x ) ? 0 : x;
1940  y = isNaN( y ) ? 0 : y;
1941  // remove padding from measurement
1942  x -= isOriginLeft ? layoutSize.paddingLeft : layoutSize.paddingRight;
1943  y -= isOriginTop ? layoutSize.paddingTop : layoutSize.paddingBottom;
1944
1945  this.position.x = x;
1946  this.position.y = y;
1947};
1948
1949// set settled position, apply padding
1950Item.prototype.layoutPosition = function() {
1951  var layoutSize = this.layout.size;
1952  var layoutOptions = this.layout.options;
1953  var style = {};
1954
1955  // x
1956  var xPadding = layoutOptions.isOriginLeft ? 'paddingLeft' : 'paddingRight';
1957  var xProperty = layoutOptions.isOriginLeft ? 'left' : 'right';
1958  var xResetProperty = layoutOptions.isOriginLeft ? 'right' : 'left';
1959
1960  var x = this.position.x + layoutSize[ xPadding ];
1961  // set in percentage or pixels
1962  style[ xProperty ] = this.getXValue( x );
1963  // reset other property
1964  style[ xResetProperty ] = '';
1965
1966  // y
1967  var yPadding = layoutOptions.isOriginTop ? 'paddingTop' : 'paddingBottom';
1968  var yProperty = layoutOptions.isOriginTop ? 'top' : 'bottom';
1969  var yResetProperty = layoutOptions.isOriginTop ? 'bottom' : 'top';
1970
1971  var y = this.position.y + layoutSize[ yPadding ];
1972  // set in percentage or pixels
1973  style[ yProperty ] = this.getYValue( y );
1974  // reset other property
1975  style[ yResetProperty ] = '';
1976
1977  this.css( style );
1978  this.emitEvent( 'layout', [ this ] );
1979};
1980
1981Item.prototype.getXValue = function( x ) {
1982  var layoutOptions = this.layout.options;
1983  return layoutOptions.percentPosition && !layoutOptions.isHorizontal ?
1984    ( ( x / this.layout.size.width ) * 100 ) + '%' : x + 'px';
1985};
1986
1987Item.prototype.getYValue = function( y ) {
1988  var layoutOptions = this.layout.options;
1989  return layoutOptions.percentPosition && layoutOptions.isHorizontal ?
1990    ( ( y / this.layout.size.height ) * 100 ) + '%' : y + 'px';
1991};
1992
1993
1994Item.prototype._transitionTo = function( x, y ) {
1995  this.getPosition();
1996  // get current x & y from top/left
1997  var curX = this.position.x;
1998  var curY = this.position.y;
1999
2000  var compareX = parseInt( x, 10 );
2001  var compareY = parseInt( y, 10 );
2002  var didNotMove = compareX === this.position.x && compareY === this.position.y;
2003
2004  // save end position
2005  this.setPosition( x, y );
2006
2007  // if did not move and not transitioning, just go to layout
2008  if ( didNotMove && !this.isTransitioning ) {
2009    this.layoutPosition();
2010    return;
2011  }
2012
2013  var transX = x - curX;
2014  var transY = y - curY;
2015  var transitionStyle = {};
2016  transitionStyle.transform = this.getTranslate( transX, transY );
2017
2018  this.transition({
2019    to: transitionStyle,
2020    onTransitionEnd: {
2021      transform: this.layoutPosition
2022    },
2023    isCleaning: true
2024  });
2025};
2026
2027Item.prototype.getTranslate = function( x, y ) {
2028  // flip cooridinates if origin on right or bottom
2029  var layoutOptions = this.layout.options;
2030  x = layoutOptions.isOriginLeft ? x : -x;
2031  y = layoutOptions.isOriginTop ? y : -y;
2032
2033  if ( is3d ) {
2034    return 'translate3d(' + x + 'px, ' + y + 'px, 0)';
2035  }
2036
2037  return 'translate(' + x + 'px, ' + y + 'px)';
2038};
2039
2040// non transition + transform support
2041Item.prototype.goTo = function( x, y ) {
2042  this.setPosition( x, y );
2043  this.layoutPosition();
2044};
2045
2046// use transition and transforms if supported
2047Item.prototype.moveTo = supportsCSS3 ?
2048  Item.prototype._transitionTo : Item.prototype.goTo;
2049
2050Item.prototype.setPosition = function( x, y ) {
2051  this.position.x = parseInt( x, 10 );
2052  this.position.y = parseInt( y, 10 );
2053};
2054
2055// ----- transition ----- //
2056
2057/**
2058 * @param {Object} style - CSS
2059 * @param {Function} onTransitionEnd
2060 */
2061
2062// non transition, just trigger callback
2063Item.prototype._nonTransition = function( args ) {
2064  this.css( args.to );
2065  if ( args.isCleaning ) {
2066    this._removeStyles( args.to );
2067  }
2068  for ( var prop in args.onTransitionEnd ) {
2069    args.onTransitionEnd[ prop ].call( this );
2070  }
2071};
2072
2073/**
2074 * proper transition
2075 * @param {Object} args - arguments
2076 *   @param {Object} to - style to transition to
2077 *   @param {Object} from - style to start transition from
2078 *   @param {Boolean} isCleaning - removes transition styles after transition
2079 *   @param {Function} onTransitionEnd - callback
2080 */
2081Item.prototype._transition = function( args ) {
2082  // redirect to nonTransition if no transition duration
2083  if ( !parseFloat( this.layout.options.transitionDuration ) ) {
2084    this._nonTransition( args );
2085    return;
2086  }
2087
2088  var _transition = this._transn;
2089  // keep track of onTransitionEnd callback by css property
2090  for ( var prop in args.onTransitionEnd ) {
2091    _transition.onEnd[ prop ] = args.onTransitionEnd[ prop ];
2092  }
2093  // keep track of properties that are transitioning
2094  for ( prop in args.to ) {
2095    _transition.ingProperties[ prop ] = true;
2096    // keep track of properties to clean up when transition is done
2097    if ( args.isCleaning ) {
2098      _transition.clean[ prop ] = true;
2099    }
2100  }
2101
2102  // set from styles
2103  if ( args.from ) {
2104    this.css( args.from );
2105    // force redraw. http://blog.alexmaccaw.com/css-transitions
2106    var h = this.element.offsetHeight;
2107    // hack for JSHint to hush about unused var
2108    h = null;
2109  }
2110  // enable transition
2111  this.enableTransition( args.to );
2112  // set styles that are transitioning
2113  this.css( args.to );
2114
2115  this.isTransitioning = true;
2116
2117};
2118
2119// dash before all cap letters, including first for
2120// WebkitTransform => -webkit-transform
2121function toDashedAll( str ) {
2122  return str.replace( /([A-Z])/g, function( $1 ) {
2123    return '-' + $1.toLowerCase();
2124  });
2125}
2126
2127var transitionProps = 'opacity,' +
vendor: 11,190 bytes, lines 2128-2574
2128  toDashedAll( vendorProperties.transform || 'transform' );
2129
2130Item.prototype.enableTransition = function(/* style */) {
2131  // HACK changing transitionProperty during a transition
2132  // will cause transition to jump
2133  if ( this.isTransitioning ) {
2134    return;
2135  }
2136
2137  // make `transition: foo, bar, baz` from style object
2138  // HACK un-comment this when enableTransition can work
2139  // while a transition is happening
2140  // var transitionValues = [];
2141  // for ( var prop in style ) {
2142  //   // dash-ify camelCased properties like WebkitTransition
2143  //   prop = vendorProperties[ prop ] || prop;
2144  //   transitionValues.push( toDashedAll( prop ) );
2145  // }
2146  // enable transition styles
2147  this.css({
2148    transitionProperty: transitionProps,
2149    transitionDuration: this.layout.options.transitionDuration
2150  });
2151  // listen for transition end event
2152  this.element.addEventListener( transitionEndEvent, this, false );
2153};
2154
2155Item.prototype.transition = Item.prototype[ transitionProperty ? '_transition' : '_nonTransition' ];
2156
2157// ----- events ----- //
2158
2159Item.prototype.onwebkitTransitionEnd = function( event ) {
2160  this.ontransitionend( event );
2161};
2162
2163Item.prototype.onotransitionend = function( event ) {
2164  this.ontransitionend( event );
2165};
2166
2167// properties that I munge to make my life easier
2168var dashedVendorProperties = {
2169  '-webkit-transform': 'transform',
2170  '-moz-transform': 'transform',
2171  '-o-transform': 'transform'
2172};
2173
2174Item.prototype.ontransitionend = function( event ) {
2175  // disregard bubbled events from children
2176  if ( event.target !== this.element ) {
2177    return;
2178  }
2179  var _transition = this._transn;
2180  // get property name of transitioned property, convert to prefix-free
2181  var propertyName = dashedVendorProperties[ event.propertyName ] || event.propertyName;
2182
2183  // remove property that has completed transitioning
2184  delete _transition.ingProperties[ propertyName ];
2185  // check if any properties are still transitioning
2186  if ( isEmptyObj( _transition.ingProperties ) ) {
2187    // all properties have completed transitioning
2188    this.disableTransition();
2189  }
2190  // clean style
2191  if ( propertyName in _transition.clean ) {
2192    // clean up style
2193    this.element.style[ event.propertyName ] = '';
2194    delete _transition.clean[ propertyName ];
2195  }
2196  // trigger onTransitionEnd callback
2197  if ( propertyName in _transition.onEnd ) {
2198    var onTransitionEnd = _transition.onEnd[ propertyName ];
2199    onTransitionEnd.call( this );
2200    delete _transition.onEnd[ propertyName ];
2201  }
2202
2203  this.emitEvent( 'transitionEnd', [ this ] );
2204};
2205
2206Item.prototype.disableTransition = function() {
2207  this.removeTransitionStyles();
2208  this.element.removeEventListener( transitionEndEvent, this, false );
2209  this.isTransitioning = false;
2210};
2211
2212/**
2213 * removes style property from element
2214 * @param {Object} style
2215**/
2216Item.prototype._removeStyles = function( style ) {
2217  // clean up transition styles
2218  var cleanStyle = {};
2219  for ( var prop in style ) {
2220    cleanStyle[ prop ] = '';
2221  }
2222  this.css( cleanStyle );
2223};
2224
2225var cleanTransitionStyle = {
2226  transitionProperty: '',
2227  transitionDuration: ''
2228};
2229
2230Item.prototype.removeTransitionStyles = function() {
2231  // remove transition
2232  this.css( cleanTransitionStyle );
2233};
2234
2235// ----- show/hide/remove ----- //
2236
2237// remove element from DOM
2238Item.prototype.removeElem = function() {
2239  this.element.parentNode.removeChild( this.element );
2240  // remove display: none
2241  this.css({ display: '' });
2242  this.emitEvent( 'remove', [ this ] );
2243};
2244
2245Item.prototype.remove = function() {
2246  // just remove element if no transition support or no transition
2247  if ( !transitionProperty || !parseFloat( this.layout.options.transitionDuration ) ) {
2248    this.removeElem();
2249    return;
2250  }
2251
2252  // start transition
2253  var _this = this;
2254  this.once( 'transitionEnd', function() {
2255    _this.removeElem();
2256  });
2257  this.hide();
2258};
2259
2260Item.prototype.reveal = function() {
2261  delete this.isHidden;
2262  // remove display: none
2263  this.css({ display: '' });
2264
2265  var options = this.layout.options;
2266
2267  var onTransitionEnd = {};
2268  var transitionEndProperty = this.getHideRevealTransitionEndProperty('visibleStyle');
2269  onTransitionEnd[ transitionEndProperty ] = this.onRevealTransitionEnd;
2270
2271  this.transition({
2272    from: options.hiddenStyle,
2273    to: options.visibleStyle,
2274    isCleaning: true,
2275    onTransitionEnd: onTransitionEnd
2276  });
2277};
2278
2279Item.prototype.onRevealTransitionEnd = function() {
2280  // check if still visible
2281  // during transition, item may have been hidden
2282  if ( !this.isHidden ) {
2283    this.emitEvent('reveal');
2284  }
2285};
2286
2287/**
2288 * get style property use for hide/reveal transition end
2289 * @param {String} styleProperty - hiddenStyle/visibleStyle
2290 * @returns {String}
2291 */
2292Item.prototype.getHideRevealTransitionEndProperty = function( styleProperty ) {
2293  var optionStyle = this.layout.options[ styleProperty ];
2294  // use opacity
2295  if ( optionStyle.opacity ) {
2296    return 'opacity';
2297  }
2298  // get first property
2299  for ( var prop in optionStyle ) {
2300    return prop;
2301  }
2302};
2303
2304Item.prototype.hide = function() {
2305  // set flag
2306  this.isHidden = true;
2307  // remove display: none
2308  this.css({ display: '' });
2309
2310  var options = this.layout.options;
2311
2312  var onTransitionEnd = {};
2313  var transitionEndProperty = this.getHideRevealTransitionEndProperty('hiddenStyle');
2314  onTransitionEnd[ transitionEndProperty ] = this.onHideTransitionEnd;
2315
2316  this.transition({
2317    from: options.visibleStyle,
2318    to: options.hiddenStyle,
2319    // keep hidden stuff hidden
2320    isCleaning: true,
2321    onTransitionEnd: onTransitionEnd
2322  });
2323};
2324
2325Item.prototype.onHideTransitionEnd = function() {
2326  // check if still hidden
2327  // during transition, item may have been un-hidden
2328  if ( this.isHidden ) {
2329    this.css({ display: 'none' });
2330    this.emitEvent('hide');
2331  }
2332};
2333
2334Item.prototype.destroy = function() {
2335  this.css({
2336    position: '',
2337    left: '',
2338    right: '',
2339    top: '',
2340    bottom: '',
2341    transition: '',
2342    transform: ''
2343  });
2344};
2345
2346return Item;
2347
2348}));
2349
2350/*!
2351 * Outlayer v1.4.2
2352 * the brains and guts of a layout library
2353 * MIT license
2354 */
2355
2356( function( window, factory ) {
2357  'use strict';
2358  // universal module definition
2359
2360  if ( typeof define == 'function' && define.amd ) {
2361    // AMD
2362    define( 'outlayer/outlayer',[
2363        'eventie/eventie',
2364        'eventEmitter/EventEmitter',
2365        'get-size/get-size',
2366        'fizzy-ui-utils/utils',
2367        './item'
2368      ],
2369      function( eventie, EventEmitter, getSize, utils, Item ) {
2370        return factory( window, eventie, EventEmitter, getSize, utils, Item);
2371      }
2372    );
2373  } else if ( typeof exports == 'object' ) {
2374    // CommonJS
2375    module.exports = factory(
2376      window,
2377      require('eventie'),
2378      require('wolfy87-eventemitter'),
2379      require('get-size'),
2380      require('fizzy-ui-utils'),
2381      require('./item')
2382    );
2383  } else {
2384    // browser global
2385    window.Outlayer = factory(
2386      window,
2387      window.eventie,
2388      window.EventEmitter,
2389      window.getSize,
2390      window.fizzyUIUtils,
2391      window.Outlayer.Item
2392    );
2393  }
2394
2395}( window, function factory( window, eventie, EventEmitter, getSize, utils, Item ) {
2396'use strict';
2397
2398// ----- vars ----- //
2399
2400var console = window.console;
2401var jQuery = window.jQuery;
2402var noop = function() {};
2403
2404// -------------------------- Outlayer -------------------------- //
2405
2406// globally unique identifiers
2407var GUID = 0;
2408// internal store of all Outlayer intances
2409var instances = {};
2410
2411
2412/**
2413 * @param {Element, String} element
2414 * @param {Object} options
2415 * @constructor
2416 */
2417function Outlayer( element, options ) {
2418  var queryElement = utils.getQueryElement( element );
2419  if ( !queryElement ) {
2420    if ( console ) {
2421      console.error( 'Bad element for ' + this.constructor.namespace +
2422        ': ' + ( queryElement || element ) );
2423    }
2424    return;
2425  }
2426  this.element = queryElement;
2427  // add jQuery
2428  if ( jQuery ) {
2429    this.$element = jQuery( this.element );
2430  }
2431
2432  // options
2433  this.options = utils.extend( {}, this.constructor.defaults );
2434  this.option( options );
2435
2436  // add id for Outlayer.getFromElement
2437  var id = ++GUID;
2438  this.element.outlayerGUID = id; // expando
2439  instances[ id ] = this; // associate via id
2440
2441  // kick it off
2442  this._create();
2443
2444  if ( this.options.isInitLayout ) {
2445    this.layout();
2446  }
2447}
2448
2449// settings are for internal use only
2450Outlayer.namespace = 'outlayer';
2451Outlayer.Item = Item;
2452
2453// default options
2454Outlayer.defaults = {
2455  containerStyle: {
2456    position: 'relative'
2457  },
2458  isInitLayout: true,
2459  isOriginLeft: true,
2460  isOriginTop: true,
2461  isResizeBound: true,
2462  isResizingContainer: true,
2463  // item options
2464  transitionDuration: '0.4s',
2465  hiddenStyle: {
2466    opacity: 0,
2467    transform: 'scale(0.001)'
2468  },
2469  visibleStyle: {
2470    opacity: 1,
2471    transform: 'scale(1)'
2472  }
2473};
2474
2475// inherit EventEmitter
2476utils.extend( Outlayer.prototype, EventEmitter.prototype );
2477
2478/**
2479 * set options
2480 * @param {Object} opts
2481 */
2482Outlayer.prototype.option = function( opts ) {
2483  utils.extend( this.options, opts );
2484};
2485
2486Outlayer.prototype._create = function() {
2487  // get items from children
2488  this.reloadItems();
2489  // elements that affect layout, but are not laid out
2490  this.stamps = [];
2491  this.stamp( this.options.stamp );
2492  // set container style
2493  utils.extend( this.element.style, this.options.containerStyle );
2494
2495  // bind resize method
2496  if ( this.options.isResizeBound ) {
2497    this.bindResize();
2498  }
2499};
2500
2501// goes through all children again and gets bricks in proper order
2502Outlayer.prototype.reloadItems = function() {
2503  // collection of item elements
2504  this.items = this._itemize( this.element.children );
2505};
2506
2507
2508/**
2509 * turn elements into Outlayer.Items to be used in layout
2510 * @param {Array or NodeList or HTMLElement} elems
2511 * @returns {Array} items - collection of new Outlayer Items
2512 */
2513Outlayer.prototype._itemize = function( elems ) {
2514
2515  var itemElems = this._filterFindItemElements( elems );
2516  var Item = this.constructor.Item;
2517
2518  // create new Outlayer Items for collection
2519  var items = [];
2520  for ( var i=0, len = itemElems.length; i < len; i++ ) {
2521    var elem = itemElems[i];
2522    var item = new Item( elem, this );
2523    items.push( item );
2524  }
2525
2526  return items;
2527};
2528
2529/**
2530 * get item elements to be used in layout
2531 * @param {Array or NodeList or HTMLElement} elems
2532 * @returns {Array} items - item elements
2533 */
2534Outlayer.prototype._filterFindItemElements = function( elems ) {
2535  return utils.filterFindElements( elems, this.options.itemSelector );
2536};
2537
2538/**
2539 * getter method for getting item elements
2540 * @returns {Array} elems - collection of item elements
2541 */
2542Outlayer.prototype.getItemElements = function() {
2543  var elems = [];
2544  for ( var i=0, len = this.items.length; i < len; i++ ) {
2545    elems.push( this.items[i].element );
2546  }
2547  return elems;
2548};
2549
2550// ----- init & layout ----- //
2551
2552/**
2553 * lays out all items
2554 */
2555Outlayer.prototype.layout = function() {
2556  this._resetLayout();
2557  this._manageStamps();
2558
2559  // don't animate first layout
2560  var isInstant = this.options.isLayoutInstant !== undefined ?
2561    this.options.isLayoutInstant : !this._isLayoutInited;
2562  this.layoutItems( this.items, isInstant );
2563
2564  // flag for initalized
2565  this._isLayoutInited = true;
2566};
2567
2568// _init is alias for layout
2569Outlayer.prototype._init = Outlayer.prototype.layout;
2570
2571/**
2572 * logic before any new layout
2573 */
2574Outlayer.prototype._resetLayout = function() {
2575  this.getSize();
2576};
2577
2578
2579Outlayer.prototype.getSize = function() {
2580  this.size = getSize( this.element );
2581};
2582
2583/**
2584 * get measurement from option, for columnWidth, rowHeight, gutter
2585 * if option is String -> get element from selector string, & get size of element
2586 * if option is Element -> get size of element
2587 * else use option as a number
2588 *
2589 * @param {String} measurement
2590 * @param {String} size - width or height
2591 * @private
2592 */
2593Outlayer.prototype._getMeasurement = function( measurement, size ) {
2594  var option = this.options[ measurement ];
2595  var elem;
2596  if ( !option ) {
2597    // default to 0
2598    this[ measurement ] = 0;
2599  } else {
2600    // use option as an element
2601    if ( typeof option === 'string' ) {
2602      elem = this.element.querySelector( option );
2603    } else if ( utils.isElement( option ) ) {
2604      elem = option;
2605    }
2606    // use size of element, if element
2607    this[ measurement ] = elem ? getSize( elem )[ size ] : option;
2608  }
2609};
2610
2611/**
2612 * layout a collection of item elements
2613 * @api public
2614 */
2615Outlayer.prototype.layoutItems = function( items, isInstant ) {
2616  items = this._getItemsForLayout( items );
2617
2618  this._layoutItems( items, isInstant );
2619
2620  this._postLayout();
2621};
2622
2623/**
2624 * get the items to be laid out
2625 * you may want to skip over some items
2626 * @param {Array} items
2627 * @returns {Array} items
2628 */
2629Outlayer.prototype._getItemsForLayout = function( items ) {
2630  var layoutItems = [];
2631  for ( var i=0, len = items.length; i < len; i++ ) {
2632    var item = items[i];
2633    if ( !item.isIgnored ) {
2634      layoutItems.push( item );
2635    }
2636  }
2637  return layoutItems;
2638};
2639
2640/**
2641 * layout items
2642 * @param {Array} items
2643 * @param {Boolean} isInstant
2644 */
2645Outlayer.prototype._layoutItems = function( items, isInstant ) {
2646  this._emitCompleteOnItems( 'layout', items );
2647
2648  if ( !items || !items.length ) {
2649    // no items, emit event with empty array
2650    return;
2651  }
2652
2653  var queue = [];
2654
2655  for ( var i=0, len = items.length; i < len; i++ ) {
2656    var item = items[i];
2657    // get x/y object from method
2658    var position = this._getItemLayoutPosition( item );
2659    // enqueue
2660    position.item = item;
2661    position.isInstant = isInstant || item.isLayoutInstant;
2662    queue.push( position );
2663  }
2664
2665  this._processLayoutQueue( queue );
2666};
2667
2668/**
2669 * get item layout position
2670 * @param {Outlayer.Item} item
2671 * @returns {Object} x and y position
2672 */
2673Outlayer.prototype._getItemLayoutPosition = function( /* item */ ) {
2674  return {
2675    x: 0,
2676    y: 0
2677  };
2678};
2679
2680/**
2681 * iterate over array and position each item
2682 * Reason being - separating this logic prevents 'layout invalidation'
2683 * thx @paul_irish
2684 * @param {Array} queue
2685 */
2686Outlayer.prototype._processLayoutQueue = function( queue ) {
2687  for ( var i=0, len = queue.length; i < len; i++ ) {
2688    var obj = queue[i];
2689    this._positionItem( obj.item, obj.x, obj.y, obj.isInstant );
2690  }
2691};
2692
2693/**
2694 * Sets position of item in DOM
2695 * @param {Outlayer.Item} item
2696 * @param {Number} x - horizontal position
2697 * @param {Number} y - vertical position
2698 * @param {Boolean} isInstant - disables transitions
2699 */
2700Outlayer.prototype._positionItem = function( item, x, y, isInstant ) {
2701  if ( isInstant ) {
2702    // if not transition, just set CSS
2703    item.goTo( x, y );
2704  } else {
2705    item.moveTo( x, y );
2706  }
2707};
2708
2709/**
2710 * Any logic you want to do after each layout,
2711 * i.e. size the container
2712 */
2713Outlayer.prototype._postLayout = function() {
2714  this.resizeContainer();
2715};
2716
2717Outlayer.prototype.resizeContainer = function() {
2718  if ( !this.options.isResizingContainer ) {
2719    return;
2720  }
2721  var size = this._getContainerSize();
2722  if ( size ) {
2723    this._setContainerMeasure( size.width, true );
2724    this._setContainerMeasure( size.height, false );
2725  }
2726};
2727
2728/**
2729 * Sets width or height of container if returned
2730 * @returns {Object} size
2731 *   @param {Number} width
2732 *   @param {Number} height
2733 */
2734Outlayer.prototype._getContainerSize = noop;
2735
2736/**
2737 * @param {Number} measure - size of width or height
2738 * @param {Boolean} isWidth
2739 */
2740Outlayer.prototype._setContainerMeasure = function( measure, isWidth ) {
2741  if ( measure === undefined ) {
2742    return;
2743  }
2744
2745  var elemSize = this.size;
2746  // add padding and border width if border box
2747  if ( elemSize.isBorderBox ) {
2748    measure += isWidth ? elemSize.paddingLeft + elemSize.paddingRight +
2749      elemSize.borderLeftWidth + elemSize.borderRightWidth :
2750      elemSize.paddingBottom + elemSize.paddingTop +
2751      elemSize.borderTopWidth + elemSize.borderBottomWidth;
2752  }
2753
2754  measure = Math.max( measure, 0 );
2755  this.element.style[ isWidth ? 'width' : 'height' ] = measure + 'px';
2756};
2757
2758/**
2759 * emit eventComplete on a collection of items events
2760 * @param {String} eventName
2761 * @param {Array} items - Outlayer.Items
2762 */
2763Outlayer.prototype._emitCompleteOnItems = function( eventName, items ) {
2764  var _this = this;
2765  function onComplete() {
2766    _this.dispatchEvent( eventName + 'Complete', null, [ items ] );
2767  }
2768
2769  var count = items.length;
2770  if ( !items || !count ) {
2771    onComplete();
2772    return;
2773  }
2774
2775  var doneCount = 0;
2776  function tick() {
2777    doneCount++;
2778    if ( doneCount === count ) {
2779      onComplete();
2780    }
2781  }
2782
2783  // bind callback
2784  for ( var i=0, len = items.length; i < len; i++ ) {
2785    var item = items[i];
2786    item.once( eventName, tick );
2787  }
2788};
2789
2790/**
2791 * emits events via eventEmitter and jQuery events
2792 * @param {String} type - name of event
2793 * @param {Event} event - original event
2794 * @param {Array} args - extra arguments
2795 */
2796Outlayer.prototype.dispatchEvent = function( type, event, args ) {
2797  // add original event to arguments
2798  var emitArgs = event ? [ event ].concat( args ) : args;
2799  this.emitEvent( type, emitArgs );
2800
2801  if ( jQuery ) {
2802    // set this.$element
2803    this.$element = this.$element || jQuery( this.element );
2804    if ( event ) {
2805      // create jQuery event
2806      var $event = jQuery.Event( event );
2807      $event.type = type;
2808      this.$element.trigger( $event, args );
2809    } else {
2810      // just trigger with type if no event available
2811      this.$element.trigger( type, args );
2812    }
2813  }
2814};
2815
2816// -------------------------- ignore & stamps -------------------------- //
2817
2818
2819/**
2820 * keep item in collection, but do not lay it out
2821 * ignored items do not get skipped in layout
2822 * @param {Element} elem
2823 */
2824Outlayer.prototype.ignore = function( elem ) {
2825  var item = this.getItem( elem );
2826  if ( item ) {
2827    item.isIgnored = true;
2828  }
2829};
2830
2831/**
2832 * return item to layout collection
2833 * @param {Element} elem
2834 */
2835Outlayer.prototype.unignore = function( elem ) {
2836  var item = this.getItem( elem );
2837  if ( item ) {
2838    delete item.isIgnored;
2839  }
2840};
2841
2842/**
2843 * adds elements to stamps
2844 * @param {NodeList, Array, Element, or String} elems
2845 */
2846Outlayer.prototype.stamp = function( elems ) {
2847  elems = this._find( elems );
2848  if ( !elems ) {
2849    return;
2850  }
2851
2852  this.stamps = this.stamps.concat( elems );
2853  // ignore
2854  for ( var i=0, len = elems.length; i < len; i++ ) {
2855    var elem = elems[i];
2856    this.ignore( elem );
2857  }
2858};
2859
2860/**
2861 * removes elements to stamps
2862 * @param {NodeList, Array, or Element} elems
2863 */
2864Outlayer.prototype.unstamp = function( elems ) {
2865  elems = this._find( elems );
2866  if ( !elems ){
2867    return;
2868  }
2869
2870  for ( var i=0, len = elems.length; i < len; i++ ) {
2871    var elem = elems[i];
2872    // filter out removed stamp elements
2873    utils.removeFrom( this.stamps, elem );
2874    this.unignore( elem );
2875  }
2876
2877};
2878
2879/**
2880 * finds child elements
2881 * @param {NodeList, Array, Element, or String} elems
2882 * @returns {Array} elems
2883 */
2884Outlayer.prototype._find = function( elems ) {
2885  if ( !elems ) {
2886    return;
2887  }
2888  // if string, use argument as selector string
2889  if ( typeof elems === 'string' ) {
2890    elems = this.element.querySelectorAll( elems );
2891  }
2892  elems = utils.makeArray( elems );
2893  return elems;
2894};
2895
2896Outlayer.prototype._manageStamps = function() {
2897  if ( !this.stamps || !this.stamps.length ) {
2898    return;
2899  }
2900
2901  this._getBoundingRect();
2902
2903  for ( var i=0, len = this.stamps.length; i < len; i++ ) {
2904    var stamp = this.stamps[i];
2905    this._manageStamp( stamp );
2906  }
2907};
2908
2909// update boundingLeft / Top
2910Outlayer.prototype._getBoundingRect = function() {
2911  // get bounding rect for container element
2912  var boundingRect = this.element.getBoundingClientRect();
2913  var size = this.size;
2914  this._boundingRect = {
2915    left: boundingRect.left + size.paddingLeft + size.borderLeftWidth,
2916    top: boundingRect.top + size.paddingTop + size.borderTopWidth,
2917    right: boundingRect.right - ( size.paddingRight + size.borderRightWidth ),
2918    bottom: boundingRect.bottom - ( size.paddingBottom + size.borderBottomWidth )
2919  };
2920};
2921
2922/**
2923 * @param {Element} stamp
2924**/
2925Outlayer.prototype._manageStamp = noop;
2926
2927/**
2928 * get x/y position of element relative to container element
2929 * @param {Element} elem
2930 * @returns {Object} offset - has left, top, right, bottom
2931 */
2932Outlayer.prototype._getElementOffset = function( elem ) {
2933  var boundingRect = elem.getBoundingClientRect();
2934  var thisRect = this._boundingRect;
2935  var size = getSize( elem );
2936  var offset = {
2937    left: boundingRect.left - thisRect.left - size.marginLeft,
2938    top: boundingRect.top - thisRect.top - size.marginTop,
2939    right: thisRect.right - boundingRect.right - size.marginRight,
2940    bottom: thisRect.bottom - boundingRect.bottom - size.marginBottom
2941  };
2942  return offset;
2943};
2944
2945// -------------------------- resize -------------------------- //
2946
2947// enable event handlers for listeners
2948// i.e. resize -> onresize
2949Outlayer.prototype.handleEvent = function( event ) {
2950  var method = 'on' + event.type;
2951  if ( this[ method ] ) {
2952    this[ method ]( event );
2953  }
2954};
2955
2956/**
2957 * Bind layout to window resizing
2958 */
2959Outlayer.prototype.bindResize = function() {
2960  // bind just one listener
2961  if ( this.isResizeBound ) {
2962    return;
2963  }
2964  eventie.bind( window, 'resize', this );
2965  this.isResizeBound = true;
2966};
2967
2968/**
2969 * Unbind layout to window resizing
2970 */
2971Outlayer.prototype.unbindResize = function() {
2972  if ( this.isResizeBound ) {
2973    eventie.unbind( window, 'resize', this );
2974  }
2975  this.isResizeBound = false;
2976};
2977
2978// original debounce by John Hann
2979// http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
2980
2981// this fires every resize
2982Outlayer.prototype.onresize = function() {
2983  if ( this.resizeTimeout ) {
2984    clearTimeout( this.resizeTimeout );
2985  }
2986
2987  var _this = this;
2988  function delayed() {
2989    _this.resize();
2990    delete _this.resizeTimeout;
2991  }
2992
2993  this.resizeTimeout = setTimeout( delayed, 100 );
2994};
2995
2996// debounced, layout on resize
2997Outlayer.prototype.resize = function() {
2998  // don't trigger if size did not change
2999  // or if resize was unbound. See #9
3000  if ( !this.isResizeBound || !this.needsResizeLayout() ) {
3001    return;
3002  }
3003
3004  this.layout();
3005};
3006
3007/**
3008 * check if layout is needed post layout
3009 * @returns Boolean
3010 */
3011Outlayer.prototype.needsResizeLayout = function() {
3012  var size = getSize( this.element );
3013  // check that this.size and size are there
3014  // IE8 triggers resize on body size change, so they might not be
3015  var hasSizes = this.size && size;
3016  return hasSizes && size.innerWidth !== this.size.innerWidth;
3017};
3018
3019// -------------------------- methods -------------------------- //
3020
3021/**
3022 * add items to Outlayer instance
3023 * @param {Array or NodeList or Element} elems
3024 * @returns {Array} items - Outlayer.Items
3025**/
3026Outlayer.prototype.addItems = function( elems ) {
3027  var items = this._itemize( elems );
3028  // add items to collection
3029  if ( items.length ) {
3030    this.items = this.items.concat( items );
3031  }
3032  return items;
3033};
3034
3035/**
3036 * Layout newly-appended item elements
3037 * @param {Array or NodeList or Element} elems
3038 */
3039Outlayer.prototype.appended = function( elems ) {
3040  var items = this.addItems( elems );
3041  if ( !items.length ) {
3042    return;
3043  }
3044  // layout and reveal just the new items
3045  this.layoutItems( items, true );
3046  this.reveal( items );
3047};
3048
3049/**
3050 * Layout prepended elements
3051 * @param {Array or NodeList or Element} elems
3052 */
3053Outlayer.prototype.prepended = function( elems ) {
3054  var items = this._itemize( elems );
3055  if ( !items.length ) {
3056    return;
3057  }
3058  // add items to beginning of collection
3059  var previousItems = this.items.slice(0);
3060  this.items = items.concat( previousItems );
3061  // start new layout
3062  this._resetLayout();
3063  this._manageStamps();
3064  // layout new stuff without transition
3065  this.layoutItems( items, true );
3066  this.reveal( items );
3067  // layout previous items
3068  this.layoutItems( previousItems );
3069};
3070
3071/**
3072 * reveal a collection of items
3073 * @param {Array of Outlayer.Items} items
3074 */
3075Outlayer.prototype.reveal = function( items ) {
3076  this._emitCompleteOnItems( 'reveal', items );
3077
3078  var len = items && items.length;
3079  for ( var i=0; len && i < len; i++ ) {
3080    var item = items[i];
3081    item.reveal();
3082  }
3083};
3084
3085/**
3086 * hide a collection of items
3087 * @param {Array of Outlayer.Items} items
3088 */
3089Outlayer.prototype.hide = function( items ) {
3090  this._emitCompleteOnItems( 'hide', items );
3091
3092  var len = items && items.length;
3093  for ( var i=0; len && i < len; i++ ) {
3094    var item = items[i];
3095    item.hide();
3096  }
3097};
3098
3099/**
3100 * reveal item elements
3101 * @param {Array}, {Element}, {NodeList} items
3102 */
3103Outlayer.prototype.revealItemElements = function( elems ) {
3104  var items = this.getItems( elems );
3105  this.reveal( items );
3106};
3107
3108/**
3109 * hide item elements
3110 * @param {Array}, {Element}, {NodeList} items
3111 */
3112Outlayer.prototype.hideItemElements = function( elems ) {
3113  var items = this.getItems( elems );
3114  this.hide( items );
3115};
3116
3117/**
3118 * get Outlayer.Item, given an Element
3119 * @param {Element} elem
3120 * @param {Function} callback
3121 * @returns {Outlayer.Item} item
3122 */
3123Outlayer.prototype.getItem = function( elem ) {
3124  // loop through items to get the one that matches
3125  for ( var i=0, len = this.items.length; i < len; i++ ) {
3126    var item = this.items[i];
3127    if ( item.element === elem ) {
3128      // return item
3129      return item;
3130    }
3131  }
3132};
3133
3134/**
3135 * get collection of Outlayer.Items, given Elements
3136 * @param {Array} elems
3137 * @returns {Array} items - Outlayer.Items
3138 */
3139Outlayer.prototype.getItems = function( elems ) {
3140  elems = utils.makeArray( elems );
3141  var items = [];
3142  for ( var i=0, len = elems.length; i < len; i++ ) {
3143    var elem = elems[i];
3144    var item = this.getItem( elem );
3145    if ( item ) {
3146      items.push( item );
3147    }
3148  }
3149
3150  return items;
3151};
3152
3153/**
3154 * remove element(s) from instance and DOM
3155 * @param {Array or NodeList or Element} elems
3156 */
3157Outlayer.prototype.remove = function( elems ) {
3158  var removeItems = this.getItems( elems );
3159
3160  this._emitCompleteOnItems( 'remove', removeItems );
3161
3162  // bail if no items to remove
3163  if ( !removeItems || !removeItems.length ) {
3164    return;
3165  }
3166
3167  for ( var i=0, len = removeItems.length; i < len; i++ ) {
3168    var item = removeItems[i];
3169    item.remove();
3170    // remove item from collection
3171    utils.removeFrom( this.items, item );
3172  }
3173};
3174
3175// ----- destroy ----- //
3176
3177// remove and disable Outlayer instance
3178Outlayer.prototype.destroy = function() {
3179  // clean up dynamic styles
3180  var style = this.element.style;
3181  style.height = '';
3182  style.position = '';
3183  style.width = '';
3184  // destroy items
3185  for ( var i=0, len = this.items.length; i < len; i++ ) {
3186    var item = this.items[i];
3187    item.destroy();
3188  }
3189
3190  this.unbindResize();
3191
3192  var id = this.element.outlayerGUID;
3193  delete instances[ id ]; // remove reference to instance by id
3194  delete this.element.outlayerGUID;
3195  // remove data for jQuery
3196  if ( jQuery ) {
3197    jQuery.removeData( this.element, this.constructor.namespace );
3198  }
3199
3200};
3201
3202// -------------------------- data -------------------------- //
3203
3204/**
3205 * get Outlayer instance from element
3206 * @param {Element} elem
3207 * @returns {Outlayer}
3208 */
3209Outlayer.data = function( elem ) {
3210  elem = utils.getQueryElement( elem );
3211  var id = elem && elem.outlayerGUID;
3212  return id && instances[ id ];
3213};
3214
3215
3216// -------------------------- create Outlayer class -------------------------- //
3217
3218/**
3219 * create a layout class
3220 * @param {String} namespace
3221 */
3222Outlayer.create = function( namespace, options ) {
3223  // sub-class Outlayer
3224  function Layout() {
3225    Outlayer.apply( this, arguments );
3226  }
3227  // inherit Outlayer prototype, use Object.create if there
3228  if ( Object.create ) {
3229    Layout.prototype = Object.create( Outlayer.prototype );
3230  } else {
3231    utils.extend( Layout.prototype, Outlayer.prototype );
3232  }
3233  // set contructor, used for namespace and Item
3234  Layout.prototype.constructor = Layout;
3235
3236  Layout.defaults = utils.extend( {}, Outlayer.defaults );
3237  // apply new options
3238  utils.extend( Layout.defaults, options );
3239  // keep prototype.settings for backwards compatibility (Packery v1.2.0)
3240  Layout.prototype.settings = {};
3241
3242  Layout.namespace = namespace;
3243
3244  Layout.data = Outlayer.data;
3245
3246  // sub-class Item
3247  Layout.Item = function LayoutItem() {
3248    Item.apply( this, arguments );
3249  };
3250
3251  Layout.Item.prototype = new Item();
3252
3253  // -------------------------- declarative -------------------------- //
3254
3255  utils.htmlInit( Layout, namespace );
3256
3257  // -------------------------- jQuery bridge -------------------------- //
3258
3259  // make into jQuery plugin
3260  if ( jQuery && jQuery.bridget ) {
3261    jQuery.bridget( namespace, Layout );
3262  }
3263
3264  return Layout;
3265};
3266
3267// ----- fin ----- //
3268
3269// back in global
3270Outlayer.Item = Item;
3271
3272return Outlayer;
3273
3274}));
3275
3276
3277/**
3278 * Isotope Item
3279**/
3280
3281( function( window, factory ) {
3282'use strict';
3283  // universal module definition
3284  if ( typeof define == 'function' && define.amd ) {
3285    // AMD
3286    define( 'isotope/js/item',[
3287        'outlayer/outlayer'
3288      ],
3289      factory );
3290  } else if ( typeof exports == 'object' ) {
3291    // CommonJS
3292    module.exports = factory(
3293      require('outlayer')
3294    );
3295  } else {
3296    // browser global
3297    window.Isotope = window.Isotope || {};
3298    window.Isotope.Item = factory(
3299      window.Outlayer
3300    );
3301  }
3302
3303}( window, function factory( Outlayer ) {
3304'use strict';
3305
3306// -------------------------- Item -------------------------- //
3307
3308// sub-class Outlayer Item
3309function Item() {
3310  Outlayer.Item.apply( this, arguments );
3311}
3312
3313Item.prototype = new Outlayer.Item();
3314
3315Item.prototype._create = function() {
3316  // assign id, used for original-order sorting
3317  this.id = this.layout.itemGUID++;
3318  Outlayer.Item.prototype._create.call( this );
3319  this.sortData = {};
3320};
3321
3322Item.prototype.updateSortData = function() {
3323  if ( this.isIgnored ) {
3324    return;
3325  }
3326  // default sorters
3327  this.sortData.id = this.id;
3328  // for backward compatibility
3329  this.sortData['original-order'] = this.id;
3330  this.sortData.random = Math.random();
3331  // go thru getSortData obj and apply the sorters
3332  var getSortData = this.layout.options.getSortData;
3333  var sorters = this.layout._sorters;
3334  for ( var key in getSortData ) {
3335    var sorter = sorters[ key ];
3336    this.sortData[ key ] = sorter( this.element, this );
3337  }
3338};
3339
3340var _destroy = Item.prototype.destroy;
3341Item.prototype.destroy = function() {
3342  // call super
3343  _destroy.apply( this, arguments );
3344  // reset display, #741
3345  this.css({
3346    display: ''
3347  });
3348};
3349
3350return Item;
3351
3352}));
3353
3354/**
3355 * Isotope LayoutMode
3356 */
3357
3358( function( window, factory ) {
3359  'use strict';
3360  // universal module definition
3361
3362  if ( typeof define == 'function' && define.amd ) {
3363    // AMD
3364    define( 'isotope/js/layout-mode',[
3365        'get-size/get-size',
3366        'outlayer/outlayer'
3367      ],
3368      factory );
3369  } else if ( typeof exports == 'object' ) {
3370    // CommonJS
3371    module.exports = factory(
3372      require('get-size'),
3373      require('outlayer')
3374    );
3375  } else {
3376    // browser global
3377    window.Isotope = window.Isotope || {};
3378    window.Isotope.LayoutMode = factory(
3379      window.getSize,
3380      window.Outlayer
3381    );
3382  }
3383
3384}( window, function factory( getSize, Outlayer ) {
3385  'use strict';
3386
3387  // layout mode class
3388  function LayoutMode( isotope ) {
3389    this.isotope = isotope;
3390    // link properties
3391    if ( isotope ) {
3392      this.options = isotope.options[ this.namespace ];
3393      this.element = isotope.element;
3394      this.items = isotope.filteredItems;
3395      this.size = isotope.size;
3396    }
3397  }
3398
3399  /**
3400   * some methods should just defer to default Outlayer method
3401   * and reference the Isotope instance as `this`
3402  **/
3403  ( function() {
3404    var facadeMethods = [
3405      '_resetLayout',
3406      '_getItemLayoutPosition',
3407      '_manageStamp',
3408      '_getContainerSize',
3409      '_getElementOffset',
3410      'needsResizeLayout'
3411    ];
3412
3413    for ( var i=0, len = facadeMethods.length; i < len; i++ ) {
3414      var methodName = facadeMethods[i];
3415      LayoutMode.prototype[ methodName ] = getOutlayerMethod( methodName );
3416    }
3417
3418    function getOutlayerMethod( methodName ) {
3419      return function() {
3420        return Outlayer.prototype[ methodName ].apply( this.isotope, arguments );
3421      };
3422    }
3423  })();
3424
3425  // -----  ----- //
3426
3427  // for horizontal layout modes, check vertical size
3428  LayoutMode.prototype.needsVerticalResizeLayout = function() {
vendor: 20,029 bytes, lines 3429-4164
3429    // don't trigger if size did not change
3430    var size = getSize( this.isotope.element );
3431    // check that this.size and size are there
3432    // IE8 triggers resize on body size change, so they might not be
3433    var hasSizes = this.isotope.size && size;
3434    return hasSizes && size.innerHeight != this.isotope.size.innerHeight;
3435  };
3436
3437  // ----- measurements ----- //
3438
3439  LayoutMode.prototype._getMeasurement = function() {
3440    this.isotope._getMeasurement.apply( this, arguments );
3441  };
3442
3443  LayoutMode.prototype.getColumnWidth = function() {
3444    this.getSegmentSize( 'column', 'Width' );
3445  };
3446
3447  LayoutMode.prototype.getRowHeight = function() {
3448    this.getSegmentSize( 'row', 'Height' );
3449  };
3450
3451  /**
3452   * get columnWidth or rowHeight
3453   * segment: 'column' or 'row'
3454   * size 'Width' or 'Height'
3455  **/
3456  LayoutMode.prototype.getSegmentSize = function( segment, size ) {
3457    var segmentName = segment + size;
3458    var outerSize = 'outer' + size;
3459    // columnWidth / outerWidth // rowHeight / outerHeight
3460    this._getMeasurement( segmentName, outerSize );
3461    // got rowHeight or columnWidth, we can chill
3462    if ( this[ segmentName ] ) {
3463      return;
3464    }
3465    // fall back to item of first element
3466    var firstItemSize = this.getFirstItemSize();
3467    this[ segmentName ] = firstItemSize && firstItemSize[ outerSize ] ||
3468      // or size of container
3469      this.isotope.size[ 'inner' + size ];
3470  };
3471
3472  LayoutMode.prototype.getFirstItemSize = function() {
3473    var firstItem = this.isotope.filteredItems[0];
3474    return firstItem && firstItem.element && getSize( firstItem.element );
3475  };
3476
3477  // ----- methods that should reference isotope ----- //
3478
3479  LayoutMode.prototype.layout = function() {
3480    this.isotope.layout.apply( this.isotope, arguments );
3481  };
3482
3483  LayoutMode.prototype.getSize = function() {
3484    this.isotope.getSize();
3485    this.size = this.isotope.size;
3486  };
3487
3488  // -------------------------- create -------------------------- //
3489
3490  LayoutMode.modes = {};
3491
3492  LayoutMode.create = function( namespace, options ) {
3493
3494    function Mode() {
3495      LayoutMode.apply( this, arguments );
3496    }
3497
3498    Mode.prototype = new LayoutMode();
3499
3500    // default options
3501    if ( options ) {
3502      Mode.options = options;
3503    }
3504
3505    Mode.prototype.namespace = namespace;
3506    // register in Isotope
3507    LayoutMode.modes[ namespace ] = Mode;
3508
3509    return Mode;
3510  };
3511
3512  return LayoutMode;
3513
3514}));
3515
3516/*!
3517 * Masonry v3.3.1
3518 * Cascading grid layout library
3519 * http://masonry.desandro.com
3520 * MIT License
3521 * by David DeSandro
3522 */
3523
3524( function( window, factory ) {
3525  'use strict';
3526  // universal module definition
3527  if ( typeof define === 'function' && define.amd ) {
3528    // AMD
3529    define( 'masonry/masonry',[
3530        'outlayer/outlayer',
3531        'get-size/get-size',
3532        'fizzy-ui-utils/utils'
3533      ],
3534      factory );
3535  } else if ( typeof exports === 'object' ) {
3536    // CommonJS
3537    module.exports = factory(
3538      require('outlayer'),
3539      require('get-size'),
3540      require('fizzy-ui-utils')
3541    );
3542  } else {
3543    // browser global
3544    window.Masonry = factory(
3545      window.Outlayer,
3546      window.getSize,
3547      window.fizzyUIUtils
3548    );
3549  }
3550
3551}( window, function factory( Outlayer, getSize, utils ) {
3552
3553
3554
3555// -------------------------- masonryDefinition -------------------------- //
3556
3557  // create an Outlayer layout class
3558  var Masonry = Outlayer.create('masonry');
3559
3560  Masonry.prototype._resetLayout = function() {
3561    this.getSize();
3562    this._getMeasurement( 'columnWidth', 'outerWidth' );
3563    this._getMeasurement( 'gutter', 'outerWidth' );
3564    this.measureColumns();
3565
3566    // reset column Y
3567    var i = this.cols;
3568    this.colYs = [];
3569    while (i--) {
3570      this.colYs.push( 0 );
3571    }
3572
3573    this.maxY = 0;
3574  };
3575
3576  Masonry.prototype.measureColumns = function() {
3577    this.getContainerWidth();
3578    // if columnWidth is 0, default to outerWidth of first item
3579    if ( !this.columnWidth ) {
3580      var firstItem = this.items[0];
3581      var firstItemElem = firstItem && firstItem.element;
3582      // columnWidth fall back to item of first element
3583      this.columnWidth = firstItemElem && getSize( firstItemElem ).outerWidth ||
3584        // if first elem has no width, default to size of container
3585        this.containerWidth;
3586    }
3587
3588    var columnWidth = this.columnWidth += this.gutter;
3589
3590    // calculate columns
3591    var containerWidth = this.containerWidth + this.gutter;
3592    var cols = containerWidth / columnWidth;
3593    // fix rounding errors, typically with gutters
3594    var excess = columnWidth - containerWidth % columnWidth;
3595    // if overshoot is less than a pixel, round up, otherwise floor it
3596    var mathMethod = excess && excess < 1 ? 'round' : 'floor';
3597    cols = Math[ mathMethod ]( cols );
3598    this.cols = Math.max( cols, 1 );
3599  };
3600
3601  Masonry.prototype.getContainerWidth = function() {
3602    // container is parent if fit width
3603    var container = this.options.isFitWidth ? this.element.parentNode : this.element;
3604    // check that this.size and size are there
3605    // IE8 triggers resize on body size change, so they might not be
3606    var size = getSize( container );
3607    this.containerWidth = size && size.innerWidth;
3608  };
3609
3610  Masonry.prototype._getItemLayoutPosition = function( item ) {
3611    item.getSize();
3612    // how many columns does this brick span
3613    var remainder = item.size.outerWidth % this.columnWidth;
3614    var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
3615    // round if off by 1 pixel, otherwise use ceil
3616    var colSpan = Math[ mathMethod ]( item.size.outerWidth / this.columnWidth );
3617    colSpan = Math.min( colSpan, this.cols );
3618
3619    var colGroup = this._getColGroup( colSpan );
3620    // get the minimum Y value from the columns
3621    var minimumY = Math.min.apply( Math, colGroup );
3622    var shortColIndex = utils.indexOf( colGroup, minimumY );
3623
3624    // position the brick
3625    var position = {
3626      x: this.columnWidth * shortColIndex,
3627      y: minimumY
3628    };
3629
3630    // apply setHeight to necessary columns
3631    var setHeight = minimumY + item.size.outerHeight;
3632    var setSpan = this.cols + 1 - colGroup.length;
3633    for ( var i = 0; i < setSpan; i++ ) {
3634      this.colYs[ shortColIndex + i ] = setHeight;
3635    }
3636
3637    return position;
3638  };
3639
3640  /**
3641   * @param {Number} colSpan - number of columns the element spans
3642   * @returns {Array} colGroup
3643   */
3644  Masonry.prototype._getColGroup = function( colSpan ) {
3645    if ( colSpan < 2 ) {
3646      // if brick spans only one column, use all the column Ys
3647      return this.colYs;
3648    }
3649
3650    var colGroup = [];
3651    // how many different places could this brick fit horizontally
3652    var groupCount = this.cols + 1 - colSpan;
3653    // for each group potential horizontal position
3654    for ( var i = 0; i < groupCount; i++ ) {
3655      // make an array of colY values for that one group
3656      var groupColYs = this.colYs.slice( i, i + colSpan );
3657      // and get the max value of the array
3658      colGroup[i] = Math.max.apply( Math, groupColYs );
3659    }
3660    return colGroup;
3661  };
3662
3663  Masonry.prototype._manageStamp = function( stamp ) {
3664    var stampSize = getSize( stamp );
3665    var offset = this._getElementOffset( stamp );
3666    // get the columns that this stamp affects
3667    var firstX = this.options.isOriginLeft ? offset.left : offset.right;
3668    var lastX = firstX + stampSize.outerWidth;
3669    var firstCol = Math.floor( firstX / this.columnWidth );
3670    firstCol = Math.max( 0, firstCol );
3671    var lastCol = Math.floor( lastX / this.columnWidth );
3672    // lastCol should not go over if multiple of columnWidth #425
3673    lastCol -= lastX % this.columnWidth ? 0 : 1;
3674    lastCol = Math.min( this.cols - 1, lastCol );
3675    // set colYs to bottom of the stamp
3676    var stampMaxY = ( this.options.isOriginTop ? offset.top : offset.bottom ) +
3677      stampSize.outerHeight;
3678    for ( var i = firstCol; i <= lastCol; i++ ) {
3679      this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
3680    }
3681  };
3682
3683  Masonry.prototype._getContainerSize = function() {
3684    this.maxY = Math.max.apply( Math, this.colYs );
3685    var size = {
3686      height: this.maxY
3687    };
3688
3689    if ( this.options.isFitWidth ) {
3690      size.width = this._getContainerFitWidth();
3691    }
3692
3693    return size;
3694  };
3695
3696  Masonry.prototype._getContainerFitWidth = function() {
3697    var unusedCols = 0;
3698    // count unused columns
3699    var i = this.cols;
3700    while ( --i ) {
3701      if ( this.colYs[i] !== 0 ) {
3702        break;
3703      }
3704      unusedCols++;
3705    }
3706    // fit container to columns that have been used
3707    return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
3708  };
3709
3710  Masonry.prototype.needsResizeLayout = function() {
3711    var previousWidth = this.containerWidth;
3712    this.getContainerWidth();
3713    return previousWidth !== this.containerWidth;
3714  };
3715
3716  return Masonry;
3717
3718}));
3719
3720/*!
3721 * Masonry layout mode
3722 * sub-classes Masonry
3723 * http://masonry.desandro.com
3724 */
3725
3726( function( window, factory ) {
3727  'use strict';
3728  // universal module definition
3729  if ( typeof define == 'function' && define.amd ) {
3730    // AMD
3731    define( 'isotope/js/layout-modes/masonry',[
3732        '../layout-mode',
3733        'masonry/masonry'
3734      ],
3735      factory );
3736  } else if ( typeof exports == 'object' ) {
3737    // CommonJS
3738    module.exports = factory(
3739      require('../layout-mode'),
3740      require('masonry-layout')
3741    );
3742  } else {
3743    // browser global
3744    factory(
3745      window.Isotope.LayoutMode,
3746      window.Masonry
3747    );
3748  }
3749
3750}( window, function factory( LayoutMode, Masonry ) {
3751'use strict';
3752
3753// -------------------------- helpers -------------------------- //
3754
3755// extend objects
3756function extend( a, b ) {
3757  for ( var prop in b ) {
3758    a[ prop ] = b[ prop ];
3759  }
3760  return a;
3761}
3762
3763// -------------------------- masonryDefinition -------------------------- //
3764
3765  // create an Outlayer layout class
3766  var MasonryMode = LayoutMode.create('masonry');
3767
3768  // save on to these methods
3769  var _getElementOffset = MasonryMode.prototype._getElementOffset;
3770  var layout = MasonryMode.prototype.layout;
3771  var _getMeasurement = MasonryMode.prototype._getMeasurement;
3772
3773  // sub-class Masonry
3774  extend( MasonryMode.prototype, Masonry.prototype );
3775
3776  // set back, as it was overwritten by Masonry
3777  MasonryMode.prototype._getElementOffset = _getElementOffset;
3778  MasonryMode.prototype.layout = layout;
3779  MasonryMode.prototype._getMeasurement = _getMeasurement;
3780
3781  var measureColumns = MasonryMode.prototype.measureColumns;
3782  MasonryMode.prototype.measureColumns = function() {
3783    // set items, used if measuring first item
3784    this.items = this.isotope.filteredItems;
3785    measureColumns.call( this );
3786  };
3787
3788  // HACK copy over isOriginLeft/Top options
3789  var _manageStamp = MasonryMode.prototype._manageStamp;
3790  MasonryMode.prototype._manageStamp = function() {
3791    this.options.isOriginLeft = this.isotope.options.isOriginLeft;
3792    this.options.isOriginTop = this.isotope.options.isOriginTop;
3793    _manageStamp.apply( this, arguments );
3794  };
3795
3796  return MasonryMode;
3797
3798}));
3799
3800/**
3801 * fitRows layout mode
3802 */
3803
3804( function( window, factory ) {
3805  'use strict';
3806  // universal module definition
3807  if ( typeof define == 'function' && define.amd ) {
3808    // AMD
3809    define( 'isotope/js/layout-modes/fit-rows',[
3810        '../layout-mode'
3811      ],
3812      factory );
3813  } else if ( typeof exports == 'object' ) {
3814    // CommonJS
3815    module.exports = factory(
3816      require('../layout-mode')
3817    );
3818  } else {
3819    // browser global
3820    factory(
3821      window.Isotope.LayoutMode
3822    );
3823  }
3824
3825}( window, function factory( LayoutMode ) {
3826'use strict';
3827
3828var FitRows = LayoutMode.create('fitRows');
3829
3830FitRows.prototype._resetLayout = function() {
3831  this.x = 0;
3832  this.y = 0;
3833  this.maxY = 0;
3834  this._getMeasurement( 'gutter', 'outerWidth' );
3835};
3836
3837FitRows.prototype._getItemLayoutPosition = function( item ) {
3838  item.getSize();
3839
3840  var itemWidth = item.size.outerWidth + this.gutter;
3841  // if this element cannot fit in the current row
3842  var containerWidth = this.isotope.size.innerWidth + this.gutter;
3843  if ( this.x !== 0 && itemWidth + this.x > containerWidth ) {
3844    this.x = 0;
3845    this.y = this.maxY;
3846  }
3847
3848  var position = {
3849    x: this.x,
3850    y: this.y
3851  };
3852
3853  this.maxY = Math.max( this.maxY, this.y + item.size.outerHeight );
3854  this.x += itemWidth;
3855
3856  return position;
3857};
3858
3859FitRows.prototype._getContainerSize = function() {
3860  return { height: this.maxY };
3861};
3862
3863return FitRows;
3864
3865}));
3866
3867/**
3868 * vertical layout mode
3869 */
3870
3871( function( window, factory ) {
3872  'use strict';
3873  // universal module definition
3874  if ( typeof define == 'function' && define.amd ) {
3875    // AMD
3876    define( 'isotope/js/layout-modes/vertical',[
3877        '../layout-mode'
3878      ],
3879      factory );
3880  } else if ( typeof exports == 'object' ) {
3881    // CommonJS
3882    module.exports = factory(
3883      require('../layout-mode')
3884    );
3885  } else {
3886    // browser global
3887    factory(
3888      window.Isotope.LayoutMode
3889    );
3890  }
3891
3892}( window, function factory( LayoutMode ) {
3893'use strict';
3894
3895var Vertical = LayoutMode.create( 'vertical', {
3896  horizontalAlignment: 0
3897});
3898
3899Vertical.prototype._resetLayout = function() {
3900  this.y = 0;
3901};
3902
3903Vertical.prototype._getItemLayoutPosition = function( item ) {
3904  item.getSize();
3905  var x = ( this.isotope.size.innerWidth - item.size.outerWidth ) *
3906    this.options.horizontalAlignment;
3907  var y = this.y;
3908  this.y += item.size.outerHeight;
3909  return { x: x, y: y };
3910};
3911
3912Vertical.prototype._getContainerSize = function() {
3913  return { height: this.y };
3914};
3915
3916return Vertical;
3917
3918}));
3919
3920/*!
3921 * Isotope v2.2.2
3922 *
3923 * Licensed GPLv3 for open source use
3924 * or Isotope Commercial License for commercial use
3925 *
3926 * http://isotope.metafizzy.co
3927 * Copyright 2015 Metafizzy
3928 */
3929
3930( function( window, factory ) {
3931  'use strict';
3932  // universal module definition
3933
3934  if ( typeof define == 'function' && define.amd ) {
3935    // AMD
3936    define( [
3937        'outlayer/outlayer',
3938        'get-size/get-size',
3939        'matches-selector/matches-selector',
3940        'fizzy-ui-utils/utils',
3941        'isotope/js/item',
3942        'isotope/js/layout-mode',
3943        // include default layout modes
3944        'isotope/js/layout-modes/masonry',
3945        'isotope/js/layout-modes/fit-rows',
3946        'isotope/js/layout-modes/vertical'
3947      ],
3948      function( Outlayer, getSize, matchesSelector, utils, Item, LayoutMode ) {
3949        return factory( window, Outlayer, getSize, matchesSelector, utils, Item, LayoutMode );
3950      });
3951  } else if ( typeof exports == 'object' ) {
3952    // CommonJS
3953    module.exports = factory(
3954      window,
3955      require('outlayer'),
3956      require('get-size'),
3957      require('desandro-matches-selector'),
3958      require('fizzy-ui-utils'),
3959      require('./item'),
3960      require('./layout-mode'),
3961      // include default layout modes
3962      require('./layout-modes/masonry'),
3963      require('./layout-modes/fit-rows'),
3964      require('./layout-modes/vertical')
3965    );
3966  } else {
3967    // browser global
3968    window.Isotope = factory(
3969      window,
3970      window.Outlayer,
3971      window.getSize,
3972      window.matchesSelector,
3973      window.fizzyUIUtils,
3974      window.Isotope.Item,
3975      window.Isotope.LayoutMode
3976    );
3977  }
3978
3979}( window, function factory( window, Outlayer, getSize, matchesSelector, utils,
3980  Item, LayoutMode ) {
3981
3982
3983
3984// -------------------------- vars -------------------------- //
3985
3986var jQuery = window.jQuery;
3987
3988// -------------------------- helpers -------------------------- //
3989
3990var trim = String.prototype.trim ?
3991  function( str ) {
3992    return str.trim();
3993  } :
3994  function( str ) {
3995    return str.replace( /^\s+|\s+$/g, '' );
3996  };
3997
3998var docElem = document.documentElement;
3999
4000var getText = docElem.textContent ?
4001  function( elem ) {
4002    return elem.textContent;
4003  } :
4004  function( elem ) {
4005    return elem.innerText;
4006  };
4007
4008// -------------------------- isotopeDefinition -------------------------- //
4009
4010  // create an Outlayer layout class
4011  var Isotope = Outlayer.create( 'isotope', {
4012    layoutMode: "masonry",
4013    isJQueryFiltering: true,
4014    sortAscending: true
4015  });
4016
4017  Isotope.Item = Item;
4018  Isotope.LayoutMode = LayoutMode;
4019
4020  Isotope.prototype._create = function() {
4021    this.itemGUID = 0;
4022    // functions that sort items
4023    this._sorters = {};
4024    this._getSorters();
4025    // call super
4026    Outlayer.prototype._create.call( this );
4027
4028    // create layout modes
4029    this.modes = {};
4030    // start filteredItems with all items
4031    this.filteredItems = this.items;
4032    // keep of track of sortBys
4033    this.sortHistory = [ 'original-order' ];
4034    // create from registered layout modes
4035    for ( var name in LayoutMode.modes ) {
4036      this._initLayoutMode( name );
4037    }
4038  };
4039
4040  Isotope.prototype.reloadItems = function() {
4041    // reset item ID counter
4042    this.itemGUID = 0;
4043    // call super
4044    Outlayer.prototype.reloadItems.call( this );
4045  };
4046
4047  Isotope.prototype._itemize = function() {
4048    var items = Outlayer.prototype._itemize.apply( this, arguments );
4049    // assign ID for original-order
4050    for ( var i=0, len = items.length; i < len; i++ ) {
4051      var item = items[i];
4052      item.id = this.itemGUID++;
4053    }
4054    this._updateItemsSortData( items );
4055    return items;
4056  };
4057
4058
4059  // -------------------------- layout -------------------------- //
4060
4061  Isotope.prototype._initLayoutMode = function( name ) {
4062    var Mode = LayoutMode.modes[ name ];
4063    // set mode options
4064    // HACK extend initial options, back-fill in default options
4065    var initialOpts = this.options[ name ] || {};
4066    this.options[ name ] = Mode.options ?
4067      utils.extend( Mode.options, initialOpts ) : initialOpts;
4068    // init layout mode instance
4069    this.modes[ name ] = new Mode( this );
4070  };
4071
4072
4073  Isotope.prototype.layout = function() {
4074    // if first time doing layout, do all magic
4075    if ( !this._isLayoutInited && this.options.isInitLayout ) {
4076      this.arrange();
4077      return;
4078    }
4079    this._layout();
4080  };
4081
4082  // private method to be used in layout() & magic()
4083  Isotope.prototype._layout = function() {
4084    // don't animate first layout
4085    var isInstant = this._getIsInstant();
4086    // layout flow
4087    this._resetLayout();
4088    this._manageStamps();
4089    this.layoutItems( this.filteredItems, isInstant );
4090
4091    // flag for initalized
4092    this._isLayoutInited = true;
4093  };
4094
4095  // filter + sort + layout
4096  Isotope.prototype.arrange = function( opts ) {
4097    // set any options pass
4098    this.option( opts );
4099    this._getIsInstant();
4100    // filter, sort, and layout
4101
4102    // filter
4103    var filtered = this._filter( this.items );
4104    this.filteredItems = filtered.matches;
4105
4106    var _this = this;
4107    function hideReveal() {
4108      _this.reveal( filtered.needReveal );
4109      _this.hide( filtered.needHide );
4110    }
4111
4112    this._bindArrangeComplete();
4113
4114    if ( this._isInstant ) {
4115      this._noTransition( hideReveal );
4116    } else {
4117      hideReveal();
4118    }
4119
4120    this._sort();
4121    this._layout();
4122  };
4123  // alias to _init for main plugin method
4124  Isotope.prototype._init = Isotope.prototype.arrange;
4125
4126  // HACK
4127  // Don't animate/transition first layout
4128  // Or don't animate/transition other layouts
4129  Isotope.prototype._getIsInstant = function() {
4130    var isInstant = this.options.isLayoutInstant !== undefined ?
4131      this.options.isLayoutInstant : !this._isLayoutInited;
4132    this._isInstant = isInstant;
4133    return isInstant;
4134  };
4135
4136  // listen for layoutComplete, hideComplete and revealComplete
4137  // to trigger arrangeComplete
4138  Isotope.prototype._bindArrangeComplete = function() {
4139    // listen for 3 events to trigger arrangeComplete
4140    var isLayoutComplete, isHideComplete, isRevealComplete;
4141    var _this = this;
4142    function arrangeParallelCallback() {
4143      if ( isLayoutComplete && isHideComplete && isRevealComplete ) {
4144        _this.dispatchEvent( 'arrangeComplete', null, [ _this.filteredItems ] );
4145      }
4146    }
4147    this.once( 'layoutComplete', function() {
4148      isLayoutComplete = true;
4149      arrangeParallelCallback();
4150    });
4151    this.once( 'hideComplete', function() {
4152      isHideComplete = true;
4153      arrangeParallelCallback();
4154    });
4155    this.once( 'revealComplete', function() {
4156      isRevealComplete = true;
4157      arrangeParallelCallback();
4158    });
4159  };
4160
4161  // -------------------------- filter -------------------------- //
4162
4163  Isotope.prototype._filter = function( items ) {
4164    var filter = this.options.filter;
4165    filter = filter || '*';
4166    var matches = [];
4167    var hiddenMatched = [];
4168    var visibleUnmatched = [];
4169
4170    var test = this._getFilterTest( filter );
4171
4172    // test each item
4173    for ( var i=0, len = items.length; i < len; i++ ) {
4174      var item = items[i];
4175      if ( item.isIgnored ) {
4176        continue;
4177      }
4178      // add item to either matched or unmatched group
4179      var isMatched = test( item );
4180      // item.isFilterMatched = isMatched;
4181      // add to matches if its a match
4182      if ( isMatched ) {
4183        matches.push( item );
4184      }
4185      // add to additional group if item needs to be hidden or revealed
4186      if ( isMatched && item.isHidden ) {
4187        hiddenMatched.push( item );
4188      } else if ( !isMatched && !item.isHidden ) {
4189        visibleUnmatched.push( item );
4190      }
4191    }
4192
4193    // return collections of items to be manipulated
4194    return {
4195      matches: matches,
4196      needReveal: hiddenMatched,
4197      needHide: visibleUnmatched
4198    };
4199  };
4200
4201  // get a jQuery, function, or a matchesSelector test given the filter
4202  Isotope.prototype._getFilterTest = function( filter ) {
4203    if ( jQuery && this.options.isJQueryFiltering ) {
4204      // use jQuery
4205      return function( item ) {
4206        return jQuery( item.element ).is( filter );
4207      };
4208    }
4209    if ( typeof filter == 'function' ) {
4210      // use filter as function
4211      return function( item ) {
4212        return filter( item.element );
4213      };
4214    }
4215    // default, use filter as selector string
4216    return function( item ) {
4217      return matchesSelector( item.element, filter );
4218    };
4219  };
4220
4221  // -------------------------- sorting -------------------------- //
4222
4223  /**
4224   * @params {Array} elems
4225   * @public
4226   */
4227  Isotope.prototype.updateSortData = function( elems ) {
4228    // get items
4229    var items;
4230    if ( elems ) {
4231      elems = utils.makeArray( elems );
4232      items = this.getItems( elems );
4233    } else {
4234      // update all items if no elems provided
4235      items = this.items;
4236    }
4237
4238    this._getSorters();
4239    this._updateItemsSortData( items );
4240  };
4241
4242  Isotope.prototype._getSorters = function() {
4243    var getSortData = this.options.getSortData;
4244    for ( var key in getSortData ) {
4245      var sorter = getSortData[ key ];
4246      this._sorters[ key ] = mungeSorter( sorter );
4247    }
4248  };
4249
4250  /**
4251   * @params {Array} items - of Isotope.Items
4252   * @private
4253   */
4254  Isotope.prototype._updateItemsSortData = function( items ) {
4255    // do not update if no items
4256    var len = items && items.length;
4257
4258    for ( var i=0; len && i < len; i++ ) {
4259      var item = items[i];
4260      item.updateSortData();
4261    }
4262  };
4263
4264  // ----- munge sorter ----- //
4265
4266  // encapsulate this, as we just need mungeSorter
4267  // other functions in here are just for munging
4268  var mungeSorter = ( function() {
4269    // add a magic layer to sorters for convienent shorthands
4270    // `.foo-bar` will use the text of .foo-bar querySelector
4271    // `[foo-bar]` will use attribute
4272    // you can also add parser
4273    // `.foo-bar parseInt` will parse that as a number
4274    function mungeSorter( sorter ) {
4275      // if not a string, return function or whatever it is
4276      if ( typeof sorter != 'string' ) {
4277        return sorter;
4278      }
4279      // parse the sorter string
4280      var args = trim( sorter ).split(' ');
4281      var query = args[0];
4282      // check if query looks like [an-attribute]
4283      var attrMatch = query.match( /^\[(.+)\]$/ );
4284      var attr = attrMatch && attrMatch[1];
4285      var getValue = getValueGetter( attr, query );
4286      // use second argument as a parser
4287      var parser = Isotope.sortDataParsers[ args[1] ];
4288      // parse the value, if there was a parser
4289      sorter = parser ? function( elem ) {
4290        return elem && parser( getValue( elem ) );
4291      } :
4292      // otherwise just return value
4293      function( elem ) {
4294        return elem && getValue( elem );
4295      };
4296
4297      return sorter;
4298    }
4299
4300    // get an attribute getter, or get text of the querySelector
4301    function getValueGetter( attr, query ) {
4302      var getValue;
4303      // if query looks like [foo-bar], get attribute
4304      if ( attr ) {
4305        getValue = function( elem ) {
4306          return elem.getAttribute( attr );
4307        };
4308      } else {
4309        // otherwise, assume its a querySelector, and get its text
4310        getValue = function( elem ) {
4311          var child = elem.querySelector( query );
4312          return child && getText( child );
4313        };
4314      }
4315      return getValue;
4316    }
4317
4318    return mungeSorter;
4319  })();
4320
4321  // parsers used in getSortData shortcut strings
4322  Isotope.sortDataParsers = {
4323    'parseInt': function( val ) {
4324      return parseInt( val, 10 );
4325    },
4326    'parseFloat': function( val ) {
4327      return parseFloat( val );
4328    }
4329  };
4330
4331  // ----- sort method ----- //
4332
4333  // sort filteredItem order
4334  Isotope.prototype._sort = function() {
4335    var sortByOpt = this.options.sortBy;
4336    if ( !sortByOpt ) {
4337      return;
4338    }
4339    // concat all sortBy and sortHistory
4340    var sortBys = [].concat.apply( sortByOpt, this.sortHistory );
4341    // sort magic
4342    var itemSorter = getItemSorter( sortBys, this.options.sortAscending );
4343    this.filteredItems.sort( itemSorter );
4344    // keep track of sortBy History
4345    if ( sortByOpt != this.sortHistory[0] ) {
4346      // add to front, oldest goes in last
4347      this.sortHistory.unshift( sortByOpt );
4348    }
4349  };
4350
4351  // returns a function used for sorting
4352  function getItemSorter( sortBys, sortAsc ) {
4353    return function sorter( itemA, itemB ) {
4354      // cycle through all sortKeys
4355      for ( var i = 0, len = sortBys.length; i < len; i++ ) {
4356        var sortBy = sortBys[i];
4357        var a = itemA.sortData[ sortBy ];
4358        var b = itemB.sortData[ sortBy ];
4359        if ( a > b || a < b ) {
4360          // if sortAsc is an object, use the value given the sortBy key
4361          var isAscending = sortAsc[ sortBy ] !== undefined ? sortAsc[ sortBy ] : sortAsc;
4362          var direction = isAscending ? 1 : -1;
4363          return ( a > b ? 1 : -1 ) * direction;
4364        }
4365      }
4366      return 0;
4367    };
4368  }
4369
4370  // -------------------------- methods -------------------------- //
4371
4372  // get layout mode
4373  Isotope.prototype._mode = function() {
4374    var layoutMode = this.options.layoutMode;
4375    var mode = this.modes[ layoutMode ];
4376    if ( !mode ) {
4377      // TODO console.error
4378      throw new Error( 'No layout mode: ' + layoutMode );
4379    }
4380    // HACK sync mode's options
4381    // any options set after init for layout mode need to be synced
4382    mode.options = this.options[ layoutMode ];
4383    return mode;
4384  };
4385
4386  Isotope.prototype._resetLayout = function() {
4387    // trigger original reset layout
4388    Outlayer.prototype._resetLayout.call( this );
4389    this._mode()._resetLayout();
4390  };
4391
4392  Isotope.prototype._getItemLayoutPosition = function( item  ) {
4393    return this._mode()._getItemLayoutPosition( item );
4394  };
4395
4396  Isotope.prototype._manageStamp = function( stamp ) {
4397    this._mode()._manageStamp( stamp );
4398  };
4399
4400  Isotope.prototype._getContainerSize = function() {
4401    return this._mode()._getContainerSize();
4402  };
4403
4404  Isotope.prototype.needsResizeLayout = function() {
4405    return this._mode().needsResizeLayout();
4406  };
4407
4408  // -------------------------- adding & removing -------------------------- //
4409
4410  // HEADS UP overwrites default Outlayer appended
4411  Isotope.prototype.appended = function( elems ) {
4412    var items = this.addItems( elems );
4413    if ( !items.length ) {
4414      return;
4415    }
4416    // filter, layout, reveal new items
4417    var filteredItems = this._filterRevealAdded( items );
4418    // add to filteredItems
4419    this.filteredItems = this.filteredItems.concat( filteredItems );
4420  };
4421
4422  // HEADS UP overwrites default Outlayer prepended
4423  Isotope.prototype.prepended = function( elems ) {
4424    var items = this._itemize( elems );
4425    if ( !items.length ) {
4426      return;
4427    }
4428    // start new layout
4429    this._resetLayout();
4430    this._manageStamps();
4431    // filter, layout, reveal new items
4432    var filteredItems = this._filterRevealAdded( items );
4433    // layout previous items
4434    this.layoutItems( this.filteredItems );
4435    // add to items and filteredItems
4436    this.filteredItems = filteredItems.concat( this.filteredItems );
4437    this.items = items.concat( this.items );
4438  };
4439
4440  Isotope.prototype._filterRevealAdded = function( items ) {
4441    var filtered = this._filter( items );
4442    this.hide( filtered.needHide );
4443    // reveal all new items
4444    this.reveal( filtered.matches );
4445    // layout new items, no transition
4446    this.layoutItems( filtered.matches, true );
4447    return filtered.matches;
4448  };
4449
4450  /**
4451   * Filter, sort, and layout newly-appended item elements
4452   * @param {Array or NodeList or Element} elems
4453   */
4454  Isotope.prototype.insert = function( elems ) {
4455    var items = this.addItems( elems );
4456    if ( !items.length ) {
4457      return;
4458    }
4459    // append item elements
4460    var i, item;
4461    var len = items.length;
4462    for ( i=0; i < len; i++ ) {
4463      item = items[i];
4464      this.element.appendChild( item.element );
4465    }
4466    // filter new stuff
4467    var filteredInsertItems = this._filter( items ).matches;
4468    // set flag
4469    for ( i=0; i < len; i++ ) {
4470      items[i].isLayoutInstant = true;
4471    }
4472    this.arrange();
4473    // reset flag
4474    for ( i=0; i < len; i++ ) {
4475      delete items[i].isLayoutInstant;
4476    }
4477    this.reveal( filteredInsertItems );
4478  };
4479
4480  var _remove = Isotope.prototype.remove;
4481  Isotope.prototype.remove = function( elems ) {
4482    elems = utils.makeArray( elems );
4483    var removeItems = this.getItems( elems );
4484    // do regular thing
4485    _remove.call( this, elems );
4486    // bail if no items to remove
4487    var len = removeItems && removeItems.length;
4488    if ( !len ) {
4489      return;
4490    }
4491    // remove elems from filteredItems
4492    for ( var i=0; i < len; i++ ) {
4493      var item = removeItems[i];
4494      // remove item from collection
4495      utils.removeFrom( this.filteredItems, item );
4496    }
4497  };
4498
4499  Isotope.prototype.shuffle = function() {
4500    // update random sortData
4501    for ( var i=0, len = this.items.length; i < len; i++ ) {
4502      var item = this.items[i];
4503      item.sortData.random = Math.random();
4504    }
4505    this.options.sortBy = 'random';
4506    this._sort();
4507    this._layout();
4508  };
4509
4510  /**
4511   * trigger fn without transition
4512   * kind of hacky to have this in the first place
4513   * @param {Function} fn
4514   * @returns ret
4515   * @private
4516   */
4517  Isotope.prototype._noTransition = function( fn ) {
4518    // save transitionDuration before disabling
4519    var transitionDuration = this.options.transitionDuration;
4520    // disable transition
4521    this.options.transitionDuration = 0;
4522    // do it
4523    var returnValue = fn.call( this );
4524    // re-enable transition for reveal
4525    this.options.transitionDuration = transitionDuration;
4526    return returnValue;
4527  };
4528
4529  // ----- helper methods ----- //
4530
4531  /**
4532   * getter method for getting filtered item elements
4533   * @returns {Array} elems - collection of item elements
4534   */
4535  Isotope.prototype.getFilteredItemElements = function() {
4536    var elems = [];
4537    for ( var i=0, len = this.filteredItems.length; i < len; i++ ) {
4538      elems.push( this.filteredItems[i].element );
4539    }
4540    return elems;
4541  };
4542
4543  // -----  ----- //
4544
4545  return Isotope;
4546
4547}));
4548
4549
4550/*!
4551 * Packery layout mode PACKAGED v2.0.1
4552 * sub-classes Packery
4553 */
4554
4555!function(a,b){"function"==typeof define&&define.amd?define("packery/js/rect",b):"object"==typeof module&&module.exports?module.exports=b():(a.Packery=a.Packery||{},a.Packery.Rect=b())}(window,function(){function a(b){for(var c in a.defaults)this[c]=a.defaults[c];for(c in b)this[c]=b[c]}a.defaults={x:0,y:0,width:0,height:0};var b=a.prototype;return b.contains=function(a){var b=a.width||0,c=a.height||0;return this.x<=a.x&&this.y<=a.y&&this.x+this.width>=a.x+b&&this.y+this.height>=a.y+c},b.overlaps=function(a){var b=this.x+this.width,c=this.y+this.height,d=a.x+a.width,e=a.y+a.height;return this.x<d&&b>a.x&&this.y<e&&c>a.y},b.getMaximalFreeRects=function(b){if(!this.overlaps(b))return!1;var c,d=[],e=this.x+this.width,f=this.y+this.height,g=b.x+b.width,h=b.y+b.height;return this.y<b.y&&(c=new a({x:this.x,y:this.y,width:this.width,height:b.y-this.y}),d.push(c)),e>g&&(c=new a({x:g,y:this.y,width:e-g,height:this.height}),d.push(c)),f>h&&(c=new a({x:this.x,y:h,width:this.width,height:f-h}),d.push(c)),this.x<b.x&&(c=new a({x:this.x,y:this.y,width:b.x-this.x,height:this.height}),d.push(c)),d},b.canFit=function(a){return this.width>=a.width&&this.height>=a.height},a}),function(a,b){if("function"==typeof define&&define.amd)define("packery/js/packer",["./rect"],b);else if("object"==typeof module&&module.exports)module.exports=b(require("./rect"));else{var c=a.Packery=a.Packery||{};c.Packer=b(c.Rect)}}(window,function(a){function b(a,b,c){this.width=a||0,this.height=b||0,this.sortDirection=c||"downwardLeftToRight",this.reset()}var c=b.prototype;c.reset=function(){this.spaces=[];var b=new a({x:0,y:0,width:this.width,height:this.height});this.spaces.push(b),this.sorter=d[this.sortDirection]||d.downwardLeftToRight},c.pack=function(a){for(var b=0;b<this.spaces.length;b++){var c=this.spaces[b];if(c.canFit(a)){this.placeInSpace(a,c);break}}},c.columnPack=function(a){for(var b=0;b<this.spaces.length;b++){var c=this.spaces[b],d=c.x<=a.x&&c.x+c.width>=a.x+a.width&&c.height>=a.height-.01;if(d){a.y=c.y,this.placed(a);break}}},c.rowPack=function(a){for(var b=0;b<this.spaces.length;b++){var c=this.spaces[b],d=c.y<=a.y&&c.y+c.height>=a.y+a.height&&c.width>=a.width-.01;if(d){a.x=c.x,this.placed(a);break}}},c.placeInSpace=function(a,b){a.x=b.x,a.y=b.y,this.placed(a)},c.placed=function(a){for(var b=[],c=0;c<this.spaces.length;c++){var d=this.spaces[c],e=d.getMaximalFreeRects(a);
vendor: 8,438 bytes, line 4555
4555e?b.push.apply(b,e):b.push(d)}this.spaces=b,this.mergeSortSpaces()},c.mergeSortSpaces=function(){b.mergeRects(this.spaces),this.spaces.sort(this.sorter)},c.addSpace=function(a){this.spaces.push(a),this.mergeSortSpaces()},b.mergeRects=function(a){var b=0,c=a[b];a:for(;c;){for(var d=0,e=a[b+d];e;){if(e==c)d++;else{if(e.contains(c)){a.splice(b,1),c=a[b];continue a}c.contains(e)?a.splice(b+d,1):d++}e=a[b+d]}b++,c=a[b]}return a};var d={downwardLeftToRight:function(a,b){return a.y-b.y||a.x-b.x},rightwardTopToBottom:function(a,b){return a.x-b.x||a.y-b.y}};return b}),function(a,b){"function"==typeof define&&define.amd?define("packery/js/item",["outlayer/outlayer","./rect"],b):"object"==typeof module&&module.exports?module.exports=b(require("outlayer"),require("./rect")):a.Packery.Item=b(a.Outlayer,a.Packery.Rect)}(window,function(a,b){var c=document.documentElement.style,d="string"==typeof c.transform?"transform":"WebkitTransform",e=function(){a.Item.apply(this,arguments)},f=e.prototype=Object.create(a.Item.prototype),g=f._create;f._create=function(){g.call(this),this.rect=new b};var h=f.moveTo;return f.moveTo=function(a,b){var c=Math.abs(this.position.x-a),d=Math.abs(this.position.y-b),e=this.layout.dragItemCount&&!this.isPlacing&&!this.isTransitioning&&1>c&&1>d;return e?void this.goTo(a,b):void h.apply(this,arguments)},f.enablePlacing=function(){this.removeTransitionStyles(),this.isTransitioning&&d&&(this.element.style[d]="none"),this.isTransitioning=!1,this.getSize(),this.layout._setRectSize(this.element,this.rect),this.isPlacing=!0},f.disablePlacing=function(){this.isPlacing=!1},f.removeElem=function(){this.element.parentNode.removeChild(this.element),this.layout.packer.addSpace(this.rect),this.emitEvent("remove",[this])},f.showDropPlaceholder=function(){var a=this.dropPlaceholder;a||(a=this.dropPlaceholder=document.createElement("div"),a.className="packery-drop-placeholder",a.style.position="absolute"),a.style.width=this.size.width+"px",a.style.height=this.size.height+"px",this.positionDropPlaceholder(),this.layout.element.appendChild(a)},f.positionDropPlaceholder=function(){this.dropPlaceholder.style[d]="translate("+this.rect.x+"px, "+this.rect.y+"px)"},f.hideDropPlaceholder=function(){this.layout.element.removeChild(this.dropPlaceholder)},e}),function(a,b){"function"==typeof define&&define.amd?define("packery/js/packery",["get-size/get-size","outlayer/outlayer","./rect","./packer","./item"],b):"object"==typeof module&&module.exports?module.exports=b(require("get-size"),require("outlayer"),require("./rect"),require("./packer"),require("./item")):a.Packery=b(a.getSize,a.Outlayer,a.Packery.Rect,a.Packery.Packer,a.Packery.Item)}(window,function(a,b,c,d,e){function f(a,b){return a.position.y-b.position.y||a.position.x-b.position.x}function g(a,b){return a.position.x-b.position.x||a.position.y-b.position.y}function h(a,b){var c=b.x-a.x,d=b.y-a.y;return Math.sqrt(c*c+d*d)}c.prototype.canFit=function(a){return this.width>=a.width-1&&this.height>=a.height-1};var i=b.create("packery");i.Item=e;var j=i.prototype;j._create=function(){b.prototype._create.call(this),this.packer=new d,this.shiftPacker=new d,this.isEnabled=!0,this.dragItemCount=0;var a=this;this.handleDraggabilly={dragStart:function(){a.itemDragStart(this.element)},dragMove:function(){a.itemDragMove(this.element,this.position.x,this.position.y)},dragEnd:function(){a.itemDragEnd(this.element)}},this.handleUIDraggable={start:function(b,c){c&&a.itemDragStart(b.currentTarget)},drag:function(b,c){c&&a.itemDragMove(b.currentTarget,c.position.left,c.position.top)},stop:function(b,c){c&&a.itemDragEnd(b.currentTarget)}}},j._resetLayout=function(){this.getSize(),this._getMeasurements();var a,b,c;this._getOption("horizontal")?(a=1/0,b=this.size.innerHeight+this.gutter,c="rightwardTopToBottom"):(a=this.size.innerWidth+this.gutter,b=1/0,c="downwardLeftToRight"),this.packer.width=this.shiftPacker.width=a,this.packer.height=this.shiftPacker.height=b,this.packer.sortDirection=this.shiftPacker.sortDirection=c,this.packer.reset(),this.maxY=0,this.maxX=0},j._getMeasurements=function(){this._getMeasurement("columnWidth","width"),this._getMeasurement("rowHeight","height"),this._getMeasurement("gutter","width")},j._getItemLayoutPosition=function(a){if(this._setRectSize(a.element,a.rect),this.isShifting||this.dragItemCount>0){var b=this._getPackMethod();this.packer[b](a.rect)}else this.packer.pack(a.rect);return this._setMaxXY(a.rect),a.rect},j.shiftLayout=function(){this.isShifting=!0,this.layout(),delete this.isShifting},j._getPackMethod=function(){return this._getOption("horizontal")?"rowPack":"columnPack"},j._setMaxXY=function(a){this.maxX=Math.max(a.x+a.width,this.maxX),this.maxY=Math.max(a.y+a.height,this.maxY)},j._setRectSize=function(b,c){var d=a(b),e=d.outerWidth,f=d.outerHeight;(e||f)&&(e=this._applyGridGutter(e,this.columnWidth),f=this._applyGridGutter(f,this.rowHeight)),c.width=Math.min(e,this.packer.width),c.height=Math.min(f,this.packer.height)},j._applyGridGutter=function(a,b){if(!b)return a+this.gutter;b+=this.gutter;var c=a%b,d=c&&1>c?"round":"ceil";return a=Math[d](a/b)*b},j._getContainerSize=function(){return this._getOption("horizontal")?{width:this.maxX-this.gutter}:{height:this.maxY-this.gutter}},j._manageStamp=function(a){var b,d=this.getItem(a);if(d&&d.isPlacing)b=d.rect;else{var e=this._getElementOffset(a);b=new c({x:this._getOption("originLeft")?e.left:e.right,y:this._getOption("originTop")?e.top:e.bottom})}this._setRectSize(a,b),this.packer.placed(b),this._setMaxXY(b)},j.sortItemsByPosition=function(){var a=this._getOption("horizontal")?g:f;this.items.sort(a)},j.fit=function(a,b,c){var d=this.getItem(a);d&&(this.stamp(d.element),d.enablePlacing(),this.updateShiftTargets(d),b=void 0===b?d.rect.x:b,c=void 0===c?d.rect.y:c,this.shift(d,b,c),this._bindFitEvents(d),d.moveTo(d.rect.x,d.rect.y),this.shiftLayout(),this.unstamp(d.element),this.sortItemsByPosition(),d.disablePlacing())},j._bindFitEvents=function(a){function b(){d++,2==d&&c.dispatchEvent("fitComplete",null,[a])}var c=this,d=0;a.once("layout",b),this.once("layoutComplete",b)},j.resize=function(){this.isResizeBound&&this.needsResizeLayout()&&(this.options.shiftPercentResize?this.resizeShiftPercentLayout():this.layout())},j.needsResizeLayout=function(){var b=a(this.element),c=this._getOption("horizontal")?"innerHeight":"innerWidth";return b[c]!=this.size[c]},j.resizeShiftPercentLayout=function(){var b=this._getItemsForLayout(this.items),c=this._getOption("horizontal"),d=c?"y":"x",e=c?"height":"width",f=c?"rowHeight":"columnWidth",g=c?"innerHeight":"innerWidth",h=this[f];if(h=h&&h+this.gutter){this._getMeasurements();var i=this[f]+this.gutter;b.forEach(function(a){var b=Math.round(a.rect[d]/h);a.rect[d]=b*i})}else{var j=a(this.element)[g]+this.gutter,k=this.packer[e];b.forEach(function(a){a.rect[d]=a.rect[d]/k*j})}this.shiftLayout()},j.itemDragStart=function(a){if(this.isEnabled){this.stamp(a);var b=this.getItem(a);b&&(b.enablePlacing(),b.showDropPlaceholder(),this.dragItemCount++,this.updateShiftTargets(b))}},j.updateShiftTargets=function(a){this.shiftPacker.reset(),this._getBoundingRect();var b=this._getOption("originLeft"),d=this._getOption("originTop");this.stamps.forEach(function(a){var e=this.getItem(a);if(!e||!e.isPlacing){var f=this._getElementOffset(a),g=new c({x:b?f.left:f.right,y:d?f.top:f.bottom});this._setRectSize(a,g),this.shiftPacker.placed(g)}},this);var e=this._getOption("horizontal"),f=e?"rowHeight":"columnWidth",g=e?"height":"width";this.shiftTargetKeys=[],this.shiftTargets=[];var h,i=this[f];if(i=i&&i+this.gutter){var j=Math.ceil(a.rect[g]/i),k=Math.floor((this.shiftPacker[g]+this.gutter)/i);h=(k-j)*i;for(var l=0;k>l;l++)this._addShiftTarget(l*i,0,h)}else h=this.shiftPacker[g]+this.gutter-a.rect[g],this._addShiftTarget(0,0,h);var m=this._getItemsForLayout(this.items),n=this._getPackMethod();m.forEach(function(a){var b=a.rect;this._setRectSize(a.element,b),this.shiftPacker[n](b),this._addShiftTarget(b.x,b.y,h);var c=e?b.x+b.width:b.x,d=e?b.y:b.y+b.height;if(this._addShiftTarget(c,d,h),i)for(var f=Math.round(b[g]/i),j=1;f>j;j++){var k=e?c:b.x+i*j,l=e?b.y+i*j:d;this._addShiftTarget(k,l,h)}},this)},j._addShiftTarget=function(a,b,c){var d=this._getOption("horizontal")?b:a;if(!(0!==d&&d>c)){var e=a+","+b,f=-1!=this.shiftTargetKeys.indexOf(e);f||(this.shiftTargetKeys.push(e),this.shiftTargets.push({x:a,y:b}))}},j.shift=function(a,b,c){var d,e=1/0,f={x:b,y:c};
4555this.shiftTargets.forEach(function(a){var b=h(a,f);e>b&&(d=a,e=b)}),a.rect.x=d.x,a.rect.y=d.y};var k=120;j.itemDragMove=function(a,b,c){function d(){f.shift(e,b,c),e.positionDropPlaceholder(),f.layout()}var e=this.isEnabled&&this.getItem(a);if(e){b-=this.size.paddingLeft,c-=this.size.paddingTop;var f=this,g=new Date;this._itemDragTime&&g-this._itemDragTime<k?(clearTimeout(this.dragTimeout),this.dragTimeout=setTimeout(d,k)):(d(),this._itemDragTime=g)}},j.itemDragEnd=function(a){function b(){d++,2==d&&(c.element.classList.remove("is-positioning-post-drag"),c.hideDropPlaceholder(),e.dispatchEvent("dragItemPositioned",null,[c]))}var c=this.isEnabled&&this.getItem(a);if(c){clearTimeout(this.dragTimeout),c.element.classList.add("is-positioning-post-drag");var d=0,e=this;c.once("layout",b),this.once("layoutComplete",b),c.moveTo(c.rect.x,c.rect.y),this.layout(),this.dragItemCount=Math.max(0,this.dragItemCount-1),this.sortItemsByPosition(),c.disablePlacing(),this.unstamp(c.element)}},j.bindDraggabillyEvents=function(a){this._bindDraggabillyEvents(a,"on")},j.unbindDraggabillyEvents=function(a){this._bindDraggabillyEvents(a,"off")},j._bindDraggabillyEvents=function(a,b){var c=this.handleDraggabilly;a[b]("dragStart",c.dragStart),a[b]("dragMove",c.dragMove),a[b]("dragEnd",c.dragEnd)},j.bindUIDraggableEvents=function(a){this._bindUIDraggableEvents(a,"on")},j.unbindUIDraggableEvents=function(a){this._bindUIDraggableEvents(a,"off")},j._bindUIDraggableEvents=function(a,b){var c=this.handleUIDraggable;a[b]("dragstart",c.start)[b]("drag",c.drag)[b]("dragstop",c.stop)};var l=j.destroy;return j.destroy=function(){l.apply(this,arguments),this.isEnabled=!1},i.Rect=c,i.Packer=d,i}),function(a,b){"function"==typeof define&&define.amd?define(["isotope-layout/js/layout-mode","packery/js/packery"],b):"object"==typeof module&&module.exports?module.exports=b(require("isotope-layout/js/layout-mode"),require("packery")):b(a.Isotope.LayoutMode,a.Packery)}(window,function(a,b){var c=a.create("packery"),d=c.prototype,e={_getElementOffset:!0,_getMeasurement:!0};for(var f in b.prototype)e[f]||(d[f]=b.prototype[f]);var g=d._resetLayout;d._resetLayout=function(){this.packer=this.packer||new b.Packer,this.shiftPacker=this.shiftPacker||new b.Packer,g.apply(this,arguments)};var h=d._getItemLayoutPosition;d._getItemLayoutPosition=function(a){return a.rect=a.rect||new b.Rect,h.call(this,a)};var i=d.needsResizeLayout;d.needsResizeLayout=function(){return this._getOption("horizontal")?this.needsVerticalResizeLayout():i.call(this)};var j=d._getOption;return d._getOption=function(a){return"horizontal"==a?void 0!==this.options.isHorizontal?this.options.isHorizontal:this.options.horizontal:j.apply(this.isotope,arguments)},c});
4556
4557/*!
4558 * cellsByRows layout mode for Isotope
4559 * v1.1.3
4560 * http://isotope.metafizzy.co/layout-modes/cellsbyrow.html
4561 */
4562
4563/*jshint browser: true, devel: false, strict: true, undef: true, unused: true */
4564
4565( function( window, factory ) {
4566  // universal module definition
4567  /* jshint strict: false */ /*globals define, module, require */
4568  if ( typeof define === 'function' && define.amd ) {
4569    // AMD
4570    define( [
4571        'isotope/js/layout-mode'
4572      ],
4573      factory );
4574  } else if ( typeof exports === 'object' ) {
4575    // CommonJS
4576    module.exports = factory(
4577      require('isotope-layout/js/layout-mode')
4578    );
4579  } else {
4580    // browser global
4581    factory(
4582      window.Isotope.LayoutMode
4583    );
4584  }
4585
4586}( window, function factory( LayoutMode ) {
4587  'use strict';
4588
4589  var CellsByRow = LayoutMode.create( 'cellsByRow' );
4590  var proto = CellsByRow.prototype;
4591
4592  proto._resetLayout = function() {
4593    // reset properties
4594    this.itemIndex = 0;
4595    // measurements
4596    this.getColumnWidth();
4597    this.getRowHeight();
4598    // set cols
4599    this.cols = Math.floor( this.isotope.size.innerWidth / this.columnWidth );
4600    this.cols = Math.max( this.cols, 1 );
4601  };
4602
4603  proto._getItemLayoutPosition = function( item ) {
4604    item.getSize();
4605    var col = this.itemIndex % this.cols;
4606    var row = Math.floor( this.itemIndex / this.cols );
4607    // center item within cell
4608    var x = ( col + 0.5 ) * this.columnWidth - item.size.outerWidth / 2;
4609    var y = ( row + 0.5 ) * this.rowHeight - item.size.outerHeight / 2;
4610    this.itemIndex++;
4611    return { x: x, y: y };
4612  };
4613
4614  proto._getContainerSize = function() {
4615    return {
4616      height: Math.ceil( this.itemIndex / this.cols ) * this.rowHeight
4617    };
4618  };
4619
4620  return CellsByRow;
4621
4622}));
4623
4624/*!
4625   --------------------------------
4626   Infinite Scroll
4627   --------------------------------
4628   + https://github.com/paulirish/infinite-scroll
4629   + version 2.1.0
4630   + Copyright 2011/12 Paul Irish & Luke Shumard
4631   + Licensed under the MIT license
4632
4633   + Documentation: http://infinite-scroll.com/
4634*/
4635;(function(e){if(typeof define==="function"&&define.amd){define(["jquery"],e)}else{e(jQuery)}})(function(e,t){"use strict";e.infinitescroll=function(n,r,i){this.element=e(i);if(!this._create(n,r)){this.failed=true}};e.infinitescroll.defaults={loading:{finished:t,finishedMsg:"<em>Congratulations, you've reached the end of the internet.</em>",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/fDuFh
4635ohVeDeCfXkcCQqDVQcQhn+VNDOYmpSWaoqBlUSfmowjEA+iEAEGDRGztAwGCDcXEA60tXEiCrq8vREMEBLIyRLCxMWSHMzExnbRvQ2Sy7vN0zvVtNfU2tLY3rPgLdnDvca4VQS/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
4635iqpQpZ4apwSwRtjqrB3muoF9BboaXKmshlqWqsWiGt2wphJkQbAU5hoCACH5BAUKABsALAcABADOAAsAAAX/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",msg:null,msgText:"<em>Loading the next set of posts...</em>",selector:null,speed:"fast",start:t},state:{isDuringAjax:false,isInvalidPage:false,isDestroyed:false,isDone:false,isPaused:false,isBeyondMaxPage:false,currPage:1},debug:false,behavior:t,binder:e(window),nextSelector:"div.navigation a:first",navSelector:"div.navigation",contentSelector:null,extraScrollPx:150,itemSelector:"div.post",animate:false,pathParse:t,dataType:"html",appendCallback:true,bufferPx:40,errorCallback:function(){},infid:0,pixelsFromNavToBottom:t,path:t,prefill:false,maxPage:t};e.infinitescroll.prototype={_binding:function(n){var r=this,i=r.options;i.v="2.0b2.120520";if(!!i.behavior&&this["_binding_"+i.behavior]!==t){this["_binding_"+i.behavior].call(this);return}if(n!=="bind"&&n!=="unbind"){this._debug("Binding value  "+n+" not valid");return false}if(n==="unbind"){this.options.binder.unbind("smartscroll.infscr."+r.options.infid)}else{this.options.binder[n]("smartscroll.infscr."+r.options.infid,function(){r.scroll()})}this._debug("Binding",n)},_create:function(r,i){var s=e.extend(true,{},e.infinitescroll.defaults,r);this.options=s;var o=e(window);var u=this;if(!u._validate(r)){return false}var a=e(s.nextSelector).attr("href");if(!a){this._debug("Navigation selector not found");return false}s.path=s.path||this._determinepath(a);s.contentSelector=s.contentSelector||this.element;s.loading.selector=s.loading.selector||s.contentSelector;s.loading.msg=s.loading.msg||e('<div id="infscr-loading"><img alt="Loading..." src="'+s.loading.img+'" /><div>'+s.loading.msgText+"</div></div>");(new Image).src=s.loading.img;if(s.pixelsFromNavToBottom===t){s.pixelsFromNavToBottom=e(document).height()-e(s.navSelector).offset().top;this._debug("pixelsFromNavToBottom: "+s.pixelsFromNavToBottom)}var f=this;s.loading.start=s.loading.start||function(){e(s.navSelector).hide();s.loading.msg.appendTo(s.loading.selector).show(s.loading.speed,e.proxy(function(){this.beginAjax(s)},f))};s.loading.finished=s.loading.finished||function(){if(!s.state.isBeyondMaxPage)s.loading.msg.fadeOut(s.loading.speed)};s.callback=function(n,r,u){if(!!s.behavior&&n["_callback_"+s.behavior]!==t){n["_callback_"+s.behavior].call(e(s.contentSelector)[0],r,u)}if(i){i.call(e(s.contentSelector)[0],r,s,u)}if(s.prefill){o.bind("resize.infinite-scroll",n._prefill)}};if(r.debug){if(Function.prototype.bind&&(typeof console==="object"||typeof console==="function")&&typeof console.log==="object"){["log","info","warn","error","assert","dir","clear","profile","profileEnd"].forEach(function(e){console[e]=this.call(console[e],console)},Function.prototype.bind)}}this._setup();if(s.prefill){this._prefill()}return true},_prefill:function(){function i(){return e(n.options.contentSelector).height()<=r.height()}var n=this;var r=e(window);this._prefill=function(){if(i()){n.scroll()}r.bind("resize.infinite-scroll",function(){if(i()){r.unbind("resize.infinite-scroll");n.scroll()}})};this._prefill()},_debug:function(){if(true!==this.options.debug){return}if(typeof console!=="undefined"&&typeof console.log==="function"){if(Array.prototype.slice.call(arguments).length===1&&typeof Array.prototype.slice.call(arguments)[0]==="str
4635ing"){console.log(Array.prototype.slice.call(arguments).toString())}else{console.log(Array.prototype.slice.call(arguments))}}else if(!Function.prototype.bind&&typeof console!=="undefined"&&typeof console.log==="object"){Function.prototype.call.call(console.log,console,Array.prototype.slice.call(arguments))}},_determinepath:function(n){var r=this.options;if(!!r.behavior&&this["_determinepath_"+r.behavior]!==t){return this["_determinepath_"+r.behavior].call(this,n)}if(!!r.pathParse){this._debug("pathParse manual");return r.pathParse(n,this.options.state.currPage+1)}else if(n.match(/^(.*?)\b2\b(.*?$)/)){n=n.match(/^(.*?)\b2\b(.*?$)/).slice(1)}else if(n.match(/^(.*?)2(.*?$)/)){if(n.match(/^(.*?page=)2(\/.*|$)/)){n=n.match(/^(.*?page=)2(\/.*|$)/).slice(1);return n}n=n.match(/^(.*?)2(.*?$)/).slice(1)}else{if(n.match(/^(.*?page=)1(\/.*|$)/)){n=n.match(/^(.*?page=)1(\/.*|$)/).slice(1);return n}else{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.");r.state.isInvalidPage=true}}this._debug("determinePath",n);return n},_error:function(n){var r=this.options;if(!!r.behavior&&this["_error_"+r.behavior]!==t){this["_error_"+r.behavior].call(this,n);return}if(n!=="destroy"&&n!=="end"){n="unknown"}this._debug("Error",n);if(n==="end"||r.state.isBeyondMaxPage){this._showdonemsg()}r.state.isDone=true;r.state.currPage=1;
4635r.state.isPaused=false;r.state.isBeyondMaxPage=false;this._binding("unbind")},_loadcallback:function(r,i,s){var o=this.options,u=this.options.callback,a=o.state.isDone?"done":!o.appendCallback?"no-append":"append",f;if(!!o.behavior&&this["_loadcallback_"+o.behavior]!==t){this["_loadcallback_"+o.behavior].call(this,r,i);return}switch(a){case"done":this._showdonemsg();return false;case"no-append":if(o.dataType==="html"){i="<div>"+i+"</div>";i=e(i).find(o.itemSelector)}if(i.length===0){return this._error("end")}break;case"append":var l=r.children();if(l.length===0){return this._error("end")}f=document.createDocumentFragment();while(r[0].firstChild){f.appendChild(r[0].firstChild)}this._debug("contentSelector",e(o.contentSelector)[0]);e(o.contentSelector)[0].appendChild(f);i=l.get();break}o.loading.finished.call(e(o.contentSelector)[0],o);if(o.animate){var c=e(window).scrollTop()+e(o.loading.msg).height()+o.extraScrollPx+"px";e("html,body").animate({scrollTop:c},800,function(){o.state.isDuringAjax=false})}if(!o.animate){o.state.isDuringAjax=false}u(this,i,s);if(o.prefill){this._prefill()}},_nearbottom:function(){var r=this.options,i=0+e(document).height()-r.binder.scrollTop()-e(window).height();if(!!r.behavior&&this["_nearbottom_"+r.behavior]!==t){return this["_nearbottom_"+r.behavior].call(this)}this._debug("math:",i,r.pixelsFromNavToBottom);return i-r.bufferPx<r.pixelsFromNavToBottom},_pausing:function(n){var r=this.options;if(!!r.behavior&&this["_pausing_"+r.behavior]!==t){this["_pausing_"+r.behavior].call(this,n);return}if(n!=="pause"&&n!=="resume"&&n!==null){this._debug("Invalid argument. Toggling pause value instead")}n=n&&(n==="pause"||n==="resume")?n:"toggle";
4635switch(n){case"pause":r.state.isPaused=true;break;case"resume":r.state.isPaused=false;break;case"toggle":r.state.isPaused=!r.state.isPaused;break}this._debug("Paused",r.state.isPaused);return false},_setup:function(){var n=this.options;if(!!n.behavior&&this["_setup_"+n.behavior]!==t){this["_setup_"+n.behavior].call(this);return}this._binding("bind");return false},_showdonemsg:function(){var r=this.options;if(!!r.behavior&&this["_showdonemsg_"+r.behavior]!==t){this["_showdonemsg_"+r.behavior].call(this);return}r.loading.msg.find("img").hide().parent().find("div").html(r.loading.finishedMsg).animate({opacity:1},2e3,function(){e(this).parent().fadeOut(r.loading.speed)});r.errorCallback.call(e(r.contentSelector)[0],"done")},_validate:function(n){for(var r in n){if(r.indexOf&&r.indexOf("Selector")>-1&&e(n[r]).length===0){this._debug("Your "+r+" found no elements.");return false}}return true},bind:function(){this._binding("bind")},destroy:function(){this.options.state.isDestroyed=true;this.options.loading.finished();return this._error("destroy")},pause:function(){this._pausing("pause")},resume:function(){this._pausing("resume")},beginAjax:function(r){var i=this,s=r.path,o,u,a,f;r.state.currPage++;if(r.maxPage!==t&&r.state.currPage>r.maxPage){r.state.isBeyondMaxPage=true;this.destroy();return}o=e(r.contentSelector).is("table, tbody")?e("<tbody/>"):e("<div/>");u=typeof s==="function"?s(r.state.currPage):s.join(r.state.currPage);i._debug("heading into ajax",u);a=r.dataType==="html"||r.dataType==="json"?r.dataType:"html+callback";if(r.appendCallback&&r.dataType==="html"){a+="+callback"}switch(a){case"html+callback":i._debug("Using HTML via .load() method");o.load(u+" "+r.itemSelector,t,function(t){i._loadcallback(o,t,u)});break;case"html":i._debug("Using "+a.toUpperCase()+" via $.ajax() method");e.ajax({url:u,dataType:r.dataType,complete:function(t,n){f=typeof t.isResolved!=="undefined"?t.isResolved():n==="success"||n==="notmodified";if(f){i._loadcallback(o,t.responseText,u)}else{i._error("end")}}});break;case"json":i._debug("Using "+a.toUpperCase()+" via $.ajax() method");e.ajax({dataType:"json",type:"GET",url:u,success:function(e,n,s){f=typeof s.isResolved!=="undefined"?s.isResolved():n==="success"||n==="notmodified";if(r.appendCallback){if(r.template!==t){var a=r.template(e);o.append(a);if(f){i._loadcallback(o,a)}else{i._error("end")}}else{i._debug("template must be defined.");i._error("end")}}else{if(f){i._loadcallback(o,e,u)}else{i._error("end")}}},error:function(){i._debug("JSON ajax request failed.");i._error("end")}});break}},retrieve:function(r){r=r||null;var i=this,s=i.options;if(!!s.behavior&&this["retrieve_"+s.behavior]!==t){this["retrieve_"+s.behavior].call(this,r);return}if(s.state.isDestroyed){this._debug("Instance is destroyed");return false}s.state.isDuringAjax=true;s.loading.start.call(e(s.contentSelector)[0],s)},scroll:function(){var n=this.options,r=n.state;if(!!n.behavior&&this["scroll_"+n.behavior]!==t){this["scroll_"+n.behavior].call(this);return}if(r.isDuringAjax||r.isInvalidPage||r.isDone||r.isDestroyed||r.isPaused){return}if(!this._nearbottom()){return}this.retrieve()},toggle:function(){this._pausing()},unbind:function(){this._binding("unbind")},update:function(n){if(e.isPlainObject(n)){this.options=e.extend(true,this.options,n)}}};e.fn.infinitescroll=function(n,r){var i=typeof n;switch(i){case"string":var s=Array.prototype.slice.call(arguments,1);this.each(function(){var t=e.data(this,"infinitescroll");if(!t){return false}if(!e.isFunction(t[n])||n.charAt(0)==="_"){return false}t[n].apply(t,s)});break;case"object":this.each(function(){var t=e.data(this,"infinitescroll");if(t){t.update(n)}else{t=new e.infinitescroll(n,r,this);if(!t.failed){e.data(this,"infinitescroll",t)}}});break}return this};var n=e.event,r;n.special.smartscroll={setup:function(){e(this).bind("scroll",n.special.smartscroll.handler)},teardown:function(){e(this).unbind("scroll",n.special.smartscroll.handler)},handler:function(t,n){var i=this,s=arguments;t.type="smartscroll";if(r){clearTimeout(r)}r=setTimeout(function(){e(i).trigger("smartscroll",s)},n==="execAsap"?0:100)}};e.fn.smartscroll=function(e){return e?this.bind("smartscroll",e):this.trigger("smartscroll",["execAsap"])}});
4636
4637/*!
4638Waypoints - 4.0.1
4639Copyright © 2011-2016 Caleb Troughton
4640Licensed under the MIT license.
4641https://github.com/imakewebthings/waypoints/blob/master/licenses.txt
4642*/
4643(function() {
4644  'use strict'
4645
4646  var keyCounter = 0
4647  var allWaypoints = {}
4648
4649  /* http://imakewebthings.com/waypoints/api/waypoint */
4650  function Waypoint(options) {
4651    if (!options) {
4652      throw new Error('No options passed to Waypoint constructor')
4653    }
4654    if (!options.element) {
4655      throw new Error('No element option passed to Waypoint constructor')
4656    }
4657    if (!options.handler) {
4658      throw new Error('No handler option passed to Waypoint constructor')
4659    }
4660
4661    this.key = 'waypoint-' + keyCounter
4662    this.options = Waypoint.Adapter.extend({}, Waypoint.defaults, options)
4663    this.element = this.options.element
4664    this.adapter = new Waypoint.Adapter(this.element)
4665    this.callback = options.handler
4666    this.axis = this.options.horizontal ? 'horizontal' : 'vertical'
4667    this.enabled = this.options.enabled
4668    this.triggerPoint = null
4669    this.group = Waypoint.Group.findOrCreate({
4670      name: this.options.group,
4671      axis: this.axis
4672    })
4673    this.context = Waypoint.Context.findOrCreateByElement(this.options.context)
4674
4675    if (Waypoint.offsetAliases[this.options.offset]) {
4676      this.options.offset = Waypoint.offsetAliases[this.options.offset]
4677    }
4678    this.group.add(this)
4679    this.context.add(this)
4680    allWaypoints[this.key] = this
4681    keyCounter += 1
4682  }
4683
4684  /* Private */
4685  Waypoint.prototype.queueTrigger = function(direction) {
4686    this.group.queueTrigger(this, direction)
4687  }
4688
4689  /* Private */
4690  Waypoint.prototype.trigger = function(args) {
4691    if (!this.enabled) {
4692      return
4693    }
4694    if (this.callback) {
4695      this.callback.apply(this, args)
4696    }
4697  }
4698
4699  /* Public */
4700  /* http://imakewebthings.com/waypoints/api/destroy */
4701  Waypoint.prototype.destroy = function() {
4702    this.context.remove(this)
4703    this.group.remove(this)
4704    delete allWaypoints[this.key]
4705  }
4706
4707  /* Public */
4708  /* http://imakewebthings.com/waypoints/api/disable */
4709  Waypoint.prototype.disable = function() {
4710    this.enabled = false
4711    return this
4712  }
4713
4714  /* Public */
4715  /* http://imakewebthings.com/waypoints/api/enable */
4716  Waypoint.prototype.enable = function() {
4717    this.context.refresh()
4718    this.enabled = true
4719    return this
4720  }
4721
4722  /* Public */
4723  /* http://imakewebthings.com/waypoints/api/next */
4724  Waypoint.prototype.next = function() {
4725    return this.group.next(this)
4726  }
4727
4728  /* Public */
4729  /* http://imakewebthings.com/waypoints/api/previous */
4730  Waypoint.prototype.previous = function() {
4731    return this.group.previous(this)
4732  }
4733
4734  /* Private */
4735  Waypoint.invokeAll = function(method) {
4736    var allWaypointsArray = []
4737    for (var waypointKey in allWaypoints) {
4738      allWaypointsArray.push(allWaypoints[waypointKey])
4739    }
4740    for (var i = 0, end = allWaypointsArray.length; i < end; i++) {
4741      allWaypointsArray[i][method]()
4742    }
4743  }
4744
4745  /* Public */
4746  /* http://imakewebthings.com/waypoints/api/destroy-all */
4747  Waypoint.destroyAll = function() {
4748    Waypoint.invokeAll('destroy')
4749  }
4750
4751  /* Public */
4752  /* http://imakewebthings.com/waypoints/api/disable-all */
4753  Waypoint.disableAll = function() {
4754    Waypoint.invokeAll('disable')
4755  }
4756
4757  /* Public */
4758  /* http://imakewebthings.com/waypoints/api/enable-all */
4759  Waypoint.enableAll = function() {
4760    Waypoint.Context.refreshAll()
4761    for (var waypointKey in allWaypoints) {
4762      allWaypoints[waypointKey].enabled = true
4763    }
4764    return this
4765  }
4766
4767  /* Public */
4768  /* http://imakewebthings.com/waypoints/api/refresh-all */
4769  Waypoint.refreshAll = function() {
4770    Waypoint.Context.refreshAll()
4771  }
4772
4773  /* Public */
4774  /* http://imakewebthings.com/waypoints/api/viewport-height */
4775  Waypoint.viewportHeight = function() {
4776    return window.innerHeight || document.documentElement.clientHeight
4777  }
4778
4779  /* Public */
4780  /* http://imakewebthings.com/waypoints/api/viewport-width */
4781  Waypoint.viewportWidth = function() {
4782    return document.documentElement.clientWidth
4783  }
4784
4785  Waypoint.adapters = []
4786
4787  Waypoint.defaults = {
4788    context: window,
4789    continuous: true,
4790    enabled: true,
4791    group: 'default',
4792    horizontal: false,
4793    offset: 0
4794  }
4795
4796  Waypoint.offsetAliases = {
4797    'bottom-in-view': function() {
4798      return this.context.innerHeight() - this.adapter.outerHeight()
4799    },
4800    'right-in-view': function() {
4801      return this.context.innerWidth() - this.adapter.outerWidth()
4802    }
4803  }
4804
4805  window.Waypoint = Waypoint
4806}())
4807;(function() {
4808  'use strict'
4809
4810  function requestAnimationFrameShim(callback) {
4811    window.setTimeout(callback, 1000 / 60)
4812  }
4813
4814  var keyCounter = 0
4815  var contexts = {}
4816  var Waypoint = window.Waypoint
4817  var oldWindowLoad = window.onload
4818
4819  /* http://imakewebthings.com/waypoints/api/context */
4820  function Context(element) {
4821    this.element = element
4822    this.Adapter = Waypoint.Adapter
4823    this.adapter = new this.Adapter(element)
4824    this.key = 'waypoint-context-' + keyCounter
4825    this.didScroll = false
4826    this.didResize = false
4827    this.oldScroll = {
4828      x: this.adapter.scrollLeft(),
4829      y: this.adapter.scrollTop()
4830    }
4831    this.waypoints = {
4832      vertical: {},
4833      horizontal: {}
4834    }
4835
4836    element.waypointContextKey = this.key
4837    contexts[element.waypointContextKey] = this
4838    keyCounter += 1
4839    if (!Waypoint.windowContext) {
4840      Waypoint.windowContext = true
4841      Waypoint.windowContext = new Context(window)
4842    }
4843
4844    this.createThrottledScrollHandler()
4845    this.createThrottledResizeHandler()
4846  }
4847
4848  /* Private */
4849  Context.prototype.add = function(waypoint) {
4850    var axis = waypoint.options.horizontal ? 'horizontal' : 'vertical'
4851    this.waypoints[axis][waypoint.key] = waypoint
4852    this.refresh()
4853  }
4854
4855  /* Private */
4856  Context.prototype.checkEmpty = function() {
4857    var horizontalEmpty = this.Adapter.isEmptyObject(this.waypoints.horizontal)
4858    var verticalEmpty = this.Adapter.isEmptyObject(this.waypoints.vertical)
4859    var isWindow = this.element == this.element.window
4860    if (horizontalEmpty && verticalEmpty && !isWindow) {
4861      this.adapter.off('.waypoints')
4862      delete contexts[this.key]
4863    }
4864  }
4865
4866  /* Private */
4867  Context.prototype.createThrottledResizeHandler = function() {
4868    var self = this
4869
4870    function resizeHandler() {
4871      self.handleResize()
4872      self.didResize = false
4873    }
4874
4875    this.adapter.on('resize.waypoints', function() {
4876      if (!self.didResize) {
4877        self.didResize = true
4878        Waypoint.requestAnimationFrame(resizeHandler)
4879      }
4880    })
4881  }
4882
4883  /* Private */
4884  Context.prototype.createThrottledScrollHandler = function() {
4885    var self = this
4886    function scrollHandler() {
4887      self.handleScroll()
4888      self.didScroll = false
4889    }
4890
4891    this.adapter.on('scroll.waypoints', function() {
4892      if (!self.didScroll || Waypoint.isTouch) {
4893        self.didScroll = true
4894        Waypoint.requestAnimationFrame(scrollHandler)
4895      }
4896    })
4897  }
4898
4899  /* Private */
4900  Context.prototype.handleResize = function() {
4901    Waypoint.Context.refreshAll()
4902  }
4903
4904  /* Private */
4905  Context.prototype.handleScroll = function() {
4906    var triggeredGroups = {}
4907    var axes = {
4908      horizontal: {
4909        newScroll: this.adapter.scrollLeft(),
4910        oldScroll: this.oldScroll.x,
4911        forward: 'right',
4912        backward: 'left'
4913      },
4914      vertical: {
4915        newScroll: this.adapter.scrollTop(),
4916        oldScroll: this.oldScroll.y,
4917        forward: 'down',
4918        backward: 'up'
4919      }
4920    }
4921
4922    for (var axisKey in axes) {
4923      var axis = axes[axisKey]
4924      var isForward = axis.newScroll > axis.oldScroll
4925      var direction = isForward ? axis.forward : axis.backward
4926
4927      for (var waypointKey in this.waypoints[axisKey]) {
4928        var waypoint = this.waypoints[axisKey][waypointKey]
4929        if (waypoint.triggerPoint === null) {
4930          continue
4931        }
4932        var wasBeforeTriggerPoint = axis.oldScroll < waypoint.triggerPoint
4933        var nowAfterTriggerPoint = axis.newScroll >= waypoint.triggerPoint
4934        var crossedForward = wasBeforeTriggerPoint && nowAfterTriggerPoint
4935        var crossedBackward = !wasBeforeTriggerPoint && !nowAfterTriggerPoint
4936        if (crossedForward || crossedBackward) {
4937          waypoint.queueTrigger(direction)
4938          triggeredGroups[waypoint.group.id] = waypoint.group
4939        }
4940      }
4941    }
4942
4943    for (var groupKey in triggeredGroups) {
4944      triggeredGroups[groupKey].flushTriggers()
4945    }
4946
4947    this.oldScroll = {
4948      x: axes.horizontal.newScroll,
4949      y: axes.vertical.newScroll
4950    }
4951  }
4952
4953  /* Private */
4954  Context.prototype.innerHeight = function() {
4955    /*eslint-disable eqeqeq */
4956    if (this.element == this.element.window) {
4957      return Waypoint.viewportHeight()
4958    }
4959    /*eslint-enable eqeqeq */
4960    return this.adapter.innerHeight()
4961  }
4962
4963  /* Private */
4964  Context.prototype.remove = function(waypoint) {
4965    delete this.waypoints[waypoint.axis][waypoint.key]
4966    this.checkEmpty()
4967  }
4968
4969  /* Private */
4970  Context.prototype.innerWidth = function() {
4971    /*eslint-disable eqeqeq */
4972    if (this.element == this.element.window) {
4973      return Waypoint.viewportWidth()
4974    }
4975    /*eslint-enable eqeqeq */
4976    return this.adapter.innerWidth()
4977  }
4978
4979  /* Public */
4980  /* http://imakewebthings.com/waypoints/api/context-destroy */
4981  Context.prototype.destroy = function() {
4982    var allWaypoints = []
4983    for (var axis in this.waypoints) {
4984      for (var waypointKey in this.waypoints[axis]) {
4985        allWaypoints.push(this.waypoints[axis][waypointKey])
4986      }
4987    }
4988    for (var i = 0, end = allWaypoints.length; i < end; i++) {
4989      allWaypoints[i].destroy()
4990    }
4991  }
4992
4993  /* Public */
4994  /* http://imakewebthings.com/waypoints/api/context-refresh */
4995  Context.prototype.refresh = function() {
4996    /*eslint-disable eqeqeq */
4997    var isWindow = this.element == this.element.window
4998    /*eslint-enable eqeqeq */
4999    var contextOffset = isWindow ? undefined : this.adapter.offset()
5000    var triggeredGroups = {}
5001    var axes
5002
5003    this.handleScroll()
5004    axes = {
5005      horizontal: {
5006        contextOffset: isWindow ? 0 : contextOffset.left,
5007        contextScroll: isWindow ? 0 : this.oldScroll.x,
5008        contextDimension: this.innerWidth(),
5009        oldScroll: this.oldScroll.x,
5010        forward: 'right',
5011        backward: 'left',
5012        offsetProp: 'left'
5013      },
5014      vertical: {
5015        contextOffset: isWindow ? 0 : contextOffset.top,
5016        contextScroll: isWindow ? 0 : this.oldScroll.y,
5017        contextDimension: this.innerHeight(),
5018        oldScroll: this.oldScroll.y,
5019        forward: 'down',
5020        backward: 'up',
5021        offsetProp: 'top'
5022      }
5023    }
5024
5025    for (var axisKey in axes) {
5026      var axis = axes[axisKey]
5027      for (var waypointKey in this.waypoints[axisKey]) {
5028        var waypoint = this.waypoints[axisKey][waypointKey]
5029        var adjustment = waypoint.options.offset
5030        var oldTriggerPoint = waypoint.triggerPoint
5031        var elementOffset = 0
5032        var freshWaypoint = oldTriggerPoint == null
5033        var contextModifier, wasBeforeScroll, nowAfterScroll
5034        var triggeredBackward, triggeredForward
5035
5036        if (waypoint.element !== waypoint.element.window) {
5037          elementOffset = waypoint.adapter.offset()[axis.offsetProp]
5038        }
5039
5040        if (typeof adjustment === 'function') {
5041          adjustment = adjustment.apply(waypoint)
5042        }
5043        else if (typeof adjustment === 'string') {
5044          adjustment = parseFloat(adjustment)
5045          if (waypoint.options.offset.indexOf('%') > - 1) {
5046            adjustment = Math.ceil(axis.contextDimension * adjustment / 100)
5047          }
5048        }
5049
5050        contextModifier = axis.contextScroll - axis.contextOffset
5051        waypoint.triggerPoint = Math.floor(elementOffset + contextModifier - adjustment)
5052        wasBeforeScroll = oldTriggerPoint < axis.oldScroll
5053        nowAfterScroll = waypoint.triggerPoint >= axis.oldScroll
5054        triggeredBackward = wasBeforeScroll && nowAfterScroll
5055        triggeredForward = !wasBeforeScroll && !nowAfterScroll
5056
5057        if (!freshWaypoint && triggeredBackward) {
5058          waypoint.queueTrigger(axis.backward)
5059          triggeredGroups[waypoint.group.id] = waypoint.group
5060        }
5061        else if (!freshWaypoint && triggeredForward) {
5062          waypoint.queueTrigger(axis.forward)
5063          triggeredGroups[waypoint.group.id] = waypoint.group
5064        }
5065        else if (freshWaypoint && axis.oldScroll >= waypoint.triggerPoint) {
5066          waypoint.queueTrigger(axis.forward)
5067          triggeredGroups[waypoint.group.id] = waypoint.group
5068        }
5069      }
5070    }
5071
5072    Waypoint.requestAnimationFrame(function() {
5073      for (var groupKey in triggeredGroups) {
5074        triggeredGroups[groupKey].flushTriggers()
5075      }
5076    })
5077
5078    return this
5079  }
5080
5081  /* Private */
5082  Context.findOrCreateByElement = function(element) {
5083    return Context.findByElement(element) || new Context(element)
5084  }
5085
5086  /* Private */
5087  Context.refreshAll = function() {
5088    for (var contextId in contexts) {
5089      contexts[contextId].refresh()
5090    }
5091  }
5092
5093  /* Public */
5094  /* http://imakewebthings.com/waypoints/api/context-find-by-element */
5095  Context.findByElement = function(element) {
5096    return contexts[element.waypointContextKey]
5097  }
5098
5099  window.onload = function() {
5100    if (oldWindowLoad) {
5101      oldWindowLoad()
5102    }
5103    Context.refreshAll()
5104  }
5105
5106
5107  Waypoint.requestAnimationFrame = function(callback) {
5108    var requestFn = window.requestAnimationFrame ||
5109      window.mozRequestAnimationFrame ||
5110      window.webkitRequestAnimationFrame ||
5111      requestAnimationFrameShim
5112    requestFn.call(window, callback)
5113  }
5114  Waypoint.Context = Context
5115}())
5116;(function() {
5117  'use strict'
5118
5119  function byTriggerPoint(a, b) {
5120    return a.triggerPoint - b.triggerPoint
5121  }
5122
5123  function byReverseTriggerPoint(a, b) {
5124    return b.triggerPoint - a.triggerPoint
5125  }
5126
5127  var groups = {
5128    vertical: {},
5129    horizontal: {}
5130  }
5131  var Waypoint = window.Waypoint
5132
5133  /* http://imakewebthings.com/waypoints/api/group */
5134  function Group(options) {
5135    this.name = options.name
5136    this.axis = options.axis
5137    this.id = this.name + '-' + this.axis
5138    this.waypoints = []
5139    this.clearTriggerQueues()
5140    groups[this.axis][this.name] = this
5141  }
5142
5143  /* Private */
5144  Group.prototype.add = function(waypoint) {
5145    this.waypoints.push(waypoint)
5146  }
5147
5148  /* Private */
5149  Group.prototype.clearTriggerQueues = function() {
5150    this.triggerQueues = {
5151      up: [],
5152      down: [],
5153      left: [],
5154      right: []
5155    }
5156  }
5157
5158  /* Private */
5159  Group.prototype.flushTriggers = function() {
5160    for (var direction in this.triggerQueues) {
5161      var waypoints = this.triggerQueues[direction]
5162      var reverse = direction === 'up' || direction === 'left'
5163      waypoints.sort(reverse ? byReverseTriggerPoint : byTriggerPoint)
5164      for (var i = 0, end = waypoints.length; i < end; i += 1) {
5165        var waypoint = waypoints[i]
5166        if (waypoint.options.continuous || i === waypoints.length - 1) {
5167          waypoint.trigger([direction])
5168        }
5169      }
5170    }
5171    this.clearTriggerQueues()
5172  }
5173
5174  /* Private */
5175  Group.prototype.next = function(waypoint) {
5176    this.waypoints.sort(byTriggerPoint)
5177    var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
5178    var isLast = index === this.waypoints.length - 1
5179    return isLast ? null : this.waypoints[index + 1]
5180  }
5181
5182  /* Private */
5183  Group.prototype.previous = function(waypoint) {
5184    this.waypoints.sort(byTriggerPoint)
5185    var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
5186    return index ? this.waypoints[index - 1] : null
5187  }
5188
5189  /* Private */
5190  Group.prototype.queueTrigger = function(waypoint, direction) {
5191    this.triggerQueues[direction].push(waypoint)
5192  }
5193
5194  /* Private */
5195  Group.prototype.remove = function(waypoint) {
5196    var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
5197    if (index > -1) {
5198      this.waypoints.splice(index, 1)
5199    }
5200  }
5201
5202  /* Public */
5203  /* http://imakewebthings.com/waypoints/api/first */
5204  Group.prototype.first = function() {
5205    return this.waypoints[0]
5206  }
5207
5208  /* Public */
5209  /* http://imakewebthings.com/waypoints/api/last */
5210  Group.prototype.last = function() {
5211    return this.waypoints[this.waypoints.length - 1]
5212  }
5213
5214  /* Private */
5215  Group.findOrCreate = function(options) {
5216    return groups[options.axis][options.name] || new Group(options)
5217  }
5218
5219  Waypoint.Group = Group
5220}())
5221;(function() {
5222  'use strict'
5223
5224  var $ = window.jQuery
5225  var Waypoint = window.Waypoint
5226
5227  function JQueryAdapter(element) {
5228    this.$element = $(element)
5229  }
5230
5231  $.each([
5232    'innerHeight',
5233    'innerWidth',
5234    'off',
5235    'offset',
5236    'on',
5237    'outerHeight',
5238    'outerWidth',
5239    'scrollLeft',
5240    'scrollTop'
5241  ], function(i, method) {
5242    JQueryAdapter.prototype[method] = function() {
5243      var args = Array.prototype.slice.call(arguments)
5244      return this.$element[method].apply(this.$element, args)
5245    }
5246  })
5247
5248  $.each([
5249    'extend',
5250    'inArray',
5251    'isEmptyObject'
5252  ], function(i, method) {
5253    JQueryAdapter[method] = $[method]
5254  })
5255
5256  Waypoint.adapters.push({
5257    name: 'jquery',
5258    Adapter: JQueryAdapter
5259  })
5260  Waypoint.Adapter = JQueryAdapter
5261}())
5262;(function() {
5263  'use strict'
5264
5265  var Waypoint = window.Waypoint
5266
5267  function createExtension(framework) {
5268    return function() {
5269      var waypoints = []
5270      var overrides = arguments[0]
5271
5272      if (framework.isFunction(arguments[0])) {
5273        overrides = framework.extend({}, arguments[1])
5274        overrides.handler = arguments[0]
5275      }
5276
5277      this.each(function() {
5278        var options = framework.extend({}, overrides, {
5279          element: this
5280        })
5281        if (typeof options.context === 'string') {
5282          options.context = framework(this).closest(options.context)[0]
5283        }
5284        waypoints.push(new Waypoint(options))
5285      })
5286
5287      return waypoints
5288    }
5289  }
5290
5291  if (window.jQuery) {
5292    window.jQuery.fn.waypoint = createExtension(window.jQuery)
5293  }
5294  if (window.Zepto) {
5295    window.Zepto.fn.waypoint = createExtension(window.Zepto)
5296  }
5297}())
5298;
5299
5300/*!
5301Waypoints Inview Shortcut - 4.0.1
5302Copyright © 2011-2016 Caleb Troughton
5303Licensed under the MIT license.
5304https://github.com/imakewebthings/waypoints/blob/master/licenses.txt
5305*/
5306(function() {
5307  'use strict'
5308
5309  function noop() {}
5310
5311  var Waypoint = window.Waypoint
5312
5313  /* http://imakewebthings.com/waypoints/shortcuts/inview */
5314  function Inview(options) {
5315    this.options = Waypoint.Adapter.extend({}, Inview.defaults, options)
5316    this.axis = this.options.horizontal ? 'horizontal' : 'vertical'
5317    this.waypoints = []
5318    this.element = this.options.element
5319    this.createWaypoints()
5320  }
5321
5322  /* Private */
5323  Inview.prototype.createWaypoints = function() {
5324    var configs = {
5325      vertical: [{
5326        down: 'enter',
5327        up: 'exited',
5328        offset: '100%'
5329      }, {
5330        down: 'entered',
5331        up: 'exit',
5332        offset: 'bottom-in-view'
5333      }, {
5334        down: 'exit',
5335        up: 'entered',
5336        offset: 0
5337      }, {
5338        down: 'exited',
5339        up: 'enter',
5340        offset: function() {
5341          return -this.adapter.outerHeight()
5342        }
5343      }],
5344      horizontal: [{
5345        right: 'enter',
5346        left: 'exited',
5347        offset: '100%'
5348      }, {
5349        right: 'entered',
5350        left: 'exit',
5351        offset: 'right-in-view'
5352      }, {
5353        right: 'exit',
5354        left: 'entered',
5355        offset: 0
5356      }, {
5357        right: 'exited',
5358        left: 'enter',
5359        offset: function() {
5360          return -this.adapter.outerWidth()
5361        }
5362      }]
5363    }
5364
5365    for (var i = 0, end = configs[this.axis].length; i < end; i++) {
5366      var config = configs[this.axis][i]
5367      this.createWaypoint(config)
5368    }
5369  }
5370
5371  /* Private */
5372  Inview.prototype.createWaypoint = function(config) {
5373    var self = this
5374    this.waypoints.push(new Waypoint({
5375      context: this.options.context,
5376      element: this.options.element,
5377      enabled: this.options.enabled,
5378      handler: (function(config) {
5379        return function(direction) {
5380          self.options[config[direction]].call(self, direction)
5381        }
5382      }(config)),
5383      offset: config.offset,
5384      horizontal: this.options.horizontal
5385    }))
5386  }
5387
5388  /* Public */
5389  Inview.prototype.destroy = function() {
5390    for (var i = 0, end = this.waypoints.length; i < end; i++) {
5391      this.waypoints[i].destroy()
5392    }
5393    this.waypoints = []
5394  }
5395
5396  Inview.prototype.disable = function() {
5397    for (var i = 0, end = this.waypoints.length; i < end; i++) {
5398      this.waypoints[i].disable()
5399    }
5400  }
5401
5402  Inview.prototype.enable = function() {
5403    for (var i = 0, end = this.waypoints.length; i < end; i++) {
5404      this.waypoints[i].enable()
5405    }
5406  }
5407
5408  Inview.defaults = {
5409    context: window,
5410    enabled: true,
5411    enter: noop,
5412    entered: noop,
5413    exit: noop,
5414    exited: noop
5415  }
5416
5417  Waypoint.Inview = Inview
5418}())
5419;
5420
5421/*
5422 * SmartMenus jQuery v0.9.6
5423 * http://www.smartmenus.org/
5424 *
5425 * Copyright 2014 Vasil Dinkov, Vadikom Web Ltd.
5426 * http://vadikom.com/
5427 *
5428 * Released under the MIT license:
5429 * http://www.opensource.org/licenses/MIT
5430 */
5431
5432(function($) {
5433
5434	var menuTrees = [],
5435		IE = !!window.createPopup, // we need to detect it, unfortunately
5436		IElt9 = IE && !document.defaultView,
5437		IElt8 = IE && !document.querySelector,
5438		IE6 = IE && typeof document.documentElement.currentStyle.minWidth == 'undefined',
5439		mouse = false, // optimize for touch by default - we will detect for mouse input
5440		mouseDetectionEnabled = false;
5441
5442	// Handle detection for mouse input (i.e. desktop browsers, tablets with a mouse, etc.)
5443	function initMouseDetection(disable) {
5444		if (!mouseDetectionEnabled && !disable) {
5445			// if we get two consecutive mousemoves within 2 pixels from each other and within 300ms, we assume a real mouse/cursor is present
5446			// 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)
5447			var firstTime = true,
5448				lastMove = null;
5449			$(document).bind({
5450				'mousemove.smartmenus_mouse': function(e) {
5451					var thisMove = { x: e.pageX, y: e.pageY, timeStamp: new Date().getTime() };
5452					if (lastMove) {
5453						var deltaX = Math.abs(lastMove.x - thisMove.x),
5454							deltaY = Math.abs(lastMove.y - thisMove.y);
5455	 					if ((deltaX > 0 || deltaY > 0) && deltaX <= 2 && deltaY <= 2 && thisMove.timeStamp - lastMove.timeStamp <= 300) {
5456							mouse = true;
5457							// 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
5458							if (firstTime) {
5459								var $a = $(e.target).closest('a');
5460								if ($a.is('a')) {
5461									$.each(menuTrees, function() {
5462										if ($.contains(this.$root[0], $a[0])) {
5463											this.itemEnter({ currentTarget: $a[0] });
5464											return false;
5465										}
5466									});
5467								}
5468								firstTime = false;
5469							}
5470						}
5471					}
5472					lastMove = thisMove;
5473				},
5474				'touchstart.smartmenus_mouse pointerover.smartmenus_mouse MSPointerOver.smartmenus_mouse': function(e) {
5475					if (!/^(4|mouse)$/.test(e.originalEvent.pointerType)) {
5476						mouse = false;
5477					}
5478				}
5479			});
5480			mouseDetectionEnabled = true;
5481		} else if (mouseDetectionEnabled && disable) {
5482			$(document).unbind('.smartmenus_mouse');
5483			mouseDetectionEnabled = false;
5484		}
5485	};
5486
5487	$.SmartMenus = function(elm, options) {
5488		this.$root = $(elm);
5489		this.opts = options;
5490		this.rootId = ''; // internal
5491		this.$subArrow = null;
5492		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)
5493		this.activatedItems = []; // stores last activated A's for each level
5494		this.visibleSubMenus = []; // stores visible sub menus UL's
5495		this.showTimeout = 0;
5496		this.hideTimeout = 0;
5497		this.scrollTimeout = 0;
5498		this.clickActivated = false;
5499		this.zIndexInc = 0;
5500		this.$firstLink = null; // we'll use these for some tests
5501		this.$firstSub = null; // at runtime so we'll cache them
5502		this.disabled = false;
5503		this.$disableOverlay = null;
5504		this.init();
5505	};
5506
5507	$.extend($.SmartMenus, {
5508		hideAll: function() {
5509			$.each(menuTrees, function() {
5510				this.menuHideAll();
5511			});
5512		},
5513		destroy: function() {
5514			while (menuTrees.length) {
5515				menuTrees[0].destroy();
5516			}
5517			initMouseDetection(true);
5518		},
5519		prototype: {
5520			init: function(refresh) {
5521				var self = this;
5522
5523				if (!refresh) {
5524					menuTrees.push(this);
5525
5526					this.rootId = (new Date().getTime() + Math.random() + '').replace(/\D/g, '');
5527
5528					if (this.$root.hasClass('sm-rtl')) {
5529						this.opts.rightToLeftSubMenus = true;
5530					}
5531
5532					// init root (main menu)
5533					this.$root
5534						.data('smartmenus', this)
5535						.attr('data-smartmenus-id', this.rootId)
5536						.dataSM('level', 1)
5537						.bind({
5538							'mouseover.smartmenus focusin.smartmenus': $.proxy(this.rootOver, this),
5539							'mouseout.smartmenus focusout.smartmenus': $.proxy(this.rootOut, this)
5540						})
5541						.delegate('a', {
5542							'mouseenter.smartmenus': $.proxy(this.itemEnter, this),
5543							'mouseleave.smartmenus': $.proxy(this.itemLeave, this),
5544							'mousedown.smartmenus': $.proxy(this.itemDown, this),
5545							'focus.smartmenus': $.proxy(this.itemFocus, this),
5546							'blur.smartmenus': $.proxy(this.itemBlur, this),
5547							'click.smartmenus': $.proxy(this.itemClick, this),
5548							'touchend.smartmenus': $.proxy(this.itemTouchEnd, this)
5549						});
5550
5551					var eNamespace = '.smartmenus' + this.rootId;
5552					// hide menus on tap or click outside the root UL
5553					if (this.opts.hideOnClick) {
5554						$(document).bind('touchstart' + eNamespace, $.proxy(this.docTouchStart, this))
5555							.bind('touchmove' + eNamespace, $.proxy(this.docTouchMove, this))
5556							.bind('touchend' + eNamespace, $.proxy(this.docTouchEnd, this))
5557							// for Opera Mobile < 11.5, webOS browser, etc. we'll check click too
5558							.bind('click' + eNamespace, $.proxy(this.docClick, this));
5559					}
5560					// hide sub menus on resize
5561					$(window).bind('resize' + eNamespace + ' orientationchange' + eNamespace, $.proxy(this.winResize, this));
5562					$(window).bind('scroll' + eNamespace + ' orientationchange' + eNamespace, $.proxy(this.winResize, this));
5563
5564					if (this.opts.subIndicators) {
5565						this.$subArrow = $('<span/>').addClass('sub-arrow');
5566						if (this.opts.subIndicatorsText) {
5567							this.$subArrow.html(this.opts.subIndicatorsText);
5568						}
5569					}
5570
5571					// make sure mouse detection is enabled
5572					initMouseDetection();
5573				}
5574
5575				// init sub menus
5576				this.$firstSub = this.$root.find('ul').each(function() { self.menuInit($(this)); }).eq(0);
5577
5578				this.$firstLink = this.$root.find('a').eq(0);
5579
5580				// find current item
5581				if (this.opts.markCurrentItem) {
5582					var reDefaultDoc = /(index|default)\.[^#\?\/]*/i,
5583						reHash = /#.*/,
5584						locHref = window.location.href.replace(reDefaultDoc, ''),
5585						locHrefNoHash = locHref.replace(reHash, '');
5586					this.$root.find('a').each(function() {
5587						var href = this.href.replace(reDefaultDoc, ''),
5588							$this = $(this);
5589						if (href == locHref || href == locHrefNoHash) {
5590							$this.addClass('current');
5591							if (self.opts.markCurrentTree) {
5592								$this.parents('li').each(function() {
5593									var $this = $(this);
5594									if ($this.dataSM('sub')) {
5595										$this.children('a').addClass('current');
5596									}
5597								});
5598							}
5599						}
5600					});
5601				}
5602			},
5603			destroy: function() {
5604				this.menuHideAll();
5605				this.$root
5606					.removeData('smartmenus')
5607					.removeAttr('data-smartmenus-id')
5608					.removeDataSM('level')
5609					.unbind('.smartmenus')
5610					.undelegate('.smartmenus');
5611				var eNamespace = '.smartmenus' + this.rootId;
5612				$(document).unbind(eNamespace);
5613				$(window).unbind(eNamespace);
5614				if (this.opts.subIndicators) {
5615					this.$subArrow = null;
5616				}
5617				var self = this;
5618				$.each(this.subMenus, function() {
5619					if (this.hasClass('mega-menu')) {
5620						this.find('ul').removeDataSM('in-mega');
5621					}
5622					if (this.dataSM('shown-before')) {
5623						if (IElt8) {
5624							this.children().css({ styleFloat: '', width: '' });
5625						}
5626						if (self.opts.subMenusMinWidth || self.opts.subMenusMaxWidth) {
5627							if (!IE6) {
5628								this.css({ width: '', minWidth: '', maxWidth: '' }).removeClass('sm-nowrap');
5629							} else {
5630								this.css({ width: '', overflowX: '', overflowY: '' }).children().children('a').css('white-space', '');
5631							}
5632						}
5633						if (this.dataSM('scroll-arrows')) {
5634							this.dataSM('scroll-arrows').remove();
5635						}
5636						this.css({ zIndex: '', top: '', left: '', marginLeft: '', marginTop: '', display: '' });
5637					}
5638					if (self.opts.subIndicators) {
5639						this.dataSM('parent-a').removeClass('has-submenu').children('span.sub-arrow').remove();
5640					}
5641					this.removeDataSM('shown-before')
5642						.removeDataSM('ie-shim')
5643						.removeDataSM('scroll-arrows')
5644						.removeDataSM('parent-a')
5645						.removeDataSM('level')
5646						.removeDataSM('beforefirstshowfired')
5647						.parent().removeDataSM('sub');
5648				});
5649				if (this.opts.markCurrentItem) {
5650					this.$root.find('a.current').removeClass('current');
5651				}
5652				this.$root = null;
5653				this.$firstLink = null;
5654				this.$firstSub = null;
5655				if (this.$disableOverlay) {
5656					this.$disableOverlay.remove();
5657					this.$disableOverlay = null;
5658				}
5659				menuTrees.splice($.inArray(this, menuTrees), 1);
5660			},
5661			disable: function(noOverlay) {
5662				if (!this.disabled) {
5663					this.menuHideAll();
5664					// display overlay over the menu to prevent interaction
5665					if (!noOverlay && !this.opts.isPopup && this.$root.is(':visible')) {
5666						var pos = this.$root.offset();
5667						this.$disableOverlay = $('<div class="sm-jquery-disable-overlay"/>').css({
5668							position: 'absolute',
5669							top: pos.top,
5670							left: pos.left,
5671							width: this.$root.outerWidth(),
5672							height: this.$root.outerHeight(),
5673							zIndex: this.getStartZIndex() + 1,
5674							opacity: 0
5675						}).appendTo(document.body);
5676					}
5677					this.disabled = true;
5678				}
5679			},
5680			docClick: function(e) {
5681				// hide on any click outside the menu or on a menu link
5682				if (this.visibleSubMenus.length && !$.contains(this.$root[0], e.target) || $(e.target).is('a')) {
5683					this.menuHideAll($(e.target));
5684				}
5685			},
5686			docTouchEnd: function(e) {
5687				if (!this.lastTouch) {
5688					return;
5689				}
5690				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))) {
5691					if (this.hideTimeout) {
5692						clearTimeout(this.hideTimeout);
5693						this.hideTimeout = 0;
5694					}
5695					// hide with a delay to prevent triggering accidental unwanted click on some page element
5696					var self = this;
5697					this.hideTimeout = setTimeout(function() { self.menuHideAll($(e.target)); }, 350);
5698				}
5699				this.lastTouch = null;
5700			},
5701			docTouchMove: function(e) {
5702				if (!this.lastTouch) {
5703					return;
5704				}
5705				var touchPoint = e.originalEvent.touches[0];
5706				this.lastTouch.x2 = touchPoint.pageX;
5707				this.lastTouch.y2 = touchPoint.pageY;
5708			},
5709			docTouchStart: function(e) {
5710				var touchPoint = e.originalEvent.touches[0];
5711				this.lastTouch = { x1: touchPoint.pageX, y1: touchPoint.pageY, target: touchPoint.target };
5712			},
5713			enable: function() {
5714				if (this.disabled) {
5715					if (this.$disableOverlay) {
5716						this.$disableOverlay.remove();
5717						this.$disableOverlay = null;
5718					}
5719					this.disabled = false;
5720				}
5721			},
5722			getHeight: function($elm) {
5723				return this.getOffset($elm, true);
5724			},
5725			// returns precise width/height float values in IE9+, FF4+, recent WebKit
5726			// http://vadikom.com/dailies/offsetwidth-offsetheight-useless-in-ie9-firefox4/
5727			getOffset: function($elm, height) {
5728				var old;
5729				if ($elm.css('display') == 'none') {
5730					old = { position: $elm[0].style.position, visibility: $elm[0].style.visibility };
5731					$elm.css({ position: 'absolute', visibility: 'hidden' }).show();
5732				}
5733				var defaultView = $elm[0].ownerDocument.defaultView,
5734					compStyle = defaultView && defaultView.getComputedStyle && defaultView.getComputedStyle($elm[0], null),
5735					val = compStyle && parseFloat(compStyle[height ? 'height' : 'width']);
5736				if (val) {
5737					val += parseFloat(compStyle[height ? 'paddingTop' : 'paddingLeft'])
5738	 					+ parseFloat(compStyle[height ? 'paddingBottom' : 'paddingRight'])
5739	 					+ parseInt(compStyle[height ? 'borderTopWidth' : 'borderLeftWidth'])
5740	 					+ parseInt(compStyle[height ? 'borderBottomWidth' : 'borderRightWidth']);
5741				} else {
5742					val = height ? $elm[0].offsetHeight : $elm[0].offsetWidth;
5743				}
5744				if (old) {
5745					$elm.hide().css(old);
5746				}
5747				return val;
5748			},
5749			getWidth: function($elm) {
5750				return this.getOffset($elm);
5751			},
5752			getStartZIndex: function() {
5753				var zIndex = parseInt(this.$root.css('z-index'));
5754				return !isNaN(zIndex) ? zIndex : 1;
5755			},
5756			handleEvents: function() {
5757				return !this.disabled && this.isCSSOn();
5758			},
5759			handleItemEvents: function($a) {
5760				return this.handleEvents() && !this.isLinkInMegaMenu($a);
5761			},
5762			isCollapsible: function() {
5763				return this.$firstSub.css('position') == 'static';
5764			},
5765			isCSSOn: function() {
5766				return this.$firstLink.css('display') == 'block' || this.$firstLink.css('display') == 'inline';
5767			},
5768			isFixed: function() {
5769				return this.$root.css('position') == 'fixed';
5770			},
5771			isLinkInMegaMenu: function($a) {
5772				return !$a.parent().parent().dataSM('level');
5773			},
5774			isTouchMode: function() {
5775				return !mouse || this.isCollapsible();
5776			},
5777			itemActivate: function($a) {
5778				var $li = $a.parent(),
5779					$ul = $li.parent(),
5780					level = $ul.dataSM('level');
5781				// 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
5782				if (level > 1 && (!this.activatedItems[level - 2] || this.activatedItems[level - 2][0] != $ul.dataSM('parent-a')[0])) {
5783					var self = this;
5784					$($ul.parentsUntil('[data-smartmenus-id]', 'ul').get().reverse()).add($ul).each(function() {
5785						self.itemActivate($(this).dataSM('parent-a'));
5786					});
5787				}
5788				// hide any visible deeper level sub menus
5789				if (this.visibleSubMenus.length > level) {
5790					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--) {
5791						this.menuHide(this.visibleSubMenus[i]);
5792					}
5793				}
5794				// save new active item and sub menu for this level
5795				this.activatedItems[level - 1] = $a;
5796				this.visibleSubMenus[level - 1] = $ul;
5797				if (this.$root.triggerHandler('activate.smapi', $a[0]) === false) {
5798					return;
5799				}
5800				// show the sub menu if this item has one
5801				var $sub = $li.dataSM('sub');
5802				if ($sub && (this.isTouchMode() || (!this.opts.showOnClick || this.clickActivated))) {
5803					this.menuShow($sub);
5804				}
5805			},
5806			itemBlur: function(e) {
5807				var $a = $(e.currentTarget);
5808				if (!this.handleItemEvents($a)) {
5809					return;
5810				}
5811				this.$root.triggerHandler('blur.smapi', $a[0]);
5812			},
5813			itemClick: function(e) {
5814				var $a = $(e.currentTarget);
5815
5816				if (!this.handleItemEvents($a)) {
5817					return;
5818				}
5819				$a.removeDataSM('mousedown');
5820				if (this.$root.triggerHandler('click.smapi', $a[0]) === false) {
5821					return false;
5822				}
5823				var $sub = $a.parent().dataSM('sub');
5824				if (this.isTouchMode()) {
5825					// undo fix: prevent the address bar on iPhone from sliding down when expanding a sub menu
5826					if ($a.dataSM('href')) {
5827						$a.attr('href', $a.dataSM('href')).removeDataSM('href');
5828					}
5829					// if the sub is not visible
5830					if ($sub && (!$sub.dataSM('shown-before') || !$sub.is(':visible'))) {
5831						// try to activate the item and show the sub
5832						this.itemActivate($a);
5833						// if "itemActivate" showed the sub, prevent the click so that the link is not loaded
5834						// 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
5835						if ($sub.is(':visible')) {
5836							return false;
5837						}
5838					}
5839				} else if (this.opts.showOnClick && $a.parent().parent().dataSM('level') == 1 && $sub) {
5840					this.clickActivated = true;
5841					this.menuShow($sub);
5842					return false;
5843				}
5844				if ($a.hasClass('disabled')) {
5845					return false;
5846				}
5847				if (this.$root.triggerHandler('select.smapi', $a[0]) === false) {
5848					return false;
5849				}
5850			},
5851			itemDown: function(e) {
5852				var $a = $(e.currentTarget);
5853				if (!this.handleItemEvents($a)) {
5854					return;
5855				}
5856				$a.dataSM('mousedown', true);
5857			},
5858			itemEnter: function(e) {
5859				var $a = $(e.currentTarget);
5860				if (!this.handleItemEvents($a)) {
5861					return;
5862				}
5863				if (!this.isTouchMode()) {
5864					if (this.showTimeout) {
5865						clearTimeout(this.showTimeout);
5866						this.showTimeout = 0;
5867					}
5868					var self = this;
5869					this.showTimeout = setTimeout(function() { self.itemActivate($a); }, this.opts.showOnClick && $a.parent().parent().dataSM('level') == 1 ? 1 : this.opts.showTimeout);
5870				}
5871				this.$root.triggerHandler('mouseenter.smapi', $a[0]);
5872			},
5873			itemFocus: function(e) {
5874				var $a = $(e.currentTarget);
5875				if (!this.handleItemEvents($a)) {
5876					return;
5877				}
5878				// 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
5879				if ((!this.isTouchMode() || !$a.dataSM('mousedown')) && (!this.activatedItems.length || this.activatedItems[this.activatedItems.length - 1][0] != $a[0])) {
5880					this.itemActivate($a);
5881				}
5882				this.$root.triggerHandler('focus.smapi', $a[0]);
5883			},
5884			itemLeave: function(e) {
5885				var $a = $(e.currentTarget);
5886				if (!this.handleItemEvents($a)) {
5887					return;
5888				}
5889				if (!this.isTouchMode()) {
5890					if ($a[0].blur) {
5891						$a[0].blur();
5892					}
5893					if (this.showTimeout) {
5894						clearTimeout(this.showTimeout);
5895						this.showTimeout = 0;
5896					}
5897				}
5898				$a.removeDataSM('mousedown');
5899				this.$root.triggerHandler('mouseleave.smapi', $a[0]);
5900			},
5901			itemTouchEnd: function(e) {
5902				var $a = $(e.currentTarget);
5903				if (!this.handleItemEvents($a)) {
5904					return;
5905				}
5906				// prevent the address bar on iPhone from sliding down when expanding a sub menu
5907				var $sub = $a.parent().dataSM('sub');
5908				if ($a.attr('href').charAt(0) !== '#' && $sub && (!$sub.dataSM('shown-before') || !$sub.is(':visible'))) {
5909					$a.dataSM('href', $a.attr('href'));
5910					$a.attr('href', '#');
5911				}
5912			},
5913			menuFixLayout: function($ul) {
5914				// fixes a menu that is being shown for the first time
5915				if (!$ul.dataSM('shown-before')) {
5916					$ul.hide().dataSM('shown-before', true);
5917					// fix the layout of the items in IE<8
5918					if (IElt8) {
5919						$ul.children().css({ styleFloat: 'left', width: '100%' });
5920					}
5921				}
5922			},
5923			menuHide: function($sub) {
5924				if (this.$root.triggerHandler('beforehide.smapi', $sub[0]) === false) {
5925					return;
5926				}
5927				$sub.stop(true, true);
5928				if ($sub.is(':visible')) {
5929					var complete = function() {
5930						// unset z-index
5931						if (IElt9) {
5932							$sub.parent().css('z-index', '');
5933						} else {
5934							$sub.css('z-index', '');
5935						}
5936					};
5937					// if sub is collapsible (mobile view)
5938					if (this.isCollapsible()) {
5939						if (this.opts.collapsibleHideFunction) {
5940							this.opts.collapsibleHideFunction.call(this, $sub, complete);
5941						} else {
5942							$sub.hide(this.opts.collapsibleHideDuration, complete);
5943						}
5944					} else {
5945						if (this.opts.hideFunction) {
5946							this.opts.hideFunction.call(this, $sub, complete);
5947						} else {
5948							$sub.hide(this.opts.hideDuration, complete);
5949						}
5950					}
5951					// remove IE iframe shim
5952					if ($sub.dataSM('ie-shim')) {
5953						$sub.dataSM('ie-shim').remove();
5954					}
5955					// deactivate scrolling if it is activated for this sub
5956					if ($sub.dataSM('scroll')) {
5957						$sub.unbind('.smartmenus_scroll').removeDataSM('scroll').dataSM('scroll-arrows').hide();
5958					}
5959					// unhighlight parent item
5960					$sub.dataSM('parent-a').removeClass('highlighted');
5961					var level = $sub.dataSM('level');
5962					this.activatedItems.splice(level - 1, 1);
5963					this.visibleSubMenus.splice(level - 1, 1);
5964					this.$root.triggerHandler('hide.smapi', $sub[0]);
5965				}
5966			},
5967			menuHideAll: function($item) {
5968				if ($item != undefined && $item.parent().hasClass('menu-item') && !$item.parent().hasClass('menu-item-has-children')) return;
5969				if (this.showTimeout) {
5970					clearTimeout(this.showTimeout);
5971					this.showTimeout = 0;
5972				}
5973				// hide all subs
5974				for (var i = this.visibleSubMenus.length - 1; i > 0; i--) {
5975					this.menuHide(this.visibleSubMenus[i]);
5976				}
5977				// hide root if it's popup
5978				if (this.opts.isPopup) {
5979					this.$root.stop(true, true);
5980					if (this.$root.is(':visible')) {
5981						if (this.opts.hideFunction) {
5982							this.opts.hideFunction.call(this, this.$root);
5983						} else {
5984							this.$root.hide(this.opts.hideDuration);
5985						}
5986						// remove IE iframe shim
5987						if (this.$root.dataSM('ie-shim')) {
5988							this.$root.dataSM('ie-shim').remove();
5989						}
5990					}
5991				}
5992				this.activatedItems = [];
5993				this.visibleSubMenus = [];
5994				this.clickActivated = false;
5995				// reset z-index increment
5996				this.zIndexInc = 0;
5997			},
5998			menuIframeShim: function($ul) {
5999				// create iframe shim for the menu
6000				if (IE && this.opts.overlapControlsInIE && !$ul.dataSM('ie-shim')) {
6001					$ul.dataSM('ie-shim', $('<iframe/>').attr({ src: 'javascript:0', tabindex: -9 })
6002						.css({ position: 'absolute', top: 'auto', left: '0', opacity: 0, border: '0' })
6003					);
6004				}
6005			},
6006			menuInit: function($ul) {
6007				if (!$ul.dataSM('in-mega')) {
6008					this.subMenus.push($ul);
6009					// mark UL's in mega drop downs (if any) so we can neglect them
6010					if ($ul.hasClass('mega-menu')) {
6011						$ul.find('ul').dataSM('in-mega', true);
6012					}
6013					// get level (much faster than, for example, using parentsUntil)
6014					var level = 2,
6015						par = $ul[0];
6016					while ((par = par.parentNode.parentNode) != this.$root[0]) {
6017						level++;
6018					}
6019					// cache stuff
6020					$ul.dataSM('parent-a', $ul.prevAll('a').eq(-1))
6021						.dataSM('level', level)
6022						.parent().dataSM('sub', $ul);
6023					// add sub indicator to parent item
6024					if (this.opts.subIndicators) {
6025						$ul.dataSM('parent-a').addClass('has-submenu')[this.opts.subIndicatorsPos](this.$subArrow.clone());
6026					}
6027				}
6028			},
6029			menuPosition: function($sub) {
6030				var fixIE = $('html.ie').length;
6031				// if (fixIE) {
6032				// 	var $rowParent = $($sub).closest('.main-menu-container');
6033				// 	$rowParent.height($rowParent.height());
6034				// }
6035				var $a = $sub.dataSM('parent-a'),
6036					$li = $sub.parent(),
6037					$ul = $sub.parent().parent(),
6038					$container = $ul.closest('.row-menu-inner'),
6039					level = $sub.dataSM('level'),
6040					subW = this.getWidth($sub),
6041					subH = this.getHeight($sub),
6042					itemOffset = $a.offset(),
6043					itemX = itemOffset.left,
6044					itemY = itemOffset.top,
6045					itemW = this.getWidth($a),
6046					itemH = this.getHeight($a),
6047					$win = $(window),
6048					winX = $win.scrollLeft(),
6049					winY = $win.scrollTop(),
6050					winW = $win.width(),
6051					winH = $win.height(),
6052					containerW = $container.width(),
6053					containerOffsetX = containerW + ((winW - containerW) / 2),
6054					horizontalParent = $ul.hasClass('sm') && !$ul.hasClass('sm-vertical'),
6055					subOffsetX = level == 2 ? this.opts.mainMenuSubOffsetX : this.opts.subMenusSubOffsetX,
6056					subOffsetY = level == 2 ? this.opts.mainMenuSubOffsetY : this.opts.subMenusSubOffsetY,
6057					x, y, leftPos;
6058				if (horizontalParent) {
6059					x = this.opts.rightToLeftSubMenus ? itemW - subW - subOffsetX : subOffsetX;
6060					y = this.opts.bottomToTopSubMenus ? -subH - subOffsetY : itemH + subOffsetY;
6061				} else {
6062					x = this.opts.rightToLeftSubMenus ? subOffsetX - subW : itemW - subOffsetX;
6063					y = this.opts.bottomToTopSubMenus ? itemH - subOffsetY - subH : subOffsetY;
6064				}
6065				if (this.opts.keepInViewport && !this.isCollapsible()) {
6066					if (this.isFixed()) {
6067						itemX -= winX;
6068						itemY -= winY;
6069						winX = winY = 0;
6070					}
6071					var absX = itemX + x,
6072						absY = itemY + y;
6073					if (this.opts.rightToLeftSubMenus && absX < winX) {
6074						x = horizontalParent ? winX - absX + x : itemW - subOffsetX;
6075					} else if (!this.opts.rightToLeftSubMenus && absX + subW > winX + containerOffsetX ) {
6076						x = horizontalParent ? winX + containerOffsetX - subW - absX + x : subOffsetX - subW;
6077					}
6078					if (!horizontalParent) {
6079						if (subH < winH && absY + subH > winY + winH) {
6080							y += winY + winH - subH - absY;
6081						} else if (subH >= winH || absY < winY) {
6082							y += winY - absY;
6083						}
6084					}
6085					// do we need scrolling?
6086					// 0.49 added for the sake of IE9/FF4+ where we might be dealing with float numbers for "subH"
6087					if (mouse && (horizontalParent && (absY + subH > winY + winH + 0.49 || absY < winY) || !horizontalParent && subH > winH + 0.49)) {
6088						var self = this;
6089						if (!$sub.dataSM('scroll-arrows')) {
6090							$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]])
6091								.bind({
6092									mouseenter: function() { self.menuScroll($sub, $(this).hasClass('scroll-up')); },
6093									mouseleave: function(e) {
6094										self.menuScrollStop($sub);
6095										self.menuScrollOut($sub, e);
6096									},
6097									'mousewheel DOMMouseScroll': function(e) { e.preventDefault(); }
6098								})
6099								.insertAfter($sub)
6100							);
6101						}
6102						// bind events to show/hide arrows on hover and save scrolling data for this sub
6103						var vportY = winY - (itemY + itemH);
6104						$sub.dataSM('scroll', {
6105								vportY: vportY,
6106								subH: subH,
6107								winH: winH,
6108								step: 1
6109							})
6110							.bind({
6111								'mouseover.smartmenus_scroll': function(e) { self.menuScrollOver($sub, e); },
6112								'mouseout.smartmenus_scroll': function(e) { self.menuScrollOut($sub, e); },
6113								'mousewheel.smartmenus_scroll DOMMouseScroll.smartmenus_scroll': function(e) { self.menuScrollMousewheel($sub, e); }
6114							})
6115							.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
6115StartZIndex() + this.zIndexInc })
6116							.eq(0).css('margin-top', vportY).end()
6117							.eq(1).css('margin-top', vportY + winH - this.getHeight($sub.dataSM('scroll-arrows').eq(1))).end()
6118							.eq(horizontalParent && this.opts.bottomToTopSubMenus ? 0 : 1).show();
6119					}
6120				}
6121				// if ($sub.hasClass('mega-menu-inner')) {
6122				// 	if ($('.menu-wrapper .megamenu-diff').length > 0) {
6123				// 		leftPos = parseFloat($('.menu-wrapper .megamenu-diff').outerWidth());
6124				// 		leftPos = -leftPos;
6125				// 	} else leftPos = parseFloat($('.menu-wrapper .main-menu-container').css('paddingLeft'));
6126
6127				// 	if ($('body.header-full-width').length > 0) {
6128				// 		$sub.css({ width: $('.box-container').width()});
6129				// 		leftPos = (leftPos - parseFloat($('.menu-wrapper .row-menu-inner').css('paddingLeft')));
6130				// 	} else $sub.css({ width: containerW});
6131				// 	leftPos = leftPos + 'px';
6132
6133				// } else {
6134				// 	leftPos = $li.position().left + 'px';
6135				// }
6136				if ($sub.hasClass('mega-menu-inner')) {
6137					$sub.css({ width: containerW});
6138					if ($('body.hmenu-center-split').length > 0 && !$sub.closest('.navbar-cta').length ) {
6139						leftPos = - ($sub.closest('.menu-primary-inner')[0].offsetLeft) + 'px';
6140					} else {
6141						if ( $sub.closest('.navbar-cta').length && !$('body.hmenu').length && !$('body.hmenu-center').length ) {
6142							leftPos = - ( parseFloat($sub.closest('.navbar-cta').offset().left) - parseFloat($sub.closest('.row-menu-inner').find('.logo-container').offset().left) ) + 'px';
6143						} else if ($sub.closest('.row-menu-inner').find('.logo-container').length > 0 && !$('body.hmenu-center-split').length) {
6144							leftPos = - (parseFloat($sub.closest('.row-menu-inner').find('.logo-container').css('paddingRight')) + ($sub.closest('.row-menu-inner').find('.logo-container').width())) + 'px';
6145						} else {
6146							leftPos = '0px';
6147						}
6148					}
6149				} else {
6150					leftPos = (level > 2 ? $li.position().left - parseFloat($li.closest('ul').css('paddingLeft')) : $li.position().left ) + 'px';
6151					x = (level > 2 && x >= 0) ? x + (parseFloat($li.closest('ul').css('paddingLeft')) * 2) + 1 : x - 1;
6152				}
6153
6154				$sub.css({ top: (level > 2) ? $a[0].offsetTop : (fixIE ? itemH : '100%'), left: leftPos, marginLeft: x, marginTop: (level > 2) ? 0 : y - itemH + ($sub.closest('.menu-mini').length ? 0 : 1) });
6155				// IE iframe shim
6156				this.menuIframeShim($sub);
6157				if ($sub.dataSM('ie-shim')) {
6158					$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) });
6159				}
6160			},
6161			menuScroll: function($sub, up, wheel) {
6162				var y = parseFloat($sub.css('margin-top')),
6163					scroll = $sub.dataSM('scroll'),
6164					end = scroll.vportY + (up ? 0 : scroll.winH - scroll.subH),
6165					step = wheel || !this.opts.scrollAccelerate ? this.opts.scrollStep : Math.floor($sub.dataSM('scroll').step);
6166				$sub.add($sub.dataSM('ie-shim')).css('margin-top', Math.abs(end - y) > step ? y + (up ? step : -step) : end);
6167				y = parseFloat($sub.css('margin-top'));
6168				// show opposite arrow if appropriate
6169				if (up && y + scroll.subH > scroll.vportY + scroll.winH || !up && y < scroll.vportY) {
6170					$sub.dataSM('scroll-arrows').eq(up ? 1 : 0).show();
6171				}
6172				// accelerate when not using mousewheel to scroll
6173				if (!wheel && this.opts.scrollAccelerate && $sub.dataSM('scroll').step < this.opts.scrollStep) {
6174					$sub.dataSM('scroll').step += 0.5;
6175				}
6176				// "y" and "end" might be float numbers in IE9/FF4+ so this weird way to check is used
6177				if (Math.abs(y - end) < 1) {
6178					$sub.dataSM('scroll-arrows').eq(up ? 0 : 1).hide();
6179					$sub.dataSM('scroll').step = 1;
6180				} else if (!wheel) {
6181					var self = this;
6182					this.scrollTimeout = setTimeout(function() { self.menuScroll($sub, up); }, this.opts.scrollInterval);
6183				}
6184			},
6185			menuScrollMousewheel: function($sub, e) {
6186				var $closestSub = $(e.target).closest('ul');
6187				while ($closestSub.dataSM('in-mega')) {
6188					$closestSub = $closestSub.parent().closest('ul');
6189				}
6190				if ($closestSub[0] == $sub[0]) {
6191					var up = (e.originalEvent.wheelDelta || -e.originalEvent.detail) > 0;
6192					if ($sub.dataSM('scroll-arrows').eq(up ? 0 : 1).is(':visible')) {
6193						this.menuScroll($sub, up, true);
6194					}
6195				}
6196				e.preventDefault();
6197			},
6198			menuScrollOut: function($sub, e) {
6199				var reClass = /^scroll-(up|down)/,
6200					$closestSub = $(e.relatedTarget).closest('ul');
6201				while ($closestSub.dataSM('in-mega')) {
6202					$closestSub = $closestSub.parent().closest('ul');
6203				}
6204				if (!reClass.test((e.relatedTarget || '').className) && ($sub[0] != e.relatedTarget && !$.contains($sub[0], e.relatedTarget) || $closestSub[0] != $sub[0])) {
6205					$sub.dataSM('scroll-arrows').css('visibility', 'hidden');
6206				}
6207			},
6208			menuScrollOver: function($sub, e) {
6209				var reClass = /^scroll-(up|down)/,
6210					$closestSub = $(e.target).closest('ul');
6211				while ($closestSub.dataSM('in-mega')) {
6212					$closestSub = $closestSub.parent().closest('ul');
6213				}
6214				if (!reClass.test(e.target.className) && $closestSub[0] == $sub[0]) {
6215					$sub.dataSM('scroll-arrows').css('visibility', 'visible');
6216				}
6217			},
6218			menuScrollStop: function($sub) {
6219				if (this.scrollTimeout) {
6220					clearTimeout(this.scrollTimeout);
6221					this.scrollTimeout = 0;
6222					$sub.dataSM('scroll').step = 1;
6223				}
6224			},
6225			menuShow: function($sub) {
6226				if (!$sub.dataSM('beforefirstshowfired')) {
6227					$sub.dataSM('beforefirstshowfired', true);
6228					if (this.$root.triggerHandler('beforefirstshow.smapi', $sub[0]) === false) {
6229						return;
6230					}
6231				}
6232				if (this.$root.triggerHandler('beforeshow.smapi', $sub[0]) === false) {
6233					return;
6234				}
6235				this.menuFixLayout($sub);
6236				$sub.stop(true, true);
6237				if (!$sub.is(':visible')) {
6238					// set z-index - for IE < 9 set it to the parent LI
6239					var zIndex = this.getStartZIndex() + (++this.zIndexInc);
6240					if (IElt9) {
6241						$sub.parent().css('z-index', zIndex);
6242					} else {
6243						$sub.css('z-index', zIndex);
6244					}
6245					// highlight parent item
6246					if (this.opts.keepHighlighted || this.isCollapsible()) {
6247						if ($sub.dataSM('parent-a').attr('data-type') != 'title')
6248							$sub.dataSM('parent-a').addClass('highlighted');
6249					}
6250					// min/max-width fix - no way to rely purely on CSS as all UL's are nested
6251					if (this.opts.subMenusMinWidth || this.opts.subMenusMaxWidth) {
6252						if (!IElt8) {
6253							$sub.css({ width: ($sub.hasClass('mega-menu-inner')) ? $('.box-container').outerWidth() + 'px' : 'auto', minWidth: '', maxWidth: '' }).addClass('sm-nowrap');
6254							if (this.opts.subMenusMinWidth) {
6255							 	$sub.css('min-width', this.opts.subMenusMinWidth);
6256							}
6257							if (this.opts.subMenusMaxWidth) {
6258							 	var noMaxWidth = this.getWidth($sub);
6259							 	$sub.css('max-width', this.opts.subMenusMaxWidth);
6260								if (noMaxWidth > this.getWidth($sub)) {
6261									$sub.removeClass('sm-nowrap').css('width', this.opts.subMenusMaxWidth);
6262								}
6263							}
6264						// IE6,7
6265 						} else {
6266							$sub.children().css('styleFloat', 'none');
6267							if (IE6) {
6268								$sub.width(this.opts.subMenusMinWidth ? this.opts.subMenusMinWidth : 1)
6269									.children().children('a').css('white-space', 'nowrap');
6270							} else { // IE7
6271								$sub.css({ width: ($sub.hasClass('mega-menu-inner')) ? $('.box-container').outerWidth() + 'px' : 'auto', minWidth: '', maxWidth: '' }).addClass('sm-nowrap');
6272								if (this.opts.subMenusMinWidth) {
6273							    $sub.css('min-width', this.opts.subMenusMinWidth);
6274								}
6275							}
6276							if (this.opts.subMenusMaxWidth) {
6277								var noMaxWidth = $sub.width();
6278								if (IE6) {
6279							 		var maxWidth = $sub.css({ width: this.opts.subMenusMaxWidth, overflowX: 'hidden', overflowY: 'hidden' }).width();
6280									if (noMaxWidth > maxWidth) {
6281										$sub.css({ width: maxWidth, overflowX: 'visible', overflowY: 'visible' }).children().children('a').css('white-space', '');
6282									} else {
6283										$sub.css({ width: noMaxWidth, overflowX: 'visible', overflowY: 'visible' });
6284									}
6285								} else { // IE7
6286									$sub.css('max-width', this.opts.subMenusMaxWidth);
6287									if (noMaxWidth > $sub.width()) {
6288										$sub.removeClass('sm-nowrap').css('width', this.opts.subMenusMaxWidth);
6289									} else {
6290										$sub.width(noMaxWidth);
6291									}
6292								}
6293							} else {
6294							 	$sub.width($sub.width());
6295							}
6296							$sub.children().css('styleFloat', 'left');
6297						}
6298					}
6299					this.menuPosition($sub);
6300					// insert IE iframe shim
6301					if ($sub.dataSM('ie-shim')) {
6302						$sub.dataSM('ie-shim').insertBefore($sub);
6303					}
6304					var complete = function() {
6305						// fix: "overflow: hidden;" is not reset on animation complete in jQuery < 1.9.0 in Chrome when global "box-sizing: border-box;" is used
6306						$sub.css('overflow', '');
6307					};
6308					// if sub is collapsible (mobile view)
6309					if (this.isCollapsible()) {
6310						if (this.opts.collapsibleShowFunction) {
6311							this.opts.collapsibleShowFunction.call(this, $sub, complete);
6312						} else {
6313							$sub.show(this.opts.collapsibleShowDuration, complete);
6314						}
6315					} else {
6316						if (this.opts.showFunction) {
6317							this.opts.showFunction.call(this, $sub, complete);
6318						} else {
6319							$sub.show(this.opts.showDuration, complete);
6320						}
6321					}
6322					// save new sub menu for this level
6323					this.visibleSubMenus[$sub.dataSM('level') - 1] = $sub;
6324					this.$root.triggerHandler('show.smapi', $sub[0]);
6325				}
6326			},
6327			popupHide: function(noHideTimeout) {
6328				if (this.hideTimeout) {
6329					clearTimeout(this.hideTimeout);
6330					this.hideTimeout = 0;
6331				}
6332				var self = this;
6333				this.hideTimeout = setTimeout(function() {
6334					self.menuHideAll();
6335				}, noHideTimeout ? 1 : this.opts.hideTimeout);
6336			},
6337			popupShow: function(left, top) {
6338				if (!this.opts.isPopup) {
6339					alert('SmartMenus jQuery Error:\n\nIf you want to show this menu via the "popupShow" method, set the isPopup:true option.');
6340					return;
6341				}
6342				if (this.hideTimeout) {
6343					clearTimeout(this.hideTimeout);
6344					this.hideTimeout = 0;
6345				}
6346				this.menuFixLayout(this.$root);
6347				this.$root.stop(true, true);
6348				if (!this.$root.is(':visible')) {
6349					this.$root.css({ left: left, top: top });
6350					// IE iframe shim
6351					this.menuIframeShim(this.$root);
6352					if (this.$root.dataSM('ie-shim')) {
6353						this.$root.dataSM('ie-shim').css({ zIndex: this.$root.css('z-index'), width: this.getWidth(this.$root), height: this.getHeight(this.$root), left
6353: left, top: top }).insertBefore(this.$root);
6354					}
6355					// show menu
6356					if (this.opts.showFunction) {
6357						this.opts.showFunction.call(this, this.$root);
6358					} else {
6359						this.$root.show(this.opts.showDuration);
6360					}
6361					this.visibleSubMenus[0] = this.$root;
6362				}
6363			},
6364			refresh: function() {
6365				this.menuHideAll();
6366				this.$root.find('ul').each(function() {
6367						var $this = $(this);
6368						if ($this.dataSM('scroll-arrows')) {
6369							$this.dataSM('scroll-arrows').remove();
6370						}
6371					})
6372					.removeDataSM('in-mega')
6373					.removeDataSM('shown-before')
6374					.removeDataSM('ie-shim')
6375					.removeDataSM('scroll-arrows')
6376					.removeDataSM('parent-a')
6377					.removeDataSM('level')
6378					.removeDataSM('beforefirstshowfired');
6379				this.$root.find('a.has-submenu').removeClass('has-submenu')
6380					.parent().removeDataSM('sub');
6381				if (this.opts.subIndicators) {
6382					this.$root.find('span.sub-arrow').remove();
6383				}
6384				if (this.opts.markCurrentItem) {
6385					this.$root.find('a.current').removeClass('current');
6386				}
6387				this.subMenus = [];
6388				this.init(true);
6389			},
6390			rootOut: function(e) {
6391				if (!this.handleEvents() || this.isTouchMode() || e.target == this.$root[0]) {
6392					return;
6393				}
6394				if (this.hideTimeout) {
6395					clearTimeout(this.hideTimeout);
6396					this.hideTimeout = 0;
6397				}
6398				if (!this.opts.showOnClick || !this.opts.hideOnClick) {
6399					var self = this;
6400					this.hideTimeout = setTimeout(function() { self.menuHideAll(); }, this.opts.hideTimeout);
6401				}
6402			},
6403			rootOver: function(e) {
6404				if (!this.handleEvents() || this.isTouchMode() || e.target == this.$root[0]) {
6405					return;
6406				}
6407				if (this.hideTimeout) {
6408					clearTimeout(this.hideTimeout);
6409					this.hideTimeout = 0;
6410				}
6411			},
6412			winResize: function(e) {
6413				if (!this.handleEvents()) {
6414					// we still need to resize the disable overlay if it's visible
6415					if (this.$disableOverlay) {
6416						var pos = this.$root.offset();
6417	 					this.$disableOverlay.css({
6418							top: pos.top,
6419							left: pos.left,
6420							width: this.$root.outerWidth(),
6421							height: this.$root.outerHeight()
6422						});
6423					}
6424					return;
6425				}
6426				// hide sub menus on resize - on mobile do it only on orientation change
6427				if (!this.isCollapsible() && (!('onorientationchange' in window) || e.type == 'orientationchange')) {
6428					if (this.activatedItems.length) {
6429						this.activatedItems[this.activatedItems.length - 1][0].blur();
6430					}
6431					this.menuHideAll();
6432				}
6433			}
6434		}
6435	});
6436
6437	$.fn.dataSM = function(key, val) {
6438		if (val) {
6439			return this.data(key + '_smartmenus', val);
6440		}
6441		return this.data(key + '_smartmenus');
6442	}
6443
6444	$.fn.removeDataSM = function(key) {
6445		return this.removeData(key + '_smartmenus');
6446	}
6447
6448	$.fn.smartmenus = function(options) {
6449		if (typeof options == 'string') {
6450			var args = arguments,
6451				method = options;
6452			Array.prototype.shift.call(args);
6453			return this.each(function() {
6454				var smartmenus = $(this).data('smartmenus');
6455				if (smartmenus && smartmenus[method]) {
6456					smartmenus[method].apply(smartmenus, args);
6457				}
6458			});
6459		}
6460		var opts = $.extend({}, $.fn.smartmenus.defaults, options);
6461		return this.each(function() {
6462			new $.SmartMenus(this, opts);
6463		});
6464	}
6465
6466	// default settings
6467	$.fn.smartmenus.defaults = {
6468		isPopup:		false,		// is this a popup menu (can be shown via the popupShow/popupHide methods) or a permanent menu bar
6469		mainMenuSubOffsetX:	0,		// pixels offset from default position
6470		mainMenuSubOffsetY:	0,		// pixels offset from default position
6471		subMenusSubOffsetX:	0,		// pixels offset from default position
6472		subMenusSubOffsetY:	0,		// pixels offset from default position
6473		subMenusMinWidth:	'10em',		// min-width for the sub menus (any CSS unit) - if set, the fixed width set in CSS will be ignored
6474		subMenusMaxWidth:	'20em',		// max-width for the sub menus (any CSS unit) - if set, the fixed width set in CSS will be ignored
6475		subIndicators: 		true,		// create sub menu indicators - creates a SPAN and inserts it in the A
6476		subIndicatorsPos: 	'prepend',	// position of the SPAN relative to the menu item content ('prepend', 'append')
6477		subIndicatorsText:	'+',		// [optionally] add text in the SPAN (e.g. '+') (you may want to check the CSS for the sub indicators too)
6478		scrollStep: 		30,		// pixels step when scrolling long sub menus that do not fit in the viewport height
6479		scrollInterval:		30,		// interval between each scrolling step
6480		scrollAccelerate:	true,		// accelerate scrolling or use a fixed step
6481		showTimeout:		250,		// timeout before showing the sub menus
6482		hideTimeout:		500,		// timeout before hiding the sub menus
6483		showDuration:		0,		// duration for show animation - set to 0 for no animation - matters only if showFunction:null
6484		showFunction:		null,		// custom function to use when showing a sub menu (the default is the jQuery 'show')
6485							// don't forget to call complete() at the end of whatever you do
6486							// e.g.: function($ul, complete) { $ul.fadeIn(250, complete); }
6487		hideDuration:		0,		// duration for hide animation - set to 0 for no animation - matters only if hideFunction:null
6488		hideFunction:		function($ul, complete) { $ul.fadeOut(200, complete); },	// custom function to use when hiding a sub menu (the default is the jQuery 'hide')
6489							// don't forget to call complete() at the end of whatever you do
6490							// e.g.: function($ul, complete) { $ul.fadeOut(250, complete); }
6491		collapsibleShowDuration:0,		// duration for show animation for collapsible sub menus - matters only if collapsibleShowFunction:null
6492		collapsibleShowFunction:function($ul, complete) { $ul.slideDown(200, complete); },	// custom function to use when showing a collapsible sub menu
6493							// (i.e. when mobile styles are used to make the sub menus collapsible)
6494		collapsibleHideDuration:0,		// duration for hide animation for collapsible sub menus - matters only if collapsibleHideFunction:null
6495		collapsibleHideFunction:function($ul, complete) { $ul.slideUp(200, complete); },	// custom function to use when hiding a collapsible sub menu
6496							// (i.e. when mobile styles are used to make the sub menus collapsible)
6497		showOnClick:		false,		// show the first-level sub menus onclick instead of onmouseover (matters only for mouse input)
6498		hideOnClick:		true,		// hide the sub menus on click/tap anywhere on the page
6499		keepInViewport:		true,		// reposition the sub menus if needed to make sure they always appear inside the viewport
6500		keepHighlighted:	true,		// keep all ancestor items of the current sub menu highlighted (adds the 'highlighted' class to the A's)
6501		markCurrentItem:	false,		// automatically add the 'current' class to the A element of the item linking to the current URL
6502		markCurrentTree:	true,		// add the 'current' class also to the A elements of all ancestor items of the current item
6503		rightToLeftSubMenus:	false,		// right to left display of the sub menus (check the CSS for the sub indicators' position)
6504		bottomToTopSubMenus:	false,		// bottom to top display of the sub menus
6505		overlapControlsInIE:	true		// make sure sub menus appear on top of special OS controls in IE (i.e. SELECT, OBJECT, EMBED, etc.)
6506	};
6507
6508})(jQuery);
6509
6510/*
6511 * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
6512 *
6513 * Uses the built in easing capabilities added In jQuery 1.1
6514 * to offer multiple easing options
6515 *
6516 * TERMS OF USE - jQuery Easing
6517 * 
6518 * Open source under the BSD License. 
6519 * 
6520 * Copyright © 2008 George McGinley Smith
6521 * All rights reserved.
6522 * 
6523 * Redistribution and use in source and binary forms, with or without modification, 
6524 * are permitted provided that the following conditions are met:
6525 * 
6526 * Redistributions of source code must retain the above copyright notice, this list of 
6527 * conditions and the following disclaimer.
6528 * Redistributions in binary form must reproduce the above copyright notice, this list 
6529 * of conditions and the following disclaimer in the documentation and/or other materials 
6530 * provided with the distribution.
6531 * 
6532 * Neither the name of the author nor the names of contributors may be used to endorse 
6533 * or promote products derived from this software without specific prior written permission.
6534 * 
6535 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
6536 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
6537 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
6538 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
vendor: 5,091 bytes, lines 6539-6684
6539 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
6540 *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
6541 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
6542 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
6543 * OF THE POSSIBILITY OF SUCH DAMAGE. 
6544 *
6545*/
6546
6547// t: current time, b: begInnIng value, c: change In value, d: duration
6548jQuery.easing['jswing'] = jQuery.easing['swing'];
6549
6550jQuery.extend( jQuery.easing,
6551{
6552	def: 'easeOutQuad',
6553	swing: function (x, t, b, c, d) {
6554		//alert(jQuery.easing.default);
6555		return jQuery.easing[jQuery.easing.def](x, t, b, c, d);
6556	},
6557	easeInQuad: function (x, t, b, c, d) {
6558		return c*(t/=d)*t + b;
6559	},
6560	easeOutQuad: function (x, t, b, c, d) {
6561		return -c *(t/=d)*(t-2) + b;
6562	},
6563	easeInOutQuad: function (x, t, b, c, d) {
6564		if ((t/=d/2) < 1) return c/2*t*t + b;
6565		return -c/2 * ((--t)*(t-2) - 1) + b;
6566	},
6567	easeInCubic: function (x, t, b, c, d) {
6568		return c*(t/=d)*t*t + b;
6569	},
6570	easeOutCubic: function (x, t, b, c, d) {
6571		return c*((t=t/d-1)*t*t + 1) + b;
6572	},
6573	easeInOutCubic: function (x, t, b, c, d) {
6574		if ((t/=d/2) < 1) return c/2*t*t*t + b;
6575		return c/2*((t-=2)*t*t + 2) + b;
6576	},
6577	easeInQuart: function (x, t, b, c, d) {
6578		return c*(t/=d)*t*t*t + b;
6579	},
6580	easeOutQuart: function (x, t, b, c, d) {
6581		return -c * ((t=t/d-1)*t*t*t - 1) + b;
6582	},
6583	easeInOutQuart: function (x, t, b, c, d) {
6584		if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
6585		return -c/2 * ((t-=2)*t*t*t - 2) + b;
6586	},
6587	easeInQuint: function (x, t, b, c, d) {
6588		return c*(t/=d)*t*t*t*t + b;
6589	},
6590	easeOutQuint: function (x, t, b, c, d) {
6591		return c*((t=t/d-1)*t*t*t*t + 1) + b;
6592	},
6593	easeInOutQuint: function (x, t, b, c, d) {
6594		if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
6595		return c/2*((t-=2)*t*t*t*t + 2) + b;
6596	},
6597	easeInSine: function (x, t, b, c, d) {
6598		return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
6599	},
6600	easeOutSine: function (x, t, b, c, d) {
6601		return c * Math.sin(t/d * (Math.PI/2)) + b;
6602	},
6603	easeInOutSine: function (x, t, b, c, d) {
6604		return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
6605	},
6606	easeInExpo: function (x, t, b, c, d) {
6607		return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
6608	},
6609	easeOutExpo: function (x, t, b, c, d) {
6610		return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
6611	},
6612	easeInOutExpo: function (x, t, b, c, d) {
6613		if (t==0) return b;
6614		if (t==d) return b+c;
6615		if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
6616		return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
6617	},
6618	easeInCirc: function (x, t, b, c, d) {
6619		return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
6620	},
6621	easeOutCirc: function (x, t, b, c, d) {
6622		return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
6623	},
6624	easeInOutCirc: function (x, t, b, c, d) {
6625		if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
6626		return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
6627	},
6628	easeInElastic: function (x, t, b, c, d) {
6629		var s=1.70158;var p=0;var a=c;
6630		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
6631		if (a < Math.abs(c)) { a=c; var s=p/4; }
6632		else var s = p/(2*Math.PI) * Math.asin (c/a);
6633		return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
6634	},
6635	easeOutElastic: function (x, t, b, c, d) {
6636		var s=1.70158;var p=0;var a=c;
6637		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
6638		if (a < Math.abs(c)) { a=c; var s=p/4; }
6639		else var s = p/(2*Math.PI) * Math.asin (c/a);
6640		return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
6641	},
6642	easeInOutElastic: function (x, t, b, c, d) {
6643		var s=1.70158;var p=0;var a=c;
6644		if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5);
6645		if (a < Math.abs(c)) { a=c; var s=p/4; }
6646		else var s = p/(2*Math.PI) * Math.asin (c/a);
6647		if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
6648		return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
6649	},
6650	easeInBack: function (x, t, b, c, d, s) {
6651		if (s == undefined) s = 1.70158;
6652		return c*(t/=d)*t*((s+1)*t - s) + b;
6653	},
6654	easeOutBack: function (x, t, b, c, d, s) {
6655		if (s == undefined) s = 1.70158;
6656		return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
6657	},
6658	easeInOutBack: function (x, t, b, c, d, s) {
6659		if (s == undefined) s = 1.70158; 
6660		if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
6661		return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
6662	},
6663	easeInBounce: function (x, t, b, c, d) {
6664		return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b;
6665	},
6666	easeOutBounce: function (x, t, b, c, d) {
6667		if ((t/=d) < (1/2.75)) {
6668			return c*(7.5625*t*t) + b;
6669		} else if (t < (2/2.75)) {
6670			return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
6671		} else if (t < (2.5/2.75)) {
6672			return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
6673		} else {
6674			return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
6675		}
6676	},
6677	easeInOutBounce: function (x, t, b, c, d) {
6678		if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
6679		return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
6680	}
6681});
6682
6683/*
6684 *
6685 * TERMS OF USE - EASING EQUATIONS
6686 * 
6687 * Open source under the BSD License. 
6688 * 
6689 * Copyright © 2001 Robert Penner
6690 * All rights reserved.
6691 * 
6692 * Redistribution and use in source and binary forms, with or without modification, 
6693 * are permitted provided that the following conditions are met:
6694 * 
6695 * Redistributions of source code must retain the above copyright notice, this list of 
6696 * conditions and the following disclaimer.
6697 * Redistributions in binary form must reproduce the above copyright notice, this list 
6698 * of conditions and the following disclaimer in the documentation and/or other materials 
6699 * provided with the distribution.
6700 * 
6701 * Neither the name of the author nor the names of contributors may be used to endorse 
6702 * or promote products derived from this software without specific prior written permission.
6703 * 
6704 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
6705 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
6706 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
6707 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
6708 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
6709 *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
6710 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
6711 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
6712 * OF THE POSSIBILITY OF SUCH DAMAGE. 
6713 *
6714 */
6715
6716(function($, window, undefined) {
6717    // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
6718    // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
6719 
6720    // requestAnimationFrame polyfill by Erik Möller
6721    // fixes from Paul Irish and Tino Zijdel
6722 
6723    var lastTime = 0,
6724        running,
6725        animate = function (elem) {
6726            if (running) {
6727                window.requestAnimationFrame(animate, elem);
6728                jQuery.fx.tick();
6729            }
6730        },
6731        vendors = ['ms', 'moz', 'webkit', 'o'];
6732 
6733    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
6734        window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
6735        window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
6736            || window[vendors[x]+'CancelRequestAnimationFrame'];
6737    }
6738 
6739    if (!window.requestAnimationFrame)
6740        window.requestAnimationFrame = function(fn, element) {
6741            var currTime = new Date().getTime(),
6742                delta = currTime - lastTime,
6743                timeToCall = Math.max(0, 16 - delta);
6744 
6745            var id = window.setTimeout(function() {
6746                    fn(currTime + timeToCall);
6747                },
6748                timeToCall
6749            );
6750 
6751            lastTime = currTime + timeToCall;
6752 
6753            return id;
6754        };
6755 
6756    if (!window.cancelAnimationFrame) {
6757        window.cancelAnimationFrame = function(id) {
6758            clearTimeout(id);
6759        };
6760    }
6761 
6762    jQuery.fx.timer = function (timer) {
6763        if (timer() && jQuery.timers.push(timer) && !running) {
6764            running = true;
6765            animate(timer.elem);
6766        }
6767    };
6768 
6769    jQuery.fx.stop = function() {
6770        running = false;
6771    };
6772 
6773}(jQuery, this));
6774
6775/*! Copyright (c) 2011 Brandon Aaron (http://brandonaaron.net)
6776 * Licensed under the MIT License (LICENSE.txt).
6777 *
6778 * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
6779 * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
6780 * Thanks to: Seamus Leahy for adding deltaX and deltaY
6781 *
6782 * Version: 3.0.6
6783 *
6784 * Requires: 1.2.2+
6785 */
6786(function($) {
6787    var types = ['DOMMouseScroll', 'mousewheel'];
6788    if ($.event.fixHooks) {
6789        for (var i = types.length; i;) {
6790            $.event.fixHooks[types[--i]] = $.event.mouseHooks;
6791        }
6792    }
6793    $.event.special.mousewheel = {
6794        setup: function() {
6795            if (this.addEventListener) {
6796                for (var i = types.length; i;) {
6797                    this.addEventListener(types[--i], handler, false);
6798                }
6799            } else {
6800                this.onmousewheel = handler;
6801            }
6802        },
6803        teardown: function() {
6804            if (this.removeEventListener) {
6805                for (var i = types.length; i;) {
6806                    this.removeEventListener(types[--i], handler, false);
6807                }
6808            } else {
6809                this.onmousewheel = null;
6810            }
6811        }
6812    };
6813    $.fn.extend({
6814        mousewheel: function(fn) {
6815            return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
6816        },
6817        unmousewheel: function(fn) {
6818            return this.unbind("mousewheel", fn);
6819        }
6820    });
6821
6822    function handler(event) {
6823        var orgEvent = event || window.event,
6824            args = [].slice.call(arguments, 1),
6825            delta = 0,
6826            returnValue = true,
6827            deltaX = 0,
6828            deltaY = 0;
6829        event = $.event.fix(orgEvent);
6830        event.type = "mousewheel";
6831        // Old school scrollwheel delta
6832        if (orgEvent.wheelDelta) {
6833            delta = orgEvent.wheelDelta / 120;
6834        }
6835        if (orgEvent.detail) {
6836            delta = -orgEvent.detail / 3;
6837        }
6838        // New school multidimensional scroll (touchpads) deltas
6839        deltaY = delta;
6840        // Gecko
6841        if (orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS) {
6842            deltaY = 0;
6843            deltaX = -1 * delta;
6844        }
6845        // Webkit
6846        if (orgEvent.wheelDeltaY !== undefined) {
6847            deltaY = orgEvent.wheelDeltaY / 120;
6848        }
6849        if (orgEvent.wheelDeltaX !== undefined) {
6850            deltaX = -1 * orgEvent.wheelDeltaX / 120;
6851        }
6852        // Add event and delta to the front of the arguments
6853        args.unshift(event, delta, deltaX, deltaY);
6854        return ($.event.dispatch || $.event.handle).apply(this, args);
6855    }
6856})(jQuery);
6857
6858/**
6859 * jQuery iLightBox - Revolutionary Lightbox Plugin
6860 * http://www.ilightbox.net/
6861 *
6862 * @version: 2.2.4 - October 14, 2017
6863 *
6864 * @author: iProDev (Hemn Chawroka)
6865 *          http://www.iprodev.com/
6866 *
6867 */
6868(function($, window, undefined) {
6869
6870	var extensions = {
6871			flash: ['swf'],
6872			image: ['bmp', 'gif', 'jpeg', 'jpg', 'png', 'tiff', 'tif', 'jfif', 'jpe'],
6873			iframe: ['asp', 'aspx', 'cgi', 'cfm', 'htm', 'html', 'jsp', 'php', 'pl', 'php3', 'php4', 'php5', 'phtml', 'rb', 'rhtml', 'shtml', 'txt'],
6874			video: ['avi', 'mov', 'mpg', 'mpeg', 'movie', 'mp4', 'webm', 'ogv', 'ogg', '3gp', 'm4v']
6875		},
6876
6877		// Global DOM elements
6878		$win = $(window),
6879		$doc = $(document),
6880
6881		// Support indicators
6882		browser,
6883		transform,
6884		gpuAcceleration,
6885		fullScreenApi = '',
6886		userAgent = navigator.userAgent || navigator.vendor || window.opera,
6887		supportTouch = !!('ontouchstart' in window) && (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent)),
6888		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)),
6889
6890		// Events
6891		clickEvent = supportTouch ? "itap.iLightBox" : "click.iLightBox",
6892		touchStartEvent = supportTouch ? "touchstart.iLightBox" : "mousedown.iLightBox",
6893		touchStopEvent = supportTouch ? "touchend.iLightBox" : "mouseup.iLightBox",
6894		touchMoveEvent = supportTouch ? "touchmove.iLightBox" : "mousemove.iLightBox",
6895
6896		// Math shorthands
6897		abs = Math.abs,
6898		sqrt = Math.sqrt,
6899		round = Math.round,
6900		max = Math.max,
6901		min = Math.min,
6902		floor = Math.floor,
6903		random = Math.random,
6904
6905		pluginspages = {
6906			quicktime: 'http://www.apple.com/quicktime/download',
6907			flash: 'http://www.adobe.com/go/getflash'
6908		},
6909
6910		iLightBox = function(el, options, items, instant) {
6911			var iL = this;
6912
6913			iL.options = options,
6914				iL.selector = el.selector || el,
6915				iL.context = el.context,
6916				iL.instant = instant;
6917
6918			if (items.length < 1) iL.attachItems();
6919			else iL.items = items;
6920
6921			iL.vars = {
6922				total: iL.items.length,
6923				start: 0,
6924				current: null,
6925				next: null,
6926				prev: null,
6927				BODY: $('body'),
6928				loadRequests: 0,
6929				overlay: $('<div class="ilightbox-overlay"></div>'),
6930				loader: $('<div class="ilightbox-loader"><div></div></div>'),
6931				toolbar: $('<div class="ilightbox-toolbar"></div>'),
6932				innerToolbar: $('<div class="ilightbox-inner-toolbar"></div>'),
6933				title: $('<div class="ilightbox-title"></div>'),
6934				closeButton: $('<a class="ilightbox-close" title="' + iL.options.text.close + '"></a>'),
6935				fullScreenButton: $('<a class="ilightbox-fullscreen" title="' + iL.options.text.enterFullscreen + '"></a>'),
6936				innerPlayButton: $('<a class="ilightbox-play" title="' + iL.options.text.slideShow + '"></a>'),
6937				innerNextButton: $('<a class="ilightbox-next-button" title="' + iL.options.text.next + '"></a>'),
6938				innerPrevButton: $('<a class="ilightbox-prev-button" title="' + iL.options.text.previous + '"></a>'),
6939				holder: $('<div class="ilightbox-holder' + (supportTouch ? ' supportTouch' : '') + '" ondragstart="return false;"><div class="ilightbox-container"></div></div>'),
6940				nextPhoto: $('<div class="ilightbox-holder' + (supportTouch ? ' supportTouch' : '') + ' ilightbox-next" ondragstart="return false;"><div class="ilightbox-container"></div></div>'),
6941				prevPhoto: $('<div class="ilightbox-holder' + (supportTouch ? ' supportTouch' : '') + ' ilightbox-prev" ondragstart="return false;"><div class="ilightbox-container"></div></div>'),
6942				nextButton: $('<a class="ilightbox-button ilightbox-next-button" ondragstart="return false;" title="' + iL.options.text.next + '"><span></span></a>'),
6943				prevButton: $('<a class="ilightbox-button ilightbox-prev-button" ondragstart="return false;" title="' + iL.options.text.previous + '"><span></span></a>'),
6944				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>'),
6945				thumbs: false,
6946				nextLock: false,
6947				prevLock: false,
6948				hashLock: false,
6949				isMobile: false,
6950				mobileMaxWidth: 980,
6951				isInFullScreen: false,
6952				isSwipe: false,
6953				mouseID: 0,
6954				cycleID: 0,
6955				isPaused: 0,
6956				captionHeight: 39,
6957			};
6958
6959			// Hideable elements with mousemove event
6960			iL.vars.hideableElements = iL.vars.nextButton.add(iL.vars.prevButton);
6961
6962			iL.normalizeItems();
6963
6964			//Check necessary plugins
6965			iL.availPlugins();
6966
6967			//Set startFrom
6968			iL.options.startFrom = (iL.options.startFrom > 0 && iL.options.startFrom >= iL.vars.total) ? iL.vars.total - 1 : iL.options.startFrom;
6969
6970			//If randomStart
6971			iL.options.startFrom = (iL.options.randomStart) ? floor(random() * iL.vars.total) : iL.options.startFrom;
6972			iL.vars.start = iL.options.startFrom;
6973
6974			if (instant) iL.instantCall();
6975			else iL.patchItemsEvents();
6976
6977			if (iL.options.linkId) {
6978				iL.hashChangeHandler();
6979				$win.iLightBoxHashChange(function() {
6980					iL.hashChangeHandler();
6981				});
6982			}
6983
6984			if (supportTouch) {
6985				var RegExp = /(click|mouseenter|mouseleave|mouseover|mouseout)/ig,
6986					replace = "itap";
6987				iL.options.caption.show = iL.options.caption.show.replace(RegExp, replace),
6988					iL.options.caption.hide = iL.options.caption.hide.replace(RegExp, replace),
6989					iL.options.social.show = iL.options.social.show.replace(RegExp, replace),
6990					iL.options.social.hide = iL.options.social.hide.replace(RegExp, replace);
6991			}
6992
6993			if (iL.options.controls.arrows || $(window).width() < UNCODE.mediaQuery) {
6994				$.extend(iL.options.styles, {
6995					nextOffsetX: ($(window).width() > UNCODE.mediaQuery) ? 36 : 4,
6996					prevOffsetX: ($(window).width() > UNCODE.mediaQuery) ? 36 : 4,
6997					nextOpacity: 0,
6998					prevOpacity: 0
6999				});
7000			}
7001		};
7002
7003	//iLightBox helpers
7004	iLightBox.prototype = {
7005		showLoader: function() {
7006			var iL = this;
7007			iL.vars.loadRequests += 1;
7008			if (iL.options.path.toLowerCase() == "horizontal") iL.vars.loader.addClass('ilightbox-show').stop().animate({
7009				//top: '-30px'
7010				opacity: '1'
7011			}, iL.options.show.speed, 'easeOutCirc');
7012			else iL.vars.loader.addClass('ilightbox-show').stop().animate({
7013				//left: '-30px'
7014				opacity: '1'
7015			}, iL.options.show.speed, 'easeOutCirc');
7016		},
7017
7018		hideLoader: function() {
7019			var iL = this;
7020			iL.vars.loadRequests -= 1;
7021			iL.vars.loadRequests = (iL.vars.loadRequests < 0) ? 0 : iL.vars.loadRequests;
7022			if (iL.options.path.toLowerCase() == "horizontal") {
7023				if (iL.vars.loadRequests <= 0) iL.vars.loader.removeClass('ilightbox-show').stop().animate({
7024					//top: '-192px'
7025					opacity: '0'
7026				}, iL.options.show.speed, 'easeInCirc');
7027			} else {
7028				if (iL.vars.loadRequests <= 0) iL.vars.loader.removeClass('ilightbox-show').stop().animate({
7029					//left: '-192px'
7030					opacity: '0'
7031				}, iL.options.show.speed, 'easeInCirc');
7032			}
7033		},
7034
7035		createUI: function() {
7036			var iL = this;
7037
7038			iL.ui = {
7039				currentElement: iL.vars.holder,
7040				nextElement: iL.vars.nextPhoto,
7041				prevElement: iL.vars.prevPhoto,
7042				currentItem: iL.vars.current,
7043				nextItem: iL.vars.next,
7044				prevItem: iL.vars.prev,
7045				hide: function() {
7046					iL.closeAction();
7047				},
7048				refresh: function() {
7049					(arguments.length > 0) ? iL.repositionPhoto(true): iL.repositionPhoto();
7050				},
7051				fullscreen: function() {
7052					iL.fullScreenAction();
7053				}
7054			};
7055		},
7056
7057		attachItems: function() {
7058			var iL = this,
7059				itemsObject = new Array(),
7060				items = new Array();
7061
7062			$(iL.selector, iL.context).each(function() {
7063				var t = $(this),
7064					URL = t.attr(iL.options.attr) || null,
7065					options = t.data("options") && eval("({" + t.data("options") + "})") || {},
7066					caption = t.data('caption'),
7067					title = t.data('title'),
7068					type = t.data('type') || getTypeByExtension(URL),
7069					clone = t.data('lbox-clone') || false;
7070
7071				if (t.data('lbox-clone') != undefined) return;
7072
7073				//Uncode addition ##START##
7074				if ( typeof t.attr('data-album') != 'undefined' && t.attr('data-album') != '' ) {
7075					var ALBUM_URLS = JSON.parse(t.attr('data-album')),
7076						URL_i,
7077						URLS_LENGHT = ALBUM_URLS.length;
7078					for (URL_i = 0; URL_i < URLS_LENGHT; URL_i++) {
7079						if ( typeof ALBUM_URLS[URL_i].url !== 'undefined' && ALBUM_URLS[URL_i].url != '' ) {
7080							var item_opts = 'width:' + ALBUM_URLS[URL_i].width + ',height:' + ALBUM_URLS[URL_i].height + ',thumbnail: \'' + ALBUM_URLS[URL_i].thumbnail + '\'';
7081							item_opts = item_opts && eval("({" + item_opts + "})") || {},
7082							items.push({
7083								URL: ALBUM_URLS[URL_i].url,
7084								caption: ALBUM_URLS[URL_i].caption,
7085								title: ALBUM_URLS[URL_i].title,
7086								type: getTypeByExtension(ALBUM_URLS[URL_i].url),
7087								options: item_opts,
7088								clone: clone
7089							});
7090							var newT = t;
7091							newT.attr('href',ALBUM_URLS[URL_i].url);
7092							if (!iL.instant) itemsObject.push(newT);
7093						}
7094					}
7095				} else {
7096				//Uncode addition ##END##
7097					items.push({
7098						URL: URL,
7099						caption: caption,
7100						title: title,
7101						type: type,
7102						options: options,
7103						clone: clone
7104					});
7105				}
7106
7107
7108				if (iL.vars != undefined) iL.vars.total = items.length;
7109
7110				if (!iL.instant) itemsObject.push(t);
7111			});
7112
7113			iL.items = items,
7114				iL.itemsObject = itemsObject;
7115
7116		},
7117
7118		normalizeItems: function() {
7119			var iL = this,
7120				newItems = new Array();
7121
7122			$.each(iL.items, function(key, val) {
7123
7124				if (typeof val == "string") val = {
7125					url: val
7126				};
7127
7128				var URL = val.url || val.URL || null,
7129					options = val.options || {},
7130					caption = val.caption || null,
7131					title = val.title || null,
7132					type = (val.type) ? val.type.toLowerCase() : getTypeByExtension(URL),
7133					ext = (typeof URL != 'object') ? getExtension(URL) : '';
7134
7135				options.thumbnail = options.thumbnail || ((type == "image") ? URL : null),
7136				options.videoType = options.videoType || null,
7137				options.skin = options.skin || iL.options.skin,
7138				options.width = options.width || null,
7139				options.height = options.height || null,
7140				options.mousewheel = (typeof options.mousewheel != 'undefined') ? options.mousewheel : true,
7141				options.swipe = (typeof options.swipe != 'undefined') ? options.swipe : true,
7142				options.social = (typeof options.social != 'undefined') ? options.social : iL.options.social.buttons && $.extend({}, {}, iL.options.social.buttons);
7143
7144				if (type == "video") {
7145					options.html5video = (typeof options.html5video != 'undefined') ? options.html5video : {};
7146
7147					options.html5video.webm = options.html5video.webm || options.html5video.WEBM || null;
7148					options.html5video.controls = (typeof options.html5video.controls != 'undefined') ? options.html5video.controls : "controls";
7149					options.html5video.preload = options.html5video.preload || "metadata";
7150					options.html5video.autoplay = (typeof options.html5video.autoplay != 'undefined') ? options.html5video.autoplay : false;
7151				}
7152
7153				if (!options.width || !options.height) {
7154					if (type == "video") options.width = 1280, options.height = 720;
7155					else if (type == "iframe") options.width = '100%', options.height = '90%';
7156					else if (type == "flash") options.width = 1280, options.height = 720;
7157				}
7158
7159				delete val.url;
7160				val.index = key;
7161				val.URL = URL;
7162				val.caption = caption;
7163				val.title = title;
7164				val.type = type;
7165				val.options = options;
7166				val.ext = ext;
7167
7168				newItems.push(val);
7169			});
7170
7171			iL.items = newItems;
7172		},
7173
7174		instantCall: function() {
7175			var iL = this,
7176				key = iL.vars.start;
7177
7178			iL.vars.current = key;
7179			iL.vars.next = (iL.items[key + 1]) ? key + 1 : null;
7180			iL.vars.prev = (iL.items[key - 1]) ? key - 1 : null;
7181
7182			iL.addContents();
7183			iL.patchEvents();
7184		},
7185
7186		addContents: function() {
7187			var iL = this,
7188				vars = iL.vars,
7189				opts = iL.options,
7190				viewport = getViewport(),
7191				path = opts.path.toLowerCase(),
7192				recognizingItems = vars.total > 0 && iL.items.filter(function(e, i, arr) {
7193					return ['image', 'flash', 'video'].indexOf(e.type) === -1 && typeof e.recognized === 'undefined' && (opts.smartRecognition || e.options.smartRecognition);
7194				}),
7195				needRecognition = recognizingItems.length > 0;
7196
7197			if (opts.mobileOptimizer && !opts.innerToolbar)
7198				vars.isMobile = viewport.width <= vars.mobileMaxWidth;
7199
7200			vars.overlay.addClass(opts.skin).hide().css('opacity', opts.overlay.opacity);
7201
7202			if (opts.linkId)
7203				vars.overlay[0].setAttribute('linkid', opts.linkId);
7204
7205			//Add Toolbar Buttons
7206			if (opts.controls.toolbar) {
7207				vars.toolbar.addClass(opts.skin).append(vars.closeButton);
7208				if (opts.controls.fullscreen)
7209					vars.toolbar.append(vars.fullScreenButton);
7210				if (opts.controls.slideshow)
7211					vars.toolbar.append(vars.innerPlayButton);
7212				if (vars.total > 1)
7213					vars.toolbar.append(vars.innerPrevButton).append(vars.innerNextButton);
7214			}
7215
7216			//Append elements to body
7217			vars.BODY.addClass('ilightbox-noscroll').append(vars.overlay).append(vars.loader).append(vars.holder).append(vars.nextPhoto).append(vars.prevPhoto);
7218
7219			if (!opts.innerToolbar)
7220				vars.BODY.append(vars.toolbar);
7221			if (opts.controls.arrows)
7222				vars.BODY.append(vars.nextButton).append(vars.prevButton);
7223
7224			if (opts.controls.thumbnail && vars.total > 1) {
7225				vars.BODY.append(vars.thumbnails);
7226				vars.thumbnails.addClass(opts.skin).addClass('ilightbox-' + path);
7227				$('div.ilightbox-thumbnails-grid', vars.thumbnails).empty();
7228				vars.thumbs = true;
7229			}
7230
7231			//Configure loader and arrows
7232			var loaderCss = (opts.path.toLowerCase() == "horizontal") ? {
7233				left: parseInt((viewport.width / 2) - (vars.loader.outerWidth() / 2))
7234			} : {
7235				top: parseInt((viewport.height / 2) - (vars.loader.outerHeight() / 2))
7236			};
7237			vars.loader.addClass(opts.skin).css(loaderCss);
7238			vars.nextButton.add(vars.prevButton).addClass(opts.skin);
7239			if (path == "horizontal")
7240				vars.loader.add(vars.nextButton).add(vars.prevButton).addClass('horizontal');
7241
7242			// Configure arrow buttons
7243			vars.BODY[vars.isMobile ? 'addClass' : 'removeClass']('isMobile');
7244
7245			if (!opts.infinite) {
7246				vars.prevButton.add(vars.prevButton).add(vars.innerPrevButton).add(vars.innerNextButton).removeClass('disabled');
7247
7248				if (vars.current == 0)
7249					vars.prevButton.add(vars.innerPrevButton).addClass('disabled');
7250				if (vars.current >= vars.total - 1)
7251					vars.nextButton.add(vars.innerNextButton).addClass('disabled');
7252			}
7253
7254			if (opts.show.effect) {
7255				vars.overlay.stop().fadeIn(opts.show.speed);
7256				vars.toolbar.stop().fadeIn(opts.show.speed);
7257			} else {
7258				vars.overlay.show();
7259				vars.toolbar.show();
7260			}
7261
7262			var length = recognizingItems.length;
7263			if (needRecognition) {
7264				iL.showLoader();
7265
7266				$.each(recognizingItems, function(key, val) {
7267					var resultFnc = function(result) {
7268							var key = -1,
7269								filter = iL.items.filter(function(e, i, arr) {
7270									if (e.URL == result.url)
7271										key = i;
7272
7273									return e.URL == result.url;
7274								}),
7275								self = iL.items[key];
7276
7277							if (result)
7278								$.extend(true, self, {
7279									URL: result.source,
7280									type: result.type,
7281									recognized: true,
7282									options: {
7283										html5video: result.html5video,
7284										width: (result.type == "image") ? 0 : (result.width || self.width),
7285										height: (result.type == "image") ? 0 : (result.height || self.height),
7286										thumbnail: self.options.thumbnail || result.thumbnail
7287									}
7288								});
7289
7290							length--;
7291
7292							if (length == 0) {
7293								iL.hideLoader();
7294
7295								vars.dontGenerateThumbs = false;
7296								iL.generateThumbnails();
7297
7298								if (opts.show.effect)
7299									setTimeout(function() {
7300										iL.generateBoxes();
7301									}, opts.show.speed);
7302								else
7303									iL.generateBoxes();
7304							}
7305						};
7306
7307					iL.ogpRecognition(this, resultFnc);
7308				});
7309			}
7310			else {
7311				if (opts.show.effect)
7312					setTimeout(function() {
7313						iL.generateBoxes();
7314					}, opts.show.speed);
7315				else
7316					iL.generateBoxes();
7317			}
7318
7319			iL.createUI();
7320
7321			window.iLightBox = {
7322				close: function() {
7323					iL.closeAction();
7324				},
7325				fullscreen: function() {
7326					iL.fullScreenAction();
7327				},
7328				moveNext: function() {
7329					iL.moveTo('next');
7330				},
7331				movePrev: function() {
7332					iL.moveTo('prev');
7333				},
7334				goTo: function(index) {
7335					iL.goTo(index);
7336				},
7337				refresh: function() {
7338					iL.refresh();
7339				},
7340				reposition: function() {
7341					(arguments.length > 0) ? iL.repositionPhoto(true): iL.repositionPhoto();
7342				},
7343				setOption: function(options) {
7344					iL.setOption(options);
7345				},
7346				destroy: function() {
7347					iL.closeAction();
7348					iL.dispatchItemsEvents();
7349				}
7350			};
7351
7352			if (opts.linkId) {
7353				vars.hashLock = true;
7354				window.location.hash = opts.linkId + '/' + vars.current;
7355				setTimeout(function() {
7356					vars.hashLock = false;
7357				}, 55);
7358			}
7359
7360			if (!opts.slideshow.startPaused) {
7361				iL.resume();
7362				vars.innerPlayButton.removeClass('ilightbox-play').addClass('ilightbox-pause');
7363			}
7364
7365			//Trigger the onOpen callback
7366			if (typeof iL.options.callback.onOpen == 'function') iL.options.callback.onOpen.call(iL);
7367		},
7368
7369		loadContent: function(obj, opt, speed) {
7370			var iL = this,
7371				holder, item;
7372
7373			iL.createUI();
7374
7375			obj.speed = speed || iL.options.effects.loadedFadeSpeed;
7376
7377			if (opt == 'current') {
7378				if (!obj.options.mousewheel) iL.vars.lockWheel = true;
7379				else iL.vars.lockWheel = false;
7380
7381				if (!obj.options.swipe) iL.vars.lockSwipe = true;
7382				else iL.vars.lockSwipe = false;
7383			}
7384
7385			switch (opt) {
7386				case 'current':
7387					holder = iL.vars.holder, item = iL.vars.current;
7388					break;
7389				case 'next':
7390					holder = iL.vars.nextPhoto, item = iL.vars.next;
7391					break;
7392				case 'prev':
7393					holder = iL.vars.prevPhoto, item = iL.vars.prev;
7394					break;
7395			}
7396
7397			holder.removeAttr('style class').addClass('ilightbox-holder' + (supportTouch ? ' supportTouch' : '')).addClass(obj.options.skin);
7398			$('div.ilightbox-inner-toolbar', holder).remove();
7399
7400			if (obj.title || iL.options.innerToolbar) {
7401				var innerToolbar = iL.vars.innerToolbar.clone();
7402				if (obj.title && iL.options.show.title) {
7403					var title = iL.vars.title.clone();
7404					title.empty().html(obj.title);
7405					innerToolbar.append(title);
7406				}
7407				if (iL.options.innerToolbar) {
7408					innerToolbar.append((iL.vars.total > 1) ? iL.vars.toolbar.clone() : iL.vars.toolbar);
7409				}
7410				holder.prepend(innerToolbar);
7411			}
7412
7413			//console.warn('loadContent', arguments);
7414
7415			iL.loadSwitcher(obj, holder, item, opt);
7416		},
7417
7418		loadSwitcher: function(obj, holder, item, opt) {
7419			var iL = this,
7420				opts = iL.options,
7421				api = {
7422					element: holder,
7423					position: item
7424				};
7425
7426			switch (obj.type) {
7427				case 'image':
7428					//Trigger the onBeforeLoad callback
7429					if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
7430					if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
7431
7432					iL.loadImage(obj.URL, function(img) {
7433						//Trigger the onAfterLoad callback
7434						if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
7435						if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
7436
7437						var width = (img) ? img.width : 400,
7438							height = (img) ? img.height : 200;
7439
7440						holder.data({
7441							naturalWidth: width,
7442							naturalHeight: height
7443						});
7444						$('div.ilightbox-container', holder).empty().append((img) ? '<img src="' + obj.URL + '" class="ilightbox-image" />' : '<span class="ilightbox-alert">' + opts.errors.loadImage + '</span>');
7445
7446						//Trigger the onRender callback
7447						if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
7448						if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
7449
7450						iL.configureHolder(obj, opt, holder);
7451					});
7452
7453					break;
7454
7455				case 'video':
7456					holder.data({
7457						naturalWidth: obj.options.width,
7458						naturalHeight: obj.options.height
7459					});
7460
7461					if (opt === 'current') {
7462						iL.addContent(holder, obj);
7463
7464						//Trigger the onRender callback
7465						if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
7466						if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
7467					} else {
7468						$('div.ilightbox-container', holder).empty();
7469					}
7470
7471					iL.configureHolder(obj, opt, holder);
7472
7473					break;
7474
7475				case 'iframe':
7476					//iL.showLoader();
7477					holder.data({
7478						naturalWidth: obj.options.width,
7479						naturalHeight: obj.options.height
7480					});
7481
7482					iL.configureHolder(obj, opt, holder);
7483
7484					if (opt === 'current') {
7485						var el = iL.addContent(holder, obj);
7486
7487						//Trigger the onRender callback
7488						if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
7489						if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
7490
7491						//Trigger the onBeforeLoad callback
7492						if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
7493						if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
7494
7495						el.bind('load', function() {
7496							//Trigger the onAfterLoad callback
7497							if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
7498							if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
7499
7500							//iL.hideLoader();
7501							el.unbind('load');
7502						});
7503					} else {
7504						$('div.ilightbox-container', holder).empty();
7505					}
7506
7507					break;
7508
7509				case 'inline':
7510					var el = $(obj.URL),
7511						content = iL.addContent(holder, obj),
7512						images = findImageInElement(holder);
7513
7514					holder.data({
7515						naturalWidth: (iL.items[item].options.width || el.outerWidth()),
7516						naturalHeight: (iL.items[item].options.height || el.outerHeight())
7517					});
7518					content.children().eq(0).show();
7519
7520					//Trigger the onRender callback
7521					if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
7522					if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
7523
7524					//Trigger the onBeforeLoad callback
7525					if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
7526					if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
7527
7528					iL.loadImage(images, function() {
7529						//Trigger the onAfterLoad callback
7530						if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
7531						if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
7532
7533						iL.configureHolder(obj, opt, holder);
7534					});
7535
7536					break;
7537
7538				case 'flash':
7539					var el = iL.addContent(holder, obj);
7540
7541					holder.data({
7542						naturalWidth: (iL.items[item].options.width || el.outerWidth()),
7543						naturalHeight: (iL.items[item].options.height || el.outerHeight())
7544					});
7545
7546					//Trigger the onRender callback
7547					if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
7548					if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
7549
7550					iL.configureHolder(obj, opt, holder);
7551
7552					break;
7553
7554				case 'ajax':
7555					var ajax = obj.options.ajax || {};
7556					//Trigger the onBeforeLoad callback
7557					if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
7558					if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
7559
7560					iL.showLoader();
7561					$.ajax({
7562						url: obj.URL || opts.ajaxSetup.url,
7563						data: ajax.data || null,
7564						dataType: ajax.dataType || "html",
7565						type: ajax.type || opts.ajaxSetup.type,
7566						cache: ajax.cache || opts.ajaxSetup.cache,
7567						crossDomain: ajax.crossDomain || opts.ajaxSetup.crossDomain,
7568						global: ajax.global || opts.ajaxSetup.global,
7569						ifModified: ajax.ifModified || opts.ajaxSetup.ifModified,
7570						username: ajax.username || opts.ajaxSetup.username,
7571						password: ajax.password || opts.ajaxSetup.password,
7572						beforeSend: ajax.beforeSend || opts.ajaxSetup.beforeSend,
7573						complete: ajax.complete || opts.ajaxSetup.complete,
7574						success: function(data, textStatus, jqXHR) {
7575							iL.hideLoader();
7576
7577							var el = $(data),
7578								container = $('div.ilightbox-container', holder),
7579								elWidth = iL.items[item].options.width || parseInt(el[0].getAttribute('width')),
7580								elHeight = iL.items[item].options.height || parseInt(el[0].getAttribute('height')),
7581								css = (el[0].getAttribute('width') && el[0].getAttribute('height')) ? {
7582									'overflow': 'hidden'
7583								} : {};
7584
7585							container.empty().append($('<div class="ilightbox-wrapper"></div>').css(css).html(el));
7586							holder.show().data({
7587								naturalWidth: (elWidth || container.outerWidth()),
7588								naturalHeight: (elHeight || container.outerHeight())
7589							}).hide();
7590
7591							//Trigger the onRender callback
7592							if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
7593							if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
7594
7595							var images = findImageInElement(holder);
7596							iL.loadImage(images, function() {
7597								//Trigger the onAfterLoad callback
7598								if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
7599								if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
7600
7601								iL.configureHolder(obj, opt, holder);
7602							});
7603
7604							opts.ajaxSetup.success(data, textStatus, jqXHR);
7605							if (typeof ajax.success == 'function') ajax.success(data, textStatus, jqXHR);
7606						},
7607						error: function(jqXHR, textStatus, errorThrown) {
7608							//Trigger the onAfterLoad callback
7609							if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
7610							if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
7611
7612							iL.hideLoader();
7613							$('div.ilightbox-container', holder).empty().append('<span class="ilightbox-alert">' + opts.errors.loadContents + '</span>');
7614							iL.configureHolder(obj, opt, holder);
7615
7616							opts.ajaxSetup.error(jqXHR, textStatus, errorThrown);
7617							if (typeof ajax.error == 'function') ajax.error(jqXHR, textStatus, errorThrown);
7618						}
7619					});
7620
7621					break;
7622
7623				case 'html':
7624					var object = obj.URL,
7625						el
7626					container = $('div.ilightbox-container', holder);
7627
7628					if (object[0].nodeName) el = object.clone();
7629					else {
7630						var dom = $(object);
7631						if (dom.selector) el = $('<div>' + dom + '</div>');
7632						else el = dom;
7633					}
7634
7635					var elWidth = iL.items[item].options.width || parseInt(el.attr('width')),
7636						elHeight = iL.items[item].options.height || parseInt(el.attr('height'));
7637
7638					iL.addContent(holder, obj);
7639
7640					el.appendTo(document.documentElement).hide();
7641
7642					//Trigger the onRender callback
7643					if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
7644					if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
7645
7646					var images = findImageInElement(holder);
7647
7648					//Trigger the onBeforeLoad callback
7649					if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
7650					if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
7651
7652					iL.loadImage(images, function() {
7653						//Trigger the onAfterLoad callback
7654						if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
7655						if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
7656
7657						holder.show().data({
7658							naturalWidth: (elWidth || container.outerWidth()),
7659							naturalHeight: (elHeight || container.outerHeight())
7660						}).hide();
7661						el.remove();
7662
7663						iL.configureHolder(obj, opt, holder);
7664					});
7665
7666					break;
7667			}
7668		},
7669
7670		configureHolder: function(obj, opt, holder) {
7671			var iL = this,
7672				vars = iL.vars,
7673				opts = iL.options;
7674
7675			if (opt != "current")(opt == "next") ? holder.addClass('ilightbox-next') : holder.addClass('ilightbox-prev');
7676
7677			if (opt == "current")
7678				var item = vars.current;
7679			else if (opt == "next")
7680				var opacity = opts.styles.nextOpacity,
7681					item = vars.next;
7682			else
7683				var opacity = opts.styles.prevOpacity,
7684					item = vars.prev;
7685
7686			var api = {
7687				element: holder,
7688				position: item
7689			};
7690
7691			iL.items[item].options.width = iL.items[item].options.width || 0,
7692			iL.items[item].options.height = iL.items[item].options.height || 0;
7693
7694			if (opt == "current") {
7695				if (opts.show.effect) holder.css(transform, gpuAcceleration).fadeIn(obj.speed, function() {
7696					holder.css(transform, '');
7697					if (obj.caption) {
7698						iL.setCaption(obj, holder);
7699						var caption = $('div.ilightbox-caption', holder),
7700							percent = parseInt((caption.outerHeight() / holder.outerHeight()) * 100);
7701						if (opts.caption.start & percent <= 50) caption.fadeIn(opts.effects.fade
7701Speed);
7702					}
7703
7704					var social = obj.options.social;
7705					if (social) {
7706						iL.setSocial(social, obj.URL, holder);
7707						if (opts.social.start) $('div.ilightbox-social', holder).fadeIn(opts.effects.fadeSpeed);
7708					}
7709
7710					//Generate thumbnails
7711					iL.generateThumbnails();
7712
7713					//Trigger the onShow callback
7714					if (typeof opts.callback.onShow == 'function') opts.callback.onShow.call(iL, iL.ui, item);
7715					if (typeof obj.options.onShow == 'function') obj.options.onShow.call(iL, api);
7716				});
7717				else {
7718					holder.show();
7719
7720					//Generate thumbnails
7721					iL.generateThumbnails();
7722
7723					//Trigger the onShow callback
7724					if (typeof opts.callback.onShow == 'function') opts.callback.onShow.call(iL, iL.ui, item);
7725					if (typeof obj.options.onShow == 'function') obj.options.onShow.call(iL, api);
7726				}
7727			} else {
7728				if (opts.show.effect) holder.fadeTo(obj.speed, opacity, function() {
7729					if (opt == "next") vars.nextLock = false;
7730					else vars.prevLock = false;
7731
7732					//Generate thumbnails
7733					iL.generateThumbnails();
7734
7735					//Trigger the onShow callback
7736					if (typeof opts.callback.onShow == 'function') opts.callback.onShow.call(iL, iL.ui, item);
7737					if (typeof obj.options.onShow == 'function') obj.options.onShow.call(iL, api);
7738				});
7739				else {
7740					holder.css({
7741						opacity: opacity
7742					}).show();
7743					if (opt == "next") vars.nextLock = false;
7744					else vars.prevLock = false;
7745
7746					//Generate thumbnails
7747					iL.generateThumbnails();
7748
7749					//Trigger the onShow callback
7750					if (typeof opts.callback.onShow == 'function') opts.callback.onShow.call(iL, iL.ui, item);
7751					if (typeof obj.options.onShow == 'function') obj.options.onShow.call(iL, api);
7752				}
7753			}
7754
7755			setTimeout(function() {
7756				iL.repositionPhoto();
7757			}, 0);
7758		},
7759
7760		generateBoxes: function() {
7761			var iL = this,
7762				vars = iL.vars,
7763				opts = iL.options;
7764
7765			if (opts.infinite && vars.total >= 3) {
7766				if (vars.current == vars.total - 1) vars.next = 0;
7767				if (vars.current == 0) vars.prev = vars.total - 1;
7768			} else opts.infinite = false;
7769
7770			//UNCODE addition		
7771			if ( typeof iL.items[vars.current] == 'undefined' )
7772				vars.current = 0;
7773
7774			iL.loadContent(iL.items[vars.current], 'current', opts.show.speed);
7775
7776			if (iL.items[vars.next]) iL.loadContent(iL.items[vars.next], 'next', opts.show.speed);
7777			if (iL.items[vars.prev]) iL.loadContent(iL.items[vars.prev], 'prev', opts.show.speed);
7778		},
7779
7780		generateThumbnails: function() {
7781			var iL = this,
7782				vars = iL.vars,
7783				opts = iL.options,
7784				timeOut = null;
7785
7786			if (vars.thumbs && !iL.vars.dontGenerateThumbs) {
7787				var thumbnails = vars.thumbnails,
7788					container = $('div.ilightbox-thumbnails-container', thumbnails),
7789					grid = $('div.ilightbox-thumbnails-grid', container),
7790					i = 0;
7791
7792				grid.removeAttr('style').empty();
7793
7794				$.each(iL.items, function(key, val) {
7795					var isActive = (vars.current == key) ? 'ilightbox-active' : '',
7796						opacity = (vars.current == key) ? opts.thumbnails.activeOpacity : opts.thumbnails.normalOpacity,
7797						thumb = val.options.thumbnail,
7798						thumbnail = $('<div class="ilightbox-thumbnail"></div>'),
7799						icon = $('<div class="ilightbox-thumbnail-icon"></div>');
7800
7801					thumbnail.css({
7802						opacity: 0
7803					}).addClass(isActive);
7804
7805					if ((val.type == "video" || val.type == "flash") && typeof val.options.icon == 'undefined') {
7806						icon.addClass('ilightbox-thumbnail-video');
7807						thumbnail.append(icon);
7808					} else if (val.options.icon) {
7809						icon.addClass('ilightbox-thumbnail-' + val.options.icon);
7810						thumbnail.append(icon);
7811					}
7812
7813					if (thumb) iL.loadImage(thumb, function(img) {
7814						i++;
7815						if (img) thumbnail.data({
7816							naturalWidth: img.width,
7817							naturalHeight: img.height
7818						}).append('<img src="' + thumb + '" border="0" />');
7819						else thumbnail.data({
7820							naturalWidth: opts.thumbnails.maxWidth,
7821							naturalHeight: opts.thumbnails.maxHeight
7822						});
7823
7824						clearTimeout(timeOut);
7825						timeOut = setTimeout(function() {
7826							iL.positionThumbnails(thumbnails, container, grid);
7827						}, 20);
7828						setTimeout(function() {
7829							thumbnail.fadeTo(opts.effects.loadedFadeSpeed, opacity);
7830						}, i * 20);
7831					});
7832
7833					grid.append(thumbnail);
7834				});
7835				iL.vars.dontGenerateThumbs = true;
7836			}
7837		},
7838
7839		positionThumbnails: function(thumbnails, container, grid) {
7840			var iL = this,
7841				vars = iL.vars,
7842				opts = iL.options,
7843				viewport = getViewport(),
7844				path = opts.path.toLowerCase();
7845
7846			if (!thumbnails) thumbnails = vars.thumbnails;
7847			if (!container) container = $('div.ilightbox-thumbnails-container', thumbnails);
7848			if (!grid) grid = $('div.ilightbox-thumbnails-grid', container);
7849
7850			var thumbs = $('.ilightbox-thumbnail', grid),
7851				widthAvail = (path == 'horizontal') ? viewport.width - opts.styles.pageOffsetX : thumbs.eq(0).outerWidth() - opts.styles.pageOffsetX,
7852				heightAvail = (path == 'horizontal') ? thumbs.eq(0).outerHeight() - opts.styles.pageOffsetY : viewport.height - opts.styles.pageOffsetY,
7853				gridWidth = (path == 'horizontal') ? 0 : widthAvail,
7854				gridHeight = (path == 'horizontal') ? heightAvail : 0,
7855				active = $('.ilightbox-active', grid),
7856				gridCss = {},
7857				css = {};
7858
7859			if (arguments.length < 3) {
7860				thumbs.css({
7861					opacity: opts.thumbnails.normalOpacity
7862				});
7863				active.css({
7864					opacity: opts.thumbnails.activeOpacity
7865				});
7866			}
7867
7868			thumbs.each(function(i) {
7869				var t = $(this),
7870					data = t.data(),
7871					width = (path == 'horizontal') ? 0 : opts.thumbnails.maxWidth;
7872				height = (path == 'horizontal') ? opts.thumbnails.maxHeight : 0;
7873				dims = iL.getNewDimenstions(width, height, data.naturalWidth, data.naturalHeight, true);
7874
7875				t.css({
7876					width: dims.width,
7877					height: dims.height
7878				});
7879				if (path == 'horizontal') t.css({
7880					'float': 'left'
7881				});
7882
7883				(path == 'horizontal') ? (
7884					gridWidth += t.outerWidth()
7885				) : (
7886					gridHeight += t.outerHeight()
7887				);
7888			});
7889
7890			gridCss = {
7891				width: gridWidth,
7892				height: gridHeight
7893			};
7894
7895			grid.css(gridCss);
7896
7897			gridCss = {};
7898
7899			var gridOffset = grid.offset(),
7900				activeOffset = (active.length) ? active.offset() : {
7901					top: parseInt(heightAvail / 2),
7902					left: parseInt(widthAvail / 2)
7903				};
7904
7905			gridOffset.top = (gridOffset.top - $doc.scrollTop()),
7906				gridOffset.left = (gridOffset.left - $doc.scrollLeft()),
7907				activeOffset.top = (activeOffset.top - gridOffset.top - $doc.scrollTop()),
7908				activeOffset.left = (activeOffset.left - gridOffset.left - $doc.scrollLeft());
7909
7910			(path == 'horizontal') ? (
7911				gridCss.top = 0,
7912				gridCss.left = parseInt((widthAvail / 2) - activeOffset.left - (active.outerWidth() / 2))
7913			) : (
7914				gridCss.top = parseInt(((heightAvail / 2) - activeOffset.top - (active.outerHeight() / 2))),
7915				gridCss.left = 0
7916			);
7917
7918			if (arguments.length < 3) grid.stop().animate(gridCss, opts.effects.repositionSpeed, 'easeOutCirc');
7919			else grid.css(gridCss);
7920		},
7921
7922		loadImage: function(image, callback) {
7923			if (!$.isArray(image)) image = [image];
7924
7925			var iL = this,
7926				length = image.length;
7927
7928			if (length > 0) {
7929				iL.showLoader();
7930				$.each(image, function(index, value) {
7931					var img = new Image();
7932					img.onload = function() {
7933						length -= 1;
7934						if (length == 0) {
7935							iL.hideLoader();
7936							callback(img);
7937						}
7938					};
7939					img.onerror = img.onabort = function() {
7940						length -= 1;
7941						if (length == 0) {
7942							iL.hideLoader();
7943							callback(false);
7944						}
7945					};
7946					img.src = image[index];
7947				});
7948			} else callback(false);
7949		},
7950
7951		patchItemsEvents: function() {
7952			var iL = this,
7953				vars = iL.vars,
7954				clickEvent = supportTouch ? "itap.iL" : "click.iL",
7955				vEvent = supportTouch ? "click.iL" : "itap.iL";
7956
7957			if (iL.context && iL.selector) {
7958				var $items = $(iL.selector, iL.context);
7959
7960				$(iL.context).on(clickEvent, iL.selector, function() {
7961					var $this = $(this),
7962						//UNCODE addition
7963						key = typeof $(this).data('lb-index') !== '' && $(this).closest('.owl-item').length ? $(this).data('lb-index') : $items.index($this);
7964					if (UNCODE.isMobile) {
7965						var isCarousel = $this.closest('.owl-carousel');
7966						if (isCarousel.length) {
7967							if (isCarousel.hasClass('owl-translating')) return false;
7968						}
7969					}
7970
7971					vars.current = key;
7972					vars.next = iL.items[key + 1] ? key + 1 : null;
7973					vars.prev = iL.items[key - 1] ? key - 1 : null;
7974
7975					//UNCODE addition
7976					if ( vars.BODY.hasClass('fp-slide-scrolling') )
7977						return false;
7978
7979					iL.addContents();
7980					iL.patchEvents();
7981
7982					return false;
7983				}).on(vEvent, iL.selector, function() {
7984					return false;
7985				});
7986			} else
7987				$.each(iL.itemsObject, function(key, val) {
7988					val.on(clickEvent, function() {
7989						vars.current = key;
7990						vars.next = iL.items[key + 1] ? key + 1 : null;
7991						vars.prev = iL.items[key - 1] ? key - 1 : null;
7992
7993						iL.addContents();
7994						iL.patchEvents();
7995
7996						return false;
7997					}).on(vEvent, function() {
7998						return false;
7999					});
8000				});
8001		},
8002
8003		dispatchItemsEvents: function() {
8004			var iL = this,
8005				vars = iL.vars,
8006				opts = iL.options;
8007
8008			if (iL.context && iL.selector)
8009				$(iL.context).off('.iL', iL.selector);
8010			else
8011				$.each(iL.itemsObject, function(key, val) {
8012					val.off('.iL');
8013				});
8014		},
8015
8016		refresh: function() {
8017			var iL = this;
8018
8019			iL.dispatchItemsEvents();
8020			iL.attachItems();
8021			iL.normalizeItems();
8022			iL.patchItemsEvents();
8023		},
8024
8025		patchEvents: function() {
8026			var iL = this,
8027				vars = iL.vars,
8028				opts = iL.options,
8029				path = opts.path.toLowerCase(),
8030				holders = $('.ilightbox-holder'),
8031				fullscreenEvent = fullScreenApi.fullScreenEventName + '.iLightBox',
8032				durationThreshold = 1000,
8033				horizontalDistanceThreshold =
8034				verticalDistanceThreshold = 100,
8035				buttonsArray = [vars.nextButton[0], vars.prevButton[0], vars.nextButton[0].firstChild, vars.prevButton[0].firstChild];
8036
8037			$win.bind('resize.iLightBox', function() {
8038				var viewport = getViewport();
8039
8040				if (opts.mobileOptimizer && !opts.innerToolbar) vars.isMobile = viewport.width <= vars.mobileMaxWidth;
8041				vars.BODY[vars.isMobile ? 'addClass' : 'removeClass']('isMobile');
8042
8043				iL.repositionPhoto(null);
8044				if (supportTouch) {
8045					clearTimeout(vars.setTime);
8046					vars.setTime = setTimeout(function() {
8047						var scrollTop = getScrollXY().y;
8048						window.scrollTo(0, scrollTop - 30);
8049						window.scrollTo(0, scrollTop + 30);
8050						window.scrollTo(0, scrollTop);
8051					}, 2000);
8052				}
8053				if (vars.thumbs) iL.positionThumbnails();
8054			}).bind('keydown.iLightBox', function(event) {
8055				if (opts.controls.keyboard) {
8056					switch (event.keyCode) {
8057						case 13:
8058							if (event.shiftKey && opts.keyboard.shift_enter) iL.fullScreenAction();
8059							break;
8060						case 27:
8061							if (opts.keyboard.esc) iL.closeAction();
8062							break;
8063						case 37:
8064							if (opts.keyboard.left && !vars.lockKey) iL.moveTo('prev');
8065							break;
8066						case 38:
8067							if (opts.keyboard.up && !vars.lockKey) iL.moveTo('prev');
8068							break;
8069						case 39:
8070							if (opts.keyboard.right && !vars.lockKey) iL.moveTo('next');
8071							break;
8072						case 40:
8073							if (opts.keyboard.down && !vars.lockKey) iL.moveTo('next');
8074							break;
8075					}
8076				}
8077			});
8078
8079			if (fullScreenApi.supportsFullScreen) $win.bind(fullscreenEvent, function() {
8080				iL.doFullscreen();
8081			});
8082
8083			var holderEventsArr = [opts.caption.show + '.iLightBox', opts.caption.hide + '.iLightBox', opts.social.show + '.iLightBox', opts.social.hide + '.iLightBox'].filter(function(e, i, arr) {
8084					return arr.lastIndexOf(e) === i;
8085				}),
8086				holderEvents = "";
8087
8088			$.each(holderEventsArr, function(key, val) {
8089				if (key != 0) holderEvents += ' ';
8090				holderEvents += val;
8091			});
8092
8093			$doc.on(clickEvent, '.ilightbox-overlay', function() {
8094				if (opts.overlay.blur) iL.closeAction();
8095			}).on(clickEvent, '.ilightbox-next, .ilightbox-next-button', function() {
8096				iL.moveTo('next');
8097			}).on(clickEvent, '.ilightbox-prev, .ilightbox-prev-button', function() {
8098				iL.moveTo('prev');
8099			}).on(clickEvent, '.ilightbox-thumbnail', function() {
8100				var t = $(this),
8101					thumbs = $('.ilightbox-thumbnail', vars.thumbnails),
8102					index = thumbs.index(t);
8103
8104				if (index != vars.current) iL.goTo(index);
8105			}).on(holderEvents, '.ilightbox-holder:not(.ilightbox-next, .ilightbox-prev)', function(e) {
8106				var caption = $('div.ilightbox-caption', vars.holder),
8107					social = $('div.ilightbox-social', vars.holder),
8108					fadeSpeed = opts.effects.fadeSpeed;
8109
8110				if (vars.nextLock || vars.prevLock) {
8111					if (e.type == opts.caption.show && !caption.is(':visible')) caption.fadeIn(fadeSpeed);
8112					else if (e.type == opts.caption.hide && caption.is(':visible')) caption.fadeOut(fadeSpeed);
8113
8114					if (e.type == opts.social.show && !social.is(':visible')) social.fadeIn(fadeSpeed);
8115					else if (e.type == opts.social.hide && social.is(':visible')) social.fadeOut(fadeSpeed);
8116				} else {
8117					if (e.type == opts.caption.show && !caption.is(':visible')) caption.stop().fadeIn(fadeSpeed);
8118					else if (e.type == opts.caption.hide && caption.is(':visible')) caption.stop().fadeOut(fadeSpeed);
8119
8120					if (e.type == opts.social.show && !social.is(':visible')) social.stop().fadeIn(fadeSpeed);
8121					else if (e.type == opts.social.hide && social.is(':visible')) social.stop().fadeOut(fadeSpeed);
8122				}
8123			}).on('mouseenter.iLightBox mouseleave.iLightBox', '.ilightbox-wrapper', function(e) {
8124				if (e.type == 'mouseenter') vars.lockWheel = true;
8125				else vars.lockWheel = false;
8126			}).on(clickEvent, '.ilightbox-toolbar a.ilightbox-close, .ilightbox-toolbar a.ilightbox-fullscreen, .ilightbox-toolbar a.ilightbox-play, .ilightbox-toolbar a.ilightbox-pause', function() {
8127				var t = $(this);
8128
8129				if (t.hasClass('ilightbox-fullscreen')) iL.fullScreenAction();
8130				else if (t.hasClass('ilightbox-play')) {
8131					iL.resume();
8132					t.addClass('ilightbox-pause').removeClass('ilightbox-play');
8133				} else if (t.hasClass('ilightbox-pause')) {
8134					iL.pause();
8135					t.addClass('ilightbox-play').removeClass('ilightbox-pause');
8136				} else iL.closeAction();
8137			}).on(touchMoveEvent, '.ilightbox-overlay, .ilightbox-thumbnails-container', function(e) {
8138				// prevent scrolling
8139				e.preventDefault();
8140			});
8141
8142			function mouseMoveHandler(e) {
8143				if (!vars.isMobile) {
8144					if (!vars.mouseID) {
8145						vars.hideableElements.show();
8146					}
8147
8148					vars.mouseID = clearTimeout(vars.mouseID);
8149
8150					if (buttonsArray.indexOf(e.target) === -1)
8151						vars.mouseID = setTimeout(function() {
8152							vars.hideableElements.hide();
8153							vars.mouseID = clearTimeout(vars.mouseID);
8154						}, 3000);
8155				}
8156			}
8157
8158			if (opts.controls.arrows && !supportTouch) $doc.on('mousemove.iLightBox', mouseMoveHandler);
8159
8160			if (opts.controls.slideshow && opts.slideshow.pauseOnHover) $doc.on('mouseenter.iLightBox mouseleave.iLightBox', '.ilightbox-holder:not(.ilightbox-next, .ilightbox-prev)', function(e) {
8161				if (e.type == 'mouseenter' && vars.cycleID) iL.pause();
8162				else if (e.type == 'mouseleave' && vars.isPaused) iL.resume();
8163			});
8164
8165			var switchers = $('.ilightbox-overlay, .ilightbox-holder, .ilightbox-thumbnails');
8166
8167			if (opts.controls.mousewheel) switchers.on('mousewheel.iLightBox', function(event, delta) {
8168				if (!vars.lockWheel) {
8169					event.preventDefault();
8170					if (delta < 0) iL.moveTo('next');
8171					else if (delta > 0) iL.moveTo('prev');
8172				}
8173			});
8174
8175			if (opts.controls.swipe) holders.on(touchStartEvent, function(event) {
8176				if (vars.nextLock || vars.prevLock || vars.total == 1 || vars.lockSwipe) return;
8177
8178				vars.BODY.addClass('ilightbox-closedhand');
8179
8180				var data = event.originalEvent.touches ? event.originalEvent.touches[0] : event,
8181					scrollTop = $doc.scrollTop(),
8182					scrollLeft = $doc.scrollLeft(),
8183					offsets = [
8184						holders.eq(0).offset(),
8185						holders.eq(1).offset(),
8186						holders.eq(2).offset()
8187					],
8188					offSet = [{
8189						top: offsets[0].top - scrollTop,
8190						left: offsets[0].left - scrollLeft
8191					}, {
8192						top: offsets[1].top - scrollTop,
8193						left: offsets[1].left - scrollLeft
8194					}, {
8195						top: offsets[2].top - scrollTop,
8196						left: offsets[2].left - scrollLeft
8197					}],
8198					start = {
8199						time: (new Date()).getTime(),
8200						coords: [data.pageX - scrollLeft, data.pageY - scrollTop]
8201					},
8202					stop;
8203
8204				function moveEachHandler(i) {
8205					var t = $(this),
8206						offset = offSet[i],
8207						scroll = [(start.coords[0] - stop.coords[0]), (start.coords[1] - stop.coords[1])];
8208
8209					t[0].style[path == "horizontal" ? 'left' : 'top'] = (path == "horizontal" ? offset.left - scroll[0] : offset.top - scroll[1]) + 'px';
8210				}
8211
8212				function moveHandler(event) {
8213
8214					if (!start) return;
8215
8216					var data = event.originalEvent.touches ? event.originalEvent.touches[0] : event;
8217
8218					stop = {
8219						time: (new Date()).getTime(),
8220						coords: [data.pageX - scrollLeft, data.pageY - scrollTop]
8221					};
8222
8223					holders.each(moveEachHandler);
8224
8225					// prevent scrolling
8226					event.preventDefault();
8227				}
8228
8229				function repositionHolders() {
8230					holders.each(function() {
8231						var t = $(this),
8232							offset = t.data('offset') || {
8233								top: t.offset().top - scrollTop,
8234								left: t.offset().left - scrollLeft
8235							},
8236							top = offset.top,
8237							left = offset.left;
8238
8239						t.css(transform, gpuAcceleration).stop().animate({
8240							top: top,
8241							left: left
8242						}, 500, 'easeOutCirc', function() {
8243							t.css(transform, '');
8244						});
8245					});
8246				}
8247
8248				holders.bind(touchMoveEvent, moveHandler);
8249				$doc.one(touchStopEvent, function(event) {
8250					holders.unbind(touchMoveEvent, moveHandler);
8251
8252					vars.BODY.removeClass('ilightbox-closedhand');
8253
8254					if (start && stop) {
8255						if (path == "horizontal" && stop.time - start.time < durationThreshold && abs(start.coords[0] - stop.coords[0]) > horizontalDistanceThreshold && abs(start.coords[1] - stop.coords[1]) < verticalDistanceThreshold) {
8256							if (start.coords[0] > stop.coords[0]) {
8257								if (vars.current == vars.total - 1 && !opts.infinite) repositionHolders();
8258								else {
8259									vars.isSwipe = true;
8260									iL.moveTo('next');
8261								}
8262							} else {
8263								if (vars.current == 0 && !opts.infinite) repositionHolders();
8264								else {
8265									vars.isSwipe = true;
8266									iL.moveTo('prev');
8267								}
8268							}
8269						} 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) {
8270							if (start.coords[1] > stop.coords[1]) {
8271								if (vars.current == vars.total - 1 && !opts.infinite) repositionHolders();
8272								else {
8273									vars.isSwipe = true;
8274									iL.moveTo('next');
8275								}
8276							} else {
8277								if (vars.current == 0 && !opts.infinite) repositionHolders();
8278								else {
8279									vars.isSwipe = true;
8280									iL.moveTo('prev');
8281								}
8282							}
8283						} else repositionHolders();
8284					}
8285					start = stop = undefined;
8286				});
8287			});
8288
8289		},
8290
8291		goTo: function(index) {
8292			var iL = this,
8293				vars = iL.vars,
8294				opts = iL.options,
8295				diff = (index - vars.current);
8296
8297			if (opts.infinite) {
8298				if (index == vars.total - 1 && vars.current == 0) diff = -1;
8299				if (vars.current == vars.total - 1 && index == 0) diff = 1;
8300			}
8301
8302			if (diff == 1) iL.moveTo('next');
8303			else if (diff == -1) iL.moveTo('prev');
8304			else {
8305
8306				if (vars.nextLock || vars.prevLock) return false;
8307
8308				//Trigger the onBeforeChange callback
8309				if (typeof opts.callback.onBeforeChange == 'function') opts.callback.onBeforeChange.call(iL, iL.ui);
8310
8311				if (opts.linkId) {
8312					vars.hashLock = true;
8313					window.location.hash = opts.linkId + '/' + index;
8314				}
8315
8316				if (iL.items[index]) {
8317					if (!iL.items[index].options.mousewheel) vars.lockWheel = true;
8318					else iL.vars.lockWheel = false;
8319
8320					if (!iL.items[index].options.swipe) vars.lockSwipe = true;
8321					else vars.lockSwipe = false;
8322				}
8323
8324				$.each([vars.holder, vars.nextPhoto, vars.prevPhoto], function(key, val) {
8325					val.css(transform, gpuAcceleration).fadeOut(opts.effects.loadedFadeSpeed);
8326				});
8327
8328				vars.current = index;
8329				vars.next = index + 1;
8330				vars.prev = index - 1;
8331
8332				iL.createUI();
8333
8334				setTimeout(function() {
8335					iL.generateBoxes();
8336				}, opts.effects.loadedFadeSpeed + 50);
8337
8338				$('.ilightbox-thumbnail', vars.thumbnails).removeClass('ilightbox-active').eq(index).addClass('ilightbox-active');
8339				iL.positionThumbnails();
8340
8341				if (opts.linkId) setTimeout(function() {
8342					vars.hashLock = false;
8343				}, 55);
8344
8345				// Configure arrow buttons
8346				if (!opts.infinite) {
8347					vars.nextButton.add(vars.prevButton).add(vars.innerPrevButton).add(vars.innerNextButton).removeClass('disabled');
8348
8349					if (vars.current == 0) {
8350						vars.prevButton.add(vars.innerPrevButton).addClass('disabled');
8351					}
8352					if (vars.current >= vars.total - 1) {
8353						vars.nextButton.add(vars.innerNextButton).addClass('disabled');
8354					}
8355				}
8356
8357				// Reset next cycle timeout
8358				iL.resetCycle();
8359
8360				//Trigger the onAfterChange callback
8361				if (typeof opts.callback.onAfterChange == 'function') opts.callback.onAfterChange.call(iL, iL.ui);
8362			}
8363		},
8364
8365		moveTo: function(side) {
8366			var iL = this,
8367				vars = iL.vars,
8368				opts = iL.options,
8369				path = opts.path.toLowerCase(),
8370				viewport = getViewport(),
8371				switchSpeed = opts.effects.switchSpeed;
8372
8373			if (vars.nextLock || vars.prevLock) return false;
8374			else {
8375
8376				var item = (side == "next") ? vars.next : vars.prev;
8377
8378				if (opts.linkId) {
8379					vars.hashLock = true;
8380					window.location.hash = opts.linkId + '/' + item;
8381				}
8382
8383				if (side == "next") {
8384					if (!iL.items[item]) return false;
8385					var firstHolder = vars.nextPhoto,
8386						secondHolder = vars.holder,
8387						lastHolder = vars.prevPhoto,
8388						firstClass = 'ilightbox-prev',
8389						secondClass = 'ilightbox-next';
8390				} else if (side == "prev") {
8391					if (!iL.items[item]) return false;
8392					var firstHolder = vars.prevPhoto,
8393						secondHolder = vars.holder,
8394						lastHolder = vars.nextPhoto,
8395						firstClass = 'ilightbox-next',
8396						secondClass = 'ilightbox-prev';
8397				}
8398
8399				//Trigger the onBeforeChange callback
8400				if (typeof opts.callback.onBeforeChange == 'function')
8401					opts.callback.onBeforeChange.call(iL, iL.ui);
8402
8403				(side == "next") ? vars.nextLock = true: vars.prevLock = true;
8404
8405				var captionFirst = $('div.ilightbox-caption', secondHolder),
8406					socialFirst = $('div.ilightbox-social', secondHolder);
8407
8408				if (captionFirst.length)
8409					captionFirst.stop().fadeOut(switchSpeed, function() {
8410						$(this).remove();
8411					});
8412				if (socialFirst.length)
8413					socialFirst.stop().fadeOut(switchSpeed, function() {
8414						$(this).remove();
8415					});
8416
8417				if (iL.items[item].caption) {
8418					iL.setCaption(iL.items[item], firstHolder);
8419					var caption = $('div.ilightbox-caption', firstHolder),
8420						percent = parseInt((caption.outerHeight() / firstHolder.outerHeight()) * 100);
8421					if (opts.caption.start && percent <= 50) caption.fadeIn(switchSpeed);
8422				}
8423
8424				var social = iL.items[item].options.social;
8425				if (social) {
8426					iL.setSocial(social, iL.items[item].URL, firstHolder);
8427					if (opts.social.start) $('div.ilightbox-social', firstHolder).fadeIn(opts.effects.fadeSpeed);
8428				}
8429
8430				$.each([firstHolder, secondHolder, lastHolder], function(key, val) {
8431					val.removeClass('ilightbox-next ilightbox-prev');
8432				});
8433
8434				var firstOffset = firstHolder.data('offset'),
8435					winW = (viewport.width - (opts.styles.pageOffsetX)),
8436					winH = (viewport.height - (opts.styles.pageOffsetY)),
8437					width = firstOffset.newDims.width,
8438					height = firstOffset.newDims.height,
8439					thumbsOffset = firstOffset.thumbsOffset,
8440					diff = firstOffset.diff,
8441					top = parseInt((winH / 2) - (height / 2) - diff.H - (thumbsOffset.H / 2)),
8442					left = parseInt((winW / 2) - (width / 2) - diff.W - (thumbsOffset.W / 2));
8443
8444				var secondOffset = secondHolder.data('offset'),
8445					object = secondOffset.object;
8446
8447				/*if (object.item.caption && !isNaN(width) && !isNaN(height) && UNCODE.wwidth > UNCODE.mediaQuery) {
8448					var objRatio = width / height;
8449					height = height - vars.captionHeight;
8450					width = height * objRatio;
8451					top = path == 'horizontal' ? parseInt((winH / 2) - (height / 2) - diff.H - (thumbsOffset.H / 2)) : top,
8452					left = parseInt((winW / 2) - (width / 2) - diff.W - (thumbsOffset.W / 2));
8453				}*/
8454
8455				firstHolder.show().css(transform, gpuAcceleration).animate({
8456					top: top,
8457					left: left,
8458					opacity: 1
8459				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc', function() {
8460					firstHolder.css(transform, '');
8461				});
8462
8463				$('div.ilightbox-container', firstHolder).animate({
8464					width: width,
8465					height: height
8466				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc');
8467
8468				diff = secondOffset.diff;
8469
8470				width = secondOffset.newDims.width,
8471					height = secondOffset.newDims.height;
8472
8473				width = parseInt(width * opts.styles[side == 'next' ? 'prevScale' : 'nextScale']),
8474					height = parseInt(height * opts.styles[side == 'next' ? 'prevScale' : 'nextScale']),
8475					top = (path == 'horizontal') ? parseInt((winH / 2) - object.offsetY - (height / 2) - diff.H - (thumbsOffset.H / 2)) : parseInt(winH - object.offsetX - diff.H - (thumbsOffset.H / 2));
8476
8477				if (side == 'prev')
8478					left = (path == 'horizontal') ? parseInt(winW - object.offsetX - diff.W - (thumbsOffset.W / 2)) : parseInt((winW / 2) - (width / 2) - diff.W - object.offsetY - (thumbsOffset.W / 2));
8479				else {
8480					top = (path == 'horizontal') ? top : parseInt(object.offsetX - diff.H - height - (thumbsOffset.H / 2)),
8481					left = (path == 'horizontal') ? parseInt(object.offsetX - diff.W - width - (thumbsOffset.W / 2)) : parseInt((winW / 2) - object.offsetY - (width / 2) - diff.W - (thumbsOffset.W / 2));
8482				}
8483
8484				/*if (object.item.caption && !isNaN(width) && !isNaN(height) && UNCODE.wwidth > UNCODE.mediaQuery) {
8485					var objRatio = width / height;
8486					height = height - vars.captionHeight;
8487					width = height * objRatio;
8488					top = path == 'horizontal' ? parseInt((winH / 2) - (height / 2) - diff.H - (thumbsOffset.H / 2)) : top;
8489				}*/
8490
8491				$('div.ilightbox-container', secondHolder).animate({
8492					width: width,
8493					height: height
8494				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc');
8495
8496				secondHolder.addClass(firstClass).css(transform, gpuAcceleration).animate({
8497					top: top,
8498					left: left,
8499					opacity: opts.styles.prevOpacity,
8500				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc', function() {
8501					secondHolder.css(transform, '');
8502
8503					$('.ilightbox-thumbnail', vars.thumbnails).removeClass('ilightbox-active').eq(item).addClass('ilightbox-active');
8504					iL.positionThumbnails();
8505
8506					if (iL.items[item]) {
8507						if (!iL.items[item].options.mousewheel) vars.lockWheel = true;
8508						else vars.lockWheel = false;
8509
8510						if (!iL.items[item].options.swipe) vars.lockSwipe = true;
8511						else vars.lockSwipe = false;
8512					}
8513
8514					vars.isSwipe = false;
8515
8516					// Remove iframe & video from previous slide
8517					if (['iframe', 'video'].indexOf(iL.items[vars.current].type) !== -1) {
8518						$('div.ilightbox-container', secondHolder).empty();
8519					}
8520
8521					if (side == "next") {
8522						vars.nextPhoto = lastHolder,
8523							vars.prevPhoto = secondHolder,
8524							vars.holder = firstHolder;
8525
8526						vars.nextPhoto.hide();
8527
8528						vars.next = vars.next + 1,
8529							vars.prev = vars.current,
8530							vars.current = vars.current + 1;
8531
8532						if (opts.infinite) {
8533							if (vars.current > vars.total - 1) vars.current = 0;
8534							if (vars.current == vars.total - 1) vars.next = 0;
8535							if (vars.current == 0) vars.prev = vars.total - 1;
8536						}
8537
8538						iL.createUI();
8539
8540						if (!iL.items[vars.next])
8541							vars.nextLock = false;
8542						else
8543							iL.loadContent(iL.items[vars.next], 'next');
8544					} else {
8545						vars.prevPhoto = lastHolder;
8546						vars.nextPhoto = secondHolder;
8547						vars.holder = firstHolder;
8548
8549						vars.prevPhoto.hide();
8550
8551						vars.next = vars.current;
8552						vars.current = vars.prev;
8553						vars.prev = vars.current - 1;
8554
8555						if (opts.infinite) {
8556							if (vars.current == vars.total - 1) vars.next = 0;
8557							if (vars.current == 0) vars.prev = vars.total - 1;
8558						}
8559
8560						iL.createUI();
8561
8562						if (!iL.items[vars.prev])
8563							vars.prevLock = false;
8564						else
8565							iL.loadContent(iL.items[vars.prev], 'prev');
8566					}
8567
8568					// Add iframe & video content for current slide
8569					if (['iframe', 'video'].indexOf(iL.items[vars.current].type) !== -1) {
8570						iL.loadContent(iL.items[vars.current], 'current');
8571					}
8572
8573					if (opts.linkId) setTimeout(function() {
8574						vars.hashLock = false;
8575					}, 55);
8576
8577					// Configure arrow buttons
8578					if (!opts.infinite) {
8579						vars.nextButton.add(vars.prevButton).add(vars.innerPrevButton).add(vars.innerNextButton).removeClass('disabled');
8580
8581						if (vars.current == 0)
8582							vars.prevButton.add(vars.innerPrevButton).addClass('disabled');
8583						if (vars.current >= vars.total - 1)
8584							vars.nextButton.add(vars.innerNextButton).addClass('disabled');
8585					}
8586
8587					iL.repositionPhoto();
8588
8589					// Reset next cycle timeout
8590					iL.resetCycle();
8591
8592					//Trigger the onAfterChange callback
8593					if (typeof opts.callback.onAfterChange == 'function')
8594						opts.callback.onAfterChange.call(iL, iL.ui);
8595				});
8596
8597				top = (path == 'horizontal') ? getPixel(lastHolder, 'top') : ((side == "next") ? parseInt(-(winH / 2) - lastHolder.outerHeight()) : parseInt(top * 2)),
8598					left = (path == 'horizontal') ? ((side == "next") ? parseInt(-(winW / 2) - lastHolder.outerWidth()) : parseInt(left * 2)) : getPixel(lastHolder, 'left');
8599
8600				lastHolder.css(transform, gpuAcceleration).animate({
8601					top: top,
8602					left: left,
8603					opacity: opts.styles.nextOpacity
8604				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc', function() {
8605					lastHolder.css(transform, '');
8606				}).addClass(secondClass);
8607			}
8608		},
8609
8610		setCaption: function(obj, target) {
8611			var iL = this,
8612				caption = $('<div class="ilightbox-caption"></div>');
8613
8614			if (obj.caption) {
8615				caption.html(obj.caption);
8616				$('div.ilightbox-container', target).append(caption);
8617			}
8618		},
8619
8620		normalizeSocial: function(obj, url) {
8621			var iL = this,
8622				vars = iL.vars,
8623				opts = iL.options,
8624				baseURL = window.location.href;
8625
8626			$.each(obj, function(key, value) {
8627				if (!value)
8628					return true;
8629
8630				var item = key.toLowerCase(),
8631					source, text;
8632
8633				switch (item) {
8634					case 'facebook':
8635						source = "http://www.facebook.com/share.php?v=4&src=bm&u={URL}",
8636							text = "Share on Facebook";
8637						break;
8638					case 'twitter':
8639						source = "http://twitter.com/home?status={URL}",
8640							text = "Share on Twitter";
8641						break;
8642					case 'googleplus':
8643						source = "https://plus.google.com/share?url={URL}",
8644							text = "Share on Google+";
8645						break;
8646					case 'delicious':
8647						source = "http://delicious.com/post?url={URL}",
8648							text = "Share on Delicious";
8649						break;
8650					case 'digg':
8651						source = "http://digg.com/submit?phase=2&url={URL}",
8652							text = "Share on Digg";
8653						break;
8654					case 'reddit':
8655						source = "http://reddit.com/submit?url={URL}",
8656							text = "Share on reddit";
8657						break;
8658				}
8659
8660				obj[key] = {
8661					URL: value.URL && absolutizeURI(baseURL, value.URL) || opts.linkId && window.location.href || typeof url !== 'string' && baseURL || url && absolutizeURI(baseURL, url) || baseURL,
8662					source: value.source || source || value.URL && absolutizeURI(baseURL, value.URL) || url && absolutizeURI(baseURL, url),
8663					text: value.text || text || "Share on " + key,
8664					width: (typeof(value.width) != 'undefined' && !isNaN(value.width)) ? parseInt(value.width) : 640,
8665					height: value.height || 360
8666				};
8667
8668			});
8669
8670			return obj;
8671		},
8672
8673		setSocial: function(obj, url, target) {
8674			var iL = this,
8675				socialBar = $('<div class="ilightbox-social"></div>'),
8676				buttons = '<ul>';
8677
8678			obj = iL.normalizeSocial(obj, url);
8679
8680			$.each(obj, function(key, value) {
8681				var item = key.toLowerCase(),
8682					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, '+'));
8683				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>';
8684			});
8685
8686			buttons += '</ul>';
8687
8688			socialBar.html(buttons);
8689			$('div.ilightbox-container', target).append(socialBar);
8690		},
8691
8692		fullScreenAction: function() {
8693			var iL = this,
8694				vars = iL.vars;
8695
8696			if (fullScreenApi.supportsFullScreen) {
8697				if (fullScreenApi.isFullScreen()) fullScreenApi.cancelFullScreen(document.documentElement);
8698				else fullScreenApi.requestFullScreen(document.documentElement);
8699			} else {
8700				iL.doFullscreen();
8701			}
8702		},
8703
8704		doFullscreen: function() {
8705			var iL = this,
8706				vars = iL.vars,
8707				viewport = getViewport(),
8708				opts = iL.options;
8709
8710			if (opts.fullAlone) {
8711				var currentHolder = vars.holder,
8712					current = iL.items[vars.current],
8713					windowWidth = viewport.width,
8714					windowHeight = viewport.height,
8715					elements = [currentHolder, vars.nextPhoto, vars.prevPhoto, vars.nextButton, vars.prevButton, vars.overlay, vars.toolbar, vars.thumbnails, vars.loader],
8716					hideElements = [vars.nextPhoto, vars.prevPhoto, vars.nextButton, vars.prevButton, vars.loader, vars.thumbnails];
8717
8718				if (!vars.isInFullScreen) {
8719					vars.isInFullScreen = vars.lockKey = vars.lockWheel = vars.lockSwipe = true;
8720					vars.overlay.css({
8721						opacity: 1
8722					});
8723
8724					$.each(hideElements, function(i, element) {
8725						element.hide();
8726					});
8727
8728					vars.fullScreenButton.attr('title', opts.text.exitFullscreen);
8729
8730					if (opts.fullStretchTypes.indexOf(current.type) != -1) currentHolder.data({
8731						naturalWidthOld: currentHolder.data('naturalWidth'),
8732						naturalHeightOld: currentHolder.data('naturalHeight'),
8733						naturalWidth: windowWidth,
8734						naturalHeight: windowHeight
8735					});
8736					else {
8737						var viewport = current.options.fullViewPort || opts.fullViewPort || '',
8738							newWidth = windowWidth,
8739							newHeight = windowHeight,
8740							width = currentHolder.data('naturalWidth'),
8741							height = currentHolder.data('naturalHeight');
8742
8743						if (viewport.toLowerCase() == 'fill') {
8744							newHeight = (newWidth / width) * height;
8745
8746							if (newHeight < windowHeight) {
8747								newWidth = (windowHeight / height) * width,
8748									newHeight = windowHeight;
8749							}
8750						} else if (viewport.toLowerCase() == 'fit') {
8751							var dims = iL.getNewDimenstions(newWidth, newHeight, width, height, true);
8752
8753							newWidth = dims.width,
8754								newHeight = dims.height;
8755						} else if (viewport.toLowerCase() == 'stretch') {
8756							newWidth = newWidth,
8757								newHeight = newHeight;
8758						} else {
8759							var scale = (width > newWidth || height > newHeight) ? true : false,
8760								dims = iL.getNewDimenstions(newWidth, newHeight, width, height, scale);
8761							newWidth = dims.width,
8762								newHeight = dims.height;
8763						}
8764
8765						currentHolder.data({
8766							naturalWidthOld: currentHolder.data('naturalWidth'),
8767							naturalHeightOld: currentHolder.data('naturalHeight'),
8768							naturalWidth: newWidth,
8769							naturalHeight: newHeight
8770						});
8771					}
8772
8773					$.each(elements, function(key, val) {
8774						val.addClass('ilightbox-fullscreen');
8775					});
8776
8777					//Trigger the onEnterFullScreen callback
8778					if (typeof opts.callback.onEnterFullScreen == 'function') opts.callback.onEnterFullScreen.call(iL, iL.ui);
8779				} else {
8780					vars.isInFullScreen = vars.lockKey = vars.lockWheel = vars.lockSwipe = false;
8781					vars.overlay.css({
8782						opacity: iL.options.overlay.opacity
8783					});
8784
8785					$.each(hideElements, function(i, element) {
8786						element.show();
8787					});
8788
8789					vars.fullScreenButton.attr('title', opts.text.enterFullscreen);
8790
8791					currentHolder.data({
8792						naturalWidth: currentHolder.data('naturalWidthOld'),
8793						naturalHeight: currentHolder.data('naturalHeightOld'),
8794						naturalWidthOld: null,
8795						naturalHeightOld: null
8796					});
8797
8798					$.each(elements, function(key, val) {
8799						val.removeClass('ilightbox-fullscreen');
8800					});
8801
8802					//Trigger the onExitFullScreen callback
8803					if (typeof opts.callback.onExitFullScreen == 'function') opts.callback.onExitFullScreen.call(iL, iL.ui);
8804				}
8805			} else {
8806				if (!vars.isInFullScreen) vars.isInFullScreen = true;
8807				else vars.isInFullScreen = false;
8808			}
8809			iL.repositionPhoto(true);
8810		},
8811
8812		closeAction: function() {
8813			var iL = this,
8814				vars = iL.vars,
8815				opts = iL.options;
8816
8817			$win.unbind('.iLightBox');
8818			$doc.off('.iLightBox');
8819
8820			if (vars.isInFullScreen) fullScreenApi.cancelFullScreen(document.documentElement);
8821
8822			$('.ilightbox-overlay, .ilightbox-holder, .ilightbox-thumbnails').off('.iLightBox');
8823
8824			if (opts.hide.effect) vars.overlay.stop().fadeOut(opts.hide.speed, function() {
8825				vars.overlay.remove();
8826				vars.BODY.removeClass('ilightbox-noscroll').off('.iLightBox');
8827			});
8828			else {
8829				vars.overlay.remove();
8830				vars.BODY.removeClass('ilightbox-noscroll').off('.iLightBox');
8831			}
8832
8833			var fadeOuts = [vars.toolbar, vars.holder, vars.nextPhoto, vars.prevPhoto, vars.nextButton, vars.prevButton, vars.loader, vars.thumbnails];
8834
8835			$.each(fadeOuts, function(i, element) {
8836				element.removeAttr('style').remove();
8837			});
8838
8839			vars.dontGenerateThumbs = vars.isInFullScreen = false;
8840
8841			window.iLightBox = null;
8842
8843			if (opts.linkId) {
8844				vars.hashLock = true;
8845				removeHash();
8846				setTimeout(function() {
8847					vars.hashLock = false;
8848				}, 55);
8849			}
8850
8851			//Trigger the onHide callback
8852			if (typeof opts.callback.onHide == 'function') opts.callback.onHide.call(iL, iL.ui);
8853		},
8854
8855		repositionPhoto: function() {
8856			var iL = this,
8857				vars = iL.vars,
8858				opts = iL.options,
8859				path = opts.path.toLowerCase(),
8860				viewport = getViewport(),
8861				winWidth = viewport.width,
8862				winHeight = viewport.height;
8863
8864			if (viewport.width < UNCODE.mediaQuery) opts.styles.nextOffsetX = 0;
8865
8866			var thumbsOffsetW = (vars.isInFullScreen && opts.fullAlone || vars.isMobile) ? 0 : ((path == 'horizontal') ? 0 : vars.thumbnails.outerWidth()),
8867				thumbsOffsetH = vars.isMobile ? vars.toolbar.outerHeight() : ((vars.isInFullScreen && opts.fullAlone) ? 0 : ((path == 'horizontal') ? vars.thumbnails.outerHeight() : 0)),
8868				width = (vars.isInFullScreen && opts.fullAlone) ? winWidth : (winWidth - (opts.styles.pageOffsetX)),
8869				height = (vars.isInFullScreen && opts.fullAlone) ? winHeight : (winHeight - (opts.styles.pageOffsetY)),
8870				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,
8871				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)));
8872
8873			var elObject = {
8874				type: 'current',
8875				width: width,
8876				height: height,
8877				item: iL.items[vars.current],
8878				offsetW: offsetW,
8879				offsetH: offsetH,
8880				thumbsOffsetW: thumbsOffsetW,
8881				thumbsOffsetH: thumbsOffsetH,
8882				animate: arguments.length,
8883				holder: vars.holder
8884			};
8885
8886			iL.repositionEl(elObject);
8887
8888			if (iL.items[vars.next]) {
8889				elObject = $.extend(elObject, {
8890					type: 'next',
8891					item: iL.items[vars.next],
8892					offsetX: opts.styles.nextOffsetX,
8893					offsetY: opts.styles.nextOffsetY,
8894					holder: vars.nextPhoto
8895				});
8896
8897				iL.repositionEl(elObject);
8898			}
8899
8900			if (iL.items[vars.prev]) {
8901				elObject = $.extend(elObject, {
8902					type: 'prev',
8903					item: iL.items[vars.prev],
8904					offsetX: opts.styles.nextOffsetX,
8905					offsetY: opts.styles.prevOffsetY,
8906					holder: vars.prevPhoto
8907				});
8908
8909				iL.repositionEl(elObject);
8910			}
8911
8912			var loaderCss = (path == "horizontal") ? {
8913				left: parseInt((width / 2) - (vars.loader.outerWidth() / 2))
8914			} : {
8915				top: parseInt((height / 2) - (vars.loader.outerHeight() / 2))
8916			};
8917			vars.loader.css(loaderCss);
8918		},
8919
8920		repositionEl: function(obj) {
8921			var iL = this,
8922				vars = iL.vars,
8923				opts = iL.options,
8924				path = opts.path.toLowerCase(),
8925				widthAvail = (obj.type == 'current') ? ((vars.isInFullScreen && opts.fullAlone) ? obj.width : (obj.width - obj.offsetW)) : (obj.width - obj.offsetW),
8926				heightAvail = (obj.type == 'current') ? ((vars.isInFullScreen && opts.fullAlone) ? obj.height : (obj.height - obj.offsetH)) : (obj.height - obj.offsetH),
8927				itemParent = obj.item,
8928				item = obj.item.options,
8929				holder = obj.holder,
8930				offsetX = obj.offsetX || 0,
8931				offsetY = obj.offsetY || 0,
8932				thumbsOffsetW = obj.thumbsOffsetW,
8933				thumbsOffsetH = obj.thumbsOffsetH;
8934
8935			if (obj.type == 'current') {
8936				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);
8937				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);
8938			} else {
8939				if (typeof item.width == 'number' && item.width) widthAvail = (item.width > widthAvail) ? widthAvail : item.width;
8940				if (typeof item.height == 'number' && item.height) heightAvail = (item.height > heightAvail) ? heightAvail : item.height;
8941			}
8942
8943			heightAvail = parseInt(heightAvail - $('.ilightbox-inner-toolbar', holder).outerHeight());
8944
8945			var width = (typeof item.width == 'string' && item.width.indexOf('%') != -1) ? percentToValue(parseInt(item.width.replace('%', '')), obj.width) : holder.data('naturalWidth'),
8946				height = (typeof item.height == 'string' && item.height.indexOf('%') != -1) ? percentToValue(parseInt(item.height.replace('%', '')), obj.height) : holder.data('naturalHeight');
8947
8948			var dims = ((typeof item.width == 'string' && item.width.indexOf('%') != -1 || typeof item.height == 'string' && item.height.indexOf('%') != -1) ? {
8949					width: width,
8950					height: height
8951				} : iL.getNewDimenstions(widthAvail, heightAvail, width, height)),
8952				newDims = $.extend({}, dims, {});
8953
8954			if (obj.type == 'prev' || obj.type == 'next')
8955				width = parseInt(dims.width * ((obj.type == 'next') ? opts.styles.nextScale : opts.styles.prevScale)),
8956				height = parseInt(dims.height * ((obj.type == 'next') ? opts.styles.nextScale : opts.styles.prevScale));
8957			else
8958				width = dims.width,
8959				height = dims.height;
8960
8961			var widthDiff = parseInt((getPixel(holder, 'padding-left') + getPixel(holder, 'padding-right') + getPixel(holder, 'border-left-width') + getPixel(holder, 'border-right-width')) / 2),
8962				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);
8963
8964			switch (obj.type) {
8965				case 'current':
8966					var top = parseInt((obj.height / 2) - (height / 2) - heightDiff - (thumbsOffsetH / 2)),
8967						left = parseInt((obj.width / 2) - (width / 2) - widthDiff - (thumbsOffsetW / 2));
8968					break;
8969
8970				case 'next':
8971					var top = (path == 'horizontal') ? parseInt((obj.height / 2) - offsetY - (height / 2) - heightDiff - (thumbsOffsetH / 2)) : parseInt(obj.height - offsetX - heightDiff - (thumbsOffsetH / 2)),
8972						left = (path == 'horizontal') ? parseInt(obj.width - offsetX - widthDiff - (thumbsOffsetW / 2)) : parseInt((obj.width / 2) - (width / 2) - widthDiff - offsetY - (thumbsOffsetW / 2));
8973					break;
8974
8975				case 'prev':
8976					var top = (path == 'horizontal') ? parseInt((obj.height / 2) - offsetY - (height / 2) - heightDiff - (thumbsOffsetH / 2)) : parseInt(offsetX - heightDiff - height - (thumbsOffsetH / 2)),
8977						left = (path == 'horizontal') ? parseInt(offsetX - widthDiff - width - (thumbsOffsetW / 2)) : parseInt((obj.width / 2) - offsetY - (width / 2) - widthDiff - (thumbsOffsetW / 2));
8978					break;
8979			}
8980
8981			holder.data('offset', {
8982				top: top,
8983				left: left,
8984				newDims: newDims,
8985				diff: {
8986					W: widthDiff,
8987					H: heightDiff
8988				},
8989				thumbsOffset: {
8990					W: thumbsOffsetW,
8991					H: thumbsOffsetH
8992				},
8993				object: obj
8994			});
8995
8996			var opacityMobile;
8997			if ( isMobile ) {
8998				opacityMobile = {
8999					opacity: 1,
9000					top: top,
9001					left: left
9002				};
9003			} else {
9004				opacityMobile = {
9005					top: top,
9006					left: left
9007				};
9008			}
9009
9010			if (obj.animate > 0 && opts.effects.reposition) {
9011				holder.css(transform, gpuAcceleration).stop().animate(
9012					opacityMobile, 
9013					opts.effects.repositionSpeed, 'easeOutCirc', function() {
9014					holder.css(transform, '');
9015				});
9016				$('div.ilightbox-container', holder).stop().animate({
9017					width: width,
9018					height: height
9019				}, opts.effects.repositionSpeed, 'easeOutCirc');
9020				$('div.ilightbox-inner-toolbar', holder).stop().animate({
9021					width: width
9022				}, opts.effects.repositionSpeed, 'easeOutCirc', function() {
9023					$(this).css('overflow', 'visible');
9024				});
9025			} else {
9026				holder.css(opacityMobile);
9027				$('div.ilightbox-container', holder).css({
9028					width: width,
9029					height: height
9030				});
9031				$('div.ilightbox-inner-toolbar', holder).css({
9032					width: width
9033				});
9034			}
9035		},
9036
9037
9038		resume: function(priority) {
9039			var iL = this,
9040				vars = iL.vars,
9041				opts = iL.options;
9042
9043			if (!opts.slideshow.pauseTime || opts.controls.slideshow && vars.total <= 1 || priority < vars.isPaused) {
9044				return;
9045			}
9046
9047			vars.isPaused = 0;
9048
9049			if (vars.cycleID) {
9050				vars.cycleID = clearTimeout(vars.cycleID);
9051			}
9052
9053			vars.cycleID = setTimeout(function() {
9054				if (vars.current == vars.total - 1) iL.goTo(0);
9055				else iL.moveTo('next');
9056			}, opts.slideshow.pauseTime);
9057		},
9058
9059		pause: function(priority) {
9060			var iL = this,
9061				vars = iL.vars,
9062				opts = iL.options;
9063
9064			if (priority < vars.isPaused) {
9065				return;
9066			}
9067
9068			vars.isPaused = priority || 100;
9069
9070			if (vars.cycleID) {
9071				vars.cycleID = clearTimeout(vars.cycleID);
9072			}
9073		},
9074
9075		resetCycle: function() {
9076			var iL = this,
9077				vars = iL.vars,
9078				opts = iL.options;
9079
9080			if (opts.controls.slideshow && vars.cycleID && !vars.isPaused) {
9081				iL.resume();
9082			}
9083		},
9084
9085		getNewDimenstions: function(width, height, width_old, height_old, thumb) {
9086			var iL = this;
9087
9088			if (!width) factor = height / height_old;
9089			else if (!height) factor = width / width_old;
9090			else factor = min(width / width_old, height / height_old);
9091
9092			if (!thumb) {
9093				if (factor > iL.options.maxScale) factor = iL.options.maxScale;
9094				else if (factor < iL.options.minScale) factor = iL.options.minScale;
9095			}
9096
9097			var final_width = (iL.options.keepAspectRatio) ? round(width_old * factor) : width,
9098				final_height = (iL.options.keepAspectRatio) ? round(height_old * factor) : height;
9099
9100			return {
9101				width: final_width,
9102				height: final_height,
9103				ratio: factor
9104			};
9105		},
9106
9107		setOption: function(options) {
9108			var iL = this;
9109
9110			iL.options = $.extend(true, iL.options, options || {});
9111			iL.refresh();
9112		},
9113
9114		availPlugins: function() {
9115			var iL = this,
9116				testEl = document.createElement("video");
9117
9118			iL.plugins = {
9119				flash: !isMobile,
9120				quicktime: (parseInt(PluginDetect.getVersion("QuickTime")) >= 0) ? true : false,
9121				html5H264: !!(testEl.canPlayType && testEl.canPlayType('video/mp4').replace(/no/, '')),
9122				html5WebM: !!(testEl.canPlayType && testEl.canPlayType('video/webm').replace(/no/, '')),
9123				html5Vorbis: !!(testEl.canPlayType && testEl.canPlayType('video/ogg').replace(/no/, '')),
9124				html5QuickTime: !!(testEl.canPlayType && testEl.canPlayType('video/quicktime').replace(/no/, ''))
9125			};
9126		},
9127
9128		addContent: function(element, obj) {
9129			var iL = this,
9130				el;
9131
9132			switch (obj.type) {
9133				case 'video':
9134					var HTML5 = false,
9135						videoType = obj.videoType,
9136						html5video = obj.options.html5video;
9137
9138					if (((videoType == 'video/mp4' || obj.ext == 'mp4' || obj.ext == 'm4v') || html5video.h264) && iL.plugins.html5H264)
9139						obj.ext = 'mp4',
9140						obj.URL = html5video.h264 || obj.URL;
9141					else if (html5video.webm && iL.plugins.html5WebM)
9142						obj.ext = 'webm',
9143						obj.URL = html5video.webm || obj.URL;
9144					else if (html5video.ogg && iL.plugins.html5Vorbis)
9145						obj.ext = 'ogv',
9146						obj.URL = html5video.ogg || obj.URL;
9147
9148					if (iL.plugins.html5H264 && (videoType == 'video/mp4' || obj.ext == 'mp4' || obj.ext == 'm4v')) HTML5 = true, videoType = "video/mp4";
9149					else if (iL.plugins.html5WebM && (videoType == 'video/webm' || obj.ext == 'webm')) HTML5 = true, videoType = "video/webm";
9150					else if (iL.plugins.html5Vorbis && (videoType == 'video/ogg' || obj.ext == 'ogv')) HTML5 = true, videoType = "video/ogg";
9151					else if (iL.plugins.html5QuickTime && (videoType == 'video/quicktime' || obj.ext == 'mov' || obj.ext == 'qt')) HTML5 = true, videoType = "video/quicktime";
9152
9153					if (HTML5) {
9154						el = $('<video />
9154', {
9155							"width": "100%",
9156							"height": "100%",
9157							"preload": html5video.preload,
9158							"autoplay": html5video.autoplay,
9159							"poster": html5video.poster,
9160							"controls": html5video.controls
9161						}).append($('<source />', {
9162							"src": obj.URL,
9163							"type": videoType
9164						}));
9165					} else {
9166						if (!iL.plugins.quicktime) el = $('<span />', {
9167							"class": "ilightbox-alert",
9168							html: iL.options.errors.missingPlugin.replace('{pluginspage}', pluginspages.quicktime).replace('{type}', 'QuickTime')
9169						});
9170						else {
9171
9172							el = $('<object />', {
9173								"type": "video/quicktime",
9174								"pluginspage": pluginspages.quicktime
9175							}).attr({
9176								"data": obj.URL,
9177								"width": "100%",
9178								"height": "100%"
9179							}).append($('<param />', {
9180								"name": "src",
9181								"value": obj.URL
9182							})).append($('<param />', {
9183								"name": "autoplay",
9184								"value": "false"
9185							})).append($('<param />', {
9186								"name": "loop",
9187								"value": "false"
9188							})).append($('<param />', {
9189								"name": "scale",
9190								"value": "tofit"
9191							}));
9192
9193							if (browser.msie) el = QT_GenerateOBJECTText(obj.URL, '100%', '100%', '', 'SCALE', 'tofit', 'AUTOPLAY', 'false', 'LOOP', 'false');
9194						}
9195					}
9196
9197					break;
9198
9199				case 'flash':
9200					if (!iL.plugins.flash) el = $('<span />', {
9201						"class": "ilightbox-alert",
9202						html: iL.options.errors.missingPlugin.replace('{pluginspage}', pluginspages.flash).replace('{type}', 'Adobe Flash player')
9203					});
9204					else {
9205						var flashvars = "",
9206							i = 0;
9207
9208						if (obj.options.flashvars) $.each(obj.options.flashvars, function(k, v) {
9209							if (i != 0) flashvars += "&";
9210							flashvars += k + "=" + encodeURIComponent(v);
9211							i++;
9212						});
9213						else flashvars = null;
9214
9215						el = $('<embed />').attr({
9216							"type": "application/x-shockwave-flash",
9217							"src": obj.URL,
9218							"width": (typeof obj.options.width == 'number' && obj.options.width && iL.options.minScale == '1' && iL.options.maxScale == '1') ? obj.options.width : "100%",
9219							"height": (typeof obj.options.height == 'number' && obj.options.height && iL.options.minScale == '1' && iL.options.maxScale == '1') ? obj.options.height : "100%",
9220							"quality": "high",
9221							"bgcolor": "#000000",
9222							"play": "true",
9223							"loop": "true",
9224							"menu": "true",
9225							"wmode": "transparent",
9226							"scale": "showall",
9227							"allowScriptAccess": "always",
9228							"allowFullScreen": "true",
9229							"flashvars": flashvars,
9230							"fullscreen": "yes"
9231						});
9232					}
9233
9234					break;
9235
9236				case 'iframe':
9237					el = $('<iframe />').attr({
9238						"width": (typeof obj.options.width == 'number' && obj.options.width && iL.options.minScale == '1' && iL.options.maxScale == '1') ? obj.options.width : "100%",
9239						"height": (typeof obj.options.height == 'number' && obj.options.height && iL.options.minScale == '1' && iL.options.maxScale == '1') ? obj.options.height : "100%",
9240						src: obj.URL,
9241						frameborder: 0,
9242						'hspace': 0,
9243						'vspace': 0,
9244						'scrolling': supportTouch ? 'auto' : 'scroll',
9245						'webkitAllowFullScreen': '',
9246						'mozallowfullscreen': '',
9247						'allowFullScreen': ''
9248					});
9249
9250					break;
9251
9252				case 'inline':
9253					el = $('<div class="ilightbox-wrapper"></div>').html($(obj.URL).clone(true));
9254
9255					break;
9256
9257				case 'html':
9258					var object = obj.URL,
9259						el;
9260
9261					if (object[0].nodeName) {
9262						el = $('<div class="ilightbox-wrapper"></div>').html(object);
9263					} else {
9264						var dom = $(obj.URL),
9265							html = (dom.selector) ? $('<div>' + dom + '</div>') : dom;
9266						el = $('<div class="ilightbox-wrapper"></div>').html(html);
9267					}
9268
9269					break;
9270			}
9271
9272			$('div.ilightbox-container', element).empty().html(el);
9273
9274			// For fixing Chrome about just playing the video for first time
9275			if (el[0].tagName.toLowerCase() === 'video' && browser.webkit) setTimeout(function() {
9276				var src = el[0].currentSrc + '?' + floor(random() * 30000);
9277				el[0].currentSrc = src;
9278				el[0].src = src;
9279			});
9280
9281			return el;
9282		},
9283
9284		ogpRecognition: function(obj, callback) {
9285			var iL = this,
9286				url = obj.URL;
9287
9288			iL.showLoader();
9289			doAjax(url, function(data) {
9290				iL.hideLoader();
9291				if (data) {
9292					var object = new Object();
9293
9294					object.length = false,
9295					object.url = data.url;
9296
9297					if (data.status == 200) {
9298						var result = data.results,
9299							type = result.type,
9300							source = result.source;
9301
9302						object.source = source.src,
9303						object.width = source.width && parseInt(source.width) || 0,
9304						object.height = source.height && parseInt(source.height) || 0,
9305						object.type = type,
9306						object.thumbnail = source.thumbnail || result.images && result.images[0],
9307						object.html5video = result.html5video || {},
9308						object.length = true;
9309
9310						if (source.type == 'application/x-shockwave-flash') object.type = "flash";
9311						else if (source.type.indexOf("video/") != -1) object.type = "video";
9312						else if (source.type.indexOf("/html") != -1) object.type = "iframe";
9313						else if (source.type.indexOf("image/") != -1) object.type = "image";
9314
9315					} else if (typeof data.response != 'undefined')
9316						throw data.response;
9317
9318					callback.call(this, object.length ? object : false);
9319				}
9320			});
9321		},
9322
9323		hashChangeHandler: function(url) {
9324			var iL = this,
9325				vars = iL.vars,
9326				opts = iL.options,
9327				URL = url || window.location.href,
9328				hash = parseURI(URL).hash,
9329				split = hash.split('/'),
9330				index = split[1];
9331
9332			if (vars.hashLock || ('#' + opts.linkId != split[0] && hash.length > 1)) return;
9333
9334			if (index) {
9335				var target = split[1] || 0;
9336				if (iL.items[target]) {
9337					var overlay = $('.ilightbox-overlay');
9338					if (overlay.length && overlay.attr('linkid') == opts.linkId) {
9339						iL.goTo(target);
9340					} else {
9341						iL.itemsObject[target].trigger(supportTouch ? 'itap' : 'click');
9342						//iL.goTo(target);
9343					}
9344				} else {
9345					var overlay = $('.ilightbox-overlay');
9346					if (overlay.length) iL.closeAction();
9347				}
9348			} else {
9349				var overlay = $('.ilightbox-overlay');
9350				if (overlay.length) iL.closeAction();
9351			}
9352		}
9353	};
9354
9355	/**
9356	 * Parse style to pixels.
9357	 *
9358	 * @param {Object}   $element   jQuery object with element.
9359	 * @param {Property} property   CSS property to get the pixels from.
9360	 *
9361	 * @return {Int}
9362	 */
9363	function getPixel($element, property) {
9364		return parseInt($element.css(property), 10) || 0;
9365	}
9366
9367	/**
9368	 * Make sure that number is within the limits.
9369	 *
9370	 * @param {Number} number
9371	 * @param {Number} min
9372	 * @param {Number} max
9373	 *
9374	 * @return {Number}
9375	 */
9376	function within(number, min, max) {
9377		return number < min ? min : number > max ? max : number;
9378	}
9379
9380	/**
9381	 * Get viewport/window size (width and height).
9382	 *
9383	 * @return {Object}
9384	 */
9385	function getViewport() {
9386		var e = window,
9387			a = 'inner';
9388		if (!('innerWidth' in window)) {
9389			a = 'client';
9390			e = document.documentElement || document.body;
9391		}
9392		return {
9393			width: e[a + 'Width'],
9394			height: e[a + 'Height']
9395		}
9396	}
9397
9398	/**
9399	 * Remove hash tag from the URL
9400	 *
9401	 * @return {Void}
9402	 */
9403	function removeHash() {
9404		var scroll = getScrollXY();
9405
9406		//UNCODE change		
9407		//window.location.hash = "";
9408		history.pushState("", document.title, window.location.pathname + window.location.search);
9409
9410		// Restore the scroll offset, should be flicker free
9411		window.scrollTo(scroll.x, scroll.y);
9412	}
9413
9414	/**
9415	 * Do the ajax requests with callback.
9416	 *
9417	 * @param {String}   url
9418	 * @param {Function} callback
9419	 *
9420	 * @return {Void}
9421	 */
9422	function doAjax(url, callback) {
9423		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');
9424		$.ajax({
9425			url: url,
9426			dataType: 'jsonp'
9427		});
9428
9429		iLCallback = function(data) {
9430			callback.call(this, data);
9431		};
9432	}
9433
9434	/**
9435	 * Find image from DOM elements
9436	 *
9437	 * @param {Element} element
9438	 *
9439	 * @return {Void}
9440	 */
9441	function findImageInElement(element) {
9442		var elements = $('*', element),
9443			imagesArr = new Array();
9444
9445		elements.each(function() {
9446			var url = "",
9447				element = this;
9448
9449			if ($(element).css("background-image") != "none") {
9450				url = $(element).css("background-image");
9451			} else if (typeof($(element).attr("src")) != "undefined" && element.nodeName.toLowerCase() == "img") {
9452				url = $(element).attr("src");
9453			}
9454
9455			if (url.indexOf("gradient") == -1) {
9456				url = url.replace(/url\(\"/g, "");
9457				url = url.replace(/url\(/g, "");
9458				url = url.replace(/\"\)/g, "");
9459				url = url.replace(/\)/g, "");
9460
9461				var urls = url.split(",");
9462
9463				for (var i = 0; i < urls.length; i++) {
9464					if (urls[i].length > 0 && $.inArray(urls[i], imagesArr) == -1) {
9465						var extra = "";
9466						if (browser.msie && browser.version < 9) {
9467							extra = "?" + floor(random() * 3000);
9468						}
9469						imagesArr.push(urls[i] + extra);
9470					}
9471				}
9472			}
9473		});
9474
9475		return imagesArr;
9476	}
9477
9478	/**
9479	 * Get file extension.
9480	 *
9481	 * @param {String} URL
9482	 *
9483	 * @return {String}
9484	 */
9485	function getExtension(URL) {
9486		if (URL !== null) {
9487			var ext = URL.split('.').pop().toLowerCase(),
9488				extra = ext.indexOf('?') !== -1 ? ext.split('?').pop() : '';
9489
9490			return ext.replace(extra, '');
9491		}
9492	}
9493
9494	/**
9495	 * Get type via extension.
9496	 *
9497	 * @param {String} URL
9498	 *
9499	 * @return {String}
9500	 */
9501	function getTypeByExtension(URL) {
9502		var type,
9503			ext = getExtension(URL);
9504
9505		if (extensions.image.indexOf(ext) !== -1) type = 'image';
9506		else if (extensions.flash.indexOf(ext) !== -1) type = 'flash';
9507		else if (extensions.video.indexOf(ext) !== -1) type = 'video';
9508		else if (ext.substring(0, 1) == "#") type = 'inline'; //Uncode addition
9509		else type = 'iframe';
9510		
9511		return type;
9512	}
9513
9514	/**
9515	 * Return value from percent of a number.
9516	 *
9517	 * @param {Number} percent
9518	 * @param {Number} total
9519	 *
9520	 * @return {Number}
9521	 */
9522	function percentToValue(percent, total) {
9523		return parseInt((total / 100) * percent);
9524	}
9525
9526	/**
9527	 * A JavaScript equivalent of PHP’s parse_url.
9528	 *
9529	 * @param {String} url           The URL to parse.
9530	 *
9531	 * @return {Mixed}
9532	 */
9533	function parseURI(url) {
9534		var m = String(url).replace(/^\s+|\s+$/g, '').match(/^([^:\/?#]+:)?(\/\/(?:[^:@]*(?::[^:@]*)?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/);
9535		// authority = '//' + user + ':' + pass '@' + hostname + ':' port
9536		return (m ? {
9537			href: m[0] || '',
9538			protocol: m[1] || '',
9539			authority: m[2] || '',
9540			host: m[3] || '',
9541			hostname: m[4] || '',
9542			port: m[5] || '',
9543			pathname: m[6] || '',
9544			search: m[7] || '',
9545			hash: m[8] || ''
9546		} : null);
9547	}
9548
9549	/**
9550	 * Gets the absolute URI.
9551	 *
9552	 * @param {String} href     The relative URL.
9553	 * @param {String} base     The base URL.
9554	 *
9555	 * @return {String}         The absolute URL.
9556	 */
9557	function absolutizeURI(base, href) { // RFC 3986
9558		var iL = this;
9559
9560		function removeDotSegments(input) {
9561			var output = [];
9562			input.replace(/^(\.\.?(\/|$))+/, '')
9563				.replace(/\/(\.(\/|$))+/g, '/')
9564				.replace(/\/\.\.$/, '/../')
9565				.replace(/\/?[^\/]*/g, function(p) {
9566					if (p === '/..') {
9567						output.pop();
9568					} else {
9569						output.push(p);
9570					}
9571				});
9572			return output.join('').replace(/^\//, input.charAt(0) === '/' ? '/' : '');
9573		}
9574
9575		href = parseURI(href || '');
9576		base = parseURI(base || '');
9577
9578		return !href || !base ? null : (href.protocol || base.protocol) +
9579			(href.protocol || href.authority ? href.authority : base.authority) +
9580			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)) +
9581			(href.protocol || href.authority || href.pathname ? href.search : (href.search || base.search)) +
9582			href.hash;
9583	}
9584
9585	/**
9586	 * A JavaScript equivalent of PHP’s version_compare.
9587	 *
9588	 * @param {String} v1
9589	 * @param {String} v2
9590	 * @param {String} operator
9591	 *
9592	 * @return {Boolean}
9593	 */
9594	function version_compare(v1, v2, operator) {
9595		this.php_js = this.php_js || {};
9596		this.php_js.ENV = this.php_js.ENV || {};
9597		var i = 0,
9598			x = 0,
9599			compare = 0,
9600			vm = {
9601				'dev': -6,
9602				'alpha': -5,
9603				'a': -5,
9604				'beta': -4,
9605				'b': -4,
9606				'RC': -3,
9607				'rc': -3,
9608				'#': -2,
9609				'p': 1,
9610				'pl': 1
9611			},
9612			prepVersion = function(v) {
9613				v = ('' + v).replace(/[_\-+]/g, '.');
9614				v = v.replace(/([^.\d]+)/g, '.$1.').replace(/\.{2,}/g, '.');
9615				return (!v.length ? [-8] : v.split('.'));
9616			},
9617			numVersion = function(v) {
9618				return !v ? 0 : (isNaN(v) ? vm[v] || -7 : parseInt(v, 10));
9619			};
9620		v1 = prepVersion(v1);
9621		v2 = prepVersion(v2);
9622		x = max(v1.length, v2.length);
9623		for (i = 0; i < x; i++) {
9624			if (v1[i] == v2[i]) {
9625				continue;
9626			}
9627			v1[i] = numVersion(v1[i]);
9628			v2[i] = numVersion(v2[i]);
9629			if (v1[i] < v2[i]) {
9630				compare = -1;
9631				break;
9632			} else if (v1[i] > v2[i]) {
9633				compare = 1;
9634				break;
9635			}
9636		}
9637		if (!operator) {
9638			return compare;
9639		}
9640
9641		switch (operator) {
9642			case '>':
9643			case 'gt':
9644				return (compare > 0);
9645			case '>=':
9646			case 'ge':
9647				return (compare >= 0);
9648			case '<=':
9649			case 'le':
9650				return (compare <= 0);
9651			case '==':
9652			case '=':
9653			case 'eq':
9654				return (compare === 0);
9655			case '<>':
9656			case '!=':
9657			case 'ne':
9658				return (compare !== 0);
9659			case '':
9660			case '<':
9661			case 'lt':
9662				return (compare < 0);
9663			default:
9664				return null;
9665		}
9666	}
9667
9668
9669	// Begin the iLightBox plugin
9670	$.fn.iLightBox = function() {
9671
9672		var args = arguments,
9673			opt = ($.isPlainObject(args[0])) ? args[0] : args[1],
9674			items = ($.isArray(args[0]) || typeof args[0] == 'string') ? args[0] : args[1];
9675
9676		if (!opt) opt = {};
9677
9678		// Default options. Play carefully.
9679		var options = $.extend(true, {
9680			attr: 'href',
9681			path: 'vertical',
9682			skin: 'dark',
9683			linkId: false,
9684			infinite: false,
9685			startFrom: 0,
9686			randomStart: false,
9687			keepAspectRatio: true,
9688			maxScale: 1,
9689			minScale: .2,
9690			innerToolbar: false,
9691			smartRecognition: false,
9692			mobileOptimizer: true,
9693			fullAlone: true,
9694			fullViewPort: null,
9695			fullStretchTypes: 'flash, video',
9696			overlay: {
9697				blur: true,
9698				opacity: .85
9699			},
9700			controls: {
9701				arrows: false,
9702				slideshow: false,
9703				toolbar: true,
9704				fullscreen: true,
9705				thumbnail: true,
9706				keyboard: true,
9707				mousewheel: true,
9708				swipe: true
9709			},
9710			keyboard: {
9711				left: true, // previous
9712				right: true, // next
9713				up: true, // previous
9714				down: true, // next
9715				esc: true, // close
9716				shift_enter: true // fullscreen
9717			},
9718			show: {
9719				effect: true,
9720				speed: 300,
9721				title: true
9722			},
9723			hide: {
9724				effect: true,
9725				speed: 300
9726			},
9727			caption: {
9728				start: true,
9729				show: 'mouseenter',
9730				hide: 'mouseleave'
9731			},
9732			social: {
9733				start: true,
9734				show: 'mouseenter',
9735				hide: 'mouseleave',
9736				buttons: false
9737			},
9738			styles: {
9739				pageOffsetX: 0,
9740				pageOffsetY: 0,
9741				nextOffsetX: 45,
9742				nextOffsetY: 0,
9743				nextOpacity: 1,
9744				nextScale: 1,
9745				prevOffsetX: 45,
9746				prevOffsetY: 0,
9747				prevOpacity: 1,
9748				prevScale: 1
9749			},
9750			thumbnails: {
9751				maxWidth: 120,
9752				maxHeight: 80,
9753				normalOpacity: 1,
9754				activeOpacity: .6
9755			},
9756			effects: {
9757				reposition: true,
9758				repositionSpeed: 200,
9759				switchSpeed: 500,
9760				loadedFadeSpeed: 180,
9761				fadeSpeed: 200
9762			},
9763			slideshow: {
9764				pauseTime: 5000,
9765				pauseOnHover: false,
9766				startPaused: true
9767			},
9768			text: {
9769				close: "Press Esc to close",
9770				enterFullscreen: "Enter Fullscreen (Shift+Enter)",
9771				exitFullscreen: "Exit Fullscreen (Shift+Enter)",
9772				slideShow: "Slideshow",
9773				next: "Next",
9774				previous: "Previous"
9775			},
9776			errors: {
9777				loadImage: "An error occurred when trying to load photo.",
9778				loadContents: "An error occurred when trying to load contents.",
9779				missingPlugin: "The content your are attempting to view requires the <a href='{pluginspage}' target='_blank'>{type} plugin<\/a>."
9780			},
9781			ajaxSetup: {
9782				url: '',
9783				beforeSend: function(jqXHR, settings) {},
9784				cache: false,
9785				complete: function(jqXHR, textStatus) {},
9786				crossDomain: false,
9787				error: function(jqXHR, textStatus, errorThrown) {},
9788				success: function(data, textStatus, jqXHR) {},
9789				global: true,
9790				ifModified: false,
9791				username: null,
9792				password: null,
9793				type: 'GET'
9794			},
9795			callback: {}
9796		}, opt);
9797
9798		var instant = ($.isArray(items) || typeof items == 'string') ? true : false;
9799
9800		items = $.isArray(items) ? items : new Array();
9801
9802		if (typeof args[0] == 'string') items[0] = args[0];
9803
9804		if (version_compare($.fn.jquery, '1.8', '>=')) {
9805			var iLB = new iLightBox($(this), options, items, instant);
9806			return {
9807				close: function() {
9808					iLB.closeAction();
9809				},
9810				fullscreen: function() {
9811					iLB.fullScreenAction();
9812				},
9813				moveNext: function() {
9814					iLB.moveTo('next');
9815				},
9816				movePrev: function() {
9817					iLB.moveTo('prev');
9818				},
9819				goTo: function(index) {
9820					iLB.goTo(index);
9821				},
9822				refresh: function() {
9823					iLB.refresh();
9824				},
9825				reposition: function() {
9826					(arguments.length > 0) ? iLB.repositionPhoto(true): iLB.repositionPhoto();
9827				},
9828				setOption: function(options) {
9829					iLB.setOption(options);
9830				},
9831				destroy: function() {
9832					iLB.closeAction();
9833					iLB.dispatchItemsEvents();
9834				}
9835			};
9836		} else {
9837			throw "The jQuery version that was loaded is too old. iLightBox requires jQuery 1.8+";
9838		}
9839
9840	};
9841
9842
9843	$.iLightBox = function() {
9844		return $.fn.iLightBox(arguments[0], arguments[1]);
9845	};
9846
9847
9848	$.extend($.easing, {
9849		easeInCirc: function(x, t, b, c, d) {
9850			return -c * (sqrt(1 - (t /= d) * t) - 1) + b;
9851		},
9852		easeOutCirc: function(x, t, b, c, d) {
9853			return c * sqrt(1 - (t = t / d - 1) * t) + b;
9854		},
9855		easeInOutCirc: function(x, t, b, c, d) {
9856			if ((t /= d / 2) < 1) return -c / 2 * (sqrt(1 - t * t) - 1) + b;
9857			return c / 2 * (sqrt(1 - (t -= 2) * t) + 1) + b;
9858		}
9859	});
9860
9861	function getScrollXY() {
9862		var scrOfX = 0,
9863			scrOfY = 0;
9864		if (typeof(window.pageYOffset) == 'number') {
9865			//Netscape compliant
9866			scrOfY = window.pageYOffset;
9867			scrOfX = window.pageXOffset;
9868		} else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) {
9869			//DOM compliant
9870			scrOfY = document.body.scrollTop;
9871			scrOfX = document.body.scrollLeft;
9872		} else if (document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) {
9873			//IE6 standards compliant mode
9874			scrOfY = document.documentElement.scrollTop;
9875			scrOfX = document.documentElement.scrollLeft;
9876		}
9877		return {
9878			x: scrOfX,
9879			y: scrOfY
9880		};
9881	}
9882
9883	(function() {
9884		// add new event shortcuts
9885		$.each(("touchstart touchmove touchend " +
9886			"tap taphold " +
9887			"swipe swipeleft swiperight " +
9888			"scrollstart scrollstop").split(" "), function(i, name) {
9889
9890			$.fn[name] = function(fn) {
9891				return fn ? this.bind(name, fn) : this.trigger(name);
9892			};
9893
9894			// jQuery < 1.8
9895			// if ($.attrFn) {
9896			// 	$.attrFn[name] = true;
9897			// }
9898		});
9899
9900		var tapSettings = {
9901			startEvent: 'touchstart.iTap',
9902			endEvent: 'touchend.iTap'
9903		};
9904
9905		// tap Event:
9906		$.event.special.itap = {
9907			setup: function() {
9908
9909				var self = this,
9910					$self = $(this),
9911					start, stop;
9912
9913				$self.bind(tapSettings.startEvent, function(event) {
9914					start = getScrollXY();
9915
9916					$self.one(tapSettings.endEvent, function(event) {
9917						stop = getScrollXY();
9918
9919						var orgEvent = event || window.event;
9920						event = $.event.fix(orgEvent);
9921						event.type = "itap";
9922
9923						if ((start && stop) && (start.x == stop.x && start.y == stop.y))($.event.dispatch || $.event.handle).call(self, event);
9924
9925						start = stop = undefined;
9926					});
9927				});
9928			},
9929
9930			teardown: function() {
9931				$(this).unbind(tapSettings.startEvent);
9932			}
9933		};
9934	}());
9935
9936
9937	//Fullscreen API
9938	(function() {
9939		fullScreenApi = {
9940				supportsFullScreen: false,
9941				isFullScreen: function() {
9942					return false;
9943				},
9944				requestFullScreen: function() {},
9945				cancelFullScreen: function() {},
9946				fullScreenEventName: '',
9947				prefix: ''
9948			},
9949			browserPrefixes = 'webkit moz o ms khtml'.split(' ');
9950
9951		// check for native support
9952		if (typeof document.cancelFullScreen != 'undefined') {
9953			fullScreenApi.supportsFullScreen = true;
9954		} else {
9955			// check for fullscreen support by vendor prefix
9956			for (var i = 0, il = browserPrefixes.length; i < il; i++) {
9957				fullScreenApi.prefix = browserPrefixes[i];
9958
9959				if (typeof document[fullScreenApi.prefix + 'CancelFullScreen'] != 'undefined') {
9960					fullScreenApi.supportsFullScreen = true;
9961
9962					break;
9963				}
9964			}
9965		}
9966
9967		// update methods to do something useful
9968		if (fullScreenApi.supportsFullScreen) {
9969			fullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange';
9970
9971			fullScreenApi.isFullScreen = function() {
9972				switch (this.prefix) {
9973					case '':
9974						return document.fullScreen;
9975					case 'webkit':
9976						return document.webkitIsFullScreen;
9977					default:
9978						return document[this.prefix + 'FullScreen'];
9979				}
9980			}
9981			fullScreenApi.requestFullScreen = function(el) {
9982				return (this.prefix === '') ? el.requestFullScreen() : el[this.prefix + 'RequestFullScreen']();
9983			}
9984			fullScreenApi.cancelFullScreen = function(el) {
9985				return (this.prefix === '') ? document.cancelFullScreen() : document[this.prefix + 'CancelFullScreen']();
9986			}
9987		}
9988	}());
9989
9990	// Browser detect
9991	(function() {
9992		function uaMatch(ua) {
9993			ua = ua.toLowerCase();
9994
9995			var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
9996				/(webkit)[ \/]([\w.]+)/.exec(ua) ||
9997				/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
9998				/(msie) ([\w.]+)/.exec(ua) ||
9999				ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || [];
10000
10001			return {
10002				browser: match[1] || "",
10003				version: match[2] || "0"
10004			};
10005		}
10006
10007		var matched = uaMatch(navigator.userAgent);
10008		browser = {};
10009
10010		if (matched.browser) {
10011			browser[matched.browser] = true;
10012			browser.version = matched.version;
10013		}
10014
10015		// Chrome is Webkit, but Webkit is also Safari.
10016		if (browser.chrome) {
10017			browser.webkit = true;
10018		} else if (browser.webkit) {
10019			browser.safari = true;
10020		}
10021	}());
10022
10023	// Feature detects
10024	(function() {
10025		var prefixes = ['', 'webkit', 'moz', 'ms', 'o'];
10026		var el = document.createElement('div');
10027
10028		function testProp(prop) {
10029			for (var p = 0, pl = prefixes.length; p < pl; p++) {
10030				var prefixedProp = prefixes[p] ? prefixes[p] + prop.charAt(0).toUpperCase() + prop.slice(1) : prop;
10031				if (el.style[prefixedProp] !== undefined) {
10032					return prefixedProp;
10033				}
10034			}
10035		}
10036
10037		// Global support indicators
10038		transform = testProp('transform') || '';
10039		gpuAcceleration = testProp('perspective') ? 'translateZ(0) ' : '';
10040	}());
10041
10042
10043	/*
10044		PluginDetect v0.7.9
10045		www.pinlady.net/PluginDetect/license/
10046		[ getVersion onWindowLoaded BetterIE ]
10047		[ Flash QuickTime Shockwave ]
10048	*/
10049	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;
10049d++){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);
10049d.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)}
10049c.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;
10049if(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();
10050
10051	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)};
10052
10053
10054	/*
10055		jQuery hashchange event v1.3
10056		https://github.com/cowboy/jquery-hashchange
10057		Copyright (c) 2010 "Cowboy" Ben Alman
10058		Dual licensed under the MIT and GPL licenses.
10059	*/
10060	(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=
10061	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||
10062	"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;
10062a!==d&&(b.title=k.title,b.open(),e&&b.write('<script>document.domain="'+e+'"\x3c/script>'),b.close(),c.location.hash=a)}}();return h}()})();
10063
10064	if (!Array.prototype.filter) {
10065		Array.prototype.filter = function(fun /*, thisp */ ) {
10066			"use strict";
10067
10068			if (this == null)
10069				throw new TypeError();
10070
10071			var t = Object(this);
10072			var len = t.length >>> 0;
10073			if (typeof fun != "function")
10074				throw new TypeError();
10075
10076			var res = [];
10077			var thisp = arguments[1];
10078			for (var i = 0; i < len; i++) {
10079				if (i in t) {
10080					var val = t[i]; // in case fun mutates this
10081					if (fun.call(thisp, val, i, t))
10082						res.push(val);
10083				}
10084			}
10085
10086			return res;
10087		};
10088	}
10089
10090	if (!Array.prototype.indexOf) {
10091		Array.prototype.indexOf = function(searchElement, fromIndex) {
10092			var k;
10093
10094			if (this == null) {
10095				throw new TypeError('"this" is null or not defined');
10096			}
10097
10098			var O = Object(this);
10099
10100			var len = O.length >>> 0;
10101
10102			if (len === 0) {
10103				return -1;
10104			}
10105
10106			var n = +fromIndex || 0;
10107
10108			if (abs(n) === Infinity) {
10109				n = 0;
10110			}
10111
10112			if (n >= len) {
10113				return -1;
10114			}
10115
10116			k = max(n >= 0 ? n : len - abs(n), 0);
10117
10118			while (k < len) {
10119				var kValue;
10120				if (k in O && O[k] === searchElement) {
10121					return k;
10122				}
10123				k++;
10124			}
10125			return -1;
10126		};
10127	}
10128
10129	if (!Array.prototype.lastIndexOf) {
10130		Array.prototype.lastIndexOf = function(searchElement /*, fromIndex*/ ) {
10131			"use strict";
10132
10133			if (this == null)
10134				throw new TypeError();
10135
10136			var t = Object(this);
10137			var len = t.length >>> 0;
10138			if (len === 0)
10139				return -1;
10140
10141			var n = len;
10142			if (arguments.length > 1) {
10143				n = Number(arguments[1]);
10144				if (n != n)
10145					n = 0;
10146				else if (n != 0 && n != (1 / 0) && n != -(1 / 0))
10147					n = (n > 0 || -1) * floor(abs(n));
10148			}
10149
10150			var k = n >= 0 ? min(n, len - 1) : len - abs(n);
10151
10152			for (; k >= 0; k--) {
10153				if (k in t && t[k] === searchElement)
10154					return k;
10155			}
10156			return -1;
10157		};
10158	}
10159})(jQuery, this);
10160
10161/**
10162 * Owl carousel
vendor: 12,045 bytes, lines 10163-10668
10163 * @version 2.0.0-beta.3
10164 * @author Bartosz Wojciechowski
10165 * @license The MIT License (MIT)
10166 * @todo Lazy Load Icon
10167 * @todo prevent animationend bubling
10168 * @todo itemsScaleUp
10169 * @todo Test Zepto
10170 * @todo stagePadding calculate wrong active classes
10171 */
10172;(function($, window, document, undefined) {
10173
10174	/**
10175	 * Creates a carousel.
10176	 * @class The Owl Carousel.
10177	 * @public
10178	 * @param {HTMLElement|jQuery} element - The element to create the carousel for.
10179	 * @param {Object} [options] - The options
10180	 */
10181	function Owl(element, options) {
10182
10183		/**
10184		 * Current settings for the carousel.
10185		 * @public
10186		 */
10187		this.settings = null;
10188
10189		/**
10190		 * Current options set by the caller including defaults.
10191		 * @public
10192		 */
10193		this.options = $.extend({}, Owl.Defaults, options);
10194
10195		/**
10196		 * Plugin element.
10197		 * @public
10198		 */
10199		this.$element = $(element);
10200
10201		/**
10202		 * Proxied event handlers.
10203		 * @protected
10204		 */
10205		this._handlers = {};
10206
10207		/**
10208		 * References to the running plugins of this carousel.
10209		 * @protected
10210		 */
10211		this._plugins = {};
10212
10213		/**
10214		 * Currently suppressed events to prevent them from beeing retriggered.
10215		 * @protected
10216		 */
10217		this._supress = {};
10218
10219		/**
10220		 * Absolute current position.
10221		 * @protected
10222		 */
10223		this._current = null;
10224
10225		/**
10226		 * Animation speed in milliseconds.
10227		 * @protected
10228		 */
10229		this._speed = null;
10230
10231		/**
10232		 * Coordinates of all items in pixel.
10233		 * @todo The name of this member is missleading.
10234		 * @protected
10235		 */
10236		this._coordinates = [];
10237
10238		/**
10239		 * Current breakpoint.
10240		 * @todo Real media queries would be nice.
10241		 * @protected
10242		 */
10243		this._breakpoint = null;
10244
10245		/**
10246		 * Current width of the plugin element.
10247		 */
10248		this._width = null;
10249
10250		/**
10251		 * All real items.
10252		 * @protected
10253		 */
10254		this._items = [];
10255
10256		/**
10257		 * All cloned items.
10258		 * @protected
10259		 */
10260		this._clones = [];
10261
10262		/**
10263		 * Merge values of all items.
10264		 * @todo Maybe this could be part of a plugin.
10265		 * @protected
10266		 */
10267		this._mergers = [];
10268
10269		/**
10270		 * Widths of all items.
10271		 */
10272		this._widths = [];
10273
10274		/**
10275		 * Invalidated parts within the update process.
10276		 * @protected
10277		 */
10278		this._invalidated = {};
10279
10280		/**
10281		 * Ordered list of workers for the update process.
10282		 * @protected
10283		 */
10284		this._pipe = [];
10285
10286		/**
10287		 * Current state information for the drag operation.
10288		 * @todo #261
10289		 * @protected
10290		 */
10291		this._drag = {
10292			time: null,
10293			target: null,
10294			pointer: null,
10295			stage: {
10296				start: null,
10297				current: null
10298			},
10299			direction: null
10300		};
10301
10302		/**
10303		 * Current state information and their tags.
10304		 * @type {Object}
10305		 * @protected
10306		 */
10307		this._states = {
10308			current: {},
10309			tags: {
10310				'initializing': [ 'busy' ],
10311				'animating': [ 'busy' ],
10312				'dragging': [ 'interacting' ]
10313			}
10314		};
10315
10316		$.each([ 'onResize', 'onThrottledResize' ], $.proxy(function(i, handler) {
10317			this._handlers[handler] = $.proxy(this[handler], this);
10318		}, this));
10319
10320		$.each(Owl.Plugins, $.proxy(function(key, plugin) {
10321			this._plugins[key.charAt(0).toLowerCase() + key.slice(1)]
10322				= new plugin(this);
10323		}, this));
10324
10325		$.each(Owl.Workers, $.proxy(function(priority, worker) {
10326			this._pipe.push({
10327				'filter': worker.filter,
10328				'run': $.proxy(worker.run, this)
10329			});
10330		}, this));
10331
10332		this.setup();
10333		this.initialize();
10334	}
10335
10336	/**
10337	 * Default options for the carousel.
10338	 * @public
10339	 */
10340	Owl.Defaults = {
10341		items: 3,
10342		loop: false,
10343		center: false,
10344		rewind: false,
10345
10346		mouseDrag: true,
10347		touchDrag: true,
10348		pullDrag: true,
10349		freeDrag: false,
10350
10351		margin: 0,
10352		stagePadding: 0,
10353
10354		merge: false,
10355		mergeFit: true,
10356		autoWidth: false,
10357
10358		startPosition: 0,
10359		rtl: false,
10360
10361		smartSpeed: 250,
10362		fluidSpeed: false,
10363		dragEndSpeed: false,
10364
10365		responsive: {},
10366		responsiveRefreshRate: 200,
10367		responsiveBaseElement: window,
10368
10369		fallbackEasing: 'swing',
10370
10371		info: false,
10372
10373		nestedItemSelector: false,
10374		itemElement: 'div',
10375		stageElement: 'div',
10376
10377		refreshClass: 'owl-refresh',
10378		loadedClass: 'owl-loaded',
10379		loadingClass: 'owl-loading',
10380		rtlClass: 'owl-rtl',
10381		responsiveClass: 'owl-responsive',
10382		dragClass: 'owl-drag',
10383		itemClass: 'owl-item',
10384		stageClass: 'owl-stage',
10385		stageOuterClass: 'owl-stage-outer',
10386		grabClass: 'owl-grab'
10387	};
10388
10389	/**
10390	 * Enumeration for width.
10391	 * @public
10392	 * @readonly
10393	 * @enum {String}
10394	 */
10395	Owl.Width = {
10396		Default: 'default',
10397		Inner: 'inner',
10398		Outer: 'outer'
10399	};
10400
10401	/**
10402	 * Enumeration for types.
10403	 * @public
10404	 * @readonly
10405	 * @enum {String}
10406	 */
10407	Owl.Type = {
10408		Event: 'event',
10409		State: 'state'
10410	};
10411
10412	/**
10413	 * Contains all registered plugins.
10414	 * @public
10415	 */
10416	Owl.Plugins = {};
10417
10418	/**
10419	 * List of workers involved in the update process.
10420	 */
10421	Owl.Workers = [ {
10422		filter: [ 'width', 'settings' ],
10423		run: function() {
10424			this._width = (this.$element.closest('.px-gutter').length) ? 12 * Math.ceil(this.$element.width() / 12) : this.$element.width();
10425			//this._width = (UNCODE.isMobile) ? this.$element.width() : 12 * Math.ceil(this.$element.width() / 12);
10426		}
10427	}, {
10428		filter: [ 'width', 'items', 'settings' ],
10429		run: function(cache) {
10430			cache.current = this._items && this._items[this.relative(this._current)];
10431		}
10432	}, {
10433		filter: [ 'items', 'settings' ],
10434		run: function() {
10435			this.$stage.children('.cloned').remove();
10436		}
10437	}, {
10438		filter: [ 'width', 'items', 'settings' ],
10439		run: function(cache) {
10440			var margin = this.settings.margin || '',
10441				grid = !this.settings.autoWidth,
10442				rtl = this.settings.rtl,
10443				css = {
10444					'width': 'auto',
10445					'margin-left': rtl ? margin : '',
10446					'margin-right': rtl ? '' : margin
10447				};
10448
10449			!grid && this.$stage.children().css(css);
10450
10451			cache.css = css;
10452		}
10453	}, {
10454		filter: [ 'width', 'items', 'settings' ],
10455		run: function(cache) {
10456			var width = (this.width() / this.settings.items).toFixed(3) - this.settings.margin,
10457				merge = null,
10458				iterator = this._items.length,
10459				grid = !this.settings.autoWidth,
10460				widths = [];
10461
10462			cache.items = {
10463				merge: false,
10464				width: width
10465			};
10466
10467			while (iterator--) {
10468				merge = this._mergers[iterator];
10469				merge = this.settings.mergeFit && Math.min(merge, this.settings.items) || merge;
10470
10471				cache.items.merge = merge > 1 || cache.items.merge;
10472
10473				widths[iterator] = !grid ? this._items[iterator].width() : width * merge;
10474			}
10475
10476			this._widths = widths;
10477		}
10478	}, {
10479		filter: [ 'items', 'settings' ],
10480		run: function() {
10481			var clones = [],
10482				items = this._items,
10483				settings = this.settings,
10484				view = Math.max(settings.items * 2, 4),
10485				size = Math.ceil(items.length / 2) * 2,
10486				repeat = settings.loop && items.length ? settings.rewind ? view : Math.max(view, size) : 0,
10487				append = '',
10488				prepend = '';
10489
10490			repeat /= 2;
10491
10492			while (repeat--) {
10493				clones.push(this.normalize(clones.length / 2, true));
10494				append = append + items[clones[clones.length - 1]][0].outerHTML;
10495				clones.push(this.normalize(items.length - 1 - (clones.length - 1) / 2, true));
10496				prepend = items[clones[clones.length - 1]][0].outerHTML + prepend;
10497			}
10498
10499			this._clones = clones;
10500
10501			// var appendVideo = $(append).find('.uncode-video-container');
10502			// if (appendVideo.length) {
10503			// 	appendVideo.attr('data-id', Math.round(Math.random() * 100000));
10504			// }
10505			// var prependVideo = $(prepend).find('.uncode-video-container');
10506			// if (prependVideo.length) {
10507			// 	prependVideo.attr('data-id', Math.round(Math.random() * 100000));
10508			// }
10509			$(append).addClass('cloned').appendTo(this.$stage);
10510			$(prepend).addClass('cloned').prependTo(this.$stage);
10511		}
10512	}, {
10513		filter: [ 'width', 'items', 'settings' ],
10514		run: function() {
10515			var rtl = this.settings.rtl ? 1 : -1,
10516				size = this._clones.length + this._items.length,
10517				iterator = -1,
10518				previous = 0,
10519				current = 0,
10520				coordinates = [];
10521
10522			while (++iterator < size) {
10523				previous = coordinates[iterator - 1] || 0;
10524				current = this._widths[this.relative(iterator)] + this.settings.margin;
10525				coordinates.push(previous + current * rtl);
10526			}
10527
10528			this._coordinates = coordinates;
10529		}
10530	}, {
10531		filter: [ 'width', 'items', 'settings' ],
10532		run: function() {
10533			var stagePadding = (this._width < 480 && this.settings.stagePadding > 0) ? 41 : (this._width * this.settings.stagePadding) / 200,
10534				padding = stagePadding,
10535				coordinates = this._coordinates,
10536				css = {
10537					'width': Math.ceil(Math.abs(coordinates[coordinates.length - 1])) + padding * 2,
10538					'padding-left': padding || '',
10539					'padding-right': padding || ''
10540				};
10541
10542			this.$stage.css(css);
10543		}
10544	}, {
10545		filter: [ 'width', 'items', 'settings' ],
10546		run: function(cache) {
10547			var iterator = this._coordinates.length,
10548				grid = !this.settings.autoWidth,
10549				items = this.$stage.children();
10550
10551			if (grid && cache.items.merge) {
10552				while (iterator--) {
10553					cache.css.width = this._widths[this.relative(iterator)];
10554					items.eq(iterator).css(cache.css);
10555				}
10556			} else if (grid) {
10557				cache.css.width = cache.items.width;
10558				items.css(cache.css);
10559			}
10560		}
10561	}, {
10562		filter: [ 'items' ],
10563		run: function() {
10564			this._coordinates.length < 1 && this.$stage.removeAttr('style');
10565		}
10566	}, {
10567		filter: [ 'width', 'items', 'settings' ],
10568		run: function(cache) {
10569			cache.current = cache.current ? this.$stage.children().index(cache.current) : 0;
10570			cache.current = Math.max(this.minimum(), Math.min(this.maximum(), cache.current));
10571			this.reset(cache.current);
10572		}
10573	}, {
10574		filter: [ 'position' ],
10575		run: function() {
10576			this.animate(this.coordinates(this._current));
10577		}
10578	}, {
10579		filter: [ 'width', 'position', 'items', 'settings' ],
10580		run: function() {
10581			var stagePadding = (this._width < 480 && this.settings.stagePadding > 0) ? 41 : (this._width * this.settings.stagePadding) / 200,
10582				rtl = this.settings.rtl ? 1 : -1,
10583				padding = this.settings.stagePadding * 2,
10584				begin = this.coordinates(this.current()) + padding,
10585				end = begin + this.width() * rtl,
10586				inner, outer, matches = [], i, n;
10587
10588			for (i = 0, n = this._coordinates.length; i < n; i++) {
10589				inner = this._coordinates[i - 1] || 0;
10590				outer = Math.abs(this._coordinates[i]) + padding * rtl;
10591
10592				if ((this.op(inner, '<=', begin) && (this.op(inner, '>', end)))
10593					|| (this.op(outer, '<', begin) && this.op(outer, '>', end))) {
10594					matches.push(i);
10595				}
10596			}
10597
10598			this.$stage.children('.active').removeClass('active');
10599			this.$stage.children(':eq(' + matches.join('), :eq(') + ')').addClass('active');
10600
10601			if (this.settings.center) {
10602				this.$stage.children('.center').removeClass('center');
10603				this.$stage.children().eq(this.current()).addClass('center');
10604			}
10605		}
10606	} ];
10607
10608	/**
10609	 * Initializes the carousel.
10610	 * @protected
10611	 */
10612	Owl.prototype.initialize = function() {
10613		this.enter('initializing');
10614		this.trigger('initialize');
10615
10616		this.$element.toggleClass(this.settings.rtlClass, this.settings.rtl);
10617
10618		if (this.settings.autoWidth && !this.is('pre-loading')) {
10619			var imgs, nestedSelector, width;
10620			imgs = this.$element.find('img');
10621			nestedSelector = this.settings.nestedItemSelector ? '.' + this.settings.nestedItemSelector : undefined;
10622			width = this.$element.children(nestedSelector).width();
10623
10624			if (imgs.length && width <= 0) {
10625				this.preloadAutoWidthImages(imgs);
10626			}
10627		}
10628
10629		this.$element.addClass(this.options.loadingClass);
10630
10631		// create stage
10632		this.$stage = $('<' + this.settings.stageElement + ' class="' + this.settings.stageClass + '"/>')
10633			.wrap('<div class="' + this.settings.stageOuterClass + '"/>');
10634
10635		// append stage
10636		this.$element.append(this.$stage.parent());
10637
10638		// append content
10639		this.replace(this.$element.children().not(this.$stage.parent()));
10640
10641		// check visibility
10642		if (this.$element.is(':visible')) {
10643			// update view
10644			this.refresh();
10645		} else {
10646			// invalidate width
10647			this.invalidate('width');
10648		}
10649
10650		this.$element
10651			.removeClass(this.options.loadingClass)
10652			.addClass(this.options.loadedClass);
10653
10654		// register event handlers
10655		this.registerEventHandlers();
10656
10657		this.leave('initializing');
10658		this.trigger('initialized');
10659	};
10660
10661	/**
10662	 * Setups the current settings.
10663	 * @todo Remove responsive classes. Why should adaptive designs be brought into IE8?
10664	 * @todo Support for media queries by using `matchMedia` would be nice.
10665	 * @public
10666	 */
10667	Owl.prototype.setup = function() {
10668		var viewport = this.viewport(),
10669			overwrites = this.options.responsive,
10670			match = -1,
10671			settings = null;
10672
10673		if (!overwrites) {
10674			settings = $.extend({}, this.options);
10675		} else {
10676			$.each(overwrites, function(breakpoint) {
10677				if (breakpoint <= viewport && breakpoint > match) {
10678					match = Number(breakpoint);
10679				}
10680			});
10681
10682			settings = $.extend({}, this.options, overwrites[match]);
10683			delete settings.responsive;
10684
10685			// responsive class
10686			if (settings.responsiveClass) {
10687				this.$element.attr('class',
10688					this.$element.attr('class').replace(new RegExp('(' + this.options.responsiveClass + '-)\\S+\\s', 'g'), '$1' + match)
10689				);
10690			}
10691		}
10692
10693		if (this.settings === null || this._breakpoint !== match) {
10694			this.trigger('change', { property: { name: 'settings', value: settings } });
10695			this._breakpoint = match;
10696			this.settings = settings;
10697			this.invalidate('settings');
10698			this.trigger('changed', { property: { name: 'settings', value: this.settings } });
10699		}
10700	};
10701
10702	/**
10703	 * Updates option logic if necessery.
10704	 * @protected
10705	 */
10706	Owl.prototype.optionsLogic = function() {
10707		if (this.settings.autoWidth) {
10708			this.settings.stagePadding = false;
10709			this.settings.merge = false;
10710		}
10711	};
10712
10713	/**
10714	 * Prepares an item before add.
10715	 * @todo Rename event parameter `content` to `item`.
10716	 * @protected
10717	 * @returns {jQuery|HTMLElement} - The item container.
10718	 */
10719	Owl.prototype.prepare = function(item, index) {
10720		var event = this.trigger('prepare', { content: item });
10721		if (!event.data) {
10722			event.data = $('<' + this.settings.itemElement + '/>')
10723				.addClass(this.options.itemClass).attr('data-index', index + 1).append(item)
10724		}
10725
10726		this.trigger('prepared', { content: event.data });
10727
10728		return event.data;
10729	};
10730
10731	/**
10732	 * Updates the view.
10733	 * @public
10734	 */
10735	Owl.prototype.update = function() {
10736		var i = 0,
10737			n = this._pipe.length,
10738			filter = $.proxy(function(p) { return this[p] }, this._invalidated),
10739			cache = {};
10740
10741		while (i < n) {
10742			if (this._invalidated.all || $.grep(this._pipe[i].filter, filter).length > 0) {
10743				this._pipe[i].run(cache);
10744			}
10745			i++;
10746		}
10747
10748		this._invalidated = {};
10749
10750		!this.is('valid') && this.enter('valid');
10751	};
10752
10753	/**
10754	 * Gets the width of the view.
10755	 * @public
10756	 * @param {Owl.Width} [dimension=Owl.Width.Default] - The dimension to return.
10757	 * @returns {Number} - The width of the view in pixel.
10758	 */
10759	Owl.prototype.width = function(dimension) {
10760		dimension = dimension || Owl.Width.Default;
10761		var stagePadding = (this._width < 480 && this.settings.stagePadding > 0) ? 41 : (this._width * this.settings.stagePadding) / 200;
10762		switch (dimension) {
10763			case Owl.Width.Inner:
10764			case Owl.Width.Outer:
10765				return this._width;
10766			default:
10767				return this._width - stagePadding * 2 + this.settings.margin;
10768		}
10769	};
10770
10771	/**
10772	 * Refreshes the carousel primarily for adaptive purposes.
10773	 * @public
10774	 */
10775	Owl.prototype.refresh = function() {
10776		this.enter('refreshing');
10777		this.trigger('refresh');
10778
10779		this.setup();
10780
10781		this.optionsLogic();
10782
10783		this.$element.addClass(this.options.refreshClass);
10784
10785		this.update();
10786
10787		this.$element.removeClass(this.options.refreshClass);
10788
10789		this.leave('refreshing');
10790		this.trigger('refreshed');
10791	};
10792
10793	/**
10794	 * Checks window `resize` event.
10795	 * @protected
10796	 */
10797	Owl.prototype.onThrottledResize = function() {
10798		window.clearTimeout(this.resizeTimer);
10799		this.resizeTimer = window.setTimeout(this._handlers.onResize, this.settings.responsiveRefreshRate);
10800	};
10801
10802	/**
10803	 * Checks window `resize` event.
10804	 * @protected
10805	 */
10806	Owl.prototype.onResize = function() {
10807		if (!this._items.length) {
10808			return false;
10809		}
10810
10811		if (this._width === this.$element.width()) {
10812			return false;
10813		}
10814
10815		if (!this.$element.is(':visible')) {
10816			return false;
10817		}
10818
10819		this.enter('resizing');
10820
10821		if (this.trigger('resize').isDefaultPrevented()) {
10822			this.leave('resizing');
10823			return false;
10824		}
10825
10826		this.invalidate('width');
10827
10828		this.refresh();
10829
10830		this.leave('resizing');
10831		this.trigger('resized');
10832	};
10833
10834	/**
10835	 * Registers event handlers.
10836	 * @todo Check `msPointerEnabled`
10837	 * @todo #261
10838	 * @protected
10839	 */
10840	Owl.prototype.registerEventHandlers = function() {
10841		if ($.support.transition) {
10842			this.$stage.on($.support.transition.end + '.owl.core', $.proxy(this.onTransitionEnd, this));
10843		}
10844
10845		if (this.settings.responsive !== false) {
10846			this.on(window, 'resize', this._handlers.onThrottledResize);
10847		}
10848
10849		if (this.settings.mouseDrag) {
10850			this.$element.addClass(this.options.dragClass);
10851			this.$stage.on('mousedown.owl.core', $.proxy(this.onDragStart, this));
10852			this.$stage.on('dragstart.owl.core selectstart.owl.core', function() { return false });
10853		}
10854
10855		if (this.settings.touchDrag){
10856			this.$stage.on('touchstart.owl.core', $.proxy(this.onDragStart, this));
10857			this.$stage.on('touchcancel.owl.core', $.proxy(this.onDragEnd, this));
10858		}
10859	};
10860
10861	/**
10862	 * Handles `touchstart` and `mousedown` events.
10863	 * @todo Horizontal swipe threshold as option
10864	 * @todo #261
10865	 * @protected
10866	 * @param {Event} event - The event arguments.
10867	 */
10868	Owl.prototype.onDragStart = function(event) {
10869		var stage = null;
10870
10871		if (event.which === 3) {
10872			return;
10873		}
10874
vendor: 9,106 bytes, lines 10875-11190
10875		if ($.support.transform) {
10876			stage = this.$stage.css('transform').replace(/.*\(|\)| /g, '').split(',');
10877			stage = {
10878				x: stage[stage.length === 16 ? 12 : 4],
10879				y: stage[stage.length === 16 ? 13 : 5]
10880			};
10881		} else {
10882			stage = this.$stage.position();
10883			stage = {
10884				x: this.settings.rtl ?
10885					stage.left + this.$stage.width() - this.width() + this.settings.margin :
10886					stage.left,
10887				y: stage.top
10888			};
10889		}
10890
10891		if (this.is('animating')) {
10892			$.support.transform ? this.animate(stage.x) : this.$stage.stop()
10893			this.invalidate('position');
10894		}
10895
10896		this.$element.toggleClass(this.options.grabClass, event.type === 'mousedown');
10897
10898		this.speed(0);
10899
10900		this._drag.time = new Date().getTime();
10901		this._drag.target = $(event.target);
10902		this._drag.stage.start = stage;
10903		this._drag.stage.current = stage;
10904		this._drag.pointer = this.pointer(event);
10905
10906		$(document).on('mouseup.owl.core touchend.owl.core', $.proxy(this.onDragEnd, this));
10907
10908		$(document).one('mousemove.owl.core touchmove.owl.core', $.proxy(function(event) {
10909			var delta = this.difference(this._drag.pointer, this.pointer(event));
10910
10911			$(document).on('mousemove.owl.core touchmove.owl.core', $.proxy(this.onDragMove, this));
10912
10913			if (Math.abs(delta.x) < Math.abs(delta.y) && this.is('valid')) {
10914				return;
10915			}
10916
10917			event.preventDefault();
10918
10919			this.enter('dragging');
10920			this.trigger('drag');
10921		}, this));
10922	};
10923
10924	/**
10925	 * Handles the `touchmove` and `mousemove` events.
10926	 * @todo #261
10927	 * @protected
10928	 * @param {Event} event - The event arguments.
10929	 */
10930	Owl.prototype.onDragMove = function(event) {
10931		var minimum = null,
10932			maximum = null,
10933			pull = null,
10934			delta = this.difference(this._drag.pointer, this.pointer(event)),
10935			stage = this.difference(this._drag.stage.start, delta);
10936
10937		if (!this.is('dragging')) {
10938			return;
10939		}
10940
10941		event.preventDefault();
10942
10943		if (this.settings.loop) {
10944			minimum = this.coordinates(this.minimum());
10945			maximum = this.coordinates(this.maximum() + 1) - minimum;
10946			stage.x = (((stage.x - minimum) % maximum + maximum) % maximum) + minimum;
10947		} else {
10948			minimum = this.settings.rtl ? this.coordinates(this.maximum()) : this.coordinates(this.minimum());
10949			maximum = this.settings.rtl ? this.coordinates(this.minimum()) : this.coordinates(this.maximum());
10950			pull = this.settings.pullDrag ? -1 * delta.x / 5 : 0;
10951			stage.x = Math.max(Math.min(stage.x, minimum + pull), maximum + pull);
10952		}
10953
10954		this._drag.stage.current = stage;
10955
10956		this.animate(stage.x);
10957	};
10958
10959	/**
10960	 * Handles the `touchend` and `mouseup` events.
10961	 * @todo #261
10962	 * @todo Threshold for click event
10963	 * @protected
10964	 * @param {Event} event - The event arguments.
10965	 */
10966	Owl.prototype.onDragEnd = function(event) {
10967		var delta = this.difference(this._drag.pointer, this.pointer(event)),
10968			stage = this._drag.stage.current,
10969			direction = delta.x > 0 ^ this.settings.rtl ? 'left' : 'right';
10970
10971		$(document).off('.owl.core');
10972
10973		this.$element.removeClass(this.options.grabClass);
10974
10975		if (delta.x !== 0 && this.is('dragging') || !this.is('valid')) {
10976			this.speed(this.settings.dragEndSpeed || this.settings.smartSpeed);
10977			this.current(this.closest(stage.x, delta.x !== 0 ? direction : this._drag.direction));
10978			this.invalidate('position');
10979			this.update();
10980
10981			this._drag.direction = direction;
10982
10983			if (Math.abs(delta.x) > 3 || new Date().getTime() - this._drag.time > 300) {
10984				this._drag.target.one('click.owl.core', function() { return false; });
10985			}
10986		}
10987
10988		if (!this.is('dragging')) {
10989			return;
10990		}
10991
10992		this.leave('dragging');
10993		this.trigger('dragged');
10994	};
10995
10996	/**
10997	 * Gets absolute position of the closest item for a coordinate.
10998	 * @todo Setting `freeDrag` makes `closest` not reusable. See #165.
10999	 * @protected
11000	 * @param {Number} coordinate - The coordinate in pixel.
11001	 * @param {String} direction - The direction to check for the closest item. Ether `left` or `right`.
11002	 * @return {Number} - The absolute position of the closest item.
11003	 */
11004	Owl.prototype.closest = function(coordinate, direction) {
11005		var position = -1,
11006			pull = 30,
11007			width = this.width(),
11008			coordinates = this.coordinates();
11009
11010		if (!this.settings.freeDrag) {
11011			// check closest item
11012			$.each(coordinates, $.proxy(function(index, value) {
11013				if (coordinate > value - pull && coordinate < value + pull) {
11014					position = index;
11015				} else if (this.op(coordinate, '<', value)
11016					&& this.op(coordinate, '>', coordinates[index + 1] || value - width)) {
11017					position = direction === 'left' ? index + 1 : index;
11018				}
11019				return position === -1;
11020			}, this));
11021		}
11022
11023		if (!this.settings.loop) {
11024			// non loop boundries
11025			if (this.op(coordinate, '>', coordinates[this.minimum()])) {
11026				position = coordinate = this.minimum();
11027			} else if (this.op(coordinate, '<', coordinates[this.maximum()])) {
11028				position = coordinate = this.maximum();
11029			}
11030		}
11031
11032		return position;
11033	};
11034
11035	/**
11036	 * Animates the stage.
11037	 * @todo #270
11038	 * @public
11039	 * @param {Number} coordinate - The coordinate in pixels.
11040	 */
11041	Owl.prototype.animate = function(coordinate) {
11042		var animate = this.speed() > 0;
11043
11044		this.is('animating') && this.onTransitionEnd();
11045
11046		if (animate) {
11047			this.enter('animating');
11048			this.trigger('translate');
11049		}
11050
11051		if ($.support.transform3d && $.support.transition) {
11052			this.$stage.css({
11053				transform: 'translate3d(' + coordinate + 'px,0px,0px)',
11054				transition: (this.speed() / 1000) + 's'
11055			});
11056		} else if (animate) {
11057			this.$stage.animate({
11058				left: coordinate + 'px'
11059			}, this.speed(), this.settings.fallbackEasing, $.proxy(this.onTransitionEnd, this));
11060		} else {
11061			this.$stage.css({
11062				left: coordinate + 'px'
11063			});
11064		}
11065	};
11066
11067	/**
11068	 * Checks whether the carousel is in a specific state or not.
11069	 * @param {String} state - The state to check.
11070	 * @returns {Boolean} - The flag which indicates if the carousel is busy.
11071	 */
11072	Owl.prototype.is = function(state) {
11073		return this._states.current[state] && this._states.current[state] > 0;
11074	};
11075
11076	/**
11077	 * Sets the absolute position of the current item.
11078	 * @public
11079	 * @param {Number} [position] - The new absolute position or nothing to leave it unchanged.
11080	 * @returns {Number} - The absolute position of the current item.
11081	 */
11082	Owl.prototype.current = function(position) {
11083		if (position === undefined) {
11084			return this._current;
11085		}
11086
11087		if (this._items.length === 0) {
11088			return undefined;
11089		}
11090
11091		position = this.normalize(position);
11092
11093		if (this._current !== position) {
11094			var event = this.trigger('change', { property: { name: 'position', value: position } });
11095
11096			if (event.data !== undefined) {
11097				position = this.normalize(event.data);
11098			}
11099
11100			this._current = position;
11101
11102			this.invalidate('position');
11103
11104			this.trigger('changed', { property: { name: 'position', value: this._current } });
11105		}
11106
11107		return this._current;
11108	};
11109
11110	/**
11111	 * Invalidates the given part of the update routine.
11112	 * @param {String} [part] - The part to invalidate.
11113	 * @returns {Array.<String>} - The invalidated parts.
11114	 */
11115	Owl.prototype.invalidate = function(part) {
11116		if ($.type(part) === 'string') {
11117			this._invalidated[part] = true;
11118			this.is('valid') && this.leave('valid');
11119		}
11120		return $.map(this._invalidated, function(v, i) { return i });
11121	};
11122
11123	/**
11124	 * Resets the absolute position of the current item.
11125	 * @public
11126	 * @param {Number} position - The absolute position of the new item.
11127	 */
11128	Owl.prototype.reset = function(position) {
11129		position = this.normalize(position);
11130
11131		if (position === undefined) {
11132			return;
11133		}
11134
11135		this._speed = 0;
11136		this._current = position;
11137
11138		this.suppress([ 'translate', 'translated' ]);
11139
11140		this.animate(this.coordinates(position));
11141
11142		this.release([ 'translate', 'translated' ]);
11143	};
11144
11145	/**
11146	 * Normalizes an absolute or a relative position of an item.
11147	 * @public
11148	 * @param {Number} position - The absolute or relative position to normalize.
11149	 * @param {Boolean} [relative=false] - Whether the given position is relative or not.
11150	 * @returns {Number} - The normalized position.
11151	 */
11152	Owl.prototype.normalize = function(position, relative) {
11153		var n = this._items.length,
11154			m = relative ? 0 : this._clones.length;
11155
11156		if (!$.isNumeric(position) || n < 1) {
11157			position = undefined;
11158		} else if (position < 0 || position >= n + m) {
11159			position = ((position - m / 2) % n + n) % n + m / 2;
11160		}
11161
11162		return position;
11163	};
11164
11165	/**
11166	 * Converts an absolute position of an item into a relative one.
11167	 * @public
11168	 * @param {Number} position - The absolute position to convert.
11169	 * @returns {Number} - The converted position.
11170	 */
11171	Owl.prototype.relative = function(position) {
11172		position -= this._clones.length / 2;
11173		return this.normalize(position, true);
11174	};
11175
11176	/**
11177	 * Gets the maximum position for the current item.
11178	 * @public
11179	 * @param {Boolean} [relative=false] - Whether to return an absolute position or a relative position.
11180	 * @returns {Number}
11181	 */
11182	Owl.prototype.maximum = function(relative) {
11183		var settings = this.settings,
11184			maximum = this._coordinates.length,
11185			boundary = Math.abs(this._coordinates[maximum - 1]) - this._width,
11186			i = -1, j;
11187
11188		if (settings.loop) {
11189			maximum = this._clones.length / 2 + this._items.length - 1;
11190		} else if (settings.autoWidth || settings.merge) {
11191			// binary search
11192			while (maximum - i > 1) {
11193				Math.abs(this._coordinates[j = maximum + i >> 1]) < boundary
11194					? i = j : maximum = j;
11195			}
11196		} else if (settings.center) {
11197			maximum = this._items.length - 1;
11198		} else {
11199			maximum = this._items.length - settings.items;
11200		}
11201
11202		if (relative) {
11203			maximum -= this._clones.length / 2;
11204		}
11205
11206		return Math.max(maximum, 0);
11207	};
11208
11209	/**
11210	 * Gets the minimum position for the current item.
11211	 * @public
11212	 * @param {Boolean} [relative=false] - Whether to return an absolute position or a relative position.
11213	 * @returns {Number}
11214	 */
11215	Owl.prototype.minimum = function(relative) {
11216		return relative ? 0 : this._clones.length / 2;
11217	};
11218
11219	/**
11220	 * Gets an item at the specified relative position.
11221	 * @public
11222	 * @param {Number} [position] - The relative position of the item.
11223	 * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given.
11224	 */
11225	Owl.prototype.items = function(position) {
11226		if (position === undefined) {
11227			return this._items.slice();
11228		}
11229
11230		position = this.normalize(position, true);
11231		return this._items[position];
11232	};
11233
11234	/**
11235	 * Gets an item at the specified relative position.
11236	 * @public
11237	 * @param {Number} [position] - The relative position of the item.
11238	 * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given.
11239	 */
11240	Owl.prototype.mergers = function(position) {
11241		if (position === undefined) {
11242			return this._mergers.slice();
11243		}
11244
11245		position = this.normalize(position, true);
11246		return this._mergers[position];
11247	};
11248
11249	/**
11250	 * Gets the absolute positions of clones for an item.
11251	 * @public
11252	 * @param {Number} [position] - The relative position of the item.
11253	 * @returns {Array.<Number>} - The absolute positions of clones for the item or all if no position was given.
11254	 */
11255	Owl.prototype.clones = function(position) {
11256		var odd = this._clones.length / 2,
11257			even = odd + this._items.length,
11258			map = function(index) { return index % 2 === 0 ? even + index / 2 : odd - (index + 1) / 2 };
11259
11260		if (position === undefined) {
11261			return $.map(this._clones, function(v, i) { return map(i) });
11262		}
11263
11264		return $.map(this._clones, function(v, i) { return v === position ? map(i) : null });
11265	};
11266
11267	/**
11268	 * Sets the current animation speed.
11269	 * @public
11270	 * @param {Number} [speed] - The animation speed in milliseconds or nothing to leave it unchanged.
11271	 * @returns {Number} - The current animation speed in milliseconds.
11272	 */
11273	Owl.prototype.speed = function(speed) {
11274		if (speed !== undefined) {
11275			this._speed = speed;
11276		}
11277
11278		return this._speed;
11279	};
11280
11281	/**
11282	 * Gets the coordinate of an item.
11283	 * @todo The name of this method is missleanding.
11284	 * @public
11285	 * @param {Number} position - The absolute position of the item within `minimum()` and `maximum()`.
11286	 * @returns {Number|Array.<Number>} - The coordinate of the item in pixel or all coordinates.
11287	 */
11288	Owl.prototype.coordinates = function(position) {
11289		var coordinate = null;
11290
11291		if (position === undefined) {
11292			return $.map(this._coordinates, $.proxy(function(coordinate, index) {
11293				return this.coordinates(index);
11294			}, this));
11295		}
11296
11297		if (this.settings.center) {
11298			coordinate = this._coordinates[position];
11299			coordinate += (this.width() - coordinate + (this._coordinates[position - 1] || 0)) / 2 * (this.settings.rtl ? -1 : 1);
11300		} else {
11301			coordinate = this._coordinates[position - 1] || 0;
11302		}
11303
11304		return coordinate;
11305	};
11306
11307	/**
11308	 * Calculates the speed for a translation.
11309	 * @protected
11310	 * @param {Number} from - The absolute position of the start item.
11311	 * @param {Number} to - The absolute position of the target item.
11312	 * @param {Number} [factor=undefined] - The time factor in milliseconds.
11313	 * @returns {Number} - The time in milliseconds for the translation.
11314	 */
11315	Owl.prototype.duration = function(from, to, factor) {
11316		return Math.min(Math.max(Math.abs(to - from), 1), 6) * Math.abs((factor || this.settings.smartSpeed));
11317	};
11318
11319	/**
11320	 * Slides to the specified item.
11321	 * @public
11322	 * @param {Number} position - The position of the item.
11323	 * @param {Number} [speed] - The time in milliseconds for the transition.
11324	 */
11325	Owl.prototype.to = function(position, speed) {
11326		var current = this.current(),
11327			revert = null,
11328			distance = position - this.relative(current),
11329			direction = (distance > 0) - (distance < 0),
11330			items = this._items.length,
11331			minimum = this.minimum(),
11332			maximum = this.maximum();
11333
11334		if (this.settings.loop) {
11335			if (!this.settings.rewind && Math.abs(distance) > items / 2) {
11336				distance += direction * -1 * items;
11337			}
11338
11339			position = current + distance;
11340			revert = ((position - minimum) % items + items) % items + minimum;
11341
11342			if (revert !== position && revert - distance <= maximum && revert - distance > 0) {
11343				current = revert - distance;
11344				position = revert;
11345				this.reset(current);
11346			}
11347		} else if (this.settings.rewind) {
11348			maximum += 1;
11349			position = (position % maximum + maximum) % maximum;
11350		} else {
11351			position = Math.max(minimum, Math.min(maximum, position));
11352		}
11353
11354		this.speed(this.duration(current, position, speed));
11355		this.current(position);
11356
11357		if (this.$element.is(':visible')) {
11358			this.update();
11359		}
11360	};
11361
11362	/**
11363	 * Slides to the next item.
11364	 * @public
11365	 * @param {Number}
vendor: 12,284 bytes, lines 11365-11785
11365 [speed] - The time in milliseconds for the transition.
11366	 */
11367	Owl.prototype.next = function(speed) {
11368		speed = speed || false;
11369		this.to(this.relative(this.current()) + 1, speed);
11370	};
11371
11372	/**
11373	 * Slides to the previous item.
11374	 * @public
11375	 * @param {Number} [speed] - The time in milliseconds for the transition.
11376	 */
11377	Owl.prototype.prev = function(speed) {
11378		speed = speed || false;
11379		this.to(this.relative(this.current()) - 1, speed);
11380	};
11381
11382	/**
11383	 * Handles the end of an animation.
11384	 * @protected
11385	 * @param {Event} event - The event arguments.
11386	 */
11387	Owl.prototype.onTransitionEnd = function(event) {
11388
11389		// if css2 animation then event object is undefined
11390		if (event !== undefined) {
11391			event.stopPropagation();
11392
11393			// Catch only owl-stage transitionEnd event
11394			if ((event.target || event.srcElement || event.originalTarget) !== this.$stage.get(0)) {
11395				return false;
11396			}
11397		}
11398
11399		this.leave('animating');
11400		this.trigger('translated');
11401	};
11402
11403	/**
11404	 * Gets viewport width.
11405	 * @protected
11406	 * @return {Number} - The width in pixel.
11407	 */
11408	Owl.prototype.viewport = function() {
11409		var width;
11410		if (this.options.responsiveBaseElement !== window) {
11411			width = $(this.options.responsiveBaseElement).width();
11412		} else if (window.innerWidth) {
11413			width = window.innerWidth;
11414		} else if (document.documentElement && document.documentElement.clientWidth) {
11415			width = document.documentElement.clientWidth;
11416		} else {
11417			throw 'Can not detect viewport width.';
11418		}
11419
11420		return width;
11421	};
11422
11423	/**
11424	 * Replaces the current content.
11425	 * @public
11426	 * @param {HTMLElement|jQuery|String} content - The new content.
11427	 */
11428	Owl.prototype.replace = function(content) {
11429		this.$stage.empty();
11430		this._items = [];
11431
11432		if (content) {
11433			content = (content instanceof jQuery) ? content : $(content);
11434		}
11435
11436		if (this.settings.nestedItemSelector) {
11437			content = content.find('.' + this.settings.nestedItemSelector);
11438		}
11439
11440		content.filter(function() {
11441			return this.nodeType === 1;
11442		}).each($.proxy(function(index, item) {
11443			item = this.prepare(item, index);
11444			this.$stage.append(item);
11445			this._items.push(item);
11446			this._mergers.push(item.find('[data-merge]').addBack('[data-merge]').attr('data-merge') * 1 || 1);
11447		}, this));
11448
11449		this.reset($.isNumeric(this.settings.startPosition) ? this.settings.startPosition : 0);
11450
11451		this.invalidate('items');
11452	};
11453
11454	/**
11455	 * Adds an item.
11456	 * @todo Use `item` instead of `content` for the event arguments.
11457	 * @public
11458	 * @param {HTMLElement|jQuery|String} content - The item content to add.
11459	 * @param {Number} [position] - The relative position at which to insert the item otherwise the item will be added to the end.
11460	 */
11461	Owl.prototype.add = function(content, position) {
11462		var current = this.relative(this._current);
11463
11464		position = position === undefined ? this._items.length : this.normalize(position, true);
11465		content = content instanceof jQuery ? content : $(content);
11466
11467		this.trigger('add', { content: content, position: position });
11468
11469		content = this.prepare(content, this._items[current].index());
11470
11471		if (this._items.length === 0 || position === this._items.length) {
11472			this._items.length === 0 && this.$stage.append(content);
11473			this._items.length !== 0 && this._items[position - 1].after(content);
11474			this._items.push(content);
11475			this._mergers.push(content.find('[data-merge]').andSelf('[data-merge]').attr('data-merge') * 1 || 1);
11476		} else {
11477			this._items[position].before(content);
11478			this._items.splice(position, 0, content);
11479			this._mergers.splice(position, 0, content.find('[data-merge]').andSelf('[data-merge]').attr('data-merge') * 1 || 1);
11480		}
11481
11482		this._items[current] && this.reset(this._items[current].index());
11483
11484		this.invalidate('items');
11485
11486		this.trigger('added', { content: content, position: position });
11487	};
11488
11489	/**
11490	 * Removes an item by its position.
11491	 * @todo Use `item` instead of `content` for the event arguments.
11492	 * @public
11493	 * @param {Number} position - The relative position of the item to remove.
11494	 */
11495	Owl.prototype.remove = function(position) {
11496		position = this.normalize(position, true);
11497
11498		if (position === undefined) {
11499			return;
11500		}
11501
11502		this.trigger('remove', { content: this._items[position], position: position });
11503
11504		this._items[position].remove();
11505		this._items.splice(position, 1);
11506		this._mergers.splice(position, 1);
11507
11508		this.invalidate('items');
11509
11510		this.trigger('removed', { content: null, position: position });
11511	};
11512
11513	/**
11514	 * Preloads images with auto width.
11515	 * @todo Replace by a more generic approach
11516	 * @protected
11517	 */
11518	Owl.prototype.preloadAutoWidthImages = function(images) {
11519		images.each($.proxy(function(i, element) {
11520			this.enter('pre-loading');
11521			element = $(element);
11522			$(new Image()).one('load', $.proxy(function(e) {
11523				element.attr('src', e.target.src);
11524				element.css('opacity', 1);
11525				this.leave('pre-loading');
11526				!this.is('pre-loading') && !this.is('initializing') && this.refresh();
11527			}, this)).attr('src', element.attr('src') || element.attr('data-src') || element.attr('data-src-retina'));
11528		}, this));
11529	};
11530
11531	/**
11532	 * Destroys the carousel.
11533	 * @public
11534	 */
11535	Owl.prototype.destroy = function() {
11536
11537		this.$element.off('.owl.core');
11538		this.$stage.off('.owl.core');
11539		$(document).off('.owl.core');
11540
11541		if (this.settings.responsive !== false) {
11542			window.clearTimeout(this.resizeTimer);
11543			this.off(window, 'resize', this._handlers.onThrottledResize);
11544		}
11545
11546		for (var i in this._plugins) {
11547			this._plugins[i].destroy();
11548		}
11549
11550		this.$stage.children('.cloned').remove();
11551
11552		this.$stage.unwrap();
11553		this.$stage.children().contents().unwrap();
11554		this.$stage.children().unwrap();
11555
11556		this.$element
11557			.removeClass(this.options.refreshClass)
11558			.removeClass(this.options.loadingClass)
11559			.removeClass(this.options.loadedClass)
11560			.removeClass(this.options.rtlClass)
11561			.removeClass(this.options.dragClass)
11562			.removeClass(this.options.grabClass)
11563			.attr('class', this.$element.attr('class').replace(new RegExp(this.options.responsiveClass + '-\\S+\\s', 'g'), ''))
11564			.removeData('owl.carousel');
11565	};
11566
11567	/**
11568	 * Operators to calculate right-to-left and left-to-right.
11569	 * @protected
11570	 * @param {Number} [a] - The left side operand.
11571	 * @param {String} [o] - The operator.
11572	 * @param {Number} [b] - The right side operand.
11573	 */
11574	Owl.prototype.op = function(a, o, b) {
11575		var rtl = this.settings.rtl;
11576		switch (o) {
11577			case '<':
11578				return rtl ? a > b : a < b;
11579			case '>':
11580				return rtl ? a < b : a > b;
11581			case '>=':
11582				return rtl ? a <= b : a >= b;
11583			case '<=':
11584				return rtl ? a >= b : a <= b;
11585			default:
11586				break;
11587		}
11588	};
11589
11590	/**
11591	 * Attaches to an internal event.
11592	 * @protected
11593	 * @param {HTMLElement} element - The event source.
11594	 * @param {String} event - The event name.
11595	 * @param {Function} listener - The event handler to attach.
11596	 * @param {Boolean} capture - Wether the event should be handled at the capturing phase or not.
11597	 */
11598	Owl.prototype.on = function(element, event, listener, capture) {
11599		if (element.addEventListener) {
11600			element.addEventListener(event, listener, capture);
11601		} else if (element.attachEvent) {
11602			element.attachEvent('on' + event, listener);
11603		}
11604	};
11605
11606	/**
11607	 * Detaches from an internal event.
11608	 * @protected
11609	 * @param {HTMLElement} element - The event source.
11610	 * @param {String} event - The event name.
11611	 * @param {Function} listener - The attached event handler to detach.
11612	 * @param {Boolean} capture - Wether the attached event handler was registered as a capturing listener or not.
11613	 */
11614	Owl.prototype.off = function(element, event, listener, capture) {
11615		if (element.removeEventListener) {
11616			element.removeEventListener(event, listener, capture);
11617		} else if (element.detachEvent) {
11618			element.detachEvent('on' + event, listener);
11619		}
11620	};
11621
11622	/**
11623	 * Triggers a public event.
11624	 * @todo Remove `status`, `relatedTarget` should be used instead.
11625	 * @protected
11626	 * @param {String} name - The event name.
11627	 * @param {*} [data=null] - The event data.
11628	 * @param {String} [namespace=carousel] - The event namespace.
11629	 * @param {String} [state] - The state which is associated with the event.
11630	 * @param {Boolean} [enter=false] - Indicates if the call enters the specified state or not.
11631	 * @returns {Event} - The event arguments.
11632	 */
11633	Owl.prototype.trigger = function(name, data, namespace, state, enter) {
11634		var status = {
11635			item: { count: this._items.length, index: this.current() }
11636		}, handler = $.camelCase(
11637			$.grep([ 'on', name, namespace ], function(v) { return v })
11638				.join('-').toLowerCase()
11639		), event = $.Event(
11640			[ name, 'owl', namespace || 'carousel' ].join('.').toLowerCase(),
11641			$.extend({ relatedTarget: this }, status, data)
11642		);
11643
11644		if (!this._supress[name]) {
11645			$.each(this._plugins, function(name, plugin) {
11646				if (plugin.onTrigger) {
11647					plugin.onTrigger(event);
11648				}
11649			});
11650
11651			this.register({ type: Owl.Type.Event, name: name });
11652			this.$element.trigger(event);
11653
11654			if (this.settings && typeof this.settings[handler] === 'function') {
11655				this.settings[handler].call(this, event);
11656			}
11657		}
11658
11659		return event;
11660	};
11661
11662	/**
11663	 * Enters a state.
11664	 * @param name - The state name.
11665	 */
11666	Owl.prototype.enter = function(name) {
11667		$.each([ name ].concat(this._states.tags[name] || []), $.proxy(function(i, name) {
11668			if (this._states.current[name] === undefined) {
11669				this._states.current[name] = 0;
11670			}
11671
11672			this._states.current[name]++;
11673		}, this));
11674	};
11675
11676	/**
11677	 * Leaves a state.
11678	 * @param name - The state name.
11679	 */
11680	Owl.prototype.leave = function(name) {
11681		$.each([ name ].concat(this._states.tags[name] || []), $.proxy(function(i, name) {
11682			this._states.current[name]--;
11683		}, this));
11684	};
11685
11686	/**
11687	 * Registers an event or state.
11688	 * @public
11689	 * @param {Object} object - The event or state to register.
11690	 */
11691	Owl.prototype.register = function(object) {
11692		if (object.type === Owl.Type.Event) {
11693			if (!$.event.special[object.name]) {
11694				$.event.special[object.name] = {};
11695			}
11696
11697			if (!$.event.special[object.name].owl) {
11698				var _default = $.event.special[object.name]._default;
11699				$.event.special[object.name]._default = function(e) {
11700					if (_default && _default.apply && (!e.namespace || e.namespace.indexOf('owl') === -1)) {
11701						return _default.apply(this, arguments);
11702					}
11703					return e.namespace && e.namespace.indexOf('owl') > -1;
11704				};
11705				$.event.special[object.name].owl = true;
11706			}
11707		} else if (object.type === Owl.Type.State) {
11708			if (!this._states.tags[object.name]) {
11709				this._states.tags[object.name] = object.tags;
11710			} else {
11711				this._states.tags[object.name] = this._states.tags[object.name].concat(object.tags);
11712			}
11713
11714			this._states.tags[object.name] = $.grep(this._states.tags[object.name], $.proxy(function(tag, i) {
11715				return $.inArray(tag, this._states.tags[object.name]) === i;
11716			}, this));
11717		}
11718	};
11719
11720	/**
11721	 * Suppresses events.
11722	 * @protected
11723	 * @param {Array.<String>} events - The events to suppress.
11724	 */
11725	Owl.prototype.suppress = function(events) {
11726		$.each(events, $.proxy(function(index, event) {
11727			this._supress[event] = true;
11728		}, this));
11729	};
11730
11731	/**
11732	 * Releases suppressed events.
11733	 * @protected
11734	 * @param {Array.<String>} events - The events to release.
11735	 */
11736	Owl.prototype.release = function(events) {
11737		$.each(events, $.proxy(function(index, event) {
11738			delete this._supress[event];
11739		}, this));
11740	};
11741
11742	/**
11743	 * Gets unified pointer coordinates from event.
11744	 * @todo #261
11745	 * @protected
11746	 * @param {Event} - The `mousedown` or `touchstart` event.
11747	 * @returns {Object} - Contains `x` and `y` coordinates of current pointer position.
11748	 */
11749	Owl.prototype.pointer = function(event) {
11750		var result = { x: null, y: null };
11751
11752		event = event.originalEvent || event || window.event;
11753
11754		event = event.touches && event.touches.length ?
11755			event.touches[0] : event.changedTouches && event.changedTouches.length ?
11756				event.changedTouches[0] : event;
11757
11758		if (event.pageX) {
11759			result.x = event.pageX;
11760			result.y = event.pageY;
11761		} else {
11762			result.x = event.clientX;
11763			result.y = event.clientY;
11764		}
11765
11766		return result;
11767	};
11768
11769	/**
11770	 * Gets the difference of two vectors.
11771	 * @todo #261
11772	 * @protected
11773	 * @param {Object} - The first vector.
11774	 * @param {Object} - The second vector.
11775	 * @returns {Object} - The difference.
11776	 */
11777	Owl.prototype.difference = function(first, second) {
11778		return {
11779			x: first.x - second.x,
11780			y: first.y - second.y
11781		};
11782	};
11783
11784	/**
11785	 * The jQuery Plugin for the Owl Carousel
vendor: 32,216 bytes, lines 11786-13068
11786	 * @todo Navigation plugin `next` and `prev`
11787	 * @public
11788	 */
11789	$.fn.owlCarousel = function(option) {
11790		var args = Array.prototype.slice.call(arguments, 1);
11791
11792		return this.each(function() {
11793			var $this = $(this),
11794				data = $this.data('owl.carousel');
11795
11796			if (!data) {
11797				data = new Owl(this, typeof option == 'object' && option);
11798				$this.data('owl.carousel', data);
11799
11800				$.each([
11801					'next', 'prev', 'to', 'destroy', 'refresh', 'replace', 'add', 'remove'
11802				], function(i, event) {
11803					data.register({ type: Owl.Type.Event, name: event });
11804					data.$element.on(event + '.owl.carousel.core', $.proxy(function(e) {
11805						if (e.namespace && e.relatedTarget !== this) {
11806							this.suppress([ event ]);
11807							data[event].apply(this, [].slice.call(arguments, 1));
11808							this.release([ event ]);
11809						}
11810					}, data));
11811				});
11812			}
11813
11814			if (typeof option == 'string' && option.charAt(0) !== '_') {
11815				data[option].apply(data, args);
11816			}
11817		});
11818	};
11819
11820	/**
11821	 * The constructor for the jQuery Plugin
11822	 * @public
11823	 */
11824	$.fn.owlCarousel.Constructor = Owl;
11825
11826})(window.Zepto || window.jQuery, window, document);
11827
11828/**
11829 * AutoRefresh Plugin
11830 * @version 2.0.0-beta.3
11831 * @author Artus Kolanowski
11832 * @license The MIT License (MIT)
11833 */
11834;(function($, window, document, undefined) {
11835
11836	/**
11837	 * Creates the auto refresh plugin.
11838	 * @class The Auto Refresh Plugin
11839	 * @param {Owl} carousel - The Owl Carousel
11840	 */
11841	var AutoRefresh = function(carousel) {
11842		/**
11843		 * Reference to the core.
11844		 * @protected
11845		 * @type {Owl}
11846		 */
11847		this._core = carousel;
11848
11849		/**
11850		 * Refresh interval.
11851		 * @protected
11852		 * @type {number}
11853		 */
11854		this._interval = null;
11855
11856		/**
11857		 * Whether the element is currently visible or not.
11858		 * @protected
11859		 * @type {Boolean}
11860		 */
11861		this._visible = null;
11862
11863		/**
11864		 * All event handlers.
11865		 * @protected
11866		 * @type {Object}
11867		 */
11868		this._handlers = {
11869			'initialized.owl.carousel': $.proxy(function(e) {
11870				if (e.namespace && this._core.settings.autoRefresh) {
11871					this.watch();
11872				}
11873			}, this)
11874		};
11875
11876		// set default options
11877		this._core.options = $.extend({}, AutoRefresh.Defaults, this._core.options);
11878
11879		// register event handlers
11880		this._core.$element.on(this._handlers);
11881	};
11882
11883	/**
11884	 * Default options.
11885	 * @public
11886	 */
11887	AutoRefresh.Defaults = {
11888		autoRefresh: true,
11889		autoRefreshInterval: 500
11890	};
11891
11892	/**
11893	 * Watches the element.
11894	 */
11895	AutoRefresh.prototype.watch = function() {
11896		if (this._interval) {
11897			return;
11898		}
11899
11900		this._visible = this._core.$element.is(':visible');
11901		this._interval = window.setInterval($.proxy(this.refresh, this), this._core.settings.autoRefreshInterval);
11902	};
11903
11904	/**
11905	 * Refreshes the element.
11906	 */
11907	AutoRefresh.prototype.refresh = function() {
11908		if (this._core.$element.is(':visible') === this._visible) {
11909			return;
11910		}
11911
11912		this._visible = !this._visible;
11913
11914		this._core.$element.toggleClass('owl-hidden', !this._visible);
11915
11916		this._visible && (this._core.invalidate('width') && this._core.refresh());
11917	};
11918
11919	/**
11920	 * Destroys the plugin.
11921	 */
11922	AutoRefresh.prototype.destroy = function() {
11923		var handler, property;
11924
11925		window.clearInterval(this._interval);
11926
11927		for (handler in this._handlers) {
11928			this._core.$element.off(handler, this._handlers[handler]);
11929		}
11930		for (property in Object.getOwnPropertyNames(this)) {
11931			typeof this[property] != 'function' && (this[property] = null);
11932		}
11933	};
11934
11935	$.fn.owlCarousel.Constructor.Plugins.AutoRefresh = AutoRefresh;
11936
11937})(window.Zepto || window.jQuery, window, document);
11938
11939/**
11940 * Lazy Plugin
11941 * @version 2.0.0-beta.3
11942 * @author Bartosz Wojciechowski
11943 * @license The MIT License (MIT)
11944 */
11945;(function($, window, document, undefined) {
11946
11947	/**
11948	 * Creates the lazy plugin.
11949	 * @class The Lazy Plugin
11950	 * @param {Owl} carousel - The Owl Carousel
11951	 */
11952	var Lazy = function(carousel) {
11953
11954		/**
11955		 * Reference to the core.
11956		 * @protected
11957		 * @type {Owl}
11958		 */
11959		this._core = carousel;
11960
11961		/**
11962		 * Already loaded items.
11963		 * @protected
11964		 * @type {Array.<jQuery>}
11965		 */
11966		this._loaded = [];
11967
11968		/**
11969		 * Event handlers.
11970		 * @protected
11971		 * @type {Object}
11972		 */
11973		this._handlers = {
11974			'initialized.owl.carousel change.owl.carousel': $.proxy(function(e) {
11975				if (!e.namespace) {
11976					return;
11977				}
11978
11979				if (!this._core.settings || !this._core.settings.lazyLoad) {
11980					return;
11981				}
11982
11983				if ((e.property && e.property.name == 'position') || e.type == 'initialized') {
11984					var settings = this._core.settings,
11985						n = (settings.center && Math.ceil(settings.items / 2) || settings.items),
11986						i = ((settings.center && n * -1) || 0),
11987						position = ((e.property && e.property.value) || this._core.current()) + i,
11988						clones = this._core.clones().length,
11989						load = $.proxy(function(i, v) { this.load(v) }, this);
11990
11991					while (i++ < n) {
11992						this.load(clones / 2 + this._core.relative(position));
11993						clones && $.each(this._core.clones(this._core.relative(position)), load);
11994						position++;
11995					}
11996				}
11997			}, this)
11998		};
11999
12000		// set the default options
12001		this._core.options = $.extend({}, Lazy.Defaults, this._core.options);
12002
12003		// register event handler
12004		this._core.$element.on(this._handlers);
12005	}
12006
12007	/**
12008	 * Default options.
12009	 * @public
12010	 */
12011	Lazy.Defaults = {
12012		lazyLoad: false
12013	}
12014
12015	/**
12016	 * Loads all resources of an item at the specified position.
12017	 * @param {Number} position - The absolute position of the item.
12018	 * @protected
12019	 */
12020	Lazy.prototype.load = function(position) {
12021		var $item = this._core.$stage.children().eq(position),
12022			$elements = $item && $item.find('.owl-lazy');
12023
12024		if (!$elements || $.inArray($item.get(0), this._loaded) > -1) {
12025			return;
12026		}
12027
12028		$elements.each($.proxy(function(index, element) {
12029			var $element = $(element), image,
12030				url = (window.devicePixelRatio > 1 && $element.attr('data-src-retina')) || $element.attr('data-src');
12031
12032			this._core.trigger('load', { element: $element, url: url }, 'lazy');
12033
12034			if ($element.is('img')) {
12035				$element.one('load.owl.lazy', $.proxy(function() {
12036					$element.css('opacity', 1);
12037					this._core.trigger('loaded', { element: $element, url: url }, 'lazy');
12038				}, this)).attr('src', url);
12039			} else {
12040				image = new Image();
12041				image.onload = $.proxy(function() {
12042					$element.css({
12043						'background-image': 'url(' + url + ')',
12044						'opacity': '1'
12045					});
12046					this._core.trigger('loaded', { element: $element, url: url }, 'lazy');
12047				}, this);
12048				image.src = url;
12049			}
12050		}, this));
12051
12052		this._loaded.push($item.get(0));
12053	}
12054
12055	/**
12056	 * Destroys the plugin.
12057	 * @public
12058	 */
12059	Lazy.prototype.destroy = function() {
12060		var handler, property;
12061
12062		for (handler in this.handlers) {
12063			this._core.$element.off(handler, this.handlers[handler]);
12064		}
12065		for (property in Object.getOwnPropertyNames(this)) {
12066			typeof this[property] != 'function' && (this[property] = null);
12067		}
12068	};
12069
12070	$.fn.owlCarousel.Constructor.Plugins.Lazy = Lazy;
12071
12072})(window.Zepto || window.jQuery, window, document);
12073
12074/**
12075 * AutoHeight Plugin
12076 * @version 2.0.0-beta.3
12077 * @author Bartosz Wojciechowski
12078 * @license The MIT License (MIT)
12079 */
12080;(function($, window, document, undefined) {
12081
12082	/**
12083	 * Creates the auto height plugin.
12084	 * @class The Auto Height Plugin
12085	 * @param {Owl} carousel - The Owl Carousel
12086	 */
12087	var AutoHeight = function(carousel) {
12088		/**
12089		 * Reference to the core.
12090		 * @protected
12091		 * @type {Owl}
12092		 */
12093		this._core = carousel;
12094
12095		/**
12096		 * All event handlers.
12097		 * @protected
12098		 * @type {Object}
12099		 */
12100		this._handlers = {
12101			'initialized.owl.carousel refreshed.owl.carousel': $.proxy(function(e) {
12102				if (e.namespace && this._core.settings.autoHeight) {
12103					this.update();
12104				}
12105			}, this),
12106			'changed.owl.carousel': $.proxy(function(e) {
12107				if (e.namespace && this._core.settings.autoHeight && e.property.name == 'position'){
12108					this.update();
12109				}
12110			}, this),
12111			'loaded.owl.lazy': $.proxy(function(e) {
12112				if (e.namespace && this._core.settings.autoHeight
12113					&& e.element.closest('.' + this._core.settings.itemClass).index() === this._core.current()) {
12114					this.update();
12115				}
12116			}, this)
12117		};
12118
12119		// set default options
12120		this._core.options = $.extend({}, AutoHeight.Defaults, this._core.options);
12121
12122		// register event handlers
12123		this._core.$element.on(this._handlers);
12124	};
12125
12126	/**
12127	 * Default options.
12128	 * @public
12129	 */
12130	AutoHeight.Defaults = {
12131		autoHeight: false,
12132		autoHeightClass: 'owl-height'
12133	};
12134
12135	/**
12136	 * Updates the view.
12137	 */
12138	AutoHeight.prototype.update = function() {
12139		var start = this._core._current,
12140			end = start + this._core.settings.items,
12141			visible = this._core.$stage.children().toArray().slice(start, end);
12142			heights = [],
12143			maxheight = 0;
12144
12145		$.each(visible, function(index, item) {
12146			heights.push($(item).height());
12147		});
12148
12149		maxheight = Math.max.apply(null, heights);
12150
12151		this._core.$stage.parent()
12152			.height(maxheight)
12153			.addClass(this._core.settings.autoHeightClass);
12154	};
12155
12156	AutoHeight.prototype.destroy = function() {
12157		var handler, property;
12158
12159		for (handler in this._handlers) {
12160			this._core.$element.off(handler, this._handlers[handler]);
12161		}
12162		for (property in Object.getOwnPropertyNames(this)) {
12163			typeof this[property] != 'function' && (this[property] = null);
12164		}
12165	};
12166
12167	$.fn.owlCarousel.Constructor.Plugins.AutoHeight = AutoHeight;
12168
12169})(window.Zepto || window.jQuery, window, document);
12170
12171/**
12172 * Video Plugin
12173 * @version 2.0.0-beta.3
12174 * @author Bartosz Wojciechowski
12175 * @license The MIT License (MIT)
12176 */
12177;(function($, window, document, undefined) {
12178
12179	/**
12180	 * Creates the video plugin.
12181	 * @class The Video Plugin
12182	 * @param {Owl} carousel - The Owl Carousel
12183	 */
12184	var Video = function(carousel) {
12185		/**
12186		 * Reference to the core.
12187		 * @protected
12188		 * @type {Owl}
12189		 */
12190		this._core = carousel;
12191
12192		/**
12193		 * Cache all video URLs.
12194		 * @protected
12195		 * @type {Object}
12196		 */
12197		this._videos = {};
12198
12199		/**
12200		 * Current playing item.
12201		 * @protected
12202		 * @type {jQuery}
12203		 */
12204		this._playing = null;
12205
12206		/**
12207		 * All event handlers.
12208		 * @todo The cloned content removale is too late
12209		 * @protected
12210		 * @type {Object}
12211		 */
12212		this._handlers = {
12213			'initialized.owl.carousel': $.proxy(function(e) {
12214				if (e.namespace) {
12215					this._core.register({ type: 'state', name: 'playing', tags: [ 'interacting' ] });
12216				}
12217			}, this),
12218			'resize.owl.carousel': $.proxy(function(e) {
12219				if (e.namespace && this._core.settings.video && this.isInFullScreen()) {
12220					e.preventDefault();
12221				}
12222			}, this),
12223			'refreshed.owl.carousel': $.proxy(function(e) {
12224				if (e.namespace && this._core.is('resizing')) {
12225					this._core.$stage.find('.cloned .owl-video-frame').remove();
12226				}
12227			}, this),
12228			'changed.owl.carousel': $.proxy(function(e) {
12229				if (e.namespace && e.property.name === 'position' && this._playing) {
12230					this.stop();
12231				}
12232			}, this),
12233			'prepared.owl.carousel': $.proxy(function(e) {
12234				if (!e.namespace) {
12235					return;
12236				}
12237
12238				var $element = $(e.content).find('.owl-video');
12239
12240				if ($element.length) {
12241					$element.css('display', 'none');
12242					this.fetch($element, $(e.content));
12243				}
12244			}, this)
12245		};
12246
12247		// set default options
12248		this._core.options = $.extend({}, Video.Defaults, this._core.options);
12249
12250		// register event handlers
12251		this._core.$element.on(this._handlers);
12252
12253		this._core.$element.on('click.owl.video', '.owl-video-play-icon', $.proxy(function(e) {
12254			this.play(e);
12255		}, this));
12256	};
12257
12258	/**
12259	 * Default options.
12260	 * @public
12261	 */
12262	Video.Defaults = {
12263		video: false,
12264		videoHeight: false,
12265		videoWidth: false
12266	};
12267
12268	/**
12269	 * Gets the video ID and the type (YouTube/Vimeo only).
12270	 * @protected
12271	 * @param {jQuery} target - The target containing the video data.
12272	 * @param {jQuery} item - The item containing the video.
12273	 */
12274	Video.prototype.fetch = function(target, item) {
12275		var type = target.attr('data-vimeo-id') ? 'vimeo' : 'youtube',
12276			id = target.attr('data-vimeo-id') || target.attr('data-youtube-id'),
12277			width = target.attr('data-width') || this._core.settings.videoWidth,
12278			height = target.attr('data-height') || this._core.settings.videoHeight,
12279			url = target.attr('href');
12280
12281		if (url) {
12282			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+)?/);
12283
12284			if (id[3].indexOf('youtu') > -1) {
12285				type = 'youtube';
12286			} else if (id[3].indexOf('vimeo') > -1) {
12287				type = 'vimeo';
12288			} else {
12289				throw new Error('Video URL not supported.');
12290			}
12291			id = id[6];
12292		} else {
12293			throw new Error('Missing video URL.');
12294		}
12295
12296		this._videos[url] = {
12297			type: type,
12298			id: id,
12299			width: width,
12300			height: height
12301		};
12302
12303		item.attr('data-video', url);
12304
12305		this.thumbnail(target, this._videos[url]);
12306	};
12307
12308	/**
12309	 * Creates video thumbnail.
12310	 * @protected
12311	 * @param {jQuery} target - The target containing the video data.
12312	 * @param {Object} info - The video info object.
12313	 * @see `fetch`
12314	 */
12315	Video.prototype.thumbnail = function(target, video) {
12316		var tnLink,
12317			icon,
12318			path,
12319			dimensions = video.width && video.height ? 'style="width:' + video.width + 'px;height:' + video.height + 'px;"' : '',
12320			customTn = target.find('img'),
12321			srcType = 'src',
12322			lazyClass = '',
12323			settings = this._core.settings,
12324			create = function(path) {
12325				icon = '<div class="owl-video-play-icon"></div>';
12326
12327				if (settings.lazyLoad) {
12328					tnLink = '<div class="owl-video-tn ' + lazyClass + '" ' + srcType + '="' + path + '"></div>';
12329				} else {
12330					tnLink = '<div class="owl-video-tn" style="opacity:1;background-image:url(' + path + ')"></div>';
12331				}
12332				target.after(tnLink);
12333				target.after(icon);
12334			};
12335
12336		// wrap video content into owl-video-wrapper div
12337		target.wrap('<div class="owl-video-wrapper"' + dimensions + '></div>');
12338
12339		if (this._core.settings.lazyLoad) {
12340			srcType = 'data-src';
12341			lazyClass = 'owl-lazy';
12342		}
12343
12344		// custom thumbnail
12345		if (customTn.length) {
12346			create(customTn.attr(srcType));
12347			customTn.remove();
12348			return false;
12349		}
12350
12351		if (video.type === 'youtube') {
12352			path = "//img.youtube.com/vi/" + video.id + "/hqdefault.jpg";
12353			create(path);
12354		} else if (video.type === 'vimeo') {
12355			$.ajax({
12356				type: 'GET',
12357				url: '//vimeo.com/api/v2/video/' + video.id + '.json',
12358				jsonp: 'callback',
12359				dataType: 'jsonp',
12360				success: function(data) {
12361					path = data[0].thumbnail_large;
12362					create(path);
12363				}
12364			});
12365		}
12366	};
12367
12368	/**
12369	 * Stops the current video.
12370	 * @public
12371	 */
12372	Video.prototype.stop = function() {
12373		this._core.trigger('stop', null, 'video');
12374		this._playing.find('.owl-video-frame').remove();
12375		this._playing.removeClass('owl-video-playing');
12376		this._playing = null;
12377		this._core.leave('playing');
12378		this._core.trigger('stopped', null, 'video');
12379	};
12380
12381	/**
12382	 * Starts the current video.
12383	 * @public
12384	 * @param {Event} event - The event arguments.
12385	 */
12386	Video.prototype.play = function(event) {
12387		var target = $(event.target),
12388			item = target.closest('.' + this._core.settings.itemClass),
12389			video = this._videos[item.attr('data-video')],
12390			width = video.width || '100%',
12391			height = video.height || this._core.$stage.height(),
12392			html;
12393
12394		if (this._playing) {
12395			return;
12396		}
12397
12398		this._core.enter('playing');
12399		this._core.trigger('play', null, 'video');
12400
12401		item = this._core.items(this._core.relative(item.index()));
12402
12403		this._core.reset(item.index());
12404
12405		if (video.type === 'youtube') {
12406			html = '<iframe width="' + width + '" height="' + height + '" src="//www.youtube.com/embed/' +
12407				video.id + '?autoplay=1&v=' + video.id + '" frameborder="0" allowfullscreen></iframe>';
12408		} else if (video.type === 'vimeo') {
12409			html = '<iframe src="//player.vimeo.com/video/' + video.id +
12410				'?autoplay=1" width="' + width + '" height="' + height +
12411				'" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>';
12412		}
12413
12414		$('<div class="owl-video-frame">' + html + '</div>').insertAfter(item.find('.owl-video'));
12415
12416		this._playing = item.addClass('owl-video-playing');
12417	};
12418
12419	/**
12420	 * Checks whether an video is currently in full screen mode or not.
12421	 * @todo Bad style because looks like a readonly method but changes members.
12422	 * @protected
12423	 * @returns {Boolean}
12424	 */
12425	Video.prototype.isInFullScreen = function() {
12426		var element = document.fullscreenElement || document.mozFullScreenElement ||
12427				document.webkitFullscreenElement;
12428
12429		return element && $(element).parent().hasClass('owl-video-frame');
12430	};
12431
12432	/**
12433	 * Destroys the plugin.
12434	 */
12435	Video.prototype.destroy = function() {
12436		var handler, property;
12437
12438		this._core.$element.off('click.owl.video');
12439
12440		for (handler in this._handlers) {
12441			this._core.$element.off(handler, this._handlers[handler]);
12442		}
12443		for (property in Object.getOwnPropertyNames(this)) {
12444			typeof this[property] != 'function' && (this[property] = null);
12445		}
12446	};
12447
12448	$.fn.owlCarousel.Constructor.Plugins.Video = Video;
12449
12450})(window.Zepto || window.jQuery, window, document);
12451
12452/**
12453 * Animate Plugin
12454 * @version 2.0.0-beta.3
12455 * @author Bartosz Wojciechowski
12456 * @license The MIT License (MIT)
12457 */
12458;(function($, window, document, undefined) {
12459
12460	/**
12461	 * Creates the animate plugin.
12462	 * @class The Navigation Plugin
12463	 * @param {Owl} scope - The Owl Carousel
12464	 */
12465	var Animate = function(scope) {
12466		this.core = scope;
12467		this.core.options = $.extend({}, Animate.Defaults, this.core.options);
12468		this.swapping = true;
12469		this.previous = undefined;
12470		this.next = undefined;
12471
12472		this.handlers = {
12473			'change.owl.carousel': $.proxy(function(e) {
12474				if (e.namespace && e.property.name == 'position') {
12475					this.previous = this.core.current();
12476					this.next = e.property.value;
12477				}
12478			}, this),
12479			'drag.owl.carousel dragged.owl.carousel translated.owl.carousel': $.proxy(function(e) {
12480				if (e.namespace) {
12481					this.swapping = e.type == 'translated';
12482				}
12483			}, this),
12484			'translate.owl.carousel': $.proxy(function(e) {
12485				if (e.namespace && this.swapping && (this.core.options.animateOut || this.core.options.animateIn)) {
12486					this.swap();
12487				}
12488			}, this)
12489		};
12490
12491		this.core.$element.on(this.handlers);
12492	};
12493
12494	/**
12495	 * Default options.
12496	 * @public
12497	 */
12498	Animate.Defaults = {
12499		animateOut: false,
12500		animateIn: false
12501	};
12502
12503	/**
12504	 * Toggles the animation classes whenever an translations starts.
12505	 * @protected
12506	 * @returns {Boolean|undefined}
12507	 */
12508	Animate.prototype.swap = function() {
12509
12510		if (this.core.settings.items !== 1) {
12511			return;
12512		}
12513
12514		if (!$.support.animation || !$.support.transition) {
12515			return;
12516		}
12517
12518		this.core.speed(0);
12519
12520		var left,
12521			clear = $.proxy(this.clear, this),
12522			previous = this.core.$stage.children().eq(this.previous),
12523			next = this.core.$stage.children().eq(this.next),
12524			incoming = this.core.settings.animateIn,
12525			outgoing = this.core.settings.animateOut;
12526
12527		if (this.core.current() === this.previous) {
12528			return;
12529		}
12530
12531		if (outgoing) {
12532			left = this.core.coordinates(this.previous) - this.core.coordinates(this.next);
12533			previous.css( { 'left': left + 'px' } )
12534				.addClass('animated owl-animated-out')
12535				.addClass(outgoing)
12536				.on($.support.animation.end, clear);
12537		}
12538
12539		if (incoming) {
12540			next.addClass('animated owl-animated-in')
12541				.addClass(incoming)
12542				.on($.support.animation.end, clear);
12543		}
12544	};
12545
12546	Animate.prototype.clear = function(e) {
12547		if ($(e.target).hasClass('animated')) {
12548			$(e.target).css( { 'left': '' } )
12549				.removeClass('animated owl-animated-out owl-animated-in')
12550				.removeClass(this.core.settings.animateIn)
12551				.removeClass(this.core.settings.animateOut);
12552			this.core.onTransitionEnd();
12553		}
12554	};
12555
12556	/**
12557	 * Destroys the plugin.
12558	 * @public
12559	 */
12560	Animate.prototype.destroy = function() {
12561		var handler, property;
12562
12563		for (handler in this.handlers) {
12564			this.core.$element.off(handler, this.handlers[handler]);
12565		}
12566		for (property in Object.getOwnPropertyNames(this)) {
12567			typeof this[property] != 'function' && (this[property] = null);
12568		}
12569	};
12570
12571	$.fn.owlCarousel.Constructor.Plugins.Animate = Animate;
12572
12573})(window.Zepto || window.jQuery, window, document);
12574
12575/**
12576 * Autoplay Plugin
12577 * @version 2.0.0-beta.3
12578 * @author Bartosz Wojciechowski
12579 * @author Artus Kolanowski
12580 * @license The MIT License (MIT)
12581 */
12582;(function($, window, document, undefined) {
12583
12584	/**
12585	 * Creates the autoplay plugin.
12586	 * @class The Autoplay Plugin
12587	 * @param {Owl} scope - The Owl Carousel
12588	 */
12589	var Autoplay = function(carousel) {
12590		/**
12591		 * Reference to the core.
12592		 * @protected
12593		 * @type {Owl}
12594		 */
12595		this._core = carousel;
12596
12597		/**
12598		 * The autoplay interval.
12599		 * @type {Number}
12600		 */
12601		this._interval = null;
12602
12603		/**
12604		 * Indicates whenever the autoplay is paused.
12605		 * @type {Boolean}
12606		 */
12607		this._paused = false;
12608
12609		/**
12610		 * All event handlers.
12611		 * @protected
12612		 * @type {Object}
12613		 */
12614		this._handlers = {
12615			'changed.owl.carousel': $.proxy(function(e) {
12616				if (e.namespace && e.property.name === 'settings') {
12617					if (this._core.settings.autoplay) {
12618						this.play();
12619					} else {
12620						this.stop();
12621					}
12622				}
12623			}, this),
12624			'initialized.owl.carousel': $.proxy(function(e) {
12625				if (e.namespace && this._core.settings.autoplay) {
12626					this.play();
12627				}
12628			}, this),
12629			'play.owl.autoplay': $.proxy(function(e, t, s) {
12630				if (e.namespace) {
12631					this.play(t, s);
12632				}
12633			}, this),
12634			'stop.owl.autoplay': $.proxy(function(e) {
12635				if (e.namespace) {
12636					this.stop();
12637				}
12638			}, this),
12639			'mouseover.owl.autoplay': $.proxy(function() {
12640				if (this._core.settings.autoplayHoverPause && this._core.is('rotating')) {
12641					this.pause();
12642				}
12643			}, this),
12644			'mouseleave.owl.autoplay': $.proxy(function() {
12645				if (this._core.settings.autoplayHoverPause && this._core.is('rotating')) {
12646					this.play();
12647				}
12648			}, this)
12649		};
12650
12651		// register event handlers
12652		this._core.$element.on(this._handlers);
12653
12654		// set default options
12655		this._core.options = $.extend({}, Autoplay.Defaults, this._core.options);
12656	};
12657
12658	/**
12659	 * Default options.
12660	 * @public
12661	 */
12662	Autoplay.Defaults = {
12663		autoplay: false,
12664		autoplayTimeout: 5000,
12665		autoplayHoverPause: false,
12666		autoplaySpeed: false
12667	};
12668
12669	/**
12670	 * Starts the autoplay.
12671	 * @public
12672	 * @param {Number} [timeout] - The interval before the next animation starts.
12673	 * @param {Number} [speed] - The animation speed for the animations.
12674	 */
12675	Autoplay.prototype.play = function(timeout, speed) {
12676		this._paused = false;
12677
12678		if (this._core.is('rotating')) {
12679			return;
12680		}
12681
12682		this._core.enter('rotating');
12683
12684		this._interval = window.setInterval($.proxy(function() {
12685			if (this._paused || this._core.is('busy') || this._core.is('interacting') || document.hidden) {
12686				return;
12687			}
12688			this._core.next(speed || this._core.settings.autoplaySpeed);
12689		}, this), timeout || this._core.settings.autoplayTimeout);
12690	};
12691
12692	/**
12693	 * Stops the autoplay.
12694	 * @public
12695	 */
12696	Autoplay.prototype.stop = function() {
12697		if (!this._core.is('rotating')) {
12698			return;
12699		}
12700
12701		window.clearInterval(this._interval);
12702		this._core.leave('rotating');
12703	};
12704
12705	/**
12706	 * Stops the autoplay.
12707	 * @public
12708	 */
12709	Autoplay.prototype.pause = function() {
12710		if (!this._core.is('rotating')) {
12711			return;
12712		}
12713
12714		this._paused = true;
12715	};
12716
12717	/**
12718	 * Destroys the plugin.
12719	 */
12720	Autoplay.prototype.destroy = function() {
12721		var handler, property;
12722
12723		this.stop();
12724
12725		for (handler in this._handlers) {
12726			this._core.$element.off(handler, this._handlers[handler]);
12727		}
12728		for (property in Object.getOwnPropertyNames(this)) {
12729			typeof this[property] != 'function' && (this[property] = null);
12730		}
12731	};
12732
12733	$.fn.owlCarousel.Constructor.Plugins.autoplay = Autoplay;
12734
12735})(window.Zepto || window.jQuery, window, document);
12736
12737/**
12738 * Navigation Plugin
12739 * @version 2.0.0-beta.3
12740 * @author Artus Kolanowski
12741 * @license The MIT License (MIT)
12742 */
12743;(function($, window, document, undefined) {
12744	'use strict';
12745
12746	/**
12747	 * Creates the navigation plugin.
12748	 * @class The Navigation Plugin
12749	 * @param {Owl} carousel - The Owl Carousel.
12750	 */
12751	var Navigation = function(carousel) {
12752		/**
12753		 * Reference to the core.
12754		 * @protected
12755		 * @type {Owl}
12756		 */
12757		this._core = carousel;
12758
12759		/**
12760		 * Indicates whether the plugin is initialized or not.
12761		 * @protected
12762		 * @type {Boolean}
12763		 */
12764		this._initialized = false;
12765
12766		/**
12767		 * The current paging indexes.
12768		 * @protected
12769		 * @type {Array}
12770		 */
12771		this._pages = [];
12772
12773		/**
12774		 * All DOM elements of the user interface.
12775		 * @protected
12776		 * @type {Object}
12777		 */
12778		this._controls = {};
12779
12780		/**
12781		 * Markup for an indicator.
12782		 * @protected
12783		 * @type {Array.<String>}
12784		 */
12785		this._templates = [];
12786
12787		/**
12788		 * The carousel element.
12789		 * @type {jQuery}
12790		 */
12791		this.$element = this._core.$element;
12792
12793		/**
12794		 * Overridden methods of the carousel.
12795		 * @protected
12796		 * @type {Object}
12797		 */
12798		this._overrides = {
12799			next: this._core.next,
12800			prev: this._core.prev,
12801			to: this._core.to
12802		};
12803
12804		/**
12805		 * All event handlers.
12806		 * @protected
12807		 * @type {Object}
12808		 */
12809		this._handlers = {
12810			'prepared.owl.carousel': $.proxy(function(e) {
12811				if (e.namespace && this._core.settings.dotsData) {
12812					this._templates.push('<div class="' + this._core.settings.dotClass + '">' +
12813						$(e.content).find('[data-dot]').andSelf('[data-dot]').attr('data-dot') + '</div>');
12814				}
12815			}, this),
12816			'added.owl.carousel': $.proxy(function(e) {
12817				if (e.namespace && this._core.settings.dotsData) {
12818					this._templates.splice(e.position, 0, this._templates.pop());
12819				}
12820			}, this),
12821			'remove.owl.carousel': $.proxy(function(e) {
12822				if (e.namespace && this._core.settings.dotsData) {
12823					this._templates.splice(e.position, 1);
12824				}
12825			}, this),
12826			'changed.owl.carousel': $.proxy(function(e) {
12827				if (e.namespace && e.property.name == 'position') {
12828					this.draw();
12829				}
12830			}, this),
12831			'initialized.owl.carousel': $.proxy(function(e) {
12832				if (e.namespace && !this._initialized) {
12833					this._core.trigger('initialize', null, 'navigation');
12834					this.initialize();
12835					this.update();
12836					this.draw();
12837					this._initialized = true;
12838					this._core.trigger('initialized', null, 'navigation');
12839				}
12840			}, this),
12841			'refreshed.owl.carousel': $.proxy(function(e) {
12842				if (e.namespace && this._initialized) {
12843					this._core.trigger('refresh', null, 'navigation');
12844					this.update();
12845					this.draw();
12846					this._core.trigger('refreshed', null, 'navigation');
12847				}
12848			}, this)
12849		};
12850
12851		// set default options
12852		this._core.options = $.extend({}, Navigation.Defaults, this._core.options);
12853
12854		// register event handlers
12855		this.$element.on(this._handlers);
12856	};
12857
12858	/**
12859	 * Default options.
12860	 * @public
12861	 * @todo Rename `slideBy` to `navBy`
12862	 */
12863	Navigation.Defaults = {
12864		nav: false,
12865		navText: [ 'prev', 'next' ],
12866		navSpeed: false,
12867		navElement: 'div',
12868		navContainer: false,
12869		navContainerClass: 'owl-nav',
12870		navClass: [ 'owl-prev', 'owl-next' ],
12871		slideBy: 1,
12872		dotClass: 'owl-dot',
12873		dotsClass: 'owl-dots',
12874		dots: true,
12875		dotsEach: false,
12876		dotsData: false,
12877		dotsSpeed: false,
12878		dotsContainer: false
12879	};
12880
12881	/**
12882	 * Initializes the layout of the plugin and extends the carousel.
12883	 * @protected
12884	 */
12885	Navigation.prototype.initialize = function() {
12886		var override,
12887			settings = this._core.settings;
12888
12889		// create DOM structure for relative navigation
12890		this._controls.$relative = (settings.navContainer ? $(settings.navContainer)
12891			: $('<div>').addClass(settings.navContainerClass).appendTo(this.$element)).addClass('disabled');
12892
12893		this._controls.$previous = $('<' + settings.navElement + '>')
12894			.addClass(settings.navClass[0])
12895			.html(settings.navText[0])
12896			.prependTo(this._controls.$relative)
12897			.on('click', $.proxy(function(e) {
12898				this.prev(settings.navSpeed);
12899			}, this));
12900		this._controls.$next = $('<' + settings.navElement + '>')
12901			.addClass(settings.navClass[1])
12902			.html(settings.navText[1])
12903			.appendTo(this._controls.$relative)
12904			.on('click', $.proxy(function(e) {
12905				this.next(settings.navSpeed);
12906			}, this));
12907
12908		// create DOM structure for absolute navigation
12909		if (!settings.dotsData) {
12910			this._templates = [ $('<div>')
12911				.addClass(settings.dotClass)
12912				.append($('<span>'))
12913				.prop('outerHTML') ];
12914		}
12915
12916		this._controls.$absolute = (settings.dotsContainer ? $(settings.dotsContainer)
12917			: $('<div>').addClass(settings.dotsClass).appendTo(this.$element)).addClass('disabled');
12918
12919		this._controls.$absolute.on('click', 'div', $.proxy(function(e) {
12920			var index = $(e.target).parent().is(this._controls.$absolute)
12921				? $(e.target).index() : $(e.target).parent().index();
12922
12923			e.preventDefault();
12924
12925			this.to(index, settings.dotsSpeed);
12926		}, this));
12927
12928		// override public methods of the carousel
12929		for (override in this._overrides) {
12930			this._core[override] = $.proxy(this[override], this);
12931		}
12932	};
12933
12934	/**
12935	 * Destroys the plugin.
12936	 * @protected
12937	 */
12938	Navigation.prototype.destroy = function() {
12939		var handler, control, property, override;
12940
12941		for (handler in this._handlers) {
12942			this.$element.off(handler, this._handlers[handler]);
12943		}
12944		for (control in this._controls) {
12945			this._controls[control].remove();
12946		}
12947		for (override in this.overides) {
12948			this._core[override] = this._overrides[override];
12949		}
12950		for (property in Object.getOwnPropertyNames(this)) {
12951			typeof this[property] != 'function' && (this[property] = null);
12952		}
12953	};
12954
12955	/**
12956	 * Updates the internal state.
12957	 * @protected
12958	 */
12959	Navigation.prototype.update = function() {
12960		var i, j, k,
12961			lower = this._core.clones().length / 2,
12962			upper = lower + this._core.items().length,
12963			maximum = this._core.maximum(true),
12964			settings = this._core.settings,
12965			size = settings.center || settings.autoWidth || settings.dotsData
12966				? 1 : settings.dotsEach || settings.items;
12967
12968		if (settings.slideBy !== 'page') {
12969			settings.slideBy = Math.min(settings.slideBy, settings.items);
12970		}
12971
12972		if (settings.dots || settings.slideBy == 'page') {
12973			this._pages = [];
12974
12975			for (i = lower, j = 0, k = 0; i < upper; i++) {
12976				if (j >= size || j === 0) {
12977					this._pages.push({
12978						start: Math.min(maximum, i - lower),
12979						end: i - lower + size - 1
12980					});
12981					if (Math.min(maximum, i - lower) === maximum) {
12982						break;
12983					}
12984					j = 0, ++k;
12985				}
12986				j += this._core.mergers(this._core.relative(i));
12987			}
12988		}
12989	};
12990
12991	/**
12992	 * Draws the user interface.
12993	 * @todo The option `dotsData` wont work.
12994	 * @protected
12995	 */
12996	Navigation.prototype.draw = function() {
12997		var difference,
12998			settings = this._core.settings,
12999			disabled = this._core.items().length <= settings.items,
13000			index = this._core.relative(this._core.current()),
13001			loop = settings.loop || settings.rewind;
13002
13003		this._controls.$relative.toggleClass('disabled', !settings.nav || disabled);
13004
13005		if (settings.nav) {
13006			this._controls.$previous.toggleClass('disabled', !loop && index <= this._core.minimum(true));
13007			this._controls.$next.toggleClass('disabled', !loop && index >= this._core.maximum(true));
13008		}
13009
13010		this._controls.$absolute.toggleClass('disabled', !settings.dots || disabled);
13011
13012		if (settings.dots) {
13013			difference = this._pages.length - this._controls.$absolute.children().length;
13014
13015			if (settings.dotsData && difference !== 0) {
13016				this._controls.$absolute.html(this._templates.join(''));
13017			} else if (difference > 0) {
13018				this._controls.$absolute.append(new Array(difference + 1).join(this._templates[0]));
13019			} else if (difference < 0) {
13020				this._controls.$absolute.children().slice(difference).remove();
13021			}
13022
13023			this._controls.$absolute.find('.active').removeClass('active');
13024			this._controls.$absolute.children().eq($.inArray(this.current(), this._pages)).addClass('active');
13025		}
13026	};
13027
13028	/**
13029	 * Extends event data.
13030	 * @protected
13031	 * @param {Event} event - The event object which gets thrown.
13032	 */
13033	Navigation.prototype.onTrigger = function(event) {
13034		var settings = this._core.settings;
13035
13036		event.page = {
13037			index: $.inArray(this.current(), this._pages),
13038			count: this._pages.length,
13039			size: settings && (settings.center || settings.autoWidth || settings.dotsData
13040				? 1 : settings.dotsEach || settings.items)
13041		};
13042	};
13043
13044	/**
13045	 * Gets the current page position of the carousel.
13046	 * @protected
13047	 * @returns {Number}
13048	 */
13049	Navigation.prototype.current = function() {
13050		var current = this._core.relative(this._core.current());
13051		return $.grep(this._pages, $.proxy(function(page, index) {
13052			return page.start <= current && page.end >= current;
13053		}, this)).pop();
13054	};
13055
13056	/**
13057	 * Gets the current succesor/predecessor position.
13058	 * @protected
13059	 * @returns {Number}
13060	 */
13061	Navigation.prototype.getPosition = function(successor) {
13062		var position, length,
13063			settings = this._core.settings;
13064
13065		if (settings.slideBy == 'page') {
13066			position = $.inArray(this.current(), this._pages);
13067			length = this._pages.length;
13068			successor ? ++position : --position;
vendor: 5,678 bytes, lines 13069-13293
13069			position = this._pages[((position % length) + length) % length].start;
13070		} else {
13071			position = this._core.relative(this._core.current());
13072			length = this._core.items().length;
13073			successor ? position += settings.slideBy : position -= settings.slideBy;
13074		}
13075
13076		return position;
13077	};
13078
13079	/**
13080	 * Slides to the next item or page.
13081	 * @public
13082	 * @param {Number} [speed=false] - The time in milliseconds for the transition.
13083	 */
13084	Navigation.prototype.next = function(speed) {
13085		$.proxy(this._overrides.to, this._core)(this.getPosition(true), speed);
13086	};
13087
13088	/**
13089	 * Slides to the previous item or page.
13090	 * @public
13091	 * @param {Number} [speed=false] - The time in milliseconds for the transition.
13092	 */
13093	Navigation.prototype.prev = function(speed) {
13094		$.proxy(this._overrides.to, this._core)(this.getPosition(false), speed);
13095	};
13096
13097	/**
13098	 * Slides to the specified item or page.
13099	 * @public
13100	 * @param {Number} position - The position of the item or page.
13101	 * @param {Number} [speed] - The time in milliseconds for the transition.
13102	 * @param {Boolean} [standard=false] - Whether to use the standard behaviour or not.
13103	 */
13104	Navigation.prototype.to = function(position, speed, standard) {
13105		var length;
13106
13107		if (!standard) {
13108			length = this._pages.length;
13109			$.proxy(this._overrides.to, this._core)(this._pages[((position % length) + length) % length].start, speed);
13110		} else {
13111			$.proxy(this._overrides.to, this._core)(position, speed);
13112		}
13113	};
13114
13115	$.fn.owlCarousel.Constructor.Plugins.Navigation = Navigation;
13116
13117})(window.Zepto || window.jQuery, window, document);
13118
13119/**
13120 * Hash Plugin
13121 * @version 2.0.0-beta.3
13122 * @author Artus Kolanowski
13123 * @license The MIT License (MIT)
13124 */
13125;(function($, window, document, undefined) {
13126	'use strict';
13127
13128	/**
13129	 * Creates the hash plugin.
13130	 * @class The Hash Plugin
13131	 * @param {Owl} carousel - The Owl Carousel
13132	 */
13133	var Hash = function(carousel) {
13134		/**
13135		 * Reference to the core.
13136		 * @protected
13137		 * @type {Owl}
13138		 */
13139		this._core = carousel;
13140
13141		/**
13142		 * Hash index for the items.
13143		 * @protected
13144		 * @type {Object}
13145		 */
13146		this._hashes = {};
13147
13148		/**
13149		 * The carousel element.
13150		 * @type {jQuery}
13151		 */
13152		this.$element = this._core.$element;
13153
13154		/**
13155		 * All event handlers.
13156		 * @protected
13157		 * @type {Object}
13158		 */
13159		this._handlers = {
13160			/*'initialized.owl.carousel': $.proxy(function(e) {
13161				if (e.namespace && this._core.settings.startPosition === 'URLHash') {
13162					$(window).trigger('hashchange.owl.navigation');
13163				}
13164			}, this),*/
13165			'prepared.owl.carousel': $.proxy(function(e) {
13166				if (e.namespace) {
13167					var hash = $(e.content).find('[data-hash]').addBack('[data-hash]').attr('data-hash');
13168
13169					if (!hash) {
13170						return;
13171					}
13172
13173					this._hashes[hash] = e.content;
13174				}
13175			}, this),
13176			'changed.owl.carousel': $.proxy(function(e) {
13177				if (e.namespace && e.property.name === 'position') {
13178					var current = this._core.items(this._core.relative(this._core.current())),
13179						hash = $.map(this._hashes, function(item, hash) {
13180							return item === current ? hash : null;
13181						}).join();
13182
13183					if (!hash || window.location.hash.slice(1) === hash) {
13184						return;
13185					}
13186
13187					window.location.hash = hash;
13188				}
13189			}, this)
13190		};
13191
13192		// set default options
13193		this._core.options = $.extend({}, Hash.Defaults, this._core.options);
13194
13195		// register the event handlers
13196		this.$element.on(this._handlers);
13197
13198		// register event listener for hash navigation
13199		/*$(window).on('hashchange.owl.navigation', $.proxy(function(e) {
13200			var hash = window.location.hash.substring(1),
13201				items = this._core.$stage.children(),
13202				position = this._hashes[hash] && items.index(this._hashes[hash]);
13203
13204			if (position === undefined || position === this._core.current()) {
13205				return;
13206			}
13207
13208			this._core.to(this._core.relative(position), false, true);
13209		}, this));*/
13210	};
13211
13212	/**
13213	 * Default options.
13214	 * @public
13215	 */
13216	Hash.Defaults = {
13217		URLhashListener: false
13218	};
13219
13220	/**
13221	 * Destroys the plugin.
13222	 * @public
13223	 */
13224	Hash.prototype.destroy = function() {
13225		var handler, property;
13226
13227		$(window).off('hashchange.owl.navigation');
13228
13229		for (handler in this._handlers) {
13230			this._core.$element.off(handler, this._handlers[handler]);
13231		}
13232		for (property in Object.getOwnPropertyNames(this)) {
13233			typeof this[property] != 'function' && (this[property] = null);
13234		}
13235	};
13236
13237	$.fn.owlCarousel.Constructor.Plugins.Hash = Hash;
13238
13239})(window.Zepto || window.jQuery, window, document);
13240
13241/**
13242 * Support Plugin
13243 *
13244 * @version 2.0.0-beta.3
13245 * @author Vivid Planet Software GmbH
13246 * @author Artus Kolanowski
13247 * @license The MIT License (MIT)
13248 */
13249;(function($, window, document, undefined) {
13250
13251	var style = $('<support>').get(0).style,
13252		prefixes = 'Webkit Moz O ms'.split(' '),
13253		events = {
13254			transition: {
13255				end: {
13256					WebkitTransition: 'webkitTransitionEnd',
13257					MozTransition: 'transitionend',
13258					OTransition: 'oTransitionEnd',
13259					transition: 'transitionend'
13260				}
13261			},
13262			animation: {
13263				end: {
13264					WebkitAnimation: 'webkitAnimationEnd',
13265					MozAnimation: 'animationend',
13266					OAnimation: 'oAnimationEnd',
13267					animation: 'animationend'
13268				}
13269			}
13270		},
13271		tests = {
13272			csstransforms: function() {
13273				return !!test('transform');
13274			},
13275			csstransforms3d: function() {
13276				return !!test('perspective');
13277			},
13278			csstransitions: function() {
13279				return !!test('transition');
13280			},
13281			cssanimations: function() {
13282				return !!test('animation');
13283			}
13284		};
13285
13286	function test(property, prefixed) {
13287		var result = false,
13288			upper = property.charAt(0).toUpperCase() + property.slice(1);
13289
13290		$.each((property + ' ' + prefixes.join(upper + ' ') + upper).split(' '), function(i, property) {
13291			if (style[property] !== undefined) {
13292				result = prefixed ? property : true;
13293				return false;
13294			}
13295		});
13296
13297		return result;
13298	}
13299
13300	function prefixed(property) {
13301		return test(property, true);
13302	}
13303
13304	if (tests.csstransitions()) {
13305		/* jshint -W053 */
13306		$.support.transition = new String(prefixed('transition'))
13307		$.support.transition.end = events.transition.end[ $.support.transition ];
13308	}
13309
13310	if (tests.cssanimations()) {
13311		/* jshint -W053 */
13312		$.support.animation = new String(prefixed('animation'))
13313		$.support.animation.end = events.animation.end[ $.support.animation ];
13314	}
13315
13316	if (tests.csstransforms()) {
13317		/* jshint -W053 */
13318		$.support.transform = new String(prefixed('transform'));
13319		$.support.transform3d = tests.csstransforms3d();
13320	}
13321
13322})(window.Zepto || window.jQuery, window, document);
13323
13324(function(window, document, undefined) {
13325
13326    /**
13327     * Find the absolute position of an element
13328     */
13329    var absPos = function(element) {
13330        var offsetLeft, offsetTop;
13331        offsetLeft = offsetTop = 0;
13332        if (element.offsetParent) {
13333            do {
13334                offsetLeft += element.offsetLeft;
13335                offsetTop += element.offsetTop;
13336            } while (element = element.offsetParent);
13337        }
13338        return [offsetLeft, offsetTop];
13339    };
13340
13341    /**
13342     * @constructor Progress Circle class
13343     * @param params.canvas Canvas on which the circles will be drawn.
13344     * @param params.minRadius Inner radius of the innermost circle, in px.
13345     * @param params.arcWidth Width of each circle(to be more accurate, ring).
13346     * @param params.gapWidth Space between each circle.
13347     * @param params.centerX X coordinate of the center of circles.
13348     * @param params.centerY Y coordinate of the center of circles.
13349     * @param params.infoLineBaseAngle Base angle of the info line.
13350     * @param params.infoLineAngleInterval Angles between the info lines.
13351     */
13352    var ProgressCircle = function(params) {
13353        this.canvas = params.canvas;
13354        this.minRadius = params.minRadius || 15;
13355        this.arcWidth = params.arcWidth || 5;
13356        this.gapWidth = params.gapWidth || 3;
13357        this.centerX = params.centerX || this.canvas.width / 2;
13358        this.centerY = params.centerY || this.canvas.height / 2;
13359        this.infoLineLength = params.infoLineLength || 60;
13360        this.horizLineLength = params.horizLineLength || 10;
13361        this.infoLineAngleInterval = params.infoLineAngleInterval || Math.PI / 8;
13362        this.infoLineBaseAngle = params.infoLineBaseAngle || Math.PI / 6;
13363
13364        this.context = this.canvas.getContext('2d');
13365
13366        this.width = this.canvas.width;
13367        this.height = this.canvas.height;
13368
13369        this.circles = [];
13370        this.runningCount = 0;
13371    };
13372
13373    ProgressCircle.prototype = {
13374        constructor: ProgressCircle,
13375
13376        /**
13377         * @method Adds an progress monitor entry.
13378         * @param params.fillColor Color to fill in the circle.
13379         * @param params.outlineColor Color to outline the circle.
13380         * @param params.progressListener Callback function to fetch the progress.
13381         * @param params.infoListener Callback function to fetch the info.
13382         * @returns this
13383         */
13384        addEntry: function(params) {
13385            this.circles.push(new Circle({
13386                canvas: this.canvas,
13387                context: this.context,
13388                centerX: this.centerX,
13389                centerY: this.centerY,
13390                innerRadius: this.minRadius + this.circles.length *
13391                    (this.gapWidth + this.arcWidth),
13392                arcWidth: this.arcWidth,
13393                infoLineLength: this.infoLineLength,
13394                horizLineLength: this.horizLineLength,
13395
13396                id: this.circles.length,
13397                fillColor: params.fillColor,
13398                outlineColor: params.outlineColor,
13399                progressListener: params.progressListener,
13400                infoListener: params.infoListener,
13401                infoLineAngle: this.infoLineBaseAngle +
13402                    this.circles.length * this.infoLineAngleInterval,
13403            }));
13404
13405            return this;
13406        },
13407
13408        /**
13409         * @method Starts the monitor and updates with the given interval.
13410         * @param interval Interval between updates, in millisecond.
13411         * @returns this
13412         */
13413        start: function(interval) {
13414            var self = this;
13415            this.timer = setInterval(function() {
13416                self._update();
13417            }, interval || 33);
13418
13419            return this;
13420        },
13421
13422        /**
13423         * @method Stop the animation.
13424         */
13425        stop: function() {
13426            clearTimeout(this.timer);
13427        },
13428
13429        /**
13430         * @private
13431         * @method Call update on each circle and redraw them.
13432         * @returns this
13433         */
13434        _update: function() {
13435            this._clear();
13436            this.circles.forEach(function(circle, idx, array) {
13437                circle.update();
13438            });
13439
13440            return this;
13441        },
13442
13443        /**
13444         * @private
13445         * @method Clear the canvas.
13446         * @returns this
13447         */
13448        _clear: function() {
13449            this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
13450
13451            return this;
13452        },
13453
13454    };
13455
13456    /**
13457     * @private
13458     * @class Individual progress circle.
13459     * @param params.canvas Canvas on which the circle will be drawn.
13460     * @param params.context Context of the canvas.
13461     * @param params.innerRadius Inner radius of the circle, in px.
13462     * @param params.arcWidth Width of each arc(circle).
13463     * @param params.gapWidth Distance between each arc.
13464     * @param params.centerX X coordinate of the center of circles.
13465     * @param params.centerY Y coordinate of the center of circles.
13466     * @param params.fillColor Color to fill in the circle.
13467     * @param params.outlineColor Color to outline the circle.
13468     * @param params.progressListener Callback function to fetch the progress.
13469     * @param params.infoListener Callback function to fetch the info.
13470     * @param params.infoLineAngle Angle of info line.
13471     */
13472    var Circle = function(params) {
13473        this.id = params.id;
13474        this.canvas = params.canvas;
13475        this.context = params.context;
13476        this.centerX = params.centerX;
13477        this.centerY = params.centerY;
13478        this.arcWidth = params.arcWidth;
13479        this.innerRadius = params.innerRadius || 0;
13480        this.fillColor = params.fillColor || '#fff';
13481        this.outlineColor = params.outlineColor || this.fillColor;
13482        this.progressListener = params.progressListener;
13483        this.infoLineLength = params.infoLineLength || 250;
13484        this.horizLineLength = params.horizLineLength || 50;
13485        this.infoListener = params.infoListener;
13486        this.infoLineAngle = params.infoLineAngle;
13487
13488        this.outerRadius = this.innerRadius + this.arcWidth;
13489
13490        // If the info listener is not registered, then don't calculate
13491        // the related coordinates
13492        if (!this.infoListener) return;
13493
13494        // calculate the info-line turning points
13495        var angle = this.infoLineAngle,
13496            arcDistance = (this.innerRadius + this.outerRadius) / 2,
13497
13498            sinA = Math.sin(angle),
13499            cosA = Math.cos(angle);
13500
13501        this.infoLineStartX = this.centerX + sinA * arcDistance;
13502        this.infoLineStartY = this.centerY - cosA * arcDistance;
13503
13504        this.infoLineMidX = this.centerX + sinA * this.infoLineLength;
13505        this.infoLineMidY = this.centerY - cosA * this.infoLineLength;
13506
13507        this.infoLineEndX = this.infoLineMidX +
13508             (sinA < 0 ? -this.horizLineLength : this.horizLineLength);
13509        this.infoLineEndY = this.infoLineMidY;
13510
13511        var infoText = document.createElement('div'),
13512            style = infoText.style;
13513
13514        style.color = this.fillColor;
13515        style.position = 'absolute';
13516        style.left = this.infoLineEndX + absPos(this.canvas)[0] + 'px';
13517        // style.top will be calculated in the `drawInfo` method. Since
13518        // user may want to change the size of the font, so the top offset
13519        // must be updated in each loop.
13520        infoText.className = 'ProgressCircleInfo'; // For css styling
13521        infoText.id = 'progress_circle_info_' + this.id;
13522        document.body.appendChild(infoText);
13523        this.infoText = infoText;
13524    };
13525
13526    Circle.prototype = {
13527        constructor: Circle,
13528
13529        update: function() {
13530            this.progress = this.progressListener();
13531            this._draw();
13532
13533            if (this.infoListener) {
13534                this.info = this.infoListener();
13535                this._drawInfo();
13536            }
13537        },
13538
13539        /**
13540         * @private
13541         * @method Draw the circle on the canvas.
13542         * @returns this
13543         */
13544        _draw: function() {
13545            var ctx = this.context,
13546
13547                ANGLE_OFFSET = -Math.PI / 2,
13548
13549                startAngle = 0 + ANGLE_OFFSET,
13550                endAngle= startAngle + this.progress * Math.PI * 2,
13551
13552                x = this.centerX,
13553                y = this.centerY,
13554
13555                innerRadius = this.innerRadius - this.arcWidth - 1,
13556                outerRadius = this.outerRadius - this.arcWidth - 1;
13557
13558            ctx.fillStyle = this.fillColor;
13559            ctx.strokeStyle = this.outlineColor;
13560
13561            ctx.beginPath();
13562            ctx.arc(x, y, innerRadius, startAngle, endAngle, false);
13563            ctx.arc(x, y, outerRadius, endAngle, startAngle, true);
13564            ctx.closePath();
13565            ctx.stroke();
13566            ctx.fill();
13567
13568            return this;
13569        },
13570
13571        /**
13572         * @private
13573         * @method Draw the info lines and info text.
13574         * @returns this
13575         */
13576        _drawInfo: function() {
13577
13578            var pointList, lineHeight;
13579
13580            pointList = [
13581                [this.infoLineStartX, this.infoLineStartY],
13582                [this.infoLineMidX, this.infoLineMidY],
13583                [this.infoLineEndX, this.infoLineEndY],
13584            ];
13585            this._drawSegments(pointList, false);
13586
13587            this.infoText.innerHTML = this.info;
13588
13589            lineHeight = this.infoText.offsetHeight;
13590            this.infoText.style.top = this.infoLineEndY +
13591                absPos(this.canvas)[1] - lineHeight / 2 + 'px';
13592
13593            return this;
13594        },
13595
13596        /**
13597         * @private
13598         * @method Helper function to draw the segments
13599         * @param pointList An array of points in the form of [x, y].
13600         * @param close Whether to connect the first and last point.
13601         */
13602        _drawSegments: function(pointList, close) {
13603            var ctx = this.context;
13604
13605            ctx.beginPath();
13606            ctx.moveTo(pointList[0][0], pointList[0][1]);
13607            for (var i = 1; i < pointList.length; ++i) {
13608                ctx.lineTo(pointList[i][0], pointList[i][1]);
13609            }
13610
13611            if (close) {
13612                ctx.closePath();
13613            }
13614            ctx.stroke();
13615        },
13616    };
13617
13618    window.ProgressCircle = ProgressCircle;
13619
13620})(window, document);
13621
13622
13623
13624
13625/* =========================================================
13626 * jquery.vc_chart.js v1.0
13627 * =========================================================
13628 * Copyright 2013 Wpbakery
13629 *
13630 * Jquery chart plugin for the Visual Composer.
13631 * ========================================================= */
13632(function($){
13633    /**
13634     * Pie chart animated.
13635     * @param element - DOM element
13636     * @param options - settings object.
13637     * @constructor
13638     */
13639    var VcChart = function(element, options) {
13640        this.el = element;
13641        this.$el = $(this.el);
13642        this.options = $.extend({
13643            color: 'wpb_button',
13644            units: '',
13645            width: '',
13646            label_selector: '.vc_pie_chart_value',
13647            back_selector: '.vc_pie_chart_back',
13648            responsive: true
13649        }, options);
13650        this.init();
13651    };
13652    VcChart.prototype = {
13653        constructor: VcChart,
13654        _progress_v: 0,
13655        animated: false,
13656        colors: {
13657            'wpb_button': 'rgba(247, 247, 247, 1)',
13658            'btn-primary': 'rgba(0, 136, 204, 1)',
13659            'btn-info': 'rgba(88, 185, 218, 1)',
13660            'btn-success': 'rgba(106, 177, 101, 1)',
13661            'btn-warning': 'rgba(255, 153, 0, 1)',
13662            'btn-danger': 'rgba(255, 103, 91, 1)',
13663            'btn-inverse': 'rgba(85, 85, 85, 1)'
13664        },
13665        init: function() {
13666            this.setupColor();
13667            this.value = this.$el.data('pie-value')/100;
13668            this.label_value = this.$el.data('pie-label-value') || this.$el.data('pie-value');
13669            this.$wrapper = $('.vc_pie_wrapper', this.$el);
13670            this.$label = $(this.options.label_selector, this.$el);
13671            this.$back = $(this.options.back_selector, this.$el);
13672            this.$canvas = this.$el.find('canvas');
13673            this.arcWidth = this.$el.data('pie-width') * 2;
13674            this.draw();
13675            this.setWayPoint();
13676            if(this.options.responsive === true) this.setResponsive();
13677            if (UNCODE.isMobile) this._progress_v = this.value;
13678        },
13679        setupColor: function() {
13680            // if(typeof this.colors[this.options.color] !== 'undefined') {
13681            //     this.color = this.colors[this.options.color];
13682            // } else if(typeof this.options.color === 'string' && this.options.color.match(/^rgba?\([^\)]+\)/)) {
13683            //     this.color = this.options.color;
13684            // } else {
13685            //     this.color = 'rgba(247, 247, 247, 0.2)';
13686            // }
13687            if(typeof this.options.color !== 'undefined') {
13688                this.color = this.options.color;
13689            } else this.color = 'rgba(247, 247, 247, 0.2)';
13690
13691        },
13692        setResponsive: function() {
13693            var that = this;
13694            if (!UNCODE.isMobile) {
13695                $(window).resize(function(){
13696                    if(that.animated === true) that.circle.stop();
13697                    that.draw(true);
13698                });
13699            }
13700        },
13701          draw: function(redraw) {
13702            var w = this.$el.addClass('vc_ready').width() * 2,
13703                border_w = this.arcWidth,
13704                radius;
13705            if(!w) w = this.$el.parents(':visible').first().width()-2;
13706            //w = Math.round(w/100*80);
13707            //if (w < 250) w = 250;
13708            radius = w/2;
13709            this.$wrapper.css({"width" : (w / 2) + "px"});
13710            this.$label.css({"width" : (w / 2), "height" : (w / 2), "line-height" : (w / 2)+"px"});
13711            this.$back.css({"width" : (w / 2), "height" : (w / 2)});
13712            this.$canvas.attr({"width" : w + "px", "height" : w + "px"});
13713            this.$el.addClass('vc_ready');
13714            this.circle = new ProgressCircle({
13715                canvas: this.$canvas.get(0),
13716                minRadius: radius,
13717                arcWidth: border_w
13718            });
13719            if(redraw === true && this.animated === true) {
13720                this._progress_v = this.value;
13721                this.circle.addEntry({
13722                    fillColor: this.color,
13723                    progressListener: $.proxy(this.setProgress, this)
13724                }).start();
13725            }
13726        },
13727        setProgress: function() {
13728            if (this._progress_v >= this.value) {
13729                this.circle.stop();
13730                this.$label.html(this.label_value + this.options.units);
13731                return this._progress_v;
13732            }
13733            this._progress_v += 0.01;
13734            if (!isNaN(this.label_value)) {
13735                var label_value = this._progress_v/this.value*this.label_value;
13736                var val = Math.round(label_value) + this.options.units;
13737                this.$label.html(val);
13738            } else this.$label.html(this.label_value + this.options.units);
13739            return this._progress_v;
13740        },
13741        animate: function() {
13742            if(this.animated !== true) {
13743                this.animated = true;
13744                this.circle.addEntry({
13745                    fillColor: this.color,
13746                    progressListener: $.proxy(this.setProgress, this)
13747                }).start(5);
13748            }
13749        },
13750        setWayPoint: function() {
13751            if (typeof $.fn.waypoint !== 'undefined' && !UNCODE.isMobile) {
13752                this.$el.waypoint($.proxy(this.animate, this), { offset: '85%' });
13753            } else {
13754                this.animate();
13755            }
13756        }
13757    };
13758    /**
13759     * jQuery plugin
13760     * @param option - object with settings
13761     * @return {*}
13762     */
13763    $.fn.vcChat = function(option, value) {
13764        return this.each(function () {
13765            var $this = $(this),
13766                data = $this.data('vc_chart'),
13767                options = typeof option === 'object' ? option : {
13768                    color: $this.data('pie-color'),
13769                    units: $this.data('pie-units')
13770                };
13771            if (typeof option == 'undefined') $this.data('vc_chart', (data = new VcChart(this, options)));
13772            if (typeof option == 'string') data[option](value);
13773        });
13774    };
13775    /**
13776     * Allows users to rewrite function inside theme.
13777     */
13778    if ( typeof window['vc_pieChart'] !== 'function' ) {
13779        window.vc_pieChart = function() {
13780            $('.vc_pie_chart:visible').vcChat();
13781        }
13782    }
13783    $(document).ready(function(){
13784        !window.vc_iframe && vc_pieChart();
13785    });
13786
13787})(window.jQuery);
13788
13789
13790/* Progress bar
13791 ---------------------------------------------------------- */
13792function uncode_progress_bar() {    
13793	jQuery.each(jQuery('.vc_progress_bar'), function(index, val) {
13794		if (!UNCODE.isMobile) {
13795			new Waypoint({
13796				element: val,
13797				handler: function() {
13798					var element = jQuery(this.element);
13799					element.find('.vc_single_bar').each(function(index) {          
13800						var $this = jQuery(this),
13801							            bar = $this.find('.vc_bar'),
13802							            val = bar.data('percentage-value');          
13803						setTimeout(function() {            
13804							bar.css({
13805								"width": val + '%'
13806							});          
13807						}, index * 200);        
13808					});
13809				},
13810				offset: '80%'
13811			});
13812		} else {
13813			var element = jQuery(val);
13814			element.find('.vc_single_bar').each(function(index) {          
13815				var $this = jQuery(this),
13816					            bar = $this.find('.vc_bar'),
13817					            val = bar.data('percentage-value');          
13818				setTimeout(function() {            
13819					bar.css({
13820						"width": val + '%'
13821					});          
13822				}, index * 200);        
13823			});
13824		}
13825	});
13826};
13827uncode_progress_bar();
13828
13829/*!
13830 * jquery.counterup.js 1.0
13831 *
13832 * Copyright 2013, Benjamin Intal http://gambit.ph @bfintal
13833 * Released under the GPL v2 License
13834 *
13835 * Date: Nov 26, 2013
13836 */
13837(function($) {
13838	"use strict";
13839	$.fn.counterUp = function(options) {
13840		// Defaults
13841		var settings = $.extend({
13842			'time': 400,
13843			'delay': 10
13844		}, options);
13845		return this.each(function() {
13846			// Store the object
13847			var $this = $(this);
13848			var $settings = settings;
13849			var counterUpper = function() {
13850				var nums = [];
13851				var divisions = $settings.time / $settings.delay;
13852				var numReal = $this.text(),
13853				num = numReal;
13854				var isComma = /[0-9]+,[0-9]+/.test(num);
13855				num = num.replace(/,/g, '');
13856				var isInt = /^[0-9]+$/.test(num);
13857				var isFloat = /^[0-9]+\.[0-9]+$/.test(num);
13858				var decimalPlaces = isFloat ? (num.split('.')[1] || []).length : 0;
13859				// Generate list of incremental numbers to display
13860				for (var i = divisions; i >= 1; i--) {
13861					// Preserve as int if input was int
13862					var newNum = parseInt(num / divisions * i);
13863					// Preserve float if input was float
13864					if (isFloat) {
13865						newNum = parseFloat(num / divisions * i).toFixed(decimalPlaces);
13866					}
13867					// Preserve commas if input had commas
13868					if (isComma) {
13869						while (/(\d+)(\d{3})/.test(newNum.toString())) {
13870							newNum = newNum.toString().replace(/(\d+)(\d{3})/, '$1' + ',' + '$2');
13871						}
13872					}
13873					nums.unshift(newNum);
13874				}
13875				nums.push(numReal);
13876				$this.data('counterup-nums', nums);
13877				$this.text('0');
13878				// Updates the number until we're done
13879				var f = function() {
13880					if ($this.data('counterup-nums') != null) {
13881						$this.text($this.data('counterup-nums').shift());
13882						if ($this.data('counterup-nums').length) {
13883							setTimeout($this.data('counterup-func'), $settings.delay);
13884						} else {
13885							delete $this.data('counterup-nums');
13886							$this.data('counterup-nums', null);
13887							$this.data('counterup-func', null);
13888						}
13889					}
13890				};
13891				$this.data('counterup-func', f);
13892				// Start the count up
13893				setTimeout($this.data('counterup-func'), $settings.delay);
13894			};
13895			// Perform counts when the element gets into view
13896			new Waypoint({
13897				element: this,
13898				handler: function() {
13899					counterUpper();
13900					this.destroy();
13901				},
13902				offset: '80%'
13903			});
13904		});
13905	};
13906})(jQuery);
13907
13908/*!
13909 * The Final Countdown for jQuery v2.0.4 (http://hilios.github.io/jQuery.countdown/)
13910 * Copyright (c) 2014 Edson Hilios
13911 * 
13912 * Permission is hereby granted, free of charge, to any person obtaining a copy of
13913 * this software and associated documentation files (the "Software"), to deal in
13914 * the Software without restriction, including without limitation the rights to
13915 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
13916 * the Software, and to permit persons to whom the Software is furnished to do so,
13917 * subject to the following conditions:
13918 * 
13919 * The above copyright notice and this permission notice shall be included in all
13920 * copies or substantial portions of the Software.
13921 * 
13922 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
13923 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
13924 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
13925 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
13926 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
13927 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
13928 */
13929!function(a){"use strict";"function"==typeof define&&define.amd?define(["jquery"],a):a(jQuery)}(function(a){"use strict";function b(a){if(a instanceof Date)return a;
13929if(String(a).match(h))return String(a).match(/^[0-9]*$/)&&(a=Number(a)),String(a).match(/\-/)&&(a=String(a).replace(/\-/g,"/")),new Date(a);throw new Error("Couldn't cast `"+a+"` to a date object.")}function c(a){var b=a.toString().replace(/([.?*+^$[\]\\(){}|-])/g,"\\$1");return new RegExp(b)}function d(a){return function(b){var d=b.match(/%(-|!)?[A-Z]{1}(:[^;]+;)?/gi);if(d)for(var f=0,g=d.length;g>f;++f){var h=d[f].match(/%(-|!)?([a-zA-Z]{1})(:[^;]+;)?/),j=c(h[0]),k=h[1]||"",l=h[3]||"",m=null;h=h[2],i.hasOwnProperty(h)&&(m=i[h],m=Number(a[m])),null!==m&&("!"===k&&(m=e(l,m)),""===k&&10>m&&(m="0"+m.toString()),b=b.replace(j,m.toString()))}return b=b.replace(/%%/,"%")}}function e(a,b){var c="s",d="";return a&&(a=a.replace(/(:|;|\s)/gi,"").split(/\,/),1===a.length?c=a[0]:(d=a[0],c=a[1])),1===Math.abs(b)?d:c}var f=100,g=[],h=[];h.push(/^[0-9]*$/.source),h.push(/([0-9]{1,2}\/){2}[0-9]{4}( [0-9]{1,2}(:[0-9]{2}){2})?/.source),h.push(/[0-9]{4}([\/\-][0-9]{1,2}){2}( [0-9]{1,2}(:[0-9]{2}){2})?/.source),h=new RegExp(h.join("|"));var i={Y:"years",m:"months",w:"weeks",d:"days",D:"totalDays",H:"hours",M:"minutes",S:"seconds"},j=function(b,c,d){this.el=b,this.$el=a(b),this.interval=null,this.offset={},this.instanceNumber=g.length,g.push(this),this.$el.data("countdown-instance",this.instanceNumber),d&&(this.$el.on("update.countdown",d),this.$el.on("stoped.countdown",d),this.$el.on("finish.countdown",d)),this.setFinalDate(c),this.start()};a.extend(j.prototype,{start:function(){null!==this.interval&&clearInterval(this.interval);var a=this;this.update(),this.interval=setInterval(function(){a.update.call(a)},f)},stop:function(){clearInterval(this.interval),this.interval=null,this.dispatchEvent("stoped")},toggle:function(){this.interval?this.stop():this.start()},pause:function(){this.stop()},resume:function(){this.start()},remove:function(){this.stop.call(this),g[this.instanceNumber]=null,delete this.$el.data().countdownInstance},setFinalDate:function(a){this.finalDate=b(a)},update:function(){return 0===this.$el.closest("html").length?void this.remove():(this.totalSecsLeft=this.finalDate.getTime()-(new Date).getTime(),this.totalSecsLeft=Math.ceil(this.totalSecsLeft/1e3),this.totalSecsLeft=this.totalSecsLeft<0?0:this.totalSecsLeft,this.offset={seconds:this.totalSecsLeft%60,minutes:Math.floor(this.totalSecsLeft/60)%60,hours:Math.floor(this.totalSecsLeft/60/60)%24,days:Math.floor(this.totalSecsLeft/60/60/24)%7,totalDays:Math.floor(this.totalSecsLeft/60/60/24),weeks:Math.floor(this.totalSecsLeft/60/60/24/7),months:Math.floor(this.totalSecsLeft/60/60/24/30),years:Math.floor(this.totalSecsLeft/60/60/24/365)},void(0===this.totalSecsLeft?(this.stop(),this.dispatchEvent("finish")):this.dispatchEvent("update")))},dispatchEvent:function(b){var c=a.Event(b+".countdown");c.finalDate=this.finalDate,c.offset=a.extend({},this.offset),c.strftime=d(this.offset),this.$el.trigger(c)}}),a.fn.countdown=function(){var b=Array.prototype.slice.call(arguments,0);return this.each(function(){var c=a(this).data("countdown-instance");if(void 0!==c){var d=g[c],e=b[0];j.prototype.hasOwnProperty(e)?d[e].apply(d,b.slice(1)):null===String(e).match(/^[$A-Z_][0-9A-Z_$]*$/i)?(d.setFinalDate.call(d,e),d.start()):a.error("Method %s does not exist on jQuery.countdown".replace(/\%s/gi,e))}else new j(this,b[0],b[1])})}});
13930
13931/*! waitForImages jQuery Plugin - v2.0.2 - 2015-05-05
13932* https://github.com/alexanderdickson/waitForImages
13933* Copyright (c) 2015 Alex Dickson; Licensed MIT */
13934;(function (factory) {
13935    if (typeof define === 'function' && define.amd) {
13936        // AMD. Register as an anonymous module.
13937        define(['jquery'], factory);
13938    } else if (typeof exports === 'object') {
13939        // CommonJS / nodejs module
13940        module.exports = factory(require('jquery'));
13941    } else {
13942        // Browser globals
13943        factory(jQuery);
13944    }
13945}(function ($) {
13946    // Namespace all events.
13947    var eventNamespace = 'waitForImages';
13948
13949    // CSS properties which contain references to images.
13950    $.waitForImages = {
13951        hasImageProperties: [
13952            'backgroundImage',
13953            'listStyleImage',
13954            'borderImage',
13955            'borderCornerImage',
13956            'cursor'
13957        ],
13958        hasImageAttributes: ['srcset']
13959    };
13960
13961    // Custom selector to find `img` elements that have a valid `src`
13962    // attribute and have not already loaded.
13963    $.expr[':'].uncached = function (obj) {
13964        // Ensure we are dealing with an `img` element with a valid
13965        // `src` attribute.
13966        if (!$(obj).is('img[src][src!=""]')) {
13967            return false;
13968        }
13969
13970        return !obj.complete;
13971    };
13972
13973    $.fn.waitForImages = function () {
13974
13975        var allImgsLength = 0;
13976        var allImgsLoaded = 0;
13977        var deferred = $.Deferred();
13978
13979        var finishedCallback;
13980        var eachCallback;
13981        var waitForAll;
13982
13983        // Handle options object (if passed).
13984        if ($.isPlainObject(arguments[0])) {
13985
13986            waitForAll = arguments[0].waitForAll;
13987            eachCallback = arguments[0].each;
13988            finishedCallback = arguments[0].finished;
13989
13990        } else {
13991
13992            // Handle if using deferred object and only one param was passed in.
13993            if (arguments.length === 1 && $.type(arguments[0]) === 'boolean') {
13994                waitForAll = arguments[0];
13995            } else {
13996                finishedCallback = arguments[0];
13997                eachCallback = arguments[1];
13998                waitForAll = arguments[2];
13999            }
14000
14001        }
14002
14003        // Handle missing callbacks.
14004        finishedCallback = finishedCallback || $.noop;
14005        eachCallback = eachCallback || $.noop;
14006
14007        // Convert waitForAll to Boolean
14008        waitForAll = !! waitForAll;
14009
14010        // Ensure callbacks are functions.
14011        if (!$.isFunction(finishedCallback) || !$.isFunction(eachCallback)) {
14012            throw new TypeError('An invalid callback was supplied.');
14013        }
14014
14015        this.each(function () {
14016            // Build a list of all imgs, dependent on what images will
14017            // be considered.
14018            var obj = $(this);
14019            var allImgs = [];
14020            // CSS properties which may contain an image.
14021            var hasImgProperties = $.waitForImages.hasImageProperties || [];
14022            // Element attributes which may contain an image.
14023            var hasImageAttributes = $.waitForImages.hasImageAttributes || [];
14024            // To match `url()` references.
14025            // Spec: http://www.w3.org/TR/CSS2/syndata.html#value-def-uri
14026            var matchUrl = /url\(\s*(['"]?)(.*?)\1\s*\)/g;
14027
14028            if (waitForAll) {
14029
14030                // Get all elements (including the original), as any one of
14031                // them could have a background image.
14032                obj.find('*').addBack().each(function () {
14033                    var element = $(this);
14034
14035                    // If an `img` element, add it. But keep iterating in
14036                    // case it has a background image too.
14037                    if (element.is('img:uncached')) {
14038                        allImgs.push({
14039                            src: element.attr('src'),
14040                            element: element[0]
14041                        });
14042                    }
14043
14044                    $.each(hasImgProperties, function (i, property) {
14045                        var propertyValue = element.css(property);
14046                        var match;
14047
14048                        // If it doesn't contain this property, skip.
14049                        if (!propertyValue) {
14050                            return true;
14051                        }
14052
14053                        // Get all url() of this element.
14054                        while (match = matchUrl.exec(propertyValue)) {
14055                            allImgs.push({
14056                                src: match[2],
14057                                element: element[0]
14058                            });
14059                        }
14060                    });
14061
14062                    $.each(hasImageAttributes, function (i, attribute) {
14063                        var attributeValue = element.attr(attribute);
14064                        var attributeValues;
14065
14066                        // If it doesn't contain this property, skip.
14067                        if (!attributeValue) {
14068                            return true;
14069                        }
14070
14071                        // Check for multiple comma separated images
14072                        attributeValues = attributeValue.split(',');
14073
14074                        $.each(attributeValues, function(i, value) {
14075                            // Trim value and get string before first
14076                            // whitespace (for use with srcset).
14077                            value = $.trim(value).split(' ')[0];
14078                            allImgs.push({
14079                                src: value,
14080                                element: element[0]
14081                            });
14082                        });
14083                    });
14084                });
14085            } else {
14086                // For images only, the task is simpler.
14087                obj.find('img:uncached')
14088                    .each(function () {
14089                    allImgs.push({
14090                        src: this.src,
14091                        element: this
14092                    });
14093                });
14094            }
14095
14096            allImgsLength = allImgs.length;
14097            allImgsLoaded = 0;
14098
14099            // If no images found, don't bother.
14100            if (allImgsLength === 0) {
14101                finishedCallback.call(obj[0]);
14102                deferred.resolveWith(obj[0]);
14103            }
14104
14105            $.each(allImgs, function (i, img) {
14106
14107                var image = new Image();
14108                var events =
14109                  'load.' + eventNamespace + ' error.' + eventNamespace;
14110
14111                // Handle the image loading and error with the same callback.
14112                $(image).one(events, function me (event) {
14113                    // If an error occurred with loading the image, set the
14114                    // third argument accordingly.
14115                    var eachArguments = [
14116                        allImgsLoaded,
14117                        allImgsLength,
14118                        event.type == 'load'
14119                    ];
14120                    allImgsLoaded++;
14121
14122                    eachCallback.apply(img.element, eachArguments);
14123                    deferred.notifyWith(img.element, eachArguments);
14124
14125                    // Unbind the event listeners. I use this in addition to
14126                    // `one` as one of those events won't be called (either
14127                    // 'load' or 'error' will be called).
14128                    $(this).off(events, me);
14129
14130                    if (allImgsLoaded == allImgsLength) {
14131                        finishedCallback.call(obj[0]);
14132                        deferred.resolveWith(obj[0]);
14133                        return false;
14134                    }
14135
14136                });
14137
14138                image.src = img.src;
14139            });
14140        });
14141
14142        return deferred.promise();
14143
14144    };
14145}));
14146
14147/**
14148 * @preserve  textfill
14149 * @name      jquery.textfill.js
14150 * @author    Russ Painter
14151 * @author    Yu-Jie Lin
14152 * @author    Alexandre Dantas
14153 * @version   0.6.0
14154 * @date      2014-08-19
14155 * @copyright (c) 2014 Alexandre Dantas
14156 * @copyright (c) 2012-2013 Yu-Jie Lin
14157 * @copyright (c) 2009 Russ Painter
14158 * @license   MIT License
14159 * @homepage  https://github.com/jquery-textfill/jquery-textfill
14160 * @example   http://jquery-textfill.github.io/jquery-textfill/index.html
14161 */
14162; (function($) {
14163
14164	/**
14165	 * Resizes an inner element's font so that the
14166	 * inner element completely fills the outer element.
14167	 *
14168	 * @param {Object} options User options that take
14169	 *                         higher precedence when
14170	 *                         merging with the default ones.
14171	 *
14172	 * @return All outer elements processed
14173	 */
14174	$.fn.textfill = function(options) {
14175
14176		// ______  _______ _______ _______ _     _        _______ _______
14177		// |     \ |______ |______ |_____| |     | |         |    |______
14178		// |_____/ |______ |       |     | |_____| |_____    |    ______|
14179        //
14180		// Merging user options with the default values
14181
14182		var defaults = {
14183			debug            : false,
14184			maxFontPixels    : 40,
14185			minFontPixels    : 4,
14186			innerTag         : 'span',
14187			widthOnly        : false,
14188			success          : null, // callback when a resizing is done
14189			callback         : null, // callback when a resizing is done (deprecated, use success)
14190			fail             : null, // callback when a resizing is failed
14191			complete         : null, // callback when all is done
14192			explicitWidth    : null,
14193			explicitHeight   : null,
14194			changeLineHeight : false
14195		};
14196
14197		var Opts = $.extend(defaults, options);
14198
14199		// _______ _     _ __   _ _______ _______ _____  _____  __   _ _______
14200		// |______ |     | | \  | |          |      |   |     | | \  | |______
14201		// |       |_____| |  \_| |_____     |    __|__ |_____| |  \_| ______|
14202		//
14203		// Predefining the awesomeness
14204
14205		// Output arguments to the Debug console
14206		// if "Debug Mode" is enabled
14207		function _debug() {
14208			if (!Opts.debug
14209				||  typeof console       == 'undefined'
14210				||  typeof console.debug == 'undefined') {
14211				return;
14212			}
14213			console.debug.apply(console, arguments);
14214		}
14215
14216		// Output arguments to the Warning console
14217		function _warn() {
14218			if (typeof console      == 'undefined' ||
14219				typeof console.warn == 'undefined') {
14220				return;
14221			}
14222			console.warn.apply(console, arguments);
14223		}
14224
14225		// Outputs all information on the current sizing
14226		// of the font.
14227		function _debug_sizing(prefix, ourText, maxHeight, maxWidth, minFontPixels, maxFontPixels) {
14228
14229			function _m(v1, v2) {
14230
14231				var marker = ' / ';
14232
14233				if (v1 > v2)
14234					marker = ' > ';
14235
14236				else if (v1 == v2)
14237					marker = ' = ';
14238
14239				return marker;
14240			}
14241
14242			_debug(
14243				'[TextFill] '  + prefix + ' { ' +
14244				'font-size: ' + ourText.css('font-size') + ',' +
14245				'Height: '    + ourText.height() + 'px ' + _m(ourText.height(), maxHeight) + maxHeight + 'px,' +
14246				'Width: '     + ourText.width()  + _m(ourText.width() , maxWidth)  + maxWidth + ',' +
14247				'minFontPixels: ' + minFontPixels + 'px, ' +
14248				'maxFontPixels: ' + maxFontPixels + 'px }'
14249			);
14250		}
14251
14252		/**
14253		 * Calculates which size the font can get resized,
14254		 * according to constrains.
14255		 *
14256		 * @param {String} prefix Gets shown on the console before
14257		 *                        all the arguments, if debug mode is on.
14258		 * @param {Object} ourText The DOM element to resize,
14259		 *                         that contains the text.
14260		 * @param {function} func Function called on `ourText` that's
14261		 *                        used to compare with `max`.
14262		 * @param {number} max Maximum value, that gets compared with
14263		 *                     `func` called on `ourText`.
14264		 * @param {number} minFontPixels Minimum value the font can
14265		 *                               get resized to (in pixels).
14266		 * @param {number} maxFontPixels Maximum value the font can
14267		 *                               get resized to (in pixels).
14268		 *
14269		 * @return Size (in pixels) that the font can be resized.
14270		 */
14271		function _sizing(prefix, ourText, func, max, maxHeight, maxWidth, minFontPixels, maxFontPixels) {
14272
14273			_debug_sizing(
14274				prefix, ourText,
14275				maxHeight, maxWidth,
14276				minFontPixels, maxFontPixels
14277			);
14278
14279			// The kernel of the whole plugin, take most attention
14280			// on this part.
14281			//
14282			// This is a loop that keeps increasing the `font-size`
14283			// until it fits the parent element.
14284			//
14285			// - Start from the minimal allowed value (`minFontPixels`)
14286			// - Guesses an average font size (in pixels) for the font,
14287			// - Resizes the text and sees if its size is within the
14288			//   boundaries (`minFontPixels` and `maxFontPixels`).
14289			//   - If so, keep guessing until we break.
14290			//   - If not, return the last calculated size.
14291			//
14292			// I understand this is not optimized and we should
14293			// consider implementing something akin to
14294			// Daniel Hoffmann's answer here:
14295			//
14296			//     http://stackoverflow.com/a/17433451/1094964
14297			//
14298
14299			while (minFontPixels < (maxFontPixels - 1)) {
14300
14301				var fontSize = Math.floor((minFontPixels + maxFontPixels) / 2);
14302				ourText.css('font-size', fontSize);
14303
14304				if (func.call(ourText) <= max) {
14305					minFontPixels = fontSize;
14306
14307					if (func.call(ourText) == max)
14308						break;
14309				}
14310				else
14311					maxFontPixels = fontSize;
14312
14313				_debug_sizing(
14314					prefix, ourText,
14315					maxHeight, maxWidth,
14316					minFontPixels, maxFontPixels
14317				);
14318			}
14319
14320			ourText.css('font-size', maxFontPixels);
14321
14322			if (func.call(ourText) <= max) {
14323				minFontPixels = maxFontPixels;
14324
14325				_debug_sizing(
14326					prefix + '* ', ourText,
14327					maxHeight, maxWidth,
14328					minFontPixels, maxFontPixels
14329				);
14330			}
14331			return minFontPixels;
14332		}
14333
14334		// _______ _______ _______  ______ _______
14335		// |______    |    |_____| |_____/    |
14336		// ______|    |    |     | |    \_    |
14337        //
14338		// Let's get it started (yeah)!
14339
14340		_debug('[TextFill] Start Debug');
14341
14342		this.each(function() {
14343
14344			// Contains the child element we will resize.
14345			// $(this) means the parent container
14346			var ourText = $(Opts.innerTag + ':visible:first', this);
14347
14348			// Will resize to this dimensions.
14349			// Use explicit dimensions when specified
14350			var maxHeight = Opts.explicitHeight || $(this).height();
14351			var maxWidth  = Opts.explicitWidth  || $(this).width();
14352
14353			var oldFontSize = ourText.css('font-size');
14354
14355			var lineHeight  = parseFloat(ourText.css('line-height')) / parseFloat(oldFontSize);
14356
14357			_debug('[TextFill] Inner text: ' + ourText.text());
14358			_debug('[TextFill] All options: ', Opts);
14359			_debug('[TextFill] Maximum sizes: { ' +
14360				   'Height: ' + maxHeight + 'px, ' +
14361				   'Width: '  + maxWidth  + 'px' + ' }'
14362				  );
14363
14364			var minFontPixels = Opts.minFontPixels;
14365
14366			// Remember, if this `maxFontPixels` is negative,
14367			// the text will resize to as long as the container
14368			// can accomodate
14369			var maxFontPixels = (Opts.maxFontPixels <= 0 ?
14370								 maxHeight :
14371								 Opts.maxFontPixels);
14372
14373
14374			// Let's start it all!
14375
14376			// 1. Calculate which `font-size` would
14377			//    be best for the Height
14378			var fontSizeHeight = undefined;
14379
14380			if (! Opts.widthOnly)
14381				fontSizeHeight = _sizing(
14382					'Height', ourText,
14383					$.fn.height, maxHeight,
14384					maxHeight, maxWidth,
14385					minFontPixels, maxFontPixels
14386				);
14387
14388			// 2. Calculate which `font-size` would
14389			//    be best for the Width
14390			var fontSizeWidth = undefined;
14391
14392			fontSizeWidth = _sizing(
14393				'Width', ourText,
14394				$.fn.width, maxWidth,
14395				maxHeight, maxWidth,
14396				minFontPixels, maxFontPixels
14397			);
14398
14399			// 3. Actually resize the text!
14400
14401			if (Opts.widthOnly) {
14402				ourText.css({
14403					'font-size'  : fontSizeWidth,
14404					'white-space': 'nowrap'
14405				});
14406
14407				if (Opts.changeLineHeight)
14408					ourText.parent().css(
14409						'line-height',
14410						(lineHeight * fontSizeWidth + 'px')
14411					);
14412			}
14413			else {
14414				var fontSizeFinal = Math.min(fontSizeHeight, fontSizeWidth);
14415
14416				ourText.css('font-size', fontSizeFinal);
14417
14418				if (Opts.changeLineHeight)
14419					ourText.parent().css(
14420						'line-height',
14421						(lineHeight * fontSizeFinal) + 'px'
14422					);
14423			}
14424
14425			_debug(
14426				'[TextFill] Finished { ' +
14427				'Old font-size: ' + oldFontSize              + ', ' +
14428				'New font-size: ' + ourText.css('font-size') + ' }'
14429			);
14430
14431			// Oops, something wrong happened!
14432			// We weren't supposed to exceed the original size
14433			if ((ourText.width()  > maxWidth) ||
14434				(ourText.height() > maxHeight && !Opts.widthOnly)) {
14435
14436				ourText.css('font-size', oldFontSize);
14437
14438				// Failure callback
14439				if (Opts.fail)
14440					Opts.fail(this);
14441
14442				_debug(
14443					'[TextFill] Failure { ' +
14444					'Current Width: '  + ourText.width()  + ', ' +
14445					'Maximum Width: '  + maxWidth         + ', ' +
14446					'Current Height: ' + ourText.height() + ', ' +
14447					'Maximum Height: ' + maxHeight        + ' }'
14448				);
14449			}
14450			else if (Opts.success) {
14451				Opts.success(this);
14452			}
14453			else if (Opts.callback) {
14454				_warn('callback is deprecated, use success, instead');
14455
14456				// Success callback
14457				Opts.callback(this);
14458			}
14459		});
14460
14461		// Complete callback
14462		if (Opts.complete)
14463			Opts.complete(this);
14464
14465		_debug('[TextFill] End Debug');
14466		return this;
14467	};
14468
14469})(window.jQuery);
14470
14471
14472!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;
14473function getStyles(config){
14474  // 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;
14474left: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+"}"
14475};
14476  var ShareUtils;
14477
14478if ((!("classList" in document.documentElement)) && Object.defineProperty && typeof HTMLElement !== "undefined") {
14479  Object.defineProperty(HTMLElement.prototype, "classList", {
14480    get: function() {
14481      var ret, self, update;
14482      update = function(fn) {
14483        return function(value) {
14484          var classes, index;
14485          classes = self.className.split(/\s+/);
14486          index = classes.indexOf(value);
14487          fn(classes, index, value);
14488          self.className = classes.join(" ");
14489        };
14490      };
14491      self = this;
14492      ret = {
14493        add: update(function(classes, index, value) {
14494          ~index || classes.push(value);
14495        }),
14496        remove: update(function(classes, index) {
14497          ~index && classes.splice(index, 1);
14498        }),
14499        toggle: update(function(classes, index, value) {
14500          if (~index) {
14501            classes.splice(index, 1);
14502          } else {
14503            classes.push(value);
14504          }
14505        }),
14506        contains: function(value) {
14507          return !!~self.className.split(/\s+/).indexOf(value);
14508        },
14509        item: function(i) {
14510          return self.className.split(/\s+/)[i] || null;
14511        }
14512      };
14513      Object.defineProperty(ret, "length", {
14514        get: function() {
14515          return self.className.split(/\s+/).length;
14516        }
14517      });
14518      return ret;
14519    }
14520  });
14521}
14522
14523String.prototype.to_rfc3986 = function() {
14524  var tmp;
14525  tmp = encodeURIComponent(this);
14526  return tmp.replace(/[!'()*]/g, function(c) {
14527    return "%" + c.charCodeAt(0).toString(16);
14528  });
14529};
14530
14531ShareUtils = (function() {
14532  function ShareUtils() {}
14533
14534  ShareUtils.prototype.extend = function(to, from, overwrite) {
14535    var hasProp, prop;
14536    for (prop in from) {
14537      hasProp = to[prop] !== undefined;
14538      if (hasProp && typeof from[prop] === "object") {
14539        this.extend(to[prop], from[prop], overwrite);
14540      } else {
14541        if (overwrite || !hasProp) {
14542          to[prop] = from[prop];
14543        }
14544      }
14545    }
14546  };
14547
14548  ShareUtils.prototype.hide = function(el) {
14549    return el.style.display = "none";
14550  };
14551
14552  ShareUtils.prototype.show = function(el) {
14553    return el.style.display = "block";
14554  };
14555
14556  ShareUtils.prototype.has_class = function(el, class_name) {
14557    return el.classList.contains(class_name);
14558  };
14559
14560  ShareUtils.prototype.add_class = function(el, class_name) {
14561    return el.classList.add(class_name);
14562  };
14563
14564  ShareUtils.prototype.remove_class = function(el, class_name) {
14565    return el.classList.remove(class_name);
14566  };
14567
14568  ShareUtils.prototype.is_encoded = function(str) {
14569    str = str.to_rfc3986();
14570    return decodeURIComponent(str) !== str;
14571  };
14572
14573  ShareUtils.prototype.encode = function(str) {
14574    if (typeof str === "undefined" || this.is_encoded(str)) {
14575      return str;
14576    } else {
14577      return str.to_rfc3986();
14578    }
14579  };
14580
14581  ShareUtils.prototype.popup = function(url, params) {
14582    var k, popup, qs, v;
14583    if (params == null) {
14584      params = {};
14585    }
14586    popup = {
14587      width: 500,
14588      height: 350
14589    };
14590    popup.top = (screen.height / 2) - (popup.height / 2);
14591    popup.left = (screen.width / 2) - (popup.width / 2);
14592    qs = ((function() {
14593      var _results;
14594      _results = [];
14595      for (k in params) {
14596        v = params[k];
14597        _results.push("" + k + "=" + (this.encode(v)));
14598      }
14599      return _results;
14600    }).call(this)).join('&');
14601    if (qs) {
14602      qs = "?" + qs;
14603    }
14604    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=" 
14604+ popup.width + ",height=" + popup.height);
14605  };
14606
14607  return ShareUtils;
14608
14609})();
14610var Share,
14611  __hasProp = {}.hasOwnProperty,
14612  __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; };
14613
14614Share = (function(_super) {
14615  __extends(Share, _super);
14616
14617  function Share(element, options) {
14618    this.element = element;
14619    this.el = {
14620      head: document.getElementsByTagName('head')[0],
14621      body: document.getElementsByTagName('body')[0]
14622    };
14623    this.config = {
14624      enabled_networks: 0,
14625      protocol: ['http', 'https'].indexOf(window.location.href.split(':')[0]) === -1 ? 'https://' : '//',
14626      url: window.location.href.replace("&", "%26"),
14627      caption: null,
14628      title: this.default_title(),
14629      image: this.default_image(),
14630      description: this.default_description(),
14631      ui: {
14632        flyout: 'top center',
14633        button_text: 'Share',
14634        button_font: true,
14635        icon_font: true
14636      },
14637      networks: {
14638        google_plus: {
14639          enabled: true,
14640          url: null
14641        },
14642        twitter: {
14643          enabled: true,
14644          url: null,
14645          title: null,
14646          description: null
14647        },
14648        facebook: {
14649          enabled: true,
14650          load_sdk: true,
14651          url: null,
14652          app_id: null,
14653          title: null,
14654          caption: null,
14655          description: null,
14656          image: null
14657        },
14658        pinterest: {
14659          enabled: true,
14660          url: null,
14661          image: null,
14662          description: null
14663        },
14664        reddit: {
14665          enabled: true,
14666          url: null,
14667          title: null
14668        },
14669        linkedin: {
14670          enabled: true,
14671          url: null,
14672          title: null,
14673          description: null
14674        },
14675        xing: {
14676          enabled: true,
14677          url: null,
14678          title: null,
14679          image: null,
14680          description: null
14681        },
14682        whatsapp: {
14683          enabled: true,
14684          description: null,
14685          url: null
14686        },
14687        email: {
14688          enabled: true,
14689          title: null,
14690          description: null,
14691          url: null
14692        }
14693      }
14694    };
14695    this.setup(element, options);
14696    return this;
14697  }
14698
14699  Share.prototype.setup = function(element, opts) {
14700    var index, instance, instances, _i, _len;
14701    instances = document.querySelectorAll(element);
14702    this.extend(this.config, opts, true);
14703    this.set_global_configuration();
14704    this.normalize_network_configuration();
14705    if (this.config.ui.icon_font) {
14706      this.inject_icons();
14707    }
14708    if (this.config.ui.button_font) {
14709      this.inject_fonts();
14710    }
14711    if (this.config.networks.facebook.enabled && this.config.networks.facebook.load_sdk) {
14712      this.inject_facebook_sdk();
14713    }
14714    for (index = _i = 0, _len = instances.length; _i < _len; index = ++_i) {
14715      instance = instances[index];
14716      this.setup_instance(element, index);
14717    }
14718  };
14719
14720  Share.prototype.setup_instance = function(element, index) {
14721    var button, instance, label, network, networks, _i, _len, _results,
14722      _this = this;
14723    instance = document.querySelectorAll(element)[index];
14724    this.hide(instance);
14725    this.add_class(instance, "sharer-" + index);
14726    instance = document.querySelectorAll(element)[index];
14727    this.inject_css(instance);
14728    this.inject_html(instance);
14729    this.show(instance);
14730    label = instance.getElementsByTagName("label")[0];
14731    button = instance.getElementsByClassName("social")[0];
14732    networks = instance.getElementsByTagName('li');
14733    this.add_class(button, "networks-" + this.config.enabled_networks);
14734    label.addEventListener("click", function() {
14735      return _this.event_toggle(button);
14736    });
14737    _this = this;
14738    _results = [];
14739    for (index = _i = 0, _len = networks.length; _i < _len; index = ++_i) {
14740      network = networks[index];
14741      _results.push(network.addEventListener("click", function() {
14742        _this.event_network(instance, this);
14743        return _this.event_close(button);
14744      }));
14745    }
14746    return _results;
14747  };
14748
14749  Share.prototype.event_toggle = function(button) {
14750    if (this.has_class(button, "active")) {
14751      return this.event_close(button);
14752    } else {
14753      return this.event_open(button);
14754    }
14755  };
14756
14757  Share.prototype.event_open = function(button) {
14758    if (this.has_class(button, "load")) {
14759      this.remove_class(button, "load");
14760    }
14761    return this.add_class(button, "active");
14762  };
14763
14764  Share.prototype.event_close = function(button) {
14765    return this.remove_class(button, "active");
14766  };
14767
14768  Share.prototype.event_network = function(instance, network) {
14769    var name;
14770    name = network.getAttribute("data-network");
14771    this.hook("before", name, instance);
14772    this["network_" + name]();
14773    return this.hook("after", name, instance);
14774  };
14775
14776  Share.prototype.open = function() {
14777    return this["public"]("open");
14778  };
14779
14780  Share.prototype.close = function() {
14781    return this["public"]("close");
14782  };
14783
14784  Share.prototype.toggle = function() {
14785    return this["public"]("toggle");
14786  };
14787
14788  Share.prototype["public"] = function(action) {
14789    var button, index, instance, _i, _len, _ref, _results;
14790    _ref = document.querySelectorAll(this.element);
14791    _results = [];
14792    for (index = _i = 0, _len = _ref.length; _i < _len; index = ++_i) {
14793      instance = _ref[index];
14794      button = instance.getElementsByClassName("social")[0];
14795      _results.push(this["event_" + action](button));
14796    }
14797    return _results;
14798  };
14799
14800  Share.prototype.network_facebook = function() {
14801    if (this.config.networks.facebook.load_sdk) {
14802      if (!window.FB) {
14803        return console.error("The Facebook JS SDK hasn't loaded yet.");
14804      }
14805      return FB.ui({
14806        method: 'feed',
14807        name: this.config.networks.facebook.title,
14808        link: this.config.networks.facebook.url,
14809        picture: this.config.networks.facebook.image,
14810        caption: this.config.networks.facebook.caption,
14811        description: this.config.networks.facebook.description
14812      });
14813    } else {
14814      return this.popup('https://www.facebook.com/sharer/sharer.php', {
14815        u: this.config.networks.facebook.url
14816      });
14817    }
14818  };
14819
14820  Share.prototype.network_twitter = function() {
14821    return this.popup('https://twitter.com/intent/tweet', {
14822      text: this.config.networks.twitter.title,
14823      url: this.config.networks.twitter.url
14824    });
14825  };
14826
14827  Share.prototype.network_google_plus = function() {
14828    return this.popup('https://plus.google.com/share', {
14829      url: this.config.networks.google_plus.url
14830    });
14831  };
14832
14833  Share.prototype.network_pinterest = function() {
14834    return this.popup('https://www.pinterest.com/pin/create/button', {
14835      url: this.config.networks.pinterest.url,
14836      media: this.config.networks.pinterest.image,
14837      description: this.config.networks.pinterest.description
14838    });
14839  };
14840
14841  Share.prototype.network_linkedin = function() {
14842    return this.popup('https://www.linkedin.com/shareArticle', {
14843      url: this.config.networks.linkedin.url,
14844      title: this.config.networks.linkedin.title,
14845      summary: this.config.networks.linkedin.description
14846    });
14847  }
14848
14849  Share.prototype.network_xing = function() {
14850    return this.popup('https://www.xing.com/spi/shares/new', {
14851      url: this.config.networks.xing.url,
14852      image: this.config.networks.xing.image,
14853      title: this.config.networks.xing.title,
14854      summary: this.config.networks.xing.description
14855    });
14856  }
14857
14858  Share.prototype.network_email = function() {
14859    return this.popup('mailto:', {
14860      subject: this.config.networks.email.title,
14861      body: this.config.networks.email.url + '%0A%0A' + this.config.networks.email.description,
14862    });
14863  };
14864
14865  Share.prototype.inject_icons = function() {
14866  //  return this.inject_stylesheet("https://www.sharebutton.co/fonts/v2/entypo.min.css");
14867  };
14868
14869  Share.prototype.inject_fonts = function() {
14870   // return this.inject_stylesheet("http://fonts.googleapis.com/css?family=Lato:900&text=" + this.config.ui.button_text);
14871  };
14872
14873  Share.prototype.inject_stylesheet = function(url) {
14874    var link;
14875    if (!this.el.head.querySelector("link[href=\"" + url + "\"]")) {
14876      link = document.createElement("link");
14877      link.setAttribute("rel", "stylesheet");
14878      link.setAttribute("href", url);
14879      return this.el.head.appendChild(link);
14880    }
14881  };
14882
14883  Share.prototype.inject_css = function(instance) {
14884    var css, meta, selector, style;
14885    selector = "." + (instance.getAttribute('class').split(" ").join("."));
14886    if (!this.el.head.querySelector("meta[name='sharer" + selector + "']")) {
14887      this.config.selector = selector;
14888      css = getStyles(this.config);
14889      style = document.createElement("style");
14890      style.type = "text/css";
14891      if (style.styleSheet) {
14892        style.styleSheet.cssText = css;
14893      } else {
14894        style.appendChild(document.createTextNode(css));
14895      }
14896      this.el.head.appendChild(style);
14897      delete this.config.selector;
14898      meta = document.createElement("meta");
14899      meta.setAttribute("name", "sharer" + selector);
14900      return this.el.head.appendChild(meta);
14901    }
14902  };
14903
14904  Share.prototype.inject_html = function(instance) {
14905    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>";
14906  };
14907
14908  Share.prototype.inject_facebook_sdk = function() {
14909    var fb_root, script;
14910    if (!window.FB && this.config.networks.facebook.app_id && !this.el.body.querySelector('#fb-root')) {
14911      script = document.createElement("script");
14912      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')";
14913      fb_root = document.createElement("div");
14914      fb_root.id = "fb-root";
14915      this.el.body.appendChild(fb_root);
14916      return this.el.body.appendChild(script);
14917    }
14918  };
14919
14920  Share.prototype.hook = function(type, network, instance) {
14921    var fn, opts;
14922    fn = this.config.networks[network][type];
14923    if (typeof fn === "function") {
14924      opts = fn.call(this.config.networks[network], instance);
14925      if (opts !== void 0) {
14926        opts = this.normalize_filter_config_updates(opts);
14927        this.extend(this.config.networks[network], opts, true);
14928        this.normalize_network_configuration();
14929      }
14930    }
14931  };
14932
14933  Share.prototype.default_title = function() {
14934    var content;
14935    if (content = document.querySelector('meta[property="og:title"]') || document.querySelector('meta[name="twitter:title"]')) {
14936      return encodeURIComponent(content.getAttribute('content'));
14937    } else if (content = document.querySelector('title')) {
14938      return encodeURIComponent(content.innerText);
14939    }
14940  };
14941
14942  Share.prototype.default_image = function() {
14943    var content;
14944    if (content = document.querySelector('meta[property="og:image"]') || document.querySelector('meta[name="twitter:image"]')) {
14945      return content.getAttribute('content');
14946    }
14947  };
14948
14949  Share.prototype.default_description = function() {
14950    var content;
14951    if (content = document.querySelector('meta[property="og:description"]') || document.querySelector('meta[name="twitter:description"]') || document.querySelector('meta[name="description"]')) {
14952      return encodeURIComponent(content.getAttribute('content'));
14953    } else {
14954      return '';
14955    }
14956  };
14957
14958  Share.prototype.set_global_configuration = function() {
14959    var display, network, option, options, _ref, _results;
14960    _ref = this.config.networks;
14961    _results = [];
14962    for (network in _ref) {
14963      options = _ref[network];
14964      for (option in options) {
14965        if (this.config.networks[network][option] == null) {
14966          this.config.networks[network][option] = this.config[option];
14967        }
14968      }
14969      if (this.config.networks[network].enabled) {
14970        display = 'block';
14971        this.config.enabled_networks += 1;
14972      } else {
14973        display = 'none';
14974      }
14975      _results.push(this.config.networks[network].display = display);
14976    }
14977    return _results;
14978  };
14979
14980  Share.prototype.normalize_network_configuration = function() {
14981    if (!this.config.networks.facebook.app_id) {
14982      this.config.networks.facebook.load_sdk = false;
14983    }
14984    if (!this.is_encoded(this.config.networks.twitter.description)) {
14985      this.config.networks.twitter.description = encodeURIComponent(this.config.networks.twitter.description);
14986    }
14987    if (typeof this.config.networks.facebook.app_id === 'number') {
14988      return this.config.networks.facebook.app_id = this.config.networks.facebook.app_id.toString();
14989    }
14990  };
14991
14992  Share.prototype.normalize_filter_config_updates = function(opts) {
14993    if (this.config.networks.facebook.app_id !== opts.app_id) {
14994      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.");
14995      delete opts.app_id;
14996    }
14997    if (this.config.networks.facebook.load_sdk !== opts.load_sdk) {
14998      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.");
14999      delete opts.app_id;
15000    }
15001    return opts;
15002  };
15003
15004  return Share;
15005
15006})(ShareUtils);
15007 return Share;
15008});
15009
15010
15011// Generated by CoffeeScript 1.9.2
15012
15013// @license Sticky-kit v1.1.2 | WTFPL | Leaf Corcoran 2015 | http://leafo.net
15014
15015(function() {
15016  var $, win;
15017
15018  $ = this.jQuery || window.jQuery;
15019
15020  win = $(window);
15021
15022  $.fn.stick_in_parent = function(opts) {
15023    var doc, elm, enable_bottoming, fn, i, inner_scrolling, len, manual_spacer, offset_top, outer_width, parent_selector, recalc_every, sticky_class;
15024    if (opts == null) {
15025      opts = {};
15026    }
15027    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;
15028    if (offset_top == null) {
15029      offset_top = 0;
15030    }
15031    if (parent_selector == null) {
15032      parent_selector = void 0;
15033    }
15034    if (inner_scrolling == null) {
15035      inner_scrolling = true;
15036    }
15037    if (sticky_class == null) {
15038      sticky_class = "is_stuck";
15039    }
15040    doc = $(document);
15041    if (enable_bottoming == null) {
15042      enable_bottoming = true;
15043    }
15044    outer_width = function(el) {
15045      var _el, computed, w;
15046      if (window.getComputedStyle) {
15047        _el = el[0];
15048        computed = window.getComputedStyle(el[0]);
15049        w = parseFloat(computed.getPropertyValue("width")) + parseFloat(computed.getPropertyValue("margin-left")) + parseFloat(computed.getPropertyValue("margin-right"));
15050        if (computed.getPropertyValue("box-sizing") !== "border-box") {
15051          w += parseFloat(computed.getPropertyValue("border-left-width")) + parseFloat(computed.getPropertyValue("border-right-width")) + parseFloat(computed.getPropertyValue("padding-left")) + parseFloat(computed.getPropertyValue("padding-right"));
15052        }
15053        return w;
15054      } else {
15055        return el.outerWidth(true);
15056      }
15057    };
15058    fn = function(elm, padding_bottom, parent_top, parent_height, top, height, el_float, detached) {
15059      var bottomed, detach, fixed, last_pos, last_scroll_height, offset, parent, recalc, recalc_and_tick, recalc_counter, spacer, tick;
15060      if (elm.data("sticky_kit")) {
15061        return;
15062      }
15063      elm.data("sticky_kit", true);
15064      last_scroll_height = doc.height();
15065      parent = elm.parent();
15066      if (parent_selector != null) {
15067        parent = parent.closest(parent_selector);
15068      }
15069      if (!parent.length) {
15070        throw "failed to find stick parent";
15071      }
15072      fixed = false;
15073      bottomed = false;
15074      spacer = manual_spacer != null ? manual_spacer && elm.closest(manual_spacer) : $("<div />");
15075      if (spacer) {
15076        spacer.css('position', elm.css('position'));
15077      }
15078      recalc = function() {
15079        var border_top, padding_top, restore;
15080        if (detached) {
15081          return;
15082        }
15083        last_scroll_height = doc.height();
15084        border_top = parseInt(parent.css("border-top-width"), 10);
15085        padding_top = parseInt(parent.css("padding-top"), 10);
15086        padding_bottom = parseInt(parent.css("padding-bottom"), 10);
15087        parent_top = parent.offset().top + border_top + padding_top;
15088        parent_height = parent.height();
15089        if (fixed) {
15090          fixed = false;
vendor: 6,069 bytes, lines 15091-15286
15091          bottomed = false;
15092          if (manual_spacer == null) {
15093            elm.insertAfter(spacer);
15094            spacer.detach();
15095          }
15096          elm.css({
15097            position: "",
15098            top: "",
15099            width: "",
15100            bottom: ""
15101          }).removeClass(sticky_class);
15102          restore = true;
15103        }
15104        top = elm.offset().top - (parseInt(elm.css("margin-top"), 10) || 0) - offset_top;
15105        height = elm.outerHeight(true);
15106        el_float = elm.css("float");
15107        if (spacer) {
15108          spacer.css({
15109            width: outer_width(elm),
15110            height: height,
15111            display: elm.css("display"),
15112            "vertical-align": elm.css("vertical-align"),
15113            "float": el_float
15114          });
15115        }
15116        if (restore) {
15117          return tick();
15118        }
15119      };
15120      recalc();
15121      if (height === parent_height) {
15122        return;
15123      }
15124      last_pos = void 0;
15125      offset = offset_top;
15126      recalc_counter = recalc_every;
15127      tick = function() {
15128        var css, delta, recalced, scroll, will_bottom, win_height;
15129        if (detached) {
15130          return;
15131        }
15132        recalced = false;
15133        if (recalc_counter != null) {
15134          recalc_counter -= 1;
15135          if (recalc_counter <= 0) {
15136            recalc_counter = recalc_every;
15137            recalc();
15138            recalced = true;
15139          }
15140        }
15141        if (!recalced && doc.height() !== last_scroll_height) {
15142          recalc();
15143          recalced = true;
15144        }
15145        scroll = win.scrollTop();
15146        if (last_pos != null) {
15147          delta = scroll - last_pos;
15148        }
15149        last_pos = scroll;
15150        if (fixed) {
15151          if (enable_bottoming) {
15152            will_bottom = scroll + height + offset > parent_height + parent_top;
15153            if (bottomed && !will_bottom) {
15154              bottomed = false;
15155              elm.css({
15156                position: "fixed",
15157                bottom: "",
15158                top: offset
15159              }).trigger("sticky_kit:unbottom");
15160            }
15161          }
15162          if (scroll < top) {
15163            fixed = false;
15164            offset = offset_top;
15165            if (manual_spacer == null) {
15166              if (el_float === "left" || el_float === "right") {
15167                elm.insertAfter(spacer);
15168              }
15169              spacer.detach();
15170            }
15171            css = {
15172              position: "",
15173              width: "",
15174              top: ""
15175            };
15176            elm.css(css).removeClass(sticky_class).trigger("sticky_kit:unstick");
15177          }
15178          if (inner_scrolling) {
15179            win_height = win.height();
15180            if (height + offset_top > win_height) {
15181              if (!bottomed) {
15182                offset -= delta;
15183                offset = Math.max(win_height - height, offset);
15184                offset = Math.min(offset_top, offset);
15185                if (fixed) {
15186                  elm.css({
15187                    top: offset + "px"
15188                  });
15189                }
15190              }
15191            }
15192          }
15193        } else {
15194          if (scroll > top) {
15195            fixed = true;
15196            css = {
15197              position: "fixed",
15198              top: offset
15199            };
15200            css.width = elm.css("box-sizing") === "border-box" ? elm.outerWidth() + "px" : elm.width() + "px";
15201            elm.css(css).addClass(sticky_class);
15202            if (manual_spacer == null) {
15203              elm.after(spacer);
15204              if (el_float === "left" || el_float === "right") {
15205                spacer.append(elm);
15206              }
15207            }
15208            elm.trigger("sticky_kit:stick");
15209          }
15210        }
15211        if (fixed && enable_bottoming) {
15212          if (will_bottom == null) {
15213            will_bottom = scroll + height + offset > parent_height + parent_top;
15214          }
15215          if (!bottomed && will_bottom) {
15216            bottomed = true;
15217            if (parent.css("position") === "static") {
15218              parent.css({
15219                position: "relative"
15220              });
15221            }
15222            return elm.css({
15223              position: "absolute",
15224              bottom: padding_bottom,
15225              top: "auto"
15226            }).trigger("sticky_kit:bottom");
15227          }
15228        }
15229      };
15230      recalc_and_tick = function() {
15231        recalc();
15232        return tick();
15233      };
15234      detach = function() {
15235        detached = true;
15236        win.off("touchmove", tick);
15237        win.off("scroll", tick);
15238        win.off("resize", recalc_and_tick);
15239        $(document.body).off("sticky_kit:recalc", recalc_and_tick);
15240        elm.off("sticky_kit:detach", detach);
15241        elm.removeData("sticky_kit");
15242        elm.css({
15243          position: "",
15244          bottom: "",
15245          top: "",
15246          width: ""
15247        });
15248        parent.position("position", "");
15249        if (fixed) {
15250          if (manual_spacer == null) {
15251            if (el_float === "left" || el_float === "right") {
15252              elm.insertAfter(spacer);
15253            }
15254            spacer.remove();
15255          }
15256          return elm.removeClass(sticky_class);
15257        }
15258      };
15259      win.on("touchmove", tick);
15260      win.on("scroll", tick);
15261      win.on("resize", recalc_and_tick);
15262      $(document.body).on("sticky_kit:recalc", recalc_and_tick);
15263      elm.on("sticky_kit:detach", detach);
15264      return setTimeout(tick, 0);
15265    };
15266    for (i = 0, len = this.length; i < len; i++) {
15267      elm = this[i];
15268      fn($(elm));
15269    }
15270    return this;
15271  };
15272
15273}).call(this);
15274
15275/* ==========================================================================
15276 * bootstrap-tab-history.js
15277 * Author: Michael Narayan <[email protected]>
15278 * Repository: https://github.com/mnarayan01/bootstrap-tab-history/
15279 * ==========================================================================
15280 * Licensed under the Apache License, Version 2.0 (the "License"); you may
15281 * not use this file except in compliance with the License. You may obtain a
15282 * copy of the License at
15283 *
15284 * http://www.apache.org/licenses/LICENSE-2.0
15285 *
15286 * Unless required by applicable law or agreed to in writing, softw
15286are
15287 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15288 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
15289 * License for the specific language governing permissions and limitations
15290 * under the License.
15291 * ========================================================================== */
15292
15293/* ========================================================================== */
15294/* JSHint directives                                                          */
15295/*                                                                            */
15296/* global BootstrapTabHistory: true                                           */
15297/*                                                                            */
15298/* global document                                                            */
15299/* global jQuery                                                              */
15300/* global history                                                             */
15301/* global window                                                              */
15302/* ========================================================================== */
15303
15304/**
15305 * Integrate [HTML5 history state tracking](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history)
15306 * with [`bootstrap/tab.js`](http://getbootstrap.com/javascript/#tabs). To enable tracking on a tab element, simply set
15307 * the `data-tab-history` attribute to true (or a string denoting a tab grouping).
15308 *
15309 * See the README for additional information.
15310 *
15311 * Functionality based upon bootstrap/tab.js v3.1.0; reference it when making any changes.
15312 */
15313
15314BootstrapTabHistory = {
15315  options: {
15316    /**
15317     * When the anchor portion of the URI is used to activate a tab, scroll down to the given offset, rather than the
15318     * element with the given `id` attribute. Set to null to disable. Only relevant if showTabsBasedOnAnchor is true.
15319     *
15320     * May be overriden on a per-element basis by the attribute `data-tab-history-anchor-y-offset`.
15321     *
15322     * @public
15323     * @type {?number}
15324     */
15325    defaultAnchorYOffset: 0,
15326    /**
15327     * Either 'push' or 'replace', for whether to use `history.pushState` or `history.replaceState`, resp.
15328     *
15329     * May be overriden on a per-element basis by the attribute `data-tab-history-changer`.
15330     *
15331     * @public
15332     * @type {string}
15333     */
15334    defaultChanger: 'replace',
15335    /**
15336     * If true, update the URL in onShownTab in the calls to `history.pushState` and `history.replaceState`. Otherwise,
15337     * `null` is passed as the third parameter to these calls.
15338     *
15339     * May be overriden on a per-element basis by the attribute `data-tab-history-update-url`.
15340     *
15341     * @public
15342     * @type {boolean}
15343     */
15344    defaultUpdateURL: false,
15345    /**
15346     * Should the anchor portion of the loaded URI be used to activate a single tab if no history was present on page
15347     * load.
15348     *
15349     * @public
15350     * @type {boolean}
15351     */
15352    showTabsBasedOnAnchor: true
15353  }
15354};
15355
15356(function () {
15357  'use strict';
15358
15359  jQuery(function () {
15360    if(history && history.pushState && history.replaceState) {
15361      var bootstrapTabHistory = history.state && history.state.bootstrapTabHistory;
15362
15363      if(bootstrapTabHistory) {
15364        showTabsBasedOnState(bootstrapTabHistory);
15365      } else {
15366        showTabsBasedOnAnchor();
15367      }
15368
15369      backfillHistoryState();
15370
15371      jQuery(document).on('shown.bs.tab show.bs.collapse', onShownTab);
15372      jQuery(window).on('popstate', onPopState);
15373    } else {
15374      showTabsBasedOnAnchor();
15375    }
15376  });
15377
15378  /**
15379   * Used to prevent onShownTab from registering shown events that we triggered via showTabsBasedOnState.
15380   *
15381   * @type {boolean}
15382   */
15383  var showingTabsBasedOnState = false;
15384
15385  /**
15386   * Used to update `history.state` to reflect the default active tabs on initial page load. This supports proper
15387   * `history.back` handling when `data-tab-history-update-url` is true.
15388   */
15389  function backfillHistoryState() {
15390    var newState = null;
15391
15392    jQuery('li.active > [data-tab-history], .panel-title.active [data-tab-history]').each(function () {//edited by Uncode
15393      var $activeTabElement = jQuery(this);
15394      var selector = getTabSelector($activeTabElement);
15395
15396      if(selector) {
15397        var tabGroup = getTabGroup($activeTabElement);
15398
15399        if(tabGroup) {
15400          newState = createNewHistoryState(newState || history.state, tabGroup, selector);
15401        }
15402      }
15403    });
15404
15405    if(newState) {
15406      history.replaceState(newState, '', null);
15407    }
15408  }
15409
15410  /**
15411   * Clone the existing state, ensure its bootstrapTabHistory attribute is an Object, and add the provided tabGroup to
15412   * said bootstrapTabHistory attribute.
15413   *
15414   * @param {?object} existingState
15415   * @param {!string} tabGroup
15416   * @param {!string} selector
15417   * @returns {!object}
15418   */
15419  function createNewHistoryState(existingState, tabGroup, selector) {
15420    // Clone history.state and ensure it has a bootstrapTabHistory entry.
15421    var newState = jQuery.extend(true, {}, existingState, {
15422      bootstrapTabHistory: {}
15423    });
15424
15425    newState.bootstrapTabHistory[tabGroup] = selector;
15426
15427    return newState;
15428  }
15429
15430  /**
15431   * @param {jQuery} $tab
15432   * @returns {?string}
15433   */
15434  function getTabGroup($tab) {
15435    return parseTruthyAttributeValue($tab.data('tab-history'));
15436  }
15437
15438  /**
15439   * @param {jQuery} $tab
15440   * @returns {?string}
15441   */
15442  function getTabSelector($tab) {
15443    return $tab.data('target') || $tab.attr('href');
15444  }
15445
15446  /**
15447   * Receives the `shown.bs.tab` event. Updates `history.state` as appropriate.
15448   *
15449   * @param {jQuery.Event} shownEvt
15450   */
15451  function onShownTab(shownEvt) {
15452    if(!showingTabsBasedOnState) {
15453      var $activatedTab = jQuery(shownEvt.target);
15454      if ( $activatedTab.hasClass('panel-collapse') )
15455        $activatedTab = $activatedTab.closest('.panel').find('a');
15456      var selector = getTabSelector($activatedTab);
15457
15458      if(selector) {
15459        var tabGroup = getTabGroup($activatedTab);
15460
15461        if(tabGroup) {
15462          var historyChanger = $activatedTab.data('tab-history-changer') || BootstrapTabHistory.options.defaultChanger;
15463          var newState = createNewHistoryState(history.state, tabGroup, selector);
15464          var updateURL = (function ($activatedTab) {
15465            if(selector[0] === '#') {
15466              var elementUpdateURLOption = parseTruthyAttributeValue($activatedTab.data('tab-history-update-url'));
15467
15468              if(elementUpdateURLOption === undefined) {
15469                return BootstrapTabHistory.options.defaultUpdateURL;
15470              } else {
15471                return elementUpdateURLOption;
15472              }
15473            } else {
15474              return false;
15475            }
15476          })($activatedTab);
15477
15478          switch(historyChanger) {
15479            case 'push':
15480              history.pushState(newState, '', updateURL ? selector : null);
15481              break;
15482            case 'replace':
15483              history.replaceState(newState, '', updateURL ? selector : null);
15484              break;
15485            default:
15486              throw new Error('Unknown tab-history-changer: ' + historyChanger);
15487          }
15488        }
15489      }
15490    }
15491  }
15492
15493  /**
15494   * Receives the `popstate` event. Shows tabs based on the value of `history.state` as appropriate.
15495   */
15496  function onPopState() {
15497    var bootstrapTabHistory = history.state && history.state.bootstrapTabHistory;
15498
15499    if(bootstrapTabHistory) {
15500      showTabsBasedOnState(bootstrapTabHistory);
15501    }
15502  }
15503
15504  /**
15505   * Returns the given value, _unless_ that value is an empty string, in which case `true` is returned.
15506   *
15507   * Rationale: HAML data attributes which are set to `true` are rendered as a blank string.
15508   *
15509   * @param {*} value
15510   * @returns {*}
15511   */
15512  function parseTruthyAttributeValue(value) {
15513    if(value) {
15514      return value;
15515    } else if(value === '') {
15516      return true;
15517    } else {
15518      return value;
15519    }
15520  }
15521
15522  /**
15523   * Show tabs based upon the anchor component of `window.location`.
15524   */
15525  function showTabsBasedOnAnchor() {
15526    if(BootstrapTabHistory.options.showTabsBasedOnAnchor) {
15527      var anchor = window.location && window.location.hash;
15528
15529      if(anchor) {
15530        var $tabElement = showTabForSelector(anchor);
15531
15532        if($tabElement && window.addEventListener && window.removeEventListener) {
15533          var anchorYOffset = (function ($tabElement) {
15534            var elementSetting = $tabElement.data('tab-history-anchor-y-offset');
15535
15536            if(elementSetting === undefined) {
15537              return BootstrapTabHistory.options.defaultAnchorYOffset;
15538            } else {
15539              return elementSetting;
15540            }
15541          })($tabElement);
15542
15543          // HACK: This prevents scrolling to the tab on page load. This relies on the fact that we should never get
15544          //   here on `history.forward`, `history.back`, or `location.reload`, since in all those situations the
15545          //   `history.state` object should have been used (unless the browser did not support the modern History API).
15546          if(anchorYOffset || anchorYOffset === 0) {
15547            var scrollListener = function resetAnchorScroll () {
15548              window.removeEventListener('scroll', scrollListener);
15549              window.scrollTo(0, anchorYOffset);
15550            };
15551
15552            window.addEventListener('scroll', scrollListener);
15553          }
15554        }
15555      }
15556    }
15557  }
15558
15559  /**
15560   * Show a tab which corresponds to the provided selector.
15561   *
15562   * @param {string} selector - A CSS selector.
15563   * @returns {?jQuery} - The tab which was found to show (even if said tab was already active).
15564   */
15565  function showTabForSelector(selector) {
15566    var $tabElement = (function (selector) {
15567      var $ret = null;
15568
15569      jQuery('[data-toggle="tab"], [data-toggle="pill"], [data-toggle="collapse"]').each(function () {
15570        var $potentialTab = jQuery(this);
15571
15572        if(($potentialTab.attr('href') === selector || $potentialTab.data('target') === selector ) && getTabGroup($potentialTab)) {
15573          $ret = $potentialTab;
15574          return false;
vendor: 4,523 bytes, lines 15575-15725
15575        } else {
15576         return null;
15577        }
15578      });
15579
15580      return $ret;
15581    })(selector);
15582
15583    if($tabElement) {
15584      $tabElement.trigger('click');
15585      //$tabElement.tab('show');
15586    }
15587
15588    return $tabElement;
15589  }
15590
15591  /**
15592   * Iterate through the provided set of tab tab groups, showing the tabs based on the stored selectors.
15593   *
15594   * @param {object} bootstrapTabHistory - Each of the values is passed to showTabForSelector; the keys are actually irrelevant.
15595   */
15596  function showTabsBasedOnState(bootstrapTabHistory) {
15597    showingTabsBasedOnState = true;
15598
15599    try {
15600      for(var k in bootstrapTabHistory) {
15601        if(bootstrapTabHistory.hasOwnProperty(k)) {
15602          showTabForSelector(bootstrapTabHistory[k]);
15603        }
15604      }
15605    } finally {
15606      showingTabsBasedOnState = false;
15607    }
15608  }
15609})();
15610
15611
15612/*!
15613 * Justified Gallery - v3.6.3
15614 * http://miromannino.github.io/Justified-Gallery/
15615 * Copyright (c) 2016 Miro Mannino
15616 * Licensed under the MIT license.
15617 */
15618(function($) {
15619
15620  function hasScrollBar() {
15621    return $("body").height() > $(window).height();
15622  }
15623  /**
15624   * Justified Gallery controller constructor
15625   *
15626   * @param $gallery the gallery to build
15627   * @param settings the settings (the defaults are in $.fn.justifiedGallery.defaults)
15628   * @constructor
15629   */
15630  var JustifiedGallery = function ($gallery, settings) {
15631    
15632    this.settings = settings;
15633    this.checkSettings();
15634
15635    this.imgAnalyzerTimeout = null;
15636    this.entries = null;
15637    this.buildingRow = {
15638      entriesBuff : [],
15639      width : 0,
15640      height : 0,
15641      aspectRatio : 0
15642    };
15643    this.lastFetchedEntry = null;
15644    this.lastAnalyzedIndex = -1;
15645    this.yield = {
15646      every : 2, // do a flush every n flushes (must be greater than 1)
15647      flushed : 0 // flushed rows without a yield
15648    };
15649    this.border = settings.border >= 0 ? settings.border : settings.margins;
15650    this.maxRowHeight = this.retrieveMaxRowHeight();
15651    this.suffixRanges = this.retrieveSuffixRanges();
15652    this.offY = this.border;
15653    this.rows = 0;
15654    this.spinner = {
15655      phase : 0,
15656      timeSlot : 150,
15657      $el : $('<div class="spinner"><span></span><span></span><span></span></div>'),
15658      intervalId : null
15659    };
15660    this.checkWidthIntervalId = null;
15661    this.galleryWidth = $gallery.width();
15662    this.$gallery = $gallery;
15663
15664  };
15665
15666  /** @returns {String} the best suffix given the width and the height */
15667  JustifiedGallery.prototype.getSuffix = function (width, height) {
15668    var longestSide, i;
15669    longestSide = (width > height) ? width : height;
15670    for (i = 0; i < this.suffixRanges.length; i++) {
15671      if (longestSide <= this.suffixRanges[i]) {
15672        return this.settings.sizeRangeSuffixes[this.suffixRanges[i]];
15673      }
15674    }
15675    return this.settings.sizeRangeSuffixes[this.suffixRanges[i - 1]];
15676  };
15677
15678  /**
15679   * Remove the suffix from the string
15680   *
15681   * @returns {string} a new string without the suffix
15682   */
15683  JustifiedGallery.prototype.removeSuffix = function (str, suffix) {
15684    return str.substring(0, str.length - suffix.length);
15685  };
15686
15687  /**
15688   * @returns {boolean} a boolean to say if the suffix is contained in the str or not
15689   */
15690  JustifiedGallery.prototype.endsWith = function (str, suffix) {
15691    return str.indexOf(suffix, str.length - suffix.length) !== -1;
15692  };
15693
15694  /**
15695   * Get the used suffix of a particular url
15696   *
15697   * @param str
15698   * @returns {String} return the used suffix
15699   */
15700  JustifiedGallery.prototype.getUsedSuffix = function (str) {
15701    for (var si in this.settings.sizeRangeSuffixes) {
15702      if (this.settings.sizeRangeSuffixes.hasOwnProperty(si)) {
15703        if (this.settings.sizeRangeSuffixes[si].length === 0) continue;
15704        if (this.endsWith(str, this.settings.sizeRangeSuffixes[si])) return this.settings.sizeRangeSuffixes[si];
15705      }
15706    }
15707    return '';
15708  };
15709
15710  /**
15711   * Given an image src, with the width and the height, returns the new image src with the
15712   * best suffix to show the best quality thumbnail.
15713   *
15714   * @returns {String} the suffix to use
15715   */
15716  JustifiedGallery.prototype.newSrc = function (imageSrc, imgWidth, imgHeight, image) {
15717    var newImageSrc;
15718
15719    if (this.settings.thumbnailPath) {
15720      newImageSrc = this.settings.thumbnailPath(imageSrc, imgWidth, imgHeight, image);
15721    } else {
15722      var matchRes = imageSrc.match(this.settings.extension);
15723      var ext = (matchRes !== null) ? matchRes[0] : '';
15724      newImageSrc = imageSrc.replace(this.settings.extension, '');
15725      newImageSrc = this.removeSuffix(newImageSrc, this.getUsedSuffix(newImageSrc));
15726      newImageSrc += this.getSuffix(imgWidth, imgHeight) + ext;
15727    }
15728
15729    return newImageSrc;
15730  };
15731
15732  /**
15733   * Shows the images that is in the given entry
15734   *
15735   * @param $entry the entry
15736   * @param callback the callback that is called when the show animation is finished
15737   */
15738  JustifiedGallery.prototype.showImg = function ($entry, callback) {
15739    if (this.settings.cssAnimation) {
15740      $entry.addClass('entry-visible');
15741      if (callback) callback();
15742    } else {
15743      $entry.stop().fadeTo(this.settings.imagesAnimationDuration, 1.0, callback);
15744      $entry.find('> img, > a > img').stop().fadeTo(this.settings.imagesAnimationDuration, 1.0, callback);
15745    }
15746  };
15747
15748  /**
15749   * Extract the image src form the image, looking from the 'safe-src', and if it can't be found, from the
15750   * 'src' attribute. It saves in the image data the 'jg.originalSrc' field, with the extracted src.
15751   *
15752   * @param $image the image to analyze
15753   * @returns {String} the extracted src
15754   */
15755  JustifiedGallery.prototype.extractImgSrcFromImage = function ($image) {
15756    var imageSrc = (typeof $image.data('safe-src') !== 'undefined') ? $image.data('safe-src') : $image.attr('src');
15757    $image.data('jg.originalSrc', imageSrc);
15758    return imageSrc;
15759  };
15760
15761  /** @returns {jQuery} the image in the given entry */
15762  JustifiedGallery.prototype.imgFromEntry = function ($entry) {
15763    var $img = $entry.find('> img');
15764    if ($img.length === 0) $img = $entry.find('> a > img');
15765    if ($img.length === 0) $img = $entry.find('.t-entry-visual-cont img');//hacked by Uncode
15766    return $img.length === 0 ? null : $img;
15767  };
15768
15769  /** @returns {jQuery} the caption in the given entry */
15770  JustifiedGallery.prototype.captionFromEntry = function ($entry) {
15771    var $caption = $entry.find('> .caption');
15772    return $caption.length === 0 ? null : $caption;
15773  };
15774
15775  /**
15776   * Display the entry
15777   *
15778   * @param {jQuery} $entry the entry to display
15779   * @param {int} x the x position where the entry must be positioned
15780   * @param y the y position where the entry must be positioned
15781   * @param imgWidth the image width
15782   * @param imgHeight the image height
15783   * @param rowHeight the row height of the row that owns the entry
15784   */
15785  JustifiedGallery.prototype.displayEntry = function ($entry, x, y, imgWidth, imgHeight, rowHeight) {
15786    $entry.width(imgWidth);
15787    $entry.height(Math.floor(rowHeight));//hacked by Uncode
15788    $entry.css('top', Math.floor(y));//hacked by Uncode
15789    $entry.css('left', x);
15790
15791    var $image = this.imgFromEntry($entry);
15792    if ($image !== null) {
15793      $image.css('width', imgWidth);
15794      $image.css('height', imgHeight);
15795      $image.css('margin-left', - imgWidth / 2);
15796      $image.css('margin-top', - imgHeight / 2);
15797
15798      // Image reloading for an high quality of thumbnails
15799      var imageSrc = $image.attr('src');
15800      var newImageSrc = this.newSrc(imageSrc, imgWidth, imgHeight, $image[0]);
15801
15802      $image.one('error', function () {
15803        $image.attr('src', $image.data('jg.originalSrc')); //revert to the original thumbnail, we got it.
15804      });
15805
15806      var loadNewImage = function () {
15807        if (imageSrc !== newImageSrc) { //load the new image after the fadeIn
15808          $image.attr('src', newImageSrc);
15809        }
15810      };
15811
15812      if ($entry.data('jg.loaded') === 'skipped') {
15813        this.onImageEvent(imageSrc, $.proxy(function() {
15814          this.showImg($entry, loadNewImage);
15815          $entry.data('jg.loaded', true);
15816        }, this));
15817      } else {
15818        this.showImg($entry, loadNewImage);
15819      }
15820
15821    } else {
15822      this.showImg($entry);
15823    }
15824
15825    this.displayEntryCaption($entry);
15826  };
15827
15828  /**
15829   * Display the entry caption. If the caption element doesn't exists, it creates the caption using the 'alt'
15830   * or the 'title' attributes.
15831   *
15832   * @param {jQuery} $entry the entry to process
15833   */
15834  JustifiedGallery.prototype.displayEntryCaption = function ($entry) {
15835    var $image = this.imgFromEntry($entry);
15836    if ($image !== null && this.settings.captions) {
15837      var $imgCaption = this.captionFromEntry($entry);
15838
15839      // Create it if it doesn't exists
15840      if ($imgCaption === null) {
15841        var caption = $image.attr('alt');
15842        if (!this.isValidCaption(caption)) caption = $entry.attr('title');
15843        if (this.isValidCaption(caption)) { // Create only we found something
15844          $imgCaption = $('<div class="caption">' + caption + '</div>');
15845          $entry.append($imgCaption);
15846          $entry.data('jg.createdCaption', true);
15847        }
15848      }
15849
15850      // Create events (we check again the $imgCaption because it can be still inexistent)
15851      if ($imgCaption !== null) {
15852        if (!this.settings.cssAnimation) $imgCaption.stop().fadeTo(0, this.settings.captionSettings.nonVisibleOpacity);
15853        this.addCaptionEventsHandlers($entry);
15854      }
15855    } else {
15856      this.removeCaptionEventsHandlers($entry);
15857    }
15858  };
15859
15860  /**
15861   * Validates the caption
15862   *
15863   * @param caption The caption that should be validated
15864   * @return {boolean} Validation result
15865   */
15866  JustifiedGallery.prototype.isValidCaption = function (caption) {
15867    return (typeof caption !== 'undefined' && caption.length > 0);
15868  };
15869
15870  /**
15871   * The callback for the event 'mouseenter'. It assumes that the event currentTarget is an entry.
15872   * It shows the caption using jQuery (or using CSS if it is configured so)
15873   *
15874   * @param {Event} eventObject the event object
15875   */
15876  JustifiedGallery.prototype.onEntryMouseEnterForCaption = function (eventObject) {
15877    var $caption = this.captionFromEntry($(eventObject.currentTarget));
15878    if (this.settings.cssAnimation) {
15879      $caption.addClass('caption-visible').removeClass('caption-hidden');
15880    } else {
15881      $caption.stop().fadeTo(this.settings.captionSettings.animationDuration,
15882          this.settings.captionSettings.visibleOpacity);
15883    }
15884  };
15885
15886  /**
15887   * The callback for the event 'mouseleave'. It assumes that the event currentTarget is an entry.
15888   * It hides the caption using jQuery (or using CSS if it is configured so)
15889   *
15890   * @param {Event} eventObject the event object
15891   */
15892  JustifiedGallery.prototype.onEntryMouseLeaveForCaption = function (eventObject) {
15893    var $caption = this.captionFromEntry($(eventObject.currentTarget));
15894    if (this.settings.cssAnimation) {
15895      $caption.removeClass('caption-visible').removeClass('caption-hidden');
15896    } else {
15897      $caption.stop().fadeTo(this.settings.captionSettings.animationDuration,
15898          this.settings.captionSettings.nonVisibleOpacity);
15899    }
15900  };
15901
15902  /**
15903   * Add the handlers of the entry for the caption
15904   *
15905   * @param $entry the entry to modify
15906   */
15907  JustifiedGallery.prototype.addCaptionEventsHandlers = function ($entry) {
15908    var captionMouseEvents = $entry.data('jg.captionMouseEvents');
15909    if (typeof captionMouseEvents === 'undefined') {
15910      captionMouseEvents = {
15911        mouseenter: $.proxy(this.onEntryMouseEnterForCaption, this),
15912        mouseleave: $.proxy(this.onEntryMouseLeaveForCaption, this)
15913      };
15914      $entry.on('mouseenter', undefined, undefined, captionMouseEvents.mouseenter);
15915      $entry.on('mouseleave', undefined, undefined, captionMouseEvents.mouseleave);
15916      $entry.data('jg.captionMouseEvents', captionMouseEvents);
15917    }
15918  };
15919
15920  /**
15921   * Remove the handlers of the entry for the caption
15922   *
15923   * @param $entry the entry to modify
15924   */
15925  JustifiedGallery.prototype.removeCaptionEventsHandlers = function ($entry) {
15926    var captionMouseEvents = $entry.data('jg.captionMouseEvents');
15927    if (typeof captionMouseEvents !== 'undefined') {
15928      $entry.off('mouseenter', undefined, captionMouseEvents.mouseenter);
15929      $entry.off('mouseleave', undefined, captionMouseEvents.mouseleave);
15930      $entry.removeData('jg.captionMouseEvents');
15931    }
15932  };
15933
15934  /**
15935   * Justify the building row, preparing it to
15936   *
15937   * @param isLastRow
15938   * @returns a boolean to know if the row has been justified or not
15939   */
15940  JustifiedGallery.prototype.prepareBuildingRow = function (isLastRow) {
15941    var i, $entry, imgAspectRatio, newImgW, newImgH, justify = true;
15942    var minHeight = 0;
15943    var availableWidth = this.galleryWidth - 2 * this.border - (
15944        (this.buildingRow.entriesBuff.length - 1) * this.settings.margins);
15945    var rowHeight = Math.floor( availableWidth / this.buildingRow.aspectRatio );//hacked by Uncode
15946    var defaultRowHeight = this.settings.rowHeight;
15947    var justifiable = this.buildingRow.width / availableWidth > this.settings.justifyThreshold;
15948
15949    //Skip the last row if we can't justify it and the lastRow == 'hide'
15950    if (isLastRow && this.settings.lastRow === 'hide' && !justifiable) {
15951      for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
15952        $entry = this.buildingRow.entriesBuff[i];
15953        if (this.settings.cssAnimation)
15954          $entry.removeClass('entry-visible');
15955        else {
15956          $entry.stop().fadeTo(0, 0.1);
15957          $entry.find('> img, > a > img').fadeTo(0, 0);
15958        }
15959      }
15960      return -1;
15961    }
15962
15963    // With lastRow = nojustify, justify if is justificable (the images will not become too big)
15964    if (isLastRow && !justifiable && this.settings.lastRow !== 'justify' && this.settings.lastRow !== 'hide') {
15965      justify = false;
15966
15967      if (this.rows > 0) {
15968        defaultRowHeight = (this.offY - this.border - this.settings.margins * this.rows) / this.rows;
15969        justify = defaultRowHeight * this.buildingRow.aspectRatio / availableWidth > this.settings.justifyThreshold;
15970      }
15971    }
15972
15973    for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
15974      $entry = this.buildingRow.entriesBuff[i];
15975      imgAspectRatio = $entry.data('jg.width') / $entry.data('jg.height');
15976
15977      if (justify) {
15978        newImgW = (i === this.buildingRow.entriesBuff.length - 1) ? availableWidth : rowHeight * imgAspectRatio;
15979        newImgH = rowHeight;
15980      } else {
15981        newImgW = defaultRowHeight * imgAspectRatio;
15982        newImgH = defaultRowHeight;
15983      }
15984
15985      availableWidth -= Math.round(newImgW);
15986      $entry.data('jg.jwidth', Math.round(newImgW));
15987      $entry.data('jg.jheight', Math.ceil(newImgH));
15988      if (i === 0 || minHeight > newImgH) minHeight = newImgH;
15989    }
15990
15991    this.buildingRow.height = minHeight;
15992    return justify;
15993  };
15994
15995  /**
15996   * Clear the building row data to be used for a new row
15997   */
15998  JustifiedGallery.prototype.clearBuildingRow = function () {
15999    this.buildingRow.entriesBuff = [];
16000    this.buildingRow.aspectRatio = 0;
16001    this.buildingRow.width = 0;
16002  };
16003
16004  /**
16005   * Flush a row: justify it, modify the gallery height accordingly to the row height
16006   *
16007   * @param isLastRow
16008   */
16009  JustifiedGallery.prototype.flushRow = function (isLastRow) {
16010    var settings = this.settings;
16011    var $entry, buildingRowRes, offX = this.border, i;
16012
16013    buildingRowRes = this.prepareBuildingRow(isLastRow);
16014    if (isLastRow && settings.lastRow === 'hide' && buildingRowRes === -1) {
16015      this.clearBuildingRow();
16016      return;
16017    }
16018
16019    if (this.maxRowHeight) {
16020      if (this.maxRowHeight.isPercentage && this.maxRowHeight.value * settings.rowHeight < this.buildingRow.height) {
16021        this.buildingRow.height = this.maxRowHeight.value * settings.rowHeight;
16022      } else if (this.maxRowHeight.value >= settings.rowHeight && this.maxRowHeight.value < this.buildingRow.height) {
16023        this.buildingRow.height = this.maxRowHeight.value;
16024      }
16025    }
16026
16027    //Align last (unjustified) row
16028    if (settings.lastRow === 'center' || settings.lastRow === 'right') {
16029      var availableWidth = this.galleryWidth - 2 * this.border - (this.buildingRow.entriesBuff.length - 1) * settings.margins;
16030
16031      for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
16032        $entry = this.buildingRow.entriesBuff[i];
16033        availableWidth -= $entry.data('jg.jwidth');
16034      }
16035
16036      if (settings.lastRow === 'center')
16037        offX += availableWidth / 2;
16038      else if (settings.lastRow === 'right')
16039        offX += availableWidth;
16040    }
16041
16042    for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
16043      $entry = this.buildingRow.entriesBuff[i];
16044      this.displayEntry($entry, offX, this.offY, $entry.data('jg.jwidth'), $entry.data('jg.jheight'), this.buildingRow.height);
16045      offX += $entry.data('jg.jwidth') + settings.margins;
16046    }
16047
16048    //Gallery Height
16049    this.galleryHeightToSet = this.offY + this.buildingRow.height + this.border;
16050    this.$gallery.height(this.galleryHeightToSet + this.getSpinnerHeight());
16051
16052    if (!isLastRow || (this.buildingRow.height <= settings.rowHeight && buildingRowRes)) {
16053      //Ready for a new row
16054      this.offY += this.buildingRow.height + settings.margins;
16055      this.rows += 1;
16056      this.clearBuildingRow();
16057      this.$gallery.trigger('jg.rowflush');
16058    }
16059  };
16060
16061  /**
16062   * Checks the width of the gallery container, to know if a new justification is needed
16063   */
16064  var scrollBarOn = false;
16065  JustifiedGallery.prototype.checkWidth = function () {
16066    this.checkWidthIntervalId = setInterval($.proxy(function () {
16067      var galleryWidth = parseFloat(this.$gallery.width());
16068      if (hasScrollBar() === scrollBarOn) {
16069        if (Math.abs(galleryWidth - this.galleryWidth) > this.settings.refreshSensitivity) {
16070          this.galleryWidth = galleryWidth;
16071          this.rewind();
16072
16073          // Restart to analyze
16074          this.startImgAnalyzer(true);
16075        }
16076      } else {
16077        scrollBarOn = hasScrollBar();
16078        this.galleryWidth = galleryWidth;
16079      }
16080    }, this), this.settings.refreshTime);
16081  };
16082
16083  /**
16084   * @returns {boolean} a boolean saying if the spinner is active or not
16085   */
16086  JustifiedGallery.prototype.isSpinnerActive = function () {
16087    return this.spinner.intervalId !== null;
16088  };
16089
16090  /**
16091   * @returns {int} the spinner height
16092   */
16093  JustifiedGallery.prototype.getSpinnerHeight = function () {
16094    return this.spinner.$el.innerHeight();
16095  };
16096
16097  /**
16098   * Stops the spinner animation and modify the gallery height to exclude the spinner
16099   */
16100  JustifiedGallery.prototype.stopLoadingSpinnerAnimation = function () {
16101    clearInterval(this.spinner.intervalId);
16102    this.spinner.intervalId = null;
16103    this.$gallery.height(this.$gallery.height() - this.getSpinnerHeight());
16104    this.spinner.$el.detach();
16105  };
16106
16107  /**
16108   * Starts the spinner animation
16109   */
16110  JustifiedGallery.prototype.startLoadingSpinnerAnimation = function () {
16111    var spinnerContext = this.spinner;
16112    var $spinnerPoints = spinnerContext.$el.find('span');
16113    clearInterval(spinnerContext.intervalId);
16114    this.$gallery.append(spinnerContext.$el);
16115    this.$gallery.height(this.offY + this.buildingRow.height + this.getSpinnerHeight());
16116    spinnerContext.intervalId = setInterval(function () {
16117      if (spinnerContext.phase < $spinnerPoints.length) {
16118        $spinnerPoints.eq(spinnerContext.phase).fadeTo(spinnerContext.timeSlot, 1);
16119      } else {
vendor: 4,333 bytes, lines 16120-16264
16120        $spinnerPoints.eq(spinnerContext.phase - $spinnerPoints.length).fadeTo(spinnerContext.timeSlot, 0);
16121      }
16122      spinnerContext.phase = (spinnerContext.phase + 1) % ($spinnerPoints.length * 2);
16123    }, spinnerContext.timeSlot);
16124  };
16125
16126  /**
16127   * Rewind the image analysis to start from the first entry.
16128   */
16129  JustifiedGallery.prototype.rewind = function () {
16130    this.lastFetchedEntry = null;
16131    this.lastAnalyzedIndex = -1;
16132    this.offY = this.border;
16133    this.rows = 0;
16134    this.clearBuildingRow();
16135  };
16136
16137  /**
16138   * Update the entries searching it from the justified gallery HTML element
16139   *
16140   * @param norewind if norewind only the new entries will be changed (i.e. randomized, sorted or filtered)
16141   * @returns {boolean} true if some entries has been founded
16142   */
16143  JustifiedGallery.prototype.updateEntries = function (norewind) {
16144    var newEntries;
16145
16146    if (norewind && this.lastFetchedEntry != null) {
16147      newEntries = $(this.lastFetchedEntry).nextAll(this.settings.selector).toArray();
16148    } else {
16149      this.entries = [];
16150      newEntries = this.$gallery.children(this.settings.selector).toArray();
16151    }
16152
16153    if (newEntries.length > 0) {
16154
16155      // Sort or randomize
16156      if ($.isFunction(this.settings.sort)) {
16157        newEntries = this.sortArray(newEntries);
16158      } else if (this.settings.randomize) {
16159        newEntries = this.shuffleArray(newEntries);
16160      }
16161      this.lastFetchedEntry = newEntries[newEntries.length - 1];
16162
16163      // Filter
16164      if (this.settings.filter) {
16165        newEntries = this.filterArray(newEntries);
16166      } else {
16167        this.resetFilters(newEntries);
16168      }
16169
16170    }
16171
16172    this.entries = this.entries.concat(newEntries);
16173    return true;
16174  };
16175
16176  /**
16177   * Apply the entries order to the DOM, iterating the entries and appending the images
16178   *
16179   * @param entries the entries that has been modified and that must be re-ordered in the DOM
16180   */
16181  JustifiedGallery.prototype.insertToGallery = function (entries) {
16182    var that = this;
16183    $.each(entries, function () {
16184      $(this).appendTo(that.$gallery);
16185    });
16186  };
16187
16188  /**
16189   * Shuffle the array using the Fisher-Yates shuffle algorithm
16190   *
16191   * @param a the array to shuffle
16192   * @return the shuffled array
16193   */
16194  JustifiedGallery.prototype.shuffleArray = function (a) {
16195    var i, j, temp;
16196    for (i = a.length - 1; i > 0; i--) {
16197      j = Math.floor(Math.random() * (i + 1));
16198      temp = a[i];
16199      a[i] = a[j];
16200      a[j] = temp;
16201    }
16202    this.insertToGallery(a);
16203    return a;
16204  };
16205
16206  /**
16207   * Sort the array using settings.comparator as comparator
16208   *
16209   * @param a the array to sort (it is sorted)
16210   * @return the sorted array
16211   */
16212  JustifiedGallery.prototype.sortArray = function (a) {
16213    a.sort(this.settings.sort);
16214    this.insertToGallery(a);
16215    return a;
16216  };
16217
16218  /**
16219   * Reset the filters removing the 'jg-filtered' class from all the entries
16220   *
16221   * @param a the array to reset
16222   */
16223  JustifiedGallery.prototype.resetFilters = function (a) {
16224    for (var i = 0; i < a.length; i++) $(a[i]).removeClass('jg-filtered');
16225  };
16226
16227  /**
16228   * Filter the entries considering theirs classes (if a string has been passed) or using a function for filtering.
16229   *
16230   * @param a the array to filter
16231   * @return the filtered array
16232   */
16233  JustifiedGallery.prototype.filterArray = function (a) {
16234    var settings = this.settings;
16235    if ($.type(settings.filter) === 'string') {
16236      // Filter only keeping the entries passed in the string
16237      return a.filter(function (el) {
16238        var $el = $(el);
16239        if ($el.is(settings.filter)) {
16240          $el.removeClass('jg-filtered');
16241          return true;
16242        } else {
16243          $el.addClass('jg-filtered').removeClass('jg-visible');
16244          return false;
16245        }
16246      });
16247    } else if ($.isFunction(settings.filter)) {
16248      // Filter using the passed function
16249      var filteredArr = a.filter(settings.filter);
16250      for (var i = 0; i < a.length; i++) {
16251        if (filteredArr.indexOf(a[i]) == -1) {
16252          $(a[i]).addClass('jg-filtered').removeClass('jg-visible');
16253        } else {
16254          $(a[i]).removeClass('jg-filtered');
16255        }
16256      }
16257      return filteredArr;
16258    }
16259  };
16260
16261  /**
16262   * Destroy the Justified Gallery instance.
16263   *
16264   * It clears all the css properties added in the style attributes. We doesn't backup the original
16265   * values for those css attributes, because it costs (performance) and because in general one
16266   * shouldn't use the style attribute for an uniform set of images (where we suppose the use of
16267   * classes). Creating a backup is also difficult because JG could be called multiple times and
16268   * with different style attributes.
16269   */
16270  JustifiedGallery.prototype.destroy = function () {
16271    clearInterval(this.checkWidthIntervalId);
16272
16273    $.each(this.entries, $.proxy(function(_, entry) {
16274      var $entry = $(entry);
16275
16276      // Reset entry style
16277      $entry.css('width', '');
16278      $entry.css('height', '');
16279      $entry.css('top', '');
16280      $entry.css('left', '');
16281      $entry.data('jg.loaded', undefined);
16282      $entry.removeClass('jg-entry');
16283
16284      // Reset image style
16285      var $img = this.imgFromEntry($entry);
16286      $img.css('width', '');
16287      $img.css('height', '');
16288      $img.css('margin-left', '');
16289      $img.css('margin-top', '');
16290      $img.attr('src', $img.data('jg.originalSrc'));
16291      $img.data('jg.originalSrc', undefined);
16292
16293      // Remove caption
16294      this.removeCaptionEventsHandlers($entry);
16295      var $caption = this.captionFromEntry($entry);
16296      if ($entry.data('jg.createdCaption')) {
16297        // remove also the caption element (if created by jg)
16298        $entry.data('jg.createdCaption', undefined);
16299        if ($caption !== null) $caption.remove();
16300      } else {
16301        if ($caption !== null) $caption.fadeTo(0, 1);
16302      }
16303
16304    }, this));
16305
16306    this.$gallery.css('height', '');
16307    this.$gallery.removeClass('justified-gallery');
16308    this.$gallery.data('jg.controller', undefined);
16309  };
16310
16311  /**
16312   * Analyze the images and builds the rows. It returns if it found an image that is not loaded.
16313   *
16314   * @param isForResize if the image analyzer is called for resizing or not, to call a different callback at the end
16315   */
16316  JustifiedGallery.prototype.analyzeImages = function (isForResize) {
16317    for (var i = this.lastAnalyzedIndex + 1; i < this.entries.length; i++) {
16318      var $entry = $(this.entries[i]);
16319      if ($entry.data('jg.loaded') === true || $entry.data('jg.loaded') === 'skipped') {
16320        var availableWidth = this.galleryWidth - 2 * this.border - (
16321            (this.buildingRow.entriesBuff.length - 1) * this.settings.margins);
16322        var imgAspectRatio = $entry.data('jg.width') / $entry.data('jg.height');
16323        if (availableWidth / (this.buildingRow.aspectRatio + imgAspectRatio) < this.settings.rowHeight) {
16324          this.flushRow(false);
16325          if(++this.yield.flushed >= this.yield.every) {
16326            this.startImgAnalyzer(isForResize);
16327            return;
16328          }
16329        }
16330
16331        this.buildingRow.entriesBuff.push($entry);
16332        this.buildingRow.aspectRatio += imgAspectRatio;
16333        this.buildingRow.width += imgAspectRatio * this.settings.rowHeight;
16334        this.lastAnalyzedIndex = i;
16335
16336      } else if ($entry.data('jg.loaded') !== 'error') {
16337        return;
16338      }
16339    }
16340
16341    // Last row flush (the row is not full)
16342    if (this.buildingRow.entriesBuff.length > 0) this.flushRow(true);
16343
16344    if (this.isSpinnerActive()) {
16345      this.stopLoadingSpinnerAnimation();
16346    }
16347
16348    /* Stop, if there is, the timeout to start the analyzeImages.
16349     This is because an image can be set loaded, and the timeout can be set,
16350     but this image can be analyzed yet.
16351     */
16352    this.stopImgAnalyzerStarter();
16353
16354    //On complete callback
16355    this.$gallery.trigger(isForResize ? 'jg.resize' : 'jg.complete');
16356    this.$gallery.height(this.galleryHeightToSet);
16357  };
16358
16359  /**
16360   * Stops any ImgAnalyzer starter (that has an assigned timeout)
16361   */
16362  JustifiedGallery.prototype.stopImgAnalyzerStarter = function () {
16363    this.yield.flushed = 0;
16364    if (this.imgAnalyzerTimeout !== null) clearTimeout(this.imgAnalyzerTimeout);
16365  };
16366
16367  /**
16368   * Starts the image analyzer. It is not immediately called to let the browser to update the view
16369   *
16370   * @param isForResize specifies if the image analyzer must be called for resizing or not
16371   */
16372  JustifiedGallery.prototype.startImgAnalyzer = function (isForResize) {
16373    var that = this;
16374    this.stopImgAnalyzerStarter();
16375    this.imgAnalyzerTimeout = setTimeout(function () {
16376      that.analyzeImages(isForResize);
16377    }, 0.001); // we can't start it immediately due to a IE different behaviour
16378  };
16379
16380  /**
16381   * Checks if the image is loaded or not using another image object. We cannot use the 'complete' image property,
16382   * because some browsers, with a 404 set complete = true.
16383   *
16384   * @param imageSrc the image src to load
16385   * @param onLoad callback that is called when the image has been loaded
vendor: 13,320 bytes, lines 16386-16736
16386   * @param onError callback that is called in case of an error
16387   */
16388  JustifiedGallery.prototype.onImageEvent = function (imageSrc, onLoad, onError) {
16389    if (!onLoad && !onError) return;
16390
16391    var memImage = new Image();
16392    var $memImage = $(memImage);
16393    if (onLoad) {
16394      $memImage.one('load', function () {
16395        $memImage.off('load error');
16396        onLoad(memImage);
16397      });
16398    }
16399    if (onError) {
16400      $memImage.one('error', function() {
16401        $memImage.off('load error');
16402        onError(memImage);
16403      });
16404    }
16405    memImage.src = imageSrc;
16406  };
16407
16408  /**
16409   * Init of Justified Gallery controlled
16410   * It analyzes all the entries starting theirs loading and calling the image analyzer (that works with loaded images)
16411   */
16412  JustifiedGallery.prototype.init = function () {
16413    var imagesToLoad = false, skippedImages = false, that = this;
16414    $.each(this.entries, function (index, entry) {
16415      var $entry = $(entry);
16416      var $image = that.imgFromEntry($entry);
16417
16418      $entry.addClass('jg-entry');
16419
16420      if ($entry.data('jg.loaded') !== true && $entry.data('jg.loaded') !== 'skipped') {
16421
16422        // Link Rel global overwrite
16423        if (that.settings.rel !== null) $entry.attr('rel', that.settings.rel);
16424
16425        // Link Target global overwrite
16426        if (that.settings.target !== null) $entry.attr('target', that.settings.target);
16427
16428        if ($image !== null) {
16429
16430          // Image src
16431          var imageSrc = that.extractImgSrcFromImage($image);
16432          $image.attr('src', imageSrc);
16433
16434          /* If we have the height and the width, we don't wait that the image is loaded, but we start directly
16435           * with the justification */
16436          if (that.settings.waitThumbnailsLoad === false) {
16437            var width = parseFloat($image.attr('width'));
16438            var height = parseFloat($image.attr('height'));
16439            if (!isNaN(width) && !isNaN(height)) {
16440              $entry.data('jg.width', width);
16441              $entry.data('jg.height', height);
16442              $entry.data('jg.loaded', 'skipped');
16443              skippedImages = true;
16444              that.startImgAnalyzer(false);
16445              return true; // continue
16446            }
16447          }
16448
16449          $entry.data('jg.loaded', false);
16450          imagesToLoad = true;
16451
16452          // Spinner start
16453          if (!that.isSpinnerActive()) that.startLoadingSpinnerAnimation();
16454
16455          that.onImageEvent(imageSrc, function (loadImg) { // image loaded
16456            $entry.data('jg.width', loadImg.width);
16457            $entry.data('jg.height', loadImg.height);
16458            $entry.data('jg.loaded', true);
16459            that.startImgAnalyzer(false);
16460          }, function () { // image load error
16461            $entry.data('jg.loaded', 'error');
16462            that.startImgAnalyzer(false);
16463          });
16464
16465        } else {
16466          $entry.data('jg.loaded', true);
16467          $entry.data('jg.width', $entry.width() | parseFloat($entry.css('width')) | 1);
16468          $entry.data('jg.height', $entry.height() | parseFloat($entry.css('height')) | 1);
16469        }
16470
16471      }
16472
16473    });
16474
16475    if (!imagesToLoad && !skippedImages) this.startImgAnalyzer(false);
16476    this.checkWidth();
16477  };
16478
16479  /**
16480   * Checks that it is a valid number. If a string is passed it is converted to a number
16481   *
16482   * @param settingContainer the object that contains the setting (to allow the conversion)
16483   * @param settingName the setting name
16484   */
16485  JustifiedGallery.prototype.checkOrConvertNumber = function (settingContainer, settingName) {
16486    if ($.type(settingContainer[settingName]) === 'string') {
16487      settingContainer[settingName] = parseFloat(settingContainer[settingName]);
16488    }
16489
16490    if ($.type(settingContainer[settingName]) === 'number') {
16491      if (isNaN(settingContainer[settingName])) throw 'invalid number for ' + settingName;
16492    } else {
16493      throw settingName + ' must be a number';
16494    }
16495  };
16496
16497  /**
16498   * Checks the sizeRangeSuffixes and, if necessary, converts
16499   * its keys from string (e.g. old settings with 'lt100') to int.
16500   */
16501  JustifiedGallery.prototype.checkSizeRangesSuffixes = function () {
16502    if ($.type(this.settings.sizeRangeSuffixes) !== 'object') {
16503      throw 'sizeRangeSuffixes must be defined and must be an object';
16504    }
16505
16506    var suffixRanges = [];
16507    for (var rangeIdx in this.settings.sizeRangeSuffixes) {
16508      if (this.settings.sizeRangeSuffixes.hasOwnProperty(rangeIdx)) suffixRanges.push(rangeIdx);
16509    }
16510
16511    var newSizeRngSuffixes = {0: ''};
16512    for (var i = 0; i < suffixRanges.length; i++) {
16513      if ($.type(suffixRanges[i]) === 'string') {
16514        try {
16515          var numIdx = parseInt(suffixRanges[i].replace(/^[a-z]+/, ''), 10);
16516          newSizeRngSuffixes[numIdx] = this.settings.sizeRangeSuffixes[suffixRanges[i]];
16517        } catch (e) {
16518          throw 'sizeRangeSuffixes keys must contains correct numbers (' + e + ')';
16519        }
16520      } else {
16521        newSizeRngSuffixes[suffixRanges[i]] = this.settings.sizeRangeSuffixes[suffixRanges[i]];
16522      }
16523    }
16524
16525    this.settings.sizeRangeSuffixes = newSizeRngSuffixes;
16526  };
16527
16528  /**
16529   * check and convert the maxRowHeight setting
16530   */
16531  JustifiedGallery.prototype.retrieveMaxRowHeight = function () {
16532    var newMaxRowHeight = { };
16533
16534    if ($.type(this.settings.maxRowHeight) === 'string') {
16535      if (this.settings.maxRowHeight.match(/^[0-9]+%$/)) {
16536        newMaxRowHeight.value = parseFloat(this.settings.maxRowHeight.match(/^([0-9]+)%$/)[1]) / 100;
16537        newMaxRowHeight.isPercentage = false;
16538      } else {
16539        newMaxRowHeight.value = parseFloat(this.settings.maxRowHeight);
16540        newMaxRowHeight.isPercentage = true;
16541      }
16542    } else if ($.type(this.settings.maxRowHeight) === 'number') {
16543      newMaxRowHeight.value = this.settings.maxRowHeight;
16544      newMaxRowHeight.isPercentage = false;
16545    } else if (this.settings.maxRowHeight === false ||
16546        this.settings.maxRowHeight === null ||
16547        typeof this.settings.maxRowHeight == 'undefined') {
16548      return null;
16549    } else {
16550      throw 'maxRowHeight must be a number or a percentage';
16551    }
16552
16553    // check if the converted value is not a number
16554    if (isNaN(newMaxRowHeight.value)) throw 'invalid number for maxRowHeight';
16555
16556    // check values
16557    if (newMaxRowHeight.isPercentage) {
16558      if (newMaxRowHeight.value < 100) newMaxRowHeight.value = 100;
16559    }
16560
16561    return newMaxRowHeight;
16562  };
16563
16564  /**
16565   * Checks the settings
16566   */
16567  JustifiedGallery.prototype.checkSettings = function () {
16568    this.checkSizeRangesSuffixes();
16569
16570    this.checkOrConvertNumber(this.settings, 'rowHeight');
16571    this.checkOrConvertNumber(this.settings, 'margins');
16572    this.checkOrConvertNumber(this.settings, 'border');
16573
16574    var lastRowModes = [
16575      'justify',
16576      'nojustify',
16577      'left',
16578      'center',
16579      'right',
16580      'hide'
16581    ];
16582    if (lastRowModes.indexOf(this.settings.lastRow) === -1) {
16583      throw 'lastRow must be one of: ' + lastRowModes.join(', ');
16584    }
16585
16586    this.checkOrConvertNumber(this.settings, 'justifyThreshold');
16587    if (this.settings.justifyThreshold < 0 || this.settings.justifyThreshold > 1) {
16588      throw 'justifyThreshold must be in the interval [0,1]';
16589    }
16590    if ($.type(this.settings.cssAnimation) !== 'boolean') {
16591      throw 'cssAnimation must be a boolean';
16592    }
16593
16594    if ($.type(this.settings.captions) !== 'boolean') throw 'captions must be a boolean';
16595    this.checkOrConvertNumber(this.settings.captionSettings, 'animationDuration');
16596
16597    this.checkOrConvertNumber(this.settings.captionSettings, 'visibleOpacity');
16598    if (this.settings.captionSettings.visibleOpacity < 0 ||
16599        this.settings.captionSettings.visibleOpacity > 1) {
16600      throw 'captionSettings.visibleOpacity must be in the interval [0, 1]';
16601    }
16602
16603    this.checkOrConvertNumber(this.settings.captionSettings, 'nonVisibleOpacity');
16604    if (this.settings.captionSettings.nonVisibleOpacity < 0 ||
16605        this.settings.captionSettings.nonVisibleOpacity > 1) {
16606      throw 'captionSettings.nonVisibleOpacity must be in the interval [0, 1]';
16607    }
16608
16609    this.checkOrConvertNumber(this.settings, 'imagesAnimationDuration');
16610    this.checkOrConvertNumber(this.settings, 'refreshTime');
16611    this.checkOrConvertNumber(this.settings, 'refreshSensitivity');
16612    if ($.type(this.settings.randomize) !== 'boolean') throw 'randomize must be a boolean';
16613    if ($.type(this.settings.selector) !== 'string') throw 'selector must be a string';
16614
16615    if (this.settings.sort !== false && !$.isFunction(this.settings.sort)) {
16616      throw 'sort must be false or a comparison function';
16617    }
16618
16619    if (this.settings.filter !== false && !$.isFunction(this.settings.filter) &&
16620        $.type(this.settings.filter) !== 'string') {
16621      throw 'filter must be false, a string or a filter function';
16622    }
16623  };
16624
16625  /**
16626   * It brings all the indexes from the sizeRangeSuffixes and it orders them. They are then sorted and returned.
16627   * @returns {Array} sorted suffix ranges
16628   */
16629  JustifiedGallery.prototype.retrieveSuffixRanges = function () {
16630    var suffixRanges = [];
16631    for (var rangeIdx in this.settings.sizeRangeSuffixes) {
16632      if (this.settings.sizeRangeSuffixes.hasOwnProperty(rangeIdx)) suffixRanges.push(parseInt(rangeIdx, 10));
16633    }
16634    suffixRanges.sort(function (a, b) { return a > b ? 1 : a < b ? -1 : 0; });
16635    return suffixRanges;
16636  };
16637
16638  /**
16639   * Update the existing settings only changing some of them
16640   *
16641   * @param newSettings the new settings (or a subgroup of them)
16642   */
16643  JustifiedGallery.prototype.updateSettings = function (newSettings) {
16644    // In this case Justified Gallery has been called again changing only some options
16645    this.settings = $.extend({}, this.settings, newSettings);
16646    this.checkSettings();
16647
16648    // As reported in the settings: negative value = same as margins, 0 = disabled
16649    this.border = this.settings.border >= 0 ? this.settings.border : this.settings.margins;
16650
16651    this.maxRowHeight = this.retrieveMaxRowHeight();
16652    this.suffixRanges = this.retrieveSuffixRanges();
16653  };
16654
16655  /**
16656   * Justified Gallery plugin for jQuery
16657   *
16658   * Events
16659   *  - jg.complete : called when all the gallery has been created
16660   *  - jg.resize : called when the gallery has been resized
16661   *  - jg.rowflush : when a new row appears
16662   *
16663   * @param arg the action (or the settings) passed when the plugin is called
16664   * @returns {*} the object itself
16665   */
16666  $.fn.justifiedGallery = function (arg) {
16667    return this.each(function (index, gallery) {
16668
16669      var $gallery = $(gallery);
16670      $gallery.addClass('justified-gallery');
16671
16672      var controller = $gallery.data('jg.controller');
16673      if (typeof controller === 'undefined') {
16674        // Create controller and assign it to the object data
16675        if (typeof arg !== 'undefined' && arg !== null && $.type(arg) !== 'object') {
16676          if (arg === 'destroy') return; // Just a call to an unexisting object
16677          throw 'The argument must be an object';
16678        }
16679        controller = new JustifiedGallery($gallery, $.extend({}, $.fn.justifiedGallery.defaults, arg));
16680        $gallery.data('jg.controller', controller);
16681      } else if (arg === 'norewind') {
16682        // In this case we don't rewind: we analyze only the latest images (e.g. to complete the last unfinished row
16683        // ... left to be more readable
16684      } else if (arg === 'destroy') {
16685        controller.destroy();
16686        return;
16687      } else {
16688        // In this case Justified Gallery has been called again changing only some options
16689        controller.updateSettings(arg);
16690        controller.rewind();
16691      }
16692
16693      // Update the entries list
16694      if (!controller.updateEntries(arg === 'norewind')) return;
16695
16696      // Init justified gallery
16697      controller.init();
16698
16699    });
16700  };
16701
16702  // Default options
16703  $.fn.justifiedGallery.defaults = {
16704    sizeRangeSuffixes: { }, /* e.g. Flickr configuration
16705        {
16706          100: '_t',  // used when longest is less than 100px
16707          240: '_m',  // used when longest is between 101px and 240px
16708          320: '_n',  // ...
16709          500: '',
16710          640: '_z',
16711          1024: '_b'  // used as else case because it is the last
16712        }
16713    */
16714    thumbnailPath: undefined, /* If defined, sizeRangeSuffixes is not used, and this function is used to determine the
16715    path relative to a specific thumbnail size. The function should accept respectively three arguments:
16716    current path, width and height */
16717    rowHeight: 120,
16718    maxRowHeight: false, // false or negative value to deactivate. Positive number to express the value in pixels,
16719                         // A string '[0-9]+%' to express in percentage (e.g. 300% means that the row height
16720                         // can't exceed 3 * rowHeight)
16721    margins: 1,
16722    border: -1, // negative value = same as margins, 0 = disabled, any other value to set the border
16723
16724    lastRow: 'nojustify', // … which is the same as 'left', or can be 'justify', 'center', 'right' or 'hide'
16725
16726    justifyThreshold: 0.90, /* if row width / available space > 0.90 it will be always justified
16727                             * (i.e. lastRow setting is not considered) */
16728    waitThumbnailsLoad: true,
16729    captions: true,
16730    cssAnimation: true,
16731    imagesAnimationDuration: 500, // ignored with css animations
16732    captionSettings: { // ignored with css animations
16733      animationDuration: 500,
16734      visibleOpacity: 0.7,
16735      nonVisibleOpacity: 0.0
16736    },
vendor: 9,476 bytes, lines 16737-17007
16737    rel: null, // rewrite the rel of each analyzed links
16738    target: null, // rewrite the target of all links
16739    extension: /\.[^.\\/]+$/, // regexp to capture the extension of an image
16740    refreshTime: 200, // time interval (in ms) to check if the page changes its width
16741    refreshSensitivity: 0, // change in width allowed (in px) without re-building the gallery
16742    randomize: false,
16743    sort: false, /*
16744      - false: to do not sort
16745      - function: to sort them using the function as comparator (see Array.prototype.sort())
16746    */
16747    filter: false, /*
16748      - false, null or undefined: for a disabled filter
16749      - a string: an entry is kept if entry.is(filter string) returns true
16750                  see jQuery's .is() function for further information
16751      - a function: invoked with arguments (entry, index, array). Return true to keep the entry, false otherwise.
16752                    It follows the specifications of the Array.prototype.filter() function of JavaScript.
16753    */
16754    selector: 'a, div:not(.spinner)' // The selector that is used to know what are the entries of the gallery
16755  };
16756
16757}(jQuery));
16758
16759/*!
16760* Customized version of iScroll.js 0.0.1
16761* It fixes bugs affecting its integration with fullpage.js
16762*/
16763/*! iScroll v5.2.0 ~ (c) 2008-2016 Matteo Spinelli ~ http://cubiq.org/license */
16764(function (window, document, Math) {
16765var rAF = window.requestAnimationFrame  ||
16766    window.webkitRequestAnimationFrame  ||
16767    window.mozRequestAnimationFrame     ||
16768    window.oRequestAnimationFrame       ||
16769    window.msRequestAnimationFrame      ||
16770    function (callback) { window.setTimeout(callback, 1000 / 60); };
16771
16772var utils = (function () {
16773    var me = {};
16774
16775    var _elementStyle = document.createElement('div').style;
16776    var _vendor = (function () {
16777        var vendors = ['t', 'webkitT', 'MozT', 'msT', 'OT'],
16778            transform,
16779            i = 0,
16780            l = vendors.length;
16781
16782        for ( ; i < l; i++ ) {
16783            transform = vendors[i] + 'ransform';
16784            if ( transform in _elementStyle ) return vendors[i].substr(0, vendors[i].length-1);
16785        }
16786
16787        return false;
16788    })();
16789
16790    function _prefixStyle (style) {
16791        if ( _vendor === false ) return false;
16792        if ( _vendor === '' ) return style;
16793        return _vendor + style.charAt(0).toUpperCase() + style.substr(1);
16794    }
16795
16796    me.getTime = Date.now || function getTime () { return new Date().getTime(); };
16797
16798    me.extend = function (target, obj) {
16799        for ( var i in obj ) {
16800            target[i] = obj[i];
16801        }
16802    };
16803
16804    me.addEvent = function (el, type, fn, capture) {
16805        el.addEventListener(type, fn, !!capture);
16806    };
16807
16808    me.removeEvent = function (el, type, fn, capture) {
16809        el.removeEventListener(type, fn, !!capture);
16810    };
16811
16812    me.prefixPointerEvent = function (pointerEvent) {
16813        return window.MSPointerEvent ?
16814            'MSPointer' + pointerEvent.charAt(7).toUpperCase() + pointerEvent.substr(8):
16815            pointerEvent;
16816    };
16817
16818    me.momentum = function (current, start, time, lowerMargin, wrapperSize, deceleration) {
16819        var distance = current - start,
16820            speed = Math.abs(distance) / time,
16821            destination,
16822            duration;
16823
16824        deceleration = deceleration === undefined ? 0.0006 : deceleration;
16825
16826        destination = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 );
16827        duration = speed / deceleration;
16828
16829        if ( destination < lowerMargin ) {
16830            destination = wrapperSize ? lowerMargin - ( wrapperSize / 2.5 * ( speed / 8 ) ) : lowerMargin;
16831            distance = Math.abs(destination - current);
16832            duration = distance / speed;
16833        } else if ( destination > 0 ) {
16834            destination = wrapperSize ? wrapperSize / 2.5 * ( speed / 8 ) : 0;
16835            distance = Math.abs(current) + destination;
16836            duration = distance / speed;
16837        }
16838
16839        return {
16840            destination: Math.round(destination),
16841            duration: duration
16842        };
16843    };
16844
16845    var _transform = _prefixStyle('transform');
16846
16847    me.extend(me, {
16848        hasTransform: _transform !== false,
16849        hasPerspective: _prefixStyle('perspective') in _elementStyle,
16850        hasTouch: 'ontouchstart' in window,
16851        hasPointer: !!(window.PointerEvent || window.MSPointerEvent), // IE10 is prefixed
16852        hasTransition: _prefixStyle('transition') in _elementStyle
16853    });
16854
16855    /*
16856    This should find all Android browsers lower than build 535.19 (both stock browser and webview)
16857    - galaxy S2 is ok
16858    - 2.3.6 : `AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1`
16859    - 4.0.4 : `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
16860   - galaxy S3 is badAndroid (stock brower, webview)
16861     `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
16862   - galaxy S4 is badAndroid (stock brower, webview)
16863     `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
16864   - galaxy S5 is OK
16865     `AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36 (Chrome/)`
16866   - galaxy S6 is OK
16867     `AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36 (Chrome/)`
16868  */
16869    me.isBadAndroid = (function() {
16870        var appVersion = window.navigator.appVersion;
16871        // Android browser is not a chrome browser.
16872        if (/Android/.test(appVersion) && !(/Chrome\/\d/.test(appVersion))) {
16873            var safariVersion = appVersion.match(/Safari\/(\d+.\d)/);
16874            if(safariVersion && typeof safariVersion === "object" && safariVersion.length >= 2) {
16875                return parseFloat(safariVersion[1]) < 535.19;
16876            } else {
16877                return true;
16878            }
16879        } else {
16880            return false;
16881        }
16882    })();
16883
16884    me.extend(me.style = {}, {
16885        transform: _transform,
16886        transitionTimingFunction: _prefixStyle('transitionTimingFunction'),
16887        transitionDuration: _prefixStyle('transitionDuration'),
16888        transitionDelay: _prefixStyle('transitionDelay'),
16889        transformOrigin: _prefixStyle('transformOrigin')
16890    });
16891
16892    me.hasClass = function (e, c) {
16893        var re = new RegExp("(^|\\s)" + c + "(\\s|$)");
16894        return re.test(e.className);
16895    };
16896
16897    me.addClass = function (e, c) {
16898        if ( me.hasClass(e, c) ) {
16899            return;
16900        }
16901
16902        var newclass = e.className.split(' ');
16903        newclass.push(c);
16904        e.className = newclass.join(' ');
16905    };
16906
16907    me.removeClass = function (e, c) {
16908        if ( !me.hasClass(e, c) ) {
16909            return;
16910        }
16911
16912        var re = new RegExp("(^|\\s)" + c + "(\\s|$)", 'g');
16913        e.className = e.className.replace(re, ' ');
16914    };
16915
16916    me.offset = function (el) {
16917        var left = -el.offsetLeft,
16918            top = -el.offsetTop;
16919
16920        // jshint -W084
16921        while (el = el.offsetParent) {
16922            left -= el.offsetLeft;
16923            top -= el.offsetTop;
16924        }
16925        // jshint +W084
16926
16927        return {
16928            left: left,
16929            top: top
16930        };
16931    };
16932
16933    me.preventDefaultException = function (el, exceptions) {
16934        for ( var i in exceptions ) {
16935            if ( exceptions[i].test(el[i]) ) {
16936                return true;
16937            }
16938        }
16939
16940        return false;
16941    };
16942
16943    me.extend(me.eventType = {}, {
16944        touchstart: 1,
16945        touchmove: 1,
16946        touchend: 1,
16947
16948        mousedown: 2,
16949        mousemove: 2,
16950        mouseup: 2,
16951
16952        pointerdown: 3,
16953        pointermove: 3,
16954        pointerup: 3,
16955
16956        MSPointerDown: 3,
16957        MSPointerMove: 3,
16958        MSPointerUp: 3
16959    });
16960
16961    me.extend(me.ease = {}, {
16962        quadratic: {
16963            style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
16964            fn: function (k) {
16965                return k * ( 2 - k );
16966            }
16967        },
16968        circular: {
16969            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)
16970            fn: function (k) {
16971                return Math.sqrt( 1 - ( --k * k ) );
16972            }
16973        },
16974        back: {
16975            style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',
16976            fn: function (k) {
16977                var b = 4;
16978                return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1;
16979            }
16980        },
16981        bounce: {
16982            style: '',
16983            fn: function (k) {
16984                if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {
16985                    return 7.5625 * k * k;
16986                } else if ( k < ( 2 / 2.75 ) ) {
16987                    return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;
16988                } else if ( k < ( 2.5 / 2.75 ) ) {
16989                    return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;
16990                } else {
16991                    return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;
16992                }
16993            }
16994        },
16995        elastic: {
16996            style: '',
16997            fn: function (k) {
16998                var f = 0.22,
16999                    e = 0.4;
17000
17001                if ( k === 0 ) { return 0; }
17002                if ( k == 1 ) { return 1; }
17003
17004                return ( e * Math.pow( 2, - 10 * k ) * Math.sin( ( k - f / 4 ) * ( 2 * Math.PI ) / f ) + 1 );
17005            }
17006        }
17007    });
17008
17009    me.tap = function (e, eventName) {
17010        var ev = document.createEvent('Event');
17011        ev.initEvent(eventName, true, true);
17012        ev.pageX = e.pageX;
17013        ev.pageY = e.pageY;
17014        e.target.dispatchEvent(ev);
17015    };
17016
17017    me.click = function (e) {
17018        var target = e.target,
17019            ev;
17020
17021        if ( !(/(SELECT|INPUT|TEXTAREA)/i).test(target.tagName) ) {
17022            // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/initMouseEvent
17023            // initMouseEvent is deprecated.
17024            ev = document.createEvent(window.MouseEvent ? 'MouseEvents' : 'Event');
17025            ev.initEvent('click', true, true);
17026            ev.view = e.view || window;
17027            ev.detail = 1;
17028            ev.screenX = target.screenX || 0;
17029            ev.screenY = target.screenY || 0;
17030            ev.clientX = target.clientX || 0;
17031            ev.clientY = target.clientY || 0;
17032            ev.ctrlKey = !!e.ctrlKey;
17033            ev.altKey = !!e.altKey;
17034            ev.shiftKey = !!e.shiftKey;
17035            ev.metaKey = !!e.metaKey;
17036            ev.button = 0;
17037            ev.relatedTarget = null;
17038            ev._constructed = true;
17039            target.dispatchEvent(ev);
17040        }
17041    };
17042
17043    return me;
17044})();
17045function IScroll (el, options) {
17046    this.wrapper = typeof el == 'string' ? document.querySelector(el) : el;
17047    this.scroller = this.wrapper.children[0];
17048    this.scrollerStyle = this.scroller.style;       // cache style for better performance
17049
17050    this.options = {
17051
17052        resizeScrollbars: true,
17053
17054        mouseWheelSpeed: 20,
17055
17056        snapThreshold: 0.334,
17057
17058// INSERT POINT: OPTIONS
17059        disablePointer : !utils.hasPointer,
17060        disableTouch : utils.hasPointer || !utils.hasTouch,
17061        disableMouse : utils.hasPointer || utils.hasTouch,
17062        startX: 0,
17063        startY: 0,
17064        scrollY: true,
17065        directionLockThreshold: 5,
17066        momentum: true,
17067
17068        bounce: true,
17069        bounceTime: 600,
17070        bounceEasing: '',
17071
17072        preventDefault: true,
17073        preventDefaultException: { tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT|LABEL)$/ },
17074
17075        HWCompositing: true,
17076        useTransition: true,
17077        useTransform: true,
17078        bindToWrapper: typeof window.onmousedown === "undefined"
17079    };
17080
17081    for ( var i in options ) {
17082        this.options[i] = options[i];
17083    }
17084
17085    // Normalize options
17086    this.translateZ = this.options.HWCompositing && utils.hasPerspective ? ' translateZ(0)' : '';
17087
17088    this.options.useTransition = utils.hasTransition && this.options.useTransition;
17089    this.options.useTransform = utils.hasTransform && this.options.useTransform;
17090
17091    this.options.eventPassthrough = this.options.eventPassthrough === true ? 'vertical' : this.options.eventPassthrough;
17092    this.options.preventDefault = !this.options.eventPassthrough && this.options.preventDefault;
17093
17094    // If you want eventPassthrough I have to lock one of the axes
17095    this.options.scrollY = this.options.eventPassthrough == 'vertical' ? false : this.options.scrollY;
17096    this.options.scrollX = this.options.eventPassthrough == 'horizontal' ? false : this.options.scrollX;
17097
17098    // With eventPassthrough we also need lockDirection mechanism
17099    this.options.freeScroll = this.options.freeScroll && !this.options.eventPassthrough;
17100    this.options.directionLockThreshold = this.options.eventPassthrough ? 0 : this.options.directionLockThreshold;
17101
17102    this.options.bounceEasing = typeof this.options.bounceEasing == 'string' ? utils.ease[this.options.bounceEasing] || utils.ease.circular : this.options.bounceEasing;
17103
17104    this.options.resizePolling = this.options.resizePolling === undefined ? 60 : this.options.resizePolling;
17105
17106    if ( this.options.tap === true ) {
17107        this.options.tap = 'tap';
17108    }
17109
17110    // https://github.com/cubiq/iscroll/issues/1029
17111    if (!this.options.useTransition && !this.options.useTransform) {
17112        if(!(/relative|absolute/i).test(this.scrollerStyle.position)) {
17113            this.scrollerStyle.position = "relative";
17114        }
17115    }
17116
17117    if ( this.options.shrinkScrollbars == 'scale' ) {
17118        this.options.useTransition = false;
17119    }
17120
17121    this.options.invertWheelDirection = this.options.invertWheelDirection ? -1 : 1;
17122
17123// INSERT POINT: NORMALIZATION
17124
17125    // Some defaults
17126    this.x = 0;
17127    this.y = 0;
17128    this.directionX = 0;
17129    this.directionY = 0;
17130    this._events = {};
17131
17132// INSERT POINT: DEFAULTS
17133
17134    this._init();
17135    this.refresh();
vendor: 4,317 bytes, lines 17136-17278
17136
17137    this.scrollTo(this.options.startX, this.options.startY);
17138    this.enable();
17139}
17140
17141IScroll.prototype = {
17142    version: '5.2.0',
17143
17144    _init: function () {
17145        this._initEvents();
17146
17147        if ( this.options.scrollbars || this.options.indicators ) {
17148            this._initIndicators();
17149        }
17150
17151        if ( this.options.mouseWheel ) {
17152            this._initWheel();
17153        }
17154
17155        if ( this.options.snap ) {
17156            this._initSnap();
17157        }
17158
17159        if ( this.options.keyBindings ) {
17160            this._initKeys();
17161        }
17162
17163// INSERT POINT: _init
17164
17165    },
17166
17167    destroy: function () {
17168        this._initEvents(true);
17169        clearTimeout(this.resizeTimeout);
17170        this.resizeTimeout = null;
17171        this._execEvent('destroy');
17172    },
17173
17174    _transitionEnd: function (e) {
17175        if ( e.target != this.scroller || !this.isInTransition ) {
17176            return;
17177        }
17178
17179        this._transitionTime();
17180        if ( !this.resetPosition(this.options.bounceTime) ) {
17181            this.isInTransition = false;
17182            this._execEvent('scrollEnd');
17183        }
17184    },
17185
17186    _start: function (e) {
17187        // React to left mouse button only
17188        if ( utils.eventType[e.type] != 1 ) {
17189          // for button property
17190          // http://unixpapa.com/js/mouse.html
17191          var button;
17192        if (!e.which) {
17193          /* IE case */
17194          button = (e.button < 2) ? 0 :
17195                   ((e.button == 4) ? 1 : 2);
17196        } else {
17197          /* All others */
17198          button = e.button;
17199        }
17200            if ( button !== 0 ) {
17201                return;
17202            }
17203        }
17204
17205        if ( !this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated) ) {
17206            return;
17207        }
17208
17209        if ( this.options.preventDefault && !utils.isBadAndroid && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) {
17210            e.preventDefault();
17211        }
17212
17213        var point = e.touches ? e.touches[0] : e,
17214            pos;
17215
17216        this.initiated  = utils.eventType[e.type];
17217        this.moved      = false;
17218        this.distX      = 0;
17219        this.distY      = 0;
17220        this.directionX = 0;
17221        this.directionY = 0;
17222        this.directionLocked = 0;
17223
17224        this.startTime = utils.getTime();
17225
17226        if ( this.options.useTransition && this.isInTransition ) {
17227            this._transitionTime();
17228            this.isInTransition = false;
17229            pos = this.getComputedPosition();
17230            this._translate(Math.round(pos.x), Math.round(pos.y));
17231            this._execEvent('scrollEnd');
17232        } else if ( !this.options.useTransition && this.isAnimating ) {
17233            this.isAnimating = false;
17234            this._execEvent('scrollEnd');
17235        }
17236
17237        this.startX    = this.x;
17238        this.startY    = this.y;
17239        this.absStartX = this.x;
17240        this.absStartY = this.y;
17241        this.pointX    = point.pageX;
17242        this.pointY    = point.pageY;
17243
17244        this._execEvent('beforeScrollStart');
17245    },
17246
17247    _move: function (e) {
17248        if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) {
17249            return;
17250        }
17251
17252        if ( this.options.preventDefault ) {    // increases performance on Android? TODO: check!
17253            e.preventDefault();
17254        }
17255
17256        var point       = e.touches ? e.touches[0] : e,
17257            deltaX      = point.pageX - this.pointX,
17258            deltaY      = point.pageY - this.pointY,
17259            timestamp   = utils.getTime(),
17260            newX, newY,
17261            absDistX, absDistY;
17262
17263        this.pointX     = point.pageX;
17264        this.pointY     = point.pageY;
17265
17266        this.distX      += deltaX;
17267        this.distY      += deltaY;
17268        absDistX        = Math.abs(this.distX);
17269        absDistY        = Math.abs(this.distY);
17270
17271        // We need to move at least 10 pixels for the scrolling to initiate
17272        if ( timestamp - this.endTime > 300 && (absDistX < 10 && absDistY < 10) ) {
17273            return;
17274        }
17275
17276        // If you are scrolling in one direction lock the other
17277        if ( !this.directionLocked && !this.options.freeScroll ) {
17278            if ( absDistX > absDistY + this.options.directionLockThreshold ) {
vendor: 5,654 bytes, lines 17279-17442
17279                this.directionLocked = 'h';     // lock horizontally
17280            } else if ( absDistY >= absDistX + this.options.directionLockThreshold ) {
17281                this.directionLocked = 'v';     // lock vertically
17282            } else {
17283                this.directionLocked = 'n';     // no lock
17284            }
17285        }
17286
17287        if ( this.directionLocked == 'h' ) {
17288            if ( this.options.eventPassthrough == 'vertical' ) {
17289                e.preventDefault();
17290            } else if ( this.options.eventPassthrough == 'horizontal' ) {
17291                this.initiated = false;
17292                return;
17293            }
17294
17295            deltaY = 0;
17296        } else if ( this.directionLocked == 'v' ) {
17297            if ( this.options.eventPassthrough == 'horizontal' ) {
17298                e.preventDefault();
17299            } else if ( this.options.eventPassthrough == 'vertical' ) {
17300                this.initiated = false;
17301                return;
17302            }
17303
17304            deltaX = 0;
17305        }
17306
17307        deltaX = this.hasHorizontalScroll ? deltaX : 0;
17308        deltaY = this.hasVerticalScroll ? deltaY : 0;
17309
17310        newX = this.x + deltaX;
17311        newY = this.y + deltaY;
17312
17313        // Slow down if outside of the boundaries
17314        if ( newX > 0 || newX < this.maxScrollX ) {
17315            newX = this.options.bounce ? this.x + deltaX / 3 : newX > 0 ? 0 : this.maxScrollX;
17316        }
17317        if ( newY > 0 || newY < this.maxScrollY ) {
17318            newY = this.options.bounce ? this.y + deltaY / 3 : newY > 0 ? 0 : this.maxScrollY;
17319        }
17320
17321        this.directionX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0;
17322        this.directionY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0;
17323
17324        if ( !this.moved ) {
17325            this._execEvent('scrollStart');
17326        }
17327
17328        this.moved = true;
17329
17330        this._translate(newX, newY);
17331
17332/* REPLACE START: _move */
17333
17334        if ( timestamp - this.startTime > 300 ) {
17335            this.startTime = timestamp;
17336            this.startX = this.x;
17337            this.startY = this.y;
17338        }
17339
17340/* REPLACE END: _move */
17341
17342    },
17343
17344    _end: function (e) {
17345        if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) {
17346            return;
17347        }
17348
17349        if ( this.options.preventDefault && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) {
17350            e.preventDefault();
17351        }
17352
17353        var point = e.changedTouches ? e.changedTouches[0] : e,
17354            momentumX,
17355            momentumY,
17356            duration = utils.getTime() - this.startTime,
17357            newX = Math.round(this.x),
17358            newY = Math.round(this.y),
17359            distanceX = Math.abs(newX - this.startX),
17360            distanceY = Math.abs(newY - this.startY),
17361            time = 0,
17362            easing = '';
17363
17364        this.isInTransition = 0;
17365        this.initiated = 0;
17366        this.endTime = utils.getTime();
17367
17368        // reset if we are outside of the boundaries
17369        if ( this.resetPosition(this.options.bounceTime) ) {
17370            return;
17371        }
17372
17373        this.scrollTo(newX, newY);  // ensures that the last position is rounded
17374
17375        // we scrolled less than 10 pixels
17376        if ( !this.moved ) {
17377            if ( this.options.tap ) {
17378                utils.tap(e, this.options.tap);
17379            }
17380
17381            if ( this.options.click ) {
17382                utils.click(e);
17383            }
17384
17385            this._execEvent('scrollCancel');
17386            return;
17387        }
17388
17389        if ( this._events.flick && duration < 200 && distanceX < 100 && distanceY < 100 ) {
17390            this._execEvent('flick');
17391            return;
17392        }
17393
17394        // start momentum animation if needed
17395        if ( this.options.momentum && duration < 300 ) {
17396            momentumX = this.hasHorizontalScroll ? utils.momentum(this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperWidth : 0, this.options.deceleration) : { destination: newX, duration: 0 };
17397            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 };
17398            newX = momentumX.destination;
17399            newY = momentumY.destination;
17400            time = Math.max(momentumX.duration, momentumY.duration);
17401            this.isInTransition = 1;
17402        }
17403
17404
17405        if ( this.options.snap ) {
17406            var snap = this._nearestSnap(newX, newY);
17407            this.currentPage = snap;
17408            time = this.options.snapSpeed || Math.max(
17409                    Math.max(
17410                        Math.min(Math.abs(newX - snap.x), 1000),
17411                        Math.min(Math.abs(newY - snap.y), 1000)
17412                    ), 300);
17413            newX = snap.x;
17414            newY = snap.y;
17415
17416            this.directionX = 0;
17417            this.directionY = 0;
17418            easing = this.options.bounceEasing;
17419        }
17420
17421// INSERT POINT: _end
17422
17423        if ( newX != this.x || newY != this.y ) {
17424            // change easing function when scroller goes out of the boundaries
17425            if ( newX > 0 || newX < this.maxScrollX || newY > 0 || newY < this.maxScrollY ) {
17426                easing = utils.ease.quadratic;
17427            }
17428
17429            this.scrollTo(newX, newY, time, easing);
17430            return;
17431        }
17432
17433        this._execEvent('scrollEnd');
17434    },
17435
17436    _resize: function () {
17437        var that = this;
17438
17439        clearTimeout(this.resizeTimeout);
17440
17441        this.resizeTimeout = setTimeout(function () {
17442            that.refresh();
vendor: 12,140 bytes, lines 17443-17836
17443        }, this.options.resizePolling);
17444    },
17445
17446    resetPosition: function (time) {
17447        var x = this.x,
17448            y = this.y;
17449
17450        time = time || 0;
17451
17452        if ( !this.hasHorizontalScroll || this.x > 0 ) {
17453            x = 0;
17454        } else if ( this.x < this.maxScrollX ) {
17455            x = this.maxScrollX;
17456        }
17457
17458        if ( !this.hasVerticalScroll || this.y > 0 ) {
17459            y = 0;
17460        } else if ( this.y < this.maxScrollY ) {
17461            y = this.maxScrollY;
17462        }
17463
17464        if ( x == this.x && y == this.y ) {
17465            return false;
17466        }
17467
17468        this.scrollTo(x, y, time, this.options.bounceEasing);
17469
17470        return true;
17471    },
17472
17473    disable: function () {
17474        this.enabled = false;
17475    },
17476
17477    enable: function () {
17478        this.enabled = true;
17479    },
17480
17481    refresh: function () {
17482        var rf = this.wrapper.offsetHeight;     // Force reflow
17483
17484        this.wrapperWidth   = this.wrapper.clientWidth;
17485        this.wrapperHeight  = this.wrapper.clientHeight;
17486
17487/* REPLACE START: refresh */
17488
17489        this.scrollerWidth  = this.scroller.offsetWidth;
17490        this.scrollerHeight = this.scroller.offsetHeight;
17491
17492        this.maxScrollX     = this.wrapperWidth - this.scrollerWidth;
17493        this.maxScrollY     = this.wrapperHeight - this.scrollerHeight;
17494
17495/* REPLACE END: refresh */
17496
17497        this.hasHorizontalScroll    = this.options.scrollX && this.maxScrollX < 0;
17498        this.hasVerticalScroll      = this.options.scrollY && this.maxScrollY < 0;
17499
17500        if ( !this.hasHorizontalScroll ) {
17501            this.maxScrollX = 0;
17502            this.scrollerWidth = this.wrapperWidth;
17503        }
17504
17505        if ( !this.hasVerticalScroll ) {
17506            this.maxScrollY = 0;
17507            this.scrollerHeight = this.wrapperHeight;
17508        }
17509
17510        this.endTime = 0;
17511        this.directionX = 0;
17512        this.directionY = 0;
17513
17514        this.wrapperOffset = utils.offset(this.wrapper);
17515
17516        this._execEvent('refresh');
17517
17518        this.resetPosition();
17519
17520// INSERT POINT: _refresh
17521
17522    },
17523
17524    on: function (type, fn) {
17525        if ( !this._events[type] ) {
17526            this._events[type] = [];
17527        }
17528
17529        this._events[type].push(fn);
17530    },
17531
17532    off: function (type, fn) {
17533        if ( !this._events[type] ) {
17534            return;
17535        }
17536
17537        var index = this._events[type].indexOf(fn);
17538
17539        if ( index > -1 ) {
17540            this._events[type].splice(index, 1);
17541        }
17542    },
17543
17544    _execEvent: function (type) {
17545        if ( !this._events[type] ) {
17546            return;
17547        }
17548
17549        var i = 0,
17550            l = this._events[type].length;
17551
17552        if ( !l ) {
17553            return;
17554        }
17555
17556        for ( ; i < l; i++ ) {
17557            this._events[type][i].apply(this, [].slice.call(arguments, 1));
17558        }
17559    },
17560
17561    scrollBy: function (x, y, time, easing) {
17562        x = this.x + x;
17563        y = this.y + y;
17564        time = time || 0;
17565
17566        this.scrollTo(x, y, time, easing);
17567    },
17568
17569    scrollTo: function (x, y, time, easing) {
17570        easing = easing || utils.ease.circular;
17571
17572        this.isInTransition = this.options.useTransition && time > 0;
17573        var transitionType = this.options.useTransition && easing.style;
17574        if ( !time || transitionType ) {
17575                if(transitionType) {
17576                    this._transitionTimingFunction(easing.style);
17577                    this._transitionTime(time);
17578                }
17579            this._translate(x, y);
17580        } else {
17581            this._animate(x, y, time, easing.fn);
17582        }
17583    },
17584
17585    scrollToElement: function (el, time, offsetX, offsetY, easing) {
17586        el = el.nodeType ? el : this.scroller.querySelector(el);
17587
17588        if ( !el ) {
17589            return;
17590        }
17591
17592        var pos = utils.offset(el);
17593
17594        pos.left -= this.wrapperOffset.left;
17595        pos.top  -= this.wrapperOffset.top;
17596
17597        // if offsetX/Y are true we center the element to the screen
17598        if ( offsetX === true ) {
17599            offsetX = Math.round(el.offsetWidth / 2 - this.wrapper.offsetWidth / 2);
17600        }
17601        if ( offsetY === true ) {
17602            offsetY = Math.round(el.offsetHeight / 2 - this.wrapper.offsetHeight / 2);
17603        }
17604
17605        pos.left -= offsetX || 0;
17606        pos.top  -= offsetY || 0;
17607
17608        pos.left = pos.left > 0 ? 0 : pos.left < this.maxScrollX ? this.maxScrollX : pos.left;
17609        pos.top  = pos.top  > 0 ? 0 : pos.top  < this.maxScrollY ? this.maxScrollY : pos.top;
17610
17611        time = time === undefined || time === null || time === 'auto' ? Math.max(Math.abs(this.x-pos.left), Math.abs(this.y-pos.top)) : time;
17612
17613        this.scrollTo(pos.left, pos.top, time, easing);
17614    },
17615
17616    _transitionTime: function (time) {
17617        if (!this.options.useTransition) {
17618            return;
17619        }
17620        time = time || 0;
17621        var durationProp = utils.style.transitionDuration;
17622        if(!durationProp) {
17623            return;
17624        }
17625
17626        this.scrollerStyle[durationProp] = time + 'ms';
17627
17628        if ( !time && utils.isBadAndroid ) {
17629            this.scrollerStyle[durationProp] = '0.0001ms';
17630            // remove 0.0001ms
17631            var self = this;
17632            rAF(function() {
17633                if(self.scrollerStyle[durationProp] === '0.0001ms') {
17634                    self.scrollerStyle[durationProp] = '0s';
17635                }
17636            });
17637        }
17638
17639
17640        if ( this.indicators ) {
17641            for ( var i = this.indicators.length; i--; ) {
17642                this.indicators[i].transitionTime(time);
17643            }
17644        }
17645
17646
17647// INSERT POINT: _transitionTime
17648
17649    },
17650
17651    _transitionTimingFunction: function (easing) {
17652        this.scrollerStyle[utils.style.transitionTimingFunction] = easing;
17653
17654
17655        if ( this.indicators ) {
17656            for ( var i = this.indicators.length; i--; ) {
17657                this.indicators[i].transitionTimingFunction(easing);
17658            }
17659        }
17660
17661
17662// INSERT POINT: _transitionTimingFunction
17663
17664    },
17665
17666    _translate: function (x, y) {
17667        //Uncode addition
17668        if ( (" " + this.wrapper.className + " ").replace(/[\n\t]/g, " ").indexOf(" no-scrolloverflow ") > -1 )
17669            return false;
17670
17671        if ( this.options.useTransform ) {
17672
17673/* REPLACE START: _translate */
17674
17675            this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ;
17676
17677/* REPLACE END: _translate */
17678
17679        } else {
17680            x = Math.round(x);
17681            y = Math.round(y);
17682            this.scrollerStyle.left = x + 'px';
17683            this.scrollerStyle.top = y + 'px';
17684        }
17685
17686        this.x = x;
17687        this.y = y;
17688
17689
17690        if ( this.indicators ) {
17691            for ( var i = this.indicators.length; i--; ) {
17692                this.indicators[i].updatePosition();
17693            }
17694        }
17695
17696        //Uncode addition
17697        var uncodevent = new CustomEvent('fp-slide-scroll');
17698        window.dispatchEvent(uncodevent);       
17699
17700// INSERT POINT: _translate
17701
17702    },
17703
17704    _initEvents: function (remove) {
17705        var eventType = remove ? utils.removeEvent : utils.addEvent,
17706            target = this.options.bindToWrapper ? this.wrapper : window;
17707
17708        eventType(window, 'orientationchange', this);
17709        eventType(window, 'resize', this);
17710
17711        if ( this.options.click ) {
17712            eventType(this.wrapper, 'click', this, true);
17713        }
17714
17715        if ( !this.options.disableMouse ) {
17716            eventType(this.wrapper, 'mousedown', this);
17717            eventType(target, 'mousemove', this);
17718            eventType(target, 'mousecancel', this);
17719            eventType(target, 'mouseup', this);
17720        }
17721
17722        if ( utils.hasPointer && !this.options.disablePointer ) {
17723            eventType(this.wrapper, utils.prefixPointerEvent('pointerdown'), this);
17724            eventType(target, utils.prefixPointerEvent('pointermove'), this);
17725            eventType(target, utils.prefixPointerEvent('pointercancel'), this);
17726            eventType(target, utils.prefixPointerEvent('pointerup'), this);
17727        }
17728
17729        if ( utils.hasTouch && !this.options.disableTouch ) {
17730            eventType(this.wrapper, 'touchstart', this);
17731            eventType(target, 'touchmove', this);
17732            eventType(target, 'touchcancel', this);
17733            eventType(target, 'touchend', this);
17734        }
17735
17736        eventType(this.scroller, 'transitionend', this);
17737        eventType(this.scroller, 'webkitTransitionEnd', this);
17738        eventType(this.scroller, 'oTransitionEnd', this);
17739        eventType(this.scroller, 'MSTransitionEnd', this);
17740    },
17741
17742    getComputedPosition: function () {
17743        var matrix = window.getComputedStyle(this.scroller, null),
17744            x, y;
17745
17746        if ( this.options.useTransform ) {
17747            matrix = matrix[utils.style.transform].split(')')[0].split(', ');
17748            x = +(matrix[12] || matrix[4]);
17749            y = +(matrix[13] || matrix[5]);
17750        } else {
17751            x = +matrix.left.replace(/[^-\d.]/g, '');
17752            y = +matrix.top.replace(/[^-\d.]/g, '');
17753        }
17754
17755        return { x: x, y: y };
17756    },
17757    _initIndicators: function () {
17758        var interactive = this.options.interactiveScrollbars,
17759            customStyle = typeof this.options.scrollbars != 'string',
17760            indicators = [],
17761            indicator;
17762
17763        var that = this;
17764
17765        this.indicators = [];
17766
17767        if ( this.options.scrollbars ) {
17768            // Vertical scrollbar
17769            if ( this.options.scrollY ) {
17770                indicator = {
17771                    el: createDefaultScrollbar('v', interactive, this.options.scrollbars),
17772                    interactive: interactive,
17773                    defaultScrollbars: true,
17774                    customStyle: customStyle,
17775                    resize: this.options.resizeScrollbars,
17776                    shrink: this.options.shrinkScrollbars,
17777                    fade: this.options.fadeScrollbars,
17778                    listenX: false
17779                };
17780
17781                this.wrapper.appendChild(indicator.el);
17782                indicators.push(indicator);
17783            }
17784
17785            // Horizontal scrollbar
17786            if ( this.options.scrollX ) {
17787                indicator = {
17788                    el: createDefaultScrollbar('h', interactive, this.options.scrollbars),
17789                    interactive: interactive,
17790                    defaultScrollbars: true,
17791                    customStyle: customStyle,
17792                    resize: this.options.resizeScrollbars,
17793                    shrink: this.options.shrinkScrollbars,
17794                    fade: this.options.fadeScrollbars,
17795                    listenY: false
17796                };
17797
17798                this.wrapper.appendChild(indicator.el);
17799                indicators.push(indicator);
17800            }
17801        }
17802
17803        if ( this.options.indicators ) {
17804            // TODO: check concat compatibility
17805            indicators = indicators.concat(this.options.indicators);
17806        }
17807
17808        for ( var i = indicators.length; i--; ) {
17809            this.indicators.push( new Indicator(this, indicators[i]) );
17810        }
17811
17812        // TODO: check if we can use array.map (wide compatibility and performance issues)
17813        function _indicatorsMap (fn) {
17814            if (that.indicators) {
17815                for ( var i = that.indicators.length; i--; ) {
17816                    fn.call(that.indicators[i]);
17817                }
17818            }
17819        }
17820
17821        if ( this.options.fadeScrollbars ) {
17822            this.on('scrollEnd', function () {
17823                _indicatorsMap(function () {
17824                    this.fade();
17825                });
17826            });
17827
17828            this.on('scrollCancel', function () {
17829                _indicatorsMap(function () {
17830                    this.fade();
17831                });
17832            });
17833
17834            this.on('scrollStart', function () {
17835                _indicatorsMap(function () {
17836                    this.fade(1);
vendor: 4,420 bytes, lines 17837-17981
17837                });
17838            });
17839
17840            this.on('beforeScrollStart', function () {
17841                _indicatorsMap(function () {
17842                    this.fade(1, true);
17843                });
17844            });
17845        }
17846
17847
17848        this.on('refresh', function () {
17849            _indicatorsMap(function () {
17850                this.refresh();
17851            });
17852        });
17853
17854        this.on('destroy', function () {
17855            _indicatorsMap(function () {
17856                this.destroy();
17857            });
17858
17859            delete this.indicators;
17860        });
17861    },
17862
17863    _initWheel: function () {
17864        utils.addEvent(this.wrapper, 'wheel', this);
17865        utils.addEvent(this.wrapper, 'mousewheel', this);
17866        utils.addEvent(this.wrapper, 'DOMMouseScroll', this);
17867
17868        this.on('destroy', function () {
17869            clearTimeout(this.wheelTimeout);
17870            this.wheelTimeout = null;
17871            utils.removeEvent(this.wrapper, 'wheel', this);
17872            utils.removeEvent(this.wrapper, 'mousewheel', this);
17873            utils.removeEvent(this.wrapper, 'DOMMouseScroll', this);
17874        });
17875    },
17876
17877    _wheel: function (e) {
17878        if ( !this.enabled ) {
17879            return;
17880        }
17881
17882        var wheelDeltaX, wheelDeltaY,
17883            newX, newY,
17884            that = this;
17885
17886        if ( this.wheelTimeout === undefined ) {
17887            that._execEvent('scrollStart');
17888        }
17889
17890        // Execute the scrollEnd event after 400ms the wheel stopped scrolling
17891        clearTimeout(this.wheelTimeout);
17892        this.wheelTimeout = setTimeout(function () {
17893            if(!that.options.snap) {
17894                that._execEvent('scrollEnd');
17895            }
17896            that.wheelTimeout = undefined;
17897        }, 400);
17898
17899        if ( 'deltaX' in e ) {
17900            if (e.deltaMode === 1) {
17901                wheelDeltaX = -e.deltaX * this.options.mouseWheelSpeed;
17902                wheelDeltaY = -e.deltaY * this.options.mouseWheelSpeed;
17903            } else {
17904                wheelDeltaX = -e.deltaX;
17905                wheelDeltaY = -e.deltaY;
17906            }
17907        } else if ( 'wheelDeltaX' in e ) {
17908            wheelDeltaX = e.wheelDeltaX / 120 * this.options.mouseWheelSpeed;
17909            wheelDeltaY = e.wheelDeltaY / 120 * this.options.mouseWheelSpeed;
17910        } else if ( 'wheelDelta' in e ) {
17911            wheelDeltaX = wheelDeltaY = e.wheelDelta / 120 * this.options.mouseWheelSpeed;
17912        } else if ( 'detail' in e ) {
17913            wheelDeltaX = wheelDeltaY = -e.detail / 3 * this.options.mouseWheelSpeed;
17914        } else {
17915            return;
17916        }
17917
17918        wheelDeltaX *= this.options.invertWheelDirection;
17919        wheelDeltaY *= this.options.invertWheelDirection;
17920
17921        if ( !this.hasVerticalScroll ) {
17922            wheelDeltaX = wheelDeltaY;
17923            wheelDeltaY = 0;
17924        }
17925
17926        if ( this.options.snap ) {
17927            newX = this.currentPage.pageX;
17928            newY = this.currentPage.pageY;
17929
17930            if ( wheelDeltaX > 0 ) {
17931                newX--;
17932            } else if ( wheelDeltaX < 0 ) {
17933                newX++;
17934            }
17935
17936            if ( wheelDeltaY > 0 ) {
17937                newY--;
17938            } else if ( wheelDeltaY < 0 ) {
17939                newY++;
17940            }
17941
17942            this.goToPage(newX, newY);
17943
17944            return;
17945        }
17946
17947        newX = this.x + Math.round(this.hasHorizontalScroll ? wheelDeltaX : 0);
17948        newY = this.y + Math.round(this.hasVerticalScroll ? wheelDeltaY : 0);
17949
17950        this.directionX = wheelDeltaX > 0 ? -1 : wheelDeltaX < 0 ? 1 : 0;
17951        this.directionY = wheelDeltaY > 0 ? -1 : wheelDeltaY < 0 ? 1 : 0;
17952
17953        if ( newX > 0 ) {
17954            newX = 0;
17955        } else if ( newX < this.maxScrollX ) {
17956            newX = this.maxScrollX;
17957        }
17958
17959        if ( newY > 0 ) {
17960            newY = 0;
17961        } else if ( newY < this.maxScrollY ) {
17962            newY = this.maxScrollY;
17963        }
17964
17965        this.scrollTo(newX, newY, 0);
17966
17967// INSERT POINT: _wheel
17968    },
17969
17970    _initSnap: function () {
17971        this.currentPage = {};
17972
17973        if ( typeof this.options.snap == 'string' ) {
17974            this.options.snap = this.scroller.querySelectorAll(this.options.snap);
17975        }
17976
17977        this.on('refresh', function () {
17978            var i = 0, l,
17979                m = 0, n,
17980                cx, cy,
17981                x = 0, y,
vendor: 7,592 bytes, lines 17982-18235
17982                stepX = this.options.snapStepX || this.wrapperWidth,
17983                stepY = this.options.snapStepY || this.wrapperHeight,
17984                el;
17985
17986            this.pages = [];
17987
17988            if ( !this.wrapperWidth || !this.wrapperHeight || !this.scrollerWidth || !this.scrollerHeight ) {
17989                return;
17990            }
17991
17992            if ( this.options.snap === true ) {
17993                cx = Math.round( stepX / 2 );
17994                cy = Math.round( stepY / 2 );
17995
17996                while ( x > -this.scrollerWidth ) {
17997                    this.pages[i] = [];
17998                    l = 0;
17999                    y = 0;
18000
18001                    while ( y > -this.scrollerHeight ) {
18002                        this.pages[i][l] = {
18003                            x: Math.max(x, this.maxScrollX),
18004                            y: Math.max(y, this.maxScrollY),
18005                            width: stepX,
18006                            height: stepY,
18007                            cx: x - cx,
18008                            cy: y - cy
18009                        };
18010
18011                        y -= stepY;
18012                        l++;
18013                    }
18014
18015                    x -= stepX;
18016                    i++;
18017                }
18018            } else {
18019                el = this.options.snap;
18020                l = el.length;
18021                n = -1;
18022
18023                for ( ; i < l; i++ ) {
18024                    if ( i === 0 || el[i].offsetLeft <= el[i-1].offsetLeft ) {
18025                        m = 0;
18026                        n++;
18027                    }
18028
18029                    if ( !this.pages[m] ) {
18030                        this.pages[m] = [];
18031                    }
18032
18033                    x = Math.max(-el[i].offsetLeft, this.maxScrollX);
18034                    y = Math.max(-el[i].offsetTop, this.maxScrollY);
18035                    cx = x - Math.round(el[i].offsetWidth / 2);
18036                    cy = y - Math.round(el[i].offsetHeight / 2);
18037
18038                    this.pages[m][n] = {
18039                        x: x,
18040                        y: y,
18041                        width: el[i].offsetWidth,
18042                        height: el[i].offsetHeight,
18043                        cx: cx,
18044                        cy: cy
18045                    };
18046
18047                    if ( x > this.maxScrollX ) {
18048                        m++;
18049                    }
18050                }
18051            }
18052
18053            this.goToPage(this.currentPage.pageX || 0, this.currentPage.pageY || 0, 0);
18054
18055            // Update snap threshold if needed
18056            if ( this.options.snapThreshold % 1 === 0 ) {
18057                this.snapThresholdX = this.options.snapThreshold;
18058                this.snapThresholdY = this.options.snapThreshold;
18059            } else {
18060                this.snapThresholdX = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].width * this.options.snapThreshold);
18061                this.snapThresholdY = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].height * this.options.snapThreshold);
18062            }
18063        });
18064
18065        this.on('flick', function () {
18066            var time = this.options.snapSpeed || Math.max(
18067                    Math.max(
18068                        Math.min(Math.abs(this.x - this.startX), 1000),
18069                        Math.min(Math.abs(this.y - this.startY), 1000)
18070                    ), 300);
18071
18072            this.goToPage(
18073                this.currentPage.pageX + this.directionX,
18074                this.currentPage.pageY + this.directionY,
18075                time
18076            );
18077        });
18078    },
18079
18080    _nearestSnap: function (x, y) {
18081        if ( !this.pages.length ) {
18082            return { x: 0, y: 0, pageX: 0, pageY: 0 };
18083        }
18084
18085        var i = 0,
18086            l = this.pages.length,
18087            m = 0;
18088
18089        // Check if we exceeded the snap threshold
18090        if ( Math.abs(x - this.absStartX) < this.snapThresholdX &&
18091            Math.abs(y - this.absStartY) < this.snapThresholdY ) {
18092            return this.currentPage;
18093        }
18094
18095        if ( x > 0 ) {
18096            x = 0;
18097        } else if ( x < this.maxScrollX ) {
18098            x = this.maxScrollX;
18099        }
18100
18101        if ( y > 0 ) {
18102            y = 0;
18103        } else if ( y < this.maxScrollY ) {
18104            y = this.maxScrollY;
18105        }
18106
18107        for ( ; i < l; i++ ) {
18108            if ( x >= this.pages[i][0].cx ) {
18109                x = this.pages[i][0].x;
18110                break;
18111            }
18112        }
18113
18114        l = this.pages[i].length;
18115
18116        for ( ; m < l; m++ ) {
18117            if ( y >= this.pages[0][m].cy ) {
18118                y = this.pages[0][m].y;
18119                break;
18120            }
18121        }
18122
18123        if ( i == this.currentPage.pageX ) {
18124            i += this.directionX;
18125
18126            if ( i < 0 ) {
18127                i = 0;
18128            } else if ( i >= this.pages.length ) {
18129                i = this.pages.length - 1;
18130            }
18131
18132            x = this.pages[i][0].x;
18133        }
18134
18135        if ( m == this.currentPage.pageY ) {
18136            m += this.directionY;
18137
18138            if ( m < 0 ) {
18139                m = 0;
18140            } else if ( m >= this.pages[0].length ) {
18141                m = this.pages[0].length - 1;
18142            }
18143
18144            y = this.pages[0][m].y;
18145        }
18146
18147        return {
18148            x: x,
18149            y: y,
18150            pageX: i,
18151            pageY: m
18152        };
18153    },
18154
18155    goToPage: function (x, y, time, easing) {
18156        easing = easing || this.options.bounceEasing;
18157
18158        if ( x >= this.pages.length ) {
18159            x = this.pages.length - 1;
18160        } else if ( x < 0 ) {
18161            x = 0;
18162        }
18163
18164        if ( y >= this.pages[x].length ) {
18165            y = this.pages[x].length - 1;
18166        } else if ( y < 0 ) {
18167            y = 0;
18168        }
18169
18170        var posX = this.pages[x][y].x,
18171            posY = this.pages[x][y].y;
18172
18173        time = time === undefined ? this.options.snapSpeed || Math.max(
18174            Math.max(
18175                Math.min(Math.abs(posX - this.x), 1000),
18176                Math.min(Math.abs(posY - this.y), 1000)
18177            ), 300) : time;
18178
18179        this.currentPage = {
18180            x: posX,
18181            y: posY,
18182            pageX: x,
18183            pageY: y
18184        };
18185
18186        this.scrollTo(posX, posY, time, easing);
18187    },
18188
18189    next: function (time, easing) {
18190        var x = this.currentPage.pageX,
18191            y = this.currentPage.pageY;
18192
18193        x++;
18194
18195        if ( x >= this.pages.length && this.hasVerticalScroll ) {
18196            x = 0;
18197            y++;
18198        }
18199
18200        this.goToPage(x, y, time, easing);
18201    },
18202
18203    prev: function (time, easing) {
18204        var x = this.currentPage.pageX,
18205            y = this.currentPage.pageY;
18206
18207        x--;
18208
18209        if ( x < 0 && this.hasVerticalScroll ) {
18210            x = 0;
18211            y--;
18212        }
18213
18214        this.goToPage(x, y, time, easing);
18215    },
18216
18217    _initKeys: function (e) {
18218        // default key bindings
18219        var keys = {
18220            pageUp: 33,
18221            pageDown: 34,
18222            end: 35,
18223            home: 36,
18224            left: 37,
18225            up: 38,
18226            right: 39,
18227            down: 40
18228        };
18229        var i;
18230
18231        // if you give me characters I give you keycode
18232        if ( typeof this.options.keyBindings == 'object' ) {
18233            for ( i in this.options.keyBindings ) {
18234                if ( typeof this.options.keyBindings[i] == 'string' ) {
18235                    this.options.keyBindings[i] = this.options.keyBindings[i].toUpperCase().charCo
vendor: 16,384 bytes, lines 18235-18735
18235deAt(0);
18236                }
18237            }
18238        } else {
18239            this.options.keyBindings = {};
18240        }
18241
18242        for ( i in keys ) {
18243            this.options.keyBindings[i] = this.options.keyBindings[i] || keys[i];
18244        }
18245
18246        utils.addEvent(window, 'keydown', this);
18247
18248        this.on('destroy', function () {
18249            utils.removeEvent(window, 'keydown', this);
18250        });
18251    },
18252
18253    _key: function (e) {
18254        if ( !this.enabled ) {
18255            return;
18256        }
18257
18258        var snap = this.options.snap,   // we are using this alot, better to cache it
18259            newX = snap ? this.currentPage.pageX : this.x,
18260            newY = snap ? this.currentPage.pageY : this.y,
18261            now = utils.getTime(),
18262            prevTime = this.keyTime || 0,
18263            acceleration = 0.250,
18264            pos;
18265
18266        if ( this.options.useTransition && this.isInTransition ) {
18267            pos = this.getComputedPosition();
18268
18269            this._translate(Math.round(pos.x), Math.round(pos.y));
18270            this.isInTransition = false;
18271        }
18272
18273        this.keyAcceleration = now - prevTime < 200 ? Math.min(this.keyAcceleration + acceleration, 50) : 0;
18274
18275        switch ( e.keyCode ) {
18276            case this.options.keyBindings.pageUp:
18277                if ( this.hasHorizontalScroll && !this.hasVerticalScroll ) {
18278                    newX += snap ? 1 : this.wrapperWidth;
18279                } else {
18280                    newY += snap ? 1 : this.wrapperHeight;
18281                }
18282                break;
18283            case this.options.keyBindings.pageDown:
18284                if ( this.hasHorizontalScroll && !this.hasVerticalScroll ) {
18285                    newX -= snap ? 1 : this.wrapperWidth;
18286                } else {
18287                    newY -= snap ? 1 : this.wrapperHeight;
18288                }
18289                break;
18290            case this.options.keyBindings.end:
18291                newX = snap ? this.pages.length-1 : this.maxScrollX;
18292                newY = snap ? this.pages[0].length-1 : this.maxScrollY;
18293                break;
18294            case this.options.keyBindings.home:
18295                newX = 0;
18296                newY = 0;
18297                break;
18298            case this.options.keyBindings.left:
18299                newX += snap ? -1 : 5 + this.keyAcceleration>>0;
18300                break;
18301            case this.options.keyBindings.up:
18302                newY += snap ? 1 : 5 + this.keyAcceleration>>0;
18303                break;
18304            case this.options.keyBindings.right:
18305                newX -= snap ? -1 : 5 + this.keyAcceleration>>0;
18306                break;
18307            case this.options.keyBindings.down:
18308                newY -= snap ? 1 : 5 + this.keyAcceleration>>0;
18309                break;
18310            default:
18311                return;
18312        }
18313
18314        if ( snap ) {
18315            this.goToPage(newX, newY);
18316            return;
18317        }
18318
18319        if ( newX > 0 ) {
18320            newX = 0;
18321            this.keyAcceleration = 0;
18322        } else if ( newX < this.maxScrollX ) {
18323            newX = this.maxScrollX;
18324            this.keyAcceleration = 0;
18325        }
18326
18327        if ( newY > 0 ) {
18328            newY = 0;
18329            this.keyAcceleration = 0;
18330        } else if ( newY < this.maxScrollY ) {
18331            newY = this.maxScrollY;
18332            this.keyAcceleration = 0;
18333        }
18334
18335        this.scrollTo(newX, newY, 0);
18336
18337        this.keyTime = now;
18338    },
18339
18340    _animate: function (destX, destY, duration, easingFn) {
18341        var that = this,
18342            startX = this.x,
18343            startY = this.y,
18344            startTime = utils.getTime(),
18345            destTime = startTime + duration;
18346
18347        function step () {
18348            var now = utils.getTime(),
18349                newX, newY,
18350                easing;
18351
18352            if ( now >= destTime ) {
18353                that.isAnimating = false;
18354                that._translate(destX, destY);
18355
18356                if ( !that.resetPosition(that.options.bounceTime) ) {
18357                    that._execEvent('scrollEnd');
18358                }
18359
18360                return;
18361            }
18362
18363            now = ( now - startTime ) / duration;
18364            easing = easingFn(now);
18365            newX = ( destX - startX ) * easing + startX;
18366            newY = ( destY - startY ) * easing + startY;
18367            that._translate(newX, newY);
18368
18369            if ( that.isAnimating ) {
18370                rAF(step);
18371            }
18372        }
18373
18374        this.isAnimating = true;
18375        step();
18376    },
18377    handleEvent: function (e) {
18378        switch ( e.type ) {
18379            case 'touchstart':
18380            case 'pointerdown':
18381            case 'MSPointerDown':
18382            case 'mousedown':
18383                this._start(e);
18384                break;
18385            case 'touchmove':
18386            case 'pointermove':
18387            case 'MSPointerMove':
18388            case 'mousemove':
18389                this._move(e);
18390                break;
18391            case 'touchend':
18392            case 'pointerup':
18393            case 'MSPointerUp':
18394            case 'mouseup':
18395            case 'touchcancel':
18396            case 'pointercancel':
18397            case 'MSPointerCancel':
18398            case 'mousecancel':
18399                this._end(e);
18400                break;
18401            case 'orientationchange':
18402            case 'resize':
18403                this._resize();
18404                break;
18405            case 'transitionend':
18406            case 'webkitTransitionEnd':
18407            case 'oTransitionEnd':
18408            case 'MSTransitionEnd':
18409                this._transitionEnd(e);
18410                break;
18411            case 'wheel':
18412            case 'DOMMouseScroll':
18413            case 'mousewheel':
18414                this._wheel(e);
18415                break;
18416            case 'keydown':
18417                this._key(e);
18418                break;
18419            case 'click':
18420                if ( this.enabled && !e._constructed ) {
18421                    e.preventDefault();
18422                    e.stopPropagation();
18423                }
18424                break;
18425        }
18426    }
18427};
18428function createDefaultScrollbar (direction, interactive, type) {
18429    var scrollbar = document.createElement('div'),
18430        indicator = document.createElement('div');
18431
18432    if ( type === true ) {
18433        scrollbar.style.cssText = 'position:absolute;z-index:9999';
18434        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';
18435    }
18436
18437    indicator.className = 'iScrollIndicator';
18438
18439    if ( direction == 'h' ) {
18440        if ( type === true ) {
18441            scrollbar.style.cssText += ';height:7px;left:2px;right:2px;bottom:0';
18442            indicator.style.height = '100%';
18443        }
18444        scrollbar.className = 'iScrollHorizontalScrollbar';
18445    } else {
18446        if ( type === true ) {
18447            scrollbar.style.cssText += ';width:7px;bottom:2px;top:2px;right:1px';
18448            indicator.style.width = '100%';
18449        }
18450        scrollbar.className = 'iScrollVerticalScrollbar';
18451    }
18452
18453    scrollbar.style.cssText += ';overflow:hidden';
18454
18455    if ( !interactive ) {
18456        scrollbar.style.pointerEvents = 'none';
18457    }
18458
18459    scrollbar.appendChild(indicator);
18460
18461    return scrollbar;
18462}
18463
18464function Indicator (scroller, options) {
18465    this.wrapper = typeof options.el == 'string' ? document.querySelector(options.el) : options.el;
18466    this.wrapperStyle = this.wrapper.style;
18467    this.indicator = this.wrapper.children[0];
18468    this.indicatorStyle = this.indicator.style;
18469    this.scroller = scroller;
18470
18471    this.options = {
18472        listenX: true,
18473        listenY: true,
18474        interactive: false,
18475        resize: true,
18476        defaultScrollbars: false,
18477        shrink: false,
18478        fade: false,
18479        speedRatioX: 0,
18480        speedRatioY: 0
18481    };
18482
18483    for ( var i in options ) {
18484        this.options[i] = options[i];
18485    }
18486
18487    this.sizeRatioX = 1;
18488    this.sizeRatioY = 1;
18489    this.maxPosX = 0;
18490    this.maxPosY = 0;
18491
18492    if ( this.options.interactive ) {
18493        if ( !this.options.disableTouch ) {
18494            utils.addEvent(this.indicator, 'touchstart', this);
18495            utils.addEvent(window, 'touchend', this);
18496        }
18497        if ( !this.options.disablePointer ) {
18498            utils.addEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this);
18499            utils.addEvent(window, utils.prefixPointerEvent('pointerup'), this);
18500        }
18501        if ( !this.options.disableMouse ) {
18502            utils.addEvent(this.indicator, 'mousedown', this);
18503            utils.addEvent(window, 'mouseup', this);
18504        }
18505    }
18506
18507    if ( this.options.fade ) {
18508        this.wrapperStyle[utils.style.transform] = this.scroller.translateZ;
18509        var durationProp = utils.style.transitionDuration;
18510        if(!durationProp) {
18511            return;
18512        }
18513        this.wrapperStyle[durationProp] = utils.isBadAndroid ? '0.0001ms' : '0ms';
18514        // remove 0.0001ms
18515        var self = this;
18516        if(utils.isBadAndroid) {
18517            rAF(function() {
18518                if(self.wrapperStyle[durationProp] === '0.0001ms') {
18519                    self.wrapperStyle[durationProp] = '0s';
18520                }
18521            });
18522        }
18523        this.wrapperStyle.opacity = '0';
18524    }
18525}
18526
18527Indicator.prototype = {
18528    handleEvent: function (e) {
18529        switch ( e.type ) {
18530            case 'touchstart':
18531            case 'pointerdown':
18532            case 'MSPointerDown':
18533            case 'mousedown':
18534                this._start(e);
18535                break;
18536            case 'touchmove':
18537            case 'pointermove':
18538            case 'MSPointerMove':
18539            case 'mousemove':
18540                this._move(e);
18541                break;
18542            case 'touchend':
18543            case 'pointerup':
18544            case 'MSPointerUp':
18545            case 'mouseup':
18546            case 'touchcancel':
18547            case 'pointercancel':
18548            case 'MSPointerCancel':
18549            case 'mousecancel':
18550                this._end(e);
18551                break;
18552        }
18553    },
18554
18555    destroy: function () {
18556        if ( this.options.fadeScrollbars ) {
18557            clearTimeout(this.fadeTimeout);
18558            this.fadeTimeout = null;
18559        }
18560        if ( this.options.interactive ) {
18561            utils.removeEvent(this.indicator, 'touchstart', this);
18562            utils.removeEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this);
18563            utils.removeEvent(this.indicator, 'mousedown', this);
18564
18565            utils.removeEvent(window, 'touchmove', this);
18566            utils.removeEvent(window, utils.prefixPointerEvent('pointermove'), this);
18567            utils.removeEvent(window, 'mousemove', this);
18568
18569            utils.removeEvent(window, 'touchend', this);
18570            utils.removeEvent(window, utils.prefixPointerEvent('pointerup'), this);
18571            utils.removeEvent(window, 'mouseup', this);
18572        }
18573
18574        if ( this.options.defaultScrollbars ) {
18575            this.wrapper.parentNode.removeChild(this.wrapper);
18576        }
18577    },
18578
18579    _start: function (e) {
18580        var point = e.touches ? e.touches[0] : e;
18581
18582        e.preventDefault();
18583        e.stopPropagation();
18584
18585        this.transitionTime();
18586
18587        this.initiated = true;
18588        this.moved = false;
18589        this.lastPointX = point.pageX;
18590        this.lastPointY = point.pageY;
18591
18592        this.startTime  = utils.getTime();
18593
18594        if ( !this.options.disableTouch ) {
18595            utils.addEvent(window, 'touchmove', this);
18596        }
18597        if ( !this.options.disablePointer ) {
18598            utils.addEvent(window, utils.prefixPointerEvent('pointermove'), this);
18599        }
18600        if ( !this.options.disableMouse ) {
18601            utils.addEvent(window, 'mousemove', this);
18602        }
18603
18604        this.scroller._execEvent('beforeScrollStart');
18605    },
18606
18607    _move: function (e) {
18608        var point = e.touches ? e.touches[0] : e,
18609            deltaX, deltaY,
18610            newX, newY,
18611            timestamp = utils.getTime();
18612
18613        if ( !this.moved ) {
18614            this.scroller._execEvent('scrollStart');
18615        }
18616
18617        this.moved = true;
18618
18619        deltaX = point.pageX - this.lastPointX;
18620        this.lastPointX = point.pageX;
18621
18622        deltaY = point.pageY - this.lastPointY;
18623        this.lastPointY = point.pageY;
18624
18625        newX = this.x + deltaX;
18626        newY = this.y + deltaY;
18627
18628        this._pos(newX, newY);
18629
18630// INSERT POINT: indicator._move
18631
18632        e.preventDefault();
18633        e.stopPropagation();
18634    },
18635
18636    _end: function (e) {
18637        if ( !this.initiated ) {
18638            return;
18639        }
18640
18641        this.initiated = false;
18642
18643        e.preventDefault();
18644        e.stopPropagation();
18645
18646        utils.removeEvent(window, 'touchmove', this);
18647        utils.removeEvent(window, utils.prefixPointerEvent('pointermove'), this);
18648        utils.removeEvent(window, 'mousemove', this);
18649
18650        if ( this.scroller.options.snap ) {
18651            var snap = this.scroller._nearestSnap(this.scroller.x, this.scroller.y);
18652
18653            var time = this.options.snapSpeed || Math.max(
18654                    Math.max(
18655                        Math.min(Math.abs(this.scroller.x - snap.x), 1000),
18656                        Math.min(Math.abs(this.scroller.y - snap.y), 1000)
18657                    ), 300);
18658
18659            if ( this.scroller.x != snap.x || this.scroller.y != snap.y ) {
18660                this.scroller.directionX = 0;
18661                this.scroller.directionY = 0;
18662                this.scroller.currentPage = snap;
18663                this.scroller.scrollTo(snap.x, snap.y, time, this.scroller.options.bounceEasing);
18664            }
18665        }
18666
18667        if ( this.moved ) {
18668            this.scroller._execEvent('scrollEnd');
18669        }
18670    },
18671
18672    transitionTime: function (time) {
18673        time = time || 0;
18674        var durationProp = utils.style.transitionDuration;
18675        if(!durationProp) {
18676            return;
18677        }
18678
18679        this.indicatorStyle[durationProp] = time + 'ms';
18680
18681        if ( !time && utils.isBadAndroid ) {
18682            this.indicatorStyle[durationProp] = '0.0001ms';
18683            // remove 0.0001ms
18684            var self = this;
18685            rAF(function() {
18686                if(self.indicatorStyle[durationProp] === '0.0001ms') {
18687                    self.indicatorStyle[durationProp] = '0s';
18688                }
18689            });
18690        }
18691    },
18692
18693    transitionTimingFunction: function (easing) {
18694        this.indicatorStyle[utils.style.transitionTimingFunction] = easing;
18695    },
18696
18697    refresh: function () {
18698        this.transitionTime();
18699
18700        if ( this.options.listenX && !this.options.listenY ) {
18701            this.indicatorStyle.display = this.scroller.hasHorizontalScroll ? 'block' : 'none';
18702        } else if ( this.options.listenY && !this.options.listenX ) {
18703            this.indicatorStyle.display = this.scroller.hasVerticalScroll ? 'block' : 'none';
18704        } else {
18705            this.indicatorStyle.display = this.scroller.hasHorizontalScroll || this.scroller.hasVerticalScroll ? 'block' : 'none';
18706        }
18707
18708        if ( this.scroller.hasHorizontalScroll && this.scroller.hasVerticalScroll ) {
18709            utils.addClass(this.wrapper, 'iScrollBothScrollbars');
18710            utils.removeClass(this.wrapper, 'iScrollLoneScrollbar');
18711
18712            if ( this.options.defaultScrollbars && this.options.customStyle ) {
18713                if ( this.options.listenX ) {
18714                    this.wrapper.style.right = '8px';
18715                } else {
18716                    this.wrapper.style.bottom = '8px';
18717                }
18718            }
18719        } else {
18720            utils.removeClass(this.wrapper, 'iScrollBothScrollbars');
18721            utils.addClass(this.wrapper, 'iScrollLoneScrollbar');
18722
18723            if ( this.options.defaultScrollbars && this.options.customStyle ) {
18724                if ( this.options.listenX ) {
18725                    this.wrapper.style.right = '2px';
18726                } else {
18727                    this.wrapper.style.bottom = '2px';
18728                }
18729            }
18730        }
18731
18732        var r = this.wrapper.offsetHeight;  // force refresh
18733
18734        if ( this.options.listenX ) {
18735     
vendor: 5,401 bytes, lines 18735-18864
18735       this.wrapperWidth = this.wrapper.clientWidth;
18736            if ( this.options.resize ) {
18737                this.indicatorWidth = Math.max(Math.round(this.wrapperWidth * this.wrapperWidth / (this.scroller.scrollerWidth || this.wrapperWidth || 1)), 8);
18738                this.indicatorStyle.width = this.indicatorWidth + 'px';
18739            } else {
18740                this.indicatorWidth = this.indicator.clientWidth;
18741            }
18742
18743            this.maxPosX = this.wrapperWidth - this.indicatorWidth;
18744
18745            if ( this.options.shrink == 'clip' ) {
18746                this.minBoundaryX = -this.indicatorWidth + 8;
18747                this.maxBoundaryX = this.wrapperWidth - 8;
18748            } else {
18749                this.minBoundaryX = 0;
18750                this.maxBoundaryX = this.maxPosX;
18751            }
18752
18753            this.sizeRatioX = this.options.speedRatioX || (this.scroller.maxScrollX && (this.maxPosX / this.scroller.maxScrollX));
18754        }
18755
18756        if ( this.options.listenY ) {
18757            this.wrapperHeight = this.wrapper.clientHeight;
18758            if ( this.options.resize ) {
18759                this.indicatorHeight = Math.max(Math.round(this.wrapperHeight * this.wrapperHeight / (this.scroller.scrollerHeight || this.wrapperHeight || 1)), 8);
18760                this.indicatorStyle.height = this.indicatorHeight + 'px';
18761            } else {
18762                this.indicatorHeight = this.indicator.clientHeight;
18763            }
18764
18765            this.maxPosY = this.wrapperHeight - this.indicatorHeight;
18766
18767            if ( this.options.shrink == 'clip' ) {
18768                this.minBoundaryY = -this.indicatorHeight + 8;
18769                this.maxBoundaryY = this.wrapperHeight - 8;
18770            } else {
18771                this.minBoundaryY = 0;
18772                this.maxBoundaryY = this.maxPosY;
18773            }
18774
18775            this.maxPosY = this.wrapperHeight - this.indicatorHeight;
18776            this.sizeRatioY = this.options.speedRatioY || (this.scroller.maxScrollY && (this.maxPosY / this.scroller.maxScrollY));
18777        }
18778
18779        this.updatePosition();
18780    },
18781
18782    updatePosition: function () {
18783        var x = this.options.listenX && Math.round(this.sizeRatioX * this.scroller.x) || 0,
18784            y = this.options.listenY && Math.round(this.sizeRatioY * this.scroller.y) || 0;
18785
18786        if ( !this.options.ignoreBoundaries ) {
18787            if ( x < this.minBoundaryX ) {
18788                if ( this.options.shrink == 'scale' ) {
18789                    this.width = Math.max(this.indicatorWidth + x, 8);
18790                    this.indicatorStyle.width = this.width + 'px';
18791                }
18792                x = this.minBoundaryX;
18793            } else if ( x > this.maxBoundaryX ) {
18794                if ( this.options.shrink == 'scale' ) {
18795                    this.width = Math.max(this.indicatorWidth - (x - this.maxPosX), 8);
18796                    this.indicatorStyle.width = this.width + 'px';
18797                    x = this.maxPosX + this.indicatorWidth - this.width;
18798                } else {
18799                    x = this.maxBoundaryX;
18800                }
18801            } else if ( this.options.shrink == 'scale' && this.width != this.indicatorWidth ) {
18802                this.width = this.indicatorWidth;
18803                this.indicatorStyle.width = this.width + 'px';
18804            }
18805
18806            if ( y < this.minBoundaryY ) {
18807                if ( this.options.shrink == 'scale' ) {
18808                    this.height = Math.max(this.indicatorHeight + y * 3, 8);
18809                    this.indicatorStyle.height = this.height + 'px';
18810                }
18811                y = this.minBoundaryY;
18812            } else if ( y > this.maxBoundaryY ) {
18813                if ( this.options.shrink == 'scale' ) {
18814                    this.height = Math.max(this.indicatorHeight - (y - this.maxPosY) * 3, 8);
18815                    this.indicatorStyle.height = this.height + 'px';
18816                    y = this.maxPosY + this.indicatorHeight - this.height;
18817                } else {
18818                    y = this.maxBoundaryY;
18819                }
18820            } else if ( this.options.shrink == 'scale' && this.height != this.indicatorHeight ) {
18821                this.height = this.indicatorHeight;
18822                this.indicatorStyle.height = this.height + 'px';
18823            }
18824        }
18825
18826        this.x = x;
18827        this.y = y;
18828
18829        if ( this.scroller.options.useTransform ) {
18830            this.indicatorStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.scroller.translateZ;
18831        } else {
18832            this.indicatorStyle.left = x + 'px';
18833            this.indicatorStyle.top = y + 'px';
18834        }
18835    },
18836
18837    _pos: function (x, y) {
18838        if ( x < 0 ) {
18839            x = 0;
18840        } else if ( x > this.maxPosX ) {
18841            x = this.maxPosX;
18842        }
18843
18844        if ( y < 0 ) {
18845            y = 0;
18846        } else if ( y > this.maxPosY ) {
18847            y = this.maxPosY;
18848        }
18849
18850        x = this.options.listenX ? Math.round(x / this.sizeRatioX) : this.scroller.x;
18851        y = this.options.listenY ? Math.round(y / this.sizeRatioY) : this.scroller.y;
18852
18853        this.scroller.scrollTo(x, y);
18854    },
18855
18856    fade: function (val, hold) {
18857        if ( hold && !this.visible ) {
18858            return;
18859        }
18860
18861        clearTimeout(this.fadeTimeout);
18862        this.fadeTimeout = null;
18863
18864        var time = val ? 250 : 500,
18865            delay = val ? 0 : 300;
18866
18867        val = val ? '1' : '0';
18868
18869        this.wrapperStyle[utils.style.transitionDuration] = time + 'ms';
18870
18871        this.fadeTimeout = setTimeout((function (val) {
18872            this.wrapperStyle.opacity = val;
18873            this.visible = +val;
18874        }).bind(this, val), delay);
18875    }
18876};
18877
18878IScroll.utils = utils;
18879
18880if ( typeof module != 'undefined' && module.exports ) {
18881    module.exports = IScroll;
18882} else if ( typeof define == 'function' && define.amd ) {
18883        define( function () { return IScroll; } );
18884} else {
18885    window.IScroll = IScroll;
18886}
18887
18888})(window, document, Math);
18889/*!
18890 * fullPage 2.9.4
18891 * https://github.com/alvarotrigo/fullPage.js
18892 * @license MIT licensed
18893 *
18894 * Copyright (C) 2015 alvarotrigo.com - A project by Alvaro Trigo
18895 */
18896(function(global, factory) {
18897    'use strict';
18898    if (typeof define === 'function' && define.amd) {
18899        define(['jquery'], function($) {
18900          return factory($, global, global.document, global.Math);
18901        });
18902    } else if (typeof exports === "object" && exports) {
18903        module.exports = factory(require('jquery'), global, global.document, global.Math);
18904    } else {
18905        factory(jQuery, global, global.document, global.Math);
18906    }
18907})(typeof window !== 'undefined' ? window : this, function($, window, document, Math, undefined) {
18908    'use strict';
18909
18910    // keeping central set of classnames and selectors
18911    var WRAPPER =               'fullpage-wrapper';
18912    var WRAPPER_SEL =           '.' + WRAPPER;
18913
18914    // slimscroll
18915    var SCROLLABLE =            'fp-scrollable';
18916    var SCROLLABLE_SEL =        '.' + SCROLLABLE;
18917
18918    // util
18919    var RESPONSIVE =            'fp-responsive';
18920    var NO_TRANSITION =         'fp-notransition';
18921    var DESTROYED =             'fp-destroyed';
18922    var ENABLED =               'fp-enabled';
18923    var VIEWING_PREFIX =        'fp-viewing';
18924    var ACTIVE =                'active';
18925    var ACTIVE_SEL =            '.' + ACTIVE;
18926    var COMPLETELY =            'fp-completely';
18927    var COMPLETELY_SEL =        '.' + COMPLETELY;
18928
18929    // section
18930    var SECTION_DEFAULT_SEL =   '.section';
18931    var SECTION =               'fp-section';
18932    var SECTION_SEL =           '.' + SECTION;
18933    var SECTION_ACTIVE_SEL =    SECTION_SEL + ACTIVE_SEL;
18934    var SECTION_FIRST_SEL =     SECTION_SEL + ':first';
18935    var SECTION_LAST_SEL =      SECTION_SEL + ':last';
18936    var TABLE_CELL =            'fp-tableCell';
18937    var TABLE_CELL_SEL =        '.' + TABLE_CELL;
18938    var AUTO_HEIGHT =           'fp-auto-height';
18939    var AUTO_HEIGHT_SEL =       '.fp-auto-height';
18940    var NORMAL_SCROLL =         'fp-normal-scroll';
18941    var NORMAL_SCROLL_SEL =     '.fp-normal-scroll';
18942
18943    // section nav
18944    var SECTION_NAV =           'fp-nav';
18945    var SECTION_NAV_SEL =       '#' + SECTION_NAV;
18946    var SECTION_NAV_TOOLTIP =   'fp-tooltip';
18947    var SECTION_NAV_TOOLTIP_SEL='.'+SECTION_NAV_TOOLTIP;
18948    var SHOW_ACTIVE_TOOLTIP =   'fp-show-active';
18949
18950    // slide
18951    var SLIDE_DEFAULT_SEL =     '.slide';
18952    var SLIDE =                 'fp-slide';
18953    var SLIDE_SEL =             '.' + SLIDE;
18954    var SLIDE_ACTIVE_SEL =      SLIDE_SEL + ACTIVE_SEL;
18955    var SLIDES_WRAPPER =        'fp-slides';
18956    var SLIDES_WRAPPER_SEL =    '.' + SLIDES_WRAPPER;
18957    var SLIDES_CONTAINER =      'fp-slidesContainer';
18958    var SLIDES_CONTAINER_SEL =  '.' + SLIDES_CONTAINER;
18959    var TABLE =                 'fp-table';
18960
18961    // slide nav
18962    var SLIDES_NAV =            'fp-slidesNav';
18963    var SLIDES_NAV_SEL =        '.' + SLIDES_NAV;
18964    var SLIDES_NAV_LINK_SEL =   SLIDES_NAV_SEL + ' a';
18965    var SLIDES_ARROW =          'fp-controlArrow';
18966    var SLIDES_ARROW_SEL =      '.' + SLIDES_ARROW;
18967    var SLIDES_PREV =           'fp-prev';
18968    var SLIDES_PREV_SEL =       '.' + SLIDES_PREV;
18969    var SLIDES_ARROW_PREV =     SLIDES_ARROW + ' ' + SLIDES_PREV;
18970    var SLIDES_ARROW_PREV_SEL = SLIDES_ARROW_SEL + SLIDES_PREV_SEL;
18971    var SLIDES_NEXT =           'fp-next';
18972    var SLIDES_NEXT_SEL =       '.' + SLIDES_NEXT;
18973    var SLIDES_ARROW_NEXT =     SLIDES_ARROW + ' ' + SLIDES_NEXT;
18974    var SLIDES_ARROW_NEXT_SEL = SLIDES_ARROW_SEL + SLIDES_NEXT_SEL;
18975
18976    var $window = $(window);
18977    var $document = $(document);
18978
18979    // Default options for iScroll.js used when using scrollOverflow
18980    var iscrollOptions = {
18981        scrollbars: true,
18982        mouseWheel: true,
18983        hideScrollbars: false,
18984        fadeScrollbars: false,
18985        disableMouse: true,
18986        interactiveScrollbars: true,
18987        bounce: false,
18988    };
18989
18990    $.fn.fullpage = function(options) {
18991        //only once my friend!
18992        if($('html').hasClass(ENABLED)){ displayWarnings(); return; }
18993
18994        // common jQuery objects
18995        var $htmlBody = $('html, body');
18996        var $body = $('body');
18997
18998        var FP = $.fn.fullpage;
18999
19000        // Creating some defaults, extending them with any options that were provided
19001        options = $.extend({
19002            //navigation
19003            menu: false,
19004            anchors:[],
19005            lockAnchors: false,
19006            navigation: false,
19007            navigationPosition: 'right',
19008            navigationTooltips: [],
19009            showActiveTooltip: false,
19010            slidesNavigation: false,
19011            slidesNavPosition: 'bottom',
19012            scrollBar: false,
19013            hybrid: false,
19014
19015            //scrolling
19016            css3: true,
19017            scrollingSpeed: 700,
19018            autoScrolling: true,
19019            fitToSection: true,
19020            fitToSectionDelay: 1000,
19021            easing: 'easeInOutCubic',
19022            easingcss3: 'ease',
19023            loopBottom: false,
19024            loopTop: false,
19025            loopHorizontal: true,
19026            continuousVertical: false,
19027            continuousHorizontal: false,
19028            scrollHorizontally: false,
19029            interlockedSlides: false,
19030            dragAndMove: false,
19031            offsetSections: false,
19032            resetSliders: false,
19033            fadingEffect: false,
19034            normalScrollElements: null,
19035            scrollOverflow: false,
19036            scrollOverflowReset: false,
19037            scrollOverflowHandler: iscrollHandler,
19038            scrollOverflowOptions: null,
19039            touchSensitivity: 5,
19040            normalScrollElementTouchThreshold: 5,
19041            bigSectionsDestination: null,
19042
19043            //Accessibility
19044            keyboardScrolling: true,
19045            animateAnchor: true,
19046            recordHistory: true,
19047
19048            //design
19049            controlArrows: true,
19050            controlArrowColor: '#fff',
19051            verticalCentered: true,
19052            sectionsColor : [],
19053            paddingTop: 0,
19054            paddingBottom: 0,
19055            fixedElements: null,
19056            responsive: 0, //backwards compabitility with responsiveWiddth
19057            responsiveWidth: 0,
19058            responsiveHeight: 0,
19059            responsiveSlides: false,
19060            parallax: false,
19061            parallaxOptions: {
19062                type: 'reveal',
19063                percentage: 62,
19064                property: 'translate'
19065            },
19066
19067            //Custom selectors
19068            sectionSelector: SECTION_DEFAULT_SEL,
19069            slideSelector: SLIDE_DEFAULT_SEL,
19070
19071            //events
19072            afterLoad: null,
19073            onLeave: null,
19074            afterRender: null,
19075            afterResize: null,
19076            afterReBuild: null,
19077            afterSlideLoad: null,
19078            onSlideLeave: null,
19079            afterResponsive: null,
19080
19081            lazyLoading: true
19082        }, options);
19083
19084        //flag to avoid very fast sliding for landscape sliders
19085        var slideMoving = false;
19086
19087        var isTouchDevice = navigator.userAgent.match(/(iPhone|iPod|iPad|Android|playbook|silk|BlackBerry|BB10|Windows Phone|Tizen|Bada|webOS|IEMobile|Opera Mini)/);
19088        var isTouch = (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0) || (navigator.maxTouchPoints));
19089        var container = $(this);
19090        var windowsHeight = $window.height();
19091        var isResizing = false;
19092        var isWindowFocused = true;
19093        var lastScrolledDestiny;
19094        var lastScrolledSlide;
19095        var canScroll = true;
19096        var scrollings = [];
19097        var controlPressed;
19098        var startingSection;
19099        var isScrollAllowed = {};
19100        isScrollAllowed.m = {  'up':true, 'down':true, 'left':true, 'right':true };
19101        isScrollAllowed.k = $.extend(true,{}, isScrollAllowed.m);
19102        var MSPointer = getMSPointer();
19103        var events = {
19104            touchmove: 'ontouchmove' in window ? 'touchmove' :  MSPointer.move,
19105            touchstart: 'ontouchstart' in window ? 'touchstart' :  MSPointer.down
19106        };
19107
19108        //timeouts
19109        var resizeId;
19110        var afterSectionLoadsId;
19111        var afterSlideLoadsId;
19112        var scrollId;
19113        var scrollId2;
19114        var keydownId;
19115        var originals = $.extend(true, {}, options); //deep copy
19116
19117        //Uncode addition
19118        var $masthead = $('#masthead');
19119        var hideMenu = !$('body').hasClass('vmenu') && $('body').hasClass('uncode-fp-menu-hide') ? true : false;
19120        var menuHeight = $masthead.hasClass('menu-transparent') || hideMenu ? 0 : UNCODE.menuHeight;
19121        var bodyBorder = UNCODE.bodyBorder;
19122        var adminBarHeight = UNCODE.adminBarHeight;
19123
19124        displayWarnings();
19125
19126        //fixing bug in iScroll with links: https://github.com/cubiq/iscroll/issues/783
19127        iscrollOptions.click = isTouch; // see #2035
19128
19129        //extending iScroll options with the user custom ones
19130        iscrollOptions = $.extend(iscrollOptions, options.scrollOverflowOptions);
19131
19132        //easeInOutCubic animation included in the plugin
19133        $.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;}});
19134
19135        /**
19136        * Sets the autoScroll option.
19137        * It changes the scroll bar visibility and the history of the site as a result.
19138        */
19139        function setAutoScrolling(value, type){
19140            //removing the transformation
19141            if(!value){
19142                silentScroll(0);
19143            }
19144
19145            setVariableState('autoScrolling', value, type);
19146
19147            var element = $(SECTION_ACTIVE_SEL);
19148
19149            if(options.autoScrolling && !options.scrollBar){
19150                $htmlBody.css({
19151                    'overflow' : 'hidden !important',
19152                    'height' : '100%'
19153                });
19154
19155                //Uncode addition
19156                //setRecordHistory(originals.recordHistory, 'internal');
19157
19158                //for IE touch devices
19159                container.css({
19160                    '-ms-touch-action': 'none',
19161                    'touch-action': 'none'
19162                });
19163
19164                if(element.length){
19165                    //moving the container up
19166                    silentScroll(element.position().top);
19167                }
19168
19169            }else{
19170                $htmlBody.css({
19171                    'overflow' : 'visible !important',
19172                    'height' : 'initial'
19173                });
19174
19175                //Uncode addition
19176                //setRecordHistory(false, 'internal');
19177
19178                //for IE touch devices
19179                container.css({
19180                    '-ms-touch-action': '',
19181                    'touch-action': ''
19182                });
19183
19184                //scrolling the page to the section with no animation
19185                if (element.length) {
19186                    $htmlBody.scrollTop(element.position().top);
19187                }
19188            }
19189        }
19190
19191        /**
19192        * Defines wheter to record the history for each hash change in the URL.
19193        */
19194        function setRecordHistory(value, type){
19195            setVariableState('recordHistory', value, type);
19196        }
19197
19198        /**
19199        * Defines the scrolling speed
19200        */
19201        function setScrollingSpeed(value, type){
19202            setVariableState('scrollingSpeed', value, type);
19203        }
19204
19205        /**
19206        * Sets fitToSection
19207        */
19208        function setFitToSection(value, type){
19209            setVariableState('fitToSection', value, type);
19210        }
19211
19212        /**
19213        * Sets lockAnchors
19214        */
19215        function setLockAnchors(value){
19216            options.lockAnchors = value;
19217        }
19218
19219        /**
19220        * Adds or remove the possiblity of scrolling through sections by using the mouse wheel or the trackpad.
19221        */
19222        function setMouseWheelScrolling(value){
19223            if(value){
19224                addMouseWheelHandler();
19225                addMiddleWheelHandler();
19226            }else{
19227                removeMouseWheelHandler();
19228                removeMiddleWheelHandler();
19229            }
19230        }
19231
19232        /**
19233        * Adds or remove the possibility of scrolling through sections by using the mouse wheel/trackpad or touch gestures.
19234        * Optionally a second parameter can be used to specify the direction for which the action will be applied.
19235        *
19236        * @param directions string containing the direction or directions separated by comma.
19237        */
19238        function setAllowScrolling(value, directions){
19239            if(typeof directions !== 'undefined'){
19240                directions = directions.replace(/ /g,'').split(',');
19241
19242                $.each(directions, function (index, direction){
19243                    setIsScrollAllowed(value, direction, 'm');
19244                });
19245            }
19246            else if(value){
19247                setMouseWheelScrolling(true);
19248                addTouchHandler();
19249            }else{
19250                setMouseWheelScrolling(false);
19251                removeTouchHandler();
19252            }
19253        }
19254
19255        /**
19256        * Adds or remove the possibility of scrolling through sections by using the keyboard arrow keys
19257        */
19258        function setKeyboardScrolling(value, directions){
19259            if(typeof directions !== 'undefined'){
19260                directions = directions.replace(/ /g,'').split(',');
19261
19262                $.each(directions, function (index, direction){
19263                    setIsScrollAllowed(value, direction, 'k');
19264                });
19265            }else{
19266                options.keyboardScrolling = value;
19267            }
19268        }
19269
19270        /**
19271        * Moves the page up one section.
19272        */
19273        function moveSectionUp(){
19274            var prev = $(SECTION_ACTIVE_SEL).prev(SECTION_SEL);
19275
19276            //looping to the bottom if there's no more sections above
19277            if (!prev.length && (options.loopTop || options.continuousVertical)) {
19278                prev = $(SECTION_SEL).last();
19279            }
19280
19281            if (prev.length) {
19282                scrollPage(prev, null, true);
19283            }
19284        }
19285
19286        /**
19287        * Moves the page down one section.
19288        */
19289        function moveSectionDown(){
19290            var next = $(SECTION_ACTIVE_SEL).next(SECTION_SEL);
19291
19292            //looping to the top if there's no more sections below
19293            if(!next.length &&
19294                (options.loopBottom || options.continuousVertical)){
19295                next = $(SECTION_SEL).first();
19296            }
19297
19298            if(next.length){
19299                scrollPage(next, null, false);
19300            }
19301        }
19302
19303        /**
19304        * Moves the page to the given section and slide with no animation.
19305        * Anchors or index positions can be used as params.
19306        */
19307        function silentMoveTo(sectionAnchor, slideAnchor){
19308            setScrollingSpeed (0, 'internal');
19309            moveTo(sectionAnchor, slideAnchor);
19310            setScrollingSpeed (originals.scrollingSpeed, 'internal');
19311        }
19312
19313        /**
19314        * Moves the page to the given section and slide.
19315        * Anchors or index positions can be used as params.
19316        */
19317        function moveTo(sectionAnchor, slideAnchor){
19318            var destiny = getSectionByAnchor(sectionAnchor);
19319
19320            if (typeof slideAnchor !== 'undefined'){
19321                scrollPageAndSlide(sectionAnchor, slideAnchor);
19322            }else if(destiny.length > 0){
19323                scrollPage(destiny);
19324            }
19325        }
19326
19327        /**
19328        * Slides right the slider of the active section.
19329        * Optional `section` param.
19330        */
19331        function moveSlideRight(section){
19332            moveSlide('right', section);
19333        }
19334
19335        /**
19336        * Slides left the slider of the active section.
19337        * Optional `section` param.
19338        */
19339        function moveSlideLeft(section){
19340            moveSlide('left', section);
19341        }
19342
19343        /**
19344         * When resizing is finished, we adjust the slides sizes and positions
19345         */
19346        function reBuild(resizing){
19347            if(container.hasClass(DESTROYED)){ return; }  //nothing to do if the plugin was destroyed
19348
19349            isResizing = true;
19350
19351            windowsHeight = $window.height();  //updating global var
19352
19353            $(SECTION_SEL).each(function(){
19354                var slidesWrap = $(this).find(SLIDES_WRAPPER_SEL);
19355                var slides = $(this).find(SLIDE_SEL);
19356
19357                //adjusting the height of the table-cell for IE and Firefox
19358                if(options.verticalCentered){
19359                    $(this).find(TABLE_CELL_SEL).css('height', getTableHeight($(this)) + 'px');
19360                }
19361
19362                $(this).css('height', windowsHeight + 'px');
19363
19364                //resizing the scrolling divs
19365                if(options.scrollOverflow){
19366                    if(slides.length){
19367                        slides.each(function(){
19368                            createScrollBar($(this));
19369                        });
19370                    }else{
19371                        createScrollBar($(this));
19372                    }
19373                }
19374
19375                //adjusting the position fo the FULL WIDTH slides...
19376                if (slides.length > 1) {
19377                    landscapeScroll(slidesWrap, slidesWrap.find(SLIDE_ACTIVE_SEL));
19378                }
19379            });
19380
19381            var activeSection = $(SECTION_ACTIVE_SEL);
19382            var sectionIndex = activeSection.index(SECTION_SEL);
19383
19384            //isn't it the first section?
19385            if(sectionIndex){
19386                //adjusting the position for the current section
19387                silentMoveTo(sectionIndex + 1);
19388            }
19389
19390            isResizing = false;
19391            $.isFunction( options.afterResize ) && resizing && options.afterResize.call(container);
19392            $.isFunction( options.afterReBuild ) && !resizing && options.afterReBuild.call(container);
19393        }
19394
19395        /**
19396        * Turns fullPage.js to normal scrolling mode when the viewport `width` or `height`
19397        * are smaller than the set limit values.
19398        */
19399        function setResponsive(active){
19400            var isResponsive = $body.hasClass(RESPONSIVE);
19401
19402            if(active){
19403                if(!isResponsive){
19404                    setAutoScrolling(false, 'internal');
19405                    setFitToSection(false, 'internal');
19406                    $(SECTION_NAV_SEL).hide();
19407                    $body.addClass(RESPONSIVE);
19408                    $.isFunction( options.afterResponsive ) && options.afterResponsive.call( container, active);
19409                }
19410            }
19411            else if(isResponsive){
19412                setAutoScrolling(originals.autoScrolling, 'internal');
19413                setFitToSection(originals.autoScrolling, 'internal');
19414                $(SECTION_NAV_SEL).show();
19415                $body.removeClass(RESPONSIVE);
19416                $.isFunction( options.afterResponsive ) && options.afterResponsive.call( container, active);
19417            }
19418        }
19419
19420        if($(this).length){
19421            //public functions
19422            FP.setAutoScrolling = setAutoScrolling;
19423            FP.setRecordHistory = setRecordHistory;
19424            FP.setScrollingSpeed = setScrollingSpeed;
19425            FP.setFitToSection = setFitToSection;
19426            FP.setLockAnchors = setLockAnchors;
19427            FP.setMouseWheelScrolling = setMouseWheelScrolling;
19428            FP.setAllowScrolling = setAllowScrolling;
19429            FP.setKeyboardScrolling = setKeyboardScrolling;
19430            FP.moveSectionUp = moveSectionUp;
19431            FP.moveSectionDown = moveSectionDown;
19432            FP.silentMoveTo = silentMoveTo;
19433            FP.moveTo = moveTo;
19434            FP.moveSlideRight = moveSlideRight;
19435            FP.moveSlideLeft = moveSlideLeft;
19436            FP.fitToSection = fitToSection;
19437            FP.reBuild = reBuild;
19438            FP.setResponsive = setResponsive;
19439            FP.destroy = destroy;
19440
19441            init();
19442
19443            bindEvents();
19444        }
19445
19446        function init(){
19447            //if css3 is not supported, it will use jQuery animations
19448            if(options.css3){
19449                options.css3 = support3d();
19450            }
19451
19452            options.scrollBar = options.scrollBar || options.hybrid;
19453
19454            setOptionsFromDOM();
19455            prepareDom();
19456            setAllowScrolling(true);
19457            setAutoScrolling(options.autoScrolling, 'internal');
19458            responsive();
19459
19460            //setting the class for the body element
19461            setBodyClass();
19462
19463            //Uncode addition
19464            // if(document.readyState === 'complete'){
19465            //     scrollToAnchor();
19466            // }
19467            // $window.on('load', scrollToAnchor);
19468        }
19469
19470        function bindEvents(){
19471            $window
19472                //when scrolling...
19473                //.on('scroll', scrollHandler)
19474
19475                //detecting any change on the URL to scroll to the given anchor link
19476                //(a way to detect back history button as we play with the hashes on the URL)
19477                .on('hashchange', hashChangeHandler)
19478
19479                //when opening a new tab (ctrl + t), `control` won't be pressed when coming back.
19480                .blur(blurHandler)
19481
19482                //when resizing the site, we adjust the heights of the sections, slimScroll...
19483                .resize(resizeHandler);
19484
19485            $document
19486                //Sliding with arrow keys, both, vertical and horizontal
19487                .keydown(keydownHandler)
19488
19489                //to prevent scrolling while zooming
19490                .keyup(keyUpHandler)
19491
19492                //Scrolls to the section when clicking the navigation bullet
19493                .on('click touchstart', SECTION_NAV_SEL + ' a', sectionBulletHandler)
19494
19495                //Scrolls the slider to the given slide destination for the given section
19496                .on('click touchstart', SLIDES_NAV_LINK_SEL, slideBulletHandler)
19497
19498                .on('click', SECTION_NAV_TOOLTIP_SEL, tooltipTextHandler);
19499
19500            //Scrolling horizontally when clicking on the slider controls.
19501            $(SECTION_SEL).on('click touchstart', SLIDES_ARROW_SEL, slideArrowHandler);
19502
19503            /**
19504            * Applying normalScroll elements.
19505            * Ignoring the scrolls over the specified selectors.
19506            */
19507            if(options.normalScrollElements){
19508                $document.on('mouseenter', options.normalScrollElements, function () {
19509                    setMouseWheelScrolling(false);
19510                });
19511
19512                $document.on('mouseleave', options.normalScrollElements, function(){
19513                    setMouseWheelScrolling(true);
19514                });
19515            }
19516        }
19517
19518        /**
19519        * Setting options from DOM elements if they are not provided.
19520        */
19521        function setOptionsFromDOM(){
19522            var sections = container.find(options.sectionSelector);
19523
19524            //no anchors option? Checking for them in the DOM attributes
19525            if(!options.anchors.length){
19526                options.anchors = sections.filter('[data-anchor]').map(function(){
19527                    return $(this).data('anchor').toString();
19528                }).get();
19529            }
19530
19531            //no tooltips option? Checking for them in the DOM attributes
19532            if(!options.navigationTooltips.length){
19533                options.navigationTooltips = sections.filter('[data-tooltip]').map(function(){
19534                    return $(this).data('tooltip').toString();
19535                }).get();
19536            }
19537        }
19538
19539        /**
19540        * Works over the DOM structure to set it up for the current fullpage options.
19541        */
19542        function prepareDom(){
19543            container.css({
19544                'height': '100%',
19545                'position': 'relative'
19546            });
19547
19548            //adding a class to recognize the container internally in the code
19549            container.addClass(WRAPPER);
19550            $('html').addClass(ENABLED);
19551
19552            windowsHeight = $window.height();
19553
19554            container.removeClass(DESTROYED); //in case it was destroyed before initializing it again
19555
19556            addInternalSelectors();
19557
19558             //styling the sections / slides / menu
19559            $(SECTION_SEL).each(function(index){
19560                var section = $(this);
19561                var slides = section.find(SLIDE_SEL);
19562                var numSlides = slides.length;
19563
19564                styleSection(section, index);
19565                styleMenu(section, index);
19566
19567                // if there's any slide
19568                if (numSlides > 0) {
19569                    styleSlides(section, slides, numSlides);
19570                }else{
19571                    if(options.verticalCentered){
19572                        addTableClass(section);
19573                    }
19574                }
19575            });
19576
19577            //fixed elements need to be moved out of the plugin container due to problems with CSS3.
19578            if(options.fixedElements && options.css3){
19579                $(options.fixedElements).appendTo($body);
19580            }
19581
19582            //vertical centered of the navigation + active bullet
19583            if(options.navigation){
19584                addVerticalNavigation();
19585            }
19586
19587            enableYoutubeAPI();
19588
19589            if(options.scrollOverflow){
19590                if(document.readyState === 'complete'){
19591                    createScrollBarHandler();
19592                }
19593                //after DOM and images are loaded
19594                $window.on('load', createScrollBarHandler);
19595            }else{
19596                afterRenderActions();
19597            }
19598        }
19599
19600        /**
19601        * Styles the horizontal slides for a section.
19602        */
19603        function styleSlides(section, slides, numSlides){
19604            var sliderWidth = numSlides * 100;
19605            var slideWidth = 100 / numSlides;
19606
19607            slides.wrapAll('<div class="' + SLIDES_CONTAINER + '" />');
19608            slides.parent().wrap('<div class="' + SLIDES_WRAPPER + '" />');
19609
19610            section.find(SLIDES_CONTAINER_SEL).css('width', sliderWidth + '%');
19611
19612            if(numSlides > 1){
19613                if(options.controlArrows){
19614                    createSlideArrows(section);
19615                }
19616
19617                if(options.slidesNavigation){
19618                    addSlidesNavigation(section, numSlides);
19619                }
19620            }
19621
19622            slides.each(function(index) {
19623                $(this).css('width', slideWidth + '%');
19624
19625                if(options.verticalCentered){
19626                    addTableClass($(this));
19627                }
19628            });
19629
19630            var startingSlide = section.find(SLIDE_ACTIVE_SEL);
19631
19632            //if the slide won't be an starting point, the default will be the first one
19633            //the active section isn't the first one? Is not the first slide of the first section? Then we load that section/sl
19633ide by default.
19634            if( startingSlide.length &&  ($(SECTION_ACTIVE_SEL).index(SECTION_SEL) !== 0 || ($(SECTION_ACTIVE_SEL).index(SECTION_SEL) === 0 && startingSlide.index() !== 0))){
19635                silentLandscapeScroll(startingSlide, 'internal');
19636            }else{
19637                slides.eq(0).addClass(ACTIVE);
19638            }
19639        }
19640
19641        /**
19642        * Styling vertical sections
19643        */
19644        function styleSection(section, index){
19645            //if no active section is defined, the 1st one will be the default one
19646            if(!index && $(SECTION_ACTIVE_SEL).length === 0) {
19647                section.addClass(ACTIVE);
19648            }
19649            startingSection = $(SECTION_ACTIVE_SEL);
19650
19651            section.css('height', windowsHeight + 'px');
19652
19653            if(options.paddingTop){
19654                section.css('padding-top', options.paddingTop);
19655            }
19656
19657            if(options.paddingBottom){
19658                section.css('padding-bottom', options.paddingBottom);
19659            }
19660
19661            if (typeof options.sectionsColor[index] !==  'undefined') {
19662                section.css('background-color', options.sectionsColor[index]);
19663            }
19664
19665            if (typeof options.anchors[index] !== 'undefined') {
19666                section.attr('data-anchor', options.anchors[index]);
19667            }
19668        }
19669
19670        /**
19671        * Sets the data-anchor attributes to the menu elements and activates the current one.
19672        */
19673        function styleMenu(section, index){
19674            if (typeof options.anchors[index] !== 'undefined') {
19675                //activating the menu / nav element on load
19676                if(section.hasClass(ACTIVE)){
19677                    activateMenuAndNav(options.anchors[index], index);
19678                }
19679            }
19680
19681            //moving the menu outside the main container if it is inside (avoid problems with fixed positions when using CSS3 tranforms)
19682            if(options.menu && options.css3 && $(options.menu).closest(WRAPPER_SEL).length){
19683                $(options.menu).appendTo($body);
19684            }
19685        }
19686
19687        /**
19688        * Adds internal classes to be able to provide customizable selectors
19689        * keeping the link with the style sheet.
19690        */
19691        function addInternalSelectors(){
19692            container.find(options.sectionSelector).addClass(SECTION);
19693            container.find(options.slideSelector).addClass(SLIDE);
19694        }
19695
19696        /**
19697        * Creates the control arrows for the given section
19698        */
19699        function createSlideArrows(section){
19700            section.find(SLIDES_WRAPPER_SEL).after('<div class="' + SLIDES_ARROW_PREV + '"></div><div class="' + SLIDES_ARROW_NEXT + '"></div>');
19701
19702            if(options.controlArrowColor!='#fff'){
19703                section.find(SLIDES_ARROW_NEXT_SEL).css('border-color', 'transparent transparent transparent '+options.controlArrowColor);
19704                section.find(SLIDES_ARROW_PREV_SEL).css('border-color', 'transparent '+ options.controlArrowColor + ' transparent transparent');
19705            }
19706
19707            if(!options.loopHorizontal){
19708                section.find(SLIDES_ARROW_PREV_SEL).hide();
19709            }
19710        }
19711
19712        /**
19713        * Creates a vertical navigation bar.
19714        */
19715        function addVerticalNavigation(){
19716            $body.append('<div id="' + SECTION_NAV + '"><ul></ul></div>');
19717            var nav = $(SECTION_NAV_SEL);
19718
19719            nav.addClass(function() {
19720                return options.showActiveTooltip ? SHOW_ACTIVE_TOOLTIP + ' ' + options.navigationPosition : options.navigationPosition;
19721            });
19722
19723            for (var i = 0; i < $(SECTION_SEL).length; i++) {
19724                var link = '';
19725                if (options.anchors.length) {
19726                    link = options.anchors[i];
19727                }
19728
19729                var li = '<li><a href="#' + link + '"><span></span></a>';
19730
19731                // Only add tooltip if needed (defined by user)
19732                var tooltip = options.navigationTooltips[i];
19733
19734                if (typeof tooltip !== 'undefined' && tooltip !== '') {
19735                    li += '<div class="' + SECTION_NAV_TOOLTIP + ' ' + options.navigationPosition + '">' + tooltip + '</div>';
19736                }
19737
19738                li += '</li>';
19739
19740                nav.find('ul').append(li);
19741            }
19742
19743            //centering it vertically
19744            $(SECTION_NAV_SEL).css('margin-top', '-' + ($(SECTION_NAV_SEL).height()/2) + 'px');
19745
19746            //activating the current active section
19747            $(SECTION_NAV_SEL).find('li').eq($(SECTION_ACTIVE_SEL).index(SECTION_SEL)).find('a').addClass(ACTIVE);
19748        }
19749
19750        /**
19751        * Creates the slim scroll scrollbar for the sections and slides inside them.
19752        */
19753        function createScrollBarHandler(){
19754            $(SECTION_SEL).each(function(){
19755                var slides = $(this).find(SLIDE_SEL);
19756
19757                if(slides.length){
19758                    slides.each(function(){
19759                        createScrollBar($(this));
19760                    });
19761                }else{
19762                    createScrollBar($(this));
19763                }
19764
19765            });
19766            afterRenderActions();
19767        }
19768
19769        /*
19770        * Enables the Youtube videos API so we can control their flow if necessary.
19771        */
19772        function enableYoutubeAPI(){
19773            container.find('iframe[src*="youtube.com/embed/"]').each(function(){
19774                addURLParam($(this), 'enablejsapi=1');
19775            });
19776        }
19777
19778        /**
19779        * Adds a new parameter and its value to the `src` of a given element
19780        */
19781        function addURLParam(element, newParam){
19782            var originalSrc = element.attr('src');
19783            element.attr('src', originalSrc + getUrlParamSign(originalSrc) + newParam);
19784        }
19785
19786        /*
19787        * Returns the prefix sign to use for a new parameter in an existen URL.
19788        *
19789        * @return {String}  ? | &
19790        */
19791        function getUrlParamSign(url){
19792            return ( !/\?/.test( url ) ) ? '?' : '&';
19793        }
19794
19795        /**
19796        * Actions and callbacks to fire afterRender
19797        */
19798        function afterRenderActions(){
19799            var section = $(SECTION_ACTIVE_SEL);
19800
19801            section.addClass(COMPLETELY);
19802
19803            if(options.scrollOverflowHandler.afterRender){
19804                options.scrollOverflowHandler.afterRender(section);
19805            }
19806            lazyLoad(section);
19807            playMedia(section);
19808            options.scrollOverflowHandler.afterLoad();
19809            
19810            if(isDestinyTheStartingSection()){
19811                $.isFunction( options.afterLoad ) && options.afterLoad.call(section, section.data('anchor'), (section.index(SECTION_SEL) + 1));
19812            }
19813
19814            $.isFunction( options.afterRender ) && options.afterRender.call(container);
19815        }
19816
19817        /**
19818        * Determines if the URL anchor destiny is the starting section (the one using 'active' class before initialization)
19819        */
19820        function isDestinyTheStartingSection(){
19821            var anchors =  window.location.hash.replace('#', '').split('/');
19822            var destinationSection = getSectionByAnchor(decodeURIComponent(anchors[0]));
19823    
19824            return !destinationSection.length || destinationSection.length && destinationSection.index() === startingSection.index();
19825        }
19826
19827
19828        var isScrolling = false;
19829        var lastScroll = 0;
19830
19831        //when scrolling...
19832        function scrollHandler(){
19833            var currentSection;
19834
19835            if(!options.autoScrolling || options.scrollBar){
19836                var currentScroll = $window.scrollTop();
19837                var scrollDirection = getScrollDirection(currentScroll);
19838                var visibleSectionIndex = 0;
19839                var screen_mid = currentScroll + ($window.height() / 2.0);
19840                var isAtBottom = $body.height() - $window.height() === currentScroll;
19841                var sections =  document.querySelectorAll(SECTION_SEL);
19842
19843                //when using `auto-height` for a small last section it won't be centered in the viewport
19844                if(isAtBottom){
19845                    visibleSectionIndex = sections.length - 1;
19846                }
19847                //is at top? when using `auto-height` for a small first section it won't be centered in the viewport
19848                else if(!currentScroll){
19849                    visibleSectionIndex = 0;
19850                }
19851
19852                //taking the section which is showing more content in the viewport
19853                else{
19854                    for (var i = 0; i < sections.length; ++i) {
19855                        var section = sections[i];
19856
19857                        // Pick the the last section which passes the middle line of the screen.
19858                        if (section.offsetTop <= screen_mid)
19859                        {
19860                            visibleSectionIndex = i;
19861                        }
19862                    }
19863                }
19864
19865                if(isCompletelyInViewPort(scrollDirection)){
19866                    if(!$(SECTION_ACTIVE_SEL).hasClass(COMPLETELY)){
19867                        $(SECTION_ACTIVE_SEL).addClass(COMPLETELY).siblings().removeClass(COMPLETELY);
19868                    }
19869                }
19870
19871                //geting the last one, the current one on the screen
19872                currentSection = $(sections).eq(visibleSectionIndex);
19873
19874                //setting the visible section as active when manually scrolling
19875                //executing only once the first time we reach the section
19876                if(!currentSection.hasClass(ACTIVE)){
19877                    isScrolling = true;
19878                    var leavingSection = $(SECTION_ACTIVE_SEL);
19879                    var leavingSectionIndex = leavingSection.index(SECTION_SEL) + 1;
19880                    var yMovement = getYmovement(currentSection);
19881                    var anchorLink  = currentSection.data('anchor');
19882                    var sectionIndex = currentSection.index(SECTION_SEL) + 1;
19883                    var activeSlide = currentSection.find(SLIDE_ACTIVE_SEL);
19884                    var slideIndex;
19885                    var slideAnchorLink;
19886
19887                    if(activeSlide.length){
19888                        slideAnchorLink = activeSlide.data('anchor');
19889                        slideIndex = activeSlide.index();
19890                    }
19891
19892                    if(canScroll){
19893                        currentSection.addClass(ACTIVE).siblings().removeClass(ACTIVE);
19894
19895                        $.isFunction( options.onLeave ) && options.onLeave.call( leavingSection, leavingSectionIndex, sectionIndex, yMovement);
19896                        $.isFunction( options.afterLoad ) && options.afterLoad.call( currentSection, anchorLink, sectionIndex);
19897
19898                        stopMedia(leavingSection);
19899                        lazyLoad(currentSection);
19900                        playMedia(currentSection);
19901
19902                        activateMenuAndNav(anchorLink, sectionIndex - 1);
19903
19904                        if(options.anchors.length){
19905                            //needed to enter in hashChange event when using the menu with anchor links
19906                            lastScrolledDestiny = anchorLink;
19907                        }
19908                        setState(slideIndex, slideAnchorLink, anchorLink, sectionIndex);
19909                    }
19910
19911                    //small timeout in order to avoid entering in hashChange event when scrolling is not finished yet
19912                    clearTimeout(scrollId);
19913                    scrollId = setTimeout(function(){
19914                        isScrolling = false;
19915                    }, 100);
19916                }
19917
19918                if(options.fitToSection){
19919                    //for the auto adjust of the viewport to fit a whole section
19920                    clearTimeout(scrollId2);
19921
19922                    scrollId2 = setTimeout(function(){
19923                        //checking it again in case it changed during the delay
19924                        if(options.fitToSection){
19925                            fitToSection();
19926                        }
19927                    }, options.fitToSectionDelay);
19928                }
19929            }
19930        }
19931
19932        /**
19933        * Fits the site to the nearest active section
19934        */
19935        function fitToSection(){
19936            //checking fitToSection again in case it was set to false before the timeout delay
19937            if(canScroll){
19938                //allows to scroll to an active section and
19939                //if the section is already active, we prevent firing callbacks
19940                isResizing = true;
19941
19942                scrollPage($(SECTION_ACTIVE_SEL));
19943                isResizing = false;
19944            }
19945        }
19946
19947        /**
19948        * Determines whether the active section has seen in its whole or not.
19949        */
19950        function isCompletelyInViewPort(movement){
19951            var top = $(SECTION_ACTIVE_SEL).position().top;
19952            var bottom = top + $window.height();
19953
19954            if(movement == 'up'){
19955                return bottom >= ($window.scrollTop() + $window.height());
19956            }
19957            return top <= $window.scrollTop();
19958        }
19959
19960        /**
19961        * Gets the directon of the the scrolling fired by the scroll event.
19962        */
19963        function getScrollDirection(currentScroll){
19964            var direction = currentScroll > lastScroll ? 'down' : 'up';
19965
19966            lastScroll = currentScroll;
19967
19968            //needed for auto-height sections to determine if we want to scroll to the top or bottom of the destination
19969            previousDestTop = currentScroll;
19970
19971            return direction;
19972        }
19973
19974        /**
19975        * Determines the way of scrolling up or down:
19976        * by 'automatically' scrolling a section or by using the default and n
19976ormal scrolling.
19977        */
19978        function scrolling(type, scrollable){
19979            if (!isScrollAllowed.m[type]){
19980                return;
19981            }
19982            var check = (type === 'down') ? 'bottom' : 'top';
19983            var scrollSection = (type === 'down') ? moveSectionDown : moveSectionUp;
19984
19985            if(scrollable.length > 0 ){
19986                //is the scrollbar at the start/end of the scroll?
19987                if(options.scrollOverflowHandler.isScrolled(check, scrollable)){
19988                    scrollSection();
19989                }else{
19990                    return true;
19991                }
19992            }else{
19993                // moved up/down
19994                scrollSection();
19995            }
19996        }
19997
19998        /*
19999        * Preventing bouncing in iOS #2285
20000        */
20001        function preventBouncing(event){
20002            var e = event.originalEvent;
20003            if(!checkParentForNormalScrollElement(event.target) && options.autoScrolling && isReallyTouch(e)){
20004                //preventing the easing on iOS devices
20005                event.preventDefault();
20006            }
20007        }
20008
20009        var touchStartY = 0;
20010        var touchStartX = 0;
20011        var touchEndY = 0;
20012        var touchEndX = 0;
20013
20014        /* Detecting touch events
20015
20016        * As we are changing the top property of the page on scrolling, we can not use the traditional way to detect it.
20017        * This way, the touchstart and the touch moves shows an small difference between them which is the
20018        * used one to determine the direction.
20019        */
20020        function touchMoveHandler(event){
20021            var e = event.originalEvent;
20022            var activeSection = $(e.target).closest(SECTION_SEL);
20023
20024            // additional: if one of the normalScrollElements isn't within options.normalScrollElementTouchThreshold hops up the DOM chain
20025            if (!checkParentForNormalScrollElement(event.target) && isReallyTouch(e) ) {
20026
20027                if(options.autoScrolling){
20028                    //preventing the easing on iOS devices
20029                    event.preventDefault();
20030                }
20031
20032                var scrollable = options.scrollOverflowHandler.scrollable(activeSection);
20033                var touchEvents = getEventsPage(e);
20034
20035                touchEndY = touchEvents.y;
20036                touchEndX = touchEvents.x;
20037
20038                //if movement in the X axys is greater than in the Y and the currect section has slides...
20039                if (activeSection.find(SLIDES_WRAPPER_SEL).length && Math.abs(touchStartX - touchEndX) > (Math.abs(touchStartY - touchEndY))) {
20040
20041                    //is the movement greater than the minimum resistance to scroll?
20042                    if (!slideMoving && Math.abs(touchStartX - touchEndX) > ($window.outerWidth() / 100 * options.touchSensitivity)) {
20043                        if (touchStartX > touchEndX) {
20044                            if(isScrollAllowed.m.right){
20045                                moveSlideRight(activeSection); //next
20046                            }
20047                        } else {
20048                            if(isScrollAllowed.m.left){
20049                                moveSlideLeft(activeSection); //prev
20050                            }
20051                        }
20052                    }
20053                }
20054
20055                //vertical scrolling (only when autoScrolling is enabled)
20056                else if(options.autoScrolling && canScroll){
20057
20058                    //is the movement greater than the minimum resistance to scroll?
20059                    if (Math.abs(touchStartY - touchEndY) > ($window.height() / 100 * options.touchSensitivity)) {
20060                        if (touchStartY > touchEndY) {
20061                            scrolling('down', scrollable);
20062                        } else if (touchEndY > touchStartY) {
20063                            scrolling('up', scrollable);
20064                        }
20065                    }
20066                }
20067            }
20068        }
20069
20070        /**
20071         * recursive function to loop up the parent nodes to check if one of them exists in options.normalScrollElements
20072         * Currently works well for iOS - Android might need some testing
20073         * @param  {Element} el  target element / jquery selector (in subsequent nodes)
20074         * @param  {int}     hop current hop compared to options.normalScrollElementTouchThreshold
20075         * @return {boolean} true if there is a match to options.normalScrollElements
20076         */
20077        function checkParentForNormalScrollElement (el, hop) {
20078            hop = hop || 0;
20079            var parent = $(el).parent();
20080
20081            if (hop < options.normalScrollElementTouchThreshold &&
20082                parent.is(options.normalScrollElements) ) {
20083                return true;
20084            } else if (hop == options.normalScrollElementTouchThreshold) {
20085                return false;
20086            } else {
20087                return checkParentForNormalScrollElement(parent, ++hop);
20088            }
20089        }
20090
20091        /**
20092        * As IE >= 10 fires both touch and mouse events when using a mouse in a touchscreen
20093        * this way we make sure that is really a touch event what IE is detecting.
20094        */
20095        function isReallyTouch(e){
20096            //if is not IE   ||  IE is detecting `touch` or `pen`
20097            return typeof e.pointerType === 'undefined' || e.pointerType != 'mouse';
20098        }
20099
20100        /**
20101        * Handler for the touch start event.
20102        */
20103        function touchStartHandler(event){
20104            var e = event.originalEvent;
20105
20106            //stopping the auto scroll to adjust to a section
20107            if(options.fitToSection){
20108                $htmlBody.stop();
20109            }
20110
20111            if(isReallyTouch(e)){
20112                var touchEvents = getEventsPage(e);
20113                touchStartY = touchEvents.y;
20114                touchStartX = touchEvents.x;
20115            }
20116        }
20117
20118        /**
20119        * Gets the average of the last `number` elements of the given array.
20120        */
20121        function getAverage(elements, number){
20122            var sum = 0;
20123
20124            //taking `number` elements from the end to make the average, if there are not enought, 1
20125            var lastElements = elements.slice(Math.max(elements.length - number, 1));
20126
20127            for(var i = 0; i < lastElements.length; i++){
20128                sum = sum + lastElements[i];
20129            }
20130
20131            return Math.ceil(sum/number);
20132        }
20133
20134        /**
20135         * Detecting mousewheel scrolling
20136         *
20137         * http://blogs.sitepointstatic.com/examples/tech/mouse-wheel/index.html
20138         * http://www.sitepoint.com/html5-javascript-mouse-wheel/
20139         */
20140        var prevTime = new Date().getTime();
20141
20142        function MouseWheelHandler(e) {
20143            var curTime = new Date().getTime();
20144            var isNormalScroll = $(COMPLETELY_SEL).hasClass(NORMAL_SCROLL);
20145
20146            //autoscrolling and not zooming?
20147            if(options.autoScrolling && !controlPressed && !isNormalScroll){
20148                // cross-browser wheel delta
20149                e = e || window.event;
20150                var value = e.wheelDelta || -e.deltaY || -e.detail;
20151                var delta = Math.max(-1, Math.min(1, value));
20152
20153                var horizontalDetection = typeof e.wheelDeltaX !== 'undefined' || typeof e.deltaX !== 'undefined';
20154                var isScrollingVertically = (Math.abs(e.wheelDeltaX) < Math.abs(e.wheelDelta)) || (Math.abs(e.deltaX ) < Math.abs(e.deltaY) || !horizontalDetection);
20155
20156                //Limiting the array to 150 (lets not waste memory!)
20157                if(scrollings.length > 149){
20158                    scrollings.shift();
20159                }
20160
20161                //keeping record of the previous scrollings
20162                scrollings.push(Math.abs(value));
20163
20164                //preventing to scroll the site on mouse wheel when scrollbar is present
20165                if(options.scrollBar){
20166                    e.preventDefault ? e.preventDefault() : e.returnValue = false;
20167                }
20168
20169                var activeSection = $(SECTION_ACTIVE_SEL);
20170                var scrollable = options.scrollOverflowHandler.scrollable(activeSection);
20171
20172                //time difference between the last scroll and the current one
20173                var timeDiff = curTime-prevTime;
20174                prevTime = curTime;
20175
20176                //haven't they scrolled in a while?
20177                //(enough to be consider a different scrolling action to scroll another section)
20178                if(timeDiff > 200){
20179                    //emptying the array, we dont care about old scrollings for our averages
20180                    scrollings = [];
20181                }
20182
20183                if(canScroll){
20184                    var averageEnd = getAverage(scrollings, 10);
20185                    var averageMiddle = getAverage(scrollings, 70);
20186                    var isAccelerating = averageEnd >= averageMiddle;
20187
20188                    //to avoid double swipes...
20189                    if(isAccelerating && isScrollingVertically){
20190                        //scrolling down?
20191                        if (delta < 0) {
20192                            scrolling('down', scrollable);
20193
20194                        //scrolling up?
20195                        }else {
20196                            scrolling('up', scrollable);
20197                        }
20198                    }
20199                }
20200
20201                return false;
20202            }
20203
20204            if(options.fitToSection){
20205                //stopping the auto scroll to adjust to a section
20206                $htmlBody.stop();
20207            }
20208        }
20209
20210        /**
20211        * Slides a slider to the given direction.
20212        * Optional `section` param.
20213        */
20214        function moveSlide(direction, section){
20215            var activeSection = typeof section === 'undefined' ? $(SECTION_ACTIVE_SEL) : section;
20216            var slides = activeSection.find(SLIDES_WRAPPER_SEL);
20217            var numSlides = slides.find(SLIDE_SEL).length;
20218
20219            // more than one slide needed and nothing should be sliding
20220            if (!slides.length || slideMoving || numSlides < 2) {
20221                return;
20222            }
20223
20224            var currentSlide = slides.find(SLIDE_ACTIVE_SEL);
20225            var destiny = null;
20226
20227            if(direction === 'left'){
20228                destiny = currentSlide.prev(SLIDE_SEL);
20229            }else{
20230                destiny = currentSlide.next(SLIDE_SEL);
20231            }
20232
20233            //isn't there a next slide in the secuence?
20234            if(!destiny.length){
20235                //respect loopHorizontal settin
20236                if (!options.loopHorizontal) return;
20237
20238                if(direction === 'left'){
20239                    destiny = currentSlide.siblings(':last');
20240                }else{
20241                    destiny = currentSlide.siblings(':first');
20242                }
20243            }
20244
20245            slideMoving = true;
20246
20247            landscapeScroll(slides, destiny, direction);
20248        }
20249
20250        /**
20251        * Maintains the active slides in the viewport
20252        * (Because the `scroll` animation might get lost with some actions, such as when using continuousVertical)
20253        */
20254        function keepSlidesPosition(){
20255            $(SLIDE_ACTIVE_SEL).each(function(){
20256                silentLandscapeScroll($(this), 'internal');
20257            });
20258        }
20259
20260        var previousDestTop = 0;
20261        /**
20262        * Returns the destination Y position based on the scrolling direction and
20263        * the height of the section.
20264        */
20265        function getDestinationPosition(element){
20266            var elemPosition = element.position();
20267
20268            //top of the desination will be at the top of the viewport
20269            var position = elemPosition.top;
20270            var isScrollingDown =  elemPosition.top > previousDestTop;
20271            var sectionBottom = position - windowsHeight + element.outerHeight();
20272            var bigSectionsDestination = options.bigSectionsDestination;
20273
20274            //Uncode addition
20275            var containerH = container.outerHeight();
20276            var containerPosition = container.offset();
20277
20278            //########### Commented by Uncode - START ###########
20279            //is the destination element bigger than the viewport?
20280            // if(element.outerHeight() > windowsHeight){
20281            //     //scrolling up?
20282            //     if(!isScrollingDown && !bigSectionsDestination || bigSectionsDestination === 'bottom' ){
20283            //         position = sectionBottom;
20284            //     }
20285            // }
20286
20287            // //sections equal or smaller than the viewport height && scrolling down? ||  is resizing and its in the last section
20288            // else if(isScrollingDown || (isResizing && element.is(':last-child')) ){
20289            //     //The bottom of the destination will be at the bottom of the viewport
20290            //     position = sectionBottom;
20291            // }
20292            //########### Commented by Uncode - END ###########
20293
20294            //Uncode addition
20295            if ( !$masthead.hasClass('menu-transparent') && $('body').hasClass('uncode-fp-menu-shrink') && !element.is(':first-child') )
20296                position += 18;
20297            if ( ( containerH + menuHeight + bodyBorder + adminBarHeight - windowsHeight ) < position || ( isResizing && element.is(':last-child') ) ) {
20298                position = sectionBottom + menuHeight + bodyBorder*2 + adminBarHeight;
20299            }
20300            
20301            /*
20302            Keeping record of the last scrolled position to determine the scrolling direction.
20303            No conventional methods can be used as the scroll bar might not be present
20304            AND the section might not be active if it is auto-height and didnt reach the middle
20305            of the viewport.
20306            */
20307            previousDestTop = position;
20308            return position;
20309        }
20310
20311        /**
20312        * Scrolls the site to the given element and scrolls to the slide if a callback is given.
20313        */
20314        function scrollPage(element, callback, isMovementUp){
20315            if(typeof element === 'undefined'){ return; } //there's no element to scroll, leaving the function
20316
20317            var dtop = getDestinationPosition(element);
20318            var slideAnchorLink;
20319            var slideIndex;
20320
20321            //local variables
20322            var v = {
20323                element: element,
20324                callback: callback,
20325                isMovementUp: isMovementUp,
20326                dtop: dtop,
20327                yMovement: getYmovement(element),
20328                anchorLink: element.data('anchor'),
20329                sectionIndex: element.index(SECTION_SEL),
20330                activeSlide: element.find(SLIDE_ACTIVE_SEL),
20331                activeSection: $(SECTION_ACTIVE_SEL),
20332                leavingSection: $(SECTION_ACTIVE_SEL).index(SECTION_SEL) + 1,
20333
20334                //caching the value of isResizing at the momment the function is called
20335                //because it will be checked later inside a setTimeout and the value might change
20336                localIsResizing: isResizing
20337            };
20338
20339            //quiting when destination scroll is the same as the current one
20340            if((v.activeSection.is(element) && !isResizing) || (options.scrollBar && $window.scrollTop() === v.dtop && !element.hasClass(AUTO_HEIGHT) )){ return; }
20341
20342            if(v.activeSlide.length){
20343                slideAnchorLink = v.activeSlide.data('anchor');
20344                slideIndex = v.activeSlide.index();
20345            }
20346
20347            // If continuousVertical && we need to wrap around
20348            if (options.autoScrolling && options.continuousVertical && typeof (v.isMovementUp) !== "undefined" &&
20349                ((!v.isMovementUp && v.yMovement == 'up') || // Intending to scroll down but about to go up or
20350                (v.isMovementUp && v.yMovement == 'down'))) { // intending to scroll up but about to go down
20351
20352                v = createInfiniteSections(v);
20353            }
20354
20355            //callback (onLeave) if the site is not just resizing and readjusting the slides
20356            if($.isFunction(options.onLeave) && !v.localIsResizing){
20357                if(options.onLeave.call(v.activeSection, v.leavingSection, (v.sectionIndex + 1), v.yMovement) === false){
20358                    return;
20359                }
20360            }
20361
20362            //pausing media of the leaving section (if we are not just resizing, as destinatino will be the same one)
20363            if(!v.localIsResizing){
20364                stopMedia(v.activeSection);
20365            }
20366
20367            options.scrollOverflowHandler.beforeLeave();
20368            element.addClass(ACTIVE).siblings().removeClass(ACTIVE);
20369            lazyLoad(element);
20370            options.scrollOverflowHandler.onLeave();
20371
20372
20373            //preventing from activating the MouseWheelHandler event
20374            //more than once if the page is scrolling
20375            canScroll = false;
20376
20377            setState(slideIndex, slideAnchorLink, v.anchorLink, v.sectionIndex);
20378
20379            performMovement(v);
20380
20381            //flag to avoid callingn `scrollPage()` twice in case of using anchor links
20382            lastScrolledDestiny = v.anchorLink;
20383
20384            //avoid firing it twice (as it does also on scroll)
20385            activateMenuAndNav(v.anchorLink, v.sectionIndex);
20386        }
20387
20388        /**
20389        * Performs the vertical movement (by CSS3 or by jQuery)
20390        */
20391        function performMovement(v){
20392            // using CSS3 translate functionality
20393            if (options.css3 && options.autoScrolling && !options.scrollBar) {
20394
20395                // The first section can have a negative value in iOS 10. Not quite sure why: -0.0142822265625
20396                // that's why we round it to 0.
20397                var translate3d = 'translate3d(0px, -' + Math.round(v.dtop) + 'px, 0px)';
20398                transformContainer(translate3d, true);
20399
20400                //even when the scrollingSpeed is 0 there's a little delay, which might cause the
20401                //scrollingSpeed to change in case of using silentMoveTo();
20402                if(options.scrollingSpeed){
20403                    clearTimeout(afterSectionLoadsId);
20404                    afterSectionLoadsId = setTimeout(function () {
20405                        afterSectionLoads(v);
20406                    }, options.scrollingSpeed);
20407                }else{
20408                    afterSectionLoads(v);
20409                }
20410            }
20411
20412            // using jQuery animate
20413            else{
20414                var scrollSettings = getScrollSettings(v);
20415
20416                $(scrollSettings.element).animate(
20417                    scrollSettings.options,
20418                options.scrollingSpeed, options.easing).promise().done(function () { //only one single callback in case of animating  `html, body`
20419                    if(options.scrollBar){
20420
20421                        /* Hack!
20422                        The timeout prevents setting the most dominant section in the viewport as "active" when the user
20423                        scrolled to a smaller section by using the mousewheel (auto scrolling) rather than draging the scroll bar.
20424
20425                        When using scrollBar:true It seems like the scroll events still getting propagated even after the scrolling animation has finished.
20426                        */
20427                        setTimeout(function(){
20428                            afterSectionLoads(v);
20429                        },30);
20430                    }else{
20431                        afterSectionLoads(v);
20432                    }
20433                });
20434            }
20435        }
20436
20437        /**
20438        * Gets the scrolling settings depending on the plugin autoScrolling option
20439        */
20440        function getScrollSettings(v){
20441            var scroll = {};
20442
20443            if(options.autoScrolling && !options.scrollBar){
20444                scroll.options = { 'top': -v.dtop};
20445                scroll.element = WRAPPER_SEL;
20446            }else{
20447                scroll.options = { 'scrollTop': v.dtop};
20448                scroll.element = 'html, body';
20449            }
20450
20451            return scroll;
20452        }
20453
20454        /**
20455        * Adds sections before or after the current one to create the infinite effect.
20456        */
20457        function createInfiniteSections(v){
20458            // Scrolling down
20459            if (!v.isMovementUp) {
20460                // Move all previous sections to after the active section
20461                $(SECTION_ACTIVE_SEL).after(v.activeSection.prevAll(SECTION_SEL).get().reverse());
20462            }
20463            else { // Scrolling up
20464                // Move all next sections to before the active section
20465                $(SECTION_ACTIVE_SEL).before(v.activeSection.nextAll(SECTION_SEL));
20466            }
20467
20468            // Maintain the displayed position (now that we changed the element order)
20469            silentScroll($(SECTION_ACTIVE_SEL).position().top);
20470
20471            // Maintain the active slides visible in the viewport
20472            keepSlidesPosition();
20473
20474            // save for later the elements that still need to be reordered
20475            v.wrapAroundElements = v.activeSection;
20476
20477            // Recalculate animation variables
20478            v.dtop = v.element.position().top;
20479            v.yMovement = getYmovement(v.element);
20480
20481            return v;
20482        }
20483
20484        /**
20485        * Fix section order after continuousVertical changes have been animated
20486        */
20487        function continuousVerticalFixSectionOrder (v) {
20488            // If continuousVertical is in effect (and autoScrolling would also be in effect then),
20489            // finish moving the elements around so the direct navigation will function more simply
20490            if (!v.wrapAroundElements || !v.wrapAroundElements.length) {
20491                return;
20492            }
20493
20494            if (v.isMovementUp) {
20495                $(SECTION_FIRST_SEL).before(v.wrapAroundElements);
20496            }
20497            else {
20498                $(SECTION_LAST_SEL).after(v.wrapAroundElements);
20499            }
20500
20501            silentScroll($(SECTION_ACTIVE_SEL).position().top);
20502
20503            // Maintain the active slides visible in the viewport
20504            keepSlidesPosition();
20505        }
20506
20507
20508        /**
20509        * Actions to do once the section is loaded.
20510        */
20511        function afterSectionLoads (v){
20512            continuousVerticalFixSectionOrder(v);
20513
20514            //callback (afterLoad) if the site is not just resizing and readjusting the slides
20515            $.isFunction(options.afterLoad) && !v.localIsResizing && options.afterLoad.call(v.element, v.anchorLink, (v.sectionIndex + 1));
20516            options.scrollOverflowHandler.afterLoad();
20517
20518            if(!v.localIsResizing){
20519                playMedia(v.element);
20520            }
20521
20522            v.element.addClass(COMPLETELY).siblings().removeClass(COMPLETELY);
20523
20524            canScroll = true;
20525
20526            $.isFunction(v.callback) && v.callback.call(this);
20527        }
20528
20529        /**
20530        * Sets the value for the given attribute from the `data-` attribute with the same suffix
20531        * ie: data-srcset ==> srcset  |  data-src ==> src
20532        */
20533        function setSrc(element, attribute){
20534            element
20535                .attr(attribute, element.data(attribute))
20536                .removeAttr('data-' + attribute);
20537        }
20538
20539        /**
20540        * Lazy loads image, video and audio elements.
20541        */
20542        function lazyLoad(destiny){
20543            if (!options.lazyLoading){
20544                return;
20545            }
20546
20547            var panel = getSlideOrSection(destiny);
20548            var element;
20549            
20550            panel.find('img[data-src], img[data-srcset], source[data-src], audio[data-src], iframe[data-src]').each(function(){
20551                element = $(this);
20552
20553                $.each(['src', 'srcset'], function(index, type){
20554                    var attribute = element.attr('data-' + type);
20555                    if(typeof attribute !== 'undefined' && attribute){
20556                        setSrc(element, type);
20557                    }
20558                });
20559
20560                if(element.is('source')){
20561                    element.closest('video').get(0).load();
20562                }
20563            });
20564        }
20565
20566        /**
20567        * Plays video and audio elements.
20568        */
20569        function playMedia(destiny){
20570            var panel = getSlideOrSection(destiny);
20571
20572            //playing HTML5 media elements
20573            panel.find('video, audio').each(function(){
20574                var element = $(this).get(0);
20575
20576                if( element.hasAttribute('data-autoplay') && typeof element.play === 'function' ) {
20577                    element.play();
20578                }
20579            });
20580
20581            //youtube videos
20582            panel.find('iframe[src*="youtube.com/embed/"]').each(function(){
20583                var element = $(this).get(0);
20584
20585                if ( element.hasAttribute('data-autoplay') ){
20586                    playYoutube(element);
20587                }
20588
20589                //in case the URL was not loaded yet. On page load we need time for the new URL (with the API string) to load.
20590                element.onload = function() {
20591                    if ( element.hasAttribute('data-autoplay') ){
20592                        playYoutube(element);
20593                    }
20594                };
20595            });
20596        }
20597
20598        /**
20599        * Plays a youtube video
20600        */
20601        function playYoutube(element){
20602            element.contentWindow.postMessage('{"event":"command","func":"playVideo","args":""}', '*');
20603        }
20604
20605        /**
20606        * Stops video and audio elements.
20607        */
20608        function stopMedia(destiny){
20609            var panel = getSlideOrSection(destiny);
20610
20611            //stopping HTML5 media elements
20612            panel.find('video, audio').each(function(){
20613                var element = $(this).get(0);
20614
20615                if( !element.hasAttribute('data-keepplaying') && typeof element.pause === 'function' ) {
20616                    element.pause();
20617                }
20618            });
20619
20620            //youtube videos
20621            panel.find('iframe[src*="youtube.com/embed/"]').each(function(){
20622                var element = $(this).get(0);
20623
20624                if( /youtube\.com\/embed\//.test($(this).attr('src')) && !element.hasAttribute('data-keepplaying')){
20625                    $(this).get(0).contentWindow.postMessage('{"event":"command","func":"pauseVideo","args":""}','*');
20626                }
20627            });
20628        }
20629
20630        /**
20631        * Gets the active slide (or section) for the given section
20632        */
20633        function getSlideOrSection(destiny){
20634            var slide = destiny.find(SLIDE_ACTIVE_SEL);
20635            if( slide.length ) {
20636                destiny = $(slide);
20637            }
20638
20639            return destiny;
20640        }
20641
20642        /**
20643        * Scrolls to the anchor in the URL when loading the site
20644        */
20645        function scrollToAnchor(){
20646            //getting the anchor link in the URL and deleting the `#`
20647            var value =  window.location.hash.replace('#', '').split('/');
20648            var sectionAnchor = decodeURIComponent(value[0]);
20649            var slideAnchor = decodeURIComponent(value[1]);
20650
20651            if(sectionAnchor){  //if theres any #
20652                if(options.animateAnchor){
20653                    scrollPageAndSlide(sectionAnchor, slideAnchor);
20654                }else{
20655                    silentMoveTo(sectionAnchor, slideAnchor);
20656                }
20657            }
20658        }
20659
20660        /**
20661        * Detecting any change on the URL to scroll to the given anchor link
20662        * (a way to detect back history button as we play with the hashes on the URL)
20663        */
20664        function hashChangeHandler(){
20665            if(!isScrolling && !options.lockAnchors){
20666                var value =  window.location.hash.replace('#', '').split('/');
20667                var sectionAnchor = decodeURIComponent(value[0]);
20668                var slideAnchor = decodeURIComponent(value[1]);
20669
20670                    //when moving to a slide in the first section for the first time (first time to add an anchor to the URL)
20671                    var isFirstSlideMove =  (typeof lastScrolledDestiny === 'undefined');
20672                    var isFirstScrollMove = (typeof lastScrolledDestiny === 'undefined' && typeof slideAnchor === 'undefined' && !slideMoving);
20673
20674
20675                if(sectionAnchor.length){
20676                    /*in order to call scrollpage() only once for each destination at a time
20677                    It is called twice for each scroll otherwise, as in case of using anchorlinks `hashChange`
20678                    event is fired on every scroll too.*/
20679                    if ((sectionAnchor && sectionAnchor !== lastScrolledDestiny) && !isFirstSlideMove || isFirstScrollMove || (!slideMoving && lastScrolledSlide != slideAnchor ))  {
20680                        scrollPageAndSlide(sectionAnchor, slideAnchor);
20681                    }
20682                }
20683            }
20684        }
20685
20686        //Sliding with arrow keys, both, vertical and horizontal
20687        function keydownHandler(e) {
20688
20689            clearTimeout(keydownId);
20690
20691            var activeElement = $(':focus');
20692
20693            if(!activeElement.is('textarea') && !activeElement.is('input') && !activeElement.is('select') &&
20694                activeElement.attr('contentEditable') !== "true" && activeElement.attr('contentEditable') !== '' &&
20695                options.keyboardScrolling && options.autoScrolling){
20696                var keyCode = e.which;
20697
20698                //preventing the scroll with arrow keys & spacebar & Page Up & Down keys
20699                var keyControls = [40, 38, 32, 33, 34];
20700                if($.inArray(keyCode, keyControls) > -1){
20701                    e.preventDefault();
20702                }
20703
20704                controlPressed = e.ctrlKey;
20705
20706                keydownId = setTimeout(function(){
20707                    onkeydown(e);
20708                },150);
20709            }
20710        }
20711
20712        function tooltipTextHandler(){
20713            $(this).prev().trigger('click');
20714        }
20715
20716        //to prevent scrolling while zooming
20717        function keyUpHandler(e){
20718            if(isWindowFocused){ //the keyup gets fired on new tab ctrl + t in Firefox
20719                controlPressed = e.ctrlKey;
20720            }
20721        }
20722
20723        //binding the mousemove when the mouse's middle button is released
20724        function mouseDownHandler(e){
20725            //middle button
20726            if (e.which == 2){
20727                oldPageY = e.pageY;
20728                container.on('mousemove', mouseMoveHandler);
20729            }
20730        }
20731
20732        //unbinding the mousemove when the mouse's middle button is released
20733        function mouseUpHandler(e){
20734            //middle button
20735            if (e.which == 2){
20736                container.off('mousemove');
20737            }
20738        }
20739
20740        //Scrolling horizontally when clicking on the slider controls.
20741        function slideArrowHandler(){
20742            var section = $(this).closest(SECTION_SEL);
20743
20744            if ($(this).hasClass(SLIDES_PREV)) {
20745                if(isScrollAllowed.m.left){
20746                    moveSlideLeft(section);
20747                }
20748            } else {
20749                if(isScrollAllowed.m.right){
20750                    moveSlideRight(section);
20751                }
20752            }
20753        }
20754
20755        //when opening a new tab (ctrl + t), `control` won't be pressed when coming back.
20756        function blurHandler(){
20757            isWindowFocused = false;
20758            controlPressed = false;
20759        }
20760
20761        //Scrolls to the section when clicking the navigation bullet
20762        function sectionBulletHandler(e){
20763            e.preventDefault();
20764            var index = $(this).parent().index();
20765            scrollPage($(SECTION_SEL).eq(index));
20766        }
20767
20768        //Scrolls the slider to the given slide destination for the given section
20769        function slideBulletHandler(e){
20770            e.preventDefault();
20771            var slides = $(this).closest(SECTION_SEL).find(SLIDES_WRAPPER_SEL);
20772            var destiny = slides.find(SLIDE_SEL).eq($(this).closest('li').index());
20773
20774            landscapeScroll(slides, destiny);
20775        }
20776
20777        /**
20778        * Keydown event
20779        */
20780        function onkeydown(e){
20781            var shiftPressed = e.shiftKey;
20782
20783            //do nothing if we can not scroll or we are not using horizotnal key arrows.
20784            if(!canScroll && [37,39].indexOf(e.which) < 0){
20785                return;
20786            }
20787
20788            switch (e.which) {
20789                //up
20790                case 38:
20791                case 33:
20792                    if(isScrollAllowed.k.up){
20793                        moveSectionUp();
20794                    }
20795                    break;
20796
20797                //down
20798                case 32: //spacebar
20799                    if(shiftPressed && isScrollAllowed.k.up){
20800                        moveSectionUp();
20801                        break;
20802                    }
20803                /* falls through */
20804                case 40:
20805                case 34:
20806                    if(isScrollAllowed.k.down){
20807                        moveSectionDown();
20808                    }
20809                    break;
20810
20811                //Home
20812                case 36:
20813                    if(isScrollAllowed.k.up){
20814                        moveTo(1);
20815                    }
20816                    break;
20817
20818                //End
20819                case 35:
20820                     if(isScrollAllowed.k.down){
20821                        moveTo( $(SECTION_SEL).length );
20822                    }
20823                    break;
20824
20825                //left
20826                case 37:
20827                    if(isScrollAllowed.k.left){
20828                        moveSlideLeft();
20829                    }
20830                    break;
20831
20832                //right
20833                case 39:
20834                    if(isScrollAllowed.k.right){
20835                        moveSlideRight();
20836                    }
20837                    break;
20838
20839                default:
20840                    return; // exit this handler for other keys
20841            }
20842        }
20843
20844        /**
20845        * Detecting the direction of the mouse movement.
20846        * Used only for the middle button of the mouse.
20847        */
20848        var oldPageY = 0;
20849        function mouseMoveHandler(e){
20850            if(canScroll){
20851                // moving up
20852                if (e.pageY < oldPageY && isScrollAllowed.m.up){
20853                    moveSectionUp();
20854                }
20855
20856                // moving down
20857                else if(e.pageY > oldPageY && isScrollAllowed.m.down){
20858                    moveSectionDown();
20859                }
20860            }
20861            oldPageY = e.pageY;
20862        }
20863
20864        /**
20865        * Scrolls horizontal sliders.
20866        */
20867        function landscapeScroll(slides, destiny, direction){
20868            var section = slides.closest(SECTION_SEL);
20869            var v = {
20870                slides: slides,
20871                destiny: destiny,
20872                direction: direction,
20873                destinyPos: destiny.position(),
20874                slideIndex: destiny.index(),
20875                section: section,
20876                sectionIndex: section.index(SECTION_SEL),
20877                anchorLink: section.data('anchor'),
20878                slidesNav: section.find(SLIDES_NAV_SEL),
20879                slideAnchor:  getAnchor(destiny),
20880                prevSlide: section.find(SLIDE_ACTIVE_SEL),
20881                prevSlideIndex: section.find(SLIDE_ACTIVE_SEL).index(),
20882
20883                //caching the value of isResizing at the momment the function is called
20884                //because it will be checked later inside a setTimeout and the value might change
20885                localIsResizing: isResizing
20886            };
20887            v.xMovement = getXmovement(v.prevSlideIndex, v.slideIndex);
20888
20889            //important!! Only do it when not resizing
20890            if(!v.localIsResizing){
20891                //preventing from scrolling to the next/prev section when using scrollHorizontally
20892                canScroll = false;
20893            }
20894
20895            if(options.onSlideLeave){
20896
20897                //if the site is not just resizing and readjusting the slides
20898                if(!v.localIsResizing && v.xMovement!=='none'){
20899                    if($.isFunction( options.onSlideLeave )){
20900                        if(options.onSlideLeave.call( v.prevSlide, v.anchorLink, (v.sectionIndex + 1), v.prevSlideIndex, v.xMovement, v.slideIndex ) === false){
20901                            slideMoving = false;
20902                            return;
20903                        }
20904                    }
20905                }
20906            }
20907
20908            destiny.addClass(ACTIVE).siblings().removeClass(ACTIVE);
20909
20910            if(!v.localIsResizing){
20911                stopMedia(v.prevSlide);
20912                lazyLoad(destiny);
20913            }
20914
20915            if(!options.loopHorizontal && options.controlArrows){
20916                //hidding it for the fist slide, showing for the rest
20917                section.find(SLIDES_ARROW_PREV_SEL).toggle(v.slideIndex!==0);
20918
20919                //hidding it for the last slide, showing for the rest
20920                section.find(SLIDES_ARROW_NEXT_SEL).toggle(!destiny.is(':last-child'));
20921            }
20922
20923            //only changing the URL if the slides are in the current section (not for resize re-adjusting)
20924            if(section.hasClass(ACTIVE) && !v.localIsResizing){
20925                setState(v.slideIndex, v.slideAnchor, v.anchorLink, v.sectionIndex);
20926            }
20927
20928            performHorizontalMove(slides, v, true);
20929        }
20930
20931
20932        function afterSlideLoads(v){
20933            activeSlidesNavigation(v.slidesNav, v.slideIndex);
20934
20935            //if the site is not just resizing and readjusting the slides
20936            if(!v.localIsResizing){
20937                $.isFunction( options.afterSlideLoad ) && options.afterSlideLoad.call( v.destiny, v.anchorLink, (v.sectionIndex + 1), v.slideAnchor, v.slideIndex);
20938
20939                //needs to be inside the condition to prevent problems with continuousVertical and scrollHorizontally
20940                //and to prevent double scroll right after a windows resize
20941                canScroll = true;
20942
20943                playMedia(v.destiny);
20944            }
20945
20946            //letting them slide again
20947            slideMoving = false;
20948        }
20949
20950        /**
20951        * Performs the horizontal movement. (CSS3 or jQuery)
20952        *
20953        * @param fireCallback {Bool} - determines whether or not to fire the callback
20954        */
20955        function performHorizontalMove(slides, v, fireCallback){
20956            var destinyPos = v.destinyPos;
20957
20958            if(options.css3){
20959                var translate3d = 'translate3d(-' + Math.round(destinyPos.left) + 'px, 0px, 0px)';
20960
20961                addAnimation(slides.find(SLIDES_CONTAINER_SEL)).css(getTransforms(translate3d), v);
20962
20963                afterSlideLoadsId = setTimeout(function(){
20964                    fireCallback && afterSlideLoads(v);
20965                }, options.scrollingSpeed, options.easing);
20966            }else{
20967                slides.animate({
20968                    scrollLeft : Math.round(destinyPos.left)
20969                }, options.scrollingSpeed, options.easing, function() {
20970
20971                    fireCallback && afterSlideLoads(v);
20972                });
20973            }
20974        }
20975
20976        /**
20977        * Sets the state for the horizontal bullet navigations.
20978        */
20979        function activeSlidesNavigation(slidesNav, slideIndex){
20980            slidesNav.find(ACTIVE_SEL).removeClass(ACTIVE);
20981            slidesNav.find('li').eq(slideIndex).find('a').addClass(ACTIVE);
20982        }
20983
20984        var previousHeight = windowsHeight;
20985
20986        //when resizing the site, we adjust the heights of the sections, slimScroll...
20987        function resizeHandler(){
20988            //checking if it needs to get responsive
20989            responsive();
20990
20991            // rebuild immediately on touch devices
20992            if (isTouchDevice) {
20993                var activeElement = $(document.activeElement);
20994
20995                //if the keyboard is NOT visible
20996                if (!activeElement.is('textarea') && !activeElement.is('input') && !activeElement.is('select')) {
20997                    var currentHeight = $window.height();
20998
20999                    //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)
21000                    if( Math.abs(currentHeight - previousHeight) > (20 * Math.max(previousHeight, currentHeight) / 100) ){
21001                        reBuild(true);
21002                        previousHeight = currentHeight;
21003                    }
21004                }
21005            }else{
21006                //in order to call the functions only when the resize is finished
21007                //http://stackoverflow.com/questions/4298612/jquery-how-to-call-resize-event-only-once-its-finished-resizing
21008                clearTimeout(resizeId);
21009
21010                resizeId = setTimeout(function(){
21011                    reBuild(true);
21012                }, 350);
21013            }
21014        }
21015
21016        /**
21017        * Checks if the site needs to get responsive and disables autoScrolling if so.
21018        * A class `fp-responsive` is added to the plugin's container in case the user wants to use it for his own responsive CSS.
21019        */
21020        function responsive(){
21021            var widthLimit = options.responsive || options.responsiveWidth; //backwards compatiblity
21022            var heightLimit = options.responsiveHeight;
21023
21024            //only calculating what we need. Remember its called on the resize event.
21025            var isBreakingPointWidth = widthLimit && $window.outerWidth() < widthLimit;
21026            var isBreakingPointHeight = heightLimit && $window.height() < heightLimit;
21027
21028            if(widthLimit && heightLimit){
21029                setResponsive(isBreakingPointWidth || isBreakingPointHeight);
21030            }
21031            else if(widthLimit){
21032                setResponsive(isBreakingPointWidth);
21033            }
21034            else if(heightLimit){
21035                setResponsive(isBreakingPointHeight);
21036            }
21037        }
21038
21039        /**
21040        * Adds transition animations for the given element
21041        */
21042        function addAnimation(container, element){
21043            var transition = 'all ' + options.scrollingSpeed + 'ms ' + options.easingcss3;
21044
21045            container.removeClass(NO_TRANSITION);
21046            return container.css({
21047                '-webkit-transition': transition,
21048                'transition': transition
21049            });
21050        }
21051
21052        /**
21053        * Remove transition animations for the given element
21054        */
21055        function removeAnimation(element){
21056            return element.addClass(NO_TRANSITION);
21057        }
21058
21059        /**
21060        * Activating the vertical navigation bullets according to the given slide name.
21061        */
21062        function activateNavDots(name, sectionIndex){
21063            if(options.navigation){
21064                $(SECTION_NAV_SEL).find(ACTIVE_SEL).removeClass(ACTIVE);
21065                if(name){
21066                    $(SECTION_NAV_SEL).find('a[href="#' + name + '"]').addClass(ACTIVE);
21067                }else{
21068                    $(SECTION_NAV_SEL).find('li').eq(sectionIndex).find('a').addClass(ACTIVE);
21069                }
21070            }
21071        }
21072
21073        /**
21074        * Activating the website main menu elements according to the given slide name.
21075        */
21076        function activateMenuElement(name){
21077            if(options.menu){
21078                $(options.menu).find(ACTIVE_SEL).removeClass(ACTIVE);
21079                $(options.menu).find('[data-menuanchor="'+name+'"]').addClass(ACTIVE);
21080            }
21081        }
21082
21083        /**
21084        * Sets to active the current menu and vertical nav items.
21085        */
21086        function activateMenuAndNav(anchor, index){
21087            activateMenuElement(anchor);
21088            activateNavDots(anchor, index);
21089        }
21090
21091        /**
21092        * Retuns `up` or `down` depending on the scrolling movement to reach its destination
21093        * from the current section.
21094        */
21095        function getYmovement(destiny){
21096            var fromIndex = $(SECTION_ACTIVE_SEL).index(SECTION_SEL);
21097            var toIndex = destiny.index(SECTION_SEL);
21098            if( fromIndex == toIndex){
21099                return 'none';
21100            }
21101            if(fromIndex > toIndex){
21102                return 'up';
21103            }
21104            return 'down';
21105        }
21106
21107        /**
21108        * Retuns `right` or `left` depending on the scrolling movement to reach its destination
21109        * from the current slide.
21110        */
21111        function getXmovement(fromIndex, toIndex){
21112            if( fromIndex == toIndex){
21113                return 'none';
21114            }
21115            if(fromIndex > toIndex){
21116                return 'left';
21117            }
21118            return 'right';
21119        }
21120
21121        /**
21122        * Checks if the element needs scrollbar and if the user wants to apply it.
21123        * If so it creates it.
21124        *
21125        * @param {Object} element   jQuery object of the section or slide
21126        */
21127        function createScrollBar(element){
21128            //User doesn't want scrollbar here? Sayonara baby!
21129            if(element.hasClass('fp-noscroll')) return;
21130
21131            //needed to make `scrollHeight` work under Opera 12
21132            element.css('overflow', 'hidden');
21133
21134            var scrollOverflowHandler = options.scrollOverflowHandler;
21135            var wrap = scrollOverflowHandler.wrapContent();
21136            //in case element is a slide
21137            var section = element.closest(SECTION_SEL);
21138            var scrollable = scrollOverflowHandler.scrollable(element);
21139            var contentHeight;
21140
21141            //if there was scroll, the contentHeight will be the one in the scrollable section
21142            if(scrollable.length){
21143                contentHeight = scrollOverflowHandler.scrollHeight(element);
21144            }else{
21145                contentHeight = element.get(0).scrollHeight;
21146                if(options.verticalCentered){
21147                    contentHeight = element.find(TABLE_CELL_SEL).get(0).scrollHeight;
21148                }
21149            }
21150
21151            var scrollHeight = windowsHeight - parseInt(section.css('padding-bottom')) - parseInt(section.css('padding-top'));
21152            //Uncode addition
21153            scrollHeight -= UNCODE.bodyBorder*2 + UNCODE.adminBarHeight;
21154
21155            //needs scroll?
21156            if ( contentHeight > scrollHeight) {
21157                //did we already have an scrollbar ? Updating it
21158                if(scrollable.length){
21159                    scrollOverflowHandler.update(element, scrollHeight);
21160                }
21161                //creating the scrolling
21162                else{
21163                    if(options.verticalCentered){
21164                        element.find(TABLE_CELL_SEL).wrapInner(wrap);
21165                    }else{
21166                        element.wrapInner(wrap);
21167                    }
21168                    scrollOverflowHandler.create(element, scrollHeight);
21169                }
21170            }
21171            //removing the scrolling when it is not necessary anymore
21172            else{
21173                scrollOverflowHandler.remove(element);
21174            }
21175
21176            //undo
21177            element.css('overflow', '');
21178        }
21179
21180        function addTableClass(element){
21181            //In case we are styling for the 2nd time as in with reponsiveSlides
21182            if(!element.hasClass(TABLE)){
21183                element.addClass(TABLE).wrapInner('<div class="' + TABLE_CELL + '" style="height:' + getTableHeight(element) + 'px;" />');
21184            }
21185        }
21186
21187        function getTableHeight(element){
21188            var sectionHeight = windowsHeight;
21189
21190            if(options.paddingTop || options.paddingBottom){
21191                var section = element;
21192                if(!section.hasClass(SECTION)){
21193                    section = element.closest(SECTION_SEL);
21194                }
21195
21196                var paddings = parseInt(section.css('padding-top')) + parseInt(section.css('padding-bottom'));
21197                sectionHeight = (windowsHeight - paddings);
21198            }
21199
21200            return sectionHeight;
21201        }
21202
21203        /**
21204        * Adds a css3 transform property to the container class with or without animation depending on the animated param.
21205        */
21206        function transformContainer(translate3d, animated){
21207            if(animated){
21208                addAnimation(container);
21209            }else{
21210                removeAnimation(container);
21211            }
21212
21213            container.css(getTransforms(translate3d));
21214
21215            //syncronously removing the class after the animation has been applied.
21216            setTimeout(function(){
21217                container.removeClass(NO_TRANSITION);
21218            },10);
21219        }
21220
21221        /**
21222        * Gets a section by its anchor / index
21223        */
21224        function getSectionByAnchor(sectionAnchor){
21225            if(!sectionAnchor) return [];
21226
21227            var section = container.find(SECTION_SEL + '[data-anchor="'+sectionAnchor+'"]');
21228            if(!section.length){
21229                section = $(SECTION_SEL).eq( sectionAnchor -1);
21230            }
21231
21232            return section;
21233        }
21234
21235        /**
21236        * Gets a slide inside a given section by its anchor / index
21237        */
21238        function getSlideByAnchor(slideAnchor, section){
21239            var slides = section.find(SLIDES_WRAPPER_SEL);
21240            var slide =  slides.find(SLIDE_SEL + '[data-anchor="'+slideAnchor+'"]');
21241
21242            if(!slide.length){
21243                slide = slides.find(SLIDE_SEL).eq(slideAnchor);
21244            }
21245
21246            return slide;
21247        }
21248
21249        /**
21250        * Scrolls to the given section and slide anchors
21251        */
21252        function scrollPageAndSlide(destiny, slide){
21253            var section = getSectionByAnchor(destiny);
21254
21255            //do nothing if there's no section with the given anchor name
21256            if(!section.length) return;
21257
21258            //default slide
21259            if (typeof slide === 'undefined') {
21260                slide = 0;
21261            }
21262
21263            //we need to scroll to the section and then to the slide
21264            if (destiny !== lastScrolledDestiny && !section.hasClass(ACTIVE)){
21265                scrollPage(section, function(){
21266                    scrollSlider(section, slide);
21267                });
21268            }
21269            //if we were already in the section
21270            else{
21271                scrollSlider(section, slide);
21272            }
21273        }
21274
21275        /**
21276        * Scrolls the slider to the given slide destination for the given section
21277        */
21278        function scrollSlider(section, slideAnchor){
21279            if(typeof slideAnchor !== 'undefined'){
21280                var slides = section.find(SLIDES_WRAPPER_SEL);
21281                var destiny =  getSlideByAnchor(slideAnchor, section);
21282
21283                if(destiny.length){
21284                    landscapeScroll(slides, destiny);
21285                }
21286            }
21287        }
21288
21289        /**
21290        * Creates a landscape navigation bar with dots for horizontal sliders.
21291        */
21292        function addSlidesNavigation(section, numSlides){
21293            section.append('<div class="' + SLIDES_NAV + '"><ul></ul></div>');
21294            var nav = section.find(SLIDES_NAV_SEL);
21295
21296            //top or bottom
21297            nav.addClass(options.slidesNavPosition);
21298
21299            for(var i=0; i< numSlides; i++){
21300                nav.find('ul').append('<li><a href="#"><span></span></a></li>');
21301            }
21302
21303            //centering it
21304            nav.css('margin-left', '-' + (nav.width()/2) + 'px');
21305
21306            nav.find('li').first().find('a').addClass(ACTIVE);
21307        }
21308
21309
21310        /**
21311        * Sets the state of the website depending on the active section/slide.
21312        * It changes the URL hash when needed and updates the body class.
21313        */
21314        function setState(slideIndex, slideAnchor, anchorLink, sectionIndex){
21315            var sectionHash = '';
21316
21317            if(options.anchors.length && !options.lockAnchors){
21318
21319                //isn't it the first slide?
21320                if(slideIndex){
21321                    if(typeof anchorLink !== 'undefined'){
21322                        sectionHash = anchorLink;
21323                    }
21324
21325                    //slide without anchor link? We take the index instead.
21326                    if(typeof slideAnchor === 'undefined'){
21327                        slideAnchor = slideIndex;
21328                    }
21329
21330                    lastScrolledSlide = slideAnchor;
21331                    setUrlHash(sectionHash + '/' + slideAnchor);
21332
21333                //first slide won't have slide anchor, just the section one
21334                }else if(typeof slideIndex !== 'undefined'){
21335                    lastScrolledSlide = slideAnchor;
21336                    setUrlHash(anchorLink);
21337                }
21338
21339                //section without slides
21340                else{
21341                    setUrlHash(anchorLink);
21342                }
21343            }
21344
21345            setBodyClass();
21346        }
21347
21348        /**
21349        * Sets the URL hash.
21350        */
21351        function setUrlHash(url){
21352            //Uncode addition
21353            if ( typeof SiteParameters.slide_footer != 'undefined' && url == SiteParameters.slide_footer )
21354                return false;
21355
21356            if(options.recordHistory){
21357                location.hash = url;
21358            }else{
21359                //Uncode addition
21360                //Mobile Chrome doesn't work the normal way, so... lets use HTML5 for phones :)
21361                // if(isTouchDevice || isTouch){
21362                //     window.history.replaceState(undefined, undefined, '#' + url);
21363                // }else{
21364                //     var baseUrl = window.location.href.split('#')[0];
21365                //     window.location.replace( baseUrl + '#' + url );
21366                // }
21367                return false;
21368            }
21369        }
21370
21371        /**
21372        * Gets the anchor for the given slide / section. Its index will be used if there's none.
21373        */
21374        function getAnchor(element){
21375            var anchor = element.data('anchor');
21376            var index = element.index();
21377
21378            //Slide without anchor link? We take the index instead.
21379            if(typeof anchor === 'undefined'){
21380                anchor = index;
21381            }
21382
21383            return anchor;
21384        }
21385
21386        /**
21387        * Sets a class for the body of the page depending on the active section / slide
21388        */
21389        function setBodyClass(){
21390            var section = $(SECTION_ACTIVE_SEL);
21391            var slide = section.find(SLIDE_ACTIVE_SEL);
21392
21393            var sectionAnchor = getAnchor(section);
21394            var slideAnchor = getAnchor(slide);
21395
21396            var text = String(sectionAnchor);
21397
21398            if(slide.length){
21399                text = text + '-' + slideAnchor;
21400            }
21401
21402            //changing slash for dash to make it a valid CSS style
21403            text = text.replace('/', '-').replace('#','');
21404
21405            //removing previous anchor classes
21406            var classRe = new RegExp('\\b\\s?' + VIEWING_PREFIX + '-[^\\s]+\\b', "g");
21407            $body[0].className = $body[0].className.replace(classRe, '');
21408
21409            //adding the current anchor
21410            $body.addClass(VIEWING_PREFIX + '-' + text);
21411        }
21412
21413        /**
21414        * Checks for translate3d support
21415        * @return boolean
21416        * http://stackoverflow.com/questions/5661671/detecting-transform-translate3d-support
21417        */
21418        function support3d() {
21419            var el = document.createElement('p'),
21420                has3d,
21421                transforms = {
21422                    'webkitTransform':'-webkit-transform',
21423                    'OTransform':'-o-transform',
21424                    'msTransform':'-ms-transform',
21425                    'MozTransform':'-moz-transform',
21426                    'transform':'transform'
21427                };
21428
21429            // Add it to the body to get the computed style.
21430            document.body.insertBefore(el, null);
21431
21432            for (var t in transforms) {
21433                if (el.style[t] !== undefined) {
21434                    el.style[t] = 'translate3d(1px,1px,1px)';
21435                    has3d = window.getComputedStyle(el).getPropertyValue(transforms[t]);
21436                }
21437            }
21438
21439            document.body.removeChild(el);
21440
21441            return (has3d !== undefined && has3d.length > 0 && has3d !== 'none');
21442        }
21443
21444        /**
21445        * Removes the auto scrolling action fired by the mouse wheel and trackpad.
21446        * After this function is called, the mousewheel and trackpad movements won't scroll through sections.
21447        */
21448        function removeMouseWheelHandler(){
21449            if (document.addEventListener) {
21450                document.removeEventListener('mousewheel', MouseWheelHandler, false); //IE9, Chrome, Safari, Oper
21451                document.removeEventListener('wheel', MouseWheelHandler, false); //Firefox
21452                document.removeEventListener('MozMousePixelScroll', MouseWheelHandler, false); //old Firefox
21453            } else {
21454                document.detachEvent('onmousewheel', MouseWheelHandler); //IE 6/7/8
21455            }
21456        }
21457
21458        /**
21459        * Adds the auto scrolling action for the mouse wheel and trackpad.
21460        * After this function is called, the mousewheel and trackpad movements will scroll through sections
21461        * https://developer.mozilla.org/en-US/docs/Web/Events/wheel
21462        */
21463        function addMouseWheelHandler(){
21464            var prefix = '';
21465            var _addEventListener;
21466
21467            if (window.addEventListener){
21468                _addEventListener = "addEventListener";
21469            }else{
21470                _addEventListener = "attachEvent";
21471                prefix = 'on';
21472            }
21473
21474             // detect available wheel event
21475            var support = 'onwheel' in document.createElement('div') ? 'wheel' : // Modern browsers support "wheel"
21476                      document.onmousewheel !== undefined ? 'mousewheel' : // Webkit and IE support at least "mousewheel"
21477                      'DOMMouseScroll'; // let's assume that remaining browsers are older Firefox
21478
21479
21480            if(support == 'DOMMouseScroll'){
21481                document[ _addEventListener ](prefix + 'MozMousePixelScroll', MouseWheelHandler, false);
21482            }
21483
21484            //handle MozMousePixelScroll in older Firefox
21485            else{
21486                document[ _addEventListener ](prefix + support, MouseWheelHandler, false);
21487            }
21488        }
21489
21490        /**
21491        * Binding the mousemove when the mouse's middle button is pressed
21492        */
21493        function addMiddleWheelHandler(){
21494            container
21495                .on('mousedown', mouseDownHandler)
21496                .on('mouseup', mouseUpHandler);
21497        }
21498
21499        /**
21500        * Unbinding the mousemove when the mouse's middle button is released
21501        */
21502        function removeMiddleWheelHandler(){
21503            container
21504                .off('mousedown', mouseDownHandler)
21505                .off('mouseup', mouseUpHandler);
21506        }
21507
21508        /**
21509        * Adds the possibility to auto scroll through sections on touch devices.
21510        */
21511        function addTouchHandler(){
21512            if(isTouchDevice || isTouch){
21513                if(options.autoScrolling){
21514                    $body.off(events.touchmove).on(events.touchmove, preventBouncing);
21515                }
21516
21517                $(WRAPPER_SEL)
21518                    .off(events.touchstart).on(events.touchstart, touchStartHandler)
21519                    .off(events.touchmove).on(events.touchmove, touchMoveHandler);
21520            }
21521        }
21522
21523        /**
21524        * Removes the auto scrolling for touch devices.
21525        */
21526        function removeTouchHandler(){
21527            if(isTouchDevice || isTouch){
21528                $(WRAPPER_SEL)
21529                    .off(events.touchstart)
21530                    .off(events.touchmove);
21531            }
21532        }
21533
21534        /*
21535        * Returns and object with Microsoft pointers (for IE<11 and for IE >= 11)
21536        * http://msdn.microsoft.com/en-us/library/ie/dn304886(v=vs.85).aspx
21537        */
21538        function getMSPointer(){
21539            var pointer;
21540
21541            //IE >= 11 & rest of browsers
21542            if(window.PointerEvent){
21543                pointer = { down: 'pointerdown', move: 'pointermove'};
21544            }
21545
21546            //IE < 11
21547            else{
21548                pointer = { down: 'MSPointerDown', move: 'MSPointerMove'};
21549            }
21550
21551            return pointer;
21552        }
21553
21554        /**
21555        * Gets the pageX and pageY properties depending on the browser.
21556        * https://github.com/alvarotrigo/fullPage.js/issues/194#issuecomment-34069854
21557        */
21558        function getEventsPage(e){
21559            var events = [];
21560
21561            events.y = (typeof e.pageY !== 'undefined' && (e.pageY || e.pageX) ? e.pageY : e.touches[0].pageY);
21562            events.x = (typeof e.pageX !== 'undefined' && (e.pageY || e.pageX) ? e.pageX : e.touches[0].pageX);
21563
21564            //in touch devices with scrollBar:true, e.pageY is detected, but we have to deal with touch events. #1008
21565            if(isTouch && isReallyTouch(e) && options.scrollBar){
21566                events.y = e.touches[0].pageY;
21567                events.x = e.touches[0].pageX;
21568            }
21569
21570            return events;
21571        }
21572
21573        /**
21574        * Slides silently (with no animation) the active slider to the given slide.
21575        * @param noCallback {bool} true or defined -> no callbacks
21576        */
21577        function silentLandscapeScroll(activeSlide, noCallbacks){
21578            setScrollingSpeed (0, 'internal');
21579
21580            if(typeof noCallbacks !== 'undefined'){
21581                //preventing firing callbacks afterSlideLoad etc.
21582                isResizing = true;
21583            }
21584
21585            landscapeScroll(activeSlide.closest(SLIDES_WRAPPER_SEL), activeSlide);
21586
21587            if(typeof noCallbacks !== 'undefined'){
21588                isResizing = false;
21589            }
21590
21591            setScrollingSpeed(originals.scrollingSpeed, 'internal');
21592        }
21593
21594        /**
21595        * Scrolls silently (with no animation) the page to the given Y position.
21596        */
21597        function silentScroll(top){
21598            // The first section can have a negative value in iOS 10. Not quite sure why: -0.0142822265625
21599            // that's why we round it to 0.
21600            var roundedTop = Math.round(top);
21601
21602            if (options.css3 && options.autoScrolling && !options.scrollBar){
21603                var translate3d = 'translate3d(0px, -' + roundedTop + 'px, 0px)';
21604                transformContainer(translate3d, false);
21605            }
21606            else if(options.autoScrolling && !options.scrollBar){
21607                container.css('top', -roundedTop);
21608            }
21609            else{
21610                $htmlBody.scrollTop(roundedTop);
21611            }
21612        }
21613
21614        /**
21615        * Returns the cross-browser transform string.
21616        */
21617        function getTransforms(translate3d){
21618            return {
21619                '-webkit-transform': translate3d,
21620                '-moz-transform': translate3d,
21621                '-ms-transform':translate3d,
21622                'transform': translate3d
21623            };
21624        }
21625
21626        /**
21627        * Allowing or disallowing the mouse/swipe scroll in a given direction. (not for keyboard)
21628        * @type  m (mouse) or k (keyboard)
21629        */
21630        function setIsScrollAllowed(value, direction, type){
21631            switch (direction){
21632                case 'up': isScrollAllowed[type].up = value; break;
21633                case 'down': isScrollAllowed[type].down = value; break;
21634                case 'left': isScrollAllowed[type].left = value; break;
21635                case 'right': isScrollAllowed[type].right = value; break;
21636                case 'all':
21637                    if(type == 'm'){
21638                        setAllowScrolling(value);
21639                    }else{
21640                        setKeyboardScrolling(value);
21641                    }
21642            }
21643        }
21644
21645        /*
21646        * Destroys fullpage.js plugin events and optinally its html markup and styles
21647        */
21648        function destroy(all){
21649            setAutoScrolling(false, 'internal');
21650            setAllowScrolling(false);
21651            setKeyboardScrolling(false);
21652            container.addClass(DESTROYED);
21653
21654            clearTimeout(afterSlideLoadsId);
21655            clearTimeout(afterSectionLoadsId);
21656            clearTimeout(resizeId);
21657            clearTimeout(scrollId);
21658            clearTimeout(scrollId2);
21659
21660            $window
21661                .off('scroll', scrollHandler)
21662                .off('hashchange', hashChangeHandler)
21663                .off('resize', resizeHandler);
21664
21665            $document
21666                .off('click touchstart', SECTION_NAV_SEL + ' a')
21667                .off('mouseenter', SECTION_NAV_SEL + ' li')
21668                .off('mouseleave', SECTION_NAV_SEL + ' li')
21669                .off('click touchstart', SLIDES_NAV_LINK_SEL)
21670                .off('mouseover', options.normalScrollElements)
21671                .off('mouseout', options.normalScrollElements);
21672
21673            $(SECTION_SEL)
21674                .off('click touchstart', SLIDES_ARROW_SEL);
21675
21676            clearTimeout(afterSlideLoadsId);
21677            clearTimeout(afterSectionLoadsId);
21678
21679            //lets make a mess!
21680            if(all){
21681                destroyStructure();
21682            }
21683        }
21684
21685        /*
21686        * Removes inline styles added by fullpage.js
21687        */
21688        function destroyStructure(){
21689            //reseting the `top` or `translate` properties to 0
21690            silentScroll(0);
21691
21692            //loading all the lazy load content
21693            container.find('img[data-src], source[data-src], audio[data-src], iframe[data-src]').each(function(){
21694                setSrc($(this), 'src');
21695            });
21696
21697            container.find('img[data-srcset]').each(function(){
21698                setSrc($(this), 'srcset');
21699            });
21700
21701            $(SECTION_NAV_SEL + ', ' + SLIDES_NAV_SEL +  ', ' + SLIDES_ARROW_SEL).remove();
21702
21703            //removing inline styles
21704            $(SECTION_SEL).css( {
21705                'height': '',
21706                'background-color' : '',
21707                'padding': ''
21708            });
21709
21710            $(SLIDE_SEL).css( {
21711                'width': ''
21712            });
21713
21714            container.css({
21715                'height': '',
21716                'position': '',
21717                '-ms-touch-action': '',
21718                'touch-action': ''
21719            });
21720
21721            $htmlBody.css({
21722                'overflow': '',
21723                'height': ''
21724            });
21725
21726            // remove .fp-enabled class
21727            $('html').removeClass(ENABLED);
21728
21729            // remove .fp-responsive class
21730            $body.removeClass(RESPONSIVE);
21731
21732            // remove all of the .fp-viewing- classes
21733            $.each($body.get(0).className.split(/\s+/), function (index, className) {
21734                if (className.indexOf(VIEWING_PREFIX) === 0) {
21735                    $body.removeClass(className);
21736                }
21737            });
21738
21739            //removing added classes
21740            $(SECTION_SEL + ', ' + SLIDE_SEL).each(function(){
21741                options.scrollOverflowHandler.remove($(this));
21742                $(this).removeClass(TABLE + ' ' + ACTIVE);
21743            });
21744
21745            removeAnimation(container);
21746
21747            //Unwrapping content
21748            container.find(TABLE_CELL_SEL + ', ' + SLIDES_CONTAINER_SEL + ', ' + SLIDES_WRAPPER_SEL).each(function(){
21749                //unwrap not being use in case there's no child element inside and its just text
21750                $(this).replaceWith(this.childNodes);
21751            });
21752
21753            //removing the applied transition from the fullpage wrapper
21754            container.css({
21755                '-webkit-transition': 'none',
21756                'transition': 'none'
21757            });
21758
21759            //scrolling the page to the top with no animation
21760            $htmlBody.scrollTop(0);
21761
21762            //removing selectors
21763            var usedSelectors = [SECTION, SLIDE, SLIDES_CONTAINER];
21764            $.each(usedSelectors, function(index, value){
21765                $('.' + value).removeClass(value);
21766            });
21767        }
21768
21769        /*
21770        * Sets the state for a variable with multiple states (original, and temporal)
21771        * Some variables such as `autoScrolling` or `recordHistory` might change automatically its state when using `responsive` or `autoScrolling:false`.
21772        * This function is used to keep track of both states, the original and the temporal one.
21773        * If type is not 'internal', then we assume the user is globally changing the variable.
21774        */
21775        function setVariableState(variable, value, type){
21776            options[variable] = value;
21777            if(type !== 'internal'){
21778                originals[variable] = value;
21779            }
21780        }
21781
21782        /**
21783        * Displays warnings
21784        */
21785        function displayWarnings(){
21786            var extensions = ['fadingEffect', 'continuousHorizontal', 'scrollHorizontally', 'interlockedSlides', 'resetSliders', 'responsiveSlides', 'offsetSections', 'dragAndMove', 'scrollOverflowReset', 'parallax'];
21787            //UNCODE.addition
21788            // if($('html').hasClass(ENABLED)){
21789            //     showError('error', 'Fullpage.js can only be initialized once and you are doing it multiple times!');
21790            //     return;
21791            // }
21792
21793            // Disable mutually exclusive settings
21794            //UNCODE.addition
21795            // if (options.continuousVertical &&
21796            //     (options.loopTop || options.loopBottom)) {
21797            //     options.continuousVertical = false;
21798            //     showError('warn', 'Option `loopTop/loopBottom` is mutually exclusive with `continuousVertical`; `continuousVertical` disabled');
21799            // }
21800
21801            // if(options.scrollBar && options.scrollOverflow){
21802            //     showError('warn', 'Option `scrollBar` is mutually exclusive with `scrollOverflow`. Sections with scrollOverflow might not work well in Firefox');
21803            // }
21804
21805            // if(options.continuousVertical && (options.scrollBar || !options.autoScrolling)){
21806            //     options.continuousVertical = false;
21807            //     showError('warn', 'Scroll bars (`scrollBar:true` or `autoScrolling:false`) are mutually exclusive with `continuousVertical`; `continuousVertical` disabled');
21808            // }
21809
21810            //using extensions? Wrong file!
21811            $.each(extensions, function(index, extension){
21812                //is the option set to true?
21813                if(options[extension]){
21814                    showError('warn', 'fullpage.js extensions require jquery.fullpage.extensions.min.js file instead of the usual jquery.fullpage.js. Requested: '+ extension);
21815                }
21816            });
21817
21818            //anchors can not have the same value as any element ID or NAME
21819            $.each(options.anchors, function(index, name){
21820
21821                //case insensitive selectors (http://stackoverflow.com/a/19465187/1081396)
21822                var nameAttr = $document.find('[name]').filter(function() {
21823                    return $(this).attr('name') && $(this).attr('name').toLowerCase() == name.toLowerCase();
21824                });
21825
21826                var idAttr = $document.find('[id]').filter(function() {
21827                    return $(this).attr('id') && $(this).attr('id').toLowerCase() == name.toLowerCase();
21828                });
21829
21830                if(idAttr.length || nameAttr.length ){
21831                    showError('error', 'data-anchor tags can not have the same value as any `id` element on the site (or `name` element for IE).');
21832                    idAttr.length && showError('error', '"' + name + '" is is being used by another element `id` property');
21833                    nameAttr.length && showError('error', '"' + name + '" is is being used by another element `name` property');
21834                }
21835            });
21836        }
21837
21838        /**
21839        * Shows a message in the console of the given type.
21840        */
21841        function showError(type, text){
21842            console && console[type] && console[type]('fullPage: ' + text);
21843        }
21844
21845    }; //end of $.fn.fullpage
21846
21847    if(typeof IScroll !== 'undefined'){
21848        /*
21849        * Turns iScroll `mousewheel` option off dynamically
21850        * https://github.com/cubiq/iscroll/issues/1036
21851        */
21852        IScroll.prototype.wheelOn = function () {
21853            this.wrapper.addEventListener('wheel', this);
21854            this.wrapper.addEventListener('mousewheel', this);
21855            this.wrapper.addEventListener('DOMMouseScroll', this);
21856        };
21857
21858        /*
21859        * Turns iScroll `mousewheel` option on dynamically
21860        * https://github.com/cubiq/iscroll/issues/1036
21861        */
21862        IScroll.prototype.wheelOff = function () {
21863            this.wrapper.removeEventListener('wheel', this);
21864            this.wrapper.removeEventListener('mousewheel', this);
21865            this.wrapper.removeEventListener('DOMMouseScroll', this);
21866        };
21867    }
21868
21869    /**
21870     * An object to handle overflow scrolling.
21871     * This uses jquery.slimScroll to accomplish overflow scrolling.
21872     * It is possible to pass in an alternate scrollOverflowHandler
21873     * to the fullpage.js option that implements the same functions
21874     * as this handler.
21875     *
21876     * @type {Object}
21877     */
21878    var iscrollHandler = {
21879        refreshId: null,
21880        iScrollInstances: [],
21881
21882        // Enables or disables the mouse wheel for the active section or all slides in it
21883        toggleWheel: function(value){
21884            var scrollable = $(SECTION_ACTIVE_SEL).find(SCROLLABLE_SEL);
21885            scrollable.each(function(){
21886                var iScrollInstance = $(this).data('iscrollInstance');
21887                if(typeof iScrollInstance !== 'undefined' && iScrollInstance){
21888                    if(value){
21889                        iScrollInstance.wheelOn();
21890                    }
21891                    else{
21892                        iScrollInstance.wheelOff();
21893                    }
21894                }
21895            });
21896        },
21897
21898        /**
21899        * Turns off iScroll for the destination section.
21900        * When scrolling very fast on some trackpads (and Apple laptops) the inertial scrolling would
21901        * scroll the destination section/slide before the sections animations ends.
21902        */
21903        onLeave: function(){
21904            iscrollHandler.toggleWheel(false);
21905        },
21906
21907        // Turns off iScroll for the leaving section
21908        beforeLeave: function(){
21909            iscrollHandler.onLeave()
21910        },
21911
21912        // Turns on iScroll on section load
21913        afterLoad: function(){
21914            iscrollHandler.toggleWheel(true);
21915        },
21916
21917        /**
21918         * Called when overflow scrolling is needed for a section.
21919         *
21920         * @param  {Object} element      jQuery object containing current section
21921         * @param  {Number} scrollHeight Current window height in pixels
21922         */
21923        create: function(element, scrollHeight) {
21924            var scrollable = element.find(SCROLLABLE_SEL);
21925
21926            scrollable.height(scrollHeight);
21927            scrollable.each(function() {
21928                var $this = $(this);
21929                var iScrollInstance = $this.data('iscrollInstance');
21930                if (iScrollInstance) {
21931                    $.each(iscrollHandler.iScrollInstances, function(){
21932                        $(this).destroy();
21933                    });
21934                }
21935
21936                iScrollInstance = new IScroll($this.get(0), iscrollOptions);
21937                iscrollHandler.iScrollInstances.push(iScrollInstance);
21938
21939                //off by default until the section gets active
21940                iScrollInstance.wheelOff();
21941
21942                $this.data('iscrollInstance', iScrollInstance);
21943            });
21944        },
21945
21946        /**
21947         * Return a boolean depending on whether the scrollable element is a
21948         * the end or at the start of the scrolling depending on the given type.
21949         *
21950         * @param  {String}  type       Either 'top' or 'bottom'
21951         * @param  {Object}  scrollable jQuery object for the scrollable element
21952         * @return {Boolean}
21953         */
21954        isScrolled: function(type, scrollable) {
21955            var scroller = scrollable.data('iscrollInstance');
21956
21957            //no scroller?
21958            if (!scroller) {
21959                return true;
21960            }
21961
21962            if (type === 'top') {
21963                return scroller.y >= 0 && !scrollable.scrollTop();
21964            } else if (type === 'bottom') {
21965                return (0 - scroller.y) + scrollable.scrollTop() + 1 + scrollable.innerHe
vendor: 16,384 bytes, lines 21965-22509
21965ight() >= scrollable[0].scrollHeight;
21966            }
21967        },
21968
21969        /**
21970         * Returns the scrollable element for the given section.
21971         * If there are landscape slides, will only return a scrollable element
21972         * if it is in the active slide.
21973         *
21974         * @param  {Object}  activeSection jQuery object containing current section
21975         * @return {Boolean}
21976         */
21977        scrollable: function(activeSection){
21978            // if there are landscape slides, we check if the scrolling bar is in the current one or not
21979            if (activeSection.find(SLIDES_WRAPPER_SEL).length) {
21980                return activeSection.find(SLIDE_ACTIVE_SEL).find(SCROLLABLE_SEL);
21981            }
21982            return activeSection.find(SCROLLABLE_SEL);
21983        },
21984
21985        /**
21986         * Returns the scroll height of the wrapped content.
21987         * If this is larger than the window height minus section padding,
21988         * overflow scrolling is needed.
21989         *
21990         * @param  {Object} element jQuery object containing current section
21991         * @return {Number}
21992         */
21993        scrollHeight: function(element) {
21994            return element.find(SCROLLABLE_SEL).children().first().get(0).scrollHeight;
21995        },
21996
21997        /**
21998         * Called when overflow scrolling is no longer needed for a section.
21999         *
22000         * @param  {Object} element      jQuery object containing current section
22001         */
22002        remove: function(element) {
22003            var scrollable = element.find(SCROLLABLE_SEL);
22004            if (scrollable.length) {
22005                var iScrollInstance = scrollable.data('iscrollInstance');
22006                iScrollInstance.destroy();
22007
22008                scrollable.data('iscrollInstance', null);
22009            }
22010            element.find(SCROLLABLE_SEL).children().first().children().first().unwrap().unwrap();
22011        },
22012
22013        /**
22014         * Called when overflow scrolling has already been setup but the
22015         * window height has potentially changed.
22016         *
22017         * @param  {Object} element      jQuery object containing current section
22018         * @param  {Number} scrollHeight Current window height in pixels
22019         */
22020        update: function(element, scrollHeight) {
22021            //using a timeout in order to execute the refresh function only once when `update` is called multiple times in a
22022            //short period of time.
22023            //it also comes on handy because iScroll requires the use of timeout when using `refresh`.
22024            clearTimeout(iscrollHandler.refreshId);
22025            iscrollHandler.refreshId = setTimeout(function(){
22026                $.each(iscrollHandler.iScrollInstances, function(){
22027                    $(this).get(0).refresh();
22028                });
22029            }, 150);
22030
22031            //updating the wrappers height
22032            element.find(SCROLLABLE_SEL).css('height', scrollHeight + 'px').parent().css('height', scrollHeight + 'px');
22033        },
22034
22035        /**
22036         * Called to get any additional elements needed to wrap the section
22037         * content in order to facilitate overflow scrolling.
22038         *
22039         * @return {String|Object} Can be a string containing HTML,
22040         *                         a DOM element, or jQuery object.
22041         */
22042        wrapContent: function() {
22043            return '<div class="' + SCROLLABLE + '"><div class="fp-scroller"></div></div>';
22044        }
22045    };
22046});
22047
22048
22049
22050/* ========================================================================
22051 * Bootstrap: collapse.js v3.1.1
22052 * http://getbootstrap.com/javascript/#collapse
22053 * ========================================================================
22054 * Copyright 2011-2014 Twitter, Inc.
22055 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
22056 * ======================================================================== */
22057
22058
22059+function ($) {
22060  'use strict';
22061
22062  // COLLAPSE PUBLIC CLASS DEFINITION
22063  // ================================
22064
22065  var Collapse = function (element, options) {
22066    this.$element      = $(element)
22067    this.options       = $.extend({}, Collapse.DEFAULTS, options)
22068    this.transitioning = null
22069
22070    if (this.options.parent) this.$parent = $(this.options.parent)
22071    if (this.options.toggle) this.toggle()
22072  }
22073
22074  Collapse.DEFAULTS = {
22075    toggle: true
22076  }
22077
22078  Collapse.prototype.dimension = function () {
22079    var hasWidth = this.$element.hasClass('width')
22080    return hasWidth ? 'width' : 'height'
22081  }
22082
22083  Collapse.prototype.show = function () {
22084    if (this.transitioning || this.$element.hasClass('in')) return
22085
22086    var startEvent = $.Event('show.bs.collapse')
22087    this.$element.trigger(startEvent)
22088    if (startEvent.isDefaultPrevented()) return
22089
22090    var actives = this.$parent && this.$parent.find('> .panel > .in')
22091
22092    if (actives && actives.length) {
22093      var hasData = actives.data('bs.collapse')
22094      if (hasData && hasData.transitioning) return
22095      actives.collapse('hide')
22096      hasData || actives.data('bs.collapse', null)
22097    }
22098
22099    var dimension = this.dimension()
22100
22101    this.$element
22102      .removeClass('collapse')
22103      .addClass('collapsing')[dimension](0)
22104
22105    this.transitioning = 1
22106
22107    var complete = function (e) {
22108      if (e && e.target != this.$element[0]) return
22109      this.$element
22110        .removeClass('collapsing')
22111        .addClass('collapse in')[dimension]('auto')
22112      this.transitioning = 0
22113      this.$element.trigger('shown.bs.collapse')
22114    }
22115
22116    if (!$.support.transition) return complete.call(this)
22117
22118    var scrollSize = $.camelCase(['scroll', dimension].join('-'))
22119
22120    this.$element
22121      .one($.support.transition.end, $.proxy(complete, this))
22122      .emulateTransitionEnd(350)[dimension](this.$element[0][scrollSize])
22123  }
22124
22125  Collapse.prototype.hide = function () {
22126    if (this.transitioning || !this.$element.hasClass('in')) return
22127
22128    var startEvent = $.Event('hide.bs.collapse')
22129    this.$element.trigger(startEvent)
22130    if (startEvent.isDefaultPrevented()) return
22131
22132    var dimension = this.dimension()
22133
22134    this.$element[dimension](this.$element[dimension]())[0].offsetHeight
22135
22136    this.$element
22137      .addClass('collapsing')
22138      .removeClass('collapse')
22139      .removeClass('in')
22140
22141    this.transitioning = 1
22142
22143    var complete = function (e) {
22144      if (e && e.target != this.$element[0]) return
22145      this.transitioning = 0
22146      this.$element
22147        .trigger('hidden.bs.collapse')
22148        .removeClass('collapsing')
22149        .addClass('collapse')
22150    }
22151
22152    if (!$.support.transition) return complete.call(this)
22153
22154    this.$element
22155      [dimension](0)
22156      .one($.support.transition.end, $.proxy(complete, this))
22157      .emulateTransitionEnd(350)
22158  }
22159
22160  Collapse.prototype.toggle = function () {
22161    this[this.$element.hasClass('in') ? 'hide' : 'show']()
22162  }
22163
22164
22165  // COLLAPSE PLUGIN DEFINITION
22166  // ==========================
22167
22168  var old = $.fn.collapse
22169
22170  $.fn.collapse = function (option) {
22171    return this.each(function () {
22172      var $this   = $(this)
22173      var data    = $this.data('bs.collapse')
22174      var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
22175
22176      if (!data && options.toggle && option == 'show') option = !option
22177      if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
22178      if (typeof option == 'string') data[option]()
22179    })
22180  }
22181
22182  $.fn.collapse.Constructor = Collapse
22183
22184
22185  // COLLAPSE NO CONFLICT
22186  // ====================
22187
22188  $.fn.collapse.noConflict = function () {
22189    $.fn.collapse = old
22190    return this
22191  }
22192
22193
22194  // COLLAPSE DATA-API
22195  // =================
22196
22197  $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
22198    var $this   = $(this), href
22199    var target  = $this.attr('data-target')
22200        || e.preventDefault()
22201        || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
22202    //Edited by Uncode to replace ID in panel [START]
22203    var _target = href.replace(/^#/, "");
22204    if ( $('[data-id="' + _target + '"]').length ) {
22205      var $target = $('[data-id="' + _target + '"]')
22206    } else {
22207      var $target = $(target)
22208    }
22209    //Edited by Uncode to replace ID in panel [END]
22210    var data    = $target.data('bs.collapse')
22211    var option  = data ? 'toggle' : $this.data()
22212    var parent  = $this.attr('data-parent')
22213    var $parent = parent && $(parent)
22214
22215    if (!data || !data.transitioning) {
22216      if ($parent) $parent.find('[data-toggle="collapse"][data-parent="' + parent + '"]').not($this).addClass('collapsed')
22217      $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
22218    }
22219
22220    $target.collapse(option)
22221  })
22222
22223}(jQuery);
22224
22225
22226/* ========================================================================
22227 * Bootstrap: tab.js v3.1.1
22228 * http://getbootstrap.com/javascript/#tabs
22229 * ========================================================================
22230 * Copyright 2011-2014 Twitter, Inc.
22231 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
22232 * ======================================================================== */
22233
22234
22235+function ($) {
22236  'use strict';
22237
22238  // TAB CLASS DEFINITION
22239  // ====================
22240
22241  var Tab = function (element) {
22242    this.element = $(element)
22243  }
22244
22245  Tab.prototype.show = function () {
22246    var $this    = this.element
22247    var $ul      = $this.closest('ul:not(.dropdown-menu)')
22248    var selector = $this.data('target')
22249
22250    if (!selector) {
22251      selector = $this.attr('href')
22252      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
22253    }
22254
22255    if ($this.parent('li').hasClass('active')) return
22256
22257    var previous = $ul.find('.active:last a')[0]
22258    var e        = $.Event('show.bs.tab', {
22259      relatedTarget: previous
22260    })
22261
22262    $this.trigger(e)
22263
22264    if (e.isDefaultPrevented()) return
22265
22266    //Edited by Uncode to replace ID in panel [START]
22267    var _target = selector.replace(/^#/, "");
22268    if ( $('[data-id="' + _target + '"]').length )
22269      var $target = $('[data-id="' + _target + '"]')
22270    else
22271      var $target = $(selector)
22272    //Edited by Uncode to replace ID in panel [END]
22273
22274    this.activate($this.parent('li'), $ul)
22275    this.activate($target, $target.parent(), function () {
22276      $this.trigger({
22277        type: 'shown.bs.tab',
22278        relatedTarget: previous
22279      })
22280    })
22281  }
22282
22283  Tab.prototype.activate = function (element, container, callback) {
22284    var $active    = container.find('> .active')
22285    var transition = callback
22286      && $.support.transition
22287      && $active.hasClass('fade')
22288
22289    function next() {
22290      $active
22291        .removeClass('active')
22292        .find('> .dropdown-menu > .active')
22293        .removeClass('active')
22294
22295      element.addClass('active')
22296
22297      if (transition) {
22298        element[0].offsetWidth // reflow for transition
22299        element.addClass('in')
22300      } else {
22301        element.removeClass('fade')
22302      }
22303
22304      if (element.parent('.dropdown-menu')) {
22305        element.closest('li.dropdown').addClass('active')
22306      }
22307
22308      callback && callback()
22309    }
22310
22311    transition ?
22312      $active
22313        .one($.support.transition.end, next)
22314        .emulateTransitionEnd(150) :
22315      next()
22316
22317    $active.removeClass('in')
22318  }
22319
22320
22321  // TAB PLUGIN DEFINITION
22322  // =====================
22323
22324  var old = $.fn.tab
22325
22326  $.fn.tab = function ( option ) {
22327    return this.each(function () {
22328      var $this = $(this)
22329      var data  = $this.data('bs.tab')
22330
22331      if (!data) $this.data('bs.tab', (data = new Tab(this)))
22332      if (typeof option == 'string') data[option]()
22333    })
22334  }
22335
22336  $.fn.tab.Constructor = Tab
22337
22338
22339  // TAB NO CONFLICT
22340  // ===============
22341
22342  $.fn.tab.noConflict = function () {
22343    $.fn.tab = old
22344    return this
22345  }
22346
22347
22348  // TAB DATA-API
22349  // ============
22350
22351  $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
22352    e.preventDefault()
22353    $(this).tab('show')
22354  })
22355
22356}(jQuery);
22357
22358
22359/* ========================================================================
22360 * Bootstrap: tooltip.js v3.1.1
22361 * http://getbootstrap.com/javascript/#tooltip
22362 * Inspired by the original jQuery.tipsy by Jason Frame
22363 * ========================================================================
22364 * Copyright 2011-2014 Twitter, Inc.
22365 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
22366 * ======================================================================== */
22367
22368
22369+function ($) {
22370  'use strict';
22371
22372  // TOOLTIP PUBLIC CLASS DEFINITION
22373  // ===============================
22374
22375  var Tooltip = function (element, options) {
22376    this.type       =
22377    this.options    =
22378    this.enabled    =
22379    this.timeout    =
22380    this.hoverState =
22381    this.$element   = null
22382
22383    this.init('tooltip', element, options)
22384  }
22385
22386  Tooltip.DEFAULTS = {
22387    animation: true,
22388    placement: 'top',
22389    selector: false,
22390    template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
22391    trigger: 'hover focus',
22392    title: '',
22393    delay: 0,
22394    html: false,
22395    container: false,
22396    viewport: {
22397      selector: 'body',
22398      padding: 0
22399    }
22400  }
22401
22402  Tooltip.prototype.init = function (type, element, options) {
22403    this.enabled   = true
22404    this.type      = type
22405    this.$element  = $(element)
22406    this.options   = this.getOptions(options)
22407    this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)
22408
22409    var triggers = this.options.trigger.split(' ')
22410
22411    for (var i = triggers.length; i--;) {
22412      var trigger = triggers[i]
22413
22414      if (trigger == 'click') {
22415        this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
22416      } else if (trigger != 'manual') {
22417        var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
22418        var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
22419
22420        this.$element.on(eventIn  + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
22421        this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
22422      }
22423    }
22424
22425    this.options.selector ?
22426      (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
22427      this.fixTitle()
22428  }
22429
22430  Tooltip.prototype.getDefaults = function () {
22431    return Tooltip.DEFAULTS
22432  }
22433
22434  Tooltip.prototype.getOptions = function (options) {
22435    options = $.extend({}, this.getDefaults(), this.$element.data(), options)
22436
22437    if (options.delay && typeof options.delay == 'number') {
22438      options.delay = {
22439        show: options.delay,
22440        hide: options.delay
22441      }
22442    }
22443
22444    return options
22445  }
22446
22447  Tooltip.prototype.getDelegateOptions = function () {
22448    var options  = {}
22449    var defaults = this.getDefaults()
22450
22451    this._options && $.each(this._options, function (key, value) {
22452      if (defaults[key] != value) options[key] = value
22453    })
22454
22455    return options
22456  }
22457
22458  Tooltip.prototype.enter = function (obj) {
22459    var self = obj instanceof this.constructor ?
22460      obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
22461
22462    clearTimeout(self.timeout)
22463
22464    self.hoverState = 'in'
22465
22466    if (!self.options.delay || !self.options.delay.show) return self.show()
22467
22468    self.timeout = setTimeout(function () {
22469      if (self.hoverState == 'in') self.show()
22470    }, self.options.delay.show)
22471  }
22472
22473  Tooltip.prototype.leave = function (obj) {
22474    var self = obj instanceof this.constructor ?
22475      obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
22476
22477    clearTimeout(self.timeout)
22478
22479    self.hoverState = 'out'
22480
22481    if (!self.options.delay || !self.options.delay.hide) return self.hide()
22482
22483    self.timeout = setTimeout(function () {
22484      if (self.hoverState == 'out') self.hide()
22485    }, self.options.delay.hide)
22486  }
22487
22488  Tooltip.prototype.show = function () {
22489    var e = $.Event('show.bs.' + this.type)
22490
22491    if (this.hasContent() && this.enabled) {
22492      this.$element.trigger(e)
22493
22494      if (e.isDefaultPrevented()) return
22495      var that = this;
22496
22497      var $tip = this.tip()
22498
22499      this.setContent()
22500
22501      if (this.options.animation) $tip.addClass('fade')
22502
22503      var placement = typeof this.options.placement == 'function' ?
22504        this.options.placement.call(this, $tip[0], this.$element[0]) :
22505        this.options.placement
22506
22507      var autoToken = /\s?auto?\s?/i
22508      var autoPlace = autoToken.test(placement)
22509      if (autoPlace) placement =
22509 placement.replace(autoToken, '') || 'top'
22510
22511      $tip
22512        .detach()
22513        .css({ top: 0, left: 0, display: 'block' })
22514        .addClass(placement)
22515
22516      this.options.container ? $tip.appendTo(this.options.container) : $tip.insertAfter(this.$element)
22517
22518      var pos          = this.getPosition()
22519      var actualWidth  = $tip[0].offsetWidth
22520      var actualHeight = $tip[0].offsetHeight
22521
22522      if (autoPlace) {
22523        var orgPlacement = placement
22524        var $parent      = this.$element.parent()
22525        var parentDim    = this.getPosition($parent)
22526
22527        placement = placement == 'bottom' && pos.top   + pos.height       + actualHeight - parentDim.scroll > parentDim.height ? 'top'    :
22528                    placement == 'top'    && pos.top   - parentDim.scroll - actualHeight < 0                                   ? 'bottom' :
22529                    placement == 'right'  && pos.right + actualWidth      > parentDim.width                                    ? 'left'   :
22530                    placement == 'left'   && pos.left  - actualWidth      < parentDim.left                                     ? 'right'  :
22531                    placement
22532
22533        $tip
22534          .removeClass(orgPlacement)
22535          .addClass(placement)
22536      }
22537
22538      var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
22539
22540      this.applyPlacement(calculatedOffset, placement)
22541      this.hoverState = null
22542
22543      var complete = function() {
22544        that.$element.trigger('shown.bs.' + that.type)
22545      }
22546
22547      $.support.transition && this.$tip.hasClass('fade') ?
22548        $tip
22549          .one($.support.transition.end, complete)
22550          .emulateTransitionEnd(150) :
22551        complete()
22552    }
22553  }
22554
22555  Tooltip.prototype.applyPlacement = function (offset, placement) {
22556    var $tip   = this.tip()
22557    var width  = $tip[0].offsetWidth
22558    var height = $tip[0].offsetHeight
22559
22560    // manually read margins because getBoundingClientRect includes difference
22561    var marginTop = parseInt($tip.css('margin-top'), 10)
22562    var marginLeft = parseInt($tip.css('margin-left'), 10)
22563
22564    // we must check for NaN for ie 8/9
22565    if (isNaN(marginTop))  marginTop  = 0
22566    if (isNaN(marginLeft)) marginLeft = 0
22567
22568    offset.top  = offset.top  + marginTop
22569    offset.left = offset.left + marginLeft
22570
22571    // $.fn.offset doesn't round pixel values
22572    // so we use setOffset directly with our own function B-0
22573    $.offset.setOffset($tip[0], $.extend({
22574      using: function (props) {
22575        $tip.css({
22576          top: Math.round(props.top),
22577          left: Math.round(props.left)
22578        })
22579      }
22580    }, offset), 0)
22581
22582    $tip.addClass('in')
22583
22584    // check to see if placing tip in new offset caused the tip to resize itself
22585    var actualWidth  = $tip[0].offsetWidth
22586    var actualHeight = $tip[0].offsetHeight
22587
22588    if (placement == 'top' && actualHeight != height) {
22589      offset.top = offset.top + height - actualHeight
22590    }
22591
22592    var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
22593
22594    if (delta.left) offset.left += delta.left
22595    else offset.top += delta.top
22596
22597    var arrowDelta          = delta.left ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
22598    var arrowPosition       = delta.left ? 'left'        : 'top'
22599    var arrowOffsetPosition = delta.left ? 'offsetWidth' : 'offsetHeight'
22600
22601    $tip.offset(offset)
22602    this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], arrowPosition)
22603  }
22604
22605  Tooltip.prototype.replaceArrow = function (delta, dimension, position) {
22606    this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '')
22607  }
22608
22609  Tooltip.prototype.setContent = function () {
22610    var $tip  = this.tip()
22611    var title = this.getTitle()
22612
22613    $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
22614    $tip.removeClass('fade in top bottom left right')
22615  }
22616
22617  Tooltip.prototype.hide = function () {
22618    var that = this
22619    var $tip = this.tip()
22620    var e    = $.Event('hide.bs.' + this.type)
22621
22622    function complete() {
22623      if (that.hoverState != 'in') $tip.detach()
22624      that.$element.trigger('hidden.bs.' + that.type)
22625    }
22626
22627    this.$element.trigger(e)
22628
22629    if (e.isDefaultPrevented()) return
22630
22631    $tip.removeClass('in')
22632
22633    $.support.transition && this.$tip.hasClass('fade') ?
22634      $tip
22635        .one($.support.transition.end, complete)
22636        .emulateTransitionEnd(150) :
22637      complete()
22638
22639    this.hoverState = null
22640
22641    return this
22642  }
22643
22644  Tooltip.prototype.fixTitle = function () {
22645    var $e = this.$element
22646    if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
22647      $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
22648    }
22649  }
22650
22651  Tooltip.prototype.hasContent = function () {
22652    return this.getTitle()
22653  }
22654
22655  Tooltip.prototype.getPosition = function ($element) {
22656    $element   = $element || this.$element
22657    var el     = $element[0]
22658    var isBody = el.tagName == 'BODY'
22659    return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : null, {
22660      scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop(),
22661      width:  isBody ? $(window).width()  : $element.outerWidth(),
22662      height: isBody ? $(window).height() : $element.outerHeight()
22663    }, isBody ? {top: 0, left: 0} : $element.offset())
22664  }
22665
22666  Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
22667    return placement == 'bottom' ? { top: pos.top + pos.height,   left: pos.left + pos.width / 2 - actualWidth / 2  } :
22668           placement == 'top'    ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2  } :
22669           placement == 'left'   ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
22670        /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width   }
22671
22672  }
22673
22674  Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
22675    var delta = { top: 0, left: 0 }
22676    if (!this.$viewport) return delta
22677
22678    var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
22679    var viewportDimensions = this.getPosition(this.$viewport)
22680
22681    if (/right|left/.test(placement)) {
22682      var topEdgeOffset    = pos.top - viewportPadding - viewportDimensions.scroll
22683      var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
22684      if (topEdgeOffset < viewportDimensions.top) { // top overflow
22685        delta.top = viewportDimensions.top - topEdgeOffset
22686      } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
22687        delta.top = viewportDimensions.top + viewportDimensions.height - bottom
22687EdgeOffset
22688      }
22689    } else {
22690      var leftEdgeOffset  = pos.left - viewportPadding
22691      var rightEdgeOffset = pos.left + viewportPadding + actualWidth
22692      if (leftEdgeOffset < viewportDimensions.left) { // left overflow
22693        delta.left = viewportDimensions.left - leftEdgeOffset
22694      } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
22695        delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
22696      }
22697    }
22698
22699    return delta
22700  }
22701
22702  Tooltip.prototype.getTitle = function () {
22703    var title
22704    var $e = this.$element
22705    var o  = this.options
22706
22707    title = $e.attr('data-original-title')
22708      || (typeof o.title == 'function' ? o.title.call($e[0]) :  o.title)
22709
22710    return title
22711  }
22712
22713  Tooltip.prototype.tip = function () {
22714    return this.$tip = this.$tip || $(this.options.template)
22715  }
22716
22717  Tooltip.prototype.arrow = function () {
22718    return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')
22719  }
22720
22721  Tooltip.prototype.validate = function () {
22722    if (!this.$element[0].parentNode) {
22723      this.hide()
22724      this.$element = null
22725      this.options  = null
22726    }
22727  }
22728
22729  Tooltip.prototype.enable = function () {
22730    this.enabled = true
22731  }
22732
22733  Tooltip.prototype.disable = function () {
22734    this.enabled = false
22735  }
22736
22737  Tooltip.prototype.toggleEnabled = function () {
22738    this.enabled = !this.enabled
22739  }
22740
22741  Tooltip.prototype.toggle = function (e) {
22742    var self = e ? $(e.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) : this
22743    self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
22744  }
22745
22746  Tooltip.prototype.destroy = function () {
22747    clearTimeout(this.timeout)
22748    this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
22749  }
22750
22751
22752  // TOOLTIP PLUGIN DEFINITION
22753  // =========================
22754
22755  var old = $.fn.tooltip
22756
22757  $.fn.tooltip = function (option) {
22758    return this.each(function () {
22759      var $this   = $(this)
22760      var data    = $this.data('bs.tooltip')
22761      var options = typeof option == 'object' && option
22762
22763      if (!data && option == 'destroy') return
22764      if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
22765      if (typeof option == 'string') data[option]()
22766    })
22767  }
22768
22769  $.fn.tooltip.Constructor = Tooltip
22770
22771
22772  // TOOLTIP NO CONFLICT
22773  // ===================
22774
22775  $.fn.tooltip.noConflict = function () {
22776    $.fn.tooltip = old
22777    return this
22778  }
22779
22780}(jQuery);
22781
22782
22783/* ========================================================================
22784 * Bootstrap: transition.js v3.1.1
22785 * http://getbootstrap.com/javascript/#transitions
22786 * ========================================================================
22787 * Copyright 2011-2014 Twitter, Inc.
22788 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
22789 * ======================================================================== */
22790
22791
22792+function ($) {
22793  'use strict';
22794
22795  // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
22796  // ============================================================
22797
22798  function transitionEnd() {
22799    var el = document.createElement('bootstrap')
22800
22801    var transEndEventNames = {
22802      WebkitTransition : 'webkitTransitionEnd',
22803      MozTransition    : 'transitionend',
22804      OTransition      : 'oTransitionEnd otransitionend',
22805      transition       : 'transitionend'
22806    }
22807
22808    for (var name in transEndEventNames) {
22809      if (el.style[name] !== undefined) {
22810        return { end: transEndEventNames[name] }
22811      }
22812    }
22813
22814    return false // explicit for ie8 (  ._.)
22815  }
22816
22817  // http://blog.alexmaccaw.com/css-transitions
22818  $.fn.emulateTransitionEnd = function (duration) {
22819    var called = false, $el = this
22820    $(this).one($.support.transition.end, function () { called = true })
22821    var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
22822    setTimeout(callback, duration)
22823    return this
22824  }
22825
22826  $(function () {
22827    $.support.transition = transitionEnd()
22828  })
22829
22830}(jQuery);
22831
22832

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.