├── component.json ├── README.md ├── composer.json ├── masonry.pkgd.min.js └── masonry.pkgd.js /component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "masonry", 3 | "repo": "components/masonry", 4 | "description": "Cascading grid layout library", 5 | "version": "4.2.0", 6 | "scripts": ["masonry.pkgd.js"], 7 | "files": ["masonry.pkgd.min.js"], 8 | "license": "MIT", 9 | "main": "masonry.pkgd.js" 10 | } 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Masonry 2 | ======= 3 | 4 | Shim repository for [Masonry](http://masonry.desandro.com/). 5 | 6 | Package Managers 7 | ---------------- 8 | 9 | * [Component](https://github.com/component/component): `components/masonry` 10 | * [Composer](http://packagist.org/packages/components/masonry): `components/masonry` 11 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "components/masonry", 3 | "description": "Cascading grid layout library", 4 | "type": "component", 5 | "homepage": "http://masonry.desandro.com", 6 | "license": "MIT", 7 | "support": { 8 | "issues": "https://github.com/desandro/masonry/issues", 9 | "source": "https://github.com/desandro/masonry" 10 | }, 11 | "authors": [ 12 | { 13 | "name": "David DeSandro", 14 | "homepage": "http://desandro.com" 15 | } 16 | ], 17 | "extra": { 18 | "component": { 19 | "main": "masonry.pkgd.js", 20 | "scripts": [ 21 | "masonry.pkgd.js" 22 | ], 23 | "files": [ 24 | "masonry.pkgd.min.js" 25 | ], 26 | "shim": { 27 | "exports": "Masonry" 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /masonry.pkgd.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Masonry PACKAGED v4.2.0 3 | * Cascading grid layout library 4 | * http://masonry.desandro.com 5 | * MIT License 6 | * by David DeSandro 7 | */ 8 | 9 | !function(t,e){"function"==typeof define&&define.amd?define("jquery-bridget/jquery-bridget",["jquery"],function(i){return e(t,i)}):"object"==typeof module&&module.exports?module.exports=e(t,require("jquery")):t.jQueryBridget=e(t,t.jQuery)}(window,function(t,e){"use strict";function i(i,r,a){function h(t,e,n){var o,r="$()."+i+'("'+e+'")';return t.each(function(t,h){var u=a.data(h,i);if(!u)return void s(i+" not initialized. Cannot call methods, i.e. "+r);var d=u[e];if(!d||"_"==e.charAt(0))return void s(r+" is not a valid method");var l=d.apply(u,n);o=void 0===o?l:o}),void 0!==o?o:t}function u(t,e){t.each(function(t,n){var o=a.data(n,i);o?(o.option(e),o._init()):(o=new r(n,e),a.data(n,i,o))})}a=a||e||t.jQuery,a&&(r.prototype.option||(r.prototype.option=function(t){a.isPlainObject(t)&&(this.options=a.extend(!0,this.options,t))}),a.fn[i]=function(t){if("string"==typeof t){var e=o.call(arguments,1);return h(this,t,e)}return u(this,t),this},n(a))}function n(t){!t||t&&t.bridget||(t.bridget=i)}var o=Array.prototype.slice,r=t.console,s="undefined"==typeof r?function(){}:function(t){r.error(t)};return n(e||t.jQuery),i}),function(t,e){"function"==typeof define&&define.amd?define("ev-emitter/ev-emitter",e):"object"==typeof module&&module.exports?module.exports=e():t.EvEmitter=e()}("undefined"!=typeof window?window:this,function(){function t(){}var e=t.prototype;return e.on=function(t,e){if(t&&e){var i=this._events=this._events||{},n=i[t]=i[t]||[];return-1==n.indexOf(e)&&n.push(e),this}},e.once=function(t,e){if(t&&e){this.on(t,e);var i=this._onceEvents=this._onceEvents||{},n=i[t]=i[t]||{};return n[e]=!0,this}},e.off=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){var n=i.indexOf(e);return-1!=n&&i.splice(n,1),this}},e.emitEvent=function(t,e){var i=this._events&&this._events[t];if(i&&i.length){var n=0,o=i[n];e=e||[];for(var r=this._onceEvents&&this._onceEvents[t];o;){var s=r&&r[o];s&&(this.off(t,o),delete r[o]),o.apply(this,e),n+=s?0:1,o=i[n]}return this}},t}),function(t,e){"use strict";"function"==typeof define&&define.amd?define("get-size/get-size",[],function(){return e()}):"object"==typeof module&&module.exports?module.exports=e():t.getSize=e()}(window,function(){"use strict";function t(t){var e=parseFloat(t),i=-1==t.indexOf("%")&&!isNaN(e);return i&&e}function e(){}function i(){for(var t={width:0,height:0,innerWidth:0,innerHeight:0,outerWidth:0,outerHeight:0},e=0;u>e;e++){var i=h[e];t[i]=0}return t}function n(t){var e=getComputedStyle(t);return e||a("Style returned "+e+". Are you running this code in a hidden iframe on Firefox? See http://bit.ly/getsizebug1"),e}function o(){if(!d){d=!0;var e=document.createElement("div");e.style.width="200px",e.style.padding="1px 2px 3px 4px",e.style.borderStyle="solid",e.style.borderWidth="1px 2px 3px 4px",e.style.boxSizing="border-box";var i=document.body||document.documentElement;i.appendChild(e);var o=n(e);r.isBoxSizeOuter=s=200==t(o.width),i.removeChild(e)}}function r(e){if(o(),"string"==typeof e&&(e=document.querySelector(e)),e&&"object"==typeof e&&e.nodeType){var r=n(e);if("none"==r.display)return i();var a={};a.width=e.offsetWidth,a.height=e.offsetHeight;for(var d=a.isBorderBox="border-box"==r.boxSizing,l=0;u>l;l++){var c=h[l],f=r[c],m=parseFloat(f);a[c]=isNaN(m)?0:m}var p=a.paddingLeft+a.paddingRight,g=a.paddingTop+a.paddingBottom,y=a.marginLeft+a.marginRight,v=a.marginTop+a.marginBottom,_=a.borderLeftWidth+a.borderRightWidth,z=a.borderTopWidth+a.borderBottomWidth,E=d&&s,b=t(r.width);b!==!1&&(a.width=b+(E?0:p+_));var x=t(r.height);return x!==!1&&(a.height=x+(E?0:g+z)),a.innerWidth=a.width-(p+_),a.innerHeight=a.height-(g+z),a.outerWidth=a.width+y,a.outerHeight=a.height+v,a}}var s,a="undefined"==typeof console?e:function(t){console.error(t)},h=["paddingLeft","paddingRight","paddingTop","paddingBottom","marginLeft","marginRight","marginTop","marginBottom","borderLeftWidth","borderRightWidth","borderTopWidth","borderBottomWidth"],u=h.length,d=!1;return r}),function(t,e){"use strict";"function"==typeof define&&define.amd?define("desandro-matches-selector/matches-selector",e):"object"==typeof module&&module.exports?module.exports=e():t.matchesSelector=e()}(window,function(){"use strict";var t=function(){var t=window.Element.prototype;if(t.matches)return"matches";if(t.matchesSelector)return"matchesSelector";for(var e=["webkit","moz","ms","o"],i=0;is?"round":"floor";r=Math[a](r),this.cols=Math.max(r,1)},n.getContainerWidth=function(){var t=this._getOption("fitWidth"),i=t?this.element.parentNode:this.element,n=e(i);this.containerWidth=n&&n.innerWidth},n._getItemLayoutPosition=function(t){t.getSize();var e=t.size.outerWidth%this.columnWidth,i=e&&1>e?"round":"ceil",n=Math[i](t.size.outerWidth/this.columnWidth);n=Math.min(n,this.cols);for(var o=this.options.horizontalOrder?"_getHorizontalColPosition":"_getTopColPosition",r=this[o](n,t),s={x:this.columnWidth*r.col,y:r.y},a=r.y+t.size.outerHeight,h=n+r.col,u=r.col;h>u;u++)this.colYs[u]=a;return s},n._getTopColPosition=function(t){var e=this._getTopColGroup(t),i=Math.min.apply(Math,e);return{col:e.indexOf(i),y:i}},n._getTopColGroup=function(t){if(2>t)return this.colYs;for(var e=[],i=this.cols+1-t,n=0;i>n;n++)e[n]=this._getColGroupY(n,t);return e},n._getColGroupY=function(t,e){if(2>e)return this.colYs[t];var i=this.colYs.slice(t,t+e);return Math.max.apply(Math,i)},n._getHorizontalColPosition=function(t,e){var i=this.horizontalColIndex%this.cols,n=t>1&&i+t>this.cols;i=n?0:i;var o=e.size.outerWidth&&e.size.outerHeight;return this.horizontalColIndex=o?i+t:this.horizontalColIndex,{col:i,y:this._getColGroupY(i,t)}},n._manageStamp=function(t){var i=e(t),n=this._getElementOffset(t),o=this._getOption("originLeft"),r=o?n.left:n.right,s=r+i.outerWidth,a=Math.floor(r/this.columnWidth);a=Math.max(0,a);var h=Math.floor(s/this.columnWidth);h-=s%this.columnWidth?0:1,h=Math.min(this.cols-1,h);for(var u=this._getOption("originTop"),d=(u?n.top:n.bottom)+i.outerHeight,l=a;h>=l;l++)this.colYs[l]=Math.max(d,this.colYs[l])},n._getContainerSize=function(){this.maxY=Math.max.apply(Math,this.colYs);var t={height:this.maxY};return this._getOption("fitWidth")&&(t.width=this._getContainerFitWidth()),t},n._getContainerFitWidth=function(){for(var t=0,e=this.cols;--e&&0===this.colYs[e];)t++;return(this.cols-t)*this.columnWidth-this.gutter},n.needsResizeLayout=function(){var t=this.containerWidth;return this.getContainerWidth(),t!=this.containerWidth},i}); -------------------------------------------------------------------------------- /masonry.pkgd.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Masonry PACKAGED v4.2.0 3 | * Cascading grid layout library 4 | * http://masonry.desandro.com 5 | * MIT License 6 | * by David DeSandro 7 | */ 8 | 9 | /** 10 | * Bridget makes jQuery widgets 11 | * v2.0.1 12 | * MIT license 13 | */ 14 | 15 | /* jshint browser: true, strict: true, undef: true, unused: true */ 16 | 17 | ( function( window, factory ) { 18 | // universal module definition 19 | /*jshint strict: false */ /* globals define, module, require */ 20 | if ( typeof define == 'function' && define.amd ) { 21 | // AMD 22 | define( 'jquery-bridget/jquery-bridget',[ 'jquery' ], function( jQuery ) { 23 | return factory( window, jQuery ); 24 | }); 25 | } else if ( typeof module == 'object' && module.exports ) { 26 | // CommonJS 27 | module.exports = factory( 28 | window, 29 | require('jquery') 30 | ); 31 | } else { 32 | // browser global 33 | window.jQueryBridget = factory( 34 | window, 35 | window.jQuery 36 | ); 37 | } 38 | 39 | }( window, function factory( window, jQuery ) { 40 | 'use strict'; 41 | 42 | // ----- utils ----- // 43 | 44 | var arraySlice = Array.prototype.slice; 45 | 46 | // helper function for logging errors 47 | // $.error breaks jQuery chaining 48 | var console = window.console; 49 | var logError = typeof console == 'undefined' ? function() {} : 50 | function( message ) { 51 | console.error( message ); 52 | }; 53 | 54 | // ----- jQueryBridget ----- // 55 | 56 | function jQueryBridget( namespace, PluginClass, $ ) { 57 | $ = $ || jQuery || window.jQuery; 58 | if ( !$ ) { 59 | return; 60 | } 61 | 62 | // add option method -> $().plugin('option', {...}) 63 | if ( !PluginClass.prototype.option ) { 64 | // option setter 65 | PluginClass.prototype.option = function( opts ) { 66 | // bail out if not an object 67 | if ( !$.isPlainObject( opts ) ){ 68 | return; 69 | } 70 | this.options = $.extend( true, this.options, opts ); 71 | }; 72 | } 73 | 74 | // make jQuery plugin 75 | $.fn[ namespace ] = function( arg0 /*, arg1 */ ) { 76 | if ( typeof arg0 == 'string' ) { 77 | // method call $().plugin( 'methodName', { options } ) 78 | // shift arguments by 1 79 | var args = arraySlice.call( arguments, 1 ); 80 | return methodCall( this, arg0, args ); 81 | } 82 | // just $().plugin({ options }) 83 | plainCall( this, arg0 ); 84 | return this; 85 | }; 86 | 87 | // $().plugin('methodName') 88 | function methodCall( $elems, methodName, args ) { 89 | var returnValue; 90 | var pluginMethodStr = '$().' + namespace + '("' + methodName + '")'; 91 | 92 | $elems.each( function( i, elem ) { 93 | // get instance 94 | var instance = $.data( elem, namespace ); 95 | if ( !instance ) { 96 | logError( namespace + ' not initialized. Cannot call methods, i.e. ' + 97 | pluginMethodStr ); 98 | return; 99 | } 100 | 101 | var method = instance[ methodName ]; 102 | if ( !method || methodName.charAt(0) == '_' ) { 103 | logError( pluginMethodStr + ' is not a valid method' ); 104 | return; 105 | } 106 | 107 | // apply method, get return value 108 | var value = method.apply( instance, args ); 109 | // set return value if value is returned, use only first value 110 | returnValue = returnValue === undefined ? value : returnValue; 111 | }); 112 | 113 | return returnValue !== undefined ? returnValue : $elems; 114 | } 115 | 116 | function plainCall( $elems, options ) { 117 | $elems.each( function( i, elem ) { 118 | var instance = $.data( elem, namespace ); 119 | if ( instance ) { 120 | // set options & init 121 | instance.option( options ); 122 | instance._init(); 123 | } else { 124 | // initialize new instance 125 | instance = new PluginClass( elem, options ); 126 | $.data( elem, namespace, instance ); 127 | } 128 | }); 129 | } 130 | 131 | updateJQuery( $ ); 132 | 133 | } 134 | 135 | // ----- updateJQuery ----- // 136 | 137 | // set $.bridget for v1 backwards compatibility 138 | function updateJQuery( $ ) { 139 | if ( !$ || ( $ && $.bridget ) ) { 140 | return; 141 | } 142 | $.bridget = jQueryBridget; 143 | } 144 | 145 | updateJQuery( jQuery || window.jQuery ); 146 | 147 | // ----- ----- // 148 | 149 | return jQueryBridget; 150 | 151 | })); 152 | 153 | /** 154 | * EvEmitter v1.0.3 155 | * Lil' event emitter 156 | * MIT License 157 | */ 158 | 159 | /* jshint unused: true, undef: true, strict: true */ 160 | 161 | ( function( global, factory ) { 162 | // universal module definition 163 | /* jshint strict: false */ /* globals define, module, window */ 164 | if ( typeof define == 'function' && define.amd ) { 165 | // AMD - RequireJS 166 | define( 'ev-emitter/ev-emitter',factory ); 167 | } else if ( typeof module == 'object' && module.exports ) { 168 | // CommonJS - Browserify, Webpack 169 | module.exports = factory(); 170 | } else { 171 | // Browser globals 172 | global.EvEmitter = factory(); 173 | } 174 | 175 | }( typeof window != 'undefined' ? window : this, function() { 176 | 177 | 178 | 179 | function EvEmitter() {} 180 | 181 | var proto = EvEmitter.prototype; 182 | 183 | proto.on = function( eventName, listener ) { 184 | if ( !eventName || !listener ) { 185 | return; 186 | } 187 | // set events hash 188 | var events = this._events = this._events || {}; 189 | // set listeners array 190 | var listeners = events[ eventName ] = events[ eventName ] || []; 191 | // only add once 192 | if ( listeners.indexOf( listener ) == -1 ) { 193 | listeners.push( listener ); 194 | } 195 | 196 | return this; 197 | }; 198 | 199 | proto.once = function( eventName, listener ) { 200 | if ( !eventName || !listener ) { 201 | return; 202 | } 203 | // add event 204 | this.on( eventName, listener ); 205 | // set once flag 206 | // set onceEvents hash 207 | var onceEvents = this._onceEvents = this._onceEvents || {}; 208 | // set onceListeners object 209 | var onceListeners = onceEvents[ eventName ] = onceEvents[ eventName ] || {}; 210 | // set flag 211 | onceListeners[ listener ] = true; 212 | 213 | return this; 214 | }; 215 | 216 | proto.off = function( eventName, listener ) { 217 | var listeners = this._events && this._events[ eventName ]; 218 | if ( !listeners || !listeners.length ) { 219 | return; 220 | } 221 | var index = listeners.indexOf( listener ); 222 | if ( index != -1 ) { 223 | listeners.splice( index, 1 ); 224 | } 225 | 226 | return this; 227 | }; 228 | 229 | proto.emitEvent = function( eventName, args ) { 230 | var listeners = this._events && this._events[ eventName ]; 231 | if ( !listeners || !listeners.length ) { 232 | return; 233 | } 234 | var i = 0; 235 | var listener = listeners[i]; 236 | args = args || []; 237 | // once stuff 238 | var onceListeners = this._onceEvents && this._onceEvents[ eventName ]; 239 | 240 | while ( listener ) { 241 | var isOnce = onceListeners && onceListeners[ listener ]; 242 | if ( isOnce ) { 243 | // remove listener 244 | // remove before trigger to prevent recursion 245 | this.off( eventName, listener ); 246 | // unset once flag 247 | delete onceListeners[ listener ]; 248 | } 249 | // trigger listener 250 | listener.apply( this, args ); 251 | // get next listener 252 | i += isOnce ? 0 : 1; 253 | listener = listeners[i]; 254 | } 255 | 256 | return this; 257 | }; 258 | 259 | return EvEmitter; 260 | 261 | })); 262 | 263 | /*! 264 | * getSize v2.0.2 265 | * measure size of elements 266 | * MIT license 267 | */ 268 | 269 | /*jshint browser: true, strict: true, undef: true, unused: true */ 270 | /*global define: false, module: false, console: false */ 271 | 272 | ( function( window, factory ) { 273 | 'use strict'; 274 | 275 | if ( typeof define == 'function' && define.amd ) { 276 | // AMD 277 | define( 'get-size/get-size',[],function() { 278 | return factory(); 279 | }); 280 | } else if ( typeof module == 'object' && module.exports ) { 281 | // CommonJS 282 | module.exports = factory(); 283 | } else { 284 | // browser global 285 | window.getSize = factory(); 286 | } 287 | 288 | })( window, function factory() { 289 | 'use strict'; 290 | 291 | // -------------------------- helpers -------------------------- // 292 | 293 | // get a number from a string, not a percentage 294 | function getStyleSize( value ) { 295 | var num = parseFloat( value ); 296 | // not a percent like '100%', and a number 297 | var isValid = value.indexOf('%') == -1 && !isNaN( num ); 298 | return isValid && num; 299 | } 300 | 301 | function noop() {} 302 | 303 | var logError = typeof console == 'undefined' ? noop : 304 | function( message ) { 305 | console.error( message ); 306 | }; 307 | 308 | // -------------------------- measurements -------------------------- // 309 | 310 | var measurements = [ 311 | 'paddingLeft', 312 | 'paddingRight', 313 | 'paddingTop', 314 | 'paddingBottom', 315 | 'marginLeft', 316 | 'marginRight', 317 | 'marginTop', 318 | 'marginBottom', 319 | 'borderLeftWidth', 320 | 'borderRightWidth', 321 | 'borderTopWidth', 322 | 'borderBottomWidth' 323 | ]; 324 | 325 | var measurementsLength = measurements.length; 326 | 327 | function getZeroSize() { 328 | var size = { 329 | width: 0, 330 | height: 0, 331 | innerWidth: 0, 332 | innerHeight: 0, 333 | outerWidth: 0, 334 | outerHeight: 0 335 | }; 336 | for ( var i=0; i < measurementsLength; i++ ) { 337 | var measurement = measurements[i]; 338 | size[ measurement ] = 0; 339 | } 340 | return size; 341 | } 342 | 343 | // -------------------------- getStyle -------------------------- // 344 | 345 | /** 346 | * getStyle, get style of element, check for Firefox bug 347 | * https://bugzilla.mozilla.org/show_bug.cgi?id=548397 348 | */ 349 | function getStyle( elem ) { 350 | var style = getComputedStyle( elem ); 351 | if ( !style ) { 352 | logError( 'Style returned ' + style + 353 | '. Are you running this code in a hidden iframe on Firefox? ' + 354 | 'See http://bit.ly/getsizebug1' ); 355 | } 356 | return style; 357 | } 358 | 359 | // -------------------------- setup -------------------------- // 360 | 361 | var isSetup = false; 362 | 363 | var isBoxSizeOuter; 364 | 365 | /** 366 | * setup 367 | * check isBoxSizerOuter 368 | * do on first getSize() rather than on page load for Firefox bug 369 | */ 370 | function setup() { 371 | // setup once 372 | if ( isSetup ) { 373 | return; 374 | } 375 | isSetup = true; 376 | 377 | // -------------------------- box sizing -------------------------- // 378 | 379 | /** 380 | * WebKit measures the outer-width on style.width on border-box elems 381 | * IE & Firefox<29 measures the inner-width 382 | */ 383 | var div = document.createElement('div'); 384 | div.style.width = '200px'; 385 | div.style.padding = '1px 2px 3px 4px'; 386 | div.style.borderStyle = 'solid'; 387 | div.style.borderWidth = '1px 2px 3px 4px'; 388 | div.style.boxSizing = 'border-box'; 389 | 390 | var body = document.body || document.documentElement; 391 | body.appendChild( div ); 392 | var style = getStyle( div ); 393 | 394 | getSize.isBoxSizeOuter = isBoxSizeOuter = getStyleSize( style.width ) == 200; 395 | body.removeChild( div ); 396 | 397 | } 398 | 399 | // -------------------------- getSize -------------------------- // 400 | 401 | function getSize( elem ) { 402 | setup(); 403 | 404 | // use querySeletor if elem is string 405 | if ( typeof elem == 'string' ) { 406 | elem = document.querySelector( elem ); 407 | } 408 | 409 | // do not proceed on non-objects 410 | if ( !elem || typeof elem != 'object' || !elem.nodeType ) { 411 | return; 412 | } 413 | 414 | var style = getStyle( elem ); 415 | 416 | // if hidden, everything is 0 417 | if ( style.display == 'none' ) { 418 | return getZeroSize(); 419 | } 420 | 421 | var size = {}; 422 | size.width = elem.offsetWidth; 423 | size.height = elem.offsetHeight; 424 | 425 | var isBorderBox = size.isBorderBox = style.boxSizing == 'border-box'; 426 | 427 | // get all measurements 428 | for ( var i=0; i < measurementsLength; i++ ) { 429 | var measurement = measurements[i]; 430 | var value = style[ measurement ]; 431 | var num = parseFloat( value ); 432 | // any 'auto', 'medium' value will be 0 433 | size[ measurement ] = !isNaN( num ) ? num : 0; 434 | } 435 | 436 | var paddingWidth = size.paddingLeft + size.paddingRight; 437 | var paddingHeight = size.paddingTop + size.paddingBottom; 438 | var marginWidth = size.marginLeft + size.marginRight; 439 | var marginHeight = size.marginTop + size.marginBottom; 440 | var borderWidth = size.borderLeftWidth + size.borderRightWidth; 441 | var borderHeight = size.borderTopWidth + size.borderBottomWidth; 442 | 443 | var isBorderBoxSizeOuter = isBorderBox && isBoxSizeOuter; 444 | 445 | // overwrite width and height if we can get it from style 446 | var styleWidth = getStyleSize( style.width ); 447 | if ( styleWidth !== false ) { 448 | size.width = styleWidth + 449 | // add padding and border unless it's already including it 450 | ( isBorderBoxSizeOuter ? 0 : paddingWidth + borderWidth ); 451 | } 452 | 453 | var styleHeight = getStyleSize( style.height ); 454 | if ( styleHeight !== false ) { 455 | size.height = styleHeight + 456 | // add padding and border unless it's already including it 457 | ( isBorderBoxSizeOuter ? 0 : paddingHeight + borderHeight ); 458 | } 459 | 460 | size.innerWidth = size.width - ( paddingWidth + borderWidth ); 461 | size.innerHeight = size.height - ( paddingHeight + borderHeight ); 462 | 463 | size.outerWidth = size.width + marginWidth; 464 | size.outerHeight = size.height + marginHeight; 465 | 466 | return size; 467 | } 468 | 469 | return getSize; 470 | 471 | }); 472 | 473 | /** 474 | * matchesSelector v2.0.2 475 | * matchesSelector( element, '.selector' ) 476 | * MIT license 477 | */ 478 | 479 | /*jshint browser: true, strict: true, undef: true, unused: true */ 480 | 481 | ( function( window, factory ) { 482 | /*global define: false, module: false */ 483 | 'use strict'; 484 | // universal module definition 485 | if ( typeof define == 'function' && define.amd ) { 486 | // AMD 487 | define( 'desandro-matches-selector/matches-selector',factory ); 488 | } else if ( typeof module == 'object' && module.exports ) { 489 | // CommonJS 490 | module.exports = factory(); 491 | } else { 492 | // browser global 493 | window.matchesSelector = factory(); 494 | } 495 | 496 | }( window, function factory() { 497 | 'use strict'; 498 | 499 | var matchesMethod = ( function() { 500 | var ElemProto = window.Element.prototype; 501 | // check for the standard method name first 502 | if ( ElemProto.matches ) { 503 | return 'matches'; 504 | } 505 | // check un-prefixed 506 | if ( ElemProto.matchesSelector ) { 507 | return 'matchesSelector'; 508 | } 509 | // check vendor prefixes 510 | var prefixes = [ 'webkit', 'moz', 'ms', 'o' ]; 511 | 512 | for ( var i=0; i < prefixes.length; i++ ) { 513 | var prefix = prefixes[i]; 514 | var method = prefix + 'MatchesSelector'; 515 | if ( ElemProto[ method ] ) { 516 | return method; 517 | } 518 | } 519 | })(); 520 | 521 | return function matchesSelector( elem, selector ) { 522 | return elem[ matchesMethod ]( selector ); 523 | }; 524 | 525 | })); 526 | 527 | /** 528 | * Fizzy UI utils v2.0.4 529 | * MIT license 530 | */ 531 | 532 | /*jshint browser: true, undef: true, unused: true, strict: true */ 533 | 534 | ( function( window, factory ) { 535 | // universal module definition 536 | /*jshint strict: false */ /*globals define, module, require */ 537 | 538 | if ( typeof define == 'function' && define.amd ) { 539 | // AMD 540 | define( 'fizzy-ui-utils/utils',[ 541 | 'desandro-matches-selector/matches-selector' 542 | ], function( matchesSelector ) { 543 | return factory( window, matchesSelector ); 544 | }); 545 | } else if ( typeof module == 'object' && module.exports ) { 546 | // CommonJS 547 | module.exports = factory( 548 | window, 549 | require('desandro-matches-selector') 550 | ); 551 | } else { 552 | // browser global 553 | window.fizzyUIUtils = factory( 554 | window, 555 | window.matchesSelector 556 | ); 557 | } 558 | 559 | }( window, function factory( window, matchesSelector ) { 560 | 561 | 562 | 563 | var utils = {}; 564 | 565 | // ----- extend ----- // 566 | 567 | // extends objects 568 | utils.extend = function( a, b ) { 569 | for ( var prop in b ) { 570 | a[ prop ] = b[ prop ]; 571 | } 572 | return a; 573 | }; 574 | 575 | // ----- modulo ----- // 576 | 577 | utils.modulo = function( num, div ) { 578 | return ( ( num % div ) + div ) % div; 579 | }; 580 | 581 | // ----- makeArray ----- // 582 | 583 | // turn element or nodeList into an array 584 | utils.makeArray = function( obj ) { 585 | var ary = []; 586 | if ( Array.isArray( obj ) ) { 587 | // use object if already an array 588 | ary = obj; 589 | } else if ( obj && typeof obj == 'object' && 590 | typeof obj.length == 'number' ) { 591 | // convert nodeList to array 592 | for ( var i=0; i < obj.length; i++ ) { 593 | ary.push( obj[i] ); 594 | } 595 | } else { 596 | // array of single index 597 | ary.push( obj ); 598 | } 599 | return ary; 600 | }; 601 | 602 | // ----- removeFrom ----- // 603 | 604 | utils.removeFrom = function( ary, obj ) { 605 | var index = ary.indexOf( obj ); 606 | if ( index != -1 ) { 607 | ary.splice( index, 1 ); 608 | } 609 | }; 610 | 611 | // ----- getParent ----- // 612 | 613 | utils.getParent = function( elem, selector ) { 614 | while ( elem != document.body ) { 615 | elem = elem.parentNode; 616 | if ( matchesSelector( elem, selector ) ) { 617 | return elem; 618 | } 619 | } 620 | }; 621 | 622 | // ----- getQueryElement ----- // 623 | 624 | // use element as selector string 625 | utils.getQueryElement = function( elem ) { 626 | if ( typeof elem == 'string' ) { 627 | return document.querySelector( elem ); 628 | } 629 | return elem; 630 | }; 631 | 632 | // ----- handleEvent ----- // 633 | 634 | // enable .ontype to trigger from .addEventListener( elem, 'type' ) 635 | utils.handleEvent = function( event ) { 636 | var method = 'on' + event.type; 637 | if ( this[ method ] ) { 638 | this[ method ]( event ); 639 | } 640 | }; 641 | 642 | // ----- filterFindElements ----- // 643 | 644 | utils.filterFindElements = function( elems, selector ) { 645 | // make array of elems 646 | elems = utils.makeArray( elems ); 647 | var ffElems = []; 648 | 649 | elems.forEach( function( elem ) { 650 | // check that elem is an actual element 651 | if ( !( elem instanceof HTMLElement ) ) { 652 | return; 653 | } 654 | // add elem if no selector 655 | if ( !selector ) { 656 | ffElems.push( elem ); 657 | return; 658 | } 659 | // filter & find items if we have a selector 660 | // filter 661 | if ( matchesSelector( elem, selector ) ) { 662 | ffElems.push( elem ); 663 | } 664 | // find children 665 | var childElems = elem.querySelectorAll( selector ); 666 | // concat childElems to filterFound array 667 | for ( var i=0; i < childElems.length; i++ ) { 668 | ffElems.push( childElems[i] ); 669 | } 670 | }); 671 | 672 | return ffElems; 673 | }; 674 | 675 | // ----- debounceMethod ----- // 676 | 677 | utils.debounceMethod = function( _class, methodName, threshold ) { 678 | // original method 679 | var method = _class.prototype[ methodName ]; 680 | var timeoutName = methodName + 'Timeout'; 681 | 682 | _class.prototype[ methodName ] = function() { 683 | var timeout = this[ timeoutName ]; 684 | if ( timeout ) { 685 | clearTimeout( timeout ); 686 | } 687 | var args = arguments; 688 | 689 | var _this = this; 690 | this[ timeoutName ] = setTimeout( function() { 691 | method.apply( _this, args ); 692 | delete _this[ timeoutName ]; 693 | }, threshold || 100 ); 694 | }; 695 | }; 696 | 697 | // ----- docReady ----- // 698 | 699 | utils.docReady = function( callback ) { 700 | var readyState = document.readyState; 701 | if ( readyState == 'complete' || readyState == 'interactive' ) { 702 | // do async to allow for other scripts to run. metafizzy/flickity#441 703 | setTimeout( callback ); 704 | } else { 705 | document.addEventListener( 'DOMContentLoaded', callback ); 706 | } 707 | }; 708 | 709 | // ----- htmlInit ----- // 710 | 711 | // http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/ 712 | utils.toDashed = function( str ) { 713 | return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) { 714 | return $1 + '-' + $2; 715 | }).toLowerCase(); 716 | }; 717 | 718 | var console = window.console; 719 | /** 720 | * allow user to initialize classes via [data-namespace] or .js-namespace class 721 | * htmlInit( Widget, 'widgetName' ) 722 | * options are parsed from data-namespace-options 723 | */ 724 | utils.htmlInit = function( WidgetClass, namespace ) { 725 | utils.docReady( function() { 726 | var dashedNamespace = utils.toDashed( namespace ); 727 | var dataAttr = 'data-' + dashedNamespace; 728 | var dataAttrElems = document.querySelectorAll( '[' + dataAttr + ']' ); 729 | var jsDashElems = document.querySelectorAll( '.js-' + dashedNamespace ); 730 | var elems = utils.makeArray( dataAttrElems ) 731 | .concat( utils.makeArray( jsDashElems ) ); 732 | var dataOptionsAttr = dataAttr + '-options'; 733 | var jQuery = window.jQuery; 734 | 735 | elems.forEach( function( elem ) { 736 | var attr = elem.getAttribute( dataAttr ) || 737 | elem.getAttribute( dataOptionsAttr ); 738 | var options; 739 | try { 740 | options = attr && JSON.parse( attr ); 741 | } catch ( error ) { 742 | // log error, do not initialize 743 | if ( console ) { 744 | console.error( 'Error parsing ' + dataAttr + ' on ' + elem.className + 745 | ': ' + error ); 746 | } 747 | return; 748 | } 749 | // initialize 750 | var instance = new WidgetClass( elem, options ); 751 | // make available via $().data('namespace') 752 | if ( jQuery ) { 753 | jQuery.data( elem, namespace, instance ); 754 | } 755 | }); 756 | 757 | }); 758 | }; 759 | 760 | // ----- ----- // 761 | 762 | return utils; 763 | 764 | })); 765 | 766 | /** 767 | * Outlayer Item 768 | */ 769 | 770 | ( function( window, factory ) { 771 | // universal module definition 772 | /* jshint strict: false */ /* globals define, module, require */ 773 | if ( typeof define == 'function' && define.amd ) { 774 | // AMD - RequireJS 775 | define( 'outlayer/item',[ 776 | 'ev-emitter/ev-emitter', 777 | 'get-size/get-size' 778 | ], 779 | factory 780 | ); 781 | } else if ( typeof module == 'object' && module.exports ) { 782 | // CommonJS - Browserify, Webpack 783 | module.exports = factory( 784 | require('ev-emitter'), 785 | require('get-size') 786 | ); 787 | } else { 788 | // browser global 789 | window.Outlayer = {}; 790 | window.Outlayer.Item = factory( 791 | window.EvEmitter, 792 | window.getSize 793 | ); 794 | } 795 | 796 | }( window, function factory( EvEmitter, getSize ) { 797 | 'use strict'; 798 | 799 | // ----- helpers ----- // 800 | 801 | function isEmptyObj( obj ) { 802 | for ( var prop in obj ) { 803 | return false; 804 | } 805 | prop = null; 806 | return true; 807 | } 808 | 809 | // -------------------------- CSS3 support -------------------------- // 810 | 811 | 812 | var docElemStyle = document.documentElement.style; 813 | 814 | var transitionProperty = typeof docElemStyle.transition == 'string' ? 815 | 'transition' : 'WebkitTransition'; 816 | var transformProperty = typeof docElemStyle.transform == 'string' ? 817 | 'transform' : 'WebkitTransform'; 818 | 819 | var transitionEndEvent = { 820 | WebkitTransition: 'webkitTransitionEnd', 821 | transition: 'transitionend' 822 | }[ transitionProperty ]; 823 | 824 | // cache all vendor properties that could have vendor prefix 825 | var vendorProperties = { 826 | transform: transformProperty, 827 | transition: transitionProperty, 828 | transitionDuration: transitionProperty + 'Duration', 829 | transitionProperty: transitionProperty + 'Property', 830 | transitionDelay: transitionProperty + 'Delay' 831 | }; 832 | 833 | // -------------------------- Item -------------------------- // 834 | 835 | function Item( element, layout ) { 836 | if ( !element ) { 837 | return; 838 | } 839 | 840 | this.element = element; 841 | // parent layout class, i.e. Masonry, Isotope, or Packery 842 | this.layout = layout; 843 | this.position = { 844 | x: 0, 845 | y: 0 846 | }; 847 | 848 | this._create(); 849 | } 850 | 851 | // inherit EvEmitter 852 | var proto = Item.prototype = Object.create( EvEmitter.prototype ); 853 | proto.constructor = Item; 854 | 855 | proto._create = function() { 856 | // transition objects 857 | this._transn = { 858 | ingProperties: {}, 859 | clean: {}, 860 | onEnd: {} 861 | }; 862 | 863 | this.css({ 864 | position: 'absolute' 865 | }); 866 | }; 867 | 868 | // trigger specified handler for event type 869 | proto.handleEvent = function( event ) { 870 | var method = 'on' + event.type; 871 | if ( this[ method ] ) { 872 | this[ method ]( event ); 873 | } 874 | }; 875 | 876 | proto.getSize = function() { 877 | this.size = getSize( this.element ); 878 | }; 879 | 880 | /** 881 | * apply CSS styles to element 882 | * @param {Object} style 883 | */ 884 | proto.css = function( style ) { 885 | var elemStyle = this.element.style; 886 | 887 | for ( var prop in style ) { 888 | // use vendor property if available 889 | var supportedProp = vendorProperties[ prop ] || prop; 890 | elemStyle[ supportedProp ] = style[ prop ]; 891 | } 892 | }; 893 | 894 | // measure position, and sets it 895 | proto.getPosition = function() { 896 | var style = getComputedStyle( this.element ); 897 | var isOriginLeft = this.layout._getOption('originLeft'); 898 | var isOriginTop = this.layout._getOption('originTop'); 899 | var xValue = style[ isOriginLeft ? 'left' : 'right' ]; 900 | var yValue = style[ isOriginTop ? 'top' : 'bottom' ]; 901 | // convert percent to pixels 902 | var layoutSize = this.layout.size; 903 | var x = xValue.indexOf('%') != -1 ? 904 | ( parseFloat( xValue ) / 100 ) * layoutSize.width : parseInt( xValue, 10 ); 905 | var y = yValue.indexOf('%') != -1 ? 906 | ( parseFloat( yValue ) / 100 ) * layoutSize.height : parseInt( yValue, 10 ); 907 | 908 | // clean up 'auto' or other non-integer values 909 | x = isNaN( x ) ? 0 : x; 910 | y = isNaN( y ) ? 0 : y; 911 | // remove padding from measurement 912 | x -= isOriginLeft ? layoutSize.paddingLeft : layoutSize.paddingRight; 913 | y -= isOriginTop ? layoutSize.paddingTop : layoutSize.paddingBottom; 914 | 915 | this.position.x = x; 916 | this.position.y = y; 917 | }; 918 | 919 | // set settled position, apply padding 920 | proto.layoutPosition = function() { 921 | var layoutSize = this.layout.size; 922 | var style = {}; 923 | var isOriginLeft = this.layout._getOption('originLeft'); 924 | var isOriginTop = this.layout._getOption('originTop'); 925 | 926 | // x 927 | var xPadding = isOriginLeft ? 'paddingLeft' : 'paddingRight'; 928 | var xProperty = isOriginLeft ? 'left' : 'right'; 929 | var xResetProperty = isOriginLeft ? 'right' : 'left'; 930 | 931 | var x = this.position.x + layoutSize[ xPadding ]; 932 | // set in percentage or pixels 933 | style[ xProperty ] = this.getXValue( x ); 934 | // reset other property 935 | style[ xResetProperty ] = ''; 936 | 937 | // y 938 | var yPadding = isOriginTop ? 'paddingTop' : 'paddingBottom'; 939 | var yProperty = isOriginTop ? 'top' : 'bottom'; 940 | var yResetProperty = isOriginTop ? 'bottom' : 'top'; 941 | 942 | var y = this.position.y + layoutSize[ yPadding ]; 943 | // set in percentage or pixels 944 | style[ yProperty ] = this.getYValue( y ); 945 | // reset other property 946 | style[ yResetProperty ] = ''; 947 | 948 | this.css( style ); 949 | this.emitEvent( 'layout', [ this ] ); 950 | }; 951 | 952 | proto.getXValue = function( x ) { 953 | var isHorizontal = this.layout._getOption('horizontal'); 954 | return this.layout.options.percentPosition && !isHorizontal ? 955 | ( ( x / this.layout.size.width ) * 100 ) + '%' : x + 'px'; 956 | }; 957 | 958 | proto.getYValue = function( y ) { 959 | var isHorizontal = this.layout._getOption('horizontal'); 960 | return this.layout.options.percentPosition && isHorizontal ? 961 | ( ( y / this.layout.size.height ) * 100 ) + '%' : y + 'px'; 962 | }; 963 | 964 | proto._transitionTo = function( x, y ) { 965 | this.getPosition(); 966 | // get current x & y from top/left 967 | var curX = this.position.x; 968 | var curY = this.position.y; 969 | 970 | var compareX = parseInt( x, 10 ); 971 | var compareY = parseInt( y, 10 ); 972 | var didNotMove = compareX === this.position.x && compareY === this.position.y; 973 | 974 | // save end position 975 | this.setPosition( x, y ); 976 | 977 | // if did not move and not transitioning, just go to layout 978 | if ( didNotMove && !this.isTransitioning ) { 979 | this.layoutPosition(); 980 | return; 981 | } 982 | 983 | var transX = x - curX; 984 | var transY = y - curY; 985 | var transitionStyle = {}; 986 | transitionStyle.transform = this.getTranslate( transX, transY ); 987 | 988 | this.transition({ 989 | to: transitionStyle, 990 | onTransitionEnd: { 991 | transform: this.layoutPosition 992 | }, 993 | isCleaning: true 994 | }); 995 | }; 996 | 997 | proto.getTranslate = function( x, y ) { 998 | // flip cooridinates if origin on right or bottom 999 | var isOriginLeft = this.layout._getOption('originLeft'); 1000 | var isOriginTop = this.layout._getOption('originTop'); 1001 | x = isOriginLeft ? x : -x; 1002 | y = isOriginTop ? y : -y; 1003 | return 'translate3d(' + x + 'px, ' + y + 'px, 0)'; 1004 | }; 1005 | 1006 | // non transition + transform support 1007 | proto.goTo = function( x, y ) { 1008 | this.setPosition( x, y ); 1009 | this.layoutPosition(); 1010 | }; 1011 | 1012 | proto.moveTo = proto._transitionTo; 1013 | 1014 | proto.setPosition = function( x, y ) { 1015 | this.position.x = parseInt( x, 10 ); 1016 | this.position.y = parseInt( y, 10 ); 1017 | }; 1018 | 1019 | // ----- transition ----- // 1020 | 1021 | /** 1022 | * @param {Object} style - CSS 1023 | * @param {Function} onTransitionEnd 1024 | */ 1025 | 1026 | // non transition, just trigger callback 1027 | proto._nonTransition = function( args ) { 1028 | this.css( args.to ); 1029 | if ( args.isCleaning ) { 1030 | this._removeStyles( args.to ); 1031 | } 1032 | for ( var prop in args.onTransitionEnd ) { 1033 | args.onTransitionEnd[ prop ].call( this ); 1034 | } 1035 | }; 1036 | 1037 | /** 1038 | * proper transition 1039 | * @param {Object} args - arguments 1040 | * @param {Object} to - style to transition to 1041 | * @param {Object} from - style to start transition from 1042 | * @param {Boolean} isCleaning - removes transition styles after transition 1043 | * @param {Function} onTransitionEnd - callback 1044 | */ 1045 | proto.transition = function( args ) { 1046 | // redirect to nonTransition if no transition duration 1047 | if ( !parseFloat( this.layout.options.transitionDuration ) ) { 1048 | this._nonTransition( args ); 1049 | return; 1050 | } 1051 | 1052 | var _transition = this._transn; 1053 | // keep track of onTransitionEnd callback by css property 1054 | for ( var prop in args.onTransitionEnd ) { 1055 | _transition.onEnd[ prop ] = args.onTransitionEnd[ prop ]; 1056 | } 1057 | // keep track of properties that are transitioning 1058 | for ( prop in args.to ) { 1059 | _transition.ingProperties[ prop ] = true; 1060 | // keep track of properties to clean up when transition is done 1061 | if ( args.isCleaning ) { 1062 | _transition.clean[ prop ] = true; 1063 | } 1064 | } 1065 | 1066 | // set from styles 1067 | if ( args.from ) { 1068 | this.css( args.from ); 1069 | // force redraw. http://blog.alexmaccaw.com/css-transitions 1070 | var h = this.element.offsetHeight; 1071 | // hack for JSHint to hush about unused var 1072 | h = null; 1073 | } 1074 | // enable transition 1075 | this.enableTransition( args.to ); 1076 | // set styles that are transitioning 1077 | this.css( args.to ); 1078 | 1079 | this.isTransitioning = true; 1080 | 1081 | }; 1082 | 1083 | // dash before all cap letters, including first for 1084 | // WebkitTransform => -webkit-transform 1085 | function toDashedAll( str ) { 1086 | return str.replace( /([A-Z])/g, function( $1 ) { 1087 | return '-' + $1.toLowerCase(); 1088 | }); 1089 | } 1090 | 1091 | var transitionProps = 'opacity,' + toDashedAll( transformProperty ); 1092 | 1093 | proto.enableTransition = function(/* style */) { 1094 | // HACK changing transitionProperty during a transition 1095 | // will cause transition to jump 1096 | if ( this.isTransitioning ) { 1097 | return; 1098 | } 1099 | 1100 | // make `transition: foo, bar, baz` from style object 1101 | // HACK un-comment this when enableTransition can work 1102 | // while a transition is happening 1103 | // var transitionValues = []; 1104 | // for ( var prop in style ) { 1105 | // // dash-ify camelCased properties like WebkitTransition 1106 | // prop = vendorProperties[ prop ] || prop; 1107 | // transitionValues.push( toDashedAll( prop ) ); 1108 | // } 1109 | // munge number to millisecond, to match stagger 1110 | var duration = this.layout.options.transitionDuration; 1111 | duration = typeof duration == 'number' ? duration + 'ms' : duration; 1112 | // enable transition styles 1113 | this.css({ 1114 | transitionProperty: transitionProps, 1115 | transitionDuration: duration, 1116 | transitionDelay: this.staggerDelay || 0 1117 | }); 1118 | // listen for transition end event 1119 | this.element.addEventListener( transitionEndEvent, this, false ); 1120 | }; 1121 | 1122 | // ----- events ----- // 1123 | 1124 | proto.onwebkitTransitionEnd = function( event ) { 1125 | this.ontransitionend( event ); 1126 | }; 1127 | 1128 | proto.onotransitionend = function( event ) { 1129 | this.ontransitionend( event ); 1130 | }; 1131 | 1132 | // properties that I munge to make my life easier 1133 | var dashedVendorProperties = { 1134 | '-webkit-transform': 'transform' 1135 | }; 1136 | 1137 | proto.ontransitionend = function( event ) { 1138 | // disregard bubbled events from children 1139 | if ( event.target !== this.element ) { 1140 | return; 1141 | } 1142 | var _transition = this._transn; 1143 | // get property name of transitioned property, convert to prefix-free 1144 | var propertyName = dashedVendorProperties[ event.propertyName ] || event.propertyName; 1145 | 1146 | // remove property that has completed transitioning 1147 | delete _transition.ingProperties[ propertyName ]; 1148 | // check if any properties are still transitioning 1149 | if ( isEmptyObj( _transition.ingProperties ) ) { 1150 | // all properties have completed transitioning 1151 | this.disableTransition(); 1152 | } 1153 | // clean style 1154 | if ( propertyName in _transition.clean ) { 1155 | // clean up style 1156 | this.element.style[ event.propertyName ] = ''; 1157 | delete _transition.clean[ propertyName ]; 1158 | } 1159 | // trigger onTransitionEnd callback 1160 | if ( propertyName in _transition.onEnd ) { 1161 | var onTransitionEnd = _transition.onEnd[ propertyName ]; 1162 | onTransitionEnd.call( this ); 1163 | delete _transition.onEnd[ propertyName ]; 1164 | } 1165 | 1166 | this.emitEvent( 'transitionEnd', [ this ] ); 1167 | }; 1168 | 1169 | proto.disableTransition = function() { 1170 | this.removeTransitionStyles(); 1171 | this.element.removeEventListener( transitionEndEvent, this, false ); 1172 | this.isTransitioning = false; 1173 | }; 1174 | 1175 | /** 1176 | * removes style property from element 1177 | * @param {Object} style 1178 | **/ 1179 | proto._removeStyles = function( style ) { 1180 | // clean up transition styles 1181 | var cleanStyle = {}; 1182 | for ( var prop in style ) { 1183 | cleanStyle[ prop ] = ''; 1184 | } 1185 | this.css( cleanStyle ); 1186 | }; 1187 | 1188 | var cleanTransitionStyle = { 1189 | transitionProperty: '', 1190 | transitionDuration: '', 1191 | transitionDelay: '' 1192 | }; 1193 | 1194 | proto.removeTransitionStyles = function() { 1195 | // remove transition 1196 | this.css( cleanTransitionStyle ); 1197 | }; 1198 | 1199 | // ----- stagger ----- // 1200 | 1201 | proto.stagger = function( delay ) { 1202 | delay = isNaN( delay ) ? 0 : delay; 1203 | this.staggerDelay = delay + 'ms'; 1204 | }; 1205 | 1206 | // ----- show/hide/remove ----- // 1207 | 1208 | // remove element from DOM 1209 | proto.removeElem = function() { 1210 | this.element.parentNode.removeChild( this.element ); 1211 | // remove display: none 1212 | this.css({ display: '' }); 1213 | this.emitEvent( 'remove', [ this ] ); 1214 | }; 1215 | 1216 | proto.remove = function() { 1217 | // just remove element if no transition support or no transition 1218 | if ( !transitionProperty || !parseFloat( this.layout.options.transitionDuration ) ) { 1219 | this.removeElem(); 1220 | return; 1221 | } 1222 | 1223 | // start transition 1224 | this.once( 'transitionEnd', function() { 1225 | this.removeElem(); 1226 | }); 1227 | this.hide(); 1228 | }; 1229 | 1230 | proto.reveal = function() { 1231 | delete this.isHidden; 1232 | // remove display: none 1233 | this.css({ display: '' }); 1234 | 1235 | var options = this.layout.options; 1236 | 1237 | var onTransitionEnd = {}; 1238 | var transitionEndProperty = this.getHideRevealTransitionEndProperty('visibleStyle'); 1239 | onTransitionEnd[ transitionEndProperty ] = this.onRevealTransitionEnd; 1240 | 1241 | this.transition({ 1242 | from: options.hiddenStyle, 1243 | to: options.visibleStyle, 1244 | isCleaning: true, 1245 | onTransitionEnd: onTransitionEnd 1246 | }); 1247 | }; 1248 | 1249 | proto.onRevealTransitionEnd = function() { 1250 | // check if still visible 1251 | // during transition, item may have been hidden 1252 | if ( !this.isHidden ) { 1253 | this.emitEvent('reveal'); 1254 | } 1255 | }; 1256 | 1257 | /** 1258 | * get style property use for hide/reveal transition end 1259 | * @param {String} styleProperty - hiddenStyle/visibleStyle 1260 | * @returns {String} 1261 | */ 1262 | proto.getHideRevealTransitionEndProperty = function( styleProperty ) { 1263 | var optionStyle = this.layout.options[ styleProperty ]; 1264 | // use opacity 1265 | if ( optionStyle.opacity ) { 1266 | return 'opacity'; 1267 | } 1268 | // get first property 1269 | for ( var prop in optionStyle ) { 1270 | return prop; 1271 | } 1272 | }; 1273 | 1274 | proto.hide = function() { 1275 | // set flag 1276 | this.isHidden = true; 1277 | // remove display: none 1278 | this.css({ display: '' }); 1279 | 1280 | var options = this.layout.options; 1281 | 1282 | var onTransitionEnd = {}; 1283 | var transitionEndProperty = this.getHideRevealTransitionEndProperty('hiddenStyle'); 1284 | onTransitionEnd[ transitionEndProperty ] = this.onHideTransitionEnd; 1285 | 1286 | this.transition({ 1287 | from: options.visibleStyle, 1288 | to: options.hiddenStyle, 1289 | // keep hidden stuff hidden 1290 | isCleaning: true, 1291 | onTransitionEnd: onTransitionEnd 1292 | }); 1293 | }; 1294 | 1295 | proto.onHideTransitionEnd = function() { 1296 | // check if still hidden 1297 | // during transition, item may have been un-hidden 1298 | if ( this.isHidden ) { 1299 | this.css({ display: 'none' }); 1300 | this.emitEvent('hide'); 1301 | } 1302 | }; 1303 | 1304 | proto.destroy = function() { 1305 | this.css({ 1306 | position: '', 1307 | left: '', 1308 | right: '', 1309 | top: '', 1310 | bottom: '', 1311 | transition: '', 1312 | transform: '' 1313 | }); 1314 | }; 1315 | 1316 | return Item; 1317 | 1318 | })); 1319 | 1320 | /*! 1321 | * Outlayer v2.1.0 1322 | * the brains and guts of a layout library 1323 | * MIT license 1324 | */ 1325 | 1326 | ( function( window, factory ) { 1327 | 'use strict'; 1328 | // universal module definition 1329 | /* jshint strict: false */ /* globals define, module, require */ 1330 | if ( typeof define == 'function' && define.amd ) { 1331 | // AMD - RequireJS 1332 | define( 'outlayer/outlayer',[ 1333 | 'ev-emitter/ev-emitter', 1334 | 'get-size/get-size', 1335 | 'fizzy-ui-utils/utils', 1336 | './item' 1337 | ], 1338 | function( EvEmitter, getSize, utils, Item ) { 1339 | return factory( window, EvEmitter, getSize, utils, Item); 1340 | } 1341 | ); 1342 | } else if ( typeof module == 'object' && module.exports ) { 1343 | // CommonJS - Browserify, Webpack 1344 | module.exports = factory( 1345 | window, 1346 | require('ev-emitter'), 1347 | require('get-size'), 1348 | require('fizzy-ui-utils'), 1349 | require('./item') 1350 | ); 1351 | } else { 1352 | // browser global 1353 | window.Outlayer = factory( 1354 | window, 1355 | window.EvEmitter, 1356 | window.getSize, 1357 | window.fizzyUIUtils, 1358 | window.Outlayer.Item 1359 | ); 1360 | } 1361 | 1362 | }( window, function factory( window, EvEmitter, getSize, utils, Item ) { 1363 | 'use strict'; 1364 | 1365 | // ----- vars ----- // 1366 | 1367 | var console = window.console; 1368 | var jQuery = window.jQuery; 1369 | var noop = function() {}; 1370 | 1371 | // -------------------------- Outlayer -------------------------- // 1372 | 1373 | // globally unique identifiers 1374 | var GUID = 0; 1375 | // internal store of all Outlayer intances 1376 | var instances = {}; 1377 | 1378 | 1379 | /** 1380 | * @param {Element, String} element 1381 | * @param {Object} options 1382 | * @constructor 1383 | */ 1384 | function Outlayer( element, options ) { 1385 | var queryElement = utils.getQueryElement( element ); 1386 | if ( !queryElement ) { 1387 | if ( console ) { 1388 | console.error( 'Bad element for ' + this.constructor.namespace + 1389 | ': ' + ( queryElement || element ) ); 1390 | } 1391 | return; 1392 | } 1393 | this.element = queryElement; 1394 | // add jQuery 1395 | if ( jQuery ) { 1396 | this.$element = jQuery( this.element ); 1397 | } 1398 | 1399 | // options 1400 | this.options = utils.extend( {}, this.constructor.defaults ); 1401 | this.option( options ); 1402 | 1403 | // add id for Outlayer.getFromElement 1404 | var id = ++GUID; 1405 | this.element.outlayerGUID = id; // expando 1406 | instances[ id ] = this; // associate via id 1407 | 1408 | // kick it off 1409 | this._create(); 1410 | 1411 | var isInitLayout = this._getOption('initLayout'); 1412 | if ( isInitLayout ) { 1413 | this.layout(); 1414 | } 1415 | } 1416 | 1417 | // settings are for internal use only 1418 | Outlayer.namespace = 'outlayer'; 1419 | Outlayer.Item = Item; 1420 | 1421 | // default options 1422 | Outlayer.defaults = { 1423 | containerStyle: { 1424 | position: 'relative' 1425 | }, 1426 | initLayout: true, 1427 | originLeft: true, 1428 | originTop: true, 1429 | resize: true, 1430 | resizeContainer: true, 1431 | // item options 1432 | transitionDuration: '0.4s', 1433 | hiddenStyle: { 1434 | opacity: 0, 1435 | transform: 'scale(0.001)' 1436 | }, 1437 | visibleStyle: { 1438 | opacity: 1, 1439 | transform: 'scale(1)' 1440 | } 1441 | }; 1442 | 1443 | var proto = Outlayer.prototype; 1444 | // inherit EvEmitter 1445 | utils.extend( proto, EvEmitter.prototype ); 1446 | 1447 | /** 1448 | * set options 1449 | * @param {Object} opts 1450 | */ 1451 | proto.option = function( opts ) { 1452 | utils.extend( this.options, opts ); 1453 | }; 1454 | 1455 | /** 1456 | * get backwards compatible option value, check old name 1457 | */ 1458 | proto._getOption = function( option ) { 1459 | var oldOption = this.constructor.compatOptions[ option ]; 1460 | return oldOption && this.options[ oldOption ] !== undefined ? 1461 | this.options[ oldOption ] : this.options[ option ]; 1462 | }; 1463 | 1464 | Outlayer.compatOptions = { 1465 | // currentName: oldName 1466 | initLayout: 'isInitLayout', 1467 | horizontal: 'isHorizontal', 1468 | layoutInstant: 'isLayoutInstant', 1469 | originLeft: 'isOriginLeft', 1470 | originTop: 'isOriginTop', 1471 | resize: 'isResizeBound', 1472 | resizeContainer: 'isResizingContainer' 1473 | }; 1474 | 1475 | proto._create = function() { 1476 | // get items from children 1477 | this.reloadItems(); 1478 | // elements that affect layout, but are not laid out 1479 | this.stamps = []; 1480 | this.stamp( this.options.stamp ); 1481 | // set container style 1482 | utils.extend( this.element.style, this.options.containerStyle ); 1483 | 1484 | // bind resize method 1485 | var canBindResize = this._getOption('resize'); 1486 | if ( canBindResize ) { 1487 | this.bindResize(); 1488 | } 1489 | }; 1490 | 1491 | // goes through all children again and gets bricks in proper order 1492 | proto.reloadItems = function() { 1493 | // collection of item elements 1494 | this.items = this._itemize( this.element.children ); 1495 | }; 1496 | 1497 | 1498 | /** 1499 | * turn elements into Outlayer.Items to be used in layout 1500 | * @param {Array or NodeList or HTMLElement} elems 1501 | * @returns {Array} items - collection of new Outlayer Items 1502 | */ 1503 | proto._itemize = function( elems ) { 1504 | 1505 | var itemElems = this._filterFindItemElements( elems ); 1506 | var Item = this.constructor.Item; 1507 | 1508 | // create new Outlayer Items for collection 1509 | var items = []; 1510 | for ( var i=0; i < itemElems.length; i++ ) { 1511 | var elem = itemElems[i]; 1512 | var item = new Item( elem, this ); 1513 | items.push( item ); 1514 | } 1515 | 1516 | return items; 1517 | }; 1518 | 1519 | /** 1520 | * get item elements to be used in layout 1521 | * @param {Array or NodeList or HTMLElement} elems 1522 | * @returns {Array} items - item elements 1523 | */ 1524 | proto._filterFindItemElements = function( elems ) { 1525 | return utils.filterFindElements( elems, this.options.itemSelector ); 1526 | }; 1527 | 1528 | /** 1529 | * getter method for getting item elements 1530 | * @returns {Array} elems - collection of item elements 1531 | */ 1532 | proto.getItemElements = function() { 1533 | return this.items.map( function( item ) { 1534 | return item.element; 1535 | }); 1536 | }; 1537 | 1538 | // ----- init & layout ----- // 1539 | 1540 | /** 1541 | * lays out all items 1542 | */ 1543 | proto.layout = function() { 1544 | this._resetLayout(); 1545 | this._manageStamps(); 1546 | 1547 | // don't animate first layout 1548 | var layoutInstant = this._getOption('layoutInstant'); 1549 | var isInstant = layoutInstant !== undefined ? 1550 | layoutInstant : !this._isLayoutInited; 1551 | this.layoutItems( this.items, isInstant ); 1552 | 1553 | // flag for initalized 1554 | this._isLayoutInited = true; 1555 | }; 1556 | 1557 | // _init is alias for layout 1558 | proto._init = proto.layout; 1559 | 1560 | /** 1561 | * logic before any new layout 1562 | */ 1563 | proto._resetLayout = function() { 1564 | this.getSize(); 1565 | }; 1566 | 1567 | 1568 | proto.getSize = function() { 1569 | this.size = getSize( this.element ); 1570 | }; 1571 | 1572 | /** 1573 | * get measurement from option, for columnWidth, rowHeight, gutter 1574 | * if option is String -> get element from selector string, & get size of element 1575 | * if option is Element -> get size of element 1576 | * else use option as a number 1577 | * 1578 | * @param {String} measurement 1579 | * @param {String} size - width or height 1580 | * @private 1581 | */ 1582 | proto._getMeasurement = function( measurement, size ) { 1583 | var option = this.options[ measurement ]; 1584 | var elem; 1585 | if ( !option ) { 1586 | // default to 0 1587 | this[ measurement ] = 0; 1588 | } else { 1589 | // use option as an element 1590 | if ( typeof option == 'string' ) { 1591 | elem = this.element.querySelector( option ); 1592 | } else if ( option instanceof HTMLElement ) { 1593 | elem = option; 1594 | } 1595 | // use size of element, if element 1596 | this[ measurement ] = elem ? getSize( elem )[ size ] : option; 1597 | } 1598 | }; 1599 | 1600 | /** 1601 | * layout a collection of item elements 1602 | * @api public 1603 | */ 1604 | proto.layoutItems = function( items, isInstant ) { 1605 | items = this._getItemsForLayout( items ); 1606 | 1607 | this._layoutItems( items, isInstant ); 1608 | 1609 | this._postLayout(); 1610 | }; 1611 | 1612 | /** 1613 | * get the items to be laid out 1614 | * you may want to skip over some items 1615 | * @param {Array} items 1616 | * @returns {Array} items 1617 | */ 1618 | proto._getItemsForLayout = function( items ) { 1619 | return items.filter( function( item ) { 1620 | return !item.isIgnored; 1621 | }); 1622 | }; 1623 | 1624 | /** 1625 | * layout items 1626 | * @param {Array} items 1627 | * @param {Boolean} isInstant 1628 | */ 1629 | proto._layoutItems = function( items, isInstant ) { 1630 | this._emitCompleteOnItems( 'layout', items ); 1631 | 1632 | if ( !items || !items.length ) { 1633 | // no items, emit event with empty array 1634 | return; 1635 | } 1636 | 1637 | var queue = []; 1638 | 1639 | items.forEach( function( item ) { 1640 | // get x/y object from method 1641 | var position = this._getItemLayoutPosition( item ); 1642 | // enqueue 1643 | position.item = item; 1644 | position.isInstant = isInstant || item.isLayoutInstant; 1645 | queue.push( position ); 1646 | }, this ); 1647 | 1648 | this._processLayoutQueue( queue ); 1649 | }; 1650 | 1651 | /** 1652 | * get item layout position 1653 | * @param {Outlayer.Item} item 1654 | * @returns {Object} x and y position 1655 | */ 1656 | proto._getItemLayoutPosition = function( /* item */ ) { 1657 | return { 1658 | x: 0, 1659 | y: 0 1660 | }; 1661 | }; 1662 | 1663 | /** 1664 | * iterate over array and position each item 1665 | * Reason being - separating this logic prevents 'layout invalidation' 1666 | * thx @paul_irish 1667 | * @param {Array} queue 1668 | */ 1669 | proto._processLayoutQueue = function( queue ) { 1670 | this.updateStagger(); 1671 | queue.forEach( function( obj, i ) { 1672 | this._positionItem( obj.item, obj.x, obj.y, obj.isInstant, i ); 1673 | }, this ); 1674 | }; 1675 | 1676 | // set stagger from option in milliseconds number 1677 | proto.updateStagger = function() { 1678 | var stagger = this.options.stagger; 1679 | if ( stagger === null || stagger === undefined ) { 1680 | this.stagger = 0; 1681 | return; 1682 | } 1683 | this.stagger = getMilliseconds( stagger ); 1684 | return this.stagger; 1685 | }; 1686 | 1687 | /** 1688 | * Sets position of item in DOM 1689 | * @param {Outlayer.Item} item 1690 | * @param {Number} x - horizontal position 1691 | * @param {Number} y - vertical position 1692 | * @param {Boolean} isInstant - disables transitions 1693 | */ 1694 | proto._positionItem = function( item, x, y, isInstant, i ) { 1695 | if ( isInstant ) { 1696 | // if not transition, just set CSS 1697 | item.goTo( x, y ); 1698 | } else { 1699 | item.stagger( i * this.stagger ); 1700 | item.moveTo( x, y ); 1701 | } 1702 | }; 1703 | 1704 | /** 1705 | * Any logic you want to do after each layout, 1706 | * i.e. size the container 1707 | */ 1708 | proto._postLayout = function() { 1709 | this.resizeContainer(); 1710 | }; 1711 | 1712 | proto.resizeContainer = function() { 1713 | var isResizingContainer = this._getOption('resizeContainer'); 1714 | if ( !isResizingContainer ) { 1715 | return; 1716 | } 1717 | var size = this._getContainerSize(); 1718 | if ( size ) { 1719 | this._setContainerMeasure( size.width, true ); 1720 | this._setContainerMeasure( size.height, false ); 1721 | } 1722 | }; 1723 | 1724 | /** 1725 | * Sets width or height of container if returned 1726 | * @returns {Object} size 1727 | * @param {Number} width 1728 | * @param {Number} height 1729 | */ 1730 | proto._getContainerSize = noop; 1731 | 1732 | /** 1733 | * @param {Number} measure - size of width or height 1734 | * @param {Boolean} isWidth 1735 | */ 1736 | proto._setContainerMeasure = function( measure, isWidth ) { 1737 | if ( measure === undefined ) { 1738 | return; 1739 | } 1740 | 1741 | var elemSize = this.size; 1742 | // add padding and border width if border box 1743 | if ( elemSize.isBorderBox ) { 1744 | measure += isWidth ? elemSize.paddingLeft + elemSize.paddingRight + 1745 | elemSize.borderLeftWidth + elemSize.borderRightWidth : 1746 | elemSize.paddingBottom + elemSize.paddingTop + 1747 | elemSize.borderTopWidth + elemSize.borderBottomWidth; 1748 | } 1749 | 1750 | measure = Math.max( measure, 0 ); 1751 | this.element.style[ isWidth ? 'width' : 'height' ] = measure + 'px'; 1752 | }; 1753 | 1754 | /** 1755 | * emit eventComplete on a collection of items events 1756 | * @param {String} eventName 1757 | * @param {Array} items - Outlayer.Items 1758 | */ 1759 | proto._emitCompleteOnItems = function( eventName, items ) { 1760 | var _this = this; 1761 | function onComplete() { 1762 | _this.dispatchEvent( eventName + 'Complete', null, [ items ] ); 1763 | } 1764 | 1765 | var count = items.length; 1766 | if ( !items || !count ) { 1767 | onComplete(); 1768 | return; 1769 | } 1770 | 1771 | var doneCount = 0; 1772 | function tick() { 1773 | doneCount++; 1774 | if ( doneCount == count ) { 1775 | onComplete(); 1776 | } 1777 | } 1778 | 1779 | // bind callback 1780 | items.forEach( function( item ) { 1781 | item.once( eventName, tick ); 1782 | }); 1783 | }; 1784 | 1785 | /** 1786 | * emits events via EvEmitter and jQuery events 1787 | * @param {String} type - name of event 1788 | * @param {Event} event - original event 1789 | * @param {Array} args - extra arguments 1790 | */ 1791 | proto.dispatchEvent = function( type, event, args ) { 1792 | // add original event to arguments 1793 | var emitArgs = event ? [ event ].concat( args ) : args; 1794 | this.emitEvent( type, emitArgs ); 1795 | 1796 | if ( jQuery ) { 1797 | // set this.$element 1798 | this.$element = this.$element || jQuery( this.element ); 1799 | if ( event ) { 1800 | // create jQuery event 1801 | var $event = jQuery.Event( event ); 1802 | $event.type = type; 1803 | this.$element.trigger( $event, args ); 1804 | } else { 1805 | // just trigger with type if no event available 1806 | this.$element.trigger( type, args ); 1807 | } 1808 | } 1809 | }; 1810 | 1811 | // -------------------------- ignore & stamps -------------------------- // 1812 | 1813 | 1814 | /** 1815 | * keep item in collection, but do not lay it out 1816 | * ignored items do not get skipped in layout 1817 | * @param {Element} elem 1818 | */ 1819 | proto.ignore = function( elem ) { 1820 | var item = this.getItem( elem ); 1821 | if ( item ) { 1822 | item.isIgnored = true; 1823 | } 1824 | }; 1825 | 1826 | /** 1827 | * return item to layout collection 1828 | * @param {Element} elem 1829 | */ 1830 | proto.unignore = function( elem ) { 1831 | var item = this.getItem( elem ); 1832 | if ( item ) { 1833 | delete item.isIgnored; 1834 | } 1835 | }; 1836 | 1837 | /** 1838 | * adds elements to stamps 1839 | * @param {NodeList, Array, Element, or String} elems 1840 | */ 1841 | proto.stamp = function( elems ) { 1842 | elems = this._find( elems ); 1843 | if ( !elems ) { 1844 | return; 1845 | } 1846 | 1847 | this.stamps = this.stamps.concat( elems ); 1848 | // ignore 1849 | elems.forEach( this.ignore, this ); 1850 | }; 1851 | 1852 | /** 1853 | * removes elements to stamps 1854 | * @param {NodeList, Array, or Element} elems 1855 | */ 1856 | proto.unstamp = function( elems ) { 1857 | elems = this._find( elems ); 1858 | if ( !elems ){ 1859 | return; 1860 | } 1861 | 1862 | elems.forEach( function( elem ) { 1863 | // filter out removed stamp elements 1864 | utils.removeFrom( this.stamps, elem ); 1865 | this.unignore( elem ); 1866 | }, this ); 1867 | }; 1868 | 1869 | /** 1870 | * finds child elements 1871 | * @param {NodeList, Array, Element, or String} elems 1872 | * @returns {Array} elems 1873 | */ 1874 | proto._find = function( elems ) { 1875 | if ( !elems ) { 1876 | return; 1877 | } 1878 | // if string, use argument as selector string 1879 | if ( typeof elems == 'string' ) { 1880 | elems = this.element.querySelectorAll( elems ); 1881 | } 1882 | elems = utils.makeArray( elems ); 1883 | return elems; 1884 | }; 1885 | 1886 | proto._manageStamps = function() { 1887 | if ( !this.stamps || !this.stamps.length ) { 1888 | return; 1889 | } 1890 | 1891 | this._getBoundingRect(); 1892 | 1893 | this.stamps.forEach( this._manageStamp, this ); 1894 | }; 1895 | 1896 | // update boundingLeft / Top 1897 | proto._getBoundingRect = function() { 1898 | // get bounding rect for container element 1899 | var boundingRect = this.element.getBoundingClientRect(); 1900 | var size = this.size; 1901 | this._boundingRect = { 1902 | left: boundingRect.left + size.paddingLeft + size.borderLeftWidth, 1903 | top: boundingRect.top + size.paddingTop + size.borderTopWidth, 1904 | right: boundingRect.right - ( size.paddingRight + size.borderRightWidth ), 1905 | bottom: boundingRect.bottom - ( size.paddingBottom + size.borderBottomWidth ) 1906 | }; 1907 | }; 1908 | 1909 | /** 1910 | * @param {Element} stamp 1911 | **/ 1912 | proto._manageStamp = noop; 1913 | 1914 | /** 1915 | * get x/y position of element relative to container element 1916 | * @param {Element} elem 1917 | * @returns {Object} offset - has left, top, right, bottom 1918 | */ 1919 | proto._getElementOffset = function( elem ) { 1920 | var boundingRect = elem.getBoundingClientRect(); 1921 | var thisRect = this._boundingRect; 1922 | var size = getSize( elem ); 1923 | var offset = { 1924 | left: boundingRect.left - thisRect.left - size.marginLeft, 1925 | top: boundingRect.top - thisRect.top - size.marginTop, 1926 | right: thisRect.right - boundingRect.right - size.marginRight, 1927 | bottom: thisRect.bottom - boundingRect.bottom - size.marginBottom 1928 | }; 1929 | return offset; 1930 | }; 1931 | 1932 | // -------------------------- resize -------------------------- // 1933 | 1934 | // enable event handlers for listeners 1935 | // i.e. resize -> onresize 1936 | proto.handleEvent = utils.handleEvent; 1937 | 1938 | /** 1939 | * Bind layout to window resizing 1940 | */ 1941 | proto.bindResize = function() { 1942 | window.addEventListener( 'resize', this ); 1943 | this.isResizeBound = true; 1944 | }; 1945 | 1946 | /** 1947 | * Unbind layout to window resizing 1948 | */ 1949 | proto.unbindResize = function() { 1950 | window.removeEventListener( 'resize', this ); 1951 | this.isResizeBound = false; 1952 | }; 1953 | 1954 | proto.onresize = function() { 1955 | this.resize(); 1956 | }; 1957 | 1958 | utils.debounceMethod( Outlayer, 'onresize', 100 ); 1959 | 1960 | proto.resize = function() { 1961 | // don't trigger if size did not change 1962 | // or if resize was unbound. See #9 1963 | if ( !this.isResizeBound || !this.needsResizeLayout() ) { 1964 | return; 1965 | } 1966 | 1967 | this.layout(); 1968 | }; 1969 | 1970 | /** 1971 | * check if layout is needed post layout 1972 | * @returns Boolean 1973 | */ 1974 | proto.needsResizeLayout = function() { 1975 | var size = getSize( this.element ); 1976 | // check that this.size and size are there 1977 | // IE8 triggers resize on body size change, so they might not be 1978 | var hasSizes = this.size && size; 1979 | return hasSizes && size.innerWidth !== this.size.innerWidth; 1980 | }; 1981 | 1982 | // -------------------------- methods -------------------------- // 1983 | 1984 | /** 1985 | * add items to Outlayer instance 1986 | * @param {Array or NodeList or Element} elems 1987 | * @returns {Array} items - Outlayer.Items 1988 | **/ 1989 | proto.addItems = function( elems ) { 1990 | var items = this._itemize( elems ); 1991 | // add items to collection 1992 | if ( items.length ) { 1993 | this.items = this.items.concat( items ); 1994 | } 1995 | return items; 1996 | }; 1997 | 1998 | /** 1999 | * Layout newly-appended item elements 2000 | * @param {Array or NodeList or Element} elems 2001 | */ 2002 | proto.appended = function( elems ) { 2003 | var items = this.addItems( elems ); 2004 | if ( !items.length ) { 2005 | return; 2006 | } 2007 | // layout and reveal just the new items 2008 | this.layoutItems( items, true ); 2009 | this.reveal( items ); 2010 | }; 2011 | 2012 | /** 2013 | * Layout prepended elements 2014 | * @param {Array or NodeList or Element} elems 2015 | */ 2016 | proto.prepended = function( elems ) { 2017 | var items = this._itemize( elems ); 2018 | if ( !items.length ) { 2019 | return; 2020 | } 2021 | // add items to beginning of collection 2022 | var previousItems = this.items.slice(0); 2023 | this.items = items.concat( previousItems ); 2024 | // start new layout 2025 | this._resetLayout(); 2026 | this._manageStamps(); 2027 | // layout new stuff without transition 2028 | this.layoutItems( items, true ); 2029 | this.reveal( items ); 2030 | // layout previous items 2031 | this.layoutItems( previousItems ); 2032 | }; 2033 | 2034 | /** 2035 | * reveal a collection of items 2036 | * @param {Array of Outlayer.Items} items 2037 | */ 2038 | proto.reveal = function( items ) { 2039 | this._emitCompleteOnItems( 'reveal', items ); 2040 | if ( !items || !items.length ) { 2041 | return; 2042 | } 2043 | var stagger = this.updateStagger(); 2044 | items.forEach( function( item, i ) { 2045 | item.stagger( i * stagger ); 2046 | item.reveal(); 2047 | }); 2048 | }; 2049 | 2050 | /** 2051 | * hide a collection of items 2052 | * @param {Array of Outlayer.Items} items 2053 | */ 2054 | proto.hide = function( items ) { 2055 | this._emitCompleteOnItems( 'hide', items ); 2056 | if ( !items || !items.length ) { 2057 | return; 2058 | } 2059 | var stagger = this.updateStagger(); 2060 | items.forEach( function( item, i ) { 2061 | item.stagger( i * stagger ); 2062 | item.hide(); 2063 | }); 2064 | }; 2065 | 2066 | /** 2067 | * reveal item elements 2068 | * @param {Array}, {Element}, {NodeList} items 2069 | */ 2070 | proto.revealItemElements = function( elems ) { 2071 | var items = this.getItems( elems ); 2072 | this.reveal( items ); 2073 | }; 2074 | 2075 | /** 2076 | * hide item elements 2077 | * @param {Array}, {Element}, {NodeList} items 2078 | */ 2079 | proto.hideItemElements = function( elems ) { 2080 | var items = this.getItems( elems ); 2081 | this.hide( items ); 2082 | }; 2083 | 2084 | /** 2085 | * get Outlayer.Item, given an Element 2086 | * @param {Element} elem 2087 | * @param {Function} callback 2088 | * @returns {Outlayer.Item} item 2089 | */ 2090 | proto.getItem = function( elem ) { 2091 | // loop through items to get the one that matches 2092 | for ( var i=0; i < this.items.length; i++ ) { 2093 | var item = this.items[i]; 2094 | if ( item.element == elem ) { 2095 | // return item 2096 | return item; 2097 | } 2098 | } 2099 | }; 2100 | 2101 | /** 2102 | * get collection of Outlayer.Items, given Elements 2103 | * @param {Array} elems 2104 | * @returns {Array} items - Outlayer.Items 2105 | */ 2106 | proto.getItems = function( elems ) { 2107 | elems = utils.makeArray( elems ); 2108 | var items = []; 2109 | elems.forEach( function( elem ) { 2110 | var item = this.getItem( elem ); 2111 | if ( item ) { 2112 | items.push( item ); 2113 | } 2114 | }, this ); 2115 | 2116 | return items; 2117 | }; 2118 | 2119 | /** 2120 | * remove element(s) from instance and DOM 2121 | * @param {Array or NodeList or Element} elems 2122 | */ 2123 | proto.remove = function( elems ) { 2124 | var removeItems = this.getItems( elems ); 2125 | 2126 | this._emitCompleteOnItems( 'remove', removeItems ); 2127 | 2128 | // bail if no items to remove 2129 | if ( !removeItems || !removeItems.length ) { 2130 | return; 2131 | } 2132 | 2133 | removeItems.forEach( function( item ) { 2134 | item.remove(); 2135 | // remove item from collection 2136 | utils.removeFrom( this.items, item ); 2137 | }, this ); 2138 | }; 2139 | 2140 | // ----- destroy ----- // 2141 | 2142 | // remove and disable Outlayer instance 2143 | proto.destroy = function() { 2144 | // clean up dynamic styles 2145 | var style = this.element.style; 2146 | style.height = ''; 2147 | style.position = ''; 2148 | style.width = ''; 2149 | // destroy items 2150 | this.items.forEach( function( item ) { 2151 | item.destroy(); 2152 | }); 2153 | 2154 | this.unbindResize(); 2155 | 2156 | var id = this.element.outlayerGUID; 2157 | delete instances[ id ]; // remove reference to instance by id 2158 | delete this.element.outlayerGUID; 2159 | // remove data for jQuery 2160 | if ( jQuery ) { 2161 | jQuery.removeData( this.element, this.constructor.namespace ); 2162 | } 2163 | 2164 | }; 2165 | 2166 | // -------------------------- data -------------------------- // 2167 | 2168 | /** 2169 | * get Outlayer instance from element 2170 | * @param {Element} elem 2171 | * @returns {Outlayer} 2172 | */ 2173 | Outlayer.data = function( elem ) { 2174 | elem = utils.getQueryElement( elem ); 2175 | var id = elem && elem.outlayerGUID; 2176 | return id && instances[ id ]; 2177 | }; 2178 | 2179 | 2180 | // -------------------------- create Outlayer class -------------------------- // 2181 | 2182 | /** 2183 | * create a layout class 2184 | * @param {String} namespace 2185 | */ 2186 | Outlayer.create = function( namespace, options ) { 2187 | // sub-class Outlayer 2188 | var Layout = subclass( Outlayer ); 2189 | // apply new options and compatOptions 2190 | Layout.defaults = utils.extend( {}, Outlayer.defaults ); 2191 | utils.extend( Layout.defaults, options ); 2192 | Layout.compatOptions = utils.extend( {}, Outlayer.compatOptions ); 2193 | 2194 | Layout.namespace = namespace; 2195 | 2196 | Layout.data = Outlayer.data; 2197 | 2198 | // sub-class Item 2199 | Layout.Item = subclass( Item ); 2200 | 2201 | // -------------------------- declarative -------------------------- // 2202 | 2203 | utils.htmlInit( Layout, namespace ); 2204 | 2205 | // -------------------------- jQuery bridge -------------------------- // 2206 | 2207 | // make into jQuery plugin 2208 | if ( jQuery && jQuery.bridget ) { 2209 | jQuery.bridget( namespace, Layout ); 2210 | } 2211 | 2212 | return Layout; 2213 | }; 2214 | 2215 | function subclass( Parent ) { 2216 | function SubClass() { 2217 | Parent.apply( this, arguments ); 2218 | } 2219 | 2220 | SubClass.prototype = Object.create( Parent.prototype ); 2221 | SubClass.prototype.constructor = SubClass; 2222 | 2223 | return SubClass; 2224 | } 2225 | 2226 | // ----- helpers ----- // 2227 | 2228 | // how many milliseconds are in each unit 2229 | var msUnits = { 2230 | ms: 1, 2231 | s: 1000 2232 | }; 2233 | 2234 | // munge time-like parameter into millisecond number 2235 | // '0.4s' -> 40 2236 | function getMilliseconds( time ) { 2237 | if ( typeof time == 'number' ) { 2238 | return time; 2239 | } 2240 | var matches = time.match( /(^\d*\.?\d*)(\w*)/ ); 2241 | var num = matches && matches[1]; 2242 | var unit = matches && matches[2]; 2243 | if ( !num.length ) { 2244 | return 0; 2245 | } 2246 | num = parseFloat( num ); 2247 | var mult = msUnits[ unit ] || 1; 2248 | return num * mult; 2249 | } 2250 | 2251 | // ----- fin ----- // 2252 | 2253 | // back in global 2254 | Outlayer.Item = Item; 2255 | 2256 | return Outlayer; 2257 | 2258 | })); 2259 | 2260 | /*! 2261 | * Masonry v4.2.0 2262 | * Cascading grid layout library 2263 | * http://masonry.desandro.com 2264 | * MIT License 2265 | * by David DeSandro 2266 | */ 2267 | 2268 | ( function( window, factory ) { 2269 | // universal module definition 2270 | /* jshint strict: false */ /*globals define, module, require */ 2271 | if ( typeof define == 'function' && define.amd ) { 2272 | // AMD 2273 | define( [ 2274 | 'outlayer/outlayer', 2275 | 'get-size/get-size' 2276 | ], 2277 | factory ); 2278 | } else if ( typeof module == 'object' && module.exports ) { 2279 | // CommonJS 2280 | module.exports = factory( 2281 | require('outlayer'), 2282 | require('get-size') 2283 | ); 2284 | } else { 2285 | // browser global 2286 | window.Masonry = factory( 2287 | window.Outlayer, 2288 | window.getSize 2289 | ); 2290 | } 2291 | 2292 | }( window, function factory( Outlayer, getSize ) { 2293 | 2294 | 2295 | 2296 | // -------------------------- masonryDefinition -------------------------- // 2297 | 2298 | // create an Outlayer layout class 2299 | var Masonry = Outlayer.create('masonry'); 2300 | // isFitWidth -> fitWidth 2301 | Masonry.compatOptions.fitWidth = 'isFitWidth'; 2302 | 2303 | var proto = Masonry.prototype; 2304 | 2305 | proto._resetLayout = function() { 2306 | this.getSize(); 2307 | this._getMeasurement( 'columnWidth', 'outerWidth' ); 2308 | this._getMeasurement( 'gutter', 'outerWidth' ); 2309 | this.measureColumns(); 2310 | 2311 | // reset column Y 2312 | this.colYs = []; 2313 | for ( var i=0; i < this.cols; i++ ) { 2314 | this.colYs.push( 0 ); 2315 | } 2316 | 2317 | this.maxY = 0; 2318 | this.horizontalColIndex = 0; 2319 | }; 2320 | 2321 | proto.measureColumns = function() { 2322 | this.getContainerWidth(); 2323 | // if columnWidth is 0, default to outerWidth of first item 2324 | if ( !this.columnWidth ) { 2325 | var firstItem = this.items[0]; 2326 | var firstItemElem = firstItem && firstItem.element; 2327 | // columnWidth fall back to item of first element 2328 | this.columnWidth = firstItemElem && getSize( firstItemElem ).outerWidth || 2329 | // if first elem has no width, default to size of container 2330 | this.containerWidth; 2331 | } 2332 | 2333 | var columnWidth = this.columnWidth += this.gutter; 2334 | 2335 | // calculate columns 2336 | var containerWidth = this.containerWidth + this.gutter; 2337 | var cols = containerWidth / columnWidth; 2338 | // fix rounding errors, typically with gutters 2339 | var excess = columnWidth - containerWidth % columnWidth; 2340 | // if overshoot is less than a pixel, round up, otherwise floor it 2341 | var mathMethod = excess && excess < 1 ? 'round' : 'floor'; 2342 | cols = Math[ mathMethod ]( cols ); 2343 | this.cols = Math.max( cols, 1 ); 2344 | }; 2345 | 2346 | proto.getContainerWidth = function() { 2347 | // container is parent if fit width 2348 | var isFitWidth = this._getOption('fitWidth'); 2349 | var container = isFitWidth ? this.element.parentNode : this.element; 2350 | // check that this.size and size are there 2351 | // IE8 triggers resize on body size change, so they might not be 2352 | var size = getSize( container ); 2353 | this.containerWidth = size && size.innerWidth; 2354 | }; 2355 | 2356 | proto._getItemLayoutPosition = function( item ) { 2357 | item.getSize(); 2358 | // how many columns does this brick span 2359 | var remainder = item.size.outerWidth % this.columnWidth; 2360 | var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil'; 2361 | // round if off by 1 pixel, otherwise use ceil 2362 | var colSpan = Math[ mathMethod ]( item.size.outerWidth / this.columnWidth ); 2363 | colSpan = Math.min( colSpan, this.cols ); 2364 | // use horizontal or top column position 2365 | var colPosMethod = this.options.horizontalOrder ? 2366 | '_getHorizontalColPosition' : '_getTopColPosition'; 2367 | var colPosition = this[ colPosMethod ]( colSpan, item ); 2368 | // position the brick 2369 | var position = { 2370 | x: this.columnWidth * colPosition.col, 2371 | y: colPosition.y 2372 | }; 2373 | // apply setHeight to necessary columns 2374 | var setHeight = colPosition.y + item.size.outerHeight; 2375 | var setMax = colSpan + colPosition.col; 2376 | for ( var i = colPosition.col; i < setMax; i++ ) { 2377 | this.colYs[i] = setHeight; 2378 | } 2379 | 2380 | return position; 2381 | }; 2382 | 2383 | proto._getTopColPosition = function( colSpan ) { 2384 | var colGroup = this._getTopColGroup( colSpan ); 2385 | // get the minimum Y value from the columns 2386 | var minimumY = Math.min.apply( Math, colGroup ); 2387 | 2388 | return { 2389 | col: colGroup.indexOf( minimumY ), 2390 | y: minimumY, 2391 | }; 2392 | }; 2393 | 2394 | /** 2395 | * @param {Number} colSpan - number of columns the element spans 2396 | * @returns {Array} colGroup 2397 | */ 2398 | proto._getTopColGroup = function( colSpan ) { 2399 | if ( colSpan < 2 ) { 2400 | // if brick spans only one column, use all the column Ys 2401 | return this.colYs; 2402 | } 2403 | 2404 | var colGroup = []; 2405 | // how many different places could this brick fit horizontally 2406 | var groupCount = this.cols + 1 - colSpan; 2407 | // for each group potential horizontal position 2408 | for ( var i = 0; i < groupCount; i++ ) { 2409 | colGroup[i] = this._getColGroupY( i, colSpan ); 2410 | } 2411 | return colGroup; 2412 | }; 2413 | 2414 | proto._getColGroupY = function( col, colSpan ) { 2415 | if ( colSpan < 2 ) { 2416 | return this.colYs[ col ]; 2417 | } 2418 | // make an array of colY values for that one group 2419 | var groupColYs = this.colYs.slice( col, col + colSpan ); 2420 | // and get the max value of the array 2421 | return Math.max.apply( Math, groupColYs ); 2422 | }; 2423 | 2424 | // get column position based on horizontal index. #873 2425 | proto._getHorizontalColPosition = function( colSpan, item ) { 2426 | var col = this.horizontalColIndex % this.cols; 2427 | var isOver = colSpan > 1 && col + colSpan > this.cols; 2428 | // shift to next row if item can't fit on current row 2429 | col = isOver ? 0 : col; 2430 | // don't let zero-size items take up space 2431 | var hasSize = item.size.outerWidth && item.size.outerHeight; 2432 | this.horizontalColIndex = hasSize ? col + colSpan : this.horizontalColIndex; 2433 | 2434 | return { 2435 | col: col, 2436 | y: this._getColGroupY( col, colSpan ), 2437 | }; 2438 | }; 2439 | 2440 | proto._manageStamp = function( stamp ) { 2441 | var stampSize = getSize( stamp ); 2442 | var offset = this._getElementOffset( stamp ); 2443 | // get the columns that this stamp affects 2444 | var isOriginLeft = this._getOption('originLeft'); 2445 | var firstX = isOriginLeft ? offset.left : offset.right; 2446 | var lastX = firstX + stampSize.outerWidth; 2447 | var firstCol = Math.floor( firstX / this.columnWidth ); 2448 | firstCol = Math.max( 0, firstCol ); 2449 | var lastCol = Math.floor( lastX / this.columnWidth ); 2450 | // lastCol should not go over if multiple of columnWidth #425 2451 | lastCol -= lastX % this.columnWidth ? 0 : 1; 2452 | lastCol = Math.min( this.cols - 1, lastCol ); 2453 | // set colYs to bottom of the stamp 2454 | 2455 | var isOriginTop = this._getOption('originTop'); 2456 | var stampMaxY = ( isOriginTop ? offset.top : offset.bottom ) + 2457 | stampSize.outerHeight; 2458 | for ( var i = firstCol; i <= lastCol; i++ ) { 2459 | this.colYs[i] = Math.max( stampMaxY, this.colYs[i] ); 2460 | } 2461 | }; 2462 | 2463 | proto._getContainerSize = function() { 2464 | this.maxY = Math.max.apply( Math, this.colYs ); 2465 | var size = { 2466 | height: this.maxY 2467 | }; 2468 | 2469 | if ( this._getOption('fitWidth') ) { 2470 | size.width = this._getContainerFitWidth(); 2471 | } 2472 | 2473 | return size; 2474 | }; 2475 | 2476 | proto._getContainerFitWidth = function() { 2477 | var unusedCols = 0; 2478 | // count unused columns 2479 | var i = this.cols; 2480 | while ( --i ) { 2481 | if ( this.colYs[i] !== 0 ) { 2482 | break; 2483 | } 2484 | unusedCols++; 2485 | } 2486 | // fit container to columns that have been used 2487 | return ( this.cols - unusedCols ) * this.columnWidth - this.gutter; 2488 | }; 2489 | 2490 | proto.needsResizeLayout = function() { 2491 | var previousWidth = this.containerWidth; 2492 | this.getContainerWidth(); 2493 | return previousWidth != this.containerWidth; 2494 | }; 2495 | 2496 | return Masonry; 2497 | 2498 | })); 2499 | 2500 | --------------------------------------------------------------------------------