Instituto Pensar
  • Inicio
  • El instituto
  • Investigación
  • Incidencia
  • Formación
  • Publicaciones
  • Eventos

 

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
Se ha producido un error al procesar la plantilla.
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 &#x2710</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 
Se ha producido un error al procesar la plantilla.
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 &#x2710</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 
Se ha producido un error al procesar la plantilla.
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 &#x2710</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 
Se ha producido un error al procesar la plantilla.
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 &#x2710</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 
Se ha producido un error al procesar la plantilla.
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 &#x2710</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