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