/*!
 * Packery PACKAGED v2.1.1
 * Gapless, draggable grid layouts
 *
 * Licensed GPLv3 for open source use
 * or Packery Commercial License for commercial use
 *
 * http://packery.metafizzy.co
 * Copyright 2016 Metafizzy
 */

/**
 * Bridget makes jQuery widgets
 * v2.0.0
 * MIT license
 */

/* jshint browser: true, strict: true, undef: true, unused: true */

(function (window, factory) {
	'use strict';
	/* globals define: false, module: false, require: false */

	if (typeof define == 'function' && define.amd) {
		// AMD
		define('jquery-bridget/jquery-bridget', ['jquery'], function (jQuery) {
			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;
});

/*!
 * getSize v2.0.2
 * measure size of elements
 * MIT license
 */

/*jshint browser: true, strict: true, undef: true, unused: true */
/*global define: false, module: false, console: false */

(function (window, factory) {
	'use strict';

	if (typeof define == 'function' && define.amd) {
		// AMD
		define('get-size/get-size', [], function () {
			return 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 http://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 -------------------------- //

		/**
		 * WebKit measures the outer-width on style.width on border-box elems
		 * IE & 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);

		getSize.isBoxSizeOuter = isBoxSizeOuter =
			getStyleSize(style.width) == 200;
		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;
});

/**
 * EvEmitter v1.0.2
 * Lil' event emitter
 * MIT License
 */

/* jshint unused: true, undef: true, strict: true */

(function (global, factory) {
	// universal module definition
	/* jshint strict: false */ /* globals define, module */
	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();
	}
})(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;
		}
		var i = 0;
		var listener = listeners[i];
		args = args || [];
		// once stuff
		var onceListeners = this._onceEvents && this._onceEvents[eventName];

		while (listener) {
			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);
			// get next listener
			i += isOnce ? 0 : 1;
			listener = listeners[i];
		}

		return this;
	};

	return EvEmitter;
});

/**
 * matchesSelector v2.0.1
 * 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('desandro-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 = 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.1
 * 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', [
			'desandro-matches-selector/matches-selector',
		], function (matchesSelector) {
			return factory(window, matchesSelector);
		});
	} else if (typeof module == 'object' && module.exports) {
		// CommonJS
		module.exports = factory(window, require('desandro-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 ----- //

	// turn element or nodeList into an array
	utils.makeArray = function (obj) {
		var ary = [];
		if (Array.isArray(obj)) {
			// use object if already an array
			ary = obj;
		} else if (obj && typeof obj.length == 'number') {
			// convert nodeList to array
			for (var i = 0; i < obj.length; i++) {
				ary.push(obj[i]);
			}
		} else {
			// array of single index
			ary.push(obj);
		}
		return ary;
	};

	// ----- 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 != 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.isElement = function (o) {
		return typeof HTMLElement === 'object'
			? o instanceof HTMLElement //DOM2
			: o &&
					typeof o === 'object' &&
					o !== null &&
					o.nodeType === 1 &&
					typeof o.nodeName === 'string';
	};

	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 (!utils.isElement(elem)) {
				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) {
		// original method
		var method = _class.prototype[methodName];
		var timeoutName = methodName + 'Timeout';

		_class.prototype[methodName] = function () {
			var timeout = this[timeoutName];
			if (timeout) {
				clearTimeout(timeout);
			}
			var args = arguments;

			var _this = this;
			this[timeoutName] = setTimeout(function () {
				method.apply(_this, args);
				delete _this[timeoutName];
			}, threshold || 100);
		};
	};

	// ----- docReady ----- //

	utils.docReady = function (callback) {
		if (document.readyState == 'complete') {
			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('layoutname')
				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'];
		// convert percent to pixels
		var layoutSize = this.layout.size;
		var x =
			xValue.indexOf('%') != -1
				? (parseFloat(xValue) / 100) * layoutSize.width
				: parseInt(xValue, 10);
		var y =
			yValue.indexOf('%') != -1
				? (parseFloat(yValue) / 100) * layoutSize.height
				: parseInt(yValue, 10);

		// 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 compareX = parseInt(x, 10);
		var compareY = parseInt(y, 10);
		var didNotMove =
			compareX === this.position.x && compareY === 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 = parseInt(x, 10);
		this.position.y = parseInt(y, 10);
	};

	// ----- 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.0
 * 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;
});

/**
 * Rect
 * low-level utility class for basic geometry
 */

(function (window, factory) {
	// universal module definition
	/* jshint strict: false */ /* globals define, module */
	if (typeof define == 'function' && define.amd) {
		// AMD
		define('packery/js/rect', factory);
	} else if (typeof module == 'object' && module.exports) {
		// CommonJS
		module.exports = factory();
	} else {
		// browser global
		window.Packery = window.Packery || {};
		window.Packery.Rect = factory();
	}
})(window, function factory() {
	'use strict';

	// -------------------------- Rect -------------------------- //

	function Rect(props) {
		// extend properties from defaults
		for (var prop in Rect.defaults) {
			this[prop] = Rect.defaults[prop];
		}

		for (prop in props) {
			this[prop] = props[prop];
		}
	}

	Rect.defaults = {
		x: 0,
		y: 0,
		width: 0,
		height: 0,
	};

	var proto = Rect.prototype;

	/**
	 * Determines whether or not this rectangle wholly encloses another rectangle or point.
	 * @param {Rect} rect
	 * @returns {Boolean}
	 **/
	proto.contains = function (rect) {
		// points don't have width or height
		var otherWidth = rect.width || 0;
		var otherHeight = rect.height || 0;
		return (
			this.x <= rect.x &&
			this.y <= rect.y &&
			this.x + this.width >= rect.x + otherWidth &&
			this.y + this.height >= rect.y + otherHeight
		);
	};

	/**
	 * Determines whether or not the rectangle intersects with another.
	 * @param {Rect} rect
	 * @returns {Boolean}
	 **/
	proto.overlaps = function (rect) {
		var thisRight = this.x + this.width;
		var thisBottom = this.y + this.height;
		var rectRight = rect.x + rect.width;
		var rectBottom = rect.y + rect.height;

		// http://stackoverflow.com/a/306332
		return (
			this.x < rectRight &&
			thisRight > rect.x &&
			this.y < rectBottom &&
			thisBottom > rect.y
		);
	};

	/**
	 * @param {Rect} rect - the overlapping rect
	 * @returns {Array} freeRects - rects representing the area around the rect
	 **/
	proto.getMaximalFreeRects = function (rect) {
		// if no intersection, return false
		if (!this.overlaps(rect)) {
			return false;
		}

		var freeRects = [];
		var freeRect;

		var thisRight = this.x + this.width;
		var thisBottom = this.y + this.height;
		var rectRight = rect.x + rect.width;
		var rectBottom = rect.y + rect.height;

		// top
		if (this.y < rect.y) {
			freeRect = new Rect({
				x: this.x,
				y: this.y,
				width: this.width,
				height: rect.y - this.y,
			});
			freeRects.push(freeRect);
		}

		// right
		if (thisRight > rectRight) {
			freeRect = new Rect({
				x: rectRight,
				y: this.y,
				width: thisRight - rectRight,
				height: this.height,
			});
			freeRects.push(freeRect);
		}

		// bottom
		if (thisBottom > rectBottom) {
			freeRect = new Rect({
				x: this.x,
				y: rectBottom,
				width: this.width,
				height: thisBottom - rectBottom,
			});
			freeRects.push(freeRect);
		}

		// left
		if (this.x < rect.x) {
			freeRect = new Rect({
				x: this.x,
				y: this.y,
				width: rect.x - this.x,
				height: this.height,
			});
			freeRects.push(freeRect);
		}

		return freeRects;
	};

	proto.canFit = function (rect) {
		return this.width >= rect.width && this.height >= rect.height;
	};

	return Rect;
});

/**
 * Packer
 * bin-packing algorithm
 */

(function (window, factory) {
	// universal module definition
	/* jshint strict: false */ /* globals define, module, require */
	if (typeof define == 'function' && define.amd) {
		// AMD
		define('packery/js/packer', ['./rect'], factory);
	} else if (typeof module == 'object' && module.exports) {
		// CommonJS
		module.exports = factory(require('./rect'));
	} else {
		// browser global
		var Packery = (window.Packery = window.Packery || {});
		Packery.Packer = factory(Packery.Rect);
	}
})(window, function factory(Rect) {
	'use strict';

	// -------------------------- Packer -------------------------- //

	/**
	 * @param {Number} width
	 * @param {Number} height
	 * @param {String} sortDirection
	 *   topLeft for vertical, leftTop for horizontal
	 */
	function Packer(width, height, sortDirection) {
		this.width = width || 0;
		this.height = height || 0;
		this.sortDirection = sortDirection || 'downwardLeftToRight';

		this.reset();
	}

	var proto = Packer.prototype;

	proto.reset = function () {
		this.spaces = [];

		var initialSpace = new Rect({
			x: 0,
			y: 0,
			width: this.width,
			height: this.height,
		});

		this.spaces.push(initialSpace);
		// set sorter
		this.sorter =
			sorters[this.sortDirection] || sorters.downwardLeftToRight;
	};

	// change x and y of rect to fit with in Packer's available spaces
	proto.pack = function (rect) {
		for (var i = 0; i < this.spaces.length; i++) {
			var space = this.spaces[i];
			if (space.canFit(rect)) {
				this.placeInSpace(rect, space);
				break;
			}
		}
	};

	proto.columnPack = function (rect) {
		for (var i = 0; i < this.spaces.length; i++) {
			var space = this.spaces[i];
			var canFitInSpaceColumn =
				space.x <= rect.x &&
				space.x + space.width >= rect.x + rect.width &&
				space.height >= rect.height - 0.01; // fudge number for rounding error
			if (canFitInSpaceColumn) {
				rect.y = space.y;
				this.placed(rect);
				break;
			}
		}
	};

	proto.rowPack = function (rect) {
		for (var i = 0; i < this.spaces.length; i++) {
			var space = this.spaces[i];
			var canFitInSpaceRow =
				space.y <= rect.y &&
				space.y + space.height >= rect.y + rect.height &&
				space.width >= rect.width - 0.01; // fudge number for rounding error
			if (canFitInSpaceRow) {
				rect.x = space.x;
				this.placed(rect);
				break;
			}
		}
	};

	proto.placeInSpace = function (rect, space) {
		// place rect in space
		rect.x = space.x;
		rect.y = space.y;

		this.placed(rect);
	};

	// update spaces with placed rect
	proto.placed = function (rect) {
		// update spaces
		var revisedSpaces = [];
		for (var i = 0; i < this.spaces.length; i++) {
			var space = this.spaces[i];
			var newSpaces = space.getMaximalFreeRects(rect);
			// add either the original space or the new spaces to the revised spaces
			if (newSpaces) {
				revisedSpaces.push.apply(revisedSpaces, newSpaces);
			} else {
				revisedSpaces.push(space);
			}
		}

		this.spaces = revisedSpaces;

		this.mergeSortSpaces();
	};

	proto.mergeSortSpaces = function () {
		// remove redundant spaces
		Packer.mergeRects(this.spaces);
		this.spaces.sort(this.sorter);
	};

	// add a space back
	proto.addSpace = function (rect) {
		this.spaces.push(rect);
		this.mergeSortSpaces();
	};

	// -------------------------- utility functions -------------------------- //

	/**
	 * Remove redundant rectangle from array of rectangles
	 * @param {Array} rects: an array of Rects
	 * @returns {Array} rects: an array of Rects
	 **/
	Packer.mergeRects = function (rects) {
		var i = 0;
		var rect = rects[i];

		rectLoop: while (rect) {
			var j = 0;
			var compareRect = rects[i + j];

			while (compareRect) {
				if (compareRect == rect) {
					j++; // next
				} else if (compareRect.contains(rect)) {
					// remove rect
					rects.splice(i, 1);
					rect = rects[i]; // set next rect
					continue rectLoop; // bail on compareLoop
				} else if (rect.contains(compareRect)) {
					// remove compareRect
					rects.splice(i + j, 1);
				} else {
					j++;
				}
				compareRect = rects[i + j]; // set next compareRect
			}
			i++;
			rect = rects[i];
		}

		return rects;
	};

	// -------------------------- sorters -------------------------- //

	// functions for sorting rects in order
	var sorters = {
		// top down, then left to right
		downwardLeftToRight: function (a, b) {
			return a.y - b.y || a.x - b.x;
		},
		// left to right, then top down
		rightwardTopToBottom: function (a, b) {
			return a.x - b.x || a.y - b.y;
		},
	};

	// --------------------------  -------------------------- //

	return Packer;
});

/**
 * Packery Item Element
 **/

(function (window, factory) {
	// universal module definition
	/* jshint strict: false */ /* globals define, module, require */
	if (typeof define == 'function' && define.amd) {
		// AMD
		define('packery/js/item', ['outlayer/outlayer', './rect'], factory);
	} else if (typeof module == 'object' && module.exports) {
		// CommonJS
		module.exports = factory(require('outlayer'), require('./rect'));
	} else {
		// browser global
		window.Packery.Item = factory(window.Outlayer, window.Packery.Rect);
	}
})(window, function factory(Outlayer, Rect) {
	'use strict';

	// -------------------------- Item -------------------------- //

	var docElemStyle = document.documentElement.style;

	var transformProperty =
		typeof docElemStyle.transform == 'string'
			? 'transform'
			: 'WebkitTransform';

	// sub-class Item
	var Item = function PackeryItem() {
		Outlayer.Item.apply(this, arguments);
	};

	var proto = (Item.prototype = Object.create(Outlayer.Item.prototype));

	var __create = proto._create;
	proto._create = function () {
		// call default _create logic
		__create.call(this);
		this.rect = new Rect();
	};

	var _moveTo = proto.moveTo;
	proto.moveTo = function (x, y) {
		// don't shift 1px while dragging
		var dx = Math.abs(this.position.x - x);
		var dy = Math.abs(this.position.y - y);

		var canHackGoTo =
			this.layout.dragItemCount &&
			!this.isPlacing &&
			!this.isTransitioning &&
			dx < 1 &&
			dy < 1;
		if (canHackGoTo) {
			this.goTo(x, y);
			return;
		}
		_moveTo.apply(this, arguments);
	};

	// -------------------------- placing -------------------------- //

	proto.enablePlacing = function () {
		this.removeTransitionStyles();
		// remove transform property from transition
		if (this.isTransitioning && transformProperty) {
			this.element.style[transformProperty] = 'none';
		}
		this.isTransitioning = false;
		this.getSize();
		this.layout._setRectSize(this.element, this.rect);
		this.isPlacing = true;
	};

	proto.disablePlacing = function () {
		this.isPlacing = false;
	};

	// -----  ----- //

	// remove element from DOM
	proto.removeElem = function () {
		this.element.parentNode.removeChild(this.element);
		// add space back to packer
		this.layout.packer.addSpace(this.rect);
		this.emitEvent('remove', [this]);
	};

	// ----- dropPlaceholder ----- //

	proto.showDropPlaceholder = function () {
		var dropPlaceholder = this.dropPlaceholder;
		if (!dropPlaceholder) {
			// create dropPlaceholder
			dropPlaceholder = this.dropPlaceholder =
				document.createElement('div');
			dropPlaceholder.className = 'packery-drop-placeholder';
			dropPlaceholder.style.position = 'absolute';
		}

		dropPlaceholder.style.width = this.size.width + 'px';
		dropPlaceholder.style.height = this.size.height + 'px';
		this.positionDropPlaceholder();
		this.layout.element.appendChild(dropPlaceholder);
	};

	proto.positionDropPlaceholder = function () {
		this.dropPlaceholder.style[transformProperty] =
			'translate(' + this.rect.x + 'px, ' + this.rect.y + 'px)';
	};

	proto.hideDropPlaceholder = function () {
		// only remove once, #333
		var parent = this.dropPlaceholder.parentNode;
		if (parent) {
			parent.removeChild(this.dropPlaceholder);
		}
	};

	// -----  ----- //

	return Item;
});

/*!
 * Packery v2.1.1
 * Gapless, draggable grid layouts
 *
 * Licensed GPLv3 for open source use
 * or Packery Commercial License for commercial use
 *
 * http://packery.metafizzy.co
 * Copyright 2016 Metafizzy
 */

(function (window, factory) {
	// universal module definition
	/* jshint strict: false */ /* globals define, module, require */
	if (typeof define == 'function' && define.amd) {
		// AMD
		define([
			'get-size/get-size',
			'outlayer/outlayer',
			'packery/js/rect',
			'packery/js/packer',
			'packery/js/item',
		], factory);
	} else if (typeof module == 'object' && module.exports) {
		// CommonJS
		module.exports = factory(
			require('get-size'),
			require('outlayer'),
			require('./rect'),
			require('./packer'),
			require('./item')
		);
	} else {
		// browser global
		window.Packery = factory(
			window.getSize,
			window.Outlayer,
			window.Packery.Rect,
			window.Packery.Packer,
			window.Packery.Item
		);
	}
})(window, function factory(getSize, Outlayer, Rect, Packer, Item) {
	'use strict';

	// ----- Rect ----- //

	// allow for pixel rounding errors IE8-IE11 & Firefox; #227
	Rect.prototype.canFit = function (rect) {
		return this.width >= rect.width - 1 && this.height >= rect.height - 1;
	};

	// -------------------------- Packery -------------------------- //

	// create an Outlayer layout class
	var Packery = Outlayer.create('packery');
	Packery.Item = Item;

	var proto = Packery.prototype;

	proto._create = function () {
		// call super
		Outlayer.prototype._create.call(this);

		// initial properties
		this.packer = new Packer();
		// packer for drop targets
		this.shiftPacker = new Packer();
		this.isEnabled = true;

		this.dragItemCount = 0;

		// create drag handlers
		var _this = this;
		this.handleDraggabilly = {
			dragStart: function () {
				_this.itemDragStart(this.element);
			},
			dragMove: function () {
				_this.itemDragMove(
					this.element,
					this.position.x,
					this.position.y
				);
			},
			dragEnd: function () {
				_this.itemDragEnd(this.element);
			},
		};

		this.handleUIDraggable = {
			start: function handleUIDraggableStart(event, ui) {
				// HTML5 may trigger dragstart, dismiss HTML5 dragging
				if (!ui) {
					return;
				}
				_this.itemDragStart(event.currentTarget);
			},
			drag: function handleUIDraggableDrag(event, ui) {
				if (!ui) {
					return;
				}
				_this.itemDragMove(
					event.currentTarget,
					ui.position.left,
					ui.position.top
				);
			},
			stop: function handleUIDraggableStop(event, ui) {
				if (!ui) {
					return;
				}
				_this.itemDragEnd(event.currentTarget);
			},
		};
	};

	// ----- init & layout ----- //

	/**
	 * logic before any new layout
	 */
	proto._resetLayout = function () {
		this.getSize();

		this._getMeasurements();

		// reset packer
		var width, height, sortDirection;
		// packer settings, if horizontal or vertical
		if (this._getOption('horizontal')) {
			width = Infinity;
			height = this.size.innerHeight + this.gutter;
			sortDirection = 'rightwardTopToBottom';
		} else {
			width = this.size.innerWidth + this.gutter;
			height = Infinity;
			sortDirection = 'downwardLeftToRight';
		}

		this.packer.width = this.shiftPacker.width = width;
		this.packer.height = this.shiftPacker.height = height;
		this.packer.sortDirection = this.shiftPacker.sortDirection =
			sortDirection;

		this.packer.reset();

		// layout
		this.maxY = 0;
		this.maxX = 0;
	};

	/**
	 * update columnWidth, rowHeight, & gutter
	 * @private
	 */
	proto._getMeasurements = function () {
		this._getMeasurement('columnWidth', 'width');
		this._getMeasurement('rowHeight', 'height');
		this._getMeasurement('gutter', 'width');
	};

	proto._getItemLayoutPosition = function (item) {
		this._setRectSize(item.element, item.rect);
		if (this.isShifting || this.dragItemCount > 0) {
			var packMethod = this._getPackMethod();
			this.packer[packMethod](item.rect);
		} else {
			this.packer.pack(item.rect);
		}

		this._setMaxXY(item.rect);
		return item.rect;
	};

	proto.shiftLayout = function () {
		this.isShifting = true;
		this.layout();
		delete this.isShifting;
	};

	proto._getPackMethod = function () {
		return this._getOption('horizontal') ? 'rowPack' : 'columnPack';
	};

	/**
	 * set max X and Y value, for size of container
	 * @param {Packery.Rect} rect
	 * @private
	 */
	proto._setMaxXY = function (rect) {
		this.maxX = Math.max(rect.x + rect.width, this.maxX);
		this.maxY = Math.max(rect.y + rect.height, this.maxY);
	};

	/**
	 * set the width and height of a rect, applying columnWidth and rowHeight
	 * @param {Element} elem
	 * @param {Packery.Rect} rect
	 */
	proto._setRectSize = function (elem, rect) {
		var size = getSize(elem);
		var w = size.outerWidth;
		var h = size.outerHeight;
		// size for columnWidth and rowHeight, if available
		// only check if size is non-zero, #177
		if (w || h) {
			w = this._applyGridGutter(w, this.columnWidth);
			h = this._applyGridGutter(h, this.rowHeight);
		}
		// rect must fit in packer
		rect.width = Math.min(w, this.packer.width);
		rect.height = Math.min(h, this.packer.height);
	};

	/**
	 * fits item to columnWidth/rowHeight and adds gutter
	 * @param {Number} measurement - item width or height
	 * @param {Number} gridSize - columnWidth or rowHeight
	 * @returns measurement
	 */
	proto._applyGridGutter = function (measurement, gridSize) {
		// just add gutter if no gridSize
		if (!gridSize) {
			return measurement + this.gutter;
		}
		gridSize += this.gutter;
		// fit item to columnWidth/rowHeight
		var remainder = measurement % gridSize;
		var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
		measurement = Math[mathMethod](measurement / gridSize) * gridSize;
		return measurement;
	};

	proto._getContainerSize = function () {
		if (this._getOption('horizontal')) {
			return {
				width: this.maxX - this.gutter,
			};
		} else {
			return {
				height: this.maxY - this.gutter,
			};
		}
	};

	// -------------------------- stamp -------------------------- //

	/**
	 * makes space for element
	 * @param {Element} elem
	 */
	proto._manageStamp = function (elem) {
		var item = this.getItem(elem);
		var rect;
		if (item && item.isPlacing) {
			rect = item.rect;
		} else {
			var offset = this._getElementOffset(elem);
			rect = new Rect({
				x: this._getOption('originLeft') ? offset.left : offset.right,
				y: this._getOption('originTop') ? offset.top : offset.bottom,
			});
		}

		this._setRectSize(elem, rect);
		// save its space in the packer
		this.packer.placed(rect);
		this._setMaxXY(rect);
	};

	// -------------------------- methods -------------------------- //

	function verticalSorter(a, b) {
		return a.position.y - b.position.y || a.position.x - b.position.x;
	}

	function horizontalSorter(a, b) {
		return a.position.x - b.position.x || a.position.y - b.position.y;
	}

	proto.sortItemsByPosition = function () {
		var sorter = this._getOption('horizontal')
			? horizontalSorter
			: verticalSorter;
		this.items.sort(sorter);
	};

	/**
	 * Fit item element in its current position
	 * Packery will position elements around it
	 * useful for expanding elements
	 *
	 * @param {Element} elem
	 * @param {Number} x - horizontal destination position, optional
	 * @param {Number} y - vertical destination position, optional
	 */
	proto.fit = function (elem, x, y) {
		var item = this.getItem(elem);
		if (!item) {
			return;
		}

		// stamp item to get it out of layout
		this.stamp(item.element);
		// set placing flag
		item.enablePlacing();
		this.updateShiftTargets(item);
		// fall back to current position for fitting
		x = x === undefined ? item.rect.x : x;
		y = y === undefined ? item.rect.y : y;
		// position it best at its destination
		this.shift(item, x, y);
		this._bindFitEvents(item);
		item.moveTo(item.rect.x, item.rect.y);
		// layout everything else
		this.shiftLayout();
		// return back to regularly scheduled programming
		this.unstamp(item.element);
		this.sortItemsByPosition();
		item.disablePlacing();
	};

	/**
	 * emit event when item is fit and other items are laid out
	 * @param {Packery.Item} item
	 * @private
	 */
	proto._bindFitEvents = function (item) {
		var _this = this;
		var ticks = 0;
		function onLayout() {
			ticks++;
			if (ticks != 2) {
				return;
			}
			_this.dispatchEvent('fitComplete', null, [item]);
		}
		// when item is laid out
		item.once('layout', onLayout);
		// when all items are laid out
		this.once('layoutComplete', onLayout);
	};

	// -------------------------- resize -------------------------- //

	// debounced, layout on resize
	proto.resize = function () {
		// don't trigger if size did not change
		// or if resize was unbound. See #285, outlayer#9
		if (!this.isResizeBound || !this.needsResizeLayout()) {
			return;
		}

		if (this.options.shiftPercentResize) {
			this.resizeShiftPercentLayout();
		} else {
			this.layout();
		}
	};

	/**
	 * check if layout is needed post layout
	 * @returns Boolean
	 */
	proto.needsResizeLayout = function () {
		var size = getSize(this.element);
		var innerSize = this._getOption('horizontal')
			? 'innerHeight'
			: 'innerWidth';
		return size[innerSize] != this.size[innerSize];
	};

	proto.resizeShiftPercentLayout = function () {
		var items = this._getItemsForLayout(this.items);

		var isHorizontal = this._getOption('horizontal');
		var coord = isHorizontal ? 'y' : 'x';
		var measure = isHorizontal ? 'height' : 'width';
		var segmentName = isHorizontal ? 'rowHeight' : 'columnWidth';
		var innerSize = isHorizontal ? 'innerHeight' : 'innerWidth';

		// proportional re-align items
		var previousSegment = this[segmentName];
		previousSegment = previousSegment && previousSegment + this.gutter;

		if (previousSegment) {
			this._getMeasurements();
			var currentSegment = this[segmentName] + this.gutter;
			items.forEach(function (item) {
				var seg = Math.round(item.rect[coord] / previousSegment);
				item.rect[coord] = seg * currentSegment;
			});
		} else {
			var currentSize = getSize(this.element)[innerSize] + this.gutter;
			var previousSize = this.packer[measure];
			items.forEach(function (item) {
				item.rect[coord] =
					(item.rect[coord] / previousSize) * currentSize;
			});
		}

		this.shiftLayout();
	};

	// -------------------------- drag -------------------------- //

	/**
	 * handle an item drag start event
	 * @param {Element} elem
	 */
	proto.itemDragStart = function (elem) {
		if (!this.isEnabled) {
			return;
		}
		this.stamp(elem);
		// this.ignore( elem );
		var item = this.getItem(elem);
		if (!item) {
			return;
		}

		item.enablePlacing();
		item.showDropPlaceholder();
		this.dragItemCount++;
		this.updateShiftTargets(item);
	};

	proto.updateShiftTargets = function (dropItem) {
		this.shiftPacker.reset();

		// pack stamps
		this._getBoundingRect();
		var isOriginLeft = this._getOption('originLeft');
		var isOriginTop = this._getOption('originTop');
		this.stamps.forEach(function (stamp) {
			// ignore dragged item
			var item = this.getItem(stamp);
			if (item && item.isPlacing) {
				return;
			}
			var offset = this._getElementOffset(stamp);
			var rect = new Rect({
				x: isOriginLeft ? offset.left : offset.right,
				y: isOriginTop ? offset.top : offset.bottom,
			});
			this._setRectSize(stamp, rect);
			// save its space in the packer
			this.shiftPacker.placed(rect);
		}, this);

		// reset shiftTargets
		var isHorizontal = this._getOption('horizontal');
		var segmentName = isHorizontal ? 'rowHeight' : 'columnWidth';
		var measure = isHorizontal ? 'height' : 'width';

		this.shiftTargetKeys = [];
		this.shiftTargets = [];
		var boundsSize;
		var segment = this[segmentName];
		segment = segment && segment + this.gutter;

		if (segment) {
			var segmentSpan = Math.ceil(dropItem.rect[measure] / segment);
			var segs = Math.floor(
				(this.shiftPacker[measure] + this.gutter) / segment
			);
			boundsSize = (segs - segmentSpan) * segment;
			// add targets on top
			for (var i = 0; i < segs; i++) {
				var initialX = isHorizontal ? 0 : i * segment;
				var initialY = isHorizontal ? i * segment : 0;
				this._addShiftTarget(initialX, initialY, boundsSize);
			}
		} else {
			boundsSize =
				this.shiftPacker[measure] +
				this.gutter -
				dropItem.rect[measure];
			this._addShiftTarget(0, 0, boundsSize);
		}

		// pack each item to measure where shiftTargets are
		var items = this._getItemsForLayout(this.items);
		var packMethod = this._getPackMethod();
		items.forEach(function (item) {
			var rect = item.rect;
			this._setRectSize(item.element, rect);
			this.shiftPacker[packMethod](rect);

			// add top left corner
			this._addShiftTarget(rect.x, rect.y, boundsSize);
			// add bottom left / top right corner
			var cornerX = isHorizontal ? rect.x + rect.width : rect.x;
			var cornerY = isHorizontal ? rect.y : rect.y + rect.height;
			this._addShiftTarget(cornerX, cornerY, boundsSize);

			if (segment) {
				// add targets for each column on bottom / row on right
				var segSpan = Math.round(rect[measure] / segment);
				for (var i = 1; i < segSpan; i++) {
					var segX = isHorizontal ? cornerX : rect.x + segment * i;
					var segY = isHorizontal ? rect.y + segment * i : cornerY;
					this._addShiftTarget(segX, segY, boundsSize);
				}
			}
		}, this);
	};

	proto._addShiftTarget = function (x, y, boundsSize) {
		var checkCoord = this._getOption('horizontal') ? y : x;
		if (checkCoord !== 0 && checkCoord > boundsSize) {
			return;
		}
		// create string for a key, easier to keep track of what targets
		var key = x + ',' + y;
		var hasKey = this.shiftTargetKeys.indexOf(key) != -1;
		if (hasKey) {
			return;
		}
		this.shiftTargetKeys.push(key);
		this.shiftTargets.push({ x: x, y: y });
	};

	// -------------------------- drop -------------------------- //

	proto.shift = function (item, x, y) {
		var shiftPosition;
		var minDistance = Infinity;
		var position = { x: x, y: y };
		this.shiftTargets.forEach(function (target) {
			var distance = getDistance(target, position);
			if (distance < minDistance) {
				shiftPosition = target;
				minDistance = distance;
			}
		});
		item.rect.x = shiftPosition.x;
		item.rect.y = shiftPosition.y;
	};

	function getDistance(a, b) {
		var dx = b.x - a.x;
		var dy = b.y - a.y;
		return Math.sqrt(dx * dx + dy * dy);
	}

	// -------------------------- drag move -------------------------- //

	var DRAG_THROTTLE_TIME = 120;

	/**
	 * handle an item drag move event
	 * @param {Element} elem
	 * @param {Number} x - horizontal change in position
	 * @param {Number} y - vertical change in position
	 */
	proto.itemDragMove = function (elem, x, y) {
		var item = this.isEnabled && this.getItem(elem);
		if (!item) {
			return;
		}

		x -= this.size.paddingLeft;
		y -= this.size.paddingTop;

		var _this = this;
		function onDrag() {
			_this.shift(item, x, y);
			item.positionDropPlaceholder();
			_this.layout();
		}

		// throttle
		var now = new Date();
		if (
			this._itemDragTime &&
			now - this._itemDragTime < DRAG_THROTTLE_TIME
		) {
			clearTimeout(this.dragTimeout);
			this.dragTimeout = setTimeout(onDrag, DRAG_THROTTLE_TIME);
		} else {
			onDrag();
			this._itemDragTime = now;
		}
	};

	// -------------------------- drag end -------------------------- //

	/**
	 * handle an item drag end event
	 * @param {Element} elem
	 */
	proto.itemDragEnd = function (elem) {
		var item = this.isEnabled && this.getItem(elem);
		if (!item) {
			return;
		}

		clearTimeout(this.dragTimeout);
		item.element.classList.add('is-positioning-post-drag');

		var completeCount = 0;
		var _this = this;
		function onDragEndLayoutComplete() {
			completeCount++;
			if (completeCount != 2) {
				return;
			}
			// reset drag item
			item.element.classList.remove('is-positioning-post-drag');
			item.hideDropPlaceholder();
			_this.dispatchEvent('dragItemPositioned', null, [item]);
		}

		item.once('layout', onDragEndLayoutComplete);
		this.once('layoutComplete', onDragEndLayoutComplete);
		item.moveTo(item.rect.x, item.rect.y);
		this.layout();
		this.dragItemCount = Math.max(0, this.dragItemCount - 1);
		this.sortItemsByPosition();
		item.disablePlacing();
		this.unstamp(item.element);
	};

	/**
	 * binds Draggabilly events
	 * @param {Draggabilly} draggie
	 */
	proto.bindDraggabillyEvents = function (draggie) {
		this._bindDraggabillyEvents(draggie, 'on');
	};

	proto.unbindDraggabillyEvents = function (draggie) {
		this._bindDraggabillyEvents(draggie, 'off');
	};

	proto._bindDraggabillyEvents = function (draggie, method) {
		var handlers = this.handleDraggabilly;
		draggie[method]('dragStart', handlers.dragStart);
		draggie[method]('dragMove', handlers.dragMove);
		draggie[method]('dragEnd', handlers.dragEnd);
	};

	/**
	 * binds jQuery UI Draggable events
	 * @param {jQuery} $elems
	 */
	proto.bindUIDraggableEvents = function ($elems) {
		this._bindUIDraggableEvents($elems, 'on');
	};

	proto.unbindUIDraggableEvents = function ($elems) {
		this._bindUIDraggableEvents($elems, 'off');
	};

	proto._bindUIDraggableEvents = function ($elems, method) {
		var handlers = this.handleUIDraggable;
		$elems[method]('dragstart', handlers.start)
			[method]('drag', handlers.drag)
			[method]('dragstop', handlers.stop);
	};

	// ----- destroy ----- //

	var _destroy = proto.destroy;
	proto.destroy = function () {
		_destroy.apply(this, arguments);
		// disable flag; prevent drag events from triggering. #72
		this.isEnabled = false;
	};

	// -----  ----- //

	Packery.Rect = Rect;
	Packery.Packer = Packer;

	return Packery;
});
