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