1/*! 2 * Masonry PACKAGED v3.1.5 3 * Cascading grid layout library 4 * http://masonry.desandro.com 5 * MIT License 6 * by David DeSandro 7 */ 8 9/** 10 * Bridget makes jQuery widgets 11 * v1.0.1 12 */ 13 14( function( window ) { 15 16 17 18// -------------------------- utils -------------------------- // 19 20var slice = Array.prototype.slice; 21 22function noop() {} 23 24// -------------------------- definition -------------------------- // 25 26function defineBridget( $ ) { 27 28// bail if no jQuery 29if ( !$ ) { 30 return; 31} 32 33// -------------------------- addOptionMethod -------------------------- // 34 35/** 36 * adds option method -> $().plugin('option', {...}) 37 * @param {Function} PluginClass - constructor class 38 */ 39function addOptionMethod( PluginClass ) { 40 // don't overwrite original option method 41 if ( PluginClass.prototype.option ) { 42 return; 43 } 44 45 // option setter 46 PluginClass.prototype.option = function( opts ) { 47 // bail out if not an object 48 if ( !$.isPlainObject( opts ) ){ 49 return; 50 } 51 this.options = $.extend( true, this.options, opts ); 52 }; 53} 54 55 56// -------------------------- plugin bridge -------------------------- // 57 58// helper function for logging errors 59// $.error breaks jQuery chaining 60var logError = typeof console === 'undefined' ? noop : 61 function( message ) { 62 console.error( message ); 63 }; 64 65/** 66 * jQuery plugin bridge, access methods like $elem.plugin('method') 67 * @param {String} namespace - plugin name 68 * @param {Function} PluginClass - constructor class 69 */ 70function bridge( namespace, PluginClass ) { 71 // add to jQuery fn namespace 72 $.fn[ namespace ] = function( options ) { 73 if ( typeof options === 'string' ) { 74 // call plugin method when first argument is a string 75 // get arguments for method 76 var args = slice.call( arguments, 1 ); 77 78 for ( var i=0, len = this.length; i < len; i++ ) { 79 var elem = this[i]; 80 var instance = $.data( elem, namespace ); 81 if ( !instance ) { 82 logError( "cannot call methods on " + namespace + " prior to initialization; " + 83 "attempted to call '" + options + "'" ); 84 continue; 85 } 86 if ( !$.isFunction( instance[options] ) || options.charAt(0) === '_' ) { 87 logError( "no such method '" + options + "' for " + namespace + " instance" ); 88 continue; 89 } 90 91 // trigger method with arguments 92 var returnValue = instance[ options ].apply( instance, args ); 93 94 // break look and return first value if provided 95 if ( returnValue !== undefined ) { 96 return returnValue; 97 } 98 } 99 // return this if no return value 100 return this; 101 } else { 102 return this.each( function() { 103 var instance = $.data( this, namespace ); 104 if ( instance ) { 105 // apply options & init 106 instance.option( options ); 107 instance._init(); 108 } else { 109 // initialize new instance 110 instance = new PluginClass( this, options ); 111 $.data( this, namespace, instance ); 112 } 113 }); 114 } 115 }; 116 117} 118 119// -------------------------- bridget -------------------------- // 120 121/** 122 * converts a Prototypical class into a proper jQuery plugin 123 * the class must have a ._init method 124 * @param {String} namespace - plugin name, used in $().pluginName 125 * @param {Function} PluginClass - constructor class 126 */ 127$.bridget = function( namespace, PluginClass ) { 128 addOptionMethod( PluginClass ); 129 bridge( namespace, PluginClass ); 130}; 131 132return $.bridget; 133 134} 135 136// transport 137if ( typeof define === 'function' && define.amd ) { 138 // AMD 139 define( 'jquery-bridget/jquery.bridget',[ 'jquery' ], defineBridget ); 140} else { 141 // get jquery from browser global 142 defineBridget( window.jQuery ); 143} 144 145})( window ); 146 147/*! 148 * eventie v1.0.5 149 * event binding helper 150 * eventie.bind( elem, 'click', myFn ) 151 * eventie.unbind( elem, 'click', myFn ) 152 * MIT license 153 */ 154 155/*jshint browser: true, undef: true, unused: true */ 156/*global define: false, module: false */ 157 158( function( window ) { 159 160 161 162var docElem = document.documentElement; 163 164var bind = function() {}; 165 166function getIEEvent( obj ) { 167 var event = window.event; 168 // add event.target 169 event.target = event.target || event.srcElement || obj; 170 return event; 171} 172 173if ( docElem.addEventListener ) { 174 bind = function( obj, type, fn ) { 175 obj.addEventListener( type, fn, false ); 176 }; 177} else if ( docElem.attachEvent ) { 178 bind = function( obj, type, fn ) { 179 obj[ type + fn ] = fn.handleEvent ? 180 function() { 181 var event = getIEEvent( obj ); 182 fn.handleEvent.call( fn, event ); 183 } : 184 function() { 185 var event = getIEEvent( obj ); 186 fn.call( obj, event ); 187 }; 188 obj.attachEvent( "on" + type, obj[ type + fn ] ); 189 }; 190} 191 192var unbind = function() {}; 193 194if ( docElem.removeEventListener ) { 195 unbind = function( obj, type, fn ) { 196 obj.removeEventListener( type, fn, false ); 197 }; 198} else if ( docElem.detachEvent ) { 199 unbind = function( obj, type, fn ) { 200 obj.detachEvent( "on" + type, obj[ type + fn ] ); 201 try { 202 delete obj[ type + fn ]; 203 } catch ( err ) { 204 // can't delete window object properties 205 obj[ type + fn ] = undefined; 206 } 207 }; 208} 209 210var eventie = { 211 bind: bind, 212 unbind: unbind 213}; 214 215// ----- module definition ----- // 216 217if ( typeof define === 'function' && define.amd ) { 218 // AMD 219 define( 'eventie/eventie',eventie ); 220} else if ( typeof exports === 'object' ) { 221 // CommonJS 222 module.exports = eventie; 223} else { 224 // browser global 225 window.eventie = eventie; 226} 227 228})( this ); 229 230/*! 231 * docReady 232 * Cross browser DOMContentLoaded event emitter 233 */ 234 235/*jshint browser: true, strict: true, undef: true, unused: true*/ 236/*global define: false */ 237 238( function( window ) { 239 240 241 242var document = window.document; 243// collection of functions to be triggered on ready 244var queue = []; 245 246function docReady( fn ) { 247 // throw out non-functions 248 if ( typeof fn !== 'function' ) { 249 return; 250 } 251 252 if ( docReady.isReady ) { 253 // ready now, hit it 254 fn(); 255 } else { 256 // queue function when ready 257 queue.push( fn ); 258 } 259} 260 261docReady.isReady = false;
262 263// triggered on various doc ready events 264function init( event ) { 265 // bail if IE8 document is not ready just yet 266 var isIE8NotReady = event.type === 'readystatechange' && document.readyState !== 'complete'; 267 if ( docReady.isReady || isIE8NotReady ) { 268 return; 269 } 270 docReady.isReady = true; 271 272 // process queue 273 for ( var i=0, len = queue.length; i < len; i++ ) { 274 var fn = queue[i]; 275 fn(); 276 } 277} 278 279function defineDocReady( eventie ) { 280 eventie.bind( document, 'DOMContentLoaded', init ); 281 eventie.bind( document, 'readystatechange', init ); 282 eventie.bind( window, 'load', init ); 283 284 return docReady; 285} 286 287// transport 288if ( typeof define === 'function' && define.amd ) { 289 // AMD 290 // if RequireJS, then doc is already ready 291 docReady.isReady = typeof requirejs === 'function'; 292 define( 'doc-ready/doc-ready',[ 'eventie/eventie' ], defineDocReady ); 293} else { 294 // browser global 295 window.docReady = defineDocReady( window.eventie ); 296} 297 298})( this ); 299 300/*! 301 * EventEmitter v4.2.7 - git.io/ee 302 * Oliver Caldwell 303 * MIT license 304 * @preserve 305 */ 306 307(function () { 308 309 310 /** 311 * Class for managing events. 312 * Can be extended to provide event functionality in other classes. 313 * 314 * @class EventEmitter Manages event registering and emitting. 315 */ 316 function EventEmitter() {} 317 318 // Shortcuts to improve speed and size 319 var proto = EventEmitter.prototype; 320 var exports = this; 321 var originalGlobalValue = exports.EventEmitter;
vendor: 15,308 bytes, lines 321-759
321 322 323 /** 324 * Finds the index of the listener for the event in it's storage array. 325 * 326 * @param {Function[]} listeners Array of listeners to search through. 327 * @param {Function} listener Method to look for. 328 * @return {Number} Index of the specified listener, -1 if not found 329 * @api private 330 */ 331 function indexOfListener(listeners, listener) { 332 var i = listeners.length; 333 while (i--) { 334 if (listeners[i].listener === listener) { 335 return i; 336 } 337 } 338 339 return -1; 340 } 341 342 /** 343 * Alias a method while keeping the context correct, to allow for overwriting of target method. 344 * 345 * @param {String} name The name of the target method. 346 * @return {Function} The aliased method 347 * @api private 348 */ 349 function alias(name) { 350 return function aliasClosure() { 351 return this[name].apply(this, arguments); 352 }; 353 } 354 355 /** 356 * Returns the listener array for the specified event. 357 * Will initialise the event object and listener arrays if required. 358 * 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. 359 * Each property in the object response is an array of listener functions. 360 * 361 * @param {String|RegExp} evt Name of the event to return the listeners from. 362 * @return {Function[]|Object} All listener functions for the event. 363 */ 364 proto.getListeners = function getListeners(evt) { 365 var events = this._getEvents(); 366 var response; 367 var key; 368 369 // Return a concatenated array of all matching events if 370 // the selector is a regular expression. 371 if (evt instanceof RegExp) { 372 response = {}; 373 for (key in events) { 374 if (events.hasOwnProperty(key) && evt.test(key)) { 375 response[key] = events[key]; 376 } 377 } 378 } 379 else { 380 response = events[evt] || (events[evt] = []); 381 } 382 383 return response; 384 }; 385 386 /** 387 * Takes a list of listener objects and flattens it into a list of listener functions. 388 * 389 * @param {Object[]} listeners Raw listener objects. 390 * @return {Function[]} Just the listener functions. 391 */ 392 proto.flattenListeners = function flattenListeners(listeners) { 393 var flatListeners = []; 394 var i; 395 396 for (i = 0; i < listeners.length; i += 1) { 397 flatListeners.push(listeners[i].listener); 398 } 399 400 return flatListeners; 401 }; 402 403 /** 404 * 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. 405 * 406 * @param {String|RegExp} evt Name of the event to return the listeners from. 407 * @return {Object} All listener functions for an event in an object. 408 */ 409 proto.getListenersAsObject = function getListenersAsObject(evt) { 410 var listeners = this.getListeners(evt); 411 var response; 412 413 if (listeners instanceof Array) { 414 response = {}; 415 response[evt] = listeners; 416 } 417 418 return response || listeners; 419 }; 420 421 /** 422 * Adds a listener function to the specified event. 423 * The listener will not be added if it is a duplicate. 424 * If the listener returns true then it will be removed after it is called. 425 * If you pass a regular expression as the event name then the listener will be added to all events that match it. 426 * 427 * @param {String|RegExp} evt Name of the event to attach the listener to. 428 * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling. 429 * @return {Object} Current instance of EventEmitter for chaining. 430 */ 431 proto.addListener = function addListener(evt, listener) { 432 var listeners = this.getListenersAsObject(evt); 433 var listenerIsWrapped = typeof listener === 'object'; 434 var key; 435 436 for (key in listeners) { 437 if (listeners.hasOwnProperty(key) && indexOfListener(listeners[key], listener) === -1) { 438 listeners[key].push(listenerIsWrapped ? listener : { 439 listener: listener, 440 once: false 441 }); 442 } 443 } 444 445 return this; 446 }; 447 448 /** 449 * Alias of addListener 450 */ 451 proto.on = alias('addListener'); 452 453 /** 454 * Semi-alias of addListener. It will add a listener that will be 455 * automatically removed after it's first execution. 456 * 457 * @param {String|RegExp} evt Name of the event to attach the listener to. 458 * @param {Function} listener Method to be called when the event is emitted. If the function returns true then it will be removed after calling. 459 * @return {Object} Current instance of EventEmitter for chaining. 460 */ 461 proto.addOnceListener = function addOnceListener(evt, listener) { 462 return this.addListener(evt, { 463 listener: listener, 464 once: true 465 }); 466 }; 467 468 /** 469 * Alias of addOnceListener. 470 */ 471 proto.once = alias('addOnceListener'); 472 473 /** 474 * 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. 475 * You need to tell it what event names should be matched by a regex. 476 * 477 * @param {String} evt Name of the event to create. 478 * @return {Object} Current instance of EventEmitter for chaining. 479 */ 480 proto.defineEvent = function defineEvent(evt) { 481 this.getListeners(evt); 482 return this; 483 }; 484 485 /** 486 * Uses defineEvent to define multiple events. 487 * 488 * @param {String[]} evts An array of event names to define. 489 * @return {Object} Current instance of EventEmitter for chaining. 490 */ 491 proto.defineEvents = function defineEvents(evts) { 492 for (var i = 0; i < evts.length; i += 1) { 493 this.defineEvent(evts[i]); 494 } 495 return this; 496 }; 497 498 /** 499 * Removes a listener function from the specified event. 500 * When passed a regular expression as the event name, it will remove the listener from all events that match it. 501 * 502 * @param {String|RegExp} evt Name of the event to remove the listener from. 503 * @param {Function} listener Method to remove from the event. 504 * @return {Object} Current instance of EventEmitter for chaining. 505 */ 506 proto.removeListener = function removeListener(evt, listener) { 507 var listeners = this.getListenersAsObject(evt); 508 var index; 509 var key; 510 511 for (key in listeners) { 512 if (listeners.hasOwnProperty(key)) { 513 index = indexOfListener(listeners[key], listener); 514 515 if (index !== -1) { 516 listeners[key].splice(index, 1); 517 } 518 } 519 } 520 521 return this; 522 }; 523 524 /** 525 * Alias of removeListener 526 */ 527 proto.off = alias('removeListener'); 528 529 /** 530 * Adds listeners in bulk using the manipulateListeners method. 531 * 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. 532 * You can also pass it a regular expression to add the array of listeners to all events that match it. 533 * Yeah, this function does quite a bit. That's probably a bad thing. 534 * 535 * @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. 536 * @param {Function[]} [listeners] An optional array of listener functions to add. 537 * @return {Object} Current instance of EventEmitter for chaining. 538 */ 539 proto.addListeners = function addListeners(evt, listeners) { 540 // Pass through to manipulateListeners 541 return this.manipulateListeners(false, evt, listeners); 542 }; 543 544 /** 545 * Removes listeners in bulk using the manipulateListeners method. 546 * 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. 547 * You can also pass it an event name and an array of listeners to be removed. 548 * You can also pass it a regular expression to remove the listeners from all events that match it. 549 * 550 * @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. 551 * @param {Function[]} [listeners] An optional array of listener functions to remove. 552 * @return {Object} Current instance of EventEmitter for chaining. 553 */ 554 proto.removeListeners = function removeListeners(evt, listeners) { 555 // Pass through to manipulateListeners 556 return this.manipulateListeners(true, evt, listeners); 557 }; 558 559 /** 560 * 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. 561 * The first argument will determine if the listeners are removed (true) or added (false). 562 * 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. 563 * You can also pass it an event name and an array of listeners to be added/removed. 564 * You can also pass it a regular expression to manipulate the listeners of all events that match it. 565 * 566 * @param {Boolean} remove True if you want to remove listeners, false if you want to add. 567 * @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. 568 * @param {Function[]} [listeners] An optional array of listener functions to add/remove. 569 * @return {Object} Current instance of EventEmitter for chaining. 570 */ 571 proto.manipulateListeners = function manipulateListeners(remove, evt, listeners) { 572 var i; 573 var value; 574 var single = remove ? this.removeListener : this.addListener; 575 var multiple = remove ? this.removeListeners : this.addListeners; 576 577 // If evt is an object then pass each of it's properties to this method 578 if (typeof evt === 'object' && !(evt instanceof RegExp)) { 579 for (i in evt) { 580 if (evt.hasOwnProperty(i) && (value = evt[i])) { 581 // Pass the single listener straight through to the singular method 582 if (typeof value === 'function') { 583 single.call(this, i, value); 584 } 585 else { 586 // Otherwise pass back to the multiple function 587 multiple.call(this, i, value); 588 } 589 } 590 } 591 } 592 else { 593 // So evt must be a string 594 // And listeners must be an array of listeners 595 // Loop over it and pass each one to the multiple method 596 i = listeners.length; 597 while (i--) { 598 single.call(this, evt, listeners[i]); 599 } 600 } 601 602 return this; 603 }; 604 605 /** 606 * Removes all listeners from a specified event. 607 * If you do not specify an event then all listeners will be removed. 608 * That means every event will be emptied. 609 * You can also pass a regex to remove all events that match it. 610 * 611 * @param {String|RegExp} [evt] Optional name of the event to remove all listeners for. Will remove from every event if not passed. 612 * @return {Object} Current instance of EventEmitter for chaining. 613 */ 614 proto.removeEvent = function removeEvent(evt) { 615 var type = typeof evt; 616 var events = this._getEvents(); 617 var key; 618 619 // Remove different things depending on the state of evt 620 if (type === 'string') { 621 // Remove all listeners for the specified event 622 delete events[evt]; 623 } 624 else if (evt instanceof RegExp) { 625 // Remove all events matching the regex. 626 for (key in events) { 627 if (events.hasOwnProperty(key) && evt.test(key)) { 628 delete events[key]; 629 } 630 } 631 } 632 else { 633 // Remove all listeners in all events 634 delete this._events; 635 } 636 637 return this; 638 }; 639 640 /** 641 * Alias of removeEvent. 642 * 643 * Added to mirror the node API. 644 */ 645 proto.removeAllListeners = alias('removeEvent'); 646 647 /** 648 * Emits an event of your choice. 649 * When emitted, every listener attached to that event will be executed. 650 * If you pass the optional argument array then those arguments will be passed to every listener upon execution. 651 * Because it uses `apply`, your array of arguments will be passed as if you wrote them out separately. 652 * So they will not arrive within the array on the other side, they will be separate. 653 * You can also pass a regular expression to emit to all events that match it. 654 * 655 * @param {String|RegExp} evt Name of the event to emit and execute listeners for. 656 * @param {Array} [args] Optional array of arguments to be passed to each listener. 657 * @return {Object} Current instance of EventEmitter for chaining. 658 */ 659 proto.emitEvent = function emitEvent(evt, args) { 660 var listeners = this.getListenersAsObject(evt); 661 var listener; 662 var i; 663 var key; 664 var response; 665 666 for (key in listeners) { 667 if (listeners.hasOwnProperty(key)) { 668 i = listeners[key].length; 669 670 while (i--) { 671 // If the listener returns true then it shall be removed from the event 672 // The function is executed either with a basic call or an apply if there is an args array 673 listener = listeners[key][i]; 674 675 if (listener.once === true) { 676 this.removeListener(evt, listener.listener); 677 } 678 679 response = listener.listener.apply(this, args || []); 680 681 if (response === this._getOnceReturnValue()) { 682 this.removeListener(evt, listener.listener); 683 } 684 } 685 } 686 } 687 688 return this; 689 }; 690 691 /** 692 * Alias of emitEvent 693 */ 694 proto.trigger = alias('emitEvent'); 695 696 /** 697 * 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. 698 * As with emitEvent, you can pass a regex in place of the event name to emit to all events that match it. 699 * 700 * @param {String|RegExp} evt Name of the event to emit and execute listeners for. 701 * @param {...*} Optional additional arguments to be passed to each listener. 702 * @return {Object} Current instance of EventEmitter for chaining. 703 */ 704 proto.emit = function emit(evt) { 705 var args = Array.prototype.slice.call(arguments, 1); 706 return this.emitEvent(evt, args); 707 }; 708 709 /** 710 * Sets the current value to check against when executing listeners. If a 711 * listeners return value matches the one set here then it will be removed 712 * after execution. This value defaults to true. 713 * 714 * @param {*} value The new value to check for when executing listeners. 715 * @return {Object} Current instance of EventEmitter for chaining. 716 */ 717 proto.setOnceReturnValue = function setOnceReturnValue(value) { 718 this._onceReturnValue = value; 719 return this; 720 }; 721 722 /** 723 * Fetches the current value to check against when executing listeners. If 724 * the listeners return value matches this one then it should be removed 725 * automatically. It will return true by default. 726 * 727 * @return {*|Boolean} The current value to check for or the default, true. 728 * @api private 729 */ 730 proto._getOnceReturnValue = function _getOnceReturnValue() { 731 if (this.hasOwnProperty('_onceReturnValue')) { 732 return this._onceReturnValue; 733 } 734 else { 735 return true; 736 } 737 }; 738 739 /** 740 * Fetches the events object and creates one if required. 741 * 742 * @return {Object} The events storage object. 743 * @api private 744 */ 745 proto._getEvents = function _getEvents() { 746 return this._events || (this._events = {}); 747 }; 748 749 /** 750 * Reverts the global {@link EventEmitter} to its previous value and returns a reference to this version. 751 * 752 * @return {Function} Non conflicting EventEmitter class. 753 */ 754 EventEmitter.noConflict = function noConflict() { 755 exports.EventEmitter = originalGlobalValue; 756 return EventEmitter; 757 }; 758 759
759// Expose the class either via AMD, CommonJS or the global object 760 if (typeof define === 'function' && define.amd) { 761 define('eventEmitter/EventEmitter',[],function () { 762 return EventEmitter; 763 }); 764 } 765 else if (typeof module === 'object' && module.exports){ 766 module.exports = EventEmitter; 767 } 768 else { 769 this.EventEmitter = EventEmitter; 770 } 771}.call(this)); 772 773/*! 774 * getStyleProperty v1.0.3 775 * original by kangax 776 * http://perfectionkills.com/feature-testing-css-properties/ 777 */ 778 779/*jshint browser: true, strict: true, undef: true */ 780/*global define: false, exports: false, module: false */ 781 782( function( window ) { 783 784 785 786var prefixes = 'Webkit Moz ms Ms O'.split(' '); 787var docElemStyle = document.documentElement.style; 788 789function getStyleProperty( propName ) { 790 if ( !propName ) { 791 return; 792 } 793 794 // test standard property first 795 if ( typeof docElemStyle[ propName ] === 'string' ) { 796 return propName; 797 } 798 799 // capitalize 800 propName = propName.charAt(0).toUpperCase() + propName.slice(1); 801 802 // test vendor specific properties 803 var prefixed; 804 for ( var i=0, len = prefixes.length; i < len; i++ ) { 805 prefixed = prefixes[i] + propName; 806 if ( typeof docElemStyle[ prefixed ] === 'string' ) { 807 return prefixed; 808 } 809 } 810} 811 812// transport 813if ( typeof define === 'function' && define.amd ) { 814 // AMD 815 define( 'get-style-property/get-style-property',[],function() { 816 return getStyleProperty; 817 }); 818} else if ( typeof exports === 'object' ) { 819 // CommonJS for Component 820 module.exports = getStyleProperty; 821} else { 822 // browser global 823 window.getStyleProperty = getStyleProperty; 824} 825 826})( window ); 827 828/** 829 * getSize v1.1.7 830 * measure size of elements 831 */ 832 833/*jshint browser: true, strict: true, undef: true, unused: true */ 834/*global define: false, exports: false, require: false, module: false */ 835 836( function( window, undefined ) { 837 838 839 840// -------------------------- helpers -------------------------- // 841 842var getComputedStyle = window.getComputedStyle; 843var getStyle = getComputedStyle ? 844 function( elem ) { 845 return getComputedStyle( elem, null ); 846 } : 847 function( elem ) { 848 return elem.currentStyle; 849 }; 850 851// get a number from a string, not a percentage 852function getStyleSize( value ) { 853 var num = parseFloat( value ); 854 // not a percent like '100%', and a number 855 var isValid = value.indexOf('%') === -1 && !isNaN( num ); 856 return isValid && num; 857} 858 859// -------------------------- measurements -------------------------- // 860 861var measurements = [ 862 'paddingLeft', 863 'paddingRight', 864 'paddingTop', 865 'paddingBottom', 866 'marginLeft', 867 'marginRight', 868 'marginTop', 869 'marginBottom', 870 'borderLeftWidth', 871 'borderRightWidth', 872 'borderTopWidth', 873 'borderBottomWidth' 874]; 875 876function getZeroSize() { 877 var size = { 878 width: 0, 879 height: 0, 880 innerWidth: 0, 881 innerHeight: 0, 882 outerWidth: 0, 883 outerHeight: 0 884 }; 885 for ( var i=0, len = measurements.length; i < len; i++ ) { 886 var measurement = measurements[i]; 887 size[ measurement ] = 0; 888 } 889 return size; 890} 891 892 893 894function defineGetSize( getStyleProperty ) { 895 896// -------------------------- box sizing -------------------------- // 897 898var boxSizingProp = getStyleProperty('boxSizing'); 899var isBoxSizeOuter; 900 901/** 902 * WebKit measures the outer-width on style.width on border-box elems 903 * IE & Firefox measures the inner-width 904 */ 905( function() { 906 if ( !boxSizingProp ) { 907 return; 908 } 909 910 var div = document.createElement('div'); 911 div.style.width = '200px'; 912 div.style.padding = '1px 2px 3px 4px'; 913 div.style.borderStyle = 'solid'; 914 div.style.borderWidth = '1px 2px 3px 4px'; 915 div.style[ boxSizingProp ] = 'border-box'; 916 917 var body = document.body || document.documentElement; 918 body.appendChild( div ); 919 var style = getStyle( div ); 920 921 isBoxSizeOuter = getStyleSize( style.width ) === 200; 922 body.removeChild( div ); 923})(); 924 925 926// -------------------------- getSize -------------------------- // 927 928function getSize( elem ) { 929 // use querySeletor if elem is string 930 if ( typeof elem === 'string' ) { 931 elem = document.querySelector( elem ); 932 } 933 934 // do not proceed on non-objects 935 if ( !elem || typeof elem !== 'object' || !elem.nodeType ) { 936 return; 937 } 938 939 var style = getStyle( elem ); 940 941 // if hidden, everything is 0 942 if ( style.display === 'none' ) { 943 return getZeroSize(); 944 } 945 946 var size = {}; 947 size.width = elem.offsetWidth; 948 size.height = elem.offsetHeight; 949 950 var isBorderBox = size.isBorderBox = !!( boxSizingProp && 951 style[ boxSizingProp ] && style[ boxSizingProp ] === 'border-box' ); 952 953 // get all measurements 954 for ( var i=0, len = measurements.length; i < len; i++ ) { 955 var measurement = measurements[i]; 956 var value = style[ measurement ]; 957 value = mungeNonPixel( elem, value ); 958 var num = parseFloat( value ); 959 // any 'auto', 'medium' value will be 0 960 size[ measurement ] = !isNaN( num ) ? num : 0; 961 } 962 963 var paddingWidth = size.paddingLeft + size.paddingRight; 964 var paddingHeight = size.paddingTop + size.paddingBottom; 965 var marginWidth = size.marginLeft + size.marginRight; 966 var marginHeight = size.marginTop + size.marginBottom; 967 var borderWidth = size.borderLeftWidth + size.borderRightWidth; 968 var borderHeight = size.borderTopWidth + size.borderBottomWidth; 969 970 var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter; 971 972 // overwrite width and height if we can get it from style 973 var styleWidth = getStyleSize( style.width ); 974 if ( styleWidth !== false ) { 975 size.width = styleWidth + 976 // add padding and border unless it's already including it 977 ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth ); 978 } 979 980 var styleHeight = getStyleSize( style.height ); 981 if ( styleHeight !== false ) { 982 size.height = styleHeight + 983 // add padding and border unless it's already including it 984 ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight ); 985 } 986 987 size.innerWidth = size.width - ( paddingWidth + borderWidth );
988 size.innerHeight = size.height - ( paddingHeight + borderHeight ); 989 990 size.outerWidth = size.width + marginWidth; 991 size.outerHeight = size.height + marginHeight; 992 993 return size; 994} 995 996// IE8 returns percent values, not pixels 997// taken from jQuery's curCSS 998function mungeNonPixel( elem, value ) { 999 // IE8 and has percent value 1000 if ( getComputedStyle || value.indexOf('%') === -1 ) { 1001 return value; 1002 } 1003 var style = elem.style; 1004 // Remember the original values 1005 var left = style.left; 1006 var rs = elem.runtimeStyle; 1007 var rsLeft = rs && rs.left; 1008 1009 // Put in the new values to get a computed value out 1010 if ( rsLeft ) { 1011 rs.left = elem.currentStyle.left; 1012 } 1013 style.left = value; 1014 value = style.pixelLeft; 1015 1016 // Revert the changed values 1017 style.left = left; 1018 if ( rsLeft ) { 1019 rs.left = rsLeft; 1020 } 1021 1022 return value; 1023} 1024 1025return getSize; 1026 1027} 1028 1029// transport 1030if ( typeof define === 'function' && define.amd ) { 1031 // AMD for RequireJS 1032 define( 'get-size/get-size',[ 'get-style-property/get-style-property' ], defineGetSize ); 1033} else if ( typeof exports === 'object' ) { 1034 // CommonJS for Component 1035 module.exports = defineGetSize( require('get-style-property') ); 1036} else { 1037 // browser global 1038 window.getSize = defineGetSize( window.getStyleProperty ); 1039} 1040 1041})( window ); 1042 1043/** 1044 * matchesSelector helper v1.0.1 1045 * 1046 * @name matchesSelector 1047 * @param {Element} elem 1048 * @param {String} selector 1049 */ 1050 1051/*jshint browser: true, strict: true, undef: true, unused: true */ 1052/*global define: false */ 1053 1054( function( global, ElemProto ) { 1055 1056 1057 1058 var matchesMethod = ( function() { 1059 // check un-prefixed 1060 if ( ElemProto.matchesSelector ) { 1061 return 'matchesSelector'; 1062 } 1063 // check vendor prefixes 1064 var prefixes = [ 'webkit', 'moz', 'ms', 'o' ]; 1065 1066 for ( var i=0, len = prefixes.length; i < len; i++ ) { 1067 var prefix = prefixes[i]; 1068 var method = prefix + 'MatchesSelector'; 1069 if ( ElemProto[ method ] ) { 1070 return method; 1071 } 1072 } 1073 })(); 1074 1075 // ----- match ----- // 1076 1077 function match( elem, selector ) { 1078 return elem[ matchesMethod ]( selector ); 1079 } 1080 1081 // ----- appendToFragment ----- // 1082 1083 function checkParent( elem ) { 1084 // not needed if already has parent 1085 if ( elem.parentNode ) { 1086 return; 1087 } 1088 var fragment = document.createDocumentFragment(); 1089 fragment.appendChild( elem ); 1090 } 1091 1092 // ----- query ----- // 1093 1094 // fall back to using QSA 1095 // thx @jonathantneal https://gist.github.com/3062955 1096 function query( elem, selector ) { 1097 // append to fragment if no parent 1098 checkParent( elem ); 1099 1100 // match elem with all selected elems of parent 1101 var elems = elem.parentNode.querySelectorAll( selector ); 1102 for ( var i=0, len = elems.length; i < len; i++ ) { 1103 // return true if match 1104 if ( elems[i] === elem ) { 1105 return true; 1106 } 1107 } 1108 // otherwise return false 1109 return false; 1110 } 1111 1112 // ----- matchChild ----- // 1113 1114 function matchChild( elem, selector ) { 1115 checkParent( elem ); 1116 return match( elem, selector ); 1117 } 1118 1119 // ----- matchesSelector ----- // 1120 1121 var matchesSelector; 1122 1123 if ( matchesMethod ) { 1124 // IE9 supports matchesSelector, but doesn't work on orphaned elems 1125 // check for that 1126 var div = document.createElement('div'); 1127 var supportsOrphans = match( div, 'div' ); 1128 matchesSelector = supportsOrphans ? match : matchChild; 1129 } else { 1130 matchesSelector = query; 1131 } 1132 1133 // transport 1134 if ( typeof define === 'function' && define.amd ) { 1135 // AMD 1136 define( 'matches-selector/matches-selector',[],function() { 1137 return matchesSelector; 1138 }); 1139 } else { 1140 // browser global 1141 window.matchesSelector = matchesSelector; 1142 } 1143 1144})( this, Element.prototype ); 1145 1146/** 1147 * Outlayer Item 1148 */ 1149 1150( function( window ) { 1151 1152 1153 1154// ----- get style ----- // 1155 1156var getComputedStyle = window.getComputedStyle; 1157var getStyle = getComputedStyle ? 1158 function( elem ) { 1159 return getComputedStyle( elem, null ); 1160 } : 1161 function( elem ) { 1162 return elem.currentStyle; 1163 }; 1164 1165 1166// extend objects 1167function extend( a, b ) { 1168 for ( var prop in b ) { 1169 a[ prop ] = b[ prop ]; 1170 } 1171 return a; 1172} 1173 1174function isEmptyObj( obj ) { 1175 for ( var prop in obj ) { 1176 return false;
1177 } 1178 prop = null; 1179 return true; 1180} 1181 1182// http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/ 1183function toDash( str ) { 1184 return str.replace( /([A-Z])/g, function( $1 ){ 1185 return '-' + $1.toLowerCase(); 1186 }); 1187} 1188 1189// -------------------------- Outlayer definition -------------------------- // 1190 1191function outlayerItemDefinition( EventEmitter, getSize, getStyleProperty ) { 1192 1193// -------------------------- CSS3 support -------------------------- // 1194 1195var transitionProperty = getStyleProperty('transition'); 1196var transformProperty = getStyleProperty('transform'); 1197var supportsCSS3 = transitionProperty && transformProperty; 1198var is3d = !!getStyleProperty('perspective'); 1199 1200var transitionEndEvent = { 1201 WebkitTransition: 'webkitTransitionEnd', 1202 MozTransition: 'transitionend', 1203 OTransition: 'otransitionend', 1204 transition: 'transitionend' 1205}[ transitionProperty ]; 1206 1207// properties that could have vendor prefix 1208var prefixableProperties = [ 1209 'transform', 1210 'transition', 1211 'transitionDuration', 1212 'transitionProperty' 1213]; 1214 1215// cache all vendor properties 1216var vendorProperties = ( function() { 1217 var cache = {}; 1218 for ( var i=0, len = prefixableProperties.length; i < len; i++ ) { 1219 var prop = prefixableProperties[i]; 1220 var supportedProp = getStyleProperty( prop ); 1221 if ( supportedProp && supportedProp !== prop ) { 1222 cache[ prop ] = supportedProp; 1223 } 1224 } 1225 return cache; 1226})(); 1227 1228// -------------------------- Item -------------------------- // 1229 1230function Item( element, layout ) { 1231 if ( !element ) { 1232 return; 1233 } 1234 1235 this.element = element; 1236 // parent layout class, i.e. Masonry, Isotope, or Packery 1237 this.layout = layout; 1238 this.position = { 1239 x: 0, 1240 y: 0 1241 }; 1242 1243 this._create(); 1244} 1245 1246// inherit EventEmitter 1247extend( Item.prototype, EventEmitter.prototype ); 1248 1249Item.prototype._create = function() { 1250 // transition objects 1251 this._transn = { 1252 ingProperties: {}, 1253 clean: {}, 1254 onEnd: {} 1255 }; 1256 1257 this.css({ 1258 position: 'absolute' 1259 }); 1260}; 1261 1262// trigger specified handler for event type 1263Item.prototype.handleEvent = function( event ) { 1264 var method = 'on' + event.type; 1265 if ( this[ method ] ) { 1266 this[ method ]( event ); 1267 } 1268}; 1269 1270Item.prototype.getSize = function() { 1271 this.size = getSize( this.element ); 1272}; 1273 1274/** 1275 * apply CSS styles to element 1276 * @param {Object} style 1277 */ 1278Item.prototype.css = function( style ) { 1279 var elemStyle = this.element.style; 1280 1281 for ( var prop in style ) { 1282 // use vendor property if available 1283 var supportedProp = vendorProperties[ prop ] || prop; 1284 elemStyle[ supportedProp ] = style[ prop ]; 1285 } 1286}; 1287 1288 // measure position, and sets it 1289Item.prototype.getPosition = function() { 1290 var style = getStyle( this.element ); 1291 var layoutOptions = this.layout.options; 1292 var isOriginLeft = layoutOptions.isOriginLeft; 1293 var isOriginTop = layoutOptions.isOriginTop; 1294 var x = parseInt( style[ isOriginLeft ? 'left' : 'right' ], 10 ); 1295 var y = parseInt( style[ isOriginTop ? 'top' : 'bottom' ], 10 ); 1296 1297 // clean up 'auto' or other non-integer values 1298 x = isNaN( x ) ? 0 : x; 1299 y = isNaN( y ) ? 0 : y; 1300 // remove padding from measurement 1301 var layoutSize = this.layout.size; 1302 x -= isOriginLeft ? layoutSize.paddingLeft : layoutSize.paddingRight; 1303 y -= isOriginTop ? layoutSize.paddingTop : layoutSize.paddingBottom; 1304 1305 this.position.x = x; 1306 this.position.y = y; 1307}; 1308 1309// set settled position, apply padding 1310Item.prototype.layoutPosition = function() { 1311 var layoutSize = this.layout.size; 1312 var layoutOptions = this.layout.options; 1313 var style = {}; 1314 1315 if ( layoutOptions.isOriginLeft ) { 1316 style.left = ( this.position.x + layoutSize.paddingLeft ) + 'px'; 1317 // reset other property 1318 style.right = ''; 1319 } else { 1320 style.right = ( this.position.x + layoutSize.paddingRight ) + 'px'; 1321 style.left = ''; 1322 } 1323 1324 if ( layoutOptions.isOriginTop ) { 1325 style.top = ( this.position.y + layoutSize.paddingTop ) + 'px'; 1326 style.bottom = ''; 1327 } else { 1328 style.bottom = ( this.position.y + layoutSize.paddingBottom ) + 'px'; 1329 style.top = ''; 1330 } 1331 1332 this.css( style ); 1333 this.emitEvent( 'layout', [ this ] ); 1334}; 1335 1336 1337// transform translate function 1338var translate = is3d ? 1339 function( x, y ) { 1340 return 'translate3d(' + x + 'px, ' + y + 'px, 0)'; 1341 } : 1342 function( x, y ) { 1343 return 'translate(' + x + 'px, ' + y + 'px)'; 1344 }; 1345 1346
1347Item.prototype._transitionTo = function( x, y ) { 1348 this.getPosition(); 1349 // get current x & y from top/left 1350 var curX = this.position.x; 1351 var curY = this.position.y; 1352 1353 var compareX = parseInt( x, 10 ); 1354 var compareY = parseInt( y, 10 ); 1355 var didNotMove = compareX === this.position.x && compareY === this.position.y; 1356 1357 // save end position 1358 this.setPosition( x, y ); 1359 1360 // if did not move and not transitioning, just go to layout 1361 if ( didNotMove && !this.isTransitioning ) { 1362 this.layoutPosition(); 1363 return; 1364 } 1365 1366 var transX = x - curX; 1367 var transY = y - curY; 1368 var transitionStyle = {}; 1369 // flip cooridinates if origin on right or bottom 1370 var layoutOptions = this.layout.options; 1371 transX = layoutOptions.isOriginLeft ? transX : -transX; 1372 transY = layoutOptions.isOriginTop ? transY : -transY; 1373 transitionStyle.transform = translate( transX, transY ); 1374 1375 this.transition({ 1376 to: transitionStyle, 1377 onTransitionEnd: { 1378 transform: this.layoutPosition 1379 }, 1380 isCleaning: true 1381 }); 1382}; 1383 1384// non transition + transform support 1385Item.prototype.goTo = function( x, y ) { 1386 this.setPosition( x, y ); 1387 this.layoutPosition(); 1388}; 1389 1390// use transition and transforms if supported 1391Item.prototype.moveTo = supportsCSS3 ? 1392 Item.prototype._transitionTo : Item.prototype.goTo; 1393 1394Item.prototype.setPosition = function( x, y ) { 1395 this.position.x = parseInt( x, 10 ); 1396 this.position.y = parseInt( y, 10 ); 1397}; 1398 1399// ----- transition ----- // 1400 1401/** 1402 * @param {Object} style - CSS 1403 * @param {Function} onTransitionEnd 1404 */ 1405 1406// non transition, just trigger callback 1407Item.prototype._nonTransition = function( args ) { 1408 this.css( args.to ); 1409 if ( args.isCleaning ) { 1410 this._removeStyles( args.to ); 1411 } 1412 for ( var prop in args.onTransitionEnd ) { 1413 args.onTransitionEnd[ prop ].call( this ); 1414 } 1415}; 1416 1417/** 1418 * proper transition 1419 * @param {Object} args - arguments 1420 * @param {Object} to - style to transition to 1421 * @param {Object} from - style to start transition from 1422 * @param {Boolean} isCleaning - removes transition styles after transition 1423 * @param {Function} onTransitionEnd - callback 1424 */ 1425Item.prototype._transition = function( args ) { 1426 // redirect to nonTransition if no transition duration 1427 if ( !parseFloat( this.layout.options.transitionDuration ) ) { 1428 this._nonTransition( args ); 1429 return; 1430 } 1431 1432 var _transition = this._transn; 1433 // keep track of onTransitionEnd callback by css property 1434 for ( var prop in args.onTransitionEnd ) { 1435 _transition.onEnd[ prop ] = args.onTransitionEnd[ prop ]; 1436 } 1437 // keep track of properties that are transitioning 1438 for ( prop in args.to ) { 1439 _transition.ingProperties[ prop ] = true; 1440 // keep track of properties to clean up when transition is done 1441 if ( args.isCleaning ) { 1442 _transition.clean[ prop ] = true; 1443 } 1444 } 1445 1446 // set from styles 1447 if ( args.from ) { 1448 this.css( args.from ); 1449 // force redraw. http://blog.alexmaccaw.com/css-transitions 1450 var h = this.element.offsetHeight; 1451 // hack for JSHint to hush about unused var 1452 h = null; 1453 } 1454 // enable transition 1455 this.enableTransition( args.to ); 1456 // set styles that are transitioning 1457 this.css( args.to ); 1458 1459 this.isTransitioning = true; 1460 1461}; 1462 1463var itemTransitionProperties = transformProperty && ( toDash( transformProperty ) + 1464 ',opacity' ); 1465 1466Item.prototype.enableTransition = function(/* style */) { 1467 // only enable if not already transitioning 1468 // bug in IE10 were re-setting transition style will prevent 1469 // transitionend event from triggering 1470 if ( this.isTransitioning ) { 1471 return; 1472 } 1473 1474 // make transition: foo, bar, baz from style object 1475 // TODO uncomment this bit when IE10 bug is resolved 1476 // var transitionValue = []; 1477 // for ( var prop in style ) { 1478 // // dash-ify camelCased properties like WebkitTransition 1479 // transitionValue.push( toDash( prop ) ); 1480 // } 1481 // enable transition styles 1482 // HACK always enable transform,opacity for IE10 1483 this.css({ 1484 transitionProperty: itemTransitionProperties, 1485 transitionDuration: this.layout.options.transitionDuration 1486 }); 1487 // listen for transition end event 1488 this.element.addEventListener( transitionEndEvent, this, false ); 1489}; 1490 1491Item.prototype.transition = Item.prototype[ transitionProperty ? '_transition' : '_nonTransition' ]; 1492 1493// ----- events ----- // 1494 1495Item.prototype.onwebkitTransitionEnd = function( event ) { 1496 this.ontransitionend( event ); 1497}; 1498 1499Item.prototype.onotransitionend = function( event ) { 1500 this.ontransitionend( event ); 1501}; 1502 1503// properties that I munge to make my life easier 1504var dashedVendorProperties = { 1505 '-webkit-transform': 'transform', 1506 '-moz-transform': 'transform', 1507 '-o-transform': 'transform' 1508}; 1509 1510Item.prototype.ontransitionend = function( event ) { 1511 // disregard bubbled events from children 1512 if ( event.target !== this.element ) { 1513 return; 1514 } 1515 var _transition = this._transn; 1516 // get property name of transitioned property, convert to prefix-free 1517 var propertyName = dashedVendorProperties[ event.propertyName ] || event.propertyName; 1518 1519 // remove property that has completed transitioning 1520 delete _transition.ingProperties[ propertyName ]; 1521 // check if any properties are still transitioning 1522 if ( isEmptyObj( _transition.ingProperties ) ) { 1523 // all properties have completed transitioning 1524 this.disableTransition(); 1525 } 1526 // clean style 1527 if ( propertyName in _transition.clean ) { 1528 // clean up style 1529 this.element.style[ event.propertyName ] = ''; 1530 delete _transition.clean[ propertyName ]; 1531 } 1532 // trigger onTransitionEnd callback 1533 if ( propertyName in _transition.onEnd ) { 1534 var onTransitionEnd = _transition.onEnd[ propertyName ]; 1535 onTransitionEnd.call( this ); 1536 delete _transition.onEnd[ propertyName ]; 1537 } 1538 1539 this.emitEvent( 'transitionEnd', [ this ] ); 1540}; 1541 1542Item.prototype.disableTransition = function() { 1543 this.removeTransitionStyles(); 1544 this.element.removeEventListener( transitionEndEvent, this, false ); 1545 this.isTransitioning = false;
1546}; 1547 1548/** 1549 * removes style property from element 1550 * @param {Object} style 1551**/ 1552Item.prototype._removeStyles = function( style ) { 1553 // clean up transition styles 1554 var cleanStyle = {}; 1555 for ( var prop in style ) { 1556 cleanStyle[ prop ] = ''; 1557 } 1558 this.css( cleanStyle ); 1559}; 1560 1561var cleanTransitionStyle = { 1562 transitionProperty: '', 1563 transitionDuration: '' 1564}; 1565 1566Item.prototype.removeTransitionStyles = function() { 1567 // remove transition 1568 this.css( cleanTransitionStyle ); 1569}; 1570 1571// ----- show/hide/remove ----- // 1572 1573// remove element from DOM 1574Item.prototype.removeElem = function() { 1575 this.element.parentNode.removeChild( this.element ); 1576 this.emitEvent( 'remove', [ this ] ); 1577}; 1578 1579Item.prototype.remove = function() { 1580 // just remove element if no transition support or no transition 1581 if ( !transitionProperty || !parseFloat( this.layout.options.transitionDuration ) ) { 1582 this.removeElem(); 1583 return; 1584 } 1585 1586 // start transition 1587 var _this = this; 1588 this.on( 'transitionEnd', function() { 1589 _this.removeElem(); 1590 return true; // bind once 1591 }); 1592 this.hide(); 1593}; 1594 1595Item.prototype.reveal = function() { 1596 delete this.isHidden; 1597 // remove display: none 1598 this.css({ display: '' }); 1599 1600 var options = this.layout.options; 1601 this.transition({ 1602 from: options.hiddenStyle, 1603 to: options.visibleStyle, 1604 isCleaning: true 1605 }); 1606}; 1607 1608Item.prototype.hide = function() { 1609 // set flag 1610 this.isHidden = true; 1611 // remove display: none 1612 this.css({ display: '' }); 1613 1614 var options = this.layout.options; 1615 this.transition({ 1616 from: options.visibleStyle, 1617 to: options.hiddenStyle, 1618 // keep hidden stuff hidden 1619 isCleaning: true, 1620 onTransitionEnd: { 1621 opacity: function() { 1622 // check if still hidden 1623 // during transition, item may have been un-hidden 1624 if ( this.isHidden ) { 1625 this.css({ display: 'none' }); 1626 } 1627 } 1628 } 1629 }); 1630}; 1631 1632Item.prototype.destroy = function() { 1633 this.css({ 1634 position: '', 1635 left: '', 1636 right: '', 1637 top: '', 1638 bottom: '', 1639 transition: '', 1640 transform: '' 1641 }); 1642}; 1643 1644return Item; 1645 1646} 1647 1648// -------------------------- transport -------------------------- // 1649 1650if ( typeof define === 'function' && define.amd ) { 1651 // AMD 1652 define( 'outlayer/item',[ 1653 'eventEmitter/EventEmitter', 1654 'get-size/get-size', 1655 'get-style-property/get-style-property' 1656 ], 1657 outlayerItemDefinition ); 1658} else { 1659 // browser global 1660 window.Outlayer = {}; 1661 window.Outlayer.Item = outlayerItemDefinition( 1662 window.EventEmitter, 1663 window.getSize, 1664 window.getStyleProperty 1665 ); 1666} 1667 1668})( window ); 1669 1670/*! 1671 * Outlayer v1.2.0 1672 * the brains and guts of a layout library 1673 * MIT license 1674 */ 1675 1676( function( window ) { 1677 1678 1679 1680// ----- vars ----- // 1681 1682var document = window.document; 1683var console = window.console; 1684var jQuery = window.jQuery; 1685 1686var noop = function() {}; 1687 1688// -------------------------- helpers -------------------------- // 1689 1690// extend objects 1691function extend( a, b ) { 1692 for ( var prop in b ) { 1693 a[ prop ] = b[ prop ]; 1694 } 1695 return a; 1696} 1697 1698 1699var objToString = Object.prototype.toString; 1700function isArray( obj ) { 1701 return objToString.call( obj ) === '[object Array]'; 1702} 1703 1704// turn element or nodeList into an array 1705function makeArray( obj ) { 1706 var ary = []; 1707 if ( isArray( obj ) ) { 1708 // use object if already an array 1709 ary = obj; 1710 } else if ( obj && typeof obj.length === 'number' ) { 1711 // convert nodeList to array 1712 for ( var i=0, len = obj.length; i < len; i++ ) { 1713 ary.push( obj[i] ); 1714 } 1715 } else { 1716 // array of single index 1717 ary.push( obj ); 1718 } 1719 return ary; 1720} 1721 1722// http://stackoverflow.com/a/384380/182183 1723var isElement = ( typeof HTMLElement === 'object' ) ? 1724 function isElementDOM2( obj ) { 1725 return obj instanceof HTMLElement; 1726 } : 1727 function isElementQuirky( obj ) { 1728 return obj && typeof obj === 'object' && 1729 obj.nodeType === 1 && typeof obj.nodeName === 'string'; 1730 }; 1731 1732// index of helper cause IE8 1733var indexOf = Array.prototype.indexOf ? function( ary, obj ) { 1734 return ary.indexOf( obj ); 1735 } : function( ary, obj ) { 1736 for ( var i=0, len = ary.length; i < len; i++ ) { 1737 if ( ary[i] === obj ) { 1738 return i; 1739 } 1740 } 1741 return -1; 1742 }; 1743 1744function removeFrom( obj, ary ) { 1745 var index = indexOf( ary, obj ); 1746 if ( index !== -1 ) { 1747 ary.splice( index, 1 ); 1748 } 1749} 1750 1751// http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/ 1752function toDashed( str ) { 1753 return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) { 1754 return $1 + '-' + $2; 1755 }).toLowerCase(); 1756} 1757 1758 1759function outlayerDefinition( eventie, docReady, EventEmitter, getSize, matchesSelector, Item ) { 1760 1761// -------------------------- Outlayer -------------------------- // 1762 1763// globally unique identifiers 1764var GUID = 0; 1765// internal store of all Outlayer intances 1766var instances = {}; 1767 1768 1769/** 1770 * @param {Element, String} element 1771 * @param {Object} options 1772 * @constructor 1773 */ 1774function Outlayer( element, options ) { 1775 // use element as selector string 1776 if ( typeof element === 'string' ) { 1777 element = document.querySelector( element ); 1778 } 1779 1780 // bail out if not proper element 1781 if ( !element || !isElement( element ) ) { 1782 if ( console ) { 1783 console.error( 'Bad ' + this.constructor.namespace + ' element: ' + element ); 1784 } 1785 return; 1786 } 1787 1788 this.element = element; 1789 1790 // options 1791 this.options = extend( {}, this.constructor.defaults ); 1792 this.option( options ); 1793 1794 // add id for Outlayer.getFromElement 1795 var id = ++GUID; 1796 this.element.outlayerGUID = id; // expando 1797 instances[ id ] = this; // associate via id 1798 1799 // kick it off 1800 this._create(); 1801 1802 if ( this.options.isInitLayout ) { 1803 this.layout(); 1804 } 1805} 1806 1807// settings are for internal use only 1808Outlayer.namespace = 'outlayer'; 1809Outlayer.Item = Item; 1810 1811// default options 1812Outlayer.defaults = { 1813 containerStyle: { 1814 position: 'relative' 1815 }, 1816 isInitLayout: true, 1817 isOriginLeft: true, 1818 isOriginTop: true, 1819 isResizeBound: true, 1820 isResizingContainer: true, 1821 // item options 1822 transitionDuration: '0.4s', 1823 hiddenStyle: { 1824 opacity: 0, 1825 transform: 'scale(0.001)' 1826 }, 1827 visibleStyle: { 1828 opacity: 1, 1829 transform: 'scale(1)' 1830 } 1831}; 1832 1833// inherit EventEmitter 1834extend( Outlayer.prototype, EventEmitter.prototype ); 1835 1836/** 1837 * set options 1838 * @param {Object} opts 1839 */ 1840Outlayer.prototype.option = function( opts ) { 1841 extend( this.options, opts ); 1842}; 1843 1844Outlayer.prototype._create = function() { 1845 // get items from children 1846 this.reloadItems(); 1847 // elements that affect layout, but are not laid out 1848 this.stamps = []; 1849 this.stamp( this.options.stamp ); 1850 // set container style 1851 extend( this.element.style, this.options.containerStyle ); 1852 1853 // bind resize method 1854 if ( this.options.isResizeBound ) { 1855 this.bindResize(); 1856 } 1857}; 1858 1859// goes through all children again and gets bricks in proper order 1860Outlayer.prototype.reloadItems = function() { 1861 // collection of item elements 1862 this.items = this._itemize( this.element.children ); 1863}; 1864 1865 1866/** 1867 * turn elements into Outlayer.Items to be used in layout 1868 * @param {Array or NodeList or HTMLElement} elems 1869 * @returns {Array} items - collection of new Outlayer Items 1870 */ 1871Outlayer.prototype._itemize = function( elems ) { 1872 1873 var itemElems = this._filterFindItemElements( elems ); 1874 var Item = this.constructor.Item; 1875 1876 // create new Outlayer Items for collection 1877 var items = []; 1878 for ( var i=0, len = itemElems.length; i < len; i++ ) { 1879 var elem = itemElems[i]; 1880 var item = new Item( elem, this ); 1881 items.push( item ); 1882 } 1883 1884 return items; 1885}; 1886 1887/** 1888 * get item elements to be used in layout 1889 * @param {Array or NodeList or HTMLElement} elems 1890 * @returns {Array} items - item elements 1891 */ 1892Outlayer.prototype._filterFindItemElements = function( elems ) { 1893 // make array of elems 1894 elems = makeArray( elems ); 1895 var itemSelector = this.options.itemSelector; 1896 var itemElems = []; 1897 1898 for ( var i=0, len = elems.length; i < len; i++ ) { 1899 var elem = elems[i]; 1900 // check that elem is an actual element 1901 if ( !isElement( elem ) ) { 1902 continue; 1903 } 1904 // filter & find items if we have an item selector 1905 if ( itemSelector ) { 1906 // filter siblings 1907 if ( matchesSelector( elem, itemSelector ) ) { 1908 itemElems.push( elem ); 1909 } 1910 // find children 1911 var childElems = elem.querySelectorAll( itemSelector ); 1912 // concat childElems to filterFound array 1913 for ( var j=0, jLen = childElems.length; j < jLen; j++ ) { 1914 itemElems.push( childElems[j] ); 1915 } 1916 } else { 1917 itemElems.push( elem ); 1918 } 1919 } 1920 1921 return itemElems; 1922}; 1923 1924/** 1925 * getter method for getting item elements 1926 * @returns {Array} elems - collection of item elements 1927 */ 1928Outlayer.prototype.getItemElements = function() { 1929 var elems = []; 1930 for ( var i=0, len = this.items.length; i < len; i++ ) { 1931 elems.push( this.items[i].element ); 1932 } 1933 return elems; 1934}; 1935 1936// ----- init & layout ----- // 1937 1938/** 1939 * lays out all items 1940 */ 1941Outlayer.prototype.layout = function() { 1942 this._resetLayout(); 1943 this._manageStamps(); 1944 1945 // don't animate first layout 1946 var isInstant = this.options.isLayoutInstant !== undefined ? 1947 this.options.isLayoutInstant : !this._isLayoutInited; 1948 this.layoutItems( this.items, isInstant ); 1949 1950 // flag for initalized 1951 this._isLayoutInited = true; 1952}; 1953 1954// _init is alias for layout 1955Outlayer.prototype._init = Outlayer.prototype.layout; 1956 1957/** 1958 * logic before any new layout 1959 */ 1960Outlayer.prototype._resetLayout = function() {
1961 this.getSize(); 1962}; 1963 1964 1965Outlayer.prototype.getSize = function() { 1966 this.size = getSize( this.element ); 1967}; 1968 1969/** 1970 * get measurement from option, for columnWidth, rowHeight, gutter 1971 * if option is String -> get element from selector string, & get size of element 1972 * if option is Element -> get size of element 1973 * else use option as a number 1974 * 1975 * @param {String} measurement 1976 * @param {String} size - width or height 1977 * @private 1978 */ 1979Outlayer.prototype._getMeasurement = function( measurement, size ) { 1980 var option = this.options[ measurement ]; 1981 var elem; 1982 if ( !option ) { 1983 // default to 0 1984 this[ measurement ] = 0; 1985 } else { 1986 // use option as an element 1987 if ( typeof option === 'string' ) { 1988 elem = this.element.querySelector( option ); 1989 } else if ( isElement( option ) ) { 1990 elem = option; 1991 } 1992 // use size of element, if element 1993 this[ measurement ] = elem ? getSize( elem )[ size ] : option; 1994 } 1995}; 1996 1997/** 1998 * layout a collection of item elements 1999 * @api public 2000 */ 2001Outlayer.prototype.layoutItems = function( items, isInstant ) { 2002 items = this._getItemsForLayout( items ); 2003 2004 this._layoutItems( items, isInstant ); 2005 2006 this._postLayout(); 2007}; 2008 2009/** 2010 * get the items to be laid out 2011 * you may want to skip over some items 2012 * @param {Array} items 2013 * @returns {Array} items 2014 */ 2015Outlayer.prototype._getItemsForLayout = function( items ) { 2016 var layoutItems = []; 2017 for ( var i=0, len = items.length; i < len; i++ ) { 2018 var item = items[i]; 2019 if ( !item.isIgnored ) { 2020 layoutItems.push( item ); 2021 } 2022 } 2023 return layoutItems; 2024}; 2025 2026/** 2027 * layout items 2028 * @param {Array} items 2029 * @param {Boolean} isInstant 2030 */ 2031Outlayer.prototype._layoutItems = function( items, isInstant ) { 2032 var _this = this; 2033 function onItemsLayout() { 2034 _this.emitEvent( 'layoutComplete', [ _this, items ] ); 2035 } 2036 2037 if ( !items || !items.length ) { 2038 // no items, emit event with empty array 2039 onItemsLayout(); 2040 return; 2041 } 2042 2043 // emit layoutComplete when done 2044 this._itemsOn( items, 'layout', onItemsLayout ); 2045 2046 var queue = []; 2047 2048 for ( var i=0, len = items.length; i < len; i++ ) { 2049 var item = items[i]; 2050 // get x/y object from method 2051 var position = this._getItemLayoutPosition( item ); 2052 // enqueue 2053 position.item = item; 2054 position.isInstant = isInstant || item.isLayoutInstant; 2055 queue.push( position ); 2056 } 2057 2058 this._processLayoutQueue( queue ); 2059}; 2060 2061/** 2062 * get item layout position 2063 * @param {Outlayer.Item} item 2064 * @returns {Object} x and y position 2065 */ 2066Outlayer.prototype._getItemLayoutPosition = function( /* item */ ) { 2067 return { 2068 x: 0, 2069 y: 0 2070 }; 2071}; 2072 2073/** 2074 * iterate over array and position each item 2075 * Reason being - separating this logic prevents 'layout invalidation' 2076 * thx @paul_irish 2077 * @param {Array} queue 2078 */ 2079Outlayer.prototype._processLayoutQueue = function( queue ) { 2080 for ( var i=0, len = queue.length; i < len; i++ ) { 2081 var obj = queue[i]; 2082 this._positionItem( obj.item, obj.x, obj.y, obj.isInstant ); 2083 } 2084}; 2085 2086/** 2087 * Sets position of item in DOM 2088 * @param {Outlayer.Item} item 2089 * @param {Number} x - horizontal position 2090 * @param {Number} y - vertical position 2091 * @param {Boolean} isInstant - disables transitions 2092 */ 2093Outlayer.prototype._positionItem = function( item, x, y, isInstant ) { 2094 if ( isInstant ) { 2095 // if not transition, just set CSS 2096 item.goTo( x, y ); 2097 } else { 2098 item.moveTo( x, y ); 2099 } 2100}; 2101 2102/** 2103 * Any logic you want to do after each layout, 2104 * i.e. size the container 2105 */ 2106Outlayer.prototype._postLayout = function() { 2107 this.resizeContainer(); 2108}; 2109 2110Outlayer.prototype.resizeContainer = function() { 2111 if ( !this.options.isResizingContainer ) { 2112 return; 2113 } 2114 var size = this._getContainerSize(); 2115 if ( size ) { 2116 this._setContainerMeasure( size.width, true ); 2117 this._setContainerMeasure( size.height, false ); 2118 } 2119}; 2120 2121/** 2122 * Sets width or height of container if returned 2123 * @returns {Object} size 2124 * @param {Number} width 2125 * @param {Number} height 2126 */ 2127Outlayer.prototype._getContainerSize = noop; 2128 2129/** 2130 * @param {Number} measure - size of width or height 2131 * @param {Boolean} isWidth 2132 */ 2133Outlayer.prototype._setContainerMeasure = function( measure, isWidth ) { 2134 if ( measure === undefined ) { 2135 return; 2136 } 2137 2138 var elemSize = this.size; 2139 // add padding and border width if border box 2140 if ( elemSize.isBorderBox ) { 2141 measure += isWidth ? elemSize.paddingLeft + elemSize.paddingRight + 2142 elemSize.borderLeftWidth + elemSize.borderRightWidth : 2143 elemSize.paddingBottom + elemSize.paddingTop + 2144 elemSize.borderTopWidth + elemSize.borderBottomWidth; 2145 } 2146 2147 measure = Math.max( measure, 0 ); 2148 this.element.style[ isWidth ? 'width' : 'height' ] = measure + 'px'; 2149}; 2150 2151/** 2152 * trigger a callback for a collection of items events 2153 * @param {Array} items - Outlayer.Items 2154 * @param {String} eventName 2155 * @param {Function} callback 2156 */ 2157Outlayer.prototype._itemsOn = function( items, eventName, callback ) { 2158 var doneCount = 0; 2159 var count = items.length; 2160 // event callback 2161 var _this = this; 2162 function tick() { 2163 doneCount++; 2164 if ( doneCount === count ) { 2165 callback.call( _this ); 2166 } 2167 return true; // bind once 2168 } 2169 // bind callback 2170 for ( var i=0, len = items.length; i < len; i++ ) { 2171 var item = items[i]; 2172 item.on( eventName, tick ); 2173 } 2174}; 2175 2176// -------------------------- ignore & stamps -------------------------- // 2177 2178 2179/** 2180 * keep item in collection, but do not lay it out 2181 * ignored items do not get skipped in layout 2182 * @param {Element} elem 2183 */ 2184Outlayer.prototype.ignore = function( elem ) { 2185 var item = this.getItem( elem ); 2186 if ( item ) { 2187 item.isIgnored = true; 2188 } 2189}; 2190 2191/** 2192 * return item to layout collection 2193 * @param {Element} elem 2194 */ 2195Outlayer.prototype.unignore = function( elem ) { 2196 var item = this.getItem( elem ); 2197 if ( item ) { 2198 delete item.isIgnored; 2199 } 2200}; 2201 2202/** 2203 * adds elements to stamps 2204 * @param {NodeList, Array, Element, or String} elems 2205 */ 2206Outlayer.prototype.stamp = function( elems ) { 2207 elems = this._find( elems ); 2208 if ( !elems ) { 2209 return; 2210 } 2211
2212 this.stamps = this.stamps.concat( elems ); 2213 // ignore 2214 for ( var i=0, len = elems.length; i < len; i++ ) { 2215 var elem = elems[i]; 2216 this.ignore( elem ); 2217 } 2218}; 2219 2220/** 2221 * removes elements to stamps 2222 * @param {NodeList, Array, or Element} elems 2223 */ 2224Outlayer.prototype.unstamp = function( elems ) { 2225 elems = this._find( elems ); 2226 if ( !elems ){ 2227 return; 2228 } 2229 2230 for ( var i=0, len = elems.length; i < len; i++ ) { 2231 var elem = elems[i]; 2232 // filter out removed stamp elements 2233 removeFrom( elem, this.stamps ); 2234 this.unignore( elem ); 2235 } 2236 2237}; 2238 2239/** 2240 * finds child elements 2241 * @param {NodeList, Array, Element, or String} elems 2242 * @returns {Array} elems 2243 */ 2244Outlayer.prototype._find = function( elems ) { 2245 if ( !elems ) { 2246 return; 2247 } 2248 // if string, use argument as selector string 2249 if ( typeof elems === 'string' ) { 2250 elems = this.element.querySelectorAll( elems ); 2251 } 2252 elems = makeArray( elems ); 2253 return elems; 2254}; 2255 2256Outlayer.prototype._manageStamps = function() { 2257 if ( !this.stamps || !this.stamps.length ) { 2258 return; 2259 } 2260 2261 this._getBoundingRect(); 2262 2263 for ( var i=0, len = this.stamps.length; i < len; i++ ) { 2264 var stamp = this.stamps[i]; 2265 this._manageStamp( stamp ); 2266 } 2267}; 2268 2269// update boundingLeft / Top 2270Outlayer.prototype._getBoundingRect = function() { 2271 // get bounding rect for container element 2272 var boundingRect = this.element.getBoundingClientRect(); 2273 var size = this.size; 2274 this._boundingRect = { 2275 left: boundingRect.left + size.paddingLeft + size.borderLeftWidth, 2276 top: boundingRect.top + size.paddingTop + size.borderTopWidth, 2277 right: boundingRect.right - ( size.paddingRight + size.borderRightWidth ), 2278 bottom: boundingRect.bottom - ( size.paddingBottom + size.borderBottomWidth ) 2279 }; 2280}; 2281 2282/** 2283 * @param {Element} stamp 2284**/ 2285Outlayer.prototype._manageStamp = noop; 2286 2287/** 2288 * get x/y position of element relative to container element 2289 * @param {Element} elem 2290 * @returns {Object} offset - has left, top, right, bottom 2291 */ 2292Outlayer.prototype._getElementOffset = function( elem ) { 2293 var boundingRect = elem.getBoundingClientRect(); 2294 var thisRect = this._boundingRect; 2295 var size = getSize( elem ); 2296 var offset = { 2297 left: boundingRect.left - thisRect.left - size.marginLeft, 2298 top: boundingRect.top - thisRect.top - size.marginTop, 2299 right: thisRect.right - boundingRect.right - size.marginRight, 2300 bottom: thisRect.bottom - boundingRect.bottom - size.marginBottom 2301 }; 2302 return offset; 2303}; 2304 2305// -------------------------- resize -------------------------- // 2306 2307// enable event handlers for listeners 2308// i.e. resize -> onresize 2309Outlayer.prototype.handleEvent = function( event ) { 2310 var method = 'on' + event.type; 2311 if ( this[ method ] ) { 2312 this[ method ]( event ); 2313 } 2314}; 2315 2316/** 2317 * Bind layout to window resizing 2318 */ 2319Outlayer.prototype.bindResize = function() { 2320 // bind just one listener 2321 if ( this.isResizeBound ) { 2322 return; 2323 } 2324 eventie.bind( window, 'resize', this ); 2325 this.isResizeBound = true; 2326}; 2327 2328/** 2329 * Unbind layout to window resizing 2330 */ 2331Outlayer.prototype.unbindResize = function() { 2332 if ( this.isResizeBound ) { 2333 eventie.unbind( window, 'resize', this ); 2334 } 2335 this.isResizeBound = false; 2336}; 2337 2338// original debounce by John Hann 2339// http://unscriptable.com/index.php/2009/03/20/debouncing-javascript-methods/ 2340 2341// this fires every resize 2342Outlayer.prototype.onresize = function() { 2343 if ( this.resizeTimeout ) { 2344 clearTimeout( this.resizeTimeout ); 2345 } 2346 2347 var _this = this; 2348 function delayed() { 2349 _this.resize(); 2350 delete _this.resizeTimeout; 2351 } 2352 2353 this.resizeTimeout = setTimeout( delayed, 100 ); 2354}; 2355 2356// debounced, layout on resize 2357Outlayer.prototype.resize = function() { 2358 // don't trigger if size did not change 2359 // or if resize was unbound. See #9 2360 2361 if ( !this.isResizeBound || !this.needsResizeLayout() ) { 2362 return; 2363 } 2364 2365 this.layout(); 2366}; 2367 2368/** 2369 * check if layout is needed post layout 2370 * @returns Boolean 2371 */ 2372Outlayer.prototype.needsResizeLayout = function() { 2373 var size = getSize( this.element ); 2374 // check that this.size and size are there 2375 // IE8 triggers resize on body size change, so they might not be 2376 var hasSizes = this.size && size; 2377 return hasSizes && size.innerWidth !== this.size.innerWidth; 2378}; 2379 2380// -------------------------- methods -------------------------- // 2381 2382/** 2383 * add items to Outlayer instance 2384 * @param {Array or NodeList or Element} elems 2385 * @returns {Array} items - Outlayer.Items 2386**/ 2387Outlayer.prototype.addItems = function( elems ) { 2388 var items = this._itemize( elems ); 2389 // add items to collection 2390 if ( items.length ) { 2391 this.items = this.items.concat( items ); 2392 } 2393 return items; 2394}; 2395 2396/** 2397 * Layout newly-appended item elements 2398 * @param {Array or NodeList or Element} elems 2399 */ 2400Outlayer.prototype.appended = function( elems ) { 2401 var items = this.addItems( elems ); 2402 if ( !items.length ) { 2403 return; 2404 } 2405 // layout and reveal just the new items 2406 this.layoutItems( items, true ); 2407 this.reveal( items ); 2408}; 2409 2410/** 2411 * Layout prepended elements 2412 * @param {Array or NodeList or Element} elems 2413 */ 2414Outlayer.prototype.prepended = function( elems ) { 2415 var items = this._itemize( elems ); 2416 if ( !items.length ) { 2417 return; 2418 } 2419 // add items to beginning of collection 2420 var previousItems = this.items.slice(0); 2421 this.items = items.concat( previousItems ); 2422 // start new layout 2423 this._resetLayout(); 2424 this._manageStamps(); 2425 // layout new stuff without transition 2426 this.layoutItems( items, true ); 2427 this.reveal( items ); 2428 // layout previous items 2429 this.layoutItems( previousItems ); 2430}; 2431 2432/** 2433 * reveal a collection of items 2434 * @param {Array of Outlayer.Items} items 2435 */ 2436Outlayer.prototype.reveal = function( items ) { 2437 var len = items && items.length; 2438 if ( !len ) { 2439 return; 2440 } 2441 for ( var i=0; i < len; i++ ) { 2442 var item = items[i]; 2443 item.reveal(); 2444 } 2445}; 2446 2447/** 2448 * hide a collection of items 2449 * @param {Array of Outlayer.Items} items 2450 */ 2451Outlayer.prototype.hide = function( items ) { 2452 var len = items && items.length; 2453 if ( !len ) { 2454 return; 2455 } 2456 for ( var i=0; i < len; i++ ) { 2457 var item = items[i]; 2458 item.hide(); 2459 } 2460}; 2461 2462/** 2463 * get Outlayer.Item, given an Element 2464 * @param {Element} elem 2465 * @param {Function} callback 2466 * @returns {Outlayer.Item} item 2467 */ 2468Outlayer.prototype.getItem = function( elem ) { 2469 // loop through items to get the one that matches 2470 for ( var i=0, len = this.items.length; i < len; i++ ) { 2471 var item = this.items[i]; 2472 if ( item.element === elem ) { 2473 // return item 2474 return item; 2475 } 2476 } 2477}; 2478 2479/** 2480 * get collection of Outlayer.Items, given Elements 2481 * @param {Array} elems 2482 * @returns {Array} items - Outlayer.Items 2483 */ 2484Outlayer.prototype.getItems = function( elems ) { 2485 if ( !elems || !elems.length ) { 2486 return; 2487 } 2488 var items = []; 2489 for ( var i=0, len = elems.length; i < len; i++ ) { 2490 var elem = elems[i]; 2491 var item = this.getItem( elem ); 2492 if ( item ) { 2493 items.push( item ); 2494 } 2495 } 2496 2497 return items; 2498}; 2499 2500/**
2501 * remove element(s) from instance and DOM 2502 * @param {Array or NodeList or Element} elems 2503 */ 2504Outlayer.prototype.remove = function( elems ) { 2505 elems = makeArray( elems ); 2506 2507 var removeItems = this.getItems( elems ); 2508 // bail if no items to remove 2509 if ( !removeItems || !removeItems.length ) { 2510 return; 2511 } 2512 2513 this._itemsOn( removeItems, 'remove', function() { 2514 this.emitEvent( 'removeComplete', [ this, removeItems ] ); 2515 }); 2516 2517 for ( var i=0, len = removeItems.length; i < len; i++ ) { 2518 var item = removeItems[i]; 2519 item.remove(); 2520 // remove item from collection 2521 removeFrom( item, this.items ); 2522 } 2523}; 2524 2525// ----- destroy ----- // 2526 2527// remove and disable Outlayer instance 2528Outlayer.prototype.destroy = function() { 2529 // clean up dynamic styles 2530 var style = this.element.style; 2531 style.height = ''; 2532 style.position = ''; 2533 style.width = ''; 2534 // destroy items 2535 for ( var i=0, len = this.items.length; i < len; i++ ) { 2536 var item = this.items[i]; 2537 item.destroy(); 2538 } 2539 2540 this.unbindResize(); 2541 2542 delete this.element.outlayerGUID; 2543 // remove data for jQuery 2544 if ( jQuery ) { 2545 jQuery.removeData( this.element, this.constructor.namespace ); 2546 } 2547 2548}; 2549 2550// -------------------------- data -------------------------- // 2551 2552/** 2553 * get Outlayer instance from element 2554 * @param {Element} elem 2555 * @returns {Outlayer} 2556 */ 2557Outlayer.data = function( elem ) { 2558 var id = elem && elem.outlayerGUID; 2559 return id && instances[ id ]; 2560}; 2561 2562 2563// -------------------------- create Outlayer class -------------------------- // 2564 2565/** 2566 * create a layout class 2567 * @param {String} namespace 2568 */ 2569Outlayer.create = function( namespace, options ) { 2570 // sub-class Outlayer 2571 function Layout() { 2572 Outlayer.apply( this, arguments ); 2573 } 2574 // inherit Outlayer prototype, use Object.create if there 2575 if ( Object.create ) { 2576 Layout.prototype = Object.create( Outlayer.prototype ); 2577 } else { 2578 extend( Layout.prototype, Outlayer.prototype ); 2579 } 2580 // set contructor, used for namespace and Item 2581 Layout.prototype.constructor = Layout; 2582 2583 Layout.defaults = extend( {}, Outlayer.defaults ); 2584 // apply new options 2585 extend( Layout.defaults, options ); 2586 // keep prototype.settings for backwards compatibility (Packery v1.2.0) 2587 Layout.prototype.settings = {}; 2588 2589 Layout.namespace = namespace; 2590 2591 Layout.data = Outlayer.data; 2592 2593 // sub-class Item 2594 Layout.Item = function LayoutItem() { 2595 Item.apply( this, arguments ); 2596 }; 2597 2598 Layout.Item.prototype = new Item(); 2599 2600 // -------------------------- declarative -------------------------- // 2601 2602 /** 2603 * allow user to initialize Outlayer via .js-namespace class 2604 * options are parsed from data-namespace-option attribute 2605 */ 2606 docReady( function() { 2607 var dashedNamespace = toDashed( namespace ); 2608 var elems = document.querySelectorAll( '.js-' + dashedNamespace ); 2609 var dataAttr = 'data-' + dashedNamespace + '-options'; 2610 2611 for ( var i=0, len = elems.length; i < len; i++ ) { 2612 var elem = elems[i]; 2613 var attr = elem.getAttribute( dataAttr ); 2614 var options; 2615 try { 2616 options = attr && JSON.parse( attr ); 2617 } catch ( error ) { 2618 // log error, do not initialize 2619 if ( console ) { 2620 console.error( 'Error parsing ' + dataAttr + ' on ' + 2621 elem.nodeName.toLowerCase() + ( elem.id ? '#' + elem.id : '' ) + ': ' + 2622 error ); 2623 } 2624 continue; 2625 } 2626 // initialize 2627 var instance = new Layout( elem, options ); 2628 // make available via $().data('layoutname') 2629 if ( jQuery ) { 2630 jQuery.data( elem, namespace, instance ); 2631 } 2632 } 2633 }); 2634 2635 // -------------------------- jQuery bridge -------------------------- // 2636 2637 // make into jQuery plugin 2638 if ( jQuery && jQuery.bridget ) { 2639 jQuery.bridget( namespace, Layout ); 2640 } 2641 2642 return Layout; 2643}; 2644 2645// ----- fin ----- // 2646 2647// back in global 2648Outlayer.Item = Item; 2649 2650return Outlayer; 2651 2652} 2653 2654// -------------------------- transport -------------------------- // 2655 2656if ( typeof define === 'function' && define.amd ) { 2657 // AMD 2658 define( 'outlayer/outlayer',[ 2659 'eventie/eventie', 2660 'doc-ready/doc-ready', 2661 'eventEmitter/EventEmitter', 2662 'get-size/get-size', 2663 'matches-selector/matches-selector', 2664 './item' 2665 ], 2666 outlayerDefinition ); 2667} else { 2668 // browser global 2669 window.Outlayer = outlayerDefinition( 2670 window.eventie, 2671 window.docReady, 2672 window.EventEmitter, 2673 window.getSize, 2674 window.matchesSelector, 2675 window.Outlayer.Item 2676 ); 2677} 2678 2679})( window ); 2680 2681/*! 2682 * Masonry v3.1.5 2683 * Cascading grid layout library 2684 * http://masonry.desandro.com 2685 * MIT License 2686 * by David DeSandro 2687 */ 2688 2689( function( window ) { 2690 2691 2692 2693// -------------------------- helpers -------------------------- // 2694 2695var indexOf = Array.prototype.indexOf ? 2696 function( items, value ) { 2697 return items.indexOf( value ); 2698 } : 2699 function ( items, value ) { 2700 for ( var i=0, len = items.length; i < len; i++ ) { 2701 var item = items[i]; 2702 if ( item === value ) { 2703 return i; 2704 } 2705 } 2706 return -1; 2707 }; 2708 2709// -------------------------- masonryDefinition -------------------------- // 2710 2711// used for AMD definition and requires 2712function masonryDefinition( Outlayer, getSize ) { 2713 // create an Outlayer layout class 2714 var Masonry = Outlayer.create('masonry'); 2715 2716 Masonry.prototype._resetLayout = function() {
2717 this.getSize(); 2718 this._getMeasurement( 'columnWidth', 'outerWidth' ); 2719 this._getMeasurement( 'gutter', 'outerWidth' ); 2720 this.measureColumns(); 2721 2722 // reset column Y 2723 var i = this.cols; 2724 this.colYs = []; 2725 while (i--) { 2726 this.colYs.push( 0 ); 2727 } 2728 2729 this.maxY = 0; 2730 }; 2731 2732 Masonry.prototype.measureColumns = function() { 2733 this.getContainerWidth(); 2734 // if columnWidth is 0, default to outerWidth of first item 2735 if ( !this.columnWidth ) { 2736 var firstItem = this.items[0]; 2737 var firstItemElem = firstItem && firstItem.element; 2738 // columnWidth fall back to item of first element 2739 this.columnWidth = firstItemElem && getSize( firstItemElem ).outerWidth || 2740 // if first elem has no width, default to size of container 2741 this.containerWidth; 2742 } 2743 2744 this.columnWidth += this.gutter; 2745 2746 this.cols = Math.floor( ( this.containerWidth + this.gutter ) / this.columnWidth ); 2747 this.cols = Math.max( this.cols, 1 ); 2748 }; 2749 2750 Masonry.prototype.getContainerWidth = function() { 2751 // container is parent if fit width 2752 var container = this.options.isFitWidth ? this.element.parentNode : this.element; 2753 // check that this.size and size are there 2754 // IE8 triggers resize on body size change, so they might not be 2755 var size = getSize( container ); 2756 this.containerWidth = size && size.innerWidth; 2757 }; 2758 2759 Masonry.prototype._getItemLayoutPosition = function( item ) { 2760 item.getSize(); 2761 // how many columns does this brick span 2762 var remainder = item.size.outerWidth % this.columnWidth; 2763 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil'; 2764 // round if off by 1 pixel, otherwise use ceil 2765 var colSpan = Math[ mathMethod ]( item.size.outerWidth / this.columnWidth ); 2766 colSpan = Math.min( colSpan, this.cols ); 2767 2768 var colGroup = this._getColGroup( colSpan ); 2769 // get the minimum Y value from the columns 2770 var minimumY = Math.min.apply( Math, colGroup ); 2771 var shortColIndex = indexOf( colGroup, minimumY ); 2772 2773 // position the brick 2774 var position = { 2775 x: this.columnWidth * shortColIndex, 2776 y: minimumY 2777 }; 2778 2779 // apply setHeight to necessary columns 2780 var setHeight = minimumY + item.size.outerHeight; 2781 var setSpan = this.cols + 1 - colGroup.length; 2782 for ( var i = 0; i < setSpan; i++ ) { 2783 this.colYs[ shortColIndex + i ] = setHeight; 2784 } 2785 2786 return position; 2787 }; 2788 2789 /** 2790 * @param {Number} colSpan - number of columns the element spans 2791 * @returns {Array} colGroup 2792 */ 2793 Masonry.prototype._getColGroup = function( colSpan ) { 2794 if ( colSpan < 2 ) { 2795 // if brick spans only one column, use all the column Ys 2796 return this.colYs; 2797 } 2798 2799 var colGroup = []; 2800 // how many different places could this brick fit horizontally 2801 var groupCount = this.cols + 1 - colSpan; 2802 // for each group potential horizontal position 2803 for ( var i = 0; i < groupCount; i++ ) { 2804 // make an array of colY values for that one group 2805 var groupColYs = this.colYs.slice( i, i + colSpan ); 2806 // and get the max value of the array 2807 colGroup[i] = Math.max.apply( Math, groupColYs ); 2808 } 2809 return colGroup; 2810 }; 2811 2812 Masonry.prototype._manageStamp = function( stamp ) { 2813 var stampSize = getSize( stamp ); 2814 var offset = this._getElementOffset( stamp ); 2815 // get the columns that this stamp affects 2816 var firstX = this.options.isOriginLeft ? offset.left : offset.right; 2817 var lastX = firstX + stampSize.outerWidth; 2818 var firstCol = Math.floor( firstX / this.columnWidth ); 2819 firstCol = Math.max( 0, firstCol ); 2820 var lastCol = Math.floor( lastX / this.columnWidth ); 2821 // lastCol should not go over if multiple of columnWidth #425 2822 lastCol -= lastX % this.columnWidth ? 0 : 1; 2823 lastCol = Math.min( this.cols - 1, lastCol ); 2824 // set colYs to bottom of the stamp 2825 var stampMaxY = ( this.options.isOriginTop ? offset.top : offset.bottom ) + 2826 stampSize.outerHeight; 2827 for ( var i = firstCol; i <= lastCol; i++ ) { 2828 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] ); 2829 } 2830 }; 2831 2832 Masonry.prototype._getContainerSize = function() { 2833 this.maxY = Math.max.apply( Math, this.colYs ); 2834 var size = { 2835 height: this.maxY 2836 }; 2837 2838 if ( this.options.isFitWidth ) { 2839 size.width = this._getContainerFitWidth(); 2840 } 2841 2842 return size; 2843 }; 2844 2845 Masonry.prototype._getContainerFitWidth = function() { 2846 var unusedCols = 0; 2847 // count unused columns 2848 var i = this.cols; 2849 while ( --i ) { 2850 if ( this.colYs[i] !== 0 ) { 2851 break; 2852 } 2853 unusedCols++; 2854 } 2855 // fit container to columns that have been used 2856 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter; 2857 }; 2858 2859 Masonry.prototype.needsResizeLayout = function() { 2860 var previousWidth = this.containerWidth; 2861 this.getContainerWidth(); 2862 return previousWidth !== this.containerWidth; 2863 }; 2864 2865 return Masonry; 2866} 2867 2868// -------------------------- transport -------------------------- // 2869 2870if ( typeof define === 'function' && define.amd ) { 2871 // AMD 2872 define( [ 2873 'outlayer/outlayer', 2874 'get-size/get-size' 2875 ], 2876 masonryDefinition ); 2877} else { 2878 // browser global 2879 window.Masonry = masonryDefinition( 2880 window.Outlayer, 2881 window.getSize 2882 ); 2883} 2884 2885})( window ); 2886
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.