PluginProbe ʕ •ᴥ•ʔ
Responsive Lightbox & Gallery / 2.5.3
Responsive Lightbox & Gallery v2.5.3
2.7.8 trunk 1.0.0 1.0.1 1.0.1.1 1.0.2 1.0.3 1.0.4 1.1.0 1.1.1 1.1.2 1.2.0 1.2.1 1.2.2 1.2.3 1.3.0 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.3.6 1.4.0 1.4.0.1 1.4.1 1.4.11 1.4.12 1.4.13 1.4.14 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.4.8 1.4.9 1.5.0 1.5.1 1.5.2 1.5.3 1.5.4 1.5.5 1.5.6 1.5.7 1.6.0 1.6.1 1.6.10 1.6.11 1.6.12 1.6.2 1.6.3 1.6.4 1.6.5 1.6.6 1.6.7 1.6.8 1.6.9 1.7.0 1.7.1 1.7.2 2.0 2.0.1 2.0.2 2.0.3 2.0.4 2.0.5 2.1 2.2.0 2.2.1 2.2.2 2.2.3 2.2.3.1 2.3.0 2.3.1 2.3.2 2.3.3 2.3.4 2.3.5 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.4.5 2.4.6 2.4.7 2.4.8 2.4.9 2.5.0 2.5.1 2.5.2 2.5.3 2.5.4 2.5.5 2.6.0 2.6.1 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.7.5 2.7.6 2.7.7
responsive-lightbox / assets / masonry / masonry.pkgd.js
responsive-lightbox / assets / masonry Last commit date
LICENSE 4 years ago README.md 4 years ago masonry.pkgd.js 6 years ago masonry.pkgd.min.js 6 years ago
masonry.pkgd.js
2505 lines
1 /*!
2 * Masonry PACKAGED v4.2.2
3 * Cascading grid layout library
4 * https://masonry.desandro.com
5 * MIT License
6 * by David DeSandro
7 */
8
9 /**
10 * Bridget makes jQuery widgets
11 * v2.0.1
12 * MIT license
13 */
14
15 /* jshint browser: true, strict: true, undef: true, unused: true */
16
17 ( function( window, factory ) {
18 // universal module definition
19 /*jshint strict: false */ /* globals define, module, require */
20 if ( typeof define == 'function' && define.amd ) {
21 // AMD
22 define( 'jquery-bridget/jquery-bridget',[ 'jquery' ], function( jQuery ) {
23 return factory( window, jQuery );
24 });
25 } else if ( typeof module == 'object' && module.exports ) {
26 // CommonJS
27 module.exports = factory(
28 window,
29 require('jquery')
30 );
31 } else {
32 // browser global
33 window.jQueryBridget = factory(
34 window,
35 window.jQuery
36 );
37 }
38
39 }( window, function factory( window, jQuery ) {
40 'use strict';
41
42 // ----- utils ----- //
43
44 var arraySlice = Array.prototype.slice;
45
46 // helper function for logging errors
47 // $.error breaks jQuery chaining
48 var console = window.console;
49 var logError = typeof console == 'undefined' ? function() {} :
50 function( message ) {
51 console.error( message );
52 };
53
54 // ----- jQueryBridget ----- //
55
56 function jQueryBridget( namespace, PluginClass, $ ) {
57 $ = $ || jQuery || window.jQuery;
58 if ( !$ ) {
59 return;
60 }
61
62 // add option method -> $().plugin('option', {...})
63 if ( !PluginClass.prototype.option ) {
64 // option setter
65 PluginClass.prototype.option = function( opts ) {
66 // bail out if not an object
67 if ( !$.isPlainObject( opts ) ){
68 return;
69 }
70 this.options = $.extend( true, this.options, opts );
71 };
72 }
73
74 // make jQuery plugin
75 $.fn[ namespace ] = function( arg0 /*, arg1 */ ) {
76 if ( typeof arg0 == 'string' ) {
77 // method call $().plugin( 'methodName', { options } )
78 // shift arguments by 1
79 var args = arraySlice.call( arguments, 1 );
80 return methodCall( this, arg0, args );
81 }
82 // just $().plugin({ options })
83 plainCall( this, arg0 );
84 return this;
85 };
86
87 // $().plugin('methodName')
88 function methodCall( $elems, methodName, args ) {
89 var returnValue;
90 var pluginMethodStr = '$().' + namespace + '("' + methodName + '")';
91
92 $elems.each( function( i, elem ) {
93 // get instance
94 var instance = $.data( elem, namespace );
95 if ( !instance ) {
96 logError( namespace + ' not initialized. Cannot call methods, i.e. ' +
97 pluginMethodStr );
98 return;
99 }
100
101 var method = instance[ methodName ];
102 if ( !method || methodName.charAt(0) == '_' ) {
103 logError( pluginMethodStr + ' is not a valid method' );
104 return;
105 }
106
107 // apply method, get return value
108 var value = method.apply( instance, args );
109 // set return value if value is returned, use only first value
110 returnValue = returnValue === undefined ? value : returnValue;
111 });
112
113 return returnValue !== undefined ? returnValue : $elems;
114 }
115
116 function plainCall( $elems, options ) {
117 $elems.each( function( i, elem ) {
118 var instance = $.data( elem, namespace );
119 if ( instance ) {
120 // set options & init
121 instance.option( options );
122 instance._init();
123 } else {
124 // initialize new instance
125 instance = new PluginClass( elem, options );
126 $.data( elem, namespace, instance );
127 }
128 });
129 }
130
131 updateJQuery( $ );
132
133 }
134
135 // ----- updateJQuery ----- //
136
137 // set $.bridget for v1 backwards compatibility
138 function updateJQuery( $ ) {
139 if ( !$ || ( $ && $.bridget ) ) {
140 return;
141 }
142 $.bridget = jQueryBridget;
143 }
144
145 updateJQuery( jQuery || window.jQuery );
146
147 // ----- ----- //
148
149 return jQueryBridget;
150
151 }));
152
153 /**
154 * EvEmitter v1.1.0
155 * Lil' event emitter
156 * MIT License
157 */
158
159 /* jshint unused: true, undef: true, strict: true */
160
161 ( function( global, factory ) {
162 // universal module definition
163 /* jshint strict: false */ /* globals define, module, window */
164 if ( typeof define == 'function' && define.amd ) {
165 // AMD - RequireJS
166 define( 'ev-emitter/ev-emitter',factory );
167 } else if ( typeof module == 'object' && module.exports ) {
168 // CommonJS - Browserify, Webpack
169 module.exports = factory();
170 } else {
171 // Browser globals
172 global.EvEmitter = factory();
173 }
174
175 }( typeof window != 'undefined' ? window : this, function() {
176
177
178
179 function EvEmitter() {}
180
181 var proto = EvEmitter.prototype;
182
183 proto.on = function( eventName, listener ) {
184 if ( !eventName || !listener ) {
185 return;
186 }
187 // set events hash
188 var events = this._events = this._events || {};
189 // set listeners array
190 var listeners = events[ eventName ] = events[ eventName ] || [];
191 // only add once
192 if ( listeners.indexOf( listener ) == -1 ) {
193 listeners.push( listener );
194 }
195
196 return this;
197 };
198
199 proto.once = function( eventName, listener ) {
200 if ( !eventName || !listener ) {
201 return;
202 }
203 // add event
204 this.on( eventName, listener );
205 // set once flag
206 // set onceEvents hash
207 var onceEvents = this._onceEvents = this._onceEvents || {};
208 // set onceListeners object
209 var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {};
210 // set flag
211 onceListeners[ listener ] = true;
212
213 return this;
214 };
215
216 proto.off = function( eventName, listener ) {
217 var listeners = this._events && this._events[ eventName ];
218 if ( !listeners || !listeners.length ) {
219 return;
220 }
221 var index = listeners.indexOf( listener );
222 if ( index != -1 ) {
223 listeners.splice( index, 1 );
224 }
225
226 return this;
227 };
228
229 proto.emitEvent = function( eventName, args ) {
230 var listeners = this._events && this._events[ eventName ];
231 if ( !listeners || !listeners.length ) {
232 return;
233 }
234 // copy over to avoid interference if .off() in listener
235 listeners = listeners.slice(0);
236 args = args || [];
237 // once stuff
238 var onceListeners = this._onceEvents && this._onceEvents[ eventName ];
239
240 for ( var i=0; i < listeners.length; i++ ) {
241 var listener = listeners[i]
242 var isOnce = onceListeners && onceListeners[ listener ];
243 if ( isOnce ) {
244 // remove listener
245 // remove before trigger to prevent recursion
246 this.off( eventName, listener );
247 // unset once flag
248 delete onceListeners[ listener ];
249 }
250 // trigger listener
251 listener.apply( this, args );
252 }
253
254 return this;
255 };
256
257 proto.allOff = function() {
258 delete this._events;
259 delete this._onceEvents;
260 };
261
262 return EvEmitter;
263
264 }));
265
266 /*!
267 * getSize v2.0.3
268 * measure size of elements
269 * MIT license
270 */
271
272 /* jshint browser: true, strict: true, undef: true, unused: true */
273 /* globals console: false */
274
275 ( function( window, factory ) {
276 /* jshint strict: false */ /* globals define, module */
277 if ( typeof define == 'function' && define.amd ) {
278 // AMD
279 define( 'get-size/get-size',factory );
280 } else if ( typeof module == 'object' && module.exports ) {
281 // CommonJS
282 module.exports = factory();
283 } else {
284 // browser global
285 window.getSize = factory();
286 }
287
288 })( window, function factory() {
289 'use strict';
290
291 // -------------------------- helpers -------------------------- //
292
293 // get a number from a string, not a percentage
294 function getStyleSize( value ) {
295 var num = parseFloat( value );
296 // not a percent like '100%', and a number
297 var isValid = value.indexOf('%') == -1 && !isNaN( num );
298 return isValid && num;
299 }
300
301 function noop() {}
302
303 var logError = typeof console == 'undefined' ? noop :
304 function( message ) {
305 console.error( message );
306 };
307
308 // -------------------------- measurements -------------------------- //
309
310 var measurements = [
311 'paddingLeft',
312 'paddingRight',
313 'paddingTop',
314 'paddingBottom',
315 'marginLeft',
316 'marginRight',
317 'marginTop',
318 'marginBottom',
319 'borderLeftWidth',
320 'borderRightWidth',
321 'borderTopWidth',
322 'borderBottomWidth'
323 ];
324
325 var measurementsLength = measurements.length;
326
327 function getZeroSize() {
328 var size = {
329 width: 0,
330 height: 0,
331 innerWidth: 0,
332 innerHeight: 0,
333 outerWidth: 0,
334 outerHeight: 0
335 };
336 for ( var i=0; i < measurementsLength; i++ ) {
337 var measurement = measurements[i];
338 size[ measurement ] = 0;
339 }
340 return size;
341 }
342
343 // -------------------------- getStyle -------------------------- //
344
345 /**
346 * getStyle, get style of element, check for Firefox bug
347 * https://bugzilla.mozilla.org/show_bug.cgi?id=548397
348 */
349 function getStyle( elem ) {
350 var style = getComputedStyle( elem );
351 if ( !style ) {
352 logError( 'Style returned ' + style +
353 '. Are you running this code in a hidden iframe on Firefox? ' +
354 'See https://bit.ly/getsizebug1' );
355 }
356 return style;
357 }
358
359 // -------------------------- setup -------------------------- //
360
361 var isSetup = false;
362
363 var isBoxSizeOuter;
364
365 /**
366 * setup
367 * check isBoxSizerOuter
368 * do on first getSize() rather than on page load for Firefox bug
369 */
370 function setup() {
371 // setup once
372 if ( isSetup ) {
373 return;
374 }
375 isSetup = true;
376
377 // -------------------------- box sizing -------------------------- //
378
379 /**
380 * Chrome & Safari measure the outer-width on style.width on border-box elems
381 * IE11 & Firefox<29 measures the inner-width
382 */
383 var div = document.createElement('div');
384 div.style.width = '200px';
385 div.style.padding = '1px 2px 3px 4px';
386 div.style.borderStyle = 'solid';
387 div.style.borderWidth = '1px 2px 3px 4px';
388 div.style.boxSizing = 'border-box';
389
390 var body = document.body || document.documentElement;
391 body.appendChild( div );
392 var style = getStyle( div );
393 // round value for browser zoom. desandro/masonry#928
394 isBoxSizeOuter = Math.round( getStyleSize( style.width ) ) == 200;
395 getSize.isBoxSizeOuter = isBoxSizeOuter;
396
397 body.removeChild( div );
398 }
399
400 // -------------------------- getSize -------------------------- //
401
402 function getSize( elem ) {
403 setup();
404
405 // use querySeletor if elem is string
406 if ( typeof elem == 'string' ) {
407 elem = document.querySelector( elem );
408 }
409
410 // do not proceed on non-objects
411 if ( !elem || typeof elem != 'object' || !elem.nodeType ) {
412 return;
413 }
414
415 var style = getStyle( elem );
416
417 // if hidden, everything is 0
418 if ( style.display == 'none' ) {
419 return getZeroSize();
420 }
421
422 var size = {};
423 size.width = elem.offsetWidth;
424 size.height = elem.offsetHeight;
425
426 var isBorderBox = size.isBorderBox = style.boxSizing == 'border-box';
427
428 // get all measurements
429 for ( var i=0; i < measurementsLength; i++ ) {
430 var measurement = measurements[i];
431 var value = style[ measurement ];
432 var num = parseFloat( value );
433 // any 'auto', 'medium' value will be 0
434 size[ measurement ] = !isNaN( num ) ? num : 0;
435 }
436
437 var paddingWidth = size.paddingLeft + size.paddingRight;
438 var paddingHeight = size.paddingTop + size.paddingBottom;
439 var marginWidth = size.marginLeft + size.marginRight;
440 var marginHeight = size.marginTop + size.marginBottom;
441 var borderWidth = size.borderLeftWidth + size.borderRightWidth;
442 var borderHeight = size.borderTopWidth + size.borderBottomWidth;
443
444 var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter;
445
446 // overwrite width and height if we can get it from style
447 var styleWidth = getStyleSize( style.width );
448 if ( styleWidth !== false ) {
449 size.width = styleWidth +
450 // add padding and border unless it's already including it
451 ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth );
452 }
453
454 var styleHeight = getStyleSize( style.height );
455 if ( styleHeight !== false ) {
456 size.height = styleHeight +
457 // add padding and border unless it's already including it
458 ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight );
459 }
460
461 size.innerWidth = size.width - ( paddingWidth + borderWidth );
462 size.innerHeight = size.height - ( paddingHeight + borderHeight );
463
464 size.outerWidth = size.width + marginWidth;
465 size.outerHeight = size.height + marginHeight;
466
467 return size;
468 }
469
470 return getSize;
471
472 });
473
474 /**
475 * matchesSelector v2.0.2
476 * matchesSelector( element, '.selector' )
477 * MIT license
478 */
479
480 /*jshint browser: true, strict: true, undef: true, unused: true */
481
482 ( function( window, factory ) {
483 /*global define: false, module: false */
484 'use strict';
485 // universal module definition
486 if ( typeof define == 'function' && define.amd ) {
487 // AMD
488 define( 'desandro-matches-selector/matches-selector',factory );
489 } else if ( typeof module == 'object' && module.exports ) {
490 // CommonJS
491 module.exports = factory();
492 } else {
493 // browser global
494 window.matchesSelector = factory();
495 }
496
497 }( window, function factory() {
498 'use strict';
499
500 var matchesMethod = ( function() {
501 var ElemProto = window.Element.prototype;
502 // check for the standard method name first
503 if ( ElemProto.matches ) {
504 return 'matches';
505 }
506 // check un-prefixed
507 if ( ElemProto.matchesSelector ) {
508 return 'matchesSelector';
509 }
510 // check vendor prefixes
511 var prefixes = [ 'webkit', 'moz', 'ms', 'o' ];
512
513 for ( var i=0; i < prefixes.length; i++ ) {
514 var prefix = prefixes[i];
515 var method = prefix + 'MatchesSelector';
516 if ( ElemProto[ method ] ) {
517 return method;
518 }
519 }
520 })();
521
522 return function matchesSelector( elem, selector ) {
523 return elem[ matchesMethod ]( selector );
524 };
525
526 }));
527
528 /**
529 * Fizzy UI utils v2.0.7
530 * MIT license
531 */
532
533 /*jshint browser: true, undef: true, unused: true, strict: true */
534
535 ( function( window, factory ) {
536 // universal module definition
537 /*jshint strict: false */ /*globals define, module, require */
538
539 if ( typeof define == 'function' && define.amd ) {
540 // AMD
541 define( 'fizzy-ui-utils/utils',[
542 'desandro-matches-selector/matches-selector'
543 ], function( matchesSelector ) {
544 return factory( window, matchesSelector );
545 });
546 } else if ( typeof module == 'object' && module.exports ) {
547 // CommonJS
548 module.exports = factory(
549 window,
550 require('desandro-matches-selector')
551 );
552 } else {
553 // browser global
554 window.fizzyUIUtils = factory(
555 window,
556 window.matchesSelector
557 );
558 }
559
560 }( window, function factory( window, matchesSelector ) {
561
562
563
564 var utils = {};
565
566 // ----- extend ----- //
567
568 // extends objects
569 utils.extend = function( a, b ) {
570 for ( var prop in b ) {
571 a[ prop ] = b[ prop ];
572 }
573 return a;
574 };
575
576 // ----- modulo ----- //
577
578 utils.modulo = function( num, div ) {
579 return ( ( num % div ) + div ) % div;
580 };
581
582 // ----- makeArray ----- //
583
584 var arraySlice = Array.prototype.slice;
585
586 // turn element or nodeList into an array
587 utils.makeArray = function( obj ) {
588 if ( Array.isArray( obj ) ) {
589 // use object if already an array
590 return obj;
591 }
592 // return empty array if undefined or null. #6
593 if ( obj === null || obj === undefined ) {
594 return [];
595 }
596
597 var isArrayLike = typeof obj == 'object' && typeof obj.length == 'number';
598 if ( isArrayLike ) {
599 // convert nodeList to array
600 return arraySlice.call( obj );
601 }
602
603 // array of single index
604 return [ obj ];
605 };
606
607 // ----- removeFrom ----- //
608
609 utils.removeFrom = function( ary, obj ) {
610 var index = ary.indexOf( obj );
611 if ( index != -1 ) {
612 ary.splice( index, 1 );
613 }
614 };
615
616 // ----- getParent ----- //
617
618 utils.getParent = function( elem, selector ) {
619 while ( elem.parentNode && elem != document.body ) {
620 elem = elem.parentNode;
621 if ( matchesSelector( elem, selector ) ) {
622 return elem;
623 }
624 }
625 };
626
627 // ----- getQueryElement ----- //
628
629 // use element as selector string
630 utils.getQueryElement = function( elem ) {
631 if ( typeof elem == 'string' ) {
632 return document.querySelector( elem );
633 }
634 return elem;
635 };
636
637 // ----- handleEvent ----- //
638
639 // enable .ontype to trigger from .addEventListener( elem, 'type' )
640 utils.handleEvent = function( event ) {
641 var method = 'on' + event.type;
642 if ( this[ method ] ) {
643 this[ method ]( event );
644 }
645 };
646
647 // ----- filterFindElements ----- //
648
649 utils.filterFindElements = function( elems, selector ) {
650 // make array of elems
651 elems = utils.makeArray( elems );
652 var ffElems = [];
653
654 elems.forEach( function( elem ) {
655 // check that elem is an actual element
656 if ( !( elem instanceof HTMLElement ) ) {
657 return;
658 }
659 // add elem if no selector
660 if ( !selector ) {
661 ffElems.push( elem );
662 return;
663 }
664 // filter & find items if we have a selector
665 // filter
666 if ( matchesSelector( elem, selector ) ) {
667 ffElems.push( elem );
668 }
669 // find children
670 var childElems = elem.querySelectorAll( selector );
671 // concat childElems to filterFound array
672 for ( var i=0; i < childElems.length; i++ ) {
673 ffElems.push( childElems[i] );
674 }
675 });
676
677 return ffElems;
678 };
679
680 // ----- debounceMethod ----- //
681
682 utils.debounceMethod = function( _class, methodName, threshold ) {
683 threshold = threshold || 100;
684 // original method
685 var method = _class.prototype[ methodName ];
686 var timeoutName = methodName + 'Timeout';
687
688 _class.prototype[ methodName ] = function() {
689 var timeout = this[ timeoutName ];
690 clearTimeout( timeout );
691
692 var args = arguments;
693 var _this = this;
694 this[ timeoutName ] = setTimeout( function() {
695 method.apply( _this, args );
696 delete _this[ timeoutName ];
697 }, threshold );
698 };
699 };
700
701 // ----- docReady ----- //
702
703 utils.docReady = function( callback ) {
704 var readyState = document.readyState;
705 if ( readyState == 'complete' || readyState == 'interactive' ) {
706 // do async to allow for other scripts to run. metafizzy/flickity#441
707 setTimeout( callback );
708 } else {
709 document.addEventListener( 'DOMContentLoaded', callback );
710 }
711 };
712
713 // ----- htmlInit ----- //
714
715 // http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
716 utils.toDashed = function( str ) {
717 return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
718 return $1 + '-' + $2;
719 }).toLowerCase();
720 };
721
722 var console = window.console;
723 /**
724 * allow user to initialize classes via [data-namespace] or .js-namespace class
725 * htmlInit( Widget, 'widgetName' )
726 * options are parsed from data-namespace-options
727 */
728 utils.htmlInit = function( WidgetClass, namespace ) {
729 utils.docReady( function() {
730 var dashedNamespace = utils.toDashed( namespace );
731 var dataAttr = 'data-' + dashedNamespace;
732 var dataAttrElems = document.querySelectorAll( '[' + dataAttr + ']' );
733 var jsDashElems = document.querySelectorAll( '.js-' + dashedNamespace );
734 var elems = utils.makeArray( dataAttrElems )
735 .concat( utils.makeArray( jsDashElems ) );
736 var dataOptionsAttr = dataAttr + '-options';
737 var jQuery = window.jQuery;
738
739 elems.forEach( function( elem ) {
740 var attr = elem.getAttribute( dataAttr ) ||
741 elem.getAttribute( dataOptionsAttr );
742 var options;
743 try {
744 options = attr && JSON.parse( attr );
745 } catch ( error ) {
746 // log error, do not initialize
747 if ( console ) {
748 console.error( 'Error parsing ' + dataAttr + ' on ' + elem.className +
749 ': ' + error );
750 }
751 return;
752 }
753 // initialize
754 var instance = new WidgetClass( elem, options );
755 // make available via $().data('namespace')
756 if ( jQuery ) {
757 jQuery.data( elem, namespace, instance );
758 }
759 });
760
761 });
762 };
763
764 // ----- ----- //
765
766 return utils;
767
768 }));
769
770 /**
771 * Outlayer Item
772 */
773
774 ( function( window, factory ) {
775 // universal module definition
776 /* jshint strict: false */ /* globals define, module, require */
777 if ( typeof define == 'function' && define.amd ) {
778 // AMD - RequireJS
779 define( 'outlayer/item',[
780 'ev-emitter/ev-emitter',
781 'get-size/get-size'
782 ],
783 factory
784 );
785 } else if ( typeof module == 'object' && module.exports ) {
786 // CommonJS - Browserify, Webpack
787 module.exports = factory(
788 require('ev-emitter'),
789 require('get-size')
790 );
791 } else {
792 // browser global
793 window.Outlayer = {};
794 window.Outlayer.Item = factory(
795 window.EvEmitter,
796 window.getSize
797 );
798 }
799
800 }( window, function factory( EvEmitter, getSize ) {
801 'use strict';
802
803 // ----- helpers ----- //
804
805 function isEmptyObj( obj ) {
806 for ( var prop in obj ) {
807 return false;
808 }
809 prop = null;
810 return true;
811 }
812
813 // -------------------------- CSS3 support -------------------------- //
814
815
816 var docElemStyle = document.documentElement.style;
817
818 var transitionProperty = typeof docElemStyle.transition == 'string' ?
819 'transition' : 'WebkitTransition';
820 var transformProperty = typeof docElemStyle.transform == 'string' ?
821 'transform' : 'WebkitTransform';
822
823 var transitionEndEvent = {
824 WebkitTransition: 'webkitTransitionEnd',
825 transition: 'transitionend'
826 }[ transitionProperty ];
827
828 // cache all vendor properties that could have vendor prefix
829 var vendorProperties = {
830 transform: transformProperty,
831 transition: transitionProperty,
832 transitionDuration: transitionProperty + 'Duration',
833 transitionProperty: transitionProperty + 'Property',
834 transitionDelay: transitionProperty + 'Delay'
835 };
836
837 // -------------------------- Item -------------------------- //
838
839 function Item( element, layout ) {
840 if ( !element ) {
841 return;
842 }
843
844 this.element = element;
845 // parent layout class, i.e. Masonry, Isotope, or Packery
846 this.layout = layout;
847 this.position = {
848 x: 0,
849 y: 0
850 };
851
852 this._create();
853 }
854
855 // inherit EvEmitter
856 var proto = Item.prototype = Object.create( EvEmitter.prototype );
857 proto.constructor = Item;
858
859 proto._create = function() {
860 // transition objects
861 this._transn = {
862 ingProperties: {},
863 clean: {},
864 onEnd: {}
865 };
866
867 this.css({
868 position: 'absolute'
869 });
870 };
871
872 // trigger specified handler for event type
873 proto.handleEvent = function( event ) {
874 var method = 'on' + event.type;
875 if ( this[ method ] ) {
876 this[ method ]( event );
877 }
878 };
879
880 proto.getSize = function() {
881 this.size = getSize( this.element );
882 };
883
884 /**
885 * apply CSS styles to element
886 * @param {Object} style
887 */
888 proto.css = function( style ) {
889 var elemStyle = this.element.style;
890
891 for ( var prop in style ) {
892 // use vendor property if available
893 var supportedProp = vendorProperties[ prop ] || prop;
894 elemStyle[ supportedProp ] = style[ prop ];
895 }
896 };
897
898 // measure position, and sets it
899 proto.getPosition = function() {
900 var style = getComputedStyle( this.element );
901 var isOriginLeft = this.layout._getOption('originLeft');
902 var isOriginTop = this.layout._getOption('originTop');
903 var xValue = style[ isOriginLeft ? 'left' : 'right' ];
904 var yValue = style[ isOriginTop ? 'top' : 'bottom' ];
905 var x = parseFloat( xValue );
906 var y = parseFloat( yValue );
907 // convert percent to pixels
908 var layoutSize = this.layout.size;
909 if ( xValue.indexOf('%') != -1 ) {
910 x = ( x / 100 ) * layoutSize.width;
911 }
912 if ( yValue.indexOf('%') != -1 ) {
913 y = ( y / 100 ) * layoutSize.height;
914 }
915 // clean up 'auto' or other non-integer values
916 x = isNaN( x ) ? 0 : x;
917 y = isNaN( y ) ? 0 : y;
918 // remove padding from measurement
919 x -= isOriginLeft ? layoutSize.paddingLeft : layoutSize.paddingRight;
920 y -= isOriginTop ? layoutSize.paddingTop : layoutSize.paddingBottom;
921
922 this.position.x = x;
923 this.position.y = y;
924 };
925
926 // set settled position, apply padding
927 proto.layoutPosition = function() {
928 var layoutSize = this.layout.size;
929 var style = {};
930 var isOriginLeft = this.layout._getOption('originLeft');
931 var isOriginTop = this.layout._getOption('originTop');
932
933 // x
934 var xPadding = isOriginLeft ? 'paddingLeft' : 'paddingRight';
935 var xProperty = isOriginLeft ? 'left' : 'right';
936 var xResetProperty = isOriginLeft ? 'right' : 'left';
937
938 var x = this.position.x + layoutSize[ xPadding ];
939 // set in percentage or pixels
940 style[ xProperty ] = this.getXValue( x );
941 // reset other property
942 style[ xResetProperty ] = '';
943
944 // y
945 var yPadding = isOriginTop ? 'paddingTop' : 'paddingBottom';
946 var yProperty = isOriginTop ? 'top' : 'bottom';
947 var yResetProperty = isOriginTop ? 'bottom' : 'top';
948
949 var y = this.position.y + layoutSize[ yPadding ];
950 // set in percentage or pixels
951 style[ yProperty ] = this.getYValue( y );
952 // reset other property
953 style[ yResetProperty ] = '';
954
955 this.css( style );
956 this.emitEvent( 'layout', [ this ] );
957 };
958
959 proto.getXValue = function( x ) {
960 var isHorizontal = this.layout._getOption('horizontal');
961 return this.layout.options.percentPosition && !isHorizontal ?
962 ( ( x / this.layout.size.width ) * 100 ) + '%' : x + 'px';
963 };
964
965 proto.getYValue = function( y ) {
966 var isHorizontal = this.layout._getOption('horizontal');
967 return this.layout.options.percentPosition && isHorizontal ?
968 ( ( y / this.layout.size.height ) * 100 ) + '%' : y + 'px';
969 };
970
971 proto._transitionTo = function( x, y ) {
972 this.getPosition();
973 // get current x & y from top/left
974 var curX = this.position.x;
975 var curY = this.position.y;
976
977 var didNotMove = x == this.position.x && y == this.position.y;
978
979 // save end position
980 this.setPosition( x, y );
981
982 // if did not move and not transitioning, just go to layout
983 if ( didNotMove && !this.isTransitioning ) {
984 this.layoutPosition();
985 return;
986 }
987
988 var transX = x - curX;
989 var transY = y - curY;
990 var transitionStyle = {};
991 transitionStyle.transform = this.getTranslate( transX, transY );
992
993 this.transition({
994 to: transitionStyle,
995 onTransitionEnd: {
996 transform: this.layoutPosition
997 },
998 isCleaning: true
999 });
1000 };
1001
1002 proto.getTranslate = function( x, y ) {
1003 // flip cooridinates if origin on right or bottom
1004 var isOriginLeft = this.layout._getOption('originLeft');
1005 var isOriginTop = this.layout._getOption('originTop');
1006 x = isOriginLeft ? x : -x;
1007 y = isOriginTop ? y : -y;
1008 return 'translate3d(' + x + 'px, ' + y + 'px, 0)';
1009 };
1010
1011 // non transition + transform support
1012 proto.goTo = function( x, y ) {
1013 this.setPosition( x, y );
1014 this.layoutPosition();
1015 };
1016
1017 proto.moveTo = proto._transitionTo;
1018
1019 proto.setPosition = function( x, y ) {
1020 this.position.x = parseFloat( x );
1021 this.position.y = parseFloat( y );
1022 };
1023
1024 // ----- transition ----- //
1025
1026 /**
1027 * @param {Object} style - CSS
1028 * @param {Function} onTransitionEnd
1029 */
1030
1031 // non transition, just trigger callback
1032 proto._nonTransition = function( args ) {
1033 this.css( args.to );
1034 if ( args.isCleaning ) {
1035 this._removeStyles( args.to );
1036 }
1037 for ( var prop in args.onTransitionEnd ) {
1038 args.onTransitionEnd[ prop ].call( this );
1039 }
1040 };
1041
1042 /**
1043 * proper transition
1044 * @param {Object} args - arguments
1045 * @param {Object} to - style to transition to
1046 * @param {Object} from - style to start transition from
1047 * @param {Boolean} isCleaning - removes transition styles after transition
1048 * @param {Function} onTransitionEnd - callback
1049 */
1050 proto.transition = function( args ) {
1051 // redirect to nonTransition if no transition duration
1052 if ( !parseFloat( this.layout.options.transitionDuration ) ) {
1053 this._nonTransition( args );
1054 return;
1055 }
1056
1057 var _transition = this._transn;
1058 // keep track of onTransitionEnd callback by css property
1059 for ( var prop in args.onTransitionEnd ) {
1060 _transition.onEnd[ prop ] = args.onTransitionEnd[ prop ];
1061 }
1062 // keep track of properties that are transitioning
1063 for ( prop in args.to ) {
1064 _transition.ingProperties[ prop ] = true;
1065 // keep track of properties to clean up when transition is done
1066 if ( args.isCleaning ) {
1067 _transition.clean[ prop ] = true;
1068 }
1069 }
1070
1071 // set from styles
1072 if ( args.from ) {
1073 this.css( args.from );
1074 // force redraw. http://blog.alexmaccaw.com/css-transitions
1075 var h = this.element.offsetHeight;
1076 // hack for JSHint to hush about unused var
1077 h = null;
1078 }
1079 // enable transition
1080 this.enableTransition( args.to );
1081 // set styles that are transitioning
1082 this.css( args.to );
1083
1084 this.isTransitioning = true;
1085
1086 };
1087
1088 // dash before all cap letters, including first for
1089 // WebkitTransform => -webkit-transform
1090 function toDashedAll( str ) {
1091 return str.replace( /([A-Z])/g, function( $1 ) {
1092 return '-' + $1.toLowerCase();
1093 });
1094 }
1095
1096 var transitionProps = 'opacity,' + toDashedAll( transformProperty );
1097
1098 proto.enableTransition = function(/* style */) {
1099 // HACK changing transitionProperty during a transition
1100 // will cause transition to jump
1101 if ( this.isTransitioning ) {
1102 return;
1103 }
1104
1105 // make `transition: foo, bar, baz` from style object
1106 // HACK un-comment this when enableTransition can work
1107 // while a transition is happening
1108 // var transitionValues = [];
1109 // for ( var prop in style ) {
1110 // // dash-ify camelCased properties like WebkitTransition
1111 // prop = vendorProperties[ prop ] || prop;
1112 // transitionValues.push( toDashedAll( prop ) );
1113 // }
1114 // munge number to millisecond, to match stagger
1115 var duration = this.layout.options.transitionDuration;
1116 duration = typeof duration == 'number' ? duration + 'ms' : duration;
1117 // enable transition styles
1118 this.css({
1119 transitionProperty: transitionProps,
1120 transitionDuration: duration,
1121 transitionDelay: this.staggerDelay || 0
1122 });
1123 // listen for transition end event
1124 this.element.addEventListener( transitionEndEvent, this, false );
1125 };
1126
1127 // ----- events ----- //
1128
1129 proto.onwebkitTransitionEnd = function( event ) {
1130 this.ontransitionend( event );
1131 };
1132
1133 proto.onotransitionend = function( event ) {
1134 this.ontransitionend( event );
1135 };
1136
1137 // properties that I munge to make my life easier
1138 var dashedVendorProperties = {
1139 '-webkit-transform': 'transform'
1140 };
1141
1142 proto.ontransitionend = function( event ) {
1143 // disregard bubbled events from children
1144 if ( event.target !== this.element ) {
1145 return;
1146 }
1147 var _transition = this._transn;
1148 // get property name of transitioned property, convert to prefix-free
1149 var propertyName = dashedVendorProperties[ event.propertyName ] || event.propertyName;
1150
1151 // remove property that has completed transitioning
1152 delete _transition.ingProperties[ propertyName ];
1153 // check if any properties are still transitioning
1154 if ( isEmptyObj( _transition.ingProperties ) ) {
1155 // all properties have completed transitioning
1156 this.disableTransition();
1157 }
1158 // clean style
1159 if ( propertyName in _transition.clean ) {
1160 // clean up style
1161 this.element.style[ event.propertyName ] = '';
1162 delete _transition.clean[ propertyName ];
1163 }
1164 // trigger onTransitionEnd callback
1165 if ( propertyName in _transition.onEnd ) {
1166 var onTransitionEnd = _transition.onEnd[ propertyName ];
1167 onTransitionEnd.call( this );
1168 delete _transition.onEnd[ propertyName ];
1169 }
1170
1171 this.emitEvent( 'transitionEnd', [ this ] );
1172 };
1173
1174 proto.disableTransition = function() {
1175 this.removeTransitionStyles();
1176 this.element.removeEventListener( transitionEndEvent, this, false );
1177 this.isTransitioning = false;
1178 };
1179
1180 /**
1181 * removes style property from element
1182 * @param {Object} style
1183 **/
1184 proto._removeStyles = function( style ) {
1185 // clean up transition styles
1186 var cleanStyle = {};
1187 for ( var prop in style ) {
1188 cleanStyle[ prop ] = '';
1189 }
1190 this.css( cleanStyle );
1191 };
1192
1193 var cleanTransitionStyle = {
1194 transitionProperty: '',
1195 transitionDuration: '',
1196 transitionDelay: ''
1197 };
1198
1199 proto.removeTransitionStyles = function() {
1200 // remove transition
1201 this.css( cleanTransitionStyle );
1202 };
1203
1204 // ----- stagger ----- //
1205
1206 proto.stagger = function( delay ) {
1207 delay = isNaN( delay ) ? 0 : delay;
1208 this.staggerDelay = delay + 'ms';
1209 };
1210
1211 // ----- show/hide/remove ----- //
1212
1213 // remove element from DOM
1214 proto.removeElem = function() {
1215 this.element.parentNode.removeChild( this.element );
1216 // remove display: none
1217 this.css({ display: '' });
1218 this.emitEvent( 'remove', [ this ] );
1219 };
1220
1221 proto.remove = function() {
1222 // just remove element if no transition support or no transition
1223 if ( !transitionProperty || !parseFloat( this.layout.options.transitionDuration ) ) {
1224 this.removeElem();
1225 return;
1226 }
1227
1228 // start transition
1229 this.once( 'transitionEnd', function() {
1230 this.removeElem();
1231 });
1232 this.hide();
1233 };
1234
1235 proto.reveal = function() {
1236 delete this.isHidden;
1237 // remove display: none
1238 this.css({ display: '' });
1239
1240 var options = this.layout.options;
1241
1242 var onTransitionEnd = {};
1243 var transitionEndProperty = this.getHideRevealTransitionEndProperty('visibleStyle');
1244 onTransitionEnd[ transitionEndProperty ] = this.onRevealTransitionEnd;
1245
1246 this.transition({
1247 from: options.hiddenStyle,
1248 to: options.visibleStyle,
1249 isCleaning: true,
1250 onTransitionEnd: onTransitionEnd
1251 });
1252 };
1253
1254 proto.onRevealTransitionEnd = function() {
1255 // check if still visible
1256 // during transition, item may have been hidden
1257 if ( !this.isHidden ) {
1258 this.emitEvent('reveal');
1259 }
1260 };
1261
1262 /**
1263 * get style property use for hide/reveal transition end
1264 * @param {String} styleProperty - hiddenStyle/visibleStyle
1265 * @returns {String}
1266 */
1267 proto.getHideRevealTransitionEndProperty = function( styleProperty ) {
1268 var optionStyle = this.layout.options[ styleProperty ];
1269 // use opacity
1270 if ( optionStyle.opacity ) {
1271 return 'opacity';
1272 }
1273 // get first property
1274 for ( var prop in optionStyle ) {
1275 return prop;
1276 }
1277 };
1278
1279 proto.hide = function() {
1280 // set flag
1281 this.isHidden = true;
1282 // remove display: none
1283 this.css({ display: '' });
1284
1285 var options = this.layout.options;
1286
1287 var onTransitionEnd = {};
1288 var transitionEndProperty = this.getHideRevealTransitionEndProperty('hiddenStyle');
1289 onTransitionEnd[ transitionEndProperty ] = this.onHideTransitionEnd;
1290
1291 this.transition({
1292 from: options.visibleStyle,
1293 to: options.hiddenStyle,
1294 // keep hidden stuff hidden
1295 isCleaning: true,
1296 onTransitionEnd: onTransitionEnd
1297 });
1298 };
1299
1300 proto.onHideTransitionEnd = function() {
1301 // check if still hidden
1302 // during transition, item may have been un-hidden
1303 if ( this.isHidden ) {
1304 this.css({ display: 'none' });
1305 this.emitEvent('hide');
1306 }
1307 };
1308
1309 proto.destroy = function() {
1310 this.css({
1311 position: '',
1312 left: '',
1313 right: '',
1314 top: '',
1315 bottom: '',
1316 transition: '',
1317 transform: ''
1318 });
1319 };
1320
1321 return Item;
1322
1323 }));
1324
1325 /*!
1326 * Outlayer v2.1.1
1327 * the brains and guts of a layout library
1328 * MIT license
1329 */
1330
1331 ( function( window, factory ) {
1332 'use strict';
1333 // universal module definition
1334 /* jshint strict: false */ /* globals define, module, require */
1335 if ( typeof define == 'function' && define.amd ) {
1336 // AMD - RequireJS
1337 define( 'outlayer/outlayer',[
1338 'ev-emitter/ev-emitter',
1339 'get-size/get-size',
1340 'fizzy-ui-utils/utils',
1341 './item'
1342 ],
1343 function( EvEmitter, getSize, utils, Item ) {
1344 return factory( window, EvEmitter, getSize, utils, Item);
1345 }
1346 );
1347 } else if ( typeof module == 'object' && module.exports ) {
1348 // CommonJS - Browserify, Webpack
1349 module.exports = factory(
1350 window,
1351 require('ev-emitter'),
1352 require('get-size'),
1353 require('fizzy-ui-utils'),
1354 require('./item')
1355 );
1356 } else {
1357 // browser global
1358 window.Outlayer = factory(
1359 window,
1360 window.EvEmitter,
1361 window.getSize,
1362 window.fizzyUIUtils,
1363 window.Outlayer.Item
1364 );
1365 }
1366
1367 }( window, function factory( window, EvEmitter, getSize, utils, Item ) {
1368 'use strict';
1369
1370 // ----- vars ----- //
1371
1372 var console = window.console;
1373 var jQuery = window.jQuery;
1374 var noop = function() {};
1375
1376 // -------------------------- Outlayer -------------------------- //
1377
1378 // globally unique identifiers
1379 var GUID = 0;
1380 // internal store of all Outlayer intances
1381 var instances = {};
1382
1383
1384 /**
1385 * @param {Element, String} element
1386 * @param {Object} options
1387 * @constructor
1388 */
1389 function Outlayer( element, options ) {
1390 var queryElement = utils.getQueryElement( element );
1391 if ( !queryElement ) {
1392 if ( console ) {
1393 console.error( 'Bad element for ' + this.constructor.namespace +
1394 ': ' + ( queryElement || element ) );
1395 }
1396 return;
1397 }
1398 this.element = queryElement;
1399 // add jQuery
1400 if ( jQuery ) {
1401 this.$element = jQuery( this.element );
1402 }
1403
1404 // options
1405 this.options = utils.extend( {}, this.constructor.defaults );
1406 this.option( options );
1407
1408 // add id for Outlayer.getFromElement
1409 var id = ++GUID;
1410 this.element.outlayerGUID = id; // expando
1411 instances[ id ] = this; // associate via id
1412
1413 // kick it off
1414 this._create();
1415
1416 var isInitLayout = this._getOption('initLayout');
1417 if ( isInitLayout ) {
1418 this.layout();
1419 }
1420 }
1421
1422 // settings are for internal use only
1423 Outlayer.namespace = 'outlayer';
1424 Outlayer.Item = Item;
1425
1426 // default options
1427 Outlayer.defaults = {
1428 containerStyle: {
1429 position: 'relative'
1430 },
1431 initLayout: true,
1432 originLeft: true,
1433 originTop: true,
1434 resize: true,
1435 resizeContainer: true,
1436 // item options
1437 transitionDuration: '0.4s',
1438 hiddenStyle: {
1439 opacity: 0,
1440 transform: 'scale(0.001)'
1441 },
1442 visibleStyle: {
1443 opacity: 1,
1444 transform: 'scale(1)'
1445 }
1446 };
1447
1448 var proto = Outlayer.prototype;
1449 // inherit EvEmitter
1450 utils.extend( proto, EvEmitter.prototype );
1451
1452 /**
1453 * set options
1454 * @param {Object} opts
1455 */
1456 proto.option = function( opts ) {
1457 utils.extend( this.options, opts );
1458 };
1459
1460 /**
1461 * get backwards compatible option value, check old name
1462 */
1463 proto._getOption = function( option ) {
1464 var oldOption = this.constructor.compatOptions[ option ];
1465 return oldOption && this.options[ oldOption ] !== undefined ?
1466 this.options[ oldOption ] : this.options[ option ];
1467 };
1468
1469 Outlayer.compatOptions = {
1470 // currentName: oldName
1471 initLayout: 'isInitLayout',
1472 horizontal: 'isHorizontal',
1473 layoutInstant: 'isLayoutInstant',
1474 originLeft: 'isOriginLeft',
1475 originTop: 'isOriginTop',
1476 resize: 'isResizeBound',
1477 resizeContainer: 'isResizingContainer'
1478 };
1479
1480 proto._create = function() {
1481 // get items from children
1482 this.reloadItems();
1483 // elements that affect layout, but are not laid out
1484 this.stamps = [];
1485 this.stamp( this.options.stamp );
1486 // set container style
1487 utils.extend( this.element.style, this.options.containerStyle );
1488
1489 // bind resize method
1490 var canBindResize = this._getOption('resize');
1491 if ( canBindResize ) {
1492 this.bindResize();
1493 }
1494 };
1495
1496 // goes through all children again and gets bricks in proper order
1497 proto.reloadItems = function() {
1498 // collection of item elements
1499 this.items = this._itemize( this.element.children );
1500 };
1501
1502
1503 /**
1504 * turn elements into Outlayer.Items to be used in layout
1505 * @param {Array or NodeList or HTMLElement} elems
1506 * @returns {Array} items - collection of new Outlayer Items
1507 */
1508 proto._itemize = function( elems ) {
1509
1510 var itemElems = this._filterFindItemElements( elems );
1511 var Item = this.constructor.Item;
1512
1513 // create new Outlayer Items for collection
1514 var items = [];
1515 for ( var i=0; i < itemElems.length; i++ ) {
1516 var elem = itemElems[i];
1517 var item = new Item( elem, this );
1518 items.push( item );
1519 }
1520
1521 return items;
1522 };
1523
1524 /**
1525 * get item elements to be used in layout
1526 * @param {Array or NodeList or HTMLElement} elems
1527 * @returns {Array} items - item elements
1528 */
1529 proto._filterFindItemElements = function( elems ) {
1530 return utils.filterFindElements( elems, this.options.itemSelector );
1531 };
1532
1533 /**
1534 * getter method for getting item elements
1535 * @returns {Array} elems - collection of item elements
1536 */
1537 proto.getItemElements = function() {
1538 return this.items.map( function( item ) {
1539 return item.element;
1540 });
1541 };
1542
1543 // ----- init & layout ----- //
1544
1545 /**
1546 * lays out all items
1547 */
1548 proto.layout = function() {
1549 this._resetLayout();
1550 this._manageStamps();
1551
1552 // don't animate first layout
1553 var layoutInstant = this._getOption('layoutInstant');
1554 var isInstant = layoutInstant !== undefined ?
1555 layoutInstant : !this._isLayoutInited;
1556 this.layoutItems( this.items, isInstant );
1557
1558 // flag for initalized
1559 this._isLayoutInited = true;
1560 };
1561
1562 // _init is alias for layout
1563 proto._init = proto.layout;
1564
1565 /**
1566 * logic before any new layout
1567 */
1568 proto._resetLayout = function() {
1569 this.getSize();
1570 };
1571
1572
1573 proto.getSize = function() {
1574 this.size = getSize( this.element );
1575 };
1576
1577 /**
1578 * get measurement from option, for columnWidth, rowHeight, gutter
1579 * if option is String -> get element from selector string, & get size of element
1580 * if option is Element -> get size of element
1581 * else use option as a number
1582 *
1583 * @param {String} measurement
1584 * @param {String} size - width or height
1585 * @private
1586 */
1587 proto._getMeasurement = function( measurement, size ) {
1588 var option = this.options[ measurement ];
1589 var elem;
1590 if ( !option ) {
1591 // default to 0
1592 this[ measurement ] = 0;
1593 } else {
1594 // use option as an element
1595 if ( typeof option == 'string' ) {
1596 elem = this.element.querySelector( option );
1597 } else if ( option instanceof HTMLElement ) {
1598 elem = option;
1599 }
1600 // use size of element, if element
1601 this[ measurement ] = elem ? getSize( elem )[ size ] : option;
1602 }
1603 };
1604
1605 /**
1606 * layout a collection of item elements
1607 * @api public
1608 */
1609 proto.layoutItems = function( items, isInstant ) {
1610 items = this._getItemsForLayout( items );
1611
1612 this._layoutItems( items, isInstant );
1613
1614 this._postLayout();
1615 };
1616
1617 /**
1618 * get the items to be laid out
1619 * you may want to skip over some items
1620 * @param {Array} items
1621 * @returns {Array} items
1622 */
1623 proto._getItemsForLayout = function( items ) {
1624 return items.filter( function( item ) {
1625 return !item.isIgnored;
1626 });
1627 };
1628
1629 /**
1630 * layout items
1631 * @param {Array} items
1632 * @param {Boolean} isInstant
1633 */
1634 proto._layoutItems = function( items, isInstant ) {
1635 this._emitCompleteOnItems( 'layout', items );
1636
1637 if ( !items || !items.length ) {
1638 // no items, emit event with empty array
1639 return;
1640 }
1641
1642 var queue = [];
1643
1644 items.forEach( function( item ) {
1645 // get x/y object from method
1646 var position = this._getItemLayoutPosition( item );
1647 // enqueue
1648 position.item = item;
1649 position.isInstant = isInstant || item.isLayoutInstant;
1650 queue.push( position );
1651 }, this );
1652
1653 this._processLayoutQueue( queue );
1654 };
1655
1656 /**
1657 * get item layout position
1658 * @param {Outlayer.Item} item
1659 * @returns {Object} x and y position
1660 */
1661 proto._getItemLayoutPosition = function( /* item */ ) {
1662 return {
1663 x: 0,
1664 y: 0
1665 };
1666 };
1667
1668 /**
1669 * iterate over array and position each item
1670 * Reason being - separating this logic prevents 'layout invalidation'
1671 * thx @paul_irish
1672 * @param {Array} queue
1673 */
1674 proto._processLayoutQueue = function( queue ) {
1675 this.updateStagger();
1676 queue.forEach( function( obj, i ) {
1677 this._positionItem( obj.item, obj.x, obj.y, obj.isInstant, i );
1678 }, this );
1679 };
1680
1681 // set stagger from option in milliseconds number
1682 proto.updateStagger = function() {
1683 var stagger = this.options.stagger;
1684 if ( stagger === null || stagger === undefined ) {
1685 this.stagger = 0;
1686 return;
1687 }
1688 this.stagger = getMilliseconds( stagger );
1689 return this.stagger;
1690 };
1691
1692 /**
1693 * Sets position of item in DOM
1694 * @param {Outlayer.Item} item
1695 * @param {Number} x - horizontal position
1696 * @param {Number} y - vertical position
1697 * @param {Boolean} isInstant - disables transitions
1698 */
1699 proto._positionItem = function( item, x, y, isInstant, i ) {
1700 if ( isInstant ) {
1701 // if not transition, just set CSS
1702 item.goTo( x, y );
1703 } else {
1704 item.stagger( i * this.stagger );
1705 item.moveTo( x, y );
1706 }
1707 };
1708
1709 /**
1710 * Any logic you want to do after each layout,
1711 * i.e. size the container
1712 */
1713 proto._postLayout = function() {
1714 this.resizeContainer();
1715 };
1716
1717 proto.resizeContainer = function() {
1718 var isResizingContainer = this._getOption('resizeContainer');
1719 if ( !isResizingContainer ) {
1720 return;
1721 }
1722 var size = this._getContainerSize();
1723 if ( size ) {
1724 this._setContainerMeasure( size.width, true );
1725 this._setContainerMeasure( size.height, false );
1726 }
1727 };
1728
1729 /**
1730 * Sets width or height of container if returned
1731 * @returns {Object} size
1732 * @param {Number} width
1733 * @param {Number} height
1734 */
1735 proto._getContainerSize = noop;
1736
1737 /**
1738 * @param {Number} measure - size of width or height
1739 * @param {Boolean} isWidth
1740 */
1741 proto._setContainerMeasure = function( measure, isWidth ) {
1742 if ( measure === undefined ) {
1743 return;
1744 }
1745
1746 var elemSize = this.size;
1747 // add padding and border width if border box
1748 if ( elemSize.isBorderBox ) {
1749 measure += isWidth ? elemSize.paddingLeft + elemSize.paddingRight +
1750 elemSize.borderLeftWidth + elemSize.borderRightWidth :
1751 elemSize.paddingBottom + elemSize.paddingTop +
1752 elemSize.borderTopWidth + elemSize.borderBottomWidth;
1753 }
1754
1755 measure = Math.max( measure, 0 );
1756 this.element.style[ isWidth ? 'width' : 'height' ] = measure + 'px';
1757 };
1758
1759 /**
1760 * emit eventComplete on a collection of items events
1761 * @param {String} eventName
1762 * @param {Array} items - Outlayer.Items
1763 */
1764 proto._emitCompleteOnItems = function( eventName, items ) {
1765 var _this = this;
1766 function onComplete() {
1767 _this.dispatchEvent( eventName + 'Complete', null, [ items ] );
1768 }
1769
1770 var count = items.length;
1771 if ( !items || !count ) {
1772 onComplete();
1773 return;
1774 }
1775
1776 var doneCount = 0;
1777 function tick() {
1778 doneCount++;
1779 if ( doneCount == count ) {
1780 onComplete();
1781 }
1782 }
1783
1784 // bind callback
1785 items.forEach( function( item ) {
1786 item.once( eventName, tick );
1787 });
1788 };
1789
1790 /**
1791 * emits events via EvEmitter and jQuery events
1792 * @param {String} type - name of event
1793 * @param {Event} event - original event
1794 * @param {Array} args - extra arguments
1795 */
1796 proto.dispatchEvent = function( type, event, args ) {
1797 // add original event to arguments
1798 var emitArgs = event ? [ event ].concat( args ) : args;
1799 this.emitEvent( type, emitArgs );
1800
1801 if ( jQuery ) {
1802 // set this.$element
1803 this.$element = this.$element || jQuery( this.element );
1804 if ( event ) {
1805 // create jQuery event
1806 var $event = jQuery.Event( event );
1807 $event.type = type;
1808 this.$element.trigger( $event, args );
1809 } else {
1810 // just trigger with type if no event available
1811 this.$element.trigger( type, args );
1812 }
1813 }
1814 };
1815
1816 // -------------------------- ignore & stamps -------------------------- //
1817
1818
1819 /**
1820 * keep item in collection, but do not lay it out
1821 * ignored items do not get skipped in layout
1822 * @param {Element} elem
1823 */
1824 proto.ignore = function( elem ) {
1825 var item = this.getItem( elem );
1826 if ( item ) {
1827 item.isIgnored = true;
1828 }
1829 };
1830
1831 /**
1832 * return item to layout collection
1833 * @param {Element} elem
1834 */
1835 proto.unignore = function( elem ) {
1836 var item = this.getItem( elem );
1837 if ( item ) {
1838 delete item.isIgnored;
1839 }
1840 };
1841
1842 /**
1843 * adds elements to stamps
1844 * @param {NodeList, Array, Element, or String} elems
1845 */
1846 proto.stamp = function( elems ) {
1847 elems = this._find( elems );
1848 if ( !elems ) {
1849 return;
1850 }
1851
1852 this.stamps = this.stamps.concat( elems );
1853 // ignore
1854 elems.forEach( this.ignore, this );
1855 };
1856
1857 /**
1858 * removes elements to stamps
1859 * @param {NodeList, Array, or Element} elems
1860 */
1861 proto.unstamp = function( elems ) {
1862 elems = this._find( elems );
1863 if ( !elems ){
1864 return;
1865 }
1866
1867 elems.forEach( function( elem ) {
1868 // filter out removed stamp elements
1869 utils.removeFrom( this.stamps, elem );
1870 this.unignore( elem );
1871 }, this );
1872 };
1873
1874 /**
1875 * finds child elements
1876 * @param {NodeList, Array, Element, or String} elems
1877 * @returns {Array} elems
1878 */
1879 proto._find = function( elems ) {
1880 if ( !elems ) {
1881 return;
1882 }
1883 // if string, use argument as selector string
1884 if ( typeof elems == 'string' ) {
1885 elems = this.element.querySelectorAll( elems );
1886 }
1887 elems = utils.makeArray( elems );
1888 return elems;
1889 };
1890
1891 proto._manageStamps = function() {
1892 if ( !this.stamps || !this.stamps.length ) {
1893 return;
1894 }
1895
1896 this._getBoundingRect();
1897
1898 this.stamps.forEach( this._manageStamp, this );
1899 };
1900
1901 // update boundingLeft / Top
1902 proto._getBoundingRect = function() {
1903 // get bounding rect for container element
1904 var boundingRect = this.element.getBoundingClientRect();
1905 var size = this.size;
1906 this._boundingRect = {
1907 left: boundingRect.left + size.paddingLeft + size.borderLeftWidth,
1908 top: boundingRect.top + size.paddingTop + size.borderTopWidth,
1909 right: boundingRect.right - ( size.paddingRight + size.borderRightWidth ),
1910 bottom: boundingRect.bottom - ( size.paddingBottom + size.borderBottomWidth )
1911 };
1912 };
1913
1914 /**
1915 * @param {Element} stamp
1916 **/
1917 proto._manageStamp = noop;
1918
1919 /**
1920 * get x/y position of element relative to container element
1921 * @param {Element} elem
1922 * @returns {Object} offset - has left, top, right, bottom
1923 */
1924 proto._getElementOffset = function( elem ) {
1925 var boundingRect = elem.getBoundingClientRect();
1926 var thisRect = this._boundingRect;
1927 var size = getSize( elem );
1928 var offset = {
1929 left: boundingRect.left - thisRect.left - size.marginLeft,
1930 top: boundingRect.top - thisRect.top - size.marginTop,
1931 right: thisRect.right - boundingRect.right - size.marginRight,
1932 bottom: thisRect.bottom - boundingRect.bottom - size.marginBottom
1933 };
1934 return offset;
1935 };
1936
1937 // -------------------------- resize -------------------------- //
1938
1939 // enable event handlers for listeners
1940 // i.e. resize -> onresize
1941 proto.handleEvent = utils.handleEvent;
1942
1943 /**
1944 * Bind layout to window resizing
1945 */
1946 proto.bindResize = function() {
1947 window.addEventListener( 'resize', this );
1948 this.isResizeBound = true;
1949 };
1950
1951 /**
1952 * Unbind layout to window resizing
1953 */
1954 proto.unbindResize = function() {
1955 window.removeEventListener( 'resize', this );
1956 this.isResizeBound = false;
1957 };
1958
1959 proto.onresize = function() {
1960 this.resize();
1961 };
1962
1963 utils.debounceMethod( Outlayer, 'onresize', 100 );
1964
1965 proto.resize = function() {
1966 // don't trigger if size did not change
1967 // or if resize was unbound. See #9
1968 if ( !this.isResizeBound || !this.needsResizeLayout() ) {
1969 return;
1970 }
1971
1972 this.layout();
1973 };
1974
1975 /**
1976 * check if layout is needed post layout
1977 * @returns Boolean
1978 */
1979 proto.needsResizeLayout = function() {
1980 var size = getSize( this.element );
1981 // check that this.size and size are there
1982 // IE8 triggers resize on body size change, so they might not be
1983 var hasSizes = this.size && size;
1984 return hasSizes && size.innerWidth !== this.size.innerWidth;
1985 };
1986
1987 // -------------------------- methods -------------------------- //
1988
1989 /**
1990 * add items to Outlayer instance
1991 * @param {Array or NodeList or Element} elems
1992 * @returns {Array} items - Outlayer.Items
1993 **/
1994 proto.addItems = function( elems ) {
1995 var items = this._itemize( elems );
1996 // add items to collection
1997 if ( items.length ) {
1998 this.items = this.items.concat( items );
1999 }
2000 return items;
2001 };
2002
2003 /**
2004 * Layout newly-appended item elements
2005 * @param {Array or NodeList or Element} elems
2006 */
2007 proto.appended = function( elems ) {
2008 var items = this.addItems( elems );
2009 if ( !items.length ) {
2010 return;
2011 }
2012 // layout and reveal just the new items
2013 this.layoutItems( items, true );
2014 this.reveal( items );
2015 };
2016
2017 /**
2018 * Layout prepended elements
2019 * @param {Array or NodeList or Element} elems
2020 */
2021 proto.prepended = function( elems ) {
2022 var items = this._itemize( elems );
2023 if ( !items.length ) {
2024 return;
2025 }
2026 // add items to beginning of collection
2027 var previousItems = this.items.slice(0);
2028 this.items = items.concat( previousItems );
2029 // start new layout
2030 this._resetLayout();
2031 this._manageStamps();
2032 // layout new stuff without transition
2033 this.layoutItems( items, true );
2034 this.reveal( items );
2035 // layout previous items
2036 this.layoutItems( previousItems );
2037 };
2038
2039 /**
2040 * reveal a collection of items
2041 * @param {Array of Outlayer.Items} items
2042 */
2043 proto.reveal = function( items ) {
2044 this._emitCompleteOnItems( 'reveal', items );
2045 if ( !items || !items.length ) {
2046 return;
2047 }
2048 var stagger = this.updateStagger();
2049 items.forEach( function( item, i ) {
2050 item.stagger( i * stagger );
2051 item.reveal();
2052 });
2053 };
2054
2055 /**
2056 * hide a collection of items
2057 * @param {Array of Outlayer.Items} items
2058 */
2059 proto.hide = function( items ) {
2060 this._emitCompleteOnItems( 'hide', items );
2061 if ( !items || !items.length ) {
2062 return;
2063 }
2064 var stagger = this.updateStagger();
2065 items.forEach( function( item, i ) {
2066 item.stagger( i * stagger );
2067 item.hide();
2068 });
2069 };
2070
2071 /**
2072 * reveal item elements
2073 * @param {Array}, {Element}, {NodeList} items
2074 */
2075 proto.revealItemElements = function( elems ) {
2076 var items = this.getItems( elems );
2077 this.reveal( items );
2078 };
2079
2080 /**
2081 * hide item elements
2082 * @param {Array}, {Element}, {NodeList} items
2083 */
2084 proto.hideItemElements = function( elems ) {
2085 var items = this.getItems( elems );
2086 this.hide( items );
2087 };
2088
2089 /**
2090 * get Outlayer.Item, given an Element
2091 * @param {Element} elem
2092 * @param {Function} callback
2093 * @returns {Outlayer.Item} item
2094 */
2095 proto.getItem = function( elem ) {
2096 // loop through items to get the one that matches
2097 for ( var i=0; i < this.items.length; i++ ) {
2098 var item = this.items[i];
2099 if ( item.element == elem ) {
2100 // return item
2101 return item;
2102 }
2103 }
2104 };
2105
2106 /**
2107 * get collection of Outlayer.Items, given Elements
2108 * @param {Array} elems
2109 * @returns {Array} items - Outlayer.Items
2110 */
2111 proto.getItems = function( elems ) {
2112 elems = utils.makeArray( elems );
2113 var items = [];
2114 elems.forEach( function( elem ) {
2115 var item = this.getItem( elem );
2116 if ( item ) {
2117 items.push( item );
2118 }
2119 }, this );
2120
2121 return items;
2122 };
2123
2124 /**
2125 * remove element(s) from instance and DOM
2126 * @param {Array or NodeList or Element} elems
2127 */
2128 proto.remove = function( elems ) {
2129 var removeItems = this.getItems( elems );
2130
2131 this._emitCompleteOnItems( 'remove', removeItems );
2132
2133 // bail if no items to remove
2134 if ( !removeItems || !removeItems.length ) {
2135 return;
2136 }
2137
2138 removeItems.forEach( function( item ) {
2139 item.remove();
2140 // remove item from collection
2141 utils.removeFrom( this.items, item );
2142 }, this );
2143 };
2144
2145 // ----- destroy ----- //
2146
2147 // remove and disable Outlayer instance
2148 proto.destroy = function() {
2149 // clean up dynamic styles
2150 var style = this.element.style;
2151 style.height = '';
2152 style.position = '';
2153 style.width = '';
2154 // destroy items
2155 this.items.forEach( function( item ) {
2156 item.destroy();
2157 });
2158
2159 this.unbindResize();
2160
2161 var id = this.element.outlayerGUID;
2162 delete instances[ id ]; // remove reference to instance by id
2163 delete this.element.outlayerGUID;
2164 // remove data for jQuery
2165 if ( jQuery ) {
2166 jQuery.removeData( this.element, this.constructor.namespace );
2167 }
2168
2169 };
2170
2171 // -------------------------- data -------------------------- //
2172
2173 /**
2174 * get Outlayer instance from element
2175 * @param {Element} elem
2176 * @returns {Outlayer}
2177 */
2178 Outlayer.data = function( elem ) {
2179 elem = utils.getQueryElement( elem );
2180 var id = elem && elem.outlayerGUID;
2181 return id && instances[ id ];
2182 };
2183
2184
2185 // -------------------------- create Outlayer class -------------------------- //
2186
2187 /**
2188 * create a layout class
2189 * @param {String} namespace
2190 */
2191 Outlayer.create = function( namespace, options ) {
2192 // sub-class Outlayer
2193 var Layout = subclass( Outlayer );
2194 // apply new options and compatOptions
2195 Layout.defaults = utils.extend( {}, Outlayer.defaults );
2196 utils.extend( Layout.defaults, options );
2197 Layout.compatOptions = utils.extend( {}, Outlayer.compatOptions );
2198
2199 Layout.namespace = namespace;
2200
2201 Layout.data = Outlayer.data;
2202
2203 // sub-class Item
2204 Layout.Item = subclass( Item );
2205
2206 // -------------------------- declarative -------------------------- //
2207
2208 utils.htmlInit( Layout, namespace );
2209
2210 // -------------------------- jQuery bridge -------------------------- //
2211
2212 // make into jQuery plugin
2213 if ( jQuery && jQuery.bridget ) {
2214 jQuery.bridget( namespace, Layout );
2215 }
2216
2217 return Layout;
2218 };
2219
2220 function subclass( Parent ) {
2221 function SubClass() {
2222 Parent.apply( this, arguments );
2223 }
2224
2225 SubClass.prototype = Object.create( Parent.prototype );
2226 SubClass.prototype.constructor = SubClass;
2227
2228 return SubClass;
2229 }
2230
2231 // ----- helpers ----- //
2232
2233 // how many milliseconds are in each unit
2234 var msUnits = {
2235 ms: 1,
2236 s: 1000
2237 };
2238
2239 // munge time-like parameter into millisecond number
2240 // '0.4s' -> 40
2241 function getMilliseconds( time ) {
2242 if ( typeof time == 'number' ) {
2243 return time;
2244 }
2245 var matches = time.match( /(^\d*\.?\d*)(\w*)/ );
2246 var num = matches && matches[1];
2247 var unit = matches && matches[2];
2248 if ( !num.length ) {
2249 return 0;
2250 }
2251 num = parseFloat( num );
2252 var mult = msUnits[ unit ] || 1;
2253 return num * mult;
2254 }
2255
2256 // ----- fin ----- //
2257
2258 // back in global
2259 Outlayer.Item = Item;
2260
2261 return Outlayer;
2262
2263 }));
2264
2265 /*!
2266 * Masonry v4.2.2
2267 * Cascading grid layout library
2268 * https://masonry.desandro.com
2269 * MIT License
2270 * by David DeSandro
2271 */
2272
2273 ( function( window, factory ) {
2274 // universal module definition
2275 /* jshint strict: false */ /*globals define, module, require */
2276 if ( typeof define == 'function' && define.amd ) {
2277 // AMD
2278 define( [
2279 'outlayer/outlayer',
2280 'get-size/get-size'
2281 ],
2282 factory );
2283 } else if ( typeof module == 'object' && module.exports ) {
2284 // CommonJS
2285 module.exports = factory(
2286 require('outlayer'),
2287 require('get-size')
2288 );
2289 } else {
2290 // browser global
2291 window.Masonry = factory(
2292 window.Outlayer,
2293 window.getSize
2294 );
2295 }
2296
2297 }( window, function factory( Outlayer, getSize ) {
2298
2299
2300
2301 // -------------------------- masonryDefinition -------------------------- //
2302
2303 // create an Outlayer layout class
2304 var Masonry = Outlayer.create('masonry');
2305 // isFitWidth -> fitWidth
2306 Masonry.compatOptions.fitWidth = 'isFitWidth';
2307
2308 var proto = Masonry.prototype;
2309
2310 proto._resetLayout = function() {
2311 this.getSize();
2312 this._getMeasurement( 'columnWidth', 'outerWidth' );
2313 this._getMeasurement( 'gutter', 'outerWidth' );
2314 this.measureColumns();
2315
2316 // reset column Y
2317 this.colYs = [];
2318 for ( var i=0; i < this.cols; i++ ) {
2319 this.colYs.push( 0 );
2320 }
2321
2322 this.maxY = 0;
2323 this.horizontalColIndex = 0;
2324 };
2325
2326 proto.measureColumns = function() {
2327 this.getContainerWidth();
2328 // if columnWidth is 0, default to outerWidth of first item
2329 if ( !this.columnWidth ) {
2330 var firstItem = this.items[0];
2331 var firstItemElem = firstItem && firstItem.element;
2332 // columnWidth fall back to item of first element
2333 this.columnWidth = firstItemElem && getSize( firstItemElem ).outerWidth ||
2334 // if first elem has no width, default to size of container
2335 this.containerWidth;
2336 }
2337
2338 var columnWidth = this.columnWidth += this.gutter;
2339
2340 // calculate columns
2341 var containerWidth = this.containerWidth + this.gutter;
2342 var cols = containerWidth / columnWidth;
2343 // fix rounding errors, typically with gutters
2344 var excess = columnWidth - containerWidth % columnWidth;
2345 // if overshoot is less than a pixel, round up, otherwise floor it
2346 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
2347 cols = Math[ mathMethod ]( cols );
2348 this.cols = Math.max( cols, 1 );
2349 };
2350
2351 proto.getContainerWidth = function() {
2352 // container is parent if fit width
2353 var isFitWidth = this._getOption('fitWidth');
2354 var container = isFitWidth ? this.element.parentNode : this.element;
2355 // check that this.size and size are there
2356 // IE8 triggers resize on body size change, so they might not be
2357 var size = getSize( container );
2358 this.containerWidth = size && size.innerWidth;
2359 };
2360
2361 proto._getItemLayoutPosition = function( item ) {
2362 item.getSize();
2363 // how many columns does this brick span
2364 var remainder = item.size.outerWidth % this.columnWidth;
2365 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
2366 // round if off by 1 pixel, otherwise use ceil
2367 var colSpan = Math[ mathMethod ]( item.size.outerWidth / this.columnWidth );
2368 colSpan = Math.min( colSpan, this.cols );
2369 // use horizontal or top column position
2370 var colPosMethod = this.options.horizontalOrder ?
2371 '_getHorizontalColPosition' : '_getTopColPosition';
2372 var colPosition = this[ colPosMethod ]( colSpan, item );
2373 // position the brick
2374 var position = {
2375 x: this.columnWidth * colPosition.col,
2376 y: colPosition.y
2377 };
2378 // apply setHeight to necessary columns
2379 var setHeight = colPosition.y + item.size.outerHeight;
2380 var setMax = colSpan + colPosition.col;
2381 for ( var i = colPosition.col; i < setMax; i++ ) {
2382 this.colYs[i] = setHeight;
2383 }
2384
2385 return position;
2386 };
2387
2388 proto._getTopColPosition = function( colSpan ) {
2389 var colGroup = this._getTopColGroup( colSpan );
2390 // get the minimum Y value from the columns
2391 var minimumY = Math.min.apply( Math, colGroup );
2392
2393 return {
2394 col: colGroup.indexOf( minimumY ),
2395 y: minimumY,
2396 };
2397 };
2398
2399 /**
2400 * @param {Number} colSpan - number of columns the element spans
2401 * @returns {Array} colGroup
2402 */
2403 proto._getTopColGroup = function( colSpan ) {
2404 if ( colSpan < 2 ) {
2405 // if brick spans only one column, use all the column Ys
2406 return this.colYs;
2407 }
2408
2409 var colGroup = [];
2410 // how many different places could this brick fit horizontally
2411 var groupCount = this.cols + 1 - colSpan;
2412 // for each group potential horizontal position
2413 for ( var i = 0; i < groupCount; i++ ) {
2414 colGroup[i] = this._getColGroupY( i, colSpan );
2415 }
2416 return colGroup;
2417 };
2418
2419 proto._getColGroupY = function( col, colSpan ) {
2420 if ( colSpan < 2 ) {
2421 return this.colYs[ col ];
2422 }
2423 // make an array of colY values for that one group
2424 var groupColYs = this.colYs.slice( col, col + colSpan );
2425 // and get the max value of the array
2426 return Math.max.apply( Math, groupColYs );
2427 };
2428
2429 // get column position based on horizontal index. #873
2430 proto._getHorizontalColPosition = function( colSpan, item ) {
2431 var col = this.horizontalColIndex % this.cols;
2432 var isOver = colSpan > 1 && col + colSpan > this.cols;
2433 // shift to next row if item can't fit on current row
2434 col = isOver ? 0 : col;
2435 // don't let zero-size items take up space
2436 var hasSize = item.size.outerWidth && item.size.outerHeight;
2437 this.horizontalColIndex = hasSize ? col + colSpan : this.horizontalColIndex;
2438
2439 return {
2440 col: col,
2441 y: this._getColGroupY( col, colSpan ),
2442 };
2443 };
2444
2445 proto._manageStamp = function( stamp ) {
2446 var stampSize = getSize( stamp );
2447 var offset = this._getElementOffset( stamp );
2448 // get the columns that this stamp affects
2449 var isOriginLeft = this._getOption('originLeft');
2450 var firstX = isOriginLeft ? offset.left : offset.right;
2451 var lastX = firstX + stampSize.outerWidth;
2452 var firstCol = Math.floor( firstX / this.columnWidth );
2453 firstCol = Math.max( 0, firstCol );
2454 var lastCol = Math.floor( lastX / this.columnWidth );
2455 // lastCol should not go over if multiple of columnWidth #425
2456 lastCol -= lastX % this.columnWidth ? 0 : 1;
2457 lastCol = Math.min( this.cols - 1, lastCol );
2458 // set colYs to bottom of the stamp
2459
2460 var isOriginTop = this._getOption('originTop');
2461 var stampMaxY = ( isOriginTop ? offset.top : offset.bottom ) +
2462 stampSize.outerHeight;
2463 for ( var i = firstCol; i <= lastCol; i++ ) {
2464 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
2465 }
2466 };
2467
2468 proto._getContainerSize = function() {
2469 this.maxY = Math.max.apply( Math, this.colYs );
2470 var size = {
2471 height: this.maxY
2472 };
2473
2474 if ( this._getOption('fitWidth') ) {
2475 size.width = this._getContainerFitWidth();
2476 }
2477
2478 return size;
2479 };
2480
2481 proto._getContainerFitWidth = function() {
2482 var unusedCols = 0;
2483 // count unused columns
2484 var i = this.cols;
2485 while ( --i ) {
2486 if ( this.colYs[i] !== 0 ) {
2487 break;
2488 }
2489 unusedCols++;
2490 }
2491 // fit container to columns that have been used
2492 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
2493 };
2494
2495 proto.needsResizeLayout = function() {
2496 var previousWidth = this.containerWidth;
2497 this.getContainerWidth();
2498 return previousWidth != this.containerWidth;
2499 };
2500
2501 return Masonry;
2502
2503 }));
2504
2505