/*! * Masonry PACKAGED v4.2.2 * Cascading grid layout library * https://masonry.desandro.com * MIT License * by David DeSandro */ /** * 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. desandro/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('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 = 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', [ '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 ----- // 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; })); /*! * Masonry v4.2.2 * Cascading grid layout library * https://masonry.desandro.com * MIT License * by David DeSandro */ (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' ], 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; })); window.AScript.set("js-masonry.min", true);