PageSourceSearch

https://hoopladigital.co.uk/wp-content/themes/uncode/library/js/plugins.js?ver=660000459

js hoopladigital.co.uk collected 2026-10-06 12:55:02 UTC 823,647 bytes, 25,074 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    // START UNCODE EDIT
1667    // if ( !utils.isElement( elem ) ) {
1668    if ( !utils.isElement( elem ) && !SiteParameters.is_frontend_editor ) {
1669    // END UNCODE EDIT
1670      continue;
1671    }
1672    // filter & find items if we have a selector
1673    if ( selector ) {
1674      // filter siblings
1675      if ( matchesSelector( elem, selector ) ) {
1676        ffElems.push( elem );
1677      }
1678      // find children
1679      var childElems = elem.querySelectorAll( selector );
1680      // concat childElems to filterFound array
1681      for ( var j=0, jLen = childElems.length; j < jLen; j++ ) {
1682        ffElems.push( childElems[j] );
1683      }
1684    } else {
1685      ffElems.push( elem );
1686    }
1687  }
1688
1689  return ffElems;
1690};
1691
1692// ----- debounceMethod ----- //
1693
1694utils.debounceMethod = function( _class, methodName, threshold ) {
1695  // original method
1696  var method = _class.prototype[ methodName ];
1697  var timeoutName = methodName + 'Timeout';
1698
1699  _class.prototype[ methodName ] = function() {
1700    var timeout = this[ timeoutName ];
1701    if ( timeout ) {
1702      clearTimeout( timeout );
1703    }
1704    var args = arguments;
1705
1706    var _this = this;
1707    this[ timeoutName ] = setTimeout( function() {
1708      method.apply( _this, args );
1709      delete _this[ timeoutName ];
1710    }, threshold || 100 );
1711  };
1712};
1713
1714// ----- htmlInit ----- //
1715
1716// http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
1717utils.toDashed = function( str ) {
1718  return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
1719    return $1 + '-' + $2;
1720  }).toLowerCase();
1721};
1722
1723var console = window.console;
1724/**
1725 * allow user to initialize classes via .js-namespace class
1726 * htmlInit( Widget, 'widgetName' )
1727 * options are parsed from data-namespace-option attribute
1728 */
1729utils.htmlInit = function( WidgetClass, namespace ) {
1730  docReady( function() {
1731    var dashedNamespace = utils.toDashed( namespace );
1732    var elems = document.querySelectorAll( '.js-' + dashedNamespace );
1733    var dataAttr = 'data-' + dashedNamespace + '-options';
1734
1735    for ( var i=0, len = elems.length; i < len; i++ ) {
1736      var elem = elems[i];
1737      var attr = elem.getAttribute( dataAttr );
1738      var options;
1739      try {
1740        options = attr && JSON.parse( attr );
1741      } catch ( error ) {
1742        // log error, do not initialize
1743        if ( console ) {
1744          console.error( 'Error parsing ' + dataAttr + ' on ' +
1745            elem.nodeName.toLowerCase() + ( elem.id ? '#' + elem.id : '' ) + ': ' +
1746            error );
1747        }
1748        continue;
1749      }
1750      // initialize
1751      var instance = new WidgetClass( elem, options );
1752      // make available via $().data('layoutname')
1753      var jQuery = window.jQuery;
1754      if ( jQuery ) {
1755        jQuery.data( elem, namespace, instance );
1756      }
1757    }
1758  });
1759};
1760
1761// -----  ----- //
1762
1763return utils;
1764
1765}));
1766
1767/**
1768 * Outlayer Item
1769 */
1770
1771( function( window, factory ) {
1772  'use strict';
1773  // universal module definition
1774  if ( typeof define === 'function' && define.amd ) {
1775    // AMD
1776    define( 'outlayer/item',[
1777        'eventEmitter/EventEmitter',
1778        'get-size/get-size',
1779        'get-style-property/get-style-property',
1780        'fizzy-ui-utils/utils'
1781      ],
1782      function( EventEmitter, getSize, getStyleProperty, utils ) {
1783        return factory( window, EventEmitter, getSize, getStyleProperty, utils );
1784      }
1785    );
1786  } else if (typeof exports === 'object') {
1787    // CommonJS
1788    module.exports = factory(
1789      window,
1790      require('wolfy87-eventemitter'),
1791      require('get-size'),
1792      require('desandro-get-style-property'),
1793      require('fizzy-ui-utils')
1794    );
1795  } else {
1796    // browser global
1797    window.Outlayer = {};
1798    window.Outlayer.Item = factory(
1799      window,
1800      window.EventEmitter,
1801      window.getSize,
1802      window.getStyleProperty,
1803      window.fizzyUIUtils
1804    );
1805  }
1806
1807}( window, function factory( window, EventEmitter, getSize, getStyleProperty, utils ) {
1808'use strict';
1809
1810// ----- helpers ----- //
1811
1812var getComputedStyle = window.getComputedStyle;
1813var getStyle = getComputedStyle ?
1814  function( elem ) {
1815    return getComputedStyle( elem, null );
1816  } :
1817  function( elem ) {
1818    return elem.currentStyle;
1819  };
1820
1821
1822function isEmptyObj( obj ) {
1823  for ( var prop in obj ) {
1824    return false;
1825  }
1826  prop = null;
1827  return true;
1828}
1829
1830// -------------------------- CSS3 support -------------------------- //
1831
1832var transitionProperty = getStyleProperty('transition');
1833var transformProperty = getStyleProperty('transform');
1834var supportsCSS3 = transitionProperty && transformProperty;
1835var is3d = !!getStyleProperty('perspective');
1836
1837var transitionEndEvent = {
1838  WebkitTransition: 'webkitTransitionEnd',
1839  MozTransition: 'transitionend',
1840  OTransition: 'otransitionend',
1841  transition: 'transitionend'
1842}[ transitionProperty ];
1843
1844// properties that could have vendor prefix
1845var prefixableProperties = [
1846  'transform',
1847  'transition',
1848  'transitionDuration',
1849  'transitionProperty'
1850];
1851
1852// cache all vendor properties
1853var vendorProperties = ( function() {
1854  var cache = {};
1855  for ( var i=0, len = prefixableProperties.length; i < len; i++ ) {
1856    var prop = prefixableProperties[i];
1857    var supportedProp = getStyleProperty( prop );
1858    if ( supportedProp && supportedProp !== prop ) {
1859      cache[ prop ] = supportedProp;
1860    }
1861  }
1862  return cache;
1863})();
1864
1865// -------------------------- Item -------------------------- //
1866
1867function Item( element, layout ) {
1868  if ( !element ) {
1869    return;
1870  }
1871
1872  this.element = element;
1873  // parent layout class, i.e. Masonry, Isotope, or Packery
1874  this.layout = layout;
1875  this.position = {
1876    x: 0,
1877    y: 0
1878  };
1879
1880  this._create();
1881}
1882
1883// inherit EventEmitter
1884utils.extend( Item.prototype, EventEmitter.prototype );
1885
1886Item.prototype._create = function() {
1887  // transition objects
1888  this._transn = {
1889    ingProperties: {},
1890    clean: {},
1891    onEnd: {}
1892  };
1893
1894  this.css({
1895    position: 'absolute'
1896  });
1897};
1898
1899// trigger specified handler for event type
1900Item.prototype.handleEvent = function( event ) {
1901  var method = 'on' + event.type;
1902  if ( this[ method ] ) {
1903    this[ method ]( event );
1904  }
1905};
1906
1907Item.prototype.getSize = function() {
1908  this.size = getSize( this.element );
1909};
1910
1911/**
1912 * apply CSS styles to element
1913 * @param {Object} style
1914 */
1915Item.prototype.css = function( style ) {
1916  var elemStyle = this.element.style;
1917
1918  for ( var prop in style ) {
1919    // use vendor property if available
1920    var supportedProp = vendorProperties[ prop ] || prop;
1921    elemStyle[ supportedProp ] = style[ prop ];
1922  }
1923};
1924
1925 // measure position, and sets it
1926Item.prototype.getPosition = function() {
1927  var style = getStyle( this.element );
1928  var layoutOptions = this.layout.options;
1929  var isOriginLeft = layoutOptions.isOriginLeft;
1930  var isOriginTop = layoutOptions.isOriginTop;
1931  var xValue = style[ isOriginLeft ? 'left' : 'right' ];
1932  var yValue = style[ isOriginTop ? 'top' : 'bottom' ];
1933  // convert percent to pixels
1934  var layoutSize = this.layout.size;
1935  var x = xValue.indexOf('%') != -1 ?
1936    ( parseFloat( xValue ) / 100 ) * layoutSize.width : parseInt( xValue, 10 );
1937  var y = yValue.indexOf('%') != -1 ?
1938    ( parseFloat( yValue ) / 100 ) * layoutSize.height : parseInt( yValue, 10 );
1939
1940  // clean up 'auto' or other non-integer values
1941  x = isNaN( x ) ? 0 : x;
1942  y = isNaN( y ) ? 0 : y;
1943  // remove padding from measurement
1944  x -= isOriginLeft ? layoutSize.paddingLeft : layoutSize.paddingRight;
1945  y -= isOriginTop ? layoutSize.paddingTop : layoutSize.paddingBottom;
1946
1947  this.position.x = x;
1948  this.position.y = y;
1949};
1950
1951// set settled position, apply padding
1952Item.prototype.layoutPosition = function() {
1953  var layoutSize = this.layout.size;
1954  var layoutOptions = this.layout.options;
1955  var style = {};
1956
1957  // x
1958  var xPadding = layoutOptions.isOriginLeft ? 'paddingLeft' : 'paddingRight';
1959  var xProperty = layoutOptions.isOriginLeft ? 'left' : 'right';
1960  var xResetProperty = layoutOptions.isOriginLeft ? 'right' : 'left';
1961
1962  var x = this.position.x + layoutSize[ xPadding ];
1963  // set in percentage or pixels
1964  style[ xProperty ] = this.getXValue( x );
1965  // reset other property
1966  style[ xResetProperty ] = '';
1967
1968  // y
1969  var yPadding = layoutOptions.isOriginTop ? 'paddingTop' : 'paddingBottom';
1970  var yProperty = layoutOptions.isOriginTop ? 'top' : 'bottom';
1971  var yResetProperty = layoutOptions.isOriginTop ? 'bottom' : 'top';
1972
1973  var y = this.position.y + layoutSize[ yPadding ];
1974  // set in percentage or pixels
1975  style[ yProperty ] = this.getYValue( y );
1976  // reset other property
1977  style[ yResetProperty ] = '';
1978
1979  this.css( style );
1980  this.emitEvent( 'layout', [ this ] );
1981};
1982
1983Item.prototype.getXValue = function( x ) {
1984  var layoutOptions = this.layout.options;
1985  return layoutOptions.percentPosition && !layoutOptions.isHorizontal ?
1986    ( ( x / this.layout.size.width ) * 100 ) + '%' : x + 'px';
1987};
1988
1989Item.prototype.getYValue = function( y ) {
1990  var layoutOptions = this.layout.options;
1991  return layoutOptions.percentPosition && layoutOptions.isHorizontal ?
1992    ( ( y / this.layout.size.height ) * 100 ) + '%' : y + 'px';
1993};
1994
1995
1996Item.prototype._transitionTo = function( x, y ) {
1997  this.getPosition();
1998  // get current x & y from top/left
1999  var curX = this.position.x;
2000  var curY = this.position.y;
2001
2002  var compareX = parseInt( x, 10 );
2003  var compareY = parseInt( y, 10 );
2004  var didNotMove = compareX === this.position.x && compareY === this.position.y;
2005
2006  // save end position
2007  this.setPosition( x, y );
2008
2009  // if did not move and not transitioning, just go to layout
2010  if ( didNotMove && !this.isTransitioning ) {
2011    this.layoutPosition();
2012    return;
2013  }
2014
2015  var transX = x - curX;
2016  var transY = y - curY;
2017  var transitionStyle = {};
2018  transitionStyle.transform = this.getTranslate( transX, transY );
2019
2020  this.transition({
2021    to: transitionStyle,
2022    onTransitionEnd: {
2023      transform: this.layoutPosition
2024    },
2025    isCleaning: true
2026  });
2027};
2028
2029Item.prototype.getTranslate = function( x, y ) {
2030  // flip cooridinates if origin on right or bottom
2031  var layoutOptions = this.layout.options;
2032  x = layoutOptions.isOriginLeft ? x : -x;
2033  y = layoutOptions.isOriginTop ? y : -y;
2034
2035  if ( is3d ) {
2036    return 'translate3d(' + x + 'px, ' + y + 'px, 0)';
2037  }
2038
2039  return 'translate(' + x + 'px, ' + y + 'px)';
2040};
2041
2042// non transition + transform support
2043Item.prototype.goTo = function( x, y ) {
2044  this.setPosition( x, y );
2045  this.layoutPosition();
2046};
2047
2048// use transition and transforms if supported
2049Item.prototype.moveTo = supportsCSS3 ?
2050  Item.prototype._transitionTo : Item.prototype.goTo;
2051
2052Item.prototype.setPosition = function( x, y ) {
2053  this.position.x = parseInt( x, 10 );
2054  this.position.y = parseInt( y, 10 );
2055};
2056
2057// ----- transition ----- //
2058
2059/**
2060 * @param {Object} style - CSS
2061 * @param {Function} onTransitionEnd
2062 */
2063
2064// non transition, just trigger callback
2065Item.prototype._nonTransition = function( args ) {
2066  this.css( args.to );
2067  if ( args.isCleaning ) {
2068    this._removeStyles( args.to );
2069  }
2070  for ( var prop in args.onTransitionEnd ) {
2071    args.onTransitionEnd[ prop ].call( this );
2072  }
2073};
2074
2075/**
2076 * proper transition
2077 * @param {Object} args - arguments
2078 *   @param {Object} to - style to transition to
2079 *   @param {Object} from - style to start transition from
2080 *   @param {Boolean} isCleaning - removes transition styles after transition
2081 *   @param {Function} onTransitionEnd - callback
2082 */
2083Item.prototype._transition = function( args ) {
2084  // redirect to nonTransition if no transition duration
2085  if ( !parseFloat( this.layout.options.transitionDuration ) ) {
2086    this._nonTransition( args );
2087    return;
2088  }
2089
2090  var _transition = this._transn;
2091  // keep track of onTransitionEnd callback by css property
2092  for ( var prop in args.onTransitionEnd ) {
2093    _transition.onEnd[ prop ] = args.onTransitionEnd[ prop ];
2094  }
2095  // keep track of properties that are transitioning
2096  for ( prop in args.to ) {
2097    _transition.ingProperties[ prop ] = true;
2098    // keep track of properties to clean up when transition is done
2099    if ( args.isCleaning ) {
2100      _transition.clean[ prop ] = true;
2101    }
2102  }
2103
2104  // set from styles
2105  if ( args.from ) {
2106    this.css( args.from );
2107    // force redraw. http://blog.alexmaccaw.com/css-transitions
2108    var h = this.element.offsetHeight;
2109    // hack for JSHint to hush about unused var
2110    h = null;
2111  }
2112  // enable transition
2113  this.enableTransition( args.to );
2114  // set styles that are transitioning
2115  this.css( args.to );
2116
2117  this.isTransitioning = true;
2118
2119};
2120
2121// dash before all cap letters, including first for
2122// WebkitTransform => -webkit-transform
2123function toDashedAll( str ) {
2124  return str.replace( /([A-Z])/g, function( $1 ) {
2125    return '-' + $1.toLowerCase();
2126  });
2127}
2128
2129var transitionProps = 'opacity,' +
vendor: 11,190 bytes, lines 2130-2576
2130  toDashedAll( vendorProperties.transform || 'transform' );
2131
2132Item.prototype.enableTransition = function(/* style */) {
2133  // HACK changing transitionProperty during a transition
2134  // will cause transition to jump
2135  if ( this.isTransitioning ) {
2136    return;
2137  }
2138
2139  // make `transition: foo, bar, baz` from style object
2140  // HACK un-comment this when enableTransition can work
2141  // while a transition is happening
2142  // var transitionValues = [];
2143  // for ( var prop in style ) {
2144  //   // dash-ify camelCased properties like WebkitTransition
2145  //   prop = vendorProperties[ prop ] || prop;
2146  //   transitionValues.push( toDashedAll( prop ) );
2147  // }
2148  // enable transition styles
2149  this.css({
2150    transitionProperty: transitionProps,
2151    transitionDuration: this.layout.options.transitionDuration
2152  });
2153  // listen for transition end event
2154  this.element.addEventListener( transitionEndEvent, this, false );
2155};
2156
2157Item.prototype.transition = Item.prototype[ transitionProperty ? '_transition' : '_nonTransition' ];
2158
2159// ----- events ----- //
2160
2161Item.prototype.onwebkitTransitionEnd = function( event ) {
2162  this.ontransitionend( event );
2163};
2164
2165Item.prototype.onotransitionend = function( event ) {
2166  this.ontransitionend( event );
2167};
2168
2169// properties that I munge to make my life easier
2170var dashedVendorProperties = {
2171  '-webkit-transform': 'transform',
2172  '-moz-transform': 'transform',
2173  '-o-transform': 'transform'
2174};
2175
2176Item.prototype.ontransitionend = function( event ) {
2177  // disregard bubbled events from children
2178  if ( event.target !== this.element ) {
2179    return;
2180  }
2181  var _transition = this._transn;
2182  // get property name of transitioned property, convert to prefix-free
2183  var propertyName = dashedVendorProperties[ event.propertyName ] || event.propertyName;
2184
2185  // remove property that has completed transitioning
2186  delete _transition.ingProperties[ propertyName ];
2187  // check if any properties are still transitioning
2188  if ( isEmptyObj( _transition.ingProperties ) ) {
2189    // all properties have completed transitioning
2190    this.disableTransition();
2191  }
2192  // clean style
2193  if ( propertyName in _transition.clean ) {
2194    // clean up style
2195    this.element.style[ event.propertyName ] = '';
2196    delete _transition.clean[ propertyName ];
2197  }
2198  // trigger onTransitionEnd callback
2199  if ( propertyName in _transition.onEnd ) {
2200    var onTransitionEnd = _transition.onEnd[ propertyName ];
2201    onTransitionEnd.call( this );
2202    delete _transition.onEnd[ propertyName ];
2203  }
2204
2205  this.emitEvent( 'transitionEnd', [ this ] );
2206};
2207
2208Item.prototype.disableTransition = function() {
2209  this.removeTransitionStyles();
2210  this.element.removeEventListener( transitionEndEvent, this, false );
2211  this.isTransitioning = false;
2212};
2213
2214/**
2215 * removes style property from element
2216 * @param {Object} style
2217**/
2218Item.prototype._removeStyles = function( style ) {
2219  // clean up transition styles
2220  var cleanStyle = {};
2221  for ( var prop in style ) {
2222    cleanStyle[ prop ] = '';
2223  }
2224  this.css( cleanStyle );
2225};
2226
2227var cleanTransitionStyle = {
2228  transitionProperty: '',
2229  transitionDuration: ''
2230};
2231
2232Item.prototype.removeTransitionStyles = function() {
2233  // remove transition
2234  this.css( cleanTransitionStyle );
2235};
2236
2237// ----- show/hide/remove ----- //
2238
2239// remove element from DOM
2240Item.prototype.removeElem = function() {
2241  this.element.parentNode.removeChild( this.element );
2242  // remove display: none
2243  this.css({ display: '' });
2244  this.emitEvent( 'remove', [ this ] );
2245};
2246
2247Item.prototype.remove = function() {
2248  // just remove element if no transition support or no transition
2249  if ( !transitionProperty || !parseFloat( this.layout.options.transitionDuration ) ) {
2250    this.removeElem();
2251    return;
2252  }
2253
2254  // start transition
2255  var _this = this;
2256  this.once( 'transitionEnd', function() {
2257    _this.removeElem();
2258  });
2259  this.hide();
2260};
2261
2262Item.prototype.reveal = function() {
2263  delete this.isHidden;
2264  // remove display: none
2265  this.css({ display: '' });
2266
2267  var options = this.layout.options;
2268
2269  var onTransitionEnd = {};
2270  var transitionEndProperty = this.getHideRevealTransitionEndProperty('visibleStyle');
2271  onTransitionEnd[ transitionEndProperty ] = this.onRevealTransitionEnd;
2272
2273  this.transition({
2274    from: options.hiddenStyle,
2275    to: options.visibleStyle,
2276    isCleaning: true,
2277    onTransitionEnd: onTransitionEnd
2278  });
2279};
2280
2281Item.prototype.onRevealTransitionEnd = function() {
2282  // check if still visible
2283  // during transition, item may have been hidden
2284  if ( !this.isHidden ) {
2285    this.emitEvent('reveal');
2286  }
2287};
2288
2289/**
2290 * get style property use for hide/reveal transition end
2291 * @param {String} styleProperty - hiddenStyle/visibleStyle
2292 * @returns {String}
2293 */
2294Item.prototype.getHideRevealTransitionEndProperty = function( styleProperty ) {
2295  var optionStyle = this.layout.options[ styleProperty ];
2296  // use opacity
2297  if ( optionStyle.opacity ) {
2298    return 'opacity';
2299  }
2300  // get first property
2301  for ( var prop in optionStyle ) {
2302    return prop;
2303  }
2304};
2305
2306Item.prototype.hide = function() {
2307  // set flag
2308  this.isHidden = true;
2309  // remove display: none
2310  this.css({ display: '' });
2311
2312  var options = this.layout.options;
2313
2314  var onTransitionEnd = {};
2315  var transitionEndProperty = this.getHideRevealTransitionEndProperty('hiddenStyle');
2316  onTransitionEnd[ transitionEndProperty ] = this.onHideTransitionEnd;
2317
2318  this.transition({
2319    from: options.visibleStyle,
2320    to: options.hiddenStyle,
2321    // keep hidden stuff hidden
2322    isCleaning: true,
2323    onTransitionEnd: onTransitionEnd
2324  });
2325};
2326
2327Item.prototype.onHideTransitionEnd = function() {
2328  // check if still hidden
2329  // during transition, item may have been un-hidden
2330  if ( this.isHidden ) {
2331    this.css({ display: 'none' });
2332    this.emitEvent('hide');
2333  }
2334};
2335
2336Item.prototype.destroy = function() {
2337  this.css({
2338    position: '',
2339    left: '',
2340    right: '',
2341    top: '',
2342    bottom: '',
2343    transition: '',
2344    transform: ''
2345  });
2346};
2347
2348return Item;
2349
2350}));
2351
2352/*!
2353 * Outlayer v1.4.2
2354 * the brains and guts of a layout library
2355 * MIT license
2356 */
2357
2358( function( window, factory ) {
2359  'use strict';
2360  // universal module definition
2361
2362  if ( typeof define == 'function' && define.amd ) {
2363    // AMD
2364    define( 'outlayer/outlayer',[
2365        'eventie/eventie',
2366        'eventEmitter/EventEmitter',
2367        'get-size/get-size',
2368        'fizzy-ui-utils/utils',
2369        './item'
2370      ],
2371      function( eventie, EventEmitter, getSize, utils, Item ) {
2372        return factory( window, eventie, EventEmitter, getSize, utils, Item);
2373      }
2374    );
2375  } else if ( typeof exports == 'object' ) {
2376    // CommonJS
2377    module.exports = factory(
2378      window,
2379      require('eventie'),
2380      require('wolfy87-eventemitter'),
2381      require('get-size'),
2382      require('fizzy-ui-utils'),
2383      require('./item')
2384    );
2385  } else {
2386    // browser global
2387    window.Outlayer = factory(
2388      window,
2389      window.eventie,
2390      window.EventEmitter,
2391      window.getSize,
2392      window.fizzyUIUtils,
2393      window.Outlayer.Item
2394    );
2395  }
2396
2397}( window, function factory( window, eventie, EventEmitter, getSize, utils, Item ) {
2398'use strict';
2399
2400// ----- vars ----- //
2401
2402var console = window.console;
2403var jQuery = window.jQuery;
2404var noop = function() {};
2405
2406// -------------------------- Outlayer -------------------------- //
2407
2408// globally unique identifiers
2409var GUID = 0;
2410// internal store of all Outlayer intances
2411var instances = {};
2412
2413
2414/**
2415 * @param {Element, String} element
2416 * @param {Object} options
2417 * @constructor
2418 */
2419function Outlayer( element, options ) {
2420  var queryElement = utils.getQueryElement( element );
2421  if ( !queryElement ) {
2422    if ( console ) {
2423      console.error( 'Bad element for ' + this.constructor.namespace +
2424        ': ' + ( queryElement || element ) );
2425    }
2426    return;
2427  }
2428  this.element = queryElement;
2429  // add jQuery
2430  if ( jQuery ) {
2431    this.$element = jQuery( this.element );
2432  }
2433
2434  // options
2435  this.options = utils.extend( {}, this.constructor.defaults );
2436  this.option( options );
2437
2438  // add id for Outlayer.getFromElement
2439  var id = ++GUID;
2440  this.element.outlayerGUID = id; // expando
2441  instances[ id ] = this; // associate via id
2442
2443  // kick it off
2444  this._create();
2445
2446  if ( this.options.isInitLayout ) {
2447    this.layout();
2448  }
2449}
2450
2451// settings are for internal use only
2452Outlayer.namespace = 'outlayer';
2453Outlayer.Item = Item;
2454
2455// default options
2456Outlayer.defaults = {
2457  containerStyle: {
2458    position: 'relative'
2459  },
2460  isInitLayout: true,
2461  isOriginLeft: true,
2462  isOriginTop: true,
2463  isResizeBound: true,
2464  isResizingContainer: true,
2465  // item options
2466  transitionDuration: '0.4s',
2467  hiddenStyle: {
2468    opacity: 0,
2469    transform: 'scale(0.001)'
2470  },
2471  visibleStyle: {
2472    opacity: 1,
2473    transform: 'scale(1)'
2474  }
2475};
2476
2477// inherit EventEmitter
2478utils.extend( Outlayer.prototype, EventEmitter.prototype );
2479
2480/**
2481 * set options
2482 * @param {Object} opts
2483 */
2484Outlayer.prototype.option = function( opts ) {
2485  utils.extend( this.options, opts );
2486};
2487
2488Outlayer.prototype._create = function() {
2489  // get items from children
2490  this.reloadItems();
2491  // elements that affect layout, but are not laid out
2492  this.stamps = [];
2493  this.stamp( this.options.stamp );
2494  // set container style
2495  utils.extend( this.element.style, this.options.containerStyle );
2496
2497  // bind resize method
2498  if ( this.options.isResizeBound ) {
2499    this.bindResize();
2500  }
2501};
2502
2503// goes through all children again and gets bricks in proper order
2504Outlayer.prototype.reloadItems = function() {
2505  // collection of item elements
2506  this.items = this._itemize( this.element.children );
2507};
2508
2509
2510/**
2511 * turn elements into Outlayer.Items to be used in layout
2512 * @param {Array or NodeList or HTMLElement} elems
2513 * @returns {Array} items - collection of new Outlayer Items
2514 */
2515Outlayer.prototype._itemize = function( elems ) {
2516
2517  var itemElems = this._filterFindItemElements( elems );
2518  var Item = this.constructor.Item;
2519
2520  // create new Outlayer Items for collection
2521  var items = [];
2522  for ( var i=0, len = itemElems.length; i < len; i++ ) {
2523    var elem = itemElems[i];
2524    var item = new Item( elem, this );
2525    items.push( item );
2526  }
2527
2528  return items;
2529};
2530
2531/**
2532 * get item elements to be used in layout
2533 * @param {Array or NodeList or HTMLElement} elems
2534 * @returns {Array} items - item elements
2535 */
2536Outlayer.prototype._filterFindItemElements = function( elems ) {
2537  return utils.filterFindElements( elems, this.options.itemSelector );
2538};
2539
2540/**
2541 * getter method for getting item elements
2542 * @returns {Array} elems - collection of item elements
2543 */
2544Outlayer.prototype.getItemElements = function() {
2545  var elems = [];
2546  for ( var i=0, len = this.items.length; i < len; i++ ) {
2547    elems.push( this.items[i].element );
2548  }
2549  return elems;
2550};
2551
2552// ----- init & layout ----- //
2553
2554/**
2555 * lays out all items
2556 */
2557Outlayer.prototype.layout = function() {
2558  this._resetLayout();
2559  this._manageStamps();
2560
2561  // don't animate first layout
2562  var isInstant = this.options.isLayoutInstant !== undefined ?
2563    this.options.isLayoutInstant : !this._isLayoutInited;
2564  this.layoutItems( this.items, isInstant );
2565
2566  // flag for initalized
2567  this._isLayoutInited = true;
2568};
2569
2570// _init is alias for layout
2571Outlayer.prototype._init = Outlayer.prototype.layout;
2572
2573/**
2574 * logic before any new layout
2575 */
2576Outlayer.prototype._resetLayout = function() {
2577  this.getSize();
2578};
2579
2580
2581Outlayer.prototype.getSize = function() {
2582  this.size = getSize( this.element );
2583};
2584
2585/**
2586 * get measurement from option, for columnWidth, rowHeight, gutter
2587 * if option is String -> get element from selector string, & get size of element
2588 * if option is Element -> get size of element
2589 * else use option as a number
2590 *
2591 * @param {String} measurement
2592 * @param {String} size - width or height
2593 * @private
2594 */
2595Outlayer.prototype._getMeasurement = function( measurement, size ) {
2596  var option = this.options[ measurement ];
2597  var elem;
2598  if ( !option ) {
2599    // default to 0
2600    this[ measurement ] = 0;
2601  } else {
2602    // use option as an element
2603    if ( typeof option === 'string' ) {
2604      elem = this.element.querySelector( option );
2605    } else if ( utils.isElement( option ) ) {
2606      elem = option;
2607    }
2608    // use size of element, if element
2609    this[ measurement ] = elem ? getSize( elem )[ size ] : option;
2610  }
2611};
2612
2613/**
2614 * layout a collection of item elements
2615 * @api public
2616 */
2617Outlayer.prototype.layoutItems = function( items, isInstant ) {
2618  items = this._getItemsForLayout( items );
2619
2620  this._layoutItems( items, isInstant );
2621
2622  this._postLayout();
2623};
2624
2625/**
2626 * get the items to be laid out
2627 * you may want to skip over some items
2628 * @param {Array} items
2629 * @returns {Array} items
2630 */
2631Outlayer.prototype._getItemsForLayout = function( items ) {
2632  var layoutItems = [];
2633  for ( var i=0, len = items.length; i < len; i++ ) {
2634    var item = items[i];
2635    if ( !item.isIgnored ) {
2636      layoutItems.push( item );
2637    }
2638  }
2639  return layoutItems;
2640};
2641
2642/**
2643 * layout items
2644 * @param {Array} items
2645 * @param {Boolean} isInstant
2646 */
2647Outlayer.prototype._layoutItems = function( items, isInstant ) {
2648  this._emitCompleteOnItems( 'layout', items );
2649
2650  if ( !items || !items.length ) {
2651    // no items, emit event with empty array
2652    return;
2653  }
2654
2655  var queue = [];
2656
2657  for ( var i=0, len = items.length; i < len; i++ ) {
2658    var item = items[i];
2659    // get x/y object from method
2660    var position = this._getItemLayoutPosition( item );
2661    // enqueue
2662    position.item = item;
2663    position.isInstant = isInstant || item.isLayoutInstant;
2664    queue.push( position );
2665  }
2666
2667  this._processLayoutQueue( queue );
2668};
2669
2670/**
2671 * get item layout position
2672 * @param {Outlayer.Item} item
2673 * @returns {Object} x and y position
2674 */
2675Outlayer.prototype._getItemLayoutPosition = function( /* item */ ) {
2676  return {
2677    x: 0,
2678    y: 0
2679  };
2680};
2681
2682/**
2683 * iterate over array and position each item
2684 * Reason being - separating this logic prevents 'layout invalidation'
2685 * thx @paul_irish
2686 * @param {Array} queue
2687 */
2688Outlayer.prototype._processLayoutQueue = function( queue ) {
2689  for ( var i=0, len = queue.length; i < len; i++ ) {
2690    var obj = queue[i];
2691    this._positionItem( obj.item, obj.x, obj.y, obj.isInstant );
2692  }
2693};
2694
2695/**
2696 * Sets position of item in DOM
2697 * @param {Outlayer.Item} item
2698 * @param {Number} x - horizontal position
2699 * @param {Number} y - vertical position
2700 * @param {Boolean} isInstant - disables transitions
2701 */
2702Outlayer.prototype._positionItem = function( item, x, y, isInstant ) {
2703  if ( isInstant ) {
2704    // if not transition, just set CSS
2705    item.goTo( x, y );
2706  } else {
2707    item.moveTo( x, y );
2708  }
2709};
2710
2711/**
2712 * Any logic you want to do after each layout,
2713 * i.e. size the container
2714 */
2715Outlayer.prototype._postLayout = function() {
2716  this.resizeContainer();
2717};
2718
2719Outlayer.prototype.resizeContainer = function() {
2720  if ( !this.options.isResizingContainer ) {
2721    return;
2722  }
2723  var size = this._getContainerSize();
2724  if ( size ) {
2725    this._setContainerMeasure( size.width, true );
2726    this._setContainerMeasure( size.height, false );
2727  }
2728};
2729
2730/**
2731 * Sets width or height of container if returned
2732 * @returns {Object} size
2733 *   @param {Number} width
2734 *   @param {Number} height
2735 */
2736Outlayer.prototype._getContainerSize = noop;
2737
2738/**
2739 * @param {Number} measure - size of width or height
2740 * @param {Boolean} isWidth
2741 */
2742Outlayer.prototype._setContainerMeasure = function( measure, isWidth ) {
2743  if ( measure === undefined ) {
2744    return;
2745  }
2746
2747  var elemSize = this.size;
2748  // add padding and border width if border box
2749  if ( elemSize.isBorderBox ) {
2750    measure += isWidth ? elemSize.paddingLeft + elemSize.paddingRight +
2751      elemSize.borderLeftWidth + elemSize.borderRightWidth :
2752      elemSize.paddingBottom + elemSize.paddingTop +
2753      elemSize.borderTopWidth + elemSize.borderBottomWidth;
2754  }
2755
2756  measure = Math.max( measure, 0 );
2757  this.element.style[ isWidth ? 'width' : 'height' ] = measure + 'px';
2758};
2759
2760/**
2761 * emit eventComplete on a collection of items events
2762 * @param {String} eventName
2763 * @param {Array} items - Outlayer.Items
2764 */
2765Outlayer.prototype._emitCompleteOnItems = function( eventName, items ) {
2766  var _this = this;
2767  function onComplete() {
2768    _this.dispatchEvent( eventName + 'Complete', null, [ items ] );
2769  }
2770
2771  var count = items.length;
2772  if ( !items || !count ) {
2773    onComplete();
2774    return;
2775  }
2776
2777  var doneCount = 0;
2778  function tick() {
2779    doneCount++;
2780    if ( doneCount === count ) {
2781      onComplete();
2782    }
2783  }
2784
2785  // bind callback
2786  for ( var i=0, len = items.length; i < len; i++ ) {
2787    var item = items[i];
2788    item.once( eventName, tick );
2789  }
2790};
2791
2792/**
2793 * emits events via eventEmitter and jQuery events
2794 * @param {String} type - name of event
2795 * @param {Event} event - original event
2796 * @param {Array} args - extra arguments
2797 */
2798Outlayer.prototype.dispatchEvent = function( type, event, args ) {
2799  // add original event to arguments
2800  var emitArgs = event ? [ event ].concat( args ) : args;
2801  this.emitEvent( type, emitArgs );
2802
2803  if ( jQuery ) {
2804    // set this.$element
2805    this.$element = this.$element || jQuery( this.element );
2806    if ( event ) {
2807      // create jQuery event
2808      var $event = jQuery.Event( event );
2809      $event.type = type;
2810      this.$element.trigger( $event, args );
2811    } else {
2812      // just trigger with type if no event available
2813      this.$element.trigger( type, args );
2814    }
2815  }
2816};
2817
2818// -------------------------- ignore & stamps -------------------------- //
2819
2820
2821/**
2822 * keep item in collection, but do not lay it out
2823 * ignored items do not get skipped in layout
2824 * @param {Element} elem
2825 */
2826Outlayer.prototype.ignore = function( elem ) {
2827  var item = this.getItem( elem );
2828  if ( item ) {
2829    item.isIgnored = true;
2830  }
2831};
2832
2833/**
2834 * return item to layout collection
2835 * @param {Element} elem
2836 */
2837Outlayer.prototype.unignore = function( elem ) {
2838  var item = this.getItem( elem );
2839  if ( item ) {
2840    delete item.isIgnored;
2841  }
2842};
2843
2844/**
2845 * adds elements to stamps
2846 * @param {NodeList, Array, Element, or String} elems
2847 */
2848Outlayer.prototype.stamp = function( elems ) {
2849  elems = this._find( elems );
2850  if ( !elems ) {
2851    return;
2852  }
2853
2854  this.stamps = this.stamps.concat( elems );
2855  // ignore
2856  for ( var i=0, len = elems.length; i < len; i++ ) {
2857    var elem = elems[i];
2858    this.ignore( elem );
2859  }
2860};
2861
2862/**
2863 * removes elements to stamps
2864 * @param {NodeList, Array, or Element} elems
2865 */
2866Outlayer.prototype.unstamp = function( elems ) {
2867  elems = this._find( elems );
2868  if ( !elems ){
2869    return;
2870  }
2871
2872  for ( var i=0, len = elems.length; i < len; i++ ) {
2873    var elem = elems[i];
2874    // filter out removed stamp elements
2875    utils.removeFrom( this.stamps, elem );
2876    this.unignore( elem );
2877  }
2878
2879};
2880
2881/**
2882 * finds child elements
2883 * @param {NodeList, Array, Element, or String} elems
2884 * @returns {Array} elems
2885 */
2886Outlayer.prototype._find = function( elems ) {
2887  if ( !elems ) {
2888    return;
2889  }
2890  // if string, use argument as selector string
2891  if ( typeof elems === 'string' ) {
2892    elems = this.element.querySelectorAll( elems );
2893  }
2894  elems = utils.makeArray( elems );
2895  return elems;
2896};
2897
2898Outlayer.prototype._manageStamps = function() {
2899  if ( !this.stamps || !this.stamps.length ) {
2900    return;
2901  }
2902
2903  this._getBoundingRect();
2904
2905  for ( var i=0, len = this.stamps.length; i < len; i++ ) {
2906    var stamp = this.stamps[i];
2907    this._manageStamp( stamp );
2908  }
2909};
2910
2911// update boundingLeft / Top
2912Outlayer.prototype._getBoundingRect = function() {
2913  // get bounding rect for container element
2914  var boundingRect = this.element.getBoundingClientRect();
2915  var size = this.size;
2916  this._boundingRect = {
2917    left: boundingRect.left + size.paddingLeft + size.borderLeftWidth,
2918    top: boundingRect.top + size.paddingTop + size.borderTopWidth,
2919    right: boundingRect.right - ( size.paddingRight + size.borderRightWidth ),
2920    bottom: boundingRect.bottom - ( size.paddingBottom + size.borderBottomWidth )
2921  };
2922};
2923
2924/**
2925 * @param {Element} stamp
2926**/
2927Outlayer.prototype._manageStamp = noop;
2928
2929/**
2930 * get x/y position of element relative to container element
2931 * @param {Element} elem
2932 * @returns {Object} offset - has left, top, right, bottom
2933 */
2934Outlayer.prototype._getElementOffset = function( elem ) {
2935  var boundingRect = elem.getBoundingClientRect();
2936  var thisRect = this._boundingRect;
2937  var size = getSize( elem );
2938  var offset = {
2939    left: boundingRect.left - thisRect.left - size.marginLeft,
2940    top: boundingRect.top - thisRect.top - size.marginTop,
2941    right: thisRect.right - boundingRect.right - size.marginRight,
2942    bottom: thisRect.bottom - boundingRect.bottom - size.marginBottom
2943  };
2944  return offset;
2945};
2946
2947// -------------------------- resize -------------------------- //
2948
2949// enable event handlers for listeners
2950// i.e. resize -> onresize
2951Outlayer.prototype.handleEvent = function( event ) {
2952  var method = 'on' + event.type;
2953  if ( this[ method ] ) {
2954    this[ method ]( event );
2955  }
2956};
2957
2958/**
2959 * Bind layout to window resizing
2960 */
2961Outlayer.prototype.bindResize = function() {
2962  // bind just one listener
2963  if ( this.isResizeBound ) {
2964    return;
2965  }
2966  eventie.bind( window, 'resize', this );
2967  this.isResizeBound = true;
2968};
2969
2970/**
2971 * Unbind layout to window resizing
2972 */
2973Outlayer.prototype.unbindResize = function() {
2974  if ( this.isResizeBound ) {
2975    eventie.unbind( window, 'resize', this );
2976  }
2977  this.isResizeBound = false;
2978};
2979
2980// original debounce by John Hann
2981// http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/
2982
2983// this fires every resize
2984Outlayer.prototype.onresize = function() {
2985  if ( this.resizeTimeout ) {
2986    clearTimeout( this.resizeTimeout );
2987  }
2988
2989  var _this = this;
2990  function delayed() {
2991    _this.resize();
2992    delete _this.resizeTimeout;
2993  }
2994
2995  this.resizeTimeout = setTimeout( delayed, 100 );
2996};
2997
2998// debounced, layout on resize
2999Outlayer.prototype.resize = function() {
3000  // don't trigger if size did not change
3001  // or if resize was unbound. See #9
3002  if ( !this.isResizeBound || !this.needsResizeLayout() ) {
3003    return;
3004  }
3005
3006  this.layout();
3007};
3008
3009/**
3010 * check if layout is needed post layout
3011 * @returns Boolean
3012 */
3013Outlayer.prototype.needsResizeLayout = function() {
3014  var size = getSize( this.element );
3015  // check that this.size and size are there
3016  // IE8 triggers resize on body size change, so they might not be
3017  var hasSizes = this.size && size;
3018  return hasSizes && size.innerWidth !== this.size.innerWidth;
3019};
3020
3021// -------------------------- methods -------------------------- //
3022
3023/**
3024 * add items to Outlayer instance
3025 * @param {Array or NodeList or Element} elems
3026 * @returns {Array} items - Outlayer.Items
3027**/
3028Outlayer.prototype.addItems = function( elems ) {
3029  var items = this._itemize( elems );
3030  // add items to collection
3031  if ( items.length ) {
3032    this.items = this.items.concat( items );
3033  }
3034  return items;
3035};
3036
3037/**
3038 * Layout newly-appended item elements
3039 * @param {Array or NodeList or Element} elems
3040 */
3041Outlayer.prototype.appended = function( elems ) {
3042  var items = this.addItems( elems );
3043  if ( !items.length ) {
3044    return;
3045  }
3046  // layout and reveal just the new items
3047  this.layoutItems( items, true );
3048  this.reveal( items );
3049};
3050
3051/**
3052 * Layout prepended elements
3053 * @param {Array or NodeList or Element} elems
3054 */
3055Outlayer.prototype.prepended = function( elems ) {
3056  var items = this._itemize( elems );
3057  if ( !items.length ) {
3058    return;
3059  }
3060  // add items to beginning of collection
3061  var previousItems = this.items.slice(0);
3062  this.items = items.concat( previousItems );
3063  // start new layout
3064  this._resetLayout();
3065  this._manageStamps();
3066  // layout new stuff without transition
3067  this.layoutItems( items, true );
3068  this.reveal( items );
3069  // layout previous items
3070  this.layoutItems( previousItems );
3071};
3072
3073/**
3074 * reveal a collection of items
3075 * @param {Array of Outlayer.Items} items
3076 */
3077Outlayer.prototype.reveal = function( items ) {
3078  this._emitCompleteOnItems( 'reveal', items );
3079
3080  var len = items && items.length;
3081  for ( var i=0; len && i < len; i++ ) {
3082    var item = items[i];
3083    item.reveal();
3084  }
3085};
3086
3087/**
3088 * hide a collection of items
3089 * @param {Array of Outlayer.Items} items
3090 */
3091Outlayer.prototype.hide = function( items ) {
3092  this._emitCompleteOnItems( 'hide', items );
3093
3094  var len = items && items.length;
3095  for ( var i=0; len && i < len; i++ ) {
3096    var item = items[i];
3097    item.hide();
3098  }
3099};
3100
3101/**
3102 * reveal item elements
3103 * @param {Array}, {Element}, {NodeList} items
3104 */
3105Outlayer.prototype.revealItemElements = function( elems ) {
3106  var items = this.getItems( elems );
3107  this.reveal( items );
3108};
3109
3110/**
3111 * hide item elements
3112 * @param {Array}, {Element}, {NodeList} items
3113 */
3114Outlayer.prototype.hideItemElements = function( elems ) {
3115  var items = this.getItems( elems );
3116  this.hide( items );
3117};
3118
3119/**
3120 * get Outlayer.Item, given an Element
3121 * @param {Element} elem
3122 * @param {Function} callback
3123 * @returns {Outlayer.Item} item
3124 */
3125Outlayer.prototype.getItem = function( elem ) {
3126  // loop through items to get the one that matches
3127  for ( var i=0, len = this.items.length; i < len; i++ ) {
3128    var item = this.items[i];
3129    if ( item.element === elem ) {
3130      // return item
3131      return item;
3132    }
3133  }
3134};
3135
3136/**
3137 * get collection of Outlayer.Items, given Elements
3138 * @param {Array} elems
3139 * @returns {Array} items - Outlayer.Items
3140 */
3141Outlayer.prototype.getItems = function( elems ) {
3142  elems = utils.makeArray( elems );
3143  var items = [];
3144  for ( var i=0, len = elems.length; i < len; i++ ) {
3145    var elem = elems[i];
3146    var item = this.getItem( elem );
3147    if ( item ) {
3148      items.push( item );
3149    }
3150  }
3151
3152  return items;
3153};
3154
3155/**
3156 * remove element(s) from instance and DOM
3157 * @param {Array or NodeList or Element} elems
3158 */
3159Outlayer.prototype.remove = function( elems ) {
3160  var removeItems = this.getItems( elems );
3161
3162  this._emitCompleteOnItems( 'remove', removeItems );
3163
3164  // bail if no items to remove
3165  if ( !removeItems || !removeItems.length ) {
3166    return;
3167  }
3168
3169  for ( var i=0, len = removeItems.length; i < len; i++ ) {
3170    var item = removeItems[i];
3171    item.remove();
3172    // remove item from collection
3173    utils.removeFrom( this.items, item );
3174  }
3175};
3176
3177// ----- destroy ----- //
3178
3179// remove and disable Outlayer instance
3180Outlayer.prototype.destroy = function() {
3181  // clean up dynamic styles
3182  var style = this.element.style;
3183  style.height = '';
3184  style.position = '';
3185  style.width = '';
3186  // destroy items
3187  for ( var i=0, len = this.items.length; i < len; i++ ) {
3188    var item = this.items[i];
3189    item.destroy();
3190  }
3191
3192  this.unbindResize();
3193
3194  var id = this.element.outlayerGUID;
3195  delete instances[ id ]; // remove reference to instance by id
3196  delete this.element.outlayerGUID;
3197  // remove data for jQuery
3198  if ( jQuery ) {
3199    jQuery.removeData( this.element, this.constructor.namespace );
3200  }
3201
3202};
3203
3204// -------------------------- data -------------------------- //
3205
3206/**
3207 * get Outlayer instance from element
3208 * @param {Element} elem
3209 * @returns {Outlayer}
3210 */
3211Outlayer.data = function( elem ) {
3212  elem = utils.getQueryElement( elem );
3213  var id = elem && elem.outlayerGUID;
3214  return id && instances[ id ];
3215};
3216
3217
3218// -------------------------- create Outlayer class -------------------------- //
3219
3220/**
3221 * create a layout class
3222 * @param {String} namespace
3223 */
3224Outlayer.create = function( namespace, options ) {
3225  // sub-class Outlayer
3226  function Layout() {
3227    Outlayer.apply( this, arguments );
3228  }
3229  // inherit Outlayer prototype, use Object.create if there
3230  if ( Object.create ) {
3231    Layout.prototype = Object.create( Outlayer.prototype );
3232  } else {
3233    utils.extend( Layout.prototype, Outlayer.prototype );
3234  }
3235  // set contructor, used for namespace and Item
3236  Layout.prototype.constructor = Layout;
3237
3238  Layout.defaults = utils.extend( {}, Outlayer.defaults );
3239  // apply new options
3240  utils.extend( Layout.defaults, options );
3241  // keep prototype.settings for backwards compatibility (Packery v1.2.0)
3242  Layout.prototype.settings = {};
3243
3244  Layout.namespace = namespace;
3245
3246  Layout.data = Outlayer.data;
3247
3248  // sub-class Item
3249  Layout.Item = function LayoutItem() {
3250    Item.apply( this, arguments );
3251  };
3252
3253  Layout.Item.prototype = new Item();
3254
3255  // -------------------------- declarative -------------------------- //
3256
3257  utils.htmlInit( Layout, namespace );
3258
3259  // -------------------------- jQuery bridge -------------------------- //
3260
3261  // make into jQuery plugin
3262  if ( jQuery && jQuery.bridget ) {
3263    jQuery.bridget( namespace, Layout );
3264  }
3265
3266  return Layout;
3267};
3268
3269// ----- fin ----- //
3270
3271// back in global
3272Outlayer.Item = Item;
3273
3274return Outlayer;
3275
3276}));
3277
3278
3279/**
3280 * Isotope Item
3281**/
3282
3283( function( window, factory ) {
3284'use strict';
3285  // universal module definition
3286  if ( typeof define == 'function' && define.amd ) {
3287    // AMD
3288    define( 'isotope/js/item',[
3289        'outlayer/outlayer'
3290      ],
3291      factory );
3292  } else if ( typeof exports == 'object' ) {
3293    // CommonJS
3294    module.exports = factory(
3295      require('outlayer')
3296    );
3297  } else {
3298    // browser global
3299    window.Isotope = window.Isotope || {};
3300    window.Isotope.Item = factory(
3301      window.Outlayer
3302    );
3303  }
3304
3305}( window, function factory( Outlayer ) {
3306'use strict';
3307
3308// -------------------------- Item -------------------------- //
3309
3310// sub-class Outlayer Item
3311function Item() {
3312  Outlayer.Item.apply( this, arguments );
3313}
3314
3315Item.prototype = new Outlayer.Item();
3316
3317Item.prototype._create = function() {
3318  // assign id, used for original-order sorting
3319  this.id = this.layout.itemGUID++;
3320  Outlayer.Item.prototype._create.call( this );
3321  this.sortData = {};
3322};
3323
3324Item.prototype.updateSortData = function() {
3325  if ( this.isIgnored ) {
3326    return;
3327  }
3328  // default sorters
3329  this.sortData.id = this.id;
3330  // for backward compatibility
3331  this.sortData['original-order'] = this.id;
3332  this.sortData.random = Math.random();
3333  // go thru getSortData obj and apply the sorters
3334  var getSortData = this.layout.options.getSortData;
3335  var sorters = this.layout._sorters;
3336  for ( var key in getSortData ) {
3337    var sorter = sorters[ key ];
3338    this.sortData[ key ] = sorter( this.element, this );
3339  }
3340};
3341
3342var _destroy = Item.prototype.destroy;
3343Item.prototype.destroy = function() {
3344  // call super
3345  _destroy.apply( this, arguments );
3346  // reset display, #741
3347  this.css({
3348    display: ''
3349  });
3350};
3351
3352return Item;
3353
3354}));
3355
3356/**
3357 * Isotope LayoutMode
3358 */
3359
3360( function( window, factory ) {
3361  'use strict';
3362  // universal module definition
3363
3364  if ( typeof define == 'function' && define.amd ) {
3365    // AMD
3366    define( 'isotope/js/layout-mode',[
3367        'get-size/get-size',
3368        'outlayer/outlayer'
3369      ],
3370      factory );
3371  } else if ( typeof exports == 'object' ) {
3372    // CommonJS
3373    module.exports = factory(
3374      require('get-size'),
3375      require('outlayer')
3376    );
3377  } else {
3378    // browser global
3379    window.Isotope = window.Isotope || {};
3380    window.Isotope.LayoutMode = factory(
3381      window.getSize,
3382      window.Outlayer
3383    );
3384  }
3385
3386}( window, function factory( getSize, Outlayer ) {
3387  'use strict';
3388
3389  // layout mode class
3390  function LayoutMode( isotope ) {
3391    this.isotope = isotope;
3392    // link properties
3393    if ( isotope ) {
3394      this.options = isotope.options[ this.namespace ];
3395      this.element = isotope.element;
3396      this.items = isotope.filteredItems;
3397      this.size = isotope.size;
3398    }
3399  }
3400
3401  /**
3402   * some methods should just defer to default Outlayer method
3403   * and reference the Isotope instance as `this`
3404  **/
3405  ( function() {
3406    var facadeMethods = [
3407      '_resetLayout',
3408      '_getItemLayoutPosition',
3409      '_manageStamp',
3410      '_getContainerSize',
3411      '_getElementOffset',
3412      'needsResizeLayout'
3413    ];
3414
3415    for ( var i=0, len = facadeMethods.length; i < len; i++ ) {
3416      var methodName = facadeMethods[i];
3417      LayoutMode.prototype[ methodName ] = getOutlayerMethod( methodName );
3418    }
3419
3420    function getOutlayerMethod( methodName ) {
3421      return function() {
3422        return Outlayer.prototype[ methodName ].apply( this.isotope, arguments );
3423      };
3424    }
3425  })();
3426
3427  // -----  ----- //
3428
3429  // for horizontal layout modes, check vertical size
3430  LayoutMode.prototype.needsVerticalResizeLayout = function() {
vendor: 20,029 bytes, lines 3431-4166
3431    // don't trigger if size did not change
3432    var size = getSize( this.isotope.element );
3433    // check that this.size and size are there
3434    // IE8 triggers resize on body size change, so they might not be
3435    var hasSizes = this.isotope.size && size;
3436    return hasSizes && size.innerHeight != this.isotope.size.innerHeight;
3437  };
3438
3439  // ----- measurements ----- //
3440
3441  LayoutMode.prototype._getMeasurement = function() {
3442    this.isotope._getMeasurement.apply( this, arguments );
3443  };
3444
3445  LayoutMode.prototype.getColumnWidth = function() {
3446    this.getSegmentSize( 'column', 'Width' );
3447  };
3448
3449  LayoutMode.prototype.getRowHeight = function() {
3450    this.getSegmentSize( 'row', 'Height' );
3451  };
3452
3453  /**
3454   * get columnWidth or rowHeight
3455   * segment: 'column' or 'row'
3456   * size 'Width' or 'Height'
3457  **/
3458  LayoutMode.prototype.getSegmentSize = function( segment, size ) {
3459    var segmentName = segment + size;
3460    var outerSize = 'outer' + size;
3461    // columnWidth / outerWidth // rowHeight / outerHeight
3462    this._getMeasurement( segmentName, outerSize );
3463    // got rowHeight or columnWidth, we can chill
3464    if ( this[ segmentName ] ) {
3465      return;
3466    }
3467    // fall back to item of first element
3468    var firstItemSize = this.getFirstItemSize();
3469    this[ segmentName ] = firstItemSize && firstItemSize[ outerSize ] ||
3470      // or size of container
3471      this.isotope.size[ 'inner' + size ];
3472  };
3473
3474  LayoutMode.prototype.getFirstItemSize = function() {
3475    var firstItem = this.isotope.filteredItems[0];
3476    return firstItem && firstItem.element && getSize( firstItem.element );
3477  };
3478
3479  // ----- methods that should reference isotope ----- //
3480
3481  LayoutMode.prototype.layout = function() {
3482    this.isotope.layout.apply( this.isotope, arguments );
3483  };
3484
3485  LayoutMode.prototype.getSize = function() {
3486    this.isotope.getSize();
3487    this.size = this.isotope.size;
3488  };
3489
3490  // -------------------------- create -------------------------- //
3491
3492  LayoutMode.modes = {};
3493
3494  LayoutMode.create = function( namespace, options ) {
3495
3496    function Mode() {
3497      LayoutMode.apply( this, arguments );
3498    }
3499
3500    Mode.prototype = new LayoutMode();
3501
3502    // default options
3503    if ( options ) {
3504      Mode.options = options;
3505    }
3506
3507    Mode.prototype.namespace = namespace;
3508    // register in Isotope
3509    LayoutMode.modes[ namespace ] = Mode;
3510
3511    return Mode;
3512  };
3513
3514  return LayoutMode;
3515
3516}));
3517
3518/*!
3519 * Masonry v3.3.1
3520 * Cascading grid layout library
3521 * http://masonry.desandro.com
3522 * MIT License
3523 * by David DeSandro
3524 */
3525
3526( function( window, factory ) {
3527  'use strict';
3528  // universal module definition
3529  if ( typeof define === 'function' && define.amd ) {
3530    // AMD
3531    define( 'masonry/masonry',[
3532        'outlayer/outlayer',
3533        'get-size/get-size',
3534        'fizzy-ui-utils/utils'
3535      ],
3536      factory );
3537  } else if ( typeof exports === 'object' ) {
3538    // CommonJS
3539    module.exports = factory(
3540      require('outlayer'),
3541      require('get-size'),
3542      require('fizzy-ui-utils')
3543    );
3544  } else {
3545    // browser global
3546    window.Masonry = factory(
3547      window.Outlayer,
3548      window.getSize,
3549      window.fizzyUIUtils
3550    );
3551  }
3552
3553}( window, function factory( Outlayer, getSize, utils ) {
3554
3555
3556
3557// -------------------------- masonryDefinition -------------------------- //
3558
3559  // create an Outlayer layout class
3560  var Masonry = Outlayer.create('masonry');
3561
3562  Masonry.prototype._resetLayout = function() {
3563    this.getSize();
3564    this._getMeasurement( 'columnWidth', 'outerWidth' );
3565    this._getMeasurement( 'gutter', 'outerWidth' );
3566    this.measureColumns();
3567
3568    // reset column Y
3569    var i = this.cols;
3570    this.colYs = [];
3571    while (i--) {
3572      this.colYs.push( 0 );
3573    }
3574
3575    this.maxY = 0;
3576  };
3577
3578  Masonry.prototype.measureColumns = function() {
3579    this.getContainerWidth();
3580    // if columnWidth is 0, default to outerWidth of first item
3581    if ( !this.columnWidth ) {
3582      var firstItem = this.items[0];
3583      var firstItemElem = firstItem && firstItem.element;
3584      // columnWidth fall back to item of first element
3585      this.columnWidth = firstItemElem && getSize( firstItemElem ).outerWidth ||
3586        // if first elem has no width, default to size of container
3587        this.containerWidth;
3588    }
3589
3590    var columnWidth = this.columnWidth += this.gutter;
3591
3592    // calculate columns
3593    var containerWidth = this.containerWidth + this.gutter;
3594    var cols = containerWidth / columnWidth;
3595    // fix rounding errors, typically with gutters
3596    var excess = columnWidth - containerWidth % columnWidth;
3597    // if overshoot is less than a pixel, round up, otherwise floor it
3598    var mathMethod = excess && excess < 1 ? 'round' : 'floor';
3599    cols = Math[ mathMethod ]( cols );
3600    this.cols = Math.max( cols, 1 );
3601  };
3602
3603  Masonry.prototype.getContainerWidth = function() {
3604    // container is parent if fit width
3605    var container = this.options.isFitWidth ? this.element.parentNode : this.element;
3606    // check that this.size and size are there
3607    // IE8 triggers resize on body size change, so they might not be
3608    var size = getSize( container );
3609    this.containerWidth = size && size.innerWidth;
3610  };
3611
3612  Masonry.prototype._getItemLayoutPosition = function( item ) {
3613    item.getSize();
3614    // how many columns does this brick span
3615    var remainder = item.size.outerWidth % this.columnWidth;
3616    var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
3617    // round if off by 1 pixel, otherwise use ceil
3618    var colSpan = Math[ mathMethod ]( item.size.outerWidth / this.columnWidth );
3619    colSpan = Math.min( colSpan, this.cols );
3620
3621    var colGroup = this._getColGroup( colSpan );
3622    // get the minimum Y value from the columns
3623    var minimumY = Math.min.apply( Math, colGroup );
3624    var shortColIndex = utils.indexOf( colGroup, minimumY );
3625
3626    // position the brick
3627    var position = {
3628      x: this.columnWidth * shortColIndex,
3629      y: minimumY
3630    };
3631
3632    // apply setHeight to necessary columns
3633    var setHeight = minimumY + item.size.outerHeight;
3634    var setSpan = this.cols + 1 - colGroup.length;
3635    for ( var i = 0; i < setSpan; i++ ) {
3636      this.colYs[ shortColIndex + i ] = setHeight;
3637    }
3638
3639    return position;
3640  };
3641
3642  /**
3643   * @param {Number} colSpan - number of columns the element spans
3644   * @returns {Array} colGroup
3645   */
3646  Masonry.prototype._getColGroup = function( colSpan ) {
3647    if ( colSpan < 2 ) {
3648      // if brick spans only one column, use all the column Ys
3649      return this.colYs;
3650    }
3651
3652    var colGroup = [];
3653    // how many different places could this brick fit horizontally
3654    var groupCount = this.cols + 1 - colSpan;
3655    // for each group potential horizontal position
3656    for ( var i = 0; i < groupCount; i++ ) {
3657      // make an array of colY values for that one group
3658      var groupColYs = this.colYs.slice( i, i + colSpan );
3659      // and get the max value of the array
3660      colGroup[i] = Math.max.apply( Math, groupColYs );
3661    }
3662    return colGroup;
3663  };
3664
3665  Masonry.prototype._manageStamp = function( stamp ) {
3666    var stampSize = getSize( stamp );
3667    var offset = this._getElementOffset( stamp );
3668    // get the columns that this stamp affects
3669    var firstX = this.options.isOriginLeft ? offset.left : offset.right;
3670    var lastX = firstX + stampSize.outerWidth;
3671    var firstCol = Math.floor( firstX / this.columnWidth );
3672    firstCol = Math.max( 0, firstCol );
3673    var lastCol = Math.floor( lastX / this.columnWidth );
3674    // lastCol should not go over if multiple of columnWidth #425
3675    lastCol -= lastX % this.columnWidth ? 0 : 1;
3676    lastCol = Math.min( this.cols - 1, lastCol );
3677    // set colYs to bottom of the stamp
3678    var stampMaxY = ( this.options.isOriginTop ? offset.top : offset.bottom ) +
3679      stampSize.outerHeight;
3680    for ( var i = firstCol; i <= lastCol; i++ ) {
3681      this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
3682    }
3683  };
3684
3685  Masonry.prototype._getContainerSize = function() {
3686    this.maxY = Math.max.apply( Math, this.colYs );
3687    var size = {
3688      height: this.maxY
3689    };
3690
3691    if ( this.options.isFitWidth ) {
3692      size.width = this._getContainerFitWidth();
3693    }
3694
3695    return size;
3696  };
3697
3698  Masonry.prototype._getContainerFitWidth = function() {
3699    var unusedCols = 0;
3700    // count unused columns
3701    var i = this.cols;
3702    while ( --i ) {
3703      if ( this.colYs[i] !== 0 ) {
3704        break;
3705      }
3706      unusedCols++;
3707    }
3708    // fit container to columns that have been used
3709    return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
3710  };
3711
3712  Masonry.prototype.needsResizeLayout = function() {
3713    var previousWidth = this.containerWidth;
3714    this.getContainerWidth();
3715    return previousWidth !== this.containerWidth;
3716  };
3717
3718  return Masonry;
3719
3720}));
3721
3722/*!
3723 * Masonry layout mode
3724 * sub-classes Masonry
3725 * http://masonry.desandro.com
3726 */
3727
3728( function( window, factory ) {
3729  'use strict';
3730  // universal module definition
3731  if ( typeof define == 'function' && define.amd ) {
3732    // AMD
3733    define( 'isotope/js/layout-modes/masonry',[
3734        '../layout-mode',
3735        'masonry/masonry'
3736      ],
3737      factory );
3738  } else if ( typeof exports == 'object' ) {
3739    // CommonJS
3740    module.exports = factory(
3741      require('../layout-mode'),
3742      require('masonry-layout')
3743    );
3744  } else {
3745    // browser global
3746    factory(
3747      window.Isotope.LayoutMode,
3748      window.Masonry
3749    );
3750  }
3751
3752}( window, function factory( LayoutMode, Masonry ) {
3753'use strict';
3754
3755// -------------------------- helpers -------------------------- //
3756
3757// extend objects
3758function extend( a, b ) {
3759  for ( var prop in b ) {
3760    a[ prop ] = b[ prop ];
3761  }
3762  return a;
3763}
3764
3765// -------------------------- masonryDefinition -------------------------- //
3766
3767  // create an Outlayer layout class
3768  var MasonryMode = LayoutMode.create('masonry');
3769
3770  // save on to these methods
3771  var _getElementOffset = MasonryMode.prototype._getElementOffset;
3772  var layout = MasonryMode.prototype.layout;
3773  var _getMeasurement = MasonryMode.prototype._getMeasurement;
3774
3775  // sub-class Masonry
3776  extend( MasonryMode.prototype, Masonry.prototype );
3777
3778  // set back, as it was overwritten by Masonry
3779  MasonryMode.prototype._getElementOffset = _getElementOffset;
3780  MasonryMode.prototype.layout = layout;
3781  MasonryMode.prototype._getMeasurement = _getMeasurement;
3782
3783  var measureColumns = MasonryMode.prototype.measureColumns;
3784  MasonryMode.prototype.measureColumns = function() {
3785    // set items, used if measuring first item
3786    this.items = this.isotope.filteredItems;
3787    measureColumns.call( this );
3788  };
3789
3790  // HACK copy over isOriginLeft/Top options
3791  var _manageStamp = MasonryMode.prototype._manageStamp;
3792  MasonryMode.prototype._manageStamp = function() {
3793    this.options.isOriginLeft = this.isotope.options.isOriginLeft;
3794    this.options.isOriginTop = this.isotope.options.isOriginTop;
3795    _manageStamp.apply( this, arguments );
3796  };
3797
3798  return MasonryMode;
3799
3800}));
3801
3802/**
3803 * fitRows layout mode
3804 */
3805
3806( function( window, factory ) {
3807  'use strict';
3808  // universal module definition
3809  if ( typeof define == 'function' && define.amd ) {
3810    // AMD
3811    define( 'isotope/js/layout-modes/fit-rows',[
3812        '../layout-mode'
3813      ],
3814      factory );
3815  } else if ( typeof exports == 'object' ) {
3816    // CommonJS
3817    module.exports = factory(
3818      require('../layout-mode')
3819    );
3820  } else {
3821    // browser global
3822    factory(
3823      window.Isotope.LayoutMode
3824    );
3825  }
3826
3827}( window, function factory( LayoutMode ) {
3828'use strict';
3829
3830var FitRows = LayoutMode.create('fitRows');
3831
3832FitRows.prototype._resetLayout = function() {
3833  this.x = 0;
3834  this.y = 0;
3835  this.maxY = 0;
3836  this._getMeasurement( 'gutter', 'outerWidth' );
3837};
3838
3839FitRows.prototype._getItemLayoutPosition = function( item ) {
3840  item.getSize();
3841
3842  var itemWidth = item.size.outerWidth + this.gutter;
3843  // if this element cannot fit in the current row
3844  var containerWidth = this.isotope.size.innerWidth + this.gutter;
3845  if ( this.x !== 0 && itemWidth + this.x > containerWidth ) {
3846    this.x = 0;
3847    this.y = this.maxY;
3848  }
3849
3850  var position = {
3851    x: this.x,
3852    y: this.y
3853  };
3854
3855  this.maxY = Math.max( this.maxY, this.y + item.size.outerHeight );
3856  this.x += itemWidth;
3857
3858  return position;
3859};
3860
3861FitRows.prototype._getContainerSize = function() {
3862  return { height: this.maxY };
3863};
3864
3865return FitRows;
3866
3867}));
3868
3869/**
3870 * vertical layout mode
3871 */
3872
3873( function( window, factory ) {
3874  'use strict';
3875  // universal module definition
3876  if ( typeof define == 'function' && define.amd ) {
3877    // AMD
3878    define( 'isotope/js/layout-modes/vertical',[
3879        '../layout-mode'
3880      ],
3881      factory );
3882  } else if ( typeof exports == 'object' ) {
3883    // CommonJS
3884    module.exports = factory(
3885      require('../layout-mode')
3886    );
3887  } else {
3888    // browser global
3889    factory(
3890      window.Isotope.LayoutMode
3891    );
3892  }
3893
3894}( window, function factory( LayoutMode ) {
3895'use strict';
3896
3897var Vertical = LayoutMode.create( 'vertical', {
3898  horizontalAlignment: 0
3899});
3900
3901Vertical.prototype._resetLayout = function() {
3902  this.y = 0;
3903};
3904
3905Vertical.prototype._getItemLayoutPosition = function( item ) {
3906  item.getSize();
3907  var x = ( this.isotope.size.innerWidth - item.size.outerWidth ) *
3908    this.options.horizontalAlignment;
3909  var y = this.y;
3910  this.y += item.size.outerHeight;
3911  return { x: x, y: y };
3912};
3913
3914Vertical.prototype._getContainerSize = function() {
3915  return { height: this.y };
3916};
3917
3918return Vertical;
3919
3920}));
3921
3922/*!
3923 * Isotope v2.2.2
3924 *
3925 * Licensed GPLv3 for open source use
3926 * or Isotope Commercial License for commercial use
3927 *
3928 * http://isotope.metafizzy.co
3929 * Copyright 2015 Metafizzy
3930 */
3931
3932( function( window, factory ) {
3933  'use strict';
3934  // universal module definition
3935
3936  if ( typeof define == 'function' && define.amd ) {
3937    // AMD
3938    define( [
3939        'outlayer/outlayer',
3940        'get-size/get-size',
3941        'matches-selector/matches-selector',
3942        'fizzy-ui-utils/utils',
3943        'isotope/js/item',
3944        'isotope/js/layout-mode',
3945        // include default layout modes
3946        'isotope/js/layout-modes/masonry',
3947        'isotope/js/layout-modes/fit-rows',
3948        'isotope/js/layout-modes/vertical'
3949      ],
3950      function( Outlayer, getSize, matchesSelector, utils, Item, LayoutMode ) {
3951        return factory( window, Outlayer, getSize, matchesSelector, utils, Item, LayoutMode );
3952      });
3953  } else if ( typeof exports == 'object' ) {
3954    // CommonJS
3955    module.exports = factory(
3956      window,
3957      require('outlayer'),
3958      require('get-size'),
3959      require('desandro-matches-selector'),
3960      require('fizzy-ui-utils'),
3961      require('./item'),
3962      require('./layout-mode'),
3963      // include default layout modes
3964      require('./layout-modes/masonry'),
3965      require('./layout-modes/fit-rows'),
3966      require('./layout-modes/vertical')
3967    );
3968  } else {
3969    // browser global
3970    window.Isotope = factory(
3971      window,
3972      window.Outlayer,
3973      window.getSize,
3974      window.matchesSelector,
3975      window.fizzyUIUtils,
3976      window.Isotope.Item,
3977      window.Isotope.LayoutMode
3978    );
3979  }
3980
3981}( window, function factory( window, Outlayer, getSize, matchesSelector, utils,
3982  Item, LayoutMode ) {
3983
3984
3985
3986// -------------------------- vars -------------------------- //
3987
3988var jQuery = window.jQuery;
3989
3990// -------------------------- helpers -------------------------- //
3991
3992var trim = String.prototype.trim ?
3993  function( str ) {
3994    return str.trim();
3995  } :
3996  function( str ) {
3997    return str.replace( /^\s+|\s+$/g, '' );
3998  };
3999
4000var docElem = document.documentElement;
4001
4002var getText = docElem.textContent ?
4003  function( elem ) {
4004    return elem.textContent;
4005  } :
4006  function( elem ) {
4007    return elem.innerText;
4008  };
4009
4010// -------------------------- isotopeDefinition -------------------------- //
4011
4012  // create an Outlayer layout class
4013  var Isotope = Outlayer.create( 'isotope', {
4014    layoutMode: "masonry",
4015    isJQueryFiltering: true,
4016    sortAscending: true
4017  });
4018
4019  Isotope.Item = Item;
4020  Isotope.LayoutMode = LayoutMode;
4021
4022  Isotope.prototype._create = function() {
4023    this.itemGUID = 0;
4024    // functions that sort items
4025    this._sorters = {};
4026    this._getSorters();
4027    // call super
4028    Outlayer.prototype._create.call( this );
4029
4030    // create layout modes
4031    this.modes = {};
4032    // start filteredItems with all items
4033    this.filteredItems = this.items;
4034    // keep of track of sortBys
4035    this.sortHistory = [ 'original-order' ];
4036    // create from registered layout modes
4037    for ( var name in LayoutMode.modes ) {
4038      this._initLayoutMode( name );
4039    }
4040  };
4041
4042  Isotope.prototype.reloadItems = function() {
4043    // reset item ID counter
4044    this.itemGUID = 0;
4045    // call super
4046    Outlayer.prototype.reloadItems.call( this );
4047  };
4048
4049  Isotope.prototype._itemize = function() {
4050    var items = Outlayer.prototype._itemize.apply( this, arguments );
4051    // assign ID for original-order
4052    for ( var i=0, len = items.length; i < len; i++ ) {
4053      var item = items[i];
4054      item.id = this.itemGUID++;
4055    }
4056    this._updateItemsSortData( items );
4057    return items;
4058  };
4059
4060
4061  // -------------------------- layout -------------------------- //
4062
4063  Isotope.prototype._initLayoutMode = function( name ) {
4064    var Mode = LayoutMode.modes[ name ];
4065    // set mode options
4066    // HACK extend initial options, back-fill in default options
4067    var initialOpts = this.options[ name ] || {};
4068    this.options[ name ] = Mode.options ?
4069      utils.extend( Mode.options, initialOpts ) : initialOpts;
4070    // init layout mode instance
4071    this.modes[ name ] = new Mode( this );
4072  };
4073
4074
4075  Isotope.prototype.layout = function() {
4076    // if first time doing layout, do all magic
4077    if ( !this._isLayoutInited && this.options.isInitLayout ) {
4078      this.arrange();
4079      return;
4080    }
4081    this._layout();
4082  };
4083
4084  // private method to be used in layout() & magic()
4085  Isotope.prototype._layout = function() {
4086    // don't animate first layout
4087    var isInstant = this._getIsInstant();
4088    // layout flow
4089    this._resetLayout();
4090    this._manageStamps();
4091    this.layoutItems( this.filteredItems, isInstant );
4092
4093    // flag for initalized
4094    this._isLayoutInited = true;
4095  };
4096
4097  // filter + sort + layout
4098  Isotope.prototype.arrange = function( opts ) {
4099    // set any options pass
4100    this.option( opts );
4101    this._getIsInstant();
4102    // filter, sort, and layout
4103
4104    // filter
4105    var filtered = this._filter( this.items );
4106    this.filteredItems = filtered.matches;
4107
4108    var _this = this;
4109    function hideReveal() {
4110      _this.reveal( filtered.needReveal );
4111      _this.hide( filtered.needHide );
4112    }
4113
4114    this._bindArrangeComplete();
4115
4116    if ( this._isInstant ) {
4117      this._noTransition( hideReveal );
4118    } else {
4119      hideReveal();
4120    }
4121
4122    this._sort();
4123    this._layout();
4124  };
4125  // alias to _init for main plugin method
4126  Isotope.prototype._init = Isotope.prototype.arrange;
4127
4128  // HACK
4129  // Don't animate/transition first layout
4130  // Or don't animate/transition other layouts
4131  Isotope.prototype._getIsInstant = function() {
4132    var isInstant = this.options.isLayoutInstant !== undefined ?
4133      this.options.isLayoutInstant : !this._isLayoutInited;
4134    this._isInstant = isInstant;
4135    return isInstant;
4136  };
4137
4138  // listen for layoutComplete, hideComplete and revealComplete
4139  // to trigger arrangeComplete
4140  Isotope.prototype._bindArrangeComplete = function() {
4141    // listen for 3 events to trigger arrangeComplete
4142    var isLayoutComplete, isHideComplete, isRevealComplete;
4143    var _this = this;
4144    function arrangeParallelCallback() {
4145      if ( isLayoutComplete && isHideComplete && isRevealComplete ) {
4146        _this.dispatchEvent( 'arrangeComplete', null, [ _this.filteredItems ] );
4147      }
4148    }
4149    this.once( 'layoutComplete', function() {
4150      isLayoutComplete = true;
4151      arrangeParallelCallback();
4152    });
4153    this.once( 'hideComplete', function() {
4154      isHideComplete = true;
4155      arrangeParallelCallback();
4156    });
4157    this.once( 'revealComplete', function() {
4158      isRevealComplete = true;
4159      arrangeParallelCallback();
4160    });
4161  };
4162
4163  // -------------------------- filter -------------------------- //
4164
4165  Isotope.prototype._filter = function( items ) {
4166    var filter = this.options.filter;
4167    filter = filter || '*';
4168    var matches = [];
4169    var hiddenMatched = [];
4170    var visibleUnmatched = [];
4171
4172    var test = this._getFilterTest( filter );
4173
4174    // test each item
4175    for ( var i=0, len = items.length; i < len; i++ ) {
4176      var item = items[i];
4177      if ( item.isIgnored ) {
4178        continue;
4179      }
4180      // add item to either matched or unmatched group
4181      var isMatched = test( item );
4182      // item.isFilterMatched = isMatched;
4183      // add to matches if its a match
4184      if ( isMatched ) {
4185        matches.push( item );
4186      }
4187      // add to additional group if item needs to be hidden or revealed
4188      if ( isMatched && item.isHidden ) {
4189        hiddenMatched.push( item );
4190      } else if ( !isMatched && !item.isHidden ) {
4191        visibleUnmatched.push( item );
4192      }
4193    }
4194
4195    // return collections of items to be manipulated
4196    return {
4197      matches: matches,
4198      needReveal: hiddenMatched,
4199      needHide: visibleUnmatched
4200    };
4201  };
4202
4203  // get a jQuery, function, or a matchesSelector test given the filter
4204  Isotope.prototype._getFilterTest = function( filter ) {
4205    if ( jQuery && this.options.isJQueryFiltering ) {
4206      // use jQuery
4207      return function( item ) {
4208        return jQuery( item.element ).is( filter );
4209      };
4210    }
4211    if ( typeof filter == 'function' ) {
4212      // use filter as function
4213      return function( item ) {
4214        return filter( item.element );
4215      };
4216    }
4217    // default, use filter as selector string
4218    return function( item ) {
4219      return matchesSelector( item.element, filter );
4220    };
4221  };
4222
4223  // -------------------------- sorting -------------------------- //
4224
4225  /**
4226   * @params {Array} elems
4227   * @public
4228   */
4229  Isotope.prototype.updateSortData = function( elems ) {
4230    // get items
4231    var items;
4232    if ( elems ) {
4233      elems = utils.makeArray( elems );
4234      items = this.getItems( elems );
4235    } else {
4236      // update all items if no elems provided
4237      items = this.items;
4238    }
4239
4240    this._getSorters();
4241    this._updateItemsSortData( items );
4242  };
4243
4244  Isotope.prototype._getSorters = function() {
4245    var getSortData = this.options.getSortData;
4246    for ( var key in getSortData ) {
4247      var sorter = getSortData[ key ];
4248      this._sorters[ key ] = mungeSorter( sorter );
4249    }
4250  };
4251
4252  /**
4253   * @params {Array} items - of Isotope.Items
4254   * @private
4255   */
4256  Isotope.prototype._updateItemsSortData = function( items ) {
4257    // do not update if no items
4258    var len = items && items.length;
4259
4260    for ( var i=0; len && i < len; i++ ) {
4261      var item = items[i];
4262      item.updateSortData();
4263    }
4264  };
4265
4266  // ----- munge sorter ----- //
4267
4268  // encapsulate this, as we just need mungeSorter
4269  // other functions in here are just for munging
4270  var mungeSorter = ( function() {
4271    // add a magic layer to sorters for convienent shorthands
4272    // `.foo-bar` will use the text of .foo-bar querySelector
4273    // `[foo-bar]` will use attribute
4274    // you can also add parser
4275    // `.foo-bar parseInt` will parse that as a number
4276    function mungeSorter( sorter ) {
4277      // if not a string, return function or whatever it is
4278      if ( typeof sorter != 'string' ) {
4279        return sorter;
4280      }
4281      // parse the sorter string
4282      var args = trim( sorter ).split(' ');
4283      var query = args[0];
4284      // check if query looks like [an-attribute]
4285      var attrMatch = query.match( /^\[(.+)\]$/ );
4286      var attr = attrMatch && attrMatch[1];
4287      var getValue = getValueGetter( attr, query );
4288      // use second argument as a parser
4289      var parser = Isotope.sortDataParsers[ args[1] ];
4290      // parse the value, if there was a parser
4291      sorter = parser ? function( elem ) {
4292        return elem && parser( getValue( elem ) );
4293      } :
4294      // otherwise just return value
4295      function( elem ) {
4296        return elem && getValue( elem );
4297      };
4298
4299      return sorter;
4300    }
4301
4302    // get an attribute getter, or get text of the querySelector
4303    function getValueGetter( attr, query ) {
4304      var getValue;
4305      // if query looks like [foo-bar], get attribute
4306      if ( attr ) {
4307        getValue = function( elem ) {
4308          return elem.getAttribute( attr );
4309        };
4310      } else {
4311        // otherwise, assume its a querySelector, and get its text
4312        getValue = function( elem ) {
4313          var child = elem.querySelector( query );
4314          return child && getText( child );
4315        };
4316      }
4317      return getValue;
4318    }
4319
4320    return mungeSorter;
4321  })();
4322
4323  // parsers used in getSortData shortcut strings
4324  Isotope.sortDataParsers = {
4325    'parseInt': function( val ) {
4326      return parseInt( val, 10 );
4327    },
4328    'parseFloat': function( val ) {
4329      return parseFloat( val );
4330    }
4331  };
4332
4333  // ----- sort method ----- //
4334
4335  // sort filteredItem order
4336  Isotope.prototype._sort = function() {
4337    var sortByOpt = this.options.sortBy;
4338    if ( !sortByOpt ) {
4339      return;
4340    }
4341    // concat all sortBy and sortHistory
4342    var sortBys = [].concat.apply( sortByOpt, this.sortHistory );
4343    // sort magic
4344    var itemSorter = getItemSorter( sortBys, this.options.sortAscending );
4345    this.filteredItems.sort( itemSorter );
4346    // keep track of sortBy History
4347    if ( sortByOpt != this.sortHistory[0] ) {
4348      // add to front, oldest goes in last
4349      this.sortHistory.unshift( sortByOpt );
4350    }
4351  };
4352
4353  // returns a function used for sorting
4354  function getItemSorter( sortBys, sortAsc ) {
4355    return function sorter( itemA, itemB ) {
4356      // cycle through all sortKeys
4357      for ( var i = 0, len = sortBys.length; i < len; i++ ) {
4358        var sortBy = sortBys[i];
4359        var a = itemA.sortData[ sortBy ];
4360        var b = itemB.sortData[ sortBy ];
4361        if ( a > b || a < b ) {
4362          // if sortAsc is an object, use the value given the sortBy key
4363          var isAscending = sortAsc[ sortBy ] !== undefined ? sortAsc[ sortBy ] : sortAsc;
4364          var direction = isAscending ? 1 : -1;
4365          return ( a > b ? 1 : -1 ) * direction;
4366        }
4367      }
4368      return 0;
4369    };
4370  }
4371
4372  // -------------------------- methods -------------------------- //
4373
4374  // get layout mode
4375  Isotope.prototype._mode = function() {
4376    var layoutMode = this.options.layoutMode;
4377    var mode = this.modes[ layoutMode ];
4378    if ( !mode ) {
4379      // TODO console.error
4380      throw new Error( 'No layout mode: ' + layoutMode );
4381    }
4382    // HACK sync mode's options
4383    // any options set after init for layout mode need to be synced
4384    mode.options = this.options[ layoutMode ];
4385    return mode;
4386  };
4387
4388  Isotope.prototype._resetLayout = function() {
4389    // trigger original reset layout
4390    Outlayer.prototype._resetLayout.call( this );
4391    this._mode()._resetLayout();
4392  };
4393
4394  Isotope.prototype._getItemLayoutPosition = function( item  ) {
4395    return this._mode()._getItemLayoutPosition( item );
4396  };
4397
4398  Isotope.prototype._manageStamp = function( stamp ) {
4399    this._mode()._manageStamp( stamp );
4400  };
4401
4402  Isotope.prototype._getContainerSize = function() {
4403    return this._mode()._getContainerSize();
4404  };
4405
4406  Isotope.prototype.needsResizeLayout = function() {
4407    return this._mode().needsResizeLayout();
4408  };
4409
4410  // -------------------------- adding & removing -------------------------- //
4411
4412  // HEADS UP overwrites default Outlayer appended
4413  Isotope.prototype.appended = function( elems ) {
4414    var items = this.addItems( elems );
4415    if ( !items.length ) {
4416      return;
4417    }
4418    // filter, layout, reveal new items
4419    var filteredItems = this._filterRevealAdded( items );
4420    // add to filteredItems
4421    this.filteredItems = this.filteredItems.concat( filteredItems );
4422  };
4423
4424  // HEADS UP overwrites default Outlayer prepended
4425  Isotope.prototype.prepended = function( elems ) {
4426    var items = this._itemize( elems );
4427    if ( !items.length ) {
4428      return;
4429    }
4430    // start new layout
4431    this._resetLayout();
4432    this._manageStamps();
4433    // filter, layout, reveal new items
4434    var filteredItems = this._filterRevealAdded( items );
4435    // layout previous items
4436    this.layoutItems( this.filteredItems );
4437    // add to items and filteredItems
4438    this.filteredItems = filteredItems.concat( this.filteredItems );
4439    this.items = items.concat( this.items );
4440  };
4441
4442  Isotope.prototype._filterRevealAdded = function( items ) {
4443    var filtered = this._filter( items );
4444    this.hide( filtered.needHide );
4445    // reveal all new items
4446    this.reveal( filtered.matches );
4447    // layout new items, no transition
4448    this.layoutItems( filtered.matches, true );
4449    return filtered.matches;
4450  };
4451
4452  /**
4453   * Filter, sort, and layout newly-appended item elements
4454   * @param {Array or NodeList or Element} elems
4455   */
4456  Isotope.prototype.insert = function( elems ) {
4457    var items = this.addItems( elems );
4458    if ( !items.length ) {
4459      return;
4460    }
4461    // append item elements
4462    var i, item;
4463    var len = items.length;
4464    for ( i=0; i < len; i++ ) {
4465      item = items[i];
4466      this.element.appendChild( item.element );
4467    }
4468    // filter new stuff
4469    var filteredInsertItems = this._filter( items ).matches;
4470    // set flag
4471    for ( i=0; i < len; i++ ) {
4472      items[i].isLayoutInstant = true;
4473    }
4474    this.arrange();
4475    // reset flag
4476    for ( i=0; i < len; i++ ) {
4477      delete items[i].isLayoutInstant;
4478    }
4479    this.reveal( filteredInsertItems );
4480  };
4481
4482  var _remove = Isotope.prototype.remove;
4483  Isotope.prototype.remove = function( elems ) {
4484    elems = utils.makeArray( elems );
4485    var removeItems = this.getItems( elems );
4486    // do regular thing
4487    _remove.call( this, elems );
4488    // bail if no items to remove
4489    var len = removeItems && removeItems.length;
4490    if ( !len ) {
4491      return;
4492    }
4493    // remove elems from filteredItems
4494    for ( var i=0; i < len; i++ ) {
4495      var item = removeItems[i];
4496      // remove item from collection
4497      utils.removeFrom( this.filteredItems, item );
4498    }
4499  };
4500
4501  Isotope.prototype.shuffle = function() {
4502    // update random sortData
4503    for ( var i=0, len = this.items.length; i < len; i++ ) {
4504      var item = this.items[i];
4505      item.sortData.random = Math.random();
4506    }
4507    this.options.sortBy = 'random';
4508    this._sort();
4509    this._layout();
4510  };
4511
4512  /**
4513   * trigger fn without transition
4514   * kind of hacky to have this in the first place
4515   * @param {Function} fn
4516   * @returns ret
4517   * @private
4518   */
4519  Isotope.prototype._noTransition = function( fn ) {
4520    // save transitionDuration before disabling
4521    var transitionDuration = this.options.transitionDuration;
4522    // disable transition
4523    this.options.transitionDuration = 0;
4524    // do it
4525    var returnValue = fn.call( this );
4526    // re-enable transition for reveal
4527    this.options.transitionDuration = transitionDuration;
4528    return returnValue;
4529  };
4530
4531  // ----- helper methods ----- //
4532
4533  /**
4534   * getter method for getting filtered item elements
4535   * @returns {Array} elems - collection of item elements
4536   */
4537  Isotope.prototype.getFilteredItemElements = function() {
4538    var elems = [];
4539    for ( var i=0, len = this.filteredItems.length; i < len; i++ ) {
4540      elems.push( this.filteredItems[i].element );
4541    }
4542    return elems;
4543  };
4544
4545  // -----  ----- //
4546
4547  return Isotope;
4548
4549}));
4550
vendor: 29,252 bytes, lines 4551-5707
4551/*!
4552 * Packery layout mode PACKAGED v2.0.1
4553 * sub-classes Packery
4554 */
4555
4556/**
4557 * Rect
4558 * low-level utility class for basic geometry
4559 */
4560
4561( function( window, factory ) {
4562  // universal module definition
4563  /* jshint strict: false */ /* globals define, module */
4564  if ( typeof define == 'function' && define.amd ) {
4565    // AMD
4566    define( 'packery/js/rect',factory );
4567  } else if ( typeof module == 'object' && module.exports ) {
4568    // CommonJS
4569    module.exports = factory();
4570  } else {
4571    // browser global
4572    window.Packery = window.Packery || {};
4573    window.Packery.Rect = factory();
4574  }
4575
4576}( window, function factory() {
4577
4578
4579// -------------------------- Rect -------------------------- //
4580
4581function Rect( props ) {
4582  // extend properties from defaults
4583  for ( var prop in Rect.defaults ) {
4584    this[ prop ] = Rect.defaults[ prop ];
4585  }
4586
4587  for ( prop in props ) {
4588    this[ prop ] = props[ prop ];
4589  }
4590
4591}
4592
4593Rect.defaults = {
4594  x: 0,
4595  y: 0,
4596  width: 0,
4597  height: 0
4598};
4599
4600var proto = Rect.prototype;
4601
4602/**
4603 * Determines whether or not this rectangle wholly encloses another rectangle or point.
4604 * @param {Rect} rect
4605 * @returns {Boolean}
4606**/
4607proto.contains = function( rect ) {
4608  // points don't have width or height
4609  var otherWidth = rect.width || 0;
4610  var otherHeight = rect.height || 0;
4611  return this.x <= rect.x &&
4612    this.y <= rect.y &&
4613    this.x + this.width >= rect.x + otherWidth &&
4614    this.y + this.height >= rect.y + otherHeight;
4615};
4616
4617/**
4618 * Determines whether or not the rectangle intersects with another.
4619 * @param {Rect} rect
4620 * @returns {Boolean}
4621**/
4622proto.overlaps = function( rect ) {
4623  var thisRight = this.x + this.width;
4624  var thisBottom = this.y + this.height;
4625  var rectRight = rect.x + rect.width;
4626  var rectBottom = rect.y + rect.height;
4627
4628  // http://stackoverflow.com/a/306332
4629  return this.x < rectRight &&
4630    thisRight > rect.x &&
4631    this.y < rectBottom &&
4632    thisBottom > rect.y;
4633};
4634
4635/**
4636 * @param {Rect} rect - the overlapping rect
4637 * @returns {Array} freeRects - rects representing the area around the rect
4638**/
4639proto.getMaximalFreeRects = function( rect ) {
4640
4641  // if no intersection, return false
4642  if ( !this.overlaps( rect ) ) {
4643    return false;
4644  }
4645
4646  var freeRects = [];
4647  var freeRect;
4648
4649  var thisRight = this.x + this.width;
4650  var thisBottom = this.y + this.height;
4651  var rectRight = rect.x + rect.width;
4652  var rectBottom = rect.y + rect.height;
4653
4654  // top
4655  if ( this.y < rect.y ) {
4656    freeRect = new Rect({
4657      x: this.x,
4658      y: this.y,
4659      width: this.width,
4660      height: rect.y - this.y
4661    });
4662    freeRects.push( freeRect );
4663  }
4664
4665  // right
4666  if ( thisRight > rectRight ) {
4667    freeRect = new Rect({
4668      x: rectRight,
4669      y: this.y,
4670      width: thisRight - rectRight,
4671      height: this.height
4672    });
4673    freeRects.push( freeRect );
4674  }
4675
4676  // bottom
4677  if ( thisBottom > rectBottom ) {
4678    freeRect = new Rect({
4679      x: this.x,
4680      y: rectBottom,
4681      width: this.width,
4682      height: thisBottom - rectBottom
4683    });
4684    freeRects.push( freeRect );
4685  }
4686
4687  // left
4688  if ( this.x < rect.x ) {
4689    freeRect = new Rect({
4690      x: this.x,
4691      y: this.y,
4692      width: rect.x - this.x,
4693      height: this.height
4694    });
4695    freeRects.push( freeRect );
4696  }
4697
4698  return freeRects;
4699};
4700
4701proto.canFit = function( rect ) {
4702  return this.width >= rect.width && this.height >= rect.height;
4703};
4704
4705return Rect;
4706
4707}));
4708
4709/**
4710 * Packer
4711 * bin-packing algorithm
4712 */
4713
4714( function( window, factory ) {
4715  // universal module definition
4716  /* jshint strict: false */ /* globals define, module, require */
4717  if ( typeof define == 'function' && define.amd ) {
4718    // AMD
4719    define( 'packery/js/packer',[ './rect' ], factory );
4720  } else if ( typeof module == 'object' && module.exports ) {
4721    // CommonJS
4722    module.exports = factory(
4723      require('./rect')
4724    );
4725  } else {
4726    // browser global
4727    var Packery = window.Packery = window.Packery || {};
4728    Packery.Packer = factory( Packery.Rect );
4729  }
4730
4731}( window, function factory( Rect ) {
4732
4733
4734// -------------------------- Packer -------------------------- //
4735
4736/**
4737 * @param {Number} width
4738 * @param {Number} height
4739 * @param {String} sortDirection
4740 *   topLeft for vertical, leftTop for horizontal
4741 */
4742function Packer( width, height, sortDirection ) {
4743  this.width = width || 0;
4744  this.height = height || 0;
4745  this.sortDirection = sortDirection || 'downwardLeftToRight';
4746
4747  this.reset();
4748}
4749
4750var proto = Packer.prototype;
4751
4752proto.reset = function() {
4753  this.spaces = [];
4754
4755  var initialSpace = new Rect({
4756    x: 0,
4757    y: 0,
4758    width: this.width,
4759    height: this.height
4760  });
4761
4762  this.spaces.push( initialSpace );
4763  // set sorter
4764  this.sorter = sorters[ this.sortDirection ] || sorters.downwardLeftToRight;
4765};
4766
4767// change x and y of rect to fit with in Packer's available spaces
4768proto.pack = function( rect ) {
4769  for ( var i=0; i < this.spaces.length; i++ ) {
4770    var space = this.spaces[i];
4771    if ( space.canFit( rect ) ) {
4772      this.placeInSpace( rect, space );
4773      break;
4774    }
4775  }
4776};
4777
4778proto.columnPack = function( rect ) {
4779  for ( var i=0; i < this.spaces.length; i++ ) {
4780    var space = this.spaces[i];
4781    var canFitInSpaceColumn = space.x <= rect.x &&
4782      space.x + space.width >= rect.x + rect.width &&
4783      space.height >= rect.height - 0.01; // fudge number for rounding error
4784    if ( canFitInSpaceColumn ) {
4785      rect.y = space.y;
4786      this.placed( rect );
4787      break;
4788    }
4789  }
4790};
4791
4792proto.rowPack = function( rect ) {
4793  for ( var i=0; i < this.spaces.length; i++ ) {
4794    var space = this.spaces[i];
4795    var canFitInSpaceRow = space.y <= rect.y &&
4796      space.y + space.height >= rect.y + rect.height &&
4797      space.width >= rect.width - 0.01; // fudge number for rounding error
4798    if ( canFitInSpaceRow ) {
4799      rect.x = space.x;
4800      this.placed( rect );
4801      break;
4802    }
4803  }
4804};
4805
4806proto.placeInSpace = function( rect, space ) {
4807  // place rect in space
4808  rect.x = space.x;
4809  rect.y = space.y;
4810
4811  this.placed( rect );
4812};
4813
4814// update spaces with placed rect
4815proto.placed = function( rect ) {
4816  // update spaces
4817  var revisedSpaces = [];
4818  for ( var i=0; i < this.spaces.length; i++ ) {
4819    var space = this.spaces[i];
4820    var newSpaces = space.getMaximalFreeRects( rect );
4821    // add either the original space or the new spaces to the revised spaces
4822    if ( newSpaces ) {
4823      revisedSpaces.push.apply( revisedSpaces, newSpaces );
4824    } else {
4825      revisedSpaces.push( space );
4826    }
4827  }
4828
4829  this.spaces = revisedSpaces;
4830
4831  this.mergeSortSpaces();
4832};
4833
4834proto.mergeSortSpaces = function() {
4835  // remove redundant spaces
4836  Packer.mergeRects( this.spaces );
4837  this.spaces.sort( this.sorter );
4838};
4839
4840// add a space back
4841proto.addSpace = function( rect ) {
4842  this.spaces.push( rect );
4843  this.mergeSortSpaces();
4844};
4845
4846// -------------------------- utility functions -------------------------- //
4847
4848/**
4849 * Remove redundant rectangle from array of rectangles
4850 * @param {Array} rects: an array of Rects
4851 * @returns {Array} rects: an array of Rects
4852**/
4853Packer.mergeRects = function( rects ) {
4854  var i = 0;
4855  var rect = rects[i];
4856
4857  rectLoop:
4858  while ( rect ) {
4859    var j = 0;
4860    var compareRect = rects[ i + j ];
4861
4862    while ( compareRect ) {
4863      if  ( compareRect == rect ) {
4864        j++; // next
4865      } else if ( compareRect.contains( rect ) ) {
4866        // remove rect
4867        rects.splice( i, 1 );
4868        rect = rects[i]; // set next rect
4869        continue rectLoop; // bail on compareLoop
4870      } else if ( rect.contains( compareRect ) ) {
4871        // remove compareRect
4872        rects.splice( i + j, 1 );
4873      } else {
4874        j++;
4875      }
4876      compareRect = rects[ i + j ]; // set next compareRect
4877    }
4878    i++;
4879    rect = rects[i];
4880  }
4881
4882  return rects;
4883};
4884
4885
4886// -------------------------- sorters -------------------------- //
4887
4888// functions for sorting rects in order
4889var sorters = {
4890  // top down, then left to right
4891  downwardLeftToRight: function( a, b ) {
4892    return a.y - b.y || a.x - b.x;
4893  },
4894  // left to right, then top down
4895  rightwardTopToBottom: function( a, b ) {
4896    return a.x - b.x || a.y - b.y;
4897  }
4898};
4899
4900
4901// --------------------------  -------------------------- //
4902
4903return Packer;
4904
4905}));
4906
4907/**
4908 * Packery Item Element
4909**/
4910
4911( function( window, factory ) {
4912  // universal module definition
4913  /* jshint strict: false */ /* globals define, module, require */
4914  if ( typeof define == 'function' && define.amd ) {
4915    // AMD
4916    define( 'packery/js/item',[
4917        'outlayer/outlayer',
4918        './rect'
4919      ],
4920      factory );
4921  } else if ( typeof module == 'object' && module.exports ) {
4922    // CommonJS
4923    module.exports = factory(
4924      require('outlayer'),
4925      require('./rect')
4926    );
4927  } else {
4928    // browser global
4929    window.Packery.Item = factory(
4930      window.Outlayer,
4931      window.Packery.Rect
4932    );
4933  }
4934
4935}( window, function factory( Outlayer, Rect ) {
4936
4937
4938// -------------------------- Item -------------------------- //
4939
4940var docElemStyle = document.documentElement.style;
4941
4942var transformProperty = typeof docElemStyle.transform == 'string' ?
4943  'transform' : 'WebkitTransform';
4944
4945// sub-class Item
4946var Item = function PackeryItem() {
4947  Outlayer.Item.apply( this, arguments );
4948};
4949
4950var proto = Item.prototype = Object.create( Outlayer.Item.prototype );
4951
4952var __create = proto._create;
4953proto._create = function() {
4954  // call default _create logic
4955  __create.call( this );
4956  this.rect = new Rect();
4957};
4958
4959var _moveTo = proto.moveTo;
4960proto.moveTo = function( x, y ) {
4961  // don't shift 1px while dragging
4962  var dx = Math.abs( this.position.x - x );
4963  var dy = Math.abs( this.position.y - y );
4964
4965  var canHackGoTo = this.layout.dragItemCount && !this.isPlacing &&
4966    !this.isTransitioning && dx < 1 && dy < 1;
4967  if ( canHackGoTo ) {
4968    this.goTo( x, y );
4969    return;
4970  }
4971  _moveTo.apply( this, arguments );
4972};
4973
4974// -------------------------- placing -------------------------- //
4975
4976proto.enablePlacing = function() {
4977  this.removeTransitionStyles();
4978  // remove transform property from transition
4979  if ( this.isTransitioning && transformProperty ) {
4980    this.element.style[ transformProperty ] = 'none';
4981  }
4982  this.isTransitioning = false;
4983  this.getSize();
4984  this.layout._setRectSize( this.element, this.rect );
4985  this.isPlacing = true;
4986};
4987
4988proto.disablePlacing = function() {
4989  this.isPlacing = false;
4990};
4991
4992// -----  ----- //
4993
4994// remove element from DOM
4995proto.removeElem = function() {
4996  this.element.parentNode.removeChild( this.element );
4997  // add space back to packer
4998  this.layout.packer.addSpace( this.rect );
4999  this.emitEvent( 'remove', [ this ] );
5000};
5001
5002// ----- dropPlaceholder ----- //
5003
5004proto.showDropPlaceholder = function() {
5005  var dropPlaceholder = this.dropPlaceholder;
5006  if ( !dropPlaceholder ) {
5007    // create dropPlaceholder
5008    dropPlaceholder = this.dropPlaceholder = document.createElement('div');
5009    dropPlaceholder.className = 'packery-drop-placeholder';
5010    dropPlaceholder.style.position = 'absolute';
5011  }
5012
5013  dropPlaceholder.style.width = this.size.width + 'px';
5014  dropPlaceholder.style.height = this.size.height + 'px';
5015  this.positionDropPlaceholder();
5016  this.layout.element.appendChild( dropPlaceholder );
5017};
5018
5019proto.positionDropPlaceholder = function() {
5020  this.dropPlaceholder.style[ transformProperty ] = 'translate(' +
5021    this.rect.x + 'px, ' + this.rect.y + 'px)';
5022};
5023
5024proto.hideDropPlaceholder = function() {
5025  this.layout.element.removeChild( this.dropPlaceholder );
5026};
5027
5028// -----  ----- //
5029
5030return Item;
5031
5032}));
5033
5034/*!
5035 * Packery v2.0.0
5036 * Gapless, draggable grid layouts
5037 *
5038 * Licensed GPLv3 for open source use
5039 * or Packery Commercial License for commercial use
5040 *
5041 * http://packery.metafizzy.co
5042 * Copyright 2016 Metafizzy
5043 */
5044
5045( function( window, factory ) {
5046  // universal module definition
5047  /* jshint strict: false */ /* globals define, module, require */
5048  if ( typeof define == 'function' && define.amd ) {
5049    // AMD
5050    define( 'packery/js/packery',[
5051        'get-size/get-size',
5052        'outlayer/outlayer',
5053        './rect',
5054        './packer',
5055        './item'
5056      ],
5057      factory );
5058  } else if ( typeof module == 'object' && module.exports ) {
5059    // CommonJS
5060    module.exports = factory(
5061      require('get-size'),
5062      require('outlayer'),
5063      require('./rect'),
5064      require('./packer'),
5065      require('./item')
5066    );
5067  } else {
5068    // browser global
5069    window.Packery = factory(
5070      window.getSize,
5071      window.Outlayer,
5072      window.Packery.Rect,
5073      window.Packery.Packer,
5074      window.Packery.Item
5075    );
5076  }
5077
5078}( window, function factory( getSize, Outlayer, Rect, Packer, Item ) {
5079
5080
5081// ----- Rect ----- //
5082
5083// allow for pixel rounding errors IE8-IE11 & Firefox; #227
5084Rect.prototype.canFit = function( rect ) {
5085  return this.width >= rect.width - 1 && this.height >= rect.height - 1;
5086};
5087
5088// -------------------------- Packery -------------------------- //
5089
5090// create an Outlayer layout class
5091var Packery = Outlayer.create('packery');
5092Packery.Item = Item;
5093
5094var proto = Packery.prototype;
5095
5096proto._create = function() {
5097  // call super
5098  Outlayer.prototype._create.call( this );
5099
5100  // initial properties
5101  this.packer = new Packer();
5102  // packer for drop targets
5103  this.shiftPacker = new Packer();
5104  this.isEnabled = true;
5105
5106  this.dragItemCount = 0;
5107
5108  // create drag handlers
5109  var _this = this;
5110  this.handleDraggabilly = {
5111    dragStart: function() {
5112      _this.itemDragStart( this.element );
5113    },
5114    dragMove: function() {
5115      _this.itemDragMove( this.element, this.position.x, this.position.y );
5116    },
5117    dragEnd: function() {
5118      _this.itemDragEnd( this.element );
5119    }
5120  };
5121
5122  this.handleUIDraggable = {
5123    start: function handleUIDraggableStart( event, ui ) {
5124      // HTML5 may trigger dragstart, dismiss HTML5 dragging
5125      if ( !ui ) {
5126        return;
5127      }
5128      _this.itemDragStart( event.currentTarget );
5129    },
5130    drag: function handleUIDraggableDrag( event, ui ) {
5131      if ( !ui ) {
5132        return;
5133      }
5134      _this.itemDragMove( event.currentTarget, ui.position.left, ui.position.top );
5135    },
5136    stop: function handleUIDraggableStop( event, ui ) {
5137      if ( !ui ) {
5138        return;
5139      }
5140      _this.itemDragEnd( event.currentTarget );
5141    }
5142  };
5143
5144};
5145
5146
5147// ----- init & layout ----- //
5148
5149/**
5150 * logic before any new layout
5151 */
5152proto._resetLayout = function() {
5153  this.getSize();
5154
5155  this._getMeasurements();
5156
5157  // reset packer
5158  var width, height, sortDirection;
5159  // packer settings, if horizontal or vertical
5160  if ( this._getOption('horizontal') ) {
5161    width = Infinity;
5162    height = this.size.innerHeight + this.gutter;
5163    sortDirection = 'rightwardTopToBottom';
5164  } else {
5165    width = this.size.innerWidth + this.gutter;
5166    height = Infinity;
5167    sortDirection = 'downwardLeftToRight';
5168  }
5169
5170  this.packer.width = this.shiftPacker.width = width;
5171  this.packer.height = this.shiftPacker.height = height;
5172  this.packer.sortDirection = this.shiftPacker.sortDirection = sortDirection;
5173
5174  this.packer.reset();
5175
5176  // layout
5177  this.maxY = 0;
5178  this.maxX = 0;
5179};
5180
5181/**
5182 * update columnWidth, rowHeight, & gutter
5183 * @private
5184 */
5185proto._getMeasurements = function() {
5186  this._getMeasurement( 'columnWidth', 'width' );
5187  this._getMeasurement( 'rowHeight', 'height' );
5188  this._getMeasurement( 'gutter', 'width' );
5189};
5190
5191proto._getItemLayoutPosition = function( item ) {
5192  this._setRectSize( item.element, item.rect );
5193  if ( this.isShifting || this.dragItemCount > 0 ) {
5194    var packMethod = this._getPackMethod();
5195    this.packer[ packMethod ]( item.rect );
5196  } else {
5197    this.packer.pack( item.rect );
5198  }
5199
5200  this._setMaxXY( item.rect );
5201  return item.rect;
5202};
5203
5204proto.shiftLayout = function() {
5205  this.isShifting = true;
5206  this.layout();
5207  delete this.isShifting;
5208};
5209
5210proto._getPackMethod = function() {
5211  return this._getOption('horizontal') ? 'rowPack' : 'columnPack';
5212};
5213
5214
5215/**
5216 * set max X and Y value, for size of container
5217 * @param {Packery.Rect} rect
5218 * @private
5219 */
5220proto._setMaxXY = function( rect ) {
5221  this.maxX = Math.max( rect.x + rect.width, this.maxX );
5222  this.maxY = Math.max( rect.y + rect.height, this.maxY );
5223};
5224
5225/**
5226 * set the width and height of a rect, applying columnWidth and rowHeight
5227 * @param {Element} elem
5228 * @param {Packery.Rect} rect
5229 */
5230proto._setRectSize = function( elem, rect ) {
5231  var size = getSize( elem );
5232  var w = size.outerWidth;
5233  var h = size.outerHeight;
5234  // size for columnWidth and rowHeight, if available
5235  // only check if size is non-zero, #177
5236  if ( w || h ) {
5237    w = this._applyGridGutter( w, this.columnWidth );
5238    h = this._applyGridGutter( h, this.rowHeight );
5239  }
5240  // rect must fit in packer
5241  rect.width = Math.min( w, this.packer.width );
5242  rect.height = Math.min( h, this.packer.height );
5243};
5244
5245/**
5246 * fits item to columnWidth/rowHeight and adds gutter
5247 * @param {Number} measurement - item width or height
5248 * @param {Number} gridSize - columnWidth or rowHeight
5249 * @returns measurement
5250 */
5251proto._applyGridGutter = function( measurement, gridSize ) {
5252  // just add gutter if no gridSize
5253  if ( !gridSize ) {
5254    return measurement + this.gutter;
5255  }
5256  gridSize += this.gutter;
5257  // fit item to columnWidth/rowHeight
5258  var remainder = measurement % gridSize;
5259  var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
5260  measurement = Math[ mathMethod ]( measurement / gridSize ) * gridSize;
5261  return measurement;
5262};
5263
5264proto._getContainerSize = function() {
5265  if ( this._getOption('horizontal') ) {
5266    return {
5267      width: this.maxX - this.gutter
5268    };
5269  } else {
5270    return {
5271      height: this.maxY - this.gutter
5272    };
5273  }
5274};
5275
5276
5277// -------------------------- stamp -------------------------- //
5278
5279/**
5280 * makes space for element
5281 * @param {Element} elem
5282 */
5283proto._manageStamp = function( elem ) {
5284
5285  var item = this.getItem( elem );
5286  var rect;
5287  if ( item && item.isPlacing ) {
5288    rect = item.rect;
5289  } else {
5290    var offset = this._getElementOffset( elem );
5291    rect = new Rect({
5292      x: this._getOption('originLeft') ? offset.left : offset.right,
5293      y: this._getOption('originTop') ? offset.top : offset.bottom
5294    });
5295  }
5296
5297  this._setRectSize( elem, rect );
5298  // save its space in the packer
5299  this.packer.placed( rect );
5300  this._setMaxXY( rect );
5301};
5302
5303// -------------------------- methods -------------------------- //
5304
5305function verticalSorter( a, b ) {
5306  return a.position.y - b.position.y || a.position.x - b.position.x;
5307}
5308
5309function horizontalSorter( a, b ) {
5310  return a.position.x - b.position.x || a.position.y - b.position.y;
5311}
5312
5313proto.sortItemsByPosition = function() {
5314  var sorter = this._getOption('horizontal') ? horizontalSorter : verticalSorter;
5315  this.items.sort( sorter );
5316};
5317
5318/**
5319 * Fit item element in its current position
5320 * Packery will position elements around it
5321 * useful for expanding elements
5322 *
5323 * @param {Element} elem
5324 * @param {Number} x - horizontal destination position, optional
5325 * @param {Number} y - vertical destination position, optional
5326 */
5327proto.fit = function( elem, x, y ) {
5328  var item = this.getItem( elem );
5329  if ( !item ) {
5330    return;
5331  }
5332
5333  // stamp item to get it out of layout
5334  this.stamp( item.element );
5335  // set placing flag
5336  item.enablePlacing();
5337  this.updateShiftTargets( item );
5338  // fall back to current position for fitting
5339  x = x === undefined ? item.rect.x: x;
5340  y = y === undefined ? item.rect.y: y;
5341  // position it best at its destination
5342  this.shift( item, x, y );
5343  this._bindFitEvents( item );
5344  item.moveTo( item.rect.x, item.rect.y );
5345  // layout everything else
5346  this.shiftLayout();
5347  // return back to regularly scheduled programming
5348  this.unstamp( item.element );
5349  this.sortItemsByPosition();
5350  item.disablePlacing();
5351};
5352
5353/**
5354 * emit event when item is fit and other items are laid out
5355 * @param {Packery.Item} item
5356 * @private
5357 */
5358proto._bindFitEvents = function( item ) {
5359  var _this = this;
5360  var ticks = 0;
5361  function onLayout() {
5362    ticks++;
5363    if ( ticks != 2 ) {
5364      return;
5365    }
5366    _this.dispatchEvent( 'fitComplete', null, [ item ] );
5367  }
5368  // when item is laid out
5369  item.once( 'layout', onLayout );
5370  // when all items are laid out
5371  this.once( 'layoutComplete', onLayout );
5372};
5373
5374// -------------------------- resize -------------------------- //
5375
5376// debounced, layout on resize
5377proto.resize = function() {
5378  // don't trigger if size did not change
5379  // or if resize was unbound. See #285, outlayer#9
5380  if ( !this.isResizeBound || !this.needsResizeLayout() ) {
5381    return;
5382  }
5383
5384  if ( this.options.shiftPercentResize ) {
5385    this.resizeShiftPercentLayout();
5386  } else {
5387    this.layout();
5388  }
5389};
5390
5391/**
5392 * check if layout is needed post layout
5393 * @returns Boolean
5394 */
5395proto.needsResizeLayout = function() {
5396  var size = getSize( this.element );
5397  var innerSize = this._getOption('horizontal') ? 'innerHeight' : 'innerWidth';
5398  return size[ innerSize ] != this.size[ innerSize ];
5399};
5400
5401proto.resizeShiftPercentLayout = function() {
5402  var items = this._getItemsForLayout( this.items );
5403
5404  var isHorizontal = this._getOption('horizontal');
5405  var coord = isHorizontal ? 'y' : 'x';
5406  var measure = isHorizontal ? 'height' : 'width';
5407  var segmentName = isHorizontal ? 'rowHeight' : 'columnWidth';
5408  var innerSize = isHorizontal ? 'innerHeight' : 'innerWidth';
5409
5410  // proportional re-align items
5411  var previousSegment = this[ segmentName ];
5412  previousSegment = previousSegment && previousSegment + this.gutter;
5413
5414  if ( previousSegment ) {
5415    this._getMeasurements();
5416    var currentSegment = this[ segmentName ] + this.gutter;
5417    items.forEach( function( item ) {
5418      var seg = Math.round( item.rect[ coord ] / previousSegment );
5419      item.rect[ coord ] = seg * currentSegment;
5420    });
5421  } else {
5422    var currentSize = getSize( this.element )[ innerSize ] + this.gutter;
5423    var previousSize = this.packer[ measure ];
5424    items.forEach( function( item ) {
5425      item.rect[ coord ] = ( item.rect[ coord ] / previousSize ) * currentSize;
5426    });
5427  }
5428
5429  this.shiftLayout();
5430};
5431
5432// -------------------------- drag -------------------------- //
5433
5434/**
5435 * handle an item drag start event
5436 * @param {Element} elem
5437 */
5438proto.itemDragStart = function( elem ) {
5439  if ( !this.isEnabled ) {
5440    return;
5441  }
5442  this.stamp( elem );
5443  // this.ignore( elem );
5444  var item = this.getItem( elem );
5445  if ( !item ) {
5446    return;
5447  }
5448
5449  item.enablePlacing();
5450  item.showDropPlaceholder();
5451  this.dragItemCount++;
5452  this.updateShiftTargets( item );
5453};
5454
5455proto.updateShiftTargets = function( dropItem ) {
5456  this.shiftPacker.reset();
5457
5458  // pack stamps
5459  this._getBoundingRect();
5460  var isOriginLeft = this._getOption('originLeft');
5461  var isOriginTop = this._getOption('originTop');
5462  this.stamps.forEach( function( stamp ) {
5463    // ignore dragged item
5464    var item = this.getItem( stamp );
5465    if ( item && item.isPlacing ) {
5466      return;
5467    }
5468    var offset = this._getElementOffset( stamp );
5469    var rect = new Rect({
5470      x: isOriginLeft ? offset.left : offset.right,
5471      y: isOriginTop ? offset.top : offset.bottom
5472    });
5473    this._setRectSize( stamp, rect );
5474    // save its space in the packer
5475    this.shiftPacker.placed( rect );
5476  }, this );
5477
5478  // reset shiftTargets
5479  var isHorizontal = this._getOption('horizontal');
5480  var segmentName = isHorizontal ? 'rowHeight' : 'columnWidth';
5481  var measure = isHorizontal ? 'height' : 'width';
5482
5483  this.shiftTargetKeys = [];
5484  this.shiftTargets = [];
5485  var boundsSize;
5486  var segment = this[ segmentName ];
5487  segment = segment && segment + this.gutter;
5488
5489  if ( segment ) {
5490    var segmentSpan = Math.ceil( dropItem.rect[ measure ] / segment );
5491    var segs = Math.floor( ( this.shiftPacker[ measure ] + this.gutter ) / segment );
5492    boundsSize = ( segs - segmentSpan ) * segment;
5493    // add targets on top
5494    for ( var i=0; i < segs; i++ ) {
5495      this._addShiftTarget( i * segment, 0, boundsSize );
5496    }
5497  } else {
5498    boundsSize = ( this.shiftPacker[ measure ] + this.gutter ) - dropItem.rect[ measure ];
5499    this._addShiftTarget( 0, 0, boundsSize );
5500  }
5501
5502  // pack each item to measure where shiftTargets are
5503  var items = this._getItemsForLayout( this.items );
5504  var packMethod = this._getPackMethod();
5505  items.forEach( function( item ) {
5506    var rect = item.rect;
5507    this._setRectSize( item.element, rect );
5508    this.shiftPacker[ packMethod ]( rect );
5509
5510    // add top left corner
5511    this._addShiftTarget( rect.x, rect.y, boundsSize );
5512    // add bottom left / top right corner
5513    var cornerX = isHorizontal ? rect.x + rect.width : rect.x;
5514    var cornerY = isHorizontal ? rect.y : rect.y + rect.height;
5515    this._addShiftTarget( cornerX, cornerY, boundsSize );
5516
5517    if ( segment ) {
5518      // add targets for each column on bottom / row on right
5519      var segSpan = Math.round( rect[ measure ] / segment );
5520      for ( var i=1; i < segSpan; i++ ) {
5521        var segX = isHorizontal ? cornerX : rect.x + segment * i;
5522        var segY = isHorizontal ? rect.y + segment * i : cornerY;
5523        this._addShiftTarget( segX, segY, boundsSize );
5524      }
5525    }
5526  }, this );
5527
5528};
5529
5530proto._addShiftTarget = function( x, y, boundsSize ) {
5531  var checkCoord = this._getOption('horizontal') ? y : x;
5532  if ( checkCoord !== 0 && checkCoord > boundsSize ) {
5533    return;
5534  }
5535  // create string for a key, easier to keep track of what targets
5536  var key = x + ',' + y;
5537  var hasKey = this.shiftTargetKeys.indexOf( key ) != -1;
5538  if ( hasKey ) {
5539    return;
5540  }
5541  this.shiftTargetKeys.push( key );
5542  this.shiftTargets.push({ x: x, y: y });
5543};
5544
5545// -------------------------- drop -------------------------- //
5546
5547proto.shift = function( item, x, y ) {
5548  var shiftPosition;
5549  var minDistance = Infinity;
5550  var position = { x: x, y: y };
5551  this.shiftTargets.forEach( function( target ) {
5552    var distance = getDistance( target, position );
5553    if ( distance < minDistance ) {
5554      shiftPosition = target;
5555      minDistance = distance;
5556    }
5557  });
5558  item.rect.x = shiftPosition.x;
5559  item.rect.y = shiftPosition.y;
5560};
5561
5562function getDistance( a, b ) {
5563  var dx = b.x - a.x;
5564  var dy = b.y - a.y;
5565  return Math.sqrt( dx * dx + dy * dy );
5566}
5567
5568// -------------------------- drag move -------------------------- //
5569
5570var DRAG_THROTTLE_TIME = 120;
5571
5572/**
5573 * handle an item drag move event
5574 * @param {Element} elem
5575 * @param {Number} x - horizontal change in position
5576 * @param {Number} y - vertical change in position
5577 */
5578proto.itemDragMove = function( elem, x, y ) {
5579  var item = this.isEnabled && this.getItem( elem );
5580  if ( !item ) {
5581    return;
5582  }
5583
5584  x -= this.size.paddingLeft;
5585  y -= this.size.paddingTop;
5586
5587  var _this = this;
5588  function onDrag() {
5589    _this.shift( item, x, y );
5590    item.positionDropPlaceholder();
5591    _this.layout();
5592  }
5593
5594  // throttle
5595  var now = new Date();
5596  if ( this._itemDragTime && now - this._itemDragTime < DRAG_THROTTLE_TIME ) {
5597    clearTimeout( this.dragTimeout );
5598    this.dragTimeout = setTimeout( onDrag, DRAG_THROTTLE_TIME );
5599  } else {
5600    onDrag();
5601    this._itemDragTime = now;
5602  }
5603};
5604
5605// -------------------------- drag end -------------------------- //
5606
5607/**
5608 * handle an item drag end event
5609 * @param {Element} elem
5610 */
5611proto.itemDragEnd = function( elem ) {
5612  var item = this.isEnabled && this.getItem( elem );
5613  if ( !item ) {
5614    return;
5615  }
5616
5617  clearTimeout( this.dragTimeout );
5618  item.element.classList.add('is-positioning-post-drag');
5619
5620  var completeCount = 0;
5621  var _this = this;
5622  function onDragEndLayoutComplete() {
5623    completeCount++;
5624    if ( completeCount != 2 ) {
5625      return;
5626    }
5627    // reset drag item
5628    item.element.classList.remove('is-positioning-post-drag');
5629    item.hideDropPlaceholder();
5630    _this.dispatchEvent( 'dragItemPositioned', null, [ item ] );
5631  }
5632
5633  item.once( 'layout', onDragEndLayoutComplete );
5634  this.once( 'layoutComplete', onDragEndLayoutComplete );
5635  item.moveTo( item.rect.x, item.rect.y );
5636  this.layout();
5637  this.dragItemCount = Math.max( 0, this.dragItemCount - 1 );
5638  this.sortItemsByPosition();
5639  item.disablePlacing();
5640  this.unstamp( item.element );
5641};
5642
5643/**
5644 * binds Draggabilly events
5645 * @param {Draggabilly} draggie
5646 */
5647proto.bindDraggabillyEvents = function( draggie ) {
5648  this._bindDraggabillyEvents( draggie, 'on' );
5649};
5650
5651proto.unbindDraggabillyEvents = function( draggie ) {
5652  this._bindDraggabillyEvents( draggie, 'off' );
5653};
5654
5655proto._bindDraggabillyEvents = function( draggie, method ) {
5656  var handlers = this.handleDraggabilly;
5657  draggie[ method ]( 'dragStart', handlers.dragStart );
5658  draggie[ method ]( 'dragMove', handlers.dragMove );
5659  draggie[ method ]( 'dragEnd', handlers.dragEnd );
5660};
5661
5662/**
5663 * binds jQuery UI Draggable events
5664 * @param {jQuery} $elems
5665 */
5666proto.bindUIDraggableEvents = function( $elems ) {
5667  this._bindUIDraggableEvents( $elems, 'on' );
5668};
5669
5670proto.unbindUIDraggableEvents = function( $elems ) {
5671  this._bindUIDraggableEvents( $elems, 'off' );
5672};
5673
5674proto._bindUIDraggableEvents = function( $elems, method ) {
5675  var handlers = this.handleUIDraggable;
5676  $elems
5677    [ method ]( 'dragstart', handlers.start )
5678    [ method ]( 'drag', handlers.drag )
5679    [ method ]( 'dragstop', handlers.stop );
5680};
5681
5682// ----- destroy ----- //
5683
5684var _destroy = proto.destroy;
5685proto.destroy = function() {
5686  _destroy.apply( this, arguments );
5687  // disable flag; prevent drag events from triggering. #72
5688  this.isEnabled = false;
5689};
5690
5691// -----  ----- //
5692
5693Packery.Rect = Rect;
5694Packery.Packer = Packer;
5695
5696return Packery;
5697
5698}));
5699
5700/*!
5701 * Packery layout mode v2.0.1
5702 * sub-classes Packery
5703 */
5704
5705/*jshint browser: true, strict: true, undef: true, unused: true */
5706
5707( function( window, factory ) {
5708
5709  // universal module definition
5710  if ( typeof define == 'function' && define.amd ) {
5711    // AMD
5712    define( [
5713        'isotope-layout/js/layout-mode',
5714        'packery/js/packery'
5715      ],
5716      factory );
5717  } else if ( typeof module == 'object' && module.exports ) {
5718    // CommonJS
5719    module.exports = factory(
5720      require('isotope-layout/js/layout-mode'),
5721      require('packery')
5722    );
5723  } else {
5724    // browser global
5725    factory(
5726      window.Isotope.LayoutMode,
5727      window.Packery
5728    );
5729  }
5730
5731}( window, function factor( LayoutMode, Packery ) {
5732
5733
5734  // create an Outlayer layout class
5735  var PackeryMode = LayoutMode.create('packery');
5736  var proto = PackeryMode.prototype;
5737
5738  var keepModeMethods = {
5739    _getElementOffset: true,
5740    _getMeasurement: true
5741  };
5742
5743  // inherit Packery prototype
5744  for ( var method in Packery.prototype ) {
5745    // do not inherit mode methods
5746    if ( !keepModeMethods[ method ] ) {
5747      proto[ method ] = Packery.prototype[ method ];
5748    }
5749  }
5750
5751  // set packer in _resetLayout
5752  var _resetLayout = proto._resetLayout;
5753  proto._resetLayout = function() {
5754    this.packer = this.packer || new Packery.Packer();
5755    this.shiftPacker = this.shiftPacker || new Packery.Packer();
5756    _resetLayout.apply( this, arguments );
5757  };
5758
5759  var _getItemLayoutPosition = proto._getItemLayoutPosition;
5760  proto._getItemLayoutPosition = function( item ) {
5761    // set packery rect
5762    item.rect = item.rect || new Packery.Rect();
5763    return _getItemLayoutPosition.call( this, item );
5764  };
5765
5766  // needsResizeLayout for vertical or horizontal
5767  var _needsResizeLayout = proto.needsResizeLayout;
5768  proto.needsResizeLayout = function() {
5769    if ( this._getOption('horizontal') ) {
5770      return this.needsVerticalResizeLayout();
5771    } else {
5772      return _needsResizeLayout.call( this );
5773    }
5774  };
5775
5776  // point to mode options for horizontal
5777  var _getOption = proto._getOption;
5778  proto._getOption = function( option ) {
5779    if ( option == 'horizontal' ) {
5780      return this.options.isHorizontal !== undefined ?
5781        this.options.isHorizontal : this.options.horizontal;
5782    }
5783    return _getOption.apply( this.isotope, arguments );
5784  };
5785
5786  return PackeryMode;
5787
5788}));
5789
5790/*!
5791 * cellsByRows layout mode for Isotope
5792 * v1.1.3
5793 * http://isotope.metafizzy.co/layout-modes/cellsbyrow.html
5794 */
5795
5796/*jshint browser: true, devel: false, strict: true, undef: true, unused: true */
5797
5798( function( window, factory ) {
5799  // universal module definition
5800  /* jshint strict: false */ /*globals define, module, require */
5801  if ( typeof define === 'function' && define.amd ) {
5802    // AMD
5803    define( [
5804        'isotope/js/layout-mode'
5805      ],
5806      factory );
5807  } else if ( typeof exports === 'object' ) {
5808    // CommonJS
5809    module.exports = factory(
5810      require('isotope-layout/js/layout-mode')
5811    );
5812  } else {
5813    // browser global
5814    factory(
5815      window.Isotope.LayoutMode
5816    );
5817  }
5818
5819}( window, function factory( LayoutMode ) {
5820  'use strict';
5821
5822  var CellsByRow = LayoutMode.create( 'cellsByRow' );
5823  var proto = CellsByRow.prototype;
5824
5825  proto._resetLayout = function() {
5826    // reset properties
5827    this.itemIndex = 0;
5828    // measurements
5829    this.getColumnWidth();
5830    this.getRowHeight();
5831    // set cols
5832    this.cols = Math.floor( this.isotope.size.innerWidth / this.columnWidth );
5833    this.cols = Math.max( this.cols, 1 );
5834  };
5835
5836  proto._getItemLayoutPosition = function( item ) {
5837    item.getSize();
5838    var col = this.itemIndex % this.cols;
5839    var row = Math.floor( this.itemIndex / this.cols );
5840    // center item within cell
5841    var x = ( col + 0.5 ) * this.columnWidth - item.size.outerWidth / 2;
5842    var y = ( row + 0.5 ) * this.rowHeight - item.size.outerHeight / 2;
5843    this.itemIndex++;
5844    return { x: x, y: y };
5845  };
5846
5847  proto._getContainerSize = function() {
5848    return {
5849      height: Math.ceil( this.itemIndex / this.cols ) * this.rowHeight
5850    };
5851  };
5852
5853  return CellsByRow;
5854
5855}));
5856/*global jQuery: true */
5857
5858/*!
5859   --------------------------------
5860   Infinite Scroll
5861   --------------------------------
5862   + https://github.com/paulirish/infinite-scroll
5863   + version 2.1.0
5864   + Copyright 2011/12 Paul Irish & Luke Shumard
5865   + Licensed under the MIT license
5866
5867   + Documentation: http://infinite-scroll.com/
5868*/
5869
5870// Uses AMD or browser globals to create a jQuery plugin.
5871(function (factory) {
5872    if (typeof define === 'function' && define.amd) {
5873        // AMD. Register as an anonymous module.
5874        define(['jquery'], factory);
5875    } else {
5876        // Browser globals
5877        factory(jQuery);
5878    }
5879}(function ($, undefined) {
5880    'use strict';
5881
5882    $.infinitescroll = function infscr(options, callback, element) {
5883        this.element = $(element);
5884
5885        // Flag the object in the event of a failed creation
5886        if (!this._create(options, callback)) {
5887            this.failed = true;
5888        }
5889    };
5890
5891    $.infinitescroll.defaults = {
5892        loading: {
5893            finished: undefined,
5894            finishedMsg: "<em>Congratulations, you've reached the end of the internet.</em>",
5895            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
5895MzExnbRvQ2Sy7vN0zvVtNfU2tLY3rPgLdnDvca4VQS/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
5895iqpQpZ4apwSwRtjqrB3muoF9BboaXKmshlqWqsWiGt2wphJkQbAU5hoCACH5BAUKABsALAcABADOAAsAAAX/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',
5896            msg: null,
5897            msgText: '<em>Loading the next set of posts...</em>',
5898            selector: null,
5899            speed: 'fast',
5900            start: undefined
5901        },
5902        state: {
5903            isDuringAjax: false,
5904            isInvalidPage: false,
5905            isDestroyed: false,
5906            isDone: false, // For when it goes all the way through the archive.
5907            isPaused: false,
5908            isBeyondMaxPage: false,
5909            currPage: 1
5910        },
5911        debug: false,
5912        behavior: undefined,
5913        binder: $(window), // used to cache the selector
5914        nextSelector: 'div.navigation a:first',
5915        navSelector: 'div.navigation',
5916        contentSelector: null, // rename to pageFragment
5917        extraScrollPx: 150,
5918        itemSelector: 'div.post',
5919        animate: false,
5920        pathParse: undefined,
5921        dataType: 'html',
5922        appendCallback: true,
5923        bufferPx: 40,
5924        errorCallback: function () { },
5925        infid: 0, //Instance ID
5926        pixelsFromNavToBottom: undefined,
5927        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
5928        prefill: false, // When the document is smaller than the window, load data until the document is larger or links are exhausted
5929        maxPage: undefined // to manually control maximum page (when maxPage is undefined, maximum page limitation is not work)
5930    };
5931
5932    $.infinitescroll.prototype = {
5933
5934        /*
5935            ----------------------------
5936            Private methods
5937            ----------------------------
5938            */
5939
5940        // Bind or unbind from scroll
5941        _binding: function infscr_binding(binding) {
5942
5943            var instance = this,
5944            opts = instance.options;
5945
5946            opts.v = '2.0b2.120520';
5947
5948            // if behavior is defined and this function is extended, call that instead of default
5949            if (!!opts.behavior && this['_binding_'+opts.behavior] !== undefined) {
5950                this['_binding_'+opts.behavior].call(this);
5951                return;
5952            }
5953
5954            if (binding !== 'bind' && binding !== 'unbind') {
5955                this._debug('Binding value  ' + binding + ' not valid');
5956                return false;
5957            }
5958
5959            if (binding === 'unbind') {
5960                (this.options.binder).unbind('smartscroll.infscr.' + instance.options.infid);
5961            } else {
5962                (this.options.binder)[binding]('smartscroll.infscr.' + instance.options.infid, function () {
5963                    instance.scroll();
5964                });
5965            }
5966
5967            this._debug('Binding', binding);
5968        },
5969
5970        // Fundamental aspects of the plugin are initialized
5971        _create: function infscr_create(options, callback) {
5972
5973            // Add custom options to defaults
5974            var opts = $.extend(true, {}, $.infinitescroll.defaults, options);
5975            this.options = opts;
5976            var $window = $(window);
5977            var instance = this;
5978
5979            // Validate selectors
5980            if (!instance._validate(options)) {
5981                return false;
5982            }
5983
5984            // Validate page fragment path
5985            var path = $(opts.nextSelector).attr('href');
5986            if (!path) {
5987                this._debug('Navigation selector not found');
5988                return false;
5989            }
5990
5991            // Set the path to be a relative URL from root.
5992            opts.path = opts.path || this._determinepath(path);
5993
5994            // contentSelector is 'page fragment' option for .load() / .ajax() calls
5995            opts.contentSelector = opts.contentSelector || this.element;
5996
5997            // loading.selector - if we want to place the load message in a specific selector, defaulted to the contentSelector
5998            opts.loading.selector = opts.loading.selector || opts.contentSelector;
5999
6000            // Define loading.msg
6001            opts.loading.msg = opts.loading.msg || $('<div id="infscr-loading"><img alt="Loading..." src="' + opts.loading.img + '" /><div>' + opts.loading.msgText + '</div></div>');
6002
6003            // Preload loading.img
6004            (new Image()).src = opts.loading.img;
6005
6006            // distance from nav links to bottom
6007            // computed as: height of the document + top offset of container - top offset of nav link
6008            if(opts.pixelsFromNavToBottom === undefined) {
6009                opts.pixelsFromNavToBottom = $(document).height() - $(opts.navSelector).offset().top;
6010                this._debug('pixelsFromNavToBottom: ' + opts.pixelsFromNavToBottom);
6011            }
6012
6013            var self = this;
6014
6015            // determine loading.start actions
6016            opts.loading.start = opts.loading.start || function() {
6017                $(opts.navSelector).hide();
6018                opts.loading.msg
6019                .appendTo(opts.loading.selector)
6020                .show(opts.loading.speed, $.proxy(function() {
6021                    this.beginAjax(opts);
6022                }, self));
6023            };
6024
6025            // determine loading.finished actions
6026            opts.loading.finished = opts.loading.finished || function() {
6027                if (!opts.state.isBeyondMaxPage)
6028                    opts.loading.msg.fadeOut(opts.loading.speed);
6029            };
6030
6031            // callback loading
6032            opts.callback = function(instance, data, url) {
6033                if (!!opts.behavior && instance['_callback_'+opts.behavior] !== undefined) {
6034                    instance['_callback_'+opts.behavior].call($(opts.contentSelector)[0], data, url);
6035                }
6036
6037                if (callback) {
6038                    callback.call($(opts.contentSelector)[0], data, opts, url);
6039                }
6040
6041                if (opts.prefill) {
6042                    $window.bind('resize.infinite-scroll', instance._prefill);
6043                }
6044            };
6045
6046            if (options.debug) {
6047                // Tell IE9 to use its built-in console
6048                if (Function.prototype.bind && (typeof console === 'object' || typeof console === 'function') && typeof console.log === 'object') {
6049                    ['log','info','warn','error','assert','dir','clear','profile','profileEnd']
6050                        .forEach(function (method) {
6051                            console[method] = this.call(console[method], console);
6052                        }, Function.prototype.bind);
6053                }
6054            }
6055
6056            this._setup();
6057
6058            // Setups the prefill method for use
6059            if (opts.prefill) {
6060                this._prefill();
6061            }
6062
6063            // Return true to indicate successful creation
6064            return true;
6065        },
6066
6067        _prefill: function infscr_prefill() {
6068            var instance = this;
6069            var $window = $(window);
6070
6071            function needsPrefill() {
6072                return ( $(instance.options.contentSelector).height() <= $window.height() );
6073            }
6074
6075            this._prefill = function() {
6076                if (needsPrefill()) {
6077                    instance.scroll();
6078                }
6079
6080                $window.bind('resize.infinite-scroll', function() {
6081                    if (needsPrefill()) {
6082                        $window.unbind('resize.infinite-scroll');
6083                        instance.scroll();
6084                    }
6085                });
6086            };
6087
6088            // Call self after setting up the new function
6089            this._prefill();
6090        },
6091
6092        // Console log wrapper
6093        _debug: function infscr_debug() {
6094            if (true !== this.options.debug) {
6095                return;
6096            }
6097
6098            if (typeof console !== 'undefined' && typeof console.log === 'function') {
6099                // Modern browsers
6100                // Single argument, which is a string
6101                if ((Array.prototype.slice.call(arguments)).length === 1 && typeof Array.prototype.slice.call(arguments)[0] === 'string') {
6102                    console.log( (Array.prototype.slice.call(arguments)).toString() );
6103                } else {
6104                    console.log( Array.prototype.slice.call(arguments) );
6105                }
6106            } else if (!Function.prototype.bind && typeof console !== 'undefined' && typeof console.log === 'object') {
6107                // IE8
6108                Function.prototype.call.call(console.log, console, Array.prototype.slice.call(arguments));
6109            }
6110        },
6111
6112        // find the number to increment in the path.
6113        _determinepath: function infscr_determinepath(path) {
6114
6115            var opts = this.options;
6116
6117            // if behavior is defined and this function is extended, call that instead of default
6118            if (!!opts.behavior && this['_determinepath_'+opts.behavior] !== undefined) {
6119                return this['_determinepath_'+opts.behavior].call(this,path);
6120            }
6121
6122            if (!!opts.pathParse) {
6123
6124                this._debug('pathParse manual');
6125                return opts.pathParse(path, this.options.state.currPage+1);
6126
6127            } else if (path.match(/^(.*?)\b2\b(.*?$)/)) {
6128                path = path.match(/^(.*?)\b2\b(.*?$)/).slice(1);
6129
6130                // if there is any 2 in the url at all.
6131            } else if (path.match(/^(.*?)2(.*?$)/)) {
6132
6133                // page= is used in django:
6134                // http://www.infinite-scroll.com/changelog/comment-page-1/#comment-127
6135                if (path.match(/^(.*?page=)2(\/.*|$)/)) {
6136                    path = path.match(/^(.*?page=)2(\/.*|$)/).slice(1);
6137                    return path;
6138                }
6139
6140                path = path.match(/^(.*?)2(.*?$)/).slice(1);
6141
6142            } else {
6143
6144                // page= is used in drupal too but second page is page=1 not page=2:
6145                // thx Jerod Fritz, vladikoff
6146                if (path.match(/^(.*?page=)1(\/.*|$)/)) {
6147                    path = path.match(/^(.*?page=)1(\/.*|$)/).slice(1);
6148                    return path;
6149                } else {
6150                    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.");
6151                    // Get rid of isInvalidPage to allow permalink to state
6152                    opts.state.isInvalidPage = true;  //prevent it from running on this page.
6153                }
6154            }
6155            this._debug('determinePath', path);
6156            return path;
6157
6158        },
6159
6160        // Custom error
6161        _error: function infscr_error(xhr) {
6162
6163            var opts = this.options;
6164
6165            // if behavior is defined and this function is extended, call that instead of default
6166            if (!!opts.behavior && this['_error_'+opts.behavior] !== undefined) {
6167                this['_error_'+opts.behavior].call(this,xhr);
6168                return;
6169            }
6170
6171            if (xhr !== 'destroy' && xhr !== 'end') {
6172                xhr = 'unknown';
6173            }
6174
6175            this._debug('Error', xhr);
6176
6177            if (xhr === 'end' || opts.state.isBeyondMaxPage) {
6178                this._showdonemsg();
6179            }
6180
6181            opts.state.isDone = true;
6182            opts.state.currPage = 1; // if you need to go back to this instance
6183            opts.state.isPaused = false;
6184            opts.state.isBeyondMaxPage = false;
6185            this._binding('unbind');
6186
6187        },
6188
6189        // Load Callback
6190        _loadcallback: function infscr_loadcallback(box, data, url) {
6191            var opts = this.options,
6192            callback = this.options.callback, // GLOBAL OBJECT FOR CALLBACK
6193            result = (opts.state.isDone) ? 'done' : (!opts.appendCallback) ? 'no-append' : 'append',
6194            frag;
6195
6196            // if behavior is defined and this function is extended, call that instead of default
6197            if (!!opts.behavior && this['_loadcallback_'+opts.behavior] !== undefined) {
6198                this['_loadcallback_'+opts.behavior].call(this,box,data);
6199                return;
6200            }
6201
6202            switch (result) {
6203                case 'done':
6204                    this._showdonemsg();
6205                    return false;
6206
6207                case 'no-append':
6208                    if (opts.dataType === 'html') {
6209                        data = '<div>' + data + '</div>';
6210                        data = $(data).find(opts.itemSelector);
6211                    }
6212
6213                    // if it didn't return anything
6214                    if (data.length === 0) {
6215                        return this._error('end');
6216                    }
6217
6218                    break;
6219
6220                case 'append':
6221                    var children = box.children();
6222                    // if it didn't return anything
6223                    if (children.length === 0) {
6224                        return this._error('end');
6225                    }
6226
6227                    // use a documentFragment because it works when content is going into a table or UL
6228                    frag = document.createDocumentFragment();
6229                    while (box[0].firstChild) {
6230                        frag.appendChild(box[0].firstChild);
6231                    }
6232
6233                    this._debug('contentSelector', $(opts.contentSelector)[0]);
6234                    $(opts.contentSelector)[0].appendChild(frag);
6235                    // previously, we would pass in the new DOM element as context for the callback
6236                    // however we're now using a documentfragment, which doesn't have parents or children,
6237                    // so the context is the contentContainer guy, and we pass in an array
6238                    // of the elements collected as the first argument.
6239
6240                    data = children.get();
6241                    break;
6242            }
6243
6244            // loadingEnd function
6245            opts.loading.finished.call($(opts.contentSelector)[0],opts);
6246
6247            // smooth scroll to ease in the new content
6248            if (opts.animate) {
6249                var scrollTo = $(window).scrollTop() + $(opts.loading.msg).height() + opts.extraScrollPx + 'px';
6250                $('html,body').animate({ scrollTop: scrollTo }, 800, function () { opts.state.isDuringAjax = false; });
6251            }
6252
6253            if (!opts.animate) {
6254                // once the call is done, we can allow it again.
6255                opts.state.isDuringAjax = false;
6256            }
6257
6258            callback(this, data, url);
6259
6260            if (opts.prefill) {
6261                this._prefill();
6262            }
6263        },
6264
6265        _nearbottom: function infscr_nearbottom() {
6266
6267            var opts = this.options,
6268            pixelsFromWindowBottomToBottom = 0 + $(document).height() - (opts.binder.scrollTop()) - $(window).height();
6269
6270            // if behavior is defined and this function is extended, call that instead of default
6271            if (!!opts.behavior && this['_nearbottom_'+opts.behavior] !== undefined) {
6272                return this['_nearbottom_'+opts.behavior].call(this);
6273            }
6274
6275            this._debug('math:', pixelsFromWindowBottomToBottom, opts.pixelsFromNavToBottom);
6276
6277            // if distance remaining in the scroll (including buffer) is less than the orignal nav to bottom....
6278            return (pixelsFromWindowBottomToBottom - opts.bufferPx < opts.pixelsFromNavToBottom);
6279
6280        },
6281
6282        // Pause / temporarily disable plugin from firing
6283        _pausing: function infscr_pausing(pause) {
6284
6285            var opts = this.options;
6286
6287            // if behavior is defined and this function is extended, call that instead of default
6288            if (!!opts.behavior && this['_pausing_'+opts.behavior] !== undefined) {
6289                this['_pausing_'+opts.behavior].call(this,pause);
6290                return;
6291            }
6292
6293            // If pause is not 'pause' or 'resume', toggle it's value
6294            if (pause !== 'pause' && pause !== 'resume' && pause !== null) {
6295                this._debug('Invalid argument. Toggling pause value instead');
6296            }
6297
6298            pause = (pause && (pause === 'pause' || pause === 'resume')) ? pause : 'toggle';
6299
6300            switch (pause) {
6301                case 'pause':
6302                    opts.state.isPaused = true;
6303                break;
6304
6305                case 'resume':
6306                    opts.state.isPaused = false;
6307                break;
6308
6309                case 'toggle':
6310                    opts.state.isPaused = !opts.state.isPaused;
6311                break;
6312            }
6313
6314            this._debug('Paused', opts.state.isPaused);
6315            return false;
6316
6317        },
6318
6319        // Behavior is determined
6320        // If the behavior option is undefined, it will set to default and bind to scroll
6321        _setup: function infscr_setup() {
6322
6323            var opts = this.options;
6324
6325            // if behavior is defined and this function is extended, call that instead of default
6326            if (!!opts.behavior && this['_setup_'+opts.behavior] !== undefined) {
6327                this['_setup_'+opts.behavior].call(this);
6328                return;
6329            }
6330
6331            this._binding('bind');
6332
6333            return false;
6334
6335        },
6336
6337        // Show done message
6338        _showdonemsg: function infscr_showdonemsg() {
6339
6340            var opts = this.options;
6341
6342            // if behavior is defined and this function is extended, call that instead of default
6343            if (!!opts.behavior && this['_showdonemsg_'+opts.behavior] !== undefined) {
6344                this['_showdonemsg_'+opts.behavior].call(this);
6345                return;
6346            }
6347
6348            opts.loading.msg
6349            .find('img')
6350            .hide()
6351            .parent()
6352            .find('div').html(opts.loading.finishedMsg).animate({ opacity: 1 }, 2000, function () {
6353                $(this).parent().fadeOut(opts.loading.speed);
6354            });
6355
6356            // user provided callback when done
6357            opts.errorCallback.call($(opts.contentSelector)[0],'done');
6358        },
6359
6360        // grab each selector option and see if any fail
6361        _validate: function infscr_validate(opts) {
6362            for (var key in opts) {
6363                if (key.indexOf && key.indexOf('Selector') > -1 && $(opts[key]).length === 0) {
6364                    this._debug('Your ' + key + ' found no elements.');
6365                    return false;
6366                }
6367            }
6368
6369            return true;
6370        },
6371
6372        /*
6373            ----------------------------
6374            Public methods
6375            ----------------------------
6376            */
6377
6378        // Bind to scroll
6379        bind: function infscr_bind() {
6380            this._binding('bind');
6381        },
6382
6383        // Destroy current instance of plugin
6384        destroy: function infscr_destroy() {
6385            this.options.state.isDestroyed = true;
6386            this.options.loading.finished();
6387            return this._error('destroy');
6388        },
6389
6390        // Set pause value to false
6391        pause: function infscr_pause() {
6392            this._pausing('pause');
6393        },
6394
6395        // Set pause value to false
6396        resume: function infscr_resume() {
6397            this._pausing('resume');
6398        },
6399
6400        beginAjax: function infscr_ajax(opts) {
6401            var instance = this,
6402                path = opts.path,
6403                box, desturl, method, condition;
6404
6405            // increment the URL bit. e.g. /page/3/
6406            opts.state.currPage++;
6407
6408            // Manually control maximum page
6409            if ( opts.maxPage !== undefined && opts.state.currPage > opts.maxPage ){
6410                opts.state.isBeyondMaxPage = true;
6411                this.destroy();
6412                return;
6413            }
6414
6415            // if we're dealing with a table we can't use DIVs
6416            box = $(opts.contentSelector).is('table, tbody') ? $('<tbody/>') : $('<div/>');
6417
6418            desturl = (typeof path === 'function') ? path(opts.state.currPage) : path.join(opts.state.currPage);
6419            instance._debug('heading into ajax', desturl);
6420
6421            method = (opts.dataType === 'html' || opts.dataType === 'json' ) ? opts.dataType : 'html+callback';
6422            if (opts.appendCallback && opts.dataType === 'html') {
6423                method += '+callback';
6424            }
6425
6426            switch (method) {
6427                case 'html+callback':
6428                    instance._debug('Using HTML via .load() method');
6429                    box.load(desturl + ' ' + opts.itemSelector, undefined, function infscr_ajax_callback(responseText) {
6430                        instance._loadcallback(box, responseText, desturl);
6431                    });
6432
6433                    break;
6434
6435                case 'html':
6436                    instance._debug('Using ' + (method.toUpperCase()) + ' via $.ajax() method');
6437                    $.ajax({
6438                        // params
6439                        url: desturl,
6440                        dataType: opts.dataType,
6441                        complete: function infscr_ajax_callback(jqXHR, textStatus) {
6442                            condition = (typeof (jqXHR.isResolved) !== 'undefined') ? (jqXHR.isResolved()) : (textStatus === 'success' || textStatus === 'notmodified');
6443                            if (condition) {
6444                                instance._loadcallback(box, jqXHR.responseText, desturl);
6445                            } else {
6446                                instance._error('end');
6447                            }
6448                        }
6449                    });
6450
6451                    break;
6452                case 'json':
6453                    instance._debug('Using ' + (method.toUpperCase()) + ' via $.ajax() method');
6454                    $.ajax({
6455                        dataType: 'json',
6456                        type: 'GET',
6457                        url: desturl,
6458                        success: function (data, textStatus, jqXHR) {
6459                            condition = (typeof (jqXHR.isResolved) !== 'undefined') ? (jqXHR.isResolved()) : (textStatus === 'success' || textStatus === 'notmodified');
6460                            if (opts.appendCallback) {
6461                                // if appendCallback is true, you must defined template in options.
6462                                // note that data passed into _loadcallback is already an html (after processed in opts.template(data)).
6463                                if (opts.template !== undefined) {
6464                                    var theData = opts.template(data);
6465                                    box.append(theData);
6466                                    if (condition) {
6467                                        instance._loadcallback(box, theData);
6468                                    } else {
6469                                        instance._error('end');
6470                                    }
6471                                } else {
6472                                    instance._debug('template must be defined.');
6473                                    instance._error('end');
6474                                }
6475                            } else {
6476                                // if appendCallback is false, we will pass in the JSON object. you should handle it yourself in your callback.
6477                                if (condition) {
6478                                    instance._loadcallback(box, data, desturl);
6479                                } else {
6480                                    instance._error('end');
6481                                }
6482                            }
6483                        },
6484                        error: function() {
6485                            instance._debug('JSON ajax request failed.');
6486                            instance._error('end');
6487                        }
6488                    });
6489
6490                    break;
6491            }
6492        },
6493
6494        // Retrieve next set of content items
6495        retrieve: function infscr_retrieve(pageNum) {
6496            pageNum = pageNum || null;
6497
6498            var instance = this,
6499            opts = instance.options;
6500
6501            // if behavior is defined and this function is extended, call that instead of default
6502            if (!!opts.behavior && this['retrieve_'+opts.behavior] !== undefined) {
6503                this['retrieve_'+opts.behavior].call(this,pageNum);
6504                return;
6505            }
6506
6507            // for manual triggers, if destroyed, get out of here
6508            if (opts.state.isDestroyed) {
6509                this._debug('Instance is destroyed');
6510                return false;
6511            }
6512
6513            // we dont want to fire the ajax multiple times
6514            opts.state.isDuringAjax = true;
6515
6516            opts.loading.start.call($(opts.contentSelector)[0],opts);
6517        },
6518
6519        // Check to see next page is needed
6520        scroll: function infscr_scroll() {
6521
6522            var opts = this.options,
6523            state = opts.state;
6524
6525            // if behavior is defined and this function is extended, call that instead of default
6526            if (!!opts.behavior && this['scroll_'+opts.behavior] !== undefined) {
6527                this['scroll_'+opts.behavior].call(this);
6528                return;
6529            }
6530
6531            if (state.isDuringAjax || state.isInvalidPage || state.isDone || state.isDestroyed || state.isPaused) {
6532                return;
6533            }
6534
6535            if (!this._nearbottom()) {
6536                return;
6537            }
6538
6539            this.retrieve();
6540
6541        },
6542
6543        // Toggle pause value
6544        toggle: function infscr_toggle() {
6545            this._pausing();
6546        },
6547
6548        // Unbind from scroll
6549        unbind: function infscr_unbind() {
6550            this._binding('unbind');
6551        },
6552
6553        // update options
6554        update: function infscr_options(key) {
6555            if ($.isPlainObject(key)) {
6556                this.options = $.extend(true,this.options,key);
6557            }
6558        }
6559    };
6560
6561
6562    /*
6563        ----------------------------
6564        Infinite Scroll function
6565        ----------------------------
6566
6567        Borrowed logic from the following...
6568
6569        jQuery UI
6570        - https://github.com/jquery/jquery-ui/blob/master/ui/jquery.ui.widget.js
6571
6572        jCarousel
6573        - https://github.com/jsor/jcarousel/blob/master/lib/jquery.jcarousel.js
6574
6575        Masonry
6576        - https://github.com/desandro/masonry/blob/master/jquery.masonry.js
6577
6578*/
6579
6580    $.fn.infinitescroll = function infscr_init(options, callback) {
6581
6582
6583        var thisCall = typeof options;
6584
6585        switch (thisCall) {
6586
6587            // method
6588            case 'string':
6589                var args = Array.prototype.slice.call(arguments, 1);
6590
6591                this.each(function () {
6592                    var instance = $.data(this, 'infinitescroll');
6593
6594                    if (!instance) {
6595                        // not setup yet
6596                        // return $.error('Method ' + options + ' cannot be called until Infinite Scroll is setup');
6597                        return false;
6598                    }
6599
6600                    if (!$.isFunction(instance[options]) || options.charAt(0) === '_') {
6601                        // return $.error('No such method ' + options + ' for Infinite Scroll');
6602                        return false;
6603                    }
6604
6605                    // no errors!
6606                    instance[options].apply(instance, args);
6607                });
6608
6609            break;
6610
6611            // creation
6612            case 'object':
6613
6614                this.each(function () {
6615
6616                var instance = $.data(this, 'infinitescroll');
6617
6618                if (instance) {
6619
6620                    // update options of current instance
6621                    instance.update(options);
6622
6623                } else {
6624
6625                    // initialize new instance
6626                    instance = new $.infinitescroll(options, callback, this);
6627
6628                    // don't attach if instantiation failed
6629                    if (!instance.failed) {
6630                        $.data(this, 'infinitescroll', instance);
6631                    }
6632
6633                }
6634
6635            });
6636
6637            break;
6638
6639        }
6640
6641        return this;
6642    };
6643
6644
6645
6646    /*
6647     * smartscroll: debounced scroll event for jQuery *
6648     * https://github.com/lukeshumard/smartscroll
6649     * Based on smartresize by @louis_remi: https://github.com/lrbabe/jquery.smartresize.js *
6650     * Copyright 2011 Louis-Remi & Luke Shumard * Licensed under the MIT license. *
6651     */
6652
6653    var event = $.event,
6654    scrollTimeout;
6655
6656    event.special.smartscroll = {
6657        setup: function () {
6658            $(this).bind('scroll', event.special.smartscroll.handler);
6659        },
6660        teardown: function () {
6661            $(this).unbind('scroll', event.special.smartscroll.handler);
6662        },
6663        handler: function (event, execAsap) {
6664            // Save the context
6665            var context = this,
6666            args = arguments;
6667
6668            // set correct event type
6669            event.type = 'smartscroll';
6670
6671            if (scrollTimeout) { clearTimeout(scrollTimeout); }
6672            scrollTimeout = setTimeout(function () {
6673                $(context).trigger('smartscroll', args);
6674            }, execAsap === 'execAsap' ? 0 : 100);
6675        }
6676    };
6677
6678    $.fn.smartscroll = function (fn) {
6679        return fn ? this.bind('smartscroll', fn) : this.trigger('smartscroll', ['execAsap']);
6680    };
6681
6682}));
6683
6684/*!
6685Waypoints - 4.0.1
6686Copyright © 2011-2016 Caleb Troughton
6687Licensed under the MIT license.
6688https://github.com/imakewebthings/waypoints/blob/master/licenses.txt
6689*/
6690(function() {
6691  'use strict'
6692
6693  var keyCounter = 0
6694  var allWaypoints = {}
6695
6696  /* http://imakewebthings.com/waypoints/api/waypoint */
6697  function Waypoint(options) {
6698    if (!options) {
6699      throw new Error('No options passed to Waypoint constructor')
6700    }
6701    if (!options.element) {
6702      throw new Error('No element option passed to Waypoint constructor')
6703    }
6704    if (!options.handler) {
6705      throw new Error('No handler option passed to Waypoint constructor')
6706    }
6707
6708    this.key = 'waypoint-' + keyCounter
6709    this.options = Waypoint.Adapter.extend({}, Waypoint.defaults, options)
6710    this.element = this.options.element
6711    this.adapter = new Waypoint.Adapter(this.element)
6712    this.callback = options.handler
6713    this.axis = this.options.horizontal ? 'horizontal' : 'vertical'
6714    this.enabled = this.options.enabled
6715    this.triggerPoint = null
6716    this.group = Waypoint.Group.findOrCreate({
6717      name: this.options.group,
6718      axis: this.axis
6719    })
6720    this.context = Waypoint.Context.findOrCreateByElement(this.options.context)
6721
6722    if (Waypoint.offsetAliases[this.options.offset]) {
6723      this.options.offset = Waypoint.offsetAliases[this.options.offset]
6724    }
6725    this.group.add(this)
6726    this.context.add(this)
6727    allWaypoints[this.key] = this
6728    keyCounter += 1
6729  }
6730
6731  /* Private */
6732  Waypoint.prototype.queueTrigger = function(direction) {
6733    this.group.queueTrigger(this, direction)
6734  }
6735
6736  /* Private */
6737  Waypoint.prototype.trigger = function(args) {
6738    if (!this.enabled) {
6739      return
6740    }
6741    if (this.callback) {
6742      this.callback.apply(this, args)
6743    }
6744  }
6745
6746  /* Public */
6747  /* http://imakewebthings.com/waypoints/api/destroy */
6748  Waypoint.prototype.destroy = function() {
6749    this.context.remove(this)
6750    this.group.remove(this)
6751    delete allWaypoints[this.key]
6752  }
6753
6754  /* Public */
6755  /* http://imakewebthings.com/waypoints/api/disable */
6756  Waypoint.prototype.disable = function() {
6757    this.enabled = false
6758    return this
6759  }
6760
6761  /* Public */
6762  /* http://imakewebthings.com/waypoints/api/enable */
6763  Waypoint.prototype.enable = function() {
6764    this.context.refresh()
6765    this.enabled = true
6766    return this
6767  }
6768
6769  /* Public */
6770  /* http://imakewebthings.com/waypoints/api/next */
6771  Waypoint.prototype.next = function() {
6772    return this.group.next(this)
6773  }
6774
6775  /* Public */
6776  /* http://imakewebthings.com/waypoints/api/previous */
6777  Waypoint.prototype.previous = function() {
6778    return this.group.previous(this)
6779  }
6780
6781  /* Private */
6782  Waypoint.invokeAll = function(method) {
6783    var allWaypointsArray = []
6784    for (var waypointKey in allWaypoints) {
6785      allWaypointsArray.push(allWaypoints[waypointKey])
6786    }
6787    for (var i = 0, end = allWaypointsArray.length; i < end; i++) {
6788      allWaypointsArray[i][method]()
6789    }
6790  }
6791
6792  /* Public */
6793  /* http://imakewebthings.com/waypoints/api/destroy-all */
6794  Waypoint.destroyAll = function() {
6795    Waypoint.invokeAll('destroy')
6796  }
6797
6798  /* Public */
6799  /* http://imakewebthings.com/waypoints/api/disable-all */
6800  Waypoint.disableAll = function() {
6801    Waypoint.invokeAll('disable')
6802  }
6803
6804  /* Public */
6805  /* http://imakewebthings.com/waypoints/api/enable-all */
6806  Waypoint.enableAll = function() {
6807    Waypoint.Context.refreshAll()
6808    for (var waypointKey in allWaypoints) {
6809      allWaypoints[waypointKey].enabled = true
6810    }
6811    return this
6812  }
6813
6814  /* Public */
6815  /* http://imakewebthings.com/waypoints/api/refresh-all */
6816  Waypoint.refreshAll = function() {
6817    Waypoint.Context.refreshAll()
6818  }
6819
6820  /* Public */
6821  /* http://imakewebthings.com/waypoints/api/viewport-height */
6822  Waypoint.viewportHeight = function() {
6823    return window.innerHeight || document.documentElement.clientHeight
6824  }
6825
6826  /* Public */
6827  /* http://imakewebthings.com/waypoints/api/viewport-width */
6828  Waypoint.viewportWidth = function() {
6829    return document.documentElement.clientWidth
6830  }
6831
6832  Waypoint.adapters = []
6833
6834  Waypoint.defaults = {
6835    context: window,
6836    continuous: true,
6837    enabled: true,
6838    group: 'default',
6839    horizontal: false,
6840    offset: 0
6841  }
6842
6843  Waypoint.offsetAliases = {
6844    'bottom-in-view': function() {
6845      return this.context.innerHeight() - this.adapter.outerHeight()
6846    },
6847    'right-in-view': function() {
6848      return this.context.innerWidth() - this.adapter.outerWidth()
6849    }
6850  }
6851
6852  window.Waypoint = Waypoint
6853}())
6854;(function() {
6855  'use strict'
6856
6857  function requestAnimationFrameShim(callback) {
6858    window.setTimeout(callback, 1000 / 60)
6859  }
6860
6861  var keyCounter = 0
6862  var contexts = {}
6863  var Waypoint = window.Waypoint
6864  var oldWindowLoad = window.onload
6865
6866  /* http://imakewebthings.com/waypoints/api/context */
6867  function Context(element) {
6868    this.element = element
6869    this.Adapter = Waypoint.Adapter
6870    this.adapter = new this.Adapter(element)
6871    this.key = 'waypoint-context-' + keyCounter
6872    this.didScroll = false
6873    this.didResize = false
6874    this.oldScroll = {
6875      x: this.adapter.scrollLeft(),
6876      y: this.adapter.scrollTop()
6877    }
6878    this.waypoints = {
6879      vertical: {},
6880      horizontal: {}
6881    }
6882
6883    element.waypointContextKey = this.key
6884    contexts[element.waypointContextKey] = this
6885    keyCounter += 1
6886    if (!Waypoint.windowContext) {
6887      Waypoint.windowContext = true
6888      Waypoint.windowContext = new Context(window)
6889    }
6890
6891    this.createThrottledScrollHandler()
6892    this.createThrottledResizeHandler()
6893  }
6894
6895  /* Private */
6896  Context.prototype.add = function(waypoint) {
6897    var axis = waypoint.options.horizontal ? 'horizontal' : 'vertical'
6898    this.waypoints[axis][waypoint.key] = waypoint
6899    this.refresh()
6900  }
6901
6902  /* Private */
6903  Context.prototype.checkEmpty = function() {
6904    var horizontalEmpty = this.Adapter.isEmptyObject(this.waypoints.horizontal)
6905    var verticalEmpty = this.Adapter.isEmptyObject(this.waypoints.vertical)
6906    var isWindow = this.element == this.element.window
6907    if (horizontalEmpty && verticalEmpty && !isWindow) {
6908      this.adapter.off('.waypoints')
6909      delete contexts[this.key]
6910    }
6911  }
6912
6913  /* Private */
6914  Context.prototype.createThrottledResizeHandler = function() {
6915    var self = this
6916
6917    function resizeHandler() {
6918      self.handleResize()
6919      self.didResize = false
6920    }
6921
6922    this.adapter.on('resize.waypoints', function() {
6923      if (!self.didResize) {
6924        self.didResize = true
6925        Waypoint.requestAnimationFrame(resizeHandler)
6926      }
6927    })
6928  }
6929
6930  /* Private */
6931  Context.prototype.createThrottledScrollHandler = function() {
6932    var self = this
6933    function scrollHandler() {
6934      self.handleScroll()
6935      self.didScroll = false
6936    }
6937
6938    this.adapter.on('scroll.waypoints', function() {
6939      if (!self.didScroll || Waypoint.isTouch) {
6940        self.didScroll = true
6941        Waypoint.requestAnimationFrame(scrollHandler)
6942      }
6943    })
6944  }
6945
6946  /* Private */
6947  Context.prototype.handleResize = function() {
6948    Waypoint.Context.refreshAll()
6949  }
6950
6951  /* Private */
6952  Context.prototype.handleScroll = function() {
6953    var triggeredGroups = {}
6954    var axes = {
6955      horizontal: {
6956        newScroll: this.adapter.scrollLeft(),
6957        oldScroll: this.oldScroll.x,
6958        forward: 'right',
6959        backward: 'left'
6960      },
6961      vertical: {
6962        newScroll: this.adapter.scrollTop(),
6963        oldScroll: this.oldScroll.y,
6964        forward: 'down',
6965        backward: 'up'
6966      }
6967    }
6968
6969    for (var axisKey in axes) {
6970      var axis = axes[axisKey]
6971      var isForward = axis.newScroll > axis.oldScroll
6972      var direction = isForward ? axis.forward : axis.backward
6973
6974      for (var waypointKey in this.waypoints[axisKey]) {
6975        var waypoint = this.waypoints[axisKey][waypointKey]
6976        if (waypoint.triggerPoint === null) {
6977          continue
6978        }
6979        var wasBeforeTriggerPoint = axis.oldScroll < waypoint.triggerPoint
6980        var nowAfterTriggerPoint = axis.newScroll >= waypoint.triggerPoint
6981        var crossedForward = wasBeforeTriggerPoint && nowAfterTriggerPoint
6982        var crossedBackward = !wasBeforeTriggerPoint && !nowAfterTriggerPoint
6983        if (crossedForward || crossedBackward) {
6984          waypoint.queueTrigger(direction)
6985          triggeredGroups[waypoint.group.id] = waypoint.group
6986        }
6987      }
6988    }
6989
6990    for (var groupKey in triggeredGroups) {
6991      triggeredGroups[groupKey].flushTriggers()
6992    }
6993
6994    this.oldScroll = {
6995      x: axes.horizontal.newScroll,
6996      y: axes.vertical.newScroll
6997    }
6998  }
6999
7000  /* Private */
7001  Context.prototype.innerHeight = function() {
7002    /*eslint-disable eqeqeq */
7003    if (this.element == this.element.window) {
7004      return Waypoint.viewportHeight()
7005    }
7006    /*eslint-enable eqeqeq */
7007    return this.adapter.innerHeight()
7008  }
7009
7010  /* Private */
7011  Context.prototype.remove = function(waypoint) {
7012    delete this.waypoints[waypoint.axis][waypoint.key]
7013    this.checkEmpty()
7014  }
7015
7016  /* Private */
7017  Context.prototype.innerWidth = function() {
7018    /*eslint-disable eqeqeq */
7019    if (this.element == this.element.window) {
7020      return Waypoint.viewportWidth()
7021    }
7022    /*eslint-enable eqeqeq */
7023    return this.adapter.innerWidth()
7024  }
7025
7026  /* Public */
7027  /* http://imakewebthings.com/waypoints/api/context-destroy */
7028  Context.prototype.destroy = function() {
7029    var allWaypoints = []
7030    for (var axis in this.waypoints) {
7031      for (var waypointKey in this.waypoints[axis]) {
7032        allWaypoints.push(this.waypoints[axis][waypointKey])
7033      }
7034    }
7035    for (var i = 0, end = allWaypoints.length; i < end; i++) {
7036      allWaypoints[i].destroy()
7037    }
7038  }
7039
7040  /* Public */
7041  /* http://imakewebthings.com/waypoints/api/context-refresh */
7042  Context.prototype.refresh = function() {
7043    /*eslint-disable eqeqeq */
7044    var isWindow = this.element == this.element.window
7045    /*eslint-enable eqeqeq */
7046    var contextOffset = isWindow ? undefined : this.adapter.offset()
7047    var triggeredGroups = {}
7048    var axes
7049
7050    this.handleScroll()
7051    axes = {
7052      horizontal: {
7053        contextOffset: isWindow ? 0 : contextOffset.left,
7054        contextScroll: isWindow ? 0 : this.oldScroll.x,
7055        contextDimension: this.innerWidth(),
7056        oldScroll: this.oldScroll.x,
7057        forward: 'right',
7058        backward: 'left',
7059        offsetProp: 'left'
7060      },
7061      vertical: {
7062        contextOffset: isWindow ? 0 : contextOffset.top,
7063        contextScroll: isWindow ? 0 : this.oldScroll.y,
7064        contextDimension: this.innerHeight(),
7065        oldScroll: this.oldScroll.y,
7066        forward: 'down',
7067        backward: 'up',
7068        offsetProp: 'top'
7069      }
7070    }
7071
7072    for (var axisKey in axes) {
7073      var axis = axes[axisKey]
7074      for (var waypointKey in this.waypoints[axisKey]) {
7075        var waypoint = this.waypoints[axisKey][waypointKey]
7076        var adjustment = waypoint.options.offset
7077        var oldTriggerPoint = waypoint.triggerPoint
7078        var elementOffset = 0
7079        var freshWaypoint = oldTriggerPoint == null
7080        var contextModifier, wasBeforeScroll, nowAfterScroll
7081        var triggeredBackward, triggeredForward
7082
7083        if (waypoint.element !== waypoint.element.window) {
7084          elementOffset = waypoint.adapter.offset()[axis.offsetProp]
7085        }
7086
7087        if (typeof adjustment === 'function') {
7088          adjustment = adjustment.apply(waypoint)
7089        }
7090        else if (typeof adjustment === 'string') {
7091          adjustment = parseFloat(adjustment)
7092          if (waypoint.options.offset.indexOf('%') > - 1) {
7093            adjustment = Math.ceil(axis.contextDimension * adjustment / 100)
7094          }
7095        }
7096
7097        contextModifier = axis.contextScroll - axis.contextOffset
7098        waypoint.triggerPoint = Math.floor(elementOffset + contextModifier - adjustment)
7099        wasBeforeScroll = oldTriggerPoint < axis.oldScroll
7100        nowAfterScroll = waypoint.triggerPoint >= axis.oldScroll
7101        triggeredBackward = wasBeforeScroll && nowAfterScroll
7102        triggeredForward = !wasBeforeScroll && !nowAfterScroll
7103
7104        if (!freshWaypoint && triggeredBackward) {
7105          waypoint.queueTrigger(axis.backward)
7106          triggeredGroups[waypoint.group.id] = waypoint.group
7107        }
7108        else if (!freshWaypoint && triggeredForward) {
7109          waypoint.queueTrigger(axis.forward)
7110          triggeredGroups[waypoint.group.id] = waypoint.group
7111        }
7112        else if (freshWaypoint && axis.oldScroll >= waypoint.triggerPoint) {
7113          waypoint.queueTrigger(axis.forward)
7114          triggeredGroups[waypoint.group.id] = waypoint.group
7115        }
7116      }
7117    }
7118
7119    Waypoint.requestAnimationFrame(function() {
7120      for (var groupKey in triggeredGroups) {
7121        triggeredGroups[groupKey].flushTriggers()
7122      }
7123    })
7124
7125    return this
7126  }
7127
7128  /* Private */
7129  Context.findOrCreateByElement = function(element) {
7130    return Context.findByElement(element) || new Context(element)
7131  }
7132
7133  /* Private */
7134  Context.refreshAll = function() {
7135    for (var contextId in contexts) {
7136      contexts[contextId].refresh()
7137    }
7138  }
7139
7140  /* Public */
7141  /* http://imakewebthings.com/waypoints/api/context-find-by-element */
7142  Context.findByElement = function(element) {
7143    return contexts[element.waypointContextKey]
7144  }
7145
7146  window.onload = function() {
7147    if (oldWindowLoad) {
7148      oldWindowLoad()
7149    }
7150    Context.refreshAll()
7151  }
7152
7153
7154  Waypoint.requestAnimationFrame = function(callback) {
7155    var requestFn = window.requestAnimationFrame ||
7156      window.mozRequestAnimationFrame ||
7157      window.webkitRequestAnimationFrame ||
7158      requestAnimationFrameShim
7159    requestFn.call(window, callback)
7160  }
7161  Waypoint.Context = Context
7162}())
7163;(function() {
7164  'use strict'
7165
7166  function byTriggerPoint(a, b) {
7167    return a.triggerPoint - b.triggerPoint
7168  }
7169
7170  function byReverseTriggerPoint(a, b) {
7171    return b.triggerPoint - a.triggerPoint
7172  }
7173
7174  var groups = {
7175    vertical: {},
7176    horizontal: {}
7177  }
7178  var Waypoint = window.Waypoint
7179
7180  /* http://imakewebthings.com/waypoints/api/group */
7181  function Group(options) {
7182    this.name = options.name
7183    this.axis = options.axis
7184    this.id = this.name + '-' + this.axis
7185    this.waypoints = []
7186    this.clearTriggerQueues()
7187    groups[this.axis][this.name] = this
7188  }
7189
7190  /* Private */
7191  Group.prototype.add = function(waypoint) {
7192    this.waypoints.push(waypoint)
7193  }
7194
7195  /* Private */
7196  Group.prototype.clearTriggerQueues = function() {
7197    this.triggerQueues = {
7198      up: [],
7199      down: [],
7200      left: [],
7201      right: []
7202    }
7203  }
7204
7205  /* Private */
7206  Group.prototype.flushTriggers = function() {
7207    for (var direction in this.triggerQueues) {
7208      var waypoints = this.triggerQueues[direction]
7209      var reverse = direction === 'up' || direction === 'left'
7210      waypoints.sort(reverse ? byReverseTriggerPoint : byTriggerPoint)
7211      for (var i = 0, end = waypoints.length; i < end; i += 1) {
7212        var waypoint = waypoints[i]
7213        if (waypoint.options.continuous || i === waypoints.length - 1) {
7214          waypoint.trigger([direction])
7215        }
7216      }
7217    }
7218    this.clearTriggerQueues()
7219  }
7220
7221  /* Private */
7222  Group.prototype.next = function(waypoint) {
7223    this.waypoints.sort(byTriggerPoint)
7224    var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
7225    var isLast = index === this.waypoints.length - 1
7226    return isLast ? null : this.waypoints[index + 1]
7227  }
7228
7229  /* Private */
7230  Group.prototype.previous = function(waypoint) {
7231    this.waypoints.sort(byTriggerPoint)
7232    var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
7233    return index ? this.waypoints[index - 1] : null
7234  }
7235
7236  /* Private */
7237  Group.prototype.queueTrigger = function(waypoint, direction) {
7238    this.triggerQueues[direction].push(waypoint)
7239  }
7240
7241  /* Private */
7242  Group.prototype.remove = function(waypoint) {
7243    var index = Waypoint.Adapter.inArray(waypoint, this.waypoints)
7244    if (index > -1) {
7245      this.waypoints.splice(index, 1)
7246    }
7247  }
7248
7249  /* Public */
7250  /* http://imakewebthings.com/waypoints/api/first */
7251  Group.prototype.first = function() {
7252    return this.waypoints[0]
7253  }
7254
7255  /* Public */
7256  /* http://imakewebthings.com/waypoints/api/last */
7257  Group.prototype.last = function() {
7258    return this.waypoints[this.waypoints.length - 1]
7259  }
7260
7261  /* Private */
7262  Group.findOrCreate = function(options) {
7263    return groups[options.axis][options.name] || new Group(options)
7264  }
7265
7266  Waypoint.Group = Group
7267}())
7268;(function() {
7269  'use strict'
7270
7271  var $ = window.jQuery
7272  var Waypoint = window.Waypoint
7273
7274  function JQueryAdapter(element) {
7275    this.$element = $(element)
7276  }
7277
7278  $.each([
7279    'innerHeight',
7280    'innerWidth',
7281    'off',
7282    'offset',
7283    'on',
7284    'outerHeight',
7285    'outerWidth',
7286    'scrollLeft',
7287    'scrollTop'
7288  ], function(i, method) {
7289    JQueryAdapter.prototype[method] = function() {
7290      var args = Array.prototype.slice.call(arguments)
7291      return this.$element[method].apply(this.$element, args)
7292    }
7293  })
7294
7295  $.each([
7296    'extend',
7297    'inArray',
7298    'isEmptyObject'
7299  ], function(i, method) {
7300    JQueryAdapter[method] = $[method]
7301  })
7302
7303  Waypoint.adapters.push({
7304    name: 'jquery',
7305    Adapter: JQueryAdapter
7306  })
7307  Waypoint.Adapter = JQueryAdapter
7308}())
7309;(function() {
7310  'use strict'
7311
7312  var Waypoint = window.Waypoint
7313
7314  function createExtension(framework) {
7315    return function() {
7316      var waypoints = []
7317      var overrides = arguments[0]
7318
7319      if (framework.isFunction(arguments[0])) {
7320        overrides = framework.extend({}, arguments[1])
7321        overrides.handler = arguments[0]
7322      }
7323
7324      this.each(function() {
7325        var options = framework.extend({}, overrides, {
7326          element: this
7327        })
7328        if (typeof options.context === 'string') {
7329          options.context = framework(this).closest(options.context)[0]
7330        }
7331        waypoints.push(new Waypoint(options))
7332      })
7333
7334      return waypoints
7335    }
7336  }
7337
7338  if (window.jQuery) {
7339    window.jQuery.fn.waypoint = createExtension(window.jQuery)
7340  }
7341  if (window.Zepto) {
7342    window.Zepto.fn.waypoint = createExtension(window.Zepto)
7343  }
7344}())
7345;
7346/*!
7347Waypoints Inview Shortcut - 4.0.1
7348Copyright © 2011-2016 Caleb Troughton
7349Licensed under the MIT license.
7350https://github.com/imakewebthings/waypoints/blob/master/licenses.txt
7351*/
7352(function() {
7353  'use strict'
7354
7355  function noop() {}
7356
7357  var Waypoint = window.Waypoint
7358
7359  /* http://imakewebthings.com/waypoints/shortcuts/inview */
7360  function Inview(options) {
7361    this.options = Waypoint.Adapter.extend({}, Inview.defaults, options)
7362    this.axis = this.options.horizontal ? 'horizontal' : 'vertical'
7363    this.waypoints = []
7364    this.element = this.options.element
7365    this.createWaypoints()
7366  }
7367
7368  /* Private */
7369  Inview.prototype.createWaypoints = function() {
7370    var configs = {
7371      vertical: [{
7372        down: 'enter',
7373        up: 'exited',
7374        offset: '100%'
7375      }, {
7376        down: 'entered',
7377        up: 'exit',
7378        offset: 'bottom-in-view'
7379      }, {
7380        down: 'exit',
7381        up: 'entered',
7382        offset: 0
7383      }, {
7384        down: 'exited',
7385        up: 'enter',
7386        offset: function() {
7387          return -this.adapter.outerHeight()
7388        }
7389      }],
7390      horizontal: [{
7391        right: 'enter',
7392        left: 'exited',
7393        offset: '100%'
7394      }, {
7395        right: 'entered',
7396        left: 'exit',
7397        offset: 'right-in-view'
7398      }, {
7399        right: 'exit',
7400        left: 'entered',
7401        offset: 0
7402      }, {
7403        right: 'exited',
7404        left: 'enter',
7405        offset: function() {
7406          return -this.adapter.outerWidth()
7407        }
7408      }]
7409    }
7410
7411    for (var i = 0, end = configs[this.axis].length; i < end; i++) {
7412      var config = configs[this.axis][i]
7413      this.createWaypoint(config)
7414    }
7415  }
7416
7417  /* Private */
7418  Inview.prototype.createWaypoint = function(config) {
7419    var self = this
7420    this.waypoints.push(new Waypoint({
7421      context: this.options.context,
7422      element: this.options.element,
7423      enabled: this.options.enabled,
7424      handler: (function(config) {
7425        return function(direction) {
7426          self.options[config[direction]].call(self, direction)
7427        }
7428      }(config)),
7429      offset: config.offset,
7430      horizontal: this.options.horizontal
7431    }))
7432  }
7433
7434  /* Public */
7435  Inview.prototype.destroy = function() {
7436    for (var i = 0, end = this.waypoints.length; i < end; i++) {
7437      this.waypoints[i].destroy()
7438    }
7439    this.waypoints = []
7440  }
7441
7442  Inview.prototype.disable = function() {
7443    for (var i = 0, end = this.waypoints.length; i < end; i++) {
7444      this.waypoints[i].disable()
7445    }
7446  }
7447
7448  Inview.prototype.enable = function() {
7449    for (var i = 0, end = this.waypoints.length; i < end; i++) {
7450      this.waypoints[i].enable()
7451    }
7452  }
7453
7454  Inview.defaults = {
7455    context: window,
7456    enabled: true,
7457    enter: noop,
7458    entered: noop,
7459    exit: noop,
7460    exited: noop
7461  }
7462
7463  Waypoint.Inview = Inview
7464}())
7465;
7466
7467/*
7468 * SmartMenus jQuery v0.9.6
7469 * http://www.smartmenus.org/
7470 *
7471 * Copyright 2014 Vasil Dinkov, Vadikom Web Ltd.
7472 * http://vadikom.com/
7473 *
7474 * Released under the MIT license:
7475 * http://www.opensource.org/licenses/MIT
7476 */
7477
7478(function($) {
7479
7480	var menuTrees = [],
7481		IE = !!window.createPopup, // we need to detect it, unfortunately
7482		IElt9 = IE && !document.defaultView,
7483		IElt8 = IE && !document.querySelector,
7484		IE6 = IE && typeof document.documentElement.currentStyle.minWidth == 'undefined',
7485		mouse = false, // optimize for touch by default - we will detect for mouse input
7486		mouseDetectionEnabled = false;
7487
7488	// Handle detection for mouse input (i.e. desktop browsers, tablets with a mouse, etc.)
7489	function initMouseDetection(disable) {
7490		if (!mouseDetectionEnabled && !disable) {
7491			// if we get two consecutive mousemoves within 2 pixels from each other and within 300ms, we assume a real mouse/cursor is present
7492			// 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)
7493			var firstTime = true,
7494				lastMove = null;
7495			$(document).bind({
7496				'mousemove.smartmenus_mouse': function(e) {
7497					var thisMove = { x: e.pageX, y: e.pageY, timeStamp: new Date().getTime() };
7498					if (lastMove) {
7499						var deltaX = Math.abs(lastMove.x - thisMove.x),
7500							deltaY = Math.abs(lastMove.y - thisMove.y);
7501	 					if ((deltaX > 0 || deltaY > 0) && deltaX <= 2 && deltaY <= 2 && thisMove.timeStamp - lastMove.timeStamp <= 300) {
7502							mouse = true;
7503							// 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
7504							if (firstTime) {
7505								var $a = $(e.target).closest('a');
7506								if ($a.is('a')) {
7507									$.each(menuTrees, function() {
7508										if ($.contains(this.$root[0], $a[0])) {
7509											this.itemEnter({ currentTarget: $a[0] });
7510											return false;
7511										}
7512									});
7513								}
7514								firstTime = false;
7515							}
7516						}
7517					}
7518					lastMove = thisMove;
7519				},
7520				'touchstart.smartmenus_mouse pointerover.smartmenus_mouse MSPointerOver.smartmenus_mouse': function(e) {
7521					if (!/^(4|mouse)$/.test(e.originalEvent.pointerType)) {
7522						mouse = false;
7523					}
7524				}
7525			});
7526			mouseDetectionEnabled = true;
7527		} else if (mouseDetectionEnabled && disable) {
7528			$(document).unbind('.smartmenus_mouse');
7529			mouseDetectionEnabled = false;
7530		}
7531	};
7532
7533	$.SmartMenus = function(elm, options) {
7534		this.$root = $(elm);
7535		this.opts = options;
7536		this.rootId = ''; // internal
7537		this.$subArrow = null;
7538		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)
7539		this.activatedItems = []; // stores last activated A's for each level
7540		this.visibleSubMenus = []; // stores visible sub menus UL's
7541		this.showTimeout = 0;
7542		this.hideTimeout = 0;
7543		this.scrollTimeout = 0;
7544		this.clickActivated = false;
7545		this.zIndexInc = 0;
7546		this.$firstLink = null; // we'll use these for some tests
7547		this.$firstSub = null; // at runtime so we'll cache them
7548		this.disabled = false;
7549		this.$disableOverlay = null;
7550		this.init();
7551	};
7552
7553	$.extend($.SmartMenus, {
7554		hideAll: function() {
7555			$.each(menuTrees, function() {
7556				this.menuHideAll();
7557			});
7558		},
7559		destroy: function() {
7560			while (menuTrees.length) {
7561				menuTrees[0].destroy();
7562			}
7563			initMouseDetection(true);
7564		},
7565		prototype: {
7566			init: function(refresh) {
7567				var self = this;
7568
7569				if (!refresh) {
7570					menuTrees.push(this);
7571
7572					this.rootId = (new Date().getTime() + Math.random() + '').replace(/\D/g, '');
7573
7574					if (this.$root.hasClass('sm-rtl')) {
7575						this.opts.rightToLeftSubMenus = true;
7576					}
7577
7578					// init root (main menu)
7579					this.$root
7580						.data('smartmenus', this)
7581						.attr('data-smartmenus-id', this.rootId)
7582						.dataSM('level', 1)
7583						.bind({
7584							'mouseover.smartmenus focusin.smartmenus': $.proxy(this.rootOver, this),
7585							'mouseout.smartmenus focusout.smartmenus': $.proxy(this.rootOut, this)
7586						})
7587						.delegate('a', {
7588							'mouseenter.smartmenus': $.proxy(this.itemEnter, this),
7589							'mouseleave.smartmenus': $.proxy(this.itemLeave, this),
7590							'mousedown.smartmenus': $.proxy(this.itemDown, this),
7591							'focus.smartmenus': $.proxy(this.itemFocus, this),
7592							'blur.smartmenus': $.proxy(this.itemBlur, this),
7593							'click.smartmenus': $.proxy(this.itemClick, this),
7594							'touchend.smartmenus': $.proxy(this.itemTouchEnd, this)
7595						});
7596
7597					var eNamespace = '.smartmenus' + this.rootId;
7598					// hide menus on tap or click outside the root UL
7599					if (this.opts.hideOnClick) {
7600						$(document).bind('touchstart' + eNamespace, $.proxy(this.docTouchStart, this))
7601							.bind('touchmove' + eNamespace, $.proxy(this.docTouchMove, this))
7602							.bind('touchend' + eNamespace, $.proxy(this.docTouchEnd, this))
7603							// for Opera Mobile < 11.5, webOS browser, etc. we'll check click too
7604							.bind('click' + eNamespace, $.proxy(this.docClick, this));
7605					}
7606					// hide sub menus on resize
7607					$(window).bind('resize' + eNamespace + ' orientationchange' + eNamespace, $.proxy(this.winResize, this));
7608					$(window).bind('scroll' + eNamespace + ' orientationchange' + eNamespace, $.proxy(this.winResize, this));
7609
7610					if (this.opts.subIndicators) {
7611						this.$subArrow = $('<span/>').addClass('sub-arrow');
7612						if (this.opts.subIndicatorsText) {
7613							this.$subArrow.html(this.opts.subIndicatorsText);
7614						}
7615					}
7616
7617					// make sure mouse detection is enabled
7618					initMouseDetection();
7619				}
7620
7621				// init sub menus
7622				this.$firstSub = this.$root.find('ul').each(function() { self.menuInit($(this)); }).eq(0);
7623
7624				this.$firstLink = this.$root.find('a').eq(0);
7625
7626				// find current item
7627				if (this.opts.markCurrentItem) {
7628					var reDefaultDoc = /(index|default)\.[^#\?\/]*/i,
7629						reHash = /#.*/,
7630						locHref = window.location.href.replace(reDefaultDoc, ''),
7631						locHrefNoHash = locHref.replace(reHash, '');
7632					this.$root.find('a').each(function() {
7633						var href = this.href.replace(reDefaultDoc, ''),
7634							$this = $(this);
7635						if (href == locHref || href == locHrefNoHash) {
7636							$this.addClass('current');
7637							if (self.opts.markCurrentTree) {
7638								$this.parents('li').each(function() {
7639									var $this = $(this);
7640									if ($this.dataSM('sub')) {
7641										$this.children('a').addClass('current');
7642									}
7643								});
7644							}
7645						}
7646					});
7647				}
7648			},
7649			destroy: function() {
7650				this.menuHideAll();
7651				this.$root
7652					.removeData('smartmenus')
7653					.removeAttr('data-smartmenus-id')
7654					.removeDataSM('level')
7655					.unbind('.smartmenus')
7656					.undelegate('.smartmenus');
7657				var eNamespace = '.smartmenus' + this.rootId;
7658				$(document).unbind(eNamespace);
7659				$(window).unbind(eNamespace);
7660				if (this.opts.subIndicators) {
7661					this.$subArrow = null;
7662				}
7663				var self = this;
7664				$.each(this.subMenus, function() {
7665					if (this.hasClass('mega-menu')) {
7666						this.find('ul').removeDataSM('in-mega');
7667					}
7668					if (this.dataSM('shown-before')) {
7669						if (IElt8) {
7670							this.children().css({ styleFloat: '', width: '' });
7671						}
7672						if (self.opts.subMenusMinWidth || self.opts.subMenusMaxWidth) {
7673							if (!IE6) {
7674								this.css({ width: '', minWidth: '', maxWidth: '' }).removeClass('sm-nowrap');
7675							} else {
7676								this.css({ width: '', overflowX: '', overflowY: '' }).children().children('a').css('white-space', '');
7677							}
7678						}
7679						if (this.dataSM('scroll-arrows')) {
7680							this.dataSM('scroll-arrows').remove();
7681						}
7682						this.css({ zIndex: '', top: '', left: '', marginLeft: '', marginTop: '', display: '' });
7683					}
7684					if (self.opts.subIndicators) {
7685						this.dataSM('parent-a').removeClass('has-submenu').children('span.sub-arrow').remove();
7686					}
7687					this.removeDataSM('shown-before')
7688						.removeDataSM('ie-shim')
7689						.removeDataSM('scroll-arrows')
7690						.removeDataSM('parent-a')
7691						.removeDataSM('level')
7692						.removeDataSM('beforefirstshowfired')
7693						.parent().removeDataSM('sub');
7694				});
7695				if (this.opts.markCurrentItem) {
7696					this.$root.find('a.current').removeClass('current');
7697				}
7698				this.$root = null;
7699				this.$firstLink = null;
7700				this.$firstSub = null;
7701				if (this.$disableOverlay) {
7702					this.$disableOverlay.remove();
7703					this.$disableOverlay = null;
7704				}
7705				menuTrees.splice($.inArray(this, menuTrees), 1);
7706			},
7707			disable: function(noOverlay) {
7708				if (!this.disabled) {
7709					this.menuHideAll();
7710					// display overlay over the menu to prevent interaction
7711					if (!noOverlay && !this.opts.isPopup && this.$root.is(':visible')) {
7712						var pos = this.$root.offset();
7713						this.$disableOverlay = $('<div class="sm-jquery-disable-overlay"/>').css({
7714							position: 'absolute',
7715							top: pos.top,
7716							left: pos.left,
7717							width: this.$root.outerWidth(),
7718							height: this.$root.outerHeight(),
7719							zIndex: this.getStartZIndex() + 1,
7720							opacity: 0
7721						}).appendTo(document.body);
7722					}
7723					this.disabled = true;
7724				}
7725			},
7726			docClick: function(e) {
7727				// hide on any click outside the menu or on a menu link
7728				if (this.visibleSubMenus.length && !$.contains(this.$root[0], e.target) || $(e.target).is('a')) {
7729					this.menuHideAll($(e.target));
7730				}
7731			},
7732			docTouchEnd: function(e) {
7733				if (!this.lastTouch) {
7734					return;
7735				}
7736				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))) {
7737					if (this.hideTimeout) {
7738						clearTimeout(this.hideTimeout);
7739						this.hideTimeout = 0;
7740					}
7741					// hide with a delay to prevent triggering accidental unwanted click on some page element
7742					var self = this;
7743					this.hideTimeout = setTimeout(function() { self.menuHideAll($(e.target)); }, 350);
7744				}
7745				this.lastTouch = null;
7746			},
7747			docTouchMove: function(e) {
7748				if (!this.lastTouch) {
7749					return;
7750				}
7751				var touchPoint = e.originalEvent.touches[0];
7752				this.lastTouch.x2 = touchPoint.pageX;
7753				this.lastTouch.y2 = touchPoint.pageY;
7754			},
7755			docTouchStart: function(e) {
7756				var touchPoint = e.originalEvent.touches[0];
7757				this.lastTouch = { x1: touchPoint.pageX, y1: touchPoint.pageY, target: touchPoint.target };
7758			},
7759			enable: function() {
7760				if (this.disabled) {
7761					if (this.$disableOverlay) {
7762						this.$disableOverlay.remove();
7763						this.$disableOverlay = null;
7764					}
7765					this.disabled = false;
7766				}
7767			},
7768			getHeight: function($elm) {
7769				return this.getOffset($elm, true);
7770			},
7771			// returns precise width/height float values in IE9+, FF4+, recent WebKit
7772			// http://vadikom.com/dailies/offsetwidth-offsetheight-useless-in-ie9-firefox4/
7773			getOffset: function($elm, height) {
7774				var old;
7775				if ($elm.css('display') == 'none') {
7776					old = { position: $elm[0].style.position, visibility: $elm[0].style.visibility };
7777					$elm.css({ position: 'absolute', visibility: 'hidden' }).show();
7778				}
7779				var defaultView = $elm[0].ownerDocument.defaultView,
7780					compStyle = defaultView && defaultView.getComputedStyle && defaultView.getComputedStyle($elm[0], null),
7781					val = compStyle && parseFloat(compStyle[height ? 'height' : 'width']);
7782				if (val) {
7783					val += parseFloat(compStyle[height ? 'paddingTop' : 'paddingLeft'])
7784	 					+ parseFloat(compStyle[height ? 'paddingBottom' : 'paddingRight'])
7785	 					+ parseInt(compStyle[height ? 'borderTopWidth' : 'borderLeftWidth'])
7786	 					+ parseInt(compStyle[height ? 'borderBottomWidth' : 'borderRightWidth']);
7787				} else {
7788					val = height ? $elm[0].offsetHeight : $elm[0].offsetWidth;
7789				}
7790				if (old) {
7791					$elm.hide().css(old);
7792				}
7793				return val;
7794			},
7795			getWidth: function($elm) {
7796				return this.getOffset($elm);
7797			},
7798			getStartZIndex: function() {
7799				var zIndex = parseInt(this.$root.css('z-index'));
7800				return !isNaN(zIndex) ? zIndex : 1;
7801			},
7802			handleEvents: function() {
7803				return !this.disabled && this.isCSSOn();
7804			},
7805			handleItemEvents: function($a) {
7806				return this.handleEvents() && !this.isLinkInMegaMenu($a);
7807			},
7808			isCollapsible: function() {
7809				return this.$firstSub.css('position') == 'static';
7810			},
7811			isCSSOn: function() {
7812				return this.$firstLink.css('display') == 'block' || this.$firstLink.css('display') == 'inline';
7813			},
7814			isFixed: function() {
7815				return this.$root.css('position') == 'fixed';
7816			},
7817			isLinkInMegaMenu: function($a) {
7818				return !$a.parent().parent().dataSM('level');
7819			},
7820			isTouchMode: function() {
7821				return !mouse || this.isCollapsible();
7822			},
7823			itemActivate: function($a) {
7824				var $li = $a.parent(),
7825					$ul = $li.parent(),
7826					level = $ul.dataSM('level');
7827				// 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
7828				if (level > 1 && (!this.activatedItems[level - 2] || this.activatedItems[level - 2][0] != $ul.dataSM('parent-a')[0])) {
7829					var self = this;
7830					$($ul.parentsUntil('[data-smartmenus-id]', 'ul').get().reverse()).add($ul).each(function() {
7831						self.itemActivate($(this).dataSM('parent-a'));
7832					});
7833				}
7834				// hide any visible deeper level sub menus
7835				if (this.visibleSubMenus.length > level) {
7836					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--) {
7837						this.menuHide(this.visibleSubMenus[i]);
7838					}
7839				}
7840				// save new active item and sub menu for this level
7841				this.activatedItems[level - 1] = $a;
7842				this.visibleSubMenus[level - 1] = $ul;
7843				if (this.$root.triggerHandler('activate.smapi', $a[0]) === false) {
7844					return;
7845				}
7846				// show the sub menu if this item has one
7847				var $sub = $li.dataSM('sub');
7848				if ($sub && (this.isTouchMode() || (!this.opts.showOnClick || this.clickActivated))) {
7849					this.menuShow($sub);
7850				}
7851			},
7852			itemBlur: function(e) {
7853				var $a = $(e.currentTarget);
7854				if (!this.handleItemEvents($a)) {
7855					return;
7856				}
7857				this.$root.triggerHandler('blur.smapi', $a[0]);
7858			},
7859			itemClick: function(e) {
7860				var $a = $(e.currentTarget);
7861
7862				if (!this.handleItemEvents($a)) {
7863					return;
7864				}
7865				$a.removeDataSM('mousedown');
7866				if (this.$root.triggerHandler('click.smapi', $a[0]) === false) {
7867					return false;
7868				}
7869				var $sub = $a.parent().dataSM('sub');
7870				if (this.isTouchMode()) {
7871					// undo fix: prevent the address bar on iPhone from sliding down when expanding a sub menu
7872					if ($a.dataSM('href')) {
7873						$a.attr('href', $a.dataSM('href')).removeDataSM('href');
7874					}
7875					// if the sub is not visible
7876					if ($sub && (!$sub.dataSM('shown-before') || !$sub.is(':visible'))) {
7877						// try to activate the item and show the sub
7878						this.itemActivate($a);
7879						// if "itemActivate" showed the sub, prevent the click so that the link is not loaded
7880						// 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
7881						if ($sub.is(':visible')) {
7882							return false;
7883						}
7884					}
7885				} else if (this.opts.showOnClick && $a.parent().parent().dataSM('level') == 1 && $sub) {
7886					this.clickActivated = true;
7887					this.menuShow($sub);
7888					return false;
7889				}
7890				if ($a.hasClass('disabled')) {
7891					return false;
7892				}
7893				if (this.$root.triggerHandler('select.smapi', $a[0]) === false) {
7894					return false;
7895				}
7896			},
7897			itemDown: function(e) {
7898				var $a = $(e.currentTarget);
7899				if (!this.handleItemEvents($a)) {
7900					return;
7901				}
7902				$a.dataSM('mousedown', true);
7903			},
7904			itemEnter: function(e) {
7905				var $a = $(e.currentTarget);
7906				if (!this.handleItemEvents($a)) {
7907					return;
7908				}
7909				if (!this.isTouchMode()) {
7910					if (this.showTimeout) {
7911						clearTimeout(this.showTimeout);
7912						this.showTimeout = 0;
7913					}
7914					var self = this;
7915					this.showTimeout = setTimeout(function() { self.itemActivate($a); }, this.opts.showOnClick && $a.parent().parent().dataSM('level') == 1 ? 1 : this.opts.showTimeout);
7916				}
7917				this.$root.triggerHandler('mouseenter.smapi', $a[0]);
7918			},
7919			itemFocus: function(e) {
7920				var $a = $(e.currentTarget);
7921				if (!this.handleItemEvents($a)) {
7922					return;
7923				}
7924				// 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
7925				if ((!this.isTouchMode() || !$a.dataSM('mousedown')) && (!this.activatedItems.length || this.activatedItems[this.activatedItems.length - 1][0] != $a[0])) {
7926					this.itemActivate($a);
7927				}
7928				this.$root.triggerHandler('focus.smapi', $a[0]);
7929			},
7930			itemLeave: function(e) {
7931				var $a = $(e.currentTarget);
7932				if (!this.handleItemEvents($a)) {
7933					return;
7934				}
7935				if (!this.isTouchMode()) {
7936					if ($a[0].blur) {
7937						$a[0].blur();
7938					}
7939					if (this.showTimeout) {
7940						clearTimeout(this.showTimeout);
7941						this.showTimeout = 0;
7942					}
7943				}
7944				$a.removeDataSM('mousedown');
7945				this.$root.triggerHandler('mouseleave.smapi', $a[0]);
7946			},
7947			itemTouchEnd: function(e) {
7948				var $a = $(e.currentTarget);
7949				if (!this.handleItemEvents($a)) {
7950					return;
7951				}
7952				// prevent the address bar on iPhone from sliding down when expanding a sub menu
7953				var $sub = $a.parent().dataSM('sub');
7954				if ($a.attr('href').charAt(0) !== '#' && $sub && (!$sub.dataSM('shown-before') || !$sub.is(':visible'))) {
7955					$a.dataSM('href', $a.attr('href'));
7956					$a.attr('href', '#');
7957				}
7958			},
7959			menuFixLayout: function($ul) {
7960				// fixes a menu that is being shown for the first time
7961				if (!$ul.dataSM('shown-before')) {
7962					$ul.hide().dataSM('shown-before', true);
7963					// fix the layout of the items in IE<8
7964					if (IElt8) {
7965						$ul.children().css({ styleFloat: 'left', width: '100%' });
7966					}
7967				}
7968			},
7969			menuHide: function($sub) {
7970				if (this.$root.triggerHandler('beforehide.smapi', $sub[0]) === false) {
7971					return;
7972				}
7973				$sub.stop(true, true);
7974				if ($sub.is(':visible')) {
7975					var complete = function() {
7976						// unset z-index
7977						if (IElt9) {
7978							$sub.parent().css('z-index', '');
7979						} else {
7980							$sub.css('z-index', '');
7981						}
7982					};
7983					// if sub is collapsible (mobile view)
7984					if (this.isCollapsible()) {
7985						if (this.opts.collapsibleHideFunction) {
7986							this.opts.collapsibleHideFunction.call(this, $sub, complete);
7987						} else {
7988							$sub.hide(this.opts.collapsibleHideDuration, complete);
7989						}
7990					} else {
7991						if (this.opts.hideFunction) {
7992							this.opts.hideFunction.call(this, $sub, complete);
7993						} else {
7994							$sub.hide(this.opts.hideDuration, complete);
7995						}
7996					}
7997					// remove IE iframe shim
7998					if ($sub.dataSM('ie-shim')) {
7999						$sub.dataSM('ie-shim').remove();
8000					}
8001					// deactivate scrolling if it is activated for this sub
8002					if ($sub.dataSM('scroll')) {
8003						$sub.unbind('.smartmenus_scroll').removeDataSM('scroll').dataSM('scroll-arrows').hide();
8004					}
8005					// unhighlight parent item
8006					$sub.dataSM('parent-a').removeClass('highlighted');
8007					var level = $sub.dataSM('level');
8008					this.activatedItems.splice(level - 1, 1);
8009					this.visibleSubMenus.splice(level - 1, 1);
8010					this.$root.triggerHandler('hide.smapi', $sub[0]);
8011				}
8012			},
8013			menuHideAll: function($item) {
8014				if ($item != undefined && $item.parent().hasClass('menu-item') && !$item.parent().hasClass('menu-item-has-children')) return;
8015				if ( $('body').hasClass('vmenu') ) return;
8016				if (this.showTimeout) {
8017					clearTimeout(this.showTimeout);
8018					this.showTimeout = 0;
8019				}
8020				// hide all subs
8021				for (var i = this.visibleSubMenus.length - 1; i > 0; i--) {
8022					this.menuHide(this.visibleSubMenus[i]);
8023				}
8024				// hide root if it's popup
8025				if (this.opts.isPopup) {
8026					this.$root.stop(true, true);
8027					if (this.$root.is(':visible')) {
8028						if (this.opts.hideFunction) {
8029							this.opts.hideFunction.call(this, this.$root);
8030						} else {
8031							this.$root.hide(this.opts.hideDuration);
8032						}
8033						// remove IE iframe shim
8034						if (this.$root.dataSM('ie-shim')) {
8035							this.$root.dataSM('ie-shim').remove();
8036						}
8037					}
8038				}
8039				this.activatedItems = [];
8040				this.visibleSubMenus = [];
8041				this.clickActivated = false;
8042				// reset z-index increment
8043				this.zIndexInc = 0;
8044			},
8045			menuIframeShim: function($ul) {
8046				// create iframe shim for the menu
8047				if (IE && this.opts.overlapControlsInIE && !$ul.dataSM('ie-shim')) {
8048					$ul.dataSM('ie-shim', $('<iframe/>').attr({ src: 'javascript:0', tabindex: -9 })
8049						.css({ position: 'absolute', top: 'auto', left: '0', opacity: 0, border: '0' })
8050					);
8051				}
8052			},
8053			menuInit: function($ul) {
8054				if (!$ul.dataSM('in-mega')) {
8055					this.subMenus.push($ul);
8056					// mark UL's in mega drop downs (if any) so we can neglect them
8057					if ($ul.hasClass('mega-menu')) {
8058						$ul.find('ul').dataSM('in-mega', true);
8059					}
8060					// get level (much faster than, for example, using parentsUntil)
8061					var level = 2,
8062						par = $ul[0];
8063					while ((par = par.parentNode.parentNode) != this.$root[0]) {
8064						level++;
8065					}
8066					// cache stuff
8067					$ul.dataSM('parent-a', $ul.prevAll('a').eq(-1))
8068						.dataSM('level', level)
8069						.parent().dataSM('sub', $ul);
8070					// add sub indicator to parent item
8071					if (this.opts.subIndicators) {
8072						$ul.dataSM('parent-a').addClass('has-submenu')[this.opts.subIndicatorsPos](this.$subArrow.clone());
8073					}
8074				}
8075			},
8076			menuPosition: function($sub) {
8077				var fixIE = $('html.ie').length;
8078				// if (fixIE) {
8079				// 	var $rowParent = $($sub).closest('.main-menu-container');
8080				// 	$rowParent.height($rowParent.height());
8081				// }
8082				var $a = $sub.dataSM('parent-a'),
8083					$li = $sub.parent(),
8084					$ul = $sub.parent().parent(),
8085					$container = $ul.closest('.row-menu-inner'),
8086					level = $sub.dataSM('level'),
8087					subW = this.getWidth($sub),
8088					subH = this.getHeight($sub),
8089					itemOffset = $a.offset(),
8090					itemX = itemOffset.left,
8091					itemY = itemOffset.top,
8092					itemW = this.getWidth($a),
8093					itemH = this.getHeight($a),
8094					$win = $(window),
8095					winX = $win.scrollLeft(),
8096					winY = $win.scrollTop(),
8097					winW = $win.width(),
8098					winH = $win.height(),
8099					containerW = $container.width(),
8100					containerOffsetX = containerW + ((winW - containerW) / 2),
8101					horizontalParent = $ul.hasClass('sm') && !$ul.hasClass('sm-vertical'),
8102					subOffsetX = level == 2 ? this.opts.mainMenuSubOffsetX : this.opts.subMenusSubOffsetX,
8103					subOffsetY = level == 2 ? this.opts.mainMenuSubOffsetY : this.opts.subMenusSubOffsetY,
8104					x, y, leftPos;
8105				if (horizontalParent) {
8106					x = this.opts.rightToLeftSubMenus ? itemW - subW - subOffsetX : subOffsetX;
8107					y = this.opts.bottomToTopSubMenus ? -subH - subOffsetY : itemH + subOffsetY;
8108				} else {
8109					x = this.opts.rightToLeftSubMenus ? subOffsetX - subW : itemW - subOffsetX;
8110					y = this.opts.bottomToTopSubMenus ? itemH - subOffsetY - subH : subOffsetY;
8111				}
8112				if (this.opts.keepInViewport && !this.isCollapsible()) {
8113					if (this.isFixed()) {
8114						itemX -= winX;
8115						itemY -= winY;
8116						winX = winY = 0;
8117					}
8118					var absX = itemX + x,
8119						absY = itemY + y;
8120					if (this.opts.rightToLeftSubMenus && absX < winX) {
8121						x = horizontalParent ? winX - absX + x : itemW - subOffsetX;
8122					} else if (!this.opts.rightToLeftSubMenus && absX + subW > winX + containerOffsetX ) {
8123						x = horizontalParent ? winX + containerOffsetX - subW - absX + x : subOffsetX - subW;
8124					}
8125					if (!horizontalParent) {
8126						if (subH < winH && absY + subH > winY + winH) {
8127							y += winY + winH - subH - absY;
8128						} else if (subH >= winH || absY < winY) {
8129							y += winY - absY;
8130						}
8131					}
8132					// do we need scrolling?
8133					// 0.49 added for the sake of IE9/FF4+ where we might be dealing with float numbers for "subH"
8134					if (mouse && (horizontalParent && (absY + subH > winY + winH + 0.49 || absY < winY) || !horizontalParent && subH > winH + 0.49)) {
8135						var self = this;
8136						if (!$sub.dataSM('scroll-arrows')) {
8137							$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]])
8138								.bind({
8139									mouseenter: function() { self.menuScroll($sub, $(this).hasClass('scroll-up')); },
8140									mouseleave: function(e) {
8141										self.menuScrollStop($sub);
8142										self.menuScrollOut($sub, e);
8143									},
8144									'mousewheel DOMMouseScroll': function(e) { e.preventDefault(); }
8145								})
8146								.insertAfter($sub)
8147							);
8148						}
8149						// bind events to show/hide arrows on hover and save scrolling data for this sub
8150						var vportY = winY - (itemY + itemH);
8151						$sub.dataSM('scroll', {
8152								vportY: vportY,
8153								subH: subH,
8154								winH: winH,
8155								step: 1
8156							})
8157							.bind({
8158								'mouseover.smartmenus_scroll': function(e) { self.menuScrollOver($sub, e); },
8159								'mouseout.smartmenus_scroll': function(e) { self.menuScrollOut($sub, e); },
8160								'mousewheel.smartmenus_scroll DOMMouseScroll.smartmenus_scroll': function(e) { self.menuScrollMousewheel($sub, e); }
8161							})
8162							.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
8162StartZIndex() + this.zIndexInc })
8163							.eq(0).css('margin-top', vportY).end()
8164							.eq(1).css('margin-top', vportY + winH - this.getHeight($sub.dataSM('scroll-arrows').eq(1))).end()
8165							.eq(horizontalParent && this.opts.bottomToTopSubMenus ? 0 : 1).show();
8166					}
8167				}
8168				// if ($sub.hasClass('mega-menu-inner')) {
8169				// 	if ($('.menu-wrapper .megamenu-diff').length > 0) {
8170				// 		leftPos = parseFloat($('.menu-wrapper .megamenu-diff').outerWidth());
8171				// 		leftPos = -leftPos;
8172				// 	} else leftPos = parseFloat($('.menu-wrapper .main-menu-container').css('paddingLeft'));
8173
8174				// 	if ($('body.header-full-width').length > 0) {
8175				// 		$sub.css({ width: $('.box-container').width()});
8176				// 		leftPos = (leftPos - parseFloat($('.menu-wrapper .row-menu-inner').css('paddingLeft')));
8177				// 	} else $sub.css({ width: containerW});
8178				// 	leftPos = leftPos + 'px';
8179
8180				// } else {
8181				// 	leftPos = $li.position().left + 'px';
8182				// }
8183				if ($sub.hasClass('mega-menu-inner')) {
8184					$sub.css({ width: containerW});
8185					if ( $('body.hmenu-center-double').length ) {
8186						leftPos = '0px';
8187					} else {
8188						if ( $('body.hmenu-center-split').length > 0 && !$sub.closest('.navbar-cta').length && ! $('body.hormenu-position-right').length ) {
8189							leftPos = - ($sub.closest('.menu-primary-inner')[0].offsetLeft) + 'px';
8190						} else {
8191							if ( $sub.closest('.navbar-cta').length && !$('body.hmenu').length && !$('body.hmenu-center').length ) {
8192								leftPos = - ( parseFloat($sub.closest('.navbar-cta').offset().left) - parseFloat($sub.closest('.row-menu-inner').find('.logo-container').offset().left) ) + 'px';
8193							} else if ($sub.closest('.row-menu-inner').find('.logo-container').length > 0 && !$('body.hmenu-center-split').length) {
8194								leftPos = - (parseFloat($sub.closest('.row-menu-inner').find('.logo-container').css('paddingRight')) + ($sub.closest('.row-menu-inner').find('.logo-container').width())) + 'px';
8195							} else {
8196								leftPos = '0px';
8197							}
8198						}
8199					}
8200				} else {
8201					leftPos = (level > 2 ? $li.position().left - parseFloat($li.closest('ul').css('paddingLeft')) : $li.position().left ) + 'px';
8202					x = (level > 2 && x >= 0) ? x + (parseFloat($li.closest('ul').css('paddingLeft')) * 2) + 1 : x - 1;
8203				}
8204
8205				$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) });
8206				// IE iframe shim
8207				this.menuIframeShim($sub);
8208				if ($sub.dataSM('ie-shim')) {
8209					$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) });
8210				}
8211			},
8212			menuScroll: function($sub, up, wheel) {
8213				var y = parseFloat($sub.css('margin-top')),
8214					scroll = $sub.dataSM('scroll'),
8215					end = scroll.vportY + (up ? 0 : scroll.winH - scroll.subH),
8216					step = wheel || !this.opts.scrollAccelerate ? this.opts.scrollStep : Math.floor($sub.dataSM('scroll').step);
8217				$sub.add($sub.dataSM('ie-shim')).css('margin-top', Math.abs(end - y) > step ? y + (up ? step : -step) : end);
8218				y = parseFloat($sub.css('margin-top'));
8219				// show opposite arrow if appropriate
8220				if (up && y + scroll.subH > scroll.vportY + scroll.winH || !up && y < scroll.vportY) {
8221					$sub.dataSM('scroll-arrows').eq(up ? 1 : 0).show();
8222				}
8223				// accelerate when not using mousewheel to scroll
8224				if (!wheel && this.opts.scrollAccelerate && $sub.dataSM('scroll').step < this.opts.scrollStep) {
8225					$sub.dataSM('scroll').step += 0.5;
8226				}
8227				// "y" and "end" might be float numbers in IE9/FF4+ so this weird way to check is used
8228				if (Math.abs(y - end) < 1) {
8229					$sub.dataSM('scroll-arrows').eq(up ? 0 : 1).hide();
8230					$sub.dataSM('scroll').step = 1;
8231				} else if (!wheel) {
8232					var self = this;
8233					this.scrollTimeout = setTimeout(function() { self.menuScroll($sub, up); }, this.opts.scrollInterval);
8234				}
8235			},
8236			menuScrollMousewheel: function($sub, e) {
8237				var $closestSub = $(e.target).closest('ul');
8238				while ($closestSub.dataSM('in-mega')) {
8239					$closestSub = $closestSub.parent().closest('ul');
8240				}
8241				if ($closestSub[0] == $sub[0]) {
8242					var up = (e.originalEvent.wheelDelta || -e.originalEvent.detail) > 0;
8243					if ($sub.dataSM('scroll-arrows').eq(up ? 0 : 1).is(':visible')) {
8244						this.menuScroll($sub, up, true);
8245					}
8246				}
8247				e.preventDefault();
8248			},
8249			menuScrollOut: function($sub, e) {
8250				var reClass = /^scroll-(up|down)/,
8251					$closestSub = $(e.relatedTarget).closest('ul');
8252				while ($closestSub.dataSM('in-mega')) {
8253					$closestSub = $closestSub.parent().closest('ul');
8254				}
8255				if (!reClass.test((e.relatedTarget || '').className) && ($sub[0] != e.relatedTarget && !$.contains($sub[0], e.relatedTarget) || $closestSub[0] != $sub[0])) {
8256					$sub.dataSM('scroll-arrows').css('visibility', 'hidden');
8257				}
8258			},
8259			menuScrollOver: function($sub, e) {
8260				var reClass = /^scroll-(up|down)/,
8261					$closestSub = $(e.target).closest('ul');
8262				while ($closestSub.dataSM('in-mega')) {
8263					$closestSub = $closestSub.parent().closest('ul');
8264				}
8265				if (!reClass.test(e.target.className) && $closestSub[0] == $sub[0]) {
8266					$sub.dataSM('scroll-arrows').css('visibility', 'visible');
8267				}
8268			},
8269			menuScrollStop: function($sub) {
8270				if (this.scrollTimeout) {
8271					clearTimeout(this.scrollTimeout);
8272					this.scrollTimeout = 0;
8273					$sub.dataSM('scroll').step = 1;
8274				}
8275			},
8276			menuShow: function($sub) {
8277				if (!$sub.dataSM('beforefirstshowfired')) {
8278					$sub.dataSM('beforefirstshowfired', true);
8279					if (this.$root.triggerHandler('beforefirstshow.smapi', $sub[0]) === false) {
8280						return;
8281					}
8282				}
8283				if (this.$root.triggerHandler('beforeshow.smapi', $sub[0]) === false) {
8284					return;
8285				}
8286				this.menuFixLayout($sub);
8287				$sub.stop(true, true);
8288				if (!$sub.is(':visible')) {
8289					// set z-index - for IE < 9 set it to the parent LI
8290					var zIndex = this.getStartZIndex() + (++this.zIndexInc);
8291					if (IElt9) {
8292						$sub.parent().css('z-index', zIndex);
8293					} else {
8294						$sub.css('z-index', zIndex);
8295					}
8296					// highlight parent item
8297					if (this.opts.keepHighlighted || this.isCollapsible()) {
8298						if ($sub.dataSM('parent-a').attr('data-type') != 'title')
8299							$sub.dataSM('parent-a').addClass('highlighted');
8300					}
8301					// min/max-width fix - no way to rely purely on CSS as all UL's are nested
8302					if (this.opts.subMenusMinWidth || this.opts.subMenusMaxWidth) {
8303						if (!IElt8) {
8304							$sub.css({ width: ($sub.hasClass('mega-menu-inner')) ? $('.box-container').outerWidth() + 'px' : 'auto', minWidth: '', maxWidth: '' }).addClass('sm-nowrap');
8305							if (this.opts.subMenusMinWidth) {
8306							 	$sub.css('min-width', this.opts.subMenusMinWidth);
8307							}
8308							if (this.opts.subMenusMaxWidth) {
8309							 	var noMaxWidth = this.getWidth($sub);
8310							 	$sub.css('max-width', this.opts.subMenusMaxWidth);
8311								if (noMaxWidth > this.getWidth($sub)) {
8312									$sub.removeClass('sm-nowrap').css('width', this.opts.subMenusMaxWidth);
8313								}
8314							}
8315						// IE6,7
8316 						} else {
8317							$sub.children().css('styleFloat', 'none');
8318							if (IE6) {
8319								$sub.width(this.opts.subMenusMinWidth ? this.opts.subMenusMinWidth : 1)
8320									.children().children('a').css('white-space', 'nowrap');
8321							} else { // IE7
8322								$sub.css({ width: ($sub.hasClass('mega-menu-inner')) ? $('.box-container').outerWidth() + 'px' : 'auto', minWidth: '', maxWidth: '' }).addClass('sm-nowrap');
8323								if (this.opts.subMenusMinWidth) {
8324							    $sub.css('min-width', this.opts.subMenusMinWidth);
8325								}
8326							}
8327							if (this.opts.subMenusMaxWidth) {
8328								var noMaxWidth = $sub.width();
8329								if (IE6) {
8330							 		var maxWidth = $sub.css({ width: this.opts.subMenusMaxWidth, overflowX: 'hidden', overflowY: 'hidden' }).width();
8331									if (noMaxWidth > maxWidth) {
8332										$sub.css({ width: maxWidth, overflowX: 'visible', overflowY: 'visible' }).children().children('a').css('white-space', '');
8333									} else {
8334										$sub.css({ width: noMaxWidth, overflowX: 'visible', overflowY: 'visible' });
8335									}
8336								} else { // IE7
8337									$sub.css('max-width', this.opts.subMenusMaxWidth);
8338									if (noMaxWidth > $sub.width()) {
8339										$sub.removeClass('sm-nowrap').css('width', this.opts.subMenusMaxWidth);
8340									} else {
8341										$sub.width(noMaxWidth);
8342									}
8343								}
8344							} else {
8345							 	$sub.width($sub.width());
8346							}
8347							$sub.children().css('styleFloat', 'left');
8348						}
8349					}
8350					this.menuPosition($sub);
8351					// insert IE iframe shim
8352					if ($sub.dataSM('ie-shim')) {
8353						$sub.dataSM('ie-shim').insertBefore($sub);
8354					}
8355					var complete = function() {
8356						// fix: "overflow: hidden;" is not reset on animation complete in jQuery < 1.9.0 in Chrome when global "box-sizing: border-box;" is used
8357						$sub.css('overflow', '');
8358					};
8359					// if sub is collapsible (mobile view)
8360					if (this.isCollapsible()) {
8361						if (this.opts.collapsibleShowFunction) {
8362							this.opts.collapsibleShowFunction.call(this, $sub, complete);
8363						} else {
8364							$sub.show(this.opts.collapsibleShowDuration, complete);
8365						}
8366					} else {
8367						if (this.opts.showFunction) {
8368							this.opts.showFunction.call(this, $sub, complete);
8369						} else {
8370							$sub.show(this.opts.showDuration, complete);
8371						}
8372					}
8373					// save new sub menu for this level
8374					this.visibleSubMenus[$sub.dataSM('level') - 1] = $sub;
8375					this.$root.triggerHandler('show.smapi', $sub[0]);
8376				}
8377			},
8378			popupHide: function(noHideTimeout) {
8379				if (this.hideTimeout) {
8380					clearTimeout(this.hideTimeout);
8381					this.hideTimeout = 0;
8382				}
8383				var self = this;
8384				this.hideTimeout = setTimeout(function() {
8385					self.menuHideAll();
8386				}, noHideTimeout ? 1 : this.opts.hideTimeout);
8387			},
8388			popupShow: function(left, top) {
8389				if (!this.opts.isPopup) {
8390					alert('SmartMenus jQuery Error:\n\nIf you want to show this menu via the "popupShow" method, set the isPopup:true option.');
8391					return;
8392				}
8393				if (this.hideTimeout) {
8394					clearTimeout(this.hideTimeout);
8395					this.hideTimeout = 0;
8396				}
8397				this.menuFixLayout(this.$root);
8398				this.$root.stop(true, true);
8399				if (!this.$root.is(':visible')) {
8400					this.$root.css({ left: left, top: top });
8401					// IE iframe shim
8402					this.menuIframeShim(this.$root);
8403					if (this.$root.dataSM('ie-shim')) {
8404						this.$root.dataSM('ie-shim').css({ zIndex: this.$root.css('z-index'), width: this.getWidth(this.$root), height: this.getHeight(this.$root), left
8404: left, top: top }).insertBefore(this.$root);
8405					}
8406					// show menu
8407					if (this.opts.showFunction) {
8408						this.opts.showFunction.call(this, this.$root);
8409					} else {
8410						this.$root.show(this.opts.showDuration);
8411					}
8412					this.visibleSubMenus[0] = this.$root;
8413				}
8414			},
8415			refresh: function() {
8416				this.menuHideAll();
8417				this.$root.find('ul').each(function() {
8418						var $this = $(this);
8419						if ($this.dataSM('scroll-arrows')) {
8420							$this.dataSM('scroll-arrows').remove();
8421						}
8422					})
8423					.removeDataSM('in-mega')
8424					.removeDataSM('shown-before')
8425					.removeDataSM('ie-shim')
8426					.removeDataSM('scroll-arrows')
8427					.removeDataSM('parent-a')
8428					.removeDataSM('level')
8429					.removeDataSM('beforefirstshowfired');
8430				this.$root.find('a.has-submenu').removeClass('has-submenu')
8431					.parent().removeDataSM('sub');
8432				if (this.opts.subIndicators) {
8433					this.$root.find('span.sub-arrow').remove();
8434				}
8435				if (this.opts.markCurrentItem) {
8436					this.$root.find('a.current').removeClass('current');
8437				}
8438				this.subMenus = [];
8439				this.init(true);
8440			},
8441			rootOut: function(e) {
8442				if (!this.handleEvents() || this.isTouchMode() || e.target == this.$root[0]) {
8443					return;
8444				}
8445				if (this.hideTimeout) {
8446					clearTimeout(this.hideTimeout);
8447					this.hideTimeout = 0;
8448				}
8449				if (!this.opts.showOnClick || !this.opts.hideOnClick) {
8450					var self = this;
8451					this.hideTimeout = setTimeout(function() { self.menuHideAll(); }, this.opts.hideTimeout);
8452				}
8453			},
8454			rootOver: function(e) {
8455				if (!this.handleEvents() || this.isTouchMode() || e.target == this.$root[0]) {
8456					return;
8457				}
8458				if (this.hideTimeout) {
8459					clearTimeout(this.hideTimeout);
8460					this.hideTimeout = 0;
8461				}
8462			},
8463			winResize: function(e) {
8464				if (!this.handleEvents()) {
8465					// we still need to resize the disable overlay if it's visible
8466					if (this.$disableOverlay) {
8467						var pos = this.$root.offset();
8468	 					this.$disableOverlay.css({
8469							top: pos.top,
8470							left: pos.left,
8471							width: this.$root.outerWidth(),
8472							height: this.$root.outerHeight()
8473						});
8474					}
8475					return;
8476				}
8477				// hide sub menus on resize - on mobile do it only on orientation change
8478				if (!this.isCollapsible() && (!('onorientationchange' in window) || e.type == 'orientationchange')) {
8479					if (this.activatedItems.length) {
8480						this.activatedItems[this.activatedItems.length - 1][0].blur();
8481					}
8482					this.menuHideAll();
8483				}
8484			}
8485		}
8486	});
8487
8488	$.fn.dataSM = function(key, val) {
8489		if (val) {
8490			return this.data(key + '_smartmenus', val);
8491		}
8492		return this.data(key + '_smartmenus');
8493	}
8494
8495	$.fn.removeDataSM = function(key) {
8496		return this.removeData(key + '_smartmenus');
8497	}
8498
8499	$.fn.smartmenus = function(options) {
8500		if (typeof options == 'string') {
8501			var args = arguments,
8502				method = options;
8503			Array.prototype.shift.call(args);
8504			return this.each(function() {
8505				var smartmenus = $(this).data('smartmenus');
8506				if (smartmenus && smartmenus[method]) {
8507					smartmenus[method].apply(smartmenus, args);
8508				}
8509			});
8510		}
8511		var opts = $.extend({}, $.fn.smartmenus.defaults, options);
8512		return this.each(function() {
8513			new $.SmartMenus(this, opts);
8514		});
8515	}
8516
8517	// default settings
8518	$.fn.smartmenus.defaults = {
8519		isPopup:		false,		// is this a popup menu (can be shown via the popupShow/popupHide methods) or a permanent menu bar
8520		mainMenuSubOffsetX:	0,		// pixels offset from default position
8521		mainMenuSubOffsetY:	0,		// pixels offset from default position
8522		subMenusSubOffsetX:	0,		// pixels offset from default position
8523		subMenusSubOffsetY:	0,		// pixels offset from default position
8524		subMenusMinWidth:	'10em',		// min-width for the sub menus (any CSS unit) - if set, the fixed width set in CSS will be ignored
8525		subMenusMaxWidth:	'20em',		// max-width for the sub menus (any CSS unit) - if set, the fixed width set in CSS will be ignored
8526		subIndicators: 		true,		// create sub menu indicators - creates a SPAN and inserts it in the A
8527		subIndicatorsPos: 	'prepend',	// position of the SPAN relative to the menu item content ('prepend', 'append')
8528		subIndicatorsText:	'+',		// [optionally] add text in the SPAN (e.g. '+') (you may want to check the CSS for the sub indicators too)
8529		scrollStep: 		30,		// pixels step when scrolling long sub menus that do not fit in the viewport height
8530		scrollInterval:		30,		// interval between each scrolling step
8531		scrollAccelerate:	true,		// accelerate scrolling or use a fixed step
8532		showTimeout:		250,		// timeout before showing the sub menus
8533		hideTimeout:		500,		// timeout before hiding the sub menus
8534		showDuration:		0,		// duration for show animation - set to 0 for no animation - matters only if showFunction:null
8535		showFunction:		null,		// custom function to use when showing a sub menu (the default is the jQuery 'show')
8536							// don't forget to call complete() at the end of whatever you do
8537							// e.g.: function($ul, complete) { $ul.fadeIn(250, complete); }
8538		hideDuration:		0,		// duration for hide animation - set to 0 for no animation - matters only if hideFunction:null
8539		hideFunction:		function($ul, complete) { $ul.fadeOut(200, complete); },	// custom function to use when hiding a sub menu (the default is the jQuery 'hide')
8540							// don't forget to call complete() at the end of whatever you do
8541							// e.g.: function($ul, complete) { $ul.fadeOut(250, complete); }
8542		collapsibleShowDuration:0,		// duration for show animation for collapsible sub menus - matters only if collapsibleShowFunction:null
8543		collapsibleShowFunction:function($ul, complete) { $ul.slideDown(200, complete); },	// custom function to use when showing a collapsible sub menu
8544							// (i.e. when mobile styles are used to make the sub menus collapsible)
8545		collapsibleHideDuration:0,		// duration for hide animation for collapsible sub menus - matters only if collapsibleHideFunction:null
8546		collapsibleHideFunction:function($ul, complete) { $ul.slideUp(200, complete); },	// custom function to use when hiding a collapsible sub menu
8547							// (i.e. when mobile styles are used to make the sub menus collapsible)
8548		showOnClick:		false,		// show the first-level sub menus onclick instead of onmouseover (matters only for mouse input)
8549		hideOnClick:		true,		// hide the sub menus on click/tap anywhere on the page
8550		keepInViewport:		true,		// reposition the sub menus if needed to make sure they always appear inside the viewport
8551		keepHighlighted:	true,		// keep all ancestor items of the current sub menu highlighted (adds the 'highlighted' class to the A's)
8552		markCurrentItem:	false,		// automatically add the 'current' class to the A element of the item linking to the current URL
8553		markCurrentTree:	true,		// add the 'current' class also to the A elements of all ancestor items of the current item
8554		rightToLeftSubMenus:	false,		// right to left display of the sub menus (check the CSS for the sub indicators' position)
8555		bottomToTopSubMenus:	false,		// bottom to top display of the sub menus
8556		overlapControlsInIE:	true		// make sure sub menus appear on top of special OS controls in IE (i.e. SELECT, OBJECT, EMBED, etc.)
8557	};
8558
8559})(jQuery);
8560
8561/*
8562 * jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
8563 *
8564 * Uses the built in easing capabilities added In jQuery 1.1
8565 * to offer multiple easing options
8566 *
8567 * TERMS OF USE - jQuery Easing
8568 * 
8569 * Open source under the BSD License.
8570 * 
8571 * Copyright © 2008 George McGinley Smith
8572 * All rights reserved.
8573 * 
8574 * Redistribution and use in source and binary forms, with or without modification, 
8575 * are permitted provided that the following conditions are met:
8576 * 
8577 * Redistributions of source code must retain the above copyright notice, this list of 
8578 * conditions and the following disclaimer.
8579 * Redistributions in binary form must reproduce the above copyright notice, this list 
8580 * of conditions and the following disclaimer in the documentation and/or other materials 
8581 * provided with the distribution.
8582 * 
8583 * Neither the name of the author nor the names of contributors may be used to endorse 
8584 * or promote products derived from this software without specific prior written permission.
8585 * 
8586 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
8587 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
8588 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
8589 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
vendor: 5,091 bytes, lines 8590-8735
8590 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
8591 *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
8592 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
8593 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
8594 * OF THE POSSIBILITY OF SUCH DAMAGE. 
8595 *
8596*/
8597
8598// t: current time, b: begInnIng value, c: change In value, d: duration
8599jQuery.easing['jswing'] = jQuery.easing['swing'];
8600
8601jQuery.extend( jQuery.easing,
8602{
8603	def: 'easeOutQuad',
8604	swing: function (x, t, b, c, d) {
8605		//alert(jQuery.easing.default);
8606		return jQuery.easing[jQuery.easing.def](x, t, b, c, d);
8607	},
8608	easeInQuad: function (x, t, b, c, d) {
8609		return c*(t/=d)*t + b;
8610	},
8611	easeOutQuad: function (x, t, b, c, d) {
8612		return -c *(t/=d)*(t-2) + b;
8613	},
8614	easeInOutQuad: function (x, t, b, c, d) {
8615		if ((t/=d/2) < 1) return c/2*t*t + b;
8616		return -c/2 * ((--t)*(t-2) - 1) + b;
8617	},
8618	easeInCubic: function (x, t, b, c, d) {
8619		return c*(t/=d)*t*t + b;
8620	},
8621	easeOutCubic: function (x, t, b, c, d) {
8622		return c*((t=t/d-1)*t*t + 1) + b;
8623	},
8624	easeInOutCubic: function (x, t, b, c, d) {
8625		if ((t/=d/2) < 1) return c/2*t*t*t + b;
8626		return c/2*((t-=2)*t*t + 2) + b;
8627	},
8628	easeInQuart: function (x, t, b, c, d) {
8629		return c*(t/=d)*t*t*t + b;
8630	},
8631	easeOutQuart: function (x, t, b, c, d) {
8632		return -c * ((t=t/d-1)*t*t*t - 1) + b;
8633	},
8634	easeInOutQuart: function (x, t, b, c, d) {
8635		if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
8636		return -c/2 * ((t-=2)*t*t*t - 2) + b;
8637	},
8638	easeInQuint: function (x, t, b, c, d) {
8639		return c*(t/=d)*t*t*t*t + b;
8640	},
8641	easeOutQuint: function (x, t, b, c, d) {
8642		return c*((t=t/d-1)*t*t*t*t + 1) + b;
8643	},
8644	easeInOutQuint: function (x, t, b, c, d) {
8645		if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
8646		return c/2*((t-=2)*t*t*t*t + 2) + b;
8647	},
8648	easeInSine: function (x, t, b, c, d) {
8649		return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
8650	},
8651	easeOutSine: function (x, t, b, c, d) {
8652		return c * Math.sin(t/d * (Math.PI/2)) + b;
8653	},
8654	easeInOutSine: function (x, t, b, c, d) {
8655		return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
8656	},
8657	easeInExpo: function (x, t, b, c, d) {
8658		return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
8659	},
8660	easeOutExpo: function (x, t, b, c, d) {
8661		return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
8662	},
8663	easeInOutExpo: function (x, t, b, c, d) {
8664		if (t==0) return b;
8665		if (t==d) return b+c;
8666		if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
8667		return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
8668	},
8669	easeInCirc: function (x, t, b, c, d) {
8670		return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
8671	},
8672	easeOutCirc: function (x, t, b, c, d) {
8673		return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
8674	},
8675	easeInOutCirc: function (x, t, b, c, d) {
8676		if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
8677		return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
8678	},
8679	easeInElastic: function (x, t, b, c, d) {
8680		var s=1.70158;var p=0;var a=c;
8681		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
8682		if (a < Math.abs(c)) { a=c; var s=p/4; }
8683		else var s = p/(2*Math.PI) * Math.asin (c/a);
8684		return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
8685	},
8686	easeOutElastic: function (x, t, b, c, d) {
8687		var s=1.70158;var p=0;var a=c;
8688		if (t==0) return b;  if ((t/=d)==1) return b+c;  if (!p) p=d*.3;
8689		if (a < Math.abs(c)) { a=c; var s=p/4; }
8690		else var s = p/(2*Math.PI) * Math.asin (c/a);
8691		return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
8692	},
8693	easeInOutElastic: function (x, t, b, c, d) {
8694		var s=1.70158;var p=0;var a=c;
8695		if (t==0) return b;  if ((t/=d/2)==2) return b+c;  if (!p) p=d*(.3*1.5);
8696		if (a < Math.abs(c)) { a=c; var s=p/4; }
8697		else var s = p/(2*Math.PI) * Math.asin (c/a);
8698		if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
8699		return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
8700	},
8701	easeInBack: function (x, t, b, c, d, s) {
8702		if (s == undefined) s = 1.70158;
8703		return c*(t/=d)*t*((s+1)*t - s) + b;
8704	},
8705	easeOutBack: function (x, t, b, c, d, s) {
8706		if (s == undefined) s = 1.70158;
8707		return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
8708	},
8709	easeInOutBack: function (x, t, b, c, d, s) {
8710		if (s == undefined) s = 1.70158; 
8711		if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
8712		return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
8713	},
8714	easeInBounce: function (x, t, b, c, d) {
8715		return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b;
8716	},
8717	easeOutBounce: function (x, t, b, c, d) {
8718		if ((t/=d) < (1/2.75)) {
8719			return c*(7.5625*t*t) + b;
8720		} else if (t < (2/2.75)) {
8721			return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
8722		} else if (t < (2.5/2.75)) {
8723			return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
8724		} else {
8725			return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
8726		}
8727	},
8728	easeInOutBounce: function (x, t, b, c, d) {
8729		if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
8730		return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
8731	}
8732});
8733
8734/*
8735 *
8736 * TERMS OF USE - EASING EQUATIONS
8737 * 
8738 * Open source under the BSD License. 
8739 * 
8740 * Copyright © 2001 Robert Penner
8741 * All rights reserved.
8742 * 
8743 * Redistribution and use in source and binary forms, with or without modification, 
8744 * are permitted provided that the following conditions are met:
8745 * 
8746 * Redistributions of source code must retain the above copyright notice, this list of 
8747 * conditions and the following disclaimer.
8748 * Redistributions in binary form must reproduce the above copyright notice, this list 
8749 * of conditions and the following disclaimer in the documentation and/or other materials 
8750 * provided with the distribution.
8751 * 
8752 * Neither the name of the author nor the names of contributors may be used to endorse 
8753 * or promote products derived from this software without specific prior written permission.
8754 * 
8755 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
8756 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
8757 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
8758 *  COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
8759 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
8760 *  GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 
8761 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
8762 *  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED 
8763 * OF THE POSSIBILITY OF SUCH DAMAGE. 
8764 *
8765 */
8766(function($, window, undefined) {
8767    // http://paulirish.com/2011/requestanimationframe-for-smart-animating/
8768    // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating
8769 
8770    // requestAnimationFrame polyfill by Erik Möller
8771    // fixes from Paul Irish and Tino Zijdel
8772 
8773    var lastTime = 0,
8774        running,
8775        animate = function (elem) {
8776            if (running) {
8777                window.requestAnimationFrame(animate, elem);
8778                jQuery.fx.tick();
8779            }
8780        },
8781        vendors = ['ms', 'moz', 'webkit', 'o'];
8782 
8783    for(var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
8784        window.requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
8785        window.cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame']
8786            || window[vendors[x]+'CancelRequestAnimationFrame'];
8787    }
8788 
8789    if (!window.requestAnimationFrame)
8790        window.requestAnimationFrame = function(fn, element) {
8791            var currTime = new Date().getTime(),
8792                delta = currTime - lastTime,
8793                timeToCall = Math.max(0, 16 - delta);
8794 
8795            var id = window.setTimeout(function() {
8796                    fn(currTime + timeToCall);
8797                },
8798                timeToCall
8799            );
8800 
8801            lastTime = currTime + timeToCall;
8802 
8803            return id;
8804        };
8805 
8806    if (!window.cancelAnimationFrame) {
8807        window.cancelAnimationFrame = function(id) {
8808            clearTimeout(id);
8809        };
8810    }
8811 
8812    jQuery.fx.timer = function (timer) {
8813        if (timer() && jQuery.timers.push(timer) && !running) {
8814            running = true;
8815            animate(timer.elem);
8816        }
8817    };
8818 
8819    jQuery.fx.stop = function() {
8820        running = false;
8821    };
8822 
8823}(jQuery, this));
8824/*! Copyright (c) 2011 Brandon Aaron (http://brandonaaron.net)
8825 * Licensed under the MIT License (LICENSE.txt).
8826 *
8827 * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
8828 * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
8829 * Thanks to: Seamus Leahy for adding deltaX and deltaY
8830 *
8831 * Version: 3.0.6
8832 *
8833 * Requires: 1.2.2+
8834 */
8835(function($) {
8836    var types = ['DOMMouseScroll', 'mousewheel'];
8837    if ($.event.fixHooks) {
8838        for (var i = types.length; i;) {
8839            $.event.fixHooks[types[--i]] = $.event.mouseHooks;
8840        }
8841    }
8842    $.event.special.mousewheel = {
8843        setup: function() {
8844            if (this.addEventListener) {
8845                for (var i = types.length; i;) {
8846                    this.addEventListener(types[--i], handler, false);
8847                }
8848            } else {
8849                this.onmousewheel = handler;
8850            }
8851        },
8852        teardown: function() {
8853            if (this.removeEventListener) {
8854                for (var i = types.length; i;) {
8855                    this.removeEventListener(types[--i], handler, false);
8856                }
8857            } else {
8858                this.onmousewheel = null;
8859            }
8860        }
8861    };
8862    $.fn.extend({
8863        mousewheel: function(fn) {
8864            return fn ? this.bind("mousewheel", fn) : this.trigger("mousewheel");
8865        },
8866        unmousewheel: function(fn) {
8867            return this.unbind("mousewheel", fn);
8868        }
8869    });
8870
8871    function handler(event) {
8872        var orgEvent = event || window.event,
8873            args = [].slice.call(arguments, 1),
8874            delta = 0,
8875            returnValue = true,
8876            deltaX = 0,
8877            deltaY = 0;
8878        event = $.event.fix(orgEvent);
8879        event.type = "mousewheel";
8880        // Old school scrollwheel delta
8881        if (orgEvent.wheelDelta) {
8882            delta = orgEvent.wheelDelta / 120;
8883        }
8884        if (orgEvent.detail) {
8885            delta = -orgEvent.detail / 3;
8886        }
8887        // New school multidimensional scroll (touchpads) deltas
8888        deltaY = delta;
8889        // Gecko
8890        if (orgEvent.axis !== undefined && orgEvent.axis === orgEvent.HORIZONTAL_AXIS) {
8891            deltaY = 0;
8892            deltaX = -1 * delta;
8893        }
8894        // Webkit
8895        if (orgEvent.wheelDeltaY !== undefined) {
8896            deltaY = orgEvent.wheelDeltaY / 120;
8897        }
8898        if (orgEvent.wheelDeltaX !== undefined) {
8899            deltaX = -1 * orgEvent.wheelDeltaX / 120;
8900        }
8901        // Add event and delta to the front of the arguments
8902        args.unshift(event, delta, deltaX, deltaY);
8903        return ($.event.dispatch || $.event.handle).apply(this, args);
8904    }
8905})(jQuery);
8906/**
8907 * jQuery iLightBox - Revolutionary Lightbox Plugin
8908 * http://www.ilightbox.net/
8909 *
8910 * @version: 2.2.4 - October 14, 2017
8911 *
8912 * @author: iProDev (Hemn Chawroka)
8913 *          http://www.iprodev.com/
8914 *
8915 */
8916(function($, window, undefined) {
8917
8918	var extensions = {
8919			flash: ['swf'],
8920			image: ['bmp', 'gif', 'jpeg', 'jpg', 'png', 'tiff', 'tif', 'jfif', 'jpe'],
8921			iframe: ['asp', 'aspx', 'cgi', 'cfm', 'htm', 'html', 'jsp', 'php', 'pl', 'php3', 'php4', 'php5', 'phtml', 'rb', 'rhtml', 'shtml', 'txt'],
8922			video: ['avi', 'mov', 'mpg', 'mpeg', 'movie', 'mp4', 'webm', 'ogv', 'ogg', '3gp', 'm4v']
8923		},
8924
8925		// Global DOM elements
8926		$win = $(window),
8927		$doc = $(document),
8928
8929		// Support indicators
8930		browser,
8931		transform,
8932		gpuAcceleration,
8933		fullScreenApi = '',
8934		userAgent = navigator.userAgent || navigator.vendor || window.opera,
8935        supportTouch = "ontouchstart" in window || navigator.msMaxTouchPoints,
8936        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)),
8937
8938		// Events
8939		clickEvent = supportTouch ? "itap.iLightBox" : "click.iLightBox",
8940		touchStartEvent = supportTouch ? "touchstart.iLightBox" : "mousedown.iLightBox",
8941		touchStopEvent = supportTouch ? "touchend.iLightBox" : "mouseup.iLightBox",
8942		touchMoveEvent = supportTouch ? "touchmove.iLightBox" : "mousemove.iLightBox",
8943
8944		// Math shorthands
8945		abs = Math.abs,
8946		sqrt = Math.sqrt,
8947		round = Math.round,
8948		max = Math.max,
8949		min = Math.min,
8950		floor = Math.floor,
8951		random = Math.random,
8952
8953		pluginspages = {
8954			quicktime: 'http://www.apple.com/quicktime/download',
8955			flash: 'http://www.adobe.com/go/getflash'
8956		},
8957
8958		iLightBox = function(el, options, items, instant) {
8959			var iL = this;
8960
8961			iL.options = options,
8962				iL.selector = el.selector || el,
8963				iL.context = el.context,
8964				iL.instant = instant;
8965
8966			if (items.length < 1) iL.attachItems();
8967			else iL.items = items;
8968
8969			iL.vars = {
8970				total: iL.items.length,
8971				start: 0,
8972				current: null,
8973				next: null,
8974				prev: null,
8975				BODY: $('body'),
8976				loadRequests: 0,
8977				overlay: $('<div class="ilightbox-overlay"></div>'),
8978				loader: $('<div class="ilightbox-loader"><div></div></div>'),
8979				toolbar: $('<div class="ilightbox-toolbar"></div>'),
8980				innerToolbar: $('<div class="ilightbox-inner-toolbar"></div>'),
8981				title: $('<div class="ilightbox-title"></div>'),
8982				closeButton: $('<a class="ilightbox-close" title="' + iL.options.text.close + '"></a>'),
8983				fullScreenButton: $('<a class="ilightbox-fullscreen" title="' + iL.options.text.enterFullscreen + '"></a>'),
8984				innerPlayButton: $('<a class="ilightbox-play" title="' + iL.options.text.slideShow + '"></a>'),
8985				innerNextButton: $('<a class="ilightbox-next-button" title="' + iL.options.text.next + '"></a>'),
8986				innerPrevButton: $('<a class="ilightbox-prev-button" title="' + iL.options.text.previous + '"></a>'),
8987				holder: $('<div class="ilightbox-holder' + (supportTouch ? ' supportTouch' : '') + '" ondragstart="return false;"><div class="ilightbox-container"></div></div>'),
8988				nextPhoto: $('<div class="ilightbox-holder' + (supportTouch ? ' supportTouch' : '') + ' ilightbox-next" ondragstart="return false;"><div class="ilightbox-container"></div></div>'),
8989				prevPhoto: $('<div class="ilightbox-holder' + (supportTouch ? ' supportTouch' : '') + ' ilightbox-prev" ondragstart="return false;"><div class="ilightbox-container"></div></div>'),
8990				nextButton: $('<a class="ilightbox-button ilightbox-next-button" ondragstart="return false;" title="' + iL.options.text.next + '"><span></span></a>'),
8991				prevButton: $('<a class="ilightbox-button ilightbox-prev-button" ondragstart="return false;" title="' + iL.options.text.previous + '"><span></span></a>'),
8992				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>'),
8993				thumbs: false,
8994				nextLock: false,
8995				prevLock: false,
8996				hashLock: false,
8997				isMobile: false,
8998				mobileMaxWidth: 980,
8999				isInFullScreen: false,
9000				isSwipe: false,
9001				mouseID: 0,
9002				cycleID: 0,
9003				isPaused: 0,
9004				captionHeight: 39,
9005			};
9006
9007			// Hideable elements with mousemove event
9008			iL.vars.hideableElements = iL.vars.nextButton.add(iL.vars.prevButton);
9009
9010			iL.normalizeItems();
9011
9012			//Check necessary plugins
9013			iL.availPlugins();
9014
9015			//Set startFrom
9016			iL.options.startFrom = (iL.options.startFrom > 0 && iL.options.startFrom >= iL.vars.total) ? iL.vars.total - 1 : iL.options.startFrom;
9017
9018			//If randomStart
9019			iL.options.startFrom = (iL.options.randomStart) ? floor(random() * iL.vars.total) : iL.options.startFrom;
9020			iL.vars.start = iL.options.startFrom;
9021
9022			if (instant) iL.instantCall();
9023			else iL.patchItemsEvents();
9024
9025			if (iL.options.linkId) {
9026				iL.hashChangeHandler();
9027				$win.iLightBoxHashChange(function() {
9028					iL.hashChangeHandler();
9029				});
9030			}
9031
9032			if (supportTouch) {
9033				var RegExp = /(click|mouseenter|mouseleave|mouseover|mouseout)/ig,
9034					replace = "itap";
9035				iL.options.caption.show = iL.options.caption.show.replace(RegExp, replace),
9036					iL.options.caption.hide = iL.options.caption.hide.replace(RegExp, replace),
9037					iL.options.social.show = iL.options.social.show.replace(RegExp, replace),
9038					iL.options.social.hide = iL.options.social.hide.replace(RegExp, replace);
9039			}
9040
9041			if (iL.options.controls.arrows || $(window).width() < UNCODE.mediaQuery) {
9042				$.extend(iL.options.styles, {
9043					nextOffsetX: ($(window).width() > UNCODE.mediaQuery) ? 36 : 4,
9044					prevOffsetX: ($(window).width() > UNCODE.mediaQuery) ? 36 : 4,
9045					nextOpacity: 0,
9046					prevOpacity: 0
9047				});
9048			}
9049		};
9050
9051	//iLightBox helpers
9052	iLightBox.prototype = {
9053		showLoader: function() {
9054			var iL = this;
9055			iL.vars.loadRequests += 1;
9056			if (iL.options.path.toLowerCase() == "horizontal") iL.vars.loader.addClass('ilightbox-show').stop().animate({
9057				//top: '-30px'
9058				opacity: '1'
9059			}, iL.options.show.speed, 'easeOutCirc');
9060			else iL.vars.loader.addClass('ilightbox-show').stop().animate({
9061				//left: '-30px'
9062				opacity: '1'
9063			}, iL.options.show.speed, 'easeOutCirc');
9064		},
9065
9066		hideLoader: function() {
9067			var iL = this;
9068			iL.vars.loadRequests -= 1;
9069			iL.vars.loadRequests = (iL.vars.loadRequests < 0) ? 0 : iL.vars.loadRequests;
9070			if (iL.options.path.toLowerCase() == "horizontal") {
9071				if (iL.vars.loadRequests <= 0) iL.vars.loader.removeClass('ilightbox-show').stop().animate({
9072					//top: '-192px'
9073					opacity: '0'
9074				}, iL.options.show.speed, 'easeInCirc');
9075			} else {
9076				if (iL.vars.loadRequests <= 0) iL.vars.loader.removeClass('ilightbox-show').stop().animate({
9077					//left: '-192px'
9078					opacity: '0'
9079				}, iL.options.show.speed, 'easeInCirc');
9080			}
9081		},
9082
9083		createUI: function() {
9084			var iL = this;
9085
9086			iL.ui = {
9087				currentElement: iL.vars.holder,
9088				nextElement: iL.vars.nextPhoto,
9089				prevElement: iL.vars.prevPhoto,
9090				currentItem: iL.vars.current,
9091				nextItem: iL.vars.next,
9092				prevItem: iL.vars.prev,
9093				hide: function() {
9094					iL.closeAction();
9095				},
9096				refresh: function() {
9097					(arguments.length > 0) ? iL.repositionPhoto(true): iL.repositionPhoto();
9098				},
9099				fullscreen: function() {
9100					iL.fullScreenAction();
9101				}
9102			};
9103		},
9104
9105		attachItems: function() {
9106			var iL = this,
9107				itemsObject = new Array(),
9108				items = new Array();
9109
9110			$(iL.selector, iL.context).each(function() {
9111				var t = $(this),
9112					URL = t.attr(iL.options.attr) || null,
9113					options = t.data("options") && eval("({" + t.data("options") + "})") || {},
9114					caption = t.data('caption'),
9115					title = t.data('title'),
9116					type = t.data('type') || getTypeByExtension(URL),
9117					clone = t.data('lbox-clone') || false;
9118
9119				if (t.data('lbox-clone') != undefined) return;
9120
9121				//Uncode addition ##START##
9122				if ( typeof t.attr('data-album') != 'undefined' && t.attr('data-album') != '' ) {
9123					var ALBUM_URLS = JSON.parse(t.attr('data-album')),
9124						URL_i,
9125						URLS_LENGHT = ALBUM_URLS.length;
9126					for (URL_i = 0; URL_i < URLS_LENGHT; URL_i++) {
9127						if ( typeof ALBUM_URLS[URL_i].url !== 'undefined' && ALBUM_URLS[URL_i].url != '' ) {
9128							var item_opts = 'width:' + ALBUM_URLS[URL_i].width + ',height:' + ALBUM_URLS[URL_i].height + ',thumbnail: \'' + ALBUM_URLS[URL_i].thumbnail + '\'';
9129							item_opts = item_opts && eval("({" + item_opts + "})") || {},
9130							items.push({
9131								URL: ALBUM_URLS[URL_i].url,
9132								caption: ALBUM_URLS[URL_i].caption,
9133								title: ALBUM_URLS[URL_i].title,
9134								type: getTypeByExtension(ALBUM_URLS[URL_i].url),
9135								options: item_opts,
9136								clone: clone
9137							});
9138							var newT = t;
9139							newT.attr('href',ALBUM_URLS[URL_i].url);
9140							if (!iL.instant) itemsObject.push(newT);
9141						}
9142					}
9143				} else {
9144				//Uncode addition ##END##
9145					items.push({
9146						URL: URL,
9147						caption: caption,
9148						title: title,
9149						type: type,
9150						options: options,
9151						clone: clone
9152					});
9153				}
9154
9155
9156				if (iL.vars != undefined) iL.vars.total = items.length;
9157
9158				if (!iL.instant) itemsObject.push(t);
9159			});
9160
9161			iL.items = items,
9162				iL.itemsObject = itemsObject;
9163
9164		},
9165
9166		normalizeItems: function() {
9167			var iL = this,
9168				newItems = new Array();
9169
9170			$.each(iL.items, function(key, val) {
9171
9172				if (typeof val == "string") val = {
9173					url: val
9174				};
9175
9176				var URL = val.url || val.URL || null,
9177					options = val.options || {},
9178					caption = val.caption || null,
9179					title = val.title || null,
9180					type = (val.type) ? val.type.toLowerCase() : getTypeByExtension(URL),
9181					ext = (typeof URL != 'object') ? getExtension(URL) : '';
9182
9183				options.thumbnail = options.thumbnail || ((type == "image") ? URL : null),
9184				options.videoType = options.videoType || null,
9185				options.skin = options.skin || iL.options.skin,
9186				options.width = options.width || null,
9187				options.height = options.height || null,
9188				options.mousewheel = (typeof options.mousewheel != 'undefined') ? options.mousewheel : true,
9189				options.swipe = (typeof options.swipe != 'undefined') ? options.swipe : true,
9190				options.social = (typeof options.social != 'undefined') ? options.social : iL.options.social.buttons && $.extend({}, {}, iL.options.social.buttons);
9191
9192				if (type == "video") {
9193					options.html5video = (typeof options.html5video != 'undefined') ? options.html5video : {};
9194
9195					options.html5video.webm = options.html5video.webm || options.html5video.WEBM || null;
9196					options.html5video.controls = (typeof options.html5video.controls != 'undefined') ? options.html5video.controls : "controls";
9197					options.html5video.preload = options.html5video.preload || "metadata";
9198					options.html5video.autoplay = (typeof options.html5video.autoplay != 'undefined') ? options.html5video.autoplay : false;
9199					//UNCODE addition
9200					options.html5video.loop = (typeof options.html5video.loop != 'undefined') ? options.html5video.loop : false;
9201				}
9202
9203				if (!options.width || !options.height) {
9204					if (type == "video") options.width = 1280, options.height = 720;
9205					else if (type == "iframe") options.width = '100%', options.height = '90%';
9206					else if (type == "flash") options.width = 1280, options.height = 720;
9207				}
9208
9209				delete val.url;
9210				val.index = key;
9211				val.URL = URL;
9212				val.caption = caption;
9213				val.title = title;
9214				val.type = type;
9215				val.options = options;
9216				val.ext = ext;
9217
9218				newItems.push(val);
9219			});
9220
9221			iL.items = newItems;
9222		},
9223
9224		instantCall: function() {
9225			var iL = this,
9226				key = iL.vars.start;
9227
9228			iL.vars.current = key;
9229			iL.vars.next = (iL.items[key + 1]) ? key + 1 : null;
9230			iL.vars.prev = (iL.items[key - 1]) ? key - 1 : null;
9231
9232			iL.addContents();
9233			iL.patchEvents();
9234		},
9235
9236		addContents: function() {
9237			var iL = this,
9238				vars = iL.vars,
9239				opts = iL.options,
9240				viewport = getViewport(),
9241				path = opts.path.toLowerCase(),
9242				recognizingItems = vars.total > 0 && iL.items.filter(function(e, i, arr) {
9243					return ['image', 'flash', 'video'].indexOf(e.type) === -1 && typeof e.recognized === 'undefined' && (opts.smartRecognition || e.options.smartRecognition);
9244				}),
9245				needRecognition = recognizingItems.length > 0;
9246
9247			if (opts.mobileOptimizer && !opts.innerToolbar)
9248				vars.isMobile = viewport.width <= vars.mobileMaxWidth;
9249
9250			vars.overlay.addClass(opts.skin).hide().css('opacity', opts.overlay.opacity);
9251
9252			if (opts.linkId)
9253				vars.overlay[0].setAttribute('linkid', opts.linkId);
9254
9255			//Add Toolbar Buttons
9256			if (opts.controls.toolbar) {
9257				vars.toolbar.addClass(opts.skin).append(vars.closeButton);
9258				if (opts.controls.fullscreen)
9259					vars.toolbar.append(vars.fullScreenButton);
9260				if (opts.controls.slideshow)
9261					vars.toolbar.append(vars.innerPlayButton);
9262				if (vars.total > 1)
9263					vars.toolbar.append(vars.innerPrevButton).append(vars.innerNextButton);
9264			}
9265
9266			//Append elements to body
9267			vars.BODY.addClass('ilightbox-noscroll').append(vars.overlay).append(vars.loader).append(vars.holder).append(vars.nextPhoto).append(vars.prevPhoto);
9268
9269			if (!opts.innerToolbar)
9270				vars.BODY.append(vars.toolbar);
9271			if (opts.controls.arrows)
9272				vars.BODY.append(vars.nextButton).append(vars.prevButton);
9273
9274			if (opts.controls.thumbnail && vars.total > 1) {
9275				vars.BODY.append(vars.thumbnails);
9276				vars.thumbnails.addClass(opts.skin).addClass('ilightbox-' + path);
9277				$('div.ilightbox-thumbnails-grid', vars.thumbnails).empty();
9278				vars.thumbs = true;
9279			}
9280
9281			//Configure loader and arrows
9282			var loaderCss = (opts.path.toLowerCase() == "horizontal") ? {
9283				left: parseInt((viewport.width / 2) - (vars.loader.outerWidth() / 2))
9284			} : {
9285				top: parseInt((viewport.height / 2) - (vars.loader.outerHeight() / 2))
9286			};
9287			vars.loader.addClass(opts.skin).css(loaderCss);
9288			vars.nextButton.add(vars.prevButton).addClass(opts.skin);
9289			if (path == "horizontal")
9290				vars.loader.add(vars.nextButton).add(vars.prevButton).addClass('horizontal');
9291
9292			// Configure arrow buttons
9293			vars.BODY[vars.isMobile ? 'addClass' : 'removeClass']('isMobile');
9294
9295			if (!opts.infinite) {
9296				vars.prevButton.add(vars.prevButton).add(vars.innerPrevButton).add(vars.innerNextButton).removeClass('disabled');
9297
9298				if (vars.current == 0)
9299					vars.prevButton.add(vars.innerPrevButton).addClass('disabled');
9300				if (vars.current >= vars.total - 1)
9301					vars.nextButton.add(vars.innerNextButton).addClass('disabled');
9302			}
9303
9304			if (opts.show.effect) {
9305				vars.overlay.stop().fadeIn(opts.show.speed);
9306				vars.toolbar.stop().fadeIn(opts.show.speed);
9307			} else {
9308				vars.overlay.show();
9309				vars.toolbar.show();
9310			}
9311
9312			var length = recognizingItems.length;
9313			if (needRecognition) {
9314				iL.showLoader();
9315
9316				$.each(recognizingItems, function(key, val) {
9317					var resultFnc = function(result) {
9318							var key = -1,
9319								filter = iL.items.filter(function(e, i, arr) {
9320									if (e.URL == result.url)
9321										key = i;
9322
9323									return e.URL == result.url;
9324								}),
9325								self = iL.items[key];
9326
9327							if (result)
9328								$.extend(true, self, {
9329									URL: result.source,
9330									type: result.type,
9331									recognized: true,
9332									options: {
9333										html5video: result.html5video,
9334										width: (result.type == "image") ? 0 : (result.width || self.width),
9335										height: (result.type == "image") ? 0 : (result.height || self.height),
9336										thumbnail: self.options.thumbnail || result.thumbnail
9337									}
9338								});
9339
9340							length--;
9341
9342							if (length == 0) {
9343								iL.hideLoader();
9344
9345								vars.dontGenerateThumbs = false;
9346								iL.generateThumbnails();
9347
9348								if (opts.show.effect)
9349									setTimeout(function() {
9350										iL.generateBoxes();
9351									}, opts.show.speed);
9352								else
9353									iL.generateBoxes();
9354							}
9355						};
9356
9357					iL.ogpRecognition(this, resultFnc);
9358				});
9359			}
9360			else {
9361				if (opts.show.effect)
9362					setTimeout(function() {
9363						iL.generateBoxes();
9364					}, opts.show.speed);
9365				else
9366					iL.generateBoxes();
9367			}
9368
9369			iL.createUI();
9370
9371			window.iLightBox = {
9372				close: function() {
9373					iL.closeAction();
9374				},
9375				fullscreen: function() {
9376					iL.fullScreenAction();
9377				},
9378				moveNext: function() {
9379					iL.moveTo('next');
9380				},
9381				movePrev: function() {
9382					iL.moveTo('prev');
9383				},
9384				goTo: function(index) {
9385					iL.goTo(index);
9386				},
9387				refresh: function() {
9388					iL.refresh();
9389				},
9390				reposition: function() {
9391					(arguments.length > 0) ? iL.repositionPhoto(true): iL.repositionPhoto();
9392				},
9393				setOption: function(options) {
9394					iL.setOption(options);
9395				},
9396				destroy: function() {
9397					iL.closeAction();
9398					iL.dispatchItemsEvents();
9399				}
9400			};
9401
9402			if (opts.linkId) {
9403				vars.hashLock = true;
9404				window.location.hash = opts.linkId + '/' + vars.current;
9405				setTimeout(function() {
9406					vars.hashLock = false;
9407				}, 55);
9408			}
9409
9410			if (!opts.slideshow.startPaused) {
9411				iL.resume();
9412				vars.innerPlayButton.removeClass('ilightbox-play').addClass('ilightbox-pause');
9413			}
9414
9415			//Trigger the onOpen callback
9416			if (typeof iL.options.callback.onOpen == 'function') iL.options.callback.onOpen.call(iL);
9417		},
9418
9419		loadContent: function(obj, opt, speed) {
9420			var iL = this,
9421				holder, item;
9422
9423			iL.createUI();
9424
9425			obj.speed = speed || iL.options.effects.loadedFadeSpeed;
9426
9427			if (opt == 'current') {
9428				if (!obj.options.mousewheel) iL.vars.lockWheel = true;
9429				else iL.vars.lockWheel = false;
9430
9431				if (!obj.options.swipe) iL.vars.lockSwipe = true;
9432				else iL.vars.lockSwipe = false;
9433			}
9434
9435			switch (opt) {
9436				case 'current':
9437					holder = iL.vars.holder, item = iL.vars.current;
9438					break;
9439				case 'next':
9440					holder = iL.vars.nextPhoto, item = iL.vars.next;
9441					break;
9442				case 'prev':
9443					holder = iL.vars.prevPhoto, item = iL.vars.prev;
9444					break;
9445			}
9446
9447			holder.removeAttr('style class').addClass('ilightbox-holder' + (supportTouch ? ' supportTouch' : '')).addClass(obj.options.skin);
9448			$('div.ilightbox-inner-toolbar', holder).remove();
9449
9450			if (obj.title || iL.options.innerToolbar) {
9451				var innerToolbar = iL.vars.innerToolbar.clone();
9452				if (obj.title && iL.options.show.title) {
9453					var title = iL.vars.title.clone();
9454					title.empty().html(obj.title);
9455					innerToolbar.append(title);
9456				}
9457				if (iL.options.innerToolbar) {
9458					innerToolbar.append((iL.vars.total > 1) ? iL.vars.toolbar.clone() : iL.vars.toolbar);
9459				}
9460				holder.prepend(innerToolbar);
9461			}
9462
9463			//console.warn('loadContent', arguments);
9464
9465			iL.loadSwitcher(obj, holder, item, opt);
9466		},
9467
9468		loadSwitcher: function(obj, holder, item, opt) {
9469			var iL = this,
9470				opts = iL.options,
9471				api = {
9472					element: holder,
9473					position: item
9474				};
9475
9476			switch (obj.type) {
9477				case 'image':
9478					//Trigger the onBeforeLoad callback
9479					if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
9480					if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
9481
9482					iL.loadImage(obj.URL, function(img) {
9483						//Trigger the onAfterLoad callback
9484						if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
9485						if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
9486
9487						var width = (img) ? img.width : 400,
9488							height = (img) ? img.height : 200;
9489
9490						holder.data({
9491							naturalWidth: width,
9492							naturalHeight: height
9493						});
9494						$('div.ilightbox-container', holder).empty().append((img) ? '<img src="' + obj.URL + '" class="ilightbox-image" />' : '<span class="ilightbox-alert">' + opts.errors.loadImage + '</span>');
9495
9496						//Trigger the onRender callback
9497						if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
9498						if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
9499
9500						iL.configureHolder(obj, opt, holder);
9501					});
9502
9503					break;
9504
9505				case 'video':
9506					holder.data({
9507						naturalWidth: obj.options.width,
9508						naturalHeight: obj.options.height
9509					});
9510
9511					if (opt === 'current') {
9512						iL.addContent(holder, obj);
9513
9514						//Trigger the onRender callback
9515						if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
9516						if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
9517					} else {
9518						$('div.ilightbox-container', holder).empty();
9519					}
9520
9521					iL.configureHolder(obj, opt, holder);
9522
9523					break;
9524
9525				case 'iframe':
9526					//iL.showLoader();
9527					holder.data({
9528						naturalWidth: obj.options.width,
9529						naturalHeight: obj.options.height
9530					});
9531
9532					iL.configureHolder(obj, opt, holder);
9533
9534					if (opt === 'current') {
9535						var el = iL.addContent(holder, obj);
9536
9537						//Trigger the onRender callback
9538						if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
9539						if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
9540
9541						//Trigger the onBeforeLoad callback
9542						if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
9543						if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
9544
9545						el.bind('load', function() {
9546							//Trigger the onAfterLoad callback
9547							if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
9548							if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
9549
9550							//iL.hideLoader();
9551							el.unbind('load');
9552						});
9553					} else {
9554						$('div.ilightbox-container', holder).empty();
9555					}
9556
9557					break;
9558
9559				case 'inline':
9560					var el = $(obj.URL),
9561						content = iL.addContent(holder, obj),
9562						images = findImageInElement(holder);
9563
9564					holder.data({
9565						naturalWidth: (iL.items[item].options.width || el.outerWidth()),
9566						naturalHeight: (iL.items[item].options.height || el.outerHeight())
9567					});
9568					content.children().eq(0).show();
9569
9570					//Trigger the onRender callback
9571					if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
9572					if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
9573
9574					//Trigger the onBeforeLoad callback
9575					if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
9576					if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
9577
9578					iL.loadImage(images, function() {
9579						//Trigger the onAfterLoad callback
9580						if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
9581						if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
9582
9583						iL.configureHolder(obj, opt, holder);
9584					});
9585
9586					break;
9587
9588				case 'flash':
9589					var el = iL.addContent(holder, obj);
9590
9591					holder.data({
9592						naturalWidth: (iL.items[item].options.width || el.outerWidth()),
9593						naturalHeight: (iL.items[item].options.height || el.outerHeight())
9594					});
9595
9596					//Trigger the onRender callback
9597					if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
9598					if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
9599
9600					iL.configureHolder(obj, opt, holder);
9601
9602					break;
9603
9604				case 'ajax':
9605					var ajax = obj.options.ajax || {};
9606					//Trigger the onBeforeLoad callback
9607					if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
9608					if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
9609
9610					iL.showLoader();
9611					$.ajax({
9612						url: obj.URL || opts.ajaxSetup.url,
9613						data: ajax.data || null,
9614						dataType: ajax.dataType || "html",
9615						type: ajax.type || opts.ajaxSetup.type,
9616						cache: ajax.cache || opts.ajaxSetup.cache,
9617						crossDomain: ajax.crossDomain || opts.ajaxSetup.crossDomain,
9618						global: ajax.global || opts.ajaxSetup.global,
9619						ifModified: ajax.ifModified || opts.ajaxSetup.ifModified,
9620						username: ajax.username || opts.ajaxSetup.username,
9621						password: ajax.password || opts.ajaxSetup.password,
9622						beforeSend: ajax.beforeSend || opts.ajaxSetup.beforeSend,
9623						complete: ajax.complete || opts.ajaxSetup.complete,
9624						success: function(data, textStatus, jqXHR) {
9625							iL.hideLoader();
9626
9627							var el = $(data),
9628								container = $('div.ilightbox-container', holder),
9629								elWidth = iL.items[item].options.width || parseInt(el[0].getAttribute('width')),
9630								elHeight = iL.items[item].options.height || parseInt(el[0].getAttribute('height')),
9631								css = (el[0].getAttribute('width') && el[0].getAttribute('height')) ? {
9632									'overflow': 'hidden'
9633								} : {};
9634
9635							container.empty().append($('<div class="ilightbox-wrapper"></div>').css(css).html(el));
9636							holder.show().data({
9637								naturalWidth: (elWidth || container.outerWidth()),
9638								naturalHeight: (elHeight || container.outerHeight())
9639							}).hide();
9640
9641							//Trigger the onRender callback
9642							if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
9643							if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
9644
9645							var images = findImageInElement(holder);
9646							iL.loadImage(images, function() {
9647								//Trigger the onAfterLoad callback
9648								if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
9649								if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
9650
9651								iL.configureHolder(obj, opt, holder);
9652							});
9653
9654							opts.ajaxSetup.success(data, textStatus, jqXHR);
9655							if (typeof ajax.success == 'function') ajax.success(data, textStatus, jqXHR);
9656						},
9657						error: function(jqXHR, textStatus, errorThrown) {
9658							//Trigger the onAfterLoad callback
9659							if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
9660							if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
9661
9662							iL.hideLoader();
9663							$('div.ilightbox-container', holder).empty().append('<span class="ilightbox-alert">' + opts.errors.loadContents + '</span>');
9664							iL.configureHolder(obj, opt, holder);
9665
9666							opts.ajaxSetup.error(jqXHR, textStatus, errorThrown);
9667							if (typeof ajax.error == 'function') ajax.error(jqXHR, textStatus, errorThrown);
9668						}
9669					});
9670
9671					break;
9672
9673				case 'html':
9674					var object = obj.URL,
9675						el
9676					container = $('div.ilightbox-container', holder);
9677
9678					if (object[0].nodeName) el = object.clone();
9679					else {
9680						var dom = $(object);
9681						if (dom.selector) el = $('<div>' + dom + '</div>');
9682						else el = dom;
9683					}
9684
9685					var elWidth = iL.items[item].options.width || parseInt(el.attr('width')),
9686						elHeight = iL.items[item].options.height || parseInt(el.attr('height'));
9687
9688					iL.addContent(holder, obj);
9689
9690					el.appendTo(document.documentElement).hide();
9691
9692					//Trigger the onRender callback
9693					if (typeof opts.callback.onRender == 'function') opts.callback.onRender.call(iL, iL.ui, item);
9694					if (typeof obj.options.onRender == 'function') obj.options.onRender.call(iL, api);
9695
9696					var images = findImageInElement(holder);
9697
9698					//Trigger the onBeforeLoad callback
9699					if (typeof opts.callback.onBeforeLoad == 'function') opts.callback.onBeforeLoad.call(iL, iL.ui, item);
9700					if (typeof obj.options.onBeforeLoad == 'function') obj.options.onBeforeLoad.call(iL, api);
9701
9702					iL.loadImage(images, function() {
9703						//Trigger the onAfterLoad callback
9704						if (typeof opts.callback.onAfterLoad == 'function') opts.callback.onAfterLoad.call(iL, iL.ui, item);
9705						if (typeof obj.options.onAfterLoad == 'function') obj.options.onAfterLoad.call(iL, api);
9706
9707						holder.show().data({
9708							naturalWidth: (elWidth || container.outerWidth()),
9709							naturalHeight: (elHeight || container.outerHeight())
9710						}).hide();
9711						el.remove();
9712
9713						iL.configureHolder(obj, opt, holder);
9714					});
9715
9716					break;
9717			}
9718		},
9719
9720		configureHolder: function(obj, opt, holder) {
9721			var iL = this,
9722				vars = iL.vars,
9723				opts = iL.options;
9724
9725			if (opt != "current")(opt == "next") ? holder.addClass('ilightbox-next') : holder.addClass('ilightbox-prev');
9726
9727			if (opt == "current")
9728				var item = vars.current;
9729			else if (opt == "next")
9730				var opacity = opts.styles.nextOpacity,
9731					item = vars.next;
9732			else
9733				var opacity = opts.styles.prevOpacity,
9734					item = vars.prev;
9735
9736			var api = {
9737				element: holder,
9738				position: item
9739			};
9740
9741			iL.items[item].options.width = iL.items[item].options.width || 0,
9742			iL.items[item].options.height = iL.items[item].options.height || 0;
9743
9744			if (opt == "current") {
9745				if (opts.show.effect) holder.css(transform, gpuAcceleration).fadeIn(obj.speed, function() {
9746					holder.css(transform, '');
9747					if (obj.caption) {
9748						iL.setCaption(obj, holder);
9749						var caption = $('div.ilightbox-caption', holder),
9750							percent = parseInt((caption.outerHeight() / holder.outerHeight()) * 100);
9751						if (opts.caption.start & percent <= 50) caption.fadeIn(opts.effects.fade
9751Speed);
9752					}
9753
9754					var social = obj.options.social;
9755					if (social) {
9756						iL.setSocial(social, obj.URL, holder);
9757						if (opts.social.start) $('div.ilightbox-social', holder).fadeIn(opts.effects.fadeSpeed);
9758					}
9759
9760					//Generate thumbnails
9761					iL.generateThumbnails();
9762
9763					//Trigger the onShow callback
9764					if (typeof opts.callback.onShow == 'function') opts.callback.onShow.call(iL, iL.ui, item);
9765					if (typeof obj.options.onShow == 'function') obj.options.onShow.call(iL, api);
9766				});
9767				else {
9768					holder.show();
9769
9770					//Generate thumbnails
9771					iL.generateThumbnails();
9772
9773					//Trigger the onShow callback
9774					if (typeof opts.callback.onShow == 'function') opts.callback.onShow.call(iL, iL.ui, item);
9775					if (typeof obj.options.onShow == 'function') obj.options.onShow.call(iL, api);
9776				}
9777			} else {
9778				if (opts.show.effect) holder.fadeTo(obj.speed, opacity, function() {
9779					if (opt == "next") vars.nextLock = false;
9780					else vars.prevLock = false;
9781
9782					//Generate thumbnails
9783					iL.generateThumbnails();
9784
9785					//Trigger the onShow callback
9786					if (typeof opts.callback.onShow == 'function') opts.callback.onShow.call(iL, iL.ui, item);
9787					if (typeof obj.options.onShow == 'function') obj.options.onShow.call(iL, api);
9788				});
9789				else {
9790					holder.css({
9791						opacity: opacity
9792					}).show();
9793					if (opt == "next") vars.nextLock = false;
9794					else vars.prevLock = false;
9795
9796					//Generate thumbnails
9797					iL.generateThumbnails();
9798
9799					//Trigger the onShow callback
9800					if (typeof opts.callback.onShow == 'function') opts.callback.onShow.call(iL, iL.ui, item);
9801					if (typeof obj.options.onShow == 'function') obj.options.onShow.call(iL, api);
9802				}
9803			}
9804
9805			setTimeout(function() {
9806				iL.repositionPhoto();
9807			}, 0);
9808		},
9809
9810		generateBoxes: function() {
9811			var iL = this,
9812				vars = iL.vars,
9813				opts = iL.options;
9814
9815			if (opts.infinite && vars.total >= 3) {
9816				if (vars.current == vars.total - 1) vars.next = 0;
9817				if (vars.current == 0) vars.prev = vars.total - 1;
9818			} else opts.infinite = false;
9819
9820			//UNCODE addition
9821			if ( typeof iL.items[vars.current] == 'undefined' )
9822				vars.current = 0;
9823
9824			iL.loadContent(iL.items[vars.current], 'current', opts.show.speed);
9825
9826			if (iL.items[vars.next]) iL.loadContent(iL.items[vars.next], 'next', opts.show.speed);
9827			if (iL.items[vars.prev]) iL.loadContent(iL.items[vars.prev], 'prev', opts.show.speed);
9828		},
9829
9830		generateThumbnails: function() {
9831			var iL = this,
9832				vars = iL.vars,
9833				opts = iL.options,
9834				timeOut = null;
9835
9836			if (vars.thumbs && !iL.vars.dontGenerateThumbs) {
9837				var thumbnails = vars.thumbnails,
9838					container = $('div.ilightbox-thumbnails-container', thumbnails),
9839					grid = $('div.ilightbox-thumbnails-grid', container),
9840					i = 0;
9841
9842				grid.removeAttr('style').empty();
9843
9844				$.each(iL.items, function(key, val) {
9845					var isActive = (vars.current == key) ? 'ilightbox-active' : '',
9846						opacity = (vars.current == key) ? opts.thumbnails.activeOpacity : opts.thumbnails.normalOpacity,
9847						thumb = val.options.thumbnail,
9848						thumbnail = $('<div class="ilightbox-thumbnail"></div>'),
9849						icon = $('<div class="ilightbox-thumbnail-icon"></div>');
9850
9851					thumbnail.css({
9852						opacity: 0
9853					}).addClass(isActive);
9854
9855					if ((val.type == "video" || val.type == "flash") && typeof val.options.icon == 'undefined') {
9856						icon.addClass('ilightbox-thumbnail-video');
9857						thumbnail.append(icon);
9858					} else if (val.options.icon) {
9859						icon.addClass('ilightbox-thumbnail-' + val.options.icon);
9860						thumbnail.append(icon);
9861					}
9862
9863					if (thumb) iL.loadImage(thumb, function(img) {
9864						i++;
9865						if (img) thumbnail.data({
9866							naturalWidth: img.width,
9867							naturalHeight: img.height
9868						}).append('<img src="' + thumb + '" border="0" />');
9869						else thumbnail.data({
9870							naturalWidth: opts.thumbnails.maxWidth,
9871							naturalHeight: opts.thumbnails.maxHeight
9872						});
9873
9874						clearTimeout(timeOut);
9875						timeOut = setTimeout(function() {
9876							iL.positionThumbnails(thumbnails, container, grid);
9877						}, 20);
9878						setTimeout(function() {
9879							thumbnail.fadeTo(opts.effects.loadedFadeSpeed, opacity);
9880						}, i * 20);
9881					});
9882
9883					grid.append(thumbnail);
9884				});
9885				iL.vars.dontGenerateThumbs = true;
9886			}
9887		},
9888
9889		positionThumbnails: function(thumbnails, container, grid) {
9890			var iL = this,
9891				vars = iL.vars,
9892				opts = iL.options,
9893				viewport = getViewport(),
9894				path = opts.path.toLowerCase();
9895
9896			if (!thumbnails) thumbnails = vars.thumbnails;
9897			if (!container) container = $('div.ilightbox-thumbnails-container', thumbnails);
9898			if (!grid) grid = $('div.ilightbox-thumbnails-grid', container);
9899
9900			var thumbs = $('.ilightbox-thumbnail', grid),
9901				widthAvail = (path == 'horizontal') ? viewport.width - opts.styles.pageOffsetX : thumbs.eq(0).outerWidth() - opts.styles.pageOffsetX,
9902				heightAvail = (path == 'horizontal') ? thumbs.eq(0).outerHeight() - opts.styles.pageOffsetY : viewport.height - opts.styles.pageOffsetY,
9903				gridWidth = (path == 'horizontal') ? 0 : widthAvail,
9904				gridHeight = (path == 'horizontal') ? heightAvail : 0,
9905				active = $('.ilightbox-active', grid),
9906				gridCss = {},
9907				css = {};
9908
9909			if (arguments.length < 3) {
9910				thumbs.css({
9911					opacity: opts.thumbnails.normalOpacity
9912				});
9913				active.css({
9914					opacity: opts.thumbnails.activeOpacity
9915				});
9916			}
9917
9918			thumbs.each(function(i) {
9919				var t = $(this),
9920					data = t.data(),
9921					width = (path == 'horizontal') ? 0 : opts.thumbnails.maxWidth;
9922				height = (path == 'horizontal') ? opts.thumbnails.maxHeight : 0;
9923				dims = iL.getNewDimenstions(width, height, data.naturalWidth, data.naturalHeight, true);
9924
9925				t.css({
9926					width: dims.width,
9927					height: dims.height
9928				});
9929				if (path == 'horizontal') t.css({
9930					'float': 'left'
9931				});
9932
9933				(path == 'horizontal') ? (
9934					gridWidth += t.outerWidth()
9935				) : (
9936					gridHeight += t.outerHeight()
9937				);
9938			});
9939
9940			gridCss = {
9941				width: gridWidth,
9942				height: gridHeight
9943			};
9944
9945			grid.css(gridCss);
9946
9947			gridCss = {};
9948
9949			var gridOffset = grid.offset(),
9950				activeOffset = (active.length) ? active.offset() : {
9951					top: parseInt(heightAvail / 2),
9952					left: parseInt(widthAvail / 2)
9953				};
9954
9955			gridOffset.top = (gridOffset.top - $doc.scrollTop()),
9956				gridOffset.left = (gridOffset.left - $doc.scrollLeft()),
9957				activeOffset.top = (activeOffset.top - gridOffset.top - $doc.scrollTop()),
9958				activeOffset.left = (activeOffset.left - gridOffset.left - $doc.scrollLeft());
9959
9960			(path == 'horizontal') ? (
9961				gridCss.top = 0,
9962				gridCss.left = parseInt((widthAvail / 2) - activeOffset.left - (active.outerWidth() / 2))
9963			) : (
9964				gridCss.top = parseInt(((heightAvail / 2) - activeOffset.top - (active.outerHeight() / 2))),
9965				gridCss.left = 0
9966			);
9967
9968			if (arguments.length < 3) grid.stop().animate(gridCss, opts.effects.repositionSpeed, 'easeOutCirc');
9969			else grid.css(gridCss);
9970		},
9971
9972		loadImage: function(image, callback) {
9973			if (!$.isArray(image)) image = [image];
9974
9975			var iL = this,
9976				length = image.length;
9977
9978			if (length > 0) {
9979				iL.showLoader();
9980				$.each(image, function(index, value) {
9981					var img = new Image();
9982					img.onload = function() {
9983						length -= 1;
9984						if (length == 0) {
9985							iL.hideLoader();
9986							callback(img);
9987						}
9988					};
9989					img.onerror = img.onabort = function() {
9990						length -= 1;
9991						if (length == 0) {
9992							iL.hideLoader();
9993							callback(false);
9994						}
9995					};
9996					img.src = image[index];
9997				});
9998			} else callback(false);
9999		},
10000
10001		patchItemsEvents: function() {
10002			var iL = this,
10003				vars = iL.vars,
10004				clickEvent = supportTouch ? "itap.iL" : "click.iL",
10005				vEvent = supportTouch ? "click.iL" : "itap.iL";
10006
10007			if (iL.context && iL.selector) {
10008				var $items = $(iL.selector, iL.context);
10009
10010				$(iL.context).on(clickEvent, iL.selector, function() {
10011					var $this = $(this),
10012						//UNCODE addition
10013						key = typeof $(this).data('lb-index') !== '' && $(this).closest('.owl-item').length ? $(this).data('lb-index') : $items.index($this);
10014					if (UNCODE.isMobile) {
10015						var isCarousel = $this.closest('.owl-carousel');
10016						if (isCarousel.length) {
10017							if (isCarousel.hasClass('owl-translating')) return false;
10018						}
10019					}
10020
10021					vars.current = key;
10022					vars.next = iL.items[key + 1] ? key + 1 : null;
10023					vars.prev = iL.items[key - 1] ? key - 1 : null;
10024
10025					//UNCODE addition
10026					if ( vars.BODY.hasClass('fp-slide-scrolling') )
10027						return false;
10028
10029					iL.addContents();
10030					iL.patchEvents();
10031
10032					return false;
10033				}).on(vEvent, iL.selector, function() {
10034					return false;
10035				});
10036			} else
10037				$.each(iL.itemsObject, function(key, val) {
10038					val.on(clickEvent, function() {
10039						vars.current = key;
10040						vars.next = iL.items[key + 1] ? key + 1 : null;
10041						vars.prev = iL.items[key - 1] ? key - 1 : null;
10042
10043						iL.addContents();
10044						iL.patchEvents();
10045
10046						return false;
10047					}).on(vEvent, function() {
10048						return false;
10049					});
10050				});
10051		},
10052
10053		dispatchItemsEvents: function() {
10054			var iL = this,
10055				vars = iL.vars,
10056				opts = iL.options;
10057
10058			if (iL.context && iL.selector)
10059				$(iL.context).off('.iL', iL.selector);
10060			else
10061				$.each(iL.itemsObject, function(key, val) {
10062					val.off('.iL');
10063				});
10064		},
10065
10066		refresh: function() {
10067			var iL = this;
10068
10069			iL.dispatchItemsEvents();
10070			iL.attachItems();
10071			iL.normalizeItems();
10072			iL.patchItemsEvents();
10073		},
10074
10075		patchEvents: function() {
10076			var iL = this,
10077				vars = iL.vars,
10078				opts = iL.options,
10079				path = opts.path.toLowerCase(),
10080				holders = $('.ilightbox-holder'),
10081				fullscreenEvent = fullScreenApi.fullScreenEventName + '.iLightBox',
10082				durationThreshold = 1000,
10083				horizontalDistanceThreshold =
10084				verticalDistanceThreshold = 100,
10085				buttonsArray = [vars.nextButton[0], vars.prevButton[0], vars.nextButton[0].firstChild, vars.prevButton[0].firstChild];
10086
10087			$win.bind('resize.iLightBox', function() {
10088				var viewport = getViewport();
10089
10090				if (opts.mobileOptimizer && !opts.innerToolbar) vars.isMobile = viewport.width <= vars.mobileMaxWidth;
10091				vars.BODY[vars.isMobile ? 'addClass' : 'removeClass']('isMobile');
10092
10093				iL.repositionPhoto(null);
10094				if (supportTouch) {
10095					clearTimeout(vars.setTime);
10096					vars.setTime = setTimeout(function() {
10097						var scrollTop = getScrollXY().y;
10098						window.scrollTo(0, scrollTop - 30);
10099						window.scrollTo(0, scrollTop + 30);
10100						window.scrollTo(0, scrollTop);
10101					}, 2000);
10102				}
10103				if (vars.thumbs) iL.positionThumbnails();
10104			}).bind('keydown.iLightBox', function(event) {
10105				if (opts.controls.keyboard) {
10106					switch (event.keyCode) {
10107						case 13:
10108							if (event.shiftKey && opts.keyboard.shift_enter) iL.fullScreenAction();
10109							break;
10110						case 27:
10111							if (opts.keyboard.esc) iL.closeAction();
10112							break;
10113						case 37:
10114							if (opts.keyboard.left && !vars.lockKey) iL.moveTo('prev');
10115							break;
10116						case 38:
10117							if (opts.keyboard.up && !vars.lockKey) iL.moveTo('prev');
10118							break;
10119						case 39:
10120							if (opts.keyboard.right && !vars.lockKey) iL.moveTo('next');
10121							break;
10122						case 40:
10123							if (opts.keyboard.down && !vars.lockKey) iL.moveTo('next');
10124							break;
10125					}
10126				}
10127			});
10128
10129			if (fullScreenApi.supportsFullScreen) $win.bind(fullscreenEvent, function() {
10130				iL.doFullscreen();
10131			});
10132
10133			var holderEventsArr = [opts.caption.show + '.iLightBox', opts.caption.hide + '.iLightBox', opts.social.show + '.iLightBox', opts.social.hide + '.iLightBox'].filter(function(e, i, arr) {
10134					return arr.lastIndexOf(e) === i;
10135				}),
10136				holderEvents = "";
10137
10138			$.each(holderEventsArr, function(key, val) {
10139				if (key != 0) holderEvents += ' ';
10140				holderEvents += val;
10141			});
10142
10143			$doc.on(clickEvent, '.ilightbox-overlay', function() {
10144				if (opts.overlay.blur) iL.closeAction();
10145			}).on(clickEvent, '.ilightbox-next, .ilightbox-next-button', function() {
10146				iL.moveTo('next');
10147			}).on(clickEvent, '.ilightbox-prev, .ilightbox-prev-button', function() {
10148				iL.moveTo('prev');
10149			}).on(clickEvent, '.ilightbox-thumbnail', function() {
10150				var t = $(this),
10151					thumbs = $('.ilightbox-thumbnail', vars.thumbnails),
10152					index = thumbs.index(t);
10153
10154				if (index != vars.current) iL.goTo(index);
10155			}).on(holderEvents, '.ilightbox-holder:not(.ilightbox-next, .ilightbox-prev)', function(e) {
10156				var caption = $('div.ilightbox-caption', vars.holder),
10157					social = $('div.ilightbox-social', vars.holder),
10158					fadeSpeed = opts.effects.fadeSpeed;
10159
10160				if (vars.nextLock || vars.prevLock) {
10161					if (e.type == opts.caption.show && !caption.is(':visible')) caption.fadeIn(fadeSpeed);
10162					else if (e.type == opts.caption.hide && caption.is(':visible')) caption.fadeOut(fadeSpeed);
10163
10164					if (e.type == opts.social.show && !social.is(':visible')) social.fadeIn(fadeSpeed);
10165					else if (e.type == opts.social.hide && social.is(':visible')) social.fadeOut(fadeSpeed);
10166				} else {
10167					if (e.type == opts.caption.show && !caption.is(':visible')) caption.stop().fadeIn(fadeSpeed);
10168					else if (e.type == opts.caption.hide && caption.is(':visible')) caption.stop().fadeOut(fadeSpeed);
10169
10170					if (e.type == opts.social.show && !social.is(':visible')) social.stop().fadeIn(fadeSpeed);
10171					else if (e.type == opts.social.hide && social.is(':visible')) social.stop().fadeOut(fadeSpeed);
10172				}
10173			}).on('mouseenter.iLightBox mouseleave.iLightBox', '.ilightbox-wrapper', function(e) {
10174				if (e.type == 'mouseenter') vars.lockWheel = true;
10175				else vars.lockWheel = false;
10176			}).on(clickEvent, '.ilightbox-toolbar a.ilightbox-close, .ilightbox-toolbar a.ilightbox-fullscreen, .ilightbox-toolbar a.ilightbox-play, .ilightbox-toolbar a.ilightbox-pause', function() {
10177				var t = $(this);
10178
10179				if (t.hasClass('ilightbox-fullscreen')) iL.fullScreenAction();
10180				else if (t.hasClass('ilightbox-play')) {
10181					iL.resume();
10182					t.addClass('ilightbox-pause').removeClass('ilightbox-play');
10183				} else if (t.hasClass('ilightbox-pause')) {
10184					iL.pause();
10185					t.addClass('ilightbox-play').removeClass('ilightbox-pause');
10186				} else iL.closeAction();
10187			}).on(touchMoveEvent, '.ilightbox-overlay, .ilightbox-thumbnails-container', function(e) {
10188				// prevent scrolling
10189				e.preventDefault();
10190			});
10191
10192			function mouseMoveHandler(e) {
10193				if (!vars.isMobile) {
10194					if (!vars.mouseID) {
10195						vars.hideableElements.show();
10196					}
10197
10198					vars.mouseID = clearTimeout(vars.mouseID);
10199
10200					if (buttonsArray.indexOf(e.target) === -1)
10201						vars.mouseID = setTimeout(function() {
10202							vars.hideableElements.hide();
10203							vars.mouseID = clearTimeout(vars.mouseID);
10204						}, 3000);
10205				}
10206			}
10207
10208			if (opts.controls.arrows && !supportTouch) $doc.on('mousemove.iLightBox', mouseMoveHandler);
10209
10210			if (opts.controls.slideshow && opts.slideshow.pauseOnHover) $doc.on('mouseenter.iLightBox mouseleave.iLightBox', '.ilightbox-holder:not(.ilightbox-next, .ilightbox-prev)', function(e) {
10211				if (e.type == 'mouseenter' && vars.cycleID) iL.pause();
10212				else if (e.type == 'mouseleave' && vars.isPaused) iL.resume();
10213			});
10214
10215			var switchers = $('.ilightbox-overlay, .ilightbox-holder, .ilightbox-thumbnails');
10216
10217			if (opts.controls.mousewheel) switchers.on('mousewheel.iLightBox', function(event, delta) {
10218				if (!vars.lockWheel) {
10219					event.preventDefault();
10220					if (delta < 0) iL.moveTo('next');
10221					else if (delta > 0) iL.moveTo('prev');
10222				}
10223			});
10224
10225			if (opts.controls.swipe) holders.on(touchStartEvent, function(event) {
10226				if (vars.nextLock || vars.prevLock || vars.total == 1 || vars.lockSwipe) return;
10227
10228				vars.BODY.addClass('ilightbox-closedhand');
10229
10230				var data = event.originalEvent.touches ? event.originalEvent.touches[0] : event,
10231					scrollTop = $doc.scrollTop(),
10232					scrollLeft = $doc.scrollLeft(),
10233					offsets = [
10234						holders.eq(0).offset(),
10235						holders.eq(1).offset(),
10236						holders.eq(2).offset()
10237					],
10238					offSet = [{
10239						top: offsets[0].top - scrollTop,
10240						left: offsets[0].left - scrollLeft
10241					}, {
10242						top: offsets[1].top - scrollTop,
10243						left: offsets[1].left - scrollLeft
10244					}, {
10245						top: offsets[2].top - scrollTop,
10246						left: offsets[2].left - scrollLeft
10247					}],
10248					start = {
10249						time: (new Date()).getTime(),
10250						coords: [data.pageX - scrollLeft, data.pageY - scrollTop]
10251					},
10252					stop;
10253
10254				function moveEachHandler(i) {
10255					var t = $(this),
10256						offset = offSet[i],
10257						scroll = [(start.coords[0] - stop.coords[0]), (start.coords[1] - stop.coords[1])];
10258
10259					t[0].style[path == "horizontal" ? 'left' : 'top'] = (path == "horizontal" ? offset.left - scroll[0] : offset.top - scroll[1]) + 'px';
10260				}
10261
10262				function moveHandler(event) {
10263
10264					if (!start) return;
10265
10266					var data = event.originalEvent.touches ? event.originalEvent.touches[0] : event;
10267
10268					stop = {
10269						time: (new Date()).getTime(),
10270						coords: [data.pageX - scrollLeft, data.pageY - scrollTop]
10271					};
10272
10273					holders.each(moveEachHandler);
10274
10275					// prevent scrolling
10276					event.preventDefault();
10277				}
10278
10279				function repositionHolders() {
10280					holders.each(function() {
10281						var t = $(this),
10282							offset = t.data('offset') || {
10283								top: t.offset().top - scrollTop,
10284								left: t.offset().left - scrollLeft
10285							},
10286							top = offset.top,
10287							left = offset.left;
10288
10289						t.css(transform, gpuAcceleration).stop().animate({
10290							top: top,
10291							left: left
10292						}, 500, 'easeOutCirc', function() {
10293							t.css(transform, '');
10294						});
10295					});
10296				}
10297
10298				holders.bind(touchMoveEvent, moveHandler);
10299				$doc.one(touchStopEvent, function(event) {
10300					holders.unbind(touchMoveEvent, moveHandler);
10301
10302					vars.BODY.removeClass('ilightbox-closedhand');
10303
10304					if (start && stop) {
10305						if (path == "horizontal" && stop.time - start.time < durationThreshold && abs(start.coords[0] - stop.coords[0]) > horizontalDistanceThreshold && abs(start.coords[1] - stop.coords[1]) < verticalDistanceThreshold) {
10306							if (start.coords[0] > stop.coords[0]) {
10307								if (vars.current == vars.total - 1 && !opts.infinite) repositionHolders();
10308								else {
10309									vars.isSwipe = true;
10310									iL.moveTo('next');
10311								}
10312							} else {
10313								if (vars.current == 0 && !opts.infinite) repositionHolders();
10314								else {
10315									vars.isSwipe = true;
10316									iL.moveTo('prev');
10317								}
10318							}
10319						} 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) {
10320							if (start.coords[1] > stop.coords[1]) {
10321								if (vars.current == vars.total - 1 && !opts.infinite) repositionHolders();
10322								else {
10323									vars.isSwipe = true;
10324									iL.moveTo('next');
10325								}
10326							} else {
10327								if (vars.current == 0 && !opts.infinite) repositionHolders();
10328								else {
10329									vars.isSwipe = true;
10330									iL.moveTo('prev');
10331								}
10332							}
10333						} else repositionHolders();
10334					}
10335					start = stop = undefined;
10336				});
10337			});
10338
10339		},
10340
10341		goTo: function(index) {
10342			var iL = this,
10343				vars = iL.vars,
10344				opts = iL.options,
10345				diff = (index - vars.current);
10346
10347			if (opts.infinite) {
10348				if (index == vars.total - 1 && vars.current == 0) diff = -1;
10349				if (vars.current == vars.total - 1 && index == 0) diff = 1;
10350			}
10351
10352			if (diff == 1) iL.moveTo('next');
10353			else if (diff == -1) iL.moveTo('prev');
10354			else {
10355
10356				if (vars.nextLock || vars.prevLock) return false;
10357
10358				//Trigger the onBeforeChange callback
10359				if (typeof opts.callback.onBeforeChange == 'function') opts.callback.onBeforeChange.call(iL, iL.ui);
10360
10361				if (opts.linkId) {
10362					vars.hashLock = true;
10363					window.location.hash = opts.linkId + '/' + index;
10364				}
10365
10366				if (iL.items[index]) {
10367					if (!iL.items[index].options.mousewheel) vars.lockWheel = true;
10368					else iL.vars.lockWheel = false;
10369
10370					if (!iL.items[index].options.swipe) vars.lockSwipe = true;
10371					else vars.lockSwipe = false;
10372				}
10373
10374				$.each([vars.holder, vars.nextPhoto, vars.prevPhoto], function(key, val) {
10375					val.css(transform, gpuAcceleration).fadeOut(opts.effects.loadedFadeSpeed);
10376				});
10377
10378				vars.current = index;
10379				vars.next = index + 1;
10380				vars.prev = index - 1;
10381
10382				iL.createUI();
10383
10384				setTimeout(function() {
10385					iL.generateBoxes();
10386				}, opts.effects.loadedFadeSpeed + 50);
10387
10388				$('.ilightbox-thumbnail', vars.thumbnails).removeClass('ilightbox-active').eq(index).addClass('ilightbox-active');
10389				iL.positionThumbnails();
10390
10391				if (opts.linkId) setTimeout(function() {
10392					vars.hashLock = false;
10393				}, 55);
10394
10395				// Configure arrow buttons
10396				if (!opts.infinite) {
10397					vars.nextButton.add(vars.prevButton).add(vars.innerPrevButton).add(vars.innerNextButton).removeClass('disabled');
10398
10399					if (vars.current == 0) {
10400						vars.prevButton.add(vars.innerPrevButton).addClass('disabled');
10401					}
10402					if (vars.current >= vars.total - 1) {
10403						vars.nextButton.add(vars.innerNextButton).addClass('disabled');
10404					}
10405				}
10406
10407				// Reset next cycle timeout
10408				iL.resetCycle();
10409
10410				//Trigger the onAfterChange callback
10411				if (typeof opts.callback.onAfterChange == 'function') opts.callback.onAfterChange.call(iL, iL.ui);
10412			}
10413		},
10414
10415		moveTo: function(side) {
10416			var iL = this,
10417				vars = iL.vars,
10418				opts = iL.options,
10419				path = opts.path.toLowerCase(),
10420				viewport = getViewport(),
10421				switchSpeed = opts.effects.switchSpeed;
10422
10423			if (vars.nextLock || vars.prevLock) return false;
10424			else {
10425
10426				var item = (side == "next") ? vars.next : vars.prev;
10427
10428				if (opts.linkId) {
10429					vars.hashLock = true;
10430					window.location.hash = opts.linkId + '/' + item;
10431				}
10432
10433				if (side == "next") {
10434					if (!iL.items[item]) return false;
10435					var firstHolder = vars.nextPhoto,
10436						secondHolder = vars.holder,
10437						lastHolder = vars.prevPhoto,
10438						firstClass = 'ilightbox-prev',
10439						secondClass = 'ilightbox-next';
10440				} else if (side == "prev") {
10441					if (!iL.items[item]) return false;
10442					var firstHolder = vars.prevPhoto,
10443						secondHolder = vars.holder,
10444						lastHolder = vars.nextPhoto,
10445						firstClass = 'ilightbox-next',
10446						secondClass = 'ilightbox-prev';
10447				}
10448
10449				//Trigger the onBeforeChange callback
10450				if (typeof opts.callback.onBeforeChange == 'function')
10451					opts.callback.onBeforeChange.call(iL, iL.ui);
10452
10453				(side == "next") ? vars.nextLock = true: vars.prevLock = true;
10454
10455				var captionFirst = $('div.ilightbox-caption', secondHolder),
10456					socialFirst = $('div.ilightbox-social', secondHolder);
10457
10458				if (captionFirst.length)
10459					captionFirst.stop().fadeOut(switchSpeed, function() {
10460						$(this).remove();
10461					});
10462				if (socialFirst.length)
10463					socialFirst.stop().fadeOut(switchSpeed, function() {
10464						$(this).remove();
10465					});
10466
10467				if (iL.items[item].caption) {
10468					iL.setCaption(iL.items[item], firstHolder);
10469					var caption = $('div.ilightbox-caption', firstHolder),
10470						percent = parseInt((caption.outerHeight() / firstHolder.outerHeight()) * 100);
10471					if (opts.caption.start && percent <= 50) caption.fadeIn(switchSpeed);
10472				}
10473
10474				var social = iL.items[item].options.social;
10475				if (social) {
10476					iL.setSocial(social, iL.items[item].URL, firstHolder);
10477					if (opts.social.start) $('div.ilightbox-social', firstHolder).fadeIn(opts.effects.fadeSpeed);
10478				}
10479
10480				$.each([firstHolder, secondHolder, lastHolder], function(key, val) {
10481					val.removeClass('ilightbox-next ilightbox-prev');
10482				});
10483
10484				var firstOffset = firstHolder.data('offset'),
10485					winW = (viewport.width - (opts.styles.pageOffsetX)),
10486					winH = (viewport.height - (opts.styles.pageOffsetY)),
10487					width = firstOffset.newDims.width,
10488					height = firstOffset.newDims.height,
10489					thumbsOffset = firstOffset.thumbsOffset,
10490					diff = firstOffset.diff,
10491					top = parseInt((winH / 2) - (height / 2) - diff.H - (thumbsOffset.H / 2)),
10492					left = parseInt((winW / 2) - (width / 2) - diff.W - (thumbsOffset.W / 2));
10493
10494				var secondOffset = secondHolder.data('offset'),
10495					object = secondOffset.object;
10496
10497				/*if (object.item.caption && !isNaN(width) && !isNaN(height) && UNCODE.wwidth > UNCODE.mediaQuery) {
10498					var objRatio = width / height;
10499					height = height - vars.captionHeight;
10500					width = height * objRatio;
10501					top = path == 'horizontal' ? parseInt((winH / 2) - (height / 2) - diff.H - (thumbsOffset.H / 2)) : top,
10502					left = parseInt((winW / 2) - (width / 2) - diff.W - (thumbsOffset.W / 2));
10503				}*/
10504
10505				firstHolder.show().css(transform, gpuAcceleration).animate({
10506					top: top,
10507					left: left,
10508					opacity: 1
10509				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc', function() {
10510					firstHolder.css(transform, '');
10511				});
10512
10513				$('div.ilightbox-container', firstHolder).animate({
10514					width: width,
10515					height: height
10516				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc');
10517
10518				diff = secondOffset.diff;
10519
10520				width = secondOffset.newDims.width,
10521					height = secondOffset.newDims.height;
10522
10523				width = parseInt(width * opts.styles[side == 'next' ? 'prevScale' : 'nextScale']),
10524					height = parseInt(height * opts.styles[side == 'next' ? 'prevScale' : 'nextScale']),
10525					top = (path == 'horizontal') ? parseInt((winH / 2) - object.offsetY - (height / 2) - diff.H - (thumbsOffset.H / 2)) : parseInt(winH - object.offsetX - diff.H - (thumbsOffset.H / 2));
10526
10527				if (side == 'prev')
10528					left = (path == 'horizontal') ? parseInt(winW - object.offsetX - diff.W - (thumbsOffset.W / 2)) : parseInt((winW / 2) - (width / 2) - diff.W - object.offsetY - (thumbsOffset.W / 2));
10529				else {
10530					top = (path == 'horizontal') ? top : parseInt(object.offsetX - diff.H - height - (thumbsOffset.H / 2)),
10531					left = (path == 'horizontal') ? parseInt(object.offsetX - diff.W - width - (thumbsOffset.W / 2)) : parseInt((winW / 2) - object.offsetY - (width / 2) - diff.W - (thumbsOffset.W / 2));
10532				}
10533
10534				/*if (object.item.caption && !isNaN(width) && !isNaN(height) && UNCODE.wwidth > UNCODE.mediaQuery) {
10535					var objRatio = width / height;
10536					height = height - vars.captionHeight;
10537					width = height * objRatio;
10538					top = path == 'horizontal' ? parseInt((winH / 2) - (height / 2) - diff.H - (thumbsOffset.H / 2)) : top;
10539				}*/
10540
10541				$('div.ilightbox-container', secondHolder).animate({
10542					width: width,
10543					height: height
10544				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc');
10545
10546				secondHolder.addClass(firstClass).css(transform, gpuAcceleration).animate({
10547					top: top,
10548					left: left,
10549					opacity: opts.styles.prevOpacity,
10550				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc', function() {
10551					secondHolder.css(transform, '');
10552
10553					$('.ilightbox-thumbnail', vars.thumbnails).removeClass('ilightbox-active').eq(item).addClass('ilightbox-active');
10554					iL.positionThumbnails();
10555
10556					if (iL.items[item]) {
10557						if (!iL.items[item].options.mousewheel) vars.lockWheel = true;
10558						else vars.lockWheel = false;
10559
10560						if (!iL.items[item].options.swipe) vars.lockSwipe = true;
10561						else vars.lockSwipe = false;
10562					}
10563
10564					vars.isSwipe = false;
10565
10566					// Remove iframe & video from previous slide
10567					if (['iframe', 'video'].indexOf(iL.items[vars.current].type) !== -1) {
10568						$('div.ilightbox-container', secondHolder).empty();
10569					}
10570
10571					if (side == "next") {
10572						vars.nextPhoto = lastHolder,
10573							vars.prevPhoto = secondHolder,
10574							vars.holder = firstHolder;
10575
10576						vars.nextPhoto.hide();
10577
10578						vars.next = vars.next + 1,
10579							vars.prev = vars.current,
10580							vars.current = vars.current + 1;
10581
10582						if (opts.infinite) {
10583							if (vars.current > vars.total - 1) vars.current = 0;
10584							if (vars.current == vars.total - 1) vars.next = 0;
10585							if (vars.current == 0) vars.prev = vars.total - 1;
10586						}
10587
10588						iL.createUI();
10589
10590						if (!iL.items[vars.next])
10591							vars.nextLock = false;
10592						else
10593							iL.loadContent(iL.items[vars.next], 'next');
10594					} else {
10595						vars.prevPhoto = lastHolder;
10596						vars.nextPhoto = secondHolder;
10597						vars.holder = firstHolder;
10598
10599						vars.prevPhoto.hide();
10600
10601						vars.next = vars.current;
10602						vars.current = vars.prev;
10603						vars.prev = vars.current - 1;
10604
10605						if (opts.infinite) {
10606							if (vars.current == vars.total - 1) vars.next = 0;
10607							if (vars.current == 0) vars.prev = vars.total - 1;
10608						}
10609
10610						iL.createUI();
10611
10612						if (!iL.items[vars.prev])
10613							vars.prevLock = false;
10614						else
10615							iL.loadContent(iL.items[vars.prev], 'prev');
10616					}
10617
10618					// Add iframe & video content for current slide
10619					if (['iframe', 'video'].indexOf(iL.items[vars.current].type) !== -1) {
10620						iL.loadContent(iL.items[vars.current], 'current');
10621					}
10622
10623					if (opts.linkId) setTimeout(function() {
10624						vars.hashLock = false;
10625					}, 55);
10626
10627					// Configure arrow buttons
10628					if (!opts.infinite) {
10629						vars.nextButton.add(vars.prevButton).add(vars.innerPrevButton).add(vars.innerNextButton).removeClass('disabled');
10630
10631						if (vars.current == 0)
10632							vars.prevButton.add(vars.innerPrevButton).addClass('disabled');
10633						if (vars.current >= vars.total - 1)
10634							vars.nextButton.add(vars.innerNextButton).addClass('disabled');
10635					}
10636
10637					iL.repositionPhoto();
10638
10639					// Reset next cycle timeout
10640					iL.resetCycle();
10641
10642					//Trigger the onAfterChange callback
10643					if (typeof opts.callback.onAfterChange == 'function')
10644						opts.callback.onAfterChange.call(iL, iL.ui);
10645				});
10646
10647				top = (path == 'horizontal') ? getPixel(lastHolder, 'top') : ((side == "next") ? parseInt(-(winH / 2) - lastHolder.outerHeight()) : parseInt(top * 2)),
10648					left = (path == 'horizontal') ? ((side == "next") ? parseInt(-(winW / 2) - lastHolder.outerWidth()) : parseInt(left * 2)) : getPixel(lastHolder, 'left');
10649
10650				lastHolder.css(transform, gpuAcceleration).animate({
10651					top: top,
10652					left: left,
10653					opacity: opts.styles.nextOpacity
10654				}, switchSpeed, (vars.isSwipe) ? 'easeOutCirc' : 'easeInOutCirc', function() {
10655					lastHolder.css(transform, '');
10656				}).addClass(secondClass);
10657			}
10658		},
10659
10660		setCaption: function(obj, target) {
10661			var iL = this,
10662				caption = $('<div class="ilightbox-caption"></div>');
10663
10664			if (obj.caption) {
10665				caption.html(obj.caption);
10666				$('div.ilightbox-container', target).append(caption);
10667			}
10668		},
10669
10670		normalizeSocial: function(obj, url) {
10671			var iL = this,
10672				vars = iL.vars,
10673				opts = iL.options,
10674				baseURL = window.location.href;
10675
10676			$.each(obj, function(key, value) {
10677				if (!value)
10678					return true;
10679
10680				var item = key.toLowerCase(),
10681					source, text;
10682
10683				switch (item) {
10684					case 'facebook':
10685						source = "http://www.facebook.com/share.php?v=4&src=bm&u={URL}",
10686							text = "Share on Facebook";
10687						break;
10688					case 'twitter':
10689						source = "http://twitter.com/home?status={URL}",
10690							text = "Share on Twitter";
10691						break;
10692					case 'googleplus':
10693						source = "https://plus.google.com/share?url={URL}",
10694							text = "Share on Google+";
10695						break;
10696					case 'delicious':
10697						source = "http://delicious.com/post?url={URL}",
10698							text = "Share on Delicious";
10699						break;
10700					case 'digg':
10701						source = "http://digg.com/submit?phase=2&url={URL}",
10702							text = "Share on Digg";
10703						break;
10704					case 'reddit':
10705						source = "http://reddit.com/submit?url={URL}",
10706							text = "Share on reddit";
10707						break;
10708				}
10709
10710				obj[key] = {
10711					URL: value.URL && absolutizeURI(baseURL, value.URL) || opts.linkId && window.location.href || typeof url !== 'string' && baseURL || url && absolutizeURI(baseURL, url) || baseURL,
10712					source: value.source || source || value.URL && absolutizeURI(baseURL, value.URL) || url && absolutizeURI(baseURL, url),
10713					text: value.text || text || "Share on " + key,
10714					width: (typeof(value.width) != 'undefined' && !isNaN(value.width)) ? parseInt(value.width) : 640,
10715					height: value.height || 360
10716				};
10717
10718			});
10719
10720			return obj;
10721		},
10722
10723		setSocial: function(obj, url, target) {
10724			var iL = this,
10725				socialBar = $('<div class="ilightbox-social"></div>'),
10726				buttons = '<ul>';
10727
10728			obj = iL.normalizeSocial(obj, url);
10729
10730			$.each(obj, function(key, value) {
10731				var item = key.toLowerCase(),
10732					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, '+'));
10733				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>';
10734			});
10735
10736			buttons += '</ul>';
10737
10738			socialBar.html(buttons);
10739			$('div.ilightbox-container', target).append(socialBar);
10740		},
10741
10742		fullScreenAction: function() {
10743			var iL = this,
10744				vars = iL.vars;
10745
10746			if (fullScreenApi.supportsFullScreen) {
10747				if (fullScreenApi.isFullScreen()) fullScreenApi.cancelFullScreen(document.documentElement);
10748				else fullScreenApi.requestFullScreen(document.documentElement);
10749			} else {
10750				iL.doFullscreen();
10751			}
10752		},
10753
10754		doFullscreen: function() {
10755			var iL = this,
10756				vars = iL.vars,
10757				viewport = getViewport(),
10758				opts = iL.options;
10759
10760			if (opts.fullAlone) {
10761				var currentHolder = vars.holder,
10762					current = iL.items[vars.current],
10763					windowWidth = viewport.width,
10764					windowHeight = viewport.height,
10765					elements = [currentHolder, vars.nextPhoto, vars.prevPhoto, vars.nextButton, vars.prevButton, vars.overlay, vars.toolbar, vars.thumbnails, vars.loader],
10766					hideElements = [vars.nextPhoto, vars.prevPhoto, vars.nextButton, vars.prevButton, vars.loader, vars.thumbnails];
10767
10768				if (!vars.isInFullScreen) {
10769					vars.isInFullScreen = vars.lockKey = vars.lockWheel = vars.lockSwipe = true;
10770					vars.overlay.css({
10771						opacity: 1
10772					});
10773
10774					$.each(hideElements, function(i, element) {
10775						element.hide();
10776					});
10777
10778					vars.fullScreenButton.attr('title', opts.text.exitFullscreen);
10779
10780					if (opts.fullStretchTypes.indexOf(current.type) != -1) currentHolder.data({
10781						naturalWidthOld: currentHolder.data('naturalWidth'),
10782						naturalHeightOld: currentHolder.data('naturalHeight'),
10783						naturalWidth: windowWidth,
10784						naturalHeight: windowHeight
10785					});
10786					else {
10787						var viewport = current.options.fullViewPort || opts.fullViewPort || '',
10788							newWidth = windowWidth,
10789							newHeight = windowHeight,
10790							width = currentHolder.data('naturalWidth'),
10791							height = currentHolder.data('naturalHeight');
10792
10793						if (viewport.toLowerCase() == 'fill') {
10794							newHeight = (newWidth / width) * height;
10795
10796							if (newHeight < windowHeight) {
10797								newWidth = (windowHeight / height) * width,
10798									newHeight = windowHeight;
10799							}
10800						} else if (viewport.toLowerCase() == 'fit') {
10801							var dims = iL.getNewDimenstions(newWidth, newHeight, width, height, true);
10802
10803							newWidth = dims.width,
10804								newHeight = dims.height;
10805						} else if (viewport.toLowerCase() == 'stretch') {
10806							newWidth = newWidth,
10807								newHeight = newHeight;
10808						} else {
10809							var scale = (width > newWidth || height > newHeight) ? true : false,
10810								dims = iL.getNewDimenstions(newWidth, newHeight, width, height, scale);
10811							newWidth = dims.width,
10812								newHeight = dims.height;
10813						}
10814
10815						currentHolder.data({
10816							naturalWidthOld: currentHolder.data('naturalWidth'),
10817							naturalHeightOld: currentHolder.data('naturalHeight'),
10818							naturalWidth: newWidth,
10819							naturalHeight: newHeight
10820						});
10821					}
10822
10823					$.each(elements, function(key, val) {
10824						val.addClass('ilightbox-fullscreen');
10825					});
10826
10827					//Trigger the onEnterFullScreen callback
10828					if (typeof opts.callback.onEnterFullScreen == 'function') opts.callback.onEnterFullScreen.call(iL, iL.ui);
10829				} else {
10830					vars.isInFullScreen = vars.lockKey = vars.lockWheel = vars.lockSwipe = false;
10831					vars.overlay.css({
10832						opacity: iL.options.overlay.opacity
10833					});
10834
10835					$.each(hideElements, function(i, element) {
10836						element.show();
10837					});
10838
10839					vars.fullScreenButton.attr('title', opts.text.enterFullscreen);
10840
10841					currentHolder.data({
10842						naturalWidth: currentHolder.data('naturalWidthOld'),
10843						naturalHeight: currentHolder.data('naturalHeightOld'),
10844						naturalWidthOld: null,
10845						naturalHeightOld: null
10846					});
10847
10848					$.each(elements, function(key, val) {
10849						val.removeClass('ilightbox-fullscreen');
10850					});
10851
10852					//Trigger the onExitFullScreen callback
10853					if (typeof opts.callback.onExitFullScreen == 'function') opts.callback.onExitFullScreen.call(iL, iL.ui);
10854				}
10855			} else {
10856				if (!vars.isInFullScreen) vars.isInFullScreen = true;
10857				else vars.isInFullScreen = false;
10858			}
10859			iL.repositionPhoto(true);
10860		},
10861
10862		closeAction: function() {
10863			var iL = this,
10864				vars = iL.vars,
10865				opts = iL.options;
10866
10867			$win.unbind('.iLightBox');
10868			$doc.off('.iLightBox');
10869
10870			if (vars.isInFullScreen) fullScreenApi.cancelFullScreen(document.documentElement);
10871
10872			$('.ilightbox-overlay, .ilightbox-holder, .ilightbox-thumbnails').off('.iLightBox');
10873
10874			if (opts.hide.effect) vars.overlay.stop().fadeOut(opts.hide.speed, function() {
10875				vars.overlay.remove();
10876				vars.BODY.removeClass('ilightbox-noscroll').off('.iLightBox');
10877			});
10878			else {
10879				vars.overlay.remove();
10880				vars.BODY.removeClass('ilightbox-noscroll').off('.iLightBox');
10881			}
10882
10883			var fadeOuts = [vars.toolbar, vars.holder, vars.nextPhoto, vars.prevPhoto, vars.nextButton, vars.prevButton, vars.loader, vars.thumbnails];
10884
10885			$.each(fadeOuts, function(i, element) {
10886				element.removeAttr('style').remove();
10887			});
10888
10889			vars.dontGenerateThumbs = vars.isInFullScreen = false;
10890
10891			window.iLightBox = null;
10892
10893			if (opts.linkId) {
10894				vars.hashLock = true;
10895				removeHash();
10896				setTimeout(function() {
10897					vars.hashLock = false;
10898				}, 55);
10899			}
10900
10901			//Trigger the onHide callback
10902			if (typeof opts.callback.onHide == 'function') opts.callback.onHide.call(iL, iL.ui);
10903		},
10904
10905		repositionPhoto: function() {
10906			var iL = this,
10907				vars = iL.vars,
10908				opts = iL.options,
10909				path = opts.path.toLowerCase(),
10910				viewport = getViewport(),
10911				winWidth = viewport.width,
10912				winHeight = viewport.height;
10913
10914			if (viewport.width < UNCODE.mediaQuery) opts.styles.nextOffsetX = 0;
10915
10916			var thumbsOffsetW = (vars.isInFullScreen && opts.fullAlone || vars.isMobile) ? 0 : ((path == 'horizontal') ? 0 : vars.thumbnails.outerWidth()),
10917				thumbsOffsetH = vars.isMobile ? vars.toolbar.outerHeight() : ((vars.isInFullScreen && opts.fullAlone) ? 0 : ((path == 'horizontal') ? vars.thumbnails.outerHeight() : 0)),
10918				width = (vars.isInFullScreen && opts.fullAlone) ? winWidth : (winWidth - (opts.styles.pageOffsetX)),
10919				height = (vars.isInFullScreen && opts.fullAlone) ? winHeight : (winHeight - (opts.styles.pageOffsetY)),
10920				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,
10921				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)));
10922
10923			var elObject = {
10924				type: 'current',
10925				width: width,
10926				height: height,
10927				item: iL.items[vars.current],
10928				offsetW: offsetW,
10929				offsetH: offsetH,
10930				thumbsOffsetW: thumbsOffsetW,
10931				thumbsOffsetH: thumbsOffsetH,
10932				animate: arguments.length,
10933				holder: vars.holder
10934			};
10935
10936			iL.repositionEl(elObject);
10937
10938			if (iL.items[vars.next]) {
10939				elObject = $.extend(elObject, {
10940					type: 'next',
10941					item: iL.items[vars.next],
10942					offsetX: opts.styles.nextOffsetX,
10943					offsetY: opts.styles.nextOffsetY,
10944					holder: vars.nextPhoto
10945				});
10946
10947				iL.repositionEl(elObject);
10948			}
10949
10950			if (iL.items[vars.prev]) {
10951				elObject = $.extend(elObject, {
10952					type: 'prev',
10953					item: iL.items[vars.prev],
10954					offsetX: opts.styles.nextOffsetX,
10955					offsetY: opts.styles.prevOffsetY,
10956					holder: vars.prevPhoto
10957				});
10958
10959				iL.repositionEl(elObject);
10960			}
10961
10962			var loaderCss = (path == "horizontal") ? {
10963				left: parseInt((width / 2) - (vars.loader.outerWidth() / 2))
10964			} : {
10965				top: parseInt((height / 2) - (vars.loader.outerHeight() / 2))
10966			};
10967			vars.loader.css(loaderCss);
10968		},
10969
10970		repositionEl: function(obj) {
10971			var iL = this,
10972				vars = iL.vars,
10973				opts = iL.options,
10974				path = opts.path.toLowerCase(),
10975				widthAvail = (obj.type == 'current') ? ((vars.isInFullScreen && opts.fullAlone) ? obj.width : (obj.width - obj.offsetW)) : (obj.width - obj.offsetW),
10976				heightAvail = (obj.type == 'current') ? ((vars.isInFullScreen && opts.fullAlone) ? obj.height : (obj.height - obj.offsetH)) : (obj.height - obj.offsetH),
10977				itemParent = obj.item,
10978				item = obj.item.options,
10979				holder = obj.holder,
10980				offsetX = obj.offsetX || 0,
10981				offsetY = obj.offsetY || 0,
10982				thumbsOffsetW = obj.thumbsOffsetW,
10983				thumbsOffsetH = obj.thumbsOffsetH;
10984
10985			if (obj.type == 'current') {
10986				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);
10987				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);
10988			} else {
10989				if (typeof item.width == 'number' && item.width) widthAvail = (item.width > widthAvail) ? widthAvail : item.width;
10990				if (typeof item.height == 'number' && item.height) heightAvail = (item.height > heightAvail) ? heightAvail : item.height;
10991			}
10992
10993			heightAvail = parseInt(heightAvail - $('.ilightbox-inner-toolbar', holder).outerHeight());
10994
10995			var width = (typeof item.width == 'string' && item.width.indexOf('%') != -1) ? percentToValue(parseInt(item.width.replace('%', '')), obj.width) : holder.data('naturalWidth'),
10996				height = (typeof item.height == 'string' && item.height.indexOf('%') != -1) ? percentToValue(parseInt(item.height.replace('%', '')), obj.height) : holder.data('naturalHeight');
10997
10998			var dims = ((typeof item.width == 'string' && item.width.indexOf('%') != -1 || typeof item.height == 'string' && item.height.indexOf('%') != -1) ? {
10999					width: width,
11000					height: height
11001				} : iL.getNewDimenstions(widthAvail, heightAvail, width, height)),
11002				newDims = $.extend({}, dims, {});
11003
11004			if (obj.type == 'prev' || obj.type == 'next')
11005				width = parseInt(dims.width * ((obj.type == 'next') ? opts.styles.nextScale : opts.styles.prevScale)),
11006				height = parseInt(dims.height * ((obj.type == 'next') ? opts.styles.nextScale : opts.styles.prevScale));
11007			else
11008				width = dims.width,
11009				height = dims.height;
11010
11011			var widthDiff = parseInt((getPixel(holder, 'padding-left') + getPixel(holder, 'padding-right') + getPixel(holder, 'border-left-width') + getPixel(holder, 'border-right-width')) / 2),
11012				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);
11013
11014			switch (obj.type) {
11015				case 'current':
11016					var top = parseInt((obj.height / 2) - (height / 2) - heightDiff - (thumbsOffsetH / 2)),
11017						left = parseInt((obj.width / 2) - (width / 2) - widthDiff - (thumbsOffsetW / 2));
11018					break;
11019
11020				case 'next':
11021					var top = (path == 'horizontal') ? parseInt((obj.height / 2) - offsetY - (height / 2) - heightDiff - (thumbsOffsetH / 2)) : parseInt(obj.height - offsetX - heightDiff - (thumbsOffsetH / 2)),
11022						left = (path == 'horizontal') ? parseInt(obj.width - offsetX - widthDiff - (thumbsOffsetW / 2)) : parseInt((obj.width / 2) - (width / 2) - widthDiff - offsetY - (thumbsOffsetW / 2));
11023					break;
11024
11025				case 'prev':
11026					var top = (path == 'horizontal') ? parseInt((obj.height / 2) - offsetY - (height / 2) - heightDiff - (thumbsOffsetH / 2)) : parseInt(offsetX - heightDiff - height - (thumbsOffsetH / 2)),
11027						left = (path == 'horizontal') ? parseInt(offsetX - widthDiff - width - (thumbsOffsetW / 2)) : parseInt((obj.width / 2) - offsetY - (width / 2) - widthDiff - (thumbsOffsetW / 2));
11028					break;
11029			}
11030
11031			holder.data('offset', {
11032				top: top,
11033				left: left,
11034				newDims: newDims,
11035				diff: {
11036					W: widthDiff,
11037					H: heightDiff
11038				},
11039				thumbsOffset: {
11040					W: thumbsOffsetW,
11041					H: thumbsOffsetH
11042				},
11043				object: obj
11044			});
11045
11046			var opacityMobile;
11047			if ( isMobile ) {
11048				opacityMobile = {
11049					opacity: 1,
11050					top: top,
11051					left: left
11052				};
11053			} else {
11054				opacityMobile = {
11055					top: top,
11056					left: left
11057				};
11058			}
11059
11060			if (obj.animate > 0 && opts.effects.reposition) {
11061				holder.css(transform, gpuAcceleration).stop().animate(
11062					opacityMobile,
11063					opts.effects.repositionSpeed, 'easeOutCirc', function() {
11064					holder.css(transform, '');
11065				});
11066				$('div.ilightbox-container', holder).stop().animate({
11067					width: width,
11068					height: height
11069				}, opts.effects.repositionSpeed, 'easeOutCirc');
11070				$('div.ilightbox-inner-toolbar', holder).stop().animate({
11071					width: width
11072				}, opts.effects.repositionSpeed, 'easeOutCirc', function() {
11073					$(this).css('overflow', 'visible');
11074				});
11075			} else {
11076				holder.css(opacityMobile);
11077				$('div.ilightbox-container', holder).css({
11078					width: width,
11079					height: height
11080				});
11081				$('div.ilightbox-inner-toolbar', holder).css({
11082					width: width
11083				});
11084			}
11085		},
11086
11087
11088		resume: function(priority) {
11089			var iL = this,
11090				vars = iL.vars,
11091				opts = iL.options;
11092
11093			if (!opts.slideshow.pauseTime || opts.controls.slideshow && vars.total <= 1 || priority < vars.isPaused) {
11094				return;
11095			}
11096
11097			vars.isPaused = 0;
11098
11099			if (vars.cycleID) {
11100				vars.cycleID = clearTimeout(vars.cycleID);
11101			}
11102
11103			vars.cycleID = setTimeout(function() {
11104				if (vars.current == vars.total - 1) iL.goTo(0);
11105				else iL.moveTo('next');
11106			}, opts.slideshow.pauseTime);
11107		},
11108
11109		pause: function(priority) {
11110			var iL = this,
11111				vars = iL.vars,
11112				opts = iL.options;
11113
11114			if (priority < vars.isPaused) {
11115				return;
11116			}
11117
11118			vars.isPaused = priority || 100;
11119
11120			if (vars.cycleID) {
11121				vars.cycleID = clearTimeout(vars.cycleID);
11122			}
11123		},
11124
11125		resetCycle: function() {
11126			var iL = this,
11127				vars = iL.vars,
11128				opts = iL.options;
11129
11130			if (opts.controls.slideshow && vars.cycleID && !vars.isPaused) {
11131				iL.resume();
11132			}
11133		},
11134
11135		getNewDimenstions: function(width, height, width_old, height_old, thumb) {
11136			var iL = this;
11137
11138			if (!width) factor = height / height_old;
11139			else if (!height) factor = width / width_old;
11140			else factor = min(width / width_old, height / height_old);
11141
11142			if (!thumb) {
11143				if (factor > iL.options.maxScale) factor = iL.options.maxScale;
11144				else if (factor < iL.options.minScale) factor = iL.options.minScale;
11145			}
11146
11147			var final_width = (iL.options.keepAspectRatio) ? round(width_old * factor) : width,
11148				final_height = (iL.options.keepAspectRatio) ? round(height_old * factor) : height;
11149
11150			return {
11151				width: final_width,
11152				height: final_height,
11153				ratio: factor
11154			};
11155		},
11156
11157		setOption: function(options) {
11158			var iL = this;
11159
11160			iL.options = $.extend(true, iL.options, options || {});
11161			iL.refresh();
11162		},
11163
11164		availPlugins: function() {
11165			var iL = this,
11166				testEl = document.createElement("video");
11167
11168			iL.plugins = {
11169				flash: !isMobile,
11170				quicktime: (parseInt(PluginDetect.getVersion("QuickTime")) >= 0) ? true : false,
11171				html5H264: !!(testEl.canPlayType && testEl.canPlayType('video/mp4').replace(/no/, '')),
11172				html5WebM: !!(testEl.canPlayType && testEl.canPlayType('video/webm').replace(/no/, '')),
11173				html5Vorbis: !!(testEl.canPlayType && testEl.canPlayType('video/ogg').replace(/no/, '')),
11174				html5QuickTime: !!(testEl.canPlayType && testEl.canPlayType('video/quicktime').replace(/no/, ''))
11175			};
11176		},
11177
11178		addContent: function(element, obj) {
11179			var iL = this,
11180				el;
11181
11182			switch (obj.type) {
11183				case 'video':
11184					var HTML5 = false,
11185						videoType = obj.videoType,
11186						html5video = obj.options.html5video;
11187
11188					if (((videoType == 'video/mp4' || obj.ext == 'mp4' || obj.ext == 'm4v') || html5video.h264) && iL.plugins.html5H264)
11189						obj.ext = 'mp4',
11190						obj.URL = html5video.h264 || obj.URL;
11191					else if (html5video.webm && iL.plugins.html5WebM)
11192						obj.ext = 'webm',
11193						obj.URL = html5video.webm || obj.URL;
11194					else if (html5video.ogg && iL.plugins.html5Vorbis)
11195						obj.ext = 'ogv',
11196						obj.URL = html5video.ogg || obj.URL;
11197
11198					if (iL.plugins.html5H264 && (videoType == 'video/mp4' || obj.ext == 'mp4' || obj.ext == 'm4v')) HTML5 = true, videoType = "video/mp4";
11199					else if (iL.plugins.html5WebM && (videoType == 'video/webm' || obj.ext == 'webm')) HTML5 = true, videoType = "video/webm";
11200					else if (iL.plugins.html5Vorbis && (videoType == 'video/ogg' || obj.ext == 'ogv')) HTML5 = true, videoType = "video/ogg";
11201					else if (iL.plugins.html5QuickTime && (videoType == 'video/quicktime' || obj.ext == 'mov' || obj.ext == 'qt')) HTML5 = true, videoType = "video/quicktime";
11202
11203					if (HTML5) {
11204						el = $('<video />
11204', {
11205							"width": "100%",
11206							"height": "100%",
11207							"preload": html5video.preload,
11208							"autoplay": html5video.autoplay,
11209							"loop": html5video.loop,
11210							"poster": html5video.poster,
11211							"controls": html5video.controls,
11212							"controlslist": "nodownload"
11213						}).append($('<source />', {
11214							"src": obj.URL,
11215							"type": videoType
11216						}));
11217					} else {
11218						if (!iL.plugins.quicktime) el = $('<span />', {
11219							"class": "ilightbox-alert",
11220							html: iL.options.errors.missingPlugin.replace('{pluginspage}', pluginspages.quicktime).replace('{type}', 'QuickTime')
11221						});
11222						else {
11223
11224							el = $('<object />', {
11225								"type": "video/quicktime",
11226								"pluginspage": pluginspages.quicktime
11227							}).attr({
11228								"data": obj.URL,
11229								"width": "100%",
11230								"height": "100%"
11231							}).append($('<param />', {
11232								"name": "src",
11233								"value": obj.URL
11234							})).append($('<param />', {
11235								"name": "autoplay",
11236								"value": "false"
11237							})).append($('<param />', {
11238								"name": "loop",
11239								"value": "false"
11240							})).append($('<param />', {
11241								"name": "scale",
11242								"value": "tofit"
11243							}));
11244
11245							if (browser.msie) el = QT_GenerateOBJECTText(obj.URL, '100%', '100%', '', 'SCALE', 'tofit', 'AUTOPLAY', 'false', 'LOOP', 'false');
11246						}
11247					}
11248
11249					break;
11250
11251				case 'flash':
11252					if (!iL.plugins.flash) el = $('<span />', {
11253						"class": "ilightbox-alert",
11254						html: iL.options.errors.missingPlugin.replace('{pluginspage}', pluginspages.flash).replace('{type}', 'Adobe Flash player')
11255					});
11256					else {
11257						var flashvars = "",
11258							i = 0;
11259
11260						if (obj.options.flashvars) $.each(obj.options.flashvars, function(k, v) {
11261							if (i != 0) flashvars += "&";
11262							flashvars += k + "=" + encodeURIComponent(v);
11263							i++;
11264						});
11265						else flashvars = null;
11266
11267						el = $('<embed />').attr({
11268							"type": "application/x-shockwave-flash",
11269							"src": obj.URL,
11270							"width": (typeof obj.options.width == 'number' && obj.options.width && iL.options.minScale == '1' && iL.options.maxScale == '1') ? obj.options.width : "100%",
11271							"height": (typeof obj.options.height == 'number' && obj.options.height && iL.options.minScale == '1' && iL.options.maxScale == '1') ? obj.options.height : "100%",
11272							"quality": "high",
11273							"bgcolor": "#000000",
11274							"play": "true",
11275							"loop": "true",
11276							"menu": "true",
11277							"wmode": "transparent",
11278							"scale": "showall",
11279							"allowScriptAccess": "always",
11280							"allowFullScreen": "true",
11281							"flashvars": flashvars,
11282							"fullscreen": "yes"
11283						});
11284					}
11285
11286					break;
11287
11288				case 'iframe':
11289					el = $('<iframe />').attr({
11290						"width": (typeof obj.options.width == 'number' && obj.options.width && iL.options.minScale == '1' && iL.options.maxScale == '1') ? obj.options.width : "100%",
11291						"height": (typeof obj.options.height == 'number' && obj.options.height && iL.options.minScale == '1' && iL.options.maxScale == '1') ? obj.options.height : "100%",
11292						src: obj.URL,
11293						frameborder: 0,
11294						'hspace': 0,
11295						'vspace': 0,
11296						'scrolling': supportTouch ? 'auto' : 'scroll',
11297						'webkitAllowFullScreen': '',
11298						'mozallowfullscreen': '',
11299						'allowFullScreen': ''
11300					});
11301
11302					break;
11303
11304				case 'inline':
11305					el = $('<div class="ilightbox-wrapper"></div>').html($(obj.URL).clone(true));
11306
11307					break;
11308
11309				case 'html':
11310					var object = obj.URL,
11311						el;
11312
11313					if (object[0].nodeName) {
11314						el = $('<div class="ilightbox-wrapper"></div>').html(object);
11315					} else {
11316						var dom = $(obj.URL),
11317							html = (dom.selector) ? $('<div>' + dom + '</div>') : dom;
11318						el = $('<div class="ilightbox-wrapper"></div>').html(html);
11319					}
11320
11321					break;
11322			}
11323
11324			$('div.ilightbox-container', element).empty().html(el);
11325
11326			// For fixing Chrome about just playing the video for first time
11327			if (el[0].tagName.toLowerCase() === 'video' && browser.webkit) setTimeout(function() {
11328				var src = el[0].currentSrc + '?' + floor(random() * 30000);
11329				el[0].currentSrc = src;
11330				el[0].src = src;
11331			});
11332
11333			return el;
11334		},
11335
11336		ogpRecognition: function(obj, callback) {
11337			var iL = this,
11338				url = obj.URL;
11339
11340			iL.showLoader();
11341			doAjax(url, function(data) {
11342				iL.hideLoader();
11343				if (data) {
11344					var object = new Object();
11345
11346					object.length = false,
11347					object.url = data.url;
11348
11349					if (data.status == 200) {
11350						var result = data.results,
11351							type = result.type,
11352							source = result.source;
11353
11354						object.source = source.src,
11355						object.width = source.width && parseInt(source.width) || 0,
11356						object.height = source.height && parseInt(source.height) || 0,
11357						object.type = type,
11358						object.thumbnail = source.thumbnail || result.images && result.images[0],
11359						object.html5video = result.html5video || {},
11360						object.length = true;
11361
11362						if (source.type == 'application/x-shockwave-flash') object.type = "flash";
11363						else if (source.type.indexOf("video/") != -1) object.type = "video";
11364						else if (source.type.indexOf("/html") != -1) object.type = "iframe";
11365						else if (source.type.indexOf("image/") != -1) object.type = "image";
11366
11367					} else if (typeof data.response != 'undefined')
11368						throw data.response;
11369
11370					callback.call(this, object.length ? object : false);
11371				}
11372			});
11373		},
11374
11375		hashChangeHandler: function(url) {
11376			var iL = this,
11377				vars = iL.vars,
11378				opts = iL.options,
11379				URL = url || window.location.href,
11380				hash = parseURI(URL).hash,
11381				split = hash.split('/'),
11382				index = split[1];
11383
11384			if (vars.hashLock || ('#' + opts.linkId != split[0] && hash.length > 1)) return;
11385
11386			if (index) {
11387				var target = split[1] || 0;
11388				if (iL.items[target]) {
11389					var overlay = $('.ilightbox-overlay');
11390					if (overlay.length && overlay.attr('linkid') == opts.linkId) {
11391						iL.goTo(target);
11392					} else {
11393						iL.itemsObject[target].trigger(supportTouch ? 'itap' : 'click');
11394						//iL.goTo(target);
11395					}
11396				} else {
11397					var overlay = $('.ilightbox-overlay');
11398					if (overlay.length) iL.closeAction();
11399				}
11400			} else {
11401				var overlay = $('.ilightbox-overlay');
11402				if (overlay.length) iL.closeAction();
11403			}
11404		}
11405	};
11406
11407	/**
11408	 * Parse style to pixels.
11409	 *
11410	 * @param {Object}   $element   jQuery object with element.
11411	 * @param {Property} property   CSS property to get the pixels from.
11412	 *
11413	 * @return {Int}
11414	 */
11415	function getPixel($element, property) {
11416		return parseInt($element.css(property), 10) || 0;
11417	}
11418
11419	/**
11420	 * Make sure that number is within the limits.
11421	 *
11422	 * @param {Number} number
11423	 * @param {Number} min
11424	 * @param {Number} max
11425	 *
11426	 * @return {Number}
11427	 */
11428	function within(number, min, max) {
11429		return number < min ? min : number > max ? max : number;
11430	}
11431
11432	/**
11433	 * Get viewport/window size (width and height).
11434	 *
11435	 * @return {Object}
11436	 */
11437	function getViewport() {
11438		var e = window,
11439			a = 'inner';
11440		if (!('innerWidth' in window)) {
11441			a = 'client';
11442			e = document.documentElement || document.body;
11443		}
11444		return {
11445			width: e[a + 'Width'],
11446			height: e[a + 'Height']
11447		}
11448	}
11449
11450	/**
11451	 * Remove hash tag from the URL
11452	 *
11453	 * @return {Void}
11454	 */
11455	function removeHash() {
11456		var scroll = getScrollXY();
11457
11458		//UNCODE change
11459		//window.location.hash = "";
11460		history.pushState("", document.title, window.location.pathname + window.location.search);
11461
11462		// Restore the scroll offset, should be flicker free
11463		window.scrollTo(scroll.x, scroll.y);
11464	}
11465
11466	/**
11467	 * Do the ajax requests with callback.
11468	 *
11469	 * @param {String}   url
11470	 * @param {Function} callback
11471	 *
11472	 * @return {Void}
11473	 */
11474	function doAjax(url, callback) {
11475		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');
11476		$.ajax({
11477			url: url,
11478			dataType: 'jsonp'
11479		});
11480
11481		iLCallback = function(data) {
11482			callback.call(this, data);
11483		};
11484	}
11485
11486	/**
11487	 * Find image from DOM elements
11488	 *
11489	 * @param {Element} element
11490	 *
11491	 * @return {Void}
11492	 */
11493	function findImageInElement(element) {
11494		var elements = $('*', element),
11495			imagesArr = new Array();
11496
11497		elements.each(function() {
11498			var url = "",
11499				element = this;
11500
11501			if ($(element).css("background-image") != "none") {
11502				url = $(element).css("background-image");
11503			} else if (typeof($(element).attr("src")) != "undefined" && element.nodeName.toLowerCase() == "img") {
11504				url = $(element).attr("src");
11505			}
11506
11507			if (url.indexOf("gradient") == -1) {
11508				url = url.replace(/url\(\"/g, "");
11509				url = url.replace(/url\(/g, "");
11510				url = url.replace(/\"\)/g, "");
11511				url = url.replace(/\)/g, "");
11512
11513				var urls = url.split(",");
11514
11515				for (var i = 0; i < urls.length; i++) {
11516					if (urls[i].length > 0 && $.inArray(urls[i], imagesArr) == -1) {
11517						var extra = "";
11518						if (browser.msie && browser.version < 9) {
11519							extra = "?" + floor(random() * 3000);
11520						}
11521						imagesArr.push(urls[i] + extra);
11522					}
11523				}
11524			}
11525		});
11526
11527		return imagesArr;
11528	}
11529
11530	/**
11531	 * Get file extension.
11532	 *
11533	 * @param {String} URL
11534	 *
11535	 * @return {String}
11536	 */
11537	function getExtension(URL) {
11538		if (URL !== null) {
11539			//UNCODE: fix issue to detect media type with query strings
11540			var ext = URL.indexOf('?') !== -1 ? URL.split('?')[0] : URL;
11541			ext = ext.split('.').pop().toLowerCase();
11542
11543			return ext;
11544		}
11545	}
11546
11547	/**
11548	 * Get type via extension.
11549	 *
11550	 * @param {String} URL
11551	 *
11552	 * @return {String}
11553	 */
11554	function getTypeByExtension(URL) {
11555		var type,
11556			ext = getExtension(URL);
11557
11558		if (extensions.image.indexOf(ext) !== -1) type = 'image';
11559		else if (extensions.flash.indexOf(ext) !== -1) type = 'flash';
11560		else if (extensions.video.indexOf(ext) !== -1) type = 'video';
11561		else if (ext.substring(0, 1) == "#") type = 'inline'; //Uncode addition
11562		else type = 'iframe';
11563
11564		return type;
11565	}
11566
11567	/**
11568	 * Return value from percent of a number.
11569	 *
11570	 * @param {Number} percent
11571	 * @param {Number} total
11572	 *
11573	 * @return {Number}
11574	 */
11575	function percentToValue(percent, total) {
11576		return parseInt((total / 100) * percent);
11577	}
11578
11579	/**
11580	 * A JavaScript equivalent of PHP’s parse_url.
11581	 *
11582	 * @param {String} url           The URL to parse.
11583	 *
11584	 * @return {Mixed}
11585	 */
11586	function parseURI(url) {
11587		var m = String(url).replace(/^\s+|\s+$/g, '').match(/^([^:\/?#]+:)?(\/\/(?:[^:@]*(?::[^:@]*)?@)?(([^:\/?#]*)(?::(\d*))?))?([^?#]*)(\?[^#]*)?(#[\s\S]*)?/);
11588		// authority = '//' + user + ':' + pass '@' + hostname + ':' port
11589		return (m ? {
11590			href: m[0] || '',
11591			protocol: m[1] || '',
11592			authority: m[2] || '',
11593			host: m[3] || '',
11594			hostname: m[4] || '',
11595			port: m[5] || '',
11596			pathname: m[6] || '',
11597			search: m[7] || '',
11598			hash: m[8] || ''
11599		} : null);
11600	}
11601
11602	/**
11603	 * Gets the absolute URI.
11604	 *
11605	 * @param {String} href     The relative URL.
11606	 * @param {String} base     The base URL.
11607	 *
11608	 * @return {String}         The absolute URL.
11609	 */
11610	function absolutizeURI(base, href) { // RFC 3986
11611		var iL = this;
11612
11613		function removeDotSegments(input) {
11614			var output = [];
11615			input.replace(/^(\.\.?(\/|$))+/, '')
11616				.replace(/\/(\.(\/|$))+/g, '/')
11617				.replace(/\/\.\.$/, '/../')
11618				.replace(/\/?[^\/]*/g, function(p) {
11619					if (p === '/..') {
11620						output.pop();
11621					} else {
11622						output.push(p);
11623					}
11624				});
11625			return output.join('').replace(/^\//, input.charAt(0) === '/' ? '/' : '');
11626		}
11627
11628		href = parseURI(href || '');
11629		base = parseURI(base || '');
11630
11631		return !href || !base ? null : (href.protocol || base.protocol) +
11632			(href.protocol || href.authority ? href.authority : base.authority) +
11633			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)) +
11634			(href.protocol || href.authority || href.pathname ? href.search : (href.search || base.search)) +
11635			href.hash;
11636	}
11637
11638	/**
11639	 * A JavaScript equivalent of PHP’s version_compare.
11640	 *
11641	 * @param {String} v1
11642	 * @param {String} v2
11643	 * @param {String} operator
11644	 *
11645	 * @return {Boolean}
11646	 */
11647	function version_compare(v1, v2, operator) {
11648		this.php_js = this.php_js || {};
11649		this.php_js.ENV = this.php_js.ENV || {};
11650		var i = 0,
11651			x = 0,
11652			compare = 0,
11653			vm = {
11654				'dev': -6,
11655				'alpha': -5,
11656				'a': -5,
11657				'beta': -4,
11658				'b': -4,
11659				'RC': -3,
11660				'rc': -3,
11661				'#': -2,
11662				'p': 1,
11663				'pl': 1
11664			},
11665			prepVersion = function(v) {
11666				v = ('' + v).replace(/[_\-+]/g, '.');
11667				v = v.replace(/([^.\d]+)/g, '.$1.').replace(/\.{2,}/g, '.');
11668				return (!v.length ? [-8] : v.split('.'));
11669			},
11670			numVersion = function(v) {
11671				return !v ? 0 : (isNaN(v) ? vm[v] || -7 : parseInt(v, 10));
11672			};
11673		v1 = prepVersion(v1);
11674		v2 = prepVersion(v2);
11675		x = max(v1.length, v2.length);
11676		for (i = 0; i < x; i++) {
11677			if (v1[i] == v2[i]) {
11678				continue;
11679			}
11680			v1[i] = numVersion(v1[i]);
11681			v2[i] = numVersion(v2[i]);
11682			if (v1[i] < v2[i]) {
11683				compare = -1;
11684				break;
11685			} else if (v1[i] > v2[i]) {
11686				compare = 1;
11687				break;
11688			}
11689		}
11690		if (!operator) {
11691			return compare;
11692		}
11693
11694		switch (operator) {
11695			case '>':
11696			case 'gt':
11697				return (compare > 0);
11698			case '>=':
11699			case 'ge':
11700				return (compare >= 0);
11701			case '<=':
11702			case 'le':
11703				return (compare <= 0);
11704			case '==':
11705			case '=':
11706			case 'eq':
11707				return (compare === 0);
11708			case '<>':
11709			case '!=':
11710			case 'ne':
11711				return (compare !== 0);
11712			case '':
11713			case '<':
11714			case 'lt':
11715				return (compare < 0);
11716			default:
11717				return null;
11718		}
11719	}
11720
11721
11722	// Begin the iLightBox plugin
11723	$.fn.iLightBox = function() {
11724
11725		var args = arguments,
11726			opt = ($.isPlainObject(args[0])) ? args[0] : args[1],
11727			items = ($.isArray(args[0]) || typeof args[0] == 'string') ? args[0] : args[1];
11728
11729		if (!opt) opt = {};
11730
11731		// Default options. Play carefully.
11732		var options = $.extend(true, {
11733			attr: 'href',
11734			path: 'vertical',
11735			skin: 'dark',
11736			linkId: false,
11737			infinite: false,
11738			startFrom: 0,
11739			randomStart: false,
11740			keepAspectRatio: true,
11741			maxScale: 1,
11742			minScale: .2,
11743			innerToolbar: false,
11744			smartRecognition: false,
11745			mobileOptimizer: true,
11746			fullAlone: true,
11747			fullViewPort: null,
11748			fullStretchTypes: 'flash, video',
11749			overlay: {
11750				blur: true,
11751				opacity: .85
11752			},
11753			controls: {
11754				arrows: false,
11755				slideshow: false,
11756				toolbar: true,
11757				fullscreen: true,
11758				thumbnail: true,
11759				keyboard: true,
11760				mousewheel: true,
11761				swipe: true
11762			},
11763			keyboard: {
11764				left: true, // previous
11765				right: true, // next
11766				up: true, // previous
11767				down: true, // next
11768				esc: true, // close
11769				shift_enter: true // fullscreen
11770			},
11771			show: {
11772				effect: true,
11773				speed: 300,
11774				title: true
11775			},
11776			hide: {
11777				effect: true,
11778				speed: 300
11779			},
11780			caption: {
11781				start: true,
11782				show: 'mouseenter',
11783				hide: 'mouseleave'
11784			},
11785			social: {
11786				start: true,
11787				show: 'mouseenter',
11788				hide: 'mouseleave',
11789				buttons: false
11790			},
11791			styles: {
11792				pageOffsetX: 0,
11793				pageOffsetY: 0,
11794				nextOffsetX: 45,
11795				nextOffsetY: 0,
11796				nextOpacity: 1,
11797				nextScale: 1,
11798				prevOffsetX: 45,
11799				prevOffsetY: 0,
11800				prevOpacity: 1,
11801				prevScale: 1
11802			},
11803			thumbnails: {
11804				maxWidth: 120,
11805				maxHeight: 80,
11806				normalOpacity: 1,
11807				activeOpacity: .6
11808			},
11809			effects: {
11810				reposition: true,
11811				repositionSpeed: 200,
11812				switchSpeed: 500,
11813				loadedFadeSpeed: 180,
11814				fadeSpeed: 200
11815			},
11816			slideshow: {
11817				pauseTime: 5000,
11818				pauseOnHover: false,
11819				startPaused: true
11820			},
11821			text: {
11822				close: "Press Esc to close",
11823				enterFullscreen: "Enter Fullscreen (Shift+Enter)",
11824				exitFullscreen: "Exit Fullscreen (Shift+Enter)",
11825				slideShow: "Slideshow",
11826				next: "Next",
11827				previous: "Previous"
11828			},
11829			errors: {
11830				loadImage: "An error occurred when trying to load photo.",
11831				loadContents: "An error occurred when trying to load contents.",
11832				missingPlugin: "The content your are attempting to view requires the <a href='{pluginspage}' target='_blank'>{type} plugin<\/a>."
11833			},
11834			ajaxSetup: {
11835				url: '',
11836				beforeSend: function(jqXHR, settings) {},
11837				cache: false,
11838				complete: function(jqXHR, textStatus) {},
11839				crossDomain: false,
11840				error: function(jqXHR, textStatus, errorThrown) {},
11841				success: function(data, textStatus, jqXHR) {},
11842				global: true,
11843				ifModified: false,
11844				username: null,
11845				password: null,
11846				type: 'GET'
11847			},
11848			callback: {}
11849		}, opt);
11850
11851		var instant = ($.isArray(items) || typeof items == 'string') ? true : false;
11852
11853		items = $.isArray(items) ? items : new Array();
11854
11855		if (typeof args[0] == 'string') items[0] = args[0];
11856
11857		if (version_compare($.fn.jquery, '1.8', '>=')) {
11858			var iLB = new iLightBox($(this), options, items, instant);
11859			return {
11860				close: function() {
11861					iLB.closeAction();
11862				},
11863				fullscreen: function() {
11864					iLB.fullScreenAction();
11865				},
11866				moveNext: function() {
11867					iLB.moveTo('next');
11868				},
11869				movePrev: function() {
11870					iLB.moveTo('prev');
11871				},
11872				goTo: function(index) {
11873					iLB.goTo(index);
11874				},
11875				refresh: function() {
11876					iLB.refresh();
11877				},
11878				reposition: function() {
11879					(arguments.length > 0) ? iLB.repositionPhoto(true): iLB.repositionPhoto();
11880				},
11881				setOption: function(options) {
11882					iLB.setOption(options);
11883				},
11884				destroy: function() {
11885					iLB.closeAction();
11886					iLB.dispatchItemsEvents();
11887				}
11888			};
11889		} else {
11890			throw "The jQuery version that was loaded is too old. iLightBox requires jQuery 1.8+";
11891		}
11892
11893	};
11894
11895
11896	$.iLightBox = function() {
11897		return $.fn.iLightBox(arguments[0], arguments[1]);
11898	};
11899
11900
11901	$.extend($.easing, {
11902		easeInCirc: function(x, t, b, c, d) {
11903			return -c * (sqrt(1 - (t /= d) * t) - 1) + b;
11904		},
11905		easeOutCirc: function(x, t, b, c, d) {
11906			return c * sqrt(1 - (t = t / d - 1) * t) + b;
11907		},
11908		easeInOutCirc: function(x, t, b, c, d) {
11909			if ((t /= d / 2) < 1) return -c / 2 * (sqrt(1 - t * t) - 1) + b;
11910			return c / 2 * (sqrt(1 - (t -= 2) * t) + 1) + b;
11911		}
11912	});
11913
11914	function getScrollXY() {
11915		var scrOfX = 0,
11916			scrOfY = 0;
11917		if (typeof(window.pageYOffset) == 'number') {
11918			//Netscape compliant
11919			scrOfY = window.pageYOffset;
11920			scrOfX = window.pageXOffset;
11921		} else if (document.body && (document.body.scrollLeft || document.body.scrollTop)) {
11922			//DOM compliant
11923			scrOfY = document.body.scrollTop;
11924			scrOfX = document.body.scrollLeft;
11925		} else if (document.documentElement && (document.documentElement.scrollLeft || document.documentElement.scrollTop)) {
11926			//IE6 standards compliant mode
11927			scrOfY = document.documentElement.scrollTop;
11928			scrOfX = document.documentElement.scrollLeft;
11929		}
11930		return {
11931			x: scrOfX,
11932			y: scrOfY
11933		};
11934	}
11935
11936	(function() {
11937		// add new event shortcuts
11938		$.each(("touchstart touchmove touchend " +
11939			"tap taphold " +
11940			"swipe swipeleft swiperight " +
11941			"scrollstart scrollstop").split(" "), function(i, name) {
11942
11943			$.fn[name] = function(fn) {
11944				return fn ? this.bind(name, fn) : this.trigger(name);
11945			};
11946
11947			// jQuery < 1.8
11948			// if ($.attrFn) {
11949			// 	$.attrFn[name] = true;
11950			// }
11951		});
11952
11953		var tapSettings = {
11954			startEvent: 'touchstart.iTap',
11955			endEvent: 'touchend.iTap'
11956		};
11957
11958		// tap Event:
11959		$.event.special.itap = {
11960			setup: function() {
11961
11962				var self = this,
11963					$self = $(this),
11964					start, stop;
11965
11966				$self.bind(tapSettings.startEvent, function(event) {
11967					start = getScrollXY();
11968
11969					$self.one(tapSettings.endEvent, function(event) {
11970						stop = getScrollXY();
11971
11972						var orgEvent = event || window.event;
11973						event = $.event.fix(orgEvent);
11974						event.type = "itap";
11975
11976						if ((start && stop) && (start.x == stop.x && start.y == stop.y))($.event.dispatch || $.event.handle).call(self, event);
11977
11978						start = stop = undefined;
11979					});
11980				});
11981			},
11982
11983			teardown: function() {
11984				$(this).unbind(tapSettings.startEvent);
11985			}
11986		};
11987	}());
11988
11989
11990	//Fullscreen API
11991	(function() {
11992		fullScreenApi = {
11993				supportsFullScreen: false,
11994				isFullScreen: function() {
11995					return false;
11996				},
11997				requestFullScreen: function() {},
11998				cancelFullScreen: function() {},
11999				fullScreenEventName: '',
12000				prefix: ''
12001			},
12002			browserPrefixes = 'webkit moz o ms khtml'.split(' ');
12003
12004		// check for native support
12005		if (typeof document.cancelFullScreen != 'undefined') {
12006			fullScreenApi.supportsFullScreen = true;
12007		} else {
12008			// check for fullscreen support by vendor prefix
12009			for (var i = 0, il = browserPrefixes.length; i < il; i++) {
12010				fullScreenApi.prefix = browserPrefixes[i];
12011
12012				if (typeof document[fullScreenApi.prefix + 'CancelFullScreen'] != 'undefined') {
12013					fullScreenApi.supportsFullScreen = true;
12014
12015					break;
12016				}
12017			}
12018		}
12019
12020		// update methods to do something useful
12021		if (fullScreenApi.supportsFullScreen) {
12022			fullScreenApi.fullScreenEventName = fullScreenApi.prefix + 'fullscreenchange';
12023
12024			fullScreenApi.isFullScreen = function() {
12025				switch (this.prefix) {
12026					case '':
12027						return document.fullScreen;
12028					case 'webkit':
12029						return document.webkitIsFullScreen;
12030					default:
12031						return document[this.prefix + 'FullScreen'];
12032				}
12033			}
12034			fullScreenApi.requestFullScreen = function(el) {
12035				return (this.prefix === '') ? el.requestFullScreen() : el[this.prefix + 'RequestFullScreen']();
12036			}
12037			fullScreenApi.cancelFullScreen = function(el) {
12038				return (this.prefix === '') ? document.cancelFullScreen() : document[this.prefix + 'CancelFullScreen']();
12039			}
12040		}
12041	}());
12042
12043	// Browser detect
12044	(function() {
12045		function uaMatch(ua) {
12046			ua = ua.toLowerCase();
12047
12048			var match = /(chrome)[ \/]([\w.]+)/.exec(ua) ||
12049				/(webkit)[ \/]([\w.]+)/.exec(ua) ||
12050				/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(ua) ||
12051				/(msie) ([\w.]+)/.exec(ua) ||
12052				ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec(ua) || [];
12053
12054			return {
12055				browser: match[1] || "",
12056				version: match[2] || "0"
12057			};
12058		}
12059
12060		var matched = uaMatch(navigator.userAgent);
12061		browser = {};
12062
12063		if (matched.browser) {
12064			browser[matched.browser] = true;
12065			browser.version = matched.version;
12066		}
12067
12068		// Chrome is Webkit, but Webkit is also Safari.
12069		if (browser.chrome) {
12070			browser.webkit = true;
12071		} else if (browser.webkit) {
12072			browser.safari = true;
12073		}
12074	}());
12075
12076	// Feature detects
12077	(function() {
12078		var prefixes = ['', 'webkit', 'moz', 'ms', 'o'];
12079		var el = document.createElement('div');
12080
12081		function testProp(prop) {
12082			for (var p = 0, pl = prefixes.length; p < pl; p++) {
12083				var prefixedProp = prefixes[p] ? prefixes[p] + prop.charAt(0).toUpperCase() + prop.slice(1) : prop;
12084				if (el.style[prefixedProp] !== undefined) {
12085					return prefixedProp;
12086				}
12087			}
12088		}
12089
12090		// Global support indicators
12091		transform = testProp('transform') || '';
12092		gpuAcceleration = testProp('perspective') ? 'translateZ(0) ' : '';
12093	}());
12094
12095
12096	/*
12097		PluginDetect v0.7.9
12098		www.pinlady.net/PluginDetect/license/
12099		[ getVersion onWindowLoaded BetterIE ]
12100		[ Flash QuickTime Shockwave ]
12101	*/
12102	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;
12102d++){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);
12102d.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)}
12102c.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;
12102if(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();
12103
12104	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)};
12105
12106
12107	/*
12108		jQuery hashchange event v1.3
12109		https://github.com/cowboy/jquery-hashchange
12110		Copyright (c) 2010 "Cowboy" Ben Alman
12111		Dual licensed under the MIT and GPL licenses.
12112	*/
12113	(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=
12114	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||
12115	"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;
12115a!==d&&(b.title=k.title,b.open(),e&&b.write('<script>document.domain="'+e+'"\x3c/script>'),b.close(),c.location.hash=a)}}();return h}()})();
12116
12117	if (!Array.prototype.filter) {
12118		Array.prototype.filter = function(fun /*, thisp */ ) {
12119			"use strict";
12120
12121			if (this == null)
12122				throw new TypeError();
12123
12124			var t = Object(this);
12125			var len = t.length >>> 0;
12126			if (typeof fun != "function")
12127				throw new TypeError();
12128
12129			var res = [];
12130			var thisp = arguments[1];
12131			for (var i = 0; i < len; i++) {
12132				if (i in t) {
12133					var val = t[i]; // in case fun mutates this
12134					if (fun.call(thisp, val, i, t))
12135						res.push(val);
12136				}
12137			}
12138
12139			return res;
12140		};
12141	}
12142
12143	if (!Array.prototype.indexOf) {
12144		Array.prototype.indexOf = function(searchElement, fromIndex) {
12145			var k;
12146
12147			if (this == null) {
12148				throw new TypeError('"this" is null or not defined');
12149			}
12150
12151			var O = Object(this);
12152
12153			var len = O.length >>> 0;
12154
12155			if (len === 0) {
12156				return -1;
12157			}
12158
12159			var n = +fromIndex || 0;
12160
12161			if (abs(n) === Infinity) {
12162				n = 0;
12163			}
12164
12165			if (n >= len) {
12166				return -1;
12167			}
12168
12169			k = max(n >= 0 ? n : len - abs(n), 0);
12170
12171			while (k < len) {
12172				var kValue;
12173				if (k in O && O[k] === searchElement) {
12174					return k;
12175				}
12176				k++;
12177			}
12178			return -1;
12179		};
12180	}
12181
12182	if (!Array.prototype.lastIndexOf) {
12183		Array.prototype.lastIndexOf = function(searchElement /*, fromIndex*/ ) {
12184			"use strict";
12185
12186			if (this == null)
12187				throw new TypeError();
12188
12189			var t = Object(this);
12190			var len = t.length >>> 0;
12191			if (len === 0)
12192				return -1;
12193
12194			var n = len;
12195			if (arguments.length > 1) {
12196				n = Number(arguments[1]);
12197				if (n != n)
12198					n = 0;
12199				else if (n != 0 && n != (1 / 0) && n != -(1 / 0))
12200					n = (n > 0 || -1) * floor(abs(n));
12201			}
12202
12203			var k = n >= 0 ? min(n, len - 1) : len - abs(n);
12204
12205			for (; k >= 0; k--) {
12206				if (k in t && t[k] === searchElement)
12207					return k;
12208			}
12209			return -1;
12210		};
12211	}
12212})(jQuery, this);
12213
12214/**
12215 * Owl carousel
vendor: 12,108 bytes, lines 12216-12722
12216 * @version 2.0.0-beta.3
12217 * @author Bartosz Wojciechowski
12218 * @license The MIT License (MIT)
12219 * @todo Lazy Load Icon
12220 * @todo prevent animationend bubling
12221 * @todo itemsScaleUp
12222 * @todo Test Zepto
12223 * @todo stagePadding calculate wrong active classes
12224 */
12225;(function($, window, document, undefined) {
12226
12227	/**
12228	 * Creates a carousel.
12229	 * @class The Owl Carousel.
12230	 * @public
12231	 * @param {HTMLElement|jQuery} element - The element to create the carousel for.
12232	 * @param {Object} [options] - The options
12233	 */
12234	function Owl(element, options) {
12235
12236		/**
12237		 * Current settings for the carousel.
12238		 * @public
12239		 */
12240		this.settings = null;
12241
12242		/**
12243		 * Current options set by the caller including defaults.
12244		 * @public
12245		 */
12246		this.options = $.extend({}, Owl.Defaults, options);
12247
12248		/**
12249		 * Plugin element.
12250		 * @public
12251		 */
12252		this.$element = $(element);
12253
12254		/**
12255		 * Proxied event handlers.
12256		 * @protected
12257		 */
12258		this._handlers = {};
12259
12260		/**
12261		 * References to the running plugins of this carousel.
12262		 * @protected
12263		 */
12264		this._plugins = {};
12265
12266		/**
12267		 * Currently suppressed events to prevent them from beeing retriggered.
12268		 * @protected
12269		 */
12270		this._supress = {};
12271
12272		/**
12273		 * Absolute current position.
12274		 * @protected
12275		 */
12276		this._current = null;
12277
12278		/**
12279		 * Animation speed in milliseconds.
12280		 * @protected
12281		 */
12282		this._speed = null;
12283
12284		/**
12285		 * Coordinates of all items in pixel.
12286		 * @todo The name of this member is missleading.
12287		 * @protected
12288		 */
12289		this._coordinates = [];
12290
12291		/**
12292		 * Current breakpoint.
12293		 * @todo Real media queries would be nice.
12294		 * @protected
12295		 */
12296		this._breakpoint = null;
12297
12298		/**
12299		 * Current width of the plugin element.
12300		 */
12301		this._width = null;
12302
12303		/**
12304		 * All real items.
12305		 * @protected
12306		 */
12307		this._items = [];
12308
12309		/**
12310		 * All cloned items.
12311		 * @protected
12312		 */
12313		this._clones = [];
12314
12315		/**
12316		 * Merge values of all items.
12317		 * @todo Maybe this could be part of a plugin.
12318		 * @protected
12319		 */
12320		this._mergers = [];
12321
12322		/**
12323		 * Widths of all items.
12324		 */
12325		this._widths = [];
12326
12327		/**
12328		 * Invalidated parts within the update process.
12329		 * @protected
12330		 */
12331		this._invalidated = {};
12332
12333		/**
12334		 * Ordered list of workers for the update process.
12335		 * @protected
12336		 */
12337		this._pipe = [];
12338
12339		/**
12340		 * Current state information for the drag operation.
12341		 * @todo #261
12342		 * @protected
12343		 */
12344		this._drag = {
12345			time: null,
12346			target: null,
12347			pointer: null,
12348			stage: {
12349				start: null,
12350				current: null
12351			},
12352			direction: null
12353		};
12354
12355		/**
12356		 * Current state information and their tags.
12357		 * @type {Object}
12358		 * @protected
12359		 */
12360		this._states = {
12361			current: {},
12362			tags: {
12363				'initializing': [ 'busy' ],
12364				'animating': [ 'busy' ],
12365				'dragging': [ 'interacting' ]
12366			}
12367		};
12368
12369		$.each([ 'onResize', 'onThrottledResize' ], $.proxy(function(i, handler) {
12370			this._handlers[handler] = $.proxy(this[handler], this);
12371		}, this));
12372
12373		$.each(Owl.Plugins, $.proxy(function(key, plugin) {
12374			this._plugins[key.charAt(0).toLowerCase() + key.slice(1)]
12375				= new plugin(this);
12376		}, this));
12377
12378		$.each(Owl.Workers, $.proxy(function(priority, worker) {
12379			this._pipe.push({
12380				'filter': worker.filter,
12381				'run': $.proxy(worker.run, this)
12382			});
12383		}, this));
12384
12385		this.setup();
12386		this.initialize();
12387	}
12388
12389	/**
12390	 * Default options for the carousel.
12391	 * @public
12392	 */
12393	Owl.Defaults = {
12394		items: 3,
12395		loop: false,
12396		center: false,
12397		rewind: false,
12398
12399		mouseDrag: true,
12400		touchDrag: true,
12401		pullDrag: true,
12402		freeDrag: false,
12403
12404		margin: 0,
12405		stagePadding: 0,
12406
12407		merge: false,
12408		mergeFit: true,
12409		autoWidth: false,
12410
12411		startPosition: 0,
12412		rtl: false,
12413
12414		smartSpeed: 250,
12415		fluidSpeed: false,
12416		dragEndSpeed: false,
12417
12418		responsive: {},
12419		responsiveRefreshRate: 200,
12420		responsiveBaseElement: window,
12421
12422		fallbackEasing: 'swing',
12423
12424		info: false,
12425
12426		nestedItemSelector: false,
12427		itemElement: 'div',
12428		stageElement: 'div',
12429
12430		refreshClass: 'owl-refresh',
12431		loadedClass: 'owl-loaded',
12432		loadingClass: 'owl-loading',
12433		rtlClass: 'owl-rtl',
12434		responsiveClass: 'owl-responsive',
12435		dragClass: 'owl-drag',
12436		itemClass: 'owl-item',
12437		stageClass: 'owl-stage',
12438		stageOuterClass: 'owl-stage-outer',
12439		grabClass: 'owl-grab'
12440	};
12441
12442	/**
12443	 * Enumeration for width.
12444	 * @public
12445	 * @readonly
12446	 * @enum {String}
12447	 */
12448	Owl.Width = {
12449		Default: 'default',
12450		Inner: 'inner',
12451		Outer: 'outer'
12452	};
12453
12454	/**
12455	 * Enumeration for types.
12456	 * @public
12457	 * @readonly
12458	 * @enum {String}
12459	 */
12460	Owl.Type = {
12461		Event: 'event',
12462		State: 'state'
12463	};
12464
12465	/**
12466	 * Contains all registered plugins.
12467	 * @public
12468	 */
12469	Owl.Plugins = {};
12470
12471	/**
12472	 * List of workers involved in the update process.
12473	 */
12474	Owl.Workers = [ {
12475		filter: [ 'width', 'settings' ],
12476		run: function() {
12477			this._width = (this.$element.closest('.px-gutter').length) ? 12 * Math.ceil(this.$element.width() / 12) : this.$element.width();
12478			//this._width = (UNCODE.isMobile) ? this.$element.width() : 12 * Math.ceil(this.$element.width() / 12);
12479		}
12480	}, {
12481		filter: [ 'width', 'items', 'settings' ],
12482		run: function(cache) {
12483			cache.current = this._items && this._items[this.relative(this._current)];
12484		}
12485	}, {
12486		filter: [ 'items', 'settings' ],
12487		run: function() {
12488			this.$stage.children('.cloned').remove();
12489		}
12490	}, {
12491		filter: [ 'width', 'items', 'settings' ],
12492		run: function(cache) {
12493			var margin = this.settings.margin || '',
12494				grid = !this.settings.autoWidth,
12495				rtl = this.settings.rtl,
12496				css = {
12497					'width': 'auto',
12498					'margin-left': rtl ? margin : '',
12499					'margin-right': rtl ? '' : margin
12500				};
12501
12502			!grid && this.$stage.children().css(css);
12503
12504			cache.css = css;
12505		}
12506	}, {
12507		filter: [ 'width', 'items', 'settings' ],
12508		run: function(cache) {
12509			//UNCODE edit to fix Chrome stage padding issue
12510			var width = Math.round((this.width() / this.settings.items).toFixed(3) - this.settings.margin),
12511				merge = null,
12512				iterator = this._items.length,
12513				grid = !this.settings.autoWidth,
12514				widths = [];
12515
12516			cache.items = {
12517				merge: false,
12518				width: width
12519			};
12520
12521			while (iterator--) {
12522				merge = this._mergers[iterator];
12523				merge = this.settings.mergeFit && Math.min(merge, this.settings.items) || merge;
12524
12525				cache.items.merge = merge > 1 || cache.items.merge;
12526
12527				widths[iterator] = !grid ? this._items[iterator].width() : width * merge;
12528			}
12529
12530			this._widths = widths;
12531		}
12532	}, {
12533		filter: [ 'items', 'settings' ],
12534		run: function() {
12535			var clones = [],
12536				items = this._items,
12537				settings = this.settings,
12538				view = Math.max(settings.items * 2, 4),
12539				size = Math.ceil(items.length / 2) * 2,
12540				repeat = settings.loop && items.length ? settings.rewind ? view : Math.max(view, size) : 0,
12541				append = '',
12542				prepend = '';
12543
12544			repeat /= 2;
12545
12546			while (repeat--) {
12547				clones.push(this.normalize(clones.length / 2, true));
12548				append = append + items[clones[clones.length - 1]][0].outerHTML;
12549				clones.push(this.normalize(items.length - 1 - (clones.length - 1) / 2, true));
12550				prepend = items[clones[clones.length - 1]][0].outerHTML + prepend;
12551			}
12552
12553			this._clones = clones;
12554
12555			// var appendVideo = $(append).find('.uncode-video-container');
12556			// if (appendVideo.length) {
12557			// 	appendVideo.attr('data-id', Math.round(Math.random() * 100000));
12558			// }
12559			// var prependVideo = $(prepend).find('.uncode-video-container');
12560			// if (prependVideo.length) {
12561			// 	prependVideo.attr('data-id', Math.round(Math.random() * 100000));
12562			// }
12563			$(append).addClass('cloned').appendTo(this.$stage);
12564			$(prepend).addClass('cloned').prependTo(this.$stage);
12565		}
12566	}, {
12567		filter: [ 'width', 'items', 'settings' ],
12568		run: function() {
12569			var rtl = this.settings.rtl ? 1 : -1,
12570				size = this._clones.length + this._items.length,
12571				iterator = -1,
12572				previous = 0,
12573				current = 0,
12574				coordinates = [];
12575
12576			while (++iterator < size) {
12577				previous = coordinates[iterator - 1] || 0;
12578				current = this._widths[this.relative(iterator)] + this.settings.margin;
12579				coordinates.push(previous + current * rtl);
12580			}
12581
12582			this._coordinates = coordinates;
12583		}
12584	}, {
12585		filter: [ 'width', 'items', 'settings' ],
12586		run: function() {
12587			var stagePadding = (this._width < 480 && this.settings.stagePadding > 0) ? 41 : (this._width * this.settings.stagePadding) / 200,
12588				padding = stagePadding,
12589				coordinates = this._coordinates,
12590				css = {
12591					'width': Math.ceil(Math.abs(coordinates[coordinates.length - 1])) + padding * 2,
12592					'padding-left': padding || '',
12593					'padding-right': padding || ''
12594				};
12595
12596			this.$stage.css(css);
12597		}
12598	}, {
12599		filter: [ 'width', 'items', 'settings' ],
12600		run: function(cache) {
12601			var iterator = this._coordinates.length,
12602				grid = !this.settings.autoWidth,
12603				items = this.$stage.children();
12604
12605			if (grid && cache.items.merge) {
12606				while (iterator--) {
12607					cache.css.width = this._widths[this.relative(iterator)];
12608					items.eq(iterator).css(cache.css);
12609				}
12610			} else if (grid) {
12611				cache.css.width = cache.items.width;
12612				items.css(cache.css);
12613			}
12614		}
12615	}, {
12616		filter: [ 'items' ],
12617		run: function() {
12618			this._coordinates.length < 1 && this.$stage.removeAttr('style');
12619		}
12620	}, {
12621		filter: [ 'width', 'items', 'settings' ],
12622		run: function(cache) {
12623			cache.current = cache.current ? this.$stage.children().index(cache.current) : 0;
12624			cache.current = Math.max(this.minimum(), Math.min(this.maximum(), cache.current));
12625			this.reset(cache.current);
12626		}
12627	}, {
12628		filter: [ 'position' ],
12629		run: function() {
12630			this.animate(this.coordinates(this._current));
12631		}
12632	}, {
12633		filter: [ 'width', 'position', 'items', 'settings' ],
12634		run: function() {
12635			var stagePadding = (this._width < 480 && this.settings.stagePadding > 0) ? 41 : (this._width * this.settings.stagePadding) / 200,
12636				rtl = this.settings.rtl ? 1 : -1,
12637				padding = this.settings.stagePadding * 2,
12638				begin = this.coordinates(this.current()) + padding,
12639				end = begin + this.width() * rtl,
12640				inner, outer, matches = [], i, n;
12641
12642			for (i = 0, n = this._coordinates.length; i < n; i++) {
12643				inner = this._coordinates[i - 1] || 0;
12644				outer = Math.abs(this._coordinates[i]) + padding * rtl;
12645
12646				if ((this.op(inner, '<=', begin) && (this.op(inner, '>', end)))
12647					|| (this.op(outer, '<', begin) && this.op(outer, '>', end))) {
12648					matches.push(i);
12649				}
12650			}
12651
12652			this.$stage.children('.active').removeClass('active');
12653			this.$stage.children(':eq(' + matches.join('), :eq(') + ')').addClass('active');
12654
12655			if (this.settings.center) {
12656				this.$stage.children('.center').removeClass('center');
12657				this.$stage.children().eq(this.current()).addClass('center');
12658			}
12659		}
12660	} ];
12661
12662	/**
12663	 * Initializes the carousel.
12664	 * @protected
12665	 */
12666	Owl.prototype.initialize = function() {
12667		this.enter('initializing');
12668		this.trigger('initialize');
12669
12670		this.$element.toggleClass(this.settings.rtlClass, this.settings.rtl);
12671
12672		if (this.settings.autoWidth && !this.is('pre-loading')) {
12673			var imgs, nestedSelector, width;
12674			imgs = this.$element.find('img');
12675			nestedSelector = this.settings.nestedItemSelector ? '.' + this.settings.nestedItemSelector : undefined;
12676			width = this.$element.children(nestedSelector).width();
12677
12678			if (imgs.length && width <= 0) {
12679				this.preloadAutoWidthImages(imgs);
12680			}
12681		}
12682
12683		this.$element.addClass(this.options.loadingClass);
12684
12685		// create stage
12686		this.$stage = $('<' + this.settings.stageElement + ' class="' + this.settings.stageClass + '"/>')
12687			.wrap('<div class="' + this.settings.stageOuterClass + '"/>');
12688
12689		// append stage
12690		this.$element.append(this.$stage.parent());
12691
12692		// append content
12693		this.replace(this.$element.children().not(this.$stage.parent()));
12694
12695		// check visibility
12696		if (this.$element.is(':visible')) {
12697			// update view
12698			this.refresh();
12699		} else {
12700			// invalidate width
12701			this.invalidate('width');
12702		}
12703
12704		this.$element
12705			.removeClass(this.options.loadingClass)
12706			.addClass(this.options.loadedClass);
12707
12708		// register event handlers
12709		this.registerEventHandlers();
12710
12711		this.leave('initializing');
12712		this.trigger('initialized');
12713	};
12714
12715	/**
12716	 * Setups the current settings.
12717	 * @todo Remove responsive classes. Why should adaptive designs be brought into IE8?
12718	 * @todo Support for media queries by using `matchMedia` would be nice.
12719	 * @public
12720	 */
12721	Owl.prototype.setup = function() {
12722		var viewport = this.viewport(),
12723			overwrites = this.options.responsive,
12724			match = -1,
12725			settings = null;
12726
12727		if (!overwrites) {
12728			settings = $.extend({}, this.options);
12729		} else {
12730			$.each(overwrites, function(breakpoint) {
12731				if (breakpoint <= viewport && breakpoint > match) {
12732					match = Number(breakpoint);
12733				}
12734			});
12735
12736			settings = $.extend({}, this.options, overwrites[match]);
12737			delete settings.responsive;
12738
12739			// responsive class
12740			if (settings.responsiveClass) {
12741				this.$element.attr('class',
12742					this.$element.attr('class').replace(new RegExp('(' + this.options.responsiveClass + '-)\\S+\\s', 'g'), '$1' + match)
12743				);
12744			}
12745		}
12746
12747		if (this.settings === null || this._breakpoint !== match) {
12748			this.trigger('change', { property: { name: 'settings', value: settings } });
12749			this._breakpoint = match;
12750			this.settings = settings;
12751			this.invalidate('settings');
12752			this.trigger('changed', { property: { name: 'settings', value: this.settings } });
12753		}
12754	};
12755
12756	/**
12757	 * Updates option logic if necessery.
12758	 * @protected
12759	 */
12760	Owl.prototype.optionsLogic = function() {
12761		if (this.settings.autoWidth) {
12762			this.settings.stagePadding = false;
12763			this.settings.merge = false;
12764		}
12765	};
12766
12767	/**
12768	 * Prepares an item before add.
12769	 * @todo Rename event parameter `content` to `item`.
12770	 * @protected
12771	 * @returns {jQuery|HTMLElement} - The item container.
12772	 */
12773	Owl.prototype.prepare = function(item, index) {
12774		var event = this.trigger('prepare', { content: item });
12775		if (!event.data) {
12776			event.data = $('<' + this.settings.itemElement + '/>')
12777				.addClass(this.options.itemClass).attr('data-index', index + 1).append(item)
12778		}
12779
12780		this.trigger('prepared', { content: event.data });
12781
12782		return event.data;
12783	};
12784
12785	/**
12786	 * Updates the view.
12787	 * @public
12788	 */
12789	Owl.prototype.update = function() {
12790		var i = 0,
12791			n = this._pipe.length,
12792			filter = $.proxy(function(p) { return this[p] }, this._invalidated),
12793			cache = {};
12794
12795		while (i < n) {
12796			if (this._invalidated.all || $.grep(this._pipe[i].filter, filter).length > 0) {
12797				this._pipe[i].run(cache);
12798			}
12799			i++;
12800		}
12801
12802		this._invalidated = {};
12803
12804		!this.is('valid') && this.enter('valid');
12805	};
12806
12807	/**
12808	 * Gets the width of the view.
12809	 * @public
12810	 * @param {Owl.Width} [dimension=Owl.Width.Default] - The dimension to return.
12811	 * @returns {Number} - The width of the view in pixel.
12812	 */
12813	Owl.prototype.width = function(dimension) {
12814		dimension = dimension || Owl.Width.Default;
12815		var stagePadding = (this._width < 480 && this.settings.stagePadding > 0) ? 41 : (this._width * this.settings.stagePadding) / 200;
12816		switch (dimension) {
12817			case Owl.Width.Inner:
12818			case Owl.Width.Outer:
12819				return this._width;
12820			default:
12821				return this._width - stagePadding * 2 + this.settings.margin;
12822		}
12823	};
12824
12825	/**
12826	 * Refreshes the carousel primarily for adaptive purposes.
12827	 * @public
12828	 */
12829	Owl.prototype.refresh = function() {
12830		this.enter('refreshing');
12831		this.trigger('refresh');
12832
12833		this.setup();
12834
12835		this.optionsLogic();
12836
12837		this.$element.addClass(this.options.refreshClass);
12838
12839		this.update();
12840
12841		this.$element.removeClass(this.options.refreshClass);
12842
12843		this.leave('refreshing');
12844		this.trigger('refreshed');
12845	};
12846
12847	/**
12848	 * Checks window `resize` event.
12849	 * @protected
12850	 */
12851	Owl.prototype.onThrottledResize = function() {
12852		window.clearTimeout(this.resizeTimer);
12853		this.resizeTimer = window.setTimeout(this._handlers.onResize, this.settings.responsiveRefreshRate);
12854	};
12855
12856	/**
12857	 * Checks window `resize` event.
12858	 * @protected
12859	 */
12860	Owl.prototype.onResize = function() {
12861		if (!this._items.length) {
12862			return false;
12863		}
12864
12865		if (this._width === this.$element.width()) {
12866			return false;
12867		}
12868
12869		if (!this.$element.is(':visible')) {
12870			return false;
12871		}
12872
12873		this.enter('resizing');
12874
12875		if (this.trigger('resize').isDefaultPrevented()) {
12876			this.leave('resizing');
12877			return false;
12878		}
12879
12880		this.invalidate('width');
12881
12882		this.refresh();
12883
12884		this.leave('resizing');
12885		this.trigger('resized');
12886	};
12887
12888	/**
12889	 * Registers event handlers.
12890	 * @todo Check `msPointerEnabled`
12891	 * @todo #261
12892	 * @protected
12893	 */
12894	Owl.prototype.registerEventHandlers = function() {
12895		if ($.support.transition) {
12896			this.$stage.on($.support.transition.end + '.owl.core', $.proxy(this.onTransitionEnd, this));
12897		}
12898
12899		if (this.settings.responsive !== false) {
12900			this.on(window, 'resize', this._handlers.onThrottledResize);
12901		}
12902
12903		if (this.settings.mouseDrag) {
12904			this.$element.addClass(this.options.dragClass);
12905			this.$stage.on('mousedown.owl.core', $.proxy(this.onDragStart, this));
12906			this.$stage.on('dragstart.owl.core selectstart.owl.core', function() { return false });
12907		}
12908
12909		if (this.settings.touchDrag){
12910			this.$stage.on('touchstart.owl.core', $.proxy(this.onDragStart, this));
12911			this.$stage.on('touchcancel.owl.core', $.proxy(this.onDragEnd, this));
12912		}
12913	};
12914
12915	/**
12916	 * Handles `touchstart` and `mousedown` events.
12917	 * @todo Horizontal swipe threshold as option
12918	 * @todo #261
12919	 * @protected
12920	 * @param {Event} event - The event arguments.
12921	 */
12922	Owl.prototype.onDragStart = function(event) {
12923		var stage = null;
12924
12925		if (event.which === 3) {
12926			return;
12927		}
12928
vendor: 9,106 bytes, lines 12929-13244
12929		if ($.support.transform) {
12930			stage = this.$stage.css('transform').replace(/.*\(|\)| /g, '').split(',');
12931			stage = {
12932				x: stage[stage.length === 16 ? 12 : 4],
12933				y: stage[stage.length === 16 ? 13 : 5]
12934			};
12935		} else {
12936			stage = this.$stage.position();
12937			stage = {
12938				x: this.settings.rtl ?
12939					stage.left + this.$stage.width() - this.width() + this.settings.margin :
12940					stage.left,
12941				y: stage.top
12942			};
12943		}
12944
12945		if (this.is('animating')) {
12946			$.support.transform ? this.animate(stage.x) : this.$stage.stop()
12947			this.invalidate('position');
12948		}
12949
12950		this.$element.toggleClass(this.options.grabClass, event.type === 'mousedown');
12951
12952		this.speed(0);
12953
12954		this._drag.time = new Date().getTime();
12955		this._drag.target = $(event.target);
12956		this._drag.stage.start = stage;
12957		this._drag.stage.current = stage;
12958		this._drag.pointer = this.pointer(event);
12959
12960		$(document).on('mouseup.owl.core touchend.owl.core', $.proxy(this.onDragEnd, this));
12961
12962		$(document).one('mousemove.owl.core touchmove.owl.core', $.proxy(function(event) {
12963			var delta = this.difference(this._drag.pointer, this.pointer(event));
12964
12965			$(document).on('mousemove.owl.core touchmove.owl.core', $.proxy(this.onDragMove, this));
12966
12967			if (Math.abs(delta.x) < Math.abs(delta.y) && this.is('valid')) {
12968				return;
12969			}
12970
12971			event.preventDefault();
12972
12973			this.enter('dragging');
12974			this.trigger('drag');
12975		}, this));
12976	};
12977
12978	/**
12979	 * Handles the `touchmove` and `mousemove` events.
12980	 * @todo #261
12981	 * @protected
12982	 * @param {Event} event - The event arguments.
12983	 */
12984	Owl.prototype.onDragMove = function(event) {
12985		var minimum = null,
12986			maximum = null,
12987			pull = null,
12988			delta = this.difference(this._drag.pointer, this.pointer(event)),
12989			stage = this.difference(this._drag.stage.start, delta);
12990
12991		if (!this.is('dragging')) {
12992			return;
12993		}
12994
12995		event.preventDefault();
12996
12997		if (this.settings.loop) {
12998			minimum = this.coordinates(this.minimum());
12999			maximum = this.coordinates(this.maximum() + 1) - minimum;
13000			stage.x = (((stage.x - minimum) % maximum + maximum) % maximum) + minimum;
13001		} else {
13002			minimum = this.settings.rtl ? this.coordinates(this.maximum()) : this.coordinates(this.minimum());
13003			maximum = this.settings.rtl ? this.coordinates(this.minimum()) : this.coordinates(this.maximum());
13004			pull = this.settings.pullDrag ? -1 * delta.x / 5 : 0;
13005			stage.x = Math.max(Math.min(stage.x, minimum + pull), maximum + pull);
13006		}
13007
13008		this._drag.stage.current = stage;
13009
13010		this.animate(stage.x);
13011	};
13012
13013	/**
13014	 * Handles the `touchend` and `mouseup` events.
13015	 * @todo #261
13016	 * @todo Threshold for click event
13017	 * @protected
13018	 * @param {Event} event - The event arguments.
13019	 */
13020	Owl.prototype.onDragEnd = function(event) {
13021		var delta = this.difference(this._drag.pointer, this.pointer(event)),
13022			stage = this._drag.stage.current,
13023			direction = delta.x > 0 ^ this.settings.rtl ? 'left' : 'right';
13024
13025		$(document).off('.owl.core');
13026
13027		this.$element.removeClass(this.options.grabClass);
13028
13029		if (delta.x !== 0 && this.is('dragging') || !this.is('valid')) {
13030			this.speed(this.settings.dragEndSpeed || this.settings.smartSpeed);
13031			this.current(this.closest(stage.x, delta.x !== 0 ? direction : this._drag.direction));
13032			this.invalidate('position');
13033			this.update();
13034
13035			this._drag.direction = direction;
13036
13037			if (Math.abs(delta.x) > 3 || new Date().getTime() - this._drag.time > 300) {
13038				this._drag.target.one('click.owl.core', function() { return false; });
13039			}
13040		}
13041
13042		if (!this.is('dragging')) {
13043			return;
13044		}
13045
13046		this.leave('dragging');
13047		this.trigger('dragged');
13048	};
13049
13050	/**
13051	 * Gets absolute position of the closest item for a coordinate.
13052	 * @todo Setting `freeDrag` makes `closest` not reusable. See #165.
13053	 * @protected
13054	 * @param {Number} coordinate - The coordinate in pixel.
13055	 * @param {String} direction - The direction to check for the closest item. Ether `left` or `right`.
13056	 * @return {Number} - The absolute position of the closest item.
13057	 */
13058	Owl.prototype.closest = function(coordinate, direction) {
13059		var position = -1,
13060			pull = 30,
13061			width = this.width(),
13062			coordinates = this.coordinates();
13063
13064		if (!this.settings.freeDrag) {
13065			// check closest item
13066			$.each(coordinates, $.proxy(function(index, value) {
13067				if (coordinate > value - pull && coordinate < value + pull) {
13068					position = index;
13069				} else if (this.op(coordinate, '<', value)
13070					&& this.op(coordinate, '>', coordinates[index + 1] || value - width)) {
13071					position = direction === 'left' ? index + 1 : index;
13072				}
13073				return position === -1;
13074			}, this));
13075		}
13076
13077		if (!this.settings.loop) {
13078			// non loop boundries
13079			if (this.op(coordinate, '>', coordinates[this.minimum()])) {
13080				position = coordinate = this.minimum();
13081			} else if (this.op(coordinate, '<', coordinates[this.maximum()])) {
13082				position = coordinate = this.maximum();
13083			}
13084		}
13085
13086		return position;
13087	};
13088
13089	/**
13090	 * Animates the stage.
13091	 * @todo #270
13092	 * @public
13093	 * @param {Number} coordinate - The coordinate in pixels.
13094	 */
13095	Owl.prototype.animate = function(coordinate) {
13096		var animate = this.speed() > 0;
13097
13098		this.is('animating') && this.onTransitionEnd();
13099
13100		if (animate) {
13101			this.enter('animating');
13102			this.trigger('translate');
13103		}
13104
13105		if ($.support.transform3d && $.support.transition) {
13106			this.$stage.css({
13107				transform: 'translate3d(' + coordinate + 'px,0px,0px)',
13108				transition: (this.speed() / 1000) + 's'
13109			});
13110		} else if (animate) {
13111			this.$stage.animate({
13112				left: coordinate + 'px'
13113			}, this.speed(), this.settings.fallbackEasing, $.proxy(this.onTransitionEnd, this));
13114		} else {
13115			this.$stage.css({
13116				left: coordinate + 'px'
13117			});
13118		}
13119	};
13120
13121	/**
13122	 * Checks whether the carousel is in a specific state or not.
13123	 * @param {String} state - The state to check.
13124	 * @returns {Boolean} - The flag which indicates if the carousel is busy.
13125	 */
13126	Owl.prototype.is = function(state) {
13127		return this._states.current[state] && this._states.current[state] > 0;
13128	};
13129
13130	/**
13131	 * Sets the absolute position of the current item.
13132	 * @public
13133	 * @param {Number} [position] - The new absolute position or nothing to leave it unchanged.
13134	 * @returns {Number} - The absolute position of the current item.
13135	 */
13136	Owl.prototype.current = function(position) {
13137		if (position === undefined) {
13138			return this._current;
13139		}
13140
13141		if (this._items.length === 0) {
13142			return undefined;
13143		}
13144
13145		position = this.normalize(position);
13146
13147		if (this._current !== position) {
13148			var event = this.trigger('change', { property: { name: 'position', value: position } });
13149
13150			if (event.data !== undefined) {
13151				position = this.normalize(event.data);
13152			}
13153
13154			this._current = position;
13155
13156			this.invalidate('position');
13157
13158			this.trigger('changed', { property: { name: 'position', value: this._current } });
13159		}
13160
13161		return this._current;
13162	};
13163
13164	/**
13165	 * Invalidates the given part of the update routine.
13166	 * @param {String} [part] - The part to invalidate.
13167	 * @returns {Array.<String>} - The invalidated parts.
13168	 */
13169	Owl.prototype.invalidate = function(part) {
13170		if ($.type(part) === 'string') {
13171			this._invalidated[part] = true;
13172			this.is('valid') && this.leave('valid');
13173		}
13174		return $.map(this._invalidated, function(v, i) { return i });
13175	};
13176
13177	/**
13178	 * Resets the absolute position of the current item.
13179	 * @public
13180	 * @param {Number} position - The absolute position of the new item.
13181	 */
13182	Owl.prototype.reset = function(position) {
13183		position = this.normalize(position);
13184
13185		if (position === undefined) {
13186			return;
13187		}
13188
13189		this._speed = 0;
13190		this._current = position;
13191
13192		this.suppress([ 'translate', 'translated' ]);
13193
13194		this.animate(this.coordinates(position));
13195
13196		this.release([ 'translate', 'translated' ]);
13197	};
13198
13199	/**
13200	 * Normalizes an absolute or a relative position of an item.
13201	 * @public
13202	 * @param {Number} position - The absolute or relative position to normalize.
13203	 * @param {Boolean} [relative=false] - Whether the given position is relative or not.
13204	 * @returns {Number} - The normalized position.
13205	 */
13206	Owl.prototype.normalize = function(position, relative) {
13207		var n = this._items.length,
13208			m = relative ? 0 : this._clones.length;
13209
13210		if (!$.isNumeric(position) || n < 1) {
13211			position = undefined;
13212		} else if (position < 0 || position >= n + m) {
13213			position = ((position - m / 2) % n + n) % n + m / 2;
13214		}
13215
13216		return position;
13217	};
13218
13219	/**
13220	 * Converts an absolute position of an item into a relative one.
13221	 * @public
13222	 * @param {Number} position - The absolute position to convert.
13223	 * @returns {Number} - The converted position.
13224	 */
13225	Owl.prototype.relative = function(position) {
13226		position -= this._clones.length / 2;
13227		return this.normalize(position, true);
13228	};
13229
13230	/**
13231	 * Gets the maximum position for the current item.
13232	 * @public
13233	 * @param {Boolean} [relative=false] - Whether to return an absolute position or a relative position.
13234	 * @returns {Number}
13235	 */
13236	Owl.prototype.maximum = function(relative) {
13237		var settings = this.settings,
13238			maximum = this._coordinates.length,
13239			boundary = Math.abs(this._coordinates[maximum - 1]) - this._width,
13240			i = -1, j;
13241
13242		if (settings.loop) {
13243			maximum = this._clones.length / 2 + this._items.length - 1;
13244		} else if (settings.autoWidth || settings.merge) {
13245			// binary search
13246			while (maximum - i > 1) {
13247				Math.abs(this._coordinates[j = maximum + i >> 1]) < boundary
13248					? i = j : maximum = j;
13249			}
13250		} else if (settings.center) {
13251			maximum = this._items.length - 1;
13252		} else {
13253			maximum = this._items.length - settings.items;
13254		}
13255
13256		if (relative) {
13257			maximum -= this._clones.length / 2;
13258		}
13259
13260		return Math.max(maximum, 0);
13261	};
13262
13263	/**
13264	 * Gets the minimum position for the current item.
13265	 * @public
13266	 * @param {Boolean} [relative=false] - Whether to return an absolute position or a relative position.
13267	 * @returns {Number}
13268	 */
13269	Owl.prototype.minimum = function(relative) {
13270		return relative ? 0 : this._clones.length / 2;
13271	};
13272
13273	/**
13274	 * Gets an item at the specified relative position.
13275	 * @public
13276	 * @param {Number} [position] - The relative position of the item.
13277	 * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given.
13278	 */
13279	Owl.prototype.items = function(position) {
13280		if (position === undefined) {
13281			return this._items.slice();
13282		}
13283
13284		position = this.normalize(position, true);
13285		return this._items[position];
13286	};
13287
13288	/**
13289	 * Gets an item at the specified relative position.
13290	 * @public
13291	 * @param {Number} [position] - The relative position of the item.
13292	 * @return {jQuery|Array.<jQuery>} - The item at the given position or all items if no position was given.
13293	 */
13294	Owl.prototype.mergers = function(position) {
13295		if (position === undefined) {
13296			return this._mergers.slice();
13297		}
13298
13299		position = this.normalize(position, true);
13300		return this._mergers[position];
13301	};
13302
13303	/**
13304	 * Gets the absolute positions of clones for an item.
13305	 * @public
13306	 * @param {Number} [position] - The relative position of the item.
13307	 * @returns {Array.<Number>} - The absolute positions of clones for the item or all if no position was given.
13308	 */
13309	Owl.prototype.clones = function(position) {
13310		var odd = this._clones.length / 2,
13311			even = odd + this._items.length,
13312			map = function(index) { return index % 2 === 0 ? even + index / 2 : odd - (index + 1) / 2 };
13313
13314		if (position === undefined) {
13315			return $.map(this._clones, function(v, i) { return map(i) });
13316		}
13317
13318		return $.map(this._clones, function(v, i) { return v === position ? map(i) : null });
13319	};
13320
13321	/**
13322	 * Sets the current animation speed.
13323	 * @public
13324	 * @param {Number} [speed] - The animation speed in milliseconds or nothing to leave it unchanged.
13325	 * @returns {Number} - The current animation speed in milliseconds.
13326	 */
13327	Owl.prototype.speed = function(speed) {
13328		if (speed !== undefined) {
13329			this._speed = speed;
13330		}
13331
13332		return this._speed;
13333	};
13334
13335	/**
13336	 * Gets the coordinate of an item.
13337	 * @todo The name of this method is missleanding.
13338	 * @public
13339	 * @param {Number} position - The absolute position of the item within `minimum()` and `maximum()`.
13340	 * @returns {Number|Array.<Number>} - The coordinate of the item in pixel or all coordinates.
13341	 */
13342	Owl.prototype.coordinates = function(position) {
13343		var coordinate = null;
13344
13345		if (position === undefined) {
13346			return $.map(this._coordinates, $.proxy(function(coordinate, index) {
13347				return this.coordinates(index);
13348			}, this));
13349		}
13350
13351		if (this.settings.center) {
13352			coordinate = this._coordinates[position];
13353			coordinate += (this.width() - coordinate + (this._coordinates[position - 1] || 0)) / 2 * (this.settings.rtl ? -1 : 1);
13354		} else {
13355			coordinate = this._coordinates[position - 1] || 0;
13356		}
13357
13358		return coordinate;
13359	};
13360
13361	/**
13362	 * Calculates the speed for a translation.
13363	 * @protected
13364	 * @param {Number} from - The absolute position of the start item.
13365	 * @param {Number} to - The absolute position of the target item.
13366	 * @param {Number} [factor=undefined] - The time factor in milliseconds.
13367	 * @returns {Number} - The time in milliseconds for the translation.
13368	 */
13369	Owl.prototype.duration = function(from, to, factor) {
13370		return Math.min(Math.max(Math.abs(to - from), 1), 6) * Math.abs((factor || this.settings.smartSpeed));
13371	};
13372
13373	/**
13374	 * Slides to the specified item.
13375	 * @public
13376	 * @param {Number} position - The position of the item.
13377	 * @param {Number} [speed] - The time in milliseconds for the transition.
13378	 */
13379	Owl.prototype.to = function(position, speed) {
13380		var current = this.current(),
13381			revert = null,
13382			distance = position - this.relative(current),
13383			direction = (distance > 0) - (distance < 0),
13384			items = this._items.length,
13385			minimum = this.minimum(),
13386			maximum = this.maximum();
13387
13388		if (this.settings.loop) {
13389			if (!this.settings.rewind && Math.abs(distance) > items / 2) {
13390				distance += direction * -1 * items;
13391			}
13392
13393			position = current + distance;
13394			revert = ((position - minimum) % items + items) % items + minimum;
13395
13396			if (revert !== position && revert - distance <= maximum && revert - distance > 0) {
13397				current = revert - distance;
13398				position = revert;
13399				this.reset(current);
13400			}
13401		} else if (this.settings.rewind) {
13402			maximum += 1;
13403			position = (position % maximum + maximum) % maximum;
13404		} else {
13405			position = Math.max(minimum, Math.min(maximum, position));
13406		}
13407
13408		this.speed(this.duration(current, position, speed));
13409		this.current(position);
13410
13411		if (this.$element.is(':visible')) {
13412			this.update();
13413		}
13414	};
13415
13416	/**
13417	 * Slides to the next item.
13418	 * @public
13419	 * @param {Number}
vendor: 12,284 bytes, lines 13419-13839
13419 [speed] - The time in milliseconds for the transition.
13420	 */
13421	Owl.prototype.next = function(speed) {
13422		speed = speed || false;
13423		this.to(this.relative(this.current()) + 1, speed);
13424	};
13425
13426	/**
13427	 * Slides to the previous item.
13428	 * @public
13429	 * @param {Number} [speed] - The time in milliseconds for the transition.
13430	 */
13431	Owl.prototype.prev = function(speed) {
13432		speed = speed || false;
13433		this.to(this.relative(this.current()) - 1, speed);
13434	};
13435
13436	/**
13437	 * Handles the end of an animation.
13438	 * @protected
13439	 * @param {Event} event - The event arguments.
13440	 */
13441	Owl.prototype.onTransitionEnd = function(event) {
13442
13443		// if css2 animation then event object is undefined
13444		if (event !== undefined) {
13445			event.stopPropagation();
13446
13447			// Catch only owl-stage transitionEnd event
13448			if ((event.target || event.srcElement || event.originalTarget) !== this.$stage.get(0)) {
13449				return false;
13450			}
13451		}
13452
13453		this.leave('animating');
13454		this.trigger('translated');
13455	};
13456
13457	/**
13458	 * Gets viewport width.
13459	 * @protected
13460	 * @return {Number} - The width in pixel.
13461	 */
13462	Owl.prototype.viewport = function() {
13463		var width;
13464		if (this.options.responsiveBaseElement !== window) {
13465			width = $(this.options.responsiveBaseElement).width();
13466		} else if (window.innerWidth) {
13467			width = window.innerWidth;
13468		} else if (document.documentElement && document.documentElement.clientWidth) {
13469			width = document.documentElement.clientWidth;
13470		} else {
13471			throw 'Can not detect viewport width.';
13472		}
13473
13474		return width;
13475	};
13476
13477	/**
13478	 * Replaces the current content.
13479	 * @public
13480	 * @param {HTMLElement|jQuery|String} content - The new content.
13481	 */
13482	Owl.prototype.replace = function(content) {
13483		this.$stage.empty();
13484		this._items = [];
13485
13486		if (content) {
13487			content = (content instanceof jQuery) ? content : $(content);
13488		}
13489
13490		if (this.settings.nestedItemSelector) {
13491			content = content.find('.' + this.settings.nestedItemSelector);
13492		}
13493
13494		content.filter(function() {
13495			return this.nodeType === 1;
13496		}).each($.proxy(function(index, item) {
13497			item = this.prepare(item, index);
13498			this.$stage.append(item);
13499			this._items.push(item);
13500			this._mergers.push(item.find('[data-merge]').addBack('[data-merge]').attr('data-merge') * 1 || 1);
13501		}, this));
13502
13503		this.reset($.isNumeric(this.settings.startPosition) ? this.settings.startPosition : 0);
13504
13505		this.invalidate('items');
13506	};
13507
13508	/**
13509	 * Adds an item.
13510	 * @todo Use `item` instead of `content` for the event arguments.
13511	 * @public
13512	 * @param {HTMLElement|jQuery|String} content - The item content to add.
13513	 * @param {Number} [position] - The relative position at which to insert the item otherwise the item will be added to the end.
13514	 */
13515	Owl.prototype.add = function(content, position) {
13516		var current = this.relative(this._current);
13517
13518		position = position === undefined ? this._items.length : this.normalize(position, true);
13519		content = content instanceof jQuery ? content : $(content);
13520
13521		this.trigger('add', { content: content, position: position });
13522
13523		content = this.prepare(content, this._items[current].index());
13524
13525		if (this._items.length === 0 || position === this._items.length) {
13526			this._items.length === 0 && this.$stage.append(content);
13527			this._items.length !== 0 && this._items[position - 1].after(content);
13528			this._items.push(content);
13529			this._mergers.push(content.find('[data-merge]').andSelf('[data-merge]').attr('data-merge') * 1 || 1);
13530		} else {
13531			this._items[position].before(content);
13532			this._items.splice(position, 0, content);
13533			this._mergers.splice(position, 0, content.find('[data-merge]').andSelf('[data-merge]').attr('data-merge') * 1 || 1);
13534		}
13535
13536		this._items[current] && this.reset(this._items[current].index());
13537
13538		this.invalidate('items');
13539
13540		this.trigger('added', { content: content, position: position });
13541	};
13542
13543	/**
13544	 * Removes an item by its position.
13545	 * @todo Use `item` instead of `content` for the event arguments.
13546	 * @public
13547	 * @param {Number} position - The relative position of the item to remove.
13548	 */
13549	Owl.prototype.remove = function(position) {
13550		position = this.normalize(position, true);
13551
13552		if (position === undefined) {
13553			return;
13554		}
13555
13556		this.trigger('remove', { content: this._items[position], position: position });
13557
13558		this._items[position].remove();
13559		this._items.splice(position, 1);
13560		this._mergers.splice(position, 1);
13561
13562		this.invalidate('items');
13563
13564		this.trigger('removed', { content: null, position: position });
13565	};
13566
13567	/**
13568	 * Preloads images with auto width.
13569	 * @todo Replace by a more generic approach
13570	 * @protected
13571	 */
13572	Owl.prototype.preloadAutoWidthImages = function(images) {
13573		images.each($.proxy(function(i, element) {
13574			this.enter('pre-loading');
13575			element = $(element);
13576			$(new Image()).one('load', $.proxy(function(e) {
13577				element.attr('src', e.target.src);
13578				element.css('opacity', 1);
13579				this.leave('pre-loading');
13580				!this.is('pre-loading') && !this.is('initializing') && this.refresh();
13581			}, this)).attr('src', element.attr('src') || element.attr('data-src') || element.attr('data-src-retina'));
13582		}, this));
13583	};
13584
13585	/**
13586	 * Destroys the carousel.
13587	 * @public
13588	 */
13589	Owl.prototype.destroy = function() {
13590
13591		this.$element.off('.owl.core');
13592		this.$stage.off('.owl.core');
13593		$(document).off('.owl.core');
13594
13595		if (this.settings.responsive !== false) {
13596			window.clearTimeout(this.resizeTimer);
13597			this.off(window, 'resize', this._handlers.onThrottledResize);
13598		}
13599
13600		for (var i in this._plugins) {
13601			this._plugins[i].destroy();
13602		}
13603
13604		this.$stage.children('.cloned').remove();
13605
13606		this.$stage.unwrap();
13607		this.$stage.children().contents().unwrap();
13608		this.$stage.children().unwrap();
13609
13610		this.$element
13611			.removeClass(this.options.refreshClass)
13612			.removeClass(this.options.loadingClass)
13613			.removeClass(this.options.loadedClass)
13614			.removeClass(this.options.rtlClass)
13615			.removeClass(this.options.dragClass)
13616			.removeClass(this.options.grabClass)
13617			.attr('class', this.$element.attr('class').replace(new RegExp(this.options.responsiveClass + '-\\S+\\s', 'g'), ''))
13618			.removeData('owl.carousel');
13619	};
13620
13621	/**
13622	 * Operators to calculate right-to-left and left-to-right.
13623	 * @protected
13624	 * @param {Number} [a] - The left side operand.
13625	 * @param {String} [o] - The operator.
13626	 * @param {Number} [b] - The right side operand.
13627	 */
13628	Owl.prototype.op = function(a, o, b) {
13629		var rtl = this.settings.rtl;
13630		switch (o) {
13631			case '<':
13632				return rtl ? a > b : a < b;
13633			case '>':
13634				return rtl ? a < b : a > b;
13635			case '>=':
13636				return rtl ? a <= b : a >= b;
13637			case '<=':
13638				return rtl ? a >= b : a <= b;
13639			default:
13640				break;
13641		}
13642	};
13643
13644	/**
13645	 * Attaches to an internal event.
13646	 * @protected
13647	 * @param {HTMLElement} element - The event source.
13648	 * @param {String} event - The event name.
13649	 * @param {Function} listener - The event handler to attach.
13650	 * @param {Boolean} capture - Wether the event should be handled at the capturing phase or not.
13651	 */
13652	Owl.prototype.on = function(element, event, listener, capture) {
13653		if (element.addEventListener) {
13654			element.addEventListener(event, listener, capture);
13655		} else if (element.attachEvent) {
13656			element.attachEvent('on' + event, listener);
13657		}
13658	};
13659
13660	/**
13661	 * Detaches from an internal event.
13662	 * @protected
13663	 * @param {HTMLElement} element - The event source.
13664	 * @param {String} event - The event name.
13665	 * @param {Function} listener - The attached event handler to detach.
13666	 * @param {Boolean} capture - Wether the attached event handler was registered as a capturing listener or not.
13667	 */
13668	Owl.prototype.off = function(element, event, listener, capture) {
13669		if (element.removeEventListener) {
13670			element.removeEventListener(event, listener, capture);
13671		} else if (element.detachEvent) {
13672			element.detachEvent('on' + event, listener);
13673		}
13674	};
13675
13676	/**
13677	 * Triggers a public event.
13678	 * @todo Remove `status`, `relatedTarget` should be used instead.
13679	 * @protected
13680	 * @param {String} name - The event name.
13681	 * @param {*} [data=null] - The event data.
13682	 * @param {String} [namespace=carousel] - The event namespace.
13683	 * @param {String} [state] - The state which is associated with the event.
13684	 * @param {Boolean} [enter=false] - Indicates if the call enters the specified state or not.
13685	 * @returns {Event} - The event arguments.
13686	 */
13687	Owl.prototype.trigger = function(name, data, namespace, state, enter) {
13688		var status = {
13689			item: { count: this._items.length, index: this.current() }
13690		}, handler = $.camelCase(
13691			$.grep([ 'on', name, namespace ], function(v) { return v })
13692				.join('-').toLowerCase()
13693		), event = $.Event(
13694			[ name, 'owl', namespace || 'carousel' ].join('.').toLowerCase(),
13695			$.extend({ relatedTarget: this }, status, data)
13696		);
13697
13698		if (!this._supress[name]) {
13699			$.each(this._plugins, function(name, plugin) {
13700				if (plugin.onTrigger) {
13701					plugin.onTrigger(event);
13702				}
13703			});
13704
13705			this.register({ type: Owl.Type.Event, name: name });
13706			this.$element.trigger(event);
13707
13708			if (this.settings && typeof this.settings[handler] === 'function') {
13709				this.settings[handler].call(this, event);
13710			}
13711		}
13712
13713		return event;
13714	};
13715
13716	/**
13717	 * Enters a state.
13718	 * @param name - The state name.
13719	 */
13720	Owl.prototype.enter = function(name) {
13721		$.each([ name ].concat(this._states.tags[name] || []), $.proxy(function(i, name) {
13722			if (this._states.current[name] === undefined) {
13723				this._states.current[name] = 0;
13724			}
13725
13726			this._states.current[name]++;
13727		}, this));
13728	};
13729
13730	/**
13731	 * Leaves a state.
13732	 * @param name - The state name.
13733	 */
13734	Owl.prototype.leave = function(name) {
13735		$.each([ name ].concat(this._states.tags[name] || []), $.proxy(function(i, name) {
13736			this._states.current[name]--;
13737		}, this));
13738	};
13739
13740	/**
13741	 * Registers an event or state.
13742	 * @public
13743	 * @param {Object} object - The event or state to register.
13744	 */
13745	Owl.prototype.register = function(object) {
13746		if (object.type === Owl.Type.Event) {
13747			if (!$.event.special[object.name]) {
13748				$.event.special[object.name] = {};
13749			}
13750
13751			if (!$.event.special[object.name].owl) {
13752				var _default = $.event.special[object.name]._default;
13753				$.event.special[object.name]._default = function(e) {
13754					if (_default && _default.apply && (!e.namespace || e.namespace.indexOf('owl') === -1)) {
13755						return _default.apply(this, arguments);
13756					}
13757					return e.namespace && e.namespace.indexOf('owl') > -1;
13758				};
13759				$.event.special[object.name].owl = true;
13760			}
13761		} else if (object.type === Owl.Type.State) {
13762			if (!this._states.tags[object.name]) {
13763				this._states.tags[object.name] = object.tags;
13764			} else {
13765				this._states.tags[object.name] = this._states.tags[object.name].concat(object.tags);
13766			}
13767
13768			this._states.tags[object.name] = $.grep(this._states.tags[object.name], $.proxy(function(tag, i) {
13769				return $.inArray(tag, this._states.tags[object.name]) === i;
13770			}, this));
13771		}
13772	};
13773
13774	/**
13775	 * Suppresses events.
13776	 * @protected
13777	 * @param {Array.<String>} events - The events to suppress.
13778	 */
13779	Owl.prototype.suppress = function(events) {
13780		$.each(events, $.proxy(function(index, event) {
13781			this._supress[event] = true;
13782		}, this));
13783	};
13784
13785	/**
13786	 * Releases suppressed events.
13787	 * @protected
13788	 * @param {Array.<String>} events - The events to release.
13789	 */
13790	Owl.prototype.release = function(events) {
13791		$.each(events, $.proxy(function(index, event) {
13792			delete this._supress[event];
13793		}, this));
13794	};
13795
13796	/**
13797	 * Gets unified pointer coordinates from event.
13798	 * @todo #261
13799	 * @protected
13800	 * @param {Event} - The `mousedown` or `touchstart` event.
13801	 * @returns {Object} - Contains `x` and `y` coordinates of current pointer position.
13802	 */
13803	Owl.prototype.pointer = function(event) {
13804		var result = { x: null, y: null };
13805
13806		event = event.originalEvent || event || window.event;
13807
13808		event = event.touches && event.touches.length ?
13809			event.touches[0] : event.changedTouches && event.changedTouches.length ?
13810				event.changedTouches[0] : event;
13811
13812		if (event.pageX) {
13813			result.x = event.pageX;
13814			result.y = event.pageY;
13815		} else {
13816			result.x = event.clientX;
13817			result.y = event.clientY;
13818		}
13819
13820		return result;
13821	};
13822
13823	/**
13824	 * Gets the difference of two vectors.
13825	 * @todo #261
13826	 * @protected
13827	 * @param {Object} - The first vector.
13828	 * @param {Object} - The second vector.
13829	 * @returns {Object} - The difference.
13830	 */
13831	Owl.prototype.difference = function(first, second) {
13832		return {
13833			x: first.x - second.x,
13834			y: first.y - second.y
13835		};
13836	};
13837
13838	/**
13839	 * The jQuery Plugin for the Owl Carousel
vendor: 32,216 bytes, lines 13840-15122
13840	 * @todo Navigation plugin `next` and `prev`
13841	 * @public
13842	 */
13843	$.fn.owlCarousel = function(option) {
13844		var args = Array.prototype.slice.call(arguments, 1);
13845
13846		return this.each(function() {
13847			var $this = $(this),
13848				data = $this.data('owl.carousel');
13849
13850			if (!data) {
13851				data = new Owl(this, typeof option == 'object' && option);
13852				$this.data('owl.carousel', data);
13853
13854				$.each([
13855					'next', 'prev', 'to', 'destroy', 'refresh', 'replace', 'add', 'remove'
13856				], function(i, event) {
13857					data.register({ type: Owl.Type.Event, name: event });
13858					data.$element.on(event + '.owl.carousel.core', $.proxy(function(e) {
13859						if (e.namespace && e.relatedTarget !== this) {
13860							this.suppress([ event ]);
13861							data[event].apply(this, [].slice.call(arguments, 1));
13862							this.release([ event ]);
13863						}
13864					}, data));
13865				});
13866			}
13867
13868			if (typeof option == 'string' && option.charAt(0) !== '_') {
13869				data[option].apply(data, args);
13870			}
13871		});
13872	};
13873
13874	/**
13875	 * The constructor for the jQuery Plugin
13876	 * @public
13877	 */
13878	$.fn.owlCarousel.Constructor = Owl;
13879
13880})(window.Zepto || window.jQuery, window, document);
13881
13882/**
13883 * AutoRefresh Plugin
13884 * @version 2.0.0-beta.3
13885 * @author Artus Kolanowski
13886 * @license The MIT License (MIT)
13887 */
13888;(function($, window, document, undefined) {
13889
13890	/**
13891	 * Creates the auto refresh plugin.
13892	 * @class The Auto Refresh Plugin
13893	 * @param {Owl} carousel - The Owl Carousel
13894	 */
13895	var AutoRefresh = function(carousel) {
13896		/**
13897		 * Reference to the core.
13898		 * @protected
13899		 * @type {Owl}
13900		 */
13901		this._core = carousel;
13902
13903		/**
13904		 * Refresh interval.
13905		 * @protected
13906		 * @type {number}
13907		 */
13908		this._interval = null;
13909
13910		/**
13911		 * Whether the element is currently visible or not.
13912		 * @protected
13913		 * @type {Boolean}
13914		 */
13915		this._visible = null;
13916
13917		/**
13918		 * All event handlers.
13919		 * @protected
13920		 * @type {Object}
13921		 */
13922		this._handlers = {
13923			'initialized.owl.carousel': $.proxy(function(e) {
13924				if (e.namespace && this._core.settings.autoRefresh) {
13925					this.watch();
13926				}
13927			}, this)
13928		};
13929
13930		// set default options
13931		this._core.options = $.extend({}, AutoRefresh.Defaults, this._core.options);
13932
13933		// register event handlers
13934		this._core.$element.on(this._handlers);
13935	};
13936
13937	/**
13938	 * Default options.
13939	 * @public
13940	 */
13941	AutoRefresh.Defaults = {
13942		autoRefresh: true,
13943		autoRefreshInterval: 500
13944	};
13945
13946	/**
13947	 * Watches the element.
13948	 */
13949	AutoRefresh.prototype.watch = function() {
13950		if (this._interval) {
13951			return;
13952		}
13953
13954		this._visible = this._core.$element.is(':visible');
13955		this._interval = window.setInterval($.proxy(this.refresh, this), this._core.settings.autoRefreshInterval);
13956	};
13957
13958	/**
13959	 * Refreshes the element.
13960	 */
13961	AutoRefresh.prototype.refresh = function() {
13962		if (this._core.$element.is(':visible') === this._visible) {
13963			return;
13964		}
13965
13966		this._visible = !this._visible;
13967
13968		this._core.$element.toggleClass('owl-hidden', !this._visible);
13969
13970		this._visible && (this._core.invalidate('width') && this._core.refresh());
13971	};
13972
13973	/**
13974	 * Destroys the plugin.
13975	 */
13976	AutoRefresh.prototype.destroy = function() {
13977		var handler, property;
13978
13979		window.clearInterval(this._interval);
13980
13981		for (handler in this._handlers) {
13982			this._core.$element.off(handler, this._handlers[handler]);
13983		}
13984		for (property in Object.getOwnPropertyNames(this)) {
13985			typeof this[property] != 'function' && (this[property] = null);
13986		}
13987	};
13988
13989	$.fn.owlCarousel.Constructor.Plugins.AutoRefresh = AutoRefresh;
13990
13991})(window.Zepto || window.jQuery, window, document);
13992
13993/**
13994 * Lazy Plugin
13995 * @version 2.0.0-beta.3
13996 * @author Bartosz Wojciechowski
13997 * @license The MIT License (MIT)
13998 */
13999;(function($, window, document, undefined) {
14000
14001	/**
14002	 * Creates the lazy plugin.
14003	 * @class The Lazy Plugin
14004	 * @param {Owl} carousel - The Owl Carousel
14005	 */
14006	var Lazy = function(carousel) {
14007
14008		/**
14009		 * Reference to the core.
14010		 * @protected
14011		 * @type {Owl}
14012		 */
14013		this._core = carousel;
14014
14015		/**
14016		 * Already loaded items.
14017		 * @protected
14018		 * @type {Array.<jQuery>}
14019		 */
14020		this._loaded = [];
14021
14022		/**
14023		 * Event handlers.
14024		 * @protected
14025		 * @type {Object}
14026		 */
14027		this._handlers = {
14028			'initialized.owl.carousel change.owl.carousel': $.proxy(function(e) {
14029				if (!e.namespace) {
14030					return;
14031				}
14032
14033				if (!this._core.settings || !this._core.settings.lazyLoad) {
14034					return;
14035				}
14036
14037				if ((e.property && e.property.name == 'position') || e.type == 'initialized') {
14038					var settings = this._core.settings,
14039						n = (settings.center && Math.ceil(settings.items / 2) || settings.items),
14040						i = ((settings.center && n * -1) || 0),
14041						position = ((e.property && e.property.value) || this._core.current()) + i,
14042						clones = this._core.clones().length,
14043						load = $.proxy(function(i, v) { this.load(v) }, this);
14044
14045					while (i++ < n) {
14046						this.load(clones / 2 + this._core.relative(position));
14047						clones && $.each(this._core.clones(this._core.relative(position)), load);
14048						position++;
14049					}
14050				}
14051			}, this)
14052		};
14053
14054		// set the default options
14055		this._core.options = $.extend({}, Lazy.Defaults, this._core.options);
14056
14057		// register event handler
14058		this._core.$element.on(this._handlers);
14059	}
14060
14061	/**
14062	 * Default options.
14063	 * @public
14064	 */
14065	Lazy.Defaults = {
14066		lazyLoad: false
14067	}
14068
14069	/**
14070	 * Loads all resources of an item at the specified position.
14071	 * @param {Number} position - The absolute position of the item.
14072	 * @protected
14073	 */
14074	Lazy.prototype.load = function(position) {
14075		var $item = this._core.$stage.children().eq(position),
14076			$elements = $item && $item.find('.owl-lazy');
14077
14078		if (!$elements || $.inArray($item.get(0), this._loaded) > -1) {
14079			return;
14080		}
14081
14082		$elements.each($.proxy(function(index, element) {
14083			var $element = $(element), image,
14084				url = (window.devicePixelRatio > 1 && $element.attr('data-src-retina')) || $element.attr('data-src');
14085
14086			this._core.trigger('load', { element: $element, url: url }, 'lazy');
14087
14088			if ($element.is('img')) {
14089				$element.one('load.owl.lazy', $.proxy(function() {
14090					$element.css('opacity', 1);
14091					this._core.trigger('loaded', { element: $element, url: url }, 'lazy');
14092				}, this)).attr('src', url);
14093			} else {
14094				image = new Image();
14095				image.onload = $.proxy(function() {
14096					$element.css({
14097						'background-image': 'url(' + url + ')',
14098						'opacity': '1'
14099					});
14100					this._core.trigger('loaded', { element: $element, url: url }, 'lazy');
14101				}, this);
14102				image.src = url;
14103			}
14104		}, this));
14105
14106		this._loaded.push($item.get(0));
14107	}
14108
14109	/**
14110	 * Destroys the plugin.
14111	 * @public
14112	 */
14113	Lazy.prototype.destroy = function() {
14114		var handler, property;
14115
14116		for (handler in this.handlers) {
14117			this._core.$element.off(handler, this.handlers[handler]);
14118		}
14119		for (property in Object.getOwnPropertyNames(this)) {
14120			typeof this[property] != 'function' && (this[property] = null);
14121		}
14122	};
14123
14124	$.fn.owlCarousel.Constructor.Plugins.Lazy = Lazy;
14125
14126})(window.Zepto || window.jQuery, window, document);
14127
14128/**
14129 * AutoHeight Plugin
14130 * @version 2.0.0-beta.3
14131 * @author Bartosz Wojciechowski
14132 * @license The MIT License (MIT)
14133 */
14134;(function($, window, document, undefined) {
14135
14136	/**
14137	 * Creates the auto height plugin.
14138	 * @class The Auto Height Plugin
14139	 * @param {Owl} carousel - The Owl Carousel
14140	 */
14141	var AutoHeight = function(carousel) {
14142		/**
14143		 * Reference to the core.
14144		 * @protected
14145		 * @type {Owl}
14146		 */
14147		this._core = carousel;
14148
14149		/**
14150		 * All event handlers.
14151		 * @protected
14152		 * @type {Object}
14153		 */
14154		this._handlers = {
14155			'initialized.owl.carousel refreshed.owl.carousel': $.proxy(function(e) {
14156				if (e.namespace && this._core.settings.autoHeight) {
14157					this.update();
14158				}
14159			}, this),
14160			'changed.owl.carousel': $.proxy(function(e) {
14161				if (e.namespace && this._core.settings.autoHeight && e.property.name == 'position'){
14162					this.update();
14163				}
14164			}, this),
14165			'loaded.owl.lazy': $.proxy(function(e) {
14166				if (e.namespace && this._core.settings.autoHeight
14167					&& e.element.closest('.' + this._core.settings.itemClass).index() === this._core.current()) {
14168					this.update();
14169				}
14170			}, this)
14171		};
14172
14173		// set default options
14174		this._core.options = $.extend({}, AutoHeight.Defaults, this._core.options);
14175
14176		// register event handlers
14177		this._core.$element.on(this._handlers);
14178	};
14179
14180	/**
14181	 * Default options.
14182	 * @public
14183	 */
14184	AutoHeight.Defaults = {
14185		autoHeight: false,
14186		autoHeightClass: 'owl-height'
14187	};
14188
14189	/**
14190	 * Updates the view.
14191	 */
14192	AutoHeight.prototype.update = function() {
14193		var start = this._core._current,
14194			end = start + this._core.settings.items,
14195			visible = this._core.$stage.children().toArray().slice(start, end);
14196			heights = [],
14197			maxheight = 0;
14198
14199		$.each(visible, function(index, item) {
14200			heights.push($(item).height());
14201		});
14202
14203		maxheight = Math.max.apply(null, heights);
14204
14205		this._core.$stage.parent()
14206			.height(maxheight)
14207			.addClass(this._core.settings.autoHeightClass);
14208	};
14209
14210	AutoHeight.prototype.destroy = function() {
14211		var handler, property;
14212
14213		for (handler in this._handlers) {
14214			this._core.$element.off(handler, this._handlers[handler]);
14215		}
14216		for (property in Object.getOwnPropertyNames(this)) {
14217			typeof this[property] != 'function' && (this[property] = null);
14218		}
14219	};
14220
14221	$.fn.owlCarousel.Constructor.Plugins.AutoHeight = AutoHeight;
14222
14223})(window.Zepto || window.jQuery, window, document);
14224
14225/**
14226 * Video Plugin
14227 * @version 2.0.0-beta.3
14228 * @author Bartosz Wojciechowski
14229 * @license The MIT License (MIT)
14230 */
14231;(function($, window, document, undefined) {
14232
14233	/**
14234	 * Creates the video plugin.
14235	 * @class The Video Plugin
14236	 * @param {Owl} carousel - The Owl Carousel
14237	 */
14238	var Video = function(carousel) {
14239		/**
14240		 * Reference to the core.
14241		 * @protected
14242		 * @type {Owl}
14243		 */
14244		this._core = carousel;
14245
14246		/**
14247		 * Cache all video URLs.
14248		 * @protected
14249		 * @type {Object}
14250		 */
14251		this._videos = {};
14252
14253		/**
14254		 * Current playing item.
14255		 * @protected
14256		 * @type {jQuery}
14257		 */
14258		this._playing = null;
14259
14260		/**
14261		 * All event handlers.
14262		 * @todo The cloned content removale is too late
14263		 * @protected
14264		 * @type {Object}
14265		 */
14266		this._handlers = {
14267			'initialized.owl.carousel': $.proxy(function(e) {
14268				if (e.namespace) {
14269					this._core.register({ type: 'state', name: 'playing', tags: [ 'interacting' ] });
14270				}
14271			}, this),
14272			'resize.owl.carousel': $.proxy(function(e) {
14273				if (e.namespace && this._core.settings.video && this.isInFullScreen()) {
14274					e.preventDefault();
14275				}
14276			}, this),
14277			'refreshed.owl.carousel': $.proxy(function(e) {
14278				if (e.namespace && this._core.is('resizing')) {
14279					this._core.$stage.find('.cloned .owl-video-frame').remove();
14280				}
14281			}, this),
14282			'changed.owl.carousel': $.proxy(function(e) {
14283				if (e.namespace && e.property.name === 'position' && this._playing) {
14284					this.stop();
14285				}
14286			}, this),
14287			'prepared.owl.carousel': $.proxy(function(e) {
14288				if (!e.namespace) {
14289					return;
14290				}
14291
14292				var $element = $(e.content).find('.owl-video');
14293
14294				if ($element.length) {
14295					$element.css('display', 'none');
14296					this.fetch($element, $(e.content));
14297				}
14298			}, this)
14299		};
14300
14301		// set default options
14302		this._core.options = $.extend({}, Video.Defaults, this._core.options);
14303
14304		// register event handlers
14305		this._core.$element.on(this._handlers);
14306
14307		this._core.$element.on('click.owl.video', '.owl-video-play-icon', $.proxy(function(e) {
14308			this.play(e);
14309		}, this));
14310	};
14311
14312	/**
14313	 * Default options.
14314	 * @public
14315	 */
14316	Video.Defaults = {
14317		video: false,
14318		videoHeight: false,
14319		videoWidth: false
14320	};
14321
14322	/**
14323	 * Gets the video ID and the type (YouTube/Vimeo only).
14324	 * @protected
14325	 * @param {jQuery} target - The target containing the video data.
14326	 * @param {jQuery} item - The item containing the video.
14327	 */
14328	Video.prototype.fetch = function(target, item) {
14329		var type = target.attr('data-vimeo-id') ? 'vimeo' : 'youtube',
14330			id = target.attr('data-vimeo-id') || target.attr('data-youtube-id'),
14331			width = target.attr('data-width') || this._core.settings.videoWidth,
14332			height = target.attr('data-height') || this._core.settings.videoHeight,
14333			url = target.attr('href');
14334
14335		if (url) {
14336			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+)?/);
14337
14338			if (id[3].indexOf('youtu') > -1) {
14339				type = 'youtube';
14340			} else if (id[3].indexOf('vimeo') > -1) {
14341				type = 'vimeo';
14342			} else {
14343				throw new Error('Video URL not supported.');
14344			}
14345			id = id[6];
14346		} else {
14347			throw new Error('Missing video URL.');
14348		}
14349
14350		this._videos[url] = {
14351			type: type,
14352			id: id,
14353			width: width,
14354			height: height
14355		};
14356
14357		item.attr('data-video', url);
14358
14359		this.thumbnail(target, this._videos[url]);
14360	};
14361
14362	/**
14363	 * Creates video thumbnail.
14364	 * @protected
14365	 * @param {jQuery} target - The target containing the video data.
14366	 * @param {Object} info - The video info object.
14367	 * @see `fetch`
14368	 */
14369	Video.prototype.thumbnail = function(target, video) {
14370		var tnLink,
14371			icon,
14372			path,
14373			dimensions = video.width && video.height ? 'style="width:' + video.width + 'px;height:' + video.height + 'px;"' : '',
14374			customTn = target.find('img'),
14375			srcType = 'src',
14376			lazyClass = '',
14377			settings = this._core.settings,
14378			create = function(path) {
14379				icon = '<div class="owl-video-play-icon"></div>';
14380
14381				if (settings.lazyLoad) {
14382					tnLink = '<div class="owl-video-tn ' + lazyClass + '" ' + srcType + '="' + path + '"></div>';
14383				} else {
14384					tnLink = '<div class="owl-video-tn" style="opacity:1;background-image:url(' + path + ')"></div>';
14385				}
14386				target.after(tnLink);
14387				target.after(icon);
14388			};
14389
14390		// wrap video content into owl-video-wrapper div
14391		target.wrap('<div class="owl-video-wrapper"' + dimensions + '></div>');
14392
14393		if (this._core.settings.lazyLoad) {
14394			srcType = 'data-src';
14395			lazyClass = 'owl-lazy';
14396		}
14397
14398		// custom thumbnail
14399		if (customTn.length) {
14400			create(customTn.attr(srcType));
14401			customTn.remove();
14402			return false;
14403		}
14404
14405		if (video.type === 'youtube') {
14406			path = "//img.youtube.com/vi/" + video.id + "/hqdefault.jpg";
14407			create(path);
14408		} else if (video.type === 'vimeo') {
14409			$.ajax({
14410				type: 'GET',
14411				url: '//vimeo.com/api/v2/video/' + video.id + '.json',
14412				jsonp: 'callback',
14413				dataType: 'jsonp',
14414				success: function(data) {
14415					path = data[0].thumbnail_large;
14416					create(path);
14417				}
14418			});
14419		}
14420	};
14421
14422	/**
14423	 * Stops the current video.
14424	 * @public
14425	 */
14426	Video.prototype.stop = function() {
14427		this._core.trigger('stop', null, 'video');
14428		this._playing.find('.owl-video-frame').remove();
14429		this._playing.removeClass('owl-video-playing');
14430		this._playing = null;
14431		this._core.leave('playing');
14432		this._core.trigger('stopped', null, 'video');
14433	};
14434
14435	/**
14436	 * Starts the current video.
14437	 * @public
14438	 * @param {Event} event - The event arguments.
14439	 */
14440	Video.prototype.play = function(event) {
14441		var target = $(event.target),
14442			item = target.closest('.' + this._core.settings.itemClass),
14443			video = this._videos[item.attr('data-video')],
14444			width = video.width || '100%',
14445			height = video.height || this._core.$stage.height(),
14446			html;
14447
14448		if (this._playing) {
14449			return;
14450		}
14451
14452		this._core.enter('playing');
14453		this._core.trigger('play', null, 'video');
14454
14455		item = this._core.items(this._core.relative(item.index()));
14456
14457		this._core.reset(item.index());
14458
14459		if (video.type === 'youtube') {
14460			html = '<iframe width="' + width + '" height="' + height + '" src="//www.youtube.com/embed/' +
14461				video.id + '?autoplay=1&v=' + video.id + '" frameborder="0" allowfullscreen></iframe>';
14462		} else if (video.type === 'vimeo') {
14463			html = '<iframe src="//player.vimeo.com/video/' + video.id +
14464				'?autoplay=1" width="' + width + '" height="' + height +
14465				'" frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen></iframe>';
14466		}
14467
14468		$('<div class="owl-video-frame">' + html + '</div>').insertAfter(item.find('.owl-video'));
14469
14470		this._playing = item.addClass('owl-video-playing');
14471	};
14472
14473	/**
14474	 * Checks whether an video is currently in full screen mode or not.
14475	 * @todo Bad style because looks like a readonly method but changes members.
14476	 * @protected
14477	 * @returns {Boolean}
14478	 */
14479	Video.prototype.isInFullScreen = function() {
14480		var element = document.fullscreenElement || document.mozFullScreenElement ||
14481				document.webkitFullscreenElement;
14482
14483		return element && $(element).parent().hasClass('owl-video-frame');
14484	};
14485
14486	/**
14487	 * Destroys the plugin.
14488	 */
14489	Video.prototype.destroy = function() {
14490		var handler, property;
14491
14492		this._core.$element.off('click.owl.video');
14493
14494		for (handler in this._handlers) {
14495			this._core.$element.off(handler, this._handlers[handler]);
14496		}
14497		for (property in Object.getOwnPropertyNames(this)) {
14498			typeof this[property] != 'function' && (this[property] = null);
14499		}
14500	};
14501
14502	$.fn.owlCarousel.Constructor.Plugins.Video = Video;
14503
14504})(window.Zepto || window.jQuery, window, document);
14505
14506/**
14507 * Animate Plugin
14508 * @version 2.0.0-beta.3
14509 * @author Bartosz Wojciechowski
14510 * @license The MIT License (MIT)
14511 */
14512;(function($, window, document, undefined) {
14513
14514	/**
14515	 * Creates the animate plugin.
14516	 * @class The Navigation Plugin
14517	 * @param {Owl} scope - The Owl Carousel
14518	 */
14519	var Animate = function(scope) {
14520		this.core = scope;
14521		this.core.options = $.extend({}, Animate.Defaults, this.core.options);
14522		this.swapping = true;
14523		this.previous = undefined;
14524		this.next = undefined;
14525
14526		this.handlers = {
14527			'change.owl.carousel': $.proxy(function(e) {
14528				if (e.namespace && e.property.name == 'position') {
14529					this.previous = this.core.current();
14530					this.next = e.property.value;
14531				}
14532			}, this),
14533			'drag.owl.carousel dragged.owl.carousel translated.owl.carousel': $.proxy(function(e) {
14534				if (e.namespace) {
14535					this.swapping = e.type == 'translated';
14536				}
14537			}, this),
14538			'translate.owl.carousel': $.proxy(function(e) {
14539				if (e.namespace && this.swapping && (this.core.options.animateOut || this.core.options.animateIn)) {
14540					this.swap();
14541				}
14542			}, this)
14543		};
14544
14545		this.core.$element.on(this.handlers);
14546	};
14547
14548	/**
14549	 * Default options.
14550	 * @public
14551	 */
14552	Animate.Defaults = {
14553		animateOut: false,
14554		animateIn: false
14555	};
14556
14557	/**
14558	 * Toggles the animation classes whenever an translations starts.
14559	 * @protected
14560	 * @returns {Boolean|undefined}
14561	 */
14562	Animate.prototype.swap = function() {
14563
14564		if (this.core.settings.items !== 1) {
14565			return;
14566		}
14567
14568		if (!$.support.animation || !$.support.transition) {
14569			return;
14570		}
14571
14572		this.core.speed(0);
14573
14574		var left,
14575			clear = $.proxy(this.clear, this),
14576			previous = this.core.$stage.children().eq(this.previous),
14577			next = this.core.$stage.children().eq(this.next),
14578			incoming = this.core.settings.animateIn,
14579			outgoing = this.core.settings.animateOut;
14580
14581		if (this.core.current() === this.previous) {
14582			return;
14583		}
14584
14585		if (outgoing) {
14586			left = this.core.coordinates(this.previous) - this.core.coordinates(this.next);
14587			previous.css( { 'left': left + 'px' } )
14588				.addClass('animated owl-animated-out')
14589				.addClass(outgoing)
14590				.on($.support.animation.end, clear);
14591		}
14592
14593		if (incoming) {
14594			next.addClass('animated owl-animated-in')
14595				.addClass(incoming)
14596				.on($.support.animation.end, clear);
14597		}
14598	};
14599
14600	Animate.prototype.clear = function(e) {
14601		if ($(e.target).hasClass('animated')) {
14602			$(e.target).css( { 'left': '' } )
14603				.removeClass('animated owl-animated-out owl-animated-in')
14604				.removeClass(this.core.settings.animateIn)
14605				.removeClass(this.core.settings.animateOut);
14606			this.core.onTransitionEnd();
14607		}
14608	};
14609
14610	/**
14611	 * Destroys the plugin.
14612	 * @public
14613	 */
14614	Animate.prototype.destroy = function() {
14615		var handler, property;
14616
14617		for (handler in this.handlers) {
14618			this.core.$element.off(handler, this.handlers[handler]);
14619		}
14620		for (property in Object.getOwnPropertyNames(this)) {
14621			typeof this[property] != 'function' && (this[property] = null);
14622		}
14623	};
14624
14625	$.fn.owlCarousel.Constructor.Plugins.Animate = Animate;
14626
14627})(window.Zepto || window.jQuery, window, document);
14628
14629/**
14630 * Autoplay Plugin
14631 * @version 2.0.0-beta.3
14632 * @author Bartosz Wojciechowski
14633 * @author Artus Kolanowski
14634 * @license The MIT License (MIT)
14635 */
14636;(function($, window, document, undefined) {
14637
14638	/**
14639	 * Creates the autoplay plugin.
14640	 * @class The Autoplay Plugin
14641	 * @param {Owl} scope - The Owl Carousel
14642	 */
14643	var Autoplay = function(carousel) {
14644		/**
14645		 * Reference to the core.
14646		 * @protected
14647		 * @type {Owl}
14648		 */
14649		this._core = carousel;
14650
14651		/**
14652		 * The autoplay interval.
14653		 * @type {Number}
14654		 */
14655		this._interval = null;
14656
14657		/**
14658		 * Indicates whenever the autoplay is paused.
14659		 * @type {Boolean}
14660		 */
14661		this._paused = false;
14662
14663		/**
14664		 * All event handlers.
14665		 * @protected
14666		 * @type {Object}
14667		 */
14668		this._handlers = {
14669			'changed.owl.carousel': $.proxy(function(e) {
14670				if (e.namespace && e.property.name === 'settings') {
14671					if (this._core.settings.autoplay) {
14672						this.play();
14673					} else {
14674						this.stop();
14675					}
14676				}
14677			}, this),
14678			'initialized.owl.carousel': $.proxy(function(e) {
14679				if (e.namespace && this._core.settings.autoplay) {
14680					this.play();
14681				}
14682			}, this),
14683			'play.owl.autoplay': $.proxy(function(e, t, s) {
14684				if (e.namespace) {
14685					this.play(t, s);
14686				}
14687			}, this),
14688			'stop.owl.autoplay': $.proxy(function(e) {
14689				if (e.namespace) {
14690					this.stop();
14691				}
14692			}, this),
14693			'mouseover.owl.autoplay': $.proxy(function() {
14694				if (this._core.settings.autoplayHoverPause && this._core.is('rotating')) {
14695					this.pause();
14696				}
14697			}, this),
14698			'mouseleave.owl.autoplay': $.proxy(function() {
14699				if (this._core.settings.autoplayHoverPause && this._core.is('rotating')) {
14700					this.play();
14701				}
14702			}, this)
14703		};
14704
14705		// register event handlers
14706		this._core.$element.on(this._handlers);
14707
14708		// set default options
14709		this._core.options = $.extend({}, Autoplay.Defaults, this._core.options);
14710	};
14711
14712	/**
14713	 * Default options.
14714	 * @public
14715	 */
14716	Autoplay.Defaults = {
14717		autoplay: false,
14718		autoplayTimeout: 5000,
14719		autoplayHoverPause: false,
14720		autoplaySpeed: false
14721	};
14722
14723	/**
14724	 * Starts the autoplay.
14725	 * @public
14726	 * @param {Number} [timeout] - The interval before the next animation starts.
14727	 * @param {Number} [speed] - The animation speed for the animations.
14728	 */
14729	Autoplay.prototype.play = function(timeout, speed) {
14730		this._paused = false;
14731
14732		if (this._core.is('rotating')) {
14733			return;
14734		}
14735
14736		this._core.enter('rotating');
14737
14738		this._interval = window.setInterval($.proxy(function() {
14739			if (this._paused || this._core.is('busy') || this._core.is('interacting') || document.hidden) {
14740				return;
14741			}
14742			this._core.next(speed || this._core.settings.autoplaySpeed);
14743		}, this), timeout || this._core.settings.autoplayTimeout);
14744	};
14745
14746	/**
14747	 * Stops the autoplay.
14748	 * @public
14749	 */
14750	Autoplay.prototype.stop = function() {
14751		if (!this._core.is('rotating')) {
14752			return;
14753		}
14754
14755		window.clearInterval(this._interval);
14756		this._core.leave('rotating');
14757	};
14758
14759	/**
14760	 * Stops the autoplay.
14761	 * @public
14762	 */
14763	Autoplay.prototype.pause = function() {
14764		if (!this._core.is('rotating')) {
14765			return;
14766		}
14767
14768		this._paused = true;
14769	};
14770
14771	/**
14772	 * Destroys the plugin.
14773	 */
14774	Autoplay.prototype.destroy = function() {
14775		var handler, property;
14776
14777		this.stop();
14778
14779		for (handler in this._handlers) {
14780			this._core.$element.off(handler, this._handlers[handler]);
14781		}
14782		for (property in Object.getOwnPropertyNames(this)) {
14783			typeof this[property] != 'function' && (this[property] = null);
14784		}
14785	};
14786
14787	$.fn.owlCarousel.Constructor.Plugins.autoplay = Autoplay;
14788
14789})(window.Zepto || window.jQuery, window, document);
14790
14791/**
14792 * Navigation Plugin
14793 * @version 2.0.0-beta.3
14794 * @author Artus Kolanowski
14795 * @license The MIT License (MIT)
14796 */
14797;(function($, window, document, undefined) {
14798	'use strict';
14799
14800	/**
14801	 * Creates the navigation plugin.
14802	 * @class The Navigation Plugin
14803	 * @param {Owl} carousel - The Owl Carousel.
14804	 */
14805	var Navigation = function(carousel) {
14806		/**
14807		 * Reference to the core.
14808		 * @protected
14809		 * @type {Owl}
14810		 */
14811		this._core = carousel;
14812
14813		/**
14814		 * Indicates whether the plugin is initialized or not.
14815		 * @protected
14816		 * @type {Boolean}
14817		 */
14818		this._initialized = false;
14819
14820		/**
14821		 * The current paging indexes.
14822		 * @protected
14823		 * @type {Array}
14824		 */
14825		this._pages = [];
14826
14827		/**
14828		 * All DOM elements of the user interface.
14829		 * @protected
14830		 * @type {Object}
14831		 */
14832		this._controls = {};
14833
14834		/**
14835		 * Markup for an indicator.
14836		 * @protected
14837		 * @type {Array.<String>}
14838		 */
14839		this._templates = [];
14840
14841		/**
14842		 * The carousel element.
14843		 * @type {jQuery}
14844		 */
14845		this.$element = this._core.$element;
14846
14847		/**
14848		 * Overridden methods of the carousel.
14849		 * @protected
14850		 * @type {Object}
14851		 */
14852		this._overrides = {
14853			next: this._core.next,
14854			prev: this._core.prev,
14855			to: this._core.to
14856		};
14857
14858		/**
14859		 * All event handlers.
14860		 * @protected
14861		 * @type {Object}
14862		 */
14863		this._handlers = {
14864			'prepared.owl.carousel': $.proxy(function(e) {
14865				if (e.namespace && this._core.settings.dotsData) {
14866					this._templates.push('<div class="' + this._core.settings.dotClass + '">' +
14867						$(e.content).find('[data-dot]').andSelf('[data-dot]').attr('data-dot') + '</div>');
14868				}
14869			}, this),
14870			'added.owl.carousel': $.proxy(function(e) {
14871				if (e.namespace && this._core.settings.dotsData) {
14872					this._templates.splice(e.position, 0, this._templates.pop());
14873				}
14874			}, this),
14875			'remove.owl.carousel': $.proxy(function(e) {
14876				if (e.namespace && this._core.settings.dotsData) {
14877					this._templates.splice(e.position, 1);
14878				}
14879			}, this),
14880			'changed.owl.carousel': $.proxy(function(e) {
14881				if (e.namespace && e.property.name == 'position') {
14882					this.draw();
14883				}
14884			}, this),
14885			'initialized.owl.carousel': $.proxy(function(e) {
14886				if (e.namespace && !this._initialized) {
14887					this._core.trigger('initialize', null, 'navigation');
14888					this.initialize();
14889					this.update();
14890					this.draw();
14891					this._initialized = true;
14892					this._core.trigger('initialized', null, 'navigation');
14893				}
14894			}, this),
14895			'refreshed.owl.carousel': $.proxy(function(e) {
14896				if (e.namespace && this._initialized) {
14897					this._core.trigger('refresh', null, 'navigation');
14898					this.update();
14899					this.draw();
14900					this._core.trigger('refreshed', null, 'navigation');
14901				}
14902			}, this)
14903		};
14904
14905		// set default options
14906		this._core.options = $.extend({}, Navigation.Defaults, this._core.options);
14907
14908		// register event handlers
14909		this.$element.on(this._handlers);
14910	};
14911
14912	/**
14913	 * Default options.
14914	 * @public
14915	 * @todo Rename `slideBy` to `navBy`
14916	 */
14917	Navigation.Defaults = {
14918		nav: false,
14919		navText: [ 'prev', 'next' ],
14920		navSpeed: false,
14921		navElement: 'div',
14922		navContainer: false,
14923		navContainerClass: 'owl-nav',
14924		navClass: [ 'owl-prev', 'owl-next' ],
14925		slideBy: 1,
14926		dotClass: 'owl-dot',
14927		dotsClass: 'owl-dots',
14928		dots: true,
14929		dotsEach: false,
14930		dotsData: false,
14931		dotsSpeed: false,
14932		dotsContainer: false
14933	};
14934
14935	/**
14936	 * Initializes the layout of the plugin and extends the carousel.
14937	 * @protected
14938	 */
14939	Navigation.prototype.initialize = function() {
14940		var override,
14941			settings = this._core.settings;
14942
14943		// create DOM structure for relative navigation
14944		this._controls.$relative = (settings.navContainer ? $(settings.navContainer)
14945			: $('<div>').addClass(settings.navContainerClass).appendTo(this.$element)).addClass('disabled');
14946
14947		this._controls.$previous = $('<' + settings.navElement + '>')
14948			.addClass(settings.navClass[0])
14949			.html(settings.navText[0])
14950			.prependTo(this._controls.$relative)
14951			.on('click', $.proxy(function(e) {
14952				this.prev(settings.navSpeed);
14953			}, this));
14954		this._controls.$next = $('<' + settings.navElement + '>')
14955			.addClass(settings.navClass[1])
14956			.html(settings.navText[1])
14957			.appendTo(this._controls.$relative)
14958			.on('click', $.proxy(function(e) {
14959				this.next(settings.navSpeed);
14960			}, this));
14961
14962		// create DOM structure for absolute navigation
14963		if (!settings.dotsData) {
14964			this._templates = [ $('<div>')
14965				.addClass(settings.dotClass)
14966				.append($('<span>'))
14967				.prop('outerHTML') ];
14968		}
14969
14970		this._controls.$absolute = (settings.dotsContainer ? $(settings.dotsContainer)
14971			: $('<div>').addClass(settings.dotsClass).appendTo(this.$element)).addClass('disabled');
14972
14973		this._controls.$absolute.on('click', 'div', $.proxy(function(e) {
14974			var index = $(e.target).parent().is(this._controls.$absolute)
14975				? $(e.target).index() : $(e.target).parent().index();
14976
14977			e.preventDefault();
14978
14979			this.to(index, settings.dotsSpeed);
14980		}, this));
14981
14982		// override public methods of the carousel
14983		for (override in this._overrides) {
14984			this._core[override] = $.proxy(this[override], this);
14985		}
14986	};
14987
14988	/**
14989	 * Destroys the plugin.
14990	 * @protected
14991	 */
14992	Navigation.prototype.destroy = function() {
14993		var handler, control, property, override;
14994
14995		for (handler in this._handlers) {
14996			this.$element.off(handler, this._handlers[handler]);
14997		}
14998		for (control in this._controls) {
14999			this._controls[control].remove();
15000		}
15001		for (override in this.overides) {
15002			this._core[override] = this._overrides[override];
15003		}
15004		for (property in Object.getOwnPropertyNames(this)) {
15005			typeof this[property] != 'function' && (this[property] = null);
15006		}
15007	};
15008
15009	/**
15010	 * Updates the internal state.
15011	 * @protected
15012	 */
15013	Navigation.prototype.update = function() {
15014		var i, j, k,
15015			lower = this._core.clones().length / 2,
15016			upper = lower + this._core.items().length,
15017			maximum = this._core.maximum(true),
15018			settings = this._core.settings,
15019			size = settings.center || settings.autoWidth || settings.dotsData
15020				? 1 : settings.dotsEach || settings.items;
15021
15022		if (settings.slideBy !== 'page') {
15023			settings.slideBy = Math.min(settings.slideBy, settings.items);
15024		}
15025
15026		if (settings.dots || settings.slideBy == 'page') {
15027			this._pages = [];
15028
15029			for (i = lower, j = 0, k = 0; i < upper; i++) {
15030				if (j >= size || j === 0) {
15031					this._pages.push({
15032						start: Math.min(maximum, i - lower),
15033						end: i - lower + size - 1
15034					});
15035					if (Math.min(maximum, i - lower) === maximum) {
15036						break;
15037					}
15038					j = 0, ++k;
15039				}
15040				j += this._core.mergers(this._core.relative(i));
15041			}
15042		}
15043	};
15044
15045	/**
15046	 * Draws the user interface.
15047	 * @todo The option `dotsData` wont work.
15048	 * @protected
15049	 */
15050	Navigation.prototype.draw = function() {
15051		var difference,
15052			settings = this._core.settings,
15053			disabled = this._core.items().length <= settings.items,
15054			index = this._core.relative(this._core.current()),
15055			loop = settings.loop || settings.rewind;
15056
15057		this._controls.$relative.toggleClass('disabled', !settings.nav || disabled);
15058
15059		if (settings.nav) {
15060			this._controls.$previous.toggleClass('disabled', !loop && index <= this._core.minimum(true));
15061			this._controls.$next.toggleClass('disabled', !loop && index >= this._core.maximum(true));
15062		}
15063
15064		this._controls.$absolute.toggleClass('disabled', !settings.dots || disabled);
15065
15066		if (settings.dots) {
15067			difference = this._pages.length - this._controls.$absolute.children().length;
15068
15069			if (settings.dotsData && difference !== 0) {
15070				this._controls.$absolute.html(this._templates.join(''));
15071			} else if (difference > 0) {
15072				this._controls.$absolute.append(new Array(difference + 1).join(this._templates[0]));
15073			} else if (difference < 0) {
15074				this._controls.$absolute.children().slice(difference).remove();
15075			}
15076
15077			this._controls.$absolute.find('.active').removeClass('active');
15078			this._controls.$absolute.children().eq($.inArray(this.current(), this._pages)).addClass('active');
15079		}
15080	};
15081
15082	/**
15083	 * Extends event data.
15084	 * @protected
15085	 * @param {Event} event - The event object which gets thrown.
15086	 */
15087	Navigation.prototype.onTrigger = function(event) {
15088		var settings = this._core.settings;
15089
15090		event.page = {
15091			index: $.inArray(this.current(), this._pages),
15092			count: this._pages.length,
15093			size: settings && (settings.center || settings.autoWidth || settings.dotsData
15094				? 1 : settings.dotsEach || settings.items)
15095		};
15096	};
15097
15098	/**
15099	 * Gets the current page position of the carousel.
15100	 * @protected
15101	 * @returns {Number}
15102	 */
15103	Navigation.prototype.current = function() {
15104		var current = this._core.relative(this._core.current());
15105		return $.grep(this._pages, $.proxy(function(page, index) {
15106			return page.start <= current && page.end >= current;
15107		}, this)).pop();
15108	};
15109
15110	/**
15111	 * Gets the current succesor/predecessor position.
15112	 * @protected
15113	 * @returns {Number}
15114	 */
15115	Navigation.prototype.getPosition = function(successor) {
15116		var position, length,
15117			settings = this._core.settings;
15118
15119		if (settings.slideBy == 'page') {
15120			position = $.inArray(this.current(), this._pages);
15121			length = this._pages.length;
15122			successor ? ++position : --position;
vendor: 5,678 bytes, lines 15123-15347
15123			position = this._pages[((position % length) + length) % length].start;
15124		} else {
15125			position = this._core.relative(this._core.current());
15126			length = this._core.items().length;
15127			successor ? position += settings.slideBy : position -= settings.slideBy;
15128		}
15129
15130		return position;
15131	};
15132
15133	/**
15134	 * Slides to the next item or page.
15135	 * @public
15136	 * @param {Number} [speed=false] - The time in milliseconds for the transition.
15137	 */
15138	Navigation.prototype.next = function(speed) {
15139		$.proxy(this._overrides.to, this._core)(this.getPosition(true), speed);
15140	};
15141
15142	/**
15143	 * Slides to the previous item or page.
15144	 * @public
15145	 * @param {Number} [speed=false] - The time in milliseconds for the transition.
15146	 */
15147	Navigation.prototype.prev = function(speed) {
15148		$.proxy(this._overrides.to, this._core)(this.getPosition(false), speed);
15149	};
15150
15151	/**
15152	 * Slides to the specified item or page.
15153	 * @public
15154	 * @param {Number} position - The position of the item or page.
15155	 * @param {Number} [speed] - The time in milliseconds for the transition.
15156	 * @param {Boolean} [standard=false] - Whether to use the standard behaviour or not.
15157	 */
15158	Navigation.prototype.to = function(position, speed, standard) {
15159		var length;
15160
15161		if (!standard) {
15162			length = this._pages.length;
15163			$.proxy(this._overrides.to, this._core)(this._pages[((position % length) + length) % length].start, speed);
15164		} else {
15165			$.proxy(this._overrides.to, this._core)(position, speed);
15166		}
15167	};
15168
15169	$.fn.owlCarousel.Constructor.Plugins.Navigation = Navigation;
15170
15171})(window.Zepto || window.jQuery, window, document);
15172
15173/**
15174 * Hash Plugin
15175 * @version 2.0.0-beta.3
15176 * @author Artus Kolanowski
15177 * @license The MIT License (MIT)
15178 */
15179;(function($, window, document, undefined) {
15180	'use strict';
15181
15182	/**
15183	 * Creates the hash plugin.
15184	 * @class The Hash Plugin
15185	 * @param {Owl} carousel - The Owl Carousel
15186	 */
15187	var Hash = function(carousel) {
15188		/**
15189		 * Reference to the core.
15190		 * @protected
15191		 * @type {Owl}
15192		 */
15193		this._core = carousel;
15194
15195		/**
15196		 * Hash index for the items.
15197		 * @protected
15198		 * @type {Object}
15199		 */
15200		this._hashes = {};
15201
15202		/**
15203		 * The carousel element.
15204		 * @type {jQuery}
15205		 */
15206		this.$element = this._core.$element;
15207
15208		/**
15209		 * All event handlers.
15210		 * @protected
15211		 * @type {Object}
15212		 */
15213		this._handlers = {
15214			/*'initialized.owl.carousel': $.proxy(function(e) {
15215				if (e.namespace && this._core.settings.startPosition === 'URLHash') {
15216					$(window).trigger('hashchange.owl.navigation');
15217				}
15218			}, this),*/
15219			'prepared.owl.carousel': $.proxy(function(e) {
15220				if (e.namespace) {
15221					var hash = $(e.content).find('[data-hash]').addBack('[data-hash]').attr('data-hash');
15222
15223					if (!hash) {
15224						return;
15225					}
15226
15227					this._hashes[hash] = e.content;
15228				}
15229			}, this),
15230			'changed.owl.carousel': $.proxy(function(e) {
15231				if (e.namespace && e.property.name === 'position') {
15232					var current = this._core.items(this._core.relative(this._core.current())),
15233						hash = $.map(this._hashes, function(item, hash) {
15234							return item === current ? hash : null;
15235						}).join();
15236
15237					if (!hash || window.location.hash.slice(1) === hash) {
15238						return;
15239					}
15240
15241					window.location.hash = hash;
15242				}
15243			}, this)
15244		};
15245
15246		// set default options
15247		this._core.options = $.extend({}, Hash.Defaults, this._core.options);
15248
15249		// register the event handlers
15250		this.$element.on(this._handlers);
15251
15252		// register event listener for hash navigation
15253		/*$(window).on('hashchange.owl.navigation', $.proxy(function(e) {
15254			var hash = window.location.hash.substring(1),
15255				items = this._core.$stage.children(),
15256				position = this._hashes[hash] && items.index(this._hashes[hash]);
15257
15258			if (position === undefined || position === this._core.current()) {
15259				return;
15260			}
15261
15262			this._core.to(this._core.relative(position), false, true);
15263		}, this));*/
15264	};
15265
15266	/**
15267	 * Default options.
15268	 * @public
15269	 */
15270	Hash.Defaults = {
15271		URLhashListener: false
15272	};
15273
15274	/**
15275	 * Destroys the plugin.
15276	 * @public
15277	 */
15278	Hash.prototype.destroy = function() {
15279		var handler, property;
15280
15281		$(window).off('hashchange.owl.navigation');
15282
15283		for (handler in this._handlers) {
15284			this._core.$element.off(handler, this._handlers[handler]);
15285		}
15286		for (property in Object.getOwnPropertyNames(this)) {
15287			typeof this[property] != 'function' && (this[property] = null);
15288		}
15289	};
15290
15291	$.fn.owlCarousel.Constructor.Plugins.Hash = Hash;
15292
15293})(window.Zepto || window.jQuery, window, document);
15294
15295/**
15296 * Support Plugin
15297 *
15298 * @version 2.0.0-beta.3
15299 * @author Vivid Planet Software GmbH
15300 * @author Artus Kolanowski
15301 * @license The MIT License (MIT)
15302 */
15303;(function($, window, document, undefined) {
15304
15305	var style = $('<support>').get(0).style,
15306		prefixes = 'Webkit Moz O ms'.split(' '),
15307		events = {
15308			transition: {
15309				end: {
15310					WebkitTransition: 'webkitTransitionEnd',
15311					MozTransition: 'transitionend',
15312					OTransition: 'oTransitionEnd',
15313					transition: 'transitionend'
15314				}
15315			},
15316			animation: {
15317				end: {
15318					WebkitAnimation: 'webkitAnimationEnd',
15319					MozAnimation: 'animationend',
15320					OAnimation: 'oAnimationEnd',
15321					animation: 'animationend'
15322				}
15323			}
15324		},
15325		tests = {
15326			csstransforms: function() {
15327				return !!test('transform');
15328			},
15329			csstransforms3d: function() {
15330				return !!test('perspective');
15331			},
15332			csstransitions: function() {
15333				return !!test('transition');
15334			},
15335			cssanimations: function() {
15336				return !!test('animation');
15337			}
15338		};
15339
15340	function test(property, prefixed) {
15341		var result = false,
15342			upper = property.charAt(0).toUpperCase() + property.slice(1);
15343
15344		$.each((property + ' ' + prefixes.join(upper + ' ') + upper).split(' '), function(i, property) {
15345			if (style[property] !== undefined) {
15346				result = prefixed ? property : true;
15347				return false;
15348			}
15349		});
15350
15351		return result;
15352	}
15353
15354	function prefixed(property) {
15355		return test(property, true);
15356	}
15357
15358	if (tests.csstransitions()) {
15359		/* jshint -W053 */
15360		$.support.transition = new String(prefixed('transition'))
15361		$.support.transition.end = events.transition.end[ $.support.transition ];
15362	}
15363
15364	if (tests.cssanimations()) {
15365		/* jshint -W053 */
15366		$.support.animation = new String(prefixed('animation'))
15367		$.support.animation.end = events.animation.end[ $.support.animation ];
15368	}
15369
15370	if (tests.csstransforms()) {
15371		/* jshint -W053 */
15372		$.support.transform = new String(prefixed('transform'));
15373		$.support.transform3d = tests.csstransforms3d();
15374	}
15375
15376})(window.Zepto || window.jQuery, window, document);
15377
15378(function(window, document, undefined) {
15379
15380    /**
15381     * Find the absolute position of an element
15382     */
15383    var absPos = function(element) {
15384        var offsetLeft, offsetTop;
15385        offsetLeft = offsetTop = 0;
15386        if (element.offsetParent) {
15387            do {
15388                offsetLeft += element.offsetLeft;
15389                offsetTop += element.offsetTop;
15390            } while (element = element.offsetParent);
15391        }
15392        return [offsetLeft, offsetTop];
15393    };
15394
15395    /**
15396     * @constructor Progress Circle class
15397     * @param params.canvas Canvas on which the circles will be drawn.
15398     * @param params.minRadius Inner radius of the innermost circle, in px.
15399     * @param params.arcWidth Width of each circle(to be more accurate, ring).
15400     * @param params.gapWidth Space between each circle.
15401     * @param params.centerX X coordinate of the center of circles.
15402     * @param params.centerY Y coordinate of the center of circles.
15403     * @param params.infoLineBaseAngle Base angle of the info line.
15404     * @param params.infoLineAngleInterval Angles between the info lines.
15405     */
15406    var ProgressCircle = function(params) {
15407        this.canvas = params.canvas;
15408        this.minRadius = params.minRadius || 15;
15409        this.arcWidth = params.arcWidth || 5;
15410        this.gapWidth = params.gapWidth || 3;
15411        this.centerX = params.centerX || this.canvas.width / 2;
15412        this.centerY = params.centerY || this.canvas.height / 2;
15413        this.infoLineLength = params.infoLineLength || 60;
15414        this.horizLineLength = params.horizLineLength || 10;
15415        this.infoLineAngleInterval = params.infoLineAngleInterval || Math.PI / 8;
15416        this.infoLineBaseAngle = params.infoLineBaseAngle || Math.PI / 6;
15417
15418        this.context = this.canvas.getContext('2d');
15419
15420        this.width = this.canvas.width;
15421        this.height = this.canvas.height;
15422
15423        this.circles = [];
15424        this.runningCount = 0;
15425    };
15426
15427    ProgressCircle.prototype = {
15428        constructor: ProgressCircle,
15429
15430        /**
15431         * @method Adds an progress monitor entry.
15432         * @param params.fillColor Color to fill in the circle.
15433         * @param params.outlineColor Color to outline the circle.
15434         * @param params.progressListener Callback function to fetch the progress.
15435         * @param params.infoListener Callback function to fetch the info.
15436         * @returns this
15437         */
15438        addEntry: function(params) {
15439            this.circles.push(new Circle({
15440                canvas: this.canvas,
15441                context: this.context,
15442                centerX: this.centerX,
15443                centerY: this.centerY,
15444                innerRadius: this.minRadius + this.circles.length *
15445                    (this.gapWidth + this.arcWidth),
15446                arcWidth: this.arcWidth,
15447                infoLineLength: this.infoLineLength,
15448                horizLineLength: this.horizLineLength,
15449
15450                id: this.circles.length,
15451                fillColor: params.fillColor,
15452                outlineColor: params.outlineColor,
15453                progressListener: params.progressListener,
15454                infoListener: params.infoListener,
15455                infoLineAngle: this.infoLineBaseAngle +
15456                    this.circles.length * this.infoLineAngleInterval,
15457            }));
15458
15459            return this;
15460        },
15461
15462        /**
15463         * @method Starts the monitor and updates with the given interval.
15464         * @param interval Interval between updates, in millisecond.
15465         * @returns this
15466         */
15467        start: function(interval) {
15468            var self = this;
15469            this.timer = setInterval(function() {
15470                self._update();
15471            }, interval || 33);
15472
15473            return this;
15474        },
15475
15476        /**
15477         * @method Stop the animation.
15478         */
15479        stop: function() {
15480            clearTimeout(this.timer);
15481        },
15482
15483        /**
15484         * @private
15485         * @method Call update on each circle and redraw them.
15486         * @returns this
15487         */
15488        _update: function() {
15489            this._clear();
15490            this.circles.forEach(function(circle, idx, array) {
15491                circle.update();
15492            });
15493
15494            return this;
15495        },
15496
15497        /**
15498         * @private
15499         * @method Clear the canvas.
15500         * @returns this
15501         */
15502        _clear: function() {
15503            this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
15504
15505            return this;
15506        },
15507
15508    };
15509
15510    /**
15511     * @private
15512     * @class Individual progress circle.
15513     * @param params.canvas Canvas on which the circle will be drawn.
15514     * @param params.context Context of the canvas.
15515     * @param params.innerRadius Inner radius of the circle, in px.
15516     * @param params.arcWidth Width of each arc(circle).
15517     * @param params.gapWidth Distance between each arc.
15518     * @param params.centerX X coordinate of the center of circles.
15519     * @param params.centerY Y coordinate of the center of circles.
15520     * @param params.fillColor Color to fill in the circle.
15521     * @param params.outlineColor Color to outline the circle.
15522     * @param params.progressListener Callback function to fetch the progress.
15523     * @param params.infoListener Callback function to fetch the info.
15524     * @param params.infoLineAngle Angle of info line.
15525     */
15526    var Circle = function(params) {
15527        this.id = params.id;
15528        this.canvas = params.canvas;
15529        this.context = params.context;
15530        this.centerX = params.centerX;
15531        this.centerY = params.centerY;
15532        this.arcWidth = params.arcWidth;
15533        this.innerRadius = params.innerRadius || 0;
15534        this.fillColor = params.fillColor || '#fff';
15535        this.outlineColor = params.outlineColor || this.fillColor;
15536        this.progressListener = params.progressListener;
15537        this.infoLineLength = params.infoLineLength || 250;
15538        this.horizLineLength = params.horizLineLength || 50;
15539        this.infoListener = params.infoListener;
15540        this.infoLineAngle = params.infoLineAngle;
15541
15542        this.outerRadius = this.innerRadius + this.arcWidth;
15543
15544        // If the info listener is not registered, then don't calculate
15545        // the related coordinates
15546        if (!this.infoListener) return;
15547
15548        // calculate the info-line turning points
15549        var angle = this.infoLineAngle,
15550            arcDistance = (this.innerRadius + this.outerRadius) / 2,
15551
15552            sinA = Math.sin(angle),
15553            cosA = Math.cos(angle);
15554
15555        this.infoLineStartX = this.centerX + sinA * arcDistance;
15556        this.infoLineStartY = this.centerY - cosA * arcDistance;
15557
15558        this.infoLineMidX = this.centerX + sinA * this.infoLineLength;
15559        this.infoLineMidY = this.centerY - cosA * this.infoLineLength;
15560
15561        this.infoLineEndX = this.infoLineMidX +
15562             (sinA < 0 ? -this.horizLineLength : this.horizLineLength);
15563        this.infoLineEndY = this.infoLineMidY;
15564
15565        var infoText = document.createElement('div'),
15566            style = infoText.style;
15567
15568        style.color = this.fillColor;
15569        style.position = 'absolute';
15570        style.left = this.infoLineEndX + absPos(this.canvas)[0] + 'px';
15571        // style.top will be calculated in the `drawInfo` method. Since
15572        // user may want to change the size of the font, so the top offset
15573        // must be updated in each loop.
15574        infoText.className = 'ProgressCircleInfo'; // For css styling
15575        infoText.id = 'progress_circle_info_' + this.id;
15576        document.body.appendChild(infoText);
15577        this.infoText = infoText;
15578    };
15579
15580    Circle.prototype = {
15581        constructor: Circle,
15582
15583        update: function() {
15584            this.progress = this.progressListener();
15585            this._draw();
15586
15587            if (this.infoListener) {
15588                this.info = this.infoListener();
15589                this._drawInfo();
15590            }
15591        },
15592
15593        /**
15594         * @private
15595         * @method Draw the circle on the canvas.
15596         * @returns this
15597         */
15598        _draw: function() {
15599            var ctx = this.context,
15600
15601                ANGLE_OFFSET = -Math.PI / 2,
15602
15603                startAngle = 0 + ANGLE_OFFSET,
15604                endAngle= startAngle + this.progress * Math.PI * 2,
15605
15606                x = this.centerX,
15607                y = this.centerY,
15608
15609                innerRadius = this.innerRadius - this.arcWidth - 1,
15610                outerRadius = this.outerRadius - this.arcWidth - 1;
15611
15612            if ( innerRadius < 0 )
15613            	return;
15614
15615            ctx.fillStyle = this.fillColor;
15616            ctx.strokeStyle = this.outlineColor;
15617
15618            ctx.beginPath();
15619            ctx.arc(x, y, innerRadius, startAngle, endAngle, false);
15620            ctx.arc(x, y, outerRadius, endAngle, startAngle, true);
15621            ctx.closePath();
15622            ctx.stroke();
15623            ctx.fill();
15624
15625            return this;
15626        },
15627
15628        /**
15629         * @private
15630         * @method Draw the info lines and info text.
15631         * @returns this
15632         */
15633        _drawInfo: function() {
15634
15635            var pointList, lineHeight;
15636
15637            pointList = [
15638                [this.infoLineStartX, this.infoLineStartY],
15639                [this.infoLineMidX, this.infoLineMidY],
15640                [this.infoLineEndX, this.infoLineEndY],
15641            ];
15642            this._drawSegments(pointList, false);
15643
15644            this.infoText.innerHTML = this.info;
15645
15646            lineHeight = this.infoText.offsetHeight;
15647            this.infoText.style.top = this.infoLineEndY +
15648                absPos(this.canvas)[1] - lineHeight / 2 + 'px';
15649
15650            return this;
15651        },
15652
15653        /**
15654         * @private
15655         * @method Helper function to draw the segments
15656         * @param pointList An array of points in the form of [x, y].
15657         * @param close Whether to connect the first and last point.
15658         */
15659        _drawSegments: function(pointList, close) {
15660            var ctx = this.context;
15661
15662            ctx.beginPath();
15663            ctx.moveTo(pointList[0][0], pointList[0][1]);
15664            for (var i = 1; i < pointList.length; ++i) {
15665                ctx.lineTo(pointList[i][0], pointList[i][1]);
15666            }
15667
15668            if (close) {
15669                ctx.closePath();
15670            }
15671            ctx.stroke();
15672        },
15673    };
15674
15675    window.ProgressCircle = ProgressCircle;
15676
15677})(window, document);
15678
15679
15680
15681
15682/* =========================================================
15683 * jquery.vc_chart.js v1.0
15684 * =========================================================
15685 * Copyright 2013 Wpbakery
15686 *
15687 * Jquery chart plugin for the Visual Composer.
15688 * ========================================================= */
15689(function($){
15690    /**
15691     * Pie chart animated.
15692     * @param element - DOM element
15693     * @param options - settings object.
15694     * @constructor
15695     */
15696    var VcChart = function(element, options) {
15697        this.el = element;
15698        this.$el = $(this.el);
15699        var $this = this;
15700        $this.options = $.extend({
15701            color: 'wpb_button',
15702            units: '',
15703            width: '',
15704            label_selector: '.vc_pie_chart_value',
15705            back_selector: '.vc_pie_chart_back',
15706            responsive: true
15707        }, options);
15708        $this.init();
15709    };
15710    VcChart.prototype = {
15711        constructor: VcChart,
15712        _progress_v: 0,
15713        animated: false,
15714        colors: {
15715            'wpb_button': 'rgba(247, 247, 247, 1)',
15716            'btn-primary': 'rgba(0, 136, 204, 1)',
15717            'btn-info': 'rgba(88, 185, 218, 1)',
15718            'btn-success': 'rgba(106, 177, 101, 1)',
15719            'btn-warning': 'rgba(255, 153, 0, 1)',
15720            'btn-danger': 'rgba(255, 103, 91, 1)',
15721            'btn-inverse': 'rgba(85, 85, 85, 1)'
15722        },
15723        init: function() {
15724            this.setupColor();
15725            this.value = this.$el.data('pie-value')/100;
15726            this.label_value = this.$el.data('pie-label-value') || this.$el.data('pie-value');
15727            this.$wrapper = $('.vc_pie_wrapper', this.$el);
15728            this.$label = $(this.options.label_selector, this.$el);
15729            this.$back = $(this.options.back_selector, this.$el);
15730            this.$canvas = this.$el.find('canvas');
15731            this.arcWidth = this.$el.data('pie-width') * 2;
15732            this.draw();
15733            this.setWayPoint();
15734            if(this.options.responsive === true) this.setResponsive();
15735            if (UNCODE.isMobile) this._progress_v = this.value;
15736        },
15737        setupColor: function() {
15738            // if(typeof this.colors[this.options.color] !== 'undefined') {
15739            //     this.color = this.colors[this.options.color];
15740            // } else if(typeof this.options.color === 'string' && this.options.color.match(/^rgba?\([^\)]+\)/)) {
15741            //     this.color = this.options.color;
15742            // } else {
15743            //     this.color = 'rgba(247, 247, 247, 0.2)';
15744            // }
15745            if(typeof this.options.color !== 'undefined') {
15746                this.color = this.options.color;
15747            } else this.color = 'rgba(247, 247, 247, 0.2)';
15748
15749        },
15750        setResponsive: function() {
15751            var that = this;
15752            if (!UNCODE.isMobile) {
15753                $(window).resize(function(){
15754                    if(that.animated === true) that.circle.stop();
15755                    that.draw(true);
15756                });
15757            }
15758        },
15759        draw: function(redraw) {
15760            var w = this.$el.addClass('vc_ready').width() * 2,
15761                border_w = this.arcWidth,
15762                radius;
15763            if(!w) w = this.$el.parents(':visible').first().width()-2;
15764            //w = Math.round(w/100*80);
15765            //if (w < 250) w = 250;
15766            radius = w/2;
15767            this.$wrapper.css({"width" : (w / 2) + "px"});
15768            this.$label.css({"width" : (w / 2), "height" : (w / 2), "line-height" : (w / 2)+"px"});
15769            this.$back.css({"width" : (w / 2), "height" : (w / 2)});
15770            this.$canvas.attr({"width" : w + "px", "height" : w + "px"});
15771            this.$el.addClass('vc_ready');
15772            this.circle = new ProgressCircle({
15773                canvas: this.$canvas.get(0),
15774                minRadius: radius,
15775                arcWidth: border_w
15776            });
15777            if(redraw === true && this.animated === true) {
15778                this._progress_v = this.value;
15779                this.circle.addEntry({
15780                    fillColor: this.color,
15781                    progressListener: $.proxy(this.setProgress, this)
15782                }).start();
15783            }
15784        },
15785        setProgress: function() {
15786            if (this._progress_v >= this.value) {
15787                this.circle.stop();
15788                this.animated = true;
15789                this.$label.html(this.label_value + this.options.units);
15790                return this._progress_v;
15791            }
15792            this._progress_v += 0.01;
15793            if (!isNaN(this.label_value)) {
15794                var label_value = this._progress_v/this.value*this.label_value;
15795                var val = Math.round(label_value) + this.options.units;
15796                this.$label.html(val);
15797            } else this.$label.html(this.label_value + this.options.units);
15798            return this._progress_v;
15799        },
15800        animate: function() {
15801            if(this.animated !== true) {
15802                this.circle.addEntry({
15803                    fillColor: this.color,
15804                    progressListener: $.proxy(this.setProgress, this)
15805                }).start(10);
15806            }
15807        },
15808        setWayPoint: function() {
15809            if (typeof $.fn.waypoint !== 'undefined' && !UNCODE.isMobile) {
15810                this.$el.waypoint($.proxy(this.animate, this), { offset: '85%' });
15811            } else {
15812                this.animate();
15813            }
15814        }
15815    };
15816    /**
15817     * jQuery plugin
15818     * @param option - object with settings
15819     * @return {*}
15820     */
15821    $.fn.vcChat = function(option, value) {
15822        return this.each(function () {
15823            var $this = $(this),
15824                data = $this.data('vc_chart'),
15825                options = typeof option === 'object' ? option : {
15826                    color: $this.data('pie-color'),
15827                    units: $this.data('pie-units')
15828                };
15829            if (typeof option == 'undefined') $this.data('vc_chart', (data = new VcChart(this, options)));
15830            if (typeof option == 'string') data[option](value);
15831        });
15832    };
15833    /**
15834     * Allows users to rewrite function inside theme.
15835     */
15836    if ( typeof window['vc_pieChart'] !== 'function' ) {
15837        window.vc_pieChart = function() {
15838            $('.vc_pie_chart:visible:not(.vc_ready)').vcChat();
15839        }
15840    }
15841    $(document).ready(function(){
15842        !window.vc_iframe && vc_pieChart();
15843    });
15844
15845})(window.jQuery);
15846
15847/* Progress bar
15848 ---------------------------------------------------------- */
15849function uncode_progress_bar() {    
15850	jQuery.each(jQuery('.vc_progress_bar'), function(index, val) {
15851		if (!UNCODE.isMobile) {
15852			new Waypoint({
15853				element: val,
15854				handler: function() {
15855					var element = jQuery(this.element);
15856					element.find('.vc_single_bar').each(function(index) {          
15857						var $this = jQuery(this),
15858							            bar = $this.find('.vc_bar'),
15859							            val = bar.data('percentage-value');          
15860						setTimeout(function() {            
15861							bar.css({
15862								"width": val + '%'
15863							});          
15864						}, index * 200);        
15865					});
15866				},
15867				offset: '80%'
15868			});
15869		} else {
15870			var element = jQuery(val);
15871			element.find('.vc_single_bar').each(function(index) {          
15872				var $this = jQuery(this),
15873					            bar = $this.find('.vc_bar'),
15874					            val = bar.data('percentage-value');          
15875				setTimeout(function() {            
15876					bar.css({
15877						"width": val + '%'
15878					});          
15879				}, index * 200);        
15880			});
15881		}
15882	});
15883};
15884uncode_progress_bar();
15885/*!
15886 * jquery.counterup.js 1.0
15887 *
15888 * Copyright 2013, Benjamin Intal http://gambit.ph @bfintal
15889 * Released under the GPL v2 License
15890 *
15891 * Date: Nov 26, 2013
15892 */
15893(function($) {
15894	"use strict";
15895	$.fn.counterUp = function(options) {
15896		// Defaults
15897		var settings = $.extend({
15898			'time': 400,
15899			'delay': 10
15900		}, options);
15901		return this.each(function() {
15902			// Store the object
15903			var $this = $(this);
15904			var $settings = settings;
15905			var counterUpper = function() {
15906				var nums = [];
15907				var divisions = $settings.time / $settings.delay;
15908				var numReal = $this.attr('data-val'),
15909				num = numReal;
15910				var isComma = /[0-9]+,[0-9]+/.test(num);
15911				num = num.replace(/,/g, '');
15912				var isInt = /^[0-9]+$/.test(num);
15913				var isFloat = /^[0-9]+\.[0-9]+$/.test(num);
15914				var decimalPlaces = isFloat ? (num.split('.')[1] || []).length : 0;
15915				// Generate list of incremental numbers to display
15916				for (var i = divisions; i >= 1; i--) {
15917					// Preserve as int if input was int
15918					var newNum = parseInt(num / divisions * i);
15919					// Preserve float if input was float
15920					if (isFloat) {
15921						newNum = parseFloat(num / divisions * i).toFixed(decimalPlaces);
15922					}
15923					// Preserve commas if input had commas
15924					if (isComma) {
15925						while (/(\d+)(\d{3})/.test(newNum.toString())) {
15926							newNum = newNum.toString().replace(/(\d+)(\d{3})/, '$1' + ',' + '$2');
15927						}
15928					}
15929					nums.unshift(newNum);
15930				}
15931				nums.push(numReal);
15932				$this.data('counterup-nums', nums);
15933				$this.text('0');
15934				// Updates the number until we're done
15935				var f = function() {
15936					if ($this.data('counterup-nums') != null) {
15937						$this.text($this.data('counterup-nums').shift());
15938						if ($this.data('counterup-nums').length) {
15939							setTimeout($this.data('counterup-func'), $settings.delay);
15940						} else {
15941							delete $this.data('counterup-nums');
15942							$this.data('counterup-nums', null);
15943							$this.data('counterup-func', null);
15944						}
15945					}
15946				};
15947				$this.data('counterup-func', f);
15948				// Start the count up
15949				setTimeout($this.data('counterup-func'), $settings.delay);
15950			};
15951			// Perform counts when the element gets into view
15952			new Waypoint({
15953				element: this,
15954				handler: function() {
15955					counterUpper();
15956					this.destroy();
15957				},
15958				offset: '100%'
15959			});
15960		});
15961	};
15962})(jQuery);
15963
15964/*!
15965 * The Final Countdown for jQuery v2.0.4 (http://hilios.github.io/jQuery.countdown/)
15966 * Copyright (c) 2014 Edson Hilios
15967 *
15968 * Permission is hereby granted, free of charge, to any person obtaining a copy of
15969 * this software and associated documentation files (the "Software"), to deal in
15970 * the Software without restriction, including without limitation the rights to
15971 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
15972 * the Software, and to permit persons to whom the Software is furnished to do so,
15973 * subject to the following conditions:
15974 *
15975 * The above copyright notice and this permission notice shall be included in all
15976 * copies or substantial portions of the Software.
15977 *
15978 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15979 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15980 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
15981 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
15982 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
15983 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
15984 */
15985(function(factory) {
15986    "use strict";
15987    if (typeof define === "function" && define.amd) {
15988        define([ "jquery" ], factory);
15989    } else {
15990        factory(jQuery);
15991    }
15992})(function($) {
15993    "use strict";
15994    var PRECISION = 100;
15995    var instances = [], matchers = [];
15996    matchers.push(/^[0-9]*$/.source);
15997    matchers.push(/([0-9]{1,2}\/){2}[0-9]{4}( [0-9]{1,2}(:[0-9]{2}){2})?/.source);
15998    matchers.push(/[0-9]{4}([\/\-][0-9]{1,2}){2}( [0-9]{1,2}(:[0-9]{2}){2})?/.source);
15999    matchers = new RegExp(matchers.join("|"));
16000    function parseDateString(dateString) {
16001        if (dateString instanceof Date) {
16002            return dateString;
16003        }
16004        if (String(dateString).match(matchers)) {
16005            if (String(dateString).match(/^[0-9]*$/)) {
16006                dateString = Number(dateString);
16007            }
16008            if (String(dateString).match(/\-/)) {
16009                dateString = String(dateString).replace(/\-/g, "/");
16010            }
16011            return new Date(dateString);
16012        } else {
16013            throw new Error("Couldn't cast `" + dateString + "` to a date object.");
16014        }
16015    }
16016    var DIRECTIVE_KEY_MAP = {
16017        Y: "years",
16018        m: "months",
16019        w: "weeks",
16020        d: "days",
16021        D: "totalDays",
16022        H: "hours",
16023        M: "minutes",
16024        S: "seconds"
16025    };
16026    function strftime(offsetObject) {
16027        return function(format) {
16028            var directives = format.match(/%(-|!)?[A-Z]{1}(:[^;]+;)?/gi);
16029            if (directives) {
16030                for (var i = 0, len = directives.length; i < len; ++i) {
16031                    var directive = directives[i].match(/%(-|!)?([a-zA-Z]{1})(:[^;]+;)?/), regexp = new RegExp(directive[0]), modifier = directive[1] || "", plural = directive[3] || "", value = null;
16032                    directive = directive[2];
16033                    if (DIRECTIVE_KEY_MAP.hasOwnProperty(directive)) {
16034                        value = DIRECTIVE_KEY_MAP[directive];
16035                        value = Number(offsetObject[value]);
16036                    }
16037                    if (value !== null) {
16038                        if (modifier === "!") {
16039                            value = pluralize(plural, value);
16040                        }
16041                        if (modifier === "") {
16042                            if (value < 10) {
16043                                value = "0" + value.toString();
16044                            }
16045                        }
16046                        format = format.replace(regexp, value.toString());
16047                    }
16048                }
16049            }
16050            format = format.replace(/%%/, "%");
16051            return format;
16052        };
16053    }
16054    function pluralize(format, count) {
16055        var plural = "s", singular = "";
16056        if (format) {
16057            format = format.replace(/(:|;|\s)/gi, "").split(/\,/);
16058            if (format.length === 1) {
16059                plural = format[0];
16060            } else {
16061                singular = format[0];
16062                plural = format[1];
16063            }
16064        }
16065        if (Math.abs(count) === 1) {
16066            return singular;
16067        } else {
16068            return plural;
16069        }
16070    }
16071    var Countdown = function(el, finalDate, callback) {
16072        this.el = el;
16073        this.$el = $(el);
16074        this.interval = null;
16075        this.offset = {};
16076        this.instanceNumber = instances.length;
16077        instances.push(this);
16078        this.$el.data("countdown-instance", this.instanceNumber);
16079        if (callback) {
16080            this.$el.on("update.countdown", callback);
16081            this.$el.on("stoped.countdown", callback);
16082            this.$el.on("finish.countdown", callback);
16083        }
16084        this.setFinalDate(finalDate);
16085        this.start();
16086    };
16087    $.extend(Countdown.prototype, {
16088        start: function() {
16089            if (this.interval !== null) {
16090                clearInterval(this.interval);
16091            }
16092            var self = this;
16093            this.update();
16094            this.interval = setInterval(function() {
16095                self.update.call(self);
16096            }, PRECISION);
16097        },
16098        stop: function() {
16099            clearInterval(this.interval);
16100            this.interval = null;
16101            this.dispatchEvent("stoped");
16102        },
16103        pause: function() {
16104            this.stop.call(this);
16105        },
16106        resume: function() {
16107            this.start.call(this);
16108        },
16109        remove: function() {
16110            this.stop();
16111            instances[this.instanceNumber] = null;
16112            delete this.$el.data().countdownInstance;
16113        },
16114        setFinalDate: function(value) {
16115            this.finalDate = parseDateString(value);
16116        },
16117        update: function() {
16118            if (this.$el.closest("html").length === 0) {
16119                this.remove();
16120                return;
16121            }
16122            this.totalSecsLeft = this.finalDate.getTime() - new Date().getTime();
16123            this.totalSecsLeft = Math.ceil(this.totalSecsLeft / 1e3);
16124            this.totalSecsLeft = this.totalSecsLeft < 0 ? 0 : this.totalSecsLeft;
16125            this.offset = {
16126                seconds: this.totalSecsLeft % 60,
16127                minutes: Math.floor(this.totalSecsLeft / 60) % 60,
16128                hours: Math.floor(this.totalSecsLeft / 60 / 60) % 24,
16129                days: Math.floor(this.totalSecsLeft / 60 / 60 / 24) % 7,
16130                totalDays: Math.floor(this.totalSecsLeft / 60 / 60 / 24),
16131                weeks: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 7),
16132                months: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 30),
16133                years: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 365)
16134            };
16135            if (this.totalSecsLeft === 0) {
16136                this.stop();
16137                this.dispatchEvent("finish");
16138            } else {
16139                this.dispatchEvent("update");
16140            }
16141        },
16142        dispatchEvent: function(eventName) {
16143            var event = $.Event(eventName + ".countdown");
16144            event.finalDate = this.finalDate;
16145            event.offset = $.extend({}, this.offset);
16146            event.strftime = strftime(this.offset);
16147            this.$el.trigger(event);
16148        }
16149    });
16150    $.fn.countdown = function() {
16151        var argumentsArray = Array.prototype.slice.call(arguments, 0);
16152        return this.each(function() {
16153            var instanceNumber = $(this).data("countdown-instance");
16154            if (instanceNumber !== undefined) {
16155                var instance = instances[instanceNumber], method = argumentsArray[0];
16156                if (Countdown.prototype.hasOwnProperty(method)) {
16157                    instance[method].apply(instance, argumentsArray.slice(1));
16158                } else if (String(method).match(/^[$A-Z_][0-9A-Z_$]*$/i) === null) {
16159                    instance.setFinalDate.call(instance, method);
16160                    instance.start();
16161                } else {
16162                    $.error("Method %s does not exist on jQuery.countdown".replace(/\%s/gi, method));
16163                }
16164            } else {
16165                new Countdown(this, argumentsArray[0], argumentsArray[1]);
16166            }
16167        });
16168    };
16169});
16170
16171/*! waitForImages jQuery Plugin - v2.0.2 - 2015-05-05
16172* https://github.com/alexanderdickson/waitForImages
16173* Copyright (c) 2015 Alex Dickson; Licensed MIT */
16174;(function (factory) {
16175    if (typeof define === 'function' && define.amd) {
16176        // AMD. Register as an anonymous module.
16177        define(['jquery'], factory);
16178    } else if (typeof exports === 'object') {
16179        // CommonJS / nodejs module
16180        module.exports = factory(require('jquery'));
16181    } else {
16182        // Browser globals
16183        factory(jQuery);
16184    }
16185}(function ($) {
16186    // Namespace all events.
16187    var eventNamespace = 'waitForImages';
16188
16189    // CSS properties which contain references to images.
16190    $.waitForImages = {
16191        hasImageProperties: [
16192            'backgroundImage',
16193            'listStyleImage',
16194            'borderImage',
16195            'borderCornerImage',
16196            'cursor'
16197        ],
16198        hasImageAttributes: ['srcset']
16199    };
16200
16201    // Custom selector to find `img` elements that have a valid `src`
16202    // attribute and have not already loaded.
16203    $.expr[':'].uncached = function (obj) {
16204        // Ensure we are dealing with an `img` element with a valid
16205        // `src` attribute.
16206        if (!$(obj).is('img[src][src!=""]')) {
16207            return false;
16208        }
16209
16210        return !obj.complete;
16211    };
16212
16213    $.fn.waitForImages = function () {
16214
16215        var allImgsLength = 0;
16216        var allImgsLoaded = 0;
16217        var deferred = $.Deferred();
16218
16219        var finishedCallback;
16220        var eachCallback;
16221        var waitForAll;
16222
16223        // Handle options object (if passed).
16224        if ($.isPlainObject(arguments[0])) {
16225
16226            waitForAll = arguments[0].waitForAll;
16227            eachCallback = arguments[0].each;
16228            finishedCallback = arguments[0].finished;
16229
16230        } else {
16231
16232            // Handle if using deferred object and only one param was passed in.
16233            if (arguments.length === 1 && $.type(arguments[0]) === 'boolean') {
16234                waitForAll = arguments[0];
16235            } else {
16236                finishedCallback = arguments[0];
16237                eachCallback = arguments[1];
16238                waitForAll = arguments[2];
16239            }
16240
16241        }
16242
16243        // Handle missing callbacks.
16244        finishedCallback = finishedCallback || $.noop;
16245        eachCallback = eachCallback || $.noop;
16246
16247        // Convert waitForAll to Boolean
16248        waitForAll = !! waitForAll;
16249
16250        // Ensure callbacks are functions.
16251        if (!$.isFunction(finishedCallback) || !$.isFunction(eachCallback)) {
16252            throw new TypeError('An invalid callback was supplied.');
16253        }
16254
16255        this.each(function () {
16256            // Build a list of all imgs, dependent on what images will
16257            // be considered.
16258            var obj = $(this);
16259            var allImgs = [];
16260            // CSS properties which may contain an image.
16261            var hasImgProperties = $.waitForImages.hasImageProperties || [];
16262            // Element attributes which may contain an image.
16263            var hasImageAttributes = $.waitForImages.hasImageAttributes || [];
16264            // To match `url()` references.
16265            // Spec: http://www.w3.org/TR/CSS2/syndata.html#value-def-uri
16266            var matchUrl = /url\(\s*(['"]?)(.*?)\1\s*\)/g;
16267
16268            if (waitForAll) {
16269
16270                // Get all elements (including the original), as any one of
16271                // them could have a background image.
16272                obj.find('*').addBack().each(function () {
16273                    var element = $(this);
16274
16275                    // If an `img` element, add it. But keep iterating in
16276                    // case it has a background image too.
16277                    if (element.is('img:uncached')) {
16278                        allImgs.push({
16279                            src: element.attr('src'),
16280                            element: element[0]
16281                        });
16282                    }
16283
16284                    $.each(hasImgProperties, function (i, property) {
16285                        var propertyValue = element.css(property);
16286                        var match;
16287
16288                        // If it doesn't contain this property, skip.
16289                        if (!propertyValue) {
16290                            return true;
16291                        }
16292
16293                        // Get all url() of this element.
16294                        while (match = matchUrl.exec(propertyValue)) {
16295                            allImgs.push({
16296                                src: match[2],
16297                                element: element[0]
16298                            });
16299                        }
16300                    });
16301
16302                    $.each(hasImageAttributes, function (i, attribute) {
16303                        var attributeValue = element.attr(attribute);
16304                        var attributeValues;
16305
16306                        // If it doesn't contain this property, skip.
16307                        if (!attributeValue) {
16308                            return true;
16309                        }
16310
16311                        // Check for multiple comma separated images
16312                        attributeValues = attributeValue.split(',');
16313
16314                        $.each(attributeValues, function(i, value) {
16315                            // Trim value and get string before first
16316                            // whitespace (for use with srcset).
16317                            value = $.trim(value).split(' ')[0];
16318                            allImgs.push({
16319                                src: value,
16320                                element: element[0]
16321                            });
16322                        });
16323                    });
16324                });
16325            } else {
16326                // For images only, the task is simpler.
16327                obj.find('img:uncached')
16328                    .each(function () {
16329                    allImgs.push({
16330                        src: this.src,
16331                        element: this
16332                    });
16333                });
16334            }
16335
16336            allImgsLength = allImgs.length;
16337            allImgsLoaded = 0;
16338
16339            // If no images found, don't bother.
16340            if (allImgsLength === 0) {
16341                finishedCallback.call(obj[0]);
16342                deferred.resolveWith(obj[0]);
16343            }
16344
16345            $.each(allImgs, function (i, img) {
16346
16347                var image = new Image();
16348                var events =
16349                  'load.' + eventNamespace + ' error.' + eventNamespace;
16350
16351                // Handle the image loading and error with the same callback.
16352                $(image).one(events, function me (event) {
16353                    // If an error occurred with loading the image, set the
16354                    // third argument accordingly.
16355                    var eachArguments = [
16356                        allImgsLoaded,
16357                        allImgsLength,
16358                        event.type == 'load'
16359                    ];
16360                    allImgsLoaded++;
16361
16362                    eachCallback.apply(img.element, eachArguments);
16363                    deferred.notifyWith(img.element, eachArguments);
16364
16365                    // Unbind the event listeners. I use this in addition to
16366                    // `one` as one of those events won't be called (either
16367                    // 'load' or 'error' will be called).
16368                    $(this).off(events, me);
16369
16370                    if (allImgsLoaded == allImgsLength) {
16371                        finishedCallback.call(obj[0]);
16372                        deferred.resolveWith(obj[0]);
16373                        return false;
16374                    }
16375
16376                });
16377
16378                image.src = img.src;
16379            });
16380        });
16381
16382        return deferred.promise();
16383
16384    };
16385}));
16386
16387/**
16388 * @preserve  textfill
16389 * @name      jquery.textfill.js
16390 * @author    Russ Painter
16391 * @author    Yu-Jie Lin
16392 * @author    Alexandre Dantas
16393 * @version   0.6.0
16394 * @date      2014-08-19
16395 * @copyright (c) 2014 Alexandre Dantas
16396 * @copyright (c) 2012-2013 Yu-Jie Lin
16397 * @copyright (c) 2009 Russ Painter
16398 * @license   MIT License
16399 * @homepage  https://github.com/jquery-textfill/jquery-textfill
16400 * @example   http://jquery-textfill.github.io/jquery-textfill/index.html
16401 */
16402; (function($) {
16403
16404	/**
16405	 * Resizes an inner element's font so that the
16406	 * inner element completely fills the outer element.
16407	 *
16408	 * @param {Object} options User options that take
16409	 *                         higher precedence when
16410	 *                         merging with the default ones.
16411	 *
16412	 * @return All outer elements processed
16413	 */
16414	$.fn.textfill = function(options) {
16415
16416		// ______  _______ _______ _______ _     _        _______ _______
16417		// |     \ |______ |______ |_____| |     | |         |    |______
16418		// |_____/ |______ |       |     | |_____| |_____    |    ______|
16419        //
16420		// Merging user options with the default values
16421
16422		var defaults = {
16423			debug            : false,
16424			maxFontPixels    : 40,
16425			minFontPixels    : 4,
16426			innerTag         : 'span',
16427			widthOnly        : false,
16428			success          : null, // callback when a resizing is done
16429			callback         : null, // callback when a resizing is done (deprecated, use success)
16430			fail             : null, // callback when a resizing is failed
16431			complete         : null, // callback when all is done
16432			explicitWidth    : null,
16433			explicitHeight   : null,
16434			changeLineHeight : false
16435		};
16436
16437		var Opts = $.extend(defaults, options);
16438
16439		// _______ _     _ __   _ _______ _______ _____  _____  __   _ _______
16440		// |______ |     | | \  | |          |      |   |     | | \  | |______
16441		// |       |_____| |  \_| |_____     |    __|__ |_____| |  \_| ______|
16442		//
16443		// Predefining the awesomeness
16444
16445		// Output arguments to the Debug console
16446		// if "Debug Mode" is enabled
16447		function _debug() {
16448			if (!Opts.debug
16449				||  typeof console       == 'undefined'
16450				||  typeof console.debug == 'undefined') {
16451				return;
16452			}
16453			console.debug.apply(console, arguments);
16454		}
16455
16456		// Output arguments to the Warning console
16457		function _warn() {
16458			if (typeof console      == 'undefined' ||
16459				typeof console.warn == 'undefined') {
16460				return;
16461			}
16462			console.warn.apply(console, arguments);
16463		}
16464
16465		// Outputs all information on the current sizing
16466		// of the font.
16467		function _debug_sizing(prefix, ourText, maxHeight, maxWidth, minFontPixels, maxFontPixels) {
16468
16469			function _m(v1, v2) {
16470
16471				var marker = ' / ';
16472
16473				if (v1 > v2)
16474					marker = ' > ';
16475
16476				else if (v1 == v2)
16477					marker = ' = ';
16478
16479				return marker;
16480			}
16481
16482			_debug(
16483				'[TextFill] '  + prefix + ' { ' +
16484				'font-size: ' + ourText.css('font-size') + ',' +
16485				'Height: '    + ourText.height() + 'px ' + _m(ourText.height(), maxHeight) + maxHeight + 'px,' +
16486				'Width: '     + ourText.width()  + _m(ourText.width() , maxWidth)  + maxWidth + ',' +
16487				'minFontPixels: ' + minFontPixels + 'px, ' +
16488				'maxFontPixels: ' + maxFontPixels + 'px }'
16489			);
16490		}
16491
16492		/**
16493		 * Calculates which size the font can get resized,
16494		 * according to constrains.
16495		 *
16496		 * @param {String} prefix Gets shown on the console before
16497		 *                        all the arguments, if debug mode is on.
16498		 * @param {Object} ourText The DOM element to resize,
16499		 *                         that contains the text.
16500		 * @param {function} func Function called on `ourText` that's
16501		 *                        used to compare with `max`.
16502		 * @param {number} max Maximum value, that gets compared with
16503		 *                     `func` called on `ourText`.
16504		 * @param {number} minFontPixels Minimum value the font can
16505		 *                               get resized to (in pixels).
16506		 * @param {number} maxFontPixels Maximum value the font can
16507		 *                               get resized to (in pixels).
16508		 *
16509		 * @return Size (in pixels) that the font can be resized.
16510		 */
16511		function _sizing(prefix, ourText, func, max, maxHeight, maxWidth, minFontPixels, maxFontPixels) {
16512
16513			_debug_sizing(
16514				prefix, ourText,
16515				maxHeight, maxWidth,
16516				minFontPixels, maxFontPixels
16517			);
16518
16519			// The kernel of the whole plugin, take most attention
16520			// on this part.
16521			//
16522			// This is a loop that keeps increasing the `font-size`
16523			// until it fits the parent element.
16524			//
16525			// - Start from the minimal allowed value (`minFontPixels`)
16526			// - Guesses an average font size (in pixels) for the font,
16527			// - Resizes the text and sees if its size is within the
16528			//   boundaries (`minFontPixels` and `maxFontPixels`).
16529			//   - If so, keep guessing until we break.
16530			//   - If not, return the last calculated size.
16531			//
16532			// I understand this is not optimized and we should
16533			// consider implementing something akin to
16534			// Daniel Hoffmann's answer here:
16535			//
16536			//     http://stackoverflow.com/a/17433451/1094964
16537			//
16538
16539			while (minFontPixels < (maxFontPixels - 1)) {
16540
16541				var fontSize = Math.floor((minFontPixels + maxFontPixels) / 2);
16542				ourText.css('font-size', fontSize);
16543
16544				if (func.call(ourText) <= max) {
16545					minFontPixels = fontSize;
16546
16547					if (func.call(ourText) == max)
16548						break;
16549				}
16550				else
16551					maxFontPixels = fontSize;
16552
16553				_debug_sizing(
16554					prefix, ourText,
16555					maxHeight, maxWidth,
16556					minFontPixels, maxFontPixels
16557				);
16558			}
16559
16560			ourText.css('font-size', maxFontPixels);
16561
16562			if (func.call(ourText) <= max) {
16563				minFontPixels = maxFontPixels;
16564
16565				_debug_sizing(
16566					prefix + '* ', ourText,
16567					maxHeight, maxWidth,
16568					minFontPixels, maxFontPixels
16569				);
16570			}
16571			return minFontPixels;
16572		}
16573
16574		// _______ _______ _______  ______ _______
16575		// |______    |    |_____| |_____/    |
16576		// ______|    |    |     | |    \_    |
16577        //
16578		// Let's get it started (yeah)!
16579
16580		_debug('[TextFill] Start Debug');
16581
16582		this.each(function() {
16583
16584			// Contains the child element we will resize.
16585			// $(this) means the parent container
16586			var ourText = $(Opts.innerTag + ':visible:first', this);
16587
16588			// Will resize to this dimensions.
16589			// Use explicit dimensions when specified
16590			var maxHeight = Opts.explicitHeight || $(this).height();
16591			var maxWidth  = Opts.explicitWidth  || $(this).width();
16592
16593			var oldFontSize = ourText.css('font-size');
16594
16595			var lineHeight  = parseFloat(ourText.css('line-height')) / parseFloat(oldFontSize);
16596
16597			_debug('[TextFill] Inner text: ' + ourText.text());
16598			_debug('[TextFill] All options: ', Opts);
16599			_debug('[TextFill] Maximum sizes: { ' +
16600				   'Height: ' + maxHeight + 'px, ' +
16601				   'Width: '  + maxWidth  + 'px' + ' }'
16602				  );
16603
16604			var minFontPixels = Opts.minFontPixels;
16605
16606			// Remember, if this `maxFontPixels` is negative,
16607			// the text will resize to as long as the container
16608			// can accomodate
16609			var maxFontPixels = (Opts.maxFontPixels <= 0 ?
16610								 maxHeight :
16611								 Opts.maxFontPixels);
16612
16613
16614			// Let's start it all!
16615
16616			// 1. Calculate which `font-size` would
16617			//    be best for the Height
16618			var fontSizeHeight = undefined;
16619
16620			if (! Opts.widthOnly)
16621				fontSizeHeight = _sizing(
16622					'Height', ourText,
16623					$.fn.height, maxHeight,
16624					maxHeight, maxWidth,
16625					minFontPixels, maxFontPixels
16626				);
16627
16628			// 2. Calculate which `font-size` would
16629			//    be best for the Width
16630			var fontSizeWidth = undefined;
16631
16632			fontSizeWidth = _sizing(
16633				'Width', ourText,
16634				$.fn.width, maxWidth,
16635				maxHeight, maxWidth,
16636				minFontPixels, maxFontPixels
16637			);
16638
16639			// 3. Actually resize the text!
16640
16641			if (Opts.widthOnly) {
16642				ourText.css({
16643					'font-size'  : fontSizeWidth,
16644					'white-space': 'nowrap'
16645				});
16646
16647				if (Opts.changeLineHeight)
16648					ourText.parent().css(
16649						'line-height',
16650						(lineHeight * fontSizeWidth + 'px')
16651					);
16652			}
16653			else {
16654				var fontSizeFinal = Math.min(fontSizeHeight, fontSizeWidth);
16655
16656				ourText.css('font-size', fontSizeFinal);
16657
16658				if (Opts.changeLineHeight)
16659					ourText.parent().css(
16660						'line-height',
16661						(lineHeight * fontSizeFinal) + 'px'
16662					);
16663			}
16664
16665			_debug(
16666				'[TextFill] Finished { ' +
16667				'Old font-size: ' + oldFontSize              + ', ' +
16668				'New font-size: ' + ourText.css('font-size') + ' }'
16669			);
16670
16671			// Oops, something wrong happened!
16672			// We weren't supposed to exceed the original size
16673			if ((ourText.width()  > maxWidth) ||
16674				(ourText.height() > maxHeight && !Opts.widthOnly)) {
16675
16676				ourText.css('font-size', oldFontSize);
16677
16678				// Failure callback
16679				if (Opts.fail)
16680					Opts.fail(this);
16681
16682				_debug(
16683					'[TextFill] Failure { ' +
16684					'Current Width: '  + ourText.width()  + ', ' +
16685					'Maximum Width: '  + maxWidth         + ', ' +
16686					'Current Height: ' + ourText.height() + ', ' +
16687					'Maximum Height: ' + maxHeight        + ' }'
16688				);
16689			}
16690			else if (Opts.success) {
16691				Opts.success(this);
16692			}
16693			else if (Opts.callback) {
16694				_warn('callback is deprecated, use success, instead');
16695
16696				// Success callback
16697				Opts.callback(this);
16698			}
16699		});
16700
16701		// Complete callback
16702		if (Opts.complete)
16703			Opts.complete(this);
16704
16705		_debug('[TextFill] End Debug');
16706		return this;
16707	};
16708
16709})(window.jQuery);
16710
16711!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;
16712function getStyles(config){
16713  // 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;
16713left: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+"}"
16714};
16715  var ShareUtils;
16716
16717if ((!("classList" in document.documentElement)) && Object.defineProperty && typeof HTMLElement !== "undefined") {
16718  Object.defineProperty(HTMLElement.prototype, "classList", {
16719    get: function() {
16720      var ret, self, update;
16721      update = function(fn) {
16722        return function(value) {
16723          var classes, index;
16724          classes = self.className.split(/\s+/);
16725          index = classes.indexOf(value);
16726          fn(classes, index, value);
16727          self.className = classes.join(" ");
16728        };
16729      };
16730      self = this;
16731      ret = {
16732        add: update(function(classes, index, value) {
16733          ~index || classes.push(value);
16734        }),
16735        remove: update(function(classes, index) {
16736          ~index && classes.splice(index, 1);
16737        }),
16738        toggle: update(function(classes, index, value) {
16739          if (~index) {
16740            classes.splice(index, 1);
16741          } else {
16742            classes.push(value);
16743          }
16744        }),
16745        contains: function(value) {
16746          return !!~self.className.split(/\s+/).indexOf(value);
16747        },
16748        item: function(i) {
16749          return self.className.split(/\s+/)[i] || null;
16750        }
16751      };
16752      Object.defineProperty(ret, "length", {
16753        get: function() {
16754          return self.className.split(/\s+/).length;
16755        }
16756      });
16757      return ret;
16758    }
16759  });
16760}
16761
16762String.prototype.to_rfc3986 = function() {
16763  var tmp;
16764  tmp = encodeURIComponent(this);
16765  return tmp.replace(/[!'()*]/g, function(c) {
16766    return "%" + c.charCodeAt(0).toString(16);
16767  });
16768};
16769
16770ShareUtils = (function() {
16771  function ShareUtils() {}
16772
16773  ShareUtils.prototype.extend = function(to, from, overwrite) {
16774    var hasProp, prop;
16775    for (prop in from) {
16776      hasProp = to[prop] !== undefined;
16777      if (hasProp && typeof from[prop] === "object") {
16778        this.extend(to[prop], from[prop], overwrite);
16779      } else {
16780        if (overwrite || !hasProp) {
16781          to[prop] = from[prop];
16782        }
16783      }
16784    }
16785  };
16786
16787  ShareUtils.prototype.hide = function(el) {
16788    return el.style.display = "none";
16789  };
16790
16791  ShareUtils.prototype.show = function(el) {
16792    return el.style.display = "block";
16793  };
16794
16795  ShareUtils.prototype.has_class = function(el, class_name) {
16796    return el.classList.contains(class_name);
16797  };
16798
16799  ShareUtils.prototype.add_class = function(el, class_name) {
16800    return el.classList.add(class_name);
16801  };
16802
16803  ShareUtils.prototype.remove_class = function(el, class_name) {
16804    return el.classList.remove(class_name);
16805  };
16806
16807  ShareUtils.prototype.is_encoded = function(str) {
16808    str = str.to_rfc3986();
16809    return decodeURIComponent(str) !== str;
16810  };
16811
16812  ShareUtils.prototype.encode = function(str) {
16813    if (typeof str === "undefined" || this.is_encoded(str)) {
16814      return str;
16815    } else {
16816      return str.to_rfc3986();
16817    }
16818  };
16819
16820  ShareUtils.prototype.popup = function(url, params) {
16821    var k, popup, qs, v;
16822    if (params == null) {
16823      params = {};
16824    }
16825    popup = {
16826      width: 500,
16827      height: 350
16828    };
16829    popup.top = (screen.height / 2) - (popup.height / 2);
16830    popup.left = (screen.width / 2) - (popup.width / 2);
16831    qs = ((function() {
16832      var _results;
16833      _results = [];
16834      for (k in params) {
16835        v = params[k];
16836        _results.push("" + k + "=" + (this.encode(v)));
16837      }
16838      return _results;
16839    }).call(this)).join('&');
16840    if (qs) {
16841      qs = "?" + qs;
16842    }
16843    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=" 
16843+ popup.width + ",height=" + popup.height);
16844  };
16845
16846  return ShareUtils;
16847
16848})();
16849var Share,
16850  __hasProp = {}.hasOwnProperty,
16851  __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; };
16852
16853Share = (function(_super) {
16854  __extends(Share, _super);
16855
16856  function Share(element, options) {
16857    this.element = element;
16858    this.el = {
16859      head: document.getElementsByTagName('head')[0],
16860      body: document.getElementsByTagName('body')[0]
16861    };
16862    this.config = {
16863      enabled_networks: 0,
16864      protocol: ['http', 'https'].indexOf(window.location.href.split(':')[0]) === -1 ? 'https://' : '//',
16865      url: window.location.href.replace("&", "%26"),
16866      caption: null,
16867      title: this.default_title(),
16868      image: this.default_image(),
16869      description: this.default_description(),
16870      ui: {
16871        flyout: 'top center',
16872        button_text: 'Share',
16873        button_font: true,
16874        icon_font: true
16875      },
16876      networks: {
16877        google_plus: {
16878          enabled: true,
16879          url: null
16880        },
16881        twitter: {
16882          enabled: true,
16883          url: null,
16884          title: null,
16885          description: null
16886        },
16887        facebook: {
16888          enabled: true,
16889          load_sdk: true,
16890          url: null,
16891          app_id: null,
16892          title: null,
16893          caption: null,
16894          description: null,
16895          image: null
16896        },
16897        pinterest: {
16898          enabled: true,
16899          url: null,
16900          image: null,
16901          description: null
16902        },
16903        reddit: {
16904          enabled: true,
16905          url: null,
16906          title: null
16907        },
16908        linkedin: {
16909          enabled: true,
16910          url: null,
16911          title: null,
16912          description: null
16913        },
16914        xing: {
16915          enabled: true,
16916          url: null,
16917          title: null,
16918          image: null,
16919          description: null
16920        },
16921        whatsapp: {
16922          enabled: true,
16923          description: null,
16924          url: null
16925        },
16926        email: {
16927          enabled: true,
16928          title: null,
16929          description: null,
16930          url: null
16931        }
16932      }
16933    };
16934    this.setup(element, options);
16935    return this;
16936  }
16937
16938  Share.prototype.setup = function(element, opts) {
16939    var index, instance, instances, _i, _len;
16940    instances = document.querySelectorAll(element);
16941    this.extend(this.config, opts, true);
16942    this.set_global_configuration();
16943    this.normalize_network_configuration();
16944    if (this.config.ui.icon_font) {
16945      this.inject_icons();
16946    }
16947    if (this.config.ui.button_font) {
16948      this.inject_fonts();
16949    }
16950    if (this.config.networks.facebook.enabled && this.config.networks.facebook.load_sdk) {
16951      this.inject_facebook_sdk();
16952    }
16953    for (index = _i = 0, _len = instances.length; _i < _len; index = ++_i) {
16954      instance = instances[index];
16955      this.setup_instance(element, index);
16956    }
16957  };
16958
16959  Share.prototype.setup_instance = function(element, index) {
16960    var button, instance, label, network, networks, _i, _len, _results,
16961      _this = this;
16962    instance = document.querySelectorAll(element)[index];
16963    this.hide(instance);
16964    this.add_class(instance, "sharer-" + index);
16965    instance = document.querySelectorAll(element)[index];
16966    this.inject_css(instance);
16967    this.inject_html(instance);
16968    this.show(instance);
16969    label = instance.getElementsByTagName("label")[0];
16970    button = instance.getElementsByClassName("social")[0];
16971    networks = instance.getElementsByTagName('li');
16972    this.add_class(button, "networks-" + this.config.enabled_networks);
16973    label.addEventListener("click", function() {
16974      return _this.event_toggle(button);
16975    });
16976    _this = this;
16977    _results = [];
16978    for (index = _i = 0, _len = networks.length; _i < _len; index = ++_i) {
16979      network = networks[index];
16980      _results.push(network.addEventListener("click", function() {
16981        _this.event_network(instance, this);
16982        return _this.event_close(button);
16983      }));
16984    }
16985    return _results;
16986  };
16987
16988  Share.prototype.event_toggle = function(button) {
16989    if (this.has_class(button, "active")) {
16990      return this.event_close(button);
16991    } else {
16992      return this.event_open(button);
16993    }
16994  };
16995
16996  Share.prototype.event_open = function(button) {
16997    if (this.has_class(button, "load")) {
16998      this.remove_class(button, "load");
16999    }
17000    return this.add_class(button, "active");
17001  };
17002
17003  Share.prototype.event_close = function(button) {
17004    return this.remove_class(button, "active");
17005  };
17006
17007  Share.prototype.event_network = function(instance, network) {
17008    var name;
17009    name = network.getAttribute("data-network");
17010    this.hook("before", name, instance);
17011    this["network_" + name]();
17012    return this.hook("after", name, instance);
17013  };
17014
17015  Share.prototype.open = function() {
17016    return this["public"]("open");
17017  };
17018
17019  Share.prototype.close = function() {
17020    return this["public"]("close");
17021  };
17022
17023  Share.prototype.toggle = function() {
17024    return this["public"]("toggle");
17025  };
17026
17027  Share.prototype["public"] = function(action) {
17028    var button, index, instance, _i, _len, _ref, _results;
17029    _ref = document.querySelectorAll(this.element);
17030    _results = [];
17031    for (index = _i = 0, _len = _ref.length; _i < _len; index = ++_i) {
17032      instance = _ref[index];
17033      button = instance.getElementsByClassName("social")[0];
17034      _results.push(this["event_" + action](button));
17035    }
17036    return _results;
17037  };
17038
17039  Share.prototype.network_facebook = function() {
17040    if (this.config.networks.facebook.load_sdk) {
17041      if (!window.FB) {
17042        return console.error("The Facebook JS SDK hasn't loaded yet.");
17043      }
17044      return FB.ui({
17045        method: 'feed',
17046        name: this.config.networks.facebook.title,
17047        link: this.config.networks.facebook.url,
17048        picture: this.config.networks.facebook.image,
17049        caption: this.config.networks.facebook.caption,
17050        description: this.config.networks.facebook.description
17051      });
17052    } else {
17053      return this.popup('https://www.facebook.com/sharer/sharer.php', {
17054        u: this.config.networks.facebook.url
17055      });
17056    }
17057  };
17058
17059  Share.prototype.network_twitter = function() {
17060    return this.popup('https://twitter.com/intent/tweet', {
17061      text: this.config.networks.twitter.title,
17062      url: this.config.networks.twitter.url
17063    });
17064  };
17065
17066  Share.prototype.network_google_plus = function() {
17067    return this.popup('https://plus.google.com/share', {
17068      url: this.config.networks.google_plus.url
17069    });
17070  };
17071
17072  Share.prototype.network_pinterest = function() {
17073    return this.popup('https://www.pinterest.com/pin/create/button', {
17074      url: this.config.networks.pinterest.url,
17075      media: this.config.networks.pinterest.image,
17076      description: this.config.networks.pinterest.description
17077    });
17078  };
17079
17080  Share.prototype.network_linkedin = function() {
17081    return this.popup('https://www.linkedin.com/shareArticle', {
17082      url: this.config.networks.linkedin.url,
17083      title: this.config.networks.linkedin.title,
17084      summary: this.config.networks.linkedin.description
17085    });
17086  }
17087
17088  Share.prototype.network_xing = function() {
17089    return this.popup('https://www.xing.com/spi/shares/new', {
17090      url: this.config.networks.xing.url,
17091      image: this.config.networks.xing.image,
17092      title: this.config.networks.xing.title,
17093      summary: this.config.networks.xing.description
17094    });
17095  }
17096
17097  Share.prototype.network_email = function() {
17098    return this.popup('mailto:', {
17099      subject: this.config.networks.email.title,
17100      body: this.config.networks.email.url + '%0A%0A' + this.config.networks.email.description,
17101    });
17102  };
17103
17104  Share.prototype.inject_icons = function() {
17105  //  return this.inject_stylesheet("https://www.sharebutton.co/fonts/v2/entypo.min.css");
17106  };
17107
17108  Share.prototype.inject_fonts = function() {
17109   // return this.inject_stylesheet("http://fonts.googleapis.com/css?family=Lato:900&text=" + this.config.ui.button_text);
17110  };
17111
17112  Share.prototype.inject_stylesheet = function(url) {
17113    var link;
17114    if (!this.el.head.querySelector("link[href=\"" + url + "\"]")) {
17115      link = document.createElement("link");
17116      link.setAttribute("rel", "stylesheet");
17117      link.setAttribute("href", url);
17118      return this.el.head.appendChild(link);
17119    }
17120  };
17121
17122  Share.prototype.inject_css = function(instance) {
17123    var css, meta, selector, style;
17124    selector = "." + (instance.getAttribute('class').split(" ").join("."));
17125    if (!this.el.head.querySelector("meta[name='sharer" + selector + "']")) {
17126      this.config.selector = selector;
17127      css = getStyles(this.config);
17128      style = document.createElement("style");
17129      style.type = "text/css";
17130      if (style.styleSheet) {
17131        style.styleSheet.cssText = css;
17132      } else {
17133        style.appendChild(document.createTextNode(css));
17134      }
17135      this.el.head.appendChild(style);
17136      delete this.config.selector;
17137      meta = document.createElement("meta");
17138      meta.setAttribute("name", "sharer" + selector);
17139      return this.el.head.appendChild(meta);
17140    }
17141  };
17142
17143  Share.prototype.inject_html = function(instance) {
17144    //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>";
17145    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>";
17146  };
17147
17148  Share.prototype.inject_facebook_sdk = function() {
17149    var fb_root, script;
17150    if (!window.FB && this.config.networks.facebook.app_id && !this.el.body.querySelector('#fb-root')) {
17151      script = document.createElement("script");
17152      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')";
17153      fb_root = document.createElement("div");
17154      fb_root.id = "fb-root";
17155      this.el.body.appendChild(fb_root);
17156      return this.el.body.appendChild(script);
17157    }
17158  };
17159
17160  Share.prototype.hook = function(type, network, instance) {
17161    var fn, opts;
17162    fn = this.config.networks[network][type];
17163    if (typeof fn === "function") {
17164      opts = fn.call(this.config.networks[network], instance);
17165      if (opts !== void 0) {
17166        opts = this.normalize_filter_config_updates(opts);
17167        this.extend(this.config.networks[network], opts, true);
17168        this.normalize_network_configuration();
17169      }
17170    }
17171  };
17172
17173  Share.prototype.default_title = function() {
17174    var content;
17175    if (content = document.querySelector('meta[property="og:title"]') || document.querySelector('meta[name="twitter:title"]')) {
17176      return encodeURIComponent(content.getAttribute('content'));
17177    } else if (content = document.querySelector('title')) {
17178      return encodeURIComponent(content.innerText);
17179    }
17180  };
17181
17182  Share.prototype.default_image = function() {
17183    var content;
17184    if (content = document.querySelector('meta[property="og:image"]') || document.querySelector('meta[name="twitter:image"]')) {
17185      return content.getAttribute('content');
17186    }
17187  };
17188
17189  Share.prototype.default_description = function() {
17190    var content;
17191    if (content = document.querySelector('meta[property="og:description"]') || document.querySelector('meta[name="twitter:description"]') || document.querySelector('meta[name="description"]')) {
17192      return encodeURIComponent(content.getAttribute('content'));
17193    } else {
17194      return '';
17195    }
17196  };
17197
17198  Share.prototype.set_global_configuration = function() {
17199    var display, network, option, options, _ref, _results;
17200    _ref = this.config.networks;
17201    _results = [];
17202    for (network in _ref) {
17203      options = _ref[network];
17204      for (option in options) {
17205        if (this.config.networks[network][option] == null) {
17206          this.config.networks[network][option] = this.config[option];
17207        }
17208      }
17209      if (this.config.networks[network].enabled) {
17210        display = 'block';
17211        this.config.enabled_networks += 1;
17212      } else {
17213        display = 'none';
17214      }
17215      _results.push(this.config.networks[network].display = display);
17216    }
17217    return _results;
17218  };
17219
17220  Share.prototype.normalize_network_configuration = function() {
17221    if (!this.config.networks.facebook.app_id) {
17222      this.config.networks.facebook.load_sdk = false;
17223    }
17224    if (!this.is_encoded(this.config.networks.twitter.description)) {
17225      this.config.networks.twitter.description = encodeURIComponent(this.config.networks.twitter.description);
17226    }
17227    if (typeof this.config.networks.facebook.app_id === 'number') {
17228      return this.config.networks.facebook.app_id = this.config.networks.facebook.app_id.toString();
17229    }
17230  };
17231
17232  Share.prototype.normalize_filter_config_updates = function(opts) {
17233    if (this.config.networks.facebook.app_id !== opts.app_id) {
17234      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.");
17235      delete opts.app_id;
17236    }
17237    if (this.config.networks.facebook.load_sdk !== opts.load_sdk) {
17238      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.");
17239      delete opts.app_id;
17240    }
17241    return opts;
17242  };
17243
17244  return Share;
17245
17246})(ShareUtils);
17247 return Share;
17248});
17249
17250// Generated by CoffeeScript 1.9.2
17251
17252// @license Sticky-kit v1.1.2 | WTFPL | Leaf Corcoran 2015 | http://leafo.net
17253
17254(function() {
17255  var $, win;
17256
17257  $ = this.jQuery || window.jQuery;
17258
17259  win = $(window);
17260
17261  $.fn.stick_in_parent = function(opts) {
17262    var doc, elm, enable_bottoming, fn, i, inner_scrolling, len, manual_spacer, offset_top, outer_width, parent_selector, recalc_every, sticky_class;
17263    if (opts == null) {
17264      opts = {};
17265    }
17266    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;
17267    if (offset_top == null) {
17268      offset_top = 0;
17269    }
17270    if (parent_selector == null) {
17271      parent_selector = void 0;
17272    }
17273    if (inner_scrolling == null) {
17274      inner_scrolling = true;
17275    }
17276    if (sticky_class == null) {
17277      sticky_class = "is_stuck";
17278    }
17279    doc = $(document);
17280    if (enable_bottoming == null) {
17281      enable_bottoming = true;
17282    }
17283    outer_width = function(el) {
17284      var _el, computed, w;
17285      if (window.getComputedStyle) {
17286        _el = el[0];
17287        computed = window.getComputedStyle(el[0]);
17288        w = parseFloat(computed.getPropertyValue("width")) + parseFloat(computed.getPropertyValue("margin-left")) + parseFloat(computed.getPropertyValue("margin-right"));
17289        if (computed.getPropertyValue("box-sizing") !== "border-box") {
17290          w += parseFloat(computed.getPropertyValue("border-left-width")) + parseFloat(computed.getPropertyValue("border-right-width")) + parseFloat(computed.getPropertyValue("padding-left")) + parseFloat(computed.getPropertyValue("padding-right"));
17291        }
17292        return w;
17293      } else {
17294        return el.outerWidth(true);
17295      }
17296    };
17297    fn = function(elm, padding_bottom, parent_top, parent_height, top, height, el_float, detached) {
17298      var bottomed, detach, fixed, last_pos, last_scroll_height, offset, parent, recalc, recalc_and_tick, recalc_counter, spacer, tick;
17299      if (elm.data("sticky_kit")) {
17300        return;
17301      }
17302      elm.data("sticky_kit", true);
17303      last_scroll_height = doc.height();
17304      parent = elm.parent();
17305      if (parent_selector != null) {
17306        parent = parent.closest(parent_selector);
17307      }
17308      if (!parent.length) {
17309        throw "failed to find stick parent";
17310      }
17311      fixed = false;
17312      bottomed = false;
17313      spacer = manual_spacer != null ? manual_spacer && elm.closest(manual_spacer) : $("<div />");
17314      if (spacer) {
17315        spacer.css('position', elm.css('position'));
17316      }
17317      recalc = function() {
17318        var border_top, padding_top, restore;
17319        if (detached) {
17320          return;
17321        }
17322        last_scroll_height = doc.height();
17323        border_top = parseInt(parent.css("border-top-width"), 10);
17324        padding_top = parseInt(parent.css("padding-top"), 10);
17325        padding_bottom = parseInt(parent.css("padding-bottom"), 10);
17326        parent_top = parent.offset().top + border_top + padding_top;
17327        parent_height = parent.height();
17328        if (fixed) {
17329          fixed = false;
vendor: 6,068 bytes, lines 17330-17524
17330          bottomed = false;
17331          if (manual_spacer == null) {
17332            elm.insertAfter(spacer);
17333            spacer.detach();
17334          }
17335          elm.css({
17336            position: "",
17337            top: "",
17338            width: "",
17339            bottom: ""
17340          }).removeClass(sticky_class);
17341          restore = true;
17342        }
17343        top = elm.offset().top - (parseInt(elm.css("margin-top"), 10) || 0) - offset_top;
17344        height = elm.outerHeight(true);
17345        el_float = elm.css("float");
17346        if (spacer) {
17347          spacer.css({
17348            width: outer_width(elm),
17349            height: height,
17350            display: elm.css("display"),
17351            "vertical-align": elm.css("vertical-align"),
17352            "float": el_float
17353          });
17354        }
17355        if (restore) {
17356          return tick();
17357        }
17358      };
17359      recalc();
17360      if (height === parent_height) {
17361        return;
17362      }
17363      last_pos = void 0;
17364      offset = offset_top;
17365      recalc_counter = recalc_every;
17366      tick = function() {
17367        var css, delta, recalced, scroll, will_bottom, win_height;
17368        if (detached) {
17369          return;
17370        }
17371        recalced = false;
17372        if (recalc_counter != null) {
17373          recalc_counter -= 1;
17374          if (recalc_counter <= 0) {
17375            recalc_counter = recalc_every;
17376            recalc();
17377            recalced = true;
17378          }
17379        }
17380        if (!recalced && doc.height() !== last_scroll_height) {
17381          recalc();
17382          recalced = true;
17383        }
17384        scroll = win.scrollTop();
17385        if (last_pos != null) {
17386          delta = scroll - last_pos;
17387        }
17388        last_pos = scroll;
17389        if (fixed) {
17390          if (enable_bottoming) {
17391            will_bottom = scroll + height + offset > parent_height + parent_top;
17392            if (bottomed && !will_bottom) {
17393              bottomed = false;
17394              elm.css({
17395                position: "fixed",
17396                bottom: "",
17397                top: offset
17398              }).trigger("sticky_kit:unbottom");
17399            }
17400          }
17401          if (scroll < top) {
17402            fixed = false;
17403            offset = offset_top;
17404            if (manual_spacer == null) {
17405              if (el_float === "left" || el_float === "right") {
17406                elm.insertAfter(spacer);
17407              }
17408              spacer.detach();
17409            }
17410            css = {
17411              position: "",
17412              width: "",
17413              top: ""
17414            };
17415            elm.css(css).removeClass(sticky_class).trigger("sticky_kit:unstick");
17416          }
17417          if (inner_scrolling) {
17418            win_height = win.height();
17419            if (height + offset_top > win_height) {
17420              if (!bottomed) {
17421                offset -= delta;
17422                offset = Math.max(win_height - height, offset);
17423                offset = Math.min(offset_top, offset);
17424                if (fixed) {
17425                  elm.css({
17426                    top: offset + "px"
17427                  });
17428                }
17429              }
17430            }
17431          }
17432        } else {
17433          if (scroll > top) {
17434            fixed = true;
17435            css = {
17436              position: "fixed",
17437              top: offset
17438            };
17439            css.width = elm.css("box-sizing") === "border-box" ? elm.outerWidth() + "px" : elm.width() + "px";
17440            elm.css(css).addClass(sticky_class);
17441            if (manual_spacer == null) {
17442              elm.after(spacer);
17443              if (el_float === "left" || el_float === "right") {
17444                spacer.append(elm);
17445              }
17446            }
17447            elm.trigger("sticky_kit:stick");
17448          }
17449        }
17450        if (fixed && enable_bottoming) {
17451          if (will_bottom == null) {
17452            will_bottom = scroll + height + offset > parent_height + parent_top;
17453          }
17454          if (!bottomed && will_bottom) {
17455            bottomed = true;
17456            if (parent.css("position") === "static") {
17457              parent.css({
17458                position: "relative"
17459              });
17460            }
17461            return elm.css({
17462              position: "absolute",
17463              bottom: padding_bottom,
17464              top: "auto"
17465            }).trigger("sticky_kit:bottom");
17466          }
17467        }
17468      };
17469      recalc_and_tick = function() {
17470        recalc();
17471        return tick();
17472      };
17473      detach = function() {
17474        detached = true;
17475        win.off("touchmove", tick);
17476        win.off("scroll", tick);
17477        win.off("resize", recalc_and_tick);
17478        $(document.body).off("sticky_kit:recalc", recalc_and_tick);
17479        elm.off("sticky_kit:detach", detach);
17480        elm.removeData("sticky_kit");
17481        elm.css({
17482          position: "",
17483          bottom: "",
17484          top: "",
17485          width: ""
17486        });
17487        parent.position("position", "");
17488        if (fixed) {
17489          if (manual_spacer == null) {
17490            if (el_float === "left" || el_float === "right") {
17491              elm.insertAfter(spacer);
17492            }
17493            spacer.remove();
17494          }
17495          return elm.removeClass(sticky_class);
17496        }
17497      };
17498      win.on("touchmove", tick);
17499      win.on("scroll", tick);
17500      win.on("resize", recalc_and_tick);
17501      $(document.body).on("sticky_kit:recalc", recalc_and_tick);
17502      elm.on("sticky_kit:detach", detach);
17503      return setTimeout(tick, 0);
17504    };
17505    for (i = 0, len = this.length; i < len; i++) {
17506      elm = this[i];
17507      fn($(elm));
17508    }
17509    return this;
17510  };
17511
17512}).call(this);
17513/* ==========================================================================
17514 * bootstrap-tab-history.js
17515 * Author: Michael Narayan <[email protected]>
17516 * Repository: https://github.com/mnarayan01/bootstrap-tab-history/
17517 * ==========================================================================
17518 * Licensed under the Apache License, Version 2.0 (the "License"); you may
17519 * not use this file except in compliance with the License. You may obtain a
17520 * copy of the License at
17521 *
17522 * http://www.apache.org/licenses/LICENSE-2.0
17523 *
17524 * Unless required by applicable law or agreed to in writing, softw
17524are
17525 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
17526 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
17527 * License for the specific language governing permissions and limitations
17528 * under the License.
17529 * ========================================================================== */
17530
17531/* ========================================================================== */
17532/* JSHint directives                                                          */
17533/*                                                                            */
17534/* global BootstrapTabHistory: true                                           */
17535/*                                                                            */
17536/* global document                                                            */
17537/* global jQuery                                                              */
17538/* global history                                                             */
17539/* global window                                                              */
17540/* ========================================================================== */
17541
17542/**
17543 * Integrate [HTML5 history state tracking](https://developer.mozilla.org/en-US/docs/Web/Guide/API/DOM/Manipulating_the_browser_history)
17544 * with [`bootstrap/tab.js`](http://getbootstrap.com/javascript/#tabs). To enable tracking on a tab element, simply set
17545 * the `data-tab-history` attribute to true (or a string denoting a tab grouping).
17546 *
17547 * See the README for additional information.
17548 *
17549 * Functionality based upon bootstrap/tab.js v3.1.0; reference it when making any changes.
17550 */
17551
17552BootstrapTabHistory = {
17553  options: {
17554    /**
17555     * When the anchor portion of the URI is used to activate a tab, scroll down to the given offset, rather than the
17556     * element with the given `id` attribute. Set to null to disable. Only relevant if showTabsBasedOnAnchor is true.
17557     *
17558     * May be overriden on a per-element basis by the attribute `data-tab-history-anchor-y-offset`.
17559     *
17560     * @public
17561     * @type {?number}
17562     */
17563    defaultAnchorYOffset: 0,
17564    /**
17565     * Either 'push' or 'replace', for whether to use `history.pushState` or `history.replaceState`, resp.
17566     *
17567     * May be overriden on a per-element basis by the attribute `data-tab-history-changer`.
17568     *
17569     * @public
17570     * @type {string}
17571     */
17572    defaultChanger: 'replace',
17573    /**
17574     * If true, update the URL in onShownTab in the calls to `history.pushState` and `history.replaceState`. Otherwise,
17575     * `null` is passed as the third parameter to these calls.
17576     *
17577     * May be overriden on a per-element basis by the attribute `data-tab-history-update-url`.
17578     *
17579     * @public
17580     * @type {boolean}
17581     */
17582    defaultUpdateURL: false,
17583    /**
17584     * Should the anchor portion of the loaded URI be used to activate a single tab if no history was present on page
17585     * load.
17586     *
17587     * @public
17588     * @type {boolean}
17589     */
17590    showTabsBasedOnAnchor: true
17591  }
17592};
17593
17594(function () {
17595  'use strict';
17596
17597  jQuery(function () {
17598    if(history && history.pushState && history.replaceState) {
17599      var bootstrapTabHistory = history.state && history.state.bootstrapTabHistory;
17600
17601      if(bootstrapTabHistory) {
17602        showTabsBasedOnState(bootstrapTabHistory);
17603      } else {
17604        showTabsBasedOnAnchor();
17605      }
17606
17607      backfillHistoryState();
17608
17609      jQuery(document).on('shown.bs.tab show.bs.collapse', onShownTab);
17610      jQuery(window).on('popstate', onPopState);
17611    } else {
17612      showTabsBasedOnAnchor();
17613    }
17614  });
17615
17616  /**
17617   * Used to prevent onShownTab from registering shown events that we triggered via showTabsBasedOnState.
17618   *
17619   * @type {boolean}
17620   */
17621  var showingTabsBasedOnState = false;
17622
17623  /**
17624   * Used to update `history.state` to reflect the default active tabs on initial page load. This supports proper
17625   * `history.back` handling when `data-tab-history-update-url` is true.
17626   */
17627  function backfillHistoryState() {
17628    var newState = null;
17629
17630    jQuery('li.active > [data-tab-history], .panel-title.active [data-tab-history]').each(function () {//edited by Uncode
17631      var $activeTabElement = jQuery(this);
17632      var selector = getTabSelector($activeTabElement);
17633
17634      if(selector) {
17635        var tabGroup = getTabGroup($activeTabElement);
17636
17637        if(tabGroup) {
17638          newState = createNewHistoryState(newState || history.state, tabGroup, selector);
17639        }
17640      }
17641    });
17642
17643    if(newState) {
17644      history.replaceState(newState, '', null);
17645    }
17646  }
17647
17648  /**
17649   * Clone the existing state, ensure its bootstrapTabHistory attribute is an Object, and add the provided tabGroup to
17650   * said bootstrapTabHistory attribute.
17651   *
17652   * @param {?object} existingState
17653   * @param {!string} tabGroup
17654   * @param {!string} selector
17655   * @returns {!object}
17656   */
17657  function createNewHistoryState(existingState, tabGroup, selector) {
17658    // Clone history.state and ensure it has a bootstrapTabHistory entry.
17659    var newState = jQuery.extend(true, {}, existingState, {
17660      bootstrapTabHistory: {}
17661    });
17662
17663    newState.bootstrapTabHistory[tabGroup] = selector;
17664
17665    return newState;
17666  }
17667
17668  /**
17669   * @param {jQuery} $tab
17670   * @returns {?string}
17671   */
17672  function getTabGroup($tab) {
17673    return parseTruthyAttributeValue($tab.data('tab-history'));
17674  }
17675
17676  /**
17677   * @param {jQuery} $tab
17678   * @returns {?string}
17679   */
17680  function getTabSelector($tab) {
17681    return $tab.data('target') || $tab.attr('href');
17682  }
17683
17684  /**
17685   * Receives the `shown.bs.tab` event. Updates `history.state` as appropriate.
17686   *
17687   * @param {jQuery.Event} shownEvt
17688   */
17689  function onShownTab(shownEvt) {
17690    if(!showingTabsBasedOnState) {
17691      var $activatedTab = jQuery(shownEvt.target);
17692      if ( $activatedTab.hasClass('panel-collapse') )
17693        $activatedTab = $activatedTab.closest('.panel').find('a');
17694      var selector = getTabSelector($activatedTab);
17695
17696      if(selector) {
17697        var tabGroup = getTabGroup($activatedTab);
17698
17699        if(tabGroup) {
17700          var historyChanger = $activatedTab.data('tab-history-changer') || BootstrapTabHistory.options.defaultChanger;
17701          var newState = createNewHistoryState(history.state, tabGroup, selector);
17702          var updateURL = (function ($activatedTab) {
17703            if(selector[0] === '#') {
17704              var elementUpdateURLOption = parseTruthyAttributeValue($activatedTab.data('tab-history-update-url'));
17705
17706              if(elementUpdateURLOption === undefined) {
17707                return BootstrapTabHistory.options.defaultUpdateURL;
17708              } else {
17709                return elementUpdateURLOption;
17710              }
17711            } else {
17712              return false;
17713            }
17714          })($activatedTab);
17715
17716          switch(historyChanger) {
17717            case 'push':
17718              history.pushState(newState, '', updateURL ? selector : null);
17719              break;
17720            case 'replace':
17721              history.replaceState(newState, '', updateURL ? selector : null);
17722              break;
17723            default:
17724              throw new Error('Unknown tab-history-changer: ' + historyChanger);
17725          }
17726        }
17727      }
17728    }
17729  }
17730
17731  /**
17732   * Receives the `popstate` event. Shows tabs based on the value of `history.state` as appropriate.
17733   */
17734  function onPopState() {
17735    var bootstrapTabHistory = history.state && history.state.bootstrapTabHistory;
17736
17737    if(bootstrapTabHistory) {
17738      showTabsBasedOnState(bootstrapTabHistory);
17739    }
17740  }
17741
17742  /**
17743   * Returns the given value, _unless_ that value is an empty string, in which case `true` is returned.
17744   *
17745   * Rationale: HAML data attributes which are set to `true` are rendered as a blank string.
17746   *
17747   * @param {*} value
17748   * @returns {*}
17749   */
17750  function parseTruthyAttributeValue(value) {
17751    if(value) {
17752      return value;
17753    } else if(value === '') {
17754      return true;
17755    } else {
17756      return value;
17757    }
17758  }
17759
17760  /**
17761   * Show tabs based upon the anchor component of `window.location`.
17762   */
17763  function showTabsBasedOnAnchor() {
17764    if(BootstrapTabHistory.options.showTabsBasedOnAnchor) {
17765      var anchor = window.location && window.location.hash;
17766
17767      if(anchor) {
17768        var $tabElement = showTabForSelector(anchor);
17769
17770        if($tabElement && window.addEventListener && window.removeEventListener) {
17771          var anchorYOffset = (function ($tabElement) {
17772            var elementSetting = $tabElement.data('tab-history-anchor-y-offset');
17773
17774            if(elementSetting === undefined) {
17775              return BootstrapTabHistory.options.defaultAnchorYOffset;
17776            } else {
17777              return elementSetting;
17778            }
17779          })($tabElement);
17780
17781          // HACK: This prevents scrolling to the tab on page load. This relies on the fact that we should never get
17782          //   here on `history.forward`, `history.back`, or `location.reload`, since in all those situations the
17783          //   `history.state` object should have been used (unless the browser did not support the modern History API).
17784          if(anchorYOffset || anchorYOffset === 0) {
17785            var scrollListener = function resetAnchorScroll () {
17786              window.removeEventListener('scroll', scrollListener);
17787              window.scrollTo(0, anchorYOffset);
17788            };
17789
17790            window.addEventListener('scroll', scrollListener);
17791          }
17792        }
17793      }
17794    }
17795  }
17796
17797  /**
17798   * Show a tab which corresponds to the provided selector.
17799   *
17800   * @param {string} selector - A CSS selector.
17801   * @returns {?jQuery} - The tab which was found to show (even if said tab was already active).
17802   */
17803  function showTabForSelector(selector) {
17804    var $tabElement = (function (selector) {
17805      var $ret = null;
17806
17807      jQuery('[data-toggle="tab"], [data-toggle="pill"], [data-toggle="collapse"]').each(function () {
17808        var $potentialTab = jQuery(this);
17809
17810        if(($potentialTab.attr('href') === selector || $potentialTab.data('target') === selector ) && getTabGroup($potentialTab)) {
17811          $ret = $potentialTab;
17812          return false;
vendor: 4,522 bytes, lines 17813-17962
17813        } else {
17814         return null;
17815        }
17816      });
17817
17818      return $ret;
17819    })(selector);
17820
17821    if($tabElement) {
17822      $tabElement.trigger('click');
17823      //$tabElement.tab('show');
17824    }
17825
17826    return $tabElement;
17827  }
17828
17829  /**
17830   * Iterate through the provided set of tab tab groups, showing the tabs based on the stored selectors.
17831   *
17832   * @param {object} bootstrapTabHistory - Each of the values is passed to showTabForSelector; the keys are actually irrelevant.
17833   */
17834  function showTabsBasedOnState(bootstrapTabHistory) {
17835    showingTabsBasedOnState = true;
17836
17837    try {
17838      for(var k in bootstrapTabHistory) {
17839        if(bootstrapTabHistory.hasOwnProperty(k)) {
17840          showTabForSelector(bootstrapTabHistory[k]);
17841        }
17842      }
17843    } finally {
17844      showingTabsBasedOnState = false;
17845    }
17846  }
17847})();
17848
17849/*!
17850 * Justified Gallery - v3.6.3
17851 * http://miromannino.github.io/Justified-Gallery/
17852 * Copyright (c) 2016 Miro Mannino
17853 * Licensed under the MIT license.
17854 */
17855(function($) {
17856
17857  function hasScrollBar() {
17858    return $("body").height() > $(window).height();
17859  }
17860  /**
17861   * Justified Gallery controller constructor
17862   *
17863   * @param $gallery the gallery to build
17864   * @param settings the settings (the defaults are in $.fn.justifiedGallery.defaults)
17865   * @constructor
17866   */
17867  var JustifiedGallery = function ($gallery, settings) {
17868    
17869    this.settings = settings;
17870    this.checkSettings();
17871
17872    this.imgAnalyzerTimeout = null;
17873    this.entries = null;
17874    this.buildingRow = {
17875      entriesBuff : [],
17876      width : 0,
17877      height : 0,
17878      aspectRatio : 0
17879    };
17880    this.lastFetchedEntry = null;
17881    this.lastAnalyzedIndex = -1;
17882    this.yield = {
17883      every : 2, // do a flush every n flushes (must be greater than 1)
17884      flushed : 0 // flushed rows without a yield
17885    };
17886    this.border = settings.border >= 0 ? settings.border : settings.margins;
17887    this.maxRowHeight = this.retrieveMaxRowHeight();
17888    this.suffixRanges = this.retrieveSuffixRanges();
17889    this.offY = this.border;
17890    this.rows = 0;
17891    this.spinner = {
17892      phase : 0,
17893      timeSlot : 150,
17894      $el : $('<div class="spinner"><span></span><span></span><span></span></div>'),
17895      intervalId : null
17896    };
17897    this.checkWidthIntervalId = null;
17898    this.galleryWidth = $gallery.width();
17899    this.$gallery = $gallery;
17900
17901  };
17902
17903  /** @returns {String} the best suffix given the width and the height */
17904  JustifiedGallery.prototype.getSuffix = function (width, height) {
17905    var longestSide, i;
17906    longestSide = (width > height) ? width : height;
17907    for (i = 0; i < this.suffixRanges.length; i++) {
17908      if (longestSide <= this.suffixRanges[i]) {
17909        return this.settings.sizeRangeSuffixes[this.suffixRanges[i]];
17910      }
17911    }
17912    return this.settings.sizeRangeSuffixes[this.suffixRanges[i - 1]];
17913  };
17914
17915  /**
17916   * Remove the suffix from the string
17917   *
17918   * @returns {string} a new string without the suffix
17919   */
17920  JustifiedGallery.prototype.removeSuffix = function (str, suffix) {
17921    return str.substring(0, str.length - suffix.length);
17922  };
17923
17924  /**
17925   * @returns {boolean} a boolean to say if the suffix is contained in the str or not
17926   */
17927  JustifiedGallery.prototype.endsWith = function (str, suffix) {
17928    return str.indexOf(suffix, str.length - suffix.length) !== -1;
17929  };
17930
17931  /**
17932   * Get the used suffix of a particular url
17933   *
17934   * @param str
17935   * @returns {String} return the used suffix
17936   */
17937  JustifiedGallery.prototype.getUsedSuffix = function (str) {
17938    for (var si in this.settings.sizeRangeSuffixes) {
17939      if (this.settings.sizeRangeSuffixes.hasOwnProperty(si)) {
17940        if (this.settings.sizeRangeSuffixes[si].length === 0) continue;
17941        if (this.endsWith(str, this.settings.sizeRangeSuffixes[si])) return this.settings.sizeRangeSuffixes[si];
17942      }
17943    }
17944    return '';
17945  };
17946
17947  /**
17948   * Given an image src, with the width and the height, returns the new image src with the
17949   * best suffix to show the best quality thumbnail.
17950   *
17951   * @returns {String} the suffix to use
17952   */
17953  JustifiedGallery.prototype.newSrc = function (imageSrc, imgWidth, imgHeight, image) {
17954    var newImageSrc;
17955
17956    if (this.settings.thumbnailPath) {
17957      newImageSrc = this.settings.thumbnailPath(imageSrc, imgWidth, imgHeight, image);
17958    } else {
17959      var matchRes = imageSrc.match(this.settings.extension);
17960      var ext = (matchRes !== null) ? matchRes[0] : '';
17961      newImageSrc = imageSrc.replace(this.settings.extension, '');
17962      newImageSrc = this.removeSuffix(newImageSrc, this.getUsedSuffix(newImageSrc));
17963      newImageSrc += this.getSuffix(imgWidth, imgHeight) + ext;
17964    }
17965
17966    return newImageSrc;
17967  };
17968
17969  /**
17970   * Shows the images that is in the given entry
17971   *
17972   * @param $entry the entry
17973   * @param callback the callback that is called when the show animation is finished
17974   */
17975  JustifiedGallery.prototype.showImg = function ($entry, callback) {
17976    if (this.settings.cssAnimation) {
17977      $entry.addClass('entry-visible');
17978      if (callback) callback();
17979    } else {
17980      $entry.stop().fadeTo(this.settings.imagesAnimationDuration, 1.0, callback);
17981      $entry.find('> img, > a > img').stop().fadeTo(this.settings.imagesAnimationDuration, 1.0, callback);
17982    }
17983  };
17984
17985  /**
17986   * Extract the image src form the image, looking from the 'safe-src', and if it can't be found, from the
17987   * 'src' attribute. It saves in the image data the 'jg.originalSrc' field, with the extracted src.
17988   *
17989   * @param $image the image to analyze
17990   * @returns {String} the extracted src
17991   */
17992  JustifiedGallery.prototype.extractImgSrcFromImage = function ($image) {
17993    var imageSrc = (typeof $image.data('safe-src') !== 'undefined') ? $image.data('safe-src') : $image.attr('src');
17994    $image.data('jg.originalSrc', imageSrc);
17995    return imageSrc;
17996  };
17997
17998  /** @returns {jQuery} the image in the given entry */
17999  JustifiedGallery.prototype.imgFromEntry = function ($entry) {
18000    var $img = $entry.find('> img');
18001    if ($img.length === 0) $img = $entry.find('> a > img');
18002    if ($img.length === 0) $img = $entry.find('.t-entry-visual-cont img');//hacked by Uncode
18003    return $img.length === 0 ? null : $img;
18004  };
18005
18006  /** @returns {jQuery} the caption in the given entry */
18007  JustifiedGallery.prototype.captionFromEntry = function ($entry) {
18008    var $caption = $entry.find('> .caption');
18009    return $caption.length === 0 ? null : $caption;
18010  };
18011
18012  /**
18013   * Display the entry
18014   *
18015   * @param {jQuery} $entry the entry to display
18016   * @param {int} x the x position where the entry must be positioned
18017   * @param y the y position where the entry must be positioned
18018   * @param imgWidth the image width
18019   * @param imgHeight the image height
18020   * @param rowHeight the row height of the row that owns the entry
18021   */
18022  JustifiedGallery.prototype.displayEntry = function ($entry, x, y, imgWidth, imgHeight, rowHeight) {
18023    $entry.width(imgWidth);
18024    $entry.height(Math.floor(rowHeight));//hacked by Uncode
18025    $entry.css('top', Math.floor(y));//hacked by Uncode
18026    $entry.css('left', x);
18027
18028    var $image = this.imgFromEntry($entry);
18029    if ($image !== null) {
18030      $image.css('width', imgWidth);
18031      $image.css('height', imgHeight);
18032      $image.css('margin-left', - imgWidth / 2);
18033      $image.css('margin-top', - imgHeight / 2);
18034
18035      // Image reloading for an high quality of thumbnails
18036      var imageSrc = $image.attr('src');
18037      var newImageSrc = this.newSrc(imageSrc, imgWidth, imgHeight, $image[0]);
18038
18039      $image.one('error', function () {
18040        $image.attr('src', $image.data('jg.originalSrc')); //revert to the original thumbnail, we got it.
18041      });
18042
18043      var loadNewImage = function () {
18044        if (imageSrc !== newImageSrc) { //load the new image after the fadeIn
18045          $image.attr('src', newImageSrc);
18046        }
18047      };
18048
18049      if ($entry.data('jg.loaded') === 'skipped') {
18050        this.onImageEvent(imageSrc, $.proxy(function() {
18051          this.showImg($entry, loadNewImage);
18052          $entry.data('jg.loaded', true);
18053        }, this));
18054      } else {
18055        this.showImg($entry, loadNewImage);
18056      }
18057
18058    } else {
18059      this.showImg($entry);
18060    }
18061
18062    this.displayEntryCaption($entry);
18063  };
18064
18065  /**
18066   * Display the entry caption. If the caption element doesn't exists, it creates the caption using the 'alt'
18067   * or the 'title' attributes.
18068   *
18069   * @param {jQuery} $entry the entry to process
18070   */
18071  JustifiedGallery.prototype.displayEntryCaption = function ($entry) {
18072    var $image = this.imgFromEntry($entry);
18073    if ($image !== null && this.settings.captions) {
18074      var $imgCaption = this.captionFromEntry($entry);
18075
18076      // Create it if it doesn't exists
18077      if ($imgCaption === null) {
18078        var caption = $image.attr('alt');
18079        if (!this.isValidCaption(caption)) caption = $entry.attr('title');
18080        if (this.isValidCaption(caption)) { // Create only we found something
18081          $imgCaption = $('<div class="caption">' + caption + '</div>');
18082          $entry.append($imgCaption);
18083          $entry.data('jg.createdCaption', true);
18084        }
18085      }
18086
18087      // Create events (we check again the $imgCaption because it can be still inexistent)
18088      if ($imgCaption !== null) {
18089        if (!this.settings.cssAnimation) $imgCaption.stop().fadeTo(0, this.settings.captionSettings.nonVisibleOpacity);
18090        this.addCaptionEventsHandlers($entry);
18091      }
18092    } else {
18093      this.removeCaptionEventsHandlers($entry);
18094    }
18095  };
18096
18097  /**
18098   * Validates the caption
18099   *
18100   * @param caption The caption that should be validated
18101   * @return {boolean} Validation result
18102   */
18103  JustifiedGallery.prototype.isValidCaption = function (caption) {
18104    return (typeof caption !== 'undefined' && caption.length > 0);
18105  };
18106
18107  /**
18108   * The callback for the event 'mouseenter'. It assumes that the event currentTarget is an entry.
18109   * It shows the caption using jQuery (or using CSS if it is configured so)
18110   *
18111   * @param {Event} eventObject the event object
18112   */
18113  JustifiedGallery.prototype.onEntryMouseEnterForCaption = function (eventObject) {
18114    var $caption = this.captionFromEntry($(eventObject.currentTarget));
18115    if (this.settings.cssAnimation) {
18116      $caption.addClass('caption-visible').removeClass('caption-hidden');
18117    } else {
18118      $caption.stop().fadeTo(this.settings.captionSettings.animationDuration,
18119          this.settings.captionSettings.visibleOpacity);
18120    }
18121  };
18122
18123  /**
18124   * The callback for the event 'mouseleave'. It assumes that the event currentTarget is an entry.
18125   * It hides the caption using jQuery (or using CSS if it is configured so)
18126   *
18127   * @param {Event} eventObject the event object
18128   */
18129  JustifiedGallery.prototype.onEntryMouseLeaveForCaption = function (eventObject) {
18130    var $caption = this.captionFromEntry($(eventObject.currentTarget));
18131    if (this.settings.cssAnimation) {
18132      $caption.removeClass('caption-visible').removeClass('caption-hidden');
18133    } else {
18134      $caption.stop().fadeTo(this.settings.captionSettings.animationDuration,
18135          this.settings.captionSettings.nonVisibleOpacity);
18136    }
18137  };
18138
18139  /**
18140   * Add the handlers of the entry for the caption
18141   *
18142   * @param $entry the entry to modify
18143   */
18144  JustifiedGallery.prototype.addCaptionEventsHandlers = function ($entry) {
18145    var captionMouseEvents = $entry.data('jg.captionMouseEvents');
18146    if (typeof captionMouseEvents === 'undefined') {
18147      captionMouseEvents = {
18148        mouseenter: $.proxy(this.onEntryMouseEnterForCaption, this),
18149        mouseleave: $.proxy(this.onEntryMouseLeaveForCaption, this)
18150      };
18151      $entry.on('mouseenter', undefined, undefined, captionMouseEvents.mouseenter);
18152      $entry.on('mouseleave', undefined, undefined, captionMouseEvents.mouseleave);
18153      $entry.data('jg.captionMouseEvents', captionMouseEvents);
18154    }
18155  };
18156
18157  /**
18158   * Remove the handlers of the entry for the caption
18159   *
18160   * @param $entry the entry to modify
18161   */
18162  JustifiedGallery.prototype.removeCaptionEventsHandlers = function ($entry) {
18163    var captionMouseEvents = $entry.data('jg.captionMouseEvents');
18164    if (typeof captionMouseEvents !== 'undefined') {
18165      $entry.off('mouseenter', undefined, captionMouseEvents.mouseenter);
18166      $entry.off('mouseleave', undefined, captionMouseEvents.mouseleave);
18167      $entry.removeData('jg.captionMouseEvents');
18168    }
18169  };
18170
18171  /**
18172   * Justify the building row, preparing it to
18173   *
18174   * @param isLastRow
18175   * @returns a boolean to know if the row has been justified or not
18176   */
18177  JustifiedGallery.prototype.prepareBuildingRow = function (isLastRow) {
18178    var i, $entry, imgAspectRatio, newImgW, newImgH, justify = true;
18179    var minHeight = 0;
18180    var availableWidth = this.galleryWidth - 2 * this.border - (
18181        (this.buildingRow.entriesBuff.length - 1) * this.settings.margins);
18182    var rowHeight = Math.floor( availableWidth / this.buildingRow.aspectRatio );//hacked by Uncode
18183    var defaultRowHeight = this.settings.rowHeight;
18184    var justifiable = this.buildingRow.width / availableWidth > this.settings.justifyThreshold;
18185
18186    //Skip the last row if we can't justify it and the lastRow == 'hide'
18187    if (isLastRow && this.settings.lastRow === 'hide' && !justifiable) {
18188      for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
18189        $entry = this.buildingRow.entriesBuff[i];
18190        if (this.settings.cssAnimation)
18191          $entry.removeClass('entry-visible');
18192        else {
18193          $entry.stop().fadeTo(0, 0.1);
18194          $entry.find('> img, > a > img').fadeTo(0, 0);
18195        }
18196      }
18197      return -1;
18198    }
18199
18200    // With lastRow = nojustify, justify if is justificable (the images will not become too big)
18201    if (isLastRow && !justifiable && this.settings.lastRow !== 'justify' && this.settings.lastRow !== 'hide') {
18202      justify = false;
18203
18204      if (this.rows > 0) {
18205        defaultRowHeight = (this.offY - this.border - this.settings.margins * this.rows) / this.rows;
18206        justify = defaultRowHeight * this.buildingRow.aspectRatio / availableWidth > this.settings.justifyThreshold;
18207      }
18208    }
18209
18210    for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
18211      $entry = this.buildingRow.entriesBuff[i];
18212      imgAspectRatio = $entry.data('jg.width') / $entry.data('jg.height');
18213
18214      if (justify) {
18215        newImgW = (i === this.buildingRow.entriesBuff.length - 1) ? availableWidth : rowHeight * imgAspectRatio;
18216        newImgH = rowHeight;
18217      } else {
18218        newImgW = defaultRowHeight * imgAspectRatio;
18219        newImgH = defaultRowHeight;
18220      }
18221
18222      availableWidth -= Math.round(newImgW);
18223      $entry.data('jg.jwidth', Math.round(newImgW));
18224      $entry.data('jg.jheight', Math.ceil(newImgH));
18225      if (i === 0 || minHeight > newImgH) minHeight = newImgH;
18226    }
18227
18228    this.buildingRow.height = minHeight;
18229    return justify;
18230  };
18231
18232  /**
18233   * Clear the building row data to be used for a new row
18234   */
18235  JustifiedGallery.prototype.clearBuildingRow = function () {
18236    this.buildingRow.entriesBuff = [];
18237    this.buildingRow.aspectRatio = 0;
18238    this.buildingRow.width = 0;
18239  };
18240
18241  /**
18242   * Flush a row: justify it, modify the gallery height accordingly to the row height
18243   *
18244   * @param isLastRow
18245   */
18246  JustifiedGallery.prototype.flushRow = function (isLastRow) {
18247    var settings = this.settings;
18248    var $entry, buildingRowRes, offX = this.border, i;
18249
18250    buildingRowRes = this.prepareBuildingRow(isLastRow);
18251    if (isLastRow && settings.lastRow === 'hide' && buildingRowRes === -1) {
18252      this.clearBuildingRow();
18253      return;
18254    }
18255
18256    if (this.maxRowHeight) {
18257      if (this.maxRowHeight.isPercentage && this.maxRowHeight.value * settings.rowHeight < this.buildingRow.height) {
18258        this.buildingRow.height = this.maxRowHeight.value * settings.rowHeight;
18259      } else if (this.maxRowHeight.value >= settings.rowHeight && this.maxRowHeight.value < this.buildingRow.height) {
18260        this.buildingRow.height = this.maxRowHeight.value;
18261      }
18262    }
18263
18264    //Align last (unjustified) row
18265    if (settings.lastRow === 'center' || settings.lastRow === 'right') {
18266      var availableWidth = this.galleryWidth - 2 * this.border - (this.buildingRow.entriesBuff.length - 1) * settings.margins;
18267
18268      for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
18269        $entry = this.buildingRow.entriesBuff[i];
18270        availableWidth -= $entry.data('jg.jwidth');
18271      }
18272
18273      if (settings.lastRow === 'center')
18274        offX += availableWidth / 2;
18275      else if (settings.lastRow === 'right')
18276        offX += availableWidth;
18277    }
18278
18279    for (i = 0; i < this.buildingRow.entriesBuff.length; i++) {
18280      $entry = this.buildingRow.entriesBuff[i];
18281      this.displayEntry($entry, offX, this.offY, $entry.data('jg.jwidth'), $entry.data('jg.jheight'), this.buildingRow.height);
18282      offX += $entry.data('jg.jwidth') + settings.margins;
18283    }
18284
18285    //Gallery Height
18286    this.galleryHeightToSet = this.offY + this.buildingRow.height + this.border;
18287    this.$gallery.height(this.galleryHeightToSet + this.getSpinnerHeight());
18288
18289    if (!isLastRow || (this.buildingRow.height <= settings.rowHeight && buildingRowRes)) {
18290      //Ready for a new row
18291      this.offY += this.buildingRow.height + settings.margins;
18292      this.rows += 1;
18293      this.clearBuildingRow();
18294      this.$gallery.trigger('jg.rowflush');
18295    }
18296  };
18297
18298  /**
18299   * Checks the width of the gallery container, to know if a new justification is needed
18300   */
18301  var scrollBarOn = false;
18302  JustifiedGallery.prototype.checkWidth = function () {
18303    this.checkWidthIntervalId = setInterval($.proxy(function () {
18304      var galleryWidth = parseFloat(this.$gallery.width());
18305      if (hasScrollBar() === scrollBarOn) {
18306        if (Math.abs(galleryWidth - this.galleryWidth) > this.settings.refreshSensitivity) {
18307          this.galleryWidth = galleryWidth;
18308          this.rewind();
18309
18310          // Restart to analyze
18311          this.startImgAnalyzer(true);
18312        }
18313      } else {
18314        scrollBarOn = hasScrollBar();
18315        this.galleryWidth = galleryWidth;
18316      }
18317    }, this), this.settings.refreshTime);
18318  };
18319
18320  /**
18321   * @returns {boolean} a boolean saying if the spinner is active or not
18322   */
18323  JustifiedGallery.prototype.isSpinnerActive = function () {
18324    return this.spinner.intervalId !== null;
18325  };
18326
18327  /**
18328   * @returns {int} the spinner height
18329   */
18330  JustifiedGallery.prototype.getSpinnerHeight = function () {
18331    return this.spinner.$el.innerHeight();
18332  };
18333
18334  /**
18335   * Stops the spinner animation and modify the gallery height to exclude the spinner
18336   */
18337  JustifiedGallery.prototype.stopLoadingSpinnerAnimation = function () {
18338    clearInterval(this.spinner.intervalId);
18339    this.spinner.intervalId = null;
18340    this.$gallery.height(this.$gallery.height() - this.getSpinnerHeight());
18341    this.spinner.$el.detach();
18342  };
18343
18344  /**
18345   * Starts the spinner animation
18346   */
18347  JustifiedGallery.prototype.startLoadingSpinnerAnimation = function () {
18348    var spinnerContext = this.spinner;
18349    var $spinnerPoints = spinnerContext.$el.find('span');
18350    clearInterval(spinnerContext.intervalId);
18351    this.$gallery.append(spinnerContext.$el);
18352    this.$gallery.height(this.offY + this.buildingRow.height + this.getSpinnerHeight());
18353    spinnerContext.intervalId = setInterval(function () {
18354      if (spinnerContext.phase < $spinnerPoints.length) {
18355        $spinnerPoints.eq(spinnerContext.phase).fadeTo(spinnerContext.timeSlot, 1);
18356      } else {
vendor: 4,333 bytes, lines 18357-18501
18357        $spinnerPoints.eq(spinnerContext.phase - $spinnerPoints.length).fadeTo(spinnerContext.timeSlot, 0);
18358      }
18359      spinnerContext.phase = (spinnerContext.phase + 1) % ($spinnerPoints.length * 2);
18360    }, spinnerContext.timeSlot);
18361  };
18362
18363  /**
18364   * Rewind the image analysis to start from the first entry.
18365   */
18366  JustifiedGallery.prototype.rewind = function () {
18367    this.lastFetchedEntry = null;
18368    this.lastAnalyzedIndex = -1;
18369    this.offY = this.border;
18370    this.rows = 0;
18371    this.clearBuildingRow();
18372  };
18373
18374  /**
18375   * Update the entries searching it from the justified gallery HTML element
18376   *
18377   * @param norewind if norewind only the new entries will be changed (i.e. randomized, sorted or filtered)
18378   * @returns {boolean} true if some entries has been founded
18379   */
18380  JustifiedGallery.prototype.updateEntries = function (norewind) {
18381    var newEntries;
18382
18383    if (norewind && this.lastFetchedEntry != null) {
18384      newEntries = $(this.lastFetchedEntry).nextAll(this.settings.selector).toArray();
18385    } else {
18386      this.entries = [];
18387      newEntries = this.$gallery.children(this.settings.selector).toArray();
18388    }
18389
18390    if (newEntries.length > 0) {
18391
18392      // Sort or randomize
18393      if ($.isFunction(this.settings.sort)) {
18394        newEntries = this.sortArray(newEntries);
18395      } else if (this.settings.randomize) {
18396        newEntries = this.shuffleArray(newEntries);
18397      }
18398      this.lastFetchedEntry = newEntries[newEntries.length - 1];
18399
18400      // Filter
18401      if (this.settings.filter) {
18402        newEntries = this.filterArray(newEntries);
18403      } else {
18404        this.resetFilters(newEntries);
18405      }
18406
18407    }
18408
18409    this.entries = this.entries.concat(newEntries);
18410    return true;
18411  };
18412
18413  /**
18414   * Apply the entries order to the DOM, iterating the entries and appending the images
18415   *
18416   * @param entries the entries that has been modified and that must be re-ordered in the DOM
18417   */
18418  JustifiedGallery.prototype.insertToGallery = function (entries) {
18419    var that = this;
18420    $.each(entries, function () {
18421      $(this).appendTo(that.$gallery);
18422    });
18423  };
18424
18425  /**
18426   * Shuffle the array using the Fisher-Yates shuffle algorithm
18427   *
18428   * @param a the array to shuffle
18429   * @return the shuffled array
18430   */
18431  JustifiedGallery.prototype.shuffleArray = function (a) {
18432    var i, j, temp;
18433    for (i = a.length - 1; i > 0; i--) {
18434      j = Math.floor(Math.random() * (i + 1));
18435      temp = a[i];
18436      a[i] = a[j];
18437      a[j] = temp;
18438    }
18439    this.insertToGallery(a);
18440    return a;
18441  };
18442
18443  /**
18444   * Sort the array using settings.comparator as comparator
18445   *
18446   * @param a the array to sort (it is sorted)
18447   * @return the sorted array
18448   */
18449  JustifiedGallery.prototype.sortArray = function (a) {
18450    a.sort(this.settings.sort);
18451    this.insertToGallery(a);
18452    return a;
18453  };
18454
18455  /**
18456   * Reset the filters removing the 'jg-filtered' class from all the entries
18457   *
18458   * @param a the array to reset
18459   */
18460  JustifiedGallery.prototype.resetFilters = function (a) {
18461    for (var i = 0; i < a.length; i++) $(a[i]).removeClass('jg-filtered');
18462  };
18463
18464  /**
18465   * Filter the entries considering theirs classes (if a string has been passed) or using a function for filtering.
18466   *
18467   * @param a the array to filter
18468   * @return the filtered array
18469   */
18470  JustifiedGallery.prototype.filterArray = function (a) {
18471    var settings = this.settings;
18472    if ($.type(settings.filter) === 'string') {
18473      // Filter only keeping the entries passed in the string
18474      return a.filter(function (el) {
18475        var $el = $(el);
18476        if ($el.is(settings.filter)) {
18477          $el.removeClass('jg-filtered');
18478          return true;
18479        } else {
18480          $el.addClass('jg-filtered').removeClass('jg-visible');
18481          return false;
18482        }
18483      });
18484    } else if ($.isFunction(settings.filter)) {
18485      // Filter using the passed function
18486      var filteredArr = a.filter(settings.filter);
18487      for (var i = 0; i < a.length; i++) {
18488        if (filteredArr.indexOf(a[i]) == -1) {
18489          $(a[i]).addClass('jg-filtered').removeClass('jg-visible');
18490        } else {
18491          $(a[i]).removeClass('jg-filtered');
18492        }
18493      }
18494      return filteredArr;
18495    }
18496  };
18497
18498  /**
18499   * Destroy the Justified Gallery instance.
18500   *
18501   * It clears all the css properties added in the style attributes. We doesn't backup the original
18502   * values for those css attributes, because it costs (performance) and because in general one
18503   * shouldn't use the style attribute for an uniform set of images (where we suppose the use of
18504   * classes). Creating a backup is also difficult because JG could be called multiple times and
18505   * with different style attributes.
18506   */
18507  JustifiedGallery.prototype.destroy = function () {
18508    clearInterval(this.checkWidthIntervalId);
18509
18510    $.each(this.entries, $.proxy(function(_, entry) {
18511      var $entry = $(entry);
18512
18513      // Reset entry style
18514      $entry.css('width', '');
18515      $entry.css('height', '');
18516      $entry.css('top', '');
18517      $entry.css('left', '');
18518      $entry.data('jg.loaded', undefined);
18519      $entry.removeClass('jg-entry');
18520
18521      // Reset image style
18522      var $img = this.imgFromEntry($entry);
18523      $img.css('width', '');
18524      $img.css('height', '');
18525      $img.css('margin-left', '');
18526      $img.css('margin-top', '');
18527      $img.attr('src', $img.data('jg.originalSrc'));
18528      $img.data('jg.originalSrc', undefined);
18529
18530      // Remove caption
18531      this.removeCaptionEventsHandlers($entry);
18532      var $caption = this.captionFromEntry($entry);
18533      if ($entry.data('jg.createdCaption')) {
18534        // remove also the caption element (if created by jg)
18535        $entry.data('jg.createdCaption', undefined);
18536        if ($caption !== null) $caption.remove();
18537      } else {
18538        if ($caption !== null) $caption.fadeTo(0, 1);
18539      }
18540
18541    }, this));
18542
18543    this.$gallery.css('height', '');
18544    this.$gallery.removeClass('justified-gallery');
18545    this.$gallery.data('jg.controller', undefined);
18546  };
18547
18548  /**
18549   * Analyze the images and builds the rows. It returns if it found an image that is not loaded.
18550   *
18551   * @param isForResize if the image analyzer is called for resizing or not, to call a different callback at the end
18552   */
18553  JustifiedGallery.prototype.analyzeImages = function (isForResize) {
18554    for (var i = this.lastAnalyzedIndex + 1; i < this.entries.length; i++) {
18555      var $entry = $(this.entries[i]);
18556      if ($entry.data('jg.loaded') === true || $entry.data('jg.loaded') === 'skipped') {
18557        var availableWidth = this.galleryWidth - 2 * this.border - (
18558            (this.buildingRow.entriesBuff.length - 1) * this.settings.margins);
18559        var imgAspectRatio = $entry.data('jg.width') / $entry.data('jg.height');
18560        if (availableWidth / (this.buildingRow.aspectRatio + imgAspectRatio) < this.settings.rowHeight) {
18561          this.flushRow(false);
18562          if(++this.yield.flushed >= this.yield.every) {
18563            this.startImgAnalyzer(isForResize);
18564            return;
18565          }
18566        }
18567
18568        this.buildingRow.entriesBuff.push($entry);
18569        this.buildingRow.aspectRatio += imgAspectRatio;
18570        this.buildingRow.width += imgAspectRatio * this.settings.rowHeight;
18571        this.lastAnalyzedIndex = i;
18572
18573      } else if ($entry.data('jg.loaded') !== 'error') {
18574        return;
18575      }
18576    }
18577
18578    // Last row flush (the row is not full)
18579    if (this.buildingRow.entriesBuff.length > 0) this.flushRow(true);
18580
18581    if (this.isSpinnerActive()) {
18582      this.stopLoadingSpinnerAnimation();
18583    }
18584
18585    /* Stop, if there is, the timeout to start the analyzeImages.
18586     This is because an image can be set loaded, and the timeout can be set,
18587     but this image can be analyzed yet.
18588     */
18589    this.stopImgAnalyzerStarter();
18590
18591    //On complete callback
18592    this.$gallery.trigger(isForResize ? 'jg.resize' : 'jg.complete');
18593    this.$gallery.height(this.galleryHeightToSet);
18594  };
18595
18596  /**
18597   * Stops any ImgAnalyzer starter (that has an assigned timeout)
18598   */
18599  JustifiedGallery.prototype.stopImgAnalyzerStarter = function () {
18600    this.yield.flushed = 0;
18601    if (this.imgAnalyzerTimeout !== null) clearTimeout(this.imgAnalyzerTimeout);
18602  };
18603
18604  /**
18605   * Starts the image analyzer. It is not immediately called to let the browser to update the view
18606   *
18607   * @param isForResize specifies if the image analyzer must be called for resizing or not
18608   */
18609  JustifiedGallery.prototype.startImgAnalyzer = function (isForResize) {
18610    var that = this;
18611    this.stopImgAnalyzerStarter();
18612    this.imgAnalyzerTimeout = setTimeout(function () {
18613      that.analyzeImages(isForResize);
18614    }, 0.001); // we can't start it immediately due to a IE different behaviour
18615  };
18616
18617  /**
18618   * Checks if the image is loaded or not using another image object. We cannot use the 'complete' image property,
18619   * because some browsers, with a 404 set complete = true.
18620   *
18621   * @param imageSrc the image src to load
18622   * @param onLoad callback that is called when the image has been loaded
vendor: 13,320 bytes, lines 18623-18973
18623   * @param onError callback that is called in case of an error
18624   */
18625  JustifiedGallery.prototype.onImageEvent = function (imageSrc, onLoad, onError) {
18626    if (!onLoad && !onError) return;
18627
18628    var memImage = new Image();
18629    var $memImage = $(memImage);
18630    if (onLoad) {
18631      $memImage.one('load', function () {
18632        $memImage.off('load error');
18633        onLoad(memImage);
18634      });
18635    }
18636    if (onError) {
18637      $memImage.one('error', function() {
18638        $memImage.off('load error');
18639        onError(memImage);
18640      });
18641    }
18642    memImage.src = imageSrc;
18643  };
18644
18645  /**
18646   * Init of Justified Gallery controlled
18647   * It analyzes all the entries starting theirs loading and calling the image analyzer (that works with loaded images)
18648   */
18649  JustifiedGallery.prototype.init = function () {
18650    var imagesToLoad = false, skippedImages = false, that = this;
18651    $.each(this.entries, function (index, entry) {
18652      var $entry = $(entry);
18653      var $image = that.imgFromEntry($entry);
18654
18655      $entry.addClass('jg-entry');
18656
18657      if ($entry.data('jg.loaded') !== true && $entry.data('jg.loaded') !== 'skipped') {
18658
18659        // Link Rel global overwrite
18660        if (that.settings.rel !== null) $entry.attr('rel', that.settings.rel);
18661
18662        // Link Target global overwrite
18663        if (that.settings.target !== null) $entry.attr('target', that.settings.target);
18664
18665        if ($image !== null) {
18666
18667          // Image src
18668          var imageSrc = that.extractImgSrcFromImage($image);
18669          $image.attr('src', imageSrc);
18670
18671          /* If we have the height and the width, we don't wait that the image is loaded, but we start directly
18672           * with the justification */
18673          if (that.settings.waitThumbnailsLoad === false) {
18674            var width = parseFloat($image.attr('width'));
18675            var height = parseFloat($image.attr('height'));
18676            if (!isNaN(width) && !isNaN(height)) {
18677              $entry.data('jg.width', width);
18678              $entry.data('jg.height', height);
18679              $entry.data('jg.loaded', 'skipped');
18680              skippedImages = true;
18681              that.startImgAnalyzer(false);
18682              return true; // continue
18683            }
18684          }
18685
18686          $entry.data('jg.loaded', false);
18687          imagesToLoad = true;
18688
18689          // Spinner start
18690          if (!that.isSpinnerActive()) that.startLoadingSpinnerAnimation();
18691
18692          that.onImageEvent(imageSrc, function (loadImg) { // image loaded
18693            $entry.data('jg.width', loadImg.width);
18694            $entry.data('jg.height', loadImg.height);
18695            $entry.data('jg.loaded', true);
18696            that.startImgAnalyzer(false);
18697          }, function () { // image load error
18698            $entry.data('jg.loaded', 'error');
18699            that.startImgAnalyzer(false);
18700          });
18701
18702        } else {
18703          $entry.data('jg.loaded', true);
18704          $entry.data('jg.width', $entry.width() | parseFloat($entry.css('width')) | 1);
18705          $entry.data('jg.height', $entry.height() | parseFloat($entry.css('height')) | 1);
18706        }
18707
18708      }
18709
18710    });
18711
18712    if (!imagesToLoad && !skippedImages) this.startImgAnalyzer(false);
18713    this.checkWidth();
18714  };
18715
18716  /**
18717   * Checks that it is a valid number. If a string is passed it is converted to a number
18718   *
18719   * @param settingContainer the object that contains the setting (to allow the conversion)
18720   * @param settingName the setting name
18721   */
18722  JustifiedGallery.prototype.checkOrConvertNumber = function (settingContainer, settingName) {
18723    if ($.type(settingContainer[settingName]) === 'string') {
18724      settingContainer[settingName] = parseFloat(settingContainer[settingName]);
18725    }
18726
18727    if ($.type(settingContainer[settingName]) === 'number') {
18728      if (isNaN(settingContainer[settingName])) throw 'invalid number for ' + settingName;
18729    } else {
18730      throw settingName + ' must be a number';
18731    }
18732  };
18733
18734  /**
18735   * Checks the sizeRangeSuffixes and, if necessary, converts
18736   * its keys from string (e.g. old settings with 'lt100') to int.
18737   */
18738  JustifiedGallery.prototype.checkSizeRangesSuffixes = function () {
18739    if ($.type(this.settings.sizeRangeSuffixes) !== 'object') {
18740      throw 'sizeRangeSuffixes must be defined and must be an object';
18741    }
18742
18743    var suffixRanges = [];
18744    for (var rangeIdx in this.settings.sizeRangeSuffixes) {
18745      if (this.settings.sizeRangeSuffixes.hasOwnProperty(rangeIdx)) suffixRanges.push(rangeIdx);
18746    }
18747
18748    var newSizeRngSuffixes = {0: ''};
18749    for (var i = 0; i < suffixRanges.length; i++) {
18750      if ($.type(suffixRanges[i]) === 'string') {
18751        try {
18752          var numIdx = parseInt(suffixRanges[i].replace(/^[a-z]+/, ''), 10);
18753          newSizeRngSuffixes[numIdx] = this.settings.sizeRangeSuffixes[suffixRanges[i]];
18754        } catch (e) {
18755          throw 'sizeRangeSuffixes keys must contains correct numbers (' + e + ')';
18756        }
18757      } else {
18758        newSizeRngSuffixes[suffixRanges[i]] = this.settings.sizeRangeSuffixes[suffixRanges[i]];
18759      }
18760    }
18761
18762    this.settings.sizeRangeSuffixes = newSizeRngSuffixes;
18763  };
18764
18765  /**
18766   * check and convert the maxRowHeight setting
18767   */
18768  JustifiedGallery.prototype.retrieveMaxRowHeight = function () {
18769    var newMaxRowHeight = { };
18770
18771    if ($.type(this.settings.maxRowHeight) === 'string') {
18772      if (this.settings.maxRowHeight.match(/^[0-9]+%$/)) {
18773        newMaxRowHeight.value = parseFloat(this.settings.maxRowHeight.match(/^([0-9]+)%$/)[1]) / 100;
18774        newMaxRowHeight.isPercentage = false;
18775      } else {
18776        newMaxRowHeight.value = parseFloat(this.settings.maxRowHeight);
18777        newMaxRowHeight.isPercentage = true;
18778      }
18779    } else if ($.type(this.settings.maxRowHeight) === 'number') {
18780      newMaxRowHeight.value = this.settings.maxRowHeight;
18781      newMaxRowHeight.isPercentage = false;
18782    } else if (this.settings.maxRowHeight === false ||
18783        this.settings.maxRowHeight === null ||
18784        typeof this.settings.maxRowHeight == 'undefined') {
18785      return null;
18786    } else {
18787      throw 'maxRowHeight must be a number or a percentage';
18788    }
18789
18790    // check if the converted value is not a number
18791    if (isNaN(newMaxRowHeight.value)) throw 'invalid number for maxRowHeight';
18792
18793    // check values
18794    if (newMaxRowHeight.isPercentage) {
18795      if (newMaxRowHeight.value < 100) newMaxRowHeight.value = 100;
18796    }
18797
18798    return newMaxRowHeight;
18799  };
18800
18801  /**
18802   * Checks the settings
18803   */
18804  JustifiedGallery.prototype.checkSettings = function () {
18805    this.checkSizeRangesSuffixes();
18806
18807    this.checkOrConvertNumber(this.settings, 'rowHeight');
18808    this.checkOrConvertNumber(this.settings, 'margins');
18809    this.checkOrConvertNumber(this.settings, 'border');
18810
18811    var lastRowModes = [
18812      'justify',
18813      'nojustify',
18814      'left',
18815      'center',
18816      'right',
18817      'hide'
18818    ];
18819    if (lastRowModes.indexOf(this.settings.lastRow) === -1) {
18820      throw 'lastRow must be one of: ' + lastRowModes.join(', ');
18821    }
18822
18823    this.checkOrConvertNumber(this.settings, 'justifyThreshold');
18824    if (this.settings.justifyThreshold < 0 || this.settings.justifyThreshold > 1) {
18825      throw 'justifyThreshold must be in the interval [0,1]';
18826    }
18827    if ($.type(this.settings.cssAnimation) !== 'boolean') {
18828      throw 'cssAnimation must be a boolean';
18829    }
18830
18831    if ($.type(this.settings.captions) !== 'boolean') throw 'captions must be a boolean';
18832    this.checkOrConvertNumber(this.settings.captionSettings, 'animationDuration');
18833
18834    this.checkOrConvertNumber(this.settings.captionSettings, 'visibleOpacity');
18835    if (this.settings.captionSettings.visibleOpacity < 0 ||
18836        this.settings.captionSettings.visibleOpacity > 1) {
18837      throw 'captionSettings.visibleOpacity must be in the interval [0, 1]';
18838    }
18839
18840    this.checkOrConvertNumber(this.settings.captionSettings, 'nonVisibleOpacity');
18841    if (this.settings.captionSettings.nonVisibleOpacity < 0 ||
18842        this.settings.captionSettings.nonVisibleOpacity > 1) {
18843      throw 'captionSettings.nonVisibleOpacity must be in the interval [0, 1]';
18844    }
18845
18846    this.checkOrConvertNumber(this.settings, 'imagesAnimationDuration');
18847    this.checkOrConvertNumber(this.settings, 'refreshTime');
18848    this.checkOrConvertNumber(this.settings, 'refreshSensitivity');
18849    if ($.type(this.settings.randomize) !== 'boolean') throw 'randomize must be a boolean';
18850    if ($.type(this.settings.selector) !== 'string') throw 'selector must be a string';
18851
18852    if (this.settings.sort !== false && !$.isFunction(this.settings.sort)) {
18853      throw 'sort must be false or a comparison function';
18854    }
18855
18856    if (this.settings.filter !== false && !$.isFunction(this.settings.filter) &&
18857        $.type(this.settings.filter) !== 'string') {
18858      throw 'filter must be false, a string or a filter function';
18859    }
18860  };
18861
18862  /**
18863   * It brings all the indexes from the sizeRangeSuffixes and it orders them. They are then sorted and returned.
18864   * @returns {Array} sorted suffix ranges
18865   */
18866  JustifiedGallery.prototype.retrieveSuffixRanges = function () {
18867    var suffixRanges = [];
18868    for (var rangeIdx in this.settings.sizeRangeSuffixes) {
18869      if (this.settings.sizeRangeSuffixes.hasOwnProperty(rangeIdx)) suffixRanges.push(parseInt(rangeIdx, 10));
18870    }
18871    suffixRanges.sort(function (a, b) { return a > b ? 1 : a < b ? -1 : 0; });
18872    return suffixRanges;
18873  };
18874
18875  /**
18876   * Update the existing settings only changing some of them
18877   *
18878   * @param newSettings the new settings (or a subgroup of them)
18879   */
18880  JustifiedGallery.prototype.updateSettings = function (newSettings) {
18881    // In this case Justified Gallery has been called again changing only some options
18882    this.settings = $.extend({}, this.settings, newSettings);
18883    this.checkSettings();
18884
18885    // As reported in the settings: negative value = same as margins, 0 = disabled
18886    this.border = this.settings.border >= 0 ? this.settings.border : this.settings.margins;
18887
18888    this.maxRowHeight = this.retrieveMaxRowHeight();
18889    this.suffixRanges = this.retrieveSuffixRanges();
18890  };
18891
18892  /**
18893   * Justified Gallery plugin for jQuery
18894   *
18895   * Events
18896   *  - jg.complete : called when all the gallery has been created
18897   *  - jg.resize : called when the gallery has been resized
18898   *  - jg.rowflush : when a new row appears
18899   *
18900   * @param arg the action (or the settings) passed when the plugin is called
18901   * @returns {*} the object itself
18902   */
18903  $.fn.justifiedGallery = function (arg) {
18904    return this.each(function (index, gallery) {
18905
18906      var $gallery = $(gallery);
18907      $gallery.addClass('justified-gallery');
18908
18909      var controller = $gallery.data('jg.controller');
18910      if (typeof controller === 'undefined') {
18911        // Create controller and assign it to the object data
18912        if (typeof arg !== 'undefined' && arg !== null && $.type(arg) !== 'object') {
18913          if (arg === 'destroy') return; // Just a call to an unexisting object
18914          throw 'The argument must be an object';
18915        }
18916        controller = new JustifiedGallery($gallery, $.extend({}, $.fn.justifiedGallery.defaults, arg));
18917        $gallery.data('jg.controller', controller);
18918      } else if (arg === 'norewind') {
18919        // In this case we don't rewind: we analyze only the latest images (e.g. to complete the last unfinished row
18920        // ... left to be more readable
18921      } else if (arg === 'destroy') {
18922        controller.destroy();
18923        return;
18924      } else {
18925        // In this case Justified Gallery has been called again changing only some options
18926        controller.updateSettings(arg);
18927        controller.rewind();
18928      }
18929
18930      // Update the entries list
18931      if (!controller.updateEntries(arg === 'norewind')) return;
18932
18933      // Init justified gallery
18934      controller.init();
18935
18936    });
18937  };
18938
18939  // Default options
18940  $.fn.justifiedGallery.defaults = {
18941    sizeRangeSuffixes: { }, /* e.g. Flickr configuration
18942        {
18943          100: '_t',  // used when longest is less than 100px
18944          240: '_m',  // used when longest is between 101px and 240px
18945          320: '_n',  // ...
18946          500: '',
18947          640: '_z',
18948          1024: '_b'  // used as else case because it is the last
18949        }
18950    */
18951    thumbnailPath: undefined, /* If defined, sizeRangeSuffixes is not used, and this function is used to determine the
18952    path relative to a specific thumbnail size. The function should accept respectively three arguments:
18953    current path, width and height */
18954    rowHeight: 120,
18955    maxRowHeight: false, // false or negative value to deactivate. Positive number to express the value in pixels,
18956                         // A string '[0-9]+%' to express in percentage (e.g. 300% means that the row height
18957                         // can't exceed 3 * rowHeight)
18958    margins: 1,
18959    border: -1, // negative value = same as margins, 0 = disabled, any other value to set the border
18960
18961    lastRow: 'nojustify', // … which is the same as 'left', or can be 'justify', 'center', 'right' or 'hide'
18962
18963    justifyThreshold: 0.90, /* if row width / available space > 0.90 it will be always justified
18964                             * (i.e. lastRow setting is not considered) */
18965    waitThumbnailsLoad: true,
18966    captions: true,
18967    cssAnimation: true,
18968    imagesAnimationDuration: 500, // ignored with css animations
18969    captionSettings: { // ignored with css animations
18970      animationDuration: 500,
18971      visibleOpacity: 0.7,
18972      nonVisibleOpacity: 0.0
18973    },
vendor: 5,759 bytes, lines 18974-19116
18974    rel: null, // rewrite the rel of each analyzed links
18975    target: null, // rewrite the target of all links
18976    extension: /\.[^.\\/]+$/, // regexp to capture the extension of an image
18977    refreshTime: 200, // time interval (in ms) to check if the page changes its width
18978    refreshSensitivity: 0, // change in width allowed (in px) without re-building the gallery
18979    randomize: false,
18980    sort: false, /*
18981      - false: to do not sort
18982      - function: to sort them using the function as comparator (see Array.prototype.sort())
18983    */
18984    filter: false, /*
18985      - false, null or undefined: for a disabled filter
18986      - a string: an entry is kept if entry.is(filter string) returns true
18987                  see jQuery's .is() function for further information
18988      - a function: invoked with arguments (entry, index, array). Return true to keep the entry, false otherwise.
18989                    It follows the specifications of the Array.prototype.filter() function of JavaScript.
18990    */
18991    selector: 'a, div:not(.spinner)' // The selector that is used to know what are the entries of the gallery
18992  };
18993
18994}(jQuery));
18995/*!
18996* Customized version of iScroll.js 0.0.1
18997* It fixes bugs affecting its integration with fullpage.js
18998*/
18999/*! iScroll v5.2.0 ~ (c) 2008-2016 Matteo Spinelli ~ http://cubiq.org/license */
19000(function (window, document, Math) {
19001var rAF = window.requestAnimationFrame  ||
19002    window.webkitRequestAnimationFrame  ||
19003    window.mozRequestAnimationFrame     ||
19004    window.oRequestAnimationFrame       ||
19005    window.msRequestAnimationFrame      ||
19006    function (callback) { window.setTimeout(callback, 1000 / 60); };
19007
19008var utils = (function () {
19009    var me = {};
19010
19011    var _elementStyle = document.createElement('div').style;
19012    var _vendor = (function () {
19013        var vendors = ['t', 'webkitT', 'MozT', 'msT', 'OT'],
19014            transform,
19015            i = 0,
19016            l = vendors.length;
19017
19018        for ( ; i < l; i++ ) {
19019            transform = vendors[i] + 'ransform';
19020            if ( transform in _elementStyle ) return vendors[i].substr(0, vendors[i].length-1);
19021        }
19022
19023        return false;
19024    })();
19025
19026    function _prefixStyle (style) {
19027        if ( _vendor === false ) return false;
19028        if ( _vendor === '' ) return style;
19029        return _vendor + style.charAt(0).toUpperCase() + style.substr(1);
19030    }
19031
19032    me.getTime = Date.now || function getTime () { return new Date().getTime(); };
19033
19034    me.extend = function (target, obj) {
19035        for ( var i in obj ) {
19036            target[i] = obj[i];
19037        }
19038    };
19039
19040    me.addEvent = function (el, type, fn, capture) {
19041        el.addEventListener(type, fn, !!capture);
19042    };
19043
19044    me.removeEvent = function (el, type, fn, capture) {
19045        el.removeEventListener(type, fn, !!capture);
19046    };
19047
19048    me.prefixPointerEvent = function (pointerEvent) {
19049        return window.MSPointerEvent ?
19050            'MSPointer' + pointerEvent.charAt(7).toUpperCase() + pointerEvent.substr(8):
19051            pointerEvent;
19052    };
19053
19054    me.momentum = function (current, start, time, lowerMargin, wrapperSize, deceleration) {
19055        var distance = current - start,
19056            speed = Math.abs(distance) / time,
19057            destination,
19058            duration;
19059
19060        deceleration = deceleration === undefined ? 0.0006 : deceleration;
19061
19062        destination = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 );
19063        duration = speed / deceleration;
19064
19065        if ( destination < lowerMargin ) {
19066            destination = wrapperSize ? lowerMargin - ( wrapperSize / 2.5 * ( speed / 8 ) ) : lowerMargin;
19067            distance = Math.abs(destination - current);
19068            duration = distance / speed;
19069        } else if ( destination > 0 ) {
19070            destination = wrapperSize ? wrapperSize / 2.5 * ( speed / 8 ) : 0;
19071            distance = Math.abs(current) + destination;
19072            duration = distance / speed;
19073        }
19074
19075        return {
19076            destination: Math.round(destination),
19077            duration: duration
19078        };
19079    };
19080
19081    var _transform = _prefixStyle('transform');
19082
19083    me.extend(me, {
19084        hasTransform: _transform !== false,
19085        hasPerspective: _prefixStyle('perspective') in _elementStyle,
19086        hasTouch: 'ontouchstart' in window,
19087        hasPointer: !!(window.PointerEvent || window.MSPointerEvent), // IE10 is prefixed
19088        hasTransition: _prefixStyle('transition') in _elementStyle
19089    });
19090
19091    /*
19092    This should find all Android browsers lower than build 535.19 (both stock browser and webview)
19093    - galaxy S2 is ok
19094    - 2.3.6 : `AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1`
19095    - 4.0.4 : `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
19096   - galaxy S3 is badAndroid (stock brower, webview)
19097     `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
19098   - galaxy S4 is badAndroid (stock brower, webview)
19099     `AppleWebKit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30`
19100   - galaxy S5 is OK
19101     `AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36 (Chrome/)`
19102   - galaxy S6 is OK
19103     `AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Mobile Safari/537.36 (Chrome/)`
19104  */
19105    me.isBadAndroid = (function() {
19106        var appVersion = window.navigator.appVersion;
19107        // Android browser is not a chrome browser.
19108        if (/Android/.test(appVersion) && !(/Chrome\/\d/.test(appVersion))) {
19109            var safariVersion = appVersion.match(/Safari\/(\d+.\d)/);
19110            if(safariVersion && typeof safariVersion === "object" && safariVersion.length >= 2) {
19111                return parseFloat(safariVersion[1]) < 535.19;
19112            } else {
19113                return true;
19114            }
19115        } else {
19116            return false;
vendor: 5,536 bytes, lines 19117-19309
19117        }
19118    })();
19119
19120    me.extend(me.style = {}, {
19121        transform: _transform,
19122        transitionTimingFunction: _prefixStyle('transitionTimingFunction'),
19123        transitionDuration: _prefixStyle('transitionDuration'),
19124        transitionDelay: _prefixStyle('transitionDelay'),
19125        transformOrigin: _prefixStyle('transformOrigin')
19126    });
19127
19128    me.hasClass = function (e, c) {
19129        var re = new RegExp("(^|\\s)" + c + "(\\s|$)");
19130        return re.test(e.className);
19131    };
19132
19133    me.addClass = function (e, c) {
19134        if ( me.hasClass(e, c) ) {
19135            return;
19136        }
19137
19138        var newclass = e.className.split(' ');
19139        newclass.push(c);
19140        e.className = newclass.join(' ');
19141    };
19142
19143    me.removeClass = function (e, c) {
19144        if ( !me.hasClass(e, c) ) {
19145            return;
19146        }
19147
19148        var re = new RegExp("(^|\\s)" + c + "(\\s|$)", 'g');
19149        e.className = e.className.replace(re, ' ');
19150    };
19151
19152    me.offset = function (el) {
19153        var left = -el.offsetLeft,
19154            top = -el.offsetTop;
19155
19156        // jshint -W084
19157        while (el = el.offsetParent) {
19158            left -= el.offsetLeft;
19159            top -= el.offsetTop;
19160        }
19161        // jshint +W084
19162
19163        return {
19164            left: left,
19165            top: top
19166        };
19167    };
19168
19169    me.preventDefaultException = function (el, exceptions) {
19170        for ( var i in exceptions ) {
19171            if ( exceptions[i].test(el[i]) ) {
19172                return true;
19173            }
19174        }
19175
19176        return false;
19177    };
19178
19179    me.extend(me.eventType = {}, {
19180        touchstart: 1,
19181        touchmove: 1,
19182        touchend: 1,
19183
19184        mousedown: 2,
19185        mousemove: 2,
19186        mouseup: 2,
19187
19188        pointerdown: 3,
19189        pointermove: 3,
19190        pointerup: 3,
19191
19192        MSPointerDown: 3,
19193        MSPointerMove: 3,
19194        MSPointerUp: 3
19195    });
19196
19197    me.extend(me.ease = {}, {
19198        quadratic: {
19199            style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
19200            fn: function (k) {
19201                return k * ( 2 - k );
19202            }
19203        },
19204        circular: {
19205            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)
19206            fn: function (k) {
19207                return Math.sqrt( 1 - ( --k * k ) );
19208            }
19209        },
19210        back: {
19211            style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',
19212            fn: function (k) {
19213                var b = 4;
19214                return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1;
19215            }
19216        },
19217        bounce: {
19218            style: '',
19219            fn: function (k) {
19220                if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {
19221                    return 7.5625 * k * k;
19222                } else if ( k < ( 2 / 2.75 ) ) {
19223                    return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;
19224                } else if ( k < ( 2.5 / 2.75 ) ) {
19225                    return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;
19226                } else {
19227                    return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;
19228                }
19229            }
19230        },
19231        elastic: {
19232            style: '',
19233            fn: function (k) {
19234                var f = 0.22,
19235                    e = 0.4;
19236
19237                if ( k === 0 ) { return 0; }
19238                if ( k == 1 ) { return 1; }
19239
19240                return ( e * Math.pow( 2, - 10 * k ) * Math.sin( ( k - f / 4 ) * ( 2 * Math.PI ) / f ) + 1 );
19241            }
19242        }
19243    });
19244
19245    me.tap = function (e, eventName) {
19246        var ev = document.createEvent('Event');
19247        ev.initEvent(eventName, true, true);
19248        ev.pageX = e.pageX;
19249        ev.pageY = e.pageY;
19250        e.target.dispatchEvent(ev);
19251    };
19252
19253    me.click = function (e) {
19254        var target = e.target,
19255            ev;
19256
19257        if ( !(/(SELECT|INPUT|TEXTAREA)/i).test(target.tagName) ) {
19258            // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/initMouseEvent
19259            // initMouseEvent is deprecated.
19260            ev = document.createEvent(window.MouseEvent ? 'MouseEvents' : 'Event');
19261            ev.initEvent('click', true, true);
19262            ev.view = e.view || window;
19263            ev.detail = 1;
19264            ev.screenX = target.screenX || 0;
19265            ev.screenY = target.screenY || 0;
19266            ev.clientX = target.clientX || 0;
19267            ev.clientY = target.clientY || 0;
19268            ev.ctrlKey = !!e.ctrlKey;
19269            ev.altKey = !!e.altKey;
19270            ev.shiftKey = !!e.shiftKey;
19271            ev.metaKey = !!e.metaKey;
19272            ev.button = 0;
19273            ev.relatedTarget = null;
19274            ev._constructed = true;
19275            target.dispatchEvent(ev);
19276        }
19277    };
19278
19279    return me;
19280})();
19281function IScroll (el, options) {
19282    this.wrapper = typeof el == 'string' ? document.querySelector(el) : el;
19283    this.scroller = this.wrapper.children[0];
19284    this.scrollerStyle = this.scroller.style;       // cache style for better performance
19285
19286    this.options = {
19287
19288        resizeScrollbars: true,
19289
19290        mouseWheelSpeed: 20,
19291
19292        snapThreshold: 0.334,
19293
19294// INSERT POINT: OPTIONS
19295        disablePointer : !utils.hasPointer,
19296        disableTouch : utils.hasPointer || !utils.hasTouch,
19297        disableMouse : utils.hasPointer || utils.hasTouch,
19298        startX: 0,
19299        startY: 0,
19300        scrollY: true,
19301        directionLockThreshold: 5,
19302        momentum: true,
19303
19304        bounce: true,
19305        bounceTime: 600,
19306        bounceEasing: '',
19307
19308        preventDefault: true,
19309        preventDefaultException: { tagName: /^(INPUT|TEXTAREA|BUTTON|
vendor: 4,420 bytes, lines 19309-19453
19309SELECT|LABEL)$/ },
19310
19311        HWCompositing: true,
19312        useTransition: true,
19313        useTransform: true,
19314        bindToWrapper: typeof window.onmousedown === "undefined"
19315    };
19316
19317    for ( var i in options ) {
19318        this.options[i] = options[i];
19319    }
19320
19321    // Normalize options
19322    this.translateZ = this.options.HWCompositing && utils.hasPerspective ? ' translateZ(0)' : '';
19323
19324    this.options.useTransition = utils.hasTransition && this.options.useTransition;
19325    this.options.useTransform = utils.hasTransform && this.options.useTransform;
19326
19327    this.options.eventPassthrough = this.options.eventPassthrough === true ? 'vertical' : this.options.eventPassthrough;
19328    this.options.preventDefault = !this.options.eventPassthrough && this.options.preventDefault;
19329
19330    // If you want eventPassthrough I have to lock one of the axes
19331    this.options.scrollY = this.options.eventPassthrough == 'vertical' ? false : this.options.scrollY;
19332    this.options.scrollX = this.options.eventPassthrough == 'horizontal' ? false : this.options.scrollX;
19333
19334    // With eventPassthrough we also need lockDirection mechanism
19335    this.options.freeScroll = this.options.freeScroll && !this.options.eventPassthrough;
19336    this.options.directionLockThreshold = this.options.eventPassthrough ? 0 : this.options.directionLockThreshold;
19337
19338    this.options.bounceEasing = typeof this.options.bounceEasing == 'string' ? utils.ease[this.options.bounceEasing] || utils.ease.circular : this.options.bounceEasing;
19339
19340    this.options.resizePolling = this.options.resizePolling === undefined ? 60 : this.options.resizePolling;
19341
19342    if ( this.options.tap === true ) {
19343        this.options.tap = 'tap';
19344    }
19345
19346    // https://github.com/cubiq/iscroll/issues/1029
19347    if (!this.options.useTransition && !this.options.useTransform) {
19348        if(!(/relative|absolute/i).test(this.scrollerStyle.position)) {
19349            this.scrollerStyle.position = "relative";
19350        }
19351    }
19352
19353    if ( this.options.shrinkScrollbars == 'scale' ) {
19354        this.options.useTransition = false;
19355    }
19356
19357    this.options.invertWheelDirection = this.options.invertWheelDirection ? -1 : 1;
19358
19359// INSERT POINT: NORMALIZATION
19360
19361    // Some defaults
19362    this.x = 0;
19363    this.y = 0;
19364    this.directionX = 0;
19365    this.directionY = 0;
19366    this._events = {};
19367
19368// INSERT POINT: DEFAULTS
19369
19370    this._init();
19371    this.refresh();
19372
19373    this.scrollTo(this.options.startX, this.options.startY);
19374    this.enable();
19375}
19376
19377IScroll.prototype = {
19378    version: '5.2.0',
19379
19380    _init: function () {
19381        this._initEvents();
19382
19383        if ( this.options.scrollbars || this.options.indicators ) {
19384            this._initIndicators();
19385        }
19386
19387        if ( this.options.mouseWheel ) {
19388            this._initWheel();
19389        }
19390
19391        if ( this.options.snap ) {
19392            this._initSnap();
19393        }
19394
19395        if ( this.options.keyBindings ) {
19396            this._initKeys();
19397        }
19398
19399// INSERT POINT: _init
19400
19401    },
19402
19403    destroy: function () {
19404        this._initEvents(true);
19405        clearTimeout(this.resizeTimeout);
19406        this.resizeTimeout = null;
19407        this._execEvent('destroy');
19408    },
19409
19410    _transitionEnd: function (e) {
19411        if ( e.target != this.scroller || !this.isInTransition ) {
19412            return;
19413        }
19414
19415        this._transitionTime();
19416        if ( !this.resetPosition(this.options.bounceTime) ) {
19417            this.isInTransition = false;
19418            this._execEvent('scrollEnd');
19419        }
19420    },
19421
19422    _start: function (e) {
19423        // React to left mouse button only
19424        if ( utils.eventType[e.type] != 1 ) {
19425          // for button property
19426          // http://unixpapa.com/js/mouse.html
19427          var button;
19428        if (!e.which) {
19429          /* IE case */
19430          button = (e.button < 2) ? 0 :
19431                   ((e.button == 4) ? 1 : 2);
19432        } else {
19433          /* All others */
19434          button = e.button;
19435        }
19436            if ( button !== 0 ) {
19437                return;
19438            }
19439        }
19440
19441        if ( !this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated) ) {
19442            return;
19443        }
19444
19445        if ( this.options.preventDefault && !utils.isBadAndroid && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) {
19446            e.preventDefault();
19447        }
19448
19449        var point = e.touches ? e.touches[0] : e,
19450            pos;
19451
19452        this.initiated  = utils.eventType[e.type];
19453        this.moved      = false;
vendor: 5,928 bytes, lines 19454-19632
19454        this.distX      = 0;
19455        this.distY      = 0;
19456        this.directionX = 0;
19457        this.directionY = 0;
19458        this.directionLocked = 0;
19459
19460        this.startTime = utils.getTime();
19461
19462        if ( this.options.useTransition && this.isInTransition ) {
19463            this._transitionTime();
19464            this.isInTransition = false;
19465            pos = this.getComputedPosition();
19466            this._translate(Math.round(pos.x), Math.round(pos.y));
19467            this._execEvent('scrollEnd');
19468        } else if ( !this.options.useTransition && this.isAnimating ) {
19469            this.isAnimating = false;
19470            this._execEvent('scrollEnd');
19471        }
19472
19473        this.startX    = this.x;
19474        this.startY    = this.y;
19475        this.absStartX = this.x;
19476        this.absStartY = this.y;
19477        this.pointX    = point.pageX;
19478        this.pointY    = point.pageY;
19479
19480        this._execEvent('beforeScrollStart');
19481    },
19482
19483    _move: function (e) {
19484        if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) {
19485            return;
19486        }
19487
19488        if ( this.options.preventDefault ) {    // increases performance on Android? TODO: check!
19489            e.preventDefault();
19490        }
19491
19492        var point       = e.touches ? e.touches[0] : e,
19493            deltaX      = point.pageX - this.pointX,
19494            deltaY      = point.pageY - this.pointY,
19495            timestamp   = utils.getTime(),
19496            newX, newY,
19497            absDistX, absDistY;
19498
19499        this.pointX     = point.pageX;
19500        this.pointY     = point.pageY;
19501
19502        this.distX      += deltaX;
19503        this.distY      += deltaY;
19504        absDistX        = Math.abs(this.distX);
19505        absDistY        = Math.abs(this.distY);
19506
19507        // We need to move at least 10 pixels for the scrolling to initiate
19508        if ( timestamp - this.endTime > 300 && (absDistX < 10 && absDistY < 10) ) {
19509            return;
19510        }
19511
19512        // If you are scrolling in one direction lock the other
19513        if ( !this.directionLocked && !this.options.freeScroll ) {
19514            if ( absDistX > absDistY + this.options.directionLockThreshold ) {
19515                this.directionLocked = 'h';     // lock horizontally
19516            } else if ( absDistY >= absDistX + this.options.directionLockThreshold ) {
19517                this.directionLocked = 'v';     // lock vertically
19518            } else {
19519                this.directionLocked = 'n';     // no lock
19520            }
19521        }
19522
19523        if ( this.directionLocked == 'h' ) {
19524            if ( this.options.eventPassthrough == 'vertical' ) {
19525                e.preventDefault();
19526            } else if ( this.options.eventPassthrough == 'horizontal' ) {
19527                this.initiated = false;
19528                return;
19529            }
19530
19531            deltaY = 0;
19532        } else if ( this.directionLocked == 'v' ) {
19533            if ( this.options.eventPassthrough == 'horizontal' ) {
19534                e.preventDefault();
19535            } else if ( this.options.eventPassthrough == 'vertical' ) {
19536                this.initiated = false;
19537                return;
19538            }
19539
19540            deltaX = 0;
19541        }
19542
19543        deltaX = this.hasHorizontalScroll ? deltaX : 0;
19544        deltaY = this.hasVerticalScroll ? deltaY : 0;
19545
19546        newX = this.x + deltaX;
19547        newY = this.y + deltaY;
19548
19549        // Slow down if outside of the boundaries
19550        if ( newX > 0 || newX < this.maxScrollX ) {
19551            newX = this.options.bounce ? this.x + deltaX / 3 : newX > 0 ? 0 : this.maxScrollX;
19552        }
19553        if ( newY > 0 || newY < this.maxScrollY ) {
19554            newY = this.options.bounce ? this.y + deltaY / 3 : newY > 0 ? 0 : this.maxScrollY;
19555        }
19556
19557        this.directionX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0;
19558        this.directionY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0;
19559
19560        if ( !this.moved ) {
19561            this._execEvent('scrollStart');
19562        }
19563
19564        this.moved = true;
19565
19566        this._translate(newX, newY);
19567
19568/* REPLACE START: _move */
19569
19570        if ( timestamp - this.startTime > 300 ) {
19571            this.startTime = timestamp;
19572            this.startX = this.x;
19573            this.startY = this.y;
19574        }
19575
19576/* REPLACE END: _move */
19577
19578    },
19579
19580    _end: function (e) {
19581        if ( !this.enabled || utils.eventType[e.type] !== this.initiated ) {
19582            return;
19583        }
19584
19585        if ( this.options.preventDefault && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) {
19586            e.preventDefault();
19587        }
19588
19589        var point = e.changedTouches ? e.changedTouches[0] : e,
19590            momentumX,
19591            momentumY,
19592            duration = utils.getTime() - this.startTime,
19593            newX = Math.round(this.x),
19594            newY = Math.round(this.y),
19595            distanceX = Math.abs(newX - this.startX),
19596            distanceY = Math.abs(newY - this.startY),
19597            time = 0,
19598            easing = '';
19599
19600        this.isInTransition = 0;
19601        this.initiated = 0;
19602        this.endTime = utils.getTime();
19603
19604        // reset if we are outside of the boundaries
19605        if ( this.resetPosition(this.options.bounceTime) ) {
19606            return;
19607        }
19608
19609        this.scrollTo(newX, newY);  // ensures that the last position is rounded
19610
19611        // we scrolled less than 10 pixels
19612        if ( !this.moved ) {
19613            if ( this.options.tap ) {
19614                utils.tap(e, this.options.tap);
19615            }
19616
19617            if ( this.options.click ) {
19618                utils.click(e);
19619            }
19620
19621            this._execEvent('scrollCancel');
19622            return;
19623        }
19624
19625        if ( this._events.flick && duration < 200 && distanceX < 100 && distanceY < 100 ) {
19626            this._execEvent('flick');
19627            return;
19628        }
19629
19630        // start momentum animation if needed
19631        if ( this.options.momentum && duration < 300 ) {
19632            momentumX = this.hasHorizontalScroll ? utils.momentum(this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperW
vendor: 7,740 bytes, lines 19632-19905
19632idth : 0, this.options.deceleration) : { destination: newX, duration: 0 };
19633            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 };
19634            newX = momentumX.destination;
19635            newY = momentumY.destination;
19636            time = Math.max(momentumX.duration, momentumY.duration);
19637            this.isInTransition = 1;
19638        }
19639
19640
19641        if ( this.options.snap ) {
19642            var snap = this._nearestSnap(newX, newY);
19643            this.currentPage = snap;
19644            time = this.options.snapSpeed || Math.max(
19645                    Math.max(
19646                        Math.min(Math.abs(newX - snap.x), 1000),
19647                        Math.min(Math.abs(newY - snap.y), 1000)
19648                    ), 300);
19649            newX = snap.x;
19650            newY = snap.y;
19651
19652            this.directionX = 0;
19653            this.directionY = 0;
19654            easing = this.options.bounceEasing;
19655        }
19656
19657// INSERT POINT: _end
19658
19659        if ( newX != this.x || newY != this.y ) {
19660            // change easing function when scroller goes out of the boundaries
19661            if ( newX > 0 || newX < this.maxScrollX || newY > 0 || newY < this.maxScrollY ) {
19662                easing = utils.ease.quadratic;
19663            }
19664
19665            this.scrollTo(newX, newY, time, easing);
19666            return;
19667        }
19668
19669        this._execEvent('scrollEnd');
19670    },
19671
19672    _resize: function () {
19673        var that = this;
19674
19675        clearTimeout(this.resizeTimeout);
19676
19677        this.resizeTimeout = setTimeout(function () {
19678            that.refresh();
19679        }, this.options.resizePolling);
19680    },
19681
19682    resetPosition: function (time) {
19683        var x = this.x,
19684            y = this.y;
19685
19686        time = time || 0;
19687
19688        if ( !this.hasHorizontalScroll || this.x > 0 ) {
19689            x = 0;
19690        } else if ( this.x < this.maxScrollX ) {
19691            x = this.maxScrollX;
19692        }
19693
19694        if ( !this.hasVerticalScroll || this.y > 0 ) {
19695            y = 0;
19696        } else if ( this.y < this.maxScrollY ) {
19697            y = this.maxScrollY;
19698        }
19699
19700        if ( x == this.x && y == this.y ) {
19701            return false;
19702        }
19703
19704        this.scrollTo(x, y, time, this.options.bounceEasing);
19705
19706        return true;
19707    },
19708
19709    disable: function () {
19710        this.enabled = false;
19711    },
19712
19713    enable: function () {
19714        this.enabled = true;
19715    },
19716
19717    refresh: function () {
19718        var rf = this.wrapper.offsetHeight;     // Force reflow
19719
19720        this.wrapperWidth   = this.wrapper.clientWidth;
19721        this.wrapperHeight  = this.wrapper.clientHeight;
19722
19723/* REPLACE START: refresh */
19724
19725        this.scrollerWidth  = this.scroller.offsetWidth;
19726        this.scrollerHeight = this.scroller.offsetHeight;
19727
19728        this.maxScrollX     = this.wrapperWidth - this.scrollerWidth;
19729        this.maxScrollY     = this.wrapperHeight - this.scrollerHeight;
19730
19731/* REPLACE END: refresh */
19732
19733        this.hasHorizontalScroll    = this.options.scrollX && this.maxScrollX < 0;
19734        this.hasVerticalScroll      = this.options.scrollY && this.maxScrollY < 0;
19735
19736        if ( !this.hasHorizontalScroll ) {
19737            this.maxScrollX = 0;
19738            this.scrollerWidth = this.wrapperWidth;
19739        }
19740
19741        if ( !this.hasVerticalScroll ) {
19742            this.maxScrollY = 0;
19743            this.scrollerHeight = this.wrapperHeight;
19744        }
19745
19746        this.endTime = 0;
19747        this.directionX = 0;
19748        this.directionY = 0;
19749
19750        this.wrapperOffset = utils.offset(this.wrapper);
19751
19752        this._execEvent('refresh');
19753
19754        this.resetPosition();
19755
19756// INSERT POINT: _refresh
19757
19758    },
19759
19760    on: function (type, fn) {
19761        if ( !this._events[type] ) {
19762            this._events[type] = [];
19763        }
19764
19765        this._events[type].push(fn);
19766    },
19767
19768    off: function (type, fn) {
19769        if ( !this._events[type] ) {
19770            return;
19771        }
19772
19773        var index = this._events[type].indexOf(fn);
19774
19775        if ( index > -1 ) {
19776            this._events[type].splice(index, 1);
19777        }
19778    },
19779
19780    _execEvent: function (type) {
19781        if ( !this._events[type] ) {
19782            return;
19783        }
19784
19785        var i = 0,
19786            l = this._events[type].length;
19787
19788        if ( !l ) {
19789            return;
19790        }
19791
19792        for ( ; i < l; i++ ) {
19793            this._events[type][i].apply(this, [].slice.call(arguments, 1));
19794        }
19795    },
19796
19797    scrollBy: function (x, y, time, easing) {
19798        x = this.x + x;
19799        y = this.y + y;
19800        time = time || 0;
19801
19802        this.scrollTo(x, y, time, easing);
19803    },
19804
19805    scrollTo: function (x, y, time, easing) {
19806        easing = easing || utils.ease.circular;
19807
19808        this.isInTransition = this.options.useTransition && time > 0;
19809        var transitionType = this.options.useTransition && easing.style;
19810        if ( !time || transitionType ) {
19811                if(transitionType) {
19812                    this._transitionTimingFunction(easing.style);
19813                    this._transitionTime(time);
19814                }
19815            this._translate(x, y);
19816        } else {
19817            this._animate(x, y, time, easing.fn);
19818        }
19819    },
19820
19821    scrollToElement: function (el, time, offsetX, offsetY, easing) {
19822        el = el.nodeType ? el : this.scroller.querySelector(el);
19823
19824        if ( !el ) {
19825            return;
19826        }
19827
19828        var pos = utils.offset(el);
19829
19830        pos.left -= this.wrapperOffset.left;
19831        pos.top  -= this.wrapperOffset.top;
19832
19833        // if offsetX/Y are true we center the element to the screen
19834        if ( offsetX === true ) {
19835            offsetX = Math.round(el.offsetWidth / 2 - this.wrapper.offsetWidth / 2);
19836        }
19837        if ( offsetY === true ) {
19838            offsetY = Math.round(el.offsetHeight / 2 - this.wrapper.offsetHeight / 2);
19839        }
19840
19841        pos.left -= offsetX || 0;
19842        pos.top  -= offsetY || 0;
19843
19844        pos.left = pos.left > 0 ? 0 : pos.left < this.maxScrollX ? this.maxScrollX : pos.left;
19845        pos.top  = pos.top  > 0 ? 0 : pos.top  < this.maxScrollY ? this.maxScrollY : pos.top;
19846
19847        time = time === undefined || time === null || time === 'auto' ? Math.max(Math.abs(this.x-pos.left), Math.abs(this.y-pos.top)) : time;
19848
19849        this.scrollTo(pos.left, pos.top, time, easing);
19850    },
19851
19852    _transitionTime: function (time) {
19853        if (!this.options.useTransition) {
19854            return;
19855        }
19856        time = time || 0;
19857        var durationProp = utils.style.transitionDuration;
19858        if(!durationProp) {
19859            return;
19860        }
19861
19862        this.scrollerStyle[durationProp] = time + 'ms';
19863
19864        if ( !time && utils.isBadAndroid ) {
19865            this.scrollerStyle[durationProp] = '0.0001ms';
19866            // remove 0.0001ms
19867            var self = this;
19868            rAF(function() {
19869                if(self.scrollerStyle[durationProp] === '0.0001ms') {
19870                    self.scrollerStyle[durationProp] = '0s';
19871                }
19872            });
19873        }
19874
19875
19876        if ( this.indicators ) {
19877            for ( var i = this.indicators.length; i--; ) {
19878                this.indicators[i].transitionTime(time);
19879            }
19880        }
19881
19882
19883// INSERT POINT: _transitionTime
19884
19885    },
19886
19887    _transitionTimingFunction: function (easing) {
19888        this.scrollerStyle[utils.style.transitionTimingFunction] = easing;
19889
19890
19891        if ( this.indicators ) {
19892            for ( var i = this.indicators.length; i--; ) {
19893                this.indicators[i].transitionTimingFunction(easing);
19894            }
19895        }
19896
19897
19898// INSERT POINT: _transitionTimingFunction
19899
19900    },
19901
19902    _translate: function (x, y) {
19903        //Uncode addition
19904        if ( (" " + this.wrapper.className + " ").replace(/[\n\t]/g, " ").indexOf(" no-scrolloverflow ") > -1 )
19905            return false;
vendor: 9,939 bytes, lines 19906-20217
19906
19907        if ( this.options.useTransform ) {
19908
19909/* REPLACE START: _translate */
19910
19911            this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ;
19912
19913/* REPLACE END: _translate */
19914
19915        } else {
19916            x = Math.round(x);
19917            y = Math.round(y);
19918            this.scrollerStyle.left = x + 'px';
19919            this.scrollerStyle.top = y + 'px';
19920        }
19921
19922        this.x = x;
19923        this.y = y;
19924
19925
19926        if ( this.indicators ) {
19927            for ( var i = this.indicators.length; i--; ) {
19928                this.indicators[i].updatePosition();
19929            }
19930        }
19931
19932        //Uncode addition
19933        var uncodevent = new CustomEvent('fp-slide-scroll');
19934        window.dispatchEvent(uncodevent);
19935
19936// INSERT POINT: _translate
19937
19938    },
19939
19940    _initEvents: function (remove) {
19941        var eventType = remove ? utils.removeEvent : utils.addEvent,
19942            target = this.options.bindToWrapper ? this.wrapper : window;
19943
19944        eventType(window, 'orientationchange', this);
19945        eventType(window, 'resize', this);
19946
19947        if ( this.options.click ) {
19948            eventType(this.wrapper, 'click', this, true);
19949        }
19950
19951        if ( !this.options.disableMouse ) {
19952            eventType(this.wrapper, 'mousedown', this);
19953            eventType(target, 'mousemove', this);
19954            eventType(target, 'mousecancel', this);
19955            eventType(target, 'mouseup', this);
19956        }
19957
19958        if ( utils.hasPointer && !this.options.disablePointer ) {
19959            eventType(this.wrapper, utils.prefixPointerEvent('pointerdown'), this);
19960            eventType(target, utils.prefixPointerEvent('pointermove'), this);
19961            eventType(target, utils.prefixPointerEvent('pointercancel'), this);
19962            eventType(target, utils.prefixPointerEvent('pointerup'), this);
19963        }
19964
19965        if ( utils.hasTouch && !this.options.disableTouch ) {
19966            eventType(this.wrapper, 'touchstart', this);
19967            eventType(target, 'touchmove', this);
19968            eventType(target, 'touchcancel', this);
19969            eventType(target, 'touchend', this);
19970        }
19971
19972        eventType(this.scroller, 'transitionend', this);
19973        eventType(this.scroller, 'webkitTransitionEnd', this);
19974        eventType(this.scroller, 'oTransitionEnd', this);
19975        eventType(this.scroller, 'MSTransitionEnd', this);
19976    },
19977
19978    getComputedPosition: function () {
19979        var matrix = window.getComputedStyle(this.scroller, null),
19980            x, y;
19981
19982        if ( this.options.useTransform ) {
19983            matrix = matrix[utils.style.transform].split(')')[0].split(', ');
19984            x = +(matrix[12] || matrix[4]);
19985            y = +(matrix[13] || matrix[5]);
19986        } else {
19987            x = +matrix.left.replace(/[^-\d.]/g, '');
19988            y = +matrix.top.replace(/[^-\d.]/g, '');
19989        }
19990
19991        return { x: x, y: y };
19992    },
19993    _initIndicators: function () {
19994        var interactive = this.options.interactiveScrollbars,
19995            customStyle = typeof this.options.scrollbars != 'string',
19996            indicators = [],
19997            indicator;
19998
19999        var that = this;
20000
20001        this.indicators = [];
20002
20003        if ( this.options.scrollbars ) {
20004            // Vertical scrollbar
20005            if ( this.options.scrollY ) {
20006                indicator = {
20007                    el: createDefaultScrollbar('v', interactive, this.options.scrollbars),
20008                    interactive: interactive,
20009                    defaultScrollbars: true,
20010                    customStyle: customStyle,
20011                    resize: this.options.resizeScrollbars,
20012                    shrink: this.options.shrinkScrollbars,
20013                    fade: this.options.fadeScrollbars,
20014                    listenX: false
20015                };
20016
20017                this.wrapper.appendChild(indicator.el);
20018                indicators.push(indicator);
20019            }
20020
20021            // Horizontal scrollbar
20022            if ( this.options.scrollX ) {
20023                indicator = {
20024                    el: createDefaultScrollbar('h', interactive, this.options.scrollbars),
20025                    interactive: interactive,
20026                    defaultScrollbars: true,
20027                    customStyle: customStyle,
20028                    resize: this.options.resizeScrollbars,
20029                    shrink: this.options.shrinkScrollbars,
20030                    fade: this.options.fadeScrollbars,
20031                    listenY: false
20032                };
20033
20034                this.wrapper.appendChild(indicator.el);
20035                indicators.push(indicator);
20036            }
20037        }
20038
20039        if ( this.options.indicators ) {
20040            // TODO: check concat compatibility
20041            indicators = indicators.concat(this.options.indicators);
20042        }
20043
20044        for ( var i = indicators.length; i--; ) {
20045            this.indicators.push( new Indicator(this, indicators[i]) );
20046        }
20047
20048        // TODO: check if we can use array.map (wide compatibility and performance issues)
20049        function _indicatorsMap (fn) {
20050            if (that.indicators) {
20051                for ( var i = that.indicators.length; i--; ) {
20052                    fn.call(that.indicators[i]);
20053                }
20054            }
20055        }
20056
20057        if ( this.options.fadeScrollbars ) {
20058            this.on('scrollEnd', function () {
20059                _indicatorsMap(function () {
20060                    this.fade();
20061                });
20062            });
20063
20064            this.on('scrollCancel', function () {
20065                _indicatorsMap(function () {
20066                    this.fade();
20067                });
20068            });
20069
20070            this.on('scrollStart', function () {
20071                _indicatorsMap(function () {
20072                    this.fade(1);
20073                });
20074            });
20075
20076            this.on('beforeScrollStart', function () {
20077                _indicatorsMap(function () {
20078                    this.fade(1, true);
20079                });
20080            });
20081        }
20082
20083
20084        this.on('refresh', function () {
20085            _indicatorsMap(function () {
20086                this.refresh();
20087            });
20088        });
20089
20090        this.on('destroy', function () {
20091            _indicatorsMap(function () {
20092                this.destroy();
20093            });
20094
20095            delete this.indicators;
20096        });
20097    },
20098
20099    _initWheel: function () {
20100        utils.addEvent(this.wrapper, 'wheel', this);
20101        utils.addEvent(this.wrapper, 'mousewheel', this);
20102        utils.addEvent(this.wrapper, 'DOMMouseScroll', this);
20103
20104        this.on('destroy', function () {
20105            clearTimeout(this.wheelTimeout);
20106            this.wheelTimeout = null;
20107            utils.removeEvent(this.wrapper, 'wheel', this);
20108            utils.removeEvent(this.wrapper, 'mousewheel', this);
20109            utils.removeEvent(this.wrapper, 'DOMMouseScroll', this);
20110        });
20111    },
20112
20113    _wheel: function (e) {
20114        if ( !this.enabled ) {
20115            return;
20116        }
20117
20118        var wheelDeltaX, wheelDeltaY,
20119            newX, newY,
20120            that = this;
20121
20122        if ( this.wheelTimeout === undefined ) {
20123            that._execEvent('scrollStart');
20124        }
20125
20126        // Execute the scrollEnd event after 400ms the wheel stopped scrolling
20127        clearTimeout(this.wheelTimeout);
20128        this.wheelTimeout = setTimeout(function () {
20129            if(!that.options.snap) {
20130                that._execEvent('scrollEnd');
20131            }
20132            that.wheelTimeout = undefined;
20133        }, 400);
20134
20135        if ( 'deltaX' in e ) {
20136            if (e.deltaMode === 1) {
20137                wheelDeltaX = -e.deltaX * this.options.mouseWheelSpeed;
20138                wheelDeltaY = -e.deltaY * this.options.mouseWheelSpeed;
20139            } else {
20140                wheelDeltaX = -e.deltaX;
20141                wheelDeltaY = -e.deltaY;
20142            }
20143        } else if ( 'wheelDeltaX' in e ) {
20144            wheelDeltaX = e.wheelDeltaX / 120 * this.options.mouseWheelSpeed;
20145            wheelDeltaY = e.wheelDeltaY / 120 * this.options.mouseWheelSpeed;
20146        } else if ( 'wheelDelta' in e ) {
20147            wheelDeltaX = wheelDeltaY = e.wheelDelta / 120 * this.options.mouseWheelSpeed;
20148        } else if ( 'detail' in e ) {
20149            wheelDeltaX = wheelDeltaY = -e.detail / 3 * this.options.mouseWheelSpeed;
20150        } else {
20151            return;
20152        }
20153
20154        wheelDeltaX *= this.options.invertWheelDirection;
20155        wheelDeltaY *= this.options.invertWheelDirection;
20156
20157        if ( !this.hasVerticalScroll ) {
20158            wheelDeltaX = wheelDeltaY;
20159            wheelDeltaY = 0;
20160        }
20161
20162        if ( this.options.snap ) {
20163            newX = this.currentPage.pageX;
20164            newY = this.currentPage.pageY;
20165
20166            if ( wheelDeltaX > 0 ) {
20167                newX--;
20168            } else if ( wheelDeltaX < 0 ) {
20169                newX++;
20170            }
20171
20172            if ( wheelDeltaY > 0 ) {
20173                newY--;
20174            } else if ( wheelDeltaY < 0 ) {
20175                newY++;
20176            }
20177
20178            this.goToPage(newX, newY);
20179
20180            return;
20181        }
20182
20183        newX = this.x + Math.round(this.hasHorizontalScroll ? wheelDeltaX : 0);
20184        newY = this.y + Math.round(this.hasVerticalScroll ? wheelDeltaY : 0);
20185
20186        this.directionX = wheelDeltaX > 0 ? -1 : wheelDeltaX < 0 ? 1 : 0;
20187        this.directionY = wheelDeltaY > 0 ? -1 : wheelDeltaY < 0 ? 1 : 0;
20188
20189        if ( newX > 0 ) {
20190            newX = 0;
20191        } else if ( newX < this.maxScrollX ) {
20192            newX = this.maxScrollX;
20193        }
20194
20195        if ( newY > 0 ) {
20196            newY = 0;
20197        } else if ( newY < this.maxScrollY ) {
20198            newY = this.maxScrollY;
20199        }
20200
20201        this.scrollTo(newX, newY, 0);
20202
20203// INSERT POINT: _wheel
20204    },
20205
20206    _initSnap: function () {
20207        this.currentPage = {};
20208
20209        if ( typeof this.options.snap == 'string' ) {
20210            this.options.snap = this.scroller.querySelectorAll(this.options.snap);
20211        }
20212
20213        this.on('refresh', function () {
20214            var i = 0, l,
20215                m = 0, n,
20216                cx, cy,
20217                x = 0, y,
vendor: 7,339 bytes, lines 20218-20471
20218                stepX = this.options.snapStepX || this.wrapperWidth,
20219                stepY = this.options.snapStepY || this.wrapperHeight,
20220                el;
20221
20222            this.pages = [];
20223
20224            if ( !this.wrapperWidth || !this.wrapperHeight || !this.scrollerWidth || !this.scrollerHeight ) {
20225                return;
20226            }
20227
20228            if ( this.options.snap === true ) {
20229                cx = Math.round( stepX / 2 );
20230                cy = Math.round( stepY / 2 );
20231
20232                while ( x > -this.scrollerWidth ) {
20233                    this.pages[i] = [];
20234                    l = 0;
20235                    y = 0;
20236
20237                    while ( y > -this.scrollerHeight ) {
20238                        this.pages[i][l] = {
20239                            x: Math.max(x, this.maxScrollX),
20240                            y: Math.max(y, this.maxScrollY),
20241                            width: stepX,
20242                            height: stepY,
20243                            cx: x - cx,
20244                            cy: y - cy
20245                        };
20246
20247                        y -= stepY;
20248                        l++;
20249                    }
20250
20251                    x -= stepX;
20252                    i++;
20253                }
20254            } else {
20255                el = this.options.snap;
20256                l = el.length;
20257                n = -1;
20258
20259                for ( ; i < l; i++ ) {
20260                    if ( i === 0 || el[i].offsetLeft <= el[i-1].offsetLeft ) {
20261                        m = 0;
20262                        n++;
20263                    }
20264
20265                    if ( !this.pages[m] ) {
20266                        this.pages[m] = [];
20267                    }
20268
20269                    x = Math.max(-el[i].offsetLeft, this.maxScrollX);
20270                    y = Math.max(-el[i].offsetTop, this.maxScrollY);
20271                    cx = x - Math.round(el[i].offsetWidth / 2);
20272                    cy = y - Math.round(el[i].offsetHeight / 2);
20273
20274                    this.pages[m][n] = {
20275                        x: x,
20276                        y: y,
20277                        width: el[i].offsetWidth,
20278                        height: el[i].offsetHeight,
20279                        cx: cx,
20280                        cy: cy
20281                    };
20282
20283                    if ( x > this.maxScrollX ) {
20284                        m++;
20285                    }
20286                }
20287            }
20288
20289            this.goToPage(this.currentPage.pageX || 0, this.currentPage.pageY || 0, 0);
20290
20291            // Update snap threshold if needed
20292            if ( this.options.snapThreshold % 1 === 0 ) {
20293                this.snapThresholdX = this.options.snapThreshold;
20294                this.snapThresholdY = this.options.snapThreshold;
20295            } else {
20296                this.snapThresholdX = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].width * this.options.snapThreshold);
20297                this.snapThresholdY = Math.round(this.pages[this.currentPage.pageX][this.currentPage.pageY].height * this.options.snapThreshold);
20298            }
20299        });
20300
20301        this.on('flick', function () {
20302            var time = this.options.snapSpeed || Math.max(
20303                    Math.max(
20304                        Math.min(Math.abs(this.x - this.startX), 1000),
20305                        Math.min(Math.abs(this.y - this.startY), 1000)
20306                    ), 300);
20307
20308            this.goToPage(
20309                this.currentPage.pageX + this.directionX,
20310                this.currentPage.pageY + this.directionY,
20311                time
20312            );
20313        });
20314    },
20315
20316    _nearestSnap: function (x, y) {
20317        if ( !this.pages.length ) {
20318            return { x: 0, y: 0, pageX: 0, pageY: 0 };
20319        }
20320
20321        var i = 0,
20322            l = this.pages.length,
20323            m = 0;
20324
20325        // Check if we exceeded the snap threshold
20326        if ( Math.abs(x - this.absStartX) < this.snapThresholdX &&
20327            Math.abs(y - this.absStartY) < this.snapThresholdY ) {
20328            return this.currentPage;
20329        }
20330
20331        if ( x > 0 ) {
20332            x = 0;
20333        } else if ( x < this.maxScrollX ) {
20334            x = this.maxScrollX;
20335        }
20336
20337        if ( y > 0 ) {
20338            y = 0;
20339        } else if ( y < this.maxScrollY ) {
20340            y = this.maxScrollY;
20341        }
20342
20343        for ( ; i < l; i++ ) {
20344            if ( x >= this.pages[i][0].cx ) {
20345                x = this.pages[i][0].x;
20346                break;
20347            }
20348        }
20349
20350        l = this.pages[i].length;
20351
20352        for ( ; m < l; m++ ) {
20353            if ( y >= this.pages[0][m].cy ) {
20354                y = this.pages[0][m].y;
20355                break;
20356            }
20357        }
20358
20359        if ( i == this.currentPage.pageX ) {
20360            i += this.directionX;
20361
20362            if ( i < 0 ) {
20363                i = 0;
20364            } else if ( i >= this.pages.length ) {
20365                i = this.pages.length - 1;
20366            }
20367
20368            x = this.pages[i][0].x;
20369        }
20370
20371        if ( m == this.currentPage.pageY ) {
20372            m += this.directionY;
20373
20374            if ( m < 0 ) {
20375                m = 0;
20376            } else if ( m >= this.pages[0].length ) {
20377                m = this.pages[0].length - 1;
20378            }
20379
20380            y = this.pages[0][m].y;
20381        }
20382
20383        return {
20384            x: x,
20385            y: y,
20386            pageX: i,
20387            pageY: m
20388        };
20389    },
20390
20391    goToPage: function (x, y, time, easing) {
20392        easing = easing || this.options.bounceEasing;
20393
20394        if ( x >= this.pages.length ) {
20395            x = this.pages.length - 1;
20396        } else if ( x < 0 ) {
20397            x = 0;
20398        }
20399
20400        if ( y >= this.pages[x].length ) {
20401            y = this.pages[x].length - 1;
20402        } else if ( y < 0 ) {
20403            y = 0;
20404        }
20405
20406        var posX = this.pages[x][y].x,
20407            posY = this.pages[x][y].y;
20408
20409        time = time === undefined ? this.options.snapSpeed || Math.max(
20410            Math.max(
20411                Math.min(Math.abs(posX - this.x), 1000),
20412                Math.min(Math.abs(posY - this.y), 1000)
20413            ), 300) : time;
20414
20415        this.currentPage = {
20416            x: posX,
20417            y: posY,
20418            pageX: x,
20419            pageY: y
20420        };
20421
20422        this.scrollTo(posX, posY, time, easing);
20423    },
20424
20425    next: function (time, easing) {
20426        var x = this.currentPage.pageX,
20427            y = this.currentPage.pageY;
20428
20429        x++;
20430
20431        if ( x >= this.pages.length && this.hasVerticalScroll ) {
20432            x = 0;
20433            y++;
20434        }
20435
20436        this.goToPage(x, y, time, easing);
20437    },
20438
20439    prev: function (time, easing) {
20440        var x = this.currentPage.pageX,
20441            y = this.currentPage.pageY;
20442
20443        x--;
20444
20445        if ( x < 0 && this.hasVerticalScroll ) {
20446            x = 0;
20447            y--;
20448        }
20449
20450        this.goToPage(x, y, time, easing);
20451    },
20452
20453    _initKeys: function (e) {
20454        // default key bindings
20455        var keys = {
20456            pageUp: 33,
20457            pageDown: 34,
20458            end: 35,
20459            home: 36,
20460            left: 37,
20461            up: 38,
20462            right: 39,
20463            down: 40
20464        };
20465        var i;
20466
20467        // if you give me characters I give you keycode
20468        if ( typeof this.options.keyBindings == 'object' ) {
20469            for ( i in this.options.keyBindings ) {
20470                if ( typeof this.options.keyBindings[i] == 'string' ) {
20471                    this.options.keyBindings[i] = this.options.keyBindings[i].toUpperCase().charCo
vendor: 11,097 bytes, lines 20471-20824
20471deAt(0);
20472                }
20473            }
20474        } else {
20475            this.options.keyBindings = {};
20476        }
20477
20478        for ( i in keys ) {
20479            this.options.keyBindings[i] = this.options.keyBindings[i] || keys[i];
20480        }
20481
20482        utils.addEvent(window, 'keydown', this);
20483
20484        this.on('destroy', function () {
20485            utils.removeEvent(window, 'keydown', this);
20486        });
20487    },
20488
20489    _key: function (e) {
20490        if ( !this.enabled ) {
20491            return;
20492        }
20493
20494        var snap = this.options.snap,   // we are using this alot, better to cache it
20495            newX = snap ? this.currentPage.pageX : this.x,
20496            newY = snap ? this.currentPage.pageY : this.y,
20497            now = utils.getTime(),
20498            prevTime = this.keyTime || 0,
20499            acceleration = 0.250,
20500            pos;
20501
20502        if ( this.options.useTransition && this.isInTransition ) {
20503            pos = this.getComputedPosition();
20504
20505            this._translate(Math.round(pos.x), Math.round(pos.y));
20506            this.isInTransition = false;
20507        }
20508
20509        this.keyAcceleration = now - prevTime < 200 ? Math.min(this.keyAcceleration + acceleration, 50) : 0;
20510
20511        switch ( e.keyCode ) {
20512            case this.options.keyBindings.pageUp:
20513                if ( this.hasHorizontalScroll && !this.hasVerticalScroll ) {
20514                    newX += snap ? 1 : this.wrapperWidth;
20515                } else {
20516                    newY += snap ? 1 : this.wrapperHeight;
20517                }
20518                break;
20519            case this.options.keyBindings.pageDown:
20520                if ( this.hasHorizontalScroll && !this.hasVerticalScroll ) {
20521                    newX -= snap ? 1 : this.wrapperWidth;
20522                } else {
20523                    newY -= snap ? 1 : this.wrapperHeight;
20524                }
20525                break;
20526            case this.options.keyBindings.end:
20527                newX = snap ? this.pages.length-1 : this.maxScrollX;
20528                newY = snap ? this.pages[0].length-1 : this.maxScrollY;
20529                break;
20530            case this.options.keyBindings.home:
20531                newX = 0;
20532                newY = 0;
20533                break;
20534            case this.options.keyBindings.left:
20535                newX += snap ? -1 : 5 + this.keyAcceleration>>0;
20536                break;
20537            case this.options.keyBindings.up:
20538                newY += snap ? 1 : 5 + this.keyAcceleration>>0;
20539                break;
20540            case this.options.keyBindings.right:
20541                newX -= snap ? -1 : 5 + this.keyAcceleration>>0;
20542                break;
20543            case this.options.keyBindings.down:
20544                newY -= snap ? 1 : 5 + this.keyAcceleration>>0;
20545                break;
20546            default:
20547                return;
20548        }
20549
20550        if ( snap ) {
20551            this.goToPage(newX, newY);
20552            return;
20553        }
20554
20555        if ( newX > 0 ) {
20556            newX = 0;
20557            this.keyAcceleration = 0;
20558        } else if ( newX < this.maxScrollX ) {
20559            newX = this.maxScrollX;
20560            this.keyAcceleration = 0;
20561        }
20562
20563        if ( newY > 0 ) {
20564            newY = 0;
20565            this.keyAcceleration = 0;
20566        } else if ( newY < this.maxScrollY ) {
20567            newY = this.maxScrollY;
20568            this.keyAcceleration = 0;
20569        }
20570
20571        this.scrollTo(newX, newY, 0);
20572
20573        this.keyTime = now;
20574    },
20575
20576    _animate: function (destX, destY, duration, easingFn) {
20577        var that = this,
20578            startX = this.x,
20579            startY = this.y,
20580            startTime = utils.getTime(),
20581            destTime = startTime + duration;
20582
20583        function step () {
20584            var now = utils.getTime(),
20585                newX, newY,
20586                easing;
20587
20588            if ( now >= destTime ) {
20589                that.isAnimating = false;
20590                that._translate(destX, destY);
20591
20592                if ( !that.resetPosition(that.options.bounceTime) ) {
20593                    that._execEvent('scrollEnd');
20594                }
20595
20596                return;
20597            }
20598
20599            now = ( now - startTime ) / duration;
20600            easing = easingFn(now);
20601            newX = ( destX - startX ) * easing + startX;
20602            newY = ( destY - startY ) * easing + startY;
20603            that._translate(newX, newY);
20604
20605            if ( that.isAnimating ) {
20606                rAF(step);
20607            }
20608        }
20609
20610        this.isAnimating = true;
20611        step();
20612    },
20613    handleEvent: function (e) {
20614        switch ( e.type ) {
20615            case 'touchstart':
20616            case 'pointerdown':
20617            case 'MSPointerDown':
20618            case 'mousedown':
20619                this._start(e);
20620                break;
20621            case 'touchmove':
20622            case 'pointermove':
20623            case 'MSPointerMove':
20624            case 'mousemove':
20625                this._move(e);
20626                break;
20627            case 'touchend':
20628            case 'pointerup':
20629            case 'MSPointerUp':
20630            case 'mouseup':
20631            case 'touchcancel':
20632            case 'pointercancel':
20633            case 'MSPointerCancel':
20634            case 'mousecancel':
20635                this._end(e);
20636                break;
20637            case 'orientationchange':
20638            case 'resize':
20639                this._resize();
20640                break;
20641            case 'transitionend':
20642            case 'webkitTransitionEnd':
20643            case 'oTransitionEnd':
20644            case 'MSTransitionEnd':
20645                this._transitionEnd(e);
20646                break;
20647            case 'wheel':
20648            case 'DOMMouseScroll':
20649            case 'mousewheel':
20650                this._wheel(e);
20651                break;
20652            case 'keydown':
20653                this._key(e);
20654                break;
20655            case 'click':
20656                if ( this.enabled && !e._constructed ) {
20657                    e.preventDefault();
20658                    e.stopPropagation();
20659                }
20660                break;
20661        }
20662    }
20663};
20664function createDefaultScrollbar (direction, interactive, type) {
20665    var scrollbar = document.createElement('div'),
20666        indicator = document.createElement('div');
20667
20668    if ( type === true ) {
20669        scrollbar.style.cssText = 'position:absolute;z-index:9999';
20670        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';
20671    }
20672
20673    indicator.className = 'iScrollIndicator';
20674
20675    if ( direction == 'h' ) {
20676        if ( type === true ) {
20677            scrollbar.style.cssText += ';height:7px;left:2px;right:2px;bottom:0';
20678            indicator.style.height = '100%';
20679        }
20680        scrollbar.className = 'iScrollHorizontalScrollbar';
20681    } else {
20682        if ( type === true ) {
20683            scrollbar.style.cssText += ';width:7px;bottom:2px;top:2px;right:1px';
20684            indicator.style.width = '100%';
20685        }
20686        scrollbar.className = 'iScrollVerticalScrollbar';
20687    }
20688
20689    scrollbar.style.cssText += ';overflow:hidden';
20690
20691    if ( !interactive ) {
20692        scrollbar.style.pointerEvents = 'none';
20693    }
20694
20695    scrollbar.appendChild(indicator);
20696
20697    return scrollbar;
20698}
20699
20700function Indicator (scroller, options) {
20701    this.wrapper = typeof options.el == 'string' ? document.querySelector(options.el) : options.el;
20702    this.wrapperStyle = this.wrapper.style;
20703    this.indicator = this.wrapper.children[0];
20704    this.indicatorStyle = this.indicator.style;
20705    this.scroller = scroller;
20706
20707    this.options = {
20708        listenX: true,
20709        listenY: true,
20710        interactive: false,
20711        resize: true,
20712        defaultScrollbars: false,
20713        shrink: false,
20714        fade: false,
20715        speedRatioX: 0,
20716        speedRatioY: 0
20717    };
20718
20719    for ( var i in options ) {
20720        this.options[i] = options[i];
20721    }
20722
20723    this.sizeRatioX = 1;
20724    this.sizeRatioY = 1;
20725    this.maxPosX = 0;
20726    this.maxPosY = 0;
20727
20728    if ( this.options.interactive ) {
20729        if ( !this.options.disableTouch ) {
20730            utils.addEvent(this.indicator, 'touchstart', this);
20731            utils.addEvent(window, 'touchend', this);
20732        }
20733        if ( !this.options.disablePointer ) {
20734            utils.addEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this);
20735            utils.addEvent(window, utils.prefixPointerEvent('pointerup'), this);
20736        }
20737        if ( !this.options.disableMouse ) {
20738            utils.addEvent(this.indicator, 'mousedown', this);
20739            utils.addEvent(window, 'mouseup', this);
20740        }
20741    }
20742
20743    if ( this.options.fade ) {
20744        this.wrapperStyle[utils.style.transform] = this.scroller.translateZ;
20745        var durationProp = utils.style.transitionDuration;
20746        if(!durationProp) {
20747            return;
20748        }
20749        this.wrapperStyle[durationProp] = utils.isBadAndroid ? '0.0001ms' : '0ms';
20750        // remove 0.0001ms
20751        var self = this;
20752        if(utils.isBadAndroid) {
20753            rAF(function() {
20754                if(self.wrapperStyle[durationProp] === '0.0001ms') {
20755                    self.wrapperStyle[durationProp] = '0s';
20756                }
20757            });
20758        }
20759        this.wrapperStyle.opacity = '0';
20760    }
20761}
20762
20763Indicator.prototype = {
20764    handleEvent: function (e) {
20765        switch ( e.type ) {
20766            case 'touchstart':
20767            case 'pointerdown':
20768            case 'MSPointerDown':
20769            case 'mousedown':
20770                this._start(e);
20771                break;
20772            case 'touchmove':
20773            case 'pointermove':
20774            case 'MSPointerMove':
20775            case 'mousemove':
20776                this._move(e);
20777                break;
20778            case 'touchend':
20779            case 'pointerup':
20780            case 'MSPointerUp':
20781            case 'mouseup':
20782            case 'touchcancel':
20783            case 'pointercancel':
20784            case 'MSPointerCancel':
20785            case 'mousecancel':
20786                this._end(e);
20787                break;
20788        }
20789    },
20790
20791    destroy: function () {
20792        if ( this.options.fadeScrollbars ) {
20793            clearTimeout(this.fadeTimeout);
20794            this.fadeTimeout = null;
20795        }
20796        if ( this.options.interactive ) {
20797            utils.removeEvent(this.indicator, 'touchstart', this);
20798            utils.removeEvent(this.indicator, utils.prefixPointerEvent('pointerdown'), this);
20799            utils.removeEvent(this.indicator, 'mousedown', this);
20800
20801            utils.removeEvent(window, 'touchmove', this);
20802            utils.removeEvent(window, utils.prefixPointerEvent('pointermove'), this);
20803            utils.removeEvent(window, 'mousemove', this);
20804
20805            utils.removeEvent(window, 'touchend', this);
20806            utils.removeEvent(window, utils.prefixPointerEvent('pointerup'), this);
20807            utils.removeEvent(window, 'mouseup', this);
20808        }
20809
20810        if ( this.options.defaultScrollbars ) {
20811            this.wrapper.parentNode.removeChild(this.wrapper);
20812        }
20813    },
20814
20815    _start: function (e) {
20816        var point = e.touches ? e.touches[0] : e;
20817
20818        e.preventDefault();
20819        e.stopPropagation();
20820
20821        this.transitionTime();
20822
20823        this.initiated = true;
20824        this.moved = false;
vendor: 9,615 bytes, lines 20825-21083
20825        this.lastPointX = point.pageX;
20826        this.lastPointY = point.pageY;
20827
20828        this.startTime  = utils.getTime();
20829
20830        if ( !this.options.disableTouch ) {
20831            utils.addEvent(window, 'touchmove', this);
20832        }
20833        if ( !this.options.disablePointer ) {
20834            utils.addEvent(window, utils.prefixPointerEvent('pointermove'), this);
20835        }
20836        if ( !this.options.disableMouse ) {
20837            utils.addEvent(window, 'mousemove', this);
20838        }
20839
20840        this.scroller._execEvent('beforeScrollStart');
20841    },
20842
20843    _move: function (e) {
20844        var point = e.touches ? e.touches[0] : e,
20845            deltaX, deltaY,
20846            newX, newY,
20847            timestamp = utils.getTime();
20848
20849        if ( !this.moved ) {
20850            this.scroller._execEvent('scrollStart');
20851        }
20852
20853        this.moved = true;
20854
20855        deltaX = point.pageX - this.lastPointX;
20856        this.lastPointX = point.pageX;
20857
20858        deltaY = point.pageY - this.lastPointY;
20859        this.lastPointY = point.pageY;
20860
20861        newX = this.x + deltaX;
20862        newY = this.y + deltaY;
20863
20864        this._pos(newX, newY);
20865
20866// INSERT POINT: indicator._move
20867
20868        e.preventDefault();
20869        e.stopPropagation();
20870    },
20871
20872    _end: function (e) {
20873        if ( !this.initiated ) {
20874            return;
20875        }
20876
20877        this.initiated = false;
20878
20879        e.preventDefault();
20880        e.stopPropagation();
20881
20882        utils.removeEvent(window, 'touchmove', this);
20883        utils.removeEvent(window, utils.prefixPointerEvent('pointermove'), this);
20884        utils.removeEvent(window, 'mousemove', this);
20885
20886        if ( this.scroller.options.snap ) {
20887            var snap = this.scroller._nearestSnap(this.scroller.x, this.scroller.y);
20888
20889            var time = this.options.snapSpeed || Math.max(
20890                    Math.max(
20891                        Math.min(Math.abs(this.scroller.x - snap.x), 1000),
20892                        Math.min(Math.abs(this.scroller.y - snap.y), 1000)
20893                    ), 300);
20894
20895            if ( this.scroller.x != snap.x || this.scroller.y != snap.y ) {
20896                this.scroller.directionX = 0;
20897                this.scroller.directionY = 0;
20898                this.scroller.currentPage = snap;
20899                this.scroller.scrollTo(snap.x, snap.y, time, this.scroller.options.bounceEasing);
20900            }
20901        }
20902
20903        if ( this.moved ) {
20904            this.scroller._execEvent('scrollEnd');
20905        }
20906    },
20907
20908    transitionTime: function (time) {
20909        time = time || 0;
20910        var durationProp = utils.style.transitionDuration;
20911        if(!durationProp) {
20912            return;
20913        }
20914
20915        this.indicatorStyle[durationProp] = time + 'ms';
20916
20917        if ( !time && utils.isBadAndroid ) {
20918            this.indicatorStyle[durationProp] = '0.0001ms';
20919            // remove 0.0001ms
20920            var self = this;
20921            rAF(function() {
20922                if(self.indicatorStyle[durationProp] === '0.0001ms') {
20923                    self.indicatorStyle[durationProp] = '0s';
20924                }
20925            });
20926        }
20927    },
20928
20929    transitionTimingFunction: function (easing) {
20930        this.indicatorStyle[utils.style.transitionTimingFunction] = easing;
20931    },
20932
20933    refresh: function () {
20934        this.transitionTime();
20935
20936        if ( this.options.listenX && !this.options.listenY ) {
20937            this.indicatorStyle.display = this.scroller.hasHorizontalScroll ? 'block' : 'none';
20938        } else if ( this.options.listenY && !this.options.listenX ) {
20939            this.indicatorStyle.display = this.scroller.hasVerticalScroll ? 'block' : 'none';
20940        } else {
20941            this.indicatorStyle.display = this.scroller.hasHorizontalScroll || this.scroller.hasVerticalScroll ? 'block' : 'none';
20942        }
20943
20944        if ( this.scroller.hasHorizontalScroll && this.scroller.hasVerticalScroll ) {
20945            utils.addClass(this.wrapper, 'iScrollBothScrollbars');
20946            utils.removeClass(this.wrapper, 'iScrollLoneScrollbar');
20947
20948            if ( this.options.defaultScrollbars && this.options.customStyle ) {
20949                if ( this.options.listenX ) {
20950                    this.wrapper.style.right = '8px';
20951                } else {
20952                    this.wrapper.style.bottom = '8px';
20953                }
20954            }
20955        } else {
20956            utils.removeClass(this.wrapper, 'iScrollBothScrollbars');
20957            utils.addClass(this.wrapper, 'iScrollLoneScrollbar');
20958
20959            if ( this.options.defaultScrollbars && this.options.customStyle ) {
20960                if ( this.options.listenX ) {
20961                    this.wrapper.style.right = '2px';
20962                } else {
20963                    this.wrapper.style.bottom = '2px';
20964                }
20965            }
20966        }
20967
20968        var r = this.wrapper.offsetHeight;  // force refresh
20969
20970        if ( this.options.listenX ) {
20971            this.wrapperWidth = this.wrapper.clientWidth;
20972            if ( this.options.resize ) {
20973                this.indicatorWidth = Math.max(Math.round(this.wrapperWidth * this.wrapperWidth / (this.scroller.scrollerWidth || this.wrapperWidth || 1)), 8);
20974                this.indicatorStyle.width = this.indicatorWidth + 'px';
20975            } else {
20976                this.indicatorWidth = this.indicator.clientWidth;
20977            }
20978
20979            this.maxPosX = this.wrapperWidth - this.indicatorWidth;
20980
20981            if ( this.options.shrink == 'clip' ) {
20982                this.minBoundaryX = -this.indicatorWidth + 8;
20983                this.maxBoundaryX = this.wrapperWidth - 8;
20984            } else {
20985                this.minBoundaryX = 0;
20986                this.maxBoundaryX = this.maxPosX;
20987            }
20988
20989            this.sizeRatioX = this.options.speedRatioX || (this.scroller.maxScrollX && (this.maxPosX / this.scroller.maxScrollX));
20990        }
20991
20992        if ( this.options.listenY ) {
20993            this.wrapperHeight = this.wrapper.clientHeight;
20994            if ( this.options.resize ) {
20995                this.indicatorHeight = Math.max(Math.round(this.wrapperHeight * this.wrapperHeight / (this.scroller.scrollerHeight || this.wrapperHeight || 1)), 8);
20996                this.indicatorStyle.height = this.indicatorHeight + 'px';
20997            } else {
20998                this.indicatorHeight = this.indicator.clientHeight;
20999            }
21000
21001            this.maxPosY = this.wrapperHeight - this.indicatorHeight;
21002
21003            if ( this.options.shrink == 'clip' ) {
21004                this.minBoundaryY = -this.indicatorHeight + 8;
21005                this.maxBoundaryY = this.wrapperHeight - 8;
21006            } else {
21007                this.minBoundaryY = 0;
21008                this.maxBoundaryY = this.maxPosY;
21009            }
21010
21011            this.maxPosY = this.wrapperHeight - this.indicatorHeight;
21012            this.sizeRatioY = this.options.speedRatioY || (this.scroller.maxScrollY && (this.maxPosY / this.scroller.maxScrollY));
21013        }
21014
21015        this.updatePosition();
21016    },
21017
21018    updatePosition: function () {
21019        var x = this.options.listenX && Math.round(this.sizeRatioX * this.scroller.x) || 0,
21020            y = this.options.listenY && Math.round(this.sizeRatioY * this.scroller.y) || 0;
21021
21022        if ( !this.options.ignoreBoundaries ) {
21023            if ( x < this.minBoundaryX ) {
21024                if ( this.options.shrink == 'scale' ) {
21025                    this.width = Math.max(this.indicatorWidth + x, 8);
21026                    this.indicatorStyle.width = this.width + 'px';
21027                }
21028                x = this.minBoundaryX;
21029            } else if ( x > this.maxBoundaryX ) {
21030                if ( this.options.shrink == 'scale' ) {
21031                    this.width = Math.max(this.indicatorWidth - (x - this.maxPosX), 8);
21032                    this.indicatorStyle.width = this.width + 'px';
21033                    x = this.maxPosX + this.indicatorWidth - this.width;
21034                } else {
21035                    x = this.maxBoundaryX;
21036                }
21037            } else if ( this.options.shrink == 'scale' && this.width != this.indicatorWidth ) {
21038                this.width = this.indicatorWidth;
21039                this.indicatorStyle.width = this.width + 'px';
21040            }
21041
21042            if ( y < this.minBoundaryY ) {
21043                if ( this.options.shrink == 'scale' ) {
21044                    this.height = Math.max(this.indicatorHeight + y * 3, 8);
21045                    this.indicatorStyle.height = this.height + 'px';
21046                }
21047                y = this.minBoundaryY;
21048            } else if ( y > this.maxBoundaryY ) {
21049                if ( this.options.shrink == 'scale' ) {
21050                    this.height = Math.max(this.indicatorHeight - (y - this.maxPosY) * 3, 8);
21051                    this.indicatorStyle.height = this.height + 'px';
21052                    y = this.maxPosY + this.indicatorHeight - this.height;
21053                } else {
21054                    y = this.maxBoundaryY;
21055                }
21056            } else if ( this.options.shrink == 'scale' && this.height != this.indicatorHeight ) {
21057                this.height = this.indicatorHeight;
21058                this.indicatorStyle.height = this.height + 'px';
21059            }
21060        }
21061
21062        this.x = x;
21063        this.y = y;
21064
21065        if ( this.scroller.options.useTransform ) {
21066            this.indicatorStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.scroller.translateZ;
21067        } else {
21068            this.indicatorStyle.left = x + 'px';
21069            this.indicatorStyle.top = y + 'px';
21070        }
21071    },
21072
21073    _pos: function (x, y) {
21074        if ( x < 0 ) {
21075            x = 0;
21076        } else if ( x > this.maxPosX ) {
21077            x = this.maxPosX;
21078        }
21079
21080        if ( y < 0 ) {
21081            y = 0;
21082        } else if ( y > this.maxPosY ) {
21083            y = this.maxPosY;
21084        }
21085
21086        x = this.options.listenX ? Math.round(x / this.sizeRatioX) : this.scroller.x;
21087        y = this.options.listenY ? Math.round(y / this.sizeRatioY) : this.scroller.y;
21088
21089        this.scroller.scrollTo(x, y);
21090    },
21091
21092    fade: function (val, hold) {
21093        if ( hold && !this.visible ) {
21094            return;
21095        }
21096
21097        clearTimeout(this.fadeTimeout);
21098        this.fadeTimeout = null;
21099
21100        var time = val ? 250 : 500,
21101            delay = val ? 0 : 300;
21102
21103        val = val ? '1' : '0';
21104
21105        this.wrapperStyle[utils.style.transitionDuration] = time + 'ms';
21106
21107        this.fadeTimeout = setTimeout((function (val) {
21108            this.wrapperStyle.opacity = val;
21109            this.visible = +val;
21110        }).bind(this, val), delay);
21111    }
21112};
21113
21114IScroll.utils = utils;
21115
21116if ( typeof module != 'undefined' && module.exports ) {
21117    module.exports = IScroll;
21118} else if ( typeof define == 'function' && define.amd ) {
21119        define( function () { return IScroll; } );
21120} else {
21121    window.IScroll = IScroll;
21122}
21123
21124})(window, document, Math);
21125/*!
21126 * fullPage 2.9.4
21127 * https://github.com/alvarotrigo/fullPage.js
21128 * @license MIT licensed
21129 *
21130 * Copyright (C) 2015 alvarotrigo.com - A project by Alvaro Trigo
21131 */
21132(function(global, factory) {
21133    'use strict';
21134    if (typeof define === 'function' && define.amd) {
21135        define(['jquery'], function($) {
21136          return factory($, global, global.document, global.Math);
21137        });
21138    } else if (typeof exports === "object" && exports) {
21139        module.exports = factory(require('jquery'), global, global.document, global.Math);
21140    } else {
21141        factory(jQuery, global, global.document, global.Math);
21142    }
21143})(typeof window !== 'undefined' ? window : this, function($, window, document, Math, undefined) {
21144    'use strict';
21145
21146    // keeping central set of classnames and selectors
21147    var WRAPPER =               'fullpage-wrapper';
21148    var WRAPPER_SEL =           '.' + WRAPPER;
21149
21150    // slimscroll
21151    var SCROLLABLE =            'fp-scrollable';
21152    var SCROLLABLE_SEL =        '.' + SCROLLABLE;
21153
21154    // util
21155    var RESPONSIVE =            'fp-responsive';
21156    var NO_TRANSITION =         'fp-notransition';
21157    var DESTROYED =             'fp-destroyed';
21158    var ENABLED =               'fp-enabled';
21159    var VIEWING_PREFIX =        'fp-viewing';
21160    var ACTIVE =                'active';
21161    var ACTIVE_SEL =            '.' + ACTIVE;
21162    var COMPLETELY =            'fp-completely';
21163    var COMPLETELY_SEL =        '.' + COMPLETELY;
21164
21165    // section
21166    var SECTION_DEFAULT_SEL =   '.section';
21167    var SECTION =               'fp-section';
21168    var SECTION_SEL =           '.' + SECTION;
21169    var SECTION_ACTIVE_SEL =    SECTION_SEL + ACTIVE_SEL;
21170    var SECTION_FIRST_SEL =     SECTION_SEL + ':first';
21171    var SECTION_LAST_SEL =      SECTION_SEL + ':last';
21172    var TABLE_CELL =            'fp-tableCell';
21173    var TABLE_CELL_SEL =        '.' + TABLE_CELL;
21174    var AUTO_HEIGHT =           'fp-auto-height';
21175    var AUTO_HEIGHT_SEL =       '.fp-auto-height';
21176    var NORMAL_SCROLL =         'fp-normal-scroll';
21177    var NORMAL_SCROLL_SEL =     '.fp-normal-scroll';
21178
21179    // section nav
21180    var SECTION_NAV =           'fp-nav';
21181    var SECTION_NAV_SEL =       '#' + SECTION_NAV;
21182    var SECTION_NAV_TOOLTIP =   'fp-tooltip';
21183    var SECTION_NAV_TOOLTIP_SEL='.'+SECTION_NAV_TOOLTIP;
21184    var SHOW_ACTIVE_TOOLTIP =   'fp-show-active';
21185
21186    // slide
21187    var SLIDE_DEFAULT_SEL =     '.slide';
21188    var SLIDE =                 'fp-slide';
21189    var SLIDE_SEL =             '.' + SLIDE;
21190    var SLIDE_ACTIVE_SEL =      SLIDE_SEL + ACTIVE_SEL;
21191    var SLIDES_WRAPPER =        'fp-slides';
21192    var SLIDES_WRAPPER_SEL =    '.' + SLIDES_WRAPPER;
21193    var SLIDES_CONTAINER =      'fp-slidesContainer';
21194    var SLIDES_CONTAINER_SEL =  '.' + SLIDES_CONTAINER;
21195    var TABLE =                 'fp-table';
21196
21197    // slide nav
21198    var SLIDES_NAV =            'fp-slidesNav';
21199    var SLIDES_NAV_SEL =        '.' + SLIDES_NAV;
21200    var SLIDES_NAV_LINK_SEL =   SLIDES_NAV_SEL + ' a';
21201    var SLIDES_ARROW =          'fp-controlArrow';
21202    var SLIDES_ARROW_SEL =      '.' + SLIDES_ARROW;
21203    var SLIDES_PREV =           'fp-prev';
21204    var SLIDES_PREV_SEL =       '.' + SLIDES_PREV;
21205    var SLIDES_ARROW_PREV =     SLIDES_ARROW + ' ' + SLIDES_PREV;
21206    var SLIDES_ARROW_PREV_SEL = SLIDES_ARROW_SEL + SLIDES_PREV_SEL;
21207    var SLIDES_NEXT =           'fp-next';
21208    var SLIDES_NEXT_SEL =       '.' + SLIDES_NEXT;
21209    var SLIDES_ARROW_NEXT =     SLIDES_ARROW + ' ' + SLIDES_NEXT;
21210    var SLIDES_ARROW_NEXT_SEL = SLIDES_ARROW_SEL + SLIDES_NEXT_SEL;
21211
21212    var $window = $(window);
21213    var $document = $(document);
21214
21215    // Default options for iScroll.js used when using scrollOverflow
21216    var iscrollOptions = {
21217        scrollbars: true,
21218        mouseWheel: true,
21219        hideScrollbars: false,
21220        fadeScrollbars: false,
21221        disableMouse: true,
21222        interactiveScrollbars: true,
21223        bounce: false,
21224    };
21225
21226    $.fn.fullpage = function(options) {
21227        //only once my friend!
21228        if($('html').hasClass(ENABLED)){ displayWarnings(); return; }
21229
21230        // common jQuery objects
21231        var $htmlBody = $('html, body');
21232        var $body = $('body');
21233
21234        var FP = $.fn.fullpage;
21235
21236        // Creating some defaults, extending them with any options that were provided
21237        options = $.extend({
21238            //navigation
21239            menu: false,
21240            anchors:[],
21241            lockAnchors: false,
21242            navigation: false,
21243            navigationPosition: 'right',
21244            navigationTooltips: [],
21245            showActiveTooltip: false,
21246            slidesNavigation: false,
21247            slidesNavPosition: 'bottom',
21248            scrollBar: false,
21249            hybrid: false,
21250
21251            //scrolling
21252            css3: true,
21253            scrollingSpeed: 700,
21254            autoScrolling: true,
21255            fitToSection: true,
21256            fitToSectionDelay: 1000,
21257            easing: 'easeInOutCubic',
21258            easingcss3: 'ease',
21259            loopBottom: false,
21260            loopTop: false,
21261            loopHorizontal: true,
21262            continuousVertical: false,
21263            continuousHorizontal: false,
21264            scrollHorizontally: false,
21265            interlockedSlides: false,
21266            dragAndMove: false,
21267            offsetSections: false,
21268            resetSliders: false,
21269            fadingEffect: false,
21270            normalScrollElements: null,
21271            scrollOverflow: false,
21272            scrollOverflowReset: false,
21273            scrollOverflowHandler: iscrollHandler,
21274            scrollOverflowOptions: null,
21275            touchSensitivity: 5,
21276            normalScrollElementTouchThreshold: 5,
21277            bigSectionsDestination: null,
21278
21279            //Accessibility
21280            keyboardScrolling: true,
21281            animateAnchor: true,
21282            recordHistory: true,
21283
21284            //design
21285            controlArrows: true,
21286            controlArrowColor: '#fff',
21287            verticalCentered: true,
21288            sectionsColor : [],
21289            paddingTop: 0,
21290            paddingBottom: 0,
21291            fixedElements: null,
21292            responsive: 0, //backwards compabitility with responsiveWiddth
21293            responsiveWidth: 0,
21294            responsiveHeight: 0,
21295            responsiveSlides: false,
21296            parallax: false,
21297            parallaxOptions: {
21298                type: 'reveal',
21299                percentage: 62,
21300                property: 'translate'
21301            },
21302
21303            //Custom selectors
21304            sectionSelector: SECTION_DEFAULT_SEL,
21305            slideSelector: SLIDE_DEFAULT_SEL,
21306
21307            //events
21308            afterLoad: null,
21309            onLeave: null,
21310            afterRender: null,
21311            afterResize: null,
21312            afterReBuild: null,
21313            afterSlideLoad: null,
21314            onSlideLeave: null,
21315            afterResponsive: null,
21316
21317            lazyLoading: true
21318        }, options);
21319
21320        //flag to avoid very fast sliding for landscape sliders
21321        var slideMoving = false;
21322
21323        var isTouchDevice = navigator.userAgent.match(/(iPhone|iPod|iPad|Android|playbook|silk|BlackBerry|BB10|Windows Phone|Tizen|Bada|webOS|IEMobile|Opera Mini)/);
21324        var isTouch = (('ontouchstart' in window) || (navigator.msMaxTouchPoints > 0) || (navigator.maxTouchPoints));
21325        var container = $(this);
21326        var windowsHeight = $window.height();
21327        var isResizing = false;
21328        var isWindowFocused = true;
21329        var lastScrolledDestiny;
21330        var lastScrolledSlide;
21331        var canScroll = true;
21332        var scrollings = [];
21333        var controlPressed;
21334        var startingSection;
21335        var isScrollAllowed = {};
21336        isScrollAllowed.m = {  'up':true, 'down':true, 'left':true, 'right':true };
21337        isScrollAllowed.k = $.extend(true,{}, isScrollAllowed.m);
21338        var MSPointer = getMSPointer();
21339        var events = {
21340            touchmove: 'ontouchmove' in window ? 'touchmove' :  MSPointer.move,
21341            touchstart: 'ontouchstart' in window ? 'touchstart' :  MSPointer.down
21342        };
21343
21344        //cheks for passive event support
21345        //added by Uncode from new fullPage updates
21346        var g_supportsPassive = false;
21347        try {
21348          var opts = Object.defineProperty({}, 'passive', {
21349            get: function() {
21350              g_supportsPassive = true;
21351            }
21352          });
21353          window.addEventListener("testPassive", null, opts);
21354          window.removeEventListener("testPassive", null, opts);
21355        } catch (e) {}
21356
21357        //timeouts
21358        var resizeId;
21359        var afterSectionLoadsId;
21360        var afterSlideLoadsId;
21361        var scrollId;
21362        var scrollId2;
21363        var keydownId;
21364        var originals = $.extend(true, {}, options); //deep copy
21365
21366        //Uncode addition
21367        var $masthead = $('#masthead');
21368        var hideMenu = !$('body').hasClass('vmenu') && $('body').hasClass('uncode-fp-menu-hide') ? true : false;
21369        var menuHeight = $masthead.hasClass('menu-transparent') || hideMenu ? 0 : UNCODE.menuHeight;
21370        var bodyBorder = UNCODE.bodyBorder;
21371        var adminBarHeight = UNCODE.adminBarHeight;
21372
21373        displayWarnings();
21374
21375        //fixing bug in iScroll with links: https://github.com/cubiq/iscroll/issues/783
21376        iscrollOptions.click = isTouch; // see #2035
21377
21378        //extending iScroll options with the user custom ones
21379        iscrollOptions = $.extend(iscrollOptions, options.scrollOverflowOptions);
21380
21381        //easeInOutCubic animation included in the plugin
21382        $.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;}});
21383
21384        /**
21385        * Sets the autoScroll option.
21386        * It changes the scroll bar visibility and the history of the site as a result.
21387        */
21388        function setAutoScrolling(value, type){
21389            //removing the transformation
21390            if(!value){
21391                silentScroll(0);
21392            }
21393
21394            setVariableState('autoScrolling', value, type);
21395
21396            var element = $(SECTION_ACTIVE_SEL);
21397
21398            if(options.autoScrolling && !options.scrollBar){
21399                $htmlBody.css({
21400                    'overflow' : 'hidden !important',
21401                    'height' : '100%'
21402                });
21403
21404                //Uncode addition
21405                //setRecordHistory(originals.recordHistory, 'internal');
21406
21407                //for IE touch devices
21408                container.css({
21409                    '-ms-touch-action': 'none',
21410                    'touch-action': 'none'
21411                });
21412
21413                if(element.length){
21414                    //moving the container up
21415                    silentScroll(element.position().top);
21416                }
21417
21418            }else{
21419                $htmlBody.css({
21420                    'overflow' : 'visible !important',
21421                    'height' : 'initial'
21422                });
21423
21424                //Uncode addition
21425                //setRecordHistory(false, 'internal');
21426
21427                //for IE touch devices
21428                container.css({
21429                    '-ms-touch-action': '',
21430                    'touch-action': ''
21431                });
21432
21433                //scrolling the page to the section with no animation
21434                if (element.length) {
21435                    $htmlBody.scrollTop(element.position().top);
21436                }
21437            }
21438        }
21439
21440        /**
21441        * Defines wheter to record the history for each hash change in the URL.
21442        */
21443        function setRecordHistory(value, type){
21444            setVariableState('recordHistory', value, type);
21445        }
21446
21447        /**
21448        * Defines the scrolling speed
21449        */
21450        function setScrollingSpeed(value, type){
21451            setVariableState('scrollingSpeed', value, type);
21452        }
21453
21454        /**
21455        * Sets fitToSection
21456        */
21457        function setFitToSection(value, type){
21458            setVariableState('fitToSection', value, type);
21459        }
21460
21461        /**
21462        * Sets lockAnchors
21463        */
21464        function setLockAnchors(value){
21465            options.lockAnchors = value;
21466        }
21467
21468        /**
21469        * Adds or remove the possiblity of scrolling through sections by using the mouse wheel or the trackpad.
21470        */
21471        function setMouseWheelScrolling(value){
21472            if(value){
21473                addMouseWheelHandler();
21474                addMiddleWheelHandler();
21475            }else{
21476                removeMouseWheelHandler();
21477                removeMiddleWheelHandler();
21478            }
21479        }
21480
21481        /**
21482        * Adds or remove the possibility of scrolling through sections by using the mouse wheel/trackpad or touch gestures.
21483        * Optionally a second parameter can be used to specify the direction for which the action will be applied.
21484        *
21485        * @param directions string containing the direction or directions separated by comma.
21486        */
21487        function setAllowScrolling(value, directions){
21488            if(typeof directions !== 'undefined'){
21489                directions = directions.replace(/ /g,'').split(',');
21490
21491                $.each(directions, function (index, direction){
21492                    setIsScrollAllowed(value, direction, 'm');
21493                });
21494            }
21495            else if(value){
21496                setMouseWheelScrolling(true);
21497                addTouchHandler();
21498            }else{
21499                setMouseWheelScrolling(false);
21500                removeTouchHandler();
21501            }
21502        }
21503
21504        /**
21505        * Adds or remove the possibility of scrolling through sections by using the keyboard arrow keys
21506        */
21507        function setKeyboardScrolling(value, directions){
21508            if(typeof directions !== 'undefined'){
21509                directions = directions.replace(/ /g,'').split(',');
21510
21511                $.each(directions, function (index, direction){
21512                    setIsScrollAllowed(value, direction, 'k');
21513                });
21514            }else{
21515                options.keyboardScrolling = value;
21516            }
21517        }
21518
21519        /**
21520        * Moves the page up one section.
21521        */
21522        function moveSectionUp(){
21523            var prev = $(SECTION_ACTIVE_SEL).prev(SECTION_SEL);
21524
21525            //looping to the bottom if there's no more sections above
21526            if (!prev.length && (options.loopTop || options.continuousVertical)) {
21527                prev = $(SECTION_SEL).last();
21528            }
21529
21530            if (prev.length) {
21531                scrollPage(prev, null, true);
21532            }
21533        }
21534
21535        /**
21536        * Moves the page down one section.
21537        */
21538        function moveSectionDown(){
21539            var next = $(SECTION_ACTIVE_SEL).next(SECTION_SEL);
21540
21541            //looping to the top if there's no more sections below
21542            if(!next.length &&
21543                (options.loopBottom || options.continuousVertical)){
21544                next = $(SECTION_SEL).first();
21545            }
21546
21547            if(next.length){
21548                scrollPage(next, null, false);
21549            }
21550        }
21551
21552        /**
21553        * Moves the page to the given section and slide with no animation.
21554        * Anchors or index positions can be used as params.
21555        */
21556        function silentMoveTo(sectionAnchor, slideAnchor){
21557            setScrollingSpeed (0, 'internal');
21558            moveTo(sectionAnchor, slideAnchor);
21559            setScrollingSpeed (originals.scrollingSpeed, 'internal');
21560        }
21561
21562        /**
21563        * Moves the page to the given section and slide.
21564        * Anchors or index positions can be used as params.
21565        */
21566        function moveTo(sectionAnchor, slideAnchor){
21567            var destiny = getSectionByAnchor(sectionAnchor);
21568
21569            if (typeof slideAnchor !== 'undefined'){
21570                scrollPageAndSlide(sectionAnchor, slideAnchor);
21571            }else if(destiny.length > 0){
21572                scrollPage(destiny);
21573            }
21574        }
21575
21576        /**
21577        * Slides right the slider of the active section.
21578        * Optional `section` param.
21579        */
21580        function moveSlideRight(section){
21581            moveSlide('right', section);
21582        }
21583
21584        /**
21585        * Slides left the slider of the active section.
21586        * Optional `section` param.
21587        */
21588        function moveSlideLeft(section){
21589            moveSlide('left', section);
21590        }
21591
21592        /**
21593         * When resizing is finished, we adjust the slides sizes and positions
21594         */
21595        function reBuild(resizing){
21596            if(container.hasClass(DESTROYED)){ return; }  //nothing to do if the plugin was destroyed
21597
21598            isResizing = true;
21599
21600            windowsHeight = $window.height();  //updating global var
21601
21602            $(SECTION_SEL).each(function(){
21603                var slidesWrap = $(this).find(SLIDES_WRAPPER_SEL);
21604                var slides = $(this).find(SLIDE_SEL);
21605
21606                //adjusting the height of the table-cell for IE and Firefox
21607                if(options.verticalCentered){
21608                    $(this).find(TABLE_CELL_SEL).css('height', getTableHeight($(this)) + 'px');
21609                }
21610
21611                $(this).css('height', windowsHeight + 'px');
21612
21613                //resizing the scrolling divs
21614                if(options.scrollOverflow){
21615                    if(slides.length){
21616                        slides.each(function(){
21617                            createScrollBar($(this));
21618                        });
21619                    }else{
21620                        createScrollBar($(this));
21621                    }
21622                }
21623
21624                //adjusting the position fo the FULL WIDTH slides...
21625                if (slides.length > 1) {
21626                    landscapeScroll(slidesWrap, slidesWrap.find(SLIDE_ACTIVE_SEL));
21627                }
21628            });
21629
21630            var activeSection = $(SECTION_ACTIVE_SEL);
21631            var sectionIndex = activeSection.index(SECTION_SEL);
21632
21633            //isn't it the first section?
21634            if(sectionIndex){
21635                //adjusting the position for the current section
21636                silentMoveTo(sectionIndex + 1);
21637            }
21638
21639            isResizing = false;
21640            $.isFunction( options.afterResize ) && resizing && options.afterResize.call(container);
21641            $.isFunction( options.afterReBuild ) && !resizing && options.afterReBuild.call(container);
21642        }
21643
21644        /**
21645        * Turns fullPage.js to normal scrolling mode when the viewport `width` or `height`
21646        * are smaller than the set limit values.
21647        */
21648        function setResponsive(active){
21649            var isResponsive = $body.hasClass(RESPONSIVE);
21650
21651            if(active){
21652                if(!isResponsive){
21653                    setAutoScrolling(false, 'internal');
21654                    setFitToSection(false, 'internal');
21655                    $(SECTION_NAV_SEL).hide();
21656                    $body.addClass(RESPONSIVE);
21657                    $.isFunction( options.afterResponsive ) && options.afterResponsive.call( container, active);
21658                }
21659            }
21660            else if(isResponsive){
21661                setAutoScrolling(originals.autoScrolling, 'internal');
21662                setFitToSection(originals.autoScrolling, 'internal');
21663                $(SECTION_NAV_SEL).show();
21664                $body.removeClass(RESPONSIVE);
21665                $.isFunction( options.afterResponsive ) && options.afterResponsive.call( container, active);
21666            }
21667        }
21668
21669        if($(this).length){
21670            //public functions
21671            FP.setAutoScrolling = setAutoScrolling;
21672            FP.setRecordHistory = setRecordHistory;
21673            FP.setScrollingSpeed = setScrollingSpeed;
21674            FP.setFitToSection = setFitToSection;
21675            FP.setLockAnchors = setLockAnchors;
21676            FP.setMouseWheelScrolling = setMouseWheelScrolling;
21677            FP.setAllowScrolling = setAllowScrolling;
21678            FP.setKeyboardScrolling = setKeyboardScrolling;
21679            FP.moveSectionUp = moveSectionUp;
21680            FP.moveSectionDown = moveSectionDown;
21681            FP.silentMoveTo = silentMoveTo;
21682            FP.moveTo = moveTo;
21683            FP.moveSlideRight = moveSlideRight;
21684            FP.moveSlideLeft = moveSlideLeft;
21685            FP.fitToSection = fitToSection;
21686            FP.reBuild = reBuild;
21687            FP.setResponsive = setResponsive;
21688            FP.destroy = destroy;
21689
21690            init();
21691
21692            bindEvents();
21693        }
21694
21695        function init(){
21696            //if css3 is not supported, it will use jQuery animations
21697            if(options.css3){
21698                options.css3 = support3d();
21699            }
21700
21701            options.scrollBar = options.scrollBar || options.hybrid;
21702
21703            setOptionsFromDOM();
21704            prepareDom();
21705            setAllowScrolling(true);
21706            setAutoScrolling(options.autoScrolling, 'internal');
21707            responsive();
21708
21709            //setting the class for the body element
21710            setBodyClass();
21711
21712            //Uncode addition
21713            // if(document.readyState === 'complete'){
21714            //     scrollToAnchor();
21715            // }
21716            // $window.on('load', scrollToAnchor);
21717        }
21718
21719        function bindEvents(){
21720            $window
21721                //when scrolling...
21722                //.on('scroll', scrollHandler)
21723
21724                //detecting any change on the URL to scroll to the given anchor link
21725                //(a way to detect back history button as we play with the hashes on the URL)
21726                .on('hashchange', hashChangeHandler)
21727
21728                //when opening a new tab (ctrl + t), `control` won't be pressed when coming back.
21729                .blur(blurHandler)
21730
21731                //when resizing the site, we adjust the heights of the sections, slimScroll...
21732                .resize(resizeHandler);
21733
21734            $document
21735                //Sliding with arrow keys, both, vertical and horizontal
21736                .keydown(keydownHandler)
21737
21738                //to prevent scrolling while zooming
21739                .keyup(keyUpHandler)
21740
21741                //Scrolls to the section when clicking the navigation bullet
21742                .on('click touchstart', SECTION_NAV_SEL + ' a', sectionBulletHandler)
21743
21744                //Scrolls the slider to the given slide destination for the given section
21745                .on('click touchstart', SLIDES_NAV_LINK_SEL, slideBulletHandler)
21746
21747                .on('click', SECTION_NAV_TOOLTIP_SEL, tooltipTextHandler);
21748
21749            //Scrolling horizontally when clicking on the slider controls.
21750            $(SECTION_SEL).on('click touchstart', SLIDES_ARROW_SEL, slideArrowHandler);
21751
21752            /**
21753            * Applying normalScroll elements.
21754            * Ignoring the scrolls over the specified selectors.
21755            */
21756            if(options.normalScrollElements){
21757                $document.on('mouseenter', options.normalScrollElements, function () {
21758                    setMouseWheelScrolling(false);
21759                });
21760
21761                $document.on('mouseleave', options.normalScrollElements, function(){
21762                    setMouseWheelScrolling(true);
21763                });
21764            }
21765        }
21766
21767        /**
21768        * Setting options from DOM elements if they are not provided.
21769        */
21770        function setOptionsFromDOM(){
21771            var sections = container.find(options.sectionSelector);
21772
21773            //no anchors option? Checking for them in the DOM attributes
21774            if(!options.anchors.length){
21775                options.anchors = sections.filter('[data-anchor]').map(function(){
21776                    return $(this).data('anchor').toString();
21777                }).get();
21778            }
21779
21780            //no tooltips option? Checking for them in the DOM attributes
21781            if(!options.navigationTooltips.length){
21782                options.navigationTooltips = sections.filter('[data-tooltip]').map(function(){
21783                    return $(this).data('tooltip').toString();
21784                }).get();
21785            }
21786        }
21787
21788        /**
21789        * Works over the DOM structure to set it up for the current fullpage options.
21790        */
21791        function prepareDom(){
21792            container.css({
21793                'height': '100%',
21794                'position': 'relative'
21795            });
21796
21797            //adding a class to recognize the container internally in the code
21798            container.addClass(WRAPPER);
21799            $('html').addClass(ENABLED);
21800
21801            windowsHeight = $window.height();
21802
21803            container.removeClass(DESTROYED); //in case it was destroyed before initializing it again
21804
21805            addInternalSelectors();
21806
21807             //styling the sections / slides / menu
21808            $(SECTION_SEL).each(function(index){
21809                var section = $(this);
21810                var slides = section.find(SLIDE_SEL);
21811                var numSlides = slides.length;
21812
21813                styleSection(section, index);
21814                styleMenu(section, index);
21815
21816                // if there's any slide
21817                if (numSlides > 0) {
21818                    styleSlides(section, slides, numSlides);
21819                }else{
21820                    if(options.verticalCentered){
21821                        addTableClass(section);
21822                    }
21823                }
21824            });
21825
21826            //fixed elements need to be moved out of the plugin container due to problems with CSS3.
21827            if(options.fixedElements && options.css3){
21828                $(options.fixedElements).appendTo($body);
21829            }
21830
21831            //vertical centered of the navigation + active bullet
21832            if(options.navigation){
21833                addVerticalNavigation();
21834            }
21835
21836            enableYoutubeAPI();
21837
21838            if(options.scrollOverflow){
21839                if(document.readyState === 'complete'){
21840                    createScrollBarHandler();
21841                }
21842                //after DOM and images are loaded
21843                $window.on('load', createScrollBarHandler);
21844            }else{
21845                afterRenderActions();
21846            }
21847        }
21848
21849        /**
21850        * Styles the horizontal slides for a section.
21851        */
21852        function styleSlides(section, slides, numSlides){
21853            var sliderWidth = numSlides * 100;
21854            var slideWidth = 100 / numSlides;
21855
21856            slides.wrapAll('<div class="' + SLIDES_CONTAINER + '" />');
21857            slides.parent().wrap('<div class="' + SLIDES_WRAPPER + '" />');
21858
21859            section.find(SLIDES_CONTAINER_SEL).css('width', sliderWidth + '%');
21860
21861            if(numSlides > 1){
21862                if(options.controlArrows){
21863                    createSlideArrows(section);
21864                }
21865
21866                if(options.slidesNavigation){
21867                    addSlidesNavigation(section, numSlides);
21868                }
21869            }
21870
21871            slides.each(function(index) {
21872                $(this).css('width', slideWidth + '%');
21873
21874                if(options.verticalCentered){
21875                    addTableClass($(this));
21876                }
21877            });
21878
21879            var startingSlide = section.find(SLIDE_ACTIVE_SEL);
21880
21881            //if the slide won't be an starting point, the default will be the first one
21882            //the active section isn't the first one? Is not the first slide of the first section? Then we load that section/sl
21882ide by default.
21883            if( startingSlide.length &&  ($(SECTION_ACTIVE_SEL).index(SECTION_SEL) !== 0 || ($(SECTION_ACTIVE_SEL).index(SECTION_SEL) === 0 && startingSlide.index() !== 0))){
21884                silentLandscapeScroll(startingSlide, 'internal');
21885            }else{
21886                slides.eq(0).addClass(ACTIVE);
21887            }
21888        }
21889
21890        /**
21891        * Styling vertical sections
21892        */
21893        function styleSection(section, index){
21894            //if no active section is defined, the 1st one will be the default one
21895            if(!index && $(SECTION_ACTIVE_SEL).length === 0) {
21896                section.addClass(ACTIVE);
21897            }
21898            startingSection = $(SECTION_ACTIVE_SEL);
21899
21900            section.css('height', windowsHeight + 'px');
21901
21902            if(options.paddingTop){
21903                section.css('padding-top', options.paddingTop);
21904            }
21905
21906            if(options.paddingBottom){
21907                section.css('padding-bottom', options.paddingBottom);
21908            }
21909
21910            if (typeof options.sectionsColor[index] !==  'undefined') {
21911                section.css('background-color', options.sectionsColor[index]);
21912            }
21913
21914            if (typeof options.anchors[index] !== 'undefined') {
21915                section.attr('data-anchor', options.anchors[index]);
21916            }
21917        }
21918
21919        /**
21920        * Sets the data-anchor attributes to the menu elements and activates the current one.
21921        */
21922        function styleMenu(section, index){
21923            if (typeof options.anchors[index] !== 'undefined') {
21924                //activating the menu / nav element on load
21925                if(section.hasClass(ACTIVE)){
21926                    activateMenuAndNav(options.anchors[index], index);
21927                }
21928            }
21929
21930            //moving the menu outside the main container if it is inside (avoid problems with fixed positions when using CSS3 tranforms)
21931            if(options.menu && options.css3 && $(options.menu).closest(WRAPPER_SEL).length){
21932                $(options.menu).appendTo($body);
21933            }
21934        }
21935
21936        /**
21937        * Adds internal classes to be able to provide customizable selectors
21938        * keeping the link with the style sheet.
21939        */
21940        function addInternalSelectors(){
21941            container.find(options.sectionSelector).addClass(SECTION);
21942            container.find(options.slideSelector).addClass(SLIDE);
21943        }
21944
21945        /**
21946        * Creates the control arrows for the given section
21947        */
21948        function createSlideArrows(section){
21949            section.find(SLIDES_WRAPPER_SEL).after('<div class="' + SLIDES_ARROW_PREV + '"></div><div class="' + SLIDES_ARROW_NEXT + '"></div>');
21950
21951            if(options.controlArrowColor!='#fff'){
21952                section.find(SLIDES_ARROW_NEXT_SEL).css('border-color', 'transparent transparent transparent '+options.controlArrowColor);
21953                section.find(SLIDES_ARROW_PREV_SEL).css('border-color', 'transparent '+ options.controlArrowColor + ' transparent transparent');
21954            }
21955
21956            if(!options.loopHorizontal){
21957                section.find(SLIDES_ARROW_PREV_SEL).hide();
21958            }
21959        }
21960
21961        /**
21962        * Creates a vertical navigation bar.
21963        */
21964        function addVerticalNavigation(){
21965            $body.append('<div id="' + SECTION_NAV + '"><ul></ul></div>');
21966            var nav = $(SECTION_NAV_SEL);
21967
21968            nav.addClass(function() {
21969                return options.showActiveTooltip ? SHOW_ACTIVE_TOOLTIP + ' ' + options.navigationPosition : options.navigationPosition;
21970            });
21971
21972            for (var i = 0; i < $(SECTION_SEL).length; i++) {
21973                var link = '';
21974                if (options.anchors.length) {
21975                    link = options.anchors[i];
21976                }
21977
21978                var li = '<li><a href="#' + link + '"><span></span></a>';
21979
21980                // Only add tooltip if needed (defined by user)
21981                var tooltip = options.navigationTooltips[i];
21982
21983                if (typeof tooltip !== 'undefined' && tooltip !== '') {
21984                    li += '<div class="' + SECTION_NAV_TOOLTIP + ' ' + options.navigationPosition + '">' + tooltip + '</div>';
21985                }
21986
21987                li += '</li>';
21988
21989                nav.find('ul').append(li);
21990            }
21991
21992            //centering it vertically
21993            $(SECTION_NAV_SEL).css('margin-top', '-' + ($(SECTION_NAV_SEL).height()/2) + 'px');
21994
21995            //activating the current active section
21996            $(SECTION_NAV_SEL).find('li').eq($(SECTION_ACTIVE_SEL).index(SECTION_SEL)).find('a').addClass(ACTIVE);
21997        }
21998
21999        /**
22000        * Creates the slim scroll scrollbar for the sections and slides inside them.
22001        */
22002        function createScrollBarHandler(){
22003            $(SECTION_SEL).each(function(){
22004                var slides = $(this).find(SLIDE_SEL);
22005
22006                if(slides.length){
22007                    slides.each(function(){
22008                        createScrollBar($(this));
22009                    });
22010                }else{
22011                    createScrollBar($(this));
22012                }
22013
22014            });
22015            afterRenderActions();
22016        }
22017
22018        /*
22019        * Enables the Youtube videos API so we can control their flow if necessary.
22020        */
22021        function enableYoutubeAPI(){
22022            container.find('iframe[src*="youtube.com/embed/"]').each(function(){
22023                addURLParam($(this), 'enablejsapi=1');
22024            });
22025        }
22026
22027        /**
22028        * Adds a new parameter and its value to the `src` of a given element
22029        */
22030        function addURLParam(element, newParam){
22031            var originalSrc = element.attr('src');
22032            element.attr('src', originalSrc + getUrlParamSign(originalSrc) + newParam);
22033        }
22034
22035        /*
22036        * Returns the prefix sign to use for a new parameter in an existen URL.
22037        *
22038        * @return {String}  ? | &
22039        */
22040        function getUrlParamSign(url){
22041            return ( !/\?/.test( url ) ) ? '?' : '&';
22042        }
22043
22044        /**
22045        * Actions and callbacks to fire afterRender
22046        */
22047        function afterRenderActions(){
22048            var section = $(SECTION_ACTIVE_SEL);
22049
22050            section.addClass(COMPLETELY);
22051
22052            if(options.scrollOverflowHandler.afterRender){
22053                options.scrollOverflowHandler.afterRender(section);
22054            }
22055            lazyLoad(section);
22056            playMedia(section);
22057            options.scrollOverflowHandler.afterLoad();
22058
22059            if(isDestinyTheStartingSection()){
22060                $.isFunction( options.afterLoad ) && options.afterLoad.call(section, section.data('anchor'), (section.index(SECTION_SEL) + 1));
22061            }
22062
22063            $.isFunction( options.afterRender ) && options.afterRender.call(container);
22064        }
22065
22066        /**
22067        * Determines if the URL anchor destiny is the starting section (the one using 'active' class before initialization)
22068        */
22069        function isDestinyTheStartingSection(){
22070            var anchors =  window.location.hash.replace('#', '').split('/');
22071            var destinationSection = getSectionByAnchor(decodeURIComponent(anchors[0]));
22072
22073            return !destinationSection.length || destinationSection.length && destinationSection.index() === startingSection.index();
22074        }
22075
22076
22077        var isScrolling = false;
22078        var lastScroll = 0;
22079
22080        //when scrolling...
22081        function scrollHandler(){
22082            var currentSection;
22083
22084            if(!options.autoScrolling || options.scrollBar){
22085                var currentScroll = $window.scrollTop();
22086                var scrollDirection = getScrollDirection(currentScroll);
22087                var visibleSectionIndex = 0;
22088                var screen_mid = currentScroll + ($window.height() / 2.0);
22089                var isAtBottom = $body.height() - $window.height() === currentScroll;
22090                var sections =  document.querySelectorAll(SECTION_SEL);
22091
22092                //when using `auto-height` for a small last section it won't be centered in the viewport
22093                if(isAtBottom){
22094                    visibleSectionIndex = sections.length - 1;
22095                }
22096                //is at top? when using `auto-height` for a small first section it won't be centered in the viewport
22097                else if(!currentScroll){
22098                    visibleSectionIndex = 0;
22099                }
22100
22101                //taking the section which is showing more content in the viewport
22102                else{
22103                    for (var i = 0; i < sections.length; ++i) {
22104                        var section = sections[i];
22105
22106                        // Pick the the last section which passes the middle line of the screen.
22107                        if (section.offsetTop <= screen_mid)
22108                        {
22109                            visibleSectionIndex = i;
22110                        }
22111                    }
22112                }
22113
22114                if(isCompletelyInViewPort(scrollDirection)){
22115                    if(!$(SECTION_ACTIVE_SEL).hasClass(COMPLETELY)){
22116                        $(SECTION_ACTIVE_SEL).addClass(COMPLETELY).siblings().removeClass(COMPLETELY);
22117                    }
22118                }
22119
22120                //geting the last one, the current one on the screen
22121                currentSection = $(sections).eq(visibleSectionIndex);
22122
22123                //setting the visible section as active when manually scrolling
22124                //executing only once the first time we reach the section
22125                if(!currentSection.hasClass(ACTIVE)){
22126                    isScrolling = true;
22127                    var leavingSection = $(SECTION_ACTIVE_SEL);
22128                    var leavingSectionIndex = leavingSection.index(SECTION_SEL) + 1;
22129                    var yMovement = getYmovement(currentSection);
22130                    var anchorLink  = currentSection.data('anchor');
22131                    var sectionIndex = currentSection.index(SECTION_SEL) + 1;
22132                    var activeSlide = currentSection.find(SLIDE_ACTIVE_SEL);
22133                    var slideIndex;
22134                    var slideAnchorLink;
22135
22136                    if(activeSlide.length){
22137                        slideAnchorLink = activeSlide.data('anchor');
22138                        slideIndex = activeSlide.index();
22139                    }
22140
22141                    if(canScroll){
22142                        currentSection.addClass(ACTIVE).siblings().removeClass(ACTIVE);
22143
22144                        $.isFunction( options.onLeave ) && options.onLeave.call( leavingSection, leavingSectionIndex, sectionIndex, yMovement);
22145                        $.isFunction( options.afterLoad ) && options.afterLoad.call( currentSection, anchorLink, sectionIndex);
22146
22147                        stopMedia(leavingSection);
22148                        lazyLoad(currentSection);
22149                        playMedia(currentSection);
22150
22151                        activateMenuAndNav(anchorLink, sectionIndex - 1);
22152
22153                        if(options.anchors.length){
22154                            //needed to enter in hashChange event when using the menu with anchor links
22155                            lastScrolledDestiny = anchorLink;
22156                        }
22157                        setState(slideIndex, slideAnchorLink, anchorLink, sectionIndex);
22158                    }
22159
22160                    //small timeout in order to avoid entering in hashChange event when scrolling is not finished yet
22161                    clearTimeout(scrollId);
22162                    scrollId = setTimeout(function(){
22163                        isScrolling = false;
22164                    }, 100);
22165                }
22166
22167                if(options.fitToSection){
22168                    //for the auto adjust of the viewport to fit a whole section
22169                    clearTimeout(scrollId2);
22170
22171                    scrollId2 = setTimeout(function(){
22172                        //checking it again in case it changed during the delay
22173                        if(options.fitToSection){
22174                            fitToSection();
22175                        }
22176                    }, options.fitToSectionDelay);
22177                }
22178            }
22179        }
22180
22181        /**
22182        * Fits the site to the nearest active section
22183        */
22184        function fitToSection(){
22185            //checking fitToSection again in case it was set to false before the timeout delay
22186            if(canScroll){
22187                //allows to scroll to an active section and
22188                //if the section is already active, we prevent firing callbacks
22189                isResizing = true;
22190
22191                scrollPage($(SECTION_ACTIVE_SEL));
22192                isResizing = false;
22193            }
22194        }
22195
22196        /**
22197        * Determines whether the active section has seen in its whole or not.
22198        */
22199        function isCompletelyInViewPort(movement){
22200            var top = $(SECTION_ACTIVE_SEL).position().top;
22201            var bottom = top + $window.height();
22202
22203            if(movement == 'up'){
22204                return bottom >= ($window.scrollTop() + $window.height());
22205            }
22206            return top <= $window.scrollTop();
22207        }
22208
22209        /**
22210        * Gets the directon of the the scrolling fired by the scroll event.
22211        */
22212        function getScrollDirection(currentScroll){
22213            var direction = currentScroll > lastScroll ? 'down' : 'up';
22214
22215            lastScroll = currentScroll;
22216
22217            //needed for auto-height sections to determine if we want to scroll to the top or bottom of the destination
22218            previousDestTop = currentScroll;
22219
22220            return direction;
22221        }
22222
22223        /**
22224        * Determines the way of scrolling up or down:
22225        * by 'automatically' scrolling a section or by using the default and normal scrolling.
22226        */
22227        function scrolling(type, scrollable){
22228            if (!isScrollAllowed.m[type]){
22229                return;
22230            }
22231            var check = (type === 'down') ? 'bottom' : 'top';
22232            var scrollSection = (type === 'down') ? moveSectionDown : moveSectionUp;
22233
22234            if(scrollable.length > 0 ){
22235                //is the scrollbar at the start/end of the scroll?
22236                if(options.scrollOverflowHandler.isScrolled(check, scrollable)){
22237                    scrollSection();
22238                }else{
22239                    return true;
22240                }
22241            }else{
22242                // moved up/down
22243                scrollSection();
22244            }
22245        }
22246
22247        /*
22248        * Preventing bouncing in iOS #2285
22249        */
22250        function preventBouncing(event){
22251            var e = event.originalEvent;
22252            if(!checkParentForNormalScrollElement(event.target) && options.autoScrolling && isReallyTouch(e) && isScrollAllowed.m.up){
22253                //preventing the easing on iOS devices
22254                event.preventDefault();
22255            }
22256        }
22257
22258        var touchStartY = 0;
22259        var touchStartX = 0;
22260        var touchEndY = 0;
22261        var touchEndX = 0;
22262
22263        /* Detecting touch events
22264
22265        * As we are changing the top property of the page on scrolling, we can not use the traditional way to detect it.
22266        * This way, the touchstart and the touch moves shows an small difference between them which is the
22267        * used one to determine the direction.
22268        */
22269        function touchMoveHandler(event){
22270            var e = event.originalEvent;
22271            var activeSection = $(e.target).closest(SECTION_SEL);
22272
22273            // additional: if one of the normalScrollElements isn't within options.normalScrollElementTouchThreshold hops up the DOM chain
22274            if (!checkParentForNormalScrollElement(event.target) && isReallyTouch(e) ) {
22275
22276                if(options.autoScrolling){
22277                    //preventing the easing on iOS devices
22278                    event.preventDefault();
22279                }
22280
22281                var scrollable = options.scrollOverflowHandler.scrollable(activeSection);
22282                var touchEvents = getEventsPage(e);
22283
22284                touchEndY = touchEvents.y;
22285                touchEndX = touchEvents.x;
22286
22287                //if movement in the X axys is greater than in the Y and the currect section has slides...
22288                if (activeSection.find(SLIDES_WRAPPER_SEL).length && Math.abs(touchStartX - touchEndX) > (Math.abs(touchStartY - touchEndY))) {
22289
22290                    //is the movement greater than the minimum resistance to scroll?
22291                    if (!slideMoving && Math.abs(touchStartX - touchEndX) > ($window.outerWidth() / 100 * options.touchSensitivity)) {
22292                        if (touchStartX > touchEndX) {
22293                            if(isScrollAllowed.m.right){
22294                                moveSlideRight(activeSection); //next
22295                            }
22296                        } else {
22297                            if(isScrollAllowed.m.left){
22298                                moveSlideLeft(activeSection); //prev
22299                            }
22300                        }
22301                    }
22302                }
22303
22304                //vertical scrolling (only when autoScrolling is enabled)
22305                else if(options.autoScrolling && canScroll){
22306
22307                    //is the movement greater than the minimum resistance to scroll?
22308                    if (Math.abs(touchStartY - touchEndY) > ($window.height() / 100 * options.touchSensitivity)) {
22309                        if (touchStartY > touchEndY) {
22310                            scrolling('down', scrollable);
22311                        } else if (touchEndY > touchStartY) {
22312                            scrolling('up', scrollable);
22313                        }
22314                    }
22315                }
22316            }
22317        }
22318
22319        /**
22320         * recursive function to loop up the parent nodes to check if one of them exists in options.normalScrollElements
22321         * Currently works well for iOS - Android might need some testing
22322         * @param  {Element} el  target element / jquery selector (in subsequent nodes)
22323         * @param  {int}     hop current hop compared to options.normalScrollElementTouchThreshold
22324         * @return {boolean} true if there is a match to options.normalScrollElements
22325         */
22326        function checkParentForNormalScrollElement (el, hop) {
22327            hop = hop || 0;
22328            var parent = $(el).parent();
22329
22330            if (hop < options.normalScrollElementTouchThreshold &&
22331                parent.is(options.normalScrollElements) ) {
22332                return true;
22333            } else if (hop == options.normalScrollElementTouchThreshold) {
22334                return false;
22335            } else {
22336                return checkParentForNormalScrollElement(parent, ++hop);
22337            }
22338        }
22339
22340        /**
22341        * As IE >= 10 fires both touch and mouse events when using a mouse in a touchscreen
22342        * this way we make sure that is really a touch event what IE is detecting.
22343        */
22344        function isReallyTouch(e){
22345            //if is not IE   ||  IE is detecting `touch` or `pen`
22346            return typeof e.pointerType === 'undefined' || e.pointerType != 'mouse';
22347        }
22348
22349        /**
22350        * Handler for the touch start event.
22351        */
22352        function touchStartHandler(event){
22353            var e = event.originalEvent;
22354
22355            //stopping the auto scroll to adjust to a section
22356            if(options.fitToSection){
22357                $htmlBody.stop();
22358            }
22359
22360            if(isReallyTouch(e)){
22361                var touchEvents = getEventsPage(e);
22362                touchStartY = touchEvents.y;
22363                touchStartX = touchEvents.x;
22364            }
22365        }
22366
22367        /**
22368        * Gets the average of the last `number` elements of the given array.
22369        */
22370        function getAverage(elements, number){
22371            var sum = 0;
22372
22373            //taking `number` elements from the end to make the average, if there are not enought, 1
22374            var lastElements = elements.slice(Math.max(elements.length - number, 1));
22375
22376            for(var i = 0; i < lastElements.length; i++){
22377                sum = sum + lastElements[i];
22378            }
22379
22380            return Math.ceil(sum/number);
22381        }
22382
22383        /**
22384         * Detecting mousewheel scrolling
22385         *
22386         * http://blogs.sitepointstatic.com/examples/tech/mouse-wheel/index.html
22387         * http://www.sitepoint.com/html5-javascript-mouse-wheel/
22388         */
22389        var prevTime = new Date().getTime();
22390
22391        function MouseWheelHandler(e) {
22392            var curTime = new Date().getTime();
22393            var isNormalScroll = $(COMPLETELY_SEL).hasClass(NORMAL_SCROLL);
22394
22395            //autoscrolling and not zooming?
22396            if(options.autoScrolling && !controlPressed && !isNormalScroll){
22397                // cross-browser wheel delta
22398                e = e || window.event;
22399                var value = e.wheelDelta || -e.deltaY || -e.detail;
22400                var delta = Math.max(-1, Math.min(1, value));
22401
22402                var horizontalDetection = typeof e.wheelDeltaX !== 'undefined' || typeof e.deltaX !== 'undefined';
22403                var isScrollingVertically = (Math.abs(e.wheelDeltaX) < Math.abs(e.wheelDelta)) || (Math.abs(e.deltaX ) < Math.abs(e.deltaY) || !horizontalDetection);
22404
22405                //Limiting the array to 150 (lets not waste memory!)
22406                if(scrollings.length > 149){
22407                    scrollings.shift();
22408                }
22409
22410                //keeping record of the previous scrollings
22411                scrollings.push(Math.abs(value));
22412
22413                //preventing to scroll the site on mouse wheel when scrollbar is present
22414                if(options.scrollBar){
22415                    e.preventDefault ? e.preventDefault() : e.returnValue = false;
22416                }
22417
22418                var activeSection = $(SECTION_ACTIVE_SEL);
22419                var scrollable = options.scrollOverflowHandler.scrollable(activeSection);
22420
22421                //time difference between the last scroll and the current one
22422                var timeDiff = curTime-prevTime;
22423                prevTime = curTime;
22424
22425                //haven't they scrolled in a while?
22426                //(enough to be consider a different scrolling action to scroll another section)
22427                if(timeDiff > 200){
22428                    //emptying the array, we dont care about old scrollings for our averages
22429                    scrollings = [];
22430                }
22431
22432                if(canScroll){
22433                    var averageEnd = getAverage(scrollings, 10);
22434                    var averageMiddle = getAverage(scrollings, 70);
22435                    var isAccelerating = averageEnd >= averageMiddle;
22436
22437                    //to avoid double swipes...
22438                    if(isAccelerating && isScrollingVertically){
22439                        //scrolling down?
22440                        if (delta < 0) {
22441                            scrolling('down', scrollable);
22442
22443                        //scrolling up?
22444                        }else {
22445                            scrolling('up', scrollable);
22446                        }
22447                    }
22448                }
22449
22450                return false;
22451            }
22452
22453            if(options.fitToSection){
22454                //stopping the auto scroll to adjust to a section
22455                $htmlBody.stop();
22456            }
22457        }
22458
22459        /**
22460        * Slides a slider to the given direction.
22461        * Optional `section` param.
22462        */
22463        function moveSlide(direction, section){
22464            var activeSection = typeof section === 'undefined' ? $(SECTION_ACTIVE_SEL) : section;
22465            var slides = activeSection.find(SLIDES_WRAPPER_SEL);
22466            var numSlides = slides.find(SLIDE_SEL).length;
22467
22468            // more than one slide needed and nothing should be sliding
22469            if (!slides.length || slideMoving || numSlides < 2) {
22470                return;
22471            }
22472
22473            var currentSlide = slides.find(SLIDE_ACTIVE_SEL);
22474            var destiny = null;
22475
22476            if(direction === 'left'){
22477                destiny = currentSlide.prev(SLIDE_SEL);
22478            }else{
22479                destiny = currentSlide.next(SLIDE_SEL);
22480            }
22481
22482            //isn't there a next slide in the secuence?
22483            if(!destiny.length){
22484                //respect loopHorizontal settin
22485                if (!options.loopHorizontal) return;
22486
22487                if(direction === 'left'){
22488                    destiny = currentSlide.siblings(':last');
22489                }else{
22490                    destiny = currentSlide.siblings(':first');
22491                }
22492            }
22493
22494            slideMoving = true;
22495
22496            landscapeScroll(slides, destiny, direction);
22497        }
22498
22499        /**
22500        * Maintains the active slides in the viewport
22501        * (Because the `scroll` animation might get lost with some actions, such as when using continuousVertical)
22502        */
22503        function keepSlidesPosition(){
22504            $(SLIDE_ACTIVE_SEL).each(function(){
22505                silentLandscapeScroll($(this), 'internal');
22506            });
22507        }
22508
22509        var previousDestTop = 0;
22510        /**
22511        * Returns the destination Y position based on the scrolling direction and
22512        * the height of the section.
22513        */
22514        function getDestinationPosition(element){
22515            var elemPosition = element.position();
22516
22517            //top of the desination will be at the top of the viewport
22518            var position = elemPosition.top;
22519            var isScrollingDown =  elemPosition.top > previousDestTop;
22520            var sectionBottom = position - windowsHeight + element.outerHeight();
22521            var bigSectionsDestination = options.bigSectionsDestination;
22522
22523            //Uncode addition
22524            var containerH = container.outerHeight();
22525            var containerPosition = container.offset();
22526
22527            //########### Commented by Uncode - START ###########
22528            //is the destination element bigger than the viewport?
22529            // if(element.outerHeight() > windowsHeight){
22530            //     //scrolling up?
22531            //     if(!isScrollingDown && !bigSectionsDestination || bigSectionsDestination === 'bottom' ){
22532            //         position = sectionBottom;
22533            //     }
22534            // }
22535
22536            // //sections equal or smaller than the viewport height && scrolling down? ||  is resizing and its in the last section
22537            // else if(isScrollingDown || (isResizing && element.is(':last-child')) ){
22538            //     //The bottom of the destination will be at the bottom of the viewport
22539            //     position = sectionBottom;
22540            // }
22541            //########### Commented by Uncode - END ###########
22542
22543            //Uncode addition
22544            if ( !$masthead.hasClass('menu-transparent') && $('body').hasClass('uncode-fp-menu-shrink') && !element.is(':first-child') )
22545                position += 18;
22546            if ( ( containerH + menuHeight + bodyBorder + adminBarHeight - windowsHeight ) < position || ( isResizing && element.is(':last-child') ) ) {
22547                position = sectionBottom + menuHeight + bodyBorder*2 + adminBarHeight;
22548            }
22549
22550            /*
22551            Keeping record of the last scrolled position to determine the scrolling direction.
22552            No conventional methods can be used as the scroll bar might not be present
22553            AND the section might not be active if it is auto-height and didnt reach the middle
22554            of the viewport.
22555            */
22556            previousDestTop = position;
22557            return position;
22558        }
22559
22560        /**
22561        * Scrolls the site to the given element and scrolls to the slide if a callback is given.
22562        */
22563        function scrollPage(element, callback, isMovementUp){
22564            if(typeof element === 'undefined'){ return; } //there's no element to scroll, leaving the function
22565
22566            var dtop = getDestinationPosition(element);
22567            var slideAnchorLink;
22568            var slideIndex;
22569
22570            //local variables
22571            var v = {
22572                element: element,
22573                callback: callback,
22574                isMovementUp: isMovementUp,
22575                dtop: dtop,
22576                yMovement: getYmovement(element),
22577                anchorLink: element.data('anchor'),
22578                sectionIndex: element.index(SECTION_SEL),
22579                activeSlide: element.find(SLIDE_ACTIVE_SEL),
22580                activeSection: $(SECTION_ACTIVE_SEL),
22581                leavingSection: $(SECTION_ACTIVE_SEL).index(SECTION_SEL) + 1,
22582
22583                //caching the value of isResizing at the momment the function is called
22584                //because it will be checked later inside a setTimeout and the value might change
22585                localIsResizing: isResizing
22586            };
22587
22588            //quiting when destination scroll is the same as the current one
22589            if((v.activeSection.is(element) && !isResizing) || (options.scrollBar && $window.scrollTop() === v.dtop && !element.hasClass(AUTO_HEIGHT) )){ return; }
22590
22591            if(v.activeSlide.length){
22592                slideAnchorLink = v.activeSlide.data('anchor');
22593                slideIndex = v.activeSlide.index();
22594            }
22595
22596            // If continuousVertical && we need to wrap around
22597            if (options.autoScrolling && options.continuousVertical && typeof (v.isMovementUp) !== "undefined" &&
22598                ((!v.isMovementUp && v.yMovement == 'up') || // Intending to scroll down but about to go up or
22599                (v.isMovementUp && v.yMovement == 'down'))) { // intending to scroll up but about to go down
22600
22601                v = createInfiniteSections(v);
22602            }
22603
22604            //callback (onLeave) if the site is not just resizing and readjusting the slides
22605            if($.isFunction(options.onLeave) && !v.localIsResizing){
22606                if(options.onLeave.call(v.activeSection, v.leavingSection, (v.sectionIndex + 1), v.yMovement) === false){
22607                    return;
22608                }
22609            }
22610
22611            //pausing media of the leaving section (if we are not just resizing, as destinatino will be the same one)
22612            if(!v.localIsResizing){
22613                stopMedia(v.activeSection);
22614            }
22615
22616            options.scrollOverflowHandler.beforeLeave();
22617            element.addClass(ACTIVE).siblings().removeClass(ACTIVE);
22618            lazyLoad(element);
22619            options.scrollOverflowHandler.onLeave();
22620
22621
22622            //preventing from activating the MouseWheelHandler event
22623            //more than once if the page is scrolling
22624            canScroll = false;
22625
22626            setState(slideIndex, slideAnchorLink, v.anchorLink, v.sectionIndex);
22627
22628            performMovement(v);
22629
22630            //flag to avoid callingn `scrollPage()` twice in case of using anchor links
22631            lastScrolledDestiny = v.anchorLink;
22632
22633            //avoid firing it twice (as it does also on scroll)
22634            activateMenuAndNav(v.anchorLink, v.sectionIndex);
22635        }
22636
22637        /**
22638        * Performs the vertical movement (by CSS3 or by jQuery)
22639        */
22640        function performMovement(v){
22641            // using CSS3 translate functionality
22642            if (options.css3 && options.autoScrolling && !options.scrollBar) {
22643
22644                // The first section can have a negative value in iOS 10. Not quite sure why: -0.0142822265625
22645                // that's why we round it to 0.
22646                var translate3d = 'translate3d(0px, -' + Math.round(v.dtop) + 'px, 0px)';
22647                transformContainer(translate3d, true);
22648
22649                //even when the scrollingSpeed is 0 there's a little delay, which might cause the
22650                //scrollingSpeed to change in case of using silentMoveTo();
22651                if(options.scrollingSpeed){
22652                    clearTimeout(afterSectionLoadsId);
22653                    afterSectionLoadsId = setTimeout(function () {
22654                        afterSectionLoads(v);
22655                    }, options.scrollingSpeed);
22656                }else{
22657                    afterSectionLoads(v);
22658                }
22659            }
22660
22661            // using jQuery animate
22662            else{
22663                var scrollSettings = getScrollSettings(v);
22664
22665                $(scrollSettings.element).animate(
22666                    scrollSettings.options,
22667                options.scrollingSpeed, options.easing).promise().done(function () { //only one single callback in case of animating  `html, body`
22668                    if(options.scrollBar){
22669
22670                        /* Hack!
22671                        The timeout prevents setting the most dominant section in the viewport as "active" when the user
22672                        scrolled to a smaller section by using the mousewheel (auto scrolling) rather than draging the scroll bar.
22673
22674                        When using scrollBar:true It seems like the scroll events still getting propagated even after the scrolling animation has finished.
22675                        */
22676                        setTimeout(function(){
22677                            afterSectionLoads(v);
22678                        },30);
22679                    }else{
22680                        afterSectionLoads(v);
22681                    }
22682                });
22683            }
22684        }
22685
22686        /**
22687        * Gets the scrolling settings depending on the plugin autoScrolling option
22688        */
22689        function getScrollSettings(v){
22690            var scroll = {};
22691
22692            if(options.autoScrolling && !options.scrollBar){
22693                scroll.options = { 'top': -v.dtop};
22694                scroll.element = WRAPPER_SEL;
22695            }else{
22696                scroll.options = { 'scrollTop': v.dtop};
22697                scroll.element = 'html, body';
22698            }
22699
22700            return scroll;
22701        }
22702
22703        /**
22704        * Adds sections before or after the current one to create the infinite effect.
22705        */
22706        function createInfiniteSections(v){
22707            // Scrolling down
22708            if (!v.isMovementUp) {
22709                // Move all previous sections to after the active section
22710                $(SECTION_ACTIVE_SEL).after(v.activeSection.prevAll(SECTION_SEL).get().reverse());
22711            }
22712            else { // Scrolling up
22713                // Move all next sections to before the active section
22714                $(SECTION_ACTIVE_SEL).before(v.activeSection.nextAll(SECTION_SEL));
22715            }
22716
22717            // Maintain the displayed position (now that we changed the element order)
22718            silentScroll($(SECTION_ACTIVE_SEL).position().top);
22719
22720            // Maintain the active slides visible in the viewport
22721            keepSlidesPosition();
22722
22723            // save for later the elements that still need to be reordered
22724            v.wrapAroundElements = v.activeSection;
22725
22726            // Recalculate animation variables
22727            v.dtop = v.element.position().top;
22728            v.yMovement = getYmovement(v.element);
22729
22730            return v;
22731        }
22732
22733        /**
22734        * Fix section order after continuousVertical changes have been animated
22735        */
22736        function continuousVerticalFixSectionOrder (v) {
22737            // If continuousVertical is in effect (and autoScrolling would also be in effect then),
22738            // finish moving the elements around so the direct navigation will function more simply
22739            if (!v.wrapAroundElements || !v.wrapAroundElements.length) {
22740                return;
22741            }
22742
22743            if (v.isMovementUp) {
22744                $(SECTION_FIRST_SEL).before(v.wrapAroundElements);
22745            }
22746            else {
22747                $(SECTION_LAST_SEL).after(v.wrapAroundElements);
22748            }
22749
22750            silentScroll($(SECTION_ACTIVE_SEL).position().top);
22751
22752            // Maintain the active slides visible in the viewport
22753            keepSlidesPosition();
22754        }
22755
22756
22757        /**
22758        * Actions to do once the section is loaded.
22759        */
22760        function afterSectionLoads (v){
22761            continuousVerticalFixSectionOrder(v);
22762
22763            //callback (afterLoad) if the site is not just resizing and readjusting the slides
22764            $.isFunction(options.afterLoad) && !v.localIsResizing && options.afterLoad.call(v.element, v.anchorLink, (v.sectionIndex + 1));
22765            options.scrollOverflowHandler.afterLoad();
22766
22767            if(!v.localIsResizing){
22768                playMedia(v.element);
22769            }
22770
22771            v.element.addClass(COMPLETELY).siblings().removeClass(COMPLETELY);
22772
22773            canScroll = true;
22774
22775            $.isFunction(v.callback) && v.callback.call(this);
22776        }
22777
22778        /**
22779        * Sets the value for the given attribute from the `data-` attribute with the same suffix
22780        * ie: data-srcset ==> srcset  |  data-src ==> src
22781        */
22782        function setSrc(element, attribute){
22783            element
22784                .attr(attribute, element.data(attribute))
22785                .removeAttr('data-' + attribute);
22786        }
22787
22788        /**
22789        * Lazy loads image, video and audio elements.
22790        */
22791        function lazyLoad(destiny){
22792            if (!options.lazyLoading){
22793                return;
22794            }
22795
22796            var panel = getSlideOrSection(destiny);
22797            var element;
22798
22799            panel.find('img[data-src], img[data-srcset], source[data-src], audio[data-src], iframe[data-src]').each(function(){
22800                element = $(this);
22801
22802                $.each(['src', 'srcset'], function(index, type){
22803                    var attribute = element.attr('data-' + type);
22804                    if(typeof attribute !== 'undefined' && attribute){
22805                        setSrc(element, type);
22806                    }
22807                });
22808
22809                if(element.is('source')){
22810                    element.closest('video').get(0).load();
22811                }
22812            });
22813        }
22814
22815        /**
22816        * Plays video and audio elements.
22817        */
22818        function playMedia(destiny){
22819            var panel = getSlideOrSection(destiny);
22820
22821            //playing HTML5 media elements
22822            panel.find('video, audio').each(function(){
22823                var element = $(this).get(0);
22824
22825                if( element.hasAttribute('data-autoplay') && typeof element.play === 'function' ) {
22826                    element.play();
22827                }
22828            });
22829
22830            //youtube videos
22831            panel.find('iframe[src*="youtube.com/embed/"]').each(function(){
22832                var element = $(this).get(0);
22833
22834                if ( element.hasAttribute('data-autoplay') ){
22835                    playYoutube(element);
22836                }
22837
22838                //in case the URL was not loaded yet. On page load we need time for the new URL (with the API string) to load.
22839                element.onload = function() {
22840                    if ( element.hasAttribute('data-autoplay') ){
22841                        playYoutube(element);
22842                    }
22843                };
22844            });
22845        }
22846
22847        /**
22848        * Plays a youtube video
22849        */
22850        function playYoutube(element){
22851            element.contentWindow.postMessage('{"event":"command","func":"playVideo","args":""}', '*');
22852        }
22853
22854        /**
22855        * Stops video and audio elements.
22856        */
22857        function stopMedia(destiny){
22858            var panel = getSlideOrSection(destiny);
22859
22860            //stopping HTML5 media elements
22861            panel.find('video, audio').each(function(){
22862                var element = $(this).get(0);
22863
22864                if( !element.hasAttribute('data-keepplaying') && typeof element.pause === 'function' ) {
22865                    element.pause();
22866                }
22867            });
22868
22869            //youtube videos
22870            panel.find('iframe[src*="youtube.com/embed/"]').each(function(){
22871                var element = $(this).get(0);
22872
22873                if( /youtube\.com\/embed\//.test($(this).attr('src')) && !element.hasAttribute('data-keepplaying')){
22874                    $(this).get(0).contentWindow.postMessage('{"event":"command","func":"pauseVideo","args":""}','*');
22875                }
22876            });
22877        }
22878
22879        /**
22880        * Gets the active slide (or section) for the given section
22881        */
22882        function getSlideOrSection(destiny){
22883            var slide = destiny.find(SLIDE_ACTIVE_SEL);
22884            if( slide.length ) {
22885                destiny = $(slide);
22886            }
22887
22888            return destiny;
22889        }
22890
22891        /**
22892        * Scrolls to the anchor in the URL when loading the site
22893        */
22894        function scrollToAnchor(){
22895            //getting the anchor link in the URL and deleting the `#`
22896            var value =  window.location.hash.replace('#', '').split('/');
22897            var sectionAnchor = decodeURIComponent(value[0]);
22898            var slideAnchor = decodeURIComponent(value[1]);
22899
22900            if(sectionAnchor){  //if theres any #
22901                if(options.animateAnchor){
22902                    scrollPageAndSlide(sectionAnchor, slideAnchor);
22903                }else{
22904                    silentMoveTo(sectionAnchor, slideAnchor);
22905                }
22906            }
22907        }
22908
22909        /**
22910        * Detecting any change on the URL to scroll to the given anchor link
22911        * (a way to detect back history button as we play with the hashes on the URL)
22912        */
22913        function hashChangeHandler(){
22914            if(!isScrolling && !options.lockAnchors){
22915                var value =  window.location.hash.replace('#', '').split('/');
22916                var sectionAnchor = decodeURIComponent(value[0]);
22917                var slideAnchor = decodeURIComponent(value[1]);
22918
22919                    //when moving to a slide in the first section for the first time (first time to add an anchor to the URL)
22920                    var isFirstSlideMove =  (typeof lastScrolledDestiny === 'undefined');
22921                    var isFirstScrollMove = (typeof lastScrolledDestiny === 'undefined' && typeof slideAnchor === 'undefined' && !slideMoving);
22922
22923
22924                if(sectionAnchor.length){
22925                    /*in order to call scrollpage() only once for each destination at a time
22926                    It is called twice for each scroll otherwise, as in case of using anchorlinks `hashChange`
22927                    event is fired on every scroll too.*/
22928                    if ((sectionAnchor && sectionAnchor !== lastScrolledDestiny) && !isFirstSlideMove || isFirstScrollMove || (!slideMoving && lastScrolledSlide != slideAnchor ))  {
22929                        scrollPageAndSlide(sectionAnchor, slideAnchor);
22930                    }
22931                }
22932            }
22933        }
22934
22935        //Sliding with arrow keys, both, vertical and horizontal
22936        function keydownHandler(e) {
22937
22938            clearTimeout(keydownId);
22939
22940            var activeElement = $(':focus');
22941
22942            if(!activeElement.is('textarea') && !activeElement.is('input') && !activeElement.is('select') &&
22943                activeElement.attr('contentEditable') !== "true" && activeElement.attr('contentEditable') !== '' &&
22944                options.keyboardScrolling && options.autoScrolling){
22945                var keyCode = e.which;
22946
22947                //preventing the scroll with arrow keys & spacebar & Page Up & Down keys
22948                var keyControls = [40, 38, 32, 33, 34];
22949                if($.inArray(keyCode, keyControls) > -1){
22950                    e.preventDefault();
22951                }
22952
22953                controlPressed = e.ctrlKey;
22954
22955                keydownId = setTimeout(function(){
22956                    onkeydown(e);
22957                },150);
22958            }
22959        }
22960
22961        function tooltipTextHandler(){
22962            $(this).prev().trigger('click');
22963        }
22964
22965        //to prevent scrolling while zooming
22966        function keyUpHandler(e){
22967            if(isWindowFocused){ //the keyup gets fired on new tab ctrl + t in Firefox
22968                controlPressed = e.ctrlKey;
22969            }
22970        }
22971
22972        //binding the mousemove when the mouse's middle button is released
22973        function mouseDownHandler(e){
22974            //middle button
22975            if (e.which == 2){
22976                oldPageY = e.pageY;
22977                container.on('mousemove', mouseMoveHandler);
22978            }
22979        }
22980
22981        //unbinding the mousemove when the mouse's middle button is released
22982        function mouseUpHandler(e){
22983            //middle button
22984            if (e.which == 2){
22985                container.off('mousemove');
22986            }
22987        }
22988
22989        //Scrolling horizontally when clicking on the slider controls.
22990        function slideArrowHandler(){
22991            var section = $(this).closest(SECTION_SEL);
22992
22993            if ($(this).hasClass(SLIDES_PREV)) {
22994                if(isScrollAllowed.m.left){
22995                    moveSlideLeft(section);
22996                }
22997            } else {
22998                if(isScrollAllowed.m.right){
22999                    moveSlideRight(section);
23000                }
23001            }
23002        }
23003
23004        //when opening a new tab (ctrl + t), `control` won't be pressed when coming back.
23005        function blurHandler(){
23006            isWindowFocused = false;
23007            controlPressed = false;
23008        }
23009
23010        //Scrolls to the section when clicking the navigation bullet
23011        function sectionBulletHandler(e){
23012            e.preventDefault();
23013            var index = $(this).parent().index();
23014            scrollPage($(SECTION_SEL).eq(index));
23015        }
23016
23017        //Scrolls the slider to the given slide destination for the given section
23018        function slideBulletHandler(e){
23019            e.preventDefault();
23020            var slides = $(this).closest(SECTION_SEL).find(SLIDES_WRAPPER_SEL);
23021            var destiny = slides.find(SLIDE_SEL).eq($(this).closest('li').index());
23022
23023            landscapeScroll(slides, destiny);
23024        }
23025
23026        /**
23027        * Keydown event
23028        */
23029        function onkeydown(e){
23030            var shiftPressed = e.shiftKey;
23031
23032            //do nothing if we can not scroll or we are not using horizotnal key arrows.
23033            if(!canScroll && [37,39].indexOf(e.which) < 0){
23034                return;
23035            }
23036
23037            switch (e.which) {
23038                //up
23039                case 38:
23040                case 33:
23041                    if(isScrollAllowed.k.up){
23042                        moveSectionUp();
23043                    }
23044                    break;
23045
23046                //down
23047                case 32: //spacebar
23048                    if(shiftPressed && isScrollAllowed.k.up){
23049                        moveSectionUp();
23050                        break;
23051                    }
23052                /* falls through */
23053                case 40:
23054                case 34:
23055                    if(isScrollAllowed.k.down){
23056                        moveSectionDown();
23057                    }
23058                    break;
23059
23060                //Home
23061                case 36:
23062                    if(isScrollAllowed.k.up){
23063                        moveTo(1);
23064                    }
23065                    break;
23066
23067                //End
23068                case 35:
23069                     if(isScrollAllowed.k.down){
23070                        moveTo( $(SECTION_SEL).length );
23071                    }
23072                    break;
23073
23074                //left
23075                case 37:
23076                    if(isScrollAllowed.k.left){
23077                        moveSlideLeft();
23078                    }
23079                    break;
23080
23081                //right
23082                case 39:
23083                    if(isScrollAllowed.k.right){
23084                        moveSlideRight();
23085                    }
23086                    break;
23087
23088                default:
23089                    return; // exit this handler for other keys
23090            }
23091        }
23092
23093        /**
23094        * Detecting the direction of the mouse movement.
23095        * Used only for the middle button of the mouse.
23096        */
23097        var oldPageY = 0;
23098        function mouseMoveHandler(e){
23099            if(canScroll){
23100                // moving up
23101                if (e.pageY < oldPageY && isScrollAllowed.m.up){
23102                    moveSectionUp();
23103                }
23104
23105                // moving down
23106                else if(e.pageY > oldPageY && isScrollAllowed.m.down){
23107                    moveSectionDown();
23108                }
23109            }
23110            oldPageY = e.pageY;
23111        }
23112
23113        /**
23114        * Scrolls horizontal sliders.
23115        */
23116        function landscapeScroll(slides, destiny, direction){
23117            var section = slides.closest(SECTION_SEL);
23118            var v = {
23119                slides: slides,
23120                destiny: destiny,
23121                direction: direction,
23122                destinyPos: destiny.position(),
23123                slideIndex: destiny.index(),
23124                section: section,
23125                sectionIndex: section.index(SECTION_SEL),
23126                anchorLink: section.data('anchor'),
23127                slidesNav: section.find(SLIDES_NAV_SEL),
23128                slideAnchor:  getAnchor(destiny),
23129                prevSlide: section.find(SLIDE_ACTIVE_SEL),
23130                prevSlideIndex: section.find(SLIDE_ACTIVE_SEL).index(),
23131
23132                //caching the value of isResizing at the momment the function is called
23133                //because it will be checked later inside a setTimeout and the value might change
23134                localIsResizing: isResizing
23135            };
23136            v.xMovement = getXmovement(v.prevSlideIndex, v.slideIndex);
23137            v.direction = v.direction ? v.direction : v.xMovement;
23138
23139            //important!! Only do it when not resizing
23140            if(!v.localIsResizing){
23141                //preventing from scrolling to the next/prev section when using scrollHorizontally
23142                canScroll = false;
23143            }
23144
23145            if(options.onSlideLeave){
23146
23147                //if the site is not just resizing and readjusting the slides
23148                if(!v.localIsResizing && v.xMovement!=='none'){
23149                    if($.isFunction( options.onSlideLeave )){
23150                        if(options.onSlideLeave.call( v.prevSlide, v.anchorLink, (v.sectionIndex + 1), v.prevSlideIndex, v.xMovement, v.slideIndex ) === false){
23151                            slideMoving = false;
23152                            return;
23153                        }
23154                    }
23155                }
23156            }
23157
23158            destiny.addClass(ACTIVE).siblings().removeClass(ACTIVE);
23159
23160            if(!v.localIsResizing){
23161                stopMedia(v.prevSlide);
23162                lazyLoad(destiny);
23163            }
23164
23165            if(!options.loopHorizontal && options.controlArrows){
23166                //hidding it for the fist slide, showing for the rest
23167                section.find(SLIDES_ARROW_PREV_SEL).toggle(v.slideIndex!==0);
23168
23169                //hidding it for the last slide, showing for the rest
23170                section.find(SLIDES_ARROW_NEXT_SEL).toggle(!destiny.is(':last-child'));
23171            }
23172
23173            //only changing the URL if the slides are in the current section (not for resize re-adjusting)
23174            if(section.hasClass(ACTIVE) && !v.localIsResizing){
23175                setState(v.slideIndex, v.slideAnchor, v.anchorLink, v.sectionIndex);
23176            }
23177
23178            performHorizontalMove(slides, v, true);
23179        }
23180
23181
23182        function afterSlideLoads(v){
23183            activeSlidesNavigation(v.slidesNav, v.slideIndex);
23184
23185            //if the site is not just resizing and readjusting the slides
23186            if(!v.localIsResizing){
23187                $.isFunction( options.afterSlideLoad ) && options.afterSlideLoad.call( v.destiny, v.anchorLink, (v.sectionIndex + 1), v.slideAnchor, v.slideIndex);
23188
23189                //needs to be inside the condition to prevent problems with continuousVertical and scrollHorizontally
23190                //and to prevent double scroll right after a windows resize
23191                canScroll = true;
23192
23193                playMedia(v.destiny);
23194            }
23195
23196            //letting them slide again
23197            slideMoving = false;
23198        }
23199
23200        /**
23201        * Performs the horizontal movement. (CSS3 or jQuery)
23202        *
23203        * @param fireCallback {Bool} - determines whether or not to fire the callback
23204        */
23205        function performHorizontalMove(slides, v, fireCallback){
23206            var destinyPos = v.destinyPos;
23207
23208            if(options.css3){
23209                var translate3d = 'translate3d(-' + Math.round(destinyPos.left) + 'px, 0px, 0px)';
23210
23211                addAnimation(slides.find(SLIDES_CONTAINER_SEL)).css(getTransforms(translate3d), v);
23212
23213                afterSlideLoadsId = setTimeout(function(){
23214                    fireCallback && afterSlideLoads(v);
23215                }, options.scrollingSpeed, options.easing);
23216            }else{
23217                slides.animate({
23218                    scrollLeft : Math.round(destinyPos.left)
23219                }, options.scrollingSpeed, options.easing, function() {
23220
23221                    fireCallback && afterSlideLoads(v);
23222                });
23223            }
23224        }
23225
23226        /**
23227        * Sets the state for the horizontal bullet navigations.
23228        */
23229        function activeSlidesNavigation(slidesNav, slideIndex){
23230            slidesNav.find(ACTIVE_SEL).removeClass(ACTIVE);
23231            slidesNav.find('li').eq(slideIndex).find('a').addClass(ACTIVE);
23232        }
23233
23234        var previousHeight = windowsHeight;
23235
23236        //when resizing the site, we adjust the heights of the sections, slimScroll...
23237        function resizeHandler(){
23238            //checking if it needs to get responsive
23239            responsive();
23240
23241            // rebuild immediately on touch devices
23242            if (isTouchDevice) {
23243                var activeElement = $(document.activeElement);
23244
23245                //if the keyboard is NOT visible
23246                if (!activeElement.is('textarea') && !activeElement.is('input') && !activeElement.is('select')) {
23247                    var currentHeight = $window.height();
23248
23249                    //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)
23250                    if( Math.abs(currentHeight - previousHeight) > (20 * Math.max(previousHeight, currentHeight) / 100) ){
23251                        reBuild(true);
23252                        previousHeight = currentHeight;
23253                    }
23254                }
23255            }else{
23256                //in order to call the functions only when the resize is finished
23257                //http://stackoverflow.com/questions/4298612/jquery-how-to-call-resize-event-only-once-its-finished-resizing
23258                clearTimeout(resizeId);
23259
23260                resizeId = setTimeout(function(){
23261                    reBuild(true);
23262                }, 350);
23263            }
23264        }
23265
23266        /**
23267        * Checks if the site needs to get responsive and disables autoScrolling if so.
23268        * A class `fp-responsive` is added to the plugin's container in case the user wants to use it for his own responsive CSS.
23269        */
23270        function responsive(){
23271            var widthLimit = options.responsive || options.responsiveWidth; //backwards compatiblity
23272            var heightLimit = options.responsiveHeight;
23273
23274            //only calculating what we need. Remember its called on the resize event.
23275            var isBreakingPointWidth = widthLimit && $window.outerWidth() < widthLimit;
23276            var isBreakingPointHeight = heightLimit && $window.height() < heightLimit;
23277
23278            if(widthLimit && heightLimit){
23279                setResponsive(isBreakingPointWidth || isBreakingPointHeight);
23280            }
23281            else if(widthLimit){
23282                setResponsive(isBreakingPointWidth);
23283            }
23284            else if(heightLimit){
23285                setResponsive(isBreakingPointHeight);
23286            }
23287        }
23288
23289        /**
23290        * Adds transition animations for the given element
23291        */
23292        function addAnimation(container, element){
23293            var transition = 'all ' + options.scrollingSpeed + 'ms ' + options.easingcss3;
23294
23295            container.removeClass(NO_TRANSITION);
23296            return container.css({
23297                '-webkit-transition': transition,
23298                'transition': transition
23299            });
23300        }
23301
23302        /**
23303        * Remove transition animations for the given element
23304        */
23305        function removeAnimation(element){
23306            return element.addClass(NO_TRANSITION);
23307        }
23308
23309        /**
23310        * Activating the vertical navigation bullets according to the given slide name.
23311        */
23312        function activateNavDots(name, sectionIndex){
23313            if(options.navigation){
23314                $(SECTION_NAV_SEL).find(ACTIVE_SEL).removeClass(ACTIVE);
23315                if(name){
23316                    $(SECTION_NAV_SEL).find('a[href="#' + name + '"]').addClass(ACTIVE);
23317                }else{
23318                    $(SECTION_NAV_SEL).find('li').eq(sectionIndex).find('a').addClass(ACTIVE);
23319                }
23320            }
23321        }
23322
23323        /**
23324        * Activating the website main menu elements according to the given slide name.
23325        */
23326        function activateMenuElement(name){
23327            if(options.menu){
23328                $(options.menu).find(ACTIVE_SEL).removeClass(ACTIVE);
23329                $(options.menu).find('[data-menuanchor="'+name+'"]').addClass(ACTIVE);
23330            }
23331        }
23332
23333        /**
23334        * Sets to active the current menu and vertical nav items.
23335        */
23336        function activateMenuAndNav(anchor, index){
23337            activateMenuElement(anchor);
23338            activateNavDots(anchor, index);
23339        }
23340
23341        /**
23342        * Retuns `up` or `down` depending on the scrolling movement to reach its destination
23343        * from the current section.
23344        */
23345        function getYmovement(destiny){
23346            var fromIndex = $(SECTION_ACTIVE_SEL).index(SECTION_SEL);
23347            var toIndex = destiny.index(SECTION_SEL);
23348            if( fromIndex == toIndex){
23349                return 'none';
23350            }
23351            if(fromIndex > toIndex){
23352                return 'up';
23353            }
23354            return 'down';
23355        }
23356
23357        /**
23358        * Retuns `right` or `left` depending on the scrolling movement to reach its destination
23359        * from the current slide.
23360        */
23361        function getXmovement(fromIndex, toIndex){
23362            if( fromIndex == toIndex){
23363                return 'none';
23364            }
23365            if(fromIndex > toIndex){
23366                return 'left';
23367            }
23368            return 'right';
23369        }
23370
23371        /**
23372        * Checks if the element needs scrollbar and if the user wants to apply it.
23373        * If so it creates it.
23374        *
23375        * @param {Object} element   jQuery object of the section or slide
23376        */
23377        function createScrollBar(element){
23378            //User doesn't want scrollbar here? Sayonara baby!
23379            if(element.hasClass('fp-noscroll')) return;
23380
23381            //needed to make `scrollHeight` work under Opera 12
23382            element.css('overflow', 'hidden');
23383
23384            var scrollOverflowHandler = options.scrollOverflowHandler;
23385            var wrap = scrollOverflowHandler.wrapContent();
23386            //in case element is a slide
23387            var section = element.closest(SECTION_SEL);
23388            var scrollable = scrollOverflowHandler.scrollable(element);
23389            var contentHeight;
23390
23391            //if there was scroll, the contentHeight will be the one in the scrollable section
23392            if(scrollable.length){
23393                contentHeight = scrollOverflowHandler.scrollHeight(element);
23394            }else{
23395                contentHeight = element.get(0).scrollHeight;
23396                if(options.verticalCentered){
23397                    contentHeight = element.find(TABLE_CELL_SEL).get(0).scrollHeight;
23398                }
23399            }
23400
23401            var scrollHeight = windowsHeight - parseInt(section.css('padding-bottom')) - parseInt(section.css('padding-top'));
23402
23403            //needs scroll?
23404            if ( contentHeight > scrollHeight) {
23405                //did we already have an scrollbar ? Updating it
23406                if(scrollable.length){
23407                    scrollOverflowHandler.update(element, scrollHeight);
23408                }
23409                //creating the scrolling
23410                else{
23411                    if(options.verticalCentered){
23412                        element.find(TABLE_CELL_SEL).wrapInner(wrap);
23413                    }else{
23414                        element.wrapInner(wrap);
23415                    }
23416                    scrollOverflowHandler.create(element, scrollHeight);
23417                }
23418            }
23419            //removing the scrolling when it is not necessary anymore
23420            else{
23421                scrollOverflowHandler.remove(element);
23422            }
23423
23424            //undo
23425            element.css('overflow', '');
23426        }
23427
23428        function addTableClass(element){
23429            //In case we are styling for the 2nd time as in with reponsiveSlides
23430            if(!element.hasClass(TABLE)){
23431                element.addClass(TABLE).wrapInner('<div class="' + TABLE_CELL + '" style="height:' + getTableHeight(element) + 'px;" />');
23432            }
23433        }
23434
23435        function getTableHeight(element){
23436            var sectionHeight = windowsHeight;
23437
23438            if(options.paddingTop || options.paddingBottom){
23439                var section = element;
23440                if(!section.hasClass(SECTION)){
23441                    section = element.closest(SECTION_SEL);
23442                }
23443
23444                var paddings = parseInt(section.css('padding-top')) + parseInt(section.css('padding-bottom'));
23445                sectionHeight = (windowsHeight - paddings);
23446            }
23447
23448            return sectionHeight;
23449        }
23450
23451        /**
23452        * Adds a css3 transform property to the container class with or without animation depending on the animated param.
23453        */
23454        function transformContainer(translate3d, animated){
23455            if(animated){
23456                addAnimation(container);
23457            }else{
23458                removeAnimation(container);
23459            }
23460
23461            container.css(getTransforms(translate3d));
23462
23463            //syncronously removing the class after the animation has been applied.
23464            setTimeout(function(){
23465                container.removeClass(NO_TRANSITION);
23466            },10);
23467        }
23468
23469        /**
23470        * Gets a section by its anchor / index
23471        */
23472        function getSectionByAnchor(sectionAnchor){
23473            if(!sectionAnchor) return [];
23474
23475            var section = container.find(SECTION_SEL + '[data-anchor="'+sectionAnchor+'"]');
23476            if(!section.length){
23477                section = $(SECTION_SEL).eq( sectionAnchor -1);
23478            }
23479
23480            return section;
23481        }
23482
23483        /**
23484        * Gets a slide inside a given section by its anchor / index
23485        */
23486        function getSlideByAnchor(slideAnchor, section){
23487            var slides = section.find(SLIDES_WRAPPER_SEL);
23488            var slide =  slides.find(SLIDE_SEL + '[data-anchor="'+slideAnchor+'"]');
23489
23490            if(!slide.length){
23491                slide = slides.find(SLIDE_SEL).eq(slideAnchor);
23492            }
23493
23494            return slide;
23495        }
23496
23497        /**
23498        * Scrolls to the given section and slide anchors
23499        */
23500        function scrollPageAndSlide(destiny, slide){
23501            var section = getSectionByAnchor(destiny);
23502
23503            //do nothing if there's no section with the given anchor name
23504            if(!section.length) return;
23505
23506            //default slide
23507            if (typeof slide === 'undefined') {
23508                slide = 0;
23509            }
23510
23511            //we need to scroll to the section and then to the slide
23512            if (destiny !== lastScrolledDestiny && !section.hasClass(ACTIVE)){
23513                scrollPage(section, function(){
23514                    scrollSlider(section, slide);
23515                });
23516            }
23517            //if we were already in the section
23518            else{
23519                scrollSlider(section, slide);
23520            }
23521        }
23522
23523        /**
23524        * Scrolls the slider to the given slide destination for the given section
23525        */
23526        function scrollSlider(section, slideAnchor){
23527            if(typeof slideAnchor !== 'undefined'){
23528                var slides = section.find(SLIDES_WRAPPER_SEL);
23529                var destiny =  getSlideByAnchor(slideAnchor, section);
23530
23531                if(destiny.length){
23532                    landscapeScroll(slides, destiny);
23533                }
23534            }
23535        }
23536
23537        /**
23538        * Creates a landscape navigation bar with dots for horizontal sliders.
23539        */
23540        function addSlidesNavigation(section, numSlides){
23541            section.append('<div class="' + SLIDES_NAV + '"><ul></ul></div>');
23542            var nav = section.find(SLIDES_NAV_SEL);
23543
23544            //top or bottom
23545            nav.addClass(options.slidesNavPosition);
23546
23547            for(var i=0; i< numSlides; i++){
23548                nav.find('ul').append('<li><a href="#"><span></span></a></li>');
23549            }
23550
23551            //centering it
23552            nav.css('margin-left', '-' + (nav.width()/2) + 'px');
23553
23554            nav.find('li').first().find('a').addClass(ACTIVE);
23555        }
23556
23557
23558        /**
23559        * Sets the state of the website depending on the active section/slide.
23560        * It changes the URL hash when needed and updates the body class.
23561        */
23562        function setState(slideIndex, slideAnchor, anchorLink, sectionIndex){
23563            var sectionHash = '';
23564
23565            if(options.anchors.length && !options.lockAnchors){
23566
23567                //isn't it the first slide?
23568                if(slideIndex){
23569                    if(typeof anchorLink !== 'undefined'){
23570                        sectionHash = anchorLink;
23571                    }
23572
23573                    //slide without anchor link? We take the index instead.
23574                    if(typeof slideAnchor === 'undefined'){
23575                        slideAnchor = slideIndex;
23576                    }
23577
23578                    lastScrolledSlide = slideAnchor;
23579                    setUrlHash(sectionHash + '/' + slideAnchor);
23580
23581                //first slide won't have slide anchor, just the section one
23582                }else if(typeof slideIndex !== 'undefined'){
23583                    lastScrolledSlide = slideAnchor;
23584                    setUrlHash(anchorLink);
23585                }
23586
23587                //section without slides
23588                else{
23589                    setUrlHash(anchorLink);
23590                }
23591            }
23592
23593            setBodyClass();
23594        }
23595
23596        /**
23597        * Sets the URL hash.
23598        */
23599        function setUrlHash(url){
23600            //Uncode addition
23601            if ( typeof SiteParameters.slide_footer != 'undefined' && url == SiteParameters.slide_footer )
23602                return false;
23603
23604            if(options.recordHistory){
23605                location.hash = url;
23606            }else{
23607                //Uncode addition
23608                //Mobile Chrome doesn't work the normal way, so... lets use HTML5 for phones :)
23609                // if(isTouchDevice || isTouch){
23610                //     window.history.replaceState(undefined, undefined, '#' + url);
23611                // }else{
23612                //     var baseUrl = window.location.href.split('#')[0];
23613                //     window.location.replace( baseUrl + '#' + url );
23614                // }
23615                return false;
23616            }
23617        }
23618
23619        /**
23620        * Gets the anchor for the given slide / section. Its index will be used if there's none.
23621        */
23622        function getAnchor(element){
23623            var anchor = element.data('anchor');
23624            var index = element.index();
23625
23626            //Slide without anchor link? We take the index instead.
23627            if(typeof anchor === 'undefined'){
23628                anchor = index;
23629            }
23630
23631            return anchor;
23632        }
23633
23634        /**
23635        * Sets a class for the body of the page depending on the active section / slide
23636        */
23637        function setBodyClass(){
23638            var section = $(SECTION_ACTIVE_SEL);
23639            var slide = section.find(SLIDE_ACTIVE_SEL);
23640
23641            var sectionAnchor = getAnchor(section);
23642            var slideAnchor = getAnchor(slide);
23643
23644            var text = String(sectionAnchor);
23645
23646            if(slide.length){
23647                text = text + '-' + slideAnchor;
23648            }
23649
23650            //changing slash for dash to make it a valid CSS style
23651            text = text.replace('/', '-').replace('#','');
23652
23653            //removing previous anchor classes
23654            var classRe = new RegExp('\\b\\s?' + VIEWING_PREFIX + '-[^\\s]+\\b', "g");
23655            $body[0].className = $body[0].className.replace(classRe, '');
23656
23657            //adding the current anchor
23658            $body.addClass(VIEWING_PREFIX + '-' + text);
23659        }
23660
23661        /**
23662        * Checks for translate3d support
23663        * @return boolean
23664        * http://stackoverflow.com/questions/5661671/detecting-transform-translate3d-support
23665        */
23666        function support3d() {
23667            var el = document.createElement('p'),
23668                has3d,
23669                transforms = {
23670                    'webkitTransform':'-webkit-transform',
23671                    'OTransform':'-o-transform',
23672                    'msTransform':'-ms-transform',
23673                    'MozTransform':'-moz-transform',
23674                    'transform':'transform'
23675                };
23676
23677            // Add it to the body to get the computed style.
23678            document.body.insertBefore(el, null);
23679
23680            for (var t in transforms) {
23681                if (el.style[t] !== undefined) {
23682                    el.style[t] = 'translate3d(1px,1px,1px)';
23683                    has3d = window.getComputedStyle(el).getPropertyValue(transforms[t]);
23684                }
23685            }
23686
23687            document.body.removeChild(el);
23688
23689            return (has3d !== undefined && has3d.length > 0 && has3d !== 'none');
23690        }
23691
23692        /**
23693        * Removes the auto scrolling action fired by the mouse wheel and trackpad.
23694        * After this function is called, the mousewheel and trackpad movements won't scroll through sections.
23695        */
23696        function removeMouseWheelHandler(){
23697            if (document.addEventListener) {
23698                document.removeEventListener('mousewheel', MouseWheelHandler, false); //IE9, Chrome, Safari, Oper
23699                document.removeEventListener('wheel', MouseWheelHandler, false); //Firefox
23700                document.removeEventListener('MozMousePixelScroll', MouseWheelHandler, false); //old Firefox
23701            } else {
23702                document.detachEvent('onmousewheel', MouseWheelHandler); //IE 6/7/8
23703            }
23704        }
23705
23706        /**
23707        * Adds the auto scrolling action for the mouse wheel and trackpad.
23708        * After this function is called, the mousewheel and trackpad movements will scroll through sections
23709        * https://developer.mozilla.org/en-US/docs/Web/Events/wheel
23710        */
23711        function addMouseWheelHandler(){
23712            var prefix = '';
23713            var _addEventListener;
23714
23715            if (window.addEventListener){
23716                _addEventListener = "addEventListener";
23717            }else{
23718                _addEventListener = "attachEvent";
23719                prefix = 'on';
23720            }
23721
23722             // detect available wheel event
23723            var support = 'onwheel' in document.createElement('div') ? 'wheel' : // Modern browsers support "wheel"
23724                      document.onmousewheel !== undefined ? 'mousewheel' : // Webkit and IE support at least "mousewheel"
23725                      'DOMMouseScroll'; // let's assume that remaining browsers are older Firefox
23726            var passiveEvent = g_supportsPassive ? {passive: false }
23726: false;
23727
23728
23729            if(support == 'DOMMouseScroll'){
23730                document[ _addEventListener ](prefix + 'MozMousePixelScroll', MouseWheelHandler, passiveEvent);
23731            }
23732
23733            //handle MozMousePixelScroll in older Firefox
23734            else{
23735                document[ _addEventListener ](prefix + support, MouseWheelHandler, passiveEvent);
23736            }
23737        }
23738
23739        /**
23740        * Binding the mousemove when the mouse's middle button is pressed
23741        */
23742        function addMiddleWheelHandler(){
23743            container
23744                .on('mousedown', mouseDownHandler)
23745                .on('mouseup', mouseUpHandler);
23746        }
23747
23748        /**
23749        * Unbinding the mousemove when the mouse's middle button is released
23750        */
23751        function removeMiddleWheelHandler(){
23752            container
23753                .off('mousedown', mouseDownHandler)
23754                .off('mouseup', mouseUpHandler);
23755        }
23756
23757        /**
23758        * Adds the possibility to auto scroll through sections on touch devices.
23759        */
23760        function addTouchHandler(){
23761            if(isTouchDevice || isTouch){
23762                if(options.autoScrolling){
23763                    $body.off(events.touchmove).on(events.touchmove, preventBouncing);
23764                }
23765
23766                $(WRAPPER_SEL)
23767                    .off(events.touchstart).on(events.touchstart, touchStartHandler)
23768                    .off(events.touchmove).on(events.touchmove, touchMoveHandler);
23769            }
23770        }
23771
23772        /**
23773        * Removes the auto scrolling for touch devices.
23774        */
23775        function removeTouchHandler(){
23776            if(isTouchDevice || isTouch){
23777                $(WRAPPER_SEL)
23778                    .off(events.touchstart)
23779                    .off(events.touchmove);
23780            }
23781        }
23782
23783        /*
23784        * Returns and object with Microsoft pointers (for IE<11 and for IE >= 11)
23785        * http://msdn.microsoft.com/en-us/library/ie/dn304886(v=vs.85).aspx
23786        */
23787        function getMSPointer(){
23788            var pointer;
23789
23790            //IE >= 11 & rest of browsers
23791            if(window.PointerEvent){
23792                pointer = { down: 'pointerdown', move: 'pointermove'};
23793            }
23794
23795            //IE < 11
23796            else{
23797                pointer = { down: 'MSPointerDown', move: 'MSPointerMove'};
23798            }
23799
23800            return pointer;
23801        }
23802
23803        /**
23804        * Gets the pageX and pageY properties depending on the browser.
23805        * https://github.com/alvarotrigo/fullPage.js/issues/194#issuecomment-34069854
23806        */
23807        function getEventsPage(e){
23808            var events = [];
23809
23810            events.y = (typeof e.pageY !== 'undefined' && (e.pageY || e.pageX) ? e.pageY : e.touches[0].pageY);
23811            events.x = (typeof e.pageX !== 'undefined' && (e.pageY || e.pageX) ? e.pageX : e.touches[0].pageX);
23812
23813            //in touch devices with scrollBar:true, e.pageY is detected, but we have to deal with touch events. #1008
23814            if(isTouch && isReallyTouch(e) && options.scrollBar){
23815                events.y = e.touches[0].pageY;
23816                events.x = e.touches[0].pageX;
23817            }
23818
23819            return events;
23820        }
23821
23822        /**
23823        * Slides silently (with no animation) the active slider to the given slide.
23824        * @param noCallback {bool} true or defined -> no callbacks
23825        */
23826        function silentLandscapeScroll(activeSlide, noCallbacks){
23827            setScrollingSpeed (0, 'internal');
23828
23829            if(typeof noCallbacks !== 'undefined'){
23830                //preventing firing callbacks afterSlideLoad etc.
23831                isResizing = true;
23832            }
23833
23834            landscapeScroll(activeSlide.closest(SLIDES_WRAPPER_SEL), activeSlide);
23835
23836            if(typeof noCallbacks !== 'undefined'){
23837                isResizing = false;
23838            }
23839
23840            setScrollingSpeed(originals.scrollingSpeed, 'internal');
23841        }
23842
23843        /**
23844        * Scrolls silently (with no animation) the page to the given Y position.
23845        */
23846        function silentScroll(top){
23847            // The first section can have a negative value in iOS 10. Not quite sure why: -0.0142822265625
23848            // that's why we round it to 0.
23849            var roundedTop = Math.round(top);
23850
23851            if (options.css3 && options.autoScrolling && !options.scrollBar){
23852                var translate3d = 'translate3d(0px, -' + roundedTop + 'px, 0px)';
23853                transformContainer(translate3d, false);
23854            }
23855            else if(options.autoScrolling && !options.scrollBar){
23856                container.css('top', -roundedTop);
23857            }
23858            else{
23859                $htmlBody.scrollTop(roundedTop);
23860            }
23861        }
23862
23863        /**
23864        * Returns the cross-browser transform string.
23865        */
23866        function getTransforms(translate3d){
23867            return {
23868                '-webkit-transform': translate3d,
23869                '-moz-transform': translate3d,
23870                '-ms-transform':translate3d,
23871                'transform': translate3d
23872            };
23873        }
23874
23875        /**
23876        * Allowing or disallowing the mouse/swipe scroll in a given direction. (not for keyboard)
23877        * @type  m (mouse) or k (keyboard)
23878        */
23879        function setIsScrollAllowed(value, direction, type){
23880            switch (direction){
23881                case 'up': isScrollAllowed[type].up = value; break;
23882                case 'down': isScrollAllowed[type].down = value; break;
23883                case 'left': isScrollAllowed[type].left = value; break;
23884                case 'right': isScrollAllowed[type].right = value; break;
23885                case 'all':
23886                    if(type == 'm'){
23887                        setAllowScrolling(value);
23888                    }else{
23889                        setKeyboardScrolling(value);
23890                    }
23891            }
23892        }
23893
23894        /*
23895        * Destroys fullpage.js plugin events and optinally its html markup and styles
23896        */
23897        function destroy(all){
23898            setAutoScrolling(false, 'internal');
23899            setAllowScrolling(false);
23900            setKeyboardScrolling(false);
23901            container.addClass(DESTROYED);
23902
23903            clearTimeout(afterSlideLoadsId);
23904            clearTimeout(afterSectionLoadsId);
23905            clearTimeout(resizeId);
23906            clearTimeout(scrollId);
23907            clearTimeout(scrollId2);
23908
23909            $window
23910                .off('scroll', scrollHandler)
23911                .off('hashchange', hashChangeHandler)
23912                .off('resize', resizeHandler);
23913
23914            $document
23915                .off('click touchstart', SECTION_NAV_SEL + ' a')
23916                .off('mouseenter', SECTION_NAV_SEL + ' li')
23917                .off('mouseleave', SECTION_NAV_SEL + ' li')
23918                .off('click touchstart', SLIDES_NAV_LINK_SEL)
23919                .off('mouseover', options.normalScrollElements)
23920                .off('mouseout', options.normalScrollElements);
23921
23922            $(SECTION_SEL)
23923                .off('click touchstart', SLIDES_ARROW_SEL);
23924
23925            clearTimeout(afterSlideLoadsId);
23926            clearTimeout(afterSectionLoadsId);
23927
23928            //lets make a mess!
23929            if(all){
23930                destroyStructure();
23931            }
23932        }
23933
23934        /*
23935        * Removes inline styles added by fullpage.js
23936        */
23937        function destroyStructure(){
23938            //reseting the `top` or `translate` properties to 0
23939            silentScroll(0);
23940
23941            //loading all the lazy load content
23942            container.find('img[data-src], source[data-src], audio[data-src], iframe[data-src]').each(function(){
23943                setSrc($(this), 'src');
23944            });
23945
23946            container.find('img[data-srcset]').each(function(){
23947                setSrc($(this), 'srcset');
23948            });
23949
23950            $(SECTION_NAV_SEL + ', ' + SLIDES_NAV_SEL +  ', ' + SLIDES_ARROW_SEL).remove();
23951
23952            //removing inline styles
23953            $(SECTION_SEL).css( {
23954                'height': '',
23955                'background-color' : '',
23956                'padding': ''
23957            });
23958
23959            $(SLIDE_SEL).css( {
23960                'width': ''
23961            });
23962
23963            container.css({
23964                'height': '',
23965                'position': '',
23966                '-ms-touch-action': '',
23967                'touch-action': ''
23968            });
23969
23970            $htmlBody.css({
23971                'overflow': '',
23972                'height': ''
23973            });
23974
23975            // remove .fp-enabled class
23976            $('html').removeClass(ENABLED);
23977
23978            // remove .fp-responsive class
23979            $body.removeClass(RESPONSIVE);
23980
23981            // remove all of the .fp-viewing- classes
23982            $.each($body.get(0).className.split(/\s+/), function (index, className) {
23983                if (className.indexOf(VIEWING_PREFIX) === 0) {
23984                    $body.removeClass(className);
23985                }
23986            });
23987
23988            //removing added classes
23989            $(SECTION_SEL + ', ' + SLIDE_SEL).each(function(){
23990                options.scrollOverflowHandler.remove($(this));
23991                $(this).removeClass(TABLE + ' ' + ACTIVE);
23992            });
23993
23994            removeAnimation(container);
23995
23996            //Unwrapping content
23997            container.find(TABLE_CELL_SEL + ', ' + SLIDES_CONTAINER_SEL + ', ' + SLIDES_WRAPPER_SEL).each(function(){
23998                //unwrap not being use in case there's no child element inside and its just text
23999                $(this).replaceWith(this.childNodes);
24000            });
24001
24002            //removing the applied transition from the fullpage wrapper
24003            container.css({
24004                '-webkit-transition': 'none',
24005                'transition': 'none'
24006            });
24007
24008            //scrolling the page to the top with no animation
24009            $htmlBody.scrollTop(0);
24010
24011            //removing selectors
24012            var usedSelectors = [SECTION, SLIDE, SLIDES_CONTAINER];
24013            $.each(usedSelectors, function(index, value){
24014                $('.' + value).removeClass(value);
24015            });
24016        }
24017
24018        /*
24019        * Sets the state for a variable with multiple states (original, and temporal)
24020        * Some variables such as `autoScrolling` or `recordHistory` might change automatically its state when using `responsive` or `autoScrolling:false`.
24021        * This function is used to keep track of both states, the original and the temporal one.
24022        * If type is not 'internal', then we assume the user is globally changing the variable.
24023        */
24024        function setVariableState(variable, value, type){
24025            options[variable] = value;
24026            if(type !== 'internal'){
24027                originals[variable] = value;
24028            }
24029        }
24030
24031        /**
24032        * Displays warnings
24033        */
24034        function displayWarnings(){
24035            var extensions = ['fadingEffect', 'continuousHorizontal', 'scrollHorizontally', 'interlockedSlides', 'resetSliders', 'responsiveSlides', 'offsetSections', 'dragAndMove', 'scrollOverflowReset', 'parallax'];
24036            //UNCODE.addition
24037            // if($('html').hasClass(ENABLED)){
24038            //     showError('error', 'Fullpage.js can only be initialized once and you are doing it multiple times!');
24039            //     return;
24040            // }
24041
24042            // Disable mutually exclusive settings
24043            //UNCODE.addition
24044            // if (options.continuousVertical &&
24045            //     (options.loopTop || options.loopBottom)) {
24046            //     options.continuousVertical = false;
24047            //     showError('warn', 'Option `loopTop/loopBottom` is mutually exclusive with `continuousVertical`; `continuousVertical` disabled');
24048            // }
24049
24050            // if(options.scrollBar && options.scrollOverflow){
24051            //     showError('warn', 'Option `scrollBar` is mutually exclusive with `scrollOverflow`. Sections with scrollOverflow might not work well in Firefox');
24052            // }
24053
24054            // if(options.continuousVertical && (options.scrollBar || !options.autoScrolling)){
24055            //     options.continuousVertical = false;
24056            //     showError('warn', 'Scroll bars (`scrollBar:true` or `autoScrolling:false`) are mutually exclusive with `continuousVertical`; `continuousVertical` disabled');
24057            // }
24058
24059            //using extensions? Wrong file!
24060            $.each(extensions, function(index, extension){
24061                //is the option set to true?
24062                if(options[extension]){
24063                    showError('warn', 'fullpage.js extensions require jquery.fullpage.extensions.min.js file instead of the usual jquery.fullpage.js. Requested: '+ extension);
24064                }
24065            });
24066
24067            //anchors can not have the same value as any element ID or NAME
24068            $.each(options.anchors, function(index, name){
24069
24070                //case insensitive selectors (http://stackoverflow.com/a/19465187/1081396)
24071                var nameAttr = $document.find('[name]').filter(function() {
24072                    return $(this).attr('name') && $(this).attr('name').toLowerCase() == name.toLowerCase();
24073                });
24074
24075                var idAttr = $document.find('[id]').filter(function() {
24076                    return $(this).attr('id') && $(this).attr('id').toLowerCase() == name.toLowerCase();
24077                });
24078
24079                if(idAttr.length || nameAttr.length ){
24080                    showError('error', 'data-anchor tags can not have the same value as any `id` element on the site (or `name` element for IE).');
24081                    idAttr.length && showError('error', '"' + name + '" is is being used by another element `id` property');
24082                    nameAttr.length && showError('error', '"' + name + '" is is being used by another element `name` property');
24083                }
24084            });
24085        }
24086
24087        /**
24088        * Shows a message in the console of the given type.
24089        */
24090        function showError(type, text){
24091            console && console[type] && console[type]('fullPage: ' + text);
24092        }
24093
24094    }; //end of $.fn.fullpage
24095
24096    if(typeof IScroll !== 'undefined'){
24097        /*
24098        * Turns iScroll `mousewheel` option off dynamically
24099        * https://github.com/cubiq/iscroll/issues/1036
24100        */
24101        IScroll.prototype.wheelOn = function () {
24102            this.wrapper.addEventListener('wheel', this);
24103            this.wrapper.addEventListener('mousewheel', this);
24104            this.wrapper.addEventListener('DOMMouseScroll', this);
24105        };
24106
24107        /*
24108        * Turns iScroll `mousewheel` option on dynamically
24109        * https://github.com/cubiq/iscroll/issues/1036
24110        */
24111        IScroll.prototype.wheelOff = function () {
24112            this.wrapper.removeEventListener('wheel', this);
24113            this.wrapper.removeEventListener('mousewheel', this);
24114            this.wrapper.removeEventListener('DOMMouseScroll', this);
24115        };
24116    }
24117
24118    /**
24119     * An object to handle overflow scrolling.
24120     * This uses jquery.slimScroll to accomplish overflow scrolling.
24121     * It is possible to pass in an alternate scrollOverflowHandler
24122     * to the fullpage.js option that implements the same functions
24123     * as this handler.
24124     *
24125     * @type {Object}
24126     */
24127    var iscrollHandler = {
24128        refreshId: null,
24129        iScrollInstances: [],
24130
24131        // Enables or disables the mouse wheel for the active section or all slides in it
24132        toggleWheel: function(value){
24133            var scrollable = $(SECTION_ACTIVE_SEL).find(SCROLLABLE_SEL);
24134            scrollable.each(function(){
24135                var iScrollInstance = $(this).data('iscrollInstance');
24136                if(typeof iScrollInstance !== 'undefined' && iScrollInstance){
24137                    if(value){
24138                        iScrollInstance.wheelOn();
24139                    }
24140                    else{
24141                        iScrollInstance.wheelOff();
24142                    }
24143                }
24144            });
24145        },
24146
24147        /**
24148        * Turns off iScroll for the destination section.
24149        * When scrolling very fast on some trackpads (and Apple laptops) the inertial scrolling would
24150        * scroll the destination section/slide before the sections animations ends.
24151        */
24152        onLeave: function(){
24153            iscrollHandler.toggleWheel(false);
24154        },
24155
24156        // Turns off iScroll for the leaving section
24157        beforeLeave: function(){
24158            iscrollHandler.onLeave()
24159        },
24160
24161        // Turns on iScroll on section load
24162        afterLoad: function(){
24163            iscrollHandler.toggleWheel(true);
24164        },
24165
24166        /**
24167         * Called when overflow scrolling is needed for a section.
24168         *
24169         * @param  {Object} element      jQuery object containing current section
24170         * @param  {Number} scrollHeight Current window height in pixels
24171         */
24172        create: function(element, scrollHeight) {
24173            var scrollable = element.find(SCROLLABLE_SEL);
24174
24175            scrollable.height(scrollHeight);
24176            scrollable.each(function() {
24177                var $this = $(this);
24178                var iScrollInstance = $this.data('iscrollInstance');
24179                if (iScrollInstance) {
24180                    $.each(iscrollHandler.iScrollInstances, function(){
24181                        $(this).destroy();
24182                    });
24183                }
24184
24185                iScrollInstance = new IScroll($this.get(0), iscrollOptions);
24186                iscrollHandler.iScrollInstances.push(iScrollInstance);
24187
24188                //off by default until the section gets active
24189                iScrollInstance.wheelOff();
24190
24191                $this.data('iscrollInstance', iScrollInstance);
24192            });
24193        },
24194
24195        /**
24196         * Return a boolean depending on whether the scrollable element is a
24197         * the end or at the start of the scrolling depending on the given type.
24198         *
24199         * @param  {String}  type       Either 'top' or 'bottom'
24200         * @param  {Object}  scrollable jQuery object for the scrollable element
24201         * @return {Boolean}
24202         */
24203        isScrolled: function(type, scrollable) {
24204            var scroller = scrollable.data('iscrollInstance');
24205
24206            //no scroller?
24207            if (!scroller) {
24208                return true;
24209            }
24210
24211            if (type === 'top') {
24212                return scroller.y >= 0 && !scrollable.scrollTop();
24213            } else if (type === 'bottom') {
24214                return (0 - scroller.y) + scrollable.scrollTop() + 1 + scrollable.innerHeight() >= scrollable[0].scrollHeight;
24215            }
24216        },
24217
24218        /**
24219         * Returns the scrollable element for the given section.
24220         * If there are landscape slides, will only return a scrollable element
24221         * if it is in the active slide.
24222         *
24223         * @param  {Object}  activeSection jQuery object containing current section
24224         * @return {Boolean}
24225         */
24226        scrollable: function(activeSection){
24227            // if there are landscape slides, we check if the scrolling bar is in the current one or not
24228            if (activeSection.find(SLIDES_WRAPPER_SEL).length) {
24229                return activeSection.find(SLIDE_ACTIVE_SEL).find(SCROLLABLE_SEL);
24230            }
24231            return activeSection.find(SCROLLABLE_SEL);
24232        },
24233
24234        /**
24235         * Returns the scroll height of the wrapped content.
24236         * If this is larger than the window height minus section padding,
24237         * overflow scrolling is needed.
24238         *
24239         * @param  {Object} element jQuery object containing current section
24240         * @return {Number}
24241         */
24242        scrollHeight: function(element) {
24243            return element.find(SCROLLABLE_SEL).children().first().get(0).scrollHeight;
24244        },
24245
24246        /**
24247         * Called when overflow scrolling is no longer needed for a section.
24248         *
24249         * @param  {Object} element      jQuery object containing current section
24250         */
24251        remove: function(element) {
24252            var scrollable = element.find(SCROLLABLE_SEL);
24253            if (scrollable.length) {
24254                var iScrollInstance = scrollable.data('iscrollInstance');
24255                iScrollInstance.destroy();
24256
24257                scrollable.data('iscrollInstance', null);
24258            }
24259            element.find(SCROLLABLE_SEL).children().first().children().first().unwrap().unwrap();
24260        },
24261
24262        /**
24263         * Called when overflow scrolling has already been setup but the
24264         * window height has potentially changed.
24265         *
24266         * @param  {Object}
vendor: 16,384 bytes, lines 24266-24809
24266 element      jQuery object containing current section
24267         * @param  {Number} scrollHeight Current window height in pixels
24268         */
24269        update: function(element, scrollHeight) {
24270            //using a timeout in order to execute the refresh function only once when `update` is called multiple times in a
24271            //short period of time.
24272            //it also comes on handy because iScroll requires the use of timeout when using `refresh`.
24273            clearTimeout(iscrollHandler.refreshId);
24274            iscrollHandler.refreshId = setTimeout(function(){
24275                $.each(iscrollHandler.iScrollInstances, function(){
24276                    $(this).get(0).refresh();
24277                });
24278            }, 150);
24279
24280            //updating the wrappers height
24281            element.find(SCROLLABLE_SEL).css('height', scrollHeight + 'px').parent().css('height', scrollHeight + 'px');
24282        },
24283
24284        /**
24285         * Called to get any additional elements needed to wrap the section
24286         * content in order to facilitate overflow scrolling.
24287         *
24288         * @return {String|Object} Can be a string containing HTML,
24289         *                         a DOM element, or jQuery object.
24290         */
24291        wrapContent: function() {
24292            return '<div class="' + SCROLLABLE + '"><div class="fp-scroller"></div></div>';
24293        }
24294    };
24295});
24296
24297/* ========================================================================
24298 * Bootstrap: collapse.js v3.1.1
24299 * http://getbootstrap.com/javascript/#collapse
24300 * ========================================================================
24301 * Copyright 2011-2014 Twitter, Inc.
24302 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
24303 * ======================================================================== */
24304
24305
24306+function ($) {
24307  'use strict';
24308
24309  // COLLAPSE PUBLIC CLASS DEFINITION
24310  // ================================
24311
24312  var Collapse = function (element, options) {
24313    this.$element      = $(element)
24314    this.options       = $.extend({}, Collapse.DEFAULTS, options)
24315    this.transitioning = null
24316
24317    if (this.options.parent) this.$parent = $(this.options.parent)
24318    if (this.options.toggle) this.toggle()
24319  }
24320
24321  Collapse.DEFAULTS = {
24322    toggle: true
24323  }
24324
24325  Collapse.prototype.dimension = function () {
24326    var hasWidth = this.$element.hasClass('width')
24327    return hasWidth ? 'width' : 'height'
24328  }
24329
24330  Collapse.prototype.show = function () {
24331    if (this.transitioning || this.$element.hasClass('in')) return
24332
24333    var startEvent = $.Event('show.bs.collapse')
24334    this.$element.trigger(startEvent)
24335    if (startEvent.isDefaultPrevented()) return
24336
24337    var actives = this.$parent && this.$parent.find('> .panel > .in')
24338
24339    if (actives && actives.length) {
24340      var hasData = actives.data('bs.collapse')
24341      if (hasData && hasData.transitioning) return
24342      actives.collapse('hide')
24343      hasData || actives.data('bs.collapse', null)
24344    }
24345
24346    var dimension = this.dimension()
24347
24348    this.$element
24349      .removeClass('collapse')
24350      .addClass('collapsing')[dimension](0)
24351
24352    this.transitioning = 1
24353
24354    var complete = function (e) {
24355      if (e && e.target != this.$element[0]) return
24356      this.$element
24357        .removeClass('collapsing')
24358        .addClass('collapse in')[dimension]('auto')
24359      this.transitioning = 0
24360      this.$element.trigger('shown.bs.collapse')
24361    }
24362
24363    if (!$.support.transition) return complete.call(this)
24364
24365    var scrollSize = $.camelCase(['scroll', dimension].join('-'))
24366
24367    this.$element
24368      .one($.support.transition.end, $.proxy(complete, this))
24369      .emulateTransitionEnd(350)[dimension](this.$element[0][scrollSize])
24370  }
24371
24372  Collapse.prototype.hide = function () {
24373    if (this.transitioning || !this.$element.hasClass('in')) return
24374
24375    var startEvent = $.Event('hide.bs.collapse')
24376    this.$element.trigger(startEvent)
24377    if (startEvent.isDefaultPrevented()) return
24378
24379    var dimension = this.dimension()
24380
24381    this.$element[dimension](this.$element[dimension]())[0].offsetHeight
24382
24383    this.$element
24384      .addClass('collapsing')
24385      .removeClass('collapse')
24386      .removeClass('in')
24387
24388    this.transitioning = 1
24389
24390    var complete = function (e) {
24391      if (e && e.target != this.$element[0]) return
24392      this.transitioning = 0
24393      this.$element
24394        .trigger('hidden.bs.collapse')
24395        .removeClass('collapsing')
24396        .addClass('collapse')
24397    }
24398
24399    if (!$.support.transition) return complete.call(this)
24400
24401    this.$element
24402      [dimension](0)
24403      .one($.support.transition.end, $.proxy(complete, this))
24404      .emulateTransitionEnd(350)
24405  }
24406
24407  Collapse.prototype.toggle = function () {
24408    this[this.$element.hasClass('in') ? 'hide' : 'show']()
24409  }
24410
24411
24412  // COLLAPSE PLUGIN DEFINITION
24413  // ==========================
24414
24415  var old = $.fn.collapse
24416
24417  $.fn.collapse = function (option) {
24418    return this.each(function () {
24419      var $this   = $(this)
24420      var data    = $this.data('bs.collapse')
24421      var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option == 'object' && option)
24422
24423      if (!data && options.toggle && option == 'show') option = !option
24424      if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)))
24425      if (typeof option == 'string') data[option]()
24426    })
24427  }
24428
24429  $.fn.collapse.Constructor = Collapse
24430
24431
24432  // COLLAPSE NO CONFLICT
24433  // ====================
24434
24435  $.fn.collapse.noConflict = function () {
24436    $.fn.collapse = old
24437    return this
24438  }
24439
24440
24441  // COLLAPSE DATA-API
24442  // =================
24443
24444  $(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function (e) {
24445    var $this   = $(this), href
24446    var target  = $this.attr('data-target')
24447        || e.preventDefault()
24448        || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '') //strip for ie7
24449    //Edited by Uncode to replace ID in panel [START]
24450    var _target = href.replace(/^#/, "");
24451    if ( $('[data-id="' + _target + '"]').length ) {
24452      var $target = $('[data-id="' + _target + '"]')
24453    } else {
24454      var $target = $(target)
24455    }
24456    //Edited by Uncode to replace ID in panel [END]
24457    var data    = $target.data('bs.collapse')
24458    var option  = data ? 'toggle' : $this.data()
24459    var parent  = $this.attr('data-parent')
24460    var $parent = parent && $(parent)
24461
24462    if (!data || !data.transitioning) {
24463      if ($parent) $parent.find('[data-toggle="collapse"][data-parent="' + parent + '"]').not($this).addClass('collapsed')
24464      $this[$target.hasClass('in') ? 'addClass' : 'removeClass']('collapsed')
24465    }
24466
24467    $target.collapse(option)
24468  })
24469
24470}(jQuery);
24471
24472/* ========================================================================
24473 * Bootstrap: tab.js v3.1.1
24474 * http://getbootstrap.com/javascript/#tabs
24475 * ========================================================================
24476 * Copyright 2011-2014 Twitter, Inc.
24477 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
24478 * ======================================================================== */
24479
24480
24481+function ($) {
24482  'use strict';
24483
24484  // TAB CLASS DEFINITION
24485  // ====================
24486
24487  var Tab = function (element) {
24488    this.element = $(element)
24489  }
24490
24491  Tab.prototype.show = function () {
24492    var $this    = this.element
24493    var $ul      = $this.closest('ul:not(.dropdown-menu)')
24494    var selector = $this.data('target')
24495
24496    if (!selector) {
24497      selector = $this.attr('href')
24498      selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7
24499    }
24500
24501    if ($this.parent('li').hasClass('active')) return
24502
24503    var previous = $ul.find('.active:last a')[0]
24504    var e        = $.Event('show.bs.tab', {
24505      relatedTarget: previous
24506    })
24507
24508    $this.trigger(e)
24509
24510    if (e.isDefaultPrevented()) return
24511
24512    //Edited by Uncode to replace ID in panel [START]
24513    var _target = selector.replace(/^#/, "");
24514    if ( $('[data-id="' + _target + '"]').length )
24515      var $target = $('[data-id="' + _target + '"]')
24516    else
24517      var $target = $(selector)
24518    //Edited by Uncode to replace ID in panel [END]
24519
24520    this.activate($this.parent('li'), $ul)
24521    this.activate($target, $target.parent(), function () {
24522      $this.trigger({
24523        type: 'shown.bs.tab',
24524        relatedTarget: previous
24525      })
24526    })
24527  }
24528
24529  Tab.prototype.activate = function (element, container, callback) {
24530    var $active    = container.find('> .active')
24531    var transition = callback
24532      && $.support.transition
24533      && $active.hasClass('fade')
24534
24535    function next() {
24536      $active
24537        .removeClass('active')
24538        .find('> .dropdown-menu > .active')
24539        .removeClass('active')
24540
24541      element.addClass('active')
24542
24543      if (transition) {
24544        element[0].offsetWidth // reflow for transition
24545        element.addClass('in')
24546      } else {
24547        element.removeClass('fade')
24548      }
24549
24550      if (element.parent('.dropdown-menu')) {
24551        element.closest('li.dropdown').addClass('active')
24552      }
24553
24554      callback && callback()
24555    }
24556
24557    transition ?
24558      $active
24559        .one($.support.transition.end, next)
24560        .emulateTransitionEnd(150) :
24561      next()
24562
24563    $active.removeClass('in')
24564  }
24565
24566
24567  // TAB PLUGIN DEFINITION
24568  // =====================
24569
24570  var old = $.fn.tab
24571
24572  $.fn.tab = function ( option ) {
24573    return this.each(function () {
24574      var $this = $(this)
24575      var data  = $this.data('bs.tab')
24576
24577      if (!data) $this.data('bs.tab', (data = new Tab(this)))
24578      if (typeof option == 'string') data[option]()
24579    })
24580  }
24581
24582  $.fn.tab.Constructor = Tab
24583
24584
24585  // TAB NO CONFLICT
24586  // ===============
24587
24588  $.fn.tab.noConflict = function () {
24589    $.fn.tab = old
24590    return this
24591  }
24592
24593
24594  // TAB DATA-API
24595  // ============
24596
24597  $(document).on('click.bs.tab.data-api', '[data-toggle="tab"], [data-toggle="pill"]', function (e) {
24598    e.preventDefault()
24599    $(this).tab('show')
24600  })
24601
24602}(jQuery);
24603
24604/* ========================================================================
24605 * Bootstrap: tooltip.js v3.1.1
24606 * http://getbootstrap.com/javascript/#tooltip
24607 * Inspired by the original jQuery.tipsy by Jason Frame
24608 * ========================================================================
24609 * Copyright 2011-2014 Twitter, Inc.
24610 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
24611 * ======================================================================== */
24612
24613
24614+function ($) {
24615  'use strict';
24616
24617  // TOOLTIP PUBLIC CLASS DEFINITION
24618  // ===============================
24619
24620  var Tooltip = function (element, options) {
24621    this.type       =
24622    this.options    =
24623    this.enabled    =
24624    this.timeout    =
24625    this.hoverState =
24626    this.$element   = null
24627
24628    this.init('tooltip', element, options)
24629  }
24630
24631  Tooltip.DEFAULTS = {
24632    animation: true,
24633    placement: 'top',
24634    selector: false,
24635    template: '<div class="tooltip" role="tooltip"><div class="tooltip-arrow"></div><div class="tooltip-inner"></div></div>',
24636    trigger: 'hover focus',
24637    title: '',
24638    delay: 0,
24639    html: false,
24640    container: false,
24641    viewport: {
24642      selector: 'body',
24643      padding: 0
24644    }
24645  }
24646
24647  Tooltip.prototype.init = function (type, element, options) {
24648    this.enabled   = true
24649    this.type      = type
24650    this.$element  = $(element)
24651    this.options   = this.getOptions(options)
24652    this.$viewport = this.options.viewport && $(this.options.viewport.selector || this.options.viewport)
24653
24654    var triggers = this.options.trigger.split(' ')
24655
24656    for (var i = triggers.length; i--;) {
24657      var trigger = triggers[i]
24658
24659      if (trigger == 'click') {
24660        this.$element.on('click.' + this.type, this.options.selector, $.proxy(this.toggle, this))
24661      } else if (trigger != 'manual') {
24662        var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin'
24663        var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout'
24664
24665        this.$element.on(eventIn  + '.' + this.type, this.options.selector, $.proxy(this.enter, this))
24666        this.$element.on(eventOut + '.' + this.type, this.options.selector, $.proxy(this.leave, this))
24667      }
24668    }
24669
24670    this.options.selector ?
24671      (this._options = $.extend({}, this.options, { trigger: 'manual', selector: '' })) :
24672      this.fixTitle()
24673  }
24674
24675  Tooltip.prototype.getDefaults = function () {
24676    return Tooltip.DEFAULTS
24677  }
24678
24679  Tooltip.prototype.getOptions = function (options) {
24680    options = $.extend({}, this.getDefaults(), this.$element.data(), options)
24681
24682    if (options.delay && typeof options.delay == 'number') {
24683      options.delay = {
24684        show: options.delay,
24685        hide: options.delay
24686      }
24687    }
24688
24689    return options
24690  }
24691
24692  Tooltip.prototype.getDelegateOptions = function () {
24693    var options  = {}
24694    var defaults = this.getDefaults()
24695
24696    this._options && $.each(this._options, function (key, value) {
24697      if (defaults[key] != value) options[key] = value
24698    })
24699
24700    return options
24701  }
24702
24703  Tooltip.prototype.enter = function (obj) {
24704    var self = obj instanceof this.constructor ?
24705      obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
24706
24707    clearTimeout(self.timeout)
24708
24709    self.hoverState = 'in'
24710
24711    if (!self.options.delay || !self.options.delay.show) return self.show()
24712
24713    self.timeout = setTimeout(function () {
24714      if (self.hoverState == 'in') self.show()
24715    }, self.options.delay.show)
24716  }
24717
24718  Tooltip.prototype.leave = function (obj) {
24719    var self = obj instanceof this.constructor ?
24720      obj : $(obj.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type)
24721
24722    clearTimeout(self.timeout)
24723
24724    self.hoverState = 'out'
24725
24726    if (!self.options.delay || !self.options.delay.hide) return self.hide()
24727
24728    self.timeout = setTimeout(function () {
24729      if (self.hoverState == 'out') self.hide()
24730    }, self.options.delay.hide)
24731  }
24732
24733  Tooltip.prototype.show = function () {
24734    var e = $.Event('show.bs.' + this.type)
24735
24736    if (this.hasContent() && this.enabled) {
24737      this.$element.trigger(e)
24738
24739      if (e.isDefaultPrevented()) return
24740      var that = this;
24741
24742      var $tip = this.tip()
24743
24744      this.setContent()
24745
24746      if (this.options.animation) $tip.addClass('fade')
24747
24748      var placement = typeof this.options.placement == 'function' ?
24749        this.options.placement.call(this, $tip[0], this.$element[0]) :
24750        this.options.placement
24751
24752      var autoToken = /\s?auto?\s?/i
24753      var autoPlace = autoToken.test(placement)
24754      if (autoPlace) placement = placement.replace(autoToken, '') || 'top'
24755
24756      $tip
24757        .detach()
24758        .css({ top: 0, left: 0, display: 'block' })
24759        .addClass(placement)
24760
24761      this.options.container ? $tip.appendTo($(document).find(this.options.container)) : $tip.insertAfter(this.$element)
24762
24763      var pos          = this.getPosition()
24764      var actualWidth  = $tip[0].offsetWidth
24765      var actualHeight = $tip[0].offsetHeight
24766
24767      if (autoPlace) {
24768        var orgPlacement = placement
24769        var $parent      = this.$element.parent()
24770        var parentDim    = this.getPosition($parent)
24771
24772        placement = placement == 'bottom' && pos.top   + pos.height       + actualHeight - parentDim.scroll > parentDim.height ? 'top'    :
24773                    placement == 'top'    && pos.top   - parentDim.scroll - actualHeight < 0                                   ? 'bottom' :
24774                    placement == 'right'  && pos.right + actualWidth      > parentDim.width                                    ? 'left'   :
24775                    placement == 'left'   && pos.left  - actualWidth      < parentDim.left                                     ? 'right'  :
24776                    placement
24777
24778        $tip
24779          .removeClass(orgPlacement)
24780          .addClass(placement)
24781      }
24782
24783      var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight)
24784
24785      this.applyPlacement(calculatedOffset, placement)
24786      this.hoverState = null
24787
24788      var complete = function() {
24789        that.$element.trigger('shown.bs.' + that.type)
24790      }
24791
24792      $.support.transition && this.$tip.hasClass('fade') ?
24793        $tip
24794          .one($.support.transition.end, complete)
24795          .emulateTransitionEnd(150) :
24796        complete()
24797    }
24798  }
24799
24800  Tooltip.prototype.applyPlacement = function (offset, placement) {
24801    var $tip   = this.tip()
24802    var width  = $tip[0].offsetWidth
24803    var height = $tip[0].offsetHeight
24804
24805    // manually read margins because getBoundingClientRect includes difference
24806    var marginTop = parseInt($tip.css('margin-top'), 10)
24807    var marginLeft = parseInt($tip.css('margin-left'), 10)
24808
24809    // we must check
24809 for NaN for ie 8/9
24810    if (isNaN(marginTop))  marginTop  = 0
24811    if (isNaN(marginLeft)) marginLeft = 0
24812
24813    offset.top  = offset.top  + marginTop
24814    offset.left = offset.left + marginLeft
24815
24816    // $.fn.offset doesn't round pixel values
24817    // so we use setOffset directly with our own function B-0
24818    $.offset.setOffset($tip[0], $.extend({
24819      using: function (props) {
24820        $tip.css({
24821          top: Math.round(props.top),
24822          left: Math.round(props.left)
24823        })
24824      }
24825    }, offset), 0)
24826
24827    $tip.addClass('in')
24828
24829    // check to see if placing tip in new offset caused the tip to resize itself
24830    var actualWidth  = $tip[0].offsetWidth
24831    var actualHeight = $tip[0].offsetHeight
24832
24833    if (placement == 'top' && actualHeight != height) {
24834      offset.top = offset.top + height - actualHeight
24835    }
24836
24837    var delta = this.getViewportAdjustedDelta(placement, offset, actualWidth, actualHeight)
24838
24839    if (delta.left) offset.left += delta.left
24840    else offset.top += delta.top
24841
24842    var arrowDelta          = delta.left ? delta.left * 2 - width + actualWidth : delta.top * 2 - height + actualHeight
24843    var arrowPosition       = delta.left ? 'left'        : 'top'
24844    var arrowOffsetPosition = delta.left ? 'offsetWidth' : 'offsetHeight'
24845
24846    $tip.offset(offset)
24847    this.replaceArrow(arrowDelta, $tip[0][arrowOffsetPosition], arrowPosition)
24848  }
24849
24850  Tooltip.prototype.replaceArrow = function (delta, dimension, position) {
24851    this.arrow().css(position, delta ? (50 * (1 - delta / dimension) + '%') : '')
24852  }
24853
24854  Tooltip.prototype.setContent = function () {
24855    var $tip  = this.tip()
24856    var title = this.getTitle()
24857
24858    $tip.find('.tooltip-inner')[this.options.html ? 'html' : 'text'](title)
24859    $tip.removeClass('fade in top bottom left right')
24860  }
24861
24862  Tooltip.prototype.hide = function () {
24863    var that = this
24864    var $tip = this.tip()
24865    var e    = $.Event('hide.bs.' + this.type)
24866
24867    function complete() {
24868      if (that.hoverState != 'in') $tip.detach()
24869      that.$element.trigger('hidden.bs.' + that.type)
24870    }
24871
24872    this.$element.trigger(e)
24873
24874    if (e.isDefaultPrevented()) return
24875
24876    $tip.removeClass('in')
24877
24878    $.support.transition && this.$tip.hasClass('fade') ?
24879      $tip
24880        .one($.support.transition.end, complete)
24881        .emulateTransitionEnd(150) :
24882      complete()
24883
24884    this.hoverState = null
24885
24886    return this
24887  }
24888
24889  Tooltip.prototype.fixTitle = function () {
24890    var $e = this.$element
24891    if ($e.attr('title') || typeof($e.attr('data-original-title')) != 'string') {
24892      $e.attr('data-original-title', $e.attr('title') || '').attr('title', '')
24893    }
24894  }
24895
24896  Tooltip.prototype.hasContent = function () {
24897    return this.getTitle()
24898  }
24899
24900  Tooltip.prototype.getPosition = function ($element) {
24901    $element   = $element || this.$element
24902    var el     = $element[0]
24903    var isBody = el.tagName == 'BODY'
24904    return $.extend({}, (typeof el.getBoundingClientRect == 'function') ? el.getBoundingClientRect() : null, {
24905      scroll: isBody ? document.documentElement.scrollTop || document.body.scrollTop : $element.scrollTop(),
24906      width:  isBody ? $(window).width()  : $element.outerWidth(),
24907      height: isBody ? $(window).height() : $element.outerHeight()
24908    }, isBody ? {top: 0, left: 0} : $element.offset())
24909  }
24910
24911  Tooltip.prototype.getCalculatedOffset = function (placement, pos, actualWidth, actualHeight) {
24912    return placement == 'bottom' ? { top: pos.top + pos.height,   left: pos.left + pos.width / 2 - actualWidth / 2  } :
24913           placement == 'top'    ? { top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2  } :
24914           placement == 'left'   ? { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth } :
24915        /* placement == 'right' */ { top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width   }
24916
24917  }
24918
24919  Tooltip.prototype.getViewportAdjustedDelta = function (placement, pos, actualWidth, actualHeight) {
24920    var delta = { top: 0, left: 0 }
24921    if (!this.$viewport) return delta
24922
24923    var viewportPadding = this.options.viewport && this.options.viewport.padding || 0
24924    var viewportDimensions = this.getPosition(this.$viewport)
24925
24926    if (/right|left/.test(placement)) {
24927      var topEdgeOffset    = pos.top - viewportPadding - viewportDimensions.scroll
24928      var bottomEdgeOffset = pos.top + viewportPadding - viewportDimensions.scroll + actualHeight
24929      if (topEdgeOffset < viewportDimensions.top) { // top overflow
24930        delta.top = viewportDimensions.top - topEdgeOffset
24931      } else if (bottomEdgeOffset > viewportDimensions.top + viewportDimensions.height) { // bottom overflow
24932        delta.top = viewportDimensions.top + viewportDimensions.height - bottom
24932EdgeOffset
24933      }
24934    } else {
24935      var leftEdgeOffset  = pos.left - viewportPadding
24936      var rightEdgeOffset = pos.left + viewportPadding + actualWidth
24937      if (leftEdgeOffset < viewportDimensions.left) { // left overflow
24938        delta.left = viewportDimensions.left - leftEdgeOffset
24939      } else if (rightEdgeOffset > viewportDimensions.width) { // right overflow
24940        delta.left = viewportDimensions.left + viewportDimensions.width - rightEdgeOffset
24941      }
24942    }
24943
24944    return delta
24945  }
24946
24947  Tooltip.prototype.getTitle = function () {
24948    var title
24949    var $e = this.$element
24950    var o  = this.options
24951
24952    title = $e.attr('data-original-title')
24953      || (typeof o.title == 'function' ? o.title.call($e[0]) :  o.title)
24954
24955    return title
24956  }
24957
24958  Tooltip.prototype.tip = function () {
24959    return this.$tip = this.$tip || $(this.options.template)
24960  }
24961
24962  Tooltip.prototype.arrow = function () {
24963    return this.$arrow = this.$arrow || this.tip().find('.tooltip-arrow')
24964  }
24965
24966  Tooltip.prototype.validate = function () {
24967    if (!this.$element[0].parentNode) {
24968      this.hide()
24969      this.$element = null
24970      this.options  = null
24971    }
24972  }
24973
24974  Tooltip.prototype.enable = function () {
24975    this.enabled = true
24976  }
24977
24978  Tooltip.prototype.disable = function () {
24979    this.enabled = false
24980  }
24981
24982  Tooltip.prototype.toggleEnabled = function () {
24983    this.enabled = !this.enabled
24984  }
24985
24986  Tooltip.prototype.toggle = function (e) {
24987    var self = e ? $(e.currentTarget)[this.type](this.getDelegateOptions()).data('bs.' + this.type) : this
24988    self.tip().hasClass('in') ? self.leave(self) : self.enter(self)
24989  }
24990
24991  Tooltip.prototype.destroy = function () {
24992    clearTimeout(this.timeout)
24993    this.hide().$element.off('.' + this.type).removeData('bs.' + this.type)
24994  }
24995
24996
24997  // TOOLTIP PLUGIN DEFINITION
24998  // =========================
24999
25000  var old = $.fn.tooltip
25001
25002  $.fn.tooltip = function (option) {
25003    return this.each(function () {
25004      var $this   = $(this)
25005      var data    = $this.data('bs.tooltip')
25006      var options = typeof option == 'object' && option
25007
25008      if (!data && option == 'destroy') return
25009      if (!data) $this.data('bs.tooltip', (data = new Tooltip(this, options)))
25010      if (typeof option == 'string') data[option]()
25011    })
25012  }
25013
25014  $.fn.tooltip.Constructor = Tooltip
25015
25016
25017  // TOOLTIP NO CONFLICT
25018  // ===================
25019
25020  $.fn.tooltip.noConflict = function () {
25021    $.fn.tooltip = old
25022    return this
25023  }
25024
25025}(jQuery);
25026
25027/* ========================================================================
25028 * Bootstrap: transition.js v3.1.1
25029 * http://getbootstrap.com/javascript/#transitions
25030 * ========================================================================
25031 * Copyright 2011-2014 Twitter, Inc.
25032 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
25033 * ======================================================================== */
25034
25035
25036+function ($) {
25037  'use strict';
25038
25039  // CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
25040  // ============================================================
25041
25042  function transitionEnd() {
25043    var el = document.createElement('bootstrap')
25044
25045    var transEndEventNames = {
25046      WebkitTransition : 'webkitTransitionEnd',
25047      MozTransition    : 'transitionend',
25048      OTransition      : 'oTransitionEnd otransitionend',
25049      transition       : 'transitionend'
25050    }
25051
25052    for (var name in transEndEventNames) {
25053      if (el.style[name] !== undefined) {
25054        return { end: transEndEventNames[name] }
25055      }
25056    }
25057
25058    return false // explicit for ie8 (  ._.)
25059  }
25060
25061  // http://blog.alexmaccaw.com/css-transitions
25062  $.fn.emulateTransitionEnd = function (duration) {
25063    var called = false, $el = this
25064    $(this).one($.support.transition.end, function () { called = true })
25065    var callback = function () { if (!called) $($el).trigger($.support.transition.end) }
25066    setTimeout(callback, duration)
25067    return this
25068  }
25069
25070  $(function () {
25071    $.support.transition = transitionEnd()
25072  })
25073
25074}(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.