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