PluginProbe
Tagembed: Social Media Feeds and Customer Reviews Widget / 4.3
Tagembed: Social Media Feeds and Customer Reviews Widget v4.3
7.8 7.5 7.6 7.7 7.4 trunk 3.10 3.9 4.0 4.1 4.2 4.3 4.4 4.5 4.6 4.7 4.8 4.9 5.0 5.1 5.2 5.3 5.4 5.5 5.6 All 43 releases
tagembed-widget / assets / js / isotope.pkgd.js

isotope.pkgd.js in Tagembed: Social Media Feeds and Customer Reviews Widget 4.3, at assets/js/isotope.pkgd.js

3,564 lines 89.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /*!
2 * Isotope PACKAGED v3.0.6
3 *
4 * Licensed GPLv3 for open source use
5 * or Isotope Commercial License for commercial use
6 *
7 * https://isotope.metafizzy.co
8 * Copyright 2010-2018 Metafizzy
9 */
10
11 /**
12 * Bridget makes jQuery widgets
13 * v2.0.1
14 * MIT license
15 */
16
17 /* jshint browser: true, strict: true, undef: true, unused: true */
18
19 ( function( window, factory ) {
20 // universal module definition
21 /*jshint strict: false */ /* globals define, module, require */
22 if ( typeof define == 'function' && define.amd ) {
23 // AMD
24 define( 'jquery-bridget/jquery-bridget',[ 'jquery' ], function( jQuery ) {
25 return factory( window, jQuery );
26 });
27 } else if ( typeof module == 'object' && module.exports ) {
28 // CommonJS
29 module.exports = factory(
30 window,
31 require('jquery')
32 );
33 } else {
34 // browser global
35 window.jQueryBridget = factory(
36 window,
37 window.jQuery
38 );
39 }
40
41 }( window, function factory( window, jQuery ) {
42 'use strict';
43
44 // ----- utils ----- //
45
46 var arraySlice = Array.prototype.slice;
47
48 // helper function for logging errors
49 // $.error breaks jQuery chaining
50 var console = window.console;
51 var logError = typeof console == 'undefined' ? function() {} :
52 function( message ) {
53 console.error( message );
54 };
55
56 // ----- jQueryBridget ----- //
57
58 function jQueryBridget( namespace, PluginClass, $ ) {
59 $ = $ || jQuery || window.jQuery;
60 if ( !$ ) {
61 return;
62 }
63
64 // add option method -> $().plugin('option', {...})
65 if ( !PluginClass.prototype.option ) {
66 // option setter
67 PluginClass.prototype.option = function( opts ) {
68 // bail out if not an object
69 if ( !$.isPlainObject( opts ) ){
70 return;
71 }
72 this.options = $.extend( true, this.options, opts );
73 };
74 }
75
76 // make jQuery plugin
77 $.fn[ namespace ] = function( arg0 /*, arg1 */ ) {
78 if ( typeof arg0 == 'string' ) {
79 // method call $().plugin( 'methodName', { options } )
80 // shift arguments by 1
81 var args = arraySlice.call( arguments, 1 );
82 return methodCall( this, arg0, args );
83 }
84 // just $().plugin({ options })
85 plainCall( this, arg0 );
86 return this;
87 };
88
89 // $().plugin('methodName')
90 function methodCall( $elems, methodName, args ) {
91 var returnValue;
92 var pluginMethodStr = '$().' + namespace + '("' + methodName + '")';
93
94 $elems.each( function( i, elem ) {
95 // get instance
96 var instance = $.data( elem, namespace );
97 if ( !instance ) {
98 logError( namespace + ' not initialized. Cannot call methods, i.e. ' +
99 pluginMethodStr );
100 return;
101 }
102
103 var method = instance[ methodName ];
104 if ( !method || methodName.charAt(0) == '_' ) {
105 logError( pluginMethodStr + ' is not a valid method' );
106 return;
107 }
108
109 // apply method, get return value
110 var value = method.apply( instance, args );
111 // set return value if value is returned, use only first value
112 returnValue = returnValue === undefined ? value : returnValue;
113 });
114
115 return returnValue !== undefined ? returnValue : $elems;
116 }
117
118 function plainCall( $elems, options ) {
119 $elems.each( function( i, elem ) {
120 var instance = $.data( elem, namespace );
121 if ( instance ) {
122 // set options & init
123 instance.option( options );
124 instance._init();
125 } else {
126 // initialize new instance
127 instance = new PluginClass( elem, options );
128 $.data( elem, namespace, instance );
129 }
130 });
131 }
132
133 updateJQuery( $ );
134
135 }
136
137 // ----- updateJQuery ----- //
138
139 // set $.bridget for v1 backwards compatibility
140 function updateJQuery( $ ) {
141 if ( !$ || ( $ && $.bridget ) ) {
142 return;
143 }
144 $.bridget = jQueryBridget;
145 }
146
147 updateJQuery( jQuery || window.jQuery );
148
149 // ----- ----- //
150
151 return jQueryBridget;
152
153 }));
154
155 /**
156 * EvEmitter v1.1.0
157 * Lil' event emitter
158 * MIT License
159 */
160
161 /* jshint unused: true, undef: true, strict: true */
162
163 ( function( global, factory ) {
164 // universal module definition
165 /* jshint strict: false */ /* globals define, module, window */
166 if ( typeof define == 'function' && define.amd ) {
167 // AMD - RequireJS
168 define( 'ev-emitter/ev-emitter',factory );
169 } else if ( typeof module == 'object' && module.exports ) {
170 // CommonJS - Browserify, Webpack
171 module.exports = factory();
172 } else {
173 // Browser globals
174 global.EvEmitter = factory();
175 }
176
177 }( typeof window != 'undefined' ? window : this, function() {
178
179
180
181 function EvEmitter() {}
182
183 var proto = EvEmitter.prototype;
184
185 proto.on = function( eventName, listener ) {
186 if ( !eventName || !listener ) {
187 return;
188 }
189 // set events hash
190 var events = this._events = this._events || {};
191 // set listeners array
192 var listeners = events[ eventName ] = events[ eventName ] || [];
193 // only add once
194 if ( listeners.indexOf( listener ) == -1 ) {
195 listeners.push( listener );
196 }
197
198 return this;
199 };
200
201 proto.once = function( eventName, listener ) {
202 if ( !eventName || !listener ) {
203 return;
204 }
205 // add event
206 this.on( eventName, listener );
207 // set once flag
208 // set onceEvents hash
209 var onceEvents = this._onceEvents = this._onceEvents || {};
210 // set onceListeners object
211 var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {};
212 // set flag
213 onceListeners[ listener ] = true;
214
215 return this;
216 };
217
218 proto.off = function( eventName, listener ) {
219 var listeners = this._events && this._events[ eventName ];
220 if ( !listeners || !listeners.length ) {
221 return;
222 }
223 var index = listeners.indexOf( listener );
224 if ( index != -1 ) {
225 listeners.splice( index, 1 );
226 }
227
228 return this;
229 };
230
231 proto.emitEvent = function( eventName, args ) {
232 var listeners = this._events && this._events[ eventName ];
233 if ( !listeners || !listeners.length ) {
234 return;
235 }
236 // copy over to avoid interference if .off() in listener
237 listeners = listeners.slice(0);
238 args = args || [];
239 // once stuff
240 var onceListeners = this._onceEvents && this._onceEvents[ eventName ];
241
242 for ( var i=0; i < listeners.length; i++ ) {
243 var listener = listeners[i]
244 var isOnce = onceListeners && onceListeners[ listener ];
245 if ( isOnce ) {
246 // remove listener
247 // remove before trigger to prevent recursion
248 this.off( eventName, listener );
249 // unset once flag
250 delete onceListeners[ listener ];
251 }
252 // trigger listener
253 listener.apply( this, args );
254 }
255
256 return this;
257 };
258
259 proto.allOff = function() {
260 delete this._events;
261 delete this._onceEvents;
262 };
263
264 return EvEmitter;
265
266 }));
267
268 /*!
269 * getSize v2.0.3
270 * measure size of elements
271 * MIT license
272 */
273
274 /* jshint browser: true, strict: true, undef: true, unused: true */
275 /* globals console: false */
276
277 ( function( window, factory ) {
278 /* jshint strict: false */ /* globals define, module */
279 if ( typeof define == 'function' && define.amd ) {
280 // AMD
281 define( 'get-size/get-size',factory );
282 } else if ( typeof module == 'object' && module.exports ) {
283 // CommonJS
284 module.exports = factory();
285 } else {
286 // browser global
287 window.getSize = factory();
288 }
289
290 })( window, function factory() {
291 'use strict';
292
293 // -------------------------- helpers -------------------------- //
294
295 // get a number from a string, not a percentage
296 function getStyleSize( value ) {
297 var num = parseFloat( value );
298 // not a percent like '100%', and a number
299 var isValid = value.indexOf('%') == -1 && !isNaN( num );
300 return isValid && num;
301 }
302
303 function noop() {}
304
305 var logError = typeof console == 'undefined' ? noop :
306 function( message ) {
307 console.error( message );
308 };
309
310 // -------------------------- measurements -------------------------- //
311
312 var measurements = [
313 'paddingLeft',
314 'paddingRight',
315 'paddingTop',
316 'paddingBottom',
317 'marginLeft',
318 'marginRight',
319 'marginTop',
320 'marginBottom',
321 'borderLeftWidth',
322 'borderRightWidth',
323 'borderTopWidth',
324 'borderBottomWidth'
325 ];
326
327 var measurementsLength = measurements.length;
328
329 function getZeroSize() {
330 var size = {
331 width: 0,
332 height: 0,
333 innerWidth: 0,
334 innerHeight: 0,
335 outerWidth: 0,
336 outerHeight: 0
337 };
338 for ( var i=0; i < measurementsLength; i++ ) {
339 var measurement = measurements[i];
340 size[ measurement ] = 0;
341 }
342 return size;
343 }
344
345 // -------------------------- getStyle -------------------------- //
346
347 /**
348 * getStyle, get style of element, check for Firefox bug
349 * https://bugzilla.mozilla.org/show_bug.cgi?id=548397
350 */
351 function getStyle( elem ) {
352 var style = getComputedStyle( elem );
353 if ( !style ) {
354 logError( 'Style returned ' + style +
355 '. Are you running this code in a hidden iframe on Firefox? ' +
356 'See https://bit.ly/getsizebug1' );
357 }
358 return style;
359 }
360
361 // -------------------------- setup -------------------------- //
362
363 var isSetup = false;
364
365 var isBoxSizeOuter;
366
367 /**
368 * setup
369 * check isBoxSizerOuter
370 * do on first getSize() rather than on page load for Firefox bug
371 */
372 function setup() {
373 // setup once
374 if ( isSetup ) {
375 return;
376 }
377 isSetup = true;
378
379 // -------------------------- box sizing -------------------------- //
380
381 /**
382 * Chrome & Safari measure the outer-width on style.width on border-box elems
383 * IE11 & Firefox<29 measures the inner-width
384 */
385 var div = document.createElement('div');
386 div.style.width = '200px';
387 div.style.padding = '1px 2px 3px 4px';
388 div.style.borderStyle = 'solid';
389 div.style.borderWidth = '1px 2px 3px 4px';
390 div.style.boxSizing = 'border-box';
391
392 var body = document.body || document.documentElement;
393 body.appendChild( div );
394 var style = getStyle( div );
395 // round value for browser zoom. desandro/masonry#928
396 isBoxSizeOuter = Math.round( getStyleSize( style.width ) ) == 200;
397 getSize.isBoxSizeOuter = isBoxSizeOuter;
398
399 body.removeChild( div );
400 }
401
402 // -------------------------- getSize -------------------------- //
403
404 function getSize( elem ) {
405 setup();
406
407 // use querySeletor if elem is string
408 if ( typeof elem == 'string' ) {
409 elem = document.querySelector( elem );
410 }
411
412 // do not proceed on non-objects
413 if ( !elem || typeof elem != 'object' || !elem.nodeType ) {
414 return;
415 }
416
417 var style = getStyle( elem );
418
419 // if hidden, everything is 0
420 if ( style.display == 'none' ) {
421 return getZeroSize();
422 }
423
424 var size = {};
425 size.width = elem.offsetWidth;
426 size.height = elem.offsetHeight;
427
428 var isBorderBox = size.isBorderBox = style.boxSizing == 'border-box';
429
430 // get all measurements
431 for ( var i=0; i < measurementsLength; i++ ) {
432 var measurement = measurements[i];
433 var value = style[ measurement ];
434 var num = parseFloat( value );
435 // any 'auto', 'medium' value will be 0
436 size[ measurement ] = !isNaN( num ) ? num : 0;
437 }
438
439 var paddingWidth = size.paddingLeft + size.paddingRight;
440 var paddingHeight = size.paddingTop + size.paddingBottom;
441 var marginWidth = size.marginLeft + size.marginRight;
442 var marginHeight = size.marginTop + size.marginBottom;
443 var borderWidth = size.borderLeftWidth + size.borderRightWidth;
444 var borderHeight = size.borderTopWidth + size.borderBottomWidth;
445
446 var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter;
447
448 // overwrite width and height if we can get it from style
449 var styleWidth = getStyleSize( style.width );
450 if ( styleWidth !== false ) {
451 size.width = styleWidth +
452 // add padding and border unless it's already including it
453 ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth );
454 }
455
456 var styleHeight = getStyleSize( style.height );
457 if ( styleHeight !== false ) {
458 size.height = styleHeight +
459 // add padding and border unless it's already including it
460 ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight );
461 }
462
463 size.innerWidth = size.width - ( paddingWidth + borderWidth );
464 size.innerHeight = size.height - ( paddingHeight + borderHeight );
465
466 size.outerWidth = size.width + marginWidth;
467 size.outerHeight = size.height + marginHeight;
468
469 return size;
470 }
471
472 return getSize;
473
474 });
475
476 /**
477 * matchesSelector v2.0.2
478 * matchesSelector( element, '.selector' )
479 * MIT license
480 */
481
482 /*jshint browser: true, strict: true, undef: true, unused: true */
483
484 ( function( window, factory ) {
485 /*global define: false, module: false */
486 'use strict';
487 // universal module definition
488 if ( typeof define == 'function' && define.amd ) {
489 // AMD
490 define( 'desandro-matches-selector/matches-selector',factory );
491 } else if ( typeof module == 'object' && module.exports ) {
492 // CommonJS
493 module.exports = factory();
494 } else {
495 // browser global
496 window.matchesSelector = factory();
497 }
498
499 }( window, function factory() {
500 'use strict';
501
502 var matchesMethod = ( function() {
503 var ElemProto = window.Element.prototype;
504 // check for the standard method name first
505 if ( ElemProto.matches ) {
506 return 'matches';
507 }
508 // check un-prefixed
509 if ( ElemProto.matchesSelector ) {
510 return 'matchesSelector';
511 }
512 // check vendor prefixes
513 var prefixes = [ 'webkit', 'moz', 'ms', 'o' ];
514
515 for ( var i=0; i < prefixes.length; i++ ) {
516 var prefix = prefixes[i];
517 var method = prefix + 'MatchesSelector';
518 if ( ElemProto[ method ] ) {
519 return method;
520 }
521 }
522 })();
523
524 return function matchesSelector( elem, selector ) {
525 return elem[ matchesMethod ]( selector );
526 };
527
528 }));
529
530 /**
531 * Fizzy UI utils v2.0.7
532 * MIT license
533 */
534
535 /*jshint browser: true, undef: true, unused: true, strict: true */
536
537 ( function( window, factory ) {
538 // universal module definition
539 /*jshint strict: false */ /*globals define, module, require */
540
541 if ( typeof define == 'function' && define.amd ) {
542 // AMD
543 define( 'fizzy-ui-utils/utils',[
544 'desandro-matches-selector/matches-selector'
545 ], function( matchesSelector ) {
546 return factory( window, matchesSelector );
547 });
548 } else if ( typeof module == 'object' && module.exports ) {
549 // CommonJS
550 module.exports = factory(
551 window,
552 require('desandro-matches-selector')
553 );
554 } else {
555 // browser global
556 window.fizzyUIUtils = factory(
557 window,
558 window.matchesSelector
559 );
560 }
561
562 }( window, function factory( window, matchesSelector ) {
563
564
565
566 var utils = {};
567
568 // ----- extend ----- //
569
570 // extends objects
571 utils.extend = function( a, b ) {
572 for ( var prop in b ) {
573 a[ prop ] = b[ prop ];
574 }
575 return a;
576 };
577
578 // ----- modulo ----- //
579
580 utils.modulo = function( num, div ) {
581 return ( ( num % div ) + div ) % div;
582 };
583
584 // ----- makeArray ----- //
585
586 var arraySlice = Array.prototype.slice;
587
588 // turn element or nodeList into an array
589 utils.makeArray = function( obj ) {
590 if ( Array.isArray( obj ) ) {
591 // use object if already an array
592 return obj;
593 }
594 // return empty array if undefined or null. #6
595 if ( obj === null || obj === undefined ) {
596 return [];
597 }
598
599 var isArrayLike = typeof obj == 'object' && typeof obj.length == 'number';
600 if ( isArrayLike ) {
601 // convert nodeList to array
602 return arraySlice.call( obj );
603 }
604
605 // array of single index
606 return [ obj ];
607 };
608
609 // ----- removeFrom ----- //
610
611 utils.removeFrom = function( ary, obj ) {
612 var index = ary.indexOf( obj );
613 if ( index != -1 ) {
614 ary.splice( index, 1 );
615 }
616 };
617
618 // ----- getParent ----- //
619
620 utils.getParent = function( elem, selector ) {
621 while ( elem.parentNode && elem != document.body ) {
622 elem = elem.parentNode;
623 if ( matchesSelector( elem, selector ) ) {
624 return elem;
625 }
626 }
627 };
628
629 // ----- getQueryElement ----- //
630
631 // use element as selector string
632 utils.getQueryElement = function( elem ) {
633 if ( typeof elem == 'string' ) {
634 return document.querySelector( elem );
635 }
636 return elem;
637 };
638
639 // ----- handleEvent ----- //
640
641 // enable .ontype to trigger from .addEventListener( elem, 'type' )
642 utils.handleEvent = function( event ) {
643 var method = 'on' + event.type;
644 if ( this[ method ] ) {
645 this[ method ]( event );
646 }
647 };
648
649 // ----- filterFindElements ----- //
650
651 utils.filterFindElements = function( elems, selector ) {
652 // make array of elems
653 elems = utils.makeArray( elems );
654 var ffElems = [];
655
656 elems.forEach( function( elem ) {
657 // check that elem is an actual element
658 if ( !( elem instanceof HTMLElement ) ) {
659 return;
660 }
661 // add elem if no selector
662 if ( !selector ) {
663 ffElems.push( elem );
664 return;
665 }
666 // filter & find items if we have a selector
667 // filter
668 if ( matchesSelector( elem, selector ) ) {
669 ffElems.push( elem );
670 }
671 // find children
672 var childElems = elem.querySelectorAll( selector );
673 // concat childElems to filterFound array
674 for ( var i=0; i < childElems.length; i++ ) {
675 ffElems.push( childElems[i] );
676 }
677 });
678
679 return ffElems;
680 };
681
682 // ----- debounceMethod ----- //
683
684 utils.debounceMethod = function( _class, methodName, threshold ) {
685 threshold = threshold || 100;
686 // original method
687 var method = _class.prototype[ methodName ];
688 var timeoutName = methodName + 'Timeout';
689
690 _class.prototype[ methodName ] = function() {
691 var timeout = this[ timeoutName ];
692 clearTimeout( timeout );
693
694 var args = arguments;
695 var _this = this;
696 this[ timeoutName ] = setTimeout( function() {
697 method.apply( _this, args );
698 delete _this[ timeoutName ];
699 }, threshold );
700 };
701 };
702
703 // ----- docReady ----- //
704
705 utils.docReady = function( callback ) {
706 var readyState = document.readyState;
707 if ( readyState == 'complete' || readyState == 'interactive' ) {
708 // do async to allow for other scripts to run. metafizzy/flickity#441
709 setTimeout( callback );
710 } else {
711 document.addEventListener( 'DOMContentLoaded', callback );
712 }
713 };
714
715 // ----- htmlInit ----- //
716
717 // http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
718 utils.toDashed = function( str ) {
719 return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
720 return $1 + '-' + $2;
721 }).toLowerCase();
722 };
723
724 var console = window.console;
725 /**
726 * allow user to initialize classes via [data-namespace] or .js-namespace class
727 * htmlInit( Widget, 'widgetName' )
728 * options are parsed from data-namespace-options
729 */
730 utils.htmlInit = function( WidgetClass, namespace ) {
731 utils.docReady( function() {
732 var dashedNamespace = utils.toDashed( namespace );
733 var dataAttr = 'data-' + dashedNamespace;
734 var dataAttrElems = document.querySelectorAll( '[' + dataAttr + ']' );
735 var jsDashElems = document.querySelectorAll( '.js-' + dashedNamespace );
736 var elems = utils.makeArray( dataAttrElems )
737 .concat( utils.makeArray( jsDashElems ) );
738 var dataOptionsAttr = dataAttr + '-options';
739 var jQuery = window.jQuery;
740
741 elems.forEach( function( elem ) {
742 var attr = elem.getAttribute( dataAttr ) ||
743 elem.getAttribute( dataOptionsAttr );
744 var options;
745 try {
746 options = attr && JSON.parse( attr );
747 } catch ( error ) {
748 // log error, do not initialize
749 if ( console ) {
750 console.error( 'Error parsing ' + dataAttr + ' on ' + elem.className +
751 ': ' + error );
752 }
753 return;
754 }
755 // initialize
756 var instance = new WidgetClass( elem, options );
757 // make available via $().data('namespace')
758 if ( jQuery ) {
759 jQuery.data( elem, namespace, instance );
760 }
761 });
762
763 });
764 };
765
766 // ----- ----- //
767
768 return utils;
769
770 }));
771
772 /**
773 * Outlayer Item
774 */
775
776 ( function( window, factory ) {
777 // universal module definition
778 /* jshint strict: false */ /* globals define, module, require */
779 if ( typeof define == 'function' && define.amd ) {
780 // AMD - RequireJS
781 define( 'outlayer/item',[
782 'ev-emitter/ev-emitter',
783 'get-size/get-size'
784 ],
785 factory
786 );
787 } else if ( typeof module == 'object' && module.exports ) {
788 // CommonJS - Browserify, Webpack
789 module.exports = factory(
790 require('ev-emitter'),
791 require('get-size')
792 );
793 } else {
794 // browser global
795 window.Outlayer = {};
796 window.Outlayer.Item = factory(
797 window.EvEmitter,
798 window.getSize
799 );
800 }
801
802 }( window, function factory( EvEmitter, getSize ) {
803 'use strict';
804
805 // ----- helpers ----- //
806
807 function isEmptyObj( obj ) {
808 for ( var prop in obj ) {
809 return false;
810 }
811 prop = null;
812 return true;
813 }
814
815 // -------------------------- CSS3 support -------------------------- //
816
817
818 var docElemStyle = document.documentElement.style;
819
820 var transitionProperty = typeof docElemStyle.transition == 'string' ?
821 'transition' : 'WebkitTransition';
822 var transformProperty = typeof docElemStyle.transform == 'string' ?
823 'transform' : 'WebkitTransform';
824
825 var transitionEndEvent = {
826 WebkitTransition: 'webkitTransitionEnd',
827 transition: 'transitionend'
828 }[ transitionProperty ];
829
830 // cache all vendor properties that could have vendor prefix
831 var vendorProperties = {
832 transform: transformProperty,
833 transition: transitionProperty,
834 transitionDuration: transitionProperty + 'Duration',
835 transitionProperty: transitionProperty + 'Property',
836 transitionDelay: transitionProperty + 'Delay'
837 };
838
839 // -------------------------- Item -------------------------- //
840
841 function Item( element, layout ) {
842 if ( !element ) {
843 return;
844 }
845
846 this.element = element;
847 // parent layout class, i.e. Masonry, Isotope, or Packery
848 this.layout = layout;
849 this.position = {
850 x: 0,
851 y: 0
852 };
853
854 this._create();
855 }
856
857 // inherit EvEmitter
858 var proto = Item.prototype = Object.create( EvEmitter.prototype );
859 proto.constructor = Item;
860
861 proto._create = function() {
862 // transition objects
863 this._transn = {
864 ingProperties: {},
865 clean: {},
866 onEnd: {}
867 };
868
869 this.css({
870 position: 'absolute'
871 });
872 };
873
874 // trigger specified handler for event type
875 proto.handleEvent = function( event ) {
876 var method = 'on' + event.type;
877 if ( this[ method ] ) {
878 this[ method ]( event );
879 }
880 };
881
882 proto.getSize = function() {
883 this.size = getSize( this.element );
884 };
885
886 /**
887 * apply CSS styles to element
888 * @param {Object} style
889 */
890 proto.css = function( style ) {
891 var elemStyle = this.element.style;
892
893 for ( var prop in style ) {
894 // use vendor property if available
895 var supportedProp = vendorProperties[ prop ] || prop;
896 elemStyle[ supportedProp ] = style[ prop ];
897 }
898 };
899
900 // measure position, and sets it
901 proto.getPosition = function() {
902 var style = getComputedStyle( this.element );
903 var isOriginLeft = this.layout._getOption('originLeft');
904 var isOriginTop = this.layout._getOption('originTop');
905 var xValue = style[ isOriginLeft ? 'left' : 'right' ];
906 var yValue = style[ isOriginTop ? 'top' : 'bottom' ];
907 var x = parseFloat( xValue );
908 var y = parseFloat( yValue );
909 // convert percent to pixels
910 var layoutSize = this.layout.size;
911 if ( xValue.indexOf('%') != -1 ) {
912 x = ( x / 100 ) * layoutSize.width;
913 }
914 if ( yValue.indexOf('%') != -1 ) {
915 y = ( y / 100 ) * layoutSize.height;
916 }
917 // clean up 'auto' or other non-integer values
918 x = isNaN( x ) ? 0 : x;
919 y = isNaN( y ) ? 0 : y;
920 // remove padding from measurement
921 x -= isOriginLeft ? layoutSize.paddingLeft : layoutSize.paddingRight;
922 y -= isOriginTop ? layoutSize.paddingTop : layoutSize.paddingBottom;
923
924 this.position.x = x;
925 this.position.y = y;
926 };
927
928 // set settled position, apply padding
929 proto.layoutPosition = function() {
930 var layoutSize = this.layout.size;
931 var style = {};
932 var isOriginLeft = this.layout._getOption('originLeft');
933 var isOriginTop = this.layout._getOption('originTop');
934
935 // x
936 var xPadding = isOriginLeft ? 'paddingLeft' : 'paddingRight';
937 var xProperty = isOriginLeft ? 'left' : 'right';
938 var xResetProperty = isOriginLeft ? 'right' : 'left';
939
940 var x = this.position.x + layoutSize[ xPadding ];
941 // set in percentage or pixels
942 style[ xProperty ] = this.getXValue( x );
943 // reset other property
944 style[ xResetProperty ] = '';
945
946 // y
947 var yPadding = isOriginTop ? 'paddingTop' : 'paddingBottom';
948 var yProperty = isOriginTop ? 'top' : 'bottom';
949 var yResetProperty = isOriginTop ? 'bottom' : 'top';
950
951 var y = this.position.y + layoutSize[ yPadding ];
952 // set in percentage or pixels
953 style[ yProperty ] = this.getYValue( y );
954 // reset other property
955 style[ yResetProperty ] = '';
956
957 this.css( style );
958 this.emitEvent( 'layout', [ this ] );
959 };
960
961 proto.getXValue = function( x ) {
962 var isHorizontal = this.layout._getOption('horizontal');
963 return this.layout.options.percentPosition && !isHorizontal ?
964 ( ( x / this.layout.size.width ) * 100 ) + '%' : x + 'px';
965 };
966
967 proto.getYValue = function( y ) {
968 var isHorizontal = this.layout._getOption('horizontal');
969 return this.layout.options.percentPosition && isHorizontal ?
970 ( ( y / this.layout.size.height ) * 100 ) + '%' : y + 'px';
971 };
972
973 proto._transitionTo = function( x, y ) {
974 this.getPosition();
975 // get current x & y from top/left
976 var curX = this.position.x;
977 var curY = this.position.y;
978
979 var didNotMove = x == this.position.x && y == this.position.y;
980
981 // save end position
982 this.setPosition( x, y );
983
984 // if did not move and not transitioning, just go to layout
985 if ( didNotMove && !this.isTransitioning ) {
986 this.layoutPosition();
987 return;
988 }
989
990 var transX = x - curX;
991 var transY = y - curY;
992 var transitionStyle = {};
993 transitionStyle.transform = this.getTranslate( transX, transY );
994
995 this.transition({
996 to: transitionStyle,
997 onTransitionEnd: {
998 transform: this.layoutPosition
999 },
1000 isCleaning: true
1001 });
1002 };
1003
1004 proto.getTranslate = function( x, y ) {
1005 // flip cooridinates if origin on right or bottom
1006 var isOriginLeft = this.layout._getOption('originLeft');
1007 var isOriginTop = this.layout._getOption('originTop');
1008 x = isOriginLeft ? x : -x;
1009 y = isOriginTop ? y : -y;
1010 return 'translate3d(' + x + 'px, ' + y + 'px, 0)';
1011 };
1012
1013 // non transition + transform support
1014 proto.goTo = function( x, y ) {
1015 this.setPosition( x, y );
1016 this.layoutPosition();
1017 };
1018
1019 proto.moveTo = proto._transitionTo;
1020
1021 proto.setPosition = function( x, y ) {
1022 this.position.x = parseFloat( x );
1023 this.position.y = parseFloat( y );
1024 };
1025
1026 // ----- transition ----- //
1027
1028 /**
1029 * @param {Object} style - CSS
1030 * @param {Function} onTransitionEnd
1031 */
1032
1033 // non transition, just trigger callback
1034 proto._nonTransition = function( args ) {
1035 this.css( args.to );
1036 if ( args.isCleaning ) {
1037 this._removeStyles( args.to );
1038 }
1039 for ( var prop in args.onTransitionEnd ) {
1040 args.onTransitionEnd[ prop ].call( this );
1041 }
1042 };
1043
1044 /**
1045 * proper transition
1046 * @param {Object} args - arguments
1047 * @param {Object} to - style to transition to
1048 * @param {Object} from - style to start transition from
1049 * @param {Boolean} isCleaning - removes transition styles after transition
1050 * @param {Function} onTransitionEnd - callback
1051 */
1052 proto.transition = function( args ) {
1053 // redirect to nonTransition if no transition duration
1054 if ( !parseFloat( this.layout.options.transitionDuration ) ) {
1055 this._nonTransition( args );
1056 return;
1057 }
1058
1059 var _transition = this._transn;
1060 // keep track of onTransitionEnd callback by css property
1061 for ( var prop in args.onTransitionEnd ) {
1062 _transition.onEnd[ prop ] = args.onTransitionEnd[ prop ];
1063 }
1064 // keep track of properties that are transitioning
1065 for ( prop in args.to ) {
1066 _transition.ingProperties[ prop ] = true;
1067 // keep track of properties to clean up when transition is done
1068 if ( args.isCleaning ) {
1069 _transition.clean[ prop ] = true;
1070 }
1071 }
1072
1073 // set from styles
1074 if ( args.from ) {
1075 this.css( args.from );
1076 // force redraw. http://blog.alexmaccaw.com/css-transitions
1077 var h = this.element.offsetHeight;
1078 // hack for JSHint to hush about unused var
1079 h = null;
1080 }
1081 // enable transition
1082 this.enableTransition( args.to );
1083 // set styles that are transitioning
1084 this.css( args.to );
1085
1086 this.isTransitioning = true;
1087
1088 };
1089
1090 // dash before all cap letters, including first for
1091 // WebkitTransform => -webkit-transform
1092 function toDashedAll( str ) {
1093 return str.replace( /([A-Z])/g, function( $1 ) {
1094 return '-' + $1.toLowerCase();
1095 });
1096 }
1097
1098 var transitionProps = 'opacity,' + toDashedAll( transformProperty );
1099
1100 proto.enableTransition = function(/* style */) {
1101 // HACK changing transitionProperty during a transition
1102 // will cause transition to jump
1103 if ( this.isTransitioning ) {
1104 return;
1105 }
1106
1107 // make `transition: foo, bar, baz` from style object
1108 // HACK un-comment this when enableTransition can work
1109 // while a transition is happening
1110 // var transitionValues = [];
1111 // for ( var prop in style ) {
1112 // // dash-ify camelCased properties like WebkitTransition
1113 // prop = vendorProperties[ prop ] || prop;
1114 // transitionValues.push( toDashedAll( prop ) );
1115 // }
1116 // munge number to millisecond, to match stagger
1117 var duration = this.layout.options.transitionDuration;
1118 duration = typeof duration == 'number' ? duration + 'ms' : duration;
1119 // enable transition styles
1120 this.css({
1121 transitionProperty: transitionProps,
1122 transitionDuration: duration,
1123 transitionDelay: this.staggerDelay || 0
1124 });
1125 // listen for transition end event
1126 this.element.addEventListener( transitionEndEvent, this, false );
1127 };
1128
1129 // ----- events ----- //
1130
1131 proto.onwebkitTransitionEnd = function( event ) {
1132 this.ontransitionend( event );
1133 };
1134
1135 proto.onotransitionend = function( event ) {
1136 this.ontransitionend( event );
1137 };
1138
1139 // properties that I munge to make my life easier
1140 var dashedVendorProperties = {
1141 '-webkit-transform': 'transform'
1142 };
1143
1144 proto.ontransitionend = function( event ) {
1145 // disregard bubbled events from children
1146 if ( event.target !== this.element ) {
1147 return;
1148 }
1149 var _transition = this._transn;
1150 // get property name of transitioned property, convert to prefix-free
1151 var propertyName = dashedVendorProperties[ event.propertyName ] || event.propertyName;
1152
1153 // remove property that has completed transitioning
1154 delete _transition.ingProperties[ propertyName ];
1155 // check if any properties are still transitioning
1156 if ( isEmptyObj( _transition.ingProperties ) ) {
1157 // all properties have completed transitioning
1158 this.disableTransition();
1159 }
1160 // clean style
1161 if ( propertyName in _transition.clean ) {
1162 // clean up style
1163 this.element.style[ event.propertyName ] = '';
1164 delete _transition.clean[ propertyName ];
1165 }
1166 // trigger onTransitionEnd callback
1167 if ( propertyName in _transition.onEnd ) {
1168 var onTransitionEnd = _transition.onEnd[ propertyName ];
1169 onTransitionEnd.call( this );
1170 delete _transition.onEnd[ propertyName ];
1171 }
1172
1173 this.emitEvent( 'transitionEnd', [ this ] );
1174 };
1175
1176 proto.disableTransition = function() {
1177 this.removeTransitionStyles();
1178 this.element.removeEventListener( transitionEndEvent, this, false );
1179 this.isTransitioning = false;
1180 };
1181
1182 /**
1183 * removes style property from element
1184 * @param {Object} style
1185 **/
1186 proto._removeStyles = function( style ) {
1187 // clean up transition styles
1188 var cleanStyle = {};
1189 for ( var prop in style ) {
1190 cleanStyle[ prop ] = '';
1191 }
1192 this.css( cleanStyle );
1193 };
1194
1195 var cleanTransitionStyle = {
1196 transitionProperty: '',
1197 transitionDuration: '',
1198 transitionDelay: ''
1199 };
1200
1201 proto.removeTransitionStyles = function() {
1202 // remove transition
1203 this.css( cleanTransitionStyle );
1204 };
1205
1206 // ----- stagger ----- //
1207
1208 proto.stagger = function( delay ) {
1209 delay = isNaN( delay ) ? 0 : delay;
1210 this.staggerDelay = delay + 'ms';
1211 };
1212
1213 // ----- show/hide/remove ----- //
1214
1215 // remove element from DOM
1216 proto.removeElem = function() {
1217 this.element.parentNode.removeChild( this.element );
1218 // remove display: none
1219 this.css({ display: '' });
1220 this.emitEvent( 'remove', [ this ] );
1221 };
1222
1223 proto.remove = function() {
1224 // just remove element if no transition support or no transition
1225 if ( !transitionProperty || !parseFloat( this.layout.options.transitionDuration ) ) {
1226 this.removeElem();
1227 return;
1228 }
1229
1230 // start transition
1231 this.once( 'transitionEnd', function() {
1232 this.removeElem();
1233 });
1234 this.hide();
1235 };
1236
1237 proto.reveal = function() {
1238 delete this.isHidden;
1239 // remove display: none
1240 this.css({ display: '' });
1241
1242 var options = this.layout.options;
1243
1244 var onTransitionEnd = {};
1245 var transitionEndProperty = this.getHideRevealTransitionEndProperty('visibleStyle');
1246 onTransitionEnd[ transitionEndProperty ] = this.onRevealTransitionEnd;
1247
1248 this.transition({
1249 from: options.hiddenStyle,
1250 to: options.visibleStyle,
1251 isCleaning: true,
1252 onTransitionEnd: onTransitionEnd
1253 });
1254 };
1255
1256 proto.onRevealTransitionEnd = function() {
1257 // check if still visible
1258 // during transition, item may have been hidden
1259 if ( !this.isHidden ) {
1260 this.emitEvent('reveal');
1261 }
1262 };
1263
1264 /**
1265 * get style property use for hide/reveal transition end
1266 * @param {String} styleProperty - hiddenStyle/visibleStyle
1267 * @returns {String}
1268 */
1269 proto.getHideRevealTransitionEndProperty = function( styleProperty ) {
1270 var optionStyle = this.layout.options[ styleProperty ];
1271 // use opacity
1272 if ( optionStyle.opacity ) {
1273 return 'opacity';
1274 }
1275 // get first property
1276 for ( var prop in optionStyle ) {
1277 return prop;
1278 }
1279 };
1280
1281 proto.hide = function() {
1282 // set flag
1283 this.isHidden = true;
1284 // remove display: none
1285 this.css({ display: '' });
1286
1287 var options = this.layout.options;
1288
1289 var onTransitionEnd = {};
1290 var transitionEndProperty = this.getHideRevealTransitionEndProperty('hiddenStyle');
1291 onTransitionEnd[ transitionEndProperty ] = this.onHideTransitionEnd;
1292
1293 this.transition({
1294 from: options.visibleStyle,
1295 to: options.hiddenStyle,
1296 // keep hidden stuff hidden
1297 isCleaning: true,
1298 onTransitionEnd: onTransitionEnd
1299 });
1300 };
1301
1302 proto.onHideTransitionEnd = function() {
1303 // check if still hidden
1304 // during transition, item may have been un-hidden
1305 if ( this.isHidden ) {
1306 this.css({ display: 'none' });
1307 this.emitEvent('hide');
1308 }
1309 };
1310
1311 proto.destroy = function() {
1312 this.css({
1313 position: '',
1314 left: '',
1315 right: '',
1316 top: '',
1317 bottom: '',
1318 transition: '',
1319 transform: ''
1320 });
1321 };
1322
1323 return Item;
1324
1325 }));
1326
1327 /*!
1328 * Outlayer v2.1.1
1329 * the brains and guts of a layout library
1330 * MIT license
1331 */
1332
1333 ( function( window, factory ) {
1334 'use strict';
1335 // universal module definition
1336 /* jshint strict: false */ /* globals define, module, require */
1337 if ( typeof define == 'function' && define.amd ) {
1338 // AMD - RequireJS
1339 define( 'outlayer/outlayer',[
1340 'ev-emitter/ev-emitter',
1341 'get-size/get-size',
1342 'fizzy-ui-utils/utils',
1343 './item'
1344 ],
1345 function( EvEmitter, getSize, utils, Item ) {
1346 return factory( window, EvEmitter, getSize, utils, Item);
1347 }
1348 );
1349 } else if ( typeof module == 'object' && module.exports ) {
1350 // CommonJS - Browserify, Webpack
1351 module.exports = factory(
1352 window,
1353 require('ev-emitter'),
1354 require('get-size'),
1355 require('fizzy-ui-utils'),
1356 require('./item')
1357 );
1358 } else {
1359 // browser global
1360 window.Outlayer = factory(
1361 window,
1362 window.EvEmitter,
1363 window.getSize,
1364 window.fizzyUIUtils,
1365 window.Outlayer.Item
1366 );
1367 }
1368
1369 }( window, function factory( window, EvEmitter, getSize, utils, Item ) {
1370 'use strict';
1371
1372 // ----- vars ----- //
1373
1374 var console = window.console;
1375 var jQuery = window.jQuery;
1376 var noop = function() {};
1377
1378 // -------------------------- Outlayer -------------------------- //
1379
1380 // globally unique identifiers
1381 var GUID = 0;
1382 // internal store of all Outlayer intances
1383 var instances = {};
1384
1385
1386 /**
1387 * @param {Element, String} element
1388 * @param {Object} options
1389 * @constructor
1390 */
1391 function Outlayer( element, options ) {
1392 var queryElement = utils.getQueryElement( element );
1393 if ( !queryElement ) {
1394 if ( console ) {
1395 console.error( 'Bad element for ' + this.constructor.namespace +
1396 ': ' + ( queryElement || element ) );
1397 }
1398 return;
1399 }
1400 this.element = queryElement;
1401 // add jQuery
1402 if ( jQuery ) {
1403 this.$element = jQuery( this.element );
1404 }
1405
1406 // options
1407 this.options = utils.extend( {}, this.constructor.defaults );
1408 this.option( options );
1409
1410 // add id for Outlayer.getFromElement
1411 var id = ++GUID;
1412 this.element.outlayerGUID = id; // expando
1413 instances[ id ] = this; // associate via id
1414
1415 // kick it off
1416 this._create();
1417
1418 var isInitLayout = this._getOption('initLayout');
1419 if ( isInitLayout ) {
1420 this.layout();
1421 }
1422 }
1423
1424 // settings are for internal use only
1425 Outlayer.namespace = 'outlayer';
1426 Outlayer.Item = Item;
1427
1428 // default options
1429 Outlayer.defaults = {
1430 containerStyle: {
1431 position: 'relative'
1432 },
1433 initLayout: true,
1434 originLeft: true,
1435 originTop: true,
1436 resize: true,
1437 resizeContainer: true,
1438 // item options
1439 transitionDuration: '0.4s',
1440 hiddenStyle: {
1441 opacity: 0,
1442 transform: 'scale(0.001)'
1443 },
1444 visibleStyle: {
1445 opacity: 1,
1446 transform: 'scale(1)'
1447 }
1448 };
1449
1450 var proto = Outlayer.prototype;
1451 // inherit EvEmitter
1452 utils.extend( proto, EvEmitter.prototype );
1453
1454 /**
1455 * set options
1456 * @param {Object} opts
1457 */
1458 proto.option = function( opts ) {
1459 utils.extend( this.options, opts );
1460 };
1461
1462 /**
1463 * get backwards compatible option value, check old name
1464 */
1465 proto._getOption = function( option ) {
1466 var oldOption = this.constructor.compatOptions[ option ];
1467 return oldOption && this.options[ oldOption ] !== undefined ?
1468 this.options[ oldOption ] : this.options[ option ];
1469 };
1470
1471 Outlayer.compatOptions = {
1472 // currentName: oldName
1473 initLayout: 'isInitLayout',
1474 horizontal: 'isHorizontal',
1475 layoutInstant: 'isLayoutInstant',
1476 originLeft: 'isOriginLeft',
1477 originTop: 'isOriginTop',
1478 resize: 'isResizeBound',
1479 resizeContainer: 'isResizingContainer'
1480 };
1481
1482 proto._create = function() {
1483 // get items from children
1484 this.reloadItems();
1485 // elements that affect layout, but are not laid out
1486 this.stamps = [];
1487 this.stamp( this.options.stamp );
1488 // set container style
1489 utils.extend( this.element.style, this.options.containerStyle );
1490
1491 // bind resize method
1492 var canBindResize = this._getOption('resize');
1493 if ( canBindResize ) {
1494 this.bindResize();
1495 }
1496 };
1497
1498 // goes through all children again and gets bricks in proper order
1499 proto.reloadItems = function() {
1500 // collection of item elements
1501 this.items = this._itemize( this.element.children );
1502 };
1503
1504
1505 /**
1506 * turn elements into Outlayer.Items to be used in layout
1507 * @param {Array or NodeList or HTMLElement} elems
1508 * @returns {Array} items - collection of new Outlayer Items
1509 */
1510 proto._itemize = function( elems ) {
1511
1512 var itemElems = this._filterFindItemElements( elems );
1513 var Item = this.constructor.Item;
1514
1515 // create new Outlayer Items for collection
1516 var items = [];
1517 for ( var i=0; i < itemElems.length; i++ ) {
1518 var elem = itemElems[i];
1519 var item = new Item( elem, this );
1520 items.push( item );
1521 }
1522
1523 return items;
1524 };
1525
1526 /**
1527 * get item elements to be used in layout
1528 * @param {Array or NodeList or HTMLElement} elems
1529 * @returns {Array} items - item elements
1530 */
1531 proto._filterFindItemElements = function( elems ) {
1532 return utils.filterFindElements( elems, this.options.itemSelector );
1533 };
1534
1535 /**
1536 * getter method for getting item elements
1537 * @returns {Array} elems - collection of item elements
1538 */
1539 proto.getItemElements = function() {
1540 return this.items.map( function( item ) {
1541 return item.element;
1542 });
1543 };
1544
1545 // ----- init & layout ----- //
1546
1547 /**
1548 * lays out all items
1549 */
1550 proto.layout = function() {
1551 this._resetLayout();
1552 this._manageStamps();
1553
1554 // don't animate first layout
1555 var layoutInstant = this._getOption('layoutInstant');
1556 var isInstant = layoutInstant !== undefined ?
1557 layoutInstant : !this._isLayoutInited;
1558 this.layoutItems( this.items, isInstant );
1559
1560 // flag for initalized
1561 this._isLayoutInited = true;
1562 };
1563
1564 // _init is alias for layout
1565 proto._init = proto.layout;
1566
1567 /**
1568 * logic before any new layout
1569 */
1570 proto._resetLayout = function() {
1571 this.getSize();
1572 };
1573
1574
1575 proto.getSize = function() {
1576 this.size = getSize( this.element );
1577 };
1578
1579 /**
1580 * get measurement from option, for columnWidth, rowHeight, gutter
1581 * if option is String -> get element from selector string, & get size of element
1582 * if option is Element -> get size of element
1583 * else use option as a number
1584 *
1585 * @param {String} measurement
1586 * @param {String} size - width or height
1587 * @private
1588 */
1589 proto._getMeasurement = function( measurement, size ) {
1590 var option = this.options[ measurement ];
1591 var elem;
1592 if ( !option ) {
1593 // default to 0
1594 this[ measurement ] = 0;
1595 } else {
1596 // use option as an element
1597 if ( typeof option == 'string' ) {
1598 elem = this.element.querySelector( option );
1599 } else if ( option instanceof HTMLElement ) {
1600 elem = option;
1601 }
1602 // use size of element, if element
1603 this[ measurement ] = elem ? getSize( elem )[ size ] : option;
1604 }
1605 };
1606
1607 /**
1608 * layout a collection of item elements
1609 * @api public
1610 */
1611 proto.layoutItems = function( items, isInstant ) {
1612 items = this._getItemsForLayout( items );
1613
1614 this._layoutItems( items, isInstant );
1615
1616 this._postLayout();
1617 };
1618
1619 /**
1620 * get the items to be laid out
1621 * you may want to skip over some items
1622 * @param {Array} items
1623 * @returns {Array} items
1624 */
1625 proto._getItemsForLayout = function( items ) {
1626 return items.filter( function( item ) {
1627 return !item.isIgnored;
1628 });
1629 };
1630
1631 /**
1632 * layout items
1633 * @param {Array} items
1634 * @param {Boolean} isInstant
1635 */
1636 proto._layoutItems = function( items, isInstant ) {
1637 this._emitCompleteOnItems( 'layout', items );
1638
1639 if ( !items || !items.length ) {
1640 // no items, emit event with empty array
1641 return;
1642 }
1643
1644 var queue = [];
1645
1646 items.forEach( function( item ) {
1647 // get x/y object from method
1648 var position = this._getItemLayoutPosition( item );
1649 // enqueue
1650 position.item = item;
1651 position.isInstant = isInstant || item.isLayoutInstant;
1652 queue.push( position );
1653 }, this );
1654
1655 this._processLayoutQueue( queue );
1656 };
1657
1658 /**
1659 * get item layout position
1660 * @param {Outlayer.Item} item
1661 * @returns {Object} x and y position
1662 */
1663 proto._getItemLayoutPosition = function( /* item */ ) {
1664 return {
1665 x: 0,
1666 y: 0
1667 };
1668 };
1669
1670 /**
1671 * iterate over array and position each item
1672 * Reason being - separating this logic prevents 'layout invalidation'
1673 * thx @paul_irish
1674 * @param {Array} queue
1675 */
1676 proto._processLayoutQueue = function( queue ) {
1677 this.updateStagger();
1678 queue.forEach( function( obj, i ) {
1679 this._positionItem( obj.item, obj.x, obj.y, obj.isInstant, i );
1680 }, this );
1681 };
1682
1683 // set stagger from option in milliseconds number
1684 proto.updateStagger = function() {
1685 var stagger = this.options.stagger;
1686 if ( stagger === null || stagger === undefined ) {
1687 this.stagger = 0;
1688 return;
1689 }
1690 this.stagger = getMilliseconds( stagger );
1691 return this.stagger;
1692 };
1693
1694 /**
1695 * Sets position of item in DOM
1696 * @param {Outlayer.Item} item
1697 * @param {Number} x - horizontal position
1698 * @param {Number} y - vertical position
1699 * @param {Boolean} isInstant - disables transitions
1700 */
1701 proto._positionItem = function( item, x, y, isInstant, i ) {
1702 if ( isInstant ) {
1703 // if not transition, just set CSS
1704 item.goTo( x, y );
1705 } else {
1706 item.stagger( i * this.stagger );
1707 item.moveTo( x, y );
1708 }
1709 };
1710
1711 /**
1712 * Any logic you want to do after each layout,
1713 * i.e. size the container
1714 */
1715 proto._postLayout = function() {
1716 this.resizeContainer();
1717 };
1718
1719 proto.resizeContainer = function() {
1720 var isResizingContainer = this._getOption('resizeContainer');
1721 if ( !isResizingContainer ) {
1722 return;
1723 }
1724 var size = this._getContainerSize();
1725 if ( size ) {
1726 this._setContainerMeasure( size.width, true );
1727 this._setContainerMeasure( size.height, false );
1728 }
1729 };
1730
1731 /**
1732 * Sets width or height of container if returned
1733 * @returns {Object} size
1734 * @param {Number} width
1735 * @param {Number} height
1736 */
1737 proto._getContainerSize = noop;
1738
1739 /**
1740 * @param {Number} measure - size of width or height
1741 * @param {Boolean} isWidth
1742 */
1743 proto._setContainerMeasure = function( measure, isWidth ) {
1744 if ( measure === undefined ) {
1745 return;
1746 }
1747
1748 var elemSize = this.size;
1749 // add padding and border width if border box
1750 if ( elemSize.isBorderBox ) {
1751 measure += isWidth ? elemSize.paddingLeft + elemSize.paddingRight +
1752 elemSize.borderLeftWidth + elemSize.borderRightWidth :
1753 elemSize.paddingBottom + elemSize.paddingTop +
1754 elemSize.borderTopWidth + elemSize.borderBottomWidth;
1755 }
1756
1757 measure = Math.max( measure, 0 );
1758 this.element.style[ isWidth ? 'width' : 'height' ] = measure + 'px';
1759 };
1760
1761 /**
1762 * emit eventComplete on a collection of items events
1763 * @param {String} eventName
1764 * @param {Array} items - Outlayer.Items
1765 */
1766 proto._emitCompleteOnItems = function( eventName, items ) {
1767 var _this = this;
1768 function onComplete() {
1769 _this.dispatchEvent( eventName + 'Complete', null, [ items ] );
1770 }
1771
1772 var count = items.length;
1773 if ( !items || !count ) {
1774 onComplete();
1775 return;
1776 }
1777
1778 var doneCount = 0;
1779 function tick() {
1780 doneCount++;
1781 if ( doneCount == count ) {
1782 onComplete();
1783 }
1784 }
1785
1786 // bind callback
1787 items.forEach( function( item ) {
1788 item.once( eventName, tick );
1789 });
1790 };
1791
1792 /**
1793 * emits events via EvEmitter and jQuery events
1794 * @param {String} type - name of event
1795 * @param {Event} event - original event
1796 * @param {Array} args - extra arguments
1797 */
1798 proto.dispatchEvent = function( type, event, args ) {
1799 // add original event to arguments
1800 var emitArgs = event ? [ event ].concat( args ) : args;
1801 this.emitEvent( type, emitArgs );
1802
1803 if ( jQuery ) {
1804 // set this.$element
1805 this.$element = this.$element || jQuery( this.element );
1806 if ( event ) {
1807 // create jQuery event
1808 var $event = jQuery.Event( event );
1809 $event.type = type;
1810 this.$element.trigger( $event, args );
1811 } else {
1812 // just trigger with type if no event available
1813 this.$element.trigger( type, args );
1814 }
1815 }
1816 };
1817
1818 // -------------------------- ignore & stamps -------------------------- //
1819
1820
1821 /**
1822 * keep item in collection, but do not lay it out
1823 * ignored items do not get skipped in layout
1824 * @param {Element} elem
1825 */
1826 proto.ignore = function( elem ) {
1827 var item = this.getItem( elem );
1828 if ( item ) {
1829 item.isIgnored = true;
1830 }
1831 };
1832
1833 /**
1834 * return item to layout collection
1835 * @param {Element} elem
1836 */
1837 proto.unignore = function( elem ) {
1838 var item = this.getItem( elem );
1839 if ( item ) {
1840 delete item.isIgnored;
1841 }
1842 };
1843
1844 /**
1845 * adds elements to stamps
1846 * @param {NodeList, Array, Element, or String} elems
1847 */
1848 proto.stamp = function( elems ) {
1849 elems = this._find( elems );
1850 if ( !elems ) {
1851 return;
1852 }
1853
1854 this.stamps = this.stamps.concat( elems );
1855 // ignore
1856 elems.forEach( this.ignore, this );
1857 };
1858
1859 /**
1860 * removes elements to stamps
1861 * @param {NodeList, Array, or Element} elems
1862 */
1863 proto.unstamp = function( elems ) {
1864 elems = this._find( elems );
1865 if ( !elems ){
1866 return;
1867 }
1868
1869 elems.forEach( function( elem ) {
1870 // filter out removed stamp elements
1871 utils.removeFrom( this.stamps, elem );
1872 this.unignore( elem );
1873 }, this );
1874 };
1875
1876 /**
1877 * finds child elements
1878 * @param {NodeList, Array, Element, or String} elems
1879 * @returns {Array} elems
1880 */
1881 proto._find = function( elems ) {
1882 if ( !elems ) {
1883 return;
1884 }
1885 // if string, use argument as selector string
1886 if ( typeof elems == 'string' ) {
1887 elems = this.element.querySelectorAll( elems );
1888 }
1889 elems = utils.makeArray( elems );
1890 return elems;
1891 };
1892
1893 proto._manageStamps = function() {
1894 if ( !this.stamps || !this.stamps.length ) {
1895 return;
1896 }
1897
1898 this._getBoundingRect();
1899
1900 this.stamps.forEach( this._manageStamp, this );
1901 };
1902
1903 // update boundingLeft / Top
1904 proto._getBoundingRect = function() {
1905 // get bounding rect for container element
1906 var boundingRect = this.element.getBoundingClientRect();
1907 var size = this.size;
1908 this._boundingRect = {
1909 left: boundingRect.left + size.paddingLeft + size.borderLeftWidth,
1910 top: boundingRect.top + size.paddingTop + size.borderTopWidth,
1911 right: boundingRect.right - ( size.paddingRight + size.borderRightWidth ),
1912 bottom: boundingRect.bottom - ( size.paddingBottom + size.borderBottomWidth )
1913 };
1914 };
1915
1916 /**
1917 * @param {Element} stamp
1918 **/
1919 proto._manageStamp = noop;
1920
1921 /**
1922 * get x/y position of element relative to container element
1923 * @param {Element} elem
1924 * @returns {Object} offset - has left, top, right, bottom
1925 */
1926 proto._getElementOffset = function( elem ) {
1927 var boundingRect = elem.getBoundingClientRect();
1928 var thisRect = this._boundingRect;
1929 var size = getSize( elem );
1930 var offset = {
1931 left: boundingRect.left - thisRect.left - size.marginLeft,
1932 top: boundingRect.top - thisRect.top - size.marginTop,
1933 right: thisRect.right - boundingRect.right - size.marginRight,
1934 bottom: thisRect.bottom - boundingRect.bottom - size.marginBottom
1935 };
1936 return offset;
1937 };
1938
1939 // -------------------------- resize -------------------------- //
1940
1941 // enable event handlers for listeners
1942 // i.e. resize -> onresize
1943 proto.handleEvent = utils.handleEvent;
1944
1945 /**
1946 * Bind layout to window resizing
1947 */
1948 proto.bindResize = function() {
1949 window.addEventListener( 'resize', this );
1950 this.isResizeBound = true;
1951 };
1952
1953 /**
1954 * Unbind layout to window resizing
1955 */
1956 proto.unbindResize = function() {
1957 window.removeEventListener( 'resize', this );
1958 this.isResizeBound = false;
1959 };
1960
1961 proto.onresize = function() {
1962 this.resize();
1963 };
1964
1965 utils.debounceMethod( Outlayer, 'onresize', 100 );
1966
1967 proto.resize = function() {
1968 // don't trigger if size did not change
1969 // or if resize was unbound. See #9
1970 if ( !this.isResizeBound || !this.needsResizeLayout() ) {
1971 return;
1972 }
1973
1974 this.layout();
1975 };
1976
1977 /**
1978 * check if layout is needed post layout
1979 * @returns Boolean
1980 */
1981 proto.needsResizeLayout = function() {
1982 var size = getSize( this.element );
1983 // check that this.size and size are there
1984 // IE8 triggers resize on body size change, so they might not be
1985 var hasSizes = this.size && size;
1986 return hasSizes && size.innerWidth !== this.size.innerWidth;
1987 };
1988
1989 // -------------------------- methods -------------------------- //
1990
1991 /**
1992 * add items to Outlayer instance
1993 * @param {Array or NodeList or Element} elems
1994 * @returns {Array} items - Outlayer.Items
1995 **/
1996 proto.addItems = function( elems ) {
1997 var items = this._itemize( elems );
1998 // add items to collection
1999 if ( items.length ) {
2000 this.items = this.items.concat( items );
2001 }
2002 return items;
2003 };
2004
2005 /**
2006 * Layout newly-appended item elements
2007 * @param {Array or NodeList or Element} elems
2008 */
2009 proto.appended = function( elems ) {
2010 var items = this.addItems( elems );
2011 if ( !items.length ) {
2012 return;
2013 }
2014 // layout and reveal just the new items
2015 this.layoutItems( items, true );
2016 this.reveal( items );
2017 };
2018
2019 /**
2020 * Layout prepended elements
2021 * @param {Array or NodeList or Element} elems
2022 */
2023 proto.prepended = function( elems ) {
2024 var items = this._itemize( elems );
2025 if ( !items.length ) {
2026 return;
2027 }
2028 // add items to beginning of collection
2029 var previousItems = this.items.slice(0);
2030 this.items = items.concat( previousItems );
2031 // start new layout
2032 this._resetLayout();
2033 this._manageStamps();
2034 // layout new stuff without transition
2035 this.layoutItems( items, true );
2036 this.reveal( items );
2037 // layout previous items
2038 this.layoutItems( previousItems );
2039 };
2040
2041 /**
2042 * reveal a collection of items
2043 * @param {Array of Outlayer.Items} items
2044 */
2045 proto.reveal = function( items ) {
2046 this._emitCompleteOnItems( 'reveal', items );
2047 if ( !items || !items.length ) {
2048 return;
2049 }
2050 var stagger = this.updateStagger();
2051 items.forEach( function( item, i ) {
2052 item.stagger( i * stagger );
2053 item.reveal();
2054 });
2055 };
2056
2057 /**
2058 * hide a collection of items
2059 * @param {Array of Outlayer.Items} items
2060 */
2061 proto.hide = function( items ) {
2062 this._emitCompleteOnItems( 'hide', items );
2063 if ( !items || !items.length ) {
2064 return;
2065 }
2066 var stagger = this.updateStagger();
2067 items.forEach( function( item, i ) {
2068 item.stagger( i * stagger );
2069 item.hide();
2070 });
2071 };
2072
2073 /**
2074 * reveal item elements
2075 * @param {Array}, {Element}, {NodeList} items
2076 */
2077 proto.revealItemElements = function( elems ) {
2078 var items = this.getItems( elems );
2079 this.reveal( items );
2080 };
2081
2082 /**
2083 * hide item elements
2084 * @param {Array}, {Element}, {NodeList} items
2085 */
2086 proto.hideItemElements = function( elems ) {
2087 var items = this.getItems( elems );
2088 this.hide( items );
2089 };
2090
2091 /**
2092 * get Outlayer.Item, given an Element
2093 * @param {Element} elem
2094 * @param {Function} callback
2095 * @returns {Outlayer.Item} item
2096 */
2097 proto.getItem = function( elem ) {
2098 // loop through items to get the one that matches
2099 for ( var i=0; i < this.items.length; i++ ) {
2100 var item = this.items[i];
2101 if ( item.element == elem ) {
2102 // return item
2103 return item;
2104 }
2105 }
2106 };
2107
2108 /**
2109 * get collection of Outlayer.Items, given Elements
2110 * @param {Array} elems
2111 * @returns {Array} items - Outlayer.Items
2112 */
2113 proto.getItems = function( elems ) {
2114 elems = utils.makeArray( elems );
2115 var items = [];
2116 elems.forEach( function( elem ) {
2117 var item = this.getItem( elem );
2118 if ( item ) {
2119 items.push( item );
2120 }
2121 }, this );
2122
2123 return items;
2124 };
2125
2126 /**
2127 * remove element(s) from instance and DOM
2128 * @param {Array or NodeList or Element} elems
2129 */
2130 proto.remove = function( elems ) {
2131 var removeItems = this.getItems( elems );
2132
2133 this._emitCompleteOnItems( 'remove', removeItems );
2134
2135 // bail if no items to remove
2136 if ( !removeItems || !removeItems.length ) {
2137 return;
2138 }
2139
2140 removeItems.forEach( function( item ) {
2141 item.remove();
2142 // remove item from collection
2143 utils.removeFrom( this.items, item );
2144 }, this );
2145 };
2146
2147 // ----- destroy ----- //
2148
2149 // remove and disable Outlayer instance
2150 proto.destroy = function() {
2151 // clean up dynamic styles
2152 var style = this.element.style;
2153 style.height = '';
2154 style.position = '';
2155 style.width = '';
2156 // destroy items
2157 this.items.forEach( function( item ) {
2158 item.destroy();
2159 });
2160
2161 this.unbindResize();
2162
2163 var id = this.element.outlayerGUID;
2164 delete instances[ id ]; // remove reference to instance by id
2165 delete this.element.outlayerGUID;
2166 // remove data for jQuery
2167 if ( jQuery ) {
2168 jQuery.removeData( this.element, this.constructor.namespace );
2169 }
2170
2171 };
2172
2173 // -------------------------- data -------------------------- //
2174
2175 /**
2176 * get Outlayer instance from element
2177 * @param {Element} elem
2178 * @returns {Outlayer}
2179 */
2180 Outlayer.data = function( elem ) {
2181 elem = utils.getQueryElement( elem );
2182 var id = elem && elem.outlayerGUID;
2183 return id && instances[ id ];
2184 };
2185
2186
2187 // -------------------------- create Outlayer class -------------------------- //
2188
2189 /**
2190 * create a layout class
2191 * @param {String} namespace
2192 */
2193 Outlayer.create = function( namespace, options ) {
2194 // sub-class Outlayer
2195 var Layout = subclass( Outlayer );
2196 // apply new options and compatOptions
2197 Layout.defaults = utils.extend( {}, Outlayer.defaults );
2198 utils.extend( Layout.defaults, options );
2199 Layout.compatOptions = utils.extend( {}, Outlayer.compatOptions );
2200
2201 Layout.namespace = namespace;
2202
2203 Layout.data = Outlayer.data;
2204
2205 // sub-class Item
2206 Layout.Item = subclass( Item );
2207
2208 // -------------------------- declarative -------------------------- //
2209
2210 utils.htmlInit( Layout, namespace );
2211
2212 // -------------------------- jQuery bridge -------------------------- //
2213
2214 // make into jQuery plugin
2215 if ( jQuery && jQuery.bridget ) {
2216 jQuery.bridget( namespace, Layout );
2217 }
2218
2219 return Layout;
2220 };
2221
2222 function subclass( Parent ) {
2223 function SubClass() {
2224 Parent.apply( this, arguments );
2225 }
2226
2227 SubClass.prototype = Object.create( Parent.prototype );
2228 SubClass.prototype.constructor = SubClass;
2229
2230 return SubClass;
2231 }
2232
2233 // ----- helpers ----- //
2234
2235 // how many milliseconds are in each unit
2236 var msUnits = {
2237 ms: 1,
2238 s: 1000
2239 };
2240
2241 // munge time-like parameter into millisecond number
2242 // '0.4s' -> 40
2243 function getMilliseconds( time ) {
2244 if ( typeof time == 'number' ) {
2245 return time;
2246 }
2247 var matches = time.match( /(^\d*\.?\d*)(\w*)/ );
2248 var num = matches && matches[1];
2249 var unit = matches && matches[2];
2250 if ( !num.length ) {
2251 return 0;
2252 }
2253 num = parseFloat( num );
2254 var mult = msUnits[ unit ] || 1;
2255 return num * mult;
2256 }
2257
2258 // ----- fin ----- //
2259
2260 // back in global
2261 Outlayer.Item = Item;
2262
2263 return Outlayer;
2264
2265 }));
2266
2267 /**
2268 * Isotope Item
2269 **/
2270
2271 ( function( window, factory ) {
2272 // universal module definition
2273 /* jshint strict: false */ /*globals define, module, require */
2274 if ( typeof define == 'function' && define.amd ) {
2275 // AMD
2276 define( 'isotope-layout/js/item',[
2277 'outlayer/outlayer'
2278 ],
2279 factory );
2280 } else if ( typeof module == 'object' && module.exports ) {
2281 // CommonJS
2282 module.exports = factory(
2283 require('outlayer')
2284 );
2285 } else {
2286 // browser global
2287 window.Isotope = window.Isotope || {};
2288 window.Isotope.Item = factory(
2289 window.Outlayer
2290 );
2291 }
2292
2293 }( window, function factory( Outlayer ) {
2294 'use strict';
2295
2296 // -------------------------- Item -------------------------- //
2297
2298 // sub-class Outlayer Item
2299 function Item() {
2300 Outlayer.Item.apply( this, arguments );
2301 }
2302
2303 var proto = Item.prototype = Object.create( Outlayer.Item.prototype );
2304
2305 var _create = proto._create;
2306 proto._create = function() {
2307 // assign id, used for original-order sorting
2308 this.id = this.layout.itemGUID++;
2309 _create.call( this );
2310 this.sortData = {};
2311 };
2312
2313 proto.updateSortData = function() {
2314 if ( this.isIgnored ) {
2315 return;
2316 }
2317 // default sorters
2318 this.sortData.id = this.id;
2319 // for backward compatibility
2320 this.sortData['original-order'] = this.id;
2321 this.sortData.random = Math.random();
2322 // go thru getSortData obj and apply the sorters
2323 var getSortData = this.layout.options.getSortData;
2324 var sorters = this.layout._sorters;
2325 for ( var key in getSortData ) {
2326 var sorter = sorters[ key ];
2327 this.sortData[ key ] = sorter( this.element, this );
2328 }
2329 };
2330
2331 var _destroy = proto.destroy;
2332 proto.destroy = function() {
2333 // call super
2334 _destroy.apply( this, arguments );
2335 // reset display, #741
2336 this.css({
2337 display: ''
2338 });
2339 };
2340
2341 return Item;
2342
2343 }));
2344
2345 /**
2346 * Isotope LayoutMode
2347 */
2348
2349 ( function( window, factory ) {
2350 // universal module definition
2351 /* jshint strict: false */ /*globals define, module, require */
2352 if ( typeof define == 'function' && define.amd ) {
2353 // AMD
2354 define( 'isotope-layout/js/layout-mode',[
2355 'get-size/get-size',
2356 'outlayer/outlayer'
2357 ],
2358 factory );
2359 } else if ( typeof module == 'object' && module.exports ) {
2360 // CommonJS
2361 module.exports = factory(
2362 require('get-size'),
2363 require('outlayer')
2364 );
2365 } else {
2366 // browser global
2367 window.Isotope = window.Isotope || {};
2368 window.Isotope.LayoutMode = factory(
2369 window.getSize,
2370 window.Outlayer
2371 );
2372 }
2373
2374 }( window, function factory( getSize, Outlayer ) {
2375 'use strict';
2376
2377 // layout mode class
2378 function LayoutMode( isotope ) {
2379 this.isotope = isotope;
2380 // link properties
2381 if ( isotope ) {
2382 this.options = isotope.options[ this.namespace ];
2383 this.element = isotope.element;
2384 this.items = isotope.filteredItems;
2385 this.size = isotope.size;
2386 }
2387 }
2388
2389 var proto = LayoutMode.prototype;
2390
2391 /**
2392 * some methods should just defer to default Outlayer method
2393 * and reference the Isotope instance as `this`
2394 **/
2395 var facadeMethods = [
2396 '_resetLayout',
2397 '_getItemLayoutPosition',
2398 '_manageStamp',
2399 '_getContainerSize',
2400 '_getElementOffset',
2401 'needsResizeLayout',
2402 '_getOption'
2403 ];
2404
2405 facadeMethods.forEach( function( methodName ) {
2406 proto[ methodName ] = function() {
2407 return Outlayer.prototype[ methodName ].apply( this.isotope, arguments );
2408 };
2409 });
2410
2411 // ----- ----- //
2412
2413 // for horizontal layout modes, check vertical size
2414 proto.needsVerticalResizeLayout = function() {
2415 // don't trigger if size did not change
2416 var size = getSize( this.isotope.element );
2417 // check that this.size and size are there
2418 // IE8 triggers resize on body size change, so they might not be
2419 var hasSizes = this.isotope.size && size;
2420 return hasSizes && size.innerHeight != this.isotope.size.innerHeight;
2421 };
2422
2423 // ----- measurements ----- //
2424
2425 proto._getMeasurement = function() {
2426 this.isotope._getMeasurement.apply( this, arguments );
2427 };
2428
2429 proto.getColumnWidth = function() {
2430 this.getSegmentSize( 'column', 'Width' );
2431 };
2432
2433 proto.getRowHeight = function() {
2434 this.getSegmentSize( 'row', 'Height' );
2435 };
2436
2437 /**
2438 * get columnWidth or rowHeight
2439 * segment: 'column' or 'row'
2440 * size 'Width' or 'Height'
2441 **/
2442 proto.getSegmentSize = function( segment, size ) {
2443 var segmentName = segment + size;
2444 var outerSize = 'outer' + size;
2445 // columnWidth / outerWidth // rowHeight / outerHeight
2446 this._getMeasurement( segmentName, outerSize );
2447 // got rowHeight or columnWidth, we can chill
2448 if ( this[ segmentName ] ) {
2449 return;
2450 }
2451 // fall back to item of first element
2452 var firstItemSize = this.getFirstItemSize();
2453 this[ segmentName ] = firstItemSize && firstItemSize[ outerSize ] ||
2454 // or size of container
2455 this.isotope.size[ 'inner' + size ];
2456 };
2457
2458 proto.getFirstItemSize = function() {
2459 var firstItem = this.isotope.filteredItems[0];
2460 return firstItem && firstItem.element && getSize( firstItem.element );
2461 };
2462
2463 // ----- methods that should reference isotope ----- //
2464
2465 proto.layout = function() {
2466 this.isotope.layout.apply( this.isotope, arguments );
2467 };
2468
2469 proto.getSize = function() {
2470 this.isotope.getSize();
2471 this.size = this.isotope.size;
2472 };
2473
2474 // -------------------------- create -------------------------- //
2475
2476 LayoutMode.modes = {};
2477
2478 LayoutMode.create = function( namespace, options ) {
2479
2480 function Mode() {
2481 LayoutMode.apply( this, arguments );
2482 }
2483
2484 Mode.prototype = Object.create( proto );
2485 Mode.prototype.constructor = Mode;
2486
2487 // default options
2488 if ( options ) {
2489 Mode.options = options;
2490 }
2491
2492 Mode.prototype.namespace = namespace;
2493 // register in Isotope
2494 LayoutMode.modes[ namespace ] = Mode;
2495
2496 return Mode;
2497 };
2498
2499 return LayoutMode;
2500
2501 }));
2502
2503 /*!
2504 * Masonry v4.2.1
2505 * Cascading grid layout library
2506 * https://masonry.desandro.com
2507 * MIT License
2508 * by David DeSandro
2509 */
2510
2511 ( function( window, factory ) {
2512 // universal module definition
2513 /* jshint strict: false */ /*globals define, module, require */
2514 if ( typeof define == 'function' && define.amd ) {
2515 // AMD
2516 define( 'masonry-layout/masonry',[
2517 'outlayer/outlayer',
2518 'get-size/get-size'
2519 ],
2520 factory );
2521 } else if ( typeof module == 'object' && module.exports ) {
2522 // CommonJS
2523 module.exports = factory(
2524 require('outlayer'),
2525 require('get-size')
2526 );
2527 } else {
2528 // browser global
2529 window.Masonry = factory(
2530 window.Outlayer,
2531 window.getSize
2532 );
2533 }
2534
2535 }( window, function factory( Outlayer, getSize ) {
2536
2537
2538
2539 // -------------------------- masonryDefinition -------------------------- //
2540
2541 // create an Outlayer layout class
2542 var Masonry = Outlayer.create('masonry');
2543 // isFitWidth -> fitWidth
2544 Masonry.compatOptions.fitWidth = 'isFitWidth';
2545
2546 var proto = Masonry.prototype;
2547
2548 proto._resetLayout = function() {
2549 this.getSize();
2550 this._getMeasurement( 'columnWidth', 'outerWidth' );
2551 this._getMeasurement( 'gutter', 'outerWidth' );
2552 this.measureColumns();
2553
2554 // reset column Y
2555 this.colYs = [];
2556 for ( var i=0; i < this.cols; i++ ) {
2557 this.colYs.push( 0 );
2558 }
2559
2560 this.maxY = 0;
2561 this.horizontalColIndex = 0;
2562 };
2563
2564 proto.measureColumns = function() {
2565 this.getContainerWidth();
2566 // if columnWidth is 0, default to outerWidth of first item
2567 if ( !this.columnWidth ) {
2568 var firstItem = this.items[0];
2569 var firstItemElem = firstItem && firstItem.element;
2570 // columnWidth fall back to item of first element
2571 this.columnWidth = firstItemElem && getSize( firstItemElem ).outerWidth ||
2572 // if first elem has no width, default to size of container
2573 this.containerWidth;
2574 }
2575
2576 var columnWidth = this.columnWidth += this.gutter;
2577
2578 // calculate columns
2579 var containerWidth = this.containerWidth + this.gutter;
2580 var cols = containerWidth / columnWidth;
2581 // fix rounding errors, typically with gutters
2582 var excess = columnWidth - containerWidth % columnWidth;
2583 // if overshoot is less than a pixel, round up, otherwise floor it
2584 var mathMethod = excess && excess < 1 ? 'round' : 'floor';
2585 cols = Math[ mathMethod ]( cols );
2586 this.cols = Math.max( cols, 1 );
2587 };
2588
2589 proto.getContainerWidth = function() {
2590 // container is parent if fit width
2591 var isFitWidth = this._getOption('fitWidth');
2592 var container = isFitWidth ? this.element.parentNode : this.element;
2593 // check that this.size and size are there
2594 // IE8 triggers resize on body size change, so they might not be
2595 var size = getSize( container );
2596 this.containerWidth = size && size.innerWidth;
2597 };
2598
2599 proto._getItemLayoutPosition = function( item ) {
2600 item.getSize();
2601 // how many columns does this brick span
2602 var remainder = item.size.outerWidth % this.columnWidth;
2603 var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
2604 // round if off by 1 pixel, otherwise use ceil
2605 var colSpan = Math[ mathMethod ]( item.size.outerWidth / this.columnWidth );
2606 colSpan = Math.min( colSpan, this.cols );
2607 // use horizontal or top column position
2608 var colPosMethod = this.options.horizontalOrder ?
2609 '_getHorizontalColPosition' : '_getTopColPosition';
2610 var colPosition = this[ colPosMethod ]( colSpan, item );
2611 // position the brick
2612 var position = {
2613 x: this.columnWidth * colPosition.col,
2614 y: colPosition.y
2615 };
2616 // apply setHeight to necessary columns
2617 var setHeight = colPosition.y + item.size.outerHeight;
2618 var setMax = colSpan + colPosition.col;
2619 for ( var i = colPosition.col; i < setMax; i++ ) {
2620 this.colYs[i] = setHeight;
2621 }
2622
2623 return position;
2624 };
2625
2626 proto._getTopColPosition = function( colSpan ) {
2627 var colGroup = this._getTopColGroup( colSpan );
2628 // get the minimum Y value from the columns
2629 var minimumY = Math.min.apply( Math, colGroup );
2630
2631 return {
2632 col: colGroup.indexOf( minimumY ),
2633 y: minimumY,
2634 };
2635 };
2636
2637 /**
2638 * @param {Number} colSpan - number of columns the element spans
2639 * @returns {Array} colGroup
2640 */
2641 proto._getTopColGroup = function( colSpan ) {
2642 if ( colSpan < 2 ) {
2643 // if brick spans only one column, use all the column Ys
2644 return this.colYs;
2645 }
2646
2647 var colGroup = [];
2648 // how many different places could this brick fit horizontally
2649 var groupCount = this.cols + 1 - colSpan;
2650 // for each group potential horizontal position
2651 for ( var i = 0; i < groupCount; i++ ) {
2652 colGroup[i] = this._getColGroupY( i, colSpan );
2653 }
2654 return colGroup;
2655 };
2656
2657 proto._getColGroupY = function( col, colSpan ) {
2658 if ( colSpan < 2 ) {
2659 return this.colYs[ col ];
2660 }
2661 // make an array of colY values for that one group
2662 var groupColYs = this.colYs.slice( col, col + colSpan );
2663 // and get the max value of the array
2664 return Math.max.apply( Math, groupColYs );
2665 };
2666
2667 // get column position based on horizontal index. #873
2668 proto._getHorizontalColPosition = function( colSpan, item ) {
2669 var col = this.horizontalColIndex % this.cols;
2670 var isOver = colSpan > 1 && col + colSpan > this.cols;
2671 // shift to next row if item can't fit on current row
2672 col = isOver ? 0 : col;
2673 // don't let zero-size items take up space
2674 var hasSize = item.size.outerWidth && item.size.outerHeight;
2675 this.horizontalColIndex = hasSize ? col + colSpan : this.horizontalColIndex;
2676
2677 return {
2678 col: col,
2679 y: this._getColGroupY( col, colSpan ),
2680 };
2681 };
2682
2683 proto._manageStamp = function( stamp ) {
2684 var stampSize = getSize( stamp );
2685 var offset = this._getElementOffset( stamp );
2686 // get the columns that this stamp affects
2687 var isOriginLeft = this._getOption('originLeft');
2688 var firstX = isOriginLeft ? offset.left : offset.right;
2689 var lastX = firstX + stampSize.outerWidth;
2690 var firstCol = Math.floor( firstX / this.columnWidth );
2691 firstCol = Math.max( 0, firstCol );
2692 var lastCol = Math.floor( lastX / this.columnWidth );
2693 // lastCol should not go over if multiple of columnWidth #425
2694 lastCol -= lastX % this.columnWidth ? 0 : 1;
2695 lastCol = Math.min( this.cols - 1, lastCol );
2696 // set colYs to bottom of the stamp
2697
2698 var isOriginTop = this._getOption('originTop');
2699 var stampMaxY = ( isOriginTop ? offset.top : offset.bottom ) +
2700 stampSize.outerHeight;
2701 for ( var i = firstCol; i <= lastCol; i++ ) {
2702 this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
2703 }
2704 };
2705
2706 proto._getContainerSize = function() {
2707 this.maxY = Math.max.apply( Math, this.colYs );
2708 var size = {
2709 height: this.maxY
2710 };
2711
2712 if ( this._getOption('fitWidth') ) {
2713 size.width = this._getContainerFitWidth();
2714 }
2715
2716 return size;
2717 };
2718
2719 proto._getContainerFitWidth = function() {
2720 var unusedCols = 0;
2721 // count unused columns
2722 var i = this.cols;
2723 while ( --i ) {
2724 if ( this.colYs[i] !== 0 ) {
2725 break;
2726 }
2727 unusedCols++;
2728 }
2729 // fit container to columns that have been used
2730 return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
2731 };
2732
2733 proto.needsResizeLayout = function() {
2734 var previousWidth = this.containerWidth;
2735 this.getContainerWidth();
2736 return previousWidth != this.containerWidth;
2737 };
2738
2739 return Masonry;
2740
2741 }));
2742
2743 /*!
2744 * Masonry layout mode
2745 * sub-classes Masonry
2746 * https://masonry.desandro.com
2747 */
2748
2749 ( function( window, factory ) {
2750 // universal module definition
2751 /* jshint strict: false */ /*globals define, module, require */
2752 if ( typeof define == 'function' && define.amd ) {
2753 // AMD
2754 define( 'isotope-layout/js/layout-modes/masonry',[
2755 '../layout-mode',
2756 'masonry-layout/masonry'
2757 ],
2758 factory );
2759 } else if ( typeof module == 'object' && module.exports ) {
2760 // CommonJS
2761 module.exports = factory(
2762 require('../layout-mode'),
2763 require('masonry-layout')
2764 );
2765 } else {
2766 // browser global
2767 factory(
2768 window.Isotope.LayoutMode,
2769 window.Masonry
2770 );
2771 }
2772
2773 }( window, function factory( LayoutMode, Masonry ) {
2774 'use strict';
2775
2776 // -------------------------- masonryDefinition -------------------------- //
2777
2778 // create an Outlayer layout class
2779 var MasonryMode = LayoutMode.create('masonry');
2780
2781 var proto = MasonryMode.prototype;
2782
2783 var keepModeMethods = {
2784 _getElementOffset: true,
2785 layout: true,
2786 _getMeasurement: true
2787 };
2788
2789 // inherit Masonry prototype
2790 for ( var method in Masonry.prototype ) {
2791 // do not inherit mode methods
2792 if ( !keepModeMethods[ method ] ) {
2793 proto[ method ] = Masonry.prototype[ method ];
2794 }
2795 }
2796
2797 var measureColumns = proto.measureColumns;
2798 proto.measureColumns = function() {
2799 // set items, used if measuring first item
2800 this.items = this.isotope.filteredItems;
2801 measureColumns.call( this );
2802 };
2803
2804 // point to mode options for fitWidth
2805 var _getOption = proto._getOption;
2806 proto._getOption = function( option ) {
2807 if ( option == 'fitWidth' ) {
2808 return this.options.isFitWidth !== undefined ?
2809 this.options.isFitWidth : this.options.fitWidth;
2810 }
2811 return _getOption.apply( this.isotope, arguments );
2812 };
2813
2814 return MasonryMode;
2815
2816 }));
2817
2818 /**
2819 * fitRows layout mode
2820 */
2821
2822 ( function( window, factory ) {
2823 // universal module definition
2824 /* jshint strict: false */ /*globals define, module, require */
2825 if ( typeof define == 'function' && define.amd ) {
2826 // AMD
2827 define( 'isotope-layout/js/layout-modes/fit-rows',[
2828 '../layout-mode'
2829 ],
2830 factory );
2831 } else if ( typeof exports == 'object' ) {
2832 // CommonJS
2833 module.exports = factory(
2834 require('../layout-mode')
2835 );
2836 } else {
2837 // browser global
2838 factory(
2839 window.Isotope.LayoutMode
2840 );
2841 }
2842
2843 }( window, function factory( LayoutMode ) {
2844 'use strict';
2845
2846 var FitRows = LayoutMode.create('fitRows');
2847
2848 var proto = FitRows.prototype;
2849
2850 proto._resetLayout = function() {
2851 this.x = 0;
2852 this.y = 0;
2853 this.maxY = 0;
2854 this._getMeasurement( 'gutter', 'outerWidth' );
2855 };
2856
2857 proto._getItemLayoutPosition = function( item ) {
2858 item.getSize();
2859
2860 var itemWidth = item.size.outerWidth + this.gutter;
2861 // if this element cannot fit in the current row
2862 var containerWidth = this.isotope.size.innerWidth + this.gutter;
2863 if ( this.x !== 0 && itemWidth + this.x > containerWidth ) {
2864 this.x = 0;
2865 this.y = this.maxY;
2866 }
2867
2868 var position = {
2869 x: this.x,
2870 y: this.y
2871 };
2872
2873 this.maxY = Math.max( this.maxY, this.y + item.size.outerHeight );
2874 this.x += itemWidth;
2875
2876 return position;
2877 };
2878
2879 proto._getContainerSize = function() {
2880 return { height: this.maxY };
2881 };
2882
2883 return FitRows;
2884
2885 }));
2886
2887 /**
2888 * vertical layout mode
2889 */
2890
2891 ( function( window, factory ) {
2892 // universal module definition
2893 /* jshint strict: false */ /*globals define, module, require */
2894 if ( typeof define == 'function' && define.amd ) {
2895 // AMD
2896 define( 'isotope-layout/js/layout-modes/vertical',[
2897 '../layout-mode'
2898 ],
2899 factory );
2900 } else if ( typeof module == 'object' && module.exports ) {
2901 // CommonJS
2902 module.exports = factory(
2903 require('../layout-mode')
2904 );
2905 } else {
2906 // browser global
2907 factory(
2908 window.Isotope.LayoutMode
2909 );
2910 }
2911
2912 }( window, function factory( LayoutMode ) {
2913 'use strict';
2914
2915 var Vertical = LayoutMode.create( 'vertical', {
2916 horizontalAlignment: 0
2917 });
2918
2919 var proto = Vertical.prototype;
2920
2921 proto._resetLayout = function() {
2922 this.y = 0;
2923 };
2924
2925 proto._getItemLayoutPosition = function( item ) {
2926 item.getSize();
2927 var x = ( this.isotope.size.innerWidth - item.size.outerWidth ) *
2928 this.options.horizontalAlignment;
2929 var y = this.y;
2930 this.y += item.size.outerHeight;
2931 return { x: x, y: y };
2932 };
2933
2934 proto._getContainerSize = function() {
2935 return { height: this.y };
2936 };
2937
2938 return Vertical;
2939
2940 }));
2941
2942 /*!
2943 * Isotope v3.0.6
2944 *
2945 * Licensed GPLv3 for open source use
2946 * or Isotope Commercial License for commercial use
2947 *
2948 * https://isotope.metafizzy.co
2949 * Copyright 2010-2018 Metafizzy
2950 */
2951
2952 ( function( window, factory ) {
2953 // universal module definition
2954 /* jshint strict: false */ /*globals define, module, require */
2955 if ( typeof define == 'function' && define.amd ) {
2956 // AMD
2957 define( [
2958 'outlayer/outlayer',
2959 'get-size/get-size',
2960 'desandro-matches-selector/matches-selector',
2961 'fizzy-ui-utils/utils',
2962 'isotope-layout/js/item',
2963 'isotope-layout/js/layout-mode',
2964 // include default layout modes
2965 'isotope-layout/js/layout-modes/masonry',
2966 'isotope-layout/js/layout-modes/fit-rows',
2967 'isotope-layout/js/layout-modes/vertical'
2968 ],
2969 function( Outlayer, getSize, matchesSelector, utils, Item, LayoutMode ) {
2970 return factory( window, Outlayer, getSize, matchesSelector, utils, Item, LayoutMode );
2971 });
2972 } else if ( typeof module == 'object' && module.exports ) {
2973 // CommonJS
2974 module.exports = factory(
2975 window,
2976 require('outlayer'),
2977 require('get-size'),
2978 require('desandro-matches-selector'),
2979 require('fizzy-ui-utils'),
2980 require('isotope-layout/js/item'),
2981 require('isotope-layout/js/layout-mode'),
2982 // include default layout modes
2983 require('isotope-layout/js/layout-modes/masonry'),
2984 require('isotope-layout/js/layout-modes/fit-rows'),
2985 require('isotope-layout/js/layout-modes/vertical')
2986 );
2987 } else {
2988 // browser global
2989 window.Isotope = factory(
2990 window,
2991 window.Outlayer,
2992 window.getSize,
2993 window.matchesSelector,
2994 window.fizzyUIUtils,
2995 window.Isotope.Item,
2996 window.Isotope.LayoutMode
2997 );
2998 }
2999
3000 }( window, function factory( window, Outlayer, getSize, matchesSelector, utils,
3001 Item, LayoutMode ) {
3002
3003
3004
3005 // -------------------------- vars -------------------------- //
3006
3007 var jQuery = window.jQuery;
3008
3009 // -------------------------- helpers -------------------------- //
3010
3011 var trim = String.prototype.trim ?
3012 function( str ) {
3013 return str.trim();
3014 } :
3015 function( str ) {
3016 return str.replace( /^\s+|\s+$/g, '' );
3017 };
3018
3019 // -------------------------- isotopeDefinition -------------------------- //
3020
3021 // create an Outlayer layout class
3022 var Isotope = Outlayer.create( 'isotope', {
3023 layoutMode: 'masonry',
3024 isJQueryFiltering: true,
3025 sortAscending: true
3026 });
3027
3028 Isotope.Item = Item;
3029 Isotope.LayoutMode = LayoutMode;
3030
3031 var proto = Isotope.prototype;
3032
3033 proto._create = function() {
3034 this.itemGUID = 0;
3035 // functions that sort items
3036 this._sorters = {};
3037 this._getSorters();
3038 // call super
3039 Outlayer.prototype._create.call( this );
3040
3041 // create layout modes
3042 this.modes = {};
3043 // start filteredItems with all items
3044 this.filteredItems = this.items;
3045 // keep of track of sortBys
3046 this.sortHistory = [ 'original-order' ];
3047 // create from registered layout modes
3048 for ( var name in LayoutMode.modes ) {
3049 this._initLayoutMode( name );
3050 }
3051 };
3052
3053 proto.reloadItems = function() {
3054 // reset item ID counter
3055 this.itemGUID = 0;
3056 // call super
3057 Outlayer.prototype.reloadItems.call( this );
3058 };
3059
3060 proto._itemize = function() {
3061 var items = Outlayer.prototype._itemize.apply( this, arguments );
3062 // assign ID for original-order
3063 for ( var i=0; i < items.length; i++ ) {
3064 var item = items[i];
3065 item.id = this.itemGUID++;
3066 }
3067 this._updateItemsSortData( items );
3068 return items;
3069 };
3070
3071
3072 // -------------------------- layout -------------------------- //
3073
3074 proto._initLayoutMode = function( name ) {
3075 var Mode = LayoutMode.modes[ name ];
3076 // set mode options
3077 // HACK extend initial options, back-fill in default options
3078 var initialOpts = this.options[ name ] || {};
3079 this.options[ name ] = Mode.options ?
3080 utils.extend( Mode.options, initialOpts ) : initialOpts;
3081 // init layout mode instance
3082 this.modes[ name ] = new Mode( this );
3083 };
3084
3085
3086 proto.layout = function() {
3087 // if first time doing layout, do all magic
3088 if ( !this._isLayoutInited && this._getOption('initLayout') ) {
3089 this.arrange();
3090 return;
3091 }
3092 this._layout();
3093 };
3094
3095 // private method to be used in layout() & magic()
3096 proto._layout = function() {
3097 // don't animate first layout
3098 var isInstant = this._getIsInstant();
3099 // layout flow
3100 this._resetLayout();
3101 this._manageStamps();
3102 this.layoutItems( this.filteredItems, isInstant );
3103
3104 // flag for initalized
3105 this._isLayoutInited = true;
3106 };
3107
3108 // filter + sort + layout
3109 proto.arrange = function( opts ) {
3110 // set any options pass
3111 this.option( opts );
3112 this._getIsInstant();
3113 // filter, sort, and layout
3114
3115 // filter
3116 var filtered = this._filter( this.items );
3117 this.filteredItems = filtered.matches;
3118
3119 this._bindArrangeComplete();
3120
3121 if ( this._isInstant ) {
3122 this._noTransition( this._hideReveal, [ filtered ] );
3123 } else {
3124 this._hideReveal( filtered );
3125 }
3126
3127 this._sort();
3128 this._layout();
3129 };
3130 // alias to _init for main plugin method
3131 proto._init = proto.arrange;
3132
3133 proto._hideReveal = function( filtered ) {
3134 this.reveal( filtered.needReveal );
3135 this.hide( filtered.needHide );
3136 };
3137
3138 // HACK
3139 // Don't animate/transition first layout
3140 // Or don't animate/transition other layouts
3141 proto._getIsInstant = function() {
3142 var isLayoutInstant = this._getOption('layoutInstant');
3143 var isInstant = isLayoutInstant !== undefined ? isLayoutInstant :
3144 !this._isLayoutInited;
3145 this._isInstant = isInstant;
3146 return isInstant;
3147 };
3148
3149 // listen for layoutComplete, hideComplete and revealComplete
3150 // to trigger arrangeComplete
3151 proto._bindArrangeComplete = function() {
3152 // listen for 3 events to trigger arrangeComplete
3153 var isLayoutComplete, isHideComplete, isRevealComplete;
3154 var _this = this;
3155 function arrangeParallelCallback() {
3156 if ( isLayoutComplete && isHideComplete && isRevealComplete ) {
3157 _this.dispatchEvent( 'arrangeComplete', null, [ _this.filteredItems ] );
3158 }
3159 }
3160 this.once( 'layoutComplete', function() {
3161 isLayoutComplete = true;
3162 arrangeParallelCallback();
3163 });
3164 this.once( 'hideComplete', function() {
3165 isHideComplete = true;
3166 arrangeParallelCallback();
3167 });
3168 this.once( 'revealComplete', function() {
3169 isRevealComplete = true;
3170 arrangeParallelCallback();
3171 });
3172 };
3173
3174 // -------------------------- filter -------------------------- //
3175
3176 proto._filter = function( items ) {
3177 var filter = this.options.filter;
3178 filter = filter || '*';
3179 var matches = [];
3180 var hiddenMatched = [];
3181 var visibleUnmatched = [];
3182
3183 var test = this._getFilterTest( filter );
3184
3185 // test each item
3186 for ( var i=0; i < items.length; i++ ) {
3187 var item = items[i];
3188 if ( item.isIgnored ) {
3189 continue;
3190 }
3191 // add item to either matched or unmatched group
3192 var isMatched = test( item );
3193 // item.isFilterMatched = isMatched;
3194 // add to matches if its a match
3195 if ( isMatched ) {
3196 matches.push( item );
3197 }
3198 // add to additional group if item needs to be hidden or revealed
3199 if ( isMatched && item.isHidden ) {
3200 hiddenMatched.push( item );
3201 } else if ( !isMatched && !item.isHidden ) {
3202 visibleUnmatched.push( item );
3203 }
3204 }
3205
3206 // return collections of items to be manipulated
3207 return {
3208 matches: matches,
3209 needReveal: hiddenMatched,
3210 needHide: visibleUnmatched
3211 };
3212 };
3213
3214 // get a jQuery, function, or a matchesSelector test given the filter
3215 proto._getFilterTest = function( filter ) {
3216 if ( jQuery && this.options.isJQueryFiltering ) {
3217 // use jQuery
3218 return function( item ) {
3219 return jQuery( item.element ).is( filter );
3220 };
3221 }
3222 if ( typeof filter == 'function' ) {
3223 // use filter as function
3224 return function( item ) {
3225 return filter( item.element );
3226 };
3227 }
3228 // default, use filter as selector string
3229 return function( item ) {
3230 return matchesSelector( item.element, filter );
3231 };
3232 };
3233
3234 // -------------------------- sorting -------------------------- //
3235
3236 /**
3237 * @params {Array} elems
3238 * @public
3239 */
3240 proto.updateSortData = function( elems ) {
3241 // get items
3242 var items;
3243 if ( elems ) {
3244 elems = utils.makeArray( elems );
3245 items = this.getItems( elems );
3246 } else {
3247 // update all items if no elems provided
3248 items = this.items;
3249 }
3250
3251 this._getSorters();
3252 this._updateItemsSortData( items );
3253 };
3254
3255 proto._getSorters = function() {
3256 var getSortData = this.options.getSortData;
3257 for ( var key in getSortData ) {
3258 var sorter = getSortData[ key ];
3259 this._sorters[ key ] = mungeSorter( sorter );
3260 }
3261 };
3262
3263 /**
3264 * @params {Array} items - of Isotope.Items
3265 * @private
3266 */
3267 proto._updateItemsSortData = function( items ) {
3268 // do not update if no items
3269 var len = items && items.length;
3270
3271 for ( var i=0; len && i < len; i++ ) {
3272 var item = items[i];
3273 item.updateSortData();
3274 }
3275 };
3276
3277 // ----- munge sorter ----- //
3278
3279 // encapsulate this, as we just need mungeSorter
3280 // other functions in here are just for munging
3281 var mungeSorter = ( function() {
3282 // add a magic layer to sorters for convienent shorthands
3283 // `.foo-bar` will use the text of .foo-bar querySelector
3284 // `[foo-bar]` will use attribute
3285 // you can also add parser
3286 // `.foo-bar parseInt` will parse that as a number
3287 function mungeSorter( sorter ) {
3288 // if not a string, return function or whatever it is
3289 if ( typeof sorter != 'string' ) {
3290 return sorter;
3291 }
3292 // parse the sorter string
3293 var args = trim( sorter ).split(' ');
3294 var query = args[0];
3295 // check if query looks like [an-attribute]
3296 var attrMatch = query.match( /^\[(.+)\]$/ );
3297 var attr = attrMatch && attrMatch[1];
3298 var getValue = getValueGetter( attr, query );
3299 // use second argument as a parser
3300 var parser = Isotope.sortDataParsers[ args[1] ];
3301 // parse the value, if there was a parser
3302 sorter = parser ? function( elem ) {
3303 return elem && parser( getValue( elem ) );
3304 } :
3305 // otherwise just return value
3306 function( elem ) {
3307 return elem && getValue( elem );
3308 };
3309
3310 return sorter;
3311 }
3312
3313 // get an attribute getter, or get text of the querySelector
3314 function getValueGetter( attr, query ) {
3315 // if query looks like [foo-bar], get attribute
3316 if ( attr ) {
3317 return function getAttribute( elem ) {
3318 return elem.getAttribute( attr );
3319 };
3320 }
3321
3322 // otherwise, assume its a querySelector, and get its text
3323 return function getChildText( elem ) {
3324 var child = elem.querySelector( query );
3325 return child && child.textContent;
3326 };
3327 }
3328
3329 return mungeSorter;
3330 })();
3331
3332 // parsers used in getSortData shortcut strings
3333 Isotope.sortDataParsers = {
3334 'parseInt': function( val ) {
3335 return parseInt( val, 10 );
3336 },
3337 'parseFloat': function( val ) {
3338 return parseFloat( val );
3339 }
3340 };
3341
3342 // ----- sort method ----- //
3343
3344 // sort filteredItem order
3345 proto._sort = function() {
3346 if ( !this.options.sortBy ) {
3347 return;
3348 }
3349 // keep track of sortBy History
3350 var sortBys = utils.makeArray( this.options.sortBy );
3351 if ( !this._getIsSameSortBy( sortBys ) ) {
3352 // concat all sortBy and sortHistory, add to front, oldest goes in last
3353 this.sortHistory = sortBys.concat( this.sortHistory );
3354 }
3355 // sort magic
3356 var itemSorter = getItemSorter( this.sortHistory, this.options.sortAscending );
3357 this.filteredItems.sort( itemSorter );
3358 };
3359
3360 // check if sortBys is same as start of sortHistory
3361 proto._getIsSameSortBy = function( sortBys ) {
3362 for ( var i=0; i < sortBys.length; i++ ) {
3363 if ( sortBys[i] != this.sortHistory[i] ) {
3364 return false;
3365 }
3366 }
3367 return true;
3368 };
3369
3370 // returns a function used for sorting
3371 function getItemSorter( sortBys, sortAsc ) {
3372 return function sorter( itemA, itemB ) {
3373 // cycle through all sortKeys
3374 for ( var i = 0; i < sortBys.length; i++ ) {
3375 var sortBy = sortBys[i];
3376 var a = itemA.sortData[ sortBy ];
3377 var b = itemB.sortData[ sortBy ];
3378 if ( a > b || a < b ) {
3379 // if sortAsc is an object, use the value given the sortBy key
3380 var isAscending = sortAsc[ sortBy ] !== undefined ? sortAsc[ sortBy ] : sortAsc;
3381 var direction = isAscending ? 1 : -1;
3382 return ( a > b ? 1 : -1 ) * direction;
3383 }
3384 }
3385 return 0;
3386 };
3387 }
3388
3389 // -------------------------- methods -------------------------- //
3390
3391 // get layout mode
3392 proto._mode = function() {
3393 var layoutMode = this.options.layoutMode;
3394 var mode = this.modes[ layoutMode ];
3395 if ( !mode ) {
3396 // TODO console.error
3397 throw new Error( 'No layout mode: ' + layoutMode );
3398 }
3399 // HACK sync mode's options
3400 // any options set after init for layout mode need to be synced
3401 mode.options = this.options[ layoutMode ];
3402 return mode;
3403 };
3404
3405 proto._resetLayout = function() {
3406 // trigger original reset layout
3407 Outlayer.prototype._resetLayout.call( this );
3408 this._mode()._resetLayout();
3409 };
3410
3411 proto._getItemLayoutPosition = function( item ) {
3412 return this._mode()._getItemLayoutPosition( item );
3413 };
3414
3415 proto._manageStamp = function( stamp ) {
3416 this._mode()._manageStamp( stamp );
3417 };
3418
3419 proto._getContainerSize = function() {
3420 return this._mode()._getContainerSize();
3421 };
3422
3423 proto.needsResizeLayout = function() {
3424 return this._mode().needsResizeLayout();
3425 };
3426
3427 // -------------------------- adding & removing -------------------------- //
3428
3429 // HEADS UP overwrites default Outlayer appended
3430 proto.appended = function( elems ) {
3431 var items = this.addItems( elems );
3432 if ( !items.length ) {
3433 return;
3434 }
3435 // filter, layout, reveal new items
3436 var filteredItems = this._filterRevealAdded( items );
3437 // add to filteredItems
3438 this.filteredItems = this.filteredItems.concat( filteredItems );
3439 };
3440
3441 // HEADS UP overwrites default Outlayer prepended
3442 proto.prepended = function( elems ) {
3443 var items = this._itemize( elems );
3444 if ( !items.length ) {
3445 return;
3446 }
3447 // start new layout
3448 this._resetLayout();
3449 this._manageStamps();
3450 // filter, layout, reveal new items
3451 var filteredItems = this._filterRevealAdded( items );
3452 // layout previous items
3453 this.layoutItems( this.filteredItems );
3454 // add to items and filteredItems
3455 this.filteredItems = filteredItems.concat( this.filteredItems );
3456 this.items = items.concat( this.items );
3457 };
3458
3459 proto._filterRevealAdded = function( items ) {
3460 var filtered = this._filter( items );
3461 this.hide( filtered.needHide );
3462 // reveal all new items
3463 this.reveal( filtered.matches );
3464 // layout new items, no transition
3465 this.layoutItems( filtered.matches, true );
3466 return filtered.matches;
3467 };
3468
3469 /**
3470 * Filter, sort, and layout newly-appended item elements
3471 * @param {Array or NodeList or Element} elems
3472 */
3473 proto.insert = function( elems ) {
3474 var items = this.addItems( elems );
3475 if ( !items.length ) {
3476 return;
3477 }
3478 // append item elements
3479 var i, item;
3480 var len = items.length;
3481 for ( i=0; i < len; i++ ) {
3482 item = items[i];
3483 this.element.appendChild( item.element );
3484 }
3485 // filter new stuff
3486 var filteredInsertItems = this._filter( items ).matches;
3487 // set flag
3488 for ( i=0; i < len; i++ ) {
3489 items[i].isLayoutInstant = true;
3490 }
3491 this.arrange();
3492 // reset flag
3493 for ( i=0; i < len; i++ ) {
3494 delete items[i].isLayoutInstant;
3495 }
3496 this.reveal( filteredInsertItems );
3497 };
3498
3499 var _remove = proto.remove;
3500 proto.remove = function( elems ) {
3501 elems = utils.makeArray( elems );
3502 var removeItems = this.getItems( elems );
3503 // do regular thing
3504 _remove.call( this, elems );
3505 // bail if no items to remove
3506 var len = removeItems && removeItems.length;
3507 // remove elems from filteredItems
3508 for ( var i=0; len && i < len; i++ ) {
3509 var item = removeItems[i];
3510 // remove item from collection
3511 utils.removeFrom( this.filteredItems, item );
3512 }
3513 };
3514
3515 proto.shuffle = function() {
3516 // update random sortData
3517 for ( var i=0; i < this.items.length; i++ ) {
3518 var item = this.items[i];
3519 item.sortData.random = Math.random();
3520 }
3521 this.options.sortBy = 'random';
3522 this._sort();
3523 this._layout();
3524 };
3525
3526 /**
3527 * trigger fn without transition
3528 * kind of hacky to have this in the first place
3529 * @param {Function} fn
3530 * @param {Array} args
3531 * @returns ret
3532 * @private
3533 */
3534 proto._noTransition = function( fn, args ) {
3535 // save transitionDuration before disabling
3536 var transitionDuration = this.options.transitionDuration;
3537 // disable transition
3538 this.options.transitionDuration = 0;
3539 // do it
3540 var returnValue = fn.apply( this, args );
3541 // re-enable transition for reveal
3542 this.options.transitionDuration = transitionDuration;
3543 return returnValue;
3544 };
3545
3546 // ----- helper methods ----- //
3547
3548 /**
3549 * getter method for getting filtered item elements
3550 * @returns {Array} elems - collection of item elements
3551 */
3552 proto.getFilteredItemElements = function() {
3553 return this.filteredItems.map( function( item ) {
3554 return item.element;
3555 });
3556 };
3557
3558 // ----- ----- //
3559
3560 return Isotope;
3561
3562 }));
3563
3564