Publicaciones - Pensar
Publicaciones
La trayectoria bibliográfica del Instituto Pensar inicia en 1996, incluyendo colecciones como:
- Pensamiento jurídico contemporáneo
- Biblioteca Virtual del Pensamiento Filosófico en Colombia (BVPFC 1996-2014)
- Cuadernos de pensar en público
- Otros sobre temas de género y juventud
- Además de una pionera serie de libros de estudios culturales y coloniales
Recientemente, el equipo ha publicado trabajos diversos adscritos a las cinco líneas de investigación del Instituto.
La política editorial del Instituto Pensar está orientada a la difusión impresa o digital de documentos (no necesariamente escriturales) críticos y transdisciplinares cuyos contenidos se inscriban en las líneas de investigación del Instituto. Se promoverán las publicaciones en coedición con otros centros investigativos y editoriales siempre y cuando los documentos se correspondan con esta política editorial.
El Instituto cuenta con un Comité editorial conformado por todos los miembros del equipo académico y la Asistente Administrativa. Este comité evalúa los documentos postulados para publicación dando prioridad a los productos del grupo de investigación del Instituto, a aquellos derivados de trabajo de miembros del Instituto con otros actores y, finalmente, otros trabajos que hagan aportes al fortalecimiento de las líneas de investigación del Instituto.
El comité se reunirá al menos dos veces por semestre por convocatoria del director(a) del Instituto. Las obras que se presenten a evaluación deben estar completamente terminadas. Las propuestas que se entreguen al Comité deben contener: 1) título, 2) texto completo de la obra, 3) nombres completos, filiación institucional y datos de contacto de todas las autoras y autores. Las propuestas se pueden presentar en cualquier momento del año.
Cada evaluación del Comité Editorial puede arrojar uno de tres resultados:
-
Aprobado. Para ser publicado en la forma y términos en que fue presentado
-
Aprobado con correcciones. Para ser publicado una vez se hayan hecho algunas correcciones que podrán indicarse como obligatorias u opcionales.
-
Rechazado.
- Publicaciones 2021-2025
- Publicaciones 2016-2020
- Publicaciones 2011-2015
- Publicaciones 2006-2010
- Publicaciones 2000-2005
The following has evaluated to null or missing: ==> urlimagen [in template "20102#20129#12795723" at line 58, column 26] ---- Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)?? ---- ---- FTL stack trace ("~" means nesting-related): - Failed at: #if urlimagen.url?has_content [in template "20102#20129#12795723" at line 58, column 21] ----
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 urlimagenNode = docXml.valueOf("//dynamic-element[@name='IMAGEN']/dynamic-content")/>
24
25
26
27 <#assign titulo = docXml.valueOf("//dynamic-element[@name='Titulopub']/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 <#if urlimagenNode?has_content>
51 <#assign urlimagenString = urlimagenNode?string />
52 <#assign urlimagen = urlimagenString?eval />
53 </#if>
54
55 <div class="gallery-cell${nameInstancePublisher}">
56 <div class="card-info${nameInstancePublisher}" id="card-info">
57 <div class="card-img${nameInstancePublisher}" id="card-img">
58 <#if urlimagen.url?has_content>
59 <img alt="${urlimagen.alt}" src='${urlimagen.url}' title="${titulo}">
60 <#else>
61 <img alt='${urlimagen["alt"]}' src='/documents/${urlimagen["groupId"]}/${urlimagen["classPK"]}/${urlimagen["name"]}/${urlimagen["uuid"]}' title="${titulo}">
62 </#if>
63 <#if assetRenderer.hasEditPermission(themeDisplay.getPermissionChecker())>
64 <#assign editPortletURL = assetRenderer.getURLEdit(renderRequest, renderResponse, windowStateFactory.getWindowState("NORMAL"), themeDisplay.getURLCurrent())!"" />
65 <#if validator.isNotNull(editPortletURL)>
66 <a class="editOptionmas" href="${editPortletURL.toString()}">Editar ✐</a>
67 </#if>
68 </#if>
69 </div>
70 <div class="card-txt${nameInstancePublisher}" id="card-txt">
71 <div class="card-title${nameInstancePublisher}" id="card-title">${titulo}</div>
72 <div class="card-lead${nameInstancePublisher}" id="card-lead">${contenido}</div>
73 <div class="card-btn${nameInstancePublisher}" id="card-btn">
74
75 <a class="link${nameInstancePublisher}" href="${linkext}" target="_blank">
76 Ver más
77 </a>
78
79 </div>
80 </div>
81
82 </div>
83 </div>
84 </#list>
85</div>
86
87
88
89
90
91<!---------------------- ESTLOS BASICOS ------------------------>
92
93 <style>
94
95 .cards-cont${nameInstancePublisher} {
96 width: 100%;
97 display: flex;
98 justify-content: center;
99 flex-wrap: wrap;
100
101 }
102
103
104 .card-info${nameInstancePublisher} {
105
106 width: 250px;
107 height: 350px;
108 margin: 15px;
109 display:flex;
110 align-content:center;
111 align-items:center;
112 flex-wrap:wrap;
113 }
114
115 .card-img${nameInstancePublisher} {
116 width: 250px;
117 height: 350px;
118 border-radius: 10px;
119 -webkit-box-shadow: 0px 2px 17px 1px rgb(0 0 0 / 23%);
120 box-shadow: 0px 2px 17px 1px rgb(0 0 0 / 23%);
121 box-sizing: border-box;
122 background-position: center;
123 background-size: cover;
124 -webkit-transition: all 500ms ease-in-out; // IE 9
125 -moz-transition: all 500ms ease-in-out; // Firefox
126 -ms-transition: all 500ms ease-in-out; // Safari and Chrome
127 -o-transition: all 500ms ease-in-out; // Opera
128 transition: all 500ms ease-in-out;
129 position:relative;
130 display:flex;
131 }
132
133 .card-info${nameInstancePublisher}:hover .card-img${nameInstancePublisher} {
134 width: 250px;
135 height: 350px;
136 border-radius: 10px;
137 box-sizing: border-box;
138 background-position: center;
139 background-size: cover;
140 -moz-transform: scale(1.02);
141 -webkit-transform: scale(1.02);
142 -o-transform: scale(1.02);
143 -ms-transform: scale(1.02);
144 transform: scale(1.02);
145 }
146
147 .card-img${nameInstancePublisher} img{
148 vertical-align: middle;
149 border-style: none;
150 width: 100%;
151 height: 350px;
152 object-fit: cover;
153 object-position: center;
154 border-radius: 10px;
155 }
156
157 .card-txt${nameInstancePublisher} {
158 position: absolute;
159 display:flex;
160 padding: 20px;
161 color: #FFF;
162 opacity: 1;
163 transition: all 500ms ease-in-out;
164 width: 90%;
165 flex-wrap:wrap;
166 max-width: 250px;
167 }
168 #card-txt{
169 display:none;
170 opacity:0;
171 transition: all 1s;
172 -webkit-transition: all 1s;
173 }
174
175 #card-info:hover #card-txt{
176 display:flex;
177 opacity:1;
178 transition: all 1s;
179 -webkit-transition: all 1s;
180 }
181
182 #card-info:hover #card-img{filter: brightness(0.3);}
183
184 .card-title${nameInstancePublisher} {
185 font-family: HelveticaBold;
186 font-size: 1rem;
187 transition: all 500ms ease-in-out;
188 width:100%;
189 }
190
191 .card-lead${nameInstancePublisher} {
192 font-family: HelveticaLight;
193 font-size: 0.9rem;
194 transition: all 500ms ease-in-out;
195 width: 100%;
196 }
197
198 .card-btn${nameInstancePublisher} {
199 padding: 5px;
200 font-family: HelveticaLight;
201 border: 1px solid #fff;
202 width: auto;
203 font-size: 0.8rem;
204 margin-top: 10px;
205 display: flex;
206 border-radius: 10px;
207 text-align: center;
208 /* margin: 0 auto; */
209 /* align-content: center; */
210 justify-content: center;
211 cursor: pointer;
212 opacity: 1;
213 }
214
215 .card-info${nameInstancePublisher}:hover .card-btn${nameInstancePublisher}{
216 background-color:#FFF;
217 color:#494949;
218 width: auto;
219 padding: 5px 10px;
220 border-radius: 5px;
221 }
222
223 .card-btn${nameInstancePublisher} a{
224 color:#FFF;
225 text-decoration:none;
226 }
227
228 .card-info${nameInstancePublisher}:hover .card-btn${nameInstancePublisher} a{
229 color:#494949;
230
231 }
232
233
234 .editOptionmas{
235 position: absolute;
236 background-color: #173268;
237 padding: 2px 5px;
238 color: #FFF;
239 font-family:HelveticaLight;
240 display:flex;
241 flex-wrap:nowrap;
242 width: 85px;
243 top:0px;
244 justify-content: center;
245 border-radius:10px 0px 10px 0px;
246 }
247
248 .editOptionmas:hover{
249 text-decoration:none;
250 color:#FFF;
251 font-family:HelveticaBold;
252 }
253
254 </style>
255
256 <style>
257 .gallery${nameInstancePublisher} {
258
259 margin: 0 auto;
260 width: 97%;
261 max-width:1200px;
262 margin-bottom:30px;
263 height:420px;
264 }
265
266 .gallery-cell${nameInstancePublisher} {
267 width: 22%;
268 height: auto;
269 margin-right: 10px;
270 counter-increment: gallery-cell;
271 }
272 /* cell number */
273
274 .gallery-cell${nameInstancePublisher}:before {
275 display: block;
276 text-align: center;
277 /content: counter(gallery-cell);/
278 line-height: 200px;
279 font-size: 80px;
280 color: white;
281 }
282
283 @media screen and (max-width: 640px) {
284 .gallery-cell${nameInstancePublisher} {
285 width: 100%;
286 height: auto;
287 margin-right: 10px;
288 counter-increment: gallery-cell;
289 }
290 }
291
292
293 .flickity-page-dots .dot.is-selected {
294 opacity: 1;
295 background: #2c5697;
296 }
297
298 .flickity-page-dots .dot {
299 display: inline-block;
300 width: 8px!important;
301 height: 8px!important;
302 margin: 0 5px!important;
303 background: #494949;
304 border-radius: 50%;
305 opacity: 0.25;
306 cursor: pointer;
307 }
308
309 .flickity-page-dots {
310 position: absolute;
311 width: 100%;
312 bottom: 0px!important;
313 padding: 0;
314 margin: 0;
315 list-style: none;
316 text-align: center;
317 line-height: 1;
318 margin-bottom: 0px;
319 }
320
321 .flickity-prev-next-button.previous {
322 left: -50px!important;
323 }
324
325 .flickity-prev-next-button.next {
326 right: -50px!important;
327 }
328
329 @media screen and (max-width: 640px) {
330 .flickity-prev-next-button.previous {
331 left: 10px!important;
332 }
333
334 .flickity-prev-next-button.next {
335 right: 10px!important;
336 }
337
338 .flickity-prev-next-button {
339 top: 25%!important;
340 width: 44px;
341 height: 44px;
342 border-radius: 50%;
343 transform: translateY(-50%);
344 }
345}
346
347
348 </style>
349
350<!------------------------------------------------------- ESTILOS DEL SCRIPT ---------------------------------------------->
351
352
353<style>
354
355.flickity-enabled {
356 position: relative;
357}
358
359.flickity-enabled:focus { outline: none; }
360
361.flickity-viewport {
362 overflow: hidden;
363 position: relative;
364 height: 100%;
365}
366
367.flickity-slider {
368 position: absolute;
369 width: 100%;
370 height: 100%;
371}
372
373/* draggable */
374
375.flickity-enabled.is-draggable {
376 -webkit-tap-highlight-color: transparent;
377 -webkit-user-select: none;
378 -moz-user-select: none;
379 -ms-user-select: none;
380 user-select: none;
381}
382
383.flickity-enabled.is-draggable .flickity-viewport {
384 cursor: move;
385 cursor: -webkit-grab;
386 cursor: grab;
387}
388
389.flickity-enabled.is-draggable .flickity-viewport.is-pointer-down {
390 cursor: -webkit-grabbing;
391 cursor: grabbing;
392}
393
394/* ---- flickity-button ---- */
395
396.flickity-button {
397 position: absolute;
398 background: hsla(0, 0%, 100%, 0.75);
399 border: none;
400 color: #333;
401}
402
403.flickity-button:hover {
404 background: white;
405 cursor: pointer;
406}
407
408.flickity-button:focus {
409 outline: none;
410 box-shadow: 0 0 0 5px #19F;
411}
412
413.flickity-button:active {
414 opacity: 0.6;
415}
416
417.flickity-button:disabled {
418 opacity: 0.3;
419 cursor: auto;
420 /* prevent disabled button from capturing pointer up event. #716 */
421 pointer-events: none;
422}
423
424.flickity-button-icon {
425 fill: currentColor;
426}
427
428/* ---- previous/next buttons ---- */
429
430.flickity-prev-next-button {
431 top: 50%;
432 width: 44px;
433 height: 44px;
434 border-radius: 50%;
435 /* vertically center */
436 transform: translateY(-50%);
437}
438
439.flickity-prev-next-button.previous { left: 10px; }
440.flickity-prev-next-button.next { right: 10px; }
441/* right to left */
442.flickity-rtl .flickity-prev-next-button.previous {
443 left: auto;
444 right: 10px;
445}
446.flickity-rtl .flickity-prev-next-button.next {
447 right: auto;
448 left: 10px;
449}
450
451.flickity-prev-next-button .flickity-button-icon {
452 position: absolute;
453 left: 20%;
454 top: 20%;
455 width: 60%;
456 height: 60%;
457}
458
459/* ---- page dots ---- */
460
461.flickity-page-dots {
462 position: absolute;
463 width: 100%;
464 bottom: -25px;
465 padding: 0;
466 margin: 0;
467 list-style: none;
468 text-align: center;
469 line-height: 1;
470}
471
472.flickity-rtl .flickity-page-dots { direction: rtl; }
473
474.flickity-page-dots .dot {
475 display: inline-block;
476 width: 10px;
477 height: 10px;
478 margin: 0 8px;
479 background: #333;
480 border-radius: 50%;
481 opacity: 0.25;
482 cursor: pointer;
483}
484
485.flickity-page-dots .dot.is-selected {
486 opacity: 1;
487}
488 </style>
489
490
491<!------------------------------------------------------------- SCRIPT PRINCIPAL -------------------------------------------------------------------------->
492
493<script>
494 /*!
495 * Flickity PACKAGED v2.2.2
496 * Touch, responsive, flickable carousels
497 *
498 * Licensed GPLv3 for open source use
499 * or Flickity Commercial License for commercial use
500 *
501 * https://flickity.metafizzy.co
502 * Copyright 2015-2021 Metafizzy
503 */
504
505/**
506 * Bridget makes jQuery widgets
507 * v2.0.1
508 * MIT license
509 */
510
511/* jshint browser: true, strict: true, undef: true, unused: true */
512
513( function( window, factory ) {
514 // universal module definition
515 /*jshint strict: false */ /* globals define, module, require */
516 if ( typeof define == 'function' && define.amd ) {
517 // AMD
518 define( 'jquery-bridget/jquery-bridget',[ 'jquery' ], function( jQuery ) {
519 return factory( window, jQuery );
520 });
521 } else if ( typeof module == 'object' && module.exports ) {
522 // CommonJS
523 module.exports = factory(
524 window,
525 require('jquery')
526 );
527 } else {
528 // browser global
529 window.jQueryBridget = factory(
530 window,
531 window.jQuery
532 );
533 }
534
535}( window, function factory( window, jQuery ) {
536'use strict';
537
538// ----- utils ----- //
539
540var arraySlice = Array.prototype.slice;
541
542// helper function for logging errors
543// $.error breaks jQuery chaining
544var console = window.console;
545var logError = typeof console == 'undefined' ? function() {} :
546 function( message ) {
547 console.error( message );
548 };
549
550// ----- jQueryBridget ----- //
551
552function jQueryBridget( namespace, PluginClass, $ ) {
553 $ = $ || jQuery || window.jQuery;
554 if ( !$ ) {
555 return;
556 }
557
558 // add option method -> $().plugin('option', {...})
559 if ( !PluginClass.prototype.option ) {
560 // option setter
561 PluginClass.prototype.option = function( opts ) {
562 // bail out if not an object
563 if ( !$.isPlainObject( opts ) ){
564 return;
565 }
566 this.options = $.extend( true, this.options, opts );
567 };
568 }
569
570 // make jQuery plugin
571 $.fn[ namespace ] = function( arg0 /*, arg1 */ ) {
572 if ( typeof arg0 == 'string' ) {
573 // method call $().plugin( 'methodName', { options } )
574 // shift arguments by 1
575 var args = arraySlice.call( arguments, 1 );
576 return methodCall( this, arg0, args );
577 }
578 // just $().plugin({ options })
579 plainCall( this, arg0 );
580 return this;
581 };
582
583 // $().plugin('methodName')
584 function methodCall( $elems, methodName, args ) {
585 var returnValue;
586 var pluginMethodStr = '$().' + namespace + '("' + methodName + '")';
587
588 $elems.each( function( i, elem ) {
589 // get instance
590 var instance = $.data( elem, namespace );
591 if ( !instance ) {
592 logError( namespace + ' not initialized. Cannot call methods, i.e. ' +
593 pluginMethodStr );
594 return;
595 }
596
597 var method = instance[ methodName ];
598 if ( !method || methodName.charAt(0) == '_' ) {
599 logError( pluginMethodStr + ' is not a valid method' );
600 return;
601 }
602
603 // apply method, get return value
604 var value = method.apply( instance, args );
605 // set return value if value is returned, use only first value
606 returnValue = returnValue === undefined ? value : returnValue;
607 });
608
609 return returnValue !== undefined ? returnValue : $elems;
610 }
611
612 function plainCall( $elems, options ) {
613 $elems.each( function( i, elem ) {
614 var instance = $.data( elem, namespace );
615 if ( instance ) {
616 // set options & init
617 instance.option( options );
618 instance._init();
619 } else {
620 // initialize new instance
621 instance = new PluginClass( elem, options );
622 $.data( elem, namespace, instance );
623 }
624 });
625 }
626
627 updateJQuery( $ );
628
629}
630
631// ----- updateJQuery ----- //
632
633// set $.bridget for v1 backwards compatibility
634function updateJQuery( $ ) {
635 if ( !$ || ( $ && $.bridget ) ) {
636 return;
637 }
638 $.bridget = jQueryBridget;
639}
640
641updateJQuery( jQuery || window.jQuery );
642
643// ----- ----- //
644
645return jQueryBridget;
646
647}));
648
649/**
650 * EvEmitter v1.1.0
651 * Lil' event emitter
652 * MIT License
653 */
654
655/* jshint unused: true, undef: true, strict: true */
656
657( function( global, factory ) {
658 // universal module definition
659 /* jshint strict: false */ /* globals define, module, window */
660 if ( typeof define == 'function' && define.amd ) {
661 // AMD - RequireJS
662 define( 'ev-emitter/ev-emitter',factory );
663 } else if ( typeof module == 'object' && module.exports ) {
664 // CommonJS - Browserify, Webpack
665 module.exports = factory();
666 } else {
667 // Browser globals
668 global.EvEmitter = factory();
669 }
670
671}( typeof window != 'undefined' ? window : this, function() {
672
673
674
675function EvEmitter() {}
676
677var proto = EvEmitter.prototype;
678
679proto.on = function( eventName, listener ) {
680 if ( !eventName || !listener ) {
681 return;
682 }
683 // set events hash
684 var events = this._events = this._events || {};
685 // set listeners array
686 var listeners = events[ eventName ] = events[ eventName ] || [];
687 // only add once
688 if ( listeners.indexOf( listener ) == -1 ) {
689 listeners.push( listener );
690 }
691
692 return this;
693};
694
695proto.once = function( eventName, listener ) {
696 if ( !eventName || !listener ) {
697 return;
698 }
699 // add event
700 this.on( eventName, listener );
701 // set once flag
702 // set onceEvents hash
703 var onceEvents = this._onceEvents = this._onceEvents || {};
704 // set onceListeners object
705 var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {};
706 // set flag
707 onceListeners[ listener ] = true;
708
709 return this;
710};
711
712proto.off = function( eventName, listener ) {
713 var listeners = this._events && this._events[ eventName ];
714 if ( !listeners || !listeners.length ) {
715 return;
716 }
717 var index = listeners.indexOf( listener );
718 if ( index != -1 ) {
719 listeners.splice( index, 1 );
720 }
721
722 return this;
723};
724
725proto.emitEvent = function( eventName, args ) {
726 var listeners = this._events && this._events[ eventName ];
727 if ( !listeners || !listeners.length ) {
728 return;
729 }
730 // copy over to avoid interference if .off() in listener
731 listeners = listeners.slice(0);
732 args = args || [];
733 // once stuff
734 var onceListeners = this._onceEvents && this._onceEvents[ eventName ];
735
736 for ( var i=0; i < listeners.length; i++ ) {
737 var listener = listeners[i]
738 var isOnce = onceListeners && onceListeners[ listener ];
739 if ( isOnce ) {
740 // remove listener
741 // remove before trigger to prevent recursion
742 this.off( eventName, listener );
743 // unset once flag
744 delete onceListeners[ listener ];
745 }
746 // trigger listener
747 listener.apply( this, args );
748 }
749
750 return this;
751};
752
753proto.allOff = function() {
754 delete this._events;
755 delete this._onceEvents;
756};
757
758return EvEmitter;
759
760}));
761
762/*!
763 * getSize v2.0.3
764 * measure size of elements
765 * MIT license
766 */
767
768/* jshint browser: true, strict: true, undef: true, unused: true */
769/* globals console: false */
770
771( function( window, factory ) {
772 /* jshint strict: false */ /* globals define, module */
773 if ( typeof define == 'function' && define.amd ) {
774 // AMD
775 define( 'get-size/get-size',factory );
776 } else if ( typeof module == 'object' && module.exports ) {
777 // CommonJS
778 module.exports = factory();
779 } else {
780 // browser global
781 window.getSize = factory();
782 }
783
784})( window, function factory() {
785'use strict';
786
787// -------------------------- helpers -------------------------- //
788
789// get a number from a string, not a percentage
790function getStyleSize( value ) {
791 var num = parseFloat( value );
792 // not a percent like '100%', and a number
793 var isValid = value.indexOf('%') == -1 && !isNaN( num );
794 return isValid && num;
795}
796
797function noop() {}
798
799var logError = typeof console == 'undefined' ? noop :
800 function( message ) {
801 console.error( message );
802 };
803
804// -------------------------- measurements -------------------------- //
805
806var measurements = [
807 'paddingLeft',
808 'paddingRight',
809 'paddingTop',
810 'paddingBottom',
811 'marginLeft',
812 'marginRight',
813 'marginTop',
814 'marginBottom',
815 'borderLeftWidth',
816 'borderRightWidth',
817 'borderTopWidth',
818 'borderBottomWidth'
819];
820
821var measurementsLength = measurements.length;
822
823function getZeroSize() {
824 var size = {
825 width: 0,
826 height: 0,
827 innerWidth: 0,
828 innerHeight: 0,
829 outerWidth: 0,
830 outerHeight: 0
831 };
832 for ( var i=0; i < measurementsLength; i++ ) {
833 var measurement = measurements[i];
834 size[ measurement ] = 0;
835 }
836 return size;
837}
838
839// -------------------------- getStyle -------------------------- //
840
841/**
842 * getStyle, get style of element, check for Firefox bug
843 * https://bugzilla.mozilla.org/show_bug.cgi?id=548397
844 */
845function getStyle( elem ) {
846 var style = getComputedStyle( elem );
847 if ( !style ) {
848 logError( 'Style returned ' + style +
849 '. Are you running this code in a hidden iframe on Firefox? ' +
850 'See https://bit.ly/getsizebug1' );
851 }
852 return style;
853}
854
855// -------------------------- setup -------------------------- //
856
857var isSetup = false;
858
859var isBoxSizeOuter;
860
861/**
862 * setup
863 * check isBoxSizerOuter
864 * do on first getSize() rather than on page load for Firefox bug
865 */
866function setup() {
867 // setup once
868 if ( isSetup ) {
869 return;
870 }
871 isSetup = true;
872
873 // -------------------------- box sizing -------------------------- //
874
875 /**
876 * Chrome & Safari measure the outer-width on style.width on border-box elems
877 * IE11 & Firefox<29 measures the inner-width
878 */
879 var div = document.createElement('div');
880 div.style.width = '200px';
881 div.style.padding = '1px 2px 3px 4px';
882 div.style.borderStyle = 'solid';
883 div.style.borderWidth = '1px 2px 3px 4px';
884 div.style.boxSizing = 'border-box';
885
886 var body = document.body || document.documentElement;
887 body.appendChild( div );
888 var style = getStyle( div );
889 // round value for browser zoom. desandro/masonry#928
890 isBoxSizeOuter = Math.round( getStyleSize( style.width ) ) == 200;
891 getSize.isBoxSizeOuter = isBoxSizeOuter;
892
893 body.removeChild( div );
894}
895
896// -------------------------- getSize -------------------------- //
897
898function getSize( elem ) {
899 setup();
900
901 // use querySeletor if elem is string
902 if ( typeof elem == 'string' ) {
903 elem = document.querySelector( elem );
904 }
905
906 // do not proceed on non-objects
907 if ( !elem || typeof elem != 'object' || !elem.nodeType ) {
908 return;
909 }
910
911 var style = getStyle( elem );
912
913 // if hidden, everything is 0
914 if ( style.display == 'none' ) {
915 return getZeroSize();
916 }
917
918 var size = {};
919 size.width = elem.offsetWidth;
920 size.height = elem.offsetHeight;
921
922 var isBorderBox = size.isBorderBox = style.boxSizing == 'border-box';
923
924 // get all measurements
925 for ( var i=0; i < measurementsLength; i++ ) {
926 var measurement = measurements[i];
927 var value = style[ measurement ];
928 var num = parseFloat( value );
929 // any 'auto', 'medium' value will be 0
930 size[ measurement ] = !isNaN( num ) ? num : 0;
931 }
932
933 var paddingWidth = size.paddingLeft + size.paddingRight;
934 var paddingHeight = size.paddingTop + size.paddingBottom;
935 var marginWidth = size.marginLeft + size.marginRight;
936 var marginHeight = size.marginTop + size.marginBottom;
937 var borderWidth = size.borderLeftWidth + size.borderRightWidth;
938 var borderHeight = size.borderTopWidth + size.borderBottomWidth;
939
940 var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter;
941
942 // overwrite width and height if we can get it from style
943 var styleWidth = getStyleSize( style.width );
944 if ( styleWidth !== false ) {
945 size.width = styleWidth +
946 // add padding and border unless it's already including it
947 ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth );
948 }
949
950 var styleHeight = getStyleSize( style.height );
951 if ( styleHeight !== false ) {
952 size.height = styleHeight +
953 // add padding and border unless it's already including it
954 ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight );
955 }
956
957 size.innerWidth = size.width - ( paddingWidth + borderWidth );
958 size.innerHeight = size.height - ( paddingHeight + borderHeight );
959
960 size.outerWidth = size.width + marginWidth;
961 size.outerHeight = size.height + marginHeight;
962
963 return size;
964}
965
966return getSize;
967
968});
969
970/**
971 * matchesSelector v2.0.2
972 * matchesSelector( element, '.selector' )
973 * MIT license
974 */
975
976/*jshint browser: true, strict: true, undef: true, unused: true */
977
978( function( window, factory ) {
979 /*global define: false, module: false */
980 'use strict';
981 // universal module definition
982 if ( typeof define == 'function' && define.amd ) {
983 // AMD
984 define( 'desandro-matches-selector/matches-selector',factory );
985 } else if ( typeof module == 'object' && module.exports ) {
986 // CommonJS
987 module.exports = factory();
988 } else {
989 // browser global
990 window.matchesSelector = factory();
991 }
992
993}( window, function factory() {
994 'use strict';
995
996 var matchesMethod = ( function() {
997 var ElemProto = window.Element.prototype;
998 // check for the standard method name first
999 if ( ElemProto.matches ) {
1000 return 'matches';
1001 }
1002 // check un-prefixed
1003 if ( ElemProto.matchesSelector ) {
1004 return 'matchesSelector';
1005 }
1006 // check vendor prefixes
1007 var prefixes = [ 'webkit', 'moz', 'ms', 'o' ];
1008
1009 for ( var i=0; i < prefixes.length; i++ ) {
1010 var prefix = prefixes[i];
1011 var method = prefix + 'MatchesSelector';
1012 if ( ElemProto[ method ] ) {
1013 return method;
1014 }
1015 }
1016 })();
1017
1018 return function matchesSelector( elem, selector ) {
1019 return elem[ matchesMethod ]( selector );
1020 };
1021
1022}));
1023
1024/**
1025 * Fizzy UI utils v2.0.7
1026 * MIT license
1027 */
1028
1029/*jshint browser: true, undef: true, unused: true, strict: true */
1030
1031( function( window, factory ) {
1032 // universal module definition
1033 /*jshint strict: false */ /*globals define, module, require */
1034
1035 if ( typeof define == 'function' && define.amd ) {
1036 // AMD
1037 define( 'fizzy-ui-utils/utils',[
1038 'desandro-matches-selector/matches-selector'
1039 ], function( matchesSelector ) {
1040 return factory( window, matchesSelector );
1041 });
1042 } else if ( typeof module == 'object' && module.exports ) {
1043 // CommonJS
1044 module.exports = factory(
1045 window,
1046 require('desandro-matches-selector')
1047 );
1048 } else {
1049 // browser global
1050 window.fizzyUIUtils = factory(
1051 window,
1052 window.matchesSelector
1053 );
1054 }
1055
1056}( window, function factory( window, matchesSelector ) {
1057
1058
1059
1060var utils = {};
1061
1062// ----- extend ----- //
1063
1064// extends objects
1065utils.extend = function( a, b ) {
1066 for ( var prop in b ) {
1067 a[ prop ] = b[ prop ];
1068 }
1069 return a;
1070};
1071
1072// ----- modulo ----- //
1073
1074utils.modulo = function( num, div ) {
1075 return ( ( num % div ) + div ) % div;
1076};
1077
1078// ----- makeArray ----- //
1079
1080var arraySlice = Array.prototype.slice;
1081
1082// turn element or nodeList into an array
1083utils.makeArray = function( obj ) {
1084 if ( Array.isArray( obj ) ) {
1085 // use object if already an array
1086 return obj;
1087 }
1088 // return empty array if undefined or null. #6
1089 if ( obj === null || obj === undefined ) {
1090 return [];
1091 }
1092
1093 var isArrayLike = typeof obj == 'object' && typeof obj.length == 'number';
1094 if ( isArrayLike ) {
1095 // convert nodeList to array
1096 return arraySlice.call( obj );
1097 }
1098
1099 // array of single index
1100 return [ obj ];
1101};
1102
1103// ----- removeFrom ----- //
1104
1105utils.removeFrom = function( ary, obj ) {
1106 var index = ary.indexOf( obj );
1107 if ( index != -1 ) {
1108 ary.splice( index, 1 );
1109 }
1110};
1111
1112// ----- getParent ----- //
1113
1114utils.getParent = function( elem, selector ) {
1115 while ( elem.parentNode && elem != document.body ) {
1116 elem = elem.parentNode;
1117 if ( matchesSelector( elem, selector ) ) {
1118 return elem;
1119 }
1120 }
1121};
1122
1123// ----- getQueryElement ----- //
1124
1125// use element as selector string
1126utils.getQueryElement = function( elem ) {
1127 if ( typeof elem == 'string' ) {
1128 return document.querySelector( elem );
1129 }
1130 return elem;
1131};
1132
1133// ----- handleEvent ----- //
1134
1135// enable .ontype to trigger from .addEventListener( elem, 'type' )
1136utils.handleEvent = function( event ) {
1137 var method = 'on' + event.type;
1138 if ( this[ method ] ) {
1139 this[ method ]( event );
1140 }
1141};
1142
1143// ----- filterFindElements ----- //
1144
1145utils.filterFindElements = function( elems, selector ) {
1146 // make array of elems
1147 elems = utils.makeArray( elems );
1148 var ffElems = [];
1149
1150 elems.forEach( function( elem ) {
1151 // check that elem is an actual element
1152 if ( !( elem instanceof HTMLElement ) ) {
1153 return;
1154 }
1155 // add elem if no selector
1156 if ( !selector ) {
1157 ffElems.push( elem );
1158 return;
1159 }
1160 // filter & find items if we have a selector
1161 // filter
1162 if ( matchesSelector( elem, selector ) ) {
1163 ffElems.push( elem );
1164 }
1165 // find children
1166 var childElems = elem.querySelectorAll( selector );
1167 // concat childElems to filterFound array
1168 for ( var i=0; i < childElems.length; i++ ) {
1169 ffElems.push( childElems[i] );
1170 }
1171 });
1172
1173 return ffElems;
1174};
1175
1176// ----- debounceMethod ----- //
1177
1178utils.debounceMethod = function( _class, methodName, threshold ) {
1179 threshold = threshold || 100;
1180 // original method
1181 var method = _class.prototype[ methodName ];
1182 var timeoutName = methodName + 'Timeout';
1183
1184 _class.prototype[ methodName ] = function() {
1185 var timeout = this[ timeoutName ];
1186 clearTimeout( timeout );
1187
1188 var args = arguments;
1189 var _this = this;
1190 this[ timeoutName ] = setTimeout( function() {
1191 method.apply( _this, args );
1192 delete _this[ timeoutName ];
1193 }, threshold );
1194 };
1195};
1196
1197// ----- docReady ----- //
1198
1199utils.docReady = function( callback ) {
1200 var readyState = document.readyState;
1201 if ( readyState == 'complete' || readyState == 'interactive' ) {
1202 // do async to allow for other scripts to run. metafizzy/flickity#441
1203 setTimeout( callback );
1204 } else {
1205 document.addEventListener( 'DOMContentLoaded', callback );
1206 }
1207};
1208
1209// ----- htmlInit ----- //
1210
1211// http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
1212utils.toDashed = function( str ) {
1213 return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
1214 return $1 + '-' + $2;
1215 }).toLowerCase();
1216};
1217
1218var console = window.console;
1219/**
1220 * allow user to initialize classes via [data-namespace] or .js-namespace class
1221 * htmlInit( Widget, 'widgetName' )
1222 * options are parsed from data-namespace-options
1223 */
1224utils.htmlInit = function( WidgetClass, namespace ) {
1225 utils.docReady( function() {
1226 var dashedNamespace = utils.toDashed( namespace );
1227 var dataAttr = 'data-' + dashedNamespace;
1228 var dataAttrElems = document.querySelectorAll( '[' + dataAttr + ']' );
1229 var jsDashElems = document.querySelectorAll( '.js-' + dashedNamespace );
1230 var elems = utils.makeArray( dataAttrElems )
1231 .concat( utils.makeArray( jsDashElems ) );
1232 var dataOptionsAttr = dataAttr + '-options';
1233 var jQuery = window.jQuery;
1234
1235 elems.forEach( function( elem ) {
1236 var attr = elem.getAttribute( dataAttr ) ||
1237 elem.getAttribute( dataOptionsAttr );
1238 var options;
1239 try {
1240 options = attr && JSON.parse( attr );
1241 } catch ( error ) {
1242 // log error, do not initialize
1243 if ( console ) {
1244 console.error( 'Error parsing ' + dataAttr + ' on ' + elem.className +
1245 ': ' + error );
1246 }
1247 return;
1248 }
1249 // initialize
1250 var instance = new WidgetClass( elem, options );
1251 // make available via $().data('namespace')
1252 if ( jQuery ) {
1253 jQuery.data( elem, namespace, instance );
1254 }
1255 });
1256
1257 });
1258};
1259
1260// ----- ----- //
1261
1262return utils;
1263
1264}));
1265
1266// Flickity.Cell
1267( function( window, factory ) {
1268 // universal module definition
1269 if ( typeof define == 'function' && define.amd ) {
1270 // AMD
1271 define( 'flickity/js/cell',[
1272 'get-size/get-size',
1273 ], function( getSize ) {
1274 return factory( window, getSize );
1275 } );
1276 } else if ( typeof module == 'object' && module.exports ) {
1277 // CommonJS
1278 module.exports = factory(
1279 window,
1280 require('get-size')
1281 );
1282 } else {
1283 // browser global
1284 window.Flickity = window.Flickity || {};
1285 window.Flickity.Cell = factory(
1286 window,
1287 window.getSize
1288 );
1289 }
1290
1291}( window, function factory( window, getSize ) {
1292
1293
1294
1295function Cell( elem, parent ) {
1296 this.element = elem;
1297 this.parent = parent;
1298
1299 this.create();
1300}
1301
1302var proto = Cell.prototype;
1303
1304proto.create = function() {
1305 this.element.style.position = 'absolute';
1306 this.element.setAttribute( 'aria-hidden', 'true' );
1307 this.x = 0;
1308 this.shift = 0;
1309};
1310
1311proto.destroy = function() {
1312 // reset style
1313 this.unselect();
1314 this.element.style.position = '';
1315 var side = this.parent.originSide;
1316 this.element.style[ side ] = '';
1317 this.element.removeAttribute('aria-hidden');
1318};
1319
1320proto.getSize = function() {
1321 this.size = getSize( this.element );
1322};
1323
1324proto.setPosition = function( x ) {
1325 this.x = x;
1326 this.updateTarget();
1327 this.renderPosition( x );
1328};
1329
1330// setDefaultTarget v1 method, backwards compatibility, remove in v3
1331proto.updateTarget = proto.setDefaultTarget = function() {
1332 var marginProperty = this.parent.originSide == 'left' ? 'marginLeft' : 'marginRight';
1333 this.target = this.x + this.size[ marginProperty ] +
1334 this.size.width * this.parent.cellAlign;
1335};
1336
1337proto.renderPosition = function( x ) {
1338 // render position of cell with in slider
1339 var side = this.parent.originSide;
1340 this.element.style[ side ] = this.parent.getPositionValue( x );
1341};
1342
1343proto.select = function() {
1344 this.element.classList.add('is-selected');
1345 this.element.removeAttribute('aria-hidden');
1346};
1347
1348proto.unselect = function() {
1349 this.element.classList.remove('is-selected');
1350 this.element.setAttribute( 'aria-hidden', 'true' );
1351};
1352
1353/**
1354 * @param {Integer} shift - 0, 1, or -1
1355 */
1356proto.wrapShift = function( shift ) {
1357 this.shift = shift;
1358 this.renderPosition( this.x + this.parent.slideableWidth * shift );
1359};
1360
1361proto.remove = function() {
1362 this.element.parentNode.removeChild( this.element );
1363};
1364
1365return Cell;
1366
1367} ) );
1368
1369// slide
1370( function( window, factory ) {
1371 // universal module definition
1372 if ( typeof define == 'function' && define.amd ) {
1373 // AMD
1374 define( 'flickity/js/slide',factory );
1375 } else if ( typeof module == 'object' && module.exports ) {
1376 // CommonJS
1377 module.exports = factory();
1378 } else {
1379 // browser global
1380 window.Flickity = window.Flickity || {};
1381 window.Flickity.Slide = factory();
1382 }
1383
1384}( window, function factory() {
1385'use strict';
1386
1387function Slide( parent ) {
1388 this.parent = parent;
1389 this.isOriginLeft = parent.originSide == 'left';
1390 this.cells = [];
1391 this.outerWidth = 0;
1392 this.height = 0;
1393}
1394
1395var proto = Slide.prototype;
1396
1397proto.addCell = function( cell ) {
1398 this.cells.push( cell );
1399 this.outerWidth += cell.size.outerWidth;
1400 this.height = Math.max( cell.size.outerHeight, this.height );
1401 // first cell stuff
1402 if ( this.cells.length == 1 ) {
1403 this.x = cell.x; // x comes from first cell
1404 var beginMargin = this.isOriginLeft ? 'marginLeft' : 'marginRight';
1405 this.firstMargin = cell.size[ beginMargin ];
1406 }
1407};
1408
1409proto.updateTarget = function() {
1410 var endMargin = this.isOriginLeft ? 'marginRight' : 'marginLeft';
1411 var lastCell = this.getLastCell();
1412 var lastMargin = lastCell ? lastCell.size[ endMargin ] : 0;
1413 var slideWidth = this.outerWidth - ( this.firstMargin + lastMargin );
1414 this.target = this.x + this.firstMargin + slideWidth * this.parent.cellAlign;
1415};
1416
1417proto.getLastCell = function() {
1418 return this.cells[ this.cells.length - 1 ];
1419};
1420
1421proto.select = function() {
1422 this.cells.forEach( function( cell ) {
1423 cell.select();
1424 } );
1425};
1426
1427proto.unselect = function() {
1428 this.cells.forEach( function( cell ) {
1429 cell.unselect();
1430 } );
1431};
1432
1433proto.getCellElements = function() {
1434 return this.cells.map( function( cell ) {
1435 return cell.element;
1436 } );
1437};
1438
1439return Slide;
1440
1441} ) );
1442
1443// animate
1444( function( window, factory ) {
1445 // universal module definition
1446 if ( typeof define == 'function' && define.amd ) {
1447 // AMD
1448 define( 'flickity/js/animate',[
1449 'fizzy-ui-utils/utils',
1450 ], function( utils ) {
1451 return factory( window, utils );
1452 } );
1453 } else if ( typeof module == 'object' && module.exports ) {
1454 // CommonJS
1455 module.exports = factory(
1456 window,
1457 require('fizzy-ui-utils')
1458 );
1459 } else {
1460 // browser global
1461 window.Flickity = window.Flickity || {};
1462 window.Flickity.animatePrototype = factory(
1463 window,
1464 window.fizzyUIUtils
1465 );
1466 }
1467
1468}( window, function factory( window, utils ) {
1469
1470
1471
1472// -------------------------- animate -------------------------- //
1473
1474var proto = {};
1475
1476proto.startAnimation = function() {
1477 if ( this.isAnimating ) {
1478 return;
1479 }
1480
1481 this.isAnimating = true;
1482 this.restingFrames = 0;
1483 this.animate();
1484};
1485
1486proto.animate = function() {
1487 this.applyDragForce();
1488 this.applySelectedAttraction();
1489
1490 var previousX = this.x;
1491
1492 this.integratePhysics();
1493 this.positionSlider();
1494 this.settle( previousX );
1495 // animate next frame
1496 if ( this.isAnimating ) {
1497 var _this = this;
1498 requestAnimationFrame( function animateFrame() {
1499 _this.animate();
1500 } );
1501 }
1502};
1503
1504proto.positionSlider = function() {
1505 var x = this.x;
1506 // wrap position around
1507 if ( this.options.wrapAround && this.cells.length > 1 ) {
1508 x = utils.modulo( x, this.slideableWidth );
1509 x -= this.slideableWidth;
1510 this.shiftWrapCells( x );
1511 }
1512
1513 this.setTranslateX( x, this.isAnimating );
1514 this.dispatchScrollEvent();
1515};
1516
1517proto.setTranslateX = function( x, is3d ) {
1518 x += this.cursorPosition;
1519 // reverse if right-to-left and using transform
1520 x = this.options.rightToLeft ? -x : x;
1521 var translateX = this.getPositionValue( x );
1522 // use 3D transforms for hardware acceleration on iOS
1523 // but use 2D when settled, for better font-rendering
1524 this.slider.style.transform = is3d ?
1525 'translate3d(' + translateX + ',0,0)' : 'translateX(' + translateX + ')';
1526};
1527
1528proto.dispatchScrollEvent = function() {
1529 var firstSlide = this.slides[0];
1530 if ( !firstSlide ) {
1531 return;
1532 }
1533 var positionX = -this.x - firstSlide.target;
1534 var progress = positionX / this.slidesWidth;
1535 this.dispatchEvent( 'scroll', null, [ progress, positionX ] );
1536};
1537
1538proto.positionSliderAtSelected = function() {
1539 if ( !this.cells.length ) {
1540 return;
1541 }
1542 this.x = -this.selectedSlide.target;
1543 this.velocity = 0; // stop wobble
1544 this.positionSlider();
1545};
1546
1547proto.getPositionValue = function( position ) {
1548 if ( this.options.percentPosition ) {
1549 // percent position, round to 2 digits, like 12.34%
1550 return ( Math.round( ( position / this.size.innerWidth ) * 10000 ) * 0.01 ) + '%';
1551 } else {
1552 // pixel positioning
1553 return Math.round( position ) + 'px';
1554 }
1555};
1556
1557proto.settle = function( previousX ) {
1558 // keep track of frames where x hasn't moved
1559 var isResting = !this.isPointerDown &&
1560 Math.round( this.x * 100 ) == Math.round( previousX * 100 );
1561 if ( isResting ) {
1562 this.restingFrames++;
1563 }
1564 // stop animating if resting for 3 or more frames
1565 if ( this.restingFrames > 2 ) {
1566 this.isAnimating = false;
1567 delete this.isFreeScrolling;
1568 // render position with translateX when settled
1569 this.positionSlider();
1570 this.dispatchEvent( 'settle', null, [ this.selectedIndex ] );
1571 }
1572};
1573
1574proto.shiftWrapCells = function( x ) {
1575 // shift before cells
1576 var beforeGap = this.cursorPosition + x;
1577 this._shiftCells( this.beforeShiftCells, beforeGap, -1 );
1578 // shift after cells
1579 var afterGap = this.size.innerWidth - ( x + this.slideableWidth + this.cursorPosition );
1580 this._shiftCells( this.afterShiftCells, afterGap, 1 );
1581};
1582
1583proto._shiftCells = function( cells, gap, shift ) {
1584 for ( var i = 0; i < cells.length; i++ ) {
1585 var cell = cells[i];
1586 var cellShift = gap > 0 ? shift : 0;
1587 cell.wrapShift( cellShift );
1588 gap -= cell.size.outerWidth;
1589 }
1590};
1591
1592proto._unshiftCells = function( cells ) {
1593 if ( !cells || !cells.length ) {
1594 return;
1595 }
1596 for ( var i = 0; i < cells.length; i++ ) {
1597 cells[i].wrapShift( 0 );
1598 }
1599};
1600
1601// -------------------------- physics -------------------------- //
1602
1603proto.integratePhysics = function() {
1604 this.x += this.velocity;
1605 this.velocity *= this.getFrictionFactor();
1606};
1607
1608proto.applyForce = function( force ) {
1609 this.velocity += force;
1610};
1611
1612proto.getFrictionFactor = function() {
1613 return 1 - this.options[ this.isFreeScrolling ? 'freeScrollFriction' : 'friction' ];
1614};
1615
1616proto.getRestingPosition = function() {
1617 // my thanks to Steven Wittens, who simplified this math greatly
1618 return this.x + this.velocity / ( 1 - this.getFrictionFactor() );
1619};
1620
1621proto.applyDragForce = function() {
1622 if ( !this.isDraggable || !this.isPointerDown ) {
1623 return;
1624 }
1625 // change the position to drag position by applying force
1626 var dragVelocity = this.dragX - this.x;
1627 var dragForce = dragVelocity - this.velocity;
1628 this.applyForce( dragForce );
1629};
1630
1631proto.applySelectedAttraction = function() {
1632 // do not attract if pointer down or no slides
1633 var dragDown = this.isDraggable && this.isPointerDown;
1634 if ( dragDown || this.isFreeScrolling || !this.slides.length ) {
1635 return;
1636 }
1637 var distance = this.selectedSlide.target * -1 - this.x;
1638 var force = distance * this.options.selectedAttraction;
1639 this.applyForce( force );
1640};
1641
1642return proto;
1643
1644} ) );
1645
1646// Flickity main
1647/* eslint-disable max-params */
1648( function( window, factory ) {
1649 // universal module definition
1650 if ( typeof define == 'function' && define.amd ) {
1651 // AMD
1652 define( 'flickity/js/flickity',[
1653 'ev-emitter/ev-emitter',
1654 'get-size/get-size',
1655 'fizzy-ui-utils/utils',
1656 './cell',
1657 './slide',
1658 './animate',
1659 ], function( EvEmitter, getSize, utils, Cell, Slide, animatePrototype ) {
1660 return factory( window, EvEmitter, getSize, utils, Cell, Slide, animatePrototype );
1661 } );
1662 } else if ( typeof module == 'object' && module.exports ) {
1663 // CommonJS
1664 module.exports = factory(
1665 window,
1666 require('ev-emitter'),
1667 require('get-size'),
1668 require('fizzy-ui-utils'),
1669 require('./cell'),
1670 require('./slide'),
1671 require('./animate')
1672 );
1673 } else {
1674 // browser global
1675 var _Flickity = window.Flickity;
1676
1677 window.Flickity = factory(
1678 window,
1679 window.EvEmitter,
1680 window.getSize,
1681 window.fizzyUIUtils,
1682 _Flickity.Cell,
1683 _Flickity.Slide,
1684 _Flickity.animatePrototype
1685 );
1686 }
1687
1688}( window, function factory( window, EvEmitter, getSize,
1689 utils, Cell, Slide, animatePrototype ) {
1690
1691/* eslint-enable max-params */
1692
1693
1694// vars
1695var jQuery = window.jQuery;
1696var getComputedStyle = window.getComputedStyle;
1697var console = window.console;
1698
1699function moveElements( elems, toElem ) {
1700 elems = utils.makeArray( elems );
1701 while ( elems.length ) {
1702 toElem.appendChild( elems.shift() );
1703 }
1704}
1705
1706// -------------------------- Flickity -------------------------- //
1707
1708// globally unique identifiers
1709var GUID = 0;
1710// internal store of all Flickity intances
1711var instances = {};
1712
1713function Flickity( element, options ) {
1714 var queryElement = utils.getQueryElement( element );
1715 if ( !queryElement ) {
1716 if ( console ) {
1717 console.error( 'Bad element for Flickity: ' + ( queryElement || element ) );
1718 }
1719 return;
1720 }
1721 this.element = queryElement;
1722 // do not initialize twice on same element
1723 if ( this.element.flickityGUID ) {
1724 var instance = instances[ this.element.flickityGUID ];
1725 if ( instance ) instance.option( options );
1726 return instance;
1727 }
1728
1729 // add jQuery
1730 if ( jQuery ) {
1731 this.$element = jQuery( this.element );
1732 }
1733 // options
1734 this.options = utils.extend( {}, this.constructor.defaults );
1735 this.option( options );
1736
1737 // kick things off
1738 this._create();
1739}
1740
1741Flickity.defaults = {
1742 accessibility: true,
1743 // adaptiveHeight: false,
1744 cellAlign: 'center',
1745 // cellSelector: undefined,
1746 // contain: false,
1747 freeScrollFriction: 0.075, // friction when free-scrolling
1748 friction: 0.28, // friction when selecting
1749 namespaceJQueryEvents: true,
1750 // initialIndex: 0,
1751 percentPosition: true,
1752 resize: true,
1753 selectedAttraction: 0.025,
1754 setGallerySize: true,
1755 // watchCSS: false,
1756 // wrapAround: false
1757};
1758
1759// hash of methods triggered on _create()
1760Flickity.createMethods = [];
1761
1762var proto = Flickity.prototype;
1763// inherit EventEmitter
1764utils.extend( proto, EvEmitter.prototype );
1765
1766proto._create = function() {
1767 // add id for Flickity.data
1768 var id = this.guid = ++GUID;
1769 this.element.flickityGUID = id; // expando
1770 instances[ id ] = this; // associate via id
1771 // initial properties
1772 this.selectedIndex = 0;
1773 // how many frames slider has been in same position
1774 this.restingFrames = 0;
1775 // initial physics properties
1776 this.x = 0;
1777 this.velocity = 0;
1778 this.originSide = this.options.rightToLeft ? 'right' : 'left';
1779 // create viewport & slider
1780 this.viewport = document.createElement('div');
1781 this.viewport.className = 'flickity-viewport';
1782 this._createSlider();
1783
1784 if ( this.options.resize || this.options.watchCSS ) {
1785 window.addEventListener( 'resize', this );
1786 }
1787
1788 // add listeners from on option
1789 for ( var eventName in this.options.on ) {
1790 var listener = this.options.on[ eventName ];
1791 this.on( eventName, listener );
1792 }
1793
1794 Flickity.createMethods.forEach( function( method ) {
1795 this[ method ]();
1796 }, this );
1797
1798 if ( this.options.watchCSS ) {
1799 this.watchCSS();
1800 } else {
1801 this.activate();
1802 }
1803
1804};
1805
1806/**
1807 * set options
1808 * @param {Object} opts - options to extend
1809 */
1810proto.option = function( opts ) {
1811 utils.extend( this.options, opts );
1812};
1813
1814proto.activate = function() {
1815 if ( this.isActive ) {
1816 return;
1817 }
1818 this.isActive = true;
1819 this.element.classList.add('flickity-enabled');
1820 if ( this.options.rightToLeft ) {
1821 this.element.classList.add('flickity-rtl');
1822 }
1823
1824 this.getSize();
1825 // move initial cell elements so they can be loaded as cells
1826 var cellElems = this._filterFindCellElements( this.element.children );
1827 moveElements( cellElems, this.slider );
1828 this.viewport.appendChild( this.slider );
1829 this.element.appendChild( this.viewport );
1830 // get cells from children
1831 this.reloadCells();
1832
1833 if ( this.options.accessibility ) {
1834 // allow element to focusable
1835 this.element.tabIndex = 0;
1836 // listen for key presses
1837 this.element.addEventListener( 'keydown', this );
1838 }
1839
1840 this.emitEvent('activate');
1841 this.selectInitialIndex();
1842 // flag for initial activation, for using initialIndex
1843 this.isInitActivated = true;
1844 // ready event. #493
1845 this.dispatchEvent('ready');
1846};
1847
1848// slider positions the cells
1849proto._createSlider = function() {
1850 // slider element does all the positioning
1851 var slider = document.createElement('div');
1852 slider.className = 'flickity-slider';
1853 slider.style[ this.originSide ] = 0;
1854 this.slider = slider;
1855};
1856
1857proto._filterFindCellElements = function( elems ) {
1858 return utils.filterFindElements( elems, this.options.cellSelector );
1859};
1860
1861// goes through all children
1862proto.reloadCells = function() {
1863 // collection of item elements
1864 this.cells = this._makeCells( this.slider.children );
1865 this.positionCells();
1866 this._getWrapShiftCells();
1867 this.setGallerySize();
1868};
1869
1870/**
1871 * turn elements into Flickity.Cells
1872 * @param {[Array, NodeList, HTMLElement]} elems - elements to make into cells
1873 * @returns {Array} items - collection of new Flickity Cells
1874 */
1875proto._makeCells = function( elems ) {
1876 var cellElems = this._filterFindCellElements( elems );
1877
1878 // create new Flickity for collection
1879 var cells = cellElems.map( function( cellElem ) {
1880 return new Cell( cellElem, this );
1881 }, this );
1882
1883 return cells;
1884};
1885
1886proto.getLastCell = function() {
1887 return this.cells[ this.cells.length - 1 ];
1888};
1889
1890proto.getLastSlide = function() {
1891 return this.slides[ this.slides.length - 1 ];
1892};
1893
1894// positions all cells
1895proto.positionCells = function() {
1896 // size all cells
1897 this._sizeCells( this.cells );
1898 // position all cells
1899 this._positionCells( 0 );
1900};
1901
1902/**
1903 * position certain cells
1904 * @param {Integer} index - which cell to start with
1905 */
1906proto._positionCells = function( index ) {
1907 index = index || 0;
1908 // also measure maxCellHeight
1909 // start 0 if positioning all cells
1910 this.maxCellHeight = index ? this.maxCellHeight || 0 : 0;
1911 var cellX = 0;
1912 // get cellX
1913 if ( index > 0 ) {
1914 var startCell = this.cells[ index - 1 ];
1915 cellX = startCell.x + startCell.size.outerWidth;
1916 }
1917 var len = this.cells.length;
1918 for ( var i = index; i < len; i++ ) {
1919 var cell = this.cells[i];
1920 cell.setPosition( cellX );
1921 cellX += cell.size.outerWidth;
1922 this.maxCellHeight = Math.max( cell.size.outerHeight, this.maxCellHeight );
1923 }
1924 // keep track of cellX for wrap-around
1925 this.slideableWidth = cellX;
1926 // slides
1927 this.updateSlides();
1928 // contain slides target
1929 this._containSlides();
1930 // update slidesWidth
1931 this.slidesWidth = len ? this.getLastSlide().target - this.slides[0].target : 0;
1932};
1933
1934/**
1935 * cell.getSize() on multiple cells
1936 * @param {Array} cells - cells to size
1937 */
1938proto._sizeCells = function( cells ) {
1939 cells.forEach( function( cell ) {
1940 cell.getSize();
1941 } );
1942};
1943
1944// -------------------------- -------------------------- //
1945
1946proto.updateSlides = function() {
1947 this.slides = [];
1948 if ( !this.cells.length ) {
1949 return;
1950 }
1951
1952 var slide = new Slide( this );
1953 this.slides.push( slide );
1954 var isOriginLeft = this.originSide == 'left';
1955 var nextMargin = isOriginLeft ? 'marginRight' : 'marginLeft';
1956
1957 var canCellFit = this._getCanCellFit();
1958
1959 this.cells.forEach( function( cell, i ) {
1960 // just add cell if first cell in slide
1961 if ( !slide.cells.length ) {
1962 slide.addCell( cell );
1963 return;
1964 }
1965
1966 var slideWidth = ( slide.outerWidth - slide.firstMargin ) +
1967 ( cell.size.outerWidth - cell.size[ nextMargin ] );
1968
1969 if ( canCellFit.call( this, i, slideWidth ) ) {
1970 slide.addCell( cell );
1971 } else {
1972 // doesn't fit, new slide
1973 slide.updateTarget();
1974
1975 slide = new Slide( this );
1976 this.slides.push( slide );
1977 slide.addCell( cell );
1978 }
1979 }, this );
1980 // last slide
1981 slide.updateTarget();
1982 // update .selectedSlide
1983 this.updateSelectedSlide();
1984};
1985
1986proto._getCanCellFit = function() {
1987 var groupCells = this.options.groupCells;
1988 if ( !groupCells ) {
1989 return function() {
1990 return false;
1991 };
1992 } else if ( typeof groupCells == 'number' ) {
1993 // group by number. 3 -> [0,1,2], [3,4,5], ...
1994 var number = parseInt( groupCells, 10 );
1995 return function( i ) {
1996 return ( i % number ) !== 0;
1997 };
1998 }
1999 // default, group by width of slide
2000 // parse '75%
2001 var percentMatch = typeof groupCells == 'string' &&
2002 groupCells.match( /^(\d+)%$/ );
2003 var percent = percentMatch ? parseInt( percentMatch[1], 10 ) / 100 : 1;
2004 return function( i, slideWidth ) {
2005 /* eslint-disable-next-line no-invalid-this */
2006 return slideWidth <= ( this.size.innerWidth + 1 ) * percent;
2007 };
2008};
2009
2010// alias _init for jQuery plugin .flickity()
2011proto._init =
2012proto.reposition = function() {
2013 this.positionCells();
2014 this.positionSliderAtSelected();
2015};
2016
2017proto.getSize = function() {
2018 this.size = getSize( this.element );
2019 this.setCellAlign();
2020 this.cursorPosition = this.size.innerWidth * this.cellAlign;
2021};
2022
2023var cellAlignShorthands = {
2024 // cell align, then based on origin side
2025 center: {
2026 left: 0.5,
2027 right: 0.5,
2028 },
2029 left: {
2030 left: 0,
2031 right: 1,
2032 },
2033 right: {
2034 right: 0,
2035 left: 1,
2036 },
2037};
2038
2039proto.setCellAlign = function() {
2040 var shorthand = cellAlignShorthands[ this.options.cellAlign ];
2041 this.cellAlign = shorthand ? shorthand[ this.originSide ] : this.options.cellAlign;
2042};
2043
2044proto.setGallerySize = function() {
2045 if ( this.options.setGallerySize ) {
2046 var height = this.options.adaptiveHeight && this.selectedSlide ?
2047 this.selectedSlide.height : this.maxCellHeight;
2048 this.viewport.style.height = height + 'px';
2049 }
2050};
2051
2052proto._getWrapShiftCells = function() {
2053 // only for wrap-around
2054 if ( !this.options.wrapAround ) {
2055 return;
2056 }
2057 // unshift previous cells
2058 this._unshiftCells( this.beforeShiftCells );
2059 this._unshiftCells( this.afterShiftCells );
2060 // get before cells
2061 // initial gap
2062 var gapX = this.cursorPosition;
2063 var cellIndex = this.cells.length - 1;
2064 this.beforeShiftCells = this._getGapCells( gapX, cellIndex, -1 );
2065 // get after cells
2066 // ending gap between last cell and end of gallery viewport
2067 gapX = this.size.innerWidth - this.cursorPosition;
2068 // start cloning at first cell, working forwards
2069 this.afterShiftCells = this._getGapCells( gapX, 0, 1 );
2070};
2071
2072proto._getGapCells = function( gapX, cellIndex, increment ) {
2073 // keep adding cells until the cover the initial gap
2074 var cells = [];
2075 while ( gapX > 0 ) {
2076 var cell = this.cells[ cellIndex ];
2077 if ( !cell ) {
2078 break;
2079 }
2080 cells.push( cell );
2081 cellIndex += increment;
2082 gapX -= cell.size.outerWidth;
2083 }
2084 return cells;
2085};
2086
2087// ----- contain ----- //
2088
2089// contain cell targets so no excess sliding
2090proto._containSlides = function() {
2091 if ( !this.options.contain || this.options.wrapAround || !this.cells.length ) {
2092 return;
2093 }
2094 var isRightToLeft = this.options.rightToLeft;
2095 var beginMargin = isRightToLeft ? 'marginRight' : 'marginLeft';
2096 var endMargin = isRightToLeft ? 'marginLeft' : 'marginRight';
2097 var contentWidth = this.slideableWidth - this.getLastCell().size[ endMargin ];
2098 // content is less than gallery size
2099 var isContentSmaller = contentWidth < this.size.innerWidth;
2100 // bounds
2101 var beginBound = this.cursorPosition + this.cells[0].size[ beginMargin ];
2102 var endBound = contentWidth - this.size.innerWidth * ( 1 - this.cellAlign );
2103 // contain each cell target
2104 this.slides.forEach( function( slide ) {
2105 if ( isContentSmaller ) {
2106 // all cells fit inside gallery
2107 slide.target = contentWidth * this.cellAlign;
2108 } else {
2109 // contain to bounds
2110 slide.target = Math.max( slide.target, beginBound );
2111 slide.target = Math.min( slide.target, endBound );
2112 }
2113 }, this );
2114};
2115
2116// ----- ----- //
2117
2118/**
2119 * emits events via eventEmitter and jQuery events
2120 * @param {String} type - name of event
2121 * @param {Event} event - original event
2122 * @param {Array} args - extra arguments
2123 */
2124proto.dispatchEvent = function( type, event, args ) {
2125 var emitArgs = event ? [ event ].concat( args ) : args;
2126 this.emitEvent( type, emitArgs );
2127
2128 if ( jQuery && this.$element ) {
2129 // default trigger with type if no event
2130 type += this.options.namespaceJQueryEvents ? '.flickity' : '';
2131 var $event = type;
2132 if ( event ) {
2133 // create jQuery event
2134 var jQEvent = new jQuery.Event( event );
2135 jQEvent.type = type;
2136 $event = jQEvent;
2137 }
2138 this.$element.trigger( $event, args );
2139 }
2140};
2141
2142// -------------------------- select -------------------------- //
2143
2144/**
2145 * @param {Integer} index - index of the slide
2146 * @param {Boolean} isWrap - will wrap-around to last/first if at the end
2147 * @param {Boolean} isInstant - will immediately set position at selected cell
2148 */
2149proto.select = function( index, isWrap, isInstant ) {
2150 if ( !this.isActive ) {
2151 return;
2152 }
2153 index = parseInt( index, 10 );
2154 this._wrapSelect( index );
2155
2156 if ( this.options.wrapAround || isWrap ) {
2157 index = utils.modulo( index, this.slides.length );
2158 }
2159 // bail if invalid index
2160 if ( !this.slides[ index ] ) {
2161 return;
2162 }
2163 var prevIndex = this.selectedIndex;
2164 this.selectedIndex = index;
2165 this.updateSelectedSlide();
2166 if ( isInstant ) {
2167 this.positionSliderAtSelected();
2168 } else {
2169 this.startAnimation();
2170 }
2171 if ( this.options.adaptiveHeight ) {
2172 this.setGallerySize();
2173 }
2174 // events
2175 this.dispatchEvent( 'select', null, [ index ] );
2176 // change event if new index
2177 if ( index != prevIndex ) {
2178 this.dispatchEvent( 'change', null, [ index ] );
2179 }
2180 // old v1 event name, remove in v3
2181 this.dispatchEvent('cellSelect');
2182};
2183
2184// wraps position for wrapAround, to move to closest slide. #113
2185proto._wrapSelect = function( index ) {
2186 var len = this.slides.length;
2187 var isWrapping = this.options.wrapAround && len > 1;
2188 if ( !isWrapping ) {
2189 return index;
2190 }
2191 var wrapIndex = utils.modulo( index, len );
2192 // go to shortest
2193 var delta = Math.abs( wrapIndex - this.selectedIndex );
2194 var backWrapDelta = Math.abs( ( wrapIndex + len ) - this.selectedIndex );
2195 var forewardWrapDelta = Math.abs( ( wrapIndex - len ) - this.selectedIndex );
2196 if ( !this.isDragSelect && backWrapDelta < delta ) {
2197 index += len;
2198 } else if ( !this.isDragSelect && forewardWrapDelta < delta ) {
2199 index -= len;
2200 }
2201 // wrap position so slider is within normal area
2202 if ( index < 0 ) {
2203 this.x -= this.slideableWidth;
2204 } else if ( index >= len ) {
2205 this.x += this.slideableWidth;
2206 }
2207};
2208
2209proto.previous = function( isWrap, isInstant ) {
2210 this.select( this.selectedIndex - 1, isWrap, isInstant );
2211};
2212
2213proto.next = function( isWrap, isInstant ) {
2214 this.select( this.selectedIndex + 1, isWrap, isInstant );
2215};
2216
2217proto.updateSelectedSlide = function() {
2218 var slide = this.slides[ this.selectedIndex ];
2219 // selectedIndex could be outside of slides, if triggered before resize()
2220 if ( !slide ) {
2221 return;
2222 }
2223 // unselect previous selected slide
2224 this.unselectSelectedSlide();
2225 // update new selected slide
2226 this.selectedSlide = slide;
2227 slide.select();
2228 this.selectedCells = slide.cells;
2229 this.selectedElements = slide.getCellElements();
2230 // HACK: selectedCell & selectedElement is first cell in slide, backwards compatibility
2231 // Remove in v3?
2232 this.selectedCell = slide.cells[0];
2233 this.selectedElement = this.selectedElements[0];
2234};
2235
2236proto.unselectSelectedSlide = function() {
2237 if ( this.selectedSlide ) {
2238 this.selectedSlide.unselect();
2239 }
2240};
2241
2242proto.selectInitialIndex = function() {
2243 var initialIndex = this.options.initialIndex;
2244 // already activated, select previous selectedIndex
2245 if ( this.isInitActivated ) {
2246 this.select( this.selectedIndex, false, true );
2247 return;
2248 }
2249 // select with selector string
2250 if ( initialIndex && typeof initialIndex == 'string' ) {
2251 var cell = this.queryCell( initialIndex );
2252 if ( cell ) {
2253 this.selectCell( initialIndex, false, true );
2254 return;
2255 }
2256 }
2257
2258 var index = 0;
2259 // select with number
2260 if ( initialIndex && this.slides[ initialIndex ] ) {
2261 index = initialIndex;
2262 }
2263 // select instantly
2264 this.select( index, false, true );
2265};
2266
2267/**
2268 * select slide from number or cell element
2269 * @param {[Element, Number]} value - zero-based index or element to select
2270 * @param {Boolean} isWrap - enables wrapping around for extra index
2271 * @param {Boolean} isInstant - disables slide animation
2272 */
2273proto.selectCell = function( value, isWrap, isInstant ) {
2274 // get cell
2275 var cell = this.queryCell( value );
2276 if ( !cell ) {
2277 return;
2278 }
2279
2280 var index = this.getCellSlideIndex( cell );
2281 this.select( index, isWrap, isInstant );
2282};
2283
2284proto.getCellSlideIndex = function( cell ) {
2285 // get index of slides that has cell
2286 for ( var i = 0; i < this.slides.length; i++ ) {
2287 var slide = this.slides[i];
2288 var index = slide.cells.indexOf( cell );
2289 if ( index != -1 ) {
2290 return i;
2291 }
2292 }
2293};
2294
2295// -------------------------- get cells -------------------------- //
2296
2297/**
2298 * get Flickity.Cell, given an Element
2299 * @param {Element} elem - matching cell element
2300 * @returns {Flickity.Cell} cell - matching cell
2301 */
2302proto.getCell = function( elem ) {
2303 // loop through cells to get the one that matches
2304 for ( var i = 0; i < this.cells.length; i++ ) {
2305 var cell = this.cells[i];
2306 if ( cell.element == elem ) {
2307 return cell;
2308 }
2309 }
2310};
2311
2312/**
2313 * get collection of Flickity.Cells, given Elements
2314 * @param {[Element, Array, NodeList]} elems - multiple elements
2315 * @returns {Array} cells - Flickity.Cells
2316 */
2317proto.getCells = function( elems ) {
2318 elems = utils.makeArray( elems );
2319 var cells = [];
2320 elems.forEach( function( elem ) {
2321 var cell = this.getCell( elem );
2322 if ( cell ) {
2323 cells.push( cell );
2324 }
2325 }, this );
2326 return cells;
2327};
2328
2329/**
2330 * get cell elements
2331 * @returns {Array} cellElems
2332 */
2333proto.getCellElements = function() {
2334 return this.cells.map( function( cell ) {
2335 return cell.element;
2336 } );
2337};
2338
2339/**
2340 * get parent cell from an element
2341 * @param {Element} elem - child element
2342 * @returns {Flickit.Cell} cell - parent cell
2343 */
2344proto.getParentCell = function( elem ) {
2345 // first check if elem is cell
2346 var cell = this.getCell( elem );
2347 if ( cell ) {
2348 return cell;
2349 }
2350 // try to get parent cell elem
2351 elem = utils.getParent( elem, '.flickity-slider > *' );
2352 return this.getCell( elem );
2353};
2354
2355/**
2356 * get cells adjacent to a slide
2357 * @param {Integer} adjCount - number of adjacent slides
2358 * @param {Integer} index - index of slide to start
2359 * @returns {Array} cells - array of Flickity.Cells
2360 */
2361proto.getAdjacentCellElements = function( adjCount, index ) {
2362 if ( !adjCount ) {
2363 return this.selectedSlide.getCellElements();
2364 }
2365 index = index === undefined ? this.selectedIndex : index;
2366
2367 var len = this.slides.length;
2368 if ( 1 + ( adjCount * 2 ) >= len ) {
2369 return this.getCellElements();
2370 }
2371
2372 var cellElems = [];
2373 for ( var i = index - adjCount; i <= index + adjCount; i++ ) {
2374 var slideIndex = this.options.wrapAround ? utils.modulo( i, len ) : i;
2375 var slide = this.slides[ slideIndex ];
2376 if ( slide ) {
2377 cellElems = cellElems.concat( slide.getCellElements() );
2378 }
2379 }
2380 return cellElems;
2381};
2382
2383/**
2384 * select slide from number or cell element
2385 * @param {[Element, String, Number]} selector - element, selector string, or index
2386 * @returns {Flickity.Cell} - matching cell
2387 */
2388proto.queryCell = function( selector ) {
2389 if ( typeof selector == 'number' ) {
2390 // use number as index
2391 return this.cells[ selector ];
2392 }
2393 if ( typeof selector == 'string' ) {
2394 // do not select invalid selectors from hash: #123, #/. #791
2395 if ( selector.match( /^[#.]?[\d/]/ ) ) {
2396 return;
2397 }
2398 // use string as selector, get element
2399 selector = this.element.querySelector( selector );
2400 }
2401 // get cell from element
2402 return this.getCell( selector );
2403};
2404
2405// -------------------------- events -------------------------- //
2406
2407proto.uiChange = function() {
2408 this.emitEvent('uiChange');
2409};
2410
2411// keep focus on element when child UI elements are clicked
2412proto.childUIPointerDown = function( event ) {
2413 // HACK iOS does not allow touch events to bubble up?!
2414 if ( event.type != 'touchstart' ) {
2415 event.preventDefault();
2416 }
2417 this.focus();
2418};
2419
2420// ----- resize ----- //
2421
2422proto.onresize = function() {
2423 this.watchCSS();
2424 this.resize();
2425};
2426
2427utils.debounceMethod( Flickity, 'onresize', 150 );
2428
2429proto.resize = function() {
2430 if ( !this.isActive ) {
2431 return;
2432 }
2433 this.getSize();
2434 // wrap values
2435 if ( this.options.wrapAround ) {
2436 this.x = utils.modulo( this.x, this.slideableWidth );
2437 }
2438 this.positionCells();
2439 this._getWrapShiftCells();
2440 this.setGallerySize();
2441 this.emitEvent('resize');
2442 // update selected index for group slides, instant
2443 // TODO: position can be lost between groups of various numbers
2444 var selectedElement = this.selectedElements && this.selectedElements[0];
2445 this.selectCell( selectedElement, false, true );
2446};
2447
2448// watches the :after property, activates/deactivates
2449proto.watchCSS = function() {
2450 var watchOption = this.options.watchCSS;
2451 if ( !watchOption ) {
2452 return;
2453 }
2454
2455 var afterContent = getComputedStyle( this.element, ':after' ).content;
2456 // activate if :after { content: 'flickity' }
2457 if ( afterContent.indexOf('flickity') != -1 ) {
2458 this.activate();
2459 } else {
2460 this.deactivate();
2461 }
2462};
2463
2464// ----- keydown ----- //
2465
2466// go previous/next if left/right keys pressed
2467proto.onkeydown = function( event ) {
2468 // only work if element is in focus
2469 var isNotFocused = document.activeElement && document.activeElement != this.element;
2470 if ( !this.options.accessibility || isNotFocused ) {
2471 return;
2472 }
2473
2474 var handler = Flickity.keyboardHandlers[ event.keyCode ];
2475 if ( handler ) {
2476 handler.call( this );
2477 }
2478};
2479
2480Flickity.keyboardHandlers = {
2481 // left arrow
2482 37: function() {
2483 var leftMethod = this.options.rightToLeft ? 'next' : 'previous';
2484 this.uiChange();
2485 this[ leftMethod ]();
2486 },
2487 // right arrow
2488 39: function() {
2489 var rightMethod = this.options.rightToLeft ? 'previous' : 'next';
2490 this.uiChange();
2491 this[ rightMethod ]();
2492 },
2493};
2494
2495// ----- focus ----- //
2496
2497proto.focus = function() {
2498 // TODO remove scrollTo once focus options gets more support
2499 // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus ...
2500 // #Browser_compatibility
2501 var prevScrollY = window.pageYOffset;
2502 this.element.focus({ preventScroll: true });
2503 // hack to fix scroll jump after focus, #76
2504 if ( window.pageYOffset != prevScrollY ) {
2505 window.scrollTo( window.pageXOffset, prevScrollY );
2506 }
2507};
2508
2509// -------------------------- destroy -------------------------- //
2510
2511// deactivate all Flickity functionality, but keep stuff available
2512proto.deactivate = function() {
2513 if ( !this.isActive ) {
2514 return;
2515 }
2516 this.element.classList.remove('flickity-enabled');
2517 this.element.classList.remove('flickity-rtl');
2518 this.unselectSelectedSlide();
2519 // destroy cells
2520 this.cells.forEach( function( cell ) {
2521 cell.destroy();
2522 } );
2523 this.element.removeChild( this.viewport );
2524 // move child elements back into element
2525 moveElements( this.slider.children, this.element );
2526 if ( this.options.accessibility ) {
2527 this.element.removeAttribute('tabIndex');
2528 this.element.removeEventListener( 'keydown', this );
2529 }
2530 // set flags
2531 this.isActive = false;
2532 this.emitEvent('deactivate');
2533};
2534
2535proto.destroy = function() {
2536 this.deactivate();
2537 window.removeEventListener( 'resize', this );
2538 this.allOff();
2539 this.emitEvent('destroy');
2540 if ( jQuery && this.$element ) {
2541 jQuery.removeData( this.element, 'flickity' );
2542 }
2543 delete this.element.flickityGUID;
2544 delete instances[ this.guid ];
2545};
2546
2547// -------------------------- prototype -------------------------- //
2548
2549utils.extend( proto, animatePrototype );
2550
2551// -------------------------- extras -------------------------- //
2552
2553/**
2554 * get Flickity instance from element
2555 * @param {[Element, String]} elem - element or selector string
2556 * @returns {Flickity} - Flickity instance
2557 */
2558Flickity.data = function( elem ) {
2559 elem = utils.getQueryElement( elem );
2560 var id = elem && elem.flickityGUID;
2561 return id && instances[ id ];
2562};
2563
2564utils.htmlInit( Flickity, 'flickity' );
2565
2566if ( jQuery && jQuery.bridget ) {
2567 jQuery.bridget( 'flickity', Flickity );
2568}
2569
2570// set internal jQuery, for Webpack + jQuery v3, #478
2571Flickity.setJQuery = function( jq ) {
2572 jQuery = jq;
2573};
2574
2575Flickity.Cell = Cell;
2576Flickity.Slide = Slide;
2577
2578return Flickity;
2579
2580} ) );
2581
2582/*!
2583 * Unipointer v2.3.0
2584 * base class for doing one thing with pointer event
2585 * MIT license
2586 */
2587
2588/*jshint browser: true, undef: true, unused: true, strict: true */
2589
2590( function( window, factory ) {
2591 // universal module definition
2592 /* jshint strict: false */ /*global define, module, require */
2593 if ( typeof define == 'function' && define.amd ) {
2594 // AMD
2595 define( 'unipointer/unipointer',[
2596 'ev-emitter/ev-emitter'
2597 ], function( EvEmitter ) {
2598 return factory( window, EvEmitter );
2599 });
2600 } else if ( typeof module == 'object' && module.exports ) {
2601 // CommonJS
2602 module.exports = factory(
2603 window,
2604 require('ev-emitter')
2605 );
2606 } else {
2607 // browser global
2608 window.Unipointer = factory(
2609 window,
2610 window.EvEmitter
2611 );
2612 }
2613
2614}( window, function factory( window, EvEmitter ) {
2615
2616
2617
2618function noop() {}
2619
2620function Unipointer() {}
2621
2622// inherit EvEmitter
2623var proto = Unipointer.prototype = Object.create( EvEmitter.prototype );
2624
2625proto.bindStartEvent = function( elem ) {
2626 this._bindStartEvent( elem, true );
2627};
2628
2629proto.unbindStartEvent = function( elem ) {
2630 this._bindStartEvent( elem, false );
2631};
2632
2633/**
2634 * Add or remove start event
2635 * @param {Boolean} isAdd - remove if falsey
2636 */
2637proto._bindStartEvent = function( elem, isAdd ) {
2638 // munge isAdd, default to true
2639 isAdd = isAdd === undefined ? true : isAdd;
2640 var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener';
2641
2642 // default to mouse events
2643 var startEvent = 'mousedown';
2644 if ( window.PointerEvent ) {
2645 // Pointer Events
2646 startEvent = 'pointerdown';
2647 } else if ( 'ontouchstart' in window ) {
2648 // Touch Events. iOS Safari
2649 startEvent = 'touchstart';
2650 }
2651 elem[ bindMethod ]( startEvent, this );
2652};
2653
2654// trigger handler methods for events
2655proto.handleEvent = function( event ) {
2656 var method = 'on' + event.type;
2657 if ( this[ method ] ) {
2658 this[ method ]( event );
2659 }
2660};
2661
2662// returns the touch that we're keeping track of
2663proto.getTouch = function( touches ) {
2664 for ( var i=0; i < touches.length; i++ ) {
2665 var touch = touches[i];
2666 if ( touch.identifier == this.pointerIdentifier ) {
2667 return touch;
2668 }
2669 }
2670};
2671
2672// ----- start event ----- //
2673
2674proto.onmousedown = function( event ) {
2675 // dismiss clicks from right or middle buttons
2676 var button = event.button;
2677 if ( button && ( button !== 0 && button !== 1 ) ) {
2678 return;
2679 }
2680 this._pointerDown( event, event );
2681};
2682
2683proto.ontouchstart = function( event ) {
2684 this._pointerDown( event, event.changedTouches[0] );
2685};
2686
2687proto.onpointerdown = function( event ) {
2688 this._pointerDown( event, event );
2689};
2690
2691/**
2692 * pointer start
2693 * @param {Event} event
2694 * @param {Event or Touch} pointer
2695 */
2696proto._pointerDown = function( event, pointer ) {
2697 // dismiss right click and other pointers
2698 // button = 0 is okay, 1-4 not
2699 if ( event.button || this.isPointerDown ) {
2700 return;
2701 }
2702
2703 this.isPointerDown = true;
2704 // save pointer identifier to match up touch events
2705 this.pointerIdentifier = pointer.pointerId !== undefined ?
2706 // pointerId for pointer events, touch.indentifier for touch events
2707 pointer.pointerId : pointer.identifier;
2708
2709 this.pointerDown( event, pointer );
2710};
2711
2712proto.pointerDown = function( event, pointer ) {
2713 this._bindPostStartEvents( event );
2714 this.emitEvent( 'pointerDown', [ event, pointer ] );
2715};
2716
2717// hash of events to be bound after start event
2718var postStartEvents = {
2719 mousedown: [ 'mousemove', 'mouseup' ],
2720 touchstart: [ 'touchmove', 'touchend', 'touchcancel' ],
2721 pointerdown: [ 'pointermove', 'pointerup', 'pointercancel' ],
2722};
2723
2724proto._bindPostStartEvents = function( event ) {
2725 if ( !event ) {
2726 return;
2727 }
2728 // get proper events to match start event
2729 var events = postStartEvents[ event.type ];
2730 // bind events to node
2731 events.forEach( function( eventName ) {
2732 window.addEventListener( eventName, this );
2733 }, this );
2734 // save these arguments
2735 this._boundPointerEvents = events;
2736};
2737
2738proto._unbindPostStartEvents = function() {
2739 // check for _boundEvents, in case dragEnd triggered twice (old IE8 bug)
2740 if ( !this._boundPointerEvents ) {
2741 return;
2742 }
2743 this._boundPointerEvents.forEach( function( eventName ) {
2744 window.removeEventListener( eventName, this );
2745 }, this );
2746
2747 delete this._boundPointerEvents;
2748};
2749
2750// ----- move event ----- //
2751
2752proto.onmousemove = function( event ) {
2753 this._pointerMove( event, event );
2754};
2755
2756proto.onpointermove = function( event ) {
2757 if ( event.pointerId == this.pointerIdentifier ) {
2758 this._pointerMove( event, event );
2759 }
2760};
2761
2762proto.ontouchmove = function( event ) {
2763 var touch = this.getTouch( event.changedTouches );
2764 if ( touch ) {
2765 this._pointerMove( event, touch );
2766 }
2767};
2768
2769/**
2770 * pointer move
2771 * @param {Event} event
2772 * @param {Event or Touch} pointer
2773 * @private
2774 */
2775proto._pointerMove = function( event, pointer ) {
2776 this.pointerMove( event, pointer );
2777};
2778
2779// public
2780proto.pointerMove = function( event, pointer ) {
2781 this.emitEvent( 'pointerMove', [ event, pointer ] );
2782};
2783
2784// ----- end event ----- //
2785
2786
2787proto.onmouseup = function( event ) {
2788 this._pointerUp( event, event );
2789};
2790
2791proto.onpointerup = function( event ) {
2792 if ( event.pointerId == this.pointerIdentifier ) {
2793 this._pointerUp( event, event );
2794 }
2795};
2796
2797proto.ontouchend = function( event ) {
2798 var touch = this.getTouch( event.changedTouches );
2799 if ( touch ) {
2800 this._pointerUp( event, touch );
2801 }
2802};
2803
2804/**
2805 * pointer up
2806 * @param {Event} event
2807 * @param {Event or Touch} pointer
2808 * @private
2809 */
2810proto._pointerUp = function( event, pointer ) {
2811 this._pointerDone();
2812 this.pointerUp( event, pointer );
2813};
2814
2815// public
2816proto.pointerUp = function( event, pointer ) {
2817 this.emitEvent( 'pointerUp', [ event, pointer ] );
2818};
2819
2820// ----- pointer done ----- //
2821
2822// triggered on pointer up & pointer cancel
2823proto._pointerDone = function() {
2824 this._pointerReset();
2825 this._unbindPostStartEvents();
2826 this.pointerDone();
2827};
2828
2829proto._pointerReset = function() {
2830 // reset properties
2831 this.isPointerDown = false;
2832 delete this.pointerIdentifier;
2833};
2834
2835proto.pointerDone = noop;
2836
2837// ----- pointer cancel ----- //
2838
2839proto.onpointercancel = function( event ) {
2840 if ( event.pointerId == this.pointerIdentifier ) {
2841 this._pointerCancel( event, event );
2842 }
2843};
2844
2845proto.ontouchcancel = function( event ) {
2846 var touch = this.getTouch( event.changedTouches );
2847 if ( touch ) {
2848 this._pointerCancel( event, touch );
2849 }
2850};
2851
2852/**
2853 * pointer cancel
2854 * @param {Event} event
2855 * @param {Event or Touch} pointer
2856 * @private
2857 */
2858proto._pointerCancel = function( event, pointer ) {
2859 this._pointerDone();
2860 this.pointerCancel( event, pointer );
2861};
2862
2863// public
2864proto.pointerCancel = function( event, pointer ) {
2865 this.emitEvent( 'pointerCancel', [ event, pointer ] );
2866};
2867
2868// ----- ----- //
2869
2870// utility function for getting x/y coords from event
2871Unipointer.getPointerPoint = function( pointer ) {
2872 return {
2873 x: pointer.pageX,
2874 y: pointer.pageY
2875 };
2876};
2877
2878// ----- ----- //
2879
2880return Unipointer;
2881
2882}));
2883
2884/*!
2885 * Unidragger v2.3.1
2886 * Draggable base class
2887 * MIT license
2888 */
2889
2890/*jshint browser: true, unused: true, undef: true, strict: true */
2891
2892( function( window, factory ) {
2893 // universal module definition
2894 /*jshint strict: false */ /*globals define, module, require */
2895
2896 if ( typeof define == 'function' && define.amd ) {
2897 // AMD
2898 define( 'unidragger/unidragger',[
2899 'unipointer/unipointer'
2900 ], function( Unipointer ) {
2901 return factory( window, Unipointer );
2902 });
2903 } else if ( typeof module == 'object' && module.exports ) {
2904 // CommonJS
2905 module.exports = factory(
2906 window,
2907 require('unipointer')
2908 );
2909 } else {
2910 // browser global
2911 window.Unidragger = factory(
2912 window,
2913 window.Unipointer
2914 );
2915 }
2916
2917}( window, function factory( window, Unipointer ) {
2918
2919
2920
2921// -------------------------- Unidragger -------------------------- //
2922
2923function Unidragger() {}
2924
2925// inherit Unipointer & EvEmitter
2926var proto = Unidragger.prototype = Object.create( Unipointer.prototype );
2927
2928// ----- bind start ----- //
2929
2930proto.bindHandles = function() {
2931 this._bindHandles( true );
2932};
2933
2934proto.unbindHandles = function() {
2935 this._bindHandles( false );
2936};
2937
2938/**
2939 * Add or remove start event
2940 * @param {Boolean} isAdd
2941 */
2942proto._bindHandles = function( isAdd ) {
2943 // munge isAdd, default to true
2944 isAdd = isAdd === undefined ? true : isAdd;
2945 // bind each handle
2946 var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener';
2947 var touchAction = isAdd ? this._touchActionValue : '';
2948 for ( var i=0; i < this.handles.length; i++ ) {
2949 var handle = this.handles[i];
2950 this._bindStartEvent( handle, isAdd );
2951 handle[ bindMethod ]( 'click', this );
2952 // touch-action: none to override browser touch gestures. metafizzy/flickity#540
2953 if ( window.PointerEvent ) {
2954 handle.style.touchAction = touchAction;
2955 }
2956 }
2957};
2958
2959// prototype so it can be overwriteable by Flickity
2960proto._touchActionValue = 'none';
2961
2962// ----- start event ----- //
2963
2964/**
2965 * pointer start
2966 * @param {Event} event
2967 * @param {Event or Touch} pointer
2968 */
2969proto.pointerDown = function( event, pointer ) {
2970 var isOkay = this.okayPointerDown( event );
2971 if ( !isOkay ) {
2972 return;
2973 }
2974 // track start event position
2975 // Safari 9 overrides pageX and pageY. These values needs to be copied. flickity#842
2976 this.pointerDownPointer = {
2977 pageX: pointer.pageX,
2978 pageY: pointer.pageY,
2979 };
2980
2981 event.preventDefault();
2982 this.pointerDownBlur();
2983 // bind move and end events
2984 this._bindPostStartEvents( event );
2985 this.emitEvent( 'pointerDown', [ event, pointer ] );
2986};
2987
2988// nodes that have text fields
2989var cursorNodes = {
2990 TEXTAREA: true,
2991 INPUT: true,
2992 SELECT: true,
2993 OPTION: true,
2994};
2995
2996// input types that do not have text fields
2997var clickTypes = {
2998 radio: true,
2999 checkbox: true,
3000 button: true,
3001 submit: true,
3002 image: true,
3003 file: true,
3004};
3005
3006// dismiss inputs with text fields. flickity#403, flickity#404
3007proto.okayPointerDown = function( event ) {
3008 var isCursorNode = cursorNodes[ event.target.nodeName ];
3009 var isClickType = clickTypes[ event.target.type ];
3010 var isOkay = !isCursorNode || isClickType;
3011 if ( !isOkay ) {
3012 this._pointerReset();
3013 }
3014 return isOkay;
3015};
3016
3017// kludge to blur previously focused input
3018proto.pointerDownBlur = function() {
3019 var focused = document.activeElement;
3020 // do not blur body for IE10, metafizzy/flickity#117
3021 var canBlur = focused && focused.blur && focused != document.body;
3022 if ( canBlur ) {
3023 focused.blur();
3024 }
3025};
3026
3027// ----- move event ----- //
3028
3029/**
3030 * drag move
3031 * @param {Event} event
3032 * @param {Event or Touch} pointer
3033 */
3034proto.pointerMove = function( event, pointer ) {
3035 var moveVector = this._dragPointerMove( event, pointer );
3036 this.emitEvent( 'pointerMove', [ event, pointer, moveVector ] );
3037 this._dragMove( event, pointer, moveVector );
3038};
3039
3040// base pointer move logic
3041proto._dragPointerMove = function( event, pointer ) {
3042 var moveVector = {
3043 x: pointer.pageX - this.pointerDownPointer.pageX,
3044 y: pointer.pageY - this.pointerDownPointer.pageY
3045 };
3046 // start drag if pointer has moved far enough to start drag
3047 if ( !this.isDragging && this.hasDragStarted( moveVector ) ) {
3048 this._dragStart( event, pointer );
3049 }
3050 return moveVector;
3051};
3052
3053// condition if pointer has moved far enough to start drag
3054proto.hasDragStarted = function( moveVector ) {
3055 return Math.abs( moveVector.x ) > 3 || Math.abs( moveVector.y ) > 3;
3056};
3057
3058// ----- end event ----- //
3059
3060/**
3061 * pointer up
3062 * @param {Event} event
3063 * @param {Event or Touch} pointer
3064 */
3065proto.pointerUp = function( event, pointer ) {
3066 this.emitEvent( 'pointerUp', [ event, pointer ] );
3067 this._dragPointerUp( event, pointer );
3068};
3069
3070proto._dragPointerUp = function( event, pointer ) {
3071 if ( this.isDragging ) {
3072 this._dragEnd( event, pointer );
3073 } else {
3074 // pointer didn't move enough for drag to start
3075 this._staticClick( event, pointer );
3076 }
3077};
3078
3079// -------------------------- drag -------------------------- //
3080
3081// dragStart
3082proto._dragStart = function( event, pointer ) {
3083 this.isDragging = true;
3084 // prevent clicks
3085 this.isPreventingClicks = true;
3086 this.dragStart( event, pointer );
3087};
3088
3089proto.dragStart = function( event, pointer ) {
3090 this.emitEvent( 'dragStart', [ event, pointer ] );
3091};
3092
3093// dragMove
3094proto._dragMove = function( event, pointer, moveVector ) {
3095 // do not drag if not dragging yet
3096 if ( !this.isDragging ) {
3097 return;
3098 }
3099
3100 this.dragMove( event, pointer, moveVector );
3101};
3102
3103proto.dragMove = function( event, pointer, moveVector ) {
3104 event.preventDefault();
3105 this.emitEvent( 'dragMove', [ event, pointer, moveVector ] );
3106};
3107
3108// dragEnd
3109proto._dragEnd = function( event, pointer ) {
3110 // set flags
3111 this.isDragging = false;
3112 // re-enable clicking async
3113 setTimeout( function() {
3114 delete this.isPreventingClicks;
3115 }.bind( this ) );
3116
3117 this.dragEnd( event, pointer );
3118};
3119
3120proto.dragEnd = function( event, pointer ) {
3121 this.emitEvent( 'dragEnd', [ event, pointer ] );
3122};
3123
3124// ----- onclick ----- //
3125
3126// handle all clicks and prevent clicks when dragging
3127proto.onclick = function( event ) {
3128 if ( this.isPreventingClicks ) {
3129 event.preventDefault();
3130 }
3131};
3132
3133// ----- staticClick ----- //
3134
3135// triggered after pointer down & up with no/tiny movement
3136proto._staticClick = function( event, pointer ) {
3137 // ignore emulated mouse up clicks
3138 if ( this.isIgnoringMouseUp && event.type == 'mouseup' ) {
3139 return;
3140 }
3141
3142 this.staticClick( event, pointer );
3143
3144 // set flag for emulated clicks 300ms after touchend
3145 if ( event.type != 'mouseup' ) {
3146 this.isIgnoringMouseUp = true;
3147 // reset flag after 300ms
3148 setTimeout( function() {
3149 delete this.isIgnoringMouseUp;
3150 }.bind( this ), 400 );
3151 }
3152};
3153
3154proto.staticClick = function( event, pointer ) {
3155 this.emitEvent( 'staticClick', [ event, pointer ] );
3156};
3157
3158// ----- utils ----- //
3159
3160Unidragger.getPointerPoint = Unipointer.getPointerPoint;
3161
3162// ----- ----- //
3163
3164return Unidragger;
3165
3166}));
3167
3168// drag
3169( function( window, factory ) {
3170 // universal module definition
3171 if ( typeof define == 'function' && define.amd ) {
3172 // AMD
3173 define( 'flickity/js/drag',[
3174 './flickity',
3175 'unidragger/unidragger',
3176 'fizzy-ui-utils/utils',
3177 ], function( Flickity, Unidragger, utils ) {
3178 return factory( window, Flickity, Unidragger, utils );
3179 } );
3180 } else if ( typeof module == 'object' && module.exports ) {
3181 // CommonJS
3182 module.exports = factory(
3183 window,
3184 require('./flickity'),
3185 require('unidragger'),
3186 require('fizzy-ui-utils')
3187 );
3188 } else {
3189 // browser global
3190 window.Flickity = factory(
3191 window,
3192 window.Flickity,
3193 window.Unidragger,
3194 window.fizzyUIUtils
3195 );
3196 }
3197
3198}( window, function factory( window, Flickity, Unidragger, utils ) {
3199
3200
3201
3202// ----- defaults ----- //
3203
3204utils.extend( Flickity.defaults, {
3205 draggable: '>1',
3206 dragThreshold: 3,
3207} );
3208
3209// ----- create ----- //
3210
3211Flickity.createMethods.push('_createDrag');
3212
3213// -------------------------- drag prototype -------------------------- //
3214
3215var proto = Flickity.prototype;
3216utils.extend( proto, Unidragger.prototype );
3217proto._touchActionValue = 'pan-y';
3218
3219// -------------------------- -------------------------- //
3220
3221var isTouch = 'createTouch' in document;
3222var isTouchmoveScrollCanceled = false;
3223
3224proto._createDrag = function() {
3225 this.on( 'activate', this.onActivateDrag );
3226 this.on( 'uiChange', this._uiChangeDrag );
3227 this.on( 'deactivate', this.onDeactivateDrag );
3228 this.on( 'cellChange', this.updateDraggable );
3229 // TODO updateDraggable on resize? if groupCells & slides change
3230 // HACK - add seemingly innocuous handler to fix iOS 10 scroll behavior
3231 // #457, RubaXa/Sortable#973
3232 if ( isTouch && !isTouchmoveScrollCanceled ) {
3233 window.addEventListener( 'touchmove', function() {} );
3234 isTouchmoveScrollCanceled = true;
3235 }
3236};
3237
3238proto.onActivateDrag = function() {
3239 this.handles = [ this.viewport ];
3240 this.bindHandles();
3241 this.updateDraggable();
3242};
3243
3244proto.onDeactivateDrag = function() {
3245 this.unbindHandles();
3246 this.element.classList.remove('is-draggable');
3247};
3248
3249proto.updateDraggable = function() {
3250 // disable dragging if less than 2 slides. #278
3251 if ( this.options.draggable == '>1' ) {
3252 this.isDraggable = this.slides.length > 1;
3253 } else {
3254 this.isDraggable = this.options.draggable;
3255 }
3256 if ( this.isDraggable ) {
3257 this.element.classList.add('is-draggable');
3258 } else {
3259 this.element.classList.remove('is-draggable');
3260 }
3261};
3262
3263// backwards compatibility
3264proto.bindDrag = function() {
3265 this.options.draggable = true;
3266 this.updateDraggable();
3267};
3268
3269proto.unbindDrag = function() {
3270 this.options.draggable = false;
3271 this.updateDraggable();
3272};
3273
3274proto._uiChangeDrag = function() {
3275 delete this.isFreeScrolling;
3276};
3277
3278// -------------------------- pointer events -------------------------- //
3279
3280proto.pointerDown = function( event, pointer ) {
3281 if ( !this.isDraggable ) {
3282 this._pointerDownDefault( event, pointer );
3283 return;
3284 }
3285 var isOkay = this.okayPointerDown( event );
3286 if ( !isOkay ) {
3287 return;
3288 }
3289
3290 this._pointerDownPreventDefault( event );
3291 this.pointerDownFocus( event );
3292 // blur
3293 if ( document.activeElement != this.element ) {
3294 // do not blur if already focused
3295 this.pointerDownBlur();
3296 }
3297
3298 // stop if it was moving
3299 this.dragX = this.x;
3300 this.viewport.classList.add('is-pointer-down');
3301 // track scrolling
3302 this.pointerDownScroll = getScrollPosition();
3303 window.addEventListener( 'scroll', this );
3304
3305 this._pointerDownDefault( event, pointer );
3306};
3307
3308// default pointerDown logic, used for staticClick
3309proto._pointerDownDefault = function( event, pointer ) {
3310 // track start event position
3311 // Safari 9 overrides pageX and pageY. These values needs to be copied. #779
3312 this.pointerDownPointer = {
3313 pageX: pointer.pageX,
3314 pageY: pointer.pageY,
3315 };
3316 // bind move and end events
3317 this._bindPostStartEvents( event );
3318 this.dispatchEvent( 'pointerDown', event, [ pointer ] );
3319};
3320
3321var focusNodes = {
3322 INPUT: true,
3323 TEXTAREA: true,
3324 SELECT: true,
3325};
3326
3327proto.pointerDownFocus = function( event ) {
3328 var isFocusNode = focusNodes[ event.target.nodeName ];
3329 if ( !isFocusNode ) {
3330 this.focus();
3331 }
3332};
3333
3334proto._pointerDownPreventDefault = function( event ) {
3335 var isTouchStart = event.type == 'touchstart';
3336 var isTouchPointer = event.pointerType == 'touch';
3337 var isFocusNode = focusNodes[ event.target.nodeName ];
3338 if ( !isTouchStart && !isTouchPointer && !isFocusNode ) {
3339 event.preventDefault();
3340 }
3341};
3342
3343// ----- move ----- //
3344
3345proto.hasDragStarted = function( moveVector ) {
3346 return Math.abs( moveVector.x ) > this.options.dragThreshold;
3347};
3348
3349// ----- up ----- //
3350
3351proto.pointerUp = function( event, pointer ) {
3352 delete this.isTouchScrolling;
3353 this.viewport.classList.remove('is-pointer-down');
3354 this.dispatchEvent( 'pointerUp', event, [ pointer ] );
3355 this._dragPointerUp( event, pointer );
3356};
3357
3358proto.pointerDone = function() {
3359 window.removeEventListener( 'scroll', this );
3360 delete this.pointerDownScroll;
3361};
3362
3363// -------------------------- dragging -------------------------- //
3364
3365proto.dragStart = function( event, pointer ) {
3366 if ( !this.isDraggable ) {
3367 return;
3368 }
3369 this.dragStartPosition = this.x;
3370 this.startAnimation();
3371 window.removeEventListener( 'scroll', this );
3372 this.dispatchEvent( 'dragStart', event, [ pointer ] );
3373};
3374
3375proto.pointerMove = function( event, pointer ) {
3376 var moveVector = this._dragPointerMove( event, pointer );
3377 this.dispatchEvent( 'pointerMove', event, [ pointer, moveVector ] );
3378 this._dragMove( event, pointer, moveVector );
3379};
3380
3381proto.dragMove = function( event, pointer, moveVector ) {
3382 if ( !this.isDraggable ) {
3383 return;
3384 }
3385 event.preventDefault();
3386
3387 this.previousDragX = this.dragX;
3388 // reverse if right-to-left
3389 var direction = this.options.rightToLeft ? -1 : 1;
3390 if ( this.options.wrapAround ) {
3391 // wrap around move. #589
3392 moveVector.x %= this.slideableWidth;
3393 }
3394 var dragX = this.dragStartPosition + moveVector.x * direction;
3395
3396 if ( !this.options.wrapAround && this.slides.length ) {
3397 // slow drag
3398 var originBound = Math.max( -this.slides[0].target, this.dragStartPosition );
3399 dragX = dragX > originBound ? ( dragX + originBound ) * 0.5 : dragX;
3400 var endBound = Math.min( -this.getLastSlide().target, this.dragStartPosition );
3401 dragX = dragX < endBound ? ( dragX + endBound ) * 0.5 : dragX;
3402 }
3403
3404 this.dragX = dragX;
3405
3406 this.dragMoveTime = new Date();
3407 this.dispatchEvent( 'dragMove', event, [ pointer, moveVector ] );
3408};
3409
3410proto.dragEnd = function( event, pointer ) {
3411 if ( !this.isDraggable ) {
3412 return;
3413 }
3414 if ( this.options.freeScroll ) {
3415 this.isFreeScrolling = true;
3416 }
3417 // set selectedIndex based on where flick will end up
3418 var index = this.dragEndRestingSelect();
3419
3420 if ( this.options.freeScroll && !this.options.wrapAround ) {
3421 // if free-scroll & not wrap around
3422 // do not free-scroll if going outside of bounding slides
3423 // so bounding slides can attract slider, and keep it in bounds
3424 var restingX = this.getRestingPosition();
3425 this.isFreeScrolling = -restingX > this.slides[0].target &&
3426 -restingX < this.getLastSlide().target;
3427 } else if ( !this.options.freeScroll && index == this.selectedIndex ) {
3428 // boost selection if selected index has not changed
3429 index += this.dragEndBoostSelect();
3430 }
3431 delete this.previousDragX;
3432 // apply selection
3433 // TODO refactor this, selecting here feels weird
3434 // HACK, set flag so dragging stays in correct direction
3435 this.isDragSelect = this.options.wrapAround;
3436 this.select( index );
3437 delete this.isDragSelect;
3438 this.dispatchEvent( 'dragEnd', event, [ pointer ] );
3439};
3440
3441proto.dragEndRestingSelect = function() {
3442 var restingX = this.getRestingPosition();
3443 // how far away from selected slide
3444 var distance = Math.abs( this.getSlideDistance( -restingX, this.selectedIndex ) );
3445 // get closet resting going up and going down
3446 var positiveResting = this._getClosestResting( restingX, distance, 1 );
3447 var negativeResting = this._getClosestResting( restingX, distance, -1 );
3448 // use closer resting for wrap-around
3449 var index = positiveResting.distance < negativeResting.distance ?
3450 positiveResting.index : negativeResting.index;
3451 return index;
3452};
3453
3454/**
3455 * given resting X and distance to selected cell
3456 * get the distance and index of the closest cell
3457 * @param {Number} restingX - estimated post-flick resting position
3458 * @param {Number} distance - distance to selected cell
3459 * @param {Integer} increment - +1 or -1, going up or down
3460 * @returns {Object} - { distance: {Number}, index: {Integer} }
3461 */
3462proto._getClosestResting = function( restingX, distance, increment ) {
3463 var index = this.selectedIndex;
3464 var minDistance = Infinity;
3465 var condition = this.options.contain && !this.options.wrapAround ?
3466 // if contain, keep going if distance is equal to minDistance
3467 function( dist, minDist ) {
3468 return dist <= minDist;
3469 } : function( dist, minDist ) {
3470 return dist < minDist;
3471 };
3472 while ( condition( distance, minDistance ) ) {
3473 // measure distance to next cell
3474 index += increment;
3475 minDistance = distance;
3476 distance = this.getSlideDistance( -restingX, index );
3477 if ( distance === null ) {
3478 break;
3479 }
3480 distance = Math.abs( distance );
3481 }
3482 return {
3483 distance: minDistance,
3484 // selected was previous index
3485 index: index - increment,
3486 };
3487};
3488
3489/**
3490 * measure distance between x and a slide target
3491 * @param {Number} x - horizontal position
3492 * @param {Integer} index - slide index
3493 * @returns {Number} - slide distance
3494 */
3495proto.getSlideDistance = function( x, index ) {
3496 var len = this.slides.length;
3497 // wrap around if at least 2 slides
3498 var isWrapAround = this.options.wrapAround && len > 1;
3499 var slideIndex = isWrapAround ? utils.modulo( index, len ) : index;
3500 var slide = this.slides[ slideIndex ];
3501 if ( !slide ) {
3502 return null;
3503 }
3504 // add distance for wrap-around slides
3505 var wrap = isWrapAround ? this.slideableWidth * Math.floor( index/len ) : 0;
3506 return x - ( slide.target + wrap );
3507};
3508
3509proto.dragEndBoostSelect = function() {
3510 // do not boost if no previousDragX or dragMoveTime
3511 if ( this.previousDragX === undefined || !this.dragMoveTime ||
3512 // or if drag was held for 100 ms
3513 new Date() - this.dragMoveTime > 100 ) {
3514 return 0;
3515 }
3516
3517 var distance = this.getSlideDistance( -this.dragX, this.selectedIndex );
3518 var delta = this.previousDragX - this.dragX;
3519 if ( distance > 0 && delta > 0 ) {
3520 // boost to next if moving towards the right, and positive velocity
3521 return 1;
3522 } else if ( distance < 0 && delta < 0 ) {
3523 // boost to previous if moving towards the left, and negative velocity
3524 return -1;
3525 }
3526 return 0;
3527};
3528
3529// ----- staticClick ----- //
3530
3531proto.staticClick = function( event, pointer ) {
3532 // get clickedCell, if cell was clicked
3533 var clickedCell = this.getParentCell( event.target );
3534 var cellElem = clickedCell && clickedCell.element;
3535 var cellIndex = clickedCell && this.cells.indexOf( clickedCell );
3536 this.dispatchEvent( 'staticClick', event, [ pointer, cellElem, cellIndex ] );
3537};
3538
3539// ----- scroll ----- //
3540
3541proto.onscroll = function() {
3542 var scroll = getScrollPosition();
3543 var scrollMoveX = this.pointerDownScroll.x - scroll.x;
3544 var scrollMoveY = this.pointerDownScroll.y - scroll.y;
3545 // cancel click/tap if scroll is too much
3546 if ( Math.abs( scrollMoveX ) > 3 || Math.abs( scrollMoveY ) > 3 ) {
3547 this._pointerDone();
3548 }
3549};
3550
3551// ----- utils ----- //
3552
3553function getScrollPosition() {
3554 return {
3555 x: window.pageXOffset,
3556 y: window.pageYOffset,
3557 };
3558}
3559
3560// ----- ----- //
3561
3562return Flickity;
3563
3564} ) );
3565
3566// prev/next buttons
3567( function( window, factory ) {
3568 // universal module definition
3569 if ( typeof define == 'function' && define.amd ) {
3570 // AMD
3571 define( 'flickity/js/prev-next-button',[
3572 './flickity',
3573 'unipointer/unipointer',
3574 'fizzy-ui-utils/utils',
3575 ], function( Flickity, Unipointer, utils ) {
3576 return factory( window, Flickity, Unipointer, utils );
3577 } );
3578 } else if ( typeof module == 'object' && module.exports ) {
3579 // CommonJS
3580 module.exports = factory(
3581 window,
3582 require('./flickity'),
3583 require('unipointer'),
3584 require('fizzy-ui-utils')
3585 );
3586 } else {
3587 // browser global
3588 factory(
3589 window,
3590 window.Flickity,
3591 window.Unipointer,
3592 window.fizzyUIUtils
3593 );
3594 }
3595
3596}( window, function factory( window, Flickity, Unipointer, utils ) {
3597'use strict';
3598
3599var svgURI = 'http://www.w3.org/2000/svg';
3600
3601// -------------------------- PrevNextButton -------------------------- //
3602
3603function PrevNextButton( direction, parent ) {
3604 this.direction = direction;
3605 this.parent = parent;
3606 this._create();
3607}
3608
3609PrevNextButton.prototype = Object.create( Unipointer.prototype );
3610
3611PrevNextButton.prototype._create = function() {
3612 // properties
3613 this.isEnabled = true;
3614 this.isPrevious = this.direction == -1;
3615 var leftDirection = this.parent.options.rightToLeft ? 1 : -1;
3616 this.isLeft = this.direction == leftDirection;
3617
3618 var element = this.element = document.createElement('button');
3619 element.className = 'flickity-button flickity-prev-next-button';
3620 element.className += this.isPrevious ? ' previous' : ' next';
3621 // prevent button from submitting form http://stackoverflow.com/a/10836076/182183
3622 element.setAttribute( 'type', 'button' );
3623 // init as disabled
3624 this.disable();
3625
3626 element.setAttribute( 'aria-label', this.isPrevious ? 'Previous' : 'Next' );
3627
3628 // create arrow
3629 var svg = this.createSVG();
3630 element.appendChild( svg );
3631 // events
3632 this.parent.on( 'select', this.update.bind( this ) );
3633 this.on( 'pointerDown', this.parent.childUIPointerDown.bind( this.parent ) );
3634};
3635
3636PrevNextButton.prototype.activate = function() {
3637 this.bindStartEvent( this.element );
3638 this.element.addEventListener( 'click', this );
3639 // add to DOM
3640 this.parent.element.appendChild( this.element );
3641};
3642
3643PrevNextButton.prototype.deactivate = function() {
3644 // remove from DOM
3645 this.parent.element.removeChild( this.element );
3646 // click events
3647 this.unbindStartEvent( this.element );
3648 this.element.removeEventListener( 'click', this );
3649};
3650
3651PrevNextButton.prototype.createSVG = function() {
3652 var svg = document.createElementNS( svgURI, 'svg' );
3653 svg.setAttribute( 'class', 'flickity-button-icon' );
3654 svg.setAttribute( 'viewBox', '0 0 100 100' );
3655 var path = document.createElementNS( svgURI, 'path' );
3656 var pathMovements = getArrowMovements( this.parent.options.arrowShape );
3657 path.setAttribute( 'd', pathMovements );
3658 path.setAttribute( 'class', 'arrow' );
3659 // rotate arrow
3660 if ( !this.isLeft ) {
3661 path.setAttribute( 'transform', 'translate(100, 100) rotate(180) ' );
3662 }
3663 svg.appendChild( path );
3664 return svg;
3665};
3666
3667// get SVG path movmement
3668function getArrowMovements( shape ) {
3669 // use shape as movement if string
3670 if ( typeof shape == 'string' ) {
3671 return shape;
3672 }
3673 // create movement string
3674 return 'M ' + shape.x0 + ',50' +
3675 ' L ' + shape.x1 + ',' + ( shape.y1 + 50 ) +
3676 ' L ' + shape.x2 + ',' + ( shape.y2 + 50 ) +
3677 ' L ' + shape.x3 + ',50 ' +
3678 ' L ' + shape.x2 + ',' + ( 50 - shape.y2 ) +
3679 ' L ' + shape.x1 + ',' + ( 50 - shape.y1 ) +
3680 ' Z';
3681}
3682
3683PrevNextButton.prototype.handleEvent = utils.handleEvent;
3684
3685PrevNextButton.prototype.onclick = function() {
3686 if ( !this.isEnabled ) {
3687 return;
3688 }
3689 this.parent.uiChange();
3690 var method = this.isPrevious ? 'previous' : 'next';
3691 this.parent[ method ]();
3692};
3693
3694// ----- ----- //
3695
3696PrevNextButton.prototype.enable = function() {
3697 if ( this.isEnabled ) {
3698 return;
3699 }
3700 this.element.disabled = false;
3701 this.isEnabled = true;
3702};
3703
3704PrevNextButton.prototype.disable = function() {
3705 if ( !this.isEnabled ) {
3706 return;
3707 }
3708 this.element.disabled = true;
3709 this.isEnabled = false;
3710};
3711
3712PrevNextButton.prototype.update = function() {
3713 // index of first or last slide, if previous or next
3714 var slides = this.parent.slides;
3715 // enable is wrapAround and at least 2 slides
3716 if ( this.parent.options.wrapAround && slides.length > 1 ) {
3717 this.enable();
3718 return;
3719 }
3720 var lastIndex = slides.length ? slides.length - 1 : 0;
3721 var boundIndex = this.isPrevious ? 0 : lastIndex;
3722 var method = this.parent.selectedIndex == boundIndex ? 'disable' : 'enable';
3723 this[ method ]();
3724};
3725
3726PrevNextButton.prototype.destroy = function() {
3727 this.deactivate();
3728 this.allOff();
3729};
3730
3731// -------------------------- Flickity prototype -------------------------- //
3732
3733utils.extend( Flickity.defaults, {
3734 prevNextButtons: true,
3735 arrowShape: {
3736 x0: 10,
3737 x1: 60, y1: 50,
3738 x2: 70, y2: 40,
3739 x3: 30,
3740 },
3741} );
3742
3743Flickity.createMethods.push('_createPrevNextButtons');
3744var proto = Flickity.prototype;
3745
3746proto._createPrevNextButtons = function() {
3747 if ( !this.options.prevNextButtons ) {
3748 return;
3749 }
3750
3751 this.prevButton = new PrevNextButton( -1, this );
3752 this.nextButton = new PrevNextButton( 1, this );
3753
3754 this.on( 'activate', this.activatePrevNextButtons );
3755};
3756
3757proto.activatePrevNextButtons = function() {
3758 this.prevButton.activate();
3759 this.nextButton.activate();
3760 this.on( 'deactivate', this.deactivatePrevNextButtons );
3761};
3762
3763proto.deactivatePrevNextButtons = function() {
3764 this.prevButton.deactivate();
3765 this.nextButton.deactivate();
3766 this.off( 'deactivate
The following has evaluated to null or missing: ==> urlimagen [in template "20102#20129#12795723" at line 58, column 26] ---- Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)?? ---- ---- FTL stack trace ("~" means nesting-related): - Failed at: #if urlimagen.url?has_content [in template "20102#20129#12795723" at line 58, column 21] ----
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 urlimagenNode = docXml.valueOf("//dynamic-element[@name='IMAGEN']/dynamic-content")/>
24
25
26
27 <#assign titulo = docXml.valueOf("//dynamic-element[@name='Titulopub']/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 <#if urlimagenNode?has_content>
51 <#assign urlimagenString = urlimagenNode?string />
52 <#assign urlimagen = urlimagenString?eval />
53 </#if>
54
55 <div class="gallery-cell${nameInstancePublisher}">
56 <div class="card-info${nameInstancePublisher}" id="card-info">
57 <div class="card-img${nameInstancePublisher}" id="card-img">
58 <#if urlimagen.url?has_content>
59 <img alt="${urlimagen.alt}" src='${urlimagen.url}' title="${titulo}">
60 <#else>
61 <img alt='${urlimagen["alt"]}' src='/documents/${urlimagen["groupId"]}/${urlimagen["classPK"]}/${urlimagen["name"]}/${urlimagen["uuid"]}' title="${titulo}">
62 </#if>
63 <#if assetRenderer.hasEditPermission(themeDisplay.getPermissionChecker())>
64 <#assign editPortletURL = assetRenderer.getURLEdit(renderRequest, renderResponse, windowStateFactory.getWindowState("NORMAL"), themeDisplay.getURLCurrent())!"" />
65 <#if validator.isNotNull(editPortletURL)>
66 <a class="editOptionmas" href="${editPortletURL.toString()}">Editar ✐</a>
67 </#if>
68 </#if>
69 </div>
70 <div class="card-txt${nameInstancePublisher}" id="card-txt">
71 <div class="card-title${nameInstancePublisher}" id="card-title">${titulo}</div>
72 <div class="card-lead${nameInstancePublisher}" id="card-lead">${contenido}</div>
73 <div class="card-btn${nameInstancePublisher}" id="card-btn">
74
75 <a class="link${nameInstancePublisher}" href="${linkext}" target="_blank">
76 Ver más
77 </a>
78
79 </div>
80 </div>
81
82 </div>
83 </div>
84 </#list>
85</div>
86
87
88
89
90
91<!---------------------- ESTLOS BASICOS ------------------------>
92
93 <style>
94
95 .cards-cont${nameInstancePublisher} {
96 width: 100%;
97 display: flex;
98 justify-content: center;
99 flex-wrap: wrap;
100
101 }
102
103
104 .card-info${nameInstancePublisher} {
105
106 width: 250px;
107 height: 350px;
108 margin: 15px;
109 display:flex;
110 align-content:center;
111 align-items:center;
112 flex-wrap:wrap;
113 }
114
115 .card-img${nameInstancePublisher} {
116 width: 250px;
117 height: 350px;
118 border-radius: 10px;
119 -webkit-box-shadow: 0px 2px 17px 1px rgb(0 0 0 / 23%);
120 box-shadow: 0px 2px 17px 1px rgb(0 0 0 / 23%);
121 box-sizing: border-box;
122 background-position: center;
123 background-size: cover;
124 -webkit-transition: all 500ms ease-in-out; // IE 9
125 -moz-transition: all 500ms ease-in-out; // Firefox
126 -ms-transition: all 500ms ease-in-out; // Safari and Chrome
127 -o-transition: all 500ms ease-in-out; // Opera
128 transition: all 500ms ease-in-out;
129 position:relative;
130 display:flex;
131 }
132
133 .card-info${nameInstancePublisher}:hover .card-img${nameInstancePublisher} {
134 width: 250px;
135 height: 350px;
136 border-radius: 10px;
137 box-sizing: border-box;
138 background-position: center;
139 background-size: cover;
140 -moz-transform: scale(1.02);
141 -webkit-transform: scale(1.02);
142 -o-transform: scale(1.02);
143 -ms-transform: scale(1.02);
144 transform: scale(1.02);
145 }
146
147 .card-img${nameInstancePublisher} img{
148 vertical-align: middle;
149 border-style: none;
150 width: 100%;
151 height: 350px;
152 object-fit: cover;
153 object-position: center;
154 border-radius: 10px;
155 }
156
157 .card-txt${nameInstancePublisher} {
158 position: absolute;
159 display:flex;
160 padding: 20px;
161 color: #FFF;
162 opacity: 1;
163 transition: all 500ms ease-in-out;
164 width: 90%;
165 flex-wrap:wrap;
166 max-width: 250px;
167 }
168 #card-txt{
169 display:none;
170 opacity:0;
171 transition: all 1s;
172 -webkit-transition: all 1s;
173 }
174
175 #card-info:hover #card-txt{
176 display:flex;
177 opacity:1;
178 transition: all 1s;
179 -webkit-transition: all 1s;
180 }
181
182 #card-info:hover #card-img{filter: brightness(0.3);}
183
184 .card-title${nameInstancePublisher} {
185 font-family: HelveticaBold;
186 font-size: 1rem;
187 transition: all 500ms ease-in-out;
188 width:100%;
189 }
190
191 .card-lead${nameInstancePublisher} {
192 font-family: HelveticaLight;
193 font-size: 0.9rem;
194 transition: all 500ms ease-in-out;
195 width: 100%;
196 }
197
198 .card-btn${nameInstancePublisher} {
199 padding: 5px;
200 font-family: HelveticaLight;
201 border: 1px solid #fff;
202 width: auto;
203 font-size: 0.8rem;
204 margin-top: 10px;
205 display: flex;
206 border-radius: 10px;
207 text-align: center;
208 /* margin: 0 auto; */
209 /* align-content: center; */
210 justify-content: center;
211 cursor: pointer;
212 opacity: 1;
213 }
214
215 .card-info${nameInstancePublisher}:hover .card-btn${nameInstancePublisher}{
216 background-color:#FFF;
217 color:#494949;
218 width: auto;
219 padding: 5px 10px;
220 border-radius: 5px;
221 }
222
223 .card-btn${nameInstancePublisher} a{
224 color:#FFF;
225 text-decoration:none;
226 }
227
228 .card-info${nameInstancePublisher}:hover .card-btn${nameInstancePublisher} a{
229 color:#494949;
230
231 }
232
233
234 .editOptionmas{
235 position: absolute;
236 background-color: #173268;
237 padding: 2px 5px;
238 color: #FFF;
239 font-family:HelveticaLight;
240 display:flex;
241 flex-wrap:nowrap;
242 width: 85px;
243 top:0px;
244 justify-content: center;
245 border-radius:10px 0px 10px 0px;
246 }
247
248 .editOptionmas:hover{
249 text-decoration:none;
250 color:#FFF;
251 font-family:HelveticaBold;
252 }
253
254 </style>
255
256 <style>
257 .gallery${nameInstancePublisher} {
258
259 margin: 0 auto;
260 width: 97%;
261 max-width:1200px;
262 margin-bottom:30px;
263 height:420px;
264 }
265
266 .gallery-cell${nameInstancePublisher} {
267 width: 22%;
268 height: auto;
269 margin-right: 10px;
270 counter-increment: gallery-cell;
271 }
272 /* cell number */
273
274 .gallery-cell${nameInstancePublisher}:before {
275 display: block;
276 text-align: center;
277 /content: counter(gallery-cell);/
278 line-height: 200px;
279 font-size: 80px;
280 color: white;
281 }
282
283 @media screen and (max-width: 640px) {
284 .gallery-cell${nameInstancePublisher} {
285 width: 100%;
286 height: auto;
287 margin-right: 10px;
288 counter-increment: gallery-cell;
289 }
290 }
291
292
293 .flickity-page-dots .dot.is-selected {
294 opacity: 1;
295 background: #2c5697;
296 }
297
298 .flickity-page-dots .dot {
299 display: inline-block;
300 width: 8px!important;
301 height: 8px!important;
302 margin: 0 5px!important;
303 background: #494949;
304 border-radius: 50%;
305 opacity: 0.25;
306 cursor: pointer;
307 }
308
309 .flickity-page-dots {
310 position: absolute;
311 width: 100%;
312 bottom: 0px!important;
313 padding: 0;
314 margin: 0;
315 list-style: none;
316 text-align: center;
317 line-height: 1;
318 margin-bottom: 0px;
319 }
320
321 .flickity-prev-next-button.previous {
322 left: -50px!important;
323 }
324
325 .flickity-prev-next-button.next {
326 right: -50px!important;
327 }
328
329 @media screen and (max-width: 640px) {
330 .flickity-prev-next-button.previous {
331 left: 10px!important;
332 }
333
334 .flickity-prev-next-button.next {
335 right: 10px!important;
336 }
337
338 .flickity-prev-next-button {
339 top: 25%!important;
340 width: 44px;
341 height: 44px;
342 border-radius: 50%;
343 transform: translateY(-50%);
344 }
345}
346
347
348 </style>
349
350<!------------------------------------------------------- ESTILOS DEL SCRIPT ---------------------------------------------->
351
352
353<style>
354
355.flickity-enabled {
356 position: relative;
357}
358
359.flickity-enabled:focus { outline: none; }
360
361.flickity-viewport {
362 overflow: hidden;
363 position: relative;
364 height: 100%;
365}
366
367.flickity-slider {
368 position: absolute;
369 width: 100%;
370 height: 100%;
371}
372
373/* draggable */
374
375.flickity-enabled.is-draggable {
376 -webkit-tap-highlight-color: transparent;
377 -webkit-user-select: none;
378 -moz-user-select: none;
379 -ms-user-select: none;
380 user-select: none;
381}
382
383.flickity-enabled.is-draggable .flickity-viewport {
384 cursor: move;
385 cursor: -webkit-grab;
386 cursor: grab;
387}
388
389.flickity-enabled.is-draggable .flickity-viewport.is-pointer-down {
390 cursor: -webkit-grabbing;
391 cursor: grabbing;
392}
393
394/* ---- flickity-button ---- */
395
396.flickity-button {
397 position: absolute;
398 background: hsla(0, 0%, 100%, 0.75);
399 border: none;
400 color: #333;
401}
402
403.flickity-button:hover {
404 background: white;
405 cursor: pointer;
406}
407
408.flickity-button:focus {
409 outline: none;
410 box-shadow: 0 0 0 5px #19F;
411}
412
413.flickity-button:active {
414 opacity: 0.6;
415}
416
417.flickity-button:disabled {
418 opacity: 0.3;
419 cursor: auto;
420 /* prevent disabled button from capturing pointer up event. #716 */
421 pointer-events: none;
422}
423
424.flickity-button-icon {
425 fill: currentColor;
426}
427
428/* ---- previous/next buttons ---- */
429
430.flickity-prev-next-button {
431 top: 50%;
432 width: 44px;
433 height: 44px;
434 border-radius: 50%;
435 /* vertically center */
436 transform: translateY(-50%);
437}
438
439.flickity-prev-next-button.previous { left: 10px; }
440.flickity-prev-next-button.next { right: 10px; }
441/* right to left */
442.flickity-rtl .flickity-prev-next-button.previous {
443 left: auto;
444 right: 10px;
445}
446.flickity-rtl .flickity-prev-next-button.next {
447 right: auto;
448 left: 10px;
449}
450
451.flickity-prev-next-button .flickity-button-icon {
452 position: absolute;
453 left: 20%;
454 top: 20%;
455 width: 60%;
456 height: 60%;
457}
458
459/* ---- page dots ---- */
460
461.flickity-page-dots {
462 position: absolute;
463 width: 100%;
464 bottom: -25px;
465 padding: 0;
466 margin: 0;
467 list-style: none;
468 text-align: center;
469 line-height: 1;
470}
471
472.flickity-rtl .flickity-page-dots { direction: rtl; }
473
474.flickity-page-dots .dot {
475 display: inline-block;
476 width: 10px;
477 height: 10px;
478 margin: 0 8px;
479 background: #333;
480 border-radius: 50%;
481 opacity: 0.25;
482 cursor: pointer;
483}
484
485.flickity-page-dots .dot.is-selected {
486 opacity: 1;
487}
488 </style>
489
490
491<!------------------------------------------------------------- SCRIPT PRINCIPAL -------------------------------------------------------------------------->
492
493<script>
494 /*!
495 * Flickity PACKAGED v2.2.2
496 * Touch, responsive, flickable carousels
497 *
498 * Licensed GPLv3 for open source use
499 * or Flickity Commercial License for commercial use
500 *
501 * https://flickity.metafizzy.co
502 * Copyright 2015-2021 Metafizzy
503 */
504
505/**
506 * Bridget makes jQuery widgets
507 * v2.0.1
508 * MIT license
509 */
510
511/* jshint browser: true, strict: true, undef: true, unused: true */
512
513( function( window, factory ) {
514 // universal module definition
515 /*jshint strict: false */ /* globals define, module, require */
516 if ( typeof define == 'function' && define.amd ) {
517 // AMD
518 define( 'jquery-bridget/jquery-bridget',[ 'jquery' ], function( jQuery ) {
519 return factory( window, jQuery );
520 });
521 } else if ( typeof module == 'object' && module.exports ) {
522 // CommonJS
523 module.exports = factory(
524 window,
525 require('jquery')
526 );
527 } else {
528 // browser global
529 window.jQueryBridget = factory(
530 window,
531 window.jQuery
532 );
533 }
534
535}( window, function factory( window, jQuery ) {
536'use strict';
537
538// ----- utils ----- //
539
540var arraySlice = Array.prototype.slice;
541
542// helper function for logging errors
543// $.error breaks jQuery chaining
544var console = window.console;
545var logError = typeof console == 'undefined' ? function() {} :
546 function( message ) {
547 console.error( message );
548 };
549
550// ----- jQueryBridget ----- //
551
552function jQueryBridget( namespace, PluginClass, $ ) {
553 $ = $ || jQuery || window.jQuery;
554 if ( !$ ) {
555 return;
556 }
557
558 // add option method -> $().plugin('option', {...})
559 if ( !PluginClass.prototype.option ) {
560 // option setter
561 PluginClass.prototype.option = function( opts ) {
562 // bail out if not an object
563 if ( !$.isPlainObject( opts ) ){
564 return;
565 }
566 this.options = $.extend( true, this.options, opts );
567 };
568 }
569
570 // make jQuery plugin
571 $.fn[ namespace ] = function( arg0 /*, arg1 */ ) {
572 if ( typeof arg0 == 'string' ) {
573 // method call $().plugin( 'methodName', { options } )
574 // shift arguments by 1
575 var args = arraySlice.call( arguments, 1 );
576 return methodCall( this, arg0, args );
577 }
578 // just $().plugin({ options })
579 plainCall( this, arg0 );
580 return this;
581 };
582
583 // $().plugin('methodName')
584 function methodCall( $elems, methodName, args ) {
585 var returnValue;
586 var pluginMethodStr = '$().' + namespace + '("' + methodName + '")';
587
588 $elems.each( function( i, elem ) {
589 // get instance
590 var instance = $.data( elem, namespace );
591 if ( !instance ) {
592 logError( namespace + ' not initialized. Cannot call methods, i.e. ' +
593 pluginMethodStr );
594 return;
595 }
596
597 var method = instance[ methodName ];
598 if ( !method || methodName.charAt(0) == '_' ) {
599 logError( pluginMethodStr + ' is not a valid method' );
600 return;
601 }
602
603 // apply method, get return value
604 var value = method.apply( instance, args );
605 // set return value if value is returned, use only first value
606 returnValue = returnValue === undefined ? value : returnValue;
607 });
608
609 return returnValue !== undefined ? returnValue : $elems;
610 }
611
612 function plainCall( $elems, options ) {
613 $elems.each( function( i, elem ) {
614 var instance = $.data( elem, namespace );
615 if ( instance ) {
616 // set options & init
617 instance.option( options );
618 instance._init();
619 } else {
620 // initialize new instance
621 instance = new PluginClass( elem, options );
622 $.data( elem, namespace, instance );
623 }
624 });
625 }
626
627 updateJQuery( $ );
628
629}
630
631// ----- updateJQuery ----- //
632
633// set $.bridget for v1 backwards compatibility
634function updateJQuery( $ ) {
635 if ( !$ || ( $ && $.bridget ) ) {
636 return;
637 }
638 $.bridget = jQueryBridget;
639}
640
641updateJQuery( jQuery || window.jQuery );
642
643// ----- ----- //
644
645return jQueryBridget;
646
647}));
648
649/**
650 * EvEmitter v1.1.0
651 * Lil' event emitter
652 * MIT License
653 */
654
655/* jshint unused: true, undef: true, strict: true */
656
657( function( global, factory ) {
658 // universal module definition
659 /* jshint strict: false */ /* globals define, module, window */
660 if ( typeof define == 'function' && define.amd ) {
661 // AMD - RequireJS
662 define( 'ev-emitter/ev-emitter',factory );
663 } else if ( typeof module == 'object' && module.exports ) {
664 // CommonJS - Browserify, Webpack
665 module.exports = factory();
666 } else {
667 // Browser globals
668 global.EvEmitter = factory();
669 }
670
671}( typeof window != 'undefined' ? window : this, function() {
672
673
674
675function EvEmitter() {}
676
677var proto = EvEmitter.prototype;
678
679proto.on = function( eventName, listener ) {
680 if ( !eventName || !listener ) {
681 return;
682 }
683 // set events hash
684 var events = this._events = this._events || {};
685 // set listeners array
686 var listeners = events[ eventName ] = events[ eventName ] || [];
687 // only add once
688 if ( listeners.indexOf( listener ) == -1 ) {
689 listeners.push( listener );
690 }
691
692 return this;
693};
694
695proto.once = function( eventName, listener ) {
696 if ( !eventName || !listener ) {
697 return;
698 }
699 // add event
700 this.on( eventName, listener );
701 // set once flag
702 // set onceEvents hash
703 var onceEvents = this._onceEvents = this._onceEvents || {};
704 // set onceListeners object
705 var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {};
706 // set flag
707 onceListeners[ listener ] = true;
708
709 return this;
710};
711
712proto.off = function( eventName, listener ) {
713 var listeners = this._events && this._events[ eventName ];
714 if ( !listeners || !listeners.length ) {
715 return;
716 }
717 var index = listeners.indexOf( listener );
718 if ( index != -1 ) {
719 listeners.splice( index, 1 );
720 }
721
722 return this;
723};
724
725proto.emitEvent = function( eventName, args ) {
726 var listeners = this._events && this._events[ eventName ];
727 if ( !listeners || !listeners.length ) {
728 return;
729 }
730 // copy over to avoid interference if .off() in listener
731 listeners = listeners.slice(0);
732 args = args || [];
733 // once stuff
734 var onceListeners = this._onceEvents && this._onceEvents[ eventName ];
735
736 for ( var i=0; i < listeners.length; i++ ) {
737 var listener = listeners[i]
738 var isOnce = onceListeners && onceListeners[ listener ];
739 if ( isOnce ) {
740 // remove listener
741 // remove before trigger to prevent recursion
742 this.off( eventName, listener );
743 // unset once flag
744 delete onceListeners[ listener ];
745 }
746 // trigger listener
747 listener.apply( this, args );
748 }
749
750 return this;
751};
752
753proto.allOff = function() {
754 delete this._events;
755 delete this._onceEvents;
756};
757
758return EvEmitter;
759
760}));
761
762/*!
763 * getSize v2.0.3
764 * measure size of elements
765 * MIT license
766 */
767
768/* jshint browser: true, strict: true, undef: true, unused: true */
769/* globals console: false */
770
771( function( window, factory ) {
772 /* jshint strict: false */ /* globals define, module */
773 if ( typeof define == 'function' && define.amd ) {
774 // AMD
775 define( 'get-size/get-size',factory );
776 } else if ( typeof module == 'object' && module.exports ) {
777 // CommonJS
778 module.exports = factory();
779 } else {
780 // browser global
781 window.getSize = factory();
782 }
783
784})( window, function factory() {
785'use strict';
786
787// -------------------------- helpers -------------------------- //
788
789// get a number from a string, not a percentage
790function getStyleSize( value ) {
791 var num = parseFloat( value );
792 // not a percent like '100%', and a number
793 var isValid = value.indexOf('%') == -1 && !isNaN( num );
794 return isValid && num;
795}
796
797function noop() {}
798
799var logError = typeof console == 'undefined' ? noop :
800 function( message ) {
801 console.error( message );
802 };
803
804// -------------------------- measurements -------------------------- //
805
806var measurements = [
807 'paddingLeft',
808 'paddingRight',
809 'paddingTop',
810 'paddingBottom',
811 'marginLeft',
812 'marginRight',
813 'marginTop',
814 'marginBottom',
815 'borderLeftWidth',
816 'borderRightWidth',
817 'borderTopWidth',
818 'borderBottomWidth'
819];
820
821var measurementsLength = measurements.length;
822
823function getZeroSize() {
824 var size = {
825 width: 0,
826 height: 0,
827 innerWidth: 0,
828 innerHeight: 0,
829 outerWidth: 0,
830 outerHeight: 0
831 };
832 for ( var i=0; i < measurementsLength; i++ ) {
833 var measurement = measurements[i];
834 size[ measurement ] = 0;
835 }
836 return size;
837}
838
839// -------------------------- getStyle -------------------------- //
840
841/**
842 * getStyle, get style of element, check for Firefox bug
843 * https://bugzilla.mozilla.org/show_bug.cgi?id=548397
844 */
845function getStyle( elem ) {
846 var style = getComputedStyle( elem );
847 if ( !style ) {
848 logError( 'Style returned ' + style +
849 '. Are you running this code in a hidden iframe on Firefox? ' +
850 'See https://bit.ly/getsizebug1' );
851 }
852 return style;
853}
854
855// -------------------------- setup -------------------------- //
856
857var isSetup = false;
858
859var isBoxSizeOuter;
860
861/**
862 * setup
863 * check isBoxSizerOuter
864 * do on first getSize() rather than on page load for Firefox bug
865 */
866function setup() {
867 // setup once
868 if ( isSetup ) {
869 return;
870 }
871 isSetup = true;
872
873 // -------------------------- box sizing -------------------------- //
874
875 /**
876 * Chrome & Safari measure the outer-width on style.width on border-box elems
877 * IE11 & Firefox<29 measures the inner-width
878 */
879 var div = document.createElement('div');
880 div.style.width = '200px';
881 div.style.padding = '1px 2px 3px 4px';
882 div.style.borderStyle = 'solid';
883 div.style.borderWidth = '1px 2px 3px 4px';
884 div.style.boxSizing = 'border-box';
885
886 var body = document.body || document.documentElement;
887 body.appendChild( div );
888 var style = getStyle( div );
889 // round value for browser zoom. desandro/masonry#928
890 isBoxSizeOuter = Math.round( getStyleSize( style.width ) ) == 200;
891 getSize.isBoxSizeOuter = isBoxSizeOuter;
892
893 body.removeChild( div );
894}
895
896// -------------------------- getSize -------------------------- //
897
898function getSize( elem ) {
899 setup();
900
901 // use querySeletor if elem is string
902 if ( typeof elem == 'string' ) {
903 elem = document.querySelector( elem );
904 }
905
906 // do not proceed on non-objects
907 if ( !elem || typeof elem != 'object' || !elem.nodeType ) {
908 return;
909 }
910
911 var style = getStyle( elem );
912
913 // if hidden, everything is 0
914 if ( style.display == 'none' ) {
915 return getZeroSize();
916 }
917
918 var size = {};
919 size.width = elem.offsetWidth;
920 size.height = elem.offsetHeight;
921
922 var isBorderBox = size.isBorderBox = style.boxSizing == 'border-box';
923
924 // get all measurements
925 for ( var i=0; i < measurementsLength; i++ ) {
926 var measurement = measurements[i];
927 var value = style[ measurement ];
928 var num = parseFloat( value );
929 // any 'auto', 'medium' value will be 0
930 size[ measurement ] = !isNaN( num ) ? num : 0;
931 }
932
933 var paddingWidth = size.paddingLeft + size.paddingRight;
934 var paddingHeight = size.paddingTop + size.paddingBottom;
935 var marginWidth = size.marginLeft + size.marginRight;
936 var marginHeight = size.marginTop + size.marginBottom;
937 var borderWidth = size.borderLeftWidth + size.borderRightWidth;
938 var borderHeight = size.borderTopWidth + size.borderBottomWidth;
939
940 var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter;
941
942 // overwrite width and height if we can get it from style
943 var styleWidth = getStyleSize( style.width );
944 if ( styleWidth !== false ) {
945 size.width = styleWidth +
946 // add padding and border unless it's already including it
947 ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth );
948 }
949
950 var styleHeight = getStyleSize( style.height );
951 if ( styleHeight !== false ) {
952 size.height = styleHeight +
953 // add padding and border unless it's already including it
954 ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight );
955 }
956
957 size.innerWidth = size.width - ( paddingWidth + borderWidth );
958 size.innerHeight = size.height - ( paddingHeight + borderHeight );
959
960 size.outerWidth = size.width + marginWidth;
961 size.outerHeight = size.height + marginHeight;
962
963 return size;
964}
965
966return getSize;
967
968});
969
970/**
971 * matchesSelector v2.0.2
972 * matchesSelector( element, '.selector' )
973 * MIT license
974 */
975
976/*jshint browser: true, strict: true, undef: true, unused: true */
977
978( function( window, factory ) {
979 /*global define: false, module: false */
980 'use strict';
981 // universal module definition
982 if ( typeof define == 'function' && define.amd ) {
983 // AMD
984 define( 'desandro-matches-selector/matches-selector',factory );
985 } else if ( typeof module == 'object' && module.exports ) {
986 // CommonJS
987 module.exports = factory();
988 } else {
989 // browser global
990 window.matchesSelector = factory();
991 }
992
993}( window, function factory() {
994 'use strict';
995
996 var matchesMethod = ( function() {
997 var ElemProto = window.Element.prototype;
998 // check for the standard method name first
999 if ( ElemProto.matches ) {
1000 return 'matches';
1001 }
1002 // check un-prefixed
1003 if ( ElemProto.matchesSelector ) {
1004 return 'matchesSelector';
1005 }
1006 // check vendor prefixes
1007 var prefixes = [ 'webkit', 'moz', 'ms', 'o' ];
1008
1009 for ( var i=0; i < prefixes.length; i++ ) {
1010 var prefix = prefixes[i];
1011 var method = prefix + 'MatchesSelector';
1012 if ( ElemProto[ method ] ) {
1013 return method;
1014 }
1015 }
1016 })();
1017
1018 return function matchesSelector( elem, selector ) {
1019 return elem[ matchesMethod ]( selector );
1020 };
1021
1022}));
1023
1024/**
1025 * Fizzy UI utils v2.0.7
1026 * MIT license
1027 */
1028
1029/*jshint browser: true, undef: true, unused: true, strict: true */
1030
1031( function( window, factory ) {
1032 // universal module definition
1033 /*jshint strict: false */ /*globals define, module, require */
1034
1035 if ( typeof define == 'function' && define.amd ) {
1036 // AMD
1037 define( 'fizzy-ui-utils/utils',[
1038 'desandro-matches-selector/matches-selector'
1039 ], function( matchesSelector ) {
1040 return factory( window, matchesSelector );
1041 });
1042 } else if ( typeof module == 'object' && module.exports ) {
1043 // CommonJS
1044 module.exports = factory(
1045 window,
1046 require('desandro-matches-selector')
1047 );
1048 } else {
1049 // browser global
1050 window.fizzyUIUtils = factory(
1051 window,
1052 window.matchesSelector
1053 );
1054 }
1055
1056}( window, function factory( window, matchesSelector ) {
1057
1058
1059
1060var utils = {};
1061
1062// ----- extend ----- //
1063
1064// extends objects
1065utils.extend = function( a, b ) {
1066 for ( var prop in b ) {
1067 a[ prop ] = b[ prop ];
1068 }
1069 return a;
1070};
1071
1072// ----- modulo ----- //
1073
1074utils.modulo = function( num, div ) {
1075 return ( ( num % div ) + div ) % div;
1076};
1077
1078// ----- makeArray ----- //
1079
1080var arraySlice = Array.prototype.slice;
1081
1082// turn element or nodeList into an array
1083utils.makeArray = function( obj ) {
1084 if ( Array.isArray( obj ) ) {
1085 // use object if already an array
1086 return obj;
1087 }
1088 // return empty array if undefined or null. #6
1089 if ( obj === null || obj === undefined ) {
1090 return [];
1091 }
1092
1093 var isArrayLike = typeof obj == 'object' && typeof obj.length == 'number';
1094 if ( isArrayLike ) {
1095 // convert nodeList to array
1096 return arraySlice.call( obj );
1097 }
1098
1099 // array of single index
1100 return [ obj ];
1101};
1102
1103// ----- removeFrom ----- //
1104
1105utils.removeFrom = function( ary, obj ) {
1106 var index = ary.indexOf( obj );
1107 if ( index != -1 ) {
1108 ary.splice( index, 1 );
1109 }
1110};
1111
1112// ----- getParent ----- //
1113
1114utils.getParent = function( elem, selector ) {
1115 while ( elem.parentNode && elem != document.body ) {
1116 elem = elem.parentNode;
1117 if ( matchesSelector( elem, selector ) ) {
1118 return elem;
1119 }
1120 }
1121};
1122
1123// ----- getQueryElement ----- //
1124
1125// use element as selector string
1126utils.getQueryElement = function( elem ) {
1127 if ( typeof elem == 'string' ) {
1128 return document.querySelector( elem );
1129 }
1130 return elem;
1131};
1132
1133// ----- handleEvent ----- //
1134
1135// enable .ontype to trigger from .addEventListener( elem, 'type' )
1136utils.handleEvent = function( event ) {
1137 var method = 'on' + event.type;
1138 if ( this[ method ] ) {
1139 this[ method ]( event );
1140 }
1141};
1142
1143// ----- filterFindElements ----- //
1144
1145utils.filterFindElements = function( elems, selector ) {
1146 // make array of elems
1147 elems = utils.makeArray( elems );
1148 var ffElems = [];
1149
1150 elems.forEach( function( elem ) {
1151 // check that elem is an actual element
1152 if ( !( elem instanceof HTMLElement ) ) {
1153 return;
1154 }
1155 // add elem if no selector
1156 if ( !selector ) {
1157 ffElems.push( elem );
1158 return;
1159 }
1160 // filter & find items if we have a selector
1161 // filter
1162 if ( matchesSelector( elem, selector ) ) {
1163 ffElems.push( elem );
1164 }
1165 // find children
1166 var childElems = elem.querySelectorAll( selector );
1167 // concat childElems to filterFound array
1168 for ( var i=0; i < childElems.length; i++ ) {
1169 ffElems.push( childElems[i] );
1170 }
1171 });
1172
1173 return ffElems;
1174};
1175
1176// ----- debounceMethod ----- //
1177
1178utils.debounceMethod = function( _class, methodName, threshold ) {
1179 threshold = threshold || 100;
1180 // original method
1181 var method = _class.prototype[ methodName ];
1182 var timeoutName = methodName + 'Timeout';
1183
1184 _class.prototype[ methodName ] = function() {
1185 var timeout = this[ timeoutName ];
1186 clearTimeout( timeout );
1187
1188 var args = arguments;
1189 var _this = this;
1190 this[ timeoutName ] = setTimeout( function() {
1191 method.apply( _this, args );
1192 delete _this[ timeoutName ];
1193 }, threshold );
1194 };
1195};
1196
1197// ----- docReady ----- //
1198
1199utils.docReady = function( callback ) {
1200 var readyState = document.readyState;
1201 if ( readyState == 'complete' || readyState == 'interactive' ) {
1202 // do async to allow for other scripts to run. metafizzy/flickity#441
1203 setTimeout( callback );
1204 } else {
1205 document.addEventListener( 'DOMContentLoaded', callback );
1206 }
1207};
1208
1209// ----- htmlInit ----- //
1210
1211// http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
1212utils.toDashed = function( str ) {
1213 return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
1214 return $1 + '-' + $2;
1215 }).toLowerCase();
1216};
1217
1218var console = window.console;
1219/**
1220 * allow user to initialize classes via [data-namespace] or .js-namespace class
1221 * htmlInit( Widget, 'widgetName' )
1222 * options are parsed from data-namespace-options
1223 */
1224utils.htmlInit = function( WidgetClass, namespace ) {
1225 utils.docReady( function() {
1226 var dashedNamespace = utils.toDashed( namespace );
1227 var dataAttr = 'data-' + dashedNamespace;
1228 var dataAttrElems = document.querySelectorAll( '[' + dataAttr + ']' );
1229 var jsDashElems = document.querySelectorAll( '.js-' + dashedNamespace );
1230 var elems = utils.makeArray( dataAttrElems )
1231 .concat( utils.makeArray( jsDashElems ) );
1232 var dataOptionsAttr = dataAttr + '-options';
1233 var jQuery = window.jQuery;
1234
1235 elems.forEach( function( elem ) {
1236 var attr = elem.getAttribute( dataAttr ) ||
1237 elem.getAttribute( dataOptionsAttr );
1238 var options;
1239 try {
1240 options = attr && JSON.parse( attr );
1241 } catch ( error ) {
1242 // log error, do not initialize
1243 if ( console ) {
1244 console.error( 'Error parsing ' + dataAttr + ' on ' + elem.className +
1245 ': ' + error );
1246 }
1247 return;
1248 }
1249 // initialize
1250 var instance = new WidgetClass( elem, options );
1251 // make available via $().data('namespace')
1252 if ( jQuery ) {
1253 jQuery.data( elem, namespace, instance );
1254 }
1255 });
1256
1257 });
1258};
1259
1260// ----- ----- //
1261
1262return utils;
1263
1264}));
1265
1266// Flickity.Cell
1267( function( window, factory ) {
1268 // universal module definition
1269 if ( typeof define == 'function' && define.amd ) {
1270 // AMD
1271 define( 'flickity/js/cell',[
1272 'get-size/get-size',
1273 ], function( getSize ) {
1274 return factory( window, getSize );
1275 } );
1276 } else if ( typeof module == 'object' && module.exports ) {
1277 // CommonJS
1278 module.exports = factory(
1279 window,
1280 require('get-size')
1281 );
1282 } else {
1283 // browser global
1284 window.Flickity = window.Flickity || {};
1285 window.Flickity.Cell = factory(
1286 window,
1287 window.getSize
1288 );
1289 }
1290
1291}( window, function factory( window, getSize ) {
1292
1293
1294
1295function Cell( elem, parent ) {
1296 this.element = elem;
1297 this.parent = parent;
1298
1299 this.create();
1300}
1301
1302var proto = Cell.prototype;
1303
1304proto.create = function() {
1305 this.element.style.position = 'absolute';
1306 this.element.setAttribute( 'aria-hidden', 'true' );
1307 this.x = 0;
1308 this.shift = 0;
1309};
1310
1311proto.destroy = function() {
1312 // reset style
1313 this.unselect();
1314 this.element.style.position = '';
1315 var side = this.parent.originSide;
1316 this.element.style[ side ] = '';
1317 this.element.removeAttribute('aria-hidden');
1318};
1319
1320proto.getSize = function() {
1321 this.size = getSize( this.element );
1322};
1323
1324proto.setPosition = function( x ) {
1325 this.x = x;
1326 this.updateTarget();
1327 this.renderPosition( x );
1328};
1329
1330// setDefaultTarget v1 method, backwards compatibility, remove in v3
1331proto.updateTarget = proto.setDefaultTarget = function() {
1332 var marginProperty = this.parent.originSide == 'left' ? 'marginLeft' : 'marginRight';
1333 this.target = this.x + this.size[ marginProperty ] +
1334 this.size.width * this.parent.cellAlign;
1335};
1336
1337proto.renderPosition = function( x ) {
1338 // render position of cell with in slider
1339 var side = this.parent.originSide;
1340 this.element.style[ side ] = this.parent.getPositionValue( x );
1341};
1342
1343proto.select = function() {
1344 this.element.classList.add('is-selected');
1345 this.element.removeAttribute('aria-hidden');
1346};
1347
1348proto.unselect = function() {
1349 this.element.classList.remove('is-selected');
1350 this.element.setAttribute( 'aria-hidden', 'true' );
1351};
1352
1353/**
1354 * @param {Integer} shift - 0, 1, or -1
1355 */
1356proto.wrapShift = function( shift ) {
1357 this.shift = shift;
1358 this.renderPosition( this.x + this.parent.slideableWidth * shift );
1359};
1360
1361proto.remove = function() {
1362 this.element.parentNode.removeChild( this.element );
1363};
1364
1365return Cell;
1366
1367} ) );
1368
1369// slide
1370( function( window, factory ) {
1371 // universal module definition
1372 if ( typeof define == 'function' && define.amd ) {
1373 // AMD
1374 define( 'flickity/js/slide',factory );
1375 } else if ( typeof module == 'object' && module.exports ) {
1376 // CommonJS
1377 module.exports = factory();
1378 } else {
1379 // browser global
1380 window.Flickity = window.Flickity || {};
1381 window.Flickity.Slide = factory();
1382 }
1383
1384}( window, function factory() {
1385'use strict';
1386
1387function Slide( parent ) {
1388 this.parent = parent;
1389 this.isOriginLeft = parent.originSide == 'left';
1390 this.cells = [];
1391 this.outerWidth = 0;
1392 this.height = 0;
1393}
1394
1395var proto = Slide.prototype;
1396
1397proto.addCell = function( cell ) {
1398 this.cells.push( cell );
1399 this.outerWidth += cell.size.outerWidth;
1400 this.height = Math.max( cell.size.outerHeight, this.height );
1401 // first cell stuff
1402 if ( this.cells.length == 1 ) {
1403 this.x = cell.x; // x comes from first cell
1404 var beginMargin = this.isOriginLeft ? 'marginLeft' : 'marginRight';
1405 this.firstMargin = cell.size[ beginMargin ];
1406 }
1407};
1408
1409proto.updateTarget = function() {
1410 var endMargin = this.isOriginLeft ? 'marginRight' : 'marginLeft';
1411 var lastCell = this.getLastCell();
1412 var lastMargin = lastCell ? lastCell.size[ endMargin ] : 0;
1413 var slideWidth = this.outerWidth - ( this.firstMargin + lastMargin );
1414 this.target = this.x + this.firstMargin + slideWidth * this.parent.cellAlign;
1415};
1416
1417proto.getLastCell = function() {
1418 return this.cells[ this.cells.length - 1 ];
1419};
1420
1421proto.select = function() {
1422 this.cells.forEach( function( cell ) {
1423 cell.select();
1424 } );
1425};
1426
1427proto.unselect = function() {
1428 this.cells.forEach( function( cell ) {
1429 cell.unselect();
1430 } );
1431};
1432
1433proto.getCellElements = function() {
1434 return this.cells.map( function( cell ) {
1435 return cell.element;
1436 } );
1437};
1438
1439return Slide;
1440
1441} ) );
1442
1443// animate
1444( function( window, factory ) {
1445 // universal module definition
1446 if ( typeof define == 'function' && define.amd ) {
1447 // AMD
1448 define( 'flickity/js/animate',[
1449 'fizzy-ui-utils/utils',
1450 ], function( utils ) {
1451 return factory( window, utils );
1452 } );
1453 } else if ( typeof module == 'object' && module.exports ) {
1454 // CommonJS
1455 module.exports = factory(
1456 window,
1457 require('fizzy-ui-utils')
1458 );
1459 } else {
1460 // browser global
1461 window.Flickity = window.Flickity || {};
1462 window.Flickity.animatePrototype = factory(
1463 window,
1464 window.fizzyUIUtils
1465 );
1466 }
1467
1468}( window, function factory( window, utils ) {
1469
1470
1471
1472// -------------------------- animate -------------------------- //
1473
1474var proto = {};
1475
1476proto.startAnimation = function() {
1477 if ( this.isAnimating ) {
1478 return;
1479 }
1480
1481 this.isAnimating = true;
1482 this.restingFrames = 0;
1483 this.animate();
1484};
1485
1486proto.animate = function() {
1487 this.applyDragForce();
1488 this.applySelectedAttraction();
1489
1490 var previousX = this.x;
1491
1492 this.integratePhysics();
1493 this.positionSlider();
1494 this.settle( previousX );
1495 // animate next frame
1496 if ( this.isAnimating ) {
1497 var _this = this;
1498 requestAnimationFrame( function animateFrame() {
1499 _this.animate();
1500 } );
1501 }
1502};
1503
1504proto.positionSlider = function() {
1505 var x = this.x;
1506 // wrap position around
1507 if ( this.options.wrapAround && this.cells.length > 1 ) {
1508 x = utils.modulo( x, this.slideableWidth );
1509 x -= this.slideableWidth;
1510 this.shiftWrapCells( x );
1511 }
1512
1513 this.setTranslateX( x, this.isAnimating );
1514 this.dispatchScrollEvent();
1515};
1516
1517proto.setTranslateX = function( x, is3d ) {
1518 x += this.cursorPosition;
1519 // reverse if right-to-left and using transform
1520 x = this.options.rightToLeft ? -x : x;
1521 var translateX = this.getPositionValue( x );
1522 // use 3D transforms for hardware acceleration on iOS
1523 // but use 2D when settled, for better font-rendering
1524 this.slider.style.transform = is3d ?
1525 'translate3d(' + translateX + ',0,0)' : 'translateX(' + translateX + ')';
1526};
1527
1528proto.dispatchScrollEvent = function() {
1529 var firstSlide = this.slides[0];
1530 if ( !firstSlide ) {
1531 return;
1532 }
1533 var positionX = -this.x - firstSlide.target;
1534 var progress = positionX / this.slidesWidth;
1535 this.dispatchEvent( 'scroll', null, [ progress, positionX ] );
1536};
1537
1538proto.positionSliderAtSelected = function() {
1539 if ( !this.cells.length ) {
1540 return;
1541 }
1542 this.x = -this.selectedSlide.target;
1543 this.velocity = 0; // stop wobble
1544 this.positionSlider();
1545};
1546
1547proto.getPositionValue = function( position ) {
1548 if ( this.options.percentPosition ) {
1549 // percent position, round to 2 digits, like 12.34%
1550 return ( Math.round( ( position / this.size.innerWidth ) * 10000 ) * 0.01 ) + '%';
1551 } else {
1552 // pixel positioning
1553 return Math.round( position ) + 'px';
1554 }
1555};
1556
1557proto.settle = function( previousX ) {
1558 // keep track of frames where x hasn't moved
1559 var isResting = !this.isPointerDown &&
1560 Math.round( this.x * 100 ) == Math.round( previousX * 100 );
1561 if ( isResting ) {
1562 this.restingFrames++;
1563 }
1564 // stop animating if resting for 3 or more frames
1565 if ( this.restingFrames > 2 ) {
1566 this.isAnimating = false;
1567 delete this.isFreeScrolling;
1568 // render position with translateX when settled
1569 this.positionSlider();
1570 this.dispatchEvent( 'settle', null, [ this.selectedIndex ] );
1571 }
1572};
1573
1574proto.shiftWrapCells = function( x ) {
1575 // shift before cells
1576 var beforeGap = this.cursorPosition + x;
1577 this._shiftCells( this.beforeShiftCells, beforeGap, -1 );
1578 // shift after cells
1579 var afterGap = this.size.innerWidth - ( x + this.slideableWidth + this.cursorPosition );
1580 this._shiftCells( this.afterShiftCells, afterGap, 1 );
1581};
1582
1583proto._shiftCells = function( cells, gap, shift ) {
1584 for ( var i = 0; i < cells.length; i++ ) {
1585 var cell = cells[i];
1586 var cellShift = gap > 0 ? shift : 0;
1587 cell.wrapShift( cellShift );
1588 gap -= cell.size.outerWidth;
1589 }
1590};
1591
1592proto._unshiftCells = function( cells ) {
1593 if ( !cells || !cells.length ) {
1594 return;
1595 }
1596 for ( var i = 0; i < cells.length; i++ ) {
1597 cells[i].wrapShift( 0 );
1598 }
1599};
1600
1601// -------------------------- physics -------------------------- //
1602
1603proto.integratePhysics = function() {
1604 this.x += this.velocity;
1605 this.velocity *= this.getFrictionFactor();
1606};
1607
1608proto.applyForce = function( force ) {
1609 this.velocity += force;
1610};
1611
1612proto.getFrictionFactor = function() {
1613 return 1 - this.options[ this.isFreeScrolling ? 'freeScrollFriction' : 'friction' ];
1614};
1615
1616proto.getRestingPosition = function() {
1617 // my thanks to Steven Wittens, who simplified this math greatly
1618 return this.x + this.velocity / ( 1 - this.getFrictionFactor() );
1619};
1620
1621proto.applyDragForce = function() {
1622 if ( !this.isDraggable || !this.isPointerDown ) {
1623 return;
1624 }
1625 // change the position to drag position by applying force
1626 var dragVelocity = this.dragX - this.x;
1627 var dragForce = dragVelocity - this.velocity;
1628 this.applyForce( dragForce );
1629};
1630
1631proto.applySelectedAttraction = function() {
1632 // do not attract if pointer down or no slides
1633 var dragDown = this.isDraggable && this.isPointerDown;
1634 if ( dragDown || this.isFreeScrolling || !this.slides.length ) {
1635 return;
1636 }
1637 var distance = this.selectedSlide.target * -1 - this.x;
1638 var force = distance * this.options.selectedAttraction;
1639 this.applyForce( force );
1640};
1641
1642return proto;
1643
1644} ) );
1645
1646// Flickity main
1647/* eslint-disable max-params */
1648( function( window, factory ) {
1649 // universal module definition
1650 if ( typeof define == 'function' && define.amd ) {
1651 // AMD
1652 define( 'flickity/js/flickity',[
1653 'ev-emitter/ev-emitter',
1654 'get-size/get-size',
1655 'fizzy-ui-utils/utils',
1656 './cell',
1657 './slide',
1658 './animate',
1659 ], function( EvEmitter, getSize, utils, Cell, Slide, animatePrototype ) {
1660 return factory( window, EvEmitter, getSize, utils, Cell, Slide, animatePrototype );
1661 } );
1662 } else if ( typeof module == 'object' && module.exports ) {
1663 // CommonJS
1664 module.exports = factory(
1665 window,
1666 require('ev-emitter'),
1667 require('get-size'),
1668 require('fizzy-ui-utils'),
1669 require('./cell'),
1670 require('./slide'),
1671 require('./animate')
1672 );
1673 } else {
1674 // browser global
1675 var _Flickity = window.Flickity;
1676
1677 window.Flickity = factory(
1678 window,
1679 window.EvEmitter,
1680 window.getSize,
1681 window.fizzyUIUtils,
1682 _Flickity.Cell,
1683 _Flickity.Slide,
1684 _Flickity.animatePrototype
1685 );
1686 }
1687
1688}( window, function factory( window, EvEmitter, getSize,
1689 utils, Cell, Slide, animatePrototype ) {
1690
1691/* eslint-enable max-params */
1692
1693
1694// vars
1695var jQuery = window.jQuery;
1696var getComputedStyle = window.getComputedStyle;
1697var console = window.console;
1698
1699function moveElements( elems, toElem ) {
1700 elems = utils.makeArray( elems );
1701 while ( elems.length ) {
1702 toElem.appendChild( elems.shift() );
1703 }
1704}
1705
1706// -------------------------- Flickity -------------------------- //
1707
1708// globally unique identifiers
1709var GUID = 0;
1710// internal store of all Flickity intances
1711var instances = {};
1712
1713function Flickity( element, options ) {
1714 var queryElement = utils.getQueryElement( element );
1715 if ( !queryElement ) {
1716 if ( console ) {
1717 console.error( 'Bad element for Flickity: ' + ( queryElement || element ) );
1718 }
1719 return;
1720 }
1721 this.element = queryElement;
1722 // do not initialize twice on same element
1723 if ( this.element.flickityGUID ) {
1724 var instance = instances[ this.element.flickityGUID ];
1725 if ( instance ) instance.option( options );
1726 return instance;
1727 }
1728
1729 // add jQuery
1730 if ( jQuery ) {
1731 this.$element = jQuery( this.element );
1732 }
1733 // options
1734 this.options = utils.extend( {}, this.constructor.defaults );
1735 this.option( options );
1736
1737 // kick things off
1738 this._create();
1739}
1740
1741Flickity.defaults = {
1742 accessibility: true,
1743 // adaptiveHeight: false,
1744 cellAlign: 'center',
1745 // cellSelector: undefined,
1746 // contain: false,
1747 freeScrollFriction: 0.075, // friction when free-scrolling
1748 friction: 0.28, // friction when selecting
1749 namespaceJQueryEvents: true,
1750 // initialIndex: 0,
1751 percentPosition: true,
1752 resize: true,
1753 selectedAttraction: 0.025,
1754 setGallerySize: true,
1755 // watchCSS: false,
1756 // wrapAround: false
1757};
1758
1759// hash of methods triggered on _create()
1760Flickity.createMethods = [];
1761
1762var proto = Flickity.prototype;
1763// inherit EventEmitter
1764utils.extend( proto, EvEmitter.prototype );
1765
1766proto._create = function() {
1767 // add id for Flickity.data
1768 var id = this.guid = ++GUID;
1769 this.element.flickityGUID = id; // expando
1770 instances[ id ] = this; // associate via id
1771 // initial properties
1772 this.selectedIndex = 0;
1773 // how many frames slider has been in same position
1774 this.restingFrames = 0;
1775 // initial physics properties
1776 this.x = 0;
1777 this.velocity = 0;
1778 this.originSide = this.options.rightToLeft ? 'right' : 'left';
1779 // create viewport & slider
1780 this.viewport = document.createElement('div');
1781 this.viewport.className = 'flickity-viewport';
1782 this._createSlider();
1783
1784 if ( this.options.resize || this.options.watchCSS ) {
1785 window.addEventListener( 'resize', this );
1786 }
1787
1788 // add listeners from on option
1789 for ( var eventName in this.options.on ) {
1790 var listener = this.options.on[ eventName ];
1791 this.on( eventName, listener );
1792 }
1793
1794 Flickity.createMethods.forEach( function( method ) {
1795 this[ method ]();
1796 }, this );
1797
1798 if ( this.options.watchCSS ) {
1799 this.watchCSS();
1800 } else {
1801 this.activate();
1802 }
1803
1804};
1805
1806/**
1807 * set options
1808 * @param {Object} opts - options to extend
1809 */
1810proto.option = function( opts ) {
1811 utils.extend( this.options, opts );
1812};
1813
1814proto.activate = function() {
1815 if ( this.isActive ) {
1816 return;
1817 }
1818 this.isActive = true;
1819 this.element.classList.add('flickity-enabled');
1820 if ( this.options.rightToLeft ) {
1821 this.element.classList.add('flickity-rtl');
1822 }
1823
1824 this.getSize();
1825 // move initial cell elements so they can be loaded as cells
1826 var cellElems = this._filterFindCellElements( this.element.children );
1827 moveElements( cellElems, this.slider );
1828 this.viewport.appendChild( this.slider );
1829 this.element.appendChild( this.viewport );
1830 // get cells from children
1831 this.reloadCells();
1832
1833 if ( this.options.accessibility ) {
1834 // allow element to focusable
1835 this.element.tabIndex = 0;
1836 // listen for key presses
1837 this.element.addEventListener( 'keydown', this );
1838 }
1839
1840 this.emitEvent('activate');
1841 this.selectInitialIndex();
1842 // flag for initial activation, for using initialIndex
1843 this.isInitActivated = true;
1844 // ready event. #493
1845 this.dispatchEvent('ready');
1846};
1847
1848// slider positions the cells
1849proto._createSlider = function() {
1850 // slider element does all the positioning
1851 var slider = document.createElement('div');
1852 slider.className = 'flickity-slider';
1853 slider.style[ this.originSide ] = 0;
1854 this.slider = slider;
1855};
1856
1857proto._filterFindCellElements = function( elems ) {
1858 return utils.filterFindElements( elems, this.options.cellSelector );
1859};
1860
1861// goes through all children
1862proto.reloadCells = function() {
1863 // collection of item elements
1864 this.cells = this._makeCells( this.slider.children );
1865 this.positionCells();
1866 this._getWrapShiftCells();
1867 this.setGallerySize();
1868};
1869
1870/**
1871 * turn elements into Flickity.Cells
1872 * @param {[Array, NodeList, HTMLElement]} elems - elements to make into cells
1873 * @returns {Array} items - collection of new Flickity Cells
1874 */
1875proto._makeCells = function( elems ) {
1876 var cellElems = this._filterFindCellElements( elems );
1877
1878 // create new Flickity for collection
1879 var cells = cellElems.map( function( cellElem ) {
1880 return new Cell( cellElem, this );
1881 }, this );
1882
1883 return cells;
1884};
1885
1886proto.getLastCell = function() {
1887 return this.cells[ this.cells.length - 1 ];
1888};
1889
1890proto.getLastSlide = function() {
1891 return this.slides[ this.slides.length - 1 ];
1892};
1893
1894// positions all cells
1895proto.positionCells = function() {
1896 // size all cells
1897 this._sizeCells( this.cells );
1898 // position all cells
1899 this._positionCells( 0 );
1900};
1901
1902/**
1903 * position certain cells
1904 * @param {Integer} index - which cell to start with
1905 */
1906proto._positionCells = function( index ) {
1907 index = index || 0;
1908 // also measure maxCellHeight
1909 // start 0 if positioning all cells
1910 this.maxCellHeight = index ? this.maxCellHeight || 0 : 0;
1911 var cellX = 0;
1912 // get cellX
1913 if ( index > 0 ) {
1914 var startCell = this.cells[ index - 1 ];
1915 cellX = startCell.x + startCell.size.outerWidth;
1916 }
1917 var len = this.cells.length;
1918 for ( var i = index; i < len; i++ ) {
1919 var cell = this.cells[i];
1920 cell.setPosition( cellX );
1921 cellX += cell.size.outerWidth;
1922 this.maxCellHeight = Math.max( cell.size.outerHeight, this.maxCellHeight );
1923 }
1924 // keep track of cellX for wrap-around
1925 this.slideableWidth = cellX;
1926 // slides
1927 this.updateSlides();
1928 // contain slides target
1929 this._containSlides();
1930 // update slidesWidth
1931 this.slidesWidth = len ? this.getLastSlide().target - this.slides[0].target : 0;
1932};
1933
1934/**
1935 * cell.getSize() on multiple cells
1936 * @param {Array} cells - cells to size
1937 */
1938proto._sizeCells = function( cells ) {
1939 cells.forEach( function( cell ) {
1940 cell.getSize();
1941 } );
1942};
1943
1944// -------------------------- -------------------------- //
1945
1946proto.updateSlides = function() {
1947 this.slides = [];
1948 if ( !this.cells.length ) {
1949 return;
1950 }
1951
1952 var slide = new Slide( this );
1953 this.slides.push( slide );
1954 var isOriginLeft = this.originSide == 'left';
1955 var nextMargin = isOriginLeft ? 'marginRight' : 'marginLeft';
1956
1957 var canCellFit = this._getCanCellFit();
1958
1959 this.cells.forEach( function( cell, i ) {
1960 // just add cell if first cell in slide
1961 if ( !slide.cells.length ) {
1962 slide.addCell( cell );
1963 return;
1964 }
1965
1966 var slideWidth = ( slide.outerWidth - slide.firstMargin ) +
1967 ( cell.size.outerWidth - cell.size[ nextMargin ] );
1968
1969 if ( canCellFit.call( this, i, slideWidth ) ) {
1970 slide.addCell( cell );
1971 } else {
1972 // doesn't fit, new slide
1973 slide.updateTarget();
1974
1975 slide = new Slide( this );
1976 this.slides.push( slide );
1977 slide.addCell( cell );
1978 }
1979 }, this );
1980 // last slide
1981 slide.updateTarget();
1982 // update .selectedSlide
1983 this.updateSelectedSlide();
1984};
1985
1986proto._getCanCellFit = function() {
1987 var groupCells = this.options.groupCells;
1988 if ( !groupCells ) {
1989 return function() {
1990 return false;
1991 };
1992 } else if ( typeof groupCells == 'number' ) {
1993 // group by number. 3 -> [0,1,2], [3,4,5], ...
1994 var number = parseInt( groupCells, 10 );
1995 return function( i ) {
1996 return ( i % number ) !== 0;
1997 };
1998 }
1999 // default, group by width of slide
2000 // parse '75%
2001 var percentMatch = typeof groupCells == 'string' &&
2002 groupCells.match( /^(\d+)%$/ );
2003 var percent = percentMatch ? parseInt( percentMatch[1], 10 ) / 100 : 1;
2004 return function( i, slideWidth ) {
2005 /* eslint-disable-next-line no-invalid-this */
2006 return slideWidth <= ( this.size.innerWidth + 1 ) * percent;
2007 };
2008};
2009
2010// alias _init for jQuery plugin .flickity()
2011proto._init =
2012proto.reposition = function() {
2013 this.positionCells();
2014 this.positionSliderAtSelected();
2015};
2016
2017proto.getSize = function() {
2018 this.size = getSize( this.element );
2019 this.setCellAlign();
2020 this.cursorPosition = this.size.innerWidth * this.cellAlign;
2021};
2022
2023var cellAlignShorthands = {
2024 // cell align, then based on origin side
2025 center: {
2026 left: 0.5,
2027 right: 0.5,
2028 },
2029 left: {
2030 left: 0,
2031 right: 1,
2032 },
2033 right: {
2034 right: 0,
2035 left: 1,
2036 },
2037};
2038
2039proto.setCellAlign = function() {
2040 var shorthand = cellAlignShorthands[ this.options.cellAlign ];
2041 this.cellAlign = shorthand ? shorthand[ this.originSide ] : this.options.cellAlign;
2042};
2043
2044proto.setGallerySize = function() {
2045 if ( this.options.setGallerySize ) {
2046 var height = this.options.adaptiveHeight && this.selectedSlide ?
2047 this.selectedSlide.height : this.maxCellHeight;
2048 this.viewport.style.height = height + 'px';
2049 }
2050};
2051
2052proto._getWrapShiftCells = function() {
2053 // only for wrap-around
2054 if ( !this.options.wrapAround ) {
2055 return;
2056 }
2057 // unshift previous cells
2058 this._unshiftCells( this.beforeShiftCells );
2059 this._unshiftCells( this.afterShiftCells );
2060 // get before cells
2061 // initial gap
2062 var gapX = this.cursorPosition;
2063 var cellIndex = this.cells.length - 1;
2064 this.beforeShiftCells = this._getGapCells( gapX, cellIndex, -1 );
2065 // get after cells
2066 // ending gap between last cell and end of gallery viewport
2067 gapX = this.size.innerWidth - this.cursorPosition;
2068 // start cloning at first cell, working forwards
2069 this.afterShiftCells = this._getGapCells( gapX, 0, 1 );
2070};
2071
2072proto._getGapCells = function( gapX, cellIndex, increment ) {
2073 // keep adding cells until the cover the initial gap
2074 var cells = [];
2075 while ( gapX > 0 ) {
2076 var cell = this.cells[ cellIndex ];
2077 if ( !cell ) {
2078 break;
2079 }
2080 cells.push( cell );
2081 cellIndex += increment;
2082 gapX -= cell.size.outerWidth;
2083 }
2084 return cells;
2085};
2086
2087// ----- contain ----- //
2088
2089// contain cell targets so no excess sliding
2090proto._containSlides = function() {
2091 if ( !this.options.contain || this.options.wrapAround || !this.cells.length ) {
2092 return;
2093 }
2094 var isRightToLeft = this.options.rightToLeft;
2095 var beginMargin = isRightToLeft ? 'marginRight' : 'marginLeft';
2096 var endMargin = isRightToLeft ? 'marginLeft' : 'marginRight';
2097 var contentWidth = this.slideableWidth - this.getLastCell().size[ endMargin ];
2098 // content is less than gallery size
2099 var isContentSmaller = contentWidth < this.size.innerWidth;
2100 // bounds
2101 var beginBound = this.cursorPosition + this.cells[0].size[ beginMargin ];
2102 var endBound = contentWidth - this.size.innerWidth * ( 1 - this.cellAlign );
2103 // contain each cell target
2104 this.slides.forEach( function( slide ) {
2105 if ( isContentSmaller ) {
2106 // all cells fit inside gallery
2107 slide.target = contentWidth * this.cellAlign;
2108 } else {
2109 // contain to bounds
2110 slide.target = Math.max( slide.target, beginBound );
2111 slide.target = Math.min( slide.target, endBound );
2112 }
2113 }, this );
2114};
2115
2116// ----- ----- //
2117
2118/**
2119 * emits events via eventEmitter and jQuery events
2120 * @param {String} type - name of event
2121 * @param {Event} event - original event
2122 * @param {Array} args - extra arguments
2123 */
2124proto.dispatchEvent = function( type, event, args ) {
2125 var emitArgs = event ? [ event ].concat( args ) : args;
2126 this.emitEvent( type, emitArgs );
2127
2128 if ( jQuery && this.$element ) {
2129 // default trigger with type if no event
2130 type += this.options.namespaceJQueryEvents ? '.flickity' : '';
2131 var $event = type;
2132 if ( event ) {
2133 // create jQuery event
2134 var jQEvent = new jQuery.Event( event );
2135 jQEvent.type = type;
2136 $event = jQEvent;
2137 }
2138 this.$element.trigger( $event, args );
2139 }
2140};
2141
2142// -------------------------- select -------------------------- //
2143
2144/**
2145 * @param {Integer} index - index of the slide
2146 * @param {Boolean} isWrap - will wrap-around to last/first if at the end
2147 * @param {Boolean} isInstant - will immediately set position at selected cell
2148 */
2149proto.select = function( index, isWrap, isInstant ) {
2150 if ( !this.isActive ) {
2151 return;
2152 }
2153 index = parseInt( index, 10 );
2154 this._wrapSelect( index );
2155
2156 if ( this.options.wrapAround || isWrap ) {
2157 index = utils.modulo( index, this.slides.length );
2158 }
2159 // bail if invalid index
2160 if ( !this.slides[ index ] ) {
2161 return;
2162 }
2163 var prevIndex = this.selectedIndex;
2164 this.selectedIndex = index;
2165 this.updateSelectedSlide();
2166 if ( isInstant ) {
2167 this.positionSliderAtSelected();
2168 } else {
2169 this.startAnimation();
2170 }
2171 if ( this.options.adaptiveHeight ) {
2172 this.setGallerySize();
2173 }
2174 // events
2175 this.dispatchEvent( 'select', null, [ index ] );
2176 // change event if new index
2177 if ( index != prevIndex ) {
2178 this.dispatchEvent( 'change', null, [ index ] );
2179 }
2180 // old v1 event name, remove in v3
2181 this.dispatchEvent('cellSelect');
2182};
2183
2184// wraps position for wrapAround, to move to closest slide. #113
2185proto._wrapSelect = function( index ) {
2186 var len = this.slides.length;
2187 var isWrapping = this.options.wrapAround && len > 1;
2188 if ( !isWrapping ) {
2189 return index;
2190 }
2191 var wrapIndex = utils.modulo( index, len );
2192 // go to shortest
2193 var delta = Math.abs( wrapIndex - this.selectedIndex );
2194 var backWrapDelta = Math.abs( ( wrapIndex + len ) - this.selectedIndex );
2195 var forewardWrapDelta = Math.abs( ( wrapIndex - len ) - this.selectedIndex );
2196 if ( !this.isDragSelect && backWrapDelta < delta ) {
2197 index += len;
2198 } else if ( !this.isDragSelect && forewardWrapDelta < delta ) {
2199 index -= len;
2200 }
2201 // wrap position so slider is within normal area
2202 if ( index < 0 ) {
2203 this.x -= this.slideableWidth;
2204 } else if ( index >= len ) {
2205 this.x += this.slideableWidth;
2206 }
2207};
2208
2209proto.previous = function( isWrap, isInstant ) {
2210 this.select( this.selectedIndex - 1, isWrap, isInstant );
2211};
2212
2213proto.next = function( isWrap, isInstant ) {
2214 this.select( this.selectedIndex + 1, isWrap, isInstant );
2215};
2216
2217proto.updateSelectedSlide = function() {
2218 var slide = this.slides[ this.selectedIndex ];
2219 // selectedIndex could be outside of slides, if triggered before resize()
2220 if ( !slide ) {
2221 return;
2222 }
2223 // unselect previous selected slide
2224 this.unselectSelectedSlide();
2225 // update new selected slide
2226 this.selectedSlide = slide;
2227 slide.select();
2228 this.selectedCells = slide.cells;
2229 this.selectedElements = slide.getCellElements();
2230 // HACK: selectedCell & selectedElement is first cell in slide, backwards compatibility
2231 // Remove in v3?
2232 this.selectedCell = slide.cells[0];
2233 this.selectedElement = this.selectedElements[0];
2234};
2235
2236proto.unselectSelectedSlide = function() {
2237 if ( this.selectedSlide ) {
2238 this.selectedSlide.unselect();
2239 }
2240};
2241
2242proto.selectInitialIndex = function() {
2243 var initialIndex = this.options.initialIndex;
2244 // already activated, select previous selectedIndex
2245 if ( this.isInitActivated ) {
2246 this.select( this.selectedIndex, false, true );
2247 return;
2248 }
2249 // select with selector string
2250 if ( initialIndex && typeof initialIndex == 'string' ) {
2251 var cell = this.queryCell( initialIndex );
2252 if ( cell ) {
2253 this.selectCell( initialIndex, false, true );
2254 return;
2255 }
2256 }
2257
2258 var index = 0;
2259 // select with number
2260 if ( initialIndex && this.slides[ initialIndex ] ) {
2261 index = initialIndex;
2262 }
2263 // select instantly
2264 this.select( index, false, true );
2265};
2266
2267/**
2268 * select slide from number or cell element
2269 * @param {[Element, Number]} value - zero-based index or element to select
2270 * @param {Boolean} isWrap - enables wrapping around for extra index
2271 * @param {Boolean} isInstant - disables slide animation
2272 */
2273proto.selectCell = function( value, isWrap, isInstant ) {
2274 // get cell
2275 var cell = this.queryCell( value );
2276 if ( !cell ) {
2277 return;
2278 }
2279
2280 var index = this.getCellSlideIndex( cell );
2281 this.select( index, isWrap, isInstant );
2282};
2283
2284proto.getCellSlideIndex = function( cell ) {
2285 // get index of slides that has cell
2286 for ( var i = 0; i < this.slides.length; i++ ) {
2287 var slide = this.slides[i];
2288 var index = slide.cells.indexOf( cell );
2289 if ( index != -1 ) {
2290 return i;
2291 }
2292 }
2293};
2294
2295// -------------------------- get cells -------------------------- //
2296
2297/**
2298 * get Flickity.Cell, given an Element
2299 * @param {Element} elem - matching cell element
2300 * @returns {Flickity.Cell} cell - matching cell
2301 */
2302proto.getCell = function( elem ) {
2303 // loop through cells to get the one that matches
2304 for ( var i = 0; i < this.cells.length; i++ ) {
2305 var cell = this.cells[i];
2306 if ( cell.element == elem ) {
2307 return cell;
2308 }
2309 }
2310};
2311
2312/**
2313 * get collection of Flickity.Cells, given Elements
2314 * @param {[Element, Array, NodeList]} elems - multiple elements
2315 * @returns {Array} cells - Flickity.Cells
2316 */
2317proto.getCells = function( elems ) {
2318 elems = utils.makeArray( elems );
2319 var cells = [];
2320 elems.forEach( function( elem ) {
2321 var cell = this.getCell( elem );
2322 if ( cell ) {
2323 cells.push( cell );
2324 }
2325 }, this );
2326 return cells;
2327};
2328
2329/**
2330 * get cell elements
2331 * @returns {Array} cellElems
2332 */
2333proto.getCellElements = function() {
2334 return this.cells.map( function( cell ) {
2335 return cell.element;
2336 } );
2337};
2338
2339/**
2340 * get parent cell from an element
2341 * @param {Element} elem - child element
2342 * @returns {Flickit.Cell} cell - parent cell
2343 */
2344proto.getParentCell = function( elem ) {
2345 // first check if elem is cell
2346 var cell = this.getCell( elem );
2347 if ( cell ) {
2348 return cell;
2349 }
2350 // try to get parent cell elem
2351 elem = utils.getParent( elem, '.flickity-slider > *' );
2352 return this.getCell( elem );
2353};
2354
2355/**
2356 * get cells adjacent to a slide
2357 * @param {Integer} adjCount - number of adjacent slides
2358 * @param {Integer} index - index of slide to start
2359 * @returns {Array} cells - array of Flickity.Cells
2360 */
2361proto.getAdjacentCellElements = function( adjCount, index ) {
2362 if ( !adjCount ) {
2363 return this.selectedSlide.getCellElements();
2364 }
2365 index = index === undefined ? this.selectedIndex : index;
2366
2367 var len = this.slides.length;
2368 if ( 1 + ( adjCount * 2 ) >= len ) {
2369 return this.getCellElements();
2370 }
2371
2372 var cellElems = [];
2373 for ( var i = index - adjCount; i <= index + adjCount; i++ ) {
2374 var slideIndex = this.options.wrapAround ? utils.modulo( i, len ) : i;
2375 var slide = this.slides[ slideIndex ];
2376 if ( slide ) {
2377 cellElems = cellElems.concat( slide.getCellElements() );
2378 }
2379 }
2380 return cellElems;
2381};
2382
2383/**
2384 * select slide from number or cell element
2385 * @param {[Element, String, Number]} selector - element, selector string, or index
2386 * @returns {Flickity.Cell} - matching cell
2387 */
2388proto.queryCell = function( selector ) {
2389 if ( typeof selector == 'number' ) {
2390 // use number as index
2391 return this.cells[ selector ];
2392 }
2393 if ( typeof selector == 'string' ) {
2394 // do not select invalid selectors from hash: #123, #/. #791
2395 if ( selector.match( /^[#.]?[\d/]/ ) ) {
2396 return;
2397 }
2398 // use string as selector, get element
2399 selector = this.element.querySelector( selector );
2400 }
2401 // get cell from element
2402 return this.getCell( selector );
2403};
2404
2405// -------------------------- events -------------------------- //
2406
2407proto.uiChange = function() {
2408 this.emitEvent('uiChange');
2409};
2410
2411// keep focus on element when child UI elements are clicked
2412proto.childUIPointerDown = function( event ) {
2413 // HACK iOS does not allow touch events to bubble up?!
2414 if ( event.type != 'touchstart' ) {
2415 event.preventDefault();
2416 }
2417 this.focus();
2418};
2419
2420// ----- resize ----- //
2421
2422proto.onresize = function() {
2423 this.watchCSS();
2424 this.resize();
2425};
2426
2427utils.debounceMethod( Flickity, 'onresize', 150 );
2428
2429proto.resize = function() {
2430 if ( !this.isActive ) {
2431 return;
2432 }
2433 this.getSize();
2434 // wrap values
2435 if ( this.options.wrapAround ) {
2436 this.x = utils.modulo( this.x, this.slideableWidth );
2437 }
2438 this.positionCells();
2439 this._getWrapShiftCells();
2440 this.setGallerySize();
2441 this.emitEvent('resize');
2442 // update selected index for group slides, instant
2443 // TODO: position can be lost between groups of various numbers
2444 var selectedElement = this.selectedElements && this.selectedElements[0];
2445 this.selectCell( selectedElement, false, true );
2446};
2447
2448// watches the :after property, activates/deactivates
2449proto.watchCSS = function() {
2450 var watchOption = this.options.watchCSS;
2451 if ( !watchOption ) {
2452 return;
2453 }
2454
2455 var afterContent = getComputedStyle( this.element, ':after' ).content;
2456 // activate if :after { content: 'flickity' }
2457 if ( afterContent.indexOf('flickity') != -1 ) {
2458 this.activate();
2459 } else {
2460 this.deactivate();
2461 }
2462};
2463
2464// ----- keydown ----- //
2465
2466// go previous/next if left/right keys pressed
2467proto.onkeydown = function( event ) {
2468 // only work if element is in focus
2469 var isNotFocused = document.activeElement && document.activeElement != this.element;
2470 if ( !this.options.accessibility || isNotFocused ) {
2471 return;
2472 }
2473
2474 var handler = Flickity.keyboardHandlers[ event.keyCode ];
2475 if ( handler ) {
2476 handler.call( this );
2477 }
2478};
2479
2480Flickity.keyboardHandlers = {
2481 // left arrow
2482 37: function() {
2483 var leftMethod = this.options.rightToLeft ? 'next' : 'previous';
2484 this.uiChange();
2485 this[ leftMethod ]();
2486 },
2487 // right arrow
2488 39: function() {
2489 var rightMethod = this.options.rightToLeft ? 'previous' : 'next';
2490 this.uiChange();
2491 this[ rightMethod ]();
2492 },
2493};
2494
2495// ----- focus ----- //
2496
2497proto.focus = function() {
2498 // TODO remove scrollTo once focus options gets more support
2499 // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus ...
2500 // #Browser_compatibility
2501 var prevScrollY = window.pageYOffset;
2502 this.element.focus({ preventScroll: true });
2503 // hack to fix scroll jump after focus, #76
2504 if ( window.pageYOffset != prevScrollY ) {
2505 window.scrollTo( window.pageXOffset, prevScrollY );
2506 }
2507};
2508
2509// -------------------------- destroy -------------------------- //
2510
2511// deactivate all Flickity functionality, but keep stuff available
2512proto.deactivate = function() {
2513 if ( !this.isActive ) {
2514 return;
2515 }
2516 this.element.classList.remove('flickity-enabled');
2517 this.element.classList.remove('flickity-rtl');
2518 this.unselectSelectedSlide();
2519 // destroy cells
2520 this.cells.forEach( function( cell ) {
2521 cell.destroy();
2522 } );
2523 this.element.removeChild( this.viewport );
2524 // move child elements back into element
2525 moveElements( this.slider.children, this.element );
2526 if ( this.options.accessibility ) {
2527 this.element.removeAttribute('tabIndex');
2528 this.element.removeEventListener( 'keydown', this );
2529 }
2530 // set flags
2531 this.isActive = false;
2532 this.emitEvent('deactivate');
2533};
2534
2535proto.destroy = function() {
2536 this.deactivate();
2537 window.removeEventListener( 'resize', this );
2538 this.allOff();
2539 this.emitEvent('destroy');
2540 if ( jQuery && this.$element ) {
2541 jQuery.removeData( this.element, 'flickity' );
2542 }
2543 delete this.element.flickityGUID;
2544 delete instances[ this.guid ];
2545};
2546
2547// -------------------------- prototype -------------------------- //
2548
2549utils.extend( proto, animatePrototype );
2550
2551// -------------------------- extras -------------------------- //
2552
2553/**
2554 * get Flickity instance from element
2555 * @param {[Element, String]} elem - element or selector string
2556 * @returns {Flickity} - Flickity instance
2557 */
2558Flickity.data = function( elem ) {
2559 elem = utils.getQueryElement( elem );
2560 var id = elem && elem.flickityGUID;
2561 return id && instances[ id ];
2562};
2563
2564utils.htmlInit( Flickity, 'flickity' );
2565
2566if ( jQuery && jQuery.bridget ) {
2567 jQuery.bridget( 'flickity', Flickity );
2568}
2569
2570// set internal jQuery, for Webpack + jQuery v3, #478
2571Flickity.setJQuery = function( jq ) {
2572 jQuery = jq;
2573};
2574
2575Flickity.Cell = Cell;
2576Flickity.Slide = Slide;
2577
2578return Flickity;
2579
2580} ) );
2581
2582/*!
2583 * Unipointer v2.3.0
2584 * base class for doing one thing with pointer event
2585 * MIT license
2586 */
2587
2588/*jshint browser: true, undef: true, unused: true, strict: true */
2589
2590( function( window, factory ) {
2591 // universal module definition
2592 /* jshint strict: false */ /*global define, module, require */
2593 if ( typeof define == 'function' && define.amd ) {
2594 // AMD
2595 define( 'unipointer/unipointer',[
2596 'ev-emitter/ev-emitter'
2597 ], function( EvEmitter ) {
2598 return factory( window, EvEmitter );
2599 });
2600 } else if ( typeof module == 'object' && module.exports ) {
2601 // CommonJS
2602 module.exports = factory(
2603 window,
2604 require('ev-emitter')
2605 );
2606 } else {
2607 // browser global
2608 window.Unipointer = factory(
2609 window,
2610 window.EvEmitter
2611 );
2612 }
2613
2614}( window, function factory( window, EvEmitter ) {
2615
2616
2617
2618function noop() {}
2619
2620function Unipointer() {}
2621
2622// inherit EvEmitter
2623var proto = Unipointer.prototype = Object.create( EvEmitter.prototype );
2624
2625proto.bindStartEvent = function( elem ) {
2626 this._bindStartEvent( elem, true );
2627};
2628
2629proto.unbindStartEvent = function( elem ) {
2630 this._bindStartEvent( elem, false );
2631};
2632
2633/**
2634 * Add or remove start event
2635 * @param {Boolean} isAdd - remove if falsey
2636 */
2637proto._bindStartEvent = function( elem, isAdd ) {
2638 // munge isAdd, default to true
2639 isAdd = isAdd === undefined ? true : isAdd;
2640 var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener';
2641
2642 // default to mouse events
2643 var startEvent = 'mousedown';
2644 if ( window.PointerEvent ) {
2645 // Pointer Events
2646 startEvent = 'pointerdown';
2647 } else if ( 'ontouchstart' in window ) {
2648 // Touch Events. iOS Safari
2649 startEvent = 'touchstart';
2650 }
2651 elem[ bindMethod ]( startEvent, this );
2652};
2653
2654// trigger handler methods for events
2655proto.handleEvent = function( event ) {
2656 var method = 'on' + event.type;
2657 if ( this[ method ] ) {
2658 this[ method ]( event );
2659 }
2660};
2661
2662// returns the touch that we're keeping track of
2663proto.getTouch = function( touches ) {
2664 for ( var i=0; i < touches.length; i++ ) {
2665 var touch = touches[i];
2666 if ( touch.identifier == this.pointerIdentifier ) {
2667 return touch;
2668 }
2669 }
2670};
2671
2672// ----- start event ----- //
2673
2674proto.onmousedown = function( event ) {
2675 // dismiss clicks from right or middle buttons
2676 var button = event.button;
2677 if ( button && ( button !== 0 && button !== 1 ) ) {
2678 return;
2679 }
2680 this._pointerDown( event, event );
2681};
2682
2683proto.ontouchstart = function( event ) {
2684 this._pointerDown( event, event.changedTouches[0] );
2685};
2686
2687proto.onpointerdown = function( event ) {
2688 this._pointerDown( event, event );
2689};
2690
2691/**
2692 * pointer start
2693 * @param {Event} event
2694 * @param {Event or Touch} pointer
2695 */
2696proto._pointerDown = function( event, pointer ) {
2697 // dismiss right click and other pointers
2698 // button = 0 is okay, 1-4 not
2699 if ( event.button || this.isPointerDown ) {
2700 return;
2701 }
2702
2703 this.isPointerDown = true;
2704 // save pointer identifier to match up touch events
2705 this.pointerIdentifier = pointer.pointerId !== undefined ?
2706 // pointerId for pointer events, touch.indentifier for touch events
2707 pointer.pointerId : pointer.identifier;
2708
2709 this.pointerDown( event, pointer );
2710};
2711
2712proto.pointerDown = function( event, pointer ) {
2713 this._bindPostStartEvents( event );
2714 this.emitEvent( 'pointerDown', [ event, pointer ] );
2715};
2716
2717// hash of events to be bound after start event
2718var postStartEvents = {
2719 mousedown: [ 'mousemove', 'mouseup' ],
2720 touchstart: [ 'touchmove', 'touchend', 'touchcancel' ],
2721 pointerdown: [ 'pointermove', 'pointerup', 'pointercancel' ],
2722};
2723
2724proto._bindPostStartEvents = function( event ) {
2725 if ( !event ) {
2726 return;
2727 }
2728 // get proper events to match start event
2729 var events = postStartEvents[ event.type ];
2730 // bind events to node
2731 events.forEach( function( eventName ) {
2732 window.addEventListener( eventName, this );
2733 }, this );
2734 // save these arguments
2735 this._boundPointerEvents = events;
2736};
2737
2738proto._unbindPostStartEvents = function() {
2739 // check for _boundEvents, in case dragEnd triggered twice (old IE8 bug)
2740 if ( !this._boundPointerEvents ) {
2741 return;
2742 }
2743 this._boundPointerEvents.forEach( function( eventName ) {
2744 window.removeEventListener( eventName, this );
2745 }, this );
2746
2747 delete this._boundPointerEvents;
2748};
2749
2750// ----- move event ----- //
2751
2752proto.onmousemove = function( event ) {
2753 this._pointerMove( event, event );
2754};
2755
2756proto.onpointermove = function( event ) {
2757 if ( event.pointerId == this.pointerIdentifier ) {
2758 this._pointerMove( event, event );
2759 }
2760};
2761
2762proto.ontouchmove = function( event ) {
2763 var touch = this.getTouch( event.changedTouches );
2764 if ( touch ) {
2765 this._pointerMove( event, touch );
2766 }
2767};
2768
2769/**
2770 * pointer move
2771 * @param {Event} event
2772 * @param {Event or Touch} pointer
2773 * @private
2774 */
2775proto._pointerMove = function( event, pointer ) {
2776 this.pointerMove( event, pointer );
2777};
2778
2779// public
2780proto.pointerMove = function( event, pointer ) {
2781 this.emitEvent( 'pointerMove', [ event, pointer ] );
2782};
2783
2784// ----- end event ----- //
2785
2786
2787proto.onmouseup = function( event ) {
2788 this._pointerUp( event, event );
2789};
2790
2791proto.onpointerup = function( event ) {
2792 if ( event.pointerId == this.pointerIdentifier ) {
2793 this._pointerUp( event, event );
2794 }
2795};
2796
2797proto.ontouchend = function( event ) {
2798 var touch = this.getTouch( event.changedTouches );
2799 if ( touch ) {
2800 this._pointerUp( event, touch );
2801 }
2802};
2803
2804/**
2805 * pointer up
2806 * @param {Event} event
2807 * @param {Event or Touch} pointer
2808 * @private
2809 */
2810proto._pointerUp = function( event, pointer ) {
2811 this._pointerDone();
2812 this.pointerUp( event, pointer );
2813};
2814
2815// public
2816proto.pointerUp = function( event, pointer ) {
2817 this.emitEvent( 'pointerUp', [ event, pointer ] );
2818};
2819
2820// ----- pointer done ----- //
2821
2822// triggered on pointer up & pointer cancel
2823proto._pointerDone = function() {
2824 this._pointerReset();
2825 this._unbindPostStartEvents();
2826 this.pointerDone();
2827};
2828
2829proto._pointerReset = function() {
2830 // reset properties
2831 this.isPointerDown = false;
2832 delete this.pointerIdentifier;
2833};
2834
2835proto.pointerDone = noop;
2836
2837// ----- pointer cancel ----- //
2838
2839proto.onpointercancel = function( event ) {
2840 if ( event.pointerId == this.pointerIdentifier ) {
2841 this._pointerCancel( event, event );
2842 }
2843};
2844
2845proto.ontouchcancel = function( event ) {
2846 var touch = this.getTouch( event.changedTouches );
2847 if ( touch ) {
2848 this._pointerCancel( event, touch );
2849 }
2850};
2851
2852/**
2853 * pointer cancel
2854 * @param {Event} event
2855 * @param {Event or Touch} pointer
2856 * @private
2857 */
2858proto._pointerCancel = function( event, pointer ) {
2859 this._pointerDone();
2860 this.pointerCancel( event, pointer );
2861};
2862
2863// public
2864proto.pointerCancel = function( event, pointer ) {
2865 this.emitEvent( 'pointerCancel', [ event, pointer ] );
2866};
2867
2868// ----- ----- //
2869
2870// utility function for getting x/y coords from event
2871Unipointer.getPointerPoint = function( pointer ) {
2872 return {
2873 x: pointer.pageX,
2874 y: pointer.pageY
2875 };
2876};
2877
2878// ----- ----- //
2879
2880return Unipointer;
2881
2882}));
2883
2884/*!
2885 * Unidragger v2.3.1
2886 * Draggable base class
2887 * MIT license
2888 */
2889
2890/*jshint browser: true, unused: true, undef: true, strict: true */
2891
2892( function( window, factory ) {
2893 // universal module definition
2894 /*jshint strict: false */ /*globals define, module, require */
2895
2896 if ( typeof define == 'function' && define.amd ) {
2897 // AMD
2898 define( 'unidragger/unidragger',[
2899 'unipointer/unipointer'
2900 ], function( Unipointer ) {
2901 return factory( window, Unipointer );
2902 });
2903 } else if ( typeof module == 'object' && module.exports ) {
2904 // CommonJS
2905 module.exports = factory(
2906 window,
2907 require('unipointer')
2908 );
2909 } else {
2910 // browser global
2911 window.Unidragger = factory(
2912 window,
2913 window.Unipointer
2914 );
2915 }
2916
2917}( window, function factory( window, Unipointer ) {
2918
2919
2920
2921// -------------------------- Unidragger -------------------------- //
2922
2923function Unidragger() {}
2924
2925// inherit Unipointer & EvEmitter
2926var proto = Unidragger.prototype = Object.create( Unipointer.prototype );
2927
2928// ----- bind start ----- //
2929
2930proto.bindHandles = function() {
2931 this._bindHandles( true );
2932};
2933
2934proto.unbindHandles = function() {
2935 this._bindHandles( false );
2936};
2937
2938/**
2939 * Add or remove start event
2940 * @param {Boolean} isAdd
2941 */
2942proto._bindHandles = function( isAdd ) {
2943 // munge isAdd, default to true
2944 isAdd = isAdd === undefined ? true : isAdd;
2945 // bind each handle
2946 var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener';
2947 var touchAction = isAdd ? this._touchActionValue : '';
2948 for ( var i=0; i < this.handles.length; i++ ) {
2949 var handle = this.handles[i];
2950 this._bindStartEvent( handle, isAdd );
2951 handle[ bindMethod ]( 'click', this );
2952 // touch-action: none to override browser touch gestures. metafizzy/flickity#540
2953 if ( window.PointerEvent ) {
2954 handle.style.touchAction = touchAction;
2955 }
2956 }
2957};
2958
2959// prototype so it can be overwriteable by Flickity
2960proto._touchActionValue = 'none';
2961
2962// ----- start event ----- //
2963
2964/**
2965 * pointer start
2966 * @param {Event} event
2967 * @param {Event or Touch} pointer
2968 */
2969proto.pointerDown = function( event, pointer ) {
2970 var isOkay = this.okayPointerDown( event );
2971 if ( !isOkay ) {
2972 return;
2973 }
2974 // track start event position
2975 // Safari 9 overrides pageX and pageY. These values needs to be copied. flickity#842
2976 this.pointerDownPointer = {
2977 pageX: pointer.pageX,
2978 pageY: pointer.pageY,
2979 };
2980
2981 event.preventDefault();
2982 this.pointerDownBlur();
2983 // bind move and end events
2984 this._bindPostStartEvents( event );
2985 this.emitEvent( 'pointerDown', [ event, pointer ] );
2986};
2987
2988// nodes that have text fields
2989var cursorNodes = {
2990 TEXTAREA: true,
2991 INPUT: true,
2992 SELECT: true,
2993 OPTION: true,
2994};
2995
2996// input types that do not have text fields
2997var clickTypes = {
2998 radio: true,
2999 checkbox: true,
3000 button: true,
3001 submit: true,
3002 image: true,
3003 file: true,
3004};
3005
3006// dismiss inputs with text fields. flickity#403, flickity#404
3007proto.okayPointerDown = function( event ) {
3008 var isCursorNode = cursorNodes[ event.target.nodeName ];
3009 var isClickType = clickTypes[ event.target.type ];
3010 var isOkay = !isCursorNode || isClickType;
3011 if ( !isOkay ) {
3012 this._pointerReset();
3013 }
3014 return isOkay;
3015};
3016
3017// kludge to blur previously focused input
3018proto.pointerDownBlur = function() {
3019 var focused = document.activeElement;
3020 // do not blur body for IE10, metafizzy/flickity#117
3021 var canBlur = focused && focused.blur && focused != document.body;
3022 if ( canBlur ) {
3023 focused.blur();
3024 }
3025};
3026
3027// ----- move event ----- //
3028
3029/**
3030 * drag move
3031 * @param {Event} event
3032 * @param {Event or Touch} pointer
3033 */
3034proto.pointerMove = function( event, pointer ) {
3035 var moveVector = this._dragPointerMove( event, pointer );
3036 this.emitEvent( 'pointerMove', [ event, pointer, moveVector ] );
3037 this._dragMove( event, pointer, moveVector );
3038};
3039
3040// base pointer move logic
3041proto._dragPointerMove = function( event, pointer ) {
3042 var moveVector = {
3043 x: pointer.pageX - this.pointerDownPointer.pageX,
3044 y: pointer.pageY - this.pointerDownPointer.pageY
3045 };
3046 // start drag if pointer has moved far enough to start drag
3047 if ( !this.isDragging && this.hasDragStarted( moveVector ) ) {
3048 this._dragStart( event, pointer );
3049 }
3050 return moveVector;
3051};
3052
3053// condition if pointer has moved far enough to start drag
3054proto.hasDragStarted = function( moveVector ) {
3055 return Math.abs( moveVector.x ) > 3 || Math.abs( moveVector.y ) > 3;
3056};
3057
3058// ----- end event ----- //
3059
3060/**
3061 * pointer up
3062 * @param {Event} event
3063 * @param {Event or Touch} pointer
3064 */
3065proto.pointerUp = function( event, pointer ) {
3066 this.emitEvent( 'pointerUp', [ event, pointer ] );
3067 this._dragPointerUp( event, pointer );
3068};
3069
3070proto._dragPointerUp = function( event, pointer ) {
3071 if ( this.isDragging ) {
3072 this._dragEnd( event, pointer );
3073 } else {
3074 // pointer didn't move enough for drag to start
3075 this._staticClick( event, pointer );
3076 }
3077};
3078
3079// -------------------------- drag -------------------------- //
3080
3081// dragStart
3082proto._dragStart = function( event, pointer ) {
3083 this.isDragging = true;
3084 // prevent clicks
3085 this.isPreventingClicks = true;
3086 this.dragStart( event, pointer );
3087};
3088
3089proto.dragStart = function( event, pointer ) {
3090 this.emitEvent( 'dragStart', [ event, pointer ] );
3091};
3092
3093// dragMove
3094proto._dragMove = function( event, pointer, moveVector ) {
3095 // do not drag if not dragging yet
3096 if ( !this.isDragging ) {
3097 return;
3098 }
3099
3100 this.dragMove( event, pointer, moveVector );
3101};
3102
3103proto.dragMove = function( event, pointer, moveVector ) {
3104 event.preventDefault();
3105 this.emitEvent( 'dragMove', [ event, pointer, moveVector ] );
3106};
3107
3108// dragEnd
3109proto._dragEnd = function( event, pointer ) {
3110 // set flags
3111 this.isDragging = false;
3112 // re-enable clicking async
3113 setTimeout( function() {
3114 delete this.isPreventingClicks;
3115 }.bind( this ) );
3116
3117 this.dragEnd( event, pointer );
3118};
3119
3120proto.dragEnd = function( event, pointer ) {
3121 this.emitEvent( 'dragEnd', [ event, pointer ] );
3122};
3123
3124// ----- onclick ----- //
3125
3126// handle all clicks and prevent clicks when dragging
3127proto.onclick = function( event ) {
3128 if ( this.isPreventingClicks ) {
3129 event.preventDefault();
3130 }
3131};
3132
3133// ----- staticClick ----- //
3134
3135// triggered after pointer down & up with no/tiny movement
3136proto._staticClick = function( event, pointer ) {
3137 // ignore emulated mouse up clicks
3138 if ( this.isIgnoringMouseUp && event.type == 'mouseup' ) {
3139 return;
3140 }
3141
3142 this.staticClick( event, pointer );
3143
3144 // set flag for emulated clicks 300ms after touchend
3145 if ( event.type != 'mouseup' ) {
3146 this.isIgnoringMouseUp = true;
3147 // reset flag after 300ms
3148 setTimeout( function() {
3149 delete this.isIgnoringMouseUp;
3150 }.bind( this ), 400 );
3151 }
3152};
3153
3154proto.staticClick = function( event, pointer ) {
3155 this.emitEvent( 'staticClick', [ event, pointer ] );
3156};
3157
3158// ----- utils ----- //
3159
3160Unidragger.getPointerPoint = Unipointer.getPointerPoint;
3161
3162// ----- ----- //
3163
3164return Unidragger;
3165
3166}));
3167
3168// drag
3169( function( window, factory ) {
3170 // universal module definition
3171 if ( typeof define == 'function' && define.amd ) {
3172 // AMD
3173 define( 'flickity/js/drag',[
3174 './flickity',
3175 'unidragger/unidragger',
3176 'fizzy-ui-utils/utils',
3177 ], function( Flickity, Unidragger, utils ) {
3178 return factory( window, Flickity, Unidragger, utils );
3179 } );
3180 } else if ( typeof module == 'object' && module.exports ) {
3181 // CommonJS
3182 module.exports = factory(
3183 window,
3184 require('./flickity'),
3185 require('unidragger'),
3186 require('fizzy-ui-utils')
3187 );
3188 } else {
3189 // browser global
3190 window.Flickity = factory(
3191 window,
3192 window.Flickity,
3193 window.Unidragger,
3194 window.fizzyUIUtils
3195 );
3196 }
3197
3198}( window, function factory( window, Flickity, Unidragger, utils ) {
3199
3200
3201
3202// ----- defaults ----- //
3203
3204utils.extend( Flickity.defaults, {
3205 draggable: '>1',
3206 dragThreshold: 3,
3207} );
3208
3209// ----- create ----- //
3210
3211Flickity.createMethods.push('_createDrag');
3212
3213// -------------------------- drag prototype -------------------------- //
3214
3215var proto = Flickity.prototype;
3216utils.extend( proto, Unidragger.prototype );
3217proto._touchActionValue = 'pan-y';
3218
3219// -------------------------- -------------------------- //
3220
3221var isTouch = 'createTouch' in document;
3222var isTouchmoveScrollCanceled = false;
3223
3224proto._createDrag = function() {
3225 this.on( 'activate', this.onActivateDrag );
3226 this.on( 'uiChange', this._uiChangeDrag );
3227 this.on( 'deactivate', this.onDeactivateDrag );
3228 this.on( 'cellChange', this.updateDraggable );
3229 // TODO updateDraggable on resize? if groupCells & slides change
3230 // HACK - add seemingly innocuous handler to fix iOS 10 scroll behavior
3231 // #457, RubaXa/Sortable#973
3232 if ( isTouch && !isTouchmoveScrollCanceled ) {
3233 window.addEventListener( 'touchmove', function() {} );
3234 isTouchmoveScrollCanceled = true;
3235 }
3236};
3237
3238proto.onActivateDrag = function() {
3239 this.handles = [ this.viewport ];
3240 this.bindHandles();
3241 this.updateDraggable();
3242};
3243
3244proto.onDeactivateDrag = function() {
3245 this.unbindHandles();
3246 this.element.classList.remove('is-draggable');
3247};
3248
3249proto.updateDraggable = function() {
3250 // disable dragging if less than 2 slides. #278
3251 if ( this.options.draggable == '>1' ) {
3252 this.isDraggable = this.slides.length > 1;
3253 } else {
3254 this.isDraggable = this.options.draggable;
3255 }
3256 if ( this.isDraggable ) {
3257 this.element.classList.add('is-draggable');
3258 } else {
3259 this.element.classList.remove('is-draggable');
3260 }
3261};
3262
3263// backwards compatibility
3264proto.bindDrag = function() {
3265 this.options.draggable = true;
3266 this.updateDraggable();
3267};
3268
3269proto.unbindDrag = function() {
3270 this.options.draggable = false;
3271 this.updateDraggable();
3272};
3273
3274proto._uiChangeDrag = function() {
3275 delete this.isFreeScrolling;
3276};
3277
3278// -------------------------- pointer events -------------------------- //
3279
3280proto.pointerDown = function( event, pointer ) {
3281 if ( !this.isDraggable ) {
3282 this._pointerDownDefault( event, pointer );
3283 return;
3284 }
3285 var isOkay = this.okayPointerDown( event );
3286 if ( !isOkay ) {
3287 return;
3288 }
3289
3290 this._pointerDownPreventDefault( event );
3291 this.pointerDownFocus( event );
3292 // blur
3293 if ( document.activeElement != this.element ) {
3294 // do not blur if already focused
3295 this.pointerDownBlur();
3296 }
3297
3298 // stop if it was moving
3299 this.dragX = this.x;
3300 this.viewport.classList.add('is-pointer-down');
3301 // track scrolling
3302 this.pointerDownScroll = getScrollPosition();
3303 window.addEventListener( 'scroll', this );
3304
3305 this._pointerDownDefault( event, pointer );
3306};
3307
3308// default pointerDown logic, used for staticClick
3309proto._pointerDownDefault = function( event, pointer ) {
3310 // track start event position
3311 // Safari 9 overrides pageX and pageY. These values needs to be copied. #779
3312 this.pointerDownPointer = {
3313 pageX: pointer.pageX,
3314 pageY: pointer.pageY,
3315 };
3316 // bind move and end events
3317 this._bindPostStartEvents( event );
3318 this.dispatchEvent( 'pointerDown', event, [ pointer ] );
3319};
3320
3321var focusNodes = {
3322 INPUT: true,
3323 TEXTAREA: true,
3324 SELECT: true,
3325};
3326
3327proto.pointerDownFocus = function( event ) {
3328 var isFocusNode = focusNodes[ event.target.nodeName ];
3329 if ( !isFocusNode ) {
3330 this.focus();
3331 }
3332};
3333
3334proto._pointerDownPreventDefault = function( event ) {
3335 var isTouchStart = event.type == 'touchstart';
3336 var isTouchPointer = event.pointerType == 'touch';
3337 var isFocusNode = focusNodes[ event.target.nodeName ];
3338 if ( !isTouchStart && !isTouchPointer && !isFocusNode ) {
3339 event.preventDefault();
3340 }
3341};
3342
3343// ----- move ----- //
3344
3345proto.hasDragStarted = function( moveVector ) {
3346 return Math.abs( moveVector.x ) > this.options.dragThreshold;
3347};
3348
3349// ----- up ----- //
3350
3351proto.pointerUp = function( event, pointer ) {
3352 delete this.isTouchScrolling;
3353 this.viewport.classList.remove('is-pointer-down');
3354 this.dispatchEvent( 'pointerUp', event, [ pointer ] );
3355 this._dragPointerUp( event, pointer );
3356};
3357
3358proto.pointerDone = function() {
3359 window.removeEventListener( 'scroll', this );
3360 delete this.pointerDownScroll;
3361};
3362
3363// -------------------------- dragging -------------------------- //
3364
3365proto.dragStart = function( event, pointer ) {
3366 if ( !this.isDraggable ) {
3367 return;
3368 }
3369 this.dragStartPosition = this.x;
3370 this.startAnimation();
3371 window.removeEventListener( 'scroll', this );
3372 this.dispatchEvent( 'dragStart', event, [ pointer ] );
3373};
3374
3375proto.pointerMove = function( event, pointer ) {
3376 var moveVector = this._dragPointerMove( event, pointer );
3377 this.dispatchEvent( 'pointerMove', event, [ pointer, moveVector ] );
3378 this._dragMove( event, pointer, moveVector );
3379};
3380
3381proto.dragMove = function( event, pointer, moveVector ) {
3382 if ( !this.isDraggable ) {
3383 return;
3384 }
3385 event.preventDefault();
3386
3387 this.previousDragX = this.dragX;
3388 // reverse if right-to-left
3389 var direction = this.options.rightToLeft ? -1 : 1;
3390 if ( this.options.wrapAround ) {
3391 // wrap around move. #589
3392 moveVector.x %= this.slideableWidth;
3393 }
3394 var dragX = this.dragStartPosition + moveVector.x * direction;
3395
3396 if ( !this.options.wrapAround && this.slides.length ) {
3397 // slow drag
3398 var originBound = Math.max( -this.slides[0].target, this.dragStartPosition );
3399 dragX = dragX > originBound ? ( dragX + originBound ) * 0.5 : dragX;
3400 var endBound = Math.min( -this.getLastSlide().target, this.dragStartPosition );
3401 dragX = dragX < endBound ? ( dragX + endBound ) * 0.5 : dragX;
3402 }
3403
3404 this.dragX = dragX;
3405
3406 this.dragMoveTime = new Date();
3407 this.dispatchEvent( 'dragMove', event, [ pointer, moveVector ] );
3408};
3409
3410proto.dragEnd = function( event, pointer ) {
3411 if ( !this.isDraggable ) {
3412 return;
3413 }
3414 if ( this.options.freeScroll ) {
3415 this.isFreeScrolling = true;
3416 }
3417 // set selectedIndex based on where flick will end up
3418 var index = this.dragEndRestingSelect();
3419
3420 if ( this.options.freeScroll && !this.options.wrapAround ) {
3421 // if free-scroll & not wrap around
3422 // do not free-scroll if going outside of bounding slides
3423 // so bounding slides can attract slider, and keep it in bounds
3424 var restingX = this.getRestingPosition();
3425 this.isFreeScrolling = -restingX > this.slides[0].target &&
3426 -restingX < this.getLastSlide().target;
3427 } else if ( !this.options.freeScroll && index == this.selectedIndex ) {
3428 // boost selection if selected index has not changed
3429 index += this.dragEndBoostSelect();
3430 }
3431 delete this.previousDragX;
3432 // apply selection
3433 // TODO refactor this, selecting here feels weird
3434 // HACK, set flag so dragging stays in correct direction
3435 this.isDragSelect = this.options.wrapAround;
3436 this.select( index );
3437 delete this.isDragSelect;
3438 this.dispatchEvent( 'dragEnd', event, [ pointer ] );
3439};
3440
3441proto.dragEndRestingSelect = function() {
3442 var restingX = this.getRestingPosition();
3443 // how far away from selected slide
3444 var distance = Math.abs( this.getSlideDistance( -restingX, this.selectedIndex ) );
3445 // get closet resting going up and going down
3446 var positiveResting = this._getClosestResting( restingX, distance, 1 );
3447 var negativeResting = this._getClosestResting( restingX, distance, -1 );
3448 // use closer resting for wrap-around
3449 var index = positiveResting.distance < negativeResting.distance ?
3450 positiveResting.index : negativeResting.index;
3451 return index;
3452};
3453
3454/**
3455 * given resting X and distance to selected cell
3456 * get the distance and index of the closest cell
3457 * @param {Number} restingX - estimated post-flick resting position
3458 * @param {Number} distance - distance to selected cell
3459 * @param {Integer} increment - +1 or -1, going up or down
3460 * @returns {Object} - { distance: {Number}, index: {Integer} }
3461 */
3462proto._getClosestResting = function( restingX, distance, increment ) {
3463 var index = this.selectedIndex;
3464 var minDistance = Infinity;
3465 var condition = this.options.contain && !this.options.wrapAround ?
3466 // if contain, keep going if distance is equal to minDistance
3467 function( dist, minDist ) {
3468 return dist <= minDist;
3469 } : function( dist, minDist ) {
3470 return dist < minDist;
3471 };
3472 while ( condition( distance, minDistance ) ) {
3473 // measure distance to next cell
3474 index += increment;
3475 minDistance = distance;
3476 distance = this.getSlideDistance( -restingX, index );
3477 if ( distance === null ) {
3478 break;
3479 }
3480 distance = Math.abs( distance );
3481 }
3482 return {
3483 distance: minDistance,
3484 // selected was previous index
3485 index: index - increment,
3486 };
3487};
3488
3489/**
3490 * measure distance between x and a slide target
3491 * @param {Number} x - horizontal position
3492 * @param {Integer} index - slide index
3493 * @returns {Number} - slide distance
3494 */
3495proto.getSlideDistance = function( x, index ) {
3496 var len = this.slides.length;
3497 // wrap around if at least 2 slides
3498 var isWrapAround = this.options.wrapAround && len > 1;
3499 var slideIndex = isWrapAround ? utils.modulo( index, len ) : index;
3500 var slide = this.slides[ slideIndex ];
3501 if ( !slide ) {
3502 return null;
3503 }
3504 // add distance for wrap-around slides
3505 var wrap = isWrapAround ? this.slideableWidth * Math.floor( index/len ) : 0;
3506 return x - ( slide.target + wrap );
3507};
3508
3509proto.dragEndBoostSelect = function() {
3510 // do not boost if no previousDragX or dragMoveTime
3511 if ( this.previousDragX === undefined || !this.dragMoveTime ||
3512 // or if drag was held for 100 ms
3513 new Date() - this.dragMoveTime > 100 ) {
3514 return 0;
3515 }
3516
3517 var distance = this.getSlideDistance( -this.dragX, this.selectedIndex );
3518 var delta = this.previousDragX - this.dragX;
3519 if ( distance > 0 && delta > 0 ) {
3520 // boost to next if moving towards the right, and positive velocity
3521 return 1;
3522 } else if ( distance < 0 && delta < 0 ) {
3523 // boost to previous if moving towards the left, and negative velocity
3524 return -1;
3525 }
3526 return 0;
3527};
3528
3529// ----- staticClick ----- //
3530
3531proto.staticClick = function( event, pointer ) {
3532 // get clickedCell, if cell was clicked
3533 var clickedCell = this.getParentCell( event.target );
3534 var cellElem = clickedCell && clickedCell.element;
3535 var cellIndex = clickedCell && this.cells.indexOf( clickedCell );
3536 this.dispatchEvent( 'staticClick', event, [ pointer, cellElem, cellIndex ] );
3537};
3538
3539// ----- scroll ----- //
3540
3541proto.onscroll = function() {
3542 var scroll = getScrollPosition();
3543 var scrollMoveX = this.pointerDownScroll.x - scroll.x;
3544 var scrollMoveY = this.pointerDownScroll.y - scroll.y;
3545 // cancel click/tap if scroll is too much
3546 if ( Math.abs( scrollMoveX ) > 3 || Math.abs( scrollMoveY ) > 3 ) {
3547 this._pointerDone();
3548 }
3549};
3550
3551// ----- utils ----- //
3552
3553function getScrollPosition() {
3554 return {
3555 x: window.pageXOffset,
3556 y: window.pageYOffset,
3557 };
3558}
3559
3560// ----- ----- //
3561
3562return Flickity;
3563
3564} ) );
3565
3566// prev/next buttons
3567( function( window, factory ) {
3568 // universal module definition
3569 if ( typeof define == 'function' && define.amd ) {
3570 // AMD
3571 define( 'flickity/js/prev-next-button',[
3572 './flickity',
3573 'unipointer/unipointer',
3574 'fizzy-ui-utils/utils',
3575 ], function( Flickity, Unipointer, utils ) {
3576 return factory( window, Flickity, Unipointer, utils );
3577 } );
3578 } else if ( typeof module == 'object' && module.exports ) {
3579 // CommonJS
3580 module.exports = factory(
3581 window,
3582 require('./flickity'),
3583 require('unipointer'),
3584 require('fizzy-ui-utils')
3585 );
3586 } else {
3587 // browser global
3588 factory(
3589 window,
3590 window.Flickity,
3591 window.Unipointer,
3592 window.fizzyUIUtils
3593 );
3594 }
3595
3596}( window, function factory( window, Flickity, Unipointer, utils ) {
3597'use strict';
3598
3599var svgURI = 'http://www.w3.org/2000/svg';
3600
3601// -------------------------- PrevNextButton -------------------------- //
3602
3603function PrevNextButton( direction, parent ) {
3604 this.direction = direction;
3605 this.parent = parent;
3606 this._create();
3607}
3608
3609PrevNextButton.prototype = Object.create( Unipointer.prototype );
3610
3611PrevNextButton.prototype._create = function() {
3612 // properties
3613 this.isEnabled = true;
3614 this.isPrevious = this.direction == -1;
3615 var leftDirection = this.parent.options.rightToLeft ? 1 : -1;
3616 this.isLeft = this.direction == leftDirection;
3617
3618 var element = this.element = document.createElement('button');
3619 element.className = 'flickity-button flickity-prev-next-button';
3620 element.className += this.isPrevious ? ' previous' : ' next';
3621 // prevent button from submitting form http://stackoverflow.com/a/10836076/182183
3622 element.setAttribute( 'type', 'button' );
3623 // init as disabled
3624 this.disable();
3625
3626 element.setAttribute( 'aria-label', this.isPrevious ? 'Previous' : 'Next' );
3627
3628 // create arrow
3629 var svg = this.createSVG();
3630 element.appendChild( svg );
3631 // events
3632 this.parent.on( 'select', this.update.bind( this ) );
3633 this.on( 'pointerDown', this.parent.childUIPointerDown.bind( this.parent ) );
3634};
3635
3636PrevNextButton.prototype.activate = function() {
3637 this.bindStartEvent( this.element );
3638 this.element.addEventListener( 'click', this );
3639 // add to DOM
3640 this.parent.element.appendChild( this.element );
3641};
3642
3643PrevNextButton.prototype.deactivate = function() {
3644 // remove from DOM
3645 this.parent.element.removeChild( this.element );
3646 // click events
3647 this.unbindStartEvent( this.element );
3648 this.element.removeEventListener( 'click', this );
3649};
3650
3651PrevNextButton.prototype.createSVG = function() {
3652 var svg = document.createElementNS( svgURI, 'svg' );
3653 svg.setAttribute( 'class', 'flickity-button-icon' );
3654 svg.setAttribute( 'viewBox', '0 0 100 100' );
3655 var path = document.createElementNS( svgURI, 'path' );
3656 var pathMovements = getArrowMovements( this.parent.options.arrowShape );
3657 path.setAttribute( 'd', pathMovements );
3658 path.setAttribute( 'class', 'arrow' );
3659 // rotate arrow
3660 if ( !this.isLeft ) {
3661 path.setAttribute( 'transform', 'translate(100, 100) rotate(180) ' );
3662 }
3663 svg.appendChild( path );
3664 return svg;
3665};
3666
3667// get SVG path movmement
3668function getArrowMovements( shape ) {
3669 // use shape as movement if string
3670 if ( typeof shape == 'string' ) {
3671 return shape;
3672 }
3673 // create movement string
3674 return 'M ' + shape.x0 + ',50' +
3675 ' L ' + shape.x1 + ',' + ( shape.y1 + 50 ) +
3676 ' L ' + shape.x2 + ',' + ( shape.y2 + 50 ) +
3677 ' L ' + shape.x3 + ',50 ' +
3678 ' L ' + shape.x2 + ',' + ( 50 - shape.y2 ) +
3679 ' L ' + shape.x1 + ',' + ( 50 - shape.y1 ) +
3680 ' Z';
3681}
3682
3683PrevNextButton.prototype.handleEvent = utils.handleEvent;
3684
3685PrevNextButton.prototype.onclick = function() {
3686 if ( !this.isEnabled ) {
3687 return;
3688 }
3689 this.parent.uiChange();
3690 var method = this.isPrevious ? 'previous' : 'next';
3691 this.parent[ method ]();
3692};
3693
3694// ----- ----- //
3695
3696PrevNextButton.prototype.enable = function() {
3697 if ( this.isEnabled ) {
3698 return;
3699 }
3700 this.element.disabled = false;
3701 this.isEnabled = true;
3702};
3703
3704PrevNextButton.prototype.disable = function() {
3705 if ( !this.isEnabled ) {
3706 return;
3707 }
3708 this.element.disabled = true;
3709 this.isEnabled = false;
3710};
3711
3712PrevNextButton.prototype.update = function() {
3713 // index of first or last slide, if previous or next
3714 var slides = this.parent.slides;
3715 // enable is wrapAround and at least 2 slides
3716 if ( this.parent.options.wrapAround && slides.length > 1 ) {
3717 this.enable();
3718 return;
3719 }
3720 var lastIndex = slides.length ? slides.length - 1 : 0;
3721 var boundIndex = this.isPrevious ? 0 : lastIndex;
3722 var method = this.parent.selectedIndex == boundIndex ? 'disable' : 'enable';
3723 this[ method ]();
3724};
3725
3726PrevNextButton.prototype.destroy = function() {
3727 this.deactivate();
3728 this.allOff();
3729};
3730
3731// -------------------------- Flickity prototype -------------------------- //
3732
3733utils.extend( Flickity.defaults, {
3734 prevNextButtons: true,
3735 arrowShape: {
3736 x0: 10,
3737 x1: 60, y1: 50,
3738 x2: 70, y2: 40,
3739 x3: 30,
3740 },
3741} );
3742
3743Flickity.createMethods.push('_createPrevNextButtons');
3744var proto = Flickity.prototype;
3745
3746proto._createPrevNextButtons = function() {
3747 if ( !this.options.prevNextButtons ) {
3748 return;
3749 }
3750
3751 this.prevButton = new PrevNextButton( -1, this );
3752 this.nextButton = new PrevNextButton( 1, this );
3753
3754 this.on( 'activate', this.activatePrevNextButtons );
3755};
3756
3757proto.activatePrevNextButtons = function() {
3758 this.prevButton.activate();
3759 this.nextButton.activate();
3760 this.on( 'deactivate', this.deactivatePrevNextButtons );
3761};
3762
3763proto.deactivatePrevNextButtons = function() {
3764 this.prevButton.deactivate();
3765 this.nextButton.deactivate();
3766 this.off( 'deactivate
The following has evaluated to null or missing: ==> urlimagen [in template "20102#20129#12795723" at line 58, column 26] ---- Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)?? ---- ---- FTL stack trace ("~" means nesting-related): - Failed at: #if urlimagen.url?has_content [in template "20102#20129#12795723" at line 58, column 21] ----
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 urlimagenNode = docXml.valueOf("//dynamic-element[@name='IMAGEN']/dynamic-content")/>
24
25
26
27 <#assign titulo = docXml.valueOf("//dynamic-element[@name='Titulopub']/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 <#if urlimagenNode?has_content>
51 <#assign urlimagenString = urlimagenNode?string />
52 <#assign urlimagen = urlimagenString?eval />
53 </#if>
54
55 <div class="gallery-cell${nameInstancePublisher}">
56 <div class="card-info${nameInstancePublisher}" id="card-info">
57 <div class="card-img${nameInstancePublisher}" id="card-img">
58 <#if urlimagen.url?has_content>
59 <img alt="${urlimagen.alt}" src='${urlimagen.url}' title="${titulo}">
60 <#else>
61 <img alt='${urlimagen["alt"]}' src='/documents/${urlimagen["groupId"]}/${urlimagen["classPK"]}/${urlimagen["name"]}/${urlimagen["uuid"]}' title="${titulo}">
62 </#if>
63 <#if assetRenderer.hasEditPermission(themeDisplay.getPermissionChecker())>
64 <#assign editPortletURL = assetRenderer.getURLEdit(renderRequest, renderResponse, windowStateFactory.getWindowState("NORMAL"), themeDisplay.getURLCurrent())!"" />
65 <#if validator.isNotNull(editPortletURL)>
66 <a class="editOptionmas" href="${editPortletURL.toString()}">Editar ✐</a>
67 </#if>
68 </#if>
69 </div>
70 <div class="card-txt${nameInstancePublisher}" id="card-txt">
71 <div class="card-title${nameInstancePublisher}" id="card-title">${titulo}</div>
72 <div class="card-lead${nameInstancePublisher}" id="card-lead">${contenido}</div>
73 <div class="card-btn${nameInstancePublisher}" id="card-btn">
74
75 <a class="link${nameInstancePublisher}" href="${linkext}" target="_blank">
76 Ver más
77 </a>
78
79 </div>
80 </div>
81
82 </div>
83 </div>
84 </#list>
85</div>
86
87
88
89
90
91<!---------------------- ESTLOS BASICOS ------------------------>
92
93 <style>
94
95 .cards-cont${nameInstancePublisher} {
96 width: 100%;
97 display: flex;
98 justify-content: center;
99 flex-wrap: wrap;
100
101 }
102
103
104 .card-info${nameInstancePublisher} {
105
106 width: 250px;
107 height: 350px;
108 margin: 15px;
109 display:flex;
110 align-content:center;
111 align-items:center;
112 flex-wrap:wrap;
113 }
114
115 .card-img${nameInstancePublisher} {
116 width: 250px;
117 height: 350px;
118 border-radius: 10px;
119 -webkit-box-shadow: 0px 2px 17px 1px rgb(0 0 0 / 23%);
120 box-shadow: 0px 2px 17px 1px rgb(0 0 0 / 23%);
121 box-sizing: border-box;
122 background-position: center;
123 background-size: cover;
124 -webkit-transition: all 500ms ease-in-out; // IE 9
125 -moz-transition: all 500ms ease-in-out; // Firefox
126 -ms-transition: all 500ms ease-in-out; // Safari and Chrome
127 -o-transition: all 500ms ease-in-out; // Opera
128 transition: all 500ms ease-in-out;
129 position:relative;
130 display:flex;
131 }
132
133 .card-info${nameInstancePublisher}:hover .card-img${nameInstancePublisher} {
134 width: 250px;
135 height: 350px;
136 border-radius: 10px;
137 box-sizing: border-box;
138 background-position: center;
139 background-size: cover;
140 -moz-transform: scale(1.02);
141 -webkit-transform: scale(1.02);
142 -o-transform: scale(1.02);
143 -ms-transform: scale(1.02);
144 transform: scale(1.02);
145 }
146
147 .card-img${nameInstancePublisher} img{
148 vertical-align: middle;
149 border-style: none;
150 width: 100%;
151 height: 350px;
152 object-fit: cover;
153 object-position: center;
154 border-radius: 10px;
155 }
156
157 .card-txt${nameInstancePublisher} {
158 position: absolute;
159 display:flex;
160 padding: 20px;
161 color: #FFF;
162 opacity: 1;
163 transition: all 500ms ease-in-out;
164 width: 90%;
165 flex-wrap:wrap;
166 max-width: 250px;
167 }
168 #card-txt{
169 display:none;
170 opacity:0;
171 transition: all 1s;
172 -webkit-transition: all 1s;
173 }
174
175 #card-info:hover #card-txt{
176 display:flex;
177 opacity:1;
178 transition: all 1s;
179 -webkit-transition: all 1s;
180 }
181
182 #card-info:hover #card-img{filter: brightness(0.3);}
183
184 .card-title${nameInstancePublisher} {
185 font-family: HelveticaBold;
186 font-size: 1rem;
187 transition: all 500ms ease-in-out;
188 width:100%;
189 }
190
191 .card-lead${nameInstancePublisher} {
192 font-family: HelveticaLight;
193 font-size: 0.9rem;
194 transition: all 500ms ease-in-out;
195 width: 100%;
196 }
197
198 .card-btn${nameInstancePublisher} {
199 padding: 5px;
200 font-family: HelveticaLight;
201 border: 1px solid #fff;
202 width: auto;
203 font-size: 0.8rem;
204 margin-top: 10px;
205 display: flex;
206 border-radius: 10px;
207 text-align: center;
208 /* margin: 0 auto; */
209 /* align-content: center; */
210 justify-content: center;
211 cursor: pointer;
212 opacity: 1;
213 }
214
215 .card-info${nameInstancePublisher}:hover .card-btn${nameInstancePublisher}{
216 background-color:#FFF;
217 color:#494949;
218 width: auto;
219 padding: 5px 10px;
220 border-radius: 5px;
221 }
222
223 .card-btn${nameInstancePublisher} a{
224 color:#FFF;
225 text-decoration:none;
226 }
227
228 .card-info${nameInstancePublisher}:hover .card-btn${nameInstancePublisher} a{
229 color:#494949;
230
231 }
232
233
234 .editOptionmas{
235 position: absolute;
236 background-color: #173268;
237 padding: 2px 5px;
238 color: #FFF;
239 font-family:HelveticaLight;
240 display:flex;
241 flex-wrap:nowrap;
242 width: 85px;
243 top:0px;
244 justify-content: center;
245 border-radius:10px 0px 10px 0px;
246 }
247
248 .editOptionmas:hover{
249 text-decoration:none;
250 color:#FFF;
251 font-family:HelveticaBold;
252 }
253
254 </style>
255
256 <style>
257 .gallery${nameInstancePublisher} {
258
259 margin: 0 auto;
260 width: 97%;
261 max-width:1200px;
262 margin-bottom:30px;
263 height:420px;
264 }
265
266 .gallery-cell${nameInstancePublisher} {
267 width: 22%;
268 height: auto;
269 margin-right: 10px;
270 counter-increment: gallery-cell;
271 }
272 /* cell number */
273
274 .gallery-cell${nameInstancePublisher}:before {
275 display: block;
276 text-align: center;
277 /content: counter(gallery-cell);/
278 line-height: 200px;
279 font-size: 80px;
280 color: white;
281 }
282
283 @media screen and (max-width: 640px) {
284 .gallery-cell${nameInstancePublisher} {
285 width: 100%;
286 height: auto;
287 margin-right: 10px;
288 counter-increment: gallery-cell;
289 }
290 }
291
292
293 .flickity-page-dots .dot.is-selected {
294 opacity: 1;
295 background: #2c5697;
296 }
297
298 .flickity-page-dots .dot {
299 display: inline-block;
300 width: 8px!important;
301 height: 8px!important;
302 margin: 0 5px!important;
303 background: #494949;
304 border-radius: 50%;
305 opacity: 0.25;
306 cursor: pointer;
307 }
308
309 .flickity-page-dots {
310 position: absolute;
311 width: 100%;
312 bottom: 0px!important;
313 padding: 0;
314 margin: 0;
315 list-style: none;
316 text-align: center;
317 line-height: 1;
318 margin-bottom: 0px;
319 }
320
321 .flickity-prev-next-button.previous {
322 left: -50px!important;
323 }
324
325 .flickity-prev-next-button.next {
326 right: -50px!important;
327 }
328
329 @media screen and (max-width: 640px) {
330 .flickity-prev-next-button.previous {
331 left: 10px!important;
332 }
333
334 .flickity-prev-next-button.next {
335 right: 10px!important;
336 }
337
338 .flickity-prev-next-button {
339 top: 25%!important;
340 width: 44px;
341 height: 44px;
342 border-radius: 50%;
343 transform: translateY(-50%);
344 }
345}
346
347
348 </style>
349
350<!------------------------------------------------------- ESTILOS DEL SCRIPT ---------------------------------------------->
351
352
353<style>
354
355.flickity-enabled {
356 position: relative;
357}
358
359.flickity-enabled:focus { outline: none; }
360
361.flickity-viewport {
362 overflow: hidden;
363 position: relative;
364 height: 100%;
365}
366
367.flickity-slider {
368 position: absolute;
369 width: 100%;
370 height: 100%;
371}
372
373/* draggable */
374
375.flickity-enabled.is-draggable {
376 -webkit-tap-highlight-color: transparent;
377 -webkit-user-select: none;
378 -moz-user-select: none;
379 -ms-user-select: none;
380 user-select: none;
381}
382
383.flickity-enabled.is-draggable .flickity-viewport {
384 cursor: move;
385 cursor: -webkit-grab;
386 cursor: grab;
387}
388
389.flickity-enabled.is-draggable .flickity-viewport.is-pointer-down {
390 cursor: -webkit-grabbing;
391 cursor: grabbing;
392}
393
394/* ---- flickity-button ---- */
395
396.flickity-button {
397 position: absolute;
398 background: hsla(0, 0%, 100%, 0.75);
399 border: none;
400 color: #333;
401}
402
403.flickity-button:hover {
404 background: white;
405 cursor: pointer;
406}
407
408.flickity-button:focus {
409 outline: none;
410 box-shadow: 0 0 0 5px #19F;
411}
412
413.flickity-button:active {
414 opacity: 0.6;
415}
416
417.flickity-button:disabled {
418 opacity: 0.3;
419 cursor: auto;
420 /* prevent disabled button from capturing pointer up event. #716 */
421 pointer-events: none;
422}
423
424.flickity-button-icon {
425 fill: currentColor;
426}
427
428/* ---- previous/next buttons ---- */
429
430.flickity-prev-next-button {
431 top: 50%;
432 width: 44px;
433 height: 44px;
434 border-radius: 50%;
435 /* vertically center */
436 transform: translateY(-50%);
437}
438
439.flickity-prev-next-button.previous { left: 10px; }
440.flickity-prev-next-button.next { right: 10px; }
441/* right to left */
442.flickity-rtl .flickity-prev-next-button.previous {
443 left: auto;
444 right: 10px;
445}
446.flickity-rtl .flickity-prev-next-button.next {
447 right: auto;
448 left: 10px;
449}
450
451.flickity-prev-next-button .flickity-button-icon {
452 position: absolute;
453 left: 20%;
454 top: 20%;
455 width: 60%;
456 height: 60%;
457}
458
459/* ---- page dots ---- */
460
461.flickity-page-dots {
462 position: absolute;
463 width: 100%;
464 bottom: -25px;
465 padding: 0;
466 margin: 0;
467 list-style: none;
468 text-align: center;
469 line-height: 1;
470}
471
472.flickity-rtl .flickity-page-dots { direction: rtl; }
473
474.flickity-page-dots .dot {
475 display: inline-block;
476 width: 10px;
477 height: 10px;
478 margin: 0 8px;
479 background: #333;
480 border-radius: 50%;
481 opacity: 0.25;
482 cursor: pointer;
483}
484
485.flickity-page-dots .dot.is-selected {
486 opacity: 1;
487}
488 </style>
489
490
491<!------------------------------------------------------------- SCRIPT PRINCIPAL -------------------------------------------------------------------------->
492
493<script>
494 /*!
495 * Flickity PACKAGED v2.2.2
496 * Touch, responsive, flickable carousels
497 *
498 * Licensed GPLv3 for open source use
499 * or Flickity Commercial License for commercial use
500 *
501 * https://flickity.metafizzy.co
502 * Copyright 2015-2021 Metafizzy
503 */
504
505/**
506 * Bridget makes jQuery widgets
507 * v2.0.1
508 * MIT license
509 */
510
511/* jshint browser: true, strict: true, undef: true, unused: true */
512
513( function( window, factory ) {
514 // universal module definition
515 /*jshint strict: false */ /* globals define, module, require */
516 if ( typeof define == 'function' && define.amd ) {
517 // AMD
518 define( 'jquery-bridget/jquery-bridget',[ 'jquery' ], function( jQuery ) {
519 return factory( window, jQuery );
520 });
521 } else if ( typeof module == 'object' && module.exports ) {
522 // CommonJS
523 module.exports = factory(
524 window,
525 require('jquery')
526 );
527 } else {
528 // browser global
529 window.jQueryBridget = factory(
530 window,
531 window.jQuery
532 );
533 }
534
535}( window, function factory( window, jQuery ) {
536'use strict';
537
538// ----- utils ----- //
539
540var arraySlice = Array.prototype.slice;
541
542// helper function for logging errors
543// $.error breaks jQuery chaining
544var console = window.console;
545var logError = typeof console == 'undefined' ? function() {} :
546 function( message ) {
547 console.error( message );
548 };
549
550// ----- jQueryBridget ----- //
551
552function jQueryBridget( namespace, PluginClass, $ ) {
553 $ = $ || jQuery || window.jQuery;
554 if ( !$ ) {
555 return;
556 }
557
558 // add option method -> $().plugin('option', {...})
559 if ( !PluginClass.prototype.option ) {
560 // option setter
561 PluginClass.prototype.option = function( opts ) {
562 // bail out if not an object
563 if ( !$.isPlainObject( opts ) ){
564 return;
565 }
566 this.options = $.extend( true, this.options, opts );
567 };
568 }
569
570 // make jQuery plugin
571 $.fn[ namespace ] = function( arg0 /*, arg1 */ ) {
572 if ( typeof arg0 == 'string' ) {
573 // method call $().plugin( 'methodName', { options } )
574 // shift arguments by 1
575 var args = arraySlice.call( arguments, 1 );
576 return methodCall( this, arg0, args );
577 }
578 // just $().plugin({ options })
579 plainCall( this, arg0 );
580 return this;
581 };
582
583 // $().plugin('methodName')
584 function methodCall( $elems, methodName, args ) {
585 var returnValue;
586 var pluginMethodStr = '$().' + namespace + '("' + methodName + '")';
587
588 $elems.each( function( i, elem ) {
589 // get instance
590 var instance = $.data( elem, namespace );
591 if ( !instance ) {
592 logError( namespace + ' not initialized. Cannot call methods, i.e. ' +
593 pluginMethodStr );
594 return;
595 }
596
597 var method = instance[ methodName ];
598 if ( !method || methodName.charAt(0) == '_' ) {
599 logError( pluginMethodStr + ' is not a valid method' );
600 return;
601 }
602
603 // apply method, get return value
604 var value = method.apply( instance, args );
605 // set return value if value is returned, use only first value
606 returnValue = returnValue === undefined ? value : returnValue;
607 });
608
609 return returnValue !== undefined ? returnValue : $elems;
610 }
611
612 function plainCall( $elems, options ) {
613 $elems.each( function( i, elem ) {
614 var instance = $.data( elem, namespace );
615 if ( instance ) {
616 // set options & init
617 instance.option( options );
618 instance._init();
619 } else {
620 // initialize new instance
621 instance = new PluginClass( elem, options );
622 $.data( elem, namespace, instance );
623 }
624 });
625 }
626
627 updateJQuery( $ );
628
629}
630
631// ----- updateJQuery ----- //
632
633// set $.bridget for v1 backwards compatibility
634function updateJQuery( $ ) {
635 if ( !$ || ( $ && $.bridget ) ) {
636 return;
637 }
638 $.bridget = jQueryBridget;
639}
640
641updateJQuery( jQuery || window.jQuery );
642
643// ----- ----- //
644
645return jQueryBridget;
646
647}));
648
649/**
650 * EvEmitter v1.1.0
651 * Lil' event emitter
652 * MIT License
653 */
654
655/* jshint unused: true, undef: true, strict: true */
656
657( function( global, factory ) {
658 // universal module definition
659 /* jshint strict: false */ /* globals define, module, window */
660 if ( typeof define == 'function' && define.amd ) {
661 // AMD - RequireJS
662 define( 'ev-emitter/ev-emitter',factory );
663 } else if ( typeof module == 'object' && module.exports ) {
664 // CommonJS - Browserify, Webpack
665 module.exports = factory();
666 } else {
667 // Browser globals
668 global.EvEmitter = factory();
669 }
670
671}( typeof window != 'undefined' ? window : this, function() {
672
673
674
675function EvEmitter() {}
676
677var proto = EvEmitter.prototype;
678
679proto.on = function( eventName, listener ) {
680 if ( !eventName || !listener ) {
681 return;
682 }
683 // set events hash
684 var events = this._events = this._events || {};
685 // set listeners array
686 var listeners = events[ eventName ] = events[ eventName ] || [];
687 // only add once
688 if ( listeners.indexOf( listener ) == -1 ) {
689 listeners.push( listener );
690 }
691
692 return this;
693};
694
695proto.once = function( eventName, listener ) {
696 if ( !eventName || !listener ) {
697 return;
698 }
699 // add event
700 this.on( eventName, listener );
701 // set once flag
702 // set onceEvents hash
703 var onceEvents = this._onceEvents = this._onceEvents || {};
704 // set onceListeners object
705 var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {};
706 // set flag
707 onceListeners[ listener ] = true;
708
709 return this;
710};
711
712proto.off = function( eventName, listener ) {
713 var listeners = this._events && this._events[ eventName ];
714 if ( !listeners || !listeners.length ) {
715 return;
716 }
717 var index = listeners.indexOf( listener );
718 if ( index != -1 ) {
719 listeners.splice( index, 1 );
720 }
721
722 return this;
723};
724
725proto.emitEvent = function( eventName, args ) {
726 var listeners = this._events && this._events[ eventName ];
727 if ( !listeners || !listeners.length ) {
728 return;
729 }
730 // copy over to avoid interference if .off() in listener
731 listeners = listeners.slice(0);
732 args = args || [];
733 // once stuff
734 var onceListeners = this._onceEvents && this._onceEvents[ eventName ];
735
736 for ( var i=0; i < listeners.length; i++ ) {
737 var listener = listeners[i]
738 var isOnce = onceListeners && onceListeners[ listener ];
739 if ( isOnce ) {
740 // remove listener
741 // remove before trigger to prevent recursion
742 this.off( eventName, listener );
743 // unset once flag
744 delete onceListeners[ listener ];
745 }
746 // trigger listener
747 listener.apply( this, args );
748 }
749
750 return this;
751};
752
753proto.allOff = function() {
754 delete this._events;
755 delete this._onceEvents;
756};
757
758return EvEmitter;
759
760}));
761
762/*!
763 * getSize v2.0.3
764 * measure size of elements
765 * MIT license
766 */
767
768/* jshint browser: true, strict: true, undef: true, unused: true */
769/* globals console: false */
770
771( function( window, factory ) {
772 /* jshint strict: false */ /* globals define, module */
773 if ( typeof define == 'function' && define.amd ) {
774 // AMD
775 define( 'get-size/get-size',factory );
776 } else if ( typeof module == 'object' && module.exports ) {
777 // CommonJS
778 module.exports = factory();
779 } else {
780 // browser global
781 window.getSize = factory();
782 }
783
784})( window, function factory() {
785'use strict';
786
787// -------------------------- helpers -------------------------- //
788
789// get a number from a string, not a percentage
790function getStyleSize( value ) {
791 var num = parseFloat( value );
792 // not a percent like '100%', and a number
793 var isValid = value.indexOf('%') == -1 && !isNaN( num );
794 return isValid && num;
795}
796
797function noop() {}
798
799var logError = typeof console == 'undefined' ? noop :
800 function( message ) {
801 console.error( message );
802 };
803
804// -------------------------- measurements -------------------------- //
805
806var measurements = [
807 'paddingLeft',
808 'paddingRight',
809 'paddingTop',
810 'paddingBottom',
811 'marginLeft',
812 'marginRight',
813 'marginTop',
814 'marginBottom',
815 'borderLeftWidth',
816 'borderRightWidth',
817 'borderTopWidth',
818 'borderBottomWidth'
819];
820
821var measurementsLength = measurements.length;
822
823function getZeroSize() {
824 var size = {
825 width: 0,
826 height: 0,
827 innerWidth: 0,
828 innerHeight: 0,
829 outerWidth: 0,
830 outerHeight: 0
831 };
832 for ( var i=0; i < measurementsLength; i++ ) {
833 var measurement = measurements[i];
834 size[ measurement ] = 0;
835 }
836 return size;
837}
838
839// -------------------------- getStyle -------------------------- //
840
841/**
842 * getStyle, get style of element, check for Firefox bug
843 * https://bugzilla.mozilla.org/show_bug.cgi?id=548397
844 */
845function getStyle( elem ) {
846 var style = getComputedStyle( elem );
847 if ( !style ) {
848 logError( 'Style returned ' + style +
849 '. Are you running this code in a hidden iframe on Firefox? ' +
850 'See https://bit.ly/getsizebug1' );
851 }
852 return style;
853}
854
855// -------------------------- setup -------------------------- //
856
857var isSetup = false;
858
859var isBoxSizeOuter;
860
861/**
862 * setup
863 * check isBoxSizerOuter
864 * do on first getSize() rather than on page load for Firefox bug
865 */
866function setup() {
867 // setup once
868 if ( isSetup ) {
869 return;
870 }
871 isSetup = true;
872
873 // -------------------------- box sizing -------------------------- //
874
875 /**
876 * Chrome & Safari measure the outer-width on style.width on border-box elems
877 * IE11 & Firefox<29 measures the inner-width
878 */
879 var div = document.createElement('div');
880 div.style.width = '200px';
881 div.style.padding = '1px 2px 3px 4px';
882 div.style.borderStyle = 'solid';
883 div.style.borderWidth = '1px 2px 3px 4px';
884 div.style.boxSizing = 'border-box';
885
886 var body = document.body || document.documentElement;
887 body.appendChild( div );
888 var style = getStyle( div );
889 // round value for browser zoom. desandro/masonry#928
890 isBoxSizeOuter = Math.round( getStyleSize( style.width ) ) == 200;
891 getSize.isBoxSizeOuter = isBoxSizeOuter;
892
893 body.removeChild( div );
894}
895
896// -------------------------- getSize -------------------------- //
897
898function getSize( elem ) {
899 setup();
900
901 // use querySeletor if elem is string
902 if ( typeof elem == 'string' ) {
903 elem = document.querySelector( elem );
904 }
905
906 // do not proceed on non-objects
907 if ( !elem || typeof elem != 'object' || !elem.nodeType ) {
908 return;
909 }
910
911 var style = getStyle( elem );
912
913 // if hidden, everything is 0
914 if ( style.display == 'none' ) {
915 return getZeroSize();
916 }
917
918 var size = {};
919 size.width = elem.offsetWidth;
920 size.height = elem.offsetHeight;
921
922 var isBorderBox = size.isBorderBox = style.boxSizing == 'border-box';
923
924 // get all measurements
925 for ( var i=0; i < measurementsLength; i++ ) {
926 var measurement = measurements[i];
927 var value = style[ measurement ];
928 var num = parseFloat( value );
929 // any 'auto', 'medium' value will be 0
930 size[ measurement ] = !isNaN( num ) ? num : 0;
931 }
932
933 var paddingWidth = size.paddingLeft + size.paddingRight;
934 var paddingHeight = size.paddingTop + size.paddingBottom;
935 var marginWidth = size.marginLeft + size.marginRight;
936 var marginHeight = size.marginTop + size.marginBottom;
937 var borderWidth = size.borderLeftWidth + size.borderRightWidth;
938 var borderHeight = size.borderTopWidth + size.borderBottomWidth;
939
940 var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter;
941
942 // overwrite width and height if we can get it from style
943 var styleWidth = getStyleSize( style.width );
944 if ( styleWidth !== false ) {
945 size.width = styleWidth +
946 // add padding and border unless it's already including it
947 ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth );
948 }
949
950 var styleHeight = getStyleSize( style.height );
951 if ( styleHeight !== false ) {
952 size.height = styleHeight +
953 // add padding and border unless it's already including it
954 ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight );
955 }
956
957 size.innerWidth = size.width - ( paddingWidth + borderWidth );
958 size.innerHeight = size.height - ( paddingHeight + borderHeight );
959
960 size.outerWidth = size.width + marginWidth;
961 size.outerHeight = size.height + marginHeight;
962
963 return size;
964}
965
966return getSize;
967
968});
969
970/**
971 * matchesSelector v2.0.2
972 * matchesSelector( element, '.selector' )
973 * MIT license
974 */
975
976/*jshint browser: true, strict: true, undef: true, unused: true */
977
978( function( window, factory ) {
979 /*global define: false, module: false */
980 'use strict';
981 // universal module definition
982 if ( typeof define == 'function' && define.amd ) {
983 // AMD
984 define( 'desandro-matches-selector/matches-selector',factory );
985 } else if ( typeof module == 'object' && module.exports ) {
986 // CommonJS
987 module.exports = factory();
988 } else {
989 // browser global
990 window.matchesSelector = factory();
991 }
992
993}( window, function factory() {
994 'use strict';
995
996 var matchesMethod = ( function() {
997 var ElemProto = window.Element.prototype;
998 // check for the standard method name first
999 if ( ElemProto.matches ) {
1000 return 'matches';
1001 }
1002 // check un-prefixed
1003 if ( ElemProto.matchesSelector ) {
1004 return 'matchesSelector';
1005 }
1006 // check vendor prefixes
1007 var prefixes = [ 'webkit', 'moz', 'ms', 'o' ];
1008
1009 for ( var i=0; i < prefixes.length; i++ ) {
1010 var prefix = prefixes[i];
1011 var method = prefix + 'MatchesSelector';
1012 if ( ElemProto[ method ] ) {
1013 return method;
1014 }
1015 }
1016 })();
1017
1018 return function matchesSelector( elem, selector ) {
1019 return elem[ matchesMethod ]( selector );
1020 };
1021
1022}));
1023
1024/**
1025 * Fizzy UI utils v2.0.7
1026 * MIT license
1027 */
1028
1029/*jshint browser: true, undef: true, unused: true, strict: true */
1030
1031( function( window, factory ) {
1032 // universal module definition
1033 /*jshint strict: false */ /*globals define, module, require */
1034
1035 if ( typeof define == 'function' && define.amd ) {
1036 // AMD
1037 define( 'fizzy-ui-utils/utils',[
1038 'desandro-matches-selector/matches-selector'
1039 ], function( matchesSelector ) {
1040 return factory( window, matchesSelector );
1041 });
1042 } else if ( typeof module == 'object' && module.exports ) {
1043 // CommonJS
1044 module.exports = factory(
1045 window,
1046 require('desandro-matches-selector')
1047 );
1048 } else {
1049 // browser global
1050 window.fizzyUIUtils = factory(
1051 window,
1052 window.matchesSelector
1053 );
1054 }
1055
1056}( window, function factory( window, matchesSelector ) {
1057
1058
1059
1060var utils = {};
1061
1062// ----- extend ----- //
1063
1064// extends objects
1065utils.extend = function( a, b ) {
1066 for ( var prop in b ) {
1067 a[ prop ] = b[ prop ];
1068 }
1069 return a;
1070};
1071
1072// ----- modulo ----- //
1073
1074utils.modulo = function( num, div ) {
1075 return ( ( num % div ) + div ) % div;
1076};
1077
1078// ----- makeArray ----- //
1079
1080var arraySlice = Array.prototype.slice;
1081
1082// turn element or nodeList into an array
1083utils.makeArray = function( obj ) {
1084 if ( Array.isArray( obj ) ) {
1085 // use object if already an array
1086 return obj;
1087 }
1088 // return empty array if undefined or null. #6
1089 if ( obj === null || obj === undefined ) {
1090 return [];
1091 }
1092
1093 var isArrayLike = typeof obj == 'object' && typeof obj.length == 'number';
1094 if ( isArrayLike ) {
1095 // convert nodeList to array
1096 return arraySlice.call( obj );
1097 }
1098
1099 // array of single index
1100 return [ obj ];
1101};
1102
1103// ----- removeFrom ----- //
1104
1105utils.removeFrom = function( ary, obj ) {
1106 var index = ary.indexOf( obj );
1107 if ( index != -1 ) {
1108 ary.splice( index, 1 );
1109 }
1110};
1111
1112// ----- getParent ----- //
1113
1114utils.getParent = function( elem, selector ) {
1115 while ( elem.parentNode && elem != document.body ) {
1116 elem = elem.parentNode;
1117 if ( matchesSelector( elem, selector ) ) {
1118 return elem;
1119 }
1120 }
1121};
1122
1123// ----- getQueryElement ----- //
1124
1125// use element as selector string
1126utils.getQueryElement = function( elem ) {
1127 if ( typeof elem == 'string' ) {
1128 return document.querySelector( elem );
1129 }
1130 return elem;
1131};
1132
1133// ----- handleEvent ----- //
1134
1135// enable .ontype to trigger from .addEventListener( elem, 'type' )
1136utils.handleEvent = function( event ) {
1137 var method = 'on' + event.type;
1138 if ( this[ method ] ) {
1139 this[ method ]( event );
1140 }
1141};
1142
1143// ----- filterFindElements ----- //
1144
1145utils.filterFindElements = function( elems, selector ) {
1146 // make array of elems
1147 elems = utils.makeArray( elems );
1148 var ffElems = [];
1149
1150 elems.forEach( function( elem ) {
1151 // check that elem is an actual element
1152 if ( !( elem instanceof HTMLElement ) ) {
1153 return;
1154 }
1155 // add elem if no selector
1156 if ( !selector ) {
1157 ffElems.push( elem );
1158 return;
1159 }
1160 // filter & find items if we have a selector
1161 // filter
1162 if ( matchesSelector( elem, selector ) ) {
1163 ffElems.push( elem );
1164 }
1165 // find children
1166 var childElems = elem.querySelectorAll( selector );
1167 // concat childElems to filterFound array
1168 for ( var i=0; i < childElems.length; i++ ) {
1169 ffElems.push( childElems[i] );
1170 }
1171 });
1172
1173 return ffElems;
1174};
1175
1176// ----- debounceMethod ----- //
1177
1178utils.debounceMethod = function( _class, methodName, threshold ) {
1179 threshold = threshold || 100;
1180 // original method
1181 var method = _class.prototype[ methodName ];
1182 var timeoutName = methodName + 'Timeout';
1183
1184 _class.prototype[ methodName ] = function() {
1185 var timeout = this[ timeoutName ];
1186 clearTimeout( timeout );
1187
1188 var args = arguments;
1189 var _this = this;
1190 this[ timeoutName ] = setTimeout( function() {
1191 method.apply( _this, args );
1192 delete _this[ timeoutName ];
1193 }, threshold );
1194 };
1195};
1196
1197// ----- docReady ----- //
1198
1199utils.docReady = function( callback ) {
1200 var readyState = document.readyState;
1201 if ( readyState == 'complete' || readyState == 'interactive' ) {
1202 // do async to allow for other scripts to run. metafizzy/flickity#441
1203 setTimeout( callback );
1204 } else {
1205 document.addEventListener( 'DOMContentLoaded', callback );
1206 }
1207};
1208
1209// ----- htmlInit ----- //
1210
1211// http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
1212utils.toDashed = function( str ) {
1213 return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
1214 return $1 + '-' + $2;
1215 }).toLowerCase();
1216};
1217
1218var console = window.console;
1219/**
1220 * allow user to initialize classes via [data-namespace] or .js-namespace class
1221 * htmlInit( Widget, 'widgetName' )
1222 * options are parsed from data-namespace-options
1223 */
1224utils.htmlInit = function( WidgetClass, namespace ) {
1225 utils.docReady( function() {
1226 var dashedNamespace = utils.toDashed( namespace );
1227 var dataAttr = 'data-' + dashedNamespace;
1228 var dataAttrElems = document.querySelectorAll( '[' + dataAttr + ']' );
1229 var jsDashElems = document.querySelectorAll( '.js-' + dashedNamespace );
1230 var elems = utils.makeArray( dataAttrElems )
1231 .concat( utils.makeArray( jsDashElems ) );
1232 var dataOptionsAttr = dataAttr + '-options';
1233 var jQuery = window.jQuery;
1234
1235 elems.forEach( function( elem ) {
1236 var attr = elem.getAttribute( dataAttr ) ||
1237 elem.getAttribute( dataOptionsAttr );
1238 var options;
1239 try {
1240 options = attr && JSON.parse( attr );
1241 } catch ( error ) {
1242 // log error, do not initialize
1243 if ( console ) {
1244 console.error( 'Error parsing ' + dataAttr + ' on ' + elem.className +
1245 ': ' + error );
1246 }
1247 return;
1248 }
1249 // initialize
1250 var instance = new WidgetClass( elem, options );
1251 // make available via $().data('namespace')
1252 if ( jQuery ) {
1253 jQuery.data( elem, namespace, instance );
1254 }
1255 });
1256
1257 });
1258};
1259
1260// ----- ----- //
1261
1262return utils;
1263
1264}));
1265
1266// Flickity.Cell
1267( function( window, factory ) {
1268 // universal module definition
1269 if ( typeof define == 'function' && define.amd ) {
1270 // AMD
1271 define( 'flickity/js/cell',[
1272 'get-size/get-size',
1273 ], function( getSize ) {
1274 return factory( window, getSize );
1275 } );
1276 } else if ( typeof module == 'object' && module.exports ) {
1277 // CommonJS
1278 module.exports = factory(
1279 window,
1280 require('get-size')
1281 );
1282 } else {
1283 // browser global
1284 window.Flickity = window.Flickity || {};
1285 window.Flickity.Cell = factory(
1286 window,
1287 window.getSize
1288 );
1289 }
1290
1291}( window, function factory( window, getSize ) {
1292
1293
1294
1295function Cell( elem, parent ) {
1296 this.element = elem;
1297 this.parent = parent;
1298
1299 this.create();
1300}
1301
1302var proto = Cell.prototype;
1303
1304proto.create = function() {
1305 this.element.style.position = 'absolute';
1306 this.element.setAttribute( 'aria-hidden', 'true' );
1307 this.x = 0;
1308 this.shift = 0;
1309};
1310
1311proto.destroy = function() {
1312 // reset style
1313 this.unselect();
1314 this.element.style.position = '';
1315 var side = this.parent.originSide;
1316 this.element.style[ side ] = '';
1317 this.element.removeAttribute('aria-hidden');
1318};
1319
1320proto.getSize = function() {
1321 this.size = getSize( this.element );
1322};
1323
1324proto.setPosition = function( x ) {
1325 this.x = x;
1326 this.updateTarget();
1327 this.renderPosition( x );
1328};
1329
1330// setDefaultTarget v1 method, backwards compatibility, remove in v3
1331proto.updateTarget = proto.setDefaultTarget = function() {
1332 var marginProperty = this.parent.originSide == 'left' ? 'marginLeft' : 'marginRight';
1333 this.target = this.x + this.size[ marginProperty ] +
1334 this.size.width * this.parent.cellAlign;
1335};
1336
1337proto.renderPosition = function( x ) {
1338 // render position of cell with in slider
1339 var side = this.parent.originSide;
1340 this.element.style[ side ] = this.parent.getPositionValue( x );
1341};
1342
1343proto.select = function() {
1344 this.element.classList.add('is-selected');
1345 this.element.removeAttribute('aria-hidden');
1346};
1347
1348proto.unselect = function() {
1349 this.element.classList.remove('is-selected');
1350 this.element.setAttribute( 'aria-hidden', 'true' );
1351};
1352
1353/**
1354 * @param {Integer} shift - 0, 1, or -1
1355 */
1356proto.wrapShift = function( shift ) {
1357 this.shift = shift;
1358 this.renderPosition( this.x + this.parent.slideableWidth * shift );
1359};
1360
1361proto.remove = function() {
1362 this.element.parentNode.removeChild( this.element );
1363};
1364
1365return Cell;
1366
1367} ) );
1368
1369// slide
1370( function( window, factory ) {
1371 // universal module definition
1372 if ( typeof define == 'function' && define.amd ) {
1373 // AMD
1374 define( 'flickity/js/slide',factory );
1375 } else if ( typeof module == 'object' && module.exports ) {
1376 // CommonJS
1377 module.exports = factory();
1378 } else {
1379 // browser global
1380 window.Flickity = window.Flickity || {};
1381 window.Flickity.Slide = factory();
1382 }
1383
1384}( window, function factory() {
1385'use strict';
1386
1387function Slide( parent ) {
1388 this.parent = parent;
1389 this.isOriginLeft = parent.originSide == 'left';
1390 this.cells = [];
1391 this.outerWidth = 0;
1392 this.height = 0;
1393}
1394
1395var proto = Slide.prototype;
1396
1397proto.addCell = function( cell ) {
1398 this.cells.push( cell );
1399 this.outerWidth += cell.size.outerWidth;
1400 this.height = Math.max( cell.size.outerHeight, this.height );
1401 // first cell stuff
1402 if ( this.cells.length == 1 ) {
1403 this.x = cell.x; // x comes from first cell
1404 var beginMargin = this.isOriginLeft ? 'marginLeft' : 'marginRight';
1405 this.firstMargin = cell.size[ beginMargin ];
1406 }
1407};
1408
1409proto.updateTarget = function() {
1410 var endMargin = this.isOriginLeft ? 'marginRight' : 'marginLeft';
1411 var lastCell = this.getLastCell();
1412 var lastMargin = lastCell ? lastCell.size[ endMargin ] : 0;
1413 var slideWidth = this.outerWidth - ( this.firstMargin + lastMargin );
1414 this.target = this.x + this.firstMargin + slideWidth * this.parent.cellAlign;
1415};
1416
1417proto.getLastCell = function() {
1418 return this.cells[ this.cells.length - 1 ];
1419};
1420
1421proto.select = function() {
1422 this.cells.forEach( function( cell ) {
1423 cell.select();
1424 } );
1425};
1426
1427proto.unselect = function() {
1428 this.cells.forEach( function( cell ) {
1429 cell.unselect();
1430 } );
1431};
1432
1433proto.getCellElements = function() {
1434 return this.cells.map( function( cell ) {
1435 return cell.element;
1436 } );
1437};
1438
1439return Slide;
1440
1441} ) );
1442
1443// animate
1444( function( window, factory ) {
1445 // universal module definition
1446 if ( typeof define == 'function' && define.amd ) {
1447 // AMD
1448 define( 'flickity/js/animate',[
1449 'fizzy-ui-utils/utils',
1450 ], function( utils ) {
1451 return factory( window, utils );
1452 } );
1453 } else if ( typeof module == 'object' && module.exports ) {
1454 // CommonJS
1455 module.exports = factory(
1456 window,
1457 require('fizzy-ui-utils')
1458 );
1459 } else {
1460 // browser global
1461 window.Flickity = window.Flickity || {};
1462 window.Flickity.animatePrototype = factory(
1463 window,
1464 window.fizzyUIUtils
1465 );
1466 }
1467
1468}( window, function factory( window, utils ) {
1469
1470
1471
1472// -------------------------- animate -------------------------- //
1473
1474var proto = {};
1475
1476proto.startAnimation = function() {
1477 if ( this.isAnimating ) {
1478 return;
1479 }
1480
1481 this.isAnimating = true;
1482 this.restingFrames = 0;
1483 this.animate();
1484};
1485
1486proto.animate = function() {
1487 this.applyDragForce();
1488 this.applySelectedAttraction();
1489
1490 var previousX = this.x;
1491
1492 this.integratePhysics();
1493 this.positionSlider();
1494 this.settle( previousX );
1495 // animate next frame
1496 if ( this.isAnimating ) {
1497 var _this = this;
1498 requestAnimationFrame( function animateFrame() {
1499 _this.animate();
1500 } );
1501 }
1502};
1503
1504proto.positionSlider = function() {
1505 var x = this.x;
1506 // wrap position around
1507 if ( this.options.wrapAround && this.cells.length > 1 ) {
1508 x = utils.modulo( x, this.slideableWidth );
1509 x -= this.slideableWidth;
1510 this.shiftWrapCells( x );
1511 }
1512
1513 this.setTranslateX( x, this.isAnimating );
1514 this.dispatchScrollEvent();
1515};
1516
1517proto.setTranslateX = function( x, is3d ) {
1518 x += this.cursorPosition;
1519 // reverse if right-to-left and using transform
1520 x = this.options.rightToLeft ? -x : x;
1521 var translateX = this.getPositionValue( x );
1522 // use 3D transforms for hardware acceleration on iOS
1523 // but use 2D when settled, for better font-rendering
1524 this.slider.style.transform = is3d ?
1525 'translate3d(' + translateX + ',0,0)' : 'translateX(' + translateX + ')';
1526};
1527
1528proto.dispatchScrollEvent = function() {
1529 var firstSlide = this.slides[0];
1530 if ( !firstSlide ) {
1531 return;
1532 }
1533 var positionX = -this.x - firstSlide.target;
1534 var progress = positionX / this.slidesWidth;
1535 this.dispatchEvent( 'scroll', null, [ progress, positionX ] );
1536};
1537
1538proto.positionSliderAtSelected = function() {
1539 if ( !this.cells.length ) {
1540 return;
1541 }
1542 this.x = -this.selectedSlide.target;
1543 this.velocity = 0; // stop wobble
1544 this.positionSlider();
1545};
1546
1547proto.getPositionValue = function( position ) {
1548 if ( this.options.percentPosition ) {
1549 // percent position, round to 2 digits, like 12.34%
1550 return ( Math.round( ( position / this.size.innerWidth ) * 10000 ) * 0.01 ) + '%';
1551 } else {
1552 // pixel positioning
1553 return Math.round( position ) + 'px';
1554 }
1555};
1556
1557proto.settle = function( previousX ) {
1558 // keep track of frames where x hasn't moved
1559 var isResting = !this.isPointerDown &&
1560 Math.round( this.x * 100 ) == Math.round( previousX * 100 );
1561 if ( isResting ) {
1562 this.restingFrames++;
1563 }
1564 // stop animating if resting for 3 or more frames
1565 if ( this.restingFrames > 2 ) {
1566 this.isAnimating = false;
1567 delete this.isFreeScrolling;
1568 // render position with translateX when settled
1569 this.positionSlider();
1570 this.dispatchEvent( 'settle', null, [ this.selectedIndex ] );
1571 }
1572};
1573
1574proto.shiftWrapCells = function( x ) {
1575 // shift before cells
1576 var beforeGap = this.cursorPosition + x;
1577 this._shiftCells( this.beforeShiftCells, beforeGap, -1 );
1578 // shift after cells
1579 var afterGap = this.size.innerWidth - ( x + this.slideableWidth + this.cursorPosition );
1580 this._shiftCells( this.afterShiftCells, afterGap, 1 );
1581};
1582
1583proto._shiftCells = function( cells, gap, shift ) {
1584 for ( var i = 0; i < cells.length; i++ ) {
1585 var cell = cells[i];
1586 var cellShift = gap > 0 ? shift : 0;
1587 cell.wrapShift( cellShift );
1588 gap -= cell.size.outerWidth;
1589 }
1590};
1591
1592proto._unshiftCells = function( cells ) {
1593 if ( !cells || !cells.length ) {
1594 return;
1595 }
1596 for ( var i = 0; i < cells.length; i++ ) {
1597 cells[i].wrapShift( 0 );
1598 }
1599};
1600
1601// -------------------------- physics -------------------------- //
1602
1603proto.integratePhysics = function() {
1604 this.x += this.velocity;
1605 this.velocity *= this.getFrictionFactor();
1606};
1607
1608proto.applyForce = function( force ) {
1609 this.velocity += force;
1610};
1611
1612proto.getFrictionFactor = function() {
1613 return 1 - this.options[ this.isFreeScrolling ? 'freeScrollFriction' : 'friction' ];
1614};
1615
1616proto.getRestingPosition = function() {
1617 // my thanks to Steven Wittens, who simplified this math greatly
1618 return this.x + this.velocity / ( 1 - this.getFrictionFactor() );
1619};
1620
1621proto.applyDragForce = function() {
1622 if ( !this.isDraggable || !this.isPointerDown ) {
1623 return;
1624 }
1625 // change the position to drag position by applying force
1626 var dragVelocity = this.dragX - this.x;
1627 var dragForce = dragVelocity - this.velocity;
1628 this.applyForce( dragForce );
1629};
1630
1631proto.applySelectedAttraction = function() {
1632 // do not attract if pointer down or no slides
1633 var dragDown = this.isDraggable && this.isPointerDown;
1634 if ( dragDown || this.isFreeScrolling || !this.slides.length ) {
1635 return;
1636 }
1637 var distance = this.selectedSlide.target * -1 - this.x;
1638 var force = distance * this.options.selectedAttraction;
1639 this.applyForce( force );
1640};
1641
1642return proto;
1643
1644} ) );
1645
1646// Flickity main
1647/* eslint-disable max-params */
1648( function( window, factory ) {
1649 // universal module definition
1650 if ( typeof define == 'function' && define.amd ) {
1651 // AMD
1652 define( 'flickity/js/flickity',[
1653 'ev-emitter/ev-emitter',
1654 'get-size/get-size',
1655 'fizzy-ui-utils/utils',
1656 './cell',
1657 './slide',
1658 './animate',
1659 ], function( EvEmitter, getSize, utils, Cell, Slide, animatePrototype ) {
1660 return factory( window, EvEmitter, getSize, utils, Cell, Slide, animatePrototype );
1661 } );
1662 } else if ( typeof module == 'object' && module.exports ) {
1663 // CommonJS
1664 module.exports = factory(
1665 window,
1666 require('ev-emitter'),
1667 require('get-size'),
1668 require('fizzy-ui-utils'),
1669 require('./cell'),
1670 require('./slide'),
1671 require('./animate')
1672 );
1673 } else {
1674 // browser global
1675 var _Flickity = window.Flickity;
1676
1677 window.Flickity = factory(
1678 window,
1679 window.EvEmitter,
1680 window.getSize,
1681 window.fizzyUIUtils,
1682 _Flickity.Cell,
1683 _Flickity.Slide,
1684 _Flickity.animatePrototype
1685 );
1686 }
1687
1688}( window, function factory( window, EvEmitter, getSize,
1689 utils, Cell, Slide, animatePrototype ) {
1690
1691/* eslint-enable max-params */
1692
1693
1694// vars
1695var jQuery = window.jQuery;
1696var getComputedStyle = window.getComputedStyle;
1697var console = window.console;
1698
1699function moveElements( elems, toElem ) {
1700 elems = utils.makeArray( elems );
1701 while ( elems.length ) {
1702 toElem.appendChild( elems.shift() );
1703 }
1704}
1705
1706// -------------------------- Flickity -------------------------- //
1707
1708// globally unique identifiers
1709var GUID = 0;
1710// internal store of all Flickity intances
1711var instances = {};
1712
1713function Flickity( element, options ) {
1714 var queryElement = utils.getQueryElement( element );
1715 if ( !queryElement ) {
1716 if ( console ) {
1717 console.error( 'Bad element for Flickity: ' + ( queryElement || element ) );
1718 }
1719 return;
1720 }
1721 this.element = queryElement;
1722 // do not initialize twice on same element
1723 if ( this.element.flickityGUID ) {
1724 var instance = instances[ this.element.flickityGUID ];
1725 if ( instance ) instance.option( options );
1726 return instance;
1727 }
1728
1729 // add jQuery
1730 if ( jQuery ) {
1731 this.$element = jQuery( this.element );
1732 }
1733 // options
1734 this.options = utils.extend( {}, this.constructor.defaults );
1735 this.option( options );
1736
1737 // kick things off
1738 this._create();
1739}
1740
1741Flickity.defaults = {
1742 accessibility: true,
1743 // adaptiveHeight: false,
1744 cellAlign: 'center',
1745 // cellSelector: undefined,
1746 // contain: false,
1747 freeScrollFriction: 0.075, // friction when free-scrolling
1748 friction: 0.28, // friction when selecting
1749 namespaceJQueryEvents: true,
1750 // initialIndex: 0,
1751 percentPosition: true,
1752 resize: true,
1753 selectedAttraction: 0.025,
1754 setGallerySize: true,
1755 // watchCSS: false,
1756 // wrapAround: false
1757};
1758
1759// hash of methods triggered on _create()
1760Flickity.createMethods = [];
1761
1762var proto = Flickity.prototype;
1763// inherit EventEmitter
1764utils.extend( proto, EvEmitter.prototype );
1765
1766proto._create = function() {
1767 // add id for Flickity.data
1768 var id = this.guid = ++GUID;
1769 this.element.flickityGUID = id; // expando
1770 instances[ id ] = this; // associate via id
1771 // initial properties
1772 this.selectedIndex = 0;
1773 // how many frames slider has been in same position
1774 this.restingFrames = 0;
1775 // initial physics properties
1776 this.x = 0;
1777 this.velocity = 0;
1778 this.originSide = this.options.rightToLeft ? 'right' : 'left';
1779 // create viewport & slider
1780 this.viewport = document.createElement('div');
1781 this.viewport.className = 'flickity-viewport';
1782 this._createSlider();
1783
1784 if ( this.options.resize || this.options.watchCSS ) {
1785 window.addEventListener( 'resize', this );
1786 }
1787
1788 // add listeners from on option
1789 for ( var eventName in this.options.on ) {
1790 var listener = this.options.on[ eventName ];
1791 this.on( eventName, listener );
1792 }
1793
1794 Flickity.createMethods.forEach( function( method ) {
1795 this[ method ]();
1796 }, this );
1797
1798 if ( this.options.watchCSS ) {
1799 this.watchCSS();
1800 } else {
1801 this.activate();
1802 }
1803
1804};
1805
1806/**
1807 * set options
1808 * @param {Object} opts - options to extend
1809 */
1810proto.option = function( opts ) {
1811 utils.extend( this.options, opts );
1812};
1813
1814proto.activate = function() {
1815 if ( this.isActive ) {
1816 return;
1817 }
1818 this.isActive = true;
1819 this.element.classList.add('flickity-enabled');
1820 if ( this.options.rightToLeft ) {
1821 this.element.classList.add('flickity-rtl');
1822 }
1823
1824 this.getSize();
1825 // move initial cell elements so they can be loaded as cells
1826 var cellElems = this._filterFindCellElements( this.element.children );
1827 moveElements( cellElems, this.slider );
1828 this.viewport.appendChild( this.slider );
1829 this.element.appendChild( this.viewport );
1830 // get cells from children
1831 this.reloadCells();
1832
1833 if ( this.options.accessibility ) {
1834 // allow element to focusable
1835 this.element.tabIndex = 0;
1836 // listen for key presses
1837 this.element.addEventListener( 'keydown', this );
1838 }
1839
1840 this.emitEvent('activate');
1841 this.selectInitialIndex();
1842 // flag for initial activation, for using initialIndex
1843 this.isInitActivated = true;
1844 // ready event. #493
1845 this.dispatchEvent('ready');
1846};
1847
1848// slider positions the cells
1849proto._createSlider = function() {
1850 // slider element does all the positioning
1851 var slider = document.createElement('div');
1852 slider.className = 'flickity-slider';
1853 slider.style[ this.originSide ] = 0;
1854 this.slider = slider;
1855};
1856
1857proto._filterFindCellElements = function( elems ) {
1858 return utils.filterFindElements( elems, this.options.cellSelector );
1859};
1860
1861// goes through all children
1862proto.reloadCells = function() {
1863 // collection of item elements
1864 this.cells = this._makeCells( this.slider.children );
1865 this.positionCells();
1866 this._getWrapShiftCells();
1867 this.setGallerySize();
1868};
1869
1870/**
1871 * turn elements into Flickity.Cells
1872 * @param {[Array, NodeList, HTMLElement]} elems - elements to make into cells
1873 * @returns {Array} items - collection of new Flickity Cells
1874 */
1875proto._makeCells = function( elems ) {
1876 var cellElems = this._filterFindCellElements( elems );
1877
1878 // create new Flickity for collection
1879 var cells = cellElems.map( function( cellElem ) {
1880 return new Cell( cellElem, this );
1881 }, this );
1882
1883 return cells;
1884};
1885
1886proto.getLastCell = function() {
1887 return this.cells[ this.cells.length - 1 ];
1888};
1889
1890proto.getLastSlide = function() {
1891 return this.slides[ this.slides.length - 1 ];
1892};
1893
1894// positions all cells
1895proto.positionCells = function() {
1896 // size all cells
1897 this._sizeCells( this.cells );
1898 // position all cells
1899 this._positionCells( 0 );
1900};
1901
1902/**
1903 * position certain cells
1904 * @param {Integer} index - which cell to start with
1905 */
1906proto._positionCells = function( index ) {
1907 index = index || 0;
1908 // also measure maxCellHeight
1909 // start 0 if positioning all cells
1910 this.maxCellHeight = index ? this.maxCellHeight || 0 : 0;
1911 var cellX = 0;
1912 // get cellX
1913 if ( index > 0 ) {
1914 var startCell = this.cells[ index - 1 ];
1915 cellX = startCell.x + startCell.size.outerWidth;
1916 }
1917 var len = this.cells.length;
1918 for ( var i = index; i < len; i++ ) {
1919 var cell = this.cells[i];
1920 cell.setPosition( cellX );
1921 cellX += cell.size.outerWidth;
1922 this.maxCellHeight = Math.max( cell.size.outerHeight, this.maxCellHeight );
1923 }
1924 // keep track of cellX for wrap-around
1925 this.slideableWidth = cellX;
1926 // slides
1927 this.updateSlides();
1928 // contain slides target
1929 this._containSlides();
1930 // update slidesWidth
1931 this.slidesWidth = len ? this.getLastSlide().target - this.slides[0].target : 0;
1932};
1933
1934/**
1935 * cell.getSize() on multiple cells
1936 * @param {Array} cells - cells to size
1937 */
1938proto._sizeCells = function( cells ) {
1939 cells.forEach( function( cell ) {
1940 cell.getSize();
1941 } );
1942};
1943
1944// -------------------------- -------------------------- //
1945
1946proto.updateSlides = function() {
1947 this.slides = [];
1948 if ( !this.cells.length ) {
1949 return;
1950 }
1951
1952 var slide = new Slide( this );
1953 this.slides.push( slide );
1954 var isOriginLeft = this.originSide == 'left';
1955 var nextMargin = isOriginLeft ? 'marginRight' : 'marginLeft';
1956
1957 var canCellFit = this._getCanCellFit();
1958
1959 this.cells.forEach( function( cell, i ) {
1960 // just add cell if first cell in slide
1961 if ( !slide.cells.length ) {
1962 slide.addCell( cell );
1963 return;
1964 }
1965
1966 var slideWidth = ( slide.outerWidth - slide.firstMargin ) +
1967 ( cell.size.outerWidth - cell.size[ nextMargin ] );
1968
1969 if ( canCellFit.call( this, i, slideWidth ) ) {
1970 slide.addCell( cell );
1971 } else {
1972 // doesn't fit, new slide
1973 slide.updateTarget();
1974
1975 slide = new Slide( this );
1976 this.slides.push( slide );
1977 slide.addCell( cell );
1978 }
1979 }, this );
1980 // last slide
1981 slide.updateTarget();
1982 // update .selectedSlide
1983 this.updateSelectedSlide();
1984};
1985
1986proto._getCanCellFit = function() {
1987 var groupCells = this.options.groupCells;
1988 if ( !groupCells ) {
1989 return function() {
1990 return false;
1991 };
1992 } else if ( typeof groupCells == 'number' ) {
1993 // group by number. 3 -> [0,1,2], [3,4,5], ...
1994 var number = parseInt( groupCells, 10 );
1995 return function( i ) {
1996 return ( i % number ) !== 0;
1997 };
1998 }
1999 // default, group by width of slide
2000 // parse '75%
2001 var percentMatch = typeof groupCells == 'string' &&
2002 groupCells.match( /^(\d+)%$/ );
2003 var percent = percentMatch ? parseInt( percentMatch[1], 10 ) / 100 : 1;
2004 return function( i, slideWidth ) {
2005 /* eslint-disable-next-line no-invalid-this */
2006 return slideWidth <= ( this.size.innerWidth + 1 ) * percent;
2007 };
2008};
2009
2010// alias _init for jQuery plugin .flickity()
2011proto._init =
2012proto.reposition = function() {
2013 this.positionCells();
2014 this.positionSliderAtSelected();
2015};
2016
2017proto.getSize = function() {
2018 this.size = getSize( this.element );
2019 this.setCellAlign();
2020 this.cursorPosition = this.size.innerWidth * this.cellAlign;
2021};
2022
2023var cellAlignShorthands = {
2024 // cell align, then based on origin side
2025 center: {
2026 left: 0.5,
2027 right: 0.5,
2028 },
2029 left: {
2030 left: 0,
2031 right: 1,
2032 },
2033 right: {
2034 right: 0,
2035 left: 1,
2036 },
2037};
2038
2039proto.setCellAlign = function() {
2040 var shorthand = cellAlignShorthands[ this.options.cellAlign ];
2041 this.cellAlign = shorthand ? shorthand[ this.originSide ] : this.options.cellAlign;
2042};
2043
2044proto.setGallerySize = function() {
2045 if ( this.options.setGallerySize ) {
2046 var height = this.options.adaptiveHeight && this.selectedSlide ?
2047 this.selectedSlide.height : this.maxCellHeight;
2048 this.viewport.style.height = height + 'px';
2049 }
2050};
2051
2052proto._getWrapShiftCells = function() {
2053 // only for wrap-around
2054 if ( !this.options.wrapAround ) {
2055 return;
2056 }
2057 // unshift previous cells
2058 this._unshiftCells( this.beforeShiftCells );
2059 this._unshiftCells( this.afterShiftCells );
2060 // get before cells
2061 // initial gap
2062 var gapX = this.cursorPosition;
2063 var cellIndex = this.cells.length - 1;
2064 this.beforeShiftCells = this._getGapCells( gapX, cellIndex, -1 );
2065 // get after cells
2066 // ending gap between last cell and end of gallery viewport
2067 gapX = this.size.innerWidth - this.cursorPosition;
2068 // start cloning at first cell, working forwards
2069 this.afterShiftCells = this._getGapCells( gapX, 0, 1 );
2070};
2071
2072proto._getGapCells = function( gapX, cellIndex, increment ) {
2073 // keep adding cells until the cover the initial gap
2074 var cells = [];
2075 while ( gapX > 0 ) {
2076 var cell = this.cells[ cellIndex ];
2077 if ( !cell ) {
2078 break;
2079 }
2080 cells.push( cell );
2081 cellIndex += increment;
2082 gapX -= cell.size.outerWidth;
2083 }
2084 return cells;
2085};
2086
2087// ----- contain ----- //
2088
2089// contain cell targets so no excess sliding
2090proto._containSlides = function() {
2091 if ( !this.options.contain || this.options.wrapAround || !this.cells.length ) {
2092 return;
2093 }
2094 var isRightToLeft = this.options.rightToLeft;
2095 var beginMargin = isRightToLeft ? 'marginRight' : 'marginLeft';
2096 var endMargin = isRightToLeft ? 'marginLeft' : 'marginRight';
2097 var contentWidth = this.slideableWidth - this.getLastCell().size[ endMargin ];
2098 // content is less than gallery size
2099 var isContentSmaller = contentWidth < this.size.innerWidth;
2100 // bounds
2101 var beginBound = this.cursorPosition + this.cells[0].size[ beginMargin ];
2102 var endBound = contentWidth - this.size.innerWidth * ( 1 - this.cellAlign );
2103 // contain each cell target
2104 this.slides.forEach( function( slide ) {
2105 if ( isContentSmaller ) {
2106 // all cells fit inside gallery
2107 slide.target = contentWidth * this.cellAlign;
2108 } else {
2109 // contain to bounds
2110 slide.target = Math.max( slide.target, beginBound );
2111 slide.target = Math.min( slide.target, endBound );
2112 }
2113 }, this );
2114};
2115
2116// ----- ----- //
2117
2118/**
2119 * emits events via eventEmitter and jQuery events
2120 * @param {String} type - name of event
2121 * @param {Event} event - original event
2122 * @param {Array} args - extra arguments
2123 */
2124proto.dispatchEvent = function( type, event, args ) {
2125 var emitArgs = event ? [ event ].concat( args ) : args;
2126 this.emitEvent( type, emitArgs );
2127
2128 if ( jQuery && this.$element ) {
2129 // default trigger with type if no event
2130 type += this.options.namespaceJQueryEvents ? '.flickity' : '';
2131 var $event = type;
2132 if ( event ) {
2133 // create jQuery event
2134 var jQEvent = new jQuery.Event( event );
2135 jQEvent.type = type;
2136 $event = jQEvent;
2137 }
2138 this.$element.trigger( $event, args );
2139 }
2140};
2141
2142// -------------------------- select -------------------------- //
2143
2144/**
2145 * @param {Integer} index - index of the slide
2146 * @param {Boolean} isWrap - will wrap-around to last/first if at the end
2147 * @param {Boolean} isInstant - will immediately set position at selected cell
2148 */
2149proto.select = function( index, isWrap, isInstant ) {
2150 if ( !this.isActive ) {
2151 return;
2152 }
2153 index = parseInt( index, 10 );
2154 this._wrapSelect( index );
2155
2156 if ( this.options.wrapAround || isWrap ) {
2157 index = utils.modulo( index, this.slides.length );
2158 }
2159 // bail if invalid index
2160 if ( !this.slides[ index ] ) {
2161 return;
2162 }
2163 var prevIndex = this.selectedIndex;
2164 this.selectedIndex = index;
2165 this.updateSelectedSlide();
2166 if ( isInstant ) {
2167 this.positionSliderAtSelected();
2168 } else {
2169 this.startAnimation();
2170 }
2171 if ( this.options.adaptiveHeight ) {
2172 this.setGallerySize();
2173 }
2174 // events
2175 this.dispatchEvent( 'select', null, [ index ] );
2176 // change event if new index
2177 if ( index != prevIndex ) {
2178 this.dispatchEvent( 'change', null, [ index ] );
2179 }
2180 // old v1 event name, remove in v3
2181 this.dispatchEvent('cellSelect');
2182};
2183
2184// wraps position for wrapAround, to move to closest slide. #113
2185proto._wrapSelect = function( index ) {
2186 var len = this.slides.length;
2187 var isWrapping = this.options.wrapAround && len > 1;
2188 if ( !isWrapping ) {
2189 return index;
2190 }
2191 var wrapIndex = utils.modulo( index, len );
2192 // go to shortest
2193 var delta = Math.abs( wrapIndex - this.selectedIndex );
2194 var backWrapDelta = Math.abs( ( wrapIndex + len ) - this.selectedIndex );
2195 var forewardWrapDelta = Math.abs( ( wrapIndex - len ) - this.selectedIndex );
2196 if ( !this.isDragSelect && backWrapDelta < delta ) {
2197 index += len;
2198 } else if ( !this.isDragSelect && forewardWrapDelta < delta ) {
2199 index -= len;
2200 }
2201 // wrap position so slider is within normal area
2202 if ( index < 0 ) {
2203 this.x -= this.slideableWidth;
2204 } else if ( index >= len ) {
2205 this.x += this.slideableWidth;
2206 }
2207};
2208
2209proto.previous = function( isWrap, isInstant ) {
2210 this.select( this.selectedIndex - 1, isWrap, isInstant );
2211};
2212
2213proto.next = function( isWrap, isInstant ) {
2214 this.select( this.selectedIndex + 1, isWrap, isInstant );
2215};
2216
2217proto.updateSelectedSlide = function() {
2218 var slide = this.slides[ this.selectedIndex ];
2219 // selectedIndex could be outside of slides, if triggered before resize()
2220 if ( !slide ) {
2221 return;
2222 }
2223 // unselect previous selected slide
2224 this.unselectSelectedSlide();
2225 // update new selected slide
2226 this.selectedSlide = slide;
2227 slide.select();
2228 this.selectedCells = slide.cells;
2229 this.selectedElements = slide.getCellElements();
2230 // HACK: selectedCell & selectedElement is first cell in slide, backwards compatibility
2231 // Remove in v3?
2232 this.selectedCell = slide.cells[0];
2233 this.selectedElement = this.selectedElements[0];
2234};
2235
2236proto.unselectSelectedSlide = function() {
2237 if ( this.selectedSlide ) {
2238 this.selectedSlide.unselect();
2239 }
2240};
2241
2242proto.selectInitialIndex = function() {
2243 var initialIndex = this.options.initialIndex;
2244 // already activated, select previous selectedIndex
2245 if ( this.isInitActivated ) {
2246 this.select( this.selectedIndex, false, true );
2247 return;
2248 }
2249 // select with selector string
2250 if ( initialIndex && typeof initialIndex == 'string' ) {
2251 var cell = this.queryCell( initialIndex );
2252 if ( cell ) {
2253 this.selectCell( initialIndex, false, true );
2254 return;
2255 }
2256 }
2257
2258 var index = 0;
2259 // select with number
2260 if ( initialIndex && this.slides[ initialIndex ] ) {
2261 index = initialIndex;
2262 }
2263 // select instantly
2264 this.select( index, false, true );
2265};
2266
2267/**
2268 * select slide from number or cell element
2269 * @param {[Element, Number]} value - zero-based index or element to select
2270 * @param {Boolean} isWrap - enables wrapping around for extra index
2271 * @param {Boolean} isInstant - disables slide animation
2272 */
2273proto.selectCell = function( value, isWrap, isInstant ) {
2274 // get cell
2275 var cell = this.queryCell( value );
2276 if ( !cell ) {
2277 return;
2278 }
2279
2280 var index = this.getCellSlideIndex( cell );
2281 this.select( index, isWrap, isInstant );
2282};
2283
2284proto.getCellSlideIndex = function( cell ) {
2285 // get index of slides that has cell
2286 for ( var i = 0; i < this.slides.length; i++ ) {
2287 var slide = this.slides[i];
2288 var index = slide.cells.indexOf( cell );
2289 if ( index != -1 ) {
2290 return i;
2291 }
2292 }
2293};
2294
2295// -------------------------- get cells -------------------------- //
2296
2297/**
2298 * get Flickity.Cell, given an Element
2299 * @param {Element} elem - matching cell element
2300 * @returns {Flickity.Cell} cell - matching cell
2301 */
2302proto.getCell = function( elem ) {
2303 // loop through cells to get the one that matches
2304 for ( var i = 0; i < this.cells.length; i++ ) {
2305 var cell = this.cells[i];
2306 if ( cell.element == elem ) {
2307 return cell;
2308 }
2309 }
2310};
2311
2312/**
2313 * get collection of Flickity.Cells, given Elements
2314 * @param {[Element, Array, NodeList]} elems - multiple elements
2315 * @returns {Array} cells - Flickity.Cells
2316 */
2317proto.getCells = function( elems ) {
2318 elems = utils.makeArray( elems );
2319 var cells = [];
2320 elems.forEach( function( elem ) {
2321 var cell = this.getCell( elem );
2322 if ( cell ) {
2323 cells.push( cell );
2324 }
2325 }, this );
2326 return cells;
2327};
2328
2329/**
2330 * get cell elements
2331 * @returns {Array} cellElems
2332 */
2333proto.getCellElements = function() {
2334 return this.cells.map( function( cell ) {
2335 return cell.element;
2336 } );
2337};
2338
2339/**
2340 * get parent cell from an element
2341 * @param {Element} elem - child element
2342 * @returns {Flickit.Cell} cell - parent cell
2343 */
2344proto.getParentCell = function( elem ) {
2345 // first check if elem is cell
2346 var cell = this.getCell( elem );
2347 if ( cell ) {
2348 return cell;
2349 }
2350 // try to get parent cell elem
2351 elem = utils.getParent( elem, '.flickity-slider > *' );
2352 return this.getCell( elem );
2353};
2354
2355/**
2356 * get cells adjacent to a slide
2357 * @param {Integer} adjCount - number of adjacent slides
2358 * @param {Integer} index - index of slide to start
2359 * @returns {Array} cells - array of Flickity.Cells
2360 */
2361proto.getAdjacentCellElements = function( adjCount, index ) {
2362 if ( !adjCount ) {
2363 return this.selectedSlide.getCellElements();
2364 }
2365 index = index === undefined ? this.selectedIndex : index;
2366
2367 var len = this.slides.length;
2368 if ( 1 + ( adjCount * 2 ) >= len ) {
2369 return this.getCellElements();
2370 }
2371
2372 var cellElems = [];
2373 for ( var i = index - adjCount; i <= index + adjCount; i++ ) {
2374 var slideIndex = this.options.wrapAround ? utils.modulo( i, len ) : i;
2375 var slide = this.slides[ slideIndex ];
2376 if ( slide ) {
2377 cellElems = cellElems.concat( slide.getCellElements() );
2378 }
2379 }
2380 return cellElems;
2381};
2382
2383/**
2384 * select slide from number or cell element
2385 * @param {[Element, String, Number]} selector - element, selector string, or index
2386 * @returns {Flickity.Cell} - matching cell
2387 */
2388proto.queryCell = function( selector ) {
2389 if ( typeof selector == 'number' ) {
2390 // use number as index
2391 return this.cells[ selector ];
2392 }
2393 if ( typeof selector == 'string' ) {
2394 // do not select invalid selectors from hash: #123, #/. #791
2395 if ( selector.match( /^[#.]?[\d/]/ ) ) {
2396 return;
2397 }
2398 // use string as selector, get element
2399 selector = this.element.querySelector( selector );
2400 }
2401 // get cell from element
2402 return this.getCell( selector );
2403};
2404
2405// -------------------------- events -------------------------- //
2406
2407proto.uiChange = function() {
2408 this.emitEvent('uiChange');
2409};
2410
2411// keep focus on element when child UI elements are clicked
2412proto.childUIPointerDown = function( event ) {
2413 // HACK iOS does not allow touch events to bubble up?!
2414 if ( event.type != 'touchstart' ) {
2415 event.preventDefault();
2416 }
2417 this.focus();
2418};
2419
2420// ----- resize ----- //
2421
2422proto.onresize = function() {
2423 this.watchCSS();
2424 this.resize();
2425};
2426
2427utils.debounceMethod( Flickity, 'onresize', 150 );
2428
2429proto.resize = function() {
2430 if ( !this.isActive ) {
2431 return;
2432 }
2433 this.getSize();
2434 // wrap values
2435 if ( this.options.wrapAround ) {
2436 this.x = utils.modulo( this.x, this.slideableWidth );
2437 }
2438 this.positionCells();
2439 this._getWrapShiftCells();
2440 this.setGallerySize();
2441 this.emitEvent('resize');
2442 // update selected index for group slides, instant
2443 // TODO: position can be lost between groups of various numbers
2444 var selectedElement = this.selectedElements && this.selectedElements[0];
2445 this.selectCell( selectedElement, false, true );
2446};
2447
2448// watches the :after property, activates/deactivates
2449proto.watchCSS = function() {
2450 var watchOption = this.options.watchCSS;
2451 if ( !watchOption ) {
2452 return;
2453 }
2454
2455 var afterContent = getComputedStyle( this.element, ':after' ).content;
2456 // activate if :after { content: 'flickity' }
2457 if ( afterContent.indexOf('flickity') != -1 ) {
2458 this.activate();
2459 } else {
2460 this.deactivate();
2461 }
2462};
2463
2464// ----- keydown ----- //
2465
2466// go previous/next if left/right keys pressed
2467proto.onkeydown = function( event ) {
2468 // only work if element is in focus
2469 var isNotFocused = document.activeElement && document.activeElement != this.element;
2470 if ( !this.options.accessibility || isNotFocused ) {
2471 return;
2472 }
2473
2474 var handler = Flickity.keyboardHandlers[ event.keyCode ];
2475 if ( handler ) {
2476 handler.call( this );
2477 }
2478};
2479
2480Flickity.keyboardHandlers = {
2481 // left arrow
2482 37: function() {
2483 var leftMethod = this.options.rightToLeft ? 'next' : 'previous';
2484 this.uiChange();
2485 this[ leftMethod ]();
2486 },
2487 // right arrow
2488 39: function() {
2489 var rightMethod = this.options.rightToLeft ? 'previous' : 'next';
2490 this.uiChange();
2491 this[ rightMethod ]();
2492 },
2493};
2494
2495// ----- focus ----- //
2496
2497proto.focus = function() {
2498 // TODO remove scrollTo once focus options gets more support
2499 // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus ...
2500 // #Browser_compatibility
2501 var prevScrollY = window.pageYOffset;
2502 this.element.focus({ preventScroll: true });
2503 // hack to fix scroll jump after focus, #76
2504 if ( window.pageYOffset != prevScrollY ) {
2505 window.scrollTo( window.pageXOffset, prevScrollY );
2506 }
2507};
2508
2509// -------------------------- destroy -------------------------- //
2510
2511// deactivate all Flickity functionality, but keep stuff available
2512proto.deactivate = function() {
2513 if ( !this.isActive ) {
2514 return;
2515 }
2516 this.element.classList.remove('flickity-enabled');
2517 this.element.classList.remove('flickity-rtl');
2518 this.unselectSelectedSlide();
2519 // destroy cells
2520 this.cells.forEach( function( cell ) {
2521 cell.destroy();
2522 } );
2523 this.element.removeChild( this.viewport );
2524 // move child elements back into element
2525 moveElements( this.slider.children, this.element );
2526 if ( this.options.accessibility ) {
2527 this.element.removeAttribute('tabIndex');
2528 this.element.removeEventListener( 'keydown', this );
2529 }
2530 // set flags
2531 this.isActive = false;
2532 this.emitEvent('deactivate');
2533};
2534
2535proto.destroy = function() {
2536 this.deactivate();
2537 window.removeEventListener( 'resize', this );
2538 this.allOff();
2539 this.emitEvent('destroy');
2540 if ( jQuery && this.$element ) {
2541 jQuery.removeData( this.element, 'flickity' );
2542 }
2543 delete this.element.flickityGUID;
2544 delete instances[ this.guid ];
2545};
2546
2547// -------------------------- prototype -------------------------- //
2548
2549utils.extend( proto, animatePrototype );
2550
2551// -------------------------- extras -------------------------- //
2552
2553/**
2554 * get Flickity instance from element
2555 * @param {[Element, String]} elem - element or selector string
2556 * @returns {Flickity} - Flickity instance
2557 */
2558Flickity.data = function( elem ) {
2559 elem = utils.getQueryElement( elem );
2560 var id = elem && elem.flickityGUID;
2561 return id && instances[ id ];
2562};
2563
2564utils.htmlInit( Flickity, 'flickity' );
2565
2566if ( jQuery && jQuery.bridget ) {
2567 jQuery.bridget( 'flickity', Flickity );
2568}
2569
2570// set internal jQuery, for Webpack + jQuery v3, #478
2571Flickity.setJQuery = function( jq ) {
2572 jQuery = jq;
2573};
2574
2575Flickity.Cell = Cell;
2576Flickity.Slide = Slide;
2577
2578return Flickity;
2579
2580} ) );
2581
2582/*!
2583 * Unipointer v2.3.0
2584 * base class for doing one thing with pointer event
2585 * MIT license
2586 */
2587
2588/*jshint browser: true, undef: true, unused: true, strict: true */
2589
2590( function( window, factory ) {
2591 // universal module definition
2592 /* jshint strict: false */ /*global define, module, require */
2593 if ( typeof define == 'function' && define.amd ) {
2594 // AMD
2595 define( 'unipointer/unipointer',[
2596 'ev-emitter/ev-emitter'
2597 ], function( EvEmitter ) {
2598 return factory( window, EvEmitter );
2599 });
2600 } else if ( typeof module == 'object' && module.exports ) {
2601 // CommonJS
2602 module.exports = factory(
2603 window,
2604 require('ev-emitter')
2605 );
2606 } else {
2607 // browser global
2608 window.Unipointer = factory(
2609 window,
2610 window.EvEmitter
2611 );
2612 }
2613
2614}( window, function factory( window, EvEmitter ) {
2615
2616
2617
2618function noop() {}
2619
2620function Unipointer() {}
2621
2622// inherit EvEmitter
2623var proto = Unipointer.prototype = Object.create( EvEmitter.prototype );
2624
2625proto.bindStartEvent = function( elem ) {
2626 this._bindStartEvent( elem, true );
2627};
2628
2629proto.unbindStartEvent = function( elem ) {
2630 this._bindStartEvent( elem, false );
2631};
2632
2633/**
2634 * Add or remove start event
2635 * @param {Boolean} isAdd - remove if falsey
2636 */
2637proto._bindStartEvent = function( elem, isAdd ) {
2638 // munge isAdd, default to true
2639 isAdd = isAdd === undefined ? true : isAdd;
2640 var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener';
2641
2642 // default to mouse events
2643 var startEvent = 'mousedown';
2644 if ( window.PointerEvent ) {
2645 // Pointer Events
2646 startEvent = 'pointerdown';
2647 } else if ( 'ontouchstart' in window ) {
2648 // Touch Events. iOS Safari
2649 startEvent = 'touchstart';
2650 }
2651 elem[ bindMethod ]( startEvent, this );
2652};
2653
2654// trigger handler methods for events
2655proto.handleEvent = function( event ) {
2656 var method = 'on' + event.type;
2657 if ( this[ method ] ) {
2658 this[ method ]( event );
2659 }
2660};
2661
2662// returns the touch that we're keeping track of
2663proto.getTouch = function( touches ) {
2664 for ( var i=0; i < touches.length; i++ ) {
2665 var touch = touches[i];
2666 if ( touch.identifier == this.pointerIdentifier ) {
2667 return touch;
2668 }
2669 }
2670};
2671
2672// ----- start event ----- //
2673
2674proto.onmousedown = function( event ) {
2675 // dismiss clicks from right or middle buttons
2676 var button = event.button;
2677 if ( button && ( button !== 0 && button !== 1 ) ) {
2678 return;
2679 }
2680 this._pointerDown( event, event );
2681};
2682
2683proto.ontouchstart = function( event ) {
2684 this._pointerDown( event, event.changedTouches[0] );
2685};
2686
2687proto.onpointerdown = function( event ) {
2688 this._pointerDown( event, event );
2689};
2690
2691/**
2692 * pointer start
2693 * @param {Event} event
2694 * @param {Event or Touch} pointer
2695 */
2696proto._pointerDown = function( event, pointer ) {
2697 // dismiss right click and other pointers
2698 // button = 0 is okay, 1-4 not
2699 if ( event.button || this.isPointerDown ) {
2700 return;
2701 }
2702
2703 this.isPointerDown = true;
2704 // save pointer identifier to match up touch events
2705 this.pointerIdentifier = pointer.pointerId !== undefined ?
2706 // pointerId for pointer events, touch.indentifier for touch events
2707 pointer.pointerId : pointer.identifier;
2708
2709 this.pointerDown( event, pointer );
2710};
2711
2712proto.pointerDown = function( event, pointer ) {
2713 this._bindPostStartEvents( event );
2714 this.emitEvent( 'pointerDown', [ event, pointer ] );
2715};
2716
2717// hash of events to be bound after start event
2718var postStartEvents = {
2719 mousedown: [ 'mousemove', 'mouseup' ],
2720 touchstart: [ 'touchmove', 'touchend', 'touchcancel' ],
2721 pointerdown: [ 'pointermove', 'pointerup', 'pointercancel' ],
2722};
2723
2724proto._bindPostStartEvents = function( event ) {
2725 if ( !event ) {
2726 return;
2727 }
2728 // get proper events to match start event
2729 var events = postStartEvents[ event.type ];
2730 // bind events to node
2731 events.forEach( function( eventName ) {
2732 window.addEventListener( eventName, this );
2733 }, this );
2734 // save these arguments
2735 this._boundPointerEvents = events;
2736};
2737
2738proto._unbindPostStartEvents = function() {
2739 // check for _boundEvents, in case dragEnd triggered twice (old IE8 bug)
2740 if ( !this._boundPointerEvents ) {
2741 return;
2742 }
2743 this._boundPointerEvents.forEach( function( eventName ) {
2744 window.removeEventListener( eventName, this );
2745 }, this );
2746
2747 delete this._boundPointerEvents;
2748};
2749
2750// ----- move event ----- //
2751
2752proto.onmousemove = function( event ) {
2753 this._pointerMove( event, event );
2754};
2755
2756proto.onpointermove = function( event ) {
2757 if ( event.pointerId == this.pointerIdentifier ) {
2758 this._pointerMove( event, event );
2759 }
2760};
2761
2762proto.ontouchmove = function( event ) {
2763 var touch = this.getTouch( event.changedTouches );
2764 if ( touch ) {
2765 this._pointerMove( event, touch );
2766 }
2767};
2768
2769/**
2770 * pointer move
2771 * @param {Event} event
2772 * @param {Event or Touch} pointer
2773 * @private
2774 */
2775proto._pointerMove = function( event, pointer ) {
2776 this.pointerMove( event, pointer );
2777};
2778
2779// public
2780proto.pointerMove = function( event, pointer ) {
2781 this.emitEvent( 'pointerMove', [ event, pointer ] );
2782};
2783
2784// ----- end event ----- //
2785
2786
2787proto.onmouseup = function( event ) {
2788 this._pointerUp( event, event );
2789};
2790
2791proto.onpointerup = function( event ) {
2792 if ( event.pointerId == this.pointerIdentifier ) {
2793 this._pointerUp( event, event );
2794 }
2795};
2796
2797proto.ontouchend = function( event ) {
2798 var touch = this.getTouch( event.changedTouches );
2799 if ( touch ) {
2800 this._pointerUp( event, touch );
2801 }
2802};
2803
2804/**
2805 * pointer up
2806 * @param {Event} event
2807 * @param {Event or Touch} pointer
2808 * @private
2809 */
2810proto._pointerUp = function( event, pointer ) {
2811 this._pointerDone();
2812 this.pointerUp( event, pointer );
2813};
2814
2815// public
2816proto.pointerUp = function( event, pointer ) {
2817 this.emitEvent( 'pointerUp', [ event, pointer ] );
2818};
2819
2820// ----- pointer done ----- //
2821
2822// triggered on pointer up & pointer cancel
2823proto._pointerDone = function() {
2824 this._pointerReset();
2825 this._unbindPostStartEvents();
2826 this.pointerDone();
2827};
2828
2829proto._pointerReset = function() {
2830 // reset properties
2831 this.isPointerDown = false;
2832 delete this.pointerIdentifier;
2833};
2834
2835proto.pointerDone = noop;
2836
2837// ----- pointer cancel ----- //
2838
2839proto.onpointercancel = function( event ) {
2840 if ( event.pointerId == this.pointerIdentifier ) {
2841 this._pointerCancel( event, event );
2842 }
2843};
2844
2845proto.ontouchcancel = function( event ) {
2846 var touch = this.getTouch( event.changedTouches );
2847 if ( touch ) {
2848 this._pointerCancel( event, touch );
2849 }
2850};
2851
2852/**
2853 * pointer cancel
2854 * @param {Event} event
2855 * @param {Event or Touch} pointer
2856 * @private
2857 */
2858proto._pointerCancel = function( event, pointer ) {
2859 this._pointerDone();
2860 this.pointerCancel( event, pointer );
2861};
2862
2863// public
2864proto.pointerCancel = function( event, pointer ) {
2865 this.emitEvent( 'pointerCancel', [ event, pointer ] );
2866};
2867
2868// ----- ----- //
2869
2870// utility function for getting x/y coords from event
2871Unipointer.getPointerPoint = function( pointer ) {
2872 return {
2873 x: pointer.pageX,
2874 y: pointer.pageY
2875 };
2876};
2877
2878// ----- ----- //
2879
2880return Unipointer;
2881
2882}));
2883
2884/*!
2885 * Unidragger v2.3.1
2886 * Draggable base class
2887 * MIT license
2888 */
2889
2890/*jshint browser: true, unused: true, undef: true, strict: true */
2891
2892( function( window, factory ) {
2893 // universal module definition
2894 /*jshint strict: false */ /*globals define, module, require */
2895
2896 if ( typeof define == 'function' && define.amd ) {
2897 // AMD
2898 define( 'unidragger/unidragger',[
2899 'unipointer/unipointer'
2900 ], function( Unipointer ) {
2901 return factory( window, Unipointer );
2902 });
2903 } else if ( typeof module == 'object' && module.exports ) {
2904 // CommonJS
2905 module.exports = factory(
2906 window,
2907 require('unipointer')
2908 );
2909 } else {
2910 // browser global
2911 window.Unidragger = factory(
2912 window,
2913 window.Unipointer
2914 );
2915 }
2916
2917}( window, function factory( window, Unipointer ) {
2918
2919
2920
2921// -------------------------- Unidragger -------------------------- //
2922
2923function Unidragger() {}
2924
2925// inherit Unipointer & EvEmitter
2926var proto = Unidragger.prototype = Object.create( Unipointer.prototype );
2927
2928// ----- bind start ----- //
2929
2930proto.bindHandles = function() {
2931 this._bindHandles( true );
2932};
2933
2934proto.unbindHandles = function() {
2935 this._bindHandles( false );
2936};
2937
2938/**
2939 * Add or remove start event
2940 * @param {Boolean} isAdd
2941 */
2942proto._bindHandles = function( isAdd ) {
2943 // munge isAdd, default to true
2944 isAdd = isAdd === undefined ? true : isAdd;
2945 // bind each handle
2946 var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener';
2947 var touchAction = isAdd ? this._touchActionValue : '';
2948 for ( var i=0; i < this.handles.length; i++ ) {
2949 var handle = this.handles[i];
2950 this._bindStartEvent( handle, isAdd );
2951 handle[ bindMethod ]( 'click', this );
2952 // touch-action: none to override browser touch gestures. metafizzy/flickity#540
2953 if ( window.PointerEvent ) {
2954 handle.style.touchAction = touchAction;
2955 }
2956 }
2957};
2958
2959// prototype so it can be overwriteable by Flickity
2960proto._touchActionValue = 'none';
2961
2962// ----- start event ----- //
2963
2964/**
2965 * pointer start
2966 * @param {Event} event
2967 * @param {Event or Touch} pointer
2968 */
2969proto.pointerDown = function( event, pointer ) {
2970 var isOkay = this.okayPointerDown( event );
2971 if ( !isOkay ) {
2972 return;
2973 }
2974 // track start event position
2975 // Safari 9 overrides pageX and pageY. These values needs to be copied. flickity#842
2976 this.pointerDownPointer = {
2977 pageX: pointer.pageX,
2978 pageY: pointer.pageY,
2979 };
2980
2981 event.preventDefault();
2982 this.pointerDownBlur();
2983 // bind move and end events
2984 this._bindPostStartEvents( event );
2985 this.emitEvent( 'pointerDown', [ event, pointer ] );
2986};
2987
2988// nodes that have text fields
2989var cursorNodes = {
2990 TEXTAREA: true,
2991 INPUT: true,
2992 SELECT: true,
2993 OPTION: true,
2994};
2995
2996// input types that do not have text fields
2997var clickTypes = {
2998 radio: true,
2999 checkbox: true,
3000 button: true,
3001 submit: true,
3002 image: true,
3003 file: true,
3004};
3005
3006// dismiss inputs with text fields. flickity#403, flickity#404
3007proto.okayPointerDown = function( event ) {
3008 var isCursorNode = cursorNodes[ event.target.nodeName ];
3009 var isClickType = clickTypes[ event.target.type ];
3010 var isOkay = !isCursorNode || isClickType;
3011 if ( !isOkay ) {
3012 this._pointerReset();
3013 }
3014 return isOkay;
3015};
3016
3017// kludge to blur previously focused input
3018proto.pointerDownBlur = function() {
3019 var focused = document.activeElement;
3020 // do not blur body for IE10, metafizzy/flickity#117
3021 var canBlur = focused && focused.blur && focused != document.body;
3022 if ( canBlur ) {
3023 focused.blur();
3024 }
3025};
3026
3027// ----- move event ----- //
3028
3029/**
3030 * drag move
3031 * @param {Event} event
3032 * @param {Event or Touch} pointer
3033 */
3034proto.pointerMove = function( event, pointer ) {
3035 var moveVector = this._dragPointerMove( event, pointer );
3036 this.emitEvent( 'pointerMove', [ event, pointer, moveVector ] );
3037 this._dragMove( event, pointer, moveVector );
3038};
3039
3040// base pointer move logic
3041proto._dragPointerMove = function( event, pointer ) {
3042 var moveVector = {
3043 x: pointer.pageX - this.pointerDownPointer.pageX,
3044 y: pointer.pageY - this.pointerDownPointer.pageY
3045 };
3046 // start drag if pointer has moved far enough to start drag
3047 if ( !this.isDragging && this.hasDragStarted( moveVector ) ) {
3048 this._dragStart( event, pointer );
3049 }
3050 return moveVector;
3051};
3052
3053// condition if pointer has moved far enough to start drag
3054proto.hasDragStarted = function( moveVector ) {
3055 return Math.abs( moveVector.x ) > 3 || Math.abs( moveVector.y ) > 3;
3056};
3057
3058// ----- end event ----- //
3059
3060/**
3061 * pointer up
3062 * @param {Event} event
3063 * @param {Event or Touch} pointer
3064 */
3065proto.pointerUp = function( event, pointer ) {
3066 this.emitEvent( 'pointerUp', [ event, pointer ] );
3067 this._dragPointerUp( event, pointer );
3068};
3069
3070proto._dragPointerUp = function( event, pointer ) {
3071 if ( this.isDragging ) {
3072 this._dragEnd( event, pointer );
3073 } else {
3074 // pointer didn't move enough for drag to start
3075 this._staticClick( event, pointer );
3076 }
3077};
3078
3079// -------------------------- drag -------------------------- //
3080
3081// dragStart
3082proto._dragStart = function( event, pointer ) {
3083 this.isDragging = true;
3084 // prevent clicks
3085 this.isPreventingClicks = true;
3086 this.dragStart( event, pointer );
3087};
3088
3089proto.dragStart = function( event, pointer ) {
3090 this.emitEvent( 'dragStart', [ event, pointer ] );
3091};
3092
3093// dragMove
3094proto._dragMove = function( event, pointer, moveVector ) {
3095 // do not drag if not dragging yet
3096 if ( !this.isDragging ) {
3097 return;
3098 }
3099
3100 this.dragMove( event, pointer, moveVector );
3101};
3102
3103proto.dragMove = function( event, pointer, moveVector ) {
3104 event.preventDefault();
3105 this.emitEvent( 'dragMove', [ event, pointer, moveVector ] );
3106};
3107
3108// dragEnd
3109proto._dragEnd = function( event, pointer ) {
3110 // set flags
3111 this.isDragging = false;
3112 // re-enable clicking async
3113 setTimeout( function() {
3114 delete this.isPreventingClicks;
3115 }.bind( this ) );
3116
3117 this.dragEnd( event, pointer );
3118};
3119
3120proto.dragEnd = function( event, pointer ) {
3121 this.emitEvent( 'dragEnd', [ event, pointer ] );
3122};
3123
3124// ----- onclick ----- //
3125
3126// handle all clicks and prevent clicks when dragging
3127proto.onclick = function( event ) {
3128 if ( this.isPreventingClicks ) {
3129 event.preventDefault();
3130 }
3131};
3132
3133// ----- staticClick ----- //
3134
3135// triggered after pointer down & up with no/tiny movement
3136proto._staticClick = function( event, pointer ) {
3137 // ignore emulated mouse up clicks
3138 if ( this.isIgnoringMouseUp && event.type == 'mouseup' ) {
3139 return;
3140 }
3141
3142 this.staticClick( event, pointer );
3143
3144 // set flag for emulated clicks 300ms after touchend
3145 if ( event.type != 'mouseup' ) {
3146 this.isIgnoringMouseUp = true;
3147 // reset flag after 300ms
3148 setTimeout( function() {
3149 delete this.isIgnoringMouseUp;
3150 }.bind( this ), 400 );
3151 }
3152};
3153
3154proto.staticClick = function( event, pointer ) {
3155 this.emitEvent( 'staticClick', [ event, pointer ] );
3156};
3157
3158// ----- utils ----- //
3159
3160Unidragger.getPointerPoint = Unipointer.getPointerPoint;
3161
3162// ----- ----- //
3163
3164return Unidragger;
3165
3166}));
3167
3168// drag
3169( function( window, factory ) {
3170 // universal module definition
3171 if ( typeof define == 'function' && define.amd ) {
3172 // AMD
3173 define( 'flickity/js/drag',[
3174 './flickity',
3175 'unidragger/unidragger',
3176 'fizzy-ui-utils/utils',
3177 ], function( Flickity, Unidragger, utils ) {
3178 return factory( window, Flickity, Unidragger, utils );
3179 } );
3180 } else if ( typeof module == 'object' && module.exports ) {
3181 // CommonJS
3182 module.exports = factory(
3183 window,
3184 require('./flickity'),
3185 require('unidragger'),
3186 require('fizzy-ui-utils')
3187 );
3188 } else {
3189 // browser global
3190 window.Flickity = factory(
3191 window,
3192 window.Flickity,
3193 window.Unidragger,
3194 window.fizzyUIUtils
3195 );
3196 }
3197
3198}( window, function factory( window, Flickity, Unidragger, utils ) {
3199
3200
3201
3202// ----- defaults ----- //
3203
3204utils.extend( Flickity.defaults, {
3205 draggable: '>1',
3206 dragThreshold: 3,
3207} );
3208
3209// ----- create ----- //
3210
3211Flickity.createMethods.push('_createDrag');
3212
3213// -------------------------- drag prototype -------------------------- //
3214
3215var proto = Flickity.prototype;
3216utils.extend( proto, Unidragger.prototype );
3217proto._touchActionValue = 'pan-y';
3218
3219// -------------------------- -------------------------- //
3220
3221var isTouch = 'createTouch' in document;
3222var isTouchmoveScrollCanceled = false;
3223
3224proto._createDrag = function() {
3225 this.on( 'activate', this.onActivateDrag );
3226 this.on( 'uiChange', this._uiChangeDrag );
3227 this.on( 'deactivate', this.onDeactivateDrag );
3228 this.on( 'cellChange', this.updateDraggable );
3229 // TODO updateDraggable on resize? if groupCells & slides change
3230 // HACK - add seemingly innocuous handler to fix iOS 10 scroll behavior
3231 // #457, RubaXa/Sortable#973
3232 if ( isTouch && !isTouchmoveScrollCanceled ) {
3233 window.addEventListener( 'touchmove', function() {} );
3234 isTouchmoveScrollCanceled = true;
3235 }
3236};
3237
3238proto.onActivateDrag = function() {
3239 this.handles = [ this.viewport ];
3240 this.bindHandles();
3241 this.updateDraggable();
3242};
3243
3244proto.onDeactivateDrag = function() {
3245 this.unbindHandles();
3246 this.element.classList.remove('is-draggable');
3247};
3248
3249proto.updateDraggable = function() {
3250 // disable dragging if less than 2 slides. #278
3251 if ( this.options.draggable == '>1' ) {
3252 this.isDraggable = this.slides.length > 1;
3253 } else {
3254 this.isDraggable = this.options.draggable;
3255 }
3256 if ( this.isDraggable ) {
3257 this.element.classList.add('is-draggable');
3258 } else {
3259 this.element.classList.remove('is-draggable');
3260 }
3261};
3262
3263// backwards compatibility
3264proto.bindDrag = function() {
3265 this.options.draggable = true;
3266 this.updateDraggable();
3267};
3268
3269proto.unbindDrag = function() {
3270 this.options.draggable = false;
3271 this.updateDraggable();
3272};
3273
3274proto._uiChangeDrag = function() {
3275 delete this.isFreeScrolling;
3276};
3277
3278// -------------------------- pointer events -------------------------- //
3279
3280proto.pointerDown = function( event, pointer ) {
3281 if ( !this.isDraggable ) {
3282 this._pointerDownDefault( event, pointer );
3283 return;
3284 }
3285 var isOkay = this.okayPointerDown( event );
3286 if ( !isOkay ) {
3287 return;
3288 }
3289
3290 this._pointerDownPreventDefault( event );
3291 this.pointerDownFocus( event );
3292 // blur
3293 if ( document.activeElement != this.element ) {
3294 // do not blur if already focused
3295 this.pointerDownBlur();
3296 }
3297
3298 // stop if it was moving
3299 this.dragX = this.x;
3300 this.viewport.classList.add('is-pointer-down');
3301 // track scrolling
3302 this.pointerDownScroll = getScrollPosition();
3303 window.addEventListener( 'scroll', this );
3304
3305 this._pointerDownDefault( event, pointer );
3306};
3307
3308// default pointerDown logic, used for staticClick
3309proto._pointerDownDefault = function( event, pointer ) {
3310 // track start event position
3311 // Safari 9 overrides pageX and pageY. These values needs to be copied. #779
3312 this.pointerDownPointer = {
3313 pageX: pointer.pageX,
3314 pageY: pointer.pageY,
3315 };
3316 // bind move and end events
3317 this._bindPostStartEvents( event );
3318 this.dispatchEvent( 'pointerDown', event, [ pointer ] );
3319};
3320
3321var focusNodes = {
3322 INPUT: true,
3323 TEXTAREA: true,
3324 SELECT: true,
3325};
3326
3327proto.pointerDownFocus = function( event ) {
3328 var isFocusNode = focusNodes[ event.target.nodeName ];
3329 if ( !isFocusNode ) {
3330 this.focus();
3331 }
3332};
3333
3334proto._pointerDownPreventDefault = function( event ) {
3335 var isTouchStart = event.type == 'touchstart';
3336 var isTouchPointer = event.pointerType == 'touch';
3337 var isFocusNode = focusNodes[ event.target.nodeName ];
3338 if ( !isTouchStart && !isTouchPointer && !isFocusNode ) {
3339 event.preventDefault();
3340 }
3341};
3342
3343// ----- move ----- //
3344
3345proto.hasDragStarted = function( moveVector ) {
3346 return Math.abs( moveVector.x ) > this.options.dragThreshold;
3347};
3348
3349// ----- up ----- //
3350
3351proto.pointerUp = function( event, pointer ) {
3352 delete this.isTouchScrolling;
3353 this.viewport.classList.remove('is-pointer-down');
3354 this.dispatchEvent( 'pointerUp', event, [ pointer ] );
3355 this._dragPointerUp( event, pointer );
3356};
3357
3358proto.pointerDone = function() {
3359 window.removeEventListener( 'scroll', this );
3360 delete this.pointerDownScroll;
3361};
3362
3363// -------------------------- dragging -------------------------- //
3364
3365proto.dragStart = function( event, pointer ) {
3366 if ( !this.isDraggable ) {
3367 return;
3368 }
3369 this.dragStartPosition = this.x;
3370 this.startAnimation();
3371 window.removeEventListener( 'scroll', this );
3372 this.dispatchEvent( 'dragStart', event, [ pointer ] );
3373};
3374
3375proto.pointerMove = function( event, pointer ) {
3376 var moveVector = this._dragPointerMove( event, pointer );
3377 this.dispatchEvent( 'pointerMove', event, [ pointer, moveVector ] );
3378 this._dragMove( event, pointer, moveVector );
3379};
3380
3381proto.dragMove = function( event, pointer, moveVector ) {
3382 if ( !this.isDraggable ) {
3383 return;
3384 }
3385 event.preventDefault();
3386
3387 this.previousDragX = this.dragX;
3388 // reverse if right-to-left
3389 var direction = this.options.rightToLeft ? -1 : 1;
3390 if ( this.options.wrapAround ) {
3391 // wrap around move. #589
3392 moveVector.x %= this.slideableWidth;
3393 }
3394 var dragX = this.dragStartPosition + moveVector.x * direction;
3395
3396 if ( !this.options.wrapAround && this.slides.length ) {
3397 // slow drag
3398 var originBound = Math.max( -this.slides[0].target, this.dragStartPosition );
3399 dragX = dragX > originBound ? ( dragX + originBound ) * 0.5 : dragX;
3400 var endBound = Math.min( -this.getLastSlide().target, this.dragStartPosition );
3401 dragX = dragX < endBound ? ( dragX + endBound ) * 0.5 : dragX;
3402 }
3403
3404 this.dragX = dragX;
3405
3406 this.dragMoveTime = new Date();
3407 this.dispatchEvent( 'dragMove', event, [ pointer, moveVector ] );
3408};
3409
3410proto.dragEnd = function( event, pointer ) {
3411 if ( !this.isDraggable ) {
3412 return;
3413 }
3414 if ( this.options.freeScroll ) {
3415 this.isFreeScrolling = true;
3416 }
3417 // set selectedIndex based on where flick will end up
3418 var index = this.dragEndRestingSelect();
3419
3420 if ( this.options.freeScroll && !this.options.wrapAround ) {
3421 // if free-scroll & not wrap around
3422 // do not free-scroll if going outside of bounding slides
3423 // so bounding slides can attract slider, and keep it in bounds
3424 var restingX = this.getRestingPosition();
3425 this.isFreeScrolling = -restingX > this.slides[0].target &&
3426 -restingX < this.getLastSlide().target;
3427 } else if ( !this.options.freeScroll && index == this.selectedIndex ) {
3428 // boost selection if selected index has not changed
3429 index += this.dragEndBoostSelect();
3430 }
3431 delete this.previousDragX;
3432 // apply selection
3433 // TODO refactor this, selecting here feels weird
3434 // HACK, set flag so dragging stays in correct direction
3435 this.isDragSelect = this.options.wrapAround;
3436 this.select( index );
3437 delete this.isDragSelect;
3438 this.dispatchEvent( 'dragEnd', event, [ pointer ] );
3439};
3440
3441proto.dragEndRestingSelect = function() {
3442 var restingX = this.getRestingPosition();
3443 // how far away from selected slide
3444 var distance = Math.abs( this.getSlideDistance( -restingX, this.selectedIndex ) );
3445 // get closet resting going up and going down
3446 var positiveResting = this._getClosestResting( restingX, distance, 1 );
3447 var negativeResting = this._getClosestResting( restingX, distance, -1 );
3448 // use closer resting for wrap-around
3449 var index = positiveResting.distance < negativeResting.distance ?
3450 positiveResting.index : negativeResting.index;
3451 return index;
3452};
3453
3454/**
3455 * given resting X and distance to selected cell
3456 * get the distance and index of the closest cell
3457 * @param {Number} restingX - estimated post-flick resting position
3458 * @param {Number} distance - distance to selected cell
3459 * @param {Integer} increment - +1 or -1, going up or down
3460 * @returns {Object} - { distance: {Number}, index: {Integer} }
3461 */
3462proto._getClosestResting = function( restingX, distance, increment ) {
3463 var index = this.selectedIndex;
3464 var minDistance = Infinity;
3465 var condition = this.options.contain && !this.options.wrapAround ?
3466 // if contain, keep going if distance is equal to minDistance
3467 function( dist, minDist ) {
3468 return dist <= minDist;
3469 } : function( dist, minDist ) {
3470 return dist < minDist;
3471 };
3472 while ( condition( distance, minDistance ) ) {
3473 // measure distance to next cell
3474 index += increment;
3475 minDistance = distance;
3476 distance = this.getSlideDistance( -restingX, index );
3477 if ( distance === null ) {
3478 break;
3479 }
3480 distance = Math.abs( distance );
3481 }
3482 return {
3483 distance: minDistance,
3484 // selected was previous index
3485 index: index - increment,
3486 };
3487};
3488
3489/**
3490 * measure distance between x and a slide target
3491 * @param {Number} x - horizontal position
3492 * @param {Integer} index - slide index
3493 * @returns {Number} - slide distance
3494 */
3495proto.getSlideDistance = function( x, index ) {
3496 var len = this.slides.length;
3497 // wrap around if at least 2 slides
3498 var isWrapAround = this.options.wrapAround && len > 1;
3499 var slideIndex = isWrapAround ? utils.modulo( index, len ) : index;
3500 var slide = this.slides[ slideIndex ];
3501 if ( !slide ) {
3502 return null;
3503 }
3504 // add distance for wrap-around slides
3505 var wrap = isWrapAround ? this.slideableWidth * Math.floor( index/len ) : 0;
3506 return x - ( slide.target + wrap );
3507};
3508
3509proto.dragEndBoostSelect = function() {
3510 // do not boost if no previousDragX or dragMoveTime
3511 if ( this.previousDragX === undefined || !this.dragMoveTime ||
3512 // or if drag was held for 100 ms
3513 new Date() - this.dragMoveTime > 100 ) {
3514 return 0;
3515 }
3516
3517 var distance = this.getSlideDistance( -this.dragX, this.selectedIndex );
3518 var delta = this.previousDragX - this.dragX;
3519 if ( distance > 0 && delta > 0 ) {
3520 // boost to next if moving towards the right, and positive velocity
3521 return 1;
3522 } else if ( distance < 0 && delta < 0 ) {
3523 // boost to previous if moving towards the left, and negative velocity
3524 return -1;
3525 }
3526 return 0;
3527};
3528
3529// ----- staticClick ----- //
3530
3531proto.staticClick = function( event, pointer ) {
3532 // get clickedCell, if cell was clicked
3533 var clickedCell = this.getParentCell( event.target );
3534 var cellElem = clickedCell && clickedCell.element;
3535 var cellIndex = clickedCell && this.cells.indexOf( clickedCell );
3536 this.dispatchEvent( 'staticClick', event, [ pointer, cellElem, cellIndex ] );
3537};
3538
3539// ----- scroll ----- //
3540
3541proto.onscroll = function() {
3542 var scroll = getScrollPosition();
3543 var scrollMoveX = this.pointerDownScroll.x - scroll.x;
3544 var scrollMoveY = this.pointerDownScroll.y - scroll.y;
3545 // cancel click/tap if scroll is too much
3546 if ( Math.abs( scrollMoveX ) > 3 || Math.abs( scrollMoveY ) > 3 ) {
3547 this._pointerDone();
3548 }
3549};
3550
3551// ----- utils ----- //
3552
3553function getScrollPosition() {
3554 return {
3555 x: window.pageXOffset,
3556 y: window.pageYOffset,
3557 };
3558}
3559
3560// ----- ----- //
3561
3562return Flickity;
3563
3564} ) );
3565
3566// prev/next buttons
3567( function( window, factory ) {
3568 // universal module definition
3569 if ( typeof define == 'function' && define.amd ) {
3570 // AMD
3571 define( 'flickity/js/prev-next-button',[
3572 './flickity',
3573 'unipointer/unipointer',
3574 'fizzy-ui-utils/utils',
3575 ], function( Flickity, Unipointer, utils ) {
3576 return factory( window, Flickity, Unipointer, utils );
3577 } );
3578 } else if ( typeof module == 'object' && module.exports ) {
3579 // CommonJS
3580 module.exports = factory(
3581 window,
3582 require('./flickity'),
3583 require('unipointer'),
3584 require('fizzy-ui-utils')
3585 );
3586 } else {
3587 // browser global
3588 factory(
3589 window,
3590 window.Flickity,
3591 window.Unipointer,
3592 window.fizzyUIUtils
3593 );
3594 }
3595
3596}( window, function factory( window, Flickity, Unipointer, utils ) {
3597'use strict';
3598
3599var svgURI = 'http://www.w3.org/2000/svg';
3600
3601// -------------------------- PrevNextButton -------------------------- //
3602
3603function PrevNextButton( direction, parent ) {
3604 this.direction = direction;
3605 this.parent = parent;
3606 this._create();
3607}
3608
3609PrevNextButton.prototype = Object.create( Unipointer.prototype );
3610
3611PrevNextButton.prototype._create = function() {
3612 // properties
3613 this.isEnabled = true;
3614 this.isPrevious = this.direction == -1;
3615 var leftDirection = this.parent.options.rightToLeft ? 1 : -1;
3616 this.isLeft = this.direction == leftDirection;
3617
3618 var element = this.element = document.createElement('button');
3619 element.className = 'flickity-button flickity-prev-next-button';
3620 element.className += this.isPrevious ? ' previous' : ' next';
3621 // prevent button from submitting form http://stackoverflow.com/a/10836076/182183
3622 element.setAttribute( 'type', 'button' );
3623 // init as disabled
3624 this.disable();
3625
3626 element.setAttribute( 'aria-label', this.isPrevious ? 'Previous' : 'Next' );
3627
3628 // create arrow
3629 var svg = this.createSVG();
3630 element.appendChild( svg );
3631 // events
3632 this.parent.on( 'select', this.update.bind( this ) );
3633 this.on( 'pointerDown', this.parent.childUIPointerDown.bind( this.parent ) );
3634};
3635
3636PrevNextButton.prototype.activate = function() {
3637 this.bindStartEvent( this.element );
3638 this.element.addEventListener( 'click', this );
3639 // add to DOM
3640 this.parent.element.appendChild( this.element );
3641};
3642
3643PrevNextButton.prototype.deactivate = function() {
3644 // remove from DOM
3645 this.parent.element.removeChild( this.element );
3646 // click events
3647 this.unbindStartEvent( this.element );
3648 this.element.removeEventListener( 'click', this );
3649};
3650
3651PrevNextButton.prototype.createSVG = function() {
3652 var svg = document.createElementNS( svgURI, 'svg' );
3653 svg.setAttribute( 'class', 'flickity-button-icon' );
3654 svg.setAttribute( 'viewBox', '0 0 100 100' );
3655 var path = document.createElementNS( svgURI, 'path' );
3656 var pathMovements = getArrowMovements( this.parent.options.arrowShape );
3657 path.setAttribute( 'd', pathMovements );
3658 path.setAttribute( 'class', 'arrow' );
3659 // rotate arrow
3660 if ( !this.isLeft ) {
3661 path.setAttribute( 'transform', 'translate(100, 100) rotate(180) ' );
3662 }
3663 svg.appendChild( path );
3664 return svg;
3665};
3666
3667// get SVG path movmement
3668function getArrowMovements( shape ) {
3669 // use shape as movement if string
3670 if ( typeof shape == 'string' ) {
3671 return shape;
3672 }
3673 // create movement string
3674 return 'M ' + shape.x0 + ',50' +
3675 ' L ' + shape.x1 + ',' + ( shape.y1 + 50 ) +
3676 ' L ' + shape.x2 + ',' + ( shape.y2 + 50 ) +
3677 ' L ' + shape.x3 + ',50 ' +
3678 ' L ' + shape.x2 + ',' + ( 50 - shape.y2 ) +
3679 ' L ' + shape.x1 + ',' + ( 50 - shape.y1 ) +
3680 ' Z';
3681}
3682
3683PrevNextButton.prototype.handleEvent = utils.handleEvent;
3684
3685PrevNextButton.prototype.onclick = function() {
3686 if ( !this.isEnabled ) {
3687 return;
3688 }
3689 this.parent.uiChange();
3690 var method = this.isPrevious ? 'previous' : 'next';
3691 this.parent[ method ]();
3692};
3693
3694// ----- ----- //
3695
3696PrevNextButton.prototype.enable = function() {
3697 if ( this.isEnabled ) {
3698 return;
3699 }
3700 this.element.disabled = false;
3701 this.isEnabled = true;
3702};
3703
3704PrevNextButton.prototype.disable = function() {
3705 if ( !this.isEnabled ) {
3706 return;
3707 }
3708 this.element.disabled = true;
3709 this.isEnabled = false;
3710};
3711
3712PrevNextButton.prototype.update = function() {
3713 // index of first or last slide, if previous or next
3714 var slides = this.parent.slides;
3715 // enable is wrapAround and at least 2 slides
3716 if ( this.parent.options.wrapAround && slides.length > 1 ) {
3717 this.enable();
3718 return;
3719 }
3720 var lastIndex = slides.length ? slides.length - 1 : 0;
3721 var boundIndex = this.isPrevious ? 0 : lastIndex;
3722 var method = this.parent.selectedIndex == boundIndex ? 'disable' : 'enable';
3723 this[ method ]();
3724};
3725
3726PrevNextButton.prototype.destroy = function() {
3727 this.deactivate();
3728 this.allOff();
3729};
3730
3731// -------------------------- Flickity prototype -------------------------- //
3732
3733utils.extend( Flickity.defaults, {
3734 prevNextButtons: true,
3735 arrowShape: {
3736 x0: 10,
3737 x1: 60, y1: 50,
3738 x2: 70, y2: 40,
3739 x3: 30,
3740 },
3741} );
3742
3743Flickity.createMethods.push('_createPrevNextButtons');
3744var proto = Flickity.prototype;
3745
3746proto._createPrevNextButtons = function() {
3747 if ( !this.options.prevNextButtons ) {
3748 return;
3749 }
3750
3751 this.prevButton = new PrevNextButton( -1, this );
3752 this.nextButton = new PrevNextButton( 1, this );
3753
3754 this.on( 'activate', this.activatePrevNextButtons );
3755};
3756
3757proto.activatePrevNextButtons = function() {
3758 this.prevButton.activate();
3759 this.nextButton.activate();
3760 this.on( 'deactivate', this.deactivatePrevNextButtons );
3761};
3762
3763proto.deactivatePrevNextButtons = function() {
3764 this.prevButton.deactivate();
3765 this.nextButton.deactivate();
3766 this.off( 'deactivate
The following has evaluated to null or missing: ==> urlimagen [in template "20102#20129#12795723" at line 58, column 26] ---- Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)?? ---- ---- FTL stack trace ("~" means nesting-related): - Failed at: #if urlimagen.url?has_content [in template "20102#20129#12795723" at line 58, column 21] ----
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 urlimagenNode = docXml.valueOf("//dynamic-element[@name='IMAGEN']/dynamic-content")/>
24
25
26
27 <#assign titulo = docXml.valueOf("//dynamic-element[@name='Titulopub']/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 <#if urlimagenNode?has_content>
51 <#assign urlimagenString = urlimagenNode?string />
52 <#assign urlimagen = urlimagenString?eval />
53 </#if>
54
55 <div class="gallery-cell${nameInstancePublisher}">
56 <div class="card-info${nameInstancePublisher}" id="card-info">
57 <div class="card-img${nameInstancePublisher}" id="card-img">
58 <#if urlimagen.url?has_content>
59 <img alt="${urlimagen.alt}" src='${urlimagen.url}' title="${titulo}">
60 <#else>
61 <img alt='${urlimagen["alt"]}' src='/documents/${urlimagen["groupId"]}/${urlimagen["classPK"]}/${urlimagen["name"]}/${urlimagen["uuid"]}' title="${titulo}">
62 </#if>
63 <#if assetRenderer.hasEditPermission(themeDisplay.getPermissionChecker())>
64 <#assign editPortletURL = assetRenderer.getURLEdit(renderRequest, renderResponse, windowStateFactory.getWindowState("NORMAL"), themeDisplay.getURLCurrent())!"" />
65 <#if validator.isNotNull(editPortletURL)>
66 <a class="editOptionmas" href="${editPortletURL.toString()}">Editar ✐</a>
67 </#if>
68 </#if>
69 </div>
70 <div class="card-txt${nameInstancePublisher}" id="card-txt">
71 <div class="card-title${nameInstancePublisher}" id="card-title">${titulo}</div>
72 <div class="card-lead${nameInstancePublisher}" id="card-lead">${contenido}</div>
73 <div class="card-btn${nameInstancePublisher}" id="card-btn">
74
75 <a class="link${nameInstancePublisher}" href="${linkext}" target="_blank">
76 Ver más
77 </a>
78
79 </div>
80 </div>
81
82 </div>
83 </div>
84 </#list>
85</div>
86
87
88
89
90
91<!---------------------- ESTLOS BASICOS ------------------------>
92
93 <style>
94
95 .cards-cont${nameInstancePublisher} {
96 width: 100%;
97 display: flex;
98 justify-content: center;
99 flex-wrap: wrap;
100
101 }
102
103
104 .card-info${nameInstancePublisher} {
105
106 width: 250px;
107 height: 350px;
108 margin: 15px;
109 display:flex;
110 align-content:center;
111 align-items:center;
112 flex-wrap:wrap;
113 }
114
115 .card-img${nameInstancePublisher} {
116 width: 250px;
117 height: 350px;
118 border-radius: 10px;
119 -webkit-box-shadow: 0px 2px 17px 1px rgb(0 0 0 / 23%);
120 box-shadow: 0px 2px 17px 1px rgb(0 0 0 / 23%);
121 box-sizing: border-box;
122 background-position: center;
123 background-size: cover;
124 -webkit-transition: all 500ms ease-in-out; // IE 9
125 -moz-transition: all 500ms ease-in-out; // Firefox
126 -ms-transition: all 500ms ease-in-out; // Safari and Chrome
127 -o-transition: all 500ms ease-in-out; // Opera
128 transition: all 500ms ease-in-out;
129 position:relative;
130 display:flex;
131 }
132
133 .card-info${nameInstancePublisher}:hover .card-img${nameInstancePublisher} {
134 width: 250px;
135 height: 350px;
136 border-radius: 10px;
137 box-sizing: border-box;
138 background-position: center;
139 background-size: cover;
140 -moz-transform: scale(1.02);
141 -webkit-transform: scale(1.02);
142 -o-transform: scale(1.02);
143 -ms-transform: scale(1.02);
144 transform: scale(1.02);
145 }
146
147 .card-img${nameInstancePublisher} img{
148 vertical-align: middle;
149 border-style: none;
150 width: 100%;
151 height: 350px;
152 object-fit: cover;
153 object-position: center;
154 border-radius: 10px;
155 }
156
157 .card-txt${nameInstancePublisher} {
158 position: absolute;
159 display:flex;
160 padding: 20px;
161 color: #FFF;
162 opacity: 1;
163 transition: all 500ms ease-in-out;
164 width: 90%;
165 flex-wrap:wrap;
166 max-width: 250px;
167 }
168 #card-txt{
169 display:none;
170 opacity:0;
171 transition: all 1s;
172 -webkit-transition: all 1s;
173 }
174
175 #card-info:hover #card-txt{
176 display:flex;
177 opacity:1;
178 transition: all 1s;
179 -webkit-transition: all 1s;
180 }
181
182 #card-info:hover #card-img{filter: brightness(0.3);}
183
184 .card-title${nameInstancePublisher} {
185 font-family: HelveticaBold;
186 font-size: 1rem;
187 transition: all 500ms ease-in-out;
188 width:100%;
189 }
190
191 .card-lead${nameInstancePublisher} {
192 font-family: HelveticaLight;
193 font-size: 0.9rem;
194 transition: all 500ms ease-in-out;
195 width: 100%;
196 }
197
198 .card-btn${nameInstancePublisher} {
199 padding: 5px;
200 font-family: HelveticaLight;
201 border: 1px solid #fff;
202 width: auto;
203 font-size: 0.8rem;
204 margin-top: 10px;
205 display: flex;
206 border-radius: 10px;
207 text-align: center;
208 /* margin: 0 auto; */
209 /* align-content: center; */
210 justify-content: center;
211 cursor: pointer;
212 opacity: 1;
213 }
214
215 .card-info${nameInstancePublisher}:hover .card-btn${nameInstancePublisher}{
216 background-color:#FFF;
217 color:#494949;
218 width: auto;
219 padding: 5px 10px;
220 border-radius: 5px;
221 }
222
223 .card-btn${nameInstancePublisher} a{
224 color:#FFF;
225 text-decoration:none;
226 }
227
228 .card-info${nameInstancePublisher}:hover .card-btn${nameInstancePublisher} a{
229 color:#494949;
230
231 }
232
233
234 .editOptionmas{
235 position: absolute;
236 background-color: #173268;
237 padding: 2px 5px;
238 color: #FFF;
239 font-family:HelveticaLight;
240 display:flex;
241 flex-wrap:nowrap;
242 width: 85px;
243 top:0px;
244 justify-content: center;
245 border-radius:10px 0px 10px 0px;
246 }
247
248 .editOptionmas:hover{
249 text-decoration:none;
250 color:#FFF;
251 font-family:HelveticaBold;
252 }
253
254 </style>
255
256 <style>
257 .gallery${nameInstancePublisher} {
258
259 margin: 0 auto;
260 width: 97%;
261 max-width:1200px;
262 margin-bottom:30px;
263 height:420px;
264 }
265
266 .gallery-cell${nameInstancePublisher} {
267 width: 22%;
268 height: auto;
269 margin-right: 10px;
270 counter-increment: gallery-cell;
271 }
272 /* cell number */
273
274 .gallery-cell${nameInstancePublisher}:before {
275 display: block;
276 text-align: center;
277 /content: counter(gallery-cell);/
278 line-height: 200px;
279 font-size: 80px;
280 color: white;
281 }
282
283 @media screen and (max-width: 640px) {
284 .gallery-cell${nameInstancePublisher} {
285 width: 100%;
286 height: auto;
287 margin-right: 10px;
288 counter-increment: gallery-cell;
289 }
290 }
291
292
293 .flickity-page-dots .dot.is-selected {
294 opacity: 1;
295 background: #2c5697;
296 }
297
298 .flickity-page-dots .dot {
299 display: inline-block;
300 width: 8px!important;
301 height: 8px!important;
302 margin: 0 5px!important;
303 background: #494949;
304 border-radius: 50%;
305 opacity: 0.25;
306 cursor: pointer;
307 }
308
309 .flickity-page-dots {
310 position: absolute;
311 width: 100%;
312 bottom: 0px!important;
313 padding: 0;
314 margin: 0;
315 list-style: none;
316 text-align: center;
317 line-height: 1;
318 margin-bottom: 0px;
319 }
320
321 .flickity-prev-next-button.previous {
322 left: -50px!important;
323 }
324
325 .flickity-prev-next-button.next {
326 right: -50px!important;
327 }
328
329 @media screen and (max-width: 640px) {
330 .flickity-prev-next-button.previous {
331 left: 10px!important;
332 }
333
334 .flickity-prev-next-button.next {
335 right: 10px!important;
336 }
337
338 .flickity-prev-next-button {
339 top: 25%!important;
340 width: 44px;
341 height: 44px;
342 border-radius: 50%;
343 transform: translateY(-50%);
344 }
345}
346
347
348 </style>
349
350<!------------------------------------------------------- ESTILOS DEL SCRIPT ---------------------------------------------->
351
352
353<style>
354
355.flickity-enabled {
356 position: relative;
357}
358
359.flickity-enabled:focus { outline: none; }
360
361.flickity-viewport {
362 overflow: hidden;
363 position: relative;
364 height: 100%;
365}
366
367.flickity-slider {
368 position: absolute;
369 width: 100%;
370 height: 100%;
371}
372
373/* draggable */
374
375.flickity-enabled.is-draggable {
376 -webkit-tap-highlight-color: transparent;
377 -webkit-user-select: none;
378 -moz-user-select: none;
379 -ms-user-select: none;
380 user-select: none;
381}
382
383.flickity-enabled.is-draggable .flickity-viewport {
384 cursor: move;
385 cursor: -webkit-grab;
386 cursor: grab;
387}
388
389.flickity-enabled.is-draggable .flickity-viewport.is-pointer-down {
390 cursor: -webkit-grabbing;
391 cursor: grabbing;
392}
393
394/* ---- flickity-button ---- */
395
396.flickity-button {
397 position: absolute;
398 background: hsla(0, 0%, 100%, 0.75);
399 border: none;
400 color: #333;
401}
402
403.flickity-button:hover {
404 background: white;
405 cursor: pointer;
406}
407
408.flickity-button:focus {
409 outline: none;
410 box-shadow: 0 0 0 5px #19F;
411}
412
413.flickity-button:active {
414 opacity: 0.6;
415}
416
417.flickity-button:disabled {
418 opacity: 0.3;
419 cursor: auto;
420 /* prevent disabled button from capturing pointer up event. #716 */
421 pointer-events: none;
422}
423
424.flickity-button-icon {
425 fill: currentColor;
426}
427
428/* ---- previous/next buttons ---- */
429
430.flickity-prev-next-button {
431 top: 50%;
432 width: 44px;
433 height: 44px;
434 border-radius: 50%;
435 /* vertically center */
436 transform: translateY(-50%);
437}
438
439.flickity-prev-next-button.previous { left: 10px; }
440.flickity-prev-next-button.next { right: 10px; }
441/* right to left */
442.flickity-rtl .flickity-prev-next-button.previous {
443 left: auto;
444 right: 10px;
445}
446.flickity-rtl .flickity-prev-next-button.next {
447 right: auto;
448 left: 10px;
449}
450
451.flickity-prev-next-button .flickity-button-icon {
452 position: absolute;
453 left: 20%;
454 top: 20%;
455 width: 60%;
456 height: 60%;
457}
458
459/* ---- page dots ---- */
460
461.flickity-page-dots {
462 position: absolute;
463 width: 100%;
464 bottom: -25px;
465 padding: 0;
466 margin: 0;
467 list-style: none;
468 text-align: center;
469 line-height: 1;
470}
471
472.flickity-rtl .flickity-page-dots { direction: rtl; }
473
474.flickity-page-dots .dot {
475 display: inline-block;
476 width: 10px;
477 height: 10px;
478 margin: 0 8px;
479 background: #333;
480 border-radius: 50%;
481 opacity: 0.25;
482 cursor: pointer;
483}
484
485.flickity-page-dots .dot.is-selected {
486 opacity: 1;
487}
488 </style>
489
490
491<!------------------------------------------------------------- SCRIPT PRINCIPAL -------------------------------------------------------------------------->
492
493<script>
494 /*!
495 * Flickity PACKAGED v2.2.2
496 * Touch, responsive, flickable carousels
497 *
498 * Licensed GPLv3 for open source use
499 * or Flickity Commercial License for commercial use
500 *
501 * https://flickity.metafizzy.co
502 * Copyright 2015-2021 Metafizzy
503 */
504
505/**
506 * Bridget makes jQuery widgets
507 * v2.0.1
508 * MIT license
509 */
510
511/* jshint browser: true, strict: true, undef: true, unused: true */
512
513( function( window, factory ) {
514 // universal module definition
515 /*jshint strict: false */ /* globals define, module, require */
516 if ( typeof define == 'function' && define.amd ) {
517 // AMD
518 define( 'jquery-bridget/jquery-bridget',[ 'jquery' ], function( jQuery ) {
519 return factory( window, jQuery );
520 });
521 } else if ( typeof module == 'object' && module.exports ) {
522 // CommonJS
523 module.exports = factory(
524 window,
525 require('jquery')
526 );
527 } else {
528 // browser global
529 window.jQueryBridget = factory(
530 window,
531 window.jQuery
532 );
533 }
534
535}( window, function factory( window, jQuery ) {
536'use strict';
537
538// ----- utils ----- //
539
540var arraySlice = Array.prototype.slice;
541
542// helper function for logging errors
543// $.error breaks jQuery chaining
544var console = window.console;
545var logError = typeof console == 'undefined' ? function() {} :
546 function( message ) {
547 console.error( message );
548 };
549
550// ----- jQueryBridget ----- //
551
552function jQueryBridget( namespace, PluginClass, $ ) {
553 $ = $ || jQuery || window.jQuery;
554 if ( !$ ) {
555 return;
556 }
557
558 // add option method -> $().plugin('option', {...})
559 if ( !PluginClass.prototype.option ) {
560 // option setter
561 PluginClass.prototype.option = function( opts ) {
562 // bail out if not an object
563 if ( !$.isPlainObject( opts ) ){
564 return;
565 }
566 this.options = $.extend( true, this.options, opts );
567 };
568 }
569
570 // make jQuery plugin
571 $.fn[ namespace ] = function( arg0 /*, arg1 */ ) {
572 if ( typeof arg0 == 'string' ) {
573 // method call $().plugin( 'methodName', { options } )
574 // shift arguments by 1
575 var args = arraySlice.call( arguments, 1 );
576 return methodCall( this, arg0, args );
577 }
578 // just $().plugin({ options })
579 plainCall( this, arg0 );
580 return this;
581 };
582
583 // $().plugin('methodName')
584 function methodCall( $elems, methodName, args ) {
585 var returnValue;
586 var pluginMethodStr = '$().' + namespace + '("' + methodName + '")';
587
588 $elems.each( function( i, elem ) {
589 // get instance
590 var instance = $.data( elem, namespace );
591 if ( !instance ) {
592 logError( namespace + ' not initialized. Cannot call methods, i.e. ' +
593 pluginMethodStr );
594 return;
595 }
596
597 var method = instance[ methodName ];
598 if ( !method || methodName.charAt(0) == '_' ) {
599 logError( pluginMethodStr + ' is not a valid method' );
600 return;
601 }
602
603 // apply method, get return value
604 var value = method.apply( instance, args );
605 // set return value if value is returned, use only first value
606 returnValue = returnValue === undefined ? value : returnValue;
607 });
608
609 return returnValue !== undefined ? returnValue : $elems;
610 }
611
612 function plainCall( $elems, options ) {
613 $elems.each( function( i, elem ) {
614 var instance = $.data( elem, namespace );
615 if ( instance ) {
616 // set options & init
617 instance.option( options );
618 instance._init();
619 } else {
620 // initialize new instance
621 instance = new PluginClass( elem, options );
622 $.data( elem, namespace, instance );
623 }
624 });
625 }
626
627 updateJQuery( $ );
628
629}
630
631// ----- updateJQuery ----- //
632
633// set $.bridget for v1 backwards compatibility
634function updateJQuery( $ ) {
635 if ( !$ || ( $ && $.bridget ) ) {
636 return;
637 }
638 $.bridget = jQueryBridget;
639}
640
641updateJQuery( jQuery || window.jQuery );
642
643// ----- ----- //
644
645return jQueryBridget;
646
647}));
648
649/**
650 * EvEmitter v1.1.0
651 * Lil' event emitter
652 * MIT License
653 */
654
655/* jshint unused: true, undef: true, strict: true */
656
657( function( global, factory ) {
658 // universal module definition
659 /* jshint strict: false */ /* globals define, module, window */
660 if ( typeof define == 'function' && define.amd ) {
661 // AMD - RequireJS
662 define( 'ev-emitter/ev-emitter',factory );
663 } else if ( typeof module == 'object' && module.exports ) {
664 // CommonJS - Browserify, Webpack
665 module.exports = factory();
666 } else {
667 // Browser globals
668 global.EvEmitter = factory();
669 }
670
671}( typeof window != 'undefined' ? window : this, function() {
672
673
674
675function EvEmitter() {}
676
677var proto = EvEmitter.prototype;
678
679proto.on = function( eventName, listener ) {
680 if ( !eventName || !listener ) {
681 return;
682 }
683 // set events hash
684 var events = this._events = this._events || {};
685 // set listeners array
686 var listeners = events[ eventName ] = events[ eventName ] || [];
687 // only add once
688 if ( listeners.indexOf( listener ) == -1 ) {
689 listeners.push( listener );
690 }
691
692 return this;
693};
694
695proto.once = function( eventName, listener ) {
696 if ( !eventName || !listener ) {
697 return;
698 }
699 // add event
700 this.on( eventName, listener );
701 // set once flag
702 // set onceEvents hash
703 var onceEvents = this._onceEvents = this._onceEvents || {};
704 // set onceListeners object
705 var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {};
706 // set flag
707 onceListeners[ listener ] = true;
708
709 return this;
710};
711
712proto.off = function( eventName, listener ) {
713 var listeners = this._events && this._events[ eventName ];
714 if ( !listeners || !listeners.length ) {
715 return;
716 }
717 var index = listeners.indexOf( listener );
718 if ( index != -1 ) {
719 listeners.splice( index, 1 );
720 }
721
722 return this;
723};
724
725proto.emitEvent = function( eventName, args ) {
726 var listeners = this._events && this._events[ eventName ];
727 if ( !listeners || !listeners.length ) {
728 return;
729 }
730 // copy over to avoid interference if .off() in listener
731 listeners = listeners.slice(0);
732 args = args || [];
733 // once stuff
734 var onceListeners = this._onceEvents && this._onceEvents[ eventName ];
735
736 for ( var i=0; i < listeners.length; i++ ) {
737 var listener = listeners[i]
738 var isOnce = onceListeners && onceListeners[ listener ];
739 if ( isOnce ) {
740 // remove listener
741 // remove before trigger to prevent recursion
742 this.off( eventName, listener );
743 // unset once flag
744 delete onceListeners[ listener ];
745 }
746 // trigger listener
747 listener.apply( this, args );
748 }
749
750 return this;
751};
752
753proto.allOff = function() {
754 delete this._events;
755 delete this._onceEvents;
756};
757
758return EvEmitter;
759
760}));
761
762/*!
763 * getSize v2.0.3
764 * measure size of elements
765 * MIT license
766 */
767
768/* jshint browser: true, strict: true, undef: true, unused: true */
769/* globals console: false */
770
771( function( window, factory ) {
772 /* jshint strict: false */ /* globals define, module */
773 if ( typeof define == 'function' && define.amd ) {
774 // AMD
775 define( 'get-size/get-size',factory );
776 } else if ( typeof module == 'object' && module.exports ) {
777 // CommonJS
778 module.exports = factory();
779 } else {
780 // browser global
781 window.getSize = factory();
782 }
783
784})( window, function factory() {
785'use strict';
786
787// -------------------------- helpers -------------------------- //
788
789// get a number from a string, not a percentage
790function getStyleSize( value ) {
791 var num = parseFloat( value );
792 // not a percent like '100%', and a number
793 var isValid = value.indexOf('%') == -1 && !isNaN( num );
794 return isValid && num;
795}
796
797function noop() {}
798
799var logError = typeof console == 'undefined' ? noop :
800 function( message ) {
801 console.error( message );
802 };
803
804// -------------------------- measurements -------------------------- //
805
806var measurements = [
807 'paddingLeft',
808 'paddingRight',
809 'paddingTop',
810 'paddingBottom',
811 'marginLeft',
812 'marginRight',
813 'marginTop',
814 'marginBottom',
815 'borderLeftWidth',
816 'borderRightWidth',
817 'borderTopWidth',
818 'borderBottomWidth'
819];
820
821var measurementsLength = measurements.length;
822
823function getZeroSize() {
824 var size = {
825 width: 0,
826 height: 0,
827 innerWidth: 0,
828 innerHeight: 0,
829 outerWidth: 0,
830 outerHeight: 0
831 };
832 for ( var i=0; i < measurementsLength; i++ ) {
833 var measurement = measurements[i];
834 size[ measurement ] = 0;
835 }
836 return size;
837}
838
839// -------------------------- getStyle -------------------------- //
840
841/**
842 * getStyle, get style of element, check for Firefox bug
843 * https://bugzilla.mozilla.org/show_bug.cgi?id=548397
844 */
845function getStyle( elem ) {
846 var style = getComputedStyle( elem );
847 if ( !style ) {
848 logError( 'Style returned ' + style +
849 '. Are you running this code in a hidden iframe on Firefox? ' +
850 'See https://bit.ly/getsizebug1' );
851 }
852 return style;
853}
854
855// -------------------------- setup -------------------------- //
856
857var isSetup = false;
858
859var isBoxSizeOuter;
860
861/**
862 * setup
863 * check isBoxSizerOuter
864 * do on first getSize() rather than on page load for Firefox bug
865 */
866function setup() {
867 // setup once
868 if ( isSetup ) {
869 return;
870 }
871 isSetup = true;
872
873 // -------------------------- box sizing -------------------------- //
874
875 /**
876 * Chrome & Safari measure the outer-width on style.width on border-box elems
877 * IE11 & Firefox<29 measures the inner-width
878 */
879 var div = document.createElement('div');
880 div.style.width = '200px';
881 div.style.padding = '1px 2px 3px 4px';
882 div.style.borderStyle = 'solid';
883 div.style.borderWidth = '1px 2px 3px 4px';
884 div.style.boxSizing = 'border-box';
885
886 var body = document.body || document.documentElement;
887 body.appendChild( div );
888 var style = getStyle( div );
889 // round value for browser zoom. desandro/masonry#928
890 isBoxSizeOuter = Math.round( getStyleSize( style.width ) ) == 200;
891 getSize.isBoxSizeOuter = isBoxSizeOuter;
892
893 body.removeChild( div );
894}
895
896// -------------------------- getSize -------------------------- //
897
898function getSize( elem ) {
899 setup();
900
901 // use querySeletor if elem is string
902 if ( typeof elem == 'string' ) {
903 elem = document.querySelector( elem );
904 }
905
906 // do not proceed on non-objects
907 if ( !elem || typeof elem != 'object' || !elem.nodeType ) {
908 return;
909 }
910
911 var style = getStyle( elem );
912
913 // if hidden, everything is 0
914 if ( style.display == 'none' ) {
915 return getZeroSize();
916 }
917
918 var size = {};
919 size.width = elem.offsetWidth;
920 size.height = elem.offsetHeight;
921
922 var isBorderBox = size.isBorderBox = style.boxSizing == 'border-box';
923
924 // get all measurements
925 for ( var i=0; i < measurementsLength; i++ ) {
926 var measurement = measurements[i];
927 var value = style[ measurement ];
928 var num = parseFloat( value );
929 // any 'auto', 'medium' value will be 0
930 size[ measurement ] = !isNaN( num ) ? num : 0;
931 }
932
933 var paddingWidth = size.paddingLeft + size.paddingRight;
934 var paddingHeight = size.paddingTop + size.paddingBottom;
935 var marginWidth = size.marginLeft + size.marginRight;
936 var marginHeight = size.marginTop + size.marginBottom;
937 var borderWidth = size.borderLeftWidth + size.borderRightWidth;
938 var borderHeight = size.borderTopWidth + size.borderBottomWidth;
939
940 var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter;
941
942 // overwrite width and height if we can get it from style
943 var styleWidth = getStyleSize( style.width );
944 if ( styleWidth !== false ) {
945 size.width = styleWidth +
946 // add padding and border unless it's already including it
947 ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth );
948 }
949
950 var styleHeight = getStyleSize( style.height );
951 if ( styleHeight !== false ) {
952 size.height = styleHeight +
953 // add padding and border unless it's already including it
954 ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight );
955 }
956
957 size.innerWidth = size.width - ( paddingWidth + borderWidth );
958 size.innerHeight = size.height - ( paddingHeight + borderHeight );
959
960 size.outerWidth = size.width + marginWidth;
961 size.outerHeight = size.height + marginHeight;
962
963 return size;
964}
965
966return getSize;
967
968});
969
970/**
971 * matchesSelector v2.0.2
972 * matchesSelector( element, '.selector' )
973 * MIT license
974 */
975
976/*jshint browser: true, strict: true, undef: true, unused: true */
977
978( function( window, factory ) {
979 /*global define: false, module: false */
980 'use strict';
981 // universal module definition
982 if ( typeof define == 'function' && define.amd ) {
983 // AMD
984 define( 'desandro-matches-selector/matches-selector',factory );
985 } else if ( typeof module == 'object' && module.exports ) {
986 // CommonJS
987 module.exports = factory();
988 } else {
989 // browser global
990 window.matchesSelector = factory();
991 }
992
993}( window, function factory() {
994 'use strict';
995
996 var matchesMethod = ( function() {
997 var ElemProto = window.Element.prototype;
998 // check for the standard method name first
999 if ( ElemProto.matches ) {
1000 return 'matches';
1001 }
1002 // check un-prefixed
1003 if ( ElemProto.matchesSelector ) {
1004 return 'matchesSelector';
1005 }
1006 // check vendor prefixes
1007 var prefixes = [ 'webkit', 'moz', 'ms', 'o' ];
1008
1009 for ( var i=0; i < prefixes.length; i++ ) {
1010 var prefix = prefixes[i];
1011 var method = prefix + 'MatchesSelector';
1012 if ( ElemProto[ method ] ) {
1013 return method;
1014 }
1015 }
1016 })();
1017
1018 return function matchesSelector( elem, selector ) {
1019 return elem[ matchesMethod ]( selector );
1020 };
1021
1022}));
1023
1024/**
1025 * Fizzy UI utils v2.0.7
1026 * MIT license
1027 */
1028
1029/*jshint browser: true, undef: true, unused: true, strict: true */
1030
1031( function( window, factory ) {
1032 // universal module definition
1033 /*jshint strict: false */ /*globals define, module, require */
1034
1035 if ( typeof define == 'function' && define.amd ) {
1036 // AMD
1037 define( 'fizzy-ui-utils/utils',[
1038 'desandro-matches-selector/matches-selector'
1039 ], function( matchesSelector ) {
1040 return factory( window, matchesSelector );
1041 });
1042 } else if ( typeof module == 'object' && module.exports ) {
1043 // CommonJS
1044 module.exports = factory(
1045 window,
1046 require('desandro-matches-selector')
1047 );
1048 } else {
1049 // browser global
1050 window.fizzyUIUtils = factory(
1051 window,
1052 window.matchesSelector
1053 );
1054 }
1055
1056}( window, function factory( window, matchesSelector ) {
1057
1058
1059
1060var utils = {};
1061
1062// ----- extend ----- //
1063
1064// extends objects
1065utils.extend = function( a, b ) {
1066 for ( var prop in b ) {
1067 a[ prop ] = b[ prop ];
1068 }
1069 return a;
1070};
1071
1072// ----- modulo ----- //
1073
1074utils.modulo = function( num, div ) {
1075 return ( ( num % div ) + div ) % div;
1076};
1077
1078// ----- makeArray ----- //
1079
1080var arraySlice = Array.prototype.slice;
1081
1082// turn element or nodeList into an array
1083utils.makeArray = function( obj ) {
1084 if ( Array.isArray( obj ) ) {
1085 // use object if already an array
1086 return obj;
1087 }
1088 // return empty array if undefined or null. #6
1089 if ( obj === null || obj === undefined ) {
1090 return [];
1091 }
1092
1093 var isArrayLike = typeof obj == 'object' && typeof obj.length == 'number';
1094 if ( isArrayLike ) {
1095 // convert nodeList to array
1096 return arraySlice.call( obj );
1097 }
1098
1099 // array of single index
1100 return [ obj ];
1101};
1102
1103// ----- removeFrom ----- //
1104
1105utils.removeFrom = function( ary, obj ) {
1106 var index = ary.indexOf( obj );
1107 if ( index != -1 ) {
1108 ary.splice( index, 1 );
1109 }
1110};
1111
1112// ----- getParent ----- //
1113
1114utils.getParent = function( elem, selector ) {
1115 while ( elem.parentNode && elem != document.body ) {
1116 elem = elem.parentNode;
1117 if ( matchesSelector( elem, selector ) ) {
1118 return elem;
1119 }
1120 }
1121};
1122
1123// ----- getQueryElement ----- //
1124
1125// use element as selector string
1126utils.getQueryElement = function( elem ) {
1127 if ( typeof elem == 'string' ) {
1128 return document.querySelector( elem );
1129 }
1130 return elem;
1131};
1132
1133// ----- handleEvent ----- //
1134
1135// enable .ontype to trigger from .addEventListener( elem, 'type' )
1136utils.handleEvent = function( event ) {
1137 var method = 'on' + event.type;
1138 if ( this[ method ] ) {
1139 this[ method ]( event );
1140 }
1141};
1142
1143// ----- filterFindElements ----- //
1144
1145utils.filterFindElements = function( elems, selector ) {
1146 // make array of elems
1147 elems = utils.makeArray( elems );
1148 var ffElems = [];
1149
1150 elems.forEach( function( elem ) {
1151 // check that elem is an actual element
1152 if ( !( elem instanceof HTMLElement ) ) {
1153 return;
1154 }
1155 // add elem if no selector
1156 if ( !selector ) {
1157 ffElems.push( elem );
1158 return;
1159 }
1160 // filter & find items if we have a selector
1161 // filter
1162 if ( matchesSelector( elem, selector ) ) {
1163 ffElems.push( elem );
1164 }
1165 // find children
1166 var childElems = elem.querySelectorAll( selector );
1167 // concat childElems to filterFound array
1168 for ( var i=0; i < childElems.length; i++ ) {
1169 ffElems.push( childElems[i] );
1170 }
1171 });
1172
1173 return ffElems;
1174};
1175
1176// ----- debounceMethod ----- //
1177
1178utils.debounceMethod = function( _class, methodName, threshold ) {
1179 threshold = threshold || 100;
1180 // original method
1181 var method = _class.prototype[ methodName ];
1182 var timeoutName = methodName + 'Timeout';
1183
1184 _class.prototype[ methodName ] = function() {
1185 var timeout = this[ timeoutName ];
1186 clearTimeout( timeout );
1187
1188 var args = arguments;
1189 var _this = this;
1190 this[ timeoutName ] = setTimeout( function() {
1191 method.apply( _this, args );
1192 delete _this[ timeoutName ];
1193 }, threshold );
1194 };
1195};
1196
1197// ----- docReady ----- //
1198
1199utils.docReady = function( callback ) {
1200 var readyState = document.readyState;
1201 if ( readyState == 'complete' || readyState == 'interactive' ) {
1202 // do async to allow for other scripts to run. metafizzy/flickity#441
1203 setTimeout( callback );
1204 } else {
1205 document.addEventListener( 'DOMContentLoaded', callback );
1206 }
1207};
1208
1209// ----- htmlInit ----- //
1210
1211// http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
1212utils.toDashed = function( str ) {
1213 return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
1214 return $1 + '-' + $2;
1215 }).toLowerCase();
1216};
1217
1218var console = window.console;
1219/**
1220 * allow user to initialize classes via [data-namespace] or .js-namespace class
1221 * htmlInit( Widget, 'widgetName' )
1222 * options are parsed from data-namespace-options
1223 */
1224utils.htmlInit = function( WidgetClass, namespace ) {
1225 utils.docReady( function() {
1226 var dashedNamespace = utils.toDashed( namespace );
1227 var dataAttr = 'data-' + dashedNamespace;
1228 var dataAttrElems = document.querySelectorAll( '[' + dataAttr + ']' );
1229 var jsDashElems = document.querySelectorAll( '.js-' + dashedNamespace );
1230 var elems = utils.makeArray( dataAttrElems )
1231 .concat( utils.makeArray( jsDashElems ) );
1232 var dataOptionsAttr = dataAttr + '-options';
1233 var jQuery = window.jQuery;
1234
1235 elems.forEach( function( elem ) {
1236 var attr = elem.getAttribute( dataAttr ) ||
1237 elem.getAttribute( dataOptionsAttr );
1238 var options;
1239 try {
1240 options = attr && JSON.parse( attr );
1241 } catch ( error ) {
1242 // log error, do not initialize
1243 if ( console ) {
1244 console.error( 'Error parsing ' + dataAttr + ' on ' + elem.className +
1245 ': ' + error );
1246 }
1247 return;
1248 }
1249 // initialize
1250 var instance = new WidgetClass( elem, options );
1251 // make available via $().data('namespace')
1252 if ( jQuery ) {
1253 jQuery.data( elem, namespace, instance );
1254 }
1255 });
1256
1257 });
1258};
1259
1260// ----- ----- //
1261
1262return utils;
1263
1264}));
1265
1266// Flickity.Cell
1267( function( window, factory ) {
1268 // universal module definition
1269 if ( typeof define == 'function' && define.amd ) {
1270 // AMD
1271 define( 'flickity/js/cell',[
1272 'get-size/get-size',
1273 ], function( getSize ) {
1274 return factory( window, getSize );
1275 } );
1276 } else if ( typeof module == 'object' && module.exports ) {
1277 // CommonJS
1278 module.exports = factory(
1279 window,
1280 require('get-size')
1281 );
1282 } else {
1283 // browser global
1284 window.Flickity = window.Flickity || {};
1285 window.Flickity.Cell = factory(
1286 window,
1287 window.getSize
1288 );
1289 }
1290
1291}( window, function factory( window, getSize ) {
1292
1293
1294
1295function Cell( elem, parent ) {
1296 this.element = elem;
1297 this.parent = parent;
1298
1299 this.create();
1300}
1301
1302var proto = Cell.prototype;
1303
1304proto.create = function() {
1305 this.element.style.position = 'absolute';
1306 this.element.setAttribute( 'aria-hidden', 'true' );
1307 this.x = 0;
1308 this.shift = 0;
1309};
1310
1311proto.destroy = function() {
1312 // reset style
1313 this.unselect();
1314 this.element.style.position = '';
1315 var side = this.parent.originSide;
1316 this.element.style[ side ] = '';
1317 this.element.removeAttribute('aria-hidden');
1318};
1319
1320proto.getSize = function() {
1321 this.size = getSize( this.element );
1322};
1323
1324proto.setPosition = function( x ) {
1325 this.x = x;
1326 this.updateTarget();
1327 this.renderPosition( x );
1328};
1329
1330// setDefaultTarget v1 method, backwards compatibility, remove in v3
1331proto.updateTarget = proto.setDefaultTarget = function() {
1332 var marginProperty = this.parent.originSide == 'left' ? 'marginLeft' : 'marginRight';
1333 this.target = this.x + this.size[ marginProperty ] +
1334 this.size.width * this.parent.cellAlign;
1335};
1336
1337proto.renderPosition = function( x ) {
1338 // render position of cell with in slider
1339 var side = this.parent.originSide;
1340 this.element.style[ side ] = this.parent.getPositionValue( x );
1341};
1342
1343proto.select = function() {
1344 this.element.classList.add('is-selected');
1345 this.element.removeAttribute('aria-hidden');
1346};
1347
1348proto.unselect = function() {
1349 this.element.classList.remove('is-selected');
1350 this.element.setAttribute( 'aria-hidden', 'true' );
1351};
1352
1353/**
1354 * @param {Integer} shift - 0, 1, or -1
1355 */
1356proto.wrapShift = function( shift ) {
1357 this.shift = shift;
1358 this.renderPosition( this.x + this.parent.slideableWidth * shift );
1359};
1360
1361proto.remove = function() {
1362 this.element.parentNode.removeChild( this.element );
1363};
1364
1365return Cell;
1366
1367} ) );
1368
1369// slide
1370( function( window, factory ) {
1371 // universal module definition
1372 if ( typeof define == 'function' && define.amd ) {
1373 // AMD
1374 define( 'flickity/js/slide',factory );
1375 } else if ( typeof module == 'object' && module.exports ) {
1376 // CommonJS
1377 module.exports = factory();
1378 } else {
1379 // browser global
1380 window.Flickity = window.Flickity || {};
1381 window.Flickity.Slide = factory();
1382 }
1383
1384}( window, function factory() {
1385'use strict';
1386
1387function Slide( parent ) {
1388 this.parent = parent;
1389 this.isOriginLeft = parent.originSide == 'left';
1390 this.cells = [];
1391 this.outerWidth = 0;
1392 this.height = 0;
1393}
1394
1395var proto = Slide.prototype;
1396
1397proto.addCell = function( cell ) {
1398 this.cells.push( cell );
1399 this.outerWidth += cell.size.outerWidth;
1400 this.height = Math.max( cell.size.outerHeight, this.height );
1401 // first cell stuff
1402 if ( this.cells.length == 1 ) {
1403 this.x = cell.x; // x comes from first cell
1404 var beginMargin = this.isOriginLeft ? 'marginLeft' : 'marginRight';
1405 this.firstMargin = cell.size[ beginMargin ];
1406 }
1407};
1408
1409proto.updateTarget = function() {
1410 var endMargin = this.isOriginLeft ? 'marginRight' : 'marginLeft';
1411 var lastCell = this.getLastCell();
1412 var lastMargin = lastCell ? lastCell.size[ endMargin ] : 0;
1413 var slideWidth = this.outerWidth - ( this.firstMargin + lastMargin );
1414 this.target = this.x + this.firstMargin + slideWidth * this.parent.cellAlign;
1415};
1416
1417proto.getLastCell = function() {
1418 return this.cells[ this.cells.length - 1 ];
1419};
1420
1421proto.select = function() {
1422 this.cells.forEach( function( cell ) {
1423 cell.select();
1424 } );
1425};
1426
1427proto.unselect = function() {
1428 this.cells.forEach( function( cell ) {
1429 cell.unselect();
1430 } );
1431};
1432
1433proto.getCellElements = function() {
1434 return this.cells.map( function( cell ) {
1435 return cell.element;
1436 } );
1437};
1438
1439return Slide;
1440
1441} ) );
1442
1443// animate
1444( function( window, factory ) {
1445 // universal module definition
1446 if ( typeof define == 'function' && define.amd ) {
1447 // AMD
1448 define( 'flickity/js/animate',[
1449 'fizzy-ui-utils/utils',
1450 ], function( utils ) {
1451 return factory( window, utils );
1452 } );
1453 } else if ( typeof module == 'object' && module.exports ) {
1454 // CommonJS
1455 module.exports = factory(
1456 window,
1457 require('fizzy-ui-utils')
1458 );
1459 } else {
1460 // browser global
1461 window.Flickity = window.Flickity || {};
1462 window.Flickity.animatePrototype = factory(
1463 window,
1464 window.fizzyUIUtils
1465 );
1466 }
1467
1468}( window, function factory( window, utils ) {
1469
1470
1471
1472// -------------------------- animate -------------------------- //
1473
1474var proto = {};
1475
1476proto.startAnimation = function() {
1477 if ( this.isAnimating ) {
1478 return;
1479 }
1480
1481 this.isAnimating = true;
1482 this.restingFrames = 0;
1483 this.animate();
1484};
1485
1486proto.animate = function() {
1487 this.applyDragForce();
1488 this.applySelectedAttraction();
1489
1490 var previousX = this.x;
1491
1492 this.integratePhysics();
1493 this.positionSlider();
1494 this.settle( previousX );
1495 // animate next frame
1496 if ( this.isAnimating ) {
1497 var _this = this;
1498 requestAnimationFrame( function animateFrame() {
1499 _this.animate();
1500 } );
1501 }
1502};
1503
1504proto.positionSlider = function() {
1505 var x = this.x;
1506 // wrap position around
1507 if ( this.options.wrapAround && this.cells.length > 1 ) {
1508 x = utils.modulo( x, this.slideableWidth );
1509 x -= this.slideableWidth;
1510 this.shiftWrapCells( x );
1511 }
1512
1513 this.setTranslateX( x, this.isAnimating );
1514 this.dispatchScrollEvent();
1515};
1516
1517proto.setTranslateX = function( x, is3d ) {
1518 x += this.cursorPosition;
1519 // reverse if right-to-left and using transform
1520 x = this.options.rightToLeft ? -x : x;
1521 var translateX = this.getPositionValue( x );
1522 // use 3D transforms for hardware acceleration on iOS
1523 // but use 2D when settled, for better font-rendering
1524 this.slider.style.transform = is3d ?
1525 'translate3d(' + translateX + ',0,0)' : 'translateX(' + translateX + ')';
1526};
1527
1528proto.dispatchScrollEvent = function() {
1529 var firstSlide = this.slides[0];
1530 if ( !firstSlide ) {
1531 return;
1532 }
1533 var positionX = -this.x - firstSlide.target;
1534 var progress = positionX / this.slidesWidth;
1535 this.dispatchEvent( 'scroll', null, [ progress, positionX ] );
1536};
1537
1538proto.positionSliderAtSelected = function() {
1539 if ( !this.cells.length ) {
1540 return;
1541 }
1542 this.x = -this.selectedSlide.target;
1543 this.velocity = 0; // stop wobble
1544 this.positionSlider();
1545};
1546
1547proto.getPositionValue = function( position ) {
1548 if ( this.options.percentPosition ) {
1549 // percent position, round to 2 digits, like 12.34%
1550 return ( Math.round( ( position / this.size.innerWidth ) * 10000 ) * 0.01 ) + '%';
1551 } else {
1552 // pixel positioning
1553 return Math.round( position ) + 'px';
1554 }
1555};
1556
1557proto.settle = function( previousX ) {
1558 // keep track of frames where x hasn't moved
1559 var isResting = !this.isPointerDown &&
1560 Math.round( this.x * 100 ) == Math.round( previousX * 100 );
1561 if ( isResting ) {
1562 this.restingFrames++;
1563 }
1564 // stop animating if resting for 3 or more frames
1565 if ( this.restingFrames > 2 ) {
1566 this.isAnimating = false;
1567 delete this.isFreeScrolling;
1568 // render position with translateX when settled
1569 this.positionSlider();
1570 this.dispatchEvent( 'settle', null, [ this.selectedIndex ] );
1571 }
1572};
1573
1574proto.shiftWrapCells = function( x ) {
1575 // shift before cells
1576 var beforeGap = this.cursorPosition + x;
1577 this._shiftCells( this.beforeShiftCells, beforeGap, -1 );
1578 // shift after cells
1579 var afterGap = this.size.innerWidth - ( x + this.slideableWidth + this.cursorPosition );
1580 this._shiftCells( this.afterShiftCells, afterGap, 1 );
1581};
1582
1583proto._shiftCells = function( cells, gap, shift ) {
1584 for ( var i = 0; i < cells.length; i++ ) {
1585 var cell = cells[i];
1586 var cellShift = gap > 0 ? shift : 0;
1587 cell.wrapShift( cellShift );
1588 gap -= cell.size.outerWidth;
1589 }
1590};
1591
1592proto._unshiftCells = function( cells ) {
1593 if ( !cells || !cells.length ) {
1594 return;
1595 }
1596 for ( var i = 0; i < cells.length; i++ ) {
1597 cells[i].wrapShift( 0 );
1598 }
1599};
1600
1601// -------------------------- physics -------------------------- //
1602
1603proto.integratePhysics = function() {
1604 this.x += this.velocity;
1605 this.velocity *= this.getFrictionFactor();
1606};
1607
1608proto.applyForce = function( force ) {
1609 this.velocity += force;
1610};
1611
1612proto.getFrictionFactor = function() {
1613 return 1 - this.options[ this.isFreeScrolling ? 'freeScrollFriction' : 'friction' ];
1614};
1615
1616proto.getRestingPosition = function() {
1617 // my thanks to Steven Wittens, who simplified this math greatly
1618 return this.x + this.velocity / ( 1 - this.getFrictionFactor() );
1619};
1620
1621proto.applyDragForce = function() {
1622 if ( !this.isDraggable || !this.isPointerDown ) {
1623 return;
1624 }
1625 // change the position to drag position by applying force
1626 var dragVelocity = this.dragX - this.x;
1627 var dragForce = dragVelocity - this.velocity;
1628 this.applyForce( dragForce );
1629};
1630
1631proto.applySelectedAttraction = function() {
1632 // do not attract if pointer down or no slides
1633 var dragDown = this.isDraggable && this.isPointerDown;
1634 if ( dragDown || this.isFreeScrolling || !this.slides.length ) {
1635 return;
1636 }
1637 var distance = this.selectedSlide.target * -1 - this.x;
1638 var force = distance * this.options.selectedAttraction;
1639 this.applyForce( force );
1640};
1641
1642return proto;
1643
1644} ) );
1645
1646// Flickity main
1647/* eslint-disable max-params */
1648( function( window, factory ) {
1649 // universal module definition
1650 if ( typeof define == 'function' && define.amd ) {
1651 // AMD
1652 define( 'flickity/js/flickity',[
1653 'ev-emitter/ev-emitter',
1654 'get-size/get-size',
1655 'fizzy-ui-utils/utils',
1656 './cell',
1657 './slide',
1658 './animate',
1659 ], function( EvEmitter, getSize, utils, Cell, Slide, animatePrototype ) {
1660 return factory( window, EvEmitter, getSize, utils, Cell, Slide, animatePrototype );
1661 } );
1662 } else if ( typeof module == 'object' && module.exports ) {
1663 // CommonJS
1664 module.exports = factory(
1665 window,
1666 require('ev-emitter'),
1667 require('get-size'),
1668 require('fizzy-ui-utils'),
1669 require('./cell'),
1670 require('./slide'),
1671 require('./animate')
1672 );
1673 } else {
1674 // browser global
1675 var _Flickity = window.Flickity;
1676
1677 window.Flickity = factory(
1678 window,
1679 window.EvEmitter,
1680 window.getSize,
1681 window.fizzyUIUtils,
1682 _Flickity.Cell,
1683 _Flickity.Slide,
1684 _Flickity.animatePrototype
1685 );
1686 }
1687
1688}( window, function factory( window, EvEmitter, getSize,
1689 utils, Cell, Slide, animatePrototype ) {
1690
1691/* eslint-enable max-params */
1692
1693
1694// vars
1695var jQuery = window.jQuery;
1696var getComputedStyle = window.getComputedStyle;
1697var console = window.console;
1698
1699function moveElements( elems, toElem ) {
1700 elems = utils.makeArray( elems );
1701 while ( elems.length ) {
1702 toElem.appendChild( elems.shift() );
1703 }
1704}
1705
1706// -------------------------- Flickity -------------------------- //
1707
1708// globally unique identifiers
1709var GUID = 0;
1710// internal store of all Flickity intances
1711var instances = {};
1712
1713function Flickity( element, options ) {
1714 var queryElement = utils.getQueryElement( element );
1715 if ( !queryElement ) {
1716 if ( console ) {
1717 console.error( 'Bad element for Flickity: ' + ( queryElement || element ) );
1718 }
1719 return;
1720 }
1721 this.element = queryElement;
1722 // do not initialize twice on same element
1723 if ( this.element.flickityGUID ) {
1724 var instance = instances[ this.element.flickityGUID ];
1725 if ( instance ) instance.option( options );
1726 return instance;
1727 }
1728
1729 // add jQuery
1730 if ( jQuery ) {
1731 this.$element = jQuery( this.element );
1732 }
1733 // options
1734 this.options = utils.extend( {}, this.constructor.defaults );
1735 this.option( options );
1736
1737 // kick things off
1738 this._create();
1739}
1740
1741Flickity.defaults = {
1742 accessibility: true,
1743 // adaptiveHeight: false,
1744 cellAlign: 'center',
1745 // cellSelector: undefined,
1746 // contain: false,
1747 freeScrollFriction: 0.075, // friction when free-scrolling
1748 friction: 0.28, // friction when selecting
1749 namespaceJQueryEvents: true,
1750 // initialIndex: 0,
1751 percentPosition: true,
1752 resize: true,
1753 selectedAttraction: 0.025,
1754 setGallerySize: true,
1755 // watchCSS: false,
1756 // wrapAround: false
1757};
1758
1759// hash of methods triggered on _create()
1760Flickity.createMethods = [];
1761
1762var proto = Flickity.prototype;
1763// inherit EventEmitter
1764utils.extend( proto, EvEmitter.prototype );
1765
1766proto._create = function() {
1767 // add id for Flickity.data
1768 var id = this.guid = ++GUID;
1769 this.element.flickityGUID = id; // expando
1770 instances[ id ] = this; // associate via id
1771 // initial properties
1772 this.selectedIndex = 0;
1773 // how many frames slider has been in same position
1774 this.restingFrames = 0;
1775 // initial physics properties
1776 this.x = 0;
1777 this.velocity = 0;
1778 this.originSide = this.options.rightToLeft ? 'right' : 'left';
1779 // create viewport & slider
1780 this.viewport = document.createElement('div');
1781 this.viewport.className = 'flickity-viewport';
1782 this._createSlider();
1783
1784 if ( this.options.resize || this.options.watchCSS ) {
1785 window.addEventListener( 'resize', this );
1786 }
1787
1788 // add listeners from on option
1789 for ( var eventName in this.options.on ) {
1790 var listener = this.options.on[ eventName ];
1791 this.on( eventName, listener );
1792 }
1793
1794 Flickity.createMethods.forEach( function( method ) {
1795 this[ method ]();
1796 }, this );
1797
1798 if ( this.options.watchCSS ) {
1799 this.watchCSS();
1800 } else {
1801 this.activate();
1802 }
1803
1804};
1805
1806/**
1807 * set options
1808 * @param {Object} opts - options to extend
1809 */
1810proto.option = function( opts ) {
1811 utils.extend( this.options, opts );
1812};
1813
1814proto.activate = function() {
1815 if ( this.isActive ) {
1816 return;
1817 }
1818 this.isActive = true;
1819 this.element.classList.add('flickity-enabled');
1820 if ( this.options.rightToLeft ) {
1821 this.element.classList.add('flickity-rtl');
1822 }
1823
1824 this.getSize();
1825 // move initial cell elements so they can be loaded as cells
1826 var cellElems = this._filterFindCellElements( this.element.children );
1827 moveElements( cellElems, this.slider );
1828 this.viewport.appendChild( this.slider );
1829 this.element.appendChild( this.viewport );
1830 // get cells from children
1831 this.reloadCells();
1832
1833 if ( this.options.accessibility ) {
1834 // allow element to focusable
1835 this.element.tabIndex = 0;
1836 // listen for key presses
1837 this.element.addEventListener( 'keydown', this );
1838 }
1839
1840 this.emitEvent('activate');
1841 this.selectInitialIndex();
1842 // flag for initial activation, for using initialIndex
1843 this.isInitActivated = true;
1844 // ready event. #493
1845 this.dispatchEvent('ready');
1846};
1847
1848// slider positions the cells
1849proto._createSlider = function() {
1850 // slider element does all the positioning
1851 var slider = document.createElement('div');
1852 slider.className = 'flickity-slider';
1853 slider.style[ this.originSide ] = 0;
1854 this.slider = slider;
1855};
1856
1857proto._filterFindCellElements = function( elems ) {
1858 return utils.filterFindElements( elems, this.options.cellSelector );
1859};
1860
1861// goes through all children
1862proto.reloadCells = function() {
1863 // collection of item elements
1864 this.cells = this._makeCells( this.slider.children );
1865 this.positionCells();
1866 this._getWrapShiftCells();
1867 this.setGallerySize();
1868};
1869
1870/**
1871 * turn elements into Flickity.Cells
1872 * @param {[Array, NodeList, HTMLElement]} elems - elements to make into cells
1873 * @returns {Array} items - collection of new Flickity Cells
1874 */
1875proto._makeCells = function( elems ) {
1876 var cellElems = this._filterFindCellElements( elems );
1877
1878 // create new Flickity for collection
1879 var cells = cellElems.map( function( cellElem ) {
1880 return new Cell( cellElem, this );
1881 }, this );
1882
1883 return cells;
1884};
1885
1886proto.getLastCell = function() {
1887 return this.cells[ this.cells.length - 1 ];
1888};
1889
1890proto.getLastSlide = function() {
1891 return this.slides[ this.slides.length - 1 ];
1892};
1893
1894// positions all cells
1895proto.positionCells = function() {
1896 // size all cells
1897 this._sizeCells( this.cells );
1898 // position all cells
1899 this._positionCells( 0 );
1900};
1901
1902/**
1903 * position certain cells
1904 * @param {Integer} index - which cell to start with
1905 */
1906proto._positionCells = function( index ) {
1907 index = index || 0;
1908 // also measure maxCellHeight
1909 // start 0 if positioning all cells
1910 this.maxCellHeight = index ? this.maxCellHeight || 0 : 0;
1911 var cellX = 0;
1912 // get cellX
1913 if ( index > 0 ) {
1914 var startCell = this.cells[ index - 1 ];
1915 cellX = startCell.x + startCell.size.outerWidth;
1916 }
1917 var len = this.cells.length;
1918 for ( var i = index; i < len; i++ ) {
1919 var cell = this.cells[i];
1920 cell.setPosition( cellX );
1921 cellX += cell.size.outerWidth;
1922 this.maxCellHeight = Math.max( cell.size.outerHeight, this.maxCellHeight );
1923 }
1924 // keep track of cellX for wrap-around
1925 this.slideableWidth = cellX;
1926 // slides
1927 this.updateSlides();
1928 // contain slides target
1929 this._containSlides();
1930 // update slidesWidth
1931 this.slidesWidth = len ? this.getLastSlide().target - this.slides[0].target : 0;
1932};
1933
1934/**
1935 * cell.getSize() on multiple cells
1936 * @param {Array} cells - cells to size
1937 */
1938proto._sizeCells = function( cells ) {
1939 cells.forEach( function( cell ) {
1940 cell.getSize();
1941 } );
1942};
1943
1944// -------------------------- -------------------------- //
1945
1946proto.updateSlides = function() {
1947 this.slides = [];
1948 if ( !this.cells.length ) {
1949 return;
1950 }
1951
1952 var slide = new Slide( this );
1953 this.slides.push( slide );
1954 var isOriginLeft = this.originSide == 'left';
1955 var nextMargin = isOriginLeft ? 'marginRight' : 'marginLeft';
1956
1957 var canCellFit = this._getCanCellFit();
1958
1959 this.cells.forEach( function( cell, i ) {
1960 // just add cell if first cell in slide
1961 if ( !slide.cells.length ) {
1962 slide.addCell( cell );
1963 return;
1964 }
1965
1966 var slideWidth = ( slide.outerWidth - slide.firstMargin ) +
1967 ( cell.size.outerWidth - cell.size[ nextMargin ] );
1968
1969 if ( canCellFit.call( this, i, slideWidth ) ) {
1970 slide.addCell( cell );
1971 } else {
1972 // doesn't fit, new slide
1973 slide.updateTarget();
1974
1975 slide = new Slide( this );
1976 this.slides.push( slide );
1977 slide.addCell( cell );
1978 }
1979 }, this );
1980 // last slide
1981 slide.updateTarget();
1982 // update .selectedSlide
1983 this.updateSelectedSlide();
1984};
1985
1986proto._getCanCellFit = function() {
1987 var groupCells = this.options.groupCells;
1988 if ( !groupCells ) {
1989 return function() {
1990 return false;
1991 };
1992 } else if ( typeof groupCells == 'number' ) {
1993 // group by number. 3 -> [0,1,2], [3,4,5], ...
1994 var number = parseInt( groupCells, 10 );
1995 return function( i ) {
1996 return ( i % number ) !== 0;
1997 };
1998 }
1999 // default, group by width of slide
2000 // parse '75%
2001 var percentMatch = typeof groupCells == 'string' &&
2002 groupCells.match( /^(\d+)%$/ );
2003 var percent = percentMatch ? parseInt( percentMatch[1], 10 ) / 100 : 1;
2004 return function( i, slideWidth ) {
2005 /* eslint-disable-next-line no-invalid-this */
2006 return slideWidth <= ( this.size.innerWidth + 1 ) * percent;
2007 };
2008};
2009
2010// alias _init for jQuery plugin .flickity()
2011proto._init =
2012proto.reposition = function() {
2013 this.positionCells();
2014 this.positionSliderAtSelected();
2015};
2016
2017proto.getSize = function() {
2018 this.size = getSize( this.element );
2019 this.setCellAlign();
2020 this.cursorPosition = this.size.innerWidth * this.cellAlign;
2021};
2022
2023var cellAlignShorthands = {
2024 // cell align, then based on origin side
2025 center: {
2026 left: 0.5,
2027 right: 0.5,
2028 },
2029 left: {
2030 left: 0,
2031 right: 1,
2032 },
2033 right: {
2034 right: 0,
2035 left: 1,
2036 },
2037};
2038
2039proto.setCellAlign = function() {
2040 var shorthand = cellAlignShorthands[ this.options.cellAlign ];
2041 this.cellAlign = shorthand ? shorthand[ this.originSide ] : this.options.cellAlign;
2042};
2043
2044proto.setGallerySize = function() {
2045 if ( this.options.setGallerySize ) {
2046 var height = this.options.adaptiveHeight && this.selectedSlide ?
2047 this.selectedSlide.height : this.maxCellHeight;
2048 this.viewport.style.height = height + 'px';
2049 }
2050};
2051
2052proto._getWrapShiftCells = function() {
2053 // only for wrap-around
2054 if ( !this.options.wrapAround ) {
2055 return;
2056 }
2057 // unshift previous cells
2058 this._unshiftCells( this.beforeShiftCells );
2059 this._unshiftCells( this.afterShiftCells );
2060 // get before cells
2061 // initial gap
2062 var gapX = this.cursorPosition;
2063 var cellIndex = this.cells.length - 1;
2064 this.beforeShiftCells = this._getGapCells( gapX, cellIndex, -1 );
2065 // get after cells
2066 // ending gap between last cell and end of gallery viewport
2067 gapX = this.size.innerWidth - this.cursorPosition;
2068 // start cloning at first cell, working forwards
2069 this.afterShiftCells = this._getGapCells( gapX, 0, 1 );
2070};
2071
2072proto._getGapCells = function( gapX, cellIndex, increment ) {
2073 // keep adding cells until the cover the initial gap
2074 var cells = [];
2075 while ( gapX > 0 ) {
2076 var cell = this.cells[ cellIndex ];
2077 if ( !cell ) {
2078 break;
2079 }
2080 cells.push( cell );
2081 cellIndex += increment;
2082 gapX -= cell.size.outerWidth;
2083 }
2084 return cells;
2085};
2086
2087// ----- contain ----- //
2088
2089// contain cell targets so no excess sliding
2090proto._containSlides = function() {
2091 if ( !this.options.contain || this.options.wrapAround || !this.cells.length ) {
2092 return;
2093 }
2094 var isRightToLeft = this.options.rightToLeft;
2095 var beginMargin = isRightToLeft ? 'marginRight' : 'marginLeft';
2096 var endMargin = isRightToLeft ? 'marginLeft' : 'marginRight';
2097 var contentWidth = this.slideableWidth - this.getLastCell().size[ endMargin ];
2098 // content is less than gallery size
2099 var isContentSmaller = contentWidth < this.size.innerWidth;
2100 // bounds
2101 var beginBound = this.cursorPosition + this.cells[0].size[ beginMargin ];
2102 var endBound = contentWidth - this.size.innerWidth * ( 1 - this.cellAlign );
2103 // contain each cell target
2104 this.slides.forEach( function( slide ) {
2105 if ( isContentSmaller ) {
2106 // all cells fit inside gallery
2107 slide.target = contentWidth * this.cellAlign;
2108 } else {
2109 // contain to bounds
2110 slide.target = Math.max( slide.target, beginBound );
2111 slide.target = Math.min( slide.target, endBound );
2112 }
2113 }, this );
2114};
2115
2116// ----- ----- //
2117
2118/**
2119 * emits events via eventEmitter and jQuery events
2120 * @param {String} type - name of event
2121 * @param {Event} event - original event
2122 * @param {Array} args - extra arguments
2123 */
2124proto.dispatchEvent = function( type, event, args ) {
2125 var emitArgs = event ? [ event ].concat( args ) : args;
2126 this.emitEvent( type, emitArgs );
2127
2128 if ( jQuery && this.$element ) {
2129 // default trigger with type if no event
2130 type += this.options.namespaceJQueryEvents ? '.flickity' : '';
2131 var $event = type;
2132 if ( event ) {
2133 // create jQuery event
2134 var jQEvent = new jQuery.Event( event );
2135 jQEvent.type = type;
2136 $event = jQEvent;
2137 }
2138 this.$element.trigger( $event, args );
2139 }
2140};
2141
2142// -------------------------- select -------------------------- //
2143
2144/**
2145 * @param {Integer} index - index of the slide
2146 * @param {Boolean} isWrap - will wrap-around to last/first if at the end
2147 * @param {Boolean} isInstant - will immediately set position at selected cell
2148 */
2149proto.select = function( index, isWrap, isInstant ) {
2150 if ( !this.isActive ) {
2151 return;
2152 }
2153 index = parseInt( index, 10 );
2154 this._wrapSelect( index );
2155
2156 if ( this.options.wrapAround || isWrap ) {
2157 index = utils.modulo( index, this.slides.length );
2158 }
2159 // bail if invalid index
2160 if ( !this.slides[ index ] ) {
2161 return;
2162 }
2163 var prevIndex = this.selectedIndex;
2164 this.selectedIndex = index;
2165 this.updateSelectedSlide();
2166 if ( isInstant ) {
2167 this.positionSliderAtSelected();
2168 } else {
2169 this.startAnimation();
2170 }
2171 if ( this.options.adaptiveHeight ) {
2172 this.setGallerySize();
2173 }
2174 // events
2175 this.dispatchEvent( 'select', null, [ index ] );
2176 // change event if new index
2177 if ( index != prevIndex ) {
2178 this.dispatchEvent( 'change', null, [ index ] );
2179 }
2180 // old v1 event name, remove in v3
2181 this.dispatchEvent('cellSelect');
2182};
2183
2184// wraps position for wrapAround, to move to closest slide. #113
2185proto._wrapSelect = function( index ) {
2186 var len = this.slides.length;
2187 var isWrapping = this.options.wrapAround && len > 1;
2188 if ( !isWrapping ) {
2189 return index;
2190 }
2191 var wrapIndex = utils.modulo( index, len );
2192 // go to shortest
2193 var delta = Math.abs( wrapIndex - this.selectedIndex );
2194 var backWrapDelta = Math.abs( ( wrapIndex + len ) - this.selectedIndex );
2195 var forewardWrapDelta = Math.abs( ( wrapIndex - len ) - this.selectedIndex );
2196 if ( !this.isDragSelect && backWrapDelta < delta ) {
2197 index += len;
2198 } else if ( !this.isDragSelect && forewardWrapDelta < delta ) {
2199 index -= len;
2200 }
2201 // wrap position so slider is within normal area
2202 if ( index < 0 ) {
2203 this.x -= this.slideableWidth;
2204 } else if ( index >= len ) {
2205 this.x += this.slideableWidth;
2206 }
2207};
2208
2209proto.previous = function( isWrap, isInstant ) {
2210 this.select( this.selectedIndex - 1, isWrap, isInstant );
2211};
2212
2213proto.next = function( isWrap, isInstant ) {
2214 this.select( this.selectedIndex + 1, isWrap, isInstant );
2215};
2216
2217proto.updateSelectedSlide = function() {
2218 var slide = this.slides[ this.selectedIndex ];
2219 // selectedIndex could be outside of slides, if triggered before resize()
2220 if ( !slide ) {
2221 return;
2222 }
2223 // unselect previous selected slide
2224 this.unselectSelectedSlide();
2225 // update new selected slide
2226 this.selectedSlide = slide;
2227 slide.select();
2228 this.selectedCells = slide.cells;
2229 this.selectedElements = slide.getCellElements();
2230 // HACK: selectedCell & selectedElement is first cell in slide, backwards compatibility
2231 // Remove in v3?
2232 this.selectedCell = slide.cells[0];
2233 this.selectedElement = this.selectedElements[0];
2234};
2235
2236proto.unselectSelectedSlide = function() {
2237 if ( this.selectedSlide ) {
2238 this.selectedSlide.unselect();
2239 }
2240};
2241
2242proto.selectInitialIndex = function() {
2243 var initialIndex = this.options.initialIndex;
2244 // already activated, select previous selectedIndex
2245 if ( this.isInitActivated ) {
2246 this.select( this.selectedIndex, false, true );
2247 return;
2248 }
2249 // select with selector string
2250 if ( initialIndex && typeof initialIndex == 'string' ) {
2251 var cell = this.queryCell( initialIndex );
2252 if ( cell ) {
2253 this.selectCell( initialIndex, false, true );
2254 return;
2255 }
2256 }
2257
2258 var index = 0;
2259 // select with number
2260 if ( initialIndex && this.slides[ initialIndex ] ) {
2261 index = initialIndex;
2262 }
2263 // select instantly
2264 this.select( index, false, true );
2265};
2266
2267/**
2268 * select slide from number or cell element
2269 * @param {[Element, Number]} value - zero-based index or element to select
2270 * @param {Boolean} isWrap - enables wrapping around for extra index
2271 * @param {Boolean} isInstant - disables slide animation
2272 */
2273proto.selectCell = function( value, isWrap, isInstant ) {
2274 // get cell
2275 var cell = this.queryCell( value );
2276 if ( !cell ) {
2277 return;
2278 }
2279
2280 var index = this.getCellSlideIndex( cell );
2281 this.select( index, isWrap, isInstant );
2282};
2283
2284proto.getCellSlideIndex = function( cell ) {
2285 // get index of slides that has cell
2286 for ( var i = 0; i < this.slides.length; i++ ) {
2287 var slide = this.slides[i];
2288 var index = slide.cells.indexOf( cell );
2289 if ( index != -1 ) {
2290 return i;
2291 }
2292 }
2293};
2294
2295// -------------------------- get cells -------------------------- //
2296
2297/**
2298 * get Flickity.Cell, given an Element
2299 * @param {Element} elem - matching cell element
2300 * @returns {Flickity.Cell} cell - matching cell
2301 */
2302proto.getCell = function( elem ) {
2303 // loop through cells to get the one that matches
2304 for ( var i = 0; i < this.cells.length; i++ ) {
2305 var cell = this.cells[i];
2306 if ( cell.element == elem ) {
2307 return cell;
2308 }
2309 }
2310};
2311
2312/**
2313 * get collection of Flickity.Cells, given Elements
2314 * @param {[Element, Array, NodeList]} elems - multiple elements
2315 * @returns {Array} cells - Flickity.Cells
2316 */
2317proto.getCells = function( elems ) {
2318 elems = utils.makeArray( elems );
2319 var cells = [];
2320 elems.forEach( function( elem ) {
2321 var cell = this.getCell( elem );
2322 if ( cell ) {
2323 cells.push( cell );
2324 }
2325 }, this );
2326 return cells;
2327};
2328
2329/**
2330 * get cell elements
2331 * @returns {Array} cellElems
2332 */
2333proto.getCellElements = function() {
2334 return this.cells.map( function( cell ) {
2335 return cell.element;
2336 } );
2337};
2338
2339/**
2340 * get parent cell from an element
2341 * @param {Element} elem - child element
2342 * @returns {Flickit.Cell} cell - parent cell
2343 */
2344proto.getParentCell = function( elem ) {
2345 // first check if elem is cell
2346 var cell = this.getCell( elem );
2347 if ( cell ) {
2348 return cell;
2349 }
2350 // try to get parent cell elem
2351 elem = utils.getParent( elem, '.flickity-slider > *' );
2352 return this.getCell( elem );
2353};
2354
2355/**
2356 * get cells adjacent to a slide
2357 * @param {Integer} adjCount - number of adjacent slides
2358 * @param {Integer} index - index of slide to start
2359 * @returns {Array} cells - array of Flickity.Cells
2360 */
2361proto.getAdjacentCellElements = function( adjCount, index ) {
2362 if ( !adjCount ) {
2363 return this.selectedSlide.getCellElements();
2364 }
2365 index = index === undefined ? this.selectedIndex : index;
2366
2367 var len = this.slides.length;
2368 if ( 1 + ( adjCount * 2 ) >= len ) {
2369 return this.getCellElements();
2370 }
2371
2372 var cellElems = [];
2373 for ( var i = index - adjCount; i <= index + adjCount; i++ ) {
2374 var slideIndex = this.options.wrapAround ? utils.modulo( i, len ) : i;
2375 var slide = this.slides[ slideIndex ];
2376 if ( slide ) {
2377 cellElems = cellElems.concat( slide.getCellElements() );
2378 }
2379 }
2380 return cellElems;
2381};
2382
2383/**
2384 * select slide from number or cell element
2385 * @param {[Element, String, Number]} selector - element, selector string, or index
2386 * @returns {Flickity.Cell} - matching cell
2387 */
2388proto.queryCell = function( selector ) {
2389 if ( typeof selector == 'number' ) {
2390 // use number as index
2391 return this.cells[ selector ];
2392 }
2393 if ( typeof selector == 'string' ) {
2394 // do not select invalid selectors from hash: #123, #/. #791
2395 if ( selector.match( /^[#.]?[\d/]/ ) ) {
2396 return;
2397 }
2398 // use string as selector, get element
2399 selector = this.element.querySelector( selector );
2400 }
2401 // get cell from element
2402 return this.getCell( selector );
2403};
2404
2405// -------------------------- events -------------------------- //
2406
2407proto.uiChange = function() {
2408 this.emitEvent('uiChange');
2409};
2410
2411// keep focus on element when child UI elements are clicked
2412proto.childUIPointerDown = function( event ) {
2413 // HACK iOS does not allow touch events to bubble up?!
2414 if ( event.type != 'touchstart' ) {
2415 event.preventDefault();
2416 }
2417 this.focus();
2418};
2419
2420// ----- resize ----- //
2421
2422proto.onresize = function() {
2423 this.watchCSS();
2424 this.resize();
2425};
2426
2427utils.debounceMethod( Flickity, 'onresize', 150 );
2428
2429proto.resize = function() {
2430 if ( !this.isActive ) {
2431 return;
2432 }
2433 this.getSize();
2434 // wrap values
2435 if ( this.options.wrapAround ) {
2436 this.x = utils.modulo( this.x, this.slideableWidth );
2437 }
2438 this.positionCells();
2439 this._getWrapShiftCells();
2440 this.setGallerySize();
2441 this.emitEvent('resize');
2442 // update selected index for group slides, instant
2443 // TODO: position can be lost between groups of various numbers
2444 var selectedElement = this.selectedElements && this.selectedElements[0];
2445 this.selectCell( selectedElement, false, true );
2446};
2447
2448// watches the :after property, activates/deactivates
2449proto.watchCSS = function() {
2450 var watchOption = this.options.watchCSS;
2451 if ( !watchOption ) {
2452 return;
2453 }
2454
2455 var afterContent = getComputedStyle( this.element, ':after' ).content;
2456 // activate if :after { content: 'flickity' }
2457 if ( afterContent.indexOf('flickity') != -1 ) {
2458 this.activate();
2459 } else {
2460 this.deactivate();
2461 }
2462};
2463
2464// ----- keydown ----- //
2465
2466// go previous/next if left/right keys pressed
2467proto.onkeydown = function( event ) {
2468 // only work if element is in focus
2469 var isNotFocused = document.activeElement && document.activeElement != this.element;
2470 if ( !this.options.accessibility || isNotFocused ) {
2471 return;
2472 }
2473
2474 var handler = Flickity.keyboardHandlers[ event.keyCode ];
2475 if ( handler ) {
2476 handler.call( this );
2477 }
2478};
2479
2480Flickity.keyboardHandlers = {
2481 // left arrow
2482 37: function() {
2483 var leftMethod = this.options.rightToLeft ? 'next' : 'previous';
2484 this.uiChange();
2485 this[ leftMethod ]();
2486 },
2487 // right arrow
2488 39: function() {
2489 var rightMethod = this.options.rightToLeft ? 'previous' : 'next';
2490 this.uiChange();
2491 this[ rightMethod ]();
2492 },
2493};
2494
2495// ----- focus ----- //
2496
2497proto.focus = function() {
2498 // TODO remove scrollTo once focus options gets more support
2499 // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus ...
2500 // #Browser_compatibility
2501 var prevScrollY = window.pageYOffset;
2502 this.element.focus({ preventScroll: true });
2503 // hack to fix scroll jump after focus, #76
2504 if ( window.pageYOffset != prevScrollY ) {
2505 window.scrollTo( window.pageXOffset, prevScrollY );
2506 }
2507};
2508
2509// -------------------------- destroy -------------------------- //
2510
2511// deactivate all Flickity functionality, but keep stuff available
2512proto.deactivate = function() {
2513 if ( !this.isActive ) {
2514 return;
2515 }
2516 this.element.classList.remove('flickity-enabled');
2517 this.element.classList.remove('flickity-rtl');
2518 this.unselectSelectedSlide();
2519 // destroy cells
2520 this.cells.forEach( function( cell ) {
2521 cell.destroy();
2522 } );
2523 this.element.removeChild( this.viewport );
2524 // move child elements back into element
2525 moveElements( this.slider.children, this.element );
2526 if ( this.options.accessibility ) {
2527 this.element.removeAttribute('tabIndex');
2528 this.element.removeEventListener( 'keydown', this );
2529 }
2530 // set flags
2531 this.isActive = false;
2532 this.emitEvent('deactivate');
2533};
2534
2535proto.destroy = function() {
2536 this.deactivate();
2537 window.removeEventListener( 'resize', this );
2538 this.allOff();
2539 this.emitEvent('destroy');
2540 if ( jQuery && this.$element ) {
2541 jQuery.removeData( this.element, 'flickity' );
2542 }
2543 delete this.element.flickityGUID;
2544 delete instances[ this.guid ];
2545};
2546
2547// -------------------------- prototype -------------------------- //
2548
2549utils.extend( proto, animatePrototype );
2550
2551// -------------------------- extras -------------------------- //
2552
2553/**
2554 * get Flickity instance from element
2555 * @param {[Element, String]} elem - element or selector string
2556 * @returns {Flickity} - Flickity instance
2557 */
2558Flickity.data = function( elem ) {
2559 elem = utils.getQueryElement( elem );
2560 var id = elem && elem.flickityGUID;
2561 return id && instances[ id ];
2562};
2563
2564utils.htmlInit( Flickity, 'flickity' );
2565
2566if ( jQuery && jQuery.bridget ) {
2567 jQuery.bridget( 'flickity', Flickity );
2568}
2569
2570// set internal jQuery, for Webpack + jQuery v3, #478
2571Flickity.setJQuery = function( jq ) {
2572 jQuery = jq;
2573};
2574
2575Flickity.Cell = Cell;
2576Flickity.Slide = Slide;
2577
2578return Flickity;
2579
2580} ) );
2581
2582/*!
2583 * Unipointer v2.3.0
2584 * base class for doing one thing with pointer event
2585 * MIT license
2586 */
2587
2588/*jshint browser: true, undef: true, unused: true, strict: true */
2589
2590( function( window, factory ) {
2591 // universal module definition
2592 /* jshint strict: false */ /*global define, module, require */
2593 if ( typeof define == 'function' && define.amd ) {
2594 // AMD
2595 define( 'unipointer/unipointer',[
2596 'ev-emitter/ev-emitter'
2597 ], function( EvEmitter ) {
2598 return factory( window, EvEmitter );
2599 });
2600 } else if ( typeof module == 'object' && module.exports ) {
2601 // CommonJS
2602 module.exports = factory(
2603 window,
2604 require('ev-emitter')
2605 );
2606 } else {
2607 // browser global
2608 window.Unipointer = factory(
2609 window,
2610 window.EvEmitter
2611 );
2612 }
2613
2614}( window, function factory( window, EvEmitter ) {
2615
2616
2617
2618function noop() {}
2619
2620function Unipointer() {}
2621
2622// inherit EvEmitter
2623var proto = Unipointer.prototype = Object.create( EvEmitter.prototype );
2624
2625proto.bindStartEvent = function( elem ) {
2626 this._bindStartEvent( elem, true );
2627};
2628
2629proto.unbindStartEvent = function( elem ) {
2630 this._bindStartEvent( elem, false );
2631};
2632
2633/**
2634 * Add or remove start event
2635 * @param {Boolean} isAdd - remove if falsey
2636 */
2637proto._bindStartEvent = function( elem, isAdd ) {
2638 // munge isAdd, default to true
2639 isAdd = isAdd === undefined ? true : isAdd;
2640 var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener';
2641
2642 // default to mouse events
2643 var startEvent = 'mousedown';
2644 if ( window.PointerEvent ) {
2645 // Pointer Events
2646 startEvent = 'pointerdown';
2647 } else if ( 'ontouchstart' in window ) {
2648 // Touch Events. iOS Safari
2649 startEvent = 'touchstart';
2650 }
2651 elem[ bindMethod ]( startEvent, this );
2652};
2653
2654// trigger handler methods for events
2655proto.handleEvent = function( event ) {
2656 var method = 'on' + event.type;
2657 if ( this[ method ] ) {
2658 this[ method ]( event );
2659 }
2660};
2661
2662// returns the touch that we're keeping track of
2663proto.getTouch = function( touches ) {
2664 for ( var i=0; i < touches.length; i++ ) {
2665 var touch = touches[i];
2666 if ( touch.identifier == this.pointerIdentifier ) {
2667 return touch;
2668 }
2669 }
2670};
2671
2672// ----- start event ----- //
2673
2674proto.onmousedown = function( event ) {
2675 // dismiss clicks from right or middle buttons
2676 var button = event.button;
2677 if ( button && ( button !== 0 && button !== 1 ) ) {
2678 return;
2679 }
2680 this._pointerDown( event, event );
2681};
2682
2683proto.ontouchstart = function( event ) {
2684 this._pointerDown( event, event.changedTouches[0] );
2685};
2686
2687proto.onpointerdown = function( event ) {
2688 this._pointerDown( event, event );
2689};
2690
2691/**
2692 * pointer start
2693 * @param {Event} event
2694 * @param {Event or Touch} pointer
2695 */
2696proto._pointerDown = function( event, pointer ) {
2697 // dismiss right click and other pointers
2698 // button = 0 is okay, 1-4 not
2699 if ( event.button || this.isPointerDown ) {
2700 return;
2701 }
2702
2703 this.isPointerDown = true;
2704 // save pointer identifier to match up touch events
2705 this.pointerIdentifier = pointer.pointerId !== undefined ?
2706 // pointerId for pointer events, touch.indentifier for touch events
2707 pointer.pointerId : pointer.identifier;
2708
2709 this.pointerDown( event, pointer );
2710};
2711
2712proto.pointerDown = function( event, pointer ) {
2713 this._bindPostStartEvents( event );
2714 this.emitEvent( 'pointerDown', [ event, pointer ] );
2715};
2716
2717// hash of events to be bound after start event
2718var postStartEvents = {
2719 mousedown: [ 'mousemove', 'mouseup' ],
2720 touchstart: [ 'touchmove', 'touchend', 'touchcancel' ],
2721 pointerdown: [ 'pointermove', 'pointerup', 'pointercancel' ],
2722};
2723
2724proto._bindPostStartEvents = function( event ) {
2725 if ( !event ) {
2726 return;
2727 }
2728 // get proper events to match start event
2729 var events = postStartEvents[ event.type ];
2730 // bind events to node
2731 events.forEach( function( eventName ) {
2732 window.addEventListener( eventName, this );
2733 }, this );
2734 // save these arguments
2735 this._boundPointerEvents = events;
2736};
2737
2738proto._unbindPostStartEvents = function() {
2739 // check for _boundEvents, in case dragEnd triggered twice (old IE8 bug)
2740 if ( !this._boundPointerEvents ) {
2741 return;
2742 }
2743 this._boundPointerEvents.forEach( function( eventName ) {
2744 window.removeEventListener( eventName, this );
2745 }, this );
2746
2747 delete this._boundPointerEvents;
2748};
2749
2750// ----- move event ----- //
2751
2752proto.onmousemove = function( event ) {
2753 this._pointerMove( event, event );
2754};
2755
2756proto.onpointermove = function( event ) {
2757 if ( event.pointerId == this.pointerIdentifier ) {
2758 this._pointerMove( event, event );
2759 }
2760};
2761
2762proto.ontouchmove = function( event ) {
2763 var touch = this.getTouch( event.changedTouches );
2764 if ( touch ) {
2765 this._pointerMove( event, touch );
2766 }
2767};
2768
2769/**
2770 * pointer move
2771 * @param {Event} event
2772 * @param {Event or Touch} pointer
2773 * @private
2774 */
2775proto._pointerMove = function( event, pointer ) {
2776 this.pointerMove( event, pointer );
2777};
2778
2779// public
2780proto.pointerMove = function( event, pointer ) {
2781 this.emitEvent( 'pointerMove', [ event, pointer ] );
2782};
2783
2784// ----- end event ----- //
2785
2786
2787proto.onmouseup = function( event ) {
2788 this._pointerUp( event, event );
2789};
2790
2791proto.onpointerup = function( event ) {
2792 if ( event.pointerId == this.pointerIdentifier ) {
2793 this._pointerUp( event, event );
2794 }
2795};
2796
2797proto.ontouchend = function( event ) {
2798 var touch = this.getTouch( event.changedTouches );
2799 if ( touch ) {
2800 this._pointerUp( event, touch );
2801 }
2802};
2803
2804/**
2805 * pointer up
2806 * @param {Event} event
2807 * @param {Event or Touch} pointer
2808 * @private
2809 */
2810proto._pointerUp = function( event, pointer ) {
2811 this._pointerDone();
2812 this.pointerUp( event, pointer );
2813};
2814
2815// public
2816proto.pointerUp = function( event, pointer ) {
2817 this.emitEvent( 'pointerUp', [ event, pointer ] );
2818};
2819
2820// ----- pointer done ----- //
2821
2822// triggered on pointer up & pointer cancel
2823proto._pointerDone = function() {
2824 this._pointerReset();
2825 this._unbindPostStartEvents();
2826 this.pointerDone();
2827};
2828
2829proto._pointerReset = function() {
2830 // reset properties
2831 this.isPointerDown = false;
2832 delete this.pointerIdentifier;
2833};
2834
2835proto.pointerDone = noop;
2836
2837// ----- pointer cancel ----- //
2838
2839proto.onpointercancel = function( event ) {
2840 if ( event.pointerId == this.pointerIdentifier ) {
2841 this._pointerCancel( event, event );
2842 }
2843};
2844
2845proto.ontouchcancel = function( event ) {
2846 var touch = this.getTouch( event.changedTouches );
2847 if ( touch ) {
2848 this._pointerCancel( event, touch );
2849 }
2850};
2851
2852/**
2853 * pointer cancel
2854 * @param {Event} event
2855 * @param {Event or Touch} pointer
2856 * @private
2857 */
2858proto._pointerCancel = function( event, pointer ) {
2859 this._pointerDone();
2860 this.pointerCancel( event, pointer );
2861};
2862
2863// public
2864proto.pointerCancel = function( event, pointer ) {
2865 this.emitEvent( 'pointerCancel', [ event, pointer ] );
2866};
2867
2868// ----- ----- //
2869
2870// utility function for getting x/y coords from event
2871Unipointer.getPointerPoint = function( pointer ) {
2872 return {
2873 x: pointer.pageX,
2874 y: pointer.pageY
2875 };
2876};
2877
2878// ----- ----- //
2879
2880return Unipointer;
2881
2882}));
2883
2884/*!
2885 * Unidragger v2.3.1
2886 * Draggable base class
2887 * MIT license
2888 */
2889
2890/*jshint browser: true, unused: true, undef: true, strict: true */
2891
2892( function( window, factory ) {
2893 // universal module definition
2894 /*jshint strict: false */ /*globals define, module, require */
2895
2896 if ( typeof define == 'function' && define.amd ) {
2897 // AMD
2898 define( 'unidragger/unidragger',[
2899 'unipointer/unipointer'
2900 ], function( Unipointer ) {
2901 return factory( window, Unipointer );
2902 });
2903 } else if ( typeof module == 'object' && module.exports ) {
2904 // CommonJS
2905 module.exports = factory(
2906 window,
2907 require('unipointer')
2908 );
2909 } else {
2910 // browser global
2911 window.Unidragger = factory(
2912 window,
2913 window.Unipointer
2914 );
2915 }
2916
2917}( window, function factory( window, Unipointer ) {
2918
2919
2920
2921// -------------------------- Unidragger -------------------------- //
2922
2923function Unidragger() {}
2924
2925// inherit Unipointer & EvEmitter
2926var proto = Unidragger.prototype = Object.create( Unipointer.prototype );
2927
2928// ----- bind start ----- //
2929
2930proto.bindHandles = function() {
2931 this._bindHandles( true );
2932};
2933
2934proto.unbindHandles = function() {
2935 this._bindHandles( false );
2936};
2937
2938/**
2939 * Add or remove start event
2940 * @param {Boolean} isAdd
2941 */
2942proto._bindHandles = function( isAdd ) {
2943 // munge isAdd, default to true
2944 isAdd = isAdd === undefined ? true : isAdd;
2945 // bind each handle
2946 var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener';
2947 var touchAction = isAdd ? this._touchActionValue : '';
2948 for ( var i=0; i < this.handles.length; i++ ) {
2949 var handle = this.handles[i];
2950 this._bindStartEvent( handle, isAdd );
2951 handle[ bindMethod ]( 'click', this );
2952 // touch-action: none to override browser touch gestures. metafizzy/flickity#540
2953 if ( window.PointerEvent ) {
2954 handle.style.touchAction = touchAction;
2955 }
2956 }
2957};
2958
2959// prototype so it can be overwriteable by Flickity
2960proto._touchActionValue = 'none';
2961
2962// ----- start event ----- //
2963
2964/**
2965 * pointer start
2966 * @param {Event} event
2967 * @param {Event or Touch} pointer
2968 */
2969proto.pointerDown = function( event, pointer ) {
2970 var isOkay = this.okayPointerDown( event );
2971 if ( !isOkay ) {
2972 return;
2973 }
2974 // track start event position
2975 // Safari 9 overrides pageX and pageY. These values needs to be copied. flickity#842
2976 this.pointerDownPointer = {
2977 pageX: pointer.pageX,
2978 pageY: pointer.pageY,
2979 };
2980
2981 event.preventDefault();
2982 this.pointerDownBlur();
2983 // bind move and end events
2984 this._bindPostStartEvents( event );
2985 this.emitEvent( 'pointerDown', [ event, pointer ] );
2986};
2987
2988// nodes that have text fields
2989var cursorNodes = {
2990 TEXTAREA: true,
2991 INPUT: true,
2992 SELECT: true,
2993 OPTION: true,
2994};
2995
2996// input types that do not have text fields
2997var clickTypes = {
2998 radio: true,
2999 checkbox: true,
3000 button: true,
3001 submit: true,
3002 image: true,
3003 file: true,
3004};
3005
3006// dismiss inputs with text fields. flickity#403, flickity#404
3007proto.okayPointerDown = function( event ) {
3008 var isCursorNode = cursorNodes[ event.target.nodeName ];
3009 var isClickType = clickTypes[ event.target.type ];
3010 var isOkay = !isCursorNode || isClickType;
3011 if ( !isOkay ) {
3012 this._pointerReset();
3013 }
3014 return isOkay;
3015};
3016
3017// kludge to blur previously focused input
3018proto.pointerDownBlur = function() {
3019 var focused = document.activeElement;
3020 // do not blur body for IE10, metafizzy/flickity#117
3021 var canBlur = focused && focused.blur && focused != document.body;
3022 if ( canBlur ) {
3023 focused.blur();
3024 }
3025};
3026
3027// ----- move event ----- //
3028
3029/**
3030 * drag move
3031 * @param {Event} event
3032 * @param {Event or Touch} pointer
3033 */
3034proto.pointerMove = function( event, pointer ) {
3035 var moveVector = this._dragPointerMove( event, pointer );
3036 this.emitEvent( 'pointerMove', [ event, pointer, moveVector ] );
3037 this._dragMove( event, pointer, moveVector );
3038};
3039
3040// base pointer move logic
3041proto._dragPointerMove = function( event, pointer ) {
3042 var moveVector = {
3043 x: pointer.pageX - this.pointerDownPointer.pageX,
3044 y: pointer.pageY - this.pointerDownPointer.pageY
3045 };
3046 // start drag if pointer has moved far enough to start drag
3047 if ( !this.isDragging && this.hasDragStarted( moveVector ) ) {
3048 this._dragStart( event, pointer );
3049 }
3050 return moveVector;
3051};
3052
3053// condition if pointer has moved far enough to start drag
3054proto.hasDragStarted = function( moveVector ) {
3055 return Math.abs( moveVector.x ) > 3 || Math.abs( moveVector.y ) > 3;
3056};
3057
3058// ----- end event ----- //
3059
3060/**
3061 * pointer up
3062 * @param {Event} event
3063 * @param {Event or Touch} pointer
3064 */
3065proto.pointerUp = function( event, pointer ) {
3066 this.emitEvent( 'pointerUp', [ event, pointer ] );
3067 this._dragPointerUp( event, pointer );
3068};
3069
3070proto._dragPointerUp = function( event, pointer ) {
3071 if ( this.isDragging ) {
3072 this._dragEnd( event, pointer );
3073 } else {
3074 // pointer didn't move enough for drag to start
3075 this._staticClick( event, pointer );
3076 }
3077};
3078
3079// -------------------------- drag -------------------------- //
3080
3081// dragStart
3082proto._dragStart = function( event, pointer ) {
3083 this.isDragging = true;
3084 // prevent clicks
3085 this.isPreventingClicks = true;
3086 this.dragStart( event, pointer );
3087};
3088
3089proto.dragStart = function( event, pointer ) {
3090 this.emitEvent( 'dragStart', [ event, pointer ] );
3091};
3092
3093// dragMove
3094proto._dragMove = function( event, pointer, moveVector ) {
3095 // do not drag if not dragging yet
3096 if ( !this.isDragging ) {
3097 return;
3098 }
3099
3100 this.dragMove( event, pointer, moveVector );
3101};
3102
3103proto.dragMove = function( event, pointer, moveVector ) {
3104 event.preventDefault();
3105 this.emitEvent( 'dragMove', [ event, pointer, moveVector ] );
3106};
3107
3108// dragEnd
3109proto._dragEnd = function( event, pointer ) {
3110 // set flags
3111 this.isDragging = false;
3112 // re-enable clicking async
3113 setTimeout( function() {
3114 delete this.isPreventingClicks;
3115 }.bind( this ) );
3116
3117 this.dragEnd( event, pointer );
3118};
3119
3120proto.dragEnd = function( event, pointer ) {
3121 this.emitEvent( 'dragEnd', [ event, pointer ] );
3122};
3123
3124// ----- onclick ----- //
3125
3126// handle all clicks and prevent clicks when dragging
3127proto.onclick = function( event ) {
3128 if ( this.isPreventingClicks ) {
3129 event.preventDefault();
3130 }
3131};
3132
3133// ----- staticClick ----- //
3134
3135// triggered after pointer down & up with no/tiny movement
3136proto._staticClick = function( event, pointer ) {
3137 // ignore emulated mouse up clicks
3138 if ( this.isIgnoringMouseUp && event.type == 'mouseup' ) {
3139 return;
3140 }
3141
3142 this.staticClick( event, pointer );
3143
3144 // set flag for emulated clicks 300ms after touchend
3145 if ( event.type != 'mouseup' ) {
3146 this.isIgnoringMouseUp = true;
3147 // reset flag after 300ms
3148 setTimeout( function() {
3149 delete this.isIgnoringMouseUp;
3150 }.bind( this ), 400 );
3151 }
3152};
3153
3154proto.staticClick = function( event, pointer ) {
3155 this.emitEvent( 'staticClick', [ event, pointer ] );
3156};
3157
3158// ----- utils ----- //
3159
3160Unidragger.getPointerPoint = Unipointer.getPointerPoint;
3161
3162// ----- ----- //
3163
3164return Unidragger;
3165
3166}));
3167
3168// drag
3169( function( window, factory ) {
3170 // universal module definition
3171 if ( typeof define == 'function' && define.amd ) {
3172 // AMD
3173 define( 'flickity/js/drag',[
3174 './flickity',
3175 'unidragger/unidragger',
3176 'fizzy-ui-utils/utils',
3177 ], function( Flickity, Unidragger, utils ) {
3178 return factory( window, Flickity, Unidragger, utils );
3179 } );
3180 } else if ( typeof module == 'object' && module.exports ) {
3181 // CommonJS
3182 module.exports = factory(
3183 window,
3184 require('./flickity'),
3185 require('unidragger'),
3186 require('fizzy-ui-utils')
3187 );
3188 } else {
3189 // browser global
3190 window.Flickity = factory(
3191 window,
3192 window.Flickity,
3193 window.Unidragger,
3194 window.fizzyUIUtils
3195 );
3196 }
3197
3198}( window, function factory( window, Flickity, Unidragger, utils ) {
3199
3200
3201
3202// ----- defaults ----- //
3203
3204utils.extend( Flickity.defaults, {
3205 draggable: '>1',
3206 dragThreshold: 3,
3207} );
3208
3209// ----- create ----- //
3210
3211Flickity.createMethods.push('_createDrag');
3212
3213// -------------------------- drag prototype -------------------------- //
3214
3215var proto = Flickity.prototype;
3216utils.extend( proto, Unidragger.prototype );
3217proto._touchActionValue = 'pan-y';
3218
3219// -------------------------- -------------------------- //
3220
3221var isTouch = 'createTouch' in document;
3222var isTouchmoveScrollCanceled = false;
3223
3224proto._createDrag = function() {
3225 this.on( 'activate', this.onActivateDrag );
3226 this.on( 'uiChange', this._uiChangeDrag );
3227 this.on( 'deactivate', this.onDeactivateDrag );
3228 this.on( 'cellChange', this.updateDraggable );
3229 // TODO updateDraggable on resize? if groupCells & slides change
3230 // HACK - add seemingly innocuous handler to fix iOS 10 scroll behavior
3231 // #457, RubaXa/Sortable#973
3232 if ( isTouch && !isTouchmoveScrollCanceled ) {
3233 window.addEventListener( 'touchmove', function() {} );
3234 isTouchmoveScrollCanceled = true;
3235 }
3236};
3237
3238proto.onActivateDrag = function() {
3239 this.handles = [ this.viewport ];
3240 this.bindHandles();
3241 this.updateDraggable();
3242};
3243
3244proto.onDeactivateDrag = function() {
3245 this.unbindHandles();
3246 this.element.classList.remove('is-draggable');
3247};
3248
3249proto.updateDraggable = function() {
3250 // disable dragging if less than 2 slides. #278
3251 if ( this.options.draggable == '>1' ) {
3252 this.isDraggable = this.slides.length > 1;
3253 } else {
3254 this.isDraggable = this.options.draggable;
3255 }
3256 if ( this.isDraggable ) {
3257 this.element.classList.add('is-draggable');
3258 } else {
3259 this.element.classList.remove('is-draggable');
3260 }
3261};
3262
3263// backwards compatibility
3264proto.bindDrag = function() {
3265 this.options.draggable = true;
3266 this.updateDraggable();
3267};
3268
3269proto.unbindDrag = function() {
3270 this.options.draggable = false;
3271 this.updateDraggable();
3272};
3273
3274proto._uiChangeDrag = function() {
3275 delete this.isFreeScrolling;
3276};
3277
3278// -------------------------- pointer events -------------------------- //
3279
3280proto.pointerDown = function( event, pointer ) {
3281 if ( !this.isDraggable ) {
3282 this._pointerDownDefault( event, pointer );
3283 return;
3284 }
3285 var isOkay = this.okayPointerDown( event );
3286 if ( !isOkay ) {
3287 return;
3288 }
3289
3290 this._pointerDownPreventDefault( event );
3291 this.pointerDownFocus( event );
3292 // blur
3293 if ( document.activeElement != this.element ) {
3294 // do not blur if already focused
3295 this.pointerDownBlur();
3296 }
3297
3298 // stop if it was moving
3299 this.dragX = this.x;
3300 this.viewport.classList.add('is-pointer-down');
3301 // track scrolling
3302 this.pointerDownScroll = getScrollPosition();
3303 window.addEventListener( 'scroll', this );
3304
3305 this._pointerDownDefault( event, pointer );
3306};
3307
3308// default pointerDown logic, used for staticClick
3309proto._pointerDownDefault = function( event, pointer ) {
3310 // track start event position
3311 // Safari 9 overrides pageX and pageY. These values needs to be copied. #779
3312 this.pointerDownPointer = {
3313 pageX: pointer.pageX,
3314 pageY: pointer.pageY,
3315 };
3316 // bind move and end events
3317 this._bindPostStartEvents( event );
3318 this.dispatchEvent( 'pointerDown', event, [ pointer ] );
3319};
3320
3321var focusNodes = {
3322 INPUT: true,
3323 TEXTAREA: true,
3324 SELECT: true,
3325};
3326
3327proto.pointerDownFocus = function( event ) {
3328 var isFocusNode = focusNodes[ event.target.nodeName ];
3329 if ( !isFocusNode ) {
3330 this.focus();
3331 }
3332};
3333
3334proto._pointerDownPreventDefault = function( event ) {
3335 var isTouchStart = event.type == 'touchstart';
3336 var isTouchPointer = event.pointerType == 'touch';
3337 var isFocusNode = focusNodes[ event.target.nodeName ];
3338 if ( !isTouchStart && !isTouchPointer && !isFocusNode ) {
3339 event.preventDefault();
3340 }
3341};
3342
3343// ----- move ----- //
3344
3345proto.hasDragStarted = function( moveVector ) {
3346 return Math.abs( moveVector.x ) > this.options.dragThreshold;
3347};
3348
3349// ----- up ----- //
3350
3351proto.pointerUp = function( event, pointer ) {
3352 delete this.isTouchScrolling;
3353 this.viewport.classList.remove('is-pointer-down');
3354 this.dispatchEvent( 'pointerUp', event, [ pointer ] );
3355 this._dragPointerUp( event, pointer );
3356};
3357
3358proto.pointerDone = function() {
3359 window.removeEventListener( 'scroll', this );
3360 delete this.pointerDownScroll;
3361};
3362
3363// -------------------------- dragging -------------------------- //
3364
3365proto.dragStart = function( event, pointer ) {
3366 if ( !this.isDraggable ) {
3367 return;
3368 }
3369 this.dragStartPosition = this.x;
3370 this.startAnimation();
3371 window.removeEventListener( 'scroll', this );
3372 this.dispatchEvent( 'dragStart', event, [ pointer ] );
3373};
3374
3375proto.pointerMove = function( event, pointer ) {
3376 var moveVector = this._dragPointerMove( event, pointer );
3377 this.dispatchEvent( 'pointerMove', event, [ pointer, moveVector ] );
3378 this._dragMove( event, pointer, moveVector );
3379};
3380
3381proto.dragMove = function( event, pointer, moveVector ) {
3382 if ( !this.isDraggable ) {
3383 return;
3384 }
3385 event.preventDefault();
3386
3387 this.previousDragX = this.dragX;
3388 // reverse if right-to-left
3389 var direction = this.options.rightToLeft ? -1 : 1;
3390 if ( this.options.wrapAround ) {
3391 // wrap around move. #589
3392 moveVector.x %= this.slideableWidth;
3393 }
3394 var dragX = this.dragStartPosition + moveVector.x * direction;
3395
3396 if ( !this.options.wrapAround && this.slides.length ) {
3397 // slow drag
3398 var originBound = Math.max( -this.slides[0].target, this.dragStartPosition );
3399 dragX = dragX > originBound ? ( dragX + originBound ) * 0.5 : dragX;
3400 var endBound = Math.min( -this.getLastSlide().target, this.dragStartPosition );
3401 dragX = dragX < endBound ? ( dragX + endBound ) * 0.5 : dragX;
3402 }
3403
3404 this.dragX = dragX;
3405
3406 this.dragMoveTime = new Date();
3407 this.dispatchEvent( 'dragMove', event, [ pointer, moveVector ] );
3408};
3409
3410proto.dragEnd = function( event, pointer ) {
3411 if ( !this.isDraggable ) {
3412 return;
3413 }
3414 if ( this.options.freeScroll ) {
3415 this.isFreeScrolling = true;
3416 }
3417 // set selectedIndex based on where flick will end up
3418 var index = this.dragEndRestingSelect();
3419
3420 if ( this.options.freeScroll && !this.options.wrapAround ) {
3421 // if free-scroll & not wrap around
3422 // do not free-scroll if going outside of bounding slides
3423 // so bounding slides can attract slider, and keep it in bounds
3424 var restingX = this.getRestingPosition();
3425 this.isFreeScrolling = -restingX > this.slides[0].target &&
3426 -restingX < this.getLastSlide().target;
3427 } else if ( !this.options.freeScroll && index == this.selectedIndex ) {
3428 // boost selection if selected index has not changed
3429 index += this.dragEndBoostSelect();
3430 }
3431 delete this.previousDragX;
3432 // apply selection
3433 // TODO refactor this, selecting here feels weird
3434 // HACK, set flag so dragging stays in correct direction
3435 this.isDragSelect = this.options.wrapAround;
3436 this.select( index );
3437 delete this.isDragSelect;
3438 this.dispatchEvent( 'dragEnd', event, [ pointer ] );
3439};
3440
3441proto.dragEndRestingSelect = function() {
3442 var restingX = this.getRestingPosition();
3443 // how far away from selected slide
3444 var distance = Math.abs( this.getSlideDistance( -restingX, this.selectedIndex ) );
3445 // get closet resting going up and going down
3446 var positiveResting = this._getClosestResting( restingX, distance, 1 );
3447 var negativeResting = this._getClosestResting( restingX, distance, -1 );
3448 // use closer resting for wrap-around
3449 var index = positiveResting.distance < negativeResting.distance ?
3450 positiveResting.index : negativeResting.index;
3451 return index;
3452};
3453
3454/**
3455 * given resting X and distance to selected cell
3456 * get the distance and index of the closest cell
3457 * @param {Number} restingX - estimated post-flick resting position
3458 * @param {Number} distance - distance to selected cell
3459 * @param {Integer} increment - +1 or -1, going up or down
3460 * @returns {Object} - { distance: {Number}, index: {Integer} }
3461 */
3462proto._getClosestResting = function( restingX, distance, increment ) {
3463 var index = this.selectedIndex;
3464 var minDistance = Infinity;
3465 var condition = this.options.contain && !this.options.wrapAround ?
3466 // if contain, keep going if distance is equal to minDistance
3467 function( dist, minDist ) {
3468 return dist <= minDist;
3469 } : function( dist, minDist ) {
3470 return dist < minDist;
3471 };
3472 while ( condition( distance, minDistance ) ) {
3473 // measure distance to next cell
3474 index += increment;
3475 minDistance = distance;
3476 distance = this.getSlideDistance( -restingX, index );
3477 if ( distance === null ) {
3478 break;
3479 }
3480 distance = Math.abs( distance );
3481 }
3482 return {
3483 distance: minDistance,
3484 // selected was previous index
3485 index: index - increment,
3486 };
3487};
3488
3489/**
3490 * measure distance between x and a slide target
3491 * @param {Number} x - horizontal position
3492 * @param {Integer} index - slide index
3493 * @returns {Number} - slide distance
3494 */
3495proto.getSlideDistance = function( x, index ) {
3496 var len = this.slides.length;
3497 // wrap around if at least 2 slides
3498 var isWrapAround = this.options.wrapAround && len > 1;
3499 var slideIndex = isWrapAround ? utils.modulo( index, len ) : index;
3500 var slide = this.slides[ slideIndex ];
3501 if ( !slide ) {
3502 return null;
3503 }
3504 // add distance for wrap-around slides
3505 var wrap = isWrapAround ? this.slideableWidth * Math.floor( index/len ) : 0;
3506 return x - ( slide.target + wrap );
3507};
3508
3509proto.dragEndBoostSelect = function() {
3510 // do not boost if no previousDragX or dragMoveTime
3511 if ( this.previousDragX === undefined || !this.dragMoveTime ||
3512 // or if drag was held for 100 ms
3513 new Date() - this.dragMoveTime > 100 ) {
3514 return 0;
3515 }
3516
3517 var distance = this.getSlideDistance( -this.dragX, this.selectedIndex );
3518 var delta = this.previousDragX - this.dragX;
3519 if ( distance > 0 && delta > 0 ) {
3520 // boost to next if moving towards the right, and positive velocity
3521 return 1;
3522 } else if ( distance < 0 && delta < 0 ) {
3523 // boost to previous if moving towards the left, and negative velocity
3524 return -1;
3525 }
3526 return 0;
3527};
3528
3529// ----- staticClick ----- //
3530
3531proto.staticClick = function( event, pointer ) {
3532 // get clickedCell, if cell was clicked
3533 var clickedCell = this.getParentCell( event.target );
3534 var cellElem = clickedCell && clickedCell.element;
3535 var cellIndex = clickedCell && this.cells.indexOf( clickedCell );
3536 this.dispatchEvent( 'staticClick', event, [ pointer, cellElem, cellIndex ] );
3537};
3538
3539// ----- scroll ----- //
3540
3541proto.onscroll = function() {
3542 var scroll = getScrollPosition();
3543 var scrollMoveX = this.pointerDownScroll.x - scroll.x;
3544 var scrollMoveY = this.pointerDownScroll.y - scroll.y;
3545 // cancel click/tap if scroll is too much
3546 if ( Math.abs( scrollMoveX ) > 3 || Math.abs( scrollMoveY ) > 3 ) {
3547 this._pointerDone();
3548 }
3549};
3550
3551// ----- utils ----- //
3552
3553function getScrollPosition() {
3554 return {
3555 x: window.pageXOffset,
3556 y: window.pageYOffset,
3557 };
3558}
3559
3560// ----- ----- //
3561
3562return Flickity;
3563
3564} ) );
3565
3566// prev/next buttons
3567( function( window, factory ) {
3568 // universal module definition
3569 if ( typeof define == 'function' && define.amd ) {
3570 // AMD
3571 define( 'flickity/js/prev-next-button',[
3572 './flickity',
3573 'unipointer/unipointer',
3574 'fizzy-ui-utils/utils',
3575 ], function( Flickity, Unipointer, utils ) {
3576 return factory( window, Flickity, Unipointer, utils );
3577 } );
3578 } else if ( typeof module == 'object' && module.exports ) {
3579 // CommonJS
3580 module.exports = factory(
3581 window,
3582 require('./flickity'),
3583 require('unipointer'),
3584 require('fizzy-ui-utils')
3585 );
3586 } else {
3587 // browser global
3588 factory(
3589 window,
3590 window.Flickity,
3591 window.Unipointer,
3592 window.fizzyUIUtils
3593 );
3594 }
3595
3596}( window, function factory( window, Flickity, Unipointer, utils ) {
3597'use strict';
3598
3599var svgURI = 'http://www.w3.org/2000/svg';
3600
3601// -------------------------- PrevNextButton -------------------------- //
3602
3603function PrevNextButton( direction, parent ) {
3604 this.direction = direction;
3605 this.parent = parent;
3606 this._create();
3607}
3608
3609PrevNextButton.prototype = Object.create( Unipointer.prototype );
3610
3611PrevNextButton.prototype._create = function() {
3612 // properties
3613 this.isEnabled = true;
3614 this.isPrevious = this.direction == -1;
3615 var leftDirection = this.parent.options.rightToLeft ? 1 : -1;
3616 this.isLeft = this.direction == leftDirection;
3617
3618 var element = this.element = document.createElement('button');
3619 element.className = 'flickity-button flickity-prev-next-button';
3620 element.className += this.isPrevious ? ' previous' : ' next';
3621 // prevent button from submitting form http://stackoverflow.com/a/10836076/182183
3622 element.setAttribute( 'type', 'button' );
3623 // init as disabled
3624 this.disable();
3625
3626 element.setAttribute( 'aria-label', this.isPrevious ? 'Previous' : 'Next' );
3627
3628 // create arrow
3629 var svg = this.createSVG();
3630 element.appendChild( svg );
3631 // events
3632 this.parent.on( 'select', this.update.bind( this ) );
3633 this.on( 'pointerDown', this.parent.childUIPointerDown.bind( this.parent ) );
3634};
3635
3636PrevNextButton.prototype.activate = function() {
3637 this.bindStartEvent( this.element );
3638 this.element.addEventListener( 'click', this );
3639 // add to DOM
3640 this.parent.element.appendChild( this.element );
3641};
3642
3643PrevNextButton.prototype.deactivate = function() {
3644 // remove from DOM
3645 this.parent.element.removeChild( this.element );
3646 // click events
3647 this.unbindStartEvent( this.element );
3648 this.element.removeEventListener( 'click', this );
3649};
3650
3651PrevNextButton.prototype.createSVG = function() {
3652 var svg = document.createElementNS( svgURI, 'svg' );
3653 svg.setAttribute( 'class', 'flickity-button-icon' );
3654 svg.setAttribute( 'viewBox', '0 0 100 100' );
3655 var path = document.createElementNS( svgURI, 'path' );
3656 var pathMovements = getArrowMovements( this.parent.options.arrowShape );
3657 path.setAttribute( 'd', pathMovements );
3658 path.setAttribute( 'class', 'arrow' );
3659 // rotate arrow
3660 if ( !this.isLeft ) {
3661 path.setAttribute( 'transform', 'translate(100, 100) rotate(180) ' );
3662 }
3663 svg.appendChild( path );
3664 return svg;
3665};
3666
3667// get SVG path movmement
3668function getArrowMovements( shape ) {
3669 // use shape as movement if string
3670 if ( typeof shape == 'string' ) {
3671 return shape;
3672 }
3673 // create movement string
3674 return 'M ' + shape.x0 + ',50' +
3675 ' L ' + shape.x1 + ',' + ( shape.y1 + 50 ) +
3676 ' L ' + shape.x2 + ',' + ( shape.y2 + 50 ) +
3677 ' L ' + shape.x3 + ',50 ' +
3678 ' L ' + shape.x2 + ',' + ( 50 - shape.y2 ) +
3679 ' L ' + shape.x1 + ',' + ( 50 - shape.y1 ) +
3680 ' Z';
3681}
3682
3683PrevNextButton.prototype.handleEvent = utils.handleEvent;
3684
3685PrevNextButton.prototype.onclick = function() {
3686 if ( !this.isEnabled ) {
3687 return;
3688 }
3689 this.parent.uiChange();
3690 var method = this.isPrevious ? 'previous' : 'next';
3691 this.parent[ method ]();
3692};
3693
3694// ----- ----- //
3695
3696PrevNextButton.prototype.enable = function() {
3697 if ( this.isEnabled ) {
3698 return;
3699 }
3700 this.element.disabled = false;
3701 this.isEnabled = true;
3702};
3703
3704PrevNextButton.prototype.disable = function() {
3705 if ( !this.isEnabled ) {
3706 return;
3707 }
3708 this.element.disabled = true;
3709 this.isEnabled = false;
3710};
3711
3712PrevNextButton.prototype.update = function() {
3713 // index of first or last slide, if previous or next
3714 var slides = this.parent.slides;
3715 // enable is wrapAround and at least 2 slides
3716 if ( this.parent.options.wrapAround && slides.length > 1 ) {
3717 this.enable();
3718 return;
3719 }
3720 var lastIndex = slides.length ? slides.length - 1 : 0;
3721 var boundIndex = this.isPrevious ? 0 : lastIndex;
3722 var method = this.parent.selectedIndex == boundIndex ? 'disable' : 'enable';
3723 this[ method ]();
3724};
3725
3726PrevNextButton.prototype.destroy = function() {
3727 this.deactivate();
3728 this.allOff();
3729};
3730
3731// -------------------------- Flickity prototype -------------------------- //
3732
3733utils.extend( Flickity.defaults, {
3734 prevNextButtons: true,
3735 arrowShape: {
3736 x0: 10,
3737 x1: 60, y1: 50,
3738 x2: 70, y2: 40,
3739 x3: 30,
3740 },
3741} );
3742
3743Flickity.createMethods.push('_createPrevNextButtons');
3744var proto = Flickity.prototype;
3745
3746proto._createPrevNextButtons = function() {
3747 if ( !this.options.prevNextButtons ) {
3748 return;
3749 }
3750
3751 this.prevButton = new PrevNextButton( -1, this );
3752 this.nextButton = new PrevNextButton( 1, this );
3753
3754 this.on( 'activate', this.activatePrevNextButtons );
3755};
3756
3757proto.activatePrevNextButtons = function() {
3758 this.prevButton.activate();
3759 this.nextButton.activate();
3760 this.on( 'deactivate', this.deactivatePrevNextButtons );
3761};
3762
3763proto.deactivatePrevNextButtons = function() {
3764 this.prevButton.deactivate();
3765 this.nextButton.deactivate();
3766 this.off( 'deactivate
The following has evaluated to null or missing: ==> urlimagen [in template "20102#20129#12795723" at line 58, column 26] ---- Tip: If the failing expression is known to legally refer to something that's sometimes null or missing, either specify a default value like myOptionalVar!myDefault, or use <#if myOptionalVar??>when-present<#else>when-missing</#if>. (These only cover the last step of the expression; to cover the whole expression, use parenthesis: (myOptionalVar.foo)!myDefault, (myOptionalVar.foo)?? ---- ---- FTL stack trace ("~" means nesting-related): - Failed at: #if urlimagen.url?has_content [in template "20102#20129#12795723" at line 58, column 21] ----
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 urlimagenNode = docXml.valueOf("//dynamic-element[@name='IMAGEN']/dynamic-content")/>
24
25
26
27 <#assign titulo = docXml.valueOf("//dynamic-element[@name='Titulopub']/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 <#if urlimagenNode?has_content>
51 <#assign urlimagenString = urlimagenNode?string />
52 <#assign urlimagen = urlimagenString?eval />
53 </#if>
54
55 <div class="gallery-cell${nameInstancePublisher}">
56 <div class="card-info${nameInstancePublisher}" id="card-info">
57 <div class="card-img${nameInstancePublisher}" id="card-img">
58 <#if urlimagen.url?has_content>
59 <img alt="${urlimagen.alt}" src='${urlimagen.url}' title="${titulo}">
60 <#else>
61 <img alt='${urlimagen["alt"]}' src='/documents/${urlimagen["groupId"]}/${urlimagen["classPK"]}/${urlimagen["name"]}/${urlimagen["uuid"]}' title="${titulo}">
62 </#if>
63 <#if assetRenderer.hasEditPermission(themeDisplay.getPermissionChecker())>
64 <#assign editPortletURL = assetRenderer.getURLEdit(renderRequest, renderResponse, windowStateFactory.getWindowState("NORMAL"), themeDisplay.getURLCurrent())!"" />
65 <#if validator.isNotNull(editPortletURL)>
66 <a class="editOptionmas" href="${editPortletURL.toString()}">Editar ✐</a>
67 </#if>
68 </#if>
69 </div>
70 <div class="card-txt${nameInstancePublisher}" id="card-txt">
71 <div class="card-title${nameInstancePublisher}" id="card-title">${titulo}</div>
72 <div class="card-lead${nameInstancePublisher}" id="card-lead">${contenido}</div>
73 <div class="card-btn${nameInstancePublisher}" id="card-btn">
74
75 <a class="link${nameInstancePublisher}" href="${linkext}" target="_blank">
76 Ver más
77 </a>
78
79 </div>
80 </div>
81
82 </div>
83 </div>
84 </#list>
85</div>
86
87
88
89
90
91<!---------------------- ESTLOS BASICOS ------------------------>
92
93 <style>
94
95 .cards-cont${nameInstancePublisher} {
96 width: 100%;
97 display: flex;
98 justify-content: center;
99 flex-wrap: wrap;
100
101 }
102
103
104 .card-info${nameInstancePublisher} {
105
106 width: 250px;
107 height: 350px;
108 margin: 15px;
109 display:flex;
110 align-content:center;
111 align-items:center;
112 flex-wrap:wrap;
113 }
114
115 .card-img${nameInstancePublisher} {
116 width: 250px;
117 height: 350px;
118 border-radius: 10px;
119 -webkit-box-shadow: 0px 2px 17px 1px rgb(0 0 0 / 23%);
120 box-shadow: 0px 2px 17px 1px rgb(0 0 0 / 23%);
121 box-sizing: border-box;
122 background-position: center;
123 background-size: cover;
124 -webkit-transition: all 500ms ease-in-out; // IE 9
125 -moz-transition: all 500ms ease-in-out; // Firefox
126 -ms-transition: all 500ms ease-in-out; // Safari and Chrome
127 -o-transition: all 500ms ease-in-out; // Opera
128 transition: all 500ms ease-in-out;
129 position:relative;
130 display:flex;
131 }
132
133 .card-info${nameInstancePublisher}:hover .card-img${nameInstancePublisher} {
134 width: 250px;
135 height: 350px;
136 border-radius: 10px;
137 box-sizing: border-box;
138 background-position: center;
139 background-size: cover;
140 -moz-transform: scale(1.02);
141 -webkit-transform: scale(1.02);
142 -o-transform: scale(1.02);
143 -ms-transform: scale(1.02);
144 transform: scale(1.02);
145 }
146
147 .card-img${nameInstancePublisher} img{
148 vertical-align: middle;
149 border-style: none;
150 width: 100%;
151 height: 350px;
152 object-fit: cover;
153 object-position: center;
154 border-radius: 10px;
155 }
156
157 .card-txt${nameInstancePublisher} {
158 position: absolute;
159 display:flex;
160 padding: 20px;
161 color: #FFF;
162 opacity: 1;
163 transition: all 500ms ease-in-out;
164 width: 90%;
165 flex-wrap:wrap;
166 max-width: 250px;
167 }
168 #card-txt{
169 display:none;
170 opacity:0;
171 transition: all 1s;
172 -webkit-transition: all 1s;
173 }
174
175 #card-info:hover #card-txt{
176 display:flex;
177 opacity:1;
178 transition: all 1s;
179 -webkit-transition: all 1s;
180 }
181
182 #card-info:hover #card-img{filter: brightness(0.3);}
183
184 .card-title${nameInstancePublisher} {
185 font-family: HelveticaBold;
186 font-size: 1rem;
187 transition: all 500ms ease-in-out;
188 width:100%;
189 }
190
191 .card-lead${nameInstancePublisher} {
192 font-family: HelveticaLight;
193 font-size: 0.9rem;
194 transition: all 500ms ease-in-out;
195 width: 100%;
196 }
197
198 .card-btn${nameInstancePublisher} {
199 padding: 5px;
200 font-family: HelveticaLight;
201 border: 1px solid #fff;
202 width: auto;
203 font-size: 0.8rem;
204 margin-top: 10px;
205 display: flex;
206 border-radius: 10px;
207 text-align: center;
208 /* margin: 0 auto; */
209 /* align-content: center; */
210 justify-content: center;
211 cursor: pointer;
212 opacity: 1;
213 }
214
215 .card-info${nameInstancePublisher}:hover .card-btn${nameInstancePublisher}{
216 background-color:#FFF;
217 color:#494949;
218 width: auto;
219 padding: 5px 10px;
220 border-radius: 5px;
221 }
222
223 .card-btn${nameInstancePublisher} a{
224 color:#FFF;
225 text-decoration:none;
226 }
227
228 .card-info${nameInstancePublisher}:hover .card-btn${nameInstancePublisher} a{
229 color:#494949;
230
231 }
232
233
234 .editOptionmas{
235 position: absolute;
236 background-color: #173268;
237 padding: 2px 5px;
238 color: #FFF;
239 font-family:HelveticaLight;
240 display:flex;
241 flex-wrap:nowrap;
242 width: 85px;
243 top:0px;
244 justify-content: center;
245 border-radius:10px 0px 10px 0px;
246 }
247
248 .editOptionmas:hover{
249 text-decoration:none;
250 color:#FFF;
251 font-family:HelveticaBold;
252 }
253
254 </style>
255
256 <style>
257 .gallery${nameInstancePublisher} {
258
259 margin: 0 auto;
260 width: 97%;
261 max-width:1200px;
262 margin-bottom:30px;
263 height:420px;
264 }
265
266 .gallery-cell${nameInstancePublisher} {
267 width: 22%;
268 height: auto;
269 margin-right: 10px;
270 counter-increment: gallery-cell;
271 }
272 /* cell number */
273
274 .gallery-cell${nameInstancePublisher}:before {
275 display: block;
276 text-align: center;
277 /content: counter(gallery-cell);/
278 line-height: 200px;
279 font-size: 80px;
280 color: white;
281 }
282
283 @media screen and (max-width: 640px) {
284 .gallery-cell${nameInstancePublisher} {
285 width: 100%;
286 height: auto;
287 margin-right: 10px;
288 counter-increment: gallery-cell;
289 }
290 }
291
292
293 .flickity-page-dots .dot.is-selected {
294 opacity: 1;
295 background: #2c5697;
296 }
297
298 .flickity-page-dots .dot {
299 display: inline-block;
300 width: 8px!important;
301 height: 8px!important;
302 margin: 0 5px!important;
303 background: #494949;
304 border-radius: 50%;
305 opacity: 0.25;
306 cursor: pointer;
307 }
308
309 .flickity-page-dots {
310 position: absolute;
311 width: 100%;
312 bottom: 0px!important;
313 padding: 0;
314 margin: 0;
315 list-style: none;
316 text-align: center;
317 line-height: 1;
318 margin-bottom: 0px;
319 }
320
321 .flickity-prev-next-button.previous {
322 left: -50px!important;
323 }
324
325 .flickity-prev-next-button.next {
326 right: -50px!important;
327 }
328
329 @media screen and (max-width: 640px) {
330 .flickity-prev-next-button.previous {
331 left: 10px!important;
332 }
333
334 .flickity-prev-next-button.next {
335 right: 10px!important;
336 }
337
338 .flickity-prev-next-button {
339 top: 25%!important;
340 width: 44px;
341 height: 44px;
342 border-radius: 50%;
343 transform: translateY(-50%);
344 }
345}
346
347
348 </style>
349
350<!------------------------------------------------------- ESTILOS DEL SCRIPT ---------------------------------------------->
351
352
353<style>
354
355.flickity-enabled {
356 position: relative;
357}
358
359.flickity-enabled:focus { outline: none; }
360
361.flickity-viewport {
362 overflow: hidden;
363 position: relative;
364 height: 100%;
365}
366
367.flickity-slider {
368 position: absolute;
369 width: 100%;
370 height: 100%;
371}
372
373/* draggable */
374
375.flickity-enabled.is-draggable {
376 -webkit-tap-highlight-color: transparent;
377 -webkit-user-select: none;
378 -moz-user-select: none;
379 -ms-user-select: none;
380 user-select: none;
381}
382
383.flickity-enabled.is-draggable .flickity-viewport {
384 cursor: move;
385 cursor: -webkit-grab;
386 cursor: grab;
387}
388
389.flickity-enabled.is-draggable .flickity-viewport.is-pointer-down {
390 cursor: -webkit-grabbing;
391 cursor: grabbing;
392}
393
394/* ---- flickity-button ---- */
395
396.flickity-button {
397 position: absolute;
398 background: hsla(0, 0%, 100%, 0.75);
399 border: none;
400 color: #333;
401}
402
403.flickity-button:hover {
404 background: white;
405 cursor: pointer;
406}
407
408.flickity-button:focus {
409 outline: none;
410 box-shadow: 0 0 0 5px #19F;
411}
412
413.flickity-button:active {
414 opacity: 0.6;
415}
416
417.flickity-button:disabled {
418 opacity: 0.3;
419 cursor: auto;
420 /* prevent disabled button from capturing pointer up event. #716 */
421 pointer-events: none;
422}
423
424.flickity-button-icon {
425 fill: currentColor;
426}
427
428/* ---- previous/next buttons ---- */
429
430.flickity-prev-next-button {
431 top: 50%;
432 width: 44px;
433 height: 44px;
434 border-radius: 50%;
435 /* vertically center */
436 transform: translateY(-50%);
437}
438
439.flickity-prev-next-button.previous { left: 10px; }
440.flickity-prev-next-button.next { right: 10px; }
441/* right to left */
442.flickity-rtl .flickity-prev-next-button.previous {
443 left: auto;
444 right: 10px;
445}
446.flickity-rtl .flickity-prev-next-button.next {
447 right: auto;
448 left: 10px;
449}
450
451.flickity-prev-next-button .flickity-button-icon {
452 position: absolute;
453 left: 20%;
454 top: 20%;
455 width: 60%;
456 height: 60%;
457}
458
459/* ---- page dots ---- */
460
461.flickity-page-dots {
462 position: absolute;
463 width: 100%;
464 bottom: -25px;
465 padding: 0;
466 margin: 0;
467 list-style: none;
468 text-align: center;
469 line-height: 1;
470}
471
472.flickity-rtl .flickity-page-dots { direction: rtl; }
473
474.flickity-page-dots .dot {
475 display: inline-block;
476 width: 10px;
477 height: 10px;
478 margin: 0 8px;
479 background: #333;
480 border-radius: 50%;
481 opacity: 0.25;
482 cursor: pointer;
483}
484
485.flickity-page-dots .dot.is-selected {
486 opacity: 1;
487}
488 </style>
489
490
491<!------------------------------------------------------------- SCRIPT PRINCIPAL -------------------------------------------------------------------------->
492
493<script>
494 /*!
495 * Flickity PACKAGED v2.2.2
496 * Touch, responsive, flickable carousels
497 *
498 * Licensed GPLv3 for open source use
499 * or Flickity Commercial License for commercial use
500 *
501 * https://flickity.metafizzy.co
502 * Copyright 2015-2021 Metafizzy
503 */
504
505/**
506 * Bridget makes jQuery widgets
507 * v2.0.1
508 * MIT license
509 */
510
511/* jshint browser: true, strict: true, undef: true, unused: true */
512
513( function( window, factory ) {
514 // universal module definition
515 /*jshint strict: false */ /* globals define, module, require */
516 if ( typeof define == 'function' && define.amd ) {
517 // AMD
518 define( 'jquery-bridget/jquery-bridget',[ 'jquery' ], function( jQuery ) {
519 return factory( window, jQuery );
520 });
521 } else if ( typeof module == 'object' && module.exports ) {
522 // CommonJS
523 module.exports = factory(
524 window,
525 require('jquery')
526 );
527 } else {
528 // browser global
529 window.jQueryBridget = factory(
530 window,
531 window.jQuery
532 );
533 }
534
535}( window, function factory( window, jQuery ) {
536'use strict';
537
538// ----- utils ----- //
539
540var arraySlice = Array.prototype.slice;
541
542// helper function for logging errors
543// $.error breaks jQuery chaining
544var console = window.console;
545var logError = typeof console == 'undefined' ? function() {} :
546 function( message ) {
547 console.error( message );
548 };
549
550// ----- jQueryBridget ----- //
551
552function jQueryBridget( namespace, PluginClass, $ ) {
553 $ = $ || jQuery || window.jQuery;
554 if ( !$ ) {
555 return;
556 }
557
558 // add option method -> $().plugin('option', {...})
559 if ( !PluginClass.prototype.option ) {
560 // option setter
561 PluginClass.prototype.option = function( opts ) {
562 // bail out if not an object
563 if ( !$.isPlainObject( opts ) ){
564 return;
565 }
566 this.options = $.extend( true, this.options, opts );
567 };
568 }
569
570 // make jQuery plugin
571 $.fn[ namespace ] = function( arg0 /*, arg1 */ ) {
572 if ( typeof arg0 == 'string' ) {
573 // method call $().plugin( 'methodName', { options } )
574 // shift arguments by 1
575 var args = arraySlice.call( arguments, 1 );
576 return methodCall( this, arg0, args );
577 }
578 // just $().plugin({ options })
579 plainCall( this, arg0 );
580 return this;
581 };
582
583 // $().plugin('methodName')
584 function methodCall( $elems, methodName, args ) {
585 var returnValue;
586 var pluginMethodStr = '$().' + namespace + '("' + methodName + '")';
587
588 $elems.each( function( i, elem ) {
589 // get instance
590 var instance = $.data( elem, namespace );
591 if ( !instance ) {
592 logError( namespace + ' not initialized. Cannot call methods, i.e. ' +
593 pluginMethodStr );
594 return;
595 }
596
597 var method = instance[ methodName ];
598 if ( !method || methodName.charAt(0) == '_' ) {
599 logError( pluginMethodStr + ' is not a valid method' );
600 return;
601 }
602
603 // apply method, get return value
604 var value = method.apply( instance, args );
605 // set return value if value is returned, use only first value
606 returnValue = returnValue === undefined ? value : returnValue;
607 });
608
609 return returnValue !== undefined ? returnValue : $elems;
610 }
611
612 function plainCall( $elems, options ) {
613 $elems.each( function( i, elem ) {
614 var instance = $.data( elem, namespace );
615 if ( instance ) {
616 // set options & init
617 instance.option( options );
618 instance._init();
619 } else {
620 // initialize new instance
621 instance = new PluginClass( elem, options );
622 $.data( elem, namespace, instance );
623 }
624 });
625 }
626
627 updateJQuery( $ );
628
629}
630
631// ----- updateJQuery ----- //
632
633// set $.bridget for v1 backwards compatibility
634function updateJQuery( $ ) {
635 if ( !$ || ( $ && $.bridget ) ) {
636 return;
637 }
638 $.bridget = jQueryBridget;
639}
640
641updateJQuery( jQuery || window.jQuery );
642
643// ----- ----- //
644
645return jQueryBridget;
646
647}));
648
649/**
650 * EvEmitter v1.1.0
651 * Lil' event emitter
652 * MIT License
653 */
654
655/* jshint unused: true, undef: true, strict: true */
656
657( function( global, factory ) {
658 // universal module definition
659 /* jshint strict: false */ /* globals define, module, window */
660 if ( typeof define == 'function' && define.amd ) {
661 // AMD - RequireJS
662 define( 'ev-emitter/ev-emitter',factory );
663 } else if ( typeof module == 'object' && module.exports ) {
664 // CommonJS - Browserify, Webpack
665 module.exports = factory();
666 } else {
667 // Browser globals
668 global.EvEmitter = factory();
669 }
670
671}( typeof window != 'undefined' ? window : this, function() {
672
673
674
675function EvEmitter() {}
676
677var proto = EvEmitter.prototype;
678
679proto.on = function( eventName, listener ) {
680 if ( !eventName || !listener ) {
681 return;
682 }
683 // set events hash
684 var events = this._events = this._events || {};
685 // set listeners array
686 var listeners = events[ eventName ] = events[ eventName ] || [];
687 // only add once
688 if ( listeners.indexOf( listener ) == -1 ) {
689 listeners.push( listener );
690 }
691
692 return this;
693};
694
695proto.once = function( eventName, listener ) {
696 if ( !eventName || !listener ) {
697 return;
698 }
699 // add event
700 this.on( eventName, listener );
701 // set once flag
702 // set onceEvents hash
703 var onceEvents = this._onceEvents = this._onceEvents || {};
704 // set onceListeners object
705 var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {};
706 // set flag
707 onceListeners[ listener ] = true;
708
709 return this;
710};
711
712proto.off = function( eventName, listener ) {
713 var listeners = this._events && this._events[ eventName ];
714 if ( !listeners || !listeners.length ) {
715 return;
716 }
717 var index = listeners.indexOf( listener );
718 if ( index != -1 ) {
719 listeners.splice( index, 1 );
720 }
721
722 return this;
723};
724
725proto.emitEvent = function( eventName, args ) {
726 var listeners = this._events && this._events[ eventName ];
727 if ( !listeners || !listeners.length ) {
728 return;
729 }
730 // copy over to avoid interference if .off() in listener
731 listeners = listeners.slice(0);
732 args = args || [];
733 // once stuff
734 var onceListeners = this._onceEvents && this._onceEvents[ eventName ];
735
736 for ( var i=0; i < listeners.length; i++ ) {
737 var listener = listeners[i]
738 var isOnce = onceListeners && onceListeners[ listener ];
739 if ( isOnce ) {
740 // remove listener
741 // remove before trigger to prevent recursion
742 this.off( eventName, listener );
743 // unset once flag
744 delete onceListeners[ listener ];
745 }
746 // trigger listener
747 listener.apply( this, args );
748 }
749
750 return this;
751};
752
753proto.allOff = function() {
754 delete this._events;
755 delete this._onceEvents;
756};
757
758return EvEmitter;
759
760}));
761
762/*!
763 * getSize v2.0.3
764 * measure size of elements
765 * MIT license
766 */
767
768/* jshint browser: true, strict: true, undef: true, unused: true */
769/* globals console: false */
770
771( function( window, factory ) {
772 /* jshint strict: false */ /* globals define, module */
773 if ( typeof define == 'function' && define.amd ) {
774 // AMD
775 define( 'get-size/get-size',factory );
776 } else if ( typeof module == 'object' && module.exports ) {
777 // CommonJS
778 module.exports = factory();
779 } else {
780 // browser global
781 window.getSize = factory();
782 }
783
784})( window, function factory() {
785'use strict';
786
787// -------------------------- helpers -------------------------- //
788
789// get a number from a string, not a percentage
790function getStyleSize( value ) {
791 var num = parseFloat( value );
792 // not a percent like '100%', and a number
793 var isValid = value.indexOf('%') == -1 && !isNaN( num );
794 return isValid && num;
795}
796
797function noop() {}
798
799var logError = typeof console == 'undefined' ? noop :
800 function( message ) {
801 console.error( message );
802 };
803
804// -------------------------- measurements -------------------------- //
805
806var measurements = [
807 'paddingLeft',
808 'paddingRight',
809 'paddingTop',
810 'paddingBottom',
811 'marginLeft',
812 'marginRight',
813 'marginTop',
814 'marginBottom',
815 'borderLeftWidth',
816 'borderRightWidth',
817 'borderTopWidth',
818 'borderBottomWidth'
819];
820
821var measurementsLength = measurements.length;
822
823function getZeroSize() {
824 var size = {
825 width: 0,
826 height: 0,
827 innerWidth: 0,
828 innerHeight: 0,
829 outerWidth: 0,
830 outerHeight: 0
831 };
832 for ( var i=0; i < measurementsLength; i++ ) {
833 var measurement = measurements[i];
834 size[ measurement ] = 0;
835 }
836 return size;
837}
838
839// -------------------------- getStyle -------------------------- //
840
841/**
842 * getStyle, get style of element, check for Firefox bug
843 * https://bugzilla.mozilla.org/show_bug.cgi?id=548397
844 */
845function getStyle( elem ) {
846 var style = getComputedStyle( elem );
847 if ( !style ) {
848 logError( 'Style returned ' + style +
849 '. Are you running this code in a hidden iframe on Firefox? ' +
850 'See https://bit.ly/getsizebug1' );
851 }
852 return style;
853}
854
855// -------------------------- setup -------------------------- //
856
857var isSetup = false;
858
859var isBoxSizeOuter;
860
861/**
862 * setup
863 * check isBoxSizerOuter
864 * do on first getSize() rather than on page load for Firefox bug
865 */
866function setup() {
867 // setup once
868 if ( isSetup ) {
869 return;
870 }
871 isSetup = true;
872
873 // -------------------------- box sizing -------------------------- //
874
875 /**
876 * Chrome & Safari measure the outer-width on style.width on border-box elems
877 * IE11 & Firefox<29 measures the inner-width
878 */
879 var div = document.createElement('div');
880 div.style.width = '200px';
881 div.style.padding = '1px 2px 3px 4px';
882 div.style.borderStyle = 'solid';
883 div.style.borderWidth = '1px 2px 3px 4px';
884 div.style.boxSizing = 'border-box';
885
886 var body = document.body || document.documentElement;
887 body.appendChild( div );
888 var style = getStyle( div );
889 // round value for browser zoom. desandro/masonry#928
890 isBoxSizeOuter = Math.round( getStyleSize( style.width ) ) == 200;
891 getSize.isBoxSizeOuter = isBoxSizeOuter;
892
893 body.removeChild( div );
894}
895
896// -------------------------- getSize -------------------------- //
897
898function getSize( elem ) {
899 setup();
900
901 // use querySeletor if elem is string
902 if ( typeof elem == 'string' ) {
903 elem = document.querySelector( elem );
904 }
905
906 // do not proceed on non-objects
907 if ( !elem || typeof elem != 'object' || !elem.nodeType ) {
908 return;
909 }
910
911 var style = getStyle( elem );
912
913 // if hidden, everything is 0
914 if ( style.display == 'none' ) {
915 return getZeroSize();
916 }
917
918 var size = {};
919 size.width = elem.offsetWidth;
920 size.height = elem.offsetHeight;
921
922 var isBorderBox = size.isBorderBox = style.boxSizing == 'border-box';
923
924 // get all measurements
925 for ( var i=0; i < measurementsLength; i++ ) {
926 var measurement = measurements[i];
927 var value = style[ measurement ];
928 var num = parseFloat( value );
929 // any 'auto', 'medium' value will be 0
930 size[ measurement ] = !isNaN( num ) ? num : 0;
931 }
932
933 var paddingWidth = size.paddingLeft + size.paddingRight;
934 var paddingHeight = size.paddingTop + size.paddingBottom;
935 var marginWidth = size.marginLeft + size.marginRight;
936 var marginHeight = size.marginTop + size.marginBottom;
937 var borderWidth = size.borderLeftWidth + size.borderRightWidth;
938 var borderHeight = size.borderTopWidth + size.borderBottomWidth;
939
940 var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter;
941
942 // overwrite width and height if we can get it from style
943 var styleWidth = getStyleSize( style.width );
944 if ( styleWidth !== false ) {
945 size.width = styleWidth +
946 // add padding and border unless it's already including it
947 ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth );
948 }
949
950 var styleHeight = getStyleSize( style.height );
951 if ( styleHeight !== false ) {
952 size.height = styleHeight +
953 // add padding and border unless it's already including it
954 ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight );
955 }
956
957 size.innerWidth = size.width - ( paddingWidth + borderWidth );
958 size.innerHeight = size.height - ( paddingHeight + borderHeight );
959
960 size.outerWidth = size.width + marginWidth;
961 size.outerHeight = size.height + marginHeight;
962
963 return size;
964}
965
966return getSize;
967
968});
969
970/**
971 * matchesSelector v2.0.2
972 * matchesSelector( element, '.selector' )
973 * MIT license
974 */
975
976/*jshint browser: true, strict: true, undef: true, unused: true */
977
978( function( window, factory ) {
979 /*global define: false, module: false */
980 'use strict';
981 // universal module definition
982 if ( typeof define == 'function' && define.amd ) {
983 // AMD
984 define( 'desandro-matches-selector/matches-selector',factory );
985 } else if ( typeof module == 'object' && module.exports ) {
986 // CommonJS
987 module.exports = factory();
988 } else {
989 // browser global
990 window.matchesSelector = factory();
991 }
992
993}( window, function factory() {
994 'use strict';
995
996 var matchesMethod = ( function() {
997 var ElemProto = window.Element.prototype;
998 // check for the standard method name first
999 if ( ElemProto.matches ) {
1000 return 'matches';
1001 }
1002 // check un-prefixed
1003 if ( ElemProto.matchesSelector ) {
1004 return 'matchesSelector';
1005 }
1006 // check vendor prefixes
1007 var prefixes = [ 'webkit', 'moz', 'ms', 'o' ];
1008
1009 for ( var i=0; i < prefixes.length; i++ ) {
1010 var prefix = prefixes[i];
1011 var method = prefix + 'MatchesSelector';
1012 if ( ElemProto[ method ] ) {
1013 return method;
1014 }
1015 }
1016 })();
1017
1018 return function matchesSelector( elem, selector ) {
1019 return elem[ matchesMethod ]( selector );
1020 };
1021
1022}));
1023
1024/**
1025 * Fizzy UI utils v2.0.7
1026 * MIT license
1027 */
1028
1029/*jshint browser: true, undef: true, unused: true, strict: true */
1030
1031( function( window, factory ) {
1032 // universal module definition
1033 /*jshint strict: false */ /*globals define, module, require */
1034
1035 if ( typeof define == 'function' && define.amd ) {
1036 // AMD
1037 define( 'fizzy-ui-utils/utils',[
1038 'desandro-matches-selector/matches-selector'
1039 ], function( matchesSelector ) {
1040 return factory( window, matchesSelector );
1041 });
1042 } else if ( typeof module == 'object' && module.exports ) {
1043 // CommonJS
1044 module.exports = factory(
1045 window,
1046 require('desandro-matches-selector')
1047 );
1048 } else {
1049 // browser global
1050 window.fizzyUIUtils = factory(
1051 window,
1052 window.matchesSelector
1053 );
1054 }
1055
1056}( window, function factory( window, matchesSelector ) {
1057
1058
1059
1060var utils = {};
1061
1062// ----- extend ----- //
1063
1064// extends objects
1065utils.extend = function( a, b ) {
1066 for ( var prop in b ) {
1067 a[ prop ] = b[ prop ];
1068 }
1069 return a;
1070};
1071
1072// ----- modulo ----- //
1073
1074utils.modulo = function( num, div ) {
1075 return ( ( num % div ) + div ) % div;
1076};
1077
1078// ----- makeArray ----- //
1079
1080var arraySlice = Array.prototype.slice;
1081
1082// turn element or nodeList into an array
1083utils.makeArray = function( obj ) {
1084 if ( Array.isArray( obj ) ) {
1085 // use object if already an array
1086 return obj;
1087 }
1088 // return empty array if undefined or null. #6
1089 if ( obj === null || obj === undefined ) {
1090 return [];
1091 }
1092
1093 var isArrayLike = typeof obj == 'object' && typeof obj.length == 'number';
1094 if ( isArrayLike ) {
1095 // convert nodeList to array
1096 return arraySlice.call( obj );
1097 }
1098
1099 // array of single index
1100 return [ obj ];
1101};
1102
1103// ----- removeFrom ----- //
1104
1105utils.removeFrom = function( ary, obj ) {
1106 var index = ary.indexOf( obj );
1107 if ( index != -1 ) {
1108 ary.splice( index, 1 );
1109 }
1110};
1111
1112// ----- getParent ----- //
1113
1114utils.getParent = function( elem, selector ) {
1115 while ( elem.parentNode && elem != document.body ) {
1116 elem = elem.parentNode;
1117 if ( matchesSelector( elem, selector ) ) {
1118 return elem;
1119 }
1120 }
1121};
1122
1123// ----- getQueryElement ----- //
1124
1125// use element as selector string
1126utils.getQueryElement = function( elem ) {
1127 if ( typeof elem == 'string' ) {
1128 return document.querySelector( elem );
1129 }
1130 return elem;
1131};
1132
1133// ----- handleEvent ----- //
1134
1135// enable .ontype to trigger from .addEventListener( elem, 'type' )
1136utils.handleEvent = function( event ) {
1137 var method = 'on' + event.type;
1138 if ( this[ method ] ) {
1139 this[ method ]( event );
1140 }
1141};
1142
1143// ----- filterFindElements ----- //
1144
1145utils.filterFindElements = function( elems, selector ) {
1146 // make array of elems
1147 elems = utils.makeArray( elems );
1148 var ffElems = [];
1149
1150 elems.forEach( function( elem ) {
1151 // check that elem is an actual element
1152 if ( !( elem instanceof HTMLElement ) ) {
1153 return;
1154 }
1155 // add elem if no selector
1156 if ( !selector ) {
1157 ffElems.push( elem );
1158 return;
1159 }
1160 // filter & find items if we have a selector
1161 // filter
1162 if ( matchesSelector( elem, selector ) ) {
1163 ffElems.push( elem );
1164 }
1165 // find children
1166 var childElems = elem.querySelectorAll( selector );
1167 // concat childElems to filterFound array
1168 for ( var i=0; i < childElems.length; i++ ) {
1169 ffElems.push( childElems[i] );
1170 }
1171 });
1172
1173 return ffElems;
1174};
1175
1176// ----- debounceMethod ----- //
1177
1178utils.debounceMethod = function( _class, methodName, threshold ) {
1179 threshold = threshold || 100;
1180 // original method
1181 var method = _class.prototype[ methodName ];
1182 var timeoutName = methodName + 'Timeout';
1183
1184 _class.prototype[ methodName ] = function() {
1185 var timeout = this[ timeoutName ];
1186 clearTimeout( timeout );
1187
1188 var args = arguments;
1189 var _this = this;
1190 this[ timeoutName ] = setTimeout( function() {
1191 method.apply( _this, args );
1192 delete _this[ timeoutName ];
1193 }, threshold );
1194 };
1195};
1196
1197// ----- docReady ----- //
1198
1199utils.docReady = function( callback ) {
1200 var readyState = document.readyState;
1201 if ( readyState == 'complete' || readyState == 'interactive' ) {
1202 // do async to allow for other scripts to run. metafizzy/flickity#441
1203 setTimeout( callback );
1204 } else {
1205 document.addEventListener( 'DOMContentLoaded', callback );
1206 }
1207};
1208
1209// ----- htmlInit ----- //
1210
1211// http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
1212utils.toDashed = function( str ) {
1213 return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
1214 return $1 + '-' + $2;
1215 }).toLowerCase();
1216};
1217
1218var console = window.console;
1219/**
1220 * allow user to initialize classes via [data-namespace] or .js-namespace class
1221 * htmlInit( Widget, 'widgetName' )
1222 * options are parsed from data-namespace-options
1223 */
1224utils.htmlInit = function( WidgetClass, namespace ) {
1225 utils.docReady( function() {
1226 var dashedNamespace = utils.toDashed( namespace );
1227 var dataAttr = 'data-' + dashedNamespace;
1228 var dataAttrElems = document.querySelectorAll( '[' + dataAttr + ']' );
1229 var jsDashElems = document.querySelectorAll( '.js-' + dashedNamespace );
1230 var elems = utils.makeArray( dataAttrElems )
1231 .concat( utils.makeArray( jsDashElems ) );
1232 var dataOptionsAttr = dataAttr + '-options';
1233 var jQuery = window.jQuery;
1234
1235 elems.forEach( function( elem ) {
1236 var attr = elem.getAttribute( dataAttr ) ||
1237 elem.getAttribute( dataOptionsAttr );
1238 var options;
1239 try {
1240 options = attr && JSON.parse( attr );
1241 } catch ( error ) {
1242 // log error, do not initialize
1243 if ( console ) {
1244 console.error( 'Error parsing ' + dataAttr + ' on ' + elem.className +
1245 ': ' + error );
1246 }
1247 return;
1248 }
1249 // initialize
1250 var instance = new WidgetClass( elem, options );
1251 // make available via $().data('namespace')
1252 if ( jQuery ) {
1253 jQuery.data( elem, namespace, instance );
1254 }
1255 });
1256
1257 });
1258};
1259
1260// ----- ----- //
1261
1262return utils;
1263
1264}));
1265
1266// Flickity.Cell
1267( function( window, factory ) {
1268 // universal module definition
1269 if ( typeof define == 'function' && define.amd ) {
1270 // AMD
1271 define( 'flickity/js/cell',[
1272 'get-size/get-size',
1273 ], function( getSize ) {
1274 return factory( window, getSize );
1275 } );
1276 } else if ( typeof module == 'object' && module.exports ) {
1277 // CommonJS
1278 module.exports = factory(
1279 window,
1280 require('get-size')
1281 );
1282 } else {
1283 // browser global
1284 window.Flickity = window.Flickity || {};
1285 window.Flickity.Cell = factory(
1286 window,
1287 window.getSize
1288 );
1289 }
1290
1291}( window, function factory( window, getSize ) {
1292
1293
1294
1295function Cell( elem, parent ) {
1296 this.element = elem;
1297 this.parent = parent;
1298
1299 this.create();
1300}
1301
1302var proto = Cell.prototype;
1303
1304proto.create = function() {
1305 this.element.style.position = 'absolute';
1306 this.element.setAttribute( 'aria-hidden', 'true' );
1307 this.x = 0;
1308 this.shift = 0;
1309};
1310
1311proto.destroy = function() {
1312 // reset style
1313 this.unselect();
1314 this.element.style.position = '';
1315 var side = this.parent.originSide;
1316 this.element.style[ side ] = '';
1317 this.element.removeAttribute('aria-hidden');
1318};
1319
1320proto.getSize = function() {
1321 this.size = getSize( this.element );
1322};
1323
1324proto.setPosition = function( x ) {
1325 this.x = x;
1326 this.updateTarget();
1327 this.renderPosition( x );
1328};
1329
1330// setDefaultTarget v1 method, backwards compatibility, remove in v3
1331proto.updateTarget = proto.setDefaultTarget = function() {
1332 var marginProperty = this.parent.originSide == 'left' ? 'marginLeft' : 'marginRight';
1333 this.target = this.x + this.size[ marginProperty ] +
1334 this.size.width * this.parent.cellAlign;
1335};
1336
1337proto.renderPosition = function( x ) {
1338 // render position of cell with in slider
1339 var side = this.parent.originSide;
1340 this.element.style[ side ] = this.parent.getPositionValue( x );
1341};
1342
1343proto.select = function() {
1344 this.element.classList.add('is-selected');
1345 this.element.removeAttribute('aria-hidden');
1346};
1347
1348proto.unselect = function() {
1349 this.element.classList.remove('is-selected');
1350 this.element.setAttribute( 'aria-hidden', 'true' );
1351};
1352
1353/**
1354 * @param {Integer} shift - 0, 1, or -1
1355 */
1356proto.wrapShift = function( shift ) {
1357 this.shift = shift;
1358 this.renderPosition( this.x + this.parent.slideableWidth * shift );
1359};
1360
1361proto.remove = function() {
1362 this.element.parentNode.removeChild( this.element );
1363};
1364
1365return Cell;
1366
1367} ) );
1368
1369// slide
1370( function( window, factory ) {
1371 // universal module definition
1372 if ( typeof define == 'function' && define.amd ) {
1373 // AMD
1374 define( 'flickity/js/slide',factory );
1375 } else if ( typeof module == 'object' && module.exports ) {
1376 // CommonJS
1377 module.exports = factory();
1378 } else {
1379 // browser global
1380 window.Flickity = window.Flickity || {};
1381 window.Flickity.Slide = factory();
1382 }
1383
1384}( window, function factory() {
1385'use strict';
1386
1387function Slide( parent ) {
1388 this.parent = parent;
1389 this.isOriginLeft = parent.originSide == 'left';
1390 this.cells = [];
1391 this.outerWidth = 0;
1392 this.height = 0;
1393}
1394
1395var proto = Slide.prototype;
1396
1397proto.addCell = function( cell ) {
1398 this.cells.push( cell );
1399 this.outerWidth += cell.size.outerWidth;
1400 this.height = Math.max( cell.size.outerHeight, this.height );
1401 // first cell stuff
1402 if ( this.cells.length == 1 ) {
1403 this.x = cell.x; // x comes from first cell
1404 var beginMargin = this.isOriginLeft ? 'marginLeft' : 'marginRight';
1405 this.firstMargin = cell.size[ beginMargin ];
1406 }
1407};
1408
1409proto.updateTarget = function() {
1410 var endMargin = this.isOriginLeft ? 'marginRight' : 'marginLeft';
1411 var lastCell = this.getLastCell();
1412 var lastMargin = lastCell ? lastCell.size[ endMargin ] : 0;
1413 var slideWidth = this.outerWidth - ( this.firstMargin + lastMargin );
1414 this.target = this.x + this.firstMargin + slideWidth * this.parent.cellAlign;
1415};
1416
1417proto.getLastCell = function() {
1418 return this.cells[ this.cells.length - 1 ];
1419};
1420
1421proto.select = function() {
1422 this.cells.forEach( function( cell ) {
1423 cell.select();
1424 } );
1425};
1426
1427proto.unselect = function() {
1428 this.cells.forEach( function( cell ) {
1429 cell.unselect();
1430 } );
1431};
1432
1433proto.getCellElements = function() {
1434 return this.cells.map( function( cell ) {
1435 return cell.element;
1436 } );
1437};
1438
1439return Slide;
1440
1441} ) );
1442
1443// animate
1444( function( window, factory ) {
1445 // universal module definition
1446 if ( typeof define == 'function' && define.amd ) {
1447 // AMD
1448 define( 'flickity/js/animate',[
1449 'fizzy-ui-utils/utils',
1450 ], function( utils ) {
1451 return factory( window, utils );
1452 } );
1453 } else if ( typeof module == 'object' && module.exports ) {
1454 // CommonJS
1455 module.exports = factory(
1456 window,
1457 require('fizzy-ui-utils')
1458 );
1459 } else {
1460 // browser global
1461 window.Flickity = window.Flickity || {};
1462 window.Flickity.animatePrototype = factory(
1463 window,
1464 window.fizzyUIUtils
1465 );
1466 }
1467
1468}( window, function factory( window, utils ) {
1469
1470
1471
1472// -------------------------- animate -------------------------- //
1473
1474var proto = {};
1475
1476proto.startAnimation = function() {
1477 if ( this.isAnimating ) {
1478 return;
1479 }
1480
1481 this.isAnimating = true;
1482 this.restingFrames = 0;
1483 this.animate();
1484};
1485
1486proto.animate = function() {
1487 this.applyDragForce();
1488 this.applySelectedAttraction();
1489
1490 var previousX = this.x;
1491
1492 this.integratePhysics();
1493 this.positionSlider();
1494 this.settle( previousX );
1495 // animate next frame
1496 if ( this.isAnimating ) {
1497 var _this = this;
1498 requestAnimationFrame( function animateFrame() {
1499 _this.animate();
1500 } );
1501 }
1502};
1503
1504proto.positionSlider = function() {
1505 var x = this.x;
1506 // wrap position around
1507 if ( this.options.wrapAround && this.cells.length > 1 ) {
1508 x = utils.modulo( x, this.slideableWidth );
1509 x -= this.slideableWidth;
1510 this.shiftWrapCells( x );
1511 }
1512
1513 this.setTranslateX( x, this.isAnimating );
1514 this.dispatchScrollEvent();
1515};
1516
1517proto.setTranslateX = function( x, is3d ) {
1518 x += this.cursorPosition;
1519 // reverse if right-to-left and using transform
1520 x = this.options.rightToLeft ? -x : x;
1521 var translateX = this.getPositionValue( x );
1522 // use 3D transforms for hardware acceleration on iOS
1523 // but use 2D when settled, for better font-rendering
1524 this.slider.style.transform = is3d ?
1525 'translate3d(' + translateX + ',0,0)' : 'translateX(' + translateX + ')';
1526};
1527
1528proto.dispatchScrollEvent = function() {
1529 var firstSlide = this.slides[0];
1530 if ( !firstSlide ) {
1531 return;
1532 }
1533 var positionX = -this.x - firstSlide.target;
1534 var progress = positionX / this.slidesWidth;
1535 this.dispatchEvent( 'scroll', null, [ progress, positionX ] );
1536};
1537
1538proto.positionSliderAtSelected = function() {
1539 if ( !this.cells.length ) {
1540 return;
1541 }
1542 this.x = -this.selectedSlide.target;
1543 this.velocity = 0; // stop wobble
1544 this.positionSlider();
1545};
1546
1547proto.getPositionValue = function( position ) {
1548 if ( this.options.percentPosition ) {
1549 // percent position, round to 2 digits, like 12.34%
1550 return ( Math.round( ( position / this.size.innerWidth ) * 10000 ) * 0.01 ) + '%';
1551 } else {
1552 // pixel positioning
1553 return Math.round( position ) + 'px';
1554 }
1555};
1556
1557proto.settle = function( previousX ) {
1558 // keep track of frames where x hasn't moved
1559 var isResting = !this.isPointerDown &&
1560 Math.round( this.x * 100 ) == Math.round( previousX * 100 );
1561 if ( isResting ) {
1562 this.restingFrames++;
1563 }
1564 // stop animating if resting for 3 or more frames
1565 if ( this.restingFrames > 2 ) {
1566 this.isAnimating = false;
1567 delete this.isFreeScrolling;
1568 // render position with translateX when settled
1569 this.positionSlider();
1570 this.dispatchEvent( 'settle', null, [ this.selectedIndex ] );
1571 }
1572};
1573
1574proto.shiftWrapCells = function( x ) {
1575 // shift before cells
1576 var beforeGap = this.cursorPosition + x;
1577 this._shiftCells( this.beforeShiftCells, beforeGap, -1 );
1578 // shift after cells
1579 var afterGap = this.size.innerWidth - ( x + this.slideableWidth + this.cursorPosition );
1580 this._shiftCells( this.afterShiftCells, afterGap, 1 );
1581};
1582
1583proto._shiftCells = function( cells, gap, shift ) {
1584 for ( var i = 0; i < cells.length; i++ ) {
1585 var cell = cells[i];
1586 var cellShift = gap > 0 ? shift : 0;
1587 cell.wrapShift( cellShift );
1588 gap -= cell.size.outerWidth;
1589 }
1590};
1591
1592proto._unshiftCells = function( cells ) {
1593 if ( !cells || !cells.length ) {
1594 return;
1595 }
1596 for ( var i = 0; i < cells.length; i++ ) {
1597 cells[i].wrapShift( 0 );
1598 }
1599};
1600
1601// -------------------------- physics -------------------------- //
1602
1603proto.integratePhysics = function() {
1604 this.x += this.velocity;
1605 this.velocity *= this.getFrictionFactor();
1606};
1607
1608proto.applyForce = function( force ) {
1609 this.velocity += force;
1610};
1611
1612proto.getFrictionFactor = function() {
1613 return 1 - this.options[ this.isFreeScrolling ? 'freeScrollFriction' : 'friction' ];
1614};
1615
1616proto.getRestingPosition = function() {
1617 // my thanks to Steven Wittens, who simplified this math greatly
1618 return this.x + this.velocity / ( 1 - this.getFrictionFactor() );
1619};
1620
1621proto.applyDragForce = function() {
1622 if ( !this.isDraggable || !this.isPointerDown ) {
1623 return;
1624 }
1625 // change the position to drag position by applying force
1626 var dragVelocity = this.dragX - this.x;
1627 var dragForce = dragVelocity - this.velocity;
1628 this.applyForce( dragForce );
1629};
1630
1631proto.applySelectedAttraction = function() {
1632 // do not attract if pointer down or no slides
1633 var dragDown = this.isDraggable && this.isPointerDown;
1634 if ( dragDown || this.isFreeScrolling || !this.slides.length ) {
1635 return;
1636 }
1637 var distance = this.selectedSlide.target * -1 - this.x;
1638 var force = distance * this.options.selectedAttraction;
1639 this.applyForce( force );
1640};
1641
1642return proto;
1643
1644} ) );
1645
1646// Flickity main
1647/* eslint-disable max-params */
1648( function( window, factory ) {
1649 // universal module definition
1650 if ( typeof define == 'function' && define.amd ) {
1651 // AMD
1652 define( 'flickity/js/flickity',[
1653 'ev-emitter/ev-emitter',
1654 'get-size/get-size',
1655 'fizzy-ui-utils/utils',
1656 './cell',
1657 './slide',
1658 './animate',
1659 ], function( EvEmitter, getSize, utils, Cell, Slide, animatePrototype ) {
1660 return factory( window, EvEmitter, getSize, utils, Cell, Slide, animatePrototype );
1661 } );
1662 } else if ( typeof module == 'object' && module.exports ) {
1663 // CommonJS
1664 module.exports = factory(
1665 window,
1666 require('ev-emitter'),
1667 require('get-size'),
1668 require('fizzy-ui-utils'),
1669 require('./cell'),
1670 require('./slide'),
1671 require('./animate')
1672 );
1673 } else {
1674 // browser global
1675 var _Flickity = window.Flickity;
1676
1677 window.Flickity = factory(
1678 window,
1679 window.EvEmitter,
1680 window.getSize,
1681 window.fizzyUIUtils,
1682 _Flickity.Cell,
1683 _Flickity.Slide,
1684 _Flickity.animatePrototype
1685 );
1686 }
1687
1688}( window, function factory( window, EvEmitter, getSize,
1689 utils, Cell, Slide, animatePrototype ) {
1690
1691/* eslint-enable max-params */
1692
1693
1694// vars
1695var jQuery = window.jQuery;
1696var getComputedStyle = window.getComputedStyle;
1697var console = window.console;
1698
1699function moveElements( elems, toElem ) {
1700 elems = utils.makeArray( elems );
1701 while ( elems.length ) {
1702 toElem.appendChild( elems.shift() );
1703 }
1704}
1705
1706// -------------------------- Flickity -------------------------- //
1707
1708// globally unique identifiers
1709var GUID = 0;
1710// internal store of all Flickity intances
1711var instances = {};
1712
1713function Flickity( element, options ) {
1714 var queryElement = utils.getQueryElement( element );
1715 if ( !queryElement ) {
1716 if ( console ) {
1717 console.error( 'Bad element for Flickity: ' + ( queryElement || element ) );
1718 }
1719 return;
1720 }
1721 this.element = queryElement;
1722 // do not initialize twice on same element
1723 if ( this.element.flickityGUID ) {
1724 var instance = instances[ this.element.flickityGUID ];
1725 if ( instance ) instance.option( options );
1726 return instance;
1727 }
1728
1729 // add jQuery
1730 if ( jQuery ) {
1731 this.$element = jQuery( this.element );
1732 }
1733 // options
1734 this.options = utils.extend( {}, this.constructor.defaults );
1735 this.option( options );
1736
1737 // kick things off
1738 this._create();
1739}
1740
1741Flickity.defaults = {
1742 accessibility: true,
1743 // adaptiveHeight: false,
1744 cellAlign: 'center',
1745 // cellSelector: undefined,
1746 // contain: false,
1747 freeScrollFriction: 0.075, // friction when free-scrolling
1748 friction: 0.28, // friction when selecting
1749 namespaceJQueryEvents: true,
1750 // initialIndex: 0,
1751 percentPosition: true,
1752 resize: true,
1753 selectedAttraction: 0.025,
1754 setGallerySize: true,
1755 // watchCSS: false,
1756 // wrapAround: false
1757};
1758
1759// hash of methods triggered on _create()
1760Flickity.createMethods = [];
1761
1762var proto = Flickity.prototype;
1763// inherit EventEmitter
1764utils.extend( proto, EvEmitter.prototype );
1765
1766proto._create = function() {
1767 // add id for Flickity.data
1768 var id = this.guid = ++GUID;
1769 this.element.flickityGUID = id; // expando
1770 instances[ id ] = this; // associate via id
1771 // initial properties
1772 this.selectedIndex = 0;
1773 // how many frames slider has been in same position
1774 this.restingFrames = 0;
1775 // initial physics properties
1776 this.x = 0;
1777 this.velocity = 0;
1778 this.originSide = this.options.rightToLeft ? 'right' : 'left';
1779 // create viewport & slider
1780 this.viewport = document.createElement('div');
1781 this.viewport.className = 'flickity-viewport';
1782 this._createSlider();
1783
1784 if ( this.options.resize || this.options.watchCSS ) {
1785 window.addEventListener( 'resize', this );
1786 }
1787
1788 // add listeners from on option
1789 for ( var eventName in this.options.on ) {
1790 var listener = this.options.on[ eventName ];
1791 this.on( eventName, listener );
1792 }
1793
1794 Flickity.createMethods.forEach( function( method ) {
1795 this[ method ]();
1796 }, this );
1797
1798 if ( this.options.watchCSS ) {
1799 this.watchCSS();
1800 } else {
1801 this.activate();
1802 }
1803
1804};
1805
1806/**
1807 * set options
1808 * @param {Object} opts - options to extend
1809 */
1810proto.option = function( opts ) {
1811 utils.extend( this.options, opts );
1812};
1813
1814proto.activate = function() {
1815 if ( this.isActive ) {
1816 return;
1817 }
1818 this.isActive = true;
1819 this.element.classList.add('flickity-enabled');
1820 if ( this.options.rightToLeft ) {
1821 this.element.classList.add('flickity-rtl');
1822 }
1823
1824 this.getSize();
1825 // move initial cell elements so they can be loaded as cells
1826 var cellElems = this._filterFindCellElements( this.element.children );
1827 moveElements( cellElems, this.slider );
1828 this.viewport.appendChild( this.slider );
1829 this.element.appendChild( this.viewport );
1830 // get cells from children
1831 this.reloadCells();
1832
1833 if ( this.options.accessibility ) {
1834 // allow element to focusable
1835 this.element.tabIndex = 0;
1836 // listen for key presses
1837 this.element.addEventListener( 'keydown', this );
1838 }
1839
1840 this.emitEvent('activate');
1841 this.selectInitialIndex();
1842 // flag for initial activation, for using initialIndex
1843 this.isInitActivated = true;
1844 // ready event. #493
1845 this.dispatchEvent('ready');
1846};
1847
1848// slider positions the cells
1849proto._createSlider = function() {
1850 // slider element does all the positioning
1851 var slider = document.createElement('div');
1852 slider.className = 'flickity-slider';
1853 slider.style[ this.originSide ] = 0;
1854 this.slider = slider;
1855};
1856
1857proto._filterFindCellElements = function( elems ) {
1858 return utils.filterFindElements( elems, this.options.cellSelector );
1859};
1860
1861// goes through all children
1862proto.reloadCells = function() {
1863 // collection of item elements
1864 this.cells = this._makeCells( this.slider.children );
1865 this.positionCells();
1866 this._getWrapShiftCells();
1867 this.setGallerySize();
1868};
1869
1870/**
1871 * turn elements into Flickity.Cells
1872 * @param {[Array, NodeList, HTMLElement]} elems - elements to make into cells
1873 * @returns {Array} items - collection of new Flickity Cells
1874 */
1875proto._makeCells = function( elems ) {
1876 var cellElems = this._filterFindCellElements( elems );
1877
1878 // create new Flickity for collection
1879 var cells = cellElems.map( function( cellElem ) {
1880 return new Cell( cellElem, this );
1881 }, this );
1882
1883 return cells;
1884};
1885
1886proto.getLastCell = function() {
1887 return this.cells[ this.cells.length - 1 ];
1888};
1889
1890proto.getLastSlide = function() {
1891 return this.slides[ this.slides.length - 1 ];
1892};
1893
1894// positions all cells
1895proto.positionCells = function() {
1896 // size all cells
1897 this._sizeCells( this.cells );
1898 // position all cells
1899 this._positionCells( 0 );
1900};
1901
1902/**
1903 * position certain cells
1904 * @param {Integer} index - which cell to start with
1905 */
1906proto._positionCells = function( index ) {
1907 index = index || 0;
1908 // also measure maxCellHeight
1909 // start 0 if positioning all cells
1910 this.maxCellHeight = index ? this.maxCellHeight || 0 : 0;
1911 var cellX = 0;
1912 // get cellX
1913 if ( index > 0 ) {
1914 var startCell = this.cells[ index - 1 ];
1915 cellX = startCell.x + startCell.size.outerWidth;
1916 }
1917 var len = this.cells.length;
1918 for ( var i = index; i < len; i++ ) {
1919 var cell = this.cells[i];
1920 cell.setPosition( cellX );
1921 cellX += cell.size.outerWidth;
1922 this.maxCellHeight = Math.max( cell.size.outerHeight, this.maxCellHeight );
1923 }
1924 // keep track of cellX for wrap-around
1925 this.slideableWidth = cellX;
1926 // slides
1927 this.updateSlides();
1928 // contain slides target
1929 this._containSlides();
1930 // update slidesWidth
1931 this.slidesWidth = len ? this.getLastSlide().target - this.slides[0].target : 0;
1932};
1933
1934/**
1935 * cell.getSize() on multiple cells
1936 * @param {Array} cells - cells to size
1937 */
1938proto._sizeCells = function( cells ) {
1939 cells.forEach( function( cell ) {
1940 cell.getSize();
1941 } );
1942};
1943
1944// -------------------------- -------------------------- //
1945
1946proto.updateSlides = function() {
1947 this.slides = [];
1948 if ( !this.cells.length ) {
1949 return;
1950 }
1951
1952 var slide = new Slide( this );
1953 this.slides.push( slide );
1954 var isOriginLeft = this.originSide == 'left';
1955 var nextMargin = isOriginLeft ? 'marginRight' : 'marginLeft';
1956
1957 var canCellFit = this._getCanCellFit();
1958
1959 this.cells.forEach( function( cell, i ) {
1960 // just add cell if first cell in slide
1961 if ( !slide.cells.length ) {
1962 slide.addCell( cell );
1963 return;
1964 }
1965
1966 var slideWidth = ( slide.outerWidth - slide.firstMargin ) +
1967 ( cell.size.outerWidth - cell.size[ nextMargin ] );
1968
1969 if ( canCellFit.call( this, i, slideWidth ) ) {
1970 slide.addCell( cell );
1971 } else {
1972 // doesn't fit, new slide
1973 slide.updateTarget();
1974
1975 slide = new Slide( this );
1976 this.slides.push( slide );
1977 slide.addCell( cell );
1978 }
1979 }, this );
1980 // last slide
1981 slide.updateTarget();
1982 // update .selectedSlide
1983 this.updateSelectedSlide();
1984};
1985
1986proto._getCanCellFit = function() {
1987 var groupCells = this.options.groupCells;
1988 if ( !groupCells ) {
1989 return function() {
1990 return false;
1991 };
1992 } else if ( typeof groupCells == 'number' ) {
1993 // group by number. 3 -> [0,1,2], [3,4,5], ...
1994 var number = parseInt( groupCells, 10 );
1995 return function( i ) {
1996 return ( i % number ) !== 0;
1997 };
1998 }
1999 // default, group by width of slide
2000 // parse '75%
2001 var percentMatch = typeof groupCells == 'string' &&
2002 groupCells.match( /^(\d+)%$/ );
2003 var percent = percentMatch ? parseInt( percentMatch[1], 10 ) / 100 : 1;
2004 return function( i, slideWidth ) {
2005 /* eslint-disable-next-line no-invalid-this */
2006 return slideWidth <= ( this.size.innerWidth + 1 ) * percent;
2007 };
2008};
2009
2010// alias _init for jQuery plugin .flickity()
2011proto._init =
2012proto.reposition = function() {
2013 this.positionCells();
2014 this.positionSliderAtSelected();
2015};
2016
2017proto.getSize = function() {
2018 this.size = getSize( this.element );
2019 this.setCellAlign();
2020 this.cursorPosition = this.size.innerWidth * this.cellAlign;
2021};
2022
2023var cellAlignShorthands = {
2024 // cell align, then based on origin side
2025 center: {
2026 left: 0.5,
2027 right: 0.5,
2028 },
2029 left: {
2030 left: 0,
2031 right: 1,
2032 },
2033 right: {
2034 right: 0,
2035 left: 1,
2036 },
2037};
2038
2039proto.setCellAlign = function() {
2040 var shorthand = cellAlignShorthands[ this.options.cellAlign ];
2041 this.cellAlign = shorthand ? shorthand[ this.originSide ] : this.options.cellAlign;
2042};
2043
2044proto.setGallerySize = function() {
2045 if ( this.options.setGallerySize ) {
2046 var height = this.options.adaptiveHeight && this.selectedSlide ?
2047 this.selectedSlide.height : this.maxCellHeight;
2048 this.viewport.style.height = height + 'px';
2049 }
2050};
2051
2052proto._getWrapShiftCells = function() {
2053 // only for wrap-around
2054 if ( !this.options.wrapAround ) {
2055 return;
2056 }
2057 // unshift previous cells
2058 this._unshiftCells( this.beforeShiftCells );
2059 this._unshiftCells( this.afterShiftCells );
2060 // get before cells
2061 // initial gap
2062 var gapX = this.cursorPosition;
2063 var cellIndex = this.cells.length - 1;
2064 this.beforeShiftCells = this._getGapCells( gapX, cellIndex, -1 );
2065 // get after cells
2066 // ending gap between last cell and end of gallery viewport
2067 gapX = this.size.innerWidth - this.cursorPosition;
2068 // start cloning at first cell, working forwards
2069 this.afterShiftCells = this._getGapCells( gapX, 0, 1 );
2070};
2071
2072proto._getGapCells = function( gapX, cellIndex, increment ) {
2073 // keep adding cells until the cover the initial gap
2074 var cells = [];
2075 while ( gapX > 0 ) {
2076 var cell = this.cells[ cellIndex ];
2077 if ( !cell ) {
2078 break;
2079 }
2080 cells.push( cell );
2081 cellIndex += increment;
2082 gapX -= cell.size.outerWidth;
2083 }
2084 return cells;
2085};
2086
2087// ----- contain ----- //
2088
2089// contain cell targets so no excess sliding
2090proto._containSlides = function() {
2091 if ( !this.options.contain || this.options.wrapAround || !this.cells.length ) {
2092 return;
2093 }
2094 var isRightToLeft = this.options.rightToLeft;
2095 var beginMargin = isRightToLeft ? 'marginRight' : 'marginLeft';
2096 var endMargin = isRightToLeft ? 'marginLeft' : 'marginRight';
2097 var contentWidth = this.slideableWidth - this.getLastCell().size[ endMargin ];
2098 // content is less than gallery size
2099 var isContentSmaller = contentWidth < this.size.innerWidth;
2100 // bounds
2101 var beginBound = this.cursorPosition + this.cells[0].size[ beginMargin ];
2102 var endBound = contentWidth - this.size.innerWidth * ( 1 - this.cellAlign );
2103 // contain each cell target
2104 this.slides.forEach( function( slide ) {
2105 if ( isContentSmaller ) {
2106 // all cells fit inside gallery
2107 slide.target = contentWidth * this.cellAlign;
2108 } else {
2109 // contain to bounds
2110 slide.target = Math.max( slide.target, beginBound );
2111 slide.target = Math.min( slide.target, endBound );
2112 }
2113 }, this );
2114};
2115
2116// ----- ----- //
2117
2118/**
2119 * emits events via eventEmitter and jQuery events
2120 * @param {String} type - name of event
2121 * @param {Event} event - original event
2122 * @param {Array} args - extra arguments
2123 */
2124proto.dispatchEvent = function( type, event, args ) {
2125 var emitArgs = event ? [ event ].concat( args ) : args;
2126 this.emitEvent( type, emitArgs );
2127
2128 if ( jQuery && this.$element ) {
2129 // default trigger with type if no event
2130 type += this.options.namespaceJQueryEvents ? '.flickity' : '';
2131 var $event = type;
2132 if ( event ) {
2133 // create jQuery event
2134 var jQEvent = new jQuery.Event( event );
2135 jQEvent.type = type;
2136 $event = jQEvent;
2137 }
2138 this.$element.trigger( $event, args );
2139 }
2140};
2141
2142// -------------------------- select -------------------------- //
2143
2144/**
2145 * @param {Integer} index - index of the slide
2146 * @param {Boolean} isWrap - will wrap-around to last/first if at the end
2147 * @param {Boolean} isInstant - will immediately set position at selected cell
2148 */
2149proto.select = function( index, isWrap, isInstant ) {
2150 if ( !this.isActive ) {
2151 return;
2152 }
2153 index = parseInt( index, 10 );
2154 this._wrapSelect( index );
2155
2156 if ( this.options.wrapAround || isWrap ) {
2157 index = utils.modulo( index, this.slides.length );
2158 }
2159 // bail if invalid index
2160 if ( !this.slides[ index ] ) {
2161 return;
2162 }
2163 var prevIndex = this.selectedIndex;
2164 this.selectedIndex = index;
2165 this.updateSelectedSlide();
2166 if ( isInstant ) {
2167 this.positionSliderAtSelected();
2168 } else {
2169 this.startAnimation();
2170 }
2171 if ( this.options.adaptiveHeight ) {
2172 this.setGallerySize();
2173 }
2174 // events
2175 this.dispatchEvent( 'select', null, [ index ] );
2176 // change event if new index
2177 if ( index != prevIndex ) {
2178 this.dispatchEvent( 'change', null, [ index ] );
2179 }
2180 // old v1 event name, remove in v3
2181 this.dispatchEvent('cellSelect');
2182};
2183
2184// wraps position for wrapAround, to move to closest slide. #113
2185proto._wrapSelect = function( index ) {
2186 var len = this.slides.length;
2187 var isWrapping = this.options.wrapAround && len > 1;
2188 if ( !isWrapping ) {
2189 return index;
2190 }
2191 var wrapIndex = utils.modulo( index, len );
2192 // go to shortest
2193 var delta = Math.abs( wrapIndex - this.selectedIndex );
2194 var backWrapDelta = Math.abs( ( wrapIndex + len ) - this.selectedIndex );
2195 var forewardWrapDelta = Math.abs( ( wrapIndex - len ) - this.selectedIndex );
2196 if ( !this.isDragSelect && backWrapDelta < delta ) {
2197 index += len;
2198 } else if ( !this.isDragSelect && forewardWrapDelta < delta ) {
2199 index -= len;
2200 }
2201 // wrap position so slider is within normal area
2202 if ( index < 0 ) {
2203 this.x -= this.slideableWidth;
2204 } else if ( index >= len ) {
2205 this.x += this.slideableWidth;
2206 }
2207};
2208
2209proto.previous = function( isWrap, isInstant ) {
2210 this.select( this.selectedIndex - 1, isWrap, isInstant );
2211};
2212
2213proto.next = function( isWrap, isInstant ) {
2214 this.select( this.selectedIndex + 1, isWrap, isInstant );
2215};
2216
2217proto.updateSelectedSlide = function() {
2218 var slide = this.slides[ this.selectedIndex ];
2219 // selectedIndex could be outside of slides, if triggered before resize()
2220 if ( !slide ) {
2221 return;
2222 }
2223 // unselect previous selected slide
2224 this.unselectSelectedSlide();
2225 // update new selected slide
2226 this.selectedSlide = slide;
2227 slide.select();
2228 this.selectedCells = slide.cells;
2229 this.selectedElements = slide.getCellElements();
2230 // HACK: selectedCell & selectedElement is first cell in slide, backwards compatibility
2231 // Remove in v3?
2232 this.selectedCell = slide.cells[0];
2233 this.selectedElement = this.selectedElements[0];
2234};
2235
2236proto.unselectSelectedSlide = function() {
2237 if ( this.selectedSlide ) {
2238 this.selectedSlide.unselect();
2239 }
2240};
2241
2242proto.selectInitialIndex = function() {
2243 var initialIndex = this.options.initialIndex;
2244 // already activated, select previous selectedIndex
2245 if ( this.isInitActivated ) {
2246 this.select( this.selectedIndex, false, true );
2247 return;
2248 }
2249 // select with selector string
2250 if ( initialIndex && typeof initialIndex == 'string' ) {
2251 var cell = this.queryCell( initialIndex );
2252 if ( cell ) {
2253 this.selectCell( initialIndex, false, true );
2254 return;
2255 }
2256 }
2257
2258 var index = 0;
2259 // select with number
2260 if ( initialIndex && this.slides[ initialIndex ] ) {
2261 index = initialIndex;
2262 }
2263 // select instantly
2264 this.select( index, false, true );
2265};
2266
2267/**
2268 * select slide from number or cell element
2269 * @param {[Element, Number]} value - zero-based index or element to select
2270 * @param {Boolean} isWrap - enables wrapping around for extra index
2271 * @param {Boolean} isInstant - disables slide animation
2272 */
2273proto.selectCell = function( value, isWrap, isInstant ) {
2274 // get cell
2275 var cell = this.queryCell( value );
2276 if ( !cell ) {
2277 return;
2278 }
2279
2280 var index = this.getCellSlideIndex( cell );
2281 this.select( index, isWrap, isInstant );
2282};
2283
2284proto.getCellSlideIndex = function( cell ) {
2285 // get index of slides that has cell
2286 for ( var i = 0; i < this.slides.length; i++ ) {
2287 var slide = this.slides[i];
2288 var index = slide.cells.indexOf( cell );
2289 if ( index != -1 ) {
2290 return i;
2291 }
2292 }
2293};
2294
2295// -------------------------- get cells -------------------------- //
2296
2297/**
2298 * get Flickity.Cell, given an Element
2299 * @param {Element} elem - matching cell element
2300 * @returns {Flickity.Cell} cell - matching cell
2301 */
2302proto.getCell = function( elem ) {
2303 // loop through cells to get the one that matches
2304 for ( var i = 0; i < this.cells.length; i++ ) {
2305 var cell = this.cells[i];
2306 if ( cell.element == elem ) {
2307 return cell;
2308 }
2309 }
2310};
2311
2312/**
2313 * get collection of Flickity.Cells, given Elements
2314 * @param {[Element, Array, NodeList]} elems - multiple elements
2315 * @returns {Array} cells - Flickity.Cells
2316 */
2317proto.getCells = function( elems ) {
2318 elems = utils.makeArray( elems );
2319 var cells = [];
2320 elems.forEach( function( elem ) {
2321 var cell = this.getCell( elem );
2322 if ( cell ) {
2323 cells.push( cell );
2324 }
2325 }, this );
2326 return cells;
2327};
2328
2329/**
2330 * get cell elements
2331 * @returns {Array} cellElems
2332 */
2333proto.getCellElements = function() {
2334 return this.cells.map( function( cell ) {
2335 return cell.element;
2336 } );
2337};
2338
2339/**
2340 * get parent cell from an element
2341 * @param {Element} elem - child element
2342 * @returns {Flickit.Cell} cell - parent cell
2343 */
2344proto.getParentCell = function( elem ) {
2345 // first check if elem is cell
2346 var cell = this.getCell( elem );
2347 if ( cell ) {
2348 return cell;
2349 }
2350 // try to get parent cell elem
2351 elem = utils.getParent( elem, '.flickity-slider > *' );
2352 return this.getCell( elem );
2353};
2354
2355/**
2356 * get cells adjacent to a slide
2357 * @param {Integer} adjCount - number of adjacent slides
2358 * @param {Integer} index - index of slide to start
2359 * @returns {Array} cells - array of Flickity.Cells
2360 */
2361proto.getAdjacentCellElements = function( adjCount, index ) {
2362 if ( !adjCount ) {
2363 return this.selectedSlide.getCellElements();
2364 }
2365 index = index === undefined ? this.selectedIndex : index;
2366
2367 var len = this.slides.length;
2368 if ( 1 + ( adjCount * 2 ) >= len ) {
2369 return this.getCellElements();
2370 }
2371
2372 var cellElems = [];
2373 for ( var i = index - adjCount; i <= index + adjCount; i++ ) {
2374 var slideIndex = this.options.wrapAround ? utils.modulo( i, len ) : i;
2375 var slide = this.slides[ slideIndex ];
2376 if ( slide ) {
2377 cellElems = cellElems.concat( slide.getCellElements() );
2378 }
2379 }
2380 return cellElems;
2381};
2382
2383/**
2384 * select slide from number or cell element
2385 * @param {[Element, String, Number]} selector - element, selector string, or index
2386 * @returns {Flickity.Cell} - matching cell
2387 */
2388proto.queryCell = function( selector ) {
2389 if ( typeof selector == 'number' ) {
2390 // use number as index
2391 return this.cells[ selector ];
2392 }
2393 if ( typeof selector == 'string' ) {
2394 // do not select invalid selectors from hash: #123, #/. #791
2395 if ( selector.match( /^[#.]?[\d/]/ ) ) {
2396 return;
2397 }
2398 // use string as selector, get element
2399 selector = this.element.querySelector( selector );
2400 }
2401 // get cell from element
2402 return this.getCell( selector );
2403};
2404
2405// -------------------------- events -------------------------- //
2406
2407proto.uiChange = function() {
2408 this.emitEvent('uiChange');
2409};
2410
2411// keep focus on element when child UI elements are clicked
2412proto.childUIPointerDown = function( event ) {
2413 // HACK iOS does not allow touch events to bubble up?!
2414 if ( event.type != 'touchstart' ) {
2415 event.preventDefault();
2416 }
2417 this.focus();
2418};
2419
2420// ----- resize ----- //
2421
2422proto.onresize = function() {
2423 this.watchCSS();
2424 this.resize();
2425};
2426
2427utils.debounceMethod( Flickity, 'onresize', 150 );
2428
2429proto.resize = function() {
2430 if ( !this.isActive ) {
2431 return;
2432 }
2433 this.getSize();
2434 // wrap values
2435 if ( this.options.wrapAround ) {
2436 this.x = utils.modulo( this.x, this.slideableWidth );
2437 }
2438 this.positionCells();
2439 this._getWrapShiftCells();
2440 this.setGallerySize();
2441 this.emitEvent('resize');
2442 // update selected index for group slides, instant
2443 // TODO: position can be lost between groups of various numbers
2444 var selectedElement = this.selectedElements && this.selectedElements[0];
2445 this.selectCell( selectedElement, false, true );
2446};
2447
2448// watches the :after property, activates/deactivates
2449proto.watchCSS = function() {
2450 var watchOption = this.options.watchCSS;
2451 if ( !watchOption ) {
2452 return;
2453 }
2454
2455 var afterContent = getComputedStyle( this.element, ':after' ).content;
2456 // activate if :after { content: 'flickity' }
2457 if ( afterContent.indexOf('flickity') != -1 ) {
2458 this.activate();
2459 } else {
2460 this.deactivate();
2461 }
2462};
2463
2464// ----- keydown ----- //
2465
2466// go previous/next if left/right keys pressed
2467proto.onkeydown = function( event ) {
2468 // only work if element is in focus
2469 var isNotFocused = document.activeElement && document.activeElement != this.element;
2470 if ( !this.options.accessibility || isNotFocused ) {
2471 return;
2472 }
2473
2474 var handler = Flickity.keyboardHandlers[ event.keyCode ];
2475 if ( handler ) {
2476 handler.call( this );
2477 }
2478};
2479
2480Flickity.keyboardHandlers = {
2481 // left arrow
2482 37: function() {
2483 var leftMethod = this.options.rightToLeft ? 'next' : 'previous';
2484 this.uiChange();
2485 this[ leftMethod ]();
2486 },
2487 // right arrow
2488 39: function() {
2489 var rightMethod = this.options.rightToLeft ? 'previous' : 'next';
2490 this.uiChange();
2491 this[ rightMethod ]();
2492 },
2493};
2494
2495// ----- focus ----- //
2496
2497proto.focus = function() {
2498 // TODO remove scrollTo once focus options gets more support
2499 // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus ...
2500 // #Browser_compatibility
2501 var prevScrollY = window.pageYOffset;
2502 this.element.focus({ preventScroll: true });
2503 // hack to fix scroll jump after focus, #76
2504 if ( window.pageYOffset != prevScrollY ) {
2505 window.scrollTo( window.pageXOffset, prevScrollY );
2506 }
2507};
2508
2509// -------------------------- destroy -------------------------- //
2510
2511// deactivate all Flickity functionality, but keep stuff available
2512proto.deactivate = function() {
2513 if ( !this.isActive ) {
2514 return;
2515 }
2516 this.element.classList.remove('flickity-enabled');
2517 this.element.classList.remove('flickity-rtl');
2518 this.unselectSelectedSlide();
2519 // destroy cells
2520 this.cells.forEach( function( cell ) {
2521 cell.destroy();
2522 } );
2523 this.element.removeChild( this.viewport );
2524 // move child elements back into element
2525 moveElements( this.slider.children, this.element );
2526 if ( this.options.accessibility ) {
2527 this.element.removeAttribute('tabIndex');
2528 this.element.removeEventListener( 'keydown', this );
2529 }
2530 // set flags
2531 this.isActive = false;
2532 this.emitEvent('deactivate');
2533};
2534
2535proto.destroy = function() {
2536 this.deactivate();
2537 window.removeEventListener( 'resize', this );
2538 this.allOff();
2539 this.emitEvent('destroy');
2540 if ( jQuery && this.$element ) {
2541 jQuery.removeData( this.element, 'flickity' );
2542 }
2543 delete this.element.flickityGUID;
2544 delete instances[ this.guid ];
2545};
2546
2547// -------------------------- prototype -------------------------- //
2548
2549utils.extend( proto, animatePrototype );
2550
2551// -------------------------- extras -------------------------- //
2552
2553/**
2554 * get Flickity instance from element
2555 * @param {[Element, String]} elem - element or selector string
2556 * @returns {Flickity} - Flickity instance
2557 */
2558Flickity.data = function( elem ) {
2559 elem = utils.getQueryElement( elem );
2560 var id = elem && elem.flickityGUID;
2561 return id && instances[ id ];
2562};
2563
2564utils.htmlInit( Flickity, 'flickity' );
2565
2566if ( jQuery && jQuery.bridget ) {
2567 jQuery.bridget( 'flickity', Flickity );
2568}
2569
2570// set internal jQuery, for Webpack + jQuery v3, #478
2571Flickity.setJQuery = function( jq ) {
2572 jQuery = jq;
2573};
2574
2575Flickity.Cell = Cell;
2576Flickity.Slide = Slide;
2577
2578return Flickity;
2579
2580} ) );
2581
2582/*!
2583 * Unipointer v2.3.0
2584 * base class for doing one thing with pointer event
2585 * MIT license
2586 */
2587
2588/*jshint browser: true, undef: true, unused: true, strict: true */
2589
2590( function( window, factory ) {
2591 // universal module definition
2592 /* jshint strict: false */ /*global define, module, require */
2593 if ( typeof define == 'function' && define.amd ) {
2594 // AMD
2595 define( 'unipointer/unipointer',[
2596 'ev-emitter/ev-emitter'
2597 ], function( EvEmitter ) {
2598 return factory( window, EvEmitter );
2599 });
2600 } else if ( typeof module == 'object' && module.exports ) {
2601 // CommonJS
2602 module.exports = factory(
2603 window,
2604 require('ev-emitter')
2605 );
2606 } else {
2607 // browser global
2608 window.Unipointer = factory(
2609 window,
2610 window.EvEmitter
2611 );
2612 }
2613
2614}( window, function factory( window, EvEmitter ) {
2615
2616
2617
2618function noop() {}
2619
2620function Unipointer() {}
2621
2622// inherit EvEmitter
2623var proto = Unipointer.prototype = Object.create( EvEmitter.prototype );
2624
2625proto.bindStartEvent = function( elem ) {
2626 this._bindStartEvent( elem, true );
2627};
2628
2629proto.unbindStartEvent = function( elem ) {
2630 this._bindStartEvent( elem, false );
2631};
2632
2633/**
2634 * Add or remove start event
2635 * @param {Boolean} isAdd - remove if falsey
2636 */
2637proto._bindStartEvent = function( elem, isAdd ) {
2638 // munge isAdd, default to true
2639 isAdd = isAdd === undefined ? true : isAdd;
2640 var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener';
2641
2642 // default to mouse events
2643 var startEvent = 'mousedown';
2644 if ( window.PointerEvent ) {
2645 // Pointer Events
2646 startEvent = 'pointerdown';
2647 } else if ( 'ontouchstart' in window ) {
2648 // Touch Events. iOS Safari
2649 startEvent = 'touchstart';
2650 }
2651 elem[ bindMethod ]( startEvent, this );
2652};
2653
2654// trigger handler methods for events
2655proto.handleEvent = function( event ) {
2656 var method = 'on' + event.type;
2657 if ( this[ method ] ) {
2658 this[ method ]( event );
2659 }
2660};
2661
2662// returns the touch that we're keeping track of
2663proto.getTouch = function( touches ) {
2664 for ( var i=0; i < touches.length; i++ ) {
2665 var touch = touches[i];
2666 if ( touch.identifier == this.pointerIdentifier ) {
2667 return touch;
2668 }
2669 }
2670};
2671
2672// ----- start event ----- //
2673
2674proto.onmousedown = function( event ) {
2675 // dismiss clicks from right or middle buttons
2676 var button = event.button;
2677 if ( button && ( button !== 0 && button !== 1 ) ) {
2678 return;
2679 }
2680 this._pointerDown( event, event );
2681};
2682
2683proto.ontouchstart = function( event ) {
2684 this._pointerDown( event, event.changedTouches[0] );
2685};
2686
2687proto.onpointerdown = function( event ) {
2688 this._pointerDown( event, event );
2689};
2690
2691/**
2692 * pointer start
2693 * @param {Event} event
2694 * @param {Event or Touch} pointer
2695 */
2696proto._pointerDown = function( event, pointer ) {
2697 // dismiss right click and other pointers
2698 // button = 0 is okay, 1-4 not
2699 if ( event.button || this.isPointerDown ) {
2700 return;
2701 }
2702
2703 this.isPointerDown = true;
2704 // save pointer identifier to match up touch events
2705 this.pointerIdentifier = pointer.pointerId !== undefined ?
2706 // pointerId for pointer events, touch.indentifier for touch events
2707 pointer.pointerId : pointer.identifier;
2708
2709 this.pointerDown( event, pointer );
2710};
2711
2712proto.pointerDown = function( event, pointer ) {
2713 this._bindPostStartEvents( event );
2714 this.emitEvent( 'pointerDown', [ event, pointer ] );
2715};
2716
2717// hash of events to be bound after start event
2718var postStartEvents = {
2719 mousedown: [ 'mousemove', 'mouseup' ],
2720 touchstart: [ 'touchmove', 'touchend', 'touchcancel' ],
2721 pointerdown: [ 'pointermove', 'pointerup', 'pointercancel' ],
2722};
2723
2724proto._bindPostStartEvents = function( event ) {
2725 if ( !event ) {
2726 return;
2727 }
2728 // get proper events to match start event
2729 var events = postStartEvents[ event.type ];
2730 // bind events to node
2731 events.forEach( function( eventName ) {
2732 window.addEventListener( eventName, this );
2733 }, this );
2734 // save these arguments
2735 this._boundPointerEvents = events;
2736};
2737
2738proto._unbindPostStartEvents = function() {
2739 // check for _boundEvents, in case dragEnd triggered twice (old IE8 bug)
2740 if ( !this._boundPointerEvents ) {
2741 return;
2742 }
2743 this._boundPointerEvents.forEach( function( eventName ) {
2744 window.removeEventListener( eventName, this );
2745 }, this );
2746
2747 delete this._boundPointerEvents;
2748};
2749
2750// ----- move event ----- //
2751
2752proto.onmousemove = function( event ) {
2753 this._pointerMove( event, event );
2754};
2755
2756proto.onpointermove = function( event ) {
2757 if ( event.pointerId == this.pointerIdentifier ) {
2758 this._pointerMove( event, event );
2759 }
2760};
2761
2762proto.ontouchmove = function( event ) {
2763 var touch = this.getTouch( event.changedTouches );
2764 if ( touch ) {
2765 this._pointerMove( event, touch );
2766 }
2767};
2768
2769/**
2770 * pointer move
2771 * @param {Event} event
2772 * @param {Event or Touch} pointer
2773 * @private
2774 */
2775proto._pointerMove = function( event, pointer ) {
2776 this.pointerMove( event, pointer );
2777};
2778
2779// public
2780proto.pointerMove = function( event, pointer ) {
2781 this.emitEvent( 'pointerMove', [ event, pointer ] );
2782};
2783
2784// ----- end event ----- //
2785
2786
2787proto.onmouseup = function( event ) {
2788 this._pointerUp( event, event );
2789};
2790
2791proto.onpointerup = function( event ) {
2792 if ( event.pointerId == this.pointerIdentifier ) {
2793 this._pointerUp( event, event );
2794 }
2795};
2796
2797proto.ontouchend = function( event ) {
2798 var touch = this.getTouch( event.changedTouches );
2799 if ( touch ) {
2800 this._pointerUp( event, touch );
2801 }
2802};
2803
2804/**
2805 * pointer up
2806 * @param {Event} event
2807 * @param {Event or Touch} pointer
2808 * @private
2809 */
2810proto._pointerUp = function( event, pointer ) {
2811 this._pointerDone();
2812 this.pointerUp( event, pointer );
2813};
2814
2815// public
2816proto.pointerUp = function( event, pointer ) {
2817 this.emitEvent( 'pointerUp', [ event, pointer ] );
2818};
2819
2820// ----- pointer done ----- //
2821
2822// triggered on pointer up & pointer cancel
2823proto._pointerDone = function() {
2824 this._pointerReset();
2825 this._unbindPostStartEvents();
2826 this.pointerDone();
2827};
2828
2829proto._pointerReset = function() {
2830 // reset properties
2831 this.isPointerDown = false;
2832 delete this.pointerIdentifier;
2833};
2834
2835proto.pointerDone = noop;
2836
2837// ----- pointer cancel ----- //
2838
2839proto.onpointercancel = function( event ) {
2840 if ( event.pointerId == this.pointerIdentifier ) {
2841 this._pointerCancel( event, event );
2842 }
2843};
2844
2845proto.ontouchcancel = function( event ) {
2846 var touch = this.getTouch( event.changedTouches );
2847 if ( touch ) {
2848 this._pointerCancel( event, touch );
2849 }
2850};
2851
2852/**
2853 * pointer cancel
2854 * @param {Event} event
2855 * @param {Event or Touch} pointer
2856 * @private
2857 */
2858proto._pointerCancel = function( event, pointer ) {
2859 this._pointerDone();
2860 this.pointerCancel( event, pointer );
2861};
2862
2863// public
2864proto.pointerCancel = function( event, pointer ) {
2865 this.emitEvent( 'pointerCancel', [ event, pointer ] );
2866};
2867
2868// ----- ----- //
2869
2870// utility function for getting x/y coords from event
2871Unipointer.getPointerPoint = function( pointer ) {
2872 return {
2873 x: pointer.pageX,
2874 y: pointer.pageY
2875 };
2876};
2877
2878// ----- ----- //
2879
2880return Unipointer;
2881
2882}));
2883
2884/*!
2885 * Unidragger v2.3.1
2886 * Draggable base class
2887 * MIT license
2888 */
2889
2890/*jshint browser: true, unused: true, undef: true, strict: true */
2891
2892( function( window, factory ) {
2893 // universal module definition
2894 /*jshint strict: false */ /*globals define, module, require */
2895
2896 if ( typeof define == 'function' && define.amd ) {
2897 // AMD
2898 define( 'unidragger/unidragger',[
2899 'unipointer/unipointer'
2900 ], function( Unipointer ) {
2901 return factory( window, Unipointer );
2902 });
2903 } else if ( typeof module == 'object' && module.exports ) {
2904 // CommonJS
2905 module.exports = factory(
2906 window,
2907 require('unipointer')
2908 );
2909 } else {
2910 // browser global
2911 window.Unidragger = factory(
2912 window,
2913 window.Unipointer
2914 );
2915 }
2916
2917}( window, function factory( window, Unipointer ) {
2918
2919
2920
2921// -------------------------- Unidragger -------------------------- //
2922
2923function Unidragger() {}
2924
2925// inherit Unipointer & EvEmitter
2926var proto = Unidragger.prototype = Object.create( Unipointer.prototype );
2927
2928// ----- bind start ----- //
2929
2930proto.bindHandles = function() {
2931 this._bindHandles( true );
2932};
2933
2934proto.unbindHandles = function() {
2935 this._bindHandles( false );
2936};
2937
2938/**
2939 * Add or remove start event
2940 * @param {Boolean} isAdd
2941 */
2942proto._bindHandles = function( isAdd ) {
2943 // munge isAdd, default to true
2944 isAdd = isAdd === undefined ? true : isAdd;
2945 // bind each handle
2946 var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener';
2947 var touchAction = isAdd ? this._touchActionValue : '';
2948 for ( var i=0; i < this.handles.length; i++ ) {
2949 var handle = this.handles[i];
2950 this._bindStartEvent( handle, isAdd );
2951 handle[ bindMethod ]( 'click', this );
2952 // touch-action: none to override browser touch gestures. metafizzy/flickity#540
2953 if ( window.PointerEvent ) {
2954 handle.style.touchAction = touchAction;
2955 }
2956 }
2957};
2958
2959// prototype so it can be overwriteable by Flickity
2960proto._touchActionValue = 'none';
2961
2962// ----- start event ----- //
2963
2964/**
2965 * pointer start
2966 * @param {Event} event
2967 * @param {Event or Touch} pointer
2968 */
2969proto.pointerDown = function( event, pointer ) {
2970 var isOkay = this.okayPointerDown( event );
2971 if ( !isOkay ) {
2972 return;
2973 }
2974 // track start event position
2975 // Safari 9 overrides pageX and pageY. These values needs to be copied. flickity#842
2976 this.pointerDownPointer = {
2977 pageX: pointer.pageX,
2978 pageY: pointer.pageY,
2979 };
2980
2981 event.preventDefault();
2982 this.pointerDownBlur();
2983 // bind move and end events
2984 this._bindPostStartEvents( event );
2985 this.emitEvent( 'pointerDown', [ event, pointer ] );
2986};
2987
2988// nodes that have text fields
2989var cursorNodes = {
2990 TEXTAREA: true,
2991 INPUT: true,
2992 SELECT: true,
2993 OPTION: true,
2994};
2995
2996// input types that do not have text fields
2997var clickTypes = {
2998 radio: true,
2999 checkbox: true,
3000 button: true,
3001 submit: true,
3002 image: true,
3003 file: true,
3004};
3005
3006// dismiss inputs with text fields. flickity#403, flickity#404
3007proto.okayPointerDown = function( event ) {
3008 var isCursorNode = cursorNodes[ event.target.nodeName ];
3009 var isClickType = clickTypes[ event.target.type ];
3010 var isOkay = !isCursorNode || isClickType;
3011 if ( !isOkay ) {
3012 this._pointerReset();
3013 }
3014 return isOkay;
3015};
3016
3017// kludge to blur previously focused input
3018proto.pointerDownBlur = function() {
3019 var focused = document.activeElement;
3020 // do not blur body for IE10, metafizzy/flickity#117
3021 var canBlur = focused && focused.blur && focused != document.body;
3022 if ( canBlur ) {
3023 focused.blur();
3024 }
3025};
3026
3027// ----- move event ----- //
3028
3029/**
3030 * drag move
3031 * @param {Event} event
3032 * @param {Event or Touch} pointer
3033 */
3034proto.pointerMove = function( event, pointer ) {
3035 var moveVector = this._dragPointerMove( event, pointer );
3036 this.emitEvent( 'pointerMove', [ event, pointer, moveVector ] );
3037 this._dragMove( event, pointer, moveVector );
3038};
3039
3040// base pointer move logic
3041proto._dragPointerMove = function( event, pointer ) {
3042 var moveVector = {
3043 x: pointer.pageX - this.pointerDownPointer.pageX,
3044 y: pointer.pageY - this.pointerDownPointer.pageY
3045 };
3046 // start drag if pointer has moved far enough to start drag
3047 if ( !this.isDragging && this.hasDragStarted( moveVector ) ) {
3048 this._dragStart( event, pointer );
3049 }
3050 return moveVector;
3051};
3052
3053// condition if pointer has moved far enough to start drag
3054proto.hasDragStarted = function( moveVector ) {
3055 return Math.abs( moveVector.x ) > 3 || Math.abs( moveVector.y ) > 3;
3056};
3057
3058// ----- end event ----- //
3059
3060/**
3061 * pointer up
3062 * @param {Event} event
3063 * @param {Event or Touch} pointer
3064 */
3065proto.pointerUp = function( event, pointer ) {
3066 this.emitEvent( 'pointerUp', [ event, pointer ] );
3067 this._dragPointerUp( event, pointer );
3068};
3069
3070proto._dragPointerUp = function( event, pointer ) {
3071 if ( this.isDragging ) {
3072 this._dragEnd( event, pointer );
3073 } else {
3074 // pointer didn't move enough for drag to start
3075 this._staticClick( event, pointer );
3076 }
3077};
3078
3079// -------------------------- drag -------------------------- //
3080
3081// dragStart
3082proto._dragStart = function( event, pointer ) {
3083 this.isDragging = true;
3084 // prevent clicks
3085 this.isPreventingClicks = true;
3086 this.dragStart( event, pointer );
3087};
3088
3089proto.dragStart = function( event, pointer ) {
3090 this.emitEvent( 'dragStart', [ event, pointer ] );
3091};
3092
3093// dragMove
3094proto._dragMove = function( event, pointer, moveVector ) {
3095 // do not drag if not dragging yet
3096 if ( !this.isDragging ) {
3097 return;
3098 }
3099
3100 this.dragMove( event, pointer, moveVector );
3101};
3102
3103proto.dragMove = function( event, pointer, moveVector ) {
3104 event.preventDefault();
3105 this.emitEvent( 'dragMove', [ event, pointer, moveVector ] );
3106};
3107
3108// dragEnd
3109proto._dragEnd = function( event, pointer ) {
3110 // set flags
3111 this.isDragging = false;
3112 // re-enable clicking async
3113 setTimeout( function() {
3114 delete this.isPreventingClicks;
3115 }.bind( this ) );
3116
3117 this.dragEnd( event, pointer );
3118};
3119
3120proto.dragEnd = function( event, pointer ) {
3121 this.emitEvent( 'dragEnd', [ event, pointer ] );
3122};
3123
3124// ----- onclick ----- //
3125
3126// handle all clicks and prevent clicks when dragging
3127proto.onclick = function( event ) {
3128 if ( this.isPreventingClicks ) {
3129 event.preventDefault();
3130 }
3131};
3132
3133// ----- staticClick ----- //
3134
3135// triggered after pointer down & up with no/tiny movement
3136proto._staticClick = function( event, pointer ) {
3137 // ignore emulated mouse up clicks
3138 if ( this.isIgnoringMouseUp && event.type == 'mouseup' ) {
3139 return;
3140 }
3141
3142 this.staticClick( event, pointer );
3143
3144 // set flag for emulated clicks 300ms after touchend
3145 if ( event.type != 'mouseup' ) {
3146 this.isIgnoringMouseUp = true;
3147 // reset flag after 300ms
3148 setTimeout( function() {
3149 delete this.isIgnoringMouseUp;
3150 }.bind( this ), 400 );
3151 }
3152};
3153
3154proto.staticClick = function( event, pointer ) {
3155 this.emitEvent( 'staticClick', [ event, pointer ] );
3156};
3157
3158// ----- utils ----- //
3159
3160Unidragger.getPointerPoint = Unipointer.getPointerPoint;
3161
3162// ----- ----- //
3163
3164return Unidragger;
3165
3166}));
3167
3168// drag
3169( function( window, factory ) {
3170 // universal module definition
3171 if ( typeof define == 'function' && define.amd ) {
3172 // AMD
3173 define( 'flickity/js/drag',[
3174 './flickity',
3175 'unidragger/unidragger',
3176 'fizzy-ui-utils/utils',
3177 ], function( Flickity, Unidragger, utils ) {
3178 return factory( window, Flickity, Unidragger, utils );
3179 } );
3180 } else if ( typeof module == 'object' && module.exports ) {
3181 // CommonJS
3182 module.exports = factory(
3183 window,
3184 require('./flickity'),
3185 require('unidragger'),
3186 require('fizzy-ui-utils')
3187 );
3188 } else {
3189 // browser global
3190 window.Flickity = factory(
3191 window,
3192 window.Flickity,
3193 window.Unidragger,
3194 window.fizzyUIUtils
3195 );
3196 }
3197
3198}( window, function factory( window, Flickity, Unidragger, utils ) {
3199
3200
3201
3202// ----- defaults ----- //
3203
3204utils.extend( Flickity.defaults, {
3205 draggable: '>1',
3206 dragThreshold: 3,
3207} );
3208
3209// ----- create ----- //
3210
3211Flickity.createMethods.push('_createDrag');
3212
3213// -------------------------- drag prototype -------------------------- //
3214
3215var proto = Flickity.prototype;
3216utils.extend( proto, Unidragger.prototype );
3217proto._touchActionValue = 'pan-y';
3218
3219// -------------------------- -------------------------- //
3220
3221var isTouch = 'createTouch' in document;
3222var isTouchmoveScrollCanceled = false;
3223
3224proto._createDrag = function() {
3225 this.on( 'activate', this.onActivateDrag );
3226 this.on( 'uiChange', this._uiChangeDrag );
3227 this.on( 'deactivate', this.onDeactivateDrag );
3228 this.on( 'cellChange', this.updateDraggable );
3229 // TODO updateDraggable on resize? if groupCells & slides change
3230 // HACK - add seemingly innocuous handler to fix iOS 10 scroll behavior
3231 // #457, RubaXa/Sortable#973
3232 if ( isTouch && !isTouchmoveScrollCanceled ) {
3233 window.addEventListener( 'touchmove', function() {} );
3234 isTouchmoveScrollCanceled = true;
3235 }
3236};
3237
3238proto.onActivateDrag = function() {
3239 this.handles = [ this.viewport ];
3240 this.bindHandles();
3241 this.updateDraggable();
3242};
3243
3244proto.onDeactivateDrag = function() {
3245 this.unbindHandles();
3246 this.element.classList.remove('is-draggable');
3247};
3248
3249proto.updateDraggable = function() {
3250 // disable dragging if less than 2 slides. #278
3251 if ( this.options.draggable == '>1' ) {
3252 this.isDraggable = this.slides.length > 1;
3253 } else {
3254 this.isDraggable = this.options.draggable;
3255 }
3256 if ( this.isDraggable ) {
3257 this.element.classList.add('is-draggable');
3258 } else {
3259 this.element.classList.remove('is-draggable');
3260 }
3261};
3262
3263// backwards compatibility
3264proto.bindDrag = function() {
3265 this.options.draggable = true;
3266 this.updateDraggable();
3267};
3268
3269proto.unbindDrag = function() {
3270 this.options.draggable = false;
3271 this.updateDraggable();
3272};
3273
3274proto._uiChangeDrag = function() {
3275 delete this.isFreeScrolling;
3276};
3277
3278// -------------------------- pointer events -------------------------- //
3279
3280proto.pointerDown = function( event, pointer ) {
3281 if ( !this.isDraggable ) {
3282 this._pointerDownDefault( event, pointer );
3283 return;
3284 }
3285 var isOkay = this.okayPointerDown( event );
3286 if ( !isOkay ) {
3287 return;
3288 }
3289
3290 this._pointerDownPreventDefault( event );
3291 this.pointerDownFocus( event );
3292 // blur
3293 if ( document.activeElement != this.element ) {
3294 // do not blur if already focused
3295 this.pointerDownBlur();
3296 }
3297
3298 // stop if it was moving
3299 this.dragX = this.x;
3300 this.viewport.classList.add('is-pointer-down');
3301 // track scrolling
3302 this.pointerDownScroll = getScrollPosition();
3303 window.addEventListener( 'scroll', this );
3304
3305 this._pointerDownDefault( event, pointer );
3306};
3307
3308// default pointerDown logic, used for staticClick
3309proto._pointerDownDefault = function( event, pointer ) {
3310 // track start event position
3311 // Safari 9 overrides pageX and pageY. These values needs to be copied. #779
3312 this.pointerDownPointer = {
3313 pageX: pointer.pageX,
3314 pageY: pointer.pageY,
3315 };
3316 // bind move and end events
3317 this._bindPostStartEvents( event );
3318 this.dispatchEvent( 'pointerDown', event, [ pointer ] );
3319};
3320
3321var focusNodes = {
3322 INPUT: true,
3323 TEXTAREA: true,
3324 SELECT: true,
3325};
3326
3327proto.pointerDownFocus = function( event ) {
3328 var isFocusNode = focusNodes[ event.target.nodeName ];
3329 if ( !isFocusNode ) {
3330 this.focus();
3331 }
3332};
3333
3334proto._pointerDownPreventDefault = function( event ) {
3335 var isTouchStart = event.type == 'touchstart';
3336 var isTouchPointer = event.pointerType == 'touch';
3337 var isFocusNode = focusNodes[ event.target.nodeName ];
3338 if ( !isTouchStart && !isTouchPointer && !isFocusNode ) {
3339 event.preventDefault();
3340 }
3341};
3342
3343// ----- move ----- //
3344
3345proto.hasDragStarted = function( moveVector ) {
3346 return Math.abs( moveVector.x ) > this.options.dragThreshold;
3347};
3348
3349// ----- up ----- //
3350
3351proto.pointerUp = function( event, pointer ) {
3352 delete this.isTouchScrolling;
3353 this.viewport.classList.remove('is-pointer-down');
3354 this.dispatchEvent( 'pointerUp', event, [ pointer ] );
3355 this._dragPointerUp( event, pointer );
3356};
3357
3358proto.pointerDone = function() {
3359 window.removeEventListener( 'scroll', this );
3360 delete this.pointerDownScroll;
3361};
3362
3363// -------------------------- dragging -------------------------- //
3364
3365proto.dragStart = function( event, pointer ) {
3366 if ( !this.isDraggable ) {
3367 return;
3368 }
3369 this.dragStartPosition = this.x;
3370 this.startAnimation();
3371 window.removeEventListener( 'scroll', this );
3372 this.dispatchEvent( 'dragStart', event, [ pointer ] );
3373};
3374
3375proto.pointerMove = function( event, pointer ) {
3376 var moveVector = this._dragPointerMove( event, pointer );
3377 this.dispatchEvent( 'pointerMove', event, [ pointer, moveVector ] );
3378 this._dragMove( event, pointer, moveVector );
3379};
3380
3381proto.dragMove = function( event, pointer, moveVector ) {
3382 if ( !this.isDraggable ) {
3383 return;
3384 }
3385 event.preventDefault();
3386
3387 this.previousDragX = this.dragX;
3388 // reverse if right-to-left
3389 var direction = this.options.rightToLeft ? -1 : 1;
3390 if ( this.options.wrapAround ) {
3391 // wrap around move. #589
3392 moveVector.x %= this.slideableWidth;
3393 }
3394 var dragX = this.dragStartPosition + moveVector.x * direction;
3395
3396 if ( !this.options.wrapAround && this.slides.length ) {
3397 // slow drag
3398 var originBound = Math.max( -this.slides[0].target, this.dragStartPosition );
3399 dragX = dragX > originBound ? ( dragX + originBound ) * 0.5 : dragX;
3400 var endBound = Math.min( -this.getLastSlide().target, this.dragStartPosition );
3401 dragX = dragX < endBound ? ( dragX + endBound ) * 0.5 : dragX;
3402 }
3403
3404 this.dragX = dragX;
3405
3406 this.dragMoveTime = new Date();
3407 this.dispatchEvent( 'dragMove', event, [ pointer, moveVector ] );
3408};
3409
3410proto.dragEnd = function( event, pointer ) {
3411 if ( !this.isDraggable ) {
3412 return;
3413 }
3414 if ( this.options.freeScroll ) {
3415 this.isFreeScrolling = true;
3416 }
3417 // set selectedIndex based on where flick will end up
3418 var index = this.dragEndRestingSelect();
3419
3420 if ( this.options.freeScroll && !this.options.wrapAround ) {
3421 // if free-scroll & not wrap around
3422 // do not free-scroll if going outside of bounding slides
3423 // so bounding slides can attract slider, and keep it in bounds
3424 var restingX = this.getRestingPosition();
3425 this.isFreeScrolling = -restingX > this.slides[0].target &&
3426 -restingX < this.getLastSlide().target;
3427 } else if ( !this.options.freeScroll && index == this.selectedIndex ) {
3428 // boost selection if selected index has not changed
3429 index += this.dragEndBoostSelect();
3430 }
3431 delete this.previousDragX;
3432 // apply selection
3433 // TODO refactor this, selecting here feels weird
3434 // HACK, set flag so dragging stays in correct direction
3435 this.isDragSelect = this.options.wrapAround;
3436 this.select( index );
3437 delete this.isDragSelect;
3438 this.dispatchEvent( 'dragEnd', event, [ pointer ] );
3439};
3440
3441proto.dragEndRestingSelect = function() {
3442 var restingX = this.getRestingPosition();
3443 // how far away from selected slide
3444 var distance = Math.abs( this.getSlideDistance( -restingX, this.selectedIndex ) );
3445 // get closet resting going up and going down
3446 var positiveResting = this._getClosestResting( restingX, distance, 1 );
3447 var negativeResting = this._getClosestResting( restingX, distance, -1 );
3448 // use closer resting for wrap-around
3449 var index = positiveResting.distance < negativeResting.distance ?
3450 positiveResting.index : negativeResting.index;
3451 return index;
3452};
3453
3454/**
3455 * given resting X and distance to selected cell
3456 * get the distance and index of the closest cell
3457 * @param {Number} restingX - estimated post-flick resting position
3458 * @param {Number} distance - distance to selected cell
3459 * @param {Integer} increment - +1 or -1, going up or down
3460 * @returns {Object} - { distance: {Number}, index: {Integer} }
3461 */
3462proto._getClosestResting = function( restingX, distance, increment ) {
3463 var index = this.selectedIndex;
3464 var minDistance = Infinity;
3465 var condition = this.options.contain && !this.options.wrapAround ?
3466 // if contain, keep going if distance is equal to minDistance
3467 function( dist, minDist ) {
3468 return dist <= minDist;
3469 } : function( dist, minDist ) {
3470 return dist < minDist;
3471 };
3472 while ( condition( distance, minDistance ) ) {
3473 // measure distance to next cell
3474 index += increment;
3475 minDistance = distance;
3476 distance = this.getSlideDistance( -restingX, index );
3477 if ( distance === null ) {
3478 break;
3479 }
3480 distance = Math.abs( distance );
3481 }
3482 return {
3483 distance: minDistance,
3484 // selected was previous index
3485 index: index - increment,
3486 };
3487};
3488
3489/**
3490 * measure distance between x and a slide target
3491 * @param {Number} x - horizontal position
3492 * @param {Integer} index - slide index
3493 * @returns {Number} - slide distance
3494 */
3495proto.getSlideDistance = function( x, index ) {
3496 var len = this.slides.length;
3497 // wrap around if at least 2 slides
3498 var isWrapAround = this.options.wrapAround && len > 1;
3499 var slideIndex = isWrapAround ? utils.modulo( index, len ) : index;
3500 var slide = this.slides[ slideIndex ];
3501 if ( !slide ) {
3502 return null;
3503 }
3504 // add distance for wrap-around slides
3505 var wrap = isWrapAround ? this.slideableWidth * Math.floor( index/len ) : 0;
3506 return x - ( slide.target + wrap );
3507};
3508
3509proto.dragEndBoostSelect = function() {
3510 // do not boost if no previousDragX or dragMoveTime
3511 if ( this.previousDragX === undefined || !this.dragMoveTime ||
3512 // or if drag was held for 100 ms
3513 new Date() - this.dragMoveTime > 100 ) {
3514 return 0;
3515 }
3516
3517 var distance = this.getSlideDistance( -this.dragX, this.selectedIndex );
3518 var delta = this.previousDragX - this.dragX;
3519 if ( distance > 0 && delta > 0 ) {
3520 // boost to next if moving towards the right, and positive velocity
3521 return 1;
3522 } else if ( distance < 0 && delta < 0 ) {
3523 // boost to previous if moving towards the left, and negative velocity
3524 return -1;
3525 }
3526 return 0;
3527};
3528
3529// ----- staticClick ----- //
3530
3531proto.staticClick = function( event, pointer ) {
3532 // get clickedCell, if cell was clicked
3533 var clickedCell = this.getParentCell( event.target );
3534 var cellElem = clickedCell && clickedCell.element;
3535 var cellIndex = clickedCell && this.cells.indexOf( clickedCell );
3536 this.dispatchEvent( 'staticClick', event, [ pointer, cellElem, cellIndex ] );
3537};
3538
3539// ----- scroll ----- //
3540
3541proto.onscroll = function() {
3542 var scroll = getScrollPosition();
3543 var scrollMoveX = this.pointerDownScroll.x - scroll.x;
3544 var scrollMoveY = this.pointerDownScroll.y - scroll.y;
3545 // cancel click/tap if scroll is too much
3546 if ( Math.abs( scrollMoveX ) > 3 || Math.abs( scrollMoveY ) > 3 ) {
3547 this._pointerDone();
3548 }
3549};
3550
3551// ----- utils ----- //
3552
3553function getScrollPosition() {
3554 return {
3555 x: window.pageXOffset,
3556 y: window.pageYOffset,
3557 };
3558}
3559
3560// ----- ----- //
3561
3562return Flickity;
3563
3564} ) );
3565
3566// prev/next buttons
3567( function( window, factory ) {
3568 // universal module definition
3569 if ( typeof define == 'function' && define.amd ) {
3570 // AMD
3571 define( 'flickity/js/prev-next-button',[
3572 './flickity',
3573 'unipointer/unipointer',
3574 'fizzy-ui-utils/utils',
3575 ], function( Flickity, Unipointer, utils ) {
3576 return factory( window, Flickity, Unipointer, utils );
3577 } );
3578 } else if ( typeof module == 'object' && module.exports ) {
3579 // CommonJS
3580 module.exports = factory(
3581 window,
3582 require('./flickity'),
3583 require('unipointer'),
3584 require('fizzy-ui-utils')
3585 );
3586 } else {
3587 // browser global
3588 factory(
3589 window,
3590 window.Flickity,
3591 window.Unipointer,
3592 window.fizzyUIUtils
3593 );
3594 }
3595
3596}( window, function factory( window, Flickity, Unipointer, utils ) {
3597'use strict';
3598
3599var svgURI = 'http://www.w3.org/2000/svg';
3600
3601// -------------------------- PrevNextButton -------------------------- //
3602
3603function PrevNextButton( direction, parent ) {
3604 this.direction = direction;
3605 this.parent = parent;
3606 this._create();
3607}
3608
3609PrevNextButton.prototype = Object.create( Unipointer.prototype );
3610
3611PrevNextButton.prototype._create = function() {
3612 // properties
3613 this.isEnabled = true;
3614 this.isPrevious = this.direction == -1;
3615 var leftDirection = this.parent.options.rightToLeft ? 1 : -1;
3616 this.isLeft = this.direction == leftDirection;
3617
3618 var element = this.element = document.createElement('button');
3619 element.className = 'flickity-button flickity-prev-next-button';
3620 element.className += this.isPrevious ? ' previous' : ' next';
3621 // prevent button from submitting form http://stackoverflow.com/a/10836076/182183
3622 element.setAttribute( 'type', 'button' );
3623 // init as disabled
3624 this.disable();
3625
3626 element.setAttribute( 'aria-label', this.isPrevious ? 'Previous' : 'Next' );
3627
3628 // create arrow
3629 var svg = this.createSVG();
3630 element.appendChild( svg );
3631 // events
3632 this.parent.on( 'select', this.update.bind( this ) );
3633 this.on( 'pointerDown', this.parent.childUIPointerDown.bind( this.parent ) );
3634};
3635
3636PrevNextButton.prototype.activate = function() {
3637 this.bindStartEvent( this.element );
3638 this.element.addEventListener( 'click', this );
3639 // add to DOM
3640 this.parent.element.appendChild( this.element );
3641};
3642
3643PrevNextButton.prototype.deactivate = function() {
3644 // remove from DOM
3645 this.parent.element.removeChild( this.element );
3646 // click events
3647 this.unbindStartEvent( this.element );
3648 this.element.removeEventListener( 'click', this );
3649};
3650
3651PrevNextButton.prototype.createSVG = function() {
3652 var svg = document.createElementNS( svgURI, 'svg' );
3653 svg.setAttribute( 'class', 'flickity-button-icon' );
3654 svg.setAttribute( 'viewBox', '0 0 100 100' );
3655 var path = document.createElementNS( svgURI, 'path' );
3656 var pathMovements = getArrowMovements( this.parent.options.arrowShape );
3657 path.setAttribute( 'd', pathMovements );
3658 path.setAttribute( 'class', 'arrow' );
3659 // rotate arrow
3660 if ( !this.isLeft ) {
3661 path.setAttribute( 'transform', 'translate(100, 100) rotate(180) ' );
3662 }
3663 svg.appendChild( path );
3664 return svg;
3665};
3666
3667// get SVG path movmement
3668function getArrowMovements( shape ) {
3669 // use shape as movement if string
3670 if ( typeof shape == 'string' ) {
3671 return shape;
3672 }
3673 // create movement string
3674 return 'M ' + shape.x0 + ',50' +
3675 ' L ' + shape.x1 + ',' + ( shape.y1 + 50 ) +
3676 ' L ' + shape.x2 + ',' + ( shape.y2 + 50 ) +
3677 ' L ' + shape.x3 + ',50 ' +
3678 ' L ' + shape.x2 + ',' + ( 50 - shape.y2 ) +
3679 ' L ' + shape.x1 + ',' + ( 50 - shape.y1 ) +
3680 ' Z';
3681}
3682
3683PrevNextButton.prototype.handleEvent = utils.handleEvent;
3684
3685PrevNextButton.prototype.onclick = function() {
3686 if ( !this.isEnabled ) {
3687 return;
3688 }
3689 this.parent.uiChange();
3690 var method = this.isPrevious ? 'previous' : 'next';
3691 this.parent[ method ]();
3692};
3693
3694// ----- ----- //
3695
3696PrevNextButton.prototype.enable = function() {
3697 if ( this.isEnabled ) {
3698 return;
3699 }
3700 this.element.disabled = false;
3701 this.isEnabled = true;
3702};
3703
3704PrevNextButton.prototype.disable = function() {
3705 if ( !this.isEnabled ) {
3706 return;
3707 }
3708 this.element.disabled = true;
3709 this.isEnabled = false;
3710};
3711
3712PrevNextButton.prototype.update = function() {
3713 // index of first or last slide, if previous or next
3714 var slides = this.parent.slides;
3715 // enable is wrapAround and at least 2 slides
3716 if ( this.parent.options.wrapAround && slides.length > 1 ) {
3717 this.enable();
3718 return;
3719 }
3720 var lastIndex = slides.length ? slides.length - 1 : 0;
3721 var boundIndex = this.isPrevious ? 0 : lastIndex;
3722 var method = this.parent.selectedIndex == boundIndex ? 'disable' : 'enable';
3723 this[ method ]();
3724};
3725
3726PrevNextButton.prototype.destroy = function() {
3727 this.deactivate();
3728 this.allOff();
3729};
3730
3731// -------------------------- Flickity prototype -------------------------- //
3732
3733utils.extend( Flickity.defaults, {
3734 prevNextButtons: true,
3735 arrowShape: {
3736 x0: 10,
3737 x1: 60, y1: 50,
3738 x2: 70, y2: 40,
3739 x3: 30,
3740 },
3741} );
3742
3743Flickity.createMethods.push('_createPrevNextButtons');
3744var proto = Flickity.prototype;
3745
3746proto._createPrevNextButtons = function() {
3747 if ( !this.options.prevNextButtons ) {
3748 return;
3749 }
3750
3751 this.prevButton = new PrevNextButton( -1, this );
3752 this.nextButton = new PrevNextButton( 1, this );
3753
3754 this.on( 'activate', this.activatePrevNextButtons );
3755};
3756
3757proto.activatePrevNextButtons = function() {
3758 this.prevButton.activate();
3759 this.nextButton.activate();
3760 this.on( 'deactivate', this.deactivatePrevNextButtons );
3761};
3762
3763proto.deactivatePrevNextButtons = function() {
3764 this.prevButton.deactivate();
3765 this.nextButton.deactivate();
3766 this.off( 'deactivate