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