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