1/*! 2 * Isotope PACKAGED v2.0.1 3 * Filter & sort magical layouts 4 * http://isotope.metafizzy.co 5 */ 6 7/** 8 * Bridget makes jQuery widgets 9 * v1.0.1 10 */ 11 12( function( window ) { 13 14 15 16// -------------------------- utils -------------------------- // 17 18var slice = Array.prototype.slice; 19 20function noop() {} 21 22// -------------------------- definition -------------------------- // 23 24function defineBridget( $ ) { 25 26// bail if no jQuery 27if ( !$ ) { 28 return; 29} 30 31// -------------------------- addOptionMethod -------------------------- // 32 33/** 34 * adds option method -> $().plugin('option', {...}) 35 * @param {Function} PluginClass - constructor class 36 */ 37function addOptionMethod( PluginClass ) { 38 // don't overwrite original option method 39 if ( PluginClass.prototype.option ) { 40 return; 41 } 42 43 // option setter 44 PluginClass.prototype.option = function( opts ) { 45 // bail out if not an object 46 if ( !$.isPlainObject( opts ) ){ 47 return; 48 } 49 this.options = $.extend( true, this.options, opts ); 50 }; 51} 52 53 54// -------------------------- plugin bridge -------------------------- // 55 56// helper function for logging errors 57// $.error breaks jQuery chaining 58var logError = typeof console === 'undefined' ? noop : 59 function( message ) { 60 console.error( message ); 61 }; 62 63/** 64 * jQuery plugin bridge, access methods like $elem.plugin('method') 65 * @param {String} namespace - plugin name 66 * @param {Function} PluginClass - constructor class 67 */ 68function bridge( namespace, PluginClass ) { 69 // add to jQuery fn namespace 70 $.fn[ namespace ] = function( options ) { 71 if ( typeof options === 'string' ) { 72 // call plugin method when first argument is a string 73 // get arguments for method 74 var args = slice.call( arguments, 1 ); 75 76 for ( var i=0, len = this.length; i < len; i++ ) { 77 var elem = this[i]; 78 var instance = $.data( elem, namespace ); 79 if ( !instance ) { 80 logError( "cannot call methods on " + namespace + " prior to initialization; " + 81 "attempted to call '" + options + "'" ); 82 continue; 83 } 84 if ( !$.isFunction( instance[options] ) || options.charAt(0) === '_' ) { 85 logError( "no such method '" + options + "' for " + namespace + " instance" ); 86 continue; 87 } 88 89 // trigger method with arguments 90 var returnValue = instance[ options ].apply( instance, args ); 91 92 // break look and return first value if provided 93 if ( returnValue !== undefined ) { 94 return returnValue; 95 } 96 } 97 // return this if no return value 98 return this; 99 } else { 100 return this.each( function() { 101 var instance = $.data( this, namespace ); 102 if ( instance ) { 103 // apply options & init 104 instance.option( options ); 105 instance._init(); 106 } else { 107 // initialize new instance 108 instance = new PluginClass( this, options ); 109 $.data( this, namespace, instance ); 110 } 111 }); 112 } 113 }; 114 115} 116 117// -------------------------- bridget -------------------------- // 118 119/** 120 * converts a Prototypical class into a proper jQuery plugin 121 * the class must have a ._init method 122 * @param {String} namespace - plugin name, used in $().pluginName 123 * @param {Function} PluginClass - constructor class 124 */ 125$.bridget = function( namespace, PluginClass ) { 126 addOptionMethod( PluginClass ); 127 bridge( namespace, PluginClass ); 128}; 129 130return $.bridget; 131 132} 133 134// transport 135if ( typeof define === 'function' && define.amd ) { 136 // AMD 137 define( 'jquery-bridget/jquery.bridget',[ 'jquery' ], defineBridget ); 138} else { 139 // get jquery from browser global 140 defineBridget( window.jQuery ); 141} 142 143})( window ); 144 145/*! 146 * eventie v1.0.5 147 * event binding helper 148 * eventie.bind( elem, 'click', myFn ) 149 * eventie.unbind( elem, 'click', myFn ) 150 * MIT license 151 */ 152 153/*jshint browser: true, undef: true, unused: true */ 154/*global define: false, module: false */ 155 156( function( window ) { 157 158 159 160var docElem = document.documentElement; 161 162var bind = function() {}; 163 164function getIEEvent( obj ) { 165 var event = window.event; 166 // add event.target 167 event.target = event.target || event.srcElement || obj; 168 return event; 169} 170 171if ( docElem.addEventListener ) { 172 bind = function( obj, type, fn ) { 173 obj.addEventListener( type, fn, false ); 174 }; 175} else if ( docElem.attachEvent ) { 176 bind = function( obj, type, fn ) { 177 obj[ type + fn ] = fn.handleEvent ? 178 function() { 179 var event = getIEEvent( obj ); 180 fn.handleEvent.call( fn, event ); 181 } : 182 function() { 183 var event = getIEEvent( obj ); 184 fn.call( obj, event ); 185 }; 186 obj.attachEvent( "on" + type, obj[ type + fn ] ); 187 }; 188} 189 190var unbind = function() {}; 191 192if ( docElem.removeEventListener ) { 193 unbind = function( obj, type, fn ) { 194 obj.removeEventListener( type, fn, false ); 195 }; 196} else if ( docElem.detachEvent ) { 197 unbind = function( obj, type, fn ) { 198 obj.detachEvent( "on" + type, obj[ type + fn ] ); 199 try { 200 delete obj[ type + fn ]; 201 } catch ( err ) { 202 // can't delete window object properties 203 obj[ type + fn ] = undefined; 204 } 205 }; 206} 207 208var eventie = { 209 bind: bind, 210 unbind: unbind 211}; 212 213// ----- module definition ----- // 214 215if ( typeof define === 'function' && define.amd ) { 216 // AMD 217 define( 'eventie/eventie',eventie ); 218} else if ( typeof exports === 'object' ) { 219 // CommonJS 220 module.exports = eventie; 221} else { 222 // browser global 223 window.eventie = eventie; 224} 225 226})( this ); 227 228/*! 229 * docReady 230 * Cross browser DOMContentLoaded event emitter 231 */ 232 233/*jshint browser: true, strict: true, undef: true, unused: true*/ 234/*global define: false */ 235 236( function( window ) { 237 238 239 240var document = window.document; 241// collection of functions to be triggered on ready 242var queue = []; 243 244function docReady( fn ) { 245 // throw out non-functions 246 if ( typeof fn !== 'function' ) { 247 return; 248 } 249 250 if ( docReady.isReady ) { 251 // ready now, hit it 252 fn(); 253 } else { 254 // queue function when ready 255 queue.push( fn ); 256 } 257} 258 259docReady.isReady = false; 260 261// triggered on various doc ready events 262function init( event ) { 263 // bail if IE8 document is not ready just yet 264 var isIE8NotReady = event.type === 'readystatechange' && document.readyState !== 'complete'; 265 if ( docReady.isReady || isIE8NotReady ) { 266 return; 267 } 268 docReady.isReady = true; 269 270 // process queue 271 for ( var i=0, len = queue.length; i < len; i++ ) { 272 var fn = queue[i]; 273 fn(); 274 } 275} 276 277function defineDocReady( eventie ) { 278 eventie.bind( document, 'DOMContentLoaded', init ); 279 eventie.bind( document, 'readystatechange', init ); 280 eventie.bind( window, 'load', init ); 281 282 return docReady; 283} 284 285// transport 286if ( typeof define === 'function' && define.amd ) { 287 // AMD 288 // if RequireJS, then doc is already ready 289 docReady.isReady = typeof requirejs === 'function'; 290 define( 'doc-ready/doc-ready',[ 'eventie/eventie' ], defineDocReady ); 291} else { 292 // browser global 293 window.docReady = defineDocReady( window.eventie ); 294} 295 296})( this ); 297 298/*! 299 * EventEmitter v4.2.7 - git.io/ee 300 * Oliver Caldwell 301 * MIT license 302 * @preserve 303 */ 304 305(function () { 306 307 308 /** 309 * Class for managing events. 310 * Can be extended to provide event functionality in other classes. 311 * 312 * @class EventEmitter Manages event registering and emitting. 313 */ 314 function EventEmitter() {} 315 316 // Shortcuts to improve speed and size 317 var proto = EventEmitter.prototype; 318 var exports = this; 319 var originalGlobalValue = exports.EventEmitter; 320 321 /** 322 * Finds the index of the listener for the event in it's storage array. 323 * 324 * @param {Function[]} listeners Array of listeners to search through. 325 * @param {Function} listener Method to look for. 326 * @return {Number} Index of the specified listener, -1 if not found 327 * @api private 328 */ 329 function indexOfListener(listeners, listener) { 330 var i = listeners.length; 331 while (i--) { 332 if (listeners[i].listener === listener) { 333 return i; 334 } 335 } 336 337 return -1; 338 } 339 340 /** 341 * Alias a method while keeping the context correct, to allow for over
vendor: 11,657 bytes, lines 341-649
341writing of target method. 342 * 343 * @param {String} name The name of the target method. 344 * @return {Function} The aliased method 345 * @api private 346 */ 347 function alias(name) { 348 return function aliasClosure() { 349 return this[name].apply(this, arguments); 350 }; 351 } 352 353 /** 354 * Returns the listener array for the specified event. 355 * Will initialise the event object and listener arrays if required. 356 * 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. 357 * Each property in the object response is an array of listener functions. 358 * 359 * @param {String|RegExp} evt Name of the event to return the listeners from. 360 * @return {Function[]|Object} All listener functions for the event. 361 */ 362 proto.getListeners = function getListeners(evt) { 363 var events = this._getEvents(); 364 var response; 365 var key; 366 367 // Return a concatenated array of all matching events if 368 // the selector is a regular expression. 369 if (evt instanceof RegExp) { 370 response = {}; 371 for (key in events) { 372 if (events.hasOwnProperty(key) && evt.test(key)) { 373 response[key] = events[key]; 374 } 375 } 376 } 377 else { 378 response = events[evt] || (events[evt] = []); 379 } 380 381 return response; 382 }; 383 384 /** 385 * Takes a list of listener objects and flattens it into a list of listener functions. 386 * 387 * @param {Object[]} listeners Raw listener objects. 388 * @return {Function[]} Just the listener functions. 389 */ 390 proto.flattenListeners = function flattenListeners(listeners) { 391 var flatListeners = []; 392 var i; 393 394 for (i = 0; i < listeners.length; i += 1) { 395 flatListeners.push(listeners[i].listener); 396 } 397 398 return flatListeners; 399 }; 400 401 /** 402 * 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. 403 * 404 * @param {String|RegExp} evt Name of the event to return the listeners from. 405 * @return {Object} All listener functions for an event in an object. 406 */ 407 proto.getListenersAsObject = function getListenersAsObject(evt) { 408 var listeners = this.getListeners(evt); 409 var response; 410 411 if (listeners instanceof Array) { 412 response = {}; 413 response[evt] = listeners; 414 } 415 416 return response || listeners; 417 }; 418 419 /** 420 * Adds a listener function to the specified event. 421 * The listener will not be added if it is a duplicate. 422 * If the listener returns true then it will be removed after it is called. 423 * If you pass a regular expression as the event name then the listener will be added to all events that match it. 424 * 425 * @param {String|RegExp} evt Name of the event to attach the listener to. 426 * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling. 427 * @return {Object} Current instance of EventEmitter for chaining. 428 */ 429 proto.addListener = function addListener(evt, listener) { 430 var listeners = this.getListenersAsObject(evt); 431 var listenerIsWrapped = typeof listener === 'object'; 432 var key; 433 434 for (key in listeners) { 435 if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) { 436 listeners[key].push(listenerIsWrapped ? listener : { 437 listener: listener, 438 once: false 439 }); 440 } 441 } 442 443 return this; 444 }; 445 446 /** 447 * Alias of addListener 448 */ 449 proto.on = alias('addListener'); 450 451 /** 452 * Semi-alias of addListener. It will add a listener that will be 453 * automatically removed after it's first execution. 454 * 455 * @param {String|RegExp} evt Name of the event to attach the listener to. 456 * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling. 457 * @return {Object} Current instance of EventEmitter for chaining. 458 */ 459 proto.addOnceListener = function addOnceListener(evt, listener) { 460 return this.addListener(evt, { 461 listener: listener, 462 once: true 463 }); 464 }; 465 466 /** 467 * Alias of addOnceListener. 468 */ 469 proto.once = alias('addOnceListener'); 470 471 /** 472 * 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. 473 * You need to tell it what event names should be matched by a regex. 474 * 475 * @param {String} evt Name of the event to create. 476 * @return {Object} Current instance of EventEmitter for chaining. 477 */ 478 proto.defineEvent = function defineEvent(evt) { 479 this.getListeners(evt); 480 return this; 481 }; 482 483 /** 484 * Uses defineEvent to define multiple events. 485 * 486 * @param {String[]} evts An array of event names to define. 487 * @return {Object} Current instance of EventEmitter for chaining. 488 */ 489 proto.defineEvents = function defineEvents(evts) { 490 for (var i = 0; i < evts.length; i += 1) { 491 this.defineEvent(evts[i]); 492 } 493 return this; 494 }; 495 496 /** 497 * Removes a listener function from the specified event. 498 * When passed a regular expression as the event name, it will remove the listener from all events that match it. 499 * 500 * @param {String|RegExp} evt Name of the event to remove the listener from. 501 * @param {Function} listener Method to remove from the event. 502 * @return {Object} Current instance of EventEmitter for chaining. 503 */ 504 proto.removeListener = function removeListener(evt, listener) { 505 var listeners = this.getListenersAsObject(evt); 506 var index; 507 var key; 508 509 for (key in listeners) { 510 if (listeners.hasOwnProperty(key)) { 511 index = indexOfListener(listeners[key], listener); 512 513 if (index !== -1) { 514 listeners[key].splice(index, 1); 515 } 516 } 517 } 518 519 return this; 520 }; 521 522 /** 523 * Alias of removeListener 524 */ 525 proto.off = alias('removeListener'); 526 527 /** 528 * Adds listeners in bulk using the manipulateListeners method. 529 * 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. 530 * You can also pass it a regular expression to add the array of listeners to all events that match it. 531 * Yeah, this function does quite a bit. That's probably a bad thing. 532 * 533 * @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. 534 * @param {Function[]} [listeners] An optional array of listener functions to add. 535 * @return {Object} Current instance of EventEmitter for chaining. 536 */ 537 proto.addListeners = function addListeners(evt, listeners) { 538 // Pass through to manipulateListeners 539 return this.manipulateListeners(false, evt, listeners); 540 }; 541 542 /** 543 * Removes listeners in bulk using the manipulateListeners method. 544 * 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. 545 * You can also pass it an event name and an array of listeners to be removed. 546 * You can also pass it a regular expression to remove the listeners from all events that match it. 547 * 548 * @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. 549 * @param {Function[]} [listeners] An optional array of listener functions to remove. 550 * @return {Object} Current instance of EventEmitter for chaining. 551 */ 552 proto.removeListeners = function removeListeners(evt, listeners) { 553 // Pass through to manipulateListeners 554 return this.manipulateListeners(true, evt, listeners); 555 }; 556 557 /** 558 * 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. 559 * The first argument will determine if the listeners are removed (true) or added (false). 560 * 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. 561 * You can also pass it an event name and an array of listeners to be added/removed. 562 * You can also pass it a regular expression to manipulate the listeners of all events that match it. 563 * 564 * @param {Boolean} remove True if you want to remove listeners, false if you want to add. 565 * @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. 566 * @param {Function[]} [listeners] An optional array of listener functions to add/remove. 567 * @return {Object} Current instance of EventEmitter for chaining. 568 */ 569 proto.manipulateListeners = function manipulateListeners(remove, evt, listeners) { 570 var i; 571 var value; 572 var single = remove ? this.removeListener : this.addListener; 573 var multiple = remove ? this.removeListeners : this.addListeners; 574 575 // If evt is an object then pass each of it's properties to this method 576 if (typeof evt === 'object' && !(evt instanceof RegExp)) { 577 for (i in evt) { 578 if (evt.hasOwnProperty(i) && (value = evt[i])) { 579 // Pass the single listener straight through to the singular method 580 if (typeof value === 'function') { 581 single.call(this, i, value); 582 } 583 else { 584 // Otherwise pass back to the multiple function 585 multiple.call(this, i, value); 586 } 587 } 588 } 589 } 590 else { 591 // So evt must be a string 592 // And listeners must be an array of listeners 593 // Loop over it and pass each one to the multiple method 594 i = listeners.length; 595 while (i--) { 596 single.call(this, evt, listeners[i]); 597 } 598 } 599 600 return this; 601 }; 602 603 /** 604 * Removes all listeners from a specified event. 605 * If you do not specify an event then all listeners will be removed. 606 * That means every event will be emptied. 607 * You can also pass a regex to remove all events that match it. 608 * 609 * @param {String|RegExp} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed. 610 * @return {Object} Current instance of EventEmitter for chaining. 611 */ 612 proto.removeEvent = function removeEvent(evt) { 613 var type = typeof evt; 614 var events = this._getEvents(); 615 var key; 616 617 // Remove different things depending on the state of evt 618 if (type === 'string') { 619 // Remove all listeners for the specified event 620 delete events[evt]; 621 } 622 else if (evt instanceof RegExp) { 623 // Remove all events matching the regex. 624 for (key in events) { 625 if (events.hasOwnProperty(key) && evt.test(key)) { 626 delete events[key]; 627 } 628 } 629 } 630 else { 631 // Remove all listeners in all events 632 delete this._events; 633 } 634 635 return this; 636 }; 637 638 /** 639 * Alias of removeEvent. 640 * 641 * Added to mirror the node API. 642 */ 643 proto.removeAllListeners = alias('removeEvent'); 644 645 /** 646 * Emits an event of your choice. 647 * When emitted, every listener attached to that event will be executed. 648 * If you pass the optional argument array then those arguments will be passed to every listener upon execution. 649 * Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately.
650 * So they will not arrive within the array on the other side, they will be separate. 651 * You can also pass a regular expression to emit to all events that match it. 652 * 653 * @param {String|RegExp} evt Name of the event to emit and execute listeners for. 654 * @param {Array} [args] Optional array of arguments to be passed to each listener. 655 * @return {Object} Current instance of EventEmitter for chaining. 656 */ 657 proto.emitEvent = function emitEvent(evt, args) { 658 var listeners = this.getListenersAsObject(evt); 659 var listener; 660 var i; 661 var key; 662 var response; 663 664 for (key in listeners) { 665 if (listeners.hasOwnProperty(key)) { 666 i = listeners[key].length; 667 668 while (i--) { 669 // If the listener returns true then it shall be removed from the event 670 // The function is executed either with a basic call or an apply if there is an args array 671 listener = listeners[key][i]; 672 673 if (listener.once === true) { 674 this.removeListener(evt, listener.listener); 675 } 676 677 response = listener.listener.apply(this, args || []); 678 679 if (response === this._getOnceReturnValue()) { 680 this.removeListener(evt, listener.listener); 681 } 682 } 683 } 684 } 685 686 return this; 687 }; 688 689 /** 690 * Alias of emitEvent 691 */ 692 proto.trigger = alias('emitEvent'); 693 694 /** 695 * 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. 696 * As with emitEvent, you can pass a regex in place of the event name to emit to all events that match it. 697 * 698 * @param {String|RegExp} evt Name of the event to emit and execute listeners for. 699 * @param {...*} Optional additional arguments to be passed to each listener. 700 * @return {Object} Current instance of EventEmitter for chaining. 701 */ 702 proto.emit = function emit(evt) { 703 var args = Array.prototype.slice.call(arguments, 1); 704 return this.emitEvent(evt, args); 705 }; 706 707 /** 708 * Sets the current value to check against when executing listeners. If a 709 * listeners return value matches the one set here then it will be removed 710 * after execution. This value defaults to true. 711 * 712 * @param {*} value The new value to check for when executing listeners. 713 * @return {Object} Current instance of EventEmitter for chaining. 714 */ 715 proto.setOnceReturnValue = function setOnceReturnValue(value) { 716 this._onceReturnValue = value; 717 return this; 718 }; 719 720 /** 721 * Fetches the current value to check against when executing listeners. If 722 * the listeners return value matches this one then it should be removed 723 * automatically. It will return true by default. 724 * 725 * @return {*|Boolean} The current value to check for or the default, true. 726 * @api private 727 */ 728 proto._getOnceReturnValue = function _getOnceReturnValue() { 729 if (this.hasOwnProperty('_onceReturnValue')) { 730 return this._onceReturnValue; 731 } 732 else { 733 return true; 734 } 735 }; 736 737 /** 738 * Fetches the events object and creates one if required. 739 * 740 * @return {Object} The events storage object. 741 * @api private 742 */ 743 proto._getEvents = function _getEvents() { 744 return this._events || (this._events = {}); 745 }; 746 747 /** 748 * Reverts the global {@link EventEmitter} to its previous value and returns a reference to this version. 749 * 750 * @return {Function} Non conflicting EventEmitter class. 751 */ 752 EventEmitter.noConflict = function noConflict() { 753 exports.EventEmitter = originalGlobalValue; 754 return EventEmitter; 755 }; 756 757 // Expose the class either via AMD, CommonJS or the global object 758 if (typeof define === 'function' && define.amd) { 759 define('eventEmitter/EventEmitter',[],function () { 760 return EventEmitter; 761 }); 762 } 763 else if (typeof module === 'object' && module.exports){ 764 module.exports = EventEmitter; 765 } 766 else { 767 this.EventEmitter = EventEmitter; 768 } 769}.call(this)); 770 771/*! 772 * getStyleProperty v1.0.3 773 * original by kangax 774 * http://perfectionkills.com/feature-testing-css-properties/ 775 */ 776 777/*jshint browser: true, strict: true, undef: true */ 778/*global define: false, exports: false, module: false */ 779 780( function( window ) { 781 782 783 784var prefixes = 'Webkit Moz ms Ms O'.split(' '); 785var docElemStyle = document.documentElement.style; 786 787function getStyleProperty( propName ) { 788 if ( !propName ) { 789 return; 790 } 791 792 // test standard property first 793 if ( typeof docElemStyle[ propName ] === 'string' ) { 794 return propName; 795 } 796 797 // capitalize 798 propName = propName.charAt(0).toUpperCase() + propName.slice(1); 799 800 // test vendor specific properties 801 var prefixed; 802 for ( var i=0, len = prefixes.length; i < len; i++ ) { 803 prefixed = prefixes[i] + propName; 804 if ( typeof docElemStyle[ prefixed ] === 'string' ) { 805 return prefixed; 806 } 807 } 808} 809 810// transport 811if ( typeof define === 'function' && define.amd ) { 812 // AMD 813 define( 'get-style-property/get-style-property',[],function() { 814 return getStyleProperty; 815 }); 816} else if ( typeof exports === 'object' ) { 817 // CommonJS for Component 818 module.exports = getStyleProperty; 819} else { 820 // browser global 821 window.getStyleProperty = getStyleProperty; 822} 823 824})( window ); 825 826/** 827 * getSize v1.1.7 828 * measure size of elements 829 */ 830 831/*jshint browser: true, strict: true, undef: true, unused: true */ 832/*global define: false, exports: false, require: false, module: false */ 833 834( function( window, undefined ) { 835 836 837 838// -------------------------- helpers -------------------------- // 839 840var getComputedStyle = window.getComputedStyle; 841var getStyle = getComputedStyle ? 842 function( elem ) { 843 return getComputedStyle( elem, null ); 844 } : 845 function( elem ) { 846 return elem.currentStyle; 847 }; 848 849// get a number from a string, not a percentage 850function getStyleSize( value ) { 851 var num = parseFloat( value ); 852 // not a percent like '100%', and a number 853 var isValid = value.indexOf('%') === -1 && !isNaN( num ); 854 return isValid && num; 855} 856 857// -------------------------- measurements -------------------------- // 858 859var measurements = [ 860 'paddingLeft', 861 'paddingRight', 862 'paddingTop', 863 'paddingBottom', 864 'marginLeft', 865 'marginRight', 866 'marginTop', 867 'marginBottom', 868 'borderLeftWidth', 869 'borderRightWidth', 870 'borderTopWidth', 871 'borderBottomWidth' 872]; 873 874function getZeroSize() { 875 var size = { 876 width: 0, 877 height: 0, 878 innerWidth: 0, 879 innerHeight: 0, 880 outerWidth: 0, 881 outerHeight: 0 882 }; 883 for ( var i=0, len = measurements.length; i < len; i++ ) { 884 var measurement = measurements[i]; 885 size[ measurement ] = 0; 886 } 887 return size; 888} 889 890 891 892function defineGetSize( getStyleProperty ) { 893 894// -------------------------- box sizing -------------------------- // 895 896var boxSizingProp = getStyleProperty('boxSizing'); 897var isBoxSizeOuter; 898 899/** 900 * WebKit measures the outer-width on style.width on border-box elems 901 * IE & Firefox measures the inner-width 902 */ 903( function() { 904 if ( !boxSizingProp ) { 905 return; 906 } 907 908 var div = document.createElement('div'); 909 div.style.width = '200px'; 910 div.style.padding = '1px 2px 3px 4px'; 911 div.style.borderStyle = 'solid'; 912 div.style.borderWidth = '1px 2px 3px 4px'; 913 div.style[ boxSizingProp ] = 'border-box'; 914 915 var body = document.body || document.documentElement; 916 body.appendChild( div ); 917 var style = getStyle( div ); 918 919 isBoxSizeOuter = getStyleSize( style.width ) === 200; 920 body.removeChild( div ); 921})(); 922 923 924// -------------------------- getSize -------------------------- // 925 926function getSize( elem ) { 927 // use querySeletor if elem is string 928 if ( typeof elem === 'string' ) { 929 elem = document.querySelector( elem ); 930 } 931 932 // do not proceed on non-objects 933 if ( !elem || typeof elem !== 'object' || !elem.nodeType ) { 934 return; 935 } 936 937 var style = getStyle( elem ); 938 939 // if hidden, everything is 0 940 if ( style.display === 'none' ) { 941 return getZeroSize(); 942 } 943 944 var size = {}; 945 size.width = elem.offsetWidth; 946 size.height = elem.offsetHeight; 947 948 var isBorderBox = size.isBorderBox = !!( boxSizingProp && 949 style[ boxSizingProp ] && style[ boxSizingProp ] === 'border-box' ); 950 951 // get all measurements 952 for ( var i=0, len = measurements.length; i < len; i++ ) { 953 var measurement = measurements[i]; 954 var value = style[ measurement ]; 955 value = mungeNonPixel( elem, value ); 956 var num = parseFloat( value ); 957 // any 'auto', 'medium' value will be 0 958 size[ measurement ] = !isNaN( num ) ? num : 0; 959 } 960 961 var paddingWidth = size.paddingLeft + size.paddingRight; 962 var paddingHeight = size.paddingTop + size.paddingBottom; 963 var marginWidth = size.marginLeft + size.marginRight; 964 var marginHeight = size.marginTop + size.marginBottom; 965 var borderWidth = size.borderLeftWidth + size.borderRightWidth; 966 var borderHeight = size.borderTopWidth + size.borderBottomWidth; 967 968 var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter; 969 970 // overwrite width and height if we can get it from style 971 var styleWidth = getStyleSize( style.width ); 972 if ( styleWidth !== false ) { 973 size.width = styleWidth + 974 // add padding and border unless it's already including it 975 ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth ); 976 } 977 978 var styleHeight = getStyleSize( style.height ); 979 if ( styleHeight !== false ) { 980 size.height = styleHeight + 981 // add padding and border unless it's already including it 982 ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight ); 983 } 984 985 size.innerWidth = size.width - ( paddingWidth + borderWidth );
986 size.innerHeight = size.height - ( paddingHeight + borderHeight ); 987 988 size.outerWidth = size.width + marginWidth; 989 size.outerHeight = size.height + marginHeight; 990 991 return size; 992} 993 994// IE8 returns percent values, not pixels 995// taken from jQuery's curCSS 996function mungeNonPixel( elem, value ) { 997 // IE8 and has percent value 998 if ( getComputedStyle || value.indexOf('%') === -1 ) { 999 return value; 1000 } 1001 var style = elem.style; 1002 // Remember the original values 1003 var left = style.left; 1004 var rs = elem.runtimeStyle; 1005 var rsLeft = rs && rs.left; 1006 1007 // Put in the new values to get a computed value out 1008 if ( rsLeft ) { 1009 rs.left = elem.currentStyle.left; 1010 } 1011 style.left = value; 1012 value = style.pixelLeft; 1013 1014 // Revert the changed values 1015 style.left = left; 1016 if ( rsLeft ) { 1017 rs.left = rsLeft; 1018 } 1019 1020 return value; 1021} 1022 1023return getSize; 1024 1025} 1026 1027// transport 1028if ( typeof define === 'function' && define.amd ) { 1029 // AMD for RequireJS 1030 define( 'get-size/get-size',[ 'get-style-property/get-style-property' ], defineGetSize ); 1031} else if ( typeof exports === 'object' ) { 1032 // CommonJS for Component 1033 module.exports = defineGetSize( require('get-style-property') ); 1034} else { 1035 // browser global 1036 window.getSize = defineGetSize( window.getStyleProperty ); 1037} 1038 1039})( window ); 1040 1041/** 1042 * matchesSelector helper v1.0.1 1043 * 1044 * @name matchesSelector 1045 * @param {Element} elem 1046 * @param {String} selector 1047 */ 1048 1049/*jshint browser: true, strict: true, undef: true, unused: true */ 1050/*global define: false */ 1051 1052( function( global, ElemProto ) { 1053 1054 1055 1056 var matchesMethod = ( function() { 1057 // check un-prefixed 1058 if ( ElemProto.matchesSelector ) { 1059 return 'matchesSelector'; 1060 } 1061 // check vendor prefixes 1062 var prefixes = [ 'webkit', 'moz', 'ms', 'o' ]; 1063 1064 for ( var i=0, len = prefixes.length; i < len; i++ ) { 1065 var prefix = prefixes[i]; 1066 var method = prefix + 'MatchesSelector'; 1067 if ( ElemProto[ method ] ) { 1068 return method; 1069 } 1070 } 1071 })(); 1072 1073 // ----- match ----- // 1074 1075 function match( elem, selector ) { 1076 return elem[ matchesMethod ]( selector ); 1077 } 1078 1079 // ----- appendToFragment ----- // 1080 1081 function checkParent( elem ) { 1082 // not needed if already has parent 1083 if ( elem.parentNode ) { 1084 return; 1085 } 1086 var fragment = document.createDocumentFragment(); 1087 fragment.appendChild( elem ); 1088 } 1089 1090 // ----- query ----- // 1091 1092 // fall back to using QSA 1093 // thx @jonathantneal https://gist.github.com/3062955 1094 function query( elem, selector ) { 1095 // append to fragment if no parent 1096 checkParent( elem ); 1097 1098 // match elem with all selected elems of parent 1099 var elems = elem.parentNode.querySelectorAll( selector ); 1100 for ( var i=0, len = elems.length; i < len; i++ ) { 1101 // return true if match 1102 if ( elems[i] === elem ) { 1103 return true; 1104 } 1105 } 1106 // otherwise return false 1107 return false; 1108 } 1109 1110 // ----- matchChild ----- // 1111 1112 function matchChild( elem, selector ) { 1113 checkParent( elem ); 1114 return match( elem, selector ); 1115 } 1116 1117 // ----- matchesSelector ----- // 1118 1119 var matchesSelector; 1120 1121 if ( matchesMethod ) { 1122 // IE9 supports matchesSelector, but doesn't work on orphaned elems 1123 // check for that 1124 var div = document.createElement('div'); 1125 var supportsOrphans = match( div, 'div' ); 1126 matchesSelector = supportsOrphans ? match : matchChild; 1127 } else { 1128 matchesSelector = query; 1129 } 1130 1131 // transport 1132 if ( typeof define === 'function' && define.amd ) { 1133 // AMD 1134 define( 'matches-selector/matches-selector',[],function() { 1135 return matchesSelector; 1136 }); 1137 } else { 1138 // browser global 1139 window.matchesSelector = matchesSelector; 1140 } 1141 1142})( this, Element.prototype ); 1143 1144/** 1145 * Outlayer Item 1146 */ 1147 1148( function( window ) { 1149 1150 1151 1152// ----- get style ----- // 1153 1154var getComputedStyle = window.getComputedStyle; 1155var getStyle = getComputedStyle ? 1156 function( elem ) { 1157 return getComputedStyle( elem, null ); 1158 } : 1159 function( elem ) { 1160 return elem.currentStyle; 1161 }; 1162 1163 1164// extend objects 1165function extend( a, b ) { 1166 for ( var prop in b ) { 1167 a[ prop ] = b[ prop ]; 1168 } 1169 return a; 1170} 1171 1172function isEmptyObj( obj ) { 1173 for ( var prop in obj ) { 1174 return false; 1175 } 1176 prop = null; 1177 return true; 1178} 1179 1180// http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/ 1181function toDash( str ) { 1182 return str.replace( /([A-Z])/g, function( $1 ){ 1183 return '-' + $1.toLowerCase(); 1184 }); 1185} 1186 1187// -------------------------- Outlayer definition -------------------------- // 1188 1189function outlayerItemDefinition( EventEmitter, getSize, getStyleProperty ) { 1190 1191// -------------------------- CSS3 support -------------------------- // 1192 1193var transitionProperty = getStyleProperty('transition'); 1194var transformProperty = getStyleProperty('transform'); 1195var supportsCSS3 = transitionProperty && transformProperty; 1196var is3d = !!getStyleProperty('perspective'); 1197 1198var transitionEndEvent = { 1199 WebkitTransition: 'webkitTransitionEnd', 1200 MozTransition: 'transitionend', 1201 OTransition: 'otransitionend', 1202 transition: 'transitionend' 1203}[ transitionProperty ]; 1204 1205// properties that could have vendor prefix 1206var prefixableProperties = [ 1207 'transform',
1208 'transition', 1209 'transitionDuration', 1210 'transitionProperty' 1211]; 1212 1213// cache all vendor properties 1214var vendorProperties = ( function() { 1215 var cache = {}; 1216 for ( var i=0, len = prefixableProperties.length; i < len; i++ ) { 1217 var prop = prefixableProperties[i]; 1218 var supportedProp = getStyleProperty( prop ); 1219 if ( supportedProp && supportedProp !== prop ) { 1220 cache[ prop ] = supportedProp; 1221 } 1222 } 1223 return cache; 1224})(); 1225 1226// -------------------------- Item -------------------------- // 1227 1228function Item( element, layout ) { 1229 if ( !element ) { 1230 return; 1231 } 1232 1233 this.element = element; 1234 // parent layout class, i.e. Masonry, Isotope, or Packery 1235 this.layout = layout; 1236 this.position = { 1237 x: 0, 1238 y: 0 1239 }; 1240 1241 this._create(); 1242} 1243 1244// inherit EventEmitter 1245extend( Item.prototype, EventEmitter.prototype ); 1246 1247Item.prototype._create = function() { 1248 // transition objects 1249 this._transn = { 1250 ingProperties: {}, 1251 clean: {}, 1252 onEnd: {} 1253 }; 1254 1255 this.css({ 1256 position: 'absolute' 1257 }); 1258}; 1259 1260// trigger specified handler for event type 1261Item.prototype.handleEvent = function( event ) { 1262 var method = 'on' + event.type; 1263 if ( this[ method ] ) { 1264 this[ method ]( event ); 1265 } 1266}; 1267 1268Item.prototype.getSize = function() { 1269 this.size = getSize( this.element ); 1270}; 1271 1272/** 1273 * apply CSS styles to element 1274 * @param {Object} style 1275 */ 1276Item.prototype.css = function( style ) { 1277 var elemStyle = this.element.style; 1278 1279 for ( var prop in style ) { 1280 // use vendor property if available 1281 var supportedProp = vendorProperties[ prop ] || prop; 1282 elemStyle[ supportedProp ] = style[ prop ]; 1283 } 1284}; 1285 1286 // measure position, and sets it 1287Item.prototype.getPosition = function() { 1288 var style = getStyle( this.element ); 1289 var layoutOptions = this.layout.options; 1290 var isOriginLeft = layoutOptions.isOriginLeft; 1291 var isOriginTop = layoutOptions.isOriginTop; 1292 var x = parseInt( style[ isOriginLeft ? 'left' : 'right' ], 10 ); 1293 var y = parseInt( style[ isOriginTop ? 'top' : 'bottom' ], 10 ); 1294 1295 // clean up 'auto' or other non-integer values 1296 x = isNaN( x ) ? 0 : x; 1297 y = isNaN( y ) ? 0 : y; 1298 // remove padding from measurement 1299 var layoutSize = this.layout.size; 1300 x -= isOriginLeft ? layoutSize.paddingLeft : layoutSize.paddingRight; 1301 y -= isOriginTop ? layoutSize.paddingTop : layoutSize.paddingBottom; 1302 1303 this.position.x = x; 1304 this.position.y = y; 1305}; 1306 1307// set settled position, apply padding 1308Item.prototype.layoutPosition = function() { 1309 var layoutSize = this.layout.size; 1310 var layoutOptions = this.layout.options; 1311 var style = {}; 1312 1313 if ( layoutOptions.isOriginLeft ) { 1314 style.left = ( this.position.x + layoutSize.paddingLeft ) + 'px'; 1315 // reset other property 1316 style.right = ''; 1317 } else { 1318 style.right = ( this.position.x + layoutSize.paddingRight ) + 'px'; 1319 style.left = ''; 1320 } 1321 1322 if ( layoutOptions.isOriginTop ) { 1323 style.top = ( this.position.y + layoutSize.paddingTop ) + 'px'; 1324 style.bottom = ''; 1325 } else { 1326 style.bottom = ( this.position.y + layoutSize.paddingBottom ) + 'px'; 1327 style.top = ''; 1328 } 1329 1330 this.css( style ); 1331 this.emitEvent( 'layout', [ this ] ); 1332}; 1333 1334 1335// transform translate function 1336var translate = is3d ? 1337 function( x, y ) { 1338 return 'translate3d(' + x + 'px, ' + y + 'px, 0)'; 1339 } : 1340 function( x, y ) { 1341 return 'translate(' + x + 'px, ' + y + 'px)'; 1342 }; 1343 1344 1345Item.prototype._transitionTo = function( x, y ) { 1346 this.getPosition(); 1347 // get current x & y from top/left 1348 var curX = this.position.x; 1349 var curY = this.position.y; 1350 1351 var compareX = parseInt( x, 10 ); 1352 var compareY = parseInt( y, 10 ); 1353 var didNotMove = compareX === this.position.x && compareY === this.position.y; 1354 1355 // save end position 1356 this.setPosition( x, y ); 1357 1358 // if did not move and not transitioning, just go to layout 1359 if ( didNotMove && !this.isTransitioning ) { 1360 this.layoutPosition(); 1361 return; 1362 } 1363 1364 var transX = x - curX; 1365 var transY = y - curY; 1366 var transitionStyle = {}; 1367 // flip cooridinates if origin on right or bottom 1368 var layoutOptions = this.layout.options; 1369 transX = layoutOptions.isOriginLeft ? transX : -transX; 1370 transY = layoutOptions.isOriginTop ? transY : -transY; 1371 transitionStyle.transform = translate( transX, transY ); 1372 1373 this.transition({ 1374 to: transitionStyle, 1375 onTransitionEnd: { 1376 transform: this.layoutPosition 1377 }, 1378 isCleaning: true 1379 }); 1380}; 1381 1382// non transition + transform support 1383Item.prototype.goTo = function( x, y ) { 1384 this.setPosition( x, y ); 1385 this.layoutPosition(); 1386}; 1387 1388// use transition and transforms if supported 1389Item.prototype.moveTo = supportsCSS3 ? 1390 Item.prototype._transitionTo : Item.prototype.goTo; 1391 1392Item.prototype.setPosition = function( x, y ) { 1393 this.position.x = parseInt( x, 10 ); 1394 this.position.y = parseInt( y, 10 ); 1395};
vendor: 4,507 bytes, lines 1396-1538
1396 1397// ----- transition ----- // 1398 1399/** 1400 * @param {Object} style - CSS 1401 * @param {Function} onTransitionEnd 1402 */ 1403 1404// non transition, just trigger callback 1405Item.prototype._nonTransition = function( args ) { 1406 this.css( args.to ); 1407 if ( args.isCleaning ) { 1408 this._removeStyles( args.to ); 1409 } 1410 for ( var prop in args.onTransitionEnd ) { 1411 args.onTransitionEnd[ prop ].call( this ); 1412 } 1413}; 1414 1415/** 1416 * proper transition 1417 * @param {Object} args - arguments 1418 * @param {Object} to - style to transition to 1419 * @param {Object} from - style to start transition from 1420 * @param {Boolean} isCleaning - removes transition styles after transition 1421 * @param {Function} onTransitionEnd - callback 1422 */ 1423Item.prototype._transition = function( args ) { 1424 // redirect to nonTransition if no transition duration 1425 if ( !parseFloat( this.layout.options.transitionDuration ) ) { 1426 this._nonTransition( args ); 1427 return; 1428 } 1429 1430 var _transition = this._transn; 1431 // keep track of onTransitionEnd callback by css property 1432 for ( var prop in args.onTransitionEnd ) { 1433 _transition.onEnd[ prop ] = args.onTransitionEnd[ prop ]; 1434 } 1435 // keep track of properties that are transitioning 1436 for ( prop in args.to ) { 1437 _transition.ingProperties[ prop ] = true; 1438 // keep track of properties to clean up when transition is done 1439 if ( args.isCleaning ) { 1440 _transition.clean[ prop ] = true; 1441 } 1442 } 1443 1444 // set from styles 1445 if ( args.from ) { 1446 this.css( args.from ); 1447 // force redraw. http://blog.alexmaccaw.com/css-transitions 1448 var h = this.element.offsetHeight; 1449 // hack for JSHint to hush about unused var 1450 h = null; 1451 } 1452 // enable transition 1453 this.enableTransition( args.to ); 1454 // set styles that are transitioning 1455 this.css( args.to ); 1456 1457 this.isTransitioning = true; 1458 1459}; 1460 1461var itemTransitionProperties = transformProperty && ( toDash( transformProperty ) + 1462 ',opacity' ); 1463 1464Item.prototype.enableTransition = function(/* style */) { 1465 // only enable if not already transitioning 1466 // bug in IE10 were re-setting transition style will prevent 1467 // transitionend event from triggering 1468 if ( this.isTransitioning ) { 1469 return; 1470 } 1471 1472 // make transition: foo, bar, baz from style object 1473 // TODO uncomment this bit when IE10 bug is resolved 1474 // var transitionValue = []; 1475 // for ( var prop in style ) { 1476 // // dash-ify camelCased properties like WebkitTransition 1477 // transitionValue.push( toDash( prop ) ); 1478 // } 1479 // enable transition styles 1480 // HACK always enable transform,opacity for IE10 1481 this.css({ 1482 transitionProperty: itemTransitionProperties, 1483 transitionDuration: this.layout.options.transitionDuration 1484 }); 1485 // listen for transition end event 1486 this.element.addEventListener( transitionEndEvent, this, false ); 1487}; 1488 1489Item.prototype.transition = Item.prototype[ transitionProperty ? '_transition' : '_nonTransition' ]; 1490 1491// ----- events ----- // 1492 1493Item.prototype.onwebkitTransitionEnd = function( event ) { 1494 this.ontransitionend( event ); 1495}; 1496 1497Item.prototype.onotransitionend = function( event ) { 1498 this.ontransitionend( event ); 1499}; 1500 1501// properties that I munge to make my life easier 1502var dashedVendorProperties = { 1503 '-webkit-transform': 'transform', 1504 '-moz-transform': 'transform', 1505 '-o-transform': 'transform' 1506}; 1507 1508Item.prototype.ontransitionend = function( event ) { 1509 // disregard bubbled events from children 1510 if ( event.target !== this.element ) { 1511 return; 1512 } 1513 var _transition = this._transn; 1514 // get property name of transitioned property, convert to prefix-free 1515 var propertyName = dashedVendorProperties[ event.propertyName ] || event.propertyName; 1516 1517 // remove property that has completed transitioning 1518 delete _transition.ingProperties[ propertyName ]; 1519 // check if any properties are still transitioning 1520 if ( isEmptyObj( _transition.ingProperties ) ) { 1521 // all properties have completed transitioning 1522 this.disableTransition(); 1523 } 1524 // clean style 1525 if ( propertyName in _transition.clean ) { 1526 // clean up style 1527 this.element.style[ event.propertyName ] = ''; 1528 delete _transition.clean[ propertyName ]; 1529 } 1530 // trigger onTransitionEnd callback 1531 if ( propertyName in _transition.onEnd ) { 1532 var onTransitionEnd = _transition.onEnd[ propertyName ]; 1533 onTransitionEnd.call( this ); 1534 delete _transition.onEnd[ propertyName ]; 1535 } 1536 1537 this.emitEvent( 'transitionEnd', [ this ] ); 1538};
1539 1540Item.prototype.disableTransition = function() { 1541 this.removeTransitionStyles(); 1542 this.element.removeEventListener( transitionEndEvent, this, false ); 1543 this.isTransitioning = false; 1544}; 1545 1546/** 1547 * removes style property from element 1548 * @param {Object} style 1549**/ 1550Item.prototype._removeStyles = function( style ) { 1551 // clean up transition styles 1552 var cleanStyle = {}; 1553 for ( var prop in style ) { 1554 cleanStyle[ prop ] = ''; 1555 } 1556 this.css( cleanStyle ); 1557}; 1558 1559var cleanTransitionStyle = { 1560 transitionProperty: '', 1561 transitionDuration: '' 1562}; 1563 1564Item.prototype.removeTransitionStyles = function() { 1565 // remove transition 1566 this.css( cleanTransitionStyle ); 1567}; 1568 1569// ----- show/hide/remove ----- // 1570 1571// remove element from DOM 1572Item.prototype.removeElem = function() { 1573 this.element.parentNode.removeChild( this.element ); 1574 this.emitEvent( 'remove', [ this ] ); 1575}; 1576 1577Item.prototype.remove = function() { 1578 // just remove element if no transition support or no transition 1579 if ( !transitionProperty || !parseFloat( this.layout.options.transitionDuration ) ) { 1580 this.removeElem(); 1581 return; 1582 } 1583 1584 // start transition 1585 var _this = this; 1586 this.on( 'transitionEnd', function() { 1587 _this.removeElem(); 1588 return true; // bind once 1589 }); 1590 this.hide(); 1591}; 1592 1593Item.prototype.reveal = function() { 1594 delete this.isHidden; 1595 // remove display: none 1596 this.css({ display: '' }); 1597 1598 var options = this.layout.options; 1599 this.transition({ 1600 from: options.hiddenStyle, 1601 to: options.visibleStyle, 1602 isCleaning: true 1603 }); 1604}; 1605 1606Item.prototype.hide = function() { 1607 // set flag 1608 this.isHidden = true; 1609 // remove display: none 1610 this.css({ display: '' }); 1611 1612 var options = this.layout.options; 1613 this.transition({ 1614 from: options.visibleStyle, 1615 to: options.hiddenStyle, 1616 // keep hidden stuff hidden 1617 isCleaning: true, 1618 onTransitionEnd: { 1619 opacity: function() { 1620 // check if still hidden 1621 // during transition, item may have been un-hidden 1622 if ( this.isHidden ) { 1623 this.css({ display: 'none' }); 1624 } 1625 } 1626 } 1627 }); 1628}; 1629 1630Item.prototype.destroy = function() { 1631 this.css({ 1632 position: '', 1633 left: '', 1634 right: '', 1635 top: '', 1636 bottom: '', 1637 transition: '', 1638 transform: '' 1639 }); 1640}; 1641 1642return Item; 1643 1644} 1645 1646// -------------------------- transport -------------------------- // 1647 1648if ( typeof define === 'function' && define.amd ) { 1649 // AMD 1650 define( 'outlayer/item',[ 1651 'eventEmitter/EventEmitter', 1652 'get-size/get-size', 1653 'get-style-property/get-style-property' 1654 ], 1655 outlayerItemDefinition ); 1656} else { 1657 // browser global 1658 window.Outlayer = {}; 1659 window.Outlayer.Item = outlayerItemDefinition( 1660 window.EventEmitter, 1661 window.getSize, 1662 window.getStyleProperty 1663 ); 1664} 1665 1666})( window ); 1667 1668/*! 1669 * Outlayer v1.2.0 1670 * the brains and guts of a layout library 1671 * MIT license 1672 */ 1673 1674( function( window ) { 1675 1676 1677 1678// ----- vars ----- // 1679 1680var document = window.document; 1681var console = window.console; 1682var jQuery = window.jQuery; 1683 1684var noop = function() {}; 1685 1686// -------------------------- helpers -------------------------- // 1687 1688// extend objects 1689function extend( a, b ) { 1690 for ( var prop in b ) { 1691 a[ prop ] = b[ prop ]; 1692 } 1693 return a; 1694} 1695 1696 1697var objToString = Object.prototype.toString; 1698function isArray( obj ) { 1699 return objToString.call( obj ) === '[object Array]'; 1700} 1701 1702// turn element or nodeList into an array 1703function makeArray( obj ) { 1704 var ary = []; 1705 if ( isArray( obj ) ) { 1706 // use object if already an array 1707 ary = obj; 1708 } else if ( obj && typeof obj.length === 'number' ) { 1709 // convert nodeList to array 1710 for ( var i=0, len = obj.length; i < len; i++ ) { 1711 ary.push( obj[i] ); 1712 } 1713 } else { 1714 // array of single index 1715 ary.push( obj ); 1716 } 1717 return ary; 1718} 1719 1720// http://stackoverflow.com/a/384380/182183 1721var isElement = ( typeof HTMLElement === 'object' ) ? 1722 function isElementDOM2( obj ) { 1723 return obj instanceof HTMLElement; 1724 }
1724 : 1725 function isElementQuirky( obj ) { 1726 return obj && typeof obj === 'object' && 1727 obj.nodeType === 1 && typeof obj.nodeName === 'string'; 1728 }; 1729 1730// index of helper cause IE8 1731var indexOf = Array.prototype.indexOf ? function( ary, obj ) { 1732 return ary.indexOf( obj ); 1733 } : function( ary, obj ) { 1734 for ( var i=0, len = ary.length; i < len; i++ ) { 1735 if ( ary[i] === obj ) { 1736 return i; 1737 } 1738 } 1739 return -1; 1740 }; 1741 1742function removeFrom( obj, ary ) { 1743 var index = indexOf( ary, obj ); 1744 if ( index !== -1 ) { 1745 ary.splice( index, 1 ); 1746 } 1747} 1748 1749// http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/ 1750function toDashed( str ) { 1751 return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) { 1752 return $1 + '-' + $2; 1753 }).toLowerCase(); 1754} 1755 1756 1757function outlayerDefinition( eventie, docReady, EventEmitter, getSize, matchesSelector, Item ) { 1758 1759// -------------------------- Outlayer -------------------------- // 1760 1761// globally unique identifiers 1762var GUID = 0; 1763// internal store of all Outlayer intances 1764var instances = {}; 1765 1766 1767/** 1768 * @param {Element, String} element 1769 * @param {Object} options 1770 * @constructor 1771 */ 1772function Outlayer( element, options ) { 1773 // use element as selector string 1774 if ( typeof element === 'string' ) { 1775 element = document.querySelector( element ); 1776 } 1777 1778 // bail out if not proper element 1779 if ( !element || !isElement( element ) ) { 1780 if ( console ) { 1781 console.error( 'Bad ' + this.constructor.namespace + ' element: ' + element ); 1782 } 1783 return; 1784 } 1785 1786 this.element = element; 1787 1788 // options 1789 this.options = extend( {}, this.constructor.defaults ); 1790 this.option( options ); 1791 1792 // add id for Outlayer.getFromElement 1793 var id = ++GUID; 1794 this.element.outlayerGUID = id; // expando 1795 instances[ id ] = this; // associate via id 1796 1797 // kick it off 1798 this._create(); 1799 1800 if ( this.options.isInitLayout ) { 1801 this.layout(); 1802 } 1803} 1804 1805// settings are for internal use only 1806Outlayer.namespace = 'outlayer'; 1807Outlayer.Item = Item; 1808 1809// default options 1810Outlayer.defaults = { 1811 containerStyle: { 1812 position: 'relative' 1813 }, 1814 isInitLayout: true, 1815 isOriginLeft: true, 1816 isOriginTop: true, 1817 isResizeBound: true, 1818 isResizingContainer: true, 1819 // item options 1820 transitionDuration: '0.4s', 1821 hiddenStyle: { 1822 opacity: 0, 1823 transform: 'scale(0.001)' 1824 }, 1825 visibleStyle: { 1826 opacity: 1, 1827 transform: 'scale(1)' 1828 } 1829}; 1830 1831// inherit EventEmitter 1832extend( Outlayer.prototype, EventEmitter.prototype ); 1833 1834/** 1835 * set options 1836 * @param {Object} opts 1837 */ 1838Outlayer.prototype.option = function( opts ) { 1839 extend( this.options, opts ); 1840}; 1841 1842Outlayer.prototype._create = function() { 1843 // get items from children 1844 this.reloadItems(); 1845 // elements that affect layout, but are not laid out 1846 this.stamps = []; 1847 this.stamp( this.options.stamp ); 1848 // set container style 1849 extend( this.element.style, this.options.containerStyle ); 1850 1851 // bind resize method 1852 if ( this.options.isResizeBound ) { 1853 this.bindResize(); 1854 } 1855}; 1856 1857// goes through all children again and gets bricks in proper order 1858Outlayer.prototype.reloadItems = function() { 1859 // collection of item elements 1860 this.items = this._itemize( this.element.children ); 1861}; 1862 1863 1864/** 1865 * turn elements into Outlayer.Items to be used in layout 1866 * @param {Array or NodeList or HTMLElement} elems 1867 * @returns {Array} items - collection of new Outlayer Items 1868 */ 1869Outlayer.prototype._itemize = function( elems ) { 1870 1871 var itemElems = this._filterFindItemElements( elems ); 1872 var Item = this.constructor.Item; 1873 1874 // create new Outlayer Items for collection 1875 var items = []; 1876 for ( var i=0, len = itemElems.length; i < len; i++ ) { 1877 var elem = itemElems[i]; 1878 var item = new Item( elem, this ); 1879 items.push( item ); 1880 } 1881 1882 return items; 1883}; 1884 1885/** 1886 * get item elements to be used in layout 1887 * @param {Array or NodeList or HTMLElement} elems 1888 * @returns {Array} items - item elements 1889 */ 1890Outlayer.prototype._filterFindItemElements = function( elems ) { 1891 // make array of elems 1892 elems = makeArray( elems ); 1893 var itemSelector = this.options.itemSelector; 1894 var itemElems = []; 1895 1896 for ( var i=0, len = elems.length; i < len; i++ ) { 1897 var elem = elems[i]; 1898 // check that elem is an actual element 1899 if ( !isElement( elem ) ) { 1900 continue; 1901 } 1902 // filter & find items if we have an item selector 1903 if ( itemSelector ) { 1904 // filter siblings 1905 if ( matchesSelector( elem, itemSelector ) ) { 1906 itemElems.push( elem ); 1907 } 1908 // find children 1909 var childElems = elem.querySelectorAll( itemSelector ); 1910 // concat childElems to filterFound array 1911 for ( var j=0, jLen = childElems.length; j < jLen; j++ ) { 1912 itemElems.push( childElems[j] ); 1913 } 1914 } else { 1915 itemElems.push( elem ); 1916 } 1917 } 1918 1919 return itemElems; 1920}; 1921 1922/** 1923 * getter method for getting item elements 1924 * @returns {Array} elems - collection of item elements 1925 */ 1926Outlayer.prototype.getItemElements = function() { 1927 var elems = []; 1928 for ( var i=0, len = this.items.length; i < len; i++ ) { 1929 elems.push( this.items[i].element ); 1930 } 1931 return elems; 1932}; 1933 1934// ----- init & layout ----- // 1935 1936/** 1937 * lays out all items 1938 */ 1939Outlayer.prototype.layout = function() { 1940 this._resetLayout(); 1941 this._manageStamps(); 1942 1943 // don't animate first layout 1944 var isInstant = this.options.isLayoutInstant !== undefined ? 1945 this.options.isLayoutInstant : !this._isLayoutInited; 1946 this.layoutItems( this.items, isInstant ); 1947 1948 // flag for initalized 1949 this._isLayoutInited = true; 1950}; 1951 1952// _init is alias for layout 1953Outlayer.prototype._init = Outlayer.prototype.layout; 1954 1955/** 1956 * logic before any new layout 1957 */ 1958Outlayer.prototype._resetLayout = function() {
1959 this.getSize(); 1960}; 1961 1962 1963Outlayer.prototype.getSize = function() { 1964 this.size = getSize( this.element ); 1965}; 1966 1967/** 1968 * get measurement from option, for columnWidth, rowHeight, gutter 1969 * if option is String -> get element from selector string, & get size of element 1970 * if option is Element -> get size of element 1971 * else use option as a number 1972 * 1973 * @param {String} measurement 1974 * @param {String} size - width or height 1975 * @private 1976 */ 1977Outlayer.prototype._getMeasurement = function( measurement, size ) { 1978 var option = this.options[ measurement ]; 1979 var elem; 1980 if ( !option ) { 1981 // default to 0 1982 this[ measurement ] = 0; 1983 } else { 1984 // use option as an element 1985 if ( typeof option === 'string' ) { 1986 elem = this.element.querySelector( option ); 1987 } else if ( isElement( option ) ) { 1988 elem = option; 1989 } 1990 // use size of element, if element 1991 this[ measurement ] = elem ? getSize( elem )[ size ] : option; 1992 } 1993}; 1994 1995/** 1996 * layout a collection of item elements 1997 * @api public 1998 */ 1999Outlayer.prototype.layoutItems = function( items, isInstant ) { 2000 items = this._getItemsForLayout( items ); 2001 2002 this._layoutItems( items, isInstant ); 2003 2004 this._postLayout(); 2005}; 2006 2007/** 2008 * get the items to be laid out 2009 * you may want to skip over some items 2010 * @param {Array} items 2011 * @returns {Array} items 2012 */ 2013Outlayer.prototype._getItemsForLayout = function( items ) { 2014 var layoutItems = []; 2015 for ( var i=0, len = items.length; i < len; i++ ) { 2016 var item = items[i]; 2017 if ( !item.isIgnored ) { 2018 layoutItems.push( item ); 2019 } 2020 } 2021 return layoutItems; 2022}; 2023 2024/** 2025 * layout items 2026 * @param {Array} items 2027 * @param {Boolean} isInstant 2028 */ 2029Outlayer.prototype._layoutItems = function( items, isInstant ) { 2030 var _this = this; 2031 function onItemsLayout() { 2032 _this.emitEvent( 'layoutComplete', [ _this, items ] ); 2033 } 2034 2035 if ( !items || !items.length ) { 2036 // no items, emit event with empty array 2037 onItemsLayout(); 2038 return; 2039 } 2040 2041 // emit layoutComplete when done 2042 this._itemsOn( items, 'layout', onItemsLayout ); 2043 2044 var queue = []; 2045 2046 for ( var i=0, len = items.length; i < len; i++ ) { 2047 var item = items[i]; 2048 // get x/y object from method 2049 var position = this._getItemLayoutPosition( item ); 2050 // enqueue 2051 position.item = item; 2052 position.isInstant = isInstant || item.isLayoutInstant; 2053 queue.push( position ); 2054 } 2055 2056 this._processLayoutQueue( queue ); 2057}; 2058 2059/** 2060 * get item layout position 2061 * @param {Outlayer.Item} item 2062 * @returns {Object} x and y position 2063 */ 2064Outlayer.prototype._getItemLayoutPosition = function( /* item */ ) { 2065 return { 2066 x: 0, 2067 y: 0 2068 }; 2069}; 2070 2071/** 2072 * iterate over array and position each item 2073 * Reason being - separating this logic prevents 'layout invalidation' 2074 * thx @paul_irish 2075 * @param {Array} queue 2076 */ 2077Outlayer.prototype._processLayoutQueue = function( queue ) { 2078 for ( var i=0, len = queue.length; i < len; i++ ) { 2079 var obj = queue[i]; 2080 this._positionItem( obj.item, obj.x, obj.y, obj.isInstant ); 2081 } 2082}; 2083 2084/** 2085 * Sets position of item in DOM 2086 * @param {Outlayer.Item} item 2087 * @param {Number} x - horizontal position 2088 * @param {Number} y - vertical position 2089 * @param {Boolean} isInstant - disables transitions 2090 */ 2091Outlayer.prototype._positionItem = function( item, x, y, isInstant ) { 2092 if ( isInstant ) { 2093 // if not transition, just set CSS 2094 item.goTo( x, y ); 2095 } else { 2096 item.moveTo( x, y ); 2097 } 2098}; 2099 2100/** 2101 * Any logic you want to do after each layout, 2102 * i.e. size the container 2103 */ 2104Outlayer.prototype._postLayout = function() { 2105 this.resizeContainer(); 2106}; 2107 2108Outlayer.prototype.resizeContainer = function() { 2109 if ( !this.options.isResizingContainer ) { 2110 return; 2111 } 2112 var size = this._getContainerSize(); 2113 if ( size ) { 2114 this._setContainerMeasure( size.width, true ); 2115 this._setContainerMeasure( size.height, false ); 2116 } 2117}; 2118 2119/** 2120 * Sets width or height of container if returned 2121 * @returns {Object} size 2122 * @param {Number} width 2123 * @param {Number} height 2124 */ 2125Outlayer.prototype._getContainerSize = noop; 2126 2127/** 2128 * @param {Number} measure - size of width or height 2129 * @param {Boolean} isWidth 2130 */ 2131Outlayer.prototype._setContainerMeasure = function( measure, isWidth ) { 2132 if ( measure === undefined ) { 2133 return; 2134 } 2135 2136 var elemSize = this.size; 2137 // add padding and border width if border box 2138 if ( elemSize.isBorderBox ) { 2139 measure += isWidth ? elemSize.paddingLeft + elemSize.paddingRight + 2140 elemSize.borderLeftWidth + elemSize.borderRightWidth : 2141 elemSize.paddingBottom + elemSize.paddingTop + 2142 elemSize.borderTopWidth + elemSize.borderBottomWidth; 2143 } 2144 2145 measure = Math.max( measure, 0 ); 2146 this.element.style[ isWidth ? 'width' : 'height' ] = measure + 'px'; 2147}; 2148 2149/** 2150 * trigger a callback for a collection of items events 2151 * @param {Array} items - Outlayer.Items 2152 * @param {String} eventName 2153 * @param {Function} callback 2154 */ 2155Outlayer.prototype._itemsOn = function( items, eventName, callback ) { 2156 var doneCount = 0; 2157 var count = items.length; 2158 // event callback 2159 var _this = this; 2160 function tick() { 2161 doneCount++; 2162 if ( doneCount === count ) { 2163 callback.call( _this ); 2164 } 2165 return true; // bind once 2166 } 2167 // bind callback 2168 for ( var i=0, len = items.length; i < len; i++ ) { 2169 var item = items[i]; 2170 item.on( eventName, tick ); 2171 } 2172}; 2173 2174// -------------------------- ignore & stamps -------------------------- // 2175 2176 2177/** 2178 * keep item in collection, but do not lay it out 2179 * ignored items do not get skipped in layout 2180 * @param {Element} elem 2181 */ 2182Outlayer.prototype.ignore = function( elem ) { 2183 var item = this.getItem( elem ); 2184 if ( item ) { 2185 item.isIgnored = true; 2186 } 2187}; 2188 2189/** 2190 * return item to layout collection 2191 * @param {Element} elem 2192 */ 2193Outlayer.prototype.unignore = function( elem ) { 2194 var item = this.getItem( elem ); 2195 if ( item ) { 2196 delete item.isIgnored; 2197 } 2198}; 2199 2200/** 2201 * adds elements to stamps 2202 * @param {NodeList, Array, Element, or String} elems 2203 */ 2204Outlayer.prototype.stamp = function( elems ) { 2205 elems = this._find( elems ); 2206 if ( !elems ) { 2207 return; 2208 } 2209
2210 this.stamps = this.stamps.concat( elems ); 2211 // ignore 2212 for ( var i=0, len = elems.length; i < len; i++ ) { 2213 var elem = elems[i]; 2214 this.ignore( elem ); 2215 } 2216}; 2217 2218/** 2219 * removes elements to stamps 2220 * @param {NodeList, Array, or Element} elems 2221 */ 2222Outlayer.prototype.unstamp = function( elems ) { 2223 elems = this._find( elems ); 2224 if ( !elems ){ 2225 return; 2226 } 2227 2228 for ( var i=0, len = elems.length; i < len; i++ ) { 2229 var elem = elems[i]; 2230 // filter out removed stamp elements 2231 removeFrom( elem, this.stamps ); 2232 this.unignore( elem ); 2233 } 2234 2235}; 2236 2237/** 2238 * finds child elements 2239 * @param {NodeList, Array, Element, or String} elems 2240 * @returns {Array} elems 2241 */ 2242Outlayer.prototype._find = function( elems ) { 2243 if ( !elems ) { 2244 return; 2245 } 2246 // if string, use argument as selector string 2247 if ( typeof elems === 'string' ) { 2248 elems = this.element.querySelectorAll( elems ); 2249 } 2250 elems = makeArray( elems ); 2251 return elems; 2252}; 2253 2254Outlayer.prototype._manageStamps = function() { 2255 if ( !this.stamps || !this.stamps.length ) { 2256 return; 2257 } 2258 2259 this._getBoundingRect(); 2260 2261 for ( var i=0, len = this.stamps.length; i < len; i++ ) { 2262 var stamp = this.stamps[i]; 2263 this._manageStamp( stamp ); 2264 } 2265}; 2266 2267// update boundingLeft / Top 2268Outlayer.prototype._getBoundingRect = function() { 2269 // get bounding rect for container element 2270 var boundingRect = this.element.getBoundingClientRect(); 2271 var size = this.size; 2272 this._boundingRect = { 2273 left: boundingRect.left + size.paddingLeft + size.borderLeftWidth, 2274 top: boundingRect.top + size.paddingTop + size.borderTopWidth, 2275 right: boundingRect.right - ( size.paddingRight + size.borderRightWidth ), 2276 bottom: boundingRect.bottom - ( size.paddingBottom + size.borderBottomWidth ) 2277 }; 2278}; 2279 2280/** 2281 * @param {Element} stamp 2282**/ 2283Outlayer.prototype._manageStamp = noop; 2284 2285/** 2286 * get x/y position of element relative to container element 2287 * @param {Element} elem 2288 * @returns {Object} offset - has left, top, right, bottom 2289 */ 2290Outlayer.prototype._getElementOffset = function( elem ) { 2291 var boundingRect = elem.getBoundingClientRect(); 2292 var thisRect = this._boundingRect; 2293 var size = getSize( elem ); 2294 var offset = { 2295 left: boundingRect.left - thisRect.left - size.marginLeft, 2296 top: boundingRect.top - thisRect.top - size.marginTop, 2297 right: thisRect.right - boundingRect.right - size.marginRight, 2298 bottom: thisRect.bottom - boundingRect.bottom - size.marginBottom 2299 }; 2300 return offset; 2301}; 2302 2303// -------------------------- resize -------------------------- // 2304 2305// enable event handlers for listeners 2306// i.e. resize -> onresize 2307Outlayer.prototype.handleEvent = function( event ) { 2308 var method = 'on' + event.type; 2309 if ( this[ method ] ) { 2310 this[ method ]( event ); 2311 } 2312}; 2313 2314/** 2315 * Bind layout to window resizing 2316 */ 2317Outlayer.prototype.bindResize = function() { 2318 // bind just one listener 2319 if ( this.isResizeBound ) { 2320 return; 2321 } 2322 eventie.bind( window, 'resize', this ); 2323 this.isResizeBound = true; 2324}; 2325 2326/** 2327 * Unbind layout to window resizing 2328 */ 2329Outlayer.prototype.unbindResize = function() { 2330 if ( this.isResizeBound ) { 2331 eventie.unbind( window, 'resize', this ); 2332 } 2333 this.isResizeBound = false; 2334}; 2335 2336// original debounce by John Hann 2337// http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/ 2338 2339// this fires every resize 2340Outlayer.prototype.onresize = function() { 2341 if ( this.resizeTimeout ) { 2342 clearTimeout( this.resizeTimeout ); 2343 } 2344 2345 var _this = this; 2346 function delayed() { 2347 _this.resize(); 2348 delete _this.resizeTimeout; 2349 } 2350 2351 this.resizeTimeout = setTimeout( delayed, 100 ); 2352}; 2353 2354// debounced, layout on resize 2355Outlayer.prototype.resize = function() { 2356 // don't trigger if size did not change 2357 // or if resize was unbound. See #9 2358 if ( !this.isResizeBound || !this.needsResizeLayout() ) { 2359 return; 2360 } 2361 2362 this.layout(); 2363}; 2364 2365/** 2366 * check if layout is needed post layout 2367 * @returns Boolean 2368 */ 2369Outlayer.prototype.needsResizeLayout = function() { 2370 var size = getSize( this.element ); 2371 // check that this.size and size are there 2372 // IE8 triggers resize on body size change, so they might not be 2373 var hasSizes = this.size && size; 2374 return hasSizes && size.innerWidth !== this.size.innerWidth; 2375};
2376 2377// -------------------------- methods -------------------------- // 2378 2379/** 2380 * add items to Outlayer instance 2381 * @param {Array or NodeList or Element} elems 2382 * @returns {Array} items - Outlayer.Items 2383**/ 2384Outlayer.prototype.addItems = function( elems ) { 2385 var items = this._itemize( elems ); 2386 // add items to collection 2387 if ( items.length ) { 2388 this.items = this.items.concat( items ); 2389 } 2390 return items; 2391}; 2392 2393/** 2394 * Layout newly-appended item elements 2395 * @param {Array or NodeList or Element} elems 2396 */ 2397Outlayer.prototype.appended = function( elems ) { 2398 var items = this.addItems( elems ); 2399 if ( !items.length ) { 2400 return; 2401 } 2402 // layout and reveal just the new items 2403 this.layoutItems( items, true ); 2404 this.reveal( items ); 2405}; 2406 2407/** 2408 * Layout prepended elements 2409 * @param {Array or NodeList or Element} elems 2410 */ 2411Outlayer.prototype.prepended = function( elems ) { 2412 var items = this._itemize( elems ); 2413 if ( !items.length ) { 2414 return; 2415 } 2416 // add items to beginning of collection 2417 var previousItems = this.items.slice(0); 2418 this.items = items.concat( previousItems ); 2419 // start new layout 2420 this._resetLayout(); 2421 this._manageStamps(); 2422 // layout new stuff without transition 2423 this.layoutItems( items, true ); 2424 this.reveal( items ); 2425 // layout previous items 2426 this.layoutItems( previousItems ); 2427}; 2428 2429/** 2430 * reveal a collection of items 2431 * @param {Array of Outlayer.Items} items 2432 */ 2433Outlayer.prototype.reveal = function( items ) { 2434 var len = items && items.length; 2435 if ( !len ) { 2436 return; 2437 } 2438 for ( var i=0; i < len; i++ ) { 2439 var item = items[i]; 2440 item.reveal(); 2441 } 2442}; 2443 2444/** 2445 * hide a collection of items 2446 * @param {Array of Outlayer.Items} items 2447 */ 2448Outlayer.prototype.hide = function( items ) { 2449 var len = items && items.length; 2450 if ( !len ) { 2451 return; 2452 } 2453 for ( var i=0; i < len; i++ ) { 2454 var item = items[i]; 2455 item.hide(); 2456 } 2457}; 2458 2459/** 2460 * get Outlayer.Item, given an Element 2461 * @param {Element} elem 2462 * @param {Function} callback 2463 * @returns {Outlayer.Item} item 2464 */ 2465Outlayer.prototype.getItem = function( elem ) { 2466 // loop through items to get the one that matches 2467 for ( var i=0, len = this.items.length; i < len; i++ ) { 2468 var item = this.items[i]; 2469 if ( item.element === elem ) { 2470 // return item 2471 return item; 2472 } 2473 } 2474}; 2475 2476/** 2477 * get collection of Outlayer.Items, given Elements 2478 * @param {Array} elems 2479 * @returns {Array} items - Outlayer.Items 2480 */ 2481Outlayer.prototype.getItems = function( elems ) { 2482 if ( !elems || !elems.length ) { 2483 return; 2484 } 2485 var items = []; 2486 for ( var i=0, len = elems.length; i < len; i++ ) { 2487 var elem = elems[i]; 2488 var item = this.getItem( elem ); 2489 if ( item ) { 2490 items.push( item ); 2491 } 2492 } 2493 2494 return items; 2495}; 2496 2497/** 2498 * remove element(s) from instance and DOM 2499 * @param {Array or NodeList or Element} elems 2500 */ 2501Outlayer.prototype.remove = function( elems ) { 2502 elems = makeArray( elems ); 2503 2504 var removeItems = this.getItems( elems ); 2505 // bail if no items to remove 2506 if ( !removeItems || !removeItems.length ) { 2507 return; 2508 } 2509 2510 this._itemsOn( removeItems, 'remove', function() { 2511 this.emitEvent( 'removeComplete', [ this, removeItems ] ); 2512 }); 2513 2514 for ( var i=0, len = removeItems.length; i < len; i++ ) { 2515 var item = removeItems[i]; 2516 item.remove(); 2517 // remove item from collection 2518 removeFrom( item, this.items ); 2519 } 2520}; 2521 2522// ----- destroy ----- // 2523 2524// remove and disable Outlayer instance 2525Outlayer.prototype.destroy = function() { 2526 // clean up dynamic styles 2527 var style = this.element.style; 2528 style.height = ''; 2529 style.position = ''; 2530 style.width = ''; 2531 // destroy items 2532 for ( var i=0, len = this.items.length; i < len; i++ ) { 2533 var item = this.items[i]; 2534 item.destroy(); 2535 } 2536 2537 this.unbindResize(); 2538 2539 delete this.element.outlayerGUID; 2540 // remove data for jQuery 2541 if ( jQuery ) { 2542 jQuery.removeData( this.element, this.constructor.namespace ); 2543 } 2544 2545}; 2546 2547// -------------------------- data -------------------------- // 2548 2549/** 2550 * get Outlayer instance from element 2551 * @param {Element} elem 2552 * @returns {Outlayer} 2553 */ 2554Outlayer.data = function( elem ) { 2555 var id = elem && elem.outlayerGUID; 2556 return id && instances[ id ]; 2557}; 2558 2559 2560// -------------------------- create Outlayer class -------------------------- // 2561 2562/** 2563 * create a layout class 2564 * @param {String} namespace 2565 */ 2566Outlayer.create = function( namespace, options ) { 2567 // sub-class Outlayer 2568 function Layout() { 2569 Outlayer.apply( this, arguments ); 2570 } 2571 // inherit Outlayer prototype, use Object.create if there 2572 if ( Object.create ) { 2573 Layout.prototype = Object.create( Outlayer.prototype ); 2574 } else { 2575 extend( Layout.prototype, Outlayer.prototype ); 2576 } 2577 // set contructor, used for namespace and Item 2578 Layout.prototype.constructor = Layout; 2579 2580 Layout.defaults = extend( {}, Outlayer.defaults ); 2581 // apply new options 2582 extend( Layout.defaults, options ); 2583 // keep prototype.settings for backwards compatibility (Packery v1.2.0) 2584 Layout.prototype.settings = {}; 2585 2586 Layout.namespace = namespace; 2587 2588 Layout.data = Outlayer.data; 2589 2590 // sub-class Item 2591 Layout.Item = function LayoutItem() { 2592 Item.apply( this, arguments ); 2593 }; 2594
2595 Layout.Item.prototype = new Item(); 2596 2597 // -------------------------- declarative -------------------------- // 2598 2599 /** 2600 * allow user to initialize Outlayer via .js-namespace class 2601 * options are parsed from data-namespace-option attribute 2602 */ 2603 docReady( function() { 2604 var dashedNamespace = toDashed( namespace ); 2605 var elems = document.querySelectorAll( '.js-' + dashedNamespace ); 2606 var dataAttr = 'data-' + dashedNamespace + '-options'; 2607 2608 for ( var i=0, len = elems.length; i < len; i++ ) { 2609 var elem = elems[i]; 2610 var attr = elem.getAttribute( dataAttr ); 2611 var options; 2612 try { 2613 options = attr && JSON.parse( attr ); 2614 } catch ( error ) { 2615 // log error, do not initialize 2616 if ( console ) { 2617 console.error( 'Error parsing ' + dataAttr + ' on ' + 2618 elem.nodeName.toLowerCase() + ( elem.id ? '#' + elem.id : '' ) + ': ' + 2619 error ); 2620 } 2621 continue; 2622 } 2623 // initialize 2624 var instance = new Layout( elem, options ); 2625 // make available via $().data('layoutname') 2626 if ( jQuery ) { 2627 jQuery.data( elem, namespace, instance ); 2628 } 2629 } 2630 }); 2631 2632 // -------------------------- jQuery bridge -------------------------- // 2633 2634 // make into jQuery plugin 2635 if ( jQuery && jQuery.bridget ) { 2636 jQuery.bridget( namespace, Layout ); 2637 } 2638 2639 return Layout; 2640}; 2641 2642// ----- fin ----- // 2643 2644// back in global 2645Outlayer.Item = Item; 2646 2647return Outlayer; 2648 2649} 2650 2651// -------------------------- transport -------------------------- // 2652 2653if ( typeof define === 'function' && define.amd ) { 2654 // AMD 2655 define( 'outlayer/outlayer',[ 2656 'eventie/eventie', 2657 'doc-ready/doc-ready', 2658 'eventEmitter/EventEmitter', 2659 'get-size/get-size', 2660 'matches-selector/matches-selector', 2661 './item' 2662 ], 2663 outlayerDefinition ); 2664} else { 2665 // browser global 2666 window.Outlayer = outlayerDefinition( 2667 window.eventie, 2668 window.docReady, 2669 window.EventEmitter, 2670 window.getSize, 2671 window.matchesSelector, 2672 window.Outlayer.Item 2673 ); 2674} 2675 2676})( window ); 2677 2678/** 2679 * Isotope Item 2680**/ 2681 2682( function( window ) { 2683 2684 2685 2686// -------------------------- Item -------------------------- // 2687 2688function itemDefinition( Outlayer ) { 2689 2690// sub-class Outlayer Item 2691function Item() { 2692 Outlayer.Item.apply( this, arguments ); 2693} 2694 2695Item.prototype = new Outlayer.Item(); 2696 2697Item.prototype._create = function() { 2698 // assign id, used for original-order sorting 2699 this.id = this.layout.itemGUID++; 2700 Outlayer.Item.prototype._create.call( this ); 2701 this.sortData = {}; 2702}; 2703 2704Item.prototype.updateSortData = function() { 2705 if ( this.isIgnored ) { 2706 return; 2707 } 2708 // default sorters 2709 this.sortData.id = this.id; 2710 // for backward compatibility 2711 this.sortData['original-order'] = this.id; 2712 this.sortData.random = Math.random(); 2713 // go thru getSortData obj and apply the sorters 2714 var getSortData = this.layout.options.getSortData; 2715 var sorters = this.layout._sorters; 2716 for ( var key in getSortData ) { 2717 var sorter = sorters[ key ]; 2718 this.sortData[ key ] = sorter( this.element, this ); 2719 } 2720}; 2721 2722var _destroy = Item.prototype.destroy; 2723Item.prototype.destroy = function() { 2724 // call super 2725 _destroy.apply( this, arguments ); 2726 // reset display, #741 2727 this.css({ 2728 display: '' 2729 }); 2730}; 2731 2732return Item; 2733 2734} 2735 2736// -------------------------- transport -------------------------- // 2737 2738if ( typeof define === 'function' && define.amd ) { 2739 // AMD 2740 define( 'isotope/js/item',[ 2741 'outlayer/outlayer' 2742 ], 2743 itemDefinition ); 2744} else { 2745 // browser global 2746 window.Isotope = window.Isotope || {}; 2747 window.Isotope.Item = itemDefinition( 2748 window.Outlayer 2749 ); 2750} 2751 2752})( window ); 2753 2754( function( window ) { 2755 2756 2757 2758// -------------------------- -------------------------- // 2759 2760function layoutModeDefinition( getSize, Outlayer ) { 2761 2762 // layout mode class 2763 function LayoutMode( isotope ) { 2764 this.isotope = isotope; 2765 // link properties 2766 if ( isotope ) { 2767 this.options = isotope.options[ this.namespace ]; 2768 this.element = isotope.element; 2769 this.items = isotope.filteredItems; 2770 this.size = isotope.size; 2771 } 2772 } 2773 2774 /** 2775 * some methods should just defer to default Outlayer method 2776 * and reference the Isotope instance as `this` 2777 **/ 2778 ( function() { 2779 var facadeMethods = [ 2780 '_resetLayout', 2781 '_getItemLayoutPosition', 2782 '_manageStamp', 2783 '_getContainerSize', 2784 '_getElementOffset', 2785 'needsResizeLayout' 2786 ]; 2787 2788 for ( var i=0, len = facadeMethods.length; i < len; i++ ) { 2789 var methodName = facadeMethods[i]; 2790 LayoutMode.prototype[ methodName ] = getOutlayerMethod( methodName ); 2791 } 2792 2793 function getOutlayerMethod( methodName ) { 2794 return function() { 2795 return Outlayer.prototype[ methodName ].apply( this.isotope, arguments ); 2796 }; 2797 } 2798 })();
vendor: 9,866 bytes, lines 2799-3141
2799 2800 // ----- ----- // 2801 2802 // for horizontal layout modes, check vertical size 2803 LayoutMode.prototype.needsVerticalResizeLayout = function() { 2804 // don't trigger if size did not change 2805 var size = getSize( this.isotope.element ); 2806 // check that this.size and size are there 2807 // IE8 triggers resize on body size change, so they might not be 2808 var hasSizes = this.isotope.size && size; 2809 return hasSizes && size.innerHeight !== this.isotope.size.innerHeight; 2810 }; 2811 2812 // ----- measurements ----- // 2813 2814 LayoutMode.prototype._getMeasurement = function() { 2815 this.isotope._getMeasurement.apply( this, arguments ); 2816 }; 2817 2818 LayoutMode.prototype.getColumnWidth = function() { 2819 this.getSegmentSize( 'column', 'Width' ); 2820 }; 2821 2822 LayoutMode.prototype.getRowHeight = function() { 2823 this.getSegmentSize( 'row', 'Height' ); 2824 }; 2825 2826 /** 2827 * get columnWidth or rowHeight 2828 * segment: 'column' or 'row' 2829 * size 'Width' or 'Height' 2830 **/ 2831 LayoutMode.prototype.getSegmentSize = function( segment, size ) { 2832 var segmentName = segment + size; 2833 var outerSize = 'outer' + size; 2834 // columnWidth / outerWidth // rowHeight / outerHeight 2835 this._getMeasurement( segmentName, outerSize ); 2836 // got rowHeight or columnWidth, we can chill 2837 if ( this[ segmentName ] ) { 2838 return; 2839 } 2840 // fall back to item of first element 2841 var firstItemSize = this.getFirstItemSize(); 2842 this[ segmentName ] = firstItemSize && firstItemSize[ outerSize ] || 2843 // or size of container 2844 this.isotope.size[ 'inner' + size ]; 2845 }; 2846 2847 LayoutMode.prototype.getFirstItemSize = function() { 2848 var firstItem = this.isotope.filteredItems[0]; 2849 return firstItem && firstItem.element && getSize( firstItem.element ); 2850 }; 2851 2852 // ----- methods that should reference isotope ----- // 2853 2854 LayoutMode.prototype.layout = function() { 2855 this.isotope.layout.apply( this.isotope, arguments ); 2856 }; 2857 2858 LayoutMode.prototype.getSize = function() { 2859 this.isotope.getSize(); 2860 this.size = this.isotope.size; 2861 }; 2862 2863 // -------------------------- create -------------------------- // 2864 2865 LayoutMode.modes = {}; 2866 2867 LayoutMode.create = function( namespace, options ) { 2868 2869 function Mode() { 2870 LayoutMode.apply( this, arguments ); 2871 } 2872 2873 Mode.prototype = new LayoutMode(); 2874 2875 // default options 2876 if ( options ) { 2877 Mode.options = options; 2878 } 2879 2880 Mode.prototype.namespace = namespace; 2881 // register in Isotope 2882 LayoutMode.modes[ namespace ] = Mode; 2883 2884 return Mode; 2885 }; 2886 2887 2888 return LayoutMode; 2889 2890} 2891 2892if ( typeof define === 'function' && define.amd ) { 2893 // AMD 2894 define( 'isotope/js/layout-mode',[ 2895 'get-size/get-size', 2896 'outlayer/outlayer' 2897 ], 2898 layoutModeDefinition ); 2899} else { 2900 // browser global 2901 window.Isotope = window.Isotope || {}; 2902 window.Isotope.LayoutMode = layoutModeDefinition( 2903 window.getSize, 2904 window.Outlayer 2905 ); 2906} 2907 2908 2909})( window ); 2910 2911/*! 2912 * Masonry v3.1.5 2913 * Cascading grid layout library 2914 * http://masonry.desandro.com 2915 * MIT License 2916 * by David DeSandro 2917 */ 2918 2919( function( window ) { 2920 2921 2922 2923// -------------------------- helpers -------------------------- // 2924 2925var indexOf = Array.prototype.indexOf ? 2926 function( items, value ) { 2927 return items.indexOf( value ); 2928 } : 2929 function ( items, value ) { 2930 for ( var i=0, len = items.length; i < len; i++ ) { 2931 var item = items[i]; 2932 if ( item === value ) { 2933 return i; 2934 } 2935 } 2936 return -1; 2937 }; 2938 2939// -------------------------- masonryDefinition -------------------------- // 2940 2941// used for AMD definition and requires 2942function masonryDefinition( Outlayer, getSize ) { 2943 // create an Outlayer layout class 2944 var Masonry = Outlayer.create('masonry'); 2945 2946 Masonry.prototype._resetLayout = function() { 2947 this.getSize(); 2948 this._getMeasurement( 'columnWidth', 'outerWidth' ); 2949 this._getMeasurement( 'gutter', 'outerWidth' ); 2950 this.measureColumns(); 2951 2952 // reset column Y 2953 var i = this.cols; 2954 this.colYs = []; 2955 while (i--) { 2956 this.colYs.push( 0 ); 2957 } 2958 2959 this.maxY = 0; 2960 }; 2961 2962 Masonry.prototype.measureColumns = function() { 2963 this.getContainerWidth(); 2964 // if columnWidth is 0, default to outerWidth of first item 2965 if ( !this.columnWidth ) { 2966 var firstItem = this.items[0]; 2967 var firstItemElem = firstItem && firstItem.element; 2968 // columnWidth fall back to item of first element 2969 this.columnWidth = firstItemElem && getSize( firstItemElem ).outerWidth || 2970 // if first elem has no width, default to size of container 2971 this.containerWidth; 2972 } 2973 2974 this.columnWidth += this.gutter; 2975 2976 this.cols = Math.floor( ( this.containerWidth + this.gutter ) / this.columnWidth ); 2977 this.cols = Math.max( this.cols, 1 ); 2978 }; 2979 2980 Masonry.prototype.getContainerWidth = function() { 2981 // container is parent if fit width 2982 var container = this.options.isFitWidth ? this.element.parentNode : this.element; 2983 // check that this.size and size are there 2984 // IE8 triggers resize on body size change, so they might not be 2985 var size = getSize( container ); 2986 this.containerWidth = size && size.innerWidth; 2987 }; 2988 2989 Masonry.prototype._getItemLayoutPosition = function( item ) { 2990 item.getSize(); 2991 // how many columns does this brick span 2992 var remainder = item.size.outerWidth % this.columnWidth; 2993 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil'; 2994 // round if off by 1 pixel, otherwise use ceil 2995 var colSpan = Math[ mathMethod ]( item.size.outerWidth / this.columnWidth ); 2996 colSpan = Math.min( colSpan, this.cols ); 2997 2998 var colGroup = this._getColGroup( colSpan ); 2999 // get the minimum Y value from the columns 3000 var minimumY = Math.min.apply( Math, colGroup ); 3001 var shortColIndex = indexOf( colGroup, minimumY ); 3002 3003 // position the brick 3004 var position = { 3005 x: this.columnWidth * shortColIndex, 3006 y: minimumY 3007 }; 3008 3009 // apply setHeight to necessary columns 3010 var setHeight = minimumY + item.size.outerHeight; 3011 var setSpan = this.cols + 1 - colGroup.length; 3012 for ( var i = 0; i < setSpan; i++ ) { 3013 this.colYs[ shortColIndex + i ] = setHeight; 3014 } 3015 3016 return position; 3017 }; 3018 3019 /** 3020 * @param {Number} colSpan - number of columns the element spans 3021 * @returns {Array} colGroup 3022 */ 3023 Masonry.prototype._getColGroup = function( colSpan ) { 3024 if ( colSpan < 2 ) { 3025 // if brick spans only one column, use all the column Ys 3026 return this.colYs; 3027 } 3028 3029 var colGroup = []; 3030 // how many different places could this brick fit horizontally 3031 var groupCount = this.cols + 1 - colSpan; 3032 // for each group potential horizontal position 3033 for ( var i = 0; i < groupCount; i++ ) { 3034 // make an array of colY values for that one group 3035 var groupColYs = this.colYs.slice( i, i + colSpan ); 3036 // and get the max value of the array 3037 colGroup[i] = Math.max.apply( Math, groupColYs ); 3038 } 3039 return colGroup; 3040 }; 3041 3042 Masonry.prototype._manageStamp = function( stamp ) { 3043 var stampSize = getSize( stamp ); 3044 var offset = this._getElementOffset( stamp ); 3045 // get the columns that this stamp affects 3046 var firstX = this.options.isOriginLeft ? offset.left : offset.right; 3047 var lastX = firstX + stampSize.outerWidth; 3048 var firstCol = Math.floor( firstX / this.columnWidth ); 3049 firstCol = Math.max( 0, firstCol ); 3050 var lastCol = Math.floor( lastX / this.columnWidth ); 3051 // lastCol should not go over if multiple of columnWidth #425 3052 lastCol -= lastX % this.columnWidth ? 0 : 1; 3053 lastCol = Math.min( this.cols - 1, lastCol ); 3054 // set colYs to bottom of the stamp 3055 var stampMaxY = ( this.options.isOriginTop ? offset.top : offset.bottom ) + 3056 stampSize.outerHeight; 3057 for ( var i = firstCol; i <= lastCol; i++ ) { 3058 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] ); 3059 } 3060 }; 3061 3062 Masonry.prototype._getContainerSize = function() { 3063 this.maxY = Math.max.apply( Math, this.colYs ); 3064 var size = { 3065 height: this.maxY 3066 }; 3067 3068 if ( this.options.isFitWidth ) { 3069 size.width = this._getContainerFitWidth(); 3070 } 3071 3072 return size; 3073 }; 3074 3075 Masonry.prototype._getContainerFitWidth = function() { 3076 var unusedCols = 0; 3077 // count unused columns 3078 var i = this.cols; 3079 while ( --i ) { 3080 if ( this.colYs[i] !== 0 ) { 3081 break; 3082 } 3083 unusedCols++; 3084 } 3085 // fit container to columns that have been used 3086 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter; 3087 }; 3088 3089 Masonry.prototype.needsResizeLayout = function() { 3090 var previousWidth = this.containerWidth; 3091 this.getContainerWidth(); 3092 return previousWidth !== this.containerWidth; 3093 }; 3094 3095 return Masonry; 3096} 3097 3098// -------------------------- transport -------------------------- // 3099 3100if ( typeof define === 'function' && define.amd ) { 3101 // AMD 3102 define( 'masonry/masonry',[ 3103 'outlayer/outlayer', 3104 'get-size/get-size' 3105 ], 3106 masonryDefinition ); 3107} else { 3108 // browser global 3109 window.Masonry = masonryDefinition( 3110 window.Outlayer, 3111 window.getSize 3112 ); 3113} 3114 3115})( window ); 3116 3117/*! 3118 * Masonry layout mode 3119 * sub-classes Masonry 3120 * http://masonry.desandro.com 3121 */ 3122 3123( function( window ) { 3124 3125 3126 3127// -------------------------- helpers -------------------------- // 3128 3129// extend objects 3130function extend( a, b ) { 3131 for ( var prop in b ) { 3132 a[ prop ] = b[ prop ]; 3133 } 3134 return a; 3135} 3136 3137// -------------------------- masonryDefinition -------------------------- // 3138 3139// used for AMD definition and requires 3140function masonryDefinition( LayoutMode, Masonry ) { 3141 // create an Outlayer layout class
3142 var MasonryMode = LayoutMode.create('masonry'); 3143 3144 // save on to these methods 3145 var _getElementOffset = MasonryMode.prototype._getElementOffset; 3146 var layout = MasonryMode.prototype.layout; 3147 var _getMeasurement = MasonryMode.prototype._getMeasurement; 3148 3149 // sub-class Masonry 3150 extend( MasonryMode.prototype, Masonry.prototype ); 3151 3152 // set back, as it was overwritten by Masonry 3153 MasonryMode.prototype._getElementOffset = _getElementOffset; 3154 MasonryMode.prototype.layout = layout; 3155 MasonryMode.prototype._getMeasurement = _getMeasurement; 3156 3157 var measureColumns = MasonryMode.prototype.measureColumns; 3158 MasonryMode.prototype.measureColumns = function() { 3159 // set items, used if measuring first item 3160 this.items = this.isotope.filteredItems; 3161 measureColumns.call( this ); 3162 }; 3163 3164 // HACK copy over isOriginLeft/Top options 3165 var _manageStamp = MasonryMode.prototype._manageStamp; 3166 MasonryMode.prototype._manageStamp = function() { 3167 this.options.isOriginLeft = this.isotope.options.isOriginLeft; 3168 this.options.isOriginTop = this.isotope.options.isOriginTop; 3169 _manageStamp.apply( this, arguments ); 3170 }; 3171 3172 return MasonryMode; 3173} 3174 3175// -------------------------- transport -------------------------- // 3176 3177if ( typeof define === 'function' && define.amd ) { 3178 // AMD 3179 define( 'isotope/js/layout-modes/masonry',[ 3180 '../layout-mode', 3181 'masonry/masonry' 3182 ], 3183 masonryDefinition ); 3184} else { 3185 // browser global 3186 masonryDefinition( 3187 window.Isotope.LayoutMode, 3188 window.Masonry 3189 ); 3190} 3191 3192})( window ); 3193 3194( function( window ) { 3195 3196 3197 3198function fitRowsDefinition( LayoutMode ) { 3199 3200var FitRows = LayoutMode.create('fitRows'); 3201 3202FitRows.prototype._resetLayout = function() { 3203 this.x = 0; 3204 this.y = 0; 3205 this.maxY = 0; 3206}; 3207 3208FitRows.prototype._getItemLayoutPosition = function( item ) { 3209 item.getSize(); 3210 3211 // if this element cannot fit in the current row 3212 if ( this.x !== 0 && item.size.outerWidth + this.x > this.isotope.size.innerWidth ) { 3213 this.x = 0; 3214 this.y = this.maxY; 3215 } 3216 3217 var position = { 3218 x: this.x, 3219 y: this.y 3220 }; 3221 3222 this.maxY = Math.max( this.maxY, this.y + item.size.outerHeight ); 3223 this.x += item.size.outerWidth; 3224 3225 return position; 3226}; 3227 3228FitRows.prototype._getContainerSize = function() { 3229 return { height: this.maxY }; 3230}; 3231 3232return FitRows; 3233 3234} 3235 3236if ( typeof define === 'function' && define.amd ) { 3237 // AMD 3238 define( 'isotope/js/layout-modes/fit-rows',[ 3239 '../layout-mode' 3240 ], 3241 fitRowsDefinition ); 3242} else { 3243 // browser global 3244 fitRowsDefinition( 3245 window.Isotope.LayoutMode 3246 ); 3247} 3248 3249})( window ); 3250 3251( function( window ) { 3252 3253 3254 3255function verticalDefinition( LayoutMode ) { 3256 3257var Vertical = LayoutMode.create( 'vertical', { 3258 horizontalAlignment: 0 3259}); 3260 3261Vertical.prototype._resetLayout = function() { 3262 this.y = 0; 3263}; 3264 3265Vertical.prototype._getItemLayoutPosition = function( item ) { 3266 item.getSize(); 3267 var x = ( this.isotope.size.innerWidth - item.size.outerWidth ) * 3268 this.options.horizontalAlignment; 3269 var y = this.y; 3270 this.y += item.size.outerHeight; 3271 return { x: x, y: y }; 3272}; 3273 3274Vertical.prototype._getContainerSize = function() { 3275 return { height: this.y }; 3276}; 3277 3278return Vertical; 3279 3280} 3281 3282if ( typeof define === 'function' && define.amd ) { 3283 // AMD 3284 define( 'isotope/js/layout-modes/vertical',[ 3285 '../layout-mode' 3286 ], 3287 verticalDefinition ); 3288} else { 3289 // browser global 3290 verticalDefinition( 3291 window.Isotope.LayoutMode 3292 ); 3293} 3294 3295})( window ); 3296 3297/*! 3298 * Isotope v2.0.1 3299 * Filter & sort magical layouts 3300 * http://isotope.metafizzy.co 3301 */ 3302 3303( function( window ) { 3304 3305 3306 3307// -------------------------- vars -------------------------- // 3308 3309var jQuery = window.jQuery; 3310 3311// -------------------------- helpers -------------------------- // 3312 3313// extend objects 3314function extend( a, b ) { 3315 for ( var prop in b ) { 3316 a[ prop ] = b[ prop ]; 3317 } 3318 return a; 3319} 3320 3321var trim = String.prototype.trim ? 3322 function( str ) { 3323 return str.trim(); 3324 } : 3325 function( str ) { 3326 return str.replace( /^\s+|\s+$/g, '' ); 3327 }; 3328 3329var docElem = document.documentElement; 3330 3331var getText = docElem.textContent ? 3332 function( elem ) { 3333 return elem.textContent; 3334 }
3334 : 3335 function( elem ) { 3336 return elem.innerText; 3337 }; 3338 3339var objToString = Object.prototype.toString; 3340function isArray( obj ) { 3341 return objToString.call( obj ) === '[object Array]'; 3342} 3343 3344// index of helper cause IE8 3345var indexOf = Array.prototype.indexOf ? function( ary, obj ) { 3346 return ary.indexOf( obj ); 3347 } : function( ary, obj ) { 3348 for ( var i=0, len = ary.length; i < len; i++ ) { 3349 if ( ary[i] === obj ) { 3350 return i; 3351 } 3352 } 3353 return -1; 3354 }; 3355 3356// turn element or nodeList into an array 3357function makeArray( obj ) { 3358 var ary = []; 3359 if ( isArray( obj ) ) { 3360 // use object if already an array 3361 ary = obj; 3362 } else if ( obj && typeof obj.length === 'number' ) { 3363 // convert nodeList to array 3364 for ( var i=0, len = obj.length; i < len; i++ ) { 3365 ary.push( obj[i] ); 3366 } 3367 } else { 3368 // array of single index 3369 ary.push( obj ); 3370 } 3371 return ary; 3372} 3373 3374function removeFrom( obj, ary ) { 3375 var index = indexOf( ary, obj ); 3376 if ( index !== -1 ) { 3377 ary.splice( index, 1 ); 3378 } 3379} 3380 3381// -------------------------- isotopeDefinition -------------------------- // 3382 3383// used for AMD definition and requires 3384function isotopeDefinition( Outlayer, getSize, matchesSelector, Item, LayoutMode ) { 3385 // create an Outlayer layout class 3386 var Isotope = Outlayer.create( 'isotope', { 3387 layoutMode: "masonry", 3388 isJQueryFiltering: true, 3389 sortAscending: true 3390 }); 3391 3392 Isotope.Item = Item; 3393 Isotope.LayoutMode = LayoutMode; 3394 3395 Isotope.prototype._create = function() { 3396 this.itemGUID = 0; 3397 // functions that sort items 3398 this._sorters = {}; 3399 this._getSorters(); 3400 // call super 3401 Outlayer.prototype._create.call( this ); 3402 3403 // create layout modes 3404 this.modes = {}; 3405 // start filteredItems with all items 3406 this.filteredItems = this.items; 3407 // keep of track of sortBys 3408 this.sortHistory = [ 'original-order' ]; 3409 // create from registered layout modes 3410 for ( var name in LayoutMode.modes ) { 3411 this._initLayoutMode( name ); 3412 } 3413 }; 3414 3415 Isotope.prototype.reloadItems = function() { 3416 // reset item ID counter 3417 this.itemGUID = 0; 3418 // call super 3419 Outlayer.prototype.reloadItems.call( this ); 3420 }; 3421 3422 Isotope.prototype._itemize = function() { 3423 var items = Outlayer.prototype._itemize.apply( this, arguments ); 3424 // assign ID for original-order 3425 for ( var i=0, len = items.length; i < len; i++ ) { 3426 var item = items[i]; 3427 item.id = this.itemGUID++; 3428 } 3429 this._updateItemsSortData( items ); 3430 return items; 3431 }; 3432 3433 3434 // -------------------------- layout -------------------------- // 3435 3436 Isotope.prototype._initLayoutMode = function( name ) { 3437 var Mode = LayoutMode.modes[ name ]; 3438 // set mode options 3439 // HACK extend initial options, back-fill in default options 3440 var initialOpts = this.options[ name ] || {}; 3441 this.options[ name ] = Mode.options ? 3442 extend( Mode.options, initialOpts ) : initialOpts; 3443 // init layout mode instance 3444 this.modes[ name ] = new Mode( this ); 3445 }; 3446 3447 3448 Isotope.prototype.layout = function() { 3449 // if first time doing layout, do all magic 3450 if ( !this._isLayoutInited && this.options.isInitLayout ) { 3451 this.arrange(); 3452 return; 3453 } 3454 this._layout(); 3455 }; 3456 3457 // private method to be used in layout() & magic() 3458 Isotope.prototype._layout = function() { 3459 // don't animate first layout 3460 var isInstant = this._getIsInstant(); 3461 // layout flow 3462 this._resetLayout(); 3463 this._manageStamps(); 3464 this.layoutItems( this.filteredItems, isInstant ); 3465 3466 // flag for initalized 3467 this._isLayoutInited = true; 3468 }; 3469 3470 // filter + sort + layout 3471 Isotope.prototype.arrange = function( opts ) { 3472 // set any options pass 3473 this.option( opts ); 3474 this._getIsInstant(); 3475 // filter, sort, and layout 3476 this.filteredItems = this._filter( this.items ); 3477 this._sort(); 3478 this._layout(); 3479 }; 3480 // alias to _init for main plugin method 3481 Isotope.prototype._init = Isotope.prototype.arrange; 3482 3483 // HACK 3484 // Don't animate/transition first layout 3485 // Or don't animate/transition other layouts 3486 Isotope.prototype._getIsInstant = function() { 3487 var isInstant = this.options.isLayoutInstant !== undefined ? 3488 this.options.isLayoutInstant : !this._isLayoutInited; 3489 this._isInstant = isInstant; 3490 return isInstant; 3491 }; 3492 3493 // -------------------------- filter -------------------------- // 3494 3495 Isotope.prototype._filter = function( items ) { 3496 var filter = this.options.filter;
3497 filter = filter || '*'; 3498 var matches = []; 3499 var hiddenMatched = []; 3500 var visibleUnmatched = []; 3501 3502 var test = this._getFilterTest( filter ); 3503 3504 // test each item 3505 for ( var i=0, len = items.length; i < len; i++ ) { 3506 var item = items[i]; 3507 if ( item.isIgnored ) { 3508 continue; 3509 } 3510 // add item to either matched or unmatched group 3511 var isMatched = test( item ); 3512 // item.isFilterMatched = isMatched; 3513 // add to matches if its a match 3514 if ( isMatched ) { 3515 matches.push( item ); 3516 } 3517 // add to additional group if item needs to be hidden or revealed 3518 if ( isMatched && item.isHidden ) { 3519 hiddenMatched.push( item ); 3520 } else if ( !isMatched && !item.isHidden ) { 3521 visibleUnmatched.push( item ); 3522 } 3523 } 3524 3525 var _this = this; 3526 function hideReveal() { 3527 _this.reveal( hiddenMatched ); 3528 _this.hide( visibleUnmatched ); 3529 } 3530 3531 if ( this._isInstant ) { 3532 this._noTransition( hideReveal ); 3533 } else { 3534 hideReveal(); 3535 } 3536 3537 return matches; 3538 }; 3539 3540 // get a jQuery, function, or a matchesSelector test given the filter 3541 Isotope.prototype._getFilterTest = function( filter ) { 3542 if ( jQuery && this.options.isJQueryFiltering ) { 3543 // use jQuery 3544 return function( item ) { 3545 return jQuery( item.element ).is( filter ); 3546 }; 3547 } 3548 if ( typeof filter === 'function' ) { 3549 // use filter as function 3550 return function( item ) { 3551 return filter( item.element ); 3552 }; 3553 } 3554 // default, use filter as selector string 3555 return function( item ) { 3556 return matchesSelector( item.element, filter ); 3557 }; 3558 }; 3559 3560 // -------------------------- sorting -------------------------- // 3561 3562 /** 3563 * @params {Array} elems 3564 * @public 3565 */ 3566 Isotope.prototype.updateSortData = function( elems ) { 3567 this._getSorters(); 3568 // update item sort data 3569 // default to all items if none are passed in 3570 elems = makeArray( elems ); 3571 var items = this.getItems( elems ); 3572 // if no items found, update all items 3573 items = items.length ? items : this.items; 3574 this._updateItemsSortData( items ); 3575 }; 3576 3577 Isotope.prototype._getSorters = function() { 3578 var getSortData = this.options.getSortData; 3579 for ( var key in getSortData ) { 3580 var sorter = getSortData[ key ]; 3581 this._sorters[ key ] = mungeSorter( sorter ); 3582 } 3583 }; 3584 3585 /** 3586 * @params {Array} items - of Isotope.Items 3587 * @private 3588 */ 3589 Isotope.prototype._updateItemsSortData = function( items ) { 3590 for ( var i=0, len = items.length; i < len; i++ ) { 3591 var item = items[i]; 3592 item.updateSortData(); 3593 } 3594 }; 3595 3596 // ----- munge sorter ----- // 3597 3598 // encapsulate this, as we just need mungeSorter 3599 // other functions in here are just for munging 3600 var mungeSorter = ( function() { 3601 // add a magic layer to sorters for convienent shorthands 3602 // `.foo-bar` will use the text of .foo-bar querySelector 3603 // `[foo-bar]` will use attribute 3604 // you can also add parser 3605 // `.foo-bar parseInt` will parse that as a number 3606 function mungeSorter( sorter ) { 3607 // if not a string, return function or whatever it is 3608 if ( typeof sorter !== 'string' ) { 3609 return sorter; 3610 } 3611 // parse the sorter string 3612 var args = trim( sorter ).split(' '); 3613 var query = args[0]; 3614 // check if query looks like [an-attribute] 3615 var attrMatch = query.match( /^\[(.+)\]$/ ); 3616 var attr = attrMatch && attrMatch[1]; 3617 var getValue = getValueGetter( attr, query ); 3618 // use second argument as a parser 3619 var parser = Isotope.sortDataParsers[ args[1] ]; 3620 // parse the value, if there was a parser 3621 sorter = parser ? function( elem ) { 3622 return elem && parser( getValue( elem ) ); 3623 } : 3624 // otherwise just return value 3625 function( elem ) { 3626 return elem && getValue( elem ); 3627 }; 3628 3629 return sorter; 3630 } 3631 3632 // get an attribute getter, or get text of the querySelector 3633 function getValueGetter( attr, query ) { 3634 var getValue; 3635 // if query looks like [foo-bar], get attribute 3636 if ( attr ) { 3637 getValue = function( elem ) { 3638 return elem.getAttribute( attr ); 3639 }; 3640 } else { 3641 // otherwise, assume its a querySelector, and get its text 3642 getValue = function( elem ) { 3643 var child = elem.querySelector( query ); 3644 return child && getText( child ); 3645 }; 3646 } 3647 return getValue; 3648 } 3649 3650 return mungeSorter; 3651 })();
vendor: 5,011 bytes, lines 3652-3808
3652 3653 // parsers used in getSortData shortcut strings 3654 Isotope.sortDataParsers = { 3655 'parseInt': function( val ) { 3656 return parseInt( val, 10 ); 3657 }, 3658 'parseFloat': function( val ) { 3659 return parseFloat( val ); 3660 } 3661 }; 3662 3663 // ----- sort method ----- // 3664 3665 // sort filteredItem order 3666 Isotope.prototype._sort = function() { 3667 var sortByOpt = this.options.sortBy; 3668 if ( !sortByOpt ) { 3669 return; 3670 } 3671 // concat all sortBy and sortHistory 3672 var sortBys = [].concat.apply( sortByOpt, this.sortHistory ); 3673 // sort magic 3674 var itemSorter = getItemSorter( sortBys, this.options.sortAscending ); 3675 this.filteredItems.sort( itemSorter ); 3676 // keep track of sortBy History 3677 if ( sortByOpt !== this.sortHistory[0] ) { 3678 // add to front, oldest goes in last 3679 this.sortHistory.unshift( sortByOpt ); 3680 } 3681 }; 3682 3683 // returns a function used for sorting 3684 function getItemSorter( sortBys, sortAsc ) { 3685 return function sorter( itemA, itemB ) { 3686 // cycle through all sortKeys 3687 for ( var i = 0, len = sortBys.length; i < len; i++ ) { 3688 var sortBy = sortBys[i]; 3689 var a = itemA.sortData[ sortBy ]; 3690 var b = itemB.sortData[ sortBy ]; 3691 if ( a > b || a < b ) { 3692 // if sortAsc is an object, use the value given the sortBy key 3693 var isAscending = sortAsc[ sortBy ] !== undefined ? sortAsc[ sortBy ] : sortAsc; 3694 var direction = isAscending ? 1 : -1; 3695 return ( a > b ? 1 : -1 ) * direction; 3696 } 3697 } 3698 return 0; 3699 }; 3700 } 3701 3702 // -------------------------- methods -------------------------- // 3703 3704 // get layout mode 3705 Isotope.prototype._mode = function() { 3706 var layoutMode = this.options.layoutMode; 3707 var mode = this.modes[ layoutMode ]; 3708 if ( !mode ) { 3709 // TODO console.error 3710 throw new Error( 'No layout mode: ' + layoutMode ); 3711 } 3712 // HACK sync mode's options 3713 // any options set after init for layout mode need to be synced 3714 mode.options = this.options[ layoutMode ]; 3715 return mode; 3716 }; 3717 3718 Isotope.prototype._resetLayout = function() { 3719 // trigger original reset layout 3720 Outlayer.prototype._resetLayout.call( this ); 3721 this._mode()._resetLayout(); 3722 }; 3723 3724 Isotope.prototype._getItemLayoutPosition = function( item ) { 3725 return this._mode()._getItemLayoutPosition( item ); 3726 }; 3727 3728 Isotope.prototype._manageStamp = function( stamp ) { 3729 this._mode()._manageStamp( stamp ); 3730 }; 3731 3732 Isotope.prototype._getContainerSize = function() { 3733 return this._mode()._getContainerSize(); 3734 }; 3735 3736 Isotope.prototype.needsResizeLayout = function() { 3737 return this._mode().needsResizeLayout(); 3738 }; 3739 3740 // -------------------------- adding & removing -------------------------- // 3741 3742 // HEADS UP overwrites default Outlayer appended 3743 Isotope.prototype.appended = function( elems ) { 3744 var items = this.addItems( elems ); 3745 if ( !items.length ) { 3746 return; 3747 } 3748 var filteredItems = this._filterRevealAdded( items ); 3749 // add to filteredItems 3750 this.filteredItems = this.filteredItems.concat( filteredItems ); 3751 }; 3752 3753 // HEADS UP overwrites default Outlayer prepended 3754 Isotope.prototype.prepended = function( elems ) { 3755 var items = this._itemize( elems ); 3756 if ( !items.length ) { 3757 return; 3758 } 3759 // add items to beginning of collection 3760 var previousItems = this.items.slice(0); 3761 this.items = items.concat( previousItems ); 3762 // start new layout 3763 this._resetLayout(); 3764 this._manageStamps(); 3765 // layout new stuff without transition 3766 var filteredItems = this._filterRevealAdded( items ); 3767 // layout previous items 3768 this.layoutItems( previousItems ); 3769 // add to filteredItems 3770 this.filteredItems = filteredItems.concat( this.filteredItems ); 3771 }; 3772 3773 Isotope.prototype._filterRevealAdded = function( items ) { 3774 var filteredItems = this._noTransition( function() { 3775 return this._filter( items ); 3776 }); 3777 // layout and reveal just the new items 3778 this.layoutItems( filteredItems, true ); 3779 this.reveal( filteredItems ); 3780 return items; 3781 }; 3782 3783 /** 3784 * Filter, sort, and layout newly-appended item elements 3785 * @param {Array or NodeList or Element} elems 3786 */ 3787 Isotope.prototype.insert = function( elems ) { 3788 var items = this.addItems( elems ); 3789 if ( !items.length ) { 3790 return; 3791 } 3792 // append item elements 3793 var i, item; 3794 var len = items.length; 3795 for ( i=0; i < len; i++ ) { 3796 item = items[i]; 3797 this.element.appendChild( item.element ); 3798 } 3799 // filter new stuff 3800 /* 3801 // this way adds hides new filtered items with NO transition 3802 // so user can't see if new hidden items have been inserted 3803 var filteredInsertItems; 3804 this._noTransition( function() { 3805 filteredInsertItems = this._filter( items ); 3806 // hide all new items 3807 this.hide( filteredInsertItems ); 3808 });
3809 // */ 3810 // this way hides new filtered items with transition 3811 // so user at least sees that something has been added 3812 var filteredInsertItems = this._filter( items ); 3813 // hide all newitems 3814 this._noTransition( function() { 3815 this.hide( filteredInsertItems ); 3816 }); 3817 // */ 3818 // set flag 3819 for ( i=0; i < len; i++ ) { 3820 items[i].isLayoutInstant = true; 3821 } 3822 this.arrange(); 3823 // reset flag 3824 for ( i=0; i < len; i++ ) { 3825 delete items[i].isLayoutInstant; 3826 } 3827 this.reveal( filteredInsertItems ); 3828 }; 3829 3830 var _remove = Isotope.prototype.remove; 3831 Isotope.prototype.remove = function( elems ) { 3832 elems = makeArray( elems ); 3833 var removeItems = this.getItems( elems ); 3834 // do regular thing 3835 _remove.call( this, elems ); 3836 // bail if no items to remove 3837 if ( !removeItems || !removeItems.length ) { 3838 return; 3839 } 3840 // remove elems from filteredItems 3841 for ( var i=0, len = removeItems.length; i < len; i++ ) { 3842 var item = removeItems[i]; 3843 // remove item from collection 3844 removeFrom( item, this.filteredItems ); 3845 } 3846 }; 3847 3848 Isotope.prototype.shuffle = function() { 3849 // update random sortData 3850 for ( var i=0, len = this.items.length; i < len; i++ ) { 3851 var item = this.items[i]; 3852 item.sortData.random = Math.random(); 3853 } 3854 this.options.sortBy = 'random'; 3855 this._sort(); 3856 this._layout(); 3857 }; 3858 3859 /** 3860 * trigger fn without transition 3861 * kind of hacky to have this in the first place 3862 * @param {Function} fn 3863 * @returns ret 3864 * @private 3865 */ 3866 Isotope.prototype._noTransition = function( fn ) { 3867 // save transitionDuration before disabling 3868 var transitionDuration = this.options.transitionDuration; 3869 // disable transition 3870 this.options.transitionDuration = 0; 3871 // do it 3872 var returnValue = fn.call( this ); 3873 // re-enable transition for reveal 3874 this.options.transitionDuration = transitionDuration; 3875 return returnValue; 3876 }; 3877 3878 // ----- helper methods ----- // 3879 3880 /** 3881 * getter method for getting filtered item elements 3882 * @returns {Array} elems - collection of item elements 3883 */ 3884 Isotope.prototype.getFilteredItemElements = function() { 3885 var elems = []; 3886 for ( var i=0, len = this.filteredItems.length; i < len; i++ ) { 3887 elems.push( this.filteredItems[i].element ); 3888 } 3889 return elems; 3890 }; 3891 3892 // ----- ----- // 3893 3894 return Isotope; 3895} 3896 3897// -------------------------- transport -------------------------- // 3898 3899if ( typeof define === 'function' && define.amd ) { 3900 // AMD 3901 define( [ 3902 'outlayer/outlayer', 3903 'get-size/get-size', 3904 'matches-selector/matches-selector', 3905 'isotope/js/item', 3906 'isotope/js/layout-mode', 3907 // include default layout modes 3908 'isotope/js/layout-modes/masonry', 3909 'isotope/js/layout-modes/fit-rows', 3910 'isotope/js/layout-modes/vertical' 3911 ], 3912 isotopeDefinition ); 3913} else { 3914 // browser global 3915 window.Isotope = isotopeDefinition( 3916 window.Outlayer, 3917 window.getSize, 3918 window.matchesSelector, 3919 window.Isotope.Item, 3920 window.Isotope.LayoutMode 3921 ); 3922} 3923 3924})( window ); 3925
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.