UNPKG

63.3 kBJavaScriptView Raw
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
44var arraySlice = Array.prototype.slice;
45
46// helper function for logging errors
47// $.error breaks jQuery chaining
48var console = window.console;
49var logError = typeof console == 'undefined' ? function() {} :
50 function( message ) {
51 console.error( message );
52 };
53
54// ----- jQueryBridget ----- //
55
56function 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
138function updateJQuery( $ ) {
139 if ( !$ || ( $ && $.bridget ) ) {
140 return;
141 }
142 $.bridget = jQueryBridget;
143}
144
145updateJQuery( jQuery || window.jQuery );
146
147// ----- ----- //
148
149return 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
179function EvEmitter() {}
180
181var proto = EvEmitter.prototype;
182
183proto.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
199proto.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
216proto.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
229proto.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
257proto.allOff = function() {
258 delete this._events;
259 delete this._onceEvents;
260};
261
262return 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
294function 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
301function noop() {}
302
303var logError = typeof console == 'undefined' ? noop :
304 function( message ) {
305 console.error( message );
306 };
307
308// -------------------------- measurements -------------------------- //
309
310var 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
325var measurementsLength = measurements.length;
326
327function 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 */
349function 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
361var isSetup = false;
362
363var isBoxSizeOuter;
364
365/**
366 * setup
367 * check isBoxSizerOuter
368 * do on first getSize() rather than on page load for Firefox bug
369 */
370function 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
402function 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
470return 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
564var utils = {};
565
566// ----- extend ----- //
567
568// extends objects
569utils.extend = function( a, b ) {
570 for ( var prop in b ) {
571 a[ prop ] = b[ prop ];
572 }
573 return a;
574};
575
576// ----- modulo ----- //
577
578utils.modulo = function( num, div ) {
579 return ( ( num % div ) + div ) % div;
580};
581
582// ----- makeArray ----- //
583
584var arraySlice = Array.prototype.slice;
585
586// turn element or nodeList into an array
587utils.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
609utils.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
618utils.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
630utils.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' )
640utils.handleEvent = function( event ) {
641 var method = 'on' + event.type;
642 if ( this[ method ] ) {
643 this[ method ]( event );
644 }
645};
646
647// ----- filterFindElements ----- //
648
649utils.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
682utils.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
703utils.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/
716utils.toDashed = function( str ) {
717 return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
718 return $1 + '-' + $2;
719 }).toLowerCase();
720};
721
722var 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 */
728utils.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
766return 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
805function 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
816var docElemStyle = document.documentElement.style;
817
818var transitionProperty = typeof docElemStyle.transition == 'string' ?
819 'transition' : 'WebkitTransition';
820var transformProperty = typeof docElemStyle.transform == 'string' ?
821 'transform' : 'WebkitTransform';
822
823var transitionEndEvent = {
824 WebkitTransition: 'webkitTransitionEnd',
825 transition: 'transitionend'
826}[ transitionProperty ];
827
828// cache all vendor properties that could have vendor prefix
829var vendorProperties = {
830 transform: transformProperty,
831 transition: transitionProperty,
832 transitionDuration: transitionProperty + 'Duration',
833 transitionProperty: transitionProperty + 'Property',
834 transitionDelay: transitionProperty + 'Delay'
835};
836
837// -------------------------- Item -------------------------- //
838
839function 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
856var proto = Item.prototype = Object.create( EvEmitter.prototype );
857proto.constructor = Item;
858
859proto._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
873proto.handleEvent = function( event ) {
874 var method = 'on' + event.type;
875 if ( this[ method ] ) {
876 this[ method ]( event );
877 }
878};
879
880proto.getSize = function() {
881 this.size = getSize( this.element );
882};
883
884/**
885 * apply CSS styles to element
886 * @param {Object} style
887 */
888proto.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
899proto.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
927proto.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
959proto.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
965proto.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
971proto._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
1002proto.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
1012proto.goTo = function( x, y ) {
1013 this.setPosition( x, y );
1014 this.layoutPosition();
1015};
1016
1017proto.moveTo = proto._transitionTo;
1018
1019proto.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
1032proto._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 */
1050proto.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
1090function toDashedAll( str ) {
1091 return str.replace( /([A-Z])/g, function( $1 ) {
1092 return '-' + $1.toLowerCase();
1093 });
1094}
1095
1096var transitionProps = 'opacity,' + toDashedAll( transformProperty );
1097
1098proto.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
1129proto.onwebkitTransitionEnd = function( event ) {
1130 this.ontransitionend( event );
1131};
1132
1133proto.onotransitionend = function( event ) {
1134 this.ontransitionend( event );
1135};
1136
1137// properties that I munge to make my life easier
1138var dashedVendorProperties = {
1139 '-webkit-transform': 'transform'
1140};
1141
1142proto.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
1174proto.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**/
1184proto._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
1193var cleanTransitionStyle = {
1194 transitionProperty: '',
1195 transitionDuration: '',
1196 transitionDelay: ''
1197};
1198
1199proto.removeTransitionStyles = function() {
1200 // remove transition
1201 this.css( cleanTransitionStyle );
1202};
1203
1204// ----- stagger ----- //
1205
1206proto.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
1214proto.removeElem = function() {
1215 this.element.parentNode.removeChild( this.element );
1216 // remove display: none
1217 this.css({ display: '' });
1218 this.emitEvent( 'remove', [ this ] );
1219};
1220
1221proto.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
1235proto.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
1254proto.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 */
1267proto.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
1279proto.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
1300proto.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
1309proto.destroy = function() {
1310 this.css({
1311 position: '',
1312 left: '',
1313 right: '',
1314 top: '',
1315 bottom: '',
1316 transition: '',
1317 transform: ''
1318 });
1319};
1320
1321return 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
1372var console = window.console;
1373var jQuery = window.jQuery;
1374var noop = function() {};
1375
1376// -------------------------- Outlayer -------------------------- //
1377
1378// globally unique identifiers
1379var GUID = 0;
1380// internal store of all Outlayer intances
1381var instances = {};
1382
1383
1384/**
1385 * @param {Element, String} element
1386 * @param {Object} options
1387 * @constructor
1388 */
1389function 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
1423Outlayer.namespace = 'outlayer';
1424Outlayer.Item = Item;
1425
1426// default options
1427Outlayer.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
1448var proto = Outlayer.prototype;
1449// inherit EvEmitter
1450utils.extend( proto, EvEmitter.prototype );
1451
1452/**
1453 * set options
1454 * @param {Object} opts
1455 */
1456proto.option = function( opts ) {
1457 utils.extend( this.options, opts );
1458};
1459
1460/**
1461 * get backwards compatible option value, check old name
1462 */
1463proto._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
1469Outlayer.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
1480proto._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
1497proto.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 */
1508proto._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 */
1529proto._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 */
1537proto.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 */
1548proto.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
1563proto._init = proto.layout;
1564
1565/**
1566 * logic before any new layout
1567 */
1568proto._resetLayout = function() {
1569 this.getSize();
1570};
1571
1572
1573proto.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 */
1587proto._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 */
1609proto.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 */
1623proto._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 */
1634proto._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 */
1661proto._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 */
1674proto._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
1682proto.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 */
1699proto._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 */
1713proto._postLayout = function() {
1714 this.resizeContainer();
1715};
1716
1717proto.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 */
1735proto._getContainerSize = noop;
1736
1737/**
1738 * @param {Number} measure - size of width or height
1739 * @param {Boolean} isWidth
1740 */
1741proto._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 */
1764proto._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 */
1796proto.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 */
1824proto.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 */
1835proto.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 */
1846proto.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 */
1861proto.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 */
1879proto._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
1891proto._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
1902proto._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**/
1917proto._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 */
1924proto._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
1941proto.handleEvent = utils.handleEvent;
1942
1943/**
1944 * Bind layout to window resizing
1945 */
1946proto.bindResize = function() {
1947 window.addEventListener( 'resize', this );
1948 this.isResizeBound = true;
1949};
1950
1951/**
1952 * Unbind layout to window resizing
1953 */
1954proto.unbindResize = function() {
1955 window.removeEventListener( 'resize', this );
1956 this.isResizeBound = false;
1957};
1958
1959proto.onresize = function() {
1960 this.resize();
1961};
1962
1963utils.debounceMethod( Outlayer, 'onresize', 100 );
1964
1965proto.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 */
1979proto.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**/
1994proto.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 */
2007proto.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 */
2021proto.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 */
2043proto.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 */
2059proto.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 */
2075proto.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 */
2084proto.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 */
2095proto.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 */
2111proto.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 */
2128proto.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
2148proto.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 */
2178Outlayer.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 */
2191Outlayer.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
2220function 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
2234var msUnits = {
2235 ms: 1,
2236 s: 1000
2237};
2238
2239// munge time-like parameter into millisecond number
2240// '0.4s' -> 40
2241function 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
2259Outlayer.Item = Item;
2260
2261return 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