1/*! 2 * Flickity PACKAGED v0.1.0 3 * Touch, responsive, flickable galleries 4 * http://flickity.metafizzy.co 5 * Copyright 2015 Metafizzy 6 */ 7 8/** 9 * Bridget makes jQuery widgets 10 * v1.1.0 11 * MIT license 12 */ 13 14( function( window ) { 15 16 17 18// -------------------------- utils -------------------------- // 19 20var slice = Array.prototype.slice; 21 22function noop() {} 23 24// -------------------------- definition -------------------------- // 25 26function defineBridget( $ ) { 27 28// bail if no jQuery 29if ( !$ ) { 30 return; 31} 32 33// -------------------------- addOptionMethod -------------------------- // 34 35/** 36 * adds option method -> $().plugin('option', {...}) 37 * @param {Function} PluginClass - constructor class 38 */ 39function addOptionMethod( PluginClass ) { 40 // don't overwrite original option method 41 if ( PluginClass.prototype.option ) { 42 return; 43 } 44 45 // option setter 46 PluginClass.prototype.option = function( opts ) { 47 // bail out if not an object 48 if ( !$.isPlainObject( opts ) ){ 49 return; 50 } 51 this.options = $.extend( true, this.options, opts ); 52 }; 53} 54 55// -------------------------- plugin bridge -------------------------- // 56 57// helper function for logging errors 58// $.error breaks jQuery chaining 59var logError = typeof console === 'undefined' ? noop : 60 function( message ) { 61 console.error( message ); 62 }; 63 64/** 65 * jQuery plugin bridge, access methods like $elem.plugin('method') 66 * @param {String} namespace - plugin name 67 * @param {Function} PluginClass - constructor class 68 */ 69function bridge( namespace, PluginClass ) { 70 // add to jQuery fn namespace 71 $.fn[ namespace ] = function( options ) { 72 if ( typeof options === 'string' ) { 73 // call plugin method when first argument is a string 74 // get arguments for method 75 var args = slice.call( arguments, 1 ); 76 77 for ( var i=0, len = this.length; i < len; i++ ) { 78 var elem = this[i]; 79 var instance = $.data( elem, namespace ); 80 if ( !instance ) { 81 logError( "cannot call methods on " + namespace + " prior to initialization; " + 82 "attempted to call '" + options + "'" ); 83 continue; 84 } 85 if ( !$.isFunction( instance[options] ) || options.charAt(0) === '_' ) { 86 logError( "no such method '" + options + "' for " + namespace + " instance" ); 87 continue; 88 } 89 90 // trigger method with arguments 91 var returnValue = instance[ options ].apply( instance, args ); 92 93 // break look and return first value if provided 94 if ( returnValue !== undefined ) { 95 return returnValue; 96 } 97 } 98 // return this if no return value 99 return this; 100 } else { 101 return this.each( function() { 102 var instance = $.data( this, namespace ); 103 if ( instance ) { 104 // apply options & init 105 instance.option( options ); 106 instance._init(); 107 } else { 108 // initialize new instance 109 instance = new PluginClass( this, options ); 110 $.data( this, namespace, instance ); 111 } 112 }); 113 } 114 }; 115 116} 117 118// -------------------------- bridget -------------------------- // 119 120/** 121 * converts a Prototypical class into a proper jQuery plugin 122 * the class must have a ._init method 123 * @param {String} namespace - plugin name, used in $().pluginName 124 * @param {Function} PluginClass - constructor class 125 */ 126$.bridget = function( namespace, PluginClass ) { 127 addOptionMethod( PluginClass ); 128 bridge( namespace, PluginClass ); 129}; 130 131return $.bridget; 132 133} 134 135// transport 136if ( typeof define === 'function' && define.amd ) { 137 // AMD 138 define( 'jquery-bridget/jquery.bridget',[ 'jquery' ], defineBridget ); 139} else if ( typeof exports === 'object' ) { 140 defineBridget( require('jquery') ); 141} else { 142 // get jquery from browser global 143 defineBridget( window.jQuery ); 144} 145 146})( window ); 147 148/*! 149 * classie v1.0.1 150 * class helper functions 151 * from bonzo https://github.com/ded/bonzo 152 * MIT license 153 * 154 * classie.has( elem, 'my-class' ) -> true/false 155 * classie.add( elem, 'my-new-class' ) 156 * classie.remove( elem, 'my-unwanted-class' ) 157 * classie.toggle( elem, 'my-class' ) 158 */ 159 160/*jshint browser: true, strict: true, undef: true, unused: true */ 161/*global define: false, module: false */ 162 163( function( window ) { 164 165 166 167// class helper functions from bonzo https://github.com/ded/bonzo 168 169function classReg( className ) { 170 return new RegExp("(^|\\s+)" + className + "(\\s+|$)"); 171} 172 173// classList support for class management 174// altho to be fair, the api sucks because it won't accept multiple classes at once 175var hasClass, addClass, removeClass; 176 177if ( 'classList' in document.documentElement ) { 178 hasClass = function( elem, c ) { 179 return elem.classList.contains( c ); 180 }; 181 addClass = function( elem, c ) { 182 elem.classList.add( c ); 183 }; 184 removeClass = function( elem, c ) { 185 elem.classList.remove( c ); 186 }; 187} 188else { 189 hasClass = function( elem, c ) { 190 return classReg( c ).test( elem.className ); 191 }; 192 addClass = function( elem, c ) { 193 if ( !hasClass( elem, c ) ) { 194 elem.className = elem.className + ' ' + c; 195 } 196 }; 197 removeClass = function( elem, c ) { 198 elem.className = elem.className.replace( classReg( c ), ' ' ); 199 }; 200} 201 202function toggleClass( elem, c ) { 203 var fn = hasClass( elem, c ) ? removeClass : addClass; 204 fn( elem, c ); 205} 206 207var classie = { 208 // full names 209 hasClass: hasClass, 210 addClass: addClass, 211 removeClass: removeClass, 212 toggleClass: toggleClass, 213 // short names 214 has: hasClass, 215 add: addClass, 216 remove: removeClass, 217 toggle: toggleClass 218}; 219 220// transport 221if ( typeof define === 'function' && define.amd ) { 222 // AMD 223 define( 'classie/classie',classie ); 224} else if ( typeof exports === 'object' ) { 225 // CommonJS 226 module.exports = classie; 227} else { 228 // browser global 229 window.classie = classie; 230} 231 232})( window ); 233 234/*! 235 * EventEmitter v4.2.11 - git.io/ee 236 * Unlicense - http://unlicense.org/ 237 * Oliver Caldwell - http://oli.me.uk/ 238 * @preserve 239 */ 240 241;(function () { 242 243 244 /** 245 * Class for managing events. 246 * Can be extended to provide event functionality in other classes. 247 * 248 * @class EventEmitter Manages event registering and emitting. 249 */ 250 function EventEmitter() {} 251 252 // Shortcuts to improve speed and size 253 var proto = EventEmitter.prototype; 254 var exports = this; 255 var originalGlobalValue = exports.EventEmitter; 256 257 /** 258 * Finds the index of the listener for the event in its storage array. 259 * 260 * @param {Function[]} listeners Array of listeners to search through. 261 * @param {Function} listener Method to look for. 262 * @return {Number} Index of the specified listener, -1 if not found 263 * @api private 264 */ 265 function indexOfListener(listeners, listener) { 266 var i = listeners.length; 267 while (i--) { 268 if (listeners[i].listener === listener) { 269 return i; 270 } 271 } 272 273 return -1; 274 } 275 276 /** 277 * Alias a method while keeping the context correct, to allow for over
vendor: 16,384 bytes, lines 277-680
277writing of target method. 278 * 279 * @param {String} name The name of the target method. 280 * @return {Function} The aliased method 281 * @api private 282 */ 283 function alias(name) { 284 return function aliasClosure() { 285 return this[name].apply(this, arguments); 286 }; 287 } 288 289 /** 290 * Returns the listener array for the specified event. 291 * Will initialise the event object and listener arrays if required. 292 * Will return an object if you use a regex search. The object contains keys for each matched event. So /ba[rz]/ might return an object containing bar and baz. But only if you have either defined them with defineEvent or added some listeners to them. 293 * Each property in the object response is an array of listener functions. 294 * 295 * @param {String|RegExp} evt Name of the event to return the listeners from. 296 * @return {Function[]|Object} All listener functions for the event. 297 */ 298 proto.getListeners = function getListeners(evt) { 299 var events = this._getEvents(); 300 var response; 301 var key; 302 303 // Return a concatenated array of all matching events if 304 // the selector is a regular expression. 305 if (evt instanceof RegExp) { 306 response = {}; 307 for (key in events) { 308 if (events.hasOwnProperty(key) && evt.test(key)) { 309 response[key] = events[key]; 310 } 311 } 312 } 313 else { 314 response = events[evt] || (events[evt] = []); 315 } 316 317 return response; 318 }; 319 320 /** 321 * Takes a list of listener objects and flattens it into a list of listener functions. 322 * 323 * @param {Object[]} listeners Raw listener objects. 324 * @return {Function[]} Just the listener functions. 325 */ 326 proto.flattenListeners = function flattenListeners(listeners) { 327 var flatListeners = []; 328 var i; 329 330 for (i = 0; i < listeners.length; i += 1) { 331 flatListeners.push(listeners[i].listener); 332 } 333 334 return flatListeners; 335 }; 336 337 /** 338 * Fetches the requested listeners via getListeners but will always return the results inside an object. This is mainly for internal use but others may find it useful. 339 * 340 * @param {String|RegExp} evt Name of the event to return the listeners from. 341 * @return {Object} All listener functions for an event in an object. 342 */ 343 proto.getListenersAsObject = function getListenersAsObject(evt) { 344 var listeners = this.getListeners(evt); 345 var response; 346 347 if (listeners instanceof Array) { 348 response = {}; 349 response[evt] = listeners; 350 } 351 352 return response || listeners; 353 }; 354 355 /** 356 * Adds a listener function to the specified event. 357 * The listener will not be added if it is a duplicate. 358 * If the listener returns true then it will be removed after it is called. 359 * If you pass a regular expression as the event name then the listener will be added to all events that match it. 360 * 361 * @param {String|RegExp} evt Name of the event to attach the listener to. 362 * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling. 363 * @return {Object} Current instance of EventEmitter for chaining. 364 */ 365 proto.addListener = function addListener(evt, listener) { 366 var listeners = this.getListenersAsObject(evt); 367 var listenerIsWrapped = typeof listener === 'object'; 368 var key; 369 370 for (key in listeners) { 371 if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) { 372 listeners[key].push(listenerIsWrapped ? listener : { 373 listener: listener, 374 once: false 375 }); 376 } 377 } 378 379 return this; 380 }; 381 382 /** 383 * Alias of addListener 384 */ 385 proto.on = alias('addListener'); 386 387 /** 388 * Semi-alias of addListener. It will add a listener that will be 389 * automatically removed after its first execution. 390 * 391 * @param {String|RegExp} evt Name of the event to attach the listener to. 392 * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling. 393 * @return {Object} Current instance of EventEmitter for chaining. 394 */ 395 proto.addOnceListener = function addOnceListener(evt, listener) { 396 return this.addListener(evt, { 397 listener: listener, 398 once: true 399 }); 400 }; 401 402 /** 403 * Alias of addOnceListener. 404 */ 405 proto.once = alias('addOnceListener'); 406 407 /** 408 * Defines an event name. This is required if you want to use a regex to add a listener to multiple events at once. If you don't do this then how do you expect it to know what event to add to? Should it just add to every possible match for a regex? No. That is scary and bad. 409 * You need to tell it what event names should be matched by a regex. 410 * 411 * @param {String} evt Name of the event to create. 412 * @return {Object} Current instance of EventEmitter for chaining. 413 */ 414 proto.defineEvent = function defineEvent(evt) { 415 this.getListeners(evt); 416 return this; 417 }; 418 419 /** 420 * Uses defineEvent to define multiple events. 421 * 422 * @param {String[]} evts An array of event names to define. 423 * @return {Object} Current instance of EventEmitter for chaining. 424 */ 425 proto.defineEvents = function defineEvents(evts) { 426 for (var i = 0; i < evts.length; i += 1) { 427 this.defineEvent(evts[i]); 428 } 429 return this; 430 }; 431 432 /** 433 * Removes a listener function from the specified event. 434 * When passed a regular expression as the event name, it will remove the listener from all events that match it. 435 * 436 * @param {String|RegExp} evt Name of the event to remove the listener from. 437 * @param {Function} listener Method to remove from the event. 438 * @return {Object} Current instance of EventEmitter for chaining. 439 */ 440 proto.removeListener = function removeListener(evt, listener) { 441 var listeners = this.getListenersAsObject(evt); 442 var index; 443 var key; 444 445 for (key in listeners) { 446 if (listeners.hasOwnProperty(key)) { 447 index = indexOfListener(listeners[key], listener); 448 449 if (index !== -1) { 450 listeners[key].splice(index, 1); 451 } 452 } 453 } 454 455 return this; 456 }; 457 458 /** 459 * Alias of removeListener 460 */ 461 proto.off = alias('removeListener'); 462 463 /** 464 * Adds listeners in bulk using the manipulateListeners method. 465 * If you pass an object as the second argument you can add to multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. You can also pass it an event name and an array of listeners to be added. 466 * You can also pass it a regular expression to add the array of listeners to all events that match it. 467 * Yeah, this function does quite a bit. That's probably a bad thing. 468 * 469 * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add to multiple events at once. 470 * @param {Function[]} [listeners] An optional array of listener functions to add. 471 * @return {Object} Current instance of EventEmitter for chaining. 472 */ 473 proto.addListeners = function addListeners(evt, listeners) { 474 // Pass through to manipulateListeners 475 return this.manipulateListeners(false, evt, listeners); 476 }; 477 478 /** 479 * Removes listeners in bulk using the manipulateListeners method. 480 * If you pass an object as the second argument you can remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. 481 * You can also pass it an event name and an array of listeners to be removed. 482 * You can also pass it a regular expression to remove the listeners from all events that match it. 483 * 484 * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to remove from multiple events at once. 485 * @param {Function[]} [listeners] An optional array of listener functions to remove. 486 * @return {Object} Current instance of EventEmitter for chaining. 487 */ 488 proto.removeListeners = function removeListeners(evt, listeners) { 489 // Pass through to manipulateListeners 490 return this.manipulateListeners(true, evt, listeners); 491 }; 492 493 /** 494 * Edits listeners in bulk. The addListeners and removeListeners methods both use this to do their job. You should really use those instead, this is a little lower level. 495 * The first argument will determine if the listeners are removed (true) or added (false). 496 * If you pass an object as the second argument you can add/remove from multiple events at once. The object should contain key value pairs of events and listeners or listener arrays. 497 * You can also pass it an event name and an array of listeners to be added/removed. 498 * You can also pass it a regular expression to manipulate the listeners of all events that match it. 499 * 500 * @param {Boolean} remove True if you want to remove listeners, false if you want to add. 501 * @param {String|Object|RegExp} evt An event name if you will pass an array of listeners next. An object if you wish to add/remove from multiple events at once. 502 * @param {Function[]} [listeners] An optional array of listener functions to add/remove. 503 * @return {Object} Current instance of EventEmitter for chaining. 504 */ 505 proto.manipulateListeners = function manipulateListeners(remove, evt, listeners) { 506 var i; 507 var value; 508 var single = remove ? this.removeListener : this.addListener; 509 var multiple = remove ? this.removeListeners : this.addListeners; 510 511 // If evt is an object then pass each of its properties to this method 512 if (typeof evt === 'object' && !(evt instanceof RegExp)) { 513 for (i in evt) { 514 if (evt.hasOwnProperty(i) && (value = evt[i])) { 515 // Pass the single listener straight through to the singular method 516 if (typeof value === 'function') { 517 single.call(this, i, value); 518 } 519 else { 520 // Otherwise pass back to the multiple function 521 multiple.call(this, i, value); 522 } 523 } 524 } 525 } 526 else { 527 // So evt must be a string 528 // And listeners must be an array of listeners 529 // Loop over it and pass each one to the multiple method 530 i = listeners.length; 531 while (i--) { 532 single.call(this, evt, listeners[i]); 533 } 534 } 535 536 return this; 537 }; 538 539 /** 540 * Removes all listeners from a specified event. 541 * If you do not specify an event then all listeners will be removed. 542 * That means every event will be emptied. 543 * You can also pass a regex to remove all events that match it. 544 * 545 * @param {String|RegExp} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed. 546 * @return {Object} Current instance of EventEmitter for chaining. 547 */ 548 proto.removeEvent = function removeEvent(evt) { 549 var type = typeof evt; 550 var events = this._getEvents(); 551 var key; 552 553 // Remove different things depending on the state of evt 554 if (type === 'string') { 555 // Remove all listeners for the specified event 556 delete events[evt]; 557 } 558 else if (evt instanceof RegExp) { 559 // Remove all events matching the regex. 560 for (key in events) { 561 if (events.hasOwnProperty(key) && evt.test(key)) { 562 delete events[key]; 563 } 564 } 565 } 566 else { 567 // Remove all listeners in all events 568 delete this._events; 569 } 570 571 return this; 572 }; 573 574 /** 575 * Alias of removeEvent. 576 * 577 * Added to mirror the node API. 578 */ 579 proto.removeAllListeners = alias('removeEvent'); 580 581 /** 582 * Emits an event of your choice. 583 * When emitted, every listener attached to that event will be executed. 584 * If you pass the optional argument array then those arguments will be passed to every listener upon execution. 585 * Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately. 586 * So they will not arrive within the array on the other side, they will be separate. 587 * You can also pass a regular expression to emit to all events that match it. 588 * 589 * @param {String|RegExp} evt Name of the event to emit and execute listeners for. 590 * @param {Array} [args] Optional array of arguments to be passed to each listener. 591 * @return {Object} Current instance of EventEmitter for chaining. 592 */ 593 proto.emitEvent = function emitEvent(evt, args) { 594 var listeners = this.getListenersAsObject(evt); 595 var listener; 596 var i; 597 var key; 598 var response; 599 600 for (key in listeners) { 601 if (listeners.hasOwnProperty(key)) { 602 i = listeners[key].length; 603 604 while (i--) { 605 // If the listener returns true then it shall be removed from the event 606 // The function is executed either with a basic call or an apply if there is an args array 607 listener = listeners[key][i]; 608 609 if (listener.once === true) { 610 this.removeListener(evt, listener.listener); 611 } 612 613 response = listener.listener.apply(this, args || []); 614 615 if (response === this._getOnceReturnValue()) { 616 this.removeListener(evt, listener.listener); 617 } 618 } 619 } 620 } 621 622 return this; 623 }; 624 625 /** 626 * Alias of emitEvent 627 */ 628 proto.trigger = alias('emitEvent'); 629 630 /** 631 * Subtly different from emitEvent in that it will pass its arguments on to the listeners, as opposed to taking a single array of arguments to pass on. 632 * As with emitEvent, you can pass a regex in place of the event name to emit to all events that match it. 633 * 634 * @param {String|RegExp} evt Name of the event to emit and execute listeners for. 635 * @param {...*} Optional additional arguments to be passed to each listener. 636 * @return {Object} Current instance of EventEmitter for chaining. 637 */ 638 proto.emit = function emit(evt) { 639 var args = Array.prototype.slice.call(arguments, 1); 640 return this.emitEvent(evt, args); 641 }; 642 643 /** 644 * Sets the current value to check against when executing listeners. If a 645 * listeners return value matches the one set here then it will be removed 646 * after execution. This value defaults to true. 647 * 648 * @param {*} value The new value to check for when executing listeners. 649 * @return {Object} Current instance of EventEmitter for chaining. 650 */ 651 proto.setOnceReturnValue = function setOnceReturnValue(value) { 652 this._onceReturnValue = value; 653 return this; 654 }; 655 656 /** 657 * Fetches the current value to check against when executing listeners. If 658 * the listeners return value matches this one then it should be removed 659 * automatically. It will return true by default. 660 * 661 * @return {*|Boolean} The current value to check for or the default, true. 662 * @api private 663 */ 664 proto._getOnceReturnValue = function _getOnceReturnValue() { 665 if (this.hasOwnProperty('_onceReturnValue')) { 666 return this._onceReturnValue; 667 } 668 else { 669 return true; 670 } 671 }; 672 673 /** 674 * Fetches the events object and creates one if required. 675 * 676 * @return {Object} The events storage object. 677 * @api private 678 */ 679 proto._getEvents = function _getEvents() { 680 return this._events || (this
680._events = {}); 681 }; 682 683 /** 684 * Reverts the global {@link EventEmitter} to its previous value and returns a reference to this version. 685 * 686 * @return {Function} Non conflicting EventEmitter class. 687 */ 688 EventEmitter.noConflict = function noConflict() { 689 exports.EventEmitter = originalGlobalValue; 690 return EventEmitter; 691 }; 692 693 // Expose the class either via AMD, CommonJS or the global object 694 if (typeof define === 'function' && define.amd) { 695 define('eventEmitter/EventEmitter',[],function () { 696 return EventEmitter; 697 }); 698 } 699 else if (typeof module === 'object' && module.exports){ 700 module.exports = EventEmitter; 701 } 702 else { 703 exports.EventEmitter = EventEmitter; 704 } 705}.call(this)); 706 707/*! 708 * eventie v1.0.5 709 * event binding helper 710 * eventie.bind( elem, 'click', myFn ) 711 * eventie.unbind( elem, 'click', myFn ) 712 * MIT license 713 */ 714 715/*jshint browser: true, undef: true, unused: true */ 716/*global define: false, module: false */ 717 718( function( window ) { 719 720 721 722var docElem = document.documentElement; 723 724var bind = function() {}; 725 726function getIEEvent( obj ) { 727 var event = window.event; 728 // add event.target 729 event.target = event.target || event.srcElement || obj; 730 return event; 731} 732 733if ( docElem.addEventListener ) { 734 bind = function( obj, type, fn ) { 735 obj.addEventListener( type, fn, false ); 736 }; 737} else if ( docElem.attachEvent ) { 738 bind = function( obj, type, fn ) { 739 obj[ type + fn ] = fn.handleEvent ? 740 function() { 741 var event = getIEEvent( obj ); 742 fn.handleEvent.call( fn, event ); 743 } : 744 function() { 745 var event = getIEEvent( obj ); 746 fn.call( obj, event ); 747 }; 748 obj.attachEvent( "on" + type, obj[ type + fn ] ); 749 }; 750} 751 752var unbind = function() {}; 753 754if ( docElem.removeEventListener ) { 755 unbind = function( obj, type, fn ) { 756 obj.removeEventListener( type, fn, false ); 757 }; 758} else if ( docElem.detachEvent ) { 759 unbind = function( obj, type, fn ) { 760 obj.detachEvent( "on" + type, obj[ type + fn ] ); 761 try { 762 delete obj[ type + fn ]; 763 } catch ( err ) { 764 // can't delete window object properties 765 obj[ type + fn ] = undefined; 766 } 767 }; 768} 769 770var eventie = { 771 bind: bind, 772 unbind: unbind 773}; 774 775// ----- module definition ----- // 776 777if ( typeof define === 'function' && define.amd ) { 778 // AMD 779 define( 'eventie/eventie',eventie ); 780} else if ( typeof exports === 'object' ) { 781 // CommonJS 782 module.exports = eventie; 783} else { 784 // browser global 785 window.eventie = eventie; 786} 787 788})( this ); 789 790/*! 791 * getStyleProperty v1.0.4 792 * original by kangax 793 * http://perfectionkills.com/feature-testing-css-properties/ 794 * MIT license 795 */ 796 797/*jshint browser: true, strict: true, undef: true */ 798/*global define: false, exports: false, module: false */ 799 800( function( window ) { 801 802 803 804var prefixes = 'Webkit Moz ms Ms O'.split(' '); 805var docElemStyle = document.documentElement.style; 806 807function getStyleProperty( propName ) { 808 if ( !propName ) { 809 return; 810 } 811 812 // test standard property first 813 if ( typeof docElemStyle[ propName ] === 'string' ) { 814 return propName; 815 } 816 817 // capitalize 818 propName = propName.charAt(0).toUpperCase() + propName.slice(1); 819 820 // test vendor specific properties 821 var prefixed; 822 for ( var i=0, len = prefixes.length; i < len; i++ ) { 823 prefixed = prefixes[i] + propName; 824 if ( typeof docElemStyle[ prefixed ] === 'string' ) { 825 return prefixed; 826 } 827 } 828} 829 830// transport 831if ( typeof define === 'function' && define.amd ) { 832 // AMD 833 define( 'get-style-property/get-style-property',[],function() { 834 return getStyleProperty; 835 }); 836} else if ( typeof exports === 'object' ) { 837 // CommonJS for Component 838 module.exports = getStyleProperty; 839} else { 840 // browser global 841 window.getStyleProperty = getStyleProperty; 842} 843 844})( window ); 845 846/*! 847 * getSize v1.2.2 848 * measure size of elements 849 * MIT license 850 */ 851 852/*jshint browser: true, strict: true, undef: true, unused: true */ 853/*global define: false, exports: false, require: false, module: false, console: false */ 854 855( function( window, undefined ) { 856 857 858 859// -------------------------- helpers -------------------------- // 860 861// get a number from a string, not a percentage 862function getStyleSize( value ) { 863 var num = parseFloat( value ); 864 // not a percent like '100%', and a number 865 var isValid = value.indexOf('%') === -1 && !isNaN( num ); 866 return isValid && num; 867} 868 869function noop() {} 870 871var logError = typeof console === 'undefined' ? noop : 872 function( message ) { 873 console.error( message ); 874 }; 875 876// -------------------------- measurements -------------------------- // 877 878var measurements = [ 879 'paddingLeft', 880 'paddingRight', 881 'paddingTop', 882 'paddingBottom', 883 'marginLeft', 884 'marginRight', 885 'marginTop', 886 'marginBottom', 887 'borderLeftWidth', 888 'borderRightWidth', 889 'borderTopWidth', 890 'borderBottomWidth' 891]; 892 893function getZeroSize() { 894 var size = { 895 width: 0, 896 height: 0, 897 innerWidth: 0, 898 innerHeight: 0, 899 outerWidth: 0, 900 outerHeight: 0 901 }; 902 for ( var i=0, len = measurements.length; i < len; i++ ) { 903 var measurement = measurements[i]; 904 size[ measurement ] = 0; 905 } 906 return size; 907} 908 909 910 911function defineGetSize( getStyleProperty ) { 912 913// -------------------------- setup -------------------------- // 914 915var isSetup = false;
916 917var getStyle, boxSizingProp, isBoxSizeOuter; 918 919/** 920 * setup vars and functions 921 * do it on initial getSize(), rather than on script load 922 * For Firefox bug https://bugzilla.mozilla.org/show_bug.cgi?id=548397 923 */ 924function setup() { 925 // setup once 926 if ( isSetup ) { 927 return; 928 } 929 isSetup = true; 930 931 var getComputedStyle = window.getComputedStyle; 932 getStyle = ( function() { 933 var getStyleFn = getComputedStyle ? 934 function( elem ) { 935 return getComputedStyle( elem, null ); 936 } : 937 function( elem ) { 938 return elem.currentStyle; 939 }; 940 941 return function getStyle( elem ) { 942 var style = getStyleFn( elem ); 943 if ( !style ) { 944 logError( 'Style returned ' + style + 945 '. Are you running this code in a hidden iframe on Firefox? ' + 946 'See http://bit.ly/getsizebug1' ); 947 } 948 return style; 949 }; 950 })(); 951 952 // -------------------------- box sizing -------------------------- // 953 954 boxSizingProp = getStyleProperty('boxSizing'); 955 956 /** 957 * WebKit measures the outer-width on style.width on border-box elems 958 * IE & Firefox measures the inner-width 959 */ 960 if ( boxSizingProp ) { 961 var div = document.createElement('div'); 962 div.style.width = '200px'; 963 div.style.padding = '1px 2px 3px 4px'; 964 div.style.borderStyle = 'solid'; 965 div.style.borderWidth = '1px 2px 3px 4px'; 966 div.style[ boxSizingProp ] = 'border-box'; 967 968 var body = document.body || document.documentElement; 969 body.appendChild( div ); 970 var style = getStyle( div ); 971 972 isBoxSizeOuter = getStyleSize( style.width ) === 200; 973 body.removeChild( div ); 974 } 975 976} 977 978// -------------------------- getSize -------------------------- // 979 980function getSize( elem ) { 981 setup(); 982 983 // use querySeletor if elem is string 984 if ( typeof elem === 'string' ) { 985 elem = document.querySelector( elem ); 986 } 987 988 // do not proceed on non-objects 989 if ( !elem || typeof elem !== 'object' || !elem.nodeType ) { 990 return; 991 } 992 993 var style = getStyle( elem ); 994 995 // if hidden, everything is 0 996 if ( style.display === 'none' ) { 997 return getZeroSize(); 998 } 999 1000 var size = {}; 1001 size.width = elem.offsetWidth; 1002 size.height = elem.offsetHeight; 1003 1004 var isBorderBox = size.isBorderBox = !!( boxSizingProp && 1005 style[ boxSizingProp ] && style[ boxSizingProp ] === 'border-box' ); 1006 1007 // get all measurements 1008 for ( var i=0, len = measurements.length; i < len; i++ ) { 1009 var measurement = measurements[i]; 1010 var value = style[ measurement ]; 1011 value = mungeNonPixel( elem, value ); 1012 var num = parseFloat( value ); 1013 // any 'auto', 'medium' value will be 0 1014 size[ measurement ] = !isNaN( num ) ? num : 0; 1015 } 1016 1017 var paddingWidth = size.paddingLeft + size.paddingRight; 1018 var paddingHeight = size.paddingTop + size.paddingBottom; 1019 var marginWidth = size.marginLeft + size.marginRight; 1020 var marginHeight = size.marginTop + size.marginBottom; 1021 var borderWidth = size.borderLeftWidth + size.borderRightWidth; 1022 var borderHeight = size.borderTopWidth + size.borderBottomWidth; 1023 1024 var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter; 1025 1026 // overwrite width and height if we can get it from style 1027 var styleWidth = getStyleSize( style.width ); 1028 if ( styleWidth !== false ) { 1029 size.width = styleWidth + 1030 // add padding and border unless it's already including it 1031 ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth ); 1032 } 1033 1034 var styleHeight = getStyleSize( style.height ); 1035 if ( styleHeight !== false ) { 1036 size.height = styleHeight + 1037 // add padding and border unless it's already including it 1038 ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight ); 1039 } 1040 1041 size.innerWidth = size.width - ( paddingWidth + borderWidth ); 1042 size.innerHeight = size.height - ( paddingHeight + borderHeight ); 1043 1044 size.outerWidth = size.width + marginWidth; 1045 size.outerHeight = size.height + marginHeight; 1046 1047 return size; 1048} 1049 1050// IE8 returns percent values, not pixels 1051// taken from jQuery's curCSS 1052function mungeNonPixel( elem, value ) { 1053 // IE8 and has percent value 1054 if ( window.getComputedStyle || value.indexOf('%') === -1 ) { 1055 return value; 1056 } 1057 var style = elem.style; 1058 // Remember the original values 1059 var left = style.left; 1060 var rs = elem.runtimeStyle; 1061 var rsLeft = rs && rs.left; 1062 1063 // Put in the new values to get a computed value out 1064 if ( rsLeft ) { 1065 rs.left = elem.currentStyle.left; 1066 } 1067 style.left = value; 1068 value = style.pixelLeft; 1069 1070 // Revert the changed values 1071 style.left = left; 1072 if ( rsLeft ) { 1073 rs.left = rsLeft; 1074 } 1075 1076 return value; 1077} 1078 1079return getSize; 1080 1081} 1082 1083// transport 1084if ( typeof define === 'function' && define.amd ) { 1085 // AMD for RequireJS 1086 define( 'get-size/get-size',[ 'get-style-property/get-style-property' ], defineGetSize ); 1087} else if ( typeof exports === 'object' ) { 1088 // CommonJS for Component 1089 module.exports = defineGetSize( require('desandro-get-style-property') ); 1090} else { 1091 // browser global 1092 window.getSize = defineGetSize( window.getStyleProperty ); 1093} 1094 1095})( window ); 1096 1097/*! 1098 * docReady v1.0.4 1099 * Cross browser DOMContentLoaded event emitter 1100 * MIT license 1101 */ 1102 1103/*jshint browser: true, strict: true, undef: true, unused: true*/ 1104/*global define: false, require: false, module: false */ 1105 1106( function( window ) { 1107 1108 1109 1110var document = window.document; 1111// collection of functions to be triggered on ready 1112var queue = []; 1113 1114function docReady( fn ) { 1115 // throw out non-functions 1116 if ( typeof fn !== 'function' ) { 1117 return; 1118 } 1119 1120 if ( docReady.isReady ) { 1121 // ready now, hit it 1122 fn(); 1123 } else { 1124 // queue function when ready 1125 queue.push( fn ); 1126 } 1127} 1128 1129docReady.isReady = false;
1130 1131// triggered on various doc ready events 1132function onReady( event ) { 1133 // bail if already triggered or IE8 document is not ready just yet 1134 var isIE8NotReady = event.type === 'readystatechange' && document.readyState !== 'complete'; 1135 if ( docReady.isReady || isIE8NotReady ) { 1136 return; 1137 } 1138 1139 trigger(); 1140} 1141 1142function trigger() { 1143 docReady.isReady = true; 1144 // process queue 1145 for ( var i=0, len = queue.length; i < len; i++ ) { 1146 var fn = queue[i]; 1147 fn(); 1148 } 1149} 1150 1151function defineDocReady( eventie ) { 1152 // trigger ready if page is ready 1153 if ( document.readyState === 'complete' ) { 1154 trigger(); 1155 } else { 1156 // listen for events 1157 eventie.bind( document, 'DOMContentLoaded', onReady ); 1158 eventie.bind( document, 'readystatechange', onReady ); 1159 eventie.bind( window, 'load', onReady ); 1160 } 1161 1162 return docReady; 1163} 1164 1165// transport 1166if ( typeof define === 'function' && define.amd ) { 1167 // AMD 1168 define( 'doc-ready/doc-ready',[ 'eventie/eventie' ], defineDocReady ); 1169} else if ( typeof exports === 'object' ) { 1170 module.exports = defineDocReady( require('eventie') ); 1171} else { 1172 // browser global 1173 window.docReady = defineDocReady( window.eventie ); 1174} 1175 1176})( window ); 1177 1178/** 1179 * matchesSelector v1.0.2 1180 * matchesSelector( element, '.selector' ) 1181 * MIT license 1182 */ 1183 1184/*jshint browser: true, strict: true, undef: true, unused: true */ 1185/*global define: false, module: false */ 1186 1187( function( ElemProto ) { 1188 1189 1190 1191 var matchesMethod = ( function() { 1192 // check un-prefixed 1193 if ( ElemProto.matchesSelector ) { 1194 return 'matchesSelector'; 1195 } 1196 // check vendor prefixes 1197 var prefixes = [ 'webkit', 'moz', 'ms', 'o' ]; 1198 1199 for ( var i=0, len = prefixes.length; i < len; i++ ) { 1200 var prefix = prefixes[i]; 1201 var method = prefix + 'MatchesSelector'; 1202 if ( ElemProto[ method ] ) { 1203 return method; 1204 } 1205 } 1206 })(); 1207 1208 // ----- match ----- // 1209 1210 function match( elem, selector ) { 1211 return elem[ matchesMethod ]( selector ); 1212 } 1213 1214 // ----- appendToFragment ----- // 1215 1216 function checkParent( elem ) { 1217 // not needed if already has parent 1218 if ( elem.parentNode ) { 1219 return; 1220 } 1221 var fragment = document.createDocumentFragment(); 1222 fragment.appendChild( elem ); 1223 } 1224 1225 // ----- query ----- // 1226 1227 // fall back to using QSA 1228 // thx @jonathantneal https://gist.github.com/3062955 1229 function query( elem, selector ) { 1230 // append to fragment if no parent 1231 checkParent( elem ); 1232 1233 // match elem with all selected elems of parent 1234 var elems = elem.parentNode.querySelectorAll( selector ); 1235 for ( var i=0, len = elems.length; i < len; i++ ) { 1236 // return true if match 1237 if ( elems[i] === elem ) { 1238 return true; 1239 } 1240 } 1241 // otherwise return false 1242 return false; 1243 } 1244 1245 // ----- matchChild ----- // 1246 1247 function matchChild( elem, selector ) { 1248 checkParent( elem ); 1249 return match( elem, selector ); 1250 } 1251 1252 // ----- matchesSelector ----- // 1253 1254 var matchesSelector; 1255 1256 if ( matchesMethod ) { 1257 // IE9 supports matchesSelector, but doesn't work on orphaned elems 1258 // check for that 1259 var div = document.createElement('div'); 1260 var supportsOrphans = match( div, 'div' ); 1261 matchesSelector = supportsOrphans ? match : matchChild; 1262 } else { 1263 matchesSelector = query; 1264 } 1265 1266 // transport 1267 if ( typeof define === 'function' && define.amd ) { 1268 // AMD 1269 define( 'matches-selector/matches-selector',[],function() { 1270 return matchesSelector; 1271 }); 1272 } else if ( typeof exports === 'object' ) { 1273 module.exports = matchesSelector; 1274 } 1275 else { 1276 // browser global 1277 window.matchesSelector = matchesSelector; 1278 } 1279 1280})( Element.prototype ); 1281 1282/** 1283 * utils 1284 */ 1285 1286( function( window, factory ) { 1287 1288 // universal module definition 1289 1290 if ( typeof define == 'function' && define.amd ) { 1291 // AMD 1292 define( 'flickity/js/utils',[ 1293 'doc-ready/doc-ready', 1294 'matches-selector/matches-selector' 1295 ], function( docReady, matchesSelector ) { 1296 return factory( window, docReady, matchesSelector ); 1297 }); 1298 } else if ( typeof exports == 'object' ) { 1299 // CommonJS 1300 module.exports = factory( 1301 window, 1302 require('doc-ready'), 1303 require('desandro-matches-selector') 1304 ); 1305 } else { 1306 // browser global 1307 window.utils = factory( 1308 window, 1309 window.docReady, 1310 window.matchesSelector 1311 ); 1312 } 1313 1314}( window, function factory( window, docReady, matchesSelector ) { 1315 1316 1317 1318var U = {}; 1319 1320// ----- extend ----- // 1321 1322// extends objects 1323U.extend = function( a, b ) { 1324 for ( var prop in b ) { 1325 a[ prop ] = b[ prop ]; 1326 } 1327 return a; 1328}; 1329 1330// ----- modulo ----- // 1331 1332U.modulo = function( num, div ) { 1333 return ( ( num % div ) + div ) % div; 1334}; 1335 1336// ----- isArray ----- // 1337 1338var objToString = Object.prototype.toString; 1339U.isArray = function( obj ) { 1340 return objToString.call( obj ) == '[object Array]'; 1341}; 1342 1343// ----- makeArray ----- // 1344 1345// turn element or nodeList into an array 1346U.makeArray = function( obj ) { 1347 var ary = []; 1348 if ( U.isArray( obj ) ) { 1349 // use object if already an array 1350 ary = obj; 1351 } else if ( obj && typeof obj.length == 'number' ) { 1352 // convert nodeList to array 1353 for ( var i=0, len = obj.length; i < len; i++ ) { 1354 ary.push( obj[i] ); 1355 } 1356 } else { 1357 // array of single index 1358 ary.push( obj ); 1359 } 1360 return ary; 1361}; 1362 1363// ----- indexOf ----- // 1364 1365// index of helper cause IE8 1366U.indexOf = Array.prototype.indexOf ? function( ary, obj ) { 1367 return ary.indexOf( obj ); 1368 } : function( ary, obj ) { 1369 for ( var i=0, len = ary.length; i < len; i++ ) { 1370 if ( ary[i] === obj ) { 1371 return i; 1372 } 1373 } 1374 return -1; 1375 }; 1376 1377// ----- removeFrom ----- // 1378 1379U.removeFrom = function( obj, ary ) { 1380 var index = U.indexOf( ary, obj ); 1381 if ( index != -1 ) { 1382 ary.splice( index, 1 ); 1383 } 1384}; 1385 1386// ----- isElement ----- // 1387 1388// http://stackoverflow.com/a/384380/182183 1389U.isElement = ( typeof HTMLElement == 'function' || typeof HTMLElement == 'object' ) ? 1390 function isElementDOM2( obj ) { 1391 return obj instanceof HTMLElement; 1392 } : 1393 function isElementQuirky( obj ) { 1394 return obj && typeof obj == 'object' && 1395 obj.nodeType == 1 && typeof obj.nodeName == 'string'; 1396 }; 1397 1398// ----- setText ----- // 1399 1400U.setText = ( function() { 1401 var setTextProperty; 1402 function setText( elem, text ) { 1403 // only check setTextProperty once 1404 setTextProperty = setTextProperty || ( document.documentElement.textContent !== undefined ? 'textContent' : 'innerText' ); 1405 elem[ setTextProperty ] = text; 1406 } 1407 return setText; 1408})(); 1409 1410// ----- getParent ----- // 1411 1412U.getParent = function( elem, selector ) { 1413 while ( elem != document.body ) { 1414 elem = elem.parentNode; 1415 if ( matchesSelector( elem, selector ) ) { 1416 return elem; 1417 } 1418 } 1419}; 1420 1421// ----- getQueryElement ----- // 1422 1423// use element as selector string 1424U.getQueryElement = function( elem ) { 1425 if ( typeof elem == 'string' ) { 1426 return document.querySelector( elem ); 1427 } 1428 return elem; 1429}; 1430 1431// ----- filterFindElements ----- // 1432 1433U.filterFindElements = function( elems, selector ) { 1434 // make array of elems 1435 elems = U.makeArray( elems ); 1436 var ffElems = []; 1437 1438 for ( var i=0, len = elems.length; i < len; i++ ) { 1439 var elem = elems[i]; 1440 // check that elem is an actual element 1441 if ( !U.isElement( elem ) ) { 1442 continue; 1443 } 1444 // filter & find items if we have a selector 1445 if ( selector ) { 1446 // filter siblings 1447 if ( matchesSelector( elem, selector ) ) { 1448 ffElems.push( elem ); 1449 } 1450 // find children 1451 var childElems = elem.querySelectorAll( selector ); 1452 // concat childElems to filterFound array 1453 for ( var j=0, jLen = childElems.length; j < jLen; j++ ) { 1454 ffElems.push( childElems[j] ); 1455 } 1456 } else { 1457 ffElems.push( elem ); 1458 } 1459 } 1460 1461 return ffElems; 1462}; 1463 1464// ----- debounceMethod ----- // 1465 1466U.debounceMethod = function( _class, methodName, threshold ) { 1467 // original method 1468 var method = _class.prototype[ methodName ]; 1469 var timeoutName = methodName + 'Timeout'; 1470 1471 _class.prototype[ methodName ] = function() { 1472 var timeout = this[ timeoutName ]; 1473 if ( timeout ) { 1474 clearTimeout( timeout ); 1475 } 1476 var args = arguments; 1477 1478 var _this = this; 1479 this[ timeoutName ] = setTimeout( function() { 1480 method.apply( _this, args ); 1481 delete _this[ timeoutName ]; 1482 }, threshold || 100 ); 1483 }; 1484}; 1485 1486// ----- htmlInit ----- // 1487 1488var jQuery = window.jQuery; 1489 1490// http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/ 1491function toDashed( str ) { 1492 return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) { 1493 return $1 + '-' + $2; 1494 }).toLowerCase(); 1495} 1496 1497 1498/** 1499 * allow user to initialize classes via .js-namespace class 1500 * htmlInit( Widget, 'widgetName' ) 1501 * options are parsed from data-namespace-option attribute 1502 */ 1503U.htmlInit = function( WidgetClass, namespace ) { 1504 docReady( function() { 1505 var dashedNamespace = toDashed( namespace ); 1506 var elems = document.querySelectorAll( '.js-' + dashedNamespace ); 1507 var dataAttr = 'data-' + dashedNamespace + '-options'; 1508 1509 for ( var i=0, len = elems.length; i < len; i++ ) { 1510 var elem = elems[i]; 1511 var attr = elem.getAttribute( dataAttr ); 1512 var options;
1513 try { 1514 options = attr && JSON.parse( attr ); 1515 } catch ( error ) { 1516 // log error, do not initialize 1517 if ( console ) { 1518 console.error( 'Error parsing ' + dataAttr + ' on ' + 1519 elem.nodeName.toLowerCase() + ( elem.id ? '#' + elem.id : '' ) + ': ' + 1520 error ); 1521 } 1522 continue; 1523 } 1524 // initialize 1525 var instance = new WidgetClass( elem, options ); 1526 // make available via $().data('layoutname') 1527 if ( jQuery ) { 1528 jQuery.data( elem, namespace, instance ); 1529 } 1530 } 1531 }); 1532}; 1533 1534// ----- ----- // 1535 1536return U; 1537 1538})); 1539 1540( function( window, factory ) { 1541 1542 // universal module definition 1543 1544 if ( typeof define == 'function' && define.amd ) { 1545 // AMD 1546 define( 'flickity/js/cell',[ 1547 'get-size/get-size' 1548 ], function( getSize ) { 1549 return factory( window, getSize ); 1550 }); 1551 } else if ( typeof exports == 'object' ) { 1552 // CommonJS 1553 module.exports = factory( 1554 window, 1555 require('get-size') 1556 ); 1557 } else { 1558 // browser global 1559 window.Flickity = window.Flickity || {}; 1560 window.Flickity.Cell = factory( 1561 window, 1562 window.getSize 1563 ); 1564 } 1565 1566}( window, function factory( window, getSize ) { 1567 1568 1569 1570function Cell( elem, parent ) { 1571 this.element = elem; 1572 this.parent = parent; 1573 1574 this.create(); 1575} 1576 1577Cell.prototype.create = function() { 1578 this.element.style.position = 'absolute'; 1579 this.x = 0; 1580 this.shift = 0; 1581}; 1582 1583Cell.prototype.destroy = function() { 1584 // reset style 1585 this.element.style.position = ''; 1586 var side = this.parent.originSide; 1587 this.element.style[ side ] = ''; 1588}; 1589 1590Cell.prototype.getSize = function() { 1591 this.size = getSize( this.element ); 1592}; 1593 1594Cell.prototype.setPosition = function( x ) { 1595 this.x = x; 1596 this.setDefaultTarget(); 1597 this.renderPosition( x ); 1598}; 1599 1600Cell.prototype.setDefaultTarget = function() { 1601 var marginProperty = this.parent.originSide == 'left' ? 'marginLeft' : 'marginRight'; 1602 this.target = this.x + this.size[ marginProperty ] + 1603 this.size.width * this.parent.cellAlign; 1604}; 1605 1606Cell.prototype.renderPosition = function( x ) { 1607 // render position of cell with in slider 1608 var side = this.parent.originSide; 1609 this.element.style[ side ] = this.parent.getPositionValue( x ); 1610}; 1611 1612/** 1613 * @param {Integer} factor - 0, 1, or -1 1614**/ 1615Cell.prototype.wrapShift = function( shift ) { 1616 this.shift = shift; 1617 this.renderPosition( this.x + this.parent.slideableWidth * shift ); 1618}; 1619 1620Cell.prototype.remove = function() { 1621 this.element.parentNode.removeChild( this.element ); 1622}; 1623 1624return Cell; 1625 1626})); 1627 1628// -------------------------- prev/next button -------------------------- // 1629 1630( function( window, factory ) { 1631 1632 // universal module definition 1633 1634 if ( typeof define == 'function' && define.amd ) { 1635 // AMD 1636 define( 'flickity/js/prev-next-button',[ 1637 './utils' 1638 ], function( utils ) { 1639 return factory( window, utils ); 1640 }); 1641 } else if ( typeof exports == 'object' ) { 1642 // CommonJS 1643 module.exports = factory( 1644 window, 1645 require('./utils') 1646 ); 1647 } else { 1648 // browser global 1649 window.Flickity = window.Flickity || {}; 1650 window.Flickity.PrevNextButton = factory( 1651 window, 1652 window.utils 1653 ); 1654 } 1655 1656}( window, function factory( window, utils ) { 1657 1658 1659 1660var svgURI = 'http://www.w3.org/2000/svg'; 1661 1662// -------------------------- inline SVG support -------------------------- // 1663 1664// only check on demand, not on script load 1665var supportsInlineSVG = ( function() { 1666 var supports; 1667 function checkSupport() { 1668 if ( supports !== undefined ) { 1669 return supports; 1670 } 1671 var div = document.createElement('div'); 1672 div.innerHTML = '<svg/>'; 1673 supports = ( div.firstChild && div.firstChild.namespaceURI ) == svgURI; 1674 return supports; 1675 } 1676 return checkSupport; 1677})(); 1678 1679function PrevNextButton( direction, parent ) { 1680 this.direction = direction; 1681 this.parent = parent; 1682 this._create(); 1683} 1684 1685PrevNextButton.prototype._create = function() { 1686 // properties 1687 this.isEnabled = true; 1688 this.isPrevious = this.direction == -1; 1689 var leftDirection = this.parent.options.rightToLeft ? 1 : -1; 1690 this.isLeft = this.direction == leftDirection; 1691 1692 this.element = document.createElement('button'); 1693 this.element.className = 'flickity-prev-next-button'; 1694 this.element.className += this.isPrevious ? ' previous' : ' next'; 1695 // create arrow 1696 if ( supportsInlineSVG() ) { 1697 var svg = this.createSVG(); 1698 this.element.appendChild( svg ); 1699 } else { 1700 // SVG not supported, set button text 1701 this.setArrowText(); 1702 this.element.className += ' no-svg'; 1703 } 1704 1705 // update on select 1706 var _this = this; 1707 this.onselect = function() { 1708 _this.update(); 1709 }; 1710 this.parent.on( 'select', this.onselect ); 1711 1712 // listen to click event 1713 this.element.onclick = function() { 1714 _this.onclick(); 1715 }; 1716 1717}; 1718 1719PrevNextButton.prototype.activate = function() { 1720 // add to DOM 1721 this.parent.element.appendChild( this.element ); 1722}; 1723 1724PrevNextButton.prototype.deactivate = function() { 1725 // remove from DOM 1726 this.parent.element.removeChild( this.element ); 1727}; 1728 1729PrevNextButton.prototype.createSVG = function() { 1730 var svg = document.createElementNS( svgURI, 'svg'); 1731 svg.setAttribute( 'viewBox', '0 0 100 100' ); 1732 var path = document.createElementNS( svgURI, 'path'); 1733 path.setAttribute( 'd', 'M 50,0 L 60,10 L 20,50 L 60,90 L 50,100 L 0,50 Z' ); 1734 path.setAttribute( 'class', 'arrow' ); 1735 // adjust arrow 1736 var arrowTransform = this.isLeft ? 'translate(15,0)' : 1737 'translate(85,100) rotate(180)'; 1738 path.setAttribute( 'transform', arrowTransform ); 1739 svg.appendChild( path ); 1740 return svg; 1741}; 1742 1743PrevNextButton.prototype.setArrowText = function() { 1744 var parentOptions = this.parent.options; 1745 var arrowText = this.isLeft ? parentOptions.leftArrowText : parentOptions.rightArrowText; 1746 utils.setText( this.element, arrowText ); 1747}; 1748 1749PrevNextButton.prototype.onclick = function() { 1750 if ( !this.isEnabled ) { 1751 return; 1752 } 1753 this.parent.uiChange(); 1754 var method = this.isPrevious ? 'previous' : 'next'; 1755 this.parent[ method ](); 1756}; 1757 1758PrevNextButton.prototype.enable = function() { 1759 if ( this.isEnabled ) { 1760 return; 1761 } 1762 this.element.disabled = false;
1763 this.isEnabled = true; 1764}; 1765 1766PrevNextButton.prototype.disable = function() { 1767 if ( !this.isEnabled ) { 1768 return; 1769 } 1770 this.element.disabled = true; 1771 this.isEnabled = false; 1772}; 1773 1774PrevNextButton.prototype.update = function() { 1775 if ( this.parent.options.wrapAround ) { 1776 this.enable(); 1777 return; 1778 } 1779 // index of first or last cell, if previous or next 1780 var boundIndex = this.isPrevious ? 0 : this.parent.cells.length - 1; 1781 var method = this.parent.selectedIndex == boundIndex ? 'disable' : 'enable'; 1782 this[ method ](); 1783}; 1784 1785return PrevNextButton; 1786 1787})); 1788 1789( function( window, factory ) { 1790 1791 // universal module definition 1792 1793 if ( typeof define == 'function' && define.amd ) { 1794 // AMD 1795 define( 'flickity/js/page-dots',[ 1796 'eventie/eventie', 1797 './utils' 1798 ], function( eventie, utils ) { 1799 return factory( window, eventie, utils ); 1800 }); 1801 } else if ( typeof exports == 'object' ) { 1802 // CommonJS 1803 module.exports = factory( 1804 window, 1805 require('eventie'), 1806 require('./utils') 1807 ); 1808 } else { 1809 // browser global 1810 window.Flickity = window.Flickity || {}; 1811 window.Flickity.PageDots = factory( 1812 window, 1813 window.eventie, 1814 window.utils 1815 ); 1816 } 1817 1818}( window, function factory( window, eventie, U ) { 1819 1820// -------------------------- PageDots -------------------------- // 1821 1822 1823 1824function PageDots( parent ) { 1825 this.parent = parent; 1826 this._create(); 1827} 1828 1829PageDots.prototype._create = function() { 1830 // create holder element 1831 this.holder = document.createElement('ol'); 1832 this.holder.className = 'flickity-page-dots'; 1833 // create dots, array of elements 1834 this.dots = []; 1835 // update on select 1836 var _this = this; 1837 this.onselect = function() { 1838 _this.updateSelected(); 1839 }; 1840 this.parent.on( 'select', this.onselect ); 1841 1842 eventie.bind( this.holder, 'click', this ); 1843}; 1844 1845PageDots.prototype.activate = function() { 1846 this.setDots(); 1847 this.updateSelected(); 1848 // add to DOM 1849 this.parent.element.appendChild( this.holder ); 1850}; 1851 1852PageDots.prototype.deactivate = function() { 1853 // remove from DOM 1854 this.parent.element.removeChild( this.holder ); 1855}; 1856 1857PageDots.prototype.setDots = function() { 1858 // get difference between number of cells and number of dots 1859 var delta = this.parent.cells.length - this.dots.length; 1860 if ( delta > 0 ) { 1861 this.addDots( delta ); 1862 } else if ( delta < 0 ) { 1863 this.removeDots( -delta ); 1864 } 1865}; 1866 1867PageDots.prototype.addDots = function( count ) { 1868 var fragment = document.createDocumentFragment(); 1869 var newDots = []; 1870 while ( count ) { 1871 var dot = document.createElement('li'); 1872 dot.className = 'dot'; 1873 fragment.appendChild( dot ); 1874 newDots.push( dot ); 1875 count--; 1876 } 1877 this.holder.appendChild( fragment ); 1878 this.dots = this.dots.concat( newDots ); 1879}; 1880 1881PageDots.prototype.removeDots = function( count ) { 1882 // remove from this.dots collection 1883 var removeDots = this.dots.splice( this.dots.length - count, count ); 1884 // remove from DOM 1885 for ( var i=0, len = removeDots.length; i < len; i++ ) { 1886 var dot = removeDots[i]; 1887 this.holder.removeChild( dot ); 1888 } 1889}; 1890 1891PageDots.prototype.updateSelected = function() { 1892 // remove selected class on previous 1893 if ( this.selectedDot ) { 1894 this.selectedDot.className = 'dot'; 1895 } 1896 1897 this.selectedDot = this.dots[ this.parent.selectedIndex ]; 1898 this.selectedDot.className = 'dot is-selected'; 1899}; 1900 1901// trigger handler methods for events 1902PageDots.prototype.handleEvent = function( event ) { 1903 var method = 'on' + event.type; 1904 if ( this[ method ] ) { 1905 this[ method ]( event ); 1906 } 1907}; 1908 1909PageDots.prototype.onclick = function( event ) { 1910 var target = event.target; 1911 // only care about dot clicks 1912 if ( target.nodeName != 'LI' ) { 1913 return; 1914 } 1915 1916 this.parent.uiChange(); 1917 var index = U.indexOf( this.dots, target ); 1918 this.parent.select( index ); 1919}; 1920 1921return PageDots; 1922 1923})); 1924 1925( function( window, factory ) { 1926 1927 // universal module definition 1928 1929 if ( typeof define == 'function' && define.amd ) { 1930 // AMD 1931 define( 'flickity/js/player',[],function() { 1932 return factory(); 1933 }); 1934 } else if ( typeof exports == 'object' ) { 1935 // CommonJS 1936 module.exports = factory(); 1937 } else { 1938 // browser global 1939 window.Flickity = window.Flickity || {}; 1940 window.Flickity.Player = factory(); 1941 } 1942 1943}( window, function factory() { 1944 1945 1946 1947function Player( parent ) { 1948 this.isPlaying = false;
1949 this.parent = parent; 1950} 1951 1952// start play 1953Player.prototype.play = function() { 1954 this.isPlaying = true; 1955 // playing kills pauses 1956 delete this.isPaused; 1957 // start ticking 1958 this.tick(); 1959}; 1960 1961Player.prototype.tick = function() { 1962 // do not tick if paused or not playing 1963 if ( !this.isPlaying || this.isPaused ) { 1964 return; 1965 } 1966 var time = this.parent.options.autoPlay; 1967 // default to 3 seconds 1968 time = typeof time == 'number' ? time : 3000; 1969 var _this = this; 1970 this.timeout = setTimeout( function() { 1971 _this.parent.next( true ); 1972 _this.tick(); 1973 }, time ); 1974}; 1975 1976Player.prototype.stop = function() { 1977 this.isPlaying = false; 1978 // stopping kills pauses 1979 delete this.isPaused; 1980 this.clear(); 1981}; 1982 1983Player.prototype.clear = function() { 1984 clearTimeout( this.timeout ); 1985}; 1986 1987Player.prototype.pause = function() { 1988 if ( this.isPlaying ) { 1989 this.isPaused = true; 1990 this.clear(); 1991 } 1992}; 1993 1994Player.prototype.unpause = function() { 1995 // re-start play if in unpaused state 1996 if ( this.isPaused ) { 1997 this.play(); 1998 } 1999}; 2000 2001return Player; 2002 2003})); 2004 2005( function( window, factory ) { 2006 2007 // universal module definition 2008 2009 if ( typeof define == 'function' && define.amd ) { 2010 // AMD 2011 define( 'flickity/js/unipointer',[ 2012 'eventie/eventie' 2013 ], function( eventie ) { 2014 return factory( window, eventie ); 2015 }); 2016 } else if ( typeof exports == 'object' ) { 2017 // CommonJS 2018 module.exports = factory( 2019 window, 2020 require('eventie') 2021 ); 2022 } else { 2023 // browser global 2024 window.Unipointer = factory( 2025 window, 2026 window.eventie 2027 ); 2028 } 2029 2030}( window, function factory( window, eventie ) { 2031 2032 2033 2034// ----- ----- // 2035 2036function noop() {} 2037 2038// -------------------------- Unipointer -------------------------- // 2039 2040function Unipointer() {} 2041 2042// trigger handler methods for events 2043Unipointer.prototype.handleEvent = function( event ) { 2044 var method = 'on' + event.type; 2045 if ( this[ method ] ) { 2046 this[ method ]( event ); 2047 } 2048}; 2049 2050// returns the touch that we're keeping track of 2051Unipointer.prototype.getTouch = function( touches ) { 2052 for ( var i=0, len = touches.length; i < len; i++ ) { 2053 var touch = touches[i]; 2054 if ( touch.identifier == this.pointerIdentifier ) { 2055 return touch; 2056 } 2057 } 2058}; 2059 2060// ----- bind start ----- // 2061 2062// works as unbinder, as you can .bindHandles( false ) to unbind 2063 2064/** 2065 * @param {Boolean} isBind - will unbind if falsey 2066 */ 2067Unipointer.prototype.bindHandles = function( isBind ) { 2068 var binder; 2069 if ( window.navigator.pointerEnabled ) { 2070 binder = this.bindPointer; 2071 } else if ( window.navigator.msPointerEnabled ) { 2072 binder = this.bindMSPointer; 2073 } else { 2074 binder = this.bindMouseTouch; 2075 } 2076 // munge isBind, default to true 2077 isBind = isBind === undefined ? true : !!isBind; 2078 for ( var i=0, len = this.handles.length; i < len; i++ ) { 2079 var handle = this.handles[i]; 2080 binder.call( this, handle, isBind ); 2081 } 2082}; 2083 2084Unipointer.prototype.bindPointer = function( handle, isBind ) { 2085 // W3C Pointer Events, IE11. See https://coderwall.com/p/mfreca 2086 var bindMethod = isBind ? 'bind' : 'unbind'; 2087 eventie[ bindMethod ]( handle, 'pointerdown', this ); 2088 // disable scrolling on the element 2089 handle.style.touchAction = isBind ? 'none' : ''; 2090}; 2091 2092Unipointer.prototype.bindMSPointer = function( handle, isBind ) { 2093 // IE10 Pointer Events 2094 var bindMethod = isBind ? 'bind' : 'unbind'; 2095 eventie[ bindMethod ]( handle, 'MSPointerDown', this ); 2096 // disable scrolling on the element 2097 handle.style.msTouchAction = isBind ? 'none' : ''; 2098}; 2099 2100Unipointer.prototype.bindMouseTouch = function( handle, isBind ) { 2101 // listen for both, for devices like Chrome Pixel 2102 // which has touch and mouse events 2103 var bindMethod = isBind ? 'bind' : 'unbind'; 2104 eventie[ bindMethod ]( handle, 'mousedown', this ); 2105 eventie[ bindMethod ]( handle, 'touchstart', this ); 2106 // TODO re-enable img.ondragstart when unbinding 2107 if ( isBind ) { 2108 disableImgOndragstart( handle ); 2109 } 2110}; 2111 2112// remove default dragging interaction on all images in IE8 2113// IE8 does its own drag thing on images, which messes stuff up 2114 2115function noDragStart() { 2116 return false;
2117} 2118 2119// TODO replace this with a IE8 test 2120var isIE8 = 'attachEvent' in document.documentElement; 2121 2122// IE8 only 2123var disableImgOndragstart = !isIE8 ? noop : function( handle ) { 2124 2125 if ( handle.nodeName == 'IMG' ) { 2126 handle.ondragstart = noDragStart; 2127 } 2128 2129 var images = handle.querySelectorAll('img'); 2130 for ( var i=0, len = images.length; i < len; i++ ) { 2131 var img = images[i]; 2132 img.ondragstart = noDragStart; 2133 } 2134}; 2135 2136// ----- start event ----- // 2137 2138Unipointer.prototype.onmousedown = function( event ) { 2139 // dismiss clicks from right or middle buttons 2140 var button = event.button; 2141 if ( button && ( button !== 0 && button !== 1 ) ) { 2142 return; 2143 } 2144 this._pointerDown( event, event ); 2145}; 2146 2147Unipointer.prototype.ontouchstart = function( event ) { 2148 this._pointerDown( event, event.changedTouches[0] ); 2149}; 2150 2151Unipointer.prototype.onMSPointerDown = 2152Unipointer.prototype.onpointerdown = function( event ) { 2153 this._pointerDown( event, event ); 2154}; 2155 2156// hash of events to be bound after start event 2157var postStartEvents = { 2158 mousedown: [ 'mousemove', 'mouseup' ], 2159 touchstart: [ 'touchmove', 'touchend', 'touchcancel' ], 2160 pointerdown: [ 'pointermove', 'pointerup', 'pointercancel' ], 2161 MSPointerDown: [ 'MSPointerMove', 'MSPointerUp', 'MSPointerCancel' ] 2162}; 2163 2164/** 2165 * pointer start 2166 * @param {Event} event 2167 * @param {Event or Touch} pointer 2168 */ 2169Unipointer.prototype._pointerDown = function( event, pointer ) { 2170 // dismiss other pointers 2171 if ( this.isPointerDown ) { 2172 return; 2173 } 2174 2175 this.isPointerDown = true; 2176 2177 // save pointer identifier to match up touch events 2178 this.pointerIdentifier = pointer.pointerId !== undefined ? 2179 // pointerId for pointer events, touch.indentifier for touch events 2180 pointer.pointerId : pointer.identifier; 2181 2182 // bind move and end events 2183 this._bindPostStartEvents({ 2184 // get proper events to match start event 2185 events: postStartEvents[ event.type ], 2186 // IE8 needs to be bound to document 2187 node: event.preventDefault ? window : document 2188 }); 2189 2190 this.pointerDown( event, pointer ); 2191 this.emitEvent( 'pointerDown', [ this, event, pointer ] ); 2192}; 2193 2194Unipointer.prototype.pointerDown = noop; 2195 2196// ----- bind/unbind ----- // 2197 2198Unipointer.prototype._bindPostStartEvents = function( args ) { 2199 for ( var i=0, len = args.events.length; i < len; i++ ) { 2200 var event = args.events[i]; 2201 eventie.bind( args.node, event, this ); 2202 } 2203 // save these arguments 2204 this._boundPointerEvents = args; 2205}; 2206 2207Unipointer.prototype._unbindPostStartEvents = function() { 2208 var args = this._boundPointerEvents; 2209 // IE8 can trigger dragEnd twice, check for _boundEvents 2210 if ( !args || !args.events ) { 2211 return; 2212 } 2213 2214 for ( var i=0, len = args.events.length; i < len; i++ ) { 2215 var event = args.events[i]; 2216 eventie.unbind( args.node, event, this ); 2217 } 2218 delete this._boundPointerEvents; 2219}; 2220 2221// ----- move event ----- // 2222 2223Unipointer.prototype.onmousemove = function( event ) { 2224 this._pointerMove( event, event ); 2225}; 2226 2227Unipointer.prototype.onMSPointerMove = 2228Unipointer.prototype.onpointermove = function( event ) { 2229 if ( event.pointerId == this.pointerIdentifier ) { 2230 this._pointerMove( event, event ); 2231 } 2232}; 2233 2234Unipointer.prototype.ontouchmove = function( event ) { 2235 var touch = this.getTouch( event.changedTouches ); 2236 if ( touch ) { 2237 this._pointerMove( event, touch ); 2238 } 2239}; 2240 2241/** 2242 * drag move 2243 * @param {Event} event 2244 * @param {Event or Touch} pointer 2245 */ 2246Unipointer.prototype._pointerMove = function( event, pointer ) { 2247 this.pointerMove( event, pointer ); 2248 this.emitEvent( 'pointerMove', [ this, event, pointer ] ); 2249}; 2250 2251Unipointer.prototype.pointerMove = noop; 2252 2253// ----- end event ----- // 2254 2255Unipointer.prototype.onmouseup = function( event ) { 2256 this._pointerUp( event, event ); 2257}; 2258 2259Unipointer.prototype.onMSPointerUp = 2260Unipointer.prototype.onpointerup = function( event ) { 2261 if ( event.pointerId == this.pointerIdentifier ) { 2262 this._pointerUp( event, event ); 2263 } 2264}; 2265 2266Unipointer.prototype.ontouchend = function( event ) { 2267 var touch = this.getTouch( event.changedTouches ); 2268 if ( touch ) { 2269 this._pointerUp( event, touch ); 2270 } 2271}; 2272 2273/** 2274 * drag end 2275 * @param {Event} event 2276 * @param {Event or Touch} pointer 2277 */ 2278Unipointer.prototype._pointerUp = function( event, pointer ) { 2279 this.isPointerDown = false;
2280 2281 delete this.pointerIdentifier; 2282 2283 // remove events 2284 this._unbindPostStartEvents(); 2285 2286 this.pointerUp( event, pointer ); 2287 this.emitEvent( 'pointerUp', [ this, event, pointer ] ); 2288}; 2289 2290Unipointer.prototype.pointerUp = noop; 2291 2292// ----- cancel event ----- // 2293 2294// coerce to end event 2295 2296Unipointer.prototype.onMSPointerCancel = 2297Unipointer.prototype.onpointercancel = function( event ) { 2298 if ( event.pointerId == this.pointerIdentifier ) { 2299 this._pointerUp( event, event ); 2300 } 2301}; 2302 2303Unipointer.prototype.ontouchcancel = function( event ) { 2304 var touch = this.getTouch( event.changedTouches ); 2305 this._pointerUp( event, touch ); 2306}; 2307 2308// ----- ----- // 2309 2310Unipointer.getPointerPoint = function( pointer ) { 2311 return { 2312 x: pointer.pageX !== undefined ? pointer.pageX : pointer.clientX, 2313 y: pointer.pageY !== undefined ? pointer.pageY : pointer.clientY 2314 }; 2315}; 2316 2317// fix for IE8 2318Unipointer.setPointerPoint = function( point, pointer ) { 2319 point.x = pointer.pageX !== undefined ? pointer.pageX : pointer.clientX; 2320 point.y = pointer.pageY !== undefined ? pointer.pageY : pointer.clientY; 2321}; 2322 2323// ----- ----- // 2324 2325return Unipointer; 2326 2327})); 2328 2329( function( window, factory ) { 2330 2331 // universal module definition 2332 2333 if ( typeof define == 'function' && define.amd ) { 2334 // AMD 2335 define( 'flickity/js/drag',[ 2336 './unipointer', 2337 'classie/classie', 2338 'eventie/eventie', 2339 './utils' 2340 ], function( Unipointer, eventie, utils ) { 2341 return factory( window, Unipointer, eventie, utils ); 2342 }); 2343 } else if ( typeof exports == 'object' ) { 2344 // CommonJS 2345 module.exports = factory( 2346 window, 2347 require('./unipointer'), 2348 require('desandro-classie'), 2349 require('eventie'), 2350 require('./utils') 2351 ); 2352 } else { 2353 // browser global 2354 window.Flickity = window.Flickity || {}; 2355 window.Flickity.dragPrototype = factory( 2356 window, 2357 window.Unipointer, 2358 window.classie, 2359 window.eventie, 2360 window.utils 2361 ); 2362 } 2363 2364}( window, function factory( window, Unipointer, classie, eventie, U ) { 2365 2366 2367 2368// handle IE8 prevent default 2369function preventDefaultEvent( event ) { 2370 if ( event.preventDefault ) { 2371 event.preventDefault(); 2372 } else { 2373 event.returnValue = false; 2374 } 2375} 2376 2377// -------------------------- drag prototype -------------------------- // 2378 2379var proto = {}; 2380U.extend( proto, Unipointer.prototype ); 2381 2382// -------------------------- -------------------------- // 2383 2384proto.bindDrag = function() { 2385 if ( !this.options.draggable ) { 2386 return; 2387 } 2388 this.handles = [ this.viewport ]; 2389 this.bindHandles(); 2390 // bind click handler 2391 eventie.bind( this.viewport, 'click', this ); 2392}; 2393 2394proto.unbindDrag = function() { 2395 if ( !this.options.draggable ) { 2396 return; 2397 } 2398 this.bindHandles( false ); 2399 // unbind click handler 2400 eventie.unbind( this.viewport, 'click', this ); 2401}; 2402 2403// -------------------------- pointer events -------------------------- // 2404 2405var allowTouchstartNodes = { 2406 INPUT: true, 2407 A: true, 2408 BUTTON: true 2409}; 2410 2411proto.pointerDown = function( event, pointer ) { 2412 var targetNodeName = event.target.nodeName; 2413 // HACK iOS, allow clicks on buttons, inputs, and links 2414 var isTouchstart = event.type == 'touchstart'; 2415 var isTouchstartNode = allowTouchstartNodes[ targetNodeName ]; 2416 if ( !isTouchstart || ( isTouchstart && !isTouchstartNode ) ) { 2417 preventDefaultEvent( event ); 2418 } 2419 // kludge to blur focused inputs in dragger 2420 var focused = document.activeElement; 2421 if ( focused && focused.blur && focused != this.element ) { 2422 focused.blur(); 2423 } 2424 // focus element, if its not an input 2425 if ( this.options.accessibility && targetNodeName != 'INPUT' ) { 2426 this.element.focus(); 2427 } 2428 // stop if it was moving 2429 this.velocity = 0; 2430 // track to see when dragging starts 2431 this.pointerDownPoint = Unipointer.getPointerPoint( pointer ); 2432 // stop auto play 2433 this.player.stop(); 2434 classie.add( this.viewport, 'is-pointer-down' ); 2435}; 2436 2437proto.pointerMove = function( event, pointer ) { 2438 var movePoint = Unipointer.getPointerPoint( pointer ); 2439 var dragMove = movePoint.x - this.pointerDownPoint.x; 2440 // start drag 2441 if ( !this.isDragging && Math.abs( dragMove ) > 3 ) { 2442 this.dragStart( event, pointer ); 2443 } 2444 2445 this.dragMove( movePoint, event, pointer ); 2446}; 2447 2448proto.pointerUp = function( event, pointer ) { 2449 if ( this.isDragging ) { 2450 this.dragEnd( event, pointer ); 2451 } else { 2452 // pointer didn't move enough for drag to start 2453 this.staticClick( event, pointer ); 2454 } 2455 classie.remove( this.viewport, 'is-pointer-down' ); 2456}; 2457 2458// -------------------------- dragging -------------------------- // 2459 2460proto.dragStart = function( event, pointer ) { 2461 this.isDragging = true; 2462 this.dragStartPoint = Unipointer.getPointerPoint( pointer ); 2463 this.dragStartPosition = this.x; 2464 this.startAnimation(); 2465 // prevent clicks
2466 this.isPreventingClicks = true; 2467 this.dispatchEvent( 'dragStart', event, [ pointer ] ); 2468}; 2469 2470proto.dragMove = function( movePoint, event, pointer ) { 2471 // do not drag if not dragging yet 2472 if ( !this.isDragging ) { 2473 return; 2474 } 2475 2476 this.previousDragX = this.x; 2477 2478 var movedX = movePoint.x - this.dragStartPoint.x; 2479 // reverse if right-to-left 2480 var direction = this.options.rightToLeft ? -1 : 1; 2481 this.x = this.dragStartPosition + movedX * direction; 2482 2483 if ( !this.options.wrapAround && this.cells.length ) { 2484 // slow drag 2485 var originBound = Math.max( -this.cells[0].target, this.dragStartPosition); 2486 this.x = this.x > originBound ? ( this.x - originBound ) * 0.5 + originBound : this.x; 2487 var endBound = Math.min( -this.getLastCell().target, this.dragStartPosition ); 2488 this.x = this.x < endBound ? ( this.x - endBound ) * 0.5 + endBound : this.x; 2489 } 2490 2491 this.previousDragMoveTime = this.dragMoveTime; 2492 this.dragMoveTime = new Date(); 2493 this.dispatchEvent( 'dragMove', event, [ pointer ] ); 2494}; 2495 2496proto.dragEnd = function( event, pointer ) { 2497 this.dragEndFlick(); 2498 if ( this.options.freeScroll ) { 2499 this.isFreeScrolling = true; 2500 } 2501 // set selectedIndex based on where flick will end up 2502 var index = this.dragEndRestingSelect(); 2503 2504 if ( this.options.freeScroll && !this.options.wrapAround ) { 2505 // if free-scroll & not wrap around 2506 // do not free-scroll if going outside of bounding cells 2507 // so bounding cells can attract slider, and keep it in bounds 2508 var restingX = this.getRestingPosition(); 2509 this.isFreeScrolling = -restingX > this.cells[0].target && 2510 -restingX < this.getLastCell().target; 2511 } else if ( !this.options.freeScroll && index == this.selectedIndex ) { 2512 // boost selection if selected index has not changed 2513 index += this.dragEndBoostSelect(); 2514 } 2515 // apply selection 2516 // TODO refactor this, selecting here feels weird 2517 this.select( index ); 2518 // set flags 2519 this.isDragging = false; 2520 // re-enable clicking async 2521 var _this = this; 2522 setTimeout( function() {
2523 delete _this.isPreventingClicks; 2524 }); 2525 2526 this.dispatchEvent( 'dragEnd', event, [ pointer ] ); 2527}; 2528 2529// apply velocity after dragging 2530proto.dragEndFlick = function() { 2531 if ( !isFinite( this.previousDragX ) ) { 2532 return; 2533 } 2534 // set slider velocity 2535 var timeDelta = this.dragMoveTime - this.previousDragMoveTime; 2536 // 60 frames per second, ideally 2537 // TODO, velocity should be in pixels per millisecond 2538 // currently in pixels per frame 2539 timeDelta /= 1000 / 60; 2540 var xDelta = this.x - this.previousDragX; 2541 this.velocity = xDelta / timeDelta; 2542 // reset 2543 delete this.previousDragX; 2544}; 2545 2546proto.dragEndRestingSelect = function() { 2547 var restingX = this.getRestingPosition(); 2548 // how far away from selected cell 2549 var distance = Math.abs( this.getCellDistance( -restingX, this.selectedIndex ) ); 2550 // get closet resting going up and going down 2551 var positiveResting = this._getClosestResting( restingX, distance, 1 ); 2552 var negativeResting = this._getClosestResting( restingX, distance, -1 ); 2553 // use closer resting for wrap-around 2554 var index = positiveResting.distance < negativeResting.distance ? 2555 positiveResting.index : negativeResting.index; 2556 return index; 2557}; 2558 2559/** 2560 * given resting X and distance to selected cell 2561 * get the distance and index of the closest cell 2562 * @param {Number} restingX - estimated post-flick resting position 2563 * @param {Number} distance - distance to selected cell 2564 * @param {Integer} increment - +1 or -1, going up or down 2565 * @returns {Object} - { distance: {Number}, index: {Integer} } 2566 */ 2567proto._getClosestResting = function( restingX, distance, increment ) { 2568 var index = this.selectedIndex; 2569 var minDistance = Infinity; 2570 var condition = this.options.contain && !this.options.wrapAround ? 2571 // if fit-content, keep going if distance is equal to minDistance 2572 function( d, md ) { return d <= md; } : function( d, md ) { return d < md; }; 2573 while ( condition( distance, minDistance ) ) { 2574 // measure distance to next cell 2575 index += increment; 2576 minDistance = distance; 2577 distance = this.getCellDistance( -restingX, index ); 2578 if ( distance === null ) { 2579 break; 2580 } 2581 distance = Math.abs( distance ); 2582 } 2583 return { 2584 distance: minDistance, 2585 // selected was previous index 2586 index: index - increment 2587 }; 2588}; 2589 2590/** 2591 * measure distance between x and a cell target 2592 * @param {Number} x 2593 * @param {Integer} index - cell index 2594 */ 2595proto.getCellDistance = function( x, index ) { 2596 var len = this.cells.length; 2597 var cellIndex = this.options.wrapAround ? U.modulo( index, len ) : index; 2598 var cell = this.cells[ cellIndex ]; 2599 if ( !cell ) { 2600 return null; 2601 } 2602 // add distance for wrap-around cells 2603 var wrap = this.options.wrapAround ? this.slideableWidth * Math.floor( index / len ) : 0; 2604 return x - ( cell.target + wrap ); 2605}; 2606 2607proto.dragEndBoostSelect = function() { 2608 var distance = this.getCellDistance( -this.x, this.selectedIndex ); 2609 if ( distance > 0 && this.velocity < -1 ) { 2610 // if moving towards the right, and positive velocity, and the next attractor 2611 return 1; 2612 } else if ( distance < 0 && this.velocity > 1 ) { 2613 // if moving towards the left, and negative velocity, and previous attractor 2614 return -1; 2615 } 2616 return 0; 2617}; 2618 2619// ----- onclick ----- // 2620 2621// handle all clicks and prevent clicks when dragging 2622proto.onclick = function( event ) {
2623 if ( this.isPreventingClicks ) { 2624 preventDefaultEvent( event ); 2625 } 2626}; 2627 2628// ----- staticClick ----- // 2629 2630// triggered after pointer down & up with no/tiny movement 2631proto.staticClick = function( event, pointer ) { 2632 // allow click in text input 2633 if ( event.target.nodeName == 'INPUT' && event.target.type == 'text' ) { 2634 event.target.focus(); 2635 } 2636 this.dispatchEvent( 'staticClick', event, [ pointer ] ); 2637}; 2638 2639// ----- ----- // 2640 2641return proto; 2642 2643})); 2644 2645( function( window, factory ) { 2646 2647 // universal module definition 2648 2649 if ( typeof define == 'function' && define.amd ) { 2650 // AMD 2651 define( 'flickity/js/animate',[ 2652 'get-style-property/get-style-property', 2653 './utils' 2654 ], function( getStyleProperty, utils ) { 2655 return factory( window, getStyleProperty, utils ); 2656 }); 2657 } else if ( typeof exports == 'object' ) { 2658 // CommonJS 2659 module.exports = factory( 2660 window, 2661 require('desandro-get-style-property'), 2662 require('./utils') 2663 ); 2664 } else { 2665 // browser global 2666 window.Flickity = window.Flickity || {}; 2667 window.Flickity.animatePrototype = factory( 2668 window, 2669 window.getStyleProperty, 2670 window.utils 2671 ); 2672 } 2673 2674}( window, function factory( window, getStyleProperty, U ) { 2675 2676 2677 2678// -------------------------- requestAnimationFrame -------------------------- // 2679 2680// https://gist.github.com/1866474 2681 2682var lastTime = 0; 2683var prefixes = 'webkit moz ms o'.split(' '); 2684// get unprefixed rAF and cAF, if present 2685var requestAnimationFrame = window.requestAnimationFrame; 2686var cancelAnimationFrame = window.cancelAnimationFrame; 2687// loop through vendor prefixes and get prefixed rAF and cAF 2688var prefix; 2689for( var i = 0; i < prefixes.length; i++ ) { 2690 if ( requestAnimationFrame && cancelAnimationFrame ) { 2691 break; 2692 } 2693 prefix = prefixes[i]; 2694 requestAnimationFrame = requestAnimationFrame || window[ prefix + 'RequestAnimationFrame' ]; 2695 cancelAnimationFrame = cancelAnimationFrame || window[ prefix + 'CancelAnimationFrame' ] || 2696 window[ prefix + 'CancelRequestAnimationFrame' ]; 2697} 2698 2699// fallback to setTimeout and clearTimeout if either request/cancel is not supported 2700if ( !requestAnimationFrame || !cancelAnimationFrame ) { 2701 requestAnimationFrame = function( callback ) { 2702 var currTime = new Date().getTime(); 2703 var timeToCall = Math.max( 0, 16 - ( currTime - lastTime ) ); 2704 var id = window.setTimeout( function() { 2705 callback( currTime + timeToCall ); 2706 }, timeToCall ); 2707 lastTime = currTime + timeToCall; 2708 return id; 2709 }; 2710 2711 cancelAnimationFrame = function( id ) { 2712 window.clearTimeout( id ); 2713 }; 2714} 2715 2716// -------------------------- animate -------------------------- // 2717 2718var proto = {}; 2719 2720proto.startAnimation = function() { 2721 if ( this.isAnimating ) { 2722 return; 2723 } 2724 2725 this.isAnimating = true; 2726 this.restingFrames = 0; 2727 this.animate(); 2728}; 2729 2730proto.animate = function() { 2731 this.applySelectedAttraction(); 2732 2733 var previousX = this.x; 2734 2735 this.updatePhysics(); 2736 this.positionSlider(); 2737 this.settle( previousX ); 2738 // animate next frame 2739 if ( this.isAnimating ) { 2740 var _this = this; 2741 requestAnimationFrame( function animateFrame() { 2742 _this.animate(); 2743 }); 2744 } 2745 2746 /** / 2747 // log animation frame rate 2748 var now = new Date(); 2749 if ( this.then ) { 2750 console.log( ~~( 1000 / (now-this.then)) + 'fps' ) 2751 } 2752 this.then = now; 2753 /**/ 2754}; 2755 2756 2757var transformProperty = getStyleProperty('transform'); 2758var is3d = !!getStyleProperty('perspective'); 2759 2760proto.positionSlider = function() { 2761 var x = this.x; 2762 // wrap position around 2763 if ( this.options.wrapAround ) { 2764 x = U.modulo( x, this.slideableWidth ); 2765 x = x - this.slideableWidth; 2766 this.shiftWrapCells( x ); 2767 } 2768 2769 x = x + this.cursorPosition; 2770 2771 // reverse if right-to-left and using transform 2772 x = this.options.rightToLeft && transformProperty ? -x : x; 2773 2774 var value = this.getPositionValue( x ); 2775 2776 if ( transformProperty ) { 2777 // use 3D tranforms for hardware acceleration on iOS 2778 // but use 2D when settled, for better font-rendering 2779 this.slider.style[ transformProperty ] = is3d && this.isAnimating ? 2780 'translate3d(' + value + ',0,0)' : 'translateX(' + value + ')'; 2781 } else { 2782 this.slider.style[ this.originSide ] = value; 2783 } 2784}; 2785 2786proto.positionSliderAtSelected = function() { 2787 var selectedCell = this.cells[ this.selectedIndex ]; 2788 this.x = -selectedCell.target; 2789 this.positionSlider(); 2790}; 2791 2792proto.getPositionValue = function( position ) { 2793 if ( this.options.percentPosition ) { 2794 // percent position, round to 2 digits, like 12.34% 2795 return ( Math.round( ( position / this.size.innerWidth ) * 10000 ) * 0.01 )+ '%'; 2796 } else { 2797 // pixel positioning 2798 return Math.round( position ) + 'px'; 2799 } 2800}; 2801 2802proto.settle = function( previousX ) { 2803 // keep track of frames where x hasn't moved 2804 if ( !this.isPointerDown && Math.round( this.x * 100 ) == Math.round( previousX * 100 ) ) { 2805 this.restingFrames++; 2806 } 2807 // stop animating if resting for 3 or more frames 2808 if ( this.restingFrames > 2 ) { 2809 this.isAnimating = false;
2810 delete this.isFreeScrolling; 2811 // render position with translateX when settled 2812 if ( is3d ) { 2813 this.positionSlider(); 2814 } 2815 this.dispatchEvent('settle'); 2816 } 2817}; 2818 2819proto.shiftWrapCells = function( x ) { 2820 // shift before cells 2821 var beforeGap = this.cursorPosition + x; 2822 this._shiftCells( this.beforeShiftCells, beforeGap, -1 ); 2823 // shift after cells 2824 var afterGap = this.size.innerWidth - ( x + this.slideableWidth + this.cursorPosition ); 2825 this._shiftCells( this.afterShiftCells, afterGap, 1 ); 2826}; 2827 2828proto._shiftCells = function( cells, gap, shift ) { 2829 for ( var i=0, len = cells.length; i < len; i++ ) { 2830 var cell = cells[i]; 2831 var cellShift = gap > 0 ? shift : 0; 2832 cell.wrapShift( cellShift ); 2833 gap -= cell.size.outerWidth; 2834 } 2835}; 2836 2837proto._unshiftCells = function( cells ) { 2838 if ( !cells || !cells.length ) { 2839 return; 2840 } 2841 for ( var i=0, len = cells.length; i < len; i++ ) { 2842 cells[i].wrapShift( 0 ); 2843 } 2844}; 2845 2846// -------------------------- physics -------------------------- // 2847 2848proto.updatePhysics = function() { 2849 this.velocity += this.accel; 2850 this.x += this.velocity; 2851 this.velocity *= this.getFrictionFactor(); 2852 // reset acceleration 2853 this.accel = 0; 2854}; 2855 2856proto.applyForce = function( force ) { 2857 this.accel += force; 2858}; 2859 2860proto.getFrictionFactor = function() { 2861 return 1 - this.options[ this.isFreeScrolling ? 'freeScrollFriction' : 'friction' ]; 2862}; 2863 2864 2865proto.getRestingPosition = function() { 2866 // my thanks to Steven Wittens, who simplified this math greatly 2867 return this.x + this.velocity / ( 1 - this.getFrictionFactor() ); 2868}; 2869 2870 2871proto.applySelectedAttraction = function() { 2872 // do not attract if pointer down or no cells 2873 if ( this.isPointerDown || this.isFreeScrolling || !this.cells.length ) { 2874 return; 2875 } 2876 var cell = this.cells[ this.selectedIndex ]; 2877 var wrap = this.options.wrapAround ? 2878 this.slideableWidth * Math.floor( this.selectedIndex / this.cells.length ) : 0; 2879 var distance = ( cell.target + wrap ) * -1 - this.x; 2880 var force = distance * this.options.selectedAttraction; 2881 this.applyForce( force ); 2882}; 2883 2884return proto; 2885 2886})); 2887 2888( function( window, factory ) { 2889 2890 // universal module definition 2891 2892 if ( typeof define == 'function' && define.amd ) { 2893 // AMD 2894 define( 'flickity/js/cell-change',[ 2895 './utils' 2896 ], function( utils ) { 2897 return factory( window, utils ); 2898 }); 2899 } else if ( typeof exports == 'object' ) { 2900 // CommonJS 2901 module.exports = factory( 2902 window, 2903 require('./utils') 2904 ); 2905 } else { 2906 // browser global 2907 window.Flickity = window.Flickity || {}; 2908 window.Flickity.cellChangePrototype = factory( 2909 window, 2910 window.utils 2911 ); 2912 } 2913 2914})( window, function factory( window, U ) { 2915 2916 2917 2918// append cells to a document fragment 2919function getCellsFragment( cells ) { 2920 var fragment = document.createDocumentFragment(); 2921 for ( var i=0, len = cells.length; i < len; i++ ) { 2922 var cell = cells[i]; 2923 fragment.appendChild( cell.element ); 2924 } 2925 return fragment; 2926} 2927 2928// -------------------------- cell change prototype -------------------------- // 2929 2930var proto = {}; 2931 2932/** 2933 * Insert, prepend, or append cells 2934 * @param {Element, Array, NodeList} elems 2935 * @param {Integer} index 2936 */ 2937proto.insert = function( elems, index ) { 2938 var cells = this._makeCells( elems ); 2939 if ( !cells || !cells.length ) { 2940 return; 2941 } 2942 var len = this.cells.length; 2943 // default to append 2944 index = index === undefined ? len : index; 2945 // add cells with document fragment 2946 var fragment = getCellsFragment( cells ); 2947 // append to slider 2948 var isAppend = index == len; 2949 if ( isAppend ) { 2950 this.slider.appendChild( fragment ); 2951 } else { 2952 var insertCellElement = this.cells[ index ].element; 2953 this.slider.insertBefore( fragment, insertCellElement ); 2954 } 2955 // add to this.cells 2956 if ( index === 0 ) { 2957 // prepend, add to start 2958 this.cells = cells.concat( this.cells ); 2959 } else if ( isAppend ) { 2960 // append, add to end 2961 this.cells = this.cells.concat( cells ); 2962 } else { 2963 // insert in this.cells 2964 var endCells = this.cells.splice( index, len - index ); 2965 this.cells = this.cells.concat( cells ).concat( endCells ); 2966 } 2967 2968 this._sizeCells( cells ); 2969 this._cellAddedRemoved( index ); 2970}; 2971 2972proto.append = function( elems ) { 2973 this.insert( elems, this.cells.length ); 2974}; 2975 2976proto.prepend = function( elems ) { 2977 this.insert( elems, 0 ); 2978}; 2979 2980/** 2981 * Remove cells 2982 * @param {Element, Array, NodeList} elems 2983 */ 2984proto.remove = function( elems ) { 2985 var cells = this.getCells( elems ); 2986 for ( var i=0, len = cells.length; i < len; i++ ) { 2987 var cell = cells[i]; 2988 cell.remove(); 2989 // remove item from collection 2990 U.removeFrom( cell, this.cells ); 2991 } 2992 2993 if ( cells.length ) { 2994 // update stuff 2995 this._cellAddedRemoved( 0 ); 2996 } 2997}; 2998 2999// updates when cells are added or removed 3000proto._cellAddedRemoved = function( index ) { 3001 // update page dots 3002 if ( this.pageDots ) { 3003 this.pageDots.setDots(); 3004 } 3005 // TODO cell is removed before the selected cell, adjust selectedIndex by -1 3006 this.selectedIndex = Math.max( 0, Math.min( this.cells.length - 1, this.selectedIndex ) ); 3007 3008 this.cellChange( index ); 3009}; 3010 3011/** 3012 * logic to be run after a cell's size changes 3013 * @param {Element} elem - cell's element 3014 */ 3015proto.cellSizeChange = function( elem ) { 3016 var cell = this.getCell( elem ); 3017 if ( !cell ) { 3018 return; 3019 } 3020 cell.getSize(); 3021 3022 var index = U.indexOf( this.cells, cell ); 3023 this.cellChange( index ); 3024}; 3025 3026/** 3027 * logic any time a cell is changed: added, removed, or size changed 3028 * @param {Integer} index - index of the changed cell, optional 3029 */ 3030proto.cellChange = function( index ) { 3031 // TODO maybe always size all cells unless isSkippingSizing 3032 // size all cells if necessary 3033 // if ( !isSkippingSizing ) { 3034 // this._sizeCells( this.cells ); 3035 // } 3036 3037 index = index || 0; 3038 3039 this._positionCells( index ); 3040 this._getWrapShiftCells(); 3041 this.setContainerSize(); 3042 // position slider 3043 if ( this.options.freeScroll ) { 3044 this.positionSlider(); 3045 } else { 3046 this.select( this.selectedIndex ); 3047 } 3048}; 3049 3050// ----- ----- // 3051 3052return proto; 3053 3054}); 3055 3056/*! 3057 * Flickity v0.1.0 3058 * Touch, responsive, flickable galleries 3059 * http://flickity.metafizzy.co 3060 * Copyright 2015 Metafizzy 3061 */ 3062 3063( function( window, factory ) { 3064 3065 // universal module definition 3066 3067 if ( typeof define == 'function' && define.amd ) { 3068 // AMD 3069 define( [ 3070 'classie/classie', 3071 'eventEmitter/EventEmitter', 3072 'eventie/eventie', 3073 'get-size/get-size', 3074 'flickity/js/utils', 3075 'flickity/js/cell', 3076 'flickity/js/prev-next-button', 3077 'flickity/js/page-dots', 3078 'flickity/js/player', 3079 'flickity/js/drag', 3080 'flickity/js/animate', 3081 'flickity/js/cell-change'
3082 ], function( classie, EventEmitter, eventie, getSize, U, Cell, PrevNextButton, PageDots, Player, dragPrototype, animatePrototype, cellChangePrototype ) { 3083 return factory( window, classie, EventEmitter, eventie, getSize, U, Cell, PrevNextButton, PageDots, Player, dragPrototype, animatePrototype, cellChangePrototype ); 3084 }); 3085 } else if ( typeof exports == 'object' ) { 3086 // CommonJS 3087 module.exports = factory( 3088 window, 3089 require('desandro-classie'), 3090 require('wolfy87-eventemitter'), 3091 require('eventie'), 3092 require('get-size'), 3093 require('./utils'), 3094 require('./cell'), 3095 require('./prev-next-button'), 3096 require('./page-dots'), 3097 require('./player'), 3098 require('./drag'), 3099 require('./animate'), 3100 require('./cell-change') 3101 ); 3102 } else { 3103 // browser global 3104 var _Flickity = window.Flickity; 3105 3106 window.Flickity = factory( 3107 window, 3108 window.classie, 3109 window.EventEmitter, 3110 window.eventie, 3111 window.getSize, 3112 window.utils, 3113 _Flickity.Cell, 3114 _Flickity.PrevNextButton, 3115 _Flickity.PageDots, 3116 _Flickity.Player, 3117 _Flickity.dragPrototype, 3118 _Flickity.animatePrototype, 3119 _Flickity.cellChangePrototype 3120 ); 3121 } 3122 3123}( window, function factory( window, classie, EventEmitter, eventie, getSize, 3124 U, Cell, PrevNextButton, PageDots, Player, dragPrototype, animatePrototype, 3125 cellChangePrototype ) { 3126 3127 3128 3129// vars 3130var jQuery = window.jQuery; 3131var getComputedStyle = window.getComputedStyle; 3132var console = window.console; 3133var imagesLoaded = window.imagesLoaded; 3134 3135function moveChildren( fromElem, toElem ) { 3136 while ( fromElem.children.length ) { 3137 toElem.appendChild( fromElem.children[0] ); 3138 } 3139} 3140 3141// -------------------------- Flickity -------------------------- // 3142 3143// globally unique identifiers 3144var GUID = 0; 3145// internal store of all Flickity intances 3146var instances = {}; 3147 3148function Flickity( element, options ) { 3149 var queryElement = U.getQueryElement( element ); 3150 if ( !queryElement ) { 3151 if ( console ) { 3152 console.error( 'Bad element for Flickity: ' + ( queryElement || element ) ); 3153 } 3154 return; 3155 } 3156 this.element = queryElement; 3157 // add jQuery 3158 if ( jQuery ) { 3159 this.$element = jQuery( this.element ); 3160 } 3161 // options 3162 this.options = U.extend( {}, this.constructor.defaults ); 3163 this.option( options ); 3164 3165 // kick things off 3166 this._create(); 3167} 3168 3169Flickity.defaults = { 3170 accessibility: true, 3171 // autoPlay: undefined, 3172 freeScrollFriction: 0.075, // friction when free-scrolling 3173 friction: 0.28, // friction when selecting 3174 cellAlign: 'center', 3175 // cellSelector: undefined, 3176 // contain: false, 3177 draggable: true, 3178 // initialIndex: 0, 3179 // imagesLoaded: false, 3180 percentPosition: true, 3181 pageDots: true, 3182 prevNextButtons: true, 3183 resizeBound: true, 3184 // watcCSS: false, 3185 // wrapAround: false, 3186 selectedAttraction: 0.025, 3187 leftArrowText: 'â', // text for prev/next button when no SVG support 3188 rightArrowText: 'â' 3189}; 3190 3191// inherit EventEmitter 3192U.extend( Flickity.prototype, EventEmitter.prototype ); 3193 3194Flickity.prototype._create = function() { 3195 // add id for Flickity.data 3196 var id = this.guid = ++GUID; 3197 this.element.flickityGUID = id; // expando 3198 instances[ id ] = this; // associate via id 3199 // initial properties 3200 this.selectedIndex = this.options.initialIndex || 0; 3201 // how many frames slider has been in same position 3202 this.restingFrames = 0; 3203 // initial physics properties 3204 this.x = 0; 3205 this.velocity = 0; 3206 this.accel = 0; 3207 this.originSide = this.options.rightToLeft ? 'right' : 'left'; 3208 // create viewport & slider 3209 this.viewport = document.createElement('div'); 3210 this.viewport.className = 'flickity-viewport'; 3211 this._createSlider(); 3212 // create prev/next buttons, page dots, and player 3213 if ( this.options.prevNextButtons ) { 3214 this.prevButton = new PrevNextButton( -1, this ); 3215 this.nextButton = new PrevNextButton( 1, this ); 3216 } 3217 if ( this.options.pageDots ) { 3218 this.pageDots = new PageDots( this ); 3219 } 3220 this.player = new Player( this ); 3221 3222 if ( this.options.resizeBound || this.options.watchCSS ) { 3223 eventie.bind( window, 'resize', this ); 3224 } 3225 3226 if ( this.options.watchCSS ) { 3227 this.watchCSS(); 3228 } else { 3229 this.activate(); 3230 } 3231 3232}; 3233 3234/** 3235 * set options 3236 * @param {Object} opts 3237 */ 3238Flickity.prototype.option = function( opts ) { 3239 U.extend( this.options, opts ); 3240}; 3241 3242Flickity.prototype.activate = function() { 3243 if ( this.isActive ) { 3244 return; 3245 } 3246 this.isActive = true; 3247 classie.add( this.element, 'flickity-enabled' ); 3248 // move children to slider 3249 moveChildren( this.element, this.slider ); 3250 this.viewport.appendChild( this.slider ); 3251 this.element.appendChild( this.viewport ); 3252
3253 this.getSize(); 3254 // get cells from children 3255 this.reloadCells(); 3256 this.setContainerSize(); 3257 // activate prev/next buttons, page dots 3258 if ( this.prevButton ) { 3259 this.prevButton.activate(); 3260 } 3261 if ( this.nextButton ) { 3262 this.nextButton.activate(); 3263 } 3264 if ( this.pageDots ) { 3265 this.pageDots.activate(); 3266 } 3267 // player 3268 if ( this.options.autoPlay ) { 3269 this.player.play(); 3270 // add hover listeners 3271 eventie.bind( this.element, 'mouseenter', this ); 3272 // TODO add event for pointer enter 3273 } 3274 3275 this.positionSliderAtSelected(); 3276 this.select( this.selectedIndex ); 3277 3278 this.imagesLoaded(); 3279 3280 // events 3281 this.bindDrag(); 3282 3283 if ( this.options.accessibility ) { 3284 // allow element to focusable 3285 this.element.tabIndex = 0; 3286 // listen for key presses 3287 eventie.bind( this.element, 'keydown', this ); 3288 } 3289}; 3290 3291// slider positions the cells 3292Flickity.prototype._createSlider = function() { 3293 // slider element does all the positioning 3294 var slider = document.createElement('div'); 3295 slider.className = 'flickity-slider'; 3296 slider.style[ this.originSide ] = 0; 3297 this.slider = slider; 3298}; 3299 3300// goes through all children 3301Flickity.prototype.reloadCells = function() { 3302 // collection of item elements 3303 this.cells = this._makeCells( this.slider.children ); 3304 this.positionCells( this.cells ); 3305 this._getWrapShiftCells(); 3306 this.setContainerSize(); 3307}; 3308 3309/** 3310 * turn elements into Flickity.Cells 3311 * @param {Array or NodeList or HTMLElement} elems 3312 * @returns {Array} items - collection of new Flickity Cells 3313 */ 3314Flickity.prototype._makeCells = function( elems ) { 3315 var cellElems = U.filterFindElements( elems, this.options.cellSelector ); 3316 3317 // create new Flickity for collection 3318 var cells = []; 3319 for ( var i=0, len = cellElems.length; i < len; i++ ) { 3320 var elem = cellElems[i]; 3321 var cell = new Cell( elem, this ); 3322 cells.push( cell ); 3323 } 3324 3325 return cells; 3326}; 3327 3328Flickity.prototype.getLastCell = function() { 3329 return this.cells[ this.cells.length - 1 ]; 3330}; 3331 3332// positions all cells 3333Flickity.prototype.positionCells = function() { 3334 // size all cells 3335 this._sizeCells( this.cells ); 3336 // position all cells 3337 this._positionCells( 0 ); 3338}; 3339 3340/** 3341 * position certain cells 3342 * @param {Integer} index - which cell to start with 3343 */ 3344Flickity.prototype._positionCells = function( index ) { 3345 // also measure maxCellHeight 3346 // start 0 if positioning all cells 3347 this.maxCellHeight = index ? this.maxCellHeight || 0 : 0; 3348 var cellX = 0; 3349 // get cellX 3350 if ( index > 0 ) { 3351 var startCell = this.cells[ index - 1 ]; 3352 cellX = startCell.x + startCell.size.outerWidth; 3353 } 3354 var cell; 3355 for ( var len = this.cells.length, i=index; i < len; i++ ) { 3356 cell = this.cells[i]; 3357 cell.setPosition( cellX ); 3358 cellX += cell.size.outerWidth; 3359 this.maxCellHeight = Math.max( cell.size.outerHeight, this.maxCellHeight ); 3360 } 3361 // keep track of cellX for wrap-around 3362 this.slideableWidth = cellX; 3363 // contain cell target 3364 this._containCells(); 3365}; 3366 3367/** 3368 * cell.getSize() on multiple cells 3369 * @param {Array} cells 3370 */ 3371Flickity.prototype._sizeCells = function( cells ) { 3372 for ( var i=0, len = cells.length; i < len; i++ ) { 3373 var cell = cells[i]; 3374 cell.getSize(); 3375 } 3376}; 3377 3378Flickity.prototype.getSize = function() { 3379 this.size = getSize( this.element ); 3380 this.setCellAlign(); 3381 this.cursorPosition = this.size.innerWidth * this.cellAlign; 3382}; 3383 3384var cellAlignShorthands = { 3385 // cell align, then based on origin side 3386 center: { 3387 left: 0.5, 3388 right: 0.5 3389 }, 3390 left: { 3391 left: 0, 3392 right: 1 3393 }, 3394 right: { 3395 right: 0, 3396 left: 1 3397 } 3398}; 3399 3400Flickity.prototype.setCellAlign = function() { 3401 var shorthand = cellAlignShorthands[ this.options.cellAlign ]; 3402 this.cellAlign = shorthand ? shorthand[ this.originSide ] : this.options.cellAlign; 3403}; 3404 3405Flickity.prototype.setContainerSize = function() { 3406 this.viewport.style.height = this.maxCellHeight + 'px'; 3407}; 3408 3409Flickity.prototype._getWrapShiftCells = function() { 3410 // only for wrap-around 3411 if ( !this.options.wrapAround ) { 3412 return; 3413 } 3414 // unshift previous cells 3415 this._unshiftCells( this.beforeShiftCells ); 3416 this._unshiftCells( this.afterShiftCells ); 3417 // get before cells 3418 // initial gap 3419 var gapX = this.cursorPosition; 3420 var cellIndex = this.cells.length - 1; 3421 this.beforeShiftCells = this._getGapCells( gapX, cellIndex, -1 ); 3422 // get after cells 3423 // ending gap between last cell and end of gallery viewport
3424 gapX = this.size.innerWidth - this.cursorPosition; 3425 // start cloning at first cell, working forwards 3426 this.afterShiftCells = this._getGapCells( gapX, 0, 1 ); 3427}; 3428 3429Flickity.prototype._getGapCells = function( gapX, cellIndex, increment ) { 3430 // keep adding cells until the cover the initial gap 3431 var cells = []; 3432 while ( gapX > 0 ) { 3433 var cell = this.cells[ cellIndex ]; 3434 if ( !cell ) { 3435 break; 3436 } 3437 cells.push( cell ); 3438 cellIndex += increment; 3439 gapX -= cell.size.outerWidth; 3440 } 3441 return cells; 3442}; 3443 3444// ----- contain ----- // 3445 3446// contain cell targets so no excess sliding 3447Flickity.prototype._containCells = function() { 3448 if ( !this.options.contain || this.options.wrapAround ) { 3449 return; 3450 } 3451 // end limit 3452 var lastCell = this.getLastCell(); 3453 var endMargin = this.options.rightToLeft ? 'marginLeft' : 'marginRight'; 3454 var contentWidth = this.slideableWidth - lastCell.size[ endMargin ]; 3455 var endLimit = contentWidth - this.size.innerWidth * ( 1 - this.cellAlign ); 3456 // contain each cell target 3457 for ( var i=0, len = this.cells.length; i < len; i++ ) { 3458 var cell = this.cells[i]; 3459 // reset default target 3460 cell.setDefaultTarget(); 3461 cell.target = Math.max( cell.target, this.cursorPosition ); 3462 cell.target = Math.min( cell.target, endLimit ); 3463 } 3464}; 3465 3466// ----- ----- // 3467 3468/** 3469 * emits events via eventEmitter and jQuery events 3470 * @param {String} type - name of event 3471 * @param {Event} event - original event 3472 * @param {Array} args - extra arguments 3473 */ 3474Flickity.prototype.dispatchEvent = function( type, event, args ) { 3475 var emitArgs = [ event ].concat( args ); 3476 this.emitEvent( type, emitArgs ); 3477 3478 if ( jQuery && this.$element ) { 3479 if ( event ) { 3480 // create jQuery event 3481 var $event = jQuery.Event( event ); 3482 $event.type = type; 3483 this.$element.trigger( $event, args ); 3484 } else { 3485 // just trigger with type if no event available 3486 this.$element.trigger( type, args ); 3487 } 3488 } 3489}; 3490 3491// -------------------------- select -------------------------- // 3492 3493/** 3494 * @param {Integer} index - index of the cell 3495 * @param {Boolean} isWrap - will wrap-around to last/first if at the end 3496 */ 3497Flickity.prototype.select = function( index, isWrap ) { 3498 if ( !this.isActive ) { 3499 return; 3500 } 3501 // wrap position so slider is within normal area 3502 if ( this.options.wrapAround ) { 3503 if ( index < 0 ) { 3504 this.x -= this.slideableWidth; 3505 } else if ( index >= this.cells.length ) { 3506 this.x += this.slideableWidth; 3507 } 3508 } 3509 3510 if ( this.options.wrapAround || isWrap ) { 3511 index = U.modulo( index, this.cells.length ); 3512 } 3513 3514 if ( this.cells[ index ] ) { 3515 this.selectedIndex = index; 3516 this.setSelectedCell(); 3517 this.startAnimation(); 3518 this.dispatchEvent('select'); 3519 } 3520}; 3521 3522Flickity.prototype.previous = function( isWrap ) { 3523 this.select( this.selectedIndex - 1, isWrap ); 3524}; 3525 3526Flickity.prototype.next = function( isWrap ) { 3527 this.select( this.selectedIndex + 1, isWrap ); 3528}; 3529 3530Flickity.prototype.updatePrevNextButtons = function() { 3531 if ( this.prevButton ) { 3532 this.prevButton.update(); 3533 } 3534 if ( this.nextButton ) { 3535 this.nextButton.update(); 3536 } 3537}; 3538 3539Flickity.prototype.setSelectedCell = function() { 3540 this._removeSelectedCellClass(); 3541 this.selectedCell = this.cells[ this.selectedIndex ]; 3542 classie.add( this.selectedCell.element, 'is-selected' ); 3543}; 3544 3545Flickity.prototype._removeSelectedCellClass = function() { 3546 if ( this.selectedCell ) { 3547 classie.remove( this.selectedCell.element, 'is-selected' ); 3548 } 3549}; 3550 3551// on button clicks and ui changes 3552// stop player and stop free scrolling 3553Flickity.prototype.uiChange = function() { 3554 this.player.stop(); 3555 delete this.isFreeScrolling; 3556}; 3557 3558// -------------------------- images -------------------------- // 3559 3560// TODO move out this repo to avoid problems with imagesLoaded dependency 3561Flickity.prototype.imagesLoaded = function() { 3562 if ( !this.options.imagesLoaded || !imagesLoaded ) { 3563 return; 3564 } 3565 var _this = this; 3566 function onImagesLoadedProgress( instance, image ) { 3567 // check if image is a cell 3568 var cell = _this.getCell( image.img ); 3569 // otherwise get its parents 3570 var cellElem = cell.element || U.getParent( image.img, '.flickity-slider > *' ); 3571 _this.cellSizeChange( cellElem ); 3572 } 3573 imagesLoaded( this.slider ).on( 'progress', onImagesLoadedProgress ); 3574}; 3575 3576// -------------------------- get cells -------------------------- // 3577 3578/** 3579 * get Flickity.Cell, given an Element 3580 * @param {Element} elem 3581 * @returns {Flickity.Cell} item 3582 */ 3583Flickity.prototype.getCell = function( elem ) { 3584 // loop through cells to get the one that matches 3585 for ( var i=0, len = this.cells.length; i < len; i++ ) { 3586 var cell = this.cells[i]; 3587 if ( cell.element == elem ) { 3588 return cell; 3589 } 3590 } 3591}; 3592 3593/** 3594 * get collection of Flickity.Cells, given Elements 3595 * @param {Element, Array, NodeList} elems 3596 * @returns {Array} cells - Flickity.Cells 3597 */ 3598Flickity.prototype.getCells = function( elems ) { 3599 elems = U.makeArray( elems ); 3600 var cells = []; 3601 for ( var i=0, len = elems.length; i < len; i++ ) { 3602 var elem = elems[i]; 3603 var cell = this.getCell( elem ); 3604 if ( cell ) { 3605 cells.push( cell ); 3606 } 3607 } 3608 return cells; 3609}; 3610 3611// -------------------------- events -------------------------- // 3612 3613// ----- resize ----- // 3614 3615Flickity.prototype.onresize = function() { 3616 this.watchCSS(); 3617 this.resize(); 3618}; 3619 3620U.debounceMethod( Flickity, 'onresize', 150 ); 3621 3622Flickity.prototype.resize = function() { 3623 if ( !this.isActive ) { 3624 return; 3625 }
3626 this.getSize(); 3627 // wrap values 3628 if ( this.options.wrapAround ) { 3629 this.x = U.modulo( this.x, this.slideableWidth ); 3630 } 3631 this.positionCells(); 3632 this._getWrapShiftCells(); 3633 this.setContainerSize(); 3634 this.positionSliderAtSelected(); 3635}; 3636 3637var supportsConditionalCSS = Flickity.supportsConditionalCSS = ( function() { 3638 var supports; 3639 return function checkSupport() { 3640 if ( supports !== undefined ) { 3641 return supports; 3642 } 3643 if ( !getComputedStyle ) { 3644 supports = false; 3645 return; 3646 } 3647 // style body's :after and check that 3648 var style = document.createElement('style'); 3649 var cssText = document.createTextNode('body:after { content: "foo"; display: none; }'); 3650 style.appendChild( cssText ); 3651 document.head.appendChild( style ); 3652 var afterContent = getComputedStyle( document.body, ':after' ).content; 3653 // check if able to get :after content 3654 supports = afterContent.indexOf('foo') != -1; 3655 document.head.removeChild( style ); 3656 return supports; 3657 }; 3658})(); 3659 3660// watches the :after property, activates/deactivates 3661Flickity.prototype.watchCSS = function() { 3662 var watchOption = this.options.watchCSS; 3663 if ( !watchOption ) { 3664 return; 3665 } 3666 var supports = supportsConditionalCSS(); 3667 if ( !supports ) { 3668 // activate if watch option is fallbackOn 3669 var method = watchOption == 'fallbackOn' ? 'activate' : 'deactivate'; 3670 this[ method ](); 3671 return; 3672 } 3673 3674 var afterContent = getComputedStyle( this.element, ':after' ).content; 3675 // activate if :after { content: 'flickity' } 3676 if ( afterContent.indexOf('flickity') != -1 ) { 3677 this.activate(); 3678 } else { 3679 this.deactivate(); 3680 } 3681}; 3682 3683// ----- keydown ----- // 3684 3685// go previous/next if left/right keys pressed 3686Flickity.prototype.onkeydown = function( event ) { 3687 // only work if element is in focus 3688 if ( !this.options.accessibility || 3689 ( document.activeElement && document.activeElement != this.element ) ) { 3690 return; 3691 } 3692 3693 if ( event.keyCode == 37 ) { 3694 // go left 3695 var leftMethod = this.options.rightToLeft ? 'next' : 'previous'; 3696 this.uiChange(); 3697 this[ leftMethod ](); 3698 } else if ( event.keyCode == 39 ) { 3699 // go right 3700 var rightMethod = this.options.rightToLeft ? 'previous' : 'next'; 3701 this.uiChange(); 3702 this[ rightMethod ](); 3703 } 3704}; 3705 3706// ----- mouseenter/leave ----- // 3707 3708// pause auto-play on hover 3709Flickity.prototype.onmouseenter = function() { 3710 this.player.pause(); 3711 eventie.bind( this.element, 'mouseleave', this ); 3712}; 3713 3714// resume auto-play on hover off 3715Flickity.prototype.onmouseleave = function() { 3716 this.player.unpause(); 3717 eventie.unbind( this.element, 'mouseleave', this ); 3718}; 3719 3720// -------------------------- destroy -------------------------- // 3721 3722// deactivate all Flickity functionality, but keep stuff available 3723Flickity.prototype.deactivate = function() { 3724 if ( !this.isActive ) { 3725 return; 3726 } 3727 classie.remove( this.element, 'flickity-enabled' ); 3728 // destroy cells 3729 for ( var i=0, len = this.cells.length; i < len; i++ ) { 3730 var cell = this.cells[i]; 3731 cell.destroy(); 3732 } 3733 this._removeSelectedCellClass(); 3734 this.element.removeChild( this.viewport ); 3735 // move child elements back into element 3736 moveChildren( this.slider, this.element ); 3737 // deactivate prev/next buttons, page dots; stop player 3738 if ( this.prevButton ) { 3739 this.prevButton.deactivate(); 3740 } 3741 if ( this.nextButton ) { 3742 this.nextButton.deactivate(); 3743 } 3744 if ( this.pageDots ) { 3745 this.pageDots.deactivate(); 3746 } 3747 this.player.stop(); 3748 // unbind events 3749 this.unbindDrag(); 3750 if ( this.options.accessibility ) { 3751 this.element.removeAttribute('tabIndex'); 3752 eventie.unbind( this.element, 'keydown', this ); 3753 } 3754 // set flags 3755 this.isActive = false; 3756 this.isAnimating = false; 3757}; 3758 3759Flickity.prototype.destroy = function() { 3760 this.deactivate(); 3761 if ( this.options.resizeBound || this.options.watch ) { 3762 eventie.unbind( window, 'resize', this ); 3763 } 3764 delete this.element.flickityGUID; 3765 delete instances[ this.guid ]; 3766}; 3767 3768// -------------------------- prototype -------------------------- // 3769 3770U.extend( Flickity.prototype, dragPrototype ); 3771U.extend( Flickity.prototype, animatePrototype ); 3772U.extend( Flickity.prototype, cellChangePrototype ); 3773 3774// -------------------------- extras -------------------------- // 3775 3776/** 3777 * get Flickity instance from element 3778 * @param {Element} elem 3779 * @returns {Flickity} 3780 */ 3781Flickity.data = function( elem ) { 3782 elem = U.getQueryElement( elem ); 3783 var id = elem && elem.flickityGUID; 3784 return id && instances[ id ]; 3785}; 3786 3787U.htmlInit( Flickity, 'flickity' ); 3788 3789if ( jQuery && jQuery.bridget ) { 3790 jQuery.bridget( 'flickity', Flickity ); 3791} 3792 3793// make classes accessible 3794Flickity.Cell = Cell; 3795Flickity.PrevNextButton = PrevNextButton; 3796Flickity.PageDots = PageDots; 3797Flickity.Player = Player; 3798 3799return Flickity; 3800 3801})); 3802
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.