Рой
Рой
Задача
В одном из захолустных городков в пустыне происходит череда странных событий. С ферм на окраине пропадают люди, полиция округа вводит комендантский час, а очевидцы сообщают о многочисленных отрядах военных без опознавательных знаков.
Место действия
Мохаве
Сюжет
С командованием Warface связывается аноним. Он утверждает, что в руках Blackwood оказались образцы неизвестной формы жизни с корабля марсианских колонистов, потерпевшего крушение в Долине Смерти.
Неподалеку от места катастрофы в кратчайшие сроки была возведена база Blackwood. Но существа, которые содержались в ее недрах, вырвались наружу и теперь представляют опасность для всего живого. Для устранения угрозы мирному населению в регион направляется один из лучших отрядов Warface — "Сигма-3".
Игровой процесс
Акт 1
Операция начинается на подступах к военной базе. Чтобы попасть на нее, вам предстоит отыскать три ноутбука с кодами доступа. Для активации ноутбуков потребуются ресурсы, ящики с которыми раскиданы по всей карте.
Акт 2
Когда вы окажетесь внутри комплекса, потребуется восстановить питание терминала доступа в центральную комнату. Здесь вас ждет неожиданный союзник, который поможет пережить спуск на нижние уровни.
Акт 3
Чтобы узнать, что управляет арахнидами, вам предстоит пробиваться к сердцу комплекса через мрачные сырые тоннели, сражаясь с ордами противников. Позаботьтесь о своем новом союзнике, и он позаботится о вас в финальной битве.
Акт 4
Приготовьтесь к встрече с самым большим противником, которого вы только видели!
Особенности спецоперации
Спецоперация «Рой» предлагает игрокам принципиально новый PvE-опыт благодаря уникальной комбинации геймплейных особенностей.
- Антураж. Окунитесь в атмосферу классических фильмов ужасов 80-х годов! Предрассветная мгла станет предвестником кошмара, с которым вам предстоит столкнуться наяву. Кромешный мрак, царящий в загадочных лабораториях и сырых тоннелях жуткого подземного бункера, заставит вздрагивать от каждого шороха и стрелять в темноту.
- Противники. Приготовьтесь к встрече с арахнидами — расой хищных паукообразных существ. Их угрожающие силуэты будут мерещиться вам за каждым углом! Это принципиально новый враг с уникальной тактикой боя, поджидающий свою жертву в самых неожиданных местах.
- Босс. В глубинах подземного комплекса затаилось то, что управляет арахнидами. И это нечто — колоссальных размеров, поэтому приготовьте самые большие пушки!
- Вооружение. Сражаться с арахнидами, защищенными твердыми хитиновыми покровами, лучше всего с помощью огнемета! Бесконечный боезапас позволит вам выжигать дотла целые орды кровожадных тварей.
- Союзник. Пережить спуск в самое логово поможет неожиданный союзник. Позаботьтесь о нем, а он прикроет вам спину в финальной битве.
- Новая система наград. Прохождение спецоперации за определенное время принесет коробку с самыми щедрыми наградами!
<div class="gallery js-flickity" style="width:90%; margin:auto;" data-flickity-options='{"wrapAround": true, "setGallerySize": false, "imagesLoaded": true}'> <div class="gallery-cell_art"> <div class="gallery-cell-content"> <div class="cell__img_art"><img src="//wf.cdn.gmru.net/wiki/images/3/30/Motel.jpg" /></div> </div></div> <div class="gallery-cell_art"> <div class="gallery-cell-content"> <div class="cell__img_art"><img src="//wf.cdn.gmru.net/wiki/images/d/da/OverPaint.jpg" /></div> </div></div> <div class="gallery-cell_art"> <div class="gallery-cell-content"> <div class="cell__img_art"><img src="//wf.cdn.gmru.net/wiki/images/3/3b/Overpaint-act2_floodzone2.jpg" /></div> </div></div> <div class="gallery-cell_art"> <div class="gallery-cell-content"> <div class="cell__img_art"><img src="//wf.cdn.gmru.net/wiki/images/4/46/Overpaint-act2flood.jpg" /></div> </div></div> <div class="gallery-cell_art"> <div class="gallery-cell-content"> <div class="cell__img_art"><img src="//wf.cdn.gmru.net/wiki/images/b/bf/Gas_station.jpg" /></div> </div></div> <div class="gallery-cell_art"> <div class="gallery-cell-content"> <div class="cell__img_art"><img src="//wf.cdn.gmru.net/wiki/images/7/7b/15_05_SWARM_2.jpg" /></div> </div></div> <div class="gallery-cell_art"> <div class="gallery-cell-content"> <div class="cell__img_art"><img src="//wf.cdn.gmru.net/wiki/images/2/2b/15_05_SWARM_3.jpg" /></div> </div></div> <div class="gallery-cell_art"> <div class="gallery-cell-content"> <div class="cell__img_art"><img src="//wf.cdn.gmru.net/wiki/images/0/09/15_05_SWARM_4.jpg" /></div> </div></div> <div class="gallery-cell_art"> <div class="gallery-cell-content"> <div class="cell__img_art"><img src="//wf.cdn.gmru.net/wiki/images/a/a9/15_05_SWARM_5.jpg" /></div> </div></div> </div>
Огнемёт «Гефест»
Давно мечтали взять в руки огнемёт и сжечь всё дотла? Теперь у вас появилась такая возможность!
Достигнув определённого прогресса миссии, игрок может подобрать огнемёт, который заметно облегчит уничтожение арахнидов и ульев.
Огнемёт имеет неограниченный боезапас и не нуждается в перезарядке, если не допускать перегрева. В случае перегрева же, придётся подождать некоторое время до полного охлаждения, прежде чем появится возможность стрелять снова.
<div class="wrapper clearfix" style="width: 240px;"> <div class="card"> <div class="front"> <img alt="Огнемёт «Гефест»" src="//wf.cdn.gmru.net/wiki/images/c/cf/Ft02.png" /></div> <div class="back"> <img alt="Огнемёт «Гефест»" src="//wf.cdn.gmru.net/wiki/images/c/cf/Ft02.png" /></div> </div> </div>
Ресурсы
На протяжении спецоперации вы сможете получать Ресурсы. Каждый открытый ящик даст вам 150 ресурсов, а за находку специального ноутбука вы получите целых 1000 ресурсов!
Используя заработанные ресурсы, вы сможете восстанавливать своё здоровье, броню и патроны в определённых точках. Кроме того накопленные ресурсы можно тратить на различные улучшения машины.
Боевая машина «Саманта»
Когда вы окажетесь внутри комплекса, вам потребуется восстановить питание терминала доступа в центральную комнату. Здесь вас ждет неожиданный союзник, который поможет пережить спуск на нижние уровни.
До конца миссии вам придётся оборонять машину, но это ещё не всё! На определённых точках будет возможность улучшать машину: вы сможете установить турель, улучшить броню или скорость автомобиля.
Арахниды
Арахниды — опасные и быстрые враги, которые предпочитают охотиться в темноте. Обычно они прячутся в земле, вентиляционных системах или в ульях, поджидая свою жертву. Существует несколько основных видов арахнидов.
Отряды жуков
<div class="gallery js-flickity" style="margin: auto; width: 80%;" data-flickity-options='{"wrapAround": true, "setGallerySize": false, "imagesLoaded": true}'> <div class="gallery-cell is-selected"> <h2 class="slider__caption">Жук-боец</h2> <div class="gallery-cell-content"> <div class="cell__img"><img src="//wf.cdn.gmru.net/wiki/images/a/af/BUG_Melee.png" /></div> <div class="cell__txt"><p>Противник ближнего боя. Наиболее многочисленный вид.</p></div> </div> </div> <div class="gallery-cell"> <h2 class="slider__caption">Жук-снайпер</h2> <div class="gallery-cell-content"> <div class="cell__img"><img src="//wf.cdn.gmru.net/wiki/images/4/48/BUG_Range.png" /></div> <div class="cell__txt"><p>Противник дальнего боя, всегда держится на расстоянии.</p></div> </div> </div> <div class="gallery-cell"> <h2 class="slider__caption">Хищный цветок</h2> <div class="gallery-cell-content"> <div class="cell__img"><img src="//wf.cdn.gmru.net/wiki/images/c/cf/BUG_BugTurret.png" /></div> <div class="cell__txt"><p>Противник дальнего боя, не способен передвигаться.</p></div> </div> </div> <div class="gallery-cell"> <h2 class="slider__caption">Жук-камикадзе</h2> <div class="gallery-cell-content"> <div class="cell__img"><img src="//wf.cdn.gmru.net/wiki/images/0/0a/BUG_Kamikaze.png" /></div> <div class="cell__txt"><p>Взрывается при контакте с целью или в случае смерти.</p></div> </div> </div> </div>
Элитные отряды жуков
<div class="gallery js-flickity" style="margin: auto; width: 80%;" data-flickity-options='{"wrapAround": true, "setGallerySize": false, "imagesLoaded": true}'> <div class="gallery-cell is-selected"> <h2 class="slider__caption">Элитный жук-боец</h2> <div class="gallery-cell-content"> <div class="cell__img"><img src="//wf.cdn.gmru.net/wiki/images/3/3a/BUG_HAZARD_melee.png" /></div> <div class="cell__txt"><p>Быстрее своего сородича. Имеет более прочный хитиновый покров.</p></div> </div> </div> <div class="gallery-cell"> <h2 class="slider__caption">Элитный жук-снайпер</h2> <div class="gallery-cell-content"> <div class="cell__img"><img src="//wf.cdn.gmru.net/wiki/images/8/83/BUG_HAZARD_range.png" /></div> <div class="cell__txt"><p>Быстрее своего сородича. Имеет более прочный хитиновый покров.</p></div> </div> </div> <div class="gallery-cell"> <h2 class="slider__caption">Элитный хищный цветок</h2> <div class="gallery-cell-content"> <div class="cell__img"><img src="//wf.cdn.gmru.net/wiki/images/5/5a/BUG_BugTurretHazard.png" /></div> <div class="cell__txt"><p>Производит больше кислоты, нанося урон по области.</p></div> </div> </div> </div>
Улей
Ульи укрывают в себе элитных арахнидов. Обычно они дожидаются своих жертв в земле.
Для быстрого уничтожения ульев вам не помешает вооружиться огнемётом!
Достижения
Скриншоты
Видеоролики
/* Wiki modified styles */ ul.swarm li {
margin: 8px 0;
} ul.swarm li b {
color: #cccccc;
} /* Tabs styles */ .wf-tabs {
margin: 0;
} .wf-tabs-menu {
position: relative;
} .wf-tabs-menu ul{
margin: 0;
} .wf-tabs-list {
display: -webkit-box; display: flex; -webkit-box-pack: justify; justify-content: space-between;
} .wf-tabs-list__item {
border-bottom: 2px solid #901819; font-size: 16px; font-family: Quantico; line-height: 20px; font-weight: 700; -webkit-box-pack: center; justify-content: center; -webkit-box-align: center; align-items: center; display: -webkit-box; display: flex; width: 50%; height: 50px; text-align: center; cursor: pointer; text-transform: uppercase; color: white;
} .wf-tabs-list__item.type-opened {
background: #901819;
} .text ul li.wf-tabs-list__item {
margin-bottom: 0;
} .text ul li.wf-tabs-list__item:before {
content: ; width: 0; display: none;
} .wf-tabs-menu__arrow, .tab-content {
display: none;
} .tab-content.type-opened {
display: block;
} @media (max-width: 768px) {
.wf-tabs-menu__arrow { display: block; position: absolute; right: 14px; top: 9px; -webkit-transform: rotate(90deg); transform: rotate(90deg); -webkit-transition: all 0.2s ease-out; transition: all 0.2s ease-out; } .wf-tabs-menu__arrow:before { display: block; } .wf-tabs-menu.type-opened .wf-tabs-menu__arrow { -webkit-transform: rotate(-90deg); transform: rotate(-90deg); } .wf-tabs-menu.type-opened .wf-tabs-list__item { display: flex; } .wf-tabs-list { flex-wrap: wrap; margin-bottom: 5px; } .wf-tabs-list__item { background: #c00; border: none; height: 32px; font-weight: normal; margin: 0; -webkit-box-ordinal-group: 3; order: 2; text-align: center; -webkit-transition: all 0.3s ease; transition: all 0.3s ease; width: 100%; } .wf-tabs-list__item:hover { background: #a80000; } .wf-tabs-list__item:not(.type-opened) { display: none; } .wf-tabs-list__item.type-opened { background: #c00; color: #fff; -webkit-box-ordinal-group: 2; order: 1; }
} .wf-wiki-2-0 ul.wf-tabs-list li:before {
display:none;
} .wf-wiki-2-0 ul.wf-tabs-list li {
padding-left: 0!important;
} /* Sliders */
- {
-webkit-box-sizing: border-box; box-sizing: border-box;
} .gallery {
background: #191921; height: 300px; margin-bottom: 25px!important;
}
.gallery-cell {
display: block; flex-direction: column; width: 450px; height: 220px; margin: 0; background: #191921; counter-increment: gallery-cell;
} .gallery-cell_art {
display: flex; flex-direction: column; width: 400px; height: 350px; margin: 0; background: #191921; counter-increment: gallery-cell;
} .gallery-cell h2 {
text-align: center; font-size: 17px; margin-bottom: 20px!important; text-transform: uppercase; font-weight: 800; color: #cccccc;
} .gallery-cell-content {
display: flex; flex-direction: column; align-items: center; height: 100%;
} .gallery-cell .cell__img {
display: flex; height: 70%; align-items: center;
} .gallery-cell_art .cell__img_art {
height: 100%; align-items: center; padding-top: 20px;
} .gallery-cell .cell__txt {
display: flex; height: 30%; align-items: flex-end; text-align: center;
} .gallery-cell.is-selected img {
-webkit-transform: scale(1.2); transform: scale(1.2); -webkit-transition: all .5s ease-out; -o-transition: all .5s ease-out; transition: all .5s ease-out;
} .gallery-cell_art.is-selected img {
-webkit-transform: scale(1.3); transform: scale(1.3); -webkit-transition: all .5s ease-out; -o-transition: all .5s ease-out; transition: all .5s ease-out;
} .gallery-cell img {
width: 200px; margin: 20px 0; -webkit-transform: scale(0.8); transform: scale(0.8); -webkit-transition: all .5s ease-out; -o-transition: all .5s ease-out; transition: all .5s ease-out;
} .gallery-cell_art img {
width: 350px; padding: 20px 0 0 0; margin: 20px 0; -webkit-transform: scale(0.8); transform: scale(0.8); -webkit-transition: all .5s ease-out; -o-transition: all .5s ease-out; transition: all .5s ease-out;
} .flickity-enabled {
position: relative;
}
.flickity-enabled:focus {
outline: none;
}
.flickity-viewport {
overflow: hidden; position: relative; height: 100%!important;
}
.flickity-slider {
position: absolute; width: 100%; height: 100%; background-color: #191921;
}
/* draggable */
.flickity-enabled.is-draggable {
-webkit-tap-highlight-color: transparent; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none;
}
.flickity-enabled.is-draggable .flickity-viewport {
cursor: move; cursor: -webkit-grab; cursor: grab;
}
.flickity-enabled.is-draggable .flickity-viewport.is-pointer-down {
cursor: -webkit-grabbing; cursor: grabbing;
}
/* ---- flickity-button ---- */
.flickity-button {
position: absolute; background: hsla(0, 0%, 100%, 0.75); border: none; color: #333;
}
.flickity-button:hover {
background: white; cursor: pointer;
}
.flickity-button:focus {
outline: none; box-shadow: 0 0 0 5px #19F;
}
.flickity-button:active {
opacity: 0.6;
}
.flickity-button:disabled {
opacity: 0.3; cursor: auto; /* prevent disabled button from capturing pointer up event. #716 */ pointer-events: none;
}
.flickity-button-icon {
fill: currentColor;
}
/* ---- previous/next buttons ---- */
.flickity-prev-next-button {
top: 50%; width: 44px; height: 44px; border-radius: 50%; /* vertically center */ transform: translateY(-50%);
}
.flickity-prev-next-button.previous { left: -60px; } .flickity-prev-next-button.next { right: -60px; } /* right to left */ .flickity-rtl .flickity-prev-next-button.previous {
left: auto; right: 10px;
} .flickity-rtl .flickity-prev-next-button.next {
right: auto; left: 10px;
}
.flickity-prev-next-button .flickity-button-icon {
position: absolute; left: 20%; top: 20%; width: 60%; height: 60%;
}
/* ---- page dots ---- */
.flickity-page-dots {
position: absolute; width: 100%; padding: 0; margin: 0; list-style: none; text-align: center; line-height: 1px;
}
.flickity-rtl .flickity-page-dots {
direction: rtl;
}
.flickity-page-dots .dot {
display: inline-block; width: 10px; height: 10px; margin: 0 8px; background: #fff; border-radius: 50%; opacity: 0.25; cursor: pointer;
}
.flickity-page-dots .dot.is-selected {
opacity: 1;
} /* Cards */ .wrapper { position: relative; width: 100%; height: 100%; left: 0; top: 40px; display: flex; flex-wrap: wrap; justify-content: flex-start; align-items: center; } .card { width: 176px; height: 105px; position: relative; perspective: 1000px; transform-style: preserve-3d; z-index: 5; transition: 0.6s; } .front, .back { position: absolute; width: 100%; height: 100%; left: 0; top: 0; display: flex; justify-content: center; align-items: center; transition: 0.6s; backface-visibility: hidden; } .front img { max-width: 100%; min-width: 100%; height: auto; } .back { -webkit-transform: rotateY(0.5turn); transform: rotateY(0.5turn); } .card:hover { z-index: 10; } .card:hover .front { -webkit-transform: rotateY(0.99turn) scale(1.4); transform: rotateY(0.99turn) scale(1.4); z-index: 11; } .card:hover .back { -webkit-transform: rotateY(1.49turn) scale(1.4); transform: rotateY(1.49turn) scale(1.4); } .clearfix:after { content: ""; display: block; height: 0; clear: both; visibility: hidden; } /* Warface news styles */ .column_block1 {
width: 45%; text-align: center; overflow: hidden; margin: 50px 10px 10px 0px;
} .column_block1_right {
width: 45%; text-align: center; overflow: hidden; margin: 50px 10px 10px 10px;
} .column_block2 {
width: 50%; margin: auto;
} .wf-wiki-2-0 .column_block2 h3 {
display: flex; font-family: Plumb; font-size: 17px; text-transform: uppercase; font-weight: 800; color: #cccccc; justify-content: center;
<script>/*! * Flickity PACKAGED v2.2.2 * Touch, responsive, flickable carousels * * Licensed GPLv3 for open source use * or Flickity Commercial License for commercial use * * https://flickity.metafizzy.co * Copyright 2015-2021 Metafizzy */ /** * Bridget makes jQuery widgets * v2.0.1 * MIT license */ /* jshint browser: true, strict: true, undef: true, unused: true */ ( function( window, factory ) { // universal module definition /*jshint strict: false */ /* globals define, module, require */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'jquery-bridget/jquery-bridget',[ 'jquery' ], function( jQuery ) { return factory( window, jQuery ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('jquery') ); } else { // browser global window.jQueryBridget = factory( window, window.jQuery ); } }( window, function factory( window, jQuery ) { 'use strict'; // ----- utils ----- // var arraySlice = Array.prototype.slice; // helper function for logging errors // $.error breaks jQuery chaining var console = window.console; var logError = typeof console == 'undefined' ? function() {} : function( message ) { console.error( message ); }; // ----- jQueryBridget ----- // function jQueryBridget( namespace, PluginClass, $ ) { $ = $ || jQuery || window.jQuery; if ( !$ ) { return; } // add option method -> $().plugin('option', {...}) if ( !PluginClass.prototype.option ) { // option setter PluginClass.prototype.option = function( opts ) { // bail out if not an object if ( !$.isPlainObject( opts ) ){ return; } this.options = $.extend( true, this.options, opts ); }; } // make jQuery plugin $.fn[ namespace ] = function( arg0 /*, arg1 */ ) { if ( typeof arg0 == 'string' ) { // method call $().plugin( 'methodName', { options } ) // shift arguments by 1 var args = arraySlice.call( arguments, 1 ); return methodCall( this, arg0, args ); } // just $().plugin({ options }) plainCall( this, arg0 ); return this; }; // $().plugin('methodName') function methodCall( $elems, methodName, args ) { var returnValue; var pluginMethodStr = '$().' + namespace + '("' + methodName + '")'; $elems.each( function( i, elem ) { // get instance var instance = $.data( elem, namespace ); if ( !instance ) { logError( namespace + ' not initialized. Cannot call methods, i.e. ' + pluginMethodStr ); return; } var method = instance[ methodName ]; if ( !method || methodName.charAt(0) == '_' ) { logError( pluginMethodStr + ' is not a valid method' ); return; } // apply method, get return value var value = method.apply( instance, args ); // set return value if value is returned, use only first value returnValue = returnValue === undefined ? value : returnValue; }); return returnValue !== undefined ? returnValue : $elems; } function plainCall( $elems, options ) { $elems.each( function( i, elem ) { var instance = $.data( elem, namespace ); if ( instance ) { // set options & init instance.option( options ); instance._init(); } else { // initialize new instance instance = new PluginClass( elem, options ); $.data( elem, namespace, instance ); } }); } updateJQuery( $ ); } // ----- updateJQuery ----- // // set $.bridget for v1 backwards compatibility function updateJQuery( $ ) { if ( !$ || ( $ && $.bridget ) ) { return; } $.bridget = jQueryBridget; } updateJQuery( jQuery || window.jQuery ); // ----- ----- // return jQueryBridget; })); /** * EvEmitter v1.1.0 * Lil' event emitter * MIT License */ /* jshint unused: true, undef: true, strict: true */ ( function( global, factory ) { // universal module definition /* jshint strict: false */ /* globals define, module, window */ if ( typeof define == 'function' && define.amd ) { // AMD - RequireJS define( 'ev-emitter/ev-emitter',factory ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS - Browserify, Webpack module.exports = factory(); } else { // Browser globals global.EvEmitter = factory(); } }( typeof window != 'undefined' ? window : this, function() { function EvEmitter() {} var proto = EvEmitter.prototype; proto.on = function( eventName, listener ) { if ( !eventName || !listener ) { return; } // set events hash var events = this._events = this._events || {}; // set listeners array var listeners = events[ eventName ] = events[ eventName ] || []; // only add once if ( listeners.indexOf( listener ) == -1 ) { listeners.push( listener ); } return this; }; proto.once = function( eventName, listener ) { if ( !eventName || !listener ) { return; } // add event this.on( eventName, listener ); // set once flag // set onceEvents hash var onceEvents = this._onceEvents = this._onceEvents || {}; // set onceListeners object var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {}; // set flag onceListeners[ listener ] = true; return this; }; proto.off = function( eventName, listener ) { var listeners = this._events && this._events[ eventName ]; if ( !listeners || !listeners.length ) { return; } var index = listeners.indexOf( listener ); if ( index != -1 ) { listeners.splice( index, 1 ); } return this; }; proto.emitEvent = function( eventName, args ) { var listeners = this._events && this._events[ eventName ]; if ( !listeners || !listeners.length ) { return; } // copy over to avoid interference if .off() in listener listeners = listeners.slice(0); args = args || []; // once stuff var onceListeners = this._onceEvents && this._onceEvents[ eventName ]; for ( var i=0; i < listeners.length; i++ ) { var listener = listeners[i] var isOnce = onceListeners && onceListeners[ listener ]; if ( isOnce ) { // remove listener // remove before trigger to prevent recursion this.off( eventName, listener ); // unset once flag delete onceListeners[ listener ]; } // trigger listener listener.apply( this, args ); } return this; }; proto.allOff = function() { delete this._events; delete this._onceEvents; }; return EvEmitter; })); /*! * getSize v2.0.3 * measure size of elements * MIT license */ /* jshint browser: true, strict: true, undef: true, unused: true */ /* globals console: false */ ( function( window, factory ) { /* jshint strict: false */ /* globals define, module */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'get-size/get-size',factory ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory(); } else { // browser global window.getSize = factory(); } })( window, function factory() { 'use strict'; // -------------------------- helpers -------------------------- // // get a number from a string, not a percentage function getStyleSize( value ) { var num = parseFloat( value ); // not a percent like '100%', and a number var isValid = value.indexOf('%') == -1 && !isNaN( num ); return isValid && num; } function noop() {} var logError = typeof console == 'undefined' ? noop : function( message ) { console.error( message ); }; // -------------------------- measurements -------------------------- // var measurements = [ 'paddingLeft', 'paddingRight', 'paddingTop', 'paddingBottom', 'marginLeft', 'marginRight', 'marginTop', 'marginBottom', 'borderLeftWidth', 'borderRightWidth', 'borderTopWidth', 'borderBottomWidth' ]; var measurementsLength = measurements.length; function getZeroSize() { var size = { width: 0, height: 0, innerWidth: 0, innerHeight: 0, outerWidth: 0, outerHeight: 0 }; for ( var i=0; i < measurementsLength; i++ ) { var measurement = measurements[i]; size[ measurement ] = 0; } return size; } // -------------------------- getStyle -------------------------- // /** * getStyle, get style of element, check for Firefox bug * https://bugzilla.mozilla.org/show_bug.cgi?id=548397 */ function getStyle( elem ) { var style = getComputedStyle( elem ); if ( !style ) { logError( 'Style returned ' + style + '. Are you running this code in a hidden iframe on Firefox? ' + 'See https://bit.ly/getsizebug1' ); } return style; } // -------------------------- setup -------------------------- // var isSetup = false; var isBoxSizeOuter; /** * setup * check isBoxSizerOuter * do on first getSize() rather than on page load for Firefox bug */ function setup() { // setup once if ( isSetup ) { return; } isSetup = true; // -------------------------- box sizing -------------------------- // /** * Chrome & Safari measure the outer-width on style.width on border-box elems * IE11 & Firefox<29 measures the inner-width */ var div = document.createElement('div'); div.style.width = '200px'; div.style.padding = '1px 2px 3px 4px'; div.style.borderStyle = 'solid'; div.style.borderWidth = '1px 2px 3px 4px'; div.style.boxSizing = 'border-box'; var body = document.body || document.documentElement; body.appendChild( div ); var style = getStyle( div ); // round value for browser zoom. desandro/masonry#928 isBoxSizeOuter = Math.round( getStyleSize( style.width ) ) == 200; getSize.isBoxSizeOuter = isBoxSizeOuter; body.removeChild( div ); } // -------------------------- getSize -------------------------- // function getSize( elem ) { setup(); // use querySeletor if elem is string if ( typeof elem == 'string' ) { elem = document.querySelector( elem ); } // do not proceed on non-objects if ( !elem || typeof elem != 'object' || !elem.nodeType ) { return; } var style = getStyle( elem ); // if hidden, everything is 0 if ( style.display == 'none' ) { return getZeroSize(); } var size = {}; size.width = elem.offsetWidth; size.height = elem.offsetHeight; var isBorderBox = size.isBorderBox = style.boxSizing == 'border-box'; // get all measurements for ( var i=0; i < measurementsLength; i++ ) { var measurement = measurements[i]; var value = style[ measurement ]; var num = parseFloat( value ); // any 'auto', 'medium' value will be 0 size[ measurement ] = !isNaN( num ) ? num : 0; } var paddingWidth = size.paddingLeft + size.paddingRight; var paddingHeight = size.paddingTop + size.paddingBottom; var marginWidth = size.marginLeft + size.marginRight; var marginHeight = size.marginTop + size.marginBottom; var borderWidth = size.borderLeftWidth + size.borderRightWidth; var borderHeight = size.borderTopWidth + size.borderBottomWidth; var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter; // overwrite width and height if we can get it from style var styleWidth = getStyleSize( style.width ); if ( styleWidth !== false ) { size.width = styleWidth + // add padding and border unless it's already including it ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth ); } var styleHeight = getStyleSize( style.height ); if ( styleHeight !== false ) { size.height = styleHeight + // add padding and border unless it's already including it ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight ); } size.innerWidth = size.width - ( paddingWidth + borderWidth ); size.innerHeight = size.height - ( paddingHeight + borderHeight ); size.outerWidth = size.width + marginWidth; size.outerHeight = size.height + marginHeight; return size; } return getSize; }); /** * matchesSelector v2.0.2 * matchesSelector( element, '.selector' ) * MIT license */ /*jshint browser: true, strict: true, undef: true, unused: true */ ( function( window, factory ) { /*global define: false, module: false */ 'use strict'; // universal module definition if ( typeof define == 'function' && define.amd ) { // AMD define( 'desandro-matches-selector/matches-selector',factory ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory(); } else { // browser global window.matchesSelector = factory(); } }( window, function factory() { 'use strict'; var matchesMethod = ( function() { var ElemProto = window.Element.prototype; // check for the standard method name first if ( ElemProto.matches ) { return 'matches'; } // check un-prefixed if ( ElemProto.matchesSelector ) { return 'matchesSelector'; } // check vendor prefixes var prefixes = [ 'webkit', 'moz', 'ms', 'o' ]; for ( var i=0; i < prefixes.length; i++ ) { var prefix = prefixes[i]; var method = prefix + 'MatchesSelector'; if ( ElemProto[ method ] ) { return method; } } })(); return function matchesSelector( elem, selector ) { return elem[ matchesMethod ]( selector ); }; })); /** * Fizzy UI utils v2.0.7 * MIT license */ /*jshint browser: true, undef: true, unused: true, strict: true */ ( function( window, factory ) { // universal module definition /*jshint strict: false */ /*globals define, module, require */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'fizzy-ui-utils/utils',[ 'desandro-matches-selector/matches-selector' ], function( matchesSelector ) { return factory( window, matchesSelector ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('desandro-matches-selector') ); } else { // browser global window.fizzyUIUtils = factory( window, window.matchesSelector ); } }( window, function factory( window, matchesSelector ) { var utils = {}; // ----- extend ----- // // extends objects utils.extend = function( a, b ) { for ( var prop in b ) { a[ prop ] = b[ prop ]; } return a; }; // ----- modulo ----- // utils.modulo = function( num, div ) { return ( ( num % div ) + div ) % div; }; // ----- makeArray ----- // var arraySlice = Array.prototype.slice; // turn element or nodeList into an array utils.makeArray = function( obj ) { if ( Array.isArray( obj ) ) { // use object if already an array return obj; } // return empty array if undefined or null. #6 if ( obj === null || obj === undefined ) { return []; } var isArrayLike = typeof obj == 'object' && typeof obj.length == 'number'; if ( isArrayLike ) { // convert nodeList to array return arraySlice.call( obj ); } // array of single index return [ obj ]; }; // ----- removeFrom ----- // utils.removeFrom = function( ary, obj ) { var index = ary.indexOf( obj ); if ( index != -1 ) { ary.splice( index, 1 ); } }; // ----- getParent ----- // utils.getParent = function( elem, selector ) { while ( elem.parentNode && elem != document.body ) { elem = elem.parentNode; if ( matchesSelector( elem, selector ) ) { return elem; } } }; // ----- getQueryElement ----- // // use element as selector string utils.getQueryElement = function( elem ) { if ( typeof elem == 'string' ) { return document.querySelector( elem ); } return elem; }; // ----- handleEvent ----- // // enable .ontype to trigger from .addEventListener( elem, 'type' ) utils.handleEvent = function( event ) { var method = 'on' + event.type; if ( this[ method ] ) { this[ method ]( event ); } }; // ----- filterFindElements ----- // utils.filterFindElements = function( elems, selector ) { // make array of elems elems = utils.makeArray( elems ); var ffElems = []; elems.forEach( function( elem ) { // check that elem is an actual element if ( !( elem instanceof HTMLElement ) ) { return; } // add elem if no selector if ( !selector ) { ffElems.push( elem ); return; } // filter & find items if we have a selector // filter if ( matchesSelector( elem, selector ) ) { ffElems.push( elem ); } // find children var childElems = elem.querySelectorAll( selector ); // concat childElems to filterFound array for ( var i=0; i < childElems.length; i++ ) { ffElems.push( childElems[i] ); } }); return ffElems; }; // ----- debounceMethod ----- // utils.debounceMethod = function( _class, methodName, threshold ) { threshold = threshold || 100; // original method var method = _class.prototype[ methodName ]; var timeoutName = methodName + 'Timeout'; _class.prototype[ methodName ] = function() { var timeout = this[ timeoutName ]; clearTimeout( timeout ); var args = arguments; var _this = this; this[ timeoutName ] = setTimeout( function() { method.apply( _this, args ); delete _this[ timeoutName ]; }, threshold ); }; }; // ----- docReady ----- // utils.docReady = function( callback ) { var readyState = document.readyState; if ( readyState == 'complete' || readyState == 'interactive' ) { // do async to allow for other scripts to run. metafizzy/flickity#441 setTimeout( callback ); } else { document.addEventListener( 'DOMContentLoaded', callback ); } }; // ----- htmlInit ----- // // http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/ utils.toDashed = function( str ) { return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) { return $1 + '-' + $2; }).toLowerCase(); }; var console = window.console; /** * allow user to initialize classes via [data-namespace] or .js-namespace class * htmlInit( Widget, 'widgetName' ) * options are parsed from data-namespace-options */ utils.htmlInit = function( WidgetClass, namespace ) { utils.docReady( function() { var dashedNamespace = utils.toDashed( namespace ); var dataAttr = 'data-' + dashedNamespace; var dataAttrElems = document.querySelectorAll( '[' + dataAttr + ']' ); var jsDashElems = document.querySelectorAll( '.js-' + dashedNamespace ); var elems = utils.makeArray( dataAttrElems ) .concat( utils.makeArray( jsDashElems ) ); var dataOptionsAttr = dataAttr + '-options'; var jQuery = window.jQuery; elems.forEach( function( elem ) { var attr = elem.getAttribute( dataAttr ) || elem.getAttribute( dataOptionsAttr ); var options; try { options = attr && JSON.parse( attr ); } catch ( error ) { // log error, do not initialize if ( console ) { console.error( 'Error parsing ' + dataAttr + ' on ' + elem.className + ': ' + error ); } return; } // initialize var instance = new WidgetClass( elem, options ); // make available via $().data('namespace') if ( jQuery ) { jQuery.data( elem, namespace, instance ); } }); }); }; // ----- ----- // return utils; })); // Flickity.Cell ( function( window, factory ) { // universal module definition if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity/js/cell',[ 'get-size/get-size', ], function( getSize ) { return factory( window, getSize ); } ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('get-size') ); } else { // browser global window.Flickity = window.Flickity || {}; window.Flickity.Cell = factory( window, window.getSize ); } }( window, function factory( window, getSize ) { function Cell( elem, parent ) { this.element = elem; this.parent = parent; this.create(); } var proto = Cell.prototype; proto.create = function() { this.element.style.position = 'absolute'; this.element.setAttribute( 'aria-hidden', 'true' ); this.x = 0; this.shift = 0; }; proto.destroy = function() { // reset style this.unselect(); this.element.style.position = ''; var side = this.parent.originSide; this.element.style[ side ] = ''; this.element.removeAttribute('aria-hidden'); }; proto.getSize = function() { this.size = getSize( this.element ); }; proto.setPosition = function( x ) { this.x = x; this.updateTarget(); this.renderPosition( x ); }; // setDefaultTarget v1 method, backwards compatibility, remove in v3 proto.updateTarget = proto.setDefaultTarget = function() { var marginProperty = this.parent.originSide == 'left' ? 'marginLeft' : 'marginRight'; this.target = this.x + this.size[ marginProperty ] + this.size.width * this.parent.cellAlign; }; proto.renderPosition = function( x ) { // render position of cell with in slider var side = this.parent.originSide; this.element.style[ side ] = this.parent.getPositionValue( x ); }; proto.select = function() { this.element.classList.add('is-selected'); this.element.removeAttribute('aria-hidden'); }; proto.unselect = function() { this.element.classList.remove('is-selected'); this.element.setAttribute( 'aria-hidden', 'true' ); }; /** * @param {Integer} shift - 0, 1, or -1 */ proto.wrapShift = function( shift ) { this.shift = shift; this.renderPosition( this.x + this.parent.slideableWidth * shift ); }; proto.remove = function() { this.element.parentNode.removeChild( this.element ); }; return Cell; } ) ); // slide ( function( window, factory ) { // universal module definition if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity/js/slide',factory ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory(); } else { // browser global window.Flickity = window.Flickity || {}; window.Flickity.Slide = factory(); } }( window, function factory() { 'use strict'; function Slide( parent ) { this.parent = parent; this.isOriginLeft = parent.originSide == 'left'; this.cells = []; this.outerWidth = 0; this.height = 0; } var proto = Slide.prototype; proto.addCell = function( cell ) { this.cells.push( cell ); this.outerWidth += cell.size.outerWidth; this.height = Math.max( cell.size.outerHeight, this.height ); // first cell stuff if ( this.cells.length == 1 ) { this.x = cell.x; // x comes from first cell var beginMargin = this.isOriginLeft ? 'marginLeft' : 'marginRight'; this.firstMargin = cell.size[ beginMargin ]; } }; proto.updateTarget = function() { var endMargin = this.isOriginLeft ? 'marginRight' : 'marginLeft'; var lastCell = this.getLastCell(); var lastMargin = lastCell ? lastCell.size[ endMargin ] : 0; var slideWidth = this.outerWidth - ( this.firstMargin + lastMargin ); this.target = this.x + this.firstMargin + slideWidth * this.parent.cellAlign; }; proto.getLastCell = function() { return this.cells[ this.cells.length - 1 ]; }; proto.select = function() { this.cells.forEach( function( cell ) { cell.select(); } ); }; proto.unselect = function() { this.cells.forEach( function( cell ) { cell.unselect(); } ); }; proto.getCellElements = function() { return this.cells.map( function( cell ) { return cell.element; } ); }; return Slide; } ) ); // animate ( function( window, factory ) { // universal module definition if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity/js/animate',[ 'fizzy-ui-utils/utils', ], function( utils ) { return factory( window, utils ); } ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('fizzy-ui-utils') ); } else { // browser global window.Flickity = window.Flickity || {}; window.Flickity.animatePrototype = factory( window, window.fizzyUIUtils ); } }( window, function factory( window, utils ) { // -------------------------- animate -------------------------- // var proto = {}; proto.startAnimation = function() { if ( this.isAnimating ) { return; } this.isAnimating = true; this.restingFrames = 0; this.animate(); }; proto.animate = function() { this.applyDragForce(); this.applySelectedAttraction(); var previousX = this.x; this.integratePhysics(); this.positionSlider(); this.settle( previousX ); // animate next frame if ( this.isAnimating ) { var _this = this; requestAnimationFrame( function animateFrame() { _this.animate(); } ); } }; proto.positionSlider = function() { var x = this.x; // wrap position around if ( this.options.wrapAround && this.cells.length > 1 ) { x = utils.modulo( x, this.slideableWidth ); x -= this.slideableWidth; this.shiftWrapCells( x ); } this.setTranslateX( x, this.isAnimating ); this.dispatchScrollEvent(); }; proto.setTranslateX = function( x, is3d ) { x += this.cursorPosition; // reverse if right-to-left and using transform x = this.options.rightToLeft ? -x : x; var translateX = this.getPositionValue( x ); // use 3D transforms for hardware acceleration on iOS // but use 2D when settled, for better font-rendering this.slider.style.transform = is3d ? 'translate3d(' + translateX + ',0,0)' : 'translateX(' + translateX + ')'; }; proto.dispatchScrollEvent = function() { var firstSlide = this.slides[0]; if ( !firstSlide ) { return; } var positionX = -this.x - firstSlide.target; var progress = positionX / this.slidesWidth; this.dispatchEvent( 'scroll', null, [ progress, positionX ] ); }; proto.positionSliderAtSelected = function() { if ( !this.cells.length ) { return; } this.x = -this.selectedSlide.target; this.velocity = 0; // stop wobble this.positionSlider(); }; proto.getPositionValue = function( position ) { if ( this.options.percentPosition ) { // percent position, round to 2 digits, like 12.34% return ( Math.round( ( position / this.size.innerWidth ) * 10000 ) * 0.01 ) + '%'; } else { // pixel positioning return Math.round( position ) + 'px'; } }; proto.settle = function( previousX ) { // keep track of frames where x hasn't moved var isResting = !this.isPointerDown && Math.round( this.x * 100 ) == Math.round( previousX * 100 ); if ( isResting ) { this.restingFrames++; } // stop animating if resting for 3 or more frames if ( this.restingFrames > 2 ) { this.isAnimating = false; delete this.isFreeScrolling; // render position with translateX when settled this.positionSlider(); this.dispatchEvent( 'settle', null, [ this.selectedIndex ] ); } }; proto.shiftWrapCells = function( x ) { // shift before cells var beforeGap = this.cursorPosition + x; this._shiftCells( this.beforeShiftCells, beforeGap, -1 ); // shift after cells var afterGap = this.size.innerWidth - ( x + this.slideableWidth + this.cursorPosition ); this._shiftCells( this.afterShiftCells, afterGap, 1 ); }; proto._shiftCells = function( cells, gap, shift ) { for ( var i = 0; i < cells.length; i++ ) { var cell = cells[i]; var cellShift = gap > 0 ? shift : 0; cell.wrapShift( cellShift ); gap -= cell.size.outerWidth; } }; proto._unshiftCells = function( cells ) { if ( !cells || !cells.length ) { return; } for ( var i = 0; i < cells.length; i++ ) { cells[i].wrapShift( 0 ); } }; // -------------------------- physics -------------------------- // proto.integratePhysics = function() { this.x += this.velocity; this.velocity *= this.getFrictionFactor(); }; proto.applyForce = function( force ) { this.velocity += force; }; proto.getFrictionFactor = function() { return 1 - this.options[ this.isFreeScrolling ? 'freeScrollFriction' : 'friction' ]; }; proto.getRestingPosition = function() { // my thanks to Steven Wittens, who simplified this math greatly return this.x + this.velocity / ( 1 - this.getFrictionFactor() ); }; proto.applyDragForce = function() { if ( !this.isDraggable || !this.isPointerDown ) { return; } // change the position to drag position by applying force var dragVelocity = this.dragX - this.x; var dragForce = dragVelocity - this.velocity; this.applyForce( dragForce ); }; proto.applySelectedAttraction = function() { // do not attract if pointer down or no slides var dragDown = this.isDraggable && this.isPointerDown; if ( dragDown || this.isFreeScrolling || !this.slides.length ) { return; } var distance = this.selectedSlide.target * -1 - this.x; var force = distance * this.options.selectedAttraction; this.applyForce( force ); }; return proto; } ) ); // Flickity main /* eslint-disable max-params */ ( function( window, factory ) { // universal module definition if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity/js/flickity',[ 'ev-emitter/ev-emitter', 'get-size/get-size', 'fizzy-ui-utils/utils', './cell', './slide', './animate', ], function( EvEmitter, getSize, utils, Cell, Slide, animatePrototype ) { return factory( window, EvEmitter, getSize, utils, Cell, Slide, animatePrototype ); } ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('ev-emitter'), require('get-size'), require('fizzy-ui-utils'), require('./cell'), require('./slide'), require('./animate') ); } else { // browser global var _Flickity = window.Flickity; window.Flickity = factory( window, window.EvEmitter, window.getSize, window.fizzyUIUtils, _Flickity.Cell, _Flickity.Slide, _Flickity.animatePrototype ); } }( window, function factory( window, EvEmitter, getSize, utils, Cell, Slide, animatePrototype ) { /* eslint-enable max-params */ // vars var jQuery = window.jQuery; var getComputedStyle = window.getComputedStyle; var console = window.console; function moveElements( elems, toElem ) { elems = utils.makeArray( elems ); while ( elems.length ) { toElem.appendChild( elems.shift() ); } } // -------------------------- Flickity -------------------------- // // globally unique identifiers var GUID = 0; // internal store of all Flickity intances var instances = {}; function Flickity( element, options ) { var queryElement = utils.getQueryElement( element ); if ( !queryElement ) { if ( console ) { console.error( 'Bad element for Flickity: ' + ( queryElement || element ) ); } return; } this.element = queryElement; // do not initialize twice on same element if ( this.element.flickityGUID ) { var instance = instances[ this.element.flickityGUID ]; if ( instance ) instance.option( options ); return instance; } // add jQuery if ( jQuery ) { this.$element = jQuery( this.element ); } // options this.options = utils.extend( {}, this.constructor.defaults ); this.option( options ); // kick things off this._create(); } Flickity.defaults = { accessibility: true, // adaptiveHeight: false, cellAlign: 'center', // cellSelector: undefined, // contain: false, freeScrollFriction: 0.075, // friction when free-scrolling friction: 0.28, // friction when selecting namespaceJQueryEvents: true, // initialIndex: 0, percentPosition: true, resize: true, selectedAttraction: 0.025, setGallerySize: true, // watchCSS: false, // wrapAround: false }; // hash of methods triggered on _create() Flickity.createMethods = []; var proto = Flickity.prototype; // inherit EventEmitter utils.extend( proto, EvEmitter.prototype ); proto._create = function() { // add id for Flickity.data var id = this.guid = ++GUID; this.element.flickityGUID = id; // expando instances[ id ] = this; // associate via id // initial properties this.selectedIndex = 0; // how many frames slider has been in same position this.restingFrames = 0; // initial physics properties this.x = 0; this.velocity = 0; this.originSide = this.options.rightToLeft ? 'right' : 'left'; // create viewport & slider this.viewport = document.createElement('div'); this.viewport.className = 'flickity-viewport'; this._createSlider(); if ( this.options.resize || this.options.watchCSS ) { window.addEventListener( 'resize', this ); } // add listeners from on option for ( var eventName in this.options.on ) { var listener = this.options.on[ eventName ]; this.on( eventName, listener ); } Flickity.createMethods.forEach( function( method ) { this[ method ](); }, this ); if ( this.options.watchCSS ) { this.watchCSS(); } else { this.activate(); } }; /** * set options * @param {Object} opts - options to extend */ proto.option = function( opts ) { utils.extend( this.options, opts ); }; proto.activate = function() { if ( this.isActive ) { return; } this.isActive = true; this.element.classList.add('flickity-enabled'); if ( this.options.rightToLeft ) { this.element.classList.add('flickity-rtl'); } this.getSize(); // move initial cell elements so they can be loaded as cells var cellElems = this._filterFindCellElements( this.element.children ); moveElements( cellElems, this.slider ); this.viewport.appendChild( this.slider ); this.element.appendChild( this.viewport ); // get cells from children this.reloadCells(); if ( this.options.accessibility ) { // allow element to focusable this.element.tabIndex = 0; // listen for key presses this.element.addEventListener( 'keydown', this ); } this.emitEvent('activate'); this.selectInitialIndex(); // flag for initial activation, for using initialIndex this.isInitActivated = true; // ready event. #493 this.dispatchEvent('ready'); }; // slider positions the cells proto._createSlider = function() { // slider element does all the positioning var slider = document.createElement('div'); slider.className = 'flickity-slider'; slider.style[ this.originSide ] = 0; this.slider = slider; }; proto._filterFindCellElements = function( elems ) { return utils.filterFindElements( elems, this.options.cellSelector ); }; // goes through all children proto.reloadCells = function() { // collection of item elements this.cells = this._makeCells( this.slider.children ); this.positionCells(); this._getWrapShiftCells(); this.setGallerySize(); }; /** * turn elements into Flickity.Cells * @param {[Array, NodeList, HTMLElement]} elems - elements to make into cells * @returns {Array} items - collection of new Flickity Cells */ proto._makeCells = function( elems ) { var cellElems = this._filterFindCellElements( elems ); // create new Flickity for collection var cells = cellElems.map( function( cellElem ) { return new Cell( cellElem, this ); }, this ); return cells; }; proto.getLastCell = function() { return this.cells[ this.cells.length - 1 ]; }; proto.getLastSlide = function() { return this.slides[ this.slides.length - 1 ]; }; // positions all cells proto.positionCells = function() { // size all cells this._sizeCells( this.cells ); // position all cells this._positionCells( 0 ); }; /** * position certain cells * @param {Integer} index - which cell to start with */ proto._positionCells = function( index ) { index = index || 0; // also measure maxCellHeight // start 0 if positioning all cells this.maxCellHeight = index ? this.maxCellHeight || 0 : 0; var cellX = 0; // get cellX if ( index > 0 ) { var startCell = this.cells[ index - 1 ]; cellX = startCell.x + startCell.size.outerWidth; } var len = this.cells.length; for ( var i = index; i < len; i++ ) { var cell = this.cells[i]; cell.setPosition( cellX ); cellX += cell.size.outerWidth; this.maxCellHeight = Math.max( cell.size.outerHeight, this.maxCellHeight ); } // keep track of cellX for wrap-around this.slideableWidth = cellX; // slides this.updateSlides(); // contain slides target this._containSlides(); // update slidesWidth this.slidesWidth = len ? this.getLastSlide().target - this.slides[0].target : 0; }; /** * cell.getSize() on multiple cells * @param {Array} cells - cells to size */ proto._sizeCells = function( cells ) { cells.forEach( function( cell ) { cell.getSize(); } ); }; // -------------------------- -------------------------- // proto.updateSlides = function() { this.slides = []; if ( !this.cells.length ) { return; } var slide = new Slide( this ); this.slides.push( slide ); var isOriginLeft = this.originSide == 'left'; var nextMargin = isOriginLeft ? 'marginRight' : 'marginLeft'; var canCellFit = this._getCanCellFit(); this.cells.forEach( function( cell, i ) { // just add cell if first cell in slide if ( !slide.cells.length ) { slide.addCell( cell ); return; } var slideWidth = ( slide.outerWidth - slide.firstMargin ) + ( cell.size.outerWidth - cell.size[ nextMargin ] ); if ( canCellFit.call( this, i, slideWidth ) ) { slide.addCell( cell ); } else { // doesn't fit, new slide slide.updateTarget(); slide = new Slide( this ); this.slides.push( slide ); slide.addCell( cell ); } }, this ); // last slide slide.updateTarget(); // update .selectedSlide this.updateSelectedSlide(); }; proto._getCanCellFit = function() { var groupCells = this.options.groupCells; if ( !groupCells ) { return function() { return false; }; } else if ( typeof groupCells == 'number' ) { // group by number. 3 -> [0,1,2], [3,4,5], ... var number = parseInt( groupCells, 10 ); return function( i ) { return ( i % number ) !== 0; }; } // default, group by width of slide // parse '75% var percentMatch = typeof groupCells == 'string' && groupCells.match( /^(\d+)%$/ ); var percent = percentMatch ? parseInt( percentMatch[1], 10 ) / 100 : 1; return function( i, slideWidth ) { /* eslint-disable-next-line no-invalid-this */ return slideWidth <= ( this.size.innerWidth + 1 ) * percent; }; }; // alias _init for jQuery plugin .flickity() proto._init = proto.reposition = function() { this.positionCells(); this.positionSliderAtSelected(); }; proto.getSize = function() { this.size = getSize( this.element ); this.setCellAlign(); this.cursorPosition = this.size.innerWidth * this.cellAlign; }; var cellAlignShorthands = { // cell align, then based on origin side center: { left: 0.5, right: 0.5, }, left: { left: 0, right: 1, }, right: { right: 0, left: 1, }, }; proto.setCellAlign = function() { var shorthand = cellAlignShorthands[ this.options.cellAlign ]; this.cellAlign = shorthand ? shorthand[ this.originSide ] : this.options.cellAlign; }; proto.setGallerySize = function() { if ( this.options.setGallerySize ) { var height = this.options.adaptiveHeight && this.selectedSlide ? this.selectedSlide.height : this.maxCellHeight; this.viewport.style.height = height + 'px'; } }; proto._getWrapShiftCells = function() { // only for wrap-around if ( !this.options.wrapAround ) { return; } // unshift previous cells this._unshiftCells( this.beforeShiftCells ); this._unshiftCells( this.afterShiftCells ); // get before cells // initial gap var gapX = this.cursorPosition; var cellIndex = this.cells.length - 1; this.beforeShiftCells = this._getGapCells( gapX, cellIndex, -1 ); // get after cells // ending gap between last cell and end of gallery viewport gapX = this.size.innerWidth - this.cursorPosition; // start cloning at first cell, working forwards this.afterShiftCells = this._getGapCells( gapX, 0, 1 ); }; proto._getGapCells = function( gapX, cellIndex, increment ) { // keep adding cells until the cover the initial gap var cells = []; while ( gapX > 0 ) { var cell = this.cells[ cellIndex ]; if ( !cell ) { break; } cells.push( cell ); cellIndex += increment; gapX -= cell.size.outerWidth; } return cells; }; // ----- contain ----- // // contain cell targets so no excess sliding proto._containSlides = function() { if ( !this.options.contain || this.options.wrapAround || !this.cells.length ) { return; } var isRightToLeft = this.options.rightToLeft; var beginMargin = isRightToLeft ? 'marginRight' : 'marginLeft'; var endMargin = isRightToLeft ? 'marginLeft' : 'marginRight'; var contentWidth = this.slideableWidth - this.getLastCell().size[ endMargin ]; // content is less than gallery size var isContentSmaller = contentWidth < this.size.innerWidth; // bounds var beginBound = this.cursorPosition + this.cells[0].size[ beginMargin ]; var endBound = contentWidth - this.size.innerWidth * ( 1 - this.cellAlign ); // contain each cell target this.slides.forEach( function( slide ) { if ( isContentSmaller ) { // all cells fit inside gallery slide.target = contentWidth * this.cellAlign; } else { // contain to bounds slide.target = Math.max( slide.target, beginBound ); slide.target = Math.min( slide.target, endBound ); } }, this ); }; // ----- ----- // /** * emits events via eventEmitter and jQuery events * @param {String} type - name of event * @param {Event} event - original event * @param {Array} args - extra arguments */ proto.dispatchEvent = function( type, event, args ) { var emitArgs = event ? [ event ].concat( args ) : args; this.emitEvent( type, emitArgs ); if ( jQuery && this.$element ) { // default trigger with type if no event type += this.options.namespaceJQueryEvents ? '.flickity' : ''; var $event = type; if ( event ) { // create jQuery event var jQEvent = new jQuery.Event( event ); jQEvent.type = type; $event = jQEvent; } this.$element.trigger( $event, args ); } }; // -------------------------- select -------------------------- // /** * @param {Integer} index - index of the slide * @param {Boolean} isWrap - will wrap-around to last/first if at the end * @param {Boolean} isInstant - will immediately set position at selected cell */ proto.select = function( index, isWrap, isInstant ) { if ( !this.isActive ) { return; } index = parseInt( index, 10 ); this._wrapSelect( index ); if ( this.options.wrapAround || isWrap ) { index = utils.modulo( index, this.slides.length ); } // bail if invalid index if ( !this.slides[ index ] ) { return; } var prevIndex = this.selectedIndex; this.selectedIndex = index; this.updateSelectedSlide(); if ( isInstant ) { this.positionSliderAtSelected(); } else { this.startAnimation(); } if ( this.options.adaptiveHeight ) { this.setGallerySize(); } // events this.dispatchEvent( 'select', null, [ index ] ); // change event if new index if ( index != prevIndex ) { this.dispatchEvent( 'change', null, [ index ] ); } // old v1 event name, remove in v3 this.dispatchEvent('cellSelect'); }; // wraps position for wrapAround, to move to closest slide. #113 proto._wrapSelect = function( index ) { var len = this.slides.length; var isWrapping = this.options.wrapAround && len > 1; if ( !isWrapping ) { return index; } var wrapIndex = utils.modulo( index, len ); // go to shortest var delta = Math.abs( wrapIndex - this.selectedIndex ); var backWrapDelta = Math.abs( ( wrapIndex + len ) - this.selectedIndex ); var forewardWrapDelta = Math.abs( ( wrapIndex - len ) - this.selectedIndex ); if ( !this.isDragSelect && backWrapDelta < delta ) { index += len; } else if ( !this.isDragSelect && forewardWrapDelta < delta ) { index -= len; } // wrap position so slider is within normal area if ( index < 0 ) { this.x -= this.slideableWidth; } else if ( index >= len ) { this.x += this.slideableWidth; } }; proto.previous = function( isWrap, isInstant ) { this.select( this.selectedIndex - 1, isWrap, isInstant ); }; proto.next = function( isWrap, isInstant ) { this.select( this.selectedIndex + 1, isWrap, isInstant ); }; proto.updateSelectedSlide = function() { var slide = this.slides[ this.selectedIndex ]; // selectedIndex could be outside of slides, if triggered before resize() if ( !slide ) { return; } // unselect previous selected slide this.unselectSelectedSlide(); // update new selected slide this.selectedSlide = slide; slide.select(); this.selectedCells = slide.cells; this.selectedElements = slide.getCellElements(); // HACK: selectedCell & selectedElement is first cell in slide, backwards compatibility // Remove in v3? this.selectedCell = slide.cells[0]; this.selectedElement = this.selectedElements[0]; }; proto.unselectSelectedSlide = function() { if ( this.selectedSlide ) { this.selectedSlide.unselect(); } }; proto.selectInitialIndex = function() { var initialIndex = this.options.initialIndex; // already activated, select previous selectedIndex if ( this.isInitActivated ) { this.select( this.selectedIndex, false, true ); return; } // select with selector string if ( initialIndex && typeof initialIndex == 'string' ) { var cell = this.queryCell( initialIndex ); if ( cell ) { this.selectCell( initialIndex, false, true ); return; } } var index = 0; // select with number if ( initialIndex && this.slides[ initialIndex ] ) { index = initialIndex; } // select instantly this.select( index, false, true ); }; /** * select slide from number or cell element * @param {[Element, Number]} value - zero-based index or element to select * @param {Boolean} isWrap - enables wrapping around for extra index * @param {Boolean} isInstant - disables slide animation */ proto.selectCell = function( value, isWrap, isInstant ) { // get cell var cell = this.queryCell( value ); if ( !cell ) { return; } var index = this.getCellSlideIndex( cell ); this.select( index, isWrap, isInstant ); }; proto.getCellSlideIndex = function( cell ) { // get index of slides that has cell for ( var i = 0; i < this.slides.length; i++ ) { var slide = this.slides[i]; var index = slide.cells.indexOf( cell ); if ( index != -1 ) { return i; } } }; // -------------------------- get cells -------------------------- // /** * get Flickity.Cell, given an Element * @param {Element} elem - matching cell element * @returns {Flickity.Cell} cell - matching cell */ proto.getCell = function( elem ) { // loop through cells to get the one that matches for ( var i = 0; i < this.cells.length; i++ ) { var cell = this.cells[i]; if ( cell.element == elem ) { return cell; } } }; /** * get collection of Flickity.Cells, given Elements * @param {[Element, Array, NodeList]} elems - multiple elements * @returns {Array} cells - Flickity.Cells */ proto.getCells = function( elems ) { elems = utils.makeArray( elems ); var cells = []; elems.forEach( function( elem ) { var cell = this.getCell( elem ); if ( cell ) { cells.push( cell ); } }, this ); return cells; }; /** * get cell elements * @returns {Array} cellElems */ proto.getCellElements = function() { return this.cells.map( function( cell ) { return cell.element; } ); }; /** * get parent cell from an element * @param {Element} elem - child element * @returns {Flickit.Cell} cell - parent cell */ proto.getParentCell = function( elem ) { // first check if elem is cell var cell = this.getCell( elem ); if ( cell ) { return cell; } // try to get parent cell elem elem = utils.getParent( elem, '.flickity-slider > *' ); return this.getCell( elem ); }; /** * get cells adjacent to a slide * @param {Integer} adjCount - number of adjacent slides * @param {Integer} index - index of slide to start * @returns {Array} cells - array of Flickity.Cells */ proto.getAdjacentCellElements = function( adjCount, index ) { if ( !adjCount ) { return this.selectedSlide.getCellElements(); } index = index === undefined ? this.selectedIndex : index; var len = this.slides.length; if ( 1 + ( adjCount * 2 ) >= len ) { return this.getCellElements(); } var cellElems = []; for ( var i = index - adjCount; i <= index + adjCount; i++ ) { var slideIndex = this.options.wrapAround ? utils.modulo( i, len ) : i; var slide = this.slides[ slideIndex ]; if ( slide ) { cellElems = cellElems.concat( slide.getCellElements() ); } } return cellElems; }; /** * select slide from number or cell element * @param {[Element, String, Number]} selector - element, selector string, or index * @returns {Flickity.Cell} - matching cell */ proto.queryCell = function( selector ) { if ( typeof selector == 'number' ) { // use number as index return this.cells[ selector ]; } if ( typeof selector == 'string' ) { // do not select invalid selectors from hash: #123, #/. #791 if ( selector.match( /^[#.]?[\d/]/ ) ) { return; } // use string as selector, get element selector = this.element.querySelector( selector ); } // get cell from element return this.getCell( selector ); }; // -------------------------- events -------------------------- // proto.uiChange = function() { this.emitEvent('uiChange'); }; // keep focus on element when child UI elements are clicked proto.childUIPointerDown = function( event ) { // HACK iOS does not allow touch events to bubble up?! if ( event.type != 'touchstart' ) { event.preventDefault(); } this.focus(); }; // ----- resize ----- // proto.onresize = function() { this.watchCSS(); this.resize(); }; utils.debounceMethod( Flickity, 'onresize', 150 ); proto.resize = function() { if ( !this.isActive ) { return; } this.getSize(); // wrap values if ( this.options.wrapAround ) { this.x = utils.modulo( this.x, this.slideableWidth ); } this.positionCells(); this._getWrapShiftCells(); this.setGallerySize(); this.emitEvent('resize'); // update selected index for group slides, instant // TODO: position can be lost between groups of various numbers var selectedElement = this.selectedElements && this.selectedElements[0]; this.selectCell( selectedElement, false, true ); }; // watches the :after property, activates/deactivates proto.watchCSS = function() { var watchOption = this.options.watchCSS; if ( !watchOption ) { return; } var afterContent = getComputedStyle( this.element, ':after' ).content; // activate if :after { content: 'flickity' } if ( afterContent.indexOf('flickity') != -1 ) { this.activate(); } else { this.deactivate(); } }; // ----- keydown ----- // // go previous/next if left/right keys pressed proto.onkeydown = function( event ) { // only work if element is in focus var isNotFocused = document.activeElement && document.activeElement != this.element; if ( !this.options.accessibility || isNotFocused ) { return; } var handler = Flickity.keyboardHandlers[ event.keyCode ]; if ( handler ) { handler.call( this ); } }; Flickity.keyboardHandlers = { // left arrow 37: function() { var leftMethod = this.options.rightToLeft ? 'next' : 'previous'; this.uiChange(); this[ leftMethod ](); }, // right arrow 39: function() { var rightMethod = this.options.rightToLeft ? 'previous' : 'next'; this.uiChange(); this[ rightMethod ](); }, }; // ----- focus ----- // proto.focus = function() { // TODO remove scrollTo once focus options gets more support // https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus ... // #Browser_compatibility var prevScrollY = window.pageYOffset; this.element.focus({ preventScroll: true }); // hack to fix scroll jump after focus, #76 if ( window.pageYOffset != prevScrollY ) { window.scrollTo( window.pageXOffset, prevScrollY ); } }; // -------------------------- destroy -------------------------- // // deactivate all Flickity functionality, but keep stuff available proto.deactivate = function() { if ( !this.isActive ) { return; } this.element.classList.remove('flickity-enabled'); this.element.classList.remove('flickity-rtl'); this.unselectSelectedSlide(); // destroy cells this.cells.forEach( function( cell ) { cell.destroy(); } ); this.element.removeChild( this.viewport ); // move child elements back into element moveElements( this.slider.children, this.element ); if ( this.options.accessibility ) { this.element.removeAttribute('tabIndex'); this.element.removeEventListener( 'keydown', this ); } // set flags this.isActive = false; this.emitEvent('deactivate'); }; proto.destroy = function() { this.deactivate(); window.removeEventListener( 'resize', this ); this.allOff(); this.emitEvent('destroy'); if ( jQuery && this.$element ) { jQuery.removeData( this.element, 'flickity' ); } delete this.element.flickityGUID; delete instances[ this.guid ]; }; // -------------------------- prototype -------------------------- // utils.extend( proto, animatePrototype ); // -------------------------- extras -------------------------- // /** * get Flickity instance from element * @param {[Element, String]} elem - element or selector string * @returns {Flickity} - Flickity instance */ Flickity.data = function( elem ) { elem = utils.getQueryElement( elem ); var id = elem && elem.flickityGUID; return id && instances[ id ]; }; utils.htmlInit( Flickity, 'flickity' ); if ( jQuery && jQuery.bridget ) { jQuery.bridget( 'flickity', Flickity ); } // set internal jQuery, for Webpack + jQuery v3, #478 Flickity.setJQuery = function( jq ) { jQuery = jq; }; Flickity.Cell = Cell; Flickity.Slide = Slide; return Flickity; } ) ); /*! * Unipointer v2.3.0 * base class for doing one thing with pointer event * MIT license */ /*jshint browser: true, undef: true, unused: true, strict: true */ ( function( window, factory ) { // universal module definition /* jshint strict: false */ /*global define, module, require */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'unipointer/unipointer',[ 'ev-emitter/ev-emitter' ], function( EvEmitter ) { return factory( window, EvEmitter ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('ev-emitter') ); } else { // browser global window.Unipointer = factory( window, window.EvEmitter ); } }( window, function factory( window, EvEmitter ) { function noop() {} function Unipointer() {} // inherit EvEmitter var proto = Unipointer.prototype = Object.create( EvEmitter.prototype ); proto.bindStartEvent = function( elem ) { this._bindStartEvent( elem, true ); }; proto.unbindStartEvent = function( elem ) { this._bindStartEvent( elem, false ); }; /** * Add or remove start event * @param {Boolean} isAdd - remove if falsey */ proto._bindStartEvent = function( elem, isAdd ) { // munge isAdd, default to true isAdd = isAdd === undefined ? true : isAdd; var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener'; // default to mouse events var startEvent = 'mousedown'; if ( window.PointerEvent ) { // Pointer Events startEvent = 'pointerdown'; } else if ( 'ontouchstart' in window ) { // Touch Events. iOS Safari startEvent = 'touchstart'; } elem[ bindMethod ]( startEvent, this ); }; // trigger handler methods for events proto.handleEvent = function( event ) { var method = 'on' + event.type; if ( this[ method ] ) { this[ method ]( event ); } }; // returns the touch that we're keeping track of proto.getTouch = function( touches ) { for ( var i=0; i < touches.length; i++ ) { var touch = touches[i]; if ( touch.identifier == this.pointerIdentifier ) { return touch; } } }; // ----- start event ----- // proto.onmousedown = function( event ) { // dismiss clicks from right or middle buttons var button = event.button; if ( button && ( button !== 0 && button !== 1 ) ) { return; } this._pointerDown( event, event ); }; proto.ontouchstart = function( event ) { this._pointerDown( event, event.changedTouches[0] ); }; proto.onpointerdown = function( event ) { this._pointerDown( event, event ); }; /** * pointer start * @param {Event} event * @param {Event or Touch} pointer */ proto._pointerDown = function( event, pointer ) { // dismiss right click and other pointers // button = 0 is okay, 1-4 not if ( event.button || this.isPointerDown ) { return; } this.isPointerDown = true; // save pointer identifier to match up touch events this.pointerIdentifier = pointer.pointerId !== undefined ? // pointerId for pointer events, touch.indentifier for touch events pointer.pointerId : pointer.identifier; this.pointerDown( event, pointer ); }; proto.pointerDown = function( event, pointer ) { this._bindPostStartEvents( event ); this.emitEvent( 'pointerDown', [ event, pointer ] ); }; // hash of events to be bound after start event var postStartEvents = { mousedown: [ 'mousemove', 'mouseup' ], touchstart: [ 'touchmove', 'touchend', 'touchcancel' ], pointerdown: [ 'pointermove', 'pointerup', 'pointercancel' ], }; proto._bindPostStartEvents = function( event ) { if ( !event ) { return; } // get proper events to match start event var events = postStartEvents[ event.type ]; // bind events to node events.forEach( function( eventName ) { window.addEventListener( eventName, this ); }, this ); // save these arguments this._boundPointerEvents = events; }; proto._unbindPostStartEvents = function() { // check for _boundEvents, in case dragEnd triggered twice (old IE8 bug) if ( !this._boundPointerEvents ) { return; } this._boundPointerEvents.forEach( function( eventName ) { window.removeEventListener( eventName, this ); }, this ); delete this._boundPointerEvents; }; // ----- move event ----- // proto.onmousemove = function( event ) { this._pointerMove( event, event ); }; proto.onpointermove = function( event ) { if ( event.pointerId == this.pointerIdentifier ) { this._pointerMove( event, event ); } }; proto.ontouchmove = function( event ) { var touch = this.getTouch( event.changedTouches ); if ( touch ) { this._pointerMove( event, touch ); } }; /** * pointer move * @param {Event} event * @param {Event or Touch} pointer * @private */ proto._pointerMove = function( event, pointer ) { this.pointerMove( event, pointer ); }; // public proto.pointerMove = function( event, pointer ) { this.emitEvent( 'pointerMove', [ event, pointer ] ); }; // ----- end event ----- // proto.onmouseup = function( event ) { this._pointerUp( event, event ); }; proto.onpointerup = function( event ) { if ( event.pointerId == this.pointerIdentifier ) { this._pointerUp( event, event ); } }; proto.ontouchend = function( event ) { var touch = this.getTouch( event.changedTouches ); if ( touch ) { this._pointerUp( event, touch ); } }; /** * pointer up * @param {Event} event * @param {Event or Touch} pointer * @private */ proto._pointerUp = function( event, pointer ) { this._pointerDone(); this.pointerUp( event, pointer ); }; // public proto.pointerUp = function( event, pointer ) { this.emitEvent( 'pointerUp', [ event, pointer ] ); }; // ----- pointer done ----- // // triggered on pointer up & pointer cancel proto._pointerDone = function() { this._pointerReset(); this._unbindPostStartEvents(); this.pointerDone(); }; proto._pointerReset = function() { // reset properties this.isPointerDown = false; delete this.pointerIdentifier; }; proto.pointerDone = noop; // ----- pointer cancel ----- // proto.onpointercancel = function( event ) { if ( event.pointerId == this.pointerIdentifier ) { this._pointerCancel( event, event ); } }; proto.ontouchcancel = function( event ) { var touch = this.getTouch( event.changedTouches ); if ( touch ) { this._pointerCancel( event, touch ); } }; /** * pointer cancel * @param {Event} event * @param {Event or Touch} pointer * @private */ proto._pointerCancel = function( event, pointer ) { this._pointerDone(); this.pointerCancel( event, pointer ); }; // public proto.pointerCancel = function( event, pointer ) { this.emitEvent( 'pointerCancel', [ event, pointer ] ); }; // ----- ----- // // utility function for getting x/y coords from event Unipointer.getPointerPoint = function( pointer ) { return { x: pointer.pageX, y: pointer.pageY }; }; // ----- ----- // return Unipointer; })); /*! * Unidragger v2.3.1 * Draggable base class * MIT license */ /*jshint browser: true, unused: true, undef: true, strict: true */ ( function( window, factory ) { // universal module definition /*jshint strict: false */ /*globals define, module, require */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'unidragger/unidragger',[ 'unipointer/unipointer' ], function( Unipointer ) { return factory( window, Unipointer ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('unipointer') ); } else { // browser global window.Unidragger = factory( window, window.Unipointer ); } }( window, function factory( window, Unipointer ) { // -------------------------- Unidragger -------------------------- // function Unidragger() {} // inherit Unipointer & EvEmitter var proto = Unidragger.prototype = Object.create( Unipointer.prototype ); // ----- bind start ----- // proto.bindHandles = function() { this._bindHandles( true ); }; proto.unbindHandles = function() { this._bindHandles( false ); }; /** * Add or remove start event * @param {Boolean} isAdd */ proto._bindHandles = function( isAdd ) { // munge isAdd, default to true isAdd = isAdd === undefined ? true : isAdd; // bind each handle var bindMethod = isAdd ? 'addEventListener' : 'removeEventListener'; var touchAction = isAdd ? this._touchActionValue : ''; for ( var i=0; i < this.handles.length; i++ ) { var handle = this.handles[i]; this._bindStartEvent( handle, isAdd ); handle[ bindMethod ]( 'click', this ); // touch-action: none to override browser touch gestures. metafizzy/flickity#540 if ( window.PointerEvent ) { handle.style.touchAction = touchAction; } } }; // prototype so it can be overwriteable by Flickity proto._touchActionValue = 'none'; // ----- start event ----- // /** * pointer start * @param {Event} event * @param {Event or Touch} pointer */ proto.pointerDown = function( event, pointer ) { var isOkay = this.okayPointerDown( event ); if ( !isOkay ) { return; } // track start event position // Safari 9 overrides pageX and pageY. These values needs to be copied. flickity#842 this.pointerDownPointer = { pageX: pointer.pageX, pageY: pointer.pageY, }; event.preventDefault(); this.pointerDownBlur(); // bind move and end events this._bindPostStartEvents( event ); this.emitEvent( 'pointerDown', [ event, pointer ] ); }; // nodes that have text fields var cursorNodes = { TEXTAREA: true, INPUT: true, SELECT: true, OPTION: true, }; // input types that do not have text fields var clickTypes = { radio: true, checkbox: true, button: true, submit: true, image: true, file: true, }; // dismiss inputs with text fields. flickity#403, flickity#404 proto.okayPointerDown = function( event ) { var isCursorNode = cursorNodes[ event.target.nodeName ]; var isClickType = clickTypes[ event.target.type ]; var isOkay = !isCursorNode || isClickType; if ( !isOkay ) { this._pointerReset(); } return isOkay; }; // kludge to blur previously focused input proto.pointerDownBlur = function() { var focused = document.activeElement; // do not blur body for IE10, metafizzy/flickity#117 var canBlur = focused && focused.blur && focused != document.body; if ( canBlur ) { focused.blur(); } }; // ----- move event ----- // /** * drag move * @param {Event} event * @param {Event or Touch} pointer */ proto.pointerMove = function( event, pointer ) { var moveVector = this._dragPointerMove( event, pointer ); this.emitEvent( 'pointerMove', [ event, pointer, moveVector ] ); this._dragMove( event, pointer, moveVector ); }; // base pointer move logic proto._dragPointerMove = function( event, pointer ) { var moveVector = { x: pointer.pageX - this.pointerDownPointer.pageX, y: pointer.pageY - this.pointerDownPointer.pageY }; // start drag if pointer has moved far enough to start drag if ( !this.isDragging && this.hasDragStarted( moveVector ) ) { this._dragStart( event, pointer ); } return moveVector; }; // condition if pointer has moved far enough to start drag proto.hasDragStarted = function( moveVector ) { return Math.abs( moveVector.x ) > 3 || Math.abs( moveVector.y ) > 3; }; // ----- end event ----- // /** * pointer up * @param {Event} event * @param {Event or Touch} pointer */ proto.pointerUp = function( event, pointer ) { this.emitEvent( 'pointerUp', [ event, pointer ] ); this._dragPointerUp( event, pointer ); }; proto._dragPointerUp = function( event, pointer ) { if ( this.isDragging ) { this._dragEnd( event, pointer ); } else { // pointer didn't move enough for drag to start this._staticClick( event, pointer ); } }; // -------------------------- drag -------------------------- // // dragStart proto._dragStart = function( event, pointer ) { this.isDragging = true; // prevent clicks this.isPreventingClicks = true; this.dragStart( event, pointer ); }; proto.dragStart = function( event, pointer ) { this.emitEvent( 'dragStart', [ event, pointer ] ); }; // dragMove proto._dragMove = function( event, pointer, moveVector ) { // do not drag if not dragging yet if ( !this.isDragging ) { return; } this.dragMove( event, pointer, moveVector ); }; proto.dragMove = function( event, pointer, moveVector ) { event.preventDefault(); this.emitEvent( 'dragMove', [ event, pointer, moveVector ] ); }; // dragEnd proto._dragEnd = function( event, pointer ) { // set flags this.isDragging = false; // re-enable clicking async setTimeout( function() { delete this.isPreventingClicks; }.bind( this ) ); this.dragEnd( event, pointer ); }; proto.dragEnd = function( event, pointer ) { this.emitEvent( 'dragEnd', [ event, pointer ] ); }; // ----- onclick ----- // // handle all clicks and prevent clicks when dragging proto.onclick = function( event ) { if ( this.isPreventingClicks ) { event.preventDefault(); } }; // ----- staticClick ----- // // triggered after pointer down & up with no/tiny movement proto._staticClick = function( event, pointer ) { // ignore emulated mouse up clicks if ( this.isIgnoringMouseUp && event.type == 'mouseup' ) { return; } this.staticClick( event, pointer ); // set flag for emulated clicks 300ms after touchend if ( event.type != 'mouseup' ) { this.isIgnoringMouseUp = true; // reset flag after 300ms setTimeout( function() { delete this.isIgnoringMouseUp; }.bind( this ), 400 ); } }; proto.staticClick = function( event, pointer ) { this.emitEvent( 'staticClick', [ event, pointer ] ); }; // ----- utils ----- // Unidragger.getPointerPoint = Unipointer.getPointerPoint; // ----- ----- // return Unidragger; })); // drag ( function( window, factory ) { // universal module definition if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity/js/drag',[ './flickity', 'unidragger/unidragger', 'fizzy-ui-utils/utils', ], function( Flickity, Unidragger, utils ) { return factory( window, Flickity, Unidragger, utils ); } ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('./flickity'), require('unidragger'), require('fizzy-ui-utils') ); } else { // browser global window.Flickity = factory( window, window.Flickity, window.Unidragger, window.fizzyUIUtils ); } }( window, function factory( window, Flickity, Unidragger, utils ) { // ----- defaults ----- // utils.extend( Flickity.defaults, { draggable: '>1', dragThreshold: 3, } ); // ----- create ----- // Flickity.createMethods.push('_createDrag'); // -------------------------- drag prototype -------------------------- // var proto = Flickity.prototype; utils.extend( proto, Unidragger.prototype ); proto._touchActionValue = 'pan-y'; // -------------------------- -------------------------- // var isTouch = 'createTouch' in document; var isTouchmoveScrollCanceled = false; proto._createDrag = function() { this.on( 'activate', this.onActivateDrag ); this.on( 'uiChange', this._uiChangeDrag ); this.on( 'deactivate', this.onDeactivateDrag ); this.on( 'cellChange', this.updateDraggable ); // TODO updateDraggable on resize? if groupCells & slides change // HACK - add seemingly innocuous handler to fix iOS 10 scroll behavior // #457, RubaXa/Sortable#973 if ( isTouch && !isTouchmoveScrollCanceled ) { window.addEventListener( 'touchmove', function() {} ); isTouchmoveScrollCanceled = true; } }; proto.onActivateDrag = function() { this.handles = [ this.viewport ]; this.bindHandles(); this.updateDraggable(); }; proto.onDeactivateDrag = function() { this.unbindHandles(); this.element.classList.remove('is-draggable'); }; proto.updateDraggable = function() { // disable dragging if less than 2 slides. #278 if ( this.options.draggable == '>1' ) { this.isDraggable = this.slides.length > 1; } else { this.isDraggable = this.options.draggable; } if ( this.isDraggable ) { this.element.classList.add('is-draggable'); } else { this.element.classList.remove('is-draggable'); } }; // backwards compatibility proto.bindDrag = function() { this.options.draggable = true; this.updateDraggable(); }; proto.unbindDrag = function() { this.options.draggable = false; this.updateDraggable(); }; proto._uiChangeDrag = function() { delete this.isFreeScrolling; }; // -------------------------- pointer events -------------------------- // proto.pointerDown = function( event, pointer ) { if ( !this.isDraggable ) { this._pointerDownDefault( event, pointer ); return; } var isOkay = this.okayPointerDown( event ); if ( !isOkay ) { return; } this._pointerDownPreventDefault( event ); this.pointerDownFocus( event ); // blur if ( document.activeElement != this.element ) { // do not blur if already focused this.pointerDownBlur(); } // stop if it was moving this.dragX = this.x; this.viewport.classList.add('is-pointer-down'); // track scrolling this.pointerDownScroll = getScrollPosition(); window.addEventListener( 'scroll', this ); this._pointerDownDefault( event, pointer ); }; // default pointerDown logic, used for staticClick proto._pointerDownDefault = function( event, pointer ) { // track start event position // Safari 9 overrides pageX and pageY. These values needs to be copied. #779 this.pointerDownPointer = { pageX: pointer.pageX, pageY: pointer.pageY, }; // bind move and end events this._bindPostStartEvents( event ); this.dispatchEvent( 'pointerDown', event, [ pointer ] ); }; var focusNodes = { INPUT: true, TEXTAREA: true, SELECT: true, }; proto.pointerDownFocus = function( event ) { var isFocusNode = focusNodes[ event.target.nodeName ]; if ( !isFocusNode ) { this.focus(); } }; proto._pointerDownPreventDefault = function( event ) { var isTouchStart = event.type == 'touchstart'; var isTouchPointer = event.pointerType == 'touch'; var isFocusNode = focusNodes[ event.target.nodeName ]; if ( !isTouchStart && !isTouchPointer && !isFocusNode ) { event.preventDefault(); } }; // ----- move ----- // proto.hasDragStarted = function( moveVector ) { return Math.abs( moveVector.x ) > this.options.dragThreshold; }; // ----- up ----- // proto.pointerUp = function( event, pointer ) { delete this.isTouchScrolling; this.viewport.classList.remove('is-pointer-down'); this.dispatchEvent( 'pointerUp', event, [ pointer ] ); this._dragPointerUp( event, pointer ); }; proto.pointerDone = function() { window.removeEventListener( 'scroll', this ); delete this.pointerDownScroll; }; // -------------------------- dragging -------------------------- // proto.dragStart = function( event, pointer ) { if ( !this.isDraggable ) { return; } this.dragStartPosition = this.x; this.startAnimation(); window.removeEventListener( 'scroll', this ); this.dispatchEvent( 'dragStart', event, [ pointer ] ); }; proto.pointerMove = function( event, pointer ) { var moveVector = this._dragPointerMove( event, pointer ); this.dispatchEvent( 'pointerMove', event, [ pointer, moveVector ] ); this._dragMove( event, pointer, moveVector ); }; proto.dragMove = function( event, pointer, moveVector ) { if ( !this.isDraggable ) { return; } event.preventDefault(); this.previousDragX = this.dragX; // reverse if right-to-left var direction = this.options.rightToLeft ? -1 : 1; if ( this.options.wrapAround ) { // wrap around move. #589 moveVector.x %= this.slideableWidth; } var dragX = this.dragStartPosition + moveVector.x * direction; if ( !this.options.wrapAround && this.slides.length ) { // slow drag var originBound = Math.max( -this.slides[0].target, this.dragStartPosition ); dragX = dragX > originBound ? ( dragX + originBound ) * 0.5 : dragX; var endBound = Math.min( -this.getLastSlide().target, this.dragStartPosition ); dragX = dragX < endBound ? ( dragX + endBound ) * 0.5 : dragX; } this.dragX = dragX; this.dragMoveTime = new Date(); this.dispatchEvent( 'dragMove', event, [ pointer, moveVector ] ); }; proto.dragEnd = function( event, pointer ) { if ( !this.isDraggable ) { return; } if ( this.options.freeScroll ) { this.isFreeScrolling = true; } // set selectedIndex based on where flick will end up var index = this.dragEndRestingSelect(); if ( this.options.freeScroll && !this.options.wrapAround ) { // if free-scroll & not wrap around // do not free-scroll if going outside of bounding slides // so bounding slides can attract slider, and keep it in bounds var restingX = this.getRestingPosition(); this.isFreeScrolling = -restingX > this.slides[0].target && -restingX < this.getLastSlide().target; } else if ( !this.options.freeScroll && index == this.selectedIndex ) { // boost selection if selected index has not changed index += this.dragEndBoostSelect(); } delete this.previousDragX; // apply selection // TODO refactor this, selecting here feels weird // HACK, set flag so dragging stays in correct direction this.isDragSelect = this.options.wrapAround; this.select( index ); delete this.isDragSelect; this.dispatchEvent( 'dragEnd', event, [ pointer ] ); }; proto.dragEndRestingSelect = function() { var restingX = this.getRestingPosition(); // how far away from selected slide var distance = Math.abs( this.getSlideDistance( -restingX, this.selectedIndex ) ); // get closet resting going up and going down var positiveResting = this._getClosestResting( restingX, distance, 1 ); var negativeResting = this._getClosestResting( restingX, distance, -1 ); // use closer resting for wrap-around var index = positiveResting.distance < negativeResting.distance ? positiveResting.index : negativeResting.index; return index; }; /** * given resting X and distance to selected cell * get the distance and index of the closest cell * @param {Number} restingX - estimated post-flick resting position * @param {Number} distance - distance to selected cell * @param {Integer} increment - +1 or -1, going up or down * @returns {Object} - { distance: {Number}, index: {Integer} } */ proto._getClosestResting = function( restingX, distance, increment ) { var index = this.selectedIndex; var minDistance = Infinity; var condition = this.options.contain && !this.options.wrapAround ? // if contain, keep going if distance is equal to minDistance function( dist, minDist ) { return dist <= minDist; } : function( dist, minDist ) { return dist < minDist; }; while ( condition( distance, minDistance ) ) { // measure distance to next cell index += increment; minDistance = distance; distance = this.getSlideDistance( -restingX, index ); if ( distance === null ) { break; } distance = Math.abs( distance ); } return { distance: minDistance, // selected was previous index index: index - increment, }; }; /** * measure distance between x and a slide target * @param {Number} x - horizontal position * @param {Integer} index - slide index * @returns {Number} - slide distance */ proto.getSlideDistance = function( x, index ) { var len = this.slides.length; // wrap around if at least 2 slides var isWrapAround = this.options.wrapAround && len > 1; var slideIndex = isWrapAround ? utils.modulo( index, len ) : index; var slide = this.slides[ slideIndex ]; if ( !slide ) { return null; } // add distance for wrap-around slides var wrap = isWrapAround ? this.slideableWidth * Math.floor( index/len ) : 0; return x - ( slide.target + wrap ); }; proto.dragEndBoostSelect = function() { // do not boost if no previousDragX or dragMoveTime if ( this.previousDragX === undefined || !this.dragMoveTime || // or if drag was held for 100 ms new Date() - this.dragMoveTime > 100 ) { return 0; } var distance = this.getSlideDistance( -this.dragX, this.selectedIndex ); var delta = this.previousDragX - this.dragX; if ( distance > 0 && delta > 0 ) { // boost to next if moving towards the right, and positive velocity return 1; } else if ( distance < 0 && delta < 0 ) { // boost to previous if moving towards the left, and negative velocity return -1; } return 0; }; // ----- staticClick ----- // proto.staticClick = function( event, pointer ) { // get clickedCell, if cell was clicked var clickedCell = this.getParentCell( event.target ); var cellElem = clickedCell && clickedCell.element; var cellIndex = clickedCell && this.cells.indexOf( clickedCell ); this.dispatchEvent( 'staticClick', event, [ pointer, cellElem, cellIndex ] ); }; // ----- scroll ----- // proto.onscroll = function() { var scroll = getScrollPosition(); var scrollMoveX = this.pointerDownScroll.x - scroll.x; var scrollMoveY = this.pointerDownScroll.y - scroll.y; // cancel click/tap if scroll is too much if ( Math.abs( scrollMoveX ) > 3 || Math.abs( scrollMoveY ) > 3 ) { this._pointerDone(); } }; // ----- utils ----- // function getScrollPosition() { return { x: window.pageXOffset, y: window.pageYOffset, }; } // ----- ----- // return Flickity; } ) ); // prev/next buttons ( function( window, factory ) { // universal module definition if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity/js/prev-next-button',[ './flickity', 'unipointer/unipointer', 'fizzy-ui-utils/utils', ], function( Flickity, Unipointer, utils ) { return factory( window, Flickity, Unipointer, utils ); } ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('./flickity'), require('unipointer'), require('fizzy-ui-utils') ); } else { // browser global factory( window, window.Flickity, window.Unipointer, window.fizzyUIUtils ); } }( window, function factory( window, Flickity, Unipointer, utils ) { 'use strict'; var svgURI = 'http://www.w3.org/2000/svg'; // -------------------------- PrevNextButton -------------------------- // function PrevNextButton( direction, parent ) { this.direction = direction; this.parent = parent; this._create(); } PrevNextButton.prototype = Object.create( Unipointer.prototype ); PrevNextButton.prototype._create = function() { // properties this.isEnabled = true; this.isPrevious = this.direction == -1; var leftDirection = this.parent.options.rightToLeft ? 1 : -1; this.isLeft = this.direction == leftDirection; var element = this.element = document.createElement('button'); element.className = 'flickity-button flickity-prev-next-button'; element.className += this.isPrevious ? ' previous' : ' next'; // prevent button from submitting form http://stackoverflow.com/a/10836076/182183 element.setAttribute( 'type', 'button' ); // init as disabled this.disable(); element.setAttribute( 'aria-label', this.isPrevious ? 'Previous' : 'Next' ); // create arrow var svg = this.createSVG(); element.appendChild( svg ); // events this.parent.on( 'select', this.update.bind( this ) ); this.on( 'pointerDown', this.parent.childUIPointerDown.bind( this.parent ) ); }; PrevNextButton.prototype.activate = function() { this.bindStartEvent( this.element ); this.element.addEventListener( 'click', this ); // add to DOM this.parent.element.appendChild( this.element ); }; PrevNextButton.prototype.deactivate = function() { // remove from DOM this.parent.element.removeChild( this.element ); // click events this.unbindStartEvent( this.element ); this.element.removeEventListener( 'click', this ); }; PrevNextButton.prototype.createSVG = function() { var svg = document.createElementNS( svgURI, 'svg' ); svg.setAttribute( 'class', 'flickity-button-icon' ); svg.setAttribute( 'viewBox', '0 0 100 100' ); var path = document.createElementNS( svgURI, 'path' ); var pathMovements = getArrowMovements( this.parent.options.arrowShape ); path.setAttribute( 'd', pathMovements ); path.setAttribute( 'class', 'arrow' ); // rotate arrow if ( !this.isLeft ) { path.setAttribute( 'transform', 'translate(100, 100) rotate(180) ' ); } svg.appendChild( path ); return svg; }; // get SVG path movmement function getArrowMovements( shape ) { // use shape as movement if string if ( typeof shape == 'string' ) { return shape; } // create movement string return 'M ' + shape.x0 + ',50' + ' L ' + shape.x1 + ',' + ( shape.y1 + 50 ) + ' L ' + shape.x2 + ',' + ( shape.y2 + 50 ) + ' L ' + shape.x3 + ',50 ' + ' L ' + shape.x2 + ',' + ( 50 - shape.y2 ) + ' L ' + shape.x1 + ',' + ( 50 - shape.y1 ) + ' Z'; } PrevNextButton.prototype.handleEvent = utils.handleEvent; PrevNextButton.prototype.onclick = function() { if ( !this.isEnabled ) { return; } this.parent.uiChange(); var method = this.isPrevious ? 'previous' : 'next'; this.parent[ method ](); }; // ----- ----- // PrevNextButton.prototype.enable = function() { if ( this.isEnabled ) { return; } this.element.disabled = false; this.isEnabled = true; }; PrevNextButton.prototype.disable = function() { if ( !this.isEnabled ) { return; } this.element.disabled = true; this.isEnabled = false; }; PrevNextButton.prototype.update = function() { // index of first or last slide, if previous or next var slides = this.parent.slides; // enable is wrapAround and at least 2 slides if ( this.parent.options.wrapAround && slides.length > 1 ) { this.enable(); return; } var lastIndex = slides.length ? slides.length - 1 : 0; var boundIndex = this.isPrevious ? 0 : lastIndex; var method = this.parent.selectedIndex == boundIndex ? 'disable' : 'enable'; this[ method ](); }; PrevNextButton.prototype.destroy = function() { this.deactivate(); this.allOff(); }; // -------------------------- Flickity prototype -------------------------- // utils.extend( Flickity.defaults, { prevNextButtons: true, arrowShape: { x0: 10, x1: 60, y1: 50, x2: 70, y2: 40, x3: 30, }, } ); Flickity.createMethods.push('_createPrevNextButtons'); var proto = Flickity.prototype; proto._createPrevNextButtons = function() { if ( !this.options.prevNextButtons ) { return; } this.prevButton = new PrevNextButton( -1, this ); this.nextButton = new PrevNextButton( 1, this ); this.on( 'activate', this.activatePrevNextButtons ); }; proto.activatePrevNextButtons = function() { this.prevButton.activate(); this.nextButton.activate(); this.on( 'deactivate', this.deactivatePrevNextButtons ); }; proto.deactivatePrevNextButtons = function() { this.prevButton.deactivate(); this.nextButton.deactivate(); this.off( 'deactivate', this.deactivatePrevNextButtons ); }; // -------------------------- -------------------------- // Flickity.PrevNextButton = PrevNextButton; return Flickity; } ) ); // page dots ( function( window, factory ) { // universal module definition if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity/js/page-dots',[ './flickity', 'unipointer/unipointer', 'fizzy-ui-utils/utils', ], function( Flickity, Unipointer, utils ) { return factory( window, Flickity, Unipointer, utils ); } ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('./flickity'), require('unipointer'), require('fizzy-ui-utils') ); } else { // browser global factory( window, window.Flickity, window.Unipointer, window.fizzyUIUtils ); } }( window, function factory( window, Flickity, Unipointer, utils ) { // -------------------------- PageDots -------------------------- // function PageDots( parent ) { this.parent = parent; this._create(); } PageDots.prototype = Object.create( Unipointer.prototype ); PageDots.prototype._create = function() { // create holder element this.holder = document.createElement('ol'); this.holder.className = 'flickity-page-dots'; // create dots, array of elements this.dots = []; // events this.handleClick = this.onClick.bind( this ); this.on( 'pointerDown', this.parent.childUIPointerDown.bind( this.parent ) ); }; PageDots.prototype.activate = function() { this.setDots(); this.holder.addEventListener( 'click', this.handleClick ); this.bindStartEvent( this.holder ); // add to DOM this.parent.element.appendChild( this.holder ); }; PageDots.prototype.deactivate = function() { this.holder.removeEventListener( 'click', this.handleClick ); this.unbindStartEvent( this.holder ); // remove from DOM this.parent.element.removeChild( this.holder ); }; PageDots.prototype.setDots = function() { // get difference between number of slides and number of dots var delta = this.parent.slides.length - this.dots.length; if ( delta > 0 ) { this.addDots( delta ); } else if ( delta < 0 ) { this.removeDots( -delta ); } }; PageDots.prototype.addDots = function( count ) { var fragment = document.createDocumentFragment(); var newDots = []; var length = this.dots.length; var max = length + count; for ( var i = length; i < max; i++ ) { var dot = document.createElement('li'); dot.className = 'dot'; dot.setAttribute( 'aria-label', 'Page dot ' + ( i + 1 ) ); fragment.appendChild( dot ); newDots.push( dot ); } this.holder.appendChild( fragment ); this.dots = this.dots.concat( newDots ); }; PageDots.prototype.removeDots = function( count ) { // remove from this.dots collection var removeDots = this.dots.splice( this.dots.length - count, count ); // remove from DOM removeDots.forEach( function( dot ) { this.holder.removeChild( dot ); }, this ); }; PageDots.prototype.updateSelected = function() { // remove selected class on previous if ( this.selectedDot ) { this.selectedDot.className = 'dot'; this.selectedDot.removeAttribute('aria-current'); } // don't proceed if no dots if ( !this.dots.length ) { return; } this.selectedDot = this.dots[ this.parent.selectedIndex ]; this.selectedDot.className = 'dot is-selected'; this.selectedDot.setAttribute( 'aria-current', 'step' ); }; PageDots.prototype.onTap = // old method name, backwards-compatible PageDots.prototype.onClick = function( event ) { var target = event.target; // only care about dot clicks if ( target.nodeName != 'LI' ) { return; } this.parent.uiChange(); var index = this.dots.indexOf( target ); this.parent.select( index ); }; PageDots.prototype.destroy = function() { this.deactivate(); this.allOff(); }; Flickity.PageDots = PageDots; // -------------------------- Flickity -------------------------- // utils.extend( Flickity.defaults, { pageDots: true, } ); Flickity.createMethods.push('_createPageDots'); var proto = Flickity.prototype; proto._createPageDots = function() { if ( !this.options.pageDots ) { return; } this.pageDots = new PageDots( this ); // events this.on( 'activate', this.activatePageDots ); this.on( 'select', this.updateSelectedPageDots ); this.on( 'cellChange', this.updatePageDots ); this.on( 'resize', this.updatePageDots ); this.on( 'deactivate', this.deactivatePageDots ); }; proto.activatePageDots = function() { this.pageDots.activate(); }; proto.updateSelectedPageDots = function() { this.pageDots.updateSelected(); }; proto.updatePageDots = function() { this.pageDots.setDots(); }; proto.deactivatePageDots = function() { this.pageDots.deactivate(); }; // ----- ----- // Flickity.PageDots = PageDots; return Flickity; } ) ); // player & autoPlay ( function( window, factory ) { // universal module definition if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity/js/player',[ 'ev-emitter/ev-emitter', 'fizzy-ui-utils/utils', './flickity', ], function( EvEmitter, utils, Flickity ) { return factory( EvEmitter, utils, Flickity ); } ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( require('ev-emitter'), require('fizzy-ui-utils'), require('./flickity') ); } else { // browser global factory( window.EvEmitter, window.fizzyUIUtils, window.Flickity ); } }( window, function factory( EvEmitter, utils, Flickity ) { // -------------------------- Player -------------------------- // function Player( parent ) { this.parent = parent; this.state = 'stopped'; // visibility change event handler this.onVisibilityChange = this.visibilityChange.bind( this ); this.onVisibilityPlay = this.visibilityPlay.bind( this ); } Player.prototype = Object.create( EvEmitter.prototype ); // start play Player.prototype.play = function() { if ( this.state == 'playing' ) { return; } // do not play if page is hidden, start playing when page is visible var isPageHidden = document.hidden; if ( isPageHidden ) { document.addEventListener( 'visibilitychange', this.onVisibilityPlay ); return; } this.state = 'playing'; // listen to visibility change document.addEventListener( 'visibilitychange', this.onVisibilityChange ); // start ticking this.tick(); }; Player.prototype.tick = function() { // do not tick if not playing if ( this.state != 'playing' ) { return; } var time = this.parent.options.autoPlay; // default to 3 seconds time = typeof time == 'number' ? time : 3000; var _this = this; // HACK: reset ticks if stopped and started within interval this.clear(); this.timeout = setTimeout( function() { _this.parent.next( true ); _this.tick(); }, time ); }; Player.prototype.stop = function() { this.state = 'stopped'; this.clear(); // remove visibility change event document.removeEventListener( 'visibilitychange', this.onVisibilityChange ); }; Player.prototype.clear = function() { clearTimeout( this.timeout ); }; Player.prototype.pause = function() { if ( this.state == 'playing' ) { this.state = 'paused'; this.clear(); } }; Player.prototype.unpause = function() { // re-start play if paused if ( this.state == 'paused' ) { this.play(); } }; // pause if page visibility is hidden, unpause if visible Player.prototype.visibilityChange = function() { var isPageHidden = document.hidden; this[ isPageHidden ? 'pause' : 'unpause' ](); }; Player.prototype.visibilityPlay = function() { this.play(); document.removeEventListener( 'visibilitychange', this.onVisibilityPlay ); }; // -------------------------- Flickity -------------------------- // utils.extend( Flickity.defaults, { pauseAutoPlayOnHover: true, } ); Flickity.createMethods.push('_createPlayer'); var proto = Flickity.prototype; proto._createPlayer = function() { this.player = new Player( this ); this.on( 'activate', this.activatePlayer ); this.on( 'uiChange', this.stopPlayer ); this.on( 'pointerDown', this.stopPlayer ); this.on( 'deactivate', this.deactivatePlayer ); }; proto.activatePlayer = function() { if ( !this.options.autoPlay ) { return; } this.player.play(); this.element.addEventListener( 'mouseenter', this ); }; // Player API, don't hate the ... thanks I know where the door is proto.playPlayer = function() { this.player.play(); }; proto.stopPlayer = function() { this.player.stop(); }; proto.pausePlayer = function() { this.player.pause(); }; proto.unpausePlayer = function() { this.player.unpause(); }; proto.deactivatePlayer = function() { this.player.stop(); this.element.removeEventListener( 'mouseenter', this ); }; // ----- mouseenter/leave ----- // // pause auto-play on hover proto.onmouseenter = function() { if ( !this.options.pauseAutoPlayOnHover ) { return; } this.player.pause(); this.element.addEventListener( 'mouseleave', this ); }; // resume auto-play on hover off proto.onmouseleave = function() { this.player.unpause(); this.element.removeEventListener( 'mouseleave', this ); }; // ----- ----- // Flickity.Player = Player; return Flickity; } ) ); // add, remove cell ( function( window, factory ) { // universal module definition if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity/js/add-remove-cell',[ './flickity', 'fizzy-ui-utils/utils', ], function( Flickity, utils ) { return factory( window, Flickity, utils ); } ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('./flickity'), require('fizzy-ui-utils') ); } else { // browser global factory( window, window.Flickity, window.fizzyUIUtils ); } }( window, function factory( window, Flickity, utils ) { // append cells to a document fragment function getCellsFragment( cells ) { var fragment = document.createDocumentFragment(); cells.forEach( function( cell ) { fragment.appendChild( cell.element ); } ); return fragment; } // -------------------------- add/remove cell prototype -------------------------- // var proto = Flickity.prototype; /** * Insert, prepend, or append cells * @param {[Element, Array, NodeList]} elems - Elements to insert * @param {Integer} index - Zero-based number to insert */ proto.insert = function( elems, index ) { var cells = this._makeCells( elems ); if ( !cells || !cells.length ) { return; } var len = this.cells.length; // default to append index = index === undefined ? len : index; // add cells with document fragment var fragment = getCellsFragment( cells ); // append to slider var isAppend = index == len; if ( isAppend ) { this.slider.appendChild( fragment ); } else { var insertCellElement = this.cells[ index ].element; this.slider.insertBefore( fragment, insertCellElement ); } // add to this.cells if ( index === 0 ) { // prepend, add to start this.cells = cells.concat( this.cells ); } else if ( isAppend ) { // append, add to end this.cells = this.cells.concat( cells ); } else { // insert in this.cells var endCells = this.cells.splice( index, len - index ); this.cells = this.cells.concat( cells ).concat( endCells ); } this._sizeCells( cells ); this.cellChange( index, true ); }; proto.append = function( elems ) { this.insert( elems, this.cells.length ); }; proto.prepend = function( elems ) { this.insert( elems, 0 ); }; /** * Remove cells * @param {[Element, Array, NodeList]} elems - ELements to remove */ proto.remove = function( elems ) { var cells = this.getCells( elems ); if ( !cells || !cells.length ) { return; } var minCellIndex = this.cells.length - 1; // remove cells from collection & DOM cells.forEach( function( cell ) { cell.remove(); var index = this.cells.indexOf( cell ); minCellIndex = Math.min( index, minCellIndex ); utils.removeFrom( this.cells, cell ); }, this ); this.cellChange( minCellIndex, true ); }; /** * logic to be run after a cell's size changes * @param {Element} elem - cell's element */ proto.cellSizeChange = function( elem ) { var cell = this.getCell( elem ); if ( !cell ) { return; } cell.getSize(); var index = this.cells.indexOf( cell ); this.cellChange( index ); }; /** * logic any time a cell is changed: added, removed, or size changed * @param {Integer} changedCellIndex - index of the changed cell, optional * @param {Boolean} isPositioningSlider - Positions slider after selection */ proto.cellChange = function( changedCellIndex, isPositioningSlider ) { var prevSelectedElem = this.selectedElement; this._positionCells( changedCellIndex ); this._getWrapShiftCells(); this.setGallerySize(); // update selectedIndex // try to maintain position & select previous selected element var cell = this.getCell( prevSelectedElem ); if ( cell ) { this.selectedIndex = this.getCellSlideIndex( cell ); } this.selectedIndex = Math.min( this.slides.length - 1, this.selectedIndex ); this.emitEvent( 'cellChange', [ changedCellIndex ] ); // position slider this.select( this.selectedIndex ); // do not position slider after lazy load if ( isPositioningSlider ) { this.positionSliderAtSelected(); } }; // ----- ----- // return Flickity; } ) ); // lazyload ( function( window, factory ) { // universal module definition if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity/js/lazyload',[ './flickity', 'fizzy-ui-utils/utils', ], function( Flickity, utils ) { return factory( window, Flickity, utils ); } ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('./flickity'), require('fizzy-ui-utils') ); } else { // browser global factory( window, window.Flickity, window.fizzyUIUtils ); } }( window, function factory( window, Flickity, utils ) { 'use strict'; Flickity.createMethods.push('_createLazyload'); var proto = Flickity.prototype; proto._createLazyload = function() { this.on( 'select', this.lazyLoad ); }; proto.lazyLoad = function() { var lazyLoad = this.options.lazyLoad; if ( !lazyLoad ) { return; } // get adjacent cells, use lazyLoad option for adjacent count var adjCount = typeof lazyLoad == 'number' ? lazyLoad : 0; var cellElems = this.getAdjacentCellElements( adjCount ); // get lazy images in those cells var lazyImages = []; cellElems.forEach( function( cellElem ) { var lazyCellImages = getCellLazyImages( cellElem ); lazyImages = lazyImages.concat( lazyCellImages ); } ); // load lazy images lazyImages.forEach( function( img ) { new LazyLoader( img, this ); }, this ); }; function getCellLazyImages( cellElem ) { // check if cell element is lazy image if ( cellElem.nodeName == 'IMG' ) { var lazyloadAttr = cellElem.getAttribute('data-flickity-lazyload'); var srcAttr = cellElem.getAttribute('data-flickity-lazyload-src'); var srcsetAttr = cellElem.getAttribute('data-flickity-lazyload-srcset'); if ( lazyloadAttr || srcAttr || srcsetAttr ) { return [ cellElem ]; } } // select lazy images in cell var lazySelector = 'img[data-flickity-lazyload], ' + 'img[data-flickity-lazyload-src], img[data-flickity-lazyload-srcset]'; var imgs = cellElem.querySelectorAll( lazySelector ); return utils.makeArray( imgs ); } // -------------------------- LazyLoader -------------------------- // /** * class to handle loading images * @param {Image} img - Image element * @param {Flickity} flickity - Flickity instance */ function LazyLoader( img, flickity ) { this.img = img; this.flickity = flickity; this.load(); } LazyLoader.prototype.handleEvent = utils.handleEvent; LazyLoader.prototype.load = function() { this.img.addEventListener( 'load', this ); this.img.addEventListener( 'error', this ); // get src & srcset var src = this.img.getAttribute('data-flickity-lazyload') || this.img.getAttribute('data-flickity-lazyload-src'); var srcset = this.img.getAttribute('data-flickity-lazyload-srcset'); // set src & serset this.img.src = src; if ( srcset ) { this.img.setAttribute( 'srcset', srcset ); } // remove attr this.img.removeAttribute('data-flickity-lazyload'); this.img.removeAttribute('data-flickity-lazyload-src'); this.img.removeAttribute('data-flickity-lazyload-srcset'); }; LazyLoader.prototype.onload = function( event ) { this.complete( event, 'flickity-lazyloaded' ); }; LazyLoader.prototype.onerror = function( event ) { this.complete( event, 'flickity-lazyerror' ); }; LazyLoader.prototype.complete = function( event, className ) { // unbind events this.img.removeEventListener( 'load', this ); this.img.removeEventListener( 'error', this ); var cell = this.flickity.getParentCell( this.img ); var cellElem = cell && cell.element; this.flickity.cellSizeChange( cellElem ); this.img.classList.add( className ); this.flickity.dispatchEvent( 'lazyLoad', event, cellElem ); }; // ----- ----- // Flickity.LazyLoader = LazyLoader; return Flickity; } ) ); /*! * Flickity v2.2.2 * Touch, responsive, flickable carousels * * Licensed GPLv3 for open source use * or Flickity Commercial License for commercial use * * https://flickity.metafizzy.co * Copyright 2015-2021 Metafizzy */ ( function( window, factory ) { // universal module definition if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity/js/index',[ './flickity', './drag', './prev-next-button', './page-dots', './player', './add-remove-cell', './lazyload', ], factory ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( require('./flickity'), require('./drag'), require('./prev-next-button'), require('./page-dots'), require('./player'), require('./add-remove-cell'), require('./lazyload') ); } } )( window, function factory( Flickity ) { return Flickity; } ); /*! * Flickity asNavFor v2.0.2 * enable asNavFor for Flickity */ /*jshint browser: true, undef: true, unused: true, strict: true*/ ( function( window, factory ) { // universal module definition /*jshint strict: false */ /*globals define, module, require */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'flickity-as-nav-for/as-nav-for',[ 'flickity/js/index', 'fizzy-ui-utils/utils' ], factory ); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( require('flickity'), require('fizzy-ui-utils') ); } else { // browser global window.Flickity = factory( window.Flickity, window.fizzyUIUtils ); } }( window, function factory( Flickity, utils ) { // -------------------------- asNavFor prototype -------------------------- // // Flickity.defaults.asNavFor = null; Flickity.createMethods.push('_createAsNavFor'); var proto = Flickity.prototype; proto._createAsNavFor = function() { this.on( 'activate', this.activateAsNavFor ); this.on( 'deactivate', this.deactivateAsNavFor ); this.on( 'destroy', this.destroyAsNavFor ); var asNavForOption = this.options.asNavFor; if ( !asNavForOption ) { return; } // HACK do async, give time for other flickity to be initalized var _this = this; setTimeout( function initNavCompanion() { _this.setNavCompanion( asNavForOption ); }); }; proto.setNavCompanion = function( elem ) { elem = utils.getQueryElement( elem ); var companion = Flickity.data( elem ); // stop if no companion or companion is self if ( !companion || companion == this ) { return; } this.navCompanion = companion; // companion select var _this = this; this.onNavCompanionSelect = function() { _this.navCompanionSelect(); }; companion.on( 'select', this.onNavCompanionSelect ); // click this.on( 'staticClick', this.onNavStaticClick ); this.navCompanionSelect( true ); }; proto.navCompanionSelect = function( isInstant ) { // wait for companion & selectedCells first. #8 var companionCells = this.navCompanion && this.navCompanion.selectedCells; if ( !companionCells ) { return; } // select slide that matches first cell of slide var selectedCell = companionCells[0]; var firstIndex = this.navCompanion.cells.indexOf( selectedCell ); var lastIndex = firstIndex + companionCells.length - 1; var selectIndex = Math.floor( lerp( firstIndex, lastIndex, this.navCompanion.cellAlign ) ); this.selectCell( selectIndex, false, isInstant ); // set nav selected class this.removeNavSelectedElements(); // stop if companion has more cells than this one if ( selectIndex >= this.cells.length ) { return; } var selectedCells = this.cells.slice( firstIndex, lastIndex + 1 ); this.navSelectedElements = selectedCells.map( function( cell ) { return cell.element; }); this.changeNavSelectedClass('add'); }; function lerp( a, b, t ) { return ( b - a ) * t + a; } proto.changeNavSelectedClass = function( method ) { this.navSelectedElements.forEach( function( navElem ) { navElem.classList[ method ]('is-nav-selected'); }); }; proto.activateAsNavFor = function() { this.navCompanionSelect( true ); }; proto.removeNavSelectedElements = function() { if ( !this.navSelectedElements ) { return; } this.changeNavSelectedClass('remove'); delete this.navSelectedElements; }; proto.onNavStaticClick = function( event, pointer, cellElement, cellIndex ) { if ( typeof cellIndex == 'number' ) { this.navCompanion.selectCell( cellIndex ); } }; proto.deactivateAsNavFor = function() { this.removeNavSelectedElements(); }; proto.destroyAsNavFor = function() { if ( !this.navCompanion ) { return; } this.navCompanion.off( 'select', this.onNavCompanionSelect ); this.off( 'staticClick', this.onNavStaticClick ); delete this.navCompanion; }; // ----- ----- // return Flickity; })); /*! * imagesLoaded v4.1.4 * JavaScript is all like "You images are done yet or what?" * MIT License */ ( function( window, factory ) { 'use strict'; // universal module definition /*global define: false, module: false, require: false */ if ( typeof define == 'function' && define.amd ) { // AMD define( 'imagesloaded/imagesloaded',[ 'ev-emitter/ev-emitter' ], function( EvEmitter ) { return factory( window, EvEmitter ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('ev-emitter') ); } else { // browser global window.imagesLoaded = factory( window, window.EvEmitter ); } })( typeof window !== 'undefined' ? window : this, // -------------------------- factory -------------------------- // function factory( window, EvEmitter ) { var $ = window.jQuery; var console = window.console; // -------------------------- helpers -------------------------- // // extend objects function extend( a, b ) { for ( var prop in b ) { a[ prop ] = b[ prop ]; } return a; } var arraySlice = Array.prototype.slice; // turn element or nodeList into an array function makeArray( obj ) { if ( Array.isArray( obj ) ) { // use object if already an array return obj; } var isArrayLike = typeof obj == 'object' && typeof obj.length == 'number'; if ( isArrayLike ) { // convert nodeList to array return arraySlice.call( obj ); } // array of single index return [ obj ]; } // -------------------------- imagesLoaded -------------------------- // /** * @param {Array, Element, NodeList, String} elem * @param {Object or Function} options - if function, use as callback * @param {Function} onAlways - callback function */ function ImagesLoaded( elem, options, onAlways ) { // coerce ImagesLoaded() without new, to be new ImagesLoaded() if ( !( this instanceof ImagesLoaded ) ) { return new ImagesLoaded( elem, options, onAlways ); } // use elem as selector string var queryElem = elem; if ( typeof elem == 'string' ) { queryElem = document.querySelectorAll( elem ); } // bail if bad element if ( !queryElem ) { console.error( 'Bad element for imagesLoaded ' + ( queryElem || elem ) ); return; } this.elements = makeArray( queryElem ); this.options = extend( {}, this.options ); // shift arguments if no options set if ( typeof options == 'function' ) { onAlways = options; } else { extend( this.options, options ); } if ( onAlways ) { this.on( 'always', onAlways ); } this.getImages(); if ( $ ) { // add jQuery Deferred object this.jqDeferred = new $.Deferred(); } // HACK check async to allow time to bind listeners setTimeout( this.check.bind( this ) ); } ImagesLoaded.prototype = Object.create( EvEmitter.prototype ); ImagesLoaded.prototype.options = {}; ImagesLoaded.prototype.getImages = function() { this.images = []; // filter & find items if we have an item selector this.elements.forEach( this.addElementImages, this ); }; /** * @param {Node} element */ ImagesLoaded.prototype.addElementImages = function( elem ) { // filter siblings if ( elem.nodeName == 'IMG' ) { this.addImage( elem ); } // get background image on element if ( this.options.background === true ) { this.addElementBackgroundImages( elem ); } // find children // no non-element nodes, #143 var nodeType = elem.nodeType; if ( !nodeType || !elementNodeTypes[ nodeType ] ) { return; } var childImgs = elem.querySelectorAll('img'); // concat childElems to filterFound array for ( var i=0; i < childImgs.length; i++ ) { var img = childImgs[i]; this.addImage( img ); } // get child background images if ( typeof this.options.background == 'string' ) { var children = elem.querySelectorAll( this.options.background ); for ( i=0; i < children.length; i++ ) { var child = children[i]; this.addElementBackgroundImages( child ); } } }; var elementNodeTypes = { 1: true, 9: true, 11: true }; ImagesLoaded.prototype.addElementBackgroundImages = function( elem ) { var style = getComputedStyle( elem ); if ( !style ) { // Firefox returns null if in a hidden iframe https://bugzil.la/548397 return; } // get url inside url("...") var reURL = /url\((['"])?(.*?)\1\)/gi; var matches = reURL.exec( style.backgroundImage ); while ( matches !== null ) { var url = matches && matches[2]; if ( url ) { this.addBackground( url, elem ); } matches = reURL.exec( style.backgroundImage ); } }; /** * @param {Image} img */ ImagesLoaded.prototype.addImage = function( img ) { var loadingImage = new LoadingImage( img ); this.images.push( loadingImage ); }; ImagesLoaded.prototype.addBackground = function( url, elem ) { var background = new Background( url, elem ); this.images.push( background ); }; ImagesLoaded.prototype.check = function() { var _this = this; this.progressedCount = 0; this.hasAnyBroken = false; // complete if no images if ( !this.images.length ) { this.complete(); return; } function onProgress( image, elem, message ) { // HACK - Chrome triggers event before object properties have changed. #83 setTimeout( function() { _this.progress( image, elem, message ); }); } this.images.forEach( function( loadingImage ) { loadingImage.once( 'progress', onProgress ); loadingImage.check(); }); }; ImagesLoaded.prototype.progress = function( image, elem, message ) { this.progressedCount++; this.hasAnyBroken = this.hasAnyBroken || !image.isLoaded; // progress event this.emitEvent( 'progress', [ this, image, elem ] ); if ( this.jqDeferred && this.jqDeferred.notify ) { this.jqDeferred.notify( this, image ); } // check if completed if ( this.progressedCount == this.images.length ) { this.complete(); } if ( this.options.debug && console ) { console.log( 'progress: ' + message, image, elem ); } }; ImagesLoaded.prototype.complete = function() { var eventName = this.hasAnyBroken ? 'fail' : 'done'; this.isComplete = true; this.emitEvent( eventName, [ this ] ); this.emitEvent( 'always', [ this ] ); if ( this.jqDeferred ) { var jqMethod = this.hasAnyBroken ? 'reject' : 'resolve'; this.jqDeferred[ jqMethod ]( this ); } }; // -------------------------- -------------------------- // function LoadingImage( img ) { this.img = img; } LoadingImage.prototype = Object.create( EvEmitter.prototype ); LoadingImage.prototype.check = function() { // If complete is true and browser supports natural sizes, // try to check for image status manually. var isComplete = this.getIsImageComplete(); if ( isComplete ) { // report based on naturalWidth this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); return; } // If none of the checks above matched, simulate loading on detached element. this.proxyImage = new Image(); this.proxyImage.addEventListener( 'load', this ); this.proxyImage.addEventListener( 'error', this ); // bind to image as well for Firefox. #191 this.img.addEventListener( 'load', this ); this.img.addEventListener( 'error', this ); this.proxyImage.src = this.img.src; }; LoadingImage.prototype.getIsImageComplete = function() { // check for non-zero, non-undefined naturalWidth // fixes Safari+InfiniteScroll+Masonry bug infinite-scroll#671 return this.img.complete && this.img.naturalWidth; }; LoadingImage.prototype.confirm = function( isLoaded, message ) { this.isLoaded = isLoaded; this.emitEvent( 'progress', [ this, this.img, message ] ); }; // ----- events ----- // // trigger specified handler for event type LoadingImage.prototype.handleEvent = function( event ) { var method = 'on' + event.type; if ( this[ method ] ) { this[ method ]( event ); } }; LoadingImage.prototype.onload = function() { this.confirm( true, 'onload' ); this.unbindEvents(); }; LoadingImage.prototype.onerror = function() { this.confirm( false, 'onerror' ); this.unbindEvents(); }; LoadingImage.prototype.unbindEvents = function() { this.proxyImage.removeEventListener( 'load', this ); this.proxyImage.removeEventListener( 'error', this ); this.img.removeEventListener( 'load', this ); this.img.removeEventListener( 'error', this ); }; // -------------------------- Background -------------------------- // function Background( url, element ) { this.url = url; this.element = element; this.img = new Image(); } // inherit LoadingImage prototype Background.prototype = Object.create( LoadingImage.prototype ); Background.prototype.check = function() { this.img.addEventListener( 'load', this ); this.img.addEventListener( 'error', this ); this.img.src = this.url; // check if image is already complete var isComplete = this.getIsImageComplete(); if ( isComplete ) { this.confirm( this.img.naturalWidth !== 0, 'naturalWidth' ); this.unbindEvents(); } }; Background.prototype.unbindEvents = function() { this.img.removeEventListener( 'load', this ); this.img.removeEventListener( 'error', this ); }; Background.prototype.confirm = function( isLoaded, message ) { this.isLoaded = isLoaded; this.emitEvent( 'progress', [ this, this.element, message ] ); }; // -------------------------- jQuery -------------------------- // ImagesLoaded.makeJQueryPlugin = function( jQuery ) { jQuery = jQuery || window.jQuery; if ( !jQuery ) { return; } // set local variable $ = jQuery; // $().imagesLoaded() $.fn.imagesLoaded = function( options, callback ) { var instance = new ImagesLoaded( this, options, callback ); return instance.jqDeferred.promise( $(this) ); }; }; // try making plugin ImagesLoaded.makeJQueryPlugin(); // -------------------------- -------------------------- // return ImagesLoaded; }); /*! * Flickity imagesLoaded v2.0.0 * enables imagesLoaded option for Flickity */ /*jshint browser: true, strict: true, undef: true, unused: true */ ( function( window, factory ) { // universal module definition /*jshint strict: false */ /*globals define, module, require */ if ( typeof define == 'function' && define.amd ) { // AMD define( [ 'flickity/js/index', 'imagesloaded/imagesloaded' ], function( Flickity, imagesLoaded ) { return factory( window, Flickity, imagesLoaded ); }); } else if ( typeof module == 'object' && module.exports ) { // CommonJS module.exports = factory( window, require('flickity'), require('imagesloaded') ); } else { // browser global window.Flickity = factory( window, window.Flickity, window.imagesLoaded ); } }( window, function factory( window, Flickity, imagesLoaded ) { 'use strict'; Flickity.createMethods.push('_createImagesLoaded'); var proto = Flickity.prototype; proto._createImagesLoaded = function() { this.on( 'activate', this.imagesLoaded ); }; proto.imagesLoaded = function() { if ( !this.options.imagesLoaded ) { return; } var _this = this; function onImagesLoadedProgress( instance, image ) { var cell = _this.getParentCell( image.img ); _this.cellSizeChange( cell && cell.element ); if ( !_this.options.freeScroll ) { _this.positionSliderAtSelected(); } } imagesLoaded( this.slider ).on( 'progress', onImagesLoadedProgress ); }; return Flickity; })); </script>