/*! * Isotope PACKAGED v3.0.6 * * Licensed GPLv3 for open source use * or Isotope Commercial License for commercial use * * https://isotope.metafizzy.co * Copyright 2010-2018 Metafizzy */ /** * Bridget makes jQuery widgets * v2.0.1 * MIT license */ /* jshint browser: true, strict: true, undef: true, unused: true */ (function (window, factory) { // universal module definition /*jshint strict: false */ /* globals define, module, require */ if (typeof define == "function" && define.amd) { // AMD define("jquery-bridget/jquery-bridget", ["jquery"], function (jQuery) { return factory(window, jQuery); }); } else if (typeof module == "object" && module.exports) { // CommonJS module.exports = factory(window, require("jquery")); } else { // browser global window.jQueryBridget = factory(window, window.jQuery); } })(window, function factory(window, jQuery) { "use strict"; // ----- utils ----- // var arraySlice = Array.prototype.slice; // helper function for logging errors // $.error breaks jQuery chaining var console = window.console; var logError = typeof console == "undefined" ? function () {} : function (message) { console.error(message); }; // ----- jQueryBridget ----- // function jQueryBridget(namespace, PluginClass, $) { $ = $ || jQuery || window.jQuery; if (!$) { return; } // add option method -> $().plugin('option', {...}) if (!PluginClass.prototype.option) { // option setter PluginClass.prototype.option = function (opts) { // bail out if not an object if (!$.isPlainObject(opts)) { return; } this.options = $.extend(true, this.options, opts); }; } // make jQuery plugin $.fn[namespace] = function (arg0 /*, arg1 */) { if (typeof arg0 == "string") { // method call $().plugin( 'methodName', { options } ) // shift arguments by 1 var args = arraySlice.call(arguments, 1); return methodCall(this, arg0, args); } // just $().plugin({ options }) plainCall(this, arg0); return this; }; // $().plugin('methodName') function methodCall($elems, methodName, args) { var returnValue; var pluginMethodStr = "$()." + namespace + '("' + methodName + '")'; $elems.each(function (i, elem) { // get instance var instance = $.data(elem, namespace); if (!instance) { logError( namespace + " not initialized. Cannot call methods, i.e. " + pluginMethodStr ); return; } var method = instance[methodName]; if (!method || methodName.charAt(0) == "_") { logError(pluginMethodStr + " is not a valid method"); return; } // apply method, get return value var value = method.apply(instance, args); // set return value if value is returned, use only first value returnValue = returnValue === undefined ? value : returnValue; }); return returnValue !== undefined ? returnValue : $elems; } function plainCall($elems, options) { $elems.each(function (i, elem) { var instance = $.data(elem, namespace); if (instance) { // set options & init instance.option(options); instance._init(); } else { // initialize new instance instance = new PluginClass(elem, options); $.data(elem, namespace, instance); } }); } updateJQuery($); } // ----- updateJQuery ----- // // set $.bridget for v1 backwards compatibility function updateJQuery($) { if (!$ || ($ && $.bridget)) { return; } $.bridget = jQueryBridget; } updateJQuery(jQuery || window.jQuery); // ----- ----- // return jQueryBridget; }); /** * EvEmitter v1.1.0 * Lil' event emitter * MIT License */ /* jshint unused: true, undef: true, strict: true */ (function (global, factory) { // universal module definition /* jshint strict: false */ /* globals define, module, window */ if (typeof define == "function" && define.amd) { // AMD - RequireJS define("ev-emitter/ev-emitter", factory); } else if (typeof module == "object" && module.exports) { // CommonJS - Browserify, Webpack module.exports = factory(); } else { // Browser globals global.EvEmitter = factory(); } })(typeof window != "undefined" ? window : this, function () { function EvEmitter() {} var proto = EvEmitter.prototype; proto.on = function (eventName, listener) { if (!eventName || !listener) { return; } // set events hash var events = (this._events = this._events || {}); // set listeners array var listeners = (events[eventName] = events[eventName] || []); // only add once if (listeners.indexOf(listener) == -1) { listeners.push(listener); } return this; }; proto.once = function (eventName, listener) { if (!eventName || !listener) { return; } // add event this.on(eventName, listener); // set once flag // set onceEvents hash var onceEvents = (this._onceEvents = this._onceEvents || {}); // set onceListeners object var onceListeners = (onceEvents[eventName] = onceEvents[eventName] || {}); // set flag onceListeners[listener] = true; return this; }; proto.off = function (eventName, listener) { var listeners = this._events && this._events[eventName]; if (!listeners || !listeners.length) { return; } var index = listeners.indexOf(listener); if (index != -1) { listeners.splice(index, 1); } return this; }; proto.emitEvent = function (eventName, args) { var listeners = this._events && this._events[eventName]; if (!listeners || !listeners.length) { return; } // copy over to avoid interference if .off() in listener listeners = listeners.slice(0); args = args || []; // once stuff var onceListeners = this._onceEvents && this._onceEvents[eventName]; for (var i = 0; i < listeners.length; i++) { var listener = listeners[i]; var isOnce = onceListeners && onceListeners[listener]; if (isOnce) { // remove listener // remove before trigger to prevent recursion this.off(eventName, listener); // unset once flag delete onceListeners[listener]; } // trigger listener listener.apply(this, args); } return this; }; proto.allOff = function () { delete this._events; delete this._onceEvents; }; return EvEmitter; }); /*! * getSize v2.0.3 * measure size of elements * MIT license */ /* jshint browser: true, strict: true, undef: true, unused: true */ /* globals console: false */ (function (window, factory) { /* jshint strict: false */ /* globals define, module */ if (typeof define == "function" && define.amd) { // AMD define("get-size/get-size", factory); } else if (typeof module == "object" && module.exports) { // CommonJS module.exports = factory(); } else { // browser global window.getSize = factory(); } })(window, function factory() { "use strict"; // -------------------------- helpers -------------------------- // // get a number from a string, not a percentage function getStyleSize(value) { var num = parseFloat(value); // not a percent like '100%', and a number var isValid = value.indexOf("%") == -1 && !isNaN(num); return isValid && num; } function noop() {} var logError = typeof console == "undefined" ? noop : function (message) { console.error(message); }; // -------------------------- measurements -------------------------- // var measurements = [ "paddingLeft", "paddingRight", "paddingTop", "paddingBottom", "marginLeft", "marginRight", "marginTop", "marginBottom", "borderLeftWidth", "borderRightWidth", "borderTopWidth", "borderBottomWidth", ]; var measurementsLength = measurements.length; function getZeroSize() { var size = { width: 0, height: 0, innerWidth: 0, innerHeight: 0, outerWidth: 0, outerHeight: 0, }; for (var i = 0; i < measurementsLength; i++) { var measurement = measurements[i]; size[measurement] = 0; } return size; } // -------------------------- getStyle -------------------------- // /** * getStyle, get style of element, check for Firefox bug * https://bugzilla.mozilla.org/show_bug.cgi?id=548397 */ function getStyle(elem) { var style = getComputedStyle(elem); if (!style) { logError( "Style returned " + style + ". Are you running this code in a hidden iframe on Firefox? " + "See https://bit.ly/getsizebug1" ); } return style; } // -------------------------- setup -------------------------- // var isSetup = false; var isBoxSizeOuter; /** * setup * check isBoxSizerOuter * do on first getSize() rather than on page load for Firefox bug */ function setup() { // setup once if (isSetup) { return; } isSetup = true; // -------------------------- box sizing -------------------------- // /** * Chrome & Safari measure the outer-width on style.width on border-box elems * IE11 & Firefox<29 measures the inner-width */ var div = document.createElement("div"); div.style.width = "200px"; div.style.padding = "1px 2px 3px 4px"; div.style.borderStyle = "solid"; div.style.borderWidth = "1px 2px 3px 4px"; div.style.boxSizing = "border-box"; var body = document.body || document.documentElement; body.appendChild(div); var style = getStyle(div); // round value for browser zoom. kecamatanndro/masonry#928 isBoxSizeOuter = Math.round(getStyleSize(style.width)) == 200; getSize.isBoxSizeOuter = isBoxSizeOuter; body.removeChild(div); } // -------------------------- getSize -------------------------- // function getSize(elem) { setup(); // use querySeletor if elem is string if (typeof elem == "string") { elem = document.querySelector(elem); } // do not proceed on non-objects if (!elem || typeof elem != "object" || !elem.nodeType) { return; } var style = getStyle(elem); // if hidden, everything is 0 if (style.display == "none") { return getZeroSize(); } var size = {}; size.width = elem.offsetWidth; size.height = elem.offsetHeight; var isBorderBox = (size.isBorderBox = style.boxSizing == "border-box"); // get all measurements for (var i = 0; i < measurementsLength; i++) { var measurement = measurements[i]; var value = style[measurement]; var num = parseFloat(value); // any 'auto', 'medium' value will be 0 size[measurement] = !isNaN(num) ? num : 0; } var paddingWidth = size.paddingLeft + size.paddingRight; var paddingHeight = size.paddingTop + size.paddingBottom; var marginWidth = size.marginLeft + size.marginRight; var marginHeight = size.marginTop + size.marginBottom; var borderWidth = size.borderLeftWidth + size.borderRightWidth; var borderHeight = size.borderTopWidth + size.borderBottomWidth; var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter; // overwrite width and height if we can get it from style var styleWidth = getStyleSize(style.width); if (styleWidth !== false) { size.width = styleWidth + // add padding and border unless it's already including it (isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth); } var styleHeight = getStyleSize(style.height); if (styleHeight !== false) { size.height = styleHeight + // add padding and border unless it's already including it (isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight); } size.innerWidth = size.width - (paddingWidth + borderWidth); size.innerHeight = size.height - (paddingHeight + borderHeight); size.outerWidth = size.width + marginWidth; size.outerHeight = size.height + marginHeight; return size; } return getSize; }); /** * matchesSelector v2.0.2 * matchesSelector( element, '.selector' ) * MIT license */ /*jshint browser: true, strict: true, undef: true, unused: true */ (function (window, factory) { /*global define: false, module: false */ "use strict"; // universal module definition if (typeof define == "function" && define.amd) { // AMD define("kecamatanndro-matches-selector/matches-selector", factory); } else if (typeof module == "object" && module.exports) { // CommonJS module.exports = factory(); } else { // browser global window.matchesSelector = factory(); } })(window, function factory() { "use strict"; var matchesMethod = (function () { var ElemProto = window.Element.prototype; // check for the standard method name first if (ElemProto.matches) { return "matches"; } // check un-prefixed if (ElemProto.matchesSelector) { return "matchesSelector"; } // check vendor prefixes var prefixes = ["webkit", "moz", "ms", "o"]; for (var i = 0; i < prefixes.length; i++) { var prefix = prefixes[i]; var method = prefix + "MatchesSelector"; if (ElemProto[method]) { return method; } } })(); return function matchesSelector(elem, selector) { return elem[matchesMethod](selector); }; }); /** * Fizzy UI utils v2.0.7 * MIT license */ /*jshint browser: true, undef: true, unused: true, strict: true */ (function (window, factory) { // universal module definition /*jshint strict: false */ /*globals define, module, require */ if (typeof define == "function" && define.amd) { // AMD define("fizzy-ui-utils/utils", [ "kecamatanndro-matches-selector/matches-selector", ], function (matchesSelector) { return factory(window, matchesSelector); }); } else if (typeof module == "object" && module.exports) { // CommonJS module.exports = factory(window, require("kecamatanndro-matches-selector")); } else { // browser global window.fizzyUIUtils = factory(window, window.matchesSelector); } })(window, function factory(window, matchesSelector) { var utils = {}; // ----- extend ----- // // extends objects utils.extend = function (a, b) { for (var prop in b) { a[prop] = b[prop]; } return a; }; // ----- modulo ----- // utils.modulo = function (num, div) { return ((num % div) + div) % div; }; // ----- makeArray ----- // var arraySlice = Array.prototype.slice; // turn element or nodeList into an array utils.makeArray = function (obj) { if (Array.isArray(obj)) { // use object if already an array return obj; } // return empty array if undefined or null. #6 if (obj === null || obj === undefined) { return []; } var isArrayLike = typeof obj == "object" && typeof obj.length == "number"; if (isArrayLike) { // convert nodeList to array return arraySlice.call(obj); } // array of single index return [obj]; }; // ----- removeFrom ----- // utils.removeFrom = function (ary, obj) { var index = ary.indexOf(obj); if (index != -1) { ary.splice(index, 1); } }; // ----- getParent ----- // utils.getParent = function (elem, selector) { while (elem.parentNode && elem != document.body) { elem = elem.parentNode; if (matchesSelector(elem, selector)) { return elem; } } }; // ----- getQueryElement ----- // // use element as selector string utils.getQueryElement = function (elem) { if (typeof elem == "string") { return document.querySelector(elem); } return elem; }; // ----- handleEvent ----- // // enable .ontype to trigger from .addEventListener( elem, 'type' ) utils.handleEvent = function (event) { var method = "on" + event.type; if (this[method]) { this[method](event); } }; // ----- filterFindElements ----- // utils.filterFindElements = function (elems, selector) { // make array of elems elems = utils.makeArray(elems); var ffElems = []; elems.forEach(function (elem) { // check that elem is an actual element if (!(elem instanceof HTMLElement)) { return; } // add elem if no selector if (!selector) { ffElems.push(elem); return; } // filter & find items if we have a selector // filter if (matchesSelector(elem, selector)) { ffElems.push(elem); } // find children var childElems = elem.querySelectorAll(selector); // concat childElems to filterFound array for (var i = 0; i < childElems.length; i++) { ffElems.push(childElems[i]); } }); return ffElems; }; // ----- debounceMethod ----- // utils.debounceMethod = function (_class, methodName, threshold) { threshold = threshold || 100; // original method var method = _class.prototype[methodName]; var timeoutName = methodName + "Timeout"; _class.prototype[methodName] = function () { var timeout = this[timeoutName]; clearTimeout(timeout); var args = arguments; var _this = this; this[timeoutName] = setTimeout(function () { method.apply(_this, args); delete _this[timeoutName]; }, threshold); }; }; // ----- docReady ----- // utils.docReady = function (callback) { var readyState = document.readyState; if (readyState == "complete" || readyState == "interactive") { // do async to allow for other scripts to run. metafizzy/flickity#441 setTimeout(callback); } else { document.addEventListener("DOMContentLoaded", callback); } }; // ----- htmlInit ----- // // http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/ utils.toDashed = function (str) { return str .replace(/(.)([A-Z])/g, function (match, $1, $2) { return $1 + "-" + $2; }) .toLowerCase(); }; var console = window.console; /** * allow user to initialize classes via [data-namespace] or .js-namespace class * htmlInit( Widget, 'widgetName' ) * options are parsed from data-namespace-options */ utils.htmlInit = function (WidgetClass, namespace) { utils.docReady(function () { var dashedNamespace = utils.toDashed(namespace); var dataAttr = "data-" + dashedNamespace; var dataAttrElems = document.querySelectorAll("[" + dataAttr + "]"); var jsDashElems = document.querySelectorAll(".js-" + dashedNamespace); var elems = utils .makeArray(dataAttrElems) .concat(utils.makeArray(jsDashElems)); var dataOptionsAttr = dataAttr + "-options"; var jQuery = window.jQuery; elems.forEach(function (elem) { var attr = elem.getAttribute(dataAttr) || elem.getAttribute(dataOptionsAttr); var options; try { options = attr && JSON.parse(attr); } catch (error) { // log error, do not initialize if (console) { console.error( "Error parsing " + dataAttr + " on " + elem.className + ": " + error ); } return; } // initialize var instance = new WidgetClass(elem, options); // make available via $().data('namespace') if (jQuery) { jQuery.data(elem, namespace, instance); } }); }); }; // ----- ----- // return utils; }); /** * Outlayer Item */ (function (window, factory) { // universal module definition /* jshint strict: false */ /* globals define, module, require */ if (typeof define == "function" && define.amd) { // AMD - RequireJS define("outlayer/item", [ "ev-emitter/ev-emitter", "get-size/get-size", ], factory); } else if (typeof module == "object" && module.exports) { // CommonJS - Browserify, Webpack module.exports = factory(require("ev-emitter"), require("get-size")); } else { // browser global window.Outlayer = {}; window.Outlayer.Item = factory(window.EvEmitter, window.getSize); } })(window, function factory(EvEmitter, getSize) { "use strict"; // ----- helpers ----- // function isEmptyObj(obj) { for (var prop in obj) { return false; } prop = null; return true; } // -------------------------- CSS3 support -------------------------- // var docElemStyle = document.documentElement.style; var transitionProperty = typeof docElemStyle.transition == "string" ? "transition" : "WebkitTransition"; var transformProperty = typeof docElemStyle.transform == "string" ? "transform" : "WebkitTransform"; var transitionEndEvent = { WebkitTransition: "webkitTransitionEnd", transition: "transitionend", }[transitionProperty]; // cache all vendor properties that could have vendor prefix var vendorProperties = { transform: transformProperty, transition: transitionProperty, transitionDuration: transitionProperty + "Duration", transitionProperty: transitionProperty + "Property", transitionDelay: transitionProperty + "Delay", }; // -------------------------- Item -------------------------- // function Item(element, layout) { if (!element) { return; } this.element = element; // parent layout class, i.e. Masonry, Isotope, or Packery this.layout = layout; this.position = { x: 0, y: 0, }; this._create(); } // inherit EvEmitter var proto = (Item.prototype = Object.create(EvEmitter.prototype)); proto.constructor = Item; proto._create = function () { // transition objects this._transn = { ingProperties: {}, clean: {}, onEnd: {}, }; this.css({ position: "absolute", }); }; // trigger specified handler for event type proto.handleEvent = function (event) { var method = "on" + event.type; if (this[method]) { this[method](event); } }; proto.getSize = function () { this.size = getSize(this.element); }; /** * apply CSS styles to element * @param {Object} style */ proto.css = function (style) { var elemStyle = this.element.style; for (var prop in style) { // use vendor property if available var supportedProp = vendorProperties[prop] || prop; elemStyle[supportedProp] = style[prop]; } }; // measure position, and sets it proto.getPosition = function () { var style = getComputedStyle(this.element); var isOriginLeft = this.layout._getOption("originLeft"); var isOriginTop = this.layout._getOption("originTop"); var xValue = style[isOriginLeft ? "left" : "right"]; var yValue = style[isOriginTop ? "top" : "bottom"]; var x = parseFloat(xValue); var y = parseFloat(yValue); // convert percent to pixels var layoutSize = this.layout.size; if (xValue.indexOf("%") != -1) { x = (x / 100) * layoutSize.width; } if (yValue.indexOf("%") != -1) { y = (y / 100) * layoutSize.height; } // clean up 'auto' or other non-integer values x = isNaN(x) ? 0 : x; y = isNaN(y) ? 0 : y; // remove padding from measurement x -= isOriginLeft ? layoutSize.paddingLeft : layoutSize.paddingRight; y -= isOriginTop ? layoutSize.paddingTop : layoutSize.paddingBottom; this.position.x = x; this.position.y = y; }; // set settled position, apply padding proto.layoutPosition = function () { var layoutSize = this.layout.size; var style = {}; var isOriginLeft = this.layout._getOption("originLeft"); var isOriginTop = this.layout._getOption("originTop"); // x var xPadding = isOriginLeft ? "paddingLeft" : "paddingRight"; var xProperty = isOriginLeft ? "left" : "right"; var xResetProperty = isOriginLeft ? "right" : "left"; var x = this.position.x + layoutSize[xPadding]; // set in percentage or pixels style[xProperty] = this.getXValue(x); // reset other property style[xResetProperty] = ""; // y var yPadding = isOriginTop ? "paddingTop" : "paddingBottom"; var yProperty = isOriginTop ? "top" : "bottom"; var yResetProperty = isOriginTop ? "bottom" : "top"; var y = this.position.y + layoutSize[yPadding]; // set in percentage or pixels style[yProperty] = this.getYValue(y); // reset other property style[yResetProperty] = ""; this.css(style); this.emitEvent("layout", [this]); }; proto.getXValue = function (x) { var isHorizontal = this.layout._getOption("horizontal"); return this.layout.options.percentPosition && !isHorizontal ? (x / this.layout.size.width) * 100 + "%" : x + "px"; }; proto.getYValue = function (y) { var isHorizontal = this.layout._getOption("horizontal"); return this.layout.options.percentPosition && isHorizontal ? (y / this.layout.size.height) * 100 + "%" : y + "px"; }; proto._transitionTo = function (x, y) { this.getPosition(); // get current x & y from top/left var curX = this.position.x; var curY = this.position.y; var didNotMove = x == this.position.x && y == this.position.y; // save end position this.setPosition(x, y); // if did not move and not transitioning, just go to layout if (didNotMove && !this.isTransitioning) { this.layoutPosition(); return; } var transX = x - curX; var transY = y - curY; var transitionStyle = {}; transitionStyle.transform = this.getTranslate(transX, transY); this.transition({ to: transitionStyle, onTransitionEnd: { transform: this.layoutPosition, }, isCleaning: true, }); }; proto.getTranslate = function (x, y) { // flip cooridinates if origin on right or bottom var isOriginLeft = this.layout._getOption("originLeft"); var isOriginTop = this.layout._getOption("originTop"); x = isOriginLeft ? x : -x; y = isOriginTop ? y : -y; return "translate3d(" + x + "px, " + y + "px, 0)"; }; // non transition + transform support proto.goTo = function (x, y) { this.setPosition(x, y); this.layoutPosition(); }; proto.moveTo = proto._transitionTo; proto.setPosition = function (x, y) { this.position.x = parseFloat(x); this.position.y = parseFloat(y); }; // ----- transition ----- // /** * @param {Object} style - CSS * @param {Function} onTransitionEnd */ // non transition, just trigger callback proto._nonTransition = function (args) { this.css(args.to); if (args.isCleaning) { this._removeStyles(args.to); } for (var prop in args.onTransitionEnd) { args.onTransitionEnd[prop].call(this); } }; /** * proper transition * @param {Object} args - arguments * @param {Object} to - style to transition to * @param {Object} from - style to start transition from * @param {Boolean} isCleaning - removes transition styles after transition * @param {Function} onTransitionEnd - callback */ proto.transition = function (args) { // redirect to nonTransition if no transition duration if (!parseFloat(this.layout.options.transitionDuration)) { this._nonTransition(args); return; } var _transition = this._transn; // keep track of onTransitionEnd callback by css property for (var prop in args.onTransitionEnd) { _transition.onEnd[prop] = args.onTransitionEnd[prop]; } // keep track of properties that are transitioning for (prop in args.to) { _transition.ingProperties[prop] = true; // keep track of properties to clean up when transition is done if (args.isCleaning) { _transition.clean[prop] = true; } } // set from styles if (args.from) { this.css(args.from); // force redraw. http://blog.alexmaccaw.com/css-transitions var h = this.element.offsetHeight; // hack for JSHint to hush about unused var h = null; } // enable transition this.enableTransition(args.to); // set styles that are transitioning this.css(args.to); this.isTransitioning = true; }; // dash before all cap letters, including first for // WebkitTransform => -webkit-transform function toDashedAll(str) { return str.replace(/([A-Z])/g, function ($1) { return "-" + $1.toLowerCase(); }); } var transitionProps = "opacity," + toDashedAll(transformProperty); proto.enableTransition = function (/* style */) { // HACK changing transitionProperty during a transition // will cause transition to jump if (this.isTransitioning) { return; } // make `transition: foo, bar, baz` from style object // HACK un-comment this when enableTransition can work // while a transition is happening // var transitionValues = []; // for ( var prop in style ) { // // dash-ify camelCased properties like WebkitTransition // prop = vendorProperties[ prop ] || prop; // transitionValues.push( toDashedAll( prop ) ); // } // munge number to millisecond, to match stagger var duration = this.layout.options.transitionDuration; duration = typeof duration == "number" ? duration + "ms" : duration; // enable transition styles this.css({ transitionProperty: transitionProps, transitionDuration: duration, transitionDelay: this.staggerDelay || 0, }); // listen for transition end event this.element.addEventListener(transitionEndEvent, this, false); }; // ----- events ----- // proto.onwebkitTransitionEnd = function (event) { this.ontransitionend(event); }; proto.onotransitionend = function (event) { this.ontransitionend(event); }; // properties that I munge to make my life easier var dashedVendorProperties = { "-webkit-transform": "transform", }; proto.ontransitionend = function (event) { // disregard bubbled events from children if (event.target !== this.element) { return; } var _transition = this._transn; // get property name of transitioned property, convert to prefix-free var propertyName = dashedVendorProperties[event.propertyName] || event.propertyName; // remove property that has completed transitioning delete _transition.ingProperties[propertyName]; // check if any properties are still transitioning if (isEmptyObj(_transition.ingProperties)) { // all properties have completed transitioning this.disableTransition(); } // clean style if (propertyName in _transition.clean) { // clean up style this.element.style[event.propertyName] = ""; delete _transition.clean[propertyName]; } // trigger onTransitionEnd callback if (propertyName in _transition.onEnd) { var onTransitionEnd = _transition.onEnd[propertyName]; onTransitionEnd.call(this); delete _transition.onEnd[propertyName]; } this.emitEvent("transitionEnd", [this]); }; proto.disableTransition = function () { this.removeTransitionStyles(); this.element.removeEventListener(transitionEndEvent, this, false); this.isTransitioning = false; }; /** * removes style property from element * @param {Object} style **/ proto._removeStyles = function (style) { // clean up transition styles var cleanStyle = {}; for (var prop in style) { cleanStyle[prop] = ""; } this.css(cleanStyle); }; var cleanTransitionStyle = { transitionProperty: "", transitionDuration: "", transitionDelay: "", }; proto.removeTransitionStyles = function () { // remove transition this.css(cleanTransitionStyle); }; // ----- stagger ----- // proto.stagger = function (delay) { delay = isNaN(delay) ? 0 : delay; this.staggerDelay = delay + "ms"; }; // ----- show/hide/remove ----- // // remove element from DOM proto.removeElem = function () { this.element.parentNode.removeChild(this.element); // remove display: none this.css({ display: "" }); this.emitEvent("remove", [this]); }; proto.remove = function () { // just remove element if no transition support or no transition if ( !transitionProperty || !parseFloat(this.layout.options.transitionDuration) ) { this.removeElem(); return; } // start transition this.once("transitionEnd", function () { this.removeElem(); }); this.hide(); }; proto.reveal = function () { delete this.isHidden; // remove display: none this.css({ display: "" }); var options = this.layout.options; var onTransitionEnd = {}; var transitionEndProperty = this.getHideRevealTransitionEndProperty("visibleStyle"); onTransitionEnd[transitionEndProperty] = this.onRevealTransitionEnd; this.transition({ from: options.hiddenStyle, to: options.visibleStyle, isCleaning: true, onTransitionEnd: onTransitionEnd, }); }; proto.onRevealTransitionEnd = function () { // check if still visible // during transition, item may have been hidden if (!this.isHidden) { this.emitEvent("reveal"); } }; /** * get style property use for hide/reveal transition end * @param {String} styleProperty - hiddenStyle/visibleStyle * @returns {String} */ proto.getHideRevealTransitionEndProperty = function (styleProperty) { var optionStyle = this.layout.options[styleProperty]; // use opacity if (optionStyle.opacity) { return "opacity"; } // get first property for (var prop in optionStyle) { return prop; } }; proto.hide = function () { // set flag this.isHidden = true; // remove display: none this.css({ display: "" }); var options = this.layout.options; var onTransitionEnd = {}; var transitionEndProperty = this.getHideRevealTransitionEndProperty("hiddenStyle"); onTransitionEnd[transitionEndProperty] = this.onHideTransitionEnd; this.transition({ from: options.visibleStyle, to: options.hiddenStyle, // keep hidden stuff hidden isCleaning: true, onTransitionEnd: onTransitionEnd, }); }; proto.onHideTransitionEnd = function () { // check if still hidden // during transition, item may have been un-hidden if (this.isHidden) { this.css({ display: "none" }); this.emitEvent("hide"); } }; proto.destroy = function () { this.css({ position: "", left: "", right: "", top: "", bottom: "", transition: "", transform: "", }); }; return Item; }); /*! * Outlayer v2.1.1 * the brains and guts of a layout library * MIT license */ (function (window, factory) { "use strict"; // universal module definition /* jshint strict: false */ /* globals define, module, require */ if (typeof define == "function" && define.amd) { // AMD - RequireJS define("outlayer/outlayer", [ "ev-emitter/ev-emitter", "get-size/get-size", "fizzy-ui-utils/utils", "./item", ], function (EvEmitter, getSize, utils, Item) { return factory(window, EvEmitter, getSize, utils, Item); }); } else if (typeof module == "object" && module.exports) { // CommonJS - Browserify, Webpack module.exports = factory( window, require("ev-emitter"), require("get-size"), require("fizzy-ui-utils"), require("./item") ); } else { // browser global window.Outlayer = factory( window, window.EvEmitter, window.getSize, window.fizzyUIUtils, window.Outlayer.Item ); } })(window, function factory(window, EvEmitter, getSize, utils, Item) { "use strict"; // ----- vars ----- // var console = window.console; var jQuery = window.jQuery; var noop = function () {}; // -------------------------- Outlayer -------------------------- // // globally unique identifiers var GUID = 0; // internal store of all Outlayer intances var instances = {}; /** * @param {Element, String} element * @param {Object} options * @constructor */ function Outlayer(element, options) { var queryElement = utils.getQueryElement(element); if (!queryElement) { if (console) { console.error( "Bad element for " + this.constructor.namespace + ": " + (queryElement || element) ); } return; } this.element = queryElement; // add jQuery if (jQuery) { this.$element = jQuery(this.element); } // options this.options = utils.extend({}, this.constructor.defaults); this.option(options); // add id for Outlayer.getFromElement var id = ++GUID; this.element.outlayerGUID = id; // expando instances[id] = this; // associate via id // kick it off this._create(); var isInitLayout = this._getOption("initLayout"); if (isInitLayout) { this.layout(); } } // settings are for internal use only Outlayer.namespace = "outlayer"; Outlayer.Item = Item; // default options Outlayer.defaults = { containerStyle: { position: "relative", }, initLayout: true, originLeft: true, originTop: true, resize: true, resizeContainer: true, // item options transitionDuration: "0.4s", hiddenStyle: { opacity: 0, transform: "scale(0.001)", }, visibleStyle: { opacity: 1, transform: "scale(1)", }, }; var proto = Outlayer.prototype; // inherit EvEmitter utils.extend(proto, EvEmitter.prototype); /** * set options * @param {Object} opts */ proto.option = function (opts) { utils.extend(this.options, opts); }; /** * get backwards compatible option value, check old name */ proto._getOption = function (option) { var oldOption = this.constructor.compatOptions[option]; return oldOption && this.options[oldOption] !== undefined ? this.options[oldOption] : this.options[option]; }; Outlayer.compatOptions = { // currentName: oldName initLayout: "isInitLayout", horizontal: "isHorizontal", layoutInstant: "isLayoutInstant", originLeft: "isOriginLeft", originTop: "isOriginTop", resize: "isResizeBound", resizeContainer: "isResizingContainer", }; proto._create = function () { // get items from children this.reloadItems(); // elements that affect layout, but are not laid out this.stamps = []; this.stamp(this.options.stamp); // set container style utils.extend(this.element.style, this.options.containerStyle); // bind resize method var canBindResize = this._getOption("resize"); if (canBindResize) { this.bindResize(); } }; // goes through all children again and gets bricks in proper order proto.reloadItems = function () { // collection of item elements this.items = this._itemize(this.element.children); }; /** * turn elements into Outlayer.Items to be used in layout * @param {Array or NodeList or HTMLElement} elems * @returns {Array} items - collection of new Outlayer Items */ proto._itemize = function (elems) { var itemElems = this._filterFindItemElements(elems); var Item = this.constructor.Item; // create new Outlayer Items for collection var items = []; for (var i = 0; i < itemElems.length; i++) { var elem = itemElems[i]; var item = new Item(elem, this); items.push(item); } return items; }; /** * get item elements to be used in layout * @param {Array or NodeList or HTMLElement} elems * @returns {Array} items - item elements */ proto._filterFindItemElements = function (elems) { return utils.filterFindElements(elems, this.options.itemSelector); }; /** * getter method for getting item elements * @returns {Array} elems - collection of item elements */ proto.getItemElements = function () { return this.items.map(function (item) { return item.element; }); }; // ----- init & layout ----- // /** * lays out all items */ proto.layout = function () { this._resetLayout(); this._manageStamps(); // don't animate first layout var layoutInstant = this._getOption("layoutInstant"); var isInstant = layoutInstant !== undefined ? layoutInstant : !this._isLayoutInited; this.layoutItems(this.items, isInstant); // flag for initalized this._isLayoutInited = true; }; // _init is alias for layout proto._init = proto.layout; /** * logic before any new layout */ proto._resetLayout = function () { this.getSize(); }; proto.getSize = function () { this.size = getSize(this.element); }; /** * get measurement from option, for columnWidth, rowHeight, gutter * if option is String -> get element from selector string, & get size of element * if option is Element -> get size of element * else use option as a number * * @param {String} measurement * @param {String} size - width or height * @private */ proto._getMeasurement = function (measurement, size) { var option = this.options[measurement]; var elem; if (!option) { // default to 0 this[measurement] = 0; } else { // use option as an element if (typeof option == "string") { elem = this.element.querySelector(option); } else if (option instanceof HTMLElement) { elem = option; } // use size of element, if element this[measurement] = elem ? getSize(elem)[size] : option; } }; /** * layout a collection of item elements * @api public */ proto.layoutItems = function (items, isInstant) { items = this._getItemsForLayout(items); this._layoutItems(items, isInstant); this._postLayout(); }; /** * get the items to be laid out * you may want to skip over some items * @param {Array} items * @returns {Array} items */ proto._getItemsForLayout = function (items) { return items.filter(function (item) { return !item.isIgnored; }); }; /** * layout items * @param {Array} items * @param {Boolean} isInstant */ proto._layoutItems = function (items, isInstant) { this._emitCompleteOnItems("layout", items); if (!items || !items.length) { // no items, emit event with empty array return; } var queue = []; items.forEach(function (item) { // get x/y object from method var position = this._getItemLayoutPosition(item); // enqueue position.item = item; position.isInstant = isInstant || item.isLayoutInstant; queue.push(position); }, this); this._processLayoutQueue(queue); }; /** * get item layout position * @param {Outlayer.Item} item * @returns {Object} x and y position */ proto._getItemLayoutPosition = function (/* item */) { return { x: 0, y: 0, }; }; /** * iterate over array and position each item * Reason being - separating this logic prevents 'layout invalidation' * thx @paul_irish * @param {Array} queue */ proto._processLayoutQueue = function (queue) { this.updateStagger(); queue.forEach(function (obj, i) { this._positionItem(obj.item, obj.x, obj.y, obj.isInstant, i); }, this); }; // set stagger from option in milliseconds number proto.updateStagger = function () { var stagger = this.options.stagger; if (stagger === null || stagger === undefined) { this.stagger = 0; return; } this.stagger = getMilliseconds(stagger); return this.stagger; }; /** * Sets position of item in DOM * @param {Outlayer.Item} item * @param {Number} x - horizontal position * @param {Number} y - vertical position * @param {Boolean} isInstant - disables transitions */ proto._positionItem = function (item, x, y, isInstant, i) { if (isInstant) { // if not transition, just set CSS item.goTo(x, y); } else { item.stagger(i * this.stagger); item.moveTo(x, y); } }; /** * Any logic you want to do after each layout, * i.e. size the container */ proto._postLayout = function () { this.resizeContainer(); }; proto.resizeContainer = function () { var isResizingContainer = this._getOption("resizeContainer"); if (!isResizingContainer) { return; } var size = this._getContainerSize(); if (size) { this._setContainerMeasure(size.width, true); this._setContainerMeasure(size.height, false); } }; /** * Sets width or height of container if returned * @returns {Object} size * @param {Number} width * @param {Number} height */ proto._getContainerSize = noop; /** * @param {Number} measure - size of width or height * @param {Boolean} isWidth */ proto._setContainerMeasure = function (measure, isWidth) { if (measure === undefined) { return; } var elemSize = this.size; // add padding and border width if border box if (elemSize.isBorderBox) { measure += isWidth ? elemSize.paddingLeft + elemSize.paddingRight + elemSize.borderLeftWidth + elemSize.borderRightWidth : elemSize.paddingBottom + elemSize.paddingTop + elemSize.borderTopWidth + elemSize.borderBottomWidth; } measure = Math.max(measure, 0); this.element.style[isWidth ? "width" : "height"] = measure + "px"; }; /** * emit eventComplete on a collection of items events * @param {String} eventName * @param {Array} items - Outlayer.Items */ proto._emitCompleteOnItems = function (eventName, items) { var _this = this; function onComplete() { _this.dispatchEvent(eventName + "Complete", null, [items]); } var count = items.length; if (!items || !count) { onComplete(); return; } var doneCount = 0; function tick() { doneCount++; if (doneCount == count) { onComplete(); } } // bind callback items.forEach(function (item) { item.once(eventName, tick); }); }; /** * emits events via EvEmitter and jQuery events * @param {String} type - name of event * @param {Event} event - original event * @param {Array} args - extra arguments */ proto.dispatchEvent = function (type, event, args) { // add original event to arguments var emitArgs = event ? [event].concat(args) : args; this.emitEvent(type, emitArgs); if (jQuery) { // set this.$element this.$element = this.$element || jQuery(this.element); if (event) { // create jQuery event var $event = jQuery.Event(event); $event.type = type; this.$element.trigger($event, args); } else { // just trigger with type if no event available this.$element.trigger(type, args); } } }; // -------------------------- ignore & stamps -------------------------- // /** * keep item in collection, but do not lay it out * ignored items do not get skipped in layout * @param {Element} elem */ proto.ignore = function (elem) { var item = this.getItem(elem); if (item) { item.isIgnored = true; } }; /** * return item to layout collection * @param {Element} elem */ proto.unignore = function (elem) { var item = this.getItem(elem); if (item) { delete item.isIgnored; } }; /** * adds elements to stamps * @param {NodeList, Array, Element, or String} elems */ proto.stamp = function (elems) { elems = this._find(elems); if (!elems) { return; } this.stamps = this.stamps.concat(elems); // ignore elems.forEach(this.ignore, this); }; /** * removes elements to stamps * @param {NodeList, Array, or Element} elems */ proto.unstamp = function (elems) { elems = this._find(elems); if (!elems) { return; } elems.forEach(function (elem) { // filter out removed stamp elements utils.removeFrom(this.stamps, elem); this.unignore(elem); }, this); }; /** * finds child elements * @param {NodeList, Array, Element, or String} elems * @returns {Array} elems */ proto._find = function (elems) { if (!elems) { return; } // if string, use argument as selector string if (typeof elems == "string") { elems = this.element.querySelectorAll(elems); } elems = utils.makeArray(elems); return elems; }; proto._manageStamps = function () { if (!this.stamps || !this.stamps.length) { return; } this._getBoundingRect(); this.stamps.forEach(this._manageStamp, this); }; // update boundingLeft / Top proto._getBoundingRect = function () { // get bounding rect for container element var boundingRect = this.element.getBoundingClientRect(); var size = this.size; this._boundingRect = { left: boundingRect.left + size.paddingLeft + size.borderLeftWidth, top: boundingRect.top + size.paddingTop + size.borderTopWidth, right: boundingRect.right - (size.paddingRight + size.borderRightWidth), bottom: boundingRect.bottom - (size.paddingBottom + size.borderBottomWidth), }; }; /** * @param {Element} stamp **/ proto._manageStamp = noop; /** * get x/y position of element relative to container element * @param {Element} elem * @returns {Object} offset - has left, top, right, bottom */ proto._getElementOffset = function (elem) { var boundingRect = elem.getBoundingClientRect(); var thisRect = this._boundingRect; var size = getSize(elem); var offset = { left: boundingRect.left - thisRect.left - size.marginLeft, top: boundingRect.top - thisRect.top - size.marginTop, right: thisRect.right - boundingRect.right - size.marginRight, bottom: thisRect.bottom - boundingRect.bottom - size.marginBottom, }; return offset; }; // -------------------------- resize -------------------------- // // enable event handlers for listeners // i.e. resize -> onresize proto.handleEvent = utils.handleEvent; /** * Bind layout to window resizing */ proto.bindResize = function () { window.addEventListener("resize", this); this.isResizeBound = true; }; /** * Unbind layout to window resizing */ proto.unbindResize = function () { window.removeEventListener("resize", this); this.isResizeBound = false; }; proto.onresize = function () { this.resize(); }; utils.debounceMethod(Outlayer, "onresize", 100); proto.resize = function () { // don't trigger if size did not change // or if resize was unbound. See #9 if (!this.isResizeBound || !this.needsResizeLayout()) { return; } this.layout(); }; /** * check if layout is needed post layout * @returns Boolean */ proto.needsResizeLayout = function () { var size = getSize(this.element); // check that this.size and size are there // IE8 triggers resize on body size change, so they might not be var hasSizes = this.size && size; return hasSizes && size.innerWidth !== this.size.innerWidth; }; // -------------------------- methods -------------------------- // /** * add items to Outlayer instance * @param {Array or NodeList or Element} elems * @returns {Array} items - Outlayer.Items **/ proto.addItems = function (elems) { var items = this._itemize(elems); // add items to collection if (items.length) { this.items = this.items.concat(items); } return items; }; /** * Layout newly-appended item elements * @param {Array or NodeList or Element} elems */ proto.appended = function (elems) { var items = this.addItems(elems); if (!items.length) { return; } // layout and reveal just the new items this.layoutItems(items, true); this.reveal(items); }; /** * Layout prepended elements * @param {Array or NodeList or Element} elems */ proto.prepended = function (elems) { var items = this._itemize(elems); if (!items.length) { return; } // add items to beginning of collection var previousItems = this.items.slice(0); this.items = items.concat(previousItems); // start new layout this._resetLayout(); this._manageStamps(); // layout new stuff without transition this.layoutItems(items, true); this.reveal(items); // layout previous items this.layoutItems(previousItems); }; /** * reveal a collection of items * @param {Array of Outlayer.Items} items */ proto.reveal = function (items) { this._emitCompleteOnItems("reveal", items); if (!items || !items.length) { return; } var stagger = this.updateStagger(); items.forEach(function (item, i) { item.stagger(i * stagger); item.reveal(); }); }; /** * hide a collection of items * @param {Array of Outlayer.Items} items */ proto.hide = function (items) { this._emitCompleteOnItems("hide", items); if (!items || !items.length) { return; } var stagger = this.updateStagger(); items.forEach(function (item, i) { item.stagger(i * stagger); item.hide(); }); }; /** * reveal item elements * @param {Array}, {Element}, {NodeList} items */ proto.revealItemElements = function (elems) { var items = this.getItems(elems); this.reveal(items); }; /** * hide item elements * @param {Array}, {Element}, {NodeList} items */ proto.hideItemElements = function (elems) { var items = this.getItems(elems); this.hide(items); }; /** * get Outlayer.Item, given an Element * @param {Element} elem * @param {Function} callback * @returns {Outlayer.Item} item */ proto.getItem = function (elem) { // loop through items to get the one that matches for (var i = 0; i < this.items.length; i++) { var item = this.items[i]; if (item.element == elem) { // return item return item; } } }; /** * get collection of Outlayer.Items, given Elements * @param {Array} elems * @returns {Array} items - Outlayer.Items */ proto.getItems = function (elems) { elems = utils.makeArray(elems); var items = []; elems.forEach(function (elem) { var item = this.getItem(elem); if (item) { items.push(item); } }, this); return items; }; /** * remove element(s) from instance and DOM * @param {Array or NodeList or Element} elems */ proto.remove = function (elems) { var removeItems = this.getItems(elems); this._emitCompleteOnItems("remove", removeItems); // bail if no items to remove if (!removeItems || !removeItems.length) { return; } removeItems.forEach(function (item) { item.remove(); // remove item from collection utils.removeFrom(this.items, item); }, this); }; // ----- destroy ----- // // remove and disable Outlayer instance proto.destroy = function () { // clean up dynamic styles var style = this.element.style; style.height = ""; style.position = ""; style.width = ""; // destroy items this.items.forEach(function (item) { item.destroy(); }); this.unbindResize(); var id = this.element.outlayerGUID; delete instances[id]; // remove reference to instance by id delete this.element.outlayerGUID; // remove data for jQuery if (jQuery) { jQuery.removeData(this.element, this.constructor.namespace); } }; // -------------------------- data -------------------------- // /** * get Outlayer instance from element * @param {Element} elem * @returns {Outlayer} */ Outlayer.data = function (elem) { elem = utils.getQueryElement(elem); var id = elem && elem.outlayerGUID; return id && instances[id]; }; // -------------------------- create Outlayer class -------------------------- // /** * create a layout class * @param {String} namespace */ Outlayer.create = function (namespace, options) { // sub-class Outlayer var Layout = subclass(Outlayer); // apply new options and compatOptions Layout.defaults = utils.extend({}, Outlayer.defaults); utils.extend(Layout.defaults, options); Layout.compatOptions = utils.extend({}, Outlayer.compatOptions); Layout.namespace = namespace; Layout.data = Outlayer.data; // sub-class Item Layout.Item = subclass(Item); // -------------------------- declarative -------------------------- // utils.htmlInit(Layout, namespace); // -------------------------- jQuery bridge -------------------------- // // make into jQuery plugin if (jQuery && jQuery.bridget) { jQuery.bridget(namespace, Layout); } return Layout; }; function subclass(Parent) { function SubClass() { Parent.apply(this, arguments); } SubClass.prototype = Object.create(Parent.prototype); SubClass.prototype.constructor = SubClass; return SubClass; } // ----- helpers ----- // // how many milliseconds are in each unit var msUnits = { ms: 1, s: 1000, }; // munge time-like parameter into millisecond number // '0.4s' -> 40 function getMilliseconds(time) { if (typeof time == "number") { return time; } var matches = time.match(/(^\d*\.?\d*)(\w*)/); var num = matches && matches[1]; var unit = matches && matches[2]; if (!num.length) { return 0; } num = parseFloat(num); var mult = msUnits[unit] || 1; return num * mult; } // ----- fin ----- // // back in global Outlayer.Item = Item; return Outlayer; }); /** * Isotope Item **/ (function (window, factory) { // universal module definition /* jshint strict: false */ /*globals define, module, require */ if (typeof define == "function" && define.amd) { // AMD define("isotope-layout/js/item", ["outlayer/outlayer"], factory); } else if (typeof module == "object" && module.exports) { // CommonJS module.exports = factory(require("outlayer")); } else { // browser global window.Isotope = window.Isotope || {}; window.Isotope.Item = factory(window.Outlayer); } })(window, function factory(Outlayer) { "use strict"; // -------------------------- Item -------------------------- // // sub-class Outlayer Item function Item() { Outlayer.Item.apply(this, arguments); } var proto = (Item.prototype = Object.create(Outlayer.Item.prototype)); var _create = proto._create; proto._create = function () { // assign id, used for original-order sorting this.id = this.layout.itemGUID++; _create.call(this); this.sortData = {}; }; proto.updateSortData = function () { if (this.isIgnored) { return; } // default sorters this.sortData.id = this.id; // for backward compatibility this.sortData["original-order"] = this.id; this.sortData.random = Math.random(); // go thru getSortData obj and apply the sorters var getSortData = this.layout.options.getSortData; var sorters = this.layout._sorters; for (var key in getSortData) { var sorter = sorters[key]; this.sortData[key] = sorter(this.element, this); } }; var _destroy = proto.destroy; proto.destroy = function () { // call super _destroy.apply(this, arguments); // reset display, #741 this.css({ display: "", }); }; return Item; }); /** * Isotope LayoutMode */ (function (window, factory) { // universal module definition /* jshint strict: false */ /*globals define, module, require */ if (typeof define == "function" && define.amd) { // AMD define("isotope-layout/js/layout-mode", [ "get-size/get-size", "outlayer/outlayer", ], factory); } else if (typeof module == "object" && module.exports) { // CommonJS module.exports = factory(require("get-size"), require("outlayer")); } else { // browser global window.Isotope = window.Isotope || {}; window.Isotope.LayoutMode = factory(window.getSize, window.Outlayer); } })(window, function factory(getSize, Outlayer) { "use strict"; // layout mode class function LayoutMode(isotope) { this.isotope = isotope; // link properties if (isotope) { this.options = isotope.options[this.namespace]; this.element = isotope.element; this.items = isotope.filteredItems; this.size = isotope.size; } } var proto = LayoutMode.prototype; /** * some methods should just defer to default Outlayer method * and reference the Isotope instance as `this` **/ var facadeMethods = [ "_resetLayout", "_getItemLayoutPosition", "_manageStamp", "_getContainerSize", "_getElementOffset", "needsResizeLayout", "_getOption", ]; facadeMethods.forEach(function (methodName) { proto[methodName] = function () { return Outlayer.prototype[methodName].apply(this.isotope, arguments); }; }); // ----- ----- // // for horizontal layout modes, check vertical size proto.needsVerticalResizeLayout = function () { // don't trigger if size did not change var size = getSize(this.isotope.element); // check that this.size and size are there // IE8 triggers resize on body size change, so they might not be var hasSizes = this.isotope.size && size; return hasSizes && size.innerHeight != this.isotope.size.innerHeight; }; // ----- measurements ----- // proto._getMeasurement = function () { this.isotope._getMeasurement.apply(this, arguments); }; proto.getColumnWidth = function () { this.getSegmentSize("column", "Width"); }; proto.getRowHeight = function () { this.getSegmentSize("row", "Height"); }; /** * get columnWidth or rowHeight * segment: 'column' or 'row' * size 'Width' or 'Height' **/ proto.getSegmentSize = function (segment, size) { var segmentName = segment + size; var outerSize = "outer" + size; // columnWidth / outerWidth // rowHeight / outerHeight this._getMeasurement(segmentName, outerSize); // got rowHeight or columnWidth, we can chill if (this[segmentName]) { return; } // fall back to item of first element var firstItemSize = this.getFirstItemSize(); this[segmentName] = (firstItemSize && firstItemSize[outerSize]) || // or size of container this.isotope.size["inner" + size]; }; proto.getFirstItemSize = function () { var firstItem = this.isotope.filteredItems[0]; return firstItem && firstItem.element && getSize(firstItem.element); }; // ----- methods that should reference isotope ----- // proto.layout = function () { this.isotope.layout.apply(this.isotope, arguments); }; proto.getSize = function () { this.isotope.getSize(); this.size = this.isotope.size; }; // -------------------------- create -------------------------- // LayoutMode.modes = {}; LayoutMode.create = function (namespace, options) { function Mode() { LayoutMode.apply(this, arguments); } Mode.prototype = Object.create(proto); Mode.prototype.constructor = Mode; // default options if (options) { Mode.options = options; } Mode.prototype.namespace = namespace; // register in Isotope LayoutMode.modes[namespace] = Mode; return Mode; }; return LayoutMode; }); /*! * Masonry v4.2.1 * Cascading grid layout library * https://masonry.kecamatanndro.com * MIT License * by David kecamatanndro */ (function (window, factory) { // universal module definition /* jshint strict: false */ /*globals define, module, require */ if (typeof define == "function" && define.amd) { // AMD define("masonry-layout/masonry", [ "outlayer/outlayer", "get-size/get-size", ], factory); } else if (typeof module == "object" && module.exports) { // CommonJS module.exports = factory(require("outlayer"), require("get-size")); } else { // browser global window.Masonry = factory(window.Outlayer, window.getSize); } })(window, function factory(Outlayer, getSize) { // -------------------------- masonryDefinition -------------------------- // // create an Outlayer layout class var Masonry = Outlayer.create("masonry"); // isFitWidth -> fitWidth Masonry.compatOptions.fitWidth = "isFitWidth"; var proto = Masonry.prototype; proto._resetLayout = function () { this.getSize(); this._getMeasurement("columnWidth", "outerWidth"); this._getMeasurement("gutter", "outerWidth"); this.measureColumns(); // reset column Y this.colYs = []; for (var i = 0; i < this.cols; i++) { this.colYs.push(0); } this.maxY = 0; this.horizontalColIndex = 0; }; proto.measureColumns = function () { this.getContainerWidth(); // if columnWidth is 0, default to outerWidth of first item if (!this.columnWidth) { var firstItem = this.items[0]; var firstItemElem = firstItem && firstItem.element; // columnWidth fall back to item of first element this.columnWidth = (firstItemElem && getSize(firstItemElem).outerWidth) || // if first elem has no width, default to size of container this.containerWidth; } var columnWidth = (this.columnWidth += this.gutter); // calculate columns var containerWidth = this.containerWidth + this.gutter; var cols = containerWidth / columnWidth; // fix rounding errors, typically with gutters var excess = columnWidth - (containerWidth % columnWidth); // if overshoot is less than a pixel, round up, otherwise floor it var mathMethod = excess && excess < 1 ? "round" : "floor"; cols = Math[mathMethod](cols); this.cols = Math.max(cols, 1); }; proto.getContainerWidth = function () { // container is parent if fit width var isFitWidth = this._getOption("fitWidth"); var container = isFitWidth ? this.element.parentNode : this.element; // check that this.size and size are there // IE8 triggers resize on body size change, so they might not be var size = getSize(container); this.containerWidth = size && size.innerWidth; }; proto._getItemLayoutPosition = function (item) { item.getSize(); // how many columns does this brick span var remainder = item.size.outerWidth % this.columnWidth; var mathMethod = remainder && remainder < 1 ? "round" : "ceil"; // round if off by 1 pixel, otherwise use ceil var colSpan = Math[mathMethod](item.size.outerWidth / this.columnWidth); colSpan = Math.min(colSpan, this.cols); // use horizontal or top column position var colPosMethod = this.options.horizontalOrder ? "_getHorizontalColPosition" : "_getTopColPosition"; var colPosition = this[colPosMethod](colSpan, item); // position the brick var position = { x: this.columnWidth * colPosition.col, y: colPosition.y, }; // apply setHeight to necessary columns var setHeight = colPosition.y + item.size.outerHeight; var setMax = colSpan + colPosition.col; for (var i = colPosition.col; i < setMax; i++) { this.colYs[i] = setHeight; } return position; }; proto._getTopColPosition = function (colSpan) { var colGroup = this._getTopColGroup(colSpan); // get the minimum Y value from the columns var minimumY = Math.min.apply(Math, colGroup); return { col: colGroup.indexOf(minimumY), y: minimumY, }; }; /** * @param {Number} colSpan - number of columns the element spans * @returns {Array} colGroup */ proto._getTopColGroup = function (colSpan) { if (colSpan < 2) { // if brick spans only one column, use all the column Ys return this.colYs; } var colGroup = []; // how many different places could this brick fit horizontally var groupCount = this.cols + 1 - colSpan; // for each group potential horizontal position for (var i = 0; i < groupCount; i++) { colGroup[i] = this._getColGroupY(i, colSpan); } return colGroup; }; proto._getColGroupY = function (col, colSpan) { if (colSpan < 2) { return this.colYs[col]; } // make an array of colY values for that one group var groupColYs = this.colYs.slice(col, col + colSpan); // and get the max value of the array return Math.max.apply(Math, groupColYs); }; // get column position based on horizontal index. #873 proto._getHorizontalColPosition = function (colSpan, item) { var col = this.horizontalColIndex % this.cols; var isOver = colSpan > 1 && col + colSpan > this.cols; // shift to next row if item can't fit on current row col = isOver ? 0 : col; // don't let zero-size items take up space var hasSize = item.size.outerWidth && item.size.outerHeight; this.horizontalColIndex = hasSize ? col + colSpan : this.horizontalColIndex; return { col: col, y: this._getColGroupY(col, colSpan), }; }; proto._manageStamp = function (stamp) { var stampSize = getSize(stamp); var offset = this._getElementOffset(stamp); // get the columns that this stamp affects var isOriginLeft = this._getOption("originLeft"); var firstX = isOriginLeft ? offset.left : offset.right; var lastX = firstX + stampSize.outerWidth; var firstCol = Math.floor(firstX / this.columnWidth); firstCol = Math.max(0, firstCol); var lastCol = Math.floor(lastX / this.columnWidth); // lastCol should not go over if multiple of columnWidth #425 lastCol -= lastX % this.columnWidth ? 0 : 1; lastCol = Math.min(this.cols - 1, lastCol); // set colYs to bottom of the stamp var isOriginTop = this._getOption("originTop"); var stampMaxY = (isOriginTop ? offset.top : offset.bottom) + stampSize.outerHeight; for (var i = firstCol; i <= lastCol; i++) { this.colYs[i] = Math.max(stampMaxY, this.colYs[i]); } }; proto._getContainerSize = function () { this.maxY = Math.max.apply(Math, this.colYs); var size = { height: this.maxY, }; if (this._getOption("fitWidth")) { size.width = this._getContainerFitWidth(); } return size; }; proto._getContainerFitWidth = function () { var unusedCols = 0; // count unused columns var i = this.cols; while (--i) { if (this.colYs[i] !== 0) { break; } unusedCols++; } // fit container to columns that have been used return (this.cols - unusedCols) * this.columnWidth - this.gutter; }; proto.needsResizeLayout = function () { var previousWidth = this.containerWidth; this.getContainerWidth(); return previousWidth != this.containerWidth; }; return Masonry; }); /*! * Masonry layout mode * sub-classes Masonry * https://masonry.kecamatanndro.com */ (function (window, factory) { // universal module definition /* jshint strict: false */ /*globals define, module, require */ if (typeof define == "function" && define.amd) { // AMD define("isotope-layout/js/layout-modes/masonry", [ "../layout-mode", "masonry-layout/masonry", ], factory); } else if (typeof module == "object" && module.exports) { // CommonJS module.exports = factory( require("../layout-mode"), require("masonry-layout") ); } else { // browser global factory(window.Isotope.LayoutMode, window.Masonry); } })(window, function factory(LayoutMode, Masonry) { "use strict"; // -------------------------- masonryDefinition -------------------------- // // create an Outlayer layout class var MasonryMode = LayoutMode.create("masonry"); var proto = MasonryMode.prototype; var keepModeMethods = { _getElementOffset: true, layout: true, _getMeasurement: true, }; // inherit Masonry prototype for (var method in Masonry.prototype) { // do not inherit mode methods if (!keepModeMethods[method]) { proto[method] = Masonry.prototype[method]; } } var measureColumns = proto.measureColumns; proto.measureColumns = function () { // set items, used if measuring first item this.items = this.isotope.filteredItems; measureColumns.call(this); }; // point to mode options for fitWidth var _getOption = proto._getOption; proto._getOption = function (option) { if (option == "fitWidth") { return this.options.isFitWidth !== undefined ? this.options.isFitWidth : this.options.fitWidth; } return _getOption.apply(this.isotope, arguments); }; return MasonryMode; }); /** * fitRows layout mode */ (function (window, factory) { // universal module definition /* jshint strict: false */ /*globals define, module, require */ if (typeof define == "function" && define.amd) { // AMD define("isotope-layout/js/layout-modes/fit-rows", [ "../layout-mode", ], factory); } else if (typeof exports == "object") { // CommonJS module.exports = factory(require("../layout-mode")); } else { // browser global factory(window.Isotope.LayoutMode); } })(window, function factory(LayoutMode) { "use strict"; var FitRows = LayoutMode.create("fitRows"); var proto = FitRows.prototype; proto._resetLayout = function () { this.x = 0; this.y = 0; this.maxY = 0; this._getMeasurement("gutter", "outerWidth"); }; proto._getItemLayoutPosition = function (item) { item.getSize(); var itemWidth = item.size.outerWidth + this.gutter; // if this element cannot fit in the current row var containerWidth = this.isotope.size.innerWidth + this.gutter; if (this.x !== 0 && itemWidth + this.x > containerWidth) { this.x = 0; this.y = this.maxY; } var position = { x: this.x, y: this.y, }; this.maxY = Math.max(this.maxY, this.y + item.size.outerHeight); this.x += itemWidth; return position; }; proto._getContainerSize = function () { return { height: this.maxY }; }; return FitRows; }); /** * vertical layout mode */ (function (window, factory) { // universal module definition /* jshint strict: false */ /*globals define, module, require */ if (typeof define == "function" && define.amd) { // AMD define("isotope-layout/js/layout-modes/vertical", [ "../layout-mode", ], factory); } else if (typeof module == "object" && module.exports) { // CommonJS module.exports = factory(require("../layout-mode")); } else { // browser global factory(window.Isotope.LayoutMode); } })(window, function factory(LayoutMode) { "use strict"; var Vertical = LayoutMode.create("vertical", { horizontalAlignment: 0, }); var proto = Vertical.prototype; proto._resetLayout = function () { this.y = 0; }; proto._getItemLayoutPosition = function (item) { item.getSize(); var x = (this.isotope.size.innerWidth - item.size.outerWidth) * this.options.horizontalAlignment; var y = this.y; this.y += item.size.outerHeight; return { x: x, y: y }; }; proto._getContainerSize = function () { return { height: this.y }; }; return Vertical; }); /*! * Isotope v3.0.6 * * Licensed GPLv3 for open source use * or Isotope Commercial License for commercial use * * https://isotope.metafizzy.co * Copyright 2010-2018 Metafizzy */ (function (window, factory) { // universal module definition /* jshint strict: false */ /*globals define, module, require */ if (typeof define == "function" && define.amd) { // AMD define([ "outlayer/outlayer", "get-size/get-size", "kecamatanndro-matches-selector/matches-selector", "fizzy-ui-utils/utils", "isotope-layout/js/item", "isotope-layout/js/layout-mode", // include default layout modes "isotope-layout/js/layout-modes/masonry", "isotope-layout/js/layout-modes/fit-rows", "isotope-layout/js/layout-modes/vertical", ], function (Outlayer, getSize, matchesSelector, utils, Item, LayoutMode) { return factory( window, Outlayer, getSize, matchesSelector, utils, Item, LayoutMode ); }); } else if (typeof module == "object" && module.exports) { // CommonJS module.exports = factory( window, require("outlayer"), require("get-size"), require("kecamatanndro-matches-selector"), require("fizzy-ui-utils"), require("isotope-layout/js/item"), require("isotope-layout/js/layout-mode"), // include default layout modes require("isotope-layout/js/layout-modes/masonry"), require("isotope-layout/js/layout-modes/fit-rows"), require("isotope-layout/js/layout-modes/vertical") ); } else { // browser global window.Isotope = factory( window, window.Outlayer, window.getSize, window.matchesSelector, window.fizzyUIUtils, window.Isotope.Item, window.Isotope.LayoutMode ); } })( window, function factory( window, Outlayer, getSize, matchesSelector, utils, Item, LayoutMode ) { // -------------------------- vars -------------------------- // var jQuery = window.jQuery; // -------------------------- helpers -------------------------- // var trim = String.prototype.trim ? function (str) { return str.trim(); } : function (str) { return str.replace(/^\s+|\s+$/g, ""); }; // -------------------------- isotopeDefinition -------------------------- // // create an Outlayer layout class var Isotope = Outlayer.create("isotope", { layoutMode: "masonry", isJQueryFiltering: true, sortAscending: true, }); Isotope.Item = Item; Isotope.LayoutMode = LayoutMode; var proto = Isotope.prototype; proto._create = function () { this.itemGUID = 0; // functions that sort items this._sorters = {}; this._getSorters(); // call super Outlayer.prototype._create.call(this); // create layout modes this.modes = {}; // start filteredItems with all items this.filteredItems = this.items; // keep of track of sortBys this.sortHistory = ["original-order"]; // create from registered layout modes for (var name in LayoutMode.modes) { this._initLayoutMode(name); } }; proto.reloadItems = function () { // reset item ID counter this.itemGUID = 0; // call super Outlayer.prototype.reloadItems.call(this); }; proto._itemize = function () { var items = Outlayer.prototype._itemize.apply(this, arguments); // assign ID for original-order for (var i = 0; i < items.length; i++) { var item = items[i]; item.id = this.itemGUID++; } this._updateItemsSortData(items); return items; }; // -------------------------- layout -------------------------- // proto._initLayoutMode = function (name) { var Mode = LayoutMode.modes[name]; // set mode options // HACK extend initial options, back-fill in default options var initialOpts = this.options[name] || {}; this.options[name] = Mode.options ? utils.extend(Mode.options, initialOpts) : initialOpts; // init layout mode instance this.modes[name] = new Mode(this); }; proto.layout = function () { // if first time doing layout, do all magic if (!this._isLayoutInited && this._getOption("initLayout")) { this.arrange(); return; } this._layout(); }; // private method to be used in layout() & magic() proto._layout = function () { // don't animate first layout var isInstant = this._getIsInstant(); // layout flow this._resetLayout(); this._manageStamps(); this.layoutItems(this.filteredItems, isInstant); // flag for initalized this._isLayoutInited = true; }; // filter + sort + layout proto.arrange = function (opts) { // set any options pass this.option(opts); this._getIsInstant(); // filter, sort, and layout // filter var filtered = this._filter(this.items); this.filteredItems = filtered.matches; this._bindArrangeComplete(); if (this._isInstant) { this._noTransition(this._hideReveal, [filtered]); } else { this._hideReveal(filtered); } this._sort(); this._layout(); }; // alias to _init for main plugin method proto._init = proto.arrange; proto._hideReveal = function (filtered) { this.reveal(filtered.needReveal); this.hide(filtered.needHide); }; // HACK // Don't animate/transition first layout // Or don't animate/transition other layouts proto._getIsInstant = function () { var isLayoutInstant = this._getOption("layoutInstant"); var isInstant = isLayoutInstant !== undefined ? isLayoutInstant : !this._isLayoutInited; this._isInstant = isInstant; return isInstant; }; // listen for layoutComplete, hideComplete and revealComplete // to trigger arrangeComplete proto._bindArrangeComplete = function () { // listen for 3 events to trigger arrangeComplete var isLayoutComplete, isHideComplete, isRevealComplete; var _this = this; function arrangeParallelCallback() { if (isLayoutComplete && isHideComplete && isRevealComplete) { _this.dispatchEvent("arrangeComplete", null, [_this.filteredItems]); } } this.once("layoutComplete", function () { isLayoutComplete = true; arrangeParallelCallback(); }); this.once("hideComplete", function () { isHideComplete = true; arrangeParallelCallback(); }); this.once("revealComplete", function () { isRevealComplete = true; arrangeParallelCallback(); }); }; // -------------------------- filter -------------------------- // proto._filter = function (items) { var filter = this.options.filter; filter = filter || "*"; var matches = []; var hiddenMatched = []; var visibleUnmatched = []; var test = this._getFilterTest(filter); // test each item for (var i = 0; i < items.length; i++) { var item = items[i]; if (item.isIgnored) { continue; } // add item to either matched or unmatched group var isMatched = test(item); // item.isFilterMatched = isMatched; // add to matches if its a match if (isMatched) { matches.push(item); } // add to additional group if item needs to be hidden or revealed if (isMatched && item.isHidden) { hiddenMatched.push(item); } else if (!isMatched && !item.isHidden) { visibleUnmatched.push(item); } } // return collections of items to be manipulated return { matches: matches, needReveal: hiddenMatched, needHide: visibleUnmatched, }; }; // get a jQuery, function, or a matchesSelector test given the filter proto._getFilterTest = function (filter) { if (jQuery && this.options.isJQueryFiltering) { // use jQuery return function (item) { return jQuery(item.element).is(filter); }; } if (typeof filter == "function") { // use filter as function return function (item) { return filter(item.element); }; } // default, use filter as selector string return function (item) { return matchesSelector(item.element, filter); }; }; // -------------------------- sorting -------------------------- // /** * @params {Array} elems * @public */ proto.updateSortData = function (elems) { // get items var items; if (elems) { elems = utils.makeArray(elems); items = this.getItems(elems); } else { // update all items if no elems provided items = this.items; } this._getSorters(); this._updateItemsSortData(items); }; proto._getSorters = function () { var getSortData = this.options.getSortData; for (var key in getSortData) { var sorter = getSortData[key]; this._sorters[key] = mungeSorter(sorter); } }; /** * @params {Array} items - of Isotope.Items * @private */ proto._updateItemsSortData = function (items) { // do not update if no items var len = items && items.length; for (var i = 0; len && i < len; i++) { var item = items[i]; item.updateSortData(); } }; // ----- munge sorter ----- // // encapsulate this, as we just need mungeSorter // other functions in here are just for munging var mungeSorter = (function () { // add a magic layer to sorters for convienent shorthands // `.foo-bar` will use the text of .foo-bar querySelector // `[foo-bar]` will use attribute // you can also add parser // `.foo-bar parseInt` will parse that as a number function mungeSorter(sorter) { // if not a string, return function or whatever it is if (typeof sorter != "string") { return sorter; } // parse the sorter string var args = trim(sorter).split(" "); var query = args[0]; // check if query looks like [an-attribute] var attrMatch = query.match(/^\[(.+)\]$/); var attr = attrMatch && attrMatch[1]; var getValue = getValueGetter(attr, query); // use second argument as a parser var parser = Isotope.sortDataParsers[args[1]]; // parse the value, if there was a parser sorter = parser ? function (elem) { return elem && parser(getValue(elem)); } : // otherwise just return value function (elem) { return elem && getValue(elem); }; return sorter; } // get an attribute getter, or get text of the querySelector function getValueGetter(attr, query) { // if query looks like [foo-bar], get attribute if (attr) { return function getAttribute(elem) { return elem.getAttribute(attr); }; } // otherwise, assume its a querySelector, and get its text return function getChildText(elem) { var child = elem.querySelector(query); return child && child.textContent; }; } return mungeSorter; })(); // parsers used in getSortData shortcut strings Isotope.sortDataParsers = { parseInt: function (val) { return parseInt(val, 10); }, parseFloat: function (val) { return parseFloat(val); }, }; // ----- sort method ----- // // sort filteredItem order proto._sort = function () { if (!this.options.sortBy) { return; } // keep track of sortBy History var sortBys = utils.makeArray(this.options.sortBy); if (!this._getIsSameSortBy(sortBys)) { // concat all sortBy and sortHistory, add to front, oldest goes in last this.sortHistory = sortBys.concat(this.sortHistory); } // sort magic var itemSorter = getItemSorter( this.sortHistory, this.options.sortAscending ); this.filteredItems.sort(itemSorter); }; // check if sortBys is same as start of sortHistory proto._getIsSameSortBy = function (sortBys) { for (var i = 0; i < sortBys.length; i++) { if (sortBys[i] != this.sortHistory[i]) { return false; } } return true; }; // returns a function used for sorting function getItemSorter(sortBys, sortAsc) { return function sorter(itemA, itemB) { // cycle through all sortKeys for (var i = 0; i < sortBys.length; i++) { var sortBy = sortBys[i]; var a = itemA.sortData[sortBy]; var b = itemB.sortData[sortBy]; if (a > b || a < b) { // if sortAsc is an object, use the value given the sortBy key var isAscending = sortAsc[sortBy] !== undefined ? sortAsc[sortBy] : sortAsc; var direction = isAscending ? 1 : -1; return (a > b ? 1 : -1) * direction; } } return 0; }; } // -------------------------- methods -------------------------- // // get layout mode proto._mode = function () { var layoutMode = this.options.layoutMode; var mode = this.modes[layoutMode]; if (!mode) { // TODO console.error throw new Error("No layout mode: " + layoutMode); } // HACK sync mode's options // any options set after init for layout mode need to be synced mode.options = this.options[layoutMode]; return mode; }; proto._resetLayout = function () { // trigger original reset layout Outlayer.prototype._resetLayout.call(this); this._mode()._resetLayout(); }; proto._getItemLayoutPosition = function (item) { return this._mode()._getItemLayoutPosition(item); }; proto._manageStamp = function (stamp) { this._mode()._manageStamp(stamp); }; proto._getContainerSize = function () { return this._mode()._getContainerSize(); }; proto.needsResizeLayout = function () { return this._mode().needsResizeLayout(); }; // -------------------------- adding & removing -------------------------- // // HEADS UP overwrites default Outlayer appended proto.appended = function (elems) { var items = this.addItems(elems); if (!items.length) { return; } // filter, layout, reveal new items var filteredItems = this._filterRevealAdded(items); // add to filteredItems this.filteredItems = this.filteredItems.concat(filteredItems); }; // HEADS UP overwrites default Outlayer prepended proto.prepended = function (elems) { var items = this._itemize(elems); if (!items.length) { return; } // start new layout this._resetLayout(); this._manageStamps(); // filter, layout, reveal new items var filteredItems = this._filterRevealAdded(items); // layout previous items this.layoutItems(this.filteredItems); // add to items and filteredItems this.filteredItems = filteredItems.concat(this.filteredItems); this.items = items.concat(this.items); }; proto._filterRevealAdded = function (items) { var filtered = this._filter(items); this.hide(filtered.needHide); // reveal all new items this.reveal(filtered.matches); // layout new items, no transition this.layoutItems(filtered.matches, true); return filtered.matches; }; /** * Filter, sort, and layout newly-appended item elements * @param {Array or NodeList or Element} elems */ proto.insert = function (elems) { var items = this.addItems(elems); if (!items.length) { return; } // append item elements var i, item; var len = items.length; for (i = 0; i < len; i++) { item = items[i]; this.element.appendChild(item.element); } // filter new stuff var filteredInsertItems = this._filter(items).matches; // set flag for (i = 0; i < len; i++) { items[i].isLayoutInstant = true; } this.arrange(); // reset flag for (i = 0; i < len; i++) { delete items[i].isLayoutInstant; } this.reveal(filteredInsertItems); }; var _remove = proto.remove; proto.remove = function (elems) { elems = utils.makeArray(elems); var removeItems = this.getItems(elems); // do regular thing _remove.call(this, elems); // bail if no items to remove var len = removeItems && removeItems.length; // remove elems from filteredItems for (var i = 0; len && i < len; i++) { var item = removeItems[i]; // remove item from collection utils.removeFrom(this.filteredItems, item); } }; proto.shuffle = function () { // update random sortData for (var i = 0; i < this.items.length; i++) { var item = this.items[i]; item.sortData.random = Math.random(); } this.options.sortBy = "random"; this._sort(); this._layout(); }; /** * trigger fn without transition * kind of hacky to have this in the first place * @param {Function} fn * @param {Array} args * @returns ret * @private */ proto._noTransition = function (fn, args) { // save transitionDuration before disabling var transitionDuration = this.options.transitionDuration; // disable transition this.options.transitionDuration = 0; // do it var returnValue = fn.apply(this, args); // re-enable transition for reveal this.options.transitionDuration = transitionDuration; return returnValue; }; // ----- helper methods ----- // /** * getter method for getting filtered item elements * @returns {Array} elems - collection of item elements */ proto.getFilteredItemElements = function () { return this.filteredItems.map(function (item) { return item.element; }); }; // ----- ----- // return Isotope; } );