UNPKG

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