PageSourceSearch

https://www.stvincent.edu/assets/scripts/vendor/flickity.js

js stvincent.edu collected 2026-09-24 08:53:16 UTC 132,433 bytes, 4,587 lines download raw bytes

vendor: 17,984 bytes, lines 1-657
1/*!
2 * Flickity PACKAGED v2.2.1
3 * Touch, responsive, flickable carousels
4 *
5 * Licensed GPLv3 for open source use
6 * or Flickity Commercial License for commercial use
7 *
8 * https://flickity.metafizzy.co
9 * Copyright 2015-2019 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
21( function( window, factory ) {
22    // universal module definition
23    /*jshint strict: false */ /* globals define, module, require */
24    if ( typeof define == 'function' && define.amd ) {
25      // AMD
26      define( 'jquery-bridget/jquery-bridget',[ 'jquery' ], function( jQuery ) {
27        return factory( window, jQuery );
28      });
29    } else if ( typeof module == 'object' && module.exports ) {
30      // CommonJS
31      module.exports = factory(
32        window,
33        require('jquery')
34      );
35    } else {
36      // browser global
37      window.jQueryBridget = factory(
38        window,
39        window.jQuery
40      );
41    }
42  
43  }( window, function factory( window, jQuery ) {
44  'use strict';
45  
46  // ----- utils ----- //
47  
48  var arraySlice = Array.prototype.slice;
49  
50  // helper function for logging errors
51  // $.error breaks jQuery chaining
52  var console = window.console;
53  var logError = typeof console == 'undefined' ? function() {} :
54    function( message ) {
55      console.error( message );
56    };
57  
58  // ----- jQueryBridget ----- //
59  
60  function jQueryBridget( namespace, PluginClass, $ ) {
61    $ = $ || jQuery || window.jQuery;
62    if ( !$ ) {
63      return;
64    }
65  
66    // add option method -> $().plugin('option', {...})
67    if ( !PluginClass.prototype.option ) {
68      // option setter
69      PluginClass.prototype.option = function( opts ) {
70        // bail out if not an object
71        if ( !$.isPlainObject( opts ) ){
72          return;
73        }
74        this.options = $.extend( true, this.options, opts );
75      };
76    }
77  
78    // make jQuery plugin
79    $.fn[ namespace ] = function( arg0 /*, arg1 */ ) {
80      if ( typeof arg0 == 'string' ) {
81        // method call $().plugin( 'methodName', { options } )
82        // shift arguments by 1
83        var args = arraySlice.call( arguments, 1 );
84        return methodCall( this, arg0, args );
85      }
86      // just $().plugin({ options })
87      plainCall( this, arg0 );
88      return this;
89    };
90  
91    // $().plugin('methodName')
92    function methodCall( $elems, methodName, args ) {
93      var returnValue;
94      var pluginMethodStr = '$().' + namespace + '("' + methodName + '")';
95  
96      $elems.each( function( i, elem ) {
97        // get instance
98        var instance = $.data( elem, namespace );
99        if ( !instance ) {
100          logError( namespace + ' not initialized. Cannot call methods, i.e. ' +
101            pluginMethodStr );
102          return;
103        }
104  
105        var method = instance[ methodName ];
106        if ( !method || methodName.charAt(0) == '_' ) {
107          logError( pluginMethodStr + ' is not a valid method' );
108          return;
109        }
110  
111        // apply method, get return value
112        var value = method.apply( instance, args );
113        // set return value if value is returned, use only first value
114        returnValue = returnValue === undefined ? value : returnValue;
115      });
116  
117      return returnValue !== undefined ? returnValue : $elems;
118    }
119  
120    function plainCall( $elems, options ) {
121      $elems.each( function( i, elem ) {
122        var instance = $.data( elem, namespace );
123        if ( instance ) {
124          // set options & init
125          instance.option( options );
126          instance._init();
127        } else {
128          // initialize new instance
129          instance = new PluginClass( elem, options );
130          $.data( elem, namespace, instance );
131        }
132      });
133    }
134  
135    updateJQuery( $ );
136  
137  }
138  
139  // ----- updateJQuery ----- //
140  
141  // set $.bridget for v1 backwards compatibility
142  function updateJQuery( $ ) {
143    if ( !$ || ( $ && $.bridget ) ) {
144      return;
145    }
146    $.bridget = jQueryBridget;
147  }
148  
149  updateJQuery( jQuery || window.jQuery );
150  
151  // -----  ----- //
152  
153  return jQueryBridget;
154  
155  }));
156  
157  /**
158   * EvEmitter v1.1.0
159   * Lil' event emitter
160   * MIT License
161   */
162  
163  /* jshint unused: true, undef: true, strict: true */
164  
165  ( function( global, factory ) {
166    // universal module definition
167    /* jshint strict: false */ /* globals define, module, window */
168    if ( typeof define == 'function' && define.amd ) {
169      // AMD - RequireJS
170      define( 'ev-emitter/ev-emitter',factory );
171    } else if ( typeof module == 'object' && module.exports ) {
172      // CommonJS - Browserify, Webpack
173      module.exports = factory();
174    } else {
175      // Browser globals
176      global.EvEmitter = factory();
177    }
178  
179  }( typeof window != 'undefined' ? window : this, function() {
180  
181  
182  
183  function EvEmitter() {}
184  
185  var proto = EvEmitter.prototype;
186  
187  proto.on = function( eventName, listener ) {
188    if ( !eventName || !listener ) {
189      return;
190    }
191    // set events hash
192    var events = this._events = this._events || {};
193    // set listeners array
194    var listeners = events[ eventName ] = events[ eventName ] || [];
195    // only add once
196    if ( listeners.indexOf( listener ) == -1 ) {
197      listeners.push( listener );
198    }
199  
200    return this;
201  };
202  
203  proto.once = function( eventName, listener ) {
204    if ( !eventName || !listener ) {
205      return;
206    }
207    // add event
208    this.on( eventName, listener );
209    // set once flag
210    // set onceEvents hash
211    var onceEvents = this._onceEvents = this._onceEvents || {};
212    // set onceListeners object
213    var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {};
214    // set flag
215    onceListeners[ listener ] = true;
216  
217    return this;
218  };
219  
220  proto.off = function( eventName, listener ) {
221    var listeners = this._events && this._events[ eventName ];
222    if ( !listeners || !listeners.length ) {
223      return;
224    }
225    var index = listeners.indexOf( listener );
226    if ( index != -1 ) {
227      listeners.splice( index, 1 );
228    }
229  
230    return this;
231  };
232  
233  proto.emitEvent = function( eventName, args ) {
234    var listeners = this._events && this._events[ eventName ];
235    if ( !listeners || !listeners.length ) {
236      return;
237    }
238    // copy over to avoid interference if .off() in listener
239    listeners = listeners.slice(0);
240    args = args || [];
241    // once stuff
242    var onceListeners = this._onceEvents && this._onceEvents[ eventName ];
243  
244    for ( var i=0; i < listeners.length; i++ ) {
245      var listener = listeners[i]
246      var isOnce = onceListeners && onceListeners[ listener ];
247      if ( isOnce ) {
248        // remove listener
249        // remove before trigger to prevent recursion
250        this.off( eventName, listener );
251        // unset once flag
252        delete onceListeners[ listener ];
253      }
254      // trigger listener
255      listener.apply( this, args );
256    }
257  
258    return this;
259  };
260  
261  proto.allOff = function() {
262    delete this._events;
263    delete this._onceEvents;
264  };
265  
266  return EvEmitter;
267  
268  }));
269  
270  /*!
271   * getSize v2.0.3
272   * measure size of elements
273   * MIT license
274   */
275  
276  /* jshint browser: true, strict: true, undef: true, unused: true */
277  /* globals console: false */
278  
279  ( function( window, factory ) {
280    /* jshint strict: false */ /* globals define, module */
281    if ( typeof define == 'function' && define.amd ) {
282      // AMD
283      define( 'get-size/get-size',factory );
284    } else if ( typeof module == 'object' && module.exports ) {
285      // CommonJS
286      module.exports = factory();
287    } else {
288      // browser global
289      window.getSize = factory();
290    }
291  
292  })( window, function factory() {
293  'use strict';
294  
295  // -------------------------- helpers -------------------------- //
296  
297  // get a number from a string, not a percentage
298  function getStyleSize( value ) {
299    var num = parseFloat( value );
300    // not a percent like '100%', and a number
301    var isValid = value.indexOf('%') == -1 && !isNaN( num );
302    return isValid && num;
303  }
304  
305  function noop() {}
306  
307  var logError = typeof console == 'undefined' ? noop :
308    function( message ) {
309      console.error( message );
310    };
311  
312  // -------------------------- measurements -------------------------- //
313  
314  var measurements = [
315    'paddingLeft',
316    'paddingRight',
317    'paddingTop',
318    'paddingBottom',
319    'marginLeft',
320    'marginRight',
321    'marginTop',
322    'marginBottom',
323    'borderLeftWidth',
324    'borderRightWidth',
325    'borderTopWidth',
326    'borderBottomWidth'
327  ];
328  
329  var measurementsLength = measurements.length;
330  
331  function getZeroSize() {
332    var size = {
333      width: 0,
334      height: 0,
335      innerWidth: 0,
336      innerHeight: 0,
337      outerWidth: 0,
338      outerHeight: 0
339    };
340    for ( var i=0; i < measurementsLength; i++ ) {
341      var measurement = measurements[i];
342      size[ measurement ] = 0;
343    }
344    return size;
345  }
346  
347  // -------------------------- getStyle -------------------------- //
348  
349  /**
350   * getStyle, get style of element, check for Firefox bug
351   * https://bugzilla.mozilla.org/show_bug.cgi?id=548397
352   */
353  function getStyle( elem ) {
354    var style = getComputedStyle( elem );
355    if ( !style ) {
356      logError( 'Style returned ' + style +
357        '. Are you running this code in a hidden iframe on Firefox? ' +
358        'See https://bit.ly/getsizebug1' );
359    }
360    return style;
361  }
362  
363  // -------------------------- setup -------------------------- //
364  
365  var isSetup = false;
366  
367  var isBoxSizeOuter;
368  
369  /**
370   * setup
371   * check isBoxSizerOuter
372   * do on first getSize() rather than on page load for Firefox bug
373   */
374  function setup() {
375    // setup once
376    if ( isSetup ) {
377      return;
378    }
379    isSetup = true;
380  
381    // -------------------------- box sizing -------------------------- //
382  
383    /**
384     * Chrome & Safari measure the outer-width on style.width on border-box elems
385     * IE11 & Firefox<29 measures the inner-width
386     */
387    var div = document.createElement('div');
388    div.style.width = '200px';
389    div.style.padding = '1px 2px 3px 4px';
390    div.style.borderStyle = 'solid';
391    div.style.borderWidth = '1px 2px 3px 4px';
392    div.style.boxSizing = 'border-box';
393  
394    var body = document.body || document.documentElement;
395    body.appendChild( div );
396    var style = getStyle( div );
397    // round value for browser zoom. desandro/masonry#928
398    isBoxSizeOuter = Math.round( getStyleSize( style.width ) ) == 200;
399    getSize.isBoxSizeOuter = isBoxSizeOuter;
400  
401    body.removeChild( div );
402  }
403  
404  // -------------------------- getSize -------------------------- //
405  
406  function getSize( elem ) {
407    setup();
408  
409    // use querySeletor if elem is string
410    if ( typeof elem == 'string' ) {
411      elem = document.querySelector( elem );
412    }
413  
414    // do not proceed on non-objects
415    if ( !elem || typeof elem != 'object' || !elem.nodeType ) {
416      return;
417    }
418  
419    var style = getStyle( elem );
420  
421    // if hidden, everything is 0
422    if ( style.display == 'none' ) {
423      return getZeroSize();
424    }
425  
426    var size = {};
427    size.width = elem.offsetWidth;
428    size.height = elem.offsetHeight;
429  
430    var isBorderBox = size.isBorderBox = style.boxSizing == 'border-box';
431  
432    // get all measurements
433    for ( var i=0; i < measurementsLength; i++ ) {
434      var measurement = measurements[i];
435      var value = style[ measurement ];
436      var num = parseFloat( value );
437      // any 'auto', 'medium' value will be 0
438      size[ measurement ] = !isNaN( num ) ? num : 0;
439    }
440  
441    var paddingWidth = size.paddingLeft + size.paddingRight;
442    var paddingHeight = size.paddingTop + size.paddingBottom;
443    var marginWidth = size.marginLeft + size.marginRight;
444    var marginHeight = size.marginTop + size.marginBottom;
445    var borderWidth = size.borderLeftWidth + size.borderRightWidth;
446    var borderHeight = size.borderTopWidth + size.borderBottomWidth;
447  
448    var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter;
449  
450    // overwrite width and height if we can get it from style
451    var styleWidth = getStyleSize( style.width );
452    if ( styleWidth !== false ) {
453      size.width = styleWidth +
454        // add padding and border unless it's already including it
455        ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth );
456    }
457  
458    var styleHeight = getStyleSize( style.height );
459    if ( styleHeight !== false ) {
460      size.height = styleHeight +
461        // add padding and border unless it's already including it
462        ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight );
463    }
464  
465    size.innerWidth = size.width - ( paddingWidth + borderWidth );
466    size.innerHeight = size.height - ( paddingHeight + borderHeight );
467  
468    size.outerWidth = size.width + marginWidth;
469    size.outerHeight = size.height + marginHeight;
470  
471    return size;
472  }
473  
474  return getSize;
475  
476  });
477  
478  /**
479   * matchesSelector v2.0.2
480   * matchesSelector( element, '.selector' )
481   * MIT license
482   */
483  
484  /*jshint browser: true, strict: true, undef: true, unused: true */
485  
486  ( function( window, factory ) {
487    /*global define: false, module: false */
488    'use strict';
489    // universal module definition
490    if ( typeof define == 'function' && define.amd ) {
491      // AMD
492      define( 'desandro-matches-selector/matches-selector',factory );
493    } else if ( typeof module == 'object' && module.exports ) {
494      // CommonJS
495      module.exports = factory();
496    } else {
497      // browser global
498      window.matchesSelector = factory();
499    }
500  
501  }( window, function factory() {
502    'use strict';
503  
504    var matchesMethod = ( function() {
505      var ElemProto = window.Element.prototype;
506      // check for the standard method name first
507      if ( ElemProto.matches ) {
508        return 'matches';
509      }
510      // check un-prefixed
511      if ( ElemProto.matchesSelector ) {
512        return 'matchesSelector';
513      }
514      // check vendor prefixes
515      var prefixes = [ 'webkit', 'moz', 'ms', 'o' ];
516  
517      for ( var i=0; i < prefixes.length; i++ ) {
518        var prefix = prefixes[i];
519        var method = prefix + 'MatchesSelector';
520        if ( ElemProto[ method ] ) {
521          return method;
522        }
523      }
524    })();
525  
526    return function matchesSelector( elem, selector ) {
527      return elem[ matchesMethod ]( selector );
528    };
529  
530  }));
531  
532  /**
533   * Fizzy UI utils v2.0.7
534   * MIT license
535   */
536  
537  /*jshint browser: true, undef: true, unused: true, strict: true */
538  
539  ( function( window, factory ) {
540    // universal module definition
541    /*jshint strict: false */ /*globals define, module, require */
542  
543    if ( typeof define == 'function' && define.amd ) {
544      // AMD
545      define( 'fizzy-ui-utils/utils',[
546        'desandro-matches-selector/matches-selector'
547      ], function( matchesSelector ) {
548        return factory( window, matchesSelector );
549      });
550    } else if ( typeof module == 'object' && module.exports ) {
551      // CommonJS
552      module.exports = factory(
553        window,
554        require('desandro-matches-selector')
555      );
556    } else {
557      // browser global
558      window.fizzyUIUtils = factory(
559        window,
560        window.matchesSelector
561      );
562    }
563  
564  }( window, function factory( window, matchesSelector ) {
565  
566  
567  
568  var utils = {};
569  
570  // ----- extend ----- //
571  
572  // extends objects
573  utils.extend = function( a, b ) {
574    for ( var prop in b ) {
575      a[ prop ] = b[ prop ];
576    }
577    return a;
578  };
579  
580  // ----- modulo ----- //
581  
582  utils.modulo = function( num, div ) {
583    return ( ( num % div ) + div ) % div;
584  };
585  
586  // ----- makeArray ----- //
587  
588  var arraySlice = Array.prototype.slice;
589  
590  // turn element or nodeList into an array
591  utils.makeArray = function( obj ) {
592    if ( Array.isArray( obj ) ) {
593      // use object if already an array
594      return obj;
595    }
596    // return empty array if undefined or null. #6
597    if ( obj === null || obj === undefined ) {
598      return [];
599    }
600  
601    var isArrayLike = typeof obj == 'object' && typeof obj.length == 'number';
602    if ( isArrayLike ) {
603      // convert nodeList to array
604      return arraySlice.call( obj );
605    }
606  
607    // array of single index
608    return [ obj ];
609  };
610  
611  // ----- removeFrom ----- //
612  
613  utils.removeFrom = function( ary, obj ) {
614    var index = ary.indexOf( obj );
615    if ( index != -1 ) {
616      ary.splice( index, 1 );
617    }
618  };
619  
620  // ----- getParent ----- //
621  
622  utils.getParent = function( elem, selector ) {
623    while ( elem.parentNode && elem != document.body ) {
624      elem = elem.parentNode;
625      if ( matchesSelector( elem, selector ) ) {
626        return elem;
627      }
628    }
629  };
630  
631  // ----- getQueryElement ----- //
632  
633  // use element as selector string
634  utils.getQueryElement = function( elem ) {
635    if ( typeof elem == 'string' ) {
636      return document.querySelector( elem );
637    }
638    return elem;
639  };
640  
641  // ----- handleEvent ----- //
642  
643  // enable .ontype to trigger from .addEventListener( elem, 'type' )
644  utils.handleEvent = function( event ) {
645    var method = 'on' + event.type;
646    if ( this[ method ] ) {
647      this[ method ]( event );
648    }
649  };
650  
651  // ----- filterFindElements ----- //
652  
653  utils.filterFindElements = function( elems, selector ) {
654    // make array of elems
655    elems = utils.makeArray( elems );
656    var ffElems = [];
657  
658    elems.forEach( function( elem ) {
659      // check that elem is an actual element
660      if ( !( elem instanceof HTMLElement ) ) {
661        return;
662      }
663      // add elem if no selector
664      if ( !selector ) {
665        ffElems.push( elem );
666        return;
667      }
668      // filter & find items if we have a selector
669      // filter
670      if ( matchesSelector( elem, selector ) ) {
671        ffElems.push( elem );
672      }
673      // find children
674      var childElems = elem.querySelectorAll( selector );
675      // concat childElems to filterFound array
676      for ( var i=0; i < childElems.length; i++ ) {
677        ffElems.push( childElems[i] );
678      }
679    });
680  
681    return ffElems;
682  };
683  
684  // ----- debounceMethod ----- //
685  
686  utils.debounceMethod = function( _class, methodName, threshold ) {
687    threshold = threshold || 100;
688    // original method
689    var method = _class.prototype[ methodName ];
690    var timeoutName = methodName + 'Timeout';
691  
692    _class.prototype[ methodName ] = function() {
693      var timeout = this[ timeoutName ];
694      clearTimeout( timeout );
695  
696      var args = arguments;
697      var _this = this;
698      this[ timeoutName ] = setTimeout( function() {
699        method.apply( _this, args );
700        delete _this[ timeoutName ];
701      }, threshold );
702    };
703  };
704  
705  // ----- docReady ----- //
706  
707  utils.docReady = function( callback ) {
708    var readyState = document.readyState;
709    if ( readyState == 'complete' || readyState == 'interactive' ) {
710      // do async to allow for other scripts to run. metafizzy/flickity#441
711      setTimeout( callback );
712    } else {
713      document.addEventListener( 'DOMContentLoaded', callback );
714    }
715  };
716  
717  // ----- htmlInit ----- //
718  
719  // http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
720  utils.toDashed = function( str ) {
721    return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
722      return $1 + '-' + $2;
723    }).toLowerCase();
724  };
725  
726  var console = window.console;
727  /**
728   * allow user to initialize classes via [data-namespace] or .js-namespace class
729   * htmlInit( Widget, 'widgetName' )
730   * options are parsed from data-namespace-options
731   */
732  utils.htmlInit = function( WidgetClass, namespace ) {
733    utils.docReady( function() {
734      var dashedNamespace = utils.toDashed( namespace );
735      var dataAttr = 'data-' + dashedNamespace;
736      var dataAttrElems = document.querySelectorAll( '[' + dataAttr + ']' );
737      var jsDashElems = document.querySelectorAll( '.js-' + dashedNamespace );
738      var elems = utils.makeArray( dataAttrElems )
739        .concat( utils.makeArray( jsDashElems ) );
740      var dataOptionsAttr = dataAttr + '-options';
741      var jQuery = window.jQuery;
742  
743      elems.forEach( function( elem ) {
744        var attr = elem.getAttribute( dataAttr ) ||
745          elem.getAttribute( dataOptionsAttr );
746        var options;
747        try {
748          options = attr && JSON.parse( attr );
749        } catch ( error ) {
750          // log error, do not initialize
751          if ( console ) {
752            console.error( 'Error parsing ' + dataAttr + ' on ' + elem.className +
753            ': ' + error );
754          }
755          return;
756        }
757        // initialize
758        var instance = new WidgetClass( elem, options );
759        // make available via $().data('namespace')
760        if ( jQuery ) {
761          jQuery.data( elem, namespace, instance );
762        }
763      });
764  
765    });
766  };
767  
768  // -----  ----- //
769  
770  return utils;
771  
772  }));
773  
774  // Flickity.Cell
775  ( function( window, factory ) {
776    // universal module definition
777    /* jshint strict: false */
778    if ( typeof define == 'function' && define.amd ) {
779      // AMD
780      define( 'flickity/js/cell',[
781        'get-size/get-size'
782      ], function( getSize ) {
783        return factory( window, getSize );
784      });
785    } else if ( typeof module == 'object' && module.exports ) {
786      // CommonJS
787      module.exports = factory(
788        window,
789        require('get-size')
790      );
791    } else {
792      // browser global
793      window.Flickity = window.Flickity || {};
794      window.Flickity.Cell = factory(
795        window,
796        window.getSize
797      );
798    }
799  
800  }( window, function factory( window, getSize ) {
801  
802  
803  
804  function Cell( elem, parent ) {
805    this.element = elem;
806    this.parent = parent;
807  
808    this.create();
809  }
810  
811  var proto = Cell.prototype;
812  
813  proto.create = function() {
814    this.element.style.position = 'absolute';
815    this.element.setAttribute( 'aria-hidden', 'true' );
816    this.x = 0;
817    this.shift = 0;
818  };
819  
820  proto.destroy = function() {
821    // reset style
822    this.unselect();
823    this.element.style.position = '';
824    var side = this.parent.originSide;
825    this.element.style[ side ] = '';
826  };
827  
828  proto.getSize = function() {
829    this.size = getSize( this.element );
830  };
831  
832  proto.setPosition = function( x ) {
833    this.x = x;
834    this.updateTarget();
835    this.renderPosition( x );
836  };
837  
838  // setDefaultTarget v1 method, backwards compatibility, remove in v3
839  proto.updateTarget = proto.setDefaultTarget = function() {
840    var marginProperty = this.parent.originSide == 'left' ? 'marginLeft' : 'marginRight';
841    this.target = this.x + this.size[ marginProperty ] +
842      this.size.width * this.parent.cellAlign;
843  };
844  
845  proto.renderPosition = function( x ) {
846    // render position of cell with in slider
847    var side = this.parent.originSide;
848    this.element.style[ side ] = this.parent.getPositionValue( x );
849  };
850  
851  proto.select = function() {
852    this.element.classList.add('is-selected');
853    this.element.removeAttribute('aria-hidden');
854  };
855  
856  proto.unselect = function() {
857    this.element.classList.remove('is-selected');
858    this.element.setAttribute( 'aria-hidden', 'true' );
859  };
860  
861  /**
862   * @param {Integer} factor - 0, 1, or -1
863  **/
864  proto.wrapShift = function( shift ) {
865    this.shift = shift;
866    this.renderPosition( this.x + this.parent.slideableWidth * shift );
867  };
868  
869  proto.remove = function() {
870    this.element.parentNode.removeChild( this.element );
871  };
872  
873  return Cell;
874  
875  }));
876  
877  // slide
878  ( function( window, factory ) {
879    // universal module definition
880    /* jshint strict: false */
881    if ( typeof define == 'function' && define.amd ) {
882      // AMD
883      define( 'flickity/js/slide',factory );
884    } else if ( typeof module == 'object' && module.exports ) {
885      // CommonJS
886      module.exports = factory();
887    } else {
888      // browser global
889      window.Flickity = window.Flickity || {};
890      window.Flickity.Slide = factory();
891    }
892  
893  }( window, function factory() {
894  'use strict';
895  
896  function Slide( parent ) {
897    this.parent = parent;
898    this.isOriginLeft = parent.originSide == 'left';
899    this.cells = [];
900    this.outerWidth = 0;
901    this.height = 0;
902  }
903  
904  var proto = Slide.prototype;
905  
906  proto.addCell = function( cell ) {
907    this.cells.push( cell );
908    this.outerWidth += cell.size.outerWidth;
909    this.height = Math.max( cell.size.outerHeight, this.height );
910    // first cell stuff
911    if ( this.cells.length == 1 ) {
912      this.x = cell.x; // x comes from first cell
913      var beginMargin = this.isOriginLeft ? 'marginLeft' : 'marginRight';
914      this.firstMargin = cell.size[ beginMargin ];
915    }
916  };
917  
918  proto.updateTarget = function() {
919    var endMargin = this.isOriginLeft ? 'marginRight' : 'marginLeft';
920    var lastCell = this.getLastCell();
921    var lastMargin = lastCell ? lastCell.size[ endMargin ] : 0;
922    var slideWidth = this.outerWidth - ( this.firstMargin + lastMargin );
923    this.target = this.x + this.firstMargin + slideWidth * this.parent.cellAlign;
924  };
925  
926  proto.getLastCell = function() {
927    return this.cells[ this.cells.length - 1 ];
928  };
929  
930  proto.select = function() {
931    this.cells.forEach( function( cell ) {
932      cell.select();
933    });
934  };
935  
936  proto.unselect = function() {
937    this.cells.forEach( function( cell ) {
938      cell.unselect();
939    });
940  };
941  
942  proto.getCellElements = function() {
943    return this.cells.map( function( cell ) {
944      return cell.element;
945    });
946  };
947  
948  return Slide;
949  
950  }));
951  
952  // animate
953  ( function( window, factory ) {
954    // universal module definition
955    /* jshint strict: false */
956    if ( typeof define == 'function' && define.amd ) {
957      // AMD
958      define( 'flickity/js/animate',[
959        'fizzy-ui-utils/utils'
960      ], function( utils ) {
961        return factory( window, utils );
962      });
963    } else if ( typeof module == 'object' && module.exports ) {
964      // CommonJS
965      module.exports = factory(
966        window,
967        require('fizzy-ui-utils')
968      );
969    } else {
970      // browser global
971      window.Flickity = window.Flickity || {};
972      window.Flickity.animatePrototype = factory(
973        window,
974        window.fizzyUIUtils
975      );
976    }
977  
978  }( window, function factory( window, utils ) {
979  
980  
981  
982  // -------------------------- animate -------------------------- //
983  
984  var proto = {};
985  
986  proto.startAnimation = function() {
987    if ( this.isAnimating ) {
988      return;
989    }
990  
991    this.isAnimating = true;
992    this.restingFrames = 0;
993    this.animate();
994  };
995  
996  proto.animate = function() {
997    this.applyDragForce();
998    this.applySelectedAttraction();
999  
1000    var previousX = this.x;
1001  
1002    this.integratePhysics();
1003    this.positionSlider();
1004    this.settle( previousX );
1005    // animate next frame
1006    if ( this.isAnimating ) {
1007      var _this = this;
1008      requestAnimationFrame( function animateFrame() {
1009        _this.animate();
1010      });
1011    }
1012  };
1013  
1014  proto.positionSlider = function() {
1015    var x = this.x;
1016    // wrap position around
1017    if ( this.options.wrapAround && this.cells.length > 1 ) {
1018      x = utils.modulo( x, this.slideableWidth );
1019      x = x - this.slideableWidth;
1020      this.shiftWrapCells( x );
1021    }
1022  
1023    this.setTranslateX( x, this.isAnimating );
1024    this.dispatchScrollEvent();
1025  };
1026  
1027  proto.setTranslateX = function( x, is3d ) {
1028    x += this.cursorPosition;
1029    // reverse if right-to-left and using transform
1030    x = this.options.rightToLeft ? -x : x;
1031    var translateX = this.getPositionValue( x );
1032    // use 3D tranforms for hardware acceleration on iOS
1033    // but use 2D when settled, for better font-rendering
1034    this.slider.style.transform = is3d ?
1035      'translate3d(' + translateX + ',0,0)' : 'translateX(' + translateX + ')';
1036  };
1037  
1038  proto.dispatchScrollEvent = function() {
1039    var firstSlide = this.slides[0];
1040    if ( !firstSlide ) {
1041      return;
1042    }
1043    var positionX = -this.x - firstSlide.target;
1044    var progress = positionX / this.slidesWidth;
1045    this.dispatchEvent( 'scroll', null, [ progress, positionX ] );
1046  };
1047  
1048  proto.positionSliderAtSelected = function() {
1049    if ( !this.cells.length ) {
1050      return;
1051    }
1052    this.x = -this.selectedSlide.target;
1053    this.velocity = 0; // stop wobble
1054    this.positionSlider();
1055  };
1056  
1057  proto.getPositionValue = function( position ) {
1058    if ( this.options.percentPosition ) {
1059      // percent position, round to 2 digits, like 12.34%
1060      return ( Math.round( ( position / this.size.innerWidth ) * 10000 ) * 0.01 )+ '%';
1061    } else {
1062      // pixel positioning
1063      return Math.round( position ) + 'px';
1064    }
1065  };
1066  
1067  proto.settle = function( previousX ) {
1068    // keep track of frames where x hasn't moved
1069    if ( !this.isPointerDown && Math.round( this.x * 100 ) == Math.round( previousX * 100 ) ) {
1070      this.restingFrames++;
1071    }
1072    // stop animating if resting for 3 or more frames
1073    if ( this.restingFrames > 2 ) {
1074      this.isAnimating = false;
1075      delete this.isFreeScrolling;
1076      // render position with translateX when settled
1077      this.positionSlider();
1078      this.dispatchEvent( 'settle', null, [ this.selectedIndex ] );
1079    }
1080  };
1081  
1082  proto.shiftWrapCells = function( x ) {
1083    // shift before cells
1084    var beforeGap = this.cursorPosition + x;
1085    this._shiftCells( this.beforeShiftCells, beforeGap, -1 );
1086    // shift after cells
1087    var afterGap = this.size.innerWidth - ( x + this.slideableWidth + this.cursorPosition );
1088    this._shiftCells( this.afterShiftCells, afterGap, 1 );
1089  };
1090  
1091  proto._shiftCells = function( cells, gap, shift ) {
1092    for ( var i=0; i < cells.length; i++ ) {
1093      var cell = cells[i];
1094      var cellShift = gap > 0 ? shift : 0;
1095      cell.wrapShift( cellShift );
1096      gap -= cell.size.outerWidth;
1097    }
1098  };
1099  
1100  proto._unshiftCells = function( cells ) {
1101    if ( !cells || !cells.length ) {
1102      return;
1103    }
1104    for ( var i=0; i < cells.length; i++ ) {
1105      cells[i].wrapShift( 0 );
1106    }
1107  };
1108  
1109  // -------------------------- physics -------------------------- //
1110  
1111  proto.integratePhysics = function() {
1112    this.x += this.velocity;
1113    this.velocity *= this.getFrictionFactor();
1114  };
1115  
1116  proto.applyForce = function( force ) {
1117    this.velocity += force;
1118  };
1119  
1120  proto.getFrictionFactor = function() {
1121    return 1 - this.options[ this.isFreeScrolling ? 'freeScrollFriction' : 'friction' ];
1122  };
1123  
1124  proto.getRestingPosition = function() {
1125    // my thanks to Steven Wittens, who simplified this math greatly
1126    return this.x + this.velocity / ( 1 - this.getFrictionFactor() );
1127  };
1128  
1129  proto.applyDragForce = function() {
1130    if ( !this.isDraggable || !this.isPointerDown ) {
1131      return;
1132    }
1133    // change the position to drag position by applying force
1134    var dragVelocity = this.dragX - this.x;
1135    var dragForce = dragVelocity - this.velocity;
1136    this.applyForce( dragForce );
1137  };
1138  
1139  proto.applySelectedAttraction = function() {
1140    // do not attract if pointer down or no slides
1141    var dragDown = this.isDraggable && this.isPointerDown;
1142    if ( dragDown || this.isFreeScrolling || !this.slides.length ) {
1143      return;
1144    }
1145    var distance = this.selectedSlide.target * -1 - this.x;
1146    var force = distance * this.options.selectedAttraction;
1147    this.applyForce( force );
1148  };
1149  
1150  return proto;
1151  
1152  }));
1153  
1154  // Flickity main
1155  ( function( window, factory ) {
1156    // universal module definition
1157    /* jshint strict: false */
1158    if ( typeof define == 'function' && define.amd ) {
1159      // AMD
1160      define( 'flickity/js/flickity',[
1161        'ev-emitter/ev-emitter',
1162        'get-size/get-size',
1163        'fizzy-ui-utils/utils',
1164        './cell',
1165        './slide',
1166        './animate'
1167      ], function( EvEmitter, getSize, utils, Cell, Slide, animatePrototype ) {
1168        return factory( window, EvEmitter, getSize, utils, Cell, Slide, animatePrototype );
1169      });
1170    } else if ( typeof module == 'object' && module.exports ) {
1171      // CommonJS
1172      module.exports = factory(
1173        window,
1174        require('ev-emitter'),
1175        require('get-size'),
1176        require('fizzy-ui-utils'),
1177        require('./cell'),
1178        require('./slide'),
1179        require('./animate')
1180      );
1181    } else {
1182      // browser global
1183      var _Flickity = window.Flickity;
1184  
1185      window.Flickity = factory(
1186        window,
1187        window.EvEmitter,
1188        window.getSize,
1189        window.fizzyUIUtils,
1190        _Flickity.Cell,
1191        _Flickity.Slide,
1192        _Flickity.animatePrototype
1193      );
1194    }
1195  
1196  }( window, function factory( window, EvEmitter, getSize,
1197    utils, Cell, Slide, animatePrototype ) {
1198  
1199  
1200  
1201  // vars
1202  var jQuery = window.jQuery;
1203  var getComputedStyle = window.getComputedStyle;
1204  var console = window.console;
1205  
1206  function moveElements( elems, toElem ) {
1207    elems = utils.makeArray( elems );
1208    while ( elems.length ) {
1209      toElem.appendChild( elems.shift() );
1210    }
1211  }
1212  
1213  // -------------------------- Flickity -------------------------- //
1214  
1215  // globally unique identifiers
1216  var GUID = 0;
1217  // internal store of all Flickity intances
1218  var instances = {};
1219  
1220  function Flickity( element, options ) {
1221    var queryElement = utils.getQueryElement( element );
1222    if ( !queryElement ) {
1223      if ( console ) {
1224        console.error( 'Bad element for Flickity: ' + ( queryElement || element ) );
1225      }
1226      return;
1227    }
1228    this.element = queryElement;
1229    // do not initialize twice on same element
1230    if ( this.element.flickityGUID ) {
1231      var instance = instances[ this.element.flickityGUID ];
1232      instance.option( options );
1233      return instance;
1234    }
1235  
1236    // add jQuery
1237    if ( jQuery ) {
1238      this.$element = jQuery( this.element );
1239    }
1240    // options
1241    this.options = utils.extend( {}, this.constructor.defaults );
1242    this.option( options );
1243  
1244    // kick things off
1245    this._create();
1246  }
1247  
1248  Flickity.defaults = {
1249    accessibility: true,
1250    // adaptiveHeight: false,
1251    cellAlign: 'center',
1252    // cellSelector: undefined,
1253    // contain: false,
1254    freeScrollFriction: 0.075, // friction when free-scrolling
1255    friction: 0.28, // friction when selecting
1256    namespaceJQueryEvents: true,
1257    // initialIndex: 0,
1258    percentPosition: true,
1259    resize: true,
1260    selectedAttraction: 0.025,
1261    setGallerySize: true
1262    // watchCSS: false,
1263    // wrapAround: false
1264  };
1265  
1266  // hash of methods triggered on _create()
1267  Flickity.createMethods = [];
1268  
1269  var proto = Flickity.prototype;
1270  // inherit EventEmitter
1271  utils.extend( proto, EvEmitter.prototype );
1272  
1273  proto._create = function() {
1274    // add id for Flickity.data
1275    var id = this.guid = ++GUID;
1276    this.element.flickityGUID = id; // expando
1277    instances[ id ] = this; // associate via id
1278    // initial properties
1279    this.selectedIndex = 0;
1280    // how many frames slider has been in same position
1281    this.restingFrames = 0;
1282    // initial physics properties
1283    this.x = 0;
1284    this.velocity = 0;
1285    this.originSide = this.options.rightToLeft ? 'right' : 'left';
1286    // create viewport & slider
1287    this.viewport = document.createElement('div');
1288    this.viewport.className = 'flickity-viewport';
1289    this._createSlider();
1290  
1291    if ( this.options.resize || this.options.watchCSS ) {
1292      window.addEventListener( 'resize', this );
1293    }
1294  
1295    // add listeners from on option
1296    for ( var eventName in this.options.on ) {
1297      var listener = this.options.on[ eventName ];
1298      this.on( eventName, listener );
1299    }
1300  
1301    Flickity.createMethods.forEach( function( method ) {
1302      this[ method ]();
1303    }, this );
1304  
1305    if ( this.options.watchCSS ) {
1306      this.watchCSS();
1307    } else {
1308      this.activate();
1309    }
1310  
1311  };
1312  
1313  /**
1314   * set options
1315   * @param {Object} opts
1316   */
1317  proto.option = function( opts ) {
1318    utils.extend( this.options, opts );
1319  };
1320  
1321  proto.activate = function() {
1322    if ( this.isActive ) {
1323      return;
1324    }
1325    this.isActive = true;
1326    this.element.classList.add('flickity-enabled');
1327    if ( this.options.rightToLeft ) {
1328      this.element.classList.add('flickity-rtl');
1329    }
1330  
1331    this.getSize();
1332    // move initial cell elements so they can be loaded as cells
1333    var cellElems = this._filterFindCellElements( this.element.children );
1334    moveElements( cellElems, this.slider );
1335    this.viewport.appendChild( this.slider );
1336    this.element.appendChild( this.viewport );
1337    // get cells from children
1338    this.reloadCells();
1339  
1340    if ( this.options.accessibility ) {
1341      // allow element to focusable
1342      this.element.tabIndex = 0;
1343      // listen for key presses
1344      this.element.addEventListener( 'keydown', this );
1345    }
1346  
1347    this.emitEvent('activate');
1348    this.selectInitialIndex();
1349    // flag for initial activation, for using initialIndex
1350    this.isInitActivated = true;
1351    // ready event. #493
1352    this.dispatchEvent('ready');
1353  };
1354  
1355  // slider positions the cells
1356  proto._createSlider = function() {
1357    // slider element does all the positioning
1358    var slider = document.createElement('div');
1359    slider.className = 'flickity-slider';
1360    slider.style[ this.originSide ] = 0;
1361    this.slider = slider;
1362  };
1363  
1364  proto._filterFindCellElements = function( elems ) {
1365    return utils.filterFindElements( elems, this.options.cellSelector );
1366  };
1367  
1368  // goes through all children
1369  proto.reloadCells = function() {
1370    // collection of item elements
1371    this.cells = this._makeCells( this.slider.children );
1372    this.positionCells();
1373    this._getWrapShiftCells();
1374    this.setGallerySize();
1375  };
1376  
1377  /**
1378   * turn elements into Flickity.Cells
1379   * @param {Array or NodeList or HTMLElement} elems
1380   * @returns {Array} items - collection of new Flickity Cells
1381   */
1382  proto._makeCells = function( elems ) {
1383    var cellElems = this._filterFindCellElements( elems );
1384  
1385    // create new Flickity for collection
1386    var cells = cellElems.map( function( cellElem ) {
1387      return new Cell( cellElem, this );
1388    }, this );
1389  
1390    return cells;
1391  };
1392  
1393  proto.getLastCell = function() {
1394    return this.cells[ this.cells.length - 1 ];
1395  };
1396  
1397  proto.getLastSlide = function() {
1398    return this.slides[ this.slides.length - 1 ];
1399  };
1400  
1401  // positions all cells
1402  proto.positionCells = function() {
1403    // size all cells
1404    this._sizeCells( this.cells );
1405    // position all cells
1406    this._positionCells( 0 );
1407  };
1408  
1409  /**
1410   * position certain cells
1411   * @param {Integer} index - which cell to start with
1412   */
1413  proto._positionCells = function( index ) {
1414    index = index || 0;
1415    // also measure maxCellHeight
1416    // start 0 if positioning all cells
1417    this.maxCellHeight = index ? this.maxCellHeight || 0 : 0;
1418    var cellX = 0;
1419    // get cellX
1420    if ( index > 0 ) {
1421      var startCell = this.cells[ index - 1 ];
1422      cellX = startCell.x + startCell.size.outerWidth;
1423    }
1424    var len = this.cells.length;
1425    for ( var i=index; i < len; i++ ) {
1426      var cell = this.cells[i];
1427      cell.setPosition( cellX );
1428      cellX += cell.size.outerWidth;
1429      this.maxCellHeight = Math.max( cell.size.outerHeight, this.maxCellHeight );
1430    }
1431    // keep track of cellX for wrap-around
1432    this.slideableWidth = cellX;
1433    // slides
1434    this.updateSlides();
1435    // contain slides target
1436    this._containSlides();
1437    // update slidesWidth
1438    this.slidesWidth = len ? this.getLastSlide().target - this.slides[0].target : 0;
1439  };
1440  
1441  /**
1442   * cell.getSize() on multiple cells
1443   * @param {Array} cells
1444   */
1445  proto._sizeCells = function( cells ) {
1446    cells.forEach( function( cell ) {
1447      cell.getSize();
1448    });
1449  };
1450  
1451  // --------------------------  -------------------------- //
1452  
1453  proto.updateSlides = function() {
1454    this.slides = [];
1455    if ( !this.cells.length ) {
1456      return;
1457    }
1458  
1459    var slide = new Slide( this );
1460    this.slides.push( slide );
1461    var isOriginLeft = this.originSide == 'left';
1462    var nextMargin = isOriginLeft ? 'marginRight' : 'marginLeft';
1463  
1464    var canCellFit = this._getCanCellFit();
1465  
1466    this.cells.forEach( function( cell, i ) {
1467      // just add cell if first cell in slide
1468      if ( !slide.cells.length ) {
1469        slide.addCell( cell );
1470        return;
1471      }
1472  
1473      var slideWidth = ( slide.outerWidth - slide.firstMargin ) +
1474        ( cell.size.outerWidth - cell.size[ nextMargin ] );
1475  
1476      if ( canCellFit.call( this, i, slideWidth ) ) {
1477        slide.addCell( cell );
1478      } else {
1479        // doesn't fit, new slide
1480        slide.updateTarget();
1481  
1482        slide = new Slide( this );
1483        this.slides.push( slide );
1484        slide.addCell( cell );
1485      }
1486    }, this );
1487    // last slide
1488    slide.updateTarget();
1489    // update .selectedSlide
1490    this.updateSelectedSlide();
1491  };
1492  
1493  proto._getCanCellFit = function() {
1494    var groupCells = this.options.groupCells;
1495    if ( !groupCells ) {
1496      return function() {
1497        return false;
1498      };
1499    } else if ( typeof groupCells == 'number' ) {
1500      // group by number. 3 -> [0,1,2], [3,4,5], ...
1501      var number = parseInt( groupCells, 10 );
1502      return function( i ) {
1503        return ( i % number ) !== 0;
1504      };
1505    }
1506    // default, group by width of slide
1507    // parse '75%
1508    var percentMatch = typeof groupCells == 'string' &&
1509      groupCells.match(/^(\d+)%$/);
1510    var percent = percentMatch ? parseInt( percentMatch[1], 10 ) / 100 : 1;
1511    return function( i, slideWidth ) {
1512      return slideWidth <= ( this.size.innerWidth + 1 ) * percent;
1513    };
1514  };
1515  
1516  // alias _init for jQuery plugin .flickity()
1517  proto._init =
1518  proto.reposition = function() {
1519    this.positionCells();
1520    this.positionSliderAtSelected();
1521  };
1522  
1523  proto.getSize = function() {
1524    this.size = getSize( this.element );
1525    this.setCellAlign();
1526    this.cursorPosition = this.size.innerWidth * this.cellAlign;
1527  };
1528  
1529  var cellAlignShorthands = {
1530    // cell align, then based on origin side
1531    center: {
1532      left: 0.5,
1533      right: 0.5
1534    },
1535    left: {
1536      left: 0,
1537      right: 1
1538    },
1539    right: {
1540      right: 0,
1541      left: 1
1542    }
1543  };
1544  
1545  proto.setCellAlign = function() {
1546    var shorthand = cellAlignShorthands[ this.options.cellAlign ];
1547    this.cellAlign = shorthand ? shorthand[ this.originSide ] : this.options.cellAlign;
1548  };
1549  
1550  proto.setGallerySize = function() {
1551    if ( this.options.setGallerySize ) {
1552      var height = this.options.adaptiveHeight && this.selectedSlide ?
1553        this.selectedSlide.height : this.maxCellHeight;
1554      this.viewport.style.height = height + 'px';
1555    }
1556  };
1557  
1558  proto._getWrapShiftCells = function() {
1559    // only for wrap-around
1560    if ( !this.options.wrapAround ) {
1561      return;
1562    }
1563    // unshift previous cells
1564    this._unshiftCells( this.beforeShiftCells );
1565    this._unshiftCells( this.afterShiftCells );
1566    // get before cells
1567    // initial gap
1568    var gapX = this.cursorPosition;
1569    var cellIndex = this.cells.length - 1;
1570    this.beforeShiftCells = this._getGapCells( gapX, cellIndex, -1 );
1571    // get after cells
1572    // ending gap between last cell and end of gallery viewport
1573    gapX = this.size.innerWidth - this.cursorPosition;
1574    // start cloning at first cell, working forwards
1575    this.afterShiftCells = this._getGapCells( gapX, 0, 1 );
1576  };
1577  
1578  proto._getGapCells = function( gapX, cellIndex, increment ) {
1579    // keep adding cells until the cover the initial gap
1580    var cells = [];
1581    while ( gapX > 0 ) {
1582      var cell = this.cells[ cellIndex ];
1583      if ( !cell ) {
1584        break;
1585      }
1586      cells.push( cell );
1587      cellIndex += increment;
1588      gapX -= cell.size.outerWidth;
1589    }
1590    return cells;
1591  };
1592  
1593  // ----- contain ----- //
1594  
1595  // contain cell targets so no excess sliding
1596  proto._containSlides = function() {
1597    if ( !this.options.contain || this.options.wrapAround || !this.cells.length ) {
1598      return;
1599    }
1600    var isRightToLeft = this.options.rightToLeft;
1601    var beginMargin = isRightToLeft ? 'marginRight' : 'marginLeft';
1602    var endMargin = isRightToLeft ? 'marginLeft' : 'marginRight';
1603    var contentWidth = this.slideableWidth - this.getLastCell().size[ endMargin ];
1604    // content is less than gallery size
1605    var isContentSmaller = contentWidth < this.size.innerWidth;
1606    // bounds
1607    var beginBound = this.cursorPosition + this.cells[0].size[ beginMargin ];
1608    var endBound = contentWidth - this.size.innerWidth * ( 1 - this.cellAlign );
1609    // contain each cell target
1610    this.slides.forEach( function( slide ) {
1611      if ( isContentSmaller ) {
1612        // all cells fit inside gallery
1613        slide.target = contentWidth * this.cellAlign;
1614      } else {
1615        // contain to bounds
1616        slide.target = Math.max( slide.target, beginBound );
1617        slide.target = Math.min( slide.target, endBound );
1618      }
1619    }, this );
1620  };
1621  
1622  // -----  ----- //
1623  
1624  /**
1625   * emits events via eventEmitter and jQuery events
1626   * @param {String} type - name of event
1627   * @param {Event} event - original event
1628   * @param {Array} args - extra arguments
1629   */
1630  proto.dispatchEvent = function( type, event, args ) {
1631    var emitArgs = event ? [ event ].concat( args ) : args;
1632    this.emitEvent( type, emitArgs );
1633  
1634    if ( jQuery && this.$element ) {
1635      // default trigger with type if no event
1636      type += this.options.namespaceJQueryEvents ? '.flickity' : '';
1637      var $event = type;
1638      if ( event ) {
1639        // create jQuery event
1640        var jQEvent = jQuery.Event( event );
1641        jQEvent.type = type;
1642        $event = jQEvent;
1643      }
1644      this.$element.trigger( $event, args );
1645    }
1646  };
1647  
1648  // -------------------------- select -------------------------- //
1649  
1650  /**
1651   * @param {Integer} index - index of the slide
1652   * @param {Boolean} isWrap - will wrap-around to last/first if at the end
1653   * @param {Boolean} isInstant - will immediately set position at selected cell
1654   */
1655  proto.select = function( index, isWrap, isInstant ) {
1656    if ( !this.isActive ) {
1657      return;
1658    }
1659    index = parseInt( index, 10 );
1660    this._wrapSelect( index );
1661  
1662    if ( this.options.wrapAround || isWrap ) {
1663      index = utils.modulo( index, this.slides.length );
1664    }
1665    // bail if invalid index
1666    if ( !this.slides[ index ] ) {
1667      return;
1668    }
1669    var prevIndex = this.selectedIndex;
1670    this.selectedIndex = index;
1671    this.updateSelectedSlide();
1672    if ( isInstant ) {
1673      this.positionSliderAtSelected();
1674    } else {
1675      this.startAnimation();
1676    }
1677    if ( this.options.adaptiveHeight ) {
1678      this.setGallerySize();
1679    }
1680    // events
1681    this.dispatchEvent( 'select', null, [ index ] );
1682    // change event if new index
1683    if ( index != prevIndex ) {
1684      this.dispatchEvent( 'change', null, [ index ] );
1685    }
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
1691  proto._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  
1715  proto.previous = function( isWrap, isInstant ) {
1716    this.select( this.selectedIndex - 1, isWrap, isInstant );
1717  };
1718  
1719  proto.next = function( isWrap, isInstant ) {
1720    this.select( this.selectedIndex + 1, isWrap, isInstant );
1721  };
1722  
1723  proto.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  
1742  proto.unselectSelectedSlide = function() {
1743    if ( this.selectedSlide ) {
1744      this.selectedSlide.unselect();
1745    }
1746  };
1747  
1748  proto.selectInitialIndex = function() {
1749    var initialIndex = this.options.initialIndex;
1750    // already activated, select previous selectedIndex
1751    if ( this.isInitActivated ) {
1752      this.select( this.selectedIndex, false, true );
1753      return;
1754    }
1755    // select with selector string
1756    if ( initialIndex && typeof initialIndex == 'string' ) {
1757      var cell = this.queryCell( initialIndex );
1758      if ( cell ) {
1759        this.selectCell( initialIndex, false, true );
1760        return;
1761      }
1762    }
1763  
1764    var index = 0;
1765    // select with number
1766    if ( initialIndex && this.slides[ initialIndex ] ) {
1767      index = initialIndex;
1768    }
1769    // select instantly
1770    this.select( index, false, true );
1771  };
1772  
1773  /**
1774   * select slide from number or cell element
1775   * @param {Element or Number} elem
1776   */
1777  proto.selectCell = function( value, isWrap, isInstant ) {
1778    // get cell
1779    var cell = this.queryCell( value );
1780    if ( !cell ) {
1781      return;
1782    }
1783  
1784    var index = this.getCellSlideIndex( cell );
1785    this.select( index, isWrap, isInstant );
1786  };
1787  
1788  proto.getCellSlideIndex = function( cell ) {
1789    // get index of slides that has cell
1790    for ( var i=0; i < this.slides.length; i++ ) {
1791      var slide = this.slides[i];
1792      var index = slide.cells.indexOf( cell );
1793      if ( index != -1 ) {
1794        return i;
1795      }
1796    }
1797  };
1798  
1799  // -------------------------- get cells -------------------------- //
1800  
1801  /**
1802   * get Flickity.Cell, given an Element
1803   * @param {Element} elem
1804   * @returns {Flickity.Cell} item
1805   */
1806  proto.getCell = function( elem ) {
1807    // loop through cells to get the one that matches
1808    for ( var i=0; i < this.cells.length; i++ ) {
1809      var cell = this.cells[i];
1810      if ( cell.element == elem ) {
1811        return cell;
1812      }
1813    }
1814  };
1815  
1816  /**
1817   * get collection of Flickity.Cells, given Elements
1818   * @param {Element, Array, NodeList} elems
1819   * @returns {Array} cells - Flickity.Cells
1820   */
1821  proto.getCells = function( elems ) {
1822    elems = utils.makeArray( elems );
1823    var cells = [];
1824    elems.forEach( function( elem ) {
1825      var cell = this.getCell( elem );
1826      if ( cell ) {
1827        cells.push( cell );
1828      }
1829    }, this );
1830    return cells;
1831  };
1832  
1833  /**
1834   * get cell elements
1835   * @returns {Array} cellElems
1836   */
1837  proto.getCellElements = function() {
1838    return this.cells.map( function( cell ) {
1839      return cell.element;
1840    });
1841  };
1842  
1843  /**
1844   * get parent cell from an element
1845   * @param {Element} elem
1846   * @returns {Flickit.Cell} cell
1847   */
1848  proto.getParentCell = function( elem ) {
1849    // first check if elem is cell
1850    var cell = this.getCell( elem );
1851    if ( cell ) {
1852      return cell;
1853    }
1854    // try to get parent cell elem
1855    elem = utils.getParent( elem, '.flickity-slider > *' );
1856    return this.getCell( elem );
1857  };
1858  
1859  /**
1860   * get cells adjacent to a slide
1861   * @param {Integer} adjCount - number of adjacent slides
1862   * @param {Integer} index - index of slide to start
1863   * @returns {Array} cells - array of Flickity.Cells
1864   */
1865  proto.getAdjacentCellElements = function( adjCount, index ) {
1866    if ( !adjCount ) {
1867      return this.selectedSlide.getCellElements();
1868    }
1869    index = index === undefined ? this.selectedIndex : index;
1870  
1871    var len = this.slides.length;
1872    if ( 1 + ( adjCount * 2 ) >= len ) {
1873      return this.getCellElements();
1874    }
1875  
1876    var cellElems = [];
1877    for ( var i = index - adjCount; i <= index + adjCount ; i++ ) {
1878      var slideIndex = this.options.wrapAround ? utils.modulo( i, len ) : i;
1879      var slide = this.slides[ slideIndex ];
1880      if ( slide ) {
1881        cellElems = cellElems.concat( slide.getCellElements() );
1882      }
1883    }
1884    return cellElems;
1885  };
1886  
1887  /**
1888   * select slide from number or cell element
1889   * @param {Element, Selector String, or Number} selector
1890   */
1891  proto.queryCell = function( selector ) {
1892    if ( typeof selector == 'number' ) {
1893      // use number as index
1894      return this.cells[ selector ];
1895    }
1896    if ( typeof selector == 'string' ) {
1897      // do not select invalid selectors from hash: #123, #/. #791
1898      if ( selector.match(/^[#\.]?[\d\/]/) ) {
1899        return;
1900      }
1901      // use string as selector, get element
1902      selector = this.element.querySelector( selector );
1903    }
1904    // get cell from element
1905    return this.getCell( selector );
1906  };
1907  
1908  // -------------------------- events -------------------------- //
1909  
1910  proto.uiChange = function() {
1911    this.emitEvent('uiChange');
1912  };
1913  
1914  // keep focus on element when child UI elements are clicked
1915  proto.childUIPointerDown = function( event ) {
1916    // HACK iOS does not allow touch events to bubble up?!
1917    if ( event.type != 'touchstart' ) {
1918      event.preventDefault();
1919    }
1920    this.focus();
1921  };
1922  
1923  // ----- resize ----- //
1924  
1925  proto.onresize = function() {
1926    this.watchCSS();
1927    this.resize();
1928  };
1929  
1930  utils.debounceMethod( Flickity, 'onresize', 150 );
1931  
1932  proto.resize = function() {
1933    if ( !this.isActive ) {
1934      return;
1935    }
1936    this.getSize();
1937    // wrap values
1938    if ( this.options.wrapAround ) {
1939      this.x = utils.modulo( this.x, this.slideableWidth );
1940    }
1941    this.positionCells();
1942    this._getWrapShiftCells();
1943    this.setGallerySize();
1944    this.emitEvent('resize');
1945    // update selected index for group slides, instant
1946    // TODO: position can be lost between groups of various numbers
1947    var selectedElement = this.selectedElements && this.selectedElements[0];
1948    this.selectCell( selectedElement, false, true );
1949  };
1950  
1951  // watches the :after property, activates/deactivates
1952  proto.watchCSS = function() {
1953    var watchOption = this.options.watchCSS;
1954    if ( !watchOption ) {
1955      return;
1956    }
1957  
1958    var afterContent = getComputedStyle( this.element, ':after' ).content;
1959    // activate if :after { content: 'flickity' }
1960    if ( afterContent.indexOf('flickity') != -1 ) {
1961      this.activate();
1962    } else {
1963      this.deactivate();
1964    }
1965  };
1966  
1967  // ----- keydown ----- //
1968  
1969  // go previous/next if left/right keys pressed
1970  proto.onkeydown = function( event ) {
1971    // only work if element is in focus
1972    var isNotFocused = document.activeElement && document.activeElement != this.element;
1973    if ( !this.options.accessibility ||isNotFocused ) {
1974      return;
1975    }
1976  
1977    var handler = Flickity.keyboardHandlers[ event.keyCode ];
1978    if ( handler ) {
1979      handler.call( this );
1980    }
1981  };
1982  
1983  Flickity.keyboardHandlers = {
1984    // left arrow
1985    37: function() {
1986      var leftMethod = this.options.rightToLeft ? 'next' : 'previous';
1987      this.uiChange();
1988      this[ leftMethod ]();
1989    },
1990    // right arrow
1991    39: function() {
1992      var rightMethod = this.options.rightToLeft ? 'previous' : 'next';
1993      this.uiChange();
1994      this[ rightMethod ]();
1995    },
1996  };
1997  
1998  // ----- focus ----- //
1999  
2000  proto.focus = function() {
2001    // TODO remove scrollTo once focus options gets more support
2002    // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus#Browser_compatibility
2003    var prevScrollY = window.pageYOffset;
2004    this.element.focus({ preventScroll: true });
2005    // hack to fix scroll jump after focus, #76
2006    if ( window.pageYOffset != prevScrollY ) {
2007      window.scrollTo( window.pageXOffset, prevScrollY );
2008    }
2009  };
2010  
2011  // -------------------------- destroy -------------------------- //
2012  
2013  // deactivate all Flickity functionality, but keep stuff available
2014  proto.deactivate = function() {
2015    if ( !this.isActive ) {
2016      return;
2017    }
2018    this.element.classList.remove('flickity-enabled');
2019    this.element.classList.remove('flickity-rtl');
2020    this.unselectSelectedSlide();
2021    // destroy cells
2022    this.cells.forEach( function( cell ) {
2023      cell.destroy();
2024    });
2025    this.element.removeChild( this.viewport );
2026    // move child elements back into element
2027    moveElements( this.slider.children, this.element );
2028    if ( this.options.accessibility ) {
2029      this.element.removeAttribute('tabIndex');
2030      this.element.removeEventListener( 'keydown', this );
2031    }
2032    // set flags
2033    this.isActive = false;
2034    this.emitEvent('deactivate');
2035  };
2036  
2037  proto.destroy = function() {
2038    this.deactivate();
2039    window.removeEventListener( 'resize', this );
2040    this.allOff();
2041    this.emitEvent('destroy');
2042    if ( jQuery && this.$element ) {
2043      jQuery.removeData( this.element, 'flickity' );
2044    }
2045    delete this.element.flickityGUID;
2046    delete instances[ this.guid ];
2047  };
2048  
2049  // -------------------------- prototype -------------------------- //
2050  
2051  utils.extend( proto, animatePrototype );
2052  
2053  // -------------------------- extras -------------------------- //
2054  
2055  /**
2056   * get Flickity instance from element
2057   * @param {Element} elem
2058   * @returns {Flickity}
2059   */
2060  Flickity.data = function( elem ) {
2061    elem = utils.getQueryElement( elem );
2062    var id = elem && elem.flickityGUID;
2063    return id && instances[ id ];
2064  };
2065  
2066  utils.htmlInit( Flickity, 'flickity' );
2067  
2068  if ( jQuery && jQuery.bridget ) {
2069    jQuery.bridget( 'flickity', Flickity );
2070  }
2071  
2072  // set internal jQuery, for Webpack + jQuery v3, #478
2073  Flickity.setJQuery = function( jq ) {
2074    jQuery = jq;
2075  };
2076  
2077  Flickity.Cell = Cell;
2078  Flickity.Slide = Slide;
2079  
2080  return Flickity;
2081  
2082  }));
2083  
2084  /*!
2085   * Unipointer v2.3.0
2086   * base class for doing one thing with pointer event
2087   * MIT license
2088   */
2089  
2090  /*jshint browser: true, undef: true, unused: true, strict: true */
2091  
2092  ( function( window, factory ) {
2093    // universal module definition
2094    /* jshint strict: false */ /*global define, module, require */
2095    if ( typeof define == 'function' && define.amd ) {
2096      // AMD
2097      define( 'unipointer/unipointer',[
2098        'ev-emitter/ev-emitter'
2099      ], function( EvEmitter ) {
2100        return factory( window, EvEmitter );
2101      });
2102    } else if ( typeof module == 'object' && module.exports ) {
2103      // CommonJS
2104      module.exports = factory(
2105        window,
2106        require('ev-emitter')
2107      );
2108    } else {
2109      // browser global
2110      window.Unipointer = factory(
2111        window,
2112        window.EvEmitter
2113      );
2114    }
2115  
2116  }( window, function factory( window, EvEmitter ) {
2117  
2118  
2119  
2120  function noop() {}
2121  
2122  function Unipointer() {}
2123  
2124  // inherit EvEmitter
2125  var proto = Unipointer.prototype = Object.create( EvEmitter.prototype );
2126  
2127  proto.bindStartEvent = function( elem ) {
2128    this._bindStartEvent( elem, true );
2129  };
2130  
2131  proto.unbindStartEvent = function( elem ) {
2132    this._bindStartEvent( elem, false );
2133  };
2134  
2135  /**
2136   * Add or remove start event
2137   * @param {Boolean} isAdd - remove if falsey
2138   */
2139  proto._bindStartEvent = function( elem, isAdd ) {
2140    // munge isAdd, default to true
2141    isAdd = isAdd === undefined ? true : isAdd;
2142    var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener';
2143  
2144    // default to mouse events
2145    var startEvent = 'mousedown';
2146    if ( window.PointerEvent ) {
2147      // Pointer Events
2148      startEvent = 'pointerdown';
2149    } else if ( 'ontouchstart' in window ) {
2150      // Touch Events. iOS Safari
2151      startEvent = 'touchstart';
2152    }
2153    elem[ bindMethod ]( startEvent, this );
2154  };
2155  
2156  // trigger handler methods for events
2157  proto.handleEvent = function( event ) {
2158    var method = 'on' + event.type;
2159    if ( this[ method ] ) {
2160      this[ method ]( event );
2161    }
2162  };
2163  
2164  // returns the touch that we're keeping track of
2165  proto.getTouch = function( touches ) {
2166    for ( var i=0; i < touches.length; i++ ) {
2167      var touch = touches[i];
2168      if ( touch.identifier == this.pointerIdentifier ) {
2169        return touch;
2170      }
2171    }
2172  };
2173  
2174  // ----- start event ----- //
2175  
2176  proto.onmousedown = function( event ) {
2177    // dismiss clicks from right or middle buttons
2178    var button = event.button;
2179    if ( button && ( button !== 0 && button !== 1 ) ) {
2180      return;
2181    }
2182    this._pointerDown( event, event );
2183  };
2184  
2185  proto.ontouchstart = function( event ) {
2186    this._pointerDown( event, event.changedTouches[0] );
2187  };
2188  
2189  proto.onpointerdown = function( event ) {
2190    this._pointerDown( event, event );
2191  };
2192  
2193  /**
2194   * pointer start
2195   * @param {Event} event
2196   * @param {Event or Touch} pointer
2197   */
2198  proto._pointerDown = function( event, pointer ) {
2199    // dismiss right click and other pointers
2200    // button = 0 is okay, 1-4 not
2201    if ( event.button || this.isPointerDown ) {
2202      return;
2203    }
2204  
2205    this.isPointerDown = true;
2206    // save pointer identifier to match up touch events
2207    this.pointerIdentifier = pointer.pointerId !== undefined ?
2208      // pointerId for pointer events, touch.indentifier for touch events
2209      pointer.pointerId : pointer.identifier;
2210  
2211    this.pointerDown( event, pointer );
2212  };
2213  
2214  proto.pointerDown = function( event, pointer ) {
2215    this._bindPostStartEvents( event );
2216    this.emitEvent( 'pointerDown', [ event, pointer ] );
2217  };
2218  
2219  // hash of events to be bound after start event
2220  var postStartEvents = {
2221    mousedown: [ 'mousemove', 'mouseup' ],
2222    touchstart: [ 'touchmove', 'touchend', 'touchcancel' ],
2223    pointerdown: [ 'pointermove', 'pointerup', 'pointercancel' ],
2224  };
2225  
2226  proto._bindPostStartEvents = function( event ) {
2227    if ( !event ) {
2228      return;
2229    }
2230    // get proper events to match start event
2231    var events = postStartEvents[ event.type ];
2232    // bind events to node
2233    events.forEach( function( eventName ) {
2234      window.addEventListener( eventName, this );
2235    }, this );
2236    // save these arguments
2237    this._boundPointerEvents = events;
2238  };
2239  
2240  proto._unbindPostStartEvents = function() {
2241    // check for _boundEvents, in case dragEnd triggered twice (old IE8 bug)
2242    if ( !this._boundPointerEvents ) {
2243      return;
2244    }
2245    this._boundPointerEvents.forEach( function( eventName ) {
2246      window.removeEventListener( eventName, this );
2247    }, this );
2248  
2249    delete this._boundPointerEvents;
2250  };
2251  
2252  // ----- move event ----- //
2253  
2254  proto.onmousemove = function( event ) {
2255    this._pointerMove( event, event );
2256  };
2257  
2258  proto.onpointermove = function( event ) {
2259    if ( event.pointerId == this.pointerIdentifier ) {
2260      this._pointerMove( event, event );
2261    }
2262  };
2263  
2264  proto.ontouchmove = function( event ) {
2265    var touch = this.getTouch( event.changedTouches );
2266    if ( touch ) {
2267      this._pointerMove( event, touch );
2268    }
2269  };
2270  
2271  /**
2272   * pointer move
2273   * @param {Event} event
2274   * @param {Event or Touch} pointer
2275   * @private
2276   */
2277  proto._pointerMove = function( event, pointer ) {
2278    this.pointerMove( event, pointer );
2279  };
2280  
2281  // public
2282  proto.pointerMove = function( event, pointer ) {
2283    this.emitEvent( 'pointerMove', [ event, pointer ] );
2284  };
2285  
2286  // ----- end event ----- //
2287  
2288  
2289  proto.onmouseup = function( event ) {
2290    this._pointerUp( event, event );
2291  };
2292  
2293  proto.onpointerup = function( event ) {
2294    if ( event.pointerId == this.pointerIdentifier ) {
2295      this._pointerUp( event, event );
2296    }
2297  };
2298  
2299  proto.ontouchend = function( event ) {
2300    var touch = this.getTouch( event.changedTouches );
2301    if ( touch ) {
2302      this._pointerUp( event, touch );
2303    }
2304  };
2305  
2306  /**
2307   * pointer up
2308   * @param {Event} event
2309   * @param {Event or Touch} pointer
2310   * @private
2311   */
2312  proto._pointerUp = function( event, pointer ) {
2313    this._pointerDone();
2314    this.pointerUp( event, pointer );
2315  };
2316  
2317  // public
2318  proto.pointerUp = function( event, pointer ) {
2319    this.emitEvent( 'pointerUp', [ event, pointer ] );
2320  };
2321  
2322  // ----- pointer done ----- //
2323  
2324  // triggered on pointer up & pointer cancel
2325  proto._pointerDone = function() {
2326    this._pointerReset();
2327    this._unbindPostStartEvents();
2328    this.pointerDone();
2329  };
2330  
2331  proto._pointerReset = function() {
2332    // reset properties
2333    this.isPointerDown = false;
2334    delete this.pointerIdentifier;
2335  };
2336  
2337  proto.pointerDone = noop;
2338  
2339  // ----- pointer cancel ----- //
2340  
2341  proto.onpointercancel = function( event ) {
2342    if ( event.pointerId == this.pointerIdentifier ) {
2343      this._pointerCancel( event, event );
2344    }
2345  };
2346  
2347  proto.ontouchcancel = function( event ) {
2348    var touch = this.getTouch( event.changedTouches );
2349    if ( touch ) {
2350      this._pointerCancel( event, touch );
2351    }
2352  };
2353  
2354  /**
2355   * pointer cancel
2356   * @param {Event} event
2357   * @param {Event or Touch} pointer
2358   * @private
2359   */
2360  proto._pointerCancel = function( event, pointer ) {
2361    this._pointerDone();
2362    this.pointerCancel( event, pointer );
2363  };
2364  
2365  // public
2366  proto.pointerCancel = function( event, pointer ) {
2367    this.emitEvent( 'pointerCancel', [ event, pointer ] );
2368  };
2369  
2370  // -----  ----- //
2371  
2372  // utility function for getting x/y coords from event
2373  Unipointer.getPointerPoint = function( pointer ) {
2374    return {
2375      x: pointer.pageX,
2376      y: pointer.pageY
2377    };
2378  };
2379  
2380  // -----  ----- //
2381  
2382  return Unipointer;
2383  
2384  }));
2385  
2386  /*!
2387   * Unidragger v2.3.0
2388   * Draggable base class
2389   * MIT license
2390   */
2391  
2392  /*jshint browser: true, unused: true, undef: true, strict: true */
2393  
2394  ( function( window, factory ) {
2395    // universal module definition
2396    /*jshint strict: false */ /*globals define, module, require */
2397  
2398    if ( typeof define == 'function' && define.amd ) {
2399      // AMD
2400      define( 'unidragger/unidragger',[
2401        'unipointer/unipointer'
2402      ], function( Unipointer ) {
2403        return factory( window, Unipointer );
2404      });
2405    } else if ( typeof module == 'object' && module.exports ) {
2406      // CommonJS
2407      module.exports = factory(
2408        window,
2409        require('unipointer')
2410      );
2411    } else {
2412      // browser global
2413      window.Unidragger = factory(
2414        window,
2415        window.Unipointer
2416      );
2417    }
2418  
2419  }( window, function factory( window, Unipointer ) {
2420  
2421  
2422  
2423  // -------------------------- Unidragger -------------------------- //
2424  
2425  function Unidragger() {}
2426  
2427  // inherit Unipointer & EvEmitter
2428  var proto = Unidragger.prototype = Object.create( Unipointer.prototype );
2429  
2430  // ----- bind start ----- //
2431  
2432  proto.bindHandles = function() {
2433    this._bindHandles( true );
2434  };
2435  
2436  proto.unbindHandles = function() {
2437    this._bindHandles( false );
2438  };
2439  
2440  /**
2441   * Add or remove start event
2442   * @param {Boolean} isAdd
2443   */
2444  proto._bindHandles = function( isAdd ) {
2445    // munge isAdd, default to true
2446    isAdd = isAdd === undefined ? true : isAdd;
2447    // bind each handle
2448    var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener';
2449    var touchAction = isAdd ? this._touchActionValue : '';
2450    for ( var i=0; i < this.handles.length; i++ ) {
2451      var handle = this.handles[i];
2452      this._bindStartEvent( handle, isAdd );
2453      handle[ bindMethod ]( 'click', this );
2454      // touch-action: none to override browser touch gestures. metafizzy/flickity#540
2455      if ( window.PointerEvent ) {
2456        handle.style.touchAction = touchAction;
2457      }
2458    }
2459  };
2460  
2461  // prototype so it can be overwriteable by Flickity
2462  proto._touchActionValue = 'none';
2463  
2464  // ----- start event ----- //
2465  
2466  /**
2467   * pointer start
2468   * @param {Event} event
2469   * @param {Event or Touch} pointer
2470   */
2471  proto.pointerDown = function( event, pointer ) {
2472    var isOkay = this.okayPointerDown( event );
2473    if ( !isOkay ) {
2474      return;
2475    }
2476    // track start event position
2477    this.pointerDownPointer = pointer;
2478  
2479    event.preventDefault();
2480    this.pointerDownBlur();
2481    // bind move and end events
2482    this._bindPostStartEvents( event );
2483    this.emitEvent( 'pointerDown', [ event, pointer ] );
2484  };
2485  
2486  // nodes that have text fields
2487  var cursorNodes = {
2488    TEXTAREA: true,
2489    INPUT: true,
2490    SELECT: true,
2491    OPTION: true,
2492  };
2493  
2494  // input types that do not have text fields
2495  var clickTypes = {
2496    radio: true,
2497    checkbox: true,
2498    button: true,
2499    submit: true,
2500    image: true,
2501    file: true,
2502  };
2503  
2504  // dismiss inputs with text fields. flickity#403, flickity#404
2505  proto.okayPointerDown = function( event ) {
2506    var isCursorNode = cursorNodes[ event.target.nodeName ];
2507    var isClickType = clickTypes[ event.target.type ];
2508    var isOkay = !isCursorNode || isClickType;
2509    if ( !isOkay ) {
2510      this._pointerReset();
2511    }
2512    return isOkay;
2513  };
2514  
2515  // kludge to blur previously focused input
2516  proto.pointerDownBlur = function() {
2517    var focused = document.activeElement;
2518    // do not blur body for IE10, metafizzy/flickity#117
2519    var canBlur = focused && focused.blur && focused != document.body;
2520    if ( canBlur ) {
2521      focused.blur();
2522    }
2523  };
2524  
2525  // ----- move event ----- //
2526  
2527  /**
2528   * drag move
2529   * @param {Event} event
2530   * @param {Event or Touch} pointer
2531   */
2532  proto.pointerMove = function( event, pointer ) {
2533    var moveVector = this._dragPointerMove( event, pointer );
2534    this.emitEvent( 'pointerMove', [ event, pointer, moveVector ] );
2535    this._dragMove( event, pointer, moveVector );
2536  };
2537  
2538  // base pointer move logic
2539  proto._dragPointerMove = function( event, pointer ) {
2540    var moveVector = {
2541      x: pointer.pageX - this.pointerDownPointer.pageX,
2542      y: pointer.pageY - this.pointerDownPointer.pageY
2543    };
2544    // start drag if pointer has moved far enough to start drag
2545    if ( !this.isDragging && this.hasDragStarted( moveVector ) ) {
2546      this._dragStart( event, pointer );
2547    }
2548    return moveVector;
2549  };
2550  
2551  // condition if pointer has moved far enough to start drag
2552  proto.hasDragStarted = function( moveVector ) {
2553    return Math.abs( moveVector.x ) > 3 || Math.abs( moveVector.y ) > 3;
2554  };
2555  
2556  // ----- end event ----- //
2557  
2558  /**
2559   * pointer up
2560   * @param {Event} event
2561   * @param {Event or Touch} pointer
2562   */
2563  proto.pointerUp = function( event, pointer ) {
2564    this.emitEvent( 'pointerUp', [ event, pointer ] );
2565    this._dragPointerUp( event, pointer );
2566  };
2567  
2568  proto._dragPointerUp = function( event, pointer ) {
2569    if ( this.isDragging ) {
2570      this._dragEnd( event, pointer );
2571    } else {
2572      // pointer didn't move enough for drag to start
2573      this._staticClick( event, pointer );
2574    }
2575  };
2576  
2577  // -------------------------- drag -------------------------- //
2578  
2579  // dragStart
2580  proto._dragStart = function( event, pointer ) {
2581    this.isDragging = true;
2582    // prevent clicks
2583    this.isPreventingClicks = true;
2584    this.dragStart( event, pointer );
2585  };
2586  
2587  proto.dragStart = function( event, pointer ) {
2588    this.emitEvent( 'dragStart', [ event, pointer ] );
2589  };
2590  
2591  // dragMove
2592  proto._dragMove = function( event, pointer, moveVector ) {
2593    // do not drag if not dragging yet
2594    if ( !this.isDragging ) {
2595      return;
2596    }
2597  
2598    this.dragMove( event, pointer, moveVector );
2599  };
2600  
2601  proto.dragMove = function( event, pointer, moveVector ) {
2602    event.preventDefault();
2603    this.emitEvent( 'dragMove', [ event, pointer, moveVector ] );
2604  };
2605  
2606  // dragEnd
2607  proto._dragEnd = function( event, pointer ) {
2608    // set flags
2609    this.isDragging = false;
2610    // re-enable clicking async
2611    setTimeout( function() {
2612      delete this.isPreventingClicks;
2613    }.bind( this ) );
2614  
2615    this.dragEnd( event, pointer );
2616  };
2617  
2618  proto.dragEnd = function( event, pointer ) {
2619    this.emitEvent( 'dragEnd', [ event, pointer ] );
2620  };
2621  
2622  // ----- onclick ----- //
2623  
2624  // handle all clicks and prevent clicks when dragging
2625  proto.onclick = function( event ) {
2626    if ( this.isPreventingClicks ) {
2627      event.preventDefault();
2628    }
2629  };
2630  
2631  // ----- staticClick ----- //
2632  
2633  // triggered after pointer down & up with no/tiny movement
2634  proto._staticClick = function( event, pointer ) {
2635    // ignore emulated mouse up clicks
2636    if ( this.isIgnoringMouseUp && event.type == 'mouseup' ) {
2637      return;
2638    }
2639  
2640    this.staticClick( event, pointer );
2641  
2642    // set flag for emulated clicks 300ms after touchend
2643    if ( event.type != 'mouseup' ) {
2644      this.isIgnoringMouseUp = true;
2645      // reset flag after 300ms
2646      setTimeout( function() {
2647        delete this.isIgnoringMouseUp;
2648      }.bind( this ), 400 );
2649    }
2650  };
2651  
2652  proto.staticClick = function( event, pointer ) {
2653    this.emitEvent( 'staticClick', [ event, pointer ] );
2654  };
2655  
2656  // ----- utils ----- //
2657  
2658  Unidragger.getPointerPoint = Unipointer.getPointerPoint;
2659  
2660  // -----  ----- //
2661  
2662  return Unidragger;
2663  
2664  }));
2665  
2666  // drag
2667  ( function( window, factory ) {
2668    // universal module definition
2669    /* jshint strict: false */
2670    if ( typeof define == 'function' && define.amd ) {
2671      // AMD
2672      define( 'flickity/js/drag',[
2673        './flickity',
2674        'unidragger/unidragger',
2675        'fizzy-ui-utils/utils'
2676      ], function( Flickity, Unidragger, utils ) {
2677        return factory( window, Flickity, Unidragger, utils );
2678      });
2679    } else if ( typeof module == 'object' && module.exports ) {
2680      // CommonJS
2681      module.exports = factory(
2682        window,
2683        require('./flickity'),
2684        require('unidragger'),
2685        require('fizzy-ui-utils')
2686      );
2687    } else {
2688      // browser global
2689      window.Flickity = factory(
2690        window,
2691        window.Flickity,
2692        window.Unidragger,
2693        window.fizzyUIUtils
2694      );
2695    }
2696  
2697  }( window, function factory( window, Flickity, Unidragger, utils ) {
2698  
2699  
2700  
2701  // ----- defaults ----- //
2702  
2703  utils.extend( Flickity.defaults, {
2704    draggable: '>1',
2705    dragThreshold: 3,
2706  });
2707  
2708  // ----- create ----- //
2709  
2710  Flickity.createMethods.push('_createDrag');
2711  
2712  // -------------------------- drag prototype -------------------------- //
2713  
2714  var proto = Flickity.prototype;
2715  utils.extend( proto, Unidragger.prototype );
2716  proto._touchActionValue = 'pan-y';
2717  
2718  // --------------------------  -------------------------- //
2719  
2720  var isTouch = 'createTouch' in document;
2721  var isTouchmoveScrollCanceled = false;
2722  
2723  proto._createDrag = function() {
2724    this.on( 'activate', this.onActivateDrag );
2725    this.on( 'uiChange', this._uiChangeDrag );
2726    this.on( 'deactivate', this.onDeactivateDrag );
2727    this.on( 'cellChange', this.updateDraggable );
2728    // TODO updateDraggable on resize? if groupCells & slides change
2729    // HACK - add seemingly innocuous handler to fix iOS 10 scroll behavior
2730    // #457, RubaXa/Sortable#973
2731    if ( isTouch && !isTouchmoveScrollCanceled ) {
2732      window.addEventListener( 'touchmove', function() {});
2733      isTouchmoveScrollCanceled = true;
2734    }
2735  };
2736  
2737  proto.onActivateDrag = function() {
2738    this.handles = [ this.viewport ];
2739    this.bindHandles();
2740    this.updateDraggable();
2741  };
2742  
2743  proto.onDeactivateDrag = function() {
2744    this.unbindHandles();
2745    this.element.classList.remove('is-draggable');
2746  };
2747  
2748  proto.updateDraggable = function() {
2749    // disable dragging if less than 2 slides. #278
2750    if ( this.options.draggable == '>1' ) {
2751      this.isDraggable = this.slides.length > 1;
2752    } else {
2753      this.isDraggable = this.options.draggable;
2754    }
2755    if ( this.isDraggable ) {
2756      this.element.classList.add('is-draggable');
2757    } else {
2758      this.element.classList.remove('is-draggable');
2759    }
2760  };
2761  
2762  // backwards compatibility
2763  proto.bindDrag = function() {
2764    this.options.draggable = true;
2765    this.updateDraggable();
2766  };
2767  
2768  proto.unbindDrag = function() {
2769    this.options.draggable = false;
2770    this.updateDraggable();
2771  };
2772  
2773  proto._uiChangeDrag = function() {
2774    delete this.isFreeScrolling;
2775  };
2776  
2777  // -------------------------- pointer events -------------------------- //
2778  
2779  proto.pointerDown = function( event, pointer ) {
2780    if ( !this.isDraggable ) {
2781      this._pointerDownDefault( event, pointer );
2782      return;
2783    }
2784    var isOkay = this.okayPointerDown( event );
2785    if ( !isOkay ) {
2786      return;
2787    }
2788  
2789    this._pointerDownPreventDefault( event );
2790    this.pointerDownFocus( event );
2791    // blur
2792    if ( document.activeElement != this.element ) {
2793      // do not blur if already focused
2794      this.pointerDownBlur();
2795    }
2796  
2797    // stop if it was moving
2798    this.dragX = this.x;
2799    this.viewport.classList.add('is-pointer-down');
2800    // track scrolling
2801    this.pointerDownScroll = getScrollPosition();
2802    window.addEventListener( 'scroll', this );
2803  
2804    this._pointerDownDefault( event, pointer );
2805  };
2806  
2807  // default pointerDown logic, used for staticClick
2808  proto._pointerDownDefault = function( event, pointer ) {
2809    // track start event position
2810    // Safari 9 overrides pageX and pageY. These values needs to be copied. #779
2811    this.pointerDownPointer = {
2812      pageX: pointer.pageX,
2813      pageY: pointer.pageY,
2814    };
2815    // bind move and end events
2816    this._bindPostStartEvents( event );
2817    this.dispatchEvent( 'pointerDown', event, [ pointer ] );
2818  };
2819  
2820  var focusNodes = {
2821    INPUT: true,
2822    TEXTAREA: true,
2823    SELECT: true,
2824  };
2825  
2826  proto.pointerDownFocus = function( event ) {
2827    var isFocusNode = focusNodes[ event.target.nodeName ];
2828    if ( !isFocusNode ) {
2829      this.focus();
2830    }
2831  };
2832  
2833  proto._pointerDownPreventDefault = function( event ) {
2834    var isTouchStart = event.type == 'touchstart';
2835    var isTouchPointer = event.pointerType == 'touch';
2836    var isFocusNode = focusNodes[ event.target.nodeName ];
2837    if ( !isTouchStart && !isTouchPointer && !isFocusNode ) {
2838      event.preventDefault();
2839    }
2840  };
2841  
2842  // ----- move ----- //
2843  
2844  proto.hasDragStarted = function( moveVector ) {
2845    return Math.abs( moveVector.x ) > this.options.dragThreshold;
2846  };
2847  
2848  // ----- up ----- //
2849  
2850  proto.pointerUp = function( event, pointer ) {
2851    delete this.isTouchScrolling;
2852    this.viewport.classList.remove('is-pointer-down');
2853    this.dispatchEvent( 'pointerUp', event, [ pointer ] );
2854    this._dragPointerUp( event, pointer );
2855  };
2856  
2857  proto.pointerDone = function() {
2858    window.removeEventListener( 'scroll', this );
2859    delete this.pointerDownScroll;
2860  };
2861  
2862  // -------------------------- dragging -------------------------- //
2863  
2864  proto.dragStart = function( event, pointer ) {
2865    if ( !this.isDraggable ) {
2866      return;
2867    }
2868    this.dragStartPosition = this.x;
2869    this.startAnimation();
2870    window.removeEventListener( 'scroll', this );
2871    this.dispatchEvent( 'dragStart', event, [ pointer ] );
2872  };
2873  
2874  proto.pointerMove = function( event, pointer ) {
2875    var moveVector = this._dragPointerMove( event, pointer );
2876    this.dispatchEvent( 'pointerMove', event, [ pointer, moveVector ] );
2877    this._dragMove( event, pointer, moveVector );
2878  };
2879  
2880  proto.dragMove = function( event, pointer, moveVector ) {
2881    if ( !this.isDraggable ) {
2882      return;
2883    }
2884    event.preventDefault();
2885  
2886    this.previousDragX = this.dragX;
2887    // reverse if right-to-left
2888    var direction = this.options.rightToLeft ? -1 : 1;
2889    if ( this.options.wrapAround ) {
2890      // wrap around move. #589
2891      moveVector.x = moveVector.x % this.slideableWidth;
2892    }
2893    var dragX = this.dragStartPosition + moveVector.x * direction;
2894  
2895    if ( !this.options.wrapAround && this.slides.length ) {
2896      // slow drag
2897      var originBound = Math.max( -this.slides[0].target, this.dragStartPosition );
2898      dragX = dragX > originBound ? ( dragX + originBound ) * 0.5 : dragX;
2899      var endBound = Math.min( -this.getLastSlide().target, this.dragStartPosition );
2900      dragX = dragX < endBound ? ( dragX + endBound ) * 0.5 : dragX;
2901    }
2902  
2903    this.dragX = dragX;
2904  
2905    this.dragMoveTime = new Date();
2906    this.dispatchEvent( 'dragMove', event, [ pointer, moveVector ] );
2907  };
2908  
2909  proto.dragEnd = function( event, pointer ) {
2910    if ( !this.isDraggable ) {
2911      return;
2912    }
2913    if ( this.options.freeScroll ) {
2914      this.isFreeScrolling = true;
2915    }
2916    // set selectedIndex based on where flick will end up
2917    var index = this.dragEndRestingSelect();
2918  
2919    if ( this.options.freeScroll && !this.options.wrapAround ) {
2920      // if free-scroll & not wrap around
2921      // do not free-scroll if going outside of bounding slides
2922      // so bounding slides can attract slider, and keep it in bounds
2923      var restingX = this.getRestingPosition();
2924      this.isFreeScrolling = -restingX > this.slides[0].target &&
2925        -restingX < this.getLastSlide().target;
2926    } else if ( !this.options.freeScroll && index == this.selectedIndex ) {
2927      // boost selection if selected index has not changed
2928      index += this.dragEndBoostSelect();
2929    }
2930    delete this.previousDragX;
2931    // apply selection
2932    // TODO refactor this, selecting here feels weird
2933    // HACK, set flag so dragging stays in correct direction
2934    this.isDragSelect = this.options.wrapAround;
2935    this.select( index );
2936    delete this.isDragSelect;
2937    this.dispatchEvent( 'dragEnd', event, [ pointer ] );
2938  };
2939  
2940  proto.dragEndRestingSelect = function() {
2941    var restingX = this.getRestingPosition();
2942    // how far away from selected slide
2943    var distance = Math.abs( this.getSlideDistance( -restingX, this.selectedIndex ) );
2944    // get closet resting going up and going down
2945    var positiveResting = this._getClosestResting( restingX, distance, 1 );
2946    var negativeResting = this._getClosestResting( restingX, distance, -1 );
2947    // use closer resting for wrap-around
2948    var index = positiveResting.distance < negativeResting.distance ?
2949      positiveResting.index : negativeResting.index;
2950    return index;
2951  };
2952  
2953  /**
2954   * given resting X and distance to selected cell
2955   * get the distance and index of the closest cell
2956   * @param {Number} restingX - estimated post-flick resting position
2957   * @param {Number} distance - distance to selected cell
2958   * @param {Integer} increment - +1 or -1, going up or down
2959   * @returns {Object} - { distance: {Number}, index: {Integer} }
2960   */
2961  proto._getClosestResting = function( restingX, distance, increment ) {
2962    var index = this.selectedIndex;
2963    var minDistance = Infinity;
2964    var condition = this.options.contain && !this.options.wrapAround ?
2965      // if contain, keep going if distance is equal to minDistance
2966      function( d, md ) { return d <= md; } : function( d, md ) { return d < md; };
2967    while ( condition( distance, minDistance ) ) {
2968      // measure distance to next cell
2969      index += increment;
2970      minDistance = distance;
2971      distance = this.getSlideDistance( -restingX, index );
2972      if ( distance === null ) {
2973        break;
2974      }
2975      distance = Math.abs( distance );
2976    }
2977    return {
2978      distance: minDistance,
2979      // selected was previous index
2980      index: index - increment
2981    };
2982  };
2983  
2984  /**
2985   * measure distance between x and a slide target
2986   * @param {Number} x
2987   * @param {Integer} index - slide index
2988   */
2989  proto.getSlideDistance = function( x, index ) {
2990    var len = this.slides.length;
2991    // wrap around if at least 2 slides
2992    var isWrapAround = this.options.wrapAround && len > 1;
2993    var slideIndex = isWrapAround ? utils.modulo( index, len ) : index;
2994    var slide = this.slides[ slideIndex ];
2995    if ( !slide ) {
2996      return null;
2997    }
2998    // add distance for wrap-around slides
2999    var wrap = isWrapAround ? this.slideableWidth * Math.floor( index / len ) : 0;
3000    return x - ( slide.target + wrap );
3001  };
3002  
3003  proto.dragEndBoostSelect = function() {
3004    // do not boost if no previousDragX or dragMoveTime
3005    if ( this.previousDragX === undefined || !this.dragMoveTime ||
3006      // or if drag was held for 100 ms
3007      new Date() - this.dragMoveTime > 100 ) {
3008      return 0;
3009    }
3010  
3011    var distance = this.getSlideDistance( -this.dragX, this.selectedIndex );
3012    var delta = this.previousDragX - this.dragX;
3013    if ( distance > 0 && delta > 0 ) {
3014      // boost to next if moving towards the right, and positive velocity
3015      return 1;
3016    } else if ( distance < 0 && delta < 0 ) {
3017      // boost to previous if moving towards the left, and negative velocity
3018      return -1;
3019    }
3020    return 0;
3021  };
3022  
3023  // ----- staticClick ----- //
3024  
3025  proto.staticClick = function( event, pointer ) {
3026    // get clickedCell, if cell was clicked
3027    var clickedCell = this.getParentCell( event.target );
3028    var cellElem = clickedCell && clickedCell.element;
3029    var cellIndex = clickedCell && this.cells.indexOf( clickedCell );
3030    this.dispatchEvent( 'staticClick', event, [ pointer, cellElem, cellIndex ] );
3031  };
3032  
3033  // ----- scroll ----- //
3034  
3035  proto.onscroll = function() {
3036    var scroll = getScrollPosition();
3037    var scrollMoveX = this.pointerDownScroll.x - scroll.x;
3038    var scrollMoveY = this.pointerDownScroll.y - scroll.y;
3039    // cancel click/tap if scroll is too much
3040    if ( Math.abs( scrollMoveX ) > 3 || Math.abs( scrollMoveY ) > 3 ) {
3041      this._pointerDone();
3042    }
3043  };
3044  
3045  // ----- utils ----- //
3046  
3047  function getScrollPosition() {
3048    return {
3049      x: window.pageXOffset,
3050      y: window.pageYOffset
3051    };
3052  }
3053  
3054  // -----  ----- //
3055  
3056  return Flickity;
3057  
3058  }));
3059  
3060  // prev/next buttons
3061  ( function( window, factory ) {
3062    // universal module definition
3063    /* jshint strict: false */
3064    if ( typeof define == 'function' && define.amd ) {
3065      // AMD
3066      define( 'flickity/js/prev-next-button',[
3067        './flickity',
3068        'unipointer/unipointer',
3069        'fizzy-ui-utils/utils'
3070      ], function( Flickity, Unipointer, utils ) {
3071        return factory( window, Flickity, Unipointer, utils );
3072      });
3073    } else if ( typeof module == 'object' && module.exports ) {
3074      // CommonJS
3075      module.exports = factory(
3076        window,
3077        require('./flickity'),
3078        require('unipointer'),
3079        require('fizzy-ui-utils')
3080      );
3081    } else {
3082      // browser global
3083      factory(
3084        window,
3085        window.Flickity,
3086        window.Unipointer,
3087        window.fizzyUIUtils
3088      );
3089    }
3090  
3091  }( window, function factory( window, Flickity, Unipointer, utils ) {
3092  'use strict';
3093  
3094  var svgURI = 'http://www.w3.org/2000/svg';
3095  
3096  // -------------------------- PrevNextButton -------------------------- //
3097  
3098  function PrevNextButton( direction, parent ) {
3099    this.direction = direction;
3100    this.parent = parent;
3101    this._create();
3102  }
3103  
3104  PrevNextButton.prototype = Object.create( Unipointer.prototype );
3105  
3106  PrevNextButton.prototype._create = function() {
3107    // properties
3108    this.isEnabled = true;
3109    this.isPrevious = this.direction == -1;
3110    var leftDirection = this.parent.options.rightToLeft ? 1 : -1;
3111    this.isLeft = this.direction == leftDirection;
3112  
3113    var element = this.element = document.createElement('button');
3114    element.className = 'flickity-button flickity-prev-next-button';
3115    element.className += this.isPrevious ? ' previous' : ' next';
3116    // prevent button from submitting form http://stackoverflow.com/a/10836076/182183
3117    element.setAttribute( 'type', 'button' );
3118    // init as disabled
3119    this.disable();
3120  
3121    element.setAttribute( 'aria-label', this.isPrevious ? 'Previous' : 'Next' );
3122  
3123    // create arrow
3124    var svg = this.createSVG();
3125    element.appendChild( svg );
3126    // events
3127    this.parent.on( 'select', this.update.bind( this ) );
3128    this.on( 'pointerDown', this.parent.childUIPointerDown.bind( this.parent ) );
3129  };
3130  
3131  PrevNextButton.prototype.activate = function() {
3132    this.bindStartEvent( this.element );
3133    this.element.addEventListener( 'click', this );
3134    // add to DOM
3135    this.parent.element.appendChild( this.element );
3136  };
3137  
3138  PrevNextButton.prototype.deactivate = function() {
3139    // remove from DOM
3140    this.parent.element.removeChild( this.element );
3141    // click events
3142    this.unbindStartEvent( this.element );
3143    this.element.removeEventListener( 'click', this );
3144  };
3145  
3146  PrevNextButton.prototype.createSVG = function() {
3147    var svg = document.createElementNS( svgURI, 'svg');
3148    svg.setAttribute( 'class', 'flickity-button-icon' );
3149    svg.setAttribute( 'viewBox', '0 0 100 100' );
3150    var path = document.createElementNS( svgURI, 'path');
3151    var pathMovements = getArrowMovements( this.parent.options.arrowShape );
3152    path.setAttribute( 'd', pathMovements );
3153    path.setAttribute( 'class', 'arrow' );
3154    // rotate arrow
3155    if ( !this.isLeft ) {
3156      path.setAttribute( 'transform', 'translate(100, 100) rotate(180) ' );
3157    }
3158    svg.appendChild( path );
3159    return svg;
3160  };
3161  
3162  // get SVG path movmement
3163  function getArrowMovements( shape ) {
3164    // use shape as movement if string
3165    if ( typeof shape == 'string' ) {
3166      return shape;
3167    }
3168    // create movement string
3169    return 'M ' + shape.x0 + ',50' +
3170      ' L ' + shape.x1 + ',' + ( shape.y1 + 50 ) +
3171      ' L ' + shape.x2 + ',' + ( shape.y2 + 50 ) +
3172      ' L ' + shape.x3 + ',50 ' +
3173      ' L ' + shape.x2 + ',' + ( 50 - shape.y2 ) +
3174      ' L ' + shape.x1 + ',' + ( 50 - shape.y1 ) +
3175      ' Z';
3176  }
3177  
3178  PrevNextButton.prototype.handleEvent = utils.handleEvent;
3179  
3180  PrevNextButton.prototype.onclick = function() {
3181    if ( !this.isEnabled ) {
3182      return;
3183    }
3184    this.parent.uiChange();
3185    var method = this.isPrevious ? 'previous' : 'next';
3186    this.parent[ method ]();
3187  };
3188  
3189  // -----  ----- //
3190  
3191  PrevNextButton.prototype.enable = function() {
3192    if ( this.isEnabled ) {
3193      return;
3194    }
3195    this.element.disabled = false;
3196    this.isEnabled = true;
3197  };
3198  
3199  PrevNextButton.prototype.disable = function() {
3200    if ( !this.isEnabled ) {
3201      return;
3202    }
3203    this.element.disabled = true;
3204    this.isEnabled = false;
3205  };
3206  
3207  PrevNextButton.prototype.update = function() {
3208    // index of first or last slide, if previous or next
3209    var slides = this.parent.slides;
3210    // enable is wrapAround and at least 2 slides
3211    if ( this.parent.options.wrapAround && slides.length > 1 ) {
3212      this.enable();
3213      return;
3214    }
3215    var lastIndex = slides.length ? slides.length - 1 : 0;
3216    var boundIndex = this.isPrevious ? 0 : lastIndex;
3217    var method = this.parent.selectedIndex == boundIndex ? 'disable' : 'enable';
3218    this[ method ]();
3219  };
3220  
3221  PrevNextButton.prototype.destroy = function() {
3222    this.deactivate();
3223    this.allOff();
3224  };
3225  
3226  // -------------------------- Flickity prototype -------------------------- //
3227  
3228  utils.extend( Flickity.defaults, {
3229    prevNextButtons: true,
3230    arrowShape: {
3231      x0: 10,
3232      x1: 60, y1: 50,
3233      x2: 70, y2: 40,
3234      x3: 30
3235    }
3236  });
3237  
3238  Flickity.createMethods.push('_createPrevNextButtons');
3239  var proto = Flickity.prototype;
3240  
3241  proto._createPrevNextButtons = function() {
3242    if ( !this.options.prevNextButtons ) {
3243      return;
3244    }
3245  
3246    this.prevButton = new PrevNextButton( -1, this );
3247    this.nextButton = new PrevNextButton( 1, this );
3248  
3249    this.on( 'activate', this.activatePrevNextButtons );
3250  };
3251  
3252  proto.activatePrevNextButtons = function() {
3253    this.prevButton.activate();
3254    this.nextButton.activate();
3255    this.on( 'deactivate', this.deactivatePrevNextButtons );
3256  };
3257  
3258  proto.deactivatePrevNextButtons = function() {
3259    this.prevButton.deactivate();
3260    this.nextButton.deactivate();
3261    this.off( 'deactivate', this.deactivatePrevNextButtons );
3262  };
3263  
3264  // --------------------------  -------------------------- //
3265  
3266  Flickity.PrevNextButton = PrevNextButton;
3267  
3268  return Flickity;
3269  
3270  }));
3271  
3272  // page dots
3273  ( function( window, factory ) {
3274    // universal module definition
3275    /* jshint strict: false */
3276    if ( typeof define == 'function' && define.amd ) {
3277      // AMD
3278      define( 'flickity/js/page-dots',[
3279        './flickity',
3280        'unipointer/unipointer',
3281        'fizzy-ui-utils/utils'
3282      ], function( Flickity, Unipointer, utils ) {
3283        return factory( window, Flickity, Unipointer, utils );
3284      });
3285    } else if ( typeof module == 'object' && module.exports ) {
3286      // CommonJS
3287      module.exports = factory(
3288        window,
3289        require('./flickity'),
3290        require('unipointer'),
3291        require('fizzy-ui-utils')
3292      );
3293    } else {
3294      // browser global
3295      factory(
3296        window,
3297        window.Flickity,
3298        window.Unipointer,
3299        window.fizzyUIUtils
3300      );
3301    }
3302  
3303  }( window, function factory( window, Flickity, Unipointer, utils ) {
3304  
3305  // -------------------------- PageDots -------------------------- //
3306  
3307  
3308  
3309  function PageDots( parent ) {
3310    this.parent = parent;
3311    this._create();
3312  }
3313  
3314  PageDots.prototype = Object.create( Unipointer.prototype );
3315  
3316  PageDots.prototype._create = function() {
3317    // create holder element
3318    this.holder = document.createElement('ol');
3319    this.holder.className = 'flickity-page-dots';
3320    // create dots, array of elements
3321    this.dots = [];
3322    // events
3323    this.handleClick = this.onClick.bind( this );
3324    this.on( 'pointerDown', this.parent.childUIPointerDown.bind( this.parent ) );
3325  };
3326  
3327  PageDots.prototype.activate = function() {
3328    this.setDots();
3329    this.holder.addEventListener( 'click', this.handleClick );
3330    this.bindStartEvent( this.holder );
3331    // add to DOM
3332    this.parent.element.appendChild( this.holder );
3333  };
3334  
3335  PageDots.prototype.deactivate = function() {
3336    this.holder.removeEventListener( 'click', this.handleClick );
3337    this.unbindStartEvent( this.holder );
3338    // remove from DOM
3339    this.parent.element.removeChild( this.holder );
3340  };
3341  
3342  PageDots.prototype.setDots = function() {
3343    // get difference between number of slides and number of dots
3344    var delta = this.parent.slides.length - this.dots.length;
3345    if ( delta > 0 ) {
3346      this.addDots( delta );
3347    } else if ( delta < 0 ) {
3348      this.removeDots( -delta );
3349    }
3350  };
3351  
3352  PageDots.prototype.addDots = function( count ) {
3353    var fragment = document.createDocumentFragment();
3354    var newDots = [];
3355    var length = this.dots.length;
3356    var max = length + count;
3357  
3358    for ( var i = length; i < max; i++ ) {
3359      var dot = document.createElement('li');
3360      dot.className = 'dot';
3361      dot.setAttribute( 'aria-label', 'Page dot ' + ( i + 1 ) );
3362      fragment.appendChild( dot );
3363      newDots.push( dot );
3364    }
3365  
3366    this.holder.appendChild( fragment );
3367    this.dots = this.dots.concat( newDots );
3368  };
3369  
3370  PageDots.prototype.removeDots = function( count ) {
3371    // remove from this.dots collection
3372    var removeDots = this.dots.splice( this.dots.length - count, count );
3373    // remove from DOM
3374    removeDots.forEach( function( dot ) {
3375      this.holder.removeChild( dot );
3376    }, this );
3377  };
3378  
3379  PageDots.prototype.updateSelected = function() {
3380    // remove selected class on previous
3381    if ( this.selectedDot ) {
3382      this.selectedDot.className = 'dot';
3383      this.selectedDot.removeAttribute('aria-current');
3384    }
3385    // don't proceed if no dots
3386    if ( !this.dots.length ) {
3387      return;
3388    }
3389    this.selectedDot = this.dots[ this.parent.selectedIndex ];
3390    this.selectedDot.className = 'dot is-selected';
3391    this.selectedDot.setAttribute( 'aria-current', 'step' );
3392  };
3393  
3394  PageDots.prototype.onTap = // old method name, backwards-compatible
3395  PageDots.prototype.onClick = function( event ) {
3396    var target = event.target;
3397    // only care about dot clicks
3398    if ( target.nodeName != 'LI' ) {
3399      return;
3400    }
3401  
3402    this.parent.uiChange();
3403    var index = this.dots.indexOf( target );
3404    this.parent.select( index );
3405  };
3406  
3407  PageDots.prototype.destroy = function() {
3408    this.deactivate();
3409    this.allOff();
3410  };
3411  
3412  Flickity.PageDots = PageDots;
3413  
3414  // -------------------------- Flickity -------------------------- //
3415  
3416  utils.extend( Flickity.defaults, {
3417    pageDots: true
3418  });
3419  
3420  Flickity.createMethods.push('_createPageDots');
3421  
3422  var proto = Flickity.prototype;
3423  
3424  proto._createPageDots = function() {
3425    if ( !this.options.pageDots ) {
3426      return;
3427    }
3428    this.pageDots = new PageDots( this );
3429    // events
3430    this.on( 'activate', this.activatePageDots );
3431    this.on( 'select', this.updateSelectedPageDots );
3432    this.on( 'cellChange', this.updatePageDots );
3433    this.on( 'resize', this.updatePageDots );
3434    this.on( 'deactivate', this.deactivatePageDots );
3435  };
3436  
3437  proto.activatePageDots = function() {
3438    this.pageDots.activate();
3439  };
3440  
3441  proto.updateSelectedPageDots = function() {
3442    this.pageDots.updateSelected();
3443  };
3444  
3445  proto.updatePageDots = function() {
3446    this.pageDots.setDots();
3447  };
3448  
3449  proto.deactivatePageDots = function() {
3450    this.pageDots.deactivate();
3451  };
3452  
3453  // -----  ----- //
3454  
3455  Flickity.PageDots = PageDots;
3456  
3457  return Flickity;
3458  
3459  }));
3460  
3461  // player & autoPlay
3462  ( function( window, factory ) {
3463    // universal module definition
3464    /* jshint strict: false */
3465    if ( typeof define == 'function' && define.amd ) {
3466      // AMD
3467      define( 'flickity/js/player',[
3468        'ev-emitter/ev-emitter',
3469        'fizzy-ui-utils/utils',
3470        './flickity'
3471      ], function( EvEmitter, utils, Flickity ) {
3472        return factory( EvEmitter, utils, Flickity );
3473      });
3474    } else if ( typeof module == 'object' && module.exports ) {
3475      // CommonJS
3476      module.exports = factory(
3477        require('ev-emitter'),
3478        require('fizzy-ui-utils'),
3479        require('./flickity')
3480      );
3481    } else {
3482      // browser global
3483      factory(
3484        window.EvEmitter,
3485        window.fizzyUIUtils,
3486        window.Flickity
3487      );
3488    }
3489  
3490  }( window, function factory( EvEmitter, utils, Flickity ) {
3491  
3492  
3493  
3494  // -------------------------- Player -------------------------- //
3495  
3496  function Player( parent ) {
3497    this.parent = parent;
3498    this.state = 'stopped';
3499    // visibility change event handler
3500    this.onVisibilityChange = this.visibilityChange.bind( this );
3501    this.onVisibilityPlay = this.visibilityPlay.bind( this );
3502  }
3503  
3504  Player.prototype = Object.create( EvEmitter.prototype );
3505  
3506  // start play
3507  Player.prototype.play = function() {
3508    if ( this.state == 'playing' ) {
3509      return;
3510    }
3511    // do not play if page is hidden, start playing when page is visible
3512    var isPageHidden = document.hidden;
3513    if ( isPageHidden ) {
3514      document.addEventListener( 'visibilitychange', this.onVisibilityPlay );
3515      return;
3516    }
3517  
3518    this.state = 'playing';
3519    // listen to visibility change
3520    document.addEventListener( 'visibilitychange', this.onVisibilityChange );
3521    // start ticking
3522    this.tick();
3523  };
3524  
3525  Player.prototype.tick = function() {
3526    // do not tick if not playing
3527    if ( this.state != 'playing' ) {
3528      return;
3529    }
3530  
3531    var time = this.parent.options.autoPlay;
3532    // default to 3 seconds
3533    time = typeof time == 'number' ? time : 3000;
3534    var _this = this;
3535    // HACK: reset ticks if stopped and started within interval
3536    this.clear();
3537    this.timeout = setTimeout( function() {
3538      _this.parent.next( true );
3539      _this.tick();
3540    }, time );
3541  };
3542  
3543  Player.prototype.stop = function() {
3544    this.state = 'stopped';
3545    this.clear();
3546    // remove visibility change event
3547    document.removeEventListener( 'visibilitychange', this.onVisibilityChange );
3548  };
3549  
3550  Player.prototype.clear = function() {
3551    clearTimeout( this.timeout );
3552  };
3553  
3554  Player.prototype.pause = function() {
3555    if ( this.state == 'playing' ) {
3556      this.state = 'paused';
3557      this.clear();
3558    }
3559  };
3560  
3561  Player.prototype.unpause = function() {
3562    // re-start play if paused
3563    if ( this.state == 'paused' ) {
3564      this.play();
3565    }
3566  };
3567  
3568  // pause if page visibility is hidden, unpause if visible
3569  Player.prototype.visibilityChange = function() {
3570    var isPageHidden = document.hidden;
3571    this[ isPageHidden ? 'pause' : 'unpause' ]();
3572  };
3573  
3574  Player.prototype.visibilityPlay = function() {
3575    this.play();
3576    document.removeEventListener( 'visibilitychange', this.onVisibilityPlay );
3577  };
3578  
3579  // -------------------------- Flickity -------------------------- //
3580  
3581  utils.extend( Flickity.defaults, {
3582    pauseAutoPlayOnHover: true
3583  });
3584  
3585  Flickity.createMethods.push('_createPlayer');
3586  var proto = Flickity.prototype;
3587  
3588  proto._createPlayer = function() {
3589    this.player = new Player( this );
3590  
3591    this.on( 'activate', this.activatePlayer );
3592    this.on( 'uiChange', this.stopPlayer );
3593    this.on( 'pointerDown', this.stopPlayer );
3594    this.on( 'deactivate', this.deactivatePlayer );
3595  };
3596  
3597  proto.activatePlayer = function() {
3598    if ( !this.options.autoPlay ) {
3599      return;
3600    }
3601    this.player.play();
3602    this.element.addEventListener( 'mouseenter', this );
3603  };
3604  
3605  // Player API, don't hate the ... thanks I know where the door is
3606  
3607  proto.playPlayer = function() {
3608    this.player.play();
3609  };
3610  
3611  proto.stopPlayer = function() {
3612    this.player.stop();
3613  };
3614  
3615  proto.pausePlayer = function() {
3616    this.player.pause();
3617  };
3618  
3619  proto.unpausePlayer = function() {
3620    this.player.unpause();
3621  };
3622  
3623  proto.deactivatePlayer = function() {
3624    this.player.stop();
3625    this.element.removeEventListener( 'mouseenter', this );
3626  };
3627  
3628  // ----- mouseenter/leave ----- //
3629  
3630  // pause auto-play on hover
3631  proto.onmouseenter = function() {
3632    if ( !this.options.pauseAutoPlayOnHover ) {
3633      return;
3634    }
3635    this.player.pause();
3636    this.element.addEventListener( 'mouseleave', this );
3637  };
3638  
3639  // resume auto-play on hover off
3640  proto.onmouseleave = function() {
3641    this.player.unpause();
3642    this.element.removeEventListener( 'mouseleave', this );
3643  };
3644  
3645  // -----  ----- //
3646  
3647  Flickity.Player = Player;
3648  
3649  return Flickity;
3650  
3651  }));
3652  
3653  // add, remove cell
3654  ( function( window, factory ) {
3655    // universal module definition
3656    /* jshint strict: false */
3657    if ( typeof define == 'function' && define.amd ) {
3658      // AMD
3659      define( 'flickity/js/add-remove-cell',[
3660        './flickity',
3661        'fizzy-ui-utils/utils'
3662      ], function( Flickity, utils ) {
3663        return factory( window, Flickity, utils );
3664      });
3665    } else if ( typeof module == 'object' && module.exports ) {
3666      // CommonJS
3667      module.exports = factory(
3668        window,
3669        require('./flickity'),
3670        require('fizzy-ui-utils')
3671      );
3672    } else {
3673      // browser global
3674      factory(
3675        window,
3676        window.Flickity,
3677        window.fizzyUIUtils
3678      );
3679    }
3680  
3681  }( window, function factory( window, Flickity, utils ) {
3682  
3683  
3684  
3685  // append cells to a document fragment
3686  function getCellsFragment( cells ) {
3687    var fragment = document.createDocumentFragment();
3688    cells.forEach( function( cell ) {
3689      fragment.appendChild( cell.element );
3690    });
3691    return fragment;
3692  }
3693  
3694  // -------------------------- add/remove cell prototype -------------------------- //
3695  
3696  var proto = Flickity.prototype;
3697  
3698  /**
3699   * Insert, prepend, or append cells
3700   * @param {Element, Array, NodeList} elems
3701   * @param {Integer} index
3702   */
3703  proto.insert = function( elems, index ) {
3704    var cells = this._makeCells( elems );
3705    if ( !cells || !cells.length ) {
3706      return;
3707    }
3708    var len = this.cells.length;
3709    // default to append
3710    index = index === undefined ? len : index;
3711    // add cells with document fragment
3712    var fragment = getCellsFragment( cells );
3713    // append to slider
3714    var isAppend = index == len;
3715    if ( isAppend ) {
3716      this.slider.appendChild( fragment );
3717    } else {
3718      var insertCellElement = this.cells[ index ].element;
3719      this.slider.insertBefore( fragment, insertCellElement );
3720    }
3721    // add to this.cells
3722    if ( index === 0 ) {
3723      // prepend, add to start
3724      this.cells = cells.concat( this.cells );
3725    } else if ( isAppend ) {
3726      // append, add to end
3727      this.cells = this.cells.concat( cells );
3728    } else {
3729      // insert in this.cells
3730      var endCells = this.cells.splice( index, len - index );
3731      this.cells = this.cells.concat( cells ).concat( endCells );
3732    }
3733  
3734    this._sizeCells( cells );
3735    this.cellChange( index, true );
3736  };
3737  
3738  proto.append = function( elems ) {
3739    this.insert( elems, this.cells.length );
3740  };
3741  
3742  proto.prepend = function( elems ) {
3743    this.insert( elems, 0 );
3744  };
3745  
3746  /**
3747   * Remove cells
3748   * @param {Element, Array, NodeList} elems
3749   */
3750  proto.remove = function( elems ) {
3751    var cells = this.getCells( elems );
3752    if ( !cells || !cells.length ) {
3753      return;
3754    }
3755  
3756    var minCellIndex = this.cells.length - 1;
3757    // remove cells from collection & DOM
3758    cells.forEach( function( cell ) {
3759      cell.remove();
3760      var index = this.cells.indexOf( cell );
3761      minCellIndex = Math.min( index, minCellIndex );
3762      utils.removeFrom( this.cells, cell );
3763    }, this );
3764  
3765    this.cellChange( minCellIndex, true );
3766  };
3767  
3768  /**
3769   * logic to be run after a cell's size changes
3770   * @param {Element} elem - cell's element
3771   */
3772  proto.cellSizeChange = function( elem ) {
3773    var cell = this.getCell( elem );
3774    if ( !cell ) {
3775      return;
3776    }
3777    cell.getSize();
3778  
3779    var index = this.cells.indexOf( cell );
3780    this.cellChange( index );
3781  };
3782  
3783  /**
3784   * logic any time a cell is changed: added, removed, or size changed
3785   * @param {Integer} changedCellIndex - index of the changed cell, optional
3786   */
3787  proto.cellChange = function( changedCellIndex, isPositioningSlider ) {
3788    var prevSelectedElem = this.selectedElement;
3789    this._positionCells( changedCellIndex );
3790    this._getWrapShiftCells();
3791    this.setGallerySize();
3792    // update selectedIndex
3793    // try to maintain position & select previous selected element
3794    var cell = this.getCell( prevSelectedElem );
3795    if ( cell ) {
3796      this.selectedIndex = this.getCellSlideIndex( cell );
3797    }
3798    this.selectedIndex = Math.min( this.slides.length - 1, this.selectedIndex );
3799  
3800    this.emitEvent( 'cellChange', [ changedCellIndex ] );
3801    // position slider
3802    this.select( this.selectedIndex );
3803    // do not position slider after lazy load
3804    if ( isPositioningSlider ) {
3805      this.positionSliderAtSelected();
3806    }
3807  };
3808  
3809  // -----  ----- //
3810  
3811  return Flickity;
3812  
3813  }));
3814  
3815  // lazyload
3816  ( function( window, factory ) {
3817    // universal module definition
3818    /* jshint strict: false */
3819    if ( typeof define == 'function' && define.amd ) {
3820      // AMD
3821      define( 'flickity/js/lazyload',[
3822        './flickity',
3823        'fizzy-ui-utils/utils'
3824      ], function( Flickity, utils ) {
3825        return factory( window, Flickity, utils );
3826      });
3827    } else if ( typeof module == 'object' && module.exports ) {
3828      // CommonJS
3829      module.exports = factory(
3830        window,
3831        require('./flickity'),
3832        require('fizzy-ui-utils')
3833      );
3834    } else {
3835      // browser global
3836      factory(
3837        window,
3838        window.Flickity,
3839        window.fizzyUIUtils
3840      );
3841    }
3842  
3843  }( window, function factory( window, Flickity, utils ) {
3844  'use strict';
3845  
3846  Flickity.createMethods.push('_createLazyload');
3847  var proto = Flickity.prototype;
3848  
3849  proto._createLazyload = function() {
3850    this.on( 'select', this.lazyLoad );
3851  };
3852  
3853  proto.lazyLoad = function() {
3854    var lazyLoad = this.options.lazyLoad;
3855    if ( !lazyLoad ) {
3856      return;
3857    }
3858    // get adjacent cells, use lazyLoad option for adjacent count
3859    var adjCount = typeof lazyLoad == 'number' ? lazyLoad : 0;
3860    var cellElems = this.getAdjacentCellElements( adjCount );
3861    // get lazy images in those cells
3862    var lazyImages = [];
3863    cellElems.forEach( function( cellElem ) {
3864      var lazyCellImages = getCellLazyImages( cellElem );
3865      lazyImages = lazyImages.concat( lazyCellImages );
3866    });
3867    // load lazy images
3868    lazyImages.forEach( function( img ) {
3869      new LazyLoader( img, this );
3870    }, this );
3871  };
3872  
3873  function getCellLazyImages( cellElem ) {
3874    // check if cell element is lazy image
3875    if ( cellElem.nodeName == 'IMG' ) {
3876      var lazyloadAttr = cellElem.getAttribute('data-flickity-lazyload');
3877      var srcAttr = cellElem.getAttribute('data-flickity-lazyload-src');
3878      var srcsetAttr = cellElem.getAttribute('data-flickity-lazyload-srcset');
3879      if ( lazyloadAttr || srcAttr || srcsetAttr ) {
3880        return [ cellElem ];
3881      }
3882    }
3883    // select lazy images in cell
3884    var lazySelector = 'img[data-flickity-lazyload], ' +
3885      'img[data-flickity-lazyload-src], img[data-flickity-lazyload-srcset]';
3886    var imgs = cellElem.querySelectorAll( lazySelector );
3887    return utils.makeArray( imgs );
3888  }
3889  
3890  // -------------------------- LazyLoader -------------------------- //
3891  
3892  /**
3893   * class to handle loading images
3894   */
3895  function LazyLoader( img, flickity ) {
3896    this.img = img;
3897    this.flickity = flickity;
3898    this.load();
3899  }
3900  
3901  LazyLoader.prototype.handleEvent = utils.handleEvent;
3902  
3903  LazyLoader.prototype.load = function() {
3904    this.img.addEventListener( 'load', this );
3905    this.img.addEventListener( 'error', this );
3906    // get src & srcset
3907    var src = this.img.getAttribute('data-flickity-lazyload') ||
3908      this.img.getAttribute('data-flickity-lazyload-src');
3909    var srcset = this.img.getAttribute('data-flickity-lazyload-srcset');
3910    // set src & serset
3911    this.img.src = src;
3912    if ( srcset ) {
3913      this.img.setAttribute( 'srcset', srcset );
3914    }
3915    // remove attr
3916    this.img.removeAttribute('data-flickity-lazyload');
3917    this.img.removeAttribute('data-flickity-lazyload-src');
3918    this.img.removeAttribute('data-flickity-lazyload-srcset');
3919  };
3920  
3921  LazyLoader.prototype.onload = function( event ) {
3922    this.complete( event, 'flickity-lazyloaded' );
3923  };
3924  
3925  LazyLoader.prototype.onerror = function( event ) {
3926    this.complete( event, 'flickity-lazyerror' );
3927  };
3928  
3929  LazyLoader.prototype.complete = function( event, className ) {
3930    // unbind events
3931    this.img.removeEventListener( 'load', this );
3932    this.img.removeEventListener( 'error', this );
3933  
3934    var cell = this.flickity.getParentCell( this.img );
3935    var cellElem = cell && cell.element;
3936    this.flickity.cellSizeChange( cellElem );
3937  
3938    this.img.classList.add( className );
3939    this.flickity.dispatchEvent( 'lazyLoad', event, cellElem );
3940  };
3941  
3942  // -----  ----- //
3943  
3944  Flickity.LazyLoader = LazyLoader;
3945  
3946  return Flickity;
3947  
3948  }));
3949  
3950  /*!
3951   * Flickity v2.2.1
3952   * Touch, responsive, flickable carousels
3953   *
3954   * Licensed GPLv3 for open source use
3955   * or Flickity Commercial License for commercial use
3956   *
3957   * https://flickity.metafizzy.co
3958   * Copyright 2015-2019 Metafizzy
3959   */
3960  
3961  ( function( window, factory ) {
3962    // universal module definition
3963    /* jshint strict: false */
3964    if ( typeof define == 'function' && define.amd ) {
3965      // AMD
3966      define( 'flickity/js/index',[
3967        './flickity',
3968        './drag',
3969        './prev-next-button',
3970        './page-dots',
3971        './player',
3972        './add-remove-cell',
3973        './lazyload'
3974      ], factory );
3975    } else if ( typeof module == 'object' && module.exports ) {
3976      // CommonJS
3977      module.exports = factory(
3978        require('./flickity'),
3979        require('./drag'),
3980        require('./prev-next-button'),
3981        require('./page-dots'),
3982        require('./player'),
3983        require('./add-remove-cell'),
3984        require('./lazyload')
3985      );
3986    }
3987  
3988  })( window, function factory( Flickity ) {
3989    /*jshint strict: false*/
3990    return Flickity;
3991  });
3992  
3993  /*!
3994   * Flickity asNavFor v2.0.2
3995   * enable asNavFor for Flickity
3996   */
3997  
3998  /*jshint browser: true, undef: true, unused: true, strict: true*/
3999  
4000  ( function( window, factory ) {
4001    // universal module definition
4002    /*jshint strict: false */ /*globals define, module, require */
4003    if ( typeof define == 'function' && define.amd ) {
4004      // AMD
4005      define( 'flickity-as-nav-for/as-nav-for',[
4006        'flickity/js/index',
4007        'fizzy-ui-utils/utils'
4008      ], factory );
4009    } else if ( typeof module == 'object' && module.exports ) {
4010      // CommonJS
4011      module.exports = factory(
4012        require('flickity'),
4013        require('fizzy-ui-utils')
4014      );
4015    } else {
4016      // browser global
4017      window.Flickity = factory(
4018        window.Flickity,
4019        window.fizzyUIUtils
4020      );
4021    }
4022  
4023  }( window, function factory( Flickity, utils ) {
4024  
4025  
4026  
4027  // -------------------------- asNavFor prototype -------------------------- //
4028  
4029  // Flickity.defaults.asNavFor = null;
4030  
4031  Flickity.createMethods.push('_createAsNavFor');
4032  
4033  var proto = Flickity.prototype;
4034  
4035  proto._createAsNavFor = function() {
4036    this.on( 'activate', this.activateAsNavFor );
4037    this.on( 'deactivate', this.deactivateAsNavFor );
4038    this.on( 'destroy', this.destroyAsNavFor );
4039  
4040    var asNavForOption = this.options.asNavFor;
4041    if ( !asNavForOption ) {
4042      return;
4043    }
4044    // HACK do async, give time for other flickity to be initalized
4045    var _this = this;
4046    setTimeout( function initNavCompanion() {
4047      _this.setNavCompanion( asNavForOption );
4048    });
4049  };
4050  
4051  proto.setNavCompanion = function( elem ) {
4052    elem = utils.getQueryElement( elem );
4053    var companion = Flickity.data( elem );
4054    // stop if no companion or companion is self
4055    if ( !companion || companion == this ) {
4056      return;
4057    }
4058  
4059    this.navCompanion = companion;
4060    // companion select
4061    var _this = this;
4062    this.onNavCompanionSelect = function() {
4063      _this.navCompanionSelect();
4064    };
4065    companion.on( 'select', this.onNavCompanionSelect );
4066    // click
4067    this.on( 'staticClick', this.onNavStaticClick );
4068  
4069    this.navCompanionSelect( true );
4070  };
4071  
4072  proto.navCompanionSelect = function( isInstant ) {
4073    // wait for companion & selectedCells first. #8
4074    var companionCells = this.navCompanion && this.navCompanion.selectedCells;
4075    if ( !companionCells ) {
4076      return;
4077    }
4078    // select slide that matches first cell of slide
4079    var selectedCell = companionCells[0];
4080    var firstIndex = this.navCompanion.cells.indexOf( selectedCell );
4081    var lastIndex = firstIndex + companionCells.length - 1;
4082    var selectIndex = Math.floor( lerp( firstIndex, lastIndex,
4083      this.navCompanion.cellAlign ) );
4084    this.selectCell( selectIndex, false, isInstant );
4085    // set nav selected class
4086    this.removeNavSelectedElements();
4087    // stop if companion has more cells than this one
4088    if ( selectIndex >= this.cells.length ) {
4089      return;
4090    }
4091  
4092    var selectedCells = this.cells.slice( firstIndex, lastIndex + 1 );
4093    this.navSelectedElements = selectedCells.map( function( cell ) {
4094      return cell.element;
4095    });
4096    this.changeNavSelectedClass('add');
4097  };
4098  
4099  function lerp( a, b, t ) {
4100    return ( b - a ) * t + a;
4101  }
4102  
4103  proto.changeNavSelectedClass = function( method ) {
4104    this.navSelectedElements.forEach( function( navElem ) {
4105      navElem.classList[ method ]('is-nav-selected');
4106    });
4107  };
4108  
4109  proto.activateAsNavFor = function() {
4110    this.navCompanionSelect( true );
4111  };
4112  
4113  proto.removeNavSelectedElements = function() {
4114    if ( !this.navSelectedElements ) {
4115      return;
4116    }
4117    this.changeNavSelectedClass('remove');
4118    delete this.navSelectedElements;
4119  };
4120  
4121  proto.onNavStaticClick = function( event, pointer, cellElement, cellIndex ) {
4122    if ( typeof cellIndex == 'number' ) {
4123      this.navCompanion.selectCell( cellIndex );
4124    }
4125  };
4126  
4127  proto.deactivateAsNavFor = function() {
4128    this.removeNavSelectedElements();
4129  };
4130  
4131  proto.destroyAsNavFor = function() {
4132    if ( !this.navCompanion ) {
4133      return;
4134    }
4135    this.navCompanion.off( 'select', this.onNavCompanionSelect );
4136    this.off( 'staticClick', this.onNavStaticClick );
4137    delete this.navCompanion;
4138  };
4139  
4140  // -----  ----- //
4141  
4142  return Flickity;
4143  
4144  }));
4145  
4146  /*!
4147   * imagesLoaded v4.1.4
4148   * JavaScript is all like "You images are done yet or what?"
4149   * MIT License
4150   */
4151  
4152  ( function( window, factory ) { 'use strict';
4153    // universal module definition
4154  
4155    /*global define: false, module: false, require: false */
4156  
4157    if ( typeof define == 'function' && define.amd ) {
4158      // AMD
4159      define( 'imagesloaded/imagesloaded',[
4160        'ev-emitter/ev-emitter'
4161      ], function( EvEmitter ) {
4162        return factory( window, EvEmitter );
4163      });
4164    } else if ( typeof module == 'object' && module.exports ) {
4165      // CommonJS
4166      module.exports = factory(
4167        window,
4168        require('ev-emitter')
4169      );
4170    } else {
4171      // browser global
4172      window.imagesLoaded = factory(
4173        window,
4174        window.EvEmitter
4175      );
4176    }
4177  
4178  })( typeof window !== 'undefined' ? window : this,
4179  
4180  // --------------------------  factory -------------------------- //
4181  
4182  function factory( window, EvEmitter ) {
4183  
4184  
4185  
4186  var $ = window.jQuery;
4187  var console = window.console;
4188  
4189  // -------------------------- helpers -------------------------- //
4190  
4191  // extend objects
4192  function extend( a, b ) {
4193    for ( var prop in b ) {
4194      a[ prop ] = b[ prop ];
4195    }
4196    return a;
4197  }
4198  
4199  var arraySlice = Array.prototype.slice;
4200  
4201  // turn element or nodeList into an array
4202  function makeArray( obj ) {
4203    if ( Array.isArray( obj ) ) {
4204      // use object if already an array
4205      return obj;
vendor: 8,205 bytes, lines 4206-4477
4206    }
4207  
4208    var isArrayLike = typeof obj == 'object' && typeof obj.length == 'number';
4209    if ( isArrayLike ) {
4210      // convert nodeList to array
4211      return arraySlice.call( obj );
4212    }
4213  
4214    // array of single index
4215    return [ obj ];
4216  }
4217  
4218  // -------------------------- imagesLoaded -------------------------- //
4219  
4220  /**
4221   * @param {Array, Element, NodeList, String} elem
4222   * @param {Object or Function} options - if function, use as callback
4223   * @param {Function} onAlways - callback function
4224   */
4225  function ImagesLoaded( elem, options, onAlways ) {
4226    // coerce ImagesLoaded() without new, to be new ImagesLoaded()
4227    if ( !( this instanceof ImagesLoaded ) ) {
4228      return new ImagesLoaded( elem, options, onAlways );
4229    }
4230    // use elem as selector string
4231    var queryElem = elem;
4232    if ( typeof elem == 'string' ) {
4233      queryElem = document.querySelectorAll( elem );
4234    }
4235    // bail if bad element
4236    if ( !queryElem ) {
4237      console.error( 'Bad element for imagesLoaded ' + ( queryElem || elem ) );
4238      return;
4239    }
4240  
4241    this.elements = makeArray( queryElem );
4242    this.options = extend( {}, this.options );
4243    // shift arguments if no options set
4244    if ( typeof options == 'function' ) {
4245      onAlways = options;
4246    } else {
4247      extend( this.options, options );
4248    }
4249  
4250    if ( onAlways ) {
4251      this.on( 'always', onAlways );
4252    }
4253  
4254    this.getImages();
4255  
4256    if ( $ ) {
4257      // add jQuery Deferred object
4258      this.jqDeferred = new $.Deferred();
4259    }
4260  
4261    // HACK check async to allow time to bind listeners
4262    setTimeout( this.check.bind( this ) );
4263  }
4264  
4265  ImagesLoaded.prototype = Object.create( EvEmitter.prototype );
4266  
4267  ImagesLoaded.prototype.options = {};
4268  
4269  ImagesLoaded.prototype.getImages = function() {
4270    this.images = [];
4271  
4272    // filter & find items if we have an item selector
4273    this.elements.forEach( this.addElementImages, this );
4274  };
4275  
4276  /**
4277   * @param {Node} element
4278   */
4279  ImagesLoaded.prototype.addElementImages = function( elem ) {
4280    // filter siblings
4281    if ( elem.nodeName == 'IMG' ) {
4282      this.addImage( elem );
4283    }
4284    // get background image on element
4285    if ( this.options.background === true ) {
4286      this.addElementBackgroundImages( elem );
4287    }
4288  
4289    // find children
4290    // no non-element nodes, #143
4291    var nodeType = elem.nodeType;
4292    if ( !nodeType || !elementNodeTypes[ nodeType ] ) {
4293      return;
4294    }
4295    var childImgs = elem.querySelectorAll('img');
4296    // concat childElems to filterFound array
4297    for ( var i=0; i < childImgs.length; i++ ) {
4298      var img = childImgs[i];
4299      this.addImage( img );
4300    }
4301  
4302    // get child background images
4303    if ( typeof this.options.background == 'string' ) {
4304      var children = elem.querySelectorAll( this.options.background );
4305      for ( i=0; i < children.length; i++ ) {
4306        var child = children[i];
4307        this.addElementBackgroundImages( child );
4308      }
4309    }
4310  };
4311  
4312  var elementNodeTypes = {
4313    1: true,
4314    9: true,
4315    11: true
4316  };
4317  
4318  ImagesLoaded.prototype.addElementBackgroundImages = function( elem ) {
4319    var style = getComputedStyle( elem );
4320    if ( !style ) {
4321      // Firefox returns null if in a hidden iframe https://bugzil.la/548397
4322      return;
4323    }
4324    // get url inside url("...")
4325    var reURL = /url\((['"])?(.*?)\1\)/gi;
4326    var matches = reURL.exec( style.backgroundImage );
4327    while ( matches !== null ) {
4328      var url = matches && matches[2];
4329      if ( url ) {
4330        this.addBackground( url, elem );
4331      }
4332      matches = reURL.exec( style.backgroundImage );
4333    }
4334  };
4335  
4336  /**
4337   * @param {Image} img
4338   */
4339  ImagesLoaded.prototype.addImage = function( img ) {
4340    var loadingImage = new LoadingImage( img );
4341    this.images.push( loadingImage );
4342  };
4343  
4344  ImagesLoaded.prototype.addBackground = function( url, elem ) {
4345    var background = new Background( url, elem );
4346    this.images.push( background );
4347  };
4348  
4349  ImagesLoaded.prototype.check = function() {
4350    var _this = this;
4351    this.progressedCount = 0;
4352    this.hasAnyBroken = false;
4353    // complete if no images
4354    if ( !this.images.length ) {
4355      this.complete();
4356      return;
4357    }
4358  
4359    function onProgress( image, elem, message ) {
4360      // HACK - Chrome triggers event before object properties have changed. #83
4361      setTimeout( function() {
4362        _this.progress( image, elem, message );
4363      });
4364    }
4365  
4366    this.images.forEach( function( loadingImage ) {
4367      loadingImage.once( 'progress', onProgress );
4368      loadingImage.check();
4369    });
4370  };
4371  
4372  ImagesLoaded.prototype.progress = function( image, elem, message ) {
4373    this.progressedCount++;
4374    this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded;
4375    // progress event
4376    this.emitEvent( 'progress', [ this, image, elem ] );
4377    if ( this.jqDeferred && this.jqDeferred.notify ) {
4378      this.jqDeferred.notify( this, image );
4379    }
4380    // check if completed
4381    if ( this.progressedCount == this.images.length ) {
4382      this.complete();
4383    }
4384  
4385    if ( this.options.debug && console ) {
4386      console.log( 'progress: ' + message, image, elem );
4387    }
4388  };
4389  
4390  ImagesLoaded.prototype.complete = function() {
4391    var eventName = this.hasAnyBroken ? 'fail' : 'done';
4392    this.isComplete = true;
4393    this.emitEvent( eventName, [ this ] );
4394    this.emitEvent( 'always', [ this ] );
4395    if ( this.jqDeferred ) {
4396      var jqMethod = this.hasAnyBroken ? 'reject' : 'resolve';
4397      this.jqDeferred[ jqMethod ]( this );
4398    }
4399  };
4400  
4401  // --------------------------  -------------------------- //
4402  
4403  function LoadingImage( img ) {
4404    this.img = img;
4405  }
4406  
4407  LoadingImage.prototype = Object.create( EvEmitter.prototype );
4408  
4409  LoadingImage.prototype.check = function() {
4410    // If complete is true and browser supports natural sizes,
4411    // try to check for image status manually.
4412    var isComplete = this.getIsImageComplete();
4413    if ( isComplete ) {
4414      // report based on naturalWidth
4415      this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' );
4416      return;
4417    }
4418  
4419    // If none of the checks above matched, simulate loading on detached element.
4420    this.proxyImage = new Image();
4421    this.proxyImage.addEventListener( 'load', this );
4422    this.proxyImage.addEventListener( 'error', this );
4423    // bind to image as well for Firefox. #191
4424    this.img.addEventListener( 'load', this );
4425    this.img.addEventListener( 'error', this );
4426    this.proxyImage.src = this.img.src;
4427  };
4428  
4429  LoadingImage.prototype.getIsImageComplete = function() {
4430    // check for non-zero, non-undefined naturalWidth
4431    // fixes Safari+InfiniteScroll+Masonry bug infinite-scroll#671
4432    return this.img.complete && this.img.naturalWidth;
4433  };
4434  
4435  LoadingImage.prototype.confirm = function( isLoaded, message ) {
4436    this.isLoaded = isLoaded;
4437    this.emitEvent( 'progress', [ this, this.img, message ] );
4438  };
4439  
4440  // ----- events ----- //
4441  
4442  // trigger specified handler for event type
4443  LoadingImage.prototype.handleEvent = function( event ) {
4444    var method = 'on' + event.type;
4445    if ( this[ method ] ) {
4446      this[ method ]( event );
4447    }
4448  };
4449  
4450  LoadingImage.prototype.onload = function() {
4451    this.confirm( true, 'onload' );
4452    this.unbindEvents();
4453  };
4454  
4455  LoadingImage.prototype.onerror = function() {
4456    this.confirm( false, 'onerror' );
4457    this.unbindEvents();
4458  };
4459  
4460  LoadingImage.prototype.unbindEvents = function() {
4461    this.proxyImage.removeEventListener( 'load', this );
4462    this.proxyImage.removeEventListener( 'error', this );
4463    this.img.removeEventListener( 'load', this );
4464    this.img.removeEventListener( 'error', this );
4465  };
4466  
4467  // -------------------------- Background -------------------------- //
4468  
4469  function Background( url, element ) {
4470    this.url = url;
4471    this.element = element;
4472    this.img = new Image();
4473  }
4474  
4475  // inherit LoadingImage prototype
4476  Background.prototype = Object.create( LoadingImage.prototype );
4477  
4478  Background.prototype.check = function() {
4479    this.img.addEventListener( 'load', this );
4480    this.img.addEventListener( 'error', this );
4481    this.img.src = this.url;
4482    // check if image is already complete
4483    var isComplete = this.getIsImageComplete();
4484    if ( isComplete ) {
4485      this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' );
4486      this.unbindEvents();
4487    }
4488  };
4489  
4490  Background.prototype.unbindEvents = function() {
4491    this.img.removeEventListener( 'load', this );
4492    this.img.removeEventListener( 'error', this );
4493  };
4494  
4495  Background.prototype.confirm = function( isLoaded, message ) {
4496    this.isLoaded = isLoaded;
4497    this.emitEvent( 'progress', [ this, this.element, message ] );
4498  };
4499  
4500  // -------------------------- jQuery -------------------------- //
4501  
4502  ImagesLoaded.makeJQueryPlugin = function( jQuery ) {
4503    jQuery = jQuery || window.jQuery;
4504    if ( !jQuery ) {
4505      return;
4506    }
4507    // set local variable
4508    $ = jQuery;
4509    // $().imagesLoaded()
4510    $.fn.imagesLoaded = function( options, callback ) {
4511      var instance = new ImagesLoaded( this, options, callback );
4512      return instance.jqDeferred.promise( $(this) );
4513    };
4514  };
4515  // try making plugin
4516  ImagesLoaded.makeJQueryPlugin();
4517  
4518  // --------------------------  -------------------------- //
4519  
4520  return ImagesLoaded;
4521  
4522  });
4523  
4524  /*!
4525   * Flickity imagesLoaded v2.0.0
4526   * enables imagesLoaded option for Flickity
4527   */
4528  
4529  /*jshint browser: true, strict: true, undef: true, unused: true */
4530  
4531  ( function( window, factory ) {
4532    // universal module definition
4533    /*jshint strict: false */ /*globals define, module, require */
4534    if ( typeof define == 'function' && define.amd ) {
4535      // AMD
4536      define( [
4537        'flickity/js/index',
4538        'imagesloaded/imagesloaded'
4539      ], function( Flickity, imagesLoaded ) {
4540        return factory( window, Flickity, imagesLoaded );
4541      });
4542    } else if ( typeof module == 'object' && module.exports ) {
4543      // CommonJS
4544      module.exports = factory(
4545        window,
4546        require('flickity'),
4547        require('imagesloaded')
4548      );
4549    } else {
4550      // browser global
4551      window.Flickity = factory(
4552        window,
4553        window.Flickity,
4554        window.imagesLoaded
4555      );
4556    }
4557  
4558  }( window, function factory( window, Flickity, imagesLoaded ) {
4559  'use strict';
4560  
4561  Flickity.createMethods.push('_createImagesLoaded');
4562  
4563  var proto = Flickity.prototype;
4564  
4565  proto._createImagesLoaded = function() {
4566    this.on( 'activate', this.imagesLoaded );
4567  };
4568  
4569  proto.imagesLoaded = function() {
4570    if ( !this.options.imagesLoaded ) {
4571      return;
4572    }
4573    var _this = this;
4574    function onImagesLoadedProgress( instance, image ) {
4575      var cell = _this.getParentCell( image.img );
4576      _this.cellSizeChange( cell && cell.element );
4577      if ( !_this.options.freeScroll ) {
4578        _this.positionSliderAtSelected();
4579      }
4580    }
4581    imagesLoaded( this.slider ).on( 'progress', onImagesLoadedProgress );
4582  };
4583  
4584  return Flickity;
4585  
4586  }));
4587  

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.