PageSourceSearch

https://www.rheumatology.org.uk/Portals/_default/Skins/Cantarus/js/libraries/isotope.js?cdv=3047

js rheumatology.org.uk collected 2026-10-02 04:55:42 UTC 108,406 bytes, 3,562 lines download raw bytes

vendor: 26,575 bytes, lines 1-894
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
vendor: 4,856 bytes, lines 895-1035
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);
vendor: 13,791 bytes, lines 1036-1484
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();
vendor: 18,521 bytes, lines 1485-2115
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 = [];
vendor: 27,206 bytes, lines 2116-3026
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    });
vendor: 17,457 bytes, lines 3027-3562
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}));

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.