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