PageSourceSearch

https://nycmtb.org/wp-content/themes/uncode/library/js/plugins.js?ver=482582601

js nycmtb.org collected 2026-10-10 00:57:13 UTC 823,532 bytes, 25,068 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 * Isotope PACKAGED v2.2.2
293 *
294 * Licensed GPLv3 for open source use
295 * or Isotope Commercial License for commercial use
296 *
297 * http://isotope.metafizzy.co
298 * Copyright 2015 Metafizzy
299 */
300
301/**
302 * Bridget makes jQuery widgets
303 * v1.1.0
304 * MIT license
305 */
306
307( function( window ) {
308
309
310
311// -------------------------- utils -------------------------- //
312
313var slice = Array.prototype.slice;
314
315function noop() {}
316
317// -------------------------- definition -------------------------- //
318
319function defineBridget( $ ) {
320
321// bail if no jQuery
322if ( !$ ) {
323  return;
324}
325
326// -------------------------- addOptionMethod -------------------------- //
327
328/**
329 * adds option method -> $().plugin('option', {...})
330 * @param {Function} PluginClass - constructor class
331 */
332function addOptionMethod( PluginClass ) {
333  // don't overwrite original option method
334  if ( PluginClass.prototype.option ) {
335    return;
336  }
337
338  // option setter
339  PluginClass.prototype.option = function( opts ) {
340    // bail out if not an object
341    if ( !$.isPlainObject( opts ) ){
342      return;
343    }
344    this.options = $.extend( true, this.options, opts );
345  };
346}
347
348// -------------------------- plugin bridge -------------------------- //
349
350// helper function for logging errors
351// $.error breaks jQuery chaining
352var logError = typeof console === 'undefined' ? noop :
353  function( message ) {
354    console.error( message );
355  };
356
357/**
358 * jQuery plugin bridge, access methods like $elem.plugin('method')
359 * @param {String} namespace - plugin name
360 * @param {Function} PluginClass - constructor class
361 */
362function bridge( namespace, PluginClass ) {
363  // add to jQuery fn namespace
364  $.fn[ namespace ] = function( options ) {
365    if ( typeof options === 'string' ) {
366      // call plugin method when first argument is a string
367      // get arguments for method
368      var args = slice.call( arguments, 1 );
369
370      for ( var i=0, len = this.length; i < len; i++ ) {
371        var elem = this[i];
372        var instance = $.data( elem, namespace );
373        if ( !instance ) {
374          logError( "cannot call methods on " + namespace + " prior to initialization; " +
375            "attempted to call '" + options + "'" );
376          continue;
377        }
378        if ( !$.isFunction( instance[options] ) || options.charAt(0) === '_' ) {
379          logError( "no such method '" + options + "' for " + namespace + " instance" );
380          continue;
381        }
382
383        // trigger method with arguments
384        var returnValue = instance[ options ].apply( instance, args );
385
386        // break look and return first value if provided
387        if ( returnValue !== undefined ) {
388          return returnValue;
389        }
390      }
391      // return this if no return value
392      return this;
393    } else {
394      return this.each( function() {
395        var instance = $.data( this, namespace );
396        if ( instance ) {
397          // apply options & init
398          instance.option( options );
399          instance._init();
400        } else {
401          // initialize new instance
402          instance = new PluginClass( this, options );
403          $.data( this, namespace, instance );
404        }
405      });
406    }
407  };
408
409}
410
411// -------------------------- bridget -------------------------- //
412
413/**
414 * converts a Prototypical class into a proper jQuery plugin
415 *   the class must have a ._init method
416 * @param {String} namespace - plugin name, used in $().pluginName
417 * @param {Function} PluginClass - constructor class
418 */
419$.bridget = function( namespace, PluginClass ) {
420  addOptionMethod( PluginClass );
421  bridge( namespace, PluginClass );
422};
423
424return $.bridget;
425
426}
427
428// transport
429if ( typeof define === 'function' && define.amd ) {
430  // AMD
431  define( 'jquery-bridget/jquery.bridget',[ 'jquery' ], defineBridget );
432} else if ( typeof exports === 'object' ) {
433  defineBridget( require('jquery') );
434} else {
435  // get jquery from browser global
436  defineBridget( window.jQuery );
437}
438
439})( window );
440
441/*!
442 * eventie v1.0.6
443 * event binding helper
444 *   eventie.bind( elem, 'click', myFn )
445 *   eventie.unbind( elem, 'click', myFn )
446 * MIT license
447 */
448
449/*jshint browser: true, undef: true, unused: true */
450/*global define: false, module: false */
451
452( function( window ) {
453
454
455
456var docElem = document.documentElement;
457
458var bind = function() {};
459
460function getIEEvent( obj ) {
461  var event = window.event;
462  // add event.target
463  event.target = event.target || event.srcElement || obj;
464  return event;
465}
466
467if ( docElem.addEventListener ) {
468  bind = function( obj, type, fn ) {
469    obj.addEventListener( type, fn, false );
470  };
471} else if ( docElem.attachEvent ) {
472  bind = function( obj, type, fn ) {
473    obj[ type + fn ] = fn.handleEvent ?
474      function() {
475        var event = getIEEvent( obj );
476        fn.handleEvent.call( fn, event );
477      } :
478      function() {
479        var event = getIEEvent( obj );
480        fn.call( obj, event );
481      };
482    obj.attachEvent( "on" + type, obj[ type + fn ] );
483  };
484}
485
486var unbind = function() {};
487
488if ( docElem.removeEventListener ) {
489  unbind = function( obj, type, fn ) {
490    obj.removeEventListener( type, fn, false );
491  };
492} else if ( docElem.detachEvent ) {
493  unbind = function( obj, type, fn ) {
494    obj.detachEvent( "on" + type, obj[ type + fn ] );
495    try {
496      delete obj[ type + fn ];
497    } catch ( err ) {
498      // can't delete window object properties
499      obj[ type + fn ] = undefined;
500    }
501  };
502}
503
504var eventie = {
505  bind: bind,
506  unbind: unbind
507};
508
509// ----- module definition ----- //
510
511if ( typeof define === 'function' && define.amd ) {
512  // AMD
513  define( 'eventie/eventie',eventie );
514} else if ( typeof exports === 'object' ) {
515  // CommonJS
516  module.exports = eventie;
517} else {
518  // browser global
519  window.eventie = eventie;
520}
521
522})( window );
523
524/*!
525 * EventEmitter v4.2.11 - git.io/ee
526 * Unlicense - http://unlicense.org/
527 * Oliver Caldwell - http://oli.me.uk/
528 * @preserve
529 */
530
531;(function () {
532    'use strict';
533
534    /**
535     * Class for managing events.
536     * Can be extended to provide event functionality in other classes.
537     *
538     * @class EventEmitter Manages event registering and emitting.
539     */
540    function EventEmitter() {}
541
542    // Shortcuts to improve speed and size
543    var proto = EventEmitter.prototype;
544    var exports = this;
545    var originalGlobalValue = exports.EventEmitter;
546
547    /**
548     * Finds the index of the listener for the event in its storage array.
549     *
550     * @param {Function[]} listeners Array of listeners to search through.
551     * @param {Function} listener Method to look for.
552     * @return {Number} Index of the specified listener, -1 if not found
553     * @api private
554     */
555    function indexOfListener(listeners, listener) {
556        var i = listeners.length;
557        while (i--) {
558            if (listeners[i].listener === listener) {
559                return i;
560            }
561        }
562
563        return -1;
564    }
565
566    /**
567     * Alias a method while keeping the context correct, to allow for over
vendor: 16,384 bytes, lines 567-970
567writing of target method.
568     *
569     * @param {String} name The name of the target method.
570     * @return {Function} The aliased method
571     * @api private
572     */
573    function alias(name) {
574        return function aliasClosure() {
575            return this[name].apply(this, arguments);
576        };
577    }
578
579    /**
580     * Returns the listener array for the specified event.
581     * Will initialise the event object and listener arrays if required.
582     * 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.
583     * Each property in the object response is an array of listener functions.
584     *
585     * @param {String|RegExp} evt Name of the event to return the listeners from.
586     * @return {Function[]|Object} All listener functions for the event.
587     */
588    proto.getListeners = function getListeners(evt) {
589        var events = this._getEvents();
590        var response;
591        var key;
592
593        // Return a concatenated array of all matching events if
594        // the selector is a regular expression.
595        if (evt instanceof RegExp) {
596            response = {};
597            for (key in events) {
598                if (events.hasOwnProperty(key) && evt.test(key)) {
599                    response[key] = events[key];
600                }
601            }
602        }
603        else {
604            response = events[evt] || (events[evt] = []);
605        }
606
607        return response;
608    };
609
610    /**
611     * Takes a list of listener objects and flattens it into a list of listener functions.
612     *
613     * @param {Object[]} listeners Raw listener objects.
614     * @return {Function[]} Just the listener functions.
615     */
616    proto.flattenListeners = function flattenListeners(listeners) {
617        var flatListeners = [];
618        var i;
619
620        for (i = 0; i < listeners.length; i += 1) {
621            flatListeners.push(listeners[i].listener);
622        }
623
624        return flatListeners;
625    };
626
627    /**
628     * 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.
629     *
630     * @param {String|RegExp} evt Name of the event to return the listeners from.
631     * @return {Object} All listener functions for an event in an object.
632     */
633    proto.getListenersAsObject = function getListenersAsObject(evt) {
634        var listeners = this.getListeners(evt);
635        var response;
636
637        if (listeners instanceof Array) {
638            response = {};
639            response[evt] = listeners;
640        }
641
642        return response || listeners;
643    };
644
645    /**
646     * Adds a listener function to the specified event.
647     * The listener will not be added if it is a duplicate.
648     * If the listener returns true then it will be removed after it is called.
649     * If you pass a regular expression as the event name then the listener will be added to all events that match it.
650     *
651     * @param {String|RegExp} evt Name of the event to attach the listener to.
652     * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
653     * @return {Object} Current instance of EventEmitter for chaining.
654     */
655    proto.addListener = function addListener(evt, listener) {
656        var listeners = this.getListenersAsObject(evt);
657        var listenerIsWrapped = typeof listener === 'object';
658        var key;
659
660        for (key in listeners) {
661            if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) {
662                listeners[key].push(listenerIsWrapped ? listener : {
663                    listener: listener,
664                    once: false
665                });
666            }
667        }
668
669        return this;
670    };
671
672    /**
673     * Alias of addListener
674     */
675    proto.on = alias('addListener');
676
677    /**
678     * Semi-alias of addListener. It will add a listener that will be
679     * automatically removed after its first execution.
680     *
681     * @param {String|RegExp} evt Name of the event to attach the listener to.
682     * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling.
683     * @return {Object} Current instance of EventEmitter for chaining.
684     */
685    proto.addOnceListener = function addOnceListener(evt, listener) {
686        return this.addListener(evt, {
687            listener: listener,
688            once: true
689        });
690    };
691
692    /**
693     * Alias of addOnceListener.
694     */
695    proto.once = alias('addOnceListener');
696
697    /**
698     * 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.
699     * You need to tell it what event names should be matched by a regex.
700     *
701     * @param {String} evt Name of the event to create.
702     * @return {Object} Current instance of EventEmitter for chaining.
703     */
704    proto.defineEvent = function defineEvent(evt) {
705        this.getListeners(evt);
706        return this;
707    };
708
709    /**
710     * Uses defineEvent to define multiple events.
711     *
712     * @param {String[]} evts An array of event names to define.
713     * @return {Object} Current instance of EventEmitter for chaining.
714     */
715    proto.defineEvents = function defineEvents(evts) {
716        for (var i = 0; i < evts.length; i += 1) {
717            this.defineEvent(evts[i]);
718        }
719        return this;
720    };
721
722    /**
723     * Removes a listener function from the specified event.
724     * When passed a regular expression as the event name, it will remove the listener from all events that match it.
725     *
726     * @param {String|RegExp} evt Name of the event to remove the listener from.
727     * @param {Function} listener Method to remove from the event.
728     * @return {Object} Current instance of EventEmitter for chaining.
729     */
730    proto.removeListener = function removeListener(evt, listener) {
731        var listeners = this.getListenersAsObject(evt);
732        var index;
733        var key;
734
735        for (key in listeners) {
736            if (listeners.hasOwnProperty(key)) {
737                index = indexOfListener(listeners[key], listener);
738
739                if (index !== -1) {
740                    listeners[key].splice(index, 1);
741                }
742            }
743        }
744
745        return this;
746    };
747
748    /**
749     * Alias of removeListener
750     */
751    proto.off = alias('removeListener');
752
753    /**
754     * Adds listeners in bulk using the manipulateListeners method.
755     * 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.
756     * You can also pass it a regular expression to add the array of listeners to all events that match it.
757     * Yeah, this function does quite a bit. That's probably a bad thing.
758     *
759     * @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.
760     * @param {Function[]} [listeners] An optional array of listener functions to add.
761     * @return {Object} Current instance of EventEmitter for chaining.
762     */
763    proto.addListeners = function addListeners(evt, listeners) {
764        // Pass through to manipulateListeners
765        return this.manipulateListeners(false, evt, listeners);
766    };
767
768    /**
769     * Removes listeners in bulk using the manipulateListeners method.
770     * 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.
771     * You can also pass it an event name and an array of listeners to be removed.
772     * You can also pass it a regular expression to remove the listeners from all events that match it.
773     *
774     * @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.
775     * @param {Function[]} [listeners] An optional array of listener functions to remove.
776     * @return {Object} Current instance of EventEmitter for chaining.
777     */
778    proto.removeListeners = function removeListeners(evt, listeners) {
779        // Pass through to manipulateListeners
780        return this.manipulateListeners(true, evt, listeners);
781    };
782
783    /**
784     * 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.
785     * The first argument will determine if the listeners are removed (true) or added (false).
786     * 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.
787     * You can also pass it an event name and an array of listeners to be added/removed.
788     * You can also pass it a regular expression to manipulate the listeners of all events that match it.
789     *
790     * @param {Boolean} remove True if you want to remove listeners, false if you want to add.
791     * @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.
792     * @param {Function[]} [listeners] An optional array of listener functions to add/remove.
793     * @return {Object} Current instance of EventEmitter for chaining.
794     */
795    proto.manipulateListeners = function manipulateListeners(remove, evt, listeners) {
796        var i;
797        var value;
798        var single = remove ? this.removeListener : this.addListener;
799        var multiple = remove ? this.removeListeners : this.addListeners;
800
801        // If evt is an object then pass each of its properties to this method
802        if (typeof evt === 'object' && !(evt instanceof RegExp)) {
803            for (i in evt) {
804                if (evt.hasOwnProperty(i) && (value = evt[i])) {
805                    // Pass the single listener straight through to the singular method
806                    if (typeof value === 'function') {
807                        single.call(this, i, value);
808                    }
809                    else {
810                        // Otherwise pass back to the multiple function
811                        multiple.call(this, i, value);
812                    }
813                }
814            }
815        }
816        else {
817            // So evt must be a string
818            // And listeners must be an array of listeners
819            // Loop over it and pass each one to the multiple method
820            i = listeners.length;
821            while (i--) {
822                single.call(this, evt, listeners[i]);
823            }
824        }
825
826        return this;
827    };
828
829    /**
830     * Removes all listeners from a specified event.
831     * If you do not specify an event then all listeners will be removed.
832     * That means every event will be emptied.
833     * You can also pass a regex to remove all events that match it.
834     *
835     * @param {String|RegExp} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed.
836     * @return {Object} Current instance of EventEmitter for chaining.
837     */
838    proto.removeEvent = function removeEvent(evt) {
839        var type = typeof evt;
840        var events = this._getEvents();
841        var key;
842
843        // Remove different things depending on the state of evt
844        if (type === 'string') {
845            // Remove all listeners for the specified event
846            delete events[evt];
847        }
848        else if (evt instanceof RegExp) {
849            // Remove all events matching the regex.
850            for (key in events) {
851                if (events.hasOwnProperty(key) && evt.test(key)) {
852                    delete events[key];
853                }
854            }
855        }
856        else {
857            // Remove all listeners in all events
858            delete this._events;
859        }
860
861        return this;
862    };
863
864    /**
865     * Alias of removeEvent.
866     *
867     * Added to mirror the node API.
868     */
869    proto.removeAllListeners = alias('removeEvent');
870
871    /**
872     * Emits an event of your choice.
873     * When emitted, every listener attached to that event will be executed.
874     * If you pass the optional argument array then those arguments will be passed to every listener upon execution.
875     * Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately.
876     * So they will not arrive within the array on the other side, they will be separate.
877     * You can also pass a regular expression to emit to all events that match it.
878     *
879     * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
880     * @param {Array} [args] Optional array of arguments to be passed to each listener.
881     * @return {Object} Current instance of EventEmitter for chaining.
882     */
883    proto.emitEvent = function emitEvent(evt, args) {
884        var listeners = this.getListenersAsObject(evt);
885        var listener;
886        var i;
887        var key;
888        var response;
889
890        for (key in listeners) {
891            if (listeners.hasOwnProperty(key)) {
892                i = listeners[key].length;
893
894                while (i--) {
895                    // If the listener returns true then it shall be removed from the event
896                    // The function is executed either with a basic call or an apply if there is an args array
897                    listener = listeners[key][i];
898
899                    if (listener.once === true) {
900                        this.removeListener(evt, listener.listener);
901                    }
902
903                    response = listener.listener.apply(this, args || []);
904
905                    if (response === this._getOnceReturnValue()) {
906                        this.removeListener(evt, listener.listener);
907                    }
908                }
909            }
910        }
911
912        return this;
913    };
914
915    /**
916     * Alias of emitEvent
917     */
918    proto.trigger = alias('emitEvent');
919
920    /**
921     * 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.
922     * As with emitEvent, you can pass a regex in place of the event name to emit to all events that match it.
923     *
924     * @param {String|RegExp} evt Name of the event to emit and execute listeners for.
925     * @param {...*} Optional additional arguments to be passed to each listener.
926     * @return {Object} Current instance of EventEmitter for chaining.
927     */
928    proto.emit = function emit(evt) {
929        var args = Array.prototype.slice.call(arguments, 1);
930        return this.emitEvent(evt, args);
931    };
932
933    /**
934     * Sets the current value to check against when executing listeners. If a
935     * listeners return value matches the one set here then it will be removed
936     * after execution. This value defaults to true.
937     *
938     * @param {*} value The new value to check for when executing listeners.
939     * @return {Object} Current instance of EventEmitter for chaining.
940     */
941    proto.setOnceReturnValue = function setOnceReturnValue(value) {
942        this._onceReturnValue = value;
943        return this;
944    };
945
946    /**
947     * Fetches the current value to check against when executing listeners. If
948     * the listeners return value matches this one then it should be removed
949     * automatically. It will return true by default.
950     *
951     * @return {*|Boolean} The current value to check for or the default, true.
952     * @api private
953     */
954    proto._getOnceReturnValue = function _getOnceReturnValue() {
955        if (this.hasOwnProperty('_onceReturnValue')) {
956            return this._onceReturnValue;
957        }
958        else {
959            return true;
960        }
961    };
962
963    /**
964     * Fetches the events object and creates one if required.
965     *
966     * @return {Object} The events storage object.
967     * @api private
968     */
969    proto._getEvents = function _getEvents() {
970        return this._events || (this
970._events = {});
971    };
972
973    /**
974     * Reverts the global {@link EventEmitter} to its previous value and returns a reference to this version.
975     *
976     * @return {Function} Non conflicting EventEmitter class.
977     */
978    EventEmitter.noConflict = function noConflict() {
979        exports.EventEmitter = originalGlobalValue;
980        return EventEmitter;
981    };
982
983    // Expose the class either via AMD, CommonJS or the global object
984    if (typeof define === 'function' && define.amd) {
985        define('eventEmitter/EventEmitter',[],function () {
986            return EventEmitter;
987        });
988    }
989    else if (typeof module === 'object' && module.exports){
990        module.exports = EventEmitter;
991    }
992    else {
993        exports.EventEmitter = EventEmitter;
994    }
995}.call(this));
996
997/*!
998 * getStyleProperty v1.0.4
999 * original by kangax
1000 * http://perfectionkills.com/feature-testing-css-properties/
1001 * MIT license
1002 */
1003
1004/*jshint browser: true, strict: true, undef: true */
1005/*global define: false, exports: false, module: false */
1006
1007( function( window ) {
1008
1009
1010
1011var prefixes = 'Webkit Moz ms Ms O'.split(' ');
1012var docElemStyle = document.documentElement.style;
1013
1014function getStyleProperty( propName ) {
1015  if ( !propName ) {
1016    return;
1017  }
1018
1019  // test standard property first
1020  if ( typeof docElemStyle[ propName ] === 'string' ) {
1021    return propName;
1022  }
1023
1024  // capitalize
1025  propName = propName.charAt(0).toUpperCase() + propName.slice(1);
1026
1027  // test vendor specific properties
1028  var prefixed;
1029  for ( var i=0, len = prefixes.length; i < len; i++ ) {
1030    prefixed = prefixes[i] + propName;
1031    if ( typeof docElemStyle[ prefixed ] === 'string' ) {
1032      return prefixed;
1033    }
1034  }
1035}
1036
1037// transport
1038if ( typeof define === 'function' && define.amd ) {
1039  // AMD
1040  define( 'get-style-property/get-style-property',[],function() {
1041    return getStyleProperty;
1042  });
1043} else if ( typeof exports === 'object' ) {
1044  // CommonJS for Component
1045  module.exports = getStyleProperty;
1046} else {
1047  // browser global
1048  window.getStyleProperty = getStyleProperty;
1049}
1050
1051})( window );
1052
1053/*!
1054 * getSize v1.2.2
1055 * measure size of elements
1056 * MIT license
1057 */
1058
1059/*jshint browser: true, strict: true, undef: true, unused: true */
1060/*global define: false, exports: false, require: false, module: false, console: false */
1061
1062( function( window, undefined ) {
1063
1064
1065
1066// -------------------------- helpers -------------------------- //
1067
1068// get a number from a string, not a percentage
1069function getStyleSize( value ) {
1070  var num = parseFloat( value );
1071  // not a percent like '100%', and a number
1072  var isValid = value.indexOf('%') === -1 && !isNaN( num );
1073  return isValid && num;
1074}
1075
1076function noop() {}
1077
1078var logError = typeof console === 'undefined' ? noop :
1079  function( message ) {
1080    console.error( message );
1081  };
1082
1083// -------------------------- measurements -------------------------- //
1084
1085var measurements = [
1086  'paddingLeft',
1087  'paddingRight',
1088  'paddingTop',
1089  'paddingBottom',
1090  'marginLeft',
1091  'marginRight',
1092  'marginTop',
1093  'marginBottom',
1094  'borderLeftWidth',
1095  'borderRightWidth',
1096  'borderTopWidth',
1097  'borderBottomWidth'
1098];
1099
1100function getZeroSize() {
1101  var size = {
1102    width: 0,
1103    height: 0,
1104    innerWidth: 0,
1105    innerHeight: 0,
1106    outerWidth: 0,
1107    outerHeight: 0
1108  };
1109  for ( var i=0, len = measurements.length; i < len; i++ ) {
1110    var measurement = measurements[i];
1111    size[ measurement ] = 0;
1112  }
1113  return size;
1114}
1115
1116
1117
1118function defineGetSize( getStyleProperty ) {
1119
1120// -------------------------- setup -------------------------- //
1121
1122var isSetup = false;
1123
1124var getStyle, boxSizingProp, isBoxSizeOuter;
1125
1126/**
1127 * setup vars and functions
1128 * do it on initial getSize(), rather than on script load
1129 * For Firefox bug https://bugzilla.mozilla.org/show_bug.cgi?id=548397
1130 */
1131function setup() {
1132  // setup once
1133  if ( isSetup ) {
1134    return;
1135  }
1136  isSetup = true;
1137
1138  var getComputedStyle = window.getComputedStyle;
1139  getStyle = ( function() {
1140    var getStyleFn = getComputedStyle ?
1141      function( elem ) {
1142        return getComputedStyle( elem, null );
1143      } :
1144      function( elem ) {
1145        return elem.currentStyle;
1146      };
1147
1148      return function getStyle( elem ) {
1149        var style = getStyleFn( elem );
1150        if ( !style ) {
1151          logError( 'Style returned ' + style +
1152            '. Are you running this code in a hidden iframe on Firefox? ' +
1153            'See http://bit.ly/getsizebug1' );
1154        }
1155        return style;
1156      };
1157  })();
1158
1159  // -------------------------- box sizing -------------------------- //
1160
1161  boxSizingProp = getStyleProperty('boxSizing');
1162
1163  /**
1164   * WebKit measures the outer-width on style.width on border-box elems
1165   * IE & Firefox measures the inner-width
1166   */
1167  if ( boxSizingProp ) {
1168    var div = document.createElement('div');
1169    div.style.width = '200px';
1170    div.style.padding = '1px 2px 3px 4px';
1171    div.style.borderStyle = 'solid';
1172    div.style.borderWidth = '1px 2px 3px 4px';
1173    div.style[ boxSizingProp ] = 'border-box';
1174
1175    var body = document.body || document.documentElement;
1176    body.appendChild( div );
1177    var style = getStyle( div );
1178
1179    isBoxSizeOuter = getStyleSize( style.width ) === 200;
1180    body.removeChild( div );
1181  }
1182
1183}
1184
1185// -------------------------- getSize -------------------------- //
1186
1187function getSize( elem ) {
1188  setup();
1189
1190  // use querySeletor if elem is string
1191  if ( typeof elem === 'string' ) {
1192    elem = document.querySelector( elem );
1193  }
1194
1195  // do not proceed on non-objects
1196  if ( !elem || typeof elem !== 'object' || !elem.nodeType ) {
1197    return;
1198  }
1199
1200  var style = getStyle( elem );
1201
1202  // if hidden, everything is 0
1203  if ( style.display === 'none' ) {
1204    return getZeroSize();
1205  }
1206
1207  var size = {};
1208  size.width = elem.offsetWidth;
1209  size.height = elem.offsetHeight;
1210
1211  var isBorderBox = size.isBorderBox = !!( boxSizingProp &&
1212    style[ boxSizingProp ] && style[ boxSizingProp ] === 'border-box' );
1213
1214  // get all measurements
1215  for ( var i=0, len = measurements.length; i < len; i++ ) {
1216    var measurement = measurements[i];
1217    var value = style[ measurement ];
1218    value = mungeNonPixel( elem, value );
1219    var num = parseFloat( value );
1220    // any 'auto', 'medium' value will be 0
1221    size[ measurement ] = !isNaN( num ) ? num : 0;
1222  }
1223
1224  var paddingWidth = size.paddingLeft + size.paddingRight;
1225  var paddingHeight = size.paddingTop + size.paddingBottom;
1226  var marginWidth = size.marginLeft + size.marginRight;
1227  var marginHeight = size.marginTop + size.marginBottom;
1228  var borderWidth = size.borderLeftWidth + size.borderRightWidth;
1229  var borderHeight = size.borderTopWidth + size.borderBottomWidth;
1230
1231  var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter;
1232
1233  // overwrite width and height if we can get it from style
1234  var styleWidth = getStyleSize( style.width );
1235  if ( styleWidth !== false ) {
1236    size.width = styleWidth +
1237      // add padding and border unless it's already including it
1238      ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth );
1239  }
1240
1241  var styleHeight = getStyleSize( style.height );
1242  if ( styleHeight !== false ) {
1243    size.height = styleHeight +
1244      // add padding and border unless it's already including it
1245      ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight );
1246  }
1247
1248  size.innerWidth = size.width - ( paddingWidth + borderWidth );
1249  size.innerHeight = size.height - ( paddingHeight + borderHeight );
1250
1251  size.outerWidth = size.width + marginWidth;
1252  size.outerHeight = size.height + marginHeight;
1253
1254  return size;
1255}
1256
1257// IE8 returns percent values, not pixels
1258// taken from jQuery's curCSS
1259function mungeNonPixel( elem, value ) {
1260  // IE8 and has percent value
1261  if ( window.getComputedStyle || value.indexOf('%') === -1 ) {
1262    return value;
1263  }
1264  var style = elem.style;
1265  // Remember the original values
1266  var left = style.left;
1267  var rs = elem.runtimeStyle;
1268  var rsLeft = rs && rs.left;
1269
1270  // Put in the new values to get a computed value out
1271  if ( rsLeft ) {
1272    rs.left = elem.currentStyle.left;
1273  }
1274  style.left = value;
1275  value = style.pixelLeft;
1276
1277  // Revert the changed values
1278  style.left = left;
1279  if ( rsLeft ) {
1280    rs.left = rsLeft;
1281  }
1282
1283  return value;
1284}
1285
1286return getSize;
1287
1288}
1289
1290// transport
1291if ( typeof define === 'function' && define.amd ) {
1292  // AMD for RequireJS
1293  define( 'get-size/get-size',[ 'get-style-property/get-style-property' ], defineGetSize );
1294} else if ( typeof exports === 'object' ) {
1295  // CommonJS for Component
1296  module.exports = defineGetSize( require('desandro-get-style-property') );
1297} else {
1298  // browser global
1299  window.getSize = defineGetSize( window.getStyleProperty );
1300}
1301
1302})( window );
1303
1304/*!
1305 * docReady v1.0.4
1306 * Cross browser DOMContentLoaded event emitter
1307 * MIT license
1308 */
1309
1310/*jshint browser: true, strict: true, undef: true, unused: true*/
1311/*global define: false, require: false, module: false */
1312
1313( function( window ) {
1314
1315
1316
1317var document = window.document;
1318// collection of functions to be triggered on ready
1319var queue = [];
1320
1321function docReady( fn ) {
1322  // throw out non-functions
1323  if ( typeof fn !== 'function' ) {
1324    return;
1325  }
1326
1327  if ( docReady.isReady ) {
1328    // ready now, hit it
1329    fn();
1330  } else {
1331    // queue function when ready
1332    queue.push( fn );
1333  }
1334}
1335
1336docReady.isReady = false;
1337
1338// triggered on various doc ready events
1339function onReady( event ) {
1340  // bail if already triggered or IE8 document is not ready just yet
1341  var isIE8NotReady = event.type === 'readystatechange' && document.readyState !== 'complete';
1342  if ( docReady.isReady || isIE8NotReady ) {
1343    return;
1344  }
1345
1346  trigger();
1347}
1348
1349function trigger() {
1350  docReady.isReady = true;
1351  // process queue
1352  for ( var i=0, len = queue.length; i < len; i++ ) {
1353    var fn = queue[i];
1354    fn();
1355  }
1356}
1357
1358function defineDocReady( eventie ) {
1359  // trigger ready if page is ready
1360  if ( document.readyState === 'complete' ) {
1361    trigger();
1362  } else {
1363    // listen for events
1364    eventie.bind( document, 'DOMContentLoaded', onReady );
1365    eventie.bind( document, 'readystatechange', onReady );
1366    eventie.bind( window, 'load', onReady );
1367  }
1368
1369  return docReady;
1370}
1371
1372// transport
1373if ( typeof define === 'function' && define.amd ) {
1374  // AMD
1375  define( 'doc-ready/doc-ready',[ 'eventie/eventie' ], defineDocReady );
1376} else if ( typeof exports === 'object' ) {
1377  module.exports = defineDocReady( require('eventie') );
1378} else {
1379  // browser global
1380  window.docReady = defineDocReady( window.eventie );
1381}
1382
1383})( window );
1384
1385/**
1386 * matchesSelector v1.0.3
1387 * matchesSelector( element, '.selector' )
1388 * MIT license
1389 */
1390
1391/*jshint browser: true, strict: true, undef: true, unused: true */
1392/*global define: false, module: false */
1393
1394( function( ElemProto ) {
1395
1396  'use strict';
1397
1398  var matchesMethod = ( function() {
1399    // check for the standard method name first
1400    if ( ElemProto.matches ) {
1401      return 'matches';
1402    }
1403    // check un-prefixed
1404    if ( ElemProto.matchesSelector ) {
1405      return 'matchesSelector';
1406    }
1407    // check vendor prefixes
1408    var prefixes = [ 'webkit', 'moz', 'ms', 'o' ];
1409
1410    for ( var i=0, len = prefixes.length; i < len; i++ ) {
1411      var prefix = prefixes[i];
1412      var method = prefix + 'MatchesSelector';
1413      if ( ElemProto[ method ] ) {
1414        return method;
1415      }
1416    }
1417  })();
1418
1419  // ----- match ----- //
1420
1421  function match( elem, selector ) {
1422    return elem[ matchesMethod ]( selector );
1423  }
1424
1425  // ----- appendToFragment ----- //
1426
1427  function checkParent( elem ) {
1428    // not needed if already has parent
1429    if ( elem.parentNode ) {
1430      return;
1431    }
1432    var fragment = document.createDocumentFragment();
1433    fragment.appendChild( elem );
1434  }
1435
1436  // ----- query ----- //
1437
1438  // fall back to using QSA
1439  // thx @jonathantneal https://gist.github.com/3062955
1440  function query( elem, selector ) {
1441    // append to fragment if no parent
1442    checkParent( elem );
1443
1444    // match elem with all selected elems of parent
1445    var elems = elem.parentNode.querySelectorAll( selector );
1446    for ( var i=0, len = elems.length; i < len; i++ ) {
1447      // return true if match
1448      if ( elems[i] === elem ) {
1449        return true;
1450      }
1451    }
1452    // otherwise return false
1453    return false;
1454  }
1455
1456  // ----- matchChild ----- //
1457
1458  function matchChild( elem, selector ) {
1459    checkParent( elem );
1460    return match( elem, selector );
1461  }
1462
1463  // ----- matchesSelector ----- //
1464
1465  var matchesSelector;
1466
1467  if ( matchesMethod ) {
1468    // IE9 supports matchesSelector, but doesn't work on orphaned elems
1469    // check for that
1470    var div = document.createElement('div');
1471    var supportsOrphans = match( div, 'div' );
1472    matchesSelector = supportsOrphans ? match : matchChild;
1473  } else {
1474    matchesSelector = query;
1475  }
1476
1477  // transport
1478  if ( typeof define === 'function' && define.amd ) {
1479    // AMD
1480    define( 'matches-selector/matches-selector',[],function() {
1481      return matchesSelector;
1482    });
1483  } else if ( typeof exports === 'object' ) {
1484    module.exports = matchesSelector;
1485  }
1486  else {
1487    // browser global
1488    window.matchesSelector = matchesSelector;
1489  }
1490
1491})( Element.prototype );
1492
1493/**
1494 * Fizzy UI utils v1.0.1
1495 * MIT license
1496 */
1497
1498/*jshint browser: true, undef: true, unused: true, strict: true */
1499
1500( function( window, factory ) {
1501  /*global define: false, module: false, require: false */
1502  'use strict';
1503  // universal module definition
1504
1505  if ( typeof define == 'function' && define.amd ) {
1506    // AMD
1507    define( 'fizzy-ui-utils/utils',[
1508      'doc-ready/doc-ready',
1509      'matches-selector/matches-selector'
1510    ], function( docReady, matchesSelector ) {
1511      return factory( window, docReady, matchesSelector );
1512    });
1513  } else if ( typeof exports == 'object' ) {
1514    // CommonJS
1515    module.exports = factory(
1516      window,
1517      require('doc-ready'),
1518      require('desandro-matches-selector')
1519    );
1520  } else {
1521    // browser global
1522    window.fizzyUIUtils = factory(
1523      window,
1524      window.docReady,
1525      window.matchesSelector
1526    );
1527  }
1528
1529}( window, function factory( window, docReady, matchesSelector ) {
1530
1531
1532
1533var utils = {};
1534
1535// ----- extend ----- //
1536
1537// extends objects
1538utils.extend = function( a, b ) {
1539  for ( var prop in b ) {
1540    a[ prop ] = b[ prop ];
1541  }
1542  return a;
1543};
1544
1545// ----- modulo ----- //
1546
1547utils.modulo = function( num, div ) {
1548  return ( ( num % div ) + div ) % div;
1549};
1550
1551// ----- isArray ----- //
1552  
1553var objToString = Object.prototype.toString;
1554utils.isArray = function( obj ) {
1555  return objToString.call( obj ) == '[object Array]';
1556};
1557
1558// ----- makeArray ----- //
1559
1560// turn element or nodeList into an array
1561utils.makeArray = function( obj ) {
1562  var ary = [];
1563  if ( utils.isArray( obj ) ) {
1564    // use object if already an array
1565    ary = obj;
1566  } else if ( obj && typeof obj.length == 'number' ) {
1567    // convert nodeList to array
1568    for ( var i=0, len = obj.length; i < len; i++ ) {
1569      ary.push( obj[i] );
1570    }
1571  } else {
1572    // array of single index
1573    ary.push( obj );
1574  }
1575  return ary;
1576};
1577
1578// ----- indexOf ----- //
1579
1580// index of helper cause IE8
1581utils.indexOf = Array.prototype.indexOf ? function( ary, obj ) {
1582    return ary.indexOf( obj );
1583  } : function( ary, obj ) {
1584    for ( var i=0, len = ary.length; i < len; i++ ) {
1585      if ( ary[i] === obj ) {
1586        return i;
1587      }
1588    }
1589    return -1;
1590  };
1591
1592// ----- removeFrom ----- //
1593
1594utils.removeFrom = function( ary, obj ) {
1595  var index = utils.indexOf( ary, obj );
1596  if ( index != -1 ) {
1597    ary.splice( index, 1 );
1598  }
1599};
1600
1601// ----- isElement ----- //
1602
1603// http://stackoverflow.com/a/384380/182183
1604utils.isElement = ( typeof HTMLElement == 'function' || typeof HTMLElement == 'object' ) ?
1605  function isElementDOM2( obj ) {
1606    return obj instanceof HTMLElement;
1607  } :
1608  function isElementQuirky( obj ) {
1609    return obj && typeof obj == 'object' &&
1610      obj.nodeType == 1 && typeof obj.nodeName == 'string';
1611  };
1612
1613// ----- setText ----- //
1614
1615utils.setText = ( function() {
1616  var setTextProperty;
1617  function setText( elem, text ) {
1618    // only check setTextProperty once
1619    setTextProperty = setTextProperty || ( document.documentElement.textContent !== undefined ? 'textContent' : 'innerText' );
1620    elem[ setTextProperty ] = text;
1621  }
1622  return setText;
1623})();
1624
1625// ----- getParent ----- //
1626
1627utils.getParent = function( elem, selector ) {
1628  while ( elem != document.body ) {
1629    elem = elem.parentNode;
1630    if ( matchesSelector( elem, selector ) ) {
1631      return elem;
1632    }
1633  }
1634};
1635
1636// ----- getQueryElement ----- //
1637
1638// use element as selector string
1639utils.getQueryElement = function( elem ) {
1640  if ( typeof elem == 'string' ) {
1641    return document.querySelector( elem );
1642  }
1643  return elem;
1644};
1645
1646// ----- handleEvent ----- //
1647
1648// enable .ontype to trigger from .addEventListener( elem, 'type' )
1649utils.handleEvent = function( event ) {
1650  var method = 'on' + event.type;
1651  if ( this[ method ] ) {
1652    this[ method ]( event );
1653  }
1654};
1655
1656// ----- filterFindElements ----- //
1657
1658utils.filterFindElements = function( elems, selector ) {
1659  // make array of elems
1660  elems = utils.makeArray( elems );
1661  var ffElems = [];
1662
1663  for ( var i=0, len = elems.length; i < len; i++ ) {
1664    var elem = elems[i];
1665    // check that elem is an actual element
1666    if ( !utils.isElement( elem ) ) {
1667      continue;
1668    }
1669    // filter & find items if we have a selector
1670    if ( selector ) {
1671      // filter siblings
1672      if ( matchesSelector( elem, selector ) ) {
1673        ffElems.push( elem );
1674      }
1675      // find children
1676      var childElems = elem.querySelectorAll( selector );
1677      // concat childElems to filterFound array
1678      for ( var j=0, jLen = childElems.length; j < jLen; j++ ) {
1679        ffElems.push( childElems[j] );
1680      }
1681    } else {
1682      ffElems.push( elem );
1683    }
1684  }
1685
1686  return ffElems;
1687};
1688
1689// ----- debounceMethod ----- //
1690
1691utils.debounceMethod = function( _class, methodName, threshold ) {
1692  // original method
1693  var method = _class.prototype[ methodName ];
1694  var timeoutName = methodName + 'Timeout';
1695
1696  _class.prototype[ methodName ] = function() {
1697    var timeout = this[ timeoutName ];
1698    if ( timeout ) {
1699      clearTimeout( timeout );
1700    }
1701    var args = arguments;
1702
1703    var _this = this;
1704    this[ timeoutName ] = setTimeout( function() {
1705      method.apply( _this, args );
1706      delete _this[ timeoutName ];
1707    }, threshold || 100 );
1708  };
1709};
1710
1711// ----- htmlInit ----- //
1712
1713// http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
1714utils.toDashed = function( str ) {
1715  return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
1716    return $1 + '-' + $2;
1717  }).toLowerCase();
1718};
1719
1720var console = window.console;
1721/**
1722 * allow user to initialize classes via .js-namespace class
1723 * htmlInit( Widget, 'widgetName' )
1724 * options are parsed from data-namespace-option attribute
1725 */
1726utils.htmlInit = function( WidgetClass, namespace ) {
1727  docReady( function() {
1728    var dashedNamespace = utils.toDashed( namespace );
1729    var elems = document.querySelectorAll( '.js-' + dashedNamespace );
1730    var dataAttr = 'data-' + dashedNamespace + '-options';
1731
1732    for ( var i=0, len = elems.length; i < len; i++ ) {
1733      var elem = elems[i];
1734      var attr = elem.getAttribute( dataAttr );
1735      var options;
1736      try {
1737        options = attr && JSON.parse( attr );
1738      } catch ( error ) {
1739        // log error, do not initialize
1740        if ( console ) {
1741          console.error( 'Error parsing ' + dataAttr + ' on ' +
1742            elem.nodeName.toLowerCase() + ( elem.id ? '#' + elem.id : '' ) + ': ' +
1743            error );
1744        }
1745        continue;
1746      }
1747      // initialize
1748      var instance = new WidgetClass( elem, options );
1749      // make available via $().data('layoutname')
1750      var jQuery = window.jQuery;
1751      if ( jQuery ) {
1752        jQuery.data( elem, namespace, instance );
1753      }
1754    }
1755  });
1756};
1757
1758// -----  ----- //
1759
1760return utils;
1761
1762}));
1763
1764/**
1765 * Outlayer Item
1766 */
1767
1768( function( window, factory ) {
1769  'use strict';
1770  // universal module definition
1771  if ( typeof define === 'function' && define.amd ) {
1772    // AMD
1773    define( 'outlayer/item',[
1774        'eventEmitter/EventEmitter',
1775        'get-size/get-size',
1776        'get-style-property/get-style-property',
1777        'fizzy-ui-utils/utils'
1778      ],
1779      function( EventEmitter, getSize, getStyleProperty, utils ) {
1780        return factory( window, EventEmitter, getSize, getStyleProperty, utils );
1781      }
1782    );
1783  } else if (typeof exports === 'object') {
1784    // CommonJS
1785    module.exports = factory(
1786      window,
1787      require('wolfy87-eventemitter'),
1788      require('get-size'),
1789      require('desandro-get-style-property'),
1790      require('fizzy-ui-utils')
1791    );
1792  } else {
1793    // browser global
1794    window.Outlayer = {};
1795    window.Outlayer.Item = factory(
1796      window,
1797      window.EventEmitter,
1798      window.getSize,
1799      window.getStyleProperty,
1800      window.fizzyUIUtils
1801    );
1802  }
1803
1804}( window, function factory( window, EventEmitter, getSize, getStyleProperty, utils ) {
1805'use strict';
1806
1807// ----- helpers ----- //
1808
1809var getComputedStyle = window.getComputedStyle;
1810var getStyle = getComputedStyle ?
1811  function( elem ) {
1812    return getComputedStyle( elem, null );
1813  } :
1814  function( elem ) {
1815    return elem.currentStyle;
1816  };
1817
1818
1819function isEmptyObj( obj ) {
1820  for ( var prop in obj ) {
1821    return false;
1822  }
1823  prop = null;
1824  return true;
1825}
1826
1827// -------------------------- CSS3 support -------------------------- //
1828
1829var transitionProperty = getStyleProperty('transition');
1830var transformProperty = getStyleProperty('transform');
1831var supportsCSS3 = transitionProperty && transformProperty;
1832var is3d = !!getStyleProperty('perspective');
1833
1834var transitionEndEvent = {
1835  WebkitTransition: 'webkitTransitionEnd',
1836  MozTransition: 'transitionend',
1837  OTransition: 'otransitionend',
1838  transition: 'transitionend'
1839}[ transitionProperty ];
1840
1841// properties that could have vendor prefix
1842var prefixableProperties = [
1843  'transform',
1844  'transition',
1845  'transitionDuration',
1846  'transitionProperty'
1847];
1848
1849// cache all vendor properties
1850var vendorProperties = ( function() {
1851  var cache = {};
1852  for ( var i=0, len = prefixableProperties.length; i < len; i++ ) {
1853    var prop = prefixableProperties[i];
1854    var supportedProp = getStyleProperty( prop );
1855    if ( supportedProp && supportedProp !== prop ) {
1856      cache[ prop ] = supportedProp;
1857    }
1858  }
1859  return cache;
1860})();
1861
1862// -------------------------- Item -------------------------- //
1863
1864function Item( element, layout ) {
1865  if ( !element ) {
1866    return;
1867  }
1868
1869  this.element = element;
1870  // parent layout class, i.e. Masonry, Isotope, or Packery
1871  this.layout = layout;
1872  this.position = {
1873    x: 0,
1874    y: 0
1875  };
1876
1877  this._create();
1878}
1879
1880// inherit EventEmitter
1881utils.extend( Item.prototype, EventEmitter.prototype );
1882
1883Item.prototype._create = function() {
1884  // transition objects
1885  this._transn = {
1886    ingProperties: {},
1887    clean: {},
1888    onEnd: {}
1889  };
1890
1891  this.css({
1892    position: 'absolute'
1893  });
1894};
1895
1896// trigger specified handler for event type
1897Item.prototype.handleEvent = function( event ) {
1898  var method = 'on' + event.type;
1899  if ( this[ method ] ) {
1900    this[ method ]( event );
1901  }
1902};
1903
1904Item.prototype.getSize = function() {
1905  this.size = getSize( this.element );
1906};
1907
1908/**
1909 * apply CSS styles to element
1910 * @param {Object} style
1911 */
1912Item.prototype.css = function( style ) {
1913  var elemStyle = this.element.style;
1914
1915  for ( var prop in style ) {
1916    // use vendor property if available
1917    var supportedProp = vendorProperties[ prop ] || prop;
1918    elemStyle[ supportedProp ] = style[ prop ];
1919  }
1920};
1921
1922 // measure position, and sets it
1923Item.prototype.getPosition = function() {
1924  var style = getStyle( this.element );
1925  var layoutOptions = this.layout.options;
1926  var isOriginLeft = layoutOptions.isOriginLeft;
1927  var isOriginTop = layoutOptions.isOriginTop;
1928  var xValue = style[ isOriginLeft ? 'left' : 'right' ];
1929  var yValue = style[ isOriginTop ? 'top' : 'bottom' ];
1930  // convert percent to pixels
1931  var layoutSize = this.layout.size;
1932  var x = xValue.indexOf('%') != -1 ?
1933    ( parseFloat( xValue ) / 100 ) * layoutSize.width : parseInt( xValue, 10 );
1934  var y = yValue.indexOf('%') != -1 ?
1935    ( parseFloat( yValue ) / 100 ) * layoutSize.height : parseInt( yValue, 10 );
1936
1937  // clean up 'auto' or other non-integer values
1938  x = isNaN( x ) ? 0 : x;
1939  y = isNaN( y ) ? 0 : y;
1940  // remove padding from measurement
1941  x -= isOriginLeft ? layoutSize.paddingLeft : layoutSize.paddingRight;
1942  y -= isOriginTop ? layoutSize.paddingTop : layoutSize.paddingBottom;
1943
1944  this.position.x = x;
1945  this.position.y = y;
1946};
1947
1948// set settled position, apply padding
1949Item.prototype.layoutPosition = function() {
1950  var layoutSize = this.layout.size;
1951  var layoutOptions = this.layout.options;
1952  var style = {};
1953
1954  // x
1955  var xPadding = layoutOptions.isOriginLeft ? 'paddingLeft' : 'paddingRight';
1956  var xProperty = layoutOptions.isOriginLeft ? 'left' : 'right';
1957  var xResetProperty = layoutOptions.isOriginLeft ? 'right' : 'left';
1958
1959  var x = this.position.x + layoutSize[ xPadding ];
1960  // set in percentage or pixels
1961  style[ xProperty ] = this.getXValue( x );
1962  // reset other property
1963  style[ xResetProperty ] = '';
1964
1965  // y
1966  var yPadding = layoutOptions.isOriginTop ? 'paddingTop' : 'paddingBottom';
1967  var yProperty = layoutOptions.isOriginTop ? 'top' : 'bottom';
1968  var yResetProperty = layoutOptions.isOriginTop ? 'bottom' : 'top';
1969
1970  var y = this.position.y + layoutSize[ yPadding ];
1971  // set in percentage or pixels
1972  style[ yProperty ] = this.getYValue( y );
1973  // reset other property
1974  style[ yResetProperty ] = '';
1975
1976  this.css( style );
1977  this.emitEvent( 'layout', [ this ] );
1978};
1979
1980Item.prototype.getXValue = function( x ) {
1981  var layoutOptions = this.layout.options;
1982  return layoutOptions.percentPosition && !layoutOptions.isHorizontal ?
1983    ( ( x / this.layout.size.width ) * 100 ) + '%' : x + 'px';
1984};
1985
1986Item.prototype.getYValue = function( y ) {
1987  var layoutOptions = this.layout.options;
1988  return layoutOptions.percentPosition && layoutOptions.isHorizontal ?
1989    ( ( y / this.layout.size.height ) * 100 ) + '%' : y + 'px';
1990};
1991
1992
1993Item.prototype._transitionTo = function( x, y ) {
1994  this.getPosition();
1995  // get current x & y from top/left
1996  var curX = this.position.x;
1997  var curY = this.position.y;
1998
1999  var compareX = parseInt( x, 10 );
2000  var compareY = parseInt( y, 10 );
2001  var didNotMove = compareX === this.position.x && compareY === this.position.y;
2002
2003  // save end position
2004  this.setPosition( x, y );
2005
2006  // if did not move and not transitioning, just go to layout
2007  if ( didNotMove && !this.isTransitioning ) {
2008    this.layoutPosition();
2009    return;
2010  }
2011
2012  var transX = x - curX;
2013  var transY = y - curY;
2014  var transitionStyle = {};
2015  transitionStyle.transform = this.getTranslate( transX, transY );
2016
2017  this.transition({
2018    to: transitionStyle,
2019    onTransitionEnd: {
2020      transform: this.layoutPosition
2021    },
2022    isCleaning: true
2023  });
2024};
2025
2026Item.prototype.getTranslate = function( x, y ) {
2027  // flip cooridinates if origin on right or bottom
2028  var layoutOptions = this.layout.options;
2029  x = layoutOptions.isOriginLeft ? x : -x;
2030  y = layoutOptions.isOriginTop ? y : -y;
2031
2032  if ( is3d ) {
2033    return 'translate3d(' + x + 'px, ' + y + 'px, 0)';
2034  }
2035
2036  return 'translate(' + x + 'px, ' + y + 'px)';
2037};
2038
2039// non transition + transform support
2040Item.prototype.goTo = function( x, y ) {
2041  this.setPosition( x, y );
2042  this.layoutPosition();
2043};
2044
2045// use transition and transforms if supported
2046Item.prototype.moveTo = supportsCSS3 ?
2047  Item.prototype._transitionTo : Item.prototype.goTo;
2048
2049Item.prototype.setPosition = function( x, y ) {
2050  this.position.x = parseInt( x, 10 );
2051  this.position.y = parseInt( y, 10 );
2052};
2053
2054// ----- transition ----- //
2055
2056/**
2057 * @param {Object} style - CSS
2058 * @param {Function} onTransitionEnd
2059 */
2060
2061// non transition, just trigger callback
2062Item.prototype._nonTransition = function( args ) {
2063  this.css( args.to );
2064  if ( args.isCleaning ) {
2065    this._removeStyles( args.to );
2066  }
2067  for ( var prop in args.onTransitionEnd ) {
2068    args.onTransitionEnd[ prop ].call( this );
2069  }
2070};
2071
2072/**
2073 * proper transition
2074 * @param {Object} args - arguments
2075 *   @param {Object} to - style to transition to
2076 *   @param {Object} from - style to start transition from
2077 *   @param {Boolean} isCleaning - removes transition styles after transition
2078 *   @param {Function} onTransitionEnd - callback
2079 */
2080Item.prototype._transition = function( args ) {
2081  // redirect to nonTransition if no transition duration
2082  if ( !parseFloat( this.layout.options.transitionDuration ) ) {
2083    this._nonTransition( args );
2084    return;
2085  }
2086
2087  var _transition = this._transn;
2088  // keep track of onTransitionEnd callback by css property
2089  for ( var prop in args.onTransitionEnd ) {
2090    _transition.onEnd[ prop ] = args.onTransitionEnd[ prop ];
2091  }
2092  // keep track of properties that are transitioning
2093  for ( prop in args.to ) {
2094    _transition.ingProperties[ prop ] = true;
2095    // keep track of properties to clean up when transition is done
2096    if ( args.isCleaning ) {
2097      _transition.clean[ prop ] = true;
2098    }
2099  }
2100
2101  // set from styles
2102  if ( args.from ) {
2103    this.css( args.from );
2104    // force redraw. http://blog.alexmaccaw.com/css-transitions
2105    var h = this.element.offsetHeight;
2106    // hack for JSHint to hush about unused var
2107    h = null;
2108  }
2109  // enable transition
2110  this.enableTransition( args.to );
2111  // set styles that are transitioning
2112  this.css( args.to );
2113
2114  this.isTransitioning = true;
2115
2116};
2117
2118// dash before all cap letters, including first for
2119// WebkitTransform => -webkit-transform
2120function toDashedAll( str ) {
2121  return str.replace( /([A-Z])/g, function( $1 ) {
2122    return '-' + $1.toLowerCase();
2123  });
2124}
2125
2126var transitionProps = 'opacity,' +
vendor: 11,190 bytes, lines 2127-2573
2127  toDashedAll( vendorProperties.transform || 'transform' );
2128
2129Item.prototype.enableTransition = function(/* style */) {
2130  // HACK changing transitionProperty during a transition
2131  // will cause transition to jump
2132  if ( this.isTransitioning ) {
2133    return;
2134  }
2135
2136  // make `transition: foo, bar, baz` from style object
2137  // HACK un-comment this when enableTransition can work
2138  // while a transition is happening
2139  // var transitionValues = [];
2140  // for ( var prop in style ) {
2141  //   // dash-ify camelCased properties like WebkitTransition
2142  //   prop = vendorProperties[ prop ] || prop;
2143  //   transitionValues.push( toDashedAll( prop ) );
2144  // }
2145  // enable transition styles
2146  this.css({
2147    transitionProperty: transitionProps,
2148    transitionDuration: this.layout.options.transitionDuration
2149  });
2150  // listen for transition end event
2151  this.element.addEventListener( transitionEndEvent, this, false );
2152};
2153
2154Item.prototype.transition = Item.prototype[ transitionProperty ? '_transition' : '_nonTransition' ];
2155
2156// ----- events ----- //
2157
2158Item.prototype.onwebkitTransitionEnd = function( event ) {
2159  this.ontransitionend( event );
2160};
2161
2162Item.prototype.onotransitionend = function( event ) {
2163  this.ontransitionend( event );
2164};
2165
2166// properties that I munge to make my life easier
2167var dashedVendorProperties = {
2168  '-webkit-transform': 'transform',
2169  '-moz-transform': 'transform',
2170  '-o-transform': 'transform'
2171};
2172
2173Item.prototype.ontransitionend = function( event ) {
2174  // disregard bubbled events from children
2175  if ( event.target !== this.element ) {
2176    return;
2177  }
2178  var _transition = this._transn;
2179  // get property name of transitioned property, convert to prefix-free
2180  var propertyName = dashedVendorProperties[ event.propertyName ] || event.propertyName;
2181
2182  // remove property that has completed transitioning
2183  delete _transition.ingProperties[ propertyName ];
2184  // check if any properties are still transitioning
2185  if ( isEmptyObj( _transition.ingProperties ) ) {
2186    // all properties have completed transitioning
2187    this.disableTransition();
2188  }
2189  // clean style
2190  if ( propertyName in _transition.clean ) {
2191    // clean up style
2192    this.element.style[ event.propertyName ] = '';
2193    delete _transition.clean[ propertyName ];
2194  }
2195  // trigger onTransitionEnd callback
2196  if ( propertyName in _transition.onEnd ) {
2197    var onTransitionEnd = _transition.onEnd[ propertyName ];
2198    onTransitionEnd.call( this );
2199    delete _transition.onEnd[ propertyName ];
2200  }
2201
2202  this.emitEvent( 'transitionEnd', [ this ] );
2203};
2204
2205Item.prototype.disableTransition = function() {
2206  this.removeTransitionStyles();
2207  this.element.removeEventListener( transitionEndEvent, this, false );
2208  this.isTransitioning = false;
2209};
2210
2211/**
2212 * removes style property from element
2213 * @param {Object} style
2214**/
2215Item.prototype._removeStyles = function( style ) {
2216  // clean up transition styles
2217  var cleanStyle = {};
2218  for ( var prop in style ) {
2219    cleanStyle[ prop ] = '';
2220  }
2221  this.css( cleanStyle );
2222};
2223
2224var cleanTransitionStyle = {
2225  transitionProperty: '',
2226  transitionDuration: ''
2227};
2228
2229Item.prototype.removeTransitionStyles = function() {
2230  // remove transition
2231  this.css( cleanTransitionStyle );
2232};
2233
2234// ----- show/hide/remove ----- //
2235
2236// remove element from DOM
2237Item.prototype.removeElem = function() {
2238  this.element.parentNode.removeChild( this.element );
2239  // remove display: none
2240  this.css({ display: '' });
2241  this.emitEvent( 'remove', [ this ] );
2242};
2243
2244Item.prototype.remove = function() {
2245  // just remove element if no transition support or no transition
2246  if ( !transitionProperty || !parseFloat( this.layout.options.transitionDuration ) ) {
2247    this.removeElem();
2248    return;
2249  }
2250
2251  // start transition
2252  var _this = this;
2253  this.once( 'transitionEnd', function() {
2254    _this.removeElem();
2255  });
2256  this.hide();
2257};
2258
2259Item.prototype.reveal = function() {
2260  delete this.isHidden;
2261  // remove display: none
2262  this.css({ display: '' });
2263
2264  var options = this.layout.options;
2265
2266  var onTransitionEnd = {};
2267  var transitionEndProperty = this.getHideRevealTransitionEndProperty('visibleStyle');
2268  onTransitionEnd[ transitionEndProperty ] = this.onRevealTransitionEnd;
2269
2270  this.transition({
2271    from: options.hiddenStyle,
2272    to: options.visibleStyle,
2273    isCleaning: true,
2274    onTransitionEnd: onTransitionEnd
2275  });
2276};
2277
2278Item.prototype.onRevealTransitionEnd = function() {
2279  // check if still visible
2280  // during transition, item may have been hidden
2281  if ( !this.isHidden ) {
2282    this.emitEvent('reveal');
2283  }
2284};
2285
2286/**
2287 * get style property use for hide/reveal transition end
2288 * @param {String} styleProperty - hiddenStyle/visibleStyle
2289 * @returns {String}
2290 */
2291Item.prototype.getHideRevealTransitionEndProperty = function( styleProperty ) {
2292  var optionStyle = this.layout.options[ styleProperty ];
2293  // use opacity
2294  if ( optionStyle.opacity ) {
2295    return 'opacity';
2296  }
2297  // get first property
2298  for ( var prop in optionStyle ) {
2299    return prop;
2300  }
2301};
2302
2303Item.prototype.hide = function() {
2304  // set flag
2305  this.isHidden = true;
2306  // remove display: none
2307  this.css({ display: '' });
2308
2309  var options = this.layout.options;
2310
2311  var onTransitionEnd = {};
2312  var transitionEndProperty = this.getHideRevealTransitionEndProperty('hiddenStyle');
2313  onTransitionEnd[ transitionEndProperty ] = this.onHideTransitionEnd;
2314
2315  this.transition({
2316    from: options.visibleStyle,
2317    to: options.hiddenStyle,
2318    // keep hidden stuff hidden
2319    isCleaning: true,
2320    onTransitionEnd: onTransitionEnd
2321  });
2322};
2323
2324Item.prototype.onHideTransitionEnd = function() {
2325  // check if still hidden
2326  // during transition, item may have been un-hidden
2327  if ( this.isHidden ) {
2328    this.css({ display: 'none' });
2329    this.emitEvent('hide');
2330  }
2331};
2332
2333Item.prototype.destroy = function() {
2334  this.css({
2335    position: '',
2336    left: '',
2337    right: '',
2338    top: '',
2339    bottom: '',
2340    transition: '',
2341    transform: ''
2342  });
2343};
2344
2345return Item;
2346
2347}));
2348
2349/*!
2350 * Outlayer v1.4.2
2351 * the brains and guts of a layout library
2352 * MIT license
2353 */
2354
2355( function( window, factory ) {
2356  'use strict';
2357  // universal module definition
2358
2359  if ( typeof define == 'function' && define.amd ) {
2360    // AMD
2361    define( 'outlayer/outlayer',[
2362        'eventie/eventie',
2363        'eventEmitter/EventEmitter',
2364        'get-size/get-size',
2365        'fizzy-ui-utils/utils',
2366        './item'
2367      ],
2368      function( eventie, EventEmitter, getSize, utils, Item ) {
2369        return factory( window, eventie, EventEmitter, getSize, utils, Item);
2370      }
2371    );
2372  } else if ( typeof exports == 'object' ) {
2373    // CommonJS
2374    module.exports = factory(
2375      window,
2376      require('eventie'),
2377      require('wolfy87-eventemitter'),
2378      require('get-size'),
2379      require('fizzy-ui-utils'),
2380      require('./item')
2381    );
2382  } else {
2383    // browser global
2384    window.Outlayer = factory(
2385      window,
2386      window.eventie,
2387      window.EventEmitter,
2388      window.getSize,
2389      window.fizzyUIUtils,
2390      window.Outlayer.Item
2391    );
2392  }
2393
2394}( window, function factory( window, eventie, EventEmitter, getSize, utils, Item ) {
2395'use strict';
2396
2397// ----- vars ----- //
2398
2399var console = window.console;
2400var jQuery = window.jQuery;
2401var noop = function() {};
2402
2403// -------------------------- Outlayer -------------------------- //
2404
2405// globally unique identifiers
2406var GUID = 0;
2407// internal store of all Outlayer intances
2408var instances = {};
2409
2410
2411/**
2412 * @param {Element, String} element
2413 * @param {Object} options
2414 * @constructor
2415 */
2416function Outlayer( element, options ) {
2417  var queryElement = utils.getQueryElement( element );
2418  if ( !queryElement ) {
2419    if ( console ) {
2420      console.error( 'Bad element for ' + this.constructor.namespace +
2421        ': ' + ( queryElement || element ) );
2422    }
2423    return;
2424  }
2425  this.element = queryElement;
2426  // add jQuery
2427  if ( jQuery ) {
2428    this.$element = jQuery( this.element );
2429  }
2430
2431  // options
2432  this.options = utils.extend( {}, this.constructor.defaults );
2433  this.option( options );
2434
2435  // add id for Outlayer.getFromElement
2436  var id = ++GUID;
2437  this.element.outlayerGUID = id; // expando
2438  instances[ id ] = this; // associate via id
2439
2440  // kick it off
2441  this._create();
2442
2443  if ( this.options.isInitLayout ) {
2444    this.layout();
2445  }
2446}
2447
2448// settings are for internal use only
2449Outlayer.namespace = 'outlayer';
2450Outlayer.Item = Item;
2451
2452// default options
2453Outlayer.defaults = {
2454  containerStyle: {
2455    position: 'relative'
2456  },
2457  isInitLayout: true,
2458  isOriginLeft: true,
2459  isOriginTop: true,
2460  isResizeBound: true,
2461  isResizingContainer: true,
2462  // item options
2463  transitionDuration: '0.4s',
2464  hiddenStyle: {
2465    opacity: 0,
2466    transform: 'scale(0.001)'
2467  },
2468  visibleStyle: {
2469    opacity: 1,
2470    transform: 'scale(1)'
2471  }
2472};
2473
2474// inherit EventEmitter
2475utils.extend( Outlayer.prototype, EventEmitter.prototype );
2476
2477/**
2478 * set options
2479 * @param {Object} opts
2480 */
2481Outlayer.prototype.option = function( opts ) {
2482  utils.extend( this.options, opts );
2483};
2484
2485Outlayer.prototype._create = function() {
2486  // get items from children
2487  this.reloadItems();
2488  // elements that affect layout, but are not laid out
2489  this.stamps = [];
2490  this.stamp( this.options.stamp );
2491  // set container style
2492  utils.extend( this.element.style, this.options.containerStyle );
2493
2494  // bind resize method
2495  if ( this.options.isResizeBound ) {
2496    this.bindResize();
2497  }
2498};
2499
2500// goes through all children again and gets bricks in proper order
2501Outlayer.prototype.reloadItems = function() {
2502  // collection of item elements
2503  this.items = this._itemize( this.element.children );
2504};
2505
2506
2507/**
2508 * turn elements into Outlayer.Items to be used in layout
2509 * @param {Array or NodeList or HTMLElement} elems
2510 * @returns {Array} items - collection of new Outlayer Items
2511 */
2512Outlayer.prototype._itemize = function( elems ) {
2513
2514  var itemElems = this._filterFindItemElements( elems );
2515  var Item = this.constructor.Item;
2516
2517  // create new Outlayer Items for collection
2518  var items = [];
2519  for ( var i=0, len = itemElems.length; i < len; i++ ) {
2520    var elem = itemElems[i];
2521    var item = new Item( elem, this );
2522    items.push( item );
2523  }
2524
2525  return items;
2526};
2527
2528/**
2529 * get item elements to be used in layout
2530 * @param {Array or NodeList or HTMLElement} elems
2531 * @returns {Array} items - item elements
2532 */
2533Outlayer.prototype._filterFindItemElements = function( elems ) {
2534  return utils.filterFindElements( elems, this.options.itemSelector );
2535};
2536
2537/**
2538 * getter method for getting item elements
2539 * @returns {Array} elems - collection of item elements
2540 */
2541Outlayer.prototype.getItemElements = function() {
2542  var elems = [];
2543  for ( var i=0, len = this.items.length; i < len; i++ ) {
2544    elems.push( this.items[i].element );
2545  }
2546  return elems;
2547};
2548
2549// ----- init & layout ----- //
2550
2551/**
2552 * lays out all items
2553 */
2554Outlayer.prototype.layout = function() {
2555  this._resetLayout();
2556  this._manageStamps();
2557
2558  // don't animate first layout
2559  var isInstant = this.options.isLayoutInstant !== undefined ?
2560    this.options.isLayoutInstant : !this._isLayoutInited;
2561  this.layoutItems( this.items, isInstant );
2562
2563  // flag for initalized
2564  this._isLayoutInited = true;
2565};
2566
2567// _init is alias for layout
2568Outlayer.prototype._init = Outlayer.prototype.layout;
2569
2570/**
2571 * logic before any new layout
2572 */
2573Outlayer.prototype._resetLayout = function() {
2574  this.getSize();
2575};
2576
2577
2578Outlayer.prototype.getSize = function() {
2579  this.size = getSize( this.element );
2580};
2581
2582/**
2583 * get measurement from option, for columnWidth, rowHeight, gutter
2584 * if option is String -> get element from selector string, & get size of element
2585 * if option is Element -> get size of element
2586 * else use option as a number
2587 *
2588 * @param {String} measurement
2589 * @param {String} size - width or height
2590 * @private
2591 */
2592Outlayer.prototype._getMeasurement = function( measurement, size ) {
2593  var option = this.options[ measurement ];
2594  var elem;
2595  if ( !option ) {
2596    // default to 0
2597    this[ measurement ] = 0;
2598  } else {
2599    // use option as an element
2600    if ( typeof option === 'string' ) {
2601      elem = this.element.querySelector( option );
2602    } else if ( utils.isElement( option ) ) {
2603      elem = option;
2604    }
2605    // use size of element, if element
2606    this[ measurement ] = elem ? getSize( elem )[ size ] : option;
2607  }
2608};
2609
2610/**
2611 * layout a collection of item elements
2612 * @api public
2613 */
2614Outlayer.prototype.layoutItems = function( items, isInstant ) {
2615  items = this._getItemsForLayout( items );
2616
2617  this._layoutItems( items, isInstant );
2618
2619  this._postLayout();
2620};
2621
2622/**
2623 * get the items to be laid out
2624 * you may want to skip over some items
2625 * @param {Array} items
2626 * @returns {Array} items
2627 */
2628Outlayer.prototype._getItemsForLayout = function( items ) {
2629  var layoutItems = [];
2630  for ( var i=0, len = items.length; i < len; i++ ) {
2631    var item = items[i];
2632    if ( !item.isIgnored ) {
2633      layoutItems.push( item );
2634    }
2635  }
2636  return layoutItems;
2637};
2638
2639/**
2640 * layout items
2641 * @param {Array} items
2642 * @param {Boolean} isInstant
2643 */
2644Outlayer.prototype._layoutItems = function( items, isInstant ) {
2645  this._emitCompleteOnItems( 'layout', items );
2646
2647  if ( !items || !items.length ) {
2648    // no items, emit event with empty array
2649    return;
2650  }
2651
2652  var queue = [];
2653
2654  for ( var i=0, len = items.length; i < len; i++ ) {
2655    var item = items[i];
2656    // get x/y object from method
2657    var position = this._getItemLayoutPosition( item );
2658    // enqueue
2659    position.item = item;
2660    position.isInstant = isInstant || item.isLayoutInstant;
2661    queue.push( position );
2662  }
2663
2664  this._processLayoutQueue( queue );
2665};
2666
2667/**
2668 * get item layout position
2669 * @param {Outlayer.Item} item
2670 * @returns {Object} x and y position
2671 */
2672Outlayer.prototype._getItemLayoutPosition = function( /* item */ ) {
2673  return {
2674    x: 0,
2675    y: 0
2676  };
2677};
2678
2679/**
2680 * iterate over array and position each item
2681 * Reason being - separating this logic prevents 'layout invalidation'
2682 * thx @paul_irish
2683 * @param {Array} queue
2684 */
2685Outlayer.prototype._processLayoutQueue = function( queue ) {
2686  for ( var i=0, len = queue.length; i < len; i++ ) {
2687    var obj = queue[i];
2688    this._positionItem( obj.item, obj.x, obj.y, obj.isInstant );
2689  }
2690};
2691
2692/**
2693 * Sets position of item in DOM
2694 * @param {Outlayer.Item} item
2695 * @param {Number} x - horizontal position
2696 * @param {Number} y - vertical position
2697 * @param {Boolean} isInstant - disables transitions
2698 */
2699Outlayer.prototype._positionItem = function( item, x, y, isInstant ) {
2700  if ( isInstant ) {
2701    // if not transition, just set CSS
2702    item.goTo( x, y );
2703  } else {
2704    item.moveTo( x, y );
2705  }
2706};
2707
2708/**
2709 * Any logic you want to do after each layout,
2710 * i.e. size the container
2711 */
2712Outlayer.prototype._postLayout = function() {
2713  this.resizeContainer();
2714};
2715
2716Outlayer.prototype.resizeContainer = function() {
2717  if ( !this.options.isResizingContainer ) {
2718    return;
2719  }
2720  var size = this._getContainerSize();
2721  if ( size ) {
2722    this._setContainerMeasure( size.width, true );
2723    this._setContainerMeasure( size.height, false );
2724  }
2725};
2726
2727/**
2728 * Sets width or height of container if returned
2729 * @returns {Object} size
2730 *   @param {Number} width
2731 *   @param {Number} height
2732 */
2733Outlayer.prototype._getContainerSize = noop;
2734
2735/**
2736 * @param {Number} measure - size of width or height
2737 * @param {Boolean} isWidth
2738 */
2739Outlayer.prototype._setContainerMeasure = function( measure, isWidth ) {
2740  if ( measure === undefined ) {
2741    return;
2742  }
2743
2744  var elemSize = this.size;
2745  // add padding and border width if border box
2746  if ( elemSize.isBorderBox ) {
2747    measure += isWidth ? elemSize.paddingLeft + elemSize.paddingRight +
2748      elemSize.borderLeftWidth + elemSize.borderRightWidth :
2749      elemSize.paddingBottom + elemSize.paddingTop +
2750      elemSize.borderTopWidth + elemSize.borderBottomWidth;
2751  }
2752
2753  measure = Math.max( measure, 0 );
2754  this.element.style[ isWidth ? 'width' : 'height' ] = measure + 'px';
2755};
2756
2757/**
2758 * emit eventComplete on a collection of items events
2759 * @param {String} eventName
2760 * @param {Array} items - Outlayer.Items
2761 */
2762Outlayer.prototype._emitCompleteOnItems = function( eventName, items ) {
2763  var _this = this;
2764  function onComplete() {
2765    _this.dispatchEvent( eventName + 'Complete', null, [ items ] );
2766  }
2767
2768  var count = items.length;
2769  if ( !items || !count ) {
2770    onComplete();
2771    return;
2772  }
2773
2774  var doneCount = 0;
2775  function tick() {
2776    doneCount++;
2777    if ( doneCount === count ) {
2778      onComplete();
2779    }
2780  }
2781
2782  // bind callback
2783  for ( var i=0, len = items.length; i < len; i++ ) {
2784    var item = items[i];
2785    item.once( eventName, tick );
2786  }
2787};
2788
2789/**
2790 * emits events via eventEmitter and jQuery events
2791 * @param {String} type - name of event
2792 * @param {Event} event - original event
2793 * @param {Array} args - extra arguments
2794 */
2795Outlayer.prototype.dispatchEvent = function( type, event, args ) {
2796  // add original event to arguments
2797  var emitArgs = event ? [ event ].concat( args ) : args;
2798  this.emitEvent( type, emitArgs );
2799
2800  if ( jQuery ) {
2801    // set this.$element
2802    this.$element = this.$element || jQuery( this.element );
2803    if ( event ) {
2804      // create jQuery event
2805      var $event = jQuery.Event( event );
2806      $event.type = type;
2807      this.$element.trigger( $event, args );
2808    } else {
2809      // just trigger with type if no event available
2810      this.$element.trigger( type, args );
2811    }
2812  }
2813};
2814
2815// -------------------------- ignore & stamps -------------------------- //
2816
2817
2818/**
2819 * keep item in collection, but do not lay it out
2820 * ignored items do not get skipped in layout
2821 * @param {Element} elem
2822 */
2823Outlayer.prototype.ignore = function( elem ) {
2824  var item = this.getItem( elem );
2825  if ( item ) {
2826    item.isIgnored = true;
2827  }
2828};
2829
2830/**
2831 * return item to layout collection
2832 * @param {Element} elem
2833 */
2834Outlayer.prototype.unignore = function( elem ) {
2835  var item = this.getItem( elem );
2836  if ( item ) {
2837    delete item.isIgnored;
2838  }
2839};
2840
2841/**
2842 * adds elements to stamps
2843 * @param {NodeList, Array, Element, or String} elems
2844 */
2845Outlayer.prototype.stamp = function( elems ) {
2846  elems = this._find( elems );
2847  if ( !elems ) {
2848    return;
2849  }
2850
2851  this.stamps = this.stamps.concat( elems );
2852  // ignore
2853  for ( var i=0, len = elems.length; i < len; i++ ) {
2854    var elem = elems[i];
2855    this.ignore( elem );
2856  }
2857};
2858
2859/**
2860 * removes elements to stamps
2861 * @param {NodeList, Array, or Element} elems
2862 */
2863Outlayer.prototype.unstamp = function( elems ) {
2864  elems = this._find( elems );
2865  if ( !elems ){
2866    return;
2867  }
2868
2869  for ( var i=0, len = elems.length; i < len; i++ ) {
2870    var elem = elems[i];
2871    // filter out removed stamp elements
2872    utils.removeFrom( this.stamps, elem );
2873    this.unignore( elem );
2874  }
2875
2876};
2877
2878/**
2879 * finds child elements
2880 * @param {NodeList, Array, Element, or String} elems
2881 * @returns {Array} elems
2882 */
2883Outlayer.prototype._find = function( elems ) {
2884  if ( !elems ) {
2885    return;
2886  }
2887  // if string, use argument as selector string
2888  if ( typeof elems === 'string' ) {
2889    elems = this.element.querySelectorAll( elems );
2890  }
2891  elems = utils.makeArray( elems );
2892  return elems;
2893};
2894
2895Outlayer.prototype._manageStamps = function() {
2896  if ( !this.stamps || !this.stamps.length ) {
2897    return;
2898  }
2899
2900  this._getBoundingRect();
2901
2902  for ( var i=0, len = this.stamps.length; i < len; i++ ) {
2903    var stamp = this.stamps[i];
2904    this._manageStamp( stamp );
2905  }
2906};
2907
2908// update boundingLeft / Top
2909Outlayer.prototype._getBoundingRect = function() {
2910  // get bounding rect for container element
2911  var boundingRect = this.element.getBoundingClientRect();
2912  var size = this.size;
2913  this._boundingRect = {
2914    left: boundingRect.left + size.paddingLeft + size.borderLeftWidth,
2915    top: boundingRect.top + size.paddingTop + size.borderTopWidth,
2916    right: boundingRect.right - ( size.paddingRight + size.borderRightWidth ),
2917    bottom: boundingRect.bottom - ( size.paddingBottom + size.borderBottomWidth )
2918  };
2919};
2920
2921/**
2922 * @param {Element} stamp
2923**/
2924Outlayer.prototype._manageStamp = noop;
2925
2926/**
2927 * get x/y position of element relative to container element
2928 * @param {Element} elem
2929 * @returns {Object} offset - has left, top, right, bottom
2930 */
2931Outlayer.prototype._getElementOffset = function( elem ) {
2932  var boundingRect = elem.getBoundingClientRect();
2933  var thisRect = this._boundingRect;
2934  var size = getSize( elem );
2935  var offset = {
2936    left: boundingRect.left - thisRect.left - size.marginLeft,
2937    top: boundingRect.top - thisRect.top - size.marginTop,
2938    right: thisRect.right - boundingRect.right - size.marginRight,
2939    bottom: thisRect.bottom - boundingRect.bottom - size.marginBottom
2940  };
2941  return offset;
2942};
2943
2944// -------------------------- resize -------------------------- //
2945
2946// enable event handlers for listeners
2947// i.e. resize -> onresize
2948Outlayer.prototype.handleEvent = function( event ) {
2949  var method = 'on' + event.type;
2950  if ( this[ method ] ) {
2951    this[ method ]( event );
2952  }
2953};
2954
2955/**
2956 * Bind layout to window resizing
2957 */
2958Outlayer.prototype.bindResize = function() {
2959  // bind just one listener
2960  if ( this.isResizeBound ) {
2961    return;
2962  }
2963  eventie.bind( window, 'resize', this );
2964  this.isResizeBound = true;
2965};
2966
2967/**
2968 * Unbind layout to window resizing
2969 */
2970Outlayer.prototype.unbindResize = function() {
2971  if ( this.isResizeBound ) {
2972    eventie.unbind( window, 'resize', this );
2973  }
2974  this.isResizeBound = false;
2975};
2976
2977// original debounce by John Hann
2978// http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
2979
2980// this fires every resize
2981Outlayer.prototype.onresize = function() {
2982  if ( this.resizeTimeout ) {
2983    clearTimeout( this.resizeTimeout );
2984  }
2985
2986  var _this = this;
2987  function delayed() {
2988    _this.resize();
2989    delete _this.resizeTimeout;
2990  }
2991
2992  this.resizeTimeout = setTimeout( delayed, 100 );
2993};
2994
2995// debounced, layout on resize
2996Outlayer.prototype.resize = function() {
2997  // don't trigger if size did not change
2998  // or if resize was unbound. See #9
2999  if ( !this.isResizeBound || !this.needsResizeLayout() ) {
3000    return;
3001  }
3002
3003  this.layout();
3004};
3005
3006/**
3007 * check if layout is needed post layout
3008 * @returns Boolean
3009 */
3010Outlayer.prototype.needsResizeLayout = function() {
3011  var size = getSize( this.element );
3012  // check that this.size and size are there
3013  // IE8 triggers resize on body size change, so they might not be
3014  var hasSizes = this.size && size;
3015  return hasSizes && size.innerWidth !== this.size.innerWidth;
3016};
3017
3018// -------------------------- methods -------------------------- //
3019
3020/**
3021 * add items to Outlayer instance
3022 * @param {Array or NodeList or Element} elems
3023 * @returns {Array} items - Outlayer.Items
3024**/
3025Outlayer.prototype.addItems = function( elems ) {
3026  var items = this._itemize( elems );
3027  // add items to collection
3028  if ( items.length ) {
3029    this.items = this.items.concat( items );
3030  }
3031  return items;
3032};
3033
3034/**
3035 * Layout newly-appended item elements
3036 * @param {Array or NodeList or Element} elems
3037 */
3038Outlayer.prototype.appended = function( elems ) {
3039  var items = this.addItems( elems );
3040  if ( !items.length ) {
3041    return;
3042  }
3043  // layout and reveal just the new items
3044  this.layoutItems( items, true );
3045  this.reveal( items );
3046};
3047
3048/**
3049 * Layout prepended elements
3050 * @param {Array or NodeList or Element} elems
3051 */
3052Outlayer.prototype.prepended = function( elems ) {
3053  var items = this._itemize( elems );
3054  if ( !items.length ) {
3055    return;
3056  }
3057  // add items to beginning of collection
3058  var previousItems = this.items.slice(0);
3059  this.items = items.concat( previousItems );
3060  // start new layout
3061  this._resetLayout();
3062  this._manageStamps();
3063  // layout new stuff without transition
3064  this.layoutItems( items, true );
3065  this.reveal( items );
3066  // layout previous items
3067  this.layoutItems( previousItems );
3068};
3069
3070/**
3071 * reveal a collection of items
3072 * @param {Array of Outlayer.Items} items
3073 */
3074Outlayer.prototype.reveal = function( items ) {
3075  this._emitCompleteOnItems( 'reveal', items );
3076
3077  var len = items && items.length;
3078  for ( var i=0; len && i < len; i++ ) {
3079    var item = items[i];
3080    item.reveal();
3081  }
3082};
3083
3084/**
3085 * hide a collection of items
3086 * @param {Array of Outlayer.Items} items
3087 */
3088Outlayer.prototype.hide = function( items ) {
3089  this._emitCompleteOnItems( 'hide', items );
3090
3091  var len = items && items.length;
3092  for ( var i=0; len && i < len; i++ ) {
3093    var item = items[i];
3094    item.hide();
3095  }
3096};
3097
3098/**
3099 * reveal item elements
3100 * @param {Array}, {Element}, {NodeList} items
3101 */
3102Outlayer.prototype.revealItemElements = function( elems ) {
3103  var items = this.getItems( elems );
3104  this.reveal( items );
3105};
3106
3107/**
3108 * hide item elements
3109 * @param {Array}, {Element}, {NodeList} items
3110 */
3111Outlayer.prototype.hideItemElements = function( elems ) {
3112  var items = this.getItems( elems );
3113  this.hide( items );
3114};
3115
3116/**
3117 * get Outlayer.Item, given an Element
3118 * @param {Element} elem
3119 * @param {Function} callback
3120 * @returns {Outlayer.Item} item
3121 */
3122Outlayer.prototype.getItem = function( elem ) {
3123  // loop through items to get the one that matches
3124  for ( var i=0, len = this.items.length; i < len; i++ ) {
3125    var item = this.items[i];
3126    if ( item.element === elem ) {
3127      // return item
3128      return item;
3129    }
3130  }
3131};
3132
3133/**
3134 * get collection of Outlayer.Items, given Elements
3135 * @param {Array} elems
3136 * @returns {Array} items - Outlayer.Items
3137 */
3138Outlayer.prototype.getItems = function( elems ) {
3139  elems = utils.makeArray( elems );
3140  var items = [];
3141  for ( var i=0, len = elems.length; i < len; i++ ) {
3142    var elem = elems[i];
3143    var item = this.getItem( elem );
3144    if ( item ) {
3145      items.push( item );
3146    }
3147  }
3148
3149  return items;
3150};
3151
3152/**
3153 * remove element(s) from instance and DOM
3154 * @param {Array or NodeList or Element} elems
3155 */
3156Outlayer.prototype.remove = function( elems ) {
3157  var removeItems = this.getItems( elems );
3158
3159  this._emitCompleteOnItems( 'remove', removeItems );
3160
3161  // bail if no items to remove
3162  if ( !removeItems || !removeItems.length ) {
3163    return;
3164  }
3165
3166  for ( var i=0, len = removeItems.length; i < len; i++ ) {
3167    var item = removeItems[i];
3168    item.remove();
3169    // remove item from collection
3170    utils.removeFrom( this.items, item );
3171  }
3172};
3173
3174// ----- destroy ----- //
3175
3176// remove and disable Outlayer instance
3177Outlayer.prototype.destroy = function() {
3178  // clean up dynamic styles
3179  var style = this.element.style;
3180  style.height = '';
3181  style.position = '';
3182  style.width = '';
3183  // destroy items
3184  for ( var i=0, len = this.items.length; i < len; i++ ) {
3185    var item = this.items[i];
3186    item.destroy();
3187  }
3188
3189  this.unbindResize();
3190
3191  var id = this.element.outlayerGUID;
3192  delete instances[ id ]; // remove reference to instance by id
3193  delete this.element.outlayerGUID;
3194  // remove data for jQuery
3195  if ( jQuery ) {
3196    jQuery.removeData( this.element, this.constructor.namespace );
3197  }
3198
3199};
3200
3201// -------------------------- data -------------------------- //
3202
3203/**
3204 * get Outlayer instance from element
3205 * @param {Element} elem
3206 * @returns {Outlayer}
3207 */
3208Outlayer.data = function( elem ) {
3209  elem = utils.getQueryElement( elem );
3210  var id = elem && elem.outlayerGUID;
3211  return id && instances[ id ];
3212};
3213
3214
3215// -------------------------- create Outlayer class -------------------------- //
3216
3217/**
3218 * create a layout class
3219 * @param {String} namespace
3220 */
3221Outlayer.create = function( namespace, options ) {
3222  // sub-class Outlayer
3223  function Layout() {
3224    Outlayer.apply( this, arguments );
3225  }
3226  // inherit Outlayer prototype, use Object.create if there
3227  if ( Object.create ) {
3228    Layout.prototype = Object.create( Outlayer.prototype );
3229  } else {
3230    utils.extend( Layout.prototype, Outlayer.prototype );
3231  }
3232  // set contructor, used for namespace and Item
3233  Layout.prototype.constructor = Layout;
3234
3235  Layout.defaults = utils.extend( {}, Outlayer.defaults );
3236  // apply new options
3237  utils.extend( Layout.defaults, options );
3238  // keep prototype.settings for backwards compatibility (Packery v1.2.0)
3239  Layout.prototype.settings = {};
3240
3241  Layout.namespace = namespace;
3242
3243  Layout.data = Outlayer.data;
3244
3245  // sub-class Item
3246  Layout.Item = function LayoutItem() {
3247    Item.apply( this, arguments );
3248  };
3249
3250  Layout.Item.prototype = new Item();
3251
3252  // -------------------------- declarative -------------------------- //
3253
3254  utils.htmlInit( Layout, namespace );
3255
3256  // -------------------------- jQuery bridge -------------------------- //
3257
3258  // make into jQuery plugin
3259  if ( jQuery && jQuery.bridget ) {
3260    jQuery.bridget( namespace, Layout );
3261  }
3262
3263  return Layout;
3264};
3265
3266// ----- fin ----- //
3267
3268// back in global
3269Outlayer.Item = Item;
3270
3271return Outlayer;
3272
3273}));
3274
3275
3276/**
3277 * Isotope Item
3278**/
3279
3280( function( window, factory ) {
3281'use strict';
3282  // universal module definition
3283  if ( typeof define == 'function' && define.amd ) {
3284    // AMD
3285    define( 'isotope/js/item',[
3286        'outlayer/outlayer'
3287      ],
3288      factory );
3289  } else if ( typeof exports == 'object' ) {
3290    // CommonJS
3291    module.exports = factory(
3292      require('outlayer')
3293    );
3294  } else {
3295    // browser global
3296    window.Isotope = window.Isotope || {};
3297    window.Isotope.Item = factory(
3298      window.Outlayer
3299    );
3300  }
3301
3302}( window, function factory( Outlayer ) {
3303'use strict';
3304
3305// -------------------------- Item -------------------------- //
3306
3307// sub-class Outlayer Item
3308function Item() {
3309  Outlayer.Item.apply( this, arguments );
3310}
3311
3312Item.prototype = new Outlayer.Item();
3313
3314Item.prototype._create = function() {
3315  // assign id, used for original-order sorting
3316  this.id = this.layout.itemGUID++;
3317  Outlayer.Item.prototype._create.call( this );
3318  this.sortData = {};
3319};
3320
3321Item.prototype.updateSortData = function() {
3322  if ( this.isIgnored ) {
3323    return;
3324  }
3325  // default sorters
3326  this.sortData.id = this.id;
3327  // for backward compatibility
3328  this.sortData['original-order'] = this.id;
3329  this.sortData.random = Math.random();
3330  // go thru getSortData obj and apply the sorters
3331  var getSortData = this.layout.options.getSortData;
3332  var sorters = this.layout._sorters;
3333  for ( var key in getSortData ) {
3334    var sorter = sorters[ key ];
3335    this.sortData[ key ] = sorter( this.element, this );
3336  }
3337};
3338
3339var _destroy = Item.prototype.destroy;
3340Item.prototype.destroy = function() {
3341  // call super
3342  _destroy.apply( this, arguments );
3343  // reset display, #741
3344  this.css({
3345    display: ''
3346  });
3347};
3348
3349return Item;
3350
3351}));
3352
3353/**
3354 * Isotope LayoutMode
3355 */
3356
3357( function( window, factory ) {
3358  'use strict';
3359  // universal module definition
3360
3361  if ( typeof define == 'function' && define.amd ) {
3362    // AMD
3363    define( 'isotope/js/layout-mode',[
3364        'get-size/get-size',
3365        'outlayer/outlayer'
3366      ],
3367      factory );
3368  } else if ( typeof exports == 'object' ) {
3369    // CommonJS
3370    module.exports = factory(
3371      require('get-size'),
3372      require('outlayer')
3373    );
3374  } else {
3375    // browser global
3376    window.Isotope = window.Isotope || {};
3377    window.Isotope.LayoutMode = factory(
3378      window.getSize,
3379      window.Outlayer
3380    );
3381  }
3382
3383}( window, function factory( getSize, Outlayer ) {
3384  'use strict';
3385
3386  // layout mode class
3387  function LayoutMode( isotope ) {
3388    this.isotope = isotope;
3389    // link properties
3390    if ( isotope ) {
3391      this.options = isotope.options[ this.namespace ];
3392      this.element = isotope.element;
3393      this.items = isotope.filteredItems;
3394      this.size = isotope.size;
3395    }
3396  }
3397
3398  /**
3399   * some methods should just defer to default Outlayer method
3400   * and reference the Isotope instance as `this`
3401  **/
3402  ( function() {
3403    var facadeMethods = [
3404      '_resetLayout',
3405      '_getItemLayoutPosition',
3406      '_manageStamp',
3407      '_getContainerSize',
3408      '_getElementOffset',
3409      'needsResizeLayout'
3410    ];
3411
3412    for ( var i=0, len = facadeMethods.length; i < len; i++ ) {
3413      var methodName = facadeMethods[i];
3414      LayoutMode.prototype[ methodName ] = getOutlayerMethod( methodName );
3415    }
3416
3417    function getOutlayerMethod( methodName ) {
3418      return function() {
3419        return Outlayer.prototype[ methodName ].apply( this.isotope, arguments );
3420      };
3421    }
3422  })();
3423
3424  // -----  ----- //
3425
3426  // for horizontal layout modes, check vertical size
3427  LayoutMode.prototype.needsVerticalResizeLayout = function() {
vendor: 20,029 bytes, lines 3428-4163
3428    // don't trigger if size did not change
3429    var size = getSize( this.isotope.element );
3430    // check that this.size and size are there
3431    // IE8 triggers resize on body size change, so they might not be
3432    var hasSizes = this.isotope.size && size;
3433    return hasSizes && size.innerHeight != this.isotope.size.innerHeight;
3434  };
3435
3436  // ----- measurements ----- //
3437
3438  LayoutMode.prototype._getMeasurement = function() {
3439    this.isotope._getMeasurement.apply( this, arguments );
3440  };
3441
3442  LayoutMode.prototype.getColumnWidth = function() {
3443    this.getSegmentSize( 'column', 'Width' );
3444  };
3445
3446  LayoutMode.prototype.getRowHeight = function() {
3447    this.getSegmentSize( 'row', 'Height' );
3448  };
3449
3450  /**
3451   * get columnWidth or rowHeight
3452   * segment: 'column' or 'row'
3453   * size 'Width' or 'Height'
3454  **/
3455  LayoutMode.prototype.getSegmentSize = function( segment, size ) {
3456    var segmentName = segment + size;
3457    var outerSize = 'outer' + size;
3458    // columnWidth / outerWidth // rowHeight / outerHeight
3459    this._getMeasurement( segmentName, outerSize );
3460    // got rowHeight or columnWidth, we can chill
3461    if ( this[ segmentName ] ) {
3462      return;
3463    }
3464    // fall back to item of first element
3465    var firstItemSize = this.getFirstItemSize();
3466    this[ segmentName ] = firstItemSize && firstItemSize[ outerSize ] ||
3467      // or size of container
3468      this.isotope.size[ 'inner' + size ];
3469  };
3470
3471  LayoutMode.prototype.getFirstItemSize = function() {
3472    var firstItem = this.isotope.filteredItems[0];
3473    return firstItem && firstItem.element && getSize( firstItem.element );
3474  };
3475
3476  // ----- methods that should reference isotope ----- //
3477
3478  LayoutMode.prototype.layout = function() {
3479    this.isotope.layout.apply( this.isotope, arguments );
3480  };
3481
3482  LayoutMode.prototype.getSize = function() {
3483    this.isotope.getSize();
3484    this.size = this.isotope.size;
3485  };
3486
3487  // -------------------------- create -------------------------- //
3488
3489  LayoutMode.modes = {};
3490
3491  LayoutMode.create = function( namespace, options ) {
3492
3493    function Mode() {
3494      LayoutMode.apply( this, arguments );
3495    }
3496
3497    Mode.prototype = new LayoutMode();
3498
3499    // default options
3500    if ( options ) {
3501      Mode.options = options;
3502    }
3503
3504    Mode.prototype.namespace = namespace;
3505    // register in Isotope
3506    LayoutMode.modes[ namespace ] = Mode;
3507
3508    return Mode;
3509  };
3510
3511  return LayoutMode;
3512
3513}));
3514
3515/*!
3516 * Masonry v3.3.1
3517 * Cascading grid layout library
3518 * http://masonry.desandro.com
3519 * MIT License
3520 * by David DeSandro
3521 */
3522
3523( function( window, factory ) {
3524  'use strict';
3525  // universal module definition
3526  if ( typeof define === 'function' && define.amd ) {
3527    // AMD
3528    define( 'masonry/masonry',[
3529        'outlayer/outlayer',
3530        'get-size/get-size',
3531        'fizzy-ui-utils/utils'
3532      ],
3533      factory );
3534  } else if ( typeof exports === 'object' ) {
3535    // CommonJS
3536    module.exports = factory(
3537      require('outlayer'),
3538      require('get-size'),
3539      require('fizzy-ui-utils')
3540    );
3541  } else {
3542    // browser global
3543    window.Masonry = factory(
3544      window.Outlayer,
3545      window.getSize,
3546      window.fizzyUIUtils
3547    );
3548  }
3549
3550}( window, function factory( Outlayer, getSize, utils ) {
3551
3552
3553
3554// -------------------------- masonryDefinition -------------------------- //
3555
3556  // create an Outlayer layout class
3557  var Masonry = Outlayer.create('masonry');
3558
3559  Masonry.prototype._resetLayout = function() {
3560    this.getSize();
3561    this._getMeasurement( 'columnWidth', 'outerWidth' );
3562    this._getMeasurement( 'gutter', 'outerWidth' );
3563    this.measureColumns();
3564
3565    // reset column Y
3566    var i = this.cols;
3567    this.colYs = [];
3568    while (i--) {
3569      this.colYs.push( 0 );
3570    }
3571
3572    this.maxY = 0;
3573  };
3574
3575  Masonry.prototype.measureColumns = function() {
3576    this.getContainerWidth();
3577    // if columnWidth is 0, default to outerWidth of first item
3578    if ( !this.columnWidth ) {
3579      var firstItem = this.items[0];
3580      var firstItemElem = firstItem && firstItem.element;
3581      // columnWidth fall back to item of first element
3582      this.columnWidth = firstItemElem && getSize( firstItemElem ).outerWidth ||
3583        // if first elem has no width, default to size of container
3584        this.containerWidth;
3585    }
3586
3587    var columnWidth = this.columnWidth += this.gutter;
3588
3589    // calculate columns
3590    var containerWidth = this.containerWidth + this.gutter;
3591    var cols = containerWidth / columnWidth;
3592    // fix rounding errors, typically with gutters
3593    var excess = columnWidth - containerWidth % columnWidth;
3594    // if overshoot is less than a pixel, round up, otherwise floor it
3595    var mathMethod = excess && excess < 1 ? 'round' : 'floor';
3596    cols = Math[ mathMethod ]( cols );
3597    this.cols = Math.max( cols, 1 );
3598  };
3599
3600  Masonry.prototype.getContainerWidth = function() {
3601    // container is parent if fit width
3602    var container = this.options.isFitWidth ? this.element.parentNode : this.element;
3603    // check that this.size and size are there
3604    // IE8 triggers resize on body size change, so they might not be
3605    var size = getSize( container );
3606    this.containerWidth = size && size.innerWidth;
3607  };
3608
3609  Masonry.prototype._getItemLayoutPosition = function( item ) {
3610    item.getSize();
3611    // how many columns does this brick span
3612    var remainder = item.size.outerWidth % this.columnWidth;
3613    var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
3614    // round if off by 1 pixel, otherwise use ceil
3615    var colSpan = Math[ mathMethod ]( item.size.outerWidth / this.columnWidth );
3616    colSpan = Math.min( colSpan, this.cols );
3617
3618    var colGroup = this._getColGroup( colSpan );
3619    // get the minimum Y value from the columns
3620    var minimumY = Math.min.apply( Math, colGroup );
3621    var shortColIndex = utils.indexOf( colGroup, minimumY );
3622
3623    // position the brick
3624    var position = {
3625      x: this.columnWidth * shortColIndex,
3626      y: minimumY
3627    };
3628
3629    // apply setHeight to necessary columns
3630    var setHeight = minimumY + item.size.outerHeight;
3631    var setSpan = this.cols + 1 - colGroup.length;
3632    for ( var i = 0; i < setSpan; i++ ) {
3633      this.colYs[ shortColIndex + i ] = setHeight;
3634    }
3635
3636    return position;
3637  };
3638
3639  /**
3640   * @param {Number} colSpan - number of columns the element spans
3641   * @returns {Array} colGroup
3642   */
3643  Masonry.prototype._getColGroup = function( colSpan ) {
3644    if ( colSpan < 2 ) {
3645      // if brick spans only one column, use all the column Ys
3646      return this.colYs;
3647    }
3648
3649    var colGroup = [];
3650    // how many different places could this brick fit horizontally
3651    var groupCount = this.cols + 1 - colSpan;
3652    // for each group potential horizontal position
3653    for ( var i = 0; i < groupCount; i++ ) {
3654      // make an array of colY values for that one group
3655      var groupColYs = this.colYs.slice( i, i + colSpan );
3656      // and get the max value of the array
3657      colGroup[i] = Math.max.apply( Math, groupColYs );
3658    }
3659    return colGroup;
3660  };
3661
3662  Masonry.prototype._manageStamp = function( stamp ) {
3663    var stampSize = getSize( stamp );
3664    var offset = this._getElementOffset( stamp );
3665    // get the columns that this stamp affects
3666    var firstX = this.options.isOriginLeft ? offset.left : offset.right;
3667    var lastX = firstX + stampSize.outerWidth;
3668    var firstCol = Math.floor( firstX / this.columnWidth );
3669    firstCol = Math.max( 0, firstCol );
3670    var lastCol = Math.floor( lastX / this.columnWidth );
3671    // lastCol should not go over if multiple of columnWidth #425
3672    lastCol -= lastX % this.columnWidth ? 0 : 1;
3673    lastCol = Math.min( this.cols - 1, lastCol );
3674    // set colYs to bottom of the stamp
3675    var stampMaxY = ( this.options.isOriginTop ? offset.top : offset.bottom ) +
3676      stampSize.outerHeight;
3677    for ( var i = firstCol; i <= lastCol; i++ ) {
3678      this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
3679    }
3680  };
3681
3682  Masonry.prototype._getContainerSize = function() {
3683    this.maxY = Math.max.apply( Math, this.colYs );
3684    var size = {
3685      height: this.maxY
3686    };
3687
3688    if ( this.options.isFitWidth ) {
3689      size.width = this._getContainerFitWidth();
3690    }
3691
3692    return size;
3693  };
3694
3695  Masonry.prototype._getContainerFitWidth = function() {
3696    var unusedCols = 0;
3697    // count unused columns
3698    var i = this.cols;
3699    while ( --i ) {
3700      if ( this.colYs[i] !== 0 ) {
3701        break;
3702      }
3703      unusedCols++;
3704    }
3705    // fit container to columns that have been used
3706    return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
3707  };
3708
3709  Masonry.prototype.needsResizeLayout = function() {
3710    var previousWidth = this.containerWidth;
3711    this.getContainerWidth();
3712    return previousWidth !== this.containerWidth;
3713  };
3714
3715  return Masonry;
3716
3717}));
3718
3719/*!
3720 * Masonry layout mode
3721 * sub-classes Masonry
3722 * http://masonry.desandro.com
3723 */
3724
3725( function( window, factory ) {
3726  'use strict';
3727  // universal module definition
3728  if ( typeof define == 'function' && define.amd ) {
3729    // AMD
3730    define( 'isotope/js/layout-modes/masonry',[
3731        '../layout-mode',
3732        'masonry/masonry'
3733      ],
3734      factory );
3735  } else if ( typeof exports == 'object' ) {
3736    // CommonJS
3737    module.exports = factory(
3738      require('../layout-mode'),
3739      require('masonry-layout')
3740    );
3741  } else {
3742    // browser global
3743    factory(
3744      window.Isotope.LayoutMode,
3745      window.Masonry
3746    );
3747  }
3748
3749}( window, function factory( LayoutMode, Masonry ) {
3750'use strict';
3751
3752// -------------------------- helpers -------------------------- //
3753
3754// extend objects
3755function extend( a, b ) {
3756  for ( var prop in b ) {
3757    a[ prop ] = b[ prop ];
3758  }
3759  return a;
3760}
3761
3762// -------------------------- masonryDefinition -------------------------- //
3763
3764  // create an Outlayer layout class
3765  var MasonryMode = LayoutMode.create('masonry');
3766
3767  // save on to these methods
3768  var _getElementOffset = MasonryMode.prototype._getElementOffset;
3769  var layout = MasonryMode.prototype.layout;
3770  var _getMeasurement = MasonryMode.prototype._getMeasurement;
3771
3772  // sub-class Masonry
3773  extend( MasonryMode.prototype, Masonry.prototype );
3774
3775  // set back, as it was overwritten by Masonry
3776  MasonryMode.prototype._getElementOffset = _getElementOffset;
3777  MasonryMode.prototype.layout = layout;
3778  MasonryMode.prototype._getMeasurement = _getMeasurement;
3779
3780  var measureColumns = MasonryMode.prototype.measureColumns;
3781  MasonryMode.prototype.measureColumns = function() {
3782    // set items, used if measuring first item
3783    this.items = this.isotope.filteredItems;
3784    measureColumns.call( this );
3785  };
3786
3787  // HACK copy over isOriginLeft/Top options
3788  var _manageStamp = MasonryMode.prototype._manageStamp;
3789  MasonryMode.prototype._manageStamp = function() {
3790    this.options.isOriginLeft = this.isotope.options.isOriginLeft;
3791    this.options.isOriginTop = this.isotope.options.isOriginTop;
3792    _manageStamp.apply( this, arguments );
3793  };
3794
3795  return MasonryMode;
3796
3797}));
3798
3799/**
3800 * fitRows layout mode
3801 */
3802
3803( function( window, factory ) {
3804  'use strict';
3805  // universal module definition
3806  if ( typeof define == 'function' && define.amd ) {
3807    // AMD
3808    define( 'isotope/js/layout-modes/fit-rows',[
3809        '../layout-mode'
3810      ],
3811      factory );
3812  } else if ( typeof exports == 'object' ) {
3813    // CommonJS
3814    module.exports = factory(
3815      require('../layout-mode')
3816    );
3817  } else {
3818    // browser global
3819    factory(
3820      window.Isotope.LayoutMode
3821    );
3822  }
3823
3824}( window, function factory( LayoutMode ) {
3825'use strict';
3826
3827var FitRows = LayoutMode.create('fitRows');
3828
3829FitRows.prototype._resetLayout = function() {
3830  this.x = 0;
3831  this.y = 0;
3832  this.maxY = 0;
3833  this._getMeasurement( 'gutter', 'outerWidth' );
3834};
3835
3836FitRows.prototype._getItemLayoutPosition = function( item ) {
3837  item.getSize();
3838
3839  var itemWidth = item.size.outerWidth + this.gutter;
3840  // if this element cannot fit in the current row
3841  var containerWidth = this.isotope.size.innerWidth + this.gutter;
3842  if ( this.x !== 0 && itemWidth + this.x > containerWidth ) {
3843    this.x = 0;
3844    this.y = this.maxY;
3845  }
3846
3847  var position = {
3848    x: this.x,
3849    y: this.y
3850  };
3851
3852  this.maxY = Math.max( this.maxY, this.y + item.size.outerHeight );
3853  this.x += itemWidth;
3854
3855  return position;
3856};
3857
3858FitRows.prototype._getContainerSize = function() {
3859  return { height: this.maxY };
3860};
3861
3862return FitRows;
3863
3864}));
3865
3866/**
3867 * vertical layout mode
3868 */
3869
3870( function( window, factory ) {
3871  'use strict';
3872  // universal module definition
3873  if ( typeof define == 'function' && define.amd ) {
3874    // AMD
3875    define( 'isotope/js/layout-modes/vertical',[
3876        '../layout-mode'
3877      ],
3878      factory );
3879  } else if ( typeof exports == 'object' ) {
3880    // CommonJS
3881    module.exports = factory(
3882      require('../layout-mode')
3883    );
3884  } else {
3885    // browser global
3886    factory(
3887      window.Isotope.LayoutMode
3888    );
3889  }
3890
3891}( window, function factory( LayoutMode ) {
3892'use strict';
3893
3894var Vertical = LayoutMode.create( 'vertical', {
3895  horizontalAlignment: 0
3896});
3897
3898Vertical.prototype._resetLayout = function() {
3899  this.y = 0;
3900};
3901
3902Vertical.prototype._getItemLayoutPosition = function( item ) {
3903  item.getSize();
3904  var x = ( this.isotope.size.innerWidth - item.size.outerWidth ) *
3905    this.options.horizontalAlignment;
3906  var y = this.y;
3907  this.y += item.size.outerHeight;
3908  return { x: x, y: y };
3909};
3910
3911Vertical.prototype._getContainerSize = function() {
3912  return { height: this.y };
3913};
3914
3915return Vertical;
3916
3917}));
3918
3919/*!
3920 * Isotope v2.2.2
3921 *
3922 * Licensed GPLv3 for open source use
3923 * or Isotope Commercial License for commercial use
3924 *
3925 * http://isotope.metafizzy.co
3926 * Copyright 2015 Metafizzy
3927 */
3928
3929( function( window, factory ) {
3930  'use strict';
3931  // universal module definition
3932
3933  if ( typeof define == 'function' && define.amd ) {
3934    // AMD
3935    define( [
3936        'outlayer/outlayer',
3937        'get-size/get-size',
3938        'matches-selector/matches-selector',
3939        'fizzy-ui-utils/utils',
3940        'isotope/js/item',
3941        'isotope/js/layout-mode',
3942        // include default layout modes
3943        'isotope/js/layout-modes/masonry',
3944        'isotope/js/layout-modes/fit-rows',
3945        'isotope/js/layout-modes/vertical'
3946      ],
3947      function( Outlayer, getSize, matchesSelector, utils, Item, LayoutMode ) {
3948        return factory( window, Outlayer, getSize, matchesSelector, utils, Item, LayoutMode );
3949      });
3950  } else if ( typeof exports == 'object' ) {
3951    // CommonJS
3952    module.exports = factory(
3953      window,
3954      require('outlayer'),
3955      require('get-size'),
3956      require('desandro-matches-selector'),
3957      require('fizzy-ui-utils'),
3958      require('./item'),
3959      require('./layout-mode'),
3960      // include default layout modes
3961      require('./layout-modes/masonry'),
3962      require('./layout-modes/fit-rows'),
3963      require('./layout-modes/vertical')
3964    );
3965  } else {
3966    // browser global
3967    window.Isotope = factory(
3968      window,
3969      window.Outlayer,
3970      window.getSize,
3971      window.matchesSelector,
3972      window.fizzyUIUtils,
3973      window.Isotope.Item,
3974      window.Isotope.LayoutMode
3975    );
3976  }
3977
3978}( window, function factory( window, Outlayer, getSize, matchesSelector, utils,
3979  Item, LayoutMode ) {
3980
3981
3982
3983// -------------------------- vars -------------------------- //
3984
3985var jQuery = window.jQuery;
3986
3987// -------------------------- helpers -------------------------- //
3988
3989var trim = String.prototype.trim ?
3990  function( str ) {
3991    return str.trim();
3992  } :
3993  function( str ) {
3994    return str.replace( /^\s+|\s+$/g, '' );
3995  };
3996
3997var docElem = document.documentElement;
3998
3999var getText = docElem.textContent ?
4000  function( elem ) {
4001    return elem.textContent;
4002  } :
4003  function( elem ) {
4004    return elem.innerText;
4005  };
4006
4007// -------------------------- isotopeDefinition -------------------------- //
4008
4009  // create an Outlayer layout class
4010  var Isotope = Outlayer.create( 'isotope', {
4011    layoutMode: "masonry",
4012    isJQueryFiltering: true,
4013    sortAscending: true
4014  });
4015
4016  Isotope.Item = Item;
4017  Isotope.LayoutMode = LayoutMode;
4018
4019  Isotope.prototype._create = function() {
4020    this.itemGUID = 0;
4021    // functions that sort items
4022    this._sorters = {};
4023    this._getSorters();
4024    // call super
4025    Outlayer.prototype._create.call( this );
4026
4027    // create layout modes
4028    this.modes = {};
4029    // start filteredItems with all items
4030    this.filteredItems = this.items;
4031    // keep of track of sortBys
4032    this.sortHistory = [ 'original-order' ];
4033    // create from registered layout modes
4034    for ( var name in LayoutMode.modes ) {
4035      this._initLayoutMode( name );
4036    }
4037  };
4038
4039  Isotope.prototype.reloadItems = function() {
4040    // reset item ID counter
4041    this.itemGUID = 0;
4042    // call super
4043    Outlayer.prototype.reloadItems.call( this );
4044  };
4045
4046  Isotope.prototype._itemize = function() {
4047    var items = Outlayer.prototype._itemize.apply( this, arguments );
4048    // assign ID for original-order
4049    for ( var i=0, len = items.length; i < len; i++ ) {
4050      var item = items[i];
4051      item.id = this.itemGUID++;
4052    }
4053    this._updateItemsSortData( items );
4054    return items;
4055  };
4056
4057
4058  // -------------------------- layout -------------------------- //
4059
4060  Isotope.prototype._initLayoutMode = function( name ) {
4061    var Mode = LayoutMode.modes[ name ];
4062    // set mode options
4063    // HACK extend initial options, back-fill in default options
4064    var initialOpts = this.options[ name ] || {};
4065    this.options[ name ] = Mode.options ?
4066      utils.extend( Mode.options, initialOpts ) : initialOpts;
4067    // init layout mode instance
4068    this.modes[ name ] = new Mode( this );
4069  };
4070
4071
4072  Isotope.prototype.layout = function() {
4073    // if first time doing layout, do all magic
4074    if ( !this._isLayoutInited && this.options.isInitLayout ) {
4075      this.arrange();
4076      return;
4077    }
4078    this._layout();
4079  };
4080
4081  // private method to be used in layout() & magic()
4082  Isotope.prototype._layout = function() {
4083    // don't animate first layout
4084    var isInstant = this._getIsInstant();
4085    // layout flow
4086    this._resetLayout();
4087    this._manageStamps();
4088    this.layoutItems( this.filteredItems, isInstant );
4089
4090    // flag for initalized
4091    this._isLayoutInited = true;
4092  };
4093
4094  // filter + sort + layout
4095  Isotope.prototype.arrange = function( opts ) {
4096    // set any options pass
4097    this.option( opts );
4098    this._getIsInstant();
4099    // filter, sort, and layout
4100
4101    // filter
4102    var filtered = this._filter( this.items );
4103    this.filteredItems = filtered.matches;
4104
4105    var _this = this;
4106    function hideReveal() {
4107      _this.reveal( filtered.needReveal );
4108      _this.hide( filtered.needHide );
4109    }
4110
4111    this._bindArrangeComplete();
4112
4113    if ( this._isInstant ) {
4114      this._noTransition( hideReveal );
4115    } else {
4116      hideReveal();
4117    }
4118
4119    this._sort();
4120    this._layout();
4121  };
4122  // alias to _init for main plugin method
4123  Isotope.prototype._init = Isotope.prototype.arrange;
4124
4125  // HACK
4126  // Don't animate/transition first layout
4127  // Or don't animate/transition other layouts
4128  Isotope.prototype._getIsInstant = function() {
4129    var isInstant = this.options.isLayoutInstant !== undefined ?
4130      this.options.isLayoutInstant : !this._isLayoutInited;
4131    this._isInstant = isInstant;
4132    return isInstant;
4133  };
4134
4135  // listen for layoutComplete, hideComplete and revealComplete
4136  // to trigger arrangeComplete
4137  Isotope.prototype._bindArrangeComplete = function() {
4138    // listen for 3 events to trigger arrangeComplete
4139    var isLayoutComplete, isHideComplete, isRevealComplete;
4140    var _this = this;
4141    function arrangeParallelCallback() {
4142      if ( isLayoutComplete && isHideComplete && isRevealComplete ) {
4143        _this.dispatchEvent( 'arrangeComplete', null, [ _this.filteredItems ] );
4144      }
4145    }
4146    this.once( 'layoutComplete', function() {
4147      isLayoutComplete = true;
4148      arrangeParallelCallback();
4149    });
4150    this.once( 'hideComplete', function() {
4151      isHideComplete = true;
4152      arrangeParallelCallback();
4153    });
4154    this.once( 'revealComplete', function() {
4155      isRevealComplete = true;
4156      arrangeParallelCallback();
4157    });
4158  };
4159
4160  // -------------------------- filter -------------------------- //
4161
4162  Isotope.prototype._filter = function( items ) {
4163    var filter = this.options.filter;
4164    filter = filter || '*';
4165    var matches = [];
4166    var hiddenMatched = [];
4167    var visibleUnmatched = [];
4168
4169    var test = this._getFilterTest( filter );
4170
4171    // test each item
4172    for ( var i=0, len = items.length; i < len; i++ ) {
4173      var item = items[i];
4174      if ( item.isIgnored ) {
4175        continue;
4176      }
4177      // add item to either matched or unmatched group
4178      var isMatched = test( item );
4179      // item.isFilterMatched = isMatched;
4180      // add to matches if its a match
4181      if ( isMatched ) {
4182        matches.push( item );
4183      }
4184      // add to additional group if item needs to be hidden or revealed
4185      if ( isMatched && item.isHidden ) {
4186        hiddenMatched.push( item );
4187      } else if ( !isMatched && !item.isHidden ) {
4188        visibleUnmatched.push( item );
4189      }
4190    }
4191
4192    // return collections of items to be manipulated
4193    return {
4194      matches: matches,
4195      needReveal: hiddenMatched,
4196      needHide: visibleUnmatched
4197    };
4198  };
4199
4200  // get a jQuery, function, or a matchesSelector test given the filter
4201  Isotope.prototype._getFilterTest = function( filter ) {
4202    if ( jQuery && this.options.isJQueryFiltering ) {
4203      // use jQuery
4204      return function( item ) {
4205        return jQuery( item.element ).is( filter );
4206      };
4207    }
4208    if ( typeof filter == 'function' ) {
4209      // use filter as function
4210      return function( item ) {
4211        return filter( item.element );
4212      };
4213    }
4214    // default, use filter as selector string
4215    return function( item ) {
4216      return matchesSelector( item.element, filter );
4217    };
4218  };
4219
4220  // -------------------------- sorting -------------------------- //
4221
4222  /**
4223   * @params {Array} elems
4224   * @public
4225   */
4226  Isotope.prototype.updateSortData = function( elems ) {
4227    // get items
4228    var items;
4229    if ( elems ) {
4230      elems = utils.makeArray( elems );
4231      items = this.getItems( elems );
4232    } else {
4233      // update all items if no elems provided
4234      items = this.items;
4235    }
4236
4237    this._getSorters();
4238    this._updateItemsSortData( items );
4239  };
4240
4241  Isotope.prototype._getSorters = function() {
4242    var getSortData = this.options.getSortData;
4243    for ( var key in getSortData ) {
4244      var sorter = getSortData[ key ];
4245      this._sorters[ key ] = mungeSorter( sorter );
4246    }
4247  };
4248
4249  /**
4250   * @params {Array} items - of Isotope.Items
4251   * @private
4252   */
4253  Isotope.prototype._updateItemsSortData = function( items ) {
4254    // do not update if no items
4255    var len = items && items.length;
4256
4257    for ( var i=0; len && i < len; i++ ) {
4258      var item = items[i];
4259      item.updateSortData();
4260    }
4261  };
4262
4263  // ----- munge sorter ----- //
4264
4265  // encapsulate this, as we just need mungeSorter
4266  // other functions in here are just for munging
4267  var mungeSorter = ( function() {
4268    // add a magic layer to sorters for convienent shorthands
4269    // `.foo-bar` will use the text of .foo-bar querySelector
4270    // `[foo-bar]` will use attribute
4271    // you can also add parser
4272    // `.foo-bar parseInt` will parse that as a number
4273    function mungeSorter( sorter ) {
4274      // if not a string, return function or whatever it is
4275      if ( typeof sorter != 'string' ) {
4276        return sorter;
4277      }
4278      // parse the sorter string
4279      var args = trim( sorter ).split(' ');
4280      var query = args[0];
4281      // check if query looks like [an-attribute]
4282      var attrMatch = query.match( /^\[(.+)\]$/ );
4283      var attr = attrMatch && attrMatch[1];
4284      var getValue = getValueGetter( attr, query );
4285      // use second argument as a parser
4286      var parser = Isotope.sortDataParsers[ args[1] ];
4287      // parse the value, if there was a parser
4288      sorter = parser ? function( elem ) {
4289        return elem && parser( getValue( elem ) );
4290      } :
4291      // otherwise just return value
4292      function( elem ) {
4293        return elem && getValue( elem );
4294      };
4295
4296      return sorter;
4297    }
4298
4299    // get an attribute getter, or get text of the querySelector
4300    function getValueGetter( attr, query ) {
4301      var getValue;
4302      // if query looks like [foo-bar], get attribute
4303      if ( attr ) {
4304        getValue = function( elem ) {
4305          return elem.getAttribute( attr );
4306        };
4307      } else {
4308        // otherwise, assume its a querySelector, and get its text
4309        getValue = function( elem ) {
4310          var child = elem.querySelector( query );
4311          return child && getText( child );
4312        };
4313      }
4314      return getValue;
4315    }
4316
4317    return mungeSorter;
4318  })();
4319
4320  // parsers used in getSortData shortcut strings
4321  Isotope.sortDataParsers = {
4322    'parseInt': function( val ) {
4323      return parseInt( val, 10 );
4324    },
4325    'parseFloat': function( val ) {
4326      return parseFloat( val );
4327    }
4328  };
4329
4330  // ----- sort method ----- //
4331
4332  // sort filteredItem order
4333  Isotope.prototype._sort = function() {
4334    var sortByOpt = this.options.sortBy;
4335    if ( !sortByOpt ) {
4336      return;
4337    }
4338    // concat all sortBy and sortHistory
4339    var sortBys = [].concat.apply( sortByOpt, this.sortHistory );
4340    // sort magic
4341    var itemSorter = getItemSorter( sortBys, this.options.sortAscending );
4342    this.filteredItems.sort( itemSorter );
4343    // keep track of sortBy History
4344    if ( sortByOpt != this.sortHistory[0] ) {
4345      // add to front, oldest goes in last
4346      this.sortHistory.unshift( sortByOpt );
4347    }
4348  };
4349
4350  // returns a function used for sorting
4351  function getItemSorter( sortBys, sortAsc ) {
4352    return function sorter( itemA, itemB ) {
4353      // cycle through all sortKeys
4354      for ( var i = 0, len = sortBys.length; i < len; i++ ) {
4355        var sortBy = sortBys[i];
4356        var a = itemA.sortData[ sortBy ];
4357        var b = itemB.sortData[ sortBy ];
4358        if ( a > b || a < b ) {
4359          // if sortAsc is an object, use the value given the sortBy key
4360          var isAscending = sortAsc[ sortBy ] !== undefined ? sortAsc[ sortBy ] : sortAsc;
4361          var direction = isAscending ? 1 : -1;
4362          return ( a > b ? 1 : -1 ) * direction;
4363        }
4364      }
4365      return 0;
4366    };
4367  }
4368
4369  // -------------------------- methods -------------------------- //
4370
4371  // get layout mode
4372  Isotope.prototype._mode = function() {
4373    var layoutMode = this.options.layoutMode;
4374    var mode = this.modes[ layoutMode ];
4375    if ( !mode ) {
4376      // TODO console.error
4377      throw new Error( 'No layout mode: ' + layoutMode );
4378    }
4379    // HACK sync mode's options
4380    // any options set after init for layout mode need to be synced
4381    mode.options = this.options[ layoutMode ];
4382    return mode;
4383  };
4384
4385  Isotope.prototype._resetLayout = function() {
4386    // trigger original reset layout
4387    Outlayer.prototype._resetLayout.call( this );
4388    this._mode()._resetLayout();
4389  };
4390
4391  Isotope.prototype._getItemLayoutPosition = function( item  ) {
4392    return this._mode()._getItemLayoutPosition( item );
4393  };
4394
4395  Isotope.prototype._manageStamp = function( stamp ) {
4396    this._mode()._manageStamp( stamp );
4397  };
4398
4399  Isotope.prototype._getContainerSize = function() {
4400    return this._mode()._getContainerSize();
4401  };
4402
4403  Isotope.prototype.needsResizeLayout = function() {
4404    return this._mode().needsResizeLayout();
4405  };
4406
4407  // -------------------------- adding & removing -------------------------- //
4408
4409  // HEADS UP overwrites default Outlayer appended
4410  Isotope.prototype.appended = function( elems ) {
4411    var items = this.addItems( elems );
4412    if ( !items.length ) {
4413      return;
4414    }
4415    // filter, layout, reveal new items
4416    var filteredItems = this._filterRevealAdded( items );
4417    // add to filteredItems
4418    this.filteredItems = this.filteredItems.concat( filteredItems );
4419  };
4420
4421  // HEADS UP overwrites default Outlayer prepended
4422  Isotope.prototype.prepended = function( elems ) {
4423    var items = this._itemize( elems );
4424    if ( !items.length ) {
4425      return;
4426    }
4427    // start new layout
4428    this._resetLayout();
4429    this._manageStamps();
4430    // filter, layout, reveal new items
4431    var filteredItems = this._filterRevealAdded( items );
4432    // layout previous items
4433    this.layoutItems( this.filteredItems );
4434    // add to items and filteredItems
4435    this.filteredItems = filteredItems.concat( this.filteredItems );
4436    this.items = items.concat( this.items );
4437  };
4438
4439  Isotope.prototype._filterRevealAdded = function( items ) {
4440    var filtered = this._filter( items );
4441    this.hide( filtered.needHide );
4442    // reveal all new items
4443    this.reveal( filtered.matches );
4444    // layout new items, no transition
4445    this.layoutItems( filtered.matches, true );
4446    return filtered.matches;
4447  };
4448
4449  /**
4450   * Filter, sort, and layout newly-appended item elements
4451   * @param {Array or NodeList or Element} elems
4452   */
4453  Isotope.prototype.insert = function( elems ) {
4454    var items = this.addItems( elems );
4455    if ( !items.length ) {
4456      return;
4457    }
4458    // append item elements
4459    var i, item;
4460    var len = items.length;
4461    for ( i=0; i < len; i++ ) {
4462      item = items[i];
4463      this.element.appendChild( item.element );
4464    }
4465    // filter new stuff
4466    var filteredInsertItems = this._filter( items ).matches;
4467    // set flag
4468    for ( i=0; i < len; i++ ) {
4469      items[i].isLayoutInstant = true;
4470    }
4471    this.arrange();
4472    // reset flag
4473    for ( i=0; i < len; i++ ) {
4474      delete items[i].isLayoutInstant;
4475    }
4476    this.reveal( filteredInsertItems );
4477  };
4478
4479  var _remove = Isotope.prototype.remove;
4480  Isotope.prototype.remove = function( elems ) {
4481    elems = utils.makeArray( elems );
4482    var removeItems = this.getItems( elems );
4483    // do regular thing
4484    _remove.call( this, elems );
4485    // bail if no items to remove
4486    var len = removeItems && removeItems.length;
4487    if ( !len ) {
4488      return;
4489    }
4490    // remove elems from filteredItems
4491    for ( var i=0; i < len; i++ ) {
4492      var item = removeItems[i];
4493      // remove item from collection
4494      utils.removeFrom( this.filteredItems, item );
4495    }
4496  };
4497
4498  Isotope.prototype.shuffle = function() {
4499    // update random sortData
4500    for ( var i=0, len = this.items.length; i < len; i++ ) {
4501      var item = this.items[i];
4502      item.sortData.random = Math.random();
4503    }
4504    this.options.sortBy = 'random';
4505    this._sort();
4506    this._layout();
4507  };
4508
4509  /**
4510   * trigger fn without transition
4511   * kind of hacky to have this in the first place
4512   * @param {Function} fn
4513   * @returns ret
4514   * @private
4515   */
4516  Isotope.prototype._noTransition = function( fn ) {
4517    // save transitionDuration before disabling
4518    var transitionDuration = this.options.transitionDuration;
4519    // disable transition
4520    this.options.transitionDuration = 0;
4521    // do it
4522    var returnValue = fn.call( this );
4523    // re-enable transition for reveal
4524    this.options.transitionDuration = transitionDuration;
4525    return returnValue;
4526  };
4527
4528  // ----- helper methods ----- //
4529
4530  /**
4531   * getter method for getting filtered item elements
4532   * @returns {Array} elems - collection of item elements
4533   */
4534  Isotope.prototype.getFilteredItemElements = function() {
4535    var elems = [];
4536    for ( var i=0, len = this.filteredItems.length; i < len; i++ ) {
4537      elems.push( this.filteredItems[i].element );
4538    }
4539    return elems;
4540  };
4541
4542  // -----  ----- //
4543
4544  return Isotope;
4545
4546}));
4547
vendor: 29,252 bytes, lines 4548-5704
4548/*!
4549 * Packery layout mode PACKAGED v2.0.1
4550 * sub-classes Packery
4551 */
4552
4553/**
4554 * Rect
4555 * low-level utility class for basic geometry
4556 */
4557
4558( function( window, factory ) {
4559  // universal module definition
4560  /* jshint strict: false */ /* globals define, module */
4561  if ( typeof define == 'function' && define.amd ) {
4562    // AMD
4563    define( 'packery/js/rect',factory );
4564  } else if ( typeof module == 'object' && module.exports ) {
4565    // CommonJS
4566    module.exports = factory();
4567  } else {
4568    // browser global
4569    window.Packery = window.Packery || {};
4570    window.Packery.Rect = factory();
4571  }
4572
4573}( window, function factory() {
4574
4575
4576// -------------------------- Rect -------------------------- //
4577
4578function Rect( props ) {
4579  // extend properties from defaults
4580  for ( var prop in Rect.defaults ) {
4581    this[ prop ] = Rect.defaults[ prop ];
4582  }
4583
4584  for ( prop in props ) {
4585    this[ prop ] = props[ prop ];
4586  }
4587
4588}
4589
4590Rect.defaults = {
4591  x: 0,
4592  y: 0,
4593  width: 0,
4594  height: 0
4595};
4596
4597var proto = Rect.prototype;
4598
4599/**
4600 * Determines whether or not this rectangle wholly encloses another rectangle or point.
4601 * @param {Rect} rect
4602 * @returns {Boolean}
4603**/
4604proto.contains = function( rect ) {
4605  // points don't have width or height
4606  var otherWidth = rect.width || 0;
4607  var otherHeight = rect.height || 0;
4608  return this.x <= rect.x &&
4609    this.y <= rect.y &&
4610    this.x + this.width >= rect.x + otherWidth &&
4611    this.y + this.height >= rect.y + otherHeight;
4612};
4613
4614/**
4615 * Determines whether or not the rectangle intersects with another.
4616 * @param {Rect} rect
4617 * @returns {Boolean}
4618**/
4619proto.overlaps = function( rect ) {
4620  var thisRight = this.x + this.width;
4621  var thisBottom = this.y + this.height;
4622  var rectRight = rect.x + rect.width;
4623  var rectBottom = rect.y + rect.height;
4624
4625  // http://stackoverflow.com/a/306332
4626  return this.x < rectRight &&
4627    thisRight > rect.x &&
4628    this.y < rectBottom &&
4629    thisBottom > rect.y;
4630};
4631
4632/**
4633 * @param {Rect} rect - the overlapping rect
4634 * @returns {Array} freeRects - rects representing the area around the rect
4635**/
4636proto.getMaximalFreeRects = function( rect ) {
4637
4638  // if no intersection, return false
4639  if ( !this.overlaps( rect ) ) {
4640    return false;
4641  }
4642
4643  var freeRects = [];
4644  var freeRect;
4645
4646  var thisRight = this.x + this.width;
4647  var thisBottom = this.y + this.height;
4648  var rectRight = rect.x + rect.width;
4649  var rectBottom = rect.y + rect.height;
4650
4651  // top
4652  if ( this.y < rect.y ) {
4653    freeRect = new Rect({
4654      x: this.x,
4655      y: this.y,
4656      width: this.width,
4657      height: rect.y - this.y
4658    });
4659    freeRects.push( freeRect );
4660  }
4661
4662  // right
4663  if ( thisRight > rectRight ) {
4664    freeRect = new Rect({
4665      x: rectRight,
4666      y: this.y,
4667      width: thisRight - rectRight,
4668      height: this.height
4669    });
4670    freeRects.push( freeRect );
4671  }
4672
4673  // bottom
4674  if ( thisBottom > rectBottom ) {
4675    freeRect = new Rect({
4676      x: this.x,
4677      y: rectBottom,
4678      width: this.width,
4679      height: thisBottom - rectBottom
4680    });
4681    freeRects.push( freeRect );
4682  }
4683
4684  // left
4685  if ( this.x < rect.x ) {
4686    freeRect = new Rect({
4687      x: this.x,
4688      y: this.y,
4689      width: rect.x - this.x,
4690      height: this.height
4691    });
4692    freeRects.push( freeRect );
4693  }
4694
4695  return freeRects;
4696};
4697
4698proto.canFit = function( rect ) {
4699  return this.width >= rect.width && this.height >= rect.height;
4700};
4701
4702return Rect;
4703
4704}));
4705
4706/**
4707 * Packer
4708 * bin-packing algorithm
4709 */
4710
4711( function( window, factory ) {
4712  // universal module definition
4713  /* jshint strict: false */ /* globals define, module, require */
4714  if ( typeof define == 'function' && define.amd ) {
4715    // AMD
4716    define( 'packery/js/packer',[ './rect' ], factory );
4717  } else if ( typeof module == 'object' && module.exports ) {
4718    // CommonJS
4719    module.exports = factory(
4720      require('./rect')
4721    );
4722  } else {
4723    // browser global
4724    var Packery = window.Packery = window.Packery || {};
4725    Packery.Packer = factory( Packery.Rect );
4726  }
4727
4728}( window, function factory( Rect ) {
4729
4730
4731// -------------------------- Packer -------------------------- //
4732
4733/**
4734 * @param {Number} width
4735 * @param {Number} height
4736 * @param {String} sortDirection
4737 *   topLeft for vertical, leftTop for horizontal
4738 */
4739function Packer( width, height, sortDirection ) {
4740  this.width = width || 0;
4741  this.height = height || 0;
4742  this.sortDirection = sortDirection || 'downwardLeftToRight';
4743
4744  this.reset();
4745}
4746
4747var proto = Packer.prototype;
4748
4749proto.reset = function() {
4750  this.spaces = [];
4751
4752  var initialSpace = new Rect({
4753    x: 0,
4754    y: 0,
4755    width: this.width,
4756    height: this.height
4757  });
4758
4759  this.spaces.push( initialSpace );
4760  // set sorter
4761  this.sorter = sorters[ this.sortDirection ] || sorters.downwardLeftToRight;
4762};
4763
4764// change x and y of rect to fit with in Packer's available spaces
4765proto.pack = function( rect ) {
4766  for ( var i=0; i < this.spaces.length; i++ ) {
4767    var space = this.spaces[i];
4768    if ( space.canFit( rect ) ) {
4769      this.placeInSpace( rect, space );
4770      break;
4771    }
4772  }
4773};
4774
4775proto.columnPack = function( rect ) {
4776  for ( var i=0; i < this.spaces.length; i++ ) {
4777    var space = this.spaces[i];
4778    var canFitInSpaceColumn = space.x <= rect.x &&
4779      space.x + space.width >= rect.x + rect.width &&
4780      space.height >= rect.height - 0.01; // fudge number for rounding error
4781    if ( canFitInSpaceColumn ) {
4782      rect.y = space.y;
4783      this.placed( rect );
4784      break;
4785    }
4786  }
4787};
4788
4789proto.rowPack = function( rect ) {
4790  for ( var i=0; i < this.spaces.length; i++ ) {
4791    var space = this.spaces[i];
4792    var canFitInSpaceRow = space.y <= rect.y &&
4793      space.y + space.height >= rect.y + rect.height &&
4794      space.width >= rect.width - 0.01; // fudge number for rounding error
4795    if ( canFitInSpaceRow ) {
4796      rect.x = space.x;
4797      this.placed( rect );
4798      break;
4799    }
4800  }
4801};
4802
4803proto.placeInSpace = function( rect, space ) {
4804  // place rect in space
4805  rect.x = space.x;
4806  rect.y = space.y;
4807
4808  this.placed( rect );
4809};
4810
4811// update spaces with placed rect
4812proto.placed = function( rect ) {
4813  // update spaces
4814  var revisedSpaces = [];
4815  for ( var i=0; i < this.spaces.length; i++ ) {
4816    var space = this.spaces[i];
4817    var newSpaces = space.getMaximalFreeRects( rect );
4818    // add either the original space or the new spaces to the revised spaces
4819    if ( newSpaces ) {
4820      revisedSpaces.push.apply( revisedSpaces, newSpaces );
4821    } else {
4822      revisedSpaces.push( space );
4823    }
4824  }
4825
4826  this.spaces = revisedSpaces;
4827
4828  this.mergeSortSpaces();
4829};
4830
4831proto.mergeSortSpaces = function() {
4832  // remove redundant spaces
4833  Packer.mergeRects( this.spaces );
4834  this.spaces.sort( this.sorter );
4835};
4836
4837// add a space back
4838proto.addSpace = function( rect ) {
4839  this.spaces.push( rect );
4840  this.mergeSortSpaces();
4841};
4842
4843// -------------------------- utility functions -------------------------- //
4844
4845/**
4846 * Remove redundant rectangle from array of rectangles
4847 * @param {Array} rects: an array of Rects
4848 * @returns {Array} rects: an array of Rects
4849**/
4850Packer.mergeRects = function( rects ) {
4851  var i = 0;
4852  var rect = rects[i];
4853
4854  rectLoop:
4855  while ( rect ) {
4856    var j = 0;
4857    var compareRect = rects[ i + j ];
4858
4859    while ( compareRect ) {
4860      if  ( compareRect == rect ) {
4861        j++; // next
4862      } else if ( compareRect.contains( rect ) ) {
4863        // remove rect
4864        rects.splice( i, 1 );
4865        rect = rects[i]; // set next rect
4866        continue rectLoop; // bail on compareLoop
4867      } else if ( rect.contains( compareRect ) ) {
4868        // remove compareRect
4869        rects.splice( i + j, 1 );
4870      } else {
4871        j++;
4872      }
4873      compareRect = rects[ i + j ]; // set next compareRect
4874    }
4875    i++;
4876    rect = rects[i];
4877  }
4878
4879  return rects;
4880};
4881
4882
4883// -------------------------- sorters -------------------------- //
4884
4885// functions for sorting rects in order
4886var sorters = {
4887  // top down, then left to right
4888  downwardLeftToRight: function( a, b ) {
4889    return a.y - b.y || a.x - b.x;
4890  },
4891  // left to right, then top down
4892  rightwardTopToBottom: function( a, b ) {
4893    return a.x - b.x || a.y - b.y;
4894  }
4895};
4896
4897
4898// --------------------------  -------------------------- //
4899
4900return Packer;
4901
4902}));
4903
4904/**
4905 * Packery Item Element
4906**/
4907
4908( function( window, factory ) {
4909  // universal module definition
4910  /* jshint strict: false */ /* globals define, module, require */
4911  if ( typeof define == 'function' && define.amd ) {
4912    // AMD
4913    define( 'packery/js/item',[
4914        'outlayer/outlayer',
4915        './rect'
4916      ],
4917      factory );
4918  } else if ( typeof module == 'object' && module.exports ) {
4919    // CommonJS
4920    module.exports = factory(
4921      require('outlayer'),
4922      require('./rect')
4923    );
4924  } else {
4925    // browser global
4926    window.Packery.Item = factory(
4927      window.Outlayer,
4928      window.Packery.Rect
4929    );
4930  }
4931
4932}( window, function factory( Outlayer, Rect ) {
4933
4934
4935// -------------------------- Item -------------------------- //
4936
4937var docElemStyle = document.documentElement.style;
4938
4939var transformProperty = typeof docElemStyle.transform == 'string' ?
4940  'transform' : 'WebkitTransform';
4941
4942// sub-class Item
4943var Item = function PackeryItem() {
4944  Outlayer.Item.apply( this, arguments );
4945};
4946
4947var proto = Item.prototype = Object.create( Outlayer.Item.prototype );
4948
4949var __create = proto._create;
4950proto._create = function() {
4951  // call default _create logic
4952  __create.call( this );
4953  this.rect = new Rect();
4954};
4955
4956var _moveTo = proto.moveTo;
4957proto.moveTo = function( x, y ) {
4958  // don't shift 1px while dragging
4959  var dx = Math.abs( this.position.x - x );
4960  var dy = Math.abs( this.position.y - y );
4961
4962  var canHackGoTo = this.layout.dragItemCount && !this.isPlacing &&
4963    !this.isTransitioning && dx < 1 && dy < 1;
4964  if ( canHackGoTo ) {
4965    this.goTo( x, y );
4966    return;
4967  }
4968  _moveTo.apply( this, arguments );
4969};
4970
4971// -------------------------- placing -------------------------- //
4972
4973proto.enablePlacing = function() {
4974  this.removeTransitionStyles();
4975  // remove transform property from transition
4976  if ( this.isTransitioning && transformProperty ) {
4977    this.element.style[ transformProperty ] = 'none';
4978  }
4979  this.isTransitioning = false;
4980  this.getSize();
4981  this.layout._setRectSize( this.element, this.rect );
4982  this.isPlacing = true;
4983};
4984
4985proto.disablePlacing = function() {
4986  this.isPlacing = false;
4987};
4988
4989// -----  ----- //
4990
4991// remove element from DOM
4992proto.removeElem = function() {
4993  this.element.parentNode.removeChild( this.element );
4994  // add space back to packer
4995  this.layout.packer.addSpace( this.rect );
4996  this.emitEvent( 'remove', [ this ] );
4997};
4998
4999// ----- dropPlaceholder ----- //
5000
5001proto.showDropPlaceholder = function() {
5002  var dropPlaceholder = this.dropPlaceholder;
5003  if ( !dropPlaceholder ) {
5004    // create dropPlaceholder
5005    dropPlaceholder = this.dropPlaceholder = document.createElement('div');
5006    dropPlaceholder.className = 'packery-drop-placeholder';
5007    dropPlaceholder.style.position = 'absolute';
5008  }
5009
5010  dropPlaceholder.style.width = this.size.width + 'px';
5011  dropPlaceholder.style.height = this.size.height + 'px';
5012  this.positionDropPlaceholder();
5013  this.layout.element.appendChild( dropPlaceholder );
5014};
5015
5016proto.positionDropPlaceholder = function() {
5017  this.dropPlaceholder.style[ transformProperty ] = 'translate(' +
5018    this.rect.x + 'px, ' + this.rect.y + 'px)';
5019};
5020
5021proto.hideDropPlaceholder = function() {
5022  this.layout.element.removeChild( this.dropPlaceholder );
5023};
5024
5025// -----  ----- //
5026
5027return Item;
5028
5029}));
5030
5031/*!
5032 * Packery v2.0.0
5033 * Gapless, draggable grid layouts
5034 *
5035 * Licensed GPLv3 for open source use
5036 * or Packery Commercial License for commercial use
5037 *
5038 * http://packery.metafizzy.co
5039 * Copyright 2016 Metafizzy
5040 */
5041
5042( function( window, factory ) {
5043  // universal module definition
5044  /* jshint strict: false */ /* globals define, module, require */
5045  if ( typeof define == 'function' && define.amd ) {
5046    // AMD
5047    define( 'packery/js/packery',[
5048        'get-size/get-size',
5049        'outlayer/outlayer',
5050        './rect',
5051        './packer',
5052        './item'
5053      ],
5054      factory );
5055  } else if ( typeof module == 'object' && module.exports ) {
5056    // CommonJS
5057    module.exports = factory(
5058      require('get-size'),
5059      require('outlayer'),
5060      require('./rect'),
5061      require('./packer'),
5062      require('./item')
5063    );
5064  } else {
5065    // browser global
5066    window.Packery = factory(
5067      window.getSize,
5068      window.Outlayer,
5069      window.Packery.Rect,
5070      window.Packery.Packer,
5071      window.Packery.Item
5072    );
5073  }
5074
5075}( window, function factory( getSize, Outlayer, Rect, Packer, Item ) {
5076
5077
5078// ----- Rect ----- //
5079
5080// allow for pixel rounding errors IE8-IE11 & Firefox; #227
5081Rect.prototype.canFit = function( rect ) {
5082  return this.width >= rect.width - 1 && this.height >= rect.height - 1;
5083};
5084
5085// -------------------------- Packery -------------------------- //
5086
5087// create an Outlayer layout class
5088var Packery = Outlayer.create('packery');
5089Packery.Item = Item;
5090
5091var proto = Packery.prototype;
5092
5093proto._create = function() {
5094  // call super
5095  Outlayer.prototype._create.call( this );
5096
5097  // initial properties
5098  this.packer = new Packer();
5099  // packer for drop targets
5100  this.shiftPacker = new Packer();
5101  this.isEnabled = true;
5102
5103  this.dragItemCount = 0;
5104
5105  // create drag handlers
5106  var _this = this;
5107  this.handleDraggabilly = {
5108    dragStart: function() {
5109      _this.itemDragStart( this.element );
5110    },
5111    dragMove: function() {
5112      _this.itemDragMove( this.element, this.position.x, this.position.y );
5113    },
5114    dragEnd: function() {
5115      _this.itemDragEnd( this.element );
5116    }
5117  };
5118
5119  this.handleUIDraggable = {
5120    start: function handleUIDraggableStart( event, ui ) {
5121      // HTML5 may trigger dragstart, dismiss HTML5 dragging
5122      if ( !ui ) {
5123        return;
5124      }
5125      _this.itemDragStart( event.currentTarget );
5126    },
5127    drag: function handleUIDraggableDrag( event, ui ) {
5128      if ( !ui ) {
5129        return;
5130      }
5131      _this.itemDragMove( event.currentTarget, ui.position.left, ui.position.top );
5132    },
5133    stop: function handleUIDraggableStop( event, ui ) {
5134      if ( !ui ) {
5135        return;
5136      }
5137      _this.itemDragEnd( event.currentTarget );
5138    }
5139  };
5140
5141};
5142
5143
5144// ----- init & layout ----- //
5145
5146/**
5147 * logic before any new layout
5148 */
5149proto._resetLayout = function() {
5150  this.getSize();
5151
5152  this._getMeasurements();
5153
5154  // reset packer
5155  var width, height, sortDirection;
5156  // packer settings, if horizontal or vertical
5157  if ( this._getOption('horizontal') ) {
5158    width = Infinity;
5159    height = this.size.innerHeight + this.gutter;
5160    sortDirection = 'rightwardTopToBottom';
5161  } else {
5162    width = this.size.innerWidth + this.gutter;
5163    height = Infinity;
5164    sortDirection = 'downwardLeftToRight';
5165  }
5166
5167  this.packer.width = this.shiftPacker.width = width;
5168  this.packer.height = this.shiftPacker.height = height;
5169  this.packer.sortDirection = this.shiftPacker.sortDirection = sortDirection;
5170
5171  this.packer.reset();
5172
5173  // layout
5174  this.maxY = 0;
5175  this.maxX = 0;
5176};
5177
5178/**
5179 * update columnWidth, rowHeight, & gutter
5180 * @private
5181 */
5182proto._getMeasurements = function() {
5183  this._getMeasurement( 'columnWidth', 'width' );
5184  this._getMeasurement( 'rowHeight', 'height' );
5185  this._getMeasurement( 'gutter', 'width' );
5186};
5187
5188proto._getItemLayoutPosition = function( item ) {
5189  this._setRectSize( item.element, item.rect );
5190  if ( this.isShifting || this.dragItemCount > 0 ) {
5191    var packMethod = this._getPackMethod();
5192    this.packer[ packMethod ]( item.rect );
5193  } else {
5194    this.packer.pack( item.rect );
5195  }
5196
5197  this._setMaxXY( item.rect );
5198  return item.rect;
5199};
5200
5201proto.shiftLayout = function() {
5202  this.isShifting = true;
5203  this.layout();
5204  delete this.isShifting;
5205};
5206
5207proto._getPackMethod = function() {
5208  return this._getOption('horizontal') ? 'rowPack' : 'columnPack';
5209};
5210
5211
5212/**
5213 * set max X and Y value, for size of container
5214 * @param {Packery.Rect} rect
5215 * @private
5216 */
5217proto._setMaxXY = function( rect ) {
5218  this.maxX = Math.max( rect.x + rect.width, this.maxX );
5219  this.maxY = Math.max( rect.y + rect.height, this.maxY );
5220};
5221
5222/**
5223 * set the width and height of a rect, applying columnWidth and rowHeight
5224 * @param {Element} elem
5225 * @param {Packery.Rect} rect
5226 */
5227proto._setRectSize = function( elem, rect ) {
5228  var size = getSize( elem );
5229  var w = size.outerWidth;
5230  var h = size.outerHeight;
5231  // size for columnWidth and rowHeight, if available
5232  // only check if size is non-zero, #177
5233  if ( w || h ) {
5234    w = this._applyGridGutter( w, this.columnWidth );
5235    h = this._applyGridGutter( h, this.rowHeight );
5236  }
5237  // rect must fit in packer
5238  rect.width = Math.min( w, this.packer.width );
5239  rect.height = Math.min( h, this.packer.height );
5240};
5241
5242/**
5243 * fits item to columnWidth/rowHeight and adds gutter
5244 * @param {Number} measurement - item width or height
5245 * @param {Number} gridSize - columnWidth or rowHeight
5246 * @returns measurement
5247 */
5248proto._applyGridGutter = function( measurement, gridSize ) {
5249  // just add gutter if no gridSize
5250  if ( !gridSize ) {
5251    return measurement + this.gutter;
5252  }
5253  gridSize += this.gutter;
5254  // fit item to columnWidth/rowHeight
5255  var remainder = measurement % gridSize;
5256  var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
5257  measurement = Math[ mathMethod ]( measurement / gridSize ) * gridSize;
5258  return measurement;
5259};
5260
5261proto._getContainerSize = function() {
5262  if ( this._getOption('horizontal') ) {
5263    return {
5264      width: this.maxX - this.gutter
5265    };
5266  } else {
5267    return {
5268      height: this.maxY - this.gutter
5269    };
5270  }
5271};
5272
5273
5274// -------------------------- stamp -------------------------- //
5275
5276/**
5277 * makes space for element
5278 * @param {Element} elem
5279 */
5280proto._manageStamp = function( elem ) {
5281
5282  var item = this.getItem( elem );
5283  var rect;
5284  if ( item && item.isPlacing ) {
5285    rect = item.rect;
5286  } else {
5287    var offset = this._getElementOffset( elem );
5288    rect = new Rect({
5289      x: this._getOption('originLeft') ? offset.left : offset.right,
5290      y: this._getOption('originTop') ? offset.top : offset.bottom
5291    });
5292  }
5293
5294  this._setRectSize( elem, rect );
5295  // save its space in the packer
5296  this.packer.placed( rect );
5297  this._setMaxXY( rect );
5298};
5299
5300// -------------------------- methods -------------------------- //
5301
5302function verticalSorter( a, b ) {
5303  return a.position.y - b.position.y || a.position.x - b.position.x;
5304}
5305
5306function horizontalSorter( a, b ) {
5307  return a.position.x - b.position.x || a.position.y - b.position.y;
5308}
5309
5310proto.sortItemsByPosition = function() {
5311  var sorter = this._getOption('horizontal') ? horizontalSorter : verticalSorter;
5312  this.items.sort( sorter );
5313};
5314
5315/**
5316 * Fit item element in its current position
5317 * Packery will position elements around it
5318 * useful for expanding elements
5319 *
5320 * @param {Element} elem
5321 * @param {Number} x - horizontal destination position, optional
5322 * @param {Number} y - vertical destination position, optional
5323 */
5324proto.fit = function( elem, x, y ) {
5325  var item = this.getItem( elem );
5326  if ( !item ) {
5327    return;
5328  }
5329
5330  // stamp item to get it out of layout
5331  this.stamp( item.element );
5332  // set placing flag
5333  item.enablePlacing();
5334  this.updateShiftTargets( item );
5335  // fall back to current position for fitting
5336  x = x === undefined ? item.rect.x: x;
5337  y = y === undefined ? item.rect.y: y;
5338  // position it best at its destination
5339  this.shift( item, x, y );
5340  this._bindFitEvents( item );
5341  item.moveTo( item.rect.x, item.rect.y );
5342  // layout everything else
5343  this.shiftLayout();
5344  // return back to regularly scheduled programming
5345  this.unstamp( item.element );
5346  this.sortItemsByPosition();
5347  item.disablePlacing();
5348};
5349
5350/**
5351 * emit event when item is fit and other items are laid out
5352 * @param {Packery.Item} item
5353 * @private
5354 */
5355proto._bindFitEvents = function( item ) {
5356  var _this = this;
5357  var ticks = 0;
5358  function onLayout() {
5359    ticks++;
5360    if ( ticks != 2 ) {
5361      return;
5362    }
5363    _this.dispatchEvent( 'fitComplete', null, [ item ] );
5364  }
5365  // when item is laid out
5366  item.once( 'layout', onLayout );
5367  // when all items are laid out
5368  this.once( 'layoutComplete', onLayout );
5369};
5370
5371// -------------------------- resize -------------------------- //
5372
5373// debounced, layout on resize
5374proto.resize = function() {
5375  // don't trigger if size did not change
5376  // or if resize was unbound. See #285, outlayer#9
5377  if ( !this.isResizeBound || !this.needsResizeLayout() ) {
5378    return;
5379  }
5380
5381  if ( this.options.shiftPercentResize ) {
5382    this.resizeShiftPercentLayout();
5383  } else {
5384    this.layout();
5385  }
5386};
5387
5388/**
5389 * check if layout is needed post layout
5390 * @returns Boolean
5391 */
5392proto.needsResizeLayout = function() {
5393  var size = getSize( this.element );
5394  var innerSize = this._getOption('horizontal') ? 'innerHeight' : 'innerWidth';
5395  return size[ innerSize ] != this.size[ innerSize ];
5396};
5397
5398proto.resizeShiftPercentLayout = function() {
5399  var items = this._getItemsForLayout( this.items );
5400
5401  var isHorizontal = this._getOption('horizontal');
5402  var coord = isHorizontal ? 'y' : 'x';
5403  var measure = isHorizontal ? 'height' : 'width';
5404  var segmentName = isHorizontal ? 'rowHeight' : 'columnWidth';
5405  var innerSize = isHorizontal ? 'innerHeight' : 'innerWidth';
5406
5407  // proportional re-align items
5408  var previousSegment = this[ segmentName ];
5409  previousSegment = previousSegment && previousSegment + this.gutter;
5410
5411  if ( previousSegment ) {
5412    this._getMeasurements();
5413    var currentSegment = this[ segmentName ] + this.gutter;
5414    items.forEach( function( item ) {
5415      var seg = Math.round( item.rect[ coord ] / previousSegment );
5416      item.rect[ coord ] = seg * currentSegment;
5417    });
5418  } else {
5419    var currentSize = getSize( this.element )[ innerSize ] + this.gutter;
5420    var previousSize = this.packer[ measure ];
5421    items.forEach( function( item ) {
5422      item.rect[ coord ] = ( item.rect[ coord ] / previousSize ) * currentSize;
5423    });
5424  }
5425
5426  this.shiftLayout();
5427};
5428
5429// -------------------------- drag -------------------------- //
5430
5431/**
5432 * handle an item drag start event
5433 * @param {Element} elem
5434 */
5435proto.itemDragStart = function( elem ) {
5436  if ( !this.isEnabled ) {
5437    return;
5438  }
5439  this.stamp( elem );
5440  // this.ignore( elem );
5441  var item = this.getItem( elem );
5442  if ( !item ) {
5443    return;
5444  }
5445
5446  item.enablePlacing();
5447  item.showDropPlaceholder();
5448  this.dragItemCount++;
5449  this.updateShiftTargets( item );
5450};
5451
5452proto.updateShiftTargets = function( dropItem ) {
5453  this.shiftPacker.reset();
5454
5455  // pack stamps
5456  this._getBoundingRect();
5457  var isOriginLeft = this._getOption('originLeft');
5458  var isOriginTop = this._getOption('originTop');
5459  this.stamps.forEach( function( stamp ) {
5460    // ignore dragged item
5461    var item = this.getItem( stamp );
5462    if ( item && item.isPlacing ) {
5463      return;
5464    }
5465    var offset = this._getElementOffset( stamp );
5466    var rect = new Rect({
5467      x: isOriginLeft ? offset.left : offset.right,
5468      y: isOriginTop ? offset.top : offset.bottom
5469    });
5470    this._setRectSize( stamp, rect );
5471    // save its space in the packer
5472    this.shiftPacker.placed( rect );
5473  }, this );
5474
5475  // reset shiftTargets
5476  var isHorizontal = this._getOption('horizontal');
5477  var segmentName = isHorizontal ? 'rowHeight' : 'columnWidth';
5478  var measure = isHorizontal ? 'height' : 'width';
5479
5480  this.shiftTargetKeys = [];
5481  this.shiftTargets = [];
5482  var boundsSize;
5483  var segment = this[ segmentName ];
5484  segment = segment && segment + this.gutter;
5485
5486  if ( segment ) {
5487    var segmentSpan = Math.ceil( dropItem.rect[ measure ] / segment );
5488    var segs = Math.floor( ( this.shiftPacker[ measure ] + this.gutter ) / segment );
5489    boundsSize = ( segs - segmentSpan ) * segment;
5490    // add targets on top
5491    for ( var i=0; i < segs; i++ ) {
5492      this._addShiftTarget( i * segment, 0, boundsSize );
5493    }
5494  } else {
5495    boundsSize = ( this.shiftPacker[ measure ] + this.gutter ) - dropItem.rect[ measure ];
5496    this._addShiftTarget( 0, 0, boundsSize );
5497  }
5498
5499  // pack each item to measure where shiftTargets are
5500  var items = this._getItemsForLayout( this.items );
5501  var packMethod = this._getPackMethod();
5502  items.forEach( function( item ) {
5503    var rect = item.rect;
5504    this._setRectSize( item.element, rect );
5505    this.shiftPacker[ packMethod ]( rect );
5506
5507    // add top left corner
5508    this._addShiftTarget( rect.x, rect.y, boundsSize );
5509    // add bottom left / top right corner
5510    var cornerX = isHorizontal ? rect.x + rect.width : rect.x;
5511    var cornerY = isHorizontal ? rect.y : rect.y + rect.height;
5512    this._addShiftTarget( cornerX, cornerY, boundsSize );
5513
5514    if ( segment ) {
5515      // add targets for each column on bottom / row on right
5516      var segSpan = Math.round( rect[ measure ] / segment );
5517      for ( var i=1; i < segSpan; i++ ) {
5518        var segX = isHorizontal ? cornerX : rect.x + segment * i;
5519        var segY = isHorizontal ? rect.y + segment * i : cornerY;
5520        this._addShiftTarget( segX, segY, boundsSize );
5521      }
5522    }
5523  }, this );
5524
5525};
5526
5527proto._addShiftTarget = function( x, y, boundsSize ) {
5528  var checkCoord = this._getOption('horizontal') ? y : x;
5529  if ( checkCoord !== 0 && checkCoord > boundsSize ) {
5530    return;
5531  }
5532  // create string for a key, easier to keep track of what targets
5533  var key = x + ',' + y;
5534  var hasKey = this.shiftTargetKeys.indexOf( key ) != -1;
5535  if ( hasKey ) {
5536    return;
5537  }
5538  this.shiftTargetKeys.push( key );
5539  this.shiftTargets.push({ x: x, y: y });
5540};
5541
5542// -------------------------- drop -------------------------- //
5543
5544proto.shift = function( item, x, y ) {
5545  var shiftPosition;
5546  var minDistance = Infinity;
5547  var position = { x: x, y: y };
5548  this.shiftTargets.forEach( function( target ) {
5549    var distance = getDistance( target, position );
5550    if ( distance < minDistance ) {
5551      shiftPosition = target;
5552      minDistance = distance;
5553    }
5554  });
5555  item.rect.x = shiftPosition.x;
5556  item.rect.y = shiftPosition.y;
5557};
5558
5559function getDistance( a, b ) {
5560  var dx = b.x - a.x;
5561  var dy = b.y - a.y;
5562  return Math.sqrt( dx * dx + dy * dy );
5563}
5564
5565// -------------------------- drag move -------------------------- //
5566
5567var DRAG_THROTTLE_TIME = 120;
5568
5569/**
5570 * handle an item drag move event
5571 * @param {Element} elem
5572 * @param {Number} x - horizontal change in position
5573 * @param {Number} y - vertical change in position
5574 */
5575proto.itemDragMove = function( elem, x, y ) {
5576  var item = this.isEnabled && this.getItem( elem );
5577  if ( !item ) {
5578    return;
5579  }
5580
5581  x -= this.size.paddingLeft;
5582  y -= this.size.paddingTop;
5583
5584  var _this = this;
5585  function onDrag() {
5586    _this.shift( item, x, y );
5587    item.positionDropPlaceholder();
5588    _this.layout();
5589  }
5590
5591  // throttle
5592  var now = new Date();
5593  if ( this._itemDragTime && now - this._itemDragTime < DRAG_THROTTLE_TIME ) {
5594    clearTimeout( this.dragTimeout );
5595    this.dragTimeout = setTimeout( onDrag, DRAG_THROTTLE_TIME );
5596  } else {
5597    onDrag();
5598    this._itemDragTime = now;
5599  }
5600};
5601
5602// -------------------------- drag end -------------------------- //
5603
5604/**
5605 * handle an item drag end event
5606 * @param {Element} elem
5607 */
5608proto.itemDragEnd = function( elem ) {
5609  var item = this.isEnabled && this.getItem( elem );
5610  if ( !item ) {
5611    return;
5612  }
5613
5614  clearTimeout( this.dragTimeout );
5615  item.element.classList.add('is-positioning-post-drag');
5616
5617  var completeCount = 0;
5618  var _this = this;
5619  function onDragEndLayoutComplete() {
5620    completeCount++;
5621    if ( completeCount != 2 ) {
5622      return;
5623    }
5624    // reset drag item
5625    item.element.classList.remove('is-positioning-post-drag');
5626    item.hideDropPlaceholder();
5627    _this.dispatchEvent( 'dragItemPositioned', null, [ item ] );
5628  }
5629
5630  item.once( 'layout', onDragEndLayoutComplete );
5631  this.once( 'layoutComplete', onDragEndLayoutComplete );
5632  item.moveTo( item.rect.x, item.rect.y );
5633  this.layout();
5634  this.dragItemCount = Math.max( 0, this.dragItemCount - 1 );
5635  this.sortItemsByPosition();
5636  item.disablePlacing();
5637  this.unstamp( item.element );
5638};
5639
5640/**
5641 * binds Draggabilly events
5642 * @param {Draggabilly} draggie
5643 */
5644proto.bindDraggabillyEvents = function( draggie ) {
5645  this._bindDraggabillyEvents( draggie, 'on' );
5646};
5647
5648proto.unbindDraggabillyEvents = function( draggie ) {
5649  this._bindDraggabillyEvents( draggie, 'off' );
5650};
5651
5652proto._bindDraggabillyEvents = function( draggie, method ) {
5653  var handlers = this.handleDraggabilly;
5654  draggie[ method ]( 'dragStart', handlers.dragStart );
5655  draggie[ method ]( 'dragMove', handlers.dragMove );
5656  draggie[ method ]( 'dragEnd', handlers.dragEnd );
5657};
5658
5659/**
5660 * binds jQuery UI Draggable events
5661 * @param {jQuery} $elems
5662 */
5663proto.bindUIDraggableEvents = function( $elems ) {
5664  this._bindUIDraggableEvents( $elems, 'on' );
5665};
5666
5667proto.unbindUIDraggableEvents = function( $elems ) {
5668  this._bindUIDraggableEvents( $elems, 'off' );
5669};
5670
5671proto._bindUIDraggableEvents = function( $elems, method ) {
5672  var handlers = this.handleUIDraggable;
5673  $elems
5674    [ method ]( 'dragstart', handlers.start )
5675    [ method ]( 'drag', handlers.drag )
5676    [ method ]( 'dragstop', handlers.stop );
5677};
5678
5679// ----- destroy ----- //
5680
5681var _destroy = proto.destroy;
5682proto.destroy = function() {
5683  _destroy.apply( this, arguments );
5684  // disable flag; prevent drag events from triggering. #72
5685  this.isEnabled = false;
5686};
5687
5688// -----  ----- //
5689
5690Packery.Rect = Rect;
5691Packery.Packer = Packer;
5692
5693return Packery;
5694
5695}));
5696
5697/*!
5698 * Packery layout mode v2.0.1
5699 * sub-classes Packery
5700 */
5701
5702/*jshint browser: true, strict: true, undef: true, unused: true */
5703
5704( function( window, factory ) {
5705
5706  // universal module definition
5707  if ( typeof define == 'function' && define.amd ) {
5708    // AMD
5709    define( [
5710        'isotope-layout/js/layout-mode',
5711        'packery/js/packery'
5712      ],
5713      factory );
5714  } else if ( typeof module == 'object' && module.exports ) {
5715    // CommonJS
5716    module.exports = factory(
5717      require('isotope-layout/js/layout-mode'),
5718      require('packery')
5719    );
5720  } else {
5721    // browser global
5722    factory(
5723      window.Isotope.LayoutMode,
5724      window.Packery
5725    );
5726  }
5727
5728}( window, function factor( LayoutMode, Packery ) {
5729
5730
5731  // create an Outlayer layout class
5732  var PackeryMode = LayoutMode.create('packery');
5733  var proto = PackeryMode.prototype;
5734
5735  var keepModeMethods = {
5736    _getElementOffset: true,
5737    _getMeasurement: true
5738  };
5739
5740  // inherit Packery prototype
5741  for ( var method in Packery.prototype ) {
5742    // do not inherit mode methods
5743    if ( !keepModeMethods[ method ] ) {
5744      proto[ method ] = Packery.prototype[ method ];
5745    }
5746  }
5747
5748  // set packer in _resetLayout
5749  var _resetLayout = proto._resetLayout;
5750  proto._resetLayout = function() {
5751    this.packer = this.packer || new Packery.Packer();
5752    this.shiftPacker = this.shiftPacker || new Packery.Packer();
5753    _resetLayout.apply( this, arguments );
5754  };
5755
5756  var _getItemLayoutPosition = proto._getItemLayoutPosition;
5757  proto._getItemLayoutPosition = function( item ) {
5758    // set packery rect
5759    item.rect = item.rect || new Packery.Rect();
5760    return _getItemLayoutPosition.call( this, item );
5761  };
5762
5763  // needsResizeLayout for vertical or horizontal
5764  var _needsResizeLayout = proto.needsResizeLayout;
5765  proto.needsResizeLayout = function() {
5766    if ( this._getOption('horizontal') ) {
5767      return this.needsVerticalResizeLayout();
5768    } else {
5769      return _needsResizeLayout.call( this );
5770    }
5771  };
5772
5773  // point to mode options for horizontal
5774  var _getOption = proto._getOption;
5775  proto._getOption = function( option ) {
5776    if ( option == 'horizontal' ) {
5777      return this.options.isHorizontal !== undefined ?
5778        this.options.isHorizontal : this.options.horizontal;
5779    }
5780    return _getOption.apply( this.isotope, arguments );
5781  };
5782
5783  return PackeryMode;
5784
5785}));
5786
5787/*!
5788 * cellsByRows layout mode for Isotope
5789 * v1.1.3
5790 * http://isotope.metafizzy.co/layout-modes/cellsbyrow.html
5791 */
5792
5793/*jshint browser: true, devel: false, strict: true, undef: true, unused: true */
5794
5795( function( window, factory ) {
5796  // universal module definition
5797  /* jshint strict: false */ /*globals define, module, require */
5798  if ( typeof define === 'function' && define.amd ) {
5799    // AMD
5800    define( [
5801        'isotope/js/layout-mode'
5802      ],
5803      factory );
5804  } else if ( typeof exports === 'object' ) {
5805    // CommonJS
5806    module.exports = factory(
5807      require('isotope-layout/js/layout-mode')
5808    );
5809  } else {
5810    // browser global
5811    factory(
5812      window.Isotope.LayoutMode
5813    );
5814  }
5815
5816}( window, function factory( LayoutMode ) {
5817  'use strict';
5818
5819  var CellsByRow = LayoutMode.create( 'cellsByRow' );
5820  var proto = CellsByRow.prototype;
5821
5822  proto._resetLayout = function() {
5823    // reset properties
5824    this.itemIndex = 0;
5825    // measurements
5826    this.getColumnWidth();
5827    this.getRowHeight();
5828    // set cols
5829    this.cols = Math.floor( this.isotope.size.innerWidth / this.columnWidth );
5830    this.cols = Math.max( this.cols, 1 );
5831  };
5832
5833  proto._getItemLayoutPosition = function( item ) {
5834    item.getSize();
5835    var col = this.itemIndex % this.cols;
5836    var row = Math.floor( this.itemIndex / this.cols );
5837    // center item within cell
5838    var x = ( col + 0.5 ) * this.columnWidth - item.size.outerWidth / 2;
5839    var y = ( row + 0.5 ) * this.rowHeight - item.size.outerHeight / 2;
5840    this.itemIndex++;
5841    return { x: x, y: y };
5842  };
5843
5844  proto._getContainerSize = function() {
5845    return {
5846      height: Math.ceil( this.itemIndex / this.cols ) * this.rowHeight
5847    };
5848  };
5849
5850  return CellsByRow;
5851
5852}));
5853/*global jQuery: true */
5854
5855/*!
5856   --------------------------------
5857   Infinite Scroll
5858   --------------------------------
5859   + https://github.com/paulirish/infinite-scroll
5860   + version 2.1.0
5861   + Copyright 2011/12 Paul Irish & Luke Shumard
5862   + Licensed under the MIT license
5863
5864   + Documentation: http://infinite-scroll.com/
5865*/
5866
5867// Uses AMD or browser globals to create a jQuery plugin.
5868(function (factory) {
5869    if (typeof define === 'function' && define.amd) {
5870        // AMD. Register as an anonymous module.
5871        define(['jquery'], factory);
5872    } else {
5873        // Browser globals
5874        factory(jQuery);
5875    }
5876}(function ($, undefined) {
5877    'use strict';
5878
5879    $.infinitescroll = function infscr(options, callback, element) {
5880        this.element = $(element);
5881
5882        // Flag the object in the event of a failed creation
5883        if (!this._create(options, callback)) {
5884            this.failed = true;
5885        }
5886    };
5887
5888    $.infinitescroll.defaults = {
5889        loading: {
5890            finished: undefined,
5891            finishedMsg: "<em>Congratulations, you've reached the end of the internet.</em>",
5892            img: 'data:image/gif;base64,R0lGODlh3AATAPQeAPDy+MnQ6LW/4N3h8MzT6rjC4sTM5r/I5NHX7N7j8c7U6tvg8OLl8uXo9Ojr9b3G5MfP6Ovu9tPZ7PT1+vX2+tbb7vf4+8/W69jd7rC73vn5/O/x+K243ai02////wAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQECgD/ACwAAAAA3AATAAAF/6AnjmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEj0BAScpHLJbDqf0Kh0Sq1ar9isdioItAKGw+MAKYMFhbF63CW438f0mg1R2O8EuXj/aOPtaHx7fn96goR4hmuId4qDdX95c4+RBIGCB4yAjpmQhZN0YGYGXitdZBIVGAsLoq4BBKQDswm1CQRkcG6ytrYKubq8vbfAcMK9v7q7EMO1ycrHvsW6zcTKsczNz8HZw9vG3cjTsMIYqQkCLBwHCgsMDQ4RDAYIqfYSFxDxEfz88/X38Onr16+Bp4ADCco7eC8hQYMAEe57yNCew4IVBU7EGNDiRn8Z831cGLHhSIgdFf9chIeBg7oA7gjaWUWTVQAGE3LqBDCTlc9WOHfm7PkTqNCh54rePDqB6M+lR536hCpUqs2gVZM+xbrTqtGoWqdy1emValeXKzggYBBB5y1acFNZmEvXAoN2cGfJrTv3bl69Ffj2xZt3L1+/fw3XRVw4sGDGcR0fJhxZsF3KtBTThZxZ8mLMgC3fRatCbYMNFCzwLEqLgE4NsDWs/tvqdezZf13Hvk2A9Szdu2X3pg18N+68xXn7rh1c+PLksI/Dhe6cuO3ow3NfV92bdArTqC2Ebd3A8vjf5QWfH6Bg7Nz17c2fj69+fnq+8N2Lty+fuP78/eV2X13neIcCeBRwxorbZrA1ANoCDGrgoG8RTshahQ9iSKEEzUmYIYfNWViUhheCGJyIP5E4oom7WWjgCeBFAJNv1DVV01MAdJhhjdkplWNzO/5oXI846njjVEIqR2OS2B1pE5PVscajkxhMycqLJghQSwT40PgfAl4GqNSXYdZXJn5gSkmmmmJu1aZYb14V51do+pTOCmA40AqVCIhG5IJ9PvYnhIFOxmdqhpaI6GeHCtpooisuutmg+Eg62KOMKuqoTaXgicQWoIYq6qiklmoqFV0UoeqqrLbq6quwxirrrLTWauutJ4QAACH5BAUKABwALAcABADOAAsAAAX/IPd0D2dyRCoUp/k8gpHOKtseR9yiSmGbuBykler9XLAhkbDavXTL5k2oqFqNOxzUZPU5YYZd1XsD72rZpBjbeh52mSNnMSC8lwblKZGwi+0QfIJ8CncnCoCDgoVnBHmKfByGJimPkIwtiAeBkH6ZHJaKmCeVnKKTHIihg5KNq4uoqmEtcRUtEREMBggtEr4QDrjCuRC8h7/BwxENeicSF8DKy82pyNLMOxzWygzFmdvD2L3P0dze4+Xh1Arkyepi7dfFvvTtLQkZBC0T/FX3CRgCMOBHsJ+EHYQY7OinAGECgQsB+Lu3AOK+CewcWjwxQeJBihtNGHSoQOE+iQ3//4XkwBBhRZMcUS6YSXOAwIL8PGqEaSJCiYt9SNoCmnJPAgUVLChdaoFBURN8MAzl2PQphwQLfDFd6lTowglHve6rKpbjhK7/pG5VinZP1qkiz1rl4+tr2LRwWU64cFEihwEtZgbgR1UiHaMVvxpOSwBA37kzGz9e8G+B5MIEKLutOGEsAH2ATQwYfTmuX8aETWdGPZmiZcccNSzeTCA1Sw0bdiitC7LBWgu8jQr8HRzqgpK6gX88QbrB14z/kF+ELpwB8eVQj/JkqdylAudji/+ts3039vEEfK8Vz2dlvxZKG0CmbkKDBvllRd6fCzDvBLKBDSCeffhRJEFebFk1k/Mv9jVIoIJZSeBggwUaNeB+Qk34IE0cXlihcfRxkOAJFFhwGmKlmWDiakZhUJtnLBpnWWcnKaAZcxI0piFGGLBm1mc90kajSCveeBVWKeYEoU2wqeaQi0PetoE+rr14EpVC7oAbAUHqhYExbn2XHHsVqbcVew9tx8+XJKk5AZsqqdlddGpqAKdbAYBn1pcczmSTdWvdmZ17c1b3FZ99vnTdCRFM8OEcAhLwm1NdXnWcBBSMRWmfkWZqVlsmLIiAp/o1gGV2vpS4lalGYsUOqXrddcKCmK61aZ8SjEpUpVFVoCpTj4r661Km7kBHjrDyc1RAIQAAIfkEBQoAGwAsBwAEAM4ACwAABf/gtmUCd4goQQgFKj6PYKi0yrrbc8i4ohQt12EHcal+MNSQiCP8gigdz7iCioaCIvUmZLp8QBzW0EN2vSlCuDtFKaq4RyHzQLEKZNdiQDhRDVooCwkbfm59EAmKi4SGIm+AjIsKjhsqB4mSjT2IOIOUnICeCaB/mZKFNTSRmqVpmJqklSqskq6PfYYCDwYHDC4REQwGCBLGxxIQDsHMwhAIX8bKzcENgSLGF9PU1j3Sy9zX2NrgzQziChLk1BHWxcjf7N046tvN82715czn9Pryz6Ilc4ACj4EBOCZM8KEnAYYADBRKnACAYUMFv1wotIhCEcaJCisqwJFgAUSQGyX/kCSVUUTIdKMwJlyo0oXHlhskwrTJciZHEXsgaqS4s6PJiCAr1uzYU8kBBSgnWFqpoMJMUjGtDmUwkmfVmVypakWhEKvXsS4nhLW5wNjVroJIoc05wSzTr0PtiigpYe4EC2vj4iWrFu5euWIMRBhacaVJhYQBEFjA9jHjyQ0xEABwGceGAZYjY0YBOrRLCxUp29QM+bRkx5s7ZyYgVbTqwwti2ybJ+vLtDYpycyZbYOlptxdx0kV+V7lC5iJAyyRrwYKxAdiz82ng0/jnAdMJFz0cPi104Ec1Vj9/M6F173vKL/feXv156dw11tlqeMMnv4V5Ap53GmjQQH97nFfg+IFiucfgRX5Z8KAgbUlQ4IULIlghhhdOSB6AgX0IVn8eReghen3NRIBsRgnH4l4LuEidZBjwRpt6NM5WGwoW0KSjCwX6yJSMab2GwwAPDXfaBCtWpluRTQqC5JM5oUZAjUNS+VeOLWpJEQ7VYQANW0INJSZVDFSnZphjSikfmzE5N4EEbQI1QJmnWXCmHulRp2edwDXF43txukenJwvI9xyg9Q26Z3MzGUcBYFEChZh6DVTq34AU8Iflh51Sd+CnKFYQ6mmZkhqfBKfSxZWqA9DZanWjxmhrWwi0qtCrt/43K6WqVjjpmhIqgEGvculaGKklKstAACEAACH5BAUKABwALAcABADOAAsAAAX/ICdyQmaMYyAUqPgIBiHPxNpy79kqRXH8wAPsRmDdXpAWgWdEIYm2llCHqjVHU+jjJkwqBTecwItShMXkEfNWSh8e1NGAcLgpDGlRgk7EJ/6Ae3VKfoF/fDuFhohVeDeCfXkcCQqDVQcQhn+VNDOYmpSWaoqBlUSfmowjEA+iEAEGDRGztAwGCDcXEA60tXEiCrq8vREMEBLIyRLCxMWSH
5892MzExnbRvQ2Sy7vN0zvVtNfU2tLY3rPgLdnDvca4VQS/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
5892iqpQpZ4apwSwRtjqrB3muoF9BboaXKmshlqWqsWiGt2wphJkQbAU5hoCACH5BAUKABsALAcABADOAAsAAAX/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',
5893            msg: null,
5894            msgText: '<em>Loading the next set of posts...</em>',
5895            selector: null,
5896            speed: 'fast',
5897            start: undefined
5898        },
5899        state: {
5900            isDuringAjax: false,
5901            isInvalidPage: false,
5902            isDestroyed: false,
5903            isDone: false, // For when it goes all the way through the archive.
5904            isPaused: false,
5905            isBeyondMaxPage: false,
5906            currPage: 1
5907        },
5908        debug: false,
5909        behavior: undefined,
5910        binder: $(window), // used to cache the selector
5911        nextSelector: 'div.navigation a:first',
5912        navSelector: 'div.navigation',
5913        contentSelector: null, // rename to pageFragment
5914        extraScrollPx: 150,
5915        itemSelector: 'div.post',
5916        animate: false,
5917        pathParse: undefined,
5918        dataType: 'html',
5919        appendCallback: true,
5920        bufferPx: 40,
5921        errorCallback: function () { },
5922        infid: 0, //Instance ID
5923        pixelsFromNavToBottom: undefined,
5924        path: undefined, // Either parts of a URL as an array (e.g. ["/page/", "/"] or a function that takes in the page number and returns a URL
5925        prefill: false, // When the document is smaller than the window, load data until the document is larger or links are exhausted
5926        maxPage: undefined // to manually control maximum page (when maxPage is undefined, maximum page limitation is not work)
5927    };
5928
5929    $.infinitescroll.prototype = {
5930
5931        /*
5932            ----------------------------
5933            Private methods
5934            ----------------------------
5935            */
5936
5937        // Bind or unbind from scroll
5938        _binding: function infscr_binding(binding) {
5939
5940            var instance = this,
5941            opts = instance.options;
5942
5943            opts.v = '2.0b2.120520';
5944
5945            // if behavior is defined and this function is extended, call that instead of default
5946            if (!!opts.behavior && this['_binding_'+opts.behavior] !== undefined) {
5947                this['_binding_'+opts.behavior].call(this);
5948                return;
5949            }
5950
5951            if (binding !== 'bind' && binding !== 'unbind') {
5952                this._debug('Binding value  ' + binding + ' not valid');
5953                return false;
5954            }
5955
5956            if (binding === 'unbind') {
5957                (this.options.binder).unbind('smartscroll.infscr.' + instance.options.infid);
5958            } else {
5959                (this.options.binder)[binding]('smartscroll.infscr.' + instance.options.infid, function () {
5960                    instance.scroll();
5961                });
5962            }
5963
5964            this._debug('Binding', binding);
5965        },
5966
5967        // Fundamental aspects of the plugin are initialized
5968        _create: function infscr_create(options, callback) {
5969
5970            // Add custom options to defaults
5971            var opts = $.extend(true, {}, $.infinitescroll.defaults, options);
5972            this.options = opts;
5973            var $window = $(window);
5974            var instance = this;
5975
5976            // Validate selectors
5977            if (!instance._validate(options)) {
5978                return false;
5979            }
5980
5981            // Validate page fragment path
5982            var path = $(opts.nextSelector).attr('href');
5983            if (!path) {
5984                this._debug('Navigation selector not found');
5985                return false;
5986            }
5987
5988            // Set the path to be a relative URL from root.
5989            opts.path = opts.path || this._determinepath(path);
5990
5991            // contentSelector is 'page fragment' option for .load() / .ajax() calls
5992            opts.contentSelector = opts.contentSelector || this.element;
5993
5994            // loading.selector - if we want to place the load message in a specific selector, defaulted to the contentSelector
5995            opts.loading.selector = opts.loading.selector || opts.contentSelector;
5996
5997            // Define loading.msg
5998            opts.loading.msg = opts.loading.msg || $('<div id="infscr-loading"><img alt="Loading..." src="' + opts.loading.img + '" /><div>' + opts.loading.msgText + '</div></div>');
5999
6000            // Preload loading.img
6001            (new Image()).src = opts.loading.img;
6002
6003            // distance from nav links to bottom
6004            // computed as: height of the document + top offset of container - top offset of nav link
6005            if(opts.pixelsFromNavToBottom === undefined) {
6006                opts.pixelsFromNavToBottom = $(document).height() - $(opts.navSelector).offset().top;
6007                this._debug('pixelsFromNavToBottom: ' + opts.pixelsFromNavToBottom);
6008            }
6009
6010            var self = this;
6011
6012            // determine loading.start actions
6013            opts.loading.start = opts.loading.start || function() {
6014                $(opts.navSelector).hide();
6015                opts.loading.msg
6016                .appendTo(opts.loading.selector)
6017                .show(opts.loading.speed, $.proxy(function() {
6018                    this.beginAjax(opts);
6019                }, self));
6020            };
6021
6022            // determine loading.finished actions
6023            opts.loading.finished = opts.loading.finished || function() {
6024                if (!opts.state.isBeyondMaxPage)
6025                    opts.loading.msg.fadeOut(opts.loading.speed);
6026            };
6027
6028            // callback loading
6029            opts.callback = function(instance, data, url) {
6030                if (!!opts.behavior && instance['_callback_'+opts.behavior] !== undefined) {
6031                    instance['_callback_'+opts.behavior].call($(opts.contentSelector)[0], data, url);
6032                }
6033
6034                if (callback) {
6035                    callback.call($(opts.contentSelector)[0], data, opts, url);
6036                }
6037
6038                if (opts.prefill) {
6039                    $window.bind('resize.infinite-scroll', instance._prefill);
6040                }
6041            };
6042
6043            if (options.debug) {
6044                // Tell IE9 to use its built-in console
6045                if (Function.prototype.bind && (typeof console === 'object' || typeof console === 'function') && typeof console.log === 'object') {
6046                    ['log','info','warn','error','assert','dir','clear','profile','profileEnd']
6047                        .forEach(function (method) {
6048                            console[method] = this.call(console[method], console);
6049                        }, Function.prototype.bind);
6050                }
6051            }
6052
6053            this._setup();
6054
6055            // Setups the prefill method for use
6056            if (opts.prefill) {
6057                this._prefill();
6058            }
6059
6060            // Return true to indicate successful creation
6061            return true;
6062        },
6063
6064        _prefill: function infscr_prefill() {
6065            var instance = this;
6066            var $window = $(window);
6067
6068            function needsPrefill() {
6069                return ( $(instance.options.contentSelector).height() <= $window.height() );
6070            }
6071
6072            this._prefill = function() {
6073                if (needsPrefill()) {
6074                    instance.scroll();
6075                }
6076
6077                $window.bind('resize.infinite-scroll', function() {
6078                    if (needsPrefill()) {
6079                        $window.unbind('resize.infinite-scroll');
6080                        instance.scroll();
6081                    }
6082                });
6083            };
6084
6085            // Call self after setting up the new function
6086            this._prefill();
6087        },
6088
6089        // Console log wrapper
6090        _debug: function infscr_debug() {
6091            if (true !== this.options.debug) {
6092                return;
6093            }
6094
6095            if (typeof console !== 'undefined' && typeof console.log === 'function') {
6096                // Modern browsers
6097                // Single argument, which is a string
6098                if ((Array.prototype.slice.call(arguments)).length === 1 && typeof Array.prototype.slice.call(arguments)[0] === 'string') {
6099                    console.log( (Array.prototype.slice.call(arguments)).toString() );
6100                } else {
6101                    console.log( Array.prototype.slice.call(arguments) );
6102                }
6103            } else if (!Function.prototype.bind && typeof console !== 'undefined' && typeof console.log === 'object') {
6104                // IE8
6105                Function.prototype.call.call(console.log, console, Array.prototype.slice.call(arguments));
6106            }
6107        },
6108
6109        // find the number to increment in the path.
6110        _determinepath: function infscr_determinepath(path) {
6111
6112            var opts = this.options;
6113
6114            // if behavior is defined and this function is extended, call that instead of default
6115            if (!!opts.behavior && this['_determinepath_'+opts.behavior] !== undefined) {
6116                return this['_determinepath_'+opts.behavior].call(this,path);
6117            }
6118
6119            if (!!opts.pathParse) {
6120
6121                this._debug('pathParse manual');
6122                return opts.pathParse(path, this.options.state.currPage+1);
6123
6124            } else if (path.match(/^(.*?)\b2\b(.*?$)/)) {
6125                path = path.match(/^(.*?)\b2\b(.*?$)/).slice(1);
6126
6127                // if there is any 2 in the url at all.
6128            } else if (path.match(/^(.*?)2(.*?$)/)) {
6129
6130                // page= is used in django:
6131                // http://www.infinite-scroll.com/changelog/comment-page-1/#comment-127
6132                if (path.match(/^(.*?page=)2(\/.*|$)/)) {
6133                    path = path.match(/^(.*?page=)2(\/.*|$)/).slice(1);
6134                    return path;
6135                }
6136
6137                path = path.match(/^(.*?)2(.*?$)/).slice(1);
6138
6139            } else {
6140
6141                // page= is used in drupal too but second page is page=1 not page=2:
6142                // thx Jerod Fritz, vladikoff
6143                if (path.match(/^(.*?page=)1(\/.*|$)/)) {
6144                    path = path.match(/^(.*?page=)1(\/.*|$)/).slice(1);
6145                    return path;
6146                } else {
6147                    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.");
6148                    // Get rid of isInvalidPage to allow permalink to state
6149                    opts.state.isInvalidPage = true;  //prevent it from running on this page.
6150                }
6151            }
6152            this._debug('determinePath', path);
6153            return path;
6154
6155        },
6156
6157        // Custom error
6158        _error: function infscr_error(xhr) {
6159
6160            var opts = this.options;
6161
6162            // if behavior is defined and this function is extended, call that instead of default
6163            if (!!opts.behavior && this['_error_'+opts.behavior] !== undefined) {
6164                this['_error_'+opts.behavior].call(this,xhr);
6165                return;
6166            }
6167
6168            if (xhr !== 'destroy' && xhr !== 'end') {
6169                xhr = 'unknown';
6170            }
6171
6172            this._debug('Error', xhr);
6173
6174            if (xhr === 'end' || opts.state.isBeyondMaxPage) {
6175                this._showdonemsg();
6176            }
6177
6178            opts.state.isDone = true;
6179            opts.state.currPage = 1; // if you need to go back to this instance
6180            opts.state.isPaused = false;
6181            opts.state.isBeyondMaxPage = false;
6182            this._binding('unbind');
6183
6184        },
6185
6186        // Load Callback
6187        _loadcallback: function infscr_loadcallback(box, data, url) {
6188            var opts = this.options,
6189            callback = this.options.callback, // GLOBAL OBJECT FOR CALLBACK
6190            result = (opts.state.isDone) ? 'done' : (!opts.appendCallback) ? 'no-append' : 'append',
6191            frag;
6192
6193            // if behavior is defined and this function is extended, call that instead of default
6194            if (!!opts.behavior && this['_loadcallback_'+opts.behavior] !== undefined) {
6195                this['_loadcallback_'+opts.behavior].call(this,box,data);
6196                return;
6197            }
6198
6199            switch (result) {
6200                case 'done':
6201                    this._showdonemsg();
6202                    return false;
6203
6204                case 'no-append':
6205                    if (opts.dataType === 'html') {
6206                        data = '<div>' + data + '</div>';
6207                        data = $(data).find(opts.itemSelector);
6208                    }
6209
6210                    // if it didn't return anything
6211                    if (data.length === 0) {
6212                        return this._error('end');
6213                    }
6214
6215                    break;
6216
6217                case 'append':
6218                    var children = box.children();
6219                    // if it didn't return anything
6220                    if (children.length === 0) {
6221                        return this._error('end');
6222                    }
6223
6224                    // use a documentFragment because it works when content is going into a table or UL
6225                    frag = document.createDocumentFragment();
6226                    while (box[0].firstChild) {
6227                        frag.appendChild(box[0].firstChild);
6228                    }
6229
6230                    this._debug('contentSelector', $(opts.contentSelector)[0]);
6231                    $(opts.contentSelector)[0].appendChild(frag);
6232                    // previously, we would pass in the new DOM element as context for the callback
6233                    // however we're now using a documentfragment, which doesn't have parents or children,
6234                    // so the context is the contentContainer guy, and we pass in an array
6235                    // of the elements collected as the first argument.
6236
6237                    data = children.get();
6238                    break;
6239            }
6240
6241            // loadingEnd function
6242            opts.loading.finished.call($(opts.contentSelector)[0],opts);
6243
6244            // smooth scroll to ease in the new content
6245            if (opts.animate) {
6246                var scrollTo = $(window).scrollTop() + $(opts.loading.msg).height() + opts.extraScrollPx + 'px';
6247                $('html,body').animate({ scrollTop: scrollTo }, 800, function () { opts.state.isDuringAjax = false; });
6248            }
6249
6250            if (!opts.animate) {
6251                // once the call is done, we can allow it again.
6252                opts.state.isDuringAjax = false;
6253            }
6254
6255            callback(this, data, url);
6256
6257            if (opts.prefill) {
6258                this._prefill();
6259            }
6260        },
6261
6262        _nearbottom: function infscr_nearbottom() {
6263
6264            var opts = this.options,
6265            pixelsFromWindowBottomToBottom = 0 + $(document).height() - (opts.binder.scrollTop()) - $(window).height();
6266
6267            // if behavior is defined and this function is extended, call that instead of default
6268            if (!!opts.behavior && this['_nearbottom_'+opts.behavior] !== undefined) {
6269                return this['_nearbottom_'+opts.behavior].call(this);
6270            }
6271
6272            this._debug('math:', pixelsFromWindowBottomToBottom, opts.pixelsFromNavToBottom);
6273
6274            // if distance remaining in the scroll (including buffer) is less than the orignal nav to bottom....
6275            return (pixelsFromWindowBottomToBottom - opts.bufferPx < opts.pixelsFromNavToBottom);
6276
6277        },
6278
6279        // Pause / temporarily disable plugin from firing
6280        _pausing: function infscr_pausing(pause) {
6281
6282            var opts = this.options;
6283
6284            // if behavior is defined and this function is extended, call that instead of default
6285            if (!!opts.behavior && this['_pausing_'+opts.behavior] !== undefined) {
6286                this['_pausing_'+opts.behavior].call(this,pause);
6287                return;
6288            }
6289
6290            // If pause is not 'pause' or 'resume', toggle it's value
6291            if (pause !== 'pause' && pause !== 'resume' && pause !== null) {
6292                this._debug('Invalid argument. Toggling pause value instead');
6293            }
6294
6295            pause = (pause && (pause === 'pause' || pause === 'resume')) ? pause : 'toggle';
6296
6297            switch (pause) {
6298                case 'pause':
6299                    opts.state.isPaused = true;
6300                break;
6301
6302                case 'resume':
6303                    opts.state.isPaused = false;
6304                break;
6305
6306                case 'toggle':
6307                    opts.state.isPaused = !opts.state.isPaused;
6308                break;
6309            }
6310
6311            this._debug('Paused', opts.state.isPaused);
6312            return false;
6313
6314        },
6315
6316        // Behavior is determined
6317        // If the behavior option is undefined, it will set to default and bind to scroll
6318        _setup: function infscr_setup() {
6319
6320            var opts = this.options;
6321
6322            // if behavior is defined and this function is extended, call that instead of default
6323            if (!!opts.behavior && this['_setup_'+opts.behavior] !== undefined) {
6324                this['_setup_'+opts.behavior].call(this);
6325                return;
6326            }
6327
6328            this._binding('bind');
6329
6330            return false;
6331
6332        },
6333
6334        // Show done message
6335        _showdonemsg: function infscr_showdonemsg() {
6336
6337            var opts = this.options;
6338
6339            // if behavior is defined and this function is extended, call that instead of default
6340            if (!!opts.behavior && this['_showdonemsg_'+opts.behavior] !== undefined) {
6341                this['_showdonemsg_'+opts.behavior].call(this);
6342                return;
6343            }
6344
6345            opts.loading.msg
6346            .find('img')
6347            .hide()
6348            .parent()
6349            .find('div').html(opts.loading.finishedMsg).animate({ opacity: 1 }, 2000, function () {
6350                $(this).parent().fadeOut(opts.loading.speed);
6351            });
6352
6353            // user provided callback when done
6354            opts.errorCallback.call($(opts.contentSelector)[0],'done');
6355        },
6356
6357        // grab each selector option and see if any fail
6358        _validate: function infscr_validate(opts) {
6359            for (var key in opts) {
6360                if (key.indexOf && key.indexOf('Selector') > -1 && $(opts[key]).length === 0) {
6361                    this._debug('Your ' + key + ' found no elements.');
6362                    return false;
6363                }
6364            }
6365
6366            return true;
6367        },
6368
6369        /*
6370            ----------------------------
6371            Public methods
6372            ----------------------------
6373            */
6374
6375        // Bind to scroll
6376        bind: function infscr_bind() {
6377            this._binding('bind');
6378        },
6379
6380        // Destroy current instance of plugin
6381        destroy: function infscr_destroy() {
6382            this.options.state.isDestroyed = true;
6383            this.options.loading.finished();
6384            return this._error('destroy');
6385        },
6386
6387        // Set pause value to false
6388        pause: function infscr_pause() {
6389            this._pausing('pause');
6390        },
6391
6392        // Set pause value to false
6393        resume: function infscr_resume() {
6394            this._pausing('resume');
6395        },
6396
6397        beginAjax: function infscr_ajax(opts) {
6398            var instance = this,
6399                path = opts.path,
6400                box, desturl, method, condition;
6401
6402            // increment the URL bit. e.g. /page/3/
6403            opts.state.currPage++;
6404
6405            // Manually control maximum page
6406            if ( opts.maxPage !== undefined && opts.state.currPage > opts.maxPage ){
6407                opts.state.isBeyondMaxPage = true;
6408                this.destroy();
6409                return;
6410            }
6411
6412            // if we're dealing with a table we can't use DIVs
6413            box = $(opts.contentSelector).is('table, tbody') ? $('<tbody/>') : $('<div/>');
6414
6415            desturl = (typeof path === 'function') ? path(opts.state.currPage) : path.join(opts.state.currPage);
6416            instance._debug('heading into ajax', desturl);
6417
6418            method = (opts.dataType === 'html' || opts.dataType === 'json' ) ? opts.dataType : 'html+callback';
6419            if (opts.appendCallback && opts.dataType === 'html') {
6420                method += '+callback';
6421            }
6422
6423            switch (method) {
6424                case 'html+callback':
6425                    instance._debug('Using HTML via .load() method');
6426                    box.load(desturl + ' ' + opts.itemSelector, undefined, function infscr_ajax_callback(responseText) {
6427                        instance._loadcallback(box, responseText, desturl);
6428                    });
6429
6430                    break;
6431
6432                case 'html':
6433                    instance._debug('Using ' + (method.toUpperCase()) + ' via $.ajax() method');
6434                    $.ajax({
6435                        // params
6436                        url: desturl,
6437                        dataType: opts.dataType,
6438                        complete: function infscr_ajax_callback(jqXHR, textStatus) {
6439                            condition = (typeof (jqXHR.isResolved) !== 'undefined') ? (jqXHR.isResolved()) : (textStatus === 'success' || textStatus === 'notmodified');
6440                            if (condition) {
6441                                instance._loadcallback(box, jqXHR.responseText, desturl);
6442                            } else {
6443                                instance._error('end');
6444                            }
6445                        }
6446                    });
6447
6448                    break;
6449                case 'json':
6450                    instance._debug('Using ' + (method.toUpperCase()) + ' via $.ajax() method');
6451                    $.ajax({
6452                        dataType: 'json',
6453                        type: 'GET',
6454                        url: desturl,
6455                        success: function (data, textStatus, jqXHR) {
6456                            condition = (typeof (jqXHR.isResolved) !== 'undefined') ? (jqXHR.isResolved()) : (textStatus === 'success' || textStatus === 'notmodified');
6457                            if (opts.appendCallback) {
6458                                // if appendCallback is true, you must defined template in options.
6459                                // note that data passed into _loadcallback is already an html (after processed in opts.template(data)).
6460                                if (opts.template !== undefined) {
6461                                    var theData = opts.template(data);
6462                                    box.append(theData);
6463                                    if (condition) {
6464                                        instance._loadcallback(box, theData);
6465                                    } else {
6466                                        instance._error('end');
6467                                    }
6468                                } else {
6469                                    instance._debug('template must be defined.');
6470                                    instance._error('end');
6471                                }
6472                            } else {
6473                                // if appendCallback is false, we will pass in the JSON object. you should handle it yourself in your callback.
6474                                if (condition) {
6475                                    instance._loadcallback(box, data, desturl);
6476                                } else {
6477                                    instance._error('end');
6478                                }
6479                            }
6480                        },
6481                        error: function() {
6482                            instance._debug('JSON ajax request failed.');
6483                            instance._error('end');
6484                        }
6485                    });
6486
6487                    break;
6488            }
6489        },
6490
6491        // Retrieve next set of content items
6492        retrieve: function infscr_retrieve(pageNum) {
6493            pageNum = pageNum || null;
6494
6495            var instance = this,
6496            opts = instance.options;
6497
6498            // if behavior is defined and this function is extended, call that instead of default
6499            if (!!opts.behavior && this['retrieve_'+opts.behavior] !== undefined) {
6500                this['retrieve_'+opts.behavior].call(this,pageNum);
6501                return;
6502            }
6503
6504            // for manual triggers, if destroyed, get out of here
6505            if (opts.state.isDestroyed) {
6506                this._debug('Instance is destroyed');
6507                return false;
6508            }
6509
6510            // we dont want to fire the ajax multiple times
6511            opts.state.isDuringAjax = true;
6512
6513            opts.loading.start.call($(opts.contentSelector)[0],opts);
6514        },
6515
6516        // Check to see next page is needed
6517        scroll: function infscr_scroll() {
6518
6519            var opts = this.options,
6520            state = opts.state;
6521
6522            // if behavior is defined and this function is extended, call that instead of default
6523            if (!!opts.behavior && this['scroll_'+opts.behavior] !== undefined) {
6524                this['scroll_'+opts.behavior].call(this);
6525                return;
6526            }
6527
6528            if (state.isDuringAjax || state.isInvalidPage || state.isDone || state.isDestroyed || state.isPaused) {
6529                return;
6530            }
6531
6532            if (!this._nearbottom()) {
6533                return;
6534            }
6535
6536            this.retrieve();
6537
6538        },
6539
6540        // Toggle pause value
6541        toggle: function infscr_toggle() {
6542            this._pausing();
6543        },
6544
6545        // Unbind from scroll
6546        unbind: function infscr_unbind() {
6547            this._binding('unbind');
6548        },
6549
6550        // update options
6551        update: function infscr_options(key) {
6552            if ($.isPlainObject(key)) {
6553                this.options = $.extend(true,this.options,key);
6554            }
6555        }
6556    };
6557
6558
6559    /*
6560        ----------------------------
6561        Infinite Scroll function
6562        ----------------------------
6563
6564        Borrowed logic from the following...
6565
6566        jQuery UI
6567        - https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js
6568
6569        jCarousel
6570        - https://github.com/jsor/jcarousel/blob/master/lib/jquery.jcarousel.js
6571
6572        Masonry
6573        - https://github.com/desandro/masonry/blob/master/jquery.masonry.js
6574
6575*/
6576
6577    $.fn.infinitescroll = function infscr_init(options, callback) {
6578
6579
6580        var thisCall = typeof options;
6581
6582        switch (thisCall) {
6583
6584            // method
6585            case 'string':
6586                var args = Array.prototype.slice.call(arguments, 1);
6587
6588                this.each(function () {
6589                    var instance = $.data(this, 'infinitescroll');
6590
6591                    if (!instance) {
6592                        // not setup yet
6593                        // return $.error('Method ' + options + ' cannot be called until Infinite Scroll is setup');
6594                        return false;
6595                    }
6596
6597                    if (!$.isFunction(instance[options]) || options.charAt(0) === '_') {
6598                        // return $.error('No such method ' + options + ' for Infinite Scroll');
6599                        return false;
6600                    }
6601
6602                    // no errors!
6603                    instance[options].apply(instance, args);
6604                });
6605
6606            break;
6607
6608            // creation
6609            case 'object':
6610
6611                this.each(function () {
6612
6613                var instance = $.data(this, 'infinitescroll');
6614
6615                if (instance) {
6616
6617                    // update options of current instance
6618                    instance.update(options);
6619
6620                } else {
6621
6622                    // initialize new instance
6623                    instance = new $.infinitescroll(options, callback, this);
6624
6625                    // don't attach if instantiation failed
6626                    if (!instance.failed) {
6627                        $.data(this, 'infinitescroll', instance);
6628                    }
6629
6630                }
6631
6632            });
6633
6634            break;
6635
6636        }
6637
6638        return this;
6639    };
6640
6641
6642
6643    /*
6644     * smartscroll: debounced scroll event for jQuery *
6645     * https://github.com/lukeshumard/smartscroll
6646     * Based on smartresize by @louis_remi: https://github.com/lrbabe/jquery.smartresize.js *
6647     * Copyright 2011 Louis-Remi & Luke Shumard * Licensed under the MIT license. *
6648     */
6649
6650    var event = $.event,
6651    scrollTimeout;
6652
6653    event.special.smartscroll = {
6654        setup: function () {
6655            $(this).bind('scroll', event.special.smartscroll.handler);
6656        },
6657        teardown: function () {
6658            $(this).unbind('scroll', event.special.smartscroll.handler);
6659        },
6660        handler: function (event, execAsap) {
6661            // Save the context
6662            var context = this,
6663            args = arguments;
6664
6665            // set correct event type
6666            event.type = 'smartscroll';
6667
6668            if (scrollTimeout) { clearTimeout(scrollTimeout); }
6669            scrollTimeout = setTimeout(function () {
6670                $(context).trigger('smartscroll', args);
6671            }, execAsap === 'execAsap' ? 0 : 100);
6672        }
6673    };
6674
6675    $.fn.smartscroll = function (fn) {
6676        return fn ? this.bind('smartscroll', fn) : this.trigger('smartscroll', ['execAsap']);
6677    };
6678
6679}));
6680
6681/*!
6682Waypoints - 4.0.1
6683Copyright © 2011-2016 Caleb Troughton
6684Licensed under the MIT license.
6685https://github.com/imakewebthings/waypoints/blob/master/licenses.txt
6686*/
6687(function() {
6688  'use strict'
6689
6690  var keyCounter = 0
6691  var allWaypoints = {}
6692
6693  /* http://imakewebthings.com/waypoints/api/waypoint */
6694  function Waypoint(options) {
6695    if (!options) {
6696      throw new Error('No options passed to Waypoint constructor')
6697    }
6698    if (!options.element) {
6699      throw new Error('No element option passed to Waypoint constructor')
6700    }
6701    if (!options.handler) {
6702      throw new Error('No handler option passed to Waypoint constructor')
6703    }
6704
6705    this.key = 'waypoint-' + keyCounter
6706    this.options = Waypoint.Adapter.extend({}, Waypoint.defaults, options)
6707    this.element = this.options.element
6708    this.adapter = new Waypoint.Adapter(this.element)
6709    this.callback = options.handler
6710    this.axis = this.options.horizontal ? 'horizontal' : 'vertical'
6711    this.enabled = this.options.enabled
6712    this.triggerPoint = null
6713    this.group = Waypoint.Group.findOrCreate({
6714      name: this.options.group,
6715      axis: this.axis
6716    })
6717    this.context = Waypoint.Context.findOrCreateByElement(this.options.context)
6718
6719    if (Waypoint.offsetAliases[this.options.offset]) {
6720      this.options.offset = Waypoint.offsetAliases[this.options.offset]
6721    }
6722    this.group.add(this)
6723    this.context.add(this)
6724    allWaypoints[this.key] = this
6725    keyCounter += 1
6726  }
6727
6728  /* Private */
6729  Waypoint.prototype.queueTrigger = function(direction) {
6730    this.group.queueTrigger(this, direction)
6731  }
6732
6733  /* Private */
6734  Waypoint.prototype.trigger = function(args) {
6735    if (!this.enabled) {
6736      return
6737    }
6738    if (this.callback) {
6739      this.callback.apply(this, args)
6740    }
6741  }
6742
6743  /* Public */
6744  /* http://imakewebthings.com/waypoints/api/destroy */
6745  Waypoint.prototype.destroy = function() {
6746    this.context.remove(this)
6747    this.group.remove(this)
6748    delete allWaypoints[this.key]
6749  }
6750
6751  /* Public */
6752  /* http://imakewebthings.com/waypoints/api/disable */
6753  Waypoint.prototype.disable = function() {
6754    this.enabled = false
6755    return this
6756  }
6757
6758  /* Public */
6759  /* http://imakewebthings.com/waypoints/api/enable */
6760  Waypoint.prototype.enable = function() {
6761    this.context.refresh()
6762    this.enabled = true
6763    return this
6764  }
6765
6766  /* Public */
6767  /* http://imakewebthings.com/waypoints/api/next */
6768  Waypoint.prototype.next = function() {
6769    return this.group.next(this)
6770  }
6771
6772  /* Public */
6773  /* http://imakewebthings.com/waypoints/api/previous */
6774  Waypoint.prototype.previous = function() {
6775    return this.group.previous(this)
6776  }
6777
6778  /* Private */
6779  Waypoint.invokeAll = function(method) {
6780    var allWaypointsArray = []
6781    for (var waypointKey in allWaypoints) {
6782      allWaypointsArray.push(allWaypoints[waypointKey])
6783    }
6784    for (var i = 0, end = allWaypointsArray.length; i < end; i++) {
6785      allWaypointsArray[i][method]()
6786    }
6787  }
6788
6789  /* Public */
6790  /* http://imakewebthings.com/waypoints/api/destroy-all */
6791  Waypoint.destroyAll = function() {
6792    Waypoint.invokeAll('destroy')
6793  }
6794
6795  /* Public */
6796  /* http://imakewebthings.com/waypoints/api/disable-all */
6797  Waypoint.disableAll = function() {
6798    Waypoint.invokeAll('disable')
6799  }
6800
6801  /* Public */
6802  /* http://imakewebthings.com/waypoints/api/enable-all */
6803  Waypoint.enableAll = function() {
6804    Waypoint.Context.refreshAll()
6805    for (var waypointKey in allWaypoints) {
6806      allWaypoints[waypointKey].enabled = true
6807    }
6808    return this
6809  }
6810
6811  /* Public */
6812  /* http://imakewebthings.com/waypoints/api/refresh-all */
6813  Waypoint.refreshAll = function() {
6814    Waypoint.Context.refreshAll()
6815  }
6816
6817  /* Public */
6818  /* http://imakewebthings.com/waypoints/api/viewport-height */
6819  Waypoint.viewportHeight = function() {
6820    return window.innerHeight || document.documentElement.clientHeight
6821  }
6822
6823  /* Public */
6824  /* http://imakewebthings.com/waypoints/api/viewport-width */
6825  Waypoint.viewportWidth = function() {
6826    return document.documentElement.clientWidth
6827  }
6828
6829  Waypoint.adapters = []
6830
6831  Waypoint.defaults = {
6832    context: window,
6833    continuous: true,
6834    enabled: true,
6835    group: 'default',
6836    horizontal: false,
6837    offset: 0
6838  }
6839
6840  Waypoint.offsetAliases = {
6841    'bottom-in-view': function() {
6842      return this.context.innerHeight() - this.adapter.outerHeight()
6843    },
6844    'right-in-view': function() {
6845      return this.context.innerWidth() - this.adapter.outerWidth()
6846    }
6847  }
6848
6849  window.Waypoint = Waypoint
6850}())
6851;(function() {
6852  'use strict'
6853
6854  function requestAnimationFrameShim(callback) {
6855    window.setTimeout(callback, 1000 / 60)
6856  }
6857
6858  var keyCounter = 0
6859  var contexts = {}
6860  var Waypoint = window.Waypoint
6861  var oldWindowLoad = window.onload
6862
6863  /* http://imakewebthings.com/waypoints/api/context */
6864  function Context(element) {
6865    this.element = element
6866    this.Adapter = Waypoint.Adapter
6867    this.adapter = new this.Adapter(element)
6868    this.key = 'waypoint-context-' + keyCounter
6869    this.didScroll = false
6870    this.didResize = false
6871    this.oldScroll = {
6872      x: this.adapter.scrollLeft(),
6873      y: this.adapter.scrollTop()
6874    }
6875    this.waypoints = {
6876      vertical: {},
6877      horizontal: {}
6878    }
6879
6880    element.waypointContextKey = this.key
6881    contexts[element.waypointContextKey] = this
6882    keyCounter += 1
6883    if (!Waypoint.windowContext) {
6884      Waypoint.windowContext = true
6885      Waypoint.windowContext = new Context(window)
6886    }
6887
6888    this.createThrottledScrollHandler()
6889    this.createThrottledResizeHandler()
6890  }
6891
6892  /* Private */
6893  Context.prototype.add = function(waypoint) {
6894    var axis = waypoint.options.horizontal ? 'horizontal' : 'vertical'
6895    this.waypoints[axis][waypoint.key] = waypoint
6896    this.refresh()
6897  }
6898
6899  /* Private */
6900  Context.prototype.checkEmpty = function() {
6901    var horizontalEmpty = this.Adapter.isEmptyObject(this.waypoints.horizontal)
6902    var verticalEmpty = this.Adapter.isEmptyObject(this.waypoints.vertical)
6903    var isWindow = this.element == this.element.window
6904    if (horizontalEmpty && verticalEmpty && !isWindow) {
6905      this.adapter.off('.waypoints')
6906      delete contexts[this.key]
6907    }
6908  }
6909
6910  /* Private */
6911  Context.prototype.createThrottledResizeHandler = function() {
6912    var self = this
6913
6914    function resizeHandler() {
6915      self.handleResize()
6916      self.didResize = false
6917    }
6918
6919    this.adapter.on('resize.waypoints', function() {
6920      if (!self.didResize) {
6921        self.didResize = true
6922        Waypoint.requestAnimationFrame(resizeHandler)
6923      }
6924    })
6925  }
6926
6927  /* Private */
6928  Context.prototype.createThrottledScrollHandler = function() {
6929    var self = this
6930    function scrollHandler() {
6931      self.handleScroll()
6932      self.didScroll = false
6933    }
6934
6935    this.adapter.on('scroll.waypoints', function() {
6936      if (!self.didScroll || Waypoint.isTouch) {
6937        self.didScroll = true
6938        Waypoint.requestAnimationFrame(scrollHandler)
6939      }
6940    })
6941  }
6942
6943  /* Private */
6944  Context.prototype.handleResize = function() {
6945    Waypoint.Context.refreshAll()
6946  }
6947
6948  /* Private */
6949  Context.prototype.handleScroll = function() {
6950    var triggeredGroups = {}
6951    var axes = {
6952      horizontal: {
6953        newScroll: this.adapter.scrollLeft(),
6954        oldScroll: this.oldScroll.x,
6955        forward: 'right',
6956        backward: 'left'
6957      },
6958      vertical: {
6959        newScroll: this.adapter.scrollTop(),
6960        oldScroll: this.oldScroll.y,
6961        forward: 'down',
6962        backward: 'up'
6963      }
6964    }
6965
6966    for (var axisKey in axes) {
6967      var axis = axes[axisKey]
6968      var isForward = axis.newScroll > axis.oldScroll
6969      var direction = isForward ? axis.forward : axis.backward
6970
6971      for (var waypointKey in this.waypoints[axisKey]) {
6972        var waypoint = this.waypoints[axisKey][waypointKey]
6973        if (waypoint.triggerPoint === null) {
6974          continue
6975        }
6976        var wasBeforeTriggerPoint = axis.oldScroll < waypoint.triggerPoint
6977        var nowAfterTriggerPoint = axis.newScroll >= waypoint.triggerPoint
6978        var crossedForward = wasBeforeTriggerPoint && nowAfterTriggerPoint
6979        var crossedBackward = !wasBeforeTriggerPoint && !nowAfterTriggerPoint
6980        if (crossedForward || crossedBackward) {
6981          waypoint.queueTrigger(direction)
6982          triggeredGroups[waypoint.group.id] = waypoint.group
6983        }
6984      }
6985    }
6986
6987    for (var groupKey in triggeredGroups) {
6988      triggeredGroups[groupKey].flushTriggers()
6989    }
6990
6991    this.oldScroll = {
6992      x: axes.horizontal.newScroll,
6993      y: axes.vertical.newScroll
6994    }
6995  }
6996
6997  /* Private */
6998  Context.prototype.innerHeight = function() {
6999    /*eslint-disable eqeqeq */
7000    if (this.element == this.element.window) {
7001      return Waypoint.viewportHeight()
7002    }
7003    /*eslint-enable eqeqeq */
7004    return this.adapter.innerHeight()
7005  }
7006
7007  /* Private */
7008  Context.prototype.remove = function(waypoint) {
7009    delete this.waypoints[waypoint.axis][waypoint.key]
7010    this.checkEmpty()
7011  }
7012
7013  /* Private */
7014  Context.prototype.innerWidth = function() {
7015    /*eslint-disable eqeqeq */
7016    if (this.element == this.element.window) {
7017      return Waypoint.viewportWidth()
7018    }
7019    /*eslint-enable eqeqeq */
7020    return this.adapter.innerWidth()
7021  }
7022
7023  /* Public */
7024  /* http://imakewebthings.com/waypoints/api/context-destroy */
7025  Context.prototype.destroy = function() {
7026    var allWaypoints = []
7027    for (var axis in this.waypoints) {
7028      for (var waypointKey in this.waypoints[axis]) {
7029        allWaypoints.push(this.waypoints[axis][waypointKey])
7030      }
7031    }
7032    for (var i = 0, end = allWaypoints.length; i < end; i++) {
7033      allWaypoints[i].destroy()
7034    }
7035  }
7036
7037  /* Public */
7038  /* http://imakewebthings.com/waypoints/api/context-refresh */
7039  Context.prototype.refresh = function() {
7040    /*eslint-disable eqeqeq */
7041    var isWindow = this.element == this.element.window
7042    /*eslint-enable eqeqeq */
7043    var contextOffset = isWindow ? undefined : this.adapter.offset()
7044    var triggeredGroups = {}
7045    var axes
7046
7047    this.handleScroll()
7048    axes = {
7049      horizontal: {
7050        contextOffset: isWindow ? 0 : contextOffset.left,
7051        contextScroll: isWindow ? 0 : this.oldScroll.x,
7052        contextDimension: this.innerWidth(),
7053        oldScroll: this.oldScroll.x,
7054        forward: 'right',
7055        backward: 'left',
7056        offsetProp: 'left'
7057      },
7058      vertical: {
7059        contextOffset: isWindow ? 0 : contextOffset.top,
7060        contextScroll: isWindow ? 0 : this.oldScroll.y,
7061        contextDimension: this.innerHeight(),
7062        oldScroll: this.oldScroll.y,
7063        forward: 'down',
7064        backward: 'up',
7065        offsetProp: 'top'
7066      }
7067    }
7068
7069    for (var axisKey in axes) {
7070      var axis = axes[axisKey]
7071      for (var waypointKey in this.waypoints[axisKey]) {
7072        var waypoint = this.waypoints[axisKey][waypointKey]
7073        var adjustment = waypoint.options.offset
7074        var oldTriggerPoint = waypoint.triggerPoint
7075        var elementOffset = 0
7076        var freshWaypoint = oldTriggerPoint == null
7077        var contextModifier, wasBeforeScroll, nowAfterScroll
7078        var triggeredBackward, triggeredForward
7079
7080        if (waypoint.element !== waypoint.element.window) {
7081          elementOffset = waypoint.adapter.offset()[axis.offsetProp]
7082        }
7083
7084        if (typeof adjustment === 'function') {
7085          adjustment = adjustment.apply(waypoint)
7086        }
7087        else if (typeof adjustment === 'string') {
7088          adjustment = parseFloat(adjustment)
7089          if (waypoint.options.offset.indexOf('%') > - 1) {
7090            adjustment = Math.ceil(axis.contextDimension * adjustment / 100)
7091          }
7092        }
7093
7094        contextModifier = axis.contextScroll - axis.contextOffset
7095        waypoint.triggerPoint = Math.floor(elementOffset + contextModifier - adjustment)
7096        wasBeforeScroll = oldTriggerPoint < axis.oldScroll
7097        nowAfterScroll = waypoint.triggerPoint >= axis.oldScroll
7098        triggeredBackward = wasBeforeScroll && nowAfterScroll
7099        triggeredForward = !wasBeforeScroll && !nowAfterScroll
7100
7101        if (!freshWaypoint && triggeredBackward) {
7102          waypoint.queueTrigger(axis.backward)
7103          triggeredGroups[waypoint.group.id] = waypoint.group
7104        }
7105        else if (!freshWaypoint && triggeredForward) {
7106          waypoint.queueTrigger(axis.forward)
7107          triggeredGroups[waypoint.group.id] = waypoint.group
7108        }
7109        else if (freshWaypoint && axis.oldScroll >= waypoint.triggerPoint) {
7110          waypoint.queueTrigger(axis.forward)
7111          triggeredGroups[waypoint.group.id] = waypoint.group
7112        }
7113      }
7114    }
7115
7116    Waypoint.requestAnimationFrame(function() {
7117      for (var groupKey in triggeredGroups) {
7118        triggeredGroups[groupKey].flushTriggers()
7119      }
7120    })
7121
7122    return this
7123  }
7124
7125  /* Private */
7126  Context.findOrCreateByElement = function(element) {
7127    return Context.findByElement(element) || new Context(element)
7128  }
7129
7130  /* Private */
7131  Context.refreshAll = function() {
7132    for (var contextId in contexts) {
7133      contexts[contextId].refresh()
7134    }
7135  }
7136
7137  /* Public */
7138  /* http://imakewebthings.com/waypoints/api/context-find-by-element */
7139  Context.findByElement = function(element) {
7140    return contexts[element.waypointContextKey]
7141  }
7142
7143  window.onload = function() {
7144    if (oldWindowLoad) {
7145      oldWindowLoad()
7146    }
7147    Context.refreshAll()
7148  }
7149
7150
7151  Waypoint.requestAnimationFrame = function(callback) {
7152    var requestFn = window.requestAnimationFrame ||
7153      window.mozRequestAnimationFrame ||
7154      window.webkitRequestAnimationFrame ||
7155      requestAnimationFrameShim
7156    requestFn.call(window, callback)
7157  }
7158  Waypoint.Context = Context
7159}())
7160;(function() {
7161  'use strict'
7162
7163  function byTriggerPoint(a, b) {
7164    return a.triggerPoint - b.triggerPoint
7165  }
7166
7167  function byReverseTriggerPoint(a, b) {
7168    return b.triggerPoint - a.triggerPoint
7169  }
7170
7171  var groups = {
7172    vertical: {},
7173    horizontal: {}
7174  }
7175  var Waypoint = window.Waypoint
7176
7177  /* http://imakewebthings.com/waypoints/api/group */
7178  function Group(options) {
7179    this.name = options.name
7180    this.axis = options.axis
7181    this.id = this.name + '-' + this.axis
7182    this.waypoints = []
7183    this.clearTriggerQueues()
7184    groups[this.axis][this.name] = this
7185  }
7186
7187  /* Private */
7188  Group.prototype.add = function(waypoint) {
7189    this.waypoints.push(waypoint)
7190  }
7191
7192  /* Private */
7193  Group.prototype.clearTriggerQueues = function() {
7194    this.triggerQueues = {
7195      up: [],
7196      down: [],
7197      left: [],
7198      right: []
7199    }
7200  }
7201
7202  /* Private */
7203  Group.prototype.flushTriggers = function() {
7204    for (var direction in this.triggerQueues) {
7205      var waypoints = this.triggerQueues[direction]
7206      var reverse = direction === 'up' || direction === 'left'
7207      waypoints.sort(reverse ? byReverseTriggerPoint : byTriggerPoint)
7208      for (var i = 0, end = waypoints.length; i < end; i += 1) {
7209        var waypoint = waypoints[i]
7210        if (waypoint.options.continuous || i === waypoints.length - 1) {
7211          waypoint.trigger([direction])
7212        }
7213      }
7214    }
7215    this.clearTriggerQueues()
7216  }
7217
7218  /* Private */
7219  Group.prototype.next = function(waypoint) {
7220    this.waypoints.sort(byTriggerPoint)
7221    var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
7222    var isLast = index === this.waypoints.length - 1
7223    return isLast ? null : this.waypoints[index + 1]
7224  }
7225
7226  /* Private */
7227  Group.prototype.previous = function(waypoint) {
7228    this.waypoints.sort(byTriggerPoint)
7229    var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
7230    return index ? this.waypoints[index - 1] : null
7231  }
7232
7233  /* Private */
7234  Group.prototype.queueTrigger = function(waypoint, direction) {
7235    this.triggerQueues[direction].push(waypoint)
7236  }
7237
7238  /* Private */
7239  Group.prototype.remove = function(waypoint) {
7240    var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
7241    if (index > -1) {
7242      this.waypoints.splice(index, 1)
7243    }
7244  }
7245
7246  /* Public */
7247  /* http://imakewebthings.com/waypoints/api/first */
7248  Group.prototype.first = function() {
7249    return this.waypoints[0]
7250  }
7251
7252  /* Public */
7253  /* http://imakewebthings.com/waypoints/api/last */
7254  Group.prototype.last = function() {
7255    return this.waypoints[this.waypoints.length - 1]
7256  }
7257
7258  /* Private */
7259  Group.findOrCreate = function(options) {
7260    return groups[options.axis][options.name] || new Group(options)
7261  }
7262
7263  Waypoint.Group = Group
7264}())
7265;(function() {
7266  'use strict'
7267
7268  var $ = window.jQuery
7269  var Waypoint = window.Waypoint
7270
7271  function JQueryAdapter(element) {
7272    this.$element = $(element)
7273  }
7274
7275  $.each([
7276    'innerHeight',
7277    'innerWidth',
7278    'off',
7279    'offset',
7280    'on',
7281    'outerHeight',
7282    'outerWidth',
7283    'scrollLeft',
7284    'scrollTop'
7285  ], function(i, method) {
7286    JQueryAdapter.prototype[method] = function() {
7287      var args = Array.prototype.slice.call(arguments)
7288      return this.$element[method].apply(this.$element, args)
7289    }
7290  })
7291
7292  $.each([
7293    'extend',
7294    'inArray',
7295    'isEmptyObject'
7296  ], function(i, method) {
7297    JQueryAdapter[method] = $[method]
7298  })
7299
7300  Waypoint.adapters.push({
7301    name: 'jquery',
7302    Adapter: JQueryAdapter
7303  })
7304  Waypoint.Adapter = JQueryAdapter
7305}())
7306;(function() {
7307  'use strict'
7308
7309  var Waypoint = window.Waypoint
7310
7311  function createExtension(framework) {
7312    return function() {
7313      var waypoints = []
7314      var overrides = arguments[0]
7315
7316      if (framework.isFunction(arguments[0])) {
7317        overrides = framework.extend({}, arguments[1])
7318        overrides.handler = arguments[0]
7319      }
7320
7321      this.each(function() {
7322        var options = framework.extend({}, overrides, {
7323          element: this
7324        })
7325        if (typeof options.context === 'string') {
7326          options.context = framework(this).closest(options.context)[0]
7327        }
7328        waypoints.push(new Waypoint(options))
7329      })
7330
7331      return waypoints
7332    }
7333  }
7334
7335  if (window.jQuery) {
7336    window.jQuery.fn.waypoint = createExtension(window.jQuery)
7337  }
7338  if (window.Zepto) {
7339    window.Zepto.fn.waypoint = createExtension(window.Zepto)
7340  }
7341}())
7342;
7343/*!
7344Waypoints Inview Shortcut - 4.0.1
7345Copyright © 2011-2016 Caleb Troughton
7346Licensed under the MIT license.
7347https://github.com/imakewebthings/waypoints/blob/master/licenses.txt
7348*/
7349(function() {
7350  'use strict'
7351
7352  function noop() {}
7353
7354  var Waypoint = window.Waypoint
7355
7356  /* http://imakewebthings.com/waypoints/shortcuts/inview */
7357  function Inview(options) {
7358    this.options = Waypoint.Adapter.extend({}, Inview.defaults, options)
7359    this.axis = this.options.horizontal ? 'horizontal' : 'vertical'
7360    this.waypoints = []
7361    this.element = this.options.element
7362    this.createWaypoints()
7363  }
7364
7365  /* Private */
7366  Inview.prototype.createWaypoints = function() {
7367    var configs = {
7368      vertical: [{
7369        down: 'enter',
7370        up: 'exited',
7371        offset: '100%'
7372      }, {
7373        down: 'entered',
7374        up: 'exit',
7375        offset: 'bottom-in-view'
7376      }, {
7377        down: 'exit',
7378        up: 'entered',
7379        offset: 0
7380      }, {
7381        down: 'exited',
7382        up: 'enter',
7383        offset: function() {
7384          return -this.adapter.outerHeight()
7385        }
7386      }],
7387      horizontal: [{
7388        right: 'enter',
7389        left: 'exited',
7390        offset: '100%'
7391      }, {
7392        right: 'entered',
7393        left: 'exit',
7394        offset: 'right-in-view'
7395      }, {
7396        right: 'exit',
7397        left: 'entered',
7398        offset: 0
7399      }, {
7400        right: 'exited',
7401        left: 'enter',
7402        offset: function() {
7403          return -this.adapter.outerWidth()
7404        }
7405      }]
7406    }
7407
7408    for (var i = 0, end = configs[this.axis].length; i < end; i++) {
7409      var config = configs[this.axis][i]
7410      this.createWaypoint(config)
7411    }
7412  }
7413
7414  /* Private */
7415  Inview.prototype.createWaypoint = function(config) {
7416    var self = this
7417    this.waypoints.push(new Waypoint({
7418      context: this.options.context,
7419      element: this.options.element,
7420      enabled: this.options.enabled,
7421      handler: (function(config) {
7422        return function(direction) {
7423          self.options[config[direction]].call(self, direction)
7424        }
7425      }(config)),
7426      offset: config.offset,
7427      horizontal: this.options.horizontal
7428    }))
7429  }
7430
7431  /* Public */
7432  Inview.prototype.destroy = function() {
7433    for (var i = 0, end = this.waypoints.length; i < end; i++) {
7434      this.waypoints[i].destroy()
7435    }
7436    this.waypoints = []
7437  }
7438
7439  Inview.prototype.disable = function() {
7440    for (var i = 0, end = this.waypoints.length; i < end; i++) {
7441      this.waypoints[i].disable()
7442    }
7443  }
7444
7445  Inview.prototype.enable = function() {
7446    for (var i = 0, end = this.waypoints.length; i < end; i++) {
7447      this.waypoints[i].enable()
7448    }
7449  }
7450
7451  Inview.defaults = {
7452    context: window,
7453    enabled: true,
7454    enter: noop,
7455    entered: noop,
7456    exit: noop,
7457    exited: noop
7458  }
7459
7460  Waypoint.Inview = Inview
7461}())
7462;
7463
7464/*
7465 * SmartMenus jQuery v0.9.6
7466 * http://www.smartmenus.org/
7467 *
7468 * Copyright 2014 Vasil Dinkov, Vadikom Web Ltd.
7469 * http://vadikom.com/
7470 *
7471 * Released under the MIT license:
7472 * http://www.opensource.org/licenses/MIT
7473 */
7474
7475(function($) {
7476
7477	var menuTrees = [],
7478		IE = !!window.createPopup, // we need to detect it, unfortunately
7479		IElt9 = IE && !document.defaultView,
7480		IElt8 = IE && !document.querySelector,
7481		IE6 = IE && typeof document.documentElement.currentStyle.minWidth == 'undefined',
7482		mouse = false, // optimize for touch by default - we will detect for mouse input
7483		mouseDetectionEnabled = false;
7484
7485	// Handle detection for mouse input (i.e. desktop browsers, tablets with a mouse, etc.)
7486	function initMouseDetection(disable) {
7487		if (!mouseDetectionEnabled && !disable) {
7488			// if we get two consecutive mousemoves within 2 pixels from each other and within 300ms, we assume a real mouse/cursor is present
7489			// 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)
7490			var firstTime = true,
7491				lastMove = null;
7492			$(document).bind({
7493				'mousemove.smartmenus_mouse': function(e) {
7494					var thisMove = { x: e.pageX, y: e.pageY, timeStamp: new Date().getTime() };
7495					if (lastMove) {
7496						var deltaX = Math.abs(lastMove.x - thisMove.x),
7497							deltaY = Math.abs(lastMove.y - thisMove.y);
7498	 					if ((deltaX > 0 || deltaY > 0) && deltaX <= 2 && deltaY <= 2 && thisMove.timeStamp - lastMove.timeStamp <= 300) {
7499							mouse = true;
7500							// 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
7501							if (firstTime) {
7502								var $a = $(e.target).closest('a');
7503								if ($a.is('a')) {
7504									$.each(menuTrees, function() {
7505										if ($.contains(this.$root[0], $a[0])) {
7506											this.itemEnter({ currentTarget: $a[0] });
7507											return false;
7508										}
7509									});
7510								}
7511								firstTime = false;
7512							}
7513						}
7514					}
7515					lastMove = thisMove;
7516				},
7517				'touchstart.smartmenus_mouse pointerover.smartmenus_mouse MSPointerOver.smartmenus_mouse': function(e) {
7518					if (!/^(4|mouse)$/.test(e.originalEvent.pointerType)) {
7519						mouse = false;
7520					}
7521				}
7522			});
7523			mouseDetectionEnabled = true;
7524		} else if (mouseDetectionEnabled && disable) {
7525			$(document).unbind('.smartmenus_mouse');
7526			mouseDetectionEnabled = false;
7527		}
7528	};
7529
7530	$.SmartMenus = function(elm, options) {
7531		this.$root = $(elm);
7532		this.opts = options;
7533		this.rootId = ''; // internal
7534		this.$subArrow = null;
7535		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)
7536		this.activatedItems = []; // stores last activated A's for each level
7537		this.visibleSubMenus = []; // stores visible sub menus UL's
7538		this.showTimeout = 0;
7539		this.hideTimeout = 0;
7540		this.scrollTimeout = 0;
7541		this.clickActivated = false;
7542		this.zIndexInc = 0;
7543		this.$firstLink = null; // we'll use these for some tests
7544		this.$firstSub = null; // at runtime so we'll cache them
7545		this.disabled = false;
7546		this.$disableOverlay = null;
7547		this.init();
7548	};
7549
7550	$.extend($.SmartMenus, {
7551		hideAll: function() {
7552			$.each(menuTrees, function() {
7553				this.menuHideAll();
7554			});
7555		},
7556		destroy: function() {
7557			while (menuTrees.length) {
7558				menuTrees[0].destroy();
7559			}
7560			initMouseDetection(true);
7561		},
7562		prototype: {
7563			init: function(refresh) {
7564				var self = this;
7565
7566				if (!refresh) {
7567					menuTrees.push(this);
7568
7569					this.rootId = (new Date().getTime() + Math.random() + '').replace(/\D/g, '');
7570
7571					if (this.$root.hasClass('sm-rtl')) {
7572						this.opts.rightToLeftSubMenus = true;
7573					}
7574
7575					// init root (main menu)
7576					this.$root
7577						.data('smartmenus', this)
7578						.attr('data-smartmenus-id', this.rootId)
7579						.dataSM('level', 1)
7580						.bind({
7581							'mouseover.smartmenus focusin.smartmenus': $.proxy(this.rootOver, this),
7582							'mouseout.smartmenus focusout.smartmenus': $.proxy(this.rootOut, this)
7583						})
7584						.delegate('a', {
7585							'mouseenter.smartmenus': $.proxy(this.itemEnter, this),
7586							'mouseleave.smartmenus': $.proxy(this.itemLeave, this),
7587							'mousedown.smartmenus': $.proxy(this.itemDown, this),
7588							'focus.smartmenus': $.proxy(this.itemFocus, this),
7589							'blur.smartmenus': $.proxy(this.itemBlur, this),
7590							'click.smartmenus': $.proxy(this.itemClick, this),
7591							'touchend.smartmenus': $.proxy(this.itemTouchEnd, this)
7592						});
7593
7594					var eNamespace = '.smartmenus' + this.rootId;
7595					// hide menus on tap or click outside the root UL
7596					if (this.opts.hideOnClick) {
7597						$(document).bind('touchstart' + eNamespace, $.proxy(this.docTouchStart, this))
7598							.bind('touchmove' + eNamespace, $.proxy(this.docTouchMove, this))
7599							.bind('touchend' + eNamespace, $.proxy(this.docTouchEnd, this))
7600							// for Opera Mobile < 11.5, webOS browser, etc. we'll check click too
7601							.bind('click' + eNamespace, $.proxy(this.docClick, this));
7602					}
7603					// hide sub menus on resize
7604					$(window).bind('resize' + eNamespace + ' orientationchange' + eNamespace, $.proxy(this.winResize, this));
7605					$(window).bind('scroll' + eNamespace + ' orientationchange' + eNamespace, $.proxy(this.winResize, this));
7606
7607					if (this.opts.subIndicators) {
7608						this.$subArrow = $('<span/>').addClass('sub-arrow');
7609						if (this.opts.subIndicatorsText) {
7610							this.$subArrow.html(this.opts.subIndicatorsText);
7611						}
7612					}
7613
7614					// make sure mouse detection is enabled
7615					initMouseDetection();
7616				}
7617
7618				// init sub menus
7619				this.$firstSub = this.$root.find('ul').each(function() { self.menuInit($(this)); }).eq(0);
7620
7621				this.$firstLink = this.$root.find('a').eq(0);
7622
7623				// find current item
7624				if (this.opts.markCurrentItem) {
7625					var reDefaultDoc = /(index|default)\.[^#\?\/]*/i,
7626						reHash = /#.*/,
7627						locHref = window.location.href.replace(reDefaultDoc, ''),
7628						locHrefNoHash = locHref.replace(reHash, '');
7629					this.$root.find('a').each(function() {
7630						var href = this.href.replace(reDefaultDoc, ''),
7631							$this = $(this);
7632						if (href == locHref || href == locHrefNoHash) {
7633							$this.addClass('current');
7634							if (self.opts.markCurrentTree) {
7635								$this.parents('li').each(function() {
7636									var $this = $(this);
7637									if ($this.dataSM('sub')) {
7638										$this.children('a').addClass('current');
7639									}
7640								});
7641							}
7642						}
7643					});
7644				}
7645			},
7646			destroy: function() {
7647				this.menuHideAll();
7648				this.$root
7649					.removeData('smartmenus')
7650					.removeAttr('data-smartmenus-id')
7651					.removeDataSM('level')
7652					.unbind('.smartmenus')
7653					.undelegate('.smartmenus');
7654				var eNamespace = '.smartmenus' + this.rootId;
7655				$(document).unbind(eNamespace);
7656				$(window).unbind(eNamespace);
7657				if (this.opts.subIndicators) {
7658					this.$subArrow = null;
7659				}
7660				var self = this;
7661				$.each(this.subMenus, function() {
7662					if (this.hasClass('mega-menu')) {
7663						this.find('ul').removeDataSM('in-mega');
7664					}
7665					if (this.dataSM('shown-before')) {
7666						if (IElt8) {
7667							this.children().css({ styleFloat: '', width: '' });
7668						}
7669						if (self.opts.subMenusMinWidth || self.opts.subMenusMaxWidth) {
7670							if (!IE6) {
7671								this.css({ width: '', minWidth: '', maxWidth: '' }).removeClass('sm-nowrap');
7672							} else {
7673								this.css({ width: '', overflowX: '', overflowY: '' }).children().children('a').css('white-space', '');
7674							}
7675						}
7676						if (this.dataSM('scroll-arrows')) {
7677							this.dataSM('scroll-arrows').remove();
7678						}
7679						this.css({ zIndex: '', top: '', left: '', marginLeft: '', marginTop: '', display: '' });
7680					}
7681					if (self.opts.subIndicators) {
7682						this.dataSM('parent-a').removeClass('has-submenu').children('span.sub-arrow').remove();
7683					}
7684					this.removeDataSM('shown-before')
7685						.removeDataSM('ie-shim')
7686						.removeDataSM('scroll-arrows')
7687						.removeDataSM('parent-a')
7688						.removeDataSM('level')
7689						.removeDataSM('beforefirstshowfired')
7690						.parent().removeDataSM('sub');
7691				});
7692				if (this.opts.markCurrentItem) {
7693					this.$root.find('a.current').removeClass('current');
7694				}
7695				this.$root = null;
7696				this.$firstLink = null;
7697				this.$firstSub = null;
7698				if (this.$disableOverlay) {
7699					this.$disableOverlay.remove();
7700					this.$disableOverlay = null;
7701				}
7702				menuTrees.splice($.inArray(this, menuTrees), 1);
7703			},
7704			disable: function(noOverlay) {
7705				if (!this.disabled) {
7706					this.menuHideAll();
7707					// display overlay over the menu to prevent interaction
7708					if (!noOverlay && !this.opts.isPopup && this.$root.is(':visible')) {
7709						var pos = this.$root.offset();
7710						this.$disableOverlay = $('<div class="sm-jquery-disable-overlay"/>').css({
7711							position: 'absolute',
7712							top: pos.top,
7713							left: pos.left,
7714							width: this.$root.outerWidth(),
7715							height: this.$root.outerHeight(),
7716							zIndex: this.getStartZIndex() + 1,
7717							opacity: 0
7718						}).appendTo(document.body);
7719					}
7720					this.disabled = true;
7721				}
7722			},
7723			docClick: function(e) {
7724				// hide on any click outside the menu or on a menu link
7725				if (this.visibleSubMenus.length && !$.contains(this.$root[0], e.target) || $(e.target).is('a')) {
7726					this.menuHideAll($(e.target));
7727				}
7728			},
7729			docTouchEnd: function(e) {
7730				if (!this.lastTouch) {
7731					return;
7732				}
7733				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))) {
7734					if (this.hideTimeout) {
7735						clearTimeout(this.hideTimeout);
7736						this.hideTimeout = 0;
7737					}
7738					// hide with a delay to prevent triggering accidental unwanted click on some page element
7739					var self = this;
7740					this.hideTimeout = setTimeout(function() { self.menuHideAll($(e.target)); }, 350);
7741				}
7742				this.lastTouch = null;
7743			},
7744			docTouchMove: function(e) {
7745				if (!this.lastTouch) {
7746					return;
7747				}
7748				var touchPoint = e.originalEvent.touches[0];
7749				this.lastTouch.x2 = touchPoint.pageX;
7750				this.lastTouch.y2 = touchPoint.pageY;
7751			},
7752			docTouchStart: function(e) {
7753				var touchPoint = e.originalEvent.touches[0];
7754				this.lastTouch = { x1: touchPoint.pageX, y1: touchPoint.pageY, target: touchPoint.target };
7755			},
7756			enable: function() {
7757				if (this.disabled) {
7758					if (this.$disableOverlay) {
7759						this.$disableOverlay.remove();
7760						this.$disableOverlay = null;
7761					}
7762					this.disabled = false;
7763				}
7764			},
7765			getHeight: function($elm) {
7766				return this.getOffset($elm, true);
7767			},
7768			// returns precise width/height float values in IE9+, FF4+, recent WebKit
7769			// http://vadikom.com/dailies/offsetwidth-offsetheight-useless-in-ie9-firefox4/
7770			getOffset: function($elm, height) {
7771				var old;
7772				if ($elm.css('display') == 'none') {
7773					old = { position: $elm[0].style.position, visibility: $elm[0].style.visibility };
7774					$elm.css({ position: 'absolute', visibility: 'hidden' }).show();
7775				}
7776				var defaultView = $elm[0].ownerDocument.defaultView,
7777					compStyle = defaultView && defaultView.getComputedStyle && defaultView.getComputedStyle($elm[0], null),
7778					val = compStyle && parseFloat(compStyle[height ? 'height' : 'width']);
7779				if (val) {
7780					val += parseFloat(compStyle[height ? 'paddingTop' : 'paddingLeft'])
7781	 					+ parseFloat(compStyle[height ? 'paddingBottom' : 'paddingRight'])
7782	 					+ parseInt(compStyle[height ? 'borderTopWidth' : 'borderLeftWidth'])
7783	 					+ parseInt(compStyle[height ? 'borderBottomWidth' : 'borderRightWidth']);
7784				} else {
7785					val = height ? $elm[0].offsetHeight : $elm[0].offsetWidth;
7786				}
7787				if (old) {
7788					$elm.hide().css(old);
7789				}
7790				return val;
7791			},
7792			getWidth: function($elm) {
7793				return this.getOffset($elm);
7794			},
7795			getStartZIndex: function() {
7796				var zIndex = parseInt(this.$root.css('z-index'));
7797				return !isNaN(zIndex) ? zIndex : 1;
7798			},
7799			handleEvents: function() {
7800				return !this.disabled && this.isCSSOn();
7801			},
7802			handleItemEvents: function($a) {
7803				return this.handleEvents() && !this.isLinkInMegaMenu($a);
7804			},
7805			isCollapsible: function() {
7806				return this.$firstSub.css('position') == 'static';
7807			},
7808			isCSSOn: function() {
7809				return this.$firstLink.css('display') == 'block' || this.$firstLink.css('display') == 'inline';
7810			},
7811			isFixed: function() {
7812				return this.$root.css('position') == 'fixed';
7813			},
7814			isLinkInMegaMenu: function($a) {
7815				return !$a.parent().parent().dataSM('level');
7816			},
7817			isTouchMode: function() {
7818				return !mouse || this.isCollapsible();
7819			},
7820			itemActivate: function($a) {
7821				var $li = $a.parent(),
7822					$ul = $li.parent(),
7823					level = $ul.dataSM('level');
7824				// 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
7825				if (level > 1 && (!this.activatedItems[level - 2] || this.activatedItems[level - 2][0] != $ul.dataSM('parent-a')[0])) {
7826					var self = this;
7827					$($ul.parentsUntil('[data-smartmenus-id]', 'ul').get().reverse()).add($ul).each(function() {
7828						self.itemActivate($(this).dataSM('parent-a'));
7829					});
7830				}
7831				// hide any visible deeper level sub menus
7832				if (this.visibleSubMenus.length > level) {
7833					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--) {
7834						this.menuHide(this.visibleSubMenus[i]);
7835					}
7836				}
7837				// save new active item and sub menu for this level
7838				this.activatedItems[level - 1] = $a;
7839				this.visibleSubMenus[level - 1] = $ul;
7840				if (this.$root.triggerHandler('activate.smapi', $a[0]) === false) {
7841					return;
7842				}
7843				// show the sub menu if this item has one
7844				var $sub = $li.dataSM('sub');
7845				if ($sub && (this.isTouchMode() || (!this.opts.showOnClick || this.clickActivated))) {
7846					this.menuShow($sub);
7847				}
7848			},
7849			itemBlur: function(e) {
7850				var $a = $(e.currentTarget);
7851				if (!this.handleItemEvents($a)) {
7852					return;
7853				}
7854				this.$root.triggerHandler('blur.smapi', $a[0]);
7855			},
7856			itemClick: function(e) {
7857				var $a = $(e.currentTarget);
7858
7859				if (!this.handleItemEvents($a)) {
7860					return;
7861				}
7862				$a.removeDataSM('mousedown');
7863				if (this.$root.triggerHandler('click.smapi', $a[0]) === false) {
7864					return false;
7865				}
7866				var $sub = $a.parent().dataSM('sub');
7867				if (this.isTouchMode()) {
7868					// undo fix: prevent the address bar on iPhone from sliding down when expanding a sub menu
7869					if ($a.dataSM('href')) {
7870						$a.attr('href', $a.dataSM('href')).removeDataSM('href');
7871					}
7872					// if the sub is not visible
7873					if ($sub && (!$sub.dataSM('shown-before') || !$sub.is(':visible'))) {
7874						// try to activate the item and show the sub
7875						this.itemActivate($a);
7876						// if "itemActivate" showed the sub, prevent the click so that the link is not loaded
7877						// 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
7878						if ($sub.is(':visible')) {
7879							return false;
7880						}
7881					}
7882				} else if (this.opts.showOnClick && $a.parent().parent().dataSM('level') == 1 && $sub) {
7883					this.clickActivated = true;
7884					this.menuShow($sub);
7885					return false;
7886				}
7887				if ($a.hasClass('disabled')) {
7888					return false;
7889				}
7890				if (this.$root.triggerHandler('select.smapi', $a[0]) === false) {
7891					return false;
7892				}
7893			},
7894			itemDown: function(e) {
7895				var $a = $(e.currentTarget);
7896				if (!this.handleItemEvents($a)) {
7897					return;
7898				}
7899				$a.dataSM('mousedown', true);
7900			},
7901			itemEnter: function(e) {
7902				var $a = $(e.currentTarget);
7903				if (!this.handleItemEvents($a)) {
7904					return;
7905				}
7906				if (!this.isTouchMode()) {
7907					if (this.showTimeout) {
7908						clearTimeout(this.showTimeout);
7909						this.showTimeout = 0;
7910					}
7911					var self = this;
7912					this.showTimeout = setTimeout(function() { self.itemActivate($a); }, this.opts.showOnClick && $a.parent().parent().dataSM('level') == 1 ? 1 : this.opts.showTimeout);
7913				}
7914				this.$root.triggerHandler('mouseenter.smapi', $a[0]);
7915			},
7916			itemFocus: function(e) {
7917				var $a = $(e.currentTarget);
7918				if (!this.handleItemEvents($a)) {
7919					return;
7920				}
7921				// 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
7922				if ((!this.isTouchMode() || !$a.dataSM('mousedown')) && (!this.activatedItems.length || this.activatedItems[this.activatedItems.length - 1][0] != $a[0])) {
7923					this.itemActivate($a);
7924				}
7925				this.$root.triggerHandler('focus.smapi', $a[0]);
7926			},
7927			itemLeave: function(e) {
7928				var $a = $(e.currentTarget);
7929				if (!this.handleItemEvents($a)) {
7930					return;
7931				}
7932				if (!this.isTouchMode()) {
7933					if ($a[0].blur) {
7934						$a[0].blur();
7935					}
7936					if (this.showTimeout) {
7937						clearTimeout(this.showTimeout);
7938						this.showTimeout = 0;
7939					}
7940				}
7941				$a.removeDataSM('mousedown');
7942				this.$root.triggerHandler('mouseleave.smapi', $a[0]);
7943			},
7944			itemTouchEnd: function(e) {
7945				var $a = $(e.currentTarget);
7946				if (!this.handleItemEvents($a)) {
7947					return;
7948				}
7949				// prevent the address bar on iPhone from sliding down when expanding a sub menu
7950				var $sub = $a.parent().dataSM('sub');
7951				if ($a.attr('href').charAt(0) !== '#' && $sub && (!$sub.dataSM('shown-before') || !$sub.is(':visible'))) {
7952					$a.dataSM('href', $a.attr('href'));
7953					$a.attr('href', '#');
7954				}
7955			},
7956			menuFixLayout: function($ul) {
7957				// fixes a menu that is being shown for the first time
7958				if (!$ul.dataSM('shown-before')) {
7959					$ul.hide().dataSM('shown-before', true);
7960					// fix the layout of the items in IE<8
7961					if (IElt8) {
7962						$ul.children().css({ styleFloat: 'left', width: '100%' });
7963					}
7964				}
7965			},
7966			menuHide: function($sub) {
7967				if (this.$root.triggerHandler('beforehide.smapi', $sub[0]) === false) {
7968					return;
7969				}
7970				$sub.stop(true, true);
7971				if ($sub.is(':visible')) {
7972					var complete = function() {
7973						// unset z-index
7974						if (IElt9) {
7975							$sub.parent().css('z-index', '');
7976						} else {
7977							$sub.css('z-index', '');
7978						}
7979					};
7980					// if sub is collapsible (mobile view)
7981					if (this.isCollapsible()) {
7982						if (this.opts.collapsibleHideFunction) {
7983							this.opts.collapsibleHideFunction.call(this, $sub, complete);
7984						} else {
7985							$sub.hide(this.opts.collapsibleHideDuration, complete);
7986						}
7987					} else {
7988						if (this.opts.hideFunction) {
7989							this.opts.hideFunction.call(this, $sub, complete);
7990						} else {
7991							$sub.hide(this.opts.hideDuration, complete);
7992						}
7993					}
7994					// remove IE iframe shim
7995					if ($sub.dataSM('ie-shim')) {
7996						$sub.dataSM('ie-shim').remove();
7997					}
7998					// deactivate scrolling if it is activated for this sub
7999					if ($sub.dataSM('scroll')) {
8000						$sub.unbind('.smartmenus_scroll').removeDataSM('scroll').dataSM('scroll-arrows').hide();
8001					}
8002					// unhighlight parent item
8003					$sub.dataSM('parent-a').removeClass('highlighted');
8004					var level = $sub.dataSM('level');
8005					this.activatedItems.splice(level - 1, 1);
8006					this.visibleSubMenus.splice(level - 1, 1);
8007					this.$root.triggerHandler('hide.smapi', $sub[0]);
8008				}
8009			},
8010			menuHideAll: function($item) {
8011				if ($item != undefined && $item.parent().hasClass('menu-item') && !$item.parent().hasClass('menu-item-has-children')) return;
8012				if ( $('body').hasClass('vmenu') ) return;
8013				if (this.showTimeout) {
8014					clearTimeout(this.showTimeout);
8015					this.showTimeout = 0;
8016				}
8017				// hide all subs
8018				for (var i = this.visibleSubMenus.length - 1; i > 0; i--) {
8019					this.menuHide(this.visibleSubMenus[i]);
8020				}
8021				// hide root if it's popup
8022				if (this.opts.isPopup) {
8023					this.$root.stop(true, true);
8024					if (this.$root.is(':visible')) {
8025						if (this.opts.hideFunction) {
8026							this.opts.hideFunction.call(this, this.$root);
8027						} else {
8028							this.$root.hide(this.opts.hideDuration);
8029						}
8030						// remove IE iframe shim
8031						if (this.$root.dataSM('ie-shim')) {
8032							this.$root.dataSM('ie-shim').remove();
8033						}
8034					}
8035				}
8036				this.activatedItems = [];
8037				this.visibleSubMenus = [];
8038				this.clickActivated = false;
8039				// reset z-index increment
8040				this.zIndexInc = 0;
8041			},
8042			menuIframeShim: function($ul) {
8043				// create iframe shim for the menu
8044				if (IE && this.opts.overlapControlsInIE && !$ul.dataSM('ie-shim')) {
8045					$ul.dataSM('ie-shim', $('<iframe/>').attr({ src: 'javascript:0', tabindex: -9 })
8046						.css({ position: 'absolute', top: 'auto', left: '0', opacity: 0, border: '0' })
8047					);
8048				}
8049			},
8050			menuInit: function($ul) {
8051				if (!$ul.dataSM('in-mega')) {
8052					this.subMenus.push($ul);
8053					// mark UL's in mega drop downs (if any) so we can neglect them
8054					if ($ul.hasClass('mega-menu')) {
8055						$ul.find('ul').dataSM('in-mega', true);
8056					}
8057					// get level (much faster than, for example, using parentsUntil)
8058					var level = 2,
8059						par = $ul[0];
8060					while ((par = par.parentNode.parentNode) != this.$root[0]) {
8061						level++;
8062					}
8063					// cache stuff
8064					$ul.dataSM('parent-a', $ul.prevAll('a').eq(-1))
8065						.dataSM('level', level)
8066						.parent().dataSM('sub', $ul);
8067					// add sub indicator to parent item
8068					if (this.opts.subIndicators) {
8069						$ul.dataSM('parent-a').addClass('has-submenu')[this.opts.subIndicatorsPos](this.$subArrow.clone());
8070					}
8071				}
8072			},
8073			menuPosition: function($sub) {
8074				var fixIE = $('html.ie').length;
8075				// if (fixIE) {
8076				// 	var $rowParent = $($sub).closest('.main-menu-container');
8077				// 	$rowParent.height($rowParent.height());
8078				// }
8079				var $a = $sub.dataSM('parent-a'),
8080					$li = $sub.parent(),
8081					$ul = $sub.parent().parent(),
8082					$container = $ul.closest('.row-menu-inner'),
8083					level = $sub.dataSM('level'),
8084					subW = this.getWidth($sub),
8085					subH = this.getHeight($sub),
8086					itemOffset = $a.offset(),
8087					itemX = itemOffset.left,
8088					itemY = itemOffset.top,
8089					itemW = this.getWidth($a),
8090					itemH = this.getHeight($a),
8091					$win = $(window),
8092					winX = $win.scrollLeft(),
8093					winY = $win.scrollTop(),
8094					winW = $win.width(),
8095					winH = $win.height(),
8096					containerW = $container.width(),
8097					containerOffsetX = containerW + ((winW - containerW) / 2),
8098					horizontalParent = $ul.hasClass('sm') && !$ul.hasClass('sm-vertical'),
8099					subOffsetX = level == 2 ? this.opts.mainMenuSubOffsetX : this.opts.subMenusSubOffsetX,
8100					subOffsetY = level == 2 ? this.opts.mainMenuSubOffsetY : this.opts.subMenusSubOffsetY,
8101					x, y, leftPos;
8102				if (horizontalParent) {
8103					x = this.opts.rightToLeftSubMenus ? itemW - subW - subOffsetX : subOffsetX;
8104					y = this.opts.bottomToTopSubMenus ? -subH - subOffsetY : itemH + subOffsetY;
8105				} else {
8106					x = this.opts.rightToLeftSubMenus ? subOffsetX - subW : itemW - subOffsetX;
8107					y = this.opts.bottomToTopSubMenus ? itemH - subOffsetY - subH : subOffsetY;
8108				}
8109				if (this.opts.keepInViewport && !this.isCollapsible()) {
8110					if (this.isFixed()) {
8111						itemX -= winX;
8112						itemY -= winY;
8113						winX = winY = 0;
8114					}
8115					var absX = itemX + x,
8116						absY = itemY + y;
8117					if (this.opts.rightToLeftSubMenus && absX < winX) {
8118						x = horizontalParent ? winX - absX + x : itemW - subOffsetX;
8119					} else if (!this.opts.rightToLeftSubMenus && absX + subW > winX + containerOffsetX ) {
8120						x = horizontalParent ? winX + containerOffsetX - subW - absX + x : subOffsetX - subW;
8121					}
8122					if (!horizontalParent) {
8123						if (subH < winH && absY + subH > winY + winH) {
8124							y += winY + winH - subH - absY;
8125						} else if (subH >= winH || absY < winY) {
8126							y += winY - absY;
8127						}
8128					}
8129					// do we need scrolling?
8130					// 0.49 added for the sake of IE9/FF4+ where we might be dealing with float numbers for "subH"
8131					if (mouse && (horizontalParent && (absY + subH > winY + winH + 0.49 || absY < winY) || !horizontalParent && subH > winH + 0.49)) {
8132						var self = this;
8133						if (!$sub.dataSM('scroll-arrows')) {
8134							$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]])
8135								.bind({
8136									mouseenter: function() { self.menuScroll($sub, $(this).hasClass('scroll-up')); },
8137									mouseleave: function(e) {
8138										self.menuScrollStop($sub);
8139										self.menuScrollOut($sub, e);
8140									},
8141									'mousewheel DOMMouseScroll': function(e) { e.preventDefault(); }
8142								})
8143								.insertAfter($sub)
8144							);
8145						}
8146						// bind events to show/hide arrows on hover and save scrolling data for this sub
8147						var vportY = winY - (itemY + itemH);
8148						$sub.dataSM('scroll', {
8149								vportY: vportY,
8150								subH: subH,
8151								winH: winH,
8152								step: 1
8153							})
8154							.bind({
8155								'mouseover.smartmenus_scroll': function(e) { self.menuScrollOver($sub, e); },
8156								'mouseout.smartmenus_scroll': function(e) { self.menuScrollOut($sub, e); },
8157								'mousewheel.smartmenus_scroll DOMMouseScroll.smartmenus_scroll': function(e) { self.menuScrollMousewheel($sub, e); }
8158							})
8159							.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
8159StartZIndex() + this.zIndexInc })
8160							.eq(0).css('margin-top', vportY).end()
8161							.eq(1).css('margin-top', vportY + winH - this.getHeight($sub.dataSM('scroll-arrows').eq(1))).end()
8162							.eq(horizontalParent && this.opts.bottomToTopSubMenus ? 0 : 1).show();
8163					}
8164				}
8165				// if ($sub.hasClass('mega-menu-inner')) {
8166				// 	if ($('.menu-wrapper .megamenu-diff').length > 0) {
8167				// 		leftPos = parseFloat($('.menu-wrapper .megamenu-diff').outerWidth());
8168				// 		leftPos = -leftPos;
8169				// 	} else leftPos = parseFloat($('.menu-wrapper .main-menu-container').css('paddingLeft'));
8170
8171				// 	if ($('body.header-full-width').length > 0) {
8172				// 		$sub.css({ width: $('.box-container').width()});
8173				// 		leftPos = (leftPos - parseFloat($('.menu-wrapper .row-menu-inner').css('paddingLeft')));
8174				// 	} else $sub.css({ width: containerW});
8175				// 	leftPos = leftPos + 'px';
8176
8177				// } else {
8178				// 	leftPos = $li.position().left + 'px';
8179				// }
8180				if ($sub.hasClass('mega-menu-inner')) {
8181					$sub.css({ width: containerW});
8182					if ( $('body.hmenu-center-double').length ) {
8183						leftPos = '0px';
8184					} else {
8185						if ( $('body.hmenu-center-split').length > 0 && !$sub.closest('.navbar-cta').length && ! $('body.hormenu-position-right').length ) {
8186							leftPos = - ($sub.closest('.menu-primary-inner')[0].offsetLeft) + 'px';
8187						} else {
8188							if ( $sub.closest('.navbar-cta').length && !$('body.hmenu').length && !$('body.hmenu-center').length ) {
8189								leftPos = - ( parseFloat($sub.closest('.navbar-cta').offset().left) - parseFloat($sub.closest('.row-menu-inner').find('.logo-container').offset().left) ) + 'px';
8190							} else if ($sub.closest('.row-menu-inner').find('.logo-container').length > 0 && !$('body.hmenu-center-split').length) {
8191								leftPos = - (parseFloat($sub.closest('.row-menu-inner').find('.logo-container').css('paddingRight')) + ($sub.closest('.row-menu-inner').find('.logo-container').width())) + 'px';
8192							} else {
8193								leftPos = '0px';
8194							}
8195						}
8196					}
8197				} else {
8198					leftPos = (level > 2 ? $li.position().left - parseFloat($li.closest('ul').css('paddingLeft')) : $li.position().left ) + 'px';
8199					x = (level > 2 && x >= 0) ? x + (parseFloat($li.closest('ul').css('paddingLeft')) * 2) + 1 : x - 1;
8200				}
8201
8202				$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) });
8203				// IE iframe shim
8204				this.menuIframeShim($sub);
8205				if ($sub.dataSM('ie-shim')) {
8206					$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) });
8207				}
8208			},
8209			menuScroll: function($sub, up, wheel) {
8210				var y = parseFloat($sub.css('margin-top')),
8211					scroll = $sub.dataSM('scroll'),
8212					end = scroll.vportY + (up ? 0 : scroll.winH - scroll.subH),
8213					step = wheel || !this.opts.scrollAccelerate ? this.opts.scrollStep : Math.floor($sub.dataSM('scroll').step);
8214				$sub.add($sub.dataSM('ie-shim')).css('margin-top', Math.abs(end - y) > step ? y + (up ? step : -step) : end);
8215				y = parseFloat($sub.css('margin-top'));
8216				// show opposite arrow if appropriate
8217				if (up && y + scroll.subH > scroll.vportY + scroll.winH || !up && y < scroll.vportY) {
8218					$sub.dataSM('scroll-arrows').eq(up ? 1 : 0).show();
8219				}
8220				// accelerate when not using mousewheel to scroll
8221				if (!wheel && this.opts.scrollAccelerate && $sub.dataSM('scroll').step < this.opts.scrollStep) {
8222					$sub.dataSM('scroll').step += 0.5;
8223				}
8224				// "y" and "end" might be float numbers in IE9/FF4+ so this weird way to check is used
8225				if (Math.abs(y - end) < 1) {
8226					$sub.dataSM('scroll-arrows').eq(up ? 0 : 1).hide();
8227					$sub.dataSM('scroll').step = 1;
8228				} else if (!wheel) {
8229					var self = this;
8230					this.scrollTimeout = setTimeout(function() { self.menuScroll($sub, up); }, this.opts.scrollInterval);
8231				}
8232			},
8233			menuScrollMousewheel: function($sub, e) {
8234				var $closestSub = $(e.target).closest('ul');
8235				while ($closestSub.dataSM('in-mega')) {
8236					$closestSub = $closestSub.parent().closest('ul');
8237				}
8238				if ($closestSub[0] == $sub[0]) {
8239					var up = (e.originalEvent.wheelDelta || -e.originalEvent.detail) > 0;
8240					if ($sub.dataSM('scroll-arrows').eq(up ? 0 : 1).is(':visible')) {
8241						this.menuScroll($sub, up, true);
8242					}
8243				}
8244				e.preventDefault();
8245			},
8246			menuScrollOut: function($sub, e) {
8247				var reClass = /^scroll-(up|down)/,
8248					$closestSub = $(e.relatedTarget).closest('ul');
8249				while ($closestSub.dataSM('in-mega')) {
8250					$closestSub = $closestSub.parent().closest('ul');
8251				}
8252				if (!reClass.test((e.relatedTarget || '').className) && ($sub[0] != e.relatedTarget && !$.contains($sub[0], e.relatedTarget) || $closestSub[0] != $sub[0])) {
8253					$sub.dataSM('scroll-arrows').css('visibility', 'hidden');
8254				}
8255			},
8256			menuScrollOver: function($sub, e) {
8257				var reClass = /^scroll-(up|down)/,
8258					$closestSub = $(e.target).closest('ul');
8259				while ($closestSub.dataSM('in-mega')) {
8260					$closestSub = $closestSub.parent().closest('ul');
8261				}
8262				if (!reClass.test(e.target.className) && $closestSub[0] == $sub[0]) {
8263					$sub.dataSM('scroll-arrows').css('visibility', 'visible');
8264				}
8265			},
8266			menuScrollStop: function($sub) {
8267				if (this.scrollTimeout) {
8268					clearTimeout(this.scrollTimeout);
8269					this.scrollTimeout = 0;
8270					$sub.dataSM('scroll').step = 1;
8271				}
8272			},
8273			menuShow: function($sub) {
8274				if (!$sub.dataSM('beforefirstshowfired')) {
8275					$sub.dataSM('beforefirstshowfired', true);
8276					if (this.$root.triggerHandler('beforefirstshow.smapi', $sub[0]) === false) {
8277						return;
8278					}
8279				}
8280				if (this.$root.triggerHandler('beforeshow.smapi', $sub[0]) === false) {
8281					return;
8282				}
8283				this.menuFixLayout($sub);
8284				$sub.stop(true, true);
8285				if (!$sub.is(':visible')) {
8286					// set z-index - for IE < 9 set it to the parent LI
8287					var zIndex = this.getStartZIndex() + (++this.zIndexInc);
8288					if (IElt9) {
8289						$sub.parent().css('z-index', zIndex);
8290					} else {
8291						$sub.css('z-index', zIndex);
8292					}
8293					// highlight parent item
8294					if (this.opts.keepHighlighted || this.isCollapsible()) {
8295						if ($sub.dataSM('parent-a').attr('data-type') != 'title')
8296							$sub.dataSM('parent-a').addClass('highlighted');
8297					}
8298					// min/max-width fix - no way to rely purely on CSS as all UL's are nested
8299					if (this.opts.subMenusMinWidth || this.opts.subMenusMaxWidth) {
8300						if (!IElt8) {
8301							$sub.css({ width: ($sub.hasClass('mega-menu-inner')) ? $('.box-container').outerWidth() + 'px' : 'auto', minWidth: '', maxWidth: '' }).addClass('sm-nowrap');
8302							if (this.opts.subMenusMinWidth) {
8303							 	$sub.css('min-width', this.opts.subMenusMinWidth);
8304							}
8305							if (this.opts.subMenusMaxWidth) {
8306							 	var noMaxWidth = this.getWidth($sub);
8307							 	$sub.css('max-width', this.opts.subMenusMaxWidth);
8308								if (noMaxWidth > this.getWidth($sub)) {
8309									$sub.removeClass('sm-nowrap').css('width', this.opts.subMenusMaxWidth);
8310								}
8311							}
8312						// IE6,7
8313 						} else {
8314							$sub.children().css('styleFloat', 'none');
8315							if (IE6) {
8316								$sub.width(this.opts.subMenusMinWidth ? this.opts.subMenusMinWidth : 1)
8317									.children().children('a').css('white-space', 'nowrap');
8318							} else { // IE7
8319								$sub.css({ width: ($sub.hasClass('mega-menu-inner')) ? $('.box-container').outerWidth() + 'px' : 'auto', minWidth: '', maxWidth: '' }).addClass('sm-nowrap');
8320								if (this.opts.subMenusMinWidth) {
8321							    $sub.css('min-width', this.opts.subMenusMinWidth);
8322								}
8323							}
8324							if (this.opts.subMenusMaxWidth) {
8325								var noMaxWidth = $sub.width();
8326								if (IE6) {
8327							 		var maxWidth = $sub.css({ width: this.opts.subMenusMaxWidth, overflowX: 'hidden', overflowY: 'hidden' }).width();
8328									if (noMaxWidth > maxWidth) {
8329										$sub.css({ width: maxWidth, overflowX: 'visible', overflowY: 'visible' }).children().children('a').css('white-space', '');
8330									} else {
8331										$sub.css({ width: noMaxWidth, overflowX: 'visible', overflowY: 'visible' });
8332									}
8333								} else { // IE7
8334									$sub.css('max-width', this.opts.subMenusMaxWidth);
8335									if (noMaxWidth > $sub.width()) {
8336										$sub.removeClass('sm-nowrap').css('width', this.opts.subMenusMaxWidth);
8337									} else {
8338										$sub.width(noMaxWidth);
8339									}
8340								}
8341							} else {
8342							 	$sub.width($sub.width());
8343							}
8344							$sub.children().css('styleFloat', 'left');
8345						}
8346					}
8347					this.menuPosition($sub);
8348					// insert IE iframe shim
8349					if ($sub.dataSM('ie-shim')) {
8350						$sub.dataSM('ie-shim').insertBefore($sub);
8351					}
8352					var complete = function() {
8353						// fix: "overflow: hidden;" is not reset on animation complete in jQuery < 1.9.0 in Chrome when global "box-sizing: border-box;" is used
8354						$sub.css('overflow', '');
8355					};
8356					// if sub is collapsible (mobile view)
8357					if (this.isCollapsible()) {
8358						if (this.opts.collapsibleShowFunction) {
8359							this.opts.collapsibleShowFunction.call(this, $sub, complete);
8360						} else {
8361							$sub.show(this.opts.collapsibleShowDuration, complete);
8362						}
8363					} else {
8364						if (this.opts.showFunction) {
8365							this.opts.showFunction.call(this, $sub, complete);
8366						} else {
8367							$sub.show(this.opts.showDuration, complete);
8368						}
8369					}
8370					// save new sub menu for this level
8371					this.visibleSubMenus[$sub.dataSM('level') - 1] = $sub;
8372					this.$root.triggerHandler('show.smapi', $sub[0]);
8373				}
8374			},
8375			popupHide: function(noHideTimeout) {
8376				if (this.hideTimeout) {
8377					clearTimeout(this.hideTimeout);
8378					this.hideTimeout = 0;
8379				}
8380				var self = this;
8381				this.hideTimeout = setTimeout(function() {
8382					self.menuHideAll();
8383				}, noHideTimeout ? 1 : this.opts.hideTimeout);
8384			},
8385			popupShow: function(left, top) {
8386				if (!this.opts.isPopup) {
8387					alert('SmartMenus jQuery Error:\n\nIf you want to show this menu via the "popupShow" method, set the isPopup:true option.');
8388					return;
8389				}
8390				if (this.hideTimeout) {
8391					clearTimeout(this.hideTimeout);
8392					this.hideTimeout = 0;
8393				}
8394				this.menuFixLayout(this.$root);
8395				this.$root.stop(true, true);
8396				if (!this.$root.is(':visible')) {
8397					this.$root.css({ left: left, top: top });
8398					// IE iframe shim
8399					this.menuIframeShim(this.$root);
8400					if (this.$root.dataSM('ie-shim')) {
8401						this.$root.dataSM('ie-shim').css({ zIndex: this.$root.css('z-index'), width: this.getWidth(this.$root), height: this.getHeight(this.$root), left
8401: left, top: top }).insertBefore(this.$root);
8402					}
8403					// show menu
8404					if (this.opts.showFunction) {
8405						this.opts.showFunction.call(this, this.$root);
8406					} else {
8407						this.$root.show(this.opts.showDuration);
8408					}
8409					this.visibleSubMenus[0] = this.$root;
8410				}
8411			},
8412			refresh: function() {
8413				this.menuHideAll();
8414				this.$root.find('ul').each(function() {
8415						var $this = $(this);
8416						if ($this.dataSM('scroll-arrows')) {
8417							$this.dataSM('scroll-arrows').remove();
8418						}
8419					})
8420					.removeDataSM('in-mega')
8421					.removeDataSM('shown-before')
8422					.removeDataSM('ie-shim')
8423					.removeDataSM('scroll-arrows')
8424					.removeDataSM('parent-a')
8425					.removeDataSM('level')
8426					.removeDataSM('beforefirstshowfired');
8427				this.$root.find('a.has-submenu').removeClass('has-submenu')
8428					.parent().removeDataSM('sub');
8429				if (this.opts.subIndicators) {
8430					this.$root.find('span.sub-arrow').remove();
8431				}
8432				if (this.opts.markCurrentItem) {
8433					this.$root.find('a.current').removeClass('current');
8434				}
8435				this.subMenus = [];
8436				this.init(true);
8437			},
8438			rootOut: function(e) {
8439				if (!this.handleEvents() || this.isTouchMode() || e.target == this.$root[0]) {
8440					return;
8441				}
8442				if (this.hideTimeout) {
8443					clearTimeout(this.hideTimeout);
8444					this.hideTimeout = 0;
8445				}
8446				if (!this.opts.showOnClick || !this.opts.hideOnClick) {
8447					var self = this;
8448					this.hideTimeout = setTimeout(function() { self.menuHideAll(); }, this.opts.hideTimeout);
8449				}
8450			},
8451			rootOver: function(e) {
8452				if (!this.handleEvents() || this.isTouchMode() || e.target == this.$root[0]) {
8453					return;
8454				}
8455				if (this.hideTimeout) {
8456					clearTimeout(this.hideTimeout);
8457					this.hideTimeout = 0;
8458				}
8459			},
8460			winResize: function(e) {
8461				if (!this.handleEvents()) {
8462					// we still need to resize the disable overlay if it's visible
8463					if (this.$disableOverlay) {
8464						var pos = this.$root.offset();
8465	 					this.$disableOverlay.css({
8466							top: pos.top,
8467							left: pos.left,
8468							width: this.$root.outerWidth(),
8469							height: this.$root.outerHeight()
8470						});
8471					}
8472					return;
8473				}
8474				// hide sub menus on resize - on mobile do it only on orientation change
8475				if (!this.isCollapsible() && (!('onorientationchange' in window) || e.type == 'orientationchange')) {
8476					if (this.activatedItems.length) {
8477						this.activatedItems[this.activatedItems.length - 1][0].blur();
8478					}
8479					this.menuHideAll();
8480				}
8481			}
8482		}
8483	});
8484
8485	$.fn.dataSM = function(key, val) {
8486		if (val) {
8487			return this.data(key + '_smartmenus', val);
8488		}
8489		return this.data(key + '_smartmenus');
8490	}
8491
8492	$.fn.removeDataSM = function(key) {
8493		return this.removeData(key + '_smartmenus');
8494	}
8495
8496	$.fn.smartmenus = function(options) {
8497		if (typeof options == 'string') {
8498			var args = arguments,
8499				method = options;
8500			Array.prototype.shift.call(args);
8501			return this.each(function() {
8502				var smartmenus = $(this).data('smartmenus');
8503				if (smartmenus && smartmenus[method]) {
8504					smartmenus[method].apply(smartmenus, args);
8505				}
8506			});
8507		}
8508		var opts = $.extend({}, $.fn.smartmenus.defaults, options);
8509		return this.each(function() {
8510			new $.SmartMenus(this, opts);
8511		});
8512	}
8513
8514	// default settings
8515	$.fn.smartmenus.defaults = {
8516		isPopup:		false,		// is this a popup menu (can be shown via the popupShow/popupHide methods) or a permanent menu bar
8517		mainMenuSubOffsetX:	0,		// pixels offset from default position
8518		mainMenuSubOffsetY:	0,		// pixels offset from default position
8519		subMenusSubOffsetX:	0,		// pixels offset from default position
8520		subMenusSubOffsetY:	0,		// pixels offset from default position
8521		subMenusMinWidth:	'10em',		// min-width for the sub menus (any CSS unit) - if set, the fixed width set in CSS will be ignored
8522		subMenusMaxWidth:	'20em',		// max-width for the sub menus (any CSS unit) - if set, the fixed width set in CSS will be ignored
8523		subIndicators: 		true,		// create sub menu indicators - creates a SPAN and inserts it in the A
8524		subIndicatorsPos: 	'prepend',	// position of the SPAN relative to the menu item content ('prepend', 'append')
8525		subIndicatorsText:	'+',		// [optionally] add text in the SPAN (e.g. '+') (you may want to check the CSS for the sub indicators too)
8526		scrollStep: 		30,		// pixels step when scrolling long sub menus that do not fit in the viewport height
8527		scrollInterval:		30,		// interval between each scrolling step
8528		scrollAccelerate:	true,		// accelerate scrolling or use a fixed step
8529		showTimeout:		250,		// timeout before showing the sub menus
8530		hideTimeout:		500,		// timeout before hiding the sub menus
8531		showDuration:		0,		// duration for show animation - set to 0 for no animation - matters only if showFunction:null
8532		showFunction:		null,		// custom function to use when showing a sub menu (the default is the jQuery 'show')
8533							// don't forget to call complete() at the end of whatever you do
8534							// e.g.: function($ul, complete) { $ul.fadeIn(250, complete); }
8535		hideDuration:		0,		// duration for hide animation - set to 0 for no animation - matters only if hideFunction:null
8536		hideFunction:		function($ul, complete) { $ul.fadeOut(200, complete); },	// custom function to use when hiding a sub menu (the default is the jQuery 'hide')
8537							// don't forget to call complete() at the end of whatever you do
8538							// e.g.: function($ul, complete) { $ul.fadeOut(250, complete); }
8539		collapsibleShowDuration:0,		// duration for show animation for collapsible sub menus - matters only if collapsibleShowFunction:null
8540		collapsibleShowFunction:function($ul, complete) { $ul.slideDown(200, complete); },	// custom function to use when showing a collapsible sub menu
8541							// (i.e. when mobile styles are used to make the sub menus collapsible)
8542		collapsibleHideDuration:0,		// duration for hide animation for collapsible sub menus - matters only if collapsibleHideFunction:null
8543		collapsibleHideFunction:function($ul, complete) { $ul.slideUp(200, complete); },	// custom function to use when hiding a collapsible sub menu
8544							// (i.e. when mobile styles are used to make the sub menus collapsible)
8545		showOnClick:		false,		// show the first-level sub menus onclick instead of onmouseover (matters only for mouse input)
8546		hideOnClick:		true,		// hide the sub menus on click/tap anywhere on the page
8547		keepInViewport:		true,		// reposition the sub menus if needed to make sure they always appear inside the viewport
8548		keepHighlighted:	true,		// keep all ancestor items of the current sub menu highlighted (adds the 'highlighted' class to the A's)
8549		markCurrentItem:	false,		// automatically add the 'current' class to the A element of the item linking to the current URL
8550		markCurrentTree:	true,		// add the 'current' class also to the A elements of all ancestor items of the current item
8551		rightToLeftSubMenus:	false,		// right to left display of the sub menus (check the CSS for the sub indicators' position)
8552		bottomToTopSubMenus:	false,		// bottom to top display of the sub menus
8553		overlapControlsInIE:	true		// make sure sub menus appear on top of special OS controls in IE (i.e. SELECT, OBJECT, EMBED, etc.)
8554	};
8555
8556})(jQuery);
8557
8558/*
8559 * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
8560 *
8561 * Uses the built in easing capabilities added In jQuery 1.1
8562 * to offer multiple easing options
8563 *
8564 * TERMS OF USE - jQuery Easing
8565 * 
8566 * Open source under the BSD License.
8567 * 
8568 * Copyright © 2008 George McGinley Smith
8569 * All rights reserved.
8570 * 
8571 * Redistribution and use in source and binary forms, with or without modification, 
8572 * are permitted provided that the following conditions are met:
8573 * 
8574 * Redistributions of source code must retain the above copyright notice, this list of 
8575 * conditions and the following disclaimer.
8576 * Redistributions in binary form must reproduce the above copyright notice, this list 
8577 * of conditions and the following disclaimer in the documentation and/or other materials 
8578 * provided with the distribution.
8579 * 
8580 * Neither the name of the author nor the names of contributors may be used to endorse 
8581 * or promote products derived from this software without specific prior written permission.
8582 * 
8583 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
8584 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
8585 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
8586 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
vendor: 5,091 bytes, lines 8587-8732
8587 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
8588 *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
8589 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
8590 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
8591 * OF THE POSSIBILITY OF SUCH DAMAGE. 
8592 *
8593*/
8594
8595// t: current time, b: begInnIng value, c: change In value, d: duration
8596jQuery.easing['jswing'] = jQuery.easing['swing'];
8597
8598jQuery.extend( jQuery.easing,
8599{
8600	def: 'easeOutQuad',
8601	swing: function (x, t, b, c, d) {
8602		//alert(jQuery.easing.default);
8603		return jQuery.easing[jQuery.easing.def](x, t, b, c, d);
8604	},
8605	easeInQuad: function (x, t, b, c, d) {
8606		return c*(t/=d)*t + b;
8607	},
8608	easeOutQuad: function (x, t, b, c, d) {
8609		return -c *(t/=d)*(t-2) + b;
8610	},
8611	easeInOutQuad: function (x, t, b, c, d) {
8612		if ((t/=d/2) < 1) return c/2*t*t + b;
8613		return -c/2 * ((--t)*(t-2) - 1) + b;
8614	},
8615	easeInCubic: function (x, t, b, c, d) {
8616		return c*(t/=d)*t*t + b;
8617	},
8618	easeOutCubic: function (x, t, b, c, d) {
8619		return c*((t=t/d-1)*t*t + 1) + b;
8620	},
8621	easeInOutCubic: function (x, t, b, c, d) {
8622		if ((t/=d/2) < 1) return c/2*t*t*t + b;
8623		return c/2*((t-=2)*t*t + 2) + b;
8624	},
8625	easeInQuart: function (x, t, b, c, d) {
8626		return c*(t/=d)*t*t*t + b;
8627	},
8628	easeOutQuart: function (x, t, b, c, d) {
8629		return -c * ((t=t/d-1)*t*t*t - 1) + b;
8630	},
8631	easeInOutQuart: function (x, t, b, c, d) {
8632		if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
8633		return -c/2 * ((t-=2)*t*t*t - 2) + b;
8634	},
8635	easeInQuint: function (x, t, b, c, d) {
8636		return c*(t/=d)*t*t*t*t + b;
8637	},
8638	easeOutQuint: function (x, t, b, c, d) {
8639		return c*((t=t/d-1)*t*t*t*t + 1) + b;
8640	},
8641	easeInOutQuint: function (x, t, b, c, d) {
8642		if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
8643		return c/2*((t-=2)*t*t*t*t + 2) + b;
8644	},
8645	easeInSine: function (x, t, b, c, d) {
8646		return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
8647	},
8648	easeOutSine: function (x, t, b, c, d) {
8649		return c * Math.sin(t/d * (Math.PI/2)) + b;
8650	},
8651	easeInOutSine: function (x, t, b, c, d) {
8652		return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
8653	},
8654	easeInExpo: function (x, t, b, c, d) {
8655		return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
8656	},
8657	easeOutExpo: function (x, t, b, c, d) {
8658		return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
8659	},
8660	easeInOutExpo: function (x, t, b, c, d) {
8661		if (t==0) return b;
8662		if (t==d) return b+c;
8663		if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
8664		return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
8665	},
8666	easeInCirc: function (x, t, b, c, d) {
8667		return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
8668	},
8669	easeOutCirc: function (x, t, b, c, d) {
8670		return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
8671	},
8672	easeInOutCirc: function (x, t, b, c, d) {
8673		if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
8674		return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
8675	},
8676	easeInElastic: function (x, t, b, c, d) {
8677		var s=1.70158;var p=0;var a=c;
8678		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
8679		if (a < Math.abs(c)) { a=c; var s=p/4; }
8680		else var s = p/(2*Math.PI) * Math.asin (c/a);
8681		return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
8682	},
8683	easeOutElastic: function (x, t, b, c, d) {
8684		var s=1.70158;var p=0;var a=c;
8685		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
8686		if (a < Math.abs(c)) { a=c; var s=p/4; }
8687		else var s = p/(2*Math.PI) * Math.asin (c/a);
8688		return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
8689	},
8690	easeInOutElastic: function (x, t, b, c, d) {
8691		var s=1.70158;var p=0;var a=c;
8692		if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5);
8693		if (a < Math.abs(c)) { a=c; var s=p/4; }
8694		else var s = p/(2*Math.PI) * Math.asin (c/a);
8695		if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
8696		return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
8697	},
8698	easeInBack: function (x, t, b, c, d, s) {
8699		if (s == undefined) s = 1.70158;
8700		return c*(t/=d)*t*((s+1)*t - s) + b;
8701	},
8702	easeOutBack: function (x, t, b, c, d, s) {
8703		if (s == undefined) s = 1.70158;
8704		return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
8705	},
8706	easeInOutBack: function (x, t, b, c, d, s) {
8707		if (s == undefined) s = 1.70158; 
8708		if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
8709		return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
8710	},
8711	easeInBounce: function (x, t, b, c, d) {
8712		return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b;
8713	},
8714	easeOutBounce: function (x, t, b, c, d) {
8715		if ((t/=d) < (1/2.75)) {
8716			return c*(7.5625*t*t) + b;
8717		} else if (t < (2/2.75)) {
8718			return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
8719		} else if (t < (2.5/2.75)) {
8720			return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
8721		} else {
8722			return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
8723		}
8724	},
8725	easeInOutBounce: function (x, t, b, c, d) {
8726		if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
8727		return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
8728	}
8729});
8730
8731/*
8732 *
8733 * TERMS OF USE - EASING EQUATIONS
8734 * 
8735 * Open source under the BSD License. 
8736 * 
8737 * Copyright © 2001 Robert Penner
8738 * All rights reserved.
8739 * 
8740 * Redistribution and use in source and binary forms, with or without modification, 
8741 * are permitted provided that the following conditions are met:
8742 * 
8743 * Redistributions of source code must retain the above copyright notice, this list of 
8744 * conditions and the following disclaimer.
8745 * Redistributions in binary form must reproduce the above copyright notice, this list 
8746 * of conditions and the following disclaimer in the documentation and/or other materials 
8747 * provided with the distribution.
8748 * 
8749 * Neither the name of the author nor the names of contributors may be used to endorse 
8750 * or promote products derived from this software without specific prior written permission.
8751 * 
8752 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
8753 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
8754 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
8755 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
8756 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
8757 *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
8758 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
8759 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
8760 * OF THE POSSIBILITY OF SUCH DAMAGE. 
8761 *
8762 */
8763(function($, window, undefined) {
8764    // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
8765    // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
8766 
8767    // requestAnimationFrame polyfill by Erik Möller
8768    // fixes from Paul Irish and Tino Zijdel
8769 
8770    var lastTime = 0,
8771        running,
8772        animate = function (elem) {
8773            if (running) {
8774                window.requestAnimationFrame(animate, elem);
8775                jQuery.fx.tick();
8776            }
8777        },
8778        vendors = ['ms', 'moz', 'webkit', 'o'];
8779 
8780    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
8781        window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
8782        window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
8783            || window[vendors[x]+'CancelRequestAnimationFrame'];
8784    }
8785 
8786    if (!window.requestAnimationFrame)
8787        window.requestAnimationFrame = function(fn, element) {
8788            var currTime = new Date().getTime(),
8789                delta = currTime - lastTime,
8790                timeToCall = Math.max(0, 16 - delta);
8791 
8792            var id = window.setTimeout(function() {
8793                    fn(currTime + timeToCall);
8794                },
8795                timeToCall
8796            );
8797 
8798            lastTime = currTime + timeToCall;
8799 
8800            return id;
8801        };
8802 
8803    if (!window.cancelAnimationFrame) {
8804        window.cancelAnimationFrame = function(id) {
8805            clearTimeout(id);
8806        };
8807    }
8808 
8809    jQuery.fx.timer = function (timer) {
8810        if (timer() && jQuery.timers.push(timer) && !running) {
8811            running = true;
8812            animate(timer.elem);
8813        }
8814    };
8815 
8816    jQuery.fx.stop = function() {
8817        running = false;
8818    };
8819 
8820}(jQuery, this));
8821/*! Copyright (c) 2011 Brandon Aaron (http://brandonaaron.net)
8822 * Licensed under the MIT License (LICENSE.txt).
8823 *
8824 * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
8825 * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
8826 * Thanks to: Seamus Leahy for adding deltaX and deltaY
8827 *
8828 * Version: 3.0.6
8829 *
8830 * Requires: 1.2.2+
8831 */
8832(function($) {
8833    var types = ['DOMMouseScroll', 'mousewheel'];
8834    if ($.event.fixHooks) {
8835        for (var i = types.length; i;) {
8836            $.event.fixHooks[types[--i]] = $.event.mouseHooks;
8837        }
8838    }
8839    $.event.special.mousewheel = {
8840        setup: function() {
8841            if (this.addEventListener) {
8842                for (var i = types.length; i;) {
8843                    this.addEventListener(types[--i], handler, false);
8844                }
8845            } else {
8846                this.onmousewheel = handler;
8847            }
8848        },
8849        teardown: function() {
8850            if (this.removeEventListener) {
8851                for (var i = types.length; i;) {
8852                    this.removeEventListener(types[--i], handler, false);
8853                }
8854            } else {
8855                this.onmousewheel = null;
8856            }
8857        }
8858    };
8859    $.fn.extend({
8860        mousewheel: function(fn) {
8861            return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
8862        },
8863        unmousewheel: function(fn) {
8864            return this.unbind("mousewheel", fn);
8865        }
8866    });
8867
8868    function handler(event) {
8869        var orgEvent = event || window.event,
8870            args = [].slice.call(arguments, 1),
8871            delta = 0,
8872            returnValue = true,
8873            deltaX = 0,
8874            deltaY = 0;
8875        event = $.event.fix(orgEvent);
8876        event.type = "mousewheel";
8877        // Old school scrollwheel delta
8878        if (orgEvent.wheelDelta) {
8879            delta = orgEvent.wheelDelta / 120;
8880        }
8881        if (orgEvent.detail) {
8882            delta = -orgEvent.detail / 3;
8883        }
8884        // New school multidimensional scroll (touchpads) deltas
8885        deltaY = delta;
8886        // Gecko
8887        if (orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS) {
8888            deltaY = 0;
8889            deltaX = -1 * delta;
8890        }
8891        // Webkit
8892        if (orgEvent.wheelDeltaY !== undefined) {
8893            deltaY = orgEvent.wheelDeltaY / 120;
8894        }
8895        if (orgEvent.wheelDeltaX !== undefined) {
8896            deltaX = -1 * orgEvent.wheelDeltaX / 120;
8897        }
8898        // Add event and delta to the front of the arguments
8899        args.unshift(event, delta, deltaX, deltaY);
8900        return ($.event.dispatch || $.event.handle).apply(this, args);
8901    }
8902})(jQuery);
8903/**
8904 * jQuery iLightBox - Revolutionary Lightbox Plugin
8905 * http://www.ilightbox.net/
8906 *
8907 * @version: 2.2.4 - October 14, 2017
8908 *
8909 * @author: iProDev (Hemn Chawroka)
8910 *          http://www.iprodev.com/
8911 *
8912 */
8913(function($, window, undefined) {
8914
8915	var extensions = {
8916			flash: ['swf'],
8917			image: ['bmp', 'gif', 'jpeg', 'jpg', 'png', 'tiff', 'tif', 'jfif', 'jpe'],
8918			iframe: ['asp', 'aspx', 'cgi', 'cfm', 'htm', 'html', 'jsp', 'php', 'pl', 'php3', 'php4', 'php5', 'phtml', 'rb', 'rhtml', 'shtml', 'txt'],
8919			video: ['avi', 'mov', 'mpg', 'mpeg', 'movie', 'mp4', 'webm', 'ogv', 'ogg', '3gp', 'm4v']
8920		},
8921
8922		// Global DOM elements
8923		$win = $(window),
8924		$doc = $(document),
8925
8926		// Support indicators
8927		browser,
8928		transform,
8929		gpuAcceleration,
8930		fullScreenApi = '',
8931		userAgent = navigator.userAgent || navigator.vendor || window.opera,
8932		supportTouch = !!('ontouchstart' in window) && (/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(userAgent)),
8933		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)),
8934
8935		// Events
8936		clickEvent = supportTouch ? "itap.iLightBox" : "click.iLightBox",
8937		touchStartEvent = supportTouch ? "touchstart.iLightBox" : "mousedown.iLightBox",
8938		touchStopEvent = supportTouch ? "touchend.iLightBox" : "mouseup.iLightBox",
8939		touchMoveEvent = supportTouch ? "touchmove.iLightBox" : "mousemove.iLightBox",
8940
8941		// Math shorthands
8942		abs = Math.abs,
8943		sqrt = Math.sqrt,
8944		round = Math.round,
8945		max = Math.max,
8946		min = Math.min,
8947		floor = Math.floor,
8948		random = Math.random,
8949
8950		pluginspages = {
8951			quicktime: 'http://www.apple.com/quicktime/download',
8952			flash: 'http://www.adobe.com/go/getflash'
8953		},
8954
8955		iLightBox = function(el, options, items, instant) {
8956			var iL = this;
8957
8958			iL.options = options,
8959				iL.selector = el.selector || el,
8960				iL.context = el.context,
8961				iL.instant = instant;
8962
8963			if (items.length < 1) iL.attachItems();
8964			else iL.items = items;
8965
8966			iL.vars = {
8967				total: iL.items.length,
8968				start: 0,
8969				current: null,
8970				next: null,
8971				prev: null,
8972				BODY: $('body'),
8973				loadRequests: 0,
8974				overlay: $('<div class="ilightbox-overlay"></div>'),
8975				loader: $('<div class="ilightbox-loader"><div></div></div>'),
8976				toolbar: $('<div class="ilightbox-toolbar"></div>'),
8977				innerToolbar: $('<div class="ilightbox-inner-toolbar"></div>'),
8978				title: $('<div class="ilightbox-title"></div>'),
8979				closeButton: $('<a class="ilightbox-close" title="' + iL.options.text.close + '"></a>'),
8980				fullScreenButton: $('<a class="ilightbox-fullscreen" title="' + iL.options.text.enterFullscreen + '"></a>'),
8981				innerPlayButton: $('<a class="ilightbox-play" title="' + iL.options.text.slideShow + '"></a>'),
8982				innerNextButton: $('<a class="ilightbox-next-button" title="' + iL.options.text.next + '"></a>'),
8983				innerPrevButton: $('<a class="ilightbox-prev-button" title="' + iL.options.text.previous + '"></a>'),
8984				holder: $('<div class="ilightbox-holder' + (supportTouch ? ' supportTouch' : '') + '" ondragstart="return false;"><div class="ilightbox-container"></div></div>'),
8985				nextPhoto: $('<div class="ilightbox-holder' + (supportTouch ? ' supportTouch' : '') + ' ilightbox-next" ondragstart="return false;"><div class="ilightbox-container"></div></div>'),
8986				prevPhoto: $('<div class="ilightbox-holder' + (supportTouch ? ' supportTouch' : '') + ' ilightbox-prev" ondragstart="return false;"><div class="ilightbox-container"></div></div>'),
8987				nextButton: $('<a class="ilightbox-button ilightbox-next-button" ondragstart="return false;" title="' + iL.options.text.next + '"><span></span></a>'),
8988				prevButton: $('<a class="ilightbox-button ilightbox-prev-button" ondragstart="return false;" title="' + iL.options.text.previous + '"><span></span></a>'),
8989				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>'),
8990				thumbs: false,
8991				nextLock: false,
8992				prevLock: false,
8993				hashLock: false,
8994				isMobile: false,
8995				mobileMaxWidth: 980,
8996				isInFullScreen: false,
8997				isSwipe: false,
8998				mouseID: 0,
8999				cycleID: 0,
9000				isPaused: 0,
9001				captionHeight: 39,
9002			};
9003
9004			// Hideable elements with mousemove event
9005			iL.vars.hideableElements = iL.vars.nextButton.add(iL.vars.prevButton);
9006
9007			iL.normalizeItems();
9008
9009			//Check necessary plugins
9010			iL.availPlugins();
9011
9012			//Set startFrom
9013			iL.options.startFrom = (iL.options.startFrom > 0 && iL.options.startFrom >= iL.vars.total) ? iL.vars.total - 1 : iL.options.startFrom;
9014
9015			//If randomStart
9016			iL.options.startFrom = (iL.options.randomStart) ? floor(random() * iL.vars.total) : iL.options.startFrom;
9017			iL.vars.start = iL.options.startFrom;
9018
9019			if (instant) iL.instantCall();
9020			else iL.patchItemsEvents();
9021
9022			if (iL.options.linkId) {
9023				iL.hashChangeHandler();
9024				$win.iLightBoxHashChange(function() {
9025					iL.hashChangeHandler();
9026				});
9027			}
9028
9029			if (supportTouch) {
9030				var RegExp = /(click|mouseenter|mouseleave|mouseover|mouseout)/ig,
9031					replace = "itap";
9032				iL.options.caption.show = iL.options.caption.show.replace(RegExp, replace),
9033					iL.options.caption.hide = iL.options.caption.hide.replace(RegExp, replace),
9034					iL.options.social.show = iL.options.social.show.replace(RegExp, replace),
9035					iL.options.social.hide = iL.options.social.hide.replace(RegExp, replace);
9036			}
9037
9038			if (iL.options.controls.arrows || $(window).width() < UNCODE.mediaQuery) {
9039				$.extend(iL.options.styles, {
9040					nextOffsetX: ($(window).width() > UNCODE.mediaQuery) ? 36 : 4,
9041					prevOffsetX: ($(window).width() > UNCODE.mediaQuery) ? 36 : 4,
9042					nextOpacity: 0,
9043					prevOpacity: 0
9044				});
9045			}
9046		};
9047
9048	//iLightBox helpers
9049	iLightBox.prototype = {
9050		showLoader: function() {
9051			var iL = this;
9052			iL.vars.loadRequests += 1;
9053			if (iL.options.path.toLowerCase() == "horizontal") iL.vars.loader.addClass('ilightbox-show').stop().animate({
9054				//top: '-30px'
9055				opacity: '1'
9056			}, iL.options.show.speed, 'easeOutCirc');
9057			else iL.vars.loader.addClass('ilightbox-show').stop().animate({
9058				//left: '-30px'
9059				opacity: '1'
9060			}, iL.options.show.speed, 'easeOutCirc');
9061		},
9062
9063		hideLoader: function() {
9064			var iL = this;
9065			iL.vars.loadRequests -= 1;
9066			iL.vars.loadRequests = (iL.vars.loadRequests < 0) ? 0 : iL.vars.loadRequests;
9067			if (iL.options.path.toLowerCase() == "horizontal") {
9068				if (iL.vars.loadRequests <= 0) iL.vars.loader.removeClass('ilightbox-show').stop().animate({
9069					//top: '-192px'
9070					opacity: '0'
9071				}, iL.options.show.speed, 'easeInCirc');
9072			} else {
9073				if (iL.vars.loadRequests <= 0) iL.vars.loader.removeClass('ilightbox-show').stop().animate({
9074					//left: '-192px'
9075					opacity: '0'
9076				}, iL.options.show.speed, 'easeInCirc');
9077			}
9078		},
9079
9080		createUI: function() {
9081			var iL = this;
9082
9083			iL.ui = {
9084				currentElement: iL.vars.holder,
9085				nextElement: iL.vars.nextPhoto,
9086				prevElement: iL.vars.prevPhoto,
9087				currentItem: iL.vars.current,
9088				nextItem: iL.vars.next,
9089				prevItem: iL.vars.prev,
9090				hide: function() {
9091					iL.closeAction();
9092				},
9093				refresh: function() {
9094					(arguments.length > 0) ? iL.repositionPhoto(true): iL.repositionPhoto();
9095				},
9096				fullscreen: function() {
9097					iL.fullScreenAction();
9098				}
9099			};
9100		},
9101
9102		attachItems: function() {
9103			var iL = this,
9104				itemsObject = new Array(),
9105				items = new Array();
9106
9107			$(iL.selector, iL.context).each(function() {
9108				var t = $(this),
9109					URL = t.attr(iL.options.attr) || null,
9110					options = t.data("options") && eval("({" + t.data("options") + "})") || {},
9111					caption = t.data('caption'),
9112					title = t.data('title'),
9113					type = t.data('type') || getTypeByExtension(URL),
9114					clone = t.data('lbox-clone') || false;
9115
9116				if (t.data('lbox-clone') != undefined) return;
9117
9118				//Uncode addition ##START##
9119				if ( typeof t.attr('data-album') != 'undefined' && t.attr('data-album') != '' ) {
9120					var ALBUM_URLS = JSON.parse(t.attr('data-album')),
9121						URL_i,
9122						URLS_LENGHT = ALBUM_URLS.length;
9123					for (URL_i = 0; URL_i < URLS_LENGHT; URL_i++) {
9124						if ( typeof ALBUM_URLS[URL_i].url !== 'undefined' && ALBUM_URLS[URL_i].url != '' ) {
9125							var item_opts = 'width:' + ALBUM_URLS[URL_i].width + ',height:' + ALBUM_URLS[URL_i].height + ',thumbnail: \'' + ALBUM_URLS[URL_i].thumbnail + '\'';
9126							item_opts = item_opts && eval("({" + item_opts + "})") || {},
9127							items.push({
9128								URL: ALBUM_URLS[URL_i].url,
9129								caption: ALBUM_URLS[URL_i].caption,
9130								title: ALBUM_URLS[URL_i].title,
9131								type: getTypeByExtension(ALBUM_URLS[URL_i].url),
9132								options: item_opts,
9133								clone: clone
9134							});
9135							var newT = t;
9136							newT.attr('href',ALBUM_URLS[URL_i].url);
9137							if (!iL.instant) itemsObject.push(newT);
9138						}
9139					}
9140				} else {
9141				//Uncode addition ##END##
9142					items.push({
9143						URL: URL,
9144						caption: caption,
9145						title: title,
9146						type: type,
9147						options: options,
9148						clone: clone
9149					});
9150				}
9151
9152
9153				if (iL.vars != undefined) iL.vars.total = items.length;
9154
9155				if (!iL.instant) itemsObject.push(t);
9156			});
9157
9158			iL.items = items,
9159				iL.itemsObject = itemsObject;
9160
9161		},
9162
9163		normalizeItems: function() {
9164			var iL = this,
9165				newItems = new Array();
9166
9167			$.each(iL.items, function(key, val) {
9168
9169				if (typeof val == "string") val = {
9170					url: val
9171				};
9172
9173				var URL = val.url || val.URL || null,
9174					options = val.options || {},
9175					caption = val.caption || null,
9176					title = val.title || null,
9177					type = (val.type) ? val.type.toLowerCase() : getTypeByExtension(URL),
9178					ext = (typeof URL != 'object') ? getExtension(URL) : '';
9179
9180				options.thumbnail = options.thumbnail || ((type == "image") ? URL : null),
9181				options.videoType = options.videoType || null,
9182				options.skin = options.skin || iL.options.skin,
9183				options.width = options.width || null,
9184				options.height = options.height || null,
9185				options.mousewheel = (typeof options.mousewheel != 'undefined') ? options.mousewheel : true,
9186				options.swipe = (typeof options.swipe != 'undefined') ? options.swipe : true,
9187				options.social = (typeof options.social != 'undefined') ? options.social : iL.options.social.buttons && $.extend({}, {}, iL.options.social.buttons);
9188
9189				if (type == "video") {
9190					options.html5video = (typeof options.html5video != 'undefined') ? options.html5video : {};
9191
9192					options.html5video.webm = options.html5video.webm || options.html5video.WEBM || null;
9193					options.html5video.controls = (typeof options.html5video.controls != 'undefined') ? options.html5video.controls : "controls";
9194					options.html5video.preload = options.html5video.preload || "metadata";
9195					options.html5video.autoplay = (typeof options.html5video.autoplay != 'undefined') ? options.html5video.autoplay : false;
9196					//UNCODE addition
9197					options.html5video.loop = (typeof options.html5video.loop != 'undefined') ? options.html5video.loop : false;
9198				}
9199
9200				if (!options.width || !options.height) {
9201					if (type == "video") options.width = 1280, options.height = 720;
9202					else if (type == "iframe") options.width = '100%', options.height = '90%';
9203					else if (type == "flash") options.width = 1280, options.height = 720;
9204				}
9205
9206				delete val.url;
9207				val.index = key;
9208				val.URL = URL;
9209				val.caption = caption;
9210				val.title = title;
9211				val.type = type;
9212				val.options = options;
9213				val.ext = ext;
9214
9215				newItems.push(val);
9216			});
9217
9218			iL.items = newItems;
9219		},
9220
9221		instantCall: function() {
9222			var iL = this,
9223				key = iL.vars.start;
9224
9225			iL.vars.current = key;
9226			iL.vars.next = (iL.items[key + 1]) ? key + 1 : null;
9227			iL.vars.prev = (iL.items[key - 1]) ? key - 1 : null;
9228
9229			iL.addContents();
9230			iL.patchEvents();
9231		},
9232
9233		addContents: function() {
9234			var iL = this,
9235				vars = iL.vars,
9236				opts = iL.options,
9237				viewport = getViewport(),
9238				path = opts.path.toLowerCase(),
9239				recognizingItems = vars.total > 0 && iL.items.filter(function(e, i, arr) {
9240					return ['image', 'flash', 'video'].indexOf(e.type) === -1 && typeof e.recognized === 'undefined' && (opts.smartRecognition || e.options.smartRecognition);
9241				}),
9242				needRecognition = recognizingItems.length > 0;
9243
9244			if (opts.mobileOptimizer && !opts.innerToolbar)
9245				vars.isMobile = viewport.width <= vars.mobileMaxWidth;
9246
9247			vars.overlay.addClass(opts.skin).hide().css('opacity', opts.overlay.opacity);
9248
9249			if (opts.linkId)
9250				vars.overlay[0].setAttribute('linkid', opts.linkId);
9251
9252			//Add Toolbar Buttons
9253			if (opts.controls.toolbar) {
9254				vars.toolbar.addClass(opts.skin).append(vars.closeButton);
9255				if (opts.controls.fullscreen)
9256					vars.toolbar.append(vars.fullScreenButton);
9257				if (opts.controls.slideshow)
9258					vars.toolbar.append(vars.innerPlayButton);
9259				if (vars.total > 1)
9260					vars.toolbar.append(vars.innerPrevButton).append(vars.innerNextButton);
9261			}
9262
9263			//Append elements to body
9264			vars.BODY.addClass('ilightbox-noscroll').append(vars.overlay).append(vars.loader).append(vars.holder).append(vars.nextPhoto).append(vars.prevPhoto);
9265
9266			if (!opts.innerToolbar)
9267				vars.BODY.append(vars.toolbar);
9268			if (opts.controls.arrows)
9269				vars.BODY.append(vars.nextButton).append(vars.prevButton);
9270
9271			if (opts.controls.thumbnail && vars.total > 1) {
9272				vars.BODY.append(vars.thumbnails);
9273				vars.thumbnails.addClass(opts.skin).addClass('ilightbox-' + path);
9274				$('div.ilightbox-thumbnails-grid', vars.thumbnails).empty();
9275				vars.thumbs = true;
9276			}
9277
9278			//Configure loader and arrows
9279			var loaderCss = (opts.path.toLowerCase() == "horizontal") ? {
9280				left: parseInt((viewport.width / 2) - (vars.loader.outerWidth() / 2))
9281			} : {
9282				top: parseInt((viewport.height / 2) - (vars.loader.outerHeight() / 2))
9283			};
9284			vars.loader.addClass(opts.skin).css(loaderCss);
9285			vars.nextButton.add(vars.prevButton).addClass(opts.skin);
9286			if (path == "horizontal")
9287				vars.loader.add(vars.nextButton).add(vars.prevButton).addClass('horizontal');
9288
9289			// Configure arrow buttons
9290			vars.BODY[vars.isMobile ? 'addClass' : 'removeClass']('isMobile');
9291
9292			if (!opts.infinite) {
9293				vars.prevButton.add(vars.prevButton).add(vars.innerPrevButton).add(vars.innerNextButton).removeClass('disabled');
9294
9295				if (vars.current == 0)
9296					vars.prevButton.add(vars.innerPrevButton).addClass('disabled');
9297				if (vars.current >= vars.total - 1)
9298					vars.nextButton.add(vars.innerNextButton).addClass('disabled');
9299			}
9300
9301			if (opts.show.effect) {
9302				vars.overlay.stop().fadeIn(opts.show.speed);
9303				vars.toolbar.stop().fadeIn(opts.show.speed);
9304			} else {
9305				vars.overlay.show();
9306				vars.toolbar.show();
9307			}
9308
9309			var length = recognizingItems.length;
9310			if (needRecognition) {
9311				iL.showLoader();
9312
9313				$.each(recognizingItems, function(key, val) {
9314					var resultFnc = function(result) {
9315							var key = -1,
9316								filter = iL.items.filter(function(e, i, arr) {
9317									if (e.URL == result.url)
9318										key = i;
9319
9320									return e.URL == result.url;
9321								}),
9322								self = iL.items[key];
9323
9324							if (result)
9325								$.extend(true, self, {
9326									URL: result.source,
9327									type: result.type,
9328									recognized: true,
9329									options: {
9330										html5video: result.html5video,
9331										width: (result.type == "image") ? 0 : (result.width || self.width),
9332										height: (result.type == "image") ? 0 : (result.height || self.height),
9333										thumbnail: self.options.thumbnail || result.thumbnail
9334									}
9335								});
9336
9337							length--;
9338
9339							if (length == 0) {
9340								iL.hideLoader();
9341
9342								vars.dontGenerateThumbs = false;
9343								iL.generateThumbnails();
9344
9345								if (opts.show.effect)
9346									setTimeout(function() {
9347										iL.generateBoxes();
9348									}, opts.show.speed);
9349								else
9350									iL.generateBoxes();
9351							}
9352						};
9353
9354					iL.ogpRecognition(this, resultFnc);
9355				});
9356			}
9357			else {
9358				if (opts.show.effect)
9359					setTimeout(function() {
9360						iL.generateBoxes();
9361					}, opts.show.speed);
9362				else
9363					iL.generateBoxes();
9364			}
9365
9366			iL.createUI();
9367
9368			window.iLightBox = {
9369				close: function() {
9370					iL.closeAction();
9371				},
9372				fullscreen: function() {
9373					iL.fullScreenAction();
9374				},
9375				moveNext: function() {
9376					iL.moveTo('next');
9377				},
9378				movePrev: function() {
9379					iL.moveTo('prev');
9380				},
9381				goTo: function(index) {
9382					iL.goTo(index);
9383				},
9384				refresh: function() {
9385					iL.refresh();
9386				},
9387				reposition: function() {
9388					(arguments.length > 0) ? iL.repositionPhoto(true): iL.repositionPhoto();
9389				},
9390				setOption: function(options) {
9391					iL.setOption(options);
9392				},
9393				destroy: function() {
9394					iL.closeAction();
9395					iL.dispatchItemsEvents();
9396				}
9397			};
9398
9399			if (opts.linkId) {
9400				vars.hashLock = true;
9401				window.location.hash = opts.linkId + '/' + vars.current;
9402				setTimeout(function() {
9403					vars.hashLock = false;
9404				}, 55);
9405			}
9406
9407			if (!opts.slideshow.startPaused) {
9408				iL.resume();
9409				vars.innerPlayButton.removeClass('ilightbox-play').addClass('ilightbox-pause');
9410			}
9411
9412			//Trigger the onOpen callback
9413			if (typeof iL.options.callback.onOpen == 'function') iL.options.callback.onOpen.call(iL);
9414		},
9415
9416		loadContent: function(obj, opt, speed) {
9417			var iL = this,
9418				holder, item;
9419
9420			iL.createUI();
9421
9422			obj.speed = speed || iL.options.effects.loadedFadeSpeed;
9423
9424			if (opt == 'current') {
9425				if (!obj.options.mousewheel) iL.vars.lockWheel = true;
9426				else iL.vars.lockWheel = false;
9427
9428				if (!obj.options.swipe) iL.vars.lockSwipe = true;
9429				else iL.vars.lockSwipe = false;
9430			}
9431
9432			switch (opt) {
9433				case 'current':
9434					holder = iL.vars.holder, item = iL.vars.current;
9435					break;
9436				case 'next':
9437					holder = iL.vars.nextPhoto, item = iL.vars.next;
9438					break;
9439				case 'prev':
9440					holder = iL.vars.prevPhoto, item = iL.vars.prev;
9441					break;
9442			}
9443
9444			holder.removeAttr('style class').addClass('ilightbox-holder' + (supportTouch ? ' supportTouch' : '')).addClass(obj.options.skin);
9445			$('div.ilightbox-inner-toolbar', holder).remove();
9446
9447			if (obj.title || iL.options.innerToolbar) {
9448				var innerToolbar = iL.vars.innerToolbar.clone();
9449				if (obj.title && iL.options.show.title) {
9450					var title = iL.vars.title.clone();
9451					title.empty().html(obj.title);
9452					innerToolbar.append(title);
9453				}
9454				if (iL.options.innerToolbar) {
9455					innerToolbar.append((iL.vars.total > 1) ? iL.vars.toolbar.clone() : iL.vars.toolbar);
9456				}
9457				holder.prepend(innerToolbar);
9458			}
9459
9460			//console.warn('loadContent', arguments);
9461
9462			iL.loadSwitcher(obj, holder, item, opt);
9463		},
9464
9465		loadSwitcher: function(obj, holder, item, opt) {
9466			var iL = this,
9467				opts = iL.options,
9468				api = {
9469					element: holder,
9470					position: item
9471				};
9472
9473			switch (obj.type) {
9474				case 'image':
9475					//Trigger the onBeforeLoad callback
9476					if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
9477					if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
9478
9479					iL.loadImage(obj.URL, function(img) {
9480						//Trigger the onAfterLoad callback
9481						if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
9482						if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
9483
9484						var width = (img) ? img.width : 400,
9485							height = (img) ? img.height : 200;
9486
9487						holder.data({
9488							naturalWidth: width,
9489							naturalHeight: height
9490						});
9491						$('div.ilightbox-container', holder).empty().append((img) ? '<img src="' + obj.URL + '" class="ilightbox-image" />' : '<span class="ilightbox-alert">' + opts.errors.loadImage + '</span>');
9492
9493						//Trigger the onRender callback
9494						if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
9495						if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
9496
9497						iL.configureHolder(obj, opt, holder);
9498					});
9499
9500					break;
9501
9502				case 'video':
9503					holder.data({
9504						naturalWidth: obj.options.width,
9505						naturalHeight: obj.options.height
9506					});
9507
9508					if (opt === 'current') {
9509						iL.addContent(holder, obj);
9510
9511						//Trigger the onRender callback
9512						if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
9513						if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
9514					} else {
9515						$('div.ilightbox-container', holder).empty();
9516					}
9517
9518					iL.configureHolder(obj, opt, holder);
9519
9520					break;
9521
9522				case 'iframe':
9523					//iL.showLoader();
9524					holder.data({
9525						naturalWidth: obj.options.width,
9526						naturalHeight: obj.options.height
9527					});
9528
9529					iL.configureHolder(obj, opt, holder);
9530
9531					if (opt === 'current') {
9532						var el = iL.addContent(holder, obj);
9533
9534						//Trigger the onRender callback
9535						if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
9536						if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
9537
9538						//Trigger the onBeforeLoad callback
9539						if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
9540						if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
9541
9542						el.bind('load', function() {
9543							//Trigger the onAfterLoad callback
9544							if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
9545							if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
9546
9547							//iL.hideLoader();
9548							el.unbind('load');
9549						});
9550					} else {
9551						$('div.ilightbox-container', holder).empty();
9552					}
9553
9554					break;
9555
9556				case 'inline':
9557					var el = $(obj.URL),
9558						content = iL.addContent(holder, obj),
9559						images = findImageInElement(holder);
9560
9561					holder.data({
9562						naturalWidth: (iL.items[item].options.width || el.outerWidth()),
9563						naturalHeight: (iL.items[item].options.height || el.outerHeight())
9564					});
9565					content.children().eq(0).show();
9566
9567					//Trigger the onRender callback
9568					if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
9569					if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
9570
9571					//Trigger the onBeforeLoad callback
9572					if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
9573					if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
9574
9575					iL.loadImage(images, function() {
9576						//Trigger the onAfterLoad callback
9577						if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
9578						if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
9579
9580						iL.configureHolder(obj, opt, holder);
9581					});
9582
9583					break;
9584
9585				case 'flash':
9586					var el = iL.addContent(holder, obj);
9587
9588					holder.data({
9589						naturalWidth: (iL.items[item].options.width || el.outerWidth()),
9590						naturalHeight: (iL.items[item].options.height || el.outerHeight())
9591					});
9592
9593					//Trigger the onRender callback
9594					if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
9595					if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
9596
9597					iL.configureHolder(obj, opt, holder);
9598
9599					break;
9600
9601				case 'ajax':
9602					var ajax = obj.options.ajax || {};
9603					//Trigger the onBeforeLoad callback
9604					if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
9605					if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
9606
9607					iL.showLoader();
9608					$.ajax({
9609						url: obj.URL || opts.ajaxSetup.url,
9610						data: ajax.data || null,
9611						dataType: ajax.dataType || "html",
9612						type: ajax.type || opts.ajaxSetup.type,
9613						cache: ajax.cache || opts.ajaxSetup.cache,
9614						crossDomain: ajax.crossDomain || opts.ajaxSetup.crossDomain,
9615						global: ajax.global || opts.ajaxSetup.global,
9616						ifModified: ajax.ifModified || opts.ajaxSetup.ifModified,
9617						username: ajax.username || opts.ajaxSetup.username,
9618						password: ajax.password || opts.ajaxSetup.password,
9619						beforeSend: ajax.beforeSend || opts.ajaxSetup.beforeSend,
9620						complete: ajax.complete || opts.ajaxSetup.complete,
9621						success: function(data, textStatus, jqXHR) {
9622							iL.hideLoader();
9623
9624							var el = $(data),
9625								container = $('div.ilightbox-container', holder),
9626								elWidth = iL.items[item].options.width || parseInt(el[0].getAttribute('width')),
9627								elHeight = iL.items[item].options.height || parseInt(el[0].getAttribute('height')),
9628								css = (el[0].getAttribute('width') && el[0].getAttribute('height')) ? {
9629									'overflow': 'hidden'
9630								} : {};
9631
9632							container.empty().append($('<div class="ilightbox-wrapper"></div>').css(css).html(el));
9633							holder.show().data({
9634								naturalWidth: (elWidth || container.outerWidth()),
9635								naturalHeight: (elHeight || container.outerHeight())
9636							}).hide();
9637
9638							//Trigger the onRender callback
9639							if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
9640							if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
9641
9642							var images = findImageInElement(holder);
9643							iL.loadImage(images, function() {
9644								//Trigger the onAfterLoad callback
9645								if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
9646								if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
9647
9648								iL.configureHolder(obj, opt, holder);
9649							});
9650
9651							opts.ajaxSetup.success(data, textStatus, jqXHR);
9652							if (typeof ajax.success == 'function') ajax.success(data, textStatus, jqXHR);
9653						},
9654						error: function(jqXHR, textStatus, errorThrown) {
9655							//Trigger the onAfterLoad callback
9656							if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
9657							if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
9658
9659							iL.hideLoader();
9660							$('div.ilightbox-container', holder).empty().append('<span class="ilightbox-alert">' + opts.errors.loadContents + '</span>');
9661							iL.configureHolder(obj, opt, holder);
9662
9663							opts.ajaxSetup.error(jqXHR, textStatus, errorThrown);
9664							if (typeof ajax.error == 'function') ajax.error(jqXHR, textStatus, errorThrown);
9665						}
9666					});
9667
9668					break;
9669
9670				case 'html':
9671					var object = obj.URL,
9672						el
9673					container = $('div.ilightbox-container', holder);
9674
9675					if (object[0].nodeName) el = object.clone();
9676					else {
9677						var dom = $(object);
9678						if (dom.selector) el = $('<div>' + dom + '</div>');
9679						else el = dom;
9680					}
9681
9682					var elWidth = iL.items[item].options.width || parseInt(el.attr('width')),
9683						elHeight = iL.items[item].options.height || parseInt(el.attr('height'));
9684
9685					iL.addContent(holder, obj);
9686
9687					el.appendTo(document.documentElement).hide();
9688
9689					//Trigger the onRender callback
9690					if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
9691					if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
9692
9693					var images = findImageInElement(holder);
9694
9695					//Trigger the onBeforeLoad callback
9696					if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
9697					if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
9698
9699					iL.loadImage(images, function() {
9700						//Trigger the onAfterLoad callback
9701						if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
9702						if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
9703
9704						holder.show().data({
9705							naturalWidth: (elWidth || container.outerWidth()),
9706							naturalHeight: (elHeight || container.outerHeight())
9707						}).hide();
9708						el.remove();
9709
9710						iL.configureHolder(obj, opt, holder);
9711					});
9712
9713					break;
9714			}
9715		},
9716
9717		configureHolder: function(obj, opt, holder) {
9718			var iL = this,
9719				vars = iL.vars,
9720				opts = iL.options;
9721
9722			if (opt != "current")(opt == "next") ? holder.addClass('ilightbox-next') : holder.addClass('ilightbox-prev');
9723
9724			if (opt == "current")
9725				var item = vars.current;
9726			else if (opt == "next")
9727				var opacity = opts.styles.nextOpacity,
9728					item = vars.next;
9729			else
9730				var opacity = opts.styles.prevOpacity,
9731					item = vars.prev;
9732
9733			var api = {
9734				element: holder,
9735				position: item
9736			};
9737
9738			iL.items[item].options.width = iL.items[item].options.width || 0,
9739			iL.items[item].options.height = iL.items[item].options.height || 0;
9740
9741			if (opt == "current") {
9742				if (opts.show.effect) holder.css(transform, gpuAcceleration).fadeIn(obj.speed, function() {
9743					holder.css(transform, '');
9744					if (obj.caption) {
9745						iL.setCaption(obj, holder);
9746						var caption = $('div.ilightbox-caption', holder),
9747							percent = parseInt((caption.outerHeight() / holder.outerHeight()) * 100);
9748						if (opts.caption.start & percent <= 50) caption.fadeIn(opts.effects.fade
9748Speed);
9749					}
9750
9751					var social = obj.options.social;
9752					if (social) {
9753						iL.setSocial(social, obj.URL, holder);
9754						if (opts.social.start) $('div.ilightbox-social', holder).fadeIn(opts.effects.fadeSpeed);
9755					}
9756
9757					//Generate thumbnails
9758					iL.generateThumbnails();
9759
9760					//Trigger the onShow callback
9761					if (typeof opts.callback.onShow == 'function') opts.callback.onShow.call(iL, iL.ui, item);
9762					if (typeof obj.options.onShow == 'function') obj.options.onShow.call(iL, api);
9763				});
9764				else {
9765					holder.show();
9766
9767					//Generate thumbnails
9768					iL.generateThumbnails();
9769
9770					//Trigger the onShow callback
9771					if (typeof opts.callback.onShow == 'function') opts.callback.onShow.call(iL, iL.ui, item);
9772					if (typeof obj.options.onShow == 'function') obj.options.onShow.call(iL, api);
9773				}
9774			} else {
9775				if (opts.show.effect) holder.fadeTo(obj.speed, opacity, function() {
9776					if (opt == "next") vars.nextLock = false;
9777					else vars.prevLock = false;
9778
9779					//Generate thumbnails
9780					iL.generateThumbnails();
9781
9782					//Trigger the onShow callback
9783					if (typeof opts.callback.onShow == 'function') opts.callback.onShow.call(iL, iL.ui, item);
9784					if (typeof obj.options.onShow == 'function') obj.options.onShow.call(iL, api);
9785				});
9786				else {
9787					holder.css({
9788						opacity: opacity
9789					}).show();
9790					if (opt == "next") vars.nextLock = false;
9791					else vars.prevLock = false;
9792
9793					//Generate thumbnails
9794					iL.generateThumbnails();
9795
9796					//Trigger the onShow callback
9797					if (typeof opts.callback.onShow == 'function') opts.callback.onShow.call(iL, iL.ui, item);
9798					if (typeof obj.options.onShow == 'function') obj.options.onShow.call(iL, api);
9799				}
9800			}
9801
9802			setTimeout(function() {
9803				iL.repositionPhoto();
9804			}, 0);
9805		},
9806
9807		generateBoxes: function() {
9808			var iL = this,
9809				vars = iL.vars,
9810				opts = iL.options;
9811
9812			if (opts.infinite && vars.total >= 3) {
9813				if (vars.current == vars.total - 1) vars.next = 0;
9814				if (vars.current == 0) vars.prev = vars.total - 1;
9815			} else opts.infinite = false;
9816
9817			//UNCODE addition
9818			if ( typeof iL.items[vars.current] == 'undefined' )
9819				vars.current = 0;
9820
9821			iL.loadContent(iL.items[vars.current], 'current', opts.show.speed);
9822
9823			if (iL.items[vars.next]) iL.loadContent(iL.items[vars.next], 'next', opts.show.speed);
9824			if (iL.items[vars.prev]) iL.loadContent(iL.items[vars.prev], 'prev', opts.show.speed);
9825		},
9826
9827		generateThumbnails: function() {
9828			var iL = this,
9829				vars = iL.vars,
9830				opts = iL.options,
9831				timeOut = null;
9832
9833			if (vars.thumbs && !iL.vars.dontGenerateThumbs) {
9834				var thumbnails = vars.thumbnails,
9835					container = $('div.ilightbox-thumbnails-container', thumbnails),
9836					grid = $('div.ilightbox-thumbnails-grid', container),
9837					i = 0;
9838
9839				grid.removeAttr('style').empty();
9840
9841				$.each(iL.items, function(key, val) {
9842					var isActive = (vars.current == key) ? 'ilightbox-active' : '',
9843						opacity = (vars.current == key) ? opts.thumbnails.activeOpacity : opts.thumbnails.normalOpacity,
9844						thumb = val.options.thumbnail,
9845						thumbnail = $('<div class="ilightbox-thumbnail"></div>'),
9846						icon = $('<div class="ilightbox-thumbnail-icon"></div>');
9847
9848					thumbnail.css({
9849						opacity: 0
9850					}).addClass(isActive);
9851
9852					if ((val.type == "video" || val.type == "flash") && typeof val.options.icon == 'undefined') {
9853						icon.addClass('ilightbox-thumbnail-video');
9854						thumbnail.append(icon);
9855					} else if (val.options.icon) {
9856						icon.addClass('ilightbox-thumbnail-' + val.options.icon);
9857						thumbnail.append(icon);
9858					}
9859
9860					if (thumb) iL.loadImage(thumb, function(img) {
9861						i++;
9862						if (img) thumbnail.data({
9863							naturalWidth: img.width,
9864							naturalHeight: img.height
9865						}).append('<img src="' + thumb + '" border="0" />');
9866						else thumbnail.data({
9867							naturalWidth: opts.thumbnails.maxWidth,
9868							naturalHeight: opts.thumbnails.maxHeight
9869						});
9870
9871						clearTimeout(timeOut);
9872						timeOut = setTimeout(function() {
9873							iL.positionThumbnails(thumbnails, container, grid);
9874						}, 20);
9875						setTimeout(function() {
9876							thumbnail.fadeTo(opts.effects.loadedFadeSpeed, opacity);
9877						}, i * 20);
9878					});
9879
9880					grid.append(thumbnail);
9881				});
9882				iL.vars.dontGenerateThumbs = true;
9883			}
9884		},
9885
9886		positionThumbnails: function(thumbnails, container, grid) {
9887			var iL = this,
9888				vars = iL.vars,
9889				opts = iL.options,
9890				viewport = getViewport(),
9891				path = opts.path.toLowerCase();
9892
9893			if (!thumbnails) thumbnails = vars.thumbnails;
9894			if (!container) container = $('div.ilightbox-thumbnails-container', thumbnails);
9895			if (!grid) grid = $('div.ilightbox-thumbnails-grid', container);
9896
9897			var thumbs = $('.ilightbox-thumbnail', grid),
9898				widthAvail = (path == 'horizontal') ? viewport.width - opts.styles.pageOffsetX : thumbs.eq(0).outerWidth() - opts.styles.pageOffsetX,
9899				heightAvail = (path == 'horizontal') ? thumbs.eq(0).outerHeight() - opts.styles.pageOffsetY : viewport.height - opts.styles.pageOffsetY,
9900				gridWidth = (path == 'horizontal') ? 0 : widthAvail,
9901				gridHeight = (path == 'horizontal') ? heightAvail : 0,
9902				active = $('.ilightbox-active', grid),
9903				gridCss = {},
9904				css = {};
9905
9906			if (arguments.length < 3) {
9907				thumbs.css({
9908					opacity: opts.thumbnails.normalOpacity
9909				});
9910				active.css({
9911					opacity: opts.thumbnails.activeOpacity
9912				});
9913			}
9914
9915			thumbs.each(function(i) {
9916				var t = $(this),
9917					data = t.data(),
9918					width = (path == 'horizontal') ? 0 : opts.thumbnails.maxWidth;
9919				height = (path == 'horizontal') ? opts.thumbnails.maxHeight : 0;
9920				dims = iL.getNewDimenstions(width, height, data.naturalWidth, data.naturalHeight, true);
9921
9922				t.css({
9923					width: dims.width,
9924					height: dims.height
9925				});
9926				if (path == 'horizontal') t.css({
9927					'float': 'left'
9928				});
9929
9930				(path == 'horizontal') ? (
9931					gridWidth += t.outerWidth()
9932				) : (
9933					gridHeight += t.outerHeight()
9934				);
9935			});
9936
9937			gridCss = {
9938				width: gridWidth,
9939				height: gridHeight
9940			};
9941
9942			grid.css(gridCss);
9943
9944			gridCss = {};
9945
9946			var gridOffset = grid.offset(),
9947				activeOffset = (active.length) ? active.offset() : {
9948					top: parseInt(heightAvail / 2),
9949					left: parseInt(widthAvail / 2)
9950				};
9951
9952			gridOffset.top = (gridOffset.top - $doc.scrollTop()),
9953				gridOffset.left = (gridOffset.left - $doc.scrollLeft()),
9954				activeOffset.top = (activeOffset.top - gridOffset.top - $doc.scrollTop()),
9955				activeOffset.left = (activeOffset.left - gridOffset.left - $doc.scrollLeft());
9956
9957			(path == 'horizontal') ? (
9958				gridCss.top = 0,
9959				gridCss.left = parseInt((widthAvail / 2) - activeOffset.left - (active.outerWidth() / 2))
9960			) : (
9961				gridCss.top = parseInt(((heightAvail / 2) - activeOffset.top - (active.outerHeight() / 2))),
9962				gridCss.left = 0
9963			);
9964
9965			if (arguments.length < 3) grid.stop().animate(gridCss, opts.effects.repositionSpeed, 'easeOutCirc');
9966			else grid.css(gridCss);
9967		},
9968
9969		loadImage: function(image, callback) {
9970			if (!$.isArray(image)) image = [image];
9971
9972			var iL = this,
9973				length = image.length;
9974
9975			if (length > 0) {
9976				iL.showLoader();
9977				$.each(image, function(index, value) {
9978					var img = new Image();
9979					img.onload = function() {
9980						length -= 1;
9981						if (length == 0) {
9982							iL.hideLoader();
9983							callback(img);
9984						}
9985					};
9986					img.onerror = img.onabort = function() {
9987						length -= 1;
9988						if (length == 0) {
9989							iL.hideLoader();
9990							callback(false);
9991						}
9992					};
9993					img.src = image[index];
9994				});
9995			} else callback(false);
9996		},
9997
9998		patchItemsEvents: function() {
9999			var iL = this,
10000				vars = iL.vars,
10001				clickEvent = supportTouch ? "itap.iL" : "click.iL",
10002				vEvent = supportTouch ? "click.iL" : "itap.iL";
10003
10004			if (iL.context && iL.selector) {
10005				var $items = $(iL.selector, iL.context);
10006
10007				$(iL.context).on(clickEvent, iL.selector, function() {
10008					var $this = $(this),
10009						//UNCODE addition
10010						key = typeof $(this).data('lb-index') !== '' && $(this).closest('.owl-item').length ? $(this).data('lb-index') : $items.index($this);
10011					if (UNCODE.isMobile) {
10012						var isCarousel = $this.closest('.owl-carousel');
10013						if (isCarousel.length) {
10014							if (isCarousel.hasClass('owl-translating')) return false;
10015						}
10016					}
10017
10018					vars.current = key;
10019					vars.next = iL.items[key + 1] ? key + 1 : null;
10020					vars.prev = iL.items[key - 1] ? key - 1 : null;
10021
10022					//UNCODE addition
10023					if ( vars.BODY.hasClass('fp-slide-scrolling') )
10024						return false;
10025
10026					iL.addContents();
10027					iL.patchEvents();
10028
10029					return false;
10030				}).on(vEvent, iL.selector, function() {
10031					return false;
10032				});
10033			} else
10034				$.each(iL.itemsObject, function(key, val) {
10035					val.on(clickEvent, function() {
10036						vars.current = key;
10037						vars.next = iL.items[key + 1] ? key + 1 : null;
10038						vars.prev = iL.items[key - 1] ? key - 1 : null;
10039
10040						iL.addContents();
10041						iL.patchEvents();
10042
10043						return false;
10044					}).on(vEvent, function() {
10045						return false;
10046					});
10047				});
10048		},
10049
10050		dispatchItemsEvents: function() {
10051			var iL = this,
10052				vars = iL.vars,
10053				opts = iL.options;
10054
10055			if (iL.context && iL.selector)
10056				$(iL.context).off('.iL', iL.selector);
10057			else
10058				$.each(iL.itemsObject, function(key, val) {
10059					val.off('.iL');
10060				});
10061		},
10062
10063		refresh: function() {
10064			var iL = this;
10065
10066			iL.dispatchItemsEvents();
10067			iL.attachItems();
10068			iL.normalizeItems();
10069			iL.patchItemsEvents();
10070		},
10071
10072		patchEvents: function() {
10073			var iL = this,
10074				vars = iL.vars,
10075				opts = iL.options,
10076				path = opts.path.toLowerCase(),
10077				holders = $('.ilightbox-holder'),
10078				fullscreenEvent = fullScreenApi.fullScreenEventName + '.iLightBox',
10079				durationThreshold = 1000,
10080				horizontalDistanceThreshold =
10081				verticalDistanceThreshold = 100,
10082				buttonsArray = [vars.nextButton[0], vars.prevButton[0], vars.nextButton[0].firstChild, vars.prevButton[0].firstChild];
10083
10084			$win.bind('resize.iLightBox', function() {
10085				var viewport = getViewport();
10086
10087				if (opts.mobileOptimizer && !opts.innerToolbar) vars.isMobile = viewport.width <= vars.mobileMaxWidth;
10088				vars.BODY[vars.isMobile ? 'addClass' : 'removeClass']('isMobile');
10089
10090				iL.repositionPhoto(null);
10091				if (supportTouch) {
10092					clearTimeout(vars.setTime);
10093					vars.setTime = setTimeout(function() {
10094						var scrollTop = getScrollXY().y;
10095						window.scrollTo(0, scrollTop - 30);
10096						window.scrollTo(0, scrollTop + 30);
10097						window.scrollTo(0, scrollTop);
10098					}, 2000);
10099				}
10100				if (vars.thumbs) iL.positionThumbnails();
10101			}).bind('keydown.iLightBox', function(event) {
10102				if (opts.controls.keyboard) {
10103					switch (event.keyCode) {
10104						case 13:
10105							if (event.shiftKey && opts.keyboard.shift_enter) iL.fullScreenAction();
10106							break;
10107						case 27:
10108							if (opts.keyboard.esc) iL.closeAction();
10109							break;
10110						case 37:
10111							if (opts.keyboard.left && !vars.lockKey) iL.moveTo('prev');
10112							break;
10113						case 38:
10114							if (opts.keyboard.up && !vars.lockKey) iL.moveTo('prev');
10115							break;
10116						case 39:
10117							if (opts.keyboard.right && !vars.lockKey) iL.moveTo('next');
10118							break;
10119						case 40:
10120							if (opts.keyboard.down && !vars.lockKey) iL.moveTo('next');
10121							break;
10122					}
10123				}
10124			});
10125
10126			if (fullScreenApi.supportsFullScreen) $win.bind(fullscreenEvent, function() {
10127				iL.doFullscreen();
10128			});
10129
10130			var holderEventsArr = [opts.caption.show + '.iLightBox', opts.caption.hide + '.iLightBox', opts.social.show + '.iLightBox', opts.social.hide + '.iLightBox'].filter(function(e, i, arr) {
10131					return arr.lastIndexOf(e) === i;
10132				}),
10133				holderEvents = "";
10134
10135			$.each(holderEventsArr, function(key, val) {
10136				if (key != 0) holderEvents += ' ';
10137				holderEvents += val;
10138			});
10139
10140			$doc.on(clickEvent, '.ilightbox-overlay', function() {
10141				if (opts.overlay.blur) iL.closeAction();
10142			}).on(clickEvent, '.ilightbox-next, .ilightbox-next-button', function() {
10143				iL.moveTo('next');
10144			}).on(clickEvent, '.ilightbox-prev, .ilightbox-prev-button', function() {
10145				iL.moveTo('prev');
10146			}).on(clickEvent, '.ilightbox-thumbnail', function() {
10147				var t = $(this),
10148					thumbs = $('.ilightbox-thumbnail', vars.thumbnails),
10149					index = thumbs.index(t);
10150
10151				if (index != vars.current) iL.goTo(index);
10152			}).on(holderEvents, '.ilightbox-holder:not(.ilightbox-next, .ilightbox-prev)', function(e) {
10153				var caption = $('div.ilightbox-caption', vars.holder),
10154					social = $('div.ilightbox-social', vars.holder),
10155					fadeSpeed = opts.effects.fadeSpeed;
10156
10157				if (vars.nextLock || vars.prevLock) {
10158					if (e.type == opts.caption.show && !caption.is(':visible')) caption.fadeIn(fadeSpeed);
10159					else if (e.type == opts.caption.hide && caption.is(':visible')) caption.fadeOut(fadeSpeed);
10160
10161					if (e.type == opts.social.show && !social.is(':visible')) social.fadeIn(fadeSpeed);
10162					else if (e.type == opts.social.hide && social.is(':visible')) social.fadeOut(fadeSpeed);
10163				} else {
10164					if (e.type == opts.caption.show && !caption.is(':visible')) caption.stop().fadeIn(fadeSpeed);
10165					else if (e.type == opts.caption.hide && caption.is(':visible')) caption.stop().fadeOut(fadeSpeed);
10166
10167					if (e.type == opts.social.show && !social.is(':visible')) social.stop().fadeIn(fadeSpeed);
10168					else if (e.type == opts.social.hide && social.is(':visible')) social.stop().fadeOut(fadeSpeed);
10169				}
10170			}).on('mouseenter.iLightBox mouseleave.iLightBox', '.ilightbox-wrapper', function(e) {
10171				if (e.type == 'mouseenter') vars.lockWheel = true;
10172				else vars.lockWheel = false;
10173			}).on(clickEvent, '.ilightbox-toolbar a.ilightbox-close, .ilightbox-toolbar a.ilightbox-fullscreen, .ilightbox-toolbar a.ilightbox-play, .ilightbox-toolbar a.ilightbox-pause', function() {
10174				var t = $(this);
10175
10176				if (t.hasClass('ilightbox-fullscreen')) iL.fullScreenAction();
10177				else if (t.hasClass('ilightbox-play')) {
10178					iL.resume();
10179					t.addClass('ilightbox-pause').removeClass('ilightbox-play');
10180				} else if (t.hasClass('ilightbox-pause')) {
10181					iL.pause();
10182					t.addClass('ilightbox-play').removeClass('ilightbox-pause');
10183				} else iL.closeAction();
10184			}).on(touchMoveEvent, '.ilightbox-overlay, .ilightbox-thumbnails-container', function(e) {
10185				// prevent scrolling
10186				e.preventDefault();
10187			});
10188
10189			function mouseMoveHandler(e) {
10190				if (!vars.isMobile) {
10191					if (!vars.mouseID) {
10192						vars.hideableElements.show();
10193					}
10194
10195					vars.mouseID = clearTimeout(vars.mouseID);
10196
10197					if (buttonsArray.indexOf(e.target) === -1)
10198						vars.mouseID = setTimeout(function() {
10199							vars.hideableElements.hide();
10200							vars.mouseID = clearTimeout(vars.mouseID);
10201						}, 3000);
10202				}
10203			}
10204
10205			if (opts.controls.arrows && !supportTouch) $doc.on('mousemove.iLightBox', mouseMoveHandler);
10206
10207			if (opts.controls.slideshow && opts.slideshow.pauseOnHover) $doc.on('mouseenter.iLightBox mouseleave.iLightBox', '.ilightbox-holder:not(.ilightbox-next, .ilightbox-prev)', function(e) {
10208				if (e.type == 'mouseenter' && vars.cycleID) iL.pause();
10209				else if (e.type == 'mouseleave' && vars.isPaused) iL.resume();
10210			});
10211
10212			var switchers = $('.ilightbox-overlay, .ilightbox-holder, .ilightbox-thumbnails');
10213
10214			if (opts.controls.mousewheel) switchers.on('mousewheel.iLightBox', function(event, delta) {
10215				if (!vars.lockWheel) {
10216					event.preventDefault();
10217					if (delta < 0) iL.moveTo('next');
10218					else if (delta > 0) iL.moveTo('prev');
10219				}
10220			});
10221
10222			if (opts.controls.swipe) holders.on(touchStartEvent, function(event) {
10223				if (vars.nextLock || vars.prevLock || vars.total == 1 || vars.lockSwipe) return;
10224
10225				vars.BODY.addClass('ilightbox-closedhand');
10226
10227				var data = event.originalEvent.touches ? event.originalEvent.touches[0] : event,
10228					scrollTop = $doc.scrollTop(),
10229					scrollLeft = $doc.scrollLeft(),
10230					offsets = [
10231						holders.eq(0).offset(),
10232						holders.eq(1).offset(),
10233						holders.eq(2).offset()
10234					],
10235					offSet = [{
10236						top: offsets[0].top - scrollTop,
10237						left: offsets[0].left - scrollLeft
10238					}, {
10239						top: offsets[1].top - scrollTop,
10240						left: offsets[1].left - scrollLeft
10241					}, {
10242						top: offsets[2].top - scrollTop,
10243						left: offsets[2].left - scrollLeft
10244					}],
10245					start = {
10246						time: (new Date()).getTime(),
10247						coords: [data.pageX - scrollLeft, data.pageY - scrollTop]
10248					},
10249					stop;
10250
10251				function moveEachHandler(i) {
10252					var t = $(this),
10253						offset = offSet[i],
10254						scroll = [(start.coords[0] - stop.coords[0]), (start.coords[1] - stop.coords[1])];
10255
10256					t[0].style[path == "horizontal" ? 'left' : 'top'] = (path == "horizontal" ? offset.left - scroll[0] : offset.top - scroll[1]) + 'px';
10257				}
10258
10259				function moveHandler(event) {
10260
10261					if (!start) return;
10262
10263					var data = event.originalEvent.touches ? event.originalEvent.touches[0] : event;
10264
10265					stop = {
10266						time: (new Date()).getTime(),
10267						coords: [data.pageX - scrollLeft, data.pageY - scrollTop]
10268					};
10269
10270					holders.each(moveEachHandler);
10271
10272					// prevent scrolling
10273					event.preventDefault();
10274				}
10275
10276				function repositionHolders() {
10277					holders.each(function() {
10278						var t = $(this),
10279							offset = t.data('offset') || {
10280								top: t.offset().top - scrollTop,
10281								left: t.offset().left - scrollLeft
10282							},
10283							top = offset.top,
10284							left = offset.left;
10285
10286						t.css(transform, gpuAcceleration).stop().animate({
10287							top: top,
10288							left: left
10289						}, 500, 'easeOutCirc', function() {
10290							t.css(transform, '');
10291						});
10292					});
10293				}
10294
10295				holders.bind(touchMoveEvent, moveHandler);
10296				$doc.one(touchStopEvent, function(event) {
10297					holders.unbind(touchMoveEvent, moveHandler);
10298
10299					vars.BODY.removeClass('ilightbox-closedhand');
10300
10301					if (start && stop) {
10302						if (path == "horizontal" && stop.time - start.time < durationThreshold && abs(start.coords[0] - stop.coords[0]) > horizontalDistanceThreshold && abs(start.coords[1] - stop.coords[1]) < verticalDistanceThreshold) {
10303							if (start.coords[0] > stop.coords[0]) {
10304								if (vars.current == vars.total - 1 && !opts.infinite) repositionHolders();
10305								else {
10306									vars.isSwipe = true;
10307									iL.moveTo('next');
10308								}
10309							} else {
10310								if (vars.current == 0 && !opts.infinite) repositionHolders();
10311								else {
10312									vars.isSwipe = true;
10313									iL.moveTo('prev');
10314								}
10315							}
10316						} 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) {
10317							if (start.coords[1] > stop.coords[1]) {
10318								if (vars.current == vars.total - 1 && !opts.infinite) repositionHolders();
10319								else {
10320									vars.isSwipe = true;
10321									iL.moveTo('next');
10322								}
10323							} else {
10324								if (vars.current == 0 && !opts.infinite) repositionHolders();
10325								else {
10326									vars.isSwipe = true;
10327									iL.moveTo('prev');
10328								}
10329							}
10330						} else repositionHolders();
10331					}
10332					start = stop = undefined;
10333				});
10334			});
10335
10336		},
10337
10338		goTo: function(index) {
10339			var iL = this,
10340				vars = iL.vars,
10341				opts = iL.options,
10342				diff = (index - vars.current);
10343
10344			if (opts.infinite) {
10345				if (index == vars.total - 1 && vars.current == 0) diff = -1;
10346				if (vars.current == vars.total - 1 && index == 0) diff = 1;
10347			}
10348
10349			if (diff == 1) iL.moveTo('next');
10350			else if (diff == -1) iL.moveTo('prev');
10351			else {
10352
10353				if (vars.nextLock || vars.prevLock) return false;
10354
10355				//Trigger the onBeforeChange callback
10356				if (typeof opts.callback.onBeforeChange == 'function') opts.callback.onBeforeChange.call(iL, iL.ui);
10357
10358				if (opts.linkId) {
10359					vars.hashLock = true;
10360					window.location.hash = opts.linkId + '/' + index;
10361				}
10362
10363				if (iL.items[index]) {
10364					if (!iL.items[index].options.mousewheel) vars.lockWheel = true;
10365					else iL.vars.lockWheel = false;
10366
10367					if (!iL.items[index].options.swipe) vars.lockSwipe = true;
10368					else vars.lockSwipe = false;
10369				}
10370
10371				$.each([vars.holder, vars.nextPhoto, vars.prevPhoto], function(key, val) {
10372					val.css(transform, gpuAcceleration).fadeOut(opts.effects.loadedFadeSpeed);
10373				});
10374
10375				vars.current = index;
10376				vars.next = index + 1;
10377				vars.prev = index - 1;
10378
10379				iL.createUI();
10380
10381				setTimeout(function() {
10382					iL.generateBoxes();
10383				}, opts.effects.loadedFadeSpeed + 50);
10384
10385				$('.ilightbox-thumbnail', vars.thumbnails).removeClass('ilightbox-active').eq(index).addClass('ilightbox-active');
10386				iL.positionThumbnails();
10387
10388				if (opts.linkId) setTimeout(function() {
10389					vars.hashLock = false;
10390				}, 55);
10391
10392				// Configure arrow buttons
10393				if (!opts.infinite) {
10394					vars.nextButton.add(vars.prevButton).add(vars.innerPrevButton).add(vars.innerNextButton).removeClass('disabled');
10395
10396					if (vars.current == 0) {
10397						vars.prevButton.add(vars.innerPrevButton).addClass('disabled');
10398					}
10399					if (vars.current >= vars.total - 1) {
10400						vars.nextButton.add(vars.innerNextButton).addClass('disabled');
10401					}
10402				}
10403
10404				// Reset next cycle timeout
10405				iL.resetCycle();
10406
10407				//Trigger the onAfterChange callback
10408				if (typeof opts.callback.onAfterChange == 'function') opts.callback.onAfterChange.call(iL, iL.ui);
10409			}
10410		},
10411
10412		moveTo: function(side) {
10413			var iL = this,
10414				vars = iL.vars,
10415				opts = iL.options,
10416				path = opts.path.toLowerCase(),
10417				viewport = getViewport(),
10418				switchSpeed = opts.effects.switchSpeed;
10419
10420			if (vars.nextLock || vars.prevLock) return false;
10421			else {
10422
10423				var item = (side == "next") ? vars.next : vars.prev;
10424
10425				if (opts.linkId) {
10426					vars.hashLock = true;
10427					window.location.hash = opts.linkId + '/' + item;
10428				}
10429
10430				if (side == "next") {
10431					if (!iL.items[item]) return false;
10432					var firstHolder = vars.nextPhoto,
10433						secondHolder = vars.holder,
10434						lastHolder = vars.prevPhoto,
10435						firstClass = 'ilightbox-prev',
10436						secondClass = 'ilightbox-next';
10437				} else if (side == "prev") {
10438					if (!iL.items[item]) return false;
10439					var firstHolder = vars.prevPhoto,
10440						secondHolder = vars.holder,
10441						lastHolder = vars.nextPhoto,
10442						firstClass = 'ilightbox-next',
10443						secondClass = 'ilightbox-prev';
10444				}
10445
10446				//Trigger the onBeforeChange callback
10447				if (typeof opts.callback.onBeforeChange == 'function')
10448					opts.callback.onBeforeChange.call(iL, iL.ui);
10449
10450				(side == "next") ? vars.nextLock = true: vars.prevLock = true;
10451
10452				var captionFirst = $('div.ilightbox-caption', secondHolder),
10453					socialFirst = $('div.ilightbox-social', secondHolder);
10454
10455				if (captionFirst.length)
10456					captionFirst.stop().fadeOut(switchSpeed, function() {
10457						$(this).remove();
10458					});
10459				if (socialFirst.length)
10460					socialFirst.stop().fadeOut(switchSpeed, function() {
10461						$(this).remove();
10462					});
10463
10464				if (iL.items[item].caption) {
10465					iL.setCaption(iL.items[item], firstHolder);
10466					var caption = $('div.ilightbox-caption', firstHolder),
10467						percent = parseInt((caption.outerHeight() / firstHolder.outerHeight()) * 100);
10468					if (opts.caption.start && percent <= 50) caption.fadeIn(switchSpeed);
10469				}
10470
10471				var social = iL.items[item].options.social;
10472				if (social) {
10473					iL.setSocial(social, iL.items[item].URL, firstHolder);
10474					if (opts.social.start) $('div.ilightbox-social', firstHolder).fadeIn(opts.effects.fadeSpeed);
10475				}
10476
10477				$.each([firstHolder, secondHolder, lastHolder], function(key, val) {
10478					val.removeClass('ilightbox-next ilightbox-prev');
10479				});
10480
10481				var firstOffset = firstHolder.data('offset'),
10482					winW = (viewport.width - (opts.styles.pageOffsetX)),
10483					winH = (viewport.height - (opts.styles.pageOffsetY)),
10484					width = firstOffset.newDims.width,
10485					height = firstOffset.newDims.height,
10486					thumbsOffset = firstOffset.thumbsOffset,
10487					diff = firstOffset.diff,
10488					top = parseInt((winH / 2) - (height / 2) - diff.H - (thumbsOffset.H / 2)),
10489					left = parseInt((winW / 2) - (width / 2) - diff.W - (thumbsOffset.W / 2));
10490
10491				var secondOffset = secondHolder.data('offset'),
10492					object = secondOffset.object;
10493
10494				/*if (object.item.caption && !isNaN(width) && !isNaN(height) && UNCODE.wwidth > UNCODE.mediaQuery) {
10495					var objRatio = width / height;
10496					height = height - vars.captionHeight;
10497					width = height * objRatio;
10498					top = path == 'horizontal' ? parseInt((winH / 2) - (height / 2) - diff.H - (thumbsOffset.H / 2)) : top,
10499					left = parseInt((winW / 2) - (width / 2) - diff.W - (thumbsOffset.W / 2));
10500				}*/
10501
10502				firstHolder.show().css(transform, gpuAcceleration).animate({
10503					top: top,
10504					left: left,
10505					opacity: 1
10506				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc', function() {
10507					firstHolder.css(transform, '');
10508				});
10509
10510				$('div.ilightbox-container', firstHolder).animate({
10511					width: width,
10512					height: height
10513				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc');
10514
10515				diff = secondOffset.diff;
10516
10517				width = secondOffset.newDims.width,
10518					height = secondOffset.newDims.height;
10519
10520				width = parseInt(width * opts.styles[side == 'next' ? 'prevScale' : 'nextScale']),
10521					height = parseInt(height * opts.styles[side == 'next' ? 'prevScale' : 'nextScale']),
10522					top = (path == 'horizontal') ? parseInt((winH / 2) - object.offsetY - (height / 2) - diff.H - (thumbsOffset.H / 2)) : parseInt(winH - object.offsetX - diff.H - (thumbsOffset.H / 2));
10523
10524				if (side == 'prev')
10525					left = (path == 'horizontal') ? parseInt(winW - object.offsetX - diff.W - (thumbsOffset.W / 2)) : parseInt((winW / 2) - (width / 2) - diff.W - object.offsetY - (thumbsOffset.W / 2));
10526				else {
10527					top = (path == 'horizontal') ? top : parseInt(object.offsetX - diff.H - height - (thumbsOffset.H / 2)),
10528					left = (path == 'horizontal') ? parseInt(object.offsetX - diff.W - width - (thumbsOffset.W / 2)) : parseInt((winW / 2) - object.offsetY - (width / 2) - diff.W - (thumbsOffset.W / 2));
10529				}
10530
10531				/*if (object.item.caption && !isNaN(width) && !isNaN(height) && UNCODE.wwidth > UNCODE.mediaQuery) {
10532					var objRatio = width / height;
10533					height = height - vars.captionHeight;
10534					width = height * objRatio;
10535					top = path == 'horizontal' ? parseInt((winH / 2) - (height / 2) - diff.H - (thumbsOffset.H / 2)) : top;
10536				}*/
10537
10538				$('div.ilightbox-container', secondHolder).animate({
10539					width: width,
10540					height: height
10541				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc');
10542
10543				secondHolder.addClass(firstClass).css(transform, gpuAcceleration).animate({
10544					top: top,
10545					left: left,
10546					opacity: opts.styles.prevOpacity,
10547				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc', function() {
10548					secondHolder.css(transform, '');
10549
10550					$('.ilightbox-thumbnail', vars.thumbnails).removeClass('ilightbox-active').eq(item).addClass('ilightbox-active');
10551					iL.positionThumbnails();
10552
10553					if (iL.items[item]) {
10554						if (!iL.items[item].options.mousewheel) vars.lockWheel = true;
10555						else vars.lockWheel = false;
10556
10557						if (!iL.items[item].options.swipe) vars.lockSwipe = true;
10558						else vars.lockSwipe = false;
10559					}
10560
10561					vars.isSwipe = false;
10562
10563					// Remove iframe & video from previous slide
10564					if (['iframe', 'video'].indexOf(iL.items[vars.current].type) !== -1) {
10565						$('div.ilightbox-container', secondHolder).empty();
10566					}
10567
10568					if (side == "next") {
10569						vars.nextPhoto = lastHolder,
10570							vars.prevPhoto = secondHolder,
10571							vars.holder = firstHolder;
10572
10573						vars.nextPhoto.hide();
10574
10575						vars.next = vars.next + 1,
10576							vars.prev = vars.current,
10577							vars.current = vars.current + 1;
10578
10579						if (opts.infinite) {
10580							if (vars.current > vars.total - 1) vars.current = 0;
10581							if (vars.current == vars.total - 1) vars.next = 0;
10582							if (vars.current == 0) vars.prev = vars.total - 1;
10583						}
10584
10585						iL.createUI();
10586
10587						if (!iL.items[vars.next])
10588							vars.nextLock = false;
10589						else
10590							iL.loadContent(iL.items[vars.next], 'next');
10591					} else {
10592						vars.prevPhoto = lastHolder;
10593						vars.nextPhoto = secondHolder;
10594						vars.holder = firstHolder;
10595
10596						vars.prevPhoto.hide();
10597
10598						vars.next = vars.current;
10599						vars.current = vars.prev;
10600						vars.prev = vars.current - 1;
10601
10602						if (opts.infinite) {
10603							if (vars.current == vars.total - 1) vars.next = 0;
10604							if (vars.current == 0) vars.prev = vars.total - 1;
10605						}
10606
10607						iL.createUI();
10608
10609						if (!iL.items[vars.prev])
10610							vars.prevLock = false;
10611						else
10612							iL.loadContent(iL.items[vars.prev], 'prev');
10613					}
10614
10615					// Add iframe & video content for current slide
10616					if (['iframe', 'video'].indexOf(iL.items[vars.current].type) !== -1) {
10617						iL.loadContent(iL.items[vars.current], 'current');
10618					}
10619
10620					if (opts.linkId) setTimeout(function() {
10621						vars.hashLock = false;
10622					}, 55);
10623
10624					// Configure arrow buttons
10625					if (!opts.infinite) {
10626						vars.nextButton.add(vars.prevButton).add(vars.innerPrevButton).add(vars.innerNextButton).removeClass('disabled');
10627
10628						if (vars.current == 0)
10629							vars.prevButton.add(vars.innerPrevButton).addClass('disabled');
10630						if (vars.current >= vars.total - 1)
10631							vars.nextButton.add(vars.innerNextButton).addClass('disabled');
10632					}
10633
10634					iL.repositionPhoto();
10635
10636					// Reset next cycle timeout
10637					iL.resetCycle();
10638
10639					//Trigger the onAfterChange callback
10640					if (typeof opts.callback.onAfterChange == 'function')
10641						opts.callback.onAfterChange.call(iL, iL.ui);
10642				});
10643
10644				top = (path == 'horizontal') ? getPixel(lastHolder, 'top') : ((side == "next") ? parseInt(-(winH / 2) - lastHolder.outerHeight()) : parseInt(top * 2)),
10645					left = (path == 'horizontal') ? ((side == "next") ? parseInt(-(winW / 2) - lastHolder.outerWidth()) : parseInt(left * 2)) : getPixel(lastHolder, 'left');
10646
10647				lastHolder.css(transform, gpuAcceleration).animate({
10648					top: top,
10649					left: left,
10650					opacity: opts.styles.nextOpacity
10651				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc', function() {
10652					lastHolder.css(transform, '');
10653				}).addClass(secondClass);
10654			}
10655		},
10656
10657		setCaption: function(obj, target) {
10658			var iL = this,
10659				caption = $('<div class="ilightbox-caption"></div>');
10660
10661			if (obj.caption) {
10662				caption.html(obj.caption);
10663				$('div.ilightbox-container', target).append(caption);
10664			}
10665		},
10666
10667		normalizeSocial: function(obj, url) {
10668			var iL = this,
10669				vars = iL.vars,
10670				opts = iL.options,
10671				baseURL = window.location.href;
10672
10673			$.each(obj, function(key, value) {
10674				if (!value)
10675					return true;
10676
10677				var item = key.toLowerCase(),
10678					source, text;
10679
10680				switch (item) {
10681					case 'facebook':
10682						source = "http://www.facebook.com/share.php?v=4&src=bm&u={URL}",
10683							text = "Share on Facebook";
10684						break;
10685					case 'twitter':
10686						source = "http://twitter.com/home?status={URL}",
10687							text = "Share on Twitter";
10688						break;
10689					case 'googleplus':
10690						source = "https://plus.google.com/share?url={URL}",
10691							text = "Share on Google+";
10692						break;
10693					case 'delicious':
10694						source = "http://delicious.com/post?url={URL}",
10695							text = "Share on Delicious";
10696						break;
10697					case 'digg':
10698						source = "http://digg.com/submit?phase=2&url={URL}",
10699							text = "Share on Digg";
10700						break;
10701					case 'reddit':
10702						source = "http://reddit.com/submit?url={URL}",
10703							text = "Share on reddit";
10704						break;
10705				}
10706
10707				obj[key] = {
10708					URL: value.URL && absolutizeURI(baseURL, value.URL) || opts.linkId && window.location.href || typeof url !== 'string' && baseURL || url && absolutizeURI(baseURL, url) || baseURL,
10709					source: value.source || source || value.URL && absolutizeURI(baseURL, value.URL) || url && absolutizeURI(baseURL, url),
10710					text: value.text || text || "Share on " + key,
10711					width: (typeof(value.width) != 'undefined' && !isNaN(value.width)) ? parseInt(value.width) : 640,
10712					height: value.height || 360
10713				};
10714
10715			});
10716
10717			return obj;
10718		},
10719
10720		setSocial: function(obj, url, target) {
10721			var iL = this,
10722				socialBar = $('<div class="ilightbox-social"></div>'),
10723				buttons = '<ul>';
10724
10725			obj = iL.normalizeSocial(obj, url);
10726
10727			$.each(obj, function(key, value) {
10728				var item = key.toLowerCase(),
10729					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, '+'));
10730				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>';
10731			});
10732
10733			buttons += '</ul>';
10734
10735			socialBar.html(buttons);
10736			$('div.ilightbox-container', target).append(socialBar);
10737		},
10738
10739		fullScreenAction: function() {
10740			var iL = this,
10741				vars = iL.vars;
10742
10743			if (fullScreenApi.supportsFullScreen) {
10744				if (fullScreenApi.isFullScreen()) fullScreenApi.cancelFullScreen(document.documentElement);
10745				else fullScreenApi.requestFullScreen(document.documentElement);
10746			} else {
10747				iL.doFullscreen();
10748			}
10749		},
10750
10751		doFullscreen: function() {
10752			var iL = this,
10753				vars = iL.vars,
10754				viewport = getViewport(),
10755				opts = iL.options;
10756
10757			if (opts.fullAlone) {
10758				var currentHolder = vars.holder,
10759					current = iL.items[vars.current],
10760					windowWidth = viewport.width,
10761					windowHeight = viewport.height,
10762					elements = [currentHolder, vars.nextPhoto, vars.prevPhoto, vars.nextButton, vars.prevButton, vars.overlay, vars.toolbar, vars.thumbnails, vars.loader],
10763					hideElements = [vars.nextPhoto, vars.prevPhoto, vars.nextButton, vars.prevButton, vars.loader, vars.thumbnails];
10764
10765				if (!vars.isInFullScreen) {
10766					vars.isInFullScreen = vars.lockKey = vars.lockWheel = vars.lockSwipe = true;
10767					vars.overlay.css({
10768						opacity: 1
10769					});
10770
10771					$.each(hideElements, function(i, element) {
10772						element.hide();
10773					});
10774
10775					vars.fullScreenButton.attr('title', opts.text.exitFullscreen);
10776
10777					if (opts.fullStretchTypes.indexOf(current.type) != -1) currentHolder.data({
10778						naturalWidthOld: currentHolder.data('naturalWidth'),
10779						naturalHeightOld: currentHolder.data('naturalHeight'),
10780						naturalWidth: windowWidth,
10781						naturalHeight: windowHeight
10782					});
10783					else {
10784						var viewport = current.options.fullViewPort || opts.fullViewPort || '',
10785							newWidth = windowWidth,
10786							newHeight = windowHeight,
10787							width = currentHolder.data('naturalWidth'),
10788							height = currentHolder.data('naturalHeight');
10789
10790						if (viewport.toLowerCase() == 'fill') {
10791							newHeight = (newWidth / width) * height;
10792
10793							if (newHeight < windowHeight) {
10794								newWidth = (windowHeight / height) * width,
10795									newHeight = windowHeight;
10796							}
10797						} else if (viewport.toLowerCase() == 'fit') {
10798							var dims = iL.getNewDimenstions(newWidth, newHeight, width, height, true);
10799
10800							newWidth = dims.width,
10801								newHeight = dims.height;
10802						} else if (viewport.toLowerCase() == 'stretch') {
10803							newWidth = newWidth,
10804								newHeight = newHeight;
10805						} else {
10806							var scale = (width > newWidth || height > newHeight) ? true : false,
10807								dims = iL.getNewDimenstions(newWidth, newHeight, width, height, scale);
10808							newWidth = dims.width,
10809								newHeight = dims.height;
10810						}
10811
10812						currentHolder.data({
10813							naturalWidthOld: currentHolder.data('naturalWidth'),
10814							naturalHeightOld: currentHolder.data('naturalHeight'),
10815							naturalWidth: newWidth,
10816							naturalHeight: newHeight
10817						});
10818					}
10819
10820					$.each(elements, function(key, val) {
10821						val.addClass('ilightbox-fullscreen');
10822					});
10823
10824					//Trigger the onEnterFullScreen callback
10825					if (typeof opts.callback.onEnterFullScreen == 'function') opts.callback.onEnterFullScreen.call(iL, iL.ui);
10826				} else {
10827					vars.isInFullScreen = vars.lockKey = vars.lockWheel = vars.lockSwipe = false;
10828					vars.overlay.css({
10829						opacity: iL.options.overlay.opacity
10830					});
10831
10832					$.each(hideElements, function(i, element) {
10833						element.show();
10834					});
10835
10836					vars.fullScreenButton.attr('title', opts.text.enterFullscreen);
10837
10838					currentHolder.data({
10839						naturalWidth: currentHolder.data('naturalWidthOld'),
10840						naturalHeight: currentHolder.data('naturalHeightOld'),
10841						naturalWidthOld: null,
10842						naturalHeightOld: null
10843					});
10844
10845					$.each(elements, function(key, val) {
10846						val.removeClass('ilightbox-fullscreen');
10847					});
10848
10849					//Trigger the onExitFullScreen callback
10850					if (typeof opts.callback.onExitFullScreen == 'function') opts.callback.onExitFullScreen.call(iL, iL.ui);
10851				}
10852			} else {
10853				if (!vars.isInFullScreen) vars.isInFullScreen = true;
10854				else vars.isInFullScreen = false;
10855			}
10856			iL.repositionPhoto(true);
10857		},
10858
10859		closeAction: function() {
10860			var iL = this,
10861				vars = iL.vars,
10862				opts = iL.options;
10863
10864			$win.unbind('.iLightBox');
10865			$doc.off('.iLightBox');
10866
10867			if (vars.isInFullScreen) fullScreenApi.cancelFullScreen(document.documentElement);
10868
10869			$('.ilightbox-overlay, .ilightbox-holder, .ilightbox-thumbnails').off('.iLightBox');
10870
10871			if (opts.hide.effect) vars.overlay.stop().fadeOut(opts.hide.speed, function() {
10872				vars.overlay.remove();
10873				vars.BODY.removeClass('ilightbox-noscroll').off('.iLightBox');
10874			});
10875			else {
10876				vars.overlay.remove();
10877				vars.BODY.removeClass('ilightbox-noscroll').off('.iLightBox');
10878			}
10879
10880			var fadeOuts = [vars.toolbar, vars.holder, vars.nextPhoto, vars.prevPhoto, vars.nextButton, vars.prevButton, vars.loader, vars.thumbnails];
10881
10882			$.each(fadeOuts, function(i, element) {
10883				element.removeAttr('style').remove();
10884			});
10885
10886			vars.dontGenerateThumbs = vars.isInFullScreen = false;
10887
10888			window.iLightBox = null;
10889
10890			if (opts.linkId) {
10891				vars.hashLock = true;
10892				removeHash();
10893				setTimeout(function() {
10894					vars.hashLock = false;
10895				}, 55);
10896			}
10897
10898			//Trigger the onHide callback
10899			if (typeof opts.callback.onHide == 'function') opts.callback.onHide.call(iL, iL.ui);
10900		},
10901
10902		repositionPhoto: function() {
10903			var iL = this,
10904				vars = iL.vars,
10905				opts = iL.options,
10906				path = opts.path.toLowerCase(),
10907				viewport = getViewport(),
10908				winWidth = viewport.width,
10909				winHeight = viewport.height;
10910
10911			if (viewport.width < UNCODE.mediaQuery) opts.styles.nextOffsetX = 0;
10912
10913			var thumbsOffsetW = (vars.isInFullScreen && opts.fullAlone || vars.isMobile) ? 0 : ((path == 'horizontal') ? 0 : vars.thumbnails.outerWidth()),
10914				thumbsOffsetH = vars.isMobile ? vars.toolbar.outerHeight() : ((vars.isInFullScreen && opts.fullAlone) ? 0 : ((path == 'horizontal') ? vars.thumbnails.outerHeight() : 0)),
10915				width = (vars.isInFullScreen && opts.fullAlone) ? winWidth : (winWidth - (opts.styles.pageOffsetX)),
10916				height = (vars.isInFullScreen && opts.fullAlone) ? winHeight : (winHeight - (opts.styles.pageOffsetY)),
10917				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,
10918				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)));
10919
10920			var elObject = {
10921				type: 'current',
10922				width: width,
10923				height: height,
10924				item: iL.items[vars.current],
10925				offsetW: offsetW,
10926				offsetH: offsetH,
10927				thumbsOffsetW: thumbsOffsetW,
10928				thumbsOffsetH: thumbsOffsetH,
10929				animate: arguments.length,
10930				holder: vars.holder
10931			};
10932
10933			iL.repositionEl(elObject);
10934
10935			if (iL.items[vars.next]) {
10936				elObject = $.extend(elObject, {
10937					type: 'next',
10938					item: iL.items[vars.next],
10939					offsetX: opts.styles.nextOffsetX,
10940					offsetY: opts.styles.nextOffsetY,
10941					holder: vars.nextPhoto
10942				});
10943
10944				iL.repositionEl(elObject);
10945			}
10946
10947			if (iL.items[vars.prev]) {
10948				elObject = $.extend(elObject, {
10949					type: 'prev',
10950					item: iL.items[vars.prev],
10951					offsetX: opts.styles.nextOffsetX,
10952					offsetY: opts.styles.prevOffsetY,
10953					holder: vars.prevPhoto
10954				});
10955
10956				iL.repositionEl(elObject);
10957			}
10958
10959			var loaderCss = (path == "horizontal") ? {
10960				left: parseInt((width / 2) - (vars.loader.outerWidth() / 2))
10961			} : {
10962				top: parseInt((height / 2) - (vars.loader.outerHeight() / 2))
10963			};
10964			vars.loader.css(loaderCss);
10965		},
10966
10967		repositionEl: function(obj) {
10968			var iL = this,
10969				vars = iL.vars,
10970				opts = iL.options,
10971				path = opts.path.toLowerCase(),
10972				widthAvail = (obj.type == 'current') ? ((vars.isInFullScreen && opts.fullAlone) ? obj.width : (obj.width - obj.offsetW)) : (obj.width - obj.offsetW),
10973				heightAvail = (obj.type == 'current') ? ((vars.isInFullScreen && opts.fullAlone) ? obj.height : (obj.height - obj.offsetH)) : (obj.height - obj.offsetH),
10974				itemParent = obj.item,
10975				item = obj.item.options,
10976				holder = obj.holder,
10977				offsetX = obj.offsetX || 0,
10978				offsetY = obj.offsetY || 0,
10979				thumbsOffsetW = obj.thumbsOffsetW,
10980				thumbsOffsetH = obj.thumbsOffsetH;
10981
10982			if (obj.type == 'current') {
10983				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);
10984				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);
10985			} else {
10986				if (typeof item.width == 'number' && item.width) widthAvail = (item.width > widthAvail) ? widthAvail : item.width;
10987				if (typeof item.height == 'number' && item.height) heightAvail = (item.height > heightAvail) ? heightAvail : item.height;
10988			}
10989
10990			heightAvail = parseInt(heightAvail - $('.ilightbox-inner-toolbar', holder).outerHeight());
10991
10992			var width = (typeof item.width == 'string' && item.width.indexOf('%') != -1) ? percentToValue(parseInt(item.width.replace('%', '')), obj.width) : holder.data('naturalWidth'),
10993				height = (typeof item.height == 'string' && item.height.indexOf('%') != -1) ? percentToValue(parseInt(item.height.replace('%', '')), obj.height) : holder.data('naturalHeight');
10994
10995			var dims = ((typeof item.width == 'string' && item.width.indexOf('%') != -1 || typeof item.height == 'string' && item.height.indexOf('%') != -1) ? {
10996					width: width,
10997					height: height
10998				} : iL.getNewDimenstions(widthAvail, heightAvail, width, height)),
10999				newDims = $.extend({}, dims, {});
11000
11001			if (obj.type == 'prev' || obj.type == 'next')
11002				width = parseInt(dims.width * ((obj.type == 'next') ? opts.styles.nextScale : opts.styles.prevScale)),
11003				height = parseInt(dims.height * ((obj.type == 'next') ? opts.styles.nextScale : opts.styles.prevScale));
11004			else
11005				width = dims.width,
11006				height = dims.height;
11007
11008			var widthDiff = parseInt((getPixel(holder, 'padding-left') + getPixel(holder, 'padding-right') + getPixel(holder, 'border-left-width') + getPixel(holder, 'border-right-width')) / 2),
11009				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);
11010
11011			switch (obj.type) {
11012				case 'current':
11013					var top = parseInt((obj.height / 2) - (height / 2) - heightDiff - (thumbsOffsetH / 2)),
11014						left = parseInt((obj.width / 2) - (width / 2) - widthDiff - (thumbsOffsetW / 2));
11015					break;
11016
11017				case 'next':
11018					var top = (path == 'horizontal') ? parseInt((obj.height / 2) - offsetY - (height / 2) - heightDiff - (thumbsOffsetH / 2)) : parseInt(obj.height - offsetX - heightDiff - (thumbsOffsetH / 2)),
11019						left = (path == 'horizontal') ? parseInt(obj.width - offsetX - widthDiff - (thumbsOffsetW / 2)) : parseInt((obj.width / 2) - (width / 2) - widthDiff - offsetY - (thumbsOffsetW / 2));
11020					break;
11021
11022				case 'prev':
11023					var top = (path == 'horizontal') ? parseInt((obj.height / 2) - offsetY - (height / 2) - heightDiff - (thumbsOffsetH / 2)) : parseInt(offsetX - heightDiff - height - (thumbsOffsetH / 2)),
11024						left = (path == 'horizontal') ? parseInt(offsetX - widthDiff - width - (thumbsOffsetW / 2)) : parseInt((obj.width / 2) - offsetY - (width / 2) - widthDiff - (thumbsOffsetW / 2));
11025					break;
11026			}
11027
11028			holder.data('offset', {
11029				top: top,
11030				left: left,
11031				newDims: newDims,
11032				diff: {
11033					W: widthDiff,
11034					H: heightDiff
11035				},
11036				thumbsOffset: {
11037					W: thumbsOffsetW,
11038					H: thumbsOffsetH
11039				},
11040				object: obj
11041			});
11042
11043			var opacityMobile;
11044			if ( isMobile ) {
11045				opacityMobile = {
11046					opacity: 1,
11047					top: top,
11048					left: left
11049				};
11050			} else {
11051				opacityMobile = {
11052					top: top,
11053					left: left
11054				};
11055			}
11056
11057			if (obj.animate > 0 && opts.effects.reposition) {
11058				holder.css(transform, gpuAcceleration).stop().animate(
11059					opacityMobile,
11060					opts.effects.repositionSpeed, 'easeOutCirc', function() {
11061					holder.css(transform, '');
11062				});
11063				$('div.ilightbox-container', holder).stop().animate({
11064					width: width,
11065					height: height
11066				}, opts.effects.repositionSpeed, 'easeOutCirc');
11067				$('div.ilightbox-inner-toolbar', holder).stop().animate({
11068					width: width
11069				}, opts.effects.repositionSpeed, 'easeOutCirc', function() {
11070					$(this).css('overflow', 'visible');
11071				});
11072			} else {
11073				holder.css(opacityMobile);
11074				$('div.ilightbox-container', holder).css({
11075					width: width,
11076					height: height
11077				});
11078				$('div.ilightbox-inner-toolbar', holder).css({
11079					width: width
11080				});
11081			}
11082		},
11083
11084
11085		resume: function(priority) {
11086			var iL = this,
11087				vars = iL.vars,
11088				opts = iL.options;
11089
11090			if (!opts.slideshow.pauseTime || opts.controls.slideshow && vars.total <= 1 || priority < vars.isPaused) {
11091				return;
11092			}
11093
11094			vars.isPaused = 0;
11095
11096			if (vars.cycleID) {
11097				vars.cycleID = clearTimeout(vars.cycleID);
11098			}
11099
11100			vars.cycleID = setTimeout(function() {
11101				if (vars.current == vars.total - 1) iL.goTo(0);
11102				else iL.moveTo('next');
11103			}, opts.slideshow.pauseTime);
11104		},
11105
11106		pause: function(priority) {
11107			var iL = this,
11108				vars = iL.vars,
11109				opts = iL.options;
11110
11111			if (priority < vars.isPaused) {
11112				return;
11113			}
11114
11115			vars.isPaused = priority || 100;
11116
11117			if (vars.cycleID) {
11118				vars.cycleID = clearTimeout(vars.cycleID);
11119			}
11120		},
11121
11122		resetCycle: function() {
11123			var iL = this,
11124				vars = iL.vars,
11125				opts = iL.options;
11126
11127			if (opts.controls.slideshow && vars.cycleID && !vars.isPaused) {
11128				iL.resume();
11129			}
11130		},
11131
11132		getNewDimenstions: function(width, height, width_old, height_old, thumb) {
11133			var iL = this;
11134
11135			if (!width) factor = height / height_old;
11136			else if (!height) factor = width / width_old;
11137			else factor = min(width / width_old, height / height_old);
11138
11139			if (!thumb) {
11140				if (factor > iL.options.maxScale) factor = iL.options.maxScale;
11141				else if (factor < iL.options.minScale) factor = iL.options.minScale;
11142			}
11143
11144			var final_width = (iL.options.keepAspectRatio) ? round(width_old * factor) : width,
11145				final_height = (iL.options.keepAspectRatio) ? round(height_old * factor) : height;
11146
11147			return {
11148				width: final_width,
11149				height: final_height,
11150				ratio: factor
11151			};
11152		},
11153
11154		setOption: function(options) {
11155			var iL = this;
11156
11157			iL.options = $.extend(true, iL.options, options || {});
11158			iL.refresh();
11159		},
11160
11161		availPlugins: function() {
11162			var iL = this,
11163				testEl = document.createElement("video");
11164
11165			iL.plugins = {
11166				flash: !isMobile,
11167				quicktime: (parseInt(PluginDetect.getVersion("QuickTime")) >= 0) ? true : false,
11168				html5H264: !!(testEl.canPlayType && testEl.canPlayType('video/mp4').replace(/no/, '')),
11169				html5WebM: !!(testEl.canPlayType && testEl.canPlayType('video/webm').replace(/no/, '')),
11170				html5Vorbis: !!(testEl.canPlayType && testEl.canPlayType('video/ogg').replace(/no/, '')),
11171				html5QuickTime: !!(testEl.canPlayType && testEl.canPlayType('video/quicktime').replace(/no/, ''))
11172			};
11173		},
11174
11175		addContent: function(element, obj) {
11176			var iL = this,
11177				el;
11178
11179			switch (obj.type) {
11180				case 'video':
11181					var HTML5 = false,
11182						videoType = obj.videoType,
11183						html5video = obj.options.html5video;
11184
11185					if (((videoType == 'video/mp4' || obj.ext == 'mp4' || obj.ext == 'm4v') || html5video.h264) && iL.plugins.html5H264)
11186						obj.ext = 'mp4',
11187						obj.URL = html5video.h264 || obj.URL;
11188					else if (html5video.webm && iL.plugins.html5WebM)
11189						obj.ext = 'webm',
11190						obj.URL = html5video.webm || obj.URL;
11191					else if (html5video.ogg && iL.plugins.html5Vorbis)
11192						obj.ext = 'ogv',
11193						obj.URL = html5video.ogg || obj.URL;
11194
11195					if (iL.plugins.html5H264 && (videoType == 'video/mp4' || obj.ext == 'mp4' || obj.ext == 'm4v')) HTML5 = true, videoType = "video/mp4";
11196					else if (iL.plugins.html5WebM && (videoType == 'video/webm' || obj.ext == 'webm')) HTML5 = true, videoType = "video/webm";
11197					else if (iL.plugins.html5Vorbis && (videoType == 'video/ogg' || obj.ext == 'ogv')) HTML5 = true, videoType = "video/ogg";
11198					else if (iL.plugins.html5QuickTime && (videoType == 'video/quicktime' || obj.ext == 'mov' || obj.ext == 'qt')) HTML5 = true, videoType = "video/quicktime";
11199
11200					if (HTML5) {
11201						el = $('<video />
11201', {
11202							"width": "100%",
11203							"height": "100%",
11204							"preload": html5video.preload,
11205							"autoplay": html5video.autoplay,
11206							"loop": html5video.loop,
11207							"poster": html5video.poster,
11208							"controls": html5video.controls,
11209							"controlslist": "nodownload"
11210						}).append($('<source />', {
11211							"src": obj.URL,
11212							"type": videoType
11213						}));
11214					} else {
11215						if (!iL.plugins.quicktime) el = $('<span />', {
11216							"class": "ilightbox-alert",
11217							html: iL.options.errors.missingPlugin.replace('{pluginspage}', pluginspages.quicktime).replace('{type}', 'QuickTime')
11218						});
11219						else {
11220
11221							el = $('<object />', {
11222								"type": "video/quicktime",
11223								"pluginspage": pluginspages.quicktime
11224							}).attr({
11225								"data": obj.URL,
11226								"width": "100%",
11227								"height": "100%"
11228							}).append($('<param />', {
11229								"name": "src",
11230								"value": obj.URL
11231							})).append($('<param />', {
11232								"name": "autoplay",
11233								"value": "false"
11234							})).append($('<param />', {
11235								"name": "loop",
11236								"value": "false"
11237							})).append($('<param />', {
11238								"name": "scale",
11239								"value": "tofit"
11240							}));
11241
11242							if (browser.msie) el = QT_GenerateOBJECTText(obj.URL, '100%', '100%', '', 'SCALE', 'tofit', 'AUTOPLAY', 'false', 'LOOP', 'false');
11243						}
11244					}
11245
11246					break;
11247
11248				case 'flash':
11249					if (!iL.plugins.flash) el = $('<span />', {
11250						"class": "ilightbox-alert",
11251						html: iL.options.errors.missingPlugin.replace('{pluginspage}', pluginspages.flash).replace('{type}', 'Adobe Flash player')
11252					});
11253					else {
11254						var flashvars = "",
11255							i = 0;
11256
11257						if (obj.options.flashvars) $.each(obj.options.flashvars, function(k, v) {
11258							if (i != 0) flashvars += "&";
11259							flashvars += k + "=" + encodeURIComponent(v);
11260							i++;
11261						});
11262						else flashvars = null;
11263
11264						el = $('<embed />').attr({
11265							"type": "application/x-shockwave-flash",
11266							"src": obj.URL,
11267							"width": (typeof obj.options.width == 'number' && obj.options.width && iL.options.minScale == '1' && iL.options.maxScale == '1') ? obj.options.width : "100%",
11268							"height": (typeof obj.options.height == 'number' && obj.options.height && iL.options.minScale == '1' && iL.options.maxScale == '1') ? obj.options.height : "100%",
11269							"quality": "high",
11270							"bgcolor": "#000000",
11271							"play": "true",
11272							"loop": "true",
11273							"menu": "true",
11274							"wmode": "transparent",
11275							"scale": "showall",
11276							"allowScriptAccess": "always",
11277							"allowFullScreen": "true",
11278							"flashvars": flashvars,
11279							"fullscreen": "yes"
11280						});
11281					}
11282
11283					break;
11284
11285				case 'iframe':
11286					el = $('<iframe />').attr({
11287						"width": (typeof obj.options.width == 'number' && obj.options.width && iL.options.minScale == '1' && iL.options.maxScale == '1') ? obj.options.width : "100%",
11288						"height": (typeof obj.options.height == 'number' && obj.options.height && iL.options.minScale == '1' && iL.options.maxScale == '1') ? obj.options.height : "100%",
11289						src: obj.URL,
11290						frameborder: 0,
11291						'hspace': 0,
11292						'vspace': 0,
11293						'scrolling': supportTouch ? 'auto' : 'scroll',
11294						'webkitAllowFullScreen': '',
11295						'mozallowfullscreen': '',
11296						'allowFullScreen': ''
11297					});
11298
11299					break;
11300
11301				case 'inline':
11302					el = $('<div class="ilightbox-wrapper"></div>').html($(obj.URL).clone(true));
11303
11304					break;
11305
11306				case 'html':
11307					var object = obj.URL,
11308						el;
11309
11310					if (object[0].nodeName) {
11311						el = $('<div class="ilightbox-wrapper"></div>').html(object);
11312					} else {
11313						var dom = $(obj.URL),
11314							html = (dom.selector) ? $('<div>' + dom + '</div>') : dom;
11315						el = $('<div class="ilightbox-wrapper"></div>').html(html);
11316					}
11317
11318					break;
11319			}
11320
11321			$('div.ilightbox-container', element).empty().html(el);
11322
11323			// For fixing Chrome about just playing the video for first time
11324			if (el[0].tagName.toLowerCase() === 'video' && browser.webkit) setTimeout(function() {
11325				var src = el[0].currentSrc + '?' + floor(random() * 30000);
11326				el[0].currentSrc = src;
11327				el[0].src = src;
11328			});
11329
11330			return el;
11331		},
11332
11333		ogpRecognition: function(obj, callback) {
11334			var iL = this,
11335				url = obj.URL;
11336
11337			iL.showLoader();
11338			doAjax(url, function(data) {
11339				iL.hideLoader();
11340				if (data) {
11341					var object = new Object();
11342
11343					object.length = false,
11344					object.url = data.url;
11345
11346					if (data.status == 200) {
11347						var result = data.results,
11348							type = result.type,
11349							source = result.source;
11350
11351						object.source = source.src,
11352						object.width = source.width && parseInt(source.width) || 0,
11353						object.height = source.height && parseInt(source.height) || 0,
11354						object.type = type,
11355						object.thumbnail = source.thumbnail || result.images && result.images[0],
11356						object.html5video = result.html5video || {},
11357						object.length = true;
11358
11359						if (source.type == 'application/x-shockwave-flash') object.type = "flash";
11360						else if (source.type.indexOf("video/") != -1) object.type = "video";
11361						else if (source.type.indexOf("/html") != -1) object.type = "iframe";
11362						else if (source.type.indexOf("image/") != -1) object.type = "image";
11363
11364					} else if (typeof data.response != 'undefined')
11365						throw data.response;
11366
11367					callback.call(this, object.length ? object : false);
11368				}
11369			});
11370		},
11371
11372		hashChangeHandler: function(url) {
11373			var iL = this,
11374				vars = iL.vars,
11375				opts = iL.options,
11376				URL = url || window.location.href,
11377				hash = parseURI(URL).hash,
11378				split = hash.split('/'),
11379				index = split[1];
11380
11381			if (vars.hashLock || ('#' + opts.linkId != split[0] && hash.length > 1)) return;
11382
11383			if (index) {
11384				var target = split[1] || 0;
11385				if (iL.items[target]) {
11386					var overlay = $('.ilightbox-overlay');
11387					if (overlay.length && overlay.attr('linkid') == opts.linkId) {
11388						iL.goTo(target);
11389					} else {
11390						iL.itemsObject[target].trigger(supportTouch ? 'itap' : 'click');
11391						//iL.goTo(target);
11392					}
11393				} else {
11394					var overlay = $('.ilightbox-overlay');
11395					if (overlay.length) iL.closeAction();
11396				}
11397			} else {
11398				var overlay = $('.ilightbox-overlay');
11399				if (overlay.length) iL.closeAction();
11400			}
11401		}
11402	};
11403
11404	/**
11405	 * Parse style to pixels.
11406	 *
11407	 * @param {Object}   $element   jQuery object with element.
11408	 * @param {Property} property   CSS property to get the pixels from.
11409	 *
11410	 * @return {Int}
11411	 */
11412	function getPixel($element, property) {
11413		return parseInt($element.css(property), 10) || 0;
11414	}
11415
11416	/**
11417	 * Make sure that number is within the limits.
11418	 *
11419	 * @param {Number} number
11420	 * @param {Number} min
11421	 * @param {Number} max
11422	 *
11423	 * @return {Number}
11424	 */
11425	function within(number, min, max) {
11426		return number < min ? min : number > max ? max : number;
11427	}
11428
11429	/**
11430	 * Get viewport/window size (width and height).
11431	 *
11432	 * @return {Object}
11433	 */
11434	function getViewport() {
11435		var e = window,
11436			a = 'inner';
11437		if (!('innerWidth' in window)) {
11438			a = 'client';
11439			e = document.documentElement || document.body;
11440		}
11441		return {
11442			width: e[a + 'Width'],
11443			height: e[a + 'Height']
11444		}
11445	}
11446
11447	/**
11448	 * Remove hash tag from the URL
11449	 *
11450	 * @return {Void}
11451	 */
11452	function removeHash() {
11453		var scroll = getScrollXY();
11454
11455		//UNCODE change
11456		//window.location.hash = "";
11457		history.pushState("", document.title, window.location.pathname + window.location.search);
11458
11459		// Restore the scroll offset, should be flicker free
11460		window.scrollTo(scroll.x, scroll.y);
11461	}
11462
11463	/**
11464	 * Do the ajax requests with callback.
11465	 *
11466	 * @param {String}   url
11467	 * @param {Function} callback
11468	 *
11469	 * @return {Void}
11470	 */
11471	function doAjax(url, callback) {
11472		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');
11473		$.ajax({
11474			url: url,
11475			dataType: 'jsonp'
11476		});
11477
11478		iLCallback = function(data) {
11479			callback.call(this, data);
11480		};
11481	}
11482
11483	/**
11484	 * Find image from DOM elements
11485	 *
11486	 * @param {Element} element
11487	 *
11488	 * @return {Void}
11489	 */
11490	function findImageInElement(element) {
11491		var elements = $('*', element),
11492			imagesArr = new Array();
11493
11494		elements.each(function() {
11495			var url = "",
11496				element = this;
11497
11498			if ($(element).css("background-image") != "none") {
11499				url = $(element).css("background-image");
11500			} else if (typeof($(element).attr("src")) != "undefined" && element.nodeName.toLowerCase() == "img") {
11501				url = $(element).attr("src");
11502			}
11503
11504			if (url.indexOf("gradient") == -1) {
11505				url = url.replace(/url\(\"/g, "");
11506				url = url.replace(/url\(/g, "");
11507				url = url.replace(/\"\)/g, "");
11508				url = url.replace(/\)/g, "");
11509
11510				var urls = url.split(",");
11511
11512				for (var i = 0; i < urls.length; i++) {
11513					if (urls[i].length > 0 && $.inArray(urls[i], imagesArr) == -1) {
11514						var extra = "";
11515						if (browser.msie && browser.version < 9) {
11516							extra = "?" + floor(random() * 3000);
11517						}
11518						imagesArr.push(urls[i] + extra);
11519					}
11520				}
11521			}
11522		});
11523
11524		return imagesArr;
11525	}
11526
11527	/**
11528	 * Get file extension.
11529	 *
11530	 * @param {String} URL
11531	 *
11532	 * @return {String}
11533	 */
11534	function getExtension(URL) {
11535		if (URL !== null) {
11536			var ext = URL.split('.').pop().toLowerCase(),
11537				extra = ext.indexOf('?') !== -1 ? ext.split('?').pop() : '';
11538
11539			//UNCODE: fix issue to detect media type with query strings
11540			ext = ext.replace('?'+extra, '');
11541
11542			return ext.replace(extra, '');
11543		}
11544	}
11545
11546	/**
11547	 * Get type via extension.
11548	 *
11549	 * @param {String} URL
11550	 *
11551	 * @return {String}
11552	 */
11553	function getTypeByExtension(URL) {
11554		var type,
11555			ext = getExtension(URL);
11556
11557		if (extensions.image.indexOf(ext) !== -1) type = 'image';
11558		else if (extensions.flash.indexOf(ext) !== -1) type = 'flash';
11559		else if (extensions.video.indexOf(ext) !== -1) type = 'video';
11560		else if (ext.substring(0, 1) == "#") type = 'inline'; //Uncode addition
11561		else type = 'iframe';
11562
11563		return type;
11564	}
11565
11566	/**
11567	 * Return value from percent of a number.
11568	 *
11569	 * @param {Number} percent
11570	 * @param {Number} total
11571	 *
11572	 * @return {Number}
11573	 */
11574	function percentToValue(percent, total) {
11575		return parseInt((total / 100) * percent);
11576	}
11577
11578	/**
11579	 * A JavaScript equivalent of PHP’s parse_url.
11580	 *
11581	 * @param {String} url           The URL to parse.
11582	 *
11583	 * @return {Mixed}
11584	 */
11585	function parseURI(url) {
11586		var m = String(url).replace(/^\s+|\s+$/g, '').match(/^([^:\/?#]+:)?(\/\/(?:[^:@]*(?::[^:@]*)?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/);
11587		// authority = '//' + user + ':' + pass '@' + hostname + ':' port
11588		return (m ? {
11589			href: m[0] || '',
11590			protocol: m[1] || '',
11591			authority: m[2] || '',
11592			host: m[3] || '',
11593			hostname: m[4] || '',
11594			port: m[5] || '',
11595			pathname: m[6] || '',
11596			search: m[7] || '',
11597			hash: m[8] || ''
11598		} : null);
11599	}
11600
11601	/**
11602	 * Gets the absolute URI.
11603	 *
11604	 * @param {String} href     The relative URL.
11605	 * @param {String} base     The base URL.
11606	 *
11607	 * @return {String}         The absolute URL.
11608	 */
11609	function absolutizeURI(base, href) { // RFC 3986
11610		var iL = this;
11611
11612		function removeDotSegments(input) {
11613			var output = [];
11614			input.replace(/^(\.\.?(\/|$))+/, '')
11615				.replace(/\/(\.(\/|$))+/g, '/')
11616				.replace(/\/\.\.$/, '/../')
11617				.replace(/\/?[^\/]*/g, function(p) {
11618					if (p === '/..') {
11619						output.pop();
11620					} else {
11621						output.push(p);
11622					}
11623				});
11624			return output.join('').replace(/^\//, input.charAt(0) === '/' ? '/' : '');
11625		}
11626
11627		href = parseURI(href || '');
11628		base = parseURI(base || '');
11629
11630		return !href || !base ? null : (href.protocol || base.protocol) +
11631			(href.protocol || href.authority ? href.authority : base.authority) +
11632			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)) +
11633			(href.protocol || href.authority || href.pathname ? href.search : (href.search || base.search)) +
11634			href.hash;
11635	}
11636
11637	/**
11638	 * A JavaScript equivalent of PHP’s version_compare.
11639	 *
11640	 * @param {String} v1
11641	 * @param {String} v2
11642	 * @param {String} operator
11643	 *
11644	 * @return {Boolean}
11645	 */
11646	function version_compare(v1, v2, operator) {
11647		this.php_js = this.php_js || {};
11648		this.php_js.ENV = this.php_js.ENV || {};
11649		var i = 0,
11650			x = 0,
11651			compare = 0,
11652			vm = {
11653				'dev': -6,
11654				'alpha': -5,
11655				'a': -5,
11656				'beta': -4,
11657				'b': -4,
11658				'RC': -3,
11659				'rc': -3,
11660				'#': -2,
11661				'p': 1,
11662				'pl': 1
11663			},
11664			prepVersion = function(v) {
11665				v = ('' + v).replace(/[_\-+]/g, '.');
11666				v = v.replace(/([^.\d]+)/g, '.$1.').replace(/\.{2,}/g, '.');
11667				return (!v.length ? [-8] : v.split('.'));
11668			},
11669			numVersion = function(v) {
11670				return !v ? 0 : (isNaN(v) ? vm[v] || -7 : parseInt(v, 10));
11671			};
11672		v1 = prepVersion(v1);
11673		v2 = prepVersion(v2);
11674		x = max(v1.length, v2.length);
11675		for (i = 0; i < x; i++) {
11676			if (v1[i] == v2[i]) {
11677				continue;
11678			}
11679			v1[i] = numVersion(v1[i]);
11680			v2[i] = numVersion(v2[i]);
11681			if (v1[i] < v2[i]) {
11682				compare = -1;
11683				break;
11684			} else if (v1[i] > v2[i]) {
11685				compare = 1;
11686				break;
11687			}
11688		}
11689		if (!operator) {
11690			return compare;
11691		}
11692
11693		switch (operator) {
11694			case '>':
11695			case 'gt':
11696				return (compare > 0);
11697			case '>=':
11698			case 'ge':
11699				return (compare >= 0);
11700			case '<=':
11701			case 'le':
11702				return (compare <= 0);
11703			case '==':
11704			case '=':
11705			case 'eq':
11706				return (compare === 0);
11707			case '<>':
11708			case '!=':
11709			case 'ne':
11710				return (compare !== 0);
11711			case '':
11712			case '<':
11713			case 'lt':
11714				return (compare < 0);
11715			default:
11716				return null;
11717		}
11718	}
11719
11720
11721	// Begin the iLightBox plugin
11722	$.fn.iLightBox = function() {
11723
11724		var args = arguments,
11725			opt = ($.isPlainObject(args[0])) ? args[0] : args[1],
11726			items = ($.isArray(args[0]) || typeof args[0] == 'string') ? args[0] : args[1];
11727
11728		if (!opt) opt = {};
11729
11730		// Default options. Play carefully.
11731		var options = $.extend(true, {
11732			attr: 'href',
11733			path: 'vertical',
11734			skin: 'dark',
11735			linkId: false,
11736			infinite: false,
11737			startFrom: 0,
11738			randomStart: false,
11739			keepAspectRatio: true,
11740			maxScale: 1,
11741			minScale: .2,
11742			innerToolbar: false,
11743			smartRecognition: false,
11744			mobileOptimizer: true,
11745			fullAlone: true,
11746			fullViewPort: null,
11747			fullStretchTypes: 'flash, video',
11748			overlay: {
11749				blur: true,
11750				opacity: .85
11751			},
11752			controls: {
11753				arrows: false,
11754				slideshow: false,
11755				toolbar: true,
11756				fullscreen: true,
11757				thumbnail: true,
11758				keyboard: true,
11759				mousewheel: true,
11760				swipe: true
11761			},
11762			keyboard: {
11763				left: true, // previous
11764				right: true, // next
11765				up: true, // previous
11766				down: true, // next
11767				esc: true, // close
11768				shift_enter: true // fullscreen
11769			},
11770			show: {
11771				effect: true,
11772				speed: 300,
11773				title: true
11774			},
11775			hide: {
11776				effect: true,
11777				speed: 300
11778			},
11779			caption: {
11780				start: true,
11781				show: 'mouseenter',
11782				hide: 'mouseleave'
11783			},
11784			social: {
11785				start: true,
11786				show: 'mouseenter',
11787				hide: 'mouseleave',
11788				buttons: false
11789			},
11790			styles: {
11791				pageOffsetX: 0,
11792				pageOffsetY: 0,
11793				nextOffsetX: 45,
11794				nextOffsetY: 0,
11795				nextOpacity: 1,
11796				nextScale: 1,
11797				prevOffsetX: 45,
11798				prevOffsetY: 0,
11799				prevOpacity: 1,
11800				prevScale: 1
11801			},
11802			thumbnails: {
11803				maxWidth: 120,
11804				maxHeight: 80,
11805				normalOpacity: 1,
11806				activeOpacity: .6
11807			},
11808			effects: {
11809				reposition: true,
11810				repositionSpeed: 200,
11811				switchSpeed: 500,
11812				loadedFadeSpeed: 180,
11813				fadeSpeed: 200
11814			},
11815			slideshow: {
11816				pauseTime: 5000,
11817				pauseOnHover: false,
11818				startPaused: true
11819			},
11820			text: {
11821				close: "Press Esc to close",
11822				enterFullscreen: "Enter Fullscreen (Shift+Enter)",
11823				exitFullscreen: "Exit Fullscreen (Shift+Enter)",
11824				slideShow: "Slideshow",
11825				next: "Next",
11826				previous: "Previous"
11827			},
11828			errors: {
11829				loadImage: "An error occurred when trying to load photo.",
11830				loadContents: "An error occurred when trying to load contents.",
11831				missingPlugin: "The content your are attempting to view requires the <a href='{pluginspage}' target='_blank'>{type} plugin<\/a>."
11832			},
11833			ajaxSetup: {
11834				url: '',
11835				beforeSend: function(jqXHR, settings) {},
11836				cache: false,
11837				complete: function(jqXHR, textStatus) {},
11838				crossDomain: false,
11839				error: function(jqXHR, textStatus, errorThrown) {},
11840				success: function(data, textStatus, jqXHR) {},
11841				global: true,
11842				ifModified: false,
11843				username: null,
11844				password: null,
11845				type: 'GET'
11846			},
11847			callback: {}
11848		}, opt);
11849
11850		var instant = ($.isArray(items) || typeof items == 'string') ? true : false;
11851
11852		items = $.isArray(items) ? items : new Array();
11853
11854		if (typeof args[0] == 'string') items[0] = args[0];
11855
11856		if (version_compare($.fn.jquery, '1.8', '>=')) {
11857			var iLB = new iLightBox($(this), options, items, instant);
11858			return {
11859				close: function() {
11860					iLB.closeAction();
11861				},
11862				fullscreen: function() {
11863					iLB.fullScreenAction();
11864				},
11865				moveNext: function() {
11866					iLB.moveTo('next');
11867				},
11868				movePrev: function() {
11869					iLB.moveTo('prev');
11870				},
11871				goTo: function(index) {
11872					iLB.goTo(index);
11873				},
11874				refresh: function() {
11875					iLB.refresh();
11876				},
11877				reposition: function() {
11878					(arguments.length > 0) ? iLB.repositionPhoto(true): iLB.repositionPhoto();
11879				},
11880				setOption: function(options) {
11881					iLB.setOption(options);
11882				},
11883				destroy: function() {
11884					iLB.closeAction();
11885					iLB.dispatchItemsEvents();
11886				}
11887			};
11888		} else {
11889			throw "The jQuery version that was loaded is too old. iLightBox requires jQuery 1.8+";
11890		}
11891
11892	};
11893
11894
11895	$.iLightBox = function() {
11896		return $.fn.iLightBox(arguments[0], arguments[1]);
11897	};
11898
11899
11900	$.extend($.easing, {
11901		easeInCirc: function(x, t, b, c, d) {
11902			return -c * (sqrt(1 - (t /= d) * t) - 1) + b;
11903		},
11904		easeOutCirc: function(x, t, b, c, d) {
11905			return c * sqrt(1 - (t = t / d - 1) * t) + b;
11906		},
11907		easeInOutCirc: function(x, t, b, c, d) {
11908			if ((t /= d / 2) < 1) return -c / 2 * (sqrt(1 - t * t) - 1) + b;
11909			return c / 2 * (sqrt(1 - (t -= 2) * t) + 1) + b;
11910		}
11911	});
11912
11913	function getScrollXY() {
11914		var scrOfX = 0,
11915			scrOfY = 0;
11916		if (typeof(window.pageYOffset) == 'number') {
11917			//Netscape compliant
11918			scrOfY = window.pageYOffset;
11919			scrOfX = window.pageXOffset;
11920		} else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) {
11921			//DOM compliant
11922			scrOfY = document.body.scrollTop;
11923			scrOfX = document.body.scrollLeft;
11924		} else if (document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) {
11925			//IE6 standards compliant mode
11926			scrOfY = document.documentElement.scrollTop;
11927			scrOfX = document.documentElement.scrollLeft;
11928		}
11929		return {
11930			x: scrOfX,
11931			y: scrOfY
11932		};
11933	}
11934
11935	(function() {
11936		// add new event shortcuts
11937		$.each(("touchstart touchmove touchend " +
11938			"tap taphold " +
11939			"swipe swipeleft swiperight " +
11940			"scrollstart scrollstop").split(" "), function(i, name) {
11941
11942			$.fn[name] = function(fn) {
11943				return fn ? this.bind(name, fn) : this.trigger(name);
11944			};
11945
11946			// jQuery < 1.8
11947			// if ($.attrFn) {
11948			// 	$.attrFn[name] = true;
11949			// }
11950		});
11951
11952		var tapSettings = {
11953			startEvent: 'touchstart.iTap',
11954			endEvent: 'touchend.iTap'
11955		};
11956
11957		// tap Event:
11958		$.event.special.itap = {
11959			setup: function() {
11960
11961				var self = this,
11962					$self = $(this),
11963					start, stop;
11964
11965				$self.bind(tapSettings.startEvent, function(event) {
11966					start = getScrollXY();
11967
11968					$self.one(tapSettings.endEvent, function(event) {
11969						stop = getScrollXY();
11970
11971						var orgEvent = event || window.event;
11972						event = $.event.fix(orgEvent);
11973						event.type = "itap";
11974
11975						if ((start && stop) && (start.x == stop.x && start.y == stop.y))($.event.dispatch || $.event.handle).call(self, event);
11976
11977						start = stop = undefined;
11978					});
11979				});
11980			},
11981
11982			teardown: function() {
11983				$(this).unbind(tapSettings.startEvent);
11984			}
11985		};
11986	}());
11987
11988
11989	//Fullscreen API
11990	(function() {
11991		fullScreenApi = {
11992				supportsFullScreen: false,
11993				isFullScreen: function() {
11994					return false;
11995				},
11996				requestFullScreen: function() {},
11997				cancelFullScreen: function() {},
11998				fullScreenEventName: '',
11999				prefix: ''
12000			},
12001			browserPrefixes = 'webkit moz o ms khtml'.split(' ');
12002
12003		// check for native support
12004		if (typeof document.cancelFullScreen != 'undefined') {
12005			fullScreenApi.supportsFullScreen = true;
12006		} else {
12007			// check for fullscreen support by vendor prefix
12008			for (var i = 0, il = browserPrefixes.length; i < il; i++) {
12009				fullScreenApi.prefix = browserPrefixes[i];
12010
12011				if (typeof document[fullScreenApi.prefix + 'CancelFullScreen'] != 'undefined') {
12012					fullScreenApi.supportsFullScreen = true;
12013
12014					break;
12015				}
12016			}
12017		}
12018
12019		// update methods to do something useful
12020		if (fullScreenApi.supportsFullScreen) {
12021			fullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange';
12022
12023			fullScreenApi.isFullScreen = function() {
12024				switch (this.prefix) {
12025					case '':
12026						return document.fullScreen;
12027					case 'webkit':
12028						return document.webkitIsFullScreen;
12029					default:
12030						return document[this.prefix + 'FullScreen'];
12031				}
12032			}
12033			fullScreenApi.requestFullScreen = function(el) {
12034				return (this.prefix === '') ? el.requestFullScreen() : el[this.prefix + 'RequestFullScreen']();
12035			}
12036			fullScreenApi.cancelFullScreen = function(el) {
12037				return (this.prefix === '') ? document.cancelFullScreen() : document[this.prefix + 'CancelFullScreen']();
12038			}
12039		}
12040	}());
12041
12042	// Browser detect
12043	(function() {
12044		function uaMatch(ua) {
12045			ua = ua.toLowerCase();
12046
12047			var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
12048				/(webkit)[ \/]([\w.]+)/.exec(ua) ||
12049				/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
12050				/(msie) ([\w.]+)/.exec(ua) ||
12051				ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || [];
12052
12053			return {
12054				browser: match[1] || "",
12055				version: match[2] || "0"
12056			};
12057		}
12058
12059		var matched = uaMatch(navigator.userAgent);
12060		browser = {};
12061
12062		if (matched.browser) {
12063			browser[matched.browser] = true;
12064			browser.version = matched.version;
12065		}
12066
12067		// Chrome is Webkit, but Webkit is also Safari.
12068		if (browser.chrome) {
12069			browser.webkit = true;
12070		} else if (browser.webkit) {
12071			browser.safari = true;
12072		}
12073	}());
12074
12075	// Feature detects
12076	(function() {
12077		var prefixes = ['', 'webkit', 'moz', 'ms', 'o'];
12078		var el = document.createElement('div');
12079
12080		function testProp(prop) {
12081			for (var p = 0, pl = prefixes.length; p < pl; p++) {
12082				var prefixedProp = prefixes[p] ? prefixes[p] + prop.charAt(0).toUpperCase() + prop.slice(1) : prop;
12083				if (el.style[prefixedProp] !== undefined) {
12084					return prefixedProp;
12085				}
12086			}
12087		}
12088
12089		// Global support indicators
12090		transform = testProp('transform') || '';
12091		gpuAcceleration = testProp('perspective') ? 'translateZ(0) ' : '';
12092	}());
12093
12094
12095	/*
12096		PluginDetect v0.7.9
12097		www.pinlady.net/PluginDetect/license/
12098		[ getVersion onWindowLoaded BetterIE ]
12099		[ Flash QuickTime Shockwave ]
12100	*/
12101	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;
12101d++){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);
12101d.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)}
12101c.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;
12101if(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();
12102
12103	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)};
12104
12105
12106	/*
12107		jQuery hashchange event v1.3
12108		https://github.com/cowboy/jquery-hashchange
12109		Copyright (c) 2010 "Cowboy" Ben Alman
12110		Dual licensed under the MIT and GPL licenses.
12111	*/
12112	(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=
12113	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||
12114	"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;
12114a!==d&&(b.title=k.title,b.open(),e&&b.write('<script>document.domain="'+e+'"\x3c/script>'),b.close(),c.location.hash=a)}}();return h}()})();
12115
12116	if (!Array.prototype.filter) {
12117		Array.prototype.filter = function(fun /*, thisp */ ) {
12118			"use strict";
12119
12120			if (this == null)
12121				throw new TypeError();
12122
12123			var t = Object(this);
12124			var len = t.length >>> 0;
12125			if (typeof fun != "function")
12126				throw new TypeError();
12127
12128			var res = [];
12129			var thisp = arguments[1];
12130			for (var i = 0; i < len; i++) {
12131				if (i in t) {
12132					var val = t[i]; // in case fun mutates this
12133					if (fun.call(thisp, val, i, t))
12134						res.push(val);
12135				}
12136			}
12137
12138			return res;
12139		};
12140	}
12141
12142	if (!Array.prototype.indexOf) {
12143		Array.prototype.indexOf = function(searchElement, fromIndex) {
12144			var k;
12145
12146			if (this == null) {
12147				throw new TypeError('"this" is null or not defined');
12148			}
12149
12150			var O = Object(this);
12151
12152			var len = O.length >>> 0;
12153
12154			if (len === 0) {
12155				return -1;
12156			}
12157
12158			var n = +fromIndex || 0;
12159
12160			if (abs(n) === Infinity) {
12161				n = 0;
12162			}
12163
12164			if (n >= len) {
12165				return -1;
12166			}
12167
12168			k = max(n >= 0 ? n : len - abs(n), 0);
12169
12170			while (k < len) {
12171				var kValue;
12172				if (k in O && O[k] === searchElement) {
12173					return k;
12174				}
12175				k++;
12176			}
12177			return -1;
12178		};
12179	}
12180
12181	if (!Array.prototype.lastIndexOf) {
12182		Array.prototype.lastIndexOf = function(searchElement /*, fromIndex*/ ) {
12183			"use strict";
12184
12185			if (this == null)
12186				throw new TypeError();
12187
12188			var t = Object(this);
12189			var len = t.length >>> 0;
12190			if (len === 0)
12191				return -1;
12192
12193			var n = len;
12194			if (arguments.length > 1) {
12195				n = Number(arguments[1]);
12196				if (n != n)
12197					n = 0;
12198				else if (n != 0 && n != (1 / 0) && n != -(1 / 0))
12199					n = (n > 0 || -1) * floor(abs(n));
12200			}
12201
12202			var k = n >= 0 ? min(n, len - 1) : len - abs(n);
12203
12204			for (; k >= 0; k--) {
12205				if (k in t && t[k] === searchElement)
12206					return k;
12207			}
12208			return -1;
12209		};
12210	}
12211})(jQuery, this);
12212
12213/**
12214 * Owl carousel
vendor: 12,109 bytes, lines 12215-12721
12215 * @version 2.0.0-beta.3
12216 * @author Bartosz Wojciechowski
12217 * @license The MIT License (MIT)
12218 * @todo Lazy Load Icon
12219 * @todo prevent animationend bubling
12220 * @todo itemsScaleUp
12221 * @todo Test Zepto
12222 * @todo stagePadding calculate wrong active classes
12223 */
12224;(function($, window, document, undefined) {
12225
12226	/**
12227	 * Creates a carousel.
12228	 * @class The Owl Carousel.
12229	 * @public
12230	 * @param {HTMLElement|jQuery} element - The element to create the carousel for.
12231	 * @param {Object} [options] - The options
12232	 */
12233	function Owl(element, options) {
12234
12235		/**
12236		 * Current settings for the carousel.
12237		 * @public
12238		 */
12239		this.settings = null;
12240
12241		/**
12242		 * Current options set by the caller including defaults.
12243		 * @public
12244		 */
12245		this.options = $.extend({}, Owl.Defaults, options);
12246 
12247		/**
12248		 * Plugin element.
12249		 * @public
12250		 */
12251		this.$element = $(element);
12252
12253		/**
12254		 * Proxied event handlers.
12255		 * @protected
12256		 */
12257		this._handlers = {};
12258
12259		/**
12260		 * References to the running plugins of this carousel.
12261		 * @protected
12262		 */
12263		this._plugins = {};
12264
12265		/**
12266		 * Currently suppressed events to prevent them from beeing retriggered.
12267		 * @protected
12268		 */
12269		this._supress = {};
12270
12271		/**
12272		 * Absolute current position.
12273		 * @protected
12274		 */
12275		this._current = null;
12276
12277		/**
12278		 * Animation speed in milliseconds.
12279		 * @protected
12280		 */
12281		this._speed = null;
12282
12283		/**
12284		 * Coordinates of all items in pixel.
12285		 * @todo The name of this member is missleading.
12286		 * @protected
12287		 */
12288		this._coordinates = [];
12289
12290		/**
12291		 * Current breakpoint.
12292		 * @todo Real media queries would be nice.
12293		 * @protected
12294		 */
12295		this._breakpoint = null;
12296
12297		/**
12298		 * Current width of the plugin element.
12299		 */
12300		this._width = null;
12301
12302		/**
12303		 * All real items.
12304		 * @protected
12305		 */
12306		this._items = [];
12307
12308		/**
12309		 * All cloned items.
12310		 * @protected
12311		 */
12312		this._clones = [];
12313
12314		/**
12315		 * Merge values of all items.
12316		 * @todo Maybe this could be part of a plugin.
12317		 * @protected
12318		 */
12319		this._mergers = [];
12320
12321		/**
12322		 * Widths of all items.
12323		 */
12324		this._widths = [];
12325
12326		/**
12327		 * Invalidated parts within the update process.
12328		 * @protected
12329		 */
12330		this._invalidated = {};
12331
12332		/**
12333		 * Ordered list of workers for the update process.
12334		 * @protected
12335		 */
12336		this._pipe = [];
12337
12338		/**
12339		 * Current state information for the drag operation.
12340		 * @todo #261
12341		 * @protected
12342		 */
12343		this._drag = {
12344			time: null,
12345			target: null,
12346			pointer: null,
12347			stage: {
12348				start: null,
12349				current: null
12350			},
12351			direction: null
12352		};
12353
12354		/**
12355		 * Current state information and their tags.
12356		 * @type {Object}
12357		 * @protected
12358		 */
12359		this._states = {
12360			current: {},
12361			tags: {
12362				'initializing': [ 'busy' ],
12363				'animating': [ 'busy' ],
12364				'dragging': [ 'interacting' ]
12365			}
12366		};
12367
12368		$.each([ 'onResize', 'onThrottledResize' ], $.proxy(function(i, handler) {
12369			this._handlers[handler] = $.proxy(this[handler], this);
12370		}, this));
12371
12372		$.each(Owl.Plugins, $.proxy(function(key, plugin) {
12373			this._plugins[key.charAt(0).toLowerCase() + key.slice(1)]
12374				= new plugin(this);
12375		}, this));
12376
12377		$.each(Owl.Workers, $.proxy(function(priority, worker) {
12378			this._pipe.push({
12379				'filter': worker.filter,
12380				'run': $.proxy(worker.run, this)
12381			});
12382		}, this));
12383
12384		this.setup();
12385		this.initialize();
12386	}
12387
12388	/**
12389	 * Default options for the carousel.
12390	 * @public
12391	 */
12392	Owl.Defaults = {
12393		items: 3,
12394		loop: false,
12395		center: false,
12396		rewind: false,
12397
12398		mouseDrag: true,
12399		touchDrag: true,
12400		pullDrag: true,
12401		freeDrag: false,
12402
12403		margin: 0,
12404		stagePadding: 0,
12405
12406		merge: false,
12407		mergeFit: true,
12408		autoWidth: false,
12409
12410		startPosition: 0,
12411		rtl: false,
12412
12413		smartSpeed: 250,
12414		fluidSpeed: false,
12415		dragEndSpeed: false,
12416
12417		responsive: {},
12418		responsiveRefreshRate: 200,
12419		responsiveBaseElement: window,
12420
12421		fallbackEasing: 'swing',
12422
12423		info: false,
12424
12425		nestedItemSelector: false,
12426		itemElement: 'div',
12427		stageElement: 'div',
12428
12429		refreshClass: 'owl-refresh',
12430		loadedClass: 'owl-loaded',
12431		loadingClass: 'owl-loading',
12432		rtlClass: 'owl-rtl',
12433		responsiveClass: 'owl-responsive',
12434		dragClass: 'owl-drag',
12435		itemClass: 'owl-item',
12436		stageClass: 'owl-stage',
12437		stageOuterClass: 'owl-stage-outer',
12438		grabClass: 'owl-grab'
12439	};
12440
12441	/**
12442	 * Enumeration for width.
12443	 * @public
12444	 * @readonly
12445	 * @enum {String}
12446	 */
12447	Owl.Width = {
12448		Default: 'default',
12449		Inner: 'inner',
12450		Outer: 'outer'
12451	};
12452
12453	/**
12454	 * Enumeration for types.
12455	 * @public
12456	 * @readonly
12457	 * @enum {String}
12458	 */
12459	Owl.Type = {
12460		Event: 'event',
12461		State: 'state'
12462	};
12463
12464	/**
12465	 * Contains all registered plugins.
12466	 * @public
12467	 */
12468	Owl.Plugins = {};
12469
12470	/**
12471	 * List of workers involved in the update process.
12472	 */
12473	Owl.Workers = [ {
12474		filter: [ 'width', 'settings' ],
12475		run: function() {
12476			this._width = (this.$element.closest('.px-gutter').length) ? 12 * Math.ceil(this.$element.width() / 12) : this.$element.width();
12477			//this._width = (UNCODE.isMobile) ? this.$element.width() : 12 * Math.ceil(this.$element.width() / 12);
12478		}
12479	}, {
12480		filter: [ 'width', 'items', 'settings' ],
12481		run: function(cache) {
12482			cache.current = this._items && this._items[this.relative(this._current)];
12483		}
12484	}, {
12485		filter: [ 'items', 'settings' ],
12486		run: function() {
12487			this.$stage.children('.cloned').remove();
12488		}
12489	}, {
12490		filter: [ 'width', 'items', 'settings' ],
12491		run: function(cache) {
12492			var margin = this.settings.margin || '',
12493				grid = !this.settings.autoWidth,
12494				rtl = this.settings.rtl,
12495				css = {
12496					'width': 'auto',
12497					'margin-left': rtl ? margin : '',
12498					'margin-right': rtl ? '' : margin
12499				};
12500
12501			!grid && this.$stage.children().css(css);
12502
12503			cache.css = css;
12504		}
12505	}, {
12506		filter: [ 'width', 'items', 'settings' ],
12507		run: function(cache) {
12508			//UNCODE edit to fix Chrome stage padding issue
12509			var width = Math.round((this.width() / this.settings.items).toFixed(3) - this.settings.margin),
12510				merge = null,
12511				iterator = this._items.length,
12512				grid = !this.settings.autoWidth,
12513				widths = [];
12514
12515			cache.items = {
12516				merge: false,
12517				width: width
12518			};
12519
12520			while (iterator--) {
12521				merge = this._mergers[iterator];
12522				merge = this.settings.mergeFit && Math.min(merge, this.settings.items) || merge;
12523
12524				cache.items.merge = merge > 1 || cache.items.merge;
12525
12526				widths[iterator] = !grid ? this._items[iterator].width() : width * merge;
12527			}
12528
12529			this._widths = widths;
12530		}
12531	}, {
12532		filter: [ 'items', 'settings' ],
12533		run: function() {
12534			var clones = [],
12535				items = this._items,
12536				settings = this.settings,
12537				view = Math.max(settings.items * 2, 4),
12538				size = Math.ceil(items.length / 2) * 2,
12539				repeat = settings.loop && items.length ? settings.rewind ? view : Math.max(view, size) : 0,
12540				append = '',
12541				prepend = '';
12542
12543			repeat /= 2;
12544
12545			while (repeat--) {
12546				clones.push(this.normalize(clones.length / 2, true));
12547				append = append + items[clones[clones.length - 1]][0].outerHTML;
12548				clones.push(this.normalize(items.length - 1 - (clones.length - 1) / 2, true));
12549				prepend = items[clones[clones.length - 1]][0].outerHTML + prepend;
12550			}
12551
12552			this._clones = clones;
12553
12554			// var appendVideo = $(append).find('.uncode-video-container');
12555			// if (appendVideo.length) {
12556			// 	appendVideo.attr('data-id', Math.round(Math.random() * 100000));
12557			// }
12558			// var prependVideo = $(prepend).find('.uncode-video-container');
12559			// if (prependVideo.length) {
12560			// 	prependVideo.attr('data-id', Math.round(Math.random() * 100000));
12561			// }
12562			$(append).addClass('cloned').appendTo(this.$stage);
12563			$(prepend).addClass('cloned').prependTo(this.$stage);
12564		}
12565	}, {
12566		filter: [ 'width', 'items', 'settings' ],
12567		run: function() {
12568			var rtl = this.settings.rtl ? 1 : -1,
12569				size = this._clones.length + this._items.length,
12570				iterator = -1,
12571				previous = 0,
12572				current = 0,
12573				coordinates = [];
12574
12575			while (++iterator < size) {
12576				previous = coordinates[iterator - 1] || 0;
12577				current = this._widths[this.relative(iterator)] + this.settings.margin;
12578				coordinates.push(previous + current * rtl);
12579			}
12580
12581			this._coordinates = coordinates;
12582		}
12583	}, {
12584		filter: [ 'width', 'items', 'settings' ],
12585		run: function() {
12586			var stagePadding = (this._width < 480 && this.settings.stagePadding > 0) ? 41 : (this._width * this.settings.stagePadding) / 200,
12587				padding = stagePadding,
12588				coordinates = this._coordinates,
12589				css = {
12590					'width': Math.ceil(Math.abs(coordinates[coordinates.length - 1])) + padding * 2,
12591					'padding-left': padding || '',
12592					'padding-right': padding || ''
12593				};
12594
12595			this.$stage.css(css);
12596		}
12597	}, {
12598		filter: [ 'width', 'items', 'settings' ],
12599		run: function(cache) {
12600			var iterator = this._coordinates.length,
12601				grid = !this.settings.autoWidth,
12602				items = this.$stage.children();
12603
12604			if (grid && cache.items.merge) {
12605				while (iterator--) {
12606					cache.css.width = this._widths[this.relative(iterator)];
12607					items.eq(iterator).css(cache.css);
12608				}
12609			} else if (grid) {
12610				cache.css.width = cache.items.width;
12611				items.css(cache.css);
12612			}
12613		}
12614	}, {
12615		filter: [ 'items' ],
12616		run: function() {
12617			this._coordinates.length < 1 && this.$stage.removeAttr('style');
12618		}
12619	}, {
12620		filter: [ 'width', 'items', 'settings' ],
12621		run: function(cache) {
12622			cache.current = cache.current ? this.$stage.children().index(cache.current) : 0;
12623			cache.current = Math.max(this.minimum(), Math.min(this.maximum(), cache.current));
12624			this.reset(cache.current);
12625		}
12626	}, {
12627		filter: [ 'position' ],
12628		run: function() {
12629			this.animate(this.coordinates(this._current));
12630		}
12631	}, {
12632		filter: [ 'width', 'position', 'items', 'settings' ],
12633		run: function() {
12634			var stagePadding = (this._width < 480 && this.settings.stagePadding > 0) ? 41 : (this._width * this.settings.stagePadding) / 200,
12635				rtl = this.settings.rtl ? 1 : -1,
12636				padding = this.settings.stagePadding * 2,
12637				begin = this.coordinates(this.current()) + padding,
12638				end = begin + this.width() * rtl,
12639				inner, outer, matches = [], i, n;
12640
12641			for (i = 0, n = this._coordinates.length; i < n; i++) {
12642				inner = this._coordinates[i - 1] || 0;
12643				outer = Math.abs(this._coordinates[i]) + padding * rtl;
12644
12645				if ((this.op(inner, '<=', begin) && (this.op(inner, '>', end)))
12646					|| (this.op(outer, '<', begin) && this.op(outer, '>', end))) {
12647					matches.push(i);
12648				}
12649			}
12650
12651			this.$stage.children('.active').removeClass('active');
12652			this.$stage.children(':eq(' + matches.join('), :eq(') + ')').addClass('active');
12653
12654			if (this.settings.center) {
12655				this.$stage.children('.center').removeClass('center');
12656				this.$stage.children().eq(this.current()).addClass('center');
12657			}
12658		}
12659	} ];
12660
12661	/**
12662	 * Initializes the carousel.
12663	 * @protected
12664	 */
12665	Owl.prototype.initialize = function() {
12666		this.enter('initializing');
12667		this.trigger('initialize');
12668
12669		this.$element.toggleClass(this.settings.rtlClass, this.settings.rtl);
12670
12671		if (this.settings.autoWidth && !this.is('pre-loading')) {
12672			var imgs, nestedSelector, width;
12673			imgs = this.$element.find('img');
12674			nestedSelector = this.settings.nestedItemSelector ? '.' + this.settings.nestedItemSelector : undefined;
12675			width = this.$element.children(nestedSelector).width();
12676
12677			if (imgs.length && width <= 0) {
12678				this.preloadAutoWidthImages(imgs);
12679			}
12680		}
12681
12682		this.$element.addClass(this.options.loadingClass);
12683
12684		// create stage
12685		this.$stage = $('<' + this.settings.stageElement + ' class="' + this.settings.stageClass + '"/>')
12686			.wrap('<div class="' + this.settings.stageOuterClass + '"/>');
12687
12688		// append stage
12689		this.$element.append(this.$stage.parent());
12690
12691		// append content
12692		this.replace(this.$element.children().not(this.$stage.parent()));
12693
12694		// check visibility
12695		if (this.$element.is(':visible')) {
12696			// update view
12697			this.refresh();
12698		} else {
12699			// invalidate width
12700			this.invalidate('width');
12701		}
12702
12703		this.$element
12704			.removeClass(this.options.loadingClass)
12705			.addClass(this.options.loadedClass);
12706
12707		// register event handlers
12708		this.registerEventHandlers();
12709
12710		this.leave('initializing');
12711		this.trigger('initialized');
12712	};
12713
12714	/**
12715	 * Setups the current settings.
12716	 * @todo Remove responsive classes. Why should adaptive designs be brought into IE8?
12717	 * @todo Support for media queries by using `matchMedia` would be nice.
12718	 * @public
12719	 */
12720	Owl.prototype.setup = function() {
12721		var viewport = this.viewport(),
12722			overwrites = this.options.responsive,
12723			match = -1,
12724			settings = null;
12725
12726		if (!overwrites) {
12727			settings = $.extend({}, this.options);
12728		} else {
12729			$.each(overwrites, function(breakpoint) {
12730				if (breakpoint <= viewport && breakpoint > match) {
12731					match = Number(breakpoint);
12732				}
12733			});
12734
12735			settings = $.extend({}, this.options, overwrites[match]);
12736			delete settings.responsive;
12737
12738			// responsive class
12739			if (settings.responsiveClass) {
12740				this.$element.attr('class',
12741					this.$element.attr('class').replace(new RegExp('(' + this.options.responsiveClass + '-)\\S+\\s', 'g'), '$1' + match)
12742				);
12743			}
12744		}
12745
12746		if (this.settings === null || this._breakpoint !== match) {
12747			this.trigger('change', { property: { name: 'settings', value: settings } });
12748			this._breakpoint = match;
12749			this.settings = settings;
12750			this.invalidate('settings');
12751			this.trigger('changed', { property: { name: 'settings', value: this.settings } });
12752		}
12753	};
12754
12755	/**
12756	 * Updates option logic if necessery.
12757	 * @protected
12758	 */
12759	Owl.prototype.optionsLogic = function() {
12760		if (this.settings.autoWidth) {
12761			this.settings.stagePadding = false;
12762			this.settings.merge = false;
12763		}
12764	};
12765
12766	/**
12767	 * Prepares an item before add.
12768	 * @todo Rename event parameter `content` to `item`.
12769	 * @protected
12770	 * @returns {jQuery|HTMLElement} - The item container.
12771	 */
12772	Owl.prototype.prepare = function(item, index) {
12773		var event = this.trigger('prepare', { content: item });
12774		if (!event.data) {
12775			event.data = $('<' + this.settings.itemElement + '/>')
12776				.addClass(this.options.itemClass).attr('data-index', index + 1).append(item)
12777		}
12778
12779		this.trigger('prepared', { content: event.data });
12780
12781		return event.data;
12782	};
12783
12784	/**
12785	 * Updates the view.
12786	 * @public
12787	 */
12788	Owl.prototype.update = function() {
12789		var i = 0,
12790			n = this._pipe.length,
12791			filter = $.proxy(function(p) { return this[p] }, this._invalidated),
12792			cache = {};
12793
12794		while (i < n) {
12795			if (this._invalidated.all || $.grep(this._pipe[i].filter, filter).length > 0) {
12796				this._pipe[i].run(cache);
12797			}
12798			i++;
12799		}
12800
12801		this._invalidated = {};
12802
12803		!this.is('valid') && this.enter('valid');
12804	};
12805
12806	/**
12807	 * Gets the width of the view.
12808	 * @public
12809	 * @param {Owl.Width} [dimension=Owl.Width.Default] - The dimension to return.
12810	 * @returns {Number} - The width of the view in pixel.
12811	 */
12812	Owl.prototype.width = function(dimension) {
12813		dimension = dimension || Owl.Width.Default;
12814		var stagePadding = (this._width < 480 && this.settings.stagePadding > 0) ? 41 : (this._width * this.settings.stagePadding) / 200;
12815		switch (dimension) {
12816			case Owl.Width.Inner:
12817			case Owl.Width.Outer:
12818				return this._width;
12819			default:
12820				return this._width - stagePadding * 2 + this.settings.margin;
12821		}
12822	};
12823
12824	/**
12825	 * Refreshes the carousel primarily for adaptive purposes.
12826	 * @public
12827	 */
12828	Owl.prototype.refresh = function() {
12829		this.enter('refreshing');
12830		this.trigger('refresh');
12831
12832		this.setup();
12833
12834		this.optionsLogic();
12835
12836		this.$element.addClass(this.options.refreshClass);
12837
12838		this.update();
12839
12840		this.$element.removeClass(this.options.refreshClass);
12841
12842		this.leave('refreshing');
12843		this.trigger('refreshed');
12844	};
12845
12846	/**
12847	 * Checks window `resize` event.
12848	 * @protected
12849	 */
12850	Owl.prototype.onThrottledResize = function() {
12851		window.clearTimeout(this.resizeTimer);
12852		this.resizeTimer = window.setTimeout(this._handlers.onResize, this.settings.responsiveRefreshRate);
12853	};
12854
12855	/**
12856	 * Checks window `resize` event.
12857	 * @protected
12858	 */
12859	Owl.prototype.onResize = function() {
12860		if (!this._items.length) {
12861			return false;
12862		}
12863
12864		if (this._width === this.$element.width()) {
12865			return false;
12866		}
12867
12868		if (!this.$element.is(':visible')) {
12869			return false;
12870		}
12871
12872		this.enter('resizing');
12873
12874		if (this.trigger('resize').isDefaultPrevented()) {
12875			this.leave('resizing');
12876			return false;
12877		}
12878
12879		this.invalidate('width');
12880
12881		this.refresh();
12882
12883		this.leave('resizing');
12884		this.trigger('resized');
12885	};
12886
12887	/**
12888	 * Registers event handlers.
12889	 * @todo Check `msPointerEnabled`
12890	 * @todo #261
12891	 * @protected
12892	 */
12893	Owl.prototype.registerEventHandlers = function() {
12894		if ($.support.transition) {
12895			this.$stage.on($.support.transition.end + '.owl.core', $.proxy(this.onTransitionEnd, this));
12896		}
12897
12898		if (this.settings.responsive !== false) {
12899			this.on(window, 'resize', this._handlers.onThrottledResize);
12900		}
12901
12902		if (this.settings.mouseDrag) {
12903			this.$element.addClass(this.options.dragClass);
12904			this.$stage.on('mousedown.owl.core', $.proxy(this.onDragStart, this));
12905			this.$stage.on('dragstart.owl.core selectstart.owl.core', function() { return false });
12906		}
12907
12908		if (this.settings.touchDrag){
12909			this.$stage.on('touchstart.owl.core', $.proxy(this.onDragStart, this));
12910			this.$stage.on('touchcancel.owl.core', $.proxy(this.onDragEnd, this));
12911		}
12912	};
12913
12914	/**
12915	 * Handles `touchstart` and `mousedown` events.
12916	 * @todo Horizontal swipe threshold as option
12917	 * @todo #261
12918	 * @protected
12919	 * @param {Event} event - The event arguments.
12920	 */
12921	Owl.prototype.onDragStart = function(event) {
12922		var stage = null;
12923
12924		if (event.which === 3) {
12925			return;
12926		}
12927
vendor: 9,106 bytes, lines 12928-13243
12928		if ($.support.transform) {
12929			stage = this.$stage.css('transform').replace(/.*\(|\)| /g, '').split(',');
12930			stage = {
12931				x: stage[stage.length === 16 ? 12 : 4],
12932				y: stage[stage.length === 16 ? 13 : 5]
12933			};
12934		} else {
12935			stage = this.$stage.position();
12936			stage = {
12937				x: this.settings.rtl ?
12938					stage.left + this.$stage.width() - this.width() + this.settings.margin :
12939					stage.left,
12940				y: stage.top
12941			};
12942		}
12943
12944		if (this.is('animating')) {
12945			$.support.transform ? this.animate(stage.x) : this.$stage.stop()
12946			this.invalidate('position');
12947		}
12948
12949		this.$element.toggleClass(this.options.grabClass, event.type === 'mousedown');
12950
12951		this.speed(0);
12952
12953		this._drag.time = new Date().getTime();
12954		this._drag.target = $(event.target);
12955		this._drag.stage.start = stage;
12956		this._drag.stage.current = stage;
12957		this._drag.pointer = this.pointer(event);
12958
12959		$(document).on('mouseup.owl.core touchend.owl.core', $.proxy(this.onDragEnd, this));
12960
12961		$(document).one('mousemove.owl.core touchmove.owl.core', $.proxy(function(event) {
12962			var delta = this.difference(this._drag.pointer, this.pointer(event));
12963
12964			$(document).on('mousemove.owl.core touchmove.owl.core', $.proxy(this.onDragMove, this));
12965
12966			if (Math.abs(delta.x) < Math.abs(delta.y) && this.is('valid')) {
12967				return;
12968			}
12969
12970			event.preventDefault();
12971
12972			this.enter('dragging');
12973			this.trigger('drag');
12974		}, this));
12975	};
12976
12977	/**
12978	 * Handles the `touchmove` and `mousemove` events.
12979	 * @todo #261
12980	 * @protected
12981	 * @param {Event} event - The event arguments.
12982	 */
12983	Owl.prototype.onDragMove = function(event) {
12984		var minimum = null,
12985			maximum = null,
12986			pull = null,
12987			delta = this.difference(this._drag.pointer, this.pointer(event)),
12988			stage = this.difference(this._drag.stage.start, delta);
12989
12990		if (!this.is('dragging')) {
12991			return;
12992		}
12993
12994		event.preventDefault();
12995
12996		if (this.settings.loop) {
12997			minimum = this.coordinates(this.minimum());
12998			maximum = this.coordinates(this.maximum() + 1) - minimum;
12999			stage.x = (((stage.x - minimum) % maximum + maximum) % maximum) + minimum;
13000		} else {
13001			minimum = this.settings.rtl ? this.coordinates(this.maximum()) : this.coordinates(this.minimum());
13002			maximum = this.settings.rtl ? this.coordinates(this.minimum()) : this.coordinates(this.maximum());
13003			pull = this.settings.pullDrag ? -1 * delta.x / 5 : 0;
13004			stage.x = Math.max(Math.min(stage.x, minimum + pull), maximum + pull);
13005		}
13006
13007		this._drag.stage.current = stage;
13008
13009		this.animate(stage.x);
13010	};
13011
13012	/**
13013	 * Handles the `touchend` and `mouseup` events.
13014	 * @todo #261
13015	 * @todo Threshold for click event
13016	 * @protected
13017	 * @param {Event} event - The event arguments.
13018	 */
13019	Owl.prototype.onDragEnd = function(event) {
13020		var delta = this.difference(this._drag.pointer, this.pointer(event)),
13021			stage = this._drag.stage.current,
13022			direction = delta.x > 0 ^ this.settings.rtl ? 'left' : 'right';
13023
13024		$(document).off('.owl.core');
13025
13026		this.$element.removeClass(this.options.grabClass);
13027
13028		if (delta.x !== 0 && this.is('dragging') || !this.is('valid')) {
13029			this.speed(this.settings.dragEndSpeed || this.settings.smartSpeed);
13030			this.current(this.closest(stage.x, delta.x !== 0 ? direction : this._drag.direction));
13031			this.invalidate('position');
13032			this.update();
13033
13034			this._drag.direction = direction;
13035
13036			if (Math.abs(delta.x) > 3 || new Date().getTime() - this._drag.time > 300) {
13037				this._drag.target.one('click.owl.core', function() { return false; });
13038			}
13039		}
13040
13041		if (!this.is('dragging')) {
13042			return;
13043		}
13044
13045		this.leave('dragging');
13046		this.trigger('dragged');
13047	};
13048
13049	/**
13050	 * Gets absolute position of the closest item for a coordinate.
13051	 * @todo Setting `freeDrag` makes `closest` not reusable. See #165.
13052	 * @protected
13053	 * @param {Number} coordinate - The coordinate in pixel.
13054	 * @param {String} direction - The direction to check for the closest item. Ether `left` or `right`.
13055	 * @return {Number} - The absolute position of the closest item.
13056	 */
13057	Owl.prototype.closest = function(coordinate, direction) {
13058		var position = -1,
13059			pull = 30,
13060			width = this.width(),
13061			coordinates = this.coordinates();
13062
13063		if (!this.settings.freeDrag) {
13064			// check closest item
13065			$.each(coordinates, $.proxy(function(index, value) {
13066				if (coordinate > value - pull && coordinate < value + pull) {
13067					position = index;
13068				} else if (this.op(coordinate, '<', value)
13069					&& this.op(coordinate, '>', coordinates[index + 1] || value - width)) {
13070					position = direction === 'left' ? index + 1 : index;
13071				}
13072				return position === -1;
13073			}, this));
13074		}
13075
13076		if (!this.settings.loop) {
13077			// non loop boundries
13078			if (this.op(coordinate, '>', coordinates[this.minimum()])) {
13079				position = coordinate = this.minimum();
13080			} else if (this.op(coordinate, '<', coordinates[this.maximum()])) {
13081				position = coordinate = this.maximum();
13082			}
13083		}
13084
13085		return position;
13086	};
13087
13088	/**
13089	 * Animates the stage.
13090	 * @todo #270
13091	 * @public
13092	 * @param {Number} coordinate - The coordinate in pixels.
13093	 */
13094	Owl.prototype.animate = function(coordinate) {
13095		var animate = this.speed() > 0;
13096
13097		this.is('animating') && this.onTransitionEnd();
13098
13099		if (animate) {
13100			this.enter('animating');
13101			this.trigger('translate');
13102		}
13103
13104		if ($.support.transform3d && $.support.transition) {
13105			this.$stage.css({
13106				transform: 'translate3d(' + coordinate + 'px,0px,0px)',
13107				transition: (this.speed() / 1000) + 's'
13108			});
13109		} else if (animate) {
13110			this.$stage.animate({
13111				left: coordinate + 'px'
13112			}, this.speed(), this.settings.fallbackEasing, $.proxy(this.onTransitionEnd, this));
13113		} else {
13114			this.$stage.css({
13115				left: coordinate + 'px'
13116			});
13117		}
13118	};
13119
13120	/**
13121	 * Checks whether the carousel is in a specific state or not.
13122	 * @param {String} state - The state to check.
13123	 * @returns {Boolean} - The flag which indicates if the carousel is busy.
13124	 */
13125	Owl.prototype.is = function(state) {
13126		return this._states.current[state] && this._states.current[state] > 0;
13127	};
13128
13129	/**
13130	 * Sets the absolute position of the current item.
13131	 * @public
13132	 * @param {Number} [position] - The new absolute position or nothing to leave it unchanged.
13133	 * @returns {Number} - The absolute position of the current item.
13134	 */
13135	Owl.prototype.current = function(position) {
13136		if (position === undefined) {
13137			return this._current;
13138		}
13139
13140		if (this._items.length === 0) {
13141			return undefined;
13142		}
13143
13144		position = this.normalize(position);
13145
13146		if (this._current !== position) {
13147			var event = this.trigger('change', { property: { name: 'position', value: position } });
13148
13149			if (event.data !== undefined) {
13150				position = this.normalize(event.data);
13151			}
13152
13153			this._current = position;
13154
13155			this.invalidate('position');
13156
13157			this.trigger('changed', { property: { name: 'position', value: this._current } });
13158		}
13159
13160		return this._current;
13161	};
13162
13163	/**
13164	 * Invalidates the given part of the update routine.
13165	 * @param {String} [part] - The part to invalidate.
13166	 * @returns {Array.<String>} - The invalidated parts.
13167	 */
13168	Owl.prototype.invalidate = function(part) {
13169		if ($.type(part) === 'string') {
13170			this._invalidated[part] = true;
13171			this.is('valid') && this.leave('valid');
13172		}
13173		return $.map(this._invalidated, function(v, i) { return i });
13174	};
13175
13176	/**
13177	 * Resets the absolute position of the current item.
13178	 * @public
13179	 * @param {Number} position - The absolute position of the new item.
13180	 */
13181	Owl.prototype.reset = function(position) {
13182		position = this.normalize(position);
13183
13184		if (position === undefined) {
13185			return;
13186		}
13187
13188		this._speed = 0;
13189		this._current = position;
13190
13191		this.suppress([ 'translate', 'translated' ]);
13192
13193		this.animate(this.coordinates(position));
13194
13195		this.release([ 'translate', 'translated' ]);
13196	};
13197
13198	/**
13199	 * Normalizes an absolute or a relative position of an item.
13200	 * @public
13201	 * @param {Number} position - The absolute or relative position to normalize.
13202	 * @param {Boolean} [relative=false] - Whether the given position is relative or not.
13203	 * @returns {Number} - The normalized position.
13204	 */
13205	Owl.prototype.normalize = function(position, relative) {
13206		var n = this._items.length,
13207			m = relative ? 0 : this._clones.length;
13208
13209		if (!$.isNumeric(position) || n < 1) {
13210			position = undefined;
13211		} else if (position < 0 || position >= n + m) {
13212			position = ((position - m / 2) % n + n) % n + m / 2;
13213		}
13214
13215		return position;
13216	};
13217
13218	/**
13219	 * Converts an absolute position of an item into a relative one.
13220	 * @public
13221	 * @param {Number} position - The absolute position to convert.
13222	 * @returns {Number} - The converted position.
13223	 */
13224	Owl.prototype.relative = function(position) {
13225		position -= this._clones.length / 2;
13226		return this.normalize(position, true);
13227	};
13228
13229	/**
13230	 * Gets the maximum position for the current item.
13231	 * @public
13232	 * @param {Boolean} [relative=false] - Whether to return an absolute position or a relative position.
13233	 * @returns {Number}
13234	 */
13235	Owl.prototype.maximum = function(relative) {
13236		var settings = this.settings,
13237			maximum = this._coordinates.length,
13238			boundary = Math.abs(this._coordinates[maximum - 1]) - this._width,
13239			i = -1, j;
13240
13241		if (settings.loop) {
13242			maximum = this._clones.length / 2 + this._items.length - 1;
13243		} else if (settings.autoWidth || settings.merge) {
13244			// binary search
13245			while (maximum - i > 1) {
13246				Math.abs(this._coordinates[j = maximum + i >> 1]) < boundary
13247					? i = j : maximum = j;
13248			}
13249		} else if (settings.center) {
13250			maximum = this._items.length - 1;
13251		} else {
13252			maximum = this._items.length - settings.items;
13253		}
13254
13255		if (relative) {
13256			maximum -= this._clones.length / 2;
13257		}
13258
13259		return Math.max(maximum, 0);
13260	};
13261
13262	/**
13263	 * Gets the minimum position for the current item.
13264	 * @public
13265	 * @param {Boolean} [relative=false] - Whether to return an absolute position or a relative position.
13266	 * @returns {Number}
13267	 */
13268	Owl.prototype.minimum = function(relative) {
13269		return relative ? 0 : this._clones.length / 2;
13270	};
13271
13272	/**
13273	 * Gets an item at the specified relative position.
13274	 * @public
13275	 * @param {Number} [position] - The relative position of the item.
13276	 * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given.
13277	 */
13278	Owl.prototype.items = function(position) {
13279		if (position === undefined) {
13280			return this._items.slice();
13281		}
13282
13283		position = this.normalize(position, true);
13284		return this._items[position];
13285	};
13286
13287	/**
13288	 * Gets an item at the specified relative position.
13289	 * @public
13290	 * @param {Number} [position] - The relative position of the item.
13291	 * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given.
13292	 */
13293	Owl.prototype.mergers = function(position) {
13294		if (position === undefined) {
13295			return this._mergers.slice();
13296		}
13297
13298		position = this.normalize(position, true);
13299		return this._mergers[position];
13300	};
13301
13302	/**
13303	 * Gets the absolute positions of clones for an item.
13304	 * @public
13305	 * @param {Number} [position] - The relative position of the item.
13306	 * @returns {Array.<Number>} - The absolute positions of clones for the item or all if no position was given.
13307	 */
13308	Owl.prototype.clones = function(position) {
13309		var odd = this._clones.length / 2,
13310			even = odd + this._items.length,
13311			map = function(index) { return index % 2 === 0 ? even + index / 2 : odd - (index + 1) / 2 };
13312
13313		if (position === undefined) {
13314			return $.map(this._clones, function(v, i) { return map(i) });
13315		}
13316
13317		return $.map(this._clones, function(v, i) { return v === position ? map(i) : null });
13318	};
13319
13320	/**
13321	 * Sets the current animation speed.
13322	 * @public
13323	 * @param {Number} [speed] - The animation speed in milliseconds or nothing to leave it unchanged.
13324	 * @returns {Number} - The current animation speed in milliseconds.
13325	 */
13326	Owl.prototype.speed = function(speed) {
13327		if (speed !== undefined) {
13328			this._speed = speed;
13329		}
13330
13331		return this._speed;
13332	};
13333
13334	/**
13335	 * Gets the coordinate of an item.
13336	 * @todo The name of this method is missleanding.
13337	 * @public
13338	 * @param {Number} position - The absolute position of the item within `minimum()` and `maximum()`.
13339	 * @returns {Number|Array.<Number>} - The coordinate of the item in pixel or all coordinates.
13340	 */
13341	Owl.prototype.coordinates = function(position) {
13342		var coordinate = null;
13343
13344		if (position === undefined) {
13345			return $.map(this._coordinates, $.proxy(function(coordinate, index) {
13346				return this.coordinates(index);
13347			}, this));
13348		}
13349
13350		if (this.settings.center) {
13351			coordinate = this._coordinates[position];
13352			coordinate += (this.width() - coordinate + (this._coordinates[position - 1] || 0)) / 2 * (this.settings.rtl ? -1 : 1);
13353		} else {
13354			coordinate = this._coordinates[position - 1] || 0;
13355		}
13356
13357		return coordinate;
13358	};
13359
13360	/**
13361	 * Calculates the speed for a translation.
13362	 * @protected
13363	 * @param {Number} from - The absolute position of the start item.
13364	 * @param {Number} to - The absolute position of the target item.
13365	 * @param {Number} [factor=undefined] - The time factor in milliseconds.
13366	 * @returns {Number} - The time in milliseconds for the translation.
13367	 */
13368	Owl.prototype.duration = function(from, to, factor) {
13369		return Math.min(Math.max(Math.abs(to - from), 1), 6) * Math.abs((factor || this.settings.smartSpeed));
13370	};
13371
13372	/**
13373	 * Slides to the specified item.
13374	 * @public
13375	 * @param {Number} position - The position of the item.
13376	 * @param {Number} [speed] - The time in milliseconds for the transition.
13377	 */
13378	Owl.prototype.to = function(position, speed) {
13379		var current = this.current(),
13380			revert = null,
13381			distance = position - this.relative(current),
13382			direction = (distance > 0) - (distance < 0),
13383			items = this._items.length,
13384			minimum = this.minimum(),
13385			maximum = this.maximum();
13386
13387		if (this.settings.loop) {
13388			if (!this.settings.rewind && Math.abs(distance) > items / 2) {
13389				distance += direction * -1 * items;
13390			}
13391
13392			position = current + distance;
13393			revert = ((position - minimum) % items + items) % items + minimum;
13394
13395			if (revert !== position && revert - distance <= maximum && revert - distance > 0) {
13396				current = revert - distance;
13397				position = revert;
13398				this.reset(current);
13399			}
13400		} else if (this.settings.rewind) {
13401			maximum += 1;
13402			position = (position % maximum + maximum) % maximum;
13403		} else {
13404			position = Math.max(minimum, Math.min(maximum, position));
13405		}
13406
13407		this.speed(this.duration(current, position, speed));
13408		this.current(position);
13409
13410		if (this.$element.is(':visible')) {
13411			this.update();
13412		}
13413	};
13414
13415	/**
13416	 * Slides to the next item.
13417	 * @public
13418	 * @param {Number}
vendor: 12,284 bytes, lines 13418-13838
13418 [speed] - The time in milliseconds for the transition.
13419	 */
13420	Owl.prototype.next = function(speed) {
13421		speed = speed || false;
13422		this.to(this.relative(this.current()) + 1, speed);
13423	};
13424
13425	/**
13426	 * Slides to the previous item.
13427	 * @public
13428	 * @param {Number} [speed] - The time in milliseconds for the transition.
13429	 */
13430	Owl.prototype.prev = function(speed) {
13431		speed = speed || false;
13432		this.to(this.relative(this.current()) - 1, speed);
13433	};
13434
13435	/**
13436	 * Handles the end of an animation.
13437	 * @protected
13438	 * @param {Event} event - The event arguments.
13439	 */
13440	Owl.prototype.onTransitionEnd = function(event) {
13441
13442		// if css2 animation then event object is undefined
13443		if (event !== undefined) {
13444			event.stopPropagation();
13445
13446			// Catch only owl-stage transitionEnd event
13447			if ((event.target || event.srcElement || event.originalTarget) !== this.$stage.get(0)) {
13448				return false;
13449			}
13450		}
13451
13452		this.leave('animating');
13453		this.trigger('translated');
13454	};
13455
13456	/**
13457	 * Gets viewport width.
13458	 * @protected
13459	 * @return {Number} - The width in pixel.
13460	 */
13461	Owl.prototype.viewport = function() {
13462		var width;
13463		if (this.options.responsiveBaseElement !== window) {
13464			width = $(this.options.responsiveBaseElement).width();
13465		} else if (window.innerWidth) {
13466			width = window.innerWidth;
13467		} else if (document.documentElement && document.documentElement.clientWidth) {
13468			width = document.documentElement.clientWidth;
13469		} else {
13470			throw 'Can not detect viewport width.';
13471		}
13472
13473		return width;
13474	};
13475
13476	/**
13477	 * Replaces the current content.
13478	 * @public
13479	 * @param {HTMLElement|jQuery|String} content - The new content.
13480	 */
13481	Owl.prototype.replace = function(content) {
13482		this.$stage.empty();
13483		this._items = [];
13484
13485		if (content) {
13486			content = (content instanceof jQuery) ? content : $(content);
13487		}
13488
13489		if (this.settings.nestedItemSelector) {
13490			content = content.find('.' + this.settings.nestedItemSelector);
13491		}
13492
13493		content.filter(function() {
13494			return this.nodeType === 1;
13495		}).each($.proxy(function(index, item) {
13496			item = this.prepare(item, index);
13497			this.$stage.append(item);
13498			this._items.push(item);
13499			this._mergers.push(item.find('[data-merge]').addBack('[data-merge]').attr('data-merge') * 1 || 1);
13500		}, this));
13501
13502		this.reset($.isNumeric(this.settings.startPosition) ? this.settings.startPosition : 0);
13503
13504		this.invalidate('items');
13505	};
13506
13507	/**
13508	 * Adds an item.
13509	 * @todo Use `item` instead of `content` for the event arguments.
13510	 * @public
13511	 * @param {HTMLElement|jQuery|String} content - The item content to add.
13512	 * @param {Number} [position] - The relative position at which to insert the item otherwise the item will be added to the end.
13513	 */
13514	Owl.prototype.add = function(content, position) {
13515		var current = this.relative(this._current);
13516
13517		position = position === undefined ? this._items.length : this.normalize(position, true);
13518		content = content instanceof jQuery ? content : $(content);
13519
13520		this.trigger('add', { content: content, position: position });
13521
13522		content = this.prepare(content, this._items[current].index());
13523
13524		if (this._items.length === 0 || position === this._items.length) {
13525			this._items.length === 0 && this.$stage.append(content);
13526			this._items.length !== 0 && this._items[position - 1].after(content);
13527			this._items.push(content);
13528			this._mergers.push(content.find('[data-merge]').andSelf('[data-merge]').attr('data-merge') * 1 || 1);
13529		} else {
13530			this._items[position].before(content);
13531			this._items.splice(position, 0, content);
13532			this._mergers.splice(position, 0, content.find('[data-merge]').andSelf('[data-merge]').attr('data-merge') * 1 || 1);
13533		}
13534
13535		this._items[current] && this.reset(this._items[current].index());
13536
13537		this.invalidate('items');
13538
13539		this.trigger('added', { content: content, position: position });
13540	};
13541
13542	/**
13543	 * Removes an item by its position.
13544	 * @todo Use `item` instead of `content` for the event arguments.
13545	 * @public
13546	 * @param {Number} position - The relative position of the item to remove.
13547	 */
13548	Owl.prototype.remove = function(position) {
13549		position = this.normalize(position, true);
13550
13551		if (position === undefined) {
13552			return;
13553		}
13554
13555		this.trigger('remove', { content: this._items[position], position: position });
13556
13557		this._items[position].remove();
13558		this._items.splice(position, 1);
13559		this._mergers.splice(position, 1);
13560
13561		this.invalidate('items');
13562
13563		this.trigger('removed', { content: null, position: position });
13564	};
13565
13566	/**
13567	 * Preloads images with auto width.
13568	 * @todo Replace by a more generic approach
13569	 * @protected
13570	 */
13571	Owl.prototype.preloadAutoWidthImages = function(images) {
13572		images.each($.proxy(function(i, element) {
13573			this.enter('pre-loading');
13574			element = $(element);
13575			$(new Image()).one('load', $.proxy(function(e) {
13576				element.attr('src', e.target.src);
13577				element.css('opacity', 1);
13578				this.leave('pre-loading');
13579				!this.is('pre-loading') && !this.is('initializing') && this.refresh();
13580			}, this)).attr('src', element.attr('src') || element.attr('data-src') || element.attr('data-src-retina'));
13581		}, this));
13582	};
13583
13584	/**
13585	 * Destroys the carousel.
13586	 * @public
13587	 */
13588	Owl.prototype.destroy = function() {
13589
13590		this.$element.off('.owl.core');
13591		this.$stage.off('.owl.core');
13592		$(document).off('.owl.core');
13593
13594		if (this.settings.responsive !== false) {
13595			window.clearTimeout(this.resizeTimer);
13596			this.off(window, 'resize', this._handlers.onThrottledResize);
13597		}
13598
13599		for (var i in this._plugins) {
13600			this._plugins[i].destroy();
13601		}
13602
13603		this.$stage.children('.cloned').remove();
13604
13605		this.$stage.unwrap();
13606		this.$stage.children().contents().unwrap();
13607		this.$stage.children().unwrap();
13608
13609		this.$element
13610			.removeClass(this.options.refreshClass)
13611			.removeClass(this.options.loadingClass)
13612			.removeClass(this.options.loadedClass)
13613			.removeClass(this.options.rtlClass)
13614			.removeClass(this.options.dragClass)
13615			.removeClass(this.options.grabClass)
13616			.attr('class', this.$element.attr('class').replace(new RegExp(this.options.responsiveClass + '-\\S+\\s', 'g'), ''))
13617			.removeData('owl.carousel');
13618	};
13619
13620	/**
13621	 * Operators to calculate right-to-left and left-to-right.
13622	 * @protected
13623	 * @param {Number} [a] - The left side operand.
13624	 * @param {String} [o] - The operator.
13625	 * @param {Number} [b] - The right side operand.
13626	 */
13627	Owl.prototype.op = function(a, o, b) {
13628		var rtl = this.settings.rtl;
13629		switch (o) {
13630			case '<':
13631				return rtl ? a > b : a < b;
13632			case '>':
13633				return rtl ? a < b : a > b;
13634			case '>=':
13635				return rtl ? a <= b : a >= b;
13636			case '<=':
13637				return rtl ? a >= b : a <= b;
13638			default:
13639				break;
13640		}
13641	};
13642
13643	/**
13644	 * Attaches to an internal event.
13645	 * @protected
13646	 * @param {HTMLElement} element - The event source.
13647	 * @param {String} event - The event name.
13648	 * @param {Function} listener - The event handler to attach.
13649	 * @param {Boolean} capture - Wether the event should be handled at the capturing phase or not.
13650	 */
13651	Owl.prototype.on = function(element, event, listener, capture) {
13652		if (element.addEventListener) {
13653			element.addEventListener(event, listener, capture);
13654		} else if (element.attachEvent) {
13655			element.attachEvent('on' + event, listener);
13656		}
13657	};
13658
13659	/**
13660	 * Detaches from an internal event.
13661	 * @protected
13662	 * @param {HTMLElement} element - The event source.
13663	 * @param {String} event - The event name.
13664	 * @param {Function} listener - The attached event handler to detach.
13665	 * @param {Boolean} capture - Wether the attached event handler was registered as a capturing listener or not.
13666	 */
13667	Owl.prototype.off = function(element, event, listener, capture) {
13668		if (element.removeEventListener) {
13669			element.removeEventListener(event, listener, capture);
13670		} else if (element.detachEvent) {
13671			element.detachEvent('on' + event, listener);
13672		}
13673	};
13674
13675	/**
13676	 * Triggers a public event.
13677	 * @todo Remove `status`, `relatedTarget` should be used instead.
13678	 * @protected
13679	 * @param {String} name - The event name.
13680	 * @param {*} [data=null] - The event data.
13681	 * @param {String} [namespace=carousel] - The event namespace.
13682	 * @param {String} [state] - The state which is associated with the event.
13683	 * @param {Boolean} [enter=false] - Indicates if the call enters the specified state or not.
13684	 * @returns {Event} - The event arguments.
13685	 */
13686	Owl.prototype.trigger = function(name, data, namespace, state, enter) {
13687		var status = {
13688			item: { count: this._items.length, index: this.current() }
13689		}, handler = $.camelCase(
13690			$.grep([ 'on', name, namespace ], function(v) { return v })
13691				.join('-').toLowerCase()
13692		), event = $.Event(
13693			[ name, 'owl', namespace || 'carousel' ].join('.').toLowerCase(),
13694			$.extend({ relatedTarget: this }, status, data)
13695		);
13696
13697		if (!this._supress[name]) {
13698			$.each(this._plugins, function(name, plugin) {
13699				if (plugin.onTrigger) {
13700					plugin.onTrigger(event);
13701				}
13702			});
13703
13704			this.register({ type: Owl.Type.Event, name: name });
13705			this.$element.trigger(event);
13706
13707			if (this.settings && typeof this.settings[handler] === 'function') {
13708				this.settings[handler].call(this, event);
13709			}
13710		}
13711
13712		return event;
13713	};
13714
13715	/**
13716	 * Enters a state.
13717	 * @param name - The state name.
13718	 */
13719	Owl.prototype.enter = function(name) {
13720		$.each([ name ].concat(this._states.tags[name] || []), $.proxy(function(i, name) {
13721			if (this._states.current[name] === undefined) {
13722				this._states.current[name] = 0;
13723			}
13724
13725			this._states.current[name]++;
13726		}, this));
13727	};
13728
13729	/**
13730	 * Leaves a state.
13731	 * @param name - The state name.
13732	 */
13733	Owl.prototype.leave = function(name) {
13734		$.each([ name ].concat(this._states.tags[name] || []), $.proxy(function(i, name) {
13735			this._states.current[name]--;
13736		}, this));
13737	};
13738
13739	/**
13740	 * Registers an event or state.
13741	 * @public
13742	 * @param {Object} object - The event or state to register.
13743	 */
13744	Owl.prototype.register = function(object) {
13745		if (object.type === Owl.Type.Event) {
13746			if (!$.event.special[object.name]) {
13747				$.event.special[object.name] = {};
13748			}
13749
13750			if (!$.event.special[object.name].owl) {
13751				var _default = $.event.special[object.name]._default;
13752				$.event.special[object.name]._default = function(e) {
13753					if (_default && _default.apply && (!e.namespace || e.namespace.indexOf('owl') === -1)) {
13754						return _default.apply(this, arguments);
13755					}
13756					return e.namespace && e.namespace.indexOf('owl') > -1;
13757				};
13758				$.event.special[object.name].owl = true;
13759			}
13760		} else if (object.type === Owl.Type.State) {
13761			if (!this._states.tags[object.name]) {
13762				this._states.tags[object.name] = object.tags;
13763			} else {
13764				this._states.tags[object.name] = this._states.tags[object.name].concat(object.tags);
13765			}
13766
13767			this._states.tags[object.name] = $.grep(this._states.tags[object.name], $.proxy(function(tag, i) {
13768				return $.inArray(tag, this._states.tags[object.name]) === i;
13769			}, this));
13770		}
13771	};
13772
13773	/**
13774	 * Suppresses events.
13775	 * @protected
13776	 * @param {Array.<String>} events - The events to suppress.
13777	 */
13778	Owl.prototype.suppress = function(events) {
13779		$.each(events, $.proxy(function(index, event) {
13780			this._supress[event] = true;
13781		}, this));
13782	};
13783
13784	/**
13785	 * Releases suppressed events.
13786	 * @protected
13787	 * @param {Array.<String>} events - The events to release.
13788	 */
13789	Owl.prototype.release = function(events) {
13790		$.each(events, $.proxy(function(index, event) {
13791			delete this._supress[event];
13792		}, this));
13793	};
13794
13795	/**
13796	 * Gets unified pointer coordinates from event.
13797	 * @todo #261
13798	 * @protected
13799	 * @param {Event} - The `mousedown` or `touchstart` event.
13800	 * @returns {Object} - Contains `x` and `y` coordinates of current pointer position.
13801	 */
13802	Owl.prototype.pointer = function(event) {
13803		var result = { x: null, y: null };
13804
13805		event = event.originalEvent || event || window.event;
13806
13807		event = event.touches && event.touches.length ?
13808			event.touches[0] : event.changedTouches && event.changedTouches.length ?
13809				event.changedTouches[0] : event;
13810
13811		if (event.pageX) {
13812			result.x = event.pageX;
13813			result.y = event.pageY;
13814		} else {
13815			result.x = event.clientX;
13816			result.y = event.clientY;
13817		}
13818
13819		return result;
13820	};
13821
13822	/**
13823	 * Gets the difference of two vectors.
13824	 * @todo #261
13825	 * @protected
13826	 * @param {Object} - The first vector.
13827	 * @param {Object} - The second vector.
13828	 * @returns {Object} - The difference.
13829	 */
13830	Owl.prototype.difference = function(first, second) {
13831		return {
13832			x: first.x - second.x,
13833			y: first.y - second.y
13834		};
13835	};
13836
13837	/**
13838	 * The jQuery Plugin for the Owl Carousel
vendor: 32,216 bytes, lines 13839-15121
13839	 * @todo Navigation plugin `next` and `prev`
13840	 * @public
13841	 */
13842	$.fn.owlCarousel = function(option) {
13843		var args = Array.prototype.slice.call(arguments, 1);
13844
13845		return this.each(function() {
13846			var $this = $(this),
13847				data = $this.data('owl.carousel');
13848
13849			if (!data) {
13850				data = new Owl(this, typeof option == 'object' && option);
13851				$this.data('owl.carousel', data);
13852
13853				$.each([
13854					'next', 'prev', 'to', 'destroy', 'refresh', 'replace', 'add', 'remove'
13855				], function(i, event) {
13856					data.register({ type: Owl.Type.Event, name: event });
13857					data.$element.on(event + '.owl.carousel.core', $.proxy(function(e) {
13858						if (e.namespace && e.relatedTarget !== this) {
13859							this.suppress([ event ]);
13860							data[event].apply(this, [].slice.call(arguments, 1));
13861							this.release([ event ]);
13862						}
13863					}, data));
13864				});
13865			}
13866
13867			if (typeof option == 'string' && option.charAt(0) !== '_') {
13868				data[option].apply(data, args);
13869			}
13870		});
13871	};
13872
13873	/**
13874	 * The constructor for the jQuery Plugin
13875	 * @public
13876	 */
13877	$.fn.owlCarousel.Constructor = Owl;
13878
13879})(window.Zepto || window.jQuery, window, document);
13880
13881/**
13882 * AutoRefresh Plugin
13883 * @version 2.0.0-beta.3
13884 * @author Artus Kolanowski
13885 * @license The MIT License (MIT)
13886 */
13887;(function($, window, document, undefined) {
13888
13889	/**
13890	 * Creates the auto refresh plugin.
13891	 * @class The Auto Refresh Plugin
13892	 * @param {Owl} carousel - The Owl Carousel
13893	 */
13894	var AutoRefresh = function(carousel) {
13895		/**
13896		 * Reference to the core.
13897		 * @protected
13898		 * @type {Owl}
13899		 */
13900		this._core = carousel;
13901
13902		/**
13903		 * Refresh interval.
13904		 * @protected
13905		 * @type {number}
13906		 */
13907		this._interval = null;
13908
13909		/**
13910		 * Whether the element is currently visible or not.
13911		 * @protected
13912		 * @type {Boolean}
13913		 */
13914		this._visible = null;
13915
13916		/**
13917		 * All event handlers.
13918		 * @protected
13919		 * @type {Object}
13920		 */
13921		this._handlers = {
13922			'initialized.owl.carousel': $.proxy(function(e) {
13923				if (e.namespace && this._core.settings.autoRefresh) {
13924					this.watch();
13925				}
13926			}, this)
13927		};
13928
13929		// set default options
13930		this._core.options = $.extend({}, AutoRefresh.Defaults, this._core.options);
13931
13932		// register event handlers
13933		this._core.$element.on(this._handlers);
13934	};
13935
13936	/**
13937	 * Default options.
13938	 * @public
13939	 */
13940	AutoRefresh.Defaults = {
13941		autoRefresh: true,
13942		autoRefreshInterval: 500
13943	};
13944
13945	/**
13946	 * Watches the element.
13947	 */
13948	AutoRefresh.prototype.watch = function() {
13949		if (this._interval) {
13950			return;
13951		}
13952
13953		this._visible = this._core.$element.is(':visible');
13954		this._interval = window.setInterval($.proxy(this.refresh, this), this._core.settings.autoRefreshInterval);
13955	};
13956
13957	/**
13958	 * Refreshes the element.
13959	 */
13960	AutoRefresh.prototype.refresh = function() {
13961		if (this._core.$element.is(':visible') === this._visible) {
13962			return;
13963		}
13964
13965		this._visible = !this._visible;
13966
13967		this._core.$element.toggleClass('owl-hidden', !this._visible);
13968
13969		this._visible && (this._core.invalidate('width') && this._core.refresh());
13970	};
13971
13972	/**
13973	 * Destroys the plugin.
13974	 */
13975	AutoRefresh.prototype.destroy = function() {
13976		var handler, property;
13977
13978		window.clearInterval(this._interval);
13979
13980		for (handler in this._handlers) {
13981			this._core.$element.off(handler, this._handlers[handler]);
13982		}
13983		for (property in Object.getOwnPropertyNames(this)) {
13984			typeof this[property] != 'function' && (this[property] = null);
13985		}
13986	};
13987
13988	$.fn.owlCarousel.Constructor.Plugins.AutoRefresh = AutoRefresh;
13989
13990})(window.Zepto || window.jQuery, window, document);
13991
13992/**
13993 * Lazy Plugin
13994 * @version 2.0.0-beta.3
13995 * @author Bartosz Wojciechowski
13996 * @license The MIT License (MIT)
13997 */
13998;(function($, window, document, undefined) {
13999
14000	/**
14001	 * Creates the lazy plugin.
14002	 * @class The Lazy Plugin
14003	 * @param {Owl} carousel - The Owl Carousel
14004	 */
14005	var Lazy = function(carousel) {
14006
14007		/**
14008		 * Reference to the core.
14009		 * @protected
14010		 * @type {Owl}
14011		 */
14012		this._core = carousel;
14013
14014		/**
14015		 * Already loaded items.
14016		 * @protected
14017		 * @type {Array.<jQuery>}
14018		 */
14019		this._loaded = [];
14020
14021		/**
14022		 * Event handlers.
14023		 * @protected
14024		 * @type {Object}
14025		 */
14026		this._handlers = {
14027			'initialized.owl.carousel change.owl.carousel': $.proxy(function(e) {
14028				if (!e.namespace) {
14029					return;
14030				}
14031
14032				if (!this._core.settings || !this._core.settings.lazyLoad) {
14033					return;
14034				}
14035
14036				if ((e.property && e.property.name == 'position') || e.type == 'initialized') {
14037					var settings = this._core.settings,
14038						n = (settings.center && Math.ceil(settings.items / 2) || settings.items),
14039						i = ((settings.center && n * -1) || 0),
14040						position = ((e.property && e.property.value) || this._core.current()) + i,
14041						clones = this._core.clones().length,
14042						load = $.proxy(function(i, v) { this.load(v) }, this);
14043
14044					while (i++ < n) {
14045						this.load(clones / 2 + this._core.relative(position));
14046						clones && $.each(this._core.clones(this._core.relative(position)), load);
14047						position++;
14048					}
14049				}
14050			}, this)
14051		};
14052
14053		// set the default options
14054		this._core.options = $.extend({}, Lazy.Defaults, this._core.options);
14055
14056		// register event handler
14057		this._core.$element.on(this._handlers);
14058	}
14059
14060	/**
14061	 * Default options.
14062	 * @public
14063	 */
14064	Lazy.Defaults = {
14065		lazyLoad: false
14066	}
14067
14068	/**
14069	 * Loads all resources of an item at the specified position.
14070	 * @param {Number} position - The absolute position of the item.
14071	 * @protected
14072	 */
14073	Lazy.prototype.load = function(position) {
14074		var $item = this._core.$stage.children().eq(position),
14075			$elements = $item && $item.find('.owl-lazy');
14076
14077		if (!$elements || $.inArray($item.get(0), this._loaded) > -1) {
14078			return;
14079		}
14080
14081		$elements.each($.proxy(function(index, element) {
14082			var $element = $(element), image,
14083				url = (window.devicePixelRatio > 1 && $element.attr('data-src-retina')) || $element.attr('data-src');
14084
14085			this._core.trigger('load', { element: $element, url: url }, 'lazy');
14086
14087			if ($element.is('img')) {
14088				$element.one('load.owl.lazy', $.proxy(function() {
14089					$element.css('opacity', 1);
14090					this._core.trigger('loaded', { element: $element, url: url }, 'lazy');
14091				}, this)).attr('src', url);
14092			} else {
14093				image = new Image();
14094				image.onload = $.proxy(function() {
14095					$element.css({
14096						'background-image': 'url(' + url + ')',
14097						'opacity': '1'
14098					});
14099					this._core.trigger('loaded', { element: $element, url: url }, 'lazy');
14100				}, this);
14101				image.src = url;
14102			}
14103		}, this));
14104
14105		this._loaded.push($item.get(0));
14106	}
14107
14108	/**
14109	 * Destroys the plugin.
14110	 * @public
14111	 */
14112	Lazy.prototype.destroy = function() {
14113		var handler, property;
14114
14115		for (handler in this.handlers) {
14116			this._core.$element.off(handler, this.handlers[handler]);
14117		}
14118		for (property in Object.getOwnPropertyNames(this)) {
14119			typeof this[property] != 'function' && (this[property] = null);
14120		}
14121	};
14122
14123	$.fn.owlCarousel.Constructor.Plugins.Lazy = Lazy;
14124
14125})(window.Zepto || window.jQuery, window, document);
14126
14127/**
14128 * AutoHeight Plugin
14129 * @version 2.0.0-beta.3
14130 * @author Bartosz Wojciechowski
14131 * @license The MIT License (MIT)
14132 */
14133;(function($, window, document, undefined) {
14134
14135	/**
14136	 * Creates the auto height plugin.
14137	 * @class The Auto Height Plugin
14138	 * @param {Owl} carousel - The Owl Carousel
14139	 */
14140	var AutoHeight = function(carousel) {
14141		/**
14142		 * Reference to the core.
14143		 * @protected
14144		 * @type {Owl}
14145		 */
14146		this._core = carousel;
14147
14148		/**
14149		 * All event handlers.
14150		 * @protected
14151		 * @type {Object}
14152		 */
14153		this._handlers = {
14154			'initialized.owl.carousel refreshed.owl.carousel': $.proxy(function(e) {
14155				if (e.namespace && this._core.settings.autoHeight) {
14156					this.update();
14157				}
14158			}, this),
14159			'changed.owl.carousel': $.proxy(function(e) {
14160				if (e.namespace && this._core.settings.autoHeight && e.property.name == 'position'){
14161					this.update();
14162				}
14163			}, this),
14164			'loaded.owl.lazy': $.proxy(function(e) {
14165				if (e.namespace && this._core.settings.autoHeight
14166					&& e.element.closest('.' + this._core.settings.itemClass).index() === this._core.current()) {
14167					this.update();
14168				}
14169			}, this)
14170		};
14171
14172		// set default options
14173		this._core.options = $.extend({}, AutoHeight.Defaults, this._core.options);
14174
14175		// register event handlers
14176		this._core.$element.on(this._handlers);
14177	};
14178
14179	/**
14180	 * Default options.
14181	 * @public
14182	 */
14183	AutoHeight.Defaults = {
14184		autoHeight: false,
14185		autoHeightClass: 'owl-height'
14186	};
14187
14188	/**
14189	 * Updates the view.
14190	 */
14191	AutoHeight.prototype.update = function() {
14192		var start = this._core._current,
14193			end = start + this._core.settings.items,
14194			visible = this._core.$stage.children().toArray().slice(start, end);
14195			heights = [],
14196			maxheight = 0;
14197
14198		$.each(visible, function(index, item) {
14199			heights.push($(item).height());
14200		});
14201
14202		maxheight = Math.max.apply(null, heights);
14203
14204		this._core.$stage.parent()
14205			.height(maxheight)
14206			.addClass(this._core.settings.autoHeightClass);
14207	};
14208
14209	AutoHeight.prototype.destroy = function() {
14210		var handler, property;
14211
14212		for (handler in this._handlers) {
14213			this._core.$element.off(handler, this._handlers[handler]);
14214		}
14215		for (property in Object.getOwnPropertyNames(this)) {
14216			typeof this[property] != 'function' && (this[property] = null);
14217		}
14218	};
14219
14220	$.fn.owlCarousel.Constructor.Plugins.AutoHeight = AutoHeight;
14221
14222})(window.Zepto || window.jQuery, window, document);
14223
14224/**
14225 * Video Plugin
14226 * @version 2.0.0-beta.3
14227 * @author Bartosz Wojciechowski
14228 * @license The MIT License (MIT)
14229 */
14230;(function($, window, document, undefined) {
14231
14232	/**
14233	 * Creates the video plugin.
14234	 * @class The Video Plugin
14235	 * @param {Owl} carousel - The Owl Carousel
14236	 */
14237	var Video = function(carousel) {
14238		/**
14239		 * Reference to the core.
14240		 * @protected
14241		 * @type {Owl}
14242		 */
14243		this._core = carousel;
14244
14245		/**
14246		 * Cache all video URLs.
14247		 * @protected
14248		 * @type {Object}
14249		 */
14250		this._videos = {};
14251
14252		/**
14253		 * Current playing item.
14254		 * @protected
14255		 * @type {jQuery}
14256		 */
14257		this._playing = null;
14258
14259		/**
14260		 * All event handlers.
14261		 * @todo The cloned content removale is too late
14262		 * @protected
14263		 * @type {Object}
14264		 */
14265		this._handlers = {
14266			'initialized.owl.carousel': $.proxy(function(e) {
14267				if (e.namespace) {
14268					this._core.register({ type: 'state', name: 'playing', tags: [ 'interacting' ] });
14269				}
14270			}, this),
14271			'resize.owl.carousel': $.proxy(function(e) {
14272				if (e.namespace && this._core.settings.video && this.isInFullScreen()) {
14273					e.preventDefault();
14274				}
14275			}, this),
14276			'refreshed.owl.carousel': $.proxy(function(e) {
14277				if (e.namespace && this._core.is('resizing')) {
14278					this._core.$stage.find('.cloned .owl-video-frame').remove();
14279				}
14280			}, this),
14281			'changed.owl.carousel': $.proxy(function(e) {
14282				if (e.namespace && e.property.name === 'position' && this._playing) {
14283					this.stop();
14284				}
14285			}, this),
14286			'prepared.owl.carousel': $.proxy(function(e) {
14287				if (!e.namespace) {
14288					return;
14289				}
14290
14291				var $element = $(e.content).find('.owl-video');
14292
14293				if ($element.length) {
14294					$element.css('display', 'none');
14295					this.fetch($element, $(e.content));
14296				}
14297			}, this)
14298		};
14299
14300		// set default options
14301		this._core.options = $.extend({}, Video.Defaults, this._core.options);
14302
14303		// register event handlers
14304		this._core.$element.on(this._handlers);
14305
14306		this._core.$element.on('click.owl.video', '.owl-video-play-icon', $.proxy(function(e) {
14307			this.play(e);
14308		}, this));
14309	};
14310
14311	/**
14312	 * Default options.
14313	 * @public
14314	 */
14315	Video.Defaults = {
14316		video: false,
14317		videoHeight: false,
14318		videoWidth: false
14319	};
14320
14321	/**
14322	 * Gets the video ID and the type (YouTube/Vimeo only).
14323	 * @protected
14324	 * @param {jQuery} target - The target containing the video data.
14325	 * @param {jQuery} item - The item containing the video.
14326	 */
14327	Video.prototype.fetch = function(target, item) {
14328		var type = target.attr('data-vimeo-id') ? 'vimeo' : 'youtube',
14329			id = target.attr('data-vimeo-id') || target.attr('data-youtube-id'),
14330			width = target.attr('data-width') || this._core.settings.videoWidth,
14331			height = target.attr('data-height') || this._core.settings.videoHeight,
14332			url = target.attr('href');
14333
14334		if (url) {
14335			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+)?/);
14336
14337			if (id[3].indexOf('youtu') > -1) {
14338				type = 'youtube';
14339			} else if (id[3].indexOf('vimeo') > -1) {
14340				type = 'vimeo';
14341			} else {
14342				throw new Error('Video URL not supported.');
14343			}
14344			id = id[6];
14345		} else {
14346			throw new Error('Missing video URL.');
14347		}
14348
14349		this._videos[url] = {
14350			type: type,
14351			id: id,
14352			width: width,
14353			height: height
14354		};
14355
14356		item.attr('data-video', url);
14357
14358		this.thumbnail(target, this._videos[url]);
14359	};
14360
14361	/**
14362	 * Creates video thumbnail.
14363	 * @protected
14364	 * @param {jQuery} target - The target containing the video data.
14365	 * @param {Object} info - The video info object.
14366	 * @see `fetch`
14367	 */
14368	Video.prototype.thumbnail = function(target, video) {
14369		var tnLink,
14370			icon,
14371			path,
14372			dimensions = video.width && video.height ? 'style="width:' + video.width + 'px;height:' + video.height + 'px;"' : '',
14373			customTn = target.find('img'),
14374			srcType = 'src',
14375			lazyClass = '',
14376			settings = this._core.settings,
14377			create = function(path) {
14378				icon = '<div class="owl-video-play-icon"></div>';
14379
14380				if (settings.lazyLoad) {
14381					tnLink = '<div class="owl-video-tn ' + lazyClass + '" ' + srcType + '="' + path + '"></div>';
14382				} else {
14383					tnLink = '<div class="owl-video-tn" style="opacity:1;background-image:url(' + path + ')"></div>';
14384				}
14385				target.after(tnLink);
14386				target.after(icon);
14387			};
14388
14389		// wrap video content into owl-video-wrapper div
14390		target.wrap('<div class="owl-video-wrapper"' + dimensions + '></div>');
14391
14392		if (this._core.settings.lazyLoad) {
14393			srcType = 'data-src';
14394			lazyClass = 'owl-lazy';
14395		}
14396
14397		// custom thumbnail
14398		if (customTn.length) {
14399			create(customTn.attr(srcType));
14400			customTn.remove();
14401			return false;
14402		}
14403
14404		if (video.type === 'youtube') {
14405			path = "//img.youtube.com/vi/" + video.id + "/hqdefault.jpg";
14406			create(path);
14407		} else if (video.type === 'vimeo') {
14408			$.ajax({
14409				type: 'GET',
14410				url: '//vimeo.com/api/v2/video/' + video.id + '.json',
14411				jsonp: 'callback',
14412				dataType: 'jsonp',
14413				success: function(data) {
14414					path = data[0].thumbnail_large;
14415					create(path);
14416				}
14417			});
14418		}
14419	};
14420
14421	/**
14422	 * Stops the current video.
14423	 * @public
14424	 */
14425	Video.prototype.stop = function() {
14426		this._core.trigger('stop', null, 'video');
14427		this._playing.find('.owl-video-frame').remove();
14428		this._playing.removeClass('owl-video-playing');
14429		this._playing = null;
14430		this._core.leave('playing');
14431		this._core.trigger('stopped', null, 'video');
14432	};
14433
14434	/**
14435	 * Starts the current video.
14436	 * @public
14437	 * @param {Event} event - The event arguments.
14438	 */
14439	Video.prototype.play = function(event) {
14440		var target = $(event.target),
14441			item = target.closest('.' + this._core.settings.itemClass),
14442			video = this._videos[item.attr('data-video')],
14443			width = video.width || '100%',
14444			height = video.height || this._core.$stage.height(),
14445			html;
14446
14447		if (this._playing) {
14448			return;
14449		}
14450
14451		this._core.enter('playing');
14452		this._core.trigger('play', null, 'video');
14453
14454		item = this._core.items(this._core.relative(item.index()));
14455
14456		this._core.reset(item.index());
14457
14458		if (video.type === 'youtube') {
14459			html = '<iframe width="' + width + '" height="' + height + '" src="//www.youtube.com/embed/' +
14460				video.id + '?autoplay=1&v=' + video.id + '" frameborder="0" allowfullscreen></iframe>';
14461		} else if (video.type === 'vimeo') {
14462			html = '<iframe src="//player.vimeo.com/video/' + video.id +
14463				'?autoplay=1" width="' + width + '" height="' + height +
14464				'" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>';
14465		}
14466
14467		$('<div class="owl-video-frame">' + html + '</div>').insertAfter(item.find('.owl-video'));
14468
14469		this._playing = item.addClass('owl-video-playing');
14470	};
14471
14472	/**
14473	 * Checks whether an video is currently in full screen mode or not.
14474	 * @todo Bad style because looks like a readonly method but changes members.
14475	 * @protected
14476	 * @returns {Boolean}
14477	 */
14478	Video.prototype.isInFullScreen = function() {
14479		var element = document.fullscreenElement || document.mozFullScreenElement ||
14480				document.webkitFullscreenElement;
14481
14482		return element && $(element).parent().hasClass('owl-video-frame');
14483	};
14484
14485	/**
14486	 * Destroys the plugin.
14487	 */
14488	Video.prototype.destroy = function() {
14489		var handler, property;
14490
14491		this._core.$element.off('click.owl.video');
14492
14493		for (handler in this._handlers) {
14494			this._core.$element.off(handler, this._handlers[handler]);
14495		}
14496		for (property in Object.getOwnPropertyNames(this)) {
14497			typeof this[property] != 'function' && (this[property] = null);
14498		}
14499	};
14500
14501	$.fn.owlCarousel.Constructor.Plugins.Video = Video;
14502
14503})(window.Zepto || window.jQuery, window, document);
14504
14505/**
14506 * Animate Plugin
14507 * @version 2.0.0-beta.3
14508 * @author Bartosz Wojciechowski
14509 * @license The MIT License (MIT)
14510 */
14511;(function($, window, document, undefined) {
14512
14513	/**
14514	 * Creates the animate plugin.
14515	 * @class The Navigation Plugin
14516	 * @param {Owl} scope - The Owl Carousel
14517	 */
14518	var Animate = function(scope) {
14519		this.core = scope;
14520		this.core.options = $.extend({}, Animate.Defaults, this.core.options);
14521		this.swapping = true;
14522		this.previous = undefined;
14523		this.next = undefined;
14524
14525		this.handlers = {
14526			'change.owl.carousel': $.proxy(function(e) {
14527				if (e.namespace && e.property.name == 'position') {
14528					this.previous = this.core.current();
14529					this.next = e.property.value;
14530				}
14531			}, this),
14532			'drag.owl.carousel dragged.owl.carousel translated.owl.carousel': $.proxy(function(e) {
14533				if (e.namespace) {
14534					this.swapping = e.type == 'translated';
14535				}
14536			}, this),
14537			'translate.owl.carousel': $.proxy(function(e) {
14538				if (e.namespace && this.swapping && (this.core.options.animateOut || this.core.options.animateIn)) {
14539					this.swap();
14540				}
14541			}, this)
14542		};
14543
14544		this.core.$element.on(this.handlers);
14545	};
14546
14547	/**
14548	 * Default options.
14549	 * @public
14550	 */
14551	Animate.Defaults = {
14552		animateOut: false,
14553		animateIn: false
14554	};
14555
14556	/**
14557	 * Toggles the animation classes whenever an translations starts.
14558	 * @protected
14559	 * @returns {Boolean|undefined}
14560	 */
14561	Animate.prototype.swap = function() {
14562
14563		if (this.core.settings.items !== 1) {
14564			return;
14565		}
14566
14567		if (!$.support.animation || !$.support.transition) {
14568			return;
14569		}
14570
14571		this.core.speed(0);
14572
14573		var left,
14574			clear = $.proxy(this.clear, this),
14575			previous = this.core.$stage.children().eq(this.previous),
14576			next = this.core.$stage.children().eq(this.next),
14577			incoming = this.core.settings.animateIn,
14578			outgoing = this.core.settings.animateOut;
14579
14580		if (this.core.current() === this.previous) {
14581			return;
14582		}
14583
14584		if (outgoing) {
14585			left = this.core.coordinates(this.previous) - this.core.coordinates(this.next);
14586			previous.css( { 'left': left + 'px' } )
14587				.addClass('animated owl-animated-out')
14588				.addClass(outgoing)
14589				.on($.support.animation.end, clear);
14590		}
14591
14592		if (incoming) {
14593			next.addClass('animated owl-animated-in')
14594				.addClass(incoming)
14595				.on($.support.animation.end, clear);
14596		}
14597	};
14598
14599	Animate.prototype.clear = function(e) {
14600		if ($(e.target).hasClass('animated')) {
14601			$(e.target).css( { 'left': '' } )
14602				.removeClass('animated owl-animated-out owl-animated-in')
14603				.removeClass(this.core.settings.animateIn)
14604				.removeClass(this.core.settings.animateOut);
14605			this.core.onTransitionEnd();
14606		}
14607	};
14608
14609	/**
14610	 * Destroys the plugin.
14611	 * @public
14612	 */
14613	Animate.prototype.destroy = function() {
14614		var handler, property;
14615
14616		for (handler in this.handlers) {
14617			this.core.$element.off(handler, this.handlers[handler]);
14618		}
14619		for (property in Object.getOwnPropertyNames(this)) {
14620			typeof this[property] != 'function' && (this[property] = null);
14621		}
14622	};
14623
14624	$.fn.owlCarousel.Constructor.Plugins.Animate = Animate;
14625
14626})(window.Zepto || window.jQuery, window, document);
14627
14628/**
14629 * Autoplay Plugin
14630 * @version 2.0.0-beta.3
14631 * @author Bartosz Wojciechowski
14632 * @author Artus Kolanowski
14633 * @license The MIT License (MIT)
14634 */
14635;(function($, window, document, undefined) {
14636
14637	/**
14638	 * Creates the autoplay plugin.
14639	 * @class The Autoplay Plugin
14640	 * @param {Owl} scope - The Owl Carousel
14641	 */
14642	var Autoplay = function(carousel) {
14643		/**
14644		 * Reference to the core.
14645		 * @protected
14646		 * @type {Owl}
14647		 */
14648		this._core = carousel;
14649
14650		/**
14651		 * The autoplay interval.
14652		 * @type {Number}
14653		 */
14654		this._interval = null;
14655
14656		/**
14657		 * Indicates whenever the autoplay is paused.
14658		 * @type {Boolean}
14659		 */
14660		this._paused = false;
14661
14662		/**
14663		 * All event handlers.
14664		 * @protected
14665		 * @type {Object}
14666		 */
14667		this._handlers = {
14668			'changed.owl.carousel': $.proxy(function(e) {
14669				if (e.namespace && e.property.name === 'settings') {
14670					if (this._core.settings.autoplay) {
14671						this.play();
14672					} else {
14673						this.stop();
14674					}
14675				}
14676			}, this),
14677			'initialized.owl.carousel': $.proxy(function(e) {
14678				if (e.namespace && this._core.settings.autoplay) {
14679					this.play();
14680				}
14681			}, this),
14682			'play.owl.autoplay': $.proxy(function(e, t, s) {
14683				if (e.namespace) {
14684					this.play(t, s);
14685				}
14686			}, this),
14687			'stop.owl.autoplay': $.proxy(function(e) {
14688				if (e.namespace) {
14689					this.stop();
14690				}
14691			}, this),
14692			'mouseover.owl.autoplay': $.proxy(function() {
14693				if (this._core.settings.autoplayHoverPause && this._core.is('rotating')) {
14694					this.pause();
14695				}
14696			}, this),
14697			'mouseleave.owl.autoplay': $.proxy(function() {
14698				if (this._core.settings.autoplayHoverPause && this._core.is('rotating')) {
14699					this.play();
14700				}
14701			}, this)
14702		};
14703
14704		// register event handlers
14705		this._core.$element.on(this._handlers);
14706
14707		// set default options
14708		this._core.options = $.extend({}, Autoplay.Defaults, this._core.options);
14709	};
14710
14711	/**
14712	 * Default options.
14713	 * @public
14714	 */
14715	Autoplay.Defaults = {
14716		autoplay: false,
14717		autoplayTimeout: 5000,
14718		autoplayHoverPause: false,
14719		autoplaySpeed: false
14720	};
14721
14722	/**
14723	 * Starts the autoplay.
14724	 * @public
14725	 * @param {Number} [timeout] - The interval before the next animation starts.
14726	 * @param {Number} [speed] - The animation speed for the animations.
14727	 */
14728	Autoplay.prototype.play = function(timeout, speed) {
14729		this._paused = false;
14730
14731		if (this._core.is('rotating')) {
14732			return;
14733		}
14734
14735		this._core.enter('rotating');
14736
14737		this._interval = window.setInterval($.proxy(function() {
14738			if (this._paused || this._core.is('busy') || this._core.is('interacting') || document.hidden) {
14739				return;
14740			}
14741			this._core.next(speed || this._core.settings.autoplaySpeed);
14742		}, this), timeout || this._core.settings.autoplayTimeout);
14743	};
14744
14745	/**
14746	 * Stops the autoplay.
14747	 * @public
14748	 */
14749	Autoplay.prototype.stop = function() {
14750		if (!this._core.is('rotating')) {
14751			return;
14752		}
14753
14754		window.clearInterval(this._interval);
14755		this._core.leave('rotating');
14756	};
14757
14758	/**
14759	 * Stops the autoplay.
14760	 * @public
14761	 */
14762	Autoplay.prototype.pause = function() {
14763		if (!this._core.is('rotating')) {
14764			return;
14765		}
14766
14767		this._paused = true;
14768	};
14769
14770	/**
14771	 * Destroys the plugin.
14772	 */
14773	Autoplay.prototype.destroy = function() {
14774		var handler, property;
14775
14776		this.stop();
14777
14778		for (handler in this._handlers) {
14779			this._core.$element.off(handler, this._handlers[handler]);
14780		}
14781		for (property in Object.getOwnPropertyNames(this)) {
14782			typeof this[property] != 'function' && (this[property] = null);
14783		}
14784	};
14785
14786	$.fn.owlCarousel.Constructor.Plugins.autoplay = Autoplay;
14787
14788})(window.Zepto || window.jQuery, window, document);
14789
14790/**
14791 * Navigation Plugin
14792 * @version 2.0.0-beta.3
14793 * @author Artus Kolanowski
14794 * @license The MIT License (MIT)
14795 */
14796;(function($, window, document, undefined) {
14797	'use strict';
14798
14799	/**
14800	 * Creates the navigation plugin.
14801	 * @class The Navigation Plugin
14802	 * @param {Owl} carousel - The Owl Carousel.
14803	 */
14804	var Navigation = function(carousel) {
14805		/**
14806		 * Reference to the core.
14807		 * @protected
14808		 * @type {Owl}
14809		 */
14810		this._core = carousel;
14811
14812		/**
14813		 * Indicates whether the plugin is initialized or not.
14814		 * @protected
14815		 * @type {Boolean}
14816		 */
14817		this._initialized = false;
14818
14819		/**
14820		 * The current paging indexes.
14821		 * @protected
14822		 * @type {Array}
14823		 */
14824		this._pages = [];
14825
14826		/**
14827		 * All DOM elements of the user interface.
14828		 * @protected
14829		 * @type {Object}
14830		 */
14831		this._controls = {};
14832
14833		/**
14834		 * Markup for an indicator.
14835		 * @protected
14836		 * @type {Array.<String>}
14837		 */
14838		this._templates = [];
14839
14840		/**
14841		 * The carousel element.
14842		 * @type {jQuery}
14843		 */
14844		this.$element = this._core.$element;
14845
14846		/**
14847		 * Overridden methods of the carousel.
14848		 * @protected
14849		 * @type {Object}
14850		 */
14851		this._overrides = {
14852			next: this._core.next,
14853			prev: this._core.prev,
14854			to: this._core.to
14855		};
14856
14857		/**
14858		 * All event handlers.
14859		 * @protected
14860		 * @type {Object}
14861		 */
14862		this._handlers = {
14863			'prepared.owl.carousel': $.proxy(function(e) {
14864				if (e.namespace && this._core.settings.dotsData) {
14865					this._templates.push('<div class="' + this._core.settings.dotClass + '">' +
14866						$(e.content).find('[data-dot]').andSelf('[data-dot]').attr('data-dot') + '</div>');
14867				}
14868			}, this),
14869			'added.owl.carousel': $.proxy(function(e) {
14870				if (e.namespace && this._core.settings.dotsData) {
14871					this._templates.splice(e.position, 0, this._templates.pop());
14872				}
14873			}, this),
14874			'remove.owl.carousel': $.proxy(function(e) {
14875				if (e.namespace && this._core.settings.dotsData) {
14876					this._templates.splice(e.position, 1);
14877				}
14878			}, this),
14879			'changed.owl.carousel': $.proxy(function(e) {
14880				if (e.namespace && e.property.name == 'position') {
14881					this.draw();
14882				}
14883			}, this),
14884			'initialized.owl.carousel': $.proxy(function(e) {
14885				if (e.namespace && !this._initialized) {
14886					this._core.trigger('initialize', null, 'navigation');
14887					this.initialize();
14888					this.update();
14889					this.draw();
14890					this._initialized = true;
14891					this._core.trigger('initialized', null, 'navigation');
14892				}
14893			}, this),
14894			'refreshed.owl.carousel': $.proxy(function(e) {
14895				if (e.namespace && this._initialized) {
14896					this._core.trigger('refresh', null, 'navigation');
14897					this.update();
14898					this.draw();
14899					this._core.trigger('refreshed', null, 'navigation');
14900				}
14901			}, this)
14902		};
14903
14904		// set default options
14905		this._core.options = $.extend({}, Navigation.Defaults, this._core.options);
14906
14907		// register event handlers
14908		this.$element.on(this._handlers);
14909	};
14910
14911	/**
14912	 * Default options.
14913	 * @public
14914	 * @todo Rename `slideBy` to `navBy`
14915	 */
14916	Navigation.Defaults = {
14917		nav: false,
14918		navText: [ 'prev', 'next' ],
14919		navSpeed: false,
14920		navElement: 'div',
14921		navContainer: false,
14922		navContainerClass: 'owl-nav',
14923		navClass: [ 'owl-prev', 'owl-next' ],
14924		slideBy: 1,
14925		dotClass: 'owl-dot',
14926		dotsClass: 'owl-dots',
14927		dots: true,
14928		dotsEach: false,
14929		dotsData: false,
14930		dotsSpeed: false,
14931		dotsContainer: false
14932	};
14933
14934	/**
14935	 * Initializes the layout of the plugin and extends the carousel.
14936	 * @protected
14937	 */
14938	Navigation.prototype.initialize = function() {
14939		var override,
14940			settings = this._core.settings;
14941
14942		// create DOM structure for relative navigation
14943		this._controls.$relative = (settings.navContainer ? $(settings.navContainer)
14944			: $('<div>').addClass(settings.navContainerClass).appendTo(this.$element)).addClass('disabled');
14945
14946		this._controls.$previous = $('<' + settings.navElement + '>')
14947			.addClass(settings.navClass[0])
14948			.html(settings.navText[0])
14949			.prependTo(this._controls.$relative)
14950			.on('click', $.proxy(function(e) {
14951				this.prev(settings.navSpeed);
14952			}, this));
14953		this._controls.$next = $('<' + settings.navElement + '>')
14954			.addClass(settings.navClass[1])
14955			.html(settings.navText[1])
14956			.appendTo(this._controls.$relative)
14957			.on('click', $.proxy(function(e) {
14958				this.next(settings.navSpeed);
14959			}, this));
14960
14961		// create DOM structure for absolute navigation
14962		if (!settings.dotsData) {
14963			this._templates = [ $('<div>')
14964				.addClass(settings.dotClass)
14965				.append($('<span>'))
14966				.prop('outerHTML') ];
14967		}
14968
14969		this._controls.$absolute = (settings.dotsContainer ? $(settings.dotsContainer)
14970			: $('<div>').addClass(settings.dotsClass).appendTo(this.$element)).addClass('disabled');
14971
14972		this._controls.$absolute.on('click', 'div', $.proxy(function(e) {
14973			var index = $(e.target).parent().is(this._controls.$absolute)
14974				? $(e.target).index() : $(e.target).parent().index();
14975
14976			e.preventDefault();
14977
14978			this.to(index, settings.dotsSpeed);
14979		}, this));
14980
14981		// override public methods of the carousel
14982		for (override in this._overrides) {
14983			this._core[override] = $.proxy(this[override], this);
14984		}
14985	};
14986
14987	/**
14988	 * Destroys the plugin.
14989	 * @protected
14990	 */
14991	Navigation.prototype.destroy = function() {
14992		var handler, control, property, override;
14993
14994		for (handler in this._handlers) {
14995			this.$element.off(handler, this._handlers[handler]);
14996		}
14997		for (control in this._controls) {
14998			this._controls[control].remove();
14999		}
15000		for (override in this.overides) {
15001			this._core[override] = this._overrides[override];
15002		}
15003		for (property in Object.getOwnPropertyNames(this)) {
15004			typeof this[property] != 'function' && (this[property] = null);
15005		}
15006	};
15007
15008	/**
15009	 * Updates the internal state.
15010	 * @protected
15011	 */
15012	Navigation.prototype.update = function() {
15013		var i, j, k,
15014			lower = this._core.clones().length / 2,
15015			upper = lower + this._core.items().length,
15016			maximum = this._core.maximum(true),
15017			settings = this._core.settings,
15018			size = settings.center || settings.autoWidth || settings.dotsData
15019				? 1 : settings.dotsEach || settings.items;
15020
15021		if (settings.slideBy !== 'page') {
15022			settings.slideBy = Math.min(settings.slideBy, settings.items);
15023		}
15024
15025		if (settings.dots || settings.slideBy == 'page') {
15026			this._pages = [];
15027
15028			for (i = lower, j = 0, k = 0; i < upper; i++) {
15029				if (j >= size || j === 0) {
15030					this._pages.push({
15031						start: Math.min(maximum, i - lower),
15032						end: i - lower + size - 1
15033					});
15034					if (Math.min(maximum, i - lower) === maximum) {
15035						break;
15036					}
15037					j = 0, ++k;
15038				}
15039				j += this._core.mergers(this._core.relative(i));
15040			}
15041		}
15042	};
15043
15044	/**
15045	 * Draws the user interface.
15046	 * @todo The option `dotsData` wont work.
15047	 * @protected
15048	 */
15049	Navigation.prototype.draw = function() {
15050		var difference,
15051			settings = this._core.settings,
15052			disabled = this._core.items().length <= settings.items,
15053			index = this._core.relative(this._core.current()),
15054			loop = settings.loop || settings.rewind;
15055
15056		this._controls.$relative.toggleClass('disabled', !settings.nav || disabled);
15057
15058		if (settings.nav) {
15059			this._controls.$previous.toggleClass('disabled', !loop && index <= this._core.minimum(true));
15060			this._controls.$next.toggleClass('disabled', !loop && index >= this._core.maximum(true));
15061		}
15062
15063		this._controls.$absolute.toggleClass('disabled', !settings.dots || disabled);
15064
15065		if (settings.dots) {
15066			difference = this._pages.length - this._controls.$absolute.children().length;
15067
15068			if (settings.dotsData && difference !== 0) {
15069				this._controls.$absolute.html(this._templates.join(''));
15070			} else if (difference > 0) {
15071				this._controls.$absolute.append(new Array(difference + 1).join(this._templates[0]));
15072			} else if (difference < 0) {
15073				this._controls.$absolute.children().slice(difference).remove();
15074			}
15075
15076			this._controls.$absolute.find('.active').removeClass('active');
15077			this._controls.$absolute.children().eq($.inArray(this.current(), this._pages)).addClass('active');
15078		}
15079	};
15080
15081	/**
15082	 * Extends event data.
15083	 * @protected
15084	 * @param {Event} event - The event object which gets thrown.
15085	 */
15086	Navigation.prototype.onTrigger = function(event) {
15087		var settings = this._core.settings;
15088
15089		event.page = {
15090			index: $.inArray(this.current(), this._pages),
15091			count: this._pages.length,
15092			size: settings && (settings.center || settings.autoWidth || settings.dotsData
15093				? 1 : settings.dotsEach || settings.items)
15094		};
15095	};
15096
15097	/**
15098	 * Gets the current page position of the carousel.
15099	 * @protected
15100	 * @returns {Number}
15101	 */
15102	Navigation.prototype.current = function() {
15103		var current = this._core.relative(this._core.current());
15104		return $.grep(this._pages, $.proxy(function(page, index) {
15105			return page.start <= current && page.end >= current;
15106		}, this)).pop();
15107	};
15108
15109	/**
15110	 * Gets the current succesor/predecessor position.
15111	 * @protected
15112	 * @returns {Number}
15113	 */
15114	Navigation.prototype.getPosition = function(successor) {
15115		var position, length,
15116			settings = this._core.settings;
15117
15118		if (settings.slideBy == 'page') {
15119			position = $.inArray(this.current(), this._pages);
15120			length = this._pages.length;
15121			successor ? ++position : --position;
vendor: 5,678 bytes, lines 15122-15346
15122			position = this._pages[((position % length) + length) % length].start;
15123		} else {
15124			position = this._core.relative(this._core.current());
15125			length = this._core.items().length;
15126			successor ? position += settings.slideBy : position -= settings.slideBy;
15127		}
15128
15129		return position;
15130	};
15131
15132	/**
15133	 * Slides to the next item or page.
15134	 * @public
15135	 * @param {Number} [speed=false] - The time in milliseconds for the transition.
15136	 */
15137	Navigation.prototype.next = function(speed) {
15138		$.proxy(this._overrides.to, this._core)(this.getPosition(true), speed);
15139	};
15140
15141	/**
15142	 * Slides to the previous item or page.
15143	 * @public
15144	 * @param {Number} [speed=false] - The time in milliseconds for the transition.
15145	 */
15146	Navigation.prototype.prev = function(speed) {
15147		$.proxy(this._overrides.to, this._core)(this.getPosition(false), speed);
15148	};
15149
15150	/**
15151	 * Slides to the specified item or page.
15152	 * @public
15153	 * @param {Number} position - The position of the item or page.
15154	 * @param {Number} [speed] - The time in milliseconds for the transition.
15155	 * @param {Boolean} [standard=false] - Whether to use the standard behaviour or not.
15156	 */
15157	Navigation.prototype.to = function(position, speed, standard) {
15158		var length;
15159
15160		if (!standard) {
15161			length = this._pages.length;
15162			$.proxy(this._overrides.to, this._core)(this._pages[((position % length) + length) % length].start, speed);
15163		} else {
15164			$.proxy(this._overrides.to, this._core)(position, speed);
15165		}
15166	};
15167
15168	$.fn.owlCarousel.Constructor.Plugins.Navigation = Navigation;
15169
15170})(window.Zepto || window.jQuery, window, document);
15171
15172/**
15173 * Hash Plugin
15174 * @version 2.0.0-beta.3
15175 * @author Artus Kolanowski
15176 * @license The MIT License (MIT)
15177 */
15178;(function($, window, document, undefined) {
15179	'use strict';
15180
15181	/**
15182	 * Creates the hash plugin.
15183	 * @class The Hash Plugin
15184	 * @param {Owl} carousel - The Owl Carousel
15185	 */
15186	var Hash = function(carousel) {
15187		/**
15188		 * Reference to the core.
15189		 * @protected
15190		 * @type {Owl}
15191		 */
15192		this._core = carousel;
15193
15194		/**
15195		 * Hash index for the items.
15196		 * @protected
15197		 * @type {Object}
15198		 */
15199		this._hashes = {};
15200
15201		/**
15202		 * The carousel element.
15203		 * @type {jQuery}
15204		 */
15205		this.$element = this._core.$element;
15206
15207		/**
15208		 * All event handlers.
15209		 * @protected
15210		 * @type {Object}
15211		 */
15212		this._handlers = {
15213			/*'initialized.owl.carousel': $.proxy(function(e) {
15214				if (e.namespace && this._core.settings.startPosition === 'URLHash') {
15215					$(window).trigger('hashchange.owl.navigation');
15216				}
15217			}, this),*/
15218			'prepared.owl.carousel': $.proxy(function(e) {
15219				if (e.namespace) {
15220					var hash = $(e.content).find('[data-hash]').addBack('[data-hash]').attr('data-hash');
15221
15222					if (!hash) {
15223						return;
15224					}
15225
15226					this._hashes[hash] = e.content;
15227				}
15228			}, this),
15229			'changed.owl.carousel': $.proxy(function(e) {
15230				if (e.namespace && e.property.name === 'position') {
15231					var current = this._core.items(this._core.relative(this._core.current())),
15232						hash = $.map(this._hashes, function(item, hash) {
15233							return item === current ? hash : null;
15234						}).join();
15235
15236					if (!hash || window.location.hash.slice(1) === hash) {
15237						return;
15238					}
15239
15240					window.location.hash = hash;
15241				}
15242			}, this)
15243		};
15244
15245		// set default options
15246		this._core.options = $.extend({}, Hash.Defaults, this._core.options);
15247
15248		// register the event handlers
15249		this.$element.on(this._handlers);
15250
15251		// register event listener for hash navigation
15252		/*$(window).on('hashchange.owl.navigation', $.proxy(function(e) {
15253			var hash = window.location.hash.substring(1),
15254				items = this._core.$stage.children(),
15255				position = this._hashes[hash] && items.index(this._hashes[hash]);
15256
15257			if (position === undefined || position === this._core.current()) {
15258				return;
15259			}
15260
15261			this._core.to(this._core.relative(position), false, true);
15262		}, this));*/
15263	};
15264
15265	/**
15266	 * Default options.
15267	 * @public
15268	 */
15269	Hash.Defaults = {
15270		URLhashListener: false
15271	};
15272
15273	/**
15274	 * Destroys the plugin.
15275	 * @public
15276	 */
15277	Hash.prototype.destroy = function() {
15278		var handler, property;
15279
15280		$(window).off('hashchange.owl.navigation');
15281
15282		for (handler in this._handlers) {
15283			this._core.$element.off(handler, this._handlers[handler]);
15284		}
15285		for (property in Object.getOwnPropertyNames(this)) {
15286			typeof this[property] != 'function' && (this[property] = null);
15287		}
15288	};
15289
15290	$.fn.owlCarousel.Constructor.Plugins.Hash = Hash;
15291
15292})(window.Zepto || window.jQuery, window, document);
15293
15294/**
15295 * Support Plugin
15296 *
15297 * @version 2.0.0-beta.3
15298 * @author Vivid Planet Software GmbH
15299 * @author Artus Kolanowski
15300 * @license The MIT License (MIT)
15301 */
15302;(function($, window, document, undefined) {
15303
15304	var style = $('<support>').get(0).style,
15305		prefixes = 'Webkit Moz O ms'.split(' '),
15306		events = {
15307			transition: {
15308				end: {
15309					WebkitTransition: 'webkitTransitionEnd',
15310					MozTransition: 'transitionend',
15311					OTransition: 'oTransitionEnd',
15312					transition: 'transitionend'
15313				}
15314			},
15315			animation: {
15316				end: {
15317					WebkitAnimation: 'webkitAnimationEnd',
15318					MozAnimation: 'animationend',
15319					OAnimation: 'oAnimationEnd',
15320					animation: 'animationend'
15321				}
15322			}
15323		},
15324		tests = {
15325			csstransforms: function() {
15326				return !!test('transform');
15327			},
15328			csstransforms3d: function() {
15329				return !!test('perspective');
15330			},
15331			csstransitions: function() {
15332				return !!test('transition');
15333			},
15334			cssanimations: function() {
15335				return !!test('animation');
15336			}
15337		};
15338
15339	function test(property, prefixed) {
15340		var result = false,
15341			upper = property.charAt(0).toUpperCase() + property.slice(1);
15342
15343		$.each((property + ' ' + prefixes.join(upper + ' ') + upper).split(' '), function(i, property) {
15344			if (style[property] !== undefined) {
15345				result = prefixed ? property : true;
15346				return false;
15347			}
15348		});
15349
15350		return result;
15351	}
15352
15353	function prefixed(property) {
15354		return test(property, true);
15355	}
15356
15357	if (tests.csstransitions()) {
15358		/* jshint -W053 */
15359		$.support.transition = new String(prefixed('transition'))
15360		$.support.transition.end = events.transition.end[ $.support.transition ];
15361	}
15362
15363	if (tests.cssanimations()) {
15364		/* jshint -W053 */
15365		$.support.animation = new String(prefixed('animation'))
15366		$.support.animation.end = events.animation.end[ $.support.animation ];
15367	}
15368
15369	if (tests.csstransforms()) {
15370		/* jshint -W053 */
15371		$.support.transform = new String(prefixed('transform'));
15372		$.support.transform3d = tests.csstransforms3d();
15373	}
15374
15375})(window.Zepto || window.jQuery, window, document);
15376(function(window, document, undefined) {
15377
15378    /**
15379     * Find the absolute position of an element
15380     */
15381    var absPos = function(element) {
15382        var offsetLeft, offsetTop;
15383        offsetLeft = offsetTop = 0;
15384        if (element.offsetParent) {
15385            do {
15386                offsetLeft += element.offsetLeft;
15387                offsetTop += element.offsetTop;
15388            } while (element = element.offsetParent);
15389        }
15390        return [offsetLeft, offsetTop];
15391    };
15392
15393    /**
15394     * @constructor Progress Circle class
15395     * @param params.canvas Canvas on which the circles will be drawn.
15396     * @param params.minRadius Inner radius of the innermost circle, in px.
15397     * @param params.arcWidth Width of each circle(to be more accurate, ring).
15398     * @param params.gapWidth Space between each circle.
15399     * @param params.centerX X coordinate of the center of circles.
15400     * @param params.centerY Y coordinate of the center of circles.
15401     * @param params.infoLineBaseAngle Base angle of the info line.
15402     * @param params.infoLineAngleInterval Angles between the info lines.
15403     */
15404    var ProgressCircle = function(params) {
15405        this.canvas = params.canvas;
15406        this.minRadius = params.minRadius || 15;
15407        this.arcWidth = params.arcWidth || 5;
15408        this.gapWidth = params.gapWidth || 3;
15409        this.centerX = params.centerX || this.canvas.width / 2;
15410        this.centerY = params.centerY || this.canvas.height / 2;
15411        this.infoLineLength = params.infoLineLength || 60;
15412        this.horizLineLength = params.horizLineLength || 10;
15413        this.infoLineAngleInterval = params.infoLineAngleInterval || Math.PI / 8;
15414        this.infoLineBaseAngle = params.infoLineBaseAngle || Math.PI / 6;
15415
15416        this.context = this.canvas.getContext('2d');
15417
15418        this.width = this.canvas.width;
15419        this.height = this.canvas.height;
15420
15421        this.circles = [];
15422        this.runningCount = 0;
15423    };
15424
15425    ProgressCircle.prototype = {
15426        constructor: ProgressCircle,
15427
15428        /**
15429         * @method Adds an progress monitor entry.
15430         * @param params.fillColor Color to fill in the circle.
15431         * @param params.outlineColor Color to outline the circle.
15432         * @param params.progressListener Callback function to fetch the progress.
15433         * @param params.infoListener Callback function to fetch the info.
15434         * @returns this
15435         */
15436        addEntry: function(params) {
15437            this.circles.push(new Circle({
15438                canvas: this.canvas,
15439                context: this.context,
15440                centerX: this.centerX,
15441                centerY: this.centerY,
15442                innerRadius: this.minRadius + this.circles.length *
15443                    (this.gapWidth + this.arcWidth),
15444                arcWidth: this.arcWidth,
15445                infoLineLength: this.infoLineLength,
15446                horizLineLength: this.horizLineLength,
15447
15448                id: this.circles.length,
15449                fillColor: params.fillColor,
15450                outlineColor: params.outlineColor,
15451                progressListener: params.progressListener,
15452                infoListener: params.infoListener,
15453                infoLineAngle: this.infoLineBaseAngle +
15454                    this.circles.length * this.infoLineAngleInterval,
15455            }));
15456
15457            return this;
15458        },
15459
15460        /**
15461         * @method Starts the monitor and updates with the given interval.
15462         * @param interval Interval between updates, in millisecond.
15463         * @returns this
15464         */
15465        start: function(interval) {
15466            var self = this;
15467            this.timer = setInterval(function() {
15468                self._update();
15469            }, interval || 33);
15470
15471            return this;
15472        },
15473
15474        /**
15475         * @method Stop the animation.
15476         */
15477        stop: function() {
15478            clearTimeout(this.timer);
15479        },
15480
15481        /**
15482         * @private
15483         * @method Call update on each circle and redraw them.
15484         * @returns this
15485         */
15486        _update: function() {
15487            this._clear();
15488            this.circles.forEach(function(circle, idx, array) {
15489                circle.update();
15490            });
15491
15492            return this;
15493        },
15494
15495        /**
15496         * @private
15497         * @method Clear the canvas.
15498         * @returns this
15499         */
15500        _clear: function() {
15501            this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
15502
15503            return this;
15504        },
15505
15506    };
15507
15508    /**
15509     * @private
15510     * @class Individual progress circle.
15511     * @param params.canvas Canvas on which the circle will be drawn.
15512     * @param params.context Context of the canvas.
15513     * @param params.innerRadius Inner radius of the circle, in px.
15514     * @param params.arcWidth Width of each arc(circle).
15515     * @param params.gapWidth Distance between each arc.
15516     * @param params.centerX X coordinate of the center of circles.
15517     * @param params.centerY Y coordinate of the center of circles.
15518     * @param params.fillColor Color to fill in the circle.
15519     * @param params.outlineColor Color to outline the circle.
15520     * @param params.progressListener Callback function to fetch the progress.
15521     * @param params.infoListener Callback function to fetch the info.
15522     * @param params.infoLineAngle Angle of info line.
15523     */
15524    var Circle = function(params) {
15525        this.id = params.id;
15526        this.canvas = params.canvas;
15527        this.context = params.context;
15528        this.centerX = params.centerX;
15529        this.centerY = params.centerY;
15530        this.arcWidth = params.arcWidth;
15531        this.innerRadius = params.innerRadius || 0;
15532        this.fillColor = params.fillColor || '#fff';
15533        this.outlineColor = params.outlineColor || this.fillColor;
15534        this.progressListener = params.progressListener;
15535        this.infoLineLength = params.infoLineLength || 250;
15536        this.horizLineLength = params.horizLineLength || 50;
15537        this.infoListener = params.infoListener;
15538        this.infoLineAngle = params.infoLineAngle;
15539
15540        this.outerRadius = this.innerRadius + this.arcWidth;
15541
15542        // If the info listener is not registered, then don't calculate
15543        // the related coordinates
15544        if (!this.infoListener) return;
15545
15546        // calculate the info-line turning points
15547        var angle = this.infoLineAngle,
15548            arcDistance = (this.innerRadius + this.outerRadius) / 2,
15549
15550            sinA = Math.sin(angle),
15551            cosA = Math.cos(angle);
15552
15553        this.infoLineStartX = this.centerX + sinA * arcDistance;
15554        this.infoLineStartY = this.centerY - cosA * arcDistance;
15555
15556        this.infoLineMidX = this.centerX + sinA * this.infoLineLength;
15557        this.infoLineMidY = this.centerY - cosA * this.infoLineLength;
15558
15559        this.infoLineEndX = this.infoLineMidX +
15560             (sinA < 0 ? -this.horizLineLength : this.horizLineLength);
15561        this.infoLineEndY = this.infoLineMidY;
15562
15563        var infoText = document.createElement('div'),
15564            style = infoText.style;
15565
15566        style.color = this.fillColor;
15567        style.position = 'absolute';
15568        style.left = this.infoLineEndX + absPos(this.canvas)[0] + 'px';
15569        // style.top will be calculated in the `drawInfo` method. Since
15570        // user may want to change the size of the font, so the top offset
15571        // must be updated in each loop.
15572        infoText.className = 'ProgressCircleInfo'; // For css styling
15573        infoText.id = 'progress_circle_info_' + this.id;
15574        document.body.appendChild(infoText);
15575        this.infoText = infoText;
15576    };
15577
15578    Circle.prototype = {
15579        constructor: Circle,
15580
15581        update: function() {
15582            this.progress = this.progressListener();
15583            this._draw();
15584
15585            if (this.infoListener) {
15586                this.info = this.infoListener();
15587                this._drawInfo();
15588            }
15589        },
15590
15591        /**
15592         * @private
15593         * @method Draw the circle on the canvas.
15594         * @returns this
15595         */
15596        _draw: function() {
15597            var ctx = this.context,
15598
15599                ANGLE_OFFSET = -Math.PI / 2,
15600
15601                startAngle = 0 + ANGLE_OFFSET,
15602                endAngle= startAngle + this.progress * Math.PI * 2,
15603
15604                x = this.centerX,
15605                y = this.centerY,
15606
15607                innerRadius = this.innerRadius - this.arcWidth - 1,
15608                outerRadius = this.outerRadius - this.arcWidth - 1;
15609
15610            ctx.fillStyle = this.fillColor;
15611            ctx.strokeStyle = this.outlineColor;
15612
15613            ctx.beginPath();
15614            ctx.arc(x, y, innerRadius, startAngle, endAngle, false);
15615            ctx.arc(x, y, outerRadius, endAngle, startAngle, true);
15616            ctx.closePath();
15617            ctx.stroke();
15618            ctx.fill();
15619
15620            return this;
15621        },
15622
15623        /**
15624         * @private
15625         * @method Draw the info lines and info text.
15626         * @returns this
15627         */
15628        _drawInfo: function() {
15629
15630            var pointList, lineHeight;
15631
15632            pointList = [
15633                [this.infoLineStartX, this.infoLineStartY],
15634                [this.infoLineMidX, this.infoLineMidY],
15635                [this.infoLineEndX, this.infoLineEndY],
15636            ];
15637            this._drawSegments(pointList, false);
15638
15639            this.infoText.innerHTML = this.info;
15640
15641            lineHeight = this.infoText.offsetHeight;
15642            this.infoText.style.top = this.infoLineEndY +
15643                absPos(this.canvas)[1] - lineHeight / 2 + 'px';
15644
15645            return this;
15646        },
15647
15648        /**
15649         * @private
15650         * @method Helper function to draw the segments
15651         * @param pointList An array of points in the form of [x, y].
15652         * @param close Whether to connect the first and last point.
15653         */
15654        _drawSegments: function(pointList, close) {
15655            var ctx = this.context;
15656
15657            ctx.beginPath();
15658            ctx.moveTo(pointList[0][0], pointList[0][1]);
15659            for (var i = 1; i < pointList.length; ++i) {
15660                ctx.lineTo(pointList[i][0], pointList[i][1]);
15661            }
15662
15663            if (close) {
15664                ctx.closePath();
15665            }
15666            ctx.stroke();
15667        },
15668    };
15669
15670    window.ProgressCircle = ProgressCircle;
15671
15672})(window, document);
15673
15674
15675
15676
15677/* =========================================================
15678 * jquery.vc_chart.js v1.0
15679 * =========================================================
15680 * Copyright 2013 Wpbakery
15681 *
15682 * Jquery chart plugin for the Visual Composer.
15683 * ========================================================= */
15684(function($){
15685    /**
15686     * Pie chart animated.
15687     * @param element - DOM element
15688     * @param options - settings object.
15689     * @constructor
15690     */
15691    var VcChart = function(element, options) {
15692        this.el = element;
15693        this.$el = $(this.el);
15694        this.options = $.extend({
15695            color: 'wpb_button',
15696            units: '',
15697            width: '',
15698            label_selector: '.vc_pie_chart_value',
15699            back_selector: '.vc_pie_chart_back',
15700            responsive: true
15701        }, options);
15702        this.init();
15703    };
15704    VcChart.prototype = {
15705        constructor: VcChart,
15706        _progress_v: 0,
15707        animated: false,
15708        colors: {
15709            'wpb_button': 'rgba(247, 247, 247, 1)',
15710            'btn-primary': 'rgba(0, 136, 204, 1)',
15711            'btn-info': 'rgba(88, 185, 218, 1)',
15712            'btn-success': 'rgba(106, 177, 101, 1)',
15713            'btn-warning': 'rgba(255, 153, 0, 1)',
15714            'btn-danger': 'rgba(255, 103, 91, 1)',
15715            'btn-inverse': 'rgba(85, 85, 85, 1)'
15716        },
15717        init: function() {
15718            this.setupColor();
15719            this.value = this.$el.data('pie-value')/100;
15720            this.label_value = this.$el.data('pie-label-value') || this.$el.data('pie-value');
15721            this.$wrapper = $('.vc_pie_wrapper', this.$el);
15722            this.$label = $(this.options.label_selector, this.$el);
15723            this.$back = $(this.options.back_selector, this.$el);
15724            this.$canvas = this.$el.find('canvas');
15725            this.arcWidth = this.$el.data('pie-width') * 2;
15726            this.draw();
15727            this.setWayPoint();
15728            if(this.options.responsive === true) this.setResponsive();
15729            if (UNCODE.isMobile) this._progress_v = this.value;
15730        },
15731        setupColor: function() {
15732            // if(typeof this.colors[this.options.color] !== 'undefined') {
15733            //     this.color = this.colors[this.options.color];
15734            // } else if(typeof this.options.color === 'string' && this.options.color.match(/^rgba?\([^\)]+\)/)) {
15735            //     this.color = this.options.color;
15736            // } else {
15737            //     this.color = 'rgba(247, 247, 247, 0.2)';
15738            // }
15739            if(typeof this.options.color !== 'undefined') {
15740                this.color = this.options.color;
15741            } else this.color = 'rgba(247, 247, 247, 0.2)';
15742
15743        },
15744        setResponsive: function() {
15745            var that = this;
15746            if (!UNCODE.isMobile) {
15747                $(window).resize(function(){
15748                    if(that.animated === true) that.circle.stop();
15749                    that.draw(true);
15750                });
15751            }
15752        },
15753        draw: function(redraw) {
15754            var w = this.$el.addClass('vc_ready').width() * 2,
15755                border_w = this.arcWidth,
15756                radius;
15757            if(!w) w = this.$el.parents(':visible').first().width()-2;
15758            //w = Math.round(w/100*80);
15759            //if (w < 250) w = 250;
15760            radius = w/2;
15761            this.$wrapper.css({"width" : (w / 2) + "px"});
15762            this.$label.css({"width" : (w / 2), "height" : (w / 2), "line-height" : (w / 2)+"px"});
15763            this.$back.css({"width" : (w / 2), "height" : (w / 2)});
15764            this.$canvas.attr({"width" : w + "px", "height" : w + "px"});
15765            this.$el.addClass('vc_ready');
15766            this.circle = new ProgressCircle({
15767                canvas: this.$canvas.get(0),
15768                minRadius: radius,
15769                arcWidth: border_w
15770            });
15771            if(redraw === true && this.animated === true) {
15772                this._progress_v = this.value;
15773                this.circle.addEntry({
15774                    fillColor: this.color,
15775                    progressListener: $.proxy(this.setProgress, this)
15776                }).start();
15777            }
15778        },
15779        setProgress: function() {
15780            if (this._progress_v >= this.value) {
15781                this.circle.stop();
15782                this.animated = true;
15783                this.$label.html(this.label_value + this.options.units);
15784                return this._progress_v;
15785            }
15786            this._progress_v += 0.01;
15787            if (!isNaN(this.label_value)) {
15788                var label_value = this._progress_v/this.value*this.label_value;
15789                var val = Math.round(label_value) + this.options.units;
15790                this.$label.html(val);
15791            } else this.$label.html(this.label_value + this.options.units);
15792            return this._progress_v;
15793        },
15794        animate: function() {
15795            if(this.animated !== true) {
15796                this.circle.addEntry({
15797                    fillColor: this.color,
15798                    progressListener: $.proxy(this.setProgress, this)
15799                }).start(10);
15800            }
15801        },
15802        setWayPoint: function() {
15803            if (typeof $.fn.waypoint !== 'undefined' && !UNCODE.isMobile) {
15804                this.$el.waypoint($.proxy(this.animate, this), { offset: '85%' });
15805            } else {
15806                this.animate();
15807            }
15808        }
15809    };
15810    /**
15811     * jQuery plugin
15812     * @param option - object with settings
15813     * @return {*}
15814     */
15815    $.fn.vcChat = function(option, value) {
15816        return this.each(function () {
15817            var $this = $(this),
15818                data = $this.data('vc_chart'),
15819                options = typeof option === 'object' ? option : {
15820                    color: $this.data('pie-color'),
15821                    units: $this.data('pie-units')
15822                };
15823            if (typeof option == 'undefined') $this.data('vc_chart', (data = new VcChart(this, options)));
15824            if (typeof option == 'string') data[option](value);
15825        });
15826    };
15827    /**
15828     * Allows users to rewrite function inside theme.
15829     */
15830    if ( typeof window['vc_pieChart'] !== 'function' ) {
15831        window.vc_pieChart = function() {
15832            $('.vc_pie_chart:visible').vcChat();
15833        }
15834    }
15835    $(document).ready(function(){
15836        !window.vc_iframe && vc_pieChart();
15837    });
15838
15839})(window.jQuery);
15840
15841/* Progress bar
15842 ---------------------------------------------------------- */
15843function uncode_progress_bar() {    
15844	jQuery.each(jQuery('.vc_progress_bar'), function(index, val) {
15845		if (!UNCODE.isMobile) {
15846			new Waypoint({
15847				element: val,
15848				handler: function() {
15849					var element = jQuery(this.element);
15850					element.find('.vc_single_bar').each(function(index) {          
15851						var $this = jQuery(this),
15852							            bar = $this.find('.vc_bar'),
15853							            val = bar.data('percentage-value');          
15854						setTimeout(function() {            
15855							bar.css({
15856								"width": val + '%'
15857							});          
15858						}, index * 200);        
15859					});
15860				},
15861				offset: '80%'
15862			});
15863		} else {
15864			var element = jQuery(val);
15865			element.find('.vc_single_bar').each(function(index) {          
15866				var $this = jQuery(this),
15867					            bar = $this.find('.vc_bar'),
15868					            val = bar.data('percentage-value');          
15869				setTimeout(function() {            
15870					bar.css({
15871						"width": val + '%'
15872					});          
15873				}, index * 200);        
15874			});
15875		}
15876	});
15877};
15878uncode_progress_bar();
15879/*!
15880 * jquery.counterup.js 1.0
15881 *
15882 * Copyright 2013, Benjamin Intal http://gambit.ph @bfintal
15883 * Released under the GPL v2 License
15884 *
15885 * Date: Nov 26, 2013
15886 */
15887(function($) {
15888	"use strict";
15889	$.fn.counterUp = function(options) {
15890		// Defaults
15891		var settings = $.extend({
15892			'time': 400,
15893			'delay': 10
15894		}, options);
15895		return this.each(function() {
15896			// Store the object
15897			var $this = $(this);
15898			var $settings = settings;
15899			var counterUpper = function() {
15900				var nums = [];
15901				var divisions = $settings.time / $settings.delay;
15902				var numReal = $this.attr('data-val'),
15903				num = numReal;
15904				var isComma = /[0-9]+,[0-9]+/.test(num);
15905				num = num.replace(/,/g, '');
15906				var isInt = /^[0-9]+$/.test(num);
15907				var isFloat = /^[0-9]+\.[0-9]+$/.test(num);
15908				var decimalPlaces = isFloat ? (num.split('.')[1] || []).length : 0;
15909				// Generate list of incremental numbers to display
15910				for (var i = divisions; i >= 1; i--) {
15911					// Preserve as int if input was int
15912					var newNum = parseInt(num / divisions * i);
15913					// Preserve float if input was float
15914					if (isFloat) {
15915						newNum = parseFloat(num / divisions * i).toFixed(decimalPlaces);
15916					}
15917					// Preserve commas if input had commas
15918					if (isComma) {
15919						while (/(\d+)(\d{3})/.test(newNum.toString())) {
15920							newNum = newNum.toString().replace(/(\d+)(\d{3})/, '$1' + ',' + '$2');
15921						}
15922					}
15923					nums.unshift(newNum);
15924				}
15925				nums.push(numReal);
15926				$this.data('counterup-nums', nums);
15927				$this.text('0');
15928				// Updates the number until we're done
15929				var f = function() {
15930					if ($this.data('counterup-nums') != null) {
15931						$this.text($this.data('counterup-nums').shift());
15932						if ($this.data('counterup-nums').length) {
15933							setTimeout($this.data('counterup-func'), $settings.delay);
15934						} else {
15935							delete $this.data('counterup-nums');
15936							$this.data('counterup-nums', null);
15937							$this.data('counterup-func', null);
15938						}
15939					}
15940				};
15941				$this.data('counterup-func', f);
15942				// Start the count up
15943				setTimeout($this.data('counterup-func'), $settings.delay);
15944			};
15945			// Perform counts when the element gets into view
15946			new Waypoint({
15947				element: this,
15948				handler: function() {
15949					counterUpper();
15950					this.destroy();
15951				},
15952				offset: '100%'
15953			});
15954		});
15955	};
15956})(jQuery);
15957
15958/*!
15959 * The Final Countdown for jQuery v2.0.4 (http://hilios.github.io/jQuery.countdown/)
15960 * Copyright (c) 2014 Edson Hilios
15961 *
15962 * Permission is hereby granted, free of charge, to any person obtaining a copy of
15963 * this software and associated documentation files (the "Software"), to deal in
15964 * the Software without restriction, including without limitation the rights to
15965 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
15966 * the Software, and to permit persons to whom the Software is furnished to do so,
15967 * subject to the following conditions:
15968 *
15969 * The above copyright notice and this permission notice shall be included in all
15970 * copies or substantial portions of the Software.
15971 *
15972 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15973 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15974 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15975 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
15976 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
15977 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15978 */
15979(function(factory) {
15980    "use strict";
15981    if (typeof define === "function" && define.amd) {
15982        define([ "jquery" ], factory);
15983    } else {
15984        factory(jQuery);
15985    }
15986})(function($) {
15987    "use strict";
15988    var PRECISION = 100;
15989    var instances = [], matchers = [];
15990    matchers.push(/^[0-9]*$/.source);
15991    matchers.push(/([0-9]{1,2}\/){2}[0-9]{4}( [0-9]{1,2}(:[0-9]{2}){2})?/.source);
15992    matchers.push(/[0-9]{4}([\/\-][0-9]{1,2}){2}( [0-9]{1,2}(:[0-9]{2}){2})?/.source);
15993    matchers = new RegExp(matchers.join("|"));
15994    function parseDateString(dateString) {
15995        if (dateString instanceof Date) {
15996            return dateString;
15997        }
15998        if (String(dateString).match(matchers)) {
15999            if (String(dateString).match(/^[0-9]*$/)) {
16000                dateString = Number(dateString);
16001            }
16002            if (String(dateString).match(/\-/)) {
16003                dateString = String(dateString).replace(/\-/g, "/");
16004            }
16005            return new Date(dateString);
16006        } else {
16007            throw new Error("Couldn't cast `" + dateString + "` to a date object.");
16008        }
16009    }
16010    var DIRECTIVE_KEY_MAP = {
16011        Y: "years",
16012        m: "months",
16013        w: "weeks",
16014        d: "days",
16015        D: "totalDays",
16016        H: "hours",
16017        M: "minutes",
16018        S: "seconds"
16019    };
16020    function strftime(offsetObject) {
16021        return function(format) {
16022            var directives = format.match(/%(-|!)?[A-Z]{1}(:[^;]+;)?/gi);
16023            if (directives) {
16024                for (var i = 0, len = directives.length; i < len; ++i) {
16025                    var directive = directives[i].match(/%(-|!)?([a-zA-Z]{1})(:[^;]+;)?/), regexp = new RegExp(directive[0]), modifier = directive[1] || "", plural = directive[3] || "", value = null;
16026                    directive = directive[2];
16027                    if (DIRECTIVE_KEY_MAP.hasOwnProperty(directive)) {
16028                        value = DIRECTIVE_KEY_MAP[directive];
16029                        value = Number(offsetObject[value]);
16030                    }
16031                    if (value !== null) {
16032                        if (modifier === "!") {
16033                            value = pluralize(plural, value);
16034                        }
16035                        if (modifier === "") {
16036                            if (value < 10) {
16037                                value = "0" + value.toString();
16038                            }
16039                        }
16040                        format = format.replace(regexp, value.toString());
16041                    }
16042                }
16043            }
16044            format = format.replace(/%%/, "%");
16045            return format;
16046        };
16047    }
16048    function pluralize(format, count) {
16049        var plural = "s", singular = "";
16050        if (format) {
16051            format = format.replace(/(:|;|\s)/gi, "").split(/\,/);
16052            if (format.length === 1) {
16053                plural = format[0];
16054            } else {
16055                singular = format[0];
16056                plural = format[1];
16057            }
16058        }
16059        if (Math.abs(count) === 1) {
16060            return singular;
16061        } else {
16062            return plural;
16063        }
16064    }
16065    var Countdown = function(el, finalDate, callback) {
16066        this.el = el;
16067        this.$el = $(el);
16068        this.interval = null;
16069        this.offset = {};
16070        this.instanceNumber = instances.length;
16071        instances.push(this);
16072        this.$el.data("countdown-instance", this.instanceNumber);
16073        if (callback) {
16074            this.$el.on("update.countdown", callback);
16075            this.$el.on("stoped.countdown", callback);
16076            this.$el.on("finish.countdown", callback);
16077        }
16078        this.setFinalDate(finalDate);
16079        this.start();
16080    };
16081    $.extend(Countdown.prototype, {
16082        start: function() {
16083            if (this.interval !== null) {
16084                clearInterval(this.interval);
16085            }
16086            var self = this;
16087            this.update();
16088            this.interval = setInterval(function() {
16089                self.update.call(self);
16090            }, PRECISION);
16091        },
16092        stop: function() {
16093            clearInterval(this.interval);
16094            this.interval = null;
16095            this.dispatchEvent("stoped");
16096        },
16097        pause: function() {
16098            this.stop.call(this);
16099        },
16100        resume: function() {
16101            this.start.call(this);
16102        },
16103        remove: function() {
16104            this.stop();
16105            instances[this.instanceNumber] = null;
16106            delete this.$el.data().countdownInstance;
16107        },
16108        setFinalDate: function(value) {
16109            this.finalDate = parseDateString(value);
16110        },
16111        update: function() {
16112            if (this.$el.closest("html").length === 0) {
16113                this.remove();
16114                return;
16115            }
16116            this.totalSecsLeft = this.finalDate.getTime() - new Date().getTime();
16117            this.totalSecsLeft = Math.ceil(this.totalSecsLeft / 1e3);
16118            this.totalSecsLeft = this.totalSecsLeft < 0 ? 0 : this.totalSecsLeft;
16119            this.offset = {
16120                seconds: this.totalSecsLeft % 60,
16121                minutes: Math.floor(this.totalSecsLeft / 60) % 60,
16122                hours: Math.floor(this.totalSecsLeft / 60 / 60) % 24,
16123                days: Math.floor(this.totalSecsLeft / 60 / 60 / 24) % 7,
16124                totalDays: Math.floor(this.totalSecsLeft / 60 / 60 / 24),
16125                weeks: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 7),
16126                months: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 30),
16127                years: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 365)
16128            };
16129            if (this.totalSecsLeft === 0) {
16130                this.stop();
16131                this.dispatchEvent("finish");
16132            } else {
16133                this.dispatchEvent("update");
16134            }
16135        },
16136        dispatchEvent: function(eventName) {
16137            var event = $.Event(eventName + ".countdown");
16138            event.finalDate = this.finalDate;
16139            event.offset = $.extend({}, this.offset);
16140            event.strftime = strftime(this.offset);
16141            this.$el.trigger(event);
16142        }
16143    });
16144    $.fn.countdown = function() {
16145        var argumentsArray = Array.prototype.slice.call(arguments, 0);
16146        return this.each(function() {
16147            var instanceNumber = $(this).data("countdown-instance");
16148            if (instanceNumber !== undefined) {
16149                var instance = instances[instanceNumber], method = argumentsArray[0];
16150                if (Countdown.prototype.hasOwnProperty(method)) {
16151                    instance[method].apply(instance, argumentsArray.slice(1));
16152                } else if (String(method).match(/^[$A-Z_][0-9A-Z_$]*$/i) === null) {
16153                    instance.setFinalDate.call(instance, method);
16154                    instance.start();
16155                } else {
16156                    $.error("Method %s does not exist on jQuery.countdown".replace(/\%s/gi, method));
16157                }
16158            } else {
16159                new Countdown(this, argumentsArray[0], argumentsArray[1]);
16160            }
16161        });
16162    };
16163});
16164
16165/*! waitForImages jQuery Plugin - v2.0.2 - 2015-05-05
16166* https://github.com/alexanderdickson/waitForImages
16167* Copyright (c) 2015 Alex Dickson; Licensed MIT */
16168;(function (factory) {
16169    if (typeof define === 'function' && define.amd) {
16170        // AMD. Register as an anonymous module.
16171        define(['jquery'], factory);
16172    } else if (typeof exports === 'object') {
16173        // CommonJS / nodejs module
16174        module.exports = factory(require('jquery'));
16175    } else {
16176        // Browser globals
16177        factory(jQuery);
16178    }
16179}(function ($) {
16180    // Namespace all events.
16181    var eventNamespace = 'waitForImages';
16182
16183    // CSS properties which contain references to images.
16184    $.waitForImages = {
16185        hasImageProperties: [
16186            'backgroundImage',
16187            'listStyleImage',
16188            'borderImage',
16189            'borderCornerImage',
16190            'cursor'
16191        ],
16192        hasImageAttributes: ['srcset']
16193    };
16194
16195    // Custom selector to find `img` elements that have a valid `src`
16196    // attribute and have not already loaded.
16197    $.expr[':'].uncached = function (obj) {
16198        // Ensure we are dealing with an `img` element with a valid
16199        // `src` attribute.
16200        if (!$(obj).is('img[src][src!=""]')) {
16201            return false;
16202        }
16203
16204        return !obj.complete;
16205    };
16206
16207    $.fn.waitForImages = function () {
16208
16209        var allImgsLength = 0;
16210        var allImgsLoaded = 0;
16211        var deferred = $.Deferred();
16212
16213        var finishedCallback;
16214        var eachCallback;
16215        var waitForAll;
16216
16217        // Handle options object (if passed).
16218        if ($.isPlainObject(arguments[0])) {
16219
16220            waitForAll = arguments[0].waitForAll;
16221            eachCallback = arguments[0].each;
16222            finishedCallback = arguments[0].finished;
16223
16224        } else {
16225
16226            // Handle if using deferred object and only one param was passed in.
16227            if (arguments.length === 1 && $.type(arguments[0]) === 'boolean') {
16228                waitForAll = arguments[0];
16229            } else {
16230                finishedCallback = arguments[0];
16231                eachCallback = arguments[1];
16232                waitForAll = arguments[2];
16233            }
16234
16235        }
16236
16237        // Handle missing callbacks.
16238        finishedCallback = finishedCallback || $.noop;
16239        eachCallback = eachCallback || $.noop;
16240
16241        // Convert waitForAll to Boolean
16242        waitForAll = !! waitForAll;
16243
16244        // Ensure callbacks are functions.
16245        if (!$.isFunction(finishedCallback) || !$.isFunction(eachCallback)) {
16246            throw new TypeError('An invalid callback was supplied.');
16247        }
16248
16249        this.each(function () {
16250            // Build a list of all imgs, dependent on what images will
16251            // be considered.
16252            var obj = $(this);
16253            var allImgs = [];
16254            // CSS properties which may contain an image.
16255            var hasImgProperties = $.waitForImages.hasImageProperties || [];
16256            // Element attributes which may contain an image.
16257            var hasImageAttributes = $.waitForImages.hasImageAttributes || [];
16258            // To match `url()` references.
16259            // Spec: http://www.w3.org/TR/CSS2/syndata.html#value-def-uri
16260            var matchUrl = /url\(\s*(['"]?)(.*?)\1\s*\)/g;
16261
16262            if (waitForAll) {
16263
16264                // Get all elements (including the original), as any one of
16265                // them could have a background image.
16266                obj.find('*').addBack().each(function () {
16267                    var element = $(this);
16268
16269                    // If an `img` element, add it. But keep iterating in
16270                    // case it has a background image too.
16271                    if (element.is('img:uncached')) {
16272                        allImgs.push({
16273                            src: element.attr('src'),
16274                            element: element[0]
16275                        });
16276                    }
16277
16278                    $.each(hasImgProperties, function (i, property) {
16279                        var propertyValue = element.css(property);
16280                        var match;
16281
16282                        // If it doesn't contain this property, skip.
16283                        if (!propertyValue) {
16284                            return true;
16285                        }
16286
16287                        // Get all url() of this element.
16288                        while (match = matchUrl.exec(propertyValue)) {
16289                            allImgs.push({
16290                                src: match[2],
16291                                element: element[0]
16292                            });
16293                        }
16294                    });
16295
16296                    $.each(hasImageAttributes, function (i, attribute) {
16297                        var attributeValue = element.attr(attribute);
16298                        var attributeValues;
16299
16300                        // If it doesn't contain this property, skip.
16301                        if (!attributeValue) {
16302                            return true;
16303                        }
16304
16305                        // Check for multiple comma separated images
16306                        attributeValues = attributeValue.split(',');
16307
16308                        $.each(attributeValues, function(i, value) {
16309                            // Trim value and get string before first
16310                            // whitespace (for use with srcset).
16311                            value = $.trim(value).split(' ')[0];
16312                            allImgs.push({
16313                                src: value,
16314                                element: element[0]
16315                            });
16316                        });
16317                    });
16318                });
16319            } else {
16320                // For images only, the task is simpler.
16321                obj.find('img:uncached')
16322                    .each(function () {
16323                    allImgs.push({
16324                        src: this.src,
16325                        element: this
16326                    });
16327                });
16328            }
16329
16330            allImgsLength = allImgs.length;
16331            allImgsLoaded = 0;
16332
16333            // If no images found, don't bother.
16334            if (allImgsLength === 0) {
16335                finishedCallback.call(obj[0]);
16336                deferred.resolveWith(obj[0]);
16337            }
16338
16339            $.each(allImgs, function (i, img) {
16340
16341                var image = new Image();
16342                var events =
16343                  'load.' + eventNamespace + ' error.' + eventNamespace;
16344
16345                // Handle the image loading and error with the same callback.
16346                $(image).one(events, function me (event) {
16347                    // If an error occurred with loading the image, set the
16348                    // third argument accordingly.
16349                    var eachArguments = [
16350                        allImgsLoaded,
16351                        allImgsLength,
16352                        event.type == 'load'
16353                    ];
16354                    allImgsLoaded++;
16355
16356                    eachCallback.apply(img.element, eachArguments);
16357                    deferred.notifyWith(img.element, eachArguments);
16358
16359                    // Unbind the event listeners. I use this in addition to
16360                    // `one` as one of those events won't be called (either
16361                    // 'load' or 'error' will be called).
16362                    $(this).off(events, me);
16363
16364                    if (allImgsLoaded == allImgsLength) {
16365                        finishedCallback.call(obj[0]);
16366                        deferred.resolveWith(obj[0]);
16367                        return false;
16368                    }
16369
16370                });
16371
16372                image.src = img.src;
16373            });
16374        });
16375
16376        return deferred.promise();
16377
16378    };
16379}));
16380
16381/**
16382 * @preserve  textfill
16383 * @name      jquery.textfill.js
16384 * @author    Russ Painter
16385 * @author    Yu-Jie Lin
16386 * @author    Alexandre Dantas
16387 * @version   0.6.0
16388 * @date      2014-08-19
16389 * @copyright (c) 2014 Alexandre Dantas
16390 * @copyright (c) 2012-2013 Yu-Jie Lin
16391 * @copyright (c) 2009 Russ Painter
16392 * @license   MIT License
16393 * @homepage  https://github.com/jquery-textfill/jquery-textfill
16394 * @example   http://jquery-textfill.github.io/jquery-textfill/index.html
16395 */
16396; (function($) {
16397
16398	/**
16399	 * Resizes an inner element's font so that the
16400	 * inner element completely fills the outer element.
16401	 *
16402	 * @param {Object} options User options that take
16403	 *                         higher precedence when
16404	 *                         merging with the default ones.
16405	 *
16406	 * @return All outer elements processed
16407	 */
16408	$.fn.textfill = function(options) {
16409
16410		// ______  _______ _______ _______ _     _        _______ _______
16411		// |     \ |______ |______ |_____| |     | |         |    |______
16412		// |_____/ |______ |       |     | |_____| |_____    |    ______|
16413        //
16414		// Merging user options with the default values
16415
16416		var defaults = {
16417			debug            : false,
16418			maxFontPixels    : 40,
16419			minFontPixels    : 4,
16420			innerTag         : 'span',
16421			widthOnly        : false,
16422			success          : null, // callback when a resizing is done
16423			callback         : null, // callback when a resizing is done (deprecated, use success)
16424			fail             : null, // callback when a resizing is failed
16425			complete         : null, // callback when all is done
16426			explicitWidth    : null,
16427			explicitHeight   : null,
16428			changeLineHeight : false
16429		};
16430
16431		var Opts = $.extend(defaults, options);
16432
16433		// _______ _     _ __   _ _______ _______ _____  _____  __   _ _______
16434		// |______ |     | | \  | |          |      |   |     | | \  | |______
16435		// |       |_____| |  \_| |_____     |    __|__ |_____| |  \_| ______|
16436		//
16437		// Predefining the awesomeness
16438
16439		// Output arguments to the Debug console
16440		// if "Debug Mode" is enabled
16441		function _debug() {
16442			if (!Opts.debug
16443				||  typeof console       == 'undefined'
16444				||  typeof console.debug == 'undefined') {
16445				return;
16446			}
16447			console.debug.apply(console, arguments);
16448		}
16449
16450		// Output arguments to the Warning console
16451		function _warn() {
16452			if (typeof console      == 'undefined' ||
16453				typeof console.warn == 'undefined') {
16454				return;
16455			}
16456			console.warn.apply(console, arguments);
16457		}
16458
16459		// Outputs all information on the current sizing
16460		// of the font.
16461		function _debug_sizing(prefix, ourText, maxHeight, maxWidth, minFontPixels, maxFontPixels) {
16462
16463			function _m(v1, v2) {
16464
16465				var marker = ' / ';
16466
16467				if (v1 > v2)
16468					marker = ' > ';
16469
16470				else if (v1 == v2)
16471					marker = ' = ';
16472
16473				return marker;
16474			}
16475
16476			_debug(
16477				'[TextFill] '  + prefix + ' { ' +
16478				'font-size: ' + ourText.css('font-size') + ',' +
16479				'Height: '    + ourText.height() + 'px ' + _m(ourText.height(), maxHeight) + maxHeight + 'px,' +
16480				'Width: '     + ourText.width()  + _m(ourText.width() , maxWidth)  + maxWidth + ',' +
16481				'minFontPixels: ' + minFontPixels + 'px, ' +
16482				'maxFontPixels: ' + maxFontPixels + 'px }'
16483			);
16484		}
16485
16486		/**
16487		 * Calculates which size the font can get resized,
16488		 * according to constrains.
16489		 *
16490		 * @param {String} prefix Gets shown on the console before
16491		 *                        all the arguments, if debug mode is on.
16492		 * @param {Object} ourText The DOM element to resize,
16493		 *                         that contains the text.
16494		 * @param {function} func Function called on `ourText` that's
16495		 *                        used to compare with `max`.
16496		 * @param {number} max Maximum value, that gets compared with
16497		 *                     `func` called on `ourText`.
16498		 * @param {number} minFontPixels Minimum value the font can
16499		 *                               get resized to (in pixels).
16500		 * @param {number} maxFontPixels Maximum value the font can
16501		 *                               get resized to (in pixels).
16502		 *
16503		 * @return Size (in pixels) that the font can be resized.
16504		 */
16505		function _sizing(prefix, ourText, func, max, maxHeight, maxWidth, minFontPixels, maxFontPixels) {
16506
16507			_debug_sizing(
16508				prefix, ourText,
16509				maxHeight, maxWidth,
16510				minFontPixels, maxFontPixels
16511			);
16512
16513			// The kernel of the whole plugin, take most attention
16514			// on this part.
16515			//
16516			// This is a loop that keeps increasing the `font-size`
16517			// until it fits the parent element.
16518			//
16519			// - Start from the minimal allowed value (`minFontPixels`)
16520			// - Guesses an average font size (in pixels) for the font,
16521			// - Resizes the text and sees if its size is within the
16522			//   boundaries (`minFontPixels` and `maxFontPixels`).
16523			//   - If so, keep guessing until we break.
16524			//   - If not, return the last calculated size.
16525			//
16526			// I understand this is not optimized and we should
16527			// consider implementing something akin to
16528			// Daniel Hoffmann's answer here:
16529			//
16530			//     http://stackoverflow.com/a/17433451/1094964
16531			//
16532
16533			while (minFontPixels < (maxFontPixels - 1)) {
16534
16535				var fontSize = Math.floor((minFontPixels + maxFontPixels) / 2);
16536				ourText.css('font-size', fontSize);
16537
16538				if (func.call(ourText) <= max) {
16539					minFontPixels = fontSize;
16540
16541					if (func.call(ourText) == max)
16542						break;
16543				}
16544				else
16545					maxFontPixels = fontSize;
16546
16547				_debug_sizing(
16548					prefix, ourText,
16549					maxHeight, maxWidth,
16550					minFontPixels, maxFontPixels
16551				);
16552			}
16553
16554			ourText.css('font-size', maxFontPixels);
16555
16556			if (func.call(ourText) <= max) {
16557				minFontPixels = maxFontPixels;
16558
16559				_debug_sizing(
16560					prefix + '* ', ourText,
16561					maxHeight, maxWidth,
16562					minFontPixels, maxFontPixels
16563				);
16564			}
16565			return minFontPixels;
16566		}
16567
16568		// _______ _______ _______  ______ _______
16569		// |______    |    |_____| |_____/    |
16570		// ______|    |    |     | |    \_    |
16571        //
16572		// Let's get it started (yeah)!
16573
16574		_debug('[TextFill] Start Debug');
16575
16576		this.each(function() {
16577
16578			// Contains the child element we will resize.
16579			// $(this) means the parent container
16580			var ourText = $(Opts.innerTag + ':visible:first', this);
16581
16582			// Will resize to this dimensions.
16583			// Use explicit dimensions when specified
16584			var maxHeight = Opts.explicitHeight || $(this).height();
16585			var maxWidth  = Opts.explicitWidth  || $(this).width();
16586
16587			var oldFontSize = ourText.css('font-size');
16588
16589			var lineHeight  = parseFloat(ourText.css('line-height')) / parseFloat(oldFontSize);
16590
16591			_debug('[TextFill] Inner text: ' + ourText.text());
16592			_debug('[TextFill] All options: ', Opts);
16593			_debug('[TextFill] Maximum sizes: { ' +
16594				   'Height: ' + maxHeight + 'px, ' +
16595				   'Width: '  + maxWidth  + 'px' + ' }'
16596				  );
16597
16598			var minFontPixels = Opts.minFontPixels;
16599
16600			// Remember, if this `maxFontPixels` is negative,
16601			// the text will resize to as long as the container
16602			// can accomodate
16603			var maxFontPixels = (Opts.maxFontPixels <= 0 ?
16604								 maxHeight :
16605								 Opts.maxFontPixels);
16606
16607
16608			// Let's start it all!
16609
16610			// 1. Calculate which `font-size` would
16611			//    be best for the Height
16612			var fontSizeHeight = undefined;
16613
16614			if (! Opts.widthOnly)
16615				fontSizeHeight = _sizing(
16616					'Height', ourText,
16617					$.fn.height, maxHeight,
16618					maxHeight, maxWidth,
16619					minFontPixels, maxFontPixels
16620				);
16621
16622			// 2. Calculate which `font-size` would
16623			//    be best for the Width
16624			var fontSizeWidth = undefined;
16625
16626			fontSizeWidth = _sizing(
16627				'Width', ourText,
16628				$.fn.width, maxWidth,
16629				maxHeight, maxWidth,
16630				minFontPixels, maxFontPixels
16631			);
16632
16633			// 3. Actually resize the text!
16634
16635			if (Opts.widthOnly) {
16636				ourText.css({
16637					'font-size'  : fontSizeWidth,
16638					'white-space': 'nowrap'
16639				});
16640
16641				if (Opts.changeLineHeight)
16642					ourText.parent().css(
16643						'line-height',
16644						(lineHeight * fontSizeWidth + 'px')
16645					);
16646			}
16647			else {
16648				var fontSizeFinal = Math.min(fontSizeHeight, fontSizeWidth);
16649
16650				ourText.css('font-size', fontSizeFinal);
16651
16652				if (Opts.changeLineHeight)
16653					ourText.parent().css(
16654						'line-height',
16655						(lineHeight * fontSizeFinal) + 'px'
16656					);
16657			}
16658
16659			_debug(
16660				'[TextFill] Finished { ' +
16661				'Old font-size: ' + oldFontSize              + ', ' +
16662				'New font-size: ' + ourText.css('font-size') + ' }'
16663			);
16664
16665			// Oops, something wrong happened!
16666			// We weren't supposed to exceed the original size
16667			if ((ourText.width()  > maxWidth) ||
16668				(ourText.height() > maxHeight && !Opts.widthOnly)) {
16669
16670				ourText.css('font-size', oldFontSize);
16671
16672				// Failure callback
16673				if (Opts.fail)
16674					Opts.fail(this);
16675
16676				_debug(
16677					'[TextFill] Failure { ' +
16678					'Current Width: '  + ourText.width()  + ', ' +
16679					'Maximum Width: '  + maxWidth         + ', ' +
16680					'Current Height: ' + ourText.height() + ', ' +
16681					'Maximum Height: ' + maxHeight        + ' }'
16682				);
16683			}
16684			else if (Opts.success) {
16685				Opts.success(this);
16686			}
16687			else if (Opts.callback) {
16688				_warn('callback is deprecated, use success, instead');
16689
16690				// Success callback
16691				Opts.callback(this);
16692			}
16693		});
16694
16695		// Complete callback
16696		if (Opts.complete)
16697			Opts.complete(this);
16698
16699		_debug('[TextFill] End Debug');
16700		return this;
16701	};
16702
16703})(window.jQuery);
16704
16705!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;
16706function getStyles(config){
16707  // 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;
16707left: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+"}"
16708};
16709  var ShareUtils;
16710
16711if ((!("classList" in document.documentElement)) && Object.defineProperty && typeof HTMLElement !== "undefined") {
16712  Object.defineProperty(HTMLElement.prototype, "classList", {
16713    get: function() {
16714      var ret, self, update;
16715      update = function(fn) {
16716        return function(value) {
16717          var classes, index;
16718          classes = self.className.split(/\s+/);
16719          index = classes.indexOf(value);
16720          fn(classes, index, value);
16721          self.className = classes.join(" ");
16722        };
16723      };
16724      self = this;
16725      ret = {
16726        add: update(function(classes, index, value) {
16727          ~index || classes.push(value);
16728        }),
16729        remove: update(function(classes, index) {
16730          ~index && classes.splice(index, 1);
16731        }),
16732        toggle: update(function(classes, index, value) {
16733          if (~index) {
16734            classes.splice(index, 1);
16735          } else {
16736            classes.push(value);
16737          }
16738        }),
16739        contains: function(value) {
16740          return !!~self.className.split(/\s+/).indexOf(value);
16741        },
16742        item: function(i) {
16743          return self.className.split(/\s+/)[i] || null;
16744        }
16745      };
16746      Object.defineProperty(ret, "length", {
16747        get: function() {
16748          return self.className.split(/\s+/).length;
16749        }
16750      });
16751      return ret;
16752    }
16753  });
16754}
16755
16756String.prototype.to_rfc3986 = function() {
16757  var tmp;
16758  tmp = encodeURIComponent(this);
16759  return tmp.replace(/[!'()*]/g, function(c) {
16760    return "%" + c.charCodeAt(0).toString(16);
16761  });
16762};
16763
16764ShareUtils = (function() {
16765  function ShareUtils() {}
16766
16767  ShareUtils.prototype.extend = function(to, from, overwrite) {
16768    var hasProp, prop;
16769    for (prop in from) {
16770      hasProp = to[prop] !== undefined;
16771      if (hasProp && typeof from[prop] === "object") {
16772        this.extend(to[prop], from[prop], overwrite);
16773      } else {
16774        if (overwrite || !hasProp) {
16775          to[prop] = from[prop];
16776        }
16777      }
16778    }
16779  };
16780
16781  ShareUtils.prototype.hide = function(el) {
16782    return el.style.display = "none";
16783  };
16784
16785  ShareUtils.prototype.show = function(el) {
16786    return el.style.display = "block";
16787  };
16788
16789  ShareUtils.prototype.has_class = function(el, class_name) {
16790    return el.classList.contains(class_name);
16791  };
16792
16793  ShareUtils.prototype.add_class = function(el, class_name) {
16794    return el.classList.add(class_name);
16795  };
16796
16797  ShareUtils.prototype.remove_class = function(el, class_name) {
16798    return el.classList.remove(class_name);
16799  };
16800
16801  ShareUtils.prototype.is_encoded = function(str) {
16802    str = str.to_rfc3986();
16803    return decodeURIComponent(str) !== str;
16804  };
16805
16806  ShareUtils.prototype.encode = function(str) {
16807    if (typeof str === "undefined" || this.is_encoded(str)) {
16808      return str;
16809    } else {
16810      return str.to_rfc3986();
16811    }
16812  };
16813
16814  ShareUtils.prototype.popup = function(url, params) {
16815    var k, popup, qs, v;
16816    if (params == null) {
16817      params = {};
16818    }
16819    popup = {
16820      width: 500,
16821      height: 350
16822    };
16823    popup.top = (screen.height / 2) - (popup.height / 2);
16824    popup.left = (screen.width / 2) - (popup.width / 2);
16825    qs = ((function() {
16826      var _results;
16827      _results = [];
16828      for (k in params) {
16829        v = params[k];
16830        _results.push("" + k + "=" + (this.encode(v)));
16831      }
16832      return _results;
16833    }).call(this)).join('&');
16834    if (qs) {
16835      qs = "?" + qs;
16836    }
16837    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=" 
16837+ popup.width + ",height=" + popup.height);
16838  };
16839
16840  return ShareUtils;
16841
16842})();
16843var Share,
16844  __hasProp = {}.hasOwnProperty,
16845  __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; };
16846
16847Share = (function(_super) {
16848  __extends(Share, _super);
16849
16850  function Share(element, options) {
16851    this.element = element;
16852    this.el = {
16853      head: document.getElementsByTagName('head')[0],
16854      body: document.getElementsByTagName('body')[0]
16855    };
16856    this.config = {
16857      enabled_networks: 0,
16858      protocol: ['http', 'https'].indexOf(window.location.href.split(':')[0]) === -1 ? 'https://' : '//',
16859      url: window.location.href.replace("&", "%26"),
16860      caption: null,
16861      title: this.default_title(),
16862      image: this.default_image(),
16863      description: this.default_description(),
16864      ui: {
16865        flyout: 'top center',
16866        button_text: 'Share',
16867        button_font: true,
16868        icon_font: true
16869      },
16870      networks: {
16871        google_plus: {
16872          enabled: true,
16873          url: null
16874        },
16875        twitter: {
16876          enabled: true,
16877          url: null,
16878          title: null,
16879          description: null
16880        },
16881        facebook: {
16882          enabled: true,
16883          load_sdk: true,
16884          url: null,
16885          app_id: null,
16886          title: null,
16887          caption: null,
16888          description: null,
16889          image: null
16890        },
16891        pinterest: {
16892          enabled: true,
16893          url: null,
16894          image: null,
16895          description: null
16896        },
16897        reddit: {
16898          enabled: true,
16899          url: null,
16900          title: null
16901        },
16902        linkedin: {
16903          enabled: true,
16904          url: null,
16905          title: null,
16906          description: null
16907        },
16908        xing: {
16909          enabled: true,
16910          url: null,
16911          title: null,
16912          image: null,
16913          description: null
16914        },
16915        whatsapp: {
16916          enabled: true,
16917          description: null,
16918          url: null
16919        },
16920        email: {
16921          enabled: true,
16922          title: null,
16923          description: null,
16924          url: null
16925        }
16926      }
16927    };
16928    this.setup(element, options);
16929    return this;
16930  }
16931
16932  Share.prototype.setup = function(element, opts) {
16933    var index, instance, instances, _i, _len;
16934    instances = document.querySelectorAll(element);
16935    this.extend(this.config, opts, true);
16936    this.set_global_configuration();
16937    this.normalize_network_configuration();
16938    if (this.config.ui.icon_font) {
16939      this.inject_icons();
16940    }
16941    if (this.config.ui.button_font) {
16942      this.inject_fonts();
16943    }
16944    if (this.config.networks.facebook.enabled && this.config.networks.facebook.load_sdk) {
16945      this.inject_facebook_sdk();
16946    }
16947    for (index = _i = 0, _len = instances.length; _i < _len; index = ++_i) {
16948      instance = instances[index];
16949      this.setup_instance(element, index);
16950    }
16951  };
16952
16953  Share.prototype.setup_instance = function(element, index) {
16954    var button, instance, label, network, networks, _i, _len, _results,
16955      _this = this;
16956    instance = document.querySelectorAll(element)[index];
16957    this.hide(instance);
16958    this.add_class(instance, "sharer-" + index);
16959    instance = document.querySelectorAll(element)[index];
16960    this.inject_css(instance);
16961    this.inject_html(instance);
16962    this.show(instance);
16963    label = instance.getElementsByTagName("label")[0];
16964    button = instance.getElementsByClassName("social")[0];
16965    networks = instance.getElementsByTagName('li');
16966    this.add_class(button, "networks-" + this.config.enabled_networks);
16967    label.addEventListener("click", function() {
16968      return _this.event_toggle(button);
16969    });
16970    _this = this;
16971    _results = [];
16972    for (index = _i = 0, _len = networks.length; _i < _len; index = ++_i) {
16973      network = networks[index];
16974      _results.push(network.addEventListener("click", function() {
16975        _this.event_network(instance, this);
16976        return _this.event_close(button);
16977      }));
16978    }
16979    return _results;
16980  };
16981
16982  Share.prototype.event_toggle = function(button) {
16983    if (this.has_class(button, "active")) {
16984      return this.event_close(button);
16985    } else {
16986      return this.event_open(button);
16987    }
16988  };
16989
16990  Share.prototype.event_open = function(button) {
16991    if (this.has_class(button, "load")) {
16992      this.remove_class(button, "load");
16993    }
16994    return this.add_class(button, "active");
16995  };
16996
16997  Share.prototype.event_close = function(button) {
16998    return this.remove_class(button, "active");
16999  };
17000
17001  Share.prototype.event_network = function(instance, network) {
17002    var name;
17003    name = network.getAttribute("data-network");
17004    this.hook("before", name, instance);
17005    this["network_" + name]();
17006    return this.hook("after", name, instance);
17007  };
17008
17009  Share.prototype.open = function() {
17010    return this["public"]("open");
17011  };
17012
17013  Share.prototype.close = function() {
17014    return this["public"]("close");
17015  };
17016
17017  Share.prototype.toggle = function() {
17018    return this["public"]("toggle");
17019  };
17020
17021  Share.prototype["public"] = function(action) {
17022    var button, index, instance, _i, _len, _ref, _results;
17023    _ref = document.querySelectorAll(this.element);
17024    _results = [];
17025    for (index = _i = 0, _len = _ref.length; _i < _len; index = ++_i) {
17026      instance = _ref[index];
17027      button = instance.getElementsByClassName("social")[0];
17028      _results.push(this["event_" + action](button));
17029    }
17030    return _results;
17031  };
17032
17033  Share.prototype.network_facebook = function() {
17034    if (this.config.networks.facebook.load_sdk) {
17035      if (!window.FB) {
17036        return console.error("The Facebook JS SDK hasn't loaded yet.");
17037      }
17038      return FB.ui({
17039        method: 'feed',
17040        name: this.config.networks.facebook.title,
17041        link: this.config.networks.facebook.url,
17042        picture: this.config.networks.facebook.image,
17043        caption: this.config.networks.facebook.caption,
17044        description: this.config.networks.facebook.description
17045      });
17046    } else {
17047      return this.popup('https://www.facebook.com/sharer/sharer.php', {
17048        u: this.config.networks.facebook.url
17049      });
17050    }
17051  };
17052
17053  Share.prototype.network_twitter = function() {
17054    return this.popup('https://twitter.com/intent/tweet', {
17055      text: this.config.networks.twitter.title,
17056      url: this.config.networks.twitter.url
17057    });
17058  };
17059
17060  Share.prototype.network_google_plus = function() {
17061    return this.popup('https://plus.google.com/share', {
17062      url: this.config.networks.google_plus.url
17063    });
17064  };
17065
17066  Share.prototype.network_pinterest = function() {
17067    return this.popup('https://www.pinterest.com/pin/create/button', {
17068      url: this.config.networks.pinterest.url,
17069      media: this.config.networks.pinterest.image,
17070      description: this.config.networks.pinterest.description
17071    });
17072  };
17073
17074  Share.prototype.network_linkedin = function() {
17075    return this.popup('https://www.linkedin.com/shareArticle', {
17076      url: this.config.networks.linkedin.url,
17077      title: this.config.networks.linkedin.title,
17078      summary: this.config.networks.linkedin.description
17079    });
17080  }
17081
17082  Share.prototype.network_xing = function() {
17083    return this.popup('https://www.xing.com/spi/shares/new', {
17084      url: this.config.networks.xing.url,
17085      image: this.config.networks.xing.image,
17086      title: this.config.networks.xing.title,
17087      summary: this.config.networks.xing.description
17088    });
17089  }
17090
17091  Share.prototype.network_email = function() {
17092    return this.popup('mailto:', {
17093      subject: this.config.networks.email.title,
17094      body: this.config.networks.email.url + '%0A%0A' + this.config.networks.email.description,
17095    });
17096  };
17097
17098  Share.prototype.inject_icons = function() {
17099  //  return this.inject_stylesheet("https://www.sharebutton.co/fonts/v2/entypo.min.css");
17100  };
17101
17102  Share.prototype.inject_fonts = function() {
17103   // return this.inject_stylesheet("http://fonts.googleapis.com/css?family=Lato:900&text=" + this.config.ui.button_text);
17104  };
17105
17106  Share.prototype.inject_stylesheet = function(url) {
17107    var link;
17108    if (!this.el.head.querySelector("link[href=\"" + url + "\"]")) {
17109      link = document.createElement("link");
17110      link.setAttribute("rel", "stylesheet");
17111      link.setAttribute("href", url);
17112      return this.el.head.appendChild(link);
17113    }
17114  };
17115
17116  Share.prototype.inject_css = function(instance) {
17117    var css, meta, selector, style;
17118    selector = "." + (instance.getAttribute('class').split(" ").join("."));
17119    if (!this.el.head.querySelector("meta[name='sharer" + selector + "']")) {
17120      this.config.selector = selector;
17121      css = getStyles(this.config);
17122      style = document.createElement("style");
17123      style.type = "text/css";
17124      if (style.styleSheet) {
17125        style.styleSheet.cssText = css;
17126      } else {
17127        style.appendChild(document.createTextNode(css));
17128      }
17129      this.el.head.appendChild(style);
17130      delete this.config.selector;
17131      meta = document.createElement("meta");
17132      meta.setAttribute("name", "sharer" + selector);
17133      return this.el.head.appendChild(meta);
17134    }
17135  };
17136
17137  Share.prototype.inject_html = function(instance) {
17138    //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>";
17139    return instance.innerHTML = "<label class='social-export'><span>" + this.config.ui.button_text + "</span></label><div class='social load " + this.config.ui.flyout + "'><ul><li class='social-facebook' data-network='facebook' tabindex='0'></li><li class='social-twitter' data-network='twitter' tabindex='0'></li><li class='social-pinterest' data-network='pinterest' tabindex='0'></li><li class='social-linkedin' data-network='linkedin' tabindex='0'></li><li class='social-xing' data-network='xing' tabindex='0'></li><li class='social-paper-plane' data-network='email' tabindex='0'></li></ul></div>";
17140  };
17141
17142  Share.prototype.inject_facebook_sdk = function() {
17143    var fb_root, script;
17144    if (!window.FB && this.config.networks.facebook.app_id && !this.el.body.querySelector('#fb-root')) {
17145      script = document.createElement("script");
17146      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')";
17147      fb_root = document.createElement("div");
17148      fb_root.id = "fb-root";
17149      this.el.body.appendChild(fb_root);
17150      return this.el.body.appendChild(script);
17151    }
17152  };
17153
17154  Share.prototype.hook = function(type, network, instance) {
17155    var fn, opts;
17156    fn = this.config.networks[network][type];
17157    if (typeof fn === "function") {
17158      opts = fn.call(this.config.networks[network], instance);
17159      if (opts !== void 0) {
17160        opts = this.normalize_filter_config_updates(opts);
17161        this.extend(this.config.networks[network], opts, true);
17162        this.normalize_network_configuration();
17163      }
17164    }
17165  };
17166
17167  Share.prototype.default_title = function() {
17168    var content;
17169    if (content = document.querySelector('meta[property="og:title"]') || document.querySelector('meta[name="twitter:title"]')) {
17170      return encodeURIComponent(content.getAttribute('content'));
17171    } else if (content = document.querySelector('title')) {
17172      return encodeURIComponent(content.innerText);
17173    }
17174  };
17175
17176  Share.prototype.default_image = function() {
17177    var content;
17178    if (content = document.querySelector('meta[property="og:image"]') || document.querySelector('meta[name="twitter:image"]')) {
17179      return content.getAttribute('content');
17180    }
17181  };
17182
17183  Share.prototype.default_description = function() {
17184    var content;
17185    if (content = document.querySelector('meta[property="og:description"]') || document.querySelector('meta[name="twitter:description"]') || document.querySelector('meta[name="description"]')) {
17186      return encodeURIComponent(content.getAttribute('content'));
17187    } else {
17188      return '';
17189    }
17190  };
17191
17192  Share.prototype.set_global_configuration = function() {
17193    var display, network, option, options, _ref, _results;
17194    _ref = this.config.networks;
17195    _results = [];
17196    for (network in _ref) {
17197      options = _ref[network];
17198      for (option in options) {
17199        if (this.config.networks[network][option] == null) {
17200          this.config.networks[network][option] = this.config[option];
17201        }
17202      }
17203      if (this.config.networks[network].enabled) {
17204        display = 'block';
17205        this.config.enabled_networks += 1;
17206      } else {
17207        display = 'none';
17208      }
17209      _results.push(this.config.networks[network].display = display);
17210    }
17211    return _results;
17212  };
17213
17214  Share.prototype.normalize_network_configuration = function() {
17215    if (!this.config.networks.facebook.app_id) {
17216      this.config.networks.facebook.load_sdk = false;
17217    }
17218    if (!this.is_encoded(this.config.networks.twitter.description)) {
17219      this.config.networks.twitter.description = encodeURIComponent(this.config.networks.twitter.description);
17220    }
17221    if (typeof this.config.networks.facebook.app_id === 'number') {
17222      return this.config.networks.facebook.app_id = this.config.networks.facebook.app_id.toString();
17223    }
17224  };
17225
17226  Share.prototype.normalize_filter_config_updates = function(opts) {
17227    if (this.config.networks.facebook.app_id !== opts.app_id) {
17228      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.");
17229      delete opts.app_id;
17230    }
17231    if (this.config.networks.facebook.load_sdk !== opts.load_sdk) {
17232      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.");
17233      delete opts.app_id;
17234    }
17235    return opts;
17236  };
17237
17238  return Share;
17239
17240})(ShareUtils);
17241 return Share;
17242});
17243
17244// Generated by CoffeeScript 1.9.2
17245
17246// @license Sticky-kit v1.1.2 | WTFPL | Leaf Corcoran 2015 | http://leafo.net
17247
17248(function() {
17249  var $, win;
17250
17251  $ = this.jQuery || window.jQuery;
17252
17253  win = $(window);
17254
17255  $.fn.stick_in_parent = function(opts) {
17256    var doc, elm, enable_bottoming, fn, i, inner_scrolling, len, manual_spacer, offset_top, outer_width, parent_selector, recalc_every, sticky_class;
17257    if (opts == null) {
17258      opts = {};
17259    }
17260    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;
17261    if (offset_top == null) {
17262      offset_top = 0;
17263    }
17264    if (parent_selector == null) {
17265      parent_selector = void 0;
17266    }
17267    if (inner_scrolling == null) {
17268      inner_scrolling = true;
17269    }
17270    if (sticky_class == null) {
17271      sticky_class = "is_stuck";
17272    }
17273    doc = $(document);
17274    if (enable_bottoming == null) {
17275      enable_bottoming = true;
17276    }
17277    outer_width = function(el) {
17278      var _el, computed, w;
17279      if (window.getComputedStyle) {
17280        _el = el[0];
17281        computed = window.getComputedStyle(el[0]);
17282        w = parseFloat(computed.getPropertyValue("width")) + parseFloat(computed.getPropertyValue("margin-left")) + parseFloat(computed.getPropertyValue("margin-right"));
17283        if (computed.getPropertyValue("box-sizing") !== "border-box") {
17284          w += parseFloat(computed.getPropertyValue("border-left-width")) + parseFloat(computed.getPropertyValue("border-right-width")) + parseFloat(computed.getPropertyValue("padding-left")) + parseFloat(computed.getPropertyValue("padding-right"));
17285        }
17286        return w;
17287      } else {
17288        return el.outerWidth(true);
17289      }
17290    };
17291    fn = function(elm, padding_bottom, parent_top, parent_height, top, height, el_float, detached) {
17292      var bottomed, detach, fixed, last_pos, last_scroll_height, offset, parent, recalc, recalc_and_tick, recalc_counter, spacer, tick;
17293      if (elm.data("sticky_kit")) {
17294        return;
17295      }
17296      elm.data("sticky_kit", true);
17297      last_scroll_height = doc.height();
17298      parent = elm.parent();
17299      if (parent_selector != null) {
17300        parent = parent.closest(parent_selector);
17301      }
17302      if (!parent.length) {
17303        throw "failed to find stick parent";
17304      }
17305      fixed = false;
17306      bottomed = false;
17307      spacer = manual_spacer != null ? manual_spacer && elm.closest(manual_spacer) : $("<div />");
17308      if (spacer) {
17309        spacer.css('position', elm.css('position'));
17310      }
17311      recalc = function() {
17312        var border_top, padding_top, restore;
17313        if (detached) {
17314          return;
17315        }
17316        last_scroll_height = doc.height();
17317        border_top = parseInt(parent.css("border-top-width"), 10);
17318        padding_top = parseInt(parent.css("padding-top"), 10);
17319        padding_bottom = parseInt(parent.css("padding-bottom"), 10);
17320        parent_top = parent.offset().top + border_top + padding_top;
17321        parent_height = parent.height();
17322        if (fixed) {
17323          fixed = false;
vendor: 6,068 bytes, lines 17324-17518
17324          bottomed = false;
17325          if (manual_spacer == null) {
17326            elm.insertAfter(spacer);
17327            spacer.detach();
17328          }
17329          elm.css({
17330            position: "",
17331            top: "",
17332            width: "",
17333            bottom: ""
17334          }).removeClass(sticky_class);
17335          restore = true;
17336        }
17337        top = elm.offset().top - (parseInt(elm.css("margin-top"), 10) || 0) - offset_top;
17338        height = elm.outerHeight(true);
17339        el_float = elm.css("float");
17340        if (spacer) {
17341          spacer.css({
17342            width: outer_width(elm),
17343            height: height,
17344            display: elm.css("display"),
17345            "vertical-align": elm.css("vertical-align"),
17346            "float": el_float
17347          });
17348        }
17349        if (restore) {
17350          return tick();
17351        }
17352      };
17353      recalc();
17354      if (height === parent_height) {
17355        return;
17356      }
17357      last_pos = void 0;
17358      offset = offset_top;
17359      recalc_counter = recalc_every;
17360      tick = function() {
17361        var css, delta, recalced, scroll, will_bottom, win_height;
17362        if (detached) {
17363          return;
17364        }
17365        recalced = false;
17366        if (recalc_counter != null) {
17367          recalc_counter -= 1;
17368          if (recalc_counter <= 0) {
17369            recalc_counter = recalc_every;
17370            recalc();
17371            recalced = true;
17372          }
17373        }
17374        if (!recalced && doc.height() !== last_scroll_height) {
17375          recalc();
17376          recalced = true;
17377        }
17378        scroll = win.scrollTop();
17379        if (last_pos != null) {
17380          delta = scroll - last_pos;
17381        }
17382        last_pos = scroll;
17383        if (fixed) {
17384          if (enable_bottoming) {
17385            will_bottom = scroll + height + offset > parent_height + parent_top;
17386            if (bottomed && !will_bottom) {
17387              bottomed = false;
17388              elm.css({
17389                position: "fixed",
17390                bottom: "",
17391                top: offset
17392              }).trigger("sticky_kit:unbottom");
17393            }
17394          }
17395          if (scroll < top) {
17396            fixed = false;
17397            offset = offset_top;
17398            if (manual_spacer == null) {
17399              if (el_float === "left" || el_float === "right") {
17400                elm.insertAfter(spacer);
17401              }
17402              spacer.detach();
17403            }
17404            css = {
17405              position: "",
17406              width: "",
17407              top: ""
17408            };
17409            elm.css(css).removeClass(sticky_class).trigger("sticky_kit:unstick");
17410          }
17411          if (inner_scrolling) {
17412            win_height = win.height();
17413            if (height + offset_top > win_height) {
17414              if (!bottomed) {
17415                offset -= delta;
17416                offset = Math.max(win_height - height, offset);
17417                offset = Math.min(offset_top, offset);
17418                if (fixed) {
17419                  elm.css({
17420                    top: offset + "px"
17421                  });
17422                }
17423              }
17424            }
17425          }
17426        } else {
17427          if (scroll > top) {
17428            fixed = true;
17429            css = {
17430              position: "fixed",
17431              top: offset
17432            };
17433            css.width = elm.css("box-sizing") === "border-box" ? elm.outerWidth() + "px" : elm.width() + "px";
17434            elm.css(css).addClass(sticky_class);
17435            if (manual_spacer == null) {
17436              elm.after(spacer);
17437              if (el_float === "left" || el_float === "right") {
17438                spacer.append(elm);
17439              }
17440            }
17441            elm.trigger("sticky_kit:stick");
17442          }
17443        }
17444        if (fixed && enable_bottoming) {
17445          if (will_bottom == null) {
17446            will_bottom = scroll + height + offset > parent_height + parent_top;
17447          }
17448          if (!bottomed && will_bottom) {
17449            bottomed = true;
17450            if (parent.css("position") === "static") {
17451              parent.css({
17452                position: "relative"
17453              });
17454            }
17455            return elm.css({
17456              position: "absolute",
17457              bottom: padding_bottom,
17458              top: "auto"
17459            }).trigger("sticky_kit:bottom");
17460          }
17461        }
17462      };
17463      recalc_and_tick = function() {
17464        recalc();
17465        return tick();
17466      };
17467      detach = function() {
17468        detached = true;
17469        win.off("touchmove", tick);
17470        win.off("scroll", tick);
17471        win.off("resize", recalc_and_tick);
17472        $(document.body).off("sticky_kit:recalc", recalc_and_tick);
17473        elm.off("sticky_kit:detach", detach);
17474        elm.removeData("sticky_kit");
17475        elm.css({
17476          position: "",
17477          bottom: "",
17478          top: "",
17479          width: ""
17480        });
17481        parent.position("position", "");
17482        if (fixed) {
17483          if (manual_spacer == null) {
17484            if (el_float === "left" || el_float === "right") {
17485              elm.insertAfter(spacer);
17486            }
17487            spacer.remove();
17488          }
17489          return elm.removeClass(sticky_class);
17490        }
17491      };
17492      win.on("touchmove", tick);
17493      win.on("scroll", tick);
17494      win.on("resize", recalc_and_tick);
17495      $(document.body).on("sticky_kit:recalc", recalc_and_tick);
17496      elm.on("sticky_kit:detach", detach);
17497      return setTimeout(tick, 0);
17498    };
17499    for (i = 0, len = this.length; i < len; i++) {
17500      elm = this[i];
17501      fn($(elm));
17502    }
17503    return this;
17504  };
17505
17506}).call(this);
17507/* ==========================================================================
17508 * bootstrap-tab-history.js
17509 * Author: Michael Narayan <[email protected]>
17510 * Repository: https://github.com/mnarayan01/bootstrap-tab-history/
17511 * ==========================================================================
17512 * Licensed under the Apache License, Version 2.0 (the "License"); you may
17513 * not use this file except in compliance with the License. You may obtain a
17514 * copy of the License at
17515 *
17516 * http://www.apache.org/licenses/LICENSE-2.0
17517 *
17518 * Unless required by applicable law or agreed to in writing, softw
17518are
17519 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
17520 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17521 * License for the specific language governing permissions and limitations
17522 * under the License.
17523 * ========================================================================== */
17524
17525/* ========================================================================== */
17526/* JSHint directives                                                          */
17527/*                                                                            */
17528/* global BootstrapTabHistory: true                                           */
17529/*                                                                            */
17530/* global document                                                            */
17531/* global jQuery                                                              */
17532/* global history                                                             */
17533/* global window                                                              */
17534/* ========================================================================== */
17535
17536/**
17537 * Integrate [HTML5 history state tracking](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history)
17538 * with [`bootstrap/tab.js`](http://getbootstrap.com/javascript/#tabs). To enable tracking on a tab element, simply set
17539 * the `data-tab-history` attribute to true (or a string denoting a tab grouping).
17540 *
17541 * See the README for additional information.
17542 *
17543 * Functionality based upon bootstrap/tab.js v3.1.0; reference it when making any changes.
17544 */
17545
17546BootstrapTabHistory = {
17547  options: {
17548    /**
17549     * When the anchor portion of the URI is used to activate a tab, scroll down to the given offset, rather than the
17550     * element with the given `id` attribute. Set to null to disable. Only relevant if showTabsBasedOnAnchor is true.
17551     *
17552     * May be overriden on a per-element basis by the attribute `data-tab-history-anchor-y-offset`.
17553     *
17554     * @public
17555     * @type {?number}
17556     */
17557    defaultAnchorYOffset: 0,
17558    /**
17559     * Either 'push' or 'replace', for whether to use `history.pushState` or `history.replaceState`, resp.
17560     *
17561     * May be overriden on a per-element basis by the attribute `data-tab-history-changer`.
17562     *
17563     * @public
17564     * @type {string}
17565     */
17566    defaultChanger: 'replace',
17567    /**
17568     * If true, update the URL in onShownTab in the calls to `history.pushState` and `history.replaceState`. Otherwise,
17569     * `null` is passed as the third parameter to these calls.
17570     *
17571     * May be overriden on a per-element basis by the attribute `data-tab-history-update-url`.
17572     *
17573     * @public
17574     * @type {boolean}
17575     */
17576    defaultUpdateURL: false,
17577    /**
17578     * Should the anchor portion of the loaded URI be used to activate a single tab if no history was present on page
17579     * load.
17580     *
17581     * @public
17582     * @type {boolean}
17583     */
17584    showTabsBasedOnAnchor: true
17585  }
17586};
17587
17588(function () {
17589  'use strict';
17590
17591  jQuery(function () {
17592    if(history && history.pushState && history.replaceState) {
17593      var bootstrapTabHistory = history.state && history.state.bootstrapTabHistory;
17594
17595      if(bootstrapTabHistory) {
17596        showTabsBasedOnState(bootstrapTabHistory);
17597      } else {
17598        showTabsBasedOnAnchor();
17599      }
17600
17601      backfillHistoryState();
17602
17603      jQuery(document).on('shown.bs.tab show.bs.collapse', onShownTab);
17604      jQuery(window).on('popstate', onPopState);
17605    } else {
17606      showTabsBasedOnAnchor();
17607    }
17608  });
17609
17610  /**
17611   * Used to prevent onShownTab from registering shown events that we triggered via showTabsBasedOnState.
17612   *
17613   * @type {boolean}
17614   */
17615  var showingTabsBasedOnState = false;
17616
17617  /**
17618   * Used to update `history.state` to reflect the default active tabs on initial page load. This supports proper
17619   * `history.back` handling when `data-tab-history-update-url` is true.
17620   */
17621  function backfillHistoryState() {
17622    var newState = null;
17623
17624    jQuery('li.active > [data-tab-history], .panel-title.active [data-tab-history]').each(function () {//edited by Uncode
17625      var $activeTabElement = jQuery(this);
17626      var selector = getTabSelector($activeTabElement);
17627
17628      if(selector) {
17629        var tabGroup = getTabGroup($activeTabElement);
17630
17631        if(tabGroup) {
17632          newState = createNewHistoryState(newState || history.state, tabGroup, selector);
17633        }
17634      }
17635    });
17636
17637    if(newState) {
17638      history.replaceState(newState, '', null);
17639    }
17640  }
17641
17642  /**
17643   * Clone the existing state, ensure its bootstrapTabHistory attribute is an Object, and add the provided tabGroup to
17644   * said bootstrapTabHistory attribute.
17645   *
17646   * @param {?object} existingState
17647   * @param {!string} tabGroup
17648   * @param {!string} selector
17649   * @returns {!object}
17650   */
17651  function createNewHistoryState(existingState, tabGroup, selector) {
17652    // Clone history.state and ensure it has a bootstrapTabHistory entry.
17653    var newState = jQuery.extend(true, {}, existingState, {
17654      bootstrapTabHistory: {}
17655    });
17656
17657    newState.bootstrapTabHistory[tabGroup] = selector;
17658
17659    return newState;
17660  }
17661
17662  /**
17663   * @param {jQuery} $tab
17664   * @returns {?string}
17665   */
17666  function getTabGroup($tab) {
17667    return parseTruthyAttributeValue($tab.data('tab-history'));
17668  }
17669
17670  /**
17671   * @param {jQuery} $tab
17672   * @returns {?string}
17673   */
17674  function getTabSelector($tab) {
17675    return $tab.data('target') || $tab.attr('href');
17676  }
17677
17678  /**
17679   * Receives the `shown.bs.tab` event. Updates `history.state` as appropriate.
17680   *
17681   * @param {jQuery.Event} shownEvt
17682   */
17683  function onShownTab(shownEvt) {
17684    if(!showingTabsBasedOnState) {
17685      var $activatedTab = jQuery(shownEvt.target);
17686      if ( $activatedTab.hasClass('panel-collapse') )
17687        $activatedTab = $activatedTab.closest('.panel').find('a');
17688      var selector = getTabSelector($activatedTab);
17689
17690      if(selector) {
17691        var tabGroup = getTabGroup($activatedTab);
17692
17693        if(tabGroup) {
17694          var historyChanger = $activatedTab.data('tab-history-changer') || BootstrapTabHistory.options.defaultChanger;
17695          var newState = createNewHistoryState(history.state, tabGroup, selector);
17696          var updateURL = (function ($activatedTab) {
17697            if(selector[0] === '#') {
17698              var elementUpdateURLOption = parseTruthyAttributeValue($activatedTab.data('tab-history-update-url'));
17699
17700              if(elementUpdateURLOption === undefined) {
17701                return BootstrapTabHistory.options.defaultUpdateURL;
17702              } else {
17703                return elementUpdateURLOption;
17704              }
17705            } else {
17706              return false;
17707            }
17708          })($activatedTab);
17709
17710          switch(historyChanger) {
17711            case 'push':
17712              history.pushState(newState, '', updateURL ? selector : null);
17713              break;
17714            case 'replace':
17715              history.replaceState(newState, '', updateURL ? selector : null);
17716              break;
17717            default:
17718              throw new Error('Unknown tab-history-changer: ' + historyChanger);
17719          }
17720        }
17721      }
17722    }
17723  }
17724
17725  /**
17726   * Receives the `popstate` event. Shows tabs based on the value of `history.state` as appropriate.
17727   */
17728  function onPopState() {
17729    var bootstrapTabHistory = history.state && history.state.bootstrapTabHistory;
17730
17731    if(bootstrapTabHistory) {
17732      showTabsBasedOnState(bootstrapTabHistory);
17733    }
17734  }
17735
17736  /**
17737   * Returns the given value, _unless_ that value is an empty string, in which case `true` is returned.
17738   *
17739   * Rationale: HAML data attributes which are set to `true` are rendered as a blank string.
17740   *
17741   * @param {*} value
17742   * @returns {*}
17743   */
17744  function parseTruthyAttributeValue(value) {
17745    if(value) {
17746      return value;
17747    } else if(value === '') {
17748      return true;
17749    } else {
17750      return value;
17751    }
17752  }
17753
17754  /**
17755   * Show tabs based upon the anchor component of `window.location`.
17756   */
17757  function showTabsBasedOnAnchor() {
17758    if(BootstrapTabHistory.options.showTabsBasedOnAnchor) {
17759      var anchor = window.location && window.location.hash;
17760
17761      if(anchor) {
17762        var $tabElement = showTabForSelector(anchor);
17763
17764        if($tabElement && window.addEventListener && window.removeEventListener) {
17765          var anchorYOffset = (function ($tabElement) {
17766            var elementSetting = $tabElement.data('tab-history-anchor-y-offset');
17767
17768            if(elementSetting === undefined) {
17769              return BootstrapTabHistory.options.defaultAnchorYOffset;
17770            } else {
17771              return elementSetting;
17772            }
17773          })($tabElement);
17774
17775          // HACK: This prevents scrolling to the tab on page load. This relies on the fact that we should never get
17776          //   here on `history.forward`, `history.back`, or `location.reload`, since in all those situations the
17777          //   `history.state` object should have been used (unless the browser did not support the modern History API).
17778          if(anchorYOffset || anchorYOffset === 0) {
17779            var scrollListener = function resetAnchorScroll () {
17780              window.removeEventListener('scroll', scrollListener);
17781              window.scrollTo(0, anchorYOffset);
17782            };
17783
17784            window.addEventListener('scroll', scrollListener);
17785          }
17786        }
17787      }
17788    }
17789  }
17790
17791  /**
17792   * Show a tab which corresponds to the provided selector.
17793   *
17794   * @param {string} selector - A CSS selector.
17795   * @returns {?jQuery} - The tab which was found to show (even if said tab was already active).
17796   */
17797  function showTabForSelector(selector) {
17798    var $tabElement = (function (selector) {
17799      var $ret = null;
17800
17801      jQuery('[data-toggle="tab"], [data-toggle="pill"], [data-toggle="collapse"]').each(function () {
17802        var $potentialTab = jQuery(this);
17803
17804        if(($potentialTab.attr('href') === selector || $potentialTab.data('target') === selector ) && getTabGroup($potentialTab)) {
17805          $ret = $potentialTab;
17806          return false;
vendor: 4,522 bytes, lines 17807-17956
17807        } else {
17808         return null;
17809        }
17810      });
17811
17812      return $ret;
17813    })(selector);
17814
17815    if($tabElement) {
17816      $tabElement.trigger('click');
17817      //$tabElement.tab('show');
17818    }
17819
17820    return $tabElement;
17821  }
17822
17823  /**
17824   * Iterate through the provided set of tab tab groups, showing the tabs based on the stored selectors.
17825   *
17826   * @param {object} bootstrapTabHistory - Each of the values is passed to showTabForSelector; the keys are actually irrelevant.
17827   */
17828  function showTabsBasedOnState(bootstrapTabHistory) {
17829    showingTabsBasedOnState = true;
17830
17831    try {
17832      for(var k in bootstrapTabHistory) {
17833        if(bootstrapTabHistory.hasOwnProperty(k)) {
17834          showTabForSelector(bootstrapTabHistory[k]);
17835        }
17836      }
17837    } finally {
17838      showingTabsBasedOnState = false;
17839    }
17840  }
17841})();
17842
17843/*!
17844 * Justified Gallery - v3.6.3
17845 * http://miromannino.github.io/Justified-Gallery/
17846 * Copyright (c) 2016 Miro Mannino
17847 * Licensed under the MIT license.
17848 */
17849(function($) {
17850
17851  function hasScrollBar() {
17852    return $("body").height() > $(window).height();
17853  }
17854  /**
17855   * Justified Gallery controller constructor
17856   *
17857   * @param $gallery the gallery to build
17858   * @param settings the settings (the defaults are in $.fn.justifiedGallery.defaults)
17859   * @constructor
17860   */
17861  var JustifiedGallery = function ($gallery, settings) {
17862    
17863    this.settings = settings;
17864    this.checkSettings();
17865
17866    this.imgAnalyzerTimeout = null;
17867    this.entries = null;
17868    this.buildingRow = {
17869      entriesBuff : [],
17870      width : 0,
17871      height : 0,
17872      aspectRatio : 0
17873    };
17874    this.lastFetchedEntry = null;
17875    this.lastAnalyzedIndex = -1;
17876    this.yield = {
17877      every : 2, // do a flush every n flushes (must be greater than 1)
17878      flushed : 0 // flushed rows without a yield
17879    };
17880    this.border = settings.border >= 0 ? settings.border : settings.margins;
17881    this.maxRowHeight = this.retrieveMaxRowHeight();
17882    this.suffixRanges = this.retrieveSuffixRanges();
17883    this.offY = this.border;
17884    this.rows = 0;
17885    this.spinner = {
17886      phase : 0,
17887      timeSlot : 150,
17888      $el : $('<div class="spinner"><span></span><span></span><span></span></div>'),
17889      intervalId : null
17890    };
17891    this.checkWidthIntervalId = null;
17892    this.galleryWidth = $gallery.width();
17893    this.$gallery = $gallery;
17894
17895  };
17896
17897  /** @returns {String} the best suffix given the width and the height */
17898  JustifiedGallery.prototype.getSuffix = function (width, height) {
17899    var longestSide, i;
17900    longestSide = (width > height) ? width : height;
17901    for (i = 0; i < this.suffixRanges.length; i++) {
17902      if (longestSide <= this.suffixRanges[i]) {
17903        return this.settings.sizeRangeSuffixes[this.suffixRanges[i]];
17904      }
17905    }
17906    return this.settings.sizeRangeSuffixes[this.suffixRanges[i - 1]];
17907  };
17908
17909  /**
17910   * Remove the suffix from the string
17911   *
17912   * @returns {string} a new string without the suffix
17913   */
17914  JustifiedGallery.prototype.removeSuffix = function (str, suffix) {
17915    return str.substring(0, str.length - suffix.length);
17916  };
17917
17918  /**
17919   * @returns {boolean} a boolean to say if the suffix is contained in the str or not
17920   */
17921  JustifiedGallery.prototype.endsWith = function (str, suffix) {
17922    return str.indexOf(suffix, str.length - suffix.length) !== -1;
17923  };
17924
17925  /**
17926   * Get the used suffix of a particular url
17927   *
17928   * @param str
17929   * @returns {String} return the used suffix
17930   */
17931  JustifiedGallery.prototype.getUsedSuffix = function (str) {
17932    for (var si in this.settings.sizeRangeSuffixes) {
17933      if (this.settings.sizeRangeSuffixes.hasOwnProperty(si)) {
17934        if (this.settings.sizeRangeSuffixes[si].length === 0) continue;
17935        if (this.endsWith(str, this.settings.sizeRangeSuffixes[si])) return this.settings.sizeRangeSuffixes[si];
17936      }
17937    }
17938    return '';
17939  };
17940
17941  /**
17942   * Given an image src, with the width and the height, returns the new image src with the
17943   * best suffix to show the best quality thumbnail.
17944   *
17945   * @returns {String} the suffix to use
17946   */
17947  JustifiedGallery.prototype.newSrc = function (imageSrc, imgWidth, imgHeight, image) {
17948    var newImageSrc;
17949
17950    if (this.settings.thumbnailPath) {
17951      newImageSrc = this.settings.thumbnailPath(imageSrc, imgWidth, imgHeight, image);
17952    } else {
17953      var matchRes = imageSrc.match(this.settings.extension);
17954      var ext = (matchRes !== null) ? matchRes[0] : '';
17955      newImageSrc = imageSrc.replace(this.settings.extension, '');
17956      newImageSrc = this.removeSuffix(newImageSrc, this.getUsedSuffix(newImageSrc));
17957      newImageSrc += this.getSuffix(imgWidth, imgHeight) + ext;
17958    }
17959
17960    return newImageSrc;
17961  };
17962
17963  /**
17964   * Shows the images that is in the given entry
17965   *
17966   * @param $entry the entry
17967   * @param callback the callback that is called when the show animation is finished
17968   */
17969  JustifiedGallery.prototype.showImg = function ($entry, callback) {
17970    if (this.settings.cssAnimation) {
17971      $entry.addClass('entry-visible');
17972      if (callback) callback();
17973    } else {
17974      $entry.stop().fadeTo(this.settings.imagesAnimationDuration, 1.0, callback);
17975      $entry.find('> img, > a > img').stop().fadeTo(this.settings.imagesAnimationDuration, 1.0, callback);
17976    }
17977  };
17978
17979  /**
17980   * Extract the image src form the image, looking from the 'safe-src', and if it can't be found, from the
17981   * 'src' attribute. It saves in the image data the 'jg.originalSrc' field, with the extracted src.
17982   *
17983   * @param $image the image to analyze
17984   * @returns {String} the extracted src
17985   */
17986  JustifiedGallery.prototype.extractImgSrcFromImage = function ($image) {
17987    var imageSrc = (typeof $image.data('safe-src') !== 'undefined') ? $image.data('safe-src') : $image.attr('src');
17988    $image.data('jg.originalSrc', imageSrc);
17989    return imageSrc;
17990  };
17991
17992  /** @returns {jQuery} the image in the given entry */
17993  JustifiedGallery.prototype.imgFromEntry = function ($entry) {
17994    var $img = $entry.find('> img');
17995    if ($img.length === 0) $img = $entry.find('> a > img');
17996    if ($img.length === 0) $img = $entry.find('.t-entry-visual-cont img');//hacked by Uncode
17997    return $img.length === 0 ? null : $img;
17998  };
17999
18000  /** @returns {jQuery} the caption in the given entry */
18001  JustifiedGallery.prototype.captionFromEntry = function ($entry) {
18002    var $caption = $entry.find('> .caption');
18003    return $caption.length === 0 ? null : $caption;
18004  };
18005
18006  /**
18007   * Display the entry
18008   *
18009   * @param {jQuery} $entry the entry to display
18010   * @param {int} x the x position where the entry must be positioned
18011   * @param y the y position where the entry must be positioned
18012   * @param imgWidth the image width
18013   * @param imgHeight the image height
18014   * @param rowHeight the row height of the row that owns the entry
18015   */
18016  JustifiedGallery.prototype.displayEntry = function ($entry, x, y, imgWidth, imgHeight, rowHeight) {
18017    $entry.width(imgWidth);
18018    $entry.height(Math.floor(rowHeight));//hacked by Uncode
18019    $entry.css('top', Math.floor(y));//hacked by Uncode
18020    $entry.css('left', x);
18021
18022    var $image = this.imgFromEntry($entry);
18023    if ($image !== null) {
18024      $image.css('width', imgWidth);
18025      $image.css('height', imgHeight);
18026      $image.css('margin-left', - imgWidth / 2);
18027      $image.css('margin-top', - imgHeight / 2);
18028
18029      // Image reloading for an high quality of thumbnails
18030      var imageSrc = $image.attr('src');
18031      var newImageSrc = this.newSrc(imageSrc, imgWidth, imgHeight, $image[0]);
18032
18033      $image.one('error', function () {
18034        $image.attr('src', $image.data('jg.originalSrc')); //revert to the original thumbnail, we got it.
18035      });
18036
18037      var loadNewImage = function () {
18038        if (imageSrc !== newImageSrc) { //load the new image after the fadeIn
18039          $image.attr('src', newImageSrc);
18040        }
18041      };
18042
18043      if ($entry.data('jg.loaded') === 'skipped') {
18044        this.onImageEvent(imageSrc, $.proxy(function() {
18045          this.showImg($entry, loadNewImage);
18046          $entry.data('jg.loaded', true);
18047        }, this));
18048      } else {
18049        this.showImg($entry, loadNewImage);
18050      }
18051
18052    } else {
18053      this.showImg($entry);
18054    }
18055
18056    this.displayEntryCaption($entry);
18057  };
18058
18059  /**
18060   * Display the entry caption. If the caption element doesn't exists, it creates the caption using the 'alt'
18061   * or the 'title' attributes.
18062   *
18063   * @param {jQuery} $entry the entry to process
18064   */
18065  JustifiedGallery.prototype.displayEntryCaption = function ($entry) {
18066    var $image = this.imgFromEntry($entry);
18067    if ($image !== null && this.settings.captions) {
18068      var $imgCaption = this.captionFromEntry($entry);
18069
18070      // Create it if it doesn't exists
18071      if ($imgCaption === null) {
18072        var caption = $image.attr('alt');
18073        if (!this.isValidCaption(caption)) caption = $entry.attr('title');
18074        if (this.isValidCaption(caption)) { // Create only we found something
18075          $imgCaption = $('<div class="caption">' + caption + '</div>');
18076          $entry.append($imgCaption);
18077          $entry.data('jg.createdCaption', true);
18078        }
18079      }
18080
18081      // Create events (we check again the $imgCaption because it can be still inexistent)
18082      if ($imgCaption !== null) {
18083        if (!this.settings.cssAnimation) $imgCaption.stop().fadeTo(0, this.settings.captionSettings.nonVisibleOpacity);
18084        this.addCaptionEventsHandlers($entry);
18085      }
18086    } else {
18087      this.removeCaptionEventsHandlers($entry);
18088    }
18089  };
18090
18091  /**
18092   * Validates the caption
18093   *
18094   * @param caption The caption that should be validated
18095   * @return {boolean} Validation result
18096   */
18097  JustifiedGallery.prototype.isValidCaption = function (caption) {
18098    return (typeof caption !== 'undefined' && caption.length > 0);
18099  };
18100
18101  /**
18102   * The callback for the event 'mouseenter'. It assumes that the event currentTarget is an entry.
18103   * It shows the caption using jQuery (or using CSS if it is configured so)
18104   *
18105   * @param {Event} eventObject the event object
18106   */
18107  JustifiedGallery.prototype.onEntryMouseEnterForCaption = function (eventObject) {
18108    var $caption = this.captionFromEntry($(eventObject.currentTarget));
18109    if (this.settings.cssAnimation) {
18110      $caption.addClass('caption-visible').removeClass('caption-hidden');
18111    } else {
18112      $caption.stop().fadeTo(this.settings.captionSettings.animationDuration,
18113          this.settings.captionSettings.visibleOpacity);
18114    }
18115  };
18116
18117  /**
18118   * The callback for the event 'mouseleave'. It assumes that the event currentTarget is an entry.
18119   * It hides the caption using jQuery (or using CSS if it is configured so)
18120   *
18121   * @param {Event} eventObject the event object
18122   */
18123  JustifiedGallery.prototype.onEntryMouseLeaveForCaption = function (eventObject) {
18124    var $caption = this.captionFromEntry($(eventObject.currentTarget));
18125    if (this.settings.cssAnimation) {
18126      $caption.removeClass('caption-visible').removeClass('caption-hidden');
18127    } else {
18128      $caption.stop().fadeTo(this.settings.captionSettings.animationDuration,
18129          this.settings.captionSettings.nonVisibleOpacity);
18130    }
18131  };
18132
18133  /**
18134   * Add the handlers of the entry for the caption
18135   *
18136   * @param $entry the entry to modify
18137   */
18138  JustifiedGallery.prototype.addCaptionEventsHandlers = function ($entry) {
18139    var captionMouseEvents = $entry.data('jg.captionMouseEvents');
18140    if (typeof captionMouseEvents === 'undefined') {
18141      captionMouseEvents = {
18142        mouseenter: $.proxy(this.onEntryMouseEnterForCaption, this),
18143        mouseleave: $.proxy(this.onEntryMouseLeaveForCaption, this)
18144      };
18145      $entry.on('mouseenter', undefined, undefined, captionMouseEvents.mouseenter);
18146      $entry.on('mouseleave', undefined, undefined, captionMouseEvents.mouseleave);
18147      $entry.data('jg.captionMouseEvents', captionMouseEvents);
18148    }
18149  };
18150
18151  /**
18152   * Remove the handlers of the entry for the caption
18153   *
18154   * @param $entry the entry to modify
18155   */
18156  JustifiedGallery.prototype.removeCaptionEventsHandlers = function ($entry) {
18157    var captionMouseEvents = $entry.data('jg.captionMouseEvents');
18158    if (typeof captionMouseEvents !== 'undefined') {
18159      $entry.off('mouseenter', undefined, captionMouseEvents.mouseenter);
18160      $entry.off('mouseleave', undefined, captionMouseEvents.mouseleave);
18161      $entry.removeData('jg.captionMouseEvents');
18162    }
18163  };
18164
18165  /**
18166   * Justify the building row, preparing it to
18167   *
18168   * @param isLastRow
18169   * @returns a boolean to know if the row has been justified or not
18170   */
18171  JustifiedGallery.prototype.prepareBuildingRow = function (isLastRow) {
18172    var i, $entry, imgAspectRatio, newImgW, newImgH, justify = true;
18173    var minHeight = 0;
18174    var availableWidth = this.galleryWidth - 2 * this.border - (
18175        (this.buildingRow.entriesBuff.length - 1) * this.settings.margins);
18176    var rowHeight = Math.floor( availableWidth / this.buildingRow.aspectRatio );//hacked by Uncode
18177    var defaultRowHeight = this.settings.rowHeight;
18178    var justifiable = this.buildingRow.width / availableWidth > this.settings.justifyThreshold;
18179
18180    //Skip the last row if we can't justify it and the lastRow == 'hide'
18181    if (isLastRow && this.settings.lastRow === 'hide' && !justifiable) {
18182      for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
18183        $entry = this.buildingRow.entriesBuff[i];
18184        if (this.settings.cssAnimation)
18185          $entry.removeClass('entry-visible');
18186        else {
18187          $entry.stop().fadeTo(0, 0.1);
18188          $entry.find('> img, > a > img').fadeTo(0, 0);
18189        }
18190      }
18191      return -1;
18192    }
18193
18194    // With lastRow = nojustify, justify if is justificable (the images will not become too big)
18195    if (isLastRow && !justifiable && this.settings.lastRow !== 'justify' && this.settings.lastRow !== 'hide') {
18196      justify = false;
18197
18198      if (this.rows > 0) {
18199        defaultRowHeight = (this.offY - this.border - this.settings.margins * this.rows) / this.rows;
18200        justify = defaultRowHeight * this.buildingRow.aspectRatio / availableWidth > this.settings.justifyThreshold;
18201      }
18202    }
18203
18204    for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
18205      $entry = this.buildingRow.entriesBuff[i];
18206      imgAspectRatio = $entry.data('jg.width') / $entry.data('jg.height');
18207
18208      if (justify) {
18209        newImgW = (i === this.buildingRow.entriesBuff.length - 1) ? availableWidth : rowHeight * imgAspectRatio;
18210        newImgH = rowHeight;
18211      } else {
18212        newImgW = defaultRowHeight * imgAspectRatio;
18213        newImgH = defaultRowHeight;
18214      }
18215
18216      availableWidth -= Math.round(newImgW);
18217      $entry.data('jg.jwidth', Math.round(newImgW));
18218      $entry.data('jg.jheight', Math.ceil(newImgH));
18219      if (i === 0 || minHeight > newImgH) minHeight = newImgH;
18220    }
18221
18222    this.buildingRow.height = minHeight;
18223    return justify;
18224  };
18225
18226  /**
18227   * Clear the building row data to be used for a new row
18228   */
18229  JustifiedGallery.prototype.clearBuildingRow = function () {
18230    this.buildingRow.entriesBuff = [];
18231    this.buildingRow.aspectRatio = 0;
18232    this.buildingRow.width = 0;
18233  };
18234
18235  /**
18236   * Flush a row: justify it, modify the gallery height accordingly to the row height
18237   *
18238   * @param isLastRow
18239   */
18240  JustifiedGallery.prototype.flushRow = function (isLastRow) {
18241    var settings = this.settings;
18242    var $entry, buildingRowRes, offX = this.border, i;
18243
18244    buildingRowRes = this.prepareBuildingRow(isLastRow);
18245    if (isLastRow && settings.lastRow === 'hide' && buildingRowRes === -1) {
18246      this.clearBuildingRow();
18247      return;
18248    }
18249
18250    if (this.maxRowHeight) {
18251      if (this.maxRowHeight.isPercentage && this.maxRowHeight.value * settings.rowHeight < this.buildingRow.height) {
18252        this.buildingRow.height = this.maxRowHeight.value * settings.rowHeight;
18253      } else if (this.maxRowHeight.value >= settings.rowHeight && this.maxRowHeight.value < this.buildingRow.height) {
18254        this.buildingRow.height = this.maxRowHeight.value;
18255      }
18256    }
18257
18258    //Align last (unjustified) row
18259    if (settings.lastRow === 'center' || settings.lastRow === 'right') {
18260      var availableWidth = this.galleryWidth - 2 * this.border - (this.buildingRow.entriesBuff.length - 1) * settings.margins;
18261
18262      for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
18263        $entry = this.buildingRow.entriesBuff[i];
18264        availableWidth -= $entry.data('jg.jwidth');
18265      }
18266
18267      if (settings.lastRow === 'center')
18268        offX += availableWidth / 2;
18269      else if (settings.lastRow === 'right')
18270        offX += availableWidth;
18271    }
18272
18273    for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
18274      $entry = this.buildingRow.entriesBuff[i];
18275      this.displayEntry($entry, offX, this.offY, $entry.data('jg.jwidth'), $entry.data('jg.jheight'), this.buildingRow.height);
18276      offX += $entry.data('jg.jwidth') + settings.margins;
18277    }
18278
18279    //Gallery Height
18280    this.galleryHeightToSet = this.offY + this.buildingRow.height + this.border;
18281    this.$gallery.height(this.galleryHeightToSet + this.getSpinnerHeight());
18282
18283    if (!isLastRow || (this.buildingRow.height <= settings.rowHeight && buildingRowRes)) {
18284      //Ready for a new row
18285      this.offY += this.buildingRow.height + settings.margins;
18286      this.rows += 1;
18287      this.clearBuildingRow();
18288      this.$gallery.trigger('jg.rowflush');
18289    }
18290  };
18291
18292  /**
18293   * Checks the width of the gallery container, to know if a new justification is needed
18294   */
18295  var scrollBarOn = false;
18296  JustifiedGallery.prototype.checkWidth = function () {
18297    this.checkWidthIntervalId = setInterval($.proxy(function () {
18298      var galleryWidth = parseFloat(this.$gallery.width());
18299      if (hasScrollBar() === scrollBarOn) {
18300        if (Math.abs(galleryWidth - this.galleryWidth) > this.settings.refreshSensitivity) {
18301          this.galleryWidth = galleryWidth;
18302          this.rewind();
18303
18304          // Restart to analyze
18305          this.startImgAnalyzer(true);
18306        }
18307      } else {
18308        scrollBarOn = hasScrollBar();
18309        this.galleryWidth = galleryWidth;
18310      }
18311    }, this), this.settings.refreshTime);
18312  };
18313
18314  /**
18315   * @returns {boolean} a boolean saying if the spinner is active or not
18316   */
18317  JustifiedGallery.prototype.isSpinnerActive = function () {
18318    return this.spinner.intervalId !== null;
18319  };
18320
18321  /**
18322   * @returns {int} the spinner height
18323   */
18324  JustifiedGallery.prototype.getSpinnerHeight = function () {
18325    return this.spinner.$el.innerHeight();
18326  };
18327
18328  /**
18329   * Stops the spinner animation and modify the gallery height to exclude the spinner
18330   */
18331  JustifiedGallery.prototype.stopLoadingSpinnerAnimation = function () {
18332    clearInterval(this.spinner.intervalId);
18333    this.spinner.intervalId = null;
18334    this.$gallery.height(this.$gallery.height() - this.getSpinnerHeight());
18335    this.spinner.$el.detach();
18336  };
18337
18338  /**
18339   * Starts the spinner animation
18340   */
18341  JustifiedGallery.prototype.startLoadingSpinnerAnimation = function () {
18342    var spinnerContext = this.spinner;
18343    var $spinnerPoints = spinnerContext.$el.find('span');
18344    clearInterval(spinnerContext.intervalId);
18345    this.$gallery.append(spinnerContext.$el);
18346    this.$gallery.height(this.offY + this.buildingRow.height + this.getSpinnerHeight());
18347    spinnerContext.intervalId = setInterval(function () {
18348      if (spinnerContext.phase < $spinnerPoints.length) {
18349        $spinnerPoints.eq(spinnerContext.phase).fadeTo(spinnerContext.timeSlot, 1);
18350      } else {
vendor: 4,333 bytes, lines 18351-18495
18351        $spinnerPoints.eq(spinnerContext.phase - $spinnerPoints.length).fadeTo(spinnerContext.timeSlot, 0);
18352      }
18353      spinnerContext.phase = (spinnerContext.phase + 1) % ($spinnerPoints.length * 2);
18354    }, spinnerContext.timeSlot);
18355  };
18356
18357  /**
18358   * Rewind the image analysis to start from the first entry.
18359   */
18360  JustifiedGallery.prototype.rewind = function () {
18361    this.lastFetchedEntry = null;
18362    this.lastAnalyzedIndex = -1;
18363    this.offY = this.border;
18364    this.rows = 0;
18365    this.clearBuildingRow();
18366  };
18367
18368  /**
18369   * Update the entries searching it from the justified gallery HTML element
18370   *
18371   * @param norewind if norewind only the new entries will be changed (i.e. randomized, sorted or filtered)
18372   * @returns {boolean} true if some entries has been founded
18373   */
18374  JustifiedGallery.prototype.updateEntries = function (norewind) {
18375    var newEntries;
18376
18377    if (norewind && this.lastFetchedEntry != null) {
18378      newEntries = $(this.lastFetchedEntry).nextAll(this.settings.selector).toArray();
18379    } else {
18380      this.entries = [];
18381      newEntries = this.$gallery.children(this.settings.selector).toArray();
18382    }
18383
18384    if (newEntries.length > 0) {
18385
18386      // Sort or randomize
18387      if ($.isFunction(this.settings.sort)) {
18388        newEntries = this.sortArray(newEntries);
18389      } else if (this.settings.randomize) {
18390        newEntries = this.shuffleArray(newEntries);
18391      }
18392      this.lastFetchedEntry = newEntries[newEntries.length - 1];
18393
18394      // Filter
18395      if (this.settings.filter) {
18396        newEntries = this.filterArray(newEntries);
18397      } else {
18398        this.resetFilters(newEntries);
18399      }
18400
18401    }
18402
18403    this.entries = this.entries.concat(newEntries);
18404    return true;
18405  };
18406
18407  /**
18408   * Apply the entries order to the DOM, iterating the entries and appending the images
18409   *
18410   * @param entries the entries that has been modified and that must be re-ordered in the DOM
18411   */
18412  JustifiedGallery.prototype.insertToGallery = function (entries) {
18413    var that = this;
18414    $.each(entries, function () {
18415      $(this).appendTo(that.$gallery);
18416    });
18417  };
18418
18419  /**
18420   * Shuffle the array using the Fisher-Yates shuffle algorithm
18421   *
18422   * @param a the array to shuffle
18423   * @return the shuffled array
18424   */
18425  JustifiedGallery.prototype.shuffleArray = function (a) {
18426    var i, j, temp;
18427    for (i = a.length - 1; i > 0; i--) {
18428      j = Math.floor(Math.random() * (i + 1));
18429      temp = a[i];
18430      a[i] = a[j];
18431      a[j] = temp;
18432    }
18433    this.insertToGallery(a);
18434    return a;
18435  };
18436
18437  /**
18438   * Sort the array using settings.comparator as comparator
18439   *
18440   * @param a the array to sort (it is sorted)
18441   * @return the sorted array
18442   */
18443  JustifiedGallery.prototype.sortArray = function (a) {
18444    a.sort(this.settings.sort);
18445    this.insertToGallery(a);
18446    return a;
18447  };
18448
18449  /**
18450   * Reset the filters removing the 'jg-filtered' class from all the entries
18451   *
18452   * @param a the array to reset
18453   */
18454  JustifiedGallery.prototype.resetFilters = function (a) {
18455    for (var i = 0; i < a.length; i++) $(a[i]).removeClass('jg-filtered');
18456  };
18457
18458  /**
18459   * Filter the entries considering theirs classes (if a string has been passed) or using a function for filtering.
18460   *
18461   * @param a the array to filter
18462   * @return the filtered array
18463   */
18464  JustifiedGallery.prototype.filterArray = function (a) {
18465    var settings = this.settings;
18466    if ($.type(settings.filter) === 'string') {
18467      // Filter only keeping the entries passed in the string
18468      return a.filter(function (el) {
18469        var $el = $(el);
18470        if ($el.is(settings.filter)) {
18471          $el.removeClass('jg-filtered');
18472          return true;
18473        } else {
18474          $el.addClass('jg-filtered').removeClass('jg-visible');
18475          return false;
18476        }
18477      });
18478    } else if ($.isFunction(settings.filter)) {
18479      // Filter using the passed function
18480      var filteredArr = a.filter(settings.filter);
18481      for (var i = 0; i < a.length; i++) {
18482        if (filteredArr.indexOf(a[i]) == -1) {
18483          $(a[i]).addClass('jg-filtered').removeClass('jg-visible');
18484        } else {
18485          $(a[i]).removeClass('jg-filtered');
18486        }
18487      }
18488      return filteredArr;
18489    }
18490  };
18491
18492  /**
18493   * Destroy the Justified Gallery instance.
18494   *
18495   * It clears all the css properties added in the style attributes. We doesn't backup the original
18496   * values for those css attributes, because it costs (performance) and because in general one
18497   * shouldn't use the style attribute for an uniform set of images (where we suppose the use of
18498   * classes). Creating a backup is also difficult because JG could be called multiple times and
18499   * with different style attributes.
18500   */
18501  JustifiedGallery.prototype.destroy = function () {
18502    clearInterval(this.checkWidthIntervalId);
18503
18504    $.each(this.entries, $.proxy(function(_, entry) {
18505      var $entry = $(entry);
18506
18507      // Reset entry style
18508      $entry.css('width', '');
18509      $entry.css('height', '');
18510      $entry.css('top', '');
18511      $entry.css('left', '');
18512      $entry.data('jg.loaded', undefined);
18513      $entry.removeClass('jg-entry');
18514
18515      // Reset image style
18516      var $img = this.imgFromEntry($entry);
18517      $img.css('width', '');
18518      $img.css('height', '');
18519      $img.css('margin-left', '');
18520      $img.css('margin-top', '');
18521      $img.attr('src', $img.data('jg.originalSrc'));
18522      $img.data('jg.originalSrc', undefined);
18523
18524      // Remove caption
18525      this.removeCaptionEventsHandlers($entry);
18526      var $caption = this.captionFromEntry($entry);
18527      if ($entry.data('jg.createdCaption')) {
18528        // remove also the caption element (if created by jg)
18529        $entry.data('jg.createdCaption', undefined);
18530        if ($caption !== null) $caption.remove();
18531      } else {
18532        if ($caption !== null) $caption.fadeTo(0, 1);
18533      }
18534
18535    }, this));
18536
18537    this.$gallery.css('height', '');
18538    this.$gallery.removeClass('justified-gallery');
18539    this.$gallery.data('jg.controller', undefined);
18540  };
18541
18542  /**
18543   * Analyze the images and builds the rows. It returns if it found an image that is not loaded.
18544   *
18545   * @param isForResize if the image analyzer is called for resizing or not, to call a different callback at the end
18546   */
18547  JustifiedGallery.prototype.analyzeImages = function (isForResize) {
18548    for (var i = this.lastAnalyzedIndex + 1; i < this.entries.length; i++) {
18549      var $entry = $(this.entries[i]);
18550      if ($entry.data('jg.loaded') === true || $entry.data('jg.loaded') === 'skipped') {
18551        var availableWidth = this.galleryWidth - 2 * this.border - (
18552            (this.buildingRow.entriesBuff.length - 1) * this.settings.margins);
18553        var imgAspectRatio = $entry.data('jg.width') / $entry.data('jg.height');
18554        if (availableWidth / (this.buildingRow.aspectRatio + imgAspectRatio) < this.settings.rowHeight) {
18555          this.flushRow(false);
18556          if(++this.yield.flushed >= this.yield.every) {
18557            this.startImgAnalyzer(isForResize);
18558            return;
18559          }
18560        }
18561
18562        this.buildingRow.entriesBuff.push($entry);
18563        this.buildingRow.aspectRatio += imgAspectRatio;
18564        this.buildingRow.width += imgAspectRatio * this.settings.rowHeight;
18565        this.lastAnalyzedIndex = i;
18566
18567      } else if ($entry.data('jg.loaded') !== 'error') {
18568        return;
18569      }
18570    }
18571
18572    // Last row flush (the row is not full)
18573    if (this.buildingRow.entriesBuff.length > 0) this.flushRow(true);
18574
18575    if (this.isSpinnerActive()) {
18576      this.stopLoadingSpinnerAnimation();
18577    }
18578
18579    /* Stop, if there is, the timeout to start the analyzeImages.
18580     This is because an image can be set loaded, and the timeout can be set,
18581     but this image can be analyzed yet.
18582     */
18583    this.stopImgAnalyzerStarter();
18584
18585    //On complete callback
18586    this.$gallery.trigger(isForResize ? 'jg.resize' : 'jg.complete');
18587    this.$gallery.height(this.galleryHeightToSet);
18588  };
18589
18590  /**
18591   * Stops any ImgAnalyzer starter (that has an assigned timeout)
18592   */
18593  JustifiedGallery.prototype.stopImgAnalyzerStarter = function () {
18594    this.yield.flushed = 0;
18595    if (this.imgAnalyzerTimeout !== null) clearTimeout(this.imgAnalyzerTimeout);
18596  };
18597
18598  /**
18599   * Starts the image analyzer. It is not immediately called to let the browser to update the view
18600   *
18601   * @param isForResize specifies if the image analyzer must be called for resizing or not
18602   */
18603  JustifiedGallery.prototype.startImgAnalyzer = function (isForResize) {
18604    var that = this;
18605    this.stopImgAnalyzerStarter();
18606    this.imgAnalyzerTimeout = setTimeout(function () {
18607      that.analyzeImages(isForResize);
18608    }, 0.001); // we can't start it immediately due to a IE different behaviour
18609  };
18610
18611  /**
18612   * Checks if the image is loaded or not using another image object. We cannot use the 'complete' image property,
18613   * because some browsers, with a 404 set complete = true.
18614   *
18615   * @param imageSrc the image src to load
18616   * @param onLoad callback that is called when the image has been loaded
vendor: 13,320 bytes, lines 18617-18967
18617   * @param onError callback that is called in case of an error
18618   */
18619  JustifiedGallery.prototype.onImageEvent = function (imageSrc, onLoad, onError) {
18620    if (!onLoad && !onError) return;
18621
18622    var memImage = new Image();
18623    var $memImage = $(memImage);
18624    if (onLoad) {
18625      $memImage.one('load', function () {
18626        $memImage.off('load error');
18627        onLoad(memImage);
18628      });
18629    }
18630    if (onError) {
18631      $memImage.one('error', function() {
18632        $memImage.off('load error');
18633        onError(memImage);
18634      });
18635    }
18636    memImage.src = imageSrc;
18637  };
18638
18639  /**
18640   * Init of Justified Gallery controlled
18641   * It analyzes all the entries starting theirs loading and calling the image analyzer (that works with loaded images)
18642   */
18643  JustifiedGallery.prototype.init = function () {
18644    var imagesToLoad = false, skippedImages = false, that = this;
18645    $.each(this.entries, function (index, entry) {
18646      var $entry = $(entry);
18647      var $image = that.imgFromEntry($entry);
18648
18649      $entry.addClass('jg-entry');
18650
18651      if ($entry.data('jg.loaded') !== true && $entry.data('jg.loaded') !== 'skipped') {
18652
18653        // Link Rel global overwrite
18654        if (that.settings.rel !== null) $entry.attr('rel', that.settings.rel);
18655
18656        // Link Target global overwrite
18657        if (that.settings.target !== null) $entry.attr('target', that.settings.target);
18658
18659        if ($image !== null) {
18660
18661          // Image src
18662          var imageSrc = that.extractImgSrcFromImage($image);
18663          $image.attr('src', imageSrc);
18664
18665          /* If we have the height and the width, we don't wait that the image is loaded, but we start directly
18666           * with the justification */
18667          if (that.settings.waitThumbnailsLoad === false) {
18668            var width = parseFloat($image.attr('width'));
18669            var height = parseFloat($image.attr('height'));
18670            if (!isNaN(width) && !isNaN(height)) {
18671              $entry.data('jg.width', width);
18672              $entry.data('jg.height', height);
18673              $entry.data('jg.loaded', 'skipped');
18674              skippedImages = true;
18675              that.startImgAnalyzer(false);
18676              return true; // continue
18677            }
18678          }
18679
18680          $entry.data('jg.loaded', false);
18681          imagesToLoad = true;
18682
18683          // Spinner start
18684          if (!that.isSpinnerActive()) that.startLoadingSpinnerAnimation();
18685
18686          that.onImageEvent(imageSrc, function (loadImg) { // image loaded
18687            $entry.data('jg.width', loadImg.width);
18688            $entry.data('jg.height', loadImg.height);
18689            $entry.data('jg.loaded', true);
18690            that.startImgAnalyzer(false);
18691          }, function () { // image load error
18692            $entry.data('jg.loaded', 'error');
18693            that.startImgAnalyzer(false);
18694          });
18695
18696        } else {
18697          $entry.data('jg.loaded', true);
18698          $entry.data('jg.width', $entry.width() | parseFloat($entry.css('width')) | 1);
18699          $entry.data('jg.height', $entry.height() | parseFloat($entry.css('height')) | 1);
18700        }
18701
18702      }
18703
18704    });
18705
18706    if (!imagesToLoad && !skippedImages) this.startImgAnalyzer(false);
18707    this.checkWidth();
18708  };
18709
18710  /**
18711   * Checks that it is a valid number. If a string is passed it is converted to a number
18712   *
18713   * @param settingContainer the object that contains the setting (to allow the conversion)
18714   * @param settingName the setting name
18715   */
18716  JustifiedGallery.prototype.checkOrConvertNumber = function (settingContainer, settingName) {
18717    if ($.type(settingContainer[settingName]) === 'string') {
18718      settingContainer[settingName] = parseFloat(settingContainer[settingName]);
18719    }
18720
18721    if ($.type(settingContainer[settingName]) === 'number') {
18722      if (isNaN(settingContainer[settingName])) throw 'invalid number for ' + settingName;
18723    } else {
18724      throw settingName + ' must be a number';
18725    }
18726  };
18727
18728  /**
18729   * Checks the sizeRangeSuffixes and, if necessary, converts
18730   * its keys from string (e.g. old settings with 'lt100') to int.
18731   */
18732  JustifiedGallery.prototype.checkSizeRangesSuffixes = function () {
18733    if ($.type(this.settings.sizeRangeSuffixes) !== 'object') {
18734      throw 'sizeRangeSuffixes must be defined and must be an object';
18735    }
18736
18737    var suffixRanges = [];
18738    for (var rangeIdx in this.settings.sizeRangeSuffixes) {
18739      if (this.settings.sizeRangeSuffixes.hasOwnProperty(rangeIdx)) suffixRanges.push(rangeIdx);
18740    }
18741
18742    var newSizeRngSuffixes = {0: ''};
18743    for (var i = 0; i < suffixRanges.length; i++) {
18744      if ($.type(suffixRanges[i]) === 'string') {
18745        try {
18746          var numIdx = parseInt(suffixRanges[i].replace(/^[a-z]+/, ''), 10);
18747          newSizeRngSuffixes[numIdx] = this.settings.sizeRangeSuffixes[suffixRanges[i]];
18748        } catch (e) {
18749          throw 'sizeRangeSuffixes keys must contains correct numbers (' + e + ')';
18750        }
18751      } else {
18752        newSizeRngSuffixes[suffixRanges[i]] = this.settings.sizeRangeSuffixes[suffixRanges[i]];
18753      }
18754    }
18755
18756    this.settings.sizeRangeSuffixes = newSizeRngSuffixes;
18757  };
18758
18759  /**
18760   * check and convert the maxRowHeight setting
18761   */
18762  JustifiedGallery.prototype.retrieveMaxRowHeight = function () {
18763    var newMaxRowHeight = { };
18764
18765    if ($.type(this.settings.maxRowHeight) === 'string') {
18766      if (this.settings.maxRowHeight.match(/^[0-9]+%$/)) {
18767        newMaxRowHeight.value = parseFloat(this.settings.maxRowHeight.match(/^([0-9]+)%$/)[1]) / 100;
18768        newMaxRowHeight.isPercentage = false;
18769      } else {
18770        newMaxRowHeight.value = parseFloat(this.settings.maxRowHeight);
18771        newMaxRowHeight.isPercentage = true;
18772      }
18773    } else if ($.type(this.settings.maxRowHeight) === 'number') {
18774      newMaxRowHeight.value = this.settings.maxRowHeight;
18775      newMaxRowHeight.isPercentage = false;
18776    } else if (this.settings.maxRowHeight === false ||
18777        this.settings.maxRowHeight === null ||
18778        typeof this.settings.maxRowHeight == 'undefined') {
18779      return null;
18780    } else {
18781      throw 'maxRowHeight must be a number or a percentage';
18782    }
18783
18784    // check if the converted value is not a number
18785    if (isNaN(newMaxRowHeight.value)) throw 'invalid number for maxRowHeight';
18786
18787    // check values
18788    if (newMaxRowHeight.isPercentage) {
18789      if (newMaxRowHeight.value < 100) newMaxRowHeight.value = 100;
18790    }
18791
18792    return newMaxRowHeight;
18793  };
18794
18795  /**
18796   * Checks the settings
18797   */
18798  JustifiedGallery.prototype.checkSettings = function () {
18799    this.checkSizeRangesSuffixes();
18800
18801    this.checkOrConvertNumber(this.settings, 'rowHeight');
18802    this.checkOrConvertNumber(this.settings, 'margins');
18803    this.checkOrConvertNumber(this.settings, 'border');
18804
18805    var lastRowModes = [
18806      'justify',
18807      'nojustify',
18808      'left',
18809      'center',
18810      'right',
18811      'hide'
18812    ];
18813    if (lastRowModes.indexOf(this.settings.lastRow) === -1) {
18814      throw 'lastRow must be one of: ' + lastRowModes.join(', ');
18815    }
18816
18817    this.checkOrConvertNumber(this.settings, 'justifyThreshold');
18818    if (this.settings.justifyThreshold < 0 || this.settings.justifyThreshold > 1) {
18819      throw 'justifyThreshold must be in the interval [0,1]';
18820    }
18821    if ($.type(this.settings.cssAnimation) !== 'boolean') {
18822      throw 'cssAnimation must be a boolean';
18823    }
18824
18825    if ($.type(this.settings.captions) !== 'boolean') throw 'captions must be a boolean';
18826    this.checkOrConvertNumber(this.settings.captionSettings, 'animationDuration');
18827
18828    this.checkOrConvertNumber(this.settings.captionSettings, 'visibleOpacity');
18829    if (this.settings.captionSettings.visibleOpacity < 0 ||
18830        this.settings.captionSettings.visibleOpacity > 1) {
18831      throw 'captionSettings.visibleOpacity must be in the interval [0, 1]';
18832    }
18833
18834    this.checkOrConvertNumber(this.settings.captionSettings, 'nonVisibleOpacity');
18835    if (this.settings.captionSettings.nonVisibleOpacity < 0 ||
18836        this.settings.captionSettings.nonVisibleOpacity > 1) {
18837      throw 'captionSettings.nonVisibleOpacity must be in the interval [0, 1]';
18838    }
18839
18840    this.checkOrConvertNumber(this.settings, 'imagesAnimationDuration');
18841    this.checkOrConvertNumber(this.settings, 'refreshTime');
18842    this.checkOrConvertNumber(this.settings, 'refreshSensitivity');
18843    if ($.type(this.settings.randomize) !== 'boolean') throw 'randomize must be a boolean';
18844    if ($.type(this.settings.selector) !== 'string') throw 'selector must be a string';
18845
18846    if (this.settings.sort !== false && !$.isFunction(this.settings.sort)) {
18847      throw 'sort must be false or a comparison function';
18848    }
18849
18850    if (this.settings.filter !== false && !$.isFunction(this.settings.filter) &&
18851        $.type(this.settings.filter) !== 'string') {
18852      throw 'filter must be false, a string or a filter function';
18853    }
18854  };
18855
18856  /**
18857   * It brings all the indexes from the sizeRangeSuffixes and it orders them. They are then sorted and returned.
18858   * @returns {Array} sorted suffix ranges
18859   */
18860  JustifiedGallery.prototype.retrieveSuffixRanges = function () {
18861    var suffixRanges = [];
18862    for (var rangeIdx in this.settings.sizeRangeSuffixes) {
18863      if (this.settings.sizeRangeSuffixes.hasOwnProperty(rangeIdx)) suffixRanges.push(parseInt(rangeIdx, 10));
18864    }
18865    suffixRanges.sort(function (a, b) { return a > b ? 1 : a < b ? -1 : 0; });
18866    return suffixRanges;
18867  };
18868
18869  /**
18870   * Update the existing settings only changing some of them
18871   *
18872   * @param newSettings the new settings (or a subgroup of them)
18873   */
18874  JustifiedGallery.prototype.updateSettings = function (newSettings) {
18875    // In this case Justified Gallery has been called again changing only some options
18876    this.settings = $.extend({}, this.settings, newSettings);
18877    this.checkSettings();
18878
18879    // As reported in the settings: negative value = same as margins, 0 = disabled
18880    this.border = this.settings.border >= 0 ? this.settings.border : this.settings.margins;
18881
18882    this.maxRowHeight = this.retrieveMaxRowHeight();
18883    this.suffixRanges = this.retrieveSuffixRanges();
18884  };
18885
18886  /**
18887   * Justified Gallery plugin for jQuery
18888   *
18889   * Events
18890   *  - jg.complete : called when all the gallery has been created
18891   *  - jg.resize : called when the gallery has been resized
18892   *  - jg.rowflush : when a new row appears
18893   *
18894   * @param arg the action (or the settings) passed when the plugin is called
18895   * @returns {*} the object itself
18896   */
18897  $.fn.justifiedGallery = function (arg) {
18898    return this.each(function (index, gallery) {
18899
18900      var $gallery = $(gallery);
18901      $gallery.addClass('justified-gallery');
18902
18903      var controller = $gallery.data('jg.controller');
18904      if (typeof controller === 'undefined') {
18905        // Create controller and assign it to the object data
18906        if (typeof arg !== 'undefined' && arg !== null && $.type(arg) !== 'object') {
18907          if (arg === 'destroy') return; // Just a call to an unexisting object
18908          throw 'The argument must be an object';
18909        }
18910        controller = new JustifiedGallery($gallery, $.extend({}, $.fn.justifiedGallery.defaults, arg));
18911        $gallery.data('jg.controller', controller);
18912      } else if (arg === 'norewind') {
18913        // In this case we don't rewind: we analyze only the latest images (e.g. to complete the last unfinished row
18914        // ... left to be more readable
18915      } else if (arg === 'destroy') {
18916        controller.destroy();
18917        return;
18918      } else {
18919        // In this case Justified Gallery has been called again changing only some options
18920        controller.updateSettings(arg);
18921        controller.rewind();
18922      }
18923
18924      // Update the entries list
18925      if (!controller.updateEntries(arg === 'norewind')) return;
18926
18927      // Init justified gallery
18928      controller.init();
18929
18930    });
18931  };
18932
18933  // Default options
18934  $.fn.justifiedGallery.defaults = {
18935    sizeRangeSuffixes: { }, /* e.g. Flickr configuration
18936        {
18937          100: '_t',  // used when longest is less than 100px
18938          240: '_m',  // used when longest is between 101px and 240px
18939          320: '_n',  // ...
18940          500: '',
18941          640: '_z',
18942          1024: '_b'  // used as else case because it is the last
18943        }
18944    */
18945    thumbnailPath: undefined, /* If defined, sizeRangeSuffixes is not used, and this function is used to determine the
18946    path relative to a specific thumbnail size. The function should accept respectively three arguments:
18947    current path, width and height */
18948    rowHeight: 120,
18949    maxRowHeight: false, // false or negative value to deactivate. Positive number to express the value in pixels,
18950                         // A string '[0-9]+%' to express in percentage (e.g. 300% means that the row height
18951                         // can't exceed 3 * rowHeight)
18952    margins: 1,
18953    border: -1, // negative value = same as margins, 0 = disabled, any other value to set the border
18954
18955    lastRow: 'nojustify', // … which is the same as 'left', or can be 'justify', 'center', 'right' or 'hide'
18956
18957    justifyThreshold: 0.90, /* if row width / available space > 0.90 it will be always justified
18958                             * (i.e. lastRow setting is not considered) */
18959    waitThumbnailsLoad: true,
18960    captions: true,
18961    cssAnimation: true,
18962    imagesAnimationDuration: 500, // ignored with css animations
18963    captionSettings: { // ignored with css animations
18964      animationDuration: 500,
18965      visibleOpacity: 0.7,
18966      nonVisibleOpacity: 0.0
18967    },
vendor: 5,759 bytes, lines 18968-19110
18968    rel: null, // rewrite the rel of each analyzed links
18969    target: null, // rewrite the target of all links
18970    extension: /\.[^.\\/]+$/, // regexp to capture the extension of an image
18971    refreshTime: 200, // time interval (in ms) to check if the page changes its width
18972    refreshSensitivity: 0, // change in width allowed (in px) without re-building the gallery
18973    randomize: false,
18974    sort: false, /*
18975      - false: to do not sort
18976      - function: to sort them using the function as comparator (see Array.prototype.sort())
18977    */
18978    filter: false, /*
18979      - false, null or undefined: for a disabled filter
18980      - a string: an entry is kept if entry.is(filter string) returns true
18981                  see jQuery's .is() function for further information
18982      - a function: invoked with arguments (entry, index, array). Return true to keep the entry, false otherwise.
18983                    It follows the specifications of the Array.prototype.filter() function of JavaScript.
18984    */
18985    selector: 'a, div:not(.spinner)' // The selector that is used to know what are the entries of the gallery
18986  };
18987
18988}(jQuery));
18989/*!
18990* Customized version of iScroll.js 0.0.1
18991* It fixes bugs affecting its integration with fullpage.js
18992*/
18993/*! iScroll v5.2.0 ~ (c) 2008-2016 Matteo Spinelli ~ http://cubiq.org/license */
18994(function (window, document, Math) {
18995var rAF = window.requestAnimationFrame  ||
18996    window.webkitRequestAnimationFrame  ||
18997    window.mozRequestAnimationFrame     ||
18998    window.oRequestAnimationFrame       ||
18999    window.msRequestAnimationFrame      ||
19000    function (callback) { window.setTimeout(callback, 1000 / 60); };
19001
19002var utils = (function () {
19003    var me = {};
19004
19005    var _elementStyle = document.createElement('div').style;
19006    var _vendor = (function () {
19007        var vendors = ['t', 'webkitT', 'MozT', 'msT', 'OT'],
19008            transform,
19009            i = 0,
19010            l = vendors.length;
19011
19012        for ( ; i < l; i++ ) {
19013            transform = vendors[i] + 'ransform';
19014            if ( transform in _elementStyle ) return vendors[i].substr(0, vendors[i].length-1);
19015        }
19016
19017        return false;
19018    })();
19019
19020    function _prefixStyle (style) {
19021        if ( _vendor === false ) return false;
19022        if ( _vendor === '' ) return style;
19023        return _vendor + style.charAt(0).toUpperCase() + style.substr(1);
19024    }
19025
19026    me.getTime = Date.now || function getTime () { return new Date().getTime(); };
19027
19028    me.extend = function (target, obj) {
19029        for ( var i in obj ) {
19030            target[i] = obj[i];
19031        }
19032    };
19033
19034    me.addEvent = function (el, type, fn, capture) {
19035        el.addEventListener(type, fn, !!capture);
19036    };
19037
19038    me.removeEvent = function (el, type, fn, capture) {
19039        el.removeEventListener(type, fn, !!capture);
19040    };
19041
19042    me.prefixPointerEvent = function (pointerEvent) {
19043        return window.MSPointerEvent ?
19044            'MSPointer' + pointerEvent.charAt(7).toUpperCase() + pointerEvent.substr(8):
19045            pointerEvent;
19046    };
19047
19048    me.momentum = function (current, start, time, lowerMargin, wrapperSize, deceleration) {
19049        var distance = current - start,
19050            speed = Math.abs(distance) / time,
19051            destination,
19052            duration;
19053
19054        deceleration = deceleration === undefined ? 0.0006 : deceleration;
19055
19056        destination = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 );
19057        duration = speed / deceleration;
19058
19059        if ( destination < lowerMargin ) {
19060            destination = wrapperSize ? lowerMargin - ( wrapperSize / 2.5 * ( speed / 8 ) ) : lowerMargin;
19061            distance = Math.abs(destination - current);
19062            duration = distance / speed;
19063        } else if ( destination > 0 ) {
19064            destination = wrapperSize ? wrapperSize / 2.5 * ( speed / 8 ) : 0;
19065            distance = Math.abs(current) + destination;
19066            duration = distance / speed;
19067        }
19068
19069        return {
19070            destination: Math.round(destination),
19071            duration: duration
19072        };
19073    };
19074
19075    var _transform = _prefixStyle('transform');
19076
19077    me.extend(me, {
19078        hasTransform: _transform !== false,
19079        hasPerspective: _prefixStyle('perspective') in _elementStyle,
19080        hasTouch: 'ontouchstart' in window,
19081        hasPointer: !!(window.PointerEvent || window.MSPointerEvent), // IE10 is prefixed
19082        hasTransition: _prefixStyle('transition') in _elementStyle
19083    });
19084
19085    /*
19086    This should find all Android browsers lower than build 535.19 (both stock browser and webview)
19087    - galaxy S2 is ok
19088    - 2.3.6 : `AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1`
19089    - 4.0.4 : `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
19090   - galaxy S3 is badAndroid (stock brower, webview)
19091     `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
19092   - galaxy S4 is badAndroid (stock brower, webview)
19093     `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
19094   - galaxy S5 is OK
19095     `AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36 (Chrome/)`
19096   - galaxy S6 is OK
19097     `AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36 (Chrome/)`
19098  */
19099    me.isBadAndroid = (function() {
19100        var appVersion = window.navigator.appVersion;
19101        // Android browser is not a chrome browser.
19102        if (/Android/.test(appVersion) && !(/Chrome\/\d/.test(appVersion))) {
19103            var safariVersion = appVersion.match(/Safari\/(\d+.\d)/);
19104            if(safariVersion && typeof safariVersion === "object" && safariVersion.length >= 2) {
19105                return parseFloat(safariVersion[1]) < 535.19;
19106            } else {
19107                return true;
19108            }
19109        } else {
19110            return false;
vendor: 5,536 bytes, lines 19111-19303
19111        }
19112    })();
19113
19114    me.extend(me.style = {}, {
19115        transform: _transform,
19116        transitionTimingFunction: _prefixStyle('transitionTimingFunction'),
19117        transitionDuration: _prefixStyle('transitionDuration'),
19118        transitionDelay: _prefixStyle('transitionDelay'),
19119        transformOrigin: _prefixStyle('transformOrigin')
19120    });
19121
19122    me.hasClass = function (e, c) {
19123        var re = new RegExp("(^|\\s)" + c + "(\\s|$)");
19124        return re.test(e.className);
19125    };
19126
19127    me.addClass = function (e, c) {
19128        if ( me.hasClass(e, c) ) {
19129            return;
19130        }
19131
19132        var newclass = e.className.split(' ');
19133        newclass.push(c);
19134        e.className = newclass.join(' ');
19135    };
19136
19137    me.removeClass = function (e, c) {
19138        if ( !me.hasClass(e, c) ) {
19139            return;
19140        }
19141
19142        var re = new RegExp("(^|\\s)" + c + "(\\s|$)", 'g');
19143        e.className = e.className.replace(re, ' ');
19144    };
19145
19146    me.offset = function (el) {
19147        var left = -el.offsetLeft,
19148            top = -el.offsetTop;
19149
19150        // jshint -W084
19151        while (el = el.offsetParent) {
19152            left -= el.offsetLeft;
19153            top -= el.offsetTop;
19154        }
19155        // jshint +W084
19156
19157        return {
19158            left: left,
19159            top: top
19160        };
19161    };
19162
19163    me.preventDefaultException = function (el, exceptions) {
19164        for ( var i in exceptions ) {
19165            if ( exceptions[i].test(el[i]) ) {
19166                return true;
19167            }
19168        }
19169
19170        return false;
19171    };
19172
19173    me.extend(me.eventType = {}, {
19174        touchstart: 1,
19175        touchmove: 1,
19176        touchend: 1,
19177
19178        mousedown: 2,
19179        mousemove: 2,
19180        mouseup: 2,
19181
19182        pointerdown: 3,
19183        pointermove: 3,
19184        pointerup: 3,
19185
19186        MSPointerDown: 3,
19187        MSPointerMove: 3,
19188        MSPointerUp: 3
19189    });
19190
19191    me.extend(me.ease = {}, {
19192        quadratic: {
19193            style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
19194            fn: function (k) {
19195                return k * ( 2 - k );
19196            }
19197        },
19198        circular: {
19199            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)
19200            fn: function (k) {
19201                return Math.sqrt( 1 - ( --k * k ) );
19202            }
19203        },
19204        back: {
19205            style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',
19206            fn: function (k) {
19207                var b = 4;
19208                return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1;
19209            }
19210        },
19211        bounce: {
19212            style: '',
19213            fn: function (k) {
19214                if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {
19215                    return 7.5625 * k * k;
19216                } else if ( k < ( 2 / 2.75 ) ) {
19217                    return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;
19218                } else if ( k < ( 2.5 / 2.75 ) ) {
19219                    return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;
19220                } else {
19221                    return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;
19222                }
19223            }
19224        },
19225        elastic: {
19226            style: '',
19227            fn: function (k) {
19228                var f = 0.22,
19229                    e = 0.4;
19230
19231                if ( k === 0 ) { return 0; }
19232                if ( k == 1 ) { return 1; }
19233
19234                return ( e * Math.pow( 2, - 10 * k ) * Math.sin( ( k - f / 4 ) * ( 2 * Math.PI ) / f ) + 1 );
19235            }
19236        }
19237    });
19238
19239    me.tap = function (e, eventName) {
19240        var ev = document.createEvent('Event');
19241        ev.initEvent(eventName, true, true);
19242        ev.pageX = e.pageX;
19243        ev.pageY = e.pageY;
19244        e.target.dispatchEvent(ev);
19245    };
19246
19247    me.click = function (e) {
19248        var target = e.target,
19249            ev;
19250
19251        if ( !(/(SELECT|INPUT|TEXTAREA)/i).test(target.tagName) ) {
19252            // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/initMouseEvent
19253            // initMouseEvent is deprecated.
19254            ev = document.createEvent(window.MouseEvent ? 'MouseEvents' : 'Event');
19255            ev.initEvent('click', true, true);
19256            ev.view = e.view || window;
19257            ev.detail = 1;
19258            ev.screenX = target.screenX || 0;
19259            ev.screenY = target.screenY || 0;
19260            ev.clientX = target.clientX || 0;
19261            ev.clientY = target.clientY || 0;
19262            ev.ctrlKey = !!e.ctrlKey;
19263            ev.altKey = !!e.altKey;
19264            ev.shiftKey = !!e.shiftKey;
19265            ev.metaKey = !!e.metaKey;
19266            ev.button = 0;
19267            ev.relatedTarget = null;
19268            ev._constructed = true;
19269            target.dispatchEvent(ev);
19270        }
19271    };
19272
19273    return me;
19274})();
19275function IScroll (el, options) {
19276    this.wrapper = typeof el == 'string' ? document.querySelector(el) : el;
19277    this.scroller = this.wrapper.children[0];
19278    this.scrollerStyle = this.scroller.style;       // cache style for better performance
19279
19280    this.options = {
19281
19282        resizeScrollbars: true,
19283
19284        mouseWheelSpeed: 20,
19285
19286        snapThreshold: 0.334,
19287
19288// INSERT POINT: OPTIONS
19289        disablePointer : !utils.hasPointer,
19290        disableTouch : utils.hasPointer || !utils.hasTouch,
19291        disableMouse : utils.hasPointer || utils.hasTouch,
19292        startX: 0,
19293        startY: 0,
19294        scrollY: true,
19295        directionLockThreshold: 5,
19296        momentum: true,
19297
19298        bounce: true,
19299        bounceTime: 600,
19300        bounceEasing: '',
19301
19302        preventDefault: true,
19303        preventDefaultException: { tagName: /^(INPUT|TEXTAREA|BUTTON|
vendor: 4,420 bytes, lines 19303-19447
19303SELECT|LABEL)$/ },
19304
19305        HWCompositing: true,
19306        useTransition: true,
19307        useTransform: true,
19308        bindToWrapper: typeof window.onmousedown === "undefined"
19309    };
19310
19311    for ( var i in options ) {
19312        this.options[i] = options[i];
19313    }
19314
19315    // Normalize options
19316    this.translateZ = this.options.HWCompositing && utils.hasPerspective ? ' translateZ(0)' : '';
19317
19318    this.options.useTransition = utils.hasTransition && this.options.useTransition;
19319    this.options.useTransform = utils.hasTransform && this.options.useTransform;
19320
19321    this.options.eventPassthrough = this.options.eventPassthrough === true ? 'vertical' : this.options.eventPassthrough;
19322    this.options.preventDefault = !this.options.eventPassthrough && this.options.preventDefault;
19323
19324    // If you want eventPassthrough I have to lock one of the axes
19325    this.options.scrollY = this.options.eventPassthrough == 'vertical' ? false : this.options.scrollY;
19326    this.options.scrollX = this.options.eventPassthrough == 'horizontal' ? false : this.options.scrollX;
19327
19328    // With eventPassthrough we also need lockDirection mechanism
19329    this.options.freeScroll = this.options.freeScroll && !this.options.eventPassthrough;
19330    this.options.directionLockThreshold = this.options.eventPassthrough ? 0 : this.options.directionLockThreshold;
19331
19332    this.options.bounceEasing = typeof this.options.bounceEasing == 'string' ? utils.ease[this.options.bounceEasing] || utils.ease.circular : this.options.bounceEasing;
19333
19334    this.options.resizePolling = this.options.resizePolling === undefined ? 60 : this.options.resizePolling;
19335
19336    if ( this.options.tap === true ) {
19337        this.options.tap = 'tap';
19338    }
19339
19340    // https://github.com/cubiq/iscroll/issues/1029
19341    if (!this.options.useTransition && !this.options.useTransform) {
19342        if(!(/relative|absolute/i).test(this.scrollerStyle.position)) {
19343            this.scrollerStyle.position = "relative";
19344        }
19345    }
19346
19347    if ( this.options.shrinkScrollbars == 'scale' ) {
19348        this.options.useTransition = false;
19349    }
19350
19351    this.options.invertWheelDirection = this.options.invertWheelDirection ? -1 : 1;
19352
19353// INSERT POINT: NORMALIZATION
19354
19355    // Some defaults
19356    this.x = 0;
19357    this.y = 0;
19358    this.directionX = 0;
19359    this.directionY = 0;
19360    this._events = {};
19361
19362// INSERT POINT: DEFAULTS
19363
19364    this._init();
19365    this.refresh();
19366
19367    this.scrollTo(this.options.startX, this.options.startY);
19368    this.enable();
19369}
19370
19371IScroll.prototype = {
19372    version: '5.2.0',
19373
19374    _init: function () {
19375        this._initEvents();
19376
19377        if ( this.options.scrollbars || this.options.indicators ) {
19378            this._initIndicators();
19379        }
19380
19381        if ( this.options.mouseWheel ) {
19382            this._initWheel();
19383        }
19384
19385        if ( this.options.snap ) {
19386            this._initSnap();
19387        }
19388
19389        if ( this.options.keyBindings ) {
19390            this._initKeys();
19391        }
19392
19393// INSERT POINT: _init
19394
19395    },
19396
19397    destroy: function () {
19398        this._initEvents(true);
19399        clearTimeout(this.resizeTimeout);
19400        this.resizeTimeout = null;
19401        this._execEvent('destroy');
19402    },
19403
19404    _transitionEnd: function (e) {
19405        if ( e.target != this.scroller || !this.isInTransition ) {
19406            return;
19407        }
19408
19409        this._transitionTime();
19410        if ( !this.resetPosition(this.options.bounceTime) ) {
19411            this.isInTransition = false;
19412            this._execEvent('scrollEnd');
19413        }
19414    },
19415
19416    _start: function (e) {
19417        // React to left mouse button only
19418        if ( utils.eventType[e.type] != 1 ) {
19419          // for button property
19420          // http://unixpapa.com/js/mouse.html
19421          var button;
19422        if (!e.which) {
19423          /* IE case */
19424          button = (e.button < 2) ? 0 :
19425                   ((e.button == 4) ? 1 : 2);
19426        } else {
19427          /* All others */
19428          button = e.button;
19429        }
19430            if ( button !== 0 ) {
19431                return;
19432            }
19433        }
19434
19435        if ( !this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated) ) {
19436            return;
19437        }
19438
19439        if ( this.options.preventDefault && !utils.isBadAndroid && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) {
19440            e.preventDefault();
19441        }
19442
19443        var point = e.touches ? e.touches[0] : e,
19444            pos;
19445
19446        this.initiated  = utils.eventType[e.type];
19447        this.moved      = false;
vendor: 5,928 bytes, lines 19448-19626
19448        this.distX      = 0;
19449        this.distY      = 0;
19450        this.directionX = 0;
19451        this.directionY = 0;
19452        this.directionLocked = 0;
19453
19454        this.startTime = utils.getTime();
19455
19456        if ( this.options.useTransition && this.isInTransition ) {
19457            this._transitionTime();
19458            this.isInTransition = false;
19459            pos = this.getComputedPosition();
19460            this._translate(Math.round(pos.x), Math.round(pos.y));
19461            this._execEvent('scrollEnd');
19462        } else if ( !this.options.useTransition && this.isAnimating ) {
19463            this.isAnimating = false;
19464            this._execEvent('scrollEnd');
19465        }
19466
19467        this.startX    = this.x;
19468        this.startY    = this.y;
19469        this.absStartX = this.x;
19470        this.absStartY = this.y;
19471        this.pointX    = point.pageX;
19472        this.pointY    = point.pageY;
19473
19474        this._execEvent('beforeScrollStart');
19475    },
19476
19477    _move: function (e) {
19478        if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) {
19479            return;
19480        }
19481
19482        if ( this.options.preventDefault ) {    // increases performance on Android? TODO: check!
19483            e.preventDefault();
19484        }
19485
19486        var point       = e.touches ? e.touches[0] : e,
19487            deltaX      = point.pageX - this.pointX,
19488            deltaY      = point.pageY - this.pointY,
19489            timestamp   = utils.getTime(),
19490            newX, newY,
19491            absDistX, absDistY;
19492
19493        this.pointX     = point.pageX;
19494        this.pointY     = point.pageY;
19495
19496        this.distX      += deltaX;
19497        this.distY      += deltaY;
19498        absDistX        = Math.abs(this.distX);
19499        absDistY        = Math.abs(this.distY);
19500
19501        // We need to move at least 10 pixels for the scrolling to initiate
19502        if ( timestamp - this.endTime > 300 && (absDistX < 10 && absDistY < 10) ) {
19503            return;
19504        }
19505
19506        // If you are scrolling in one direction lock the other
19507        if ( !this.directionLocked && !this.options.freeScroll ) {
19508            if ( absDistX > absDistY + this.options.directionLockThreshold ) {
19509                this.directionLocked = 'h';     // lock horizontally
19510            } else if ( absDistY >= absDistX + this.options.directionLockThreshold ) {
19511                this.directionLocked = 'v';     // lock vertically
19512            } else {
19513                this.directionLocked = 'n';     // no lock
19514            }
19515        }
19516
19517        if ( this.directionLocked == 'h' ) {
19518            if ( this.options.eventPassthrough == 'vertical' ) {
19519                e.preventDefault();
19520            } else if ( this.options.eventPassthrough == 'horizontal' ) {
19521                this.initiated = false;
19522                return;
19523            }
19524
19525            deltaY = 0;
19526        } else if ( this.directionLocked == 'v' ) {
19527            if ( this.options.eventPassthrough == 'horizontal' ) {
19528                e.preventDefault();
19529            } else if ( this.options.eventPassthrough == 'vertical' ) {
19530                this.initiated = false;
19531                return;
19532            }
19533
19534            deltaX = 0;
19535        }
19536
19537        deltaX = this.hasHorizontalScroll ? deltaX : 0;
19538        deltaY = this.hasVerticalScroll ? deltaY : 0;
19539
19540        newX = this.x + deltaX;
19541        newY = this.y + deltaY;
19542
19543        // Slow down if outside of the boundaries
19544        if ( newX > 0 || newX < this.maxScrollX ) {
19545            newX = this.options.bounce ? this.x + deltaX / 3 : newX > 0 ? 0 : this.maxScrollX;
19546        }
19547        if ( newY > 0 || newY < this.maxScrollY ) {
19548            newY = this.options.bounce ? this.y + deltaY / 3 : newY > 0 ? 0 : this.maxScrollY;
19549        }
19550
19551        this.directionX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0;
19552        this.directionY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0;
19553
19554        if ( !this.moved ) {
19555            this._execEvent('scrollStart');
19556        }
19557
19558        this.moved = true;
19559
19560        this._translate(newX, newY);
19561
19562/* REPLACE START: _move */
19563
19564        if ( timestamp - this.startTime > 300 ) {
19565            this.startTime = timestamp;
19566            this.startX = this.x;
19567            this.startY = this.y;
19568        }
19569
19570/* REPLACE END: _move */
19571
19572    },
19573
19574    _end: function (e) {
19575        if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) {
19576            return;
19577        }
19578
19579        if ( this.options.preventDefault && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) {
19580            e.preventDefault();
19581        }
19582
19583        var point = e.changedTouches ? e.changedTouches[0] : e,
19584            momentumX,
19585            momentumY,
19586            duration = utils.getTime() - this.startTime,
19587            newX = Math.round(this.x),
19588            newY = Math.round(this.y),
19589            distanceX = Math.abs(newX - this.startX),
19590            distanceY = Math.abs(newY - this.startY),
19591            time = 0,
19592            easing = '';
19593
19594        this.isInTransition = 0;
19595        this.initiated = 0;
19596        this.endTime = utils.getTime();
19597
19598        // reset if we are outside of the boundaries
19599        if ( this.resetPosition(this.options.bounceTime) ) {
19600            return;
19601        }
19602
19603        this.scrollTo(newX, newY);  // ensures that the last position is rounded
19604
19605        // we scrolled less than 10 pixels
19606        if ( !this.moved ) {
19607            if ( this.options.tap ) {
19608                utils.tap(e, this.options.tap);
19609            }
19610
19611            if ( this.options.click ) {
19612                utils.click(e);
19613            }
19614
19615            this._execEvent('scrollCancel');
19616            return;
19617        }
19618
19619        if ( this._events.flick && duration < 200 && distanceX < 100 && distanceY < 100 ) {
19620            this._execEvent('flick');
19621            return;
19622        }
19623
19624        // start momentum animation if needed
19625        if ( this.options.momentum && duration < 300 ) {
19626            momentumX = this.hasHorizontalScroll ? utils.momentum(this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperW
vendor: 7,740 bytes, lines 19626-19899
19626idth : 0, this.options.deceleration) : { destination: newX, duration: 0 };
19627            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 };
19628            newX = momentumX.destination;
19629            newY = momentumY.destination;
19630            time = Math.max(momentumX.duration, momentumY.duration);
19631            this.isInTransition = 1;
19632        }
19633
19634
19635        if ( this.options.snap ) {
19636            var snap = this._nearestSnap(newX, newY);
19637            this.currentPage = snap;
19638            time = this.options.snapSpeed || Math.max(
19639                    Math.max(
19640                        Math.min(Math.abs(newX - snap.x), 1000),
19641                        Math.min(Math.abs(newY - snap.y), 1000)
19642                    ), 300);
19643            newX = snap.x;
19644            newY = snap.y;
19645
19646            this.directionX = 0;
19647            this.directionY = 0;
19648            easing = this.options.bounceEasing;
19649        }
19650
19651// INSERT POINT: _end
19652
19653        if ( newX != this.x || newY != this.y ) {
19654            // change easing function when scroller goes out of the boundaries
19655            if ( newX > 0 || newX < this.maxScrollX || newY > 0 || newY < this.maxScrollY ) {
19656                easing = utils.ease.quadratic;
19657            }
19658
19659            this.scrollTo(newX, newY, time, easing);
19660            return;
19661        }
19662
19663        this._execEvent('scrollEnd');
19664    },
19665
19666    _resize: function () {
19667        var that = this;
19668
19669        clearTimeout(this.resizeTimeout);
19670
19671        this.resizeTimeout = setTimeout(function () {
19672            that.refresh();
19673        }, this.options.resizePolling);
19674    },
19675
19676    resetPosition: function (time) {
19677        var x = this.x,
19678            y = this.y;
19679
19680        time = time || 0;
19681
19682        if ( !this.hasHorizontalScroll || this.x > 0 ) {
19683            x = 0;
19684        } else if ( this.x < this.maxScrollX ) {
19685            x = this.maxScrollX;
19686        }
19687
19688        if ( !this.hasVerticalScroll || this.y > 0 ) {
19689            y = 0;
19690        } else if ( this.y < this.maxScrollY ) {
19691            y = this.maxScrollY;
19692        }
19693
19694        if ( x == this.x && y == this.y ) {
19695            return false;
19696        }
19697
19698        this.scrollTo(x, y, time, this.options.bounceEasing);
19699
19700        return true;
19701    },
19702
19703    disable: function () {
19704        this.enabled = false;
19705    },
19706
19707    enable: function () {
19708        this.enabled = true;
19709    },
19710
19711    refresh: function () {
19712        var rf = this.wrapper.offsetHeight;     // Force reflow
19713
19714        this.wrapperWidth   = this.wrapper.clientWidth;
19715        this.wrapperHeight  = this.wrapper.clientHeight;
19716
19717/* REPLACE START: refresh */
19718
19719        this.scrollerWidth  = this.scroller.offsetWidth;
19720        this.scrollerHeight = this.scroller.offsetHeight;
19721
19722        this.maxScrollX     = this.wrapperWidth - this.scrollerWidth;
19723        this.maxScrollY     = this.wrapperHeight - this.scrollerHeight;
19724
19725/* REPLACE END: refresh */
19726
19727        this.hasHorizontalScroll    = this.options.scrollX && this.maxScrollX < 0;
19728        this.hasVerticalScroll      = this.options.scrollY && this.maxScrollY < 0;
19729
19730        if ( !this.hasHorizontalScroll ) {
19731            this.maxScrollX = 0;
19732            this.scrollerWidth = this.wrapperWidth;
19733        }
19734
19735        if ( !this.hasVerticalScroll ) {
19736            this.maxScrollY = 0;
19737            this.scrollerHeight = this.wrapperHeight;
19738        }
19739
19740        this.endTime = 0;
19741        this.directionX = 0;
19742        this.directionY = 0;
19743
19744        this.wrapperOffset = utils.offset(this.wrapper);
19745
19746        this._execEvent('refresh');
19747
19748        this.resetPosition();
19749
19750// INSERT POINT: _refresh
19751
19752    },
19753
19754    on: function (type, fn) {
19755        if ( !this._events[type] ) {
19756            this._events[type] = [];
19757        }
19758
19759        this._events[type].push(fn);
19760    },
19761
19762    off: function (type, fn) {
19763        if ( !this._events[type] ) {
19764            return;
19765        }
19766
19767        var index = this._events[type].indexOf(fn);
19768
19769        if ( index > -1 ) {
19770            this._events[type].splice(index, 1);
19771        }
19772    },
19773
19774    _execEvent: function (type) {
19775        if ( !this._events[type] ) {
19776            return;
19777        }
19778
19779        var i = 0,
19780            l = this._events[type].length;
19781
19782        if ( !l ) {
19783            return;
19784        }
19785
19786        for ( ; i < l; i++ ) {
19787            this._events[type][i].apply(this, [].slice.call(arguments, 1));
19788        }
19789    },
19790
19791    scrollBy: function (x, y, time, easing) {
19792        x = this.x + x;
19793        y = this.y + y;
19794        time = time || 0;
19795
19796        this.scrollTo(x, y, time, easing);
19797    },
19798
19799    scrollTo: function (x, y, time, easing) {
19800        easing = easing || utils.ease.circular;
19801
19802        this.isInTransition = this.options.useTransition && time > 0;
19803        var transitionType = this.options.useTransition && easing.style;
19804        if ( !time || transitionType ) {
19805                if(transitionType) {
19806                    this._transitionTimingFunction(easing.style);
19807                    this._transitionTime(time);
19808                }
19809            this._translate(x, y);
19810        } else {
19811            this._animate(x, y, time, easing.fn);
19812        }
19813    },
19814
19815    scrollToElement: function (el, time, offsetX, offsetY, easing) {
19816        el = el.nodeType ? el : this.scroller.querySelector(el);
19817
19818        if ( !el ) {
19819            return;
19820        }
19821
19822        var pos = utils.offset(el);
19823
19824        pos.left -= this.wrapperOffset.left;
19825        pos.top  -= this.wrapperOffset.top;
19826
19827        // if offsetX/Y are true we center the element to the screen
19828        if ( offsetX === true ) {
19829            offsetX = Math.round(el.offsetWidth / 2 - this.wrapper.offsetWidth / 2);
19830        }
19831        if ( offsetY === true ) {
19832            offsetY = Math.round(el.offsetHeight / 2 - this.wrapper.offsetHeight / 2);
19833        }
19834
19835        pos.left -= offsetX || 0;
19836        pos.top  -= offsetY || 0;
19837
19838        pos.left = pos.left > 0 ? 0 : pos.left < this.maxScrollX ? this.maxScrollX : pos.left;
19839        pos.top  = pos.top  > 0 ? 0 : pos.top  < this.maxScrollY ? this.maxScrollY : pos.top;
19840
19841        time = time === undefined || time === null || time === 'auto' ? Math.max(Math.abs(this.x-pos.left), Math.abs(this.y-pos.top)) : time;
19842
19843        this.scrollTo(pos.left, pos.top, time, easing);
19844    },
19845
19846    _transitionTime: function (time) {
19847        if (!this.options.useTransition) {
19848            return;
19849        }
19850        time = time || 0;
19851        var durationProp = utils.style.transitionDuration;
19852        if(!durationProp) {
19853            return;
19854        }
19855
19856        this.scrollerStyle[durationProp] = time + 'ms';
19857
19858        if ( !time && utils.isBadAndroid ) {
19859            this.scrollerStyle[durationProp] = '0.0001ms';
19860            // remove 0.0001ms
19861            var self = this;
19862            rAF(function() {
19863                if(self.scrollerStyle[durationProp] === '0.0001ms') {
19864                    self.scrollerStyle[durationProp] = '0s';
19865                }
19866            });
19867        }
19868
19869
19870        if ( this.indicators ) {
19871            for ( var i = this.indicators.length; i--; ) {
19872                this.indicators[i].transitionTime(time);
19873            }
19874        }
19875
19876
19877// INSERT POINT: _transitionTime
19878
19879    },
19880
19881    _transitionTimingFunction: function (easing) {
19882        this.scrollerStyle[utils.style.transitionTimingFunction] = easing;
19883
19884
19885        if ( this.indicators ) {
19886            for ( var i = this.indicators.length; i--; ) {
19887                this.indicators[i].transitionTimingFunction(easing);
19888            }
19889        }
19890
19891
19892// INSERT POINT: _transitionTimingFunction
19893
19894    },
19895
19896    _translate: function (x, y) {
19897        //Uncode addition
19898        if ( (" " + this.wrapper.className + " ").replace(/[\n\t]/g, " ").indexOf(" no-scrolloverflow ") > -1 )
19899            return false;
vendor: 9,939 bytes, lines 19900-20211
19900
19901        if ( this.options.useTransform ) {
19902
19903/* REPLACE START: _translate */
19904
19905            this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ;
19906
19907/* REPLACE END: _translate */
19908
19909        } else {
19910            x = Math.round(x);
19911            y = Math.round(y);
19912            this.scrollerStyle.left = x + 'px';
19913            this.scrollerStyle.top = y + 'px';
19914        }
19915
19916        this.x = x;
19917        this.y = y;
19918
19919
19920        if ( this.indicators ) {
19921            for ( var i = this.indicators.length; i--; ) {
19922                this.indicators[i].updatePosition();
19923            }
19924        }
19925
19926        //Uncode addition
19927        var uncodevent = new CustomEvent('fp-slide-scroll');
19928        window.dispatchEvent(uncodevent);
19929
19930// INSERT POINT: _translate
19931
19932    },
19933
19934    _initEvents: function (remove) {
19935        var eventType = remove ? utils.removeEvent : utils.addEvent,
19936            target = this.options.bindToWrapper ? this.wrapper : window;
19937
19938        eventType(window, 'orientationchange', this);
19939        eventType(window, 'resize', this);
19940
19941        if ( this.options.click ) {
19942            eventType(this.wrapper, 'click', this, true);
19943        }
19944
19945        if ( !this.options.disableMouse ) {
19946            eventType(this.wrapper, 'mousedown', this);
19947            eventType(target, 'mousemove', this);
19948            eventType(target, 'mousecancel', this);
19949            eventType(target, 'mouseup', this);
19950        }
19951
19952        if ( utils.hasPointer && !this.options.disablePointer ) {
19953            eventType(this.wrapper, utils.prefixPointerEvent('pointerdown'), this);
19954            eventType(target, utils.prefixPointerEvent('pointermove'), this);
19955            eventType(target, utils.prefixPointerEvent('pointercancel'), this);
19956            eventType(target, utils.prefixPointerEvent('pointerup'), this);
19957        }
19958
19959        if ( utils.hasTouch && !this.options.disableTouch ) {
19960            eventType(this.wrapper, 'touchstart', this);
19961            eventType(target, 'touchmove', this);
19962            eventType(target, 'touchcancel', this);
19963            eventType(target, 'touchend', this);
19964        }
19965
19966        eventType(this.scroller, 'transitionend', this);
19967        eventType(this.scroller, 'webkitTransitionEnd', this);
19968        eventType(this.scroller, 'oTransitionEnd', this);
19969        eventType(this.scroller, 'MSTransitionEnd', this);
19970    },
19971
19972    getComputedPosition: function () {
19973        var matrix = window.getComputedStyle(this.scroller, null),
19974            x, y;
19975
19976        if ( this.options.useTransform ) {
19977            matrix = matrix[utils.style.transform].split(')')[0].split(', ');
19978            x = +(matrix[12] || matrix[4]);
19979            y = +(matrix[13] || matrix[5]);
19980        } else {
19981            x = +matrix.left.replace(/[^-\d.]/g, '');
19982            y = +matrix.top.replace(/[^-\d.]/g, '');
19983        }
19984
19985        return { x: x, y: y };
19986    },
19987    _initIndicators: function () {
19988        var interactive = this.options.interactiveScrollbars,
19989            customStyle = typeof this.options.scrollbars != 'string',
19990            indicators = [],
19991            indicator;
19992
19993        var that = this;
19994
19995        this.indicators = [];
19996
19997        if ( this.options.scrollbars ) {
19998            // Vertical scrollbar
19999            if ( this.options.scrollY ) {
20000                indicator = {
20001                    el: createDefaultScrollbar('v', interactive, this.options.scrollbars),
20002                    interactive: interactive,
20003                    defaultScrollbars: true,
20004                    customStyle: customStyle,
20005                    resize: this.options.resizeScrollbars,
20006                    shrink: this.options.shrinkScrollbars,
20007                    fade: this.options.fadeScrollbars,
20008                    listenX: false
20009                };
20010
20011                this.wrapper.appendChild(indicator.el);
20012                indicators.push(indicator);
20013            }
20014
20015            // Horizontal scrollbar
20016            if ( this.options.scrollX ) {
20017                indicator = {
20018                    el: createDefaultScrollbar('h', interactive, this.options.scrollbars),
20019                    interactive: interactive,
20020                    defaultScrollbars: true,
20021                    customStyle: customStyle,
20022                    resize: this.options.resizeScrollbars,
20023                    shrink: this.options.shrinkScrollbars,
20024                    fade: this.options.fadeScrollbars,
20025                    listenY: false
20026                };
20027
20028                this.wrapper.appendChild(indicator.el);
20029                indicators.push(indicator);
20030            }
20031        }
20032
20033        if ( this.options.indicators ) {
20034            // TODO: check concat compatibility
20035            indicators = indicators.concat(this.options.indicators);
20036        }
20037
20038        for ( var i = indicators.length; i--; ) {
20039            this.indicators.push( new Indicator(this, indicators[i]) );
20040        }
20041
20042        // TODO: check if we can use array.map (wide compatibility and performance issues)
20043        function _indicatorsMap (fn) {
20044            if (that.indicators) {
20045                for ( var i = that.indicators.length; i--; ) {
20046                    fn.call(that.indicators[i]);
20047                }
20048            }
20049        }
20050
20051        if ( this.options.fadeScrollbars ) {
20052            this.on('scrollEnd', function () {
20053                _indicatorsMap(function () {
20054                    this.fade();
20055                });
20056            });
20057
20058            this.on('scrollCancel', function () {
20059                _indicatorsMap(function () {
20060                    this.fade();
20061                });
20062            });
20063
20064            this.on('scrollStart', function () {
20065                _indicatorsMap(function () {
20066                    this.fade(1);
20067                });
20068            });
20069
20070            this.on('beforeScrollStart', function () {
20071                _indicatorsMap(function () {
20072                    this.fade(1, true);
20073                });
20074            });
20075        }
20076
20077
20078        this.on('refresh', function () {
20079            _indicatorsMap(function () {
20080                this.refresh();
20081            });
20082        });
20083
20084        this.on('destroy', function () {
20085            _indicatorsMap(function () {
20086                this.destroy();
20087            });
20088
20089            delete this.indicators;
20090        });
20091    },
20092
20093    _initWheel: function () {
20094        utils.addEvent(this.wrapper, 'wheel', this);
20095        utils.addEvent(this.wrapper, 'mousewheel', this);
20096        utils.addEvent(this.wrapper, 'DOMMouseScroll', this);
20097
20098        this.on('destroy', function () {
20099            clearTimeout(this.wheelTimeout);
20100            this.wheelTimeout = null;
20101            utils.removeEvent(this.wrapper, 'wheel', this);
20102            utils.removeEvent(this.wrapper, 'mousewheel', this);
20103            utils.removeEvent(this.wrapper, 'DOMMouseScroll', this);
20104        });
20105    },
20106
20107    _wheel: function (e) {
20108        if ( !this.enabled ) {
20109            return;
20110        }
20111
20112        var wheelDeltaX, wheelDeltaY,
20113            newX, newY,
20114            that = this;
20115
20116        if ( this.wheelTimeout === undefined ) {
20117            that._execEvent('scrollStart');
20118        }
20119
20120        // Execute the scrollEnd event after 400ms the wheel stopped scrolling
20121        clearTimeout(this.wheelTimeout);
20122        this.wheelTimeout = setTimeout(function () {
20123            if(!that.options.snap) {
20124                that._execEvent('scrollEnd');
20125            }
20126            that.wheelTimeout = undefined;
20127        }, 400);
20128
20129        if ( 'deltaX' in e ) {
20130            if (e.deltaMode === 1) {
20131                wheelDeltaX = -e.deltaX * this.options.mouseWheelSpeed;
20132                wheelDeltaY = -e.deltaY * this.options.mouseWheelSpeed;
20133            } else {
20134                wheelDeltaX = -e.deltaX;
20135                wheelDeltaY = -e.deltaY;
20136            }
20137        } else if ( 'wheelDeltaX' in e ) {
20138            wheelDeltaX = e.wheelDeltaX / 120 * this.options.mouseWheelSpeed;
20139            wheelDeltaY = e.wheelDeltaY / 120 * this.options.mouseWheelSpeed;
20140        } else if ( 'wheelDelta' in e ) {
20141            wheelDeltaX = wheelDeltaY = e.wheelDelta / 120 * this.options.mouseWheelSpeed;
20142        } else if ( 'detail' in e ) {
20143            wheelDeltaX = wheelDeltaY = -e.detail / 3 * this.options.mouseWheelSpeed;
20144        } else {
20145            return;
20146        }
20147
20148        wheelDeltaX *= this.options.invertWheelDirection;
20149        wheelDeltaY *= this.options.invertWheelDirection;
20150
20151        if ( !this.hasVerticalScroll ) {
20152            wheelDeltaX = wheelDeltaY;
20153            wheelDeltaY = 0;
20154        }
20155
20156        if ( this.options.snap ) {
20157            newX = this.currentPage.pageX;
20158            newY = this.currentPage.pageY;
20159
20160            if ( wheelDeltaX > 0 ) {
20161                newX--;
20162            } else if ( wheelDeltaX < 0 ) {
20163                newX++;
20164            }
20165
20166            if ( wheelDeltaY > 0 ) {
20167                newY--;
20168            } else if ( wheelDeltaY < 0 ) {
20169                newY++;
20170            }
20171
20172            this.goToPage(newX, newY);
20173
20174            return;
20175        }
20176
20177        newX = this.x + Math.round(this.hasHorizontalScroll ? wheelDeltaX : 0);
20178        newY = this.y + Math.round(this.hasVerticalScroll ? wheelDeltaY : 0);
20179
20180        this.directionX = wheelDeltaX > 0 ? -1 : wheelDeltaX < 0 ? 1 : 0;
20181        this.directionY = wheelDeltaY > 0 ? -1 : wheelDeltaY < 0 ? 1 : 0;
20182
20183        if ( newX > 0 ) {
20184            newX = 0;
20185        } else if ( newX < this.maxScrollX ) {
20186            newX = this.maxScrollX;
20187        }
20188
20189        if ( newY > 0 ) {
20190            newY = 0;
20191        } else if ( newY < this.maxScrollY ) {
20192            newY = this.maxScrollY;
20193        }
20194
20195        this.scrollTo(newX, newY, 0);
20196
20197// INSERT POINT: _wheel
20198    },
20199
20200    _initSnap: function () {
20201        this.currentPage = {};
20202
20203        if ( typeof this.options.snap == 'string' ) {
20204            this.options.snap = this.scroller.querySelectorAll(this.options.snap);
20205        }
20206
20207        this.on('refresh', function () {
20208            var i = 0, l,
20209                m = 0, n,
20210                cx, cy,
20211                x = 0, y,
vendor: 7,339 bytes, lines 20212-20465
20212                stepX = this.options.snapStepX || this.wrapperWidth,
20213                stepY = this.options.snapStepY || this.wrapperHeight,
20214                el;
20215
20216            this.pages = [];
20217
20218            if ( !this.wrapperWidth || !this.wrapperHeight || !this.scrollerWidth || !this.scrollerHeight ) {
20219                return;
20220            }
20221
20222            if ( this.options.snap === true ) {
20223                cx = Math.round( stepX / 2 );
20224                cy = Math.round( stepY / 2 );
20225
20226                while ( x > -this.scrollerWidth ) {
20227                    this.pages[i] = [];
20228                    l = 0;
20229                    y = 0;
20230
20231                    while ( y > -this.scrollerHeight ) {
20232                        this.pages[i][l] = {
20233                            x: Math.max(x, this.maxScrollX),
20234                            y: Math.max(y, this.maxScrollY),
20235                            width: stepX,
20236                            height: stepY,
20237                            cx: x - cx,
20238                            cy: y - cy
20239                        };
20240
20241                        y -= stepY;
20242                        l++;
20243                    }
20244
20245                    x -= stepX;
20246                    i++;
20247                }
20248            } else {
20249                el = this.options.snap;
20250                l = el.length;
20251                n = -1;
20252
20253                for ( ; i < l; i++ ) {
20254                    if ( i === 0 || el[i].offsetLeft <= el[i-1].offsetLeft ) {
20255                        m = 0;
20256                        n++;
20257                    }
20258
20259                    if ( !this.pages[m] ) {
20260                        this.pages[m] = [];
20261                    }
20262
20263                    x = Math.max(-el[i].offsetLeft, this.maxScrollX);
20264                    y = Math.max(-el[i].offsetTop, this.maxScrollY);
20265                    cx = x - Math.round(el[i].offsetWidth / 2);
20266                    cy = y - Math.round(el[i].offsetHeight / 2);
20267
20268                    this.pages[m][n] = {
20269                        x: x,
20270                        y: y,
20271                        width: el[i].offsetWidth,
20272                        height: el[i].offsetHeight,
20273                        cx: cx,
20274                        cy: cy
20275                    };
20276
20277                    if ( x > this.maxScrollX ) {
20278                        m++;
20279                    }
20280                }
20281            }
20282
20283            this.goToPage(this.currentPage.pageX || 0, this.currentPage.pageY || 0, 0);
20284
20285            // Update snap threshold if needed
20286            if ( this.options.snapThreshold % 1 === 0 ) {
20287                this.snapThresholdX = this.options.snapThreshold;
20288                this.snapThresholdY = this.options.snapThreshold;
20289            } else {
20290                this.snapThresholdX = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].width * this.options.snapThreshold);
20291                this.snapThresholdY = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].height * this.options.snapThreshold);
20292            }
20293        });
20294
20295        this.on('flick', function () {
20296            var time = this.options.snapSpeed || Math.max(
20297                    Math.max(
20298                        Math.min(Math.abs(this.x - this.startX), 1000),
20299                        Math.min(Math.abs(this.y - this.startY), 1000)
20300                    ), 300);
20301
20302            this.goToPage(
20303                this.currentPage.pageX + this.directionX,
20304                this.currentPage.pageY + this.directionY,
20305                time
20306            );
20307        });
20308    },
20309
20310    _nearestSnap: function (x, y) {
20311        if ( !this.pages.length ) {
20312            return { x: 0, y: 0, pageX: 0, pageY: 0 };
20313        }
20314
20315        var i = 0,
20316            l = this.pages.length,
20317            m = 0;
20318
20319        // Check if we exceeded the snap threshold
20320        if ( Math.abs(x - this.absStartX) < this.snapThresholdX &&
20321            Math.abs(y - this.absStartY) < this.snapThresholdY ) {
20322            return this.currentPage;
20323        }
20324
20325        if ( x > 0 ) {
20326            x = 0;
20327        } else if ( x < this.maxScrollX ) {
20328            x = this.maxScrollX;
20329        }
20330
20331        if ( y > 0 ) {
20332            y = 0;
20333        } else if ( y < this.maxScrollY ) {
20334            y = this.maxScrollY;
20335        }
20336
20337        for ( ; i < l; i++ ) {
20338            if ( x >= this.pages[i][0].cx ) {
20339                x = this.pages[i][0].x;
20340                break;
20341            }
20342        }
20343
20344        l = this.pages[i].length;
20345
20346        for ( ; m < l; m++ ) {
20347            if ( y >= this.pages[0][m].cy ) {
20348                y = this.pages[0][m].y;
20349                break;
20350            }
20351        }
20352
20353        if ( i == this.currentPage.pageX ) {
20354            i += this.directionX;
20355
20356            if ( i < 0 ) {
20357                i = 0;
20358            } else if ( i >= this.pages.length ) {
20359                i = this.pages.length - 1;
20360            }
20361
20362            x = this.pages[i][0].x;
20363        }
20364
20365        if ( m == this.currentPage.pageY ) {
20366            m += this.directionY;
20367
20368            if ( m < 0 ) {
20369                m = 0;
20370            } else if ( m >= this.pages[0].length ) {
20371                m = this.pages[0].length - 1;
20372            }
20373
20374            y = this.pages[0][m].y;
20375        }
20376
20377        return {
20378            x: x,
20379            y: y,
20380            pageX: i,
20381            pageY: m
20382        };
20383    },
20384
20385    goToPage: function (x, y, time, easing) {
20386        easing = easing || this.options.bounceEasing;
20387
20388        if ( x >= this.pages.length ) {
20389            x = this.pages.length - 1;
20390        } else if ( x < 0 ) {
20391            x = 0;
20392        }
20393
20394        if ( y >= this.pages[x].length ) {
20395            y = this.pages[x].length - 1;
20396        } else if ( y < 0 ) {
20397            y = 0;
20398        }
20399
20400        var posX = this.pages[x][y].x,
20401            posY = this.pages[x][y].y;
20402
20403        time = time === undefined ? this.options.snapSpeed || Math.max(
20404            Math.max(
20405                Math.min(Math.abs(posX - this.x), 1000),
20406                Math.min(Math.abs(posY - this.y), 1000)
20407            ), 300) : time;
20408
20409        this.currentPage = {
20410            x: posX,
20411            y: posY,
20412            pageX: x,
20413            pageY: y
20414        };
20415
20416        this.scrollTo(posX, posY, time, easing);
20417    },
20418
20419    next: function (time, easing) {
20420        var x = this.currentPage.pageX,
20421            y = this.currentPage.pageY;
20422
20423        x++;
20424
20425        if ( x >= this.pages.length && this.hasVerticalScroll ) {
20426            x = 0;
20427            y++;
20428        }
20429
20430        this.goToPage(x, y, time, easing);
20431    },
20432
20433    prev: function (time, easing) {
20434        var x = this.currentPage.pageX,
20435            y = this.currentPage.pageY;
20436
20437        x--;
20438
20439        if ( x < 0 && this.hasVerticalScroll ) {
20440            x = 0;
20441            y--;
20442        }
20443
20444        this.goToPage(x, y, time, easing);
20445    },
20446
20447    _initKeys: function (e) {
20448        // default key bindings
20449        var keys = {
20450            pageUp: 33,
20451            pageDown: 34,
20452            end: 35,
20453            home: 36,
20454            left: 37,
20455            up: 38,
20456            right: 39,
20457            down: 40
20458        };
20459        var i;
20460
20461        // if you give me characters I give you keycode
20462        if ( typeof this.options.keyBindings == 'object' ) {
20463            for ( i in this.options.keyBindings ) {
20464                if ( typeof this.options.keyBindings[i] == 'string' ) {
20465                    this.options.keyBindings[i] = this.options.keyBindings[i].toUpperCase().charCo
vendor: 11,097 bytes, lines 20465-20818
20465deAt(0);
20466                }
20467            }
20468        } else {
20469            this.options.keyBindings = {};
20470        }
20471
20472        for ( i in keys ) {
20473            this.options.keyBindings[i] = this.options.keyBindings[i] || keys[i];
20474        }
20475
20476        utils.addEvent(window, 'keydown', this);
20477
20478        this.on('destroy', function () {
20479            utils.removeEvent(window, 'keydown', this);
20480        });
20481    },
20482
20483    _key: function (e) {
20484        if ( !this.enabled ) {
20485            return;
20486        }
20487
20488        var snap = this.options.snap,   // we are using this alot, better to cache it
20489            newX = snap ? this.currentPage.pageX : this.x,
20490            newY = snap ? this.currentPage.pageY : this.y,
20491            now = utils.getTime(),
20492            prevTime = this.keyTime || 0,
20493            acceleration = 0.250,
20494            pos;
20495
20496        if ( this.options.useTransition && this.isInTransition ) {
20497            pos = this.getComputedPosition();
20498
20499            this._translate(Math.round(pos.x), Math.round(pos.y));
20500            this.isInTransition = false;
20501        }
20502
20503        this.keyAcceleration = now - prevTime < 200 ? Math.min(this.keyAcceleration + acceleration, 50) : 0;
20504
20505        switch ( e.keyCode ) {
20506            case this.options.keyBindings.pageUp:
20507                if ( this.hasHorizontalScroll && !this.hasVerticalScroll ) {
20508                    newX += snap ? 1 : this.wrapperWidth;
20509                } else {
20510                    newY += snap ? 1 : this.wrapperHeight;
20511                }
20512                break;
20513            case this.options.keyBindings.pageDown:
20514                if ( this.hasHorizontalScroll && !this.hasVerticalScroll ) {
20515                    newX -= snap ? 1 : this.wrapperWidth;
20516                } else {
20517                    newY -= snap ? 1 : this.wrapperHeight;
20518                }
20519                break;
20520            case this.options.keyBindings.end:
20521                newX = snap ? this.pages.length-1 : this.maxScrollX;
20522                newY = snap ? this.pages[0].length-1 : this.maxScrollY;
20523                break;
20524            case this.options.keyBindings.home:
20525                newX = 0;
20526                newY = 0;
20527                break;
20528            case this.options.keyBindings.left:
20529                newX += snap ? -1 : 5 + this.keyAcceleration>>0;
20530                break;
20531            case this.options.keyBindings.up:
20532                newY += snap ? 1 : 5 + this.keyAcceleration>>0;
20533                break;
20534            case this.options.keyBindings.right:
20535                newX -= snap ? -1 : 5 + this.keyAcceleration>>0;
20536                break;
20537            case this.options.keyBindings.down:
20538                newY -= snap ? 1 : 5 + this.keyAcceleration>>0;
20539                break;
20540            default:
20541                return;
20542        }
20543
20544        if ( snap ) {
20545            this.goToPage(newX, newY);
20546            return;
20547        }
20548
20549        if ( newX > 0 ) {
20550            newX = 0;
20551            this.keyAcceleration = 0;
20552        } else if ( newX < this.maxScrollX ) {
20553            newX = this.maxScrollX;
20554            this.keyAcceleration = 0;
20555        }
20556
20557        if ( newY > 0 ) {
20558            newY = 0;
20559            this.keyAcceleration = 0;
20560        } else if ( newY < this.maxScrollY ) {
20561            newY = this.maxScrollY;
20562            this.keyAcceleration = 0;
20563        }
20564
20565        this.scrollTo(newX, newY, 0);
20566
20567        this.keyTime = now;
20568    },
20569
20570    _animate: function (destX, destY, duration, easingFn) {
20571        var that = this,
20572            startX = this.x,
20573            startY = this.y,
20574            startTime = utils.getTime(),
20575            destTime = startTime + duration;
20576
20577        function step () {
20578            var now = utils.getTime(),
20579                newX, newY,
20580                easing;
20581
20582            if ( now >= destTime ) {
20583                that.isAnimating = false;
20584                that._translate(destX, destY);
20585
20586                if ( !that.resetPosition(that.options.bounceTime) ) {
20587                    that._execEvent('scrollEnd');
20588                }
20589
20590                return;
20591            }
20592
20593            now = ( now - startTime ) / duration;
20594            easing = easingFn(now);
20595            newX = ( destX - startX ) * easing + startX;
20596            newY = ( destY - startY ) * easing + startY;
20597            that._translate(newX, newY);
20598
20599            if ( that.isAnimating ) {
20600                rAF(step);
20601            }
20602        }
20603
20604        this.isAnimating = true;
20605        step();
20606    },
20607    handleEvent: function (e) {
20608        switch ( e.type ) {
20609            case 'touchstart':
20610            case 'pointerdown':
20611            case 'MSPointerDown':
20612            case 'mousedown':
20613                this._start(e);
20614                break;
20615            case 'touchmove':
20616            case 'pointermove':
20617            case 'MSPointerMove':
20618            case 'mousemove':
20619                this._move(e);
20620                break;
20621            case 'touchend':
20622            case 'pointerup':
20623            case 'MSPointerUp':
20624            case 'mouseup':
20625            case 'touchcancel':
20626            case 'pointercancel':
20627            case 'MSPointerCancel':
20628            case 'mousecancel':
20629                this._end(e);
20630                break;
20631            case 'orientationchange':
20632            case 'resize':
20633                this._resize();
20634                break;
20635            case 'transitionend':
20636            case 'webkitTransitionEnd':
20637            case 'oTransitionEnd':
20638            case 'MSTransitionEnd':
20639                this._transitionEnd(e);
20640                break;
20641            case 'wheel':
20642            case 'DOMMouseScroll':
20643            case 'mousewheel':
20644                this._wheel(e);
20645                break;
20646            case 'keydown':
20647                this._key(e);
20648                break;
20649            case 'click':
20650                if ( this.enabled && !e._constructed ) {
20651                    e.preventDefault();
20652                    e.stopPropagation();
20653                }
20654                break;
20655        }
20656    }
20657};
20658function createDefaultScrollbar (direction, interactive, type) {
20659    var scrollbar = document.createElement('div'),
20660        indicator = document.createElement('div');
20661
20662    if ( type === true ) {
20663        scrollbar.style.cssText = 'position:absolute;z-index:9999';
20664        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';
20665    }
20666
20667    indicator.className = 'iScrollIndicator';
20668
20669    if ( direction == 'h' ) {
20670        if ( type === true ) {
20671            scrollbar.style.cssText += ';height:7px;left:2px;right:2px;bottom:0';
20672            indicator.style.height = '100%';
20673        }
20674        scrollbar.className = 'iScrollHorizontalScrollbar';
20675    } else {
20676        if ( type === true ) {
20677            scrollbar.style.cssText += ';width:7px;bottom:2px;top:2px;right:1px';
20678            indicator.style.width = '100%';
20679        }
20680        scrollbar.className = 'iScrollVerticalScrollbar';
20681    }
20682
20683    scrollbar.style.cssText += ';overflow:hidden';
20684
20685    if ( !interactive ) {
20686        scrollbar.style.pointerEvents = 'none';
20687    }
20688
20689    scrollbar.appendChild(indicator);
20690
20691    return scrollbar;
20692}
20693
20694function Indicator (scroller, options) {
20695    this.wrapper = typeof options.el == 'string' ? document.querySelector(options.el) : options.el;
20696    this.wrapperStyle = this.wrapper.style;
20697    this.indicator = this.wrapper.children[0];
20698    this.indicatorStyle = this.indicator.style;
20699    this.scroller = scroller;
20700
20701    this.options = {
20702        listenX: true,
20703        listenY: true,
20704        interactive: false,
20705        resize: true,
20706        defaultScrollbars: false,
20707        shrink: false,
20708        fade: false,
20709        speedRatioX: 0,
20710        speedRatioY: 0
20711    };
20712
20713    for ( var i in options ) {
20714        this.options[i] = options[i];
20715    }
20716
20717    this.sizeRatioX = 1;
20718    this.sizeRatioY = 1;
20719    this.maxPosX = 0;
20720    this.maxPosY = 0;
20721
20722    if ( this.options.interactive ) {
20723        if ( !this.options.disableTouch ) {
20724            utils.addEvent(this.indicator, 'touchstart', this);
20725            utils.addEvent(window, 'touchend', this);
20726        }
20727        if ( !this.options.disablePointer ) {
20728            utils.addEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this);
20729            utils.addEvent(window, utils.prefixPointerEvent('pointerup'), this);
20730        }
20731        if ( !this.options.disableMouse ) {
20732            utils.addEvent(this.indicator, 'mousedown', this);
20733            utils.addEvent(window, 'mouseup', this);
20734        }
20735    }
20736
20737    if ( this.options.fade ) {
20738        this.wrapperStyle[utils.style.transform] = this.scroller.translateZ;
20739        var durationProp = utils.style.transitionDuration;
20740        if(!durationProp) {
20741            return;
20742        }
20743        this.wrapperStyle[durationProp] = utils.isBadAndroid ? '0.0001ms' : '0ms';
20744        // remove 0.0001ms
20745        var self = this;
20746        if(utils.isBadAndroid) {
20747            rAF(function() {
20748                if(self.wrapperStyle[durationProp] === '0.0001ms') {
20749                    self.wrapperStyle[durationProp] = '0s';
20750                }
20751            });
20752        }
20753        this.wrapperStyle.opacity = '0';
20754    }
20755}
20756
20757Indicator.prototype = {
20758    handleEvent: function (e) {
20759        switch ( e.type ) {
20760            case 'touchstart':
20761            case 'pointerdown':
20762            case 'MSPointerDown':
20763            case 'mousedown':
20764                this._start(e);
20765                break;
20766            case 'touchmove':
20767            case 'pointermove':
20768            case 'MSPointerMove':
20769            case 'mousemove':
20770                this._move(e);
20771                break;
20772            case 'touchend':
20773            case 'pointerup':
20774            case 'MSPointerUp':
20775            case 'mouseup':
20776            case 'touchcancel':
20777            case 'pointercancel':
20778            case 'MSPointerCancel':
20779            case 'mousecancel':
20780                this._end(e);
20781                break;
20782        }
20783    },
20784
20785    destroy: function () {
20786        if ( this.options.fadeScrollbars ) {
20787            clearTimeout(this.fadeTimeout);
20788            this.fadeTimeout = null;
20789        }
20790        if ( this.options.interactive ) {
20791            utils.removeEvent(this.indicator, 'touchstart', this);
20792            utils.removeEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this);
20793            utils.removeEvent(this.indicator, 'mousedown', this);
20794
20795            utils.removeEvent(window, 'touchmove', this);
20796            utils.removeEvent(window, utils.prefixPointerEvent('pointermove'), this);
20797            utils.removeEvent(window, 'mousemove', this);
20798
20799            utils.removeEvent(window, 'touchend', this);
20800            utils.removeEvent(window, utils.prefixPointerEvent('pointerup'), this);
20801            utils.removeEvent(window, 'mouseup', this);
20802        }
20803
20804        if ( this.options.defaultScrollbars ) {
20805            this.wrapper.parentNode.removeChild(this.wrapper);
20806        }
20807    },
20808
20809    _start: function (e) {
20810        var point = e.touches ? e.touches[0] : e;
20811
20812        e.preventDefault();
20813        e.stopPropagation();
20814
20815        this.transitionTime();
20816
20817        this.initiated = true;
20818        this.moved = false;
vendor: 9,615 bytes, lines 20819-21077
20819        this.lastPointX = point.pageX;
20820        this.lastPointY = point.pageY;
20821
20822        this.startTime  = utils.getTime();
20823
20824        if ( !this.options.disableTouch ) {
20825            utils.addEvent(window, 'touchmove', this);
20826        }
20827        if ( !this.options.disablePointer ) {
20828            utils.addEvent(window, utils.prefixPointerEvent('pointermove'), this);
20829        }
20830        if ( !this.options.disableMouse ) {
20831            utils.addEvent(window, 'mousemove', this);
20832        }
20833
20834        this.scroller._execEvent('beforeScrollStart');
20835    },
20836
20837    _move: function (e) {
20838        var point = e.touches ? e.touches[0] : e,
20839            deltaX, deltaY,
20840            newX, newY,
20841            timestamp = utils.getTime();
20842
20843        if ( !this.moved ) {
20844            this.scroller._execEvent('scrollStart');
20845        }
20846
20847        this.moved = true;
20848
20849        deltaX = point.pageX - this.lastPointX;
20850        this.lastPointX = point.pageX;
20851
20852        deltaY = point.pageY - this.lastPointY;
20853        this.lastPointY = point.pageY;
20854
20855        newX = this.x + deltaX;
20856        newY = this.y + deltaY;
20857
20858        this._pos(newX, newY);
20859
20860// INSERT POINT: indicator._move
20861
20862        e.preventDefault();
20863        e.stopPropagation();
20864    },
20865
20866    _end: function (e) {
20867        if ( !this.initiated ) {
20868            return;
20869        }
20870
20871        this.initiated = false;
20872
20873        e.preventDefault();
20874        e.stopPropagation();
20875
20876        utils.removeEvent(window, 'touchmove', this);
20877        utils.removeEvent(window, utils.prefixPointerEvent('pointermove'), this);
20878        utils.removeEvent(window, 'mousemove', this);
20879
20880        if ( this.scroller.options.snap ) {
20881            var snap = this.scroller._nearestSnap(this.scroller.x, this.scroller.y);
20882
20883            var time = this.options.snapSpeed || Math.max(
20884                    Math.max(
20885                        Math.min(Math.abs(this.scroller.x - snap.x), 1000),
20886                        Math.min(Math.abs(this.scroller.y - snap.y), 1000)
20887                    ), 300);
20888
20889            if ( this.scroller.x != snap.x || this.scroller.y != snap.y ) {
20890                this.scroller.directionX = 0;
20891                this.scroller.directionY = 0;
20892                this.scroller.currentPage = snap;
20893                this.scroller.scrollTo(snap.x, snap.y, time, this.scroller.options.bounceEasing);
20894            }
20895        }
20896
20897        if ( this.moved ) {
20898            this.scroller._execEvent('scrollEnd');
20899        }
20900    },
20901
20902    transitionTime: function (time) {
20903        time = time || 0;
20904        var durationProp = utils.style.transitionDuration;
20905        if(!durationProp) {
20906            return;
20907        }
20908
20909        this.indicatorStyle[durationProp] = time + 'ms';
20910
20911        if ( !time && utils.isBadAndroid ) {
20912            this.indicatorStyle[durationProp] = '0.0001ms';
20913            // remove 0.0001ms
20914            var self = this;
20915            rAF(function() {
20916                if(self.indicatorStyle[durationProp] === '0.0001ms') {
20917                    self.indicatorStyle[durationProp] = '0s';
20918                }
20919            });
20920        }
20921    },
20922
20923    transitionTimingFunction: function (easing) {
20924        this.indicatorStyle[utils.style.transitionTimingFunction] = easing;
20925    },
20926
20927    refresh: function () {
20928        this.transitionTime();
20929
20930        if ( this.options.listenX && !this.options.listenY ) {
20931            this.indicatorStyle.display = this.scroller.hasHorizontalScroll ? 'block' : 'none';
20932        } else if ( this.options.listenY && !this.options.listenX ) {
20933            this.indicatorStyle.display = this.scroller.hasVerticalScroll ? 'block' : 'none';
20934        } else {
20935            this.indicatorStyle.display = this.scroller.hasHorizontalScroll || this.scroller.hasVerticalScroll ? 'block' : 'none';
20936        }
20937
20938        if ( this.scroller.hasHorizontalScroll && this.scroller.hasVerticalScroll ) {
20939            utils.addClass(this.wrapper, 'iScrollBothScrollbars');
20940            utils.removeClass(this.wrapper, 'iScrollLoneScrollbar');
20941
20942            if ( this.options.defaultScrollbars && this.options.customStyle ) {
20943                if ( this.options.listenX ) {
20944                    this.wrapper.style.right = '8px';
20945                } else {
20946                    this.wrapper.style.bottom = '8px';
20947                }
20948            }
20949        } else {
20950            utils.removeClass(this.wrapper, 'iScrollBothScrollbars');
20951            utils.addClass(this.wrapper, 'iScrollLoneScrollbar');
20952
20953            if ( this.options.defaultScrollbars && this.options.customStyle ) {
20954                if ( this.options.listenX ) {
20955                    this.wrapper.style.right = '2px';
20956                } else {
20957                    this.wrapper.style.bottom = '2px';
20958                }
20959            }
20960        }
20961
20962        var r = this.wrapper.offsetHeight;  // force refresh
20963
20964        if ( this.options.listenX ) {
20965            this.wrapperWidth = this.wrapper.clientWidth;
20966            if ( this.options.resize ) {
20967                this.indicatorWidth = Math.max(Math.round(this.wrapperWidth * this.wrapperWidth / (this.scroller.scrollerWidth || this.wrapperWidth || 1)), 8);
20968                this.indicatorStyle.width = this.indicatorWidth + 'px';
20969            } else {
20970                this.indicatorWidth = this.indicator.clientWidth;
20971            }
20972
20973            this.maxPosX = this.wrapperWidth - this.indicatorWidth;
20974
20975            if ( this.options.shrink == 'clip' ) {
20976                this.minBoundaryX = -this.indicatorWidth + 8;
20977                this.maxBoundaryX = this.wrapperWidth - 8;
20978            } else {
20979                this.minBoundaryX = 0;
20980                this.maxBoundaryX = this.maxPosX;
20981            }
20982
20983            this.sizeRatioX = this.options.speedRatioX || (this.scroller.maxScrollX && (this.maxPosX / this.scroller.maxScrollX));
20984        }
20985
20986        if ( this.options.listenY ) {
20987            this.wrapperHeight = this.wrapper.clientHeight;
20988            if ( this.options.resize ) {
20989                this.indicatorHeight = Math.max(Math.round(this.wrapperHeight * this.wrapperHeight / (this.scroller.scrollerHeight || this.wrapperHeight || 1)), 8);
20990                this.indicatorStyle.height = this.indicatorHeight + 'px';
20991            } else {
20992                this.indicatorHeight = this.indicator.clientHeight;
20993            }
20994
20995            this.maxPosY = this.wrapperHeight - this.indicatorHeight;
20996
20997            if ( this.options.shrink == 'clip' ) {
20998                this.minBoundaryY = -this.indicatorHeight + 8;
20999                this.maxBoundaryY = this.wrapperHeight - 8;
21000            } else {
21001                this.minBoundaryY = 0;
21002                this.maxBoundaryY = this.maxPosY;
21003            }
21004
21005            this.maxPosY = this.wrapperHeight - this.indicatorHeight;
21006            this.sizeRatioY = this.options.speedRatioY || (this.scroller.maxScrollY && (this.maxPosY / this.scroller.maxScrollY));
21007        }
21008
21009        this.updatePosition();
21010    },
21011
21012    updatePosition: function () {
21013        var x = this.options.listenX && Math.round(this.sizeRatioX * this.scroller.x) || 0,
21014            y = this.options.listenY && Math.round(this.sizeRatioY * this.scroller.y) || 0;
21015
21016        if ( !this.options.ignoreBoundaries ) {
21017            if ( x < this.minBoundaryX ) {
21018                if ( this.options.shrink == 'scale' ) {
21019                    this.width = Math.max(this.indicatorWidth + x, 8);
21020                    this.indicatorStyle.width = this.width + 'px';
21021                }
21022                x = this.minBoundaryX;
21023            } else if ( x > this.maxBoundaryX ) {
21024                if ( this.options.shrink == 'scale' ) {
21025                    this.width = Math.max(this.indicatorWidth - (x - this.maxPosX), 8);
21026                    this.indicatorStyle.width = this.width + 'px';
21027                    x = this.maxPosX + this.indicatorWidth - this.width;
21028                } else {
21029                    x = this.maxBoundaryX;
21030                }
21031            } else if ( this.options.shrink == 'scale' && this.width != this.indicatorWidth ) {
21032                this.width = this.indicatorWidth;
21033                this.indicatorStyle.width = this.width + 'px';
21034            }
21035
21036            if ( y < this.minBoundaryY ) {
21037                if ( this.options.shrink == 'scale' ) {
21038                    this.height = Math.max(this.indicatorHeight + y * 3, 8);
21039                    this.indicatorStyle.height = this.height + 'px';
21040                }
21041                y = this.minBoundaryY;
21042            } else if ( y > this.maxBoundaryY ) {
21043                if ( this.options.shrink == 'scale' ) {
21044                    this.height = Math.max(this.indicatorHeight - (y - this.maxPosY) * 3, 8);
21045                    this.indicatorStyle.height = this.height + 'px';
21046                    y = this.maxPosY + this.indicatorHeight - this.height;
21047                } else {
21048                    y = this.maxBoundaryY;
21049                }
21050            } else if ( this.options.shrink == 'scale' && this.height != this.indicatorHeight ) {
21051                this.height = this.indicatorHeight;
21052                this.indicatorStyle.height = this.height + 'px';
21053            }
21054        }
21055
21056        this.x = x;
21057        this.y = y;
21058
21059        if ( this.scroller.options.useTransform ) {
21060            this.indicatorStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.scroller.translateZ;
21061        } else {
21062            this.indicatorStyle.left = x + 'px';
21063            this.indicatorStyle.top = y + 'px';
21064        }
21065    },
21066
21067    _pos: function (x, y) {
21068        if ( x < 0 ) {
21069            x = 0;
21070        } else if ( x > this.maxPosX ) {
21071            x = this.maxPosX;
21072        }
21073
21074        if ( y < 0 ) {
21075            y = 0;
21076        } else if ( y > this.maxPosY ) {
21077            y = this.maxPosY;
21078        }
21079
21080        x = this.options.listenX ? Math.round(x / this.sizeRatioX) : this.scroller.x;
21081        y = this.options.listenY ? Math.round(y / this.sizeRatioY) : this.scroller.y;
21082
21083        this.scroller.scrollTo(x, y);
21084    },
21085
21086    fade: function (val, hold) {
21087        if ( hold && !this.visible ) {
21088            return;
21089        }
21090
21091        clearTimeout(this.fadeTimeout);
21092        this.fadeTimeout = null;
21093
21094        var time = val ? 250 : 500,
21095            delay = val ? 0 : 300;
21096
21097        val = val ? '1' : '0';
21098
21099        this.wrapperStyle[utils.style.transitionDuration] = time + 'ms';
21100
21101        this.fadeTimeout = setTimeout((function (val) {
21102            this.wrapperStyle.opacity = val;
21103            this.visible = +val;
21104        }).bind(this, val), delay);
21105    }
21106};
21107
21108IScroll.utils = utils;
21109
21110if ( typeof module != 'undefined' && module.exports ) {
21111    module.exports = IScroll;
21112} else if ( typeof define == 'function' && define.amd ) {
21113        define( function () { return IScroll; } );
21114} else {
21115    window.IScroll = IScroll;
21116}
21117
21118})(window, document, Math);
21119/*!
21120 * fullPage 2.9.4
21121 * https://github.com/alvarotrigo/fullPage.js
21122 * @license MIT licensed
21123 *
21124 * Copyright (C) 2015 alvarotrigo.com - A project by Alvaro Trigo
21125 */
21126(function(global, factory) {
21127    'use strict';
21128    if (typeof define === 'function' && define.amd) {
21129        define(['jquery'], function($) {
21130          return factory($, global, global.document, global.Math);
21131        });
21132    } else if (typeof exports === "object" && exports) {
21133        module.exports = factory(require('jquery'), global, global.document, global.Math);
21134    } else {
21135        factory(jQuery, global, global.document, global.Math);
21136    }
21137})(typeof window !== 'undefined' ? window : this, function($, window, document, Math, undefined) {
21138    'use strict';
21139
21140    // keeping central set of classnames and selectors
21141    var WRAPPER =               'fullpage-wrapper';
21142    var WRAPPER_SEL =           '.' + WRAPPER;
21143
21144    // slimscroll
21145    var SCROLLABLE =            'fp-scrollable';
21146    var SCROLLABLE_SEL =        '.' + SCROLLABLE;
21147
21148    // util
21149    var RESPONSIVE =            'fp-responsive';
21150    var NO_TRANSITION =         'fp-notransition';
21151    var DESTROYED =             'fp-destroyed';
21152    var ENABLED =               'fp-enabled';
21153    var VIEWING_PREFIX =        'fp-viewing';
21154    var ACTIVE =                'active';
21155    var ACTIVE_SEL =            '.' + ACTIVE;
21156    var COMPLETELY =            'fp-completely';
21157    var COMPLETELY_SEL =        '.' + COMPLETELY;
21158
21159    // section
21160    var SECTION_DEFAULT_SEL =   '.section';
21161    var SECTION =               'fp-section';
21162    var SECTION_SEL =           '.' + SECTION;
21163    var SECTION_ACTIVE_SEL =    SECTION_SEL + ACTIVE_SEL;
21164    var SECTION_FIRST_SEL =     SECTION_SEL + ':first';
21165    var SECTION_LAST_SEL =      SECTION_SEL + ':last';
21166    var TABLE_CELL =            'fp-tableCell';
21167    var TABLE_CELL_SEL =        '.' + TABLE_CELL;
21168    var AUTO_HEIGHT =           'fp-auto-height';
21169    var AUTO_HEIGHT_SEL =       '.fp-auto-height';
21170    var NORMAL_SCROLL =         'fp-normal-scroll';
21171    var NORMAL_SCROLL_SEL =     '.fp-normal-scroll';
21172
21173    // section nav
21174    var SECTION_NAV =           'fp-nav';
21175    var SECTION_NAV_SEL =       '#' + SECTION_NAV;
21176    var SECTION_NAV_TOOLTIP =   'fp-tooltip';
21177    var SECTION_NAV_TOOLTIP_SEL='.'+SECTION_NAV_TOOLTIP;
21178    var SHOW_ACTIVE_TOOLTIP =   'fp-show-active';
21179
21180    // slide
21181    var SLIDE_DEFAULT_SEL =     '.slide';
21182    var SLIDE =                 'fp-slide';
21183    var SLIDE_SEL =             '.' + SLIDE;
21184    var SLIDE_ACTIVE_SEL =      SLIDE_SEL + ACTIVE_SEL;
21185    var SLIDES_WRAPPER =        'fp-slides';
21186    var SLIDES_WRAPPER_SEL =    '.' + SLIDES_WRAPPER;
21187    var SLIDES_CONTAINER =      'fp-slidesContainer';
21188    var SLIDES_CONTAINER_SEL =  '.' + SLIDES_CONTAINER;
21189    var TABLE =                 'fp-table';
21190
21191    // slide nav
21192    var SLIDES_NAV =            'fp-slidesNav';
21193    var SLIDES_NAV_SEL =        '.' + SLIDES_NAV;
21194    var SLIDES_NAV_LINK_SEL =   SLIDES_NAV_SEL + ' a';
21195    var SLIDES_ARROW =          'fp-controlArrow';
21196    var SLIDES_ARROW_SEL =      '.' + SLIDES_ARROW;
21197    var SLIDES_PREV =           'fp-prev';
21198    var SLIDES_PREV_SEL =       '.' + SLIDES_PREV;
21199    var SLIDES_ARROW_PREV =     SLIDES_ARROW + ' ' + SLIDES_PREV;
21200    var SLIDES_ARROW_PREV_SEL = SLIDES_ARROW_SEL + SLIDES_PREV_SEL;
21201    var SLIDES_NEXT =           'fp-next';
21202    var SLIDES_NEXT_SEL =       '.' + SLIDES_NEXT;
21203    var SLIDES_ARROW_NEXT =     SLIDES_ARROW + ' ' + SLIDES_NEXT;
21204    var SLIDES_ARROW_NEXT_SEL = SLIDES_ARROW_SEL + SLIDES_NEXT_SEL;
21205
21206    var $window = $(window);
21207    var $document = $(document);
21208
21209    // Default options for iScroll.js used when using scrollOverflow
21210    var iscrollOptions = {
21211        scrollbars: true,
21212        mouseWheel: true,
21213        hideScrollbars: false,
21214        fadeScrollbars: false,
21215        disableMouse: true,
21216        interactiveScrollbars: true,
21217        bounce: false,
21218    };
21219
21220    $.fn.fullpage = function(options) {
21221        //only once my friend!
21222        if($('html').hasClass(ENABLED)){ displayWarnings(); return; }
21223
21224        // common jQuery objects
21225        var $htmlBody = $('html, body');
21226        var $body = $('body');
21227
21228        var FP = $.fn.fullpage;
21229
21230        // Creating some defaults, extending them with any options that were provided
21231        options = $.extend({
21232            //navigation
21233            menu: false,
21234            anchors:[],
21235            lockAnchors: false,
21236            navigation: false,
21237            navigationPosition: 'right',
21238            navigationTooltips: [],
21239            showActiveTooltip: false,
21240            slidesNavigation: false,
21241            slidesNavPosition: 'bottom',
21242            scrollBar: false,
21243            hybrid: false,
21244
21245            //scrolling
21246            css3: true,
21247            scrollingSpeed: 700,
21248            autoScrolling: true,
21249            fitToSection: true,
21250            fitToSectionDelay: 1000,
21251            easing: 'easeInOutCubic',
21252            easingcss3: 'ease',
21253            loopBottom: false,
21254            loopTop: false,
21255            loopHorizontal: true,
21256            continuousVertical: false,
21257            continuousHorizontal: false,
21258            scrollHorizontally: false,
21259            interlockedSlides: false,
21260            dragAndMove: false,
21261            offsetSections: false,
21262            resetSliders: false,
21263            fadingEffect: false,
21264            normalScrollElements: null,
21265            scrollOverflow: false,
21266            scrollOverflowReset: false,
21267            scrollOverflowHandler: iscrollHandler,
21268            scrollOverflowOptions: null,
21269            touchSensitivity: 5,
21270            normalScrollElementTouchThreshold: 5,
21271            bigSectionsDestination: null,
21272
21273            //Accessibility
21274            keyboardScrolling: true,
21275            animateAnchor: true,
21276            recordHistory: true,
21277
21278            //design
21279            controlArrows: true,
21280            controlArrowColor: '#fff',
21281            verticalCentered: true,
21282            sectionsColor : [],
21283            paddingTop: 0,
21284            paddingBottom: 0,
21285            fixedElements: null,
21286            responsive: 0, //backwards compabitility with responsiveWiddth
21287            responsiveWidth: 0,
21288            responsiveHeight: 0,
21289            responsiveSlides: false,
21290            parallax: false,
21291            parallaxOptions: {
21292                type: 'reveal',
21293                percentage: 62,
21294                property: 'translate'
21295            },
21296
21297            //Custom selectors
21298            sectionSelector: SECTION_DEFAULT_SEL,
21299            slideSelector: SLIDE_DEFAULT_SEL,
21300
21301            //events
21302            afterLoad: null,
21303            onLeave: null,
21304            afterRender: null,
21305            afterResize: null,
21306            afterReBuild: null,
21307            afterSlideLoad: null,
21308            onSlideLeave: null,
21309            afterResponsive: null,
21310
21311            lazyLoading: true
21312        }, options);
21313
21314        //flag to avoid very fast sliding for landscape sliders
21315        var slideMoving = false;
21316
21317        var isTouchDevice = navigator.userAgent.match(/(iPhone|iPod|iPad|Android|playbook|silk|BlackBerry|BB10|Windows Phone|Tizen|Bada|webOS|IEMobile|Opera Mini)/);
21318        var isTouch = (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0) || (navigator.maxTouchPoints));
21319        var container = $(this);
21320        var windowsHeight = $window.height();
21321        var isResizing = false;
21322        var isWindowFocused = true;
21323        var lastScrolledDestiny;
21324        var lastScrolledSlide;
21325        var canScroll = true;
21326        var scrollings = [];
21327        var controlPressed;
21328        var startingSection;
21329        var isScrollAllowed = {};
21330        isScrollAllowed.m = {  'up':true, 'down':true, 'left':true, 'right':true };
21331        isScrollAllowed.k = $.extend(true,{}, isScrollAllowed.m);
21332        var MSPointer = getMSPointer();
21333        var events = {
21334            touchmove: 'ontouchmove' in window ? 'touchmove' :  MSPointer.move,
21335            touchstart: 'ontouchstart' in window ? 'touchstart' :  MSPointer.down
21336        };
21337
21338        //cheks for passive event support
21339        //added by Uncode from new fullPage updates
21340        var g_supportsPassive = false;
21341        try {
21342          var opts = Object.defineProperty({}, 'passive', {
21343            get: function() {
21344              g_supportsPassive = true;
21345            }
21346          });
21347          window.addEventListener("testPassive", null, opts);
21348          window.removeEventListener("testPassive", null, opts);
21349        } catch (e) {}
21350
21351        //timeouts
21352        var resizeId;
21353        var afterSectionLoadsId;
21354        var afterSlideLoadsId;
21355        var scrollId;
21356        var scrollId2;
21357        var keydownId;
21358        var originals = $.extend(true, {}, options); //deep copy
21359
21360        //Uncode addition
21361        var $masthead = $('#masthead');
21362        var hideMenu = !$('body').hasClass('vmenu') && $('body').hasClass('uncode-fp-menu-hide') ? true : false;
21363        var menuHeight = $masthead.hasClass('menu-transparent') || hideMenu ? 0 : UNCODE.menuHeight;
21364        var bodyBorder = UNCODE.bodyBorder;
21365        var adminBarHeight = UNCODE.adminBarHeight;
21366
21367        displayWarnings();
21368
21369        //fixing bug in iScroll with links: https://github.com/cubiq/iscroll/issues/783
21370        iscrollOptions.click = isTouch; // see #2035
21371
21372        //extending iScroll options with the user custom ones
21373        iscrollOptions = $.extend(iscrollOptions, options.scrollOverflowOptions);
21374
21375        //easeInOutCubic animation included in the plugin
21376        $.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;}});
21377
21378        /**
21379        * Sets the autoScroll option.
21380        * It changes the scroll bar visibility and the history of the site as a result.
21381        */
21382        function setAutoScrolling(value, type){
21383            //removing the transformation
21384            if(!value){
21385                silentScroll(0);
21386            }
21387
21388            setVariableState('autoScrolling', value, type);
21389
21390            var element = $(SECTION_ACTIVE_SEL);
21391
21392            if(options.autoScrolling && !options.scrollBar){
21393                $htmlBody.css({
21394                    'overflow' : 'hidden !important',
21395                    'height' : '100%'
21396                });
21397
21398                //Uncode addition
21399                //setRecordHistory(originals.recordHistory, 'internal');
21400
21401                //for IE touch devices
21402                container.css({
21403                    '-ms-touch-action': 'none',
21404                    'touch-action': 'none'
21405                });
21406
21407                if(element.length){
21408                    //moving the container up
21409                    silentScroll(element.position().top);
21410                }
21411
21412            }else{
21413                $htmlBody.css({
21414                    'overflow' : 'visible !important',
21415                    'height' : 'initial'
21416                });
21417
21418                //Uncode addition
21419                //setRecordHistory(false, 'internal');
21420
21421                //for IE touch devices
21422                container.css({
21423                    '-ms-touch-action': '',
21424                    'touch-action': ''
21425                });
21426
21427                //scrolling the page to the section with no animation
21428                if (element.length) {
21429                    $htmlBody.scrollTop(element.position().top);
21430                }
21431            }
21432        }
21433
21434        /**
21435        * Defines wheter to record the history for each hash change in the URL.
21436        */
21437        function setRecordHistory(value, type){
21438            setVariableState('recordHistory', value, type);
21439        }
21440
21441        /**
21442        * Defines the scrolling speed
21443        */
21444        function setScrollingSpeed(value, type){
21445            setVariableState('scrollingSpeed', value, type);
21446        }
21447
21448        /**
21449        * Sets fitToSection
21450        */
21451        function setFitToSection(value, type){
21452            setVariableState('fitToSection', value, type);
21453        }
21454
21455        /**
21456        * Sets lockAnchors
21457        */
21458        function setLockAnchors(value){
21459            options.lockAnchors = value;
21460        }
21461
21462        /**
21463        * Adds or remove the possiblity of scrolling through sections by using the mouse wheel or the trackpad.
21464        */
21465        function setMouseWheelScrolling(value){
21466            if(value){
21467                addMouseWheelHandler();
21468                addMiddleWheelHandler();
21469            }else{
21470                removeMouseWheelHandler();
21471                removeMiddleWheelHandler();
21472            }
21473        }
21474
21475        /**
21476        * Adds or remove the possibility of scrolling through sections by using the mouse wheel/trackpad or touch gestures.
21477        * Optionally a second parameter can be used to specify the direction for which the action will be applied.
21478        *
21479        * @param directions string containing the direction or directions separated by comma.
21480        */
21481        function setAllowScrolling(value, directions){
21482            if(typeof directions !== 'undefined'){
21483                directions = directions.replace(/ /g,'').split(',');
21484
21485                $.each(directions, function (index, direction){
21486                    setIsScrollAllowed(value, direction, 'm');
21487                });
21488            }
21489            else if(value){
21490                setMouseWheelScrolling(true);
21491                addTouchHandler();
21492            }else{
21493                setMouseWheelScrolling(false);
21494                removeTouchHandler();
21495            }
21496        }
21497
21498        /**
21499        * Adds or remove the possibility of scrolling through sections by using the keyboard arrow keys
21500        */
21501        function setKeyboardScrolling(value, directions){
21502            if(typeof directions !== 'undefined'){
21503                directions = directions.replace(/ /g,'').split(',');
21504
21505                $.each(directions, function (index, direction){
21506                    setIsScrollAllowed(value, direction, 'k');
21507                });
21508            }else{
21509                options.keyboardScrolling = value;
21510            }
21511        }
21512
21513        /**
21514        * Moves the page up one section.
21515        */
21516        function moveSectionUp(){
21517            var prev = $(SECTION_ACTIVE_SEL).prev(SECTION_SEL);
21518
21519            //looping to the bottom if there's no more sections above
21520            if (!prev.length && (options.loopTop || options.continuousVertical)) {
21521                prev = $(SECTION_SEL).last();
21522            }
21523
21524            if (prev.length) {
21525                scrollPage(prev, null, true);
21526            }
21527        }
21528
21529        /**
21530        * Moves the page down one section.
21531        */
21532        function moveSectionDown(){
21533            var next = $(SECTION_ACTIVE_SEL).next(SECTION_SEL);
21534
21535            //looping to the top if there's no more sections below
21536            if(!next.length &&
21537                (options.loopBottom || options.continuousVertical)){
21538                next = $(SECTION_SEL).first();
21539            }
21540
21541            if(next.length){
21542                scrollPage(next, null, false);
21543            }
21544        }
21545
21546        /**
21547        * Moves the page to the given section and slide with no animation.
21548        * Anchors or index positions can be used as params.
21549        */
21550        function silentMoveTo(sectionAnchor, slideAnchor){
21551            setScrollingSpeed (0, 'internal');
21552            moveTo(sectionAnchor, slideAnchor);
21553            setScrollingSpeed (originals.scrollingSpeed, 'internal');
21554        }
21555
21556        /**
21557        * Moves the page to the given section and slide.
21558        * Anchors or index positions can be used as params.
21559        */
21560        function moveTo(sectionAnchor, slideAnchor){
21561            var destiny = getSectionByAnchor(sectionAnchor);
21562
21563            if (typeof slideAnchor !== 'undefined'){
21564                scrollPageAndSlide(sectionAnchor, slideAnchor);
21565            }else if(destiny.length > 0){
21566                scrollPage(destiny);
21567            }
21568        }
21569
21570        /**
21571        * Slides right the slider of the active section.
21572        * Optional `section` param.
21573        */
21574        function moveSlideRight(section){
21575            moveSlide('right', section);
21576        }
21577
21578        /**
21579        * Slides left the slider of the active section.
21580        * Optional `section` param.
21581        */
21582        function moveSlideLeft(section){
21583            moveSlide('left', section);
21584        }
21585
21586        /**
21587         * When resizing is finished, we adjust the slides sizes and positions
21588         */
21589        function reBuild(resizing){
21590            if(container.hasClass(DESTROYED)){ return; }  //nothing to do if the plugin was destroyed
21591
21592            isResizing = true;
21593
21594            windowsHeight = $window.height();  //updating global var
21595
21596            $(SECTION_SEL).each(function(){
21597                var slidesWrap = $(this).find(SLIDES_WRAPPER_SEL);
21598                var slides = $(this).find(SLIDE_SEL);
21599
21600                //adjusting the height of the table-cell for IE and Firefox
21601                if(options.verticalCentered){
21602                    $(this).find(TABLE_CELL_SEL).css('height', getTableHeight($(this)) + 'px');
21603                }
21604
21605                $(this).css('height', windowsHeight + 'px');
21606
21607                //resizing the scrolling divs
21608                if(options.scrollOverflow){
21609                    if(slides.length){
21610                        slides.each(function(){
21611                            createScrollBar($(this));
21612                        });
21613                    }else{
21614                        createScrollBar($(this));
21615                    }
21616                }
21617
21618                //adjusting the position fo the FULL WIDTH slides...
21619                if (slides.length > 1) {
21620                    landscapeScroll(slidesWrap, slidesWrap.find(SLIDE_ACTIVE_SEL));
21621                }
21622            });
21623
21624            var activeSection = $(SECTION_ACTIVE_SEL);
21625            var sectionIndex = activeSection.index(SECTION_SEL);
21626
21627            //isn't it the first section?
21628            if(sectionIndex){
21629                //adjusting the position for the current section
21630                silentMoveTo(sectionIndex + 1);
21631            }
21632
21633            isResizing = false;
21634            $.isFunction( options.afterResize ) && resizing && options.afterResize.call(container);
21635            $.isFunction( options.afterReBuild ) && !resizing && options.afterReBuild.call(container);
21636        }
21637
21638        /**
21639        * Turns fullPage.js to normal scrolling mode when the viewport `width` or `height`
21640        * are smaller than the set limit values.
21641        */
21642        function setResponsive(active){
21643            var isResponsive = $body.hasClass(RESPONSIVE);
21644
21645            if(active){
21646                if(!isResponsive){
21647                    setAutoScrolling(false, 'internal');
21648                    setFitToSection(false, 'internal');
21649                    $(SECTION_NAV_SEL).hide();
21650                    $body.addClass(RESPONSIVE);
21651                    $.isFunction( options.afterResponsive ) && options.afterResponsive.call( container, active);
21652                }
21653            }
21654            else if(isResponsive){
21655                setAutoScrolling(originals.autoScrolling, 'internal');
21656                setFitToSection(originals.autoScrolling, 'internal');
21657                $(SECTION_NAV_SEL).show();
21658                $body.removeClass(RESPONSIVE);
21659                $.isFunction( options.afterResponsive ) && options.afterResponsive.call( container, active);
21660            }
21661        }
21662
21663        if($(this).length){
21664            //public functions
21665            FP.setAutoScrolling = setAutoScrolling;
21666            FP.setRecordHistory = setRecordHistory;
21667            FP.setScrollingSpeed = setScrollingSpeed;
21668            FP.setFitToSection = setFitToSection;
21669            FP.setLockAnchors = setLockAnchors;
21670            FP.setMouseWheelScrolling = setMouseWheelScrolling;
21671            FP.setAllowScrolling = setAllowScrolling;
21672            FP.setKeyboardScrolling = setKeyboardScrolling;
21673            FP.moveSectionUp = moveSectionUp;
21674            FP.moveSectionDown = moveSectionDown;
21675            FP.silentMoveTo = silentMoveTo;
21676            FP.moveTo = moveTo;
21677            FP.moveSlideRight = moveSlideRight;
21678            FP.moveSlideLeft = moveSlideLeft;
21679            FP.fitToSection = fitToSection;
21680            FP.reBuild = reBuild;
21681            FP.setResponsive = setResponsive;
21682            FP.destroy = destroy;
21683
21684            init();
21685
21686            bindEvents();
21687        }
21688
21689        function init(){
21690            //if css3 is not supported, it will use jQuery animations
21691            if(options.css3){
21692                options.css3 = support3d();
21693            }
21694
21695            options.scrollBar = options.scrollBar || options.hybrid;
21696
21697            setOptionsFromDOM();
21698            prepareDom();
21699            setAllowScrolling(true);
21700            setAutoScrolling(options.autoScrolling, 'internal');
21701            responsive();
21702
21703            //setting the class for the body element
21704            setBodyClass();
21705
21706            //Uncode addition
21707            // if(document.readyState === 'complete'){
21708            //     scrollToAnchor();
21709            // }
21710            // $window.on('load', scrollToAnchor);
21711        }
21712
21713        function bindEvents(){
21714            $window
21715                //when scrolling...
21716                //.on('scroll', scrollHandler)
21717
21718                //detecting any change on the URL to scroll to the given anchor link
21719                //(a way to detect back history button as we play with the hashes on the URL)
21720                .on('hashchange', hashChangeHandler)
21721
21722                //when opening a new tab (ctrl + t), `control` won't be pressed when coming back.
21723                .blur(blurHandler)
21724
21725                //when resizing the site, we adjust the heights of the sections, slimScroll...
21726                .resize(resizeHandler);
21727
21728            $document
21729                //Sliding with arrow keys, both, vertical and horizontal
21730                .keydown(keydownHandler)
21731
21732                //to prevent scrolling while zooming
21733                .keyup(keyUpHandler)
21734
21735                //Scrolls to the section when clicking the navigation bullet
21736                .on('click touchstart', SECTION_NAV_SEL + ' a', sectionBulletHandler)
21737
21738                //Scrolls the slider to the given slide destination for the given section
21739                .on('click touchstart', SLIDES_NAV_LINK_SEL, slideBulletHandler)
21740
21741                .on('click', SECTION_NAV_TOOLTIP_SEL, tooltipTextHandler);
21742
21743            //Scrolling horizontally when clicking on the slider controls.
21744            $(SECTION_SEL).on('click touchstart', SLIDES_ARROW_SEL, slideArrowHandler);
21745
21746            /**
21747            * Applying normalScroll elements.
21748            * Ignoring the scrolls over the specified selectors.
21749            */
21750            if(options.normalScrollElements){
21751                $document.on('mouseenter', options.normalScrollElements, function () {
21752                    setMouseWheelScrolling(false);
21753                });
21754
21755                $document.on('mouseleave', options.normalScrollElements, function(){
21756                    setMouseWheelScrolling(true);
21757                });
21758            }
21759        }
21760
21761        /**
21762        * Setting options from DOM elements if they are not provided.
21763        */
21764        function setOptionsFromDOM(){
21765            var sections = container.find(options.sectionSelector);
21766
21767            //no anchors option? Checking for them in the DOM attributes
21768            if(!options.anchors.length){
21769                options.anchors = sections.filter('[data-anchor]').map(function(){
21770                    return $(this).data('anchor').toString();
21771                }).get();
21772            }
21773
21774            //no tooltips option? Checking for them in the DOM attributes
21775            if(!options.navigationTooltips.length){
21776                options.navigationTooltips = sections.filter('[data-tooltip]').map(function(){
21777                    return $(this).data('tooltip').toString();
21778                }).get();
21779            }
21780        }
21781
21782        /**
21783        * Works over the DOM structure to set it up for the current fullpage options.
21784        */
21785        function prepareDom(){
21786            container.css({
21787                'height': '100%',
21788                'position': 'relative'
21789            });
21790
21791            //adding a class to recognize the container internally in the code
21792            container.addClass(WRAPPER);
21793            $('html').addClass(ENABLED);
21794
21795            windowsHeight = $window.height();
21796
21797            container.removeClass(DESTROYED); //in case it was destroyed before initializing it again
21798
21799            addInternalSelectors();
21800
21801             //styling the sections / slides / menu
21802            $(SECTION_SEL).each(function(index){
21803                var section = $(this);
21804                var slides = section.find(SLIDE_SEL);
21805                var numSlides = slides.length;
21806
21807                styleSection(section, index);
21808                styleMenu(section, index);
21809
21810                // if there's any slide
21811                if (numSlides > 0) {
21812                    styleSlides(section, slides, numSlides);
21813                }else{
21814                    if(options.verticalCentered){
21815                        addTableClass(section);
21816                    }
21817                }
21818            });
21819
21820            //fixed elements need to be moved out of the plugin container due to problems with CSS3.
21821            if(options.fixedElements && options.css3){
21822                $(options.fixedElements).appendTo($body);
21823            }
21824
21825            //vertical centered of the navigation + active bullet
21826            if(options.navigation){
21827                addVerticalNavigation();
21828            }
21829
21830            enableYoutubeAPI();
21831
21832            if(options.scrollOverflow){
21833                if(document.readyState === 'complete'){
21834                    createScrollBarHandler();
21835                }
21836                //after DOM and images are loaded
21837                $window.on('load', createScrollBarHandler);
21838            }else{
21839                afterRenderActions();
21840            }
21841        }
21842
21843        /**
21844        * Styles the horizontal slides for a section.
21845        */
21846        function styleSlides(section, slides, numSlides){
21847            var sliderWidth = numSlides * 100;
21848            var slideWidth = 100 / numSlides;
21849
21850            slides.wrapAll('<div class="' + SLIDES_CONTAINER + '" />');
21851            slides.parent().wrap('<div class="' + SLIDES_WRAPPER + '" />');
21852
21853            section.find(SLIDES_CONTAINER_SEL).css('width', sliderWidth + '%');
21854
21855            if(numSlides > 1){
21856                if(options.controlArrows){
21857                    createSlideArrows(section);
21858                }
21859
21860                if(options.slidesNavigation){
21861                    addSlidesNavigation(section, numSlides);
21862                }
21863            }
21864
21865            slides.each(function(index) {
21866                $(this).css('width', slideWidth + '%');
21867
21868                if(options.verticalCentered){
21869                    addTableClass($(this));
21870                }
21871            });
21872
21873            var startingSlide = section.find(SLIDE_ACTIVE_SEL);
21874
21875            //if the slide won't be an starting point, the default will be the first one
21876            //the active section isn't the first one? Is not the first slide of the first section? Then we load that section/sl
21876ide by default.
21877            if( startingSlide.length &&  ($(SECTION_ACTIVE_SEL).index(SECTION_SEL) !== 0 || ($(SECTION_ACTIVE_SEL).index(SECTION_SEL) === 0 && startingSlide.index() !== 0))){
21878                silentLandscapeScroll(startingSlide, 'internal');
21879            }else{
21880                slides.eq(0).addClass(ACTIVE);
21881            }
21882        }
21883
21884        /**
21885        * Styling vertical sections
21886        */
21887        function styleSection(section, index){
21888            //if no active section is defined, the 1st one will be the default one
21889            if(!index && $(SECTION_ACTIVE_SEL).length === 0) {
21890                section.addClass(ACTIVE);
21891            }
21892            startingSection = $(SECTION_ACTIVE_SEL);
21893
21894            section.css('height', windowsHeight + 'px');
21895
21896            if(options.paddingTop){
21897                section.css('padding-top', options.paddingTop);
21898            }
21899
21900            if(options.paddingBottom){
21901                section.css('padding-bottom', options.paddingBottom);
21902            }
21903
21904            if (typeof options.sectionsColor[index] !==  'undefined') {
21905                section.css('background-color', options.sectionsColor[index]);
21906            }
21907
21908            if (typeof options.anchors[index] !== 'undefined') {
21909                section.attr('data-anchor', options.anchors[index]);
21910            }
21911        }
21912
21913        /**
21914        * Sets the data-anchor attributes to the menu elements and activates the current one.
21915        */
21916        function styleMenu(section, index){
21917            if (typeof options.anchors[index] !== 'undefined') {
21918                //activating the menu / nav element on load
21919                if(section.hasClass(ACTIVE)){
21920                    activateMenuAndNav(options.anchors[index], index);
21921                }
21922            }
21923
21924            //moving the menu outside the main container if it is inside (avoid problems with fixed positions when using CSS3 tranforms)
21925            if(options.menu && options.css3 && $(options.menu).closest(WRAPPER_SEL).length){
21926                $(options.menu).appendTo($body);
21927            }
21928        }
21929
21930        /**
21931        * Adds internal classes to be able to provide customizable selectors
21932        * keeping the link with the style sheet.
21933        */
21934        function addInternalSelectors(){
21935            container.find(options.sectionSelector).addClass(SECTION);
21936            container.find(options.slideSelector).addClass(SLIDE);
21937        }
21938
21939        /**
21940        * Creates the control arrows for the given section
21941        */
21942        function createSlideArrows(section){
21943            section.find(SLIDES_WRAPPER_SEL).after('<div class="' + SLIDES_ARROW_PREV + '"></div><div class="' + SLIDES_ARROW_NEXT + '"></div>');
21944
21945            if(options.controlArrowColor!='#fff'){
21946                section.find(SLIDES_ARROW_NEXT_SEL).css('border-color', 'transparent transparent transparent '+options.controlArrowColor);
21947                section.find(SLIDES_ARROW_PREV_SEL).css('border-color', 'transparent '+ options.controlArrowColor + ' transparent transparent');
21948            }
21949
21950            if(!options.loopHorizontal){
21951                section.find(SLIDES_ARROW_PREV_SEL).hide();
21952            }
21953        }
21954
21955        /**
21956        * Creates a vertical navigation bar.
21957        */
21958        function addVerticalNavigation(){
21959            $body.append('<div id="' + SECTION_NAV + '"><ul></ul></div>');
21960            var nav = $(SECTION_NAV_SEL);
21961
21962            nav.addClass(function() {
21963                return options.showActiveTooltip ? SHOW_ACTIVE_TOOLTIP + ' ' + options.navigationPosition : options.navigationPosition;
21964            });
21965
21966            for (var i = 0; i < $(SECTION_SEL).length; i++) {
21967                var link = '';
21968                if (options.anchors.length) {
21969                    link = options.anchors[i];
21970                }
21971
21972                var li = '<li><a href="#' + link + '"><span></span></a>';
21973
21974                // Only add tooltip if needed (defined by user)
21975                var tooltip = options.navigationTooltips[i];
21976
21977                if (typeof tooltip !== 'undefined' && tooltip !== '') {
21978                    li += '<div class="' + SECTION_NAV_TOOLTIP + ' ' + options.navigationPosition + '">' + tooltip + '</div>';
21979                }
21980
21981                li += '</li>';
21982
21983                nav.find('ul').append(li);
21984            }
21985
21986            //centering it vertically
21987            $(SECTION_NAV_SEL).css('margin-top', '-' + ($(SECTION_NAV_SEL).height()/2) + 'px');
21988
21989            //activating the current active section
21990            $(SECTION_NAV_SEL).find('li').eq($(SECTION_ACTIVE_SEL).index(SECTION_SEL)).find('a').addClass(ACTIVE);
21991        }
21992
21993        /**
21994        * Creates the slim scroll scrollbar for the sections and slides inside them.
21995        */
21996        function createScrollBarHandler(){
21997            $(SECTION_SEL).each(function(){
21998                var slides = $(this).find(SLIDE_SEL);
21999
22000                if(slides.length){
22001                    slides.each(function(){
22002                        createScrollBar($(this));
22003                    });
22004                }else{
22005                    createScrollBar($(this));
22006                }
22007
22008            });
22009            afterRenderActions();
22010        }
22011
22012        /*
22013        * Enables the Youtube videos API so we can control their flow if necessary.
22014        */
22015        function enableYoutubeAPI(){
22016            container.find('iframe[src*="youtube.com/embed/"]').each(function(){
22017                addURLParam($(this), 'enablejsapi=1');
22018            });
22019        }
22020
22021        /**
22022        * Adds a new parameter and its value to the `src` of a given element
22023        */
22024        function addURLParam(element, newParam){
22025            var originalSrc = element.attr('src');
22026            element.attr('src', originalSrc + getUrlParamSign(originalSrc) + newParam);
22027        }
22028
22029        /*
22030        * Returns the prefix sign to use for a new parameter in an existen URL.
22031        *
22032        * @return {String}  ? | &
22033        */
22034        function getUrlParamSign(url){
22035            return ( !/\?/.test( url ) ) ? '?' : '&';
22036        }
22037
22038        /**
22039        * Actions and callbacks to fire afterRender
22040        */
22041        function afterRenderActions(){
22042            var section = $(SECTION_ACTIVE_SEL);
22043
22044            section.addClass(COMPLETELY);
22045
22046            if(options.scrollOverflowHandler.afterRender){
22047                options.scrollOverflowHandler.afterRender(section);
22048            }
22049            lazyLoad(section);
22050            playMedia(section);
22051            options.scrollOverflowHandler.afterLoad();
22052
22053            if(isDestinyTheStartingSection()){
22054                $.isFunction( options.afterLoad ) && options.afterLoad.call(section, section.data('anchor'), (section.index(SECTION_SEL) + 1));
22055            }
22056
22057            $.isFunction( options.afterRender ) && options.afterRender.call(container);
22058        }
22059
22060        /**
22061        * Determines if the URL anchor destiny is the starting section (the one using 'active' class before initialization)
22062        */
22063        function isDestinyTheStartingSection(){
22064            var anchors =  window.location.hash.replace('#', '').split('/');
22065            var destinationSection = getSectionByAnchor(decodeURIComponent(anchors[0]));
22066
22067            return !destinationSection.length || destinationSection.length && destinationSection.index() === startingSection.index();
22068        }
22069
22070
22071        var isScrolling = false;
22072        var lastScroll = 0;
22073
22074        //when scrolling...
22075        function scrollHandler(){
22076            var currentSection;
22077
22078            if(!options.autoScrolling || options.scrollBar){
22079                var currentScroll = $window.scrollTop();
22080                var scrollDirection = getScrollDirection(currentScroll);
22081                var visibleSectionIndex = 0;
22082                var screen_mid = currentScroll + ($window.height() / 2.0);
22083                var isAtBottom = $body.height() - $window.height() === currentScroll;
22084                var sections =  document.querySelectorAll(SECTION_SEL);
22085
22086                //when using `auto-height` for a small last section it won't be centered in the viewport
22087                if(isAtBottom){
22088                    visibleSectionIndex = sections.length - 1;
22089                }
22090                //is at top? when using `auto-height` for a small first section it won't be centered in the viewport
22091                else if(!currentScroll){
22092                    visibleSectionIndex = 0;
22093                }
22094
22095                //taking the section which is showing more content in the viewport
22096                else{
22097                    for (var i = 0; i < sections.length; ++i) {
22098                        var section = sections[i];
22099
22100                        // Pick the the last section which passes the middle line of the screen.
22101                        if (section.offsetTop <= screen_mid)
22102                        {
22103                            visibleSectionIndex = i;
22104                        }
22105                    }
22106                }
22107
22108                if(isCompletelyInViewPort(scrollDirection)){
22109                    if(!$(SECTION_ACTIVE_SEL).hasClass(COMPLETELY)){
22110                        $(SECTION_ACTIVE_SEL).addClass(COMPLETELY).siblings().removeClass(COMPLETELY);
22111                    }
22112                }
22113
22114                //geting the last one, the current one on the screen
22115                currentSection = $(sections).eq(visibleSectionIndex);
22116
22117                //setting the visible section as active when manually scrolling
22118                //executing only once the first time we reach the section
22119                if(!currentSection.hasClass(ACTIVE)){
22120                    isScrolling = true;
22121                    var leavingSection = $(SECTION_ACTIVE_SEL);
22122                    var leavingSectionIndex = leavingSection.index(SECTION_SEL) + 1;
22123                    var yMovement = getYmovement(currentSection);
22124                    var anchorLink  = currentSection.data('anchor');
22125                    var sectionIndex = currentSection.index(SECTION_SEL) + 1;
22126                    var activeSlide = currentSection.find(SLIDE_ACTIVE_SEL);
22127                    var slideIndex;
22128                    var slideAnchorLink;
22129
22130                    if(activeSlide.length){
22131                        slideAnchorLink = activeSlide.data('anchor');
22132                        slideIndex = activeSlide.index();
22133                    }
22134
22135                    if(canScroll){
22136                        currentSection.addClass(ACTIVE).siblings().removeClass(ACTIVE);
22137
22138                        $.isFunction( options.onLeave ) && options.onLeave.call( leavingSection, leavingSectionIndex, sectionIndex, yMovement);
22139                        $.isFunction( options.afterLoad ) && options.afterLoad.call( currentSection, anchorLink, sectionIndex);
22140
22141                        stopMedia(leavingSection);
22142                        lazyLoad(currentSection);
22143                        playMedia(currentSection);
22144
22145                        activateMenuAndNav(anchorLink, sectionIndex - 1);
22146
22147                        if(options.anchors.length){
22148                            //needed to enter in hashChange event when using the menu with anchor links
22149                            lastScrolledDestiny = anchorLink;
22150                        }
22151                        setState(slideIndex, slideAnchorLink, anchorLink, sectionIndex);
22152                    }
22153
22154                    //small timeout in order to avoid entering in hashChange event when scrolling is not finished yet
22155                    clearTimeout(scrollId);
22156                    scrollId = setTimeout(function(){
22157                        isScrolling = false;
22158                    }, 100);
22159                }
22160
22161                if(options.fitToSection){
22162                    //for the auto adjust of the viewport to fit a whole section
22163                    clearTimeout(scrollId2);
22164
22165                    scrollId2 = setTimeout(function(){
22166                        //checking it again in case it changed during the delay
22167                        if(options.fitToSection){
22168                            fitToSection();
22169                        }
22170                    }, options.fitToSectionDelay);
22171                }
22172            }
22173        }
22174
22175        /**
22176        * Fits the site to the nearest active section
22177        */
22178        function fitToSection(){
22179            //checking fitToSection again in case it was set to false before the timeout delay
22180            if(canScroll){
22181                //allows to scroll to an active section and
22182                //if the section is already active, we prevent firing callbacks
22183                isResizing = true;
22184
22185                scrollPage($(SECTION_ACTIVE_SEL));
22186                isResizing = false;
22187            }
22188        }
22189
22190        /**
22191        * Determines whether the active section has seen in its whole or not.
22192        */
22193        function isCompletelyInViewPort(movement){
22194            var top = $(SECTION_ACTIVE_SEL).position().top;
22195            var bottom = top + $window.height();
22196
22197            if(movement == 'up'){
22198                return bottom >= ($window.scrollTop() + $window.height());
22199            }
22200            return top <= $window.scrollTop();
22201        }
22202
22203        /**
22204        * Gets the directon of the the scrolling fired by the scroll event.
22205        */
22206        function getScrollDirection(currentScroll){
22207            var direction = currentScroll > lastScroll ? 'down' : 'up';
22208
22209            lastScroll = currentScroll;
22210
22211            //needed for auto-height sections to determine if we want to scroll to the top or bottom of the destination
22212            previousDestTop = currentScroll;
22213
22214            return direction;
22215        }
22216
22217        /**
22218        * Determines the way of scrolling up or down:
22219        * by 'automatically' scrolling a section or by using the default and normal scrolling.
22220        */
22221        function scrolling(type, scrollable){
22222            if (!isScrollAllowed.m[type]){
22223                return;
22224            }
22225            var check = (type === 'down') ? 'bottom' : 'top';
22226            var scrollSection = (type === 'down') ? moveSectionDown : moveSectionUp;
22227
22228            if(scrollable.length > 0 ){
22229                //is the scrollbar at the start/end of the scroll?
22230                if(options.scrollOverflowHandler.isScrolled(check, scrollable)){
22231                    scrollSection();
22232                }else{
22233                    return true;
22234                }
22235            }else{
22236                // moved up/down
22237                scrollSection();
22238            }
22239        }
22240
22241        /*
22242        * Preventing bouncing in iOS #2285
22243        */
22244        function preventBouncing(event){
22245            var e = event.originalEvent;
22246            if(!checkParentForNormalScrollElement(event.target) && options.autoScrolling && isReallyTouch(e) && isScrollAllowed.m.up){
22247                //preventing the easing on iOS devices
22248                event.preventDefault();
22249            }
22250        }
22251
22252        var touchStartY = 0;
22253        var touchStartX = 0;
22254        var touchEndY = 0;
22255        var touchEndX = 0;
22256
22257        /* Detecting touch events
22258
22259        * As we are changing the top property of the page on scrolling, we can not use the traditional way to detect it.
22260        * This way, the touchstart and the touch moves shows an small difference between them which is the
22261        * used one to determine the direction.
22262        */
22263        function touchMoveHandler(event){
22264            var e = event.originalEvent;
22265            var activeSection = $(e.target).closest(SECTION_SEL);
22266
22267            // additional: if one of the normalScrollElements isn't within options.normalScrollElementTouchThreshold hops up the DOM chain
22268            if (!checkParentForNormalScrollElement(event.target) && isReallyTouch(e) ) {
22269
22270                if(options.autoScrolling){
22271                    //preventing the easing on iOS devices
22272                    event.preventDefault();
22273                }
22274
22275                var scrollable = options.scrollOverflowHandler.scrollable(activeSection);
22276                var touchEvents = getEventsPage(e);
22277
22278                touchEndY = touchEvents.y;
22279                touchEndX = touchEvents.x;
22280
22281                //if movement in the X axys is greater than in the Y and the currect section has slides...
22282                if (activeSection.find(SLIDES_WRAPPER_SEL).length && Math.abs(touchStartX - touchEndX) > (Math.abs(touchStartY - touchEndY))) {
22283
22284                    //is the movement greater than the minimum resistance to scroll?
22285                    if (!slideMoving && Math.abs(touchStartX - touchEndX) > ($window.outerWidth() / 100 * options.touchSensitivity)) {
22286                        if (touchStartX > touchEndX) {
22287                            if(isScrollAllowed.m.right){
22288                                moveSlideRight(activeSection); //next
22289                            }
22290                        } else {
22291                            if(isScrollAllowed.m.left){
22292                                moveSlideLeft(activeSection); //prev
22293                            }
22294                        }
22295                    }
22296                }
22297
22298                //vertical scrolling (only when autoScrolling is enabled)
22299                else if(options.autoScrolling && canScroll){
22300
22301                    //is the movement greater than the minimum resistance to scroll?
22302                    if (Math.abs(touchStartY - touchEndY) > ($window.height() / 100 * options.touchSensitivity)) {
22303                        if (touchStartY > touchEndY) {
22304                            scrolling('down', scrollable);
22305                        } else if (touchEndY > touchStartY) {
22306                            scrolling('up', scrollable);
22307                        }
22308                    }
22309                }
22310            }
22311        }
22312
22313        /**
22314         * recursive function to loop up the parent nodes to check if one of them exists in options.normalScrollElements
22315         * Currently works well for iOS - Android might need some testing
22316         * @param  {Element} el  target element / jquery selector (in subsequent nodes)
22317         * @param  {int}     hop current hop compared to options.normalScrollElementTouchThreshold
22318         * @return {boolean} true if there is a match to options.normalScrollElements
22319         */
22320        function checkParentForNormalScrollElement (el, hop) {
22321            hop = hop || 0;
22322            var parent = $(el).parent();
22323
22324            if (hop < options.normalScrollElementTouchThreshold &&
22325                parent.is(options.normalScrollElements) ) {
22326                return true;
22327            } else if (hop == options.normalScrollElementTouchThreshold) {
22328                return false;
22329            } else {
22330                return checkParentForNormalScrollElement(parent, ++hop);
22331            }
22332        }
22333
22334        /**
22335        * As IE >= 10 fires both touch and mouse events when using a mouse in a touchscreen
22336        * this way we make sure that is really a touch event what IE is detecting.
22337        */
22338        function isReallyTouch(e){
22339            //if is not IE   ||  IE is detecting `touch` or `pen`
22340            return typeof e.pointerType === 'undefined' || e.pointerType != 'mouse';
22341        }
22342
22343        /**
22344        * Handler for the touch start event.
22345        */
22346        function touchStartHandler(event){
22347            var e = event.originalEvent;
22348
22349            //stopping the auto scroll to adjust to a section
22350            if(options.fitToSection){
22351                $htmlBody.stop();
22352            }
22353
22354            if(isReallyTouch(e)){
22355                var touchEvents = getEventsPage(e);
22356                touchStartY = touchEvents.y;
22357                touchStartX = touchEvents.x;
22358            }
22359        }
22360
22361        /**
22362        * Gets the average of the last `number` elements of the given array.
22363        */
22364        function getAverage(elements, number){
22365            var sum = 0;
22366
22367            //taking `number` elements from the end to make the average, if there are not enought, 1
22368            var lastElements = elements.slice(Math.max(elements.length - number, 1));
22369
22370            for(var i = 0; i < lastElements.length; i++){
22371                sum = sum + lastElements[i];
22372            }
22373
22374            return Math.ceil(sum/number);
22375        }
22376
22377        /**
22378         * Detecting mousewheel scrolling
22379         *
22380         * http://blogs.sitepointstatic.com/examples/tech/mouse-wheel/index.html
22381         * http://www.sitepoint.com/html5-javascript-mouse-wheel/
22382         */
22383        var prevTime = new Date().getTime();
22384
22385        function MouseWheelHandler(e) {
22386            var curTime = new Date().getTime();
22387            var isNormalScroll = $(COMPLETELY_SEL).hasClass(NORMAL_SCROLL);
22388
22389            //autoscrolling and not zooming?
22390            if(options.autoScrolling && !controlPressed && !isNormalScroll){
22391                // cross-browser wheel delta
22392                e = e || window.event;
22393                var value = e.wheelDelta || -e.deltaY || -e.detail;
22394                var delta = Math.max(-1, Math.min(1, value));
22395
22396                var horizontalDetection = typeof e.wheelDeltaX !== 'undefined' || typeof e.deltaX !== 'undefined';
22397                var isScrollingVertically = (Math.abs(e.wheelDeltaX) < Math.abs(e.wheelDelta)) || (Math.abs(e.deltaX ) < Math.abs(e.deltaY) || !horizontalDetection);
22398
22399                //Limiting the array to 150 (lets not waste memory!)
22400                if(scrollings.length > 149){
22401                    scrollings.shift();
22402                }
22403
22404                //keeping record of the previous scrollings
22405                scrollings.push(Math.abs(value));
22406
22407                //preventing to scroll the site on mouse wheel when scrollbar is present
22408                if(options.scrollBar){
22409                    e.preventDefault ? e.preventDefault() : e.returnValue = false;
22410                }
22411
22412                var activeSection = $(SECTION_ACTIVE_SEL);
22413                var scrollable = options.scrollOverflowHandler.scrollable(activeSection);
22414
22415                //time difference between the last scroll and the current one
22416                var timeDiff = curTime-prevTime;
22417                prevTime = curTime;
22418
22419                //haven't they scrolled in a while?
22420                //(enough to be consider a different scrolling action to scroll another section)
22421                if(timeDiff > 200){
22422                    //emptying the array, we dont care about old scrollings for our averages
22423                    scrollings = [];
22424                }
22425
22426                if(canScroll){
22427                    var averageEnd = getAverage(scrollings, 10);
22428                    var averageMiddle = getAverage(scrollings, 70);
22429                    var isAccelerating = averageEnd >= averageMiddle;
22430
22431                    //to avoid double swipes...
22432                    if(isAccelerating && isScrollingVertically){
22433                        //scrolling down?
22434                        if (delta < 0) {
22435                            scrolling('down', scrollable);
22436
22437                        //scrolling up?
22438                        }else {
22439                            scrolling('up', scrollable);
22440                        }
22441                    }
22442                }
22443
22444                return false;
22445            }
22446
22447            if(options.fitToSection){
22448                //stopping the auto scroll to adjust to a section
22449                $htmlBody.stop();
22450            }
22451        }
22452
22453        /**
22454        * Slides a slider to the given direction.
22455        * Optional `section` param.
22456        */
22457        function moveSlide(direction, section){
22458            var activeSection = typeof section === 'undefined' ? $(SECTION_ACTIVE_SEL) : section;
22459            var slides = activeSection.find(SLIDES_WRAPPER_SEL);
22460            var numSlides = slides.find(SLIDE_SEL).length;
22461
22462            // more than one slide needed and nothing should be sliding
22463            if (!slides.length || slideMoving || numSlides < 2) {
22464                return;
22465            }
22466
22467            var currentSlide = slides.find(SLIDE_ACTIVE_SEL);
22468            var destiny = null;
22469
22470            if(direction === 'left'){
22471                destiny = currentSlide.prev(SLIDE_SEL);
22472            }else{
22473                destiny = currentSlide.next(SLIDE_SEL);
22474            }
22475
22476            //isn't there a next slide in the secuence?
22477            if(!destiny.length){
22478                //respect loopHorizontal settin
22479                if (!options.loopHorizontal) return;
22480
22481                if(direction === 'left'){
22482                    destiny = currentSlide.siblings(':last');
22483                }else{
22484                    destiny = currentSlide.siblings(':first');
22485                }
22486            }
22487
22488            slideMoving = true;
22489
22490            landscapeScroll(slides, destiny, direction);
22491        }
22492
22493        /**
22494        * Maintains the active slides in the viewport
22495        * (Because the `scroll` animation might get lost with some actions, such as when using continuousVertical)
22496        */
22497        function keepSlidesPosition(){
22498            $(SLIDE_ACTIVE_SEL).each(function(){
22499                silentLandscapeScroll($(this), 'internal');
22500            });
22501        }
22502
22503        var previousDestTop = 0;
22504        /**
22505        * Returns the destination Y position based on the scrolling direction and
22506        * the height of the section.
22507        */
22508        function getDestinationPosition(element){
22509            var elemPosition = element.position();
22510
22511            //top of the desination will be at the top of the viewport
22512            var position = elemPosition.top;
22513            var isScrollingDown =  elemPosition.top > previousDestTop;
22514            var sectionBottom = position - windowsHeight + element.outerHeight();
22515            var bigSectionsDestination = options.bigSectionsDestination;
22516
22517            //Uncode addition
22518            var containerH = container.outerHeight();
22519            var containerPosition = container.offset();
22520
22521            //########### Commented by Uncode - START ###########
22522            //is the destination element bigger than the viewport?
22523            // if(element.outerHeight() > windowsHeight){
22524            //     //scrolling up?
22525            //     if(!isScrollingDown && !bigSectionsDestination || bigSectionsDestination === 'bottom' ){
22526            //         position = sectionBottom;
22527            //     }
22528            // }
22529
22530            // //sections equal or smaller than the viewport height && scrolling down? ||  is resizing and its in the last section
22531            // else if(isScrollingDown || (isResizing && element.is(':last-child')) ){
22532            //     //The bottom of the destination will be at the bottom of the viewport
22533            //     position = sectionBottom;
22534            // }
22535            //########### Commented by Uncode - END ###########
22536
22537            //Uncode addition
22538            if ( !$masthead.hasClass('menu-transparent') && $('body').hasClass('uncode-fp-menu-shrink') && !element.is(':first-child') )
22539                position += 18;
22540            if ( ( containerH + menuHeight + bodyBorder + adminBarHeight - windowsHeight ) < position || ( isResizing && element.is(':last-child') ) ) {
22541                position = sectionBottom + menuHeight + bodyBorder*2 + adminBarHeight;
22542            }
22543
22544            /*
22545            Keeping record of the last scrolled position to determine the scrolling direction.
22546            No conventional methods can be used as the scroll bar might not be present
22547            AND the section might not be active if it is auto-height and didnt reach the middle
22548            of the viewport.
22549            */
22550            previousDestTop = position;
22551            return position;
22552        }
22553
22554        /**
22555        * Scrolls the site to the given element and scrolls to the slide if a callback is given.
22556        */
22557        function scrollPage(element, callback, isMovementUp){
22558            if(typeof element === 'undefined'){ return; } //there's no element to scroll, leaving the function
22559
22560            var dtop = getDestinationPosition(element);
22561            var slideAnchorLink;
22562            var slideIndex;
22563
22564            //local variables
22565            var v = {
22566                element: element,
22567                callback: callback,
22568                isMovementUp: isMovementUp,
22569                dtop: dtop,
22570                yMovement: getYmovement(element),
22571                anchorLink: element.data('anchor'),
22572                sectionIndex: element.index(SECTION_SEL),
22573                activeSlide: element.find(SLIDE_ACTIVE_SEL),
22574                activeSection: $(SECTION_ACTIVE_SEL),
22575                leavingSection: $(SECTION_ACTIVE_SEL).index(SECTION_SEL) + 1,
22576
22577                //caching the value of isResizing at the momment the function is called
22578                //because it will be checked later inside a setTimeout and the value might change
22579                localIsResizing: isResizing
22580            };
22581
22582            //quiting when destination scroll is the same as the current one
22583            if((v.activeSection.is(element) && !isResizing) || (options.scrollBar && $window.scrollTop() === v.dtop && !element.hasClass(AUTO_HEIGHT) )){ return; }
22584
22585            if(v.activeSlide.length){
22586                slideAnchorLink = v.activeSlide.data('anchor');
22587                slideIndex = v.activeSlide.index();
22588            }
22589
22590            // If continuousVertical && we need to wrap around
22591            if (options.autoScrolling && options.continuousVertical && typeof (v.isMovementUp) !== "undefined" &&
22592                ((!v.isMovementUp && v.yMovement == 'up') || // Intending to scroll down but about to go up or
22593                (v.isMovementUp && v.yMovement == 'down'))) { // intending to scroll up but about to go down
22594
22595                v = createInfiniteSections(v);
22596            }
22597
22598            //callback (onLeave) if the site is not just resizing and readjusting the slides
22599            if($.isFunction(options.onLeave) && !v.localIsResizing){
22600                if(options.onLeave.call(v.activeSection, v.leavingSection, (v.sectionIndex + 1), v.yMovement) === false){
22601                    return;
22602                }
22603            }
22604
22605            //pausing media of the leaving section (if we are not just resizing, as destinatino will be the same one)
22606            if(!v.localIsResizing){
22607                stopMedia(v.activeSection);
22608            }
22609
22610            options.scrollOverflowHandler.beforeLeave();
22611            element.addClass(ACTIVE).siblings().removeClass(ACTIVE);
22612            lazyLoad(element);
22613            options.scrollOverflowHandler.onLeave();
22614
22615
22616            //preventing from activating the MouseWheelHandler event
22617            //more than once if the page is scrolling
22618            canScroll = false;
22619
22620            setState(slideIndex, slideAnchorLink, v.anchorLink, v.sectionIndex);
22621
22622            performMovement(v);
22623
22624            //flag to avoid callingn `scrollPage()` twice in case of using anchor links
22625            lastScrolledDestiny = v.anchorLink;
22626
22627            //avoid firing it twice (as it does also on scroll)
22628            activateMenuAndNav(v.anchorLink, v.sectionIndex);
22629        }
22630
22631        /**
22632        * Performs the vertical movement (by CSS3 or by jQuery)
22633        */
22634        function performMovement(v){
22635            // using CSS3 translate functionality
22636            if (options.css3 && options.autoScrolling && !options.scrollBar) {
22637
22638                // The first section can have a negative value in iOS 10. Not quite sure why: -0.0142822265625
22639                // that's why we round it to 0.
22640                var translate3d = 'translate3d(0px, -' + Math.round(v.dtop) + 'px, 0px)';
22641                transformContainer(translate3d, true);
22642
22643                //even when the scrollingSpeed is 0 there's a little delay, which might cause the
22644                //scrollingSpeed to change in case of using silentMoveTo();
22645                if(options.scrollingSpeed){
22646                    clearTimeout(afterSectionLoadsId);
22647                    afterSectionLoadsId = setTimeout(function () {
22648                        afterSectionLoads(v);
22649                    }, options.scrollingSpeed);
22650                }else{
22651                    afterSectionLoads(v);
22652                }
22653            }
22654
22655            // using jQuery animate
22656            else{
22657                var scrollSettings = getScrollSettings(v);
22658
22659                $(scrollSettings.element).animate(
22660                    scrollSettings.options,
22661                options.scrollingSpeed, options.easing).promise().done(function () { //only one single callback in case of animating  `html, body`
22662                    if(options.scrollBar){
22663
22664                        /* Hack!
22665                        The timeout prevents setting the most dominant section in the viewport as "active" when the user
22666                        scrolled to a smaller section by using the mousewheel (auto scrolling) rather than draging the scroll bar.
22667
22668                        When using scrollBar:true It seems like the scroll events still getting propagated even after the scrolling animation has finished.
22669                        */
22670                        setTimeout(function(){
22671                            afterSectionLoads(v);
22672                        },30);
22673                    }else{
22674                        afterSectionLoads(v);
22675                    }
22676                });
22677            }
22678        }
22679
22680        /**
22681        * Gets the scrolling settings depending on the plugin autoScrolling option
22682        */
22683        function getScrollSettings(v){
22684            var scroll = {};
22685
22686            if(options.autoScrolling && !options.scrollBar){
22687                scroll.options = { 'top': -v.dtop};
22688                scroll.element = WRAPPER_SEL;
22689            }else{
22690                scroll.options = { 'scrollTop': v.dtop};
22691                scroll.element = 'html, body';
22692            }
22693
22694            return scroll;
22695        }
22696
22697        /**
22698        * Adds sections before or after the current one to create the infinite effect.
22699        */
22700        function createInfiniteSections(v){
22701            // Scrolling down
22702            if (!v.isMovementUp) {
22703                // Move all previous sections to after the active section
22704                $(SECTION_ACTIVE_SEL).after(v.activeSection.prevAll(SECTION_SEL).get().reverse());
22705            }
22706            else { // Scrolling up
22707                // Move all next sections to before the active section
22708                $(SECTION_ACTIVE_SEL).before(v.activeSection.nextAll(SECTION_SEL));
22709            }
22710
22711            // Maintain the displayed position (now that we changed the element order)
22712            silentScroll($(SECTION_ACTIVE_SEL).position().top);
22713
22714            // Maintain the active slides visible in the viewport
22715            keepSlidesPosition();
22716
22717            // save for later the elements that still need to be reordered
22718            v.wrapAroundElements = v.activeSection;
22719
22720            // Recalculate animation variables
22721            v.dtop = v.element.position().top;
22722            v.yMovement = getYmovement(v.element);
22723
22724            return v;
22725        }
22726
22727        /**
22728        * Fix section order after continuousVertical changes have been animated
22729        */
22730        function continuousVerticalFixSectionOrder (v) {
22731            // If continuousVertical is in effect (and autoScrolling would also be in effect then),
22732            // finish moving the elements around so the direct navigation will function more simply
22733            if (!v.wrapAroundElements || !v.wrapAroundElements.length) {
22734                return;
22735            }
22736
22737            if (v.isMovementUp) {
22738                $(SECTION_FIRST_SEL).before(v.wrapAroundElements);
22739            }
22740            else {
22741                $(SECTION_LAST_SEL).after(v.wrapAroundElements);
22742            }
22743
22744            silentScroll($(SECTION_ACTIVE_SEL).position().top);
22745
22746            // Maintain the active slides visible in the viewport
22747            keepSlidesPosition();
22748        }
22749
22750
22751        /**
22752        * Actions to do once the section is loaded.
22753        */
22754        function afterSectionLoads (v){
22755            continuousVerticalFixSectionOrder(v);
22756
22757            //callback (afterLoad) if the site is not just resizing and readjusting the slides
22758            $.isFunction(options.afterLoad) && !v.localIsResizing && options.afterLoad.call(v.element, v.anchorLink, (v.sectionIndex + 1));
22759            options.scrollOverflowHandler.afterLoad();
22760
22761            if(!v.localIsResizing){
22762                playMedia(v.element);
22763            }
22764
22765            v.element.addClass(COMPLETELY).siblings().removeClass(COMPLETELY);
22766
22767            canScroll = true;
22768
22769            $.isFunction(v.callback) && v.callback.call(this);
22770        }
22771
22772        /**
22773        * Sets the value for the given attribute from the `data-` attribute with the same suffix
22774        * ie: data-srcset ==> srcset  |  data-src ==> src
22775        */
22776        function setSrc(element, attribute){
22777            element
22778                .attr(attribute, element.data(attribute))
22779                .removeAttr('data-' + attribute);
22780        }
22781
22782        /**
22783        * Lazy loads image, video and audio elements.
22784        */
22785        function lazyLoad(destiny){
22786            if (!options.lazyLoading){
22787                return;
22788            }
22789
22790            var panel = getSlideOrSection(destiny);
22791            var element;
22792
22793            panel.find('img[data-src], img[data-srcset], source[data-src], audio[data-src], iframe[data-src]').each(function(){
22794                element = $(this);
22795
22796                $.each(['src', 'srcset'], function(index, type){
22797                    var attribute = element.attr('data-' + type);
22798                    if(typeof attribute !== 'undefined' && attribute){
22799                        setSrc(element, type);
22800                    }
22801                });
22802
22803                if(element.is('source')){
22804                    element.closest('video').get(0).load();
22805                }
22806            });
22807        }
22808
22809        /**
22810        * Plays video and audio elements.
22811        */
22812        function playMedia(destiny){
22813            var panel = getSlideOrSection(destiny);
22814
22815            //playing HTML5 media elements
22816            panel.find('video, audio').each(function(){
22817                var element = $(this).get(0);
22818
22819                if( element.hasAttribute('data-autoplay') && typeof element.play === 'function' ) {
22820                    element.play();
22821                }
22822            });
22823
22824            //youtube videos
22825            panel.find('iframe[src*="youtube.com/embed/"]').each(function(){
22826                var element = $(this).get(0);
22827
22828                if ( element.hasAttribute('data-autoplay') ){
22829                    playYoutube(element);
22830                }
22831
22832                //in case the URL was not loaded yet. On page load we need time for the new URL (with the API string) to load.
22833                element.onload = function() {
22834                    if ( element.hasAttribute('data-autoplay') ){
22835                        playYoutube(element);
22836                    }
22837                };
22838            });
22839        }
22840
22841        /**
22842        * Plays a youtube video
22843        */
22844        function playYoutube(element){
22845            element.contentWindow.postMessage('{"event":"command","func":"playVideo","args":""}', '*');
22846        }
22847
22848        /**
22849        * Stops video and audio elements.
22850        */
22851        function stopMedia(destiny){
22852            var panel = getSlideOrSection(destiny);
22853
22854            //stopping HTML5 media elements
22855            panel.find('video, audio').each(function(){
22856                var element = $(this).get(0);
22857
22858                if( !element.hasAttribute('data-keepplaying') && typeof element.pause === 'function' ) {
22859                    element.pause();
22860                }
22861            });
22862
22863            //youtube videos
22864            panel.find('iframe[src*="youtube.com/embed/"]').each(function(){
22865                var element = $(this).get(0);
22866
22867                if( /youtube\.com\/embed\//.test($(this).attr('src')) && !element.hasAttribute('data-keepplaying')){
22868                    $(this).get(0).contentWindow.postMessage('{"event":"command","func":"pauseVideo","args":""}','*');
22869                }
22870            });
22871        }
22872
22873        /**
22874        * Gets the active slide (or section) for the given section
22875        */
22876        function getSlideOrSection(destiny){
22877            var slide = destiny.find(SLIDE_ACTIVE_SEL);
22878            if( slide.length ) {
22879                destiny = $(slide);
22880            }
22881
22882            return destiny;
22883        }
22884
22885        /**
22886        * Scrolls to the anchor in the URL when loading the site
22887        */
22888        function scrollToAnchor(){
22889            //getting the anchor link in the URL and deleting the `#`
22890            var value =  window.location.hash.replace('#', '').split('/');
22891            var sectionAnchor = decodeURIComponent(value[0]);
22892            var slideAnchor = decodeURIComponent(value[1]);
22893
22894            if(sectionAnchor){  //if theres any #
22895                if(options.animateAnchor){
22896                    scrollPageAndSlide(sectionAnchor, slideAnchor);
22897                }else{
22898                    silentMoveTo(sectionAnchor, slideAnchor);
22899                }
22900            }
22901        }
22902
22903        /**
22904        * Detecting any change on the URL to scroll to the given anchor link
22905        * (a way to detect back history button as we play with the hashes on the URL)
22906        */
22907        function hashChangeHandler(){
22908            if(!isScrolling && !options.lockAnchors){
22909                var value =  window.location.hash.replace('#', '').split('/');
22910                var sectionAnchor = decodeURIComponent(value[0]);
22911                var slideAnchor = decodeURIComponent(value[1]);
22912
22913                    //when moving to a slide in the first section for the first time (first time to add an anchor to the URL)
22914                    var isFirstSlideMove =  (typeof lastScrolledDestiny === 'undefined');
22915                    var isFirstScrollMove = (typeof lastScrolledDestiny === 'undefined' && typeof slideAnchor === 'undefined' && !slideMoving);
22916
22917
22918                if(sectionAnchor.length){
22919                    /*in order to call scrollpage() only once for each destination at a time
22920                    It is called twice for each scroll otherwise, as in case of using anchorlinks `hashChange`
22921                    event is fired on every scroll too.*/
22922                    if ((sectionAnchor && sectionAnchor !== lastScrolledDestiny) && !isFirstSlideMove || isFirstScrollMove || (!slideMoving && lastScrolledSlide != slideAnchor ))  {
22923                        scrollPageAndSlide(sectionAnchor, slideAnchor);
22924                    }
22925                }
22926            }
22927        }
22928
22929        //Sliding with arrow keys, both, vertical and horizontal
22930        function keydownHandler(e) {
22931
22932            clearTimeout(keydownId);
22933
22934            var activeElement = $(':focus');
22935
22936            if(!activeElement.is('textarea') && !activeElement.is('input') && !activeElement.is('select') &&
22937                activeElement.attr('contentEditable') !== "true" && activeElement.attr('contentEditable') !== '' &&
22938                options.keyboardScrolling && options.autoScrolling){
22939                var keyCode = e.which;
22940
22941                //preventing the scroll with arrow keys & spacebar & Page Up & Down keys
22942                var keyControls = [40, 38, 32, 33, 34];
22943                if($.inArray(keyCode, keyControls) > -1){
22944                    e.preventDefault();
22945                }
22946
22947                controlPressed = e.ctrlKey;
22948
22949                keydownId = setTimeout(function(){
22950                    onkeydown(e);
22951                },150);
22952            }
22953        }
22954
22955        function tooltipTextHandler(){
22956            $(this).prev().trigger('click');
22957        }
22958
22959        //to prevent scrolling while zooming
22960        function keyUpHandler(e){
22961            if(isWindowFocused){ //the keyup gets fired on new tab ctrl + t in Firefox
22962                controlPressed = e.ctrlKey;
22963            }
22964        }
22965
22966        //binding the mousemove when the mouse's middle button is released
22967        function mouseDownHandler(e){
22968            //middle button
22969            if (e.which == 2){
22970                oldPageY = e.pageY;
22971                container.on('mousemove', mouseMoveHandler);
22972            }
22973        }
22974
22975        //unbinding the mousemove when the mouse's middle button is released
22976        function mouseUpHandler(e){
22977            //middle button
22978            if (e.which == 2){
22979                container.off('mousemove');
22980            }
22981        }
22982
22983        //Scrolling horizontally when clicking on the slider controls.
22984        function slideArrowHandler(){
22985            var section = $(this).closest(SECTION_SEL);
22986
22987            if ($(this).hasClass(SLIDES_PREV)) {
22988                if(isScrollAllowed.m.left){
22989                    moveSlideLeft(section);
22990                }
22991            } else {
22992                if(isScrollAllowed.m.right){
22993                    moveSlideRight(section);
22994                }
22995            }
22996        }
22997
22998        //when opening a new tab (ctrl + t), `control` won't be pressed when coming back.
22999        function blurHandler(){
23000            isWindowFocused = false;
23001            controlPressed = false;
23002        }
23003
23004        //Scrolls to the section when clicking the navigation bullet
23005        function sectionBulletHandler(e){
23006            e.preventDefault();
23007            var index = $(this).parent().index();
23008            scrollPage($(SECTION_SEL).eq(index));
23009        }
23010
23011        //Scrolls the slider to the given slide destination for the given section
23012        function slideBulletHandler(e){
23013            e.preventDefault();
23014            var slides = $(this).closest(SECTION_SEL).find(SLIDES_WRAPPER_SEL);
23015            var destiny = slides.find(SLIDE_SEL).eq($(this).closest('li').index());
23016
23017            landscapeScroll(slides, destiny);
23018        }
23019
23020        /**
23021        * Keydown event
23022        */
23023        function onkeydown(e){
23024            var shiftPressed = e.shiftKey;
23025
23026            //do nothing if we can not scroll or we are not using horizotnal key arrows.
23027            if(!canScroll && [37,39].indexOf(e.which) < 0){
23028                return;
23029            }
23030
23031            switch (e.which) {
23032                //up
23033                case 38:
23034                case 33:
23035                    if(isScrollAllowed.k.up){
23036                        moveSectionUp();
23037                    }
23038                    break;
23039
23040                //down
23041                case 32: //spacebar
23042                    if(shiftPressed && isScrollAllowed.k.up){
23043                        moveSectionUp();
23044                        break;
23045                    }
23046                /* falls through */
23047                case 40:
23048                case 34:
23049                    if(isScrollAllowed.k.down){
23050                        moveSectionDown();
23051                    }
23052                    break;
23053
23054                //Home
23055                case 36:
23056                    if(isScrollAllowed.k.up){
23057                        moveTo(1);
23058                    }
23059                    break;
23060
23061                //End
23062                case 35:
23063                     if(isScrollAllowed.k.down){
23064                        moveTo( $(SECTION_SEL).length );
23065                    }
23066                    break;
23067
23068                //left
23069                case 37:
23070                    if(isScrollAllowed.k.left){
23071                        moveSlideLeft();
23072                    }
23073                    break;
23074
23075                //right
23076                case 39:
23077                    if(isScrollAllowed.k.right){
23078                        moveSlideRight();
23079                    }
23080                    break;
23081
23082                default:
23083                    return; // exit this handler for other keys
23084            }
23085        }
23086
23087        /**
23088        * Detecting the direction of the mouse movement.
23089        * Used only for the middle button of the mouse.
23090        */
23091        var oldPageY = 0;
23092        function mouseMoveHandler(e){
23093            if(canScroll){
23094                // moving up
23095                if (e.pageY < oldPageY && isScrollAllowed.m.up){
23096                    moveSectionUp();
23097                }
23098
23099                // moving down
23100                else if(e.pageY > oldPageY && isScrollAllowed.m.down){
23101                    moveSectionDown();
23102                }
23103            }
23104            oldPageY = e.pageY;
23105        }
23106
23107        /**
23108        * Scrolls horizontal sliders.
23109        */
23110        function landscapeScroll(slides, destiny, direction){
23111            var section = slides.closest(SECTION_SEL);
23112            var v = {
23113                slides: slides,
23114                destiny: destiny,
23115                direction: direction,
23116                destinyPos: destiny.position(),
23117                slideIndex: destiny.index(),
23118                section: section,
23119                sectionIndex: section.index(SECTION_SEL),
23120                anchorLink: section.data('anchor'),
23121                slidesNav: section.find(SLIDES_NAV_SEL),
23122                slideAnchor:  getAnchor(destiny),
23123                prevSlide: section.find(SLIDE_ACTIVE_SEL),
23124                prevSlideIndex: section.find(SLIDE_ACTIVE_SEL).index(),
23125
23126                //caching the value of isResizing at the momment the function is called
23127                //because it will be checked later inside a setTimeout and the value might change
23128                localIsResizing: isResizing
23129            };
23130            v.xMovement = getXmovement(v.prevSlideIndex, v.slideIndex);
23131            v.direction = v.direction ? v.direction : v.xMovement;
23132
23133            //important!! Only do it when not resizing
23134            if(!v.localIsResizing){
23135                //preventing from scrolling to the next/prev section when using scrollHorizontally
23136                canScroll = false;
23137            }
23138
23139            if(options.onSlideLeave){
23140
23141                //if the site is not just resizing and readjusting the slides
23142                if(!v.localIsResizing && v.xMovement!=='none'){
23143                    if($.isFunction( options.onSlideLeave )){
23144                        if(options.onSlideLeave.call( v.prevSlide, v.anchorLink, (v.sectionIndex + 1), v.prevSlideIndex, v.xMovement, v.slideIndex ) === false){
23145                            slideMoving = false;
23146                            return;
23147                        }
23148                    }
23149                }
23150            }
23151
23152            destiny.addClass(ACTIVE).siblings().removeClass(ACTIVE);
23153
23154            if(!v.localIsResizing){
23155                stopMedia(v.prevSlide);
23156                lazyLoad(destiny);
23157            }
23158
23159            if(!options.loopHorizontal && options.controlArrows){
23160                //hidding it for the fist slide, showing for the rest
23161                section.find(SLIDES_ARROW_PREV_SEL).toggle(v.slideIndex!==0);
23162
23163                //hidding it for the last slide, showing for the rest
23164                section.find(SLIDES_ARROW_NEXT_SEL).toggle(!destiny.is(':last-child'));
23165            }
23166
23167            //only changing the URL if the slides are in the current section (not for resize re-adjusting)
23168            if(section.hasClass(ACTIVE) && !v.localIsResizing){
23169                setState(v.slideIndex, v.slideAnchor, v.anchorLink, v.sectionIndex);
23170            }
23171
23172            performHorizontalMove(slides, v, true);
23173        }
23174
23175
23176        function afterSlideLoads(v){
23177            activeSlidesNavigation(v.slidesNav, v.slideIndex);
23178
23179            //if the site is not just resizing and readjusting the slides
23180            if(!v.localIsResizing){
23181                $.isFunction( options.afterSlideLoad ) && options.afterSlideLoad.call( v.destiny, v.anchorLink, (v.sectionIndex + 1), v.slideAnchor, v.slideIndex);
23182
23183                //needs to be inside the condition to prevent problems with continuousVertical and scrollHorizontally
23184                //and to prevent double scroll right after a windows resize
23185                canScroll = true;
23186
23187                playMedia(v.destiny);
23188            }
23189
23190            //letting them slide again
23191            slideMoving = false;
23192        }
23193
23194        /**
23195        * Performs the horizontal movement. (CSS3 or jQuery)
23196        *
23197        * @param fireCallback {Bool} - determines whether or not to fire the callback
23198        */
23199        function performHorizontalMove(slides, v, fireCallback){
23200            var destinyPos = v.destinyPos;
23201
23202            if(options.css3){
23203                var translate3d = 'translate3d(-' + Math.round(destinyPos.left) + 'px, 0px, 0px)';
23204
23205                addAnimation(slides.find(SLIDES_CONTAINER_SEL)).css(getTransforms(translate3d), v);
23206
23207                afterSlideLoadsId = setTimeout(function(){
23208                    fireCallback && afterSlideLoads(v);
23209                }, options.scrollingSpeed, options.easing);
23210            }else{
23211                slides.animate({
23212                    scrollLeft : Math.round(destinyPos.left)
23213                }, options.scrollingSpeed, options.easing, function() {
23214
23215                    fireCallback && afterSlideLoads(v);
23216                });
23217            }
23218        }
23219
23220        /**
23221        * Sets the state for the horizontal bullet navigations.
23222        */
23223        function activeSlidesNavigation(slidesNav, slideIndex){
23224            slidesNav.find(ACTIVE_SEL).removeClass(ACTIVE);
23225            slidesNav.find('li').eq(slideIndex).find('a').addClass(ACTIVE);
23226        }
23227
23228        var previousHeight = windowsHeight;
23229
23230        //when resizing the site, we adjust the heights of the sections, slimScroll...
23231        function resizeHandler(){
23232            //checking if it needs to get responsive
23233            responsive();
23234
23235            // rebuild immediately on touch devices
23236            if (isTouchDevice) {
23237                var activeElement = $(document.activeElement);
23238
23239                //if the keyboard is NOT visible
23240                if (!activeElement.is('textarea') && !activeElement.is('input') && !activeElement.is('select')) {
23241                    var currentHeight = $window.height();
23242
23243                    //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)
23244                    if( Math.abs(currentHeight - previousHeight) > (20 * Math.max(previousHeight, currentHeight) / 100) ){
23245                        reBuild(true);
23246                        previousHeight = currentHeight;
23247                    }
23248                }
23249            }else{
23250                //in order to call the functions only when the resize is finished
23251                //http://stackoverflow.com/questions/4298612/jquery-how-to-call-resize-event-only-once-its-finished-resizing
23252                clearTimeout(resizeId);
23253
23254                resizeId = setTimeout(function(){
23255                    reBuild(true);
23256                }, 350);
23257            }
23258        }
23259
23260        /**
23261        * Checks if the site needs to get responsive and disables autoScrolling if so.
23262        * A class `fp-responsive` is added to the plugin's container in case the user wants to use it for his own responsive CSS.
23263        */
23264        function responsive(){
23265            var widthLimit = options.responsive || options.responsiveWidth; //backwards compatiblity
23266            var heightLimit = options.responsiveHeight;
23267
23268            //only calculating what we need. Remember its called on the resize event.
23269            var isBreakingPointWidth = widthLimit && $window.outerWidth() < widthLimit;
23270            var isBreakingPointHeight = heightLimit && $window.height() < heightLimit;
23271
23272            if(widthLimit && heightLimit){
23273                setResponsive(isBreakingPointWidth || isBreakingPointHeight);
23274            }
23275            else if(widthLimit){
23276                setResponsive(isBreakingPointWidth);
23277            }
23278            else if(heightLimit){
23279                setResponsive(isBreakingPointHeight);
23280            }
23281        }
23282
23283        /**
23284        * Adds transition animations for the given element
23285        */
23286        function addAnimation(container, element){
23287            var transition = 'all ' + options.scrollingSpeed + 'ms ' + options.easingcss3;
23288
23289            container.removeClass(NO_TRANSITION);
23290            return container.css({
23291                '-webkit-transition': transition,
23292                'transition': transition
23293            });
23294        }
23295
23296        /**
23297        * Remove transition animations for the given element
23298        */
23299        function removeAnimation(element){
23300            return element.addClass(NO_TRANSITION);
23301        }
23302
23303        /**
23304        * Activating the vertical navigation bullets according to the given slide name.
23305        */
23306        function activateNavDots(name, sectionIndex){
23307            if(options.navigation){
23308                $(SECTION_NAV_SEL).find(ACTIVE_SEL).removeClass(ACTIVE);
23309                if(name){
23310                    $(SECTION_NAV_SEL).find('a[href="#' + name + '"]').addClass(ACTIVE);
23311                }else{
23312                    $(SECTION_NAV_SEL).find('li').eq(sectionIndex).find('a').addClass(ACTIVE);
23313                }
23314            }
23315        }
23316
23317        /**
23318        * Activating the website main menu elements according to the given slide name.
23319        */
23320        function activateMenuElement(name){
23321            if(options.menu){
23322                $(options.menu).find(ACTIVE_SEL).removeClass(ACTIVE);
23323                $(options.menu).find('[data-menuanchor="'+name+'"]').addClass(ACTIVE);
23324            }
23325        }
23326
23327        /**
23328        * Sets to active the current menu and vertical nav items.
23329        */
23330        function activateMenuAndNav(anchor, index){
23331            activateMenuElement(anchor);
23332            activateNavDots(anchor, index);
23333        }
23334
23335        /**
23336        * Retuns `up` or `down` depending on the scrolling movement to reach its destination
23337        * from the current section.
23338        */
23339        function getYmovement(destiny){
23340            var fromIndex = $(SECTION_ACTIVE_SEL).index(SECTION_SEL);
23341            var toIndex = destiny.index(SECTION_SEL);
23342            if( fromIndex == toIndex){
23343                return 'none';
23344            }
23345            if(fromIndex > toIndex){
23346                return 'up';
23347            }
23348            return 'down';
23349        }
23350
23351        /**
23352        * Retuns `right` or `left` depending on the scrolling movement to reach its destination
23353        * from the current slide.
23354        */
23355        function getXmovement(fromIndex, toIndex){
23356            if( fromIndex == toIndex){
23357                return 'none';
23358            }
23359            if(fromIndex > toIndex){
23360                return 'left';
23361            }
23362            return 'right';
23363        }
23364
23365        /**
23366        * Checks if the element needs scrollbar and if the user wants to apply it.
23367        * If so it creates it.
23368        *
23369        * @param {Object} element   jQuery object of the section or slide
23370        */
23371        function createScrollBar(element){
23372            //User doesn't want scrollbar here? Sayonara baby!
23373            if(element.hasClass('fp-noscroll')) return;
23374
23375            //needed to make `scrollHeight` work under Opera 12
23376            element.css('overflow', 'hidden');
23377
23378            var scrollOverflowHandler = options.scrollOverflowHandler;
23379            var wrap = scrollOverflowHandler.wrapContent();
23380            //in case element is a slide
23381            var section = element.closest(SECTION_SEL);
23382            var scrollable = scrollOverflowHandler.scrollable(element);
23383            var contentHeight;
23384
23385            //if there was scroll, the contentHeight will be the one in the scrollable section
23386            if(scrollable.length){
23387                contentHeight = scrollOverflowHandler.scrollHeight(element);
23388            }else{
23389                contentHeight = element.get(0).scrollHeight;
23390                if(options.verticalCentered){
23391                    contentHeight = element.find(TABLE_CELL_SEL).get(0).scrollHeight;
23392                }
23393            }
23394
23395            var scrollHeight = windowsHeight - parseInt(section.css('padding-bottom')) - parseInt(section.css('padding-top'));
23396
23397            //needs scroll?
23398            if ( contentHeight > scrollHeight) {
23399                //did we already have an scrollbar ? Updating it
23400                if(scrollable.length){
23401                    scrollOverflowHandler.update(element, scrollHeight);
23402                }
23403                //creating the scrolling
23404                else{
23405                    if(options.verticalCentered){
23406                        element.find(TABLE_CELL_SEL).wrapInner(wrap);
23407                    }else{
23408                        element.wrapInner(wrap);
23409                    }
23410                    scrollOverflowHandler.create(element, scrollHeight);
23411                }
23412            }
23413            //removing the scrolling when it is not necessary anymore
23414            else{
23415                scrollOverflowHandler.remove(element);
23416            }
23417
23418            //undo
23419            element.css('overflow', '');
23420        }
23421
23422        function addTableClass(element){
23423            //In case we are styling for the 2nd time as in with reponsiveSlides
23424            if(!element.hasClass(TABLE)){
23425                element.addClass(TABLE).wrapInner('<div class="' + TABLE_CELL + '" style="height:' + getTableHeight(element) + 'px;" />');
23426            }
23427        }
23428
23429        function getTableHeight(element){
23430            var sectionHeight = windowsHeight;
23431
23432            if(options.paddingTop || options.paddingBottom){
23433                var section = element;
23434                if(!section.hasClass(SECTION)){
23435                    section = element.closest(SECTION_SEL);
23436                }
23437
23438                var paddings = parseInt(section.css('padding-top')) + parseInt(section.css('padding-bottom'));
23439                sectionHeight = (windowsHeight - paddings);
23440            }
23441
23442            return sectionHeight;
23443        }
23444
23445        /**
23446        * Adds a css3 transform property to the container class with or without animation depending on the animated param.
23447        */
23448        function transformContainer(translate3d, animated){
23449            if(animated){
23450                addAnimation(container);
23451            }else{
23452                removeAnimation(container);
23453            }
23454
23455            container.css(getTransforms(translate3d));
23456
23457            //syncronously removing the class after the animation has been applied.
23458            setTimeout(function(){
23459                container.removeClass(NO_TRANSITION);
23460            },10);
23461        }
23462
23463        /**
23464        * Gets a section by its anchor / index
23465        */
23466        function getSectionByAnchor(sectionAnchor){
23467            if(!sectionAnchor) return [];
23468
23469            var section = container.find(SECTION_SEL + '[data-anchor="'+sectionAnchor+'"]');
23470            if(!section.length){
23471                section = $(SECTION_SEL).eq( sectionAnchor -1);
23472            }
23473
23474            return section;
23475        }
23476
23477        /**
23478        * Gets a slide inside a given section by its anchor / index
23479        */
23480        function getSlideByAnchor(slideAnchor, section){
23481            var slides = section.find(SLIDES_WRAPPER_SEL);
23482            var slide =  slides.find(SLIDE_SEL + '[data-anchor="'+slideAnchor+'"]');
23483
23484            if(!slide.length){
23485                slide = slides.find(SLIDE_SEL).eq(slideAnchor);
23486            }
23487
23488            return slide;
23489        }
23490
23491        /**
23492        * Scrolls to the given section and slide anchors
23493        */
23494        function scrollPageAndSlide(destiny, slide){
23495            var section = getSectionByAnchor(destiny);
23496
23497            //do nothing if there's no section with the given anchor name
23498            if(!section.length) return;
23499
23500            //default slide
23501            if (typeof slide === 'undefined') {
23502                slide = 0;
23503            }
23504
23505            //we need to scroll to the section and then to the slide
23506            if (destiny !== lastScrolledDestiny && !section.hasClass(ACTIVE)){
23507                scrollPage(section, function(){
23508                    scrollSlider(section, slide);
23509                });
23510            }
23511            //if we were already in the section
23512            else{
23513                scrollSlider(section, slide);
23514            }
23515        }
23516
23517        /**
23518        * Scrolls the slider to the given slide destination for the given section
23519        */
23520        function scrollSlider(section, slideAnchor){
23521            if(typeof slideAnchor !== 'undefined'){
23522                var slides = section.find(SLIDES_WRAPPER_SEL);
23523                var destiny =  getSlideByAnchor(slideAnchor, section);
23524
23525                if(destiny.length){
23526                    landscapeScroll(slides, destiny);
23527                }
23528            }
23529        }
23530
23531        /**
23532        * Creates a landscape navigation bar with dots for horizontal sliders.
23533        */
23534        function addSlidesNavigation(section, numSlides){
23535            section.append('<div class="' + SLIDES_NAV + '"><ul></ul></div>');
23536            var nav = section.find(SLIDES_NAV_SEL);
23537
23538            //top or bottom
23539            nav.addClass(options.slidesNavPosition);
23540
23541            for(var i=0; i< numSlides; i++){
23542                nav.find('ul').append('<li><a href="#"><span></span></a></li>');
23543            }
23544
23545            //centering it
23546            nav.css('margin-left', '-' + (nav.width()/2) + 'px');
23547
23548            nav.find('li').first().find('a').addClass(ACTIVE);
23549        }
23550
23551
23552        /**
23553        * Sets the state of the website depending on the active section/slide.
23554        * It changes the URL hash when needed and updates the body class.
23555        */
23556        function setState(slideIndex, slideAnchor, anchorLink, sectionIndex){
23557            var sectionHash = '';
23558
23559            if(options.anchors.length && !options.lockAnchors){
23560
23561                //isn't it the first slide?
23562                if(slideIndex){
23563                    if(typeof anchorLink !== 'undefined'){
23564                        sectionHash = anchorLink;
23565                    }
23566
23567                    //slide without anchor link? We take the index instead.
23568                    if(typeof slideAnchor === 'undefined'){
23569                        slideAnchor = slideIndex;
23570                    }
23571
23572                    lastScrolledSlide = slideAnchor;
23573                    setUrlHash(sectionHash + '/' + slideAnchor);
23574
23575                //first slide won't have slide anchor, just the section one
23576                }else if(typeof slideIndex !== 'undefined'){
23577                    lastScrolledSlide = slideAnchor;
23578                    setUrlHash(anchorLink);
23579                }
23580
23581                //section without slides
23582                else{
23583                    setUrlHash(anchorLink);
23584                }
23585            }
23586
23587            setBodyClass();
23588        }
23589
23590        /**
23591        * Sets the URL hash.
23592        */
23593        function setUrlHash(url){
23594            //Uncode addition
23595            if ( typeof SiteParameters.slide_footer != 'undefined' && url == SiteParameters.slide_footer )
23596                return false;
23597
23598            if(options.recordHistory){
23599                location.hash = url;
23600            }else{
23601                //Uncode addition
23602                //Mobile Chrome doesn't work the normal way, so... lets use HTML5 for phones :)
23603                // if(isTouchDevice || isTouch){
23604                //     window.history.replaceState(undefined, undefined, '#' + url);
23605                // }else{
23606                //     var baseUrl = window.location.href.split('#')[0];
23607                //     window.location.replace( baseUrl + '#' + url );
23608                // }
23609                return false;
23610            }
23611        }
23612
23613        /**
23614        * Gets the anchor for the given slide / section. Its index will be used if there's none.
23615        */
23616        function getAnchor(element){
23617            var anchor = element.data('anchor');
23618            var index = element.index();
23619
23620            //Slide without anchor link? We take the index instead.
23621            if(typeof anchor === 'undefined'){
23622                anchor = index;
23623            }
23624
23625            return anchor;
23626        }
23627
23628        /**
23629        * Sets a class for the body of the page depending on the active section / slide
23630        */
23631        function setBodyClass(){
23632            var section = $(SECTION_ACTIVE_SEL);
23633            var slide = section.find(SLIDE_ACTIVE_SEL);
23634
23635            var sectionAnchor = getAnchor(section);
23636            var slideAnchor = getAnchor(slide);
23637
23638            var text = String(sectionAnchor);
23639
23640            if(slide.length){
23641                text = text + '-' + slideAnchor;
23642            }
23643
23644            //changing slash for dash to make it a valid CSS style
23645            text = text.replace('/', '-').replace('#','');
23646
23647            //removing previous anchor classes
23648            var classRe = new RegExp('\\b\\s?' + VIEWING_PREFIX + '-[^\\s]+\\b', "g");
23649            $body[0].className = $body[0].className.replace(classRe, '');
23650
23651            //adding the current anchor
23652            $body.addClass(VIEWING_PREFIX + '-' + text);
23653        }
23654
23655        /**
23656        * Checks for translate3d support
23657        * @return boolean
23658        * http://stackoverflow.com/questions/5661671/detecting-transform-translate3d-support
23659        */
23660        function support3d() {
23661            var el = document.createElement('p'),
23662                has3d,
23663                transforms = {
23664                    'webkitTransform':'-webkit-transform',
23665                    'OTransform':'-o-transform',
23666                    'msTransform':'-ms-transform',
23667                    'MozTransform':'-moz-transform',
23668                    'transform':'transform'
23669                };
23670
23671            // Add it to the body to get the computed style.
23672            document.body.insertBefore(el, null);
23673
23674            for (var t in transforms) {
23675                if (el.style[t] !== undefined) {
23676                    el.style[t] = 'translate3d(1px,1px,1px)';
23677                    has3d = window.getComputedStyle(el).getPropertyValue(transforms[t]);
23678                }
23679            }
23680
23681            document.body.removeChild(el);
23682
23683            return (has3d !== undefined && has3d.length > 0 && has3d !== 'none');
23684        }
23685
23686        /**
23687        * Removes the auto scrolling action fired by the mouse wheel and trackpad.
23688        * After this function is called, the mousewheel and trackpad movements won't scroll through sections.
23689        */
23690        function removeMouseWheelHandler(){
23691            if (document.addEventListener) {
23692                document.removeEventListener('mousewheel', MouseWheelHandler, false); //IE9, Chrome, Safari, Oper
23693                document.removeEventListener('wheel', MouseWheelHandler, false); //Firefox
23694                document.removeEventListener('MozMousePixelScroll', MouseWheelHandler, false); //old Firefox
23695            } else {
23696                document.detachEvent('onmousewheel', MouseWheelHandler); //IE 6/7/8
23697            }
23698        }
23699
23700        /**
23701        * Adds the auto scrolling action for the mouse wheel and trackpad.
23702        * After this function is called, the mousewheel and trackpad movements will scroll through sections
23703        * https://developer.mozilla.org/en-US/docs/Web/Events/wheel
23704        */
23705        function addMouseWheelHandler(){
23706            var prefix = '';
23707            var _addEventListener;
23708
23709            if (window.addEventListener){
23710                _addEventListener = "addEventListener";
23711            }else{
23712                _addEventListener = "attachEvent";
23713                prefix = 'on';
23714            }
23715
23716             // detect available wheel event
23717            var support = 'onwheel' in document.createElement('div') ? 'wheel' : // Modern browsers support "wheel"
23718                      document.onmousewheel !== undefined ? 'mousewheel' : // Webkit and IE support at least "mousewheel"
23719                      'DOMMouseScroll'; // let's assume that remaining browsers are older Firefox
23720            var passiveEvent = g_supportsPassive ? {passive: false }
23720: false;
23721
23722
23723            if(support == 'DOMMouseScroll'){
23724                document[ _addEventListener ](prefix + 'MozMousePixelScroll', MouseWheelHandler, passiveEvent);
23725            }
23726
23727            //handle MozMousePixelScroll in older Firefox
23728            else{
23729                document[ _addEventListener ](prefix + support, MouseWheelHandler, passiveEvent);
23730            }
23731        }
23732
23733        /**
23734        * Binding the mousemove when the mouse's middle button is pressed
23735        */
23736        function addMiddleWheelHandler(){
23737            container
23738                .on('mousedown', mouseDownHandler)
23739                .on('mouseup', mouseUpHandler);
23740        }
23741
23742        /**
23743        * Unbinding the mousemove when the mouse's middle button is released
23744        */
23745        function removeMiddleWheelHandler(){
23746            container
23747                .off('mousedown', mouseDownHandler)
23748                .off('mouseup', mouseUpHandler);
23749        }
23750
23751        /**
23752        * Adds the possibility to auto scroll through sections on touch devices.
23753        */
23754        function addTouchHandler(){
23755            if(isTouchDevice || isTouch){
23756                if(options.autoScrolling){
23757                    $body.off(events.touchmove).on(events.touchmove, preventBouncing);
23758                }
23759
23760                $(WRAPPER_SEL)
23761                    .off(events.touchstart).on(events.touchstart, touchStartHandler)
23762                    .off(events.touchmove).on(events.touchmove, touchMoveHandler);
23763            }
23764        }
23765
23766        /**
23767        * Removes the auto scrolling for touch devices.
23768        */
23769        function removeTouchHandler(){
23770            if(isTouchDevice || isTouch){
23771                $(WRAPPER_SEL)
23772                    .off(events.touchstart)
23773                    .off(events.touchmove);
23774            }
23775        }
23776
23777        /*
23778        * Returns and object with Microsoft pointers (for IE<11 and for IE >= 11)
23779        * http://msdn.microsoft.com/en-us/library/ie/dn304886(v=vs.85).aspx
23780        */
23781        function getMSPointer(){
23782            var pointer;
23783
23784            //IE >= 11 & rest of browsers
23785            if(window.PointerEvent){
23786                pointer = { down: 'pointerdown', move: 'pointermove'};
23787            }
23788
23789            //IE < 11
23790            else{
23791                pointer = { down: 'MSPointerDown', move: 'MSPointerMove'};
23792            }
23793
23794            return pointer;
23795        }
23796
23797        /**
23798        * Gets the pageX and pageY properties depending on the browser.
23799        * https://github.com/alvarotrigo/fullPage.js/issues/194#issuecomment-34069854
23800        */
23801        function getEventsPage(e){
23802            var events = [];
23803
23804            events.y = (typeof e.pageY !== 'undefined' && (e.pageY || e.pageX) ? e.pageY : e.touches[0].pageY);
23805            events.x = (typeof e.pageX !== 'undefined' && (e.pageY || e.pageX) ? e.pageX : e.touches[0].pageX);
23806
23807            //in touch devices with scrollBar:true, e.pageY is detected, but we have to deal with touch events. #1008
23808            if(isTouch && isReallyTouch(e) && options.scrollBar){
23809                events.y = e.touches[0].pageY;
23810                events.x = e.touches[0].pageX;
23811            }
23812
23813            return events;
23814        }
23815
23816        /**
23817        * Slides silently (with no animation) the active slider to the given slide.
23818        * @param noCallback {bool} true or defined -> no callbacks
23819        */
23820        function silentLandscapeScroll(activeSlide, noCallbacks){
23821            setScrollingSpeed (0, 'internal');
23822
23823            if(typeof noCallbacks !== 'undefined'){
23824                //preventing firing callbacks afterSlideLoad etc.
23825                isResizing = true;
23826            }
23827
23828            landscapeScroll(activeSlide.closest(SLIDES_WRAPPER_SEL), activeSlide);
23829
23830            if(typeof noCallbacks !== 'undefined'){
23831                isResizing = false;
23832            }
23833
23834            setScrollingSpeed(originals.scrollingSpeed, 'internal');
23835        }
23836
23837        /**
23838        * Scrolls silently (with no animation) the page to the given Y position.
23839        */
23840        function silentScroll(top){
23841            // The first section can have a negative value in iOS 10. Not quite sure why: -0.0142822265625
23842            // that's why we round it to 0.
23843            var roundedTop = Math.round(top);
23844
23845            if (options.css3 && options.autoScrolling && !options.scrollBar){
23846                var translate3d = 'translate3d(0px, -' + roundedTop + 'px, 0px)';
23847                transformContainer(translate3d, false);
23848            }
23849            else if(options.autoScrolling && !options.scrollBar){
23850                container.css('top', -roundedTop);
23851            }
23852            else{
23853                $htmlBody.scrollTop(roundedTop);
23854            }
23855        }
23856
23857        /**
23858        * Returns the cross-browser transform string.
23859        */
23860        function getTransforms(translate3d){
23861            return {
23862                '-webkit-transform': translate3d,
23863                '-moz-transform': translate3d,
23864                '-ms-transform':translate3d,
23865                'transform': translate3d
23866            };
23867        }
23868
23869        /**
23870        * Allowing or disallowing the mouse/swipe scroll in a given direction. (not for keyboard)
23871        * @type  m (mouse) or k (keyboard)
23872        */
23873        function setIsScrollAllowed(value, direction, type){
23874            switch (direction){
23875                case 'up': isScrollAllowed[type].up = value; break;
23876                case 'down': isScrollAllowed[type].down = value; break;
23877                case 'left': isScrollAllowed[type].left = value; break;
23878                case 'right': isScrollAllowed[type].right = value; break;
23879                case 'all':
23880                    if(type == 'm'){
23881                        setAllowScrolling(value);
23882                    }else{
23883                        setKeyboardScrolling(value);
23884                    }
23885            }
23886        }
23887
23888        /*
23889        * Destroys fullpage.js plugin events and optinally its html markup and styles
23890        */
23891        function destroy(all){
23892            setAutoScrolling(false, 'internal');
23893            setAllowScrolling(false);
23894            setKeyboardScrolling(false);
23895            container.addClass(DESTROYED);
23896
23897            clearTimeout(afterSlideLoadsId);
23898            clearTimeout(afterSectionLoadsId);
23899            clearTimeout(resizeId);
23900            clearTimeout(scrollId);
23901            clearTimeout(scrollId2);
23902
23903            $window
23904                .off('scroll', scrollHandler)
23905                .off('hashchange', hashChangeHandler)
23906                .off('resize', resizeHandler);
23907
23908            $document
23909                .off('click touchstart', SECTION_NAV_SEL + ' a')
23910                .off('mouseenter', SECTION_NAV_SEL + ' li')
23911                .off('mouseleave', SECTION_NAV_SEL + ' li')
23912                .off('click touchstart', SLIDES_NAV_LINK_SEL)
23913                .off('mouseover', options.normalScrollElements)
23914                .off('mouseout', options.normalScrollElements);
23915
23916            $(SECTION_SEL)
23917                .off('click touchstart', SLIDES_ARROW_SEL);
23918
23919            clearTimeout(afterSlideLoadsId);
23920            clearTimeout(afterSectionLoadsId);
23921
23922            //lets make a mess!
23923            if(all){
23924                destroyStructure();
23925            }
23926        }
23927
23928        /*
23929        * Removes inline styles added by fullpage.js
23930        */
23931        function destroyStructure(){
23932            //reseting the `top` or `translate` properties to 0
23933            silentScroll(0);
23934
23935            //loading all the lazy load content
23936            container.find('img[data-src], source[data-src], audio[data-src], iframe[data-src]').each(function(){
23937                setSrc($(this), 'src');
23938            });
23939
23940            container.find('img[data-srcset]').each(function(){
23941                setSrc($(this), 'srcset');
23942            });
23943
23944            $(SECTION_NAV_SEL + ', ' + SLIDES_NAV_SEL +  ', ' + SLIDES_ARROW_SEL).remove();
23945
23946            //removing inline styles
23947            $(SECTION_SEL).css( {
23948                'height': '',
23949                'background-color' : '',
23950                'padding': ''
23951            });
23952
23953            $(SLIDE_SEL).css( {
23954                'width': ''
23955            });
23956
23957            container.css({
23958                'height': '',
23959                'position': '',
23960                '-ms-touch-action': '',
23961                'touch-action': ''
23962            });
23963
23964            $htmlBody.css({
23965                'overflow': '',
23966                'height': ''
23967            });
23968
23969            // remove .fp-enabled class
23970            $('html').removeClass(ENABLED);
23971
23972            // remove .fp-responsive class
23973            $body.removeClass(RESPONSIVE);
23974
23975            // remove all of the .fp-viewing- classes
23976            $.each($body.get(0).className.split(/\s+/), function (index, className) {
23977                if (className.indexOf(VIEWING_PREFIX) === 0) {
23978                    $body.removeClass(className);
23979                }
23980            });
23981
23982            //removing added classes
23983            $(SECTION_SEL + ', ' + SLIDE_SEL).each(function(){
23984                options.scrollOverflowHandler.remove($(this));
23985                $(this).removeClass(TABLE + ' ' + ACTIVE);
23986            });
23987
23988            removeAnimation(container);
23989
23990            //Unwrapping content
23991            container.find(TABLE_CELL_SEL + ', ' + SLIDES_CONTAINER_SEL + ', ' + SLIDES_WRAPPER_SEL).each(function(){
23992                //unwrap not being use in case there's no child element inside and its just text
23993                $(this).replaceWith(this.childNodes);
23994            });
23995
23996            //removing the applied transition from the fullpage wrapper
23997            container.css({
23998                '-webkit-transition': 'none',
23999                'transition': 'none'
24000            });
24001
24002            //scrolling the page to the top with no animation
24003            $htmlBody.scrollTop(0);
24004
24005            //removing selectors
24006            var usedSelectors = [SECTION, SLIDE, SLIDES_CONTAINER];
24007            $.each(usedSelectors, function(index, value){
24008                $('.' + value).removeClass(value);
24009            });
24010        }
24011
24012        /*
24013        * Sets the state for a variable with multiple states (original, and temporal)
24014        * Some variables such as `autoScrolling` or `recordHistory` might change automatically its state when using `responsive` or `autoScrolling:false`.
24015        * This function is used to keep track of both states, the original and the temporal one.
24016        * If type is not 'internal', then we assume the user is globally changing the variable.
24017        */
24018        function setVariableState(variable, value, type){
24019            options[variable] = value;
24020            if(type !== 'internal'){
24021                originals[variable] = value;
24022            }
24023        }
24024
24025        /**
24026        * Displays warnings
24027        */
24028        function displayWarnings(){
24029            var extensions = ['fadingEffect', 'continuousHorizontal', 'scrollHorizontally', 'interlockedSlides', 'resetSliders', 'responsiveSlides', 'offsetSections', 'dragAndMove', 'scrollOverflowReset', 'parallax'];
24030            //UNCODE.addition
24031            // if($('html').hasClass(ENABLED)){
24032            //     showError('error', 'Fullpage.js can only be initialized once and you are doing it multiple times!');
24033            //     return;
24034            // }
24035
24036            // Disable mutually exclusive settings
24037            //UNCODE.addition
24038            // if (options.continuousVertical &&
24039            //     (options.loopTop || options.loopBottom)) {
24040            //     options.continuousVertical = false;
24041            //     showError('warn', 'Option `loopTop/loopBottom` is mutually exclusive with `continuousVertical`; `continuousVertical` disabled');
24042            // }
24043
24044            // if(options.scrollBar && options.scrollOverflow){
24045            //     showError('warn', 'Option `scrollBar` is mutually exclusive with `scrollOverflow`. Sections with scrollOverflow might not work well in Firefox');
24046            // }
24047
24048            // if(options.continuousVertical && (options.scrollBar || !options.autoScrolling)){
24049            //     options.continuousVertical = false;
24050            //     showError('warn', 'Scroll bars (`scrollBar:true` or `autoScrolling:false`) are mutually exclusive with `continuousVertical`; `continuousVertical` disabled');
24051            // }
24052
24053            //using extensions? Wrong file!
24054            $.each(extensions, function(index, extension){
24055                //is the option set to true?
24056                if(options[extension]){
24057                    showError('warn', 'fullpage.js extensions require jquery.fullpage.extensions.min.js file instead of the usual jquery.fullpage.js. Requested: '+ extension);
24058                }
24059            });
24060
24061            //anchors can not have the same value as any element ID or NAME
24062            $.each(options.anchors, function(index, name){
24063
24064                //case insensitive selectors (http://stackoverflow.com/a/19465187/1081396)
24065                var nameAttr = $document.find('[name]').filter(function() {
24066                    return $(this).attr('name') && $(this).attr('name').toLowerCase() == name.toLowerCase();
24067                });
24068
24069                var idAttr = $document.find('[id]').filter(function() {
24070                    return $(this).attr('id') && $(this).attr('id').toLowerCase() == name.toLowerCase();
24071                });
24072
24073                if(idAttr.length || nameAttr.length ){
24074                    showError('error', 'data-anchor tags can not have the same value as any `id` element on the site (or `name` element for IE).');
24075                    idAttr.length && showError('error', '"' + name + '" is is being used by another element `id` property');
24076                    nameAttr.length && showError('error', '"' + name + '" is is being used by another element `name` property');
24077                }
24078            });
24079        }
24080
24081        /**
24082        * Shows a message in the console of the given type.
24083        */
24084        function showError(type, text){
24085            console && console[type] && console[type]('fullPage: ' + text);
24086        }
24087
24088    }; //end of $.fn.fullpage
24089
24090    if(typeof IScroll !== 'undefined'){
24091        /*
24092        * Turns iScroll `mousewheel` option off dynamically
24093        * https://github.com/cubiq/iscroll/issues/1036
24094        */
24095        IScroll.prototype.wheelOn = function () {
24096            this.wrapper.addEventListener('wheel', this);
24097            this.wrapper.addEventListener('mousewheel', this);
24098            this.wrapper.addEventListener('DOMMouseScroll', this);
24099        };
24100
24101        /*
24102        * Turns iScroll `mousewheel` option on dynamically
24103        * https://github.com/cubiq/iscroll/issues/1036
24104        */
24105        IScroll.prototype.wheelOff = function () {
24106            this.wrapper.removeEventListener('wheel', this);
24107            this.wrapper.removeEventListener('mousewheel', this);
24108            this.wrapper.removeEventListener('DOMMouseScroll', this);
24109        };
24110    }
24111
24112    /**
24113     * An object to handle overflow scrolling.
24114     * This uses jquery.slimScroll to accomplish overflow scrolling.
24115     * It is possible to pass in an alternate scrollOverflowHandler
24116     * to the fullpage.js option that implements the same functions
24117     * as this handler.
24118     *
24119     * @type {Object}
24120     */
24121    var iscrollHandler = {
24122        refreshId: null,
24123        iScrollInstances: [],
24124
24125        // Enables or disables the mouse wheel for the active section or all slides in it
24126        toggleWheel: function(value){
24127            var scrollable = $(SECTION_ACTIVE_SEL).find(SCROLLABLE_SEL);
24128            scrollable.each(function(){
24129                var iScrollInstance = $(this).data('iscrollInstance');
24130                if(typeof iScrollInstance !== 'undefined' && iScrollInstance){
24131                    if(value){
24132                        iScrollInstance.wheelOn();
24133                    }
24134                    else{
24135                        iScrollInstance.wheelOff();
24136                    }
24137                }
24138            });
24139        },
24140
24141        /**
24142        * Turns off iScroll for the destination section.
24143        * When scrolling very fast on some trackpads (and Apple laptops) the inertial scrolling would
24144        * scroll the destination section/slide before the sections animations ends.
24145        */
24146        onLeave: function(){
24147            iscrollHandler.toggleWheel(false);
24148        },
24149
24150        // Turns off iScroll for the leaving section
24151        beforeLeave: function(){
24152            iscrollHandler.onLeave()
24153        },
24154
24155        // Turns on iScroll on section load
24156        afterLoad: function(){
24157            iscrollHandler.toggleWheel(true);
24158        },
24159
24160        /**
24161         * Called when overflow scrolling is needed for a section.
24162         *
24163         * @param  {Object} element      jQuery object containing current section
24164         * @param  {Number} scrollHeight Current window height in pixels
24165         */
24166        create: function(element, scrollHeight) {
24167            var scrollable = element.find(SCROLLABLE_SEL);
24168
24169            scrollable.height(scrollHeight);
24170            scrollable.each(function() {
24171                var $this = $(this);
24172                var iScrollInstance = $this.data('iscrollInstance');
24173                if (iScrollInstance) {
24174                    $.each(iscrollHandler.iScrollInstances, function(){
24175                        $(this).destroy();
24176                    });
24177                }
24178
24179                iScrollInstance = new IScroll($this.get(0), iscrollOptions);
24180                iscrollHandler.iScrollInstances.push(iScrollInstance);
24181
24182                //off by default until the section gets active
24183                iScrollInstance.wheelOff();
24184
24185                $this.data('iscrollInstance', iScrollInstance);
24186            });
24187        },
24188
24189        /**
24190         * Return a boolean depending on whether the scrollable element is a
24191         * the end or at the start of the scrolling depending on the given type.
24192         *
24193         * @param  {String}  type       Either 'top' or 'bottom'
24194         * @param  {Object}  scrollable jQuery object for the scrollable element
24195         * @return {Boolean}
24196         */
24197        isScrolled: function(type, scrollable) {
24198            var scroller = scrollable.data('iscrollInstance');
24199
24200            //no scroller?
24201            if (!scroller) {
24202                return true;
24203            }
24204
24205            if (type === 'top') {
24206                return scroller.y >= 0 && !scrollable.scrollTop();
24207            } else if (type === 'bottom') {
24208                return (0 - scroller.y) + scrollable.scrollTop() + 1 + scrollable.innerHeight() >= scrollable[0].scrollHeight;
24209            }
24210        },
24211
24212        /**
24213         * Returns the scrollable element for the given section.
24214         * If there are landscape slides, will only return a scrollable element
24215         * if it is in the active slide.
24216         *
24217         * @param  {Object}  activeSection jQuery object containing current section
24218         * @return {Boolean}
24219         */
24220        scrollable: function(activeSection){
24221            // if there are landscape slides, we check if the scrolling bar is in the current one or not
24222            if (activeSection.find(SLIDES_WRAPPER_SEL).length) {
24223                return activeSection.find(SLIDE_ACTIVE_SEL).find(SCROLLABLE_SEL);
24224            }
24225            return activeSection.find(SCROLLABLE_SEL);
24226        },
24227
24228        /**
24229         * Returns the scroll height of the wrapped content.
24230         * If this is larger than the window height minus section padding,
24231         * overflow scrolling is needed.
24232         *
24233         * @param  {Object} element jQuery object containing current section
24234         * @return {Number}
24235         */
24236        scrollHeight: function(element) {
24237            return element.find(SCROLLABLE_SEL).children().first().get(0).scrollHeight;
24238        },
24239
24240        /**
24241         * Called when overflow scrolling is no longer needed for a section.
24242         *
24243         * @param  {Object} element      jQuery object containing current section
24244         */
24245        remove: function(element) {
24246            var scrollable = element.find(SCROLLABLE_SEL);
24247            if (scrollable.length) {
24248                var iScrollInstance = scrollable.data('iscrollInstance');
24249                iScrollInstance.destroy();
24250
24251                scrollable.data('iscrollInstance', null);
24252            }
24253            element.find(SCROLLABLE_SEL).children().first().children().first().unwrap().unwrap();
24254        },
24255
24256        /**
24257         * Called when overflow scrolling has already been setup but the
24258         * window height has potentially changed.
24259         *
24260         * @param  {Object}
vendor: 16,384 bytes, lines 24260-24803
24260 element      jQuery object containing current section
24261         * @param  {Number} scrollHeight Current window height in pixels
24262         */
24263        update: function(element, scrollHeight) {
24264            //using a timeout in order to execute the refresh function only once when `update` is called multiple times in a
24265            //short period of time.
24266            //it also comes on handy because iScroll requires the use of timeout when using `refresh`.
24267            clearTimeout(iscrollHandler.refreshId);
24268            iscrollHandler.refreshId = setTimeout(function(){
24269                $.each(iscrollHandler.iScrollInstances, function(){
24270                    $(this).get(0).refresh();
24271                });
24272            }, 150);
24273
24274            //updating the wrappers height
24275            element.find(SCROLLABLE_SEL).css('height', scrollHeight + 'px').parent().css('height', scrollHeight + 'px');
24276        },
24277
24278        /**
24279         * Called to get any additional elements needed to wrap the section
24280         * content in order to facilitate overflow scrolling.
24281         *
24282         * @return {String|Object} Can be a string containing HTML,
24283         *                         a DOM element, or jQuery object.
24284         */
24285        wrapContent: function() {
24286            return '<div class="' + SCROLLABLE + '"><div class="fp-scroller"></div></div>';
24287        }
24288    };
24289});
24290
24291/* ========================================================================
24292 * Bootstrap: collapse.js v3.1.1
24293 * http://getbootstrap.com/javascript/#collapse
24294 * ========================================================================
24295 * Copyright 2011-2014 Twitter, Inc.
24296 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
24297 * ======================================================================== */
24298
24299
24300+function ($) {
24301  'use strict';
24302
24303  // COLLAPSE PUBLIC CLASS DEFINITION
24304  // ================================
24305
24306  var Collapse = function (element, options) {
24307    this.$element      = $(element)
24308    this.options       = $.extend({}, Collapse.DEFAULTS, options)
24309    this.transitioning = null
24310
24311    if (this.options.parent) this.$parent = $(this.options.parent)
24312    if (this.options.toggle) this.toggle()
24313  }
24314
24315  Collapse.DEFAULTS = {
24316    toggle: true
24317  }
24318
24319  Collapse.prototype.dimension = function () {
24320    var hasWidth = this.$element.hasClass('width')
24321    return hasWidth ? 'width' : 'height'
24322  }
24323
24324  Collapse.prototype.show = function () {
24325    if (this.transitioning || this.$element.hasClass('in')) return
24326
24327    var startEvent = $.Event('show.bs.collapse')
24328    this.$element.trigger(startEvent)
24329    if (startEvent.isDefaultPrevented()) return
24330
24331    var actives = this.$parent && this.$parent.find('> .panel > .in')
24332
24333    if (actives && actives.length) {
24334      var hasData = actives.data('bs.collapse')
24335      if (hasData && hasData.transitioning) return
24336      actives.collapse('hide')
24337      hasData || actives.data('bs.collapse', null)
24338    }
24339
24340    var dimension = this.dimension()
24341
24342    this.$element
24343      .removeClass('collapse')
24344      .addClass('collapsing')[dimension](0)
24345
24346    this.transitioning = 1
24347
24348    var complete = function (e) {
24349      if (e && e.target != this.$element[0]) return
24350      this.$element
24351        .removeClass('collapsing')
24352        .addClass('collapse in')[dimension]('auto')
24353      this.transitioning = 0
24354      this.$element.trigger('shown.bs.collapse')
24355    }
24356
24357    if (!$.support.transition) return complete.call(this)
24358
24359    var scrollSize = $.camelCase(['scroll', dimension].join('-'))
24360
24361    this.$element
24362      .one($.support.transition.end, $.proxy(complete, this))
24363      .emulateTransitionEnd(350)[dimension](this.$element[0][scrollSize])
24364  }
24365
24366  Collapse.prototype.hide = function () {
24367    if (this.transitioning || !this.$element.hasClass('in')) return
24368
24369    var startEvent = $.Event('hide.bs.collapse')
24370    this.$element.trigger(startEvent)
24371    if (startEvent.isDefaultPrevented()) return
24372
24373    var dimension = this.dimension()
24374
24375    this.$element[dimension](this.$element[dimension]())[0].offsetHeight
24376
24377    this.$element
24378      .addClass('collapsing')
24379      .removeClass('collapse')
24380      .removeClass('in')
24381
24382    this.transitioning = 1
24383
24384    var complete = function (e) {
24385      if (e && e.target != this.$element[0]) return
24386      this.transitioning = 0
24387      this.$element
24388        .trigger('hidden.bs.collapse')
24389        .removeClass('collapsing')
24390        .addClass('collapse')
24391    }
24392
24393    if (!$.support.transition) return complete.call(this)
24394
24395    this.$element
24396      [dimension](0)
24397      .one($.support.transition.end, $.proxy(complete, this))
24398      .emulateTransitionEnd(350)
24399  }
24400
24401  Collapse.prototype.toggle = function () {
24402    this[this.$element.hasClass('in') ? 'hide' : 'show']()
24403  }
24404
24405
24406  // COLLAPSE PLUGIN DEFINITION
24407  // ==========================
24408
24409  var old = $.fn.collapse
24410
24411  $.fn.collapse = function (option) {
24412    return this.each(function () {
24413      var $this   = $(this)
24414      var data    = $this.data('bs.collapse')
24415      var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
24416
24417      if (!data && options.toggle && option == 'show') option = !option
24418      if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
24419      if (typeof option == 'string') data[option]()
24420    })
24421  }
24422
24423  $.fn.collapse.Constructor = Collapse
24424
24425
24426  // COLLAPSE NO CONFLICT
24427  // ====================
24428
24429  $.fn.collapse.noConflict = function () {
24430    $.fn.collapse = old
24431    return this
24432  }
24433
24434
24435  // COLLAPSE DATA-API
24436  // =================
24437
24438  $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
24439    var $this   = $(this), href
24440    var target  = $this.attr('data-target')
24441        || e.preventDefault()
24442        || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
24443    //Edited by Uncode to replace ID in panel [START]
24444    var _target = href.replace(/^#/, "");
24445    if ( $('[data-id="' + _target + '"]').length ) {
24446      var $target = $('[data-id="' + _target + '"]')
24447    } else {
24448      var $target = $(target)
24449    }
24450    //Edited by Uncode to replace ID in panel [END]
24451    var data    = $target.data('bs.collapse')
24452    var option  = data ? 'toggle' : $this.data()
24453    var parent  = $this.attr('data-parent')
24454    var $parent = parent && $(parent)
24455
24456    if (!data || !data.transitioning) {
24457      if ($parent) $parent.find('[data-toggle="collapse"][data-parent="' + parent + '"]').not($this).addClass('collapsed')
24458      $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
24459    }
24460
24461    $target.collapse(option)
24462  })
24463
24464}(jQuery);
24465
24466/* ========================================================================
24467 * Bootstrap: tab.js v3.1.1
24468 * http://getbootstrap.com/javascript/#tabs
24469 * ========================================================================
24470 * Copyright 2011-2014 Twitter, Inc.
24471 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
24472 * ======================================================================== */
24473
24474
24475+function ($) {
24476  'use strict';
24477
24478  // TAB CLASS DEFINITION
24479  // ====================
24480
24481  var Tab = function (element) {
24482    this.element = $(element)
24483  }
24484
24485  Tab.prototype.show = function () {
24486    var $this    = this.element
24487    var $ul      = $this.closest('ul:not(.dropdown-menu)')
24488    var selector = $this.data('target')
24489
24490    if (!selector) {
24491      selector = $this.attr('href')
24492      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
24493    }
24494
24495    if ($this.parent('li').hasClass('active')) return
24496
24497    var previous = $ul.find('.active:last a')[0]
24498    var e        = $.Event('show.bs.tab', {
24499      relatedTarget: previous
24500    })
24501
24502    $this.trigger(e)
24503
24504    if (e.isDefaultPrevented()) return
24505
24506    //Edited by Uncode to replace ID in panel [START]
24507    var _target = selector.replace(/^#/, "");
24508    if ( $('[data-id="' + _target + '"]').length )
24509      var $target = $('[data-id="' + _target + '"]')
24510    else
24511      var $target = $(selector)
24512    //Edited by Uncode to replace ID in panel [END]
24513
24514    this.activate($this.parent('li'), $ul)
24515    this.activate($target, $target.parent(), function () {
24516      $this.trigger({
24517        type: 'shown.bs.tab',
24518        relatedTarget: previous
24519      })
24520    })
24521  }
24522
24523  Tab.prototype.activate = function (element, container, callback) {
24524    var $active    = container.find('> .active')
24525    var transition = callback
24526      && $.support.transition
24527      && $active.hasClass('fade')
24528
24529    function next() {
24530      $active
24531        .removeClass('active')
24532        .find('> .dropdown-menu > .active')
24533        .removeClass('active')
24534
24535      element.addClass('active')
24536
24537      if (transition) {
24538        element[0].offsetWidth // reflow for transition
24539        element.addClass('in')
24540      } else {
24541        element.removeClass('fade')
24542      }
24543
24544      if (element.parent('.dropdown-menu')) {
24545        element.closest('li.dropdown').addClass('active')
24546      }
24547
24548      callback && callback()
24549    }
24550
24551    transition ?
24552      $active
24553        .one($.support.transition.end, next)
24554        .emulateTransitionEnd(150) :
24555      next()
24556
24557    $active.removeClass('in')
24558  }
24559
24560
24561  // TAB PLUGIN DEFINITION
24562  // =====================
24563
24564  var old = $.fn.tab
24565
24566  $.fn.tab = function ( option ) {
24567    return this.each(function () {
24568      var $this = $(this)
24569      var data  = $this.data('bs.tab')
24570
24571      if (!data) $this.data('bs.tab', (data = new Tab(this)))
24572      if (typeof option == 'string') data[option]()
24573    })
24574  }
24575
24576  $.fn.tab.Constructor = Tab
24577
24578
24579  // TAB NO CONFLICT
24580  // ===============
24581
24582  $.fn.tab.noConflict = function () {
24583    $.fn.tab = old
24584    return this
24585  }
24586
24587
24588  // TAB DATA-API
24589  // ============
24590
24591  $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
24592    e.preventDefault()
24593    $(this).tab('show')
24594  })
24595
24596}(jQuery);
24597
24598/* ========================================================================
24599 * Bootstrap: tooltip.js v3.1.1
24600 * http://getbootstrap.com/javascript/#tooltip
24601 * Inspired by the original jQuery.tipsy by Jason Frame
24602 * ========================================================================
24603 * Copyright 2011-2014 Twitter, Inc.
24604 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
24605 * ======================================================================== */
24606
24607
24608+function ($) {
24609  'use strict';
24610
24611  // TOOLTIP PUBLIC CLASS DEFINITION
24612  // ===============================
24613
24614  var Tooltip = function (element, options) {
24615    this.type       =
24616    this.options    =
24617    this.enabled    =
24618    this.timeout    =
24619    this.hoverState =
24620    this.$element   = null
24621
24622    this.init('tooltip', element, options)
24623  }
24624
24625  Tooltip.DEFAULTS = {
24626    animation: true,
24627    placement: 'top',
24628    selector: false,
24629    template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
24630    trigger: 'hover focus',
24631    title: '',
24632    delay: 0,
24633    html: false,
24634    container: false,
24635    viewport: {
24636      selector: 'body',
24637      padding: 0
24638    }
24639  }
24640
24641  Tooltip.prototype.init = function (type, element, options) {
24642    this.enabled   = true
24643    this.type      = type
24644    this.$element  = $(element)
24645    this.options   = this.getOptions(options)
24646    this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)
24647
24648    var triggers = this.options.trigger.split(' ')
24649
24650    for (var i = triggers.length; i--;) {
24651      var trigger = triggers[i]
24652
24653      if (trigger == 'click') {
24654        this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
24655      } else if (trigger != 'manual') {
24656        var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
24657        var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
24658
24659        this.$element.on(eventIn  + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
24660        this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
24661      }
24662    }
24663
24664    this.options.selector ?
24665      (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
24666      this.fixTitle()
24667  }
24668
24669  Tooltip.prototype.getDefaults = function () {
24670    return Tooltip.DEFAULTS
24671  }
24672
24673  Tooltip.prototype.getOptions = function (options) {
24674    options = $.extend({}, this.getDefaults(), this.$element.data(), options)
24675
24676    if (options.delay && typeof options.delay == 'number') {
24677      options.delay = {
24678        show: options.delay,
24679        hide: options.delay
24680      }
24681    }
24682
24683    return options
24684  }
24685
24686  Tooltip.prototype.getDelegateOptions = function () {
24687    var options  = {}
24688    var defaults = this.getDefaults()
24689
24690    this._options && $.each(this._options, function (key, value) {
24691      if (defaults[key] != value) options[key] = value
24692    })
24693
24694    return options
24695  }
24696
24697  Tooltip.prototype.enter = function (obj) {
24698    var self = obj instanceof this.constructor ?
24699      obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
24700
24701    clearTimeout(self.timeout)
24702
24703    self.hoverState = 'in'
24704
24705    if (!self.options.delay || !self.options.delay.show) return self.show()
24706
24707    self.timeout = setTimeout(function () {
24708      if (self.hoverState == 'in') self.show()
24709    }, self.options.delay.show)
24710  }
24711
24712  Tooltip.prototype.leave = function (obj) {
24713    var self = obj instanceof this.constructor ?
24714      obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
24715
24716    clearTimeout(self.timeout)
24717
24718    self.hoverState = 'out'
24719
24720    if (!self.options.delay || !self.options.delay.hide) return self.hide()
24721
24722    self.timeout = setTimeout(function () {
24723      if (self.hoverState == 'out') self.hide()
24724    }, self.options.delay.hide)
24725  }
24726
24727  Tooltip.prototype.show = function () {
24728    var e = $.Event('show.bs.' + this.type)
24729
24730    if (this.hasContent() && this.enabled) {
24731      this.$element.trigger(e)
24732
24733      if (e.isDefaultPrevented()) return
24734      var that = this;
24735
24736      var $tip = this.tip()
24737
24738      this.setContent()
24739
24740      if (this.options.animation) $tip.addClass('fade')
24741
24742      var placement = typeof this.options.placement == 'function' ?
24743        this.options.placement.call(this, $tip[0], this.$element[0]) :
24744        this.options.placement
24745
24746      var autoToken = /\s?auto?\s?/i
24747      var autoPlace = autoToken.test(placement)
24748      if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
24749
24750      $tip
24751        .detach()
24752        .css({ top: 0, left: 0, display: 'block' })
24753        .addClass(placement)
24754
24755      this.options.container ? $tip.appendTo($(document).find(this.options.container)) : $tip.insertAfter(this.$element)
24756
24757      var pos          = this.getPosition()
24758      var actualWidth  = $tip[0].offsetWidth
24759      var actualHeight = $tip[0].offsetHeight
24760
24761      if (autoPlace) {
24762        var orgPlacement = placement
24763        var $parent      = this.$element.parent()
24764        var parentDim    = this.getPosition($parent)
24765
24766        placement = placement == 'bottom' && pos.top   + pos.height       + actualHeight - parentDim.scroll > parentDim.height ? 'top'    :
24767                    placement == 'top'    && pos.top   - parentDim.scroll - actualHeight < 0                                   ? 'bottom' :
24768                    placement == 'right'  && pos.right + actualWidth      > parentDim.width                                    ? 'left'   :
24769                    placement == 'left'   && pos.left  - actualWidth      < parentDim.left                                     ? 'right'  :
24770                    placement
24771
24772        $tip
24773          .removeClass(orgPlacement)
24774          .addClass(placement)
24775      }
24776
24777      var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
24778
24779      this.applyPlacement(calculatedOffset, placement)
24780      this.hoverState = null
24781
24782      var complete = function() {
24783        that.$element.trigger('shown.bs.' + that.type)
24784      }
24785
24786      $.support.transition && this.$tip.hasClass('fade') ?
24787        $tip
24788          .one($.support.transition.end, complete)
24789          .emulateTransitionEnd(150) :
24790        complete()
24791    }
24792  }
24793
24794  Tooltip.prototype.applyPlacement = function (offset, placement) {
24795    var $tip   = this.tip()
24796    var width  = $tip[0].offsetWidth
24797    var height = $tip[0].offsetHeight
24798
24799    // manually read margins because getBoundingClientRect includes difference
24800    var marginTop = parseInt($tip.css('margin-top'), 10)
24801    var marginLeft = parseInt($tip.css('margin-left'), 10)
24802
24803    // we must check
24803 for NaN for ie 8/9
24804    if (isNaN(marginTop))  marginTop  = 0
24805    if (isNaN(marginLeft)) marginLeft = 0
24806
24807    offset.top  = offset.top  + marginTop
24808    offset.left = offset.left + marginLeft
24809
24810    // $.fn.offset doesn't round pixel values
24811    // so we use setOffset directly with our own function B-0
24812    $.offset.setOffset($tip[0], $.extend({
24813      using: function (props) {
24814        $tip.css({
24815          top: Math.round(props.top),
24816          left: Math.round(props.left)
24817        })
24818      }
24819    }, offset), 0)
24820
24821    $tip.addClass('in')
24822
24823    // check to see if placing tip in new offset caused the tip to resize itself
24824    var actualWidth  = $tip[0].offsetWidth
24825    var actualHeight = $tip[0].offsetHeight
24826
24827    if (placement == 'top' && actualHeight != height) {
24828      offset.top = offset.top + height - actualHeight
24829    }
24830
24831    var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
24832
24833    if (delta.left) offset.left += delta.left
24834    else offset.top += delta.top
24835
24836    var arrowDelta          = delta.left ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
24837    var arrowPosition       = delta.left ? 'left'        : 'top'
24838    var arrowOffsetPosition = delta.left ? 'offsetWidth' : 'offsetHeight'
24839
24840    $tip.offset(offset)
24841    this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], arrowPosition)
24842  }
24843
24844  Tooltip.prototype.replaceArrow = function (delta, dimension, position) {
24845    this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '')
24846  }
24847
24848  Tooltip.prototype.setContent = function () {
24849    var $tip  = this.tip()
24850    var title = this.getTitle()
24851
24852    $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
24853    $tip.removeClass('fade in top bottom left right')
24854  }
24855
24856  Tooltip.prototype.hide = function () {
24857    var that = this
24858    var $tip = this.tip()
24859    var e    = $.Event('hide.bs.' + this.type)
24860
24861    function complete() {
24862      if (that.hoverState != 'in') $tip.detach()
24863      that.$element.trigger('hidden.bs.' + that.type)
24864    }
24865
24866    this.$element.trigger(e)
24867
24868    if (e.isDefaultPrevented()) return
24869
24870    $tip.removeClass('in')
24871
24872    $.support.transition && this.$tip.hasClass('fade') ?
24873      $tip
24874        .one($.support.transition.end, complete)
24875        .emulateTransitionEnd(150) :
24876      complete()
24877
24878    this.hoverState = null
24879
24880    return this
24881  }
24882
24883  Tooltip.prototype.fixTitle = function () {
24884    var $e = this.$element
24885    if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
24886      $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
24887    }
24888  }
24889
24890  Tooltip.prototype.hasContent = function () {
24891    return this.getTitle()
24892  }
24893
24894  Tooltip.prototype.getPosition = function ($element) {
24895    $element   = $element || this.$element
24896    var el     = $element[0]
24897    var isBody = el.tagName == 'BODY'
24898    return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : null, {
24899      scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop(),
24900      width:  isBody ? $(window).width()  : $element.outerWidth(),
24901      height: isBody ? $(window).height() : $element.outerHeight()
24902    }, isBody ? {top: 0, left: 0} : $element.offset())
24903  }
24904
24905  Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
24906    return placement == 'bottom' ? { top: pos.top + pos.height,   left: pos.left + pos.width / 2 - actualWidth / 2  } :
24907           placement == 'top'    ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2  } :
24908           placement == 'left'   ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
24909        /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width   }
24910
24911  }
24912
24913  Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
24914    var delta = { top: 0, left: 0 }
24915    if (!this.$viewport) return delta
24916
24917    var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
24918    var viewportDimensions = this.getPosition(this.$viewport)
24919
24920    if (/right|left/.test(placement)) {
24921      var topEdgeOffset    = pos.top - viewportPadding - viewportDimensions.scroll
24922      var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
24923      if (topEdgeOffset < viewportDimensions.top) { // top overflow
24924        delta.top = viewportDimensions.top - topEdgeOffset
24925      } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
24926        delta.top = viewportDimensions.top + viewportDimensions.height - bottom
24926EdgeOffset
24927      }
24928    } else {
24929      var leftEdgeOffset  = pos.left - viewportPadding
24930      var rightEdgeOffset = pos.left + viewportPadding + actualWidth
24931      if (leftEdgeOffset < viewportDimensions.left) { // left overflow
24932        delta.left = viewportDimensions.left - leftEdgeOffset
24933      } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
24934        delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
24935      }
24936    }
24937
24938    return delta
24939  }
24940
24941  Tooltip.prototype.getTitle = function () {
24942    var title
24943    var $e = this.$element
24944    var o  = this.options
24945
24946    title = $e.attr('data-original-title')
24947      || (typeof o.title == 'function' ? o.title.call($e[0]) :  o.title)
24948
24949    return title
24950  }
24951
24952  Tooltip.prototype.tip = function () {
24953    return this.$tip = this.$tip || $(this.options.template)
24954  }
24955
24956  Tooltip.prototype.arrow = function () {
24957    return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')
24958  }
24959
24960  Tooltip.prototype.validate = function () {
24961    if (!this.$element[0].parentNode) {
24962      this.hide()
24963      this.$element = null
24964      this.options  = null
24965    }
24966  }
24967
24968  Tooltip.prototype.enable = function () {
24969    this.enabled = true
24970  }
24971
24972  Tooltip.prototype.disable = function () {
24973    this.enabled = false
24974  }
24975
24976  Tooltip.prototype.toggleEnabled = function () {
24977    this.enabled = !this.enabled
24978  }
24979
24980  Tooltip.prototype.toggle = function (e) {
24981    var self = e ? $(e.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) : this
24982    self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
24983  }
24984
24985  Tooltip.prototype.destroy = function () {
24986    clearTimeout(this.timeout)
24987    this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
24988  }
24989
24990
24991  // TOOLTIP PLUGIN DEFINITION
24992  // =========================
24993
24994  var old = $.fn.tooltip
24995
24996  $.fn.tooltip = function (option) {
24997    return this.each(function () {
24998      var $this   = $(this)
24999      var data    = $this.data('bs.tooltip')
25000      var options = typeof option == 'object' && option
25001
25002      if (!data && option == 'destroy') return
25003      if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
25004      if (typeof option == 'string') data[option]()
25005    })
25006  }
25007
25008  $.fn.tooltip.Constructor = Tooltip
25009
25010
25011  // TOOLTIP NO CONFLICT
25012  // ===================
25013
25014  $.fn.tooltip.noConflict = function () {
25015    $.fn.tooltip = old
25016    return this
25017  }
25018
25019}(jQuery);
25020
25021/* ========================================================================
25022 * Bootstrap: transition.js v3.1.1
25023 * http://getbootstrap.com/javascript/#transitions
25024 * ========================================================================
25025 * Copyright 2011-2014 Twitter, Inc.
25026 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
25027 * ======================================================================== */
25028
25029
25030+function ($) {
25031  'use strict';
25032
25033  // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
25034  // ============================================================
25035
25036  function transitionEnd() {
25037    var el = document.createElement('bootstrap')
25038
25039    var transEndEventNames = {
25040      WebkitTransition : 'webkitTransitionEnd',
25041      MozTransition    : 'transitionend',
25042      OTransition      : 'oTransitionEnd otransitionend',
25043      transition       : 'transitionend'
25044    }
25045
25046    for (var name in transEndEventNames) {
25047      if (el.style[name] !== undefined) {
25048        return { end: transEndEventNames[name] }
25049      }
25050    }
25051
25052    return false // explicit for ie8 (  ._.)
25053  }
25054
25055  // http://blog.alexmaccaw.com/css-transitions
25056  $.fn.emulateTransitionEnd = function (duration) {
25057    var called = false, $el = this
25058    $(this).one($.support.transition.end, function () { called = true })
25059    var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
25060    setTimeout(callback, duration)
25061    return this
25062  }
25063
25064  $(function () {
25065    $.support.transition = transitionEnd()
25066  })
25067
25068}(jQuery);

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