PageSourceSearch

https://www.chongmd.com/wp-content/themes/fmBuildr2.0/assets/js/scripts.js?ver=1790673030

js chongmd.com collected 2026-09-29 09:10:52 UTC 250,830 bytes, 9,440 lines download raw bytes

vendor: 15,719 bytes, lines 1-650
1/*!
2 * Flickity PACKAGED v2.0.5
3 * Touch, responsive, flickable carousels
4 *
5 * Licensed GPLv3 for open source use
6 * or Flickity Commercial License for commercial use
7 *
8 * http://flickity.metafizzy.co
9 * Copyright 2016 Metafizzy
10 */
11
12/**
13 * Bridget makes jQuery widgets
14 * v2.0.1
15 * MIT license
16 */
17
18/* jshint browser: true, strict: true, undef: true, unused: true */
19
20( function( window, factory ) {
21  // universal module definition
22  /*jshint strict: false */ /* globals define, module, require */
23  if ( typeof define == 'function' && define.amd ) {
24    // AMD
25    define( 'jquery-bridget/jquery-bridget',[ 'jquery' ], function( jQuery ) {
26      return factory( window, jQuery );
27    });
28  } else if ( typeof module == 'object' && module.exports ) {
29    // CommonJS
30    module.exports = factory(
31      window,
32      require('jquery')
33    );
34  } else {
35    // browser global
36    window.jQueryBridget = factory(
37      window,
38      window.jQuery
39    );
40  }
41
42}( window, function factory( window, jQuery ) {
43'use strict';
44
45// ----- utils ----- //
46
47var arraySlice = Array.prototype.slice;
48
49// helper function for logging errors
50// $.error breaks jQuery chaining
51var console = window.console;
52var logError = typeof console == 'undefined' ? function() {} :
53  function( message ) {
54    console.error( message );
55  };
56
57// ----- jQueryBridget ----- //
58
59function jQueryBridget( namespace, PluginClass, $ ) {
60  $ = $ || jQuery || window.jQuery;
61  if ( !$ ) {
62    return;
63  }
64
65  // add option method -> $().plugin('option', {...})
66  if ( !PluginClass.prototype.option ) {
67    // option setter
68    PluginClass.prototype.option = function( opts ) {
69      // bail out if not an object
70      if ( !$.isPlainObject( opts ) ){
71        return;
72      }
73      this.options = $.extend( true, this.options, opts );
74    };
75  }
76
77  // make jQuery plugin
78  $.fn[ namespace ] = function( arg0 /*, arg1 */ ) {
79    if ( typeof arg0 == 'string' ) {
80      // method call $().plugin( 'methodName', { options } )
81      // shift arguments by 1
82      var args = arraySlice.call( arguments, 1 );
83      return methodCall( this, arg0, args );
84    }
85    // just $().plugin({ options })
86    plainCall( this, arg0 );
87    return this;
88  };
89
90  // $().plugin('methodName')
91  function methodCall( $elems, methodName, args ) {
92    var returnValue;
93    var pluginMethodStr = '$().' + namespace + '("' + methodName + '")';
94
95    $elems.each( function( i, elem ) {
96      // get instance
97      var instance = $.data( elem, namespace );
98      if ( !instance ) {
99        logError( namespace + ' not initialized. Cannot call methods, i.e. ' +
100          pluginMethodStr );
101        return;
102      }
103
104      var method = instance[ methodName ];
105      if ( !method || methodName.charAt(0) == '_' ) {
106        logError( pluginMethodStr + ' is not a valid method' );
107        return;
108      }
109
110      // apply method, get return value
111      var value = method.apply( instance, args );
112      // set return value if value is returned, use only first value
113      returnValue = returnValue === undefined ? value : returnValue;
114    });
115
116    return returnValue !== undefined ? returnValue : $elems;
117  }
118
119  function plainCall( $elems, options ) {
120    $elems.each( function( i, elem ) {
121      var instance = $.data( elem, namespace );
122      if ( instance ) {
123        // set options & init
124        instance.option( options );
125        instance._init();
126      } else {
127        // initialize new instance
128        instance = new PluginClass( elem, options );
129        $.data( elem, namespace, instance );
130      }
131    });
132  }
133
134  updateJQuery( $ );
135
136}
137
138// ----- updateJQuery ----- //
139
140// set $.bridget for v1 backwards compatibility
141function updateJQuery( $ ) {
142  if ( !$ || ( $ && $.bridget ) ) {
143    return;
144  }
145  $.bridget = jQueryBridget;
146}
147
148updateJQuery( jQuery || window.jQuery );
149
150// -----  ----- //
151
152return jQueryBridget;
153
154}));
155
156/**
157 * EvEmitter v1.0.3
158 * Lil' event emitter
159 * MIT License
160 */
161
162/* jshint unused: true, undef: true, strict: true */
163
164( function( global, factory ) {
165  // universal module definition
166  /* jshint strict: false */ /* globals define, module, window */
167  if ( typeof define == 'function' && define.amd ) {
168    // AMD - RequireJS
169    define( 'ev-emitter/ev-emitter',factory );
170  } else if ( typeof module == 'object' && module.exports ) {
171    // CommonJS - Browserify, Webpack
172    module.exports = factory();
173  } else {
174    // Browser globals
175    global.EvEmitter = factory();
176  }
177
178}( typeof window != 'undefined' ? window : this, function() {
179
180
181
182function EvEmitter() {}
183
184var proto = EvEmitter.prototype;
185
186proto.on = function( eventName, listener ) {
187  if ( !eventName || !listener ) {
188    return;
189  }
190  // set events hash
191  var events = this._events = this._events || {};
192  // set listeners array
193  var listeners = events[ eventName ] = events[ eventName ] || [];
194  // only add once
195  if ( listeners.indexOf( listener ) == -1 ) {
196    listeners.push( listener );
197  }
198
199  return this;
200};
201
202proto.once = function( eventName, listener ) {
203  if ( !eventName || !listener ) {
204    return;
205  }
206  // add event
207  this.on( eventName, listener );
208  // set once flag
209  // set onceEvents hash
210  var onceEvents = this._onceEvents = this._onceEvents || {};
211  // set onceListeners object
212  var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {};
213  // set flag
214  onceListeners[ listener ] = true;
215
216  return this;
217};
218
219proto.off = function( eventName, listener ) {
220  var listeners = this._events && this._events[ eventName ];
221  if ( !listeners || !listeners.length ) {
222    return;
223  }
224  var index = listeners.indexOf( listener );
225  if ( index != -1 ) {
226    listeners.splice( index, 1 );
227  }
228
229  return this;
230};
231
232proto.emitEvent = function( eventName, args ) {
233  var listeners = this._events && this._events[ eventName ];
234  if ( !listeners || !listeners.length ) {
235    return;
236  }
237  var i = 0;
238  var listener = listeners[i];
239  args = args || [];
240  // once stuff
241  var onceListeners = this._onceEvents && this._onceEvents[ eventName ];
242
243  while ( listener ) {
244    var isOnce = onceListeners && onceListeners[ listener ];
245    if ( isOnce ) {
246      // remove listener
247      // remove before trigger to prevent recursion
248      this.off( eventName, listener );
249      // unset once flag
250      delete onceListeners[ listener ];
251    }
252    // trigger listener
253    listener.apply( this, args );
254    // get next listener
255    i += isOnce ? 0 : 1;
256    listener = listeners[i];
257  }
258
259  return this;
260};
261
262return EvEmitter;
263
264}));
265
266/*!
267 * getSize v2.0.2
268 * measure size of elements
269 * MIT license
270 */
271
272/*jshint browser: true, strict: true, undef: true, unused: true */
273/*global define: false, module: false, console: false */
274
275( function( window, factory ) {
276  'use strict';
277
278  if ( typeof define == 'function' && define.amd ) {
279    // AMD
280    define( 'get-size/get-size',[],function() {
281      return factory();
282    });
283  } else if ( typeof module == 'object' && module.exports ) {
284    // CommonJS
285    module.exports = factory();
286  } else {
287    // browser global
288    window.getSize = factory();
289  }
290
291})( window, function factory() {
292'use strict';
293
294// -------------------------- helpers -------------------------- //
295
296// get a number from a string, not a percentage
297function getStyleSize( value ) {
298  var num = parseFloat( value );
299  // not a percent like '100%', and a number
300  var isValid = value.indexOf('%') == -1 && !isNaN( num );
301  return isValid && num;
302}
303
304function noop() {}
305
306var logError = typeof console == 'undefined' ? noop :
307  function( message ) {
308    console.error( message );
309  };
310
311// -------------------------- measurements -------------------------- //
312
313var measurements = [
314  'paddingLeft',
315  'paddingRight',
316  'paddingTop',
317  'paddingBottom',
318  'marginLeft',
319  'marginRight',
320  'marginTop',
321  'marginBottom',
322  'borderLeftWidth',
323  'borderRightWidth',
324  'borderTopWidth',
325  'borderBottomWidth'
326];
327
328var measurementsLength = measurements.length;
329
330function getZeroSize() {
331  var size = {
332    width: 0,
333    height: 0,
334    innerWidth: 0,
335    innerHeight: 0,
336    outerWidth: 0,
337    outerHeight: 0
338  };
339  for ( var i=0; i < measurementsLength; i++ ) {
340    var measurement = measurements[i];
341    size[ measurement ] = 0;
342  }
343  return size;
344}
345
346// -------------------------- getStyle -------------------------- //
347
348/**
349 * getStyle, get style of element, check for Firefox bug
350 * https://bugzilla.mozilla.org/show_bug.cgi?id=548397
351 */
352function getStyle( elem ) {
353  var style = getComputedStyle( elem );
354  if ( !style ) {
355    logError( 'Style returned ' + style +
356      '. Are you running this code in a hidden iframe on Firefox? ' +
357      'See http://bit.ly/getsizebug1' );
358  }
359  return style;
360}
361
362// -------------------------- setup -------------------------- //
363
364var isSetup = false;
365
366var isBoxSizeOuter;
367
368/**
369 * setup
370 * check isBoxSizerOuter
371 * do on first getSize() rather than on page load for Firefox bug
372 */
373function setup() {
374  // setup once
375  if ( isSetup ) {
376    return;
377  }
378  isSetup = true;
379
380  // -------------------------- box sizing -------------------------- //
381
382  /**
383   * WebKit measures the outer-width on style.width on border-box elems
384   * IE & Firefox<29 measures the inner-width
385   */
386  var div = document.createElement('div');
387  div.style.width = '200px';
388  div.style.padding = '1px 2px 3px 4px';
389  div.style.borderStyle = 'solid';
390  div.style.borderWidth = '1px 2px 3px 4px';
391  div.style.boxSizing = 'border-box';
392
393  var body = document.body || document.documentElement;
394  body.appendChild( div );
395  var style = getStyle( div );
396
397  getSize.isBoxSizeOuter = isBoxSizeOuter = getStyleSize( style.width ) == 200;
398  body.removeChild( div );
399
400}
401
402// -------------------------- getSize -------------------------- //
403
404function getSize( elem ) {
405  setup();
406
407  // use querySeletor if elem is string
408  if ( typeof elem == 'string' ) {
409    elem = document.querySelector( elem );
410  }
411
412  // do not proceed on non-objects
413  if ( !elem || typeof elem != 'object' || !elem.nodeType ) {
414    return;
415  }
416
417  var style = getStyle( elem );
418
419  // if hidden, everything is 0
420  if ( style.display == 'none' ) {
421    return getZeroSize();
422  }
423
424  var size = {};
425  size.width = elem.offsetWidth;
426  size.height = elem.offsetHeight;
427
428  var isBorderBox = size.isBorderBox = style.boxSizing == 'border-box';
429
430  // get all measurements
431  for ( var i=0; i < measurementsLength; i++ ) {
432    var measurement = measurements[i];
433    var value = style[ measurement ];
434    var num = parseFloat( value );
435    // any 'auto', 'medium' value will be 0
436    size[ measurement ] = !isNaN( num ) ? num : 0;
437  }
438
439  var paddingWidth = size.paddingLeft + size.paddingRight;
440  var paddingHeight = size.paddingTop + size.paddingBottom;
441  var marginWidth = size.marginLeft + size.marginRight;
442  var marginHeight = size.marginTop + size.marginBottom;
443  var borderWidth = size.borderLeftWidth + size.borderRightWidth;
444  var borderHeight = size.borderTopWidth + size.borderBottomWidth;
445
446  var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter;
447
448  // overwrite width and height if we can get it from style
449  var styleWidth = getStyleSize( style.width );
450  if ( styleWidth !== false ) {
451    size.width = styleWidth +
452      // add padding and border unless it's already including it
453      ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth );
454  }
455
456  var styleHeight = getStyleSize( style.height );
457  if ( styleHeight !== false ) {
458    size.height = styleHeight +
459      // add padding and border unless it's already including it
460      ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight );
461  }
462
463  size.innerWidth = size.width - ( paddingWidth + borderWidth );
464  size.innerHeight = size.height - ( paddingHeight + borderHeight );
465
466  size.outerWidth = size.width + marginWidth;
467  size.outerHeight = size.height + marginHeight;
468
469  return size;
470}
471
472return getSize;
473
474});
475
476/**
477 * matchesSelector v2.0.1
478 * matchesSelector( element, '.selector' )
479 * MIT license
480 */
481
482/*jshint browser: true, strict: true, undef: true, unused: true */
483
484( function( window, factory ) {
485  /*global define: false, module: false */
486  'use strict';
487  // universal module definition
488  if ( typeof define == 'function' && define.amd ) {
489    // AMD
490    define( 'desandro-matches-selector/matches-selector',factory );
491  } else if ( typeof module == 'object' && module.exports ) {
492    // CommonJS
493    module.exports = factory();
494  } else {
495    // browser global
496    window.matchesSelector = factory();
497  }
498
499}( window, function factory() {
500  'use strict';
501
502  var matchesMethod = ( function() {
503    var ElemProto = Element.prototype;
504    // check for the standard method name first
505    if ( ElemProto.matches ) {
506      return 'matches';
507    }
508    // check un-prefixed
509    if ( ElemProto.matchesSelector ) {
510      return 'matchesSelector';
511    }
512    // check vendor prefixes
513    var prefixes = [ 'webkit', 'moz', 'ms', 'o' ];
514
515    for ( var i=0; i < prefixes.length; i++ ) {
516      var prefix = prefixes[i];
517      var method = prefix + 'MatchesSelector';
518      if ( ElemProto[ method ] ) {
519        return method;
520      }
521    }
522  })();
523
524  return function matchesSelector( elem, selector ) {
525    return elem[ matchesMethod ]( selector );
526  };
527
528}));
529
530/**
531 * Fizzy UI utils v2.0.3
532 * MIT license
533 */
534
535/*jshint browser: true, undef: true, unused: true, strict: true */
536
537( function( window, factory ) {
538  // universal module definition
539  /*jshint strict: false */ /*globals define, module, require */
540
541  if ( typeof define == 'function' && define.amd ) {
542    // AMD
543    define( 'fizzy-ui-utils/utils',[
544      'desandro-matches-selector/matches-selector'
545    ], function( matchesSelector ) {
546      return factory( window, matchesSelector );
547    });
548  } else if ( typeof module == 'object' && module.exports ) {
549    // CommonJS
550    module.exports = factory(
551      window,
552      require('desandro-matches-selector')
553    );
554  } else {
555    // browser global
556    window.fizzyUIUtils = factory(
557      window,
558      window.matchesSelector
559    );
560  }
561
562}( window, function factory( window, matchesSelector ) {
563
564
565
566var utils = {};
567
568// ----- extend ----- //
569
570// extends objects
571utils.extend = function( a, b ) {
572  for ( var prop in b ) {
573    a[ prop ] = b[ prop ];
574  }
575  return a;
576};
577
578// ----- modulo ----- //
579
580utils.modulo = function( num, div ) {
581  return ( ( num % div ) + div ) % div;
582};
583
584// ----- makeArray ----- //
585
586// turn element or nodeList into an array
587utils.makeArray = function( obj ) {
588  var ary = [];
589  if ( Array.isArray( obj ) ) {
590    // use object if already an array
591    ary = obj;
592  } else if ( obj && typeof obj.length == 'number' ) {
593    // convert nodeList to array
594    for ( var i=0; i < obj.length; i++ ) {
595      ary.push( obj[i] );
596    }
597  } else {
598    // array of single index
599    ary.push( obj );
600  }
601  return ary;
602};
603
604// ----- removeFrom ----- //
605
606utils.removeFrom = function( ary, obj ) {
607  var index = ary.indexOf( obj );
608  if ( index != -1 ) {
609    ary.splice( index, 1 );
610  }
611};
612
613// ----- getParent ----- //
614
615utils.getParent = function( elem, selector ) {
616  while ( elem != document.body ) {
617    elem = elem.parentNode;
618    if ( matchesSelector( elem, selector ) ) {
619      return elem;
620    }
621  }
622};
623
624// ----- getQueryElement ----- //
625
626// use element as selector string
627utils.getQueryElement = function( elem ) {
628  if ( typeof elem == 'string' ) {
629    return document.querySelector( elem );
630  }
631  return elem;
632};
633
634// ----- handleEvent ----- //
635
636// enable .ontype to trigger from .addEventListener( elem, 'type' )
637utils.handleEvent = function( event ) {
638  var method = 'on' + event.type;
639  if ( this[ method ] ) {
640    this[ method ]( event );
641  }
642};
643
644// ----- filterFindElements ----- //
645
646utils.filterFindElements = function( elems, selector ) {
647  // make array of elems
648  elems = utils.makeArray( elems );
649  var ffElems = [];
650
651  elems.forEach( function( elem ) {
652    // check that elem is an actual element
653    if ( !( elem instanceof HTMLElement ) ) {
654      return;
655    }
656    // add elem if no selector
657    if ( !selector ) {
658      ffElems.push( elem );
659      return;
660    }
661    // filter & find items if we have a selector
662    // filter
663    if ( matchesSelector( elem, selector ) ) {
664      ffElems.push( elem );
665    }
666    // find children
667    var childElems = elem.querySelectorAll( selector );
668    // concat childElems to filterFound array
669    for ( var i=0; i < childElems.length; i++ ) {
670      ffElems.push( childElems[i] );
671    }
672  });
673
674  return ffElems;
675};
676
677// ----- debounceMethod ----- //
678
679utils.debounceMethod = function( _class, methodName, threshold ) {
680  // original method
681  var method = _class.prototype[ methodName ];
682  var timeoutName = methodName + 'Timeout';
683
684  _class.prototype[ methodName ] = function() {
685    var timeout = this[ timeoutName ];
686    if ( timeout ) {
687      clearTimeout( timeout );
688    }
689    var args = arguments;
690
691    var _this = this;
692    this[ timeoutName ] = setTimeout( function() {
693      method.apply( _this, args );
694      delete _this[ timeoutName ];
695    }, threshold || 100 );
696  };
697};
698
699// ----- docReady ----- //
700
701utils.docReady = function( callback ) {
702  var readyState = document.readyState;
703  if ( readyState == 'complete' || readyState == 'interactive' ) {
704    // do async to allow for other scripts to run. metafizzy/flickity#441
705    setTimeout( callback );
706  } else {
707    document.addEventListener( 'DOMContentLoaded', callback );
708  }
709};
710
711// ----- htmlInit ----- //
712
713// http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
714utils.toDashed = function( str ) {
715  return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
716    return $1 + '-' + $2;
717  }).toLowerCase();
718};
719
720var console = window.console;
721/**
722 * allow user to initialize classes via [data-namespace] or .js-namespace class
723 * htmlInit( Widget, 'widgetName' )
724 * options are parsed from data-namespace-options
725 */
726utils.htmlInit = function( WidgetClass, namespace ) {
727  utils.docReady( function() {
728    var dashedNamespace = utils.toDashed( namespace );
729    var dataAttr = 'data-' + dashedNamespace;
730    var dataAttrElems = document.querySelectorAll( '[' + dataAttr + ']' );
731    var jsDashElems = document.querySelectorAll( '.js-' + dashedNamespace );
732    var elems = utils.makeArray( dataAttrElems )
733      .concat( utils.makeArray( jsDashElems ) );
734    var dataOptionsAttr = dataAttr + '-options';
735    var jQuery = window.jQuery;
736
737    elems.forEach( function( elem ) {
738      var attr = elem.getAttribute( dataAttr ) ||
739        elem.getAttribute( dataOptionsAttr );
740      var options;
741      try {
742        options = attr && JSON.parse( attr );
743      } catch ( error ) {
744        // log error, do not initialize
745        if ( console ) {
746          console.error( 'Error parsing ' + dataAttr + ' on ' + elem.className +
747          ': ' + error );
748        }
749        return;
750      }
751      // initialize
752      var instance = new WidgetClass( elem, options );
753      // make available via $().data('namespace')
754      if ( jQuery ) {
755        jQuery.data( elem, namespace, instance );
756      }
757    });
758
759  });
760};
761
762// -----  ----- //
763
764return utils;
765
766}));
767
768// Flickity.Cell
769( function( window, factory ) {
770  // universal module definition
771  /* jshint strict: false */
772  if ( typeof define == 'function' && define.amd ) {
773    // AMD
774    define( 'flickity/js/cell',[
775      'get-size/get-size'
776    ], function( getSize ) {
777      return factory( window, getSize );
778    });
779  } else if ( typeof module == 'object' && module.exports ) {
780    // CommonJS
781    module.exports = factory(
782      window,
783      require('get-size')
784    );
785  } else {
786    // browser global
787    window.Flickity = window.Flickity || {};
788    window.Flickity.Cell = factory(
789      window,
790      window.getSize
791    );
792  }
793
794}( window, function factory( window, getSize ) {
795
796
797
798function Cell( elem, parent ) {
799  this.element = elem;
800  this.parent = parent;
801
802  this.create();
803}
804
805var proto = Cell.prototype;
806
807proto.create = function() {
808  this.element.style.position = 'absolute';
809  this.x = 0;
810  this.shift = 0;
811};
812
813proto.destroy = function() {
814  // reset style
815  this.element.style.position = '';
816  var side = this.parent.originSide;
817  this.element.style[ side ] = '';
818};
819
820proto.getSize = function() {
821  this.size = getSize( this.element );
822};
823
824proto.setPosition = function( x ) {
825  this.x = x;
826  this.updateTarget();
827  this.renderPosition( x );
828};
829
830// setDefaultTarget v1 method, backwards compatibility, remove in v3
831proto.updateTarget = proto.setDefaultTarget = function() {
832  var marginProperty = this.parent.originSide == 'left' ? 'marginLeft' : 'marginRight';
833  this.target = this.x + this.size[ marginProperty ] +
834    this.size.width * this.parent.cellAlign;
835};
836
837proto.renderPosition = function( x ) {
838  // render position of cell with in slider
839  var side = this.parent.originSide;
840  this.element.style[ side ] = this.parent.getPositionValue( x );
841};
842
843/**
844 * @param {Integer} factor - 0, 1, or -1
845**/
846proto.wrapShift = function( shift ) {
847  this.shift = shift;
848  this.renderPosition( this.x + this.parent.slideableWidth * shift );
849};
850
851proto.remove = function() {
852  this.element.parentNode.removeChild( this.element );
853};
854
855return Cell;
856
857}));
858
859// slide
860( function( window, factory ) {
861  // universal module definition
862  /* jshint strict: false */
863  if ( typeof define == 'function' && define.amd ) {
864    // AMD
865    define( 'flickity/js/slide',factory );
866  } else if ( typeof module == 'object' && module.exports ) {
867    // CommonJS
868    module.exports = factory();
869  } else {
870    // browser global
871    window.Flickity = window.Flickity || {};
872    window.Flickity.Slide = factory();
873  }
874
875}( window, function factory() {
876'use strict';
877
878function Slide( parent ) {
879  this.parent = parent;
880  this.isOriginLeft = parent.originSide == 'left';
881  this.cells = [];
882  this.outerWidth = 0;
883  this.height = 0;
884}
885
886var proto = Slide.prototype;
887
888proto.addCell = function( cell ) {
889  this.cells.push( cell );
890  this.outerWidth += cell.size.outerWidth;
891  this.height = Math.max( cell.size.outerHeight, this.height );
892  // first cell stuff
893  if ( this.cells.length == 1 ) {
894    this.x = cell.x; // x comes from first cell
895    var beginMargin = this.isOriginLeft ? 'marginLeft' : 'marginRight';
896    this.firstMargin = cell.size[ beginMargin ];
897  }
898};
899
900proto.updateTarget = function() {
901  var endMargin = this.isOriginLeft ? 'marginRight' : 'marginLeft';
902  var lastCell = this.getLastCell();
903  var lastMargin = lastCell ? lastCell.size[ endMargin ] : 0;
904  var slideWidth = this.outerWidth - ( this.firstMargin + lastMargin );
905  this.target = this.x + this.firstMargin + slideWidth * this.parent.cellAlign;
906};
907
908proto.getLastCell = function() {
909  return this.cells[ this.cells.length - 1 ];
910};
911
912proto.select = function() {
913  this.changeSelectedClass('add');
914};
915
916proto.unselect = function() {
917  this.changeSelectedClass('remove');
918};
919
920proto.changeSelectedClass = function( method ) {
921  this.cells.forEach( function( cell ) {
922    cell.element.classList[ method ]('is-selected');
923  });
924};
925
926proto.getCellElements = function() {
927  return this.cells.map( function( cell ) {
928    return cell.element;
929  });
930};
931
932return Slide;
933
934}));
935
936// animate
937( function( window, factory ) {
938  // universal module definition
939  /* jshint strict: false */
940  if ( typeof define == 'function' && define.amd ) {
941    // AMD
942    define( 'flickity/js/animate',[
943      'fizzy-ui-utils/utils'
944    ], function( utils ) {
945      return factory( window, utils );
946    });
947  } else if ( typeof module == 'object' && module.exports ) {
948    // CommonJS
949    module.exports = factory(
950      window,
951      require('fizzy-ui-utils')
952    );
953  } else {
954    // browser global
955    window.Flickity = window.Flickity || {};
956    window.Flickity.animatePrototype = factory(
957      window,
958      window.fizzyUIUtils
959    );
960  }
961
962}( window, function factory( window, utils ) {
963
964
965
966// -------------------------- requestAnimationFrame -------------------------- //
967
968// get rAF, prefixed, if present
969var requestAnimationFrame = window.requestAnimationFrame ||
970  window.webkitRequestAnimationFrame;
971
972// fallback to setTimeout
973var lastTime = 0;
974if ( !requestAnimationFrame )  {
975  requestAnimationFrame = function( callback ) {
976    var currTime = new Date().getTime();
977    var timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) );
978    var id = setTimeout( callback, timeToCall );
979    lastTime = currTime + timeToCall;
980    return id;
981  };
982}
983
984// -------------------------- animate -------------------------- //
985
986var proto = {};
987
988proto.startAnimation = function() {
989  if ( this.isAnimating ) {
990    return;
991  }
992
993  this.isAnimating = true;
994  this.restingFrames = 0;
995  this.animate();
996};
997
998proto.animate = function() {
999  this.applyDragForce();
1000  this.applySelectedAttraction();
1001
1002  var previousX = this.x;
1003
1004  this.integratePhysics();
1005  this.positionSlider();
1006  this.settle( previousX );
1007  // animate next frame
1008  if ( this.isAnimating ) {
1009    var _this = this;
1010    requestAnimationFrame( function animateFrame() {
1011      _this.animate();
1012    });
1013  }
1014};
1015
1016
1017var transformProperty = ( function () {
1018  var style = document.documentElement.style;
1019  if ( typeof style.transform == 'string' ) {
1020    return 'transform';
1021  }
1022  return 'WebkitTransform';
1023})();
1024
1025proto.positionSlider = function() {
1026  var x = this.x;
1027  // wrap position around
1028  if ( this.options.wrapAround && this.cells.length > 1 ) {
1029    x = utils.modulo( x, this.slideableWidth );
1030    x = x - this.slideableWidth;
1031    this.shiftWrapCells( x );
1032  }
1033
1034  x = x + this.cursorPosition;
1035  // reverse if right-to-left and using transform
1036  x = this.options.rightToLeft && transformProperty ? -x : x;
1037  var value = this.getPositionValue( x );
1038  // use 3D tranforms for hardware acceleration on iOS
1039  // but use 2D when settled, for better font-rendering
1040  this.slider.style[ transformProperty ] = this.isAnimating ?
1041    'translate3d(' + value + ',0,0)' : 'translateX(' + value + ')';
1042
1043  // scroll event
1044  var firstSlide = this.slides[0];
1045  if ( firstSlide ) {
1046    var positionX = -this.x - firstSlide.target;
1047    var progress = positionX / this.slidesWidth;
1048    this.dispatchEvent( 'scroll', null, [ progress, positionX ] );
1049  }
1050};
1051
1052proto.positionSliderAtSelected = function() {
1053  if ( !this.cells.length ) {
1054    return;
1055  }
1056  this.x = -this.selectedSlide.target;
1057  this.positionSlider();
1058};
1059
1060proto.getPositionValue = function( position ) {
1061  if ( this.options.percentPosition ) {
1062    // percent position, round to 2 digits, like 12.34%
1063    return ( Math.round( ( position / this.size.innerWidth ) * 10000 ) * 0.01 )+ '%';
1064  } else {
1065    // pixel positioning
1066    return Math.round( position ) + 'px';
1067  }
1068};
1069
1070proto.settle = function( previousX ) {
1071  // keep track of frames where x hasn't moved
1072  if ( !this.isPointerDown && Math.round( this.x * 100 ) == Math.round( previousX * 100 ) ) {
1073    this.restingFrames++;
1074  }
1075  // stop animating if resting for 3 or more frames
1076  if ( this.restingFrames > 2 ) {
1077    this.isAnimating = false;
1078    delete this.isFreeScrolling;
1079    // render position with translateX when settled
1080    this.positionSlider();
1081    this.dispatchEvent('settle');
1082  }
1083};
1084
1085proto.shiftWrapCells = function( x ) {
1086  // shift before cells
1087  var beforeGap = this.cursorPosition + x;
1088  this._shiftCells( this.beforeShiftCells, beforeGap, -1 );
1089  // shift after cells
1090  var afterGap = this.size.innerWidth - ( x + this.slideableWidth + this.cursorPosition );
1091  this._shiftCells( this.afterShiftCells, afterGap, 1 );
1092};
1093
1094proto._shiftCells = function( cells, gap, shift ) {
1095  for ( var i=0; i < cells.length; i++ ) {
1096    var cell = cells[i];
1097    var cellShift = gap > 0 ? shift : 0;
1098    cell.wrapShift( cellShift );
1099    gap -= cell.size.outerWidth;
1100  }
1101};
1102
1103proto._unshiftCells = function( cells ) {
1104  if ( !cells || !cells.length ) {
1105    return;
1106  }
1107  for ( var i=0; i < cells.length; i++ ) {
1108    cells[i].wrapShift( 0 );
1109  }
1110};
1111
1112// -------------------------- physics -------------------------- //
1113
1114proto.integratePhysics = function() {
1115  this.x += this.velocity;
1116  this.velocity *= this.getFrictionFactor();
1117};
1118
1119proto.applyForce = function( force ) {
1120  this.velocity += force;
1121};
1122
1123proto.getFrictionFactor = function() {
1124  return 1 - this.options[ this.isFreeScrolling ? 'freeScrollFriction' : 'friction' ];
1125};
1126
1127proto.getRestingPosition = function() {
1128  // my thanks to Steven Wittens, who simplified this math greatly
1129  return this.x + this.velocity / ( 1 - this.getFrictionFactor() );
1130};
1131
1132proto.applyDragForce = function() {
1133  if ( !this.isPointerDown ) {
1134    return;
1135  }
1136  // change the position to drag position by applying force
1137  var dragVelocity = this.dragX - this.x;
1138  var dragForce = dragVelocity - this.velocity;
1139  this.applyForce( dragForce );
1140};
1141
1142proto.applySelectedAttraction = function() {
1143  // do not attract if pointer down or no cells
1144  if ( this.isPointerDown || this.isFreeScrolling || !this.cells.length ) {
1145    return;
1146  }
1147  var distance = this.selectedSlide.target * -1 - this.x;
1148  var force = distance * this.options.selectedAttraction;
1149  this.applyForce( force );
1150};
1151
1152return proto;
1153
1154}));
1155
1156// Flickity main
1157( function( window, factory ) {
1158  // universal module definition
1159  /* jshint strict: false */
1160  if ( typeof define == 'function' && define.amd ) {
1161    // AMD
1162    define( 'flickity/js/flickity',[
1163      'ev-emitter/ev-emitter',
1164      'get-size/get-size',
1165      'fizzy-ui-utils/utils',
1166      './cell',
1167      './slide',
1168      './animate'
1169    ], function( EvEmitter, getSize, utils, Cell, Slide, animatePrototype ) {
1170      return factory( window, EvEmitter, getSize, utils, Cell, Slide, animatePrototype );
1171    });
1172  } else if ( typeof module == 'object' && module.exports ) {
1173    // CommonJS
1174    module.exports = factory(
1175      window,
1176      require('ev-emitter'),
1177      require('get-size'),
1178      require('fizzy-ui-utils'),
1179      require('./cell'),
1180      require('./slide'),
1181      require('./animate')
1182    );
1183  } else {
1184    // browser global
1185    var _Flickity = window.Flickity;
1186
1187    window.Flickity = factory(
1188      window,
1189      window.EvEmitter,
1190      window.getSize,
1191      window.fizzyUIUtils,
1192      _Flickity.Cell,
1193      _Flickity.Slide,
1194      _Flickity.animatePrototype
1195    );
1196  }
1197
1198}( window, function factory( window, EvEmitter, getSize,
1199  utils, Cell, Slide, animatePrototype ) {
1200
1201
1202
1203// vars
1204var jQuery = window.jQuery;
1205var getComputedStyle = window.getComputedStyle;
1206var console = window.console;
1207
1208function moveElements( elems, toElem ) {
1209  elems = utils.makeArray( elems );
1210  while ( elems.length ) {
1211    toElem.appendChild( elems.shift() );
1212  }
1213}
1214
1215// -------------------------- Flickity -------------------------- //
1216
1217// globally unique identifiers
1218var GUID = 0;
1219// internal store of all Flickity intances
1220var instances = {};
1221
1222function Flickity( element, options ) {
1223  var queryElement = utils.getQueryElement( element );
1224  if ( !queryElement ) {
1225    if ( console ) {
1226      console.error( 'Bad element for Flickity: ' + ( queryElement || element ) );
1227    }
1228    return;
1229  }
1230  this.element = queryElement;
1231  // do not initialize twice on same element
1232  if ( this.element.flickityGUID ) {
1233    var instance = instances[ this.element.flickityGUID ];
1234    instance.option( options );
1235    return instance;
1236  }
1237
1238  // add jQuery
1239  if ( jQuery ) {
1240    this.$element = jQuery( this.element );
1241  }
1242  // options
1243  this.options = utils.extend( {}, this.constructor.defaults );
1244  this.option( options );
1245
1246  // kick things off
1247  this._create();
1248}
1249
1250Flickity.defaults = {
1251  accessibility: true,
1252  // adaptiveHeight: false,
1253  cellAlign: 'center',
1254  // cellSelector: undefined,
1255  // contain: false,
1256  freeScrollFriction: 0.075, // friction when free-scrolling
1257  friction: 0.28, // friction when selecting
1258  namespaceJQueryEvents: true,
1259  // initialIndex: 0,
1260  percentPosition: true,
1261  resize: true,
1262  selectedAttraction: 0.025,
1263  setGallerySize: true
1264  // watchCSS: false,
1265  // wrapAround: false
1266};
1267
1268// hash of methods triggered on _create()
1269Flickity.createMethods = [];
1270
1271var proto = Flickity.prototype;
1272// inherit EventEmitter
1273utils.extend( proto, EvEmitter.prototype );
1274
1275proto._create = function() {
1276  // add id for Flickity.data
1277  var id = this.guid = ++GUID;
1278  this.element.flickityGUID = id; // expando
1279  instances[ id ] = this; // associate via id
1280  // initial properties
1281  this.selectedIndex = 0;
1282  // how many frames slider has been in same position
1283  this.restingFrames = 0;
1284  // initial physics properties
1285  this.x = 0;
1286  this.velocity = 0;
1287  this.originSide = this.options.rightToLeft ? 'right' : 'left';
1288  // create viewport & slider
1289  this.viewport = document.createElement('div');
1290  this.viewport.className = 'flickity-viewport';
1291  this._createSlider();
1292
1293  if ( this.options.resize || this.options.watchCSS ) {
1294    window.addEventListener( 'resize', this );
1295  }
1296
1297  Flickity.createMethods.forEach( function( method ) {
1298    this[ method ]();
1299  }, this );
1300
1301  if ( this.options.watchCSS ) {
1302    this.watchCSS();
1303  } else {
1304    this.activate();
1305  }
1306
1307};
1308
1309/**
1310 * set options
1311 * @param {Object} opts
1312 */
1313proto.option = function( opts ) {
1314  utils.extend( this.options, opts );
1315};
1316
1317proto.activate = function() {
1318  if ( this.isActive ) {
1319    return;
1320  }
1321  this.isActive = true;
1322  this.element.classList.add('flickity-enabled');
1323  if ( this.options.rightToLeft ) {
1324    this.element.classList.add('flickity-rtl');
1325  }
1326
1327  this.getSize();
1328  // move initial cell elements so they can be loaded as cells
1329  var cellElems = this._filterFindCellElements( this.element.children );
1330  moveElements( cellElems, this.slider );
1331  this.viewport.appendChild( this.slider );
1332  this.element.appendChild( this.viewport );
1333  // get cells from children
1334  this.reloadCells();
1335
1336  if ( this.options.accessibility ) {
1337    // allow element to focusable
1338    this.element.tabIndex = 0;
1339    // listen for key presses
1340    this.element.addEventListener( 'keydown', this );
1341  }
1342
1343  this.emitEvent('activate');
1344
1345  var index;
1346  var initialIndex = this.options.initialIndex;
1347  if ( this.isInitActivated ) {
1348    index = this.selectedIndex;
1349  } else if ( initialIndex !== undefined ) {
1350    index = this.cells[ initialIndex ] ? initialIndex : 0;
1351  } else {
1352    index = 0;
1353  }
1354  // select instantly
1355  this.select( index, false, true );
1356  // flag for initial activation, for using initialIndex
1357  this.isInitActivated = true;
1358};
1359
1360// slider positions the cells
1361proto._createSlider = function() {
1362  // slider element does all the positioning
1363  var slider = document.createElement('div');
1364  slider.className = 'flickity-slider';
1365  slider.style[ this.originSide ] = 0;
1366  this.slider = slider;
1367};
1368
1369proto._filterFindCellElements = function( elems ) {
1370  return utils.filterFindElements( elems, this.options.cellSelector );
1371};
1372
1373// goes through all children
1374proto.reloadCells = function() {
1375  // collection of item elements
1376  this.cells = this._makeCells( this.slider.children );
1377  this.positionCells();
1378  this._getWrapShiftCells();
1379  this.setGallerySize();
1380};
1381
1382/**
1383 * turn elements into Flickity.Cells
1384 * @param {Array or NodeList or HTMLElement} elems
1385 * @returns {Array} items - collection of new Flickity Cells
1386 */
1387proto._makeCells = function( elems ) {
1388  var cellElems = this._filterFindCellElements( elems );
1389
1390  // create new Flickity for collection
1391  var cells = cellElems.map( function( cellElem ) {
1392    return new Cell( cellElem, this );
1393  }, this );
1394
1395  return cells;
1396};
1397
1398proto.getLastCell = function() {
1399  return this.cells[ this.cells.length - 1 ];
1400};
1401
1402proto.getLastSlide = function() {
1403  return this.slides[ this.slides.length - 1 ];
1404};
1405
1406// positions all cells
1407proto.positionCells = function() {
1408  // size all cells
1409  this._sizeCells( this.cells );
1410  // position all cells
1411  this._positionCells( 0 );
1412};
1413
1414/**
1415 * position certain cells
1416 * @param {Integer} index - which cell to start with
1417 */
1418proto._positionCells = function( index ) {
1419  index = index || 0;
1420  // also measure maxCellHeight
1421  // start 0 if positioning all cells
1422  this.maxCellHeight = index ? this.maxCellHeight || 0 : 0;
1423  var cellX = 0;
1424  // get cellX
1425  if ( index > 0 ) {
1426    var startCell = this.cells[ index - 1 ];
1427    cellX = startCell.x + startCell.size.outerWidth;
1428  }
1429  var len = this.cells.length;
1430  for ( var i=index; i < len; i++ ) {
1431    var cell = this.cells[i];
1432    cell.setPosition( cellX );
1433    cellX += cell.size.outerWidth;
1434    this.maxCellHeight = Math.max( cell.size.outerHeight, this.maxCellHeight );
1435  }
1436  // keep track of cellX for wrap-around
1437  this.slideableWidth = cellX;
1438  // slides
1439  this.updateSlides();
1440  // contain slides target
1441  this._containSlides();
1442  // update slidesWidth
1443  this.slidesWidth = len ? this.getLastSlide().target - this.slides[0].target : 0;
1444};
1445
1446/**
1447 * cell.getSize() on multiple cells
1448 * @param {Array} cells
1449 */
1450proto._sizeCells = function( cells ) {
1451  cells.forEach( function( cell ) {
1452    cell.getSize();
1453  });
1454};
1455
1456// --------------------------  -------------------------- //
1457
1458proto.updateSlides = function() {
1459  this.slides = [];
1460  if ( !this.cells.length ) {
1461    return;
1462  }
1463
1464  var slide = new Slide( this );
1465  this.slides.push( slide );
1466  var isOriginLeft = this.originSide == 'left';
1467  var nextMargin = isOriginLeft ? 'marginRight' : 'marginLeft';
1468
1469  var canCellFit = this._getCanCellFit();
1470
1471  this.cells.forEach( function( cell, i ) {
1472    // just add cell if first cell in slide
1473    if ( !slide.cells.length ) {
1474      slide.addCell( cell );
1475      return;
1476    }
1477
1478    var slideWidth = ( slide.outerWidth - slide.firstMargin ) +
1479      ( cell.size.outerWidth - cell.size[ nextMargin ] );
1480
1481    if ( canCellFit.call( this, i, slideWidth ) ) {
1482      slide.addCell( cell );
1483    } else {
1484      // doesn't fit, new slide
1485      slide.updateTarget();
1486
1487      slide = new Slide( this );
1488      this.slides.push( slide );
1489      slide.addCell( cell );
1490    }
1491  }, this );
1492  // last slide
1493  slide.updateTarget();
1494  // update .selectedSlide
1495  this.updateSelectedSlide();
1496};
1497
1498proto._getCanCellFit = function() {
1499  var groupCells = this.options.groupCells;
1500  if ( !groupCells ) {
1501    return function() {
1502      return false;
1503    };
1504  } else if ( typeof groupCells == 'number' ) {
1505    // group by number. 3 -> [0,1,2], [3,4,5], ...
1506    var number = parseInt( groupCells, 10 );
1507    return function( i ) {
1508      return ( i % number ) !== 0;
1509    };
1510  }
1511  // default, group by width of slide
1512  // parse '75%
1513  var percentMatch = typeof groupCells == 'string' &&
1514    groupCells.match(/^(\d+)%$/);
1515  var percent = percentMatch ? parseInt( percentMatch[1], 10 ) / 100 : 1;
1516  return function( i, slideWidth ) {
1517    return slideWidth <= ( this.size.innerWidth + 1 ) * percent;
1518  };
1519};
1520
1521// alias _init for jQuery plugin .flickity()
1522proto._init =
1523proto.reposition = function() {
1524  this.positionCells();
1525  this.positionSliderAtSelected();
1526};
1527
1528proto.getSize = function() {
1529  this.size = getSize( this.element );
1530  this.setCellAlign();
1531  this.cursorPosition = this.size.innerWidth * this.cellAlign;
1532};
1533
1534var cellAlignShorthands = {
1535  // cell align, then based on origin side
1536  center: {
1537    left: 0.5,
1538    right: 0.5
1539  },
1540  left: {
1541    left: 0,
1542    right: 1
1543  },
1544  right: {
1545    right: 0,
1546    left: 1
1547  }
1548};
1549
1550proto.setCellAlign = function() {
1551  var shorthand = cellAlignShorthands[ this.options.cellAlign ];
1552  this.cellAlign = shorthand ? shorthand[ this.originSide ] : this.options.cellAlign;
1553};
1554
1555proto.setGallerySize = function() {
1556  if ( this.options.setGallerySize ) {
1557    var height = this.options.adaptiveHeight && this.selectedSlide ?
1558      this.selectedSlide.height : this.maxCellHeight;
1559    this.viewport.style.height = height + 'px';
1560  }
1561};
1562
1563proto._getWrapShiftCells = function() {
1564  // only for wrap-around
1565  if ( !this.options.wrapAround ) {
1566    return;
1567  }
1568  // unshift previous cells
1569  this._unshiftCells( this.beforeShiftCells );
1570  this._unshiftCells( this.afterShiftCells );
1571  // get before cells
1572  // initial gap
1573  var gapX = this.cursorPosition;
1574  var cellIndex = this.cells.length - 1;
1575  this.beforeShiftCells = this._getGapCells( gapX, cellIndex, -1 );
1576  // get after cells
1577  // ending gap between last cell and end of gallery viewport
1578  gapX = this.size.innerWidth - this.cursorPosition;
1579  // start cloning at first cell, working forwards
1580  this.afterShiftCells = this._getGapCells( gapX, 0, 1 );
1581};
1582
1583proto._getGapCells = function( gapX, cellIndex, increment ) {
1584  // keep adding cells until the cover the initial gap
1585  var cells = [];
1586  while ( gapX > 0 ) {
1587    var cell = this.cells[ cellIndex ];
1588    if ( !cell ) {
1589      break;
1590    }
1591    cells.push( cell );
1592    cellIndex += increment;
1593    gapX -= cell.size.outerWidth;
1594  }
1595  return cells;
1596};
1597
1598// ----- contain ----- //
1599
1600// contain cell targets so no excess sliding
1601proto._containSlides = function() {
1602  if ( !this.options.contain || this.options.wrapAround || !this.cells.length ) {
1603    return;
1604  }
1605  var isRightToLeft = this.options.rightToLeft;
1606  var beginMargin = isRightToLeft ? 'marginRight' : 'marginLeft';
1607  var endMargin = isRightToLeft ? 'marginLeft' : 'marginRight';
1608  var contentWidth = this.slideableWidth - this.getLastCell().size[ endMargin ];
1609  // content is less than gallery size
1610  var isContentSmaller = contentWidth < this.size.innerWidth;
1611  // bounds
1612  var beginBound = this.cursorPosition + this.cells[0].size[ beginMargin ];
1613  var endBound = contentWidth - this.size.innerWidth * ( 1 - this.cellAlign );
1614  // contain each cell target
1615  this.slides.forEach( function( slide ) {
1616    if ( isContentSmaller ) {
1617      // all cells fit inside gallery
1618      slide.target = contentWidth * this.cellAlign;
1619    } else {
1620      // contain to bounds
1621      slide.target = Math.max( slide.target, beginBound );
1622      slide.target = Math.min( slide.target, endBound );
1623    }
1624  }, this );
1625};
1626
1627// -----  ----- //
1628
1629/**
1630 * emits events via eventEmitter and jQuery events
1631 * @param {String} type - name of event
1632 * @param {Event} event - original event
1633 * @param {Array} args - extra arguments
1634 */
1635proto.dispatchEvent = function( type, event, args ) {
1636  var emitArgs = event ? [ event ].concat( args ) : args;
1637  this.emitEvent( type, emitArgs );
1638
1639  if ( jQuery && this.$element ) {
1640    // default trigger with type if no event
1641    type += this.options.namespaceJQueryEvents ? '.flickity' : '';
1642    var $event = type;
1643    if ( event ) {
1644      // create jQuery event
1645      var jQEvent = jQuery.Event( event );
1646      jQEvent.type = type;
1647      $event = jQEvent;
1648    }
1649    this.$element.trigger( $event, args );
1650  }
1651};
1652
1653// -------------------------- select -------------------------- //
1654
1655/**
1656 * @param {Integer} index - index of the slide
1657 * @param {Boolean} isWrap - will wrap-around to last/first if at the end
1658 * @param {Boolean} isInstant - will immediately set position at selected cell
1659 */
1660proto.select = function( index, isWrap, isInstant ) {
1661  if ( !this.isActive ) {
1662    return;
1663  }
1664  index = parseInt( index, 10 );
1665  this._wrapSelect( index );
1666
1667  if ( this.options.wrapAround || isWrap ) {
1668    index = utils.modulo( index, this.slides.length );
1669  }
1670  // bail if invalid index
1671  if ( !this.slides[ index ] ) {
1672    return;
1673  }
1674  this.selectedIndex = index;
1675  this.updateSelectedSlide();
1676  if ( isInstant ) {
1677    this.positionSliderAtSelected();
1678  } else {
1679    this.startAnimation();
1680  }
1681  if ( this.options.adaptiveHeight ) {
1682    this.setGallerySize();
1683  }
1684
1685  this.dispatchEvent('select');
1686  // old v1 event name, remove in v3
1687  this.dispatchEvent('cellSelect');
1688};
1689
1690// wraps position for wrapAround, to move to closest slide. #113
1691proto._wrapSelect = function( index ) {
1692  var len = this.slides.length;
1693  var isWrapping = this.options.wrapAround && len > 1;
1694  if ( !isWrapping ) {
1695    return index;
1696  }
1697  var wrapIndex = utils.modulo( index, len );
1698  // go to shortest
1699  var delta = Math.abs( wrapIndex - this.selectedIndex );
1700  var backWrapDelta = Math.abs( ( wrapIndex + len ) - this.selectedIndex );
1701  var forewardWrapDelta = Math.abs( ( wrapIndex - len ) - this.selectedIndex );
1702  if ( !this.isDragSelect && backWrapDelta < delta ) {
1703    index += len;
1704  } else if ( !this.isDragSelect && forewardWrapDelta < delta ) {
1705    index -= len;
1706  }
1707  // wrap position so slider is within normal area
1708  if ( index < 0 ) {
1709    this.x -= this.slideableWidth;
1710  } else if ( index >= len ) {
1711    this.x += this.slideableWidth;
1712  }
1713};
1714
1715proto.previous = function( isWrap, isInstant ) {
1716  this.select( this.selectedIndex - 1, isWrap, isInstant );
1717};
1718
1719proto.next = function( isWrap, isInstant ) {
1720  this.select( this.selectedIndex + 1, isWrap, isInstant );
1721};
1722
1723proto.updateSelectedSlide = function() {
1724  var slide = this.slides[ this.selectedIndex ];
1725  // selectedIndex could be outside of slides, if triggered before resize()
1726  if ( !slide ) {
1727    return;
1728  }
1729  // unselect previous selected slide
1730  this.unselectSelectedSlide();
1731  // update new selected slide
1732  this.selectedSlide = slide;
1733  slide.select();
1734  this.selectedCells = slide.cells;
1735  this.selectedElements = slide.getCellElements();
1736  // HACK: selectedCell & selectedElement is first cell in slide, backwards compatibility
1737  // Remove in v3?
1738  this.selectedCell = slide.cells[0];
1739  this.selectedElement = this.selectedElements[0];
1740};
1741
1742proto.unselectSelectedSlide = function() {
1743  if ( this.selectedSlide ) {
1744    this.selectedSlide.unselect();
1745  }
1746};
1747
1748/**
1749 * select slide from number or cell element
1750 * @param {Element or Number} elem
1751 */
1752proto.selectCell = function( value, isWrap, isInstant ) {
1753  // get cell
1754  var cell;
1755  if ( typeof value == 'number' ) {
1756    cell = this.cells[ value ];
1757  } else {
1758    // use string as selector
1759    if ( typeof value == 'string' ) {
1760      value = this.element.querySelector( value );
1761    }
1762    // get cell from element
1763    cell = this.getCell( value );
1764  }
1765  // select slide that has cell
1766  for ( var i=0; cell && i < this.slides.length; i++ ) {
1767    var slide = this.slides[i];
1768    var index = slide.cells.indexOf( cell );
1769    if ( index != -1 ) {
1770      this.select( i, isWrap, isInstant );
1771      return;
1772    }
1773  }
1774};
1775
1776// -------------------------- get cells -------------------------- //
1777
1778/**
1779 * get Flickity.Cell, given an Element
1780 * @param {Element} elem
1781 * @returns {Flickity.Cell} item
1782 */
1783proto.getCell = function( elem ) {
1784  // loop through cells to get the one that matches
1785  for ( var i=0; i < this.cells.length; i++ ) {
1786    var cell = this.cells[i];
1787    if ( cell.element == elem ) {
1788      return cell;
1789    }
1790  }
1791};
1792
1793/**
1794 * get collection of Flickity.Cells, given Elements
1795 * @param {Element, Array, NodeList} elems
1796 * @returns {Array} cells - Flickity.Cells
1797 */
1798proto.getCells = function( elems ) {
1799  elems = utils.makeArray( elems );
1800  var cells = [];
1801  elems.forEach( function( elem ) {
1802    var cell = this.getCell( elem );
1803    if ( cell ) {
1804      cells.push( cell );
1805    }
1806  }, this );
1807  return cells;
1808};
1809
1810/**
1811 * get cell elements
1812 * @returns {Array} cellElems
1813 */
1814proto.getCellElements = function() {
1815  return this.cells.map( function( cell ) {
1816    return cell.element;
1817  });
1818};
1819
1820/**
1821 * get parent cell from an element
1822 * @param {Element} elem
1823 * @returns {Flickit.Cell} cell
1824 */
1825proto.getParentCell = function( elem ) {
1826  // first check if elem is cell
1827  var cell = this.getCell( elem );
1828  if ( cell ) {
1829    return cell;
1830  }
1831  // try to get parent cell elem
1832  elem = utils.getParent( elem, '.flickity-slider > *' );
1833  return this.getCell( elem );
1834};
1835
1836/**
1837 * get cells adjacent to a slide
1838 * @param {Integer} adjCount - number of adjacent slides
1839 * @param {Integer} index - index of slide to start
1840 * @returns {Array} cells - array of Flickity.Cells
1841 */
1842proto.getAdjacentCellElements = function( adjCount, index ) {
1843  if ( !adjCount ) {
1844    return this.selectedSlide.getCellElements();
1845  }
1846  index = index === undefined ? this.selectedIndex : index;
1847
1848  var len = this.slides.length;
1849  if ( 1 + ( adjCount * 2 ) >= len ) {
1850    return this.getCellElements();
1851  }
1852
1853  var cellElems = [];
1854  for ( var i = index - adjCount; i <= index + adjCount ; i++ ) {
1855    var slideIndex = this.options.wrapAround ? utils.modulo( i, len ) : i;
1856    var slide = this.slides[ slideIndex ];
1857    if ( slide ) {
1858      cellElems = cellElems.concat( slide.getCellElements() );
1859    }
1860  }
1861  return cellElems;
1862};
1863
1864// -------------------------- events -------------------------- //
1865
1866proto.uiChange = function() {
1867  this.emitEvent('uiChange');
1868};
1869
1870proto.childUIPointerDown = function( event ) {
1871  this.emitEvent( 'childUIPointerDown', [ event ] );
1872};
1873
1874// ----- resize ----- //
1875
1876proto.onresize = function() {
1877  this.watchCSS();
1878  this.resize();
1879};
1880
1881utils.debounceMethod( Flickity, 'onresize', 150 );
1882
1883proto.resize = function() {
1884  if ( !this.isActive ) {
1885    return;
1886  }
1887  this.getSize();
1888  // wrap values
1889  if ( this.options.wrapAround ) {
1890    this.x = utils.modulo( this.x, this.slideableWidth );
1891  }
1892  this.positionCells();
1893  this._getWrapShiftCells();
1894  this.setGallerySize();
1895  this.emitEvent('resize');
1896  // update selected index for group slides, instant
1897  // TODO: position can be lost between groups of various numbers
1898  var selectedElement = this.selectedElements && this.selectedElements[0];
1899  this.selectCell( selectedElement, false, true );
1900};
1901
1902// watches the :after property, activates/deactivates
1903proto.watchCSS = function() {
1904  var watchOption = this.options.watchCSS;
1905  if ( !watchOption ) {
1906    return;
1907  }
1908
1909  var afterContent = getComputedStyle( this.element, ':after' ).content;
1910  // activate if :after { content: 'flickity' }
1911  if ( afterContent.indexOf('flickity') != -1 ) {
1912    this.activate();
1913  } else {
1914    this.deactivate();
1915  }
1916};
1917
1918// ----- keydown ----- //
1919
1920// go previous/next if left/right keys pressed
1921proto.onkeydown = function( event ) {
1922  // only work if element is in focus
1923  if ( !this.options.accessibility ||
1924    ( document.activeElement && document.activeElement != this.element ) ) {
1925    return;
1926  }
1927
1928  if ( event.keyCode == 37 ) {
1929    // go left
1930    var leftMethod = this.options.rightToLeft ? 'next' : 'previous';
1931    this.uiChange();
1932    this[ leftMethod ]();
1933  } else if ( event.keyCode == 39 ) {
1934    // go right
1935    var rightMethod = this.options.rightToLeft ? 'previous' : 'next';
1936    this.uiChange();
1937    this[ rightMethod ]();
1938  }
1939};
1940
1941// -------------------------- destroy -------------------------- //
1942
1943// deactivate all Flickity functionality, but keep stuff available
1944proto.deactivate = function() {
1945  if ( !this.isActive ) {
1946    return;
1947  }
1948  this.element.classList.remove('flickity-enabled');
1949  this.element.classList.remove('flickity-rtl');
1950  // destroy cells
1951  this.cells.forEach( function( cell ) {
1952    cell.destroy();
1953  });
1954  this.unselectSelectedSlide();
1955  this.element.removeChild( this.viewport );
1956  // move child elements back into element
1957  moveElements( this.slider.children, this.element );
1958  if ( this.options.accessibility ) {
1959    this.element.removeAttribute('tabIndex');
1960    this.element.removeEventListener( 'keydown', this );
1961  }
1962  // set flags
1963  this.isActive = false;
1964  this.emitEvent('deactivate');
1965};
1966
1967proto.destroy = function() {
1968  this.deactivate();
1969  window.removeEventListener( 'resize', this );
1970  this.emitEvent('destroy');
1971  if ( jQuery && this.$element ) {
1972    jQuery.removeData( this.element, 'flickity' );
1973  }
1974  delete this.element.flickityGUID;
1975  delete instances[ this.guid ];
1976};
1977
1978// -------------------------- prototype -------------------------- //
1979
1980utils.extend( proto, animatePrototype );
1981
1982// -------------------------- extras -------------------------- //
1983
1984/**
1985 * get Flickity instance from element
1986 * @param {Element} elem
1987 * @returns {Flickity}
1988 */
1989Flickity.data = function( elem ) {
1990  elem = utils.getQueryElement( elem );
1991  var id = elem && elem.flickityGUID;
1992  return id && instances[ id ];
1993};
1994
1995utils.htmlInit( Flickity, 'flickity' );
1996
1997if ( jQuery && jQuery.bridget ) {
1998  jQuery.bridget( 'flickity', Flickity );
1999}
2000
2001Flickity.Cell = Cell;
2002
2003return Flickity;
2004
2005}));
2006
2007/*!
2008 * Unipointer v2.1.0
2009 * base class for doing one thing with pointer event
2010 * MIT license
2011 */
2012
2013/*jshint browser: true, undef: true, unused: true, strict: true */
2014
2015( function( window, factory ) {
2016  // universal module definition
2017  /* jshint strict: false */ /*global define, module, require */
2018  if ( typeof define == 'function' && define.amd ) {
2019    // AMD
2020    define( 'unipointer/unipointer',[
2021      'ev-emitter/ev-emitter'
2022    ], function( EvEmitter ) {
2023      return factory( window, EvEmitter );
2024    });
2025  } else if ( typeof module == 'object' && module.exports ) {
2026    // CommonJS
2027    module.exports = factory(
2028      window,
2029      require('ev-emitter')
2030    );
2031  } else {
2032    // browser global
2033    window.Unipointer = factory(
2034      window,
2035      window.EvEmitter
2036    );
2037  }
2038
2039}( window, function factory( window, EvEmitter ) {
2040
2041
2042
2043function noop() {}
2044
2045function Unipointer() {}
2046
2047// inherit EvEmitter
2048var proto = Unipointer.prototype = Object.create( EvEmitter.prototype );
2049
2050proto.bindStartEvent = function( elem ) {
2051  this._bindStartEvent( elem, true );
2052};
2053
2054proto.unbindStartEvent = function( elem ) {
2055  this._bindStartEvent( elem, false );
2056};
2057
2058/**
2059 * works as unbinder, as you can ._bindStart( false ) to unbind
2060 * @param {Boolean} isBind - will unbind if falsey
2061 */
2062proto._bindStartEvent = function( elem, isBind ) {
2063  // munge isBind, default to true
2064  isBind = isBind === undefined ? true : !!isBind;
2065  var bindMethod = isBind ? 'addEventListener' : 'removeEventListener';
2066
2067  if ( window.navigator.pointerEnabled ) {
2068    // W3C Pointer Events, IE11. See https://coderwall.com/p/mfreca
2069    elem[ bindMethod ]( 'pointerdown', this );
2070  } else if ( window.navigator.msPointerEnabled ) {
2071    // IE10 Pointer Events
2072    elem[ bindMethod ]( 'MSPointerDown', this );
2073  } else {
2074    // listen for both, for devices like Chrome Pixel
2075    elem[ bindMethod ]( 'mousedown', this );
2076    elem[ bindMethod ]( 'touchstart', this );
2077  }
2078};
2079
2080// trigger handler methods for events
2081proto.handleEvent = function( event ) {
2082  var method = 'on' + event.type;
2083  if ( this[ method ] ) {
2084    this[ method ]( event );
2085  }
2086};
2087
2088// returns the touch that we're keeping track of
2089proto.getTouch = function( touches ) {
2090  for ( var i=0; i < touches.length; i++ ) {
2091    var touch = touches[i];
2092    if ( touch.identifier == this.pointerIdentifier ) {
2093      return touch;
2094    }
2095  }
2096};
2097
2098// ----- start event ----- //
2099
2100proto.onmousedown = function( event ) {
2101  // dismiss clicks from right or middle buttons
2102  var button = event.button;
2103  if ( button && ( button !== 0 && button !== 1 ) ) {
2104    return;
2105  }
2106  this._pointerDown( event, event );
2107};
2108
2109proto.ontouchstart = function( event ) {
2110  this._pointerDown( event, event.changedTouches[0] );
2111};
2112
2113proto.onMSPointerDown =
2114proto.onpointerdown = function( event ) {
2115  this._pointerDown( event, event );
2116};
2117
2118/**
2119 * pointer start
2120 * @param {Event} event
2121 * @param {Event or Touch} pointer
2122 */
2123proto._pointerDown = function( event, pointer ) {
2124  // dismiss other pointers
2125  if ( this.isPointerDown ) {
2126    return;
2127  }
2128
2129  this.isPointerDown = true;
2130  // save pointer identifier to match up touch events
2131  this.pointerIdentifier = pointer.pointerId !== undefined ?
2132    // pointerId for pointer events, touch.indentifier for touch events
2133    pointer.pointerId : pointer.identifier;
2134
2135  this.pointerDown( event, pointer );
2136};
2137
2138proto.pointerDown = function( event, pointer ) {
2139  this._bindPostStartEvents( event );
2140  this.emitEvent( 'pointerDown', [ event, pointer ] );
2141};
2142
2143// hash of events to be bound after start event
2144var postStartEvents = {
2145  mousedown: [ 'mousemove', 'mouseup' ],
2146  touchstart: [ 'touchmove', 'touchend', 'touchcancel' ],
2147  pointerdown: [ 'pointermove', 'pointerup', 'pointercancel' ],
2148  MSPointerDown: [ 'MSPointerMove', 'MSPointerUp', 'MSPointerCancel' ]
2149};
2150
2151proto._bindPostStartEvents = function( event ) {
2152  if ( !event ) {
2153    return;
2154  }
2155  // get proper events to match start event
2156  var events = postStartEvents[ event.type ];
2157  // bind events to node
2158  events.forEach( function( eventName ) {
2159    window.addEventListener( eventName, this );
2160  }, this );
2161  // save these arguments
2162  this._boundPointerEvents = events;
2163};
2164
2165proto._unbindPostStartEvents = function() {
2166  // check for _boundEvents, in case dragEnd triggered twice (old IE8 bug)
2167  if ( !this._boundPointerEvents ) {
2168    return;
2169  }
2170  this._boundPointerEvents.forEach( function( eventName ) {
2171    window.removeEventListener( eventName, this );
2172  }, this );
2173
2174  delete this._boundPointerEvents;
2175};
2176
2177// ----- move event ----- //
2178
2179proto.onmousemove = function( event ) {
2180  this._pointerMove( event, event );
2181};
2182
2183proto.onMSPointerMove =
2184proto.onpointermove = function( event ) {
2185  if ( event.pointerId == this.pointerIdentifier ) {
2186    this._pointerMove( event, event );
2187  }
2188};
2189
2190proto.ontouchmove = function( event ) {
2191  var touch = this.getTouch( event.changedTouches );
2192  if ( touch ) {
2193    this._pointerMove( event, touch );
2194  }
2195};
2196
2197/**
2198 * pointer move
2199 * @param {Event} event
2200 * @param {Event or Touch} pointer
2201 * @private
2202 */
2203proto._pointerMove = function( event, pointer ) {
2204  this.pointerMove( event, pointer );
2205};
2206
2207// public
2208proto.pointerMove = function( event, pointer ) {
2209  this.emitEvent( 'pointerMove', [ event, pointer ] );
2210};
2211
2212// ----- end event ----- //
2213
2214
2215proto.onmouseup = function( event ) {
2216  this._pointerUp( event, event );
2217};
2218
2219proto.onMSPointerUp =
2220proto.onpointerup = function( event ) {
2221  if ( event.pointerId == this.pointerIdentifier ) {
2222    this._pointerUp( event, event );
2223  }
2224};
2225
2226proto.ontouchend = function( event ) {
2227  var touch = this.getTouch( event.changedTouches );
2228  if ( touch ) {
2229    this._pointerUp( event, touch );
2230  }
2231};
2232
2233/**
2234 * pointer up
2235 * @param {Event} event
2236 * @param {Event or Touch} pointer
2237 * @private
2238 */
2239proto._pointerUp = function( event, pointer ) {
2240  this._pointerDone();
2241  this.pointerUp( event, pointer );
2242};
2243
2244// public
2245proto.pointerUp = function( event, pointer ) {
2246  this.emitEvent( 'pointerUp', [ event, pointer ] );
2247};
2248
2249// ----- pointer done ----- //
2250
2251// triggered on pointer up & pointer cancel
2252proto._pointerDone = function() {
2253  // reset properties
2254  this.isPointerDown = false;
2255  delete this.pointerIdentifier;
2256  // remove events
2257  this._unbindPostStartEvents();
2258  this.pointerDone();
2259};
2260
2261proto.pointerDone = noop;
2262
2263// ----- pointer cancel ----- //
2264
2265proto.onMSPointerCancel =
2266proto.onpointercancel = function( event ) {
2267  if ( event.pointerId == this.pointerIdentifier ) {
2268    this._pointerCancel( event, event );
2269  }
2270};
2271
2272proto.ontouchcancel = function( event ) {
2273  var touch = this.getTouch( event.changedTouches );
2274  if ( touch ) {
2275    this._pointerCancel( event, touch );
2276  }
2277};
2278
2279/**
2280 * pointer cancel
2281 * @param {Event} event
2282 * @param {Event or Touch} pointer
2283 * @private
2284 */
2285proto._pointerCancel = function( event, pointer ) {
2286  this._pointerDone();
2287  this.pointerCancel( event, pointer );
2288};
2289
2290// public
2291proto.pointerCancel = function( event, pointer ) {
2292  this.emitEvent( 'pointerCancel', [ event, pointer ] );
2293};
2294
2295// -----  ----- //
2296
2297// utility function for getting x/y coords from event
2298Unipointer.getPointerPoint = function( pointer ) {
2299  return {
2300    x: pointer.pageX,
2301    y: pointer.pageY
2302  };
2303};
2304
2305// -----  ----- //
2306
2307return Unipointer;
2308
2309}));
2310
2311/*!
2312 * Unidragger v2.1.0
2313 * Draggable base class
2314 * MIT license
2315 */
2316
2317/*jshint browser: true, unused: true, undef: true, strict: true */
2318
2319( function( window, factory ) {
2320  // universal module definition
2321  /*jshint strict: false */ /*globals define, module, require */
2322
2323  if ( typeof define == 'function' && define.amd ) {
2324    // AMD
2325    define( 'unidragger/unidragger',[
2326      'unipointer/unipointer'
2327    ], function( Unipointer ) {
2328      return factory( window, Unipointer );
2329    });
2330  } else if ( typeof module == 'object' && module.exports ) {
2331    // CommonJS
2332    module.exports = factory(
2333      window,
2334      require('unipointer')
2335    );
2336  } else {
2337    // browser global
2338    window.Unidragger = factory(
2339      window,
2340      window.Unipointer
2341    );
2342  }
2343
2344}( window, function factory( window, Unipointer ) {
2345
2346
2347
2348// -----  ----- //
2349
2350function noop() {}
2351
2352// -------------------------- Unidragger -------------------------- //
2353
2354function Unidragger() {}
2355
2356// inherit Unipointer & EvEmitter
2357var proto = Unidragger.prototype = Object.create( Unipointer.prototype );
2358
2359// ----- bind start ----- //
2360
2361proto.bindHandles = function() {
2362  this._bindHandles( true );
2363};
2364
2365proto.unbindHandles = function() {
2366  this._bindHandles( false );
2367};
2368
2369var navigator = window.navigator;
2370/**
2371 * works as unbinder, as you can .bindHandles( false ) to unbind
2372 * @param {Boolean} isBind - will unbind if falsey
2373 */
2374proto._bindHandles = function( isBind ) {
2375  // munge isBind, default to true
2376  isBind = isBind === undefined ? true : !!isBind;
2377  // extra bind logic
2378  var binderExtra;
2379  if ( navigator.pointerEnabled ) {
2380    binderExtra = function( handle ) {
2381      // disable scrolling on the element
2382      handle.style.touchAction = isBind ? 'none' : '';
2383    };
2384  } else if ( navigator.msPointerEnabled ) {
2385    binderExtra = function( handle ) {
2386      // disable scrolling on the element
2387      handle.style.msTouchAction = isBind ? 'none' : '';
2388    };
2389  } else {
2390    binderExtra = noop;
2391  }
2392  // bind each handle
2393  var bindMethod = isBind ? 'addEventListener' : 'removeEventListener';
2394  for ( var i=0; i < this.handles.length; i++ ) {
2395    var handle = this.handles[i];
2396    this._bindStartEvent( handle, isBind );
2397    binderExtra( handle );
2398    handle[ bindMethod ]( 'click', this );
2399  }
2400};
2401
2402// ----- start event ----- //
2403
2404/**
2405 * pointer start
2406 * @param {Event} event
2407 * @param {Event or Touch} pointer
2408 */
2409proto.pointerDown = function( event, pointer ) {
2410  // dismiss range sliders
2411  if ( event.target.nodeName == 'INPUT' && event.target.type == 'range' ) {
2412    // reset pointerDown logic
2413    this.isPointerDown = false;
2414    delete this.pointerIdentifier;
2415    return;
2416  }
2417
2418  this._dragPointerDown( event, pointer );
2419  // kludge to blur focused inputs in dragger
2420  var focused = document.activeElement;
2421  if ( focused && focused.blur ) {
2422    focused.blur();
2423  }
2424  // bind move and end events
2425  this._bindPostStartEvents( event );
2426  this.emitEvent( 'pointerDown', [ event, pointer ] );
2427};
2428
2429// base pointer down logic
2430proto._dragPointerDown = function( event, pointer ) {
2431  // track to see when dragging starts
2432  this.pointerDownPoint = Unipointer.getPointerPoint( pointer );
2433
2434  var canPreventDefault = this.canPreventDefaultOnPointerDown( event, pointer );
2435  if ( canPreventDefault ) {
2436    event.preventDefault();
2437  }
2438};
2439
2440// overwriteable method so Flickity can prevent for scrolling
2441proto.canPreventDefaultOnPointerDown = function( event ) {
2442  // prevent default, unless touchstart or <select>
2443  return event.target.nodeName != 'SELECT';
2444};
2445
2446// ----- move event ----- //
2447
2448/**
2449 * drag move
2450 * @param {Event} event
2451 * @param {Event or Touch} pointer
2452 */
2453proto.pointerMove = function( event, pointer ) {
2454  var moveVector = this._dragPointerMove( event, pointer );
2455  this.emitEvent( 'pointerMove', [ event, pointer, moveVector ] );
2456  this._dragMove( event, pointer, moveVector );
2457};
2458
2459// base pointer move logic
2460proto._dragPointerMove = function( event, pointer ) {
2461  var movePoint = Unipointer.getPointerPoint( pointer );
2462  var moveVector = {
2463    x: movePoint.x - this.pointerDownPoint.x,
2464    y: movePoint.y - this.pointerDownPoint.y
2465  };
2466  // start drag if pointer has moved far enough to start drag
2467  if ( !this.isDragging && this.hasDragStarted( moveVector ) ) {
2468    this._dragStart( event, pointer );
2469  }
2470  return moveVector;
2471};
2472
2473// condition if pointer has moved far enough to start drag
2474proto.hasDragStarted = function( moveVector ) {
2475  return Math.abs( moveVector.x ) > 3 || Math.abs( moveVector.y ) > 3;
2476};
2477
2478
2479// ----- end event ----- //
2480
2481/**
2482 * pointer up
2483 * @param {Event} event
2484 * @param {Event or Touch} pointer
2485 */
2486proto.pointerUp = function( event, pointer ) {
2487  this.emitEvent( 'pointerUp', [ event, pointer ] );
2488  this._dragPointerUp( event, pointer );
2489};
2490
2491proto._dragPointerUp = function( event, pointer ) {
2492  if ( this.isDragging ) {
2493    this._dragEnd( event, pointer );
2494  } else {
2495    // pointer didn't move enough for drag to start
2496    this._staticClick( event, pointer );
2497  }
2498};
2499
2500// -------------------------- drag -------------------------- //
2501
2502// dragStart
2503proto._dragStart = function( event, pointer ) {
2504  this.isDragging = true;
2505  this.dragStartPoint = Unipointer.getPointerPoint( pointer );
2506  // prevent clicks
2507  this.isPreventingClicks = true;
2508
2509  this.dragStart( event, pointer );
2510};
2511
2512proto.dragStart = function( event, pointer ) {
2513  this.emitEvent( 'dragStart', [ event, pointer ] );
2514};
2515
2516// dragMove
2517proto._dragMove = function( event, pointer, moveVector ) {
2518  // do not drag if not dragging yet
2519  if ( !this.isDragging ) {
2520    return;
2521  }
2522
2523  this.dragMove( event, pointer, moveVector );
2524};
2525
2526proto.dragMove = function( event, pointer, moveVector ) {
2527  event.preventDefault();
2528  this.emitEvent( 'dragMove', [ event, pointer, moveVector ] );
2529};
2530
2531// dragEnd
2532proto._dragEnd = function( event, pointer ) {
2533  // set flags
2534  this.isDragging = false;
2535  // re-enable clicking async
2536  setTimeout( function() {
2537    delete this.isPreventingClicks;
2538  }.bind( this ) );
2539
2540  this.dragEnd( event, pointer );
2541};
2542
2543proto.dragEnd = function( event, pointer ) {
2544  this.emitEvent( 'dragEnd', [ event, pointer ] );
2545};
2546
2547// ----- onclick ----- //
2548
2549// handle all clicks and prevent clicks when dragging
2550proto.onclick = function( event ) {
2551  if ( this.isPreventingClicks ) {
2552    event.preventDefault();
2553  }
2554};
2555
2556// ----- staticClick ----- //
2557
2558// triggered after pointer down & up with no/tiny movement
2559proto._staticClick = function( event, pointer ) {
2560  // ignore emulated mouse up clicks
2561  if ( this.isIgnoringMouseUp && event.type == 'mouseup' ) {
2562    return;
2563  }
2564
2565  // allow click in <input>s and <textarea>s
2566  var nodeName = event.target.nodeName;
2567  if ( nodeName == 'INPUT' || nodeName == 'TEXTAREA' ) {
2568    event.target.focus();
2569  }
2570  this.staticClick( event, pointer );
2571
2572  // set flag for emulated clicks 300ms after touchend
2573  if ( event.type != 'mouseup' ) {
2574    this.isIgnoringMouseUp = true;
2575    // reset flag after 300ms
2576    setTimeout( function() {
2577      delete this.isIgnoringMouseUp;
2578    }.bind( this ), 400 );
2579  }
2580};
2581
2582proto.staticClick = function( event, pointer ) {
2583  this.emitEvent( 'staticClick', [ event, pointer ] );
2584};
2585
2586// ----- utils ----- //
2587
2588Unidragger.getPointerPoint = Unipointer.getPointerPoint;
2589
2590// -----  ----- //
2591
2592return Unidragger;
2593
2594}));
2595
2596// drag
2597( function( window, factory ) {
2598  // universal module definition
2599  /* jshint strict: false */
2600  if ( typeof define == 'function' && define.amd ) {
2601    // AMD
2602    define( 'flickity/js/drag',[
2603      './flickity',
2604      'unidragger/unidragger',
2605      'fizzy-ui-utils/utils'
2606    ], function( Flickity, Unidragger, utils ) {
2607      return factory( window, Flickity, Unidragger, utils );
2608    });
2609  } else if ( typeof module == 'object' && module.exports ) {
2610    // CommonJS
2611    module.exports = factory(
2612      window,
2613      require('./flickity'),
2614      require('unidragger'),
2615      require('fizzy-ui-utils')
2616    );
2617  } else {
2618    // browser global
2619    window.Flickity = factory(
2620      window,
2621      window.Flickity,
2622      window.Unidragger,
2623      window.fizzyUIUtils
2624    );
2625  }
2626
2627}( window, function factory( window, Flickity, Unidragger, utils ) {
2628
2629
2630
2631// ----- defaults ----- //
2632
2633utils.extend( Flickity.defaults, {
2634  draggable: true,
2635  dragThreshold: 3,
2636});
2637
2638// ----- create ----- //
2639
2640Flickity.createMethods.push('_createDrag');
2641
2642// -------------------------- drag prototype -------------------------- //
2643
2644var proto = Flickity.prototype;
2645utils.extend( proto, Unidragger.prototype );
2646
2647// --------------------------  -------------------------- //
2648
2649var isTouch = 'createTouch' in document;
2650var isTouchmoveScrollCanceled = false;
2651
2652proto._createDrag = function() {
2653  this.on( 'activate', this.bindDrag );
2654  this.on( 'uiChange', this._uiChangeDrag );
2655  this.on( 'childUIPointerDown', this._childUIPointerDownDrag );
2656  this.on( 'deactivate', this.unbindDrag );
2657  // HACK - add seemingly innocuous handler to fix iOS 10 scroll behavior
2658  // #457, RubaXa/Sortable#973
2659  if ( isTouch && !isTouchmoveScrollCanceled ) {
2660    window.addEventListener( 'touchmove', function() {});
2661    isTouchmoveScrollCanceled = true;
2662  }
2663};
2664
2665proto.bindDrag = function() {
2666  if ( !this.options.draggable || this.isDragBound ) {
2667    return;
2668  }
2669  this.element.classList.add('is-draggable');
2670  this.handles = [ this.viewport ];
2671  this.bindHandles();
2672  this.isDragBound = true;
2673};
2674
2675proto.unbindDrag = function() {
2676  if ( !this.isDragBound ) {
2677    return;
2678  }
2679  this.element.classList.remove('is-draggable');
2680  this.unbindHandles();
2681  delete this.isDragBound;
2682};
2683
2684proto._uiChangeDrag = function() {
2685  delete this.isFreeScrolling;
2686};
2687
2688proto._childUIPointerDownDrag = function( event ) {
2689  event.preventDefault();
2690  this.pointerDownFocus( event );
2691};
2692
2693// -------------------------- pointer events -------------------------- //
2694
2695// nodes that have text fields
2696var cursorNodes = {
2697  TEXTAREA: true,
2698  INPUT: true,
2699  OPTION: true,
2700};
2701
2702// input types that do not have text fields
2703var clickTypes = {
2704  radio: true,
2705  checkbox: true,
2706  button: true,
2707  submit: true,
2708  image: true,
2709  file: true,
2710};
2711
2712proto.pointerDown = function( event, pointer ) {
2713  // dismiss inputs with text fields. #403, #404
2714  var isCursorInput = cursorNodes[ event.target.nodeName ] &&
2715    !clickTypes[ event.target.type ];
2716  if ( isCursorInput ) {
2717    // reset pointerDown logic
2718    this.isPointerDown = false;
2719    delete this.pointerIdentifier;
2720    return;
2721  }
2722
2723  this._dragPointerDown( event, pointer );
2724
2725  // kludge to blur focused inputs in dragger
2726  var focused = document.activeElement;
2727  if ( focused && focused.blur && focused != this.element &&
2728    // do not blur body for IE9 & 10, #117
2729    focused != document.body ) {
2730    focused.blur();
2731  }
2732  this.pointerDownFocus( event );
2733  // stop if it was moving
2734  this.dragX = this.x;
2735  this.viewport.classList.add('is-pointer-down');
2736  // bind move and end events
2737  this._bindPostStartEvents( event );
2738  // track scrolling
2739  this.pointerDownScroll = getScrollPosition();
2740  window.addEventListener( 'scroll', this );
2741
2742  this.dispatchEvent( 'pointerDown', event, [ pointer ] );
2743};
2744
2745var touchStartEvents = {
2746  touchstart: true,
2747  MSPointerDown: true
2748};
2749
2750var focusNodes = {
2751  INPUT: true,
2752  SELECT: true
2753};
2754
2755proto.pointerDownFocus = function( event ) {
2756  // focus element, if not touch, and its not an input or select
2757  if ( !this.options.accessibility || touchStartEvents[ event.type ] ||
2758      focusNodes[ event.target.nodeName ] ) {
2759    return;
2760  }
2761  var prevScrollY = window.pageYOffset;
2762  this.element.focus();
2763  // hack to fix scroll jump after focus, #76
2764  if ( window.pageYOffset != prevScrollY ) {
2765    window.scrollTo( window.pageXOffset, prevScrollY );
2766  }
2767};
2768
2769proto.canPreventDefaultOnPointerDown = function( event ) {
2770  // prevent default, unless touchstart or <select>
2771  var isTouchstart = event.type == 'touchstart';
2772  var targetNodeName = event.target.nodeName;
2773  return !isTouchstart && targetNodeName != 'SELECT';
2774};
2775
2776// ----- move ----- //
2777
2778proto.hasDragStarted = function( moveVector ) {
2779  return Math.abs( moveVector.x ) > this.options.dragThreshold;
2780};
2781
2782// ----- up ----- //
2783
2784proto.pointerUp = function( event, pointer ) {
2785  delete this.isTouchScrolling;
2786  this.viewport.classList.remove('is-pointer-down');
2787  this.dispatchEvent( 'pointerUp', event, [ pointer ] );
2788  this._dragPointerUp( event, pointer );
2789};
2790
2791proto.pointerDone = function() {
2792  window.removeEventListener( 'scroll', this );
2793  delete this.pointerDownScroll;
2794};
2795
2796// -------------------------- dragging -------------------------- //
2797
2798proto.dragStart = function( event, pointer ) {
2799  this.dragStartPosition = this.x;
2800  this.startAnimation();
2801  window.removeEventListener( 'scroll', this );
2802  this.dispatchEvent( 'dragStart', event, [ pointer ] );
2803};
2804
2805proto.pointerMove = function( event, pointer ) {
2806  var moveVector = this._dragPointerMove( event, pointer );
2807  this.dispatchEvent( 'pointerMove', event, [ pointer, moveVector ] );
2808  this._dragMove( event, pointer, moveVector );
2809};
2810
2811proto.dragMove = function( event, pointer, moveVector ) {
2812  event.preventDefault();
2813
2814  this.previousDragX = this.dragX;
2815  // reverse if right-to-left
2816  var direction = this.options.rightToLeft ? -1 : 1;
2817  var dragX = this.dragStartPosition + moveVector.x * direction;
2818
2819  if ( !this.options.wrapAround && this.slides.length ) {
2820    // slow drag
2821    var originBound = Math.max( -this.slides[0].target, this.dragStartPosition );
2822    dragX = dragX > originBound ? ( dragX + originBound ) * 0.5 : dragX;
2823    var endBound = Math.min( -this.getLastSlide().target, this.dragStartPosition );
2824    dragX = dragX < endBound ? ( dragX + endBound ) * 0.5 : dragX;
2825  }
2826
2827  this.dragX = dragX;
2828
2829  this.dragMoveTime = new Date();
2830  this.dispatchEvent( 'dragMove', event, [ pointer, moveVector ] );
2831};
2832
2833proto.dragEnd = function( event, pointer ) {
2834  if ( this.options.freeScroll ) {
2835    this.isFreeScrolling = true;
2836  }
2837  // set selectedIndex based on where flick will end up
2838  var index = this.dragEndRestingSelect();
2839
2840  if ( this.options.freeScroll && !this.options.wrapAround ) {
2841    // if free-scroll & not wrap around
2842    // do not free-scroll if going outside of bounding slides
2843    // so bounding slides can attract slider, and keep it in bounds
2844    var restingX = this.getRestingPosition();
2845    this.isFreeScrolling = -restingX > this.slides[0].target &&
2846      -restingX < this.getLastSlide().target;
2847  } else if ( !this.options.freeScroll && index == this.selectedIndex ) {
2848    // boost selection if selected index has not changed
2849    index += this.dragEndBoostSelect();
2850  }
2851  delete this.previousDragX;
2852  // apply selection
2853  // TODO refactor this, selecting here feels weird
2854  // HACK, set flag so dragging stays in correct direction
2855  this.isDragSelect = this.options.wrapAround;
2856  this.select( index );
2857  delete this.isDragSelect;
2858  this.dispatchEvent( 'dragEnd', event, [ pointer ] );
2859};
2860
2861proto.dragEndRestingSelect = function() {
2862  var restingX = this.getRestingPosition();
2863  // how far away from selected slide
2864  var distance = Math.abs( this.getSlideDistance( -restingX, this.selectedIndex ) );
2865  // get closet resting going up and going down
2866  var positiveResting = this._getClosestResting( restingX, distance, 1 );
2867  var negativeResting = this._getClosestResting( restingX, distance, -1 );
2868  // use closer resting for wrap-around
2869  var index = positiveResting.distance < negativeResting.distance ?
2870    positiveResting.index : negativeResting.index;
2871  return index;
2872};
2873
2874/**
2875 * given resting X and distance to selected cell
2876 * get the distance and index of the closest cell
2877 * @param {Number} restingX - estimated post-flick resting position
2878 * @param {Number} distance - distance to selected cell
2879 * @param {Integer} increment - +1 or -1, going up or down
2880 * @returns {Object} - { distance: {Number}, index: {Integer} }
2881 */
2882proto._getClosestResting = function( restingX, distance, increment ) {
2883  var index = this.selectedIndex;
2884  var minDistance = Infinity;
2885  var condition = this.options.contain && !this.options.wrapAround ?
2886    // if contain, keep going if distance is equal to minDistance
2887    function( d, md ) { return d <= md; } : function( d, md ) { return d < md; };
2888  while ( condition( distance, minDistance ) ) {
2889    // measure distance to next cell
2890    index += increment;
2891    minDistance = distance;
2892    distance = this.getSlideDistance( -restingX, index );
2893    if ( distance === null ) {
2894      break;
2895    }
2896    distance = Math.abs( distance );
2897  }
2898  return {
2899    distance: minDistance,
2900    // selected was previous index
2901    index: index - increment
2902  };
2903};
2904
2905/**
2906 * measure distance between x and a slide target
2907 * @param {Number} x
2908 * @param {Integer} index - slide index
2909 */
2910proto.getSlideDistance = function( x, index ) {
2911  var len = this.slides.length;
2912  // wrap around if at least 2 slides
2913  var isWrapAround = this.options.wrapAround && len > 1;
2914  var slideIndex = isWrapAround ? utils.modulo( index, len ) : index;
2915  var slide = this.slides[ slideIndex ];
2916  if ( !slide ) {
2917    return null;
2918  }
2919  // add distance for wrap-around slides
2920  var wrap = isWrapAround ? this.slideableWidth * Math.floor( index / len ) : 0;
2921  return x - ( slide.target + wrap );
2922};
2923
2924proto.dragEndBoostSelect = function() {
2925  // do not boost if no previousDragX or dragMoveTime
2926  if ( this.previousDragX === undefined || !this.dragMoveTime ||
2927    // or if drag was held for 100 ms
2928    new Date() - this.dragMoveTime > 100 ) {
2929    return 0;
2930  }
2931
2932  var distance = this.getSlideDistance( -this.dragX, this.selectedIndex );
2933  var delta = this.previousDragX - this.dragX;
2934  if ( distance > 0 && delta > 0 ) {
2935    // boost to next if moving towards the right, and positive velocity
2936    return 1;
2937  } else if ( distance < 0 && delta < 0 ) {
2938    // boost to previous if moving towards the left, and negative velocity
2939    return -1;
2940  }
2941  return 0;
2942};
2943
2944// ----- staticClick ----- //
2945
2946proto.staticClick = function( event, pointer ) {
2947  // get clickedCell, if cell was clicked
2948  var clickedCell = this.getParentCell( event.target );
2949  var cellElem = clickedCell && clickedCell.element;
2950  var cellIndex = clickedCell && this.cells.indexOf( clickedCell );
2951  this.dispatchEvent( 'staticClick', event, [ pointer, cellElem, cellIndex ] );
2952};
2953
2954// ----- scroll ----- //
2955
2956proto.onscroll = function() {
2957  var scroll = getScrollPosition();
2958  var scrollMoveX = this.pointerDownScroll.x - scroll.x;
2959  var scrollMoveY = this.pointerDownScroll.y - scroll.y;
2960  // cancel click/tap if scroll is too much
2961  if ( Math.abs( scrollMoveX ) > 3 || Math.abs( scrollMoveY ) > 3 ) {
2962    this._pointerDone();
2963  }
2964};
2965
2966// ----- utils ----- //
2967
2968function getScrollPosition() {
2969  return {
2970    x: window.pageXOffset,
2971    y: window.pageYOffset
2972  };
2973}
2974
2975// -----  ----- //
2976
2977return Flickity;
2978
2979}));
2980
2981/*!
2982 * Tap listener v2.0.0
2983 * listens to taps
2984 * MIT license
2985 */
2986
2987/*jshint browser: true, unused: true, undef: true, strict: true */
2988
2989( function( window, factory ) {
2990  // universal module definition
2991  /*jshint strict: false*/ /*globals define, module, require */
2992
2993  if ( typeof define == 'function' && define.amd ) {
2994    // AMD
2995    define( 'tap-listener/tap-listener',[
2996      'unipointer/unipointer'
2997    ], function( Unipointer ) {
2998      return factory( window, Unipointer );
2999    });
3000  } else if ( typeof module == 'object' && module.exports ) {
3001    // CommonJS
3002    module.exports = factory(
3003      window,
3004      require('unipointer')
3005    );
3006  } else {
3007    // browser global
3008    window.TapListener = factory(
3009      window,
3010      window.Unipointer
3011    );
3012  }
3013
3014}( window, function factory( window, Unipointer ) {
3015
3016
3017
3018// --------------------------  TapListener -------------------------- //
3019
3020function TapListener( elem ) {
3021  this.bindTap( elem );
3022}
3023
3024// inherit Unipointer & EventEmitter
3025var proto = TapListener.prototype = Object.create( Unipointer.prototype );
3026
3027/**
3028 * bind tap event to element
3029 * @param {Element} elem
3030 */
3031proto.bindTap = function( elem ) {
3032  if ( !elem ) {
3033    return;
3034  }
3035  this.unbindTap();
3036  this.tapElement = elem;
3037  this._bindStartEvent( elem, true );
3038};
3039
3040proto.unbindTap = function() {
3041  if ( !this.tapElement ) {
3042    return;
3043  }
3044  this._bindStartEvent( this.tapElement, true );
3045  delete this.tapElement;
3046};
3047
3048/**
3049 * pointer up
3050 * @param {Event} event
3051 * @param {Event or Touch} pointer
3052 */
3053proto.pointerUp = function( event, pointer ) {
3054  // ignore emulated mouse up clicks
3055  if ( this.isIgnoringMouseUp && event.type == 'mouseup' ) {
3056    return;
3057  }
3058
3059  var pointerPoint = Unipointer.getPointerPoint( pointer );
3060  var boundingRect = this.tapElement.getBoundingClientRect();
3061  var scrollX = window.pageXOffset;
3062  var scrollY = window.pageYOffset;
3063  // calculate if pointer is inside tapElement
3064  var isInside = pointerPoint.x >
3064= boundingRect.left + scrollX &&
3065    pointerPoint.x <= boundingRect.right + scrollX &&
3066    pointerPoint.y >= boundingRect.top + scrollY &&
3067    pointerPoint.y <= boundingRect.bottom + scrollY;
3068  // trigger callback if pointer is inside element
3069  if ( isInside ) {
3070    this.emitEvent( 'tap', [ event, pointer ] );
3071  }
3072
3073  // set flag for emulated clicks 300ms after touchend
3074  if ( event.type != 'mouseup' ) {
3075    this.isIgnoringMouseUp = true;
3076    // reset flag after 300ms
3077    var _this = this;
3078    setTimeout( function() {
3079      delete _this.isIgnoringMouseUp;
3080    }, 400 );
3081  }
3082};
3083
3084proto.destroy = function() {
3085  this.pointerDone();
3086  this.unbindTap();
3087};
3088
3089// -----  ----- //
3090
3091return TapListener;
3092
3093}));
3094
3095// prev/next buttons
3096( function( window, factory ) {
3097  // universal module definition
3098  /* jshint strict: false */
3099  if ( typeof define == 'function' && define.amd ) {
3100    // AMD
3101    define( 'flickity/js/prev-next-button',[
3102      './flickity',
3103      'tap-listener/tap-listener',
3104      'fizzy-ui-utils/utils'
3105    ], function( Flickity, TapListener, utils ) {
3106      return factory( window, Flickity, TapListener, utils );
3107    });
3108  } else if ( typeof module == 'object' && module.exports ) {
3109    // CommonJS
3110    module.exports = factory(
3111      window,
3112      require('./flickity'),
3113      require('tap-listener'),
3114      require('fizzy-ui-utils')
3115    );
3116  } else {
3117    // browser global
3118    factory(
3119      window,
3120      window.Flickity,
3121      window.TapListener,
3122      window.fizzyUIUtils
3123    );
3124  }
3125
3126}( window, function factory( window, Flickity, TapListener, utils ) {
3127'use strict';
3128
3129var svgURI = 'http://www.w3.org/2000/svg';
3130
3131// -------------------------- PrevNextButton -------------------------- //
3132
3133function PrevNextButton( direction, parent ) {
3134  this.direction = direction;
3135  this.parent = parent;
3136  this._create();
3137}
3138
3139PrevNextButton.prototype = new TapListener();
3140
3141PrevNextButton.prototype._create = function() {
3142  // properties
3143  this.isEnabled = true;
3144  this.isPrevious = this.direction == -1;
3145  var leftDirection = this.parent.options.rightToLeft ? 1 : -1;
3146  this.isLeft = this.direction == leftDirection;
3147
3148  var element = this.element = document.createElement('button');
3149  element.className = 'flickity-prev-next-button';
3150  element.className += this.isPrevious ? ' previous' : ' next';
3151  // prevent button from submitting form http://stackoverflow.com/a/10836076/182183
3152  element.setAttribute( 'type', 'button' );
3153  // init as disabled
3154  this.disable();
3155
3156  element.setAttribute( 'aria-label', this.isPrevious ? 'previous' : 'next' );
3157
3158  // create arrow
3159  var svg = this.createSVG();
3160  element.appendChild( svg );
3161  // events
3162  this.on( 'tap', this.onTap );
3163  this.parent.on( 'select', this.update.bind( this ) );
3164  this.on( 'pointerDown', this.parent.childUIPointerDown.bind( this.parent ) );
3165};
3166
3167PrevNextButton.prototype.activate = function() {
3168  this.bindTap( this.element );
3169  // click events from keyboard
3170  this.element.addEventListener( 'click', this );
3171  // add to DOM
3172  this.parent.element.appendChild( this.element );
3173};
3174
3175PrevNextButton.prototype.deactivate = function() {
3176  // remove from DOM
3177  this.parent.element.removeChild( this.element );
3178  // do regular TapListener destroy
3179  TapListener.prototype.destroy.call( this );
3180  // click events from keyboard
3181  this.element.removeEventListener( 'click', this );
3182};
3183
3184PrevNextButton.prototype.createSVG = function() {
3185  var svg = document.createElementNS( svgURI, 'svg');
3186  svg.setAttribute( 'viewBox', '0 0 100 100' );
3187  var path = document.createElementNS( svgURI, 'path');
3188  var pathMovements = getArrowMovements( this.parent.options.arrowShape );
3189  path.setAttribute( 'd', pathMovements );
3190  path.setAttribute( 'class', 'arrow' );
3191  // rotate arrow
3192  if ( !this.isLeft ) {
3193    path.setAttribute( 'transform', 'translate(100, 100) rotate(180) ' );
3194  }
3195  svg.appendChild( path );
3196  return svg;
3197};
3198
3199// get SVG path movmement
3200function getArrowMovements( shape ) {
3201  // use shape as movement if string
3202  if ( typeof shape == 'string' ) {
3203    return shape;
3204  }
3205  // create movement string
3206  return 'M ' + shape.x0 + ',50' +
3207    ' L ' + shape.x1 + ',' + ( shape.y1 + 50 ) +
3208    ' L ' + shape.x2 + ',' + ( shape.y2 + 50 ) +
3209    ' L ' + shape.x3 + ',50 ' +
3210    ' L ' + shape.x2 + ',' + ( 50 - shape.y2 ) +
3211    ' L ' + shape.x1 + ',' + ( 50 - shape.y1 ) +
3212    ' Z';
3213}
3214
3215PrevNextButton.prototype.onTap = function() {
3216  if ( !this.isEnabled ) {
3217    return;
3218  }
3219  this.parent.uiChange();
3220  var method = this.isPrevious ? 'previous' : 'next';
3221  this.parent[ method ]();
3222};
3223
3224PrevNextButton.prototype.handleEvent = utils.handleEvent;
3225
3226PrevNextButton.prototype.onclick = function() {
3227  // only allow clicks from keyboard
3228  var focused = document.activeElement;
3229  if ( focused && focused == this.element ) {
3230    this.onTap();
3231  }
3232};
3233
3234// -----  ----- //
3235
3236PrevNextButton.prototype.enable = function() {
3237  if ( this.isEnabled ) {
3238    return;
3239  }
3240  this.element.disabled = false;
3241  this.isEnabled = true;
3242};
3243
3244PrevNextButton.prototype.disable = function() {
3245  if ( !this.isEnabled ) {
3246    return;
3247  }
3248  this.element.disabled = true;
3249  this.isEnabled = false;
3250};
3251
3252PrevNextButton.prototype.update = function() {
3253  // index of first or last slide, if previous or next
3254  var slides = this.parent.slides;
3255  // enable is wrapAround and at least 2 slides
3256  if ( this.parent.options.wrapAround && slides.length > 1 ) {
3257    this.enable();
3258    return;
3259  }
3260  var lastIndex = slides.length ? slides.length - 1 : 0;
3261  var boundIndex = this.isPrevious ? 0 : lastIndex;
3262  var method = this.parent.selectedIndex == boundIndex ? 'disable' : 'enable';
3263  this[ method ]();
3264};
3265
3266PrevNextButton.prototype.destroy = function() {
3267  this.deactivate();
3268};
3269
3270// -------------------------- Flickity prototype -------------------------- //
3271
3272utils.extend( Flickity.defaults, {
3273  prevNextButtons: true,
3274  arrowShape: {
3275    x0: 10,
3276    x1: 60, y1: 50,
3277    x2: 70, y2: 40,
3278    x3: 30
3279  }
3280});
3281
3282Flickity.createMethods.push('_createPrevNextButtons');
3283var proto = Flickity.prototype;
3284
3285proto._createPrevNextButtons = function() {
3286  if ( !this.options.prevNextButtons ) {
3287    return;
3288  }
3289
3290  this.prevButton = new PrevNextButton( -1, this );
3291  this.nextButton = new PrevNextButton( 1, this );
3292
3293  this.on( 'activate', this.activatePrevNextButtons );
3294};
3295
3296proto.activatePrevNextButtons = function() {
3297  this.prevButton.activate();
3298  this.nextButton.activate();
3299  this.on( 'deactivate', this.deactivatePrevNextButtons );
3300};
3301
3302proto.deactivatePrevNextButtons = function() {
3303  this.prevButton.deactivate();
3304  this.nextButton.deactivate();
3305  this.off( 'deactivate', this.deactivatePrevNextButtons );
3306};
3307
3308// --------------------------  -------------------------- //
3309
3310Flickity.PrevNextButton = PrevNextButton;
3311
3312return Flickity;
3313
3314}));
3315
3316// page dots
3317( function( window, factory ) {
3318  // universal module definition
3319  /* jshint strict: false */
3320  if ( typeof define == 'function' && define.amd ) {
3321    // AMD
3322    define( 'flickity/js/page-dots',[
3323      './flickity',
3324      'tap-listener/tap-listener',
3325      'fizzy-ui-utils/utils'
3326    ], function( Flickity, TapListener, utils ) {
3327      return factory( window, Flickity, TapListener, utils );
3328    });
3329  } else if ( typeof module == 'object' && module.exports ) {
3330    // CommonJS
3331    module.exports = factory(
3332      window,
3333      require('./flickity'),
3334      require('tap-listener'),
3335      require('fizzy-ui-utils')
3336    );
3337  } else {
3338    // browser global
3339    factory(
3340      window,
3341      window.Flickity,
3342      window.TapListener,
3343      window.fizzyUIUtils
3344    );
3345  }
3346
3347}( window, function factory( window, Flickity, TapListener, utils ) {
3348
3349// -------------------------- PageDots -------------------------- //
3350
3351
3352
3353function PageDots( parent ) {
3354  this.parent = parent;
3355  this._create();
3356}
3357
3358PageDots.prototype = new TapListener();
3359
3360PageDots.prototype._create = function() {
3361  // create holder element
3362  this.holder = document.createElement('ol');
3363  this.holder.className = 'flickity-page-dots';
3364  // create dots, array of elements
3365  this.dots = [];
3366  // events
3367  this.on( 'tap', this.onTap );
3368  this.on( 'pointerDown', this.parent.childUIPointerDown.bind( this.parent ) );
3369};
3370
3371PageDots.prototype.activate = function() {
3372  this.setDots();
3373  this.bindTap( this.holder );
3374  // add to DOM
3375  this.parent.element.appendChild( this.holder );
3376};
3377
3378PageDots.prototype.deactivate = function() {
3379  // remove from DOM
3380  this.parent.element.removeChild( this.holder );
3381  TapListener.prototype.destroy.call( this );
3382};
3383
3384PageDots.prototype.setDots = function() {
3385  // get difference between number of slides and number of dots
3386  var delta = this.parent.slides.length - this.dots.length;
3387  if ( delta > 0 ) {
3388    this.addDots( delta );
3389  } else if ( delta < 0 ) {
3390    this.removeDots( -delta );
3391  }
3392};
3393
3394PageDots.prototype.addDots = function( count ) {
3395  var fragment = document.createDocumentFragment();
3396  var newDots = [];
3397  while ( count ) {
3398    var dot = document.createElement('li');
3399    dot.className = 'dot';
3400    fragment.appendChild( dot );
3401    newDots.push( dot );
3402    count--;
3403  }
3404  this.holder.appendChild( fragment );
3405  this.dots = this.dots.concat( newDots );
3406};
3407
3408PageDots.prototype.removeDots = function( count ) {
3409  // remove from this.dots collection
3410  var removeDots = this.dots.splice( this.dots.length - count, count );
3411  // remove from DOM
3412  removeDots.forEach( function( dot ) {
3413    this.holder.removeChild( dot );
3414  }, this );
3415};
3416
3417PageDots.prototype.updateSelected = function() {
3418  // remove selected class on previous
3419  if ( this.selectedDot ) {
3420    this.selectedDot.className = 'dot';
3421  }
3422  // don't proceed if no dots
3423  if ( !this.dots.length ) {
3424    return;
3425  }
3426  this.selectedDot = this.dots[ this.parent.selectedIndex ];
3427  this.selectedDot.className = 'dot is-selected';
3428};
3429
3430PageDots.prototype.onTap = function( event ) {
3431  var target = event.target;
3432  // only care about dot clicks
3433  if ( target.nodeName != 'LI' ) {
3434    return;
3435  }
3436
3437  this.parent.uiChange();
3438  var index = this.dots.indexOf( target );
3439  this.parent.select( index );
3440};
3441
3442PageDots.prototype.destroy = function() {
3443  this.deactivate();
3444};
3445
3446Flickity.PageDots = PageDots;
3447
3448// -------------------------- Flickity -------------------------- //
3449
3450utils.extend( Flickity.defaults, {
3451  pageDots: true
3452});
3453
3454Flickity.createMethods.push('_createPageDots');
3455
3456var proto = Flickity.prototype;
3457
3458proto._createPageDots = function() {
3459  if ( !this.options.pageDots ) {
3460    return;
3461  }
3462  this.pageDots = new PageDots( this );
3463  // events
3464  this.on( 'activate', this.activatePageDots );
3465  this.on( 'select', this.updateSelectedPageDots );
3466  this.on( 'cellChange', this.updatePageDots );
3467  this.on( 'resize', this.updatePageDots );
3468  this.on( 'deactivate', this.deactivatePageDots );
3469};
3470
3471proto.activatePageDots = function() {
3472  this.pageDots.activate();
3473};
3474
3475proto.updateSelectedPageDots = function() {
3476  this.pageDots.updateSelected();
3477};
3478
3479proto.updatePageDots = function() {
3480  this.pageDots.setDots();
3481};
3482
3483proto.deactivatePageDots = function() {
3484  this.pageDots.deactivate();
3485};
3486
3487// -----  ----- //
3488
3489Flickity.PageDots = PageDots;
3490
3491return Flickity;
3492
3493}));
3494
3495// player & autoPlay
3496( function( window, factory ) {
3497  // universal module definition
3498  /* jshint strict: false */
3499  if ( typeof define == 'function' && define.amd ) {
3500    // AMD
3501    define( 'flickity/js/player',[
3502      'ev-emitter/ev-emitter',
3503      'fizzy-ui-utils/utils',
3504      './flickity'
3505    ], function( EvEmitter, utils, Flickity ) {
3506      return factory( EvEmitter, utils, Flickity );
3507    });
3508  } else if ( typeof module == 'object' && module.exports ) {
3509    // CommonJS
3510    module.exports = factory(
3511      require('ev-emitter'),
3512      require('fizzy-ui-utils'),
3513      require('./flickity')
3514    );
3515  } else {
3516    // browser global
3517    factory(
3518      window.EvEmitter,
3519      window.fizzyUIUtils,
3520      window.Flickity
3521    );
3522  }
3523
3524}( window, function factory( EvEmitter, utils, Flickity ) {
3525
3526
3527
3528// -------------------------- Page Visibility -------------------------- //
3529// https://developer.mozilla.org/en-US/docs/Web/Guide/User_experience/Using_the_Page_Visibility_API
3530
3531var hiddenProperty, visibilityEvent;
3532if ( 'hidden' in document ) {
3533  hiddenProperty = 'hidden';
3534  visibilityEvent = 'visibilitychange';
3535} else if ( 'webkitHidden' in document ) {
3536  hiddenProperty = 'webkitHidden';
3537  visibilityEvent = 'webkitvisibilitychange';
3538}
3539
3540// -------------------------- Player -------------------------- //
3541
3542function Player( parent ) {
3543  this.parent = parent;
3544  this.state = 'stopped';
3545  // visibility change event handler
3546  if ( visibilityEvent ) {
3547    this.onVisibilityChange = function() {
3548      this.visibilityChange();
3549    }.bind( this );
3550    this.onVisibilityPlay = function() {
3551      this.visibilityPlay();
3552    }.bind( this );
3553  }
3554}
3555
3556Player.prototype = Object.create( EvEmitter.prototype );
3557
3558// start play
3559Player.prototype.play = function() {
3560  if ( this.state == 'playing' ) {
3561    return;
3562  }
3563  // do not play if page is hidden, start playing when page is visible
3564  var isPageHidden = document[ hiddenProperty ];
3565  if ( visibilityEvent && isPageHidden ) {
3566    document.addEventListener( visibilityEvent, this.onVisibilityPlay );
3567    return;
3568  }
3569
3570  this.state = 'playing';
3571  // listen to visibility change
3572  if ( visibilityEvent ) {
3573    document.addEventListener( visibilityEvent, this.onVisibilityChange );
3574  }
3575  // start ticking
3576  this.tick();
3577};
3578
3579Player.prototype.tick = function() {
3580  // do not tick if not playing
3581  if ( this.state != 'playing' ) {
3582    return;
3583  }
3584
3585  var time = this.parent.options.autoPlay;
3586  // default to 3 seconds
3587  time = typeof time == 'number' ? time : 3000;
3588  var _this = this;
3589  // HACK: reset ticks if stopped and started within interval
3590  this.clear();
3591  this.timeout = setTimeout( function() {
3592    _this.parent.next( true );
3593    _this.tick();
3594  }, time );
3595};
3596
3597Player.prototype.stop = function() {
3598  this.state = 'stopped';
3599  this.clear();
3600  // remove visibility change event
3601  if ( visibilityEvent ) {
3602    document.removeEventListener( visibilityEvent, this.onVisibilityChange );
3603  }
3604};
3605
3606Player.prototype.clear = function() {
3607  clearTimeout( this.timeout );
3608};
3609
3610Player.prototype.pause = function() {
3611  if ( this.state == 'playing' ) {
3612    this.state = 'paused';
3613    this.clear();
3614  }
3615};
3616
3617Player.prototype.unpause = function() {
3618  // re-start play if paused
3619  if ( this.state == 'paused' ) {
3620    this.play();
3621  }
3622};
3623
3624// pause if page visibility is hidden, unpause if visible
3625Player.prototype.visibilityChange = function() {
3626  var isPageHidden = document[ hiddenProperty ];
3627  this[ isPageHidden ? 'pause' : 'unpause' ]();
3628};
3629
3630Player.prototype.visibilityPlay = function() {
3631  this.play();
3632  document.removeEventListener( visibilityEvent, this.onVisibilityPlay );
3633};
3634
3635// -------------------------- Flickity -------------------------- //
3636
3637utils.extend( Flickity.defaults, {
3638  pauseAutoPlayOnHover: true
3639});
3640
3641Flickity.createMethods.push('_createPlayer');
3642var proto = Flickity.prototype;
3643
3644proto._createPlayer = function() {
3645  this.player = new Player( this );
3646
3647  this.on( 'activate', this.activatePlayer );
3648  this.on( 'uiChange', this.stopPlayer );
3649  this.on( 'pointerDown', this.stopPlayer );
3650  this.on( 'deactivate', this.deactivatePlayer );
3651};
3652
3653proto.activatePlayer = function() {
3654  if ( !this.options.autoPlay ) {
3655    return;
3656  }
3657  this.player.play();
3658  this.element.addEventListener( 'mouseenter', this );
3659};
3660
3661// Player API, don't hate the ... thanks I know where the door is
3662
3663proto.playPlayer = function() {
3664  this.player.play();
3665};
3666
3667proto.stopPlayer = function() {
3668  this.player.stop();
3669};
3670
3671proto.pausePlayer = function() {
3672  this.player.pause();
3673};
3674
3675proto.unpausePlayer = function() {
3676  this.player.unpause();
3677};
3678
3679proto.deactivatePlayer = function() {
3680  this.player.stop();
3681  this.element.removeEventListener( 'mouseenter', this );
3682};
3683
3684// ----- mouseenter/leave ----- //
3685
3686// pause auto-play on hover
3687proto.onmouseenter = function() {
3688  if ( !this.options.pauseAutoPlayOnHover ) {
3689    return;
3690  }
3691  this.player.pause();
3692  this.element.addEventListener( 'mouseleave', this );
3693};
3694
3695// resume auto-play on hover off
3696proto.onmouseleave = function() {
3697  this.player.unpause();
3698  this.element.removeEventListener( 'mouseleave', this );
3699};
3700
3701// -----  ----- //
3702
3703Flickity.Player = Player;
3704
3705return Flickity;
3706
3707}));
3708
3709// add, remove cell
3710( function( window, factory ) {
3711  // universal module definition
3712  /* jshint strict: false */
3713  if ( typeof define == 'function' && define.amd ) {
3714    // AMD
3715    define( 'flickity/js/add-remove-cell',[
3716      './flickity',
3717      'fizzy-ui-utils/utils'
3718    ], function( Flickity, utils ) {
3719      return factory( window, Flickity, utils );
3720    });
3721  } else if ( typeof module == 'object' && module.exports ) {
3722    // CommonJS
3723    module.exports = factory(
3724      window,
3725      require('./flickity'),
3726      require('fizzy-ui-utils')
3727    );
3728  } else {
3729    // browser global
3730    factory(
3731      window,
3732      window.Flickity,
3733      window.fizzyUIUtils
3734    );
3735  }
3736
3737}( window, function factory( window, Flickity, utils ) {
3738
3739
3740
3741// append cells to a document fragment
3742function getCellsFragment( cells ) {
3743  var fragment = document.createDocumentFragment();
3744  cells.forEach( function( cell ) {
3745    fragment.appendChild( cell.element );
3746  });
3747  return fragment;
3748}
3749
3750// -------------------------- add/remove cell prototype -------------------------- //
3751
3752var proto = Flickity.prototype;
3753
3754/**
3755 * Insert, prepend, or append cells
3756 * @param {Element, Array, NodeList} elems
3757 * @param {Integer} index
3758 */
3759proto.insert = function( elems, index ) {
3760  var cells = this._makeCells( elems );
3761  if ( !cells || !cells.length ) {
3762    return;
3763  }
3764  var len = this.cells.length;
3765  // default to append
3766  index = index === undefined ? len : index;
3767  // add cells with document fragment
3768  var fragment = getCellsFragment( cells );
3769  // append to slider
3770  var isAppend = index == len;
3771  if ( isAppend ) {
3772    this.slider.appendChild( fragment );
3773  } else {
3774    var insertCellElement = this.cells[ index ].element;
3775    this.slider.insertBefore( fragment, insertCellElement );
3776  }
3777  // add to this.cells
3778  if ( index === 0 ) {
3779    // prepend, add to start
3780    this.cells = cells.concat( this.cells );
3781  } else if ( isAppend ) {
3782    // append, add to end
3783    this.cells = this.cells.concat( cells );
3784  } else {
3785    // insert in this.cells
3786    var endCells = this.cells.splice( index, len - index );
3787    this.cells = this.cells.concat( cells ).concat( endCells );
3788  }
3789
3790  this._sizeCells( cells );
3791
3792  var selectedIndexDelta = index > this.selectedIndex ? 0 : cells.length;
3793  this._cellAddedRemoved( index, selectedIndexDelta );
3794};
3795
3796proto.append = function( elems ) {
3797  this.insert( elems, this.cells.length );
3798};
3799
3800proto.prepend = function( elems ) {
3801  this.insert( elems, 0 );
3802};
3803
3804/**
3805 * Remove cells
3806 * @param {Element, Array, NodeList} elems
3807 */
3808proto.remove = function( elems ) {
3809  var cells = this.getCells( elems );
3810  var selectedIndexDelta = 0;
3811  var len = cells.length;
3812  var i, cell;
3813  // calculate selectedIndexDelta, easier if done in seperate loop
3814  for ( i=0; i < len; i++ ) {
3815    cell = cells[i];
3816    var wasBefore = this.cells.indexOf( cell ) < this.selectedIndex;
3817    selectedIndexDelta -= wasBefore ? 1 : 0;
3818  }
3819
3820  for ( i=0; i < len; i++ ) {
3821    cell = cells[i];
3822    cell.remove();
3823    // remove item from collection
3824    utils.removeFrom( this.cells, cell );
3825  }
3826
3827  if ( cells.length ) {
3828    // update stuff
3829    this._cellAddedRemoved( 0, selectedIndexDelta );
3830  }
3831};
3832
3833// updates when cells are added or removed
3834proto._cellAddedRemoved = function( changedCellIndex, selectedIndexDelta ) {
3835  // TODO this math isn't perfect with grouped slides
3836  selectedIndexDelta = selectedIndexDelta || 0;
3837  this.selectedIndex += selectedIndexDelta;
3838  this.selectedIndex = Math.max( 0, Math.min( this.slides.length - 1, this.selectedIndex ) );
3839
3840  this.cellChange( changedCellIndex, true );
3841  // backwards compatibility
3842  this.emitEvent( 'cellAddedRemoved', [ changedCellIndex, selectedIndexDelta ] );
3843};
3844
3845/**
3846 * logic to be run after a cell's size changes
3847 * @param {Element} elem - cell's element
3848 */
3849proto.cellSizeChange = function( elem ) {
3850  var cell = this.getCell( elem );
3851  if ( !cell ) {
3852    return;
3853  }
3854  cell.getSize();
3855
3856  var index = this.cells.indexOf( cell );
3857  this.cellChange( index );
3858};
3859
3860/**
3861 * logic any time a cell is changed: added, removed, or size changed
3862 * @param {Integer} changedCellIndex - index of the changed cell, optional
3863 */
3864proto.cellChange = function( changedCellIndex, isPositioningSlider ) {
3865  var prevSlideableWidth = this.slideableWidth;
3866  this._positionCells( changedCellIndex );
3867  this._getWrapShiftCells();
3868  this.setGallerySize();
3869  this.emitEvent( 'cellChange', [ changedCellIndex ] );
3870  // position slider
3871  if ( this.options.freeScroll ) {
3872    // shift x by change in slideableWidth
3873    // TODO fix position shifts when prepending w/ freeScroll
3874    var deltaX = prevSlideableWidth - this.slideableWidth;
3875    this.x += deltaX * this.cellAlign;
3876    this.positionSlider();
3877  } else {
3878    // do not position slider after lazy load
3879    if ( isPositioningSlider ) {
3880      this.positionSliderAtSelected();
3881    }
3882    this.select( this.selectedIndex );
3883  }
3884};
3885
3886// -----  ----- //
3887
3888return Flickity;
3889
3890}));
3891
3892// lazyload
3893( function( window, factory ) {
3894  // universal module definition
3895  /* jshint strict: false */
3896  if ( typeof define == 'function' && define.amd ) {
3897    // AMD
3898    define( 'flickity/js/lazyload',[
3899      './flickity',
3900      'fizzy-ui-utils/utils'
3901    ], function( Flickity, utils ) {
3902      return factory( window, Flickity, utils );
3903    });
3904  } else if ( typeof module == 'object' && module.exports ) {
3905    // CommonJS
3906    module.exports = factory(
3907      window,
3908      require('./flickity'),
3909      require('fizzy-ui-utils')
3910    );
3911  } else {
3912    // browser global
3913    factory(
3914      window,
3915      window.Flickity,
3916      window.fizzyUIUtils
3917    );
3918  }
3919
3920}( window, function factory( window, Flickity, utils ) {
3921'use strict';
3922
3923Flickity.createMethods.push('_createLazyload');
3924var proto = Flickity.prototype;
3925
3926proto._createLazyload = function() {
3927  this.on( 'select', this.lazyLoad );
3928};
3929
3930proto.lazyLoad = function() {
3931  var lazyLoad = this.options.lazyLoad;
3932  if ( !lazyLoad ) {
3933    return;
3934  }
3935  // get adjacent cells, use lazyLoad option for adjacent count
3936  var adjCount = typeof lazyLoad == 'number' ? lazyLoad : 0;
3937  var cellElems = this.getAdjacentCellElements( adjCount );
3938  // get lazy images in those cells
3939  var lazyImages = [];
3940  cellElems.forEach( function( cellElem ) {
3941    var lazyCellImages = getCellLazyImages( cellElem );
3942    lazyImages = lazyImages.concat( lazyCellImages );
3943  });
3944  // load lazy images
3945  lazyImages.forEach( function( img ) {
3946    new LazyLoader( img, this );
3947  }, this );
3948};
3949
3950function getCellLazyImages( cellElem ) {
3951  // check if cell element is lazy image
3952  if ( cellElem.nodeName == 'IMG' &&
3953    cellElem.getAttribute('data-flickity-lazyload') ) {
3954    return [ cellElem ];
3955  }
3956  // select lazy images in cell
3957  var imgs = cellElem.querySelectorAll('img[data-flickity-lazyload]');
3958  return utils.makeArray( imgs );
3959}
3960
3961// -------------------------- LazyLoader -------------------------- //
3962
3963/**
3964 * class to handle loading images
3965 */
3966function LazyLoader( img, flickity ) {
3967  this.img = img;
3968  this.flickity = flickity;
3969  this.load();
3970}
3971
3972LazyLoader.prototype.handleEvent = utils.handleEvent;
3973
3974LazyLoader.prototype.load = function() {
3975  this.img.addEventListener( 'load', this );
3976  this.img.addEventListener( 'error', this );
3977  // load image
3978  this.img.src = this.img.getAttribute('data-flickity-lazyload');
3979  // remove attr
3980  this.img.removeAttribute('data-flickity-lazyload');
3981};
3982
3983LazyLoader.prototype.onload = function( event ) {
3984  this.complete( event, 'flickity-lazyloaded' );
3985};
3986
3987LazyLoader.prototype.onerror = function( event ) {
3988  this.complete( event, 'flickity-lazyerror' );
3989};
3990
3991LazyLoader.prototype.complete = function( event, className ) {
3992  // unbind events
3993  this.img.removeEventListener( 'load', this );
3994  this.img.removeEventListener( 'error', this );
3995
3996  var cell = this.flickity.getParentCell( this.img );
3997  var cellElem = cell && cell.element;
3998  this.flickity.cellSizeChange( cellElem );
3999
4000  this.img.classList.add( className );
4001  this.flickity.dispatchEvent( 'lazyLoad', event, cellElem );
4002};
4003
4004// -----  ----- //
4005
4006Flickity.LazyLoader = LazyLoader;
4007
4008return Flickity;
4009
4010}));
4011
4012/*!
4013 * Flickity v2.0.5
4014 * Touch, responsive, flickable carousels
4015 *
4016 * Licensed GPLv3 for open source use
4017 * or Flickity Commercial License for commercial use
4018 *
4019 * http://flickity.metafizzy.co
4020 * Copyright 2016 Metafizzy
4021 */
4022
4023( function( window, factory ) {
4024  // universal module definition
4025  /* jshint strict: false */
4026  if ( typeof define == 'function' && define.amd ) {
4027    // AMD
4028    define( 'flickity/js/index',[
4029      './flickity',
4030      './drag',
4031      './prev-next-button',
4032      './page-dots',
4033      './player',
4034      './add-remove-cell',
4035      './lazyload'
4036    ], factory );
4037  } else if ( typeof module == 'object' && module.exports ) {
4038    // CommonJS
4039    module.exports = factory(
4040      require('./flickity'),
4041      require('./drag'),
4042      require('./prev-next-button'),
4043      require('./page-dots'),
4044      require('./player'),
4045      require('./add-remove-cell'),
4046      require('./lazyload')
4047    );
4048  }
4049
4050})( window, function factory( Flickity ) {
4051  /*jshint strict: false*/
4052  return Flickity;
4053});
4054
4055/*!
4056 * Flickity asNavFor v2.0.1
4057 * enable asNavFor for Flickity
4058 */
4059
4060/*jshint browser: true, undef: true, unused: true, strict: true*/
4061
4062( function( window, factory ) {
4063  // universal module definition
4064  /*jshint strict: false */ /*globals define, module, require */
4065  if ( typeof define == 'function' && define.amd ) {
4066    // AMD
4067    define( 'flickity-as-nav-for/as-nav-for',[
4068      'flickity/js/index',
4069      'fizzy-ui-utils/utils'
4070    ], factory );
4071  } else if ( typeof module == 'object' && module.exports ) {
4072    // CommonJS
4073    module.exports = factory(
4074      require('flickity'),
4075      require('fizzy-ui-utils')
4076    );
4077  } else {
4078    // browser global
4079    window.Flickity = factory(
4080      window.Flickity,
4081      window.fizzyUIUtils
4082    );
4083  }
4084
4085}( window, function factory( Flickity, utils ) {
4086
4087
4088
4089// -------------------------- asNavFor prototype -------------------------- //
4090
4091// Flickity.defaults.asNavFor = null;
4092
4093Flickity.createMethods.push('_createAsNavFor');
4094
4095var proto = Flickity.prototype;
4096
4097proto._createAsNavFor = function() {
4098  this.on( 'activate', this.activateAsNavFor );
4099  this.on( 'deactivate', this.deactivateAsNavFor );
4100  this.on( 'destroy', this.destroyAsNavFor );
4101
4102  var asNavForOption = this.options.asNavFor;
4103  if ( !asNavForOption ) {
4104    return;
4105  }
4106  // HACK do async, give time for other flickity to be initalized
4107  var _this = this;
4108  setTimeout( function initNavCompanion() {
4109    _this.setNavCompanion( asNavForOption );
4110  });
4111};
4112
4113proto.setNavCompanion = function( elem ) {
4114  elem = utils.getQueryElement( elem );
4115  var companion = Flickity.data( elem );
4116  // stop if no companion or companion is self
4117  if ( !companion || companion == this ) {
4118    return;
4119  }
4120
4121  this.navCompanion = companion;
4122  // companion select
4123  var _this = this;
4124  this.onNavCompanionSelect = function() {
4125    _this.navCompanionSelect();
4126  };
4127  companion.on( 'select', this.onNavCompanionSelect );
4128  // click
4129  this.on( 'staticClick', this.onNavStaticClick );
4130
4131  this.navCompanionSelect( true );
4132};
4133
4134proto.navCompanionSelect = function( isInstant ) {
4135  if ( !this.navCompanion ) {
4136    return;
4137  }
4138  // select slide that matches first cell of slide
4139  var selectedCell = this.navCompanion.selectedCells[0];
4140  var firstIndex = this.navCompanion.cells.indexOf( selectedCell );
4141  var lastIndex = firstIndex + this.navCompanion.selectedCells.length - 1;
4142  var selectIndex = Math.floor( lerp( firstIndex, lastIndex,
4143    this.navCompanion.cellAlign ) );
4144  this.selectCell( selectIndex, false, isInstant );
4145  // set nav selected class
4146  this.removeNavSelectedElements();
4147  // stop if companion has more cells than this one
4148  if ( selectIndex >= this.cells.length ) {
4149    return;
4150  }
4151
4152  var selectedCells = this.cells.slice( firstIndex, lastIndex + 1 );
4153  this.navSelectedElements = selectedCells.map( function( cell ) {
4154    return cell.element;
4155  });
4156  this.changeNavSelectedClass('add');
4157};
4158
4159function lerp( a, b, t ) {
4160  return ( b - a ) * t + a;
4161}
4162
4163proto.changeNavSelectedClass = function( method ) {
4164  this.navSelectedElements.forEach( function( navElem ) {
4165    navElem.classList[ method ]('is-nav-selected');
4166  });
4167};
4168
4169proto.activateAsNavFor = function() {
4170  this.navCompanionSelect( true );
4171};
4172
4173proto.removeNavSelectedElements = function() {
4174  if ( !this.navSelectedElements ) {
4175    return;
4176  }
4177  this.changeNavSelectedClass('remove');
4178  delete this.navSelectedElements;
4179};
4180
4181proto.onNavStaticClick = function( event, pointer, cellElement, cellIndex ) {
4182  if ( typeof cellIndex == 'number' ) {
4183    this.navCompanion.selectCell( cellIndex );
4184  }
4185};
4186
4187proto.deactivateAsNavFor = function() {
4188  this.removeNavSelectedElements();
4189};
4190
4191proto.destroyAsNavFor = function() {
4192  if ( !this.navCompanion ) {
4193    return;
4194  }
4195  this.navCompanion.off( 'select', this.onNavCompanionSelect );
4196  this.off( 'staticClick', this.onNavStaticClick );
4197  delete this.navCompanion;
4198};
4199
4200// -----  ----- //
4201
4202return Flickity;
4203
4204}));
4205
4206/*!
4207 * imagesLoaded v4.1.1
4208 * JavaScript is all like "You images are done yet or what?"
4209 * MIT License
4210 */
4211
4212( function( window, factory ) { 'use strict';
4213  // universal module definition
4214
4215  /*global define: false, module: false, require: false */
4216
4217  if ( typeof define == 'function' && define.amd ) {
4218    // AMD
4219    define( 'imagesloaded/imagesloaded',[
4220      'ev-emitter/ev-emitter'
4221    ], function( EvEmitter ) {
4222      return factory( window, EvEmitter );
4223    });
4224  } else if ( typeof module == 'object' && module.exports ) {
4225    // CommonJS
4226    module.exports = factory(
4227      window,
4228      require('ev-emitter')
4229    );
4230  } else {
4231    // browser global
4232    window.imagesLoaded = factory(
4233      window,
4234      window.EvEmitter
4235    );
4236  }
4237
4238})( window,
4239
4240// --------------------------  factory -------------------------- //
4241
4242function factory( window, EvEmitter ) {
4243
4244
4245
4246var $ = window.jQuery;
4247var console = window.console;
4248
4249// -------------------------- helpers -------------------------- //
4250
4251// extend objects
4252function extend( a, b ) {
4253  for ( var prop in b ) {
4254    a[ prop ] = b[ prop ];
4255  }
4256  return a;
4257}
4258
4259// turn element or nodeList into an array
4260function makeArray( obj ) {
4261  var ary = [];
4262  if ( Array.isArray( obj ) ) {
4263    // use object if already an array
4264    ary = obj;
4265  } else if ( typeof obj.length == 'number' ) {
4266    // convert nodeList to array
4267    for ( var i=0; i < obj.length; i++ ) {
4268      ary.push( obj[i] );
4269    }
4270  } else {
4271    // array of single index
4272    ary.push( obj );
4273  }
4274  return ary;
4275}
4276
4277// -------------------------- imagesLoaded -------------------------- //
4278
4279/**
4280 * @param {Array, Element, NodeList, String} elem
4281 * @param {Object or Function} options - if function, use as callback
4282 * @param {Function} onAlways - callback function
4283 */
4284function ImagesLoaded( elem, options, onAlways ) {
4285  // coerce ImagesLoaded() without new, to be new ImagesLoaded()
4286  if ( !( this instanceof ImagesLoaded ) ) {
4287    return new ImagesLoaded( elem, options, onAlways );
4288  }
4289  // use elem as selector string
4290  if ( typeof elem == 'string' ) {
4291    elem = document.querySelectorAll( elem );
4292  }
4293
4294  this.elements = makeArray( elem );
4295  this.options = extend( {}, this.options );
4296
4297  if ( typeof options == 'function' ) {
4298    onAlways = options;
4299  } else {
4300    extend( this.options, options );
4301  }
4302
4303  if ( onAlways ) {
4304    this.on( 'always', onAlways );
4305  }
4306
4307  this.getImages();
4308
4309  if ( $ ) {
4310    // add jQuery Deferred object
4311    this.jqDeferred = new $.Deferred();
4312  }
4313
4314  // HACK check async to allow time to bind listeners
4315  setTimeout( function() {
4316    this.check();
4317  }.bind( this ));
4318}
4319
4320ImagesLoaded.prototype = Object.create( EvEmitter.prototype );
4321
4322ImagesLoaded.prototype.options = {};
4323
4324ImagesLoaded.prototype.getImages = function() {
4325  this.images = [];
4326
4327  // filter & find items if we have an item selector
vendor: 4,347 bytes, lines 4328-4484
4328  this.elements.forEach( this.addElementImages, this );
4329};
4330
4331/**
4332 * @param {Node} element
4333 */
4334ImagesLoaded.prototype.addElementImages = function( elem ) {
4335  // filter siblings
4336  if ( elem.nodeName == 'IMG' ) {
4337    this.addImage( elem );
4338  }
4339  // get background image on element
4340  if ( this.options.background === true ) {
4341    this.addElementBackgroundImages( elem );
4342  }
4343
4344  // find children
4345  // no non-element nodes, #143
4346  var nodeType = elem.nodeType;
4347  if ( !nodeType || !elementNodeTypes[ nodeType ] ) {
4348    return;
4349  }
4350  var childImgs = elem.querySelectorAll('img');
4351  // concat childElems to filterFound array
4352  for ( var i=0; i < childImgs.length; i++ ) {
4353    var img = childImgs[i];
4354    this.addImage( img );
4355  }
4356
4357  // get child background images
4358  if ( typeof this.options.background == 'string' ) {
4359    var children = elem.querySelectorAll( this.options.background );
4360    for ( i=0; i < children.length; i++ ) {
4361      var child = children[i];
4362      this.addElementBackgroundImages( child );
4363    }
4364  }
4365};
4366
4367var elementNodeTypes = {
4368  1: true,
4369  9: true,
4370  11: true
4371};
4372
4373ImagesLoaded.prototype.addElementBackgroundImages = function( elem ) {
4374  var style = getComputedStyle( elem );
4375  if ( !style ) {
4376    // Firefox returns null if in a hidden iframe https://bugzil.la/548397
4377    return;
4378  }
4379  // get url inside url("...")
4380  var reURL = /url\((['"])?(.*?)\1\)/gi;
4381  var matches = reURL.exec( style.backgroundImage );
4382  while ( matches !== null ) {
4383    var url = matches && matches[2];
4384    if ( url ) {
4385      this.addBackground( url, elem );
4386    }
4387    matches = reURL.exec( style.backgroundImage );
4388  }
4389};
4390
4391/**
4392 * @param {Image} img
4393 */
4394ImagesLoaded.prototype.addImage = function( img ) {
4395  var loadingImage = new LoadingImage( img );
4396  this.images.push( loadingImage );
4397};
4398
4399ImagesLoaded.prototype.addBackground = function( url, elem ) {
4400  var background = new Background( url, elem );
4401  this.images.push( background );
4402};
4403
4404ImagesLoaded.prototype.check = function() {
4405  var _this = this;
4406  this.progressedCount = 0;
4407  this.hasAnyBroken = false;
4408  // complete if no images
4409  if ( !this.images.length ) {
4410    this.complete();
4411    return;
4412  }
4413
4414  function onProgress( image, elem, message ) {
4415    // HACK - Chrome triggers event before object properties have changed. #83
4416    setTimeout( function() {
4417      _this.progress( image, elem, message );
4418    });
4419  }
4420
4421  this.images.forEach( function( loadingImage ) {
4422    loadingImage.once( 'progress', onProgress );
4423    loadingImage.check();
4424  });
4425};
4426
4427ImagesLoaded.prototype.progress = function( image, elem, message ) {
4428  this.progressedCount++;
4429  this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded;
4430  // progress event
4431  this.emitEvent( 'progress', [ this, image, elem ] );
4432  if ( this.jqDeferred && this.jqDeferred.notify ) {
4433    this.jqDeferred.notify( this, image );
4434  }
4435  // check if completed
4436  if ( this.progressedCount == this.images.length ) {
4437    this.complete();
4438  }
4439
4440  if ( this.options.debug && console ) {
4441    console.log( 'progress: ' + message, image, elem );
4442  }
4443};
4444
4445ImagesLoaded.prototype.complete = function() {
4446  var eventName = this.hasAnyBroken ? 'fail' : 'done';
4447  this.isComplete = true;
4448  this.emitEvent( eventName, [ this ] );
4449  this.emitEvent( 'always', [ this ] );
4450  if ( this.jqDeferred ) {
4451    var jqMethod = this.hasAnyBroken ? 'reject' : 'resolve';
4452    this.jqDeferred[ jqMethod ]( this );
4453  }
4454};
4455
4456// --------------------------  -------------------------- //
4457
4458function LoadingImage( img ) {
4459  this.img = img;
4460}
4461
4462LoadingImage.prototype = Object.create( EvEmitter.prototype );
4463
4464LoadingImage.prototype.check = function() {
4465  // If complete is true and browser supports natural sizes,
4466  // try to check for image status manually.
4467  var isComplete = this.getIsImageComplete();
4468  if ( isComplete ) {
4469    // report based on naturalWidth
4470    this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' );
4471    return;
4472  }
4473
4474  // If none of the checks above matched, simulate loading on detached element.
4475  this.proxyImage = new Image();
4476  this.proxyImage.addEventListener( 'load', this );
4477  this.proxyImage.addEventListener( 'error', this );
4478  // bind to image as well for Firefox. #191
4479  this.img.addEventListener( 'load', this );
4480  this.img.addEventListener( 'error', this );
4481  this.proxyImage.src = this.img.src;
4482};
4483
4484LoadingImage.prototype.getIsImageComplete = function() {
4485  return this.img.complete && this.img.naturalWidth !== undefined;
4486};
4487
4488LoadingImage.prototype.confirm = function( isLoaded, message ) {
4489  this.isLoaded = isLoaded;
4490  this.emitEvent( 'progress', [ this, this.img, message ] );
4491};
4492
4493// ----- events ----- //
4494
4495// trigger specified handler for event type
4496LoadingImage.prototype.handleEvent = function( event ) {
4497  var method = 'on' + event.type;
4498  if ( this[ method ] ) {
4499    this[ method ]( event );
4500  }
4501};
4502
4503LoadingImage.prototype.onload = function() {
4504  this.confirm( true, 'onload' );
4505  this.unbindEvents();
4506};
4507
4508LoadingImage.prototype.onerror = function() {
4509  this.confirm( false, 'onerror' );
4510  this.unbindEvents();
4511};
4512
4513LoadingImage.prototype.unbindEvents = function() {
4514  this.proxyImage.removeEventListener( 'load', this );
4515  this.proxyImage.removeEventListener( 'error', this );
4516  this.img.removeEventListener( 'load', this );
4517  this.img.removeEventListener( 'error', this );
4518};
4519
4520// -------------------------- Background -------------------------- //
4521
4522function Background( url, element ) {
4523  this.url = url;
4524  this.element = element;
4525  this.img = new Image();
4526}
4527
4528// inherit LoadingImage prototype
4529Background.prototype = Object.create( LoadingImage.prototype );
4530
4531Background.prototype.check = function() {
4532  this.img.addEventListener( 'load', this );
4533  this.img.addEventListener( 'error', this );
4534  this.img.src = this.url;
4535  // check if image is already complete
4536  var isComplete = this.getIsImageComplete();
4537  if ( isComplete ) {
4538    this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' );
4539    this.unbindEvents();
4540  }
4541};
4542
4543Background.prototype.unbindEvents = function() {
4544  this.img.removeEventListener( 'load', this );
4545  this.img.removeEventListener( 'error', this );
4546};
4547
4548Background.prototype.confirm = function( isLoaded, message ) {
4549  this.isLoaded = isLoaded;
4550  this.emitEvent( 'progress', [ this, this.element, message ] );
4551};
4552
4553// -------------------------- jQuery -------------------------- //
4554
4555ImagesLoaded.makeJQueryPlugin = function( jQuery ) {
4556  jQuery = jQuery || window.jQuery;
4557  if ( !jQuery ) {
4558    return;
4559  }
4560  // set local variable
4561  $ = jQuery;
4562  // $().imagesLoaded()
4563  $.fn.imagesLoaded = function( options, callback ) {
4564    var instance = new ImagesLoaded( this, options, callback );
4565    return instance.jqDeferred.promise( $(this) );
4566  };
4567};
4568// try making plugin
4569ImagesLoaded.makeJQueryPlugin();
4570
4571// --------------------------  -------------------------- //
4572
4573return ImagesLoaded;
4574
4575});
4576
4577/*!
4578 * Flickity imagesLoaded v2.0.0
4579 * enables imagesLoaded option for Flickity
4580 */
4581
4582/*jshint browser: true, strict: true, undef: true, unused: true */
4583
4584( function( window, factory ) {
4585  // universal module definition
4586  /*jshint strict: false */ /*globals define, module, require */
4587  if ( typeof define == 'function' && define.amd ) {
4588    // AMD
4589    define( [
4590      'flickity/js/index',
4591      'imagesloaded/imagesloaded'
4592    ], function( Flickity, imagesLoaded ) {
4593      return factory( window, Flickity, imagesLoaded );
4594    });
4595  } else if ( typeof module == 'object' && module.exports ) {
4596    // CommonJS
4597    module.exports = factory(
4598      window,
4599      require('flickity'),
4600      require('imagesloaded')
4601    );
4602  } else {
4603    // browser global
4604    window.Flickity = factory(
4605      window,
4606      window.Flickity,
4607      window.imagesLoaded
4608    );
4609  }
4610
4611}( window, function factory( window, Flickity, imagesLoaded ) {
4612'use strict';
4613
4614Flickity.createMethods.push('_createImagesLoaded');
4615
4616var proto = Flickity.prototype;
4617
4618proto._createImagesLoaded = function() {
4619  this.on( 'activate', this.imagesLoaded );
4620};
4621
4622proto.imagesLoaded = function() {
4623  if ( !this.options.imagesLoaded ) {
4624    return;
4625  }
4626  var _this = this;
4627  function onImagesLoadedProgress( instance, image ) {
4628    var cell = _this.getParentCell( image.img );
4629    _this.cellSizeChange( cell && cell.element );
4630    if ( !_this.options.freeScroll ) {
4631      _this.positionSliderAtSelected();
4632    }
4633  }
4634  imagesLoaded( this.slider ).on( 'progress', onImagesLoadedProgress );
4635};
4636
4637return Flickity;
4638
4639}));
4640
4641
4642// ==================================================
4643// fancyBox v3.0.47
4644//
4645// Licensed GPLv3 for open source use
4646// or fancyBox Commercial License for commercial use
4647//
4648// http://fancyapps.com/fancybox/
4649// Copyright 2017 fancyApps
4650//
4651// ==================================================
4652;(function (window, document, $, undefined) {
4653    'use strict';
4654
4655    // If there's no jQuery, fancyBox can't work
4656    // =========================================
4657
4658    if ( !$ ) {
4659        return undefined;
4660    }
4661
4662    // Private default settings
4663    // ========================
4664
4665    var defaults = {
4666
4667        // Animation duration in ms
4668        speed : 330,
4669
4670        // Enable infinite gallery navigation
4671        loop : true,
4672
4673        // Should zoom animation change opacity, too
4674        // If opacity is 'auto', then fade-out if image and thumbnail have different aspect ratios
4675        opacity : 'auto',
4676
4677        // Space around image, ignored if zoomed-in or viewport smaller than 800px
4678        margin : [44, 0],
4679
4680        // Horizontal space between slides
4681        gutter : 30,
4682
4683        // Should display toolbars
4684        infobar : true,
4685        buttons : true,
4686
4687        // What buttons should appear in the toolbar
4688        slideShow  : true,
4689        fullScreen : true,
4690        thumbs     : true,
4691        closeBtn   : true,
4692
4693        // Should apply small close button at top right corner of the content
4694        // If 'auto' - will be set for content having type 'html', 'inline' or 'ajax'
4695        smallBtn : 'auto',
4696
4697        image : {
4698
4699            // Wait for images to load before displaying
4700            // Requires predefined image dimensions
4701            // If 'auto' - will zoom in thumbnail if 'width' and 'height' attributes are found
4702            preload : "auto",
4703
4704            // Protect an image from downloading by right-click
4705            protect : false
4706
4707        },
4708
4709        ajax : {
4710
4711            // Object containing settings for ajax request
4712            settings : {
4713
4714                // This helps to indicate that request comes from the modal
4715                // Feel free to change naming
4716                data : {
4717                    fancybox : true
4718                }
4719            }
4720
4721        },
4722
4723        iframe : {
4724
4725            // Iframe template
4726            tpl : '<iframe id="fancybox-frame{rnd}" name="fancybox-frame{rnd}" class="fancybox-iframe" frameborder="0" vspace="0" hspace="0" webkitAllowFullScreen mozallowfullscreen allowFullScreen allowtransparency="true" src=""></iframe>',
4727
4728            // Preload iframe before displaying it
4729            // This allows to calculate iframe content width and height
4730            // (note: Due to "Same Origin Policy", you can't get cross domain data).
4731            preload : true,
4732
4733            // Scrolling attribute for iframe tag
4734            scrolling : 'no',
4735
4736            // Custom CSS styling for iframe wrapping element
4737            css : {}
4738
4739        },
4740
4741        // Custom CSS class for layout
4742        baseClass : '',
4743
4744        // Custom CSS class for slide element
4745        slideClass : '',
4746
4747        // Base template for layout
4748        baseTpl	: '<div class="fancybox-container" role="dialog" tabindex="-1">' +
4749                '<div class="fancybox-bg"></div>' +
4750                '<div class="fancybox-controls">' +
4751                    '<div class="fancybox-infobar">' +
4752                        '<button data-fancybox-previous class="fancybox-button fancybox-button--left" title="Previous"></button>' +
4753                        '<div class="fancybox-infobar__body">' +
4754                            '<span class="js-fancybox-index"></span>&nbsp;/&nbsp;<span class="js-fancybox-count"></span>' +
4755                        '</div>' +
4756                        '<button data-fancybox-next class="fancybox-button fancybox-button--right" title="Next"></button>' +
4757                    '</div>' +
4758                    '<div class="fancybox-buttons">' +
4759                        '<button data-fancybox-close class="fancybox-button fancybox-button--close" title="Close (Esc)"></button>' +
4760                    '</div>' +
4761                '</div>' +
4762                '<div class="fancybox-slider-wrap">' +
4763                    '<div class="fancybox-slider"></div>' +
4764                '</div>' +
4765                '<div class="fancybox-caption-wrap"><div class="fancybox-caption"></div></div>' +
4766            '</div>',
4767
4768        // Loading indicator template
4769        spinnerTpl : '<div class="fancybox-loading"></div>',
4770
4771        // Error message template
4772        errorTpl : '<div class="fancybox-error"><p>The requested content cannot be loaded. <br /> Please try again later.<p></div>',
4773
4774        // This will be appended to html content, if "smallBtn" option is not set to false
4775        closeTpl : '<button data-fancybox-close class="fancybox-close-small"></button>',
4776
4777        // Container is injected into this element
4778        parentEl : 'body',
4779
4780        // Enable gestures (tap, zoom, pan and pinch)
4781        touch : true,
4782
4783        // Enable keyboard navigation
4784        keyboard : true,
4785
4786        // Try to focus on first focusable element after opening
4787        focus : true,
4788
4789        // Close when clicked outside of the content
4790        closeClickOutside : true,
4791
4792        // Callbacks
4793        beforeLoad	 : $.noop,
4794        afterLoad    : $.noop,
4795        beforeMove 	 : $.noop,
4796        afterMove    : $.noop,
4797        onComplete	 : $.noop,
4798
4799        onInit       : $.noop,
4800        beforeClose	 : $.noop,
4801        afterClose	 : $.noop,
4802        onActivate   : $.noop,
4803        onDeactivate : $.noop
4804
4805    };
4806
4807    var $W = $(window);
4808    var $D = $(document);
4809
4810    var called = 0;
4811
4812    // Check if an object is a jQuery object and not a native JavaScript object
4813    // ========================================================================
4814
4815    var isQuery = function (obj) {
4816        return obj && obj.hasOwnProperty && obj instanceof $;
4817    };
4818
4819    // Handle multiple browsers for requestAnimationFrame()
4820    // ====================================================
4821
4822    var requestAFrame = (function() {
4823        return  window.requestAnimationFrame ||
4824                window.webkitRequestAnimationFrame ||
4825                window.mozRequestAnimationFrame ||
4826                function( callback ) {
4827                    window.setTimeout(callback, 1000 / 60); };
4828                })();
4829
4830
4831    // Check if element is inside the viewport by at least 1 pixel
4832    // ===========================================================
4833
4834    var isElementInViewport = function( el ) {
4835        var rect;
4836
4837        if ( typeof $ === "function" && el instanceof $ ) {
4838            el = el[0];
4839        }
4840
4841        rect = el.getBoundingClientRect();
4842
4843        return rect.bottom > 0 && rect.right > 0 &&
4844                rect.left < (window.innerWidth || document.documentElement.clientWidth)  &&
4845                rect.top < (window.innerHeight || document.documentElement.clientHeight);
4846    };
4847
4848
4849    // Class definition
4850    // ================
4851
4852    var FancyBox = function( content, opts, index ) {
4853        var self = this;
4854
4855        self.opts  = $.extend( true, { index : index }, defaults, opts || {} );
4856        self.id    = self.opts.id || ++called;
4857        self.group = [];
4858
4859        self.currIndex = parseInt( self.opts.index, 10 ) || 0;
4860        self.prevIndex = null;
4861
4862        self.prevPos = null;
4863        self.currPos = 0;
4864
4865        self.firstRun = null;
4866
4867        // Create group elements from original item collection
4868        self.createGroup( content );
4869
4870        if ( !self.group.length ) {
4871            return;
4872        }
4873
4874        // Save last active element and current scroll position
4875        self.$lastFocus = $(document.activeElement).blur();
4876
4877        // Collection of gallery objects
4878        self.slides = {};
4879
4880        self.init( content );
4881
4882    };
4883
4884    $.extend(FancyBox.prototype, {
4885
4886        // Create DOM structure
4887        // ====================
4888
4889        init : function() {
4890            var self = this;
4891
4892            var galleryHasHtml = false;
4893
4894            var testWidth;
4895            var $container;
4896
4897            self.scrollTop  = $D.scrollTop();
4898            self.scrollLeft = $D.scrollLeft();
4899
4900            if ( !$.fancybox.getInstance() ) {
4901                testWidth = $( 'body' ).width();
4902
4903                $( 'html' ).addClass( 'fancybox-enabled' );
4904
4905                if ( $.fancybox.isTouch ) {
4906
4907                    // Ugly workaround for iOS page shifting issue (when inputs get focus)
4908                    // Do not apply for images, otherwise top/bottom bars will appear
4909                    $.each( self.group, function( key, item ) {
4910                        if ( item.type !== 'image' && item.type !== 'iframe' ) {
4911                            galleryHasHtml = true;
4912                            return false;
4913                        }
4914                    });
4915
4916                    if ( galleryHasHtml ) {
4917                        $('body').css({
4918                            position : 'fixed',
4919                            width    : testWidth,
4920                            top      : self.scrollTop * -1
4921                        });
4922                    }
4923
4924                } else {
4925
4926                    // Compare page width after adding "overflow:hidden"
4927                    testWidth = $( 'body' ).width() - testWidth;
4928
4929                    // Width has changed - compensate missing scrollbars
4930                    if ( testWidth > 1 ) {
4931                        $( '<style id="fancybox-noscroll" type="text/css">' ).html( '.compensate-for-scrollbar, .fancybox-enabled body { margin-right: ' + testWidth + 'px; }' ).appendTo( 'head' );
4932                    }
4933
4934                }
4935            }
4936
4937            $container = $( self.opts.baseTpl )
4938                .attr('id', 'fancybox-container-' + self.id)
4939                .data( 'FancyBox', self )
4940                .addClass( self.opts.baseClass )
4941                .hide()
4942                .prependTo( self.opts.parentEl );
4943
4944            // Create object holding references to jQuery wrapped nodes
4945            self.$refs = {
4946                container   : $container,
4947                bg          : $container.find('.fancybox-bg'),
4948                controls    : $container.find('.fancybox-controls'),
4949                buttons     : $container.find('.fancybox-buttons'),
4950                slider_wrap : $container.find('.fancybox-slider-wrap'),
4951                slider      : $container.find('.fancybox-slider'),
4952                caption     : $container.find('.fancybox-caption')
4953            };
4954
4955            self.trigger( 'onInit' );
4956
4957            // Bring to front and enable events
4958            self.activate();
4959
4960            // Try to avoid running multiple times
4961            if ( self.current ) {
4962                return;
4963            }
4964
4965            self.jumpTo( self.currIndex );
4966
4967        },
4968
4969
4970        // Create array of gally item objects
4971        // Check if each object has valid type and content
4972        // ===============================================
4973
4974        createGroup : function ( content ) {
4975            var self  = this;
4976            var items = $.makeArray( content );
4977
4978            $.each(items, function( i, item ) {
4979                var obj  = {},
4980                    opts = {},
4981                    data = [],
4982                    $item,
4983                    type,
4984                    src,
4985                    srcParts;
4986
4987                // Step 1 - Make sure we have an object
4988
4989                if ( $.isPlainObject( item ) ) {
4990
4991                    obj  = item;
4992                    opts = item.opts || {};
4993
4994                } else if ( $.type( item ) === 'object' && $( item ).length ) {
4995
4996                    $item = $( item );
4997                    data  = $item.data();
4998
4999                    opts = 'options' in data ? data.options : {};
5000
5001                    opts = $.type( opts ) === 'object' ? opts : {};
5002
5003                    obj.type = 'type' in data ? data.type : opts.type;
5004                    obj.src  = 'src'  in data ? data.src  : ( opts.src || $item.attr( 'href' ) );
5005
5006                    opts.width   = 'width'   in data ? data.width   : opts.width;
5007                    opts.height  = 'height'  in data ? data.height  : opts.height;
5008                    opts.thumb   = 'thumb'   in data ? data.thumb   : opts.thumb;
5009
5010                    opts.selector = 'selector'  in data ? data.selector  : opts.selector;
5011
5012                    if ( 'srcset' in data ) {
5013                        opts.image = { srcset : data.srcset };
5014                    }
5015
5016                    opts.$orig = $item;
5017
5018                } else {
5019
5020                    obj = {
5021                        type    : 'html',
5022                        content : item + ''
5023                    };
5024
5025                }
5026
5027                obj.opts = $.extend( true, {}, self.opts, opts );
5028
5029                // Step 2 - Make sure we have supported content type
5030
5031                type = obj.type;
5032                src  = obj.src || '';
5033
5034                if ( !type ) {
5035
5036                    if ( obj.content ) {
5037                        type = 'html';
5038
5039                    } else if ( src.match(/(^data:image\/[a-z0-9+\/=]*,)|(\.(jp(e|g|eg)|gif|png|bmp|webp|svg|ico)((\?|#).*)?$)/i) ) {
5040                        type = 'image';
5041
5042                    } else if ( src.match(/\.(pdf)((\?|#).*)?$/i) ) {
5043                        type = 'pdf';
5044
5045                    } else if ( src.charAt(0) === '#' ) {
5046                        type = 'inline';
5047
5048                    }
5049
5050                    obj.type = type;
5051
5052                }
5053
5054                // Step 3 - Some adjustments
5055
5056                obj.index = self.group.length;
5057
5058                // Check if $orig and $thumb objects exist
5059                if ( obj.opts.$orig && !obj.opts.$orig.length ) {
5060                    delete obj.opts.$orig;
5061                }
5062
5063                if ( !obj.opts.$thumb && obj.opts.$orig ) {
5064                    obj.opts.$thumb = obj.opts.$orig.find( 'img:first' );
5065                }
5066
5067                if ( obj.opts.$thumb && !obj.opts.$thumb.length ) {
5068                    delete obj.opts.$thumb;
5069                }
5070
5071                // Caption is a "special" option, it can be passed as a method
5072                if ( $.type( obj.opts.caption ) === 'function' ) {
5073                    obj.opts.caption = obj.opts.caption.apply( item, [ self, obj ] );
5074
5075                } else if ( 'caption' in data ) {
5076                    obj.opts.caption = data.caption;
5077
5078                } else if ( opts.$orig ) {
5079                    obj.opts.caption = $item.attr( 'title' );
5080                }
5081
5082                // Make sure we have caption as a string
5083                obj.opts.caption = obj.opts.caption === undefined ? '' : obj.opts.caption + '';
5084
5085                // Check if url contains selector used to filter the content
5086                // Example: "ajax.html #something"
5087                if ( type === 'ajax' ) {
5088                    srcParts = src.split(/\s+/, 2);
5089
5090                    if ( srcParts.length > 1 ) {
5091                        obj.src = srcParts.shift();
5092
5093                        obj.opts.selector = srcParts.shift();
5094                    }
5095                }
5096
5097                if ( obj.opts.smallBtn == 'auto' ) {
5098
5099                    if ( $.inArray( type, ['html', 'inline', 'ajax'] ) > -1 ) {
5100                        obj.opts.buttons  = false;
5101                        obj.opts.smallBtn = true;
5102
5103                    } else {
5104                        obj.opts.smallBtn = false;
5105                    }
5106
5107                }
5108
5109                if ( type === 'pdf' ) {
5110
5111                    obj.type = 'iframe';
5112
5113                    obj.opts.closeBtn = true;
5114                    obj.opts.smallBtn = false;
5115
5116                    obj.opts.iframe.preload = false;
5117
5118                }
5119
5120                if ( obj.opts.modal ) {
5121
5122                    $.extend(true, obj.opts, {
5123                        infobar		: 0,
5124                        buttons		: 0,
5125                        keyboard	: 0,
5126                        slideShow	: 0,
5127                        fullScreen	: 0,
5128                        closeClickOutside	: 0
5129                    });
5130
5131                }
5132
5133                self.group.push( obj );
5134
5135            });
5136
5137        },
5138
5139
5140        // Attach an event handler functions for:
5141        //   - navigation elements
5142        //   - browser scrolling, resizing;
5143        //   - focusing
5144        //   - keyboard
5145        // =================
5146
5147        addEvents : function() {
5148            var self = this;
5149
5150            self.removeEvents();
5151
5152            // Make navigation elements clickable
5153
5154            self.$refs.container.on('click.fb-close', '[data-fancybox-close]', function(e) {
5155                e.stopPropagation();
5156                e.preventDefault();
5157
5158                self.close( e );
5159
5160            }).on('click.fb-previous', '[data-fancybox-previous]', function(e) {
5161                e.stopPropagation();
5162                e.preventDefault();
5163
5164                self.previous();
5165
5166            }).on('click.fb-next', '[data-fancybox-next]', function(e) {
5167                e.stopPropagation();
5168                e.preventDefault();
5169
5170                self.next();
5171            });
5172
5173
5174            // Handle page scrolling and browser resizing
5175
5176            $( window ).on('orientationchange.fb resize.fb', function(e) {
5177                requestAFrame(function() {
5178
5179                    if ( e && e.originalEvent && e.originalEvent.type === "resize" ) {
5180                        self.update();
5181
5182                    } else {
5183                        self.$refs.slider_wrap.hide();
5184
5185                        requestAFrame(function () {
5186                            self.$refs.slider_wrap.show();
5187
5188                            self.update();
5189                        });
5190
5191                    }
5192
5193                });
5194            });
5195
5196
5197            // Trap focus
5198
5199            $D.on('focusin.fb', function(e) {
5200                var instance = $.fancybox ? $.fancybox.getInstance() : null;
5201
5202                if ( instance && !$( e.target ).hasClass( 'fancybox-container' ) && !$.contains( instance.$refs.container[0], e.target ) ) {
5203                    e.stopPropagation();
5204
5205                    instance.focus();
5206
5207                    // Sometimes page gets scrolled, set it back
5208                    $W.scrollTop( self.scrollTop ).scrollLeft( self.scrollLeft );
5209                }
5210
5211            });
5212
5213            // Enable keyboard navigation
5214
5215            $D.on('keydown.fb', function (e) {
5216                var current = self.current,
5217                    keycode = e.keyCode || e.which;
5218
5219                if ( !current || !current.opts.keyboard ) {
5220                    return;
5221                }
5222
5223                if ( $(e.target).is('input') || $(e.target).is('textarea') ) {
5224                    return;
5225                }
5226
5227                // Backspace and Esc keys
5228                if ( keycode === 8 || keycode === 27 ) {
5229                    e.preventDefault();
5230
5231                    self.close( e );
5232
5233                    return;
5234                }
5235
5236                switch ( keycode ) {
5237
5238                    case 37: // Left arrow
5239                    case 38: // Up arrow
5240
5241                        e.preventDefault();
5242
5243                        self.previous();
5244
5245                    break;
5246
5247                    case 39: // Right arrow
5248                    case 40: // Down arrow
5249
5250                        e.preventDefault();
5251
5252                        self.next();
5253
5254                    break;
5255
5256                    case 80: // "P"
5257					case 32: // Spacebar
5258
5259						e.preventDefault();
5260
5261						if ( self.SlideShow ) {
5262							e.preventDefault();
5263
5264							self.SlideShow.toggle();
5265						}
5266
5267					break;
5268
5269                    case 70: // "F"
5270
5271						if ( self.FullScreen ) {
5272							e.preventDefault();
5273
5274							self.FullScreen.toggle();
5275						}
5276
5277					break;
5278
5279                    case 71: // "G"
5280
5281						if ( self.Thumbs ) {
5282							e.preventDefault();
5283
5284							self.Thumbs.toggle();
5285						}
5286
5287					break;
5288                }
5289            });
5290
5291
5292        },
5293
5294
5295        // Remove events added by the core
5296        // ===============================
5297
5298        removeEvents : function () {
5299
5300            $W.off( 'scroll.fb resize.fb orientationchange.fb' );
5301            $D.off( 'keydown.fb focusin.fb click.fb-close' );
5302
5303            this.$refs.container.off('click.fb-close click.fb-previous click.fb-next');
5304        },
5305
5306
5307        // Slide to left
5308        // ==================
5309
5310        previous : function( duration ) {
5311            this.jumpTo( this.currIndex - 1, duration );
5312        },
5313
5314
5315        // Slide to right
5316        // ===================
5317
5318        next : function( duration ) {
5319            this.jumpTo( this.currIndex + 1, duration );
5320        },
5321
5322
5323        // Display current gallery item, move slider to current position
5324        // =============================================================
5325
5326        jumpTo : function ( to, duration ) {
5327            var self = this,
5328                firstRun,
5329                index,
5330                pos,
5331                loop;
5332
5333            firstRun = self.firstRun = ( self.firstRun === null );
5334
5335            index = pos = to = parseInt( to, 10 );
5336            loop  = self.current ? self.current.opts.loop : false;
5337
5338            if ( self.isAnimating || ( index == self.currIndex && !firstRun ) ) {
5339                return;
5340            }
5341
5342            if ( self.group.length > 1 && loop ) {
5343
5344                index = index % self.group.length;
5345                index = index < 0 ? self.group.length + index : index;
5346
5347                // Calculate closest position of upcoming item from the current one
5348                if ( self.group.length == 2 ) {
5349                    pos = to - self.currIndex + self.currPos;
5350
5351                } else {
5352                    pos = index - self.currIndex + self.currPos;
5353
5354                    if ( Math.abs( self.currPos - ( pos + self.group.length ) ) < Math.abs( self.currPos - pos ) ) {
5355                        pos = pos + self.group.length;
5356
5357                    } else if ( Math.abs( self.currPos - ( pos - self.group.length ) ) < Math.abs( self.currPos - pos ) ) {
5358                        pos = pos - self.group.length;
5359
5360                    }
5361                }
5362
5363            } else if ( !self.group[ index ] ) {
5364                self.update( false, false, duration );
5365
5366                return;
5367            }
5368
5369            if ( self.current ) {
5370                self.current.$slide.removeClass('fancybox-slide--current fancybox-slide--complete');
5371
5372                self.updateSlide( self.current, true );
5373            }
5374
5375            self.prevIndex = self.currIndex;
5376            self.prevPos   = self.currPos;
5377
5378            self.currIndex = index;
5379            self.currPos   = pos;
5380
5381            // Create slides
5382
5383            self.current = self.createSlide( pos );
5384
5385            if ( self.group.length > 1 ) {
5386
5387                if ( self.opts.loop || pos - 1 >= 0 ) {
5388                    self.createSlide( pos - 1 );
5389                }
5390
5391                if ( self.opts.loop || pos + 1 < self.group.length ) {
5392                    self.createSlide( pos + 1 );
5393                }
5394            }
5395
5396            self.current.isMoved    = false;
5397            self.current.isComplete = false;
5398
5399            duration = parseInt( duration === undefined ? self.current.opts.speed * 1.5 : duration, 10 );
5400
5401            // Move slider to the next position
5402            // Note: the content might still be loading
5403            self.trigger( 'beforeMove' );
5404
5405            self.updateControls();
5406
5407            if ( firstRun ) {
5408                self.current.$slide.addClass('fancybox-slide--current');
5409
5410                self.$refs.container.show();
5411
5412                requestAFrame(function() {
5413                    self.$refs.bg.css('transition-duration', self.current.opts.speed + 'ms');
5414
5415                    self.$refs.container.addClass( 'fancybox-container--ready' );
5416                });
5417            }
5418
5419            // Set position immediately on first opening
5420            self.update( true, false, firstRun ? 0 : duration, function() {
5421                self.afterMove();
5422            });
5423
5424            self.loadSlide( self.current );
5425
5426            if ( !( firstRun && self.current.$ghost ) ) {
5427                self.preload();
5428            }
5429
5430        },
5431
5432
5433        // Create new "slide" element
5434        // These are gallery items  that are actually added to DOM
5435        // =======================================================
5436
5437        createSlide : function( pos ) {
5438
5439            var self = this;
5440            var $slide;
5441            var index;
5442            var found;
5443
5444            index = pos % self.group.length;
5445            index = index < 0 ? self.group.length + index : index;
5446
5447            if ( !self.slides[ pos ] && self.group[ index ] ) {
5448
5449                // If we are looping and slide with that index already exists, then reuse it
5450                if ( self.opts.loop && self.group.length > 2 ) {
5451                    for (var key in self.slides) {
5452                        if ( self.slides[ key ].index === index ) {
5453                            found = self.slides[ key ];
5454                            found.pos = pos;
5455
5456                            self.slides[ pos ] = found;
5457
5458                            delete self.slides[ key ];
5459
5460                            self.updateSlide( found );
5461
5462                            return found;
5463                        }
5464                    }
5465                }
5466
5467                $slide = $('<div class="fancybox-slide"></div>').appendTo( self.$refs.slider );
5468
5469                self.slides[ pos ] = $.extend( true, {}, self.group[ index ], {
5470                    pos      : pos,
5471                    $slide   : $slide,
5472                    isMoved  : false,
5473                    isLoaded : false
5474                });
5475
5476            }
5477
5478            return self.slides[ pos ];
5479
5480        },
5481
5482        zoomInOut : function( type, duration, callback ) {
5483
5484            var self     = this;
5485            var current  = self.current;
5486            var $what    = current.$placeholder;
5487            var opacity  = current.opts.opacity;
5488            var $thumb   = current.opts.$thumb;
5489            var thumbPos = $thumb ? $thumb.offset() : 0;
5490            var slidePos = current.$slide.offset();
5491            var props;
5492            var start;
5493            var end;
5494
5495            if ( !$what || !current.isMoved || !thumbPos || !isElementInViewport( $thumb ) ) {
5496                return false;
5497            }
5498
5499            if ( type === 'In' && !self.firstRun ) {
5500                return false;
5501            }
5502
5503            $.fancybox.stop( $what );
5504
5505            self.isAnimating = true;
5506
5507            props = {
5508                top    : thumbPos.top  - slidePos.top  + parseFloat( $thumb.css( "border-top-width" ) || 0 ),
5509                left   : thumbPos.left - slidePos.left + parseFloat( $thumb.css( "border-left-width" ) || 0 ),
5510                width  : $thumb.width(),
5511                height : $thumb.height(),
5512                scaleX : 1,
5513                scaleY : 1
5514            };
5515
5516            // Check if we need to animate opacity
5517            if ( opacity == 'auto' ) {
5518                opacity = Math.abs( current.width / current.height - props.width / props.height ) > 0.1;
5519            }
5520
5521            if ( type === 'In' ) {
5522                start = props;
5523                end   = self.getFitPos( current );
5524
5525                end.scaleX = end.width  / start.width;
5526                end.scaleY = end.height / start.height;
5527
5528                if ( opacity ) {
5529                    start.opacity = 0.1;
5530                    end.opacity   = 1;
5531                }
5532
5533            } else {
5534
5535                start = $.fancybox.getTranslate( $what );
5536                end   = props;
5537
5538                // Switch to thumbnail image to improve animation performance
5539                if ( current.$ghost ) {
5540                    current.$ghost.show();
5541
5542                    if ( current.$image ) {
5543                        current.$image.remove();
5544                    }
5545                }
5546
5547                start.scaleX = start.width  / end.width;
5548                start.scaleY = start.height / end.height;
5549
5550                start.width  = end.width;
5551                start.height = end.height;
5552
5553                if ( opacity ) {
5554                    end.opacity = 0;
5555                }
5556
5557            }
5558
5559            self.updateCursor( end.width, end.height );
5560
5561            // There is no need to animate width/height properties
5562            delete end.width;
5563            delete end.height;
5564
5565            $.fancybox.setTranslate( $what, start );
5566
5567            $what.show();
5568
5569            self.trigger( 'beforeZoom' + type );
5570
5571            $what.css( 'transition', 'all ' + duration + 'ms' );
5572
5573            $.fancybox.setTranslate( $what, end );
5574
5575            setTimeout(function() {
5576                var reset;
5577
5578                $what.css( 'transition', 'none' );
5579
5580                reset = $.fancybox.getTranslate( $what );
5581
5582                reset.scaleX = 1;
5583                reset.scaleY = 1;
5584
5585                // Reset scalex/scaleY values; this helps for perfomance
5586                $.fancybox.setTranslate( $what, reset );
5587
5588                self.trigger( 'afterZoom' + type );
5589
5590                callback.apply( self );
5591
5592                self.isAnimating = false;
5593
5594            }, duration);
5595
5596
5597            return true;
5598
5599        },
5600
5601        // Check if image dimensions exceed parent element
5602        // ===============================================
5603
5604        canPan : function() {
5605
5606            var self = this;
5607
5608            var current = self.current;
5609            var $what   = current.$placeholder;
5610
5611            var rez = false;
5612
5613            if ( $what ) {
5614                rez = self.getFitPos( current );
5615                rez = Math.abs( $what.width() - rez.width ) > 1  || Math.abs( $what.height() - rez.height ) > 1;
5616
5617            }
5618
5619            return rez;
5620
5621        },
5622
5623
5624        // Check if current image dimensions are smaller than actual
5625        // =========================================================
5626
5627        isScaledDown : function() {
5628
5629            var self = this;
5630
5631            var current = self.current;
5632            var $what   = current.$placeholder;
5633
5634            var rez = false;
5635
5636            if ( $what ) {
5637                rez = $.fancybox.getTranslate( $what );
5638                rez = rez.width < current.width || rez.height < current.height;
5639            }
5640
5641            return rez;
5642
5643        },
5644
5645
5646        // Scale image to the actual size of the image
5647        // ===========================================
5648
5649        scaleToActual : function( x, y, duration ) {
5650
5651            var self = this;
5652
5653            var current = self.current;
5654            var $what   = current.$placeholder;
5655
5656            var imgPos, posX, posY, scaleX, scaleY;
5657
5658            var canvasWidth  = parseInt( current.$slide.width(), 10 );
5659            var canvasHeight = parseInt( current.$slide.height(), 10 );
5660
5661            var newImgWidth  = current.width;
5662            var newImgHeight = current.height;
5663
5664            if ( !$what ) {
5665                return;
5666            }
5667
5668            self.isAnimating = true;
5669
5670            x = x === undefined ? canvasWidth  * 0.5  : x;
5671            y = y === undefined ? canvasHeight * 0.5  : y;
5672
5673            imgPos = $.fancybox.getTranslate( $what );
5674
5675            scaleX  = newImgWidth  / imgPos.width;
5676            scaleY  = newImgHeight / imgPos.height;
5677
5678            // Get center position for original image
5679            posX = ( canvasWidth * 0.5  - newImgWidth * 0.5 );
5680            posY = ( canvasHeight * 0.5 - newImgHeight * 0.5 );
5681
5682            // Make sure image does not move away from edges
5683
5684            if ( newImgWidth > canvasWidth ) {
5685                posX = imgPos.left * scaleX - ( ( x * scaleX ) - x );
5686
5687                if ( posX > 0 ) {
5688                    posX = 0;
5689                }
5690
5691                if ( posX <  canvasWidth - newImgWidth ) {
5692                    posX = canvasWidth - newImgWidth;
5693                }
5694            }
5695
5696            if ( newImgHeight > canvasHeight) {
5697                posY = imgPos.top  * scaleY - ( ( y * scaleY ) - y );
5698
5699                if ( posY > 0 ) {
5700                    posY = 0;
5701                }
5702
5703                if ( posY <  canvasHeight - newImgHeight ) {
5704                    posY = canvasHeight - newImgHeight;
5705                }
5706            }
5707
5708            self.updateCursor( newImgWidth, newImgHeight );
5709
5710            $.fancybox.animate( $what, null, {
5711                top    : posY,
5712                left   : posX,
5713                scaleX : scaleX,
5714                scaleY : scaleY
5715            }, duration || current.opts.speed, function() {
5716                self.isAnimating = false;
5717            });
5718
5719        },
5720
5721
5722        // Scale image to fit inside parent element
5723        // ========================================
5724
5725        scaleToFit : function( duration ) {
5726
5727            var self = this;
5728
5729            var current = self.current;
5730            var $what   = current.$placeholder;
5731            var end;
5732
5733            if ( !$what ) {
5734                return;
5735            }
5736
5737            self.isAnimating = true;
5738
5739            end = self.getFitPos( current );
5740
5741            self.updateCursor( end.width, end.height );
5742
5743            $.fancybox.animate( $what, null, {
5744                top    : end.top,
5745                left   : end.left,
5746                scaleX : end.width  / $what.width(),
5747                scaleY : end.height / $what.height()
5748            }, duration || current.opts.speed, function() {
5749                self.isAnimating = false;
5750            });
5751
5752        },
5753
5754        // Calculate image size to fit inside viewport
5755        // ===========================================
5756
5757        getFitPos : function( slide ) {
5758            var $what = slide.$placeholder || slide.$content;
5759
5760            var imgWidth  = slide.width;
5761            var imgHeight = slide.height;
5762
5763            var margin = slide.opts.margin;
5764
5765            var canvasWidth, canvasHeight, minRatio, top, left, width, height;
5766
5767            if ( !$what || !$what.length || ( !imgWidth && !imgHeight) ) {
5768                return false;
5769            }
5770
5771            // Convert "margin to CSS style: [ top, right, bottom, left ]
5772            if ( $.type( margin ) === "number" ) {
5773                margin = [ margin, margin ];
5774            }
5775
5776            if ( margin.length == 2 ) {
5777                margin = [ margin[0], margin[1], margin[0], margin[1] ];
5778            }
5779
5780            if ( $W.width() < 800 ) {
5781                margin = [0, 0, 0, 0];
5782            }
5783
5784            canvasWidth  = parseInt( slide.$slide.width(), 10 )  - ( margin[ 1 ] + margin[ 3 ] );
5785            canvasHeight = parseInt( slide.$slide.height(), 10 ) - ( margin[ 0 ] + margin[ 2 ] );
5786
5787            minRatio = Math.min(1, canvasWidth / imgWidth, canvasHeight / imgHeight );
5788
5789            // Use floor rounding to make sure it really fits
5790
5791            width  = Math.floor( minRatio * imgWidth );
5792            height = Math.floor( minRatio * imgHeight );
5793
5794            top  = Math.floor( ( canvasHeight - height ) * 0.5 ) + margin[ 0 ];
5795            left = Math.floor( ( canvasWidth  - width )  * 0.5 ) + margin[ 3 ];
5796
5797            return {
5798                top    : top,
5799                left   : left,
5800                width  : width,
5801                height : height
5802            };
5803
5804        },
5805
5806        // Move slider to current position
5807        // Update all slides (and their content)
5808        // =====================================
5809
5810        update : function( andSlides, andContent, duration, callback ) {
5811
5812            var self = this;
5813            var leftValue;
5814
5815            if ( self.isAnimating === true || !self.current ) {
5816                return;
5817            }
5818
5819            leftValue = ( self.current.pos * Math.floor( self.current.$slide.width() ) * -1 ) - ( self.current.pos * self.current.opts.gutter );
5820            duration  = parseInt( duration, 10 ) || 0;
5821
5822            $.fancybox.stop( self.$refs.slider );
5823
5824            if ( andSlides === false ) {
5825                self.updateSlide( self.current, andContent );
5826
5827            } else {
5828
5829                $.each( self.slides, function( key, slide ) {
5830                    self.updateSlide( slide, andContent );
5831                });
5832
5833            }
5834
5835            if ( duration ) {
5836
5837                $.fancybox.animate( self.$refs.slider, null, {
5838                    top  : 0,
5839                    left : leftValue
5840                }, duration, function() {
5841                    self.current.isMoved = true;
5842
5843                    if ( $.type( callback ) === 'function' ) {
5844                        callback.apply( self );
5845                    }
5846
5847                });
5848
5849            } else {
5850
5851                $.fancybox.setTranslate( self.$refs.slider, { top : 0, left : leftValue } );
5852
5853                self.current.isMoved = true;
5854
5855                if ( $.type( callback ) === 'function' ) {
5856                    callback.apply( self );
5857                }
5858
5859            }
5860
5861        },
5862
5863
5864        // Update slide position and scale content to fit
5865        // ==============================================
5866
5867        updateSlide : function( slide, andContent ) {
5868
5869            var self  = this;
5870            var $what = slide.$placeholder;
5871            var leftPos;
5872
5873            slide = slide || self.current;
5874
5875            if ( !slide || self.isClosing ) {
5876                return;
5877            }
5878
5879            leftPos = ( slide.pos * Math.floor( slide.$slide.width() )  ) + ( slide.pos * slide.opts.gutter);
5880
5881            if ( leftPos !== slide.leftPos ) {
5882                $.fancybox.setTranslate( slide.$slide, { top: 0, left : leftPos } );
5883
5884                slide.leftPos = leftPos;
5885            }
5886
5887            if ( andContent !== false && $what ) {
5888                $.fancybox.setTranslate( $what, self.getFitPos( slide ) );
5889
5890                if ( slide.pos === self.currPos ) {
5891                    self.updateCursor();
5892                }
5893            }
5894
5895            slide.$slide.trigger( 'refresh' );
5896
5897            self.trigger( 'onUpdate', slide );
5898        },
5899
5900        // Update cursor style depending if content can be zoomed
5901        // ======================================================
5902
5903        updateCursor : function( nextWidth, nextHeight ) {
5904
5905            var self = this;
5906            var canScale;
5907
5908            var $container = self.$refs.container.removeClass('fancybox-controls--canzoomIn fancybox-controls--canzoomOut fancybox-controls--canGrab');
5909
5910            if ( self.isClosing || !self.opts.touch ) {
5911                return;
5912            }
5913
5914            if ( nextWidth !== undefined && nextHeight !== undefined ) {
5915                canScale = nextWidth < self.current.width && nextHeight < self.current.height;
5916
5917            } else {
5918                canScale = self.isScaledDown();
5919            }
5920
5921            if ( canScale ) {
5922                $container.addClass('fancybox-controls--canzoomIn');
5923
5924            } else if ( self.group.length < 2 ) {
5925                $container.addClass('fancybox-controls--canzoomOut');
5926
5927            } else {
5928                $container.addClass('fancybox-controls--canGrab');
5929            }
5930
5931        },
5932
5933        // Load content into the slide
5934        // ===========================
5935
5936        loadSlide : function( slide ) {
5937
5938            var self = this, type, $slide;
5939            var ajaxLoad;
5940
5941            if ( !slide || slide.isLoaded || slide.isLoading ) {
5942                return;
5943            }
5944
5945            slide.isLoading = true;
5946
5947            self.trigger( 'beforeLoad', slide );
5948
5949            type   = slide.type;
5950            $slide = slide.$slide;
5951
5952            $slide
5953                .off( 'refresh' )
5954                .trigger( 'onReset' )
5955                .addClass( 'fancybox-slide--' + ( type || 'unknown' ) )
5956                .addClass( slide.opts.slideClass );
5957
5958            // Create content depending on the type
5959
5960            switch ( type ) {
5961
5962                case 'image':
5963
5964                    self.setImage( slide );
5965
5966                break;
5967
5968                case 'iframe':
5969
5970                    self.setIframe( slide );
5971
5972                break;
5973
5974                case 'html':
5975
5976                    self.setContent( slide, slide.content );
5977
5978                break;
5979
5980                case 'inline':
5981
5982                    if ( $( slide.src ).length ) {
5983                        self.setContent( slide, $( slide.src ) );
5984
5985                    } else {
5986                        self.setError( slide );
5987                    }
5988
5989                break;
5990
5991                case 'ajax':
5992
5993                    self.showLoading( slide );
5994
5995                    ajaxLoad = $.ajax( $.extend( {}, slide.opts.ajax.settings, {
5996
5997                        url: slide.src,
5998
5999                        success: function ( data, textStatus ) {
6000
6001                            if ( textStatus === 'success' ) {
6002                                self.setContent( slide, data );
6003                            }
6004
6005                        },
6006
6007                        error: function ( jqXHR, textStatus ) {
6008
6009                            if ( jqXHR && textStatus !== 'abort' ) {
6010                                self.setError( slide );
6011                            }
6012
6013                        }
6014
6015                    }));
6016
6017                    $slide.one( 'onReset', function () {
6018                        ajaxLoad.abort();
6019                    });
6020
6021                break;
6022
6023                default:
6024
6025                    self.setError( slide );
6026
6027                break;
6028
6029            }
6030
6031            return true;
6032
6033        },
6034
6035
6036        // Use thumbnail image, if possible
6037        // ================================
6038
6039        setImage : function( slide ) {
6040
6041            var self   = this;
6042            var srcset = slide.opts.image.srcset;
6043
6044            var found, temp, pxRatio, windowWidth;
6045
6046            if ( slide.isLoaded && !slide.hasError ) {
6047                self.afterLoad( slide );
6048
6049                return;
6050            }
6051
6052            // If we have "srcset", then we need to find matching "src" value.
6053            // This is necessary, because when you set an src attribute, the browser will preload the image
6054            // before any javascript or even CSS is applied.
6055            if ( srcset ) {
6056                pxRatio     = window.devicePixelRatio || 1;
6057                windowWidth = window.innerWidth  * pxRatio;
6058
6059                temp = srcset.split(',').map(function (el) {
6060            		var ret = {};
6061
6062            		el.trim().split(/\s+/).forEach(function (el, i) {
6063                        var value = parseInt(el.substring(0, el.length - 1), 10);
6064
6065            			if ( i === 0 ) {
6066            				return (ret.url = el);
6067            			}
6068
6069                        if ( value ) {
6070                            ret.value   = value;
6071                            ret.postfix = el[el.length - 1];
6072                        }
6073
6074            		});
6075
6076            		return ret;
6077            	});
6078
6079                // Sort by value
6080                temp.sort(function (a, b) {
6081                  return a.value - b.value;
6082                });
6083
6084                // Ok, now we have an array of all srcset values
6085                for ( var j = 0; j < temp.length; j++ ) {
6086                    var el = temp[ j ];
6087
6088                    if ( ( el.postfix === 'w' && el.value >= windowWidth ) || ( el.postfix === 'x' && el.value >= pxRatio ) ) {
6089                        found = el;
6090                        break;
6091                    }
6092                }
6093
6094                // If not found, take the last one
6095                if ( !found && temp.length ) {
6096                    found = temp[ temp.length - 1 ];
6097                }
6098
6099                if ( found ) {
6100                    slide.src = found.url;
6101
6102                    // If we have default width/height values, we can calculate height for matching source
6103                    if ( slide.width && slide.height && found.postfix == 'w' ) {
6104                        slide.height = ( slide.width / slide.height ) * found.value;
6105                        slide.width  = found.value;
6106                    }
6107                }
6108            }
6109
6110            slide.$placeholder = $('<div class="fancybox-placeholder"></div>')
6111                .hide()
6112                .appendTo( slide.$slide );
6113
6114            if ( slide.opts.preload !== false && slide.opts.width && slide.opts.height && ( slide.opts.thumb || slide.opts.$thumb ) ) {
6115
6116                slide.width  = slide.opts.width;
6117                slide.height = slide.opts.height;
6118
6119                slide.$ghost = $('<img />')
6120                    .one('load error', function() {
6121
6122                        if ( self.isClosing ) {
6123                            return;
6124                        }
6125
6126                        // Start preloading full size image
6127                        $('<img/>')[0].src = slide.src;
6128
6129                        // zoomIn or just show
6130                        self.revealImage( slide, function() {
6131
6132                            self.setBigImage( slide );
6133
6134                            if ( self.firstRun && slide.index === self.currIndex ) {
6135                                self.preload();
6136                            }
6137                        });
6138
6139                    })
6140                    .addClass( 'fancybox-image' )
6141                    .appendTo( slide.$placeholder )
6142                    .attr( 'src', slide.opts.thumb || slide.opts.$thumb.attr( 'src' ) );
6143
6144            } else {
6145
6146                self.setBigImage( slide );
6147
6148            }
6149
6150        },
6151
6152
6153        // Create full-size image
6154        // ======================
6155
6156        setBigImage : function ( slide ) {
6157            var self = this;
6158            var $img = $('<img />');
6159
6160            slide.$image = $img
6161                .one('error', function() {
6162
6163                    self.setError( slide );
6164
6165                })
6166                .one('load', function() {
6167
6168                    // Clear timeout that checks if loading icon needs to be displayed
6169                    clearTimeout( slide.timouts );
6170
6171                    slide.timouts = null;
6172
6173                    if ( self.isClosing ) {
6174                        return;
6175                    }
6176
6177                    slide.width  = this.naturalWidth;
6178                    slide.height = this.naturalHeight;
6179
6180                    if ( slide.opts.image.srcset ) {
6181                        $img.attr('sizes', '100vw').attr('srcset', slide.opts.image.srcset);
6182                    }
6183
6184                    self.afterLoad( slide );
6185
6186                    if ( slide.$ghost ) {
6187                        slide.timouts = setTimeout(function() {
6188                            slide.$ghost.hide();
6189
6190                        }, 350);
6191                    }
6192
6193                })
6194                .addClass('fancybox-image')
6195                .attr('src', slide.src)
6196                .appendTo( slide.$placeholder );
6197
6198            if ( $img[0].complete ) {
6199                  $img.trigger('load');
6200
6201            } else if( $img[0].error ) {
6202                 $img.trigger('error');
6203
6204            } else {
6205
6206                slide.timouts = setTimeout(function() {
6207                    if ( !$img[0].complete && !slide.hasError ) {
6208                        self.showLoading( slide );
6209                    }
6210
6211                }, 150);
6212
6213            }
6214
6215            if ( slide.opts.image.protect ) {
6216                $('<div class="fancybox-spaceball"></div>').appendTo( slide.$placeholder ).on('contextmenu.fb',function(e){
6217                     if ( e.button == 2 ) {
6218                         e.preventDefault();
6219                     }
6220
6221                    return true;
6222                });
6223            }
6224
6225        },
6226
6227        // Simply show image holder without animation
6228        // It has been hidden initially to avoid flickering
6229        // ================================================
6230
6231        revealImage : function( slide, callback ) {
6232
6233            var self = this;
6234
6235            callback = callback || $.noop;
6236
6237            if ( slide.type !== 'image' || slide.hasError || slide.isRevealed === true ) {
6238
6239                callback.apply( self );
6240
6241                return;
6242            }
6243
6244            slide.isRevealed = true;
6245
6246            if ( !( slide.pos === self.currPos && self.zoomInOut( 'In', slide.opts.speed, callback ) ) ) {
6247
6248                if ( slide.$ghost && !slide.isLoaded ) {
6249                    self.updateSlide( slide, true );
6250                }
6251
6252                if ( slide.pos === self.currPos ) {
6253                    $.fancybox.animate( slide.$placeholder, { opacity: 0 }, { opacity: 1 }, 300, callback );
6254
6255                } else {
6256                    slide.$placeholder.show();
6257                }
6258
6259                callback.apply( self );
6260
6261            }
6262
6263        },
6264
6265        // Create iframe wrapper, iframe and bindings
6266        // ==========================================
6267
6268        setIframe : function( slide ) {
6269            var self	= this,
6270                opts    = slide.opts.iframe,
6271                $slide	= slide.$slide,
6272                $iframe;
6273
6274            slide.$content = $('<div class="fancybox-content"></div>')
6275                .css( opts.css )
6276                .appendTo( $slide );
6277
6278            $iframe = $( opts.tpl.replace(/\{rnd\}/g, new Date().getTime()) )
6279                .attr('scrolling', $.fancybox.isTouch ? 'auto' : opts.scrolling)
6280                .appendTo( slide.$content );
6281
6282            if ( opts.preload ) {
6283                slide.$content.addClass( 'fancybox-tmp' );
6284
6285                self.showLoading( slide );
6286
6287                // Unfortunately, it is not always possible to determine if iframe is successfully loaded
6288                // (due to browser security policy)
6289
6290                $iframe.on('load.fb error.fb', function(e) {
6291                    this.isReady = 1;
6292
6293                    slide.$slide.trigger( 'refresh' );
6294
6295                    self.afterLoad( slide );
6296
6297                });
6298
6299                // Recalculate iframe content size
6300
6301                $slide.on('refresh.fb', function() {
6302                    var $wrap = slide.$content,
6303                        $contents,
6304                        $body,
6305                        scrollWidth,
6306                        frameWidth,
6307                        frameHeight;
6308
6309                    if ( $iframe[0].isReady !== 1 ) {
6310                        return;
6311                    }
6312
6313                    // Check if content is accessible,
6314                    // it will fail if frame is not with the same origin
6315
6316                    try {
6317                        $contents = $iframe.contents();
6318                        $body     = $contents.find('body');
6319
6320                    } catch (ignore) {}
6321
6322                    // Calculate dimensions for the wrapper
6323
6324                    if ( $body && $body.length && !( opts.css.width !== undefined && opts.css.height !== undefined ) ) {
6325
6326                        scrollWidth = $iframe[0].contentWindow.document.documentElement.scrollWidth;
6327
6328                        frameWidth	= Math.ceil( $body.outerWidth(true) + ( $wrap.width() - scrollWidth ) );
6329                        frameHeight	= Math.ceil( $body.outerHeight(true) );
6330
6331                        // Resize wrapper to fit iframe content
6332
6333                        $wrap.css({
6334                            'width'  : opts.css.width  === undefined ? frameWidth  + ( $wrap.outerWidth()  - $wrap.innerWidth() )  : opts.css.width,
6335                            'height' : opts.css.height === undefined ? frameHeight + ( $wrap.outerHeight() - $wrap.innerHeight() ) : opts.css.height
6336                        });
6337
6338                    }
6339
6340                    $wrap.removeClass( 'fancybox-tmp' );
6341
6342                });
6343
6344            } else {
6345
6346                this.afterLoad( slide );
6347
6348            }
6349
6350            $iframe.attr( 'src', slide.src );
6351
6352            if ( slide.opts.smallBtn ) {
6353                slide.$content.prepend( slide.opts.closeTpl );
6354            }
6355
6356            // Remove iframe if closing or changing gallery item
6357
6358            $slide.one('onReset', function () {
6359
6360                // This helps IE not to throw errors when closing
6361
6362                try {
6363
6364                    $(this).find('iframe').hide().attr('src', '//about:blank');
6365
6366                } catch (ignore) {}
6367
6368                $(this).empty();
6369
6370                slide.isLoaded = false;
6371
6372            });
6373
6374        },
6375
6376
6377        // Wrap and append content to the slide
6378        // ======================================
6379
6380        setContent : function ( slide, content ) {
6381
6382            var self = this;
6383
6384            if ( self.isClosing ) {
6385                return;
6386            }
6387
6388            self.hideLoading( slide );
6389
6390            slide.$slide.empty();
6391
6392            if ( isQuery( content ) && content.parent().length ) {
6393
6394                // If it is a jQuery object, then it will be moved to the box.
6395                // The placeholder is created so we will know where to put it back.
6396                // If user is navigating gallery fast, then the content might be already moved to the box
6397
6398                if ( content.data( 'placeholder' ) ) {
6399                    content.parents('.fancybox-slide').trigger( 'onReset' );
6400                }
6401
6402                content.data({'placeholder' : $('<div></div>' ).hide().insertAfter( content ) }).css('display', 'inline-block');
6403
6404            } else {
6405
6406                if ( $.type( content ) === 'string' ) {
6407
6408                    content = $('<div>').append( content ).contents();
6409
6410                    if ( content[0].nodeType === 3 ) {
6411                        content = $('<div>').html( content );
6412                    }
6413
6414                }
6415
6416                if ( slide.opts.selector ) {
6417                    content = $('<div>').html( content ).find( slide.opts.selector );
6418                }
6419
6420            }
6421
6422            slide.$slide.one('onReset', function () {
6423                var placeholder = isQuery( content ) ? content.data('placeholder') : 0;
6424
6425                if ( placeholder ) {
6426                    content.hide().replaceAll( placeholder );
6427
6428                    content.data( 'placeholder', null );
6429                }
6430
6431                if ( !slide.hasError ) {
6432                    $(this).empty();
6433
6434                    slide.isLoaded = false;
6435                }
6436
6437            });
6438
6439            slide.$content = $( content ).appendTo( slide.$slide );
6440
6441            if ( slide.opts.smallBtn === true ) {
6442                slide.$content.find( '.fancybox-close-small' ).remove().end().eq(0).append( slide.opts.closeTpl );
6443            }
6444
6445            this.afterLoad( slide );
6446
6447        },
6448
6449        // Display error message
6450        // =====================
6451
6452        setError : function ( slide ) {
6453
6454            slide.hasError = true;
6455
6456            this.setContent( slide, slide.opts.errorTpl );
6457
6458        },
6459
6460
6461        showLoading : function( slide ) {
6462            var self = this;
6463
6464            slide = slide || self.current;
6465
6466            if ( slide && !slide.$spinner ) {
6467                slide.$spinner = $( self.opts.spinnerTpl ).appendTo( slide.$slide );
6468            }
6469
6470        },
6471
6472        hideLoading : function( slide ) {
6473
6474            var self = this;
6475
6476            slide = slide || self.current;
6477
6478            if ( slide && slide.$spinner ) {
6479                slide.$spinner.remove();
6480
6481                delete slide.$spinner;
6482            }
6483
6484        },
6485
6486        afterMove : function() {
6487
6488            var self    = this;
6489            var current = self.current;
6490            var slides  = {};
6491
6492            if ( !current ) {
6493                return;
6494            }
6495
6496            current.$slide.siblings().trigger( 'onReset' );
6497
6498            // Remove unnecessary slides
6499            $.each( self.slides, function( key, slide ) {
6500
6501                if (  slide.pos >= self.currPos - 1 && slide.pos <= self.currPos + 1 ) {
6502                    slides[ slide.pos ] = slide;
6503
6504                } else if ( slide ) {
6505                    slide.$slide.remove();
6506                }
6507
6508            });
6509
6510            self.slides = slides;
6511
6512            self.trigger( 'afterMove' );
6513
6514            if ( current.isLoaded ) {
6515                self.complete();
6516            }
6517
6518        },
6519
6520        // Adjustments after slide has been loaded
6521        // =======================================
6522
6523        afterLoad : function( slide ) {
6524
6525            var self = this;
6526
6527            if ( self.isClosing ) {
6528                return;
6529            }
6530
6531            slide.isLoading = false;
6532            slide.isLoaded  = true;
6533
6534            self.trigger( 'afterLoad', slide );
6535
6536            self.hideLoading( slide );
6537
6538            // Resize content to fit inside slide
6539            // Skip if slide has an $ghost element, because then it has been already processed
6540            if ( !slide.$ghost ) {
6541                self.updateSlide( slide, true );
6542            }
6543
6544            if ( slide.index === self.currIndex && slide.isMoved ) {
6545                self.complete();
6546
6547            } else if ( !slide.$ghost ) {
6548                self.revealImage( slide );
6549            }
6550
6551        },
6552
6553
6554        // Final adjustments after current gallery item is moved to position
6555        // and it`s content is loaded
6556        // ==================================================================
6557
6558        complete : function() {
6559
6560            var self = this;
6561
6562            var current = self.current;
6563
6564            self.revealImage( current, function() {
6565                current.isComplete = true;
6566
6567                current.$slide.addClass('fancybox-slide--complete');
6568
6569                self.updateCursor();
6570
6571                self.trigger( 'onComplete' );
6572
6573                // Try to focus on the first focusable element, skip for images and iframes
6574                if ( current.opts.focus && !( current.type === 'image' || current.type === 'iframe' ) ) {
6575                    self.focus();
6576                }
6577
6578            });
6579
6580        },
6581
6582
6583        // Preload next and previous slides
6584        // ================================
6585
6586        preload : function() {
6587            var self = this;
6588            var next, prev;
6589
6590            if ( self.group.length < 2 ) {
6591                return;
6592            }
6593
6594            next  = self.slides[ self.currPos + 1 ];
6595            prev  = self.slides[ self.currPos - 1 ];
6596
6597            if ( next && next.type === 'image' ) {
6598                self.loadSlide( next );
6599            }
6600
6601            if ( prev && prev.type === 'image' ) {
6602                self.loadSlide( prev );
6603            }
6604
6605        },
6606
6607
6608        // Try to find and focus on the first focusable element
6609        // ====================================================
6610
6611        focus : function() {
6612            var current = this.current;
6613            var $el;
6614
6615            $el = current && current.isComplete ? current.$slide.find('button,:input,[tabindex],a:not(".disabled")').filter(':visible:first') : null;
6616
6617            if ( !$el || !$el.length ) {
6618                $el = this.$refs.container;
6619            }
6620
6621            $el.focus();
6622
6623            // Scroll position of wrapper element sometimes changes after focusing (IE)
6624            this.$refs.slider_wrap.scrollLeft(0);
6625
6626            // And the same goes for slide element
6627            if ( current ) {
6628                current.$slide.scrollTop(0);
6629            }
6630
6631        },
6632
6633
6634        // Activates current instance - brings container to the front and enables keyboard,
6635        // notifies other instances about deactivating
6636        // =================================================================================
6637
6638        activate : function () {
6639            var self = this;
6640
6641            // Deactivate all instances
6642
6643            $( '.fancybox-container' ).each(function () {
6644                var instance = $(this).data( 'FancyBox' );
6645
6646                // Skip self and closing instances
6647
6648                if (instance && instance.uid !== self.uid && !instance.isClosing) {
6649                    instance.trigger( 'onDeactivate' );
6650                }
6651
6652            });
6653
6654            if ( self.current ) {
6655
6656                if ( self.$refs.container.index() > 0 ) {
6657                    self.$refs.container.prependTo( document.body );
6658                }
6659
6660                self.updateControls();
6661            }
6662
6663            self.trigger( 'onActivate' );
6664
6665            self.addEvents();
6666
6667        },
6668
6669
6670        // Start closing procedure
6671        // This will start "zoom-out" animation if needed and clean everything up afterwards
6672        // =================================================================================
6673
6674        close : function( e ) {
6675
6676            var self     = this;
6677            var current  = self.current;
6678            var duration = current.opts.speed;
6679
6680            var done = $.proxy(function() {
6681
6682                self.cleanUp( e );  // Now "this" is again our instance
6683
6684            }, this);
6685
6686            if ( self.isAnimating || self.isClosing ) {
6687                return false;
6688            }
6689
6690            // If beforeClose callback prevents closing, make sure content is centered
6691            if ( self.trigger( 'beforeClose', e ) === false ) {
6692                $.fancybox.stop( self.$refs.slider );
6693
6694                requestAFrame(function() {
6695                    self.update( true, true, 150 );
6696                });
6697
6698                return;
6699            }
6700
6701            self.isClosing = true;
6702
6703            if ( current.timouts ) {
6704                clearTimeout( current.timouts );
6705            }
6706
6707            if ( e !== true) {
6708                $.fancybox.stop( self.$refs.slider );
6709            }
6710
6711            self.$refs.container
6712                .removeClass('fancybox-container--active')
6713                .addClass('fancybox-container--closing');
6714
6715            current.$slide
6716                .removeClass('fancybox-slide--complete')
6717                .siblings()
6718                .remove();
6719
6720
6721            if ( !current.isMoved ) {
6722                current.$slide.css('overflow', 'visible');
6723            }
6724
6725            // Remove all events
6726            // If there are multiple instances, they will be set again by "activate" method
6727
6728            self.removeEvents();
6729
6730            // Clean up
6731
6732            self.hideLoading( current );
6733
6734            self.hideControls();
6735
6736            self.updateCursor();
6737
6738            self.$refs.bg.css('transition-duration', duration + 'ms');
6739
6740            this.$refs.container.removeClass( 'fancybox-container--ready' );
6741
6742            if ( e === true ) {
6743                setTimeout( done, duration );
6744
6745            } else if ( !self.zoomInOut( 'Out', duration, done ) ) {
6746                $.fancybox.animate( self.$refs.container, null, { opacity : 0 }, duration, "easeInSine", done );
6747            }
6748
6749        },
6750
6751
6752        // Final adjustments after removing the instance
6753        // =============================================
6754
6755        cleanUp : function( e ) {
6756            var self = this,
6757                instance;
6758
6759            self.$refs.slider.children().trigger( 'onReset' );
6760
6761            self.$refs.container.empty().remove();
6762
6763            self.trigger( 'afterClose', e );
6764
6765            self.current = null;
6766
6767            // Check if there are other instances
6768            instance = $.fancybox.getInstance();
6769
6770            if ( instance ) {
6771                instance.activate();
6772
6773            } else {
6774
6775                $( 'html' ).removeClass( 'fancybox-enabled' );
6776                $( 'body' ).removeAttr( 'style' );
6777
6778                $W.scrollTop( self.scrollTop ).scrollLeft( self.scrollLeft );
6779
6780                $( '#fancybox-noscroll' ).remove();
6781
6782            }
6783
6784            // Place back focus
6785            if ( self.$lastFocus ) {
6786                self.$lastFocus.focus();
6787            }
6788
6789        },
6790
6791
6792        // Call callback and trigger an event
6793        // ==================================
6794
6795        trigger : function( name, slide ) {
6796            var args  = Array.prototype.slice.call(arguments, 1),
6797                self  = this,
6798                obj   = slide && slide.opts ? slide : self.current,
6799                rez;
6800
6801            if ( obj ) {
6802                args.unshift( obj );
6803
6804            } else {
6805                obj = self;
6806            }
6807
6808            args.unshift( self );
6809
6810            if ( $.isFunction( obj.opts[ name ] ) ) {
6811                rez = obj.opts[ name ].apply( obj, args );
6812            }
6813
6814            if ( rez === false ) {
6815                return rez;
6816            }
6817
6818            if ( name === 'afterClose' ) {
6819                $( document ).trigger( name + '.fb', args );
6820
6821            } else {
6822                self.$refs.container.trigger( name + '.fb', args );
6823            }
6824
6825        },
6826
6827
6828        // Toggle toolbar and caption
6829        // ==========================
6830
6831        toggleControls : function( force ) {
6832
6833            if ( this.isHiddenControls ) {
6834                this.updateControls( force );
6835
6836            } else {
6837                this.hideControls();
6838            }
6839
6840
6841        },
6842
6843
6844        // Hide toolbar and caption
6845        // ========================
6846
6847        hideControls : function () {
6848
6849            this.isHiddenControls = true;
6850
6851            this.$refs.container.removeClass('fancybox-show-controls');
6852
6853            this.$refs.container.removeClass('fancybox-show-caption');
6854
6855        },
6856
6857
6858        // Update infobar values, navigation button states and reveal caption
6859        // ==================================================================
6860
6861        updateControls : function ( force ) {
6862
6863            var self = this;
6864
6865            var $container = self.$refs.container;
6866            var $caption   = self.$refs.caption;
6867
6868            // Toggle infobar and buttons
6869
6870            var current  = self.current;
6871            var index    = current.index;
6872            var opts     = current.opts;
6873            var caption  = opts.caption;
6874
6875            if ( this.isHiddenControls && force !== true ) {
6876                return;
6877            }
6878
6879            this.isHiddenControls = false;
6880
6881            $container
6882                .addClass('fancybox-show-controls')
6883                .toggleClass('fancybox-show-infobar', !!opts.infobar && self.group.length > 1)
6884                .toggleClass('fancybox-show-buttons', !!opts.buttons )
6885                .toggleClass('fancybox-is-modal',     !!opts.modal );
6886
6887            $('.fancybox-button--left',  $container).toggleClass( 'fancybox-button--disabled', (!opts.loop && index <= 0 ) );
6888            $('.fancybox-button--right', $container).toggleClass( 'fancybox-button--disabled', (!opts.loop && index >= self.group.length - 1) );
6889
6890            $('.fancybox-button--play',  $container).toggle( !!( opts.slideShow && self.group.length > 1) );
6891            $('.fancybox-button--close', $container).toggle( !!opts.closeBtn );
6892
6893            // Update infobar values
6894
6895            $('.js-fancybox-count', $container).html( self.group.length );
6896            $('.js-fancybox-index', $container).html( index + 1 );
6897
6898            // Recalculate content dimensions
6899            current.$slide.trigger( 'refresh' );
6900
6901            // Reveal or create new caption
6902            if ( $caption ) {
6903                $caption.empty();
6904            }
6905
6906            if ( caption && caption.length ) {
6907                $caption.html( caption );
6908
6909                this.$refs.container.addClass( 'fancybox-show-caption ');
6910
6911                self.$caption = $caption;
6912
6913            } else {
6914                this.$refs.container.removeClass( 'fancybox-show-caption' );
6915            }
6916
6917        }
6918
6919    });
6920
6921
6922    $.fancybox = {
6923
6924        version  : "3.0.47",
6925        defaults : defaults,
6926
6927
6928        // Get current instance and execute a command.
6929        //
6930        // Examples of usage:
6931        //
6932        //   $instance = $.fancybox.getInstance();
6933        //   $.fancybox.getInstance().jumpTo( 1 );
6934        //   $.fancybox.getInstance( 'jumpTo', 1 );
6935        //   $.fancybox.getInstance( function() {
6936        //       console.info( this.currIndex );
6937        //   });
6938        // ======================================================
6939
6940        getInstance : function ( command ) {
6941            var instance = $('.fancybox-container:not(".fancybox-container--closing"):first').data( 'FancyBox' );
6942            var args     = Array.prototype.slice.call(arguments, 1);
6943
6944            if ( instance instanceof FancyBox ) {
6945
6946                if ( $.type( command ) === 'string' ) {
6947                    instance[ command ].apply( instance, args );
6948
6949                } else if ( $.type( command ) === 'function' ) {
6950                    command.apply( instance, args );
6951
6952                }
6953
6954                return instance;
6955            }
6956
6957            return false;
6958
6959        },
6960
6961
6962        // Create new instance
6963        // ===================
6964
6965        open : function ( items, opts, index ) {
6966            return new FancyBox( items, opts, index );
6967        },
6968
6969
6970        // Close current or all instances
6971        // ==============================
6972
6973        close : function ( all ) {
6974            var instance = this.getInstance();
6975
6976            if ( instance ) {
6977                instance.close();
6978
6979                // Try to find and close next instance
6980
6981                if ( all === true ) {
6982                    this.close();
6983                }
6984            }
6985
6986        },
6987
6988
6989        // Test for the existence of touch events in the browser
6990        // Limit to mobile devices
6991        // ====================================================
6992
6993        isTouch : document.createTouch !== undefined && /Android|webOS|iPhone|iPad|iPod|BlackBerry/i.test(navigator.userAgent),
6994
6995
6996        // Detect if 'translate3d' support is available
6997        // ============================================
6998
6999        use3d : (function() {
7000            var div = document.createElement('div');
7001
7002            return window.getComputedStyle( div ).getPropertyValue('transform') && !(document.documentMode && document.documentMode <= 11);
7003        }()),
7004
7005
7006        // Helper function to get current visual state of an element
7007        // returns array[ top, left, horizontal-scale, vertical-scale, opacity ]
7008        // =====================================================================
7009
7010        getTranslate : function( $el ) {
7011            var position, matrix;
7012
7013            if ( !$el || !$el.length ) {
7014                return false;
7015            }
7016
7017            position = $el.get( 0 ).getBoundingClientRect();
7018            matrix   = $el.eq( 0 ).css('transform');
7019
7020            if ( matrix && matrix.indexOf( 'matrix' ) !== -1 ) {
7021                matrix = matrix.split('(')[1];
7022                matrix = matrix.split(')')[0];
7023                matrix = matrix.split(',');
7024            } else {
7025                matrix = [];
7026            }
7027
7028            if ( matrix.length ) {
7029
7030                // If IE
7031                if ( matrix.length > 10 ) {
7032                    matrix = [ matrix[13], matrix[12], matrix[0], matrix[5] ];
7033
7034                } else {
7035                    matrix = [ matrix[5], matrix[4], matrix[0], matrix[3]];
7036                }
7037
7038                matrix = matrix.map(parseFloat);
7039
7040            } else {
7041                matrix = [ 0, 0, 1, 1 ];
7042            }
7043
7044            return {
7045                top     : matrix[ 0 ],
7046                left    : matrix[ 1 ],
7047                scaleX  : matrix[ 2 ],
7048                scaleY  : matrix[ 3 ],
7049                opacity : parseFloat( $el.css('opacity') ),
7050                width   : position.width,
7051                height  : position.height
7052            };
7053
7054        },
7055
7056
7057        // Shortcut for setting "translate3d" properties for element
7058        // Can set be used to set opacity, too
7059        // ========================================================
7060
7061        setTranslate : function( $el, props ) {
7062            var str  = '';
7063            var css  = {};
7064
7065            if ( !$el || !props ) {
7066                return;
7067            }
7068
7069            if ( props.left !== undefined || props.top !== undefined ) {
7070
7071                str = ( props.left === undefined ? $el.position().top : props.left )  + 'px, ' + ( props.top === undefined ? $el.position().top : props.top ) + 'px';
7072
7073                if ( this.use3d ) {
7074                    str = 'translate3d(' + str + ', 0px)';
7075
7076                } else {
7077                    str = 'translate(' + str + ')';
7078                }
7079
7080            }
7081
7082            if ( props.scaleX !== undefined && props.scaleY !== undefined ) {
7083                str = (str.length ? str + ' ' : '') + 'scale(' + props.scaleX + ', ' + props.scaleY + ')';
7084            }
7085
7086            if ( str.length ) {
7087                css.transform = str;
7088            }
7089
7090            if ( props.opacity !== undefined ) {
7091                css.opacity = props.opacity;
7092            }
7093
7094            if ( props.width !== undefined ) {
7095                css.width = props.width;
7096            }
7097
7098            if ( props.height !== undefined ) {
7099                css.height = props.height;
7100            }
7101
7102            return $el.css( css );
7103
7104        },
7105
7106
7107        // Common easings for entrances and exits
7108        // t: current time, b: begInnIng value, c: change In value, d: duration
7109        // ====================================================================
7110
7111        easing : {
7112            easeOutCubic : function (t, b, c, d) {
7113                return c * ((t=t/d-1)*t*t + 1) + b;
7114            },
7115            easeInCubic : function (t, b, c, d) {
7116                return c * (t/=d)*t*t + b;
7117            },
7118            easeOutSine : function (t, b, c, d) {
7119                return c * Math.sin(t/d * (Math.PI/2)) + b;
7120            },
7121            easeInSine : function (t, b, c, d) {
7122                return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
7123            }
7124        },
7125
7126
7127        // Stop fancyBox animation
7128        // =======================
7129
7130        stop : function( $el ) {
7131
7132            $el.removeData( 'animateID' );
7133
7134        },
7135
7136        // Animate element using "translate3d"
7137        // Usage:
7138        // animate( element, start properties, end properties, duration, easing, callback )
7139        // or
7140        // animate( element, start properties, end properties, duration, callback )
7141        // =================================================================================
7142
7143        animate : function( $el, from, to, duration, easing, done ) {
7144
7145            var self = this;
7146
7147            var lastTime = null;
7148            var animTime = 0;
7149
7150            var curr;
7151            var diff;
7152            var id;
7153
7154            var finish = function() {
7155                if ( to.scaleX !== undefined && to.scaleY !== undefined && from && from.width !== undefined && from.height !== undefined ) {
7156                    to.width  = from.width  * to.scaleX;
7157                    to.height = from.height * to.scaleY;
7158
7159                    to.scaleX = 1;
7160                    to.scaleY = 1;
7161                }
7162
7163                self.setTranslate( $el, to );
7164
7165                done();
7166            };
7167
7168            var frame = function ( timestamp ) {
7169                curr = [];
7170                diff = 0;
7171
7172                // If "stop" method has been called on this element, then just stop
7173                if ( !$el.length || $el.data( 'animateID' ) !== id ) {
7174                    return;
7175                }
7176
7177                timestamp = timestamp || Date.now();
7178
7179                if ( lastTime ) {
7180                    diff = timestamp - lastTime;
7181                }
7182
7183                lastTime = timestamp;
7184                animTime += diff;
7185
7186                // Are we done?
7187                if ( animTime >= duration ) {
7188
7189                    finish();
7190
7191                    return;
7192                }
7193
7194                for ( var prop in to ) {
7195
7196                    if ( to.hasOwnProperty( prop ) && from[ prop ] !== undefined ) {
7197
7198                        if ( from[ prop ] == to[ prop ] ) {
7199                            curr[ prop ] = to[ prop ];
7200
7201                        } else {
7202                            curr[ prop ] = self.easing[ easing ]( animTime, from[ prop ], to[ prop ] - from[ prop ], duration );
7203                        }
7204
7205                    }
7206                }
7207
7208                self.setTranslate( $el, curr );
7209
7210                requestAFrame( frame );
7211            };
7212
7213            self.animateID = id = self.animateID === undefined ? 1 : self.animateID + 1;
7214
7215            $el.data( 'animateID', id );
7216
7217            if ( done === undefined && $.type(easing) == 'function' ) {
7218                done   = easing;
7219                easing = undefined;
7220            }
7221
7222            if ( !easing ) {
7223                easing = "easeOutCubic";
7224            }
7225
7226            done = done || $.noop;
7227
7228            if ( from ) {
7229                this.setTranslate( $el, from );
7230
7231            } else {
7232
7233                // We need current values to calculate change in time
7234                from = this.getTranslate( $el );
7235            }
7236
7237            if ( duration ) {
7238                $el.show();
7239
7240                requestAFrame( frame );
7241
7242            } else {
7243                finish();
7244            }
7245
7246        }
7247
7248    };
7249
7250
7251    // Event handler for click event to "fancyboxed" links
7252    // ===================================================
7253
7254    function _run( e ) {
7255        var target	= e.currentTarget,
7256            opts	= e.data ? e.data.options : {},
7257            items	= e.data ? e.data.items : [],
7258            value	= '',
7259            index	= 0;
7260
7261        e.preventDefault();
7262        e.stopPropagation();
7263
7264        // Get all related items and find index for clicked one
7265
7266        if ( $(target).attr( 'data-fancybox' ) ) {
7267            value = $(target).data( 'fancybox' );
7268        }
7269
7270        if ( value ) {
7271            items = items.length ? items.filter( '[data-fancybox="' + value + '"]' ) : $( '[data-fancybox=' + value + ']' );
7272            index = items.index( target );
7273
7274        } else {
7275            items = [ target ];
7276        }
7277
7278        $.fancybox.open( items, opts, index );
7279    }
7280
7281
7282    // Create a jQuery plugin
7283    // ======================
7284
7285    $.fn.fancybox = function (options) {
7286
7287        this.off('click.fb-start').on('click.fb-start', {
7288            items   : this,
7289            options : options || {}
7290        }, _run);
7291
7292        return this;
7293
7294    };
7295
7296
7297    // Self initializing plugin
7298    // ========================
7299
7300    $(document).on('click.fb-start', '[data-fancybox]', _run);
7301
7302}(window, document, window.jQuery));
7303
7304// ==========================================================================
7305//
7306// Media
7307// Adds additional media type support
7308//
7309// ==========================================================================
7310;(function ($) {
7311
7312	'use strict';
7313
7314	// Formats matching url to final form
7315
7316	var format = function (url, rez, params) {
7317		if ( !url ) {
7318			return;
7319		}
7320
7321		params = params || '';
7322
7323		if ( $.type(params) === "object" ) {
7324			params = $.param(params, true);
7325		}
7326
7327		$.each(rez, function (key, value) {
7328			url = url.replace('$' + key, value || '');
7329		});
7330
7331		if (params.length) {
7332			url += (url.indexOf('?') > 0 ? '&' : '?') + params;
7333		}
7334
7335		return url;
7336	};
7337
7338	// Object containing properties for each media type
7339
7340	var media = {
7341		youtube: {
7342			matcher: /(youtube\.com|youtu\.be|youtube\-nocookie\.com)\/(watch\?(.*&)?v=|v\/|u\/|embed\/?)?(videoseries\?list=(.*)|[\w-]{11}|\?listType=(.*)&list=(.*))(.*)/i,
7343			params: {
7344				autoplay: 1,
7345				autohide: 1,
7346				fs: 1,
7347				rel: 0,
7348				hd: 1,
7349				wmode: 'transparent',
7350				enablejsapi: 1,
7351				html5: 1
7352			},
7353			paramPlace : 8,
7354			type: 'iframe',
7355			url: '//www.youtube.com/embed/$4',
7356			thumb: '//img.youtube.com/vi/$4/hqdefault.jpg'
7357		},
7358
7359		vimeo: {
7360			matcher: /^.+vimeo.com\/(.*\/)?([\d]+)(.*)?/,
7361			params: {
7362				autoplay: 1,
7363				hd: 1,
7364				show_title: 1,
7365				show_byline: 1,
7366				show_portrait: 0,
7367				fullscreen: 1,
7368				api: 1
7369			},
7370			paramPlace : 3,
7371			type: 'iframe',
7372			url: '//player.vimeo.com/video/$2'
7373		},
7374
7375		metacafe: {
7376			matcher: /metacafe.com\/watch\/(\d+)\/(.*)?/,
7377			type: 'iframe',
7378			url: '//www.metacafe.com/embed/$1/?ap=1'
7379		},
7380
7381		dailymotion: {
7382			matcher: /dailymotion.com\/video\/(.*)\/?(.*)/,
7383			params: {
7384				additionalInfos: 0,
7385				autoStart: 1
7386			},
7387			type: 'iframe',
7388			url: '//www.dailymotion.com/embed/video/$1'
7389		},
7390
7391		vine: {
7392			matcher: /vine.co\/v\/([a-zA-Z0-9\?\=\-]+)/,
7393			type: 'iframe',
7394			url: '//vine.co/v/$1/embed/simple'
7395		},
7396
7397		instagram: {
7398			matcher: /(instagr\.am|instagram\.com)\/p\/([a-zA-Z0-9_\-]+)\/?/i,
7399			type: 'image',
7400			url: '//$1/p/$2/media/?size=l'
7401		},
7402
7403		// Examples:
7404		// http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16
7405		// http://maps.google.com/?ll=48.857995,2.294297&spn=0.007666,0.021136&t=m&z=16
7406		// https://www.google.lv/maps/place/Googleplex/@37.4220041,-122.0833494,17z/data=!4m5!3m4!1s0x0:0x6c296c66619367e0!8m2!3d37.4219998!4d-122.0840572
7407		google_maps: {
7408			matcher: /(maps\.)?google\.([a-z]{2,3}(\.[a-z]{2})?)\/(((maps\/(place\/(.*)\/)?\@(.*),(\d+.?\d+?)z))|(\?ll=))(.*)?/i,
7409			type: 'iframe',
7410			url: function (rez) {
7411				return '//maps.google.' + rez[2] + '/?ll=' + ( rez[9] ? rez[9] + '&z=' + Math.floor(  rez[10]  ) + ( rez[12] ? rez[12].replace(/^\//, "&") : '' )  : rez[12] ) + '&output=' + ( rez[12] && rez[12].indexOf('layer=c') > 0 ? 'svembed' : 'embed' );
7412			}
7413		}
7414	};
7415
7416	$(document).on('onInit.fb', function (e, instance) {
7417
7418		$.each(instance.group, function( i, item ) {
7419
7420			var url	 = item.src || '',
7421				type = false,
7422				thumb,
7423				rez,
7424				params,
7425				urlParams,
7426				o,
7427				provider;
7428
7429			// Skip items that already have content type
7430			if ( item.type ) {
7431				return;
7432			}
7433
7434			// Look for any matching media type
7435
7436			$.each(media, function ( n, el ) {
7437				rez = url.match(el.matcher);
7438				o   = {};
7439				provider = n;
7440
7441				if (!rez) {
7442					return;
7443				}
7444
7445				type = el.type;
7446
7447				if ( el.paramPlace && rez[ el.paramPlace ] ) {
7448					urlParams = rez[ el.paramPlace ];
7449
7450					if ( urlParams[ 0 ] == '?' ) {
7451						urlParams = urlParams.substring(1);
7452					}
7453
7454					urlParams = urlParams.split('&');
7455
7456					for ( var m = 0; m < urlParams.length; ++m ) {
7457						var p = urlParams[ m ].split('=', 2);
7458
7459						if ( p.length == 2 ) {
7460							o[ p[0] ] = decodeURIComponent( p[1].replace(/\+/g, " ") );
7461						}
7462					}
7463				}
7464
7465				params = $.extend( true, {}, el.params, item.opts[ n ], o );
7466
7467				url   = $.type(el.url) === "function" ? el.url.call(this, rez, params, item) : format(el.url, rez, params);
7468				thumb = $.type(el.thumb) === "function" ? el.thumb.call(this, rez, params, item) : format(el.thumb, rez);
7469
7470				if ( provider === 'vimeo' ) {
7471					url = url.replace('&%23', '#');
7472				}
7473
7474				return false;
7475			});
7476
7477			// If it is found, then change content type and update the url
7478
7479			if ( type ) {
7480				item.src  = url;
7481				item.type = type;
7482
7483				if ( !item.opts.thumb && !(item.opts.$thumb && item.opts.$thumb.length ) ) {
7484					item.opts.thumb = thumb;
7485				}
7486
7487				if ( type === 'iframe' ) {
7488					$.extend(true, item.opts, {
7489						iframe : {
7490							preload   : false,
7491							scrolling : "no"
7492						},
7493						smallBtn   : false,
7494						closeBtn   : true,
7495						fullScreen : false,
7496						slideShow  : false
7497					});
7498
7499					item.opts.slideClass += ' fancybox-slide--video';
7500				}
7501
7502			} else {
7503
7504				// If no content type is found, then set it to `iframe` as fallback
7505				item.type = 'iframe';
7506
7507			}
7508
7509		});
7510
7511	});
7512
7513}(window.jQuery));
7514
7515// ==========================================================================
7516//
7517// Guestures
7518// Adds touch guestures, handles click and tap events
7519//
7520// ==========================================================================
7521;(function (window, document, $) {
7522	'use strict';
7523
7524	var requestAFrame = (function() {
7525		return  window.requestAnimationFrame ||
7526				window.webkitRequestAnimationFrame ||
7527				window.mozRequestAnimationFrame ||
7528				function( callback ) {
7529					window.setTimeout(callback, 1000 / 60); };
7530				})();
7531
7532
7533	var pointers = function( e ) {
7534		var result = [];
7535
7536		e = e.originalEvent || e || window.e;
7537		e = e.touches && e.touches.length ? e.touches : ( e.changedTouches && e.changedTouches.length ? e.changedTouches : [ e ] );
7538
7539		for ( var key in e ) {
7540
7541			if ( e[ key ].pageX ) {
7542				result.push( { x : e[ key ].pageX, y : e[ key ].pageY } );
7543
7544			} else if ( e[ key ].clientX ) {
7545				result.push( { x : e[ key ].clientX, y : e[ key ].clientY } );
7546			}
7547		}
7548
7549		return result;
7550	};
7551
7552	var distance = function( point2, point1, what ) {
7553
7554		if ( !point1 || !point2 ) {
7555			return 0;
7556		}
7557
7558		if ( what === 'x' ) {
7559			return point2.x - point1.x;
7560
7561		} else if ( what === 'y' ) {
7562			return point2.y - point1.y;
7563		}
7564
7565		return Math.sqrt( Math.pow( point2.x - point1.x, 2 ) + Math.pow( point2.y - point1.y, 2 ) );
7566
7567	};
7568
7569	var isClickable = function( $el ) {
7570
7571	 	return $el.is('a') || $el.is('button') || $el.is('input') || $el.is('select') || $el.is('textarea') || $.isFunction( $el.get(0).onclick );
7572
7573	};
7574
7575	var hasScrollbars = function( el ) {
7576		var overflowY = window.getComputedStyle( el )['overflow-y'];
7577		var overflowX = window.getComputedStyle( el )['overflow-x'];
7578
7579		var vertical   = (overflowY === 'scroll' || overflowY === 'auto') && el.scrollHeight > el.clientHeight;
7580		var horizontal = (overflowX === 'scroll' || overflowX === 'auto') && el.scrollWidth > el.clientWidth;
7581
7582		return vertical || horizontal;
7583	};
7584
7585	var isScrollable = function ( $el ) {
7586
7587		var rez = false;
7588
7589		while ( true ) {
7590			rez	= hasScrollbars( $el.get(0) );
7591
7592			if ( rez ) {
7593				break;
7594			}
7595
7596			$el = $el.parent();
7597
7598			if ( !$el.length || $el.hasClass('fancybox-slider') || $el.is('body') ) {
7599				break;
7600			}
7601
7602		}
7603
7604		return rez;
7605
7606	};
7607
7608
7609	var Guestures = function ( instance ) {
7610
7611		var self = this;
7612
7613		self.instance = instance;
7614
7615		self.$wrap       = instance.$refs.slider_wrap;
7616		self.$slider     = instance.$refs.slider;
7617		self.$container  = instance.$refs.container;
7618
7619		self.destroy();
7620
7621		self.$wrap.on('touchstart.fb mousedown.fb', $.proxy(self, "ontouchstart"));
7622
7623	};
7624
7625	Guestures.prototype.destroy = function() {
7626
7627		this.$wrap.off('touchstart.fb mousedown.fb touchmove.fb mousemove.fb touchend.fb touchcancel.fb mouseup.fb mouseleave.fb');
7628
7629	};
7630
7631	Guestures.prototype.ontouchstart = function( e ) {
7632
7633		var self = this;
7634
7635		var $target  = $( e.target );
7636		var instance = self.instance;
7637		var current  = instance.current;
7638		var $content = current.$content || current.$placeholder;
7639
7640		self.startPoints = pointers( e );
7641
7642		self.$target  = $target;
7643		self.$content = $content;
7644
7645		self.canvasWidth  = Math.round( current.$slide[0].clientWidth );
7646		self.canvasHeight = Math.round( current.$slide[0].clientHeight );
7647
7648		self.startEvent = e;
7649
7650		// Skip if clicked on the scrollbar
7651		if ( e.originalEvent.clientX > self.canvasWidth + current.$slide.offset().left ) {
7652			return true;
7653		}
7654
7655		// Ignore taping on links, buttons and scrollable items
7656		if ( isClickable( $target ) || isClickable( $target.parent() ) || ( isScrollable( $target ) ) ) {
7657			return;
7658		}
7659
7660		// If "touch" is disabled, then handle click event
7661		if ( !current.opts.touch ) {
7662			self.endPoints = self.startPoints;
7663
7664			return self.ontap();
7665		}
7666
7667		// Ignore right click
7668		if ( e.originalEvent && e.originalEvent.button == 2 ) {
7669			return;
7670		}
7671
7672		e.stopPropagation();
7673		e.preventDefault();
7674
7675		if ( !current || self.instance.isAnimating || self.instance.isClosing ) {
7676			return;
7677		}
7678
7679		// Prevent zooming if already swiping
7680		if ( !self.startPoints || ( self.startPoints.length > 1 && !current.isMoved ) ) {
7681			return;
7682		}
7683
7684		self.$wrap.off('touchmove.fb mousemove.fb',  $.proxy(self, "ontouchmove"));
7685		self.$wrap.off('touchend.fb touchcancel.fb mouseup.fb mouseleave.fb',  $.proxy(self, "ontouchend"));
7686
7687		self.$wrap.on('touchend.fb touchcancel.fb mouseup.fb mouseleave.fb',  $.proxy(self, "ontouchend"));
7688		self.$wrap.on('touchmove.fb mousemove.fb',  $.proxy(self, "ontouchmove"));
7689
7690		self.startTime = new Date().getTime();
7691		self.distanceX = self.distanceY = self.distance = 0;
7692
7693		self.canTap    = false;
7694		self.isPanning = false;
7695		self.isSwiping = false;
7696		self.isZooming = false;
7697
7698		self.sliderStartPos = $.fancybox.getTranslate( self.$slider );
7699
7700		self.contentStartPos = $.fancybox.getTranslate( self.$content );
7701		self.contentLastPos  = null;
7702
7703		if ( self.startPoints.length === 1 && !self.isZooming ) {
7704			self.canTap = current.isMoved;
7705
7706			if ( current.type === 'image' && ( self.contentStartPos.width > self.canvasWidth + 1 || self.contentStartPos.height > self.canvasHeight + 1 ) ) {
7707
7708				$.fancybox.stop( self.$content );
7709
7710				self.isPanning = true;
7711
7712			} else {
7713
7714				$.fancybox.stop( self.$slider );
7715
7716				self.isSwiping = true;
7717			}
7718
7719			self.$container.addClass('fancybox-controls--isGrabbing');
7720
7721		}
7722
7723		if ( self.startPoints.length === 2 && current.isMoved  && !current.hasError && current.type === 'image' && ( current.isLoaded || current.$ghost ) ) {
7724
7725			self.isZooming = true;
7726
7727			self.isSwiping = false;
7728			self.isPanning = false;
7729
7730			$.fancybox.stop( self.$content );
7731
7732			self.centerPointStartX = ( ( self.startPoints[0].x + self.startPoints[1].x ) * 0.5 ) - $(window).scrollLeft();
7733			self.centerPointStartY = ( ( self.startPoints[0].y + self.startPoints[1].y ) * 0.5 ) - $(window).scrollTop();
7734
7735			self.percentageOfImageAtPinchPointX = ( self.centerPointStartX - self.contentStartPos.left ) / self.contentStartPos.width;
7736			self.percentageOfImageAtPinchPointY = ( self.centerPointStartY - self.contentStartPos.top  ) / self.contentStartPos.height;
7737
7738			self.startDistanceBetweenFingers = distance( self.startPoints[0], self.startPoints[1] );
7739		}
7740
7741	};
7742
7743	Guestures.prototype.ontouchmove = function( e ) {
7744
7745		var self = this;
7746
7747		e.preventDefault();
7748
7749		self.newPoints = pointers( e );
7750
7751		if ( !self.newPoints || !self.newPoints.length ) {
7752			return;
7753		}
7754
7755		self.distanceX = distance( self.newPoints[0], self.startPoints[0], 'x' );
7756		self.distanceY = distance( self.newPoints[0], self.startPoints[0], 'y' );
7757
7758		self.distance = distance( self.newPoints[0], self.startPoints[0] );
7759
7760		// Skip false ontouchmove events (Chrome)
7761		if ( self.distance > 0 ) {
7762
7763			if ( self.isSwiping ) {
7764				self.onSwipe();
7765
7766			} else if ( self.isPanning ) {
7767				self.onPan();
7768
7769			} else if ( self.isZooming ) {
7770				self.onZoom();
7771			}
7772
7773		}
7774
7775	};
7776
7777	Guestures.prototype.onSwipe = function() {
7778
7779		var self = this;
7780
7781		var swiping = self.isSwiping;
7782		var left    = self.sliderStartPos.left;
7783		var angle;
7784
7785		if ( swiping === true ) {
7786
7787			if ( Math.abs( self.distance ) > 10 )  {
7788
7789				if ( self.instance.group.length < 2 ) {
7790					self.isSwiping  = 'y';
7791
7792				} else if ( !self.instance.current.isMoved || self.instance.opts.touch.vertical === false || ( self.instance.opts.touch.vertical === 'auto' && $( window ).width() > 800 ) ) {
7793					self.isSwiping  = 'x';
7794
7795				} else {
7796					angle = Math.abs( Math.atan2( self.distanceY, self.distanceX ) * 180 / Math.PI );
7797
7798					self.isSwiping = ( angle > 45 && angle < 135 ) ? 'y' : 'x';
7799				}
7800
7801				self.canTap  = false;
7802
7803				self.instance.current.isMoved = false;
7804
7805				// Reset points to avoid jumping, because we dropped first swipes to calculate the angle
7806				self.startPoints = self.newPoints;
7807			}
7808
7809		} else {
7810
7811			if ( swiping == 'x' ) {
7812
7813				// Sticky edges
7814				if ( !self.instance.current.opts.loop && self.instance.current.index === 0  && self.distanceX > 0 ) {
7815					left = left + Math.pow( self.distanceX, 0.8 );
7816
7817				} else if ( !self.instance.current.opts.loop &&self.instance.current.index === self.instance.group.length - 1 && self.distanceX < 0 ) {
7818					left = left - Math.pow( -self.distanceX, 0.8 );
7819
7820				} else {
7821					left = left + self.distanceX;
7822				}
7823
7824			}
7825
7826			self.sliderLastPos = {
7827				top  : swiping == 'x' ? 0 : self.sliderStartPos.top + self.distanceY,
7828				left : left
7829			};
7830
7831			requestAFrame(function() {
7832				$.fancybox.setTranslate( self.$slider, self.sliderLastPos );
7833			});
7834		}
7835
7836	};
7837
7838	Guestures.prototype.onPan = function() {
7839
7840		var self = this;
7841
7842		var newOffsetX, newOffsetY, newPos;
7843
7844		self.canTap = false;
7845
7846		if ( self.contentStartPos.width > self.canvasWidth ) {
7847			newOffsetX = self.contentStartPos.left + self.distanceX;
7848
7849		} else {
7850			newOffsetX = self.contentStartPos.left;
7851		}
7852
7853		newOffsetY = self.contentStartPos.top + self.distanceY;
7854
7855		newPos = self.limitMovement( newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height );
7856
7857		newPos.scaleX = self.contentStartPos.scaleX;
7858		newPos.scaleY = self.contentStartPos.scaleY;
7859
7860		self.contentLastPos = newPos;
7861
7862		requestAFrame(function() {
7863			$.fancybox.setTranslate( self.$content, self.contentLastPos );
7864		});
7865	};
7866
7867	// Make panning sticky to the edges
7868	Guestures.prototype.limitMovement = function( newOffsetX, newOffsetY, newWidth, newHeight ) {
7869
7870		var self = this;
7871
7872		var minTranslateX, minTranslateY, maxTranslateX, maxTranslateY;
7873
7874		var canvasWidth  = self.canvasWidth;
7875		var canvasHeight = self.canvasHeight;
7876
7877		var currentOffsetX = self.contentStartPos.left;
7878		var currentOffsetY = self.contentStartPos.top;
7879
7880		var distanceX = self.distanceX;
7881		var distanceY = self.distanceY;
7882
7883		// Slow down proportionally to traveled distance
7884
7885		minTranslateX = Math.max(0, canvasWidth  * 0.5 - newWidth  * 0.5 );
7886		minTranslateY = Math.max(0, canvasHeight * 0.5 - newHeight * 0.5 );
7887
7888		maxTranslateX = Math.min( canvasWidth  - newWidth,  canvasWidth  * 0.5 - newWidth  * 0.5 );
7889		maxTranslateY = Math.min( canvasHeight - newHeight, canvasHeight * 0.5 - newHeight * 0.5 );
7890
7891		if ( newWidth > canvasWidth ) {
7892
7893			//   ->
7894			if ( distanceX > 0 && newOffsetX > minTranslateX ) {
7895				newOffsetX = minTranslateX - 1 + Math.pow( -minTranslateX + currentOffsetX + distanceX, 0.8 ) || 0;
7896			}
7897
7898			//    <-
7899			if ( distanceX  < 0 && newOffsetX < maxTranslateX ) {
7900				newOffsetX = maxTranslateX + 1 - Math.pow( maxTranslateX - currentOffsetX - distanceX, 0.8 ) || 0;
7901			}
7902
7903		}
7904
7905		if ( newHeight > canvasHeight ) {
7906
7907			//   \/
7908			if ( distanceY > 0 && newOffsetY > minTranslateY ) {
7909				newOffsetY = minTranslateY - 1 + Math.pow(-minTranslateY + currentOffsetY + distanceY, 0.8 ) || 0;
7910			}
7911
7912			//   /\
7913			if ( distanceY < 0 && newOffsetY < maxTranslateY ) {
7914				newOffsetY = maxTranslateY + 1 - Math.pow ( maxTranslateY - currentOffsetY - distanceY, 0.8 ) || 0;
7915			}
7916
7917		}
7918
7919		return {
7920			top  : newOffsetY,
7921			left : newOffsetX
7922		};
7923
7924	};
7925
7926
7927	Guestures.prototype.limitPosition = function( newOffsetX, newOffsetY, newWidth, newHeight ) {
7928
7929		var self = this;
7930
7931		var canvasWidth  = self.canvasWidth;
7932		var canvasHeight = self.canvasHeight;
7933
7934		if ( newWidth > canvasWidth ) {
7935			newOffsetX = newOffsetX > 0 ? 0 : newOffsetX;
7936			newOffsetX = newOffsetX < canvasWidth - newWidth ? canvasWidth - newWidth : newOffsetX;
7937
7938		} else {
7939
7940			// Center horizontally
7941			newOffsetX = Math.max( 0, canvasWidth / 2 - newWidth / 2 );
7942
7943		}
7944
7945		if ( newHeight > canvasHeight ) {
7946			newOffsetY = newOffsetY > 0 ? 0 : newOffsetY;
7947			newOffsetY = newOffsetY < canvasHeight - newHeight ? canvasHeight - newHeight : newOffsetY;
7948
7949		} else {
7950
7951			// Center vertically
7952			newOffsetY = Math.max( 0, canvasHeight / 2 - newHeight / 2 );
7953
7954		}
7955
7956		return {
7957			top  : newOffsetY,
7958			left : newOffsetX
7959		};
7960
7961	};
7962
7963	Guestures.prototype.onZoom = function() {
7964
7965		var self = this;
7966
7967		// Calculate current distance between points to get pinch ratio and new width and height
7968
7969		var currentWidth  = self.contentStartPos.width;
7970		var currentHeight = self.contentStartPos.height;
7971
7972		var currentOffsetX = self.contentStartPos.left;
7973		var currentOffsetY = self.contentStartPos.top;
7974
7975		var endDistanceBetweenFingers = distance( self.newPoints[0], self.newPoints[1] );
7976
7977		var pinchRatio = endDistanceBetweenFingers / self.startDistanceBetweenFingers;
7978
7979		var newWidth  = Math.floor( currentWidth  * pinchRatio );
7980		var newHeight = Math.floor( currentHeight * pinchRatio );
7981
7982		// This is the translation due to pinch-zooming
7983		var translateFromZoomingX = (currentWidth  - newWidth)  * self.percentageOfImageAtPinchPointX;
7984		var translateFromZoomingY = (currentHeight - newHeight) * self.percentageOfImageAtPinchPointY;
7985
7986		//Point between the two touches
7987
7988		var centerPointEndX = ((self.newPoints[0].x + self.newPoints[1].x) / 2) - $(window).scrollLeft();
7989		var centerPointEndY = ((self.newPoints[0].y + self.newPoints[1].y) / 2) - $(window).scrollTop();
7990
7991		// And this is the translation due to translation of the centerpoint
7992		// between the two fingers
7993
7994		var translateFromTranslatingX = centerPointEndX - self.centerPointStartX;
7995		var translateFromTranslatingY = centerPointEndY - self.centerPointStartY;
7996
7997		// The new offset is the old/current one plus the total translation
7998
7999		var newOffsetX = currentOffsetX + ( translateFromZoomingX + translateFromTranslatingX );
8000		var newOffsetY = currentOffsetY + ( translateFromZoomingY + translateFromTranslatingY );
8001
8002		var newPos = {
8003			top    : newOffsetY,
8004			left   : newOffsetX,
8005			scaleX : self.contentStartPos.scaleX * pinchRatio,
8006			scaleY : self.contentStartPos.scaleY * pinchRatio
8007		};
8008
8009		self.canTap = false;
8010
8011		self.newWidth  = newWidth;
8012		self.newHeight = newHeight;
8013
8014		self.contentLastPos = newPos;
8015
8016		requestAFrame(function() {
8017			$.fancybox.setTranslate( self.$content, self.contentLastPos );
8018		});
8019
8020	};
8021
8022	Guestures.prototype.ontouchend = function( e ) {
8023
8024		var self = this;
8025
8026		var current = self.instance.current;
8027
8028		var dMs = Math.max( (new Date().getTime() ) - self.startTime, 1);
8029
8030		var swiping = self.isSwiping;
8031		var panning = self.isPanning;
8032		var zooming = self.isZooming;
8033
8034		self.endPoints = pointers( e );
8035
8036		self.$container.removeClass('fancybox-controls--isGrabbing');
8037
8038		self.$wrap.off('touchmove.fb mousemove.fb',  $.proxy(this, "ontouchmove"));
8039		self.$wrap.off('touchend.fb touchcancel.fb mouseup.fb mouseleave.fb',  $.proxy(this, "ontouchend"));
8040
8041		self.isSwiping = false;
8042		self.isPanning = false;
8043		self.isZooming = false;
8044
8045		if ( self.canTap )  {
8046			return self.ontap();
8047		}
8048
8049		// Speed in px/ms
8050		self.velocityX = self.distanceX / dMs * 0.5;
8051		self.velocityY = self.distanceY / dMs * 0.5;
8052
8053		self.speed = current.opts.speed || 330;
8054
8055		self.speedX = Math.max( self.speed * 0.75, Math.min( self.speed * 1.5, ( 1 / Math.abs( self.velocityX ) ) * self.speed ) );
8056		self.speedY = Math.max( self.speed * 0.75, Math.min( self.speed * 1.5, ( 1 / Math.abs( self.velocityY ) ) * self.speed ) );
8057
8058		if ( panning ) {
8059			self.endPanning();
8060
8061		} else if ( zooming ) {
8062			self.endZooming();
8063
8064		} else {
8065			self.endSwiping( swiping );
8066		}
8067
8068		return;
8069	};
8070
8071	Guestures.prototype.endSwiping = function( swiping ) {
8072
8073		var self = this;
8074
8075		// Close if swiped vertically / navigate if horizontally
8076
8077		if ( swiping == 'y' && Math.abs( self.distanceY ) > 50 ) {
8078
8079			// Continue vertical movement
8080
8081			$.fancybox.animate( self.$slider, null, {
8082				top     : self.sliderStartPos.top + self.distanceY + self.velocityY * 150,
8083				left    : self.sliderStartPos.left,
8084				opacity : 0
8085			}, self.speedY );
8086
8087			self.instance.close( true );
8088
8089		} else if ( swiping == 'x' && self.distanceX > 50 ) {
8090			self.instance.previous( self.speedX );
8091
8092		} else if ( swiping == 'x' && self.distanceX < -50 ) {
8093			self.instance.next( self.speedX );
8094
8095		} else {
8096
8097			// Move back to center
8098			self.instance.update( false, true, 150 );
8099
8100		}
8101
8102	};
8103
8104	Guestures.prototype.endPanning = function() {
8105
8106		var self = this;
8107		var newOffsetX, newOffsetY, newPos;
8108
8109		if ( !self.contentLastPos ) {
8110			return;
8111		}
8112
8113		newOffsetX = self.contentLastPos.left + ( self.velocityX * self.speed * 2 );
8114		newOffsetY = self.contentLastPos.top  + ( self.velocityY * self.speed * 2 );
8115
8116		newPos = self.limitPosition( newOffsetX, newOffsetY, self.contentStartPos.width, self.contentStartPos.height );
8117
8118		 newPos.width  = self.contentStartPos.width;
8119		 newPos.height = self.contentStartPos.height;
8120
8121		$.fancybox.animate( self.$content, null, newPos, self.speed, "easeOutSine" );
8122
8123	};
8124
8125
8126	Guestures.prototype.endZooming = function() {
8127
8128		var self = this;
8129
8130		var current = self.instance.current;
8131
8132		var newOffsetX, newOffsetY, newPos, reset;
8133
8134		var newWidth  = self.newWidth;
8135		var newHeight = self.newHeight;
8136
8137		if ( !self.contentLastPos ) {
8138			return;
8139		}
8140
8141		newOffsetX = self.contentLastPos.left;
8142		newOffsetY = self.contentLastPos.top;
8143
8144		reset = {
8145		   	top    : newOffsetY,
8146		   	left   : newOffsetX,
8147		   	width  : newWidth,
8148		   	height : newHeight,
8149			scaleX : 1,
8150			scaleY : 1
8151	   };
8152
8153	   // Reset scalex/scaleY values; this helps for perfomance and does not break animation
8154	   $.fancybox.setTranslate( self.$content, reset );
8155
8156		if ( newWidth < self.canvasWidth && newHeight < self.canvasHeight ) {
8157			self.instance.scaleToFit( 150 );
8158
8159		} else if ( newWidth > current.width || newHeight > current.height ) {
8160			self.instance.scaleToActual( self.centerPointStartX, self.centerPointStartY, 150 );
8161
8162		} else {
8163
8164			newPos = self.limitPosition( newOffsetX, newOffsetY, newWidth, newHeight );
8165
8166			$.fancybox.animate( self.$content, null, newPos, self.speed, "easeOutSine" );
8167
8168		}
8169
8170	};
8171
8172	Guestures.prototype.ontap = function() {
8173
8174		var self = this;
8175
8176		var instance = self.instance;
8177		var current  = instance.current;
8178
8179		var x = self.endPoints[0].x;
8180		var y = self.endPoints[0].y;
8181
8182		x = x - self.$wrap.offset().left;
8183		y = y - self.$wrap.offset().top;
8184
8185		// Stop slideshow
8186		if ( instance.SlideShow && instance.SlideShow.isActive ) {
8187			instance.SlideShow.stop();
8188		}
8189
8190		if ( !$.fancybox.isTouch ) {
8191
8192			if ( current.opts.closeClickOutside && self.$target.is('.fancybox-slide') ) {
8193				instance.close( self.startEvent );
8194
8195				return;
8196			}
8197
8198			if ( current.type == 'image' && current.isMoved ) {
8199
8200				if ( instance.canPan() ) {
8201					instance.scaleToFit();
8202
8203				} else if ( instance.isScaledDown() ) {
8204					instance.scaleToActual( x, y );
8205
8206				} else if ( instance.group.length < 2 ) {
8207					instance.close( self.startEvent );
8208				}
8209
8210			}
8211
8212			return;
8213		}
8214
8215
8216		// Double tap
8217		if ( self.tapped ) {
8218
8219			clearTimeout( self.tapped );
8220
8221			self.tapped = null;
8222
8223			if ( Math.abs( x - self.x ) > 50 || Math.abs( y - self.y ) > 50 || !current.isMoved ) {
8224				return this;
8225			}
8226
8227			if ( current.type == 'image' && ( current.isLoaded || current.$ghost ) ) {
8228
8229				if ( instance.canPan() ) {
8230					instance.scaleToFit();
8231
8232				} else if ( instance.isScaledDown() ) {
8233					instance.scaleToActual( x, y );
8234
8235				}
8236
8237			}
8238
8239		} else {
8240
8241			// Single tap
8242
8243			self.x = x;
8244			self.y = y;
8245
8246			self.tapped = setTimeout(function() {
8247				self.tapped = null;
8248
8249				instance.toggleControls( true );
8250
8251			}, 300);
8252		}
8253
8254		return this;
8255	};
8256
8257	$(document).on('onActivate.fb', function (e, instance) {
8258
8259		if ( instance && !instance.Guestures ) {
8260			instance.Guestures = new Guestures( instance );
8261		}
8262
8263	});
8264
8265	$(document).on('beforeClose.fb', function (e, instance) {
8266
8267		if ( instance && instance.Guestures ) {
8268			instance.Guestures.destroy();
8269		}
8270
8271	});
8272
8273
8274}(window, document, window.jQuery));
8275
8276// ==========================================================================
8277//
8278// SlideShow
8279// Enables slideshow functionality
8280//
8281// Example of usage:
8282// $.fancybox.getInstance().slideShow.start()
8283//
8284// ==========================================================================
8285;(function (document, $) {
8286	'use strict';
8287
8288	var SlideShow = function( instance ) {
8289
8290		this.instance = instance;
8291
8292		this.init();
8293
8294	};
8295
8296	$.extend( SlideShow.prototype, {
8297		timer    : null,
8298		isActive : false,
8299		$button  : null,
8300		speed    : 3000,
8301
8302		init : function() {
8303			var self = this;
8304
8305			self.$button = $('<button data-fancybox-play class="fancybox-button fancybox-button--play" title="Slideshow (P)"></button>')
8306				.appendTo( self.instance.$refs.buttons );
8307
8308			self.instance.$refs.container.on('click', '[data-fancybox-play]', function() {
8309				self.toggle();
8310			});
8311
8312		},
8313
8314		set : function() {
8315			var self = this;
8316
8317			// Check if reached last element
8318			if ( self.instance && self.instance.current && (self.instance.current.opts.loop || self.instance.currIndex < self.instance.group.length - 1 )) {
8319
8320				self.timer = setTimeout(function() {
8321					self.instance.next();
8322
8323				}, self.instance.current.opts.slideShow.speed || self.speed);
8324
8325			} else {
8326				self.stop();
8327			}
8328		},
8329
8330		clear : function() {
8331			var self = this;
8332
8333			clearTimeout( self.timer );
8334
8335			self.timer = null;
8336		},
8337
8338		start : function() {
8339			var self = this;
8340
8341			self.stop();
8342
8343			if ( self.instance && self.instance.current && ( self.instance.current.opts.loop || self.instance.currIndex < self.instance.group.length - 1 )) {
8344
8345				self.instance.$refs.container.on({
8346					'beforeLoad.fb.player'	: $.proxy(self, "clear"),
8347					'onComplete.fb.player'	: $.proxy(self, "set"),
8348				});
8349
8350				self.isActive = true;
8351
8352				if ( self.instance.current.isComplete ) {
8353					self.set();
8354				}
8355
8356				self.instance.$refs.container.trigger('onPlayStart');
8357
8358				self.$button.addClass('fancybox-button--pause');
8359			}
8360
8361		},
8362
8363		stop: function() {
8364			var self = this;
8365
8366			self.clear();
8367
8368			self.instance.$refs.container
8369				.trigger('onPlayEnd')
8370				.off('.player');
8371
8372			self.$button.removeClass('fancybox-button--pause');
8373
8374			self.isActive = false;
8375		},
8376
8377		toggle : function() {
8378			var self = this;
8379
8380			if ( self.isActive ) {
8381				self.stop();
8382
8383			} else {
8384				self.start();
8385			}
8386		}
8387
8388	});
8389
8390	$(document).on('onInit.fb', function(e, instance) {
8391
8392		if ( instance && instance.group.length > 1 && !!instance.opts.slideShow && !instance.SlideShow ) {
8393			instance.SlideShow = new SlideShow( instance );
8394		}
8395
8396	});
8397
8398	$(document).on('beforeClose.fb onDeactivate.fb', function(e, instance) {
8399
8400		if ( instance && instance.SlideShow ) {
8401			instance.SlideShow.stop();
8402		}
8403
8404	});
8405
8406}(document, window.jQuery));
8407
8408// ==========================================================================
8409//
8410// FullScreen
8411// Adds fullscreen functionality
8412//
8413// ==========================================================================
8414;(function (document, $) {
8415	'use strict';
8416
8417	// Collection of methods supported by user browser
8418	var fn = (function () {
8419
8420		var fnMap = [
8421			[
8422				'requestFullscreen',
8423				'exitFullscreen',
8424				'fullscreenElement',
8425				'fullscreenEnabled',
8426				'fullscreenchange',
8427				'fullscreenerror'
8428			],
8429			// new WebKit
8430			[
8431				'webkitRequestFullscreen',
8432				'webkitExitFullscreen',
8433				'webkitFullscreenElement',
8434				'webkitFullscreenEnabled',
8435				'webkitfullscreenchange',
8436				'webkitfullscreenerror'
8437
8438			],
8439			// old WebKit (Safari 5.1)
8440			[
8441				'webkitRequestFullScreen',
8442				'webkitCancelFullScreen',
8443				'webkitCurrentFullScreenElement',
8444				'webkitCancelFullScreen',
8445				'webkitfullscreenchange',
8446				'webkitfullscreenerror'
8447
8448			],
8449			[
8450				'mozRequestFullScreen',
8451				'mozCancelFullScreen',
8452				'mozFullScreenElement',
8453				'mozFullScreenEnabled',
8454				'mozfullscreenchange',
8455				'mozfullscreenerror'
8456			],
8457			[
8458				'msRequestFullscreen',
8459				'msExitFullscreen',
8460				'msFullscreenElement',
8461				'msFullscreenEnabled',
8462				'MSFullscreenChange',
8463				'MSFullscreenError'
8464			]
8465		];
8466
8467		var val;
8468		var ret = {};
8469		var i, j;
8470
8471		for ( i = 0; i < fnMap.length; i++ ) {
8472			val = fnMap[ i ];
8473
8474			if ( val && val[ 1 ] in document ) {
8475				for ( j = 0; j < val.length; j++ ) {
8476					ret[ fnMap[ 0 ][ j ] ] = val[ j ];
8477				}
8478
8479				return ret;
8480			}
8481		}
8482
8483		return false;
8484	})();
8485
8486	if ( !fn ) {
8487		return;
8488	}
8489
8490	var FullScreen = {
8491		request : function ( elem ) {
8492
8493			elem = elem || document.documentElement;
8494
8495			elem[ fn.requestFullscreen ]( elem.ALLOW_KEYBOARD_INPUT );
8496
8497		},
8498		exit : function () {
8499			document[ fn.exitFullscreen ]();
8500		},
8501		toggle : function ( elem ) {
8502
8503			if ( this.isFullscreen() ) {
8504				this.exit();
8505			} else {
8506				this.request( elem );
8507			}
8508
8509		},
8510		isFullscreen : function()  {
8511			return Boolean( document[ fn.fullscreenElement ] );
8512		},
8513		enabled : function()  {
8514			return Boolean( document[ fn.fullscreenEnabled ] );
8515		}
8516	};
8517
8518	$(document).on({
8519		'onInit.fb' : function(e, instance) {
8520			var $container;
8521
8522			if ( instance && !!instance.opts.fullScreen && !instance.FullScreen) {
8523				$container = instance.$refs.container;
8524
8525				instance.$refs.button_fs = $('<button data-fancybox-fullscreen class="fancybox-button fancybox-button--fullscreen" title="Full screen (F)"></button>')
8526					.appendTo( instance.$refs.buttons );
8527
8528				$container.on('click.fb-fullscreen', '[data-fancybox-fullscreen]', function(e) {
8529
8530					e.stopPropagation();
8531					e.preventDefault();
8532
8533					FullScreen.toggle( $container[ 0 ] );
8534
8535				});
8536
8537				if ( instance.opts.fullScreen.requestOnStart === true ) {
8538					FullScreen.request( $container[ 0 ] );
8539				}
8540
8541			}
8542
8543		}, 'beforeMove.fb' : function(e, instance) {
8544
8545			if ( instance && instance.$refs.button_fs ) {
8546				instance.$refs.button_fs.toggle( !!instance.current.opts.fullScreen );
8547			}
8548
8549		}, 'beforeClose.fb':  function() {
8550			FullScreen.exit();
8551		}
8552	});
8553
8554	$(document).on(fn.fullscreenchange, function() {
8555		var instance = $.fancybox.getInstance();
8556		var  $what   = instance ? instance.current.$placeholder : null;
8557
8558		if ( $what ) {
8559
8560			// If image is zooming, then this will force it to stop and reposition properly
8561			$what.css( 'transition', 'none' );
8562
8563			instance.isAnimating = false;
8564
8565			instance.update( true, true, 0 );
8566		}
8567
8568	});
8569
8570}(document, window.jQuery));
8571
8572// ==========================================================================
8573//
8574// Thumbs
8575// Displays thumbnails in a grid
8576//
8577// ==========================================================================
8578;(function (document, $) {
8579	'use strict';
8580
8581	var FancyThumbs = function( instance ) {
8582
8583		this.instance = instance;
8584
8585		this.init();
8586
8587	};
8588
8589	$.extend( FancyThumbs.prototype, {
8590
8591		$button		: null,
8592		$grid		: null,
8593		$list		: null,
8594		isVisible	: false,
8595
8596		init : function() {
8597			var self = this;
8598
8599			self.$button = $('<button data-fancybox-thumbs class="fancybox-button fancybox-button--thumbs" title="Thumbnails (G)"></button>')
8600				.appendTo( this.instance.$refs.buttons )
8601				.on('touchend click', function(e) {
8602					e.stopPropagation();
8603					e.preventDefault();
8604
8605					self.toggle();
8606				});
8607
8608		},
8609
8610		create : function() {
8611			var instance = this.instance,
8612				list,
8613				src;
8614
8615			this.$grid = $('<div class="fancybox-thumbs"></div>').appendTo( instance.$refs.container );
8616
8617			list = '<ul>';
8618
8619			$.each(instance.group, function( i, item ) {
8620
8621				src = item.opts.thumb || ( item.opts.$thumb ? item.opts.$thumb.attr('src') : null );
8622
8623				if ( !src && item.type === 'image' ) {
8624					src = item.src;
8625				}
8626
8627				if ( src && src.length ) {
8628					list += '<li data-index="' + i + '"  tabindex="0" class="fancybox-thumbs-loading"><img data-src="' + src + '" /></li>';
8629				}
8630
8631			});
8632
8633			list += '</ul>';
8634
8635			this.$list = $( list ).appendTo( this.$grid ).on('click touchstart', 'li', function() {
8636
8637				instance.jumpTo( $(this).data('index') );
8638
8639			});
8640
8641			this.$list.find('img').hide().one('load', function() {
8642
8643				var $parent		= $(this).parent().removeClass('fancybox-thumbs-loading'),
8644					thumbWidth	= $parent.outerWidth(),
8645					thumbHeight	= $parent.outerHeight(),
8646					width,
8647					height,
8648					widthRatio,
8649					heightRatio;
8650
8651				width  = this.naturalWidth	|| this.width;
8652				height = this.naturalHeight	|| this.height;
8653
8654				//Calculate thumbnail width/height and center it
8655
8656				widthRatio  = width  / thumbWidth;
8657				heightRatio = height / thumbHeight;
8658
8659				if (widthRatio >= 1 && heightRatio >= 1) {
8660					if (widthRatio > heightRatio) {
8661						width  = width / heightRatio;
8662						height = thumbHeight;
8663
8664					} else {
8665						width  = thumbWidth;
8666						height = height / widthRatio;
8667					}
8668				}
8669
8670				$(this).css({
8671					width         : Math.floor(width),
8672					height        : Math.floor(height),
8673					'margin-top'  : Math.min( 0, Math.floor(thumbHeight * 0.3 - he
8673ight * 0.3 ) ),
8674					'margin-left' : Math.min( 0, Math.floor(thumbWidth  * 0.5 - width  * 0.5 ) )
8675				}).show();
8676
8677			})
8678			.each(function() {
8679				this.src = $( this ).data( 'src' );
8680			});
8681
8682		},
8683
8684		focus : function() {
8685
8686			if ( this.instance.current ) {
8687				this.$list
8688					.children()
8689					.removeClass('fancybox-thumbs-active')
8690					.filter('[data-index="' + this.instance.current.index  + '"]')
8691					.addClass('fancybox-thumbs-active')
8692					.focus();
8693			}
8694
8695		},
8696
8697		close : function() {
8698
8699			this.$grid.hide();
8700
8701		},
8702
8703		update : function() {
8704
8705			this.instance.$refs.container.toggleClass('fancybox-container--thumbs', this.isVisible);
8706
8707			if ( this.isVisible ) {
8708
8709				if ( !this.$grid ) {
8710					this.create();
8711				}
8712
8713				this.$grid.show();
8714
8715				this.focus();
8716
8717			} else if ( this.$grid ) {
8718				this.$grid.hide();
8719			}
8720
8721			this.instance.update();
8722
8723		},
8724
8725		hide : function() {
8726
8727			this.isVisible = false;
8728
8729			this.update();
8730
8731		},
8732
8733		show : function() {
8734
8735			this.isVisible = true;
8736
8737			this.update();
8738
8739		},
8740
8741		toggle : function() {
8742
8743			if ( this.isVisible ) {
8744				this.hide();
8745
8746			} else {
8747				this.show();
8748			}
8749		}
8750
8751	});
8752
8753	$(document).on('onInit.fb', function(e, instance) {
8754		var first  = instance.group[0],
8755			second = instance.group[1];
8756
8757		if ( !!instance.opts.thumbs && !instance.Thumbs && instance.group.length > 1 && (
8758		    		( first.type == 'image'  || first.opts.thumb  || first.opts.$thumb ) &&
8759		    		( second.type == 'image' || second.opts.thumb || second.opts.$thumb )
8760			 	)
8761		   ) {
8762
8763			instance.Thumbs = new FancyThumbs( instance );
8764		}
8765
8766	});
8767
8768	$(document).on('beforeMove.fb', function(e, instance, item) {
8769		var self = instance && instance.Thumbs;
8770
8771		if ( !self ) {
8772			return;
8773		}
8774
8775		if ( item.modal ) {
8776
8777			self.$button.hide();
8778
8779			self.hide();
8780
8781		} else {
8782
8783			if ( instance.opts.thumbs.showOnStart === true && instance.firstRun ) {
8784				self.show();
8785
8786			}
8787
8788			self.$button.show();
8789
8790			if ( self.isVisible ) {
8791				self.focus();
8792			}
8793
8794		}
8795
8796	});
8797
8798	$(document).on('beforeClose.fb', function(e, instance) {
8799
8800		if ( instance && instance.Thumbs) {
8801			if ( instance.Thumbs.isVisible && instance.opts.thumbs.hideOnClosing !== false ) {
8802				instance.Thumbs.close();
8803			}
8804
8805			instance.Thumbs = null;
8806		}
8807
8808	});
8809
8810}(document, window.jQuery));
8811
8812// ==========================================================================
8813//
8814// Hash
8815// Enables linking to each modal
8816//
8817// ==========================================================================
8818;(function (document, window, $) {
8819	'use strict';
8820
8821	// Simple $.escapeSelector polyfill (for jQuery prior v3)
8822	if ( !$.escapeSelector ) {
8823		$.escapeSelector = function( sel ) {
8824			var rcssescape = /([\0-\x1f\x7f]|^-?\d)|^-$|[^\x80-\uFFFF\w-]/g;
8825			var fcssescape = function( ch, asCodePoint ) {
8826				if ( asCodePoint ) {
8827					// U+0000 NULL becomes U+FFFD REPLACEMENT CHARACTER
8828					if ( ch === "\0" ) {
8829						return "\uFFFD";
8830					}
8831
8832					// Control characters and (dependent upon position) numbers get escaped as code points
8833					return ch.slice( 0, -1 ) + "\\" + ch.charCodeAt( ch.length - 1 ).toString( 16 ) + " ";
8834				}
8835
8836				// Other potentially-special ASCII characters get backslash-escaped
8837				return "\\" + ch;
8838			};
8839
8840			return ( sel + "" ).replace( rcssescape, fcssescape );
8841		};
8842	}
8843
8844	// Variable containing last hash value set by fancyBox
8845	// It will be used to determine if fancyBox needs to close after hash change is detected
8846    var currentHash = null;
8847
8848	// Get info about gallery name and current index from url
8849    function parseUrl() {
8850        var hash    = window.location.hash.substr( 1 );
8851        var rez     = hash.split( '-' );
8852        var index   = rez.length > 1 && /^\+?\d+$/.test( rez[ rez.length - 1 ] ) ? parseInt( rez.pop( -1 ), 10 ) || 1 : 1;
8853        var gallery = rez.join( '-' );
8854
8855		// Index is starting from 1
8856		if ( index < 1 ) {
8857			index = 1;
8858		}
8859
8860        return {
8861            hash    : hash,
8862            index   : index,
8863            gallery : gallery
8864        };
8865    }
8866
8867	// Trigger click evnt on links to open new fancyBox instance
8868	function triggerFromUrl( url ) {
8869		var $el;
8870
8871        if ( url.gallery !== '' ) {
8872
8873			// If we can find element matching 'data-fancybox' atribute, then trigger click event for that ..
8874			$el = $( "[data-fancybox='" + $.escapeSelector( url.gallery ) + "']" ).eq( url.index - 1 );
8875
8876            if ( $el.length ) {
8877				$el.trigger( 'click' );
8878
8879			} else {
8880
8881				// .. if not, try finding element by ID
8882				$( "#" + $.escapeSelector( url.gallery ) + "" ).trigger( 'click' );
8883
8884			}
8885
8886        }
8887	}
8888
8889	// Get gallery name from current instance
8890	function getGallery( instance ) {
8891		var opts;
8892
8893		if ( !instance ) {
8894			return false;
8895		}
8896
8897		opts = instance.current ? instance.current.opts : instance.opts;
8898
8899		return opts.$orig ? opts.$orig.data( 'fancybox' ) : ( opts.hash || '' );
8900	}
8901
8902	// Star when DOM becomes ready
8903    $(function() {
8904
8905		// Small delay is used to allow other scripts to process "dom ready" event
8906		setTimeout(function() {
8907
8908			// Check if this module is not disabled
8909			if ( $.fancybox.defaults.hash === false ) {
8910				return;
8911			}
8912
8913			// Check if need to close after url has changed
8914		    $(window).on('hashchange.fb', function() {
8915		        var url = parseUrl();
8916
8917				if ( $.fancybox.getInstance() ) {
8918					if ( currentHash && currentHash !== url.gallery + '-' + url.index )  {
8919						currentHash = null;
8920
8921						$.fancybox.close();
8922					}
8923
8924				} else if ( url.gallery !== '' ) {
8925		            triggerFromUrl( url );
8926		        }
8927
8928		    });
8929
8930			// Update hash when opening/closing fancyBox
8931		    $(document).on({
8932				'onInit.fb' : function( e, instance ) {
8933					var url     = parseUrl();
8934					var gallery = getGallery( instance );
8935
8936					// Make sure gallery start index matches index from hash
8937					if ( gallery && url.gallery && gallery == url.gallery ) {
8938						instance.currIndex = url.index - 1;
8939					}
8940
8941				}, 'beforeMove.fb' : function( e, instance, current ) {
8942		            var gallery = getGallery( instance );
8943
8944		            // Update window hash
8945		            if ( gallery && gallery !== '' ) {
8946
8947						if ( window.location.hash.indexOf( gallery ) < 0 ) {
8948			                instance.opts.origHash = window.location.hash;
8949			            }
8950
8951						currentHash = gallery + ( instance.group.length > 1 ? '-' + ( current.index + 1 ) : '' );
8952
8953						if ( "pushState" in history ) {
8954		                    history.pushState( '', document.title, window.location.pathname + window.location.search + '#' +  currentHash );
8955
8956						} else {
8957							window.location.hash = currentHash;
8958						}
8959
8960		            }
8961
8962		        }, 'beforeClose.fb' : function( e, instance, current ) {
8963					var gallery  = getGallery( instance );
8964					var origHash = instance && instance.opts.origHash ? instance.opts.origHash : '';
8965
8966		            // Remove hash from location bar
8967		            if ( gallery && gallery !== '' ) {
8968		                if ( "pushState" in history ) {
8969		                    history.pushState( '', document.title, window.location.pathname + window.location.search + origHash );
8970
8971		                } else {
8972		                    window.location.hash = origHash;
8973		                }
8974		            }
8975
8976					currentHash = null;
8977		        }
8978		    });
8979
8980			// Check current hash and trigger click event on matching element to start fancyBox, if needed
8981			triggerFromUrl( parseUrl() );
8982
8983		}, 50);
8984    });
8985
8986
8987}(document, window, window.jQuery));
8988
8989jQuery(document).foundation();
8990/* 
8991These functions make sure WordPress 
8992and Foundation play nice together.
8993*/
8994
8995jQuery(document).ready(function() {
8996  	initFlickity();
8997  	siteJsEssentials();
8998  	initParallax();
8999  	coolColorOverLay();
9000  	initHpVideo();
9001	checkVideo();
9002	initScroller();	
9003	initGalleries();
9004	//formLabel2Placeholder();
9005	cf7fix();
9006	initPreloaderScreen();
9007	initAccordionScripts();
9008});
9009
9010jQuery(window).resize(function() {	
9011	navCheck();
9012});
9013
9014jQuery(window).scroll(function() { 
9015	navCheck();
9016	
9017	 jQuery('.fadeInBlock').each( function(i){        
9018        var bottom_of_object = jQuery(this).position().top + jQuery(this).outerHeight();
9019        var bottom_of_window = jQuery(window).scrollTop() + jQuery(window).height();
9020        
9021        /* Adjust the "200" to either have a delay or that the content starts fading a bit before you reach it  */
9022        bottom_of_window = bottom_of_window + 500;  
9023      
9024        if( bottom_of_window > bottom_of_object ){            
9025            jQuery(this).animate({'opacity':'1'},800);                
9026        }
9027    }); 
9028	
9029});
9030
9031jQuery(window).on('changed.zf.mediaquery', function(event, newSize, oldSize) {
9032  // newSize is the name of the now-current breakpoint, oldSize is the previous breakpoint  
9033
9034});
9035
9036
9037
9038function initAccordionScripts() {
9039
9040		jQuery("ul.gdl-accordion li").each(function(){
9041	
9042	
9043	jQuery(this).children(".accordion-head").bind("click", function(){
9044			jQuery(this).toggleClass(function(){
9045			if(jQuery(this).is("active")) return "";
9046				return "active";
9047			});
9048			
9049			jQuery(this).siblings(".accordion-content").slideToggle();
9050			jQuery(this).parent().siblings("li").children(".accordion-content").slideUp();
9051			jQuery(this).parent().siblings("li").children(".accordion-head").removeClass("active");
9052		});
9053	});
9054}
9055
9056	
9057
9058
9059// dont forget to put the CSS
9060// form.form label { display: none; }
9061//
9062function formLabel2Placeholder() {
9063    jQuery("form :input").each(function(index, elem) {
9064        var eId = jQuery(elem).attr("id");
9065        var label = null;
9066        if (eId && (label =  jQuery(elem).parents("form").find("label[for="+eId+"]")).length == 1) {
9067             jQuery(elem).attr("placeholder",  jQuery(label).html());
9068            jQuery(label).remove();
9069        }
9070    });
9071 }
9072
9073function initGalleries(){
9074	jQuery('.imgGalItem').click(function() {
9075		event.preventDefault();		
9076		var link = jQuery(this).attr('href');
9077		var rel  = jQuery(this).attr('rel');
9078		var counts = jQuery(this).attr('data-groups');			
9079		var newContent = '<a href="'+link+'" rel="galSlideCell'+rel+'" data-fancybox="group'+counts+'" data-caption="*Individual Results May Vary"><img src="'+link+'"></a> ';
9080		
9081		jQuery(".photoGallery"+rel).removeClass("is-active");
9082		jQuery(this).addClass("is-active");		
9083				
9084		jQuery(".galSlideCell"+rel).stop().css('opacity', '0').html(function () {
9085		 		return newContent;
9086		    }).animate({
9087		        opacity: 1
9088		    }, 400);			
9089		
9090	});
9091	
9092	jQuery( "#listView" ).click(function() {
9093      event.preventDefault();		  
9094	  jQuery(this).addClass("active");
9095	  jQuery( "#gridView" ).removeClass("active"); 	  
9096	  jQuery( ".imgWrapSub" ).removeClass( "toggleHide" );
9097	});
9098	
9099	jQuery( "#gridView" ).click(function() {
9100	  event.preventDefault();	
9101	  jQuery(this).addClass("active");
9102	  jQuery( "#listView" ).removeClass("active"); 
9103	  jQuery( ".imgWrapSub" ).addClass( "toggleHide" );
9104	});
9105	
9106	
9107	jQuery('select#mobile-sorting').on('change', function() {
9108	  window.location.replace(this.value);
9109	});
9110	
9111}
9112
9113function cf7fix() {
9114	jQuery('input.wpcf7-submit').click(function() { 			
9115			setTimeout(function(){
9116				jQuery('input.wpcf7-submit').attr('disabled','disabled');
9117			}, 100);			
9118		});
9119
9120		jQuery(".wpcf7").on('wpcf7:submit', function(event){
9121			jQuery(this).find('input.wpcf7-submit').removeAttr('disabled');
9122		});
9123
9124}
9125
9126function initSubmenus() {
9127	jQuery('.top-bar #menu-topnav-1').fadeIn('slow').css('display','inline-block');	
9128	jQuery('#head-bar-menu #head-content').addClass('animated fadeInDown');
9129	
9130}
9131
9132function initPreloaderScreen() {
9133	setTimeout(function(){
9134		jQuery('.home #loader').addClass('animated zoomOut');
9135	}, 800);
9136	setTimeout(function(){
9137		
9138		jQuery('.home #loader-wrapper').fadeOut('slow');	
9139		jQuery('.home .off-canvas-wrapper').css('opacity', 1);
9140	}, 1200);
9141	
9142	var fpBannerLogo = jQuery('.slide-logo');
9143	var fpButton = jQuery('.fp-banner-link');
9144	var fpfixedlogo = jQuery('.box .button');
9145	if(fpBannerLogo){
9146		fpBannerLogo.css('opacity',0);
9147		setTimeout(function(){
9148			fpBannerLogo.css('opacity',1);
9149			fpBannerLogo.addClass('animated zoomIn');			 
9150		}, 1400);
9151	}
9152	if(fpfixedlogo) {
9153		setTimeout(function(){ 
9154			fpfixedlogo.addClass('animated fadeIn');
9155		}, 1400);
9156	}
9157	
9158	if(fpButton) {
9159		fpButton.css('opacity',0);
9160		setTimeout(function(){
9161				fpButton.css('opacity',1);			
9162			fpButton.addClass('animated fadeIn');
9163			initSubmenus();
9164		}, 1300);
9165	}
9166
9167}
9168
9169
9170// CHECKS FOR STATIC / FIXED NAVIGATION
9171function navCheck(){
9172		var isDesktop = jQuery(document).width() > 1024? true : false; 
9173		// If document (your website) is wider than 1023px, notVisible = true, else notVisible = false
9174		if(!isDesktop) {
9175		   jQuery('#menu-bar-wrapper').removeClass('swipeup'); 
9176		   jQuery('#menu-bar-wrapper').removeClass('fixed');	
9177		} else { desktopNavToggle();  }
9178}
9179
9180var toggler = false;
9181var previousScroll = 0;
9182function desktopNavToggle() {
9183	var offset = jQuery('#menu-bar-wrapper').offset();
9184    
9185    if(offset) {
9186	    var height = jQuery(window).scrollTop();		
9187		var currentScroll = jQuery(window).scrollTop();		
9188
9189       if (currentScroll < previousScroll && toggler === true && height  < 95 && offset.top < 145){                   
9190		   jQuery('.home #menu-bar-wrapper').addClass('swipeup');		  
9191		   toggler = false;	
9192		
9193       }
9194       previousScroll = currentScroll;
9195		
9196	
9197	   if(height  < 89 && offset.top < 140) {			
9198			toggler = false;		
9199	    } else {
9200			toggler = true;
9201			jQuery('.home #menu-bar-wrapper').addClass('fixed').removeClass('swipeup');
9202			jQuery('.home .menu-wrapper ').addClass('small-12').removeClass('small-10');			
9203	    }
9204		if ( currentScroll < 30 ) { 
9205		   jQuery('.home #menu-bar-wrapper').removeClass('swipeup').removeClass('fixed'); 
9206		   jQuery('.home .menu-wrapper ').addClass('small-10').removeClass('small-12');
9207		}
9208	   
9209    }
9210}	
9211
9212// CUSTOM SLIDER WIDGET SCRIPT
9213function initFlickity() {
9214	jQuery('.sbReviewsSlider').flickity({
9215	  // options
9216	  cellAlign: 'left',
9217	  contain: true,
9218	  autoPlay: 3500,
9219	  wrapAround: true,
9220	  pageDots: false,
9221	  prevNextButtons: false,
9222	  imagesLoaded: true,
9223	  cellSelector: ".wSlide",
9224	  arrowShape: { 
9225		  x0: 10,
9226		  x1: 70, y1: 40,
9227		  x2: 70, y2: 40,
9228		  x3: 70
9229		}
9230	});
9231	
9232	
9233	var servDiv = jQuery('#services-gallery');
9234	if(servDiv){	
9235		var $servCarousel;
9236	 	 	
9237		$servCarousel = servDiv.flickity({
9238		  // options
9239		  cellAlign: 'center',
9240		  contain: true,
9241		  autoPlay: 15000,
9242		  wrapAround:true,
9243		  pageDots: true,
9244		  selectedAttraction: 0.008,
9245		  friction: 0.12,	
9246		  imagesLoaded: true,
9247		  prevNextButtons:true,
9248		  arrowShape: 'M76.854.8,99.207,23.152a2.709,2.709,0,0,1,0,3.831L76.854,49.335A2.709,2.709,0,0,1,72.23,47.42V31.387H2.709A2.709,2.709,0,0,1,0,28.678V21.455a2.709,2.709,0,0,1,2.709-2.709H72.23V2.714A2.709,2.709,0,0,1,76.854.8Z',		
9249		  //watchCSS: 'fallbackOn'	
9250		});		
9251	}
9252	
9253		var revDiv = jQuery('#review-slider');
9254	if(revDiv){	
9255		var $revCarousel;
9256	 	 	
9257		$revCarousel = revDiv.flickity({
9258		  // options
9259		  cellAlign: 'center',
9260		  contain: true,
9261		  autoPlay: 15000,
9262		  wrapAround:true,
9263		  pageDots: true,
9264		  selectedAttraction: 0.008,
9265		  friction: 0.12,	
9266		  imagesLoaded: true,
9267		  prevNextButtons:true,
9268		  arrowShape: 'M76.854.8,99.207,23.152a2.709,2.709,0,0,1,0,3.831L76.854,49.335A2.709,2.709,0,0,1,72.23,47.42V31.387H2.709A2.709,2.709,0,0,1,0,28.678V21.455a2.709,2.709,0,0,1,2.709-2.709H72.23V2.714A2.709,2.709,0,0,1,76.854.8Z',
9269		    	
9270		  //watchCSS: 'fallbackOn'	
9271		});		
9272	}		
9273}
9274
9275function initParallax() {		  
9276  
9277	  // use data-fptop to set the default starting point of the bg image
9278	  // eg. <div data-fptop="100" style="background-image:url('somebg.jpg')></div>
9279	  
9280	  var parallax = document.querySelectorAll(".fpparallax"),
9281	      speed = 0.4;
9282		
9283	  window.onscroll = function(){
9284	    [].slice.call(parallax).forEach(function(el,i){
9285		  
9286		  var w = window.innerWidth;
9287		  var mediaQueryVal = "";
9288		  var ParaOffset = el.offsetTop;	
9289		 
9290		  if(el.getAttribute('data-speed')) {			  
9291			  speed = el.getAttribute('data-speed');
9292		  }  
9293		  
9294		  if(w > 1024){ mediaQueryVal = el.getAttribute('data-fp-large'); }
9295		  else if (w <= 1024 && w > 640) { mediaQueryVal = el.getAttribute('data-fp-medium'); }
9296		  else if (w <= 640 && w > 420) { mediaQueryVal = el.getAttribute('data-fp-small'); }
9297		  else if (w <= 420) { mediaQueryVal = el.getAttribute('data-fp-xsmall'); }	  
9298		  
9299	      var windowYOffset = window.pageYOffset,
9300	      minusTop = mediaQueryVal ,
9301	         //  elBackgrounPosVanilla = "50% " + (windowYOffset * speed) + "px";
9302			  elBackgrounPos = "50% " + (((windowYOffset - ParaOffset) * speed) - minusTop) + "px";
9303	      el.style.backgroundPosition = elBackgrounPos;	
9304	    });
9305	  };
9306}
9307
9308function siteJsEssentials() {
9309	  // Remove empty P tags created by WP inside of Accordion and Orbit
9310    jQuery('.accordion p:empty, .orbit p:empty').remove();
9311    
9312	 // Makes sure last grid item floats left
9313	jQuery('.archive-grid .columns').last().addClass( 'end' );
9314	
9315	// Emma Form Tracking
9316	jQuery('#emma-form-submit').addClass('emmaTrackingLink');
9317	
9318	// Adds Flex Video to YouTube and Vimeo Embeds
9319	jQuery('iframe[src*="youtube.com"], iframe[src*="vimeo.com"]').wrap("<div class='flex-vi
9319deo responsive-embed widescreen'/>");
9320	//console.log('hello dev team!');
9321	
9322	jQuery( ".ginput_complex.ginput_container span" ).wrap( "<div class='theGcontent'></div>" );
9323
9324}
9325
9326// VIDEO FOR FRONT PAGE
9327function initHpVideo() {		
9328			var isTouchDevice = /Windows Phone/.test(navigator.userAgent) || ('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch;
9329	jQuery('.promo .video').each(function() {
9330		var holder = jQuery(this);
9331		var video = holder.find('video');
9332		var isDesktop = jQuery(document).width() < 1025 ? true : false; 
9333		
9334		if (isTouchDevice && isDesktop) {
9335			video.remove();
9336			jQuery(".promo").addClass("mobile");
9337			jQuery(".promo").removeClass("desktop");
9338		} else {
9339			jQuery(".promo").addClass("desktop");
9340		}
9341	});
9342}
9343
9344function checkVideo(){
9345var vid = document.getElementById("fpVideo");
9346
9347	if (vid) {
9348		vid.onloadedmetadata = function() {   
9349		    jQuery('.loader').fadeOut('slow');
9350		};
9351	}
9352}
9353
9354function initScroller(){
9355	
9356	jQuery('.scrollink').click(function() { 
9357		if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
9358		var target = jQuery(this.hash); 
9359		target = target.length ? target : jQuery('[name=' + this.hash.slice(1) +']');
9360		if (target.length) {
9361			jQuery('html,body').animate({
9362			scrollTop: target.offset().top-40
9363			}, 1000);				
9364			return false;
9365			}
9366		}
9367	});
9368
9369}
9370
9371//  coolColorOverlay Hover box 
9372//  this is dependent on /scss/sections/plugins/cool-color.scss (include it via style.scss)
9373/*
9374	- to add coolColorOverlay to and element in your templates add the class .coolColor 
9375	the js will look for a link with a class of .button to act as the trigger that will fade the background color when hovered.
9376	
9377	- if you would like to fade out any child elements with the color add a class of .unThis to them.  
9378	
9379	- Available data attributes for coolColorOverlay  
9380	
9381	data-cool-this="1" 
9382	this allows the color to fade away when we hover on the target button
9383	set value to 1 or true to enable and 0 or false to disable
9384	(by default this is disabled)
9385	
9386	data-image-src="url"
9387	(add this if you would like a background image)
9388	
9389	data-cool-color="primary"
9390	this chooses the overlay color. It pull directly from the foundation color pallet 
9391	the default / fallback is primary
9392	you may also set it to: secondary, success, warning, alert, black or white
9393	
9394	data-cool-btn="primary"
9395	this sets the hover color of the target button (if not set it will fallback to the data-cool-color value)
9396	you may also set it to: secondary, success, warning, alert, black or white
9397*/
9398
9399function coolColorOverLay() {
9400	jQuery(".coolColor").each(function(){
9401		var ccattr = jQuery(this).attr('data-cool-color');
9402		var bcattr = jQuery(this).attr('data-cool-btn');
9403		if (typeof ccattr !== typeof undefined && ccattr !== false && ccattr !== '') {
9404			ccattr = jQuery(this).attr('data-cool-color');
9405		} else {
9406			ccattr = 'primary';
9407		}
9408		if (typeof bcattr !== typeof undefined && bcattr !== false && bcattr !== '') {
9409			bcattr = jQuery(this).attr('data-cool-btn');
9410		} else {
9411			bcattr = ccattr;
9412		}
9413		jQuery(this).children().wrapAll("<div class='coolContent'></div>");
9414		jQuery(this).children().find('.button').addClass(bcattr);
9415		var bgattr = jQuery(this).attr('data-image-src');
9416		if (typeof bgattr !== typeof undefined && bgattr !== false) {
9417			jQuery(this).addClass(ccattr).css({
9418				'background' : 'url('+bgattr+')',
9419				'background-repeat': 'no-repeat',
9420				'background-size': 'cover',
9421				'background-position': 'center top',
9422				'padding': '3rem 0 5rem',
9423			});
9424		} else {
9425			jQuery(this).css({
9426				'position': 'relative',
9427				'padding': '3rem 0 5rem',
9428			});			
9429		}
9430		var coolthis = jQuery(this).attr('data-cool-this');
9431		if (typeof coolthis !== typeof undefined && coolthis !== false && (coolthis === 'true'|| coolthis === '1') && (coolthis !== '' || coolthis !== 0 || coolthis !== 'false')) {
9432			jQuery(this).find('.button').hover( function(){
9433				jQuery(this).parents('.coolColor').toggleClass('unCoolThis');
9434			});
9435		} else {
9436			
9437		}
9438	}); 
9439}
9440

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.